参考
UVM_CALLBACK—VerificationGuide网站
《UVM实战1》
1. 介绍
Where callbacks can be implemented?
Callbacks can be implemented in an object or component.
What are the benefits of callback?
Different flavors of the component can be obtained by customizing the empty callback methods.
About UVM Callbacks
UVM provides a set of classes, methods, and macros to implement the callbacks.
Only the required callback methods are explained here. refer to UVM Callback Classes for UVM Callback classes, methods, and macros.
2. callback
callback中的机制分成三步:
- Adding callback support
- Implementing the callback methods
- Using callback
2.1 Adding callback support
自定义callback类,继承自uvm_callback;在callback类中,自定义要回调的虚函数。
| class driver_callback extends uvm_callback; `uvm_object_utils(driver_callback) function new(string name = "driver_callback"); super.new(name); endfunction virtual task pre_drive; endtask virtual task post_drive; endtask endclass
|
用宏`uvm_register_do将callback类注册到将要被使用到的component类。
将回调钩子(callback hook)放到要使用callback的object或者component类中。(或许只能放在component中)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class driver extends uvm_component; `uvm_component_utils(driver) `uvm_register_cb(driver,driver_callback) function new(string name, uvm_component parent); super.new(name,parent); endfunction task run_phase(uvm_phase phase); `uvm_do_callbacks(driver,driver_callback,pre_drive()); drive_pkt(); `uvm_do_callbacks(driver,driver_callback,post_drive()); endtask task drive_pkt(); `uvm_info("DRIVER","Inside drive_pkt method",UVM_LOW); endtask endclass
|
2.2 Implementing the callback methods
之前的那一步是VIP设计者要做的工作,在代码中预留出一些接口(回调钩子),供程序使用者扩展功能。接下来的两小节是VIP使用者(验证师)要做的。
用户自定义回调类,继承自上面的driver_callback类。
重载回调的虚函数。
1 2 3 4 5 6 7 8 9 10 11 12
| class user_callback extends driver_callback; `uvm_object_utils(user_callback) function new(string name = "user_callback"); super.new(name); endfunction task pre_drive; `uvm_info("USER_CALLBACK","Inside pre_drive method",UVM_LOW); endtask task post_drive; `uvm_info("USER_CALLBACK","Inside post_drive method",UVM_LOW); endtask endclass
|
2.3 Using callback
在testcase中:
1. 定义user_callback回调类。
1
| user_callback callback_1;
|
2. 创建回调对象。
1
| callback_1 = user_callback::type_id::create("callback_1", this);
|
3. 将回调对象添加到要使用回调的component中(这里是driver)的回调池子。
1
| uvm_callbacks#(driver,driver_callback)::add(env.driv,callback_1);
|
类的参数类型和两个参数对应。
UVM会自动调用回调池子中所有对象的回调函数。类似于SV中的回调,在SV中回调池子是一个回调基类的队列。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class user_callback_test extends basic_test; user_callback callback_1; `uvm_component_utils(user_callback_test) function new(string name = "user_callback_test", uvm_component parent=null); super.new(name,parent); endfunction function void build_phase(uvm_phase phase); super.build_phase(phase); uvm_config_db#(uvm_object_wrapper)::set(this,"env0.i_agt.sqr.run_phase","default_sequence",sequence0::type_id::get()); endfunction function void connect_phase(uvm_phase phase); callback_1 = user_callback::type_id::create("callback_1", this); uvm_callbacks#(driver,driver_callback)::add(env.driv,callback_1); endfunction endclass
|
这里一定要注意,启动sequence写在build phase中,而callback_1对象在connect_phase中创建、添加,如果都写在build phase中,会出错。
3. 注册了多个callback类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class user_callback_2_test extends basic_test; user_callback_1 callback_1; user_callback_2 callback_2; `uvm_component_utils(user_callback_2_test) function new(string name = "user_callback_2_test", uvm_component parent=null); super.new(name,parent); endfunction function void build_phase(uvm_phase phase); super.build_phase(phase); callback_1 = user_callback_1::type_id::create("callback_1", this); callback_2 = user_callback_2::type_id::create("callback_2", this); uvm_callbacks#(driver,driver_callback)::add(env.driv,callback_2); uvm_callbacks#(driver,driver_callback)::add(env.driv,callback_1); endfunction endclass
|
输出:
1 2 3 4 5 6
| UVM_INFO @ 0: reporter [RNTST] Running test user_callback_2_test... UVM_INFO user_callback.sv(39) @ 0: reporter [USER_CALLBACK-2] Inside pre_drive method UVM_INFO user_callback.sv(18) @ 0: reporter [USER_CALLBACK-1] Inside pre_drive method UVM_INFO slave_driver.sv(23) @ 0: uvm_test_top.env.driver [DRIVER] Inside drive_pkt method UVM_INFO user_callback.sv(43) @ 0: reporter [USER_CALLBACK-2] Inside post_drive method UVM_INFO user_callback.sv(22) @ 0: reporter [USER_CALLBACK-1] Inside post_drive method
|
user_callback_1、user_callback_2继承自driver_callback;上面代码中向driver添加了两个回调类,那么执行顺序是根据自定义回调类(user_callback_1、user_callback_2)注册到dirver(add函数)的顺序,来执行回调虚函数的。
4. 子类继承父类的回调机制
如果要在原来的验证环境基础上开发第二代验证环境,并且需要扩展my_driver的功能,新的new_driver继承自driver,我们希望new_driver中也可以用driver中的callback函数。
需要使用下面这个宏将子类父类关联起来。
1
| `uvm_set_super_type(new_driver,driver);
|
在new_driver中添加的回调钩子是原来的my_driver
1
| `uvm_do_callbacks(driver,driver_callback,pre_drive());
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class new_driver extends driver; `uvm_component_utils(new_driver) function new(string name, uvm_component parent); super.new(name,parent); endfunction task run_phase(uvm_phase phase); `uvm_do_callbacks(driver,driver_callback,pre_drive()); drive_pkt(); `uvm_do_callbacks(driver,driver_callback,post_drive()); endtask task drive_pkt(); `uvm_info("DRIVER","Inside drive_pkt method",UVM_LOW); endtask endclass
|
5. 在sequence中使用callback
拆成5步:
- Write callback class
- Register the callback class
- Place the callback hook
- Implement the callback method
- Create and register the callback object
5.1 Write callback class
1 2 3 4 5 6 7 8 9 10
| class mem_callback extends uvm_callback; `uvm_object_utils(mem_callback) function new(string name = "mem_callback"); super.new(name); endfunction virtual task update_pkt(ref mem_seq_item pkt); endtask endclass
|
5.2 Register the callback class
callback类需要注册在component中,因为sequence是动态的。将callback类注册在sequencer中。
1 2 3 4 5 6 7
| class mem_sequencer extends uvm_sequencer#(mem_seq_item); `uvm_component_utils(mem_sequencer) `uvm_register_cb(mem_sequencer,mem_callback) function new(string name, uvm_component parent); super.new(name,parent); endfunction endclass
|
5.3 Place the callback hook
回调钩子要放在sequence类中,因为是在sequence中回调。
`uvm_do_obj_callbacks的第一个参数是mem_sequencer,因为回调注册在mem_sequencer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| class mem_sequence extends uvm_sequence#(mem_seq_item); `uvm_object_utils(mem_sequence) function new(string name = "mem_sequence"); super.new(name); endfunction `uvm_declare_p_sequencer(mem_sequencer) virtual task body(); req = mem_seq_item::type_id::create("req"); wait_for_grant(); req.randomize(); `uvm_do_obj_callbacks(mem_sequencer,mem_callback,p_sequencer,update_pkt(req)); send_request(req); wait_for_item_done(); endtask endclass
|
5.4 Implement the callback method
自定义回调类和回调函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class user_callback extends mem_callback; `uvm_object_utils(user_callback) function new(string name = "user_callback"); super.new(name); endfunction task update_pkt(ref mem_seq_item pkt); `uvm_info("USER_CALLBACK","[update_pkt] before packet modification",UVM_LOW); pkt.print(); pkt.addr = ~pkt.addr; `uvm_info("USER_CALLBACK","[update_pkt] after packet modification",UVM_LOW); pkt.print(); endtask endclass
|
5.5 Create and register the callback object
将回调对象添加到回调注册的地方,在这里注册到了sequencer。所以在add函数中用的是sequencer而不是sequence。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class user_callback_test extends mem_test; user_callback callback_1; `uvm_component_utils(user_callback_test) function new(string name = "user_callback_test", uvm_component parent=null); super.new(name,parent); endfunction function void build_phase(uvm_phase phase); super.build_phase(phase); callback_1 = user_callback::type_id::create("callback_1", this); endfunction function void end_of_elaboration(); uvm_callbacks#(mem_sequencer,mem_callback)::add(env.mem_agnt.sequencer,callback_1); endfunction : end_of_elaboration endclass
|