I am trying to implement a simple event based Verilog simulator in Python, but I actually struggle to find some details in the specification (section 11 of IEEE 1364-2005).
Let's say I just perfomed an update event on clk
which now obtained the new value 1 (0 before). According to the specification this requires me to schedule 'evaluation events' for sensitive processes.
Do I have to schedule the evaluation of an always @(posedge clk)
block as active or inactive event? I'm guessing that the latter is correct?
Or actually, to speak more general. Are basically all events scheduled as inactive events with the following exceptions:
- Non blocking assignment updates as non-blocking assignment events
- continuous assignments as active events
Thanks a lot!
By default everything runs in the Active region. The exceptions being:
- #0 blocking assignments are in the Inactive region
- Non-blocking assignments are in the NBA region
- $monitor, $strobe, and PLI/VPI are in the Monitor region
We can prove that @
happens in the Active region, by running the following in any existing simulator:
int x; initial begin $monitor("From $monitor: x is %0d",x); #0 x = 5; // inactive event x = 3; // active event (after inactive event) #1; // go to next time stamp fork #0 x = 7; // inactive event x = 11; // active event join #0 fork // inactive region #0 x = 2; // inactive event x = 5; // active event x <= 4; // NBA event join end // active event region always @* $display("From @* $display: x is %0d",x);
Outputs:
From @* $display: x is 3 From $monitor: x is 3 From @* $display: x is 11 From @* $display: x is 7 From @* $display: x is 5 From @* $display: x is 2 From @* $display: x is 4 From $monitor: x is 4
Display reports more times then monitor. This rules out @
accruing in the Monitor or Future region. Display is is reporting on every event region. Each event region can only loop back to the Active region. Therefor, the @
must be handled in the Active region and each region that updates the variable x
is triggering a new Active region event.
This can also be proved by knowing looking at the history of Verilog. Unfortunately, this is not well documented. I leaned learned about it through people that were using/developing verilog in the late '80s early '90s. The over all explanation is: The Inactive and NBA regions were added to Verilog before IEEE Std 1364-1995, @
predates these two region. The regions were added to add determinism to a non-deterministic simulator.
always @(posedge clk) pipe0 = in; always @(posedge clk) pipe1 = pipe0; // unpredictable, non-deterministic order
always @(posedge clk) #0 pipe0 = in; // Inactive region added some determinism always @(posedge clk) pipe1 = pipe0;
always @(posedge clk) #0 pipe0 = in; // But was fragile always @(posedge clk) #0 pipe1 = pipe0; // unpredictable order again always @(posedge clk) pipe2 = pipe1;
always @(posedge clk) pipe0 <= in; always @(posedge clk) pipe1 <= pipe0; always @(posedge clk) pipe2 <= pipe1; // NBA region fixed it ... always @(posedge clk) pipeN <= pipeM; // and made it scalable
clarification based on feedback from comments
Events are
activated, not
moved. Activated NBA events enter the true condition of
if (E is an update event)
, modified object and schedule new
evaluation events (handled the next time
Active region entered). Once all the
activated events are completed, the schedule goes back to the top of the while-loop. The
NBA region only assigns values, the evaluation was actually done in an earlier
Active region stage.
From your example:
module TEST; reg test = 0; reg test2 = 0; reg clk = 0; initial begin clk <= 1; test <= 1; end always @(posedge clk) begin test2 <= test; end endmodule
Each iteration of the while loop would look something like this:
Iteration:0 Active: <----- This is region is executing clk$tmp = eval(1) test$tmp = eval(1) Inactive: NBA: clk = clk$tmp test = test$tmp Iteration:1 Active: Inactive: NBA: <----- This is region is executing clk = clk$tmp test = test$tmp Active.schedule( eval( "@(posedge clk)" ) Iteration:2 Active: <----- This is region is executing eval( "@(posedge clk)" ) Active.schedule( "test2$tmp = eval(test)" ) NBA.schedule( "test2 = test2$tmp" ) Inactive: NBA: Iteration:3 Active: <----- This is region is executing test2$tmp = eval(test) Inactive: NBA: test2 = test2$tmp Iteration:4 Active: Inactive: NBA: <----- This is region is executing test2 = test2$tmp Iteration:5 --> next simulation cycle