In SystemVerilog, events are powerful synchronization tools used to coordinate between multiple processes. They are static objects that allow one process to trigger an event while another process waits for it. In this guide, we will explore how events work in SystemVerilog and how they can be used effectively in simulations.

What is a SystemVerilog Event?

An event in SystemVerilog is a handle that synchronizes multiple processes. One process triggers the event, and other processes wait for it. Here’s a breakdown of key features of events in SystemVerilog:

  • Assignable: Events can be assigned to other events.
  • Null Assignment: Events can be assigned a null value, meaning no synchronization object is attached.
  • Sharing Events: When one event is assigned to another, both variables will refer to the same synchronization object.
  • Passing Events: Events can be passed to queues, functions, and tasks.

Example of Event Assignment

event event1;                // Declare a new event
event event2 = event1;       // event2 is an alias for event1
event event3 = null;         // event3 is not connected to any synchronization object

Triggering and Waiting for Events

SystemVerilog provides several ways for processes to interact with events, including:

  • Triggering an Event: The -> or ->> operator is used to trigger an event.
  • Waiting for an Event: The @ operator or .triggered function is used to wait for an event to be triggered.

Example 1: Simple Event Synchronization

In this example, we will create an event and use different processes to trigger and wait for it.

module tb;

  // Declare an event
  event event_a;

  // Thread1: Triggers the event using "->" operator
  initial begin
    #20 -> event_a;
    $display ("[%0t] Thread1: triggered event_a", $time);
  end

  // Thread2: Waits for the event using "@" operator
  initial begin
    $display ("[%0t] Thread2: waiting for trigger", $time);
    @(event_a);
    $display ("[%0t] Thread2: received event_a trigger", $time);
  end

  // Thread3: Waits for the event using ".triggered"
  initial begin
    $display ("[%0t] Thread3: waiting for trigger", $time);
    wait(event_a.triggered);
    $display ("[%0t] Thread3: received event_a trigger", $time);
  end

endmodule

Output Simulation Log

[0] Thread2: waiting for trigger
[0] Thread3: waiting for trigger
[20] Thread1: triggered event_a
[20] Thread2: received event_a trigger
[20] Thread3: received event_a trigger

In this example, Thread1 triggers the event after 20 time units. Both Thread2 and Thread3 are waiting for the event to be triggered. As a result, both threads receive the event trigger at the same time.

Race Condition in Event Synchronization

A race condition can occur when two processes are trying to trigger and wait for an event at the same time. The state of the event persists until the simulation advances, and if the event is triggered and awaited at the same time, it can lead to unexpected behavior. However, the triggered state ensures that a process will unblock once the event is triggered, regardless of the order of wait and trigger.

Example 2: Race Condition Scenario

module tb;

  // Create an event
  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: Waits 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: Waits 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

Output Simulation Log

[0] Thread2: waiting for trigger
[0] Thread3: waiting for trigger
[20] Thread1: triggered event_a
[20] Thread3: received event_a trigger

In this case, Thread2 never receives the trigger because the @ operator and the -> operator executed at the same time, causing a race condition. The triggered state, however, ensures that Thread3 successfully waits for the event.

Waiting for Events in Specific Order

SystemVerilog allows you to wait for events in a specific order. The wait_order function ensures that events are triggered and processed in the correct sequence.

Example 3: Event Execution Order

module tb;

  // Declare three events
  event a, b, c;

  // Trigger events in a specific order
  initial begin
    #10 -> a;
    #10 -> b;
    #10 -> c;
  end

  // Wait for the events in the correct order
  initial begin
    wait_order(a, b, c);
    $display ("Events were executed in the correct order");
  end

endmodule

Output Simulation Log

Events were executed in the correct order

Assigning One Event to Another

When one event variable is assigned to another, all processes waiting for the first event will wait for the second event to be triggered.

Example 4: Event Assignment Synchronization

module tb;

  // Declare event variables
  event event_a, event_b;

  initial begin
    fork
      // Thread1: Waits for event_a
      begin
        wait(event_a.triggered);
        $display ("[%0t] Thread1: Wait for event_a is over", $time);
      end
      // Thread2: Waits for event_b
      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
        #10 event_b = event_a;
      end
    join
  end
endmodule

Output Simulation Log

[20] Thread1: Wait for event_a is over
[20] Thread2: Wait for event_b is over

In this example, Thread5 assigns event_b to event_a, so both threads waiting for event_a and event_b will wait for the same event to trigger.


Conclusion

SystemVerilog events are essential for synchronizing multiple processes. Whether you’re triggering events, waiting for them, or managing their order, events provide an efficient way to handle concurrency in your simulation. By understanding how to trigger and wait for events, handle race conditions, and ensure correct execution order, you can create more robust and reliable SystemVerilog simulations.

For more detailed and advanced topics, explore event handling, race conditions, and synchronization techniques in your next simulation project!

Scroll to Top