文章目录
1.3 Persistent trigger: triggered built-in method
1.2.1.2 uvm_object_string_pool
1.2.2.2.1 wait_trigger/wait_ptrigger
0. 引言
systemverilog 中介绍了semapahore,event和mailbox,以进行线程间的通信,而它们的实例需要同处一个对象中。而在uvm中,涉及多个组件,组件间的同步线程不再只局限于在同一个对象中。如果组件A定义了一个semapahore,event或mailbox,组件B需要与其通信,则组件B中需要通过hierarchy name的方式来引用组件A中的semapahore,event或mailbox。这种方式与tb间的耦合极为紧密,为将来环境变动,环境复用带来很大的麻烦。
为此,uvm中定义了uvm_event, uvm_barrier,通过全局资源池(唯一的),环境中的任何组件都可以从资源池获取共享的对象句柄,避免组件之间的互相依赖。
uvm定义了如下元件,来实现组件间的同步:
- uvm_event, uvm_event_poll, uvm_event_callback
- uvm_barrier, uvm_barrier_poll
本文通过源码介绍uvm_event,uvm_barrier 的机制。
1. uvm_event
1.1 system verilog 中的event
sv中一个以event数据类型声明的变量被称为named event,它提供了基础同步对象的句柄。当一个进程等待一个将触发的event,该进程会被同步对象放进它所维护的queue中。进程可以通过@操作符或wait() 来等待一个将要触发的named event,以检查它们的触发状态。
两种触发方式,-> 和 ->>,主要区别是->>的语句会在timeslot中(re)NBA region中调用,而前者是(re)active。
1.2 waiting for an event
@阻塞了调用的进程,直到event被触发。
@需要早于 -> ,否则 进程依然阻塞。
1.3 Persistent trigger: triggered built-in method
事件的triggered内建方法,可以用来获取事件的触发状态,该状态持续一个time step。
hierarchical_event_identifier.triggered
使用这种机制,无论等待操作是在触发操作之前还是在相同的仿真时间执行,事件触发都将解除等待进程的阻塞。因此,triggered
方法有助于消除当触发和等待操作同时发生时常见的竞争条件。一个阻塞等待事件(@)的进程可能会解除阻塞,也可能不会,这取决于等待和触发进程的执行顺序。然而,一个等待触发状态(wait)的进程总是会解除阻塞,无论等待和触发操作的执行顺序如何。
举例:
1. 正常操作:使用@ 等待事件。在1ns之后 触发事件。
event done;
initial begin
fork
begin
$display("%0t, Waitted the event done, before",$time);
@done;
$display("%0t, Waitted the event done, after",$time);
end
begin
#1;
-> done;
$display("%0t, trigger the event done, after",$time);
end
join
end
结果:
0, Waitted the event done, before
1, trigger the event done, after
1, Waitted the event done, after
是一对一的,触发一次,最多只能等到一次。即,如果
@done;
$display("%0t, Waitted the event done, after",$time);
@done;
$display("%0t, Waitted the event done, after",$time);
触发后,也只显示一个。
2. 先触发事件,1ns后使用@等待事件:
event done;
initial begin
fork
begin
#1;
$display("%0t, Waitted the event done, before",$time);
@done;
$display("%0t, Waitted the event done, after",$time);
end
begin
-> done;
$display("%0t, trigger the event done, after",$time);
end
join
end
结果。没有等到事件的触发。
0, trigger the event done, after
1, Waitted the event done, before
3. 使用@, 同时触发和等待。
begin
#1;
-> done;
$display("%0t, trigger the event done, after",$time);
end
begin
#1;
$display("%0t, Waitted the event done, before",$time);
@done;
$display("%0t, Waitted the event done, after",$time);
end
该结果是:
1, trigger the event done, after
1, Waitted the event done, before
调换begin end block之后:
begin
#1;
$display("%0t, Waitted the event done, before",$time);
@done;
$display("%0t, Waitted the event done, after",$time);
end
begin
#1;
-> done;
$display("%0t, trigger the event done, after",$time);
end
1, Waitted the event done, before
1, trigger the event done, after
1, Waitted the event done, after
对于编译器实则不能区分同一时刻,在同一时刻,还是存在顺序,与编译器有关(在这里,vcs 是将begin...end block顺序执行)。
以下使用wait(done.triggered)
4. 同一仿真时刻,触发和wait
begin
#1;
-> done;
$display("%0t, trigger the event done, after",$time);
end
begin
#1;
$display("%0t, Waitted the event done, before",$time);
wait(done.triggered);
$display("%0t, Waitted the event done, after",$time);
end
结果:
1, trigger the event done, after
1, Waitted the event done, before
1, Waitted the event done, after
调换之后:
begin
#1;
$display("%0t, Waitted the event done, before",$time);
wait(done.triggered);
$display("%0t, Waitted the event done, after",$time);
end
begin
#1;
-> done;
$display("%0t, trigger the event done, after",$time);
end
结果:
1, Waitted the event done, before
1, trigger the event done, after
1, Waitted the event done, after
5. 先触发,1ns 之后 wait
begin
#1;
$display("%0t, Waitted the event done, before",$time);
wait(done.triggered);
$display("%0t, Waitted the event done, after",$time);
end
begin
-> done;
$display("%0t, trigger the event done, after",$time);
end
结果:
0, trigger the event done, after
1, Waitted the event done, before
该结果并没有等到事件的触发,这是由于事件触发的状态只维持一个time step。1ns已经开始了一个新的time step。将#1 改为#0,使其在同一个time step即可等到事件的触发。
同一time step,可以wait 多次。
OK,开始正题。
1.2 UVM中的uvm_event
不同组件可以共享同一个uvm_event,这不需要通过跨层次传递uvm_event对象句柄来实现共享,它们是通过uvm_event_pool这一全局资源池来实现的。
在uvm_component类中包括一个protected变量,uvm_event_pool event_pool。该类的衍生如下。
这个event_pool内部具体是什么呢?
1.2.1 Pool classes
1.2.1.1 uvm_pool
实现了一个基于class的动态关联数组。是一个继承于uvm_object的 参数化类。
class uvm_pool #(type KEY=int, T=uvm_void) extends uvm_object;
...
typedef uvm_pool #(KEY,T) this_type;
static protected this_type m_global_pool;
protected T pool[KEY];
...
// Function: get_global_pool
//
// Returns the singleton global pool for the item type, T.
//
// This allows items to be shared amongst components throughout the
// verification environment.
static function this_type get_global_pool ();
if (m_global_pool==null)
m_global_pool = new("pool");
return m_global_pool;
endfunction
...
// Function: get_global
//
// Returns the specified item instance from the global item pool.
static function T get_global (KEY key);
this_type gpool;
gpool = get_global_pool();
return gpool.get(key);
endfunction
...
// Function: get
//
// Returns the item with the given ~key~.
//
// If no item exists by that key, a new item is created with that key
// and returned.
virtual function T get (KEY key);
if (!pool.exists(key)) begin
T default_value;
pool[key] = default_value;
end
return pool[key];
endfunction
...
m_global_pool 属性定义为static,实现在验证环境中组件间的共享,并在类中维护关联数组 pool[KEY]。
通过get_global 方法获取对象,如果对象不存在就会创建该对象。
1.2.1.2 uvm_object_string_pool
指定uvm_pool的索引类型为"string"。参数化存放的类型。
class uvm_object_string_pool #(type T=uvm_object) extends uvm_pool #(string,T);
typedef uvm_object_string_pool #(T) this_type;
static protected this_type m_global_pool;
...
// Function: get_global_pool
//
// Returns the singleton global pool for the item type, T.
//
// This allows items to be shared amongst components throughout the
// verification environment.
static function this_type get_global_pool ();
if (m_global_pool==null)
m_global_pool = new("global_pool");
return m_global_pool;
endfunction
// Function: get_global
//
// Returns the specified item instance from the global item pool.
static function T get_global (string key);
this_type gpool;
gpool = get_global_pool();
return gpool.get(key);
endfunction
// Function: get
//
// Returns the object item at the given string ~key~.
//
// If no item exists by the given ~key~, a new item is created for that key
// and returned.
virtual function T get (string key);
if (!pool.exists(key))
pool[key] = new (key);
return pool[key];
endfunction
...
uvm_event和uvm_barrier,即是对该类,决定存放的类型分别为uvm_event#(uvm_object)和 uvm_barrier。
typedef class uvm_barrier;
typedef class uvm_event;
typedef uvm_object_string_pool #(uvm_barrier) uvm_barrier_pool;
typedef uvm_object_string_pool #(uvm_event#(uvm_object)) uvm_event_pool;
如上,我们知道了uvm_event_pool 里面包含了一个共享的关联数组:
uvm_event#(object) pool [ string ]
通过uvm_event_pool::get_global(xx),以字符串的形式创建或引用一个uvm_event对象。进而在验证环境的组件中使用。
1.2.2 uvm_event 的 使用方法
- 在组件中使用uvm_event#(xx)来声明该T,xx需要继承于uvm_object。可在build phase中通过get_global在组件中创建或引用该uvm_event,如1.2.1.1所述。
- 可使用get_num_waiters()来获取等待它的进程数目,可以通过cancel function,在event被disabled或通过其他方式激活触发时,减掉num_waitters,保证计数正确。
1.2.2.1 触发uvm_event
通过event1.trigger(T data=null) ,如下,来触发该event1。与sv event不同的是,它可以选择传递参数,来携带更多的信息。虽然uvm_event参数化类定义了可变的T,但是uvm_event_pool 中定义的数据类型是uvm_event#(uvm_object),即限定了传入的参数类型须是uvm_object或其子类。
此外,uvm_event 也可以通过add_callback(uvm_event_callback cb, bit append=1) 函数来添加回调函数,在cb中定义pre_trigger或post_trigger,在trigger的时候,调用这两个方法。注意在定义pre_trigger 时,return 0才可以trigger event(skip=0)。
virtual function void uvm_event::trigger (T data=null);
int skip;
skip=0;
if (callbacks.size()) begin
for (int i=0;i<callbacks.size();i++) begin
uvm_event_callback#(T) tmp=callbacks[i];
skip = skip + tmp.pre_trigger(this,data);
end
end
if (skip==0) begin
->m_event;
if (callbacks.size()) begin
for (int i=0;i<callbacks.size();i++) begin
uvm_event_callback#(T) tmp=callbacks[i];
tmp.post_trigger(this,data);
end
end
num_waiters = 0;
on = 1;
trigger_time = $realtime;
trigger_data = data;
end
endfunction
1.2.2.1.1 uvm_event_callback
virtual class uvm_event_callback#(type T=uvm_object) extends uvm_object;
...
virtual function bit pre_trigger (uvm_event#(T) e, T data);
return 0;
endfunction
...
virtual function void post_trigger (uvm_event#(T) e, T data);
return;
endfunction
...
1.2.2.2 等待uvm_event
1.2.2.2.1 wait_trigger/wait_ptrigger
在uvm_event中,使用wait_trigger()/wait_ptigger(), wait_trigger_data(data)/wait_ptrigger_data(data) 来等待将要触发的事件。具体定义如下:
virtual task uvm_event::wait_trigger_data (output T data);
wait_trigger();
data = get_trigger_data();
endtask
virtual task uvm_event::wait_ptrigger_data (output T data);
wait_ptrigger();
data = get_trigger_data();
endtask
virtual task wait_trigger ();
num_waiters++;
@m_event;
endtask
virtual task wait_ptrigger ();
if (m_event.triggered)
return;
num_waiters++;
@m_event;
endtask
ptrigger 是针对event的persistent trigger,如1.3。wait_ptrigger 将触发视为在给定时间片内持久存在,从而避免了某些竞争条件。如果在触发之后但仍在同一时间片内调用此方法,调用者将立即返回。
注意等到事件触发后,并没有清除掉num_waiters。如果后续想获得等待该event的进程数量,需要在等到触发后进行reset。
1.2.2.2.2 wait_on
uvm_event还提供了wait_on task来等待event 的第一次的激活触发。主要基于内部变量on。该变量在trigger时,置1,然后一直保持1,直到event 被reset。
如果调用的时候已经被触发了,则立刻返回。
virtual task wait_on (bit delta = 0);
if (on) begin
if (delta)
#0;
return;
end
num_waiters++;
@on;
endtask
virtual function void reset (bit wakeup = 0);
event e;
if (wakeup)
->m_event;
m_event = e;
num_waiters = 0;
on = 0;
trigger_time = 0;
endfunction
2. uvm_barrier
uvm_barrier与uvm_event的实现方法类似。使用uvm_barrier_pool来管理uvm_barrier对象,如1.2.1。
不同的是,它可以设置一定的等待阈值(threshold),仅在有不少于该阈值的进程在等待该对象时 才触发该事件,同时激活所有正在等待的进程,使其继续进行。
class uvm_barrier extends uvm_object;
local int threshold;
local int num_waiters;
local bit at_threshold;
local bit auto_reset;
local uvm_event#(uvm_object) m_event;
...
virtual task wait_for();
if (at_threshold)
return;
num_waiters++;
if (num_waiters >= threshold) begin
if (!auto_reset)
at_threshold=1;
m_trigger();
return;
end
m_event.wait_trigger();
endtask
...
设置阈值时,如果当前等待事件的对象已经高于了阈值,则会复位,num_waitters清零,并选择性立即触发该事件或复位event。
virtual function void set_threshold (int threshold);
this.threshold = threshold;
if (threshold <= num_waiters)
reset(1);
endfunction
virtual function void reset (bit wakeup=1);
at_threshold = 0;
if (num_waiters) begin
if (wakeup)
m_event.trigger();
else
m_event.reset();
end
num_waiters = 0;
endfunction