tests: move out of the main package.

Compared to tests in the repository root, tests in the package have
many downsides:
  * Unless explicitly excluded in find_packages(), tests and their
    support code effectively become a part of public API.
    This, unfortunately, happened with FHDLTestCase, which was never
    intended for downstream use.
  * Even if explicitly excluded from the setuptools package, using
    an editable install, or setting PYTHONPATH still allows accessing
    the tests.
  * Having a sub-package that is present in the source tree but not
    exported (or, worse, exported only sometimes) is confusing.
  * The name `nmigen.test` cannot be used for anything else, such as
    testing utilities that *are* intended for downstream use.
This commit is contained in:
whitequark 2020-08-27 00:33:31 +00:00
parent ef7a3bcfb1
commit 67b957d4f4
32 changed files with 103 additions and 82 deletions

View file

@ -1,16 +0,0 @@
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)

View file

@ -1,116 +0,0 @@
# 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())

View file

@ -1,29 +0,0 @@
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())

View file

@ -1,38 +0,0 @@
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())

View file

@ -1,87 +0,0 @@
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())

View file

@ -1,23 +0,0 @@
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)

View file

@ -1,26 +0,0 @@
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())

View file

@ -1,42 +0,0 @@
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())

View file

@ -1,21 +0,0 @@
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)

View file

@ -1,328 +0,0 @@
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})

View file

@ -1,52 +0,0 @@
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")

View file

@ -1,313 +0,0 @@
# 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)

View file

@ -1,9 +0,0 @@
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)

View file

@ -1,34 +0,0 @@
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)

File diff suppressed because it is too large Load diff

View file

@ -1,78 +0,0 @@
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()

View file

@ -1,769 +0,0 @@
# 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)

View file

@ -1,862 +0,0 @@
# 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),
]))

View file

@ -1,137 +0,0 @@
# 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)

View file

@ -1,329 +0,0 @@
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)

View file

@ -1,649 +0,0 @@
# 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

View file

@ -1,230 +0,0 @@
# 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()

View file

@ -1,126 +0,0 @@
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")

View file

@ -1,281 +0,0 @@
# 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))

View file

@ -1,208 +0,0 @@
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)

View file

@ -1,93 +0,0 @@
# 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()

View file

@ -1,798 +0,0 @@
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()

View file

@ -1,78 +0,0 @@
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)