Can anybody please explain why that's the order of the outputs for each case?
1.
event e1, e2 ;
initial begin
fork
begin : WAITER_1
$display ( "%m waiting event e1" ) ;
@ e1
$display ( "%m received event e1" ) ;
end : WAITER_1
begin : EMITTER_1
$display ( "%m emitting event e1" ) ;
-> e1 ;
end : EMITTER_1
begin : EMITTER_2
$display ( "%m emitting event e2" ) ;
-> e2 ;
end : EMITTER_2
begin : WAITER_2
$display ( "%m waiting event e2" ) ;
@ e2
$display ( "%m received event e2" ) ;
end : WAITER_2
join_none
#10ns;
disable fork;
output:
event_m.WAITER_1 waiting event e1
event_m.EMITTER_1 emitting event e1
event_m.EMITTER_2 emitting event e2
event_m.WAITER_2 waiting event e2
event_m.WAITER_1 received event e1
2.
event e1, e2 ;
initial begin
fork
begin : WAITER_1
$display ( "%m waiting event e1" ) ;
@ e1
$display ( "%m received event e1" ) ;
end : WAITER_1
begin : EMITTER_1
$display ( "%m emitting event e1" ) ;
->> e1 ;
end : EMITTER_1
begin : EMITTER_2
$display ( "%m emitting event e2" ) ;
->> e2 ;
end : EMITTER_2
begin : WAITER_2
$display ( "%m waiting event e2" ) ;
@ e2
$display ( "%m received event e2" ) ;
end : WAITER_2
join_none
#10ns;
disable fork;
output:
event_m.WAITER_1 waiting event e1
event_m.EMITTER_1 emitting event e1
event_m.EMITTER_2 emitting event e2
event_m.WAITER_2 waiting event e2
event_m.WAITER_1 received event e1
event_m.WAITER_2 received event e2
3.
event e1, e2 ;
initial begin
fork
begin : WAITER_1
$display ( "%m waiting event e1" ) ;
wait(e1.triggered);
$display ( "%m received event e1" ) ;
end : WAITER_1
begin : EMITTER_1
$display ( "%m emitting event e1" ) ;
->> e1 ;
end : EMITTER_1
begin : EMITTER_2
$display ( "%m emitting event e2" ) ;
->> e2 ;
end : EMITTER_2
begin : WAITER_2
$display ( "%m waiting event e2" ) ;
wait(e2.triggered);
$display ( "%m received event e2" ) ;
end : WAITER_2
join_none
#10ns;
disable fork;
output:
event_m.WAITER_1 waiting event e1
event_m.EMITTER_1 emitting event e1
event_m.EMITTER_2 emitting event e2
event_m.WAITER_2 waiting event e2
event_m.WAITER_1 received event e1
event_m.WAITER_2 received event e2
Thanks!!!
1 Answer 1
A fork
construct executes all included statements in an indeterminate order. Each tool might pick the order they appear in the source, or the reverse. But you cannot depend on the ordering.
Using events in SystemVerilog requires careful knowledge of simulation execution semantics. I usually recommend avoiding them if possible.
An event trigger ->e
is an instantaneous event. The event control @e
has to execute and block the current process before the trigger occurs in another process, and the block process resumes. Many times there are race conditions between the two processes and the trigger executes before the event control, and the event is missed, and the event control has to wait for another trigger.
The wait(e.triggered)
statement says wait for the condition that event e has been triggered in the current time slot. Now you no longer have to worry which came first, the trigger or the wait statement. But you still have to execute the wait statement in the current time slot to catch the event.
Better options include using a mailbox or semaphore, as well as other options, depending on what you are trying to accomplish.
Your case 1) shows just one of many possible results—it is race condition.
Case 2) avoids the race by scheduling the event triggers in the non-blocking assignment region, so all the triggers are guaranteed to happen after execute the event control.
Case 3) also avoids the race condition because your triggers al happen in the same time slot as the wait
statement.