amaranth-lang / amaranth Goto Github PK
View Code? Open in Web Editor NEWA modern hardware definition language and toolchain based on Python
Home Page: https://amaranth-lang.org/docs/amaranth/
License: BSD 2-Clause "Simplified" License
A modern hardware definition language and toolchain based on Python
Home Page: https://amaranth-lang.org/docs/amaranth/
License: BSD 2-Clause "Simplified" License
Issue by q3k
Saturday Jan 26, 2019 at 20:11 GMT
Originally opened as m-labs/nmigen#27
From IRC, when I run:
class PulseSynchronizerTestCase(FHDLTestCase):
def test_basic(self):
m = Module()
m.domains += ClockDomain("one")
m.domains += ClockDomain("two")
with Simulator(m, vcd_file=open("test.vcd", "w")) as sim:
sim.add_clock(2e-6, domain="one")
sim.add_clock(3e-6, domain="one")
def process():
yield Tick(domain="one")
yield Tick(domain="two")
sim.add_process(process)
sim.run()
The bug is sim.add_clock(3e-6, domain="one")
, instead of domain="two"
. However, instead of a nice error, I get:
self = <nmigen.back.pysim.Simulator object at 0x7fb1836445f8>, run_passive = False
def step(self, run_passive=False):
# Are there any delta cycles we should run?
if self._state.curr_dirty.any():
# We might run some delta cycles, and we have simulator processes waiting on
# a deadline. Take care to not exceed the closest deadline.
if self._wait_deadline and \
(self._timestamp + self._delta) >= min(self._wait_deadline.values()):
# Oops, we blew the deadline. We *could* run the processes now, but this is
# virtually certainly a logic loop and a design bug, so bail out instead.d
> raise DeadlineError("Delta cycles exceeded process deadline; combinatorial loop?")
E nmigen.back.pysim.DeadlineError: Delta cycles exceeded process deadline; combinatorial loop?
nmigen/back/pysim.py:755: DeadlineError
Issue by cr1901
Tuesday Jan 15, 2019 at 22:08 GMT
Originally opened as m-labs/nmigen#25
AnyConst
and AnySeq
correspond to to the $anyconst
and $anyseq
RTLIL cells. They are useful for formal verification to represent "any value that doesn't change", and "any value that can change to another arbitrary value each clock cycle" respectively.
Usage is analogous to declaring a Constant
or Signal
:
self.foo = AnyConst(2)
self.bar = AnySeq(16)
cr1901 included the following code: https://github.com/m-labs/nmigen/pull/25/commits
Issue by programmerjake
Saturday Mar 23, 2019 at 01:09 GMT
Originally opened as m-labs/nmigen#47
I'm using nmigen d69a4e2 (master as of march 22 2019)
Test Case:
from nmigen import Module, Memory
from nmigen.back.pysim import Simulator, Delay, Tick
import unittest
class TestMemory(unittest.TestCase):
def test(self) -> None:
class TestModule:
def __init__(self):
self.mem = Memory(1, 2, name="mem", init=[0, 0])
self.mem_rd0 = self.mem.read_port(synchronous=False)
self.mem_rd1 = self.mem.read_port(synchronous=False)
self.mem_wr0 = self.mem.write_port(priority=0)
self.mem_wr1 = self.mem.write_port(priority=1)
def elaborate(self, platform):
m = Module()
m.submodules.mem_rd0 = self.mem_rd0
m.submodules.mem_rd1 = self.mem_rd1
m.submodules.mem_wr0 = self.mem_wr0
m.submodules.mem_wr1 = self.mem_wr1
m.d.comb += self.mem_rd0.addr.eq(0)
m.d.comb += self.mem_rd1.addr.eq(1)
m.d.comb += self.mem_wr0.addr.eq(0)
m.d.comb += self.mem_wr0.data.eq(1)
m.d.comb += self.mem_wr0.en.eq(1)
m.d.comb += self.mem_wr1.addr.eq(1)
m.d.comb += self.mem_wr1.data.eq(1)
m.d.comb += self.mem_wr1.en.eq(1)
return m
module = TestModule()
with Simulator(module,
vcd_file=open("test.vcd", "w"),
gtkw_file=open("test.gtkw", "w"),
traces=[module.mem_rd0.data,
module.mem_rd1.data]) as sim:
sim.add_clock(1e-6, 0.25e-6)
def async_process():
yield Delay(1e-7)
self.assertEqual((yield module.mem_rd0.data), 0)
self.assertEqual((yield module.mem_rd1.data), 0)
yield Tick()
yield Delay(1e-7)
self.assertEqual((yield module.mem_rd0.data), 1)
self.assertEqual((yield module.mem_rd1.data), 1)
sim.add_process(async_process)
sim.run()
Issue by adamgreig
Saturday Jan 05, 2019 at 02:19 GMT
Originally opened as m-labs/nmigen#20
A memory with a read port but no write port leads to a mysterious KeyError when simulated. Small example:
from nmigen import Module, Memory
from nmigen.back import pysim
def test_mem():
m = Module()
mem = Memory(width=8, depth=4, init=[0xaa, 0x55])
m.submodules.rdport = rdport = mem.read_port()
# Uncomment to fix this test
# m.submodules.wrport = mem.write_port()
with pysim.Simulator(m.lower(None)) as sim:
def process():
yield rdport.addr.eq(1)
yield
yield
assert (yield rdport.data) == 0x55
sim.add_clock(1e-6)
sim.add_sync_process(process)
sim.run()
if __name__ == "__main__":
test_mem()
As shown, this example results in the following traceback:
Traceback (most recent call last):
File "rom.py", line 22, in <module>
test_mem()
File "rom.py", line 10, in test_mem
with pysim.Simulator(m.lower(None)) as sim:
File "/home/adam/.local/lib/python3.6/site-packages/nmigen-0.1-py3.6.egg/nmigen/back/pysim.py", line 551, in __enter__
funclet = compiler(statements)
File "/home/adam/.local/lib/python3.6/site-packages/nmigen-0.1-py3.6.egg/nmigen/hdl/xfrm.py", line 179, in __call__
return self.on_statement(value)
File "/home/adam/.local/lib/python3.6/site-packages/nmigen-0.1-py3.6.egg/nmigen/hdl/xfrm.py", line 174, in on_statement
return self.on_statements(stmt)
File "/home/adam/.local/lib/python3.6/site-packages/nmigen-0.1-py3.6.egg/nmigen/back/pysim.py", line 329, in on_statements
stmts = [self.on_statement(stmt) for stmt in stmts]
File "/home/adam/.local/lib/python3.6/site-packages/nmigen-0.1-py3.6.egg/nmigen/back/pysim.py", line 329, in <listcomp>
stmts = [self.on_statement(stmt) for stmt in stmts]
File "/home/adam/.local/lib/python3.6/site-packages/nmigen-0.1-py3.6.egg/nmigen/hdl/xfrm.py", line 172, in on_statement
return self.on_Switch(stmt)
File "/home/adam/.local/lib/python3.6/site-packages/nmigen-0.1-py3.6.egg/nmigen/back/pysim.py", line 319, in on_Switch
cases.append((make_test(mask, value), self.on_statements(stmts)))
File "/home/adam/.local/lib/python3.6/site-packages/nmigen-0.1-py3.6.egg/nmigen/back/pysim.py", line 329, in on_statements
stmts = [self.on_statement(stmt) for stmt in stmts]
File "/home/adam/.local/lib/python3.6/site-packages/nmigen-0.1-py3.6.egg/nmigen/back/pysim.py", line 329, in <listcomp>
stmts = [self.on_statement(stmt) for stmt in stmts]
File "/home/adam/.local/lib/python3.6/site-packages/nmigen-0.1-py3.6.egg/nmigen/hdl/xfrm.py", line 165, in on_statement
return self.on_Assign(stmt)
File "/home/adam/.local/lib/python3.6/site-packages/nmigen-0.1-py3.6.egg/nmigen/back/pysim.py", line 295, in on_Assign
rhs = self.rrhs_compiler(stmt.rhs)
File "/home/adam/.local/lib/python3.6/site-packages/nmigen-0.1-py3.6.egg/nmigen/hdl/xfrm.py", line 100, in __call__
return self.on_value(value)
File "/home/adam/.local/lib/python3.6/site-packages/nmigen-0.1-py3.6.egg/nmigen/hdl/xfrm.py", line 92, in on_value
new_value = self.on_ArrayProxy(value)
File "/home/adam/.local/lib/python3.6/site-packages/nmigen-0.1-py3.6.egg/nmigen/back/pysim.py", line 201, in on_ArrayProxy
elems = list(map(self, value.elems))
File "/home/adam/.local/lib/python3.6/site-packages/nmigen-0.1-py3.6.egg/nmigen/hdl/xfrm.py", line 100, in __call__
return self.on_value(value)
File "/home/adam/.local/lib/python3.6/site-packages/nmigen-0.1-py3.6.egg/nmigen/hdl/xfrm.py", line 73, in on_value
new_value = self.on_Signal(value)
File "/home/adam/.local/lib/python3.6/site-packages/nmigen-0.1-py3.6.egg/nmigen/back/pysim.py", line 93, in on_Signal
value_slot = self.signal_slots[value]
File "/home/adam/.local/lib/python3.6/site-packages/nmigen-0.1-py3.6.egg/nmigen/hdl/ast.py", line 961, in __getitem__
return self._storage[key]
KeyError: <nmigen.hdl.ast.SignalKey (sig mem(0))>
Uncommenting the write_port
fixes the example which then works as expected.
Issue by mithro
Thursday Dec 13, 2018 at 20:46 GMT
Originally opened as m-labs/nmigen#1
Could we get an example of embedding existing Verilog code? With specials having gone, it's unclear to me how that works?
Issue by whitequark
Sunday Jan 27, 2019 at 00:32 GMT
Originally opened as m-labs/nmigen#30
Issue by The6P4C
Tuesday Jan 01, 2019 at 09:55 GMT
Originally opened as m-labs/nmigen#19
Should fix #17.
Not sure if this is the error message format you'd like, couldn't find any examples of raising a similar error. Keeps the original FileNotFound
error intact by chaining the RuntimeError
to it.
Tested on both Linux and Windows.
The6P4C included the following code: https://github.com/m-labs/nmigen/pull/19/commits
Issue by whitequark
Friday Dec 14, 2018 at 17:39 GMT
Originally opened as m-labs/nmigen#3
17:26 < sb0> whitequark: https://github.com/m-labs/nmigen/blob/master/examples/alu_hier.py#L41-L42
17:26 < sb0> a typical bug is forgetting to add this in migen. would there be a way of addressing this issue in nmigen?
Issue by peteut
Monday Mar 11, 2019 at 17:27 GMT
Originally opened as m-labs/nmigen#42
ResetSignal().eq(delay == 0)
should spell ResetSignal().eq(delay != 0)
as delay
counts down to zero.
peteut included the following code: https://github.com/m-labs/nmigen/pull/42/commits
Issue by jfng
Tuesday Feb 05, 2019 at 15:56 GMT
Originally opened as m-labs/nmigen#35
Leaving a Signal
unassigned does not make it assume its reset
value when used as the RHS of an assignment.
Consider the following snippet:
from nmigen import *
from nmigen.back import rtlil
class Foo:
def elaborate(self, platform):
m = Module()
foo = Signal(reset=1)
bar = Signal()
m.d.comb += bar.eq(foo)
return m
foo = Foo()
frag = foo.elaborate(platform=None)
print(rtlil.convert(frag))
which produces the following IR:
attribute \generator "nMigen"
attribute \top 1
module \top
attribute \src "foo.py:9"
wire width 1 input 0 \foo
attribute \src "foo.py:10"
wire width 1 \bar
attribute \src "foo.py:10"
wire width 1 $next\bar
process $group_0
assign $next\bar 1'0
assign $next\bar \foo
sync init
sync always
update \bar $next\bar
end
end
Should \foo
be set to its reset value instead of being propagated as an input ?
Issue by adamgreig
Sunday Jan 06, 2019 at 00:09 GMT
Originally opened as m-labs/nmigen#21
At present the root_fragment
in the pysim hierarchy
has an empty scope=()
, and when this is added to the vcd file we get:
$scope module $end
i.e. the top-level module has no name. This breaks gtkwave somewhat as you can't re-select the top level after selecting a submodule:
This patch sets the root_fragment
's scope to "top"
, producing a vcd with a useful hierarchy:
I can't see an obvious way to get a more useful name than "top" for the root fragment, but if one exists that would be even better.
adamgreig included the following code: https://github.com/m-labs/nmigen/pull/21/commits
Issue by whitequark
Sunday Jan 27, 2019 at 23:15 GMT
Originally opened as m-labs/nmigen#31
Issue by Wren6991
Tuesday Mar 19, 2019 at 03:51 GMT
Originally opened as m-labs/nmigen#44
Following on from #37.
This patch adds a reset_less
parameter to MultiReg. The default behaviour is the same as the original behaviour. It also adds a docstring to MultiReg which, besides usual docstring stuff, adds the following footnote:
Note on Reset
-------------
MultiReg is non-resettable by default. Usually this is the safest option;
on FPGA the MultiReg will still be initialised to its `reset` value,
when the FPGA loads its configuration bitstream.
However, in designs where the value of the MultiReg must be valid
immediately after reset, consider `reset_less`=False if:
- You are targeting an ASIC, or an FPGA that does not initialise
flipflop values
- Your design features warm (non-power-on) resets of `odomain`, so
the one-time initialisation at power on is insufficient
- Your design features a sequenced reset, and the MultiReg must maintain
its reset value until `odomain` reset specifically is deasserted.
MultiRegs are reset by the `odomain` reset only.
I made sure to make it explicit that the default behaviour relies on FPGA bitstream config. Not sure if this is exactly the kind of documentation you intended -- let me know and I will fix it up :)
Thanks
Wren6991 included the following code: https://github.com/m-labs/nmigen/pull/44/commits
Issue by cr1901
Tuesday Jan 01, 2019 at 09:24 GMT
Originally opened as m-labs/nmigen#18
This PR adds the beginning of formal verification support to nmigen
. With this PR, a user can now write statements of the following form:
m.d.comb += Assert(foo == bar)
m.d.sync += Assert(baz == 0)
with m.If(quux):
m.d.comb += Assume(dummy == 1)
The first statement in particular will create Assert
s/Assume
s analogous to yosys
's "properties
":
assert property (foo == 0)
A yosys
property
is syntactic sugar for:
always @(*) assert(foo == 0)
The third statement is analogous to a SystemVerilog immediate assert
/assume
.
The generated RTLIL can then be fed to yosys
, which outputs an smtv2
file. Using yosys-smtbmc
or symbiyosys
, one can now run a basic formal verification flow using nmigen
!
initial assume/assert
not yet supported (easy to add initial support, but I don't know what the semantics should be if they are used within an If/Else/Switch
context.property
is for concurrent asserts, and AFAIK yosys
does not support these).cr1901 included the following code: https://github.com/m-labs/nmigen/pull/18/commits
Issue by whitequark
Monday Dec 17, 2018 at 20:57 GMT
Originally opened as m-labs/nmigen#9
I know @jordens expressed in the past a desire to simplify the boilerplate around .get_fragment
. Also, there is currently both Module.lower
and Module.get_fragment
(aliased to one another), which is confusing and unnecessary. Also#2, Migen has a Module.get_fragment()
and combining Migen modules with nMigen modules gets even more confusing with that.
The design I see for the function currently named .get_fragment()
is as follows:
.get_fragment(platform)
(which is iterated to a fixed point, i.e. a fragment, when a fragment is required).Fragment.get(obj, platform)
function that iterates an object to a fixed point with .get_fragment(platform)
or fails in a nice way.Modules are in a somewhat weird space here. Modules should be synthesizable so that you can replace:
sm = Module()
# ... operate on sm
m = Module()
m.submodules += sm
with:
class SubCircuit:
def get_fragment(self, platform):
m = Module()
return m.lower(platform)
sm = SubCircuit()
m = Module()
m.submodules += sm
that is without having to do downstream changes after replacing a Module
with a user class. This means that in principle you can directly return a module, like return m
.
But this means two things:
get_fragment
call for that module is delayed. The conversion of a module to a fragment is mostly (but not entirely) error-free, so delaying this call means some errors lose context. I think it might be more useful to make Module.get_fragment
actually never fail, and permit return m
.get_fragment
on a submodule, it gets a Module
object with the DSL enabled, as opposed to a Fragment
object. So it can potentially abuse the DSL that way. This isn't very likely so maybe we should just ignore it.And finally, there is an important question of what to rename .get_fragment
too. I don't want something like .build
because it is ambiguous and prone to collisions. What about .synthesize
?
Issue by cr1901
Tuesday Jan 29, 2019 at 14:14 GMT
Originally opened as m-labs/nmigen#33
Multiple times, I have forgotten to return a Module
from elaborate
, and I've found the resulting error to be unhelpful (even though I realize what it means now). The following traceback is the alu.py example with line 23 commented out:
$ python3 alu.py generate
Traceback (most recent call last):
File "alu.py", line 28, in <module>
main(alu, ports=[alu.sel, alu.a, alu.b, alu.o, alu.co])
File "C:\msys64\home\william\src\nmigen\nmigen\cli.py", line 76, in main
main_runner(parser, parser.parse_args(), *args, **kwargs)
File "C:\msys64\home\william\src\nmigen\nmigen\cli.py", line 46, in main_runner
fragment = Fragment.get(design, platform)
File "C:\msys64\home\william\src\nmigen\nmigen\hdl\ir.py", line 23, in get
return Fragment.get(obj.elaborate(platform), platform)
File "C:\msys64\home\william\src\nmigen\nmigen\hdl\ir.py", line 22, in get
return Fragment.get(obj.get_fragment(platform), platform)
AttributeError: 'NoneType' object has no attribute 'get_fragment'
With this PR, I have added code to give a hint to users what the problem is likely to be. The traceback is changed to the following:
$ python3 alu.py generate
Traceback (most recent call last):
File "alu.py", line 28, in <module>
main(alu, ports=[alu.sel, alu.a, alu.b, alu.o, alu.co])
File "C:\msys64\home\william\src\nmigen\nmigen\cli.py", line 76, in main
main_runner(parser, parser.parse_args(), *args, **kwargs)
File "C:\msys64\home\william\src\nmigen\nmigen\cli.py", line 46, in main_runner
fragment = Fragment.get(design, platform)
File "C:\msys64\home\william\src\nmigen\nmigen\hdl\ir.py", line 27, in get
return Fragment.get(obj.elaborate(platform), platform)
File "C:\msys64\home\william\src\nmigen\nmigen\hdl\ir.py", line 25, in get
.format(type(obj).__name__))
AttributeError: 'NoneType' object has no attribute 'elaborate' or 'get_fragment'. Did you remember to return a Module object from each user-defined 'elaborate' or 'get_fragment' method?
cr1901 included the following code: https://github.com/m-labs/nmigen/pull/33/commits
Issue by rroohhh
Monday Mar 04, 2019 at 19:01 GMT
Originally opened as m-labs/nmigen#39
Currently the maximum value of a Signal
specified using the max
parameter is rounded up to the next power of two.
Because of this something like the following fails with IndexError: Cannot end slice 10 bits into 9-bit value
class Test:
def __init__(self):
self.ctr = Signal(max=9)
self.out = Signal(9)
self.input = Signal()
def elaborate(self, platform):
m = Module()
m.d.sync += self.out.part(self.ctr, 1).eq(self.input)
with m.If(self.ctr == 9):
m.d.sync += self.ctr.eq(0)
with m.Else():
m.d.sync += self.ctr.eq(self.ctr + 1)
return m
While I understand the cause for this error, I don't think this is the best solution possible.
I can think of two possible ways to improve:
Signal
that supports non power of two maximum values (and guarantees that it is not excceded). Guaranteeing this may come with a high cost, but would also simplify some code, like this counter.Signal
proposed. Issue by Wren6991
Wednesday Feb 27, 2019 at 17:34 GMT
Originally opened as m-labs/nmigen#37
I was browsing the code to learn the standard library, and bumped into this in cdc.py:
class MultiReg:
def __init__(self, i, o, odomain="sync", n=2, reset=0):
...
self._regs = [Signal(self.i.shape(), name="cdc{}".format(i),
reset=reset, reset_less=True, attrs={"no_retiming": True})
for i in range(n)]
I understand there are caveats with resets on CDC capture flops, but sometimes you need it. For example, a pulse-to-pulse synchroniser (or toggle synchroniser) contains a toggling NRZ signal crossing the domains, and the capture flops need a capture-domain reset to avoid spurious toggling observed by the logic when deasserting reset.
platform.get_multi_reg(self)
may contain considerable magic for some synthesis flows, and should only need writing once; ideally, something like a pulse-to-pulse synchroniser should be implementable using the existing MultiReg.
A ligh-touch option could be to just add a reset_less=True
to MultiReg.__init__
, and pass this down.
Happy to be argued with, told to go implement it, or just told I'm wrong :)
Issue by Wren6991
Tuesday Mar 05, 2019 at 19:10 GMT
Originally opened as m-labs/nmigen#41
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)
Issue by programmerjake
Thursday Mar 14, 2019 at 03:44 GMT
Originally opened as m-labs/nmigen#43
Issue by whitequark
Sunday Dec 23, 2018 at 11:44 GMT
Originally opened as m-labs/nmigen#13
Any of these cases are unreachable by definition. Should we at least warn?
Issue by adamgreig
Friday Feb 01, 2019 at 00:06 GMT
Originally opened as m-labs/nmigen#34
I noticed in my design that two memories, both defined the same way and in the same clock domain, seemed to have different write behaviours:
This first one is what I'd expect: one cycle after addr=2 and data=2, mem(2) gets the value 2:
This is the weird one: last cycle's data is written to this cycle's slot:
I made a small reproduction of the buggy case:
from nmigen import Module, Memory, Signal
from nmigen.back import pysim
def test_mem():
m = Module()
mem = Memory(8, 32)
m.submodules.wrport = wrport = mem.write_port()
ctr = Signal(4)
m.d.sync += [
wrport.data.eq(ctr + 0x30),
wrport.addr.eq(ctr),
wrport.en.eq(1),
ctr.eq(ctr + 1),
]
vcdf = open("membug.vcd", "w")
with pysim.Simulator(m, vcd_file=vcdf) as sim:
def testbench():
for _ in range(5):
yield
for idx in range(5):
print(idx, ":", hex((yield mem[idx])))
sim.add_clock(1e-6)
sim.add_sync_process(testbench())
sim.run()
if __name__ == "__main__":
test_mem()
Moving the assignments to wrport.addr
and wrport.data
into a comb block restores the expected behaviour, so I guess it's to do with the addr/data being driven from a sync block?
Issue by jfng
Wednesday Dec 26, 2018 at 14:29 GMT
Originally opened as m-labs/nmigen#14
jfng included the following code: https://github.com/m-labs/nmigen/pull/14/commits
Issue by anuejn
Sunday Mar 24, 2019 at 23:57 GMT
Originally opened as m-labs/nmigen#48
When creating a Record
, the signals inside it are named recordname_fieldname
currently.
This makes it hard to see the hierarchy when using snake case names for records and fields as well. I.e. having a record named some_record
with a field named some_field
the resulting signal is called some_record_some_field
which could also be the name of a signal in a nested record with depth 3.
I propose to use another character for separation (for example .
), which would lead to some_record.some_field
in that case.
(see: https://github.com/m-labs/nmigen/blob/master/nmigen/hdl/rec.py#L75)
Issue by jfng
Tuesday Mar 19, 2019 at 18:57 GMT
Originally opened as m-labs/nmigen#45
Tests are missing.
jfng included the following code: https://github.com/m-labs/nmigen/pull/45/commits
Issue by jfng
Monday Jan 28, 2019 at 16:07 GMT
Originally opened as m-labs/nmigen#32
When nMigen elaborates an asynchronous read port, it does not wire the latter to a clock signal.
https://github.com/m-labs/nmigen/blob/43e4833ddb5f36d8343ae830288eca221cec5fad/nmigen/hdl/mem.py#L102
The following nMigen code instantiates an asynchronous and transparent read port:
from nmigen import *
from nmigen.back import verilog
class Foo:
def __init__(self):
mem = Memory(4, 4)
self.rp = mem.read_port(synchronous=False, transparent=True)
def elaborate(self, platform):
m = Module()
m.submodules += self.rp
return m
foo = Foo()
frag = foo.elaborate(platform=None)
print(verilog.convert(frag))
which produces the following (broken?) output:
/* Generated by Yosys 0.8+147 (git sha1 266511b2, gcc 8.2.1 -fPIC -Os) */
(* top = 1 *)
(* generator = "nMigen" *)
module top(mem_r_addr);
(* src = "/home/jf/src/nmigen/nmigen/hdl/mem.py:86" *)
input [1:0] mem_r_addr;
(* src = "/home/jf/src/nmigen/nmigen/hdl/mem.py:88" *)
wire [3:0] mem_r_data;
reg [3:0] mem [3:0];
initial begin
mem[0] = 4'h0;
mem[1] = 4'h0;
mem[2] = 4'h0;
mem[3] = 4'h0;
end
reg [1:0] _0_;
always @(posedge 1'h0) begin
_0_ <= mem_r_addr;
end
assign mem_r_data = mem[_0_];
endmodule
The Yosys Verilog backend seems to make transparent read ports rely on the presence of a clock input regardless of them being synchronous or not.
https://github.com/YosysHQ/yosys/blob/266511b29eb66486bd17210eb28454a2efee218a/backends/verilog/verilog_backend.cc#L1104-L1109
Removing the if self.synchronous else Const(0)
in the mem.py snippet above seems to work, but it makes asynchronous read ports rely on a clock signal.
This behaviour can currently be encountered when instantiating a SyncFIFO with fwft=True
.
Issue by whitequark
Saturday Jan 26, 2019 at 22:15 GMT
Originally opened as m-labs/nmigen#28
Repro:
from nmigen import *
from nmigen.back.pysim import *
m = Module()
m.domains += ClockDomain("input")
m.domains += ClockDomain("output")
with Simulator(m, vcd_file=open("test.vcd", "w")) as sim:
sim.add_clock(3e-6, domain="input")
sim.add_clock(5e-6, domain="output")
sim.run_until(1, run_passive=True)
Issue by Wren6991
Sunday Mar 03, 2019 at 15:56 GMT
Originally opened as m-labs/nmigen#38
The AsyncFIFO class is subtly different from the Clifford Cummings paper referenced in the source code. Here is a diagram showing the difference:
In the current nMigen code, there is a path that goes from:
produce
counter register in the write domainGrayEncoder
MultiReg
in the read domain.The problem is that there is a narrow window after a write clock edge where all of the Gray pointer bits may toggle, due to static hazards in the encoder, since all encoder inputs can toggle simultaneously. If you sample this with an asynchronous clock, it's possible to see a completely wrong value for the produce
pointer, which can cause irreversible corruption/loss of data.
I've only shown this for the produce
pointer, but the same is true in the other direction too. The fix is to drive the Gray encoder from the binary counter register's input, rather than its output, and register the encoder output before resynchronising. From a synchronous point of view this behaves the same as the old Gray count, so the full/empty logic stays the same.
This bug will only be observed with a low probability, and may not be observed at all, e.g. if your two asynchronous clocks share some fixed rational relationship with a common clock source through PLLs, which affects the distribution of the relative phase of one observed at the rising edge of the other. However it is a legitimate bug, with fatal consequences.
This code is not ready to merge:
MultiReg
s should ideally be resettable (although that is a different conversation!)GrayBinCounter
, which is currently only used by AsyncFIFO
. It contains some extra detail, like putting a no_retiming
attribute on the Gray register.
__init__
of this class, like MultiReg
, but I saw references elsewhere to the DomainRenamer
class, which seems cleaner. However it seems you can only call this on Fragment
s, not Modules
, and I currently don't know what a Fragment
is :)AsyncFIFO
. It just looks a bit copy-pastey.I'm really new to nMigen, so this code definitely needs a close review.
Wren6991 included the following code: https://github.com/m-labs/nmigen/pull/38/commits
Issue by mithro
Friday Dec 14, 2018 at 01:13 GMT
Originally opened as m-labs/nmigen#2
I'm interested in how the If
statement actually works. The m
module seems to track the current "state" in some way?
with m.If(self.a):
m.d.comb += self.o.eq(self.sub.o)
with m.If(self.b):
m.d.comb += self.o.eq(self.add.o)
with m.Else():
m.d.comb += self.o.eq(self.rnd.o)
return m.lower(platform)
How does it actually know that the m.Else()
applies to the first m.If(
?
Issue by peteut
Thursday Jan 10, 2019 at 16:14 GMT
Originally opened as m-labs/nmigen#24
May you accept a pull request to prevent potential issues when using with tox
?
As described in [1] best practice is to use a structure like this if used with tox
[2]:
setup.py
src/
mypkg/
__init__.py
app.py
view.py
tests/
__init__.py
foo/
__init__.py
test_view.py
bar/
__init__.py
test_view.py
The proposed project structure is used for instance in [3].
[1] Pytest:: Choosing a test layout / import rules
[2] Welcome to the tox automation project
[3] migen-axi
Issue by 104player
Tuesday Jan 08, 2019 at 20:32 GMT
Originally opened as m-labs/nmigen#23
Hi, trying to learn nMigen here :)
I have run into a case where .. maybe it's as simple as I have some logic that is not used?
I get an error message like:
python oops.py generate -t v oops.v
Traceback (most recent call last):
File "oops.py", line 34, in main(tw, ports=[])
File "/opt/conda/lib/python3.6/site-packages/nmigen/cli.py", line 74, in main
main_runner(parser, parser.parse_args(), args, kwargs)
File "/opt/conda/lib/python3.6/site-packages/nmigen/cli.py", line 56, in main_runner output = verilog.convert(fragment, name=name, ports=ports)
File "/opt/conda/lib/python3.6/site-packages/nmigen/back/verilog.py", line 37, in convert
raise YosysError(error.strip())
nmigen.back.verilog.YosysError: Warning: Module engine contains unmapped RTLIL proccesses. 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.Warning: Module top contains unmapped RTLIL proccesses. RTLIL process
es
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.ERROR: Assertion failed: selection is not empty: w: i: %a %d o: %a
%ci* %d c:* %co* %a %d n:$* %d
It's not immediately apparent to a user (especially if they change more than a few lines of code at a time) which one is unused or causing this to break..
(I partially manually reduced my example code for this example)
(I have a more complex example that I think I've wired up correctly, but for some reason I need to route some signals out to pins or else it gives this same error ! (kind of like having load bearing nops) I add another signal somewhere from some verilog code, and suddenly this breaks .. sometimes even when I do wire it up to a pin -- hopefully improving the error message here will help! * Ideally let me know which signal, show why it's unmapped? )
Issue by whitequark
Sunday Jan 27, 2019 at 00:28 GMT
Originally opened as m-labs/nmigen#29
For added fun, they cannot be used on CompatModules either.
Issue by whitequark
Sunday Dec 16, 2018 at 10:21 GMT
Originally opened as m-labs/nmigen#7
Migen left this undefined so we have wide latitude here. IMO the following should happen:
x
s, to let the synthesizer discard useless logic. Migen's output causes waste in inferred multiplexers, and Migen's output already causes way too many multiplexers to be inferred. Issue by peteut
Thursday Jan 17, 2019 at 20:41 GMT
Originally opened as m-labs/nmigen#26
There should probably be a default for generate (as there is is hint already). This adds v
as default for -t
.
peteut included the following code: https://github.com/m-labs/nmigen/pull/26/commits
Issue by whitequark
Friday Dec 14, 2018 at 17:43 GMT
Originally opened as m-labs/nmigen#5
Right now nMigen silently generates invalid Verilog for that. It should (eventually) auto-flatten the hierarchy, but at least this should be detected.
Issue by cr1901
Sunday Dec 16, 2018 at 23:11 GMT
Originally opened as m-labs/nmigen#8
The statement m.comb += ResetSignal().eq(0)
can be used in principle to tie the reset signal of a clock domain low (and remove the rst
signal from the top-level port list). However, as-is a backtrace similar to the following results:
William@William-THINK MINGW64 ~/src/migen_mercury/baseboard
$ python3 baseboard.py
Traceback (most recent call last):
File "baseboard.py", line 373, in <module>
m.comb += ResetSignal().eq(0)
File "C:/msys64/home/william/src/nmigen\nmigen\tools.py", line 52, in wrapper
return f(*args, **kwargs)
File "C:/msys64/home/william/src/nmigen\nmigen\compat\fhdl\module.py", line 32, in __iadd__
self._cm._module._add_statement(assigns, domain=None, depth=0, compat_mode=True)
File "C:/msys64/home/william/src/nmigen\nmigen\hdl\dsl.py", line 248, in _add_statement
for signal in assign._lhs_signals():
File "C:/msys64/home/william/src/nmigen\nmigen\hdl\ast.py", line 813, in _lhs_signals
return self.lhs._lhs_signals()
File "C:/msys64/home/william/src/nmigen\nmigen\hdl\ast.py", line 194, in _lhs_signals
raise TypeError("Value {!r} cannot be used in assignments".format(self))
TypeError: Value (rst sync) cannot be used in assignments
Workaround for the time being is to manually declare the clock domain with reset_less=True
.
Issue by nakengelhardt
Tuesday Dec 18, 2018 at 05:24 GMT
Originally opened as m-labs/nmigen#11
Sometimes I want to use generators to build a simulation model of some module. In migen, this wasn't possible for modules that implement combinatorial logic.
Trying my hand at nmigen syntax, an example could be something like this:
class RoutingPolicy:
def __init__(self, width, n):
self.data = Signal(width)
self.destination = Signal(max=n)
# I haven't decided yet what I want this module to do, let's try some options
def gen_destination(self):
while True: # or something?
data = (yield self.data)
if (data == 23):
yield self.destination.eq(0)
else:
yield self.destination.eq(self.data[:log2_int(n)])
class Router:
def __init__(self, width, n):
self.in_port = Signal(width)
self.out_ports = Array(Signal(width) for _ in range(n))
self.policy = RoutingPolicy(width, n)
def get_fragment(self, platform):
m = Module()
m.submodules.policy = self.policy
m.d.comb += [
self.policy.data.eq(self.in_port),
self.out_ports[self.policy.destination].eq(self.in_port)
]
return m.lower(platform)
m = Router(8, 2)
run_simulation(m, m.policy.gen_destination())
There probably needs to be something to tell what constitutes one iteration of the generator, with while True
it would just run indefinitely. while (yield input_values_changed()):
? Also the whole thing would need to be @passive
or so since there's no predefined end point.
Another question is whether it's possible, or even makes sense, to mix combinatorial assignments and synchronous assignments in one generator.
Issue by jfng
Tuesday Mar 19, 2019 at 19:14 GMT
Originally opened as m-labs/nmigen#46
This is a proof-of-concept for a nMigen equivalent of Migen's build system.
The main difference lies in the use of Edalize to invoke vendor tools instead of doing so ourselves.
However, as far as I understand, constraint file (XDC, PCF, etc.) formatting must still be done on our side, which prevents nMigen from being completely vendor agnostic.
jfng included the following code: https://github.com/m-labs/nmigen/pull/46/commits
Issue by whitequark
Friday Dec 21, 2018 at 06:38 GMT
Originally opened as m-labs/nmigen#12
There are a lot of possible combinations of memory ports and many of them are not legal. This depends on the vendor. In this issue we collect such behaviors to eventually implement a sanitizer, either as a part of nMigen or as a Yosys pass.
Issue by sam-falvo
Sunday Jan 06, 2019 at 20:02 GMT
Originally opened as m-labs/nmigen#22
The goal is to work through each section, one by one, receiving documentation reviews along the way. The introduction is a good first step. Feedback appreciated!
sam-falvo included the following code: https://github.com/m-labs/nmigen/pull/22/commits
Issue by mithro
Tuesday Dec 18, 2018 at 00:37 GMT
Originally opened as m-labs/nmigen#10
A huge number of issues in the old migen are caused by forgetting to assign something to xxx.submodules
.
Is there any reason that we can't do something like this?
class Module:
def __setattr__(self, k, v):
if isinstance(v, Module):
self.add_module(k, v)
object.__setattr__(self, k, v)
Issue by The6P4C
Tuesday Jan 01, 2019 at 03:48 GMT
Originally opened as m-labs/nmigen#17
When the YOSYS
environment variable isn't defined, the fallback yosys
is used as the name of the yosys executable (src). When this fails (i.e. the environment variable is not defined and yosys
is not in the user's PATH
), a file not found error is thrown.
Suggestion: add a more user-friendly error message that prompts the user to define/check the YOSYS
environment variable or add yosys to their PATH
Issue by Wren6991
Monday Mar 04, 2019 at 21:06 GMT
Originally opened as m-labs/nmigen#40
Very much a work in progress, but I wanted to open early to get feedback on the approach/direction. I'm porting/rewriting some of the missing modules from migen.genlib.cdc.
Currently in this patch set:
Known issues:
I definitely still intend to port:
And we probably ought to do something about GrayCounter at some point, but I think it's less important than the others.
As a general style question, there seem to be two ways of doing module wiring in nmigen.lib
at the moment. One is to pass external signals into __init__
, which gives you more of a Verilog-style instantiation. The other is to create "port" signals in __init__
, which the parent then wires up during elaboration. I've used the second style because it seems a bit cleaner, and doesn't require creating extraneous signals like in Verilog, but not sure if this is the right thing to do?
Wren6991 included the following code: https://github.com/m-labs/nmigen/pull/40/commits
Issue by anuejn
Monday Mar 25, 2019 at 13:56 GMT
Originally opened as m-labs/nmigen#50
This PR changes the seperator for signal names in records from a single underscore to a double underscore. When creating a Record, the signals inside it are then named recordname__fieldname
.
This gives one more useful information, when looking at the signal names of fields which themselves contain underscores. E.g. record__some_field
is now unambiguous :)
It closes #48.
anuejn included the following code: https://github.com/m-labs/nmigen/pull/50/commits
Issue by whitequark
Tuesday Jan 01, 2019 at 03:26 GMT
Originally opened as m-labs/nmigen#16
This is currently not done because of a quirk (bug?) in Yosys: YosysHQ/yosys#760. This is not right and affects user designs as the en
signal is a constant and cannot be assigned to: https://github.com/m-labs/nmigen/blob/d78e6c155b6de72ab56f7cb123bc2b7cdb8f6b04/nmigen/hdl/mem.py#L89-L92.
Issue by The6P4C
Monday Dec 31, 2018 at 02:12 GMT
Originally opened as m-labs/nmigen#15
The following MWE generates invalid RTLIL and as such fails Verilog generation.
from nmigen import *
from nmigen.cli import main
class Test:
def __init__(self):
self.out = Signal(8)
def get_fragment(self, platform):
data = [0b100, 0b1000, 0b10000]
arr = Array(data)
idx = Signal(max=len(data))
m = Module()
m.d.comb += self.out.eq(arr[idx])
m.d.sync += idx.eq(idx + 1)
return m.lower(platform)
test = Test()
main(test, ports=[test.out])
The following error is produced:
D:\test> python test.py generate -t v test.v
Traceback (most recent call last):
File "test.py", line 19, in <module>
main(test, ports=[test.out])
File "D:\Program Files\Python36\lib\site-packages\nmigen-0.1-py3.6.egg\nmigen\cli.py", line 74, in main
main_runner(parser, parser.parse_args(), *args, **kwargs)
File "D:\Program Files\Python36\lib\site-packages\nmigen-0.1-py3.6.egg\nmigen\cli.py", line 56, in main_runner
output = verilog.convert(fragment, name=name, ports=ports)
File "D:\Program Files\Python36\lib\site-packages\nmigen-0.1-py3.6.egg\nmigen\back\verilog.py", line 37, in convert
raise YosysError(error.strip())
nmigen.back.verilog.YosysError: ERROR: Found error in internal cell \top.$5 ($pos) at kernel/rtlil.cc:704:
attribute \src "test.py:14"
cell $pos $5
parameter \Y_WIDTH 8
parameter \A_WIDTH 5
parameter \A_SIGNED 0
connect \Y $4
connect \A 4'1000
end
This error does not occur when the elements of the Array
all have the same shape (i.e. [0b100, 0b101, 0b111, 0b110]
).
Issue by programmerjake
Monday Mar 25, 2019 at 03:27 GMT
Originally opened as m-labs/nmigen#49
10e6 is incorrect since 10e6 is 10.0*10^6 not 10^6.
Issue by peteut
Thursday Feb 21, 2019 at 16:53 GMT
Originally opened as m-labs/nmigen#36
Installation should be constraint to supported Python versions, using python_requires
, refer to [1] for details.
[1] https://packaging.python.org/guides/distributing-packages-using-setuptools/#python-requires
peteut included the following code: https://github.com/m-labs/nmigen/pull/36/commits
Issue by whitequark
Friday Dec 14, 2018 at 17:40 GMT
Originally opened as m-labs/nmigen#4
17:30 < sb0> whitequark: for multi clock domain designs, I'd also like signals driven in one domain and sampled in another to cause a warning
17:30 < sb0> unless each such path is explicitly marked by the user as valid
17:30 < whitequark> sure
17:30 < whitequark> that's even easier
17:31 < whitequark> what about I/O pins?
17:31 < whitequark> forgetting a MultiReg is a fairly frequent error
17:31 < sb0> ideally, there should be some framework to automatically instantiate LVDS buffers and such
17:31 < sb0> ah, for CDC
17:32 < whitequark> oh, I have this kind of thing in Glasgow
17:32 < whitequark> maybe we should rethink I/O for nmigen
17:32 < sb0> in artiq there are certain things that are sometimes run over CMOS (on KC705) and LVDS (on Kasli over the ribbon cables)
17:33 < sb0> right now this is not handled in an elegant manner
17:34 < sb0> I guess I/O pins could be marked as asynchronous by default, and also could have a clock domain associated with them
17:34 < sb0> then the same rule applies
17:34 < whitequark> ok
17:35 < sb0> the "asychronous mode" is basically another "invisible" clock domain
17:36 < whitequark> what if one half of a comb signal is driven from domain A and another half from domain B?
17:36 < whitequark> how should this be handled?
17:39 < sb0> 1. emit a warning (unless the user says it is valid) 2. treat the result as asynchronous ?
17:40 < whitequark> ok
17:41 < sb0> also, there should also be warnings for paths that are marked as valid CDC but end up being in the same domain
17:41 < sb0> i.e. anything other than marking exactly the CDC paths as valid CDCs results in warnings
Issue by whitequark
Saturday Dec 15, 2018 at 12:10 GMT
Originally opened as m-labs/nmigen#6
These are:
Part
Array
Record
Instance
Memory
FSM
Tristate
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.