回调用于改变组件或对象的行为而不修改其代码。请参阅SystemVerilog callback - VLSI Verify以更好地理解。UVM 中的phasing机制就是回调的一个简单示例。
1 UVM Callback Usage
- 允许即插即用机制以建立可重用的验证环境。
- 基于钩子方法(hook method)调用,执行用户自定义的代码,而不是空的回调方法。这带来了组件或对象的各种风格。
- 回调可用于在组件中引入错误或延迟。
2 UVM Callback Macros
下面仅提及广泛使用的回调宏:
3 UVM Callback Classes
UVM 回调为其实现提供了一组类。
4 UVM Callback Methods
uvm_callback 方法可以使用范围解析运算符来调用,因为它们是静态方法。
例如: uvm_callbacks#(T, CB)::add(obj,cb);
其中:
T :使用用户定义回调的对象类型,它必须从 uvm_object 派生。
CB :用户自定义回调类型
obj : 使用用户定义回调函数的对象句柄
cb :用户定义回调对象
uvm_callbacks、uvm_callback_iter 和 uvm_callback 类还有许多其他方法,这里不讨论,常用方法和宏在上一节中介绍。
5 Steps to implement uvm_callback
1. 创建一个派生自uvm_callback类的用户自定义callback类。
class driver_cb extends uvm_callback;
2. 添加一个空的回调方法。
virtual task modify_pkt();
endtask
driver_cb回调类代码:
class driver_cb extends uvm_callback;
`uvm_object_utils(driver_cb)
function new(string name = "driver_cb");
super.new(name);
endfunction
virtual task modify_pkt();
endtask
endclass
3 基于上面用户定义的类来派生一个类并实现回调方法。
class derived_cb extends driver_cb;
`uvm_object_utils(derived_cb)
function new(string name = "derived_cb");
super.new(name);
endfunction
task modify_pkt; // callback method implementation
`uvm_info(get_full_name(),"Inside modify_pkt method: Injecting error pkt",UVM_LOW);
std::randomize(pkt) with {pkt inside {BAD_ERR1, BAD_ERR2};};
endtask
endclass
4 在调用回调的组件/component或对象/object中使用`uvm_register_cb注册用户定义的回调方法(在下面的示例中,它是在驱动程序组件中注册的)。
`uvm_register_cb(driver,driver_cb)
如果未使用`uvm_register_cb,则会发出警告。
例如:
UVM_WARNING @ 0: reporter [CBUNREG] Callback drvd_cb cannot be registered with object (*) because callback type derived_cb is not registered with object type uvm_object
5 使用`uvm_do_callbacks宏在所需组件或对象中放置回调挂钩,即调用回调方法
`uvm_do_callbacks(driver,driver_cb,modify_pkt());
Driver component code:
typedef enum {GOOD, BAD_ERR1, BAD_ERR2} pkt_type;
class driver extends uvm_component;
`uvm_component_utils(driver)
`uvm_register_cb(driver,driver_cb) // callback registration
function new(string name = "driver", uvm_component parent = null);
super.new(name,parent);
endfunction
task run_phase(uvm_phase phase);
super.run_phase(phase);
drive();
`uvm_do_callbacks(driver,driver_cb,modify_pkt()); // callback hook
endtask
task drive();
`uvm_info(get_full_name(),"Inside drive method",UVM_LOW);
std::randomize(pkt) with {pkt == GOOD;};
endtask
endclass
Output:
1. 当base_test执行时,(Run options: +UVM_TESTNAME=base_test)
class base_test extends uvm_test;
`uvm_component_utils(base_test)
env env_o;
function new(string name = "base_test", uvm_component parent = null);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
env_o = env::type_id::create("env_o", this);
endfunction
endclass
会产生一个GOOD包。
UVM_INFO @ 0: reporter [RNTST] Running test base_test...
UVM_INFO driver.sv(17) @ 0: uvm_test_top.env_o.drv [uvm_test_top.env_o.drv] Inside drive method
UVM_INFO driver.sv(13) @ 0: uvm_test_top.env_o.drv [uvm_test_top.env_o.drv] Driven pkt is GOOD
2. 当err_test执行时: (Run options: +UVM_TESTNAME=err_test)
class err_test extends base_test;
derived_cb drvd_cb;
`uvm_component_utils(err_test)
function new(string name = "err_test", uvm_component parent = null);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
drvd_cb = derived_cb::type_id::create("drvd_cb", this);
uvm_callbacks#(driver, driver_cb)::add(env_o.drv, drvd_cb);
endfunction
endclass
使用UVM callback,产生了一个error包,如下:
VM_INFO @ 0: reporter [RNTST] Running test err_test...
UVM_INFO driver.sv(17) @ 0: uvm_test_top.env_o.drv [uvm_test_top.env_o.drv] Inside drive method
UVM_INFO callbacks.sv(20) @ 0: reporter [drvd_cb] Inside modify_pkt method: Injecting error pkt
UVM_INFO driver.sv(13) @ 0: uvm_test_top.env_o.drv [uvm_test_top.env_o.drv] Driven pkt is BAD_ERR2
6 UVM callback in uvm_sequence
uvm_sequence中实现回调函数的步骤与上面的步骤相同。
请注意,`uvm_do_obj_callbacks宏被用作带有associated sequencer的回调钩子/callback hook。
`uvm_do_obj_callbacks(sequencer,seq_cb,l_seqr,modify_pkt(req));
6.1 UVM callback in uvm_sequence example
class base_seq extends uvm_sequence #(seq_item);
seq_item req;
sequencer l_seqr; // Provided sequencer hierarchy from base_test before starting the sequence.
`uvm_object_utils(base_seq)
function new (string name = "base_seq");
super.new(name);
endfunction
task body();
`uvm_info(get_type_name(), "Base seq: Inside Body", UVM_LOW);
req = seq_item::type_id::create("req");
wait_for_grant();
assert(req.randomize());
`uvm_do_obj_callbacks(sequencer,seq_cb,l_seqr,modify_pkt(req));
send_request(req);
wait_for_item_done();
endtask
endclass
Output:
1 当base_test执行时,(Run options: +UVM_TESTNAME=base_test)
class base_test extends uvm_test;
env env_o;
base_seq bseq;
`uvm_component_utils(base_test)
function new(string name = "base_test", uvm_component parent = null);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
env_o = env::type_id::create("env_o", this);
endfunction
task run_phase(uvm_phase phase);
phase.raise_objection(this);
bseq = base_seq::type_id::create("bseq");
bseq.l_seqr = env_o.agt.seqr;
bseq.start(env_o.agt.seqr);
phase.drop_objection(this);
endtask
endclass
产生一个GOOD包:
UVM_INFO @ 0: reporter [RNTST] Running test base_test...
UVM_INFO sequences.sv(11) @ 0: uvm_test_top.env_o.agt.seqr@@bseq [base_seq] Base seq: Inside Body
UVM_INFO driver.sv(15) @ 0: uvm_test_top.env_o.agt.drv [uvm_test_top.env_o.agt.drv] Driving pkt =
------------------------------
Name Type Size Value
------------------------------
req seq_item - @578
addr integral 16 'he157
data integral 16 'h96b
pkt pkt_type 32 GOOD
------------------------------
UVM_INFO /apps/vcsmx/vcs/Q-2020.03-SP1-1//etc/uvm-1.2/src/base/uvm_objection.svh(1276) @ 50: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
2 当err_test执行时:+UVM_TESTNAME=err_test
class err_test extends base_test;
derived_seq_cb drvd_seq;
`uvm_component_utils(err_test)
function new(string name = "err_test", uvm_component parent = null);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
drvd_seq = derived_seq_cb::type_id::create("drvd_seq", this);
endfunction
function void end_of_elaboration();
super.end_of_elaboration();
uvm_callbacks#(sequencer, seq_cb)::add(env_o.agt.seqr,drvd_seq);
endfunction : end_of_elaboration
endclass
使用UVM callback,产生一个error 包,如下:
UVM_INFO @ 0: reporter [RNTST] Running test err_test...
UVM_INFO sequences.sv(11) @ 0: uvm_test_top.env_o.agt.seqr@@bseq [base_seq] Base seq: Inside Body
UVM_INFO callbacks.sv(20) @ 0: reporter [drvd_seq] Inside modify_pkt method: Injecting error in the seq item
--------------------------------
Name Type Size Value
--------------------------------
req seq_item - @580
addr integral 16 'hffff
data integral 16 'h96b
pkt pkt_type 32 BAD_ERR1
--------------------------------
UVM_INFO driver.sv(15) @ 0: uvm_test_top.env_o.agt.drv [uvm_test_top.env_o.agt.drv] Driving pkt =
--------------------------------
Name Type Size Value
--------------------------------
req seq_item - @580
addr integral 16 'hffff
data integral 16 'h96b
pkt pkt_type 32 BAD_ERR1
--------------------------------
UVM_INFO /apps/vcsmx/vcs/Q-2020.03-SP1-1//etc/uvm-1.2/src/base/uvm_objection.svh(1276) @ 50: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase