Logic Design - Events (SystemVerilog)


[Edit of Image1]


Hey it's a me again @drifter1!

Today we continue with the Logic Design series on SystemVerilog in order to cover Events, which are one of the three main interprocess communication mechanisms of SystemVerilog. The other two will be covered next time.

So, without further ado, let's get straight into it!

Interprocess Communication

Processes often exchange data with each other, which is what is known as interprocess communication. SystemVerilog provides three main mechanisms for that purpose, that allow us to manage the control flow in processes. This is done through specific interactions between dynamic processes, that are basically synchronization techniques, but on a hardware level. They are of course only meant to be used in Testbenches for validation and verification purposes.

The mechanisms are the following:

  • Events : synchronization through event handlers (triggering and waiting for events)
  • Semaphores : synchronization mechanism which is necessary for accessing a shared resource in turns (mutual exclusion)
  • Mailboxes : mechanism for exchanging data between processes


In Verilog, events are static objects which are triggered via the -> operator and waited on via the @ operator. So, the @posedge(clk), @negedge(clk) and @(sensitivity_list) in general that we put in always blocks are also part of the event mechanism!

SystemVerilog enhances those events allowing them to be triggered for a longer duration. In Verilog, these events have no duration, whilst in SystemVerilog the trigger state perisists throughout the whole time step the event is triggered in. Additionaly, in SystemVerilog such event objects can also be used like synchronization queues, by being passed as arguments to tasks and functions.

Event Definition

Event objects are defined using the event keyword, and can be assigned to and compared to other event variables. When assigned to another event, both events / variables then point to the same synchronization object. It's also possible to assign a null value, which basically specifies no synchronization object.

event e1;           // a new event with identifier "e1"
event e2 = e1;      // "e2" and "e1" refer to the same event
event e3 = null;    // "e3" has no synchronization object

Triggering Events

Events can be triggered using the -> and ->> operators. Both of them unblock all processes which are currently waiting for the event to occur. The difference between them is about how the unblocking is executed on the calling process. The first one is of blocking nature, whilst the second one is non-blocking, which basically means that the trigger statement executes without blocking the execution of the statements after it.

Triggering event e1 can thus be done with either of the following two statements:


Waiting on Events

Blocking a process until an event is triggered can be done using the @ operator, as well as a wait on the event's triggered state, which can be accessed using .triggered. The second one should be used if there is a race condition between triggering and waiting for the event, as the triggered state persists throughout the simulation time step. So, using the @ operator the specific order in which waiting and triggering happens matters, whilst using .triggered the process will unblock independent of that order.

Waiting for event e1 is thus done using either of the following statements:


Event Sequencing

Of course, it's possible to make a process wait on multiple events. Waiting on events in a specific order can be done using wait_order(), which fails if the events are triggered out-of-order (from left-to-right in the arguments) and is basically a conditional statement.

So, if the events e1, e2 and e3 should occur in the order e1 -> e2 -> e3 for the process to unblock, the corresponding conditional statement would be:

wait_order(e1, e2, e3)
    // events triggered in-order
    // events triggered out-of-order

Merging Events

When an event variable is assigned to another, then all processes waiting on the first event will basically wait until the second event triggers.

For example the following definition:

event e1;
event e2 = e1;

would make any process waiting on e2 wait on e1 instead.

In that case, the following statements are basically all equal:


Of course, such an assignment can occur anywhere, and is not limited to the definition / declaration section, which allows for some very advanced synchronization!

Events as Arguments

Events can also be passed to functions, tasks or queues as arguments. This basically makes the task or function point to the corresponding synchronization object, allowing it to be used (trigger or wait) within the task or function.



  1. https://www.chipverify.com/systemverilog/systemverilog-tutorial
  2. https://www.asic-world.com/systemverilog/tutorial.html


  1. https://www.flickr.com/photos/creative_stock/5227842611

Block diagrams and other visualizations were made using draw.io

Previous articles of the series



  • From Verilog To SystemVerilog → Data Types, Arrays, Structures, Operators and Expressions
  • Control Flow → Additional Procedural Blocks, Loops, Conditional Statements, Functions and Task Features
  • Processes → Fork - Join in Verilog and SystemVerilog, Process Control (wait fork, disable fork)

Final words | Next up

And this is actually it for today's post!

Next time we will cover Semaphores and Mailboxes...

See Ya!

Keep on drifting!

Posted with STEMGeeks