GithubHelp home page GithubHelp logo

amaranth-lang / amaranth Goto Github PK

View Code? Open in Web Editor NEW
1.4K 1.4K 163.0 3.37 MB

A modern hardware definition language and toolchain based on Python

Home Page: https://amaranth-lang.org/docs/amaranth/

License: BSD 2-Clause "Simplified" License

Python 100.00%
amaranth-hdl fpga hdl

amaranth's People

Contributors

adamgreig avatar anuejn avatar arusekk avatar awygle avatar bl0x avatar cr1901 avatar dlharmon avatar emilazy avatar fatsie avatar hofstee avatar jfng avatar kbeckmann avatar ktemkin avatar lunaphied avatar mcclure avatar mwkmwkmwk avatar neuschaefer avatar newhouseb avatar peteut avatar programmerjake avatar rroohhh avatar sbourdeauducq avatar sjolsen avatar tpwrules avatar trabucayre avatar wanda-phi avatar whitequark avatar xiretza avatar zignig avatar zyp avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

amaranth's Issues

Simulator: adding two clocks to a single domain causes DeadlineError in simulation

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

Support for yosys' $anyconst and $anyseq cells

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

multiple memory write ports fails in pysim

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()

Memory with only a read port fails in pysim

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
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.

Fix #17 (Add a more user-friendly error message when the yosys binary can't be found)

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

Unassigned signals do not assume their reset values

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 ?

Give the top level scope a name to fix vcd file production

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:

image

This patch sets the root_fragment's scope to "top", producing a vcd with a useful hierarchy:

image

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

lib.cdc: add optional reset to MultiReg, and document its use cases

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

Assume/Assert Cell Support

Issue by cr1901
Tuesday Jan 01, 2019 at 09:24 GMT
Originally opened as m-labs/nmigen#18


Scope

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 Asserts/Assumes 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!

Limitations

  • Something analogous to 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.
  • You can only do immediate asserts (in SystemVerilog, 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

Bikeshed: design for .get_fragment()

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:

  • There are 2 kinds of synthesizable entities in nMigen: fragments (which are directly synthesizable) and anything that has .get_fragment(platform) (which is iterated to a fixed point, i.e. a fragment, when a fragment is required).
  • Instances, memory ports, etc, are synthesizable. Which class they actually are is an implementation detail that is subject to change. (Instances are currently just fragments, memory ports are significantly more complex due to some mismatch between Yosys and Migen representation of ports, etc).
  • There is a 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:

  1. The actual 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.
  2. If a parent module directly calls 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?

Give hint when user forgets to return `Module` from `elaborate`.

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

Signals with non power of two maximum value

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:

  1. Adding a new kind of 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.
  2. Adding a new parameter to the Signal constructor which lets the user assure the specified maximum value is never exceded. Something like this could also be added to the new kind of Signal proposed.

Please consider an optional reset on MultiReg

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 :)

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

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)

Unexpected Memory behaviour in simulation

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:

image

This is the weird one: last cycle's data is written to this cycle's slot:

image

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()

image

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?

seperator of signal names in records causes ambiguity

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)

back.verilog: Memory with asynchronous transparent read port needs a CLK signal ?

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.

Multiclock simulation broken

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)

Fix CDC issue in async FIFO: ensure that Gray code pointers are registered before resynchronisation.

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 domain
  • -> GrayEncoder
  • -> 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:

  • So far I've only run the FIFO smoke test on it. I haven't yet figured out how to run the formal test :( and it also needs soak testing on an FPGA board with two completely separate oscillators.
  • The pointer MultiRegs should ideally be resettable (although that is a different conversation!)
  • I added a helper class, GrayBinCounter, which is currently only used by AsyncFIFO. It contains some extra detail, like putting a no_retiming attribute on the Gray register.
    • Currently I'm passing the domain name into the __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 Fragments, not Modules, and I currently don't know what a Fragment is :)
    • If you would prefer not to have an extra helper class, I can give an alternative patch that just modifies 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

How do multiple If statements work?

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(?

Project Structure And Tox

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

Show a better error message when "module contains unmapped RTLIL processes"

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? )

Decide on the specific simulation and synthesis behavior for out-of-bounds Array access

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:

  • In simulation, a hard crash, with a nice message. (This already happens, but without the nice message.)
  • In synthesis, xs, 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.

ClockSignal/ResetSignal cannot be used on LHS

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.

Make it possible for generators to simulate combinatorial logic

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.

[WIP] Add nmigen.build

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

Implement a sanitizer for memory port combinations

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.

Is there any reason to keep `xxx.submodules`?

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)

Add a more user-friendly error message when the yosys binary can't be found

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

WIP: Expand and document lib.cdc

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:

  • Add docstrings to the existing modules. I tried to copy the "house style" I saw elsewhere.
  • Basic checking to give more meaningful traces when you do something like request a MultiReg of length 0. Again I tried to copy the idioms I saw elsewhere
  • Implement PulseSynchronizer
  • Implement Gearbox

Known issues:

  • Smoke tests only; no formal checks
  • Checks are limited by single-clock simulation -- maybe it was a bad idea to play with the CDC library first :)
  • I'm passing in the domain names rather than using DomainRenamer. Not sure whether it's best to leave this PR open until this is in place, or merge early so people can start hacking on this. Not my decision :)
  • The storage size calculation in this version of Gearbox is different to the one in Migen. IMO the old one was unsafe, but this one may be too conservative. I could also be plain wrong!
  • (edit:) The output mux on the Gearbox is driven from two clock domains. This is a legitimate thing to do here, but I saw mention somewhere that doing this should cause an error.

I definitely still intend to port:

  • BusSynchronizer
  • ElasticBuffer

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

Change name seperator for siganls in records to double underscore

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

Indexing an Array of differently shaped integer constants with a Signal generates invalid RTLIL

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]).

Require signals crossing clock domains to be explicitly marked

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

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.