0
\$\begingroup\$

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!!!

\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

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.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.