参考文献:https://www.chipverify.com/systemverilog/systemverilog-event
event和旗语semaphore以及mailbox都是用于线程间的通信(IPC),负责线程之间的数据交换和同步。
EVENT
事件是静态的同步对象句柄(可以像参数一样在子程序中传递),它用来同步多个并发的进程,比如某个进程等待着事件,而另一个进程则触发这个事件。
几个特征:
- 可以被赋值成null
- 可以被赋值给其它事件,这样两个事件变量(句柄)会指向同一个同步化对象,触发任意一个变量就触发这个事件, 如下所示
event eve;
event tmp;
initial begin
tmp = eve;
#10 -> tmp;
end
initial begin
wait(eve.triggered());
$display("@%0t : event eve receive trigger",$time);
end
------------------------------------
@10 : event eve receive trigger //执行结果
-------------------------------------
- 可以传给队列,函数和任务
事件的触发和阻塞等待
触发: -> (or ->>) event_handle
等待:@ or wait(event_handle.triggered)
两种等待条件的区别以及竞争条件的发生
首先分析竞争的产生。如下代码所示:
module tb;
// Create an event variable that processes can use to trigger and wait
event event_a;
// Thread1: Triggers the event using "->" operator at 20ns
initial begin
#20 ->event_a;
$display ("[%0t] Thread1: triggered event_a", $time);
end
// Thread2: Starts waiting for the event using "@" operator at 20ns
initial begin
$display ("[%0t] Thread2: waiting for trigger ", $time);
#20 @(event_a);
$display ("[%0t] Thread2: received event_a trigger ", $time);
end
// Thread3: Starts waiting for the event using ".triggered" at 20ns
initial begin
$display ("[%0t] Thread3: waiting for trigger ", $time);
#20 wait(event_a.triggered);
$display ("[%0t] Thread3: received event_a trigger", $time);
end
endmodule
上面module的运行结果是
[0] Thread2: waiting for trigger
[0] Thread3: waiting for trigger
[20] Thread1: triggered event_a
[20] Thread3: received event_a trigger
发现使用@的阻塞等待没有收到trigger。分析如下:事件的触发状态只会在当前时间步长保持,一但仿真开始向前运行,就会失去这个状态。也就是说,在仿真时间20的这个时间步长中(可以把它想象成1ns或1ps),事件是处于触发状态的,到21仿真时间后触发状态消失。在上面的代码中,两个阻塞等待@和.triggered都是和事件触发同时发生的,出现了竞争条件,结果是虽然触发和等待发生在同一个仿真时间,但是还有执行顺序,根据代码顺序,先执行事件的触发,由于@事件控制是边沿敏感的,等到执行@语句时,事件的触发的“上升沿”已经消失,所以@并没有被触发,而@和.triggered的不同就在于此,@是边沿敏感,后者是电平敏感,所以在整个20仿真时间步长中,wait语句都能被触发。
触发和等待在同一时间开始的话就不要用@改用.triggered,当然如果触发进程在等待进程(的仿真时间)之前发生,那这两种等待都不会收到trigger。
其实将Thread2initial块移动到Thread1 initial块之前,也能让@阻塞等待被触发,这就是因为@的执行顺序在触发事件之前。
wait_order的使用
wait_order阻塞等待多个事件的触发,并且要求这多个事件按照用户决定顺序触发。wait_order可以和else一同使用,当多个事件按顺序触发时,执行wait_order后的语句,否则执行else后的语句。
module tb;
// Declare three events that can be triggered separately
event a, b, c;
// This block triggers each event one by one
initial begin
#10 -> a;
#10 -> b;
#10 -> c;
end
// This block waits until each event is triggered in the given order
initial begin
wait_order (a,b,c)
$display ("Events were executed in the correct order");
else
$display ("Events were NOT executed in the correct order !");
end
endmodule
--------------------------------------------------------
Events were executed in the correct order //执行结果
--------------------------------------------------------------
事件的合并
之前提到一个事件可以赋值给其它的事件变量,这样两个事件变量实际指向同一个事件对象,触发任意一个变量就是触发了这个对象
module tb;
// Create event variables
event event_a, event_b;
initial begin
fork
// Thread1: waits for event_a to be triggered
begin
wait(event_a.triggered);
$display ("[%0t] Thread1: Wait for event_a is over", $time);
end
// Thread2: waits for event_b to be triggered
begin
wait(event_b.triggered);
$display ("[%0t] Thread2: Wait for event_b is over", $time);
end
// Thread3: triggers event_a at 20ns
#20 ->event_a;
// Thread4: triggers event_b at 30ns
#30 ->event_b;
// Thread5: Assigns event_b to event_a at 10ns
begin
// Comment code below and try again to see Thread2 finish later
#10 event_b = event_a;
end
join
end
endmodule
--------------------------------------------------------------------
[20] Thread1: Wait for event_a is over
[20] Thread2: Wait for event_b is over
---------------------------------------------------------------------
上面结果可见,触发event_a后也触发了event_b,因为这两个句柄指向同一个事件对象。