SystemVerilog 对重载的支持
在SystemVerilog中,给函数或者任务前添加virtual关键字,就能够在子类中重载这个函数或者任务。重载的优势在于,可以让子类的指针以父类的类型传递时,仍然保持子类的行为。
class bird extends uvm_object;
virtual function void hungry();
$display("I am a bird, I am hungry");
endfunction
endclass
class parrot extends from bird;
virtual function void hungry();
$display("I am a parrot, I am hungry");
endfunction
endclass
/*调用hungry*/
function void my_case0::print_hungry(bird b);
b.hungry();
endfunction
bird bird_inst;
parrot parrot_inst;
bird_inst = new();
parrot_inst = new();
print_hungry(bird_inst);/*输出bird*/
print_hungry(parrot_inst);/*输出parrot*/
同样,SystemVerilog也支持约束的重载,可以在派生类中重新定义约束。
使用factory 机制进行重载
利用factory机制进行重载,需要用到如下的函数。
set_type_override_by_type(bird::get_type(),parrot::get_type());
当把类注册到factory后,在实例化时,factory机制就会检查重载记录,如果有,则会利用新类型代替旧类型。使用这种重载机制,有几个要求。
- 要重载的类以及新的类都要注册到factory中;
- 在对类进行实例化时,需要采用factory机制的实例化方法;
- 两个类之间应当有派生关系,被重载的类应当是新类的父类;
- component与object之间不能重载。
除了上述一种函数,还有其他函数也能重载。
set_type_override_by_type(uvm_object_wrapper original_type,uvm_object_wrapper override_type,bit replace=1);
/*前两个参数,利用classname::get_type()获得,replace参数表示能否被后续重载覆盖*/
set_inst_override_by_type(string relative_inst_path,uvm_object_wrapper original_type,uvm_object_wrapper override_type);
/*针对某些实例重载,第一个参数表示相对路径*/
set_type_override(string original_type_name, string override_type_name,bit replace=1);
/*用字符串形式的类名重载*/
set_inst_override(string relative_inst_path,string original_type_name,string override_type_name);
/*针对某些实例重载,利用类名*/
另外还有其他函数,能够支持在无法使用component的地方实现factory重载。
同样,factory机制支持连续的重载,和覆盖重载。
factory机制提供了一些调试手段。
env.o_agt.mon.print_override_info("my_monitor");
extern function void print(int all_type=1);/*0,显示被重载的实例;1,多显示用户创建注册到factory的类;2,再多显示系统创建的,注册到factory中的类*/
uvm_top.print_topology();/*打印整棵UVM树的拓扑结构*/
factory重载是个很强大的功能,有了重载以后可以方便地派生新的子类,来拓展验证组件的功能,比如transaction,sequence,driver等等。
callback机制的使用
uvm中提供了更方便的callback机制。
/*定义派生类A*/
class A extends uvm_callback;
virtual task pre_tran(my_driver drv, ref my_transaction tr);/*需要是virtual,后续要重载*/
endtask
endclass
/*定义一个A_pool类,声明是A类的callback,且my_driver会用到*/
typedef uvm_callbacks#(my_driver,A) A_pool;
typedef class A;
class my_driver extends uvm_driver#(my_transaction);
`uvm_component_utils(my_driver)
`uvm_register_cb(my_driver,A)/*指明my_driver及A*/
endclass
task my_driver::main_phase(uvm_phase phase);
while(1) begin
seq_item_port.get_next_item(req);
`uvm_do_callbacks(my_driver,A,pre_tran(this,req))/*调用pre_tran类的名字,具有pre_tran的类,pre_tran*/
drive_one_pkt(req);
seq_item_port.item_done();
end
endtask
作为用户,应该如下使用。
/*先从A派生一个类*/
class my_callback extends A;
virtual task pre_tran(my_driver drv,ref my_transaction tr);
...
endtask
endclass
/*实例化这个类,并加入A_pool,并指定给具体的driver*/
function void my_case0::connect_phase(uvm_phase phase);
my_callback my_cb;
super.connect_phase(phase);
my_cb = my_callback::type_id::create("my_cb");
A_pool::add(env.i_agt.drv,my_cb);
endfunction
/*可以用下面这个宏实现子类继承父类的callback;要在子类中使用*/
`uvm_set_super_type(new_driver,my_driver)
总结来说,UVM 里的callback分为几个步骤:
- 声明一个包含callback函数的基类(派生自uvm_callback)
- 用`uvm_register_cb(comp, cb)注册该基类以及使用该基类的component
- 根据需要扩展callback基类,主要是补充原本没有内容的callback函数或任务
- 利用uvm_callbacks #(comp,cb)::add(comp, cb_new)注册新的callback类
- 通过`uvm_do_callbacks(comp,cb,…)进行调用callback函数或任务