SV中的回调(callback)明确了定义方法的规则,并且指定了一个方法用来回调已经定义的该方法;简而言之就是,被调用的方法就是回调方法(callback method),调用回调方法的方法称为回调勾(callback hook),回调方法一般是虚方法(dummy method),一般通过继承来进行覆盖重写.
我们来看回调方法是如何工作的:
在上面的图中,temp()方法就是回调勾,callback_1()和callback_2()方法是回调方法,也就是在用户调用temp()方法的时候,callback_1()和 callback_2()会自动调用;当然,用户也可以通过继承来覆盖回调方法;
在SV中最长用到的回调勾就是randomize()方法,它带有两个回调方法pre_randomize()和post_randomize(),这两个方法分别会在randomize()方法的前后执行.
UVM callback
在UVM中,我们通过uvm_callback来派生出回调方法类,在这个类中可以定义多个回调方法,这些方法都是虚方法.如下:
class driver_callback extends uvm_callback;
`uvm_component_utils(driver_callback)
function new(string name, uvm_component parent);
super.new(name);
endfunction
virtual task pre_drive; endtask//定义了两个虚方法作为回调方法
virtual task post_drive; endtask
endclass
在上面的回调类driver_callback中定义了两个回调方法pre_drive和post_drive.
为了使用定义的回调类中的回调方法,首先要通过宏`uvm_register_cb在相应的组件中注册,然后再使用宏`uvm_do_callbacks 在组件的方法中调用回调方法,如下所示:
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
所以简单的test如下:
class environment extends uvm_env;
driver driv;
`uvm_component_utils(environment)
function new(string name, uvm_component parent);
super.new(name,parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
driv = driver::type_id::create("driv", this);
endfunction
endclass
class basic_test extends uvm_test;
environment env;
`uvm_component_utils(basic_test)
function new(string name = "basic_test", uvm_component parent=null);
super.new(name,parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
env = environment::type_id::create("env", this);
endfunction
endclass
//testbench
`include "driver_callback.sv"
`include "driver.sv"
`include "environment.sv"
`include "basic_test.sv"
program testbench_top;
//---------------------------------------
//calling test
//---------------------------------------
initial begin//{
run_test();
end //}
endprogram
这样我们就将回调方法加入到整个testbench中了,只不过上面的回调方法为空方法,什么操作也没有;
还可以通过继承,覆盖上面定义的回调类中的回调方法,如下:
class user_callback extends driver_callback;
`uvm_component_utils(user_callback)
function new(string name, uvm_component parent);
super.new(name,parent);
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
对于派生的回调类以及其中覆盖的回调方法,只需要通过调用add()方法将回调类添加到test中即可,如下:
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);
callback_1 = user_callback::type_id::create("callback_1", this);
uvm_callbacks#(driver,driver_callback)::add(env.driv,callback_1);
endfunction
endclass
对于uvm_callbacks#(T, CB)::add(t,cb)方法,其几个参数的含义如下:
T:表示回调类所在的组件名;
CB:表示回调类的类名;
t:表示回调类所在的组件的实例名,也就是组件的对象名;
cb:表示回调类的实例名,也就是对象名;
导入上面的模块,如下:
`include "driver_callback.sv"
`include "driver.sv"
`include "environment.sv"
`include "basic_test.sv"
`include "user_callback.sv"
`include "user_callback_test.sv"
program testbench_top;
//---------------------------------------
//calling test
//---------------------------------------
initial begin//{
run_test();
end //}
endprogram
运行结果为:
总结上面在UVM中使用回调的方法步骤:
(1) 由uvm_callback派生出一个回调类A,这个类中包含若干个回调方法,且这些方法都是空的;
(2)将回调类A在对应的组件中通过宏`uvm_register_cb在相应的组件中注册,然后再使用宏`uvm_do_callbacks 在组件的方法中调用回调方法;
(3)由A派生出回调类的子类B,在该类中对回调方法进行覆盖重写,可以用户自定义回调方法的操作;
(4)在test中将B实例化,并将实例化后的对象通过uvm_callbacks#(T, CB)::add(t,cb)方法添加到组件中,这就实现了回调方法的具体操作;