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

Invalid Verilog generated for .bool() of zero-width slice #41

Closed
Wren6991 opened this issue Mar 5, 2019 · 7 comments
Closed

Invalid Verilog generated for .bool() of zero-width slice #41

Wren6991 opened this issue Mar 5, 2019 · 7 comments
Milestone

Comments

@Wren6991
Copy link
Contributor

Wren6991 commented Mar 5, 2019

Repro:

#!/usr/bin/env python3

from nmigen import *
from nmigen.cli import *

class Problem:
	def __init__(self):
		self.a = Signal()

	def elaborate(self, platform):
		m = Module()
		m.d.comb += self.a.eq(Signal()[:0].bool())
		return m

if __name__ == "__main__":
	p = Problem()
	main(p, ports=[p.a])
$ ./width.py generate > width.v
$ yosys -p "read_verilog width.v"
1. Executing Verilog-2005 frontend.
Parsing Verilog input from `width.v' to AST representation.
width.v:13: ERROR: syntax error, unexpected '}'

The generated file contains an empty concat list. On the other hand, this seems to work fine:

	def elaborate(self, platform):
		m = Module()
		m.d.comb += self.a.eq(Signal(0).bool())
		return m

This is with nMigen master and a very recent Yosys.

Designs doing this kind of slice simulate fine, and Signal(0).bool() == Signal(n)[:0].bool() == 0 which makes sense to me. (Is this written down anywhere? Verilog does an awful job of 0-width signals, but they're a useful generalisation, take a lot of edge cases out of your code)

@whitequark
Copy link
Contributor

That's a Yosys bug, since Yosys is what writes the Verilog in the first place.

@Wren6991
Copy link
Contributor Author

Wren6991 commented Mar 6, 2019

You're 100% right, sorry. This is the ilang nMigen generates:

module \top
  wire width 1 input 0 $signal
  wire width 1 output 1 \a
  wire width 1 $next\a
  wire width 1 $1
  cell $reduce_bool $2
    parameter \A_SIGNED 0
    parameter \A_WIDTH 0
    parameter \Y_WIDTH 1
    connect \A {}
    connect \Y $1
  end
  process $group_0
    assign $next\a 1'0
    assign $next\a $1
    sync init
    sync always
      update \a $next\a
  end
end

And this is syntactically valid as it's deliberately special-cased in ilang_parser.y in Yosys, although the Yosys manual is less clear on it (4.2.4):

A “signal” is everything that can be applied to a cell port. I.e.
...
• Concatenations of the above
For example: {16’d1337, mywire[15:8]}

However the sequence read_ilang; write_verilog; delete; read_verilog fails, whilst read_ilang; synth; write_verilog; delete; read_verilog succeeds. I'll open an upstream issue after a bit more digging.

By the way this wasn't deliberate troublemaking, I had some code that looked like this:

def smear_lx(sig):
	return Cat(sig[:i].bool() for i in range(sig.nbits))

def priority_select(req):
	return req & ~smear_lx(req)

def round_robin(req, prev_grant):
	req_unwrap = Cat(req & smear_lx(prev_grant), req)
	grant_unwrap = priority_select(req_unwrap)
	return grant_unwrap[:req.nbits] | grant_unwrap[req.nbits:]

and I guess there's a secondary question on this issue, namely, is this intended to be valid in nMigen?

@whitequark
Copy link
Contributor

I think zero width values are perfectly reasonable. We currently don't support zero width signals, mostly for historic reasons; I'm less certain about those but it probably makes sense to support those too.

@whitequark
Copy link
Contributor

We currently don't support zero width signals, mostly for historic reasons

Actually, we do support them, which is good.

@Wren6991
Copy link
Contributor Author

Wren6991 commented Apr 10, 2019 via email

@Wren6991
Copy link
Contributor Author

Clifford's reponse on Yosys issue:

You should never use write_verilog with an RTLIL design with processes. It even gives you the following warning:

Warning: Module top contains unmapped RTLIL processes. RTLIL processes
can't always be mapped directly to Verilog always blocks. Unintended
changes in simulation behavior are possible! Use "proc" to convert
processes to logic networks and registers.

I think I've fixed it in #953. But generating Verilog with zero-width expressions is really asking for troubles imho. I think it's better to get rid of them by running something like opt_expr before writing the Verilog output.

The Yosys patch just generates an empty Verilog string literal when a zero-width expression is encountered. The resulting Verilog synthesises fine with Yosys, so I think this issue can be closed.

However as Clifford said, this is a dingy corner of the standard, so perhaps:

  • If RTLIL has a strong opinion about how zero-width signals should work, it should enforce that internally
  • OR if nMigen has a strong opinion on how zero-width signals should work, it should enforce that before generating RTLIIL
  • OR option 3, "shut up Luke"

I also note that that same warning ^^^ is produced for the full nMigen pipeline of

read_ilang <<rtlil
{}
rtlil
proc_init
proc_arst
proc_dff
proc_clean
memory_collect
write_verilog -norename

@whitequark
Copy link
Contributor

whitequark commented Apr 23, 2019

Ugh, that's annoying. I think if Yosys doesn't want to support zero-width signals in RTLIL or write_verilog it should enforce that. Otherwise the current situation is fine.

Thanks for investigating.

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

2 participants