SystemVerilog中的callback机制
Callback机制是SystemVerilog面向对象特性实现的一种由程序开发者预留一个callback函数/任务接口(hook),程序使用者通过该接口实现对程序开发者开发的验证环境进行重用的一种机制,一种广义callback机制的结构示意图如下:
object_callback是一个类,其中定义了两个虚方法pre_callback和post_callback,这两个虚方法中不包含任何内容,此处是预留给用户进行重载修改的接口方法,当用户不对object_callback进行派生时,因为pre_callback和post_callback是空的,不会执行任何操作。验证环境的用户可以通过对object_callback进行派生后,在派生类中对pre_callback和post_callback进行重写实现自己期望的功能逻辑而不需要对验证环境中的object_callback进行任何修改。在对pre_callback和post_callback进行重写后,再次执行object_callback时就会执行修改后的pre_callback和post_callback的行为。
那么如何使用SystemVerilog的OOP特性构建callback机制呢?下面我们通过示例逐步实现callback。
【第一步】创建回调基类
在SystemVerilog中,抽象类对于后续的扩展类除了提供可重用或覆盖的现有功能之外,抽象类对于定义扩展类的约定(即扩展类必须实现特定功能)也很有用。回调基类cbb之所以使用抽象类,因为实际使用中,将会有多个不同的子类将派生自回调基类。在回调基类中定义了回调任务(pre_fcallback和post_fcallback),定义的回调任务(pre_fcallback和post_fcallback)均为虚方法,并且任务体均为空,之所以定义为虚方法是为了后续扩展回调基类后,通过句柄引用时可以任务进行重载,而之所以在这里没有使用纯虚方法,是因为派生自抽象类的派生类如果要实例化必须要实现所有父类中的纯虚方法,然而实际使用时,不一定在派生类中对所有的函数(任务)进行重写。
【第二步】插入回调基类
示例中,我们要在driver中插入回调类。首先在driver中声明类型为cbb(回调基类)的队列cbb_pool[$],用于后续存放回调基类或者其派生类。17行到19行,调用队列cbb_pool中成员的回调方法pre_fcallback,22行到24行调用队列cbb_pool中成员的回调方法post_fcallback,因为在没有对回调基类进行扩展,并且也没有回调基类派生类压入队列cbb_pool中,即cbb_pool为空,所以foreach循环不会执行。后续将派生类压入队列后,当环境运行时,因为cbb_pool不为空,就会执行对应的回调方法(pre_fcallback和post_fcallback)。此处一般为验证环境开发人员提供给验证环境用户的回调方法的入口hook。
【第三部】派生回调基类
原回调基类进行扩展,在扩展类中对回调基类中需要重写的方法进行重写,可以按照用户的需要实现特定的行为。一般情况下,该扩展类为验证环境用户根据实际情况开发。示例中分别对回调基类中的虚方法进行了重写。
【第四部】使用回调机制
第23行创建了派生类ecbb的对象,第24行将创建的对象压入driver中声明的回调基类队列cbb_pool中(这里注意,第22行到25行之所以使用begin...end主要是这里需要声明一个新的局部变量pecbb,可参考《硅心思见:【111】局部变量声明不能太随意》)。当后续程序调用driver中的方法run时,其中的cbb_pool因为不为空,就会执行此时压入队列中的pecbb,而pecbb指向的对象中方法pre_fcallback和post_fcallback均被重写,所以此时就会执行重写后的pre_fcallback和post_fcallback。整个过程中,验证环境的用户不需要对原有验证环境进行修改,即可在数据的传输过程中实现特定的功能。至此,一个简单的回调机制的实现就完成了。
【注意】上述示例其他代码见文末附图。
回调机制其实是使用SystemVerilog OOP实现的一种验证环境开发者向验证环境使用者提供的模块内部接口hook,是一种重要的重用机制,也是UVM中的一种重要机制,其在UVM中的实现过程与本文示例相同,但是UVM实现的过程相当复杂,但基本原理与本文示例基本相同,使用的基本操作流程也基本一样,在UVM中针对验证环境的开发者和使用者,需要分别完成以下几步以实现回调机制。此处参考《UVM实战》
【验证环境开发人员】
1 定义一个A类;
2 声明一个A_pool,用于指明该pool被哪个类使用;
typedef uvm_callbacks #(my_driver,A) A_pool;
3 在要预留callback方法接口的类中调用uvm_register_cb宏;
【验证环境使用人员】
1 从A类派生一个类,在派生类中定义好pre_tran;
【本文callback示例中剩余代码如下】
// 待测设计,为一个空壳
