UVM——同步通信元件(uvm_event、uvm_barrier)


  SV中用来做线程间同步的几种方法,它们分别是 semaphore、event和mailbox

  UVM中,同步不再只局限于同一个对象中的各个线程,而是还有各个组件之间的同步问题。一旦发生同步的要求发生在各个组件之间,这就要求组件之间通过某种可以同步的方法来实现。而考虑到UVM各个组件的封闭性原则,我们并不推荐通过层次索引的形式在组件中来索引公共的event或者semaphore。UVM为了解决封闭性的问题,定义了如下的类来满足组件之间的同步:

  • uvm_event,uvm_event_pool 和 uvm_event_callback
  • uvm_barrier, uvm_barrier_pool

一、uvm_event用法

  uvm_event类与event相比,它有下面的几个重要特性:

uvm_eventevnet
trigger()来触发,使用wait_trigger()等待的对象
再次等待事件触发,需要先通过reset()方法重置初始状态,再使用trigger()触发
->触发,使用**@等待的对象
再次等待事件触发,只需要再次用
->触发**
通过trigger(uvm_object data = null)的可选参数,可携带数据对象信息
可通过wait_trigger_data(output uvm_object data)等待事件获取写入的数据对象。
->触发事件时无法携带信息
可通过get_num_waiters()获取等待进程数目无法获取等待它的进程数目
可通过add_callback(uvm_event_callback cb, bit append = 1)函数来添加回调函数触发时无法直接触发回调函数

  常用的uvm_event类的方法如下:
在这里插入图片描述
   不同的组件可以共享同一个uvm_event,这不是通过跨层次传递uvm_event对象句柄来实现共享的,因为这并不符合组件环境封闭的原则。这种共享同一个uvm_event对象是通过uvm_event_pool这一全局资源池来实现的。这个资源池类是uvm_object_string_pool #(T)的子类,它可以生成和获取通过字符串来索引的uvm_event对象。通过全局资源池对象(唯一的)在环境中任何一个地方的组件都可以从资源池中获取共同的对象,这就避免了组件之间的互相依赖。

class edata extends uvm_object;      //--创建trigger()触发时传递的数据信息
   int data;
   `uvm_component_utils(edata)
   ...
endclass

calss ecb extends uvm_event_callback;     //--创建add_callback()添加的回调函数
   ...
   function bit pre_trigger(uvm_event e,uvm_object data=null);
      `uvm_info("EPRETRIG",$sformatf("before trigger event %s",e.get_name()),UVM_LOW)
      return  0;
   endfunction
   function void post_trigger(uvm_event e,uvm_object data=null);
      `uvm_info("EPRETRIG",$sformatf("after trigger event %s",e.get_name()),UVM_LOW)
   endfunction   
endclass

class comp1 extends uvm_component;       //组件comp1
   ...
   uvm_event  e1;                                   //**1.声明事件变量(句柄)**
   function void build_phase(uvm_phase phase);
      super.build_phase(phase);
      e1 = uvm_event_pool::get_global("e1");       //**2.在资源池中创建一个共享事件e1,并赋值给本地事件e1** 
   endfunction
   task run_phase(uvm_phase phase);
      edata  d = new();      //实例化数据包
      ecb  cb = new();       //实例化回调函数类
      d.data = 100;
      #10ns;
      e1.add_callback(cb);                    //**3.通过add_callback()调用回调类中的回调函数**
      e1.trigger(d);                        //**4.触发事件,同时将数据包对象d传递给等待事件**
      `uvm_info("ETRIG",$sformatf("trigger sync event at %t ps",$time),UVM_LOW)
   endtask
endclass

class comp2 extends uvm_component;       //组件comp1
   ...
   uvm_event  e1;       //1.声明事件变量(句柄)
   function void build_phase(uvm_phase phase);
      super.build_phase(phase);
      e1 = uvm_event_pool::get_global("e1");       //2.在资源池中创建一个共享事件e1,并赋值给本地事件e1 
   endfunction
   task run_phase(uvm_phase phase);
      uvm_object   tmp;
      edata    d;
      `uvm_info("ESYNC",$sformatf("wait sync event at %t ps",$time),UVM_LOW)      
      e1.wait_trigger_data(tmp);              //4.等待事件,同时将接收触发事件发来的数据包对象
      void`($cast(d,tmp));               //5.类型转换,将tmp对象中的值赋给d中
        `uvm_info("ESYNC",$sformatf("get data %0d after sync at %t ps",d.data,$time),UVM_LOW)     
   endtask
endclass

class env1 extends uvm_env;
   comp1   c1;
   comp2   c2;
   ...
endclass     

输出结果:

UVM_INFO @ 0: reporter [RNTST] Running test test1...
UVM_INFO @ 0: uvm_test_top.env.c2 [ESYNC] wait sync event at 0 ps
UVM_INFO @ 10000: reporter [EPRETRIG] before trigger event e1
UVM_INFO @ 10000: reporter [EPOSTRIG] after trigger event e1
UVM_INFO @ 10000: uvm_test_top.env.c1 [ETRIG] trigger sync event at 10000 ps
UVM_INFO @ 10000: uvm_test_top.env.c2 [ESYNC] get data 100 after sync at 10000 ps

  

  • 无论有多少个组件,只要它们共同寻求同一个名称的uvm_event,将会共享该uvm_event对象的句柄。上面c1和c2通过uvm_event_pool::get_global("e1")来获取同一个名称的uvm_event对象。即便该对象不存在,也会在第一次调用get_global()函数时创建这样一个对象。
  • 要传递数据,用户可以自定义扩展uvm_object的子类,并且通过uvm_event::trigger(uvm_object data = null)来传递给数据对象。而在等待uvm_event一侧的组件,则需要通过uvm_event::wait_trigger_data(output uvm_object data)来获取该对象
  • 可以扩展uvm_event_callback类,自定义在uvm_event被trigger前后的调用方法。pre_trigger()需要有返回值,如果返回值为1,则表示uvm_event不会被trigger,也不会再执行post_trigger()方法;如果返回值为0,则会继续trigger该事件对象。

二、uvm_barrier用法

  UVM还提供了一个新的类uvm_barrier用来对多个组件进行同步协调,同时为了解决组件独立运作的封闭性需要,也定义了新的类uvm_barrier_pool来全局管理这些uvm_barrier对象。这里的uvm_barrier_pool同之前的uvm_event_pool一样,也是基于uvm_object_string_pool的通用参数类来进一步定义的。

  • typedef uvm_object_string_pool #(uvm_barrier)   uvm_barrier_pool;
  • typedef uvm_object_string_pool #(uvm_event#(uvm_object))   uvm_event_pool;

  uvm_barrier可以设置一定的等待阈值(threshold)待有不少于该阈值的进程在等待该对象时,才触发该事件,同时激活所有正在等待的进程,使得可以继续进行。

  常用的uvm_event类的方法如下:
在这里插入图片描述

class comp1 extends uvm_component;       //组件comp1
   ...
   uvm_barrier  b1;                                   //**1.声明事件阻塞变量(句柄)**
   function void build_phase(uvm_phase phase);
      super.build_phase(phase);
      b1 = uvm_event_pool::get_global("b1");       //**2.在资源池中创建一个共享阻塞事件b1,并赋值给本地阻塞事件b1** 
   endfunction
   task run_phase(uvm_phase phase);
      #10ns;
      `uvm_info("BSYNC",$sformatf("c1 wait for b1 at %t ps",$time),UVM_LOW)
      b1.wait_for();                       //**3. 在达到阻塞阈值threshold之前,一直阻塞语句执行**
      `uvm_info("BSYNC",$sformatf("c1 is activated at %t ps",$time),UVM_LOW)      
   endtask
endclass

class comp2 extends uvm_component;       //组件comp1
   ...
   uvm_barrier  b1;                              
   function void build_phase(uvm_phase phase);
      super.build_phase(phase);
      b1 = uvm_event_pool::get_global("b1");       //共享资源池中的阻塞事件b1
   endfunction
   task run_phase(uvm_phase phase);
      #20ns;
      `uvm_info("BSYNC",$sformatf("c2 wait for b1 at %t ps",$time),UVM_LOW)
      b1.wait_for();                        //**3. 在达到阻塞阈值threshold之前,一直阻塞语句执行**
      `uvm_info("BSYNC",$sformatf("c2 is activated at %t ps",$time),UVM_LOW)      
   endtask
endclass

class env1 extends uvm_env;
   comp1   c1;
   comp2   c2;
   uvm_barrier  b1;
   ...
   function void build_phase(uvm_phase phase);
      super.build_phase(phase);
      c1 = comp1::type_id::create("c1",this);
      c2 = comp2::type_id::create("c2",this);      
      b1 = uvm_event_pool::get_global("b1");       //共享资源池中的阻塞事件b1
   endfunction
   task run_phase(uvm_phase phase);
      b1.set_threshold(3);                          //**4. 设置阈值为3,即需要有三个等待事件**
      `uvm_info("BSYNC",$sformatf("env set b1 threshold %d at %t ps",b1.get_threshold(),$time),UVM_LOW)      
      #50ns;
      b1.set_threshold(2);                     //**4. 修改,设置阈值为2,即需要两个等待事件**      
      `uvm_info("BSYNC",$sformatf("env set b1 threshold %d at %t ps",b1.get_threshold(),$time),UVM_LOW)      
   endtask
endclass     

输出结果:

UVM_INFO @ 0: reporter [RNTST] Running test test1...
UVM_INFO @ 0: uvm_test_top.env [BSYNC] env set b1 threshold 3 at 0 ps
UVM_INFO @ 10000: uvm_test_top.env.c1 [BSYNC] c1 wait for b1 at 10000 ps
UVM_INFO @ 20000: uvm_test_top.env.c2 [BSYNC] c2 wait for b1 at 20000 ps
UVM_INFO @ 50000: uvm_test_top.env [BSYNC] env set b1 threshold 2 at 50000 ps
UVM_INFO @ 50000: uvm_test_top.env.c1 [BSYNC] c1 is activated at 50000 ps
UVM_INFO @ 50000: uvm_test_top.env.c2 [BSYNC] c2 is activated at 50000 ps

  c1和c2的run_phase任务之间需要同步,而同步它们的则是顶层的一个uvm_barrier b1由于c1、c2和env1都共享该对象,这使得c1和c2可以通过wait_for()来等待激活,而env1则可以设置阈值来调控什么时间来“开阀”。从仿真结果可以看到,在一开始的时候,阈值设置为3,但由于等待该barrier的进程只有2个,无法达到阈值条件,使得两个进程都无法激活。而在env1将b1的阈值调低为2时,则等待该barrier的两个进程都被激活。因此通过set_threshold()和wait_for()这样的方式,可以实现多个组件之间的同步,同时又可以保持各个组件之间的独立性。

  补充:除了UVM提供新的类方便组件之间的同步之外,另外一种同步方式回调函数(callback)也方便了类的封装复用。在通常情况下,如果得到了一个封闭的包,其中的类如果有些成员方法需要修改,或者需要扩展新的方法时,应该怎么做呢?如果这个包是外来的,那么它的维护方首先不建议我们去修改这个类的本身。如果我们通过类的继承来实现这一要求,又无法进一步在该包的环境中用新的子类替换原来的父类。那么,UVM的覆盖机制(override)就可以帮到忙。除了覆盖机制,还有callback也可以为用户提供可以自定义的处理方法。这就使得,如果用户不需要添加新的方法,而是想延展之前的方法,那么就不需要通过继承类的方式,而只需要通过在后期自定义callback方法来实现。

  • uvm_event / uvm_barrier对多个组件进行同步协调,同时为了解决组件独立运作的封闭性需要
  • Callback机制在不修改类的本身的前提下,去修改类中某些成员方法,或者扩展新的方法;
  • 工厂的override机制在不修改原有验证环境层次和验证包的代同时,实现对验证环境内部组件类型或者对象的覆盖(override);

  各个机制的目的都是为了实现一个环境多个用例,提高代码的重用性。

  • 12
    点赞
  • 86
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SD.ZHAI

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值