Tutorials

Learn More

Interprocess communication is a way to communicate between processes or testbench components. SystemVerilog provides three mechanisms for communication.

  1. Events
  2. Semaphores
  3. Mailbox

SystemVerilog event

SystemVerilog event is used to synchronize between two or more processes or threads. An event is also a synchronization object that can be passed to a function or task or class constructor. This allows event sharing without declaring it as a global event.

How two or more processes are synchronized?

One process triggers an event while other processes will wait until the event is triggered.

The System  Verilog events are triggered using -> or ->> operator. The processes can wait for an event to be triggered either via @ operator or wait() construct.

Syntax:

// To trigger an event
-> <event_name>;
->> <event_name>; 

//wait for an event
@(<event_name>); or @(<event_name>.triggered);
wait(<event_name>.triggered);

Event operator

Description

->

Used to trigger an event that unblocks all waiting processes due to this event. It is an instantaneous event.

->>

This operator is used to trigger non-blocking events.

@

The @ operator is used to block the process till an event is triggered. This is an edge-sensitive operator. Hence, waiting for an event should be executed before triggering an event to avoid blocking the waiting process.

wait

The wait() construct is similar to @ operator except it will unblock the process even if triggering an event and waiting for an event to happen at the same time.

SystemVerilog event Examples

I. Event is triggered using -> and waiting for SystemVerilog event to be triggered via the @ operator

For example, there are two processes A and B. The process_A task is used to trigger an event e1 and the process_B task is used to wait for the event using @ operator.

Type A: An event is triggered after waiting for the event trigger

The process_A task has a 10ns delay which makes sure event e1 triggers after waiting for the event trigger. The wait for the event to be triggered via @ operator will be unblocked once the e1 event is triggered.

module event_example();
  event e1;
  
  task process_A();
    #10;
    $display("@%0t: Before triggering event e1", $time);
    ->e1;
    $display("@%0t: After triggering event e1", $time);
  endtask
  
  task process_B();
    $display("@%0t: waiting for the event e1", $time);
    @e1;
    $display("@%0t: event e1 is triggered", $time);
  endtask

  initial begin
    fork
      process_A();
      process_B();
    join
  end
endmodule

Output:

@0: waiting for the event e1
@10: Before triggering event e1
@10: After triggering event e1
@10: event e1 is triggered
Type B: An event is triggered before waiting for event trigger

The process_B task has a 10ns delay which makes sure event e1 triggers before waiting for an event trigger. The wait for the event to be triggered via @ operator will not be unblocked since the e1 event is triggered before. Hence, statements after waiting for the trigger (with @ operator) will not be executed.

module event_example();
  event e1;
  
  task process_A();
    $display("@%0t: Before triggering event e1", $time);
    ->e1;
    $display("@%0t: After triggering event e1", $time);
  endtask
  
  task process_B();
    #10;
    $display("@%0t: waiting for the event e1", $time);
    @e1;
    $display("@%0t: event e1 is triggered", $time);
  endtask

  initial begin
    fork
      process_A();
      process_B();
    join
  end
endmodule

Output:

@0: Before triggering event e1
@0: After triggering event e1
@10: waiting for the event e1
Type C: An event is triggered at the same time as waiting for the event trigger

The process_A and process_B have no delay involved to ensure triggering of an event and waiting for the event trigger to happen at the same time. Since both processes are triggered at the same time, the @ operator will not detect an event triggering. The SystemVerilog provides a wait() construct to solve this problem (Check 2. Type C).

module event_example();
  event e1;
  
  task process_A();
    $display("@%0t: Before triggering event e1", $time);
    ->e1;
    $display("@%0t: After triggering event e1", $time);
  endtask
  
  task process_B();
    $display("@%0t: waiting for the event e1", $time);
    @e1;
    $display("@%0t: event e1 is triggered", $time);
  endtask

  initial begin
    fork
      process_A();
      process_B();
    join
  end
endmodule

Output:

@0: Before triggering event e1
@0: After triggering event e1
@0: waiting for the event e1

II. Event is triggered using -> and waiting for SystemVerilog event to be triggered via wait() construct

For example, there are two processes A and B. The process_A task is used to trigger an event e1 and the process_B task is used to wait for the event using the wait() construct.

Type A: An event is triggered after waiting for the event trigger.

The process_A task has a 10ns delay which makes sure event e1 triggers after waiting for the event trigger. The wait of the event to be triggered via wait() construct will be unblocked once the e1 event is triggered.

module event_example();
  event e1;
  
  task process_A();
    #10;
    $display("@%0t: Before triggering event e1", $time);
    ->e1;
    $display("@%0t: After triggering event e1", $time);
  endtask
  
  task process_B();
    $display("@%0t: waiting for the event e1", $time);
    wait(e1.triggered);
    $display("@%0t: event e1 is triggered", $time);
  endtask

  initial begin
    fork
      process_A();
      process_B();
    join
  end
endmodule

Output:

@0: waiting for the event e1
@10: Before triggering event e1
@10: After triggering event e1
@10: event e1 is triggered
Type B: An event is triggered before waiting for event trigger

The process_B task has a 10ns delay which makes sure event e1 triggers before waiting for an event trigger. The wait of the event to be triggered via wait() construct will not be unblocked since the e1 event is triggered before. Hence, statements after waiting for the trigger (with wait() construct) will not be executed.

module event_example();
  event e1;
  
  task process_A();
    $display("@%0t: Before triggering event e1", $time);
    ->e1;
    $display("@%0t: After triggering event e1", $time);
  endtask
  
  task process_B();
    #10;
    $display("@%0t: waiting for the event e1", $time);
    wait(e1.triggered);
    $display("@%0t: event e1 is triggered", $time);
  endtask

  initial begin
    fork
      process_A();
      process_B();
    join
  end
endmodule

Output:

@0: Before triggering event e1
@0: After triggering event e1
@10: waiting for the event e1
Type C: An event is triggered at the same time as waiting for the event trigger.

The process_A and process_B have no delay involved to ensure triggering of an event and waiting for even triggers happens at the same time and wait () construct will detect an event triggering.

module event_example();
  event e1;
  
  task process_A();
    $display("@%0t: Before triggering event e1", $time);
    ->e1;
    $display("@%0t: After triggering event e1", $time);
  endtask
  
  task process_B();
    $display("@%0t: waiting for the event e1", $time);
    wait(e1.triggered);
    $display("@%0t: event e1 is triggered", $time);
  endtask

  initial begin
    fork
      process_A();
      process_B();
    join
  end
endmodule

Output:

@0: Before triggering event e1
@0: After triggering event e1
@0: waiting for the event e1
@0: event e1 is triggered

Difference between @(event) and wait (event.triggered)

@(event): The waiting for an event using @ operator blocks the current process until an event is triggered. In a certain condition like waiting for an event and triggering an event can occur at the same time, this will cause race conditions between them and triggering an event will be missed if the wait for an event happens earlier.

wait(event.triggered): The waiting for an event using the wait() construct will unblock the waiting process even if an event is triggered at the time. Thus, the wait() construct eliminates race around condition between waiting for an event and triggering an event.

In short, wait() construct catches an event triggering at the same simulation whereas @ operator waiting for an event would lead to race conditions.

To explain using an example, three processes execute in the same simulation time.

process_A: Triggers at event e1;

process_B: wait for event e1 using @ operator

process_C: wait for event e1 using wait() construct

The process_C will be unblocked due to event e1 triggering whereas process B is blocked due to race around condition.

module event_example();
  event e1;
  
  task process_A();
    $display("@%0t: Process A: Before triggering event e1", $time);
    ->e1;
    $display("@%0t: Process A: After triggering event e1", $time);
  endtask
  
  task process_B();
    $display("@%0t: Process B: waiting for the event e1 using @", $time);
    @e1;
    $display("@%0t: Process B: event e1 is triggered using @", $time);
  endtask

  task process_C();
    $display("@%0t: Process C: waiting for the event e1 using wait(e1.triggered)", $time);
    wait(e1.triggered);
    $display("@%0t: Process C: event e1 is triggered using wait(e1.triggered)", $time);
  endtask
  
  initial begin
    fork
      process_A();
      process_B();
      process_C();
    join
  end
endmodule

Output:

@0: Process A: Before triggering event e1
@0: Process A: After triggering event e1
@0: Process B: waiting for the event e1 using @
@0: Process C: waiting for the event e1 using wait(e1.triggered)
@0: Process C: event e1 is triggered using wait(e1.triggered)

System Verilog Tutorials