1

Why am I getting the result shown below? I would expect that multiple trigger to the update_ev event should cause the display "Main Loop ,.." to be executed twice. But it is only executed once.

program multiple_trigger();
  initial begin
     event update_ev;
     bit orResult, prev_orResult, A, B;

     // Multiple trigg logic
     fork
        begin : threadDisplay
           forever begin
              prev_orResult = orResult;
              // Update status
              @(update_ev);
              // Compute A OR B
              orResult = A | B;
              $display("\n Main Loop , %0t  A=%0b, B=%0b orResult=%0b",$time(), A, B, orResult);

              if (prev_orResult != orResult) begin
                 $display("\n In the IF condition, %0t  A=%0b, B=%0b orResult=%0b",$time(), A, B, orResult);
              end
           end // forever
        end : threadDisplay

        // 10 A=0
        begin : threadA
           #10;
           A = 1'b0;
           ->update_ev;
        end : threadA

        // 10 B=1'b1
        begin : threadB
           #10;
           B = 1'b1;
           ->update_ev;
        end : threadB
     join_none
     #100;
  end      
endprogram

// Actual Result----------------------------------------
     Main Loop , 10  A=0, B=1 orResult=1
     In the IF condition , 10  A=0, B=1 orResult=1
//-----------------------------------------------------

// Expected Result----------------------------------------
    Main Loop , 10  A=0, B=0 orResult=0
    Main Loop , 10  A=0, B=1 orResult=1
    In the IF condition , 10  A=0, B=1 orResult=1
// -------------------------------------------------------
Cœur
  • 37,241
  • 25
  • 195
  • 267

3 Answers3

0

Both actual and expected outcomes are possible because it is a race condition. There is nothing to prevent the second ->update_ev from triggering before the @(update_ev) responds.

I usually recommend people avoid SV events and in this case, a function call would serve the desired purpose.

dave_59
  • 39,096
  • 3
  • 24
  • 63
  • Hi Dave, If I do want the outcome to always behave as the actual, can I use the ->> operator? – Kanishk Sugand Jul 22 '15 at 16:49
  • Yes. But consider other mechanisms like a semaphore or mailbox to communicate between threads. Have the listing thread use a fork join_any to wait on the other processes. – dave_59 Jul 25 '15 at 15:00
0

The event trigger -> can appear to be stack depending on the scheduling order. The LRM states the scheduler may evaluate events in the same region in an indeterminate order. Every simulator I have used don't appear to randomized the event order; they tend to give priority to compiling order.

Lets give label names to the threads in fork. Then walk through a likely scenario.

  • disp_thread will be the process with forever
  • thread_A will be the process with A = 1'b0;
  • thread_B will be the process with B = 1'b1;

The simulator executes disp_thread first and stops when it reaches @(update_ev);. Then thread_A will kicks off and immediately sleep (will walk up in #10). thread_B stats up and also sleeps for #10. With no operations scheduled, the simulate will incorrect the timestep. thread_A and thread_B wake up, and thread_A executes first and triggers ->update_ev and finishes. disp_thread is now unblocked, the simulator has a choice: continue disp_thread or thread_B. Often, it will pick to continue the disp_thread and start its second loop (because the forever is in the same process thread) and pause again at @(update_ev);. thread_B now get a change to run and it triggers ->update_ev and finishes. Once again disp_thread is unblocked so it can run again.

If you moved the event triggers above the event wait statement you might see the $display message issued only once. However this is a brittle solution and not recommenced.

Instead use a nonblocking event trigger (->>). This will move the event trigger NBA region of the scheduler. Allowing both triggering threads to run and schedule updates before unblocking the disp_thread. With this change the schedule will be as follows:

The simulator executes disp_thread first and stops when it reaches @(update_ev);. Then thread_A will kicks off and immediately sleep (will walk up in #10). thread_B stats up and also sleeps for #10. With no operations scheduled, the simulate will incorrect the timestep. thread_A and thread_B wake up, and thread_A executes first and schedules ->>update_ev for the NBA and finishes. At this point the scheduler is still in the Active region; disp_thread is still blocked. thread_B (the only thing that can run currently run) will run and schedule ->>update_ev for the NBA region and finishes. With noting that can run in the current region, the scheduler continues to the next non-empty region; NBA. the event trigger to update_ev both happen. The scheduler re-enters the active region with disp_thread now unblocked.

Note: If you plan on use this, keep it simple and make yourself very knowledgeable with the scheduling semantics (IEEE std 1800-2012 § 4). Predictability will become brittle with added complexity (such as unblocked blocking events triggering new events). When predictability breaks, it may happen randomly form different simulators, version, machines, OS, system time, system load, etc.

Greg
  • 18,111
  • 5
  • 46
  • 68
  • HI Greg, Thanks for your reply. I am intending to use this in my verification environment. It is suppose to solve the purpose of multiple processes (thread_A, thread_B ....) trying to inform one hearing process (disp_thread) about some change in the shared variable. The hearing process should then decide, irrelevant of the order in which the threads executed, that the final result is the same or different than the previous. Do you think ->> should solve the purpose? – Kanishk Sugand Jul 23 '15 at 09:21
  • `->>` should work in that case. Another option is to put a little delay after `@(update_ev)`; assuming if a little delay is acceptable. – Greg Jul 23 '15 at 18:46
  • I would never recommend putting a little delay in a testbench. You can wind up with things becoming unmanageable once you start putting delays all over the place just to define ordering. – dave_59 Jul 25 '15 at 14:57
0
program multiple_trigger();
  initial begin
     event update_ev1, update_ev2;
     bit orResult, prev_orResult, A, B;

     // Multiple trigg logic
     fork
        begin : threadDisplay
           forever begin
              prev_orResult = orResult;
              // Update status
             @(update_ev1, update_ev2);
              // Compute A OR B
              orResult = A | B;
              $display("\n Main Loop , %0t  A=%0b, B=%0b orResult=%0b",$time(), A, B, orResult);

              if (prev_orResult != orResult) begin
                 $display("\n In the IF condition, %0t  A=%0b, B=%0b orResult=%0b",$time(), A, B, orResult);
              end
           end // forever
        end : threadDisplay

        // 10 A=0
        begin : threadA
           #10;
           A = 1'b0;
           ->>update_ev1;
        end : threadA

        // 10 B=1'b1
        begin : threadB
           #10;
           wait(update_ev1.triggered);
           B = 1'b1;        
           ->>update_ev2;
        end : threadB
     join_none
     #100;
  end      
endprogram
adiles
  • 55
  • 1
  • 7