Search code examples
verilogsystem-verilogsystem-verilog-assertionsiverilog

What exactly is "Current Simulation Time" and Event Queue in Verilog?


Consider the below example:

module test;
 reg a;
 initial begin
  a = 1'b0;
  a <= 1'b1;
  $display(a);
 end
endmodule

The above example displays 0. My reason is because the non-blocking assignment will be assigned at step 3 of the "Stratified Event Queue" while the blocking assignment and $display are done at step 1. If I modify the example as:

module test;
 reg a;
 initial begin
  a = 1'b0;
  a <= 1'b1;
  $display(a);
  $monitor(a);
 end
endmodule

Then 0 and 1 are printed because, I assume $monitor is executed at step 4 of the Event Queue(?). But if I modify the example further:

module test;
 reg a;
 initial begin
  a = 1'b0;
  a <= 1'b1;
  $monitor(a);
  $display(a);
 end
endmodule

Again the output is: 0 and 1 -- which I did not expect. I expected 1 and 1 to be printed because $monitor will be evaluated at step 4 of the Event Queue, by which time, "a" is already 1. After that we have $display, which should print 1.

References I could find talk about "current simulation time" and the "stratified event queue" but I'm not sure how it works.

I appreciate your explanation! Thanks


Solution

  • Verilog simulation is event driven. Event is a change of the value of a verilog variable (or a named event). Simulation is done in steps.

    A step starts with input events put on an event queue. Every new change in values because of the evaluation creates new events which are added to the queue. Simulation ends when the queue is empty (there are no more active events). Every such a step advances simulation time.

    The step itself is divided in multiple zones which are executed using algorithm defined in the standard.

    For verilog 2K, there are roughly 3 main zones:

    1. blocking assignment zone. Verilog executes all the procedural blocks scheduled by the event queue and reacts to new blocking assignment events. It just schedules nbas events to be executed later. When all blocking events are done, it gets to the next zone.

    2. non-blocking assignment zone. Here it executes all blocks which react to the schedules nba events. It will put both ba, and nba event on the queue. When all nba is done, it might get back to the zone '1' if there are ba event and do all over.

    3. monitor/strobe zone -- this is the zone when $monitor (and $strob) work. It gets execute after both ba and nba zones are done (no more events).

    In your case a = 1 is executed in the blocking assignment zone. this value is persistent till the end of this zone. $display will also be executed in this zone. So, it will see the value of the 'a == 0`.

    a <= 1 will be schedule for execution in the non-blocking zone, after $dislpay is done. $monitor will pick events in the monitor zone, after the non-blocking is done. So, it will show you the value of 1.

    Your statements are executed in the initial block. As a result, there is no event propagation. Events only are picked by always and assign statements. If you put your $display in an always block, you will see more interesting results. always @* $display(a);

    You should read about standard simulation semantics in verilog to get more information.