UVM 覆盖方法
一、概述
工厂提供的便利—覆盖
覆盖机制可以将其原来所属的类型替换为另外一个新的类型。
在覆盖之后,原本用来创建原属类型的请求,将由工厂来创建新的替换类型。
覆盖带来的便利:
1.无需再修改原始代码,继而保持了原有代码的封装性;
2. 新的替换类型必须与被替换类型相兼容,否则稍后的句柄赋值将失败,所以使用继承。//新定义的类型必须继承于原有的类型。
做顶层修改时,非常方便!
1.允许灵活的配置,例如可使用子类来覆盖原本的父类;
2.可使用不同的对象来修改其代码行为。
要想实现覆盖特性,原有类型和新类型均需要注册。
当使用create()来创建对象时:
1.工厂会检查,是否原有类型被覆盖
2.如果是,那么它会创建一个新类型的对象
3.如果不是,那么它会创建一个原有类型的对象
覆盖发生时,可以使用“类型覆盖”或者"实例覆盖"
1.类型覆盖指,UVM层次结构下的所有原有类型都被覆盖类型所替换。
2.实例覆盖指,在某些位置中的原有类型会被覆盖类型所替换。
二、覆盖方法
set_type_override()
static function void set_type_override(uvm_object_wrapper override_type,bit replace=1);
- uvm_object_wrapper override_type
它并不是某一个具体实例的句柄,实际上是注册过后的某一个类在工厂中注册时的句柄。怎么找到它呢?就使用new_type::get_type()。 - bit replace
1:如果已经有覆盖存在,那么新的覆盖会替代旧的覆盖。
2:如果已经有覆盖存在,那么该覆盖将不会生效。 - set_type_override是一个静态函数
orig_type::type_id::set_type_override(new_type::get_type())
orig_type是原有的类型,new_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::set_{type,inst}_override{_by_type}
uvm_component_registry::set_{type,inst}_override
uvm_object_registry::set_{type,inst}_override
uvm_factory::set_{type,inst}_overrdie
因此,想要实现类型替换,也有不止一种方式。包括上述给的例子中通过orig_type::type_id来调用覆盖函数,还可以在uvm_component的域中直接调用,或者使用uvm_factory来做覆盖。
四、覆盖实例
module factory_override;
import uvm_pkg::*;
`include "uvm_macros.svh"
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);
$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
利用questasim仿真结果如下:
- 因为c1用的是new而没有通过工厂,所以C1没有替换,而C2通过工厂,所以C2替换了。此外,如果想要完成override,继承很重要。
- 需要注意这里出现了三个 is created,原因是super。comp2是comp1的子类,用super继承了父类的方法和变量。因此在创建c2时,会使用父类的new中的display,故而产生了comp1::c2 is created。
五、确保正确覆盖的代码要求
- 将UVM环境中所有的类都注册到工厂中,并通过工厂来创建对象;
- 在使用某些类的时候,确保该类已经被导入(import)到当前域(scope)中;
- 通过工厂创建对象时,句柄名称应该同传递到create()方法中的字符串名称相同。无论是通过层次路径名称来覆盖还是配置,将例化组件中的句柄名称同创建时create()方法中的字符串名称保持一致;
- 由于覆盖是采用parent wins模式,因此要注意在同一个顶层build_phase()中覆盖方法应发生在对象创建之前;
- 为了尽量保持运行时覆盖类可以替换原始类,覆盖类最好是原始类的子类,而调用成员方法也应当声明为虚方法;
- 另外一种确保运行时覆盖类型句柄正确使用的方式,需要通过$cast()进行动态类型转换
parent win的两个含义
- 覆盖要发生在创建之前。
- 如果有两个以上的地方对同一个类型都做了override,层次越高优先级越高
六、建议
UVM学习中的一大阻碍就是,实现某一种效果的方法有很多种,但是对于初学者,只需要掌握最常用的一种实现方式,因为我们最终需要掌握UVM世界的全貌,而不是研究全部的用法,毕竟我们时间有限。
适用于 object
替换 set_type_override_by_type(orig::get_type(),new::get_type());
适用于 component
替换 set_type_override(“orig”,“new”);
关注作者
- 自述
作者是一位中科大数字设计专业的研究生,水平有限,如有错误,请大家指正,想要与大家一同进步。 - 经历
曾获得国家奖学金,“高教社杯”数学建模国家二等奖等 - 陆续更新:
1.与UVM验证相关的system verilog后续内容;
2.与verilog数字设计相关的一些基础模块设计,例如FIFO,UART,I2C等的书写。
3.保研与竞赛经历等 - 微信公众号
欢迎大家关注公众号“数字IC小白的日常修炼”,期待与大家一同仗剑遨游数字IC世界。