参考文献: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,因为这两个句柄指向同一个事件对象。