2.9 factory
前言
本文以uvm-1.2/examples/simple/factory为例,通过代码了解UVM中factory机制的一些用法。通过这个例子可以基本了解以下知识点:
- 如何将组件注册到UVM的factory机制中
- 如何利用UVM的factory机制实现组件的重载
- 如何利用virtual关键字实现类方法的重载
一、基本介绍
在张强的《UVM实战》的第8章中,对UVM的factory机制已经有了比较详细的介绍。这里主要结合代码介绍一下,如何将UVM的组件注册到UVM的factory机制中,以及利用UVM的factory机制实现组件的重载。
首先,介绍一下如何将UVM的组件注册到UVM的factory机制中。在UVM中的组件主要分为两大类,一类是uvm_object,另一类是uvm_component,他们之间的关系在张强《UVM实战》的3.1节有比较详细的介绍。针对这两大类的组件,UVM分别提供了两个宏来处理factory注册的问题。其中派生自uvm_object和uvm_component的组件,通过以下宏实现注册。
`uvm_object_utils //注册不带参数的uvm_object
`uvm_object_param_utils //注册带参数的uvm_object
`uvm_component_utils //注册不带参数的uvm_component
`uvm_component_param_utils//注册不带参数的uvm_component
其次,介绍一下如何利用UVM的factory机制实现组件的重载。重载的意思是重新载入,重载要实现的功能是,在不影响其他组件正常工作的前提下,将某一类或者某一个组件,利用新的组件重新载入进去,替换掉原有的组件,从而实现新的功能。UVM提供了以下两类重载函数,具体的使用方法可以参考张强《UVM实战》8.2节。值得注意的是,重载与被重载组件之间必须是父子关系,或者关系比较近的继承关系。
set_type_override() //类型重载。不管例化的路径和次数,都会被重载。
set_inst_override() //例化重载。只有一个组件会被重载,需要提供被替换组件的路径。
最后,在类方法前面加上virtual关键字后,子类声明与父类名字相同的方法时,会重载父类的方法。
二、代码分析
这个测试用例包括四个文件:test.sv、env_pkg.sv、gen_pkg.sv、packet_pkg.sv。test.sv中主要实现了顶层的封装以及组件的重载;env_pkg.sv中主要封装了环境;gen_pkg.sv和packet_pkg.sv中,分别实现了uvm_object和uvm_component的factory注册。
2.1 test.sv
/*
About: factory
This example will illustrate the usage of uvm_factory methods.
To get more details about the factory related methods, check the file:
- uvm/src/base/uvm_factory.svh
*/
module top;
import uvm_pkg::*;
import packet_pkg::*;
import gen_pkg::*;
import env_pkg::*;
`include "uvm_macros.svh"
env e; //you need to use something from the package to have the
//factory registration occur.
class mygen extends gen;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function packet get_packet();
`uvm_info("PKTGEN", $sformatf("Getting a packet from %s (%s)", get_full_name(), get_type_name()),UVM_MEDIUM)
return super.get_packet();
endfunction
//Use the macro in a class to implement factory registration along with other
//utilities (create, get_type_name). To just do factory registration, use the
//macro `uvm_object_registry(mygen,"mygen")
`uvm_component_utils(mygen)
endclass
class mypacket extends packet;
constraint ct10 { addr >= 0 && addr <= 10; }
//Use the macro in a class to implement factory registration along with other
//utilities (create, get_type_name).
`uvm_object_utils(mypacket)
function new(string name="mypacket");
super.new(name);
endfunction
endclass
initial begin automatic uvm_coreservice_t cs_ = uvm_coreservice_t::get();
uvm_factory factory;
factory = cs_.get_factory();
gen::type_id::set_inst_override(mygen::get_type(), "uvm_test_top.gen1");
packet::type_id::set_type_override(mypacket::get_type());
factory.print(1);
//If a string is used to run_test, run_test will used the string based factory
//create method to create an object of the desired type.
run_test("env");
end
endmodule
17到20行,分别导入了UVM的包,三个在其他文件中封装的包。
23行声明了env,env的实现在env_pkg.sv文件中。
26到41行,声明了一个继承自gen的类mygen,gen的实现在gen_pkg.sv文件中。在mygen中,重新写了一个get_packet()的方法,打印相关的信息,返回并调用了父类的get_packet()方法。其中,通过打印的信息可以了解到gen组件是否被重载。
39行通过调用`uvm_component_utils,实现了mygen的注册。
43到54行,声明了一个继承自packet的类mypacket,packet的实现在packet_pkg.sv文件中。在mypacket中,增加了一个约束,并在48行通过`uvm_object_utils,实现了mypacket的注册。
56到68行,通过一个initial-end模块实现仿真。
56到59行获取当前环境的factory;
60行通过调用set_inst_override()方法,实现了gen的重载;
61行通过调用set_type_override()方法,实现了packet的重载;
63行调用print函数,打印factory的基本信息,包括注册了哪些组件,哪些组件被重载;
67行执行仿真。
2.2 env_pkg.sv
package env_pkg;
import uvm_pkg::*;
import packet_pkg::*;
import gen_pkg::*;
`include "uvm_macros.svh"
class env extends uvm_env;
gen gen1;
//Use the macro in a class to implement factory registration along with other
//utilities (create, get_type_name). For only factory registration, use the
// macro `uvm_component_registry(env,"env").
`uvm_component_utils(env)
function new (string name, uvm_component parent);
uvm_component cmp;
super.new(name, parent);
//use the factory to create the generator
gen1 = gen::type_id::create("gen1", this);
endfunction
task run_phase(uvm_phase phase);
packet p;
phase.raise_objection(this);
uvm_default_tree_printer.knobs.separator = "";
repeat(5) begin
#15 p = gen1.get_packet();
`uvm_info("PKTGEN", $sformatf("Got packet: %s", p.sprint(uvm_default_tree_printer)), UVM_NONE)
end
#15;
phase.drop_objection(this);
endtask
endclass
endpackage
8到36行封装了env的类。
9行声明了gen;
14行调用`uvm_component_utils将env注册到UVM的factory机制中;
21行利用factory机制,创建了一个gen;
25到35行,在run_phase中,利用UVM的objection机制(参考张强《UVM实战》5.2节),通过“举手”和“放手”启动仿真,在仿真中,重复5次调用gen中的get_packet()方法,并打印packet的信息,通过打印的信息可以判断packet是否被重载。
2.3 gen_pkg.sv
package gen_pkg;
import uvm_pkg::*;
import packet_pkg::*;
`include "uvm_macros.svh"
class gen extends uvm_component;
//Use the macro in a class to implement factory registration along with other
//utilities (create, get_type_name). To do only factory registration, use
//the macro `uvm_component_utils(gen,"gen").
`uvm_component_utils(gen)
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction
virtual function packet get_packet();
packet p;
//use the factory to generate a package
p = packet::type_id::create("p", this);
//randomize it
void'(p.randomize());
return p;
endfunction
endclass
endpackage
6到28行封装了gen。
11行通过`uvm_component_utils实现了将gen注册到UVM的factory机制中;
17到27行定义了gen的get_packet方法,其中方法前加了一个virtual关键字,这个关键字表明这是一个虚方法,可以被子类重构,也就是说,子类可以通过定义一个名字一样的方法,替换父类的方法。
21行利用UVM的factory机制创建了一个packet;
24行对创建的packet进行随机;
26行返回随机后的packet。
2.4 packet_pkg.sv
package packet_pkg;
import uvm_pkg::*;
`include "uvm_macros.svh"
class packet extends uvm_object;
rand int addr;
rand int data;
//Use the macro in a class to implement factory registration along with other
//utilities (create, get_type_name). For only factory registration, use
//the macro `uvm_object_registry(packet,"packet").
`uvm_object_utils(packet)
//Base constraints
constraint c1 { addr inside { ['h0:'h40], ['h100:'h200], ['h1000: 'h1fff], ['h4000: 'h4fff] }; }
constraint c2 { (addr <= 'h40) -> (data inside { [10:20] } ); }
constraint c3 { (addr >= 'h100 && addr <= 'h200) -> (data inside { [100:200] } ); }
constraint c4 { (addr >= 'h1000 && addr <= 'h1fff) -> (data inside { [300:400] } ); }
constraint c5 { (addr >= 'h4000 && addr <= 'h4fff) -> (data inside { [600:800] } ); }
//do printing, comparing, etc. These functions can also be automated inside
//the `uvm_object_utils_begin/end macros if desired. Below show the manual
//approach.
function void do_print(uvm_printer printer);
printer.print_field("addr", addr, $bits(addr));
printer.print_field("data", data, $bits(data));
endfunction
function bit do_compare(uvm_object rhs, uvm_comparer comparer);
packet rhs_;
if(rhs==null || !$cast(rhs_, rhs)) return 0;
do_compare = 1;
do_compare &= comparer.compare_field("addr", addr, rhs_.addr, $bits(addr));
do_compare &= comparer.compare_field("data", data, rhs_.data, $bits(data));
endfunction
function void do_copy(uvm_object rhs);
packet rhs_;
if(rhs==null || !$cast(rhs_, rhs)) return;
addr = rhs_.addr;
data = rhs_.data;
endfunction
function new(string name="packet");
super.new(name);
endfunction
endclass
endpackage
5到46行封装了packet。
下面主要提以下几个比较重要的代码。
12行通过`uvm_object_utils,实现了packet的注册;
15到19行,分别对addr和data进行了约束;
24到40行,分别声明了一些方法,这些方法可以通过UVM中的field机制来实现,具体可以参考张强《UVM实战》3.3节。
总结
通过这个例子,主要可以学习到,如何实现UVM factory机制的注册,以及如何利用UVM factory机制对组件进行重载,另外,可以稍微了解以下virtual关键字的用法。