精益求精
在RVM、VMM、OVM/UVM中,常常提到callback这个概念。指在事先设置好的地方留下一个接口,通过向这个接口添加一些函数对象,来达到不改变代码结构而动态修改代码行为。下面用systemverilog来举一个简单的callback例子:
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/*
最简单的systemverilog callback实例
*/
class callback; // 用一个类来封装callback函数,systemverilog中函数不是第一类型,所以要封装
virtual task cb_pre_run( );
$display("base callback run");
endtask:cb_pre_run
endclass:callback
class widget;
callback cb_queue[$]; // 需要调用的callback队列
function void add_cb(callback cb);
cb_queue.push_back(cb);
endfunction:add_cb
task run();
// 在进行动作之前,先调用callback函数,做一些准备工作,或者故意加错等机制
foreach(cb_queue[i]) begin
cb_queue[i].cb_pre_run();
end
$display("widget run....");
endtask:run
endclass:widget
module top;
// class in module
class ext_callback extends callback;
task cb_pre_run();
$display("ext callback run");
endtask:cb_pre_run
endclass:ext_callback
widget w;
callback cb0;
ext_callback cb_ext;
initial begin
w = new;
cb0 = new;
cb_ext = new;
w.run;
w.add_cb(cb0); // 添加基类的callback
$display("=========== After Add base Callback");
w.run;
w.add_cb(cb_ext); // 添加扩展类callback
$display("=========== After Add extention Callback");
w.run;
end
endmodule; // top
在sysverilog中,没有函数指针的概念,因此必须将函数包装成为一个对象,就是上面例子中的callback class. 而为了在widget的对象中使用这个函数对象,必须事先在设计好的调用点对callback对象中的函数进行逐个调用(代码19-21行)。
widget的对象事先并不知道有多少callback对象,而是将所有的callback对象放到自己的一个callback对象队列中(cb_queue)。然后逐个对这个队列中的所有对象进行函数调用。
上面的程序编译执行后,结果如下:
widget run….
=========== After Add base Callback
base callback run
widget run….
=========== After Add extention Callback
base callback run
ext callback run
widget run….
可以看出,在执行的过程中,可以对widget对象进行动态的添加callback,从而动态的改变widget对象的动作。
上面的例子非常简单,仅仅是输出一些讯息而已,有一些局限:
这个callback结构并不能够真的改变widget对象的内部成员,以及处理的数据内容,仅仅能够输出一些讯息。
对每一个widget的对象,都需要单独添加相关callback对象,假如程序中又创建了一个新的widget对象,那么这个对象的callback queue初始是空的,也就是没有callback。必须再次添加才能让这个新的widget调用相应的callback功能。
callback只有一个地方,可以扩展到多个地方。另外也可以使用function,而不仅仅是task.