在sv中达成同步的方式有 event, semaphore和mailbox。
而在UVM中event进化成uvm_event,不仅仅拥有达成不同组件进程之间同步的功能,还能像TLM通信一样传递数据,并且作用范围更广(TLM通信只能在uvm_component之间,而uvm_event不限于此)。
uvm_event的使用
关于uvm_event的使用,最大的好处是摆脱了system Verilog中对event使用时必须按照路径引用的办法,而在uvm中只需要将 uvm_event放到 uvm_event_pool中即可,这在进行移植验证环境时,是非常便利的事情,并且我们在使用时,也不需要关心event的路径,只需要从event_pool中get到这个event就可以了。比如说,我们在tc里的main_phase中发完包之后,一般都会设置drain_time来等带dut将数据吐完,我们就可以在scoreboard中设置一个uvm_event等待socreboard接收完数据,然后再通过uvm_event通知tc,触发main_phase中的uvm_event.trigger,结束仿真。下面我们来看实现。
class my_component extends uvm_comopnent;
…
uvm_event my_event ;
`uvm_component_utils(my_component)
function void build_phase(uvm_phase phase);
super.build_phase(phase);
my_event = uvm_event_pool::get_global("my_event");
endfunction
task main_phase(uvm_phase phase);
...
set_trigger();
...
endtask
task set_trigger();
my_event.trigger();
endtask
endclass
class get_component extends uvm_comopnent;
…
uvm_event get_my_event ;
`uvm_component_utils(get_component)
function void build_phase(uvm_phase phase);
super.build_phase(phase);
get_my_event = uvm_event_pool::get_global("my_event");
endfunction
task main_phase(uvm_phase phase);
...
set_trigger();
...
endtask
task set_trigger();
...
get_my_event.wait_trigger();
...
endtask
endclass
通过 my_event = uvm_event_pool::get_global(“my_event”) 这种方法将my_event 注册到event_pool中;在set_component中将uvm_event放到event_pool中,并设置好触发条件,在get_component中,从uvm_event_pool中取出来,通过wait_trigger捕获这次事件。(这里用component只是作为例子使用,实际上在object中也可以这么做)。
对于uvm_event_pool的使用,也可以用下面的方法进行替换:
events_pool = uvm_event_pool::get_global_pool();
my_event = events_pool.get(“my_event”);
对于uvm_event提供的其他方法,我们可以看看uvm源码,这部分源码很简单,建议有时间可以看看,浮于表面的方法,大多都在底层有着很深层次的实现逻辑,实现这一系列逻辑并不是一件轻松的事情!
event被->触发之后,会触发使用@等待该事件的对象;uvm_event通过trigger()来触发,会触发使用wait_trigger()等待的对象。如果要再次等待事件触发,event只需要再次用->来触发,而uvm_event需要先通过reset()方法重置初始状态,再使用trigger()来触发。
event无法携带更多的信息,而uvm_event可以通过trigger(uvm_event data = null)的可选参数,将所要伴随触发的数据信息都写入到该触发事件中,而等待该事件的对象可以通过方法wait_trigger_data(output uvm_object data)来获取事件触发时写入的数据对象。这实际上是一个句柄的传递,句柄的类型是uvm_object,也就是说传递的对象句柄得是uvm_object类型的。那如何传递非uvm_object类型的对象呢,首先这个对象必须是uvm_object的子类或者子类的子类,然后才能将其句柄作为trigger()方法的参数,然后wait_trigger ()的参数必须是uvm_object类型的,可以用一个uvm_object类型的tmp作为参数传递句柄,然后使用$cast赋值给目标类型句柄。可以参考:http://www.sohu.com/a/140684109_778637
event触发时无法直接触发回调函数,而uvm_event可以通过add_callback(uvm_event_callback cb, bit append = 1)函数来添加回调函数。
event无法直接获取等待它的进程数目,而uvm_event不但可以通过get_num_waiters()来获取等待它的进程数目。
uvm_event 能够使用callback机制(这里就不讨论uvm_event_callback了,也不复杂), uvm_event能够获取目前事件的等待获触发状态。两者最大的区别还是uvm_event能够传递数据,而event不行。
不使用get()而使用get_global()
由于m_global_pool是一个静态全局的单例,没有必要专门创建一个uvm_event_pool的句柄来指向它;
static function T get_global (string key);
this_type gpool;
gpool = get_global_pool();
return gpool.get(key);
endfunction
static function T get_global (string key);
this_type gpool;
gpool = get_global_pool();
return gpool.get(key);
endfunction
get_global (string key)中集合了get_global_pool()和 get()方法,于是下面两部可以合成一句话:
uvm_event_pool ev_pool = uvm_event_pool::get_global_pool();
// either create a uvm_event or return a reference to it
uvm_event ev = ev_pool.get(“ev”);
uvm_event_pool ev_pool = uvm_event_pool::get_global_pool();
// either create a uvm_event or return a reference to it
uvm_event ev = ev_pool.get(“ev”);
uvm_event ev = uvm_event_pool::get_global (“ev”);
1 uvm_event ev = uvm_event_pool::get_global (“ev”);
其实这两种做法的结果都是一样的,都是在uvm_event_pool::m_global_pool这个静态全局单例中的创建了一个uvm_event对象,并存放在关联数组uvm_event_pool::m_global_pool.pool[string]中,索引就是get or get_global ()的参数, 如果这个字符串索引已经存在,那么就返回这个pool[string]句柄。
uvm_event和uvm_event_pool是UVM中用于实现事件和事件池的两个类。
uvm_event是一种同步机制,用于线程之间的通信和同步。一个uvm_event对象可以有多个等待它的线程,并且在该事件被触发之前,所有等待线程都会被阻塞。触发事件后,所有等待线程将被唤醒并可以继续执行。uvm_event对象被用作UVM中各种通信和同步操作的基础,例如用于线程间同步、消息传递等。
uvm_event_pool是一个管理uvm_event对象的池。它允许动态地创建和销毁uvm_event对象,并为线程提供对它们的访问。通过使用uvm_event_pool,可以避免在每次需要事件时都创建新的uvm_event对象,从而提高性能和效率。uvm_event_pool提供了创建事件、获取事件和释放事件的方法,还可以设置最大事件数量和超时时间等。
综上所述,uvm_event和uvm_event_pool是UVM中用于实现事件和事件池的两个类,分别用于线程之间的通信和同步,以及管理事件对象的创建和销毁。它们是UVM中实现多线程和同步机制的重要组成部分。