Replace 12.6 and 12.7 with the following (adding 12.8 and moving the existing 12.8 to 12.9):

 

12.6     Event

In Verilog, named events are static objects that can be triggered via the -> operator, and processes can block until an event is triggered via the @ operator. SystemVerilog events support the same basic operations, but enhance Verilog events in several ways.  The most salient semantic difference is that Verilog named events do not have a value or duration, whereas SystemVerilog events can have a persistency that lasts throughout the time-step on which they are triggered.  Also, SystemVerilog events act as handles to synchronization queues, thus, they can be passed as arguments to tasks, and they can be dynamically allocated and reclaimed.  In this respect, SystemVerilog events behave like object handles; they can be assigned to one another, they can be assigned the special value null, they can be arguments to tasks (but not functions), and they can be dynamically allocated and reclaimed.

 

Existing Verilog event operations (@ and ->) are backward compatible and continue to work the same way when used in the static Verilog context.  The additional functionality described below works with all events in either the static or dynamic context.

 

A SystemVerilog event provides a handle to an underlying synchronization object.  When a process waits for an event to be triggered, the process is put on a FIFO queue maintained within the synchronization object.  Processes can wait for a SystemVerilog event to be triggered either via the @ operator or the wait() construct.  Events are always triggered using the -> operator.

 

SystemVerilog provides for two different types of events:  persistent events and non-persistent events.  These two are described below.

12.6.1 Non-Persistent Events

Non-persistent events are the same as named Verilog events.  They behave like a one-shot, that is, their triggered state is not observable, only its effect.  This is similar to the way in which an edge can trigger a latch but the state of the edge can not be ascertained: if( posedge clock ) is illegal.

 

Triggering a non-persistent event causes all processes currently waiting on the event to unblock.  For a trigger to unblock a process that is waiting on non-persistent event, that process must execute the wait (or @) before the triggering process executes the trigger operator, ->.  A process that executes wait() on a non-persistent event after the event has been triggered will block.

 

The syntax to declare a non-persistent event is:

     event event_identifier;

 

12.6.2 Persistent Events: event bit

Persistent events are similar to non-persistent events except that once triggered, the triggered state persists throughout the time-step, that is, until simulation time advances.  Thus, a persistent event will unblock all processes that execute the wait() construct either before or at the same simulation time as the trigger operation.

 

The persistent trigger behavior helps eliminate a common race condition that occurs when both the trigger and the wait operations happen at the same time.  A process that blocks on a regular (non-persistent) event may or may not unblock depending on the execution order of the waiting and triggering processes, while a persistent event always unblock the waiting process, regardless of the order of execution.

 

The syntax to declare a persistent event is:

     event bit event_identifier;

 

Persistent and non-persistent events support the same set of operators, but they are different types.  A persistent event may only be assigned (or passed as an argument) to another persistent event and vice-versa (see Section 11.6.2).

12.6.3 Triggering an Event

All events regardless of their type (persistent or non-persistent) are triggered via the -> operator.

 

The syntax to trigger an event is:

     -> event_identifier;

 

If the event_identifier is a persistent event then the event will remain in the triggered state until the simulation time advances.  Otherwise, the persistent state is unobservable.

Triggering a persistent event more than once at the same simulation time has no effect. However, triggering a non-persistent event more than once, at the same simulation time, results in multiple triggers.

12.6.4 Waiting for an Event

There are two mechanisms that can be used to wait for an event.  The @ operator and the wait construct.

 

The syntax for this use of the @ operator is:

     @ event_identifier;

           

The @ operator always blocks the calling process until an event is triggered.

 

The syntax for this use of the wait construct is:

     wait( event_identifier );

 

The wait construct blocks if the given event is a non-persistent event or the persistent event has not been triggered at the current simulation time.

 

Both mechanisms can be used to wait for either a persistent or a non-persistent event.  The wait construct is only meaningful when the event is persistent.

 

Examples:

event done;                       // declare a new event

event done_too = done;       // declare done_too as alias to done

event bit blast;                  // persistent event

 

task trigger( event ev );

  -> ev;

endtask

 

...

 

fork

@ done_too;                    // wait for done through done_too

trigger( done );                    // trigger done through task trigger

     join

 

event bit blast;                  // persistent event

 

fork  

           -> blast;                       // trigger blast event

wait( blast );            // wait for blast event

            join

 

The first fork in the examples shows how two event identifiers done and done_too refer to the same synchronization object, and also how an event can be passed to a generic task that will trigger either event.  In the example, the first process waits for the event via done_too, while the actual triggering is done via the trigger  task that is passed done as an argument.

 

When the second fork executes, the first process triggers the event blast before the second process has a chance to execute and wait for the event.  Nonetheless the second process unblocks and the fork terminates.  This is because blast is a persistent event so it remains in its triggered state for the duration of the time-step.  Note that if blast was declared as a non-persistent event the second process would never unblock.

 

12.6      Event Synchronization Utilities

12.6.1 $wait_all()

The $wait_all system tasks suspends the calling process until all of the specified events are triggered.

 

The syntax for the $wait_all task is:

     $wait_all( event_identifier {, event_identifier } )

 

For example:

    $wait_all( a, b, c);

suspends the current process until the 3 events a, b, and c are triggered.

 

Any of the specified events may be triggered more than once; the only requirement to unblock the calling process is that each event be triggered at least once.

12.6.2 $wait_any()

The $wait_any system tasks suspends the calling process until any of the specified events are triggered.

 

The syntax for the $wait_any task is:

     $wait_any( event_identifier {, event_identifier } )

 

For example:

    $wait_any( a, b, c);

suspends the current process until either event a, or event b, or event c is triggered.

12.6.3 $wait_order()

The $wait_order system task suspends the calling process until all of the specified events are triggered (similar to $wait_all), but the events must be triggered in the given order (left to right).  If an event is received out of order, the process unblocks and generates a run-time error.

 

The syntax for the $wait_order task is:

     $wait_order( event_identifier {, event_identifier } )

 

When $wait_order() is called, only the first event in the list can be in the triggered state.  If any other persistent event is in triggered state, it generates a run-time error.

 

For example:

    $wait_order( a, b, c);

suspends the current process until events trigger in the order a –> b –> c.

 

12.7      Event Variables

An event is a unique data type with several important properties.  Unlike Verilog, SystemVerilog events can be assigned to one another.  When one event is assigned to another the synchronization queue of the source event is shared by both the source and the destination event.  In this sense, events act as full fledged variables and not merely as labels.

12.7.1 Disabling Events

If an event variable is assigned the special null value, the event is ignored in subsequent calls to wait(). That is, when the event is set to null, no process can wait for the event again.

 

For example:

event E1 = null;

@ E1;

The statement @ E1 does not block because event E1 is no longer blocking.

12.7.2 Merging Events

When one event variable is assigned to another, the two become merged.  Thus, executing -> on either event variable affects processes waiting on either event variable.

 

For example:

event a, b, c;

a = b;

-> c;

-> a;        // also triggers b

-> b;        // also triggers a

a = c;

b = a;

-> a;      // also triggers b and c

-> b;        // also triggers a and c

-> c;        // also triggers a and b

 

When merging events, the assignment only affects subsequent executions of ->and wait().  If a process is blocked waiting for event1 when another event is assigned to event1, the wait() will never unblock.  For example:

 

fork

T1: while(1) @ E2;

T2: while(1) @ E1;

T3: begin

E2 = E1;

while(1) -> E2;

                end

join

 

This example forks off three concurrent processes. Each process starts at the same time. Thus, at the same time that process T1 and T2 are blocked, process T3 assigns event E1 to E2. This means that process T1 will never unblock, because the event E2 is now E1. To unblock both threads T1 and T2, the merger of E2 and E1 must take place before the fork.