SystemVerilog中的callback(回调)
在第二次systemverilog实验中,我看到有同学用到了callback函数,今天就是简单讲讲这个方法。
1、什么是callback
callback是SystemVerilog学习者的主要困惑点之一。许多人在许多论坛上都提出了相同的问题,但答案似乎并不能尽如人意。
我们可以将数据成员传递给任何函数。现在考虑一种情况,将一个函数(例如func1)作为数据成员传递给另一个函数(例如func2),并且得到所谓的callback。之所以称为callback,是因为函数func2现在可以在其代码函数func1中的任何地方调用。
这个是一个基类,其中:
-
temp是一个方法
-
方法temp中的一些语句还调用了方法callback_1和callback_2,在这其中的两个方法都是虚方法,并不含有任何逻辑。
-
用户可以在派生类中将所需逻辑添加到方法callback_1和callback_2,不需要更改方法temp。
例如,“randomize”是systemverilog中的一个带有callback的内建方法。randomize方法通过在randomize()前后分别调用pre_randomize()和post_randomize()去实现callback。
方法将按照下面提到的顺序执行,
-
pre_randomize();
-
randomize();
-
pre_randomize();
2、如何实现callback
实现systemverilog中callback的一种方式如下:
-
编写一个方法,并且其中调用了其他的虚方法
-
编写被调用的虚方法,此方法中一般不含有任何逻辑
3、如何使用callback
方法如下:
-
派生类并且实现callback方法,重写虚方法的内容
-
通过派生类覆盖基类
4、使用范例
class abc_transactor; virtual task pre_send(); endtask virtual task post_send(); endtask task xyz(); // Some code here this.pre_send(); // Some more code here this.post_send(); // And some more code here endtask : xyz endclass : abc_transactor class my_abc_transactor extend abc_transactor; virtual task pre_send(); ... // This function is implemented here endtask virtual task post_send(); ... // This function is implemented here endtask endclass : my_abc_transactor
上述代码中,基类含有3个task,其中2个task声明为virtual并且没有任何逻辑,但是这2个task都被另外的task xyz()调用,而xyz()是已经有逻辑代码了的,在这其中,这2个virtual task就被称为callback class。
my_abc_transactor派生自abc_transactor类,并且实现了基类中没有添加任何逻辑的task,这样我们可以直接把需要执行的代码添加到virtual task中而不需要对其进行修改。
上图实现的是一个slaver driver,用来对master进行反馈。
其中包括以下组件:
-
slave_driver - Normal driver to drive response
-
响应类型为 OKAY, EXOKAY, SLVERR, DECERR
-
slave_driver 被限制为始终发送OKAY响应以查看回调用法差异
-
-
slave_env -在其中创建了slave_driver的环境
-
basic_test - 发送正常响应
-
error_test - 具有回调方法的测试用例,用于生成错误响应
-
err_inject - 扩展的驱动程序类,用于实现回调方法
首先,编写slave_driver,并在其中添加空方法,放置挂钩以进行回调,在此示例中,由于需要在响应生成后立即对其进行更改,因此最好在调用randomize方法之后放置回调挂钩:
typedef enum {OKAY, EXOKAY, SLVERR, DECERR} resp_type; class slave_driver; resp_type resp; //callback hook virtual task update_resp; endtask //send response task task send_response; std::randomize(resp) with { resp == OKAY;}; update_resp();//hook endtask endclass实现callback,首先派生类,完善virtual task:
class err_inject extends slave_driver; virtual task update_resp; $display("Injecting SLVERR"); resp = SLVERR; endtask endclass使用callback,生成error_test:
program error_test; slave_env env; err_inject err_driver; initial begin //Create env env = new(); err_driver = new(); //Overriding slave_driver by error_driver env.slv_driver = err_driver; //Calling run of env env.run(); end endprogram其中slave_env如下:
class slave_env; slave_driver slv_driver; function new(); slv_driver = new(); endfunction //run task to call driver logic task run; repeat(2) begin //{ slv_driver.send_response(); $display("Slave generated response is %s",slv_driver.resp.name()); end //} endtask endclass此外,还添加basic_test如下,以便进行比对:
program basic_test; slave_env env; initial begin //Create env env = new(); //Calling run of env env.run(); end endprogram
在Synopsys VCS 2019.06下进行仿真
-
当执行basic_test时,输出如下:
-
当执行error_test时,输出如下:
可见,我们通过调用改变派生类中的virtual task中的内容,可以实现我们特定的内容。可以在不改变现有环境的情况下就实现错误的注入,因此好处如下:
-
易于向现有逻辑添加其他功能
- 使组件可重用,扩展类的功能
欢迎点赞,关注,分享~~, 本文原发于微信公众号【 数字ic小站 】