The difference between @(eventname.triggered) and @(eventname)

Hi
I understand that we can use the statement ‘wait(eventname.triggered);’ in order to avoid certain event-related race conditions. But I don’t realize the difference between @(eventname.triggered) and @(eventname). For all I know, they are equivalent and can replace each other. Am I right? Or is there some subtle difference between the two?
Thanks in advance
Farhad

In reply to Farhad:
1800 clarified this, though I never used the event_name.triggered.

15.5.3 Persistent trigger: triggered built-in method
SystemVerilog can distinguish the event trigger itself, which is instantaneous, from the named event’s triggered state, which persists throughout the time step (i.e., until simulation time advances). The triggered built-in method of a named event allows users to examine this state.
The prototype for the triggered() method is as follows:
function bit triggered();
The triggered method evaluates to true (1’b1) if the given event has been triggered in the current time step and false (1’b0) otherwise. If the named event is null, then the triggered method returns false.
The triggered method is most useful when used in the context of a wait construct:
wait ( hierarchical_event_identifier.triggered )
Using this mechanism, an event trigger shall unblock the waiting process whether the wait executes before or at the same simulation time as the trigger operation. The triggered method, thus, helps eliminate a common race condition that occurs when both the trigger and the wait happen at the same time. A process that blocks waiting for an event might or might not unblock, depending on the execution order of the waiting and triggering processes. However, a process that waits on the triggered state always unblocks, regardless of
the order of execution of the wait and trigger operations.
Example:


event done, blast; // declare two new events
event done_too = done; // declare done_too as alias to done
task trigger( event ev );   -> ev; endtask
...
fork
@ done_too; // wait for done through done_too
#1 trigger( done ); // trigger done through task trigger
join
fork  
-> blast;
wait ( blast.triggered );
join

The first fork in the example 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 triggers the event. In the example, one 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.
In the second fork, one process can trigger the event blast before the other process (if the processes in the fork-join execute in source order) has a chance to execute, and wait for the event. Nonetheless, the second process unblocks and the fork terminates. This is because the process waits for the event’s triggered state, which remains in its triggered state for the duration of the time step.
An event expression or wait condition is only reevaluated on a change to an operand in the expression, such as the event prefix of the triggered method. This means that the change of the return value of the triggered method from 1’b1 to 1’b0 at the end of the current time step will not affect an event control or wait statement waiting on the triggered method.

In reply to ben@SystemVerilog.us:

Thank you very much, ben. I read your reply, and it explains the usage of event_name.triggered with the wait statement very well. But unless I’m missing the point, it doesn’t quite answer my question. What I’m confused about is the difference between eventname.triggered and eventname, when used with the @ operator, not the wait statement. I have seen examples that use @(event_name), while some other examples use @(event_name.triggered). I assume these two are equivalent but I’m not sure if I’m right.

In reply to Farhad:

When used with the @ construct, @(event_name) and @(event_name.triggered()) have the same behavior in certain cases. But saying they are equivalent is misleading. It’s like saying the expression (1’b1) is equivalent to expression ('h139295 < 'h139395), but why would I ever want to write the latter if I just meant to write 1’b1.

A named event is a valueless variable that generates an event using the → or ->> operators. An event control construct @() blocks the current process until a generated named event, or the change of the value of any expression.

The triggered() method of an event variable is a function that returns 1’b1 or 1’b0 as Ben explains. When you put any expression in an event control, that expression gets evaluated whenever any operand changes, and if the expression changes value, the event control resumes the process. When using @(event_name.triggered), the generated event causes the triggered() method to get evaluated. The first time this happens in a time step, the value of the expression changes from 1’b0 to 1’b1. But the second time, there is no change. Note that the event control never sees the change of the triggered() result going from 1’b1 to 1’b0 because there is no operand changing to cause reevaluation of the expression.

Try this little example:

module top;
  event event_name;
  initial begin
    #10
    -> event_name;
    ->>event_name; // race condition if I just used ->
     #10
    -> event_name;
    ->>event_name; // race condition if I just used ->
  end
  always @(event_name) $display($time,,"@(event_name)");
  always @(event_name.triggered) $display($time,,"@(event_name.triggered)");
endmodule

In reply to dave_59:

Note that the event control never sees the change of the triggered() result going from 1’b1 to 1’b0 because there is no operand changing to cause reevaluation of the expression.

So what causes the triggered method to go from 1’b1 to 1’b0?
Why does it work in this wait statement?
From 1800: “An event expression or wait condition is only reevaluated on a change to an operand in the expression, such as the event prefix of the triggered method. This means that the change of the return value of the triggered method from 1’b1 to 1’b0 at the end of the current time step will not affect an event control or wait statement waiting on the triggered method.”
If" triggered method from 1’b1 to 1’b0 at the end of the current time step",
Then in your example Edit code - EDA Playground
At 20, why don’t we see the triggered going back to an evaluation from 0 to 1?

The mysterious side of systemverilog… :)

In reply to ben@SystemVerilog.us:

Ben,

The triggered() method returns 0 when called in a time slot before the trigger ( or ->>). The key is knowing what situation causes the method to get called when used in an event control.

In my example, triggered() gets called at time 0 upon encountering the event control
@(event_control.triggered())
which returns 0 and suspends the always process. Then at time 10, event_name triggers which causes the second evaluation of the
event_control.triggered()
method. It returns 1 which causes the always process to resume and prints the $display statement. And since there are no other delays in the always block, it starts from the top and encounters the
@(event_control.triggered())
for the third time, which still returns 1. Finally the event_name triggers again and the triggered method gets called for the fourth time returning 1, which is the same value the expression had the last time it got evaluated.

So other than time 0, there is no event that causes evaluation of the triggered method to return 0. That would change if I put a unit delay in the always block

always @(event_name.triggered) #1 $display($time,,"@(event_name.triggered)");

Now the event control gets encountered at times 0,11, and 21. The triggered() method would return 0 each time. [br]
The crux of of this discussion hopefully leads you way from using events unless you are one of thetwo dozen people who really understand them. And that the triggered method no longer has any use since the non-blocking trigger construct ->> was added to SystemVerilog to prevent races between and @event_name.

In reply to dave_59:
Dave,
Many thanks for your explanation and support. I would like to share with this community your other example and add a few more words.

  1. The syntax (only relevant parts shown here)

event_control ::=
@ hierarchical_event_identifier
| @ ( event_expression )
event_expression ::=
[ edge_identifier ] expression [ iff expression ] // int i; event e;
// e.g., (posedge clk), (I), (e.triggered)
| ( event_expression )
//    (e)
  1. Reevaluation: The entire event control expression essentially has static value, and there needs to be an event on one of the visible operands in the expression for it to get reevaluated.
  2. Blocking: If the event_expression is true, it unblocks.
  3. the e.triggered: The triggered() method returns 0 when called in a time slot before the trigger (-> or ->>). It is static for the process that made the call.
    If the(e.triggered) changes in value it unblocks. For example:

always @(e.triggered) begin // CALL THIS Z (for zero)
$display("%t  NO delay after the unblock",  $realtime);
-> e1;
end
always @(e.triggered) begin // CALL THIS D (for delay)
$display("%t  With #4 delay after the unblock ",  $realtime);
-> e2;
#4;
end 

The Z always-block, without the #4 delay, unblocks once. This is because

  • 5. After 1st evaluation at 70ns, e.triggered has a static value of 1 (was 0).
    6. At the end of the Z always-block (at 70ns since the block has no delay statement), the e.triggered gets reevaluated because it goes back to the always statement and returns a 1 (no change in the same time step). Thus, that Z always-block gets blocked
    7. After 2nd evaluation at 110ns, e.triggered returns a 1 because at 110ns, there was an event e. In that 2nd evaluation, the @(e.triggered) sees no change (was a 1 at 70ns, is a 1 at 110ns), thus no unblock!


    The D always-block, with the #4 delay, unblocks at every event. This is because

    - After 1st evaluation at 70ns, e.triggered has a static value of 1 (was 0).
    - At the end of the D always-block (at 74ns since the block has a 4ns delay statement), the e.triggered gets reevaluated because it goes back to the always statement and returns a 0 (no event in that time step).
    - After 2nd evaluation at 110ns, e.triggered returns a 1 because at 110ns, there was an event e. In that 2nd evaluation, the @(e.triggered) sees a change (was a 0 at 74ns, is a 1 at 110ns), thus it unblocks!



    http://SystemVerilog.us/vf/eventtrig.sv



Dave gave me this other example using a generic function, instead of the .triggered. That explains the same concept as well:


module top;
  bit A,C=1;
  int B;

  function automatic bit AB  (int in);
    $display("AB called %4d, a=%b, b=%d, c=%b",$time, A, B, C);
    return A || (B>2);
  endfunction

  always @(AB(B) && C)
     $display("AB changed %3d, , a=%b, b=%d, c=%b",$time, A, B, C); 

  initial begin
    #1 B = 1; // called no change
    #1 A = 1; // not caled
    #1 B = 0; // called changed
    #1 A = 0; // not called
    #1 C = 0; // called changed
    #1;
    $finish;
  end

endmodule
/* Simulation results
# AB called    0, a=0, b=          0, c=1
# AB called    1, a=0, b=          1, c=1
# AB called    3, a=1, b=          0, c=1
# AB changed   3, , a=1, b=          0, c=1
# AB called    3, a=1, b=          0, c=1
# AB called    5, a=0, b=          0, c=0
# AB changed   5, , a=0, b=          0, c=0
# AB called    5, a=0, b=          0, c=0 */ 

Thank you very much, Dave and Ben. I learned a a lot from your detailed replies. I really appreciate your taking the time and sharing your knowledge and experience.

In reply to dave_59:

Dave, there is just one more point which I want to make sure of. When I simulated your example, my initial guess was that the term “@(event_name.triggered)” would be printed twice, first at #10 and then at #20. But I saw in the results that it was only printed once at #10. I guess the reason for this is that, as Ben explained, “…the change of the return value of the triggered method from 1’b1 to 1’b0 at the end of the current time step will not affect an event control or wait statement waiting on the triggered method.”.
So, in short, the event control construct does not see the return of the triggered from 1 to 0 at the end of time step at #10, therefore, it’s re-becoming 1 again at #20 will be invisible too. The event control construct never saw the method become 0, so when at 20 it sees the value of 1, it won’t detect any change.
Is my understanding correct?

In reply to Farhad:

Correct.

Just remember if you ever find the triggered method in an event control, you have probably found a bug.

In reply to dave_59:

I see, thank you very much.