什么是callback?它有什么作用呢?
引用自《UVM实战》第九章 UVM中代码的重用性:验证人员希望在在一个项目中开发的验证平台能够用于另一个项目,但是,完全重用是很难实现的,不同项目多少存在差异。但是,将两个项目不同的地方使用callback函数来做,而将相同的地方写成一个完整的env,重用时只要改变callback函数env就可以完全重用。
上代码来说明什么是callback
参考硅芯思见,很不错的博主
代码架构如下:
- cbb是callback函数的基类(这个类是一个虚类或者说是抽象类,可以被扩展不能被实例化,只有扩展类中所有虚方法都有实体时,才能被实例化。将实例化的callback类的句柄写入 driver 组件的callback_pool中,相当于一个池子,放各种不同的被扩展的callback类。
- ecbb重载了cbb中的pre post方法
- env中实例化了 generator 和 driver 两个组件,作为可以复用的模块,generator 将 transaction 的句柄通过 mailbox 送给了driver
- driver 组件中会有相应的地方从callback_pool中取出callback类并调用方法,这些地方成为hook(鱼钩)。
code:
top_tb.sv
`timescale 1ns / 1ps
module test(input [7 : 0] data, input [1 : 0] addr);
endmodule
interface bitf(input bit clk, input bit rst_n);
bit [7 : 0] data;
bit [1 : 0] addr;
endinterface
package tpkg;
`include "transaction.sv"
`include "cbb.sv"
`include "ecbb.sv"
`include "driver.sv"
`include "generator.sv"
`include "enviroment.sv"
endpackage
module top_tb;
import tpkg::*;
bit clk;
bit rst_n;
initial begin
clk = 1'b0;
forever #1 clk = ~clk;
end
initial begin
rst_n = 1'b0;
#10 rst_n = 1'b1;
end
bitf itf(clk, rst_n);
enviroment env;
initial begin
env = new(itf, 1);
env.wait_opt();
env.gend('h10);
begin
ecbb pecbb = new();//类的实例化必须紧挨begin,否则编译错误
env.drv.cbb_pool.push_back(pecbb);
end
begin
ecbb pecbb1 = new();
env.drv.cbb_pool.push_back(pecbb1);
end
env.sim();
repeat(10) @(posedge clk);
$stop;
end
test u_dut(itf.data, itf.addr);
`ifdef FSDB
initial begin
$fsdbDumpfile("top.fsdb");
$fsdbDumpvars;
$fsdbDumpMDA();
end
`endif
endmodule
cbb.sv
virtual class cbb;
virtual task pre_fcallback(ref transaction tr, ref bit drop);
endtask
virtual task fcallback(ref transaction tr);
$display("fcallback between pre and post");
endtask
virtual task post_fcallback(ref transaction tr);
endtask
endclass
ecbb.sv
class ecbb extends cbb;
virtual task pre_fcallback(ref transaction tr, ref bit drop);
$display("hello pre");
$display(tr);
endtask
virtual task post_fcallback(ref transaction tr);
$display("hello post");
tr.data = ~tr.data;
tr.addr = ~tr.addr;
$display(tr);
endtask
endclass
transaction.sv
class transaction;
rand bit [7 : 0] data;
rand bit [1 : 0] addr;
endclass
generator.sv
class generator;
transaction tr;
mailbox mbx;
function new(mailbox mbx);
this.mbx = mbx;
endfunction
task send(input int len);
$display("generator start sending!");
for (int i = 0; i < len; i++)begin
tr = new();
assert(tr.randomize());
mbx.put(tr);
$display("new data is %h", tr.data);
$display("new addr is %h", tr.addr);
end
$display("generator end sending!");
endtask
endclass
driver.sv
class driver;
cbb cbb_pool[$];
mailbox mbx;
transaction tr;
bit drop;
virtual bitf itf;
function new(mailbox mbx, virtual bitf itf, input int drop);
this.mbx = mbx;
this.itf = itf;
this.drop = drop;
endfunction
task run();
while(mbx.num())begin
mbx.get(tr);
foreach(cbb_pool[i])begin//hook
cbb_pool[i].pre_fcallback(tr, drop);
end
if(!drop) continue;
send(tr);
foreach(cbb_pool[i])begin//hook
cbb_pool[i].post_fcallback(tr);
end
end
endtask
task send(transaction tr);
@(posedge itf.clk);
this.itf.data = tr.data;
this.itf.addr = tr.addr;
endtask
endclass
enviroment.sv
class enviroment;
generator gen;
driver drv;
mailbox mbx;
virtual bitf itf;
function new(virtual bitf itf, input int drop);
this.itf = itf;
mbx = new();
gen = new(mbx);
drv = new(mbx, itf, drop);
endfunction
task wait_opt();
@(posedge itf.rst_n);
endtask
task gend(input int len);
gen.send(len);
endtask
task sim();
drv.run();
endtask
endclass
make后的结果:(部分)
从程序回到书本,我们再深化对callback的认识
将systemverilog开发过程视为项目A,那么用户使用开发就是项目B,A的开发者预料到了B可能会在env.driver.send()函数的前后完成一些别的事情,因此在此函数的前后添加了callback的hook,B如A所料,扩展了callback类,重载了函数,并注册了扩展类(将callback扩展类的句柄放入callback_pool中,等待hook处的调用)。
UVM中的callback机制
以上代码其实已经实现了callback机制基于systemverilog的简单开发。那么UVM中如何开发callback机制又如何作为用户使用开发过的callback机制呢?
- 首先如何去开发一个callback机制?