New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Emit initial block and remove dummy_signal #127
Conversation
It used to be done like that - except with non-blocking assignment - but was changed in c0fb0ef Is the blocking assignment better? |
Yes, non-blocking isn't accepted by verilator - that is why it doesn't compile. Why was it changed? |
This [0] is @cliffordwolf 's solution to this problem. I think it does the same as dummy signal, but I'm not a verilog expert... |
Added some more commits:
|
migen/genlib/fsm.py
Outdated
@@ -208,7 +209,9 @@ def after_leaving(self, state): | |||
|
|||
def do_finalize(self): | |||
nstates = len(self.actions) | |||
self.encoding = dict((s, n) for n, s in enumerate(self.actions.keys())) | |||
nbits = math.ceil(math.log2(nstates)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nstates.bit_length()
?
Looks similar to me: the
They always have an explicit width in the Verilog output. The problem is actually that the width could be different for different states.
There were some intractable problems (I cannot remember what it was exactly) with every simulator other than Verilator when using blocking assignments. Using non-blocking ones often works, but sometimes some simulators run comb blocks in infinite loops - and I do not know if this is a bug in the simulator, or a problem with the Migen approach. This kind of EDA snafu is the main reason for the integrated Migen Python simulator, where the behavior is better determined and such stupid issues avoided.
This is fine (assuming commonly used EDA tools do not choke on it), but it would be easier to merge if you made a separate PR for such changes. |
IIRC @nakengelhardt tried Verilator and blocking assignments and there were cases where it would refuse to compile due to combinatorial loops (that could be broken but are not). |
Can you give an example of where combinatorial loops are generated so that I can take a look? always @ (a or b)
a = a + b;
It won't detect domain crossing issues, but if all clock signals are added as ios and they are driven correctly in the verilator bench it should work, although I haven't tried it yet. |
Obviously not; the combinatorial loop cannot be broken here and this won't even synthesize to something sensible. But there are cases that are useful, where loops are "fake", and which synthesize just fine but break the simulator. |
The fake combinatorial loops come from blocks that assign multiple values. If a first block is e.g.
and the second is
then despite there not being any real combinatorial loop (since a is always assigned a fixed value), executing the first block marks 'a' as changed, so the second block is run, which marks 'c' changed, so the first block is run, etc... For this simple case some optimizing simulators manage to realize the problem and make it go away, but for a big design you're almost guaranteed to run into some problem of this type. It makes migen code impossible to run with any simulator I've tried, including icarus verilog, xsim, modelsim, and verilator. (Although verilator will give compile-time errors rather than infinite loops.) Changing to blocking assignments doesn't interact well with the way If() is implemented either. When migen exports e.g. If(a, b.eq(c)), it includes first a reset statement, to avoid latches when no else is provided:
There can be whole groups of those in blocks with multiple assignments. Crucially, all the resets happen at the beginning of the block. With blocking assignments, these resets sometimes end up overwriting the values that should be assigned, and spurious reset values get propagated through the design. |
Right, here is an example:
With non-blocking assignments, this is equivalent to |
Actually, we should probably drop |
Is there a way to fix this these cases or are we stuck forever with the python simulator? |
Yes, transform the code as I explained above; but it's not simple. It is definitely doable though, as synthesizers do it internally.
|
I see: // blocking
/* Generated by Yosys 0.7+631 (git sha1 93efbd5d, clang 6.0.1 -fPIC -Os) */
(* src = "if_reset.v:3" *)
module top(clk, a, b, c);
(* src = "if_reset.v:25" *)
wire _0_;
(* init = 1'h1 *)
(* src = "if_reset.v:3" *)
output a;
reg a = 1'h1;
(* src = "if_reset.v:3" *)
output b;
(* src = "if_reset.v:3" *)
output c;
(* src = "if_reset.v:3" *)
input clk;
assign _0_ = ~ (* src = "if_reset.v:26" *) a;
always @(posedge clk)
a <= _0_;
assign b = a ? (* src = "if_reset.v:15" *) 1'h1 : 1'h0;
assign c = 1'h0;
endmodule // non-blocking
/* Generated by Yosys 0.7+631 (git sha1 93efbd5d, clang 6.0.1 -fPIC -Os) */
(* src = "if_reset.v:3" *)
module top(clk, a, b, c);
(* src = "if_reset.v:25" *)
wire _0_;
(* init = 1'h1 *)
(* src = "if_reset.v:3" *)
output a;
reg a = 1'h1;
(* src = "if_reset.v:3" *)
output b;
(* src = "if_reset.v:3" *)
output c;
(* src = "if_reset.v:3" *)
input clk;
assign _0_ = ~ (* src = "if_reset.v:26" *) a;
always @(posedge clk)
a <= _0_;
assign b = a ? (* src = "if_reset.v:15" *) 1'h1 : 1'h0;
assign c = b ? (* src = "if_reset.v:14" *) 1'h1 : 1'h0;
endmodule I'll have a look at how yosys does it. Output regs should be initialized. So what is the status of this PR? Or do you prefer to revert commit c0fb0ef? |
There are (too) many things in this PR, what are you asking about exactly? Register initialization (sync ones) is independent from blocking/non-blocking assignments in comb blocks. |
@sbourdeauducq I dropped the other commits, I forgot to mention. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would also like to solve the incompatibilities with generated code/tools (if possible in a generic way).
I'm using hacks on my side to be able to simulate the generated code: https://github.com/enjoy-digital/litex/blob/master/litex/gen/fhdl/verilog.py:
- split async_syntax to have better control on the generated code.
- added a (dirty) patch to prevent the the combinatorial loop (generate the code with regular_comb=False)
We can try to solves these issues one by one.
For the initial change, if working on hardware and simulation, i'm ok with it.
How does it prevent the combinatorial loops? |
I'm not the author (davidcarne) but it's doing:
|
But isn't it generating each statement many times? |
Yes, here is the same design with and without it: |
Currently in fpga mode regs are initialized as following: reg a = 1'b1; This isn't recognized by symbiyosys as initializing the regs. Also it does not initialize output reg when the ClockDomain is reset_less. New way of initializing regs and output regs: initial begin a = 1'b1; end
Currently in fpga mode regs are initialized as following:
This isn't recognized by symbiyosys as initializing the regs. Also it does not
initialize output reg when the ClockDomain is reset_less.
New way of initializing regs and output regs:
When initialized correctly this dummy_signal should not be required. Also
verilator refuses to compile.