在使用synopsys的VIP时,会经常使用到VIP自带的callback方法,对DUT进行不同方式的注错,就和注水牛肉一样,通过针管向牛肉里注水达到增重的目的,而callback是通过driver向DUT注入错误,达到测试DUT遇到异常激励时的行为的目的(当然你说在monitor中能不能这么干,当然也是可以的,问题的关键在于你要做什么)。下面是如何在环境中使用callback的一个小例子:
1,建立一个callback class基类
class xxx_base_callback extends uvm_callback;
`uvm_object_utils(xxx_base_callback)
function new (string name = "xxx_base_callback");
super.new(name);
endfunction
virtual task print_item(xxx_item item);
endclass
类似于抽象类,这个基类就相当于一个模板,我们所有要用到的callback类都扩展于此,通过重载print_item 这个函数来实现不同的callback内容。
2,扩展这个基类,制作第一个callback类
class xxx_print_callback extends xxx_base_callback;
`uvm_object_utils(xxx_print_callback)
function new (string name = "xxx_print_callback");
super.new(name);
endfunction
virtual task print_item(xxx_item item);
item.print();
endtask : print_item
endclass
上面这个 callback class中我们重载了 print_item 函数,在函数内用自带的print函数将 sequence_item 打印了出来。
3,在driver中注册这个callback方法
typedef class xxx_base_callback
//注意这里很重要,相当于告诉driver,我这里有一个class类型的变量要用,你先记着,我后面告诉你什么,
//不然在这里会出现编译的问题,简单的说一下,有时候,我们会在callback类中的 task内做一些奇怪的事情,
//比如说把driver传进来,然后再用driver中的一些信号标志位,用来注入错误,为了避免编译driver时
//callback还未定义的情况(callback肯定会在后面编译进来),所以这里提前编译了callback
class xxx_tx_driver extends uvm_driver#(xxx_item);
`uvm_component_utils_begin(xxx_tx_driver)
...
...
...
`uvm_component_utils_end
`uvm_register_cb(xxx_tx_driver,xxx_base_callback)
virtual task main_phase(uvm_phase phase);
seq_item_port.get_next_item(item);
`uvm_do_callbacks(xxx_tx_driver,xxx_base_callback,print_item(item))
seq_item_port.item_done();
endtask : main_phase
endclass
我们对比看一段源码中的例子:
//| virtual class mycb extends uvm_callback;
//| virtual function void doit();
//| endclass
//|
//| class my_comp extends uvm_component;
//| `uvm_register_cb(my_comp,mycb)
//| ...
//| task run_phase(uvm_phase phase);
//| ...
//| `uvm_do_callbacks(my_comp, mycb, doit())
//| endtask
//| endclass
//-----------------------------------------------------------------------------
`define uvm_register_cb(T,CB) \
static local bit m_register_cb_``CB = uvm_callbacks#(T,CB)::m_register_pair(`"T`",`"CB`");
这部分完全仿照源码中的例子去实现callback再driver中的注册和使用。有一点不懂的时,为什么这里要使用callback的基类?
4,在testcase中创建并登记callback
class xxx_testcase extends uvm_test
xxx_print_callback xxx_print_callback_u;
`uvm_component_utils(xxx_testcase)
function new (string name = "xxx_testcase", uvm_component parent = null)
super.new(name,parent);
endfunction : new
virtual function build_phase(uvm_phase phase);
super.build_phase(phase);
xxx_print_callback_u = xxx_print_callback::type_id::create("xxx_print_callbakc_u");
endfunction : build_phase
virtual function connect_phase(uvm_phase phase);
super.connect_phase(phase);
uvm_callbacks#(xxx_tx_driver,xxx_print_callback)::add(env.agent.driver,xxx_print_callback_u);
endfunction : connect_phase
endclass xxx_testcase
至此,uvm_callback的简单的使用方法就介绍完了,但是在看源码时,还有很多关于callback的方法,像display , delete,等等的方法,仅仅一个非常小的功能,当你阅读源码时发现这仅仅时冰山一角,隐藏在整个UVM源码背后的东西何其多也!!!还有很多我没用到的东西需要我去探索。