systemverilog的callback与C语言的callback

最近使用systemverilog和C的频率很高,重新解构两种语言的回调函数。
C语言:
先上菜鸟营的解释:
回调函数:类似这么一个场景————A君去B君店里买东西,恰好缺货,A君留下号码给B君,有货时通知A君。
If you call me, I will call you back;
Don't call me, I will call you.
如果是这样的应用场景,为什么不能重写成个普通函数,

int purchase(int A,int B)//**错误示例,无缝耦合.**
{
  if(A!=0)
    {
       if(B=0)    
        {
              printf("A没花钱");
              return notice(A);
        }
       else 
        printf("A花钱了");
    }
}
int notice(int A)
{
    printf("缺货,留下电话,call you");
    if(B!=0)
        return purchase(A,B);
}

回调函数,就是一个通过函数指针调用调用的函数。如果你把函数的指针作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。
理解,1:被回调的函数;2:回头执行调用动作的函数。回头调用????
维基百科的解释:In computer programming, a callback is any executable code that is passed as an argument to other code, which is expected to call back (execute) the argument at a given time. This execution may be immediate as in a synchronous callback, or it might happen at a later time as in an asynchronous callback.如果代码被立即执行,叫做同步回调,晚点再执行,叫异步回调。
A "callback" is any function that is called by another function which takes the first function as a parameter.函数 F1 调用函数 F2 的时候,函数 F1 通过参数给 函数 F2 传递了另外一个函数 F3 的指针,在函数 F2 执行的过程中,函数F2 调用了函数 F3,这个动作就叫做回调(Callback),而先被当做指针传入、后面又被回调的函数 F3 就是回调函数。
我明白了什么意思。但还是不知道为什么要使用?怎么使用?
解耦:

#include<softwareLib.h> // 包含Library Function所在读得Software library库的头文件//F2

int Callback() // Callback Function//F3
{
    // TODO
    return 0;
}
int main() // Main program
{
    // TODO
    Library(Callback);//F1
    // TODO
    return 0;
}

在回调中:主函数main调用了Library,而Callback作为参数被Library调用. callback就在Library中执行了一遍.如果此时想一想,库函数对我不可见,我不好更改库函数的功能,那就只能通过回调函数了.下面是个回调函数的例子:

#include<stdio.h>

int Callback_1() // Callback Function 1
{
    printf("Hello, this is Callback_1 \n");
    return 0;
}

int Callback_2() // Callback Function 2
{
    printf("Hello, this is Callback_2 \n");
    return 0;
}

int Callback_3() // Callback Function 3
{
    printf("Hello, this is Callback_3 \n");
    return 0;
}

int Handle(int (*Callback)())
{
    printf("Entering Handle Function. \n");
    Callback();
    printf("Leaving Handle Function.\n");
}

int main()
{
    printf("Entering Main Function. \n");
    Handle(Callback_1);
    Handle(Callback_2);
    Handle(Callback_3);
    printf("Leaving Main Function. \n");
    return 0;
}

Handle()函数里面的参数是一个指针,在main()函数里调用handle()函数时,给它传入了函数Callback_1,_2,_3的函数名,函数名就是对应函数的指针,现在我发现这个是真的方便.
但是带参数的回调函数在调用时.即:F1调用F2时,如果F3有参数,应该在F1调用F2时加入一个变量用以保存F2调用F3时 return回来的值.

#include<stdio.h>

int Callback_1(int x) // Callback Function 1
{
    printf("Hello, this is Callback_1 \n");
    return 0;
}

int Callback_2(int x) // Callback Function 2
{
    printf("Hello, this is Callback_2 \n");
    return 0;
}

int Callback_3(int x) // Callback Function 3
{
    printf("Hello, this is Callback_3 \n");
    return 0;
}

int Handle(int y,int (*Callback)(int))
{
    printf("Entering Handle Function. \n");
    Callback(y);
    printf("Leaving Handle Function.\n");
}

int main()
{
    int a  = 0;
    int b = 1;
    int c = 2;
    printf("Entering Main Function. \n");
    Handle(a,Callback_1);
    Handle(b,Callback_2);
    Handle(c,Callback_3);
    printf("Leaving Main Function. \n");
    return 0;
}

systemverilog部分:
randomize(),是systemverilog中一个带有callback的内建方法。randomize方法通过在randomize()前后分别调用pre_randomize()和post_randomize()去实现callback。而我们做验证的,往往为了最大可能性的提高验证平台的可重用性,会在验证环境中增加callback机制,从而为验证环境的使用人员提供了一个接口,可以实现对于传输的激励进行一些修改而不对原验证环境产生任何影响。
Callback机制是systemverilog面向对象特性实现的一种由程序开发者预留一个callback函数/任务接口(hook),程序使用者通过该接口实现对程序开发者开发的验证环境进行重用的一种机制。
这种callback机制,往往用在class中.

calss object_callback();
...
virtual function/task pre_callback();endtask/endfunction—————<<<<task/functionpre_callback();
...
virtual function/task post_callback();endtask/endfunction————<<<task/function post_callback();
...
endcalss

object_callback是一个类,pre_callback post_callback是两个空的虚方法,此处留给用户进行重载修改的接口方法,当用户不对object_callback进行派生时,因为pre_callback和post_callback是空的,不会执行任何操作。验证环境的用户可以通过对object_callback进行派生后,在pre_ ,post_ 进行重写实现自己期望的功能逻辑而不需要对验证环境中的object_callback进行任何修改。那么如何使用systemverilog的oop特性构建callback机制?
我在linux中跑一下代码:
一:创建回调基类:

抽象类对于后续的扩展类除提供可重用或覆盖的现有功能之外,抽象类对于定义扩展类的约定(即扩展类必须实现特定功能)也很有用。回调基类cbb之所以使用抽象类,因为实际使用中,将会有多个不同的子类将派生自回调基类。在回调基类中定义了回调任务(pre_fcallback和post_fcallback),定义的回调任务(pre_fcallback和post_fcallback)均为虚方法,并且任务体也为空,之所以定义虚方法是为了后续扩展回调基类后,通过handle引用时可以任务进行重载,之所以在这里没有使用纯虚方法,是因为派生自抽象类的派生类如果要实例化必须要实现所有父类中的纯虚方法,然而实际使用中,不一定在派生类中对所有的函数(任务)进行重写。

二:插入回调基类:

示例中:我们要在driver中插入回调类。首先在driver中声明类型为cbb(回调基类)的队列cbb_pool[$],用于后续存放回调基类或者派生类。30-32行,调用队列cbb_pool中成员的回调方法pre_fcallback,35-37行调用队列cbb_pool中成员的回调方法post_fcallback,因为在没有对回调基类进行扩展,并且也没有回调基类派生类压入队列cbb_pool中,即cbb_pook为空,所以foreach循环不会执行。后续将派生类压入队列后,当环境运行时,因为cbb_pool不为空,就会执行对应的回调方法(pre_fcallback和post_fcallback)。此处一般为验证环境开发人员提供给验证环境用户的回调方法的入口hook。

三:派生回调基类

回调基类进行扩展,在扩展类中回调基类中需要重写的方法进行重写,可以按照用户的需要实现特定的行为。一般情况下,该扩展类为验证环境用户根据实际情况开发。实例中分别对回调基类中的虚方法进行了重写。

四:使用回调机制

第23行创建了派生类ecbb的对象,第24行将创建的对象压入driver中声明的回调基类队列cbb_pool中(这里注意,第22行到25行之所以使用begin...end主要时这里需要声明一个新的局部变量pecbb)。当后续程序调用driver中的方法run时,其中的cbb_pool因为不为空,就会执行此时压入队列中的pecbb,而pecbb指向的对象中方法pre_fcallback和post_fcallback均被重写,所以此时就会执行重写后的pre_fcallback和post_fcallback。整个过程中,验证环境的用户不需要对原有验证环境进行修改,即可在数据的传输过程中实现特定的功能。至此,一个简单的回调机制的实现就完成了。

注意:上述示例其他代码见文末:
回调机制其实时使用systemverilog oop实现的一种验证环境开发者向验证环境使用者提供的模块内部接口hook,是一种重要的重用机制,也是UVM中的一种重要机制,其在UVM中的实现过程与本文示例相同,但是UVN实现的过程相当复杂,但基本原理与本文实例基本相同,使用的基本操作流程也是基本一样,在UVM中针对验证环境的开发者和使用者,需要分别完成以下几步以实现回调机制。————————<<< 《UVM实战》

验证环境开发人员:
1.定义一个A类:

class A extends UVM_callback;
  virtual task pre_tran();
  endtask
endclass

2.声明一个A_pool,用于指明该pool被哪个类使用; typedef uvm_callback #(my_driver,A)A_pool;

3.在需要预留callback方法接口的类中调用uvm_reigster_cb宏;

typedef calss A;
class my_driver extends vum_driver#(my_transaction);
.....
  `uvm_component_utles(my_driver)
  `uvm_register_cb(my_driver,A)
.....
endclass

4.在要调用callback方法接口的方法中,使用uvm_do_callbacks宏;

task my_driver::main_phase(uvm_phase phase);
.....
  while(1)begin
      seq_item_port.get_next_iterm(req);
      `uvm_do_callback(my_driver,A,pre_tran(this.req))
      drive_one_pkt(req);
      seq_item_port.item_done();
  end
.....
endtask

其他都是涉及更多的都是UVM,我这里不再做过多的讨论,ps:因为那部分我也还没搞懂

callback示例代码:

代码有些地方是有问题的,编译时请注意改错。

更多关于systemverilog的学习内容请前往硅芯思见,参见nanoty老师114课内容。

posted @ 2021-10-29 16:52  肆月黄妙之  阅读(331)  评论(1编辑  收藏  举报