@event Vs wait(event.triggered) in SystemVerilog

SystemVerilog supports two ways through which we can wait for a particular event to be triggered. So let’s understand what is the exact difference between those two ways of event trigger with the following example.

event e;
// Triggered event 'e'
->e;

// wait for event 'e' to be triggered (instantaneous)
@(e);

An event trigger ->e is an instantaneous event. The event control @e has to execute and block the current process before the trigger occurs in another process, and the blocking process resumes. Many times there are race conditions between the two processes and the trigger executes before the event control, and the event is missed, and the event control has to wait for another trigger.

// wait for event 'e' to be triggered (Persistent)
wait(e.triggered);

The wait(e.triggered) statement says wait for the condition that event e has been triggered in the current time slot means it evaluates as true (1’b1) if event e has been triggered in the current time slot else false (1’b0). 

Now you no longer have to worry about which came first, the trigger or the wait statement. But you still have to execute the wait statement in the current time slot to catch the event.

the triggering process –> or the waiting process @ statement. But you still have to execute the waiting process @ in the current time slot to catch the event.
Let’s see the actual behavior of event trigger and wait statements with below examples

Example_1: Missed Event Trigger

class event_class;
  event e;

  task trigger;
    ->e;
  endtask : trigger
   
  task execute;
    @(e);
  $display("Testing of the event @");
  endtask : execute
   
  // First trigger event and after that
  // wait for event to be triggered in same simulation time
  task ex;
    trigger;
    execute;
  endtask : ex
endclass : event_class

program main();
  event_class E;

  initial begin
    E = new();
    fork
      begin
        E.ex; // wait using @
      end
      begin
        #10;
        $display("Missed Event trigger");
      end
    join_any
    disable fork;
  end
endprogram : main

Example_2: Testing of the event @ (wait)

class event_class;
  event e;
 
  task trigger;
    ->e;
  endtask : trigger

  task execute;
    wait(e.triggered);
    $display("Testing of the event @(wait)");
  endtask : execute

  // First trigger event and after that
  // wait for event to be triggered in same simulation time
  task ex;
    trigger;
    execute;
  endtask : ex
endclass : event_class

program main;
  event_class E;

  initial begin
    E = new();
    fork
      begin
        E.ex; // wait using wait(event.triggered)
      end
      begin
        #10;
        $display("Missed Even Trigger");
      end
    join_any
    disable fork;
  end
endprogram : main

Non-Blocking Event ->>

Non-blocking events are triggered using the ->> operator.

The effect of the ->> operator is that the statement executes without blocking and it creates a non-blocking assign update event in the time in which the delay control expires or the event control occurs. The effect of this update event shall be to trigger the referenced event in the nonblocking assignment region of the simulation cycle.

Let’s understand behavior of Non-blocking Event with below example

program main;
  event blocking_e;
  event non_blocking_e;
  process process_b;
  process process_nb;

  initial begin
    fork
      begin
        process_b = process::self();
        -> blocking_e;
        @ blocking_e;
      end
      begin
        process_nb = process::self();
        ->> non_blocking_e;
        @ non_blocking_e;
      end
    join_none
    #10;
    $display("process_b.status:%s", process_b.status);
    $display("process_nb.status:%s", process_nb.status);
  end
endprogram : main