3

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!

Alex
  • 409
  • 4
  • 12
  • 2
    Sort've an aside, but the following blog post and its predecessors make for interesting reading about scheduling in verilog and VHDL... http://www.sigasi.com/content/vhdls-crown-jewel – Marty Apr 02 '14 at 15:47
  • 1
    Out of curiosity what is the goal of your project? If you're trying to use Python for FPGA development I assume you've seen [myHDL](http://www.myhdl.org/) and [cocotb](http://cocotb.com)? – Chiggs Apr 03 '14 at 08:33
  • @Chiggs MyHDL and I actually have a tough relation ;-) So I'm trying to replicate it but with a different approach (less magic and more object oriented to the user) – Alex Apr 03 '14 at 14:55
  • 1
    @Alex So you are trying to simplify MyHDL and therefore looking at Verilog scheduling. Interesting :-) – Jan Decaluwe Apr 05 '14 at 07:56

1 Answers1

3

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
Greg
  • 18,111
  • 5
  • 46
  • 68
  • Thanks a lot for your answer! But I'm not completely convinced and struggling with [this example](http://pastebin.com/SbCsfxxB). If events are handled the way you described it, the outcome of test2 should be non-predictable? – Alex Apr 03 '14 at 13:10
  • The example is deterministic. The NBA region _assigns_ all scheduled non-blocking values before returning back to the Active region. The `@(posedge clk)` blocks `test2 <= test` from being queued in the scheduler. `#`, `@`, and `wait` postpone evaluation. `<=` is evaluated in the Active region, but its assignment is post postponed to the NBA region. Example `x=1;y=3;z<=x+y;x=7` z will be assigned 4. – Greg Apr 03 '14 at 16:59
  • Note: if you add to the example `always @(posedge clk) test = ~test;`, then the result of `test2` becomes (or allowed to be) non-deterministic again. `test` itself is predictable. – Greg Apr 03 '14 at 17:04
  • could you maybe have a look at [this link](http://pastebin.com/3TgTaWvR) where I wrote down how I understood the simulator executes its events? Because in the specification they say at the example algorithm (section 11.4) that NBA events are actually _moved_ to the active region as soon as there are no other events left. – Alex Apr 03 '14 at 22:10
  • 1
    @Alex, I have extended my answer explaining your test case. – Greg Apr 04 '14 at 23:49
  • 1
    The original example is *not* deterministic. See the code on EDA playground [here](http://www.edaplayground.com/x/25q) and note how GPLCver behaves differently from other simulators. After the NBA to clk occurs, Verilog may or may not trigger the clocked always block immediately. – Jan Decaluwe Apr 05 '14 at 07:52
  • @JanDecaluwe, It _could_ be a bug in GPLCver. Every other simulator says test2=1, including top simulators from Cadence/Mentor/Synopsys. I look briefly at the CPLCver source, but I couldn't tell if it is using prioritized single-process scheduling or multi-process region scheduling. The _assignment_ of a NBA should happen in parallel, or be considered one operation. Otherwise you get the same race condition problem as `#0`. – Greg Apr 09 '14 at 19:36
  • @Greg It's not a bug, but normal Verilog: arbitrary execution order in zero time (Thomas & Moorby's 8.3). See also [my blog post](http://www.sigasi.com/content/verilogs-major-flaw). It does not matter how many "top simulators" give the same answer: you cannot prove the absence of nondeterminism by simulation. A single simulator with a different answer proves its presence though, as do thought experiments based on Verilog scheduling basics. – Jan Decaluwe Apr 09 '14 at 20:51
  • @Greg, thanks for your clarification! But as I interpret the specification, I still don't agree. You move a 'this region is executing pointer', but the spec says `activate all *** events` and afterwards `E = any active event`. So the events have to be moved to the active region otherwise E would stay empty? @JanDecaluwe, great to see here and thank you for the EDA playground hint. Of course it _might_ be a GPLCver bug, but it probably isn't? However, my intuition agrees with most of the simulators, my brain doesn't ;-) . What would be your answer to my original question? – Alex Apr 12 '14 at 18:43
  • 1
    Your original question: As @Greg said, the @ event would be scheduled in the active region. However, you cannot make any assumptions about the evaluation order of that event and other existing active events. That explains why your example in this comment section is nondeterministic. – Jan Decaluwe Apr 12 '14 at 20:08