Running Icarus Verilog version 0.9.6 on Linux. I have a test case with 2 Verilog modules - 'tb.v' and 'delay.v' and this test case is producing incorrect VCD waveforms. See comment on lines 22 and 23 on source file 'tb.v'. Execute only one of these lines in 'tb.v' and inspect VCD waves ( I am using GTKwave) on signal 'y1'. The #10 delay in line 22 of 'tb.v', seems to break the waveform for signal 'y1'. #15 or larger delay values produce correct waveforms (or meets my expectation of a correct waveform).
The problem is a race in your code. Simplifying it, you have
The initial block sets in=0, goes to sleep for 10 time units, then sets in=1. The always block is triggered by in=0, schedules the assignment to y1, goes to sleep for 10 time units, schedules the assignment to y2, then goes back to waiting for a trigger.
At time 10, the simulator has a choice of which action it executes first. Icarus chooses the in=1 action, thus that event occurs before the always block is waiting for it.
There's another potential race in this code, and you should really write
to ensure the always block is waiting for an event before the first trigger occurs.
Martin
Sorry, but I'm not understanding your explanation.
The problem is with operation on y1. I see no problem with y2 - it
operates as expected. It is true that there are simultaneous
events at time unit 10 - and this could possibly affect the y2 output - but
I'm wondering why does this affect y1? If I delay the high assertion
of 'in' to 15 time units - both y1 and y2 look correct.
The following is my understanding of how 'y1' should function (and I see
found no difference using "#0 in = 0")
1) At time unit 0, 'in' is assigned a value of 0. This is verified
correct on the waveform.
2) This triggers the always block. The new value of 'in' is sampled at
the current time unit (0) and after 15 time units
y1 is updated to the sampled value of 'in' (or the 0 value). This is
verified correct on the waveform. I can see that 'y1' transitions from
'x' to a 0. So, I don't see any issues around the 0 time value - as you
mention. I believe you were implying that by adding
the #0 in = 0 statement this would enforce a fixed sequence of execution?
Perhaps, but I don't see a problem here with 'y1'.
2) At time unit 10, 'in' is assigned a value of 1. This is verified
correct on the waveform.
2) This triggers the always block. My expectation is the new value of
'in' is sampled and found to be 1, at the current time unit (i.e. 10).
And after 15 time units, y1 is driven to the sampled value of 'in' (or the
1 value). This would agree with your statement that Icarus
chooses the in=1 action before execution of the always block. This doesn't
look to be the situation when viewed in the waveform - y1 is still at the 0
value.
It appears that the old value of 'in' (0) is being used and has not been
updated to a 1 value. I would assume the always block was triggered, but
was the old
value of 'in' was used? And later in the simulation, the operation of 'y1'
looks correct.
Thank you for your detailed response.
Dave
On Mon, Jun 8, 2015 at 2:11 AM, Martin Whitaker <martinwhitaker@users.sf.net
Related
Bugs:
#981Ignore the #0 for the moment - that's a separate issue. Perhaps it will be clearer if you consider the alternative (but functionally equivalent) way of writing your 'always' block
So if 'in' changes at time 0, the assignment to 'y1' is executed at time 0 (with the write delayed to time 15) and the assignment to 'y2' is executed at time 10 (with the write delayed to the end of time 10). But your next assignment to 'in' is also executed at time 10. The simulator is single-threaded, and has a choice of which statement to execute first. If it chooses to execute the assignment to 'in' first (as Icarus is doing), the other block won't have got back to the '@(in)' statement, so will miss the change.
The IEEE standard allows a simulator to choose either order, so you might find your example worked in another simulator.
You could fix your code for this particular case by changing the assignments to 'in' to be non-blocking. But if the time between changes to 'in' is less than 10ns, it will still break.
Back to the #0. There's a similar problem at time 0, because there is no required order for 'initial' and 'always' statements to be executed at time 0. So if a simulator chose to execute your 'initial' block first, the 'always' block would miss the first change to in. In fact Icarus protects you from this by giving priority to 'always' blocks. Other simulators don't necessarily do this, so it's good practice to add the #0 to guarantee execution order.
Martin
See my comments below.
Thanks,
Dave
On Mon, Jun 8, 2015 at 12:52 PM, Martin Whitaker martinwhitaker@users.sf.net wrote:
I have a lot of respect for the Verilog guys at Sunburst Design - I was
lucky enough to attend one of their 3 day training sessions around 2002.
And as someone who has used Icarus Verilog for over 10 years and has a few
shipping commercial designs using Icarus Verilog - I have a lot of respect
for you guys at Icarus also.
See attachment for recommendations on using #0 delays.
Related
Bugs:
#981I'm not sure how to explain this more clearly. Taking my simplified example
the sequence of steps Icarus performs is
time 0:
thread 1 : wait for in
thread 2 : assign in=0
update in=0
thread 2 : suspend until time 10
thread 1 : evaluate RHS of y1 assignment, schedule y1=0 @ time 15
thread 1 : suspend until time 10
time 10:
thread 2 : assign in=1
update in=1
thread 2 : finished
thread 1 : evaluate RHS of y2 assignment, schedule y2=1 @ end of time 10
thread 1 : wait for in
update y2=1
time 15:
update y1=0
(where thread 1 is the always block, thread 2 is the initial block).
Thread 1 doesn't get triggered again because 'in' was updated before it started waiting on it.
What part of this sequence do you think violates the standard?
P.S. Please can you trim off any unnecessary text in your replies - it makes the bug discussion on SourceForge hard to read.
Since most Verilog simulators are single threaded only one block is usually execute at a time. Note that SystemVerilog allows some of these race conditions at time zero to be fixed by defining the order that certain blocks are executed, but Icarus does not currently have this implemented.
Here is my interpretation of the races in this example code:
There is a time zero race between the initial block executing the in = 0; and the sensitizing of the always block to "in" changing. If the initial block runs first then "in" is assigned a value of zero and then it waits at the #10 statement. The always would run next and would wait at the @(in) statement for "in" to change to one at time 10.
If they are run in the opposite order then the always would be run and the @(in) would be waiting when the initial block ran the in = 0; so that would trigger the body of the always to run. This order creates the second race case. y1 would get assign the current value of "in" at time 15, but at time 10 you again have a choice of which statement runs first. Is it the assignment to "in" of one in the initial block or the non-blocking assignment of y2 to "in"?
Again depending on the order you will get different results. If the initial block assignment runs first then the value of "in" will be updated and then the non-blocking assignment will run using the new value of "in" (one). since we were not waiting at the @(in) statement the always block will only be run once. If the always block run first then the non-blocking assignment will trigger assigning the original value of "in" to y2 (zero) and it will then start waiting at the @(in) statement. Now when the initial block runs the assignment the always block will be executed a second time.
As a note: normally blocking delays should not be used in an always block because the @() will not respond while waiting for the blocking delay to finish. This is a great source of confusion for most people. Also, to avoid missing changes at time zero because an @() statement may not be activated initial values should be delayed using a blocking or non blocking delay. Even a #0 delay like Martin suggested is enough.
Note that I only looked at the reduced code not the original code. This reduced/example code certainly does have timing races and can generate unexpected result that may not match your thinking. I will also note that the order a block is executed can even change from activation to activation (e.g. two always block may run in a different order even when they both depend on the same activation signal).
I see this whole discussion bouncing back and forth between what is
currently implemented in Icarus and what is the correct operation of a
Verilog simulator.
I believe the correct operation was outlined in Figure 5 on page 6 of the
paper I sent to Martin earlier today.
I'd like to focus on what is correct operation - I am not considering
threads or how Icarus is currently implemented. Call this the ideal case.
There are 2 blocks - the initial block and the always block. The initial
blocks assigns values to 'in' at different times - that is all it does.
When a simulator assigns a new value to 'in' during
the time between a time step - doesn't matter. The always block ONLY
executes its 2 statements ONLY IF there is a change on 'in'. That is how I
consider an always block 'should' work. Therefore, I don't understand how
the always block can execute the 2 statements (y1 <= #15 in, etc) before
the initial block assigns a new value to 'in' (or before the initial block
creates a change to a value in the sensitivity list of an always block).
If we only consider these 2 blocks - the execution order should be fixed by
the Verilog code. And I don't understand how the order of execution of the
initial and always blocks can be random - for this ideal case. Of course,
the executing code in Icarus could skip executing the statements in the
always block, if it has determined there has been no change on 'in' and
then later, during the same time step - 'in' gets updated. I can
understand how this could happen, BUT, I would not call this correct
operation.
Can we agree that this is the ideal case? This is how things should work -
there would be no 'race' conditions?
Dave
On Mon, Jun 8, 2015 at 6:49 PM, Cary R. caryr@users.sf.net wrote:
Related
Bugs:
#981I don't see the attachment you are referring to. Giving the name of the paper may be enough. The only attachment I see is the original test code. The point Martin and I are both trying to make is that Icarus is working correctly. I was describing what the standard allows. My only comment regarding the new SystemVerilog functionality was to note that it was not currently implemented.
I believe I understand what you are missing so maybe this will help.
We are looking at two control constructs: always and initial. The always construct will execute its statement continually which is why you need to have something that blocks time in the statement it executes to prevent an infinite loop. The @(in) is the statement that is being executed by the always construct. An initial construct only executes its statement once and then terminates. The standard does not define the order the constructs should execute so the always constructs do not have to execute before the initial constructs.
From 1364-2005:
This is what gives the race between the initial construct executing the first statement in the block attached to it, in = 0; and the always construct executing its only statement @(in) which then wait for a change in the "in" signal before running the statement attached to it. Depending on which executes first you will get different results (i.e. the @(in) statement will run its statement at time zero if the always construct runs first, otherwise it will not run until time 10).
Title is "Verilog Nonblocking Assignments with Delays, Myths & Mysteries"
Clifford Cummings, Sunburst Design, SNUG Boston 2002.
See pages 5 and 6.
On Mon, Jun 8, 2015 at 9:59 PM, Cary R. caryr@users.sf.net wrote:
Related
Bugs:
#981I looked at pages 5 and 6 of the paper. They just describe the stratified event queue that Verilog uses (specified in Chapter 11 of 1364-2005). I do not see an example or how anything Martin and I have written is inconsistent with what the standard or this paper specifies regarding the event queue. So using the simple example Martin gave, how exactly do you see a hypothetical reference simulator executing the statements? Answering that should help us resolve this issue.
On Tue, Jun 9, 2015 at 12:58 AM, Cary R. caryr@users.sf.net wrote:
Anyway, at the top of page 6, there is this wording "This blocking
assignment or this continuous assignment can trigger additional events in
the same time step". When the initial block executes the blocking
statement (#10 in = 1;) - anytime during the 10 time step - why wouldn't
this trigger the always block to be executed?
Related
Bugs:
#981"why wouldn't this trigger the always block to be executed?" Read my follow up post for the answer to that question. I believe you have a common misunderstanding regarding how "always @(in) ..." works.
Cary, yes, there could be a miss understanding on the operation of an
always @(..) block.
Here is a cut from Doulos (www.doulos.com) and my understanding agrees with
their explanation below.
always @(sensitivity-list)
begin
// statements
end
The sensitivity list consists of one or more signals. When at least one of
these signals changes, the always block executes through to the end keyword
as before. Except that now, the sensitivity list prevents the always
block from executing again until another change occurs on a signal in the
sensitivity list.
Based on this explanation - I'm struggling to understand how the always
@(in) executes before a change on 'in'.
On Tue, Jun 9, 2015 at 12:02 PM, Cary R. caryr@users.sf.net wrote:
Related
Bugs:
#981Dave, please take the time to study and understand the example in my last comment. That is the key to understanding what is wrong with your code. Hint - if you haven't reached the end of the always block before the next trigger event occurs, you will miss that trigger event and have to wait for the next one.
OK, you and Cary are saying that the always block may or may not complete
execution by the time the 'in' signal is being updated. Is that correct?
But what I have been saying is you should only execute the always block if
and only if the 'in' signal has changed value. Therefore, the execution
sequence is
guaranteed to be correct. Why do I say this? Because the initial block
establishes new values for 'in' and this results in triggering the always
@(in) block to execute.
But, in a previous post, Cary indicated that I may not have a complete
understanding the operation of the always @(,,) statement, so I posted what
I consider
a common explanation of the always @(,,,) construct and now Cary is saying
that is not entirely correct? (I did see Cary's post of the difference
between "always' and
"always @(,,)"
Is the always block waiting 10 time steps to complete in your
implementation in Icarus? And during this time, another thread could be
changing the value of 'in'?
On Tue, Jun 9, 2015 at 3:58 PM, Martin Whitaker <martinwhitaker@users.sf.net
Related
Bugs:
#981Yes, that is exactly what we are saying.
Every simulator that is following the standard will wait ten time units before finishing the block attached to the @(...) statement. That is the point of a blocking delay and why we usually use non-blocking delays in conjunction with @(...) statements.
That is exactly the reason your example is giving results you do not expect. The block attached to the @(...) has not finished when "in" is changed.
This is correct though imprecise and the key part you are likely missing in their description is that the always block must executes through to the end before the sensitivity list comes into play again.
Remember always @(...) is not a single construct. You have an always construct that has its automatic repeat controlled by an @(..) statement.
Hopefully the following examples will make it more clear:
Maybe even slightly more obvious you could also write this as:
Using the first example we often omit the begin/end pair associated with the always construct since @(in) is a single statement it does not need to be inside a block statement. Removing the block statement gives the reduced example Martin originally posted. Every always and initial construct is run at time zero and the order is not deterministic. A delay or event control of some kind must be in every always construct statement or it will create a zero time infinite loop. I actually added checking code to the Icarus compiler to warn users about this because it is hard to debug Verilog when time does not advance.
I just read your replies again and I think this is the crux of the problem.
"I'm wondering why does this affect y1? If I delay the high assertion
of 'in' to 15 time units - both y1 and y2 look correct."
A delay of 11 would also work, but a delay of 10 causes a race and a delay less than 10 will break in the same way and it may be more obvious why.
Here is the race described again and it assumes that the "@(in)" fired at time zero. At time 10 you can execute the "in = 1;" or the "y2 <= in;" statements. Icarus executes the "in = 1;" statement which updates "in" and then executes the "y2 <= in;" statement. Once the non-blocking assignment RHS is evaluated the statement attached to the always has finishes so the always construct starts over and the "@(in)" becomes active again. This is after "in" has changed so the change is missed. The "always @(in)" does not continuously sit there waiting for events to happen. The process represented by the always construct has to be waiting at the "@(in)" statement for it to be sensitive to changes. This is the reason people suggest not using blocking delays in an always construct like this. It needs to execute atomically so it is ready to run whenever a signal in the sensitivity list changes.
I will also note that the time zero race is still there, but Icarus is doing what you expect so nothing is needed for it, though other simulators may generate a different result.
Dave, maybe this example will make it clear:
This example has no timing races and will produce the same output with any standard-conformant simulator:
If you change the #11 to #10, you introduce a race, and different simulators may give different results.
And changing the #11 to #9 will not miss a trigger.