/htmlcov
# tests
-**/test/spec_*/
+/tests/spec_*/
*.vcd
*.gtkw
+++ /dev/null
-from ..._utils import _ignore_deprecated
-from ...compat import *
-from ...compat.fhdl import verilog
-
-
-class SimCase:
- def setUp(self, *args, **kwargs):
- with _ignore_deprecated():
- self.tb = self.TestBench(*args, **kwargs)
-
- def test_to_verilog(self):
- verilog.convert(self.tb)
-
- def run_with(self, generator):
- with _ignore_deprecated():
- run_simulation(self.tb, generator)
+++ /dev/null
-# nmigen: UnusedElaboratable=no
-
-import unittest
-
-from ...compat import *
-from ...compat.genlib.coding import *
-
-from .support import SimCase
-
-
-class EncCase(SimCase, unittest.TestCase):
- class TestBench(Module):
- def __init__(self):
- self.submodules.dut = Encoder(8)
-
- def test_sizes(self):
- self.assertEqual(len(self.tb.dut.i), 8)
- self.assertEqual(len(self.tb.dut.o), 3)
- self.assertEqual(len(self.tb.dut.n), 1)
-
- def test_run_sequence(self):
- seq = list(range(1<<8))
- def gen():
- for _ in range(256):
- if seq:
- yield self.tb.dut.i.eq(seq.pop(0))
- yield
- if (yield self.tb.dut.n):
- self.assertNotIn((yield self.tb.dut.i), [1<<i for i in range(8)])
- else:
- self.assertEqual((yield self.tb.dut.i), 1<<(yield self.tb.dut.o))
- self.run_with(gen())
-
-
-class PrioEncCase(SimCase, unittest.TestCase):
- class TestBench(Module):
- def __init__(self):
- self.submodules.dut = PriorityEncoder(8)
-
- def test_sizes(self):
- self.assertEqual(len(self.tb.dut.i), 8)
- self.assertEqual(len(self.tb.dut.o), 3)
- self.assertEqual(len(self.tb.dut.n), 1)
-
- def test_run_sequence(self):
- seq = list(range(1<<8))
- def gen():
- for _ in range(256):
- if seq:
- yield self.tb.dut.i.eq(seq.pop(0))
- yield
- i = yield self.tb.dut.i
- if (yield self.tb.dut.n):
- self.assertEqual(i, 0)
- else:
- o = yield self.tb.dut.o
- if o > 0:
- self.assertEqual(i & 1<<(o - 1), 0)
- self.assertGreaterEqual(i, 1<<o)
- self.run_with(gen())
-
-
-class DecCase(SimCase, unittest.TestCase):
- class TestBench(Module):
- def __init__(self):
- self.submodules.dut = Decoder(8)
-
- def test_sizes(self):
- self.assertEqual(len(self.tb.dut.i), 3)
- self.assertEqual(len(self.tb.dut.o), 8)
- self.assertEqual(len(self.tb.dut.n), 1)
-
- def test_run_sequence(self):
- seq = list(range(8*2))
- def gen():
- for _ in range(256):
- if seq:
- i = seq.pop()
- yield self.tb.dut.i.eq(i//2)
- yield self.tb.dut.n.eq(i%2)
- yield
- i = yield self.tb.dut.i
- o = yield self.tb.dut.o
- if (yield self.tb.dut.n):
- self.assertEqual(o, 0)
- else:
- self.assertEqual(o, 1<<i)
- self.run_with(gen())
-
-
-class SmallPrioEncCase(SimCase, unittest.TestCase):
- class TestBench(Module):
- def __init__(self):
- self.submodules.dut = PriorityEncoder(1)
-
- def test_sizes(self):
- self.assertEqual(len(self.tb.dut.i), 1)
- self.assertEqual(len(self.tb.dut.o), 1)
- self.assertEqual(len(self.tb.dut.n), 1)
-
- def test_run_sequence(self):
- seq = list(range(1))
- def gen():
- for _ in range(5):
- if seq:
- yield self.tb.dut.i.eq(seq.pop(0))
- yield
- i = yield self.tb.dut.i
- if (yield self.tb.dut.n):
- self.assertEqual(i, 0)
- else:
- o = yield self.tb.dut.o
- if o > 0:
- self.assertEqual(i & 1<<(o - 1), 0)
- self.assertGreaterEqual(i, 1<<o)
- self.run_with(gen())
+++ /dev/null
-import unittest
-
-from ...compat import *
-from .support import SimCase
-
-
-class ConstantCase(SimCase, unittest.TestCase):
- class TestBench(Module):
- def __init__(self):
- self.sigs = [
- (Signal(3), Constant(0), 0),
- (Signal(3), Constant(5), 5),
- (Signal(3), Constant(1, 2), 1),
- (Signal(3), Constant(-1, 7), 7),
- (Signal(3), Constant(0b10101)[:3], 0b101),
- (Signal(3), Constant(0b10101)[1:4], 0b10),
- (Signal(4), Constant(0b1100)[::-1], 0b0011),
- ]
- self.comb += [a.eq(b) for a, b, c in self.sigs]
-
- def test_comparisons(self):
- def gen():
- for s, l, v in self.tb.sigs:
- s = yield s
- self.assertEqual(
- s, int(v),
- "got {}, want {} from literal {}".format(
- s, v, l))
- self.run_with(gen())
+++ /dev/null
-import unittest
-from itertools import count
-
-from ...compat import *
-from ...compat.genlib.fifo import SyncFIFO
-
-from .support import SimCase
-
-
-class SyncFIFOCase(SimCase, unittest.TestCase):
- class TestBench(Module):
- def __init__(self):
- self.submodules.dut = SyncFIFO(64, 2)
-
- self.sync += [
- If(self.dut.we & self.dut.writable,
- self.dut.din[:32].eq(self.dut.din[:32] + 1),
- self.dut.din[32:].eq(self.dut.din[32:] + 2)
- )
- ]
-
- def test_run_sequence(self):
- seq = list(range(20))
- def gen():
- for cycle in count():
- # fire re and we at "random"
- yield self.tb.dut.we.eq(cycle % 2 == 0)
- yield self.tb.dut.re.eq(cycle % 3 == 0)
- # the output if valid must be correct
- if (yield self.tb.dut.readable) and (yield self.tb.dut.re):
- try:
- i = seq.pop(0)
- except IndexError:
- break
- self.assertEqual((yield self.tb.dut.dout[:32]), i)
- self.assertEqual((yield self.tb.dut.dout[32:]), i*2)
- yield
- self.run_with(gen())
+++ /dev/null
-import unittest
-from itertools import count
-
-from ...compat import *
-from ...compat.genlib.fsm import FSM
-
-from .support import SimCase
-
-
-class FSMCase(SimCase, unittest.TestCase):
- class TestBench(Module):
- def __init__(self):
- self.ctrl = Signal()
- self.data = Signal()
- self.status = Signal(8)
-
- self.submodules.dut = FSM()
- self.dut.act("IDLE",
- If(self.ctrl,
- NextState("START")
- )
- )
- self.dut.act("START",
- If(self.data,
- NextState("SET-STATUS-LOW")
- ).Else(
- NextState("SET-STATUS")
- )
- )
- self.dut.act("SET-STATUS",
- NextValue(self.status, 0xaa),
- NextState("IDLE")
- )
- self.dut.act("SET-STATUS-LOW",
- NextValue(self.status[:4], 0xb),
- NextState("IDLE")
- )
-
- def assertState(self, fsm, state):
- self.assertEqual(fsm.decoding[(yield fsm.state)], state)
-
- def test_next_state(self):
- def gen():
- yield from self.assertState(self.tb.dut, "IDLE")
- yield
- yield from self.assertState(self.tb.dut, "IDLE")
- yield self.tb.ctrl.eq(1)
- yield
- yield from self.assertState(self.tb.dut, "IDLE")
- yield self.tb.ctrl.eq(0)
- yield
- yield from self.assertState(self.tb.dut, "START")
- yield
- yield from self.assertState(self.tb.dut, "SET-STATUS")
- yield self.tb.ctrl.eq(1)
- yield
- yield from self.assertState(self.tb.dut, "IDLE")
- yield self.tb.ctrl.eq(0)
- yield self.tb.data.eq(1)
- yield
- yield from self.assertState(self.tb.dut, "START")
- yield self.tb.data.eq(0)
- yield
- yield from self.assertState(self.tb.dut, "SET-STATUS-LOW")
- self.run_with(gen())
-
- def test_next_value(self):
- def gen():
- self.assertEqual((yield self.tb.status), 0x00)
- yield self.tb.ctrl.eq(1)
- yield
- yield self.tb.ctrl.eq(0)
- yield
- yield
- yield from self.assertState(self.tb.dut, "SET-STATUS")
- yield self.tb.ctrl.eq(1)
- yield
- self.assertEqual((yield self.tb.status), 0xaa)
- yield self.tb.ctrl.eq(0)
- yield self.tb.data.eq(1)
- yield
- yield self.tb.data.eq(0)
- yield
- yield from self.assertState(self.tb.dut, "SET-STATUS-LOW")
- yield
- self.assertEqual((yield self.tb.status), 0xab)
- self.run_with(gen())
+++ /dev/null
-import unittest
-
-from ...compat import *
-
-
-class PassiveCase(unittest.TestCase):
- def test_terminates_correctly(self):
- n = 5
-
- count = 0
- @passive
- def counter():
- nonlocal count
- while True:
- yield
- count += 1
-
- def terminator():
- for i in range(n):
- yield
-
- run_simulation(Module(), [counter(), terminator()])
- self.assertEqual(count, n)
+++ /dev/null
-import unittest
-from ... import Signal, Module, Elaboratable
-from .support import SimCase
-
-
-class RunSimulation(SimCase, unittest.TestCase):
- """ test for https://github.com/nmigen/nmigen/issues/344 """
-
- class TestBench(Elaboratable):
- def __init__(self):
- self.a = Signal()
-
- def elaborate(self, platform):
- m = Module()
- m.d.sync += self.a.eq(~self.a)
- return m
-
- def test_run_simulation(self):
- def gen():
- yield
- for i in range(10):
- yield
- a = (yield self.tb.a)
- self.assertEqual(a, i % 2)
-
- self.run_with(gen())
+++ /dev/null
-import unittest
-
-from ...compat import *
-from .support import SimCase
-
-
-class SignedCase(SimCase, unittest.TestCase):
- class TestBench(Module):
- def __init__(self):
- self.a = Signal((3, True))
- self.b = Signal((4, True))
- comps = [
- lambda p, q: p > q,
- lambda p, q: p >= q,
- lambda p, q: p < q,
- lambda p, q: p <= q,
- lambda p, q: p == q,
- lambda p, q: p != q,
- ]
- self.vals = []
- for asign in 1, -1:
- for bsign in 1, -1:
- for f in comps:
- r = Signal()
- r0 = f(asign*self.a, bsign*self.b)
- self.comb += r.eq(r0)
- self.vals.append((asign, bsign, f, r, r0.op))
-
- def test_comparisons(self):
- def gen():
- for i in range(-4, 4):
- yield self.tb.a.eq(i)
- yield self.tb.b.eq(i)
- yield
- a = yield self.tb.a
- b = yield self.tb.b
- for asign, bsign, f, r, op in self.tb.vals:
- r, r0 = (yield r), f(asign*a, bsign*b)
- self.assertEqual(r, int(r0),
- "got {}, want {}*{} {} {}*{} = {}".format(
- r, asign, a, op, bsign, b, r0))
- self.run_with(gen())
+++ /dev/null
-import unittest
-
-from ..._utils import _ignore_deprecated
-from ...compat import *
-
-
-def _same_slices(a, b):
- return a.value is b.value and a.start == b.start and a.stop == b.stop
-
-
-class SignalSizeCase(unittest.TestCase):
- def setUp(self):
- self.i = C(0xaa)
- self.j = C(-127)
- with _ignore_deprecated():
- self.s = Signal((13, True))
-
- def test_len(self):
- self.assertEqual(len(self.s), 13)
- self.assertEqual(len(self.i), 8)
- self.assertEqual(len(self.j), 8)
+++ /dev/null
-from collections import OrderedDict
-
-from ..build.dsl import *
-from .utils import *
-
-
-class PinsTestCase(FHDLTestCase):
- def test_basic(self):
- p = Pins("A0 A1 A2")
- self.assertEqual(repr(p), "(pins io A0 A1 A2)")
- self.assertEqual(len(p.names), 3)
- self.assertEqual(p.dir, "io")
- self.assertEqual(p.invert, False)
- self.assertEqual(list(p), ["A0", "A1", "A2"])
-
- def test_invert(self):
- p = PinsN("A0")
- self.assertEqual(repr(p), "(pins-n io A0)")
- self.assertEqual(p.invert, True)
-
- def test_invert_arg(self):
- p = Pins("A0", invert=True)
- self.assertEqual(p.invert, True)
-
- def test_conn(self):
- p = Pins("0 1 2", conn=("pmod", 0))
- self.assertEqual(list(p), ["pmod_0:0", "pmod_0:1", "pmod_0:2"])
- p = Pins("0 1 2", conn=("pmod", "a"))
- self.assertEqual(list(p), ["pmod_a:0", "pmod_a:1", "pmod_a:2"])
-
- def test_map_names(self):
- p = Pins("0 1 2", conn=("pmod", 0))
- mapping = {
- "pmod_0:0": "A0",
- "pmod_0:1": "A1",
- "pmod_0:2": "A2",
- }
- self.assertEqual(p.map_names(mapping, p), ["A0", "A1", "A2"])
-
- def test_map_names_recur(self):
- p = Pins("0", conn=("pmod", 0))
- mapping = {
- "pmod_0:0": "ext_0:1",
- "ext_0:1": "A1",
- }
- self.assertEqual(p.map_names(mapping, p), ["A1"])
-
- def test_wrong_names(self):
- with self.assertRaisesRegex(TypeError,
- r"^Names must be a whitespace-separated string, not \['A0', 'A1', 'A2'\]$"):
- p = Pins(["A0", "A1", "A2"])
-
- def test_wrong_dir(self):
- with self.assertRaisesRegex(TypeError,
- r"^Direction must be one of \"i\", \"o\", \"oe\", or \"io\", not 'wrong'$"):
- p = Pins("A0 A1", dir="wrong")
-
- def test_wrong_conn(self):
- with self.assertRaisesRegex(TypeError,
- (r"^Connector must be None or a pair of string \(connector name\) and "
- r"integer\/string \(connector number\), not \('foo', None\)$")):
- p = Pins("A0 A1", conn=("foo", None))
-
- def test_wrong_map_names(self):
- p = Pins("0 1 2", conn=("pmod", 0))
- mapping = {
- "pmod_0:0": "A0",
- }
- with self.assertRaisesRegex(NameError,
- (r"^Resource \(pins io pmod_0:0 pmod_0:1 pmod_0:2\) refers to nonexistent "
- r"connector pin pmod_0:1$")):
- p.map_names(mapping, p)
-
- def test_wrong_assert_width(self):
- with self.assertRaisesRegex(AssertionError,
- r"^3 names are specified \(0 1 2\), but 4 names are expected$"):
- Pins("0 1 2", assert_width=4)
-
-
-class DiffPairsTestCase(FHDLTestCase):
- def test_basic(self):
- dp = DiffPairs(p="A0 A1", n="B0 B1")
- self.assertEqual(repr(dp), "(diffpairs io (p A0 A1) (n B0 B1))")
- self.assertEqual(dp.p.names, ["A0", "A1"])
- self.assertEqual(dp.n.names, ["B0", "B1"])
- self.assertEqual(dp.dir, "io")
- self.assertEqual(list(dp), [("A0", "B0"), ("A1", "B1")])
-
- def test_invert(self):
- dp = DiffPairsN(p="A0", n="B0")
- self.assertEqual(repr(dp), "(diffpairs-n io (p A0) (n B0))")
- self.assertEqual(dp.p.names, ["A0"])
- self.assertEqual(dp.n.names, ["B0"])
- self.assertEqual(dp.invert, True)
-
- def test_conn(self):
- dp = DiffPairs(p="0 1 2", n="3 4 5", conn=("pmod", 0))
- self.assertEqual(list(dp), [
- ("pmod_0:0", "pmod_0:3"),
- ("pmod_0:1", "pmod_0:4"),
- ("pmod_0:2", "pmod_0:5"),
- ])
-
- def test_dir(self):
- dp = DiffPairs("A0", "B0", dir="o")
- self.assertEqual(dp.dir, "o")
- self.assertEqual(dp.p.dir, "o")
- self.assertEqual(dp.n.dir, "o")
-
- def test_wrong_width(self):
- with self.assertRaisesRegex(TypeError,
- (r"^Positive and negative pins must have the same width, but \(pins io A0\) "
- r"and \(pins io B0 B1\) do not$")):
- dp = DiffPairs("A0", "B0 B1")
-
- def test_wrong_assert_width(self):
- with self.assertRaisesRegex(AssertionError,
- r"^3 names are specified \(0 1 2\), but 4 names are expected$"):
- DiffPairs("0 1 2", "3 4 5", assert_width=4)
-
-
-class AttrsTestCase(FHDLTestCase):
- def test_basic(self):
- a = Attrs(IO_STANDARD="LVCMOS33", PULLUP=1)
- self.assertEqual(a["IO_STANDARD"], "LVCMOS33")
- self.assertEqual(repr(a), "(attrs IO_STANDARD='LVCMOS33' PULLUP=1)")
-
- def test_remove(self):
- a = Attrs(FOO=None)
- self.assertEqual(a["FOO"], None)
- self.assertEqual(repr(a), "(attrs !FOO)")
-
- def test_callable(self):
- fn = lambda self: "FOO"
- a = Attrs(FOO=fn)
- self.assertEqual(a["FOO"], fn)
- self.assertEqual(repr(a), "(attrs FOO={!r})".format(fn))
-
- def test_wrong_value(self):
- with self.assertRaisesRegex(TypeError,
- r"^Value of attribute FOO must be None, int, str, or callable, not 1\.0$"):
- a = Attrs(FOO=1.0)
-
-
-class ClockTestCase(FHDLTestCase):
- def test_basic(self):
- c = Clock(1_000_000)
- self.assertEqual(c.frequency, 1e6)
- self.assertEqual(c.period, 1e-6)
- self.assertEqual(repr(c), "(clock 1000000.0)")
-
-
-class SubsignalTestCase(FHDLTestCase):
- def test_basic_pins(self):
- s = Subsignal("a", Pins("A0"), Attrs(IOSTANDARD="LVCMOS33"))
- self.assertEqual(repr(s),
- "(subsignal a (pins io A0) (attrs IOSTANDARD='LVCMOS33'))")
-
- def test_basic_diffpairs(self):
- s = Subsignal("a", DiffPairs("A0", "B0"))
- self.assertEqual(repr(s),
- "(subsignal a (diffpairs io (p A0) (n B0)))")
-
- def test_basic_subsignals(self):
- s = Subsignal("a",
- Subsignal("b", Pins("A0")),
- Subsignal("c", Pins("A1")))
- self.assertEqual(repr(s),
- "(subsignal a (subsignal b (pins io A0)) "
- "(subsignal c (pins io A1)))")
-
- def test_attrs(self):
- s = Subsignal("a",
- Subsignal("b", Pins("A0")),
- Subsignal("c", Pins("A0"), Attrs(SLEW="FAST")),
- Attrs(IOSTANDARD="LVCMOS33"))
- self.assertEqual(s.attrs, {"IOSTANDARD": "LVCMOS33"})
- self.assertEqual(s.ios[0].attrs, {})
- self.assertEqual(s.ios[1].attrs, {"SLEW": "FAST"})
-
- def test_attrs_many(self):
- s = Subsignal("a", Pins("A0"), Attrs(SLEW="FAST"), Attrs(PULLUP="1"))
- self.assertEqual(s.attrs, {"SLEW": "FAST", "PULLUP": "1"})
-
- def test_clock(self):
- s = Subsignal("a", Pins("A0"), Clock(1e6))
- self.assertEqual(s.clock.frequency, 1e6)
-
- def test_wrong_empty_io(self):
- with self.assertRaisesRegex(ValueError, r"^Missing I\/O constraints$"):
- s = Subsignal("a")
-
- def test_wrong_io(self):
- with self.assertRaisesRegex(TypeError,
- (r"^Constraint must be one of Pins, DiffPairs, Subsignal, Attrs, or Clock, "
- r"not 'wrong'$")):
- s = Subsignal("a", "wrong")
-
- def test_wrong_pins(self):
- with self.assertRaisesRegex(TypeError,
- (r"^Pins and DiffPairs are incompatible with other location or subsignal "
- r"constraints, but \(pins io A1\) appears after \(pins io A0\)$")):
- s = Subsignal("a", Pins("A0"), Pins("A1"))
-
- def test_wrong_diffpairs(self):
- with self.assertRaisesRegex(TypeError,
- (r"^Pins and DiffPairs are incompatible with other location or subsignal "
- r"constraints, but \(pins io A1\) appears after \(diffpairs io \(p A0\) \(n B0\)\)$")):
- s = Subsignal("a", DiffPairs("A0", "B0"), Pins("A1"))
-
- def test_wrong_subsignals(self):
- with self.assertRaisesRegex(TypeError,
- (r"^Pins and DiffPairs are incompatible with other location or subsignal "
- r"constraints, but \(pins io B0\) appears after \(subsignal b \(pins io A0\)\)$")):
- s = Subsignal("a", Subsignal("b", Pins("A0")), Pins("B0"))
-
- def test_wrong_clock(self):
- with self.assertRaisesRegex(TypeError,
- (r"^Clock constraint can only be applied to Pins or DiffPairs, not "
- r"\(subsignal b \(pins io A0\)\)$")):
- s = Subsignal("a", Subsignal("b", Pins("A0")), Clock(1e6))
-
- def test_wrong_clock_many(self):
- with self.assertRaisesRegex(ValueError,
- r"^Clock constraint can be applied only once$"):
- s = Subsignal("a", Pins("A0"), Clock(1e6), Clock(1e7))
-
-
-class ResourceTestCase(FHDLTestCase):
- def test_basic(self):
- r = Resource("serial", 0,
- Subsignal("tx", Pins("A0", dir="o")),
- Subsignal("rx", Pins("A1", dir="i")),
- Attrs(IOSTANDARD="LVCMOS33"))
- self.assertEqual(repr(r), "(resource serial 0"
- " (subsignal tx (pins o A0))"
- " (subsignal rx (pins i A1))"
- " (attrs IOSTANDARD='LVCMOS33'))")
-
- def test_family(self):
- ios = [Subsignal("clk", Pins("A0", dir="o"))]
- r1 = Resource.family(0, default_name="spi", ios=ios)
- r2 = Resource.family("spi_flash", 0, default_name="spi", ios=ios)
- r3 = Resource.family("spi_flash", 0, default_name="spi", ios=ios, name_suffix="4x")
- r4 = Resource.family(0, default_name="spi", ios=ios, name_suffix="2x")
- self.assertEqual(r1.name, "spi")
- self.assertEqual(r1.ios, ios)
- self.assertEqual(r2.name, "spi_flash")
- self.assertEqual(r2.ios, ios)
- self.assertEqual(r3.name, "spi_flash_4x")
- self.assertEqual(r3.ios, ios)
- self.assertEqual(r4.name, "spi_2x")
- self.assertEqual(r4.ios, ios)
-
-
-class ConnectorTestCase(FHDLTestCase):
- def test_string(self):
- c = Connector("pmod", 0, "A0 A1 A2 A3 - - A4 A5 A6 A7 - -")
- self.assertEqual(c.name, "pmod")
- self.assertEqual(c.number, 0)
- self.assertEqual(c.mapping, OrderedDict([
- ("1", "A0"),
- ("2", "A1"),
- ("3", "A2"),
- ("4", "A3"),
- ("7", "A4"),
- ("8", "A5"),
- ("9", "A6"),
- ("10", "A7"),
- ]))
- self.assertEqual(list(c), [
- ("pmod_0:1", "A0"),
- ("pmod_0:2", "A1"),
- ("pmod_0:3", "A2"),
- ("pmod_0:4", "A3"),
- ("pmod_0:7", "A4"),
- ("pmod_0:8", "A5"),
- ("pmod_0:9", "A6"),
- ("pmod_0:10", "A7"),
- ])
- self.assertEqual(repr(c),
- "(connector pmod 0 1=>A0 2=>A1 3=>A2 4=>A3 7=>A4 8=>A5 9=>A6 10=>A7)")
-
- def test_dict(self):
- c = Connector("ext", 1, {"DP0": "A0", "DP1": "A1"})
- self.assertEqual(c.name, "ext")
- self.assertEqual(c.number, 1)
- self.assertEqual(c.mapping, OrderedDict([
- ("DP0", "A0"),
- ("DP1", "A1"),
- ]))
-
- def test_conn(self):
- c = Connector("pmod", 0, "0 1 2 3 - - 4 5 6 7 - -", conn=("expansion", 0))
- self.assertEqual(c.mapping, OrderedDict([
- ("1", "expansion_0:0"),
- ("2", "expansion_0:1"),
- ("3", "expansion_0:2"),
- ("4", "expansion_0:3"),
- ("7", "expansion_0:4"),
- ("8", "expansion_0:5"),
- ("9", "expansion_0:6"),
- ("10", "expansion_0:7"),
- ]))
-
- def test_str_name(self):
- c = Connector("ext", "A", "0 1 2")
- self.assertEqual(c.name, "ext")
- self.assertEqual(c.number, "A")
-
- def test_conn_wrong_name(self):
- with self.assertRaisesRegex(TypeError,
- (r"^Connector must be None or a pair of string \(connector name\) and "
- r"integer\/string \(connector number\), not \('foo', None\)$")):
- Connector("ext", "A", "0 1 2", conn=("foo", None))
-
- def test_wrong_io(self):
- with self.assertRaisesRegex(TypeError,
- r"^Connector I\/Os must be a dictionary or a string, not \[\]$"):
- Connector("pmod", 0, [])
-
- def test_wrong_dict_key_value(self):
- with self.assertRaisesRegex(TypeError,
- r"^Connector pin name must be a string, not 0$"):
- Connector("pmod", 0, {0: "A"})
- with self.assertRaisesRegex(TypeError,
- r"^Platform pin name must be a string, not 0$"):
- Connector("pmod", 0, {"A": 0})
+++ /dev/null
-from .. import *
-from ..build.plat import *
-from .utils import *
-
-
-class MockPlatform(Platform):
- resources = []
- connectors = []
-
- required_tools = []
-
- def toolchain_prepare(self, fragment, name, **kwargs):
- raise NotImplementedError
-
-
-class PlatformTestCase(FHDLTestCase):
- def setUp(self):
- self.platform = MockPlatform()
-
- def test_add_file_str(self):
- self.platform.add_file("x.txt", "foo")
- self.assertEqual(self.platform.extra_files["x.txt"], "foo")
-
- def test_add_file_bytes(self):
- self.platform.add_file("x.txt", b"foo")
- self.assertEqual(self.platform.extra_files["x.txt"], b"foo")
-
- def test_add_file_exact_duplicate(self):
- self.platform.add_file("x.txt", b"foo")
- self.platform.add_file("x.txt", b"foo")
-
- def test_add_file_io(self):
- with open(__file__) as f:
- self.platform.add_file("x.txt", f)
- with open(__file__) as f:
- self.assertEqual(self.platform.extra_files["x.txt"], f.read())
-
- def test_add_file_wrong_filename(self):
- with self.assertRaisesRegex(TypeError,
- r"^File name must be a string, not 1$"):
- self.platform.add_file(1, "")
-
- def test_add_file_wrong_contents(self):
- with self.assertRaisesRegex(TypeError,
- r"^File contents must be str, bytes, or a file-like object, not 1$"):
- self.platform.add_file("foo", 1)
-
- def test_add_file_wrong_duplicate(self):
- self.platform.add_file("foo", "")
- with self.assertRaisesRegex(ValueError,
- r"^File 'foo' already exists$"):
- self.platform.add_file("foo", "bar")
+++ /dev/null
-# nmigen: UnusedElaboratable=no
-
-from .. import *
-from ..hdl.rec import *
-from ..lib.io import *
-from ..build.dsl import *
-from ..build.res import *
-from .utils import *
-
-
-class ResourceManagerTestCase(FHDLTestCase):
- def setUp(self):
- self.resources = [
- Resource("clk100", 0, DiffPairs("H1", "H2", dir="i"), Clock(100e6)),
- Resource("clk50", 0, Pins("K1"), Clock(50e6)),
- Resource("user_led", 0, Pins("A0", dir="o")),
- Resource("i2c", 0,
- Subsignal("scl", Pins("N10", dir="o")),
- Subsignal("sda", Pins("N11"))
- )
- ]
- self.connectors = [
- Connector("pmod", 0, "B0 B1 B2 B3 - -"),
- ]
- self.cm = ResourceManager(self.resources, self.connectors)
-
- def test_basic(self):
- self.cm = ResourceManager(self.resources, self.connectors)
- self.assertEqual(self.cm.resources, {
- ("clk100", 0): self.resources[0],
- ("clk50", 0): self.resources[1],
- ("user_led", 0): self.resources[2],
- ("i2c", 0): self.resources[3]
- })
- self.assertEqual(self.cm.connectors, {
- ("pmod", 0): self.connectors[0],
- })
-
- def test_add_resources(self):
- new_resources = [
- Resource("user_led", 1, Pins("A1", dir="o"))
- ]
- self.cm.add_resources(new_resources)
- self.assertEqual(self.cm.resources, {
- ("clk100", 0): self.resources[0],
- ("clk50", 0): self.resources[1],
- ("user_led", 0): self.resources[2],
- ("i2c", 0): self.resources[3],
- ("user_led", 1): new_resources[0]
- })
-
- def test_lookup(self):
- r = self.cm.lookup("user_led", 0)
- self.assertIs(r, self.cm.resources["user_led", 0])
-
- def test_request_basic(self):
- r = self.cm.lookup("user_led", 0)
- user_led = self.cm.request("user_led", 0)
-
- self.assertIsInstance(user_led, Pin)
- self.assertEqual(user_led.name, "user_led_0")
- self.assertEqual(user_led.width, 1)
- self.assertEqual(user_led.dir, "o")
-
- ports = list(self.cm.iter_ports())
- self.assertEqual(len(ports), 1)
-
- self.assertEqual(list(self.cm.iter_port_constraints()), [
- ("user_led_0__io", ["A0"], {})
- ])
-
- def test_request_with_dir(self):
- i2c = self.cm.request("i2c", 0, dir={"sda": "o"})
- self.assertIsInstance(i2c, Record)
- self.assertIsInstance(i2c.sda, Pin)
- self.assertEqual(i2c.sda.dir, "o")
-
- def test_request_tristate(self):
- i2c = self.cm.request("i2c", 0)
- self.assertEqual(i2c.sda.dir, "io")
-
- ports = list(self.cm.iter_ports())
- self.assertEqual(len(ports), 2)
- scl, sda = ports
- self.assertEqual(ports[1].name, "i2c_0__sda__io")
- self.assertEqual(ports[1].width, 1)
-
- scl_info, sda_info = self.cm.iter_single_ended_pins()
- self.assertIs(scl_info[0], i2c.scl)
- self.assertIs(scl_info[1].io, scl)
- self.assertEqual(scl_info[2], {})
- self.assertEqual(scl_info[3], False)
- self.assertIs(sda_info[0], i2c.sda)
- self.assertIs(sda_info[1].io, sda)
-
- self.assertEqual(list(self.cm.iter_port_constraints()), [
- ("i2c_0__scl__io", ["N10"], {}),
- ("i2c_0__sda__io", ["N11"], {})
- ])
-
- def test_request_diffpairs(self):
- clk100 = self.cm.request("clk100", 0)
- self.assertIsInstance(clk100, Pin)
- self.assertEqual(clk100.dir, "i")
- self.assertEqual(clk100.width, 1)
-
- ports = list(self.cm.iter_ports())
- self.assertEqual(len(ports), 2)
- p, n = ports
- self.assertEqual(p.name, "clk100_0__p")
- self.assertEqual(p.width, clk100.width)
- self.assertEqual(n.name, "clk100_0__n")
- self.assertEqual(n.width, clk100.width)
-
- clk100_info, = self.cm.iter_differential_pins()
- self.assertIs(clk100_info[0], clk100)
- self.assertIs(clk100_info[1].p, p)
- self.assertIs(clk100_info[1].n, n)
- self.assertEqual(clk100_info[2], {})
- self.assertEqual(clk100_info[3], False)
-
- self.assertEqual(list(self.cm.iter_port_constraints()), [
- ("clk100_0__p", ["H1"], {}),
- ("clk100_0__n", ["H2"], {}),
- ])
-
- def test_request_inverted(self):
- new_resources = [
- Resource("cs", 0, PinsN("X0")),
- Resource("clk", 0, DiffPairsN("Y0", "Y1")),
- ]
- self.cm.add_resources(new_resources)
-
- cs = self.cm.request("cs")
- clk = self.cm.request("clk")
- cs_io, clk_p, clk_n = self.cm.iter_ports()
-
- cs_info, = self.cm.iter_single_ended_pins()
- self.assertIs(cs_info[0], cs)
- self.assertIs(cs_info[1].io, cs_io)
- self.assertEqual(cs_info[2], {})
- self.assertEqual(cs_info[3], True)
-
- clk_info, = self.cm.iter_differential_pins()
- self.assertIs(clk_info[0], clk)
- self.assertIs(clk_info[1].p, clk_p)
- self.assertIs(clk_info[1].n, clk_n)
- self.assertEqual(clk_info[2], {})
- self.assertEqual(clk_info[3], True)
-
- def test_request_raw(self):
- clk50 = self.cm.request("clk50", 0, dir="-")
- self.assertIsInstance(clk50, Record)
- self.assertIsInstance(clk50.io, Signal)
-
- ports = list(self.cm.iter_ports())
- self.assertEqual(len(ports), 1)
- self.assertIs(ports[0], clk50.io)
-
- def test_request_raw_diffpairs(self):
- clk100 = self.cm.request("clk100", 0, dir="-")
- self.assertIsInstance(clk100, Record)
- self.assertIsInstance(clk100.p, Signal)
- self.assertIsInstance(clk100.n, Signal)
-
- ports = list(self.cm.iter_ports())
- self.assertEqual(len(ports), 2)
- self.assertIs(ports[0], clk100.p)
- self.assertIs(ports[1], clk100.n)
-
- def test_request_via_connector(self):
- self.cm.add_resources([
- Resource("spi", 0,
- Subsignal("ss", Pins("1", conn=("pmod", 0))),
- Subsignal("clk", Pins("2", conn=("pmod", 0))),
- Subsignal("miso", Pins("3", conn=("pmod", 0))),
- Subsignal("mosi", Pins("4", conn=("pmod", 0))),
- )
- ])
- spi0 = self.cm.request("spi", 0)
- self.assertEqual(list(self.cm.iter_port_constraints()), [
- ("spi_0__ss__io", ["B0"], {}),
- ("spi_0__clk__io", ["B1"], {}),
- ("spi_0__miso__io", ["B2"], {}),
- ("spi_0__mosi__io", ["B3"], {}),
- ])
-
- def test_request_via_nested_connector(self):
- new_connectors = [
- Connector("pmod_extension", 0, "1 2 3 4 - -", conn=("pmod", 0)),
- ]
- self.cm.add_connectors(new_connectors)
- self.cm.add_resources([
- Resource("spi", 0,
- Subsignal("ss", Pins("1", conn=("pmod_extension", 0))),
- Subsignal("clk", Pins("2", conn=("pmod_extension", 0))),
- Subsignal("miso", Pins("3", conn=("pmod_extension", 0))),
- Subsignal("mosi", Pins("4", conn=("pmod_extension", 0))),
- )
- ])
- spi0 = self.cm.request("spi", 0)
- self.assertEqual(list(self.cm.iter_port_constraints()), [
- ("spi_0__ss__io", ["B0"], {}),
- ("spi_0__clk__io", ["B1"], {}),
- ("spi_0__miso__io", ["B2"], {}),
- ("spi_0__mosi__io", ["B3"], {}),
- ])
-
- def test_request_clock(self):
- clk100 = self.cm.request("clk100", 0)
- clk50 = self.cm.request("clk50", 0, dir="i")
- clk100_port_p, clk100_port_n, clk50_port = self.cm.iter_ports()
- self.assertEqual(list(self.cm.iter_clock_constraints()), [
- (clk100.i, clk100_port_p, 100e6),
- (clk50.i, clk50_port, 50e6)
- ])
-
- def test_add_clock(self):
- i2c = self.cm.request("i2c")
- self.cm.add_clock_constraint(i2c.scl.o, 100e3)
- self.assertEqual(list(self.cm.iter_clock_constraints()), [
- (i2c.scl.o, None, 100e3)
- ])
-
- def test_wrong_resources(self):
- with self.assertRaisesRegex(TypeError, r"^Object 'wrong' is not a Resource$"):
- self.cm.add_resources(['wrong'])
-
- def test_wrong_resources_duplicate(self):
- with self.assertRaisesRegex(NameError,
- (r"^Trying to add \(resource user_led 0 \(pins o A1\)\), but "
- r"\(resource user_led 0 \(pins o A0\)\) has the same name and number$")):
- self.cm.add_resources([Resource("user_led", 0, Pins("A1", dir="o"))])
-
- def test_wrong_connectors(self):
- with self.assertRaisesRegex(TypeError, r"^Object 'wrong' is not a Connector$"):
- self.cm.add_connectors(['wrong'])
-
- def test_wrong_connectors_duplicate(self):
- with self.assertRaisesRegex(NameError,
- (r"^Trying to add \(connector pmod 0 1=>1 2=>2\), but "
- r"\(connector pmod 0 1=>B0 2=>B1 3=>B2 4=>B3\) has the same name and number$")):
- self.cm.add_connectors([Connector("pmod", 0, "1 2")])
-
- def test_wrong_lookup(self):
- with self.assertRaisesRegex(ResourceError,
- r"^Resource user_led#1 does not exist$"):
- r = self.cm.lookup("user_led", 1)
-
- def test_wrong_clock_signal(self):
- with self.assertRaisesRegex(TypeError,
- r"^Object None is not a Signal$"):
- self.cm.add_clock_constraint(None, 10e6)
-
- def test_wrong_clock_frequency(self):
- with self.assertRaisesRegex(TypeError,
- r"^Frequency must be a number, not None$"):
- self.cm.add_clock_constraint(Signal(), None)
-
- def test_wrong_request_duplicate(self):
- with self.assertRaisesRegex(ResourceError,
- r"^Resource user_led#0 has already been requested$"):
- self.cm.request("user_led", 0)
- self.cm.request("user_led", 0)
-
- def test_wrong_request_duplicate_physical(self):
- self.cm.add_resources([
- Resource("clk20", 0, Pins("H1", dir="i")),
- ])
- self.cm.request("clk100", 0)
- with self.assertRaisesRegex(ResourceError,
- (r"^Resource component clk20_0 uses physical pin H1, but it is already "
- r"used by resource component clk100_0 that was requested earlier$")):
- self.cm.request("clk20", 0)
-
- def test_wrong_request_with_dir(self):
- with self.assertRaisesRegex(TypeError,
- (r"^Direction must be one of \"i\", \"o\", \"oe\", \"io\", or \"-\", "
- r"not 'wrong'$")):
- user_led = self.cm.request("user_led", 0, dir="wrong")
-
- def test_wrong_request_with_dir_io(self):
- with self.assertRaisesRegex(ValueError,
- (r"^Direction of \(pins o A0\) cannot be changed from \"o\" to \"i\"; direction "
- r"can be changed from \"io\" to \"i\", \"o\", or \"oe\", or from anything "
- r"to \"-\"$")):
- user_led = self.cm.request("user_led", 0, dir="i")
-
- def test_wrong_request_with_dir_dict(self):
- with self.assertRaisesRegex(TypeError,
- (r"^Directions must be a dict, not 'i', because \(resource i2c 0 \(subsignal scl "
- r"\(pins o N10\)\) \(subsignal sda \(pins io N11\)\)\) "
- r"has subsignals$")):
- i2c = self.cm.request("i2c", 0, dir="i")
-
- def test_wrong_request_with_wrong_xdr(self):
- with self.assertRaisesRegex(ValueError,
- r"^Data rate of \(pins o A0\) must be a non-negative integer, not -1$"):
- user_led = self.cm.request("user_led", 0, xdr=-1)
-
- def test_wrong_request_with_xdr_dict(self):
- with self.assertRaisesRegex(TypeError,
- r"^Data rate must be a dict, not 2, because \(resource i2c 0 \(subsignal scl "
- r"\(pins o N10\)\) \(subsignal sda \(pins io N11\)\)\) "
- r"has subsignals$"):
- i2c = self.cm.request("i2c", 0, xdr=2)
-
- def test_wrong_clock_constraint_twice(self):
- clk100 = self.cm.request("clk100")
- with self.assertRaisesRegex(ValueError,
- (r"^Cannot add clock constraint on \(sig clk100_0__i\), which is already "
- r"constrained to 100000000\.0 Hz$")):
- self.cm.add_clock_constraint(clk100.i, 1e6)
+++ /dev/null
-from ..hdl.ir import Fragment
-from ..compat import *
-from .utils import *
-
-
-class CompatTestCase(FHDLTestCase):
- def test_fragment_get(self):
- m = Module()
- f = Fragment.get(m, platform=None)
+++ /dev/null
-import sys
-import subprocess
-from pathlib import Path
-
-from .utils import *
-
-
-def example_test(name):
- path = (Path(__file__).parent / ".." / ".." / "examples" / name).resolve()
- def test_function(self):
- subprocess.check_call([sys.executable, str(path), "generate", "-t", "v"],
- stdout=subprocess.DEVNULL)
- return test_function
-
-
-class ExamplesTestCase(FHDLTestCase):
- test_alu = example_test("basic/alu.py")
- test_alu_hier = example_test("basic/alu_hier.py")
- test_arst = example_test("basic/arst.py")
- test_cdc = example_test("basic/cdc.py")
- test_ctr = example_test("basic/ctr.py")
- test_ctr_en = example_test("basic/ctr_en.py")
- test_fsm = example_test("basic/fsm.py")
- test_gpio = example_test("basic/gpio.py")
- test_inst = example_test("basic/inst.py")
- test_mem = example_test("basic/mem.py")
- test_pmux = example_test("basic/pmux.py")
- test_por = example_test("basic/por.py")
-
- def test_uart(self):
- path = (Path(__file__).parent / ".." / ".." / "examples" / "basic" / "uart.py").resolve()
- def test_function(self):
- subprocess.check_call([sys.executable, str(path), "generate"],
- stdout=subprocess.DEVNULL)
+++ /dev/null
-import warnings
-from enum import Enum
-
-from ..hdl.ast import *
-from .utils import *
-
-
-class UnsignedEnum(Enum):
- FOO = 1
- BAR = 2
- BAZ = 3
-
-
-class SignedEnum(Enum):
- FOO = -1
- BAR = 0
- BAZ = +1
-
-
-class StringEnum(Enum):
- FOO = "a"
- BAR = "b"
-
-
-class ShapeTestCase(FHDLTestCase):
- def test_make(self):
- s1 = Shape()
- self.assertEqual(s1.width, 1)
- self.assertEqual(s1.signed, False)
- s2 = Shape(signed=True)
- self.assertEqual(s2.width, 1)
- self.assertEqual(s2.signed, True)
- s3 = Shape(3, True)
- self.assertEqual(s3.width, 3)
- self.assertEqual(s3.signed, True)
-
- def test_make_wrong(self):
- with self.assertRaisesRegex(TypeError,
- r"^Width must be a non-negative integer, not -1$"):
- Shape(-1)
-
- def test_compare_wrong(self):
- with self.assertRaisesRegex(TypeError,
- r"^Shapes may be compared with other Shapes and \(int, bool\) tuples, not 'hi'$"):
- Shape(1, True) == 'hi'
-
- def test_compare_tuple_wrong(self):
- with self.assertRaisesRegex(TypeError,
- r"^Shapes may be compared with other Shapes and \(int, bool\) tuples, not \(2, 3\)$"):
- Shape(1, True) == (2, 3)
-
- def test_repr(self):
- self.assertEqual(repr(Shape()), "unsigned(1)")
- self.assertEqual(repr(Shape(2, True)), "signed(2)")
-
- def test_tuple(self):
- width, signed = Shape()
- self.assertEqual(width, 1)
- self.assertEqual(signed, False)
-
- def test_unsigned(self):
- s1 = unsigned(2)
- self.assertIsInstance(s1, Shape)
- self.assertEqual(s1.width, 2)
- self.assertEqual(s1.signed, False)
-
- def test_signed(self):
- s1 = signed(2)
- self.assertIsInstance(s1, Shape)
- self.assertEqual(s1.width, 2)
- self.assertEqual(s1.signed, True)
-
- def test_cast_shape(self):
- s1 = Shape.cast(unsigned(1))
- self.assertEqual(s1.width, 1)
- self.assertEqual(s1.signed, False)
- s2 = Shape.cast(signed(3))
- self.assertEqual(s2.width, 3)
- self.assertEqual(s2.signed, True)
-
- def test_cast_int(self):
- s1 = Shape.cast(2)
- self.assertEqual(s1.width, 2)
- self.assertEqual(s1.signed, False)
-
- def test_cast_int_wrong(self):
- with self.assertRaisesRegex(TypeError,
- r"^Width must be a non-negative integer, not -1$"):
- Shape.cast(-1)
-
- def test_cast_tuple(self):
- with warnings.catch_warnings():
- warnings.filterwarnings(action="ignore", category=DeprecationWarning)
- s1 = Shape.cast((1, True))
- self.assertEqual(s1.width, 1)
- self.assertEqual(s1.signed, True)
-
- def test_cast_tuple_wrong(self):
- with warnings.catch_warnings():
- warnings.filterwarnings(action="ignore", category=DeprecationWarning)
- with self.assertRaisesRegex(TypeError,
- r"^Width must be a non-negative integer, not -1$"):
- Shape.cast((-1, True))
-
- def test_cast_range(self):
- s1 = Shape.cast(range(0, 8))
- self.assertEqual(s1.width, 3)
- self.assertEqual(s1.signed, False)
- s2 = Shape.cast(range(0, 9))
- self.assertEqual(s2.width, 4)
- self.assertEqual(s2.signed, False)
- s3 = Shape.cast(range(-7, 8))
- self.assertEqual(s3.width, 4)
- self.assertEqual(s3.signed, True)
- s4 = Shape.cast(range(0, 1))
- self.assertEqual(s4.width, 1)
- self.assertEqual(s4.signed, False)
- s5 = Shape.cast(range(-1, 0))
- self.assertEqual(s5.width, 1)
- self.assertEqual(s5.signed, True)
- s6 = Shape.cast(range(0, 0))
- self.assertEqual(s6.width, 0)
- self.assertEqual(s6.signed, False)
- s7 = Shape.cast(range(-1, -1))
- self.assertEqual(s7.width, 0)
- self.assertEqual(s7.signed, True)
-
- def test_cast_enum(self):
- s1 = Shape.cast(UnsignedEnum)
- self.assertEqual(s1.width, 2)
- self.assertEqual(s1.signed, False)
- s2 = Shape.cast(SignedEnum)
- self.assertEqual(s2.width, 2)
- self.assertEqual(s2.signed, True)
-
- def test_cast_enum_bad(self):
- with self.assertRaisesRegex(TypeError,
- r"^Only enumerations with integer values can be used as value shapes$"):
- Shape.cast(StringEnum)
-
- def test_cast_bad(self):
- with self.assertRaisesRegex(TypeError,
- r"^Object 'foo' cannot be used as value shape$"):
- Shape.cast("foo")
-
-
-class ValueTestCase(FHDLTestCase):
- def test_cast(self):
- self.assertIsInstance(Value.cast(0), Const)
- self.assertIsInstance(Value.cast(True), Const)
- c = Const(0)
- self.assertIs(Value.cast(c), c)
- with self.assertRaisesRegex(TypeError,
- r"^Object 'str' cannot be converted to an nMigen value$"):
- Value.cast("str")
-
- def test_cast_enum(self):
- e1 = Value.cast(UnsignedEnum.FOO)
- self.assertIsInstance(e1, Const)
- self.assertEqual(e1.shape(), unsigned(2))
- e2 = Value.cast(SignedEnum.FOO)
- self.assertIsInstance(e2, Const)
- self.assertEqual(e2.shape(), signed(2))
-
- def test_cast_enum_wrong(self):
- with self.assertRaisesRegex(TypeError,
- r"^Only enumerations with integer values can be used as value shapes$"):
- Value.cast(StringEnum.FOO)
-
- def test_bool(self):
- with self.assertRaisesRegex(TypeError,
- r"^Attempted to convert nMigen value to Python boolean$"):
- if Const(0):
- pass
-
- def test_len(self):
- self.assertEqual(len(Const(10)), 4)
-
- def test_getitem_int(self):
- s1 = Const(10)[0]
- self.assertIsInstance(s1, Slice)
- self.assertEqual(s1.start, 0)
- self.assertEqual(s1.stop, 1)
- s2 = Const(10)[-1]
- self.assertIsInstance(s2, Slice)
- self.assertEqual(s2.start, 3)
- self.assertEqual(s2.stop, 4)
- with self.assertRaisesRegex(IndexError,
- r"^Cannot index 5 bits into 4-bit value$"):
- Const(10)[5]
-
- def test_getitem_slice(self):
- s1 = Const(10)[1:3]
- self.assertIsInstance(s1, Slice)
- self.assertEqual(s1.start, 1)
- self.assertEqual(s1.stop, 3)
- s2 = Const(10)[1:-2]
- self.assertIsInstance(s2, Slice)
- self.assertEqual(s2.start, 1)
- self.assertEqual(s2.stop, 2)
- s3 = Const(31)[::2]
- self.assertIsInstance(s3, Cat)
- self.assertIsInstance(s3.parts[0], Slice)
- self.assertEqual(s3.parts[0].start, 0)
- self.assertEqual(s3.parts[0].stop, 1)
- self.assertIsInstance(s3.parts[1], Slice)
- self.assertEqual(s3.parts[1].start, 2)
- self.assertEqual(s3.parts[1].stop, 3)
- self.assertIsInstance(s3.parts[2], Slice)
- self.assertEqual(s3.parts[2].start, 4)
- self.assertEqual(s3.parts[2].stop, 5)
-
- def test_getitem_wrong(self):
- with self.assertRaisesRegex(TypeError,
- r"^Cannot index value with 'str'$"):
- Const(31)["str"]
-
- def test_shift_left(self):
- self.assertRepr(Const(256, unsigned(9)).shift_left(0),
- "(cat (const 0'd0) (const 9'd256))")
-
- self.assertRepr(Const(256, unsigned(9)).shift_left(1),
- "(cat (const 1'd0) (const 9'd256))")
- self.assertRepr(Const(256, unsigned(9)).shift_left(5),
- "(cat (const 5'd0) (const 9'd256))")
- self.assertRepr(Const(256, signed(9)).shift_left(1),
- "(s (cat (const 1'd0) (const 9'sd-256)))")
- self.assertRepr(Const(256, signed(9)).shift_left(5),
- "(s (cat (const 5'd0) (const 9'sd-256)))")
-
- self.assertRepr(Const(256, unsigned(9)).shift_left(-1),
- "(slice (const 9'd256) 1:9)")
- self.assertRepr(Const(256, unsigned(9)).shift_left(-5),
- "(slice (const 9'd256) 5:9)")
- self.assertRepr(Const(256, signed(9)).shift_left(-1),
- "(s (slice (const 9'sd-256) 1:9))")
- self.assertRepr(Const(256, signed(9)).shift_left(-5),
- "(s (slice (const 9'sd-256) 5:9))")
- self.assertRepr(Const(256, signed(9)).shift_left(-15),
- "(s (slice (const 9'sd-256) 9:9))")
-
- def test_shift_left_wrong(self):
- with self.assertRaisesRegex(TypeError,
- r"^Shift amount must be an integer, not 'str'$"):
- Const(31).shift_left("str")
-
- def test_shift_right(self):
- self.assertRepr(Const(256, unsigned(9)).shift_right(0),
- "(slice (const 9'd256) 0:9)")
-
- self.assertRepr(Const(256, unsigned(9)).shift_right(-1),
- "(cat (const 1'd0) (const 9'd256))")
- self.assertRepr(Const(256, unsigned(9)).shift_right(-5),
- "(cat (const 5'd0) (const 9'd256))")
- self.assertRepr(Const(256, signed(9)).shift_right(-1),
- "(s (cat (const 1'd0) (const 9'sd-256)))")
- self.assertRepr(Const(256, signed(9)).shift_right(-5),
- "(s (cat (const 5'd0) (const 9'sd-256)))")
-
- self.assertRepr(Const(256, unsigned(9)).shift_right(1),
- "(slice (const 9'd256) 1:9)")
- self.assertRepr(Const(256, unsigned(9)).shift_right(5),
- "(slice (const 9'd256) 5:9)")
- self.assertRepr(Const(256, signed(9)).shift_right(1),
- "(s (slice (const 9'sd-256) 1:9))")
- self.assertRepr(Const(256, signed(9)).shift_right(5),
- "(s (slice (const 9'sd-256) 5:9))")
- self.assertRepr(Const(256, signed(9)).shift_right(15),
- "(s (slice (const 9'sd-256) 9:9))")
-
- def test_shift_right_wrong(self):
- with self.assertRaisesRegex(TypeError,
- r"^Shift amount must be an integer, not 'str'$"):
- Const(31).shift_left("str")
-
- def test_rotate_left(self):
- self.assertRepr(Const(256).rotate_left(1),
- "(cat (slice (const 9'd256) 8:9) (slice (const 9'd256) 0:8))")
- self.assertRepr(Const(256).rotate_left(7),
- "(cat (slice (const 9'd256) 2:9) (slice (const 9'd256) 0:2))")
- self.assertRepr(Const(256).rotate_left(-1),
- "(cat (slice (const 9'd256) 1:9) (slice (const 9'd256) 0:1))")
- self.assertRepr(Const(256).rotate_left(-7),
- "(cat (slice (const 9'd256) 7:9) (slice (const 9'd256) 0:7))")
-
- def test_rotate_left_wrong(self):
- with self.assertRaisesRegex(TypeError,
- r"^Rotate amount must be an integer, not 'str'$"):
- Const(31).rotate_left("str")
-
- def test_rotate_right(self):
- self.assertRepr(Const(256).rotate_right(1),
- "(cat (slice (const 9'd256) 1:9) (slice (const 9'd256) 0:1))")
- self.assertRepr(Const(256).rotate_right(7),
- "(cat (slice (const 9'd256) 7:9) (slice (const 9'd256) 0:7))")
- self.assertRepr(Const(256).rotate_right(-1),
- "(cat (slice (const 9'd256) 8:9) (slice (const 9'd256) 0:8))")
- self.assertRepr(Const(256).rotate_right(-7),
- "(cat (slice (const 9'd256) 2:9) (slice (const 9'd256) 0:2))")
-
- def test_rotate_right_wrong(self):
- with self.assertRaisesRegex(TypeError,
- r"^Rotate amount must be an integer, not 'str'$"):
- Const(31).rotate_right("str")
-
-
-class ConstTestCase(FHDLTestCase):
- def test_shape(self):
- self.assertEqual(Const(0).shape(), unsigned(1))
- self.assertIsInstance(Const(0).shape(), Shape)
- self.assertEqual(Const(1).shape(), unsigned(1))
- self.assertEqual(Const(10).shape(), unsigned(4))
- self.assertEqual(Const(-10).shape(), signed(5))
-
- self.assertEqual(Const(1, 4).shape(), unsigned(4))
- self.assertEqual(Const(-1, 4).shape(), signed(4))
- self.assertEqual(Const(1, signed(4)).shape(), signed(4))
- self.assertEqual(Const(0, unsigned(0)).shape(), unsigned(0))
-
- def test_shape_wrong(self):
- with self.assertRaisesRegex(TypeError,
- r"^Width must be a non-negative integer, not -1$"):
- Const(1, -1)
-
- def test_normalization(self):
- self.assertEqual(Const(0b10110, signed(5)).value, -10)
-
- def test_value(self):
- self.assertEqual(Const(10).value, 10)
-
- def test_repr(self):
- self.assertEqual(repr(Const(10)), "(const 4'd10)")
- self.assertEqual(repr(Const(-10)), "(const 5'sd-10)")
-
- def test_hash(self):
- with self.assertRaises(TypeError):
- hash(Const(0))
-
-
-class OperatorTestCase(FHDLTestCase):
- def test_bool(self):
- v = Const(0, 4).bool()
- self.assertEqual(repr(v), "(b (const 4'd0))")
- self.assertEqual(v.shape(), unsigned(1))
-
- def test_invert(self):
- v = ~Const(0, 4)
- self.assertEqual(repr(v), "(~ (const 4'd0))")
- self.assertEqual(v.shape(), unsigned(4))
-
- def test_as_unsigned(self):
- v = Const(-1, signed(4)).as_unsigned()
- self.assertEqual(repr(v), "(u (const 4'sd-1))")
- self.assertEqual(v.shape(), unsigned(4))
-
- def test_as_signed(self):
- v = Const(1, unsigned(4)).as_signed()
- self.assertEqual(repr(v), "(s (const 4'd1))")
- self.assertEqual(v.shape(), signed(4))
-
- def test_neg(self):
- v1 = -Const(0, unsigned(4))
- self.assertEqual(repr(v1), "(- (const 4'd0))")
- self.assertEqual(v1.shape(), signed(5))
- v2 = -Const(0, signed(4))
- self.assertEqual(repr(v2), "(- (const 4'sd0))")
- self.assertEqual(v2.shape(), signed(5))
-
- def test_add(self):
- v1 = Const(0, unsigned(4)) + Const(0, unsigned(6))
- self.assertEqual(repr(v1), "(+ (const 4'd0) (const 6'd0))")
- self.assertEqual(v1.shape(), unsigned(7))
- v2 = Const(0, signed(4)) + Const(0, signed(6))
- self.assertEqual(v2.shape(), signed(7))
- v3 = Const(0, signed(4)) + Const(0, unsigned(4))
- self.assertEqual(v3.shape(), signed(6))
- v4 = Const(0, unsigned(4)) + Const(0, signed(4))
- self.assertEqual(v4.shape(), signed(6))
- v5 = 10 + Const(0, 4)
- self.assertEqual(v5.shape(), unsigned(5))
-
- def test_sub(self):
- v1 = Const(0, unsigned(4)) - Const(0, unsigned(6))
- self.assertEqual(repr(v1), "(- (const 4'd0) (const 6'd0))")
- self.assertEqual(v1.shape(), unsigned(7))
- v2 = Const(0, signed(4)) - Const(0, signed(6))
- self.assertEqual(v2.shape(), signed(7))
- v3 = Const(0, signed(4)) - Const(0, unsigned(4))
- self.assertEqual(v3.shape(), signed(6))
- v4 = Const(0, unsigned(4)) - Const(0, signed(4))
- self.assertEqual(v4.shape(), signed(6))
- v5 = 10 - Const(0, 4)
- self.assertEqual(v5.shape(), unsigned(5))
-
- def test_mul(self):
- v1 = Const(0, unsigned(4)) * Const(0, unsigned(6))
- self.assertEqual(repr(v1), "(* (const 4'd0) (const 6'd0))")
- self.assertEqual(v1.shape(), unsigned(10))
- v2 = Const(0, signed(4)) * Const(0, signed(6))
- self.assertEqual(v2.shape(), signed(10))
- v3 = Const(0, signed(4)) * Const(0, unsigned(4))
- self.assertEqual(v3.shape(), signed(8))
- v5 = 10 * Const(0, 4)
- self.assertEqual(v5.shape(), unsigned(8))
-
- def test_mod(self):
- v1 = Const(0, unsigned(4)) % Const(0, unsigned(6))
- self.assertEqual(repr(v1), "(% (const 4'd0) (const 6'd0))")
- self.assertEqual(v1.shape(), unsigned(4))
- v3 = Const(0, signed(4)) % Const(0, unsigned(4))
- self.assertEqual(v3.shape(), signed(4))
- v5 = 10 % Const(0, 4)
- self.assertEqual(v5.shape(), unsigned(4))
-
- def test_mod_wrong(self):
- with self.assertRaisesRegex(NotImplementedError,
- r"^Division by a signed value is not supported$"):
- Const(0, signed(4)) % Const(0, signed(6))
-
- def test_floordiv(self):
- v1 = Const(0, unsigned(4)) // Const(0, unsigned(6))
- self.assertEqual(repr(v1), "(// (const 4'd0) (const 6'd0))")
- self.assertEqual(v1.shape(), unsigned(4))
- v3 = Const(0, signed(4)) // Const(0, unsigned(4))
- self.assertEqual(v3.shape(), signed(4))
- v5 = 10 // Const(0, 4)
- self.assertEqual(v5.shape(), unsigned(4))
-
- def test_floordiv_wrong(self):
- with self.assertRaisesRegex(NotImplementedError,
- r"^Division by a signed value is not supported$"):
- Const(0, signed(4)) // Const(0, signed(6))
-
- def test_and(self):
- v1 = Const(0, unsigned(4)) & Const(0, unsigned(6))
- self.assertEqual(repr(v1), "(& (const 4'd0) (const 6'd0))")
- self.assertEqual(v1.shape(), unsigned(6))
- v2 = Const(0, signed(4)) & Const(0, signed(6))
- self.assertEqual(v2.shape(), signed(6))
- v3 = Const(0, signed(4)) & Const(0, unsigned(4))
- self.assertEqual(v3.shape(), signed(5))
- v4 = Const(0, unsigned(4)) & Const(0, signed(4))
- self.assertEqual(v4.shape(), signed(5))
- v5 = 10 & Const(0, 4)
- self.assertEqual(v5.shape(), unsigned(4))
-
- def test_or(self):
- v1 = Const(0, unsigned(4)) | Const(0, unsigned(6))
- self.assertEqual(repr(v1), "(| (const 4'd0) (const 6'd0))")
- self.assertEqual(v1.shape(), unsigned(6))
- v2 = Const(0, signed(4)) | Const(0, signed(6))
- self.assertEqual(v2.shape(), signed(6))
- v3 = Const(0, signed(4)) | Const(0, unsigned(4))
- self.assertEqual(v3.shape(), signed(5))
- v4 = Const(0, unsigned(4)) | Const(0, signed(4))
- self.assertEqual(v4.shape(), signed(5))
- v5 = 10 | Const(0, 4)
- self.assertEqual(v5.shape(), unsigned(4))
-
- def test_xor(self):
- v1 = Const(0, unsigned(4)) ^ Const(0, unsigned(6))
- self.assertEqual(repr(v1), "(^ (const 4'd0) (const 6'd0))")
- self.assertEqual(v1.shape(), unsigned(6))
- v2 = Const(0, signed(4)) ^ Const(0, signed(6))
- self.assertEqual(v2.shape(), signed(6))
- v3 = Const(0, signed(4)) ^ Const(0, unsigned(4))
- self.assertEqual(v3.shape(), signed(5))
- v4 = Const(0, unsigned(4)) ^ Const(0, signed(4))
- self.assertEqual(v4.shape(), signed(5))
- v5 = 10 ^ Const(0, 4)
- self.assertEqual(v5.shape(), unsigned(4))
-
- def test_shl(self):
- v1 = Const(1, 4) << Const(4)
- self.assertEqual(repr(v1), "(<< (const 4'd1) (const 3'd4))")
- self.assertEqual(v1.shape(), unsigned(11))
-
- def test_shl_wrong(self):
- with self.assertRaisesRegex(TypeError,
- r"^Shift amount must be unsigned$"):
- 1 << Const(0, signed(6))
- with self.assertRaisesRegex(TypeError,
- r"^Shift amount must be unsigned$"):
- Const(1, unsigned(4)) << -1
-
- def test_shr(self):
- v1 = Const(1, 4) >> Const(4)
- self.assertEqual(repr(v1), "(>> (const 4'd1) (const 3'd4))")
- self.assertEqual(v1.shape(), unsigned(4))
-
- def test_shr_wrong(self):
- with self.assertRaisesRegex(TypeError,
- r"^Shift amount must be unsigned$"):
- 1 << Const(0, signed(6))
- with self.assertRaisesRegex(TypeError,
- r"^Shift amount must be unsigned$"):
- Const(1, unsigned(4)) << -1
-
- def test_lt(self):
- v = Const(0, 4) < Const(0, 6)
- self.assertEqual(repr(v), "(< (const 4'd0) (const 6'd0))")
- self.assertEqual(v.shape(), unsigned(1))
-
- def test_le(self):
- v = Const(0, 4) <= Const(0, 6)
- self.assertEqual(repr(v), "(<= (const 4'd0) (const 6'd0))")
- self.assertEqual(v.shape(), unsigned(1))
-
- def test_gt(self):
- v = Const(0, 4) > Const(0, 6)
- self.assertEqual(repr(v), "(> (const 4'd0) (const 6'd0))")
- self.assertEqual(v.shape(), unsigned(1))
-
- def test_ge(self):
- v = Const(0, 4) >= Const(0, 6)
- self.assertEqual(repr(v), "(>= (const 4'd0) (const 6'd0))")
- self.assertEqual(v.shape(), unsigned(1))
-
- def test_eq(self):
- v = Const(0, 4) == Const(0, 6)
- self.assertEqual(repr(v), "(== (const 4'd0) (const 6'd0))")
- self.assertEqual(v.shape(), unsigned(1))
-
- def test_ne(self):
- v = Const(0, 4) != Const(0, 6)
- self.assertEqual(repr(v), "(!= (const 4'd0) (const 6'd0))")
- self.assertEqual(v.shape(), unsigned(1))
-
- def test_mux(self):
- s = Const(0)
- v1 = Mux(s, Const(0, unsigned(4)), Const(0, unsigned(6)))
- self.assertEqual(repr(v1), "(m (const 1'd0) (const 4'd0) (const 6'd0))")
- self.assertEqual(v1.shape(), unsigned(6))
- v2 = Mux(s, Const(0, signed(4)), Const(0, signed(6)))
- self.assertEqual(v2.shape(), signed(6))
- v3 = Mux(s, Const(0, signed(4)), Const(0, unsigned(4)))
- self.assertEqual(v3.shape(), signed(5))
- v4 = Mux(s, Const(0, unsigned(4)), Const(0, signed(4)))
- self.assertEqual(v4.shape(), signed(5))
-
- def test_mux_wide(self):
- s = Const(0b100)
- v = Mux(s, Const(0, unsigned(4)), Const(0, unsigned(6)))
- self.assertEqual(repr(v), "(m (b (const 3'd4)) (const 4'd0) (const 6'd0))")
-
- def test_mux_bool(self):
- v = Mux(True, Const(0), Const(0))
- self.assertEqual(repr(v), "(m (const 1'd1) (const 1'd0) (const 1'd0))")
-
- def test_bool(self):
- v = Const(0).bool()
- self.assertEqual(repr(v), "(b (const 1'd0))")
- self.assertEqual(v.shape(), unsigned(1))
-
- def test_any(self):
- v = Const(0b101).any()
- self.assertEqual(repr(v), "(r| (const 3'd5))")
-
- def test_all(self):
- v = Const(0b101).all()
- self.assertEqual(repr(v), "(r& (const 3'd5))")
-
- def test_xor(self):
- v = Const(0b101).xor()
- self.assertEqual(repr(v), "(r^ (const 3'd5))")
-
- def test_matches(self):
- s = Signal(4)
- self.assertRepr(s.matches(), "(const 1'd0)")
- self.assertRepr(s.matches(1), """
- (== (sig s) (const 1'd1))
- """)
- self.assertRepr(s.matches(0, 1), """
- (r| (cat (== (sig s) (const 1'd0)) (== (sig s) (const 1'd1))))
- """)
- self.assertRepr(s.matches("10--"), """
- (== (& (sig s) (const 4'd12)) (const 4'd8))
- """)
- self.assertRepr(s.matches("1 0--"), """
- (== (& (sig s) (const 4'd12)) (const 4'd8))
- """)
-
- def test_matches_enum(self):
- s = Signal(SignedEnum)
- self.assertRepr(s.matches(SignedEnum.FOO), """
- (== (sig s) (const 1'sd-1))
- """)
-
- def test_matches_width_wrong(self):
- s = Signal(4)
- with self.assertRaisesRegex(SyntaxError,
- r"^Match pattern '--' must have the same width as match value \(which is 4\)$"):
- s.matches("--")
- with self.assertWarnsRegex(SyntaxWarning,
- (r"^Match pattern '10110' is wider than match value \(which has width 4\); "
- r"comparison will never be true$")):
- s.matches(0b10110)
-
- def test_matches_bits_wrong(self):
- s = Signal(4)
- with self.assertRaisesRegex(SyntaxError,
- (r"^Match pattern 'abc' must consist of 0, 1, and - \(don't care\) bits, "
- r"and may include whitespace$")):
- s.matches("abc")
-
- def test_matches_pattern_wrong(self):
- s = Signal(4)
- with self.assertRaisesRegex(SyntaxError,
- r"^Match pattern must be an integer, a string, or an enumeration, not 1\.0$"):
- s.matches(1.0)
-
- def test_hash(self):
- with self.assertRaises(TypeError):
- hash(Const(0) + Const(0))
-
-
-class SliceTestCase(FHDLTestCase):
- def test_shape(self):
- s1 = Const(10)[2]
- self.assertEqual(s1.shape(), unsigned(1))
- self.assertIsInstance(s1.shape(), Shape)
- s2 = Const(-10)[0:2]
- self.assertEqual(s2.shape(), unsigned(2))
-
- def test_start_end_negative(self):
- c = Const(0, 8)
- s1 = Slice(c, 0, -1)
- self.assertEqual((s1.start, s1.stop), (0, 7))
- s1 = Slice(c, -4, -1)
- self.assertEqual((s1.start, s1.stop), (4, 7))
-
- def test_start_end_wrong(self):
- with self.assertRaisesRegex(TypeError,
- r"^Slice start must be an integer, not 'x'$"):
- Slice(0, "x", 1)
- with self.assertRaisesRegex(TypeError,
- r"^Slice stop must be an integer, not 'x'$"):
- Slice(0, 1, "x")
-
- def test_start_end_out_of_range(self):
- c = Const(0, 8)
- with self.assertRaisesRegex(IndexError,
- r"^Cannot start slice 10 bits into 8-bit value$"):
- Slice(c, 10, 12)
- with self.assertRaisesRegex(IndexError,
- r"^Cannot stop slice 12 bits into 8-bit value$"):
- Slice(c, 0, 12)
- with self.assertRaisesRegex(IndexError,
- r"^Slice start 4 must be less than slice stop 2$"):
- Slice(c, 4, 2)
-
- def test_repr(self):
- s1 = Const(10)[2]
- self.assertEqual(repr(s1), "(slice (const 4'd10) 2:3)")
-
-
-class BitSelectTestCase(FHDLTestCase):
- def setUp(self):
- self.c = Const(0, 8)
- self.s = Signal(range(self.c.width))
-
- def test_shape(self):
- s1 = self.c.bit_select(self.s, 2)
- self.assertIsInstance(s1, Part)
- self.assertEqual(s1.shape(), unsigned(2))
- self.assertIsInstance(s1.shape(), Shape)
- s2 = self.c.bit_select(self.s, 0)
- self.assertIsInstance(s2, Part)
- self.assertEqual(s2.shape(), unsigned(0))
-
- def test_stride(self):
- s1 = self.c.bit_select(self.s, 2)
- self.assertIsInstance(s1, Part)
- self.assertEqual(s1.stride, 1)
-
- def test_const(self):
- s1 = self.c.bit_select(1, 2)
- self.assertIsInstance(s1, Slice)
- self.assertRepr(s1, """(slice (const 8'd0) 1:3)""")
-
- def test_width_wrong(self):
- with self.assertRaises(TypeError):
- self.c.bit_select(self.s, -1)
-
- def test_repr(self):
- s = self.c.bit_select(self.s, 2)
- self.assertEqual(repr(s), "(part (const 8'd0) (sig s) 2 1)")
-
-
-class WordSelectTestCase(FHDLTestCase):
- def setUp(self):
- self.c = Const(0, 8)
- self.s = Signal(range(self.c.width))
-
- def test_shape(self):
- s1 = self.c.word_select(self.s, 2)
- self.assertIsInstance(s1, Part)
- self.assertEqual(s1.shape(), unsigned(2))
- self.assertIsInstance(s1.shape(), Shape)
-
- def test_stride(self):
- s1 = self.c.word_select(self.s, 2)
- self.assertIsInstance(s1, Part)
- self.assertEqual(s1.stride, 2)
-
- def test_const(self):
- s1 = self.c.word_select(1, 2)
- self.assertIsInstance(s1, Slice)
- self.assertRepr(s1, """(slice (const 8'd0) 2:4)""")
-
- def test_width_wrong(self):
- with self.assertRaises(TypeError):
- self.c.word_select(self.s, 0)
- with self.assertRaises(TypeError):
- self.c.word_select(self.s, -1)
-
- def test_repr(self):
- s = self.c.word_select(self.s, 2)
- self.assertEqual(repr(s), "(part (const 8'd0) (sig s) 2 2)")
-
-
-class CatTestCase(FHDLTestCase):
- def test_shape(self):
- c0 = Cat()
- self.assertEqual(c0.shape(), unsigned(0))
- self.assertIsInstance(c0.shape(), Shape)
- c1 = Cat(Const(10))
- self.assertEqual(c1.shape(), unsigned(4))
- c2 = Cat(Const(10), Const(1))
- self.assertEqual(c2.shape(), unsigned(5))
- c3 = Cat(Const(10), Const(1), Const(0))
- self.assertEqual(c3.shape(), unsigned(6))
-
- def test_repr(self):
- c1 = Cat(Const(10), Const(1))
- self.assertEqual(repr(c1), "(cat (const 4'd10) (const 1'd1))")
-
-
-class ReplTestCase(FHDLTestCase):
- def test_shape(self):
- s1 = Repl(Const(10), 3)
- self.assertEqual(s1.shape(), unsigned(12))
- self.assertIsInstance(s1.shape(), Shape)
- s2 = Repl(Const(10), 0)
- self.assertEqual(s2.shape(), unsigned(0))
-
- def test_count_wrong(self):
- with self.assertRaises(TypeError):
- Repl(Const(10), -1)
- with self.assertRaises(TypeError):
- Repl(Const(10), "str")
-
- def test_repr(self):
- s = Repl(Const(10), 3)
- self.assertEqual(repr(s), "(repl (const 4'd10) 3)")
-
-
-class ArrayTestCase(FHDLTestCase):
- def test_acts_like_array(self):
- a = Array([1,2,3])
- self.assertSequenceEqual(a, [1,2,3])
- self.assertEqual(a[1], 2)
- a[1] = 4
- self.assertSequenceEqual(a, [1,4,3])
- del a[1]
- self.assertSequenceEqual(a, [1,3])
- a.insert(1, 2)
- self.assertSequenceEqual(a, [1,2,3])
-
- def test_becomes_immutable(self):
- a = Array([1,2,3])
- s1 = Signal(range(len(a)))
- s2 = Signal(range(len(a)))
- v1 = a[s1]
- v2 = a[s2]
- with self.assertRaisesRegex(ValueError,
- r"^Array can no longer be mutated after it was indexed with a value at "):
- a[1] = 2
- with self.assertRaisesRegex(ValueError,
- r"^Array can no longer be mutated after it was indexed with a value at "):
- del a[1]
- with self.assertRaisesRegex(ValueError,
- r"^Array can no longer be mutated after it was indexed with a value at "):
- a.insert(1, 2)
-
- def test_repr(self):
- a = Array([1,2,3])
- self.assertEqual(repr(a), "(array mutable [1, 2, 3])")
- s = Signal(range(len(a)))
- v = a[s]
- self.assertEqual(repr(a), "(array [1, 2, 3])")
-
-
-class ArrayProxyTestCase(FHDLTestCase):
- def test_index_shape(self):
- m = Array(Array(x * y for y in range(1, 4)) for x in range(1, 4))
- a = Signal(range(3))
- b = Signal(range(3))
- v = m[a][b]
- self.assertEqual(v.shape(), unsigned(4))
-
- def test_attr_shape(self):
- from collections import namedtuple
- pair = namedtuple("pair", ("p", "n"))
- a = Array(pair(i, -i) for i in range(10))
- s = Signal(range(len(a)))
- v = a[s]
- self.assertEqual(v.p.shape(), unsigned(4))
- self.assertEqual(v.n.shape(), signed(5))
-
- def test_attr_shape_signed(self):
- # [unsigned(1), unsigned(1)] → unsigned(1)
- a1 = Array([1, 1])
- v1 = a1[Const(0)]
- self.assertEqual(v1.shape(), unsigned(1))
- # [signed(1), signed(1)] → signed(1)
- a2 = Array([-1, -1])
- v2 = a2[Const(0)]
- self.assertEqual(v2.shape(), signed(1))
- # [unsigned(1), signed(2)] → signed(2)
- a3 = Array([1, -2])
- v3 = a3[Const(0)]
- self.assertEqual(v3.shape(), signed(2))
- # [unsigned(1), signed(1)] → signed(2); 1st operand padded with sign bit!
- a4 = Array([1, -1])
- v4 = a4[Const(0)]
- self.assertEqual(v4.shape(), signed(2))
- # [unsigned(2), signed(1)] → signed(3); 1st operand padded with sign bit!
- a5 = Array([1, -1])
- v5 = a5[Const(0)]
- self.assertEqual(v5.shape(), signed(2))
-
- def test_repr(self):
- a = Array([1, 2, 3])
- s = Signal(range(3))
- v = a[s]
- self.assertEqual(repr(v), "(proxy (array [1, 2, 3]) (sig s))")
-
-
-class SignalTestCase(FHDLTestCase):
- def test_shape(self):
- s1 = Signal()
- self.assertEqual(s1.shape(), unsigned(1))
- self.assertIsInstance(s1.shape(), Shape)
- s2 = Signal(2)
- self.assertEqual(s2.shape(), unsigned(2))
- s3 = Signal(unsigned(2))
- self.assertEqual(s3.shape(), unsigned(2))
- s4 = Signal(signed(2))
- self.assertEqual(s4.shape(), signed(2))
- s5 = Signal(0)
- self.assertEqual(s5.shape(), unsigned(0))
- s6 = Signal(range(16))
- self.assertEqual(s6.shape(), unsigned(4))
- s7 = Signal(range(4, 16))
- self.assertEqual(s7.shape(), unsigned(4))
- s8 = Signal(range(-4, 16))
- self.assertEqual(s8.shape(), signed(5))
- s9 = Signal(range(-20, 16))
- self.assertEqual(s9.shape(), signed(6))
- s10 = Signal(range(0))
- self.assertEqual(s10.shape(), unsigned(0))
- s11 = Signal(range(1))
- self.assertEqual(s11.shape(), unsigned(1))
-
- def test_shape_wrong(self):
- with self.assertRaisesRegex(TypeError,
- r"^Width must be a non-negative integer, not -10$"):
- Signal(-10)
-
- def test_name(self):
- s1 = Signal()
- self.assertEqual(s1.name, "s1")
- s2 = Signal(name="sig")
- self.assertEqual(s2.name, "sig")
-
- def test_reset(self):
- s1 = Signal(4, reset=0b111, reset_less=True)
- self.assertEqual(s1.reset, 0b111)
- self.assertEqual(s1.reset_less, True)
-
- def test_reset_enum(self):
- s1 = Signal(2, reset=UnsignedEnum.BAR)
- self.assertEqual(s1.reset, 2)
- with self.assertRaisesRegex(TypeError,
- r"^Reset value has to be an int or an integral Enum$"
- ):
- Signal(1, reset=StringEnum.FOO)
-
- def test_reset_narrow(self):
- with self.assertWarnsRegex(SyntaxWarning,
- r"^Reset value 8 requires 4 bits to represent, but the signal only has 3 bits$"):
- Signal(3, reset=8)
- with self.assertWarnsRegex(SyntaxWarning,
- r"^Reset value 4 requires 4 bits to represent, but the signal only has 3 bits$"):
- Signal(signed(3), reset=4)
- with self.assertWarnsRegex(SyntaxWarning,
- r"^Reset value -5 requires 4 bits to represent, but the signal only has 3 bits$"):
- Signal(signed(3), reset=-5)
-
- def test_attrs(self):
- s1 = Signal()
- self.assertEqual(s1.attrs, {})
- s2 = Signal(attrs={"no_retiming": True})
- self.assertEqual(s2.attrs, {"no_retiming": True})
-
- def test_repr(self):
- s1 = Signal()
- self.assertEqual(repr(s1), "(sig s1)")
-
- def test_like(self):
- s1 = Signal.like(Signal(4))
- self.assertEqual(s1.shape(), unsigned(4))
- s2 = Signal.like(Signal(range(-15, 1)))
- self.assertEqual(s2.shape(), signed(5))
- s3 = Signal.like(Signal(4, reset=0b111, reset_less=True))
- self.assertEqual(s3.reset, 0b111)
- self.assertEqual(s3.reset_less, True)
- s4 = Signal.like(Signal(attrs={"no_retiming": True}))
- self.assertEqual(s4.attrs, {"no_retiming": True})
- s5 = Signal.like(Signal(decoder=str))
- self.assertEqual(s5.decoder, str)
- s6 = Signal.like(10)
- self.assertEqual(s6.shape(), unsigned(4))
- s7 = [Signal.like(Signal(4))][0]
- self.assertEqual(s7.name, "$like")
- s8 = Signal.like(s1, name_suffix="_ff")
- self.assertEqual(s8.name, "s1_ff")
-
- def test_decoder(self):
- class Color(Enum):
- RED = 1
- BLUE = 2
- s = Signal(decoder=Color)
- self.assertEqual(s.decoder(1), "RED/1")
- self.assertEqual(s.decoder(3), "3")
-
- def test_enum(self):
- s1 = Signal(UnsignedEnum)
- self.assertEqual(s1.shape(), unsigned(2))
- s2 = Signal(SignedEnum)
- self.assertEqual(s2.shape(), signed(2))
- self.assertEqual(s2.decoder(SignedEnum.FOO), "FOO/-1")
-
-
-class ClockSignalTestCase(FHDLTestCase):
- def test_domain(self):
- s1 = ClockSignal()
- self.assertEqual(s1.domain, "sync")
- s2 = ClockSignal("pix")
- self.assertEqual(s2.domain, "pix")
-
- with self.assertRaisesRegex(TypeError,
- r"^Clock domain name must be a string, not 1$"):
- ClockSignal(1)
-
- def test_shape(self):
- s1 = ClockSignal()
- self.assertEqual(s1.shape(), unsigned(1))
- self.assertIsInstance(s1.shape(), Shape)
-
- def test_repr(self):
- s1 = ClockSignal()
- self.assertEqual(repr(s1), "(clk sync)")
-
- def test_wrong_name_comb(self):
- with self.assertRaisesRegex(ValueError,
- r"^Domain 'comb' does not have a clock$"):
- ClockSignal("comb")
-
-
-class ResetSignalTestCase(FHDLTestCase):
- def test_domain(self):
- s1 = ResetSignal()
- self.assertEqual(s1.domain, "sync")
- s2 = ResetSignal("pix")
- self.assertEqual(s2.domain, "pix")
-
- with self.assertRaisesRegex(TypeError,
- r"^Clock domain name must be a string, not 1$"):
- ResetSignal(1)
-
- def test_shape(self):
- s1 = ResetSignal()
- self.assertEqual(s1.shape(), unsigned(1))
- self.assertIsInstance(s1.shape(), Shape)
-
- def test_repr(self):
- s1 = ResetSignal()
- self.assertEqual(repr(s1), "(rst sync)")
-
- def test_wrong_name_comb(self):
- with self.assertRaisesRegex(ValueError,
- r"^Domain 'comb' does not have a reset$"):
- ResetSignal("comb")
-
-
-class MockUserValue(UserValue):
- def __init__(self, lowered):
- super().__init__()
- self.lower_count = 0
- self.lowered = lowered
-
- def lower(self):
- self.lower_count += 1
- return self.lowered
-
-
-class UserValueTestCase(FHDLTestCase):
- def test_shape(self):
- uv = MockUserValue(1)
- self.assertEqual(uv.shape(), unsigned(1))
- self.assertIsInstance(uv.shape(), Shape)
- uv.lowered = 2
- self.assertEqual(uv.shape(), unsigned(1))
- self.assertEqual(uv.lower_count, 1)
-
- def test_lower_to_user_value(self):
- uv = MockUserValue(MockUserValue(1))
- self.assertEqual(uv.shape(), unsigned(1))
- self.assertIsInstance(uv.shape(), Shape)
- uv.lowered = MockUserValue(2)
- self.assertEqual(uv.shape(), unsigned(1))
- self.assertEqual(uv.lower_count, 1)
-
-
-class SampleTestCase(FHDLTestCase):
- def test_const(self):
- s = Sample(1, 1, "sync")
- self.assertEqual(s.shape(), unsigned(1))
-
- def test_signal(self):
- s1 = Sample(Signal(2), 1, "sync")
- self.assertEqual(s1.shape(), unsigned(2))
- s2 = Sample(ClockSignal(), 1, "sync")
- s3 = Sample(ResetSignal(), 1, "sync")
-
- def test_wrong_value_operator(self):
- with self.assertRaisesRegex(TypeError,
- (r"^Sampled value must be a signal or a constant, not "
- r"\(\+ \(sig \$signal\) \(const 1'd1\)\)$")):
- Sample(Signal() + 1, 1, "sync")
-
- def test_wrong_clocks_neg(self):
- with self.assertRaisesRegex(ValueError,
- r"^Cannot sample a value 1 cycles in the future$"):
- Sample(Signal(), -1, "sync")
-
- def test_wrong_domain(self):
- with self.assertRaisesRegex(TypeError,
- r"^Domain name must be a string or None, not 0$"):
- Sample(Signal(), 1, 0)
-
-
-class InitialTestCase(FHDLTestCase):
- def test_initial(self):
- i = Initial()
- self.assertEqual(i.shape(), unsigned(1))
+++ /dev/null
-from ..hdl.cd import *
-from .utils import *
-
-
-class ClockDomainTestCase(FHDLTestCase):
- def test_name(self):
- sync = ClockDomain()
- self.assertEqual(sync.name, "sync")
- self.assertEqual(sync.clk.name, "clk")
- self.assertEqual(sync.rst.name, "rst")
- self.assertEqual(sync.local, False)
- pix = ClockDomain()
- self.assertEqual(pix.name, "pix")
- self.assertEqual(pix.clk.name, "pix_clk")
- self.assertEqual(pix.rst.name, "pix_rst")
- cd_pix = ClockDomain()
- self.assertEqual(pix.name, "pix")
- dom = [ClockDomain("foo")][0]
- self.assertEqual(dom.name, "foo")
- with self.assertRaisesRegex(ValueError,
- r"^Clock domain name must be specified explicitly$"):
- ClockDomain()
- cd_reset = ClockDomain(local=True)
- self.assertEqual(cd_reset.local, True)
-
- def test_edge(self):
- sync = ClockDomain()
- self.assertEqual(sync.clk_edge, "pos")
- sync = ClockDomain(clk_edge="pos")
- self.assertEqual(sync.clk_edge, "pos")
- sync = ClockDomain(clk_edge="neg")
- self.assertEqual(sync.clk_edge, "neg")
-
- def test_edge_wrong(self):
- with self.assertRaisesRegex(ValueError,
- r"^Domain clock edge must be one of 'pos' or 'neg', not 'xxx'$"):
- ClockDomain("sync", clk_edge="xxx")
-
- def test_with_reset(self):
- pix = ClockDomain()
- self.assertIsNotNone(pix.clk)
- self.assertIsNotNone(pix.rst)
- self.assertFalse(pix.async_reset)
-
- def test_without_reset(self):
- pix = ClockDomain(reset_less=True)
- self.assertIsNotNone(pix.clk)
- self.assertIsNone(pix.rst)
- self.assertFalse(pix.async_reset)
-
- def test_async_reset(self):
- pix = ClockDomain(async_reset=True)
- self.assertIsNotNone(pix.clk)
- self.assertIsNotNone(pix.rst)
- self.assertTrue(pix.async_reset)
-
- def test_rename(self):
- sync = ClockDomain()
- self.assertEqual(sync.name, "sync")
- self.assertEqual(sync.clk.name, "clk")
- self.assertEqual(sync.rst.name, "rst")
- sync.rename("pix")
- self.assertEqual(sync.name, "pix")
- self.assertEqual(sync.clk.name, "pix_clk")
- self.assertEqual(sync.rst.name, "pix_rst")
-
- def test_rename_reset_less(self):
- sync = ClockDomain(reset_less=True)
- self.assertEqual(sync.name, "sync")
- self.assertEqual(sync.clk.name, "clk")
- sync.rename("pix")
- self.assertEqual(sync.name, "pix")
- self.assertEqual(sync.clk.name, "pix_clk")
-
- def test_wrong_name_comb(self):
- with self.assertRaisesRegex(ValueError,
- r"^Domain 'comb' may not be clocked$"):
- comb = ClockDomain()
+++ /dev/null
-# nmigen: UnusedElaboratable=no
-
-from collections import OrderedDict
-from enum import Enum
-
-from ..hdl.ast import *
-from ..hdl.cd import *
-from ..hdl.dsl import *
-from .utils import *
-
-
-class DSLTestCase(FHDLTestCase):
- def setUp(self):
- self.s1 = Signal()
- self.s2 = Signal()
- self.s3 = Signal()
- self.c1 = Signal()
- self.c2 = Signal()
- self.c3 = Signal()
- self.w1 = Signal(4)
-
- def test_cant_inherit(self):
- with self.assertRaisesRegex(SyntaxError,
- (r"^Instead of inheriting from `Module`, inherit from `Elaboratable` and "
- r"return a `Module` from the `elaborate\(self, platform\)` method$")):
- class ORGate(Module):
- pass
-
- def test_d_comb(self):
- m = Module()
- m.d.comb += self.c1.eq(1)
- m._flush()
- self.assertEqual(m._driving[self.c1], None)
- self.assertRepr(m._statements, """(
- (eq (sig c1) (const 1'd1))
- )""")
-
- def test_d_sync(self):
- m = Module()
- m.d.sync += self.c1.eq(1)
- m._flush()
- self.assertEqual(m._driving[self.c1], "sync")
- self.assertRepr(m._statements, """(
- (eq (sig c1) (const 1'd1))
- )""")
-
- def test_d_pix(self):
- m = Module()
- m.d.pix += self.c1.eq(1)
- m._flush()
- self.assertEqual(m._driving[self.c1], "pix")
- self.assertRepr(m._statements, """(
- (eq (sig c1) (const 1'd1))
- )""")
-
- def test_d_index(self):
- m = Module()
- m.d["pix"] += self.c1.eq(1)
- m._flush()
- self.assertEqual(m._driving[self.c1], "pix")
- self.assertRepr(m._statements, """(
- (eq (sig c1) (const 1'd1))
- )""")
-
- def test_d_no_conflict(self):
- m = Module()
- m.d.comb += self.w1[0].eq(1)
- m.d.comb += self.w1[1].eq(1)
-
- def test_d_conflict(self):
- m = Module()
- with self.assertRaisesRegex(SyntaxError,
- (r"^Driver-driver conflict: trying to drive \(sig c1\) from d\.sync, but it "
- r"is already driven from d\.comb$")):
- m.d.comb += self.c1.eq(1)
- m.d.sync += self.c1.eq(1)
-
- def test_d_wrong(self):
- m = Module()
- with self.assertRaisesRegex(AttributeError,
- r"^Cannot assign 'd\.pix' attribute; did you mean 'd.pix \+='\?$"):
- m.d.pix = None
-
- def test_d_asgn_wrong(self):
- m = Module()
- with self.assertRaisesRegex(SyntaxError,
- r"^Only assignments and property checks may be appended to d\.sync$"):
- m.d.sync += Switch(self.s1, {})
-
- def test_comb_wrong(self):
- m = Module()
- with self.assertRaisesRegex(AttributeError,
- r"^'Module' object has no attribute 'comb'; did you mean 'd\.comb'\?$"):
- m.comb += self.c1.eq(1)
-
- def test_sync_wrong(self):
- m = Module()
- with self.assertRaisesRegex(AttributeError,
- r"^'Module' object has no attribute 'sync'; did you mean 'd\.sync'\?$"):
- m.sync += self.c1.eq(1)
-
- def test_attr_wrong(self):
- m = Module()
- with self.assertRaisesRegex(AttributeError,
- r"^'Module' object has no attribute 'nonexistentattr'$"):
- m.nonexistentattr
-
- def test_d_suspicious(self):
- m = Module()
- with self.assertWarnsRegex(SyntaxWarning,
- (r"^Using '<module>\.d\.submodules' would add statements to clock domain "
- r"'submodules'; did you mean <module>\.submodules instead\?$")):
- m.d.submodules += []
-
- def test_clock_signal(self):
- m = Module()
- m.d.comb += ClockSignal("pix").eq(ClockSignal())
- self.assertRepr(m._statements, """
- (
- (eq (clk pix) (clk sync))
- )
- """)
-
- def test_reset_signal(self):
- m = Module()
- m.d.comb += ResetSignal("pix").eq(1)
- self.assertRepr(m._statements, """
- (
- (eq (rst pix) (const 1'd1))
- )
- """)
-
- def test_sample_domain(self):
- m = Module()
- i = Signal()
- o1 = Signal()
- o2 = Signal()
- o3 = Signal()
- m.d.sync += o1.eq(Past(i))
- m.d.pix += o2.eq(Past(i))
- m.d.pix += o3.eq(Past(i, domain="sync"))
- f = m.elaborate(platform=None)
- self.assertRepr(f.statements, """
- (
- (eq (sig o1) (sample (sig i) @ sync[1]))
- (eq (sig o2) (sample (sig i) @ pix[1]))
- (eq (sig o3) (sample (sig i) @ sync[1]))
- )
- """)
-
- def test_If(self):
- m = Module()
- with m.If(self.s1):
- m.d.comb += self.c1.eq(1)
- m._flush()
- self.assertRepr(m._statements, """
- (
- (switch (cat (sig s1))
- (case 1 (eq (sig c1) (const 1'd1)))
- )
- )
- """)
-
- def test_If_Elif(self):
- m = Module()
- with m.If(self.s1):
- m.d.comb += self.c1.eq(1)
- with m.Elif(self.s2):
- m.d.sync += self.c2.eq(0)
- m._flush()
- self.assertRepr(m._statements, """
- (
- (switch (cat (sig s1) (sig s2))
- (case -1 (eq (sig c1) (const 1'd1)))
- (case 1- (eq (sig c2) (const 1'd0)))
- )
- )
- """)
-
- def test_If_Elif_Else(self):
- m = Module()
- with m.If(self.s1):
- m.d.comb += self.c1.eq(1)
- with m.Elif(self.s2):
- m.d.sync += self.c2.eq(0)
- with m.Else():
- m.d.comb += self.c3.eq(1)
- m._flush()
- self.assertRepr(m._statements, """
- (
- (switch (cat (sig s1) (sig s2))
- (case -1 (eq (sig c1) (const 1'd1)))
- (case 1- (eq (sig c2) (const 1'd0)))
- (default (eq (sig c3) (const 1'd1)))
- )
- )
- """)
-
- def test_If_If(self):
- m = Module()
- with m.If(self.s1):
- m.d.comb += self.c1.eq(1)
- with m.If(self.s2):
- m.d.comb += self.c2.eq(1)
- m._flush()
- self.assertRepr(m._statements, """
- (
- (switch (cat (sig s1))
- (case 1 (eq (sig c1) (const 1'd1)))
- )
- (switch (cat (sig s2))
- (case 1 (eq (sig c2) (const 1'd1)))
- )
- )
- """)
-
- def test_If_nested_If(self):
- m = Module()
- with m.If(self.s1):
- m.d.comb += self.c1.eq(1)
- with m.If(self.s2):
- m.d.comb += self.c2.eq(1)
- m._flush()
- self.assertRepr(m._statements, """
- (
- (switch (cat (sig s1))
- (case 1 (eq (sig c1) (const 1'd1))
- (switch (cat (sig s2))
- (case 1 (eq (sig c2) (const 1'd1)))
- )
- )
- )
- )
- """)
-
- def test_If_dangling_Else(self):
- m = Module()
- with m.If(self.s1):
- m.d.comb += self.c1.eq(1)
- with m.If(self.s2):
- m.d.comb += self.c2.eq(1)
- with m.Else():
- m.d.comb += self.c3.eq(1)
- m._flush()
- self.assertRepr(m._statements, """
- (
- (switch (cat (sig s1))
- (case 1
- (eq (sig c1) (const 1'd1))
- (switch (cat (sig s2))
- (case 1 (eq (sig c2) (const 1'd1)))
- )
- )
- (default
- (eq (sig c3) (const 1'd1))
- )
- )
- )
- """)
-
- def test_Elif_wrong(self):
- m = Module()
- with self.assertRaisesRegex(SyntaxError,
- r"^Elif without preceding If$"):
- with m.Elif(self.s2):
- pass
-
- def test_Else_wrong(self):
- m = Module()
- with self.assertRaisesRegex(SyntaxError,
- r"^Else without preceding If\/Elif$"):
- with m.Else():
- pass
-
- def test_If_wide(self):
- m = Module()
- with m.If(self.w1):
- m.d.comb += self.c1.eq(1)
- m._flush()
- self.assertRepr(m._statements, """
- (
- (switch (cat (b (sig w1)))
- (case 1 (eq (sig c1) (const 1'd1)))
- )
- )
- """)
-
- def test_If_signed_suspicious(self):
- m = Module()
- with self.assertWarnsRegex(SyntaxWarning,
- (r"^Signed values in If\/Elif conditions usually result from inverting Python "
- r"booleans with ~, which leads to unexpected results\. Replace `~flag` with "
- r"`not flag`\. \(If this is a false positive, silence this warning with "
- r"`m\.If\(x\)` → `m\.If\(x\.bool\(\)\)`\.\)$")):
- with m.If(~True):
- pass
-
- def test_Elif_signed_suspicious(self):
- m = Module()
- with m.If(0):
- pass
- with self.assertWarnsRegex(SyntaxWarning,
- (r"^Signed values in If\/Elif conditions usually result from inverting Python "
- r"booleans with ~, which leads to unexpected results\. Replace `~flag` with "
- r"`not flag`\. \(If this is a false positive, silence this warning with "
- r"`m\.If\(x\)` → `m\.If\(x\.bool\(\)\)`\.\)$")):
- with m.Elif(~True):
- pass
-
- def test_if_If_Elif_Else(self):
- m = Module()
- with self.assertRaisesRegex(SyntaxError,
- r"^`if m\.If\(\.\.\.\):` does not work; use `with m\.If\(\.\.\.\)`$"):
- if m.If(0):
- pass
- with m.If(0):
- pass
- with self.assertRaisesRegex(SyntaxError,
- r"^`if m\.Elif\(\.\.\.\):` does not work; use `with m\.Elif\(\.\.\.\)`$"):
- if m.Elif(0):
- pass
- with self.assertRaisesRegex(SyntaxError,
- r"^`if m\.Else\(\.\.\.\):` does not work; use `with m\.Else\(\.\.\.\)`$"):
- if m.Else():
- pass
-
- def test_Switch(self):
- m = Module()
- with m.Switch(self.w1):
- with m.Case(3):
- m.d.comb += self.c1.eq(1)
- with m.Case("11--"):
- m.d.comb += self.c2.eq(1)
- with m.Case("1 0--"):
- m.d.comb += self.c2.eq(1)
- m._flush()
- self.assertRepr(m._statements, """
- (
- (switch (sig w1)
- (case 0011 (eq (sig c1) (const 1'd1)))
- (case 11-- (eq (sig c2) (const 1'd1)))
- (case 10-- (eq (sig c2) (const 1'd1)))
- )
- )
- """)
-
- def test_Switch_default_Case(self):
- m = Module()
- with m.Switch(self.w1):
- with m.Case(3):
- m.d.comb += self.c1.eq(1)
- with m.Case():
- m.d.comb += self.c2.eq(1)
- m._flush()
- self.assertRepr(m._statements, """
- (
- (switch (sig w1)
- (case 0011 (eq (sig c1) (const 1'd1)))
- (default (eq (sig c2) (const 1'd1)))
- )
- )
- """)
-
- def test_Switch_default_Default(self):
- m = Module()
- with m.Switch(self.w1):
- with m.Case(3):
- m.d.comb += self.c1.eq(1)
- with m.Default():
- m.d.comb += self.c2.eq(1)
- m._flush()
- self.assertRepr(m._statements, """
- (
- (switch (sig w1)
- (case 0011 (eq (sig c1) (const 1'd1)))
- (default (eq (sig c2) (const 1'd1)))
- )
- )
- """)
-
- def test_Switch_const_test(self):
- m = Module()
- with m.Switch(1):
- with m.Case(1):
- m.d.comb += self.c1.eq(1)
- m._flush()
- self.assertRepr(m._statements, """
- (
- (switch (const 1'd1)
- (case 1 (eq (sig c1) (const 1'd1)))
- )
- )
- """)
-
- def test_Switch_enum(self):
- class Color(Enum):
- RED = 1
- BLUE = 2
- m = Module()
- se = Signal(Color)
- with m.Switch(se):
- with m.Case(Color.RED):
- m.d.comb += self.c1.eq(1)
- self.assertRepr(m._statements, """
- (
- (switch (sig se)
- (case 01 (eq (sig c1) (const 1'd1)))
- )
- )
- """)
-
- def test_Case_width_wrong(self):
- class Color(Enum):
- RED = 0b10101010
- m = Module()
- with m.Switch(self.w1):
- with self.assertRaisesRegex(SyntaxError,
- r"^Case pattern '--' must have the same width as switch value \(which is 4\)$"):
- with m.Case("--"):
- pass
- with self.assertWarnsRegex(SyntaxWarning,
- (r"^Case pattern '10110' is wider than switch value \(which has width 4\); "
- r"comparison will never be true$")):
- with m.Case(0b10110):
- pass
- with self.assertWarnsRegex(SyntaxWarning,
- (r"^Case pattern '10101010' \(Color\.RED\) is wider than switch value "
- r"\(which has width 4\); comparison will never be true$")):
- with m.Case(Color.RED):
- pass
- self.assertRepr(m._statements, """
- (
- (switch (sig w1) )
- )
- """)
-
- def test_Case_bits_wrong(self):
- m = Module()
- with m.Switch(self.w1):
- with self.assertRaisesRegex(SyntaxError,
- (r"^Case pattern 'abc' must consist of 0, 1, and - \(don't care\) bits, "
- r"and may include whitespace$")):
- with m.Case("abc"):
- pass
-
- def test_Case_pattern_wrong(self):
- m = Module()
- with m.Switch(self.w1):
- with self.assertRaisesRegex(SyntaxError,
- r"^Case pattern must be an integer, a string, or an enumeration, not 1\.0$"):
- with m.Case(1.0):
- pass
-
- def test_Case_outside_Switch_wrong(self):
- m = Module()
- with self.assertRaisesRegex(SyntaxError,
- r"^Case is not permitted outside of Switch$"):
- with m.Case():
- pass
-
- def test_If_inside_Switch_wrong(self):
- m = Module()
- with m.Switch(self.s1):
- with self.assertRaisesRegex(SyntaxError,
- (r"^If is not permitted directly inside of Switch; "
- r"it is permitted inside of Switch Case$")):
- with m.If(self.s2):
- pass
-
- def test_FSM_basic(self):
- a = Signal()
- b = Signal()
- c = Signal()
- m = Module()
- with m.FSM():
- with m.State("FIRST"):
- m.d.comb += a.eq(1)
- m.next = "SECOND"
- with m.State("SECOND"):
- m.d.sync += b.eq(~b)
- with m.If(c):
- m.next = "FIRST"
- m._flush()
- self.assertRepr(m._statements, """
- (
- (switch (sig fsm_state)
- (case 0
- (eq (sig a) (const 1'd1))
- (eq (sig fsm_state) (const 1'd1))
- )
- (case 1
- (eq (sig b) (~ (sig b)))
- (switch (cat (sig c))
- (case 1
- (eq (sig fsm_state) (const 1'd0)))
- )
- )
- )
- )
- """)
- self.assertEqual({repr(k): v for k, v in m._driving.items()}, {
- "(sig a)": None,
- "(sig fsm_state)": "sync",
- "(sig b)": "sync",
- })
-
- frag = m.elaborate(platform=None)
- fsm = frag.find_generated("fsm")
- self.assertIsInstance(fsm.state, Signal)
- self.assertEqual(fsm.encoding, OrderedDict({
- "FIRST": 0,
- "SECOND": 1,
- }))
- self.assertEqual(fsm.decoding, OrderedDict({
- 0: "FIRST",
- 1: "SECOND"
- }))
-
- def test_FSM_reset(self):
- a = Signal()
- m = Module()
- with m.FSM(reset="SECOND"):
- with m.State("FIRST"):
- m.d.comb += a.eq(0)
- m.next = "SECOND"
- with m.State("SECOND"):
- m.next = "FIRST"
- m._flush()
- self.assertRepr(m._statements, """
- (
- (switch (sig fsm_state)
- (case 0
- (eq (sig a) (const 1'd0))
- (eq (sig fsm_state) (const 1'd1))
- )
- (case 1
- (eq (sig fsm_state) (const 1'd0))
- )
- )
- )
- """)
-
- def test_FSM_ongoing(self):
- a = Signal()
- b = Signal()
- m = Module()
- with m.FSM() as fsm:
- m.d.comb += b.eq(fsm.ongoing("SECOND"))
- with m.State("FIRST"):
- pass
- m.d.comb += a.eq(fsm.ongoing("FIRST"))
- with m.State("SECOND"):
- pass
- m._flush()
- self.assertEqual(m._generated["fsm"].state.reset, 1)
- self.maxDiff = 10000
- self.assertRepr(m._statements, """
- (
- (eq (sig b) (== (sig fsm_state) (const 1'd0)))
- (eq (sig a) (== (sig fsm_state) (const 1'd1)))
- (switch (sig fsm_state)
- (case 1
- )
- (case 0
- )
- )
- )
- """)
-
- def test_FSM_empty(self):
- m = Module()
- with m.FSM():
- pass
- self.assertRepr(m._statements, """
- ()
- """)
-
- def test_FSM_wrong_domain(self):
- m = Module()
- with self.assertRaisesRegex(ValueError,
- r"^FSM may not be driven by the 'comb' domain$"):
- with m.FSM(domain="comb"):
- pass
-
- def test_FSM_wrong_undefined(self):
- m = Module()
- with self.assertRaisesRegex(NameError,
- r"^FSM state 'FOO' is referenced but not defined$"):
- with m.FSM() as fsm:
- fsm.ongoing("FOO")
-
- def test_FSM_wrong_redefined(self):
- m = Module()
- with m.FSM():
- with m.State("FOO"):
- pass
- with self.assertRaisesRegex(NameError,
- r"^FSM state 'FOO' is already defined$"):
- with m.State("FOO"):
- pass
-
- def test_FSM_wrong_next(self):
- m = Module()
- with self.assertRaisesRegex(SyntaxError,
- r"^Only assignment to `m\.next` is permitted$"):
- m.next
- with self.assertRaisesRegex(SyntaxError,
- r"^`m\.next = <\.\.\.>` is only permitted inside an FSM state$"):
- m.next = "FOO"
- with self.assertRaisesRegex(SyntaxError,
- r"^`m\.next = <\.\.\.>` is only permitted inside an FSM state$"):
- with m.FSM():
- m.next = "FOO"
-
- def test_If_inside_FSM_wrong(self):
- m = Module()
- with m.FSM():
- with m.State("FOO"):
- pass
- with self.assertRaisesRegex(SyntaxError,
- (r"^If is not permitted directly inside of FSM; "
- r"it is permitted inside of FSM State$")):
- with m.If(self.s2):
- pass
-
- def test_auto_pop_ctrl(self):
- m = Module()
- with m.If(self.w1):
- m.d.comb += self.c1.eq(1)
- m.d.comb += self.c2.eq(1)
- self.assertRepr(m._statements, """
- (
- (switch (cat (b (sig w1)))
- (case 1 (eq (sig c1) (const 1'd1)))
- )
- (eq (sig c2) (const 1'd1))
- )
- """)
-
- def test_submodule_anon(self):
- m1 = Module()
- m2 = Module()
- m1.submodules += m2
- self.assertEqual(m1._anon_submodules, [m2])
- self.assertEqual(m1._named_submodules, {})
-
- def test_submodule_anon_multi(self):
- m1 = Module()
- m2 = Module()
- m3 = Module()
- m1.submodules += m2, m3
- self.assertEqual(m1._anon_submodules, [m2, m3])
- self.assertEqual(m1._named_submodules, {})
-
- def test_submodule_named(self):
- m1 = Module()
- m2 = Module()
- m1.submodules.foo = m2
- self.assertEqual(m1._anon_submodules, [])
- self.assertEqual(m1._named_submodules, {"foo": m2})
-
- def test_submodule_named_index(self):
- m1 = Module()
- m2 = Module()
- m1.submodules["foo"] = m2
- self.assertEqual(m1._anon_submodules, [])
- self.assertEqual(m1._named_submodules, {"foo": m2})
-
- def test_submodule_wrong(self):
- m = Module()
- with self.assertRaisesRegex(TypeError,
- r"^Trying to add 1, which does not implement \.elaborate\(\), as a submodule$"):
- m.submodules.foo = 1
- with self.assertRaisesRegex(TypeError,
- r"^Trying to add 1, which does not implement \.elaborate\(\), as a submodule$"):
- m.submodules += 1
-
- def test_submodule_named_conflict(self):
- m1 = Module()
- m2 = Module()
- m1.submodules.foo = m2
- with self.assertRaisesRegex(NameError, r"^Submodule named 'foo' already exists$"):
- m1.submodules.foo = m2
-
- def test_submodule_get(self):
- m1 = Module()
- m2 = Module()
- m1.submodules.foo = m2
- m3 = m1.submodules.foo
- self.assertEqual(m2, m3)
-
- def test_submodule_get_index(self):
- m1 = Module()
- m2 = Module()
- m1.submodules["foo"] = m2
- m3 = m1.submodules["foo"]
- self.assertEqual(m2, m3)
-
- def test_submodule_get_unset(self):
- m1 = Module()
- with self.assertRaisesRegex(AttributeError, r"^No submodule named 'foo' exists$"):
- m2 = m1.submodules.foo
- with self.assertRaisesRegex(AttributeError, r"^No submodule named 'foo' exists$"):
- m2 = m1.submodules["foo"]
-
- def test_domain_named_implicit(self):
- m = Module()
- m.domains += ClockDomain("sync")
- self.assertEqual(len(m._domains), 1)
-
- def test_domain_named_explicit(self):
- m = Module()
- m.domains.foo = ClockDomain()
- self.assertEqual(len(m._domains), 1)
- self.assertEqual(m._domains["foo"].name, "foo")
-
- def test_domain_add_wrong(self):
- m = Module()
- with self.assertRaisesRegex(TypeError,
- r"^Only clock domains may be added to `m\.domains`, not 1$"):
- m.domains.foo = 1
- with self.assertRaisesRegex(TypeError,
- r"^Only clock domains may be added to `m\.domains`, not 1$"):
- m.domains += 1
-
- def test_domain_add_wrong_name(self):
- m = Module()
- with self.assertRaisesRegex(NameError,
- r"^Clock domain name 'bar' must match name in `m\.domains\.foo \+= \.\.\.` syntax$"):
- m.domains.foo = ClockDomain("bar")
-
- def test_domain_add_wrong_duplicate(self):
- m = Module()
- m.domains += ClockDomain("foo")
- with self.assertRaisesRegex(NameError,
- r"^Clock domain named 'foo' already exists$"):
- m.domains += ClockDomain("foo")
-
- def test_lower(self):
- m1 = Module()
- m1.d.comb += self.c1.eq(self.s1)
- m2 = Module()
- m2.d.comb += self.c2.eq(self.s2)
- m2.d.sync += self.c3.eq(self.s3)
- m1.submodules.foo = m2
-
- f1 = m1.elaborate(platform=None)
- self.assertRepr(f1.statements, """
- (
- (eq (sig c1) (sig s1))
- )
- """)
- self.assertEqual(f1.drivers, {
- None: SignalSet((self.c1,))
- })
- self.assertEqual(len(f1.subfragments), 1)
- (f2, f2_name), = f1.subfragments
- self.assertEqual(f2_name, "foo")
- self.assertRepr(f2.statements, """
- (
- (eq (sig c2) (sig s2))
- (eq (sig c3) (sig s3))
- )
- """)
- self.assertEqual(f2.drivers, {
- None: SignalSet((self.c2,)),
- "sync": SignalSet((self.c3,))
- })
- self.assertEqual(len(f2.subfragments), 0)
+++ /dev/null
-# nmigen: UnusedElaboratable=no
-
-from collections import OrderedDict
-
-from ..hdl.ast import *
-from ..hdl.cd import *
-from ..hdl.ir import *
-from ..hdl.mem import *
-from .utils import *
-
-
-class BadElaboratable(Elaboratable):
- def elaborate(self, platform):
- return
-
-
-class FragmentGetTestCase(FHDLTestCase):
- def test_get_wrong(self):
- with self.assertRaisesRegex(AttributeError,
- r"^Object None cannot be elaborated$"):
- Fragment.get(None, platform=None)
-
- with self.assertWarnsRegex(UserWarning,
- r"^\.elaborate\(\) returned None; missing return statement\?$"):
- with self.assertRaisesRegex(AttributeError,
- r"^Object None cannot be elaborated$"):
- Fragment.get(BadElaboratable(), platform=None)
-
-
-class FragmentGeneratedTestCase(FHDLTestCase):
- def test_find_subfragment(self):
- f1 = Fragment()
- f2 = Fragment()
- f1.add_subfragment(f2, "f2")
-
- self.assertEqual(f1.find_subfragment(0), f2)
- self.assertEqual(f1.find_subfragment("f2"), f2)
-
- def test_find_subfragment_wrong(self):
- f1 = Fragment()
- f2 = Fragment()
- f1.add_subfragment(f2, "f2")
-
- with self.assertRaisesRegex(NameError,
- r"^No subfragment at index #1$"):
- f1.find_subfragment(1)
- with self.assertRaisesRegex(NameError,
- r"^No subfragment with name 'fx'$"):
- f1.find_subfragment("fx")
-
- def test_find_generated(self):
- f1 = Fragment()
- f2 = Fragment()
- f2.generated["sig"] = sig = Signal()
- f1.add_subfragment(f2, "f2")
-
- self.assertEqual(SignalKey(f1.find_generated("f2", "sig")),
- SignalKey(sig))
-
-
-class FragmentDriversTestCase(FHDLTestCase):
- def test_empty(self):
- f = Fragment()
- self.assertEqual(list(f.iter_comb()), [])
- self.assertEqual(list(f.iter_sync()), [])
-
-
-class FragmentPortsTestCase(FHDLTestCase):
- def setUp(self):
- self.s1 = Signal()
- self.s2 = Signal()
- self.s3 = Signal()
- self.c1 = Signal()
- self.c2 = Signal()
- self.c3 = Signal()
-
- def test_empty(self):
- f = Fragment()
- self.assertEqual(list(f.iter_ports()), [])
-
- f._propagate_ports(ports=(), all_undef_as_ports=True)
- self.assertEqual(f.ports, SignalDict([]))
-
- def test_iter_signals(self):
- f = Fragment()
- f.add_ports(self.s1, self.s2, dir="io")
- self.assertEqual(SignalSet((self.s1, self.s2)), f.iter_signals())
-
- def test_self_contained(self):
- f = Fragment()
- f.add_statements(
- self.c1.eq(self.s1),
- self.s1.eq(self.c1)
- )
-
- f._propagate_ports(ports=(), all_undef_as_ports=True)
- self.assertEqual(f.ports, SignalDict([]))
-
- def test_infer_input(self):
- f = Fragment()
- f.add_statements(
- self.c1.eq(self.s1)
- )
-
- f._propagate_ports(ports=(), all_undef_as_ports=True)
- self.assertEqual(f.ports, SignalDict([
- (self.s1, "i")
- ]))
-
- def test_request_output(self):
- f = Fragment()
- f.add_statements(
- self.c1.eq(self.s1)
- )
-
- f._propagate_ports(ports=(self.c1,), all_undef_as_ports=True)
- self.assertEqual(f.ports, SignalDict([
- (self.s1, "i"),
- (self.c1, "o")
- ]))
-
- def test_input_in_subfragment(self):
- f1 = Fragment()
- f1.add_statements(
- self.c1.eq(self.s1)
- )
- f2 = Fragment()
- f2.add_statements(
- self.s1.eq(0)
- )
- f1.add_subfragment(f2)
- f1._propagate_ports(ports=(), all_undef_as_ports=True)
- self.assertEqual(f1.ports, SignalDict())
- self.assertEqual(f2.ports, SignalDict([
- (self.s1, "o"),
- ]))
-
- def test_input_only_in_subfragment(self):
- f1 = Fragment()
- f2 = Fragment()
- f2.add_statements(
- self.c1.eq(self.s1)
- )
- f1.add_subfragment(f2)
- f1._propagate_ports(ports=(), all_undef_as_ports=True)
- self.assertEqual(f1.ports, SignalDict([
- (self.s1, "i"),
- ]))
- self.assertEqual(f2.ports, SignalDict([
- (self.s1, "i"),
- ]))
-
- def test_output_from_subfragment(self):
- f1 = Fragment()
- f1.add_statements(
- self.c1.eq(0)
- )
- f2 = Fragment()
- f2.add_statements(
- self.c2.eq(1)
- )
- f1.add_subfragment(f2)
-
- f1._propagate_ports(ports=(self.c2,), all_undef_as_ports=True)
- self.assertEqual(f1.ports, SignalDict([
- (self.c2, "o"),
- ]))
- self.assertEqual(f2.ports, SignalDict([
- (self.c2, "o"),
- ]))
-
- def test_output_from_subfragment_2(self):
- f1 = Fragment()
- f1.add_statements(
- self.c1.eq(self.s1)
- )
- f2 = Fragment()
- f2.add_statements(
- self.c2.eq(self.s1)
- )
- f1.add_subfragment(f2)
- f3 = Fragment()
- f3.add_statements(
- self.s1.eq(0)
- )
- f2.add_subfragment(f3)
-
- f1._propagate_ports(ports=(), all_undef_as_ports=True)
- self.assertEqual(f2.ports, SignalDict([
- (self.s1, "o"),
- ]))
-
- def test_input_output_sibling(self):
- f1 = Fragment()
- f2 = Fragment()
- f2.add_statements(
- self.c1.eq(self.c2)
- )
- f1.add_subfragment(f2)
- f3 = Fragment()
- f3.add_statements(
- self.c2.eq(0)
- )
- f3.add_driver(self.c2)
- f1.add_subfragment(f3)
-
- f1._propagate_ports(ports=(), all_undef_as_ports=True)
- self.assertEqual(f1.ports, SignalDict())
-
- def test_output_input_sibling(self):
- f1 = Fragment()
- f2 = Fragment()
- f2.add_statements(
- self.c2.eq(0)
- )
- f2.add_driver(self.c2)
- f1.add_subfragment(f2)
- f3 = Fragment()
- f3.add_statements(
- self.c1.eq(self.c2)
- )
- f1.add_subfragment(f3)
-
- f1._propagate_ports(ports=(), all_undef_as_ports=True)
- self.assertEqual(f1.ports, SignalDict())
-
- def test_input_cd(self):
- sync = ClockDomain()
- f = Fragment()
- f.add_statements(
- self.c1.eq(self.s1)
- )
- f.add_domains(sync)
- f.add_driver(self.c1, "sync")
-
- f._propagate_ports(ports=(), all_undef_as_ports=True)
- self.assertEqual(f.ports, SignalDict([
- (self.s1, "i"),
- (sync.clk, "i"),
- (sync.rst, "i"),
- ]))
-
- def test_input_cd_reset_less(self):
- sync = ClockDomain(reset_less=True)
- f = Fragment()
- f.add_statements(
- self.c1.eq(self.s1)
- )
- f.add_domains(sync)
- f.add_driver(self.c1, "sync")
-
- f._propagate_ports(ports=(), all_undef_as_ports=True)
- self.assertEqual(f.ports, SignalDict([
- (self.s1, "i"),
- (sync.clk, "i"),
- ]))
-
- def test_inout(self):
- s = Signal()
- f1 = Fragment()
- f2 = Instance("foo", io_x=s)
- f1.add_subfragment(f2)
-
- f1._propagate_ports(ports=(), all_undef_as_ports=True)
- self.assertEqual(f1.ports, SignalDict([
- (s, "io")
- ]))
-
- def test_in_out_same_signal(self):
- s = Signal()
-
- f1 = Instance("foo", i_x=s, o_y=s)
- f2 = Fragment()
- f2.add_subfragment(f1)
-
- f2._propagate_ports(ports=(), all_undef_as_ports=True)
- self.assertEqual(f1.ports, SignalDict([
- (s, "o")
- ]))
-
- f3 = Instance("foo", o_y=s, i_x=s)
- f4 = Fragment()
- f4.add_subfragment(f3)
-
- f4._propagate_ports(ports=(), all_undef_as_ports=True)
- self.assertEqual(f3.ports, SignalDict([
- (s, "o")
- ]))
-
- def test_clk_rst(self):
- sync = ClockDomain()
- f = Fragment()
- f.add_domains(sync)
-
- f = f.prepare(ports=(ClockSignal("sync"), ResetSignal("sync")))
- self.assertEqual(f.ports, SignalDict([
- (sync.clk, "i"),
- (sync.rst, "i"),
- ]))
-
- def test_port_wrong(self):
- f = Fragment()
- with self.assertRaisesRegex(TypeError,
- r"^Only signals may be added as ports, not \(const 1'd1\)$"):
- f.prepare(ports=(Const(1),))
-
- def test_port_not_iterable(self):
- f = Fragment()
- with self.assertRaisesRegex(TypeError,
- r"^`ports` must be either a list or a tuple, not 1$"):
- f.prepare(ports=1)
- with self.assertRaisesRegex(TypeError,
- (r"^`ports` must be either a list or a tuple, not \(const 1'd1\)"
- r" \(did you mean `ports=\(<signal>,\)`, rather than `ports=<signal>`\?\)$")):
- f.prepare(ports=Const(1))
-
-class FragmentDomainsTestCase(FHDLTestCase):
- def test_iter_signals(self):
- cd1 = ClockDomain()
- cd2 = ClockDomain(reset_less=True)
- s1 = Signal()
- s2 = Signal()
-
- f = Fragment()
- f.add_domains(cd1, cd2)
- f.add_driver(s1, "cd1")
- self.assertEqual(SignalSet((cd1.clk, cd1.rst, s1)), f.iter_signals())
- f.add_driver(s2, "cd2")
- self.assertEqual(SignalSet((cd1.clk, cd1.rst, cd2.clk, s1, s2)), f.iter_signals())
-
- def test_propagate_up(self):
- cd = ClockDomain()
-
- f1 = Fragment()
- f2 = Fragment()
- f1.add_subfragment(f2)
- f2.add_domains(cd)
-
- f1._propagate_domains_up()
- self.assertEqual(f1.domains, {"cd": cd})
-
- def test_propagate_up_local(self):
- cd = ClockDomain(local=True)
-
- f1 = Fragment()
- f2 = Fragment()
- f1.add_subfragment(f2)
- f2.add_domains(cd)
-
- f1._propagate_domains_up()
- self.assertEqual(f1.domains, {})
-
- def test_domain_conflict(self):
- cda = ClockDomain("sync")
- cdb = ClockDomain("sync")
-
- fa = Fragment()
- fa.add_domains(cda)
- fb = Fragment()
- fb.add_domains(cdb)
- f = Fragment()
- f.add_subfragment(fa, "a")
- f.add_subfragment(fb, "b")
-
- f._propagate_domains_up()
- self.assertEqual(f.domains, {"a_sync": cda, "b_sync": cdb})
- (fa, _), (fb, _) = f.subfragments
- self.assertEqual(fa.domains, {"a_sync": cda})
- self.assertEqual(fb.domains, {"b_sync": cdb})
-
- def test_domain_conflict_anon(self):
- cda = ClockDomain("sync")
- cdb = ClockDomain("sync")
-
- fa = Fragment()
- fa.add_domains(cda)
- fb = Fragment()
- fb.add_domains(cdb)
- f = Fragment()
- f.add_subfragment(fa, "a")
- f.add_subfragment(fb)
-
- with self.assertRaisesRegex(DomainError,
- (r"^Domain 'sync' is defined by subfragments 'a', <unnamed #1> of fragment "
- r"'top'; it is necessary to either rename subfragment domains explicitly, "
- r"or give names to subfragments$")):
- f._propagate_domains_up()
-
- def test_domain_conflict_name(self):
- cda = ClockDomain("sync")
- cdb = ClockDomain("sync")
-
- fa = Fragment()
- fa.add_domains(cda)
- fb = Fragment()
- fb.add_domains(cdb)
- f = Fragment()
- f.add_subfragment(fa, "x")
- f.add_subfragment(fb, "x")
-
- with self.assertRaisesRegex(DomainError,
- (r"^Domain 'sync' is defined by subfragments #0, #1 of fragment 'top', some "
- r"of which have identical names; it is necessary to either rename subfragment "
- r"domains explicitly, or give distinct names to subfragments$")):
- f._propagate_domains_up()
-
- def test_domain_conflict_rename_drivers(self):
- cda = ClockDomain("sync")
- cdb = ClockDomain("sync")
-
- fa = Fragment()
- fa.add_domains(cda)
- fb = Fragment()
- fb.add_domains(cdb)
- fb.add_driver(ResetSignal("sync"), None)
- f = Fragment()
- f.add_subfragment(fa, "a")
- f.add_subfragment(fb, "b")
-
- f._propagate_domains_up()
- fb_new, _ = f.subfragments[1]
- self.assertEqual(fb_new.drivers, OrderedDict({
- None: SignalSet((ResetSignal("b_sync"),))
- }))
-
- def test_domain_conflict_rename_drivers(self):
- cda = ClockDomain("sync")
- cdb = ClockDomain("sync")
- s = Signal()
-
- fa = Fragment()
- fa.add_domains(cda)
- fb = Fragment()
- fb.add_domains(cdb)
- f = Fragment()
- f.add_subfragment(fa, "a")
- f.add_subfragment(fb, "b")
- f.add_driver(s, "b_sync")
-
- f._propagate_domains(lambda name: ClockDomain(name))
-
- def test_propagate_down(self):
- cd = ClockDomain()
-
- f1 = Fragment()
- f2 = Fragment()
- f1.add_domains(cd)
- f1.add_subfragment(f2)
-
- f1._propagate_domains_down()
- self.assertEqual(f2.domains, {"cd": cd})
-
- def test_propagate_down_idempotent(self):
- cd = ClockDomain()
-
- f1 = Fragment()
- f1.add_domains(cd)
- f2 = Fragment()
- f2.add_domains(cd)
- f1.add_subfragment(f2)
-
- f1._propagate_domains_down()
- self.assertEqual(f1.domains, {"cd": cd})
- self.assertEqual(f2.domains, {"cd": cd})
-
- def test_propagate(self):
- cd = ClockDomain()
-
- f1 = Fragment()
- f2 = Fragment()
- f1.add_domains(cd)
- f1.add_subfragment(f2)
-
- new_domains = f1._propagate_domains(missing_domain=lambda name: None)
- self.assertEqual(f1.domains, {"cd": cd})
- self.assertEqual(f2.domains, {"cd": cd})
- self.assertEqual(new_domains, [])
-
- def test_propagate_missing(self):
- s1 = Signal()
- f1 = Fragment()
- f1.add_driver(s1, "sync")
-
- with self.assertRaisesRegex(DomainError,
- r"^Domain 'sync' is used but not defined$"):
- f1._propagate_domains(missing_domain=lambda name: None)
-
- def test_propagate_create_missing(self):
- s1 = Signal()
- f1 = Fragment()
- f1.add_driver(s1, "sync")
- f2 = Fragment()
- f1.add_subfragment(f2)
-
- new_domains = f1._propagate_domains(missing_domain=lambda name: ClockDomain(name))
- self.assertEqual(f1.domains.keys(), {"sync"})
- self.assertEqual(f2.domains.keys(), {"sync"})
- self.assertEqual(f1.domains["sync"], f2.domains["sync"])
- self.assertEqual(new_domains, [f1.domains["sync"]])
-
- def test_propagate_create_missing_fragment(self):
- s1 = Signal()
- f1 = Fragment()
- f1.add_driver(s1, "sync")
-
- cd = ClockDomain("sync")
- f2 = Fragment()
- f2.add_domains(cd)
-
- new_domains = f1._propagate_domains(missing_domain=lambda name: f2)
- self.assertEqual(f1.domains.keys(), {"sync"})
- self.assertEqual(f1.domains["sync"], f2.domains["sync"])
- self.assertEqual(new_domains, [])
- self.assertEqual(f1.subfragments, [
- (f2, "cd_sync")
- ])
-
- def test_propagate_create_missing_fragment_many_domains(self):
- s1 = Signal()
- f1 = Fragment()
- f1.add_driver(s1, "sync")
-
- cd_por = ClockDomain("por")
- cd_sync = ClockDomain("sync")
- f2 = Fragment()
- f2.add_domains(cd_por, cd_sync)
-
- new_domains = f1._propagate_domains(missing_domain=lambda name: f2)
- self.assertEqual(f1.domains.keys(), {"sync", "por"})
- self.assertEqual(f2.domains.keys(), {"sync", "por"})
- self.assertEqual(f1.domains["sync"], f2.domains["sync"])
- self.assertEqual(new_domains, [])
- self.assertEqual(f1.subfragments, [
- (f2, "cd_sync")
- ])
-
- def test_propagate_create_missing_fragment_wrong(self):
- s1 = Signal()
- f1 = Fragment()
- f1.add_driver(s1, "sync")
-
- f2 = Fragment()
- f2.add_domains(ClockDomain("foo"))
-
- with self.assertRaisesRegex(DomainError,
- (r"^Fragment returned by missing domain callback does not define requested "
- r"domain 'sync' \(defines 'foo'\)\.$")):
- f1._propagate_domains(missing_domain=lambda name: f2)
-
-
-class FragmentHierarchyConflictTestCase(FHDLTestCase):
- def setUp_self_sub(self):
- self.s1 = Signal()
- self.c1 = Signal()
- self.c2 = Signal()
-
- self.f1 = Fragment()
- self.f1.add_statements(self.c1.eq(0))
- self.f1.add_driver(self.s1)
- self.f1.add_driver(self.c1, "sync")
-
- self.f1a = Fragment()
- self.f1.add_subfragment(self.f1a, "f1a")
-
- self.f2 = Fragment()
- self.f2.add_statements(self.c2.eq(1))
- self.f2.add_driver(self.s1)
- self.f2.add_driver(self.c2, "sync")
- self.f1.add_subfragment(self.f2)
-
- self.f1b = Fragment()
- self.f1.add_subfragment(self.f1b, "f1b")
-
- self.f2a = Fragment()
- self.f2.add_subfragment(self.f2a, "f2a")
-
- def test_conflict_self_sub(self):
- self.setUp_self_sub()
-
- self.f1._resolve_hierarchy_conflicts(mode="silent")
- self.assertEqual(self.f1.subfragments, [
- (self.f1a, "f1a"),
- (self.f1b, "f1b"),
- (self.f2a, "f2a"),
- ])
- self.assertRepr(self.f1.statements, """
- (
- (eq (sig c1) (const 1'd0))
- (eq (sig c2) (const 1'd1))
- )
- """)
- self.assertEqual(self.f1.drivers, {
- None: SignalSet((self.s1,)),
- "sync": SignalSet((self.c1, self.c2)),
- })
-
- def test_conflict_self_sub_error(self):
- self.setUp_self_sub()
-
- with self.assertRaisesRegex(DriverConflict,
- r"^Signal '\(sig s1\)' is driven from multiple fragments: top, top.<unnamed #1>$"):
- self.f1._resolve_hierarchy_conflicts(mode="error")
-
- def test_conflict_self_sub_warning(self):
- self.setUp_self_sub()
-
- with self.assertWarnsRegex(DriverConflict,
- (r"^Signal '\(sig s1\)' is driven from multiple fragments: top, top.<unnamed #1>; "
- r"hierarchy will be flattened$")):
- self.f1._resolve_hierarchy_conflicts(mode="warn")
-
- def setUp_sub_sub(self):
- self.s1 = Signal()
- self.c1 = Signal()
- self.c2 = Signal()
-
- self.f1 = Fragment()
-
- self.f2 = Fragment()
- self.f2.add_driver(self.s1)
- self.f2.add_statements(self.c1.eq(0))
- self.f1.add_subfragment(self.f2)
-
- self.f3 = Fragment()
- self.f3.add_driver(self.s1)
- self.f3.add_statements(self.c2.eq(1))
- self.f1.add_subfragment(self.f3)
-
- def test_conflict_sub_sub(self):
- self.setUp_sub_sub()
-
- self.f1._resolve_hierarchy_conflicts(mode="silent")
- self.assertEqual(self.f1.subfragments, [])
- self.assertRepr(self.f1.statements, """
- (
- (eq (sig c1) (const 1'd0))
- (eq (sig c2) (const 1'd1))
- )
- """)
-
- def setUp_self_subsub(self):
- self.s1 = Signal()
- self.c1 = Signal()
- self.c2 = Signal()
-
- self.f1 = Fragment()
- self.f1.add_driver(self.s1)
-
- self.f2 = Fragment()
- self.f2.add_statements(self.c1.eq(0))
- self.f1.add_subfragment(self.f2)
-
- self.f3 = Fragment()
- self.f3.add_driver(self.s1)
- self.f3.add_statements(self.c2.eq(1))
- self.f2.add_subfragment(self.f3)
-
- def test_conflict_self_subsub(self):
- self.setUp_self_subsub()
-
- self.f1._resolve_hierarchy_conflicts(mode="silent")
- self.assertEqual(self.f1.subfragments, [])
- self.assertRepr(self.f1.statements, """
- (
- (eq (sig c1) (const 1'd0))
- (eq (sig c2) (const 1'd1))
- )
- """)
-
- def setUp_memory(self):
- self.m = Memory(width=8, depth=4)
- self.fr = self.m.read_port().elaborate(platform=None)
- self.fw = self.m.write_port().elaborate(platform=None)
- self.f1 = Fragment()
- self.f2 = Fragment()
- self.f2.add_subfragment(self.fr)
- self.f1.add_subfragment(self.f2)
- self.f3 = Fragment()
- self.f3.add_subfragment(self.fw)
- self.f1.add_subfragment(self.f3)
-
- def test_conflict_memory(self):
- self.setUp_memory()
-
- self.f1._resolve_hierarchy_conflicts(mode="silent")
- self.assertEqual(self.f1.subfragments, [
- (self.fr, None),
- (self.fw, None),
- ])
-
- def test_conflict_memory_error(self):
- self.setUp_memory()
-
- with self.assertRaisesRegex(DriverConflict,
- r"^Memory 'm' is accessed from multiple fragments: top\.<unnamed #0>, "
- r"top\.<unnamed #1>$"):
- self.f1._resolve_hierarchy_conflicts(mode="error")
-
- def test_conflict_memory_warning(self):
- self.setUp_memory()
-
- with self.assertWarnsRegex(DriverConflict,
- (r"^Memory 'm' is accessed from multiple fragments: top.<unnamed #0>, "
- r"top.<unnamed #1>; hierarchy will be flattened$")):
- self.f1._resolve_hierarchy_conflicts(mode="warn")
-
- def test_explicit_flatten(self):
- self.f1 = Fragment()
- self.f2 = Fragment()
- self.f2.flatten = True
- self.f1.add_subfragment(self.f2)
-
- self.f1._resolve_hierarchy_conflicts(mode="silent")
- self.assertEqual(self.f1.subfragments, [])
-
- def test_no_conflict_local_domains(self):
- f1 = Fragment()
- cd1 = ClockDomain("d", local=True)
- f1.add_domains(cd1)
- f1.add_driver(ClockSignal("d"))
- f2 = Fragment()
- cd2 = ClockDomain("d", local=True)
- f2.add_domains(cd2)
- f2.add_driver(ClockSignal("d"))
- f3 = Fragment()
- f3.add_subfragment(f1)
- f3.add_subfragment(f2)
- f3.prepare()
-
-
-class InstanceTestCase(FHDLTestCase):
- def test_construct(self):
- s1 = Signal()
- s2 = Signal()
- s3 = Signal()
- s4 = Signal()
- s5 = Signal()
- s6 = Signal()
- inst = Instance("foo",
- ("a", "ATTR1", 1),
- ("p", "PARAM1", 0x1234),
- ("i", "s1", s1),
- ("o", "s2", s2),
- ("io", "s3", s3),
- a_ATTR2=2,
- p_PARAM2=0x5678,
- i_s4=s4,
- o_s5=s5,
- io_s6=s6,
- )
- self.assertEqual(inst.attrs, OrderedDict([
- ("ATTR1", 1),
- ("ATTR2", 2),
- ]))
- self.assertEqual(inst.parameters, OrderedDict([
- ("PARAM1", 0x1234),
- ("PARAM2", 0x5678),
- ]))
- self.assertEqual(inst.named_ports, OrderedDict([
- ("s1", (s1, "i")),
- ("s2", (s2, "o")),
- ("s3", (s3, "io")),
- ("s4", (s4, "i")),
- ("s5", (s5, "o")),
- ("s6", (s6, "io")),
- ]))
-
- def test_cast_ports(self):
- inst = Instance("foo",
- ("i", "s1", 1),
- ("o", "s2", 2),
- ("io", "s3", 3),
- i_s4=4,
- o_s5=5,
- io_s6=6,
- )
- self.assertRepr(inst.named_ports["s1"][0], "(const 1'd1)")
- self.assertRepr(inst.named_ports["s2"][0], "(const 2'd2)")
- self.assertRepr(inst.named_ports["s3"][0], "(const 2'd3)")
- self.assertRepr(inst.named_ports["s4"][0], "(const 3'd4)")
- self.assertRepr(inst.named_ports["s5"][0], "(const 3'd5)")
- self.assertRepr(inst.named_ports["s6"][0], "(const 3'd6)")
-
- def test_wrong_construct_arg(self):
- s = Signal()
- with self.assertRaisesRegex(NameError,
- (r"^Instance argument \('', 's1', \(sig s\)\) should be a tuple "
- r"\(kind, name, value\) where kind is one of \"p\", \"i\", \"o\", or \"io\"$")):
- Instance("foo", ("", "s1", s))
-
- def test_wrong_construct_kwarg(self):
- s = Signal()
- with self.assertRaisesRegex(NameError,
- (r"^Instance keyword argument x_s1=\(sig s\) does not start with one of "
- r"\"p_\", \"i_\", \"o_\", or \"io_\"$")):
- Instance("foo", x_s1=s)
-
- def setUp_cpu(self):
- self.rst = Signal()
- self.stb = Signal()
- self.pins = Signal(8)
- self.datal = Signal(4)
- self.datah = Signal(4)
- self.inst = Instance("cpu",
- p_RESET=0x1234,
- i_clk=ClockSignal(),
- i_rst=self.rst,
- o_stb=self.stb,
- o_data=Cat(self.datal, self.datah),
- io_pins=self.pins[:]
- )
- self.wrap = Fragment()
- self.wrap.add_subfragment(self.inst)
-
- def test_init(self):
- self.setUp_cpu()
- f = self.inst
- self.assertEqual(f.type, "cpu")
- self.assertEqual(f.parameters, OrderedDict([("RESET", 0x1234)]))
- self.assertEqual(list(f.named_ports.keys()), ["clk", "rst", "stb", "data", "pins"])
- self.assertEqual(f.ports, SignalDict([]))
-
- def test_prepare(self):
- self.setUp_cpu()
- f = self.wrap.prepare()
- sync_clk = f.domains["sync"].clk
- self.assertEqual(f.ports, SignalDict([
- (sync_clk, "i"),
- (self.rst, "i"),
- (self.pins, "io"),
- ]))
-
- def test_prepare_explicit_ports(self):
- self.setUp_cpu()
- f = self.wrap.prepare(ports=[self.rst, self.stb])
- sync_clk = f.domains["sync"].clk
- sync_rst = f.domains["sync"].rst
- self.assertEqual(f.ports, SignalDict([
- (sync_clk, "i"),
- (sync_rst, "i"),
- (self.rst, "i"),
- (self.stb, "o"),
- (self.pins, "io"),
- ]))
-
- def test_prepare_slice_in_port(self):
- s = Signal(2)
- f = Fragment()
- f.add_subfragment(Instance("foo", o_O=s[0]))
- f.add_subfragment(Instance("foo", o_O=s[1]))
- fp = f.prepare(ports=[s], missing_domain=lambda name: None)
- self.assertEqual(fp.ports, SignalDict([
- (s, "o"),
- ]))
-
- def test_prepare_attrs(self):
- self.setUp_cpu()
- self.inst.attrs["ATTR"] = 1
- f = self.inst.prepare()
- self.assertEqual(f.attrs, OrderedDict([
- ("ATTR", 1),
- ]))
+++ /dev/null
-# nmigen: UnusedElaboratable=no
-
-from ..hdl.ast import *
-from ..hdl.mem import *
-from .utils import *
-
-
-class MemoryTestCase(FHDLTestCase):
- def test_name(self):
- m1 = Memory(width=8, depth=4)
- self.assertEqual(m1.name, "m1")
- m2 = [Memory(width=8, depth=4)][0]
- self.assertEqual(m2.name, "$memory")
- m3 = Memory(width=8, depth=4, name="foo")
- self.assertEqual(m3.name, "foo")
-
- def test_geometry(self):
- m = Memory(width=8, depth=4)
- self.assertEqual(m.width, 8)
- self.assertEqual(m.depth, 4)
-
- def test_geometry_wrong(self):
- with self.assertRaisesRegex(TypeError,
- r"^Memory width must be a non-negative integer, not -1$"):
- m = Memory(width=-1, depth=4)
- with self.assertRaisesRegex(TypeError,
- r"^Memory depth must be a non-negative integer, not -1$"):
- m = Memory(width=8, depth=-1)
-
- def test_init(self):
- m = Memory(width=8, depth=4, init=range(4))
- self.assertEqual(m.init, [0, 1, 2, 3])
-
- def test_init_wrong_count(self):
- with self.assertRaisesRegex(ValueError,
- r"^Memory initialization value count exceed memory depth \(8 > 4\)$"):
- m = Memory(width=8, depth=4, init=range(8))
-
- def test_init_wrong_type(self):
- with self.assertRaisesRegex(TypeError,
- (r"^Memory initialization value at address 1: "
- r"'str' object cannot be interpreted as an integer$")):
- m = Memory(width=8, depth=4, init=[1, "0"])
-
- def test_attrs(self):
- m1 = Memory(width=8, depth=4)
- self.assertEqual(m1.attrs, {})
- m2 = Memory(width=8, depth=4, attrs={"ram_block": True})
- self.assertEqual(m2.attrs, {"ram_block": True})
-
- def test_read_port_transparent(self):
- mem = Memory(width=8, depth=4)
- rdport = mem.read_port()
- self.assertEqual(rdport.memory, mem)
- self.assertEqual(rdport.domain, "sync")
- self.assertEqual(rdport.transparent, True)
- self.assertEqual(len(rdport.addr), 2)
- self.assertEqual(len(rdport.data), 8)
- self.assertEqual(len(rdport.en), 1)
- self.assertIsInstance(rdport.en, Const)
- self.assertEqual(rdport.en.value, 1)
-
- def test_read_port_non_transparent(self):
- mem = Memory(width=8, depth=4)
- rdport = mem.read_port(transparent=False)
- self.assertEqual(rdport.memory, mem)
- self.assertEqual(rdport.domain, "sync")
- self.assertEqual(rdport.transparent, False)
- self.assertEqual(len(rdport.en), 1)
- self.assertIsInstance(rdport.en, Signal)
- self.assertEqual(rdport.en.reset, 1)
-
- def test_read_port_asynchronous(self):
- mem = Memory(width=8, depth=4)
- rdport = mem.read_port(domain="comb")
- self.assertEqual(rdport.memory, mem)
- self.assertEqual(rdport.domain, "comb")
- self.assertEqual(rdport.transparent, True)
- self.assertEqual(len(rdport.en), 1)
- self.assertIsInstance(rdport.en, Const)
- self.assertEqual(rdport.en.value, 1)
-
- def test_read_port_wrong(self):
- mem = Memory(width=8, depth=4)
- with self.assertRaisesRegex(ValueError,
- r"^Read port cannot be simultaneously asynchronous and non-transparent$"):
- mem.read_port(domain="comb", transparent=False)
-
- def test_write_port(self):
- mem = Memory(width=8, depth=4)
- wrport = mem.write_port()
- self.assertEqual(wrport.memory, mem)
- self.assertEqual(wrport.domain, "sync")
- self.assertEqual(wrport.granularity, 8)
- self.assertEqual(len(wrport.addr), 2)
- self.assertEqual(len(wrport.data), 8)
- self.assertEqual(len(wrport.en), 1)
-
- def test_write_port_granularity(self):
- mem = Memory(width=8, depth=4)
- wrport = mem.write_port(granularity=2)
- self.assertEqual(wrport.memory, mem)
- self.assertEqual(wrport.domain, "sync")
- self.assertEqual(wrport.granularity, 2)
- self.assertEqual(len(wrport.addr), 2)
- self.assertEqual(len(wrport.data), 8)
- self.assertEqual(len(wrport.en), 4)
-
- def test_write_port_granularity_wrong(self):
- mem = Memory(width=8, depth=4)
- with self.assertRaisesRegex(TypeError,
- r"^Write port granularity must be a non-negative integer, not -1$"):
- mem.write_port(granularity=-1)
- with self.assertRaisesRegex(ValueError,
- r"^Write port granularity must not be greater than memory width \(10 > 8\)$"):
- mem.write_port(granularity=10)
- with self.assertRaisesRegex(ValueError,
- r"^Write port granularity must divide memory width evenly$"):
- mem.write_port(granularity=3)
-
-
-class DummyPortTestCase(FHDLTestCase):
- def test_name(self):
- p1 = DummyPort(data_width=8, addr_width=2)
- self.assertEqual(p1.addr.name, "p1_addr")
- p2 = [DummyPort(data_width=8, addr_width=2)][0]
- self.assertEqual(p2.addr.name, "dummy_addr")
- p3 = DummyPort(data_width=8, addr_width=2, name="foo")
- self.assertEqual(p3.addr.name, "foo_addr")
-
- def test_sizes(self):
- p1 = DummyPort(data_width=8, addr_width=2)
- self.assertEqual(p1.addr.width, 2)
- self.assertEqual(p1.data.width, 8)
- self.assertEqual(p1.en.width, 1)
- p2 = DummyPort(data_width=8, addr_width=2, granularity=2)
- self.assertEqual(p2.en.width, 4)
+++ /dev/null
-from enum import Enum
-
-from ..hdl.ast import *
-from ..hdl.rec import *
-from .utils import *
-
-
-class UnsignedEnum(Enum):
- FOO = 1
- BAR = 2
- BAZ = 3
-
-
-class LayoutTestCase(FHDLTestCase):
- def assertFieldEqual(self, field, expected):
- (shape, dir) = field
- shape = Shape.cast(shape)
- self.assertEqual((shape, dir), expected)
-
- def test_fields(self):
- layout = Layout.cast([
- ("cyc", 1),
- ("data", signed(32)),
- ("stb", 1, DIR_FANOUT),
- ("ack", 1, DIR_FANIN),
- ("info", [
- ("a", 1),
- ("b", 1),
- ])
- ])
-
- self.assertFieldEqual(layout["cyc"], ((1, False), DIR_NONE))
- self.assertFieldEqual(layout["data"], ((32, True), DIR_NONE))
- self.assertFieldEqual(layout["stb"], ((1, False), DIR_FANOUT))
- self.assertFieldEqual(layout["ack"], ((1, False), DIR_FANIN))
- sublayout = layout["info"][0]
- self.assertEqual(layout["info"][1], DIR_NONE)
- self.assertFieldEqual(sublayout["a"], ((1, False), DIR_NONE))
- self.assertFieldEqual(sublayout["b"], ((1, False), DIR_NONE))
-
- def test_enum_field(self):
- layout = Layout.cast([
- ("enum", UnsignedEnum),
- ("enum_dir", UnsignedEnum, DIR_FANOUT),
- ])
- self.assertFieldEqual(layout["enum"], ((2, False), DIR_NONE))
- self.assertFieldEqual(layout["enum_dir"], ((2, False), DIR_FANOUT))
-
- def test_range_field(self):
- layout = Layout.cast([
- ("range", range(0, 7)),
- ])
- self.assertFieldEqual(layout["range"], ((3, False), DIR_NONE))
-
- def test_slice_tuple(self):
- layout = Layout.cast([
- ("a", 1),
- ("b", 2),
- ("c", 3)
- ])
- expect = Layout.cast([
- ("a", 1),
- ("c", 3)
- ])
- self.assertEqual(layout["a", "c"], expect)
-
- def test_repr(self):
- self.assertEqual(repr(Layout([("a", unsigned(1)), ("b", signed(2))])),
- "Layout([('a', unsigned(1)), ('b', signed(2))])")
- self.assertEqual(repr(Layout([("a", unsigned(1)), ("b", [("c", signed(3))])])),
- "Layout([('a', unsigned(1)), "
- "('b', Layout([('c', signed(3))]))])")
-
- def test_wrong_field(self):
- with self.assertRaisesRegex(TypeError,
- (r"^Field \(1,\) has invalid layout: should be either \(name, shape\) or "
- r"\(name, shape, direction\)$")):
- Layout.cast([(1,)])
-
- def test_wrong_name(self):
- with self.assertRaisesRegex(TypeError,
- r"^Field \(1, 1\) has invalid name: should be a string$"):
- Layout.cast([(1, 1)])
-
- def test_wrong_name_duplicate(self):
- with self.assertRaisesRegex(NameError,
- r"^Field \('a', 2\) has a name that is already present in the layout$"):
- Layout.cast([("a", 1), ("a", 2)])
-
- def test_wrong_direction(self):
- with self.assertRaisesRegex(TypeError,
- (r"^Field \('a', 1, 0\) has invalid direction: should be a Direction "
- r"instance like DIR_FANIN$")):
- Layout.cast([("a", 1, 0)])
-
- def test_wrong_shape(self):
- with self.assertRaisesRegex(TypeError,
- (r"^Field \('a', 'x'\) has invalid shape: should be castable to Shape or "
- r"a list of fields of a nested record$")):
- Layout.cast([("a", "x")])
-
-
-class RecordTestCase(FHDLTestCase):
- def test_basic(self):
- r = Record([
- ("stb", 1),
- ("data", 32),
- ("info", [
- ("a", 1),
- ("b", 1),
- ])
- ])
-
- self.assertEqual(repr(r), "(rec r stb data (rec r__info a b))")
- self.assertEqual(len(r), 35)
- self.assertIsInstance(r.stb, Signal)
- self.assertEqual(r.stb.name, "r__stb")
- self.assertEqual(r["stb"].name, "r__stb")
-
- self.assertTrue(hasattr(r, "stb"))
- self.assertFalse(hasattr(r, "xxx"))
-
- def test_unnamed(self):
- r = [Record([
- ("stb", 1)
- ])][0]
-
- self.assertEqual(repr(r), "(rec <unnamed> stb)")
- self.assertEqual(r.stb.name, "stb")
-
- def test_iter(self):
- r = Record([
- ("data", 4),
- ("stb", 1),
- ])
-
- self.assertEqual(repr(r[0]), "(slice (rec r data stb) 0:1)")
- self.assertEqual(repr(r[0:3]), "(slice (rec r data stb) 0:3)")
-
- def test_wrong_field(self):
- r = Record([
- ("stb", 1),
- ("ack", 1),
- ])
- with self.assertRaisesRegex(AttributeError,
- r"^Record 'r' does not have a field 'en'\. Did you mean one of: stb, ack\?$"):
- r["en"]
- with self.assertRaisesRegex(AttributeError,
- r"^Record 'r' does not have a field 'en'\. Did you mean one of: stb, ack\?$"):
- r.en
-
- def test_wrong_field_unnamed(self):
- r = [Record([
- ("stb", 1),
- ("ack", 1),
- ])][0]
- with self.assertRaisesRegex(AttributeError,
- r"^Unnamed record does not have a field 'en'\. Did you mean one of: stb, ack\?$"):
- r.en
-
- def test_construct_with_fields(self):
- ns = Signal(1)
- nr = Record([
- ("burst", 1)
- ])
- r = Record([
- ("stb", 1),
- ("info", [
- ("burst", 1)
- ])
- ], fields={
- "stb": ns,
- "info": nr
- })
- self.assertIs(r.stb, ns)
- self.assertIs(r.info, nr)
-
- def test_like(self):
- r1 = Record([("a", 1), ("b", 2)])
- r2 = Record.like(r1)
- self.assertEqual(r1.layout, r2.layout)
- self.assertEqual(r2.name, "r2")
- r3 = Record.like(r1, name="foo")
- self.assertEqual(r3.name, "foo")
- r4 = Record.like(r1, name_suffix="foo")
- self.assertEqual(r4.name, "r1foo")
-
- def test_like_modifications(self):
- r1 = Record([("a", 1), ("b", [("s", 1)])])
- self.assertEqual(r1.a.name, "r1__a")
- self.assertEqual(r1.b.name, "r1__b")
- self.assertEqual(r1.b.s.name, "r1__b__s")
- r1.a.reset = 1
- r1.b.s.reset = 1
- r2 = Record.like(r1)
- self.assertEqual(r2.a.reset, 1)
- self.assertEqual(r2.b.s.reset, 1)
- self.assertEqual(r2.a.name, "r2__a")
- self.assertEqual(r2.b.name, "r2__b")
- self.assertEqual(r2.b.s.name, "r2__b__s")
-
- def test_slice_tuple(self):
- r1 = Record([("a", 1), ("b", 2), ("c", 3)])
- r2 = r1["a", "c"]
- self.assertEqual(r2.layout, Layout([("a", 1), ("c", 3)]))
- self.assertIs(r2.a, r1.a)
- self.assertIs(r2.c, r1.c)
-
- def test_enum_decoder(self):
- r1 = Record([("a", UnsignedEnum)])
- self.assertEqual(r1.a.decoder(UnsignedEnum.FOO), "FOO/1")
-
-
-class ConnectTestCase(FHDLTestCase):
- def setUp_flat(self):
- self.core_layout = [
- ("addr", 32, DIR_FANOUT),
- ("data_r", 32, DIR_FANIN),
- ("data_w", 32, DIR_FANIN),
- ]
- self.periph_layout = [
- ("addr", 32, DIR_FANOUT),
- ("data_r", 32, DIR_FANIN),
- ("data_w", 32, DIR_FANIN),
- ]
-
- def setUp_nested(self):
- self.core_layout = [
- ("addr", 32, DIR_FANOUT),
- ("data", [
- ("r", 32, DIR_FANIN),
- ("w", 32, DIR_FANIN),
- ]),
- ]
- self.periph_layout = [
- ("addr", 32, DIR_FANOUT),
- ("data", [
- ("r", 32, DIR_FANIN),
- ("w", 32, DIR_FANIN),
- ]),
- ]
-
- def test_flat(self):
- self.setUp_flat()
-
- core = Record(self.core_layout)
- periph1 = Record(self.periph_layout)
- periph2 = Record(self.periph_layout)
-
- stmts = core.connect(periph1, periph2)
- self.assertRepr(stmts, """(
- (eq (sig periph1__addr) (sig core__addr))
- (eq (sig periph2__addr) (sig core__addr))
- (eq (sig core__data_r) (| (sig periph1__data_r) (sig periph2__data_r)))
- (eq (sig core__data_w) (| (sig periph1__data_w) (sig periph2__data_w)))
- )""")
-
- def test_flat_include(self):
- self.setUp_flat()
-
- core = Record(self.core_layout)
- periph1 = Record(self.periph_layout)
- periph2 = Record(self.periph_layout)
-
- stmts = core.connect(periph1, periph2, include={"addr": True})
- self.assertRepr(stmts, """(
- (eq (sig periph1__addr) (sig core__addr))
- (eq (sig periph2__addr) (sig core__addr))
- )""")
-
- def test_flat_exclude(self):
- self.setUp_flat()
-
- core = Record(self.core_layout)
- periph1 = Record(self.periph_layout)
- periph2 = Record(self.periph_layout)
-
- stmts = core.connect(periph1, periph2, exclude={"addr": True})
- self.assertRepr(stmts, """(
- (eq (sig core__data_r) (| (sig periph1__data_r) (sig periph2__data_r)))
- (eq (sig core__data_w) (| (sig periph1__data_w) (sig periph2__data_w)))
- )""")
-
- def test_nested(self):
- self.setUp_nested()
-
- core = Record(self.core_layout)
- periph1 = Record(self.periph_layout)
- periph2 = Record(self.periph_layout)
-
- stmts = core.connect(periph1, periph2)
- self.maxDiff = None
- self.assertRepr(stmts, """(
- (eq (sig periph1__addr) (sig core__addr))
- (eq (sig periph2__addr) (sig core__addr))
- (eq (sig core__data__r) (| (sig periph1__data__r) (sig periph2__data__r)))
- (eq (sig core__data__w) (| (sig periph1__data__w) (sig periph2__data__w)))
- )""")
-
- def test_wrong_include_exclude(self):
- self.setUp_flat()
-
- core = Record(self.core_layout)
- periph = Record(self.periph_layout)
-
- with self.assertRaisesRegex(AttributeError,
- r"^Cannot include field 'foo' because it is not present in record 'core'$"):
- core.connect(periph, include={"foo": True})
-
- with self.assertRaisesRegex(AttributeError,
- r"^Cannot exclude field 'foo' because it is not present in record 'core'$"):
- core.connect(periph, exclude={"foo": True})
-
- def test_wrong_direction(self):
- recs = [Record([("x", 1)]) for _ in range(2)]
-
- with self.assertRaisesRegex(TypeError,
- (r"^Cannot connect field 'x' of unnamed record because it does not have "
- r"a direction$")):
- recs[0].connect(recs[1])
-
- def test_wrong_missing_field(self):
- core = Record([("addr", 32, DIR_FANOUT)])
- periph = Record([])
-
- with self.assertRaisesRegex(AttributeError,
- (r"^Cannot connect field 'addr' of record 'core' to subordinate record 'periph' "
- r"because the subordinate record does not have this field$")):
- core.connect(periph)
+++ /dev/null
-# nmigen: UnusedElaboratable=no
-
-from ..hdl.ast import *
-from ..hdl.cd import *
-from ..hdl.ir import *
-from ..hdl.xfrm import *
-from ..hdl.mem import *
-from .utils import *
-
-
-class DomainRenamerTestCase(FHDLTestCase):
- def setUp(self):
- self.s1 = Signal()
- self.s2 = Signal()
- self.s3 = Signal()
- self.s4 = Signal()
- self.s5 = Signal()
- self.c1 = Signal()
-
- def test_rename_signals(self):
- f = Fragment()
- f.add_statements(
- self.s1.eq(ClockSignal()),
- ResetSignal().eq(self.s2),
- self.s3.eq(0),
- self.s4.eq(ClockSignal("other")),
- self.s5.eq(ResetSignal("other")),
- )
- f.add_driver(self.s1, None)
- f.add_driver(self.s2, None)
- f.add_driver(self.s3, "sync")
-
- f = DomainRenamer("pix")(f)
- self.assertRepr(f.statements, """
- (
- (eq (sig s1) (clk pix))
- (eq (rst pix) (sig s2))
- (eq (sig s3) (const 1'd0))
- (eq (sig s4) (clk other))
- (eq (sig s5) (rst other))
- )
- """)
- self.assertEqual(f.drivers, {
- None: SignalSet((self.s1, self.s2)),
- "pix": SignalSet((self.s3,)),
- })
-
- def test_rename_multi(self):
- f = Fragment()
- f.add_statements(
- self.s1.eq(ClockSignal()),
- self.s2.eq(ResetSignal("other")),
- )
-
- f = DomainRenamer({"sync": "pix", "other": "pix2"})(f)
- self.assertRepr(f.statements, """
- (
- (eq (sig s1) (clk pix))
- (eq (sig s2) (rst pix2))
- )
- """)
-
- def test_rename_cd(self):
- cd_sync = ClockDomain()
- cd_pix = ClockDomain()
-
- f = Fragment()
- f.add_domains(cd_sync, cd_pix)
-
- f = DomainRenamer("ext")(f)
- self.assertEqual(cd_sync.name, "ext")
- self.assertEqual(f.domains, {
- "ext": cd_sync,
- "pix": cd_pix,
- })
-
- def test_rename_cd_preserves_allow_reset_less(self):
- cd_pix = ClockDomain(reset_less=True)
-
- f = Fragment()
- f.add_domains(cd_pix)
- f.add_statements(
- self.s1.eq(ResetSignal(allow_reset_less=True)),
- )
-
- f = DomainRenamer("pix")(f)
- f = DomainLowerer()(f)
- self.assertRepr(f.statements, """
- (
- (eq (sig s1) (const 1'd0))
- )
- """)
-
-
- def test_rename_cd_subfragment(self):
- cd_sync = ClockDomain()
- cd_pix = ClockDomain()
-
- f1 = Fragment()
- f1.add_domains(cd_sync, cd_pix)
- f2 = Fragment()
- f2.add_domains(cd_sync)
- f1.add_subfragment(f2)
-
- f1 = DomainRenamer("ext")(f1)
- self.assertEqual(cd_sync.name, "ext")
- self.assertEqual(f1.domains, {
- "ext": cd_sync,
- "pix": cd_pix,
- })
-
- def test_rename_wrong_to_comb(self):
- with self.assertRaisesRegex(ValueError,
- r"^Domain 'sync' may not be renamed to 'comb'$"):
- DomainRenamer("comb")
-
- def test_rename_wrong_from_comb(self):
- with self.assertRaisesRegex(ValueError,
- r"^Domain 'comb' may not be renamed$"):
- DomainRenamer({"comb": "sync"})
-
-
-class DomainLowererTestCase(FHDLTestCase):
- def setUp(self):
- self.s = Signal()
-
- def test_lower_clk(self):
- sync = ClockDomain()
- f = Fragment()
- f.add_domains(sync)
- f.add_statements(
- self.s.eq(ClockSignal("sync"))
- )
-
- f = DomainLowerer()(f)
- self.assertRepr(f.statements, """
- (
- (eq (sig s) (sig clk))
- )
- """)
-
- def test_lower_rst(self):
- sync = ClockDomain()
- f = Fragment()
- f.add_domains(sync)
- f.add_statements(
- self.s.eq(ResetSignal("sync"))
- )
-
- f = DomainLowerer()(f)
- self.assertRepr(f.statements, """
- (
- (eq (sig s) (sig rst))
- )
- """)
-
- def test_lower_rst_reset_less(self):
- sync = ClockDomain(reset_less=True)
- f = Fragment()
- f.add_domains(sync)
- f.add_statements(
- self.s.eq(ResetSignal("sync", allow_reset_less=True))
- )
-
- f = DomainLowerer()(f)
- self.assertRepr(f.statements, """
- (
- (eq (sig s) (const 1'd0))
- )
- """)
-
- def test_lower_drivers(self):
- sync = ClockDomain()
- pix = ClockDomain()
- f = Fragment()
- f.add_domains(sync, pix)
- f.add_driver(ClockSignal("pix"), None)
- f.add_driver(ResetSignal("pix"), "sync")
-
- f = DomainLowerer()(f)
- self.assertEqual(f.drivers, {
- None: SignalSet((pix.clk,)),
- "sync": SignalSet((pix.rst,))
- })
-
- def test_lower_wrong_domain(self):
- f = Fragment()
- f.add_statements(
- self.s.eq(ClockSignal("xxx"))
- )
-
- with self.assertRaisesRegex(DomainError,
- r"^Signal \(clk xxx\) refers to nonexistent domain 'xxx'$"):
- DomainLowerer()(f)
-
- def test_lower_wrong_reset_less_domain(self):
- sync = ClockDomain(reset_less=True)
- f = Fragment()
- f.add_domains(sync)
- f.add_statements(
- self.s.eq(ResetSignal("sync"))
- )
-
- with self.assertRaisesRegex(DomainError,
- r"^Signal \(rst sync\) refers to reset of reset-less domain 'sync'$"):
- DomainLowerer()(f)
-
-
-class SampleLowererTestCase(FHDLTestCase):
- def setUp(self):
- self.i = Signal()
- self.o1 = Signal()
- self.o2 = Signal()
- self.o3 = Signal()
-
- def test_lower_signal(self):
- f = Fragment()
- f.add_statements(
- self.o1.eq(Sample(self.i, 2, "sync")),
- self.o2.eq(Sample(self.i, 1, "sync")),
- self.o3.eq(Sample(self.i, 1, "pix")),
- )
-
- f = SampleLowerer()(f)
- self.assertRepr(f.statements, """
- (
- (eq (sig o1) (sig $sample$s$i$sync$2))
- (eq (sig o2) (sig $sample$s$i$sync$1))
- (eq (sig o3) (sig $sample$s$i$pix$1))
- (eq (sig $sample$s$i$sync$1) (sig i))
- (eq (sig $sample$s$i$sync$2) (sig $sample$s$i$sync$1))
- (eq (sig $sample$s$i$pix$1) (sig i))
- )
- """)
- self.assertEqual(len(f.drivers["sync"]), 2)
- self.assertEqual(len(f.drivers["pix"]), 1)
-
- def test_lower_const(self):
- f = Fragment()
- f.add_statements(
- self.o1.eq(Sample(1, 2, "sync")),
- )
-
- f = SampleLowerer()(f)
- self.assertRepr(f.statements, """
- (
- (eq (sig o1) (sig $sample$c$1$sync$2))
- (eq (sig $sample$c$1$sync$1) (const 1'd1))
- (eq (sig $sample$c$1$sync$2) (sig $sample$c$1$sync$1))
- )
- """)
- self.assertEqual(len(f.drivers["sync"]), 2)
-
-
-class SwitchCleanerTestCase(FHDLTestCase):
- def test_clean(self):
- a = Signal()
- b = Signal()
- c = Signal()
- stmts = [
- Switch(a, {
- 1: a.eq(0),
- 0: [
- b.eq(1),
- Switch(b, {1: [
- Switch(a|b, {})
- ]})
- ]
- })
- ]
-
- self.assertRepr(SwitchCleaner()(stmts), """
- (
- (switch (sig a)
- (case 1
- (eq (sig a) (const 1'd0)))
- (case 0
- (eq (sig b) (const 1'd1)))
- )
- )
- """)
-
-
-class LHSGroupAnalyzerTestCase(FHDLTestCase):
- def test_no_group_unrelated(self):
- a = Signal()
- b = Signal()
- stmts = [
- a.eq(0),
- b.eq(0),
- ]
-
- groups = LHSGroupAnalyzer()(stmts)
- self.assertEqual(list(groups.values()), [
- SignalSet((a,)),
- SignalSet((b,)),
- ])
-
- def test_group_related(self):
- a = Signal()
- b = Signal()
- stmts = [
- a.eq(0),
- Cat(a, b).eq(0),
- ]
-
- groups = LHSGroupAnalyzer()(stmts)
- self.assertEqual(list(groups.values()), [
- SignalSet((a, b)),
- ])
-
- def test_no_loops(self):
- a = Signal()
- b = Signal()
- stmts = [
- a.eq(0),
- Cat(a, b).eq(0),
- Cat(a, b).eq(0),
- ]
-
- groups = LHSGroupAnalyzer()(stmts)
- self.assertEqual(list(groups.values()), [
- SignalSet((a, b)),
- ])
-
- def test_switch(self):
- a = Signal()
- b = Signal()
- stmts = [
- a.eq(0),
- Switch(a, {
- 1: b.eq(0),
- })
- ]
-
- groups = LHSGroupAnalyzer()(stmts)
- self.assertEqual(list(groups.values()), [
- SignalSet((a,)),
- SignalSet((b,)),
- ])
-
- def test_lhs_empty(self):
- stmts = [
- Cat().eq(0)
- ]
-
- groups = LHSGroupAnalyzer()(stmts)
- self.assertEqual(list(groups.values()), [
- ])
-
-
-class LHSGroupFilterTestCase(FHDLTestCase):
- def test_filter(self):
- a = Signal()
- b = Signal()
- c = Signal()
- stmts = [
- Switch(a, {
- 1: a.eq(0),
- 0: [
- b.eq(1),
- Switch(b, {1: []})
- ]
- })
- ]
-
- self.assertRepr(LHSGroupFilter(SignalSet((a,)))(stmts), """
- (
- (switch (sig a)
- (case 1
- (eq (sig a) (const 1'd0)))
- (case 0 )
- )
- )
- """)
-
- def test_lhs_empty(self):
- stmts = [
- Cat().eq(0)
- ]
-
- self.assertRepr(LHSGroupFilter(SignalSet())(stmts), "()")
-
-
-class ResetInserterTestCase(FHDLTestCase):
- def setUp(self):
- self.s1 = Signal()
- self.s2 = Signal(reset=1)
- self.s3 = Signal(reset=1, reset_less=True)
- self.c1 = Signal()
-
- def test_reset_default(self):
- f = Fragment()
- f.add_statements(
- self.s1.eq(1)
- )
- f.add_driver(self.s1, "sync")
-
- f = ResetInserter(self.c1)(f)
- self.assertRepr(f.statements, """
- (
- (eq (sig s1) (const 1'd1))
- (switch (sig c1)
- (case 1 (eq (sig s1) (const 1'd0)))
- )
- )
- """)
-
- def test_reset_cd(self):
- f = Fragment()
- f.add_statements(
- self.s1.eq(1),
- self.s2.eq(0),
- )
- f.add_domains(ClockDomain("sync"))
- f.add_driver(self.s1, "sync")
- f.add_driver(self.s2, "pix")
-
- f = ResetInserter({"pix": self.c1})(f)
- self.assertRepr(f.statements, """
- (
- (eq (sig s1) (const 1'd1))
- (eq (sig s2) (const 1'd0))
- (switch (sig c1)
- (case 1 (eq (sig s2) (const 1'd1)))
- )
- )
- """)
-
- def test_reset_value(self):
- f = Fragment()
- f.add_statements(
- self.s2.eq(0)
- )
- f.add_driver(self.s2, "sync")
-
- f = ResetInserter(self.c1)(f)
- self.assertRepr(f.statements, """
- (
- (eq (sig s2) (const 1'd0))
- (switch (sig c1)
- (case 1 (eq (sig s2) (const 1'd1)))
- )
- )
- """)
-
- def test_reset_less(self):
- f = Fragment()
- f.add_statements(
- self.s3.eq(0)
- )
- f.add_driver(self.s3, "sync")
-
- f = ResetInserter(self.c1)(f)
- self.assertRepr(f.statements, """
- (
- (eq (sig s3) (const 1'd0))
- (switch (sig c1)
- (case 1 )
- )
- )
- """)
-
-
-class EnableInserterTestCase(FHDLTestCase):
- def setUp(self):
- self.s1 = Signal()
- self.s2 = Signal()
- self.s3 = Signal()
- self.c1 = Signal()
-
- def test_enable_default(self):
- f = Fragment()
- f.add_statements(
- self.s1.eq(1)
- )
- f.add_driver(self.s1, "sync")
-
- f = EnableInserter(self.c1)(f)
- self.assertRepr(f.statements, """
- (
- (eq (sig s1) (const 1'd1))
- (switch (sig c1)
- (case 0 (eq (sig s1) (sig s1)))
- )
- )
- """)
-
- def test_enable_cd(self):
- f = Fragment()
- f.add_statements(
- self.s1.eq(1),
- self.s2.eq(0),
- )
- f.add_driver(self.s1, "sync")
- f.add_driver(self.s2, "pix")
-
- f = EnableInserter({"pix": self.c1})(f)
- self.assertRepr(f.statements, """
- (
- (eq (sig s1) (const 1'd1))
- (eq (sig s2) (const 1'd0))
- (switch (sig c1)
- (case 0 (eq (sig s2) (sig s2)))
- )
- )
- """)
-
- def test_enable_subfragment(self):
- f1 = Fragment()
- f1.add_statements(
- self.s1.eq(1)
- )
- f1.add_driver(self.s1, "sync")
-
- f2 = Fragment()
- f2.add_statements(
- self.s2.eq(1)
- )
- f2.add_driver(self.s2, "sync")
- f1.add_subfragment(f2)
-
- f1 = EnableInserter(self.c1)(f1)
- (f2, _), = f1.subfragments
- self.assertRepr(f1.statements, """
- (
- (eq (sig s1) (const 1'd1))
- (switch (sig c1)
- (case 0 (eq (sig s1) (sig s1)))
- )
- )
- """)
- self.assertRepr(f2.statements, """
- (
- (eq (sig s2) (const 1'd1))
- (switch (sig c1)
- (case 0 (eq (sig s2) (sig s2)))
- )
- )
- """)
-
- def test_enable_read_port(self):
- mem = Memory(width=8, depth=4)
- f = EnableInserter(self.c1)(mem.read_port(transparent=False)).elaborate(platform=None)
- self.assertRepr(f.named_ports["EN"][0], """
- (m (sig c1) (sig mem_r_en) (const 1'd0))
- """)
-
- def test_enable_write_port(self):
- mem = Memory(width=8, depth=4)
- f = EnableInserter(self.c1)(mem.write_port()).elaborate(platform=None)
- self.assertRepr(f.named_ports["EN"][0], """
- (m (sig c1) (cat (repl (slice (sig mem_w_en) 0:1) 8)) (const 8'd0))
- """)
-
-
-class _MockElaboratable(Elaboratable):
- def __init__(self):
- self.s1 = Signal()
-
- def elaborate(self, platform):
- f = Fragment()
- f.add_statements(
- self.s1.eq(1)
- )
- f.add_driver(self.s1, "sync")
- return f
-
-
-class TransformedElaboratableTestCase(FHDLTestCase):
- def setUp(self):
- self.c1 = Signal()
- self.c2 = Signal()
-
- def test_getattr(self):
- e = _MockElaboratable()
- te = EnableInserter(self.c1)(e)
-
- self.assertIs(te.s1, e.s1)
-
- def test_composition(self):
- e = _MockElaboratable()
- te1 = EnableInserter(self.c1)(e)
- te2 = ResetInserter(self.c2)(te1)
-
- self.assertIsInstance(te1, TransformedElaboratable)
- self.assertIs(te1, te2)
-
- f = Fragment.get(te2, None)
- self.assertRepr(f.statements, """
- (
- (eq (sig s1) (const 1'd1))
- (switch (sig c1)
- (case 0 (eq (sig s1) (sig s1)))
- )
- (switch (sig c2)
- (case 1 (eq (sig s1) (const 1'd0)))
- )
- )
- """)
-
-
-class MockUserValue(UserValue):
- def __init__(self, lowered):
- super().__init__()
- self.lowered = lowered
-
- def lower(self):
- return self.lowered
-
-
-class UserValueTestCase(FHDLTestCase):
- def setUp(self):
- self.s = Signal()
- self.c = Signal()
- self.uv = MockUserValue(self.s)
-
- def test_lower(self):
- sync = ClockDomain()
- f = Fragment()
- f.add_domains(sync)
- f.add_statements(
- self.uv.eq(1)
- )
- for signal in self.uv._lhs_signals():
- f.add_driver(signal, "sync")
-
- f = ResetInserter(self.c)(f)
- f = DomainLowerer()(f)
- self.assertRepr(f.statements, """
- (
- (eq (sig s) (const 1'd1))
- (switch (sig c)
- (case 1 (eq (sig s) (const 1'd0)))
- )
- (switch (sig rst)
- (case 1 (eq (sig s) (const 1'd0)))
- )
- )
- """)
-
-
-class UserValueRecursiveTestCase(UserValueTestCase):
- def setUp(self):
- self.s = Signal()
- self.c = Signal()
- self.uv = MockUserValue(MockUserValue(self.s))
-
- # inherit the test_lower method from UserValueTestCase because the checks are the same
+++ /dev/null
-# nmigen: UnusedElaboratable=no
-
-from .utils import *
-from ..hdl import *
-from ..back.pysim import *
-from ..lib.cdc import *
-
-
-class FFSynchronizerTestCase(FHDLTestCase):
- def test_stages_wrong(self):
- with self.assertRaisesRegex(TypeError,
- r"^Synchronization stage count must be a positive integer, not 0$"):
- FFSynchronizer(Signal(), Signal(), stages=0)
- with self.assertRaisesRegex(ValueError,
- r"^Synchronization stage count may not safely be less than 2$"):
- FFSynchronizer(Signal(), Signal(), stages=1)
-
- def test_basic(self):
- i = Signal()
- o = Signal()
- frag = FFSynchronizer(i, o)
-
- sim = Simulator(frag)
- sim.add_clock(1e-6)
- def process():
- self.assertEqual((yield o), 0)
- yield i.eq(1)
- yield Tick()
- self.assertEqual((yield o), 0)
- yield Tick()
- self.assertEqual((yield o), 0)
- yield Tick()
- self.assertEqual((yield o), 1)
- sim.add_process(process)
- sim.run()
-
- def test_reset_value(self):
- i = Signal(reset=1)
- o = Signal()
- frag = FFSynchronizer(i, o, reset=1)
-
- sim = Simulator(frag)
- sim.add_clock(1e-6)
- def process():
- self.assertEqual((yield o), 1)
- yield i.eq(0)
- yield Tick()
- self.assertEqual((yield o), 1)
- yield Tick()
- self.assertEqual((yield o), 1)
- yield Tick()
- self.assertEqual((yield o), 0)
- sim.add_process(process)
- sim.run()
-
-
-class AsyncFFSynchronizerTestCase(FHDLTestCase):
- def test_stages_wrong(self):
- with self.assertRaisesRegex(TypeError,
- r"^Synchronization stage count must be a positive integer, not 0$"):
- ResetSynchronizer(Signal(), stages=0)
- with self.assertRaisesRegex(ValueError,
- r"^Synchronization stage count may not safely be less than 2$"):
- ResetSynchronizer(Signal(), stages=1)
-
- def test_edge_wrong(self):
- with self.assertRaisesRegex(ValueError,
- r"^AsyncFFSynchronizer async edge must be one of 'pos' or 'neg', not 'xxx'$"):
- AsyncFFSynchronizer(Signal(), Signal(), o_domain="sync", async_edge="xxx")
-
- def test_pos_edge(self):
- i = Signal()
- o = Signal()
- m = Module()
- m.domains += ClockDomain("sync")
- m.submodules += AsyncFFSynchronizer(i, o)
-
- sim = Simulator(m)
- sim.add_clock(1e-6)
- def process():
- # initial reset
- self.assertEqual((yield i), 0)
- self.assertEqual((yield o), 1)
- yield Tick(); yield Delay(1e-8)
- self.assertEqual((yield o), 1)
- yield Tick(); yield Delay(1e-8)
- self.assertEqual((yield o), 0)
- yield Tick(); yield Delay(1e-8)
- self.assertEqual((yield o), 0)
- yield Tick(); yield Delay(1e-8)
-
- yield i.eq(1)
- yield Delay(1e-8)
- self.assertEqual((yield o), 1)
- yield Tick(); yield Delay(1e-8)
- self.assertEqual((yield o), 1)
- yield i.eq(0)
- yield Tick(); yield Delay(1e-8)
- self.assertEqual((yield o), 1)
- yield Tick(); yield Delay(1e-8)
- self.assertEqual((yield o), 0)
- yield Tick(); yield Delay(1e-8)
- self.assertEqual((yield o), 0)
- yield Tick(); yield Delay(1e-8)
- sim.add_process(process)
- with sim.write_vcd("test.vcd"):
- sim.run()
-
- def test_neg_edge(self):
- i = Signal(reset=1)
- o = Signal()
- m = Module()
- m.domains += ClockDomain("sync")
- m.submodules += AsyncFFSynchronizer(i, o, async_edge="neg")
-
- sim = Simulator(m)
- sim.add_clock(1e-6)
- def process():
- # initial reset
- self.assertEqual((yield i), 1)
- self.assertEqual((yield o), 1)
- yield Tick(); yield Delay(1e-8)
- self.assertEqual((yield o), 1)
- yield Tick(); yield Delay(1e-8)
- self.assertEqual((yield o), 0)
- yield Tick(); yield Delay(1e-8)
- self.assertEqual((yield o), 0)
- yield Tick(); yield Delay(1e-8)
-
- yield i.eq(0)
- yield Delay(1e-8)
- self.assertEqual((yield o), 1)
- yield Tick(); yield Delay(1e-8)
- self.assertEqual((yield o), 1)
- yield i.eq(1)
- yield Tick(); yield Delay(1e-8)
- self.assertEqual((yield o), 1)
- yield Tick(); yield Delay(1e-8)
- self.assertEqual((yield o), 0)
- yield Tick(); yield Delay(1e-8)
- self.assertEqual((yield o), 0)
- yield Tick(); yield Delay(1e-8)
- sim.add_process(process)
- with sim.write_vcd("test.vcd"):
- sim.run()
-
-
-class ResetSynchronizerTestCase(FHDLTestCase):
- def test_stages_wrong(self):
- with self.assertRaisesRegex(TypeError,
- r"^Synchronization stage count must be a positive integer, not 0$"):
- ResetSynchronizer(Signal(), stages=0)
- with self.assertRaisesRegex(ValueError,
- r"^Synchronization stage count may not safely be less than 2$"):
- ResetSynchronizer(Signal(), stages=1)
-
- def test_basic(self):
- arst = Signal()
- m = Module()
- m.domains += ClockDomain("sync")
- m.submodules += ResetSynchronizer(arst)
- s = Signal(reset=1)
- m.d.sync += s.eq(0)
-
- sim = Simulator(m)
- sim.add_clock(1e-6)
- def process():
- # initial reset
- self.assertEqual((yield s), 1)
- yield Tick(); yield Delay(1e-8)
- self.assertEqual((yield s), 1)
- yield Tick(); yield Delay(1e-8)
- self.assertEqual((yield s), 1)
- yield Tick(); yield Delay(1e-8)
- self.assertEqual((yield s), 0)
- yield Tick(); yield Delay(1e-8)
-
- yield arst.eq(1)
- yield Delay(1e-8)
- self.assertEqual((yield s), 0)
- yield Tick(); yield Delay(1e-8)
- self.assertEqual((yield s), 1)
- yield arst.eq(0)
- yield Tick(); yield Delay(1e-8)
- self.assertEqual((yield s), 1)
- yield Tick(); yield Delay(1e-8)
- self.assertEqual((yield s), 1)
- yield Tick(); yield Delay(1e-8)
- self.assertEqual((yield s), 0)
- yield Tick(); yield Delay(1e-8)
- sim.add_process(process)
- with sim.write_vcd("test.vcd"):
- sim.run()
-
-
-# TODO: test with distinct clocks
-class PulseSynchronizerTestCase(FHDLTestCase):
- def test_stages_wrong(self):
- with self.assertRaisesRegex(TypeError,
- r"^Synchronization stage count must be a positive integer, not 0$"):
- PulseSynchronizer("w", "r", stages=0)
- with self.assertRaisesRegex(ValueError,
- r"^Synchronization stage count may not safely be less than 2$"):
- PulseSynchronizer("w", "r", stages=1)
-
- def test_smoke(self):
- m = Module()
- m.domains += ClockDomain("sync")
- ps = m.submodules.dut = PulseSynchronizer("sync", "sync")
-
- sim = Simulator(m)
- sim.add_clock(1e-6)
- def process():
- yield ps.i.eq(0)
- # TODO: think about reset
- for n in range(5):
- yield Tick()
- # Make sure no pulses are generated in quiescent state
- for n in range(3):
- yield Tick()
- self.assertEqual((yield ps.o), 0)
- # Check conservation of pulses
- accum = 0
- for n in range(10):
- yield ps.i.eq(1 if n < 4 else 0)
- yield Tick()
- accum += yield ps.o
- self.assertEqual(accum, 4)
- sim.add_process(process)
- sim.run()
+++ /dev/null
-from .utils import *
-from ..hdl import *
-from ..asserts import *
-from ..back.pysim import *
-from ..lib.coding import *
-
-
-class EncoderTestCase(FHDLTestCase):
- def test_basic(self):
- enc = Encoder(4)
- def process():
- self.assertEqual((yield enc.n), 1)
- self.assertEqual((yield enc.o), 0)
-
- yield enc.i.eq(0b0001)
- yield Settle()
- self.assertEqual((yield enc.n), 0)
- self.assertEqual((yield enc.o), 0)
-
- yield enc.i.eq(0b0100)
- yield Settle()
- self.assertEqual((yield enc.n), 0)
- self.assertEqual((yield enc.o), 2)
-
- yield enc.i.eq(0b0110)
- yield Settle()
- self.assertEqual((yield enc.n), 1)
- self.assertEqual((yield enc.o), 0)
-
- sim = Simulator(enc)
- sim.add_process(process)
- sim.run()
-
-
-class PriorityEncoderTestCase(FHDLTestCase):
- def test_basic(self):
- enc = PriorityEncoder(4)
- def process():
- self.assertEqual((yield enc.n), 1)
- self.assertEqual((yield enc.o), 0)
-
- yield enc.i.eq(0b0001)
- yield Settle()
- self.assertEqual((yield enc.n), 0)
- self.assertEqual((yield enc.o), 0)
-
- yield enc.i.eq(0b0100)
- yield Settle()
- self.assertEqual((yield enc.n), 0)
- self.assertEqual((yield enc.o), 2)
-
- yield enc.i.eq(0b0110)
- yield Settle()
- self.assertEqual((yield enc.n), 0)
- self.assertEqual((yield enc.o), 1)
-
- sim = Simulator(enc)
- sim.add_process(process)
- sim.run()
-
-
-class DecoderTestCase(FHDLTestCase):
- def test_basic(self):
- dec = Decoder(4)
- def process():
- self.assertEqual((yield dec.o), 0b0001)
-
- yield dec.i.eq(1)
- yield Settle()
- self.assertEqual((yield dec.o), 0b0010)
-
- yield dec.i.eq(3)
- yield Settle()
- self.assertEqual((yield dec.o), 0b1000)
-
- yield dec.n.eq(1)
- yield Settle()
- self.assertEqual((yield dec.o), 0b0000)
-
- sim = Simulator(dec)
- sim.add_process(process)
- sim.run()
-
-
-class ReversibleSpec(Elaboratable):
- def __init__(self, encoder_cls, decoder_cls, args):
- self.encoder_cls = encoder_cls
- self.decoder_cls = decoder_cls
- self.coder_args = args
-
- def elaborate(self, platform):
- m = Module()
- enc, dec = self.encoder_cls(*self.coder_args), self.decoder_cls(*self.coder_args)
- m.submodules += enc, dec
- m.d.comb += [
- dec.i.eq(enc.o),
- Assert(enc.i == dec.o)
- ]
- return m
-
-
-class HammingDistanceSpec(Elaboratable):
- def __init__(self, distance, encoder_cls, args):
- self.distance = distance
- self.encoder_cls = encoder_cls
- self.coder_args = args
-
- def elaborate(self, platform):
- m = Module()
- enc1, enc2 = self.encoder_cls(*self.coder_args), self.encoder_cls(*self.coder_args)
- m.submodules += enc1, enc2
- m.d.comb += [
- Assume(enc1.i + 1 == enc2.i),
- Assert(sum(enc1.o ^ enc2.o) == self.distance)
- ]
- return m
-
-
-class GrayCoderTestCase(FHDLTestCase):
- def test_reversible(self):
- spec = ReversibleSpec(encoder_cls=GrayEncoder, decoder_cls=GrayDecoder, args=(16,))
- self.assertFormal(spec, mode="prove")
-
- def test_distance(self):
- spec = HammingDistanceSpec(distance=1, encoder_cls=GrayEncoder, args=(16,))
- self.assertFormal(spec, mode="prove")
+++ /dev/null
-# nmigen: UnusedElaboratable=no
-
-from .utils import *
-from ..hdl import *
-from ..asserts import *
-from ..back.pysim import *
-from ..lib.fifo import *
-
-
-class FIFOTestCase(FHDLTestCase):
- def test_depth_wrong(self):
- with self.assertRaisesRegex(TypeError,
- r"^FIFO width must be a non-negative integer, not -1$"):
- FIFOInterface(width=-1, depth=8, fwft=True)
- with self.assertRaisesRegex(TypeError,
- r"^FIFO depth must be a non-negative integer, not -1$"):
- FIFOInterface(width=8, depth=-1, fwft=True)
-
- def test_sync_depth(self):
- self.assertEqual(SyncFIFO(width=8, depth=0).depth, 0)
- self.assertEqual(SyncFIFO(width=8, depth=1).depth, 1)
- self.assertEqual(SyncFIFO(width=8, depth=2).depth, 2)
-
- def test_sync_buffered_depth(self):
- self.assertEqual(SyncFIFOBuffered(width=8, depth=0).depth, 0)
- self.assertEqual(SyncFIFOBuffered(width=8, depth=1).depth, 1)
- self.assertEqual(SyncFIFOBuffered(width=8, depth=2).depth, 2)
-
- def test_async_depth(self):
- self.assertEqual(AsyncFIFO(width=8, depth=0 ).depth, 0)
- self.assertEqual(AsyncFIFO(width=8, depth=1 ).depth, 1)
- self.assertEqual(AsyncFIFO(width=8, depth=2 ).depth, 2)
- self.assertEqual(AsyncFIFO(width=8, depth=3 ).depth, 4)
- self.assertEqual(AsyncFIFO(width=8, depth=4 ).depth, 4)
- self.assertEqual(AsyncFIFO(width=8, depth=15).depth, 16)
- self.assertEqual(AsyncFIFO(width=8, depth=16).depth, 16)
- self.assertEqual(AsyncFIFO(width=8, depth=17).depth, 32)
-
- def test_async_depth_wrong(self):
- with self.assertRaisesRegex(ValueError,
- (r"^AsyncFIFO only supports depths that are powers of 2; "
- r"requested exact depth 15 is not$")):
- AsyncFIFO(width=8, depth=15, exact_depth=True)
-
- def test_async_buffered_depth(self):
- self.assertEqual(AsyncFIFOBuffered(width=8, depth=0 ).depth, 0)
- self.assertEqual(AsyncFIFOBuffered(width=8, depth=1 ).depth, 2)
- self.assertEqual(AsyncFIFOBuffered(width=8, depth=2 ).depth, 2)
- self.assertEqual(AsyncFIFOBuffered(width=8, depth=3 ).depth, 3)
- self.assertEqual(AsyncFIFOBuffered(width=8, depth=4 ).depth, 5)
- self.assertEqual(AsyncFIFOBuffered(width=8, depth=15).depth, 17)
- self.assertEqual(AsyncFIFOBuffered(width=8, depth=16).depth, 17)
- self.assertEqual(AsyncFIFOBuffered(width=8, depth=17).depth, 17)
- self.assertEqual(AsyncFIFOBuffered(width=8, depth=18).depth, 33)
-
- def test_async_buffered_depth_wrong(self):
- with self.assertRaisesRegex(ValueError,
- (r"^AsyncFIFOBuffered only supports depths that are one higher than powers of 2; "
- r"requested exact depth 16 is not$")):
- AsyncFIFOBuffered(width=8, depth=16, exact_depth=True)
-
-class FIFOModel(Elaboratable, FIFOInterface):
- """
- Non-synthesizable first-in first-out queue, implemented naively as a chain of registers.
- """
- def __init__(self, *, width, depth, fwft, r_domain, w_domain):
- super().__init__(width=width, depth=depth, fwft=fwft)
-
- self.r_domain = r_domain
- self.w_domain = w_domain
-
- self.level = Signal(range(self.depth + 1))
- self.r_level = Signal(range(self.depth + 1))
- self.w_level = Signal(range(self.depth + 1))
-
- def elaborate(self, platform):
- m = Module()
-
- storage = Memory(width=self.width, depth=self.depth)
- w_port = m.submodules.w_port = storage.write_port(domain=self.w_domain)
- r_port = m.submodules.r_port = storage.read_port (domain="comb")
-
- produce = Signal(range(self.depth))
- consume = Signal(range(self.depth))
-
- m.d.comb += self.r_rdy.eq(self.level > 0)
- m.d.comb += r_port.addr.eq((consume + 1) % self.depth)
- if self.fwft:
- m.d.comb += self.r_data.eq(r_port.data)
- with m.If(self.r_en & self.r_rdy):
- if not self.fwft:
- m.d[self.r_domain] += self.r_data.eq(r_port.data)
- m.d[self.r_domain] += consume.eq(r_port.addr)
-
- m.d.comb += self.w_rdy.eq(self.level < self.depth)
- m.d.comb += w_port.data.eq(self.w_data)
- with m.If(self.w_en & self.w_rdy):
- m.d.comb += w_port.addr.eq((produce + 1) % self.depth)
- m.d.comb += w_port.en.eq(1)
- m.d[self.w_domain] += produce.eq(w_port.addr)
-
- with m.If(ResetSignal(self.r_domain) | ResetSignal(self.w_domain)):
- m.d.sync += self.level.eq(0)
- with m.Else():
- m.d.sync += self.level.eq(self.level
- + (self.w_rdy & self.w_en)
- - (self.r_rdy & self.r_en))
-
- m.d.comb += [
- self.r_level.eq(self.level),
- self.w_level.eq(self.level),
- ]
- m.d.comb += Assert(ResetSignal(self.r_domain) == ResetSignal(self.w_domain))
-
- return m
-
-
-class FIFOModelEquivalenceSpec(Elaboratable):
- """
- The first-in first-out queue model equivalence specification: for any inputs and control
- signals, the behavior of the implementation under test exactly matches the ideal model,
- except for behavior not defined by the model.
- """
- def __init__(self, fifo, r_domain, w_domain):
- self.fifo = fifo
-
- self.r_domain = r_domain
- self.w_domain = w_domain
-
- def elaborate(self, platform):
- m = Module()
- m.submodules.dut = dut = self.fifo
- m.submodules.gold = gold = FIFOModel(width=dut.width, depth=dut.depth, fwft=dut.fwft,
- r_domain=self.r_domain, w_domain=self.w_domain)
-
- m.d.comb += [
- gold.r_en.eq(dut.r_rdy & dut.r_en),
- gold.w_en.eq(dut.w_en),
- gold.w_data.eq(dut.w_data),
- ]
-
- m.d.comb += Assert(dut.r_rdy.implies(gold.r_rdy))
- m.d.comb += Assert(dut.w_rdy.implies(gold.w_rdy))
- m.d.comb += Assert(dut.r_level == gold.r_level)
- m.d.comb += Assert(dut.w_level == gold.w_level)
-
- if dut.fwft:
- m.d.comb += Assert(dut.r_rdy
- .implies(dut.r_data == gold.r_data))
- else:
- m.d.comb += Assert((Past(dut.r_rdy, domain=self.r_domain) &
- Past(dut.r_en, domain=self.r_domain))
- .implies(dut.r_data == gold.r_data))
-
- return m
-
-
-class FIFOContractSpec(Elaboratable):
- """
- The first-in first-out queue contract specification: if two elements are written to the queue
- consecutively, they must be read out consecutively at some later point, no matter all other
- circumstances, with the exception of reset.
- """
- def __init__(self, fifo, *, r_domain, w_domain, bound):
- self.fifo = fifo
- self.r_domain = r_domain
- self.w_domain = w_domain
- self.bound = bound
-
- def elaborate(self, platform):
- m = Module()
- m.submodules.dut = fifo = self.fifo
-
- m.domains += ClockDomain("sync")
- m.d.comb += ResetSignal().eq(0)
- if self.w_domain != "sync":
- m.domains += ClockDomain(self.w_domain)
- m.d.comb += ResetSignal(self.w_domain).eq(0)
- if self.r_domain != "sync":
- m.domains += ClockDomain(self.r_domain)
- m.d.comb += ResetSignal(self.r_domain).eq(0)
-
- entry_1 = AnyConst(fifo.width)
- entry_2 = AnyConst(fifo.width)
-
- with m.FSM(domain=self.w_domain) as write_fsm:
- with m.State("WRITE-1"):
- with m.If(fifo.w_rdy):
- m.d.comb += [
- fifo.w_data.eq(entry_1),
- fifo.w_en.eq(1)
- ]
- m.next = "WRITE-2"
- with m.State("WRITE-2"):
- with m.If(fifo.w_rdy):
- m.d.comb += [
- fifo.w_data.eq(entry_2),
- fifo.w_en.eq(1)
- ]
- m.next = "DONE"
- with m.State("DONE"):
- pass
-
- with m.FSM(domain=self.r_domain) as read_fsm:
- read_1 = Signal(fifo.width)
- read_2 = Signal(fifo.width)
- with m.State("READ"):
- m.d.comb += fifo.r_en.eq(1)
- if fifo.fwft:
- r_rdy = fifo.r_rdy
- else:
- r_rdy = Past(fifo.r_rdy, domain=self.r_domain)
- with m.If(r_rdy):
- m.d.sync += [
- read_1.eq(read_2),
- read_2.eq(fifo.r_data),
- ]
- with m.If((read_1 == entry_1) & (read_2 == entry_2)):
- m.next = "DONE"
- with m.State("DONE"):
- pass
-
- with m.If(Initial()):
- m.d.comb += Assume(write_fsm.ongoing("WRITE-1"))
- m.d.comb += Assume(read_fsm.ongoing("READ"))
- with m.If(Past(Initial(), self.bound - 1)):
- m.d.comb += Assert(read_fsm.ongoing("DONE"))
-
- with m.If(ResetSignal(domain=self.w_domain)):
- m.d.comb += Assert(~fifo.r_rdy)
-
- if self.w_domain != "sync" or self.r_domain != "sync":
- m.d.comb += Assume(Rose(ClockSignal(self.w_domain)) |
- Rose(ClockSignal(self.r_domain)))
-
- return m
-
-
-class FIFOFormalCase(FHDLTestCase):
- def check_sync_fifo(self, fifo):
- self.assertFormal(FIFOModelEquivalenceSpec(fifo, r_domain="sync", w_domain="sync"),
- mode="bmc", depth=fifo.depth + 1)
- self.assertFormal(FIFOContractSpec(fifo, r_domain="sync", w_domain="sync",
- bound=fifo.depth * 2 + 1),
- mode="hybrid", depth=fifo.depth * 2 + 1)
-
- def test_sync_fwft_pot(self):
- self.check_sync_fifo(SyncFIFO(width=8, depth=4, fwft=True))
-
- def test_sync_fwft_npot(self):
- self.check_sync_fifo(SyncFIFO(width=8, depth=5, fwft=True))
-
- def test_sync_not_fwft_pot(self):
- self.check_sync_fifo(SyncFIFO(width=8, depth=4, fwft=False))
-
- def test_sync_not_fwft_npot(self):
- self.check_sync_fifo(SyncFIFO(width=8, depth=5, fwft=False))
-
- def test_sync_buffered_pot(self):
- self.check_sync_fifo(SyncFIFOBuffered(width=8, depth=4))
-
- def test_sync_buffered_potp1(self):
- self.check_sync_fifo(SyncFIFOBuffered(width=8, depth=5))
-
- def test_sync_buffered_potm1(self):
- self.check_sync_fifo(SyncFIFOBuffered(width=8, depth=3))
-
- def check_async_fifo(self, fifo):
- # TODO: properly doing model equivalence checking on this likely requires multiclock,
- # which is not really documented nor is it clear how to use it.
- # self.assertFormal(FIFOModelEquivalenceSpec(fifo, r_domain="read", w_domain="write"),
- # mode="bmc", depth=fifo.depth * 3 + 1)
- self.assertFormal(FIFOContractSpec(fifo, r_domain="read", w_domain="write",
- bound=fifo.depth * 4 + 1),
- mode="hybrid", depth=fifo.depth * 4 + 1)
-
- def test_async(self):
- self.check_async_fifo(AsyncFIFO(width=8, depth=4))
-
- def test_async_buffered(self):
- self.check_async_fifo(AsyncFIFOBuffered(width=8, depth=4))
+++ /dev/null
-from .utils import *
-from ..hdl import *
-from ..hdl.rec import *
-from ..back.pysim import *
-from ..lib.io import *
-
-
-class PinLayoutTestCase(FHDLTestCase):
- def assertLayoutEqual(self, layout, expected):
- casted_layout = {}
- for name, (shape, dir) in layout.items():
- casted_layout[name] = (Shape.cast(shape), dir)
-
- self.assertEqual(casted_layout, expected)
-
-
-class PinLayoutCombTestCase(PinLayoutTestCase):
- def test_pin_layout_i(self):
- layout_1 = pin_layout(1, dir="i")
- self.assertLayoutEqual(layout_1.fields, {
- "i": ((1, False), DIR_NONE),
- })
-
- layout_2 = pin_layout(2, dir="i")
- self.assertLayoutEqual(layout_2.fields, {
- "i": ((2, False), DIR_NONE),
- })
-
- def test_pin_layout_o(self):
- layout_1 = pin_layout(1, dir="o")
- self.assertLayoutEqual(layout_1.fields, {
- "o": ((1, False), DIR_NONE),
- })
-
- layout_2 = pin_layout(2, dir="o")
- self.assertLayoutEqual(layout_2.fields, {
- "o": ((2, False), DIR_NONE),
- })
-
- def test_pin_layout_oe(self):
- layout_1 = pin_layout(1, dir="oe")
- self.assertLayoutEqual(layout_1.fields, {
- "o": ((1, False), DIR_NONE),
- "oe": ((1, False), DIR_NONE),
- })
-
- layout_2 = pin_layout(2, dir="oe")
- self.assertLayoutEqual(layout_2.fields, {
- "o": ((2, False), DIR_NONE),
- "oe": ((1, False), DIR_NONE),
- })
-
- def test_pin_layout_io(self):
- layout_1 = pin_layout(1, dir="io")
- self.assertLayoutEqual(layout_1.fields, {
- "i": ((1, False), DIR_NONE),
- "o": ((1, False), DIR_NONE),
- "oe": ((1, False), DIR_NONE),
- })
-
- layout_2 = pin_layout(2, dir="io")
- self.assertLayoutEqual(layout_2.fields, {
- "i": ((2, False), DIR_NONE),
- "o": ((2, False), DIR_NONE),
- "oe": ((1, False), DIR_NONE),
- })
-
-
-class PinLayoutSDRTestCase(PinLayoutTestCase):
- def test_pin_layout_i(self):
- layout_1 = pin_layout(1, dir="i", xdr=1)
- self.assertLayoutEqual(layout_1.fields, {
- "i_clk": ((1, False), DIR_NONE),
- "i": ((1, False), DIR_NONE),
- })
-
- layout_2 = pin_layout(2, dir="i", xdr=1)
- self.assertLayoutEqual(layout_2.fields, {
- "i_clk": ((1, False), DIR_NONE),
- "i": ((2, False), DIR_NONE),
- })
-
- def test_pin_layout_o(self):
- layout_1 = pin_layout(1, dir="o", xdr=1)
- self.assertLayoutEqual(layout_1.fields, {
- "o_clk": ((1, False), DIR_NONE),
- "o": ((1, False), DIR_NONE),
- })
-
- layout_2 = pin_layout(2, dir="o", xdr=1)
- self.assertLayoutEqual(layout_2.fields, {
- "o_clk": ((1, False), DIR_NONE),
- "o": ((2, False), DIR_NONE),
- })
-
- def test_pin_layout_oe(self):
- layout_1 = pin_layout(1, dir="oe", xdr=1)
- self.assertLayoutEqual(layout_1.fields, {
- "o_clk": ((1, False), DIR_NONE),
- "o": ((1, False), DIR_NONE),
- "oe": ((1, False), DIR_NONE),
- })
-
- layout_2 = pin_layout(2, dir="oe", xdr=1)
- self.assertLayoutEqual(layout_2.fields, {
- "o_clk": ((1, False), DIR_NONE),
- "o": ((2, False), DIR_NONE),
- "oe": ((1, False), DIR_NONE),
- })
-
- def test_pin_layout_io(self):
- layout_1 = pin_layout(1, dir="io", xdr=1)
- self.assertLayoutEqual(layout_1.fields, {
- "i_clk": ((1, False), DIR_NONE),
- "i": ((1, False), DIR_NONE),
- "o_clk": ((1, False), DIR_NONE),
- "o": ((1, False), DIR_NONE),
- "oe": ((1, False), DIR_NONE),
- })
-
- layout_2 = pin_layout(2, dir="io", xdr=1)
- self.assertLayoutEqual(layout_2.fields, {
- "i_clk": ((1, False), DIR_NONE),
- "i": ((2, False), DIR_NONE),
- "o_clk": ((1, False), DIR_NONE),
- "o": ((2, False), DIR_NONE),
- "oe": ((1, False), DIR_NONE),
- })
-
-
-class PinLayoutDDRTestCase(PinLayoutTestCase):
- def test_pin_layout_i(self):
- layout_1 = pin_layout(1, dir="i", xdr=2)
- self.assertLayoutEqual(layout_1.fields, {
- "i_clk": ((1, False), DIR_NONE),
- "i0": ((1, False), DIR_NONE),
- "i1": ((1, False), DIR_NONE),
- })
-
- layout_2 = pin_layout(2, dir="i", xdr=2)
- self.assertLayoutEqual(layout_2.fields, {
- "i_clk": ((1, False), DIR_NONE),
- "i0": ((2, False), DIR_NONE),
- "i1": ((2, False), DIR_NONE),
- })
-
- def test_pin_layout_o(self):
- layout_1 = pin_layout(1, dir="o", xdr=2)
- self.assertLayoutEqual(layout_1.fields, {
- "o_clk": ((1, False), DIR_NONE),
- "o0": ((1, False), DIR_NONE),
- "o1": ((1, False), DIR_NONE),
- })
-
- layout_2 = pin_layout(2, dir="o", xdr=2)
- self.assertLayoutEqual(layout_2.fields, {
- "o_clk": ((1, False), DIR_NONE),
- "o0": ((2, False), DIR_NONE),
- "o1": ((2, False), DIR_NONE),
- })
-
- def test_pin_layout_oe(self):
- layout_1 = pin_layout(1, dir="oe", xdr=2)
- self.assertLayoutEqual(layout_1.fields, {
- "o_clk": ((1, False), DIR_NONE),
- "o0": ((1, False), DIR_NONE),
- "o1": ((1, False), DIR_NONE),
- "oe": ((1, False), DIR_NONE),
- })
-
- layout_2 = pin_layout(2, dir="oe", xdr=2)
- self.assertLayoutEqual(layout_2.fields, {
- "o_clk": ((1, False), DIR_NONE),
- "o0": ((2, False), DIR_NONE),
- "o1": ((2, False), DIR_NONE),
- "oe": ((1, False), DIR_NONE),
- })
-
- def test_pin_layout_io(self):
- layout_1 = pin_layout(1, dir="io", xdr=2)
- self.assertLayoutEqual(layout_1.fields, {
- "i_clk": ((1, False), DIR_NONE),
- "i0": ((1, False), DIR_NONE),
- "i1": ((1, False), DIR_NONE),
- "o_clk": ((1, False), DIR_NONE),
- "o0": ((1, False), DIR_NONE),
- "o1": ((1, False), DIR_NONE),
- "oe": ((1, False), DIR_NONE),
- })
-
- layout_2 = pin_layout(2, dir="io", xdr=2)
- self.assertLayoutEqual(layout_2.fields, {
- "i_clk": ((1, False), DIR_NONE),
- "i0": ((2, False), DIR_NONE),
- "i1": ((2, False), DIR_NONE),
- "o_clk": ((1, False), DIR_NONE),
- "o0": ((2, False), DIR_NONE),
- "o1": ((2, False), DIR_NONE),
- "oe": ((1, False), DIR_NONE),
- })
-
-
-class PinTestCase(FHDLTestCase):
- def test_attributes(self):
- pin = Pin(2, dir="io", xdr=2)
- self.assertEqual(pin.width, 2)
- self.assertEqual(pin.dir, "io")
- self.assertEqual(pin.xdr, 2)
+++ /dev/null
-# nmigen: UnusedElaboratable=no
-import unittest
-from .utils import *
-from ..hdl import *
-from ..asserts import *
-from ..sim.pysim import *
-from ..lib.scheduler import *
-
-
-class RoundRobinTestCase(unittest.TestCase):
- def test_count(self):
- dut = RoundRobin(count=32)
- self.assertEqual(dut.count, 32)
- self.assertEqual(len(dut.requests), 32)
- self.assertEqual(len(dut.grant), 5)
-
- def test_wrong_count(self):
- with self.assertRaisesRegex(ValueError, r"Count must be a non-negative integer, not 'foo'"):
- dut = RoundRobin(count="foo")
- with self.assertRaisesRegex(ValueError, r"Count must be a non-negative integer, not -1"):
- dut = RoundRobin(count=-1)
-
-
-class RoundRobinSimulationTestCase(unittest.TestCase):
- def test_count_one(self):
- dut = RoundRobin(count=1)
- sim = Simulator(dut)
- def process():
- yield dut.requests.eq(0)
- yield; yield Delay(1e-8)
- self.assertEqual((yield dut.grant), 0)
- self.assertFalse((yield dut.valid))
-
- yield dut.requests.eq(1)
- yield; yield Delay(1e-8)
- self.assertEqual((yield dut.grant), 0)
- self.assertTrue((yield dut.valid))
- sim.add_sync_process(process)
- sim.add_clock(1e-6)
- with sim.write_vcd("test.vcd"):
- sim.run()
-
- def test_transitions(self):
- dut = RoundRobin(count=3)
- sim = Simulator(dut)
- def process():
- yield dut.requests.eq(0b111)
- yield; yield Delay(1e-8)
- self.assertEqual((yield dut.grant), 1)
- self.assertTrue((yield dut.valid))
-
- yield dut.requests.eq(0b110)
- yield; yield Delay(1e-8)
- self.assertEqual((yield dut.grant), 2)
- self.assertTrue((yield dut.valid))
-
- yield dut.requests.eq(0b010)
- yield; yield Delay(1e-8)
- self.assertEqual((yield dut.grant), 1)
- self.assertTrue((yield dut.valid))
-
- yield dut.requests.eq(0b011)
- yield; yield Delay(1e-8)
- self.assertEqual((yield dut.grant), 0)
- self.assertTrue((yield dut.valid))
-
- yield dut.requests.eq(0b001)
- yield; yield Delay(1e-8)
- self.assertEqual((yield dut.grant), 0)
- self.assertTrue((yield dut.valid))
-
- yield dut.requests.eq(0b101)
- yield; yield Delay(1e-8)
- self.assertEqual((yield dut.grant), 2)
- self.assertTrue((yield dut.valid))
-
- yield dut.requests.eq(0b100)
- yield; yield Delay(1e-8)
- self.assertEqual((yield dut.grant), 2)
- self.assertTrue((yield dut.valid))
-
- yield dut.requests.eq(0b000)
- yield; yield Delay(1e-8)
- self.assertFalse((yield dut.valid))
-
- yield dut.requests.eq(0b001)
- yield; yield Delay(1e-8)
- self.assertEqual((yield dut.grant), 0)
- self.assertTrue((yield dut.valid))
- sim.add_sync_process(process)
- sim.add_clock(1e-6)
- with sim.write_vcd("test.vcd"):
- sim.run()
+++ /dev/null
-import os
-from contextlib import contextmanager
-
-from .utils import *
-from .._utils import flatten, union
-from ..hdl.ast import *
-from ..hdl.cd import *
-from ..hdl.mem import *
-from ..hdl.rec import *
-from ..hdl.dsl import *
-from ..hdl.ir import *
-from ..back.pysim import *
-
-
-class SimulatorUnitTestCase(FHDLTestCase):
- def assertStatement(self, stmt, inputs, output, reset=0):
- inputs = [Value.cast(i) for i in inputs]
- output = Value.cast(output)
-
- isigs = [Signal(i.shape(), name=n) for i, n in zip(inputs, "abcd")]
- osig = Signal(output.shape(), name="y", reset=reset)
-
- stmt = stmt(osig, *isigs)
- frag = Fragment()
- frag.add_statements(stmt)
- for signal in flatten(s._lhs_signals() for s in Statement.cast(stmt)):
- frag.add_driver(signal)
-
- sim = Simulator(frag)
- def process():
- for isig, input in zip(isigs, inputs):
- yield isig.eq(input)
- yield Settle()
- self.assertEqual((yield osig), output.value)
- sim.add_process(process)
- with sim.write_vcd("test.vcd", "test.gtkw", traces=[*isigs, osig]):
- sim.run()
-
- def test_invert(self):
- stmt = lambda y, a: y.eq(~a)
- self.assertStatement(stmt, [C(0b0000, 4)], C(0b1111, 4))
- self.assertStatement(stmt, [C(0b1010, 4)], C(0b0101, 4))
- self.assertStatement(stmt, [C(0, 4)], C(-1, 4))
-
- def test_neg(self):
- stmt = lambda y, a: y.eq(-a)
- self.assertStatement(stmt, [C(0b0000, 4)], C(0b0000, 4))
- self.assertStatement(stmt, [C(0b0001, 4)], C(0b1111, 4))
- self.assertStatement(stmt, [C(0b1010, 4)], C(0b0110, 4))
- self.assertStatement(stmt, [C(1, 4)], C(-1, 4))
- self.assertStatement(stmt, [C(5, 4)], C(-5, 4))
-
- def test_bool(self):
- stmt = lambda y, a: y.eq(a.bool())
- self.assertStatement(stmt, [C(0, 4)], C(0))
- self.assertStatement(stmt, [C(1, 4)], C(1))
- self.assertStatement(stmt, [C(2, 4)], C(1))
-
- def test_as_unsigned(self):
- stmt = lambda y, a, b: y.eq(a.as_unsigned() == b)
- self.assertStatement(stmt, [C(0b01, signed(2)), C(0b0001, unsigned(4))], C(1))
- self.assertStatement(stmt, [C(0b11, signed(2)), C(0b0011, unsigned(4))], C(1))
-
- def test_as_signed(self):
- stmt = lambda y, a, b: y.eq(a.as_signed() == b)
- self.assertStatement(stmt, [C(0b01, unsigned(2)), C(0b0001, signed(4))], C(1))
- self.assertStatement(stmt, [C(0b11, unsigned(2)), C(0b1111, signed(4))], C(1))
-
- def test_any(self):
- stmt = lambda y, a: y.eq(a.any())
- self.assertStatement(stmt, [C(0b00, 2)], C(0))
- self.assertStatement(stmt, [C(0b01, 2)], C(1))
- self.assertStatement(stmt, [C(0b10, 2)], C(1))
- self.assertStatement(stmt, [C(0b11, 2)], C(1))
-
- def test_all(self):
- stmt = lambda y, a: y.eq(a.all())
- self.assertStatement(stmt, [C(0b00, 2)], C(0))
- self.assertStatement(stmt, [C(0b01, 2)], C(0))
- self.assertStatement(stmt, [C(0b10, 2)], C(0))
- self.assertStatement(stmt, [C(0b11, 2)], C(1))
-
- def test_xor_unary(self):
- stmt = lambda y, a: y.eq(a.xor())
- self.assertStatement(stmt, [C(0b00, 2)], C(0))
- self.assertStatement(stmt, [C(0b01, 2)], C(1))
- self.assertStatement(stmt, [C(0b10, 2)], C(1))
- self.assertStatement(stmt, [C(0b11, 2)], C(0))
-
- def test_add(self):
- stmt = lambda y, a, b: y.eq(a + b)
- self.assertStatement(stmt, [C(0, 4), C(1, 4)], C(1, 4))
- self.assertStatement(stmt, [C(-5, 4), C(-5, 4)], C(-10, 5))
-
- def test_sub(self):
- stmt = lambda y, a, b: y.eq(a - b)
- self.assertStatement(stmt, [C(2, 4), C(1, 4)], C(1, 4))
- self.assertStatement(stmt, [C(0, 4), C(1, 4)], C(-1, 4))
- self.assertStatement(stmt, [C(0, 4), C(10, 4)], C(-10, 5))
-
- def test_mul(self):
- stmt = lambda y, a, b: y.eq(a * b)
- self.assertStatement(stmt, [C(2, 4), C(1, 4)], C(2, 8))
- self.assertStatement(stmt, [C(2, 4), C(2, 4)], C(4, 8))
- self.assertStatement(stmt, [C(7, 4), C(7, 4)], C(49, 8))
-
- def test_floordiv(self):
- stmt = lambda y, a, b: y.eq(a // b)
- self.assertStatement(stmt, [C(2, 4), C(1, 4)], C(2, 8))
- self.assertStatement(stmt, [C(2, 4), C(2, 4)], C(1, 8))
- self.assertStatement(stmt, [C(7, 4), C(2, 4)], C(3, 8))
-
- def test_mod(self):
- stmt = lambda y, a, b: y.eq(a % b)
- self.assertStatement(stmt, [C(2, 4), C(0, 4)], C(0, 8))
- self.assertStatement(stmt, [C(2, 4), C(1, 4)], C(0, 8))
- self.assertStatement(stmt, [C(2, 4), C(2, 4)], C(0, 8))
- self.assertStatement(stmt, [C(7, 4), C(2, 4)], C(1, 8))
-
- def test_and(self):
- stmt = lambda y, a, b: y.eq(a & b)
- self.assertStatement(stmt, [C(0b1100, 4), C(0b1010, 4)], C(0b1000, 4))
-
- def test_or(self):
- stmt = lambda y, a, b: y.eq(a | b)
- self.assertStatement(stmt, [C(0b1100, 4), C(0b1010, 4)], C(0b1110, 4))
-
- def test_xor_binary(self):
- stmt = lambda y, a, b: y.eq(a ^ b)
- self.assertStatement(stmt, [C(0b1100, 4), C(0b1010, 4)], C(0b0110, 4))
-
- def test_shl(self):
- stmt = lambda y, a, b: y.eq(a << b)
- self.assertStatement(stmt, [C(0b1001, 4), C(0)], C(0b1001, 5))
- self.assertStatement(stmt, [C(0b1001, 4), C(3)], C(0b1001000, 7))
-
- def test_shr(self):
- stmt = lambda y, a, b: y.eq(a >> b)
- self.assertStatement(stmt, [C(0b1001, 4), C(0)], C(0b1001, 4))
- self.assertStatement(stmt, [C(0b1001, 4), C(2)], C(0b10, 4))
-
- def test_eq(self):
- stmt = lambda y, a, b: y.eq(a == b)
- self.assertStatement(stmt, [C(0, 4), C(0, 4)], C(1))
- self.assertStatement(stmt, [C(0, 4), C(1, 4)], C(0))
- self.assertStatement(stmt, [C(1, 4), C(0, 4)], C(0))
-
- def test_ne(self):
- stmt = lambda y, a, b: y.eq(a != b)
- self.assertStatement(stmt, [C(0, 4), C(0, 4)], C(0))
- self.assertStatement(stmt, [C(0, 4), C(1, 4)], C(1))
- self.assertStatement(stmt, [C(1, 4), C(0, 4)], C(1))
-
- def test_lt(self):
- stmt = lambda y, a, b: y.eq(a < b)
- self.assertStatement(stmt, [C(0, 4), C(0, 4)], C(0))
- self.assertStatement(stmt, [C(0, 4), C(1, 4)], C(1))
- self.assertStatement(stmt, [C(1, 4), C(0, 4)], C(0))
-
- def test_ge(self):
- stmt = lambda y, a, b: y.eq(a >= b)
- self.assertStatement(stmt, [C(0, 4), C(0, 4)], C(1))
- self.assertStatement(stmt, [C(0, 4), C(1, 4)], C(0))
- self.assertStatement(stmt, [C(1, 4), C(0, 4)], C(1))
-
- def test_gt(self):
- stmt = lambda y, a, b: y.eq(a > b)
- self.assertStatement(stmt, [C(0, 4), C(0, 4)], C(0))
- self.assertStatement(stmt, [C(0, 4), C(1, 4)], C(0))
- self.assertStatement(stmt, [C(1, 4), C(0, 4)], C(1))
-
- def test_le(self):
- stmt = lambda y, a, b: y.eq(a <= b)
- self.assertStatement(stmt, [C(0, 4), C(0, 4)], C(1))
- self.assertStatement(stmt, [C(0, 4), C(1, 4)], C(1))
- self.assertStatement(stmt, [C(1, 4), C(0, 4)], C(0))
-
- def test_mux(self):
- stmt = lambda y, a, b, c: y.eq(Mux(c, a, b))
- self.assertStatement(stmt, [C(2, 4), C(3, 4), C(0)], C(3, 4))
- self.assertStatement(stmt, [C(2, 4), C(3, 4), C(1)], C(2, 4))
-
- def test_abs(self):
- stmt = lambda y, a: y.eq(abs(a))
- self.assertStatement(stmt, [C(3, unsigned(8))], C(3, unsigned(8)))
- self.assertStatement(stmt, [C(-3, unsigned(8))], C(-3, unsigned(8)))
- self.assertStatement(stmt, [C(3, signed(8))], C(3, signed(8)))
- self.assertStatement(stmt, [C(-3, signed(8))], C(3, signed(8)))
-
- def test_slice(self):
- stmt1 = lambda y, a: y.eq(a[2])
- self.assertStatement(stmt1, [C(0b10110100, 8)], C(0b1, 1))
- stmt2 = lambda y, a: y.eq(a[2:4])
- self.assertStatement(stmt2, [C(0b10110100, 8)], C(0b01, 2))
-
- def test_slice_lhs(self):
- stmt1 = lambda y, a: y[2].eq(a)
- self.assertStatement(stmt1, [C(0b0, 1)], C(0b11111011, 8), reset=0b11111111)
- stmt2 = lambda y, a: y[2:4].eq(a)
- self.assertStatement(stmt2, [C(0b01, 2)], C(0b11110111, 8), reset=0b11111011)
-
- def test_bit_select(self):
- stmt = lambda y, a, b: y.eq(a.bit_select(b, 3))
- self.assertStatement(stmt, [C(0b10110100, 8), C(0)], C(0b100, 3))
- self.assertStatement(stmt, [C(0b10110100, 8), C(2)], C(0b101, 3))
- self.assertStatement(stmt, [C(0b10110100, 8), C(3)], C(0b110, 3))
-
- def test_bit_select_lhs(self):
- stmt = lambda y, a, b: y.bit_select(a, 3).eq(b)
- self.assertStatement(stmt, [C(0), C(0b100, 3)], C(0b11111100, 8), reset=0b11111111)
- self.assertStatement(stmt, [C(2), C(0b101, 3)], C(0b11110111, 8), reset=0b11111111)
- self.assertStatement(stmt, [C(3), C(0b110, 3)], C(0b11110111, 8), reset=0b11111111)
-
- def test_word_select(self):
- stmt = lambda y, a, b: y.eq(a.word_select(b, 3))
- self.assertStatement(stmt, [C(0b10110100, 8), C(0)], C(0b100, 3))
- self.assertStatement(stmt, [C(0b10110100, 8), C(1)], C(0b110, 3))
- self.assertStatement(stmt, [C(0b10110100, 8), C(2)], C(0b010, 3))
-
- def test_word_select_lhs(self):
- stmt = lambda y, a, b: y.word_select(a, 3).eq(b)
- self.assertStatement(stmt, [C(0), C(0b100, 3)], C(0b11111100, 8), reset=0b11111111)
- self.assertStatement(stmt, [C(1), C(0b101, 3)], C(0b11101111, 8), reset=0b11111111)
- self.assertStatement(stmt, [C(2), C(0b110, 3)], C(0b10111111, 8), reset=0b11111111)
-
- def test_cat(self):
- stmt = lambda y, *xs: y.eq(Cat(*xs))
- self.assertStatement(stmt, [C(0b10, 2), C(0b01, 2)], C(0b0110, 4))
-
- def test_cat_lhs(self):
- l = Signal(3)
- m = Signal(3)
- n = Signal(3)
- stmt = lambda y, a: [Cat(l, m, n).eq(a), y.eq(Cat(n, m, l))]
- self.assertStatement(stmt, [C(0b100101110, 9)], C(0b110101100, 9))
-
- def test_nested_cat_lhs(self):
- l = Signal(3)
- m = Signal(3)
- n = Signal(3)
- stmt = lambda y, a: [Cat(Cat(l, Cat(m)), n).eq(a), y.eq(Cat(n, m, l))]
- self.assertStatement(stmt, [C(0b100101110, 9)], C(0b110101100, 9))
-
- def test_record(self):
- rec = Record([
- ("l", 1),
- ("m", 2),
- ])
- stmt = lambda y, a: [rec.eq(a), y.eq(rec)]
- self.assertStatement(stmt, [C(0b101, 3)], C(0b101, 3))
-
- def test_repl(self):
- stmt = lambda y, a: y.eq(Repl(a, 3))
- self.assertStatement(stmt, [C(0b10, 2)], C(0b101010, 6))
-
- def test_array(self):
- array = Array([1, 4, 10])
- stmt = lambda y, a: y.eq(array[a])
- self.assertStatement(stmt, [C(0)], C(1))
- self.assertStatement(stmt, [C(1)], C(4))
- self.assertStatement(stmt, [C(2)], C(10))
-
- def test_array_oob(self):
- array = Array([1, 4, 10])
- stmt = lambda y, a: y.eq(array[a])
- self.assertStatement(stmt, [C(3)], C(10))
- self.assertStatement(stmt, [C(4)], C(10))
-
- def test_array_lhs(self):
- l = Signal(3, reset=1)
- m = Signal(3, reset=4)
- n = Signal(3, reset=7)
- array = Array([l, m, n])
- stmt = lambda y, a, b: [array[a].eq(b), y.eq(Cat(*array))]
- self.assertStatement(stmt, [C(0), C(0b000)], C(0b111100000))
- self.assertStatement(stmt, [C(1), C(0b010)], C(0b111010001))
- self.assertStatement(stmt, [C(2), C(0b100)], C(0b100100001))
-
- def test_array_lhs_oob(self):
- l = Signal(3)
- m = Signal(3)
- n = Signal(3)
- array = Array([l, m, n])
- stmt = lambda y, a, b: [array[a].eq(b), y.eq(Cat(*array))]
- self.assertStatement(stmt, [C(3), C(0b001)], C(0b001000000))
- self.assertStatement(stmt, [C(4), C(0b010)], C(0b010000000))
-
- def test_array_index(self):
- array = Array(Array(x * y for y in range(10)) for x in range(10))
- stmt = lambda y, a, b: y.eq(array[a][b])
- for x in range(10):
- for y in range(10):
- self.assertStatement(stmt, [C(x), C(y)], C(x * y))
-
- def test_array_attr(self):
- from collections import namedtuple
- pair = namedtuple("pair", ("p", "n"))
-
- array = Array(pair(x, -x) for x in range(10))
- stmt = lambda y, a: y.eq(array[a].p + array[a].n)
- for i in range(10):
- self.assertStatement(stmt, [C(i)], C(0))
-
- def test_shift_left(self):
- stmt1 = lambda y, a: y.eq(a.shift_left(1))
- self.assertStatement(stmt1, [C(0b10100010, 8)], C( 0b101000100, 9))
- stmt2 = lambda y, a: y.eq(a.shift_left(4))
- self.assertStatement(stmt2, [C(0b10100010, 8)], C(0b101000100000, 12))
-
- def test_shift_right(self):
- stmt1 = lambda y, a: y.eq(a.shift_right(1))
- self.assertStatement(stmt1, [C(0b10100010, 8)], C(0b1010001, 7))
- stmt2 = lambda y, a: y.eq(a.shift_right(4))
- self.assertStatement(stmt2, [C(0b10100010, 8)], C( 0b1010, 4))
-
- def test_rotate_left(self):
- stmt = lambda y, a: y.eq(a.rotate_left(1))
- self.assertStatement(stmt, [C(0b1)], C(0b1))
- self.assertStatement(stmt, [C(0b1001000)], C(0b0010001))
- stmt = lambda y, a: y.eq(a.rotate_left(5))
- self.assertStatement(stmt, [C(0b1000000)], C(0b0010000))
- self.assertStatement(stmt, [C(0b1000001)], C(0b0110000))
- stmt = lambda y, a: y.eq(a.rotate_left(7))
- self.assertStatement(stmt, [C(0b1000000)], C(0b1000000))
- self.assertStatement(stmt, [C(0b1000001)], C(0b1000001))
- stmt = lambda y, a: y.eq(a.rotate_left(9))
- self.assertStatement(stmt, [C(0b1000000)], C(0b0000010))
- self.assertStatement(stmt, [C(0b1000001)], C(0b0000110))
- stmt = lambda y, a: y.eq(a.rotate_left(-1))
- self.assertStatement(stmt, [C(0b1)], C(0b1))
- self.assertStatement(stmt, [C(0b1001000)], C(0b0100100))
- stmt = lambda y, a: y.eq(a.rotate_left(-5))
- self.assertStatement(stmt, [C(0b1000000)], C(0b0000010))
- self.assertStatement(stmt, [C(0b1000001)], C(0b0000110))
- stmt = lambda y, a: y.eq(a.rotate_left(-7))
- self.assertStatement(stmt, [C(0b1000000)], C(0b1000000))
- self.assertStatement(stmt, [C(0b1000001)], C(0b1000001))
- stmt = lambda y, a: y.eq(a.rotate_left(-9))
- self.assertStatement(stmt, [C(0b1000000)], C(0b0010000))
- self.assertStatement(stmt, [C(0b1000001)], C(0b0110000))
-
- def test_rotate_right(self):
- stmt = lambda y, a: y.eq(a.rotate_right(1))
- self.assertStatement(stmt, [C(0b1)], C(0b1))
- self.assertStatement(stmt, [C(0b1001000)], C(0b0100100))
- stmt = lambda y, a: y.eq(a.rotate_right(5))
- self.assertStatement(stmt, [C(0b1000000)], C(0b0000010))
- self.assertStatement(stmt, [C(0b1000001)], C(0b0000110))
- stmt = lambda y, a: y.eq(a.rotate_right(7))
- self.assertStatement(stmt, [C(0b1000000)], C(0b1000000))
- self.assertStatement(stmt, [C(0b1000001)], C(0b1000001))
- stmt = lambda y, a: y.eq(a.rotate_right(9))
- self.assertStatement(stmt, [C(0b1000000)], C(0b0010000))
- self.assertStatement(stmt, [C(0b1000001)], C(0b0110000))
- stmt = lambda y, a: y.eq(a.rotate_right(-1))
- self.assertStatement(stmt, [C(0b1)], C(0b1))
- self.assertStatement(stmt, [C(0b1001000)], C(0b0010001))
- stmt = lambda y, a: y.eq(a.rotate_right(-5))
- self.assertStatement(stmt, [C(0b1000000)], C(0b0010000))
- self.assertStatement(stmt, [C(0b1000001)], C(0b0110000))
- stmt = lambda y, a: y.eq(a.rotate_right(-7))
- self.assertStatement(stmt, [C(0b1000000)], C(0b1000000))
- self.assertStatement(stmt, [C(0b1000001)], C(0b1000001))
- stmt = lambda y, a: y.eq(a.rotate_right(-9))
- self.assertStatement(stmt, [C(0b1000000)], C(0b0000010))
- self.assertStatement(stmt, [C(0b1000001)], C(0b0000110))
-
-
-class SimulatorIntegrationTestCase(FHDLTestCase):
- @contextmanager
- def assertSimulation(self, module, deadline=None):
- sim = Simulator(module)
- yield sim
- with sim.write_vcd("test.vcd", "test.gtkw"):
- if deadline is None:
- sim.run()
- else:
- sim.run_until(deadline)
-
- def setUp_counter(self):
- self.count = Signal(3, reset=4)
- self.sync = ClockDomain()
-
- self.m = Module()
- self.m.d.sync += self.count.eq(self.count + 1)
- self.m.domains += self.sync
-
- def test_counter_process(self):
- self.setUp_counter()
- with self.assertSimulation(self.m) as sim:
- def process():
- self.assertEqual((yield self.count), 4)
- yield Delay(1e-6)
- self.assertEqual((yield self.count), 4)
- yield self.sync.clk.eq(1)
- self.assertEqual((yield self.count), 4)
- yield Settle()
- self.assertEqual((yield self.count), 5)
- yield Delay(1e-6)
- self.assertEqual((yield self.count), 5)
- yield self.sync.clk.eq(0)
- self.assertEqual((yield self.count), 5)
- yield Settle()
- self.assertEqual((yield self.count), 5)
- for _ in range(3):
- yield Delay(1e-6)
- yield self.sync.clk.eq(1)
- yield Delay(1e-6)
- yield self.sync.clk.eq(0)
- self.assertEqual((yield self.count), 0)
- sim.add_process(process)
-
- def test_counter_clock_and_sync_process(self):
- self.setUp_counter()
- with self.assertSimulation(self.m) as sim:
- sim.add_clock(1e-6, domain="sync")
- def process():
- self.assertEqual((yield self.count), 4)
- self.assertEqual((yield self.sync.clk), 1)
- yield
- self.assertEqual((yield self.count), 5)
- self.assertEqual((yield self.sync.clk), 1)
- for _ in range(3):
- yield
- self.assertEqual((yield self.count), 0)
- sim.add_sync_process(process)
-
- def test_reset(self):
- self.setUp_counter()
- sim = Simulator(self.m)
- sim.add_clock(1e-6)
- times = 0
- def process():
- nonlocal times
- self.assertEqual((yield self.count), 4)
- yield
- self.assertEqual((yield self.count), 5)
- yield
- self.assertEqual((yield self.count), 6)
- yield
- times += 1
- sim.add_sync_process(process)
- sim.run()
- sim.reset()
- sim.run()
- self.assertEqual(times, 2)
-
- def setUp_alu(self):
- self.a = Signal(8)
- self.b = Signal(8)
- self.o = Signal(8)
- self.x = Signal(8)
- self.s = Signal(2)
- self.sync = ClockDomain(reset_less=True)
-
- self.m = Module()
- self.m.d.comb += self.x.eq(self.a ^ self.b)
- with self.m.Switch(self.s):
- with self.m.Case(0):
- self.m.d.sync += self.o.eq(self.a + self.b)
- with self.m.Case(1):
- self.m.d.sync += self.o.eq(self.a - self.b)
- with self.m.Case():
- self.m.d.sync += self.o.eq(0)
- self.m.domains += self.sync
-
- def test_alu(self):
- self.setUp_alu()
- with self.assertSimulation(self.m) as sim:
- sim.add_clock(1e-6)
- def process():
- yield self.a.eq(5)
- yield self.b.eq(1)
- yield
- self.assertEqual((yield self.x), 4)
- yield
- self.assertEqual((yield self.o), 6)
- yield self.s.eq(1)
- yield
- yield
- self.assertEqual((yield self.o), 4)
- yield self.s.eq(2)
- yield
- yield
- self.assertEqual((yield self.o), 0)
- sim.add_sync_process(process)
-
- def setUp_multiclock(self):
- self.sys = ClockDomain()
- self.pix = ClockDomain()
-
- self.m = Module()
- self.m.domains += self.sys, self.pix
-
- def test_multiclock(self):
- self.setUp_multiclock()
- with self.assertSimulation(self.m) as sim:
- sim.add_clock(1e-6, domain="sys")
- sim.add_clock(0.3e-6, domain="pix")
-
- def sys_process():
- yield Passive()
- yield
- yield
- self.fail()
- def pix_process():
- yield
- yield
- yield
- sim.add_sync_process(sys_process, domain="sys")
- sim.add_sync_process(pix_process, domain="pix")
-
- def setUp_lhs_rhs(self):
- self.i = Signal(8)
- self.o = Signal(8)
-
- self.m = Module()
- self.m.d.comb += self.o.eq(self.i)
-
- def test_complex_lhs_rhs(self):
- self.setUp_lhs_rhs()
- with self.assertSimulation(self.m) as sim:
- def process():
- yield self.i.eq(0b10101010)
- yield self.i[:4].eq(-1)
- yield Settle()
- self.assertEqual((yield self.i[:4]), 0b1111)
- self.assertEqual((yield self.i), 0b10101111)
- sim.add_process(process)
-
- def test_run_until(self):
- m = Module()
- s = Signal()
- m.d.sync += s.eq(0)
- with self.assertSimulation(m, deadline=100e-6) as sim:
- sim.add_clock(1e-6)
- def process():
- for _ in range(101):
- yield Delay(1e-6)
- self.fail()
- sim.add_process(process)
-
- def test_add_process_wrong(self):
- with self.assertSimulation(Module()) as sim:
- with self.assertRaisesRegex(TypeError,
- r"^Cannot add a process 1 because it is not a generator function$"):
- sim.add_process(1)
-
- def test_add_process_wrong_generator(self):
- with self.assertSimulation(Module()) as sim:
- with self.assertRaisesRegex(TypeError,
- r"^Cannot add a process <.+?> because it is not a generator function$"):
- def process():
- yield Delay()
- sim.add_process(process())
-
- def test_add_clock_wrong_twice(self):
- m = Module()
- s = Signal()
- m.d.sync += s.eq(0)
- with self.assertSimulation(m) as sim:
- sim.add_clock(1)
- with self.assertRaisesRegex(ValueError,
- r"^Domain 'sync' already has a clock driving it$"):
- sim.add_clock(1)
-
- def test_add_clock_wrong_missing(self):
- m = Module()
- with self.assertSimulation(m) as sim:
- with self.assertRaisesRegex(ValueError,
- r"^Domain 'sync' is not present in simulation$"):
- sim.add_clock(1)
-
- def test_add_clock_if_exists(self):
- m = Module()
- with self.assertSimulation(m) as sim:
- sim.add_clock(1, if_exists=True)
-
- def test_command_wrong(self):
- survived = False
- with self.assertSimulation(Module()) as sim:
- def process():
- nonlocal survived
- with self.assertRaisesRegex(TypeError,
- r"Received unsupported command 1 from process .+?"):
- yield 1
- yield Settle()
- survived = True
- sim.add_process(process)
- self.assertTrue(survived)
-
- def setUp_memory(self, rd_synchronous=True, rd_transparent=True, wr_granularity=None):
- self.m = Module()
- self.memory = Memory(width=8, depth=4, init=[0xaa, 0x55])
- self.m.submodules.rdport = self.rdport = \
- self.memory.read_port(domain="sync" if rd_synchronous else "comb",
- transparent=rd_transparent)
- self.m.submodules.wrport = self.wrport = \
- self.memory.write_port(granularity=wr_granularity)
-
- def test_memory_init(self):
- self.setUp_memory()
- with self.assertSimulation(self.m) as sim:
- def process():
- self.assertEqual((yield self.rdport.data), 0xaa)
- yield self.rdport.addr.eq(1)
- yield
- yield
- self.assertEqual((yield self.rdport.data), 0x55)
- yield self.rdport.addr.eq(2)
- yield
- yield
- self.assertEqual((yield self.rdport.data), 0x00)
- sim.add_clock(1e-6)
- sim.add_sync_process(process)
-
- def test_memory_write(self):
- self.setUp_memory()
- with self.assertSimulation(self.m) as sim:
- def process():
- yield self.wrport.addr.eq(4)
- yield self.wrport.data.eq(0x33)
- yield self.wrport.en.eq(1)
- yield
- yield self.wrport.en.eq(0)
- yield self.rdport.addr.eq(4)
- yield
- self.assertEqual((yield self.rdport.data), 0x33)
- sim.add_clock(1e-6)
- sim.add_sync_process(process)
-
- def test_memory_write_granularity(self):
- self.setUp_memory(wr_granularity=4)
- with self.assertSimulation(self.m) as sim:
- def process():
- yield self.wrport.data.eq(0x50)
- yield self.wrport.en.eq(0b00)
- yield
- yield self.wrport.en.eq(0)
- yield
- self.assertEqual((yield self.rdport.data), 0xaa)
- yield self.wrport.en.eq(0b10)
- yield
- yield self.wrport.en.eq(0)
- yield
- self.assertEqual((yield self.rdport.data), 0x5a)
- yield self.wrport.data.eq(0x33)
- yield self.wrport.en.eq(0b01)
- yield
- yield self.wrport.en.eq(0)
- yield
- self.assertEqual((yield self.rdport.data), 0x53)
- sim.add_clock(1e-6)
- sim.add_sync_process(process)
-
- def test_memory_read_before_write(self):
- self.setUp_memory(rd_transparent=False)
- with self.assertSimulation(self.m) as sim:
- def process():
- yield self.wrport.data.eq(0x33)
- yield self.wrport.en.eq(1)
- yield
- self.assertEqual((yield self.rdport.data), 0xaa)
- yield
- self.assertEqual((yield self.rdport.data), 0xaa)
- yield Settle()
- self.assertEqual((yield self.rdport.data), 0x33)
- sim.add_clock(1e-6)
- sim.add_sync_process(process)
-
- def test_memory_write_through(self):
- self.setUp_memory(rd_transparent=True)
- with self.assertSimulation(self.m) as sim:
- def process():
- yield self.wrport.data.eq(0x33)
- yield self.wrport.en.eq(1)
- yield
- self.assertEqual((yield self.rdport.data), 0xaa)
- yield Settle()
- self.assertEqual((yield self.rdport.data), 0x33)
- yield
- yield self.rdport.addr.eq(1)
- yield Settle()
- self.assertEqual((yield self.rdport.data), 0x33)
- sim.add_clock(1e-6)
- sim.add_sync_process(process)
-
- def test_memory_async_read_write(self):
- self.setUp_memory(rd_synchronous=False)
- with self.assertSimulation(self.m) as sim:
- def process():
- yield self.rdport.addr.eq(0)
- yield Settle()
- self.assertEqual((yield self.rdport.data), 0xaa)
- yield self.rdport.addr.eq(1)
- yield Settle()
- self.assertEqual((yield self.rdport.data), 0x55)
- yield self.rdport.addr.eq(0)
- yield self.wrport.addr.eq(0)
- yield self.wrport.data.eq(0x33)
- yield self.wrport.en.eq(1)
- yield Tick("sync")
- self.assertEqual((yield self.rdport.data), 0xaa)
- yield Settle()
- self.assertEqual((yield self.rdport.data), 0x33)
- sim.add_clock(1e-6)
- sim.add_process(process)
-
- def test_memory_read_only(self):
- self.m = Module()
- self.memory = Memory(width=8, depth=4, init=[0xaa, 0x55])
- self.m.submodules.rdport = self.rdport = self.memory.read_port()
- with self.assertSimulation(self.m) as sim:
- def process():
- self.assertEqual((yield self.rdport.data), 0xaa)
- yield self.rdport.addr.eq(1)
- yield
- yield
- self.assertEqual((yield self.rdport.data), 0x55)
- sim.add_clock(1e-6)
- sim.add_sync_process(process)
-
- def test_sample_helpers(self):
- m = Module()
- s = Signal(2)
- def mk(x):
- y = Signal.like(x)
- m.d.comb += y.eq(x)
- return y
- p0, r0, f0, s0 = mk(Past(s, 0)), mk(Rose(s)), mk(Fell(s)), mk(Stable(s))
- p1, r1, f1, s1 = mk(Past(s)), mk(Rose(s, 1)), mk(Fell(s, 1)), mk(Stable(s, 1))
- p2, r2, f2, s2 = mk(Past(s, 2)), mk(Rose(s, 2)), mk(Fell(s, 2)), mk(Stable(s, 2))
- p3, r3, f3, s3 = mk(Past(s, 3)), mk(Rose(s, 3)), mk(Fell(s, 3)), mk(Stable(s, 3))
- with self.assertSimulation(m) as sim:
- def process_gen():
- yield s.eq(0b10)
- yield
- yield
- yield s.eq(0b01)
- yield
- def process_check():
- yield
- yield
- yield
-
- self.assertEqual((yield p0), 0b01)
- self.assertEqual((yield p1), 0b10)
- self.assertEqual((yield p2), 0b10)
- self.assertEqual((yield p3), 0b00)
-
- self.assertEqual((yield s0), 0b0)
- self.assertEqual((yield s1), 0b1)
- self.assertEqual((yield s2), 0b0)
- self.assertEqual((yield s3), 0b1)
-
- self.assertEqual((yield r0), 0b01)
- self.assertEqual((yield r1), 0b00)
- self.assertEqual((yield r2), 0b10)
- self.assertEqual((yield r3), 0b00)
-
- self.assertEqual((yield f0), 0b10)
- self.assertEqual((yield f1), 0b00)
- self.assertEqual((yield f2), 0b00)
- self.assertEqual((yield f3), 0b00)
- sim.add_clock(1e-6)
- sim.add_sync_process(process_gen)
- sim.add_sync_process(process_check)
-
- def test_vcd_wrong_nonzero_time(self):
- s = Signal()
- m = Module()
- m.d.sync += s.eq(s)
- sim = Simulator(m)
- sim.add_clock(1e-6)
- sim.run_until(1e-5)
- with self.assertRaisesRegex(ValueError,
- r"^Cannot start writing waveforms after advancing simulation time$"):
- with sim.write_vcd(open(os.path.devnull, "wt")):
- pass
-
-
-class SimulatorRegressionTestCase(FHDLTestCase):
- def test_bug_325(self):
- dut = Module()
- dut.d.comb += Signal().eq(Cat())
- Simulator(dut).run()
-
- def test_bug_325_bis(self):
- dut = Module()
- dut.d.comb += Signal().eq(Repl(Const(1), 0))
- Simulator(dut).run()
-
- def test_bug_473(self):
- sim = Simulator(Module())
- def process():
- self.assertEqual((yield -(Const(0b11, 2).as_signed())), 1)
- sim.add_process(process)
- sim.run()
+++ /dev/null
-import os
-import re
-import shutil
-import subprocess
-import textwrap
-import traceback
-import unittest
-import warnings
-from contextlib import contextmanager
-
-from ..hdl.ast import *
-from ..hdl.ir import *
-from ..back import rtlil
-from .._toolchain import require_tool
-
-
-__all__ = ["FHDLTestCase"]
-
-
-class FHDLTestCase(unittest.TestCase):
- def assertRepr(self, obj, repr_str):
- if isinstance(obj, list):
- obj = Statement.cast(obj)
- def prepare_repr(repr_str):
- repr_str = re.sub(r"\s+", " ", repr_str)
- repr_str = re.sub(r"\( (?=\()", "(", repr_str)
- repr_str = re.sub(r"\) (?=\))", ")", repr_str)
- return repr_str.strip()
- self.assertEqual(prepare_repr(repr(obj)), prepare_repr(repr_str))
-
- def assertFormal(self, spec, mode="bmc", depth=1):
- caller, *_ = traceback.extract_stack(limit=2)
- spec_root, _ = os.path.splitext(caller.filename)
- spec_dir = os.path.dirname(spec_root)
- spec_name = "{}_{}".format(
- os.path.basename(spec_root).replace("test_", "spec_"),
- caller.name.replace("test_", "")
- )
-
- # The sby -f switch seems not fully functional when sby is reading from stdin.
- if os.path.exists(os.path.join(spec_dir, spec_name)):
- shutil.rmtree(os.path.join(spec_dir, spec_name))
-
- if mode == "hybrid":
- # A mix of BMC and k-induction, as per personal communication with Clifford Wolf.
- script = "setattr -unset init w:* a:nmigen.sample_reg %d"
- mode = "bmc"
- else:
- script = ""
-
- config = textwrap.dedent("""\
- [options]
- mode {mode}
- depth {depth}
- wait on
-
- [engines]
- smtbmc
-
- [script]
- read_ilang top.il
- prep
- {script}
-
- [file top.il]
- {rtlil}
- """).format(
- mode=mode,
- depth=depth,
- script=script,
- rtlil=rtlil.convert(Fragment.get(spec, platform="formal"))
- )
- with subprocess.Popen([require_tool("sby"), "-f", "-d", spec_name], cwd=spec_dir,
- universal_newlines=True,
- stdin=subprocess.PIPE, stdout=subprocess.PIPE) as proc:
- stdout, stderr = proc.communicate(config)
- if proc.returncode != 0:
- self.fail("Formal verification failed:\n" + stdout)
"builtin-yosys": ["nmigen-yosys>=0.9.*"],
"remote-build": ["paramiko~=2.7"],
},
- packages=find_packages(exclude=["*.test*"]),
+ packages=find_packages(),
entry_points={
"console_scripts": [
"nmigen-rpc = nmigen.rpc:main",
--- /dev/null
+from nmigen.compat import *
+from nmigen.compat.fhdl import verilog
+from nmigen._utils import _ignore_deprecated
+
+
+class SimCase:
+ def setUp(self, *args, **kwargs):
+ with _ignore_deprecated():
+ self.tb = self.TestBench(*args, **kwargs)
+
+ def test_to_verilog(self):
+ verilog.convert(self.tb)
+
+ def run_with(self, generator):
+ with _ignore_deprecated():
+ run_simulation(self.tb, generator)
--- /dev/null
+# nmigen: UnusedElaboratable=no
+
+import unittest
+
+from nmigen.compat import *
+from nmigen.compat.genlib.coding import *
+
+from .support import SimCase
+
+
+class EncCase(SimCase, unittest.TestCase):
+ class TestBench(Module):
+ def __init__(self):
+ self.submodules.dut = Encoder(8)
+
+ def test_sizes(self):
+ self.assertEqual(len(self.tb.dut.i), 8)
+ self.assertEqual(len(self.tb.dut.o), 3)
+ self.assertEqual(len(self.tb.dut.n), 1)
+
+ def test_run_sequence(self):
+ seq = list(range(1<<8))
+ def gen():
+ for _ in range(256):
+ if seq:
+ yield self.tb.dut.i.eq(seq.pop(0))
+ yield
+ if (yield self.tb.dut.n):
+ self.assertNotIn((yield self.tb.dut.i), [1<<i for i in range(8)])
+ else:
+ self.assertEqual((yield self.tb.dut.i), 1<<(yield self.tb.dut.o))
+ self.run_with(gen())
+
+
+class PrioEncCase(SimCase, unittest.TestCase):
+ class TestBench(Module):
+ def __init__(self):
+ self.submodules.dut = PriorityEncoder(8)
+
+ def test_sizes(self):
+ self.assertEqual(len(self.tb.dut.i), 8)
+ self.assertEqual(len(self.tb.dut.o), 3)
+ self.assertEqual(len(self.tb.dut.n), 1)
+
+ def test_run_sequence(self):
+ seq = list(range(1<<8))
+ def gen():
+ for _ in range(256):
+ if seq:
+ yield self.tb.dut.i.eq(seq.pop(0))
+ yield
+ i = yield self.tb.dut.i
+ if (yield self.tb.dut.n):
+ self.assertEqual(i, 0)
+ else:
+ o = yield self.tb.dut.o
+ if o > 0:
+ self.assertEqual(i & 1<<(o - 1), 0)
+ self.assertGreaterEqual(i, 1<<o)
+ self.run_with(gen())
+
+
+class DecCase(SimCase, unittest.TestCase):
+ class TestBench(Module):
+ def __init__(self):
+ self.submodules.dut = Decoder(8)
+
+ def test_sizes(self):
+ self.assertEqual(len(self.tb.dut.i), 3)
+ self.assertEqual(len(self.tb.dut.o), 8)
+ self.assertEqual(len(self.tb.dut.n), 1)
+
+ def test_run_sequence(self):
+ seq = list(range(8*2))
+ def gen():
+ for _ in range(256):
+ if seq:
+ i = seq.pop()
+ yield self.tb.dut.i.eq(i//2)
+ yield self.tb.dut.n.eq(i%2)
+ yield
+ i = yield self.tb.dut.i
+ o = yield self.tb.dut.o
+ if (yield self.tb.dut.n):
+ self.assertEqual(o, 0)
+ else:
+ self.assertEqual(o, 1<<i)
+ self.run_with(gen())
+
+
+class SmallPrioEncCase(SimCase, unittest.TestCase):
+ class TestBench(Module):
+ def __init__(self):
+ self.submodules.dut = PriorityEncoder(1)
+
+ def test_sizes(self):
+ self.assertEqual(len(self.tb.dut.i), 1)
+ self.assertEqual(len(self.tb.dut.o), 1)
+ self.assertEqual(len(self.tb.dut.n), 1)
+
+ def test_run_sequence(self):
+ seq = list(range(1))
+ def gen():
+ for _ in range(5):
+ if seq:
+ yield self.tb.dut.i.eq(seq.pop(0))
+ yield
+ i = yield self.tb.dut.i
+ if (yield self.tb.dut.n):
+ self.assertEqual(i, 0)
+ else:
+ o = yield self.tb.dut.o
+ if o > 0:
+ self.assertEqual(i & 1<<(o - 1), 0)
+ self.assertGreaterEqual(i, 1<<o)
+ self.run_with(gen())
--- /dev/null
+import unittest
+
+from nmigen.compat import *
+
+from .support import SimCase
+
+
+class ConstantCase(SimCase, unittest.TestCase):
+ class TestBench(Module):
+ def __init__(self):
+ self.sigs = [
+ (Signal(3), Constant(0), 0),
+ (Signal(3), Constant(5), 5),
+ (Signal(3), Constant(1, 2), 1),
+ (Signal(3), Constant(-1, 7), 7),
+ (Signal(3), Constant(0b10101)[:3], 0b101),
+ (Signal(3), Constant(0b10101)[1:4], 0b10),
+ (Signal(4), Constant(0b1100)[::-1], 0b0011),
+ ]
+ self.comb += [a.eq(b) for a, b, c in self.sigs]
+
+ def test_comparisons(self):
+ def gen():
+ for s, l, v in self.tb.sigs:
+ s = yield s
+ self.assertEqual(
+ s, int(v),
+ "got {}, want {} from literal {}".format(
+ s, v, l))
+ self.run_with(gen())
--- /dev/null
+import unittest
+from itertools import count
+
+from nmigen.compat import *
+from nmigen.compat.genlib.fifo import SyncFIFO
+
+from .support import SimCase
+
+
+class SyncFIFOCase(SimCase, unittest.TestCase):
+ class TestBench(Module):
+ def __init__(self):
+ self.submodules.dut = SyncFIFO(64, 2)
+
+ self.sync += [
+ If(self.dut.we & self.dut.writable,
+ self.dut.din[:32].eq(self.dut.din[:32] + 1),
+ self.dut.din[32:].eq(self.dut.din[32:] + 2)
+ )
+ ]
+
+ def test_run_sequence(self):
+ seq = list(range(20))
+ def gen():
+ for cycle in count():
+ # fire re and we at "random"
+ yield self.tb.dut.we.eq(cycle % 2 == 0)
+ yield self.tb.dut.re.eq(cycle % 3 == 0)
+ # the output if valid must be correct
+ if (yield self.tb.dut.readable) and (yield self.tb.dut.re):
+ try:
+ i = seq.pop(0)
+ except IndexError:
+ break
+ self.assertEqual((yield self.tb.dut.dout[:32]), i)
+ self.assertEqual((yield self.tb.dut.dout[32:]), i*2)
+ yield
+ self.run_with(gen())
--- /dev/null
+import unittest
+from itertools import count
+
+from nmigen.compat import *
+from nmigen.compat.genlib.fsm import FSM
+
+from .support import SimCase
+
+
+class FSMCase(SimCase, unittest.TestCase):
+ class TestBench(Module):
+ def __init__(self):
+ self.ctrl = Signal()
+ self.data = Signal()
+ self.status = Signal(8)
+
+ self.submodules.dut = FSM()
+ self.dut.act("IDLE",
+ If(self.ctrl,
+ NextState("START")
+ )
+ )
+ self.dut.act("START",
+ If(self.data,
+ NextState("SET-STATUS-LOW")
+ ).Else(
+ NextState("SET-STATUS")
+ )
+ )
+ self.dut.act("SET-STATUS",
+ NextValue(self.status, 0xaa),
+ NextState("IDLE")
+ )
+ self.dut.act("SET-STATUS-LOW",
+ NextValue(self.status[:4], 0xb),
+ NextState("IDLE")
+ )
+
+ def assertState(self, fsm, state):
+ self.assertEqual(fsm.decoding[(yield fsm.state)], state)
+
+ def test_next_state(self):
+ def gen():
+ yield from self.assertState(self.tb.dut, "IDLE")
+ yield
+ yield from self.assertState(self.tb.dut, "IDLE")
+ yield self.tb.ctrl.eq(1)
+ yield
+ yield from self.assertState(self.tb.dut, "IDLE")
+ yield self.tb.ctrl.eq(0)
+ yield
+ yield from self.assertState(self.tb.dut, "START")
+ yield
+ yield from self.assertState(self.tb.dut, "SET-STATUS")
+ yield self.tb.ctrl.eq(1)
+ yield
+ yield from self.assertState(self.tb.dut, "IDLE")
+ yield self.tb.ctrl.eq(0)
+ yield self.tb.data.eq(1)
+ yield
+ yield from self.assertState(self.tb.dut, "START")
+ yield self.tb.data.eq(0)
+ yield
+ yield from self.assertState(self.tb.dut, "SET-STATUS-LOW")
+ self.run_with(gen())
+
+ def test_next_value(self):
+ def gen():
+ self.assertEqual((yield self.tb.status), 0x00)
+ yield self.tb.ctrl.eq(1)
+ yield
+ yield self.tb.ctrl.eq(0)
+ yield
+ yield
+ yield from self.assertState(self.tb.dut, "SET-STATUS")
+ yield self.tb.ctrl.eq(1)
+ yield
+ self.assertEqual((yield self.tb.status), 0xaa)
+ yield self.tb.ctrl.eq(0)
+ yield self.tb.data.eq(1)
+ yield
+ yield self.tb.data.eq(0)
+ yield
+ yield from self.assertState(self.tb.dut, "SET-STATUS-LOW")
+ yield
+ self.assertEqual((yield self.tb.status), 0xab)
+ self.run_with(gen())
--- /dev/null
+import unittest
+
+from nmigen.compat import *
+
+
+class PassiveCase(unittest.TestCase):
+ def test_terminates_correctly(self):
+ n = 5
+
+ count = 0
+ @passive
+ def counter():
+ nonlocal count
+ while True:
+ yield
+ count += 1
+
+ def terminator():
+ for i in range(n):
+ yield
+
+ run_simulation(Module(), [counter(), terminator()])
+ self.assertEqual(count, n)
--- /dev/null
+import unittest
+
+from nmigen import Signal, Module, Elaboratable
+
+from .support import SimCase
+
+
+class RunSimulation(SimCase, unittest.TestCase):
+ """ test for https://github.com/nmigen/nmigen/issues/344 """
+
+ class TestBench(Elaboratable):
+ def __init__(self):
+ self.a = Signal()
+
+ def elaborate(self, platform):
+ m = Module()
+ m.d.sync += self.a.eq(~self.a)
+ return m
+
+ def test_run_simulation(self):
+ def gen():
+ yield
+ for i in range(10):
+ yield
+ a = (yield self.tb.a)
+ self.assertEqual(a, i % 2)
+
+ self.run_with(gen())
--- /dev/null
+import unittest
+
+from nmigen.compat import *
+
+from .support import SimCase
+
+
+class SignedCase(SimCase, unittest.TestCase):
+ class TestBench(Module):
+ def __init__(self):
+ self.a = Signal((3, True))
+ self.b = Signal((4, True))
+ comps = [
+ lambda p, q: p > q,
+ lambda p, q: p >= q,
+ lambda p, q: p < q,
+ lambda p, q: p <= q,
+ lambda p, q: p == q,
+ lambda p, q: p != q,
+ ]
+ self.vals = []
+ for asign in 1, -1:
+ for bsign in 1, -1:
+ for f in comps:
+ r = Signal()
+ r0 = f(asign*self.a, bsign*self.b)
+ self.comb += r.eq(r0)
+ self.vals.append((asign, bsign, f, r, r0.op))
+
+ def test_comparisons(self):
+ def gen():
+ for i in range(-4, 4):
+ yield self.tb.a.eq(i)
+ yield self.tb.b.eq(i)
+ yield
+ a = yield self.tb.a
+ b = yield self.tb.b
+ for asign, bsign, f, r, op in self.tb.vals:
+ r, r0 = (yield r), f(asign*a, bsign*b)
+ self.assertEqual(r, int(r0),
+ "got {}, want {}*{} {} {}*{} = {}".format(
+ r, asign, a, op, bsign, b, r0))
+ self.run_with(gen())
--- /dev/null
+import unittest
+
+from nmigen._utils import _ignore_deprecated
+from nmigen.compat import *
+
+
+def _same_slices(a, b):
+ return a.value is b.value and a.start == b.start and a.stop == b.stop
+
+
+class SignalSizeCase(unittest.TestCase):
+ def setUp(self):
+ self.i = C(0xaa)
+ self.j = C(-127)
+ with _ignore_deprecated():
+ self.s = Signal((13, True))
+
+ def test_len(self):
+ self.assertEqual(len(self.s), 13)
+ self.assertEqual(len(self.i), 8)
+ self.assertEqual(len(self.j), 8)
--- /dev/null
+from collections import OrderedDict
+
+from nmigen.build.dsl import *
+
+from .utils import *
+
+
+class PinsTestCase(FHDLTestCase):
+ def test_basic(self):
+ p = Pins("A0 A1 A2")
+ self.assertEqual(repr(p), "(pins io A0 A1 A2)")
+ self.assertEqual(len(p.names), 3)
+ self.assertEqual(p.dir, "io")
+ self.assertEqual(p.invert, False)
+ self.assertEqual(list(p), ["A0", "A1", "A2"])
+
+ def test_invert(self):
+ p = PinsN("A0")
+ self.assertEqual(repr(p), "(pins-n io A0)")
+ self.assertEqual(p.invert, True)
+
+ def test_invert_arg(self):
+ p = Pins("A0", invert=True)
+ self.assertEqual(p.invert, True)
+
+ def test_conn(self):
+ p = Pins("0 1 2", conn=("pmod", 0))
+ self.assertEqual(list(p), ["pmod_0:0", "pmod_0:1", "pmod_0:2"])
+ p = Pins("0 1 2", conn=("pmod", "a"))
+ self.assertEqual(list(p), ["pmod_a:0", "pmod_a:1", "pmod_a:2"])
+
+ def test_map_names(self):
+ p = Pins("0 1 2", conn=("pmod", 0))
+ mapping = {
+ "pmod_0:0": "A0",
+ "pmod_0:1": "A1",
+ "pmod_0:2": "A2",
+ }
+ self.assertEqual(p.map_names(mapping, p), ["A0", "A1", "A2"])
+
+ def test_map_names_recur(self):
+ p = Pins("0", conn=("pmod", 0))
+ mapping = {
+ "pmod_0:0": "ext_0:1",
+ "ext_0:1": "A1",
+ }
+ self.assertEqual(p.map_names(mapping, p), ["A1"])
+
+ def test_wrong_names(self):
+ with self.assertRaisesRegex(TypeError,
+ r"^Names must be a whitespace-separated string, not \['A0', 'A1', 'A2'\]$"):
+ p = Pins(["A0", "A1", "A2"])
+
+ def test_wrong_dir(self):
+ with self.assertRaisesRegex(TypeError,
+ r"^Direction must be one of \"i\", \"o\", \"oe\", or \"io\", not 'wrong'$"):
+ p = Pins("A0 A1", dir="wrong")
+
+ def test_wrong_conn(self):
+ with self.assertRaisesRegex(TypeError,
+ (r"^Connector must be None or a pair of string \(connector name\) and "
+ r"integer\/string \(connector number\), not \('foo', None\)$")):
+ p = Pins("A0 A1", conn=("foo", None))
+
+ def test_wrong_map_names(self):
+ p = Pins("0 1 2", conn=("pmod", 0))
+ mapping = {
+ "pmod_0:0": "A0",
+ }
+ with self.assertRaisesRegex(NameError,
+ (r"^Resource \(pins io pmod_0:0 pmod_0:1 pmod_0:2\) refers to nonexistent "
+ r"connector pin pmod_0:1$")):
+ p.map_names(mapping, p)
+
+ def test_wrong_assert_width(self):
+ with self.assertRaisesRegex(AssertionError,
+ r"^3 names are specified \(0 1 2\), but 4 names are expected$"):
+ Pins("0 1 2", assert_width=4)
+
+
+class DiffPairsTestCase(FHDLTestCase):
+ def test_basic(self):
+ dp = DiffPairs(p="A0 A1", n="B0 B1")
+ self.assertEqual(repr(dp), "(diffpairs io (p A0 A1) (n B0 B1))")
+ self.assertEqual(dp.p.names, ["A0", "A1"])
+ self.assertEqual(dp.n.names, ["B0", "B1"])
+ self.assertEqual(dp.dir, "io")
+ self.assertEqual(list(dp), [("A0", "B0"), ("A1", "B1")])
+
+ def test_invert(self):
+ dp = DiffPairsN(p="A0", n="B0")
+ self.assertEqual(repr(dp), "(diffpairs-n io (p A0) (n B0))")
+ self.assertEqual(dp.p.names, ["A0"])
+ self.assertEqual(dp.n.names, ["B0"])
+ self.assertEqual(dp.invert, True)
+
+ def test_conn(self):
+ dp = DiffPairs(p="0 1 2", n="3 4 5", conn=("pmod", 0))
+ self.assertEqual(list(dp), [
+ ("pmod_0:0", "pmod_0:3"),
+ ("pmod_0:1", "pmod_0:4"),
+ ("pmod_0:2", "pmod_0:5"),
+ ])
+
+ def test_dir(self):
+ dp = DiffPairs("A0", "B0", dir="o")
+ self.assertEqual(dp.dir, "o")
+ self.assertEqual(dp.p.dir, "o")
+ self.assertEqual(dp.n.dir, "o")
+
+ def test_wrong_width(self):
+ with self.assertRaisesRegex(TypeError,
+ (r"^Positive and negative pins must have the same width, but \(pins io A0\) "
+ r"and \(pins io B0 B1\) do not$")):
+ dp = DiffPairs("A0", "B0 B1")
+
+ def test_wrong_assert_width(self):
+ with self.assertRaisesRegex(AssertionError,
+ r"^3 names are specified \(0 1 2\), but 4 names are expected$"):
+ DiffPairs("0 1 2", "3 4 5", assert_width=4)
+
+
+class AttrsTestCase(FHDLTestCase):
+ def test_basic(self):
+ a = Attrs(IO_STANDARD="LVCMOS33", PULLUP=1)
+ self.assertEqual(a["IO_STANDARD"], "LVCMOS33")
+ self.assertEqual(repr(a), "(attrs IO_STANDARD='LVCMOS33' PULLUP=1)")
+
+ def test_remove(self):
+ a = Attrs(FOO=None)
+ self.assertEqual(a["FOO"], None)
+ self.assertEqual(repr(a), "(attrs !FOO)")
+
+ def test_callable(self):
+ fn = lambda self: "FOO"
+ a = Attrs(FOO=fn)
+ self.assertEqual(a["FOO"], fn)
+ self.assertEqual(repr(a), "(attrs FOO={!r})".format(fn))
+
+ def test_wrong_value(self):
+ with self.assertRaisesRegex(TypeError,
+ r"^Value of attribute FOO must be None, int, str, or callable, not 1\.0$"):
+ a = Attrs(FOO=1.0)
+
+
+class ClockTestCase(FHDLTestCase):
+ def test_basic(self):
+ c = Clock(1_000_000)
+ self.assertEqual(c.frequency, 1e6)
+ self.assertEqual(c.period, 1e-6)
+ self.assertEqual(repr(c), "(clock 1000000.0)")
+
+
+class SubsignalTestCase(FHDLTestCase):
+ def test_basic_pins(self):
+ s = Subsignal("a", Pins("A0"), Attrs(IOSTANDARD="LVCMOS33"))
+ self.assertEqual(repr(s),
+ "(subsignal a (pins io A0) (attrs IOSTANDARD='LVCMOS33'))")
+
+ def test_basic_diffpairs(self):
+ s = Subsignal("a", DiffPairs("A0", "B0"))
+ self.assertEqual(repr(s),
+ "(subsignal a (diffpairs io (p A0) (n B0)))")
+
+ def test_basic_subsignals(self):
+ s = Subsignal("a",
+ Subsignal("b", Pins("A0")),
+ Subsignal("c", Pins("A1")))
+ self.assertEqual(repr(s),
+ "(subsignal a (subsignal b (pins io A0)) "
+ "(subsignal c (pins io A1)))")
+
+ def test_attrs(self):
+ s = Subsignal("a",
+ Subsignal("b", Pins("A0")),
+ Subsignal("c", Pins("A0"), Attrs(SLEW="FAST")),
+ Attrs(IOSTANDARD="LVCMOS33"))
+ self.assertEqual(s.attrs, {"IOSTANDARD": "LVCMOS33"})
+ self.assertEqual(s.ios[0].attrs, {})
+ self.assertEqual(s.ios[1].attrs, {"SLEW": "FAST"})
+
+ def test_attrs_many(self):
+ s = Subsignal("a", Pins("A0"), Attrs(SLEW="FAST"), Attrs(PULLUP="1"))
+ self.assertEqual(s.attrs, {"SLEW": "FAST", "PULLUP": "1"})
+
+ def test_clock(self):
+ s = Subsignal("a", Pins("A0"), Clock(1e6))
+ self.assertEqual(s.clock.frequency, 1e6)
+
+ def test_wrong_empty_io(self):
+ with self.assertRaisesRegex(ValueError, r"^Missing I\/O constraints$"):
+ s = Subsignal("a")
+
+ def test_wrong_io(self):
+ with self.assertRaisesRegex(TypeError,
+ (r"^Constraint must be one of Pins, DiffPairs, Subsignal, Attrs, or Clock, "
+ r"not 'wrong'$")):
+ s = Subsignal("a", "wrong")
+
+ def test_wrong_pins(self):
+ with self.assertRaisesRegex(TypeError,
+ (r"^Pins and DiffPairs are incompatible with other location or subsignal "
+ r"constraints, but \(pins io A1\) appears after \(pins io A0\)$")):
+ s = Subsignal("a", Pins("A0"), Pins("A1"))
+
+ def test_wrong_diffpairs(self):
+ with self.assertRaisesRegex(TypeError,
+ (r"^Pins and DiffPairs are incompatible with other location or subsignal "
+ r"constraints, but \(pins io A1\) appears after \(diffpairs io \(p A0\) \(n B0\)\)$")):
+ s = Subsignal("a", DiffPairs("A0", "B0"), Pins("A1"))
+
+ def test_wrong_subsignals(self):
+ with self.assertRaisesRegex(TypeError,
+ (r"^Pins and DiffPairs are incompatible with other location or subsignal "
+ r"constraints, but \(pins io B0\) appears after \(subsignal b \(pins io A0\)\)$")):
+ s = Subsignal("a", Subsignal("b", Pins("A0")), Pins("B0"))
+
+ def test_wrong_clock(self):
+ with self.assertRaisesRegex(TypeError,
+ (r"^Clock constraint can only be applied to Pins or DiffPairs, not "
+ r"\(subsignal b \(pins io A0\)\)$")):
+ s = Subsignal("a", Subsignal("b", Pins("A0")), Clock(1e6))
+
+ def test_wrong_clock_many(self):
+ with self.assertRaisesRegex(ValueError,
+ r"^Clock constraint can be applied only once$"):
+ s = Subsignal("a", Pins("A0"), Clock(1e6), Clock(1e7))
+
+
+class ResourceTestCase(FHDLTestCase):
+ def test_basic(self):
+ r = Resource("serial", 0,
+ Subsignal("tx", Pins("A0", dir="o")),
+ Subsignal("rx", Pins("A1", dir="i")),
+ Attrs(IOSTANDARD="LVCMOS33"))
+ self.assertEqual(repr(r), "(resource serial 0"
+ " (subsignal tx (pins o A0))"
+ " (subsignal rx (pins i A1))"
+ " (attrs IOSTANDARD='LVCMOS33'))")
+
+ def test_family(self):
+ ios = [Subsignal("clk", Pins("A0", dir="o"))]
+ r1 = Resource.family(0, default_name="spi", ios=ios)
+ r2 = Resource.family("spi_flash", 0, default_name="spi", ios=ios)
+ r3 = Resource.family("spi_flash", 0, default_name="spi", ios=ios, name_suffix="4x")
+ r4 = Resource.family(0, default_name="spi", ios=ios, name_suffix="2x")
+ self.assertEqual(r1.name, "spi")
+ self.assertEqual(r1.ios, ios)
+ self.assertEqual(r2.name, "spi_flash")
+ self.assertEqual(r2.ios, ios)
+ self.assertEqual(r3.name, "spi_flash_4x")
+ self.assertEqual(r3.ios, ios)
+ self.assertEqual(r4.name, "spi_2x")
+ self.assertEqual(r4.ios, ios)
+
+
+class ConnectorTestCase(FHDLTestCase):
+ def test_string(self):
+ c = Connector("pmod", 0, "A0 A1 A2 A3 - - A4 A5 A6 A7 - -")
+ self.assertEqual(c.name, "pmod")
+ self.assertEqual(c.number, 0)
+ self.assertEqual(c.mapping, OrderedDict([
+ ("1", "A0"),
+ ("2", "A1"),
+ ("3", "A2"),
+ ("4", "A3"),
+ ("7", "A4"),
+ ("8", "A5"),
+ ("9", "A6"),
+ ("10", "A7"),
+ ]))
+ self.assertEqual(list(c), [
+ ("pmod_0:1", "A0"),
+ ("pmod_0:2", "A1"),
+ ("pmod_0:3", "A2"),
+ ("pmod_0:4", "A3"),
+ ("pmod_0:7", "A4"),
+ ("pmod_0:8", "A5"),
+ ("pmod_0:9", "A6"),
+ ("pmod_0:10", "A7"),
+ ])
+ self.assertEqual(repr(c),
+ "(connector pmod 0 1=>A0 2=>A1 3=>A2 4=>A3 7=>A4 8=>A5 9=>A6 10=>A7)")
+
+ def test_dict(self):
+ c = Connector("ext", 1, {"DP0": "A0", "DP1": "A1"})
+ self.assertEqual(c.name, "ext")
+ self.assertEqual(c.number, 1)
+ self.assertEqual(c.mapping, OrderedDict([
+ ("DP0", "A0"),
+ ("DP1", "A1"),
+ ]))
+
+ def test_conn(self):
+ c = Connector("pmod", 0, "0 1 2 3 - - 4 5 6 7 - -", conn=("expansion", 0))
+ self.assertEqual(c.mapping, OrderedDict([
+ ("1", "expansion_0:0"),
+ ("2", "expansion_0:1"),
+ ("3", "expansion_0:2"),
+ ("4", "expansion_0:3"),
+ ("7", "expansion_0:4"),
+ ("8", "expansion_0:5"),
+ ("9", "expansion_0:6"),
+ ("10", "expansion_0:7"),
+ ]))
+
+ def test_str_name(self):
+ c = Connector("ext", "A", "0 1 2")
+ self.assertEqual(c.name, "ext")
+ self.assertEqual(c.number, "A")
+
+ def test_conn_wrong_name(self):
+ with self.assertRaisesRegex(TypeError,
+ (r"^Connector must be None or a pair of string \(connector name\) and "
+ r"integer\/string \(connector number\), not \('foo', None\)$")):
+ Connector("ext", "A", "0 1 2", conn=("foo", None))
+
+ def test_wrong_io(self):
+ with self.assertRaisesRegex(TypeError,
+ r"^Connector I\/Os must be a dictionary or a string, not \[\]$"):
+ Connector("pmod", 0, [])
+
+ def test_wrong_dict_key_value(self):
+ with self.assertRaisesRegex(TypeError,
+ r"^Connector pin name must be a string, not 0$"):
+ Connector("pmod", 0, {0: "A"})
+ with self.assertRaisesRegex(TypeError,
+ r"^Platform pin name must be a string, not 0$"):
+ Connector("pmod", 0, {"A": 0})
--- /dev/null
+from nmigen import *
+from nmigen.build.plat import *
+
+from .utils import *
+
+
+class MockPlatform(Platform):
+ resources = []
+ connectors = []
+
+ required_tools = []
+
+ def toolchain_prepare(self, fragment, name, **kwargs):
+ raise NotImplementedError
+
+
+class PlatformTestCase(FHDLTestCase):
+ def setUp(self):
+ self.platform = MockPlatform()
+
+ def test_add_file_str(self):
+ self.platform.add_file("x.txt", "foo")
+ self.assertEqual(self.platform.extra_files["x.txt"], "foo")
+
+ def test_add_file_bytes(self):
+ self.platform.add_file("x.txt", b"foo")
+ self.assertEqual(self.platform.extra_files["x.txt"], b"foo")
+
+ def test_add_file_exact_duplicate(self):
+ self.platform.add_file("x.txt", b"foo")
+ self.platform.add_file("x.txt", b"foo")
+
+ def test_add_file_io(self):
+ with open(__file__) as f:
+ self.platform.add_file("x.txt", f)
+ with open(__file__) as f:
+ self.assertEqual(self.platform.extra_files["x.txt"], f.read())
+
+ def test_add_file_wrong_filename(self):
+ with self.assertRaisesRegex(TypeError,
+ r"^File name must be a string, not 1$"):
+ self.platform.add_file(1, "")
+
+ def test_add_file_wrong_contents(self):
+ with self.assertRaisesRegex(TypeError,
+ r"^File contents must be str, bytes, or a file-like object, not 1$"):
+ self.platform.add_file("foo", 1)
+
+ def test_add_file_wrong_duplicate(self):
+ self.platform.add_file("foo", "")
+ with self.assertRaisesRegex(ValueError,
+ r"^File 'foo' already exists$"):
+ self.platform.add_file("foo", "bar")
--- /dev/null
+# nmigen: UnusedElaboratable=no
+
+from nmigen import *
+from nmigen.hdl.rec import *
+from nmigen.lib.io import *
+from nmigen.build.dsl import *
+from nmigen.build.res import *
+
+from .utils import *
+
+
+class ResourceManagerTestCase(FHDLTestCase):
+ def setUp(self):
+ self.resources = [
+ Resource("clk100", 0, DiffPairs("H1", "H2", dir="i"), Clock(100e6)),
+ Resource("clk50", 0, Pins("K1"), Clock(50e6)),
+ Resource("user_led", 0, Pins("A0", dir="o")),
+ Resource("i2c", 0,
+ Subsignal("scl", Pins("N10", dir="o")),
+ Subsignal("sda", Pins("N11"))
+ )
+ ]
+ self.connectors = [
+ Connector("pmod", 0, "B0 B1 B2 B3 - -"),
+ ]
+ self.cm = ResourceManager(self.resources, self.connectors)
+
+ def test_basic(self):
+ self.cm = ResourceManager(self.resources, self.connectors)
+ self.assertEqual(self.cm.resources, {
+ ("clk100", 0): self.resources[0],
+ ("clk50", 0): self.resources[1],
+ ("user_led", 0): self.resources[2],
+ ("i2c", 0): self.resources[3]
+ })
+ self.assertEqual(self.cm.connectors, {
+ ("pmod", 0): self.connectors[0],
+ })
+
+ def test_add_resources(self):
+ new_resources = [
+ Resource("user_led", 1, Pins("A1", dir="o"))
+ ]
+ self.cm.add_resources(new_resources)
+ self.assertEqual(self.cm.resources, {
+ ("clk100", 0): self.resources[0],
+ ("clk50", 0): self.resources[1],
+ ("user_led", 0): self.resources[2],
+ ("i2c", 0): self.resources[3],
+ ("user_led", 1): new_resources[0]
+ })
+
+ def test_lookup(self):
+ r = self.cm.lookup("user_led", 0)
+ self.assertIs(r, self.cm.resources["user_led", 0])
+
+ def test_request_basic(self):
+ r = self.cm.lookup("user_led", 0)
+ user_led = self.cm.request("user_led", 0)
+
+ self.assertIsInstance(user_led, Pin)
+ self.assertEqual(user_led.name, "user_led_0")
+ self.assertEqual(user_led.width, 1)
+ self.assertEqual(user_led.dir, "o")
+
+ ports = list(self.cm.iter_ports())
+ self.assertEqual(len(ports), 1)
+
+ self.assertEqual(list(self.cm.iter_port_constraints()), [
+ ("user_led_0__io", ["A0"], {})
+ ])
+
+ def test_request_with_dir(self):
+ i2c = self.cm.request("i2c", 0, dir={"sda": "o"})
+ self.assertIsInstance(i2c, Record)
+ self.assertIsInstance(i2c.sda, Pin)
+ self.assertEqual(i2c.sda.dir, "o")
+
+ def test_request_tristate(self):
+ i2c = self.cm.request("i2c", 0)
+ self.assertEqual(i2c.sda.dir, "io")
+
+ ports = list(self.cm.iter_ports())
+ self.assertEqual(len(ports), 2)
+ scl, sda = ports
+ self.assertEqual(ports[1].name, "i2c_0__sda__io")
+ self.assertEqual(ports[1].width, 1)
+
+ scl_info, sda_info = self.cm.iter_single_ended_pins()
+ self.assertIs(scl_info[0], i2c.scl)
+ self.assertIs(scl_info[1].io, scl)
+ self.assertEqual(scl_info[2], {})
+ self.assertEqual(scl_info[3], False)
+ self.assertIs(sda_info[0], i2c.sda)
+ self.assertIs(sda_info[1].io, sda)
+
+ self.assertEqual(list(self.cm.iter_port_constraints()), [
+ ("i2c_0__scl__io", ["N10"], {}),
+ ("i2c_0__sda__io", ["N11"], {})
+ ])
+
+ def test_request_diffpairs(self):
+ clk100 = self.cm.request("clk100", 0)
+ self.assertIsInstance(clk100, Pin)
+ self.assertEqual(clk100.dir, "i")
+ self.assertEqual(clk100.width, 1)
+
+ ports = list(self.cm.iter_ports())
+ self.assertEqual(len(ports), 2)
+ p, n = ports
+ self.assertEqual(p.name, "clk100_0__p")
+ self.assertEqual(p.width, clk100.width)
+ self.assertEqual(n.name, "clk100_0__n")
+ self.assertEqual(n.width, clk100.width)
+
+ clk100_info, = self.cm.iter_differential_pins()
+ self.assertIs(clk100_info[0], clk100)
+ self.assertIs(clk100_info[1].p, p)
+ self.assertIs(clk100_info[1].n, n)
+ self.assertEqual(clk100_info[2], {})
+ self.assertEqual(clk100_info[3], False)
+
+ self.assertEqual(list(self.cm.iter_port_constraints()), [
+ ("clk100_0__p", ["H1"], {}),
+ ("clk100_0__n", ["H2"], {}),
+ ])
+
+ def test_request_inverted(self):
+ new_resources = [
+ Resource("cs", 0, PinsN("X0")),
+ Resource("clk", 0, DiffPairsN("Y0", "Y1")),
+ ]
+ self.cm.add_resources(new_resources)
+
+ cs = self.cm.request("cs")
+ clk = self.cm.request("clk")
+ cs_io, clk_p, clk_n = self.cm.iter_ports()
+
+ cs_info, = self.cm.iter_single_ended_pins()
+ self.assertIs(cs_info[0], cs)
+ self.assertIs(cs_info[1].io, cs_io)
+ self.assertEqual(cs_info[2], {})
+ self.assertEqual(cs_info[3], True)
+
+ clk_info, = self.cm.iter_differential_pins()
+ self.assertIs(clk_info[0], clk)
+ self.assertIs(clk_info[1].p, clk_p)
+ self.assertIs(clk_info[1].n, clk_n)
+ self.assertEqual(clk_info[2], {})
+ self.assertEqual(clk_info[3], True)
+
+ def test_request_raw(self):
+ clk50 = self.cm.request("clk50", 0, dir="-")
+ self.assertIsInstance(clk50, Record)
+ self.assertIsInstance(clk50.io, Signal)
+
+ ports = list(self.cm.iter_ports())
+ self.assertEqual(len(ports), 1)
+ self.assertIs(ports[0], clk50.io)
+
+ def test_request_raw_diffpairs(self):
+ clk100 = self.cm.request("clk100", 0, dir="-")
+ self.assertIsInstance(clk100, Record)
+ self.assertIsInstance(clk100.p, Signal)
+ self.assertIsInstance(clk100.n, Signal)
+
+ ports = list(self.cm.iter_ports())
+ self.assertEqual(len(ports), 2)
+ self.assertIs(ports[0], clk100.p)
+ self.assertIs(ports[1], clk100.n)
+
+ def test_request_via_connector(self):
+ self.cm.add_resources([
+ Resource("spi", 0,
+ Subsignal("ss", Pins("1", conn=("pmod", 0))),
+ Subsignal("clk", Pins("2", conn=("pmod", 0))),
+ Subsignal("miso", Pins("3", conn=("pmod", 0))),
+ Subsignal("mosi", Pins("4", conn=("pmod", 0))),
+ )
+ ])
+ spi0 = self.cm.request("spi", 0)
+ self.assertEqual(list(self.cm.iter_port_constraints()), [
+ ("spi_0__ss__io", ["B0"], {}),
+ ("spi_0__clk__io", ["B1"], {}),
+ ("spi_0__miso__io", ["B2"], {}),
+ ("spi_0__mosi__io", ["B3"], {}),
+ ])
+
+ def test_request_via_nested_connector(self):
+ new_connectors = [
+ Connector("pmod_extension", 0, "1 2 3 4 - -", conn=("pmod", 0)),
+ ]
+ self.cm.add_connectors(new_connectors)
+ self.cm.add_resources([
+ Resource("spi", 0,
+ Subsignal("ss", Pins("1", conn=("pmod_extension", 0))),
+ Subsignal("clk", Pins("2", conn=("pmod_extension", 0))),
+ Subsignal("miso", Pins("3", conn=("pmod_extension", 0))),
+ Subsignal("mosi", Pins("4", conn=("pmod_extension", 0))),
+ )
+ ])
+ spi0 = self.cm.request("spi", 0)
+ self.assertEqual(list(self.cm.iter_port_constraints()), [
+ ("spi_0__ss__io", ["B0"], {}),
+ ("spi_0__clk__io", ["B1"], {}),
+ ("spi_0__miso__io", ["B2"], {}),
+ ("spi_0__mosi__io", ["B3"], {}),
+ ])
+
+ def test_request_clock(self):
+ clk100 = self.cm.request("clk100", 0)
+ clk50 = self.cm.request("clk50", 0, dir="i")
+ clk100_port_p, clk100_port_n, clk50_port = self.cm.iter_ports()
+ self.assertEqual(list(self.cm.iter_clock_constraints()), [
+ (clk100.i, clk100_port_p, 100e6),
+ (clk50.i, clk50_port, 50e6)
+ ])
+
+ def test_add_clock(self):
+ i2c = self.cm.request("i2c")
+ self.cm.add_clock_constraint(i2c.scl.o, 100e3)
+ self.assertEqual(list(self.cm.iter_clock_constraints()), [
+ (i2c.scl.o, None, 100e3)
+ ])
+
+ def test_wrong_resources(self):
+ with self.assertRaisesRegex(TypeError, r"^Object 'wrong' is not a Resource$"):
+ self.cm.add_resources(['wrong'])
+
+ def test_wrong_resources_duplicate(self):
+ with self.assertRaisesRegex(NameError,
+ (r"^Trying to add \(resource user_led 0 \(pins o A1\)\), but "
+ r"\(resource user_led 0 \(pins o A0\)\) has the same name and number$")):
+ self.cm.add_resources([Resource("user_led", 0, Pins("A1", dir="o"))])
+
+ def test_wrong_connectors(self):
+ with self.assertRaisesRegex(TypeError, r"^Object 'wrong' is not a Connector$"):
+ self.cm.add_connectors(['wrong'])
+
+ def test_wrong_connectors_duplicate(self):
+ with self.assertRaisesRegex(NameError,
+ (r"^Trying to add \(connector pmod 0 1=>1 2=>2\), but "
+ r"\(connector pmod 0 1=>B0 2=>B1 3=>B2 4=>B3\) has the same name and number$")):
+ self.cm.add_connectors([Connector("pmod", 0, "1 2")])
+
+ def test_wrong_lookup(self):
+ with self.assertRaisesRegex(ResourceError,
+ r"^Resource user_led#1 does not exist$"):
+ r = self.cm.lookup("user_led", 1)
+
+ def test_wrong_clock_signal(self):
+ with self.assertRaisesRegex(TypeError,
+ r"^Object None is not a Signal$"):
+ self.cm.add_clock_constraint(None, 10e6)
+
+ def test_wrong_clock_frequency(self):
+ with self.assertRaisesRegex(TypeError,
+ r"^Frequency must be a number, not None$"):
+ self.cm.add_clock_constraint(Signal(), None)
+
+ def test_wrong_request_duplicate(self):
+ with self.assertRaisesRegex(ResourceError,
+ r"^Resource user_led#0 has already been requested$"):
+ self.cm.request("user_led", 0)
+ self.cm.request("user_led", 0)
+
+ def test_wrong_request_duplicate_physical(self):
+ self.cm.add_resources([
+ Resource("clk20", 0, Pins("H1", dir="i")),
+ ])
+ self.cm.request("clk100", 0)
+ with self.assertRaisesRegex(ResourceError,
+ (r"^Resource component clk20_0 uses physical pin H1, but it is already "
+ r"used by resource component clk100_0 that was requested earlier$")):
+ self.cm.request("clk20", 0)
+
+ def test_wrong_request_with_dir(self):
+ with self.assertRaisesRegex(TypeError,
+ (r"^Direction must be one of \"i\", \"o\", \"oe\", \"io\", or \"-\", "
+ r"not 'wrong'$")):
+ user_led = self.cm.request("user_led", 0, dir="wrong")
+
+ def test_wrong_request_with_dir_io(self):
+ with self.assertRaisesRegex(ValueError,
+ (r"^Direction of \(pins o A0\) cannot be changed from \"o\" to \"i\"; direction "
+ r"can be changed from \"io\" to \"i\", \"o\", or \"oe\", or from anything "
+ r"to \"-\"$")):
+ user_led = self.cm.request("user_led", 0, dir="i")
+
+ def test_wrong_request_with_dir_dict(self):
+ with self.assertRaisesRegex(TypeError,
+ (r"^Directions must be a dict, not 'i', because \(resource i2c 0 \(subsignal scl "
+ r"\(pins o N10\)\) \(subsignal sda \(pins io N11\)\)\) "
+ r"has subsignals$")):
+ i2c = self.cm.request("i2c", 0, dir="i")
+
+ def test_wrong_request_with_wrong_xdr(self):
+ with self.assertRaisesRegex(ValueError,
+ r"^Data rate of \(pins o A0\) must be a non-negative integer, not -1$"):
+ user_led = self.cm.request("user_led", 0, xdr=-1)
+
+ def test_wrong_request_with_xdr_dict(self):
+ with self.assertRaisesRegex(TypeError,
+ r"^Data rate must be a dict, not 2, because \(resource i2c 0 \(subsignal scl "
+ r"\(pins o N10\)\) \(subsignal sda \(pins io N11\)\)\) "
+ r"has subsignals$"):
+ i2c = self.cm.request("i2c", 0, xdr=2)
+
+ def test_wrong_clock_constraint_twice(self):
+ clk100 = self.cm.request("clk100")
+ with self.assertRaisesRegex(ValueError,
+ (r"^Cannot add clock constraint on \(sig clk100_0__i\), which is already "
+ r"constrained to 100000000\.0 Hz$")):
+ self.cm.add_clock_constraint(clk100.i, 1e6)
--- /dev/null
+from nmigen.hdl.ir import Fragment
+from nmigen.compat import *
+
+from .utils import *
+
+
+class CompatTestCase(FHDLTestCase):
+ def test_fragment_get(self):
+ m = Module()
+ f = Fragment.get(m, platform=None)
--- /dev/null
+import sys
+import subprocess
+from pathlib import Path
+
+from .utils import *
+
+
+def example_test(name):
+ path = (Path(__file__).parent / ".." / "examples" / name).resolve()
+ def test_function(self):
+ subprocess.check_call([sys.executable, str(path), "generate", "-t", "v"],
+ stdout=subprocess.DEVNULL)
+ return test_function
+
+
+class ExamplesTestCase(FHDLTestCase):
+ test_alu = example_test("basic/alu.py")
+ test_alu_hier = example_test("basic/alu_hier.py")
+ test_arst = example_test("basic/arst.py")
+ test_cdc = example_test("basic/cdc.py")
+ test_ctr = example_test("basic/ctr.py")
+ test_ctr_en = example_test("basic/ctr_en.py")
+ test_fsm = example_test("basic/fsm.py")
+ test_gpio = example_test("basic/gpio.py")
+ test_inst = example_test("basic/inst.py")
+ test_mem = example_test("basic/mem.py")
+ test_pmux = example_test("basic/pmux.py")
+ test_por = example_test("basic/por.py")
+
+ def test_uart(self):
+ path = (Path(__file__).parent / ".." / "examples" / "basic" / "uart.py").resolve()
+ subprocess.check_call([sys.executable, str(path), "generate"],
+ stdout=subprocess.DEVNULL)
--- /dev/null
+import warnings
+from enum import Enum
+
+from nmigen.hdl.ast import *
+
+from .utils import *
+
+
+class UnsignedEnum(Enum):
+ FOO = 1
+ BAR = 2
+ BAZ = 3
+
+
+class SignedEnum(Enum):
+ FOO = -1
+ BAR = 0
+ BAZ = +1
+
+
+class StringEnum(Enum):
+ FOO = "a"
+ BAR = "b"
+
+
+class ShapeTestCase(FHDLTestCase):
+ def test_make(self):
+ s1 = Shape()
+ self.assertEqual(s1.width, 1)
+ self.assertEqual(s1.signed, False)
+ s2 = Shape(signed=True)
+ self.assertEqual(s2.width, 1)
+ self.assertEqual(s2.signed, True)
+ s3 = Shape(3, True)
+ self.assertEqual(s3.width, 3)
+ self.assertEqual(s3.signed, True)
+
+ def test_make_wrong(self):
+ with self.assertRaisesRegex(TypeError,
+ r"^Width must be a non-negative integer, not -1$"):
+ Shape(-1)
+
+ def test_compare_wrong(self):
+ with self.assertRaisesRegex(TypeError,
+ r"^Shapes may be compared with other Shapes and \(int, bool\) tuples, not 'hi'$"):
+ Shape(1, True) == 'hi'
+
+ def test_compare_tuple_wrong(self):
+ with self.assertRaisesRegex(TypeError,
+ r"^Shapes may be compared with other Shapes and \(int, bool\) tuples, not \(2, 3\)$"):
+ Shape(1, True) == (2, 3)
+
+ def test_repr(self):
+ self.assertEqual(repr(Shape()), "unsigned(1)")
+ self.assertEqual(repr(Shape(2, True)), "signed(2)")
+
+ def test_tuple(self):
+ width, signed = Shape()
+ self.assertEqual(width, 1)
+ self.assertEqual(signed, False)
+
+ def test_unsigned(self):
+ s1 = unsigned(2)
+ self.assertIsInstance(s1, Shape)
+ self.assertEqual(s1.width, 2)
+ self.assertEqual(s1.signed, False)
+
+ def test_signed(self):
+ s1 = signed(2)
+ self.assertIsInstance(s1, Shape)
+ self.assertEqual(s1.width, 2)
+ self.assertEqual(s1.signed, True)
+
+ def test_cast_shape(self):
+ s1 = Shape.cast(unsigned(1))
+ self.assertEqual(s1.width, 1)
+ self.assertEqual(s1.signed, False)
+ s2 = Shape.cast(signed(3))
+ self.assertEqual(s2.width, 3)
+ self.assertEqual(s2.signed, True)
+
+ def test_cast_int(self):
+ s1 = Shape.cast(2)
+ self.assertEqual(s1.width, 2)
+ self.assertEqual(s1.signed, False)
+
+ def test_cast_int_wrong(self):
+ with self.assertRaisesRegex(TypeError,
+ r"^Width must be a non-negative integer, not -1$"):
+ Shape.cast(-1)
+
+ def test_cast_tuple(self):
+ with warnings.catch_warnings():
+ warnings.filterwarnings(action="ignore", category=DeprecationWarning)
+ s1 = Shape.cast((1, True))
+ self.assertEqual(s1.width, 1)
+ self.assertEqual(s1.signed, True)
+
+ def test_cast_tuple_wrong(self):
+ with warnings.catch_warnings():
+ warnings.filterwarnings(action="ignore", category=DeprecationWarning)
+ with self.assertRaisesRegex(TypeError,
+ r"^Width must be a non-negative integer, not -1$"):
+ Shape.cast((-1, True))
+
+ def test_cast_range(self):
+ s1 = Shape.cast(range(0, 8))
+ self.assertEqual(s1.width, 3)
+ self.assertEqual(s1.signed, False)
+ s2 = Shape.cast(range(0, 9))
+ self.assertEqual(s2.width, 4)
+ self.assertEqual(s2.signed, False)
+ s3 = Shape.cast(range(-7, 8))
+ self.assertEqual(s3.width, 4)
+ self.assertEqual(s3.signed, True)
+ s4 = Shape.cast(range(0, 1))
+ self.assertEqual(s4.width, 1)
+ self.assertEqual(s4.signed, False)
+ s5 = Shape.cast(range(-1, 0))
+ self.assertEqual(s5.width, 1)
+ self.assertEqual(s5.signed, True)
+ s6 = Shape.cast(range(0, 0))
+ self.assertEqual(s6.width, 0)
+ self.assertEqual(s6.signed, False)
+ s7 = Shape.cast(range(-1, -1))
+ self.assertEqual(s7.width, 0)
+ self.assertEqual(s7.signed, True)
+
+ def test_cast_enum(self):
+ s1 = Shape.cast(UnsignedEnum)
+ self.assertEqual(s1.width, 2)
+ self.assertEqual(s1.signed, False)
+ s2 = Shape.cast(SignedEnum)
+ self.assertEqual(s2.width, 2)
+ self.assertEqual(s2.signed, True)
+
+ def test_cast_enum_bad(self):
+ with self.assertRaisesRegex(TypeError,
+ r"^Only enumerations with integer values can be used as value shapes$"):
+ Shape.cast(StringEnum)
+
+ def test_cast_bad(self):
+ with self.assertRaisesRegex(TypeError,
+ r"^Object 'foo' cannot be used as value shape$"):
+ Shape.cast("foo")
+
+
+class ValueTestCase(FHDLTestCase):
+ def test_cast(self):
+ self.assertIsInstance(Value.cast(0), Const)
+ self.assertIsInstance(Value.cast(True), Const)
+ c = Const(0)
+ self.assertIs(Value.cast(c), c)
+ with self.assertRaisesRegex(TypeError,
+ r"^Object 'str' cannot be converted to an nMigen value$"):
+ Value.cast("str")
+
+ def test_cast_enum(self):
+ e1 = Value.cast(UnsignedEnum.FOO)
+ self.assertIsInstance(e1, Const)
+ self.assertEqual(e1.shape(), unsigned(2))
+ e2 = Value.cast(SignedEnum.FOO)
+ self.assertIsInstance(e2, Const)
+ self.assertEqual(e2.shape(), signed(2))
+
+ def test_cast_enum_wrong(self):
+ with self.assertRaisesRegex(TypeError,
+ r"^Only enumerations with integer values can be used as value shapes$"):
+ Value.cast(StringEnum.FOO)
+
+ def test_bool(self):
+ with self.assertRaisesRegex(TypeError,
+ r"^Attempted to convert nMigen value to Python boolean$"):
+ if Const(0):
+ pass
+
+ def test_len(self):
+ self.assertEqual(len(Const(10)), 4)
+
+ def test_getitem_int(self):
+ s1 = Const(10)[0]
+ self.assertIsInstance(s1, Slice)
+ self.assertEqual(s1.start, 0)
+ self.assertEqual(s1.stop, 1)
+ s2 = Const(10)[-1]
+ self.assertIsInstance(s2, Slice)
+ self.assertEqual(s2.start, 3)
+ self.assertEqual(s2.stop, 4)
+ with self.assertRaisesRegex(IndexError,
+ r"^Cannot index 5 bits into 4-bit value$"):
+ Const(10)[5]
+
+ def test_getitem_slice(self):
+ s1 = Const(10)[1:3]
+ self.assertIsInstance(s1, Slice)
+ self.assertEqual(s1.start, 1)
+ self.assertEqual(s1.stop, 3)
+ s2 = Const(10)[1:-2]
+ self.assertIsInstance(s2, Slice)
+ self.assertEqual(s2.start, 1)
+ self.assertEqual(s2.stop, 2)
+ s3 = Const(31)[::2]
+ self.assertIsInstance(s3, Cat)
+ self.assertIsInstance(s3.parts[0], Slice)
+ self.assertEqual(s3.parts[0].start, 0)
+ self.assertEqual(s3.parts[0].stop, 1)
+ self.assertIsInstance(s3.parts[1], Slice)
+ self.assertEqual(s3.parts[1].start, 2)
+ self.assertEqual(s3.parts[1].stop, 3)
+ self.assertIsInstance(s3.parts[2], Slice)
+ self.assertEqual(s3.parts[2].start, 4)
+ self.assertEqual(s3.parts[2].stop, 5)
+
+ def test_getitem_wrong(self):
+ with self.assertRaisesRegex(TypeError,
+ r"^Cannot index value with 'str'$"):
+ Const(31)["str"]
+
+ def test_shift_left(self):
+ self.assertRepr(Const(256, unsigned(9)).shift_left(0),
+ "(cat (const 0'd0) (const 9'd256))")
+
+ self.assertRepr(Const(256, unsigned(9)).shift_left(1),
+ "(cat (const 1'd0) (const 9'd256))")
+ self.assertRepr(Const(256, unsigned(9)).shift_left(5),
+ "(cat (const 5'd0) (const 9'd256))")
+ self.assertRepr(Const(256, signed(9)).shift_left(1),
+ "(s (cat (const 1'd0) (const 9'sd-256)))")
+ self.assertRepr(Const(256, signed(9)).shift_left(5),
+ "(s (cat (const 5'd0) (const 9'sd-256)))")
+
+ self.assertRepr(Const(256, unsigned(9)).shift_left(-1),
+ "(slice (const 9'd256) 1:9)")
+ self.assertRepr(Const(256, unsigned(9)).shift_left(-5),
+ "(slice (const 9'd256) 5:9)")
+ self.assertRepr(Const(256, signed(9)).shift_left(-1),
+ "(s (slice (const 9'sd-256) 1:9))")
+ self.assertRepr(Const(256, signed(9)).shift_left(-5),
+ "(s (slice (const 9'sd-256) 5:9))")
+ self.assertRepr(Const(256, signed(9)).shift_left(-15),
+ "(s (slice (const 9'sd-256) 9:9))")
+
+ def test_shift_left_wrong(self):
+ with self.assertRaisesRegex(TypeError,
+ r"^Shift amount must be an integer, not 'str'$"):
+ Const(31).shift_left("str")
+
+ def test_shift_right(self):
+ self.assertRepr(Const(256, unsigned(9)).shift_right(0),
+ "(slice (const 9'd256) 0:9)")
+
+ self.assertRepr(Const(256, unsigned(9)).shift_right(-1),
+ "(cat (const 1'd0) (const 9'd256))")
+ self.assertRepr(Const(256, unsigned(9)).shift_right(-5),
+ "(cat (const 5'd0) (const 9'd256))")
+ self.assertRepr(Const(256, signed(9)).shift_right(-1),
+ "(s (cat (const 1'd0) (const 9'sd-256)))")
+ self.assertRepr(Const(256, signed(9)).shift_right(-5),
+ "(s (cat (const 5'd0) (const 9'sd-256)))")
+
+ self.assertRepr(Const(256, unsigned(9)).shift_right(1),
+ "(slice (const 9'd256) 1:9)")
+ self.assertRepr(Const(256, unsigned(9)).shift_right(5),
+ "(slice (const 9'd256) 5:9)")
+ self.assertRepr(Const(256, signed(9)).shift_right(1),
+ "(s (slice (const 9'sd-256) 1:9))")
+ self.assertRepr(Const(256, signed(9)).shift_right(5),
+ "(s (slice (const 9'sd-256) 5:9))")
+ self.assertRepr(Const(256, signed(9)).shift_right(15),
+ "(s (slice (const 9'sd-256) 9:9))")
+
+ def test_shift_right_wrong(self):
+ with self.assertRaisesRegex(TypeError,
+ r"^Shift amount must be an integer, not 'str'$"):
+ Const(31).shift_left("str")
+
+ def test_rotate_left(self):
+ self.assertRepr(Const(256).rotate_left(1),
+ "(cat (slice (const 9'd256) 8:9) (slice (const 9'd256) 0:8))")
+ self.assertRepr(Const(256).rotate_left(7),
+ "(cat (slice (const 9'd256) 2:9) (slice (const 9'd256) 0:2))")
+ self.assertRepr(Const(256).rotate_left(-1),
+ "(cat (slice (const 9'd256) 1:9) (slice (const 9'd256) 0:1))")
+ self.assertRepr(Const(256).rotate_left(-7),
+ "(cat (slice (const 9'd256) 7:9) (slice (const 9'd256) 0:7))")
+
+ def test_rotate_left_wrong(self):
+ with self.assertRaisesRegex(TypeError,
+ r"^Rotate amount must be an integer, not 'str'$"):
+ Const(31).rotate_left("str")
+
+ def test_rotate_right(self):
+ self.assertRepr(Const(256).rotate_right(1),
+ "(cat (slice (const 9'd256) 1:9) (slice (const 9'd256) 0:1))")
+ self.assertRepr(Const(256).rotate_right(7),
+ "(cat (slice (const 9'd256) 7:9) (slice (const 9'd256) 0:7))")
+ self.assertRepr(Const(256).rotate_right(-1),
+ "(cat (slice (const 9'd256) 8:9) (slice (const 9'd256) 0:8))")
+ self.assertRepr(Const(256).rotate_right(-7),
+ "(cat (slice (const 9'd256) 2:9) (slice (const 9'd256) 0:2))")
+
+ def test_rotate_right_wrong(self):
+ with self.assertRaisesRegex(TypeError,
+ r"^Rotate amount must be an integer, not 'str'$"):
+ Const(31).rotate_right("str")
+
+
+class ConstTestCase(FHDLTestCase):
+ def test_shape(self):
+ self.assertEqual(Const(0).shape(), unsigned(1))
+ self.assertIsInstance(Const(0).shape(), Shape)
+ self.assertEqual(Const(1).shape(), unsigned(1))
+ self.assertEqual(Const(10).shape(), unsigned(4))
+ self.assertEqual(Const(-10).shape(), signed(5))
+
+ self.assertEqual(Const(1, 4).shape(), unsigned(4))
+ self.assertEqual(Const(-1, 4).shape(), signed(4))
+ self.assertEqual(Const(1, signed(4)).shape(), signed(4))
+ self.assertEqual(Const(0, unsigned(0)).shape(), unsigned(0))
+
+ def test_shape_wrong(self):
+ with self.assertRaisesRegex(TypeError,
+ r"^Width must be a non-negative integer, not -1$"):
+ Const(1, -1)
+
+ def test_normalization(self):
+ self.assertEqual(Const(0b10110, signed(5)).value, -10)
+
+ def test_value(self):
+ self.assertEqual(Const(10).value, 10)
+
+ def test_repr(self):
+ self.assertEqual(repr(Const(10)), "(const 4'd10)")
+ self.assertEqual(repr(Const(-10)), "(const 5'sd-10)")
+
+ def test_hash(self):
+ with self.assertRaises(TypeError):
+ hash(Const(0))
+
+
+class OperatorTestCase(FHDLTestCase):
+ def test_bool(self):
+ v = Const(0, 4).bool()
+ self.assertEqual(repr(v), "(b (const 4'd0))")
+ self.assertEqual(v.shape(), unsigned(1))
+
+ def test_invert(self):
+ v = ~Const(0, 4)
+ self.assertEqual(repr(v), "(~ (const 4'd0))")
+ self.assertEqual(v.shape(), unsigned(4))
+
+ def test_as_unsigned(self):
+ v = Const(-1, signed(4)).as_unsigned()
+ self.assertEqual(repr(v), "(u (const 4'sd-1))")
+ self.assertEqual(v.shape(), unsigned(4))
+
+ def test_as_signed(self):
+ v = Const(1, unsigned(4)).as_signed()
+ self.assertEqual(repr(v), "(s (const 4'd1))")
+ self.assertEqual(v.shape(), signed(4))
+
+ def test_neg(self):
+ v1 = -Const(0, unsigned(4))
+ self.assertEqual(repr(v1), "(- (const 4'd0))")
+ self.assertEqual(v1.shape(), signed(5))
+ v2 = -Const(0, signed(4))
+ self.assertEqual(repr(v2), "(- (const 4'sd0))")
+ self.assertEqual(v2.shape(), signed(5))
+
+ def test_add(self):
+ v1 = Const(0, unsigned(4)) + Const(0, unsigned(6))
+ self.assertEqual(repr(v1), "(+ (const 4'd0) (const 6'd0))")
+ self.assertEqual(v1.shape(), unsigned(7))
+ v2 = Const(0, signed(4)) + Const(0, signed(6))
+ self.assertEqual(v2.shape(), signed(7))
+ v3 = Const(0, signed(4)) + Const(0, unsigned(4))
+ self.assertEqual(v3.shape(), signed(6))
+ v4 = Const(0, unsigned(4)) + Const(0, signed(4))
+ self.assertEqual(v4.shape(), signed(6))
+ v5 = 10 + Const(0, 4)
+ self.assertEqual(v5.shape(), unsigned(5))
+
+ def test_sub(self):
+ v1 = Const(0, unsigned(4)) - Const(0, unsigned(6))
+ self.assertEqual(repr(v1), "(- (const 4'd0) (const 6'd0))")
+ self.assertEqual(v1.shape(), unsigned(7))
+ v2 = Const(0, signed(4)) - Const(0, signed(6))
+ self.assertEqual(v2.shape(), signed(7))
+ v3 = Const(0, signed(4)) - Const(0, unsigned(4))
+ self.assertEqual(v3.shape(), signed(6))
+ v4 = Const(0, unsigned(4)) - Const(0, signed(4))
+ self.assertEqual(v4.shape(), signed(6))
+ v5 = 10 - Const(0, 4)
+ self.assertEqual(v5.shape(), unsigned(5))
+
+ def test_mul(self):
+ v1 = Const(0, unsigned(4)) * Const(0, unsigned(6))
+ self.assertEqual(repr(v1), "(* (const 4'd0) (const 6'd0))")
+ self.assertEqual(v1.shape(), unsigned(10))
+ v2 = Const(0, signed(4)) * Const(0, signed(6))
+ self.assertEqual(v2.shape(), signed(10))
+ v3 = Const(0, signed(4)) * Const(0, unsigned(4))
+ self.assertEqual(v3.shape(), signed(8))
+ v5 = 10 * Const(0, 4)
+ self.assertEqual(v5.shape(), unsigned(8))
+
+ def test_mod(self):
+ v1 = Const(0, unsigned(4)) % Const(0, unsigned(6))
+ self.assertEqual(repr(v1), "(% (const 4'd0) (const 6'd0))")
+ self.assertEqual(v1.shape(), unsigned(4))
+ v3 = Const(0, signed(4)) % Const(0, unsigned(4))
+ self.assertEqual(v3.shape(), signed(4))
+ v5 = 10 % Const(0, 4)
+ self.assertEqual(v5.shape(), unsigned(4))
+
+ def test_mod_wrong(self):
+ with self.assertRaisesRegex(NotImplementedError,
+ r"^Division by a signed value is not supported$"):
+ Const(0, signed(4)) % Const(0, signed(6))
+
+ def test_floordiv(self):
+ v1 = Const(0, unsigned(4)) // Const(0, unsigned(6))
+ self.assertEqual(repr(v1), "(// (const 4'd0) (const 6'd0))")
+ self.assertEqual(v1.shape(), unsigned(4))
+ v3 = Const(0, signed(4)) // Const(0, unsigned(4))
+ self.assertEqual(v3.shape(), signed(4))
+ v5 = 10 // Const(0, 4)
+ self.assertEqual(v5.shape(), unsigned(4))
+
+ def test_floordiv_wrong(self):
+ with self.assertRaisesRegex(NotImplementedError,
+ r"^Division by a signed value is not supported$"):
+ Const(0, signed(4)) // Const(0, signed(6))
+
+ def test_and(self):
+ v1 = Const(0, unsigned(4)) & Const(0, unsigned(6))
+ self.assertEqual(repr(v1), "(& (const 4'd0) (const 6'd0))")
+ self.assertEqual(v1.shape(), unsigned(6))
+ v2 = Const(0, signed(4)) & Const(0, signed(6))
+ self.assertEqual(v2.shape(), signed(6))
+ v3 = Const(0, signed(4)) & Const(0, unsigned(4))
+ self.assertEqual(v3.shape(), signed(5))
+ v4 = Const(0, unsigned(4)) & Const(0, signed(4))
+ self.assertEqual(v4.shape(), signed(5))
+ v5 = 10 & Const(0, 4)
+ self.assertEqual(v5.shape(), unsigned(4))
+
+ def test_or(self):
+ v1 = Const(0, unsigned(4)) | Const(0, unsigned(6))
+ self.assertEqual(repr(v1), "(| (const 4'd0) (const 6'd0))")
+ self.assertEqual(v1.shape(), unsigned(6))
+ v2 = Const(0, signed(4)) | Const(0, signed(6))
+ self.assertEqual(v2.shape(), signed(6))
+ v3 = Const(0, signed(4)) | Const(0, unsigned(4))
+ self.assertEqual(v3.shape(), signed(5))
+ v4 = Const(0, unsigned(4)) | Const(0, signed(4))
+ self.assertEqual(v4.shape(), signed(5))
+ v5 = 10 | Const(0, 4)
+ self.assertEqual(v5.shape(), unsigned(4))
+
+ def test_xor(self):
+ v1 = Const(0, unsigned(4)) ^ Const(0, unsigned(6))
+ self.assertEqual(repr(v1), "(^ (const 4'd0) (const 6'd0))")
+ self.assertEqual(v1.shape(), unsigned(6))
+ v2 = Const(0, signed(4)) ^ Const(0, signed(6))
+ self.assertEqual(v2.shape(), signed(6))
+ v3 = Const(0, signed(4)) ^ Const(0, unsigned(4))
+ self.assertEqual(v3.shape(), signed(5))
+ v4 = Const(0, unsigned(4)) ^ Const(0, signed(4))
+ self.assertEqual(v4.shape(), signed(5))
+ v5 = 10 ^ Const(0, 4)
+ self.assertEqual(v5.shape(), unsigned(4))
+
+ def test_shl(self):
+ v1 = Const(1, 4) << Const(4)
+ self.assertEqual(repr(v1), "(<< (const 4'd1) (const 3'd4))")
+ self.assertEqual(v1.shape(), unsigned(11))
+
+ def test_shl_wrong(self):
+ with self.assertRaisesRegex(TypeError,
+ r"^Shift amount must be unsigned$"):
+ 1 << Const(0, signed(6))
+ with self.assertRaisesRegex(TypeError,
+ r"^Shift amount must be unsigned$"):
+ Const(1, unsigned(4)) << -1
+
+ def test_shr(self):
+ v1 = Const(1, 4) >> Const(4)
+ self.assertEqual(repr(v1), "(>> (const 4'd1) (const 3'd4))")
+ self.assertEqual(v1.shape(), unsigned(4))
+
+ def test_shr_wrong(self):
+ with self.assertRaisesRegex(TypeError,
+ r"^Shift amount must be unsigned$"):
+ 1 << Const(0, signed(6))
+ with self.assertRaisesRegex(TypeError,
+ r"^Shift amount must be unsigned$"):
+ Const(1, unsigned(4)) << -1
+
+ def test_lt(self):
+ v = Const(0, 4) < Const(0, 6)
+ self.assertEqual(repr(v), "(< (const 4'd0) (const 6'd0))")
+ self.assertEqual(v.shape(), unsigned(1))
+
+ def test_le(self):
+ v = Const(0, 4) <= Const(0, 6)
+ self.assertEqual(repr(v), "(<= (const 4'd0) (const 6'd0))")
+ self.assertEqual(v.shape(), unsigned(1))
+
+ def test_gt(self):
+ v = Const(0, 4) > Const(0, 6)
+ self.assertEqual(repr(v), "(> (const 4'd0) (const 6'd0))")
+ self.assertEqual(v.shape(), unsigned(1))
+
+ def test_ge(self):
+ v = Const(0, 4) >= Const(0, 6)
+ self.assertEqual(repr(v), "(>= (const 4'd0) (const 6'd0))")
+ self.assertEqual(v.shape(), unsigned(1))
+
+ def test_eq(self):
+ v = Const(0, 4) == Const(0, 6)
+ self.assertEqual(repr(v), "(== (const 4'd0) (const 6'd0))")
+ self.assertEqual(v.shape(), unsigned(1))
+
+ def test_ne(self):
+ v = Const(0, 4) != Const(0, 6)
+ self.assertEqual(repr(v), "(!= (const 4'd0) (const 6'd0))")
+ self.assertEqual(v.shape(), unsigned(1))
+
+ def test_mux(self):
+ s = Const(0)
+ v1 = Mux(s, Const(0, unsigned(4)), Const(0, unsigned(6)))
+ self.assertEqual(repr(v1), "(m (const 1'd0) (const 4'd0) (const 6'd0))")
+ self.assertEqual(v1.shape(), unsigned(6))
+ v2 = Mux(s, Const(0, signed(4)), Const(0, signed(6)))
+ self.assertEqual(v2.shape(), signed(6))
+ v3 = Mux(s, Const(0, signed(4)), Const(0, unsigned(4)))
+ self.assertEqual(v3.shape(), signed(5))
+ v4 = Mux(s, Const(0, unsigned(4)), Const(0, signed(4)))
+ self.assertEqual(v4.shape(), signed(5))
+
+ def test_mux_wide(self):
+ s = Const(0b100)
+ v = Mux(s, Const(0, unsigned(4)), Const(0, unsigned(6)))
+ self.assertEqual(repr(v), "(m (b (const 3'd4)) (const 4'd0) (const 6'd0))")
+
+ def test_mux_bool(self):
+ v = Mux(True, Const(0), Const(0))
+ self.assertEqual(repr(v), "(m (const 1'd1) (const 1'd0) (const 1'd0))")
+
+ def test_bool(self):
+ v = Const(0).bool()
+ self.assertEqual(repr(v), "(b (const 1'd0))")
+ self.assertEqual(v.shape(), unsigned(1))
+
+ def test_any(self):
+ v = Const(0b101).any()
+ self.assertEqual(repr(v), "(r| (const 3'd5))")
+
+ def test_all(self):
+ v = Const(0b101).all()
+ self.assertEqual(repr(v), "(r& (const 3'd5))")
+
+ def test_xor(self):
+ v = Const(0b101).xor()
+ self.assertEqual(repr(v), "(r^ (const 3'd5))")
+
+ def test_matches(self):
+ s = Signal(4)
+ self.assertRepr(s.matches(), "(const 1'd0)")
+ self.assertRepr(s.matches(1), """
+ (== (sig s) (const 1'd1))
+ """)
+ self.assertRepr(s.matches(0, 1), """
+ (r| (cat (== (sig s) (const 1'd0)) (== (sig s) (const 1'd1))))
+ """)
+ self.assertRepr(s.matches("10--"), """
+ (== (& (sig s) (const 4'd12)) (const 4'd8))
+ """)
+ self.assertRepr(s.matches("1 0--"), """
+ (== (& (sig s) (const 4'd12)) (const 4'd8))
+ """)
+
+ def test_matches_enum(self):
+ s = Signal(SignedEnum)
+ self.assertRepr(s.matches(SignedEnum.FOO), """
+ (== (sig s) (const 1'sd-1))
+ """)
+
+ def test_matches_width_wrong(self):
+ s = Signal(4)
+ with self.assertRaisesRegex(SyntaxError,
+ r"^Match pattern '--' must have the same width as match value \(which is 4\)$"):
+ s.matches("--")
+ with self.assertWarnsRegex(SyntaxWarning,
+ (r"^Match pattern '10110' is wider than match value \(which has width 4\); "
+ r"comparison will never be true$")):
+ s.matches(0b10110)
+
+ def test_matches_bits_wrong(self):
+ s = Signal(4)
+ with self.assertRaisesRegex(SyntaxError,
+ (r"^Match pattern 'abc' must consist of 0, 1, and - \(don't care\) bits, "
+ r"and may include whitespace$")):
+ s.matches("abc")
+
+ def test_matches_pattern_wrong(self):
+ s = Signal(4)
+ with self.assertRaisesRegex(SyntaxError,
+ r"^Match pattern must be an integer, a string, or an enumeration, not 1\.0$"):
+ s.matches(1.0)
+
+ def test_hash(self):
+ with self.assertRaises(TypeError):
+ hash(Const(0) + Const(0))
+
+
+class SliceTestCase(FHDLTestCase):
+ def test_shape(self):
+ s1 = Const(10)[2]
+ self.assertEqual(s1.shape(), unsigned(1))
+ self.assertIsInstance(s1.shape(), Shape)
+ s2 = Const(-10)[0:2]
+ self.assertEqual(s2.shape(), unsigned(2))
+
+ def test_start_end_negative(self):
+ c = Const(0, 8)
+ s1 = Slice(c, 0, -1)
+ self.assertEqual((s1.start, s1.stop), (0, 7))
+ s1 = Slice(c, -4, -1)
+ self.assertEqual((s1.start, s1.stop), (4, 7))
+
+ def test_start_end_wrong(self):
+ with self.assertRaisesRegex(TypeError,
+ r"^Slice start must be an integer, not 'x'$"):
+ Slice(0, "x", 1)
+ with self.assertRaisesRegex(TypeError,
+ r"^Slice stop must be an integer, not 'x'$"):
+ Slice(0, 1, "x")
+
+ def test_start_end_out_of_range(self):
+ c = Const(0, 8)
+ with self.assertRaisesRegex(IndexError,
+ r"^Cannot start slice 10 bits into 8-bit value$"):
+ Slice(c, 10, 12)
+ with self.assertRaisesRegex(IndexError,
+ r"^Cannot stop slice 12 bits into 8-bit value$"):
+ Slice(c, 0, 12)
+ with self.assertRaisesRegex(IndexError,
+ r"^Slice start 4 must be less than slice stop 2$"):
+ Slice(c, 4, 2)
+
+ def test_repr(self):
+ s1 = Const(10)[2]
+ self.assertEqual(repr(s1), "(slice (const 4'd10) 2:3)")
+
+
+class BitSelectTestCase(FHDLTestCase):
+ def setUp(self):
+ self.c = Const(0, 8)
+ self.s = Signal(range(self.c.width))
+
+ def test_shape(self):
+ s1 = self.c.bit_select(self.s, 2)
+ self.assertIsInstance(s1, Part)
+ self.assertEqual(s1.shape(), unsigned(2))
+ self.assertIsInstance(s1.shape(), Shape)
+ s2 = self.c.bit_select(self.s, 0)
+ self.assertIsInstance(s2, Part)
+ self.assertEqual(s2.shape(), unsigned(0))
+
+ def test_stride(self):
+ s1 = self.c.bit_select(self.s, 2)
+ self.assertIsInstance(s1, Part)
+ self.assertEqual(s1.stride, 1)
+
+ def test_const(self):
+ s1 = self.c.bit_select(1, 2)
+ self.assertIsInstance(s1, Slice)
+ self.assertRepr(s1, """(slice (const 8'd0) 1:3)""")
+
+ def test_width_wrong(self):
+ with self.assertRaises(TypeError):
+ self.c.bit_select(self.s, -1)
+
+ def test_repr(self):
+ s = self.c.bit_select(self.s, 2)
+ self.assertEqual(repr(s), "(part (const 8'd0) (sig s) 2 1)")
+
+
+class WordSelectTestCase(FHDLTestCase):
+ def setUp(self):
+ self.c = Const(0, 8)
+ self.s = Signal(range(self.c.width))
+
+ def test_shape(self):
+ s1 = self.c.word_select(self.s, 2)
+ self.assertIsInstance(s1, Part)
+ self.assertEqual(s1.shape(), unsigned(2))
+ self.assertIsInstance(s1.shape(), Shape)
+
+ def test_stride(self):
+ s1 = self.c.word_select(self.s, 2)
+ self.assertIsInstance(s1, Part)
+ self.assertEqual(s1.stride, 2)
+
+ def test_const(self):
+ s1 = self.c.word_select(1, 2)
+ self.assertIsInstance(s1, Slice)
+ self.assertRepr(s1, """(slice (const 8'd0) 2:4)""")
+
+ def test_width_wrong(self):
+ with self.assertRaises(TypeError):
+ self.c.word_select(self.s, 0)
+ with self.assertRaises(TypeError):
+ self.c.word_select(self.s, -1)
+
+ def test_repr(self):
+ s = self.c.word_select(self.s, 2)
+ self.assertEqual(repr(s), "(part (const 8'd0) (sig s) 2 2)")
+
+
+class CatTestCase(FHDLTestCase):
+ def test_shape(self):
+ c0 = Cat()
+ self.assertEqual(c0.shape(), unsigned(0))
+ self.assertIsInstance(c0.shape(), Shape)
+ c1 = Cat(Const(10))
+ self.assertEqual(c1.shape(), unsigned(4))
+ c2 = Cat(Const(10), Const(1))
+ self.assertEqual(c2.shape(), unsigned(5))
+ c3 = Cat(Const(10), Const(1), Const(0))
+ self.assertEqual(c3.shape(), unsigned(6))
+
+ def test_repr(self):
+ c1 = Cat(Const(10), Const(1))
+ self.assertEqual(repr(c1), "(cat (const 4'd10) (const 1'd1))")
+
+
+class ReplTestCase(FHDLTestCase):
+ def test_shape(self):
+ s1 = Repl(Const(10), 3)
+ self.assertEqual(s1.shape(), unsigned(12))
+ self.assertIsInstance(s1.shape(), Shape)
+ s2 = Repl(Const(10), 0)
+ self.assertEqual(s2.shape(), unsigned(0))
+
+ def test_count_wrong(self):
+ with self.assertRaises(TypeError):
+ Repl(Const(10), -1)
+ with self.assertRaises(TypeError):
+ Repl(Const(10), "str")
+
+ def test_repr(self):
+ s = Repl(Const(10), 3)
+ self.assertEqual(repr(s), "(repl (const 4'd10) 3)")
+
+
+class ArrayTestCase(FHDLTestCase):
+ def test_acts_like_array(self):
+ a = Array([1,2,3])
+ self.assertSequenceEqual(a, [1,2,3])
+ self.assertEqual(a[1], 2)
+ a[1] = 4
+ self.assertSequenceEqual(a, [1,4,3])
+ del a[1]
+ self.assertSequenceEqual(a, [1,3])
+ a.insert(1, 2)
+ self.assertSequenceEqual(a, [1,2,3])
+
+ def test_becomes_immutable(self):
+ a = Array([1,2,3])
+ s1 = Signal(range(len(a)))
+ s2 = Signal(range(len(a)))
+ v1 = a[s1]
+ v2 = a[s2]
+ with self.assertRaisesRegex(ValueError,
+ r"^Array can no longer be mutated after it was indexed with a value at "):
+ a[1] = 2
+ with self.assertRaisesRegex(ValueError,
+ r"^Array can no longer be mutated after it was indexed with a value at "):
+ del a[1]
+ with self.assertRaisesRegex(ValueError,
+ r"^Array can no longer be mutated after it was indexed with a value at "):
+ a.insert(1, 2)
+
+ def test_repr(self):
+ a = Array([1,2,3])
+ self.assertEqual(repr(a), "(array mutable [1, 2, 3])")
+ s = Signal(range(len(a)))
+ v = a[s]
+ self.assertEqual(repr(a), "(array [1, 2, 3])")
+
+
+class ArrayProxyTestCase(FHDLTestCase):
+ def test_index_shape(self):
+ m = Array(Array(x * y for y in range(1, 4)) for x in range(1, 4))
+ a = Signal(range(3))
+ b = Signal(range(3))
+ v = m[a][b]
+ self.assertEqual(v.shape(), unsigned(4))
+
+ def test_attr_shape(self):
+ from collections import namedtuple
+ pair = namedtuple("pair", ("p", "n"))
+ a = Array(pair(i, -i) for i in range(10))
+ s = Signal(range(len(a)))
+ v = a[s]
+ self.assertEqual(v.p.shape(), unsigned(4))
+ self.assertEqual(v.n.shape(), signed(5))
+
+ def test_attr_shape_signed(self):
+ # [unsigned(1), unsigned(1)] → unsigned(1)
+ a1 = Array([1, 1])
+ v1 = a1[Const(0)]
+ self.assertEqual(v1.shape(), unsigned(1))
+ # [signed(1), signed(1)] → signed(1)
+ a2 = Array([-1, -1])
+ v2 = a2[Const(0)]
+ self.assertEqual(v2.shape(), signed(1))
+ # [unsigned(1), signed(2)] → signed(2)
+ a3 = Array([1, -2])
+ v3 = a3[Const(0)]
+ self.assertEqual(v3.shape(), signed(2))
+ # [unsigned(1), signed(1)] → signed(2); 1st operand padded with sign bit!
+ a4 = Array([1, -1])
+ v4 = a4[Const(0)]
+ self.assertEqual(v4.shape(), signed(2))
+ # [unsigned(2), signed(1)] → signed(3); 1st operand padded with sign bit!
+ a5 = Array([1, -1])
+ v5 = a5[Const(0)]
+ self.assertEqual(v5.shape(), signed(2))
+
+ def test_repr(self):
+ a = Array([1, 2, 3])
+ s = Signal(range(3))
+ v = a[s]
+ self.assertEqual(repr(v), "(proxy (array [1, 2, 3]) (sig s))")
+
+
+class SignalTestCase(FHDLTestCase):
+ def test_shape(self):
+ s1 = Signal()
+ self.assertEqual(s1.shape(), unsigned(1))
+ self.assertIsInstance(s1.shape(), Shape)
+ s2 = Signal(2)
+ self.assertEqual(s2.shape(), unsigned(2))
+ s3 = Signal(unsigned(2))
+ self.assertEqual(s3.shape(), unsigned(2))
+ s4 = Signal(signed(2))
+ self.assertEqual(s4.shape(), signed(2))
+ s5 = Signal(0)
+ self.assertEqual(s5.shape(), unsigned(0))
+ s6 = Signal(range(16))
+ self.assertEqual(s6.shape(), unsigned(4))
+ s7 = Signal(range(4, 16))
+ self.assertEqual(s7.shape(), unsigned(4))
+ s8 = Signal(range(-4, 16))
+ self.assertEqual(s8.shape(), signed(5))
+ s9 = Signal(range(-20, 16))
+ self.assertEqual(s9.shape(), signed(6))
+ s10 = Signal(range(0))
+ self.assertEqual(s10.shape(), unsigned(0))
+ s11 = Signal(range(1))
+ self.assertEqual(s11.shape(), unsigned(1))
+
+ def test_shape_wrong(self):
+ with self.assertRaisesRegex(TypeError,
+ r"^Width must be a non-negative integer, not -10$"):
+ Signal(-10)
+
+ def test_name(self):
+ s1 = Signal()
+ self.assertEqual(s1.name, "s1")
+ s2 = Signal(name="sig")
+ self.assertEqual(s2.name, "sig")
+
+ def test_reset(self):
+ s1 = Signal(4, reset=0b111, reset_less=True)
+ self.assertEqual(s1.reset, 0b111)
+ self.assertEqual(s1.reset_less, True)
+
+ def test_reset_enum(self):
+ s1 = Signal(2, reset=UnsignedEnum.BAR)
+ self.assertEqual(s1.reset, 2)
+ with self.assertRaisesRegex(TypeError,
+ r"^Reset value has to be an int or an integral Enum$"
+ ):
+ Signal(1, reset=StringEnum.FOO)
+
+ def test_reset_narrow(self):
+ with self.assertWarnsRegex(SyntaxWarning,
+ r"^Reset value 8 requires 4 bits to represent, but the signal only has 3 bits$"):
+ Signal(3, reset=8)
+ with self.assertWarnsRegex(SyntaxWarning,
+ r"^Reset value 4 requires 4 bits to represent, but the signal only has 3 bits$"):
+ Signal(signed(3), reset=4)
+ with self.assertWarnsRegex(SyntaxWarning,
+ r"^Reset value -5 requires 4 bits to represent, but the signal only has 3 bits$"):
+ Signal(signed(3), reset=-5)
+
+ def test_attrs(self):
+ s1 = Signal()
+ self.assertEqual(s1.attrs, {})
+ s2 = Signal(attrs={"no_retiming": True})
+ self.assertEqual(s2.attrs, {"no_retiming": True})
+
+ def test_repr(self):
+ s1 = Signal()
+ self.assertEqual(repr(s1), "(sig s1)")
+
+ def test_like(self):
+ s1 = Signal.like(Signal(4))
+ self.assertEqual(s1.shape(), unsigned(4))
+ s2 = Signal.like(Signal(range(-15, 1)))
+ self.assertEqual(s2.shape(), signed(5))
+ s3 = Signal.like(Signal(4, reset=0b111, reset_less=True))
+ self.assertEqual(s3.reset, 0b111)
+ self.assertEqual(s3.reset_less, True)
+ s4 = Signal.like(Signal(attrs={"no_retiming": True}))
+ self.assertEqual(s4.attrs, {"no_retiming": True})
+ s5 = Signal.like(Signal(decoder=str))
+ self.assertEqual(s5.decoder, str)
+ s6 = Signal.like(10)
+ self.assertEqual(s6.shape(), unsigned(4))
+ s7 = [Signal.like(Signal(4))][0]
+ self.assertEqual(s7.name, "$like")
+ s8 = Signal.like(s1, name_suffix="_ff")
+ self.assertEqual(s8.name, "s1_ff")
+
+ def test_decoder(self):
+ class Color(Enum):
+ RED = 1
+ BLUE = 2
+ s = Signal(decoder=Color)
+ self.assertEqual(s.decoder(1), "RED/1")
+ self.assertEqual(s.decoder(3), "3")
+
+ def test_enum(self):
+ s1 = Signal(UnsignedEnum)
+ self.assertEqual(s1.shape(), unsigned(2))
+ s2 = Signal(SignedEnum)
+ self.assertEqual(s2.shape(), signed(2))
+ self.assertEqual(s2.decoder(SignedEnum.FOO), "FOO/-1")
+
+
+class ClockSignalTestCase(FHDLTestCase):
+ def test_domain(self):
+ s1 = ClockSignal()
+ self.assertEqual(s1.domain, "sync")
+ s2 = ClockSignal("pix")
+ self.assertEqual(s2.domain, "pix")
+
+ with self.assertRaisesRegex(TypeError,
+ r"^Clock domain name must be a string, not 1$"):
+ ClockSignal(1)
+
+ def test_shape(self):
+ s1 = ClockSignal()
+ self.assertEqual(s1.shape(), unsigned(1))
+ self.assertIsInstance(s1.shape(), Shape)
+
+ def test_repr(self):
+ s1 = ClockSignal()
+ self.assertEqual(repr(s1), "(clk sync)")
+
+ def test_wrong_name_comb(self):
+ with self.assertRaisesRegex(ValueError,
+ r"^Domain 'comb' does not have a clock$"):
+ ClockSignal("comb")
+
+
+class ResetSignalTestCase(FHDLTestCase):
+ def test_domain(self):
+ s1 = ResetSignal()
+ self.assertEqual(s1.domain, "sync")
+ s2 = ResetSignal("pix")
+ self.assertEqual(s2.domain, "pix")
+
+ with self.assertRaisesRegex(TypeError,
+ r"^Clock domain name must be a string, not 1$"):
+ ResetSignal(1)
+
+ def test_shape(self):
+ s1 = ResetSignal()
+ self.assertEqual(s1.shape(), unsigned(1))
+ self.assertIsInstance(s1.shape(), Shape)
+
+ def test_repr(self):
+ s1 = ResetSignal()
+ self.assertEqual(repr(s1), "(rst sync)")
+
+ def test_wrong_name_comb(self):
+ with self.assertRaisesRegex(ValueError,
+ r"^Domain 'comb' does not have a reset$"):
+ ResetSignal("comb")
+
+
+class MockUserValue(UserValue):
+ def __init__(self, lowered):
+ super().__init__()
+ self.lower_count = 0
+ self.lowered = lowered
+
+ def lower(self):
+ self.lower_count += 1
+ return self.lowered
+
+
+class UserValueTestCase(FHDLTestCase):
+ def test_shape(self):
+ uv = MockUserValue(1)
+ self.assertEqual(uv.shape(), unsigned(1))
+ self.assertIsInstance(uv.shape(), Shape)
+ uv.lowered = 2
+ self.assertEqual(uv.shape(), unsigned(1))
+ self.assertEqual(uv.lower_count, 1)
+
+ def test_lower_to_user_value(self):
+ uv = MockUserValue(MockUserValue(1))
+ self.assertEqual(uv.shape(), unsigned(1))
+ self.assertIsInstance(uv.shape(), Shape)
+ uv.lowered = MockUserValue(2)
+ self.assertEqual(uv.shape(), unsigned(1))
+ self.assertEqual(uv.lower_count, 1)
+
+
+class SampleTestCase(FHDLTestCase):
+ def test_const(self):
+ s = Sample(1, 1, "sync")
+ self.assertEqual(s.shape(), unsigned(1))
+
+ def test_signal(self):
+ s1 = Sample(Signal(2), 1, "sync")
+ self.assertEqual(s1.shape(), unsigned(2))
+ s2 = Sample(ClockSignal(), 1, "sync")
+ s3 = Sample(ResetSignal(), 1, "sync")
+
+ def test_wrong_value_operator(self):
+ with self.assertRaisesRegex(TypeError,
+ (r"^Sampled value must be a signal or a constant, not "
+ r"\(\+ \(sig \$signal\) \(const 1'd1\)\)$")):
+ Sample(Signal() + 1, 1, "sync")
+
+ def test_wrong_clocks_neg(self):
+ with self.assertRaisesRegex(ValueError,
+ r"^Cannot sample a value 1 cycles in the future$"):
+ Sample(Signal(), -1, "sync")
+
+ def test_wrong_domain(self):
+ with self.assertRaisesRegex(TypeError,
+ r"^Domain name must be a string or None, not 0$"):
+ Sample(Signal(), 1, 0)
+
+
+class InitialTestCase(FHDLTestCase):
+ def test_initial(self):
+ i = Initial()
+ self.assertEqual(i.shape(), unsigned(1))
--- /dev/null
+from nmigen.hdl.cd import *
+
+from .utils import *
+
+
+class ClockDomainTestCase(FHDLTestCase):
+ def test_name(self):
+ sync = ClockDomain()
+ self.assertEqual(sync.name, "sync")
+ self.assertEqual(sync.clk.name, "clk")
+ self.assertEqual(sync.rst.name, "rst")
+ self.assertEqual(sync.local, False)
+ pix = ClockDomain()
+ self.assertEqual(pix.name, "pix")
+ self.assertEqual(pix.clk.name, "pix_clk")
+ self.assertEqual(pix.rst.name, "pix_rst")
+ cd_pix = ClockDomain()
+ self.assertEqual(pix.name, "pix")
+ dom = [ClockDomain("foo")][0]
+ self.assertEqual(dom.name, "foo")
+ with self.assertRaisesRegex(ValueError,
+ r"^Clock domain name must be specified explicitly$"):
+ ClockDomain()
+ cd_reset = ClockDomain(local=True)
+ self.assertEqual(cd_reset.local, True)
+
+ def test_edge(self):
+ sync = ClockDomain()
+ self.assertEqual(sync.clk_edge, "pos")
+ sync = ClockDomain(clk_edge="pos")
+ self.assertEqual(sync.clk_edge, "pos")
+ sync = ClockDomain(clk_edge="neg")
+ self.assertEqual(sync.clk_edge, "neg")
+
+ def test_edge_wrong(self):
+ with self.assertRaisesRegex(ValueError,
+ r"^Domain clock edge must be one of 'pos' or 'neg', not 'xxx'$"):
+ ClockDomain("sync", clk_edge="xxx")
+
+ def test_with_reset(self):
+ pix = ClockDomain()
+ self.assertIsNotNone(pix.clk)
+ self.assertIsNotNone(pix.rst)
+ self.assertFalse(pix.async_reset)
+
+ def test_without_reset(self):
+ pix = ClockDomain(reset_less=True)
+ self.assertIsNotNone(pix.clk)
+ self.assertIsNone(pix.rst)
+ self.assertFalse(pix.async_reset)
+
+ def test_async_reset(self):
+ pix = ClockDomain(async_reset=True)
+ self.assertIsNotNone(pix.clk)
+ self.assertIsNotNone(pix.rst)
+ self.assertTrue(pix.async_reset)
+
+ def test_rename(self):
+ sync = ClockDomain()
+ self.assertEqual(sync.name, "sync")
+ self.assertEqual(sync.clk.name, "clk")
+ self.assertEqual(sync.rst.name, "rst")
+ sync.rename("pix")
+ self.assertEqual(sync.name, "pix")
+ self.assertEqual(sync.clk.name, "pix_clk")
+ self.assertEqual(sync.rst.name, "pix_rst")
+
+ def test_rename_reset_less(self):
+ sync = ClockDomain(reset_less=True)
+ self.assertEqual(sync.name, "sync")
+ self.assertEqual(sync.clk.name, "clk")
+ sync.rename("pix")
+ self.assertEqual(sync.name, "pix")
+ self.assertEqual(sync.clk.name, "pix_clk")
+
+ def test_wrong_name_comb(self):
+ with self.assertRaisesRegex(ValueError,
+ r"^Domain 'comb' may not be clocked$"):
+ comb = ClockDomain()
--- /dev/null
+# nmigen: UnusedElaboratable=no
+
+from collections import OrderedDict
+from enum import Enum
+
+from nmigen.hdl.ast import *
+from nmigen.hdl.cd import *
+from nmigen.hdl.dsl import *
+
+from .utils import *
+
+
+class DSLTestCase(FHDLTestCase):
+ def setUp(self):
+ self.s1 = Signal()
+ self.s2 = Signal()
+ self.s3 = Signal()
+ self.c1 = Signal()
+ self.c2 = Signal()
+ self.c3 = Signal()
+ self.w1 = Signal(4)
+
+ def test_cant_inherit(self):
+ with self.assertRaisesRegex(SyntaxError,
+ (r"^Instead of inheriting from `Module`, inherit from `Elaboratable` and "
+ r"return a `Module` from the `elaborate\(self, platform\)` method$")):
+ class ORGate(Module):
+ pass
+
+ def test_d_comb(self):
+ m = Module()
+ m.d.comb += self.c1.eq(1)
+ m._flush()
+ self.assertEqual(m._driving[self.c1], None)
+ self.assertRepr(m._statements, """(
+ (eq (sig c1) (const 1'd1))
+ )""")
+
+ def test_d_sync(self):
+ m = Module()
+ m.d.sync += self.c1.eq(1)
+ m._flush()
+ self.assertEqual(m._driving[self.c1], "sync")
+ self.assertRepr(m._statements, """(
+ (eq (sig c1) (const 1'd1))
+ )""")
+
+ def test_d_pix(self):
+ m = Module()
+ m.d.pix += self.c1.eq(1)
+ m._flush()
+ self.assertEqual(m._driving[self.c1], "pix")
+ self.assertRepr(m._statements, """(
+ (eq (sig c1) (const 1'd1))
+ )""")
+
+ def test_d_index(self):
+ m = Module()
+ m.d["pix"] += self.c1.eq(1)
+ m._flush()
+ self.assertEqual(m._driving[self.c1], "pix")
+ self.assertRepr(m._statements, """(
+ (eq (sig c1) (const 1'd1))
+ )""")
+
+ def test_d_no_conflict(self):
+ m = Module()
+ m.d.comb += self.w1[0].eq(1)
+ m.d.comb += self.w1[1].eq(1)
+
+ def test_d_conflict(self):
+ m = Module()
+ with self.assertRaisesRegex(SyntaxError,
+ (r"^Driver-driver conflict: trying to drive \(sig c1\) from d\.sync, but it "
+ r"is already driven from d\.comb$")):
+ m.d.comb += self.c1.eq(1)
+ m.d.sync += self.c1.eq(1)
+
+ def test_d_wrong(self):
+ m = Module()
+ with self.assertRaisesRegex(AttributeError,
+ r"^Cannot assign 'd\.pix' attribute; did you mean 'd.pix \+='\?$"):
+ m.d.pix = None
+
+ def test_d_asgn_wrong(self):
+ m = Module()
+ with self.assertRaisesRegex(SyntaxError,
+ r"^Only assignments and property checks may be appended to d\.sync$"):
+ m.d.sync += Switch(self.s1, {})
+
+ def test_comb_wrong(self):
+ m = Module()
+ with self.assertRaisesRegex(AttributeError,
+ r"^'Module' object has no attribute 'comb'; did you mean 'd\.comb'\?$"):
+ m.comb += self.c1.eq(1)
+
+ def test_sync_wrong(self):
+ m = Module()
+ with self.assertRaisesRegex(AttributeError,
+ r"^'Module' object has no attribute 'sync'; did you mean 'd\.sync'\?$"):
+ m.sync += self.c1.eq(1)
+
+ def test_attr_wrong(self):
+ m = Module()
+ with self.assertRaisesRegex(AttributeError,
+ r"^'Module' object has no attribute 'nonexistentattr'$"):
+ m.nonexistentattr
+
+ def test_d_suspicious(self):
+ m = Module()
+ with self.assertWarnsRegex(SyntaxWarning,
+ (r"^Using '<module>\.d\.submodules' would add statements to clock domain "
+ r"'submodules'; did you mean <module>\.submodules instead\?$")):
+ m.d.submodules += []
+
+ def test_clock_signal(self):
+ m = Module()
+ m.d.comb += ClockSignal("pix").eq(ClockSignal())
+ self.assertRepr(m._statements, """
+ (
+ (eq (clk pix) (clk sync))
+ )
+ """)
+
+ def test_reset_signal(self):
+ m = Module()
+ m.d.comb += ResetSignal("pix").eq(1)
+ self.assertRepr(m._statements, """
+ (
+ (eq (rst pix) (const 1'd1))
+ )
+ """)
+
+ def test_sample_domain(self):
+ m = Module()
+ i = Signal()
+ o1 = Signal()
+ o2 = Signal()
+ o3 = Signal()
+ m.d.sync += o1.eq(Past(i))
+ m.d.pix += o2.eq(Past(i))
+ m.d.pix += o3.eq(Past(i, domain="sync"))
+ f = m.elaborate(platform=None)
+ self.assertRepr(f.statements, """
+ (
+ (eq (sig o1) (sample (sig i) @ sync[1]))
+ (eq (sig o2) (sample (sig i) @ pix[1]))
+ (eq (sig o3) (sample (sig i) @ sync[1]))
+ )
+ """)
+
+ def test_If(self):
+ m = Module()
+ with m.If(self.s1):
+ m.d.comb += self.c1.eq(1)
+ m._flush()
+ self.assertRepr(m._statements, """
+ (
+ (switch (cat (sig s1))
+ (case 1 (eq (sig c1) (const 1'd1)))
+ )
+ )
+ """)
+
+ def test_If_Elif(self):
+ m = Module()
+ with m.If(self.s1):
+ m.d.comb += self.c1.eq(1)
+ with m.Elif(self.s2):
+ m.d.sync += self.c2.eq(0)
+ m._flush()
+ self.assertRepr(m._statements, """
+ (
+ (switch (cat (sig s1) (sig s2))
+ (case -1 (eq (sig c1) (const 1'd1)))
+ (case 1- (eq (sig c2) (const 1'd0)))
+ )
+ )
+ """)
+
+ def test_If_Elif_Else(self):
+ m = Module()
+ with m.If(self.s1):
+ m.d.comb += self.c1.eq(1)
+ with m.Elif(self.s2):
+ m.d.sync += self.c2.eq(0)
+ with m.Else():
+ m.d.comb += self.c3.eq(1)
+ m._flush()
+ self.assertRepr(m._statements, """
+ (
+ (switch (cat (sig s1) (sig s2))
+ (case -1 (eq (sig c1) (const 1'd1)))
+ (case 1- (eq (sig c2) (const 1'd0)))
+ (default (eq (sig c3) (const 1'd1)))
+ )
+ )
+ """)
+
+ def test_If_If(self):
+ m = Module()
+ with m.If(self.s1):
+ m.d.comb += self.c1.eq(1)
+ with m.If(self.s2):
+ m.d.comb += self.c2.eq(1)
+ m._flush()
+ self.assertRepr(m._statements, """
+ (
+ (switch (cat (sig s1))
+ (case 1 (eq (sig c1) (const 1'd1)))
+ )
+ (switch (cat (sig s2))
+ (case 1 (eq (sig c2) (const 1'd1)))
+ )
+ )
+ """)
+
+ def test_If_nested_If(self):
+ m = Module()
+ with m.If(self.s1):
+ m.d.comb += self.c1.eq(1)
+ with m.If(self.s2):
+ m.d.comb += self.c2.eq(1)
+ m._flush()
+ self.assertRepr(m._statements, """
+ (
+ (switch (cat (sig s1))
+ (case 1 (eq (sig c1) (const 1'd1))
+ (switch (cat (sig s2))
+ (case 1 (eq (sig c2) (const 1'd1)))
+ )
+ )
+ )
+ )
+ """)
+
+ def test_If_dangling_Else(self):
+ m = Module()
+ with m.If(self.s1):
+ m.d.comb += self.c1.eq(1)
+ with m.If(self.s2):
+ m.d.comb += self.c2.eq(1)
+ with m.Else():
+ m.d.comb += self.c3.eq(1)
+ m._flush()
+ self.assertRepr(m._statements, """
+ (
+ (switch (cat (sig s1))
+ (case 1
+ (eq (sig c1) (const 1'd1))
+ (switch (cat (sig s2))
+ (case 1 (eq (sig c2) (const 1'd1)))
+ )
+ )
+ (default
+ (eq (sig c3) (const 1'd1))
+ )
+ )
+ )
+ """)
+
+ def test_Elif_wrong(self):
+ m = Module()
+ with self.assertRaisesRegex(SyntaxError,
+ r"^Elif without preceding If$"):
+ with m.Elif(self.s2):
+ pass
+
+ def test_Else_wrong(self):
+ m = Module()
+ with self.assertRaisesRegex(SyntaxError,
+ r"^Else without preceding If\/Elif$"):
+ with m.Else():
+ pass
+
+ def test_If_wide(self):
+ m = Module()
+ with m.If(self.w1):
+ m.d.comb += self.c1.eq(1)
+ m._flush()
+ self.assertRepr(m._statements, """
+ (
+ (switch (cat (b (sig w1)))
+ (case 1 (eq (sig c1) (const 1'd1)))
+ )
+ )
+ """)
+
+ def test_If_signed_suspicious(self):
+ m = Module()
+ with self.assertWarnsRegex(SyntaxWarning,
+ (r"^Signed values in If\/Elif conditions usually result from inverting Python "
+ r"booleans with ~, which leads to unexpected results\. Replace `~flag` with "
+ r"`not flag`\. \(If this is a false positive, silence this warning with "
+ r"`m\.If\(x\)` → `m\.If\(x\.bool\(\)\)`\.\)$")):
+ with m.If(~True):
+ pass
+
+ def test_Elif_signed_suspicious(self):
+ m = Module()
+ with m.If(0):
+ pass
+ with self.assertWarnsRegex(SyntaxWarning,
+ (r"^Signed values in If\/Elif conditions usually result from inverting Python "
+ r"booleans with ~, which leads to unexpected results\. Replace `~flag` with "
+ r"`not flag`\. \(If this is a false positive, silence this warning with "
+ r"`m\.If\(x\)` → `m\.If\(x\.bool\(\)\)`\.\)$")):
+ with m.Elif(~True):
+ pass
+
+ def test_if_If_Elif_Else(self):
+ m = Module()
+ with self.assertRaisesRegex(SyntaxError,
+ r"^`if m\.If\(\.\.\.\):` does not work; use `with m\.If\(\.\.\.\)`$"):
+ if m.If(0):
+ pass
+ with m.If(0):
+ pass
+ with self.assertRaisesRegex(SyntaxError,
+ r"^`if m\.Elif\(\.\.\.\):` does not work; use `with m\.Elif\(\.\.\.\)`$"):
+ if m.Elif(0):
+ pass
+ with self.assertRaisesRegex(SyntaxError,
+ r"^`if m\.Else\(\.\.\.\):` does not work; use `with m\.Else\(\.\.\.\)`$"):
+ if m.Else():
+ pass
+
+ def test_Switch(self):
+ m = Module()
+ with m.Switch(self.w1):
+ with m.Case(3):
+ m.d.comb += self.c1.eq(1)
+ with m.Case("11--"):
+ m.d.comb += self.c2.eq(1)
+ with m.Case("1 0--"):
+ m.d.comb += self.c2.eq(1)
+ m._flush()
+ self.assertRepr(m._statements, """
+ (
+ (switch (sig w1)
+ (case 0011 (eq (sig c1) (const 1'd1)))
+ (case 11-- (eq (sig c2) (const 1'd1)))
+ (case 10-- (eq (sig c2) (const 1'd1)))
+ )
+ )
+ """)
+
+ def test_Switch_default_Case(self):
+ m = Module()
+ with m.Switch(self.w1):
+ with m.Case(3):
+ m.d.comb += self.c1.eq(1)
+ with m.Case():
+ m.d.comb += self.c2.eq(1)
+ m._flush()
+ self.assertRepr(m._statements, """
+ (
+ (switch (sig w1)
+ (case 0011 (eq (sig c1) (const 1'd1)))
+ (default (eq (sig c2) (const 1'd1)))
+ )
+ )
+ """)
+
+ def test_Switch_default_Default(self):
+ m = Module()
+ with m.Switch(self.w1):
+ with m.Case(3):
+ m.d.comb += self.c1.eq(1)
+ with m.Default():
+ m.d.comb += self.c2.eq(1)
+ m._flush()
+ self.assertRepr(m._statements, """
+ (
+ (switch (sig w1)
+ (case 0011 (eq (sig c1) (const 1'd1)))
+ (default (eq (sig c2) (const 1'd1)))
+ )
+ )
+ """)
+
+ def test_Switch_const_test(self):
+ m = Module()
+ with m.Switch(1):
+ with m.Case(1):
+ m.d.comb += self.c1.eq(1)
+ m._flush()
+ self.assertRepr(m._statements, """
+ (
+ (switch (const 1'd1)
+ (case 1 (eq (sig c1) (const 1'd1)))
+ )
+ )
+ """)
+
+ def test_Switch_enum(self):
+ class Color(Enum):
+ RED = 1
+ BLUE = 2
+ m = Module()
+ se = Signal(Color)
+ with m.Switch(se):
+ with m.Case(Color.RED):
+ m.d.comb += self.c1.eq(1)
+ self.assertRepr(m._statements, """
+ (
+ (switch (sig se)
+ (case 01 (eq (sig c1) (const 1'd1)))
+ )
+ )
+ """)
+
+ def test_Case_width_wrong(self):
+ class Color(Enum):
+ RED = 0b10101010
+ m = Module()
+ with m.Switch(self.w1):
+ with self.assertRaisesRegex(SyntaxError,
+ r"^Case pattern '--' must have the same width as switch value \(which is 4\)$"):
+ with m.Case("--"):
+ pass
+ with self.assertWarnsRegex(SyntaxWarning,
+ (r"^Case pattern '10110' is wider than switch value \(which has width 4\); "
+ r"comparison will never be true$")):
+ with m.Case(0b10110):
+ pass
+ with self.assertWarnsRegex(SyntaxWarning,
+ (r"^Case pattern '10101010' \(Color\.RED\) is wider than switch value "
+ r"\(which has width 4\); comparison will never be true$")):
+ with m.Case(Color.RED):
+ pass
+ self.assertRepr(m._statements, """
+ (
+ (switch (sig w1) )
+ )
+ """)
+
+ def test_Case_bits_wrong(self):
+ m = Module()
+ with m.Switch(self.w1):
+ with self.assertRaisesRegex(SyntaxError,
+ (r"^Case pattern 'abc' must consist of 0, 1, and - \(don't care\) bits, "
+ r"and may include whitespace$")):
+ with m.Case("abc"):
+ pass
+
+ def test_Case_pattern_wrong(self):
+ m = Module()
+ with m.Switch(self.w1):
+ with self.assertRaisesRegex(SyntaxError,
+ r"^Case pattern must be an integer, a string, or an enumeration, not 1\.0$"):
+ with m.Case(1.0):
+ pass
+
+ def test_Case_outside_Switch_wrong(self):
+ m = Module()
+ with self.assertRaisesRegex(SyntaxError,
+ r"^Case is not permitted outside of Switch$"):
+ with m.Case():
+ pass
+
+ def test_If_inside_Switch_wrong(self):
+ m = Module()
+ with m.Switch(self.s1):
+ with self.assertRaisesRegex(SyntaxError,
+ (r"^If is not permitted directly inside of Switch; "
+ r"it is permitted inside of Switch Case$")):
+ with m.If(self.s2):
+ pass
+
+ def test_FSM_basic(self):
+ a = Signal()
+ b = Signal()
+ c = Signal()
+ m = Module()
+ with m.FSM():
+ with m.State("FIRST"):
+ m.d.comb += a.eq(1)
+ m.next = "SECOND"
+ with m.State("SECOND"):
+ m.d.sync += b.eq(~b)
+ with m.If(c):
+ m.next = "FIRST"
+ m._flush()
+ self.assertRepr(m._statements, """
+ (
+ (switch (sig fsm_state)
+ (case 0
+ (eq (sig a) (const 1'd1))
+ (eq (sig fsm_state) (const 1'd1))
+ )
+ (case 1
+ (eq (sig b) (~ (sig b)))
+ (switch (cat (sig c))
+ (case 1
+ (eq (sig fsm_state) (const 1'd0)))
+ )
+ )
+ )
+ )
+ """)
+ self.assertEqual({repr(k): v for k, v in m._driving.items()}, {
+ "(sig a)": None,
+ "(sig fsm_state)": "sync",
+ "(sig b)": "sync",
+ })
+
+ frag = m.elaborate(platform=None)
+ fsm = frag.find_generated("fsm")
+ self.assertIsInstance(fsm.state, Signal)
+ self.assertEqual(fsm.encoding, OrderedDict({
+ "FIRST": 0,
+ "SECOND": 1,
+ }))
+ self.assertEqual(fsm.decoding, OrderedDict({
+ 0: "FIRST",
+ 1: "SECOND"
+ }))
+
+ def test_FSM_reset(self):
+ a = Signal()
+ m = Module()
+ with m.FSM(reset="SECOND"):
+ with m.State("FIRST"):
+ m.d.comb += a.eq(0)
+ m.next = "SECOND"
+ with m.State("SECOND"):
+ m.next = "FIRST"
+ m._flush()
+ self.assertRepr(m._statements, """
+ (
+ (switch (sig fsm_state)
+ (case 0
+ (eq (sig a) (const 1'd0))
+ (eq (sig fsm_state) (const 1'd1))
+ )
+ (case 1
+ (eq (sig fsm_state) (const 1'd0))
+ )
+ )
+ )
+ """)
+
+ def test_FSM_ongoing(self):
+ a = Signal()
+ b = Signal()
+ m = Module()
+ with m.FSM() as fsm:
+ m.d.comb += b.eq(fsm.ongoing("SECOND"))
+ with m.State("FIRST"):
+ pass
+ m.d.comb += a.eq(fsm.ongoing("FIRST"))
+ with m.State("SECOND"):
+ pass
+ m._flush()
+ self.assertEqual(m._generated["fsm"].state.reset, 1)
+ self.maxDiff = 10000
+ self.assertRepr(m._statements, """
+ (
+ (eq (sig b) (== (sig fsm_state) (const 1'd0)))
+ (eq (sig a) (== (sig fsm_state) (const 1'd1)))
+ (switch (sig fsm_state)
+ (case 1
+ )
+ (case 0
+ )
+ )
+ )
+ """)
+
+ def test_FSM_empty(self):
+ m = Module()
+ with m.FSM():
+ pass
+ self.assertRepr(m._statements, """
+ ()
+ """)
+
+ def test_FSM_wrong_domain(self):
+ m = Module()
+ with self.assertRaisesRegex(ValueError,
+ r"^FSM may not be driven by the 'comb' domain$"):
+ with m.FSM(domain="comb"):
+ pass
+
+ def test_FSM_wrong_undefined(self):
+ m = Module()
+ with self.assertRaisesRegex(NameError,
+ r"^FSM state 'FOO' is referenced but not defined$"):
+ with m.FSM() as fsm:
+ fsm.ongoing("FOO")
+
+ def test_FSM_wrong_redefined(self):
+ m = Module()
+ with m.FSM():
+ with m.State("FOO"):
+ pass
+ with self.assertRaisesRegex(NameError,
+ r"^FSM state 'FOO' is already defined$"):
+ with m.State("FOO"):
+ pass
+
+ def test_FSM_wrong_next(self):
+ m = Module()
+ with self.assertRaisesRegex(SyntaxError,
+ r"^Only assignment to `m\.next` is permitted$"):
+ m.next
+ with self.assertRaisesRegex(SyntaxError,
+ r"^`m\.next = <\.\.\.>` is only permitted inside an FSM state$"):
+ m.next = "FOO"
+ with self.assertRaisesRegex(SyntaxError,
+ r"^`m\.next = <\.\.\.>` is only permitted inside an FSM state$"):
+ with m.FSM():
+ m.next = "FOO"
+
+ def test_If_inside_FSM_wrong(self):
+ m = Module()
+ with m.FSM():
+ with m.State("FOO"):
+ pass
+ with self.assertRaisesRegex(SyntaxError,
+ (r"^If is not permitted directly inside of FSM; "
+ r"it is permitted inside of FSM State$")):
+ with m.If(self.s2):
+ pass
+
+ def test_auto_pop_ctrl(self):
+ m = Module()
+ with m.If(self.w1):
+ m.d.comb += self.c1.eq(1)
+ m.d.comb += self.c2.eq(1)
+ self.assertRepr(m._statements, """
+ (
+ (switch (cat (b (sig w1)))
+ (case 1 (eq (sig c1) (const 1'd1)))
+ )
+ (eq (sig c2) (const 1'd1))
+ )
+ """)
+
+ def test_submodule_anon(self):
+ m1 = Module()
+ m2 = Module()
+ m1.submodules += m2
+ self.assertEqual(m1._anon_submodules, [m2])
+ self.assertEqual(m1._named_submodules, {})
+
+ def test_submodule_anon_multi(self):
+ m1 = Module()
+ m2 = Module()
+ m3 = Module()
+ m1.submodules += m2, m3
+ self.assertEqual(m1._anon_submodules, [m2, m3])
+ self.assertEqual(m1._named_submodules, {})
+
+ def test_submodule_named(self):
+ m1 = Module()
+ m2 = Module()
+ m1.submodules.foo = m2
+ self.assertEqual(m1._anon_submodules, [])
+ self.assertEqual(m1._named_submodules, {"foo": m2})
+
+ def test_submodule_named_index(self):
+ m1 = Module()
+ m2 = Module()
+ m1.submodules["foo"] = m2
+ self.assertEqual(m1._anon_submodules, [])
+ self.assertEqual(m1._named_submodules, {"foo": m2})
+
+ def test_submodule_wrong(self):
+ m = Module()
+ with self.assertRaisesRegex(TypeError,
+ r"^Trying to add 1, which does not implement \.elaborate\(\), as a submodule$"):
+ m.submodules.foo = 1
+ with self.assertRaisesRegex(TypeError,
+ r"^Trying to add 1, which does not implement \.elaborate\(\), as a submodule$"):
+ m.submodules += 1
+
+ def test_submodule_named_conflict(self):
+ m1 = Module()
+ m2 = Module()
+ m1.submodules.foo = m2
+ with self.assertRaisesRegex(NameError, r"^Submodule named 'foo' already exists$"):
+ m1.submodules.foo = m2
+
+ def test_submodule_get(self):
+ m1 = Module()
+ m2 = Module()
+ m1.submodules.foo = m2
+ m3 = m1.submodules.foo
+ self.assertEqual(m2, m3)
+
+ def test_submodule_get_index(self):
+ m1 = Module()
+ m2 = Module()
+ m1.submodules["foo"] = m2
+ m3 = m1.submodules["foo"]
+ self.assertEqual(m2, m3)
+
+ def test_submodule_get_unset(self):
+ m1 = Module()
+ with self.assertRaisesRegex(AttributeError, r"^No submodule named 'foo' exists$"):
+ m2 = m1.submodules.foo
+ with self.assertRaisesRegex(AttributeError, r"^No submodule named 'foo' exists$"):
+ m2 = m1.submodules["foo"]
+
+ def test_domain_named_implicit(self):
+ m = Module()
+ m.domains += ClockDomain("sync")
+ self.assertEqual(len(m._domains), 1)
+
+ def test_domain_named_explicit(self):
+ m = Module()
+ m.domains.foo = ClockDomain()
+ self.assertEqual(len(m._domains), 1)
+ self.assertEqual(m._domains["foo"].name, "foo")
+
+ def test_domain_add_wrong(self):
+ m = Module()
+ with self.assertRaisesRegex(TypeError,
+ r"^Only clock domains may be added to `m\.domains`, not 1$"):
+ m.domains.foo = 1
+ with self.assertRaisesRegex(TypeError,
+ r"^Only clock domains may be added to `m\.domains`, not 1$"):
+ m.domains += 1
+
+ def test_domain_add_wrong_name(self):
+ m = Module()
+ with self.assertRaisesRegex(NameError,
+ r"^Clock domain name 'bar' must match name in `m\.domains\.foo \+= \.\.\.` syntax$"):
+ m.domains.foo = ClockDomain("bar")
+
+ def test_domain_add_wrong_duplicate(self):
+ m = Module()
+ m.domains += ClockDomain("foo")
+ with self.assertRaisesRegex(NameError,
+ r"^Clock domain named 'foo' already exists$"):
+ m.domains += ClockDomain("foo")
+
+ def test_lower(self):
+ m1 = Module()
+ m1.d.comb += self.c1.eq(self.s1)
+ m2 = Module()
+ m2.d.comb += self.c2.eq(self.s2)
+ m2.d.sync += self.c3.eq(self.s3)
+ m1.submodules.foo = m2
+
+ f1 = m1.elaborate(platform=None)
+ self.assertRepr(f1.statements, """
+ (
+ (eq (sig c1) (sig s1))
+ )
+ """)
+ self.assertEqual(f1.drivers, {
+ None: SignalSet((self.c1,))
+ })
+ self.assertEqual(len(f1.subfragments), 1)
+ (f2, f2_name), = f1.subfragments
+ self.assertEqual(f2_name, "foo")
+ self.assertRepr(f2.statements, """
+ (
+ (eq (sig c2) (sig s2))
+ (eq (sig c3) (sig s3))
+ )
+ """)
+ self.assertEqual(f2.drivers, {
+ None: SignalSet((self.c2,)),
+ "sync": SignalSet((self.c3,))
+ })
+ self.assertEqual(len(f2.subfragments), 0)
--- /dev/null
+# nmigen: UnusedElaboratable=no
+
+from collections import OrderedDict
+
+from nmigen.hdl.ast import *
+from nmigen.hdl.cd import *
+from nmigen.hdl.ir import *
+from nmigen.hdl.mem import *
+
+from .utils import *
+
+
+class BadElaboratable(Elaboratable):
+ def elaborate(self, platform):
+ return
+
+
+class FragmentGetTestCase(FHDLTestCase):
+ def test_get_wrong(self):
+ with self.assertRaisesRegex(AttributeError,
+ r"^Object None cannot be elaborated$"):
+ Fragment.get(None, platform=None)
+
+ with self.assertWarnsRegex(UserWarning,
+ r"^\.elaborate\(\) returned None; missing return statement\?$"):
+ with self.assertRaisesRegex(AttributeError,
+ r"^Object None cannot be elaborated$"):
+ Fragment.get(BadElaboratable(), platform=None)
+
+
+class FragmentGeneratedTestCase(FHDLTestCase):
+ def test_find_subfragment(self):
+ f1 = Fragment()
+ f2 = Fragment()
+ f1.add_subfragment(f2, "f2")
+
+ self.assertEqual(f1.find_subfragment(0), f2)
+ self.assertEqual(f1.find_subfragment("f2"), f2)
+
+ def test_find_subfragment_wrong(self):
+ f1 = Fragment()
+ f2 = Fragment()
+ f1.add_subfragment(f2, "f2")
+
+ with self.assertRaisesRegex(NameError,
+ r"^No subfragment at index #1$"):
+ f1.find_subfragment(1)
+ with self.assertRaisesRegex(NameError,
+ r"^No subfragment with name 'fx'$"):
+ f1.find_subfragment("fx")
+
+ def test_find_generated(self):
+ f1 = Fragment()
+ f2 = Fragment()
+ f2.generated["sig"] = sig = Signal()
+ f1.add_subfragment(f2, "f2")
+
+ self.assertEqual(SignalKey(f1.find_generated("f2", "sig")),
+ SignalKey(sig))
+
+
+class FragmentDriversTestCase(FHDLTestCase):
+ def test_empty(self):
+ f = Fragment()
+ self.assertEqual(list(f.iter_comb()), [])
+ self.assertEqual(list(f.iter_sync()), [])
+
+
+class FragmentPortsTestCase(FHDLTestCase):
+ def setUp(self):
+ self.s1 = Signal()
+ self.s2 = Signal()
+ self.s3 = Signal()
+ self.c1 = Signal()
+ self.c2 = Signal()
+ self.c3 = Signal()
+
+ def test_empty(self):
+ f = Fragment()
+ self.assertEqual(list(f.iter_ports()), [])
+
+ f._propagate_ports(ports=(), all_undef_as_ports=True)
+ self.assertEqual(f.ports, SignalDict([]))
+
+ def test_iter_signals(self):
+ f = Fragment()
+ f.add_ports(self.s1, self.s2, dir="io")
+ self.assertEqual(SignalSet((self.s1, self.s2)), f.iter_signals())
+
+ def test_self_contained(self):
+ f = Fragment()
+ f.add_statements(
+ self.c1.eq(self.s1),
+ self.s1.eq(self.c1)
+ )
+
+ f._propagate_ports(ports=(), all_undef_as_ports=True)
+ self.assertEqual(f.ports, SignalDict([]))
+
+ def test_infer_input(self):
+ f = Fragment()
+ f.add_statements(
+ self.c1.eq(self.s1)
+ )
+
+ f._propagate_ports(ports=(), all_undef_as_ports=True)
+ self.assertEqual(f.ports, SignalDict([
+ (self.s1, "i")
+ ]))
+
+ def test_request_output(self):
+ f = Fragment()
+ f.add_statements(
+ self.c1.eq(self.s1)
+ )
+
+ f._propagate_ports(ports=(self.c1,), all_undef_as_ports=True)
+ self.assertEqual(f.ports, SignalDict([
+ (self.s1, "i"),
+ (self.c1, "o")
+ ]))
+
+ def test_input_in_subfragment(self):
+ f1 = Fragment()
+ f1.add_statements(
+ self.c1.eq(self.s1)
+ )
+ f2 = Fragment()
+ f2.add_statements(
+ self.s1.eq(0)
+ )
+ f1.add_subfragment(f2)
+ f1._propagate_ports(ports=(), all_undef_as_ports=True)
+ self.assertEqual(f1.ports, SignalDict())
+ self.assertEqual(f2.ports, SignalDict([
+ (self.s1, "o"),
+ ]))
+
+ def test_input_only_in_subfragment(self):
+ f1 = Fragment()
+ f2 = Fragment()
+ f2.add_statements(
+ self.c1.eq(self.s1)
+ )
+ f1.add_subfragment(f2)
+ f1._propagate_ports(ports=(), all_undef_as_ports=True)
+ self.assertEqual(f1.ports, SignalDict([
+ (self.s1, "i"),
+ ]))
+ self.assertEqual(f2.ports, SignalDict([
+ (self.s1, "i"),
+ ]))
+
+ def test_output_from_subfragment(self):
+ f1 = Fragment()
+ f1.add_statements(
+ self.c1.eq(0)
+ )
+ f2 = Fragment()
+ f2.add_statements(
+ self.c2.eq(1)
+ )
+ f1.add_subfragment(f2)
+
+ f1._propagate_ports(ports=(self.c2,), all_undef_as_ports=True)
+ self.assertEqual(f1.ports, SignalDict([
+ (self.c2, "o"),
+ ]))
+ self.assertEqual(f2.ports, SignalDict([
+ (self.c2, "o"),
+ ]))
+
+ def test_output_from_subfragment_2(self):
+ f1 = Fragment()
+ f1.add_statements(
+ self.c1.eq(self.s1)
+ )
+ f2 = Fragment()
+ f2.add_statements(
+ self.c2.eq(self.s1)
+ )
+ f1.add_subfragment(f2)
+ f3 = Fragment()
+ f3.add_statements(
+ self.s1.eq(0)
+ )
+ f2.add_subfragment(f3)
+
+ f1._propagate_ports(ports=(), all_undef_as_ports=True)
+ self.assertEqual(f2.ports, SignalDict([
+ (self.s1, "o"),
+ ]))
+
+ def test_input_output_sibling(self):
+ f1 = Fragment()
+ f2 = Fragment()
+ f2.add_statements(
+ self.c1.eq(self.c2)
+ )
+ f1.add_subfragment(f2)
+ f3 = Fragment()
+ f3.add_statements(
+ self.c2.eq(0)
+ )
+ f3.add_driver(self.c2)
+ f1.add_subfragment(f3)
+
+ f1._propagate_ports(ports=(), all_undef_as_ports=True)
+ self.assertEqual(f1.ports, SignalDict())
+
+ def test_output_input_sibling(self):
+ f1 = Fragment()
+ f2 = Fragment()
+ f2.add_statements(
+ self.c2.eq(0)
+ )
+ f2.add_driver(self.c2)
+ f1.add_subfragment(f2)
+ f3 = Fragment()
+ f3.add_statements(
+ self.c1.eq(self.c2)
+ )
+ f1.add_subfragment(f3)
+
+ f1._propagate_ports(ports=(), all_undef_as_ports=True)
+ self.assertEqual(f1.ports, SignalDict())
+
+ def test_input_cd(self):
+ sync = ClockDomain()
+ f = Fragment()
+ f.add_statements(
+ self.c1.eq(self.s1)
+ )
+ f.add_domains(sync)
+ f.add_driver(self.c1, "sync")
+
+ f._propagate_ports(ports=(), all_undef_as_ports=True)
+ self.assertEqual(f.ports, SignalDict([
+ (self.s1, "i"),
+ (sync.clk, "i"),
+ (sync.rst, "i"),
+ ]))
+
+ def test_input_cd_reset_less(self):
+ sync = ClockDomain(reset_less=True)
+ f = Fragment()
+ f.add_statements(
+ self.c1.eq(self.s1)
+ )
+ f.add_domains(sync)
+ f.add_driver(self.c1, "sync")
+
+ f._propagate_ports(ports=(), all_undef_as_ports=True)
+ self.assertEqual(f.ports, SignalDict([
+ (self.s1, "i"),
+ (sync.clk, "i"),
+ ]))
+
+ def test_inout(self):
+ s = Signal()
+ f1 = Fragment()
+ f2 = Instance("foo", io_x=s)
+ f1.add_subfragment(f2)
+
+ f1._propagate_ports(ports=(), all_undef_as_ports=True)
+ self.assertEqual(f1.ports, SignalDict([
+ (s, "io")
+ ]))
+
+ def test_in_out_same_signal(self):
+ s = Signal()
+
+ f1 = Instance("foo", i_x=s, o_y=s)
+ f2 = Fragment()
+ f2.add_subfragment(f1)
+
+ f2._propagate_ports(ports=(), all_undef_as_ports=True)
+ self.assertEqual(f1.ports, SignalDict([
+ (s, "o")
+ ]))
+
+ f3 = Instance("foo", o_y=s, i_x=s)
+ f4 = Fragment()
+ f4.add_subfragment(f3)
+
+ f4._propagate_ports(ports=(), all_undef_as_ports=True)
+ self.assertEqual(f3.ports, SignalDict([
+ (s, "o")
+ ]))
+
+ def test_clk_rst(self):
+ sync = ClockDomain()
+ f = Fragment()
+ f.add_domains(sync)
+
+ f = f.prepare(ports=(ClockSignal("sync"), ResetSignal("sync")))
+ self.assertEqual(f.ports, SignalDict([
+ (sync.clk, "i"),
+ (sync.rst, "i"),
+ ]))
+
+ def test_port_wrong(self):
+ f = Fragment()
+ with self.assertRaisesRegex(TypeError,
+ r"^Only signals may be added as ports, not \(const 1'd1\)$"):
+ f.prepare(ports=(Const(1),))
+
+ def test_port_not_iterable(self):
+ f = Fragment()
+ with self.assertRaisesRegex(TypeError,
+ r"^`ports` must be either a list or a tuple, not 1$"):
+ f.prepare(ports=1)
+ with self.assertRaisesRegex(TypeError,
+ (r"^`ports` must be either a list or a tuple, not \(const 1'd1\)"
+ r" \(did you mean `ports=\(<signal>,\)`, rather than `ports=<signal>`\?\)$")):
+ f.prepare(ports=Const(1))
+
+class FragmentDomainsTestCase(FHDLTestCase):
+ def test_iter_signals(self):
+ cd1 = ClockDomain()
+ cd2 = ClockDomain(reset_less=True)
+ s1 = Signal()
+ s2 = Signal()
+
+ f = Fragment()
+ f.add_domains(cd1, cd2)
+ f.add_driver(s1, "cd1")
+ self.assertEqual(SignalSet((cd1.clk, cd1.rst, s1)), f.iter_signals())
+ f.add_driver(s2, "cd2")
+ self.assertEqual(SignalSet((cd1.clk, cd1.rst, cd2.clk, s1, s2)), f.iter_signals())
+
+ def test_propagate_up(self):
+ cd = ClockDomain()
+
+ f1 = Fragment()
+ f2 = Fragment()
+ f1.add_subfragment(f2)
+ f2.add_domains(cd)
+
+ f1._propagate_domains_up()
+ self.assertEqual(f1.domains, {"cd": cd})
+
+ def test_propagate_up_local(self):
+ cd = ClockDomain(local=True)
+
+ f1 = Fragment()
+ f2 = Fragment()
+ f1.add_subfragment(f2)
+ f2.add_domains(cd)
+
+ f1._propagate_domains_up()
+ self.assertEqual(f1.domains, {})
+
+ def test_domain_conflict(self):
+ cda = ClockDomain("sync")
+ cdb = ClockDomain("sync")
+
+ fa = Fragment()
+ fa.add_domains(cda)
+ fb = Fragment()
+ fb.add_domains(cdb)
+ f = Fragment()
+ f.add_subfragment(fa, "a")
+ f.add_subfragment(fb, "b")
+
+ f._propagate_domains_up()
+ self.assertEqual(f.domains, {"a_sync": cda, "b_sync": cdb})
+ (fa, _), (fb, _) = f.subfragments
+ self.assertEqual(fa.domains, {"a_sync": cda})
+ self.assertEqual(fb.domains, {"b_sync": cdb})
+
+ def test_domain_conflict_anon(self):
+ cda = ClockDomain("sync")
+ cdb = ClockDomain("sync")
+
+ fa = Fragment()
+ fa.add_domains(cda)
+ fb = Fragment()
+ fb.add_domains(cdb)
+ f = Fragment()
+ f.add_subfragment(fa, "a")
+ f.add_subfragment(fb)
+
+ with self.assertRaisesRegex(DomainError,
+ (r"^Domain 'sync' is defined by subfragments 'a', <unnamed #1> of fragment "
+ r"'top'; it is necessary to either rename subfragment domains explicitly, "
+ r"or give names to subfragments$")):
+ f._propagate_domains_up()
+
+ def test_domain_conflict_name(self):
+ cda = ClockDomain("sync")
+ cdb = ClockDomain("sync")
+
+ fa = Fragment()
+ fa.add_domains(cda)
+ fb = Fragment()
+ fb.add_domains(cdb)
+ f = Fragment()
+ f.add_subfragment(fa, "x")
+ f.add_subfragment(fb, "x")
+
+ with self.assertRaisesRegex(DomainError,
+ (r"^Domain 'sync' is defined by subfragments #0, #1 of fragment 'top', some "
+ r"of which have identical names; it is necessary to either rename subfragment "
+ r"domains explicitly, or give distinct names to subfragments$")):
+ f._propagate_domains_up()
+
+ def test_domain_conflict_rename_drivers(self):
+ cda = ClockDomain("sync")
+ cdb = ClockDomain("sync")
+
+ fa = Fragment()
+ fa.add_domains(cda)
+ fb = Fragment()
+ fb.add_domains(cdb)
+ fb.add_driver(ResetSignal("sync"), None)
+ f = Fragment()
+ f.add_subfragment(fa, "a")
+ f.add_subfragment(fb, "b")
+
+ f._propagate_domains_up()
+ fb_new, _ = f.subfragments[1]
+ self.assertEqual(fb_new.drivers, OrderedDict({
+ None: SignalSet((ResetSignal("b_sync"),))
+ }))
+
+ def test_domain_conflict_rename_drivers(self):
+ cda = ClockDomain("sync")
+ cdb = ClockDomain("sync")
+ s = Signal()
+
+ fa = Fragment()
+ fa.add_domains(cda)
+ fb = Fragment()
+ fb.add_domains(cdb)
+ f = Fragment()
+ f.add_subfragment(fa, "a")
+ f.add_subfragment(fb, "b")
+ f.add_driver(s, "b_sync")
+
+ f._propagate_domains(lambda name: ClockDomain(name))
+
+ def test_propagate_down(self):
+ cd = ClockDomain()
+
+ f1 = Fragment()
+ f2 = Fragment()
+ f1.add_domains(cd)
+ f1.add_subfragment(f2)
+
+ f1._propagate_domains_down()
+ self.assertEqual(f2.domains, {"cd": cd})
+
+ def test_propagate_down_idempotent(self):
+ cd = ClockDomain()
+
+ f1 = Fragment()
+ f1.add_domains(cd)
+ f2 = Fragment()
+ f2.add_domains(cd)
+ f1.add_subfragment(f2)
+
+ f1._propagate_domains_down()
+ self.assertEqual(f1.domains, {"cd": cd})
+ self.assertEqual(f2.domains, {"cd": cd})
+
+ def test_propagate(self):
+ cd = ClockDomain()
+
+ f1 = Fragment()
+ f2 = Fragment()
+ f1.add_domains(cd)
+ f1.add_subfragment(f2)
+
+ new_domains = f1._propagate_domains(missing_domain=lambda name: None)
+ self.assertEqual(f1.domains, {"cd": cd})
+ self.assertEqual(f2.domains, {"cd": cd})
+ self.assertEqual(new_domains, [])
+
+ def test_propagate_missing(self):
+ s1 = Signal()
+ f1 = Fragment()
+ f1.add_driver(s1, "sync")
+
+ with self.assertRaisesRegex(DomainError,
+ r"^Domain 'sync' is used but not defined$"):
+ f1._propagate_domains(missing_domain=lambda name: None)
+
+ def test_propagate_create_missing(self):
+ s1 = Signal()
+ f1 = Fragment()
+ f1.add_driver(s1, "sync")
+ f2 = Fragment()
+ f1.add_subfragment(f2)
+
+ new_domains = f1._propagate_domains(missing_domain=lambda name: ClockDomain(name))
+ self.assertEqual(f1.domains.keys(), {"sync"})
+ self.assertEqual(f2.domains.keys(), {"sync"})
+ self.assertEqual(f1.domains["sync"], f2.domains["sync"])
+ self.assertEqual(new_domains, [f1.domains["sync"]])
+
+ def test_propagate_create_missing_fragment(self):
+ s1 = Signal()
+ f1 = Fragment()
+ f1.add_driver(s1, "sync")
+
+ cd = ClockDomain("sync")
+ f2 = Fragment()
+ f2.add_domains(cd)
+
+ new_domains = f1._propagate_domains(missing_domain=lambda name: f2)
+ self.assertEqual(f1.domains.keys(), {"sync"})
+ self.assertEqual(f1.domains["sync"], f2.domains["sync"])
+ self.assertEqual(new_domains, [])
+ self.assertEqual(f1.subfragments, [
+ (f2, "cd_sync")
+ ])
+
+ def test_propagate_create_missing_fragment_many_domains(self):
+ s1 = Signal()
+ f1 = Fragment()
+ f1.add_driver(s1, "sync")
+
+ cd_por = ClockDomain("por")
+ cd_sync = ClockDomain("sync")
+ f2 = Fragment()
+ f2.add_domains(cd_por, cd_sync)
+
+ new_domains = f1._propagate_domains(missing_domain=lambda name: f2)
+ self.assertEqual(f1.domains.keys(), {"sync", "por"})
+ self.assertEqual(f2.domains.keys(), {"sync", "por"})
+ self.assertEqual(f1.domains["sync"], f2.domains["sync"])
+ self.assertEqual(new_domains, [])
+ self.assertEqual(f1.subfragments, [
+ (f2, "cd_sync")
+ ])
+
+ def test_propagate_create_missing_fragment_wrong(self):
+ s1 = Signal()
+ f1 = Fragment()
+ f1.add_driver(s1, "sync")
+
+ f2 = Fragment()
+ f2.add_domains(ClockDomain("foo"))
+
+ with self.assertRaisesRegex(DomainError,
+ (r"^Fragment returned by missing domain callback does not define requested "
+ r"domain 'sync' \(defines 'foo'\)\.$")):
+ f1._propagate_domains(missing_domain=lambda name: f2)
+
+
+class FragmentHierarchyConflictTestCase(FHDLTestCase):
+ def setUp_self_sub(self):
+ self.s1 = Signal()
+ self.c1 = Signal()
+ self.c2 = Signal()
+
+ self.f1 = Fragment()
+ self.f1.add_statements(self.c1.eq(0))
+ self.f1.add_driver(self.s1)
+ self.f1.add_driver(self.c1, "sync")
+
+ self.f1a = Fragment()
+ self.f1.add_subfragment(self.f1a, "f1a")
+
+ self.f2 = Fragment()
+ self.f2.add_statements(self.c2.eq(1))
+ self.f2.add_driver(self.s1)
+ self.f2.add_driver(self.c2, "sync")
+ self.f1.add_subfragment(self.f2)
+
+ self.f1b = Fragment()
+ self.f1.add_subfragment(self.f1b, "f1b")
+
+ self.f2a = Fragment()
+ self.f2.add_subfragment(self.f2a, "f2a")
+
+ def test_conflict_self_sub(self):
+ self.setUp_self_sub()
+
+ self.f1._resolve_hierarchy_conflicts(mode="silent")
+ self.assertEqual(self.f1.subfragments, [
+ (self.f1a, "f1a"),
+ (self.f1b, "f1b"),
+ (self.f2a, "f2a"),
+ ])
+ self.assertRepr(self.f1.statements, """
+ (
+ (eq (sig c1) (const 1'd0))
+ (eq (sig c2) (const 1'd1))
+ )
+ """)
+ self.assertEqual(self.f1.drivers, {
+ None: SignalSet((self.s1,)),
+ "sync": SignalSet((self.c1, self.c2)),
+ })
+
+ def test_conflict_self_sub_error(self):
+ self.setUp_self_sub()
+
+ with self.assertRaisesRegex(DriverConflict,
+ r"^Signal '\(sig s1\)' is driven from multiple fragments: top, top.<unnamed #1>$"):
+ self.f1._resolve_hierarchy_conflicts(mode="error")
+
+ def test_conflict_self_sub_warning(self):
+ self.setUp_self_sub()
+
+ with self.assertWarnsRegex(DriverConflict,
+ (r"^Signal '\(sig s1\)' is driven from multiple fragments: top, top.<unnamed #1>; "
+ r"hierarchy will be flattened$")):
+ self.f1._resolve_hierarchy_conflicts(mode="warn")
+
+ def setUp_sub_sub(self):
+ self.s1 = Signal()
+ self.c1 = Signal()
+ self.c2 = Signal()
+
+ self.f1 = Fragment()
+
+ self.f2 = Fragment()
+ self.f2.add_driver(self.s1)
+ self.f2.add_statements(self.c1.eq(0))
+ self.f1.add_subfragment(self.f2)
+
+ self.f3 = Fragment()
+ self.f3.add_driver(self.s1)
+ self.f3.add_statements(self.c2.eq(1))
+ self.f1.add_subfragment(self.f3)
+
+ def test_conflict_sub_sub(self):
+ self.setUp_sub_sub()
+
+ self.f1._resolve_hierarchy_conflicts(mode="silent")
+ self.assertEqual(self.f1.subfragments, [])
+ self.assertRepr(self.f1.statements, """
+ (
+ (eq (sig c1) (const 1'd0))
+ (eq (sig c2) (const 1'd1))
+ )
+ """)
+
+ def setUp_self_subsub(self):
+ self.s1 = Signal()
+ self.c1 = Signal()
+ self.c2 = Signal()
+
+ self.f1 = Fragment()
+ self.f1.add_driver(self.s1)
+
+ self.f2 = Fragment()
+ self.f2.add_statements(self.c1.eq(0))
+ self.f1.add_subfragment(self.f2)
+
+ self.f3 = Fragment()
+ self.f3.add_driver(self.s1)
+ self.f3.add_statements(self.c2.eq(1))
+ self.f2.add_subfragment(self.f3)
+
+ def test_conflict_self_subsub(self):
+ self.setUp_self_subsub()
+
+ self.f1._resolve_hierarchy_conflicts(mode="silent")
+ self.assertEqual(self.f1.subfragments, [])
+ self.assertRepr(self.f1.statements, """
+ (
+ (eq (sig c1) (const 1'd0))
+ (eq (sig c2) (const 1'd1))
+ )
+ """)
+
+ def setUp_memory(self):
+ self.m = Memory(width=8, depth=4)
+ self.fr = self.m.read_port().elaborate(platform=None)
+ self.fw = self.m.write_port().elaborate(platform=None)
+ self.f1 = Fragment()
+ self.f2 = Fragment()
+ self.f2.add_subfragment(self.fr)
+ self.f1.add_subfragment(self.f2)
+ self.f3 = Fragment()
+ self.f3.add_subfragment(self.fw)
+ self.f1.add_subfragment(self.f3)
+
+ def test_conflict_memory(self):
+ self.setUp_memory()
+
+ self.f1._resolve_hierarchy_conflicts(mode="silent")
+ self.assertEqual(self.f1.subfragments, [
+ (self.fr, None),
+ (self.fw, None),
+ ])
+
+ def test_conflict_memory_error(self):
+ self.setUp_memory()
+
+ with self.assertRaisesRegex(DriverConflict,
+ r"^Memory 'm' is accessed from multiple fragments: top\.<unnamed #0>, "
+ r"top\.<unnamed #1>$"):
+ self.f1._resolve_hierarchy_conflicts(mode="error")
+
+ def test_conflict_memory_warning(self):
+ self.setUp_memory()
+
+ with self.assertWarnsRegex(DriverConflict,
+ (r"^Memory 'm' is accessed from multiple fragments: top.<unnamed #0>, "
+ r"top.<unnamed #1>; hierarchy will be flattened$")):
+ self.f1._resolve_hierarchy_conflicts(mode="warn")
+
+ def test_explicit_flatten(self):
+ self.f1 = Fragment()
+ self.f2 = Fragment()
+ self.f2.flatten = True
+ self.f1.add_subfragment(self.f2)
+
+ self.f1._resolve_hierarchy_conflicts(mode="silent")
+ self.assertEqual(self.f1.subfragments, [])
+
+ def test_no_conflict_local_domains(self):
+ f1 = Fragment()
+ cd1 = ClockDomain("d", local=True)
+ f1.add_domains(cd1)
+ f1.add_driver(ClockSignal("d"))
+ f2 = Fragment()
+ cd2 = ClockDomain("d", local=True)
+ f2.add_domains(cd2)
+ f2.add_driver(ClockSignal("d"))
+ f3 = Fragment()
+ f3.add_subfragment(f1)
+ f3.add_subfragment(f2)
+ f3.prepare()
+
+
+class InstanceTestCase(FHDLTestCase):
+ def test_construct(self):
+ s1 = Signal()
+ s2 = Signal()
+ s3 = Signal()
+ s4 = Signal()
+ s5 = Signal()
+ s6 = Signal()
+ inst = Instance("foo",
+ ("a", "ATTR1", 1),
+ ("p", "PARAM1", 0x1234),
+ ("i", "s1", s1),
+ ("o", "s2", s2),
+ ("io", "s3", s3),
+ a_ATTR2=2,
+ p_PARAM2=0x5678,
+ i_s4=s4,
+ o_s5=s5,
+ io_s6=s6,
+ )
+ self.assertEqual(inst.attrs, OrderedDict([
+ ("ATTR1", 1),
+ ("ATTR2", 2),
+ ]))
+ self.assertEqual(inst.parameters, OrderedDict([
+ ("PARAM1", 0x1234),
+ ("PARAM2", 0x5678),
+ ]))
+ self.assertEqual(inst.named_ports, OrderedDict([
+ ("s1", (s1, "i")),
+ ("s2", (s2, "o")),
+ ("s3", (s3, "io")),
+ ("s4", (s4, "i")),
+ ("s5", (s5, "o")),
+ ("s6", (s6, "io")),
+ ]))
+
+ def test_cast_ports(self):
+ inst = Instance("foo",
+ ("i", "s1", 1),
+ ("o", "s2", 2),
+ ("io", "s3", 3),
+ i_s4=4,
+ o_s5=5,
+ io_s6=6,
+ )
+ self.assertRepr(inst.named_ports["s1"][0], "(const 1'd1)")
+ self.assertRepr(inst.named_ports["s2"][0], "(const 2'd2)")
+ self.assertRepr(inst.named_ports["s3"][0], "(const 2'd3)")
+ self.assertRepr(inst.named_ports["s4"][0], "(const 3'd4)")
+ self.assertRepr(inst.named_ports["s5"][0], "(const 3'd5)")
+ self.assertRepr(inst.named_ports["s6"][0], "(const 3'd6)")
+
+ def test_wrong_construct_arg(self):
+ s = Signal()
+ with self.assertRaisesRegex(NameError,
+ (r"^Instance argument \('', 's1', \(sig s\)\) should be a tuple "
+ r"\(kind, name, value\) where kind is one of \"p\", \"i\", \"o\", or \"io\"$")):
+ Instance("foo", ("", "s1", s))
+
+ def test_wrong_construct_kwarg(self):
+ s = Signal()
+ with self.assertRaisesRegex(NameError,
+ (r"^Instance keyword argument x_s1=\(sig s\) does not start with one of "
+ r"\"p_\", \"i_\", \"o_\", or \"io_\"$")):
+ Instance("foo", x_s1=s)
+
+ def setUp_cpu(self):
+ self.rst = Signal()
+ self.stb = Signal()
+ self.pins = Signal(8)
+ self.datal = Signal(4)
+ self.datah = Signal(4)
+ self.inst = Instance("cpu",
+ p_RESET=0x1234,
+ i_clk=ClockSignal(),
+ i_rst=self.rst,
+ o_stb=self.stb,
+ o_data=Cat(self.datal, self.datah),
+ io_pins=self.pins[:]
+ )
+ self.wrap = Fragment()
+ self.wrap.add_subfragment(self.inst)
+
+ def test_init(self):
+ self.setUp_cpu()
+ f = self.inst
+ self.assertEqual(f.type, "cpu")
+ self.assertEqual(f.parameters, OrderedDict([("RESET", 0x1234)]))
+ self.assertEqual(list(f.named_ports.keys()), ["clk", "rst", "stb", "data", "pins"])
+ self.assertEqual(f.ports, SignalDict([]))
+
+ def test_prepare(self):
+ self.setUp_cpu()
+ f = self.wrap.prepare()
+ sync_clk = f.domains["sync"].clk
+ self.assertEqual(f.ports, SignalDict([
+ (sync_clk, "i"),
+ (self.rst, "i"),
+ (self.pins, "io"),
+ ]))
+
+ def test_prepare_explicit_ports(self):
+ self.setUp_cpu()
+ f = self.wrap.prepare(ports=[self.rst, self.stb])
+ sync_clk = f.domains["sync"].clk
+ sync_rst = f.domains["sync"].rst
+ self.assertEqual(f.ports, SignalDict([
+ (sync_clk, "i"),
+ (sync_rst, "i"),
+ (self.rst, "i"),
+ (self.stb, "o"),
+ (self.pins, "io"),
+ ]))
+
+ def test_prepare_slice_in_port(self):
+ s = Signal(2)
+ f = Fragment()
+ f.add_subfragment(Instance("foo", o_O=s[0]))
+ f.add_subfragment(Instance("foo", o_O=s[1]))
+ fp = f.prepare(ports=[s], missing_domain=lambda name: None)
+ self.assertEqual(fp.ports, SignalDict([
+ (s, "o"),
+ ]))
+
+ def test_prepare_attrs(self):
+ self.setUp_cpu()
+ self.inst.attrs["ATTR"] = 1
+ f = self.inst.prepare()
+ self.assertEqual(f.attrs, OrderedDict([
+ ("ATTR", 1),
+ ]))
--- /dev/null
+# nmigen: UnusedElaboratable=no
+
+from nmigen.hdl.ast import *
+from nmigen.hdl.mem import *
+
+from .utils import *
+
+
+class MemoryTestCase(FHDLTestCase):
+ def test_name(self):
+ m1 = Memory(width=8, depth=4)
+ self.assertEqual(m1.name, "m1")
+ m2 = [Memory(width=8, depth=4)][0]
+ self.assertEqual(m2.name, "$memory")
+ m3 = Memory(width=8, depth=4, name="foo")
+ self.assertEqual(m3.name, "foo")
+
+ def test_geometry(self):
+ m = Memory(width=8, depth=4)
+ self.assertEqual(m.width, 8)
+ self.assertEqual(m.depth, 4)
+
+ def test_geometry_wrong(self):
+ with self.assertRaisesRegex(TypeError,
+ r"^Memory width must be a non-negative integer, not -1$"):
+ m = Memory(width=-1, depth=4)
+ with self.assertRaisesRegex(TypeError,
+ r"^Memory depth must be a non-negative integer, not -1$"):
+ m = Memory(width=8, depth=-1)
+
+ def test_init(self):
+ m = Memory(width=8, depth=4, init=range(4))
+ self.assertEqual(m.init, [0, 1, 2, 3])
+
+ def test_init_wrong_count(self):
+ with self.assertRaisesRegex(ValueError,
+ r"^Memory initialization value count exceed memory depth \(8 > 4\)$"):
+ m = Memory(width=8, depth=4, init=range(8))
+
+ def test_init_wrong_type(self):
+ with self.assertRaisesRegex(TypeError,
+ (r"^Memory initialization value at address 1: "
+ r"'str' object cannot be interpreted as an integer$")):
+ m = Memory(width=8, depth=4, init=[1, "0"])
+
+ def test_attrs(self):
+ m1 = Memory(width=8, depth=4)
+ self.assertEqual(m1.attrs, {})
+ m2 = Memory(width=8, depth=4, attrs={"ram_block": True})
+ self.assertEqual(m2.attrs, {"ram_block": True})
+
+ def test_read_port_transparent(self):
+ mem = Memory(width=8, depth=4)
+ rdport = mem.read_port()
+ self.assertEqual(rdport.memory, mem)
+ self.assertEqual(rdport.domain, "sync")
+ self.assertEqual(rdport.transparent, True)
+ self.assertEqual(len(rdport.addr), 2)
+ self.assertEqual(len(rdport.data), 8)
+ self.assertEqual(len(rdport.en), 1)
+ self.assertIsInstance(rdport.en, Const)
+ self.assertEqual(rdport.en.value, 1)
+
+ def test_read_port_non_transparent(self):
+ mem = Memory(width=8, depth=4)
+ rdport = mem.read_port(transparent=False)
+ self.assertEqual(rdport.memory, mem)
+ self.assertEqual(rdport.domain, "sync")
+ self.assertEqual(rdport.transparent, False)
+ self.assertEqual(len(rdport.en), 1)
+ self.assertIsInstance(rdport.en, Signal)
+ self.assertEqual(rdport.en.reset, 1)
+
+ def test_read_port_asynchronous(self):
+ mem = Memory(width=8, depth=4)
+ rdport = mem.read_port(domain="comb")
+ self.assertEqual(rdport.memory, mem)
+ self.assertEqual(rdport.domain, "comb")
+ self.assertEqual(rdport.transparent, True)
+ self.assertEqual(len(rdport.en), 1)
+ self.assertIsInstance(rdport.en, Const)
+ self.assertEqual(rdport.en.value, 1)
+
+ def test_read_port_wrong(self):
+ mem = Memory(width=8, depth=4)
+ with self.assertRaisesRegex(ValueError,
+ r"^Read port cannot be simultaneously asynchronous and non-transparent$"):
+ mem.read_port(domain="comb", transparent=False)
+
+ def test_write_port(self):
+ mem = Memory(width=8, depth=4)
+ wrport = mem.write_port()
+ self.assertEqual(wrport.memory, mem)
+ self.assertEqual(wrport.domain, "sync")
+ self.assertEqual(wrport.granularity, 8)
+ self.assertEqual(len(wrport.addr), 2)
+ self.assertEqual(len(wrport.data), 8)
+ self.assertEqual(len(wrport.en), 1)
+
+ def test_write_port_granularity(self):
+ mem = Memory(width=8, depth=4)
+ wrport = mem.write_port(granularity=2)
+ self.assertEqual(wrport.memory, mem)
+ self.assertEqual(wrport.domain, "sync")
+ self.assertEqual(wrport.granularity, 2)
+ self.assertEqual(len(wrport.addr), 2)
+ self.assertEqual(len(wrport.data), 8)
+ self.assertEqual(len(wrport.en), 4)
+
+ def test_write_port_granularity_wrong(self):
+ mem = Memory(width=8, depth=4)
+ with self.assertRaisesRegex(TypeError,
+ r"^Write port granularity must be a non-negative integer, not -1$"):
+ mem.write_port(granularity=-1)
+ with self.assertRaisesRegex(ValueError,
+ r"^Write port granularity must not be greater than memory width \(10 > 8\)$"):
+ mem.write_port(granularity=10)
+ with self.assertRaisesRegex(ValueError,
+ r"^Write port granularity must divide memory width evenly$"):
+ mem.write_port(granularity=3)
+
+
+class DummyPortTestCase(FHDLTestCase):
+ def test_name(self):
+ p1 = DummyPort(data_width=8, addr_width=2)
+ self.assertEqual(p1.addr.name, "p1_addr")
+ p2 = [DummyPort(data_width=8, addr_width=2)][0]
+ self.assertEqual(p2.addr.name, "dummy_addr")
+ p3 = DummyPort(data_width=8, addr_width=2, name="foo")
+ self.assertEqual(p3.addr.name, "foo_addr")
+
+ def test_sizes(self):
+ p1 = DummyPort(data_width=8, addr_width=2)
+ self.assertEqual(p1.addr.width, 2)
+ self.assertEqual(p1.data.width, 8)
+ self.assertEqual(p1.en.width, 1)
+ p2 = DummyPort(data_width=8, addr_width=2, granularity=2)
+ self.assertEqual(p2.en.width, 4)
--- /dev/null
+from enum import Enum
+
+from nmigen.hdl.ast import *
+from nmigen.hdl.rec import *
+
+from .utils import *
+
+
+class UnsignedEnum(Enum):
+ FOO = 1
+ BAR = 2
+ BAZ = 3
+
+
+class LayoutTestCase(FHDLTestCase):
+ def assertFieldEqual(self, field, expected):
+ (shape, dir) = field
+ shape = Shape.cast(shape)
+ self.assertEqual((shape, dir), expected)
+
+ def test_fields(self):
+ layout = Layout.cast([
+ ("cyc", 1),
+ ("data", signed(32)),
+ ("stb", 1, DIR_FANOUT),
+ ("ack", 1, DIR_FANIN),
+ ("info", [
+ ("a", 1),
+ ("b", 1),
+ ])
+ ])
+
+ self.assertFieldEqual(layout["cyc"], ((1, False), DIR_NONE))
+ self.assertFieldEqual(layout["data"], ((32, True), DIR_NONE))
+ self.assertFieldEqual(layout["stb"], ((1, False), DIR_FANOUT))
+ self.assertFieldEqual(layout["ack"], ((1, False), DIR_FANIN))
+ sublayout = layout["info"][0]
+ self.assertEqual(layout["info"][1], DIR_NONE)
+ self.assertFieldEqual(sublayout["a"], ((1, False), DIR_NONE))
+ self.assertFieldEqual(sublayout["b"], ((1, False), DIR_NONE))
+
+ def test_enum_field(self):
+ layout = Layout.cast([
+ ("enum", UnsignedEnum),
+ ("enum_dir", UnsignedEnum, DIR_FANOUT),
+ ])
+ self.assertFieldEqual(layout["enum"], ((2, False), DIR_NONE))
+ self.assertFieldEqual(layout["enum_dir"], ((2, False), DIR_FANOUT))
+
+ def test_range_field(self):
+ layout = Layout.cast([
+ ("range", range(0, 7)),
+ ])
+ self.assertFieldEqual(layout["range"], ((3, False), DIR_NONE))
+
+ def test_slice_tuple(self):
+ layout = Layout.cast([
+ ("a", 1),
+ ("b", 2),
+ ("c", 3)
+ ])
+ expect = Layout.cast([
+ ("a", 1),
+ ("c", 3)
+ ])
+ self.assertEqual(layout["a", "c"], expect)
+
+ def test_repr(self):
+ self.assertEqual(repr(Layout([("a", unsigned(1)), ("b", signed(2))])),
+ "Layout([('a', unsigned(1)), ('b', signed(2))])")
+ self.assertEqual(repr(Layout([("a", unsigned(1)), ("b", [("c", signed(3))])])),
+ "Layout([('a', unsigned(1)), "
+ "('b', Layout([('c', signed(3))]))])")
+
+ def test_wrong_field(self):
+ with self.assertRaisesRegex(TypeError,
+ (r"^Field \(1,\) has invalid layout: should be either \(name, shape\) or "
+ r"\(name, shape, direction\)$")):
+ Layout.cast([(1,)])
+
+ def test_wrong_name(self):
+ with self.assertRaisesRegex(TypeError,
+ r"^Field \(1, 1\) has invalid name: should be a string$"):
+ Layout.cast([(1, 1)])
+
+ def test_wrong_name_duplicate(self):
+ with self.assertRaisesRegex(NameError,
+ r"^Field \('a', 2\) has a name that is already present in the layout$"):
+ Layout.cast([("a", 1), ("a", 2)])
+
+ def test_wrong_direction(self):
+ with self.assertRaisesRegex(TypeError,
+ (r"^Field \('a', 1, 0\) has invalid direction: should be a Direction "
+ r"instance like DIR_FANIN$")):
+ Layout.cast([("a", 1, 0)])
+
+ def test_wrong_shape(self):
+ with self.assertRaisesRegex(TypeError,
+ (r"^Field \('a', 'x'\) has invalid shape: should be castable to Shape or "
+ r"a list of fields of a nested record$")):
+ Layout.cast([("a", "x")])
+
+
+class RecordTestCase(FHDLTestCase):
+ def test_basic(self):
+ r = Record([
+ ("stb", 1),
+ ("data", 32),
+ ("info", [
+ ("a", 1),
+ ("b", 1),
+ ])
+ ])
+
+ self.assertEqual(repr(r), "(rec r stb data (rec r__info a b))")
+ self.assertEqual(len(r), 35)
+ self.assertIsInstance(r.stb, Signal)
+ self.assertEqual(r.stb.name, "r__stb")
+ self.assertEqual(r["stb"].name, "r__stb")
+
+ self.assertTrue(hasattr(r, "stb"))
+ self.assertFalse(hasattr(r, "xxx"))
+
+ def test_unnamed(self):
+ r = [Record([
+ ("stb", 1)
+ ])][0]
+
+ self.assertEqual(repr(r), "(rec <unnamed> stb)")
+ self.assertEqual(r.stb.name, "stb")
+
+ def test_iter(self):
+ r = Record([
+ ("data", 4),
+ ("stb", 1),
+ ])
+
+ self.assertEqual(repr(r[0]), "(slice (rec r data stb) 0:1)")
+ self.assertEqual(repr(r[0:3]), "(slice (rec r data stb) 0:3)")
+
+ def test_wrong_field(self):
+ r = Record([
+ ("stb", 1),
+ ("ack", 1),
+ ])
+ with self.assertRaisesRegex(AttributeError,
+ r"^Record 'r' does not have a field 'en'\. Did you mean one of: stb, ack\?$"):
+ r["en"]
+ with self.assertRaisesRegex(AttributeError,
+ r"^Record 'r' does not have a field 'en'\. Did you mean one of: stb, ack\?$"):
+ r.en
+
+ def test_wrong_field_unnamed(self):
+ r = [Record([
+ ("stb", 1),
+ ("ack", 1),
+ ])][0]
+ with self.assertRaisesRegex(AttributeError,
+ r"^Unnamed record does not have a field 'en'\. Did you mean one of: stb, ack\?$"):
+ r.en
+
+ def test_construct_with_fields(self):
+ ns = Signal(1)
+ nr = Record([
+ ("burst", 1)
+ ])
+ r = Record([
+ ("stb", 1),
+ ("info", [
+ ("burst", 1)
+ ])
+ ], fields={
+ "stb": ns,
+ "info": nr
+ })
+ self.assertIs(r.stb, ns)
+ self.assertIs(r.info, nr)
+
+ def test_like(self):
+ r1 = Record([("a", 1), ("b", 2)])
+ r2 = Record.like(r1)
+ self.assertEqual(r1.layout, r2.layout)
+ self.assertEqual(r2.name, "r2")
+ r3 = Record.like(r1, name="foo")
+ self.assertEqual(r3.name, "foo")
+ r4 = Record.like(r1, name_suffix="foo")
+ self.assertEqual(r4.name, "r1foo")
+
+ def test_like_modifications(self):
+ r1 = Record([("a", 1), ("b", [("s", 1)])])
+ self.assertEqual(r1.a.name, "r1__a")
+ self.assertEqual(r1.b.name, "r1__b")
+ self.assertEqual(r1.b.s.name, "r1__b__s")
+ r1.a.reset = 1
+ r1.b.s.reset = 1
+ r2 = Record.like(r1)
+ self.assertEqual(r2.a.reset, 1)
+ self.assertEqual(r2.b.s.reset, 1)
+ self.assertEqual(r2.a.name, "r2__a")
+ self.assertEqual(r2.b.name, "r2__b")
+ self.assertEqual(r2.b.s.name, "r2__b__s")
+
+ def test_slice_tuple(self):
+ r1 = Record([("a", 1), ("b", 2), ("c", 3)])
+ r2 = r1["a", "c"]
+ self.assertEqual(r2.layout, Layout([("a", 1), ("c", 3)]))
+ self.assertIs(r2.a, r1.a)
+ self.assertIs(r2.c, r1.c)
+
+ def test_enum_decoder(self):
+ r1 = Record([("a", UnsignedEnum)])
+ self.assertEqual(r1.a.decoder(UnsignedEnum.FOO), "FOO/1")
+
+
+class ConnectTestCase(FHDLTestCase):
+ def setUp_flat(self):
+ self.core_layout = [
+ ("addr", 32, DIR_FANOUT),
+ ("data_r", 32, DIR_FANIN),
+ ("data_w", 32, DIR_FANIN),
+ ]
+ self.periph_layout = [
+ ("addr", 32, DIR_FANOUT),
+ ("data_r", 32, DIR_FANIN),
+ ("data_w", 32, DIR_FANIN),
+ ]
+
+ def setUp_nested(self):
+ self.core_layout = [
+ ("addr", 32, DIR_FANOUT),
+ ("data", [
+ ("r", 32, DIR_FANIN),
+ ("w", 32, DIR_FANIN),
+ ]),
+ ]
+ self.periph_layout = [
+ ("addr", 32, DIR_FANOUT),
+ ("data", [
+ ("r", 32, DIR_FANIN),
+ ("w", 32, DIR_FANIN),
+ ]),
+ ]
+
+ def test_flat(self):
+ self.setUp_flat()
+
+ core = Record(self.core_layout)
+ periph1 = Record(self.periph_layout)
+ periph2 = Record(self.periph_layout)
+
+ stmts = core.connect(periph1, periph2)
+ self.assertRepr(stmts, """(
+ (eq (sig periph1__addr) (sig core__addr))
+ (eq (sig periph2__addr) (sig core__addr))
+ (eq (sig core__data_r) (| (sig periph1__data_r) (sig periph2__data_r)))
+ (eq (sig core__data_w) (| (sig periph1__data_w) (sig periph2__data_w)))
+ )""")
+
+ def test_flat_include(self):
+ self.setUp_flat()
+
+ core = Record(self.core_layout)
+ periph1 = Record(self.periph_layout)
+ periph2 = Record(self.periph_layout)
+
+ stmts = core.connect(periph1, periph2, include={"addr": True})
+ self.assertRepr(stmts, """(
+ (eq (sig periph1__addr) (sig core__addr))
+ (eq (sig periph2__addr) (sig core__addr))
+ )""")
+
+ def test_flat_exclude(self):
+ self.setUp_flat()
+
+ core = Record(self.core_layout)
+ periph1 = Record(self.periph_layout)
+ periph2 = Record(self.periph_layout)
+
+ stmts = core.connect(periph1, periph2, exclude={"addr": True})
+ self.assertRepr(stmts, """(
+ (eq (sig core__data_r) (| (sig periph1__data_r) (sig periph2__data_r)))
+ (eq (sig core__data_w) (| (sig periph1__data_w) (sig periph2__data_w)))
+ )""")
+
+ def test_nested(self):
+ self.setUp_nested()
+
+ core = Record(self.core_layout)
+ periph1 = Record(self.periph_layout)
+ periph2 = Record(self.periph_layout)
+
+ stmts = core.connect(periph1, periph2)
+ self.maxDiff = None
+ self.assertRepr(stmts, """(
+ (eq (sig periph1__addr) (sig core__addr))
+ (eq (sig periph2__addr) (sig core__addr))
+ (eq (sig core__data__r) (| (sig periph1__data__r) (sig periph2__data__r)))
+ (eq (sig core__data__w) (| (sig periph1__data__w) (sig periph2__data__w)))
+ )""")
+
+ def test_wrong_include_exclude(self):
+ self.setUp_flat()
+
+ core = Record(self.core_layout)
+ periph = Record(self.periph_layout)
+
+ with self.assertRaisesRegex(AttributeError,
+ r"^Cannot include field 'foo' because it is not present in record 'core'$"):
+ core.connect(periph, include={"foo": True})
+
+ with self.assertRaisesRegex(AttributeError,
+ r"^Cannot exclude field 'foo' because it is not present in record 'core'$"):
+ core.connect(periph, exclude={"foo": True})
+
+ def test_wrong_direction(self):
+ recs = [Record([("x", 1)]) for _ in range(2)]
+
+ with self.assertRaisesRegex(TypeError,
+ (r"^Cannot connect field 'x' of unnamed record because it does not have "
+ r"a direction$")):
+ recs[0].connect(recs[1])
+
+ def test_wrong_missing_field(self):
+ core = Record([("addr", 32, DIR_FANOUT)])
+ periph = Record([])
+
+ with self.assertRaisesRegex(AttributeError,
+ (r"^Cannot connect field 'addr' of record 'core' to subordinate record 'periph' "
+ r"because the subordinate record does not have this field$")):
+ core.connect(periph)
--- /dev/null
+# nmigen: UnusedElaboratable=no
+
+from nmigen.hdl.ast import *
+from nmigen.hdl.cd import *
+from nmigen.hdl.ir import *
+from nmigen.hdl.xfrm import *
+from nmigen.hdl.mem import *
+
+from .utils import *
+
+
+class DomainRenamerTestCase(FHDLTestCase):
+ def setUp(self):
+ self.s1 = Signal()
+ self.s2 = Signal()
+ self.s3 = Signal()
+ self.s4 = Signal()
+ self.s5 = Signal()
+ self.c1 = Signal()
+
+ def test_rename_signals(self):
+ f = Fragment()
+ f.add_statements(
+ self.s1.eq(ClockSignal()),
+ ResetSignal().eq(self.s2),
+ self.s3.eq(0),
+ self.s4.eq(ClockSignal("other")),
+ self.s5.eq(ResetSignal("other")),
+ )
+ f.add_driver(self.s1, None)
+ f.add_driver(self.s2, None)
+ f.add_driver(self.s3, "sync")
+
+ f = DomainRenamer("pix")(f)
+ self.assertRepr(f.statements, """
+ (
+ (eq (sig s1) (clk pix))
+ (eq (rst pix) (sig s2))
+ (eq (sig s3) (const 1'd0))
+ (eq (sig s4) (clk other))
+ (eq (sig s5) (rst other))
+ )
+ """)
+ self.assertEqual(f.drivers, {
+ None: SignalSet((self.s1, self.s2)),
+ "pix": SignalSet((self.s3,)),
+ })
+
+ def test_rename_multi(self):
+ f = Fragment()
+ f.add_statements(
+ self.s1.eq(ClockSignal()),
+ self.s2.eq(ResetSignal("other")),
+ )
+
+ f = DomainRenamer({"sync": "pix", "other": "pix2"})(f)
+ self.assertRepr(f.statements, """
+ (
+ (eq (sig s1) (clk pix))
+ (eq (sig s2) (rst pix2))
+ )
+ """)
+
+ def test_rename_cd(self):
+ cd_sync = ClockDomain()
+ cd_pix = ClockDomain()
+
+ f = Fragment()
+ f.add_domains(cd_sync, cd_pix)
+
+ f = DomainRenamer("ext")(f)
+ self.assertEqual(cd_sync.name, "ext")
+ self.assertEqual(f.domains, {
+ "ext": cd_sync,
+ "pix": cd_pix,
+ })
+
+ def test_rename_cd_preserves_allow_reset_less(self):
+ cd_pix = ClockDomain(reset_less=True)
+
+ f = Fragment()
+ f.add_domains(cd_pix)
+ f.add_statements(
+ self.s1.eq(ResetSignal(allow_reset_less=True)),
+ )
+
+ f = DomainRenamer("pix")(f)
+ f = DomainLowerer()(f)
+ self.assertRepr(f.statements, """
+ (
+ (eq (sig s1) (const 1'd0))
+ )
+ """)
+
+
+ def test_rename_cd_subfragment(self):
+ cd_sync = ClockDomain()
+ cd_pix = ClockDomain()
+
+ f1 = Fragment()
+ f1.add_domains(cd_sync, cd_pix)
+ f2 = Fragment()
+ f2.add_domains(cd_sync)
+ f1.add_subfragment(f2)
+
+ f1 = DomainRenamer("ext")(f1)
+ self.assertEqual(cd_sync.name, "ext")
+ self.assertEqual(f1.domains, {
+ "ext": cd_sync,
+ "pix": cd_pix,
+ })
+
+ def test_rename_wrong_to_comb(self):
+ with self.assertRaisesRegex(ValueError,
+ r"^Domain 'sync' may not be renamed to 'comb'$"):
+ DomainRenamer("comb")
+
+ def test_rename_wrong_from_comb(self):
+ with self.assertRaisesRegex(ValueError,
+ r"^Domain 'comb' may not be renamed$"):
+ DomainRenamer({"comb": "sync"})
+
+
+class DomainLowererTestCase(FHDLTestCase):
+ def setUp(self):
+ self.s = Signal()
+
+ def test_lower_clk(self):
+ sync = ClockDomain()
+ f = Fragment()
+ f.add_domains(sync)
+ f.add_statements(
+ self.s.eq(ClockSignal("sync"))
+ )
+
+ f = DomainLowerer()(f)
+ self.assertRepr(f.statements, """
+ (
+ (eq (sig s) (sig clk))
+ )
+ """)
+
+ def test_lower_rst(self):
+ sync = ClockDomain()
+ f = Fragment()
+ f.add_domains(sync)
+ f.add_statements(
+ self.s.eq(ResetSignal("sync"))
+ )
+
+ f = DomainLowerer()(f)
+ self.assertRepr(f.statements, """
+ (
+ (eq (sig s) (sig rst))
+ )
+ """)
+
+ def test_lower_rst_reset_less(self):
+ sync = ClockDomain(reset_less=True)
+ f = Fragment()
+ f.add_domains(sync)
+ f.add_statements(
+ self.s.eq(ResetSignal("sync", allow_reset_less=True))
+ )
+
+ f = DomainLowerer()(f)
+ self.assertRepr(f.statements, """
+ (
+ (eq (sig s) (const 1'd0))
+ )
+ """)
+
+ def test_lower_drivers(self):
+ sync = ClockDomain()
+ pix = ClockDomain()
+ f = Fragment()
+ f.add_domains(sync, pix)
+ f.add_driver(ClockSignal("pix"), None)
+ f.add_driver(ResetSignal("pix"), "sync")
+
+ f = DomainLowerer()(f)
+ self.assertEqual(f.drivers, {
+ None: SignalSet((pix.clk,)),
+ "sync": SignalSet((pix.rst,))
+ })
+
+ def test_lower_wrong_domain(self):
+ f = Fragment()
+ f.add_statements(
+ self.s.eq(ClockSignal("xxx"))
+ )
+
+ with self.assertRaisesRegex(DomainError,
+ r"^Signal \(clk xxx\) refers to nonexistent domain 'xxx'$"):
+ DomainLowerer()(f)
+
+ def test_lower_wrong_reset_less_domain(self):
+ sync = ClockDomain(reset_less=True)
+ f = Fragment()
+ f.add_domains(sync)
+ f.add_statements(
+ self.s.eq(ResetSignal("sync"))
+ )
+
+ with self.assertRaisesRegex(DomainError,
+ r"^Signal \(rst sync\) refers to reset of reset-less domain 'sync'$"):
+ DomainLowerer()(f)
+
+
+class SampleLowererTestCase(FHDLTestCase):
+ def setUp(self):
+ self.i = Signal()
+ self.o1 = Signal()
+ self.o2 = Signal()
+ self.o3 = Signal()
+
+ def test_lower_signal(self):
+ f = Fragment()
+ f.add_statements(
+ self.o1.eq(Sample(self.i, 2, "sync")),
+ self.o2.eq(Sample(self.i, 1, "sync")),
+ self.o3.eq(Sample(self.i, 1, "pix")),
+ )
+
+ f = SampleLowerer()(f)
+ self.assertRepr(f.statements, """
+ (
+ (eq (sig o1) (sig $sample$s$i$sync$2))
+ (eq (sig o2) (sig $sample$s$i$sync$1))
+ (eq (sig o3) (sig $sample$s$i$pix$1))
+ (eq (sig $sample$s$i$sync$1) (sig i))
+ (eq (sig $sample$s$i$sync$2) (sig $sample$s$i$sync$1))
+ (eq (sig $sample$s$i$pix$1) (sig i))
+ )
+ """)
+ self.assertEqual(len(f.drivers["sync"]), 2)
+ self.assertEqual(len(f.drivers["pix"]), 1)
+
+ def test_lower_const(self):
+ f = Fragment()
+ f.add_statements(
+ self.o1.eq(Sample(1, 2, "sync")),
+ )
+
+ f = SampleLowerer()(f)
+ self.assertRepr(f.statements, """
+ (
+ (eq (sig o1) (sig $sample$c$1$sync$2))
+ (eq (sig $sample$c$1$sync$1) (const 1'd1))
+ (eq (sig $sample$c$1$sync$2) (sig $sample$c$1$sync$1))
+ )
+ """)
+ self.assertEqual(len(f.drivers["sync"]), 2)
+
+
+class SwitchCleanerTestCase(FHDLTestCase):
+ def test_clean(self):
+ a = Signal()
+ b = Signal()
+ c = Signal()
+ stmts = [
+ Switch(a, {
+ 1: a.eq(0),
+ 0: [
+ b.eq(1),
+ Switch(b, {1: [
+ Switch(a|b, {})
+ ]})
+ ]
+ })
+ ]
+
+ self.assertRepr(SwitchCleaner()(stmts), """
+ (
+ (switch (sig a)
+ (case 1
+ (eq (sig a) (const 1'd0)))
+ (case 0
+ (eq (sig b) (const 1'd1)))
+ )
+ )
+ """)
+
+
+class LHSGroupAnalyzerTestCase(FHDLTestCase):
+ def test_no_group_unrelated(self):
+ a = Signal()
+ b = Signal()
+ stmts = [
+ a.eq(0),
+ b.eq(0),
+ ]
+
+ groups = LHSGroupAnalyzer()(stmts)
+ self.assertEqual(list(groups.values()), [
+ SignalSet((a,)),
+ SignalSet((b,)),
+ ])
+
+ def test_group_related(self):
+ a = Signal()
+ b = Signal()
+ stmts = [
+ a.eq(0),
+ Cat(a, b).eq(0),
+ ]
+
+ groups = LHSGroupAnalyzer()(stmts)
+ self.assertEqual(list(groups.values()), [
+ SignalSet((a, b)),
+ ])
+
+ def test_no_loops(self):
+ a = Signal()
+ b = Signal()
+ stmts = [
+ a.eq(0),
+ Cat(a, b).eq(0),
+ Cat(a, b).eq(0),
+ ]
+
+ groups = LHSGroupAnalyzer()(stmts)
+ self.assertEqual(list(groups.values()), [
+ SignalSet((a, b)),
+ ])
+
+ def test_switch(self):
+ a = Signal()
+ b = Signal()
+ stmts = [
+ a.eq(0),
+ Switch(a, {
+ 1: b.eq(0),
+ })
+ ]
+
+ groups = LHSGroupAnalyzer()(stmts)
+ self.assertEqual(list(groups.values()), [
+ SignalSet((a,)),
+ SignalSet((b,)),
+ ])
+
+ def test_lhs_empty(self):
+ stmts = [
+ Cat().eq(0)
+ ]
+
+ groups = LHSGroupAnalyzer()(stmts)
+ self.assertEqual(list(groups.values()), [
+ ])
+
+
+class LHSGroupFilterTestCase(FHDLTestCase):
+ def test_filter(self):
+ a = Signal()
+ b = Signal()
+ c = Signal()
+ stmts = [
+ Switch(a, {
+ 1: a.eq(0),
+ 0: [
+ b.eq(1),
+ Switch(b, {1: []})
+ ]
+ })
+ ]
+
+ self.assertRepr(LHSGroupFilter(SignalSet((a,)))(stmts), """
+ (
+ (switch (sig a)
+ (case 1
+ (eq (sig a) (const 1'd0)))
+ (case 0 )
+ )
+ )
+ """)
+
+ def test_lhs_empty(self):
+ stmts = [
+ Cat().eq(0)
+ ]
+
+ self.assertRepr(LHSGroupFilter(SignalSet())(stmts), "()")
+
+
+class ResetInserterTestCase(FHDLTestCase):
+ def setUp(self):
+ self.s1 = Signal()
+ self.s2 = Signal(reset=1)
+ self.s3 = Signal(reset=1, reset_less=True)
+ self.c1 = Signal()
+
+ def test_reset_default(self):
+ f = Fragment()
+ f.add_statements(
+ self.s1.eq(1)
+ )
+ f.add_driver(self.s1, "sync")
+
+ f = ResetInserter(self.c1)(f)
+ self.assertRepr(f.statements, """
+ (
+ (eq (sig s1) (const 1'd1))
+ (switch (sig c1)
+ (case 1 (eq (sig s1) (const 1'd0)))
+ )
+ )
+ """)
+
+ def test_reset_cd(self):
+ f = Fragment()
+ f.add_statements(
+ self.s1.eq(1),
+ self.s2.eq(0),
+ )
+ f.add_domains(ClockDomain("sync"))
+ f.add_driver(self.s1, "sync")
+ f.add_driver(self.s2, "pix")
+
+ f = ResetInserter({"pix": self.c1})(f)
+ self.assertRepr(f.statements, """
+ (
+ (eq (sig s1) (const 1'd1))
+ (eq (sig s2) (const 1'd0))
+ (switch (sig c1)
+ (case 1 (eq (sig s2) (const 1'd1)))
+ )
+ )
+ """)
+
+ def test_reset_value(self):
+ f = Fragment()
+ f.add_statements(
+ self.s2.eq(0)
+ )
+ f.add_driver(self.s2, "sync")
+
+ f = ResetInserter(self.c1)(f)
+ self.assertRepr(f.statements, """
+ (
+ (eq (sig s2) (const 1'd0))
+ (switch (sig c1)
+ (case 1 (eq (sig s2) (const 1'd1)))
+ )
+ )
+ """)
+
+ def test_reset_less(self):
+ f = Fragment()
+ f.add_statements(
+ self.s3.eq(0)
+ )
+ f.add_driver(self.s3, "sync")
+
+ f = ResetInserter(self.c1)(f)
+ self.assertRepr(f.statements, """
+ (
+ (eq (sig s3) (const 1'd0))
+ (switch (sig c1)
+ (case 1 )
+ )
+ )
+ """)
+
+
+class EnableInserterTestCase(FHDLTestCase):
+ def setUp(self):
+ self.s1 = Signal()
+ self.s2 = Signal()
+ self.s3 = Signal()
+ self.c1 = Signal()
+
+ def test_enable_default(self):
+ f = Fragment()
+ f.add_statements(
+ self.s1.eq(1)
+ )
+ f.add_driver(self.s1, "sync")
+
+ f = EnableInserter(self.c1)(f)
+ self.assertRepr(f.statements, """
+ (
+ (eq (sig s1) (const 1'd1))
+ (switch (sig c1)
+ (case 0 (eq (sig s1) (sig s1)))
+ )
+ )
+ """)
+
+ def test_enable_cd(self):
+ f = Fragment()
+ f.add_statements(
+ self.s1.eq(1),
+ self.s2.eq(0),
+ )
+ f.add_driver(self.s1, "sync")
+ f.add_driver(self.s2, "pix")
+
+ f = EnableInserter({"pix": self.c1})(f)
+ self.assertRepr(f.statements, """
+ (
+ (eq (sig s1) (const 1'd1))
+ (eq (sig s2) (const 1'd0))
+ (switch (sig c1)
+ (case 0 (eq (sig s2) (sig s2)))
+ )
+ )
+ """)
+
+ def test_enable_subfragment(self):
+ f1 = Fragment()
+ f1.add_statements(
+ self.s1.eq(1)
+ )
+ f1.add_driver(self.s1, "sync")
+
+ f2 = Fragment()
+ f2.add_statements(
+ self.s2.eq(1)
+ )
+ f2.add_driver(self.s2, "sync")
+ f1.add_subfragment(f2)
+
+ f1 = EnableInserter(self.c1)(f1)
+ (f2, _), = f1.subfragments
+ self.assertRepr(f1.statements, """
+ (
+ (eq (sig s1) (const 1'd1))
+ (switch (sig c1)
+ (case 0 (eq (sig s1) (sig s1)))
+ )
+ )
+ """)
+ self.assertRepr(f2.statements, """
+ (
+ (eq (sig s2) (const 1'd1))
+ (switch (sig c1)
+ (case 0 (eq (sig s2) (sig s2)))
+ )
+ )
+ """)
+
+ def test_enable_read_port(self):
+ mem = Memory(width=8, depth=4)
+ f = EnableInserter(self.c1)(mem.read_port(transparent=False)).elaborate(platform=None)
+ self.assertRepr(f.named_ports["EN"][0], """
+ (m (sig c1) (sig mem_r_en) (const 1'd0))
+ """)
+
+ def test_enable_write_port(self):
+ mem = Memory(width=8, depth=4)
+ f = EnableInserter(self.c1)(mem.write_port()).elaborate(platform=None)
+ self.assertRepr(f.named_ports["EN"][0], """
+ (m (sig c1) (cat (repl (slice (sig mem_w_en) 0:1) 8)) (const 8'd0))
+ """)
+
+
+class _MockElaboratable(Elaboratable):
+ def __init__(self):
+ self.s1 = Signal()
+
+ def elaborate(self, platform):
+ f = Fragment()
+ f.add_statements(
+ self.s1.eq(1)
+ )
+ f.add_driver(self.s1, "sync")
+ return f
+
+
+class TransformedElaboratableTestCase(FHDLTestCase):
+ def setUp(self):
+ self.c1 = Signal()
+ self.c2 = Signal()
+
+ def test_getattr(self):
+ e = _MockElaboratable()
+ te = EnableInserter(self.c1)(e)
+
+ self.assertIs(te.s1, e.s1)
+
+ def test_composition(self):
+ e = _MockElaboratable()
+ te1 = EnableInserter(self.c1)(e)
+ te2 = ResetInserter(self.c2)(te1)
+
+ self.assertIsInstance(te1, TransformedElaboratable)
+ self.assertIs(te1, te2)
+
+ f = Fragment.get(te2, None)
+ self.assertRepr(f.statements, """
+ (
+ (eq (sig s1) (const 1'd1))
+ (switch (sig c1)
+ (case 0 (eq (sig s1) (sig s1)))
+ )
+ (switch (sig c2)
+ (case 1 (eq (sig s1) (const 1'd0)))
+ )
+ )
+ """)
+
+
+class MockUserValue(UserValue):
+ def __init__(self, lowered):
+ super().__init__()
+ self.lowered = lowered
+
+ def lower(self):
+ return self.lowered
+
+
+class UserValueTestCase(FHDLTestCase):
+ def setUp(self):
+ self.s = Signal()
+ self.c = Signal()
+ self.uv = MockUserValue(self.s)
+
+ def test_lower(self):
+ sync = ClockDomain()
+ f = Fragment()
+ f.add_domains(sync)
+ f.add_statements(
+ self.uv.eq(1)
+ )
+ for signal in self.uv._lhs_signals():
+ f.add_driver(signal, "sync")
+
+ f = ResetInserter(self.c)(f)
+ f = DomainLowerer()(f)
+ self.assertRepr(f.statements, """
+ (
+ (eq (sig s) (const 1'd1))
+ (switch (sig c)
+ (case 1 (eq (sig s) (const 1'd0)))
+ )
+ (switch (sig rst)
+ (case 1 (eq (sig s) (const 1'd0)))
+ )
+ )
+ """)
+
+
+class UserValueRecursiveTestCase(UserValueTestCase):
+ def setUp(self):
+ self.s = Signal()
+ self.c = Signal()
+ self.uv = MockUserValue(MockUserValue(self.s))
+
+ # inherit the test_lower method from UserValueTestCase because the checks are the same
--- /dev/null
+# nmigen: UnusedElaboratable=no
+
+from nmigen.hdl import *
+from nmigen.back.pysim import *
+from nmigen.lib.cdc import *
+
+from .utils import *
+
+
+class FFSynchronizerTestCase(FHDLTestCase):
+ def test_stages_wrong(self):
+ with self.assertRaisesRegex(TypeError,
+ r"^Synchronization stage count must be a positive integer, not 0$"):
+ FFSynchronizer(Signal(), Signal(), stages=0)
+ with self.assertRaisesRegex(ValueError,
+ r"^Synchronization stage count may not safely be less than 2$"):
+ FFSynchronizer(Signal(), Signal(), stages=1)
+
+ def test_basic(self):
+ i = Signal()
+ o = Signal()
+ frag = FFSynchronizer(i, o)
+
+ sim = Simulator(frag)
+ sim.add_clock(1e-6)
+ def process():
+ self.assertEqual((yield o), 0)
+ yield i.eq(1)
+ yield Tick()
+ self.assertEqual((yield o), 0)
+ yield Tick()
+ self.assertEqual((yield o), 0)
+ yield Tick()
+ self.assertEqual((yield o), 1)
+ sim.add_process(process)
+ sim.run()
+
+ def test_reset_value(self):
+ i = Signal(reset=1)
+ o = Signal()
+ frag = FFSynchronizer(i, o, reset=1)
+
+ sim = Simulator(frag)
+ sim.add_clock(1e-6)
+ def process():
+ self.assertEqual((yield o), 1)
+ yield i.eq(0)
+ yield Tick()
+ self.assertEqual((yield o), 1)
+ yield Tick()
+ self.assertEqual((yield o), 1)
+ yield Tick()
+ self.assertEqual((yield o), 0)
+ sim.add_process(process)
+ sim.run()
+
+
+class AsyncFFSynchronizerTestCase(FHDLTestCase):
+ def test_stages_wrong(self):
+ with self.assertRaisesRegex(TypeError,
+ r"^Synchronization stage count must be a positive integer, not 0$"):
+ ResetSynchronizer(Signal(), stages=0)
+ with self.assertRaisesRegex(ValueError,
+ r"^Synchronization stage count may not safely be less than 2$"):
+ ResetSynchronizer(Signal(), stages=1)
+
+ def test_edge_wrong(self):
+ with self.assertRaisesRegex(ValueError,
+ r"^AsyncFFSynchronizer async edge must be one of 'pos' or 'neg', not 'xxx'$"):
+ AsyncFFSynchronizer(Signal(), Signal(), o_domain="sync", async_edge="xxx")
+
+ def test_pos_edge(self):
+ i = Signal()
+ o = Signal()
+ m = Module()
+ m.domains += ClockDomain("sync")
+ m.submodules += AsyncFFSynchronizer(i, o)
+
+ sim = Simulator(m)
+ sim.add_clock(1e-6)
+ def process():
+ # initial reset
+ self.assertEqual((yield i), 0)
+ self.assertEqual((yield o), 1)
+ yield Tick(); yield Delay(1e-8)
+ self.assertEqual((yield o), 1)
+ yield Tick(); yield Delay(1e-8)
+ self.assertEqual((yield o), 0)
+ yield Tick(); yield Delay(1e-8)
+ self.assertEqual((yield o), 0)
+ yield Tick(); yield Delay(1e-8)
+
+ yield i.eq(1)
+ yield Delay(1e-8)
+ self.assertEqual((yield o), 1)
+ yield Tick(); yield Delay(1e-8)
+ self.assertEqual((yield o), 1)
+ yield i.eq(0)
+ yield Tick(); yield Delay(1e-8)
+ self.assertEqual((yield o), 1)
+ yield Tick(); yield Delay(1e-8)
+ self.assertEqual((yield o), 0)
+ yield Tick(); yield Delay(1e-8)
+ self.assertEqual((yield o), 0)
+ yield Tick(); yield Delay(1e-8)
+ sim.add_process(process)
+ with sim.write_vcd("test.vcd"):
+ sim.run()
+
+ def test_neg_edge(self):
+ i = Signal(reset=1)
+ o = Signal()
+ m = Module()
+ m.domains += ClockDomain("sync")
+ m.submodules += AsyncFFSynchronizer(i, o, async_edge="neg")
+
+ sim = Simulator(m)
+ sim.add_clock(1e-6)
+ def process():
+ # initial reset
+ self.assertEqual((yield i), 1)
+ self.assertEqual((yield o), 1)
+ yield Tick(); yield Delay(1e-8)
+ self.assertEqual((yield o), 1)
+ yield Tick(); yield Delay(1e-8)
+ self.assertEqual((yield o), 0)
+ yield Tick(); yield Delay(1e-8)
+ self.assertEqual((yield o), 0)
+ yield Tick(); yield Delay(1e-8)
+
+ yield i.eq(0)
+ yield Delay(1e-8)
+ self.assertEqual((yield o), 1)
+ yield Tick(); yield Delay(1e-8)
+ self.assertEqual((yield o), 1)
+ yield i.eq(1)
+ yield Tick(); yield Delay(1e-8)
+ self.assertEqual((yield o), 1)
+ yield Tick(); yield Delay(1e-8)
+ self.assertEqual((yield o), 0)
+ yield Tick(); yield Delay(1e-8)
+ self.assertEqual((yield o), 0)
+ yield Tick(); yield Delay(1e-8)
+ sim.add_process(process)
+ with sim.write_vcd("test.vcd"):
+ sim.run()
+
+
+class ResetSynchronizerTestCase(FHDLTestCase):
+ def test_stages_wrong(self):
+ with self.assertRaisesRegex(TypeError,
+ r"^Synchronization stage count must be a positive integer, not 0$"):
+ ResetSynchronizer(Signal(), stages=0)
+ with self.assertRaisesRegex(ValueError,
+ r"^Synchronization stage count may not safely be less than 2$"):
+ ResetSynchronizer(Signal(), stages=1)
+
+ def test_basic(self):
+ arst = Signal()
+ m = Module()
+ m.domains += ClockDomain("sync")
+ m.submodules += ResetSynchronizer(arst)
+ s = Signal(reset=1)
+ m.d.sync += s.eq(0)
+
+ sim = Simulator(m)
+ sim.add_clock(1e-6)
+ def process():
+ # initial reset
+ self.assertEqual((yield s), 1)
+ yield Tick(); yield Delay(1e-8)
+ self.assertEqual((yield s), 1)
+ yield Tick(); yield Delay(1e-8)
+ self.assertEqual((yield s), 1)
+ yield Tick(); yield Delay(1e-8)
+ self.assertEqual((yield s), 0)
+ yield Tick(); yield Delay(1e-8)
+
+ yield arst.eq(1)
+ yield Delay(1e-8)
+ self.assertEqual((yield s), 0)
+ yield Tick(); yield Delay(1e-8)
+ self.assertEqual((yield s), 1)
+ yield arst.eq(0)
+ yield Tick(); yield Delay(1e-8)
+ self.assertEqual((yield s), 1)
+ yield Tick(); yield Delay(1e-8)
+ self.assertEqual((yield s), 1)
+ yield Tick(); yield Delay(1e-8)
+ self.assertEqual((yield s), 0)
+ yield Tick(); yield Delay(1e-8)
+ sim.add_process(process)
+ with sim.write_vcd("test.vcd"):
+ sim.run()
+
+
+# TODO: test with distinct clocks
+class PulseSynchronizerTestCase(FHDLTestCase):
+ def test_stages_wrong(self):
+ with self.assertRaisesRegex(TypeError,
+ r"^Synchronization stage count must be a positive integer, not 0$"):
+ PulseSynchronizer("w", "r", stages=0)
+ with self.assertRaisesRegex(ValueError,
+ r"^Synchronization stage count may not safely be less than 2$"):
+ PulseSynchronizer("w", "r", stages=1)
+
+ def test_smoke(self):
+ m = Module()
+ m.domains += ClockDomain("sync")
+ ps = m.submodules.dut = PulseSynchronizer("sync", "sync")
+
+ sim = Simulator(m)
+ sim.add_clock(1e-6)
+ def process():
+ yield ps.i.eq(0)
+ # TODO: think about reset
+ for n in range(5):
+ yield Tick()
+ # Make sure no pulses are generated in quiescent state
+ for n in range(3):
+ yield Tick()
+ self.assertEqual((yield ps.o), 0)
+ # Check conservation of pulses
+ accum = 0
+ for n in range(10):
+ yield ps.i.eq(1 if n < 4 else 0)
+ yield Tick()
+ accum += yield ps.o
+ self.assertEqual(accum, 4)
+ sim.add_process(process)
+ sim.run()
--- /dev/null
+from nmigen.hdl import *
+from nmigen.asserts import *
+from nmigen.back.pysim import *
+from nmigen.lib.coding import *
+
+from .utils import *
+
+
+class EncoderTestCase(FHDLTestCase):
+ def test_basic(self):
+ enc = Encoder(4)
+ def process():
+ self.assertEqual((yield enc.n), 1)
+ self.assertEqual((yield enc.o), 0)
+
+ yield enc.i.eq(0b0001)
+ yield Settle()
+ self.assertEqual((yield enc.n), 0)
+ self.assertEqual((yield enc.o), 0)
+
+ yield enc.i.eq(0b0100)
+ yield Settle()
+ self.assertEqual((yield enc.n), 0)
+ self.assertEqual((yield enc.o), 2)
+
+ yield enc.i.eq(0b0110)
+ yield Settle()
+ self.assertEqual((yield enc.n), 1)
+ self.assertEqual((yield enc.o), 0)
+
+ sim = Simulator(enc)
+ sim.add_process(process)
+ sim.run()
+
+
+class PriorityEncoderTestCase(FHDLTestCase):
+ def test_basic(self):
+ enc = PriorityEncoder(4)
+ def process():
+ self.assertEqual((yield enc.n), 1)
+ self.assertEqual((yield enc.o), 0)
+
+ yield enc.i.eq(0b0001)
+ yield Settle()
+ self.assertEqual((yield enc.n), 0)
+ self.assertEqual((yield enc.o), 0)
+
+ yield enc.i.eq(0b0100)
+ yield Settle()
+ self.assertEqual((yield enc.n), 0)
+ self.assertEqual((yield enc.o), 2)
+
+ yield enc.i.eq(0b0110)
+ yield Settle()
+ self.assertEqual((yield enc.n), 0)
+ self.assertEqual((yield enc.o), 1)
+
+ sim = Simulator(enc)
+ sim.add_process(process)
+ sim.run()
+
+
+class DecoderTestCase(FHDLTestCase):
+ def test_basic(self):
+ dec = Decoder(4)
+ def process():
+ self.assertEqual((yield dec.o), 0b0001)
+
+ yield dec.i.eq(1)
+ yield Settle()
+ self.assertEqual((yield dec.o), 0b0010)
+
+ yield dec.i.eq(3)
+ yield Settle()
+ self.assertEqual((yield dec.o), 0b1000)
+
+ yield dec.n.eq(1)
+ yield Settle()
+ self.assertEqual((yield dec.o), 0b0000)
+
+ sim = Simulator(dec)
+ sim.add_process(process)
+ sim.run()
+
+
+class ReversibleSpec(Elaboratable):
+ def __init__(self, encoder_cls, decoder_cls, args):
+ self.encoder_cls = encoder_cls
+ self.decoder_cls = decoder_cls
+ self.coder_args = args
+
+ def elaborate(self, platform):
+ m = Module()
+ enc, dec = self.encoder_cls(*self.coder_args), self.decoder_cls(*self.coder_args)
+ m.submodules += enc, dec
+ m.d.comb += [
+ dec.i.eq(enc.o),
+ Assert(enc.i == dec.o)
+ ]
+ return m
+
+
+class HammingDistanceSpec(Elaboratable):
+ def __init__(self, distance, encoder_cls, args):
+ self.distance = distance
+ self.encoder_cls = encoder_cls
+ self.coder_args = args
+
+ def elaborate(self, platform):
+ m = Module()
+ enc1, enc2 = self.encoder_cls(*self.coder_args), self.encoder_cls(*self.coder_args)
+ m.submodules += enc1, enc2
+ m.d.comb += [
+ Assume(enc1.i + 1 == enc2.i),
+ Assert(sum(enc1.o ^ enc2.o) == self.distance)
+ ]
+ return m
+
+
+class GrayCoderTestCase(FHDLTestCase):
+ def test_reversible(self):
+ spec = ReversibleSpec(encoder_cls=GrayEncoder, decoder_cls=GrayDecoder, args=(16,))
+ self.assertFormal(spec, mode="prove")
+
+ def test_distance(self):
+ spec = HammingDistanceSpec(distance=1, encoder_cls=GrayEncoder, args=(16,))
+ self.assertFormal(spec, mode="prove")
--- /dev/null
+# nmigen: UnusedElaboratable=no
+
+from nmigen.hdl import *
+from nmigen.asserts import *
+from nmigen.back.pysim import *
+from nmigen.lib.fifo import *
+
+from .utils import *
+
+
+class FIFOTestCase(FHDLTestCase):
+ def test_depth_wrong(self):
+ with self.assertRaisesRegex(TypeError,
+ r"^FIFO width must be a non-negative integer, not -1$"):
+ FIFOInterface(width=-1, depth=8, fwft=True)
+ with self.assertRaisesRegex(TypeError,
+ r"^FIFO depth must be a non-negative integer, not -1$"):
+ FIFOInterface(width=8, depth=-1, fwft=True)
+
+ def test_sync_depth(self):
+ self.assertEqual(SyncFIFO(width=8, depth=0).depth, 0)
+ self.assertEqual(SyncFIFO(width=8, depth=1).depth, 1)
+ self.assertEqual(SyncFIFO(width=8, depth=2).depth, 2)
+
+ def test_sync_buffered_depth(self):
+ self.assertEqual(SyncFIFOBuffered(width=8, depth=0).depth, 0)
+ self.assertEqual(SyncFIFOBuffered(width=8, depth=1).depth, 1)
+ self.assertEqual(SyncFIFOBuffered(width=8, depth=2).depth, 2)
+
+ def test_async_depth(self):
+ self.assertEqual(AsyncFIFO(width=8, depth=0 ).depth, 0)
+ self.assertEqual(AsyncFIFO(width=8, depth=1 ).depth, 1)
+ self.assertEqual(AsyncFIFO(width=8, depth=2 ).depth, 2)
+ self.assertEqual(AsyncFIFO(width=8, depth=3 ).depth, 4)
+ self.assertEqual(AsyncFIFO(width=8, depth=4 ).depth, 4)
+ self.assertEqual(AsyncFIFO(width=8, depth=15).depth, 16)
+ self.assertEqual(AsyncFIFO(width=8, depth=16).depth, 16)
+ self.assertEqual(AsyncFIFO(width=8, depth=17).depth, 32)
+
+ def test_async_depth_wrong(self):
+ with self.assertRaisesRegex(ValueError,
+ (r"^AsyncFIFO only supports depths that are powers of 2; "
+ r"requested exact depth 15 is not$")):
+ AsyncFIFO(width=8, depth=15, exact_depth=True)
+
+ def test_async_buffered_depth(self):
+ self.assertEqual(AsyncFIFOBuffered(width=8, depth=0 ).depth, 0)
+ self.assertEqual(AsyncFIFOBuffered(width=8, depth=1 ).depth, 2)
+ self.assertEqual(AsyncFIFOBuffered(width=8, depth=2 ).depth, 2)
+ self.assertEqual(AsyncFIFOBuffered(width=8, depth=3 ).depth, 3)
+ self.assertEqual(AsyncFIFOBuffered(width=8, depth=4 ).depth, 5)
+ self.assertEqual(AsyncFIFOBuffered(width=8, depth=15).depth, 17)
+ self.assertEqual(AsyncFIFOBuffered(width=8, depth=16).depth, 17)
+ self.assertEqual(AsyncFIFOBuffered(width=8, depth=17).depth, 17)
+ self.assertEqual(AsyncFIFOBuffered(width=8, depth=18).depth, 33)
+
+ def test_async_buffered_depth_wrong(self):
+ with self.assertRaisesRegex(ValueError,
+ (r"^AsyncFIFOBuffered only supports depths that are one higher than powers of 2; "
+ r"requested exact depth 16 is not$")):
+ AsyncFIFOBuffered(width=8, depth=16, exact_depth=True)
+
+class FIFOModel(Elaboratable, FIFOInterface):
+ """
+ Non-synthesizable first-in first-out queue, implemented naively as a chain of registers.
+ """
+ def __init__(self, *, width, depth, fwft, r_domain, w_domain):
+ super().__init__(width=width, depth=depth, fwft=fwft)
+
+ self.r_domain = r_domain
+ self.w_domain = w_domain
+
+ self.level = Signal(range(self.depth + 1))
+ self.r_level = Signal(range(self.depth + 1))
+ self.w_level = Signal(range(self.depth + 1))
+
+ def elaborate(self, platform):
+ m = Module()
+
+ storage = Memory(width=self.width, depth=self.depth)
+ w_port = m.submodules.w_port = storage.write_port(domain=self.w_domain)
+ r_port = m.submodules.r_port = storage.read_port (domain="comb")
+
+ produce = Signal(range(self.depth))
+ consume = Signal(range(self.depth))
+
+ m.d.comb += self.r_rdy.eq(self.level > 0)
+ m.d.comb += r_port.addr.eq((consume + 1) % self.depth)
+ if self.fwft:
+ m.d.comb += self.r_data.eq(r_port.data)
+ with m.If(self.r_en & self.r_rdy):
+ if not self.fwft:
+ m.d[self.r_domain] += self.r_data.eq(r_port.data)
+ m.d[self.r_domain] += consume.eq(r_port.addr)
+
+ m.d.comb += self.w_rdy.eq(self.level < self.depth)
+ m.d.comb += w_port.data.eq(self.w_data)
+ with m.If(self.w_en & self.w_rdy):
+ m.d.comb += w_port.addr.eq((produce + 1) % self.depth)
+ m.d.comb += w_port.en.eq(1)
+ m.d[self.w_domain] += produce.eq(w_port.addr)
+
+ with m.If(ResetSignal(self.r_domain) | ResetSignal(self.w_domain)):
+ m.d.sync += self.level.eq(0)
+ with m.Else():
+ m.d.sync += self.level.eq(self.level
+ + (self.w_rdy & self.w_en)
+ - (self.r_rdy & self.r_en))
+
+ m.d.comb += [
+ self.r_level.eq(self.level),
+ self.w_level.eq(self.level),
+ ]
+ m.d.comb += Assert(ResetSignal(self.r_domain) == ResetSignal(self.w_domain))
+
+ return m
+
+
+class FIFOModelEquivalenceSpec(Elaboratable):
+ """
+ The first-in first-out queue model equivalence specification: for any inputs and control
+ signals, the behavior of the implementation under test exactly matches the ideal model,
+ except for behavior not defined by the model.
+ """
+ def __init__(self, fifo, r_domain, w_domain):
+ self.fifo = fifo
+
+ self.r_domain = r_domain
+ self.w_domain = w_domain
+
+ def elaborate(self, platform):
+ m = Module()
+ m.submodules.dut = dut = self.fifo
+ m.submodules.gold = gold = FIFOModel(width=dut.width, depth=dut.depth, fwft=dut.fwft,
+ r_domain=self.r_domain, w_domain=self.w_domain)
+
+ m.d.comb += [
+ gold.r_en.eq(dut.r_rdy & dut.r_en),
+ gold.w_en.eq(dut.w_en),
+ gold.w_data.eq(dut.w_data),
+ ]
+
+ m.d.comb += Assert(dut.r_rdy.implies(gold.r_rdy))
+ m.d.comb += Assert(dut.w_rdy.implies(gold.w_rdy))
+ m.d.comb += Assert(dut.r_level == gold.r_level)
+ m.d.comb += Assert(dut.w_level == gold.w_level)
+
+ if dut.fwft:
+ m.d.comb += Assert(dut.r_rdy
+ .implies(dut.r_data == gold.r_data))
+ else:
+ m.d.comb += Assert((Past(dut.r_rdy, domain=self.r_domain) &
+ Past(dut.r_en, domain=self.r_domain))
+ .implies(dut.r_data == gold.r_data))
+
+ return m
+
+
+class FIFOContractSpec(Elaboratable):
+ """
+ The first-in first-out queue contract specification: if two elements are written to the queue
+ consecutively, they must be read out consecutively at some later point, no matter all other
+ circumstances, with the exception of reset.
+ """
+ def __init__(self, fifo, *, r_domain, w_domain, bound):
+ self.fifo = fifo
+ self.r_domain = r_domain
+ self.w_domain = w_domain
+ self.bound = bound
+
+ def elaborate(self, platform):
+ m = Module()
+ m.submodules.dut = fifo = self.fifo
+
+ m.domains += ClockDomain("sync")
+ m.d.comb += ResetSignal().eq(0)
+ if self.w_domain != "sync":
+ m.domains += ClockDomain(self.w_domain)
+ m.d.comb += ResetSignal(self.w_domain).eq(0)
+ if self.r_domain != "sync":
+ m.domains += ClockDomain(self.r_domain)
+ m.d.comb += ResetSignal(self.r_domain).eq(0)
+
+ entry_1 = AnyConst(fifo.width)
+ entry_2 = AnyConst(fifo.width)
+
+ with m.FSM(domain=self.w_domain) as write_fsm:
+ with m.State("WRITE-1"):
+ with m.If(fifo.w_rdy):
+ m.d.comb += [
+ fifo.w_data.eq(entry_1),
+ fifo.w_en.eq(1)
+ ]
+ m.next = "WRITE-2"
+ with m.State("WRITE-2"):
+ with m.If(fifo.w_rdy):
+ m.d.comb += [
+ fifo.w_data.eq(entry_2),
+ fifo.w_en.eq(1)
+ ]
+ m.next = "DONE"
+ with m.State("DONE"):
+ pass
+
+ with m.FSM(domain=self.r_domain) as read_fsm:
+ read_1 = Signal(fifo.width)
+ read_2 = Signal(fifo.width)
+ with m.State("READ"):
+ m.d.comb += fifo.r_en.eq(1)
+ if fifo.fwft:
+ r_rdy = fifo.r_rdy
+ else:
+ r_rdy = Past(fifo.r_rdy, domain=self.r_domain)
+ with m.If(r_rdy):
+ m.d.sync += [
+ read_1.eq(read_2),
+ read_2.eq(fifo.r_data),
+ ]
+ with m.If((read_1 == entry_1) & (read_2 == entry_2)):
+ m.next = "DONE"
+ with m.State("DONE"):
+ pass
+
+ with m.If(Initial()):
+ m.d.comb += Assume(write_fsm.ongoing("WRITE-1"))
+ m.d.comb += Assume(read_fsm.ongoing("READ"))
+ with m.If(Past(Initial(), self.bound - 1)):
+ m.d.comb += Assert(read_fsm.ongoing("DONE"))
+
+ with m.If(ResetSignal(domain=self.w_domain)):
+ m.d.comb += Assert(~fifo.r_rdy)
+
+ if self.w_domain != "sync" or self.r_domain != "sync":
+ m.d.comb += Assume(Rose(ClockSignal(self.w_domain)) |
+ Rose(ClockSignal(self.r_domain)))
+
+ return m
+
+
+class FIFOFormalCase(FHDLTestCase):
+ def check_sync_fifo(self, fifo):
+ self.assertFormal(FIFOModelEquivalenceSpec(fifo, r_domain="sync", w_domain="sync"),
+ mode="bmc", depth=fifo.depth + 1)
+ self.assertFormal(FIFOContractSpec(fifo, r_domain="sync", w_domain="sync",
+ bound=fifo.depth * 2 + 1),
+ mode="hybrid", depth=fifo.depth * 2 + 1)
+
+ def test_sync_fwft_pot(self):
+ self.check_sync_fifo(SyncFIFO(width=8, depth=4, fwft=True))
+
+ def test_sync_fwft_npot(self):
+ self.check_sync_fifo(SyncFIFO(width=8, depth=5, fwft=True))
+
+ def test_sync_not_fwft_pot(self):
+ self.check_sync_fifo(SyncFIFO(width=8, depth=4, fwft=False))
+
+ def test_sync_not_fwft_npot(self):
+ self.check_sync_fifo(SyncFIFO(width=8, depth=5, fwft=False))
+
+ def test_sync_buffered_pot(self):
+ self.check_sync_fifo(SyncFIFOBuffered(width=8, depth=4))
+
+ def test_sync_buffered_potp1(self):
+ self.check_sync_fifo(SyncFIFOBuffered(width=8, depth=5))
+
+ def test_sync_buffered_potm1(self):
+ self.check_sync_fifo(SyncFIFOBuffered(width=8, depth=3))
+
+ def check_async_fifo(self, fifo):
+ # TODO: properly doing model equivalence checking on this likely requires multiclock,
+ # which is not really documented nor is it clear how to use it.
+ # self.assertFormal(FIFOModelEquivalenceSpec(fifo, r_domain="read", w_domain="write"),
+ # mode="bmc", depth=fifo.depth * 3 + 1)
+ self.assertFormal(FIFOContractSpec(fifo, r_domain="read", w_domain="write",
+ bound=fifo.depth * 4 + 1),
+ mode="hybrid", depth=fifo.depth * 4 + 1)
+
+ def test_async(self):
+ self.check_async_fifo(AsyncFIFO(width=8, depth=4))
+
+ def test_async_buffered(self):
+ self.check_async_fifo(AsyncFIFOBuffered(width=8, depth=4))
--- /dev/null
+from nmigen.hdl import *
+from nmigen.hdl.rec import *
+from nmigen.back.pysim import *
+from nmigen.lib.io import *
+
+from .utils import *
+
+
+class PinLayoutTestCase(FHDLTestCase):
+ def assertLayoutEqual(self, layout, expected):
+ casted_layout = {}
+ for name, (shape, dir) in layout.items():
+ casted_layout[name] = (Shape.cast(shape), dir)
+
+ self.assertEqual(casted_layout, expected)
+
+
+class PinLayoutCombTestCase(PinLayoutTestCase):
+ def test_pin_layout_i(self):
+ layout_1 = pin_layout(1, dir="i")
+ self.assertLayoutEqual(layout_1.fields, {
+ "i": ((1, False), DIR_NONE),
+ })
+
+ layout_2 = pin_layout(2, dir="i")
+ self.assertLayoutEqual(layout_2.fields, {
+ "i": ((2, False), DIR_NONE),
+ })
+
+ def test_pin_layout_o(self):
+ layout_1 = pin_layout(1, dir="o")
+ self.assertLayoutEqual(layout_1.fields, {
+ "o": ((1, False), DIR_NONE),
+ })
+
+ layout_2 = pin_layout(2, dir="o")
+ self.assertLayoutEqual(layout_2.fields, {
+ "o": ((2, False), DIR_NONE),
+ })
+
+ def test_pin_layout_oe(self):
+ layout_1 = pin_layout(1, dir="oe")
+ self.assertLayoutEqual(layout_1.fields, {
+ "o": ((1, False), DIR_NONE),
+ "oe": ((1, False), DIR_NONE),
+ })
+
+ layout_2 = pin_layout(2, dir="oe")
+ self.assertLayoutEqual(layout_2.fields, {
+ "o": ((2, False), DIR_NONE),
+ "oe": ((1, False), DIR_NONE),
+ })
+
+ def test_pin_layout_io(self):
+ layout_1 = pin_layout(1, dir="io")
+ self.assertLayoutEqual(layout_1.fields, {
+ "i": ((1, False), DIR_NONE),
+ "o": ((1, False), DIR_NONE),
+ "oe": ((1, False), DIR_NONE),
+ })
+
+ layout_2 = pin_layout(2, dir="io")
+ self.assertLayoutEqual(layout_2.fields, {
+ "i": ((2, False), DIR_NONE),
+ "o": ((2, False), DIR_NONE),
+ "oe": ((1, False), DIR_NONE),
+ })
+
+
+class PinLayoutSDRTestCase(PinLayoutTestCase):
+ def test_pin_layout_i(self):
+ layout_1 = pin_layout(1, dir="i", xdr=1)
+ self.assertLayoutEqual(layout_1.fields, {
+ "i_clk": ((1, False), DIR_NONE),
+ "i": ((1, False), DIR_NONE),
+ })
+
+ layout_2 = pin_layout(2, dir="i", xdr=1)
+ self.assertLayoutEqual(layout_2.fields, {
+ "i_clk": ((1, False), DIR_NONE),
+ "i": ((2, False), DIR_NONE),
+ })
+
+ def test_pin_layout_o(self):
+ layout_1 = pin_layout(1, dir="o", xdr=1)
+ self.assertLayoutEqual(layout_1.fields, {
+ "o_clk": ((1, False), DIR_NONE),
+ "o": ((1, False), DIR_NONE),
+ })
+
+ layout_2 = pin_layout(2, dir="o", xdr=1)
+ self.assertLayoutEqual(layout_2.fields, {
+ "o_clk": ((1, False), DIR_NONE),
+ "o": ((2, False), DIR_NONE),
+ })
+
+ def test_pin_layout_oe(self):
+ layout_1 = pin_layout(1, dir="oe", xdr=1)
+ self.assertLayoutEqual(layout_1.fields, {
+ "o_clk": ((1, False), DIR_NONE),
+ "o": ((1, False), DIR_NONE),
+ "oe": ((1, False), DIR_NONE),
+ })
+
+ layout_2 = pin_layout(2, dir="oe", xdr=1)
+ self.assertLayoutEqual(layout_2.fields, {
+ "o_clk": ((1, False), DIR_NONE),
+ "o": ((2, False), DIR_NONE),
+ "oe": ((1, False), DIR_NONE),
+ })
+
+ def test_pin_layout_io(self):
+ layout_1 = pin_layout(1, dir="io", xdr=1)
+ self.assertLayoutEqual(layout_1.fields, {
+ "i_clk": ((1, False), DIR_NONE),
+ "i": ((1, False), DIR_NONE),
+ "o_clk": ((1, False), DIR_NONE),
+ "o": ((1, False), DIR_NONE),
+ "oe": ((1, False), DIR_NONE),
+ })
+
+ layout_2 = pin_layout(2, dir="io", xdr=1)
+ self.assertLayoutEqual(layout_2.fields, {
+ "i_clk": ((1, False), DIR_NONE),
+ "i": ((2, False), DIR_NONE),
+ "o_clk": ((1, False), DIR_NONE),
+ "o": ((2, False), DIR_NONE),
+ "oe": ((1, False), DIR_NONE),
+ })
+
+
+class PinLayoutDDRTestCase(PinLayoutTestCase):
+ def test_pin_layout_i(self):
+ layout_1 = pin_layout(1, dir="i", xdr=2)
+ self.assertLayoutEqual(layout_1.fields, {
+ "i_clk": ((1, False), DIR_NONE),
+ "i0": ((1, False), DIR_NONE),
+ "i1": ((1, False), DIR_NONE),
+ })
+
+ layout_2 = pin_layout(2, dir="i", xdr=2)
+ self.assertLayoutEqual(layout_2.fields, {
+ "i_clk": ((1, False), DIR_NONE),
+ "i0": ((2, False), DIR_NONE),
+ "i1": ((2, False), DIR_NONE),
+ })
+
+ def test_pin_layout_o(self):
+ layout_1 = pin_layout(1, dir="o", xdr=2)
+ self.assertLayoutEqual(layout_1.fields, {
+ "o_clk": ((1, False), DIR_NONE),
+ "o0": ((1, False), DIR_NONE),
+ "o1": ((1, False), DIR_NONE),
+ })
+
+ layout_2 = pin_layout(2, dir="o", xdr=2)
+ self.assertLayoutEqual(layout_2.fields, {
+ "o_clk": ((1, False), DIR_NONE),
+ "o0": ((2, False), DIR_NONE),
+ "o1": ((2, False), DIR_NONE),
+ })
+
+ def test_pin_layout_oe(self):
+ layout_1 = pin_layout(1, dir="oe", xdr=2)
+ self.assertLayoutEqual(layout_1.fields, {
+ "o_clk": ((1, False), DIR_NONE),
+ "o0": ((1, False), DIR_NONE),
+ "o1": ((1, False), DIR_NONE),
+ "oe": ((1, False), DIR_NONE),
+ })
+
+ layout_2 = pin_layout(2, dir="oe", xdr=2)
+ self.assertLayoutEqual(layout_2.fields, {
+ "o_clk": ((1, False), DIR_NONE),
+ "o0": ((2, False), DIR_NONE),
+ "o1": ((2, False), DIR_NONE),
+ "oe": ((1, False), DIR_NONE),
+ })
+
+ def test_pin_layout_io(self):
+ layout_1 = pin_layout(1, dir="io", xdr=2)
+ self.assertLayoutEqual(layout_1.fields, {
+ "i_clk": ((1, False), DIR_NONE),
+ "i0": ((1, False), DIR_NONE),
+ "i1": ((1, False), DIR_NONE),
+ "o_clk": ((1, False), DIR_NONE),
+ "o0": ((1, False), DIR_NONE),
+ "o1": ((1, False), DIR_NONE),
+ "oe": ((1, False), DIR_NONE),
+ })
+
+ layout_2 = pin_layout(2, dir="io", xdr=2)
+ self.assertLayoutEqual(layout_2.fields, {
+ "i_clk": ((1, False), DIR_NONE),
+ "i0": ((2, False), DIR_NONE),
+ "i1": ((2, False), DIR_NONE),
+ "o_clk": ((1, False), DIR_NONE),
+ "o0": ((2, False), DIR_NONE),
+ "o1": ((2, False), DIR_NONE),
+ "oe": ((1, False), DIR_NONE),
+ })
+
+
+class PinTestCase(FHDLTestCase):
+ def test_attributes(self):
+ pin = Pin(2, dir="io", xdr=2)
+ self.assertEqual(pin.width, 2)
+ self.assertEqual(pin.dir, "io")
+ self.assertEqual(pin.xdr, 2)
--- /dev/null
+# nmigen: UnusedElaboratable=no
+
+import unittest
+
+from nmigen.hdl import *
+from nmigen.asserts import *
+from nmigen.sim.pysim import *
+from nmigen.lib.scheduler import *
+
+from .utils import *
+
+
+class RoundRobinTestCase(unittest.TestCase):
+ def test_count(self):
+ dut = RoundRobin(count=32)
+ self.assertEqual(dut.count, 32)
+ self.assertEqual(len(dut.requests), 32)
+ self.assertEqual(len(dut.grant), 5)
+
+ def test_wrong_count(self):
+ with self.assertRaisesRegex(ValueError, r"Count must be a non-negative integer, not 'foo'"):
+ dut = RoundRobin(count="foo")
+ with self.assertRaisesRegex(ValueError, r"Count must be a non-negative integer, not -1"):
+ dut = RoundRobin(count=-1)
+
+
+class RoundRobinSimulationTestCase(unittest.TestCase):
+ def test_count_one(self):
+ dut = RoundRobin(count=1)
+ sim = Simulator(dut)
+ def process():
+ yield dut.requests.eq(0)
+ yield; yield Delay(1e-8)
+ self.assertEqual((yield dut.grant), 0)
+ self.assertFalse((yield dut.valid))
+
+ yield dut.requests.eq(1)
+ yield; yield Delay(1e-8)
+ self.assertEqual((yield dut.grant), 0)
+ self.assertTrue((yield dut.valid))
+ sim.add_sync_process(process)
+ sim.add_clock(1e-6)
+ with sim.write_vcd("test.vcd"):
+ sim.run()
+
+ def test_transitions(self):
+ dut = RoundRobin(count=3)
+ sim = Simulator(dut)
+ def process():
+ yield dut.requests.eq(0b111)
+ yield; yield Delay(1e-8)
+ self.assertEqual((yield dut.grant), 1)
+ self.assertTrue((yield dut.valid))
+
+ yield dut.requests.eq(0b110)
+ yield; yield Delay(1e-8)
+ self.assertEqual((yield dut.grant), 2)
+ self.assertTrue((yield dut.valid))
+
+ yield dut.requests.eq(0b010)
+ yield; yield Delay(1e-8)
+ self.assertEqual((yield dut.grant), 1)
+ self.assertTrue((yield dut.valid))
+
+ yield dut.requests.eq(0b011)
+ yield; yield Delay(1e-8)
+ self.assertEqual((yield dut.grant), 0)
+ self.assertTrue((yield dut.valid))
+
+ yield dut.requests.eq(0b001)
+ yield; yield Delay(1e-8)
+ self.assertEqual((yield dut.grant), 0)
+ self.assertTrue((yield dut.valid))
+
+ yield dut.requests.eq(0b101)
+ yield; yield Delay(1e-8)
+ self.assertEqual((yield dut.grant), 2)
+ self.assertTrue((yield dut.valid))
+
+ yield dut.requests.eq(0b100)
+ yield; yield Delay(1e-8)
+ self.assertEqual((yield dut.grant), 2)
+ self.assertTrue((yield dut.valid))
+
+ yield dut.requests.eq(0b000)
+ yield; yield Delay(1e-8)
+ self.assertFalse((yield dut.valid))
+
+ yield dut.requests.eq(0b001)
+ yield; yield Delay(1e-8)
+ self.assertEqual((yield dut.grant), 0)
+ self.assertTrue((yield dut.valid))
+ sim.add_sync_process(process)
+ sim.add_clock(1e-6)
+ with sim.write_vcd("test.vcd"):
+ sim.run()
--- /dev/null
+import os
+from contextlib import contextmanager
+
+from nmigen._utils import flatten, union
+from nmigen.hdl.ast import *
+from nmigen.hdl.cd import *
+from nmigen.hdl.mem import *
+from nmigen.hdl.rec import *
+from nmigen.hdl.dsl import *
+from nmigen.hdl.ir import *
+from nmigen.back.pysim import *
+
+from .utils import *
+
+
+class SimulatorUnitTestCase(FHDLTestCase):
+ def assertStatement(self, stmt, inputs, output, reset=0):
+ inputs = [Value.cast(i) for i in inputs]
+ output = Value.cast(output)
+
+ isigs = [Signal(i.shape(), name=n) for i, n in zip(inputs, "abcd")]
+ osig = Signal(output.shape(), name="y", reset=reset)
+
+ stmt = stmt(osig, *isigs)
+ frag = Fragment()
+ frag.add_statements(stmt)
+ for signal in flatten(s._lhs_signals() for s in Statement.cast(stmt)):
+ frag.add_driver(signal)
+
+ sim = Simulator(frag)
+ def process():
+ for isig, input in zip(isigs, inputs):
+ yield isig.eq(input)
+ yield Settle()
+ self.assertEqual((yield osig), output.value)
+ sim.add_process(process)
+ with sim.write_vcd("test.vcd", "test.gtkw", traces=[*isigs, osig]):
+ sim.run()
+
+ def test_invert(self):
+ stmt = lambda y, a: y.eq(~a)
+ self.assertStatement(stmt, [C(0b0000, 4)], C(0b1111, 4))
+ self.assertStatement(stmt, [C(0b1010, 4)], C(0b0101, 4))
+ self.assertStatement(stmt, [C(0, 4)], C(-1, 4))
+
+ def test_neg(self):
+ stmt = lambda y, a: y.eq(-a)
+ self.assertStatement(stmt, [C(0b0000, 4)], C(0b0000, 4))
+ self.assertStatement(stmt, [C(0b0001, 4)], C(0b1111, 4))
+ self.assertStatement(stmt, [C(0b1010, 4)], C(0b0110, 4))
+ self.assertStatement(stmt, [C(1, 4)], C(-1, 4))
+ self.assertStatement(stmt, [C(5, 4)], C(-5, 4))
+
+ def test_bool(self):
+ stmt = lambda y, a: y.eq(a.bool())
+ self.assertStatement(stmt, [C(0, 4)], C(0))
+ self.assertStatement(stmt, [C(1, 4)], C(1))
+ self.assertStatement(stmt, [C(2, 4)], C(1))
+
+ def test_as_unsigned(self):
+ stmt = lambda y, a, b: y.eq(a.as_unsigned() == b)
+ self.assertStatement(stmt, [C(0b01, signed(2)), C(0b0001, unsigned(4))], C(1))
+ self.assertStatement(stmt, [C(0b11, signed(2)), C(0b0011, unsigned(4))], C(1))
+
+ def test_as_signed(self):
+ stmt = lambda y, a, b: y.eq(a.as_signed() == b)
+ self.assertStatement(stmt, [C(0b01, unsigned(2)), C(0b0001, signed(4))], C(1))
+ self.assertStatement(stmt, [C(0b11, unsigned(2)), C(0b1111, signed(4))], C(1))
+
+ def test_any(self):
+ stmt = lambda y, a: y.eq(a.any())
+ self.assertStatement(stmt, [C(0b00, 2)], C(0))
+ self.assertStatement(stmt, [C(0b01, 2)], C(1))
+ self.assertStatement(stmt, [C(0b10, 2)], C(1))
+ self.assertStatement(stmt, [C(0b11, 2)], C(1))
+
+ def test_all(self):
+ stmt = lambda y, a: y.eq(a.all())
+ self.assertStatement(stmt, [C(0b00, 2)], C(0))
+ self.assertStatement(stmt, [C(0b01, 2)], C(0))
+ self.assertStatement(stmt, [C(0b10, 2)], C(0))
+ self.assertStatement(stmt, [C(0b11, 2)], C(1))
+
+ def test_xor_unary(self):
+ stmt = lambda y, a: y.eq(a.xor())
+ self.assertStatement(stmt, [C(0b00, 2)], C(0))
+ self.assertStatement(stmt, [C(0b01, 2)], C(1))
+ self.assertStatement(stmt, [C(0b10, 2)], C(1))
+ self.assertStatement(stmt, [C(0b11, 2)], C(0))
+
+ def test_add(self):
+ stmt = lambda y, a, b: y.eq(a + b)
+ self.assertStatement(stmt, [C(0, 4), C(1, 4)], C(1, 4))
+ self.assertStatement(stmt, [C(-5, 4), C(-5, 4)], C(-10, 5))
+
+ def test_sub(self):
+ stmt = lambda y, a, b: y.eq(a - b)
+ self.assertStatement(stmt, [C(2, 4), C(1, 4)], C(1, 4))
+ self.assertStatement(stmt, [C(0, 4), C(1, 4)], C(-1, 4))
+ self.assertStatement(stmt, [C(0, 4), C(10, 4)], C(-10, 5))
+
+ def test_mul(self):
+ stmt = lambda y, a, b: y.eq(a * b)
+ self.assertStatement(stmt, [C(2, 4), C(1, 4)], C(2, 8))
+ self.assertStatement(stmt, [C(2, 4), C(2, 4)], C(4, 8))
+ self.assertStatement(stmt, [C(7, 4), C(7, 4)], C(49, 8))
+
+ def test_floordiv(self):
+ stmt = lambda y, a, b: y.eq(a // b)
+ self.assertStatement(stmt, [C(2, 4), C(1, 4)], C(2, 8))
+ self.assertStatement(stmt, [C(2, 4), C(2, 4)], C(1, 8))
+ self.assertStatement(stmt, [C(7, 4), C(2, 4)], C(3, 8))
+
+ def test_mod(self):
+ stmt = lambda y, a, b: y.eq(a % b)
+ self.assertStatement(stmt, [C(2, 4), C(0, 4)], C(0, 8))
+ self.assertStatement(stmt, [C(2, 4), C(1, 4)], C(0, 8))
+ self.assertStatement(stmt, [C(2, 4), C(2, 4)], C(0, 8))
+ self.assertStatement(stmt, [C(7, 4), C(2, 4)], C(1, 8))
+
+ def test_and(self):
+ stmt = lambda y, a, b: y.eq(a & b)
+ self.assertStatement(stmt, [C(0b1100, 4), C(0b1010, 4)], C(0b1000, 4))
+
+ def test_or(self):
+ stmt = lambda y, a, b: y.eq(a | b)
+ self.assertStatement(stmt, [C(0b1100, 4), C(0b1010, 4)], C(0b1110, 4))
+
+ def test_xor_binary(self):
+ stmt = lambda y, a, b: y.eq(a ^ b)
+ self.assertStatement(stmt, [C(0b1100, 4), C(0b1010, 4)], C(0b0110, 4))
+
+ def test_shl(self):
+ stmt = lambda y, a, b: y.eq(a << b)
+ self.assertStatement(stmt, [C(0b1001, 4), C(0)], C(0b1001, 5))
+ self.assertStatement(stmt, [C(0b1001, 4), C(3)], C(0b1001000, 7))
+
+ def test_shr(self):
+ stmt = lambda y, a, b: y.eq(a >> b)
+ self.assertStatement(stmt, [C(0b1001, 4), C(0)], C(0b1001, 4))
+ self.assertStatement(stmt, [C(0b1001, 4), C(2)], C(0b10, 4))
+
+ def test_eq(self):
+ stmt = lambda y, a, b: y.eq(a == b)
+ self.assertStatement(stmt, [C(0, 4), C(0, 4)], C(1))
+ self.assertStatement(stmt, [C(0, 4), C(1, 4)], C(0))
+ self.assertStatement(stmt, [C(1, 4), C(0, 4)], C(0))
+
+ def test_ne(self):
+ stmt = lambda y, a, b: y.eq(a != b)
+ self.assertStatement(stmt, [C(0, 4), C(0, 4)], C(0))
+ self.assertStatement(stmt, [C(0, 4), C(1, 4)], C(1))
+ self.assertStatement(stmt, [C(1, 4), C(0, 4)], C(1))
+
+ def test_lt(self):
+ stmt = lambda y, a, b: y.eq(a < b)
+ self.assertStatement(stmt, [C(0, 4), C(0, 4)], C(0))
+ self.assertStatement(stmt, [C(0, 4), C(1, 4)], C(1))
+ self.assertStatement(stmt, [C(1, 4), C(0, 4)], C(0))
+
+ def test_ge(self):
+ stmt = lambda y, a, b: y.eq(a >= b)
+ self.assertStatement(stmt, [C(0, 4), C(0, 4)], C(1))
+ self.assertStatement(stmt, [C(0, 4), C(1, 4)], C(0))
+ self.assertStatement(stmt, [C(1, 4), C(0, 4)], C(1))
+
+ def test_gt(self):
+ stmt = lambda y, a, b: y.eq(a > b)
+ self.assertStatement(stmt, [C(0, 4), C(0, 4)], C(0))
+ self.assertStatement(stmt, [C(0, 4), C(1, 4)], C(0))
+ self.assertStatement(stmt, [C(1, 4), C(0, 4)], C(1))
+
+ def test_le(self):
+ stmt = lambda y, a, b: y.eq(a <= b)
+ self.assertStatement(stmt, [C(0, 4), C(0, 4)], C(1))
+ self.assertStatement(stmt, [C(0, 4), C(1, 4)], C(1))
+ self.assertStatement(stmt, [C(1, 4), C(0, 4)], C(0))
+
+ def test_mux(self):
+ stmt = lambda y, a, b, c: y.eq(Mux(c, a, b))
+ self.assertStatement(stmt, [C(2, 4), C(3, 4), C(0)], C(3, 4))
+ self.assertStatement(stmt, [C(2, 4), C(3, 4), C(1)], C(2, 4))
+
+ def test_abs(self):
+ stmt = lambda y, a: y.eq(abs(a))
+ self.assertStatement(stmt, [C(3, unsigned(8))], C(3, unsigned(8)))
+ self.assertStatement(stmt, [C(-3, unsigned(8))], C(-3, unsigned(8)))
+ self.assertStatement(stmt, [C(3, signed(8))], C(3, signed(8)))
+ self.assertStatement(stmt, [C(-3, signed(8))], C(3, signed(8)))
+
+ def test_slice(self):
+ stmt1 = lambda y, a: y.eq(a[2])
+ self.assertStatement(stmt1, [C(0b10110100, 8)], C(0b1, 1))
+ stmt2 = lambda y, a: y.eq(a[2:4])
+ self.assertStatement(stmt2, [C(0b10110100, 8)], C(0b01, 2))
+
+ def test_slice_lhs(self):
+ stmt1 = lambda y, a: y[2].eq(a)
+ self.assertStatement(stmt1, [C(0b0, 1)], C(0b11111011, 8), reset=0b11111111)
+ stmt2 = lambda y, a: y[2:4].eq(a)
+ self.assertStatement(stmt2, [C(0b01, 2)], C(0b11110111, 8), reset=0b11111011)
+
+ def test_bit_select(self):
+ stmt = lambda y, a, b: y.eq(a.bit_select(b, 3))
+ self.assertStatement(stmt, [C(0b10110100, 8), C(0)], C(0b100, 3))
+ self.assertStatement(stmt, [C(0b10110100, 8), C(2)], C(0b101, 3))
+ self.assertStatement(stmt, [C(0b10110100, 8), C(3)], C(0b110, 3))
+
+ def test_bit_select_lhs(self):
+ stmt = lambda y, a, b: y.bit_select(a, 3).eq(b)
+ self.assertStatement(stmt, [C(0), C(0b100, 3)], C(0b11111100, 8), reset=0b11111111)
+ self.assertStatement(stmt, [C(2), C(0b101, 3)], C(0b11110111, 8), reset=0b11111111)
+ self.assertStatement(stmt, [C(3), C(0b110, 3)], C(0b11110111, 8), reset=0b11111111)
+
+ def test_word_select(self):
+ stmt = lambda y, a, b: y.eq(a.word_select(b, 3))
+ self.assertStatement(stmt, [C(0b10110100, 8), C(0)], C(0b100, 3))
+ self.assertStatement(stmt, [C(0b10110100, 8), C(1)], C(0b110, 3))
+ self.assertStatement(stmt, [C(0b10110100, 8), C(2)], C(0b010, 3))
+
+ def test_word_select_lhs(self):
+ stmt = lambda y, a, b: y.word_select(a, 3).eq(b)
+ self.assertStatement(stmt, [C(0), C(0b100, 3)], C(0b11111100, 8), reset=0b11111111)
+ self.assertStatement(stmt, [C(1), C(0b101, 3)], C(0b11101111, 8), reset=0b11111111)
+ self.assertStatement(stmt, [C(2), C(0b110, 3)], C(0b10111111, 8), reset=0b11111111)
+
+ def test_cat(self):
+ stmt = lambda y, *xs: y.eq(Cat(*xs))
+ self.assertStatement(stmt, [C(0b10, 2), C(0b01, 2)], C(0b0110, 4))
+
+ def test_cat_lhs(self):
+ l = Signal(3)
+ m = Signal(3)
+ n = Signal(3)
+ stmt = lambda y, a: [Cat(l, m, n).eq(a), y.eq(Cat(n, m, l))]
+ self.assertStatement(stmt, [C(0b100101110, 9)], C(0b110101100, 9))
+
+ def test_nested_cat_lhs(self):
+ l = Signal(3)
+ m = Signal(3)
+ n = Signal(3)
+ stmt = lambda y, a: [Cat(Cat(l, Cat(m)), n).eq(a), y.eq(Cat(n, m, l))]
+ self.assertStatement(stmt, [C(0b100101110, 9)], C(0b110101100, 9))
+
+ def test_record(self):
+ rec = Record([
+ ("l", 1),
+ ("m", 2),
+ ])
+ stmt = lambda y, a: [rec.eq(a), y.eq(rec)]
+ self.assertStatement(stmt, [C(0b101, 3)], C(0b101, 3))
+
+ def test_repl(self):
+ stmt = lambda y, a: y.eq(Repl(a, 3))
+ self.assertStatement(stmt, [C(0b10, 2)], C(0b101010, 6))
+
+ def test_array(self):
+ array = Array([1, 4, 10])
+ stmt = lambda y, a: y.eq(array[a])
+ self.assertStatement(stmt, [C(0)], C(1))
+ self.assertStatement(stmt, [C(1)], C(4))
+ self.assertStatement(stmt, [C(2)], C(10))
+
+ def test_array_oob(self):
+ array = Array([1, 4, 10])
+ stmt = lambda y, a: y.eq(array[a])
+ self.assertStatement(stmt, [C(3)], C(10))
+ self.assertStatement(stmt, [C(4)], C(10))
+
+ def test_array_lhs(self):
+ l = Signal(3, reset=1)
+ m = Signal(3, reset=4)
+ n = Signal(3, reset=7)
+ array = Array([l, m, n])
+ stmt = lambda y, a, b: [array[a].eq(b), y.eq(Cat(*array))]
+ self.assertStatement(stmt, [C(0), C(0b000)], C(0b111100000))
+ self.assertStatement(stmt, [C(1), C(0b010)], C(0b111010001))
+ self.assertStatement(stmt, [C(2), C(0b100)], C(0b100100001))
+
+ def test_array_lhs_oob(self):
+ l = Signal(3)
+ m = Signal(3)
+ n = Signal(3)
+ array = Array([l, m, n])
+ stmt = lambda y, a, b: [array[a].eq(b), y.eq(Cat(*array))]
+ self.assertStatement(stmt, [C(3), C(0b001)], C(0b001000000))
+ self.assertStatement(stmt, [C(4), C(0b010)], C(0b010000000))
+
+ def test_array_index(self):
+ array = Array(Array(x * y for y in range(10)) for x in range(10))
+ stmt = lambda y, a, b: y.eq(array[a][b])
+ for x in range(10):
+ for y in range(10):
+ self.assertStatement(stmt, [C(x), C(y)], C(x * y))
+
+ def test_array_attr(self):
+ from collections import namedtuple
+ pair = namedtuple("pair", ("p", "n"))
+
+ array = Array(pair(x, -x) for x in range(10))
+ stmt = lambda y, a: y.eq(array[a].p + array[a].n)
+ for i in range(10):
+ self.assertStatement(stmt, [C(i)], C(0))
+
+ def test_shift_left(self):
+ stmt1 = lambda y, a: y.eq(a.shift_left(1))
+ self.assertStatement(stmt1, [C(0b10100010, 8)], C( 0b101000100, 9))
+ stmt2 = lambda y, a: y.eq(a.shift_left(4))
+ self.assertStatement(stmt2, [C(0b10100010, 8)], C(0b101000100000, 12))
+
+ def test_shift_right(self):
+ stmt1 = lambda y, a: y.eq(a.shift_right(1))
+ self.assertStatement(stmt1, [C(0b10100010, 8)], C(0b1010001, 7))
+ stmt2 = lambda y, a: y.eq(a.shift_right(4))
+ self.assertStatement(stmt2, [C(0b10100010, 8)], C( 0b1010, 4))
+
+ def test_rotate_left(self):
+ stmt = lambda y, a: y.eq(a.rotate_left(1))
+ self.assertStatement(stmt, [C(0b1)], C(0b1))
+ self.assertStatement(stmt, [C(0b1001000)], C(0b0010001))
+ stmt = lambda y, a: y.eq(a.rotate_left(5))
+ self.assertStatement(stmt, [C(0b1000000)], C(0b0010000))
+ self.assertStatement(stmt, [C(0b1000001)], C(0b0110000))
+ stmt = lambda y, a: y.eq(a.rotate_left(7))
+ self.assertStatement(stmt, [C(0b1000000)], C(0b1000000))
+ self.assertStatement(stmt, [C(0b1000001)], C(0b1000001))
+ stmt = lambda y, a: y.eq(a.rotate_left(9))
+ self.assertStatement(stmt, [C(0b1000000)], C(0b0000010))
+ self.assertStatement(stmt, [C(0b1000001)], C(0b0000110))
+ stmt = lambda y, a: y.eq(a.rotate_left(-1))
+ self.assertStatement(stmt, [C(0b1)], C(0b1))
+ self.assertStatement(stmt, [C(0b1001000)], C(0b0100100))
+ stmt = lambda y, a: y.eq(a.rotate_left(-5))
+ self.assertStatement(stmt, [C(0b1000000)], C(0b0000010))
+ self.assertStatement(stmt, [C(0b1000001)], C(0b0000110))
+ stmt = lambda y, a: y.eq(a.rotate_left(-7))
+ self.assertStatement(stmt, [C(0b1000000)], C(0b1000000))
+ self.assertStatement(stmt, [C(0b1000001)], C(0b1000001))
+ stmt = lambda y, a: y.eq(a.rotate_left(-9))
+ self.assertStatement(stmt, [C(0b1000000)], C(0b0010000))
+ self.assertStatement(stmt, [C(0b1000001)], C(0b0110000))
+
+ def test_rotate_right(self):
+ stmt = lambda y, a: y.eq(a.rotate_right(1))
+ self.assertStatement(stmt, [C(0b1)], C(0b1))
+ self.assertStatement(stmt, [C(0b1001000)], C(0b0100100))
+ stmt = lambda y, a: y.eq(a.rotate_right(5))
+ self.assertStatement(stmt, [C(0b1000000)], C(0b0000010))
+ self.assertStatement(stmt, [C(0b1000001)], C(0b0000110))
+ stmt = lambda y, a: y.eq(a.rotate_right(7))
+ self.assertStatement(stmt, [C(0b1000000)], C(0b1000000))
+ self.assertStatement(stmt, [C(0b1000001)], C(0b1000001))
+ stmt = lambda y, a: y.eq(a.rotate_right(9))
+ self.assertStatement(stmt, [C(0b1000000)], C(0b0010000))
+ self.assertStatement(stmt, [C(0b1000001)], C(0b0110000))
+ stmt = lambda y, a: y.eq(a.rotate_right(-1))
+ self.assertStatement(stmt, [C(0b1)], C(0b1))
+ self.assertStatement(stmt, [C(0b1001000)], C(0b0010001))
+ stmt = lambda y, a: y.eq(a.rotate_right(-5))
+ self.assertStatement(stmt, [C(0b1000000)], C(0b0010000))
+ self.assertStatement(stmt, [C(0b1000001)], C(0b0110000))
+ stmt = lambda y, a: y.eq(a.rotate_right(-7))
+ self.assertStatement(stmt, [C(0b1000000)], C(0b1000000))
+ self.assertStatement(stmt, [C(0b1000001)], C(0b1000001))
+ stmt = lambda y, a: y.eq(a.rotate_right(-9))
+ self.assertStatement(stmt, [C(0b1000000)], C(0b0000010))
+ self.assertStatement(stmt, [C(0b1000001)], C(0b0000110))
+
+
+class SimulatorIntegrationTestCase(FHDLTestCase):
+ @contextmanager
+ def assertSimulation(self, module, deadline=None):
+ sim = Simulator(module)
+ yield sim
+ with sim.write_vcd("test.vcd", "test.gtkw"):
+ if deadline is None:
+ sim.run()
+ else:
+ sim.run_until(deadline)
+
+ def setUp_counter(self):
+ self.count = Signal(3, reset=4)
+ self.sync = ClockDomain()
+
+ self.m = Module()
+ self.m.d.sync += self.count.eq(self.count + 1)
+ self.m.domains += self.sync
+
+ def test_counter_process(self):
+ self.setUp_counter()
+ with self.assertSimulation(self.m) as sim:
+ def process():
+ self.assertEqual((yield self.count), 4)
+ yield Delay(1e-6)
+ self.assertEqual((yield self.count), 4)
+ yield self.sync.clk.eq(1)
+ self.assertEqual((yield self.count), 4)
+ yield Settle()
+ self.assertEqual((yield self.count), 5)
+ yield Delay(1e-6)
+ self.assertEqual((yield self.count), 5)
+ yield self.sync.clk.eq(0)
+ self.assertEqual((yield self.count), 5)
+ yield Settle()
+ self.assertEqual((yield self.count), 5)
+ for _ in range(3):
+ yield Delay(1e-6)
+ yield self.sync.clk.eq(1)
+ yield Delay(1e-6)
+ yield self.sync.clk.eq(0)
+ self.assertEqual((yield self.count), 0)
+ sim.add_process(process)
+
+ def test_counter_clock_and_sync_process(self):
+ self.setUp_counter()
+ with self.assertSimulation(self.m) as sim:
+ sim.add_clock(1e-6, domain="sync")
+ def process():
+ self.assertEqual((yield self.count), 4)
+ self.assertEqual((yield self.sync.clk), 1)
+ yield
+ self.assertEqual((yield self.count), 5)
+ self.assertEqual((yield self.sync.clk), 1)
+ for _ in range(3):
+ yield
+ self.assertEqual((yield self.count), 0)
+ sim.add_sync_process(process)
+
+ def test_reset(self):
+ self.setUp_counter()
+ sim = Simulator(self.m)
+ sim.add_clock(1e-6)
+ times = 0
+ def process():
+ nonlocal times
+ self.assertEqual((yield self.count), 4)
+ yield
+ self.assertEqual((yield self.count), 5)
+ yield
+ self.assertEqual((yield self.count), 6)
+ yield
+ times += 1
+ sim.add_sync_process(process)
+ sim.run()
+ sim.reset()
+ sim.run()
+ self.assertEqual(times, 2)
+
+ def setUp_alu(self):
+ self.a = Signal(8)
+ self.b = Signal(8)
+ self.o = Signal(8)
+ self.x = Signal(8)
+ self.s = Signal(2)
+ self.sync = ClockDomain(reset_less=True)
+
+ self.m = Module()
+ self.m.d.comb += self.x.eq(self.a ^ self.b)
+ with self.m.Switch(self.s):
+ with self.m.Case(0):
+ self.m.d.sync += self.o.eq(self.a + self.b)
+ with self.m.Case(1):
+ self.m.d.sync += self.o.eq(self.a - self.b)
+ with self.m.Case():
+ self.m.d.sync += self.o.eq(0)
+ self.m.domains += self.sync
+
+ def test_alu(self):
+ self.setUp_alu()
+ with self.assertSimulation(self.m) as sim:
+ sim.add_clock(1e-6)
+ def process():
+ yield self.a.eq(5)
+ yield self.b.eq(1)
+ yield
+ self.assertEqual((yield self.x), 4)
+ yield
+ self.assertEqual((yield self.o), 6)
+ yield self.s.eq(1)
+ yield
+ yield
+ self.assertEqual((yield self.o), 4)
+ yield self.s.eq(2)
+ yield
+ yield
+ self.assertEqual((yield self.o), 0)
+ sim.add_sync_process(process)
+
+ def setUp_multiclock(self):
+ self.sys = ClockDomain()
+ self.pix = ClockDomain()
+
+ self.m = Module()
+ self.m.domains += self.sys, self.pix
+
+ def test_multiclock(self):
+ self.setUp_multiclock()
+ with self.assertSimulation(self.m) as sim:
+ sim.add_clock(1e-6, domain="sys")
+ sim.add_clock(0.3e-6, domain="pix")
+
+ def sys_process():
+ yield Passive()
+ yield
+ yield
+ self.fail()
+ def pix_process():
+ yield
+ yield
+ yield
+ sim.add_sync_process(sys_process, domain="sys")
+ sim.add_sync_process(pix_process, domain="pix")
+
+ def setUp_lhs_rhs(self):
+ self.i = Signal(8)
+ self.o = Signal(8)
+
+ self.m = Module()
+ self.m.d.comb += self.o.eq(self.i)
+
+ def test_complex_lhs_rhs(self):
+ self.setUp_lhs_rhs()
+ with self.assertSimulation(self.m) as sim:
+ def process():
+ yield self.i.eq(0b10101010)
+ yield self.i[:4].eq(-1)
+ yield Settle()
+ self.assertEqual((yield self.i[:4]), 0b1111)
+ self.assertEqual((yield self.i), 0b10101111)
+ sim.add_process(process)
+
+ def test_run_until(self):
+ m = Module()
+ s = Signal()
+ m.d.sync += s.eq(0)
+ with self.assertSimulation(m, deadline=100e-6) as sim:
+ sim.add_clock(1e-6)
+ def process():
+ for _ in range(101):
+ yield Delay(1e-6)
+ self.fail()
+ sim.add_process(process)
+
+ def test_add_process_wrong(self):
+ with self.assertSimulation(Module()) as sim:
+ with self.assertRaisesRegex(TypeError,
+ r"^Cannot add a process 1 because it is not a generator function$"):
+ sim.add_process(1)
+
+ def test_add_process_wrong_generator(self):
+ with self.assertSimulation(Module()) as sim:
+ with self.assertRaisesRegex(TypeError,
+ r"^Cannot add a process <.+?> because it is not a generator function$"):
+ def process():
+ yield Delay()
+ sim.add_process(process())
+
+ def test_add_clock_wrong_twice(self):
+ m = Module()
+ s = Signal()
+ m.d.sync += s.eq(0)
+ with self.assertSimulation(m) as sim:
+ sim.add_clock(1)
+ with self.assertRaisesRegex(ValueError,
+ r"^Domain 'sync' already has a clock driving it$"):
+ sim.add_clock(1)
+
+ def test_add_clock_wrong_missing(self):
+ m = Module()
+ with self.assertSimulation(m) as sim:
+ with self.assertRaisesRegex(ValueError,
+ r"^Domain 'sync' is not present in simulation$"):
+ sim.add_clock(1)
+
+ def test_add_clock_if_exists(self):
+ m = Module()
+ with self.assertSimulation(m) as sim:
+ sim.add_clock(1, if_exists=True)
+
+ def test_command_wrong(self):
+ survived = False
+ with self.assertSimulation(Module()) as sim:
+ def process():
+ nonlocal survived
+ with self.assertRaisesRegex(TypeError,
+ r"Received unsupported command 1 from process .+?"):
+ yield 1
+ yield Settle()
+ survived = True
+ sim.add_process(process)
+ self.assertTrue(survived)
+
+ def setUp_memory(self, rd_synchronous=True, rd_transparent=True, wr_granularity=None):
+ self.m = Module()
+ self.memory = Memory(width=8, depth=4, init=[0xaa, 0x55])
+ self.m.submodules.rdport = self.rdport = \
+ self.memory.read_port(domain="sync" if rd_synchronous else "comb",
+ transparent=rd_transparent)
+ self.m.submodules.wrport = self.wrport = \
+ self.memory.write_port(granularity=wr_granularity)
+
+ def test_memory_init(self):
+ self.setUp_memory()
+ with self.assertSimulation(self.m) as sim:
+ def process():
+ self.assertEqual((yield self.rdport.data), 0xaa)
+ yield self.rdport.addr.eq(1)
+ yield
+ yield
+ self.assertEqual((yield self.rdport.data), 0x55)
+ yield self.rdport.addr.eq(2)
+ yield
+ yield
+ self.assertEqual((yield self.rdport.data), 0x00)
+ sim.add_clock(1e-6)
+ sim.add_sync_process(process)
+
+ def test_memory_write(self):
+ self.setUp_memory()
+ with self.assertSimulation(self.m) as sim:
+ def process():
+ yield self.wrport.addr.eq(4)
+ yield self.wrport.data.eq(0x33)
+ yield self.wrport.en.eq(1)
+ yield
+ yield self.wrport.en.eq(0)
+ yield self.rdport.addr.eq(4)
+ yield
+ self.assertEqual((yield self.rdport.data), 0x33)
+ sim.add_clock(1e-6)
+ sim.add_sync_process(process)
+
+ def test_memory_write_granularity(self):
+ self.setUp_memory(wr_granularity=4)
+ with self.assertSimulation(self.m) as sim:
+ def process():
+ yield self.wrport.data.eq(0x50)
+ yield self.wrport.en.eq(0b00)
+ yield
+ yield self.wrport.en.eq(0)
+ yield
+ self.assertEqual((yield self.rdport.data), 0xaa)
+ yield self.wrport.en.eq(0b10)
+ yield
+ yield self.wrport.en.eq(0)
+ yield
+ self.assertEqual((yield self.rdport.data), 0x5a)
+ yield self.wrport.data.eq(0x33)
+ yield self.wrport.en.eq(0b01)
+ yield
+ yield self.wrport.en.eq(0)
+ yield
+ self.assertEqual((yield self.rdport.data), 0x53)
+ sim.add_clock(1e-6)
+ sim.add_sync_process(process)
+
+ def test_memory_read_before_write(self):
+ self.setUp_memory(rd_transparent=False)
+ with self.assertSimulation(self.m) as sim:
+ def process():
+ yield self.wrport.data.eq(0x33)
+ yield self.wrport.en.eq(1)
+ yield
+ self.assertEqual((yield self.rdport.data), 0xaa)
+ yield
+ self.assertEqual((yield self.rdport.data), 0xaa)
+ yield Settle()
+ self.assertEqual((yield self.rdport.data), 0x33)
+ sim.add_clock(1e-6)
+ sim.add_sync_process(process)
+
+ def test_memory_write_through(self):
+ self.setUp_memory(rd_transparent=True)
+ with self.assertSimulation(self.m) as sim:
+ def process():
+ yield self.wrport.data.eq(0x33)
+ yield self.wrport.en.eq(1)
+ yield
+ self.assertEqual((yield self.rdport.data), 0xaa)
+ yield Settle()
+ self.assertEqual((yield self.rdport.data), 0x33)
+ yield
+ yield self.rdport.addr.eq(1)
+ yield Settle()
+ self.assertEqual((yield self.rdport.data), 0x33)
+ sim.add_clock(1e-6)
+ sim.add_sync_process(process)
+
+ def test_memory_async_read_write(self):
+ self.setUp_memory(rd_synchronous=False)
+ with self.assertSimulation(self.m) as sim:
+ def process():
+ yield self.rdport.addr.eq(0)
+ yield Settle()
+ self.assertEqual((yield self.rdport.data), 0xaa)
+ yield self.rdport.addr.eq(1)
+ yield Settle()
+ self.assertEqual((yield self.rdport.data), 0x55)
+ yield self.rdport.addr.eq(0)
+ yield self.wrport.addr.eq(0)
+ yield self.wrport.data.eq(0x33)
+ yield self.wrport.en.eq(1)
+ yield Tick("sync")
+ self.assertEqual((yield self.rdport.data), 0xaa)
+ yield Settle()
+ self.assertEqual((yield self.rdport.data), 0x33)
+ sim.add_clock(1e-6)
+ sim.add_process(process)
+
+ def test_memory_read_only(self):
+ self.m = Module()
+ self.memory = Memory(width=8, depth=4, init=[0xaa, 0x55])
+ self.m.submodules.rdport = self.rdport = self.memory.read_port()
+ with self.assertSimulation(self.m) as sim:
+ def process():
+ self.assertEqual((yield self.rdport.data), 0xaa)
+ yield self.rdport.addr.eq(1)
+ yield
+ yield
+ self.assertEqual((yield self.rdport.data), 0x55)
+ sim.add_clock(1e-6)
+ sim.add_sync_process(process)
+
+ def test_sample_helpers(self):
+ m = Module()
+ s = Signal(2)
+ def mk(x):
+ y = Signal.like(x)
+ m.d.comb += y.eq(x)
+ return y
+ p0, r0, f0, s0 = mk(Past(s, 0)), mk(Rose(s)), mk(Fell(s)), mk(Stable(s))
+ p1, r1, f1, s1 = mk(Past(s)), mk(Rose(s, 1)), mk(Fell(s, 1)), mk(Stable(s, 1))
+ p2, r2, f2, s2 = mk(Past(s, 2)), mk(Rose(s, 2)), mk(Fell(s, 2)), mk(Stable(s, 2))
+ p3, r3, f3, s3 = mk(Past(s, 3)), mk(Rose(s, 3)), mk(Fell(s, 3)), mk(Stable(s, 3))
+ with self.assertSimulation(m) as sim:
+ def process_gen():
+ yield s.eq(0b10)
+ yield
+ yield
+ yield s.eq(0b01)
+ yield
+ def process_check():
+ yield
+ yield
+ yield
+
+ self.assertEqual((yield p0), 0b01)
+ self.assertEqual((yield p1), 0b10)
+ self.assertEqual((yield p2), 0b10)
+ self.assertEqual((yield p3), 0b00)
+
+ self.assertEqual((yield s0), 0b0)
+ self.assertEqual((yield s1), 0b1)
+ self.assertEqual((yield s2), 0b0)
+ self.assertEqual((yield s3), 0b1)
+
+ self.assertEqual((yield r0), 0b01)
+ self.assertEqual((yield r1), 0b00)
+ self.assertEqual((yield r2), 0b10)
+ self.assertEqual((yield r3), 0b00)
+
+ self.assertEqual((yield f0), 0b10)
+ self.assertEqual((yield f1), 0b00)
+ self.assertEqual((yield f2), 0b00)
+ self.assertEqual((yield f3), 0b00)
+ sim.add_clock(1e-6)
+ sim.add_sync_process(process_gen)
+ sim.add_sync_process(process_check)
+
+ def test_vcd_wrong_nonzero_time(self):
+ s = Signal()
+ m = Module()
+ m.d.sync += s.eq(s)
+ sim = Simulator(m)
+ sim.add_clock(1e-6)
+ sim.run_until(1e-5)
+ with self.assertRaisesRegex(ValueError,
+ r"^Cannot start writing waveforms after advancing simulation time$"):
+ with sim.write_vcd(open(os.path.devnull, "wt")):
+ pass
+
+
+class SimulatorRegressionTestCase(FHDLTestCase):
+ def test_bug_325(self):
+ dut = Module()
+ dut.d.comb += Signal().eq(Cat())
+ Simulator(dut).run()
+
+ def test_bug_325_bis(self):
+ dut = Module()
+ dut.d.comb += Signal().eq(Repl(Const(1), 0))
+ Simulator(dut).run()
+
+ def test_bug_473(self):
+ sim = Simulator(Module())
+ def process():
+ self.assertEqual((yield -(Const(0b11, 2).as_signed())), 1)
+ sim.add_process(process)
+ sim.run()
--- /dev/null
+import os
+import re
+import shutil
+import subprocess
+import textwrap
+import traceback
+import unittest
+from contextlib import contextmanager
+
+from nmigen.hdl.ast import *
+from nmigen.hdl.ir import *
+from nmigen.back import rtlil
+from nmigen._toolchain import require_tool
+
+
+__all__ = ["FHDLTestCase"]
+
+
+class FHDLTestCase(unittest.TestCase):
+ def assertRepr(self, obj, repr_str):
+ if isinstance(obj, list):
+ obj = Statement.cast(obj)
+ def prepare_repr(repr_str):
+ repr_str = re.sub(r"\s+", " ", repr_str)
+ repr_str = re.sub(r"\( (?=\()", "(", repr_str)
+ repr_str = re.sub(r"\) (?=\))", ")", repr_str)
+ return repr_str.strip()
+ self.assertEqual(prepare_repr(repr(obj)), prepare_repr(repr_str))
+
+ def assertFormal(self, spec, mode="bmc", depth=1):
+ caller, *_ = traceback.extract_stack(limit=2)
+ spec_root, _ = os.path.splitext(caller.filename)
+ spec_dir = os.path.dirname(spec_root)
+ spec_name = "{}_{}".format(
+ os.path.basename(spec_root).replace("test_", "spec_"),
+ caller.name.replace("test_", "")
+ )
+
+ # The sby -f switch seems not fully functional when sby is reading from stdin.
+ if os.path.exists(os.path.join(spec_dir, spec_name)):
+ shutil.rmtree(os.path.join(spec_dir, spec_name))
+
+ if mode == "hybrid":
+ # A mix of BMC and k-induction, as per personal communication with Clifford Wolf.
+ script = "setattr -unset init w:* a:nmigen.sample_reg %d"
+ mode = "bmc"
+ else:
+ script = ""
+
+ config = textwrap.dedent("""\
+ [options]
+ mode {mode}
+ depth {depth}
+ wait on
+
+ [engines]
+ smtbmc
+
+ [script]
+ read_ilang top.il
+ prep
+ {script}
+
+ [file top.il]
+ {rtlil}
+ """).format(
+ mode=mode,
+ depth=depth,
+ script=script,
+ rtlil=rtlil.convert(Fragment.get(spec, platform="formal"))
+ )
+ with subprocess.Popen([require_tool("sby"), "-f", "-d", spec_name], cwd=spec_dir,
+ universal_newlines=True,
+ stdin=subprocess.PIPE, stdout=subprocess.PIPE) as proc:
+ stdout, stderr = proc.communicate(config)
+ if proc.returncode != 0:
+ self.fail("Formal verification failed:\n" + stdout)