【UVM源码】uvm_event


一、源代码中是什么?

uvm_event.svh (361行)中实现了uvm_event机制,实现了两个类:uvm_event_base和uvm_event。
uvm_event_callback.svh中实现了uvm_event_callback类;

uvm_event_base类

类的介绍:

uvm_event_base直接扩展自uvm_object,相当于在SystemVerilog event基础上包了层uvm的外衣。除了sv event支持的属性外,uvm_event_base还能保存waiter的数目且支持callback。

类的方法:

uvm_event_base中实现的主要方法有:
在这里插入图片描述
在这里插入图片描述

uvm_event类

类的介绍:

uvm_event派生自uvm_event_base,其中实现的方法有:

类的方法:

在这里插入图片描述

uvm_event_callback.svh

uvm_event_callback类:

类的介绍:

uvm_event_callback直接从uvm_object扩展而来,是个参数化的虚类,该类用于uvm_event的callback

该类中,实现了两个function:pre_trigger和post_trigger,使用该类时需重载这两个函数。
pre_trigger中默认返回0,如果如要在触发时间之前做判断,可以重载pre_trigger函数使其返回1,这样就不再触发event且不执行post_trigger。
post_traigger在触发uvm_event之后自动执行,默认返回0,用户可重载该callback函数实现自定义功能。

回调是使用等待事件的进程的替代方法。 当回调附加到事件时,每次触发事件时都会调用该回调对象的回调函数。

类的方法:

new: 创建一个新的回调对象。
pre_trigger :在触发相关事件之前调用此回调。
post_trigger: 触发关联事件后调用此回调。

二、uvm_event用法是怎么样?

为什么要有uvm_event?

uvm_event等类是同步通信元件;

《芯片验证漫游指南》8.5 节介绍了 SV 用来做线程同步的几种元件,分别是 semaphore、event 和 mailbox。然而在 UVM 中,需要同步线程不再只局限于在同一个对象中,还需要解决不同组件之间的线程同步问题。一旦线程同步要求发生在不同组件,就要求组件之间通过某种方法来实现同步。考虑到 UVM 组件的封闭性原则,我们并不推荐通过层次索引的形式在组件中来索引公共的event 或 semaphore。 UVM 为了解决封闭性的问题,定义了如下的类来满足组件之间的同步要求:

uvm_event, uvm_event_pool 和 tvm_event_ calback 
uvm_barrier, uym_ barrier_pool

这两组类分别用于服务两个组件之间的同步和多个组件之间的同步。此外,回调函数作为一种实现基类复用的手段,在 UVM 中也被进一步封装为一个类uvm_callback,它不但具备普通回调函数可以在函数执行前后调用的特点,还增加了丰富的特性来完成层次化调用,uvm_callback 类作为我们对函数调用的同步手段来了解。下面我们给出实例来讲解这三组类的特性和用法。

uvm_event 类比 event多的几个重要特性

  • event 被->触发之后,触发使用@等待该事件的对象;uvm_event通过 trigger()来触发,触发使用 wait_trigger()等待该事件的对象。要再次等待事件触发, event 只需再次用->触发,而uvm_event 需要先通过 rese()方法重置初始状态,再使用 trigger()来触发。
  • event无法携带更多的信息,而 uvm_event 可以通过 trigger(T data = null)的可选参数,将伴随触发的数据对象都写入到该触发事件中,而等待该事件的对象可以通过方法wait_ triger_ data(output T data)来获取事件触发时写入的数据对象。
  • event 触发时无法直接触发回调函数,而 uvm_ event 可以通过 add_callback(uvm_event_callback cb, bit append = 1)函数来添加回调函数。
    event 无法直接获取等待它的进程数目,而 uvm_event 可以通过 get_num_waiters()来获取等符它的进程数目。

不同组件可以共享同一个 uvm_event,这不需要通过跨层次传递 uvm_event 对象句柄来实现共享,因为这不符合组件环境封闭的原则,该其享方式是通过uvm _even_poot这一全局资源池来实现的。这个资源池类是 uvm_object_string pool #(T)的子类,它可以生成和获取通过字符串来索引的uvm_event对象。通过全局资源池(唯一的),环境中的任何组件都可以从资源池获取共享的对象句柄,这就避免了组作之间的互相依赖

具体代码例子讲解uvm_event的使用

接下来我们就结合uvm_event、 uvm_event_pool 和 uvm_event_callback 来讲解一个典型用例。

class edata extends uvm_object;
int data;
	`uvm_object_utils(edata)
	…
endclass

class ecb extends uvm_event_callback;
	`uvm_object_utils(ecb)
    …
	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("EPOSTRIG", $sformatf("after trigger event %s", e.get_name()),
UVM_LOW)
	endfunction
endclass

class comp1 extends uvm_component;
	uvm_event e1
	`uvm_component_utils(comp1)
	function void build phase(uvm_phase phase);
		super.build_phase(phase);
		e1= uvm_event_pool::get_global("e1");
	endfunction
	task run_phase(uvm_phase phase);
		edata d = new();
		ecb cb = new() ;
		d.data = 100;
		#10ns;
		e1.add_callback(cb);
		e1.trigger(d);
		`uvm_info("ETRIG", $sformatf("trigger sync event at %t ps", %time), UVM_LOW)
	endtask
endclass

class comp2 extends uvm_component;
	uvm_event e1;
	`uvm_component_utils(comp2)
	...
	function void build_phase(uvm_phase phase);
		super.build_phase(phase);
		e1 = uvm_event_pool::get_global("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);
		void'($cast(d, tmp));
		`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;
	`uvm_component_utils(env)
	...
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

在上面的例子中,组件 c1 和 c2 之间完成了从 c1 到 c2 的同步,且在同步过程中通过uvm_event e1传递了数据 edata,还调用了回调函数类 ecb 的 pre_trigger()和 post_trigger()方法。关于这个用例,有几点需要读者注意:

  • 无论有多少个组件,只要它们寻求同一个名称的 uym_event,就可以共享该 uvm_event对象。例如,上面的 c1 和 c2 通过 uvm_event_pool::get_global(“e1”)来获取同一个名称的 uym_event 对象,即便该对象不存在,uvm_event_pool资源池也会在第一次调用get_global()函数时创建这样一个对象以供使用。
  • 如果要传递数据,用户可以定义扩展于uvm_object的数据子类,并通过uvm_event:trigger(T data = null)来传递数据对象。而在等待 uvm_event 一侧的组件,则需要通过uvm_event:wait_trigger_data(output T data)来获取该对象。
  • 用户也可以扩展 uvm_event_callback 类,定义 uvm_event 被 trigger 前后的调用方法pre_trigger()和 post_trigger()。pre_trigger()需要有返回值,如果返回值为 1,则表示uvm_event 不会被trigger,也不会再执行 post trigger()方法;如果返回值为 0,则会继续 trigger 该事件对象。
  • 如果用户无法确定在等待事件之前,uvm_event 是否已经被 trigger,那么用户还可以通过方法 wait_ptrigger()和 wait _ptrigger_data()来完成等待。这样即便在调用事件等待方法之前该事件已经被触发,等待方法仍然不会被阻塞并且可以继续执行结束。

什么情况下会使用uvm_event?

那么在日常应用中,什么情况下会使用 uvm_event 呢?第 12 章提到,组件之间的常规数据流向是通过 TLM 通信方法实现的,比如 sequencer 与 driver 之间,或者 monitor 与 scoreboard之间。然而有些时候,数据传输是偶然触发的,并且需要立即响应,这个时候 uvm_event 就是得力的助手了uvm_event 同时也解决了一个重要问题,那就是在一些 uvm object 和uvm_component 对象之间如果要发生同步,但是无法通过 TLM 完成数据传输,因为 TLM传输必须是在组件(component)和组件之间进行的。然而,要在 sequence 与 sequence 之间进行同步,或 sequence 与 driver 之间进行同步,可以借助 uvm_event 来实现。


三、致谢

本文部分参考CSDN博主「MangoPapa」的文章,特此致谢!(https://blog.csdn.net/weixin_40357487/article/details/111103107)
本文部分转载自《芯片验证漫游指南》第12章的内容,特此致谢!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值