Replace 12.6 and 12.7 with the following (adding 12.8 and moving the existing 12.8 to 12.9):
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.
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;
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).
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.
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.
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.
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.
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.
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.
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.
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.