一、工厂机制——覆盖(override)
- 覆盖机制可以将原来所属的类型替换为另一个新的类型。
- 在覆盖之后,原本原来创建原属类型的请求,将由工厂来创建新的替换类型。无需再修改原始代码,继而保证了原有代码的封装性。新的替换类型必须与被替换类型相兼容,否则稍后的句柄赋值将失败,所以使用继承。
- 做顶层修改时非常方便,允许灵活的配置,例如可使用子类来覆盖原本的父类,可使用不同的对象来修改其代码行为。
- 要实现覆盖的特性,原有类型和新类型都需要注册。
- 当使用create()来创建对象时,工厂会检查是否原有类型被覆盖,如果是则会创建一个新类型的对象,如果没有,那么会创建一个原有类型的对象。
- 覆盖发生时,可以使用“类型覆盖”或者“实例覆盖”。“类型覆盖”是指UVM层次结构下所有原有类型都被覆盖类型所替换。“实例覆盖”是指,在某些位置中的原有类型会被覆盖类型所替代。
二、覆盖方法
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
1:如果已经有覆盖存在,那么新的覆盖会替代旧的覆盖。
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}来作为目标路径。
set_inst_override()
这是一个静态函数,使用方式orig_type::type_id::set_inst_override(new_type::get_type(),“orig_inst_path”)
需要注意的是,有不止一个类提供与覆盖有关的函数,然而名称与参数列表可能各不相同。因此,需要实现类型替换,也有不止一种方式,可以通过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
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
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
仿真结果:
c1和c2都是comp1类型,但是c1是通过new()
函数创建的,所以调用c1.hello()
打印comp1::c1 said hello!,而c2是通过工厂创建的,调用c2.hello()
打印comp2::c2 said hello!。这说明set_type_override()
影响到了由工厂创建的对象,工厂会进行类型替换。
我们只执行
initial begin
comp1::type_id::set_type_override(comp2::get_type());
c2 = comp1::type_id::create("c2", null);
end
跑一下仿真,看一下打印出来的日志
可以看出,当通过工厂创建对象c2时,comp1类型会被comp2类型覆盖,但是仍然会调用comp1的new()
打印comp1::c2 said hello!,然后再调用comp2的new()函数打印出comp2::c2 said hello!。所以在有了注册类型词典和覆盖类型队列的信息之后,当c2通过factory创建时,会查看被创建类型是否已经被覆盖,如果被覆盖则从uvm_default_factory::m_type_overrides
中取得覆盖类型的信息。comp1::type_id::set_type_override(comp2::get_type())
使得comp2覆盖了comp1类型,紧接着对c1和c2对象进行了创建,可以从输出结果看到,c1的所属类型仍然是comp1,c2的所属类型则变成了comp2,这说明了factory的覆盖机制只会影响通过factory注册并且创建的对象。所以通过type_id::create()
和factory
的类型覆盖可以实现对象所属类型在例化时的灵活替换。在例化c2之前,首先应该用comp2来替换comp1的类型,只有先完成了类型替换,才可以在后面的例化时由factory选择正确的类型。在后面发生了类型替换以后,如果原有的代码不做更新,那么c2句柄的类型仍然为comp1,但是却指向了comp2类型的对象,这就要求comp2应该是comp1的子类,只有这样,句柄的指向才是安全合法的。c2在调用hello()
方法时,由于首先是comp1类型,那么会查看comp1::hello()
,又由于该方法在定义时被指定为虚函数,这就通过了多态性的方法调用,转而调用了comp2::hello()
函数,因此,打印的结果是comp2::c2 said hello!