UVM中的factory机制及callback机制 | UVM笔记 5

Posted by Kion on January 31, 2020

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机制就会检查重载记录,如果有,则会利用新类型代替旧类型。使用这种重载机制,有几个要求。

  1. 要重载的类以及新的类都要注册到factory中;
  2. 在对类进行实例化时,需要采用factory机制的实例化方法;
  3. 两个类之间应当有派生关系,被重载的类应当是新类的父类;
  4. 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分为几个步骤:

  1. 声明一个包含callback函数的基类(派生自uvm_callback)
  2. 用`uvm_register_cb(comp, cb)注册该基类以及使用该基类的component
  3. 根据需要扩展callback基类,主要是补充原本没有内容的callback函数或任务
  4. 利用uvm_callbacks #(comp,cb)::add(comp, cb_new)注册新的callback类
  5. 通过`uvm_do_callbacks(comp,cb,…)进行调用callback函数或任务