关于VIP中的callback

如上图所示,VIP的开发一般都会提供env的层次,或者最少是agent的层次,原则上用户是不可以修改vip本身的代码的,因此针对不同用户的需求,需要留一些勾子,可以在不修改源码的前提下又可以完成不同的代码需求,这就是callback。

下面来举例说明:

以monitor为例,我希望它在每次完成数据包采集后,都告诉我一下,采到了一个什么类型的数据包,假如这个monitor是我自己写的呢,一个显而易见的做法就是我在退出task之前调一个函数,就叫fun_print()好了,在这个function里完成打印。

class monitor;

  virtual task fun_sample();
    ...
    fun_print();
  endtask

  virtual function fun_print();
    $display("this is a read trans");
  endfunction

endclass

然后在需要用到这个monitor的地方比如agent中例化一个monitor A, 调用A.func_sample()时就能自然而然地调用fun_print()了。假如我下次想让fun_print() 打印的是 "this is a crazy trans", 我该怎么做呢?我想起来以前讲virtual的功能时提到过,父类句柄是可以调用子类实例的function的,那我可以从monitor extend一个new monitor,并且修改它的fun_print函数,就能达到目的了。 但是,父类句柄调用子类实例function的一个重要前提,就是完成了一个句柄转换的操作, A=new monitor inst, 又要改agent的代码了。。。显然也和VIP不改代码的需求冲突。(*这里还需要补充一些说明,放在最后)

假如我不把fun_print写在monitor里面会不会好一点呢:

class monitor;
  classC  c_father;
  
  virtual task fun_sample();
    ...
    c_father.fun_print();

  endtask

endclass



class classC;

  virtual function fun_print();
    $display("this is a crazy trans");
  endfunction

endclass

这样的话,我就扩展 classC 就好了,好像比较轻量级, 但是仔细想想,根本原则和上面是一样的,我还是要利用virtual的特性,利用父类句柄调用子类实例的function,只不过改代码的地方从agent移到monitor了。

为了解决如何告知调用方的问题,源码开发的大神们提供了一个厉害的思路:

class monitor;
  classC  c_father;
  
  virtual task fun_sample();
    ...
    classC的所有子类.fun_print();//将单一句柄的调用改成了调用其所有子类的fun_print

  endtask

endclass



class classC;

  virtual function fun_print();
    $display("this is a crazy trans");
  endfunction

endclass

这样用户就可以不用改monitor源码了,但是如何实现查找并调用所有子类函数的目的呢-------------创建一个篮子,让用户可以把所有新建的子类实例丢进去,monitor实例则将所有子类实例都拿起来看一下是不是自己的。(敲完这段,突然觉得好像去拿快递啊。。。,快递员把所有快递都丢在前台,然后我去把所有快递都翻一遍,看看有没有我的。。。里面装了东西的快递盒子岂不就是我的子类实例,没装东西的快递盒子就是我上面的classC)。源码开发大佬们将这种专门为了回调函数而定义的类称为uvm_callback,指定它为开山鼻祖。

于是上述classC就应该直接从uvm_callback扩展。

class classC extends uvm_callback; 

  virtual function fun_print();
  
  endfunction 

endclass

新建子类的动作则是:

class classC1 extends classC; 

  virtual function fun_print():
    $display("this is a crazy trans");
  endfunction

endclass


class classC2 extends classC; 

  virtual function fun_print():
    $display("this is a mad trans");
  endfunction

endclass

往篮子里丢实例的动作则由参数化类uvm_callback#(component,uvm_callback)里的add函数来完成:

classC1 c1;
classC2 c2;

c1=classC1::type_id::create("c1");
c2=classC2::type_id::create("c2");

uvm_callback#(monitor,classC)::add("xxxx.xxx.monitor_inst1",c1);
uvm_callback#(monitor,classC)::add("xxxx.xxx.monitor_inst2",c2);

实际上这个过程也创建了monitor实例和callback子类实例的联系,让验证平台中的不同monitor实例可以找到属于自己的fun_print();

让monitor启动找子类fun_print()则用一个宏来进行:

`uvm_do_callbacks(monitor,classC,fun_print())

为了这个宏能顺利执行,还需要在monitor类中注册一下callback

 `uvm_register_cb(monitor,classC)

至此可以重新整理一下这个过程了。

公司前台告诉快递员我们只收寄到公司的快递(VIP 定义了一个classC),快递员将快递盒子放到公司前台(用户将扩展的子类定义好,并实例化,add到某个约定的地方), 我去翻一遍前台的所有快递,找我的名字(VIP monitor查看一遍所有的子类实例,然后找出与自己匹配的那个执行),我有资格翻找的前提是,我是这个公司的员工(VIP monitor代码中需要将callback注册好)。

至此,VIP的使用者就是一个快递员的角色,动作非常简单,就是extend 一个类,重载函数,然后调用add。其余都是VIP开发者的事情。

 

*这里是特殊说明:其实有很多其他办法能实现相同的目的,比如我把VIP的monitor 扩展一下,产生一个new monitor,然后用工厂模式,将monitor override整个类替换掉,或者用实例替换也是可以的,能支持不同的monitor实例做不同的事情。callback和重载两者的优劣我也没有比对过。希望以后能带来一些使用感受。

*我上面举的例子非常傻,只是打印而已,有一些稍微正经一点的应用,比如在AHB VIP的monitor中,在每拍结束的位置做一下覆盖率采样,由于使用者无法触及VIP内部代码,想要知道每拍结束的位置,不使用VIP留的接口是很难实现的,除非自己实现一个monitor。

**自己看一遍书总以为自己懂了,写一遍分享出来发现原来有更简单地理解方式,这就是分享的意义,希望有疑问也能留言,一起探讨学习~

 

  • 8
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值