SV——SV实现callback

0. 介绍

用SV实现回调的功能。

回调思路可以让我们在top层向验证平台注入代码到我们预留的“钩子”(hook)中,实现一些功能,比如:在driver回调可以实现错误注入,收集激励到scoreboard中,收集覆盖率等。

这篇文章介绍在driver中回调,实现对driver驱动的transaction的控制,可以产生不同的激励,比如错误激励。

1. 结构图

driver中有一个回调的“钩子”——是一个driver_cb类型的队列,在顶层将扩展driver_cb得到user_cb,在user_cb中重载一些回调函数,并将user_cb对象注入到这个队列里,就可以注入回调代码。

本文中,driver将transaction驱动到接口,外面没有DUT。

2. 文件结构

1
2
3
4
5
6
7
8
9
10
11
12
[classes]:
trans.sv
driver_cb.sv
driver.sv
generator.sv
enviroment.sv
user_cb.sv
cb_pkg.sv
top.sv
Makefile
filelist
bp_bfm.sv

3. transaction

我们的transaction如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class trans;

rand bit [7:0] A;
rand bit [7:0] B;
rand operation_e op;
string name;

function new(string name="trans");
this.name=name;
endfunction

constraint c_data {
A inside {[10:20]}; //A的范围是10到20
B inside {[30:40]};
}

constraint c_op {
op dist {
add_op:=1,
and_op:=1,
xor_op:=1,
mul_op:=1
};
}

function print();
$display("@%0t : %s :",$time,name);
$display("@%0t : A is %d",$time,A);
$display("@%0t : B is %d",$time,B);
$display("@%0t : op is %3b",$time,op);//enum do not need transform to bit[2:0]
endfunction

endclass

其中约束A的值的范围是10到20,我们在回调中会将其改成0到9.

4. 回调基类

1
2
3
4
5
6
7
8
9
10
11
class driver_cb;

virtual task pre_cb(ref trans tr); //空的虚函数
//do nothings
endtask

virtual task post_cb();
//do nothings
endtask

endclass

在回调基类中声明的回调函数是虚函数,不做任何事,用户自己定义回调类(继承自回调基类),利用OOP中的多态概念实现父类指针调用子类函数。

5. 驱动器driver

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class driver;
driver_cb cb_queue[$]; // 回调基类
trans driver_tr;
mailbox#(trans) gen2drv; // 用来和generator进行通信,传递transaction

virtual bp_bfm bfm; //虚接口,通过new函数得到真实的接口

function new(input mailbox#(trans) mlb,virtual bp_bfm bfm);
this.gen2drv=mlb; // 传递mailbox
this.bfm=bfm; //传递接口
endfunction

virtual task run();
while(1) begin
gen2drv.get(driver_tr); // 从generator得到transaction
foreach(cb_queue[i]) cb_queue[i].pre_cb(driver_tr); // 回调钩子
drive_one_pkg(driver_tr); //驱动transaction到接口,也就dut
foreach(cb_queue[i]) cb_queue[i].post_cb(); //回调钩子
end
endtask

virtual task drive_one_pkg(trans tr);
$display("@ %0t : driver one pkg!!!",$time);
tr.print();
@(posedge bfm.clk);
bfm.A = tr.A;
bfm.B = tr.B;
bfm.op = tr.op;
endtask

endclass

6. 用户自定义回调类

1
2
3
4
5
6
7
8
9
10
11
12
13
class user_cb extends driver_cb;

virtual task pre_cb(ref trans tr);
$display("@ %0t : modified A value!!!",$time);
tr.A = $urandom_range(0,9); //在重载的回调函数中,将A的值改为0到9的随机值,
//在transaction类中,A的值是10到20
endtask

virtual task post_cb();
$display("@ %0t : calling post_cb!!!",$time);
endtask

endclass

user_cb继承自driver_cb,并且重载其中的回调函数。

7. top层注入回调类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
`include "bp_bfm.sv"
module top;
import cb_pkg::*;

bp_bfm bfm();
enviroment env;

initial begin
env=new(bfm);
env.build();
begin
user_cb ucb; //定义回调类对象
ucb=new();
env.drv.cb_queue.push_back(ucb); //将回调对象注入到driver中的回调队列中。
end
env.run();
end
endmodule

8. 仿真结果

在下面打印值中可以看到,A的值在0到9之间,说明回调起作用了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@ 40000 : modified A value!!!
@ 40000 : driver one pkg!!!
@40000 : trans :
@40000 : A is 9
@40000 : B is 32
@40000 : op is 010
@ 50000 : calling post_cb!!!
@ 50000 : modified A value!!!
@ 50000 : driver one pkg!!!
@50000 : trans :
@50000 : A is 8
@50000 : B is 37
@50000 : op is 011
@ 70000 : calling post_cb!!!

仿真结果如下:

9. 代码

完整代码查看 》》》》 代码


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