Skip to content
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

Should Migen generate explicit Verilog for mismatched bit vector lengths (rather then relying on implicit Verilog behaviour?) #102

Open
mithro opened this issue Feb 14, 2018 · 23 comments

Comments

@mithro
Copy link
Contributor

mithro commented Feb 14, 2018

@bunnie has been working with LiteX / Migen recently and mentioned he thought following issue would be a big source of bugs in LiteX / Migen designs. I would like to get @sbourdeauducq 's (and others) thoughts on this and if we want to fix it in some way?

Mismatches in bit vector lengths is kicked down the road to Verilog. So,

a = Signal(31)
b = Signal(16)
self.comb += a.eq(b)

Just generates the verilog "assign a = b".

It's best to confirm if the compiler does what you think it does in this case. There is a spec but there are extensions that can, for example, allow for signed numbers.

Confirmed that Vivado's convention is:

  • if lvalue is wider than rvalue, then LSB-align rvalue and zero-pad
  • if lvalue is narrower than rvalue, then lvalue gets the LSB's of rvalue

So

wire [15:0] a;
wire[7:0] b; 
assign a = b;

is implicitly

assign a[15:0] = {8'b0, b};

and

wire [7:0] a;
wire[15:0] b;
assign a = b;

is implicitly

assign a[7:0] = b[7:0];

My personal feeling is that Migen should be generating the explicit output it is after rather then relying on Verilog implementations to be sane....

Thoughts?

@jordens
Copy link
Member

jordens commented Feb 14, 2018

This is definitely not the only area where we are relying on Verilog implementations being sane. How do you judge between reasonable and dangerous expectation?

@bunnie
Copy link
Contributor

bunnie commented Feb 15, 2018 via email

@sbourdeauducq
Copy link
Member

Migen should be generating the explicit output it is after rather then relying on Verilog implementations to be sane....

FPGA tools have many problems, but this isn't one of them. This is a very common case which is even present with a <= a + 1'b1 and I haven't found any FPGA tool bug around there.

My feeling on this is it helps immensely with code readability.

I disagree, I think this clutters the Verilog output. Manually-written Verilog doesn't do that.

One of the problems of migen is that signal widths are largely implicit

How are they implicit? You mean they are not specified again at every statement?

and inter-block interfaces aren't clearly defined as you can just yank into any module by referencing an internal signal,

You can, but this doesn't mean you should. It is up to you to define clean interfaces between your modules.

and I find myself chasing down ratholes to try and figure out what the original width of a signal was.

print(len(signal)) or do more elaborate things with len(signal)? Define a custom class/type for addressing signals based on the number of bytes that one LSB indexes into, and add some code-generation-time sanity checks and fancy methods?

@sbourdeauducq
Copy link
Member

you can just yank into any module by referencing an internal signal

This feature is actually very useful e.g. in http://github.com/m-labs/microscope

@bunnie
Copy link
Contributor

bunnie commented Feb 15, 2018 via email

@bunnie
Copy link
Contributor

bunnie commented Feb 15, 2018 via email

@sbourdeauducq
Copy link
Member

I find that to be bad practice, but that's just me...

In that case, it should be addressed in Migen as well, maybe in a way similar to Rust's as u... construct (but still, we have to be careful that this doesn't introduce too much clutter, e.g. in arithmetic operations). This requires finding a good Python syntax hack :)

Also, {carry, value} <= value + 1'b1; should be supported.

This statement would make more sense if the output of migen wasn't a single 20,000 line verilog file. It's nice that for the blink demo the migen output looks almost hand-written but when debugging a 20k line verilog file I'll happily sacrifice looking human-written for being more human-readable.

I would actually like to change that and support splitting the output into multiple modules and Verilog files. But this is major work and I do not have time for it at the moment.

Is there a specific module from someone that you are trying to reuse and find the interface problematic?

@bunnie
Copy link
Contributor

bunnie commented Feb 15, 2018 via email

@sbourdeauducq
Copy link
Member

I have a problem right now on litevideo where enabling DMA hard crashes the CPU and thus I can't even get a scope trace out.

With microscope (and probably litescope with the proper options) you just need to get a second UART channel, a CPU is not needed. Also you can perhaps try to simulate the litevideo design.

FWIW, if the DMA core is connected to the SDRAM bus directly, bypassing L2 (is that the LASMI system, that Florent renamed in his fork to LiteDRAM?), it doesn't matter whether you use 0x40000000 or 0x00000000 as reference address. 0x40000000 is just where the L2 cache is mapped in the CPU address space.

@mithro
Copy link
Contributor Author

mithro commented Feb 15, 2018

FYI - The DMA engine in LiteVideo has changed between absolute addressing and start of the DDR addressing at some point in the past.

@bunnie
Copy link
Contributor

bunnie commented Feb 15, 2018 via email

@jordens
Copy link
Member

jordens commented Feb 15, 2018

I am pretty sure that vivado does the relevant stages of the optimization on the flat netlist. It may do something early on when synthesizing but I don't think that's impacting it.
I would actually go as far as saying that relying on implicit verilog behavior and not trying to outsmart the synthesizer is a good thing here.

@jordens
Copy link
Member

jordens commented Feb 15, 2018

Supporting a hard interfaces between modules and making it easy and fast to use them has a lot of benefits (see #70). I'd like to see that implemented. But I wouldn't enforce it. It collides with lot of really important metaprogramming use cases in migen.

@bunnie
Copy link
Contributor

bunnie commented Feb 15, 2018 via email

@enjoy-digital
Copy link
Contributor

A bit of context: @bunnie is trying to get a a design with 2 hdmi input working. This is something that has already been validated, but @bunnie's case is a bit specific since only second hdmi is doing DMA. I haven't found time yet to help him with this and he's trying to debug things. One interesting thing is that things that seems clear for us are maybe not that natural without documentation.

@bunnie: the DMA is only able to target the DRAM. So the way you define the addresses depends from which point of view you are seeing things. If your DRAM is only used by some DRAM DMA engines, then yes you can use relative DRAM addresses. But if you want to access if from your CPU (writing a color pattern for example in your framebuffer, you have to use absolute DRAM addresses. (and DRAM is mapped to 0x40000000 so translation is natural).

@bunnie: to help debug, you can also generate litescope as a single core (will include the UART + analyzer) and integrate that in your design. This allow you to debug even when CPU system is crashed. This woud be similar to microscope.

@mithro
Copy link
Contributor Author

mithro commented Feb 15, 2018

So I think this bug report has gotten a bit off topic here. Can we loop back to just the idea around the question;

Should Migen generate explicit Verilog for mismatched bit vector lengths (rather then relying on implicit Verilog behaviour?)

The summary so far seems to be;

  • @jordens Seems to be against the idea as it seems like a normal thing to do in Verilog and we rely on Verilog to be somewhat sane in many cases.
  • @bunnie Thinks that doing this would help reading the generate verilog - specially in larger design cases.
  • @sbourdeauducq Seems open to the idea of sending Migen to support some type of explicit stuff around this "maybe in a way similar to Rust's as u... construct" but doesn't know what the Python syntax might look like?
  • @enjoy-digital Has not expressed an opinion yet.

FYI From what I can see both Vivado and ISE generate warnings about both the truncation and extension cases? There also seems to be a huge amount of complaints about this warning existing.

WARNING:HDLCompiler:413 - "my_module.v" Line 123: Result of 25-bit expression is truncated to fit in 24-bit target.

According to some smug people on the internet the "rules are rather explicit in 1364-2005 LRM". This was countered with;

All operands are widened to the same width as the widest - including
the left-hand side. Then, do the arithmetic in that width.
Then assign the result to the left-hand side,
dropping more-significant bits if necessary.

BUT... it isn't quite that simple. The main difficulties are:

(1) signed vs. unsigned. If ALL the right-hand side operands
are signed, then widening is performed by sign extension
rather than by zero-fill. If ANY right-hand side operand
is unsigned, then ALL widening and arithmetic is unsigned.
This behaviour is NOT affected by whether the left-hand
side is signed or unsigned.

(2) self-determined expressions. There are some places in
Verilog where an expression or operand is isolated - it is
in a "self-determined context" - and its width is not affected
by, and does not affect, the surrounding expression. All
detailed fully in IEEE Std.1364-2005.

There have been many discussions of this issue here.
A glance at Steven Sharp's posting history over the
past two years will produce some gems.

I'm actually very much leaning towards thinking that Migen should generate the explicit assignments -- or atleast have a flag which enables this behaviour?

@sbourdeauducq Would you be willing to merge it if we added this functionality?

@sbourdeauducq
Copy link
Member

The Verilog rules are convoluted and a footgun, but synthesizers implement them correctly and Migen makes use of them (it works similarly to MyHDL in that respect, i.e. http://www.jandecaluwe.com/hdldesign/counting.html).

I'm actually very much leaning towards thinking that Migen should generate the explicit assignments -- or atleast have a flag which enables this behaviour?
Would you be willing to merge it if we added this functionality?

Maybe, but you have to think carefully about things such as:

  • how does it works with signed signals?
  • does it really help since the current Migen syntax also makes uses of implicit extensions/truncations? Is it consistent?
  • who will test thoroughly the new code on all ARTIQ core devices and fix regressions?

@mithro
Copy link
Contributor Author

mithro commented Feb 16, 2018

From the counting page you linked too, it says;

The convertor will generate the required sign extensions, resizings and type castings. It therefore automates this tedious and error-prone bookkeeping task for you.

This is what Migen is doing, correct?

Migen understands the sign extension / resizing / type casting -- it's the human who is reading the generated code from Verilog that doesn't understand it correctly.

So, we are asking Migen to be explicit about the required sign extensions, resizings and type castings to help human readers. If the Verilog implementation is correct, there should be no change in behaviour in the output Verilog.

@jordens
Copy link
Member

jordens commented Feb 16, 2018

I am ok with merging something like that because it can be made optional. But my guess is also that readability will actually suffer and that being explicit is complicated. But the code is there and we already implement those rules in the simulator.

@whitequark
Copy link
Contributor

whitequark commented Feb 16, 2018

who will test thoroughly the new code on all ARTIQ core devices and fix regressions?

The fact that this question needs to be asked points to serious deficiencies in the design of migen. Namely, why isn't there a testbench that compares the Migen simulator with a Verilog simulator? Should be easy given they both can generate a .vcd.

(And of course, you can generate testcases using a quickcheck-like tool, hypothesis in case of Python.)

@sbourdeauducq
Copy link
Member

Even with good tests, there can be unexpected problems followed by long yak-shaving sessions, so this question needs to be asked in any case. For example, I can imagine that Vivado might not optimize well some new explicit sign extension code, and timing gets broken. But yes, good idea.

@mithro
Copy link
Contributor Author

mithro commented Feb 20, 2018

Just FYI - @cliffordwolf has a tool called vloghammer which he uses to find bugs in various Verilog implementations.

@whitequark
Copy link
Contributor

Triage: fixed in nMigen, which relies on Yosys to emit Verilog. The Yosys verilog backend is heavily tested using vloghammer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants