How does forever get scheduled in SV event region?

I have 3 questions based on this small piece of code:

  1. Where does forever @event sit in event region in SV? Before NBA or after NBA?
  2. Why the $monitor in interface never get print?
  3. Why the last posedge a is not detected by $monitor and forever?
interface bus();
   logic a;
   initial $monitor($time,,a);
   initial forever @(posedge a) $display("IF",, $time,, "Detect a posedge");
endinterface // bus

module tb;
   bus busA();
   initial begin
      busA.a <= 0;
      #1 busA.a <= 1;
      #1 busA.a <= 0;
      #1 busA.a <= 1;
      #10 busA.a <= 0;
      #20 busA.a <= 1;
      #0 $finish;
   end
   initial $monitor("TB",, $time,, busA.a);
endmodule

The output is like this:
TB 0 0
IF 1 Detect a posedge
TB 1 1
TB 2 0
IF 3 Detect a posedge
TB 3 1
TB 13 0
Simulation complete via $finish(1) at time 33 NS + 1

  1. Beforeā€¦ and after. There is only one active region where statements in a module/interface get executed. All initial blocks begin executing in the active region until hitting a blocking statement. Then they become suspended until the blocking condition is satisfied, and resume execution in the active region. The execution of statements will put more activity in the active region, or schedule activity in a later event region. An NBA statement schedules a updates to variables in the NBA region. A #0 will block the current thread by putting it into the inactive region.
    When there are no more statements/events to process in the active region, the events in the inactive region are moved to the active region, we go back to executing active events. The simulator keeps repeating this until there are no more active and inactive events. Then the updates in the NBA region get moved to the active region, and then loop back to executing active events. Finally, when there are no active/inactive/NBA events, the simulator is ready to advance time to the next active time slot. (there are a few more defined regions, but for the most part, the scheduling semantics are as I have described).
  2. You are only allowed 1 active $monitor at a time. Each $monitor statement executed replaces the previous, and you have a race condition as to which $monitor wins. $monitor was intended to be a quick debugging aid before Verilog waveforms and command line interfaces were invented.
  3. The reason the last edge of a is not detected is because you called $finish before the last NBA update to a has a chance to execute. After executing the last NBA statement to a, there is a #0. That suspends the current process and schedules it to resume in the inactive region. Now you have one inactive event (resume from #0), and one NBA event updated to a scheduled. Then there are no active events. So the inactive event becomes active, and the process resumes executing the $finish, leaving the remaining NBA event in its region. Note that the process waiting on @(posedge a) is not scheduled in any region. Only an update to a will cause it to become actively scheduled.

In reply to dave_59:

Hello dave_59,

I am new to SV and have a side question based on the example here. From this youtube video, I learned that all Non-blocking statements in initial block run in parallel.

but based on the output of the example from mlsxdx, looks like the non-blocking statements here are running sequentially. Could you please tell what is the right behavior here?

In reply to jsaluja:

That video is incorrect saying non-blocking assignment (NBA) statements run in parallel. All statements within a begin/end block run sequentially. I think they are referring to how synthesis tools create logic for these statements, but that does not apply to this code.

And for the code above, there is no difference between using non-blocking and blocking assignments.

In reply to dave_59:

You are right. I tried the above code with blocking and non-blocking statement. And I got the same output. Thanks for your reply