芯有所想

精益求精

最简单的callback

在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对象的动作。

上面的例子非常简单,仅仅是输出一些讯息而已,有一些局限:

  1. 这个callback结构并不能够真的改变widget对象的内部成员,以及处理的数据内容,仅仅能够输出一些讯息。

  2. 对每一个widget的对象,都需要单独添加相关callback对象,假如程序中又创建了一个新的widget对象,那么这个对象的callback queue初始是空的,也就是没有callback。必须再次添加才能让这个新的widget调用相应的callback功能。

  3. callback只有一个地方,可以扩展到多个地方。另外也可以使用function,而不仅仅是task.