目录
基本知识
uvm_event是UVM内用于线程间控制或者通信的语句。
基本的用法如下:
uvm_event a; //声明一个名字为a的uvm_event。
a.trigger(T); //触发一个事件,括号中可以带一个参数,也可以不带。
a.wait_trigger(); //等待该事件的触发。
a.wait_trigger_data(T); //等待一个含有可选参数的事件。当事件触发时,可以传递一个参数。
全局资源池
用uvm_event有一个好处就是可以实现UVM环境内所有组件共享使用uvm_event。具体的做法就是创建一个共享资源池。每个组件都可以从这个资源池里获取同名的uvm_event来使用。
全局资源池是唯一的。
a = uvm_event_pool::get_gobal("a");
资源池的创建要写在各个UVM组件的build_phase中。上面还是以声明的uvm_event a来举例,凡是在UVM环境当中触发或者等待事件a的组件都可以使用该语句来建立自身与全局资源池的联系。
无论有多少个组件,只要它们寻求同一个名称的uvm_event,就可以共享该uvm_event对象。即便该对象不存在,uvm_event_pool资源池也会在第一次调用get_global()函数时创建这样一个对象以供使用。
可以通过以下语句来获取等待它的进程数目
a.get_num_waiters();
这是一种很方便的线程等待方式。可以利用它来控制等待任意个数的进程。
uvm_event和event的区别
event是SV里面用于线程控制的语句;而uvm_event是UVM里的语句。
event是边沿敏感的,所以它总是要阻塞着等待一个带@的操作符事件。这一点uvm_event可以满足,但是又不限于这种单一的模式,所以在可以使用UVM的情况下,uvm_event可以完美替代event。
event a; //声明一个名字为a的event。
-> a; //触发a事件,边沿敏感。
a.triggered(); //触发a事件的另一种写法,电平敏感。
@a; //等待a事件触发。
注意!!!使用电平敏感的wait(a.triggered())来替代边沿敏感的@a,可以避免在相同时刻触发event而带来的竞争问题,但无法捕捉到已经被触发,后续才等待的事件。
避坑指南
传递的参数类型只能是uvm_object
在使用trigger(T)和wait_trigger_data(T)传递参数的时候,并非什么类型的参数都可以传递,事实上只能传递uvm_object类型的参数。所以小编建议大家在使用的时候可以先自行定义一个transaction,然后把自己要传递的参数全部放到这个transaction当中去。
这样做你就可以实现传递多个参数了。
注意!!!类型传递时需要做类型转换
uvm_object tmp; //声明一个中间类型,待转换的父类。
uvm_event a; //声明即将使用的uvm_event
my_transaction b; //声明要传递的句柄
a.wait_trigger_data(tmp); //等待事件触发的同时参数传递过来
void'($cast(b,tmp)); //父类句柄转换为子类句柄
在等待方需要做类型转换,但是触发方不需要做类型转换,只需要将需要传递的参数写入括号即可。
uvm_event a;
my_transaction b;
b =new("b"); //别忘记例化这个对象
a.trigger(b);
事件等待前就触发了
使用uvm_event的时候不时得会遇见的窘境就是,在还没执行到wait_trigger()的时候,trigger()已经执行了。由于trigger()语句的触发只是一个脉冲,这样的话会出现wait_trigger永远也等不到的情况,仿真就会出现超时。
对于这种常见的情况我们的应对方式就是使用电平的触发和等待。这个uvm_event能做到,而SV中的event不能。
wait_ptrigger(); //电平等待
wait_ptrigger_data();
这样即便在调用事件等待方法之前该事件已被触发,等待方法仍然不会被阻塞并且可以继续执行。
但是这种方式在等待通过之后最好将uvm_event复位。否则可能导致部分事件等待的语句没有按照预期将仿真阻塞。
a.reset();