UVM——callback机制

参考

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类中,自定义要回调的虚函数。

1
2
3
4
5
6
7
8
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); //创建对象,这里有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); //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()); // 还是driver
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) //将回调基类注册到sqr中
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)

//---------------------------------------
// create, randomize and send the item to driver
//---------------------------------------
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));//在sequence中使用sqr中的回调函数。
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);//注册到sequencer中
endfunction : end_of_elaboration
endclass

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!