在sv中达成同步的方式有 event, semaphore和mailbox。还有uvm_barrier
而在UVM中event进化成uvm_event,不仅仅拥有达成不同组件进程之间同步的功能,还能像TLM通信一样传递数据,并且作用范围更广(TLM通信只能在uvm_component之间,而uvm_event不限于此)。
- uvm_event 是uvm_event_base的子类;
- wait_trigger是uvm_event_base的方法;
- 通过uvm_event对象的方法trigget () 和wait_trigger ()达成同步
- event是sv的数据类型;
uvm_event_pool
uvm_event_pool是一个参数为uvm_event类型的uvm_object_string_pool,而uvm_object_string_pool又是继承于uvm_pool。uvm_event_pool和uvm_pool没有太大差别,也不是很复杂,在这里不是重点。
UVM : uvm_event, uvm_event_pool
module test
import uvm_pkg::*;
initial begin
// get a reference to the global singleton object by
// calling a static method
uvm_event_pool ev_pool = uvm_event_pool::get_global_pool();
// either create a uvm_event or return a reference to it
// (which depends on the order of execution of the two
// initial blocks - the first call creates the event,
// the second and subsequent calls return a reference to
// an existing event.)
uvm_event ev = ev_pool.get("ev"); //调用get()方法得到一个uvm_event对象的的句柄
// wait (an arbitrary) 10 and then trigger the event
#10 ev.trigger();
$display("%t: event ev triggered", $time);
end
initial begin
// get a reference to the global singleton object by
// calling a static method
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");
// wait for the trigger
ev.wait_trigger();
$display("%t: event ev trigger received", $time);
end
endmodule
- 调用get_global_pool()会返回一个类型为uvm_event_pool的句柄,句柄指向的对象是全局并且单例的。也就是说两个initial块中两次调用get_global_pool都是返回同一个uvm_event_pool类型对象的句柄,只是先调用的那个get_global_pool函数会创建这个对象。
- 两个initial块两次调用get(),是为了获得同一个pool[string]对象的句柄,先调用的那个get()会创建这个pool[string],而后调用的那个只是获得这个pool[string]对象的句柄。这个过程和systemverilog中event的传递是一样的,是在两个进程中完成同步的基础。
- 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赋值给目标类型句柄。参考
- get_global (string key)中集合了get_global_pool()和 get()方法,于是下面两部可以合成一句话,
- 都是在uvm_event_pool::m_global_pool这个静态全局单例中的创建了一个uvm_event对象,并存放在关联数组uvm_event_pool::m_global_pool.pool[string]中,索引就是get or get_global ()的参数, 如果这个字符串索引已经存在,那么就返回这个pool[string]句柄。
- uvm_event/sv mailbox/config_db/tlm 使用场景有啥区别 都能传东西;
- uvm_tlm_fifo 就是基于mailbox来实现的;而不同的地方在于uvm_tlm_fifo提供了各种端口供用户使用;
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_event,这不是通过跨层次传递uvm_event对象句柄来实现共享的,因为这并不符合组件环境封闭的原则。这种共享同一个uvm_event对象是通过uvm_event_pool这一全局资源池来实现的。这个资源池类是uvm_object_string_pool #(T)的子类,它可以生成和获取通过字符串来索引的uvm_event对象。通过全局资源池对象(唯一的),在环境中任何一个地方的组件都可以从资源池中获取共同的对象,这就避免了组件之间的互相依赖。
uvm_event | evnet |
---|---|
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_barrier : 对多个组件进行同步协调,同时为了解决组件独立运作的封闭性需要;
- Callback机制:在不修改类的本身的前提下,去修改类中某些成员方法,或者扩展新的方法;
- 工厂的override机制:在不修改原有验证环境层次和验证包的代同时,实现对验证环境内部组件类型或者对象的覆盖(override);
barrier
uvm_barrier可以设置一定的等待阈值(threshold),待有不少于该阈值的进程在等待该对象时,才触发该事件,同时激活所有正在等待的进程,使得可以继续进行。
uvm_barrier