文章目录
1 工厂机制
- UVM工厂的存在是为了更方便地替换验证环境中的示例或注册了的类型。
- UVM验证环境构成可以分为环境层级和环境属性(例如配置)和数据传输。分别通过uvm_component和uvm_object类完成,这两种类是进出工厂的主要模具和生产对象。
- 三个核心要素:注册、创建、覆盖
1.1 uvm_component & uvm_object
作用:
- uvm_component:构建验证环境,包括环境层次、组件。
- uvm_object:构建测试场景,包括环境属性和数据传输,动态产生。
注册:
-
使用注册宏,将类注册到factory中。
-
使用注册,是通过例化该类的对象来完成。
注册组件类uvm_component:````uvm_component_utils```
注册核心基类uvm_object:````uvm_object_utils```
构建函数:
class comp1 extends uvm_component; //组件注册和构建
`uvm_component_utils(comp1) //使用宏注册
function new(string name = "comp1",uvm_component parent=null); //构建函数,parent指当前类的上一层
super.new(name, parent);
$display($sformatf("%s is created",name));
endfunction:new
function void build_phase(uvm_phase phase); //先不看
super.build_phase(phase);
endfunction:build_phase
endclass
class obj1 extends uvm_object; //object注册和构建
`uvm_object_utils(obj1)
function new(string name = "obj1"); //只有一个参数
super.new(name);
$display($sformatf("%s is created",name));
endfunction:new
endclass
创建(create):
uvm_component:
comp_type::type_id::create(string name, uvm_component parent)
comp_type指要创建的类,type_id指注册到工厂的类型,调用create方法(后台是利用工厂来做创建)
uvm_object:
object_type::type_id::create(string name)
1.2 uvm_coreservice_t类
-
该类内置了UVM世界核心的组件和方法,主要包括:
- 唯一的uvm_factory,该组件用来注册、覆盖和例化
- 全局的report_server,该组件用来做消息统筹和报告
- 全局的tr_database,该组件用来记录transaction记录
- get_root()方法用来返回当前UVM环境的结构顶层对象 (root是UVM结构的顶层)
-
该类并不是uvm_component或者uvm_object,它也没有例化在UVM环境中,而是独立在UVM环境之外的。
-
uvm_coreservice_t 只会被UVM系统在仿真开始时例化一次
1.3 注册机制
-
uvm_default_factory::register():该方法在该类没有被注册过或者覆盖过时,会将该类例化过的对象句柄放置到factory内的类型字典(关联数组)uvm_default_factory::m_type_names中,同时将对象句柄作为uvm_default_factory::m_types字典的索引键,而键值设置为1。
-
uvm_default_factory::create_component_by_type(): 首先检查处在该层次路径中需要被例化的对象,是否受到了“类型覆盖”或者“实例覆盖”的影响,进而将最终类型对应的对象句柄(正确的产品模板)交给工厂。有了正确地产品模板,接下来就可以通过**uvm_component_registry::create_component()**来完成例化。实际上,uvm_component_registry::create_component()内部就是直接通过调用uvm_component的构建函数new(name, parent)来实现的。
-
uvm_component和uvm_object的区别:
- 工厂创建出的uvm_component是表示在UVM层次结构中的,而uvm_object则不会显示在层次结构中。
- uvm_component::new(name,parent)通过parent参数,能够将UVM结构一层层串起来;
- uvm_object::new(name)只能作为configuration或者transaction等用来做传递的配置结构体或抽象数据传输的结构体,成为uvm_component的成员变量
- 在注册过程中,uvm_component_registry或者uvm_object_registry(均继承于uvm_object_wrapper)来分别注册uvm_component和uvm_object。
- 以uvm_component_registry来说明,对于这样一种专门用来注册的类而言,它们自身的方法是专门为配合factory的注册、创建和覆盖而生的,这些方法分别是:
- create()
- create_component()
- get()
- get_type_name()
- set_inst_override()
- set_type_override()
每一个uvm_component的类在注册时都定义了一个新的uvm_component_registry类,该类的形式定义为:typedef uvm_component_registry #(T,Tname) this_type;
1.4 覆盖方法
factory提供覆盖特性,覆盖机制可以将原来所属的类型替换为另一个新的类型
-
无需修改原始代码,保证了原有代码的封装性;
-
做顶层修改很方便;允许灵活的配置,例如可以用子类覆盖原本的父类;可使用不同的对象来修改其代码行为;
-
要想实现覆盖特性,原有类型和新类型均需要注册;
-
覆盖发生时,可以使用“类型覆盖”或者“实例覆盖”;
- 类型覆盖:指UVM层次结构下的所有类型都被覆盖类型所替换;
- 实例覆盖:指某些位置中的原有类型会被覆盖类型所替换
1.4.1 类型覆盖 & 实例覆盖
类型覆盖(set_type_override):
static function void set_type_override (uvm_object_wrapper override_type, bit replace=1);
- uvm_object_wrapper override_type是注册后的某一类在工厂注册时的句柄
- bit replace=1:如果已经有覆盖存在,新覆盖会替代旧覆盖
- bit replace=0:如果已经有覆盖存在,那么该覆盖将不会生效
- set_type_override是一个静态函数
orig_type::type_id::set_type_override (new_type::get_type())
实例覆盖(set_inst_override):
static function void set_inst_override (uvm_object_wrapper override_type, string inst_path, uvm_component parent=null);
- string inst_path指向组件结构的路径;
- uvm_component parent=null
- 如果缺省,表示使用inst_path为绝对路径
- 如果有值传递,则使用{parent.get_full_name(),’.’,inst_path}作为目标路径
orig_type::type_id::set_inst_override (new_type::get_type(), "orig_inst_path")
上面这两种方法由类型箱子uvm_component_registry和uvm_object_registry来提供。
1.4.2 覆盖实例
module factory_override;
import uvm_pkg::*;
`include "uvm_macros.svh" //inst_path
class comp1 extends uvm_component;
`uvm_component_utils(comp1)
function new(string name="comp1", uvm_component parent=null);
super.new(name, parent);
$display($sformatf("comp1:: %s is created", name));
endfunction: new
virtual function void hello(string name); //注意virtual
$display($sformatf("comp1:: %s said hello!", name));
endfunction
endclass
class comp2 extends comp1;
`uvm_component_utils(comp2)
function new(string name="comp2", uvm_component parent=null);
super.new(name, parent);
$display($sformatf("comp2:: %s is created", name));
endfunction: new
function void hello(string name);
$display($sformatf("comp2:: %s said hello!", name));
endfunction
endclass
comp1 c1, c2;
initial begin
comp1::type_id::set_type_override(comp2::get_type());
c1 = new("c1");
c2 = comp1::type_id::create("c2", null);
c1.hello("c1");
c2.hello("c2");
end
endmodule
输出结果为:
comp1::c1 is created
comp1::c2 is created
comp2::c2 is created
comp1::c1 said hello!
comp2::c2 said hello!
结果分析:
- 只有c2所属类型改变,说明:factory覆盖机制只会影响通过factory注册并创建的对象。
- 在例化之前类型替换;
- c2句柄类型仍为comp1,但却指向了comp2类型的对象。因此要求comp2是comp1的子类。
- c2在调用hello()方法时,由于首先是comp1类型,那么会查看comp1::hello(),又由于该方法定义时被指定为虚函数,这就通过了多态性的方法调用,转而又调用了comp2::hello()函数。
1.4.3 确保正确覆盖的代码要求
- 将UVM环境中所有的新类都注册到工厂中,并通过工厂来创建对象
- 在使用某些类的时候,确保该类已经被导入到当前域中
- 通过工厂创建对象时,句柄名称应该同传递到create()方法中的字符串名称相同(c2)
- 由于覆盖是采用parent wins模式,因此要注意在同一个顶层build_phase()中覆盖方法应发生在对象创建之前
- 为了尽量保证运行时覆盖类可以替换原始类,覆盖类最好是原始类的子类,而调用成员方法也应当声明为虚方法
厂中,并通过工厂来创建对象 - 在使用某些类的时候,确保该类已经被导入到当前域中
- 通过工厂创建对象时,句柄名称应该同传递到create()方法中的字符串名称相同(c2)
- 由于覆盖是采用parent wins模式,因此要注意在同一个顶层build_phase()中覆盖方法应发生在对象创建之前
- 为了尽量保证运行时覆盖类可以替换原始类,覆盖类最好是原始类的子类,而调用成员方法也应当声明为虚方法
- 另外一种确保运行时覆盖类句柄类型正确的方式,则需要通过$cast()进行动态类型转换