2021-12-09 22:39:50 -07:00
|
|
|
# amaranth: UnusedElaboratable=no
|
2019-10-26 00:36:54 -06:00
|
|
|
|
2020-11-05 19:21:53 -07:00
|
|
|
import warnings
|
|
|
|
|
2021-12-09 22:39:50 -07:00
|
|
|
from amaranth.hdl.ast import *
|
|
|
|
from amaranth.hdl.cd import *
|
|
|
|
from amaranth.hdl.ir import *
|
|
|
|
from amaranth.hdl.xfrm import *
|
|
|
|
from amaranth.hdl.mem import *
|
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.
2020-08-26 18:33:31 -06:00
|
|
|
|
2019-10-13 12:53:38 -06:00
|
|
|
from .utils import *
|
2018-12-13 01:39:02 -07:00
|
|
|
|
|
|
|
|
2018-12-13 01:57:14 -07:00
|
|
|
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")),
|
|
|
|
)
|
2018-12-14 13:58:29 -07:00
|
|
|
f.add_driver(self.s1, None)
|
|
|
|
f.add_driver(self.s2, None)
|
|
|
|
f.add_driver(self.s3, "sync")
|
2018-12-13 01:57:14 -07:00
|
|
|
|
|
|
|
f = DomainRenamer("pix")(f)
|
|
|
|
self.assertRepr(f.statements, """
|
|
|
|
(
|
|
|
|
(eq (sig s1) (clk pix))
|
|
|
|
(eq (rst pix) (sig s2))
|
2018-12-13 11:00:05 -07:00
|
|
|
(eq (sig s3) (const 1'd0))
|
2018-12-13 01:57:14 -07:00
|
|
|
(eq (sig s4) (clk other))
|
|
|
|
(eq (sig s5) (rst other))
|
|
|
|
)
|
|
|
|
""")
|
|
|
|
self.assertEqual(f.drivers, {
|
2018-12-17 10:21:12 -07:00
|
|
|
None: SignalSet((self.s1, self.s2)),
|
|
|
|
"pix": SignalSet((self.s3,)),
|
2018-12-13 01:57:14 -07:00
|
|
|
})
|
|
|
|
|
|
|
|
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))
|
|
|
|
)
|
|
|
|
""")
|
|
|
|
|
2018-12-13 04:01:03 -07:00
|
|
|
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,
|
|
|
|
})
|
|
|
|
|
2020-06-06 05:43:25 -06:00
|
|
|
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))
|
|
|
|
)
|
|
|
|
""")
|
|
|
|
|
|
|
|
|
2018-12-13 04:01:03 -07:00
|
|
|
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,
|
|
|
|
})
|
|
|
|
|
2019-07-08 04:26:49 -06:00
|
|
|
def test_rename_wrong_to_comb(self):
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(ValueError,
|
|
|
|
r"^Domain 'sync' may not be renamed to 'comb'$"):
|
2019-07-08 04:26:49 -06:00
|
|
|
DomainRenamer("comb")
|
|
|
|
|
|
|
|
def test_rename_wrong_from_comb(self):
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(ValueError,
|
|
|
|
r"^Domain 'comb' may not be renamed$"):
|
2019-07-08 04:26:49 -06:00
|
|
|
DomainRenamer({"comb": "sync"})
|
|
|
|
|
2018-12-13 01:57:14 -07:00
|
|
|
|
2018-12-14 03:56:53 -07:00
|
|
|
class DomainLowererTestCase(FHDLTestCase):
|
|
|
|
def setUp(self):
|
|
|
|
self.s = Signal()
|
|
|
|
|
|
|
|
def test_lower_clk(self):
|
|
|
|
sync = ClockDomain()
|
|
|
|
f = Fragment()
|
2019-08-19 15:06:54 -06:00
|
|
|
f.add_domains(sync)
|
2018-12-14 03:56:53 -07:00
|
|
|
f.add_statements(
|
|
|
|
self.s.eq(ClockSignal("sync"))
|
|
|
|
)
|
|
|
|
|
2019-08-19 15:06:54 -06:00
|
|
|
f = DomainLowerer()(f)
|
2018-12-14 03:56:53 -07:00
|
|
|
self.assertRepr(f.statements, """
|
|
|
|
(
|
|
|
|
(eq (sig s) (sig clk))
|
|
|
|
)
|
|
|
|
""")
|
|
|
|
|
|
|
|
def test_lower_rst(self):
|
|
|
|
sync = ClockDomain()
|
|
|
|
f = Fragment()
|
2019-08-19 15:06:54 -06:00
|
|
|
f.add_domains(sync)
|
2018-12-14 03:56:53 -07:00
|
|
|
f.add_statements(
|
|
|
|
self.s.eq(ResetSignal("sync"))
|
|
|
|
)
|
|
|
|
|
2019-08-19 15:06:54 -06:00
|
|
|
f = DomainLowerer()(f)
|
2018-12-14 03:56:53 -07:00
|
|
|
self.assertRepr(f.statements, """
|
|
|
|
(
|
|
|
|
(eq (sig s) (sig rst))
|
|
|
|
)
|
|
|
|
""")
|
|
|
|
|
|
|
|
def test_lower_rst_reset_less(self):
|
|
|
|
sync = ClockDomain(reset_less=True)
|
|
|
|
f = Fragment()
|
2019-08-19 15:06:54 -06:00
|
|
|
f.add_domains(sync)
|
2018-12-14 03:56:53 -07:00
|
|
|
f.add_statements(
|
|
|
|
self.s.eq(ResetSignal("sync", allow_reset_less=True))
|
|
|
|
)
|
|
|
|
|
2019-08-19 15:06:54 -06:00
|
|
|
f = DomainLowerer()(f)
|
2018-12-14 03:56:53 -07:00
|
|
|
self.assertRepr(f.statements, """
|
|
|
|
(
|
|
|
|
(eq (sig s) (const 1'd0))
|
|
|
|
)
|
|
|
|
""")
|
|
|
|
|
2019-01-14 08:38:16 -07:00
|
|
|
def test_lower_drivers(self):
|
2019-08-19 15:32:48 -06:00
|
|
|
sync = ClockDomain()
|
2019-01-14 08:38:16 -07:00
|
|
|
pix = ClockDomain()
|
|
|
|
f = Fragment()
|
2019-08-19 15:32:48 -06:00
|
|
|
f.add_domains(sync, pix)
|
2019-01-14 08:38:16 -07:00
|
|
|
f.add_driver(ClockSignal("pix"), None)
|
|
|
|
f.add_driver(ResetSignal("pix"), "sync")
|
|
|
|
|
2019-08-19 15:06:54 -06:00
|
|
|
f = DomainLowerer()(f)
|
2019-01-14 08:38:16 -07:00
|
|
|
self.assertEqual(f.drivers, {
|
|
|
|
None: SignalSet((pix.clk,)),
|
|
|
|
"sync": SignalSet((pix.rst,))
|
|
|
|
})
|
|
|
|
|
2018-12-14 03:56:53 -07:00
|
|
|
def test_lower_wrong_domain(self):
|
|
|
|
f = Fragment()
|
|
|
|
f.add_statements(
|
|
|
|
self.s.eq(ClockSignal("xxx"))
|
|
|
|
)
|
|
|
|
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(DomainError,
|
|
|
|
r"^Signal \(clk xxx\) refers to nonexistent domain 'xxx'$"):
|
2019-08-19 15:06:54 -06:00
|
|
|
DomainLowerer()(f)
|
2018-12-14 03:56:53 -07:00
|
|
|
|
|
|
|
def test_lower_wrong_reset_less_domain(self):
|
|
|
|
sync = ClockDomain(reset_less=True)
|
|
|
|
f = Fragment()
|
2019-08-19 15:06:54 -06:00
|
|
|
f.add_domains(sync)
|
2018-12-14 03:56:53 -07:00
|
|
|
f.add_statements(
|
|
|
|
self.s.eq(ResetSignal("sync"))
|
|
|
|
)
|
|
|
|
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(DomainError,
|
|
|
|
r"^Signal \(rst sync\) refers to reset of reset-less domain 'sync'$"):
|
2019-08-19 15:06:54 -06:00
|
|
|
DomainLowerer()(f)
|
2018-12-14 03:56:53 -07:00
|
|
|
|
|
|
|
|
2019-01-16 18:41:02 -07:00
|
|
|
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)
|
|
|
|
|
|
|
|
|
2018-12-23 19:02:59 -07:00
|
|
|
class SwitchCleanerTestCase(FHDLTestCase):
|
|
|
|
def test_clean(self):
|
|
|
|
a = Signal()
|
|
|
|
b = Signal()
|
|
|
|
c = Signal()
|
|
|
|
stmts = [
|
|
|
|
Switch(a, {
|
|
|
|
1: a.eq(0),
|
|
|
|
0: [
|
|
|
|
b.eq(1),
|
2018-12-23 19:17:28 -07:00
|
|
|
Switch(b, {1: [
|
|
|
|
Switch(a|b, {})
|
|
|
|
]})
|
2018-12-23 19:02:59 -07:00
|
|
|
]
|
|
|
|
})
|
|
|
|
]
|
|
|
|
|
|
|
|
self.assertRepr(SwitchCleaner()(stmts), """
|
|
|
|
(
|
|
|
|
(switch (sig a)
|
|
|
|
(case 1
|
|
|
|
(eq (sig a) (const 1'd0)))
|
|
|
|
(case 0
|
|
|
|
(eq (sig b) (const 1'd1)))
|
|
|
|
)
|
|
|
|
)
|
|
|
|
""")
|
|
|
|
|
|
|
|
|
2018-12-21 23:50:32 -07:00
|
|
|
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)),
|
|
|
|
])
|
|
|
|
|
2018-12-22 15:19:14 -07:00
|
|
|
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)),
|
|
|
|
])
|
|
|
|
|
2018-12-21 23:50:32 -07:00
|
|
|
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,)),
|
|
|
|
])
|
|
|
|
|
2019-06-04 04:19:54 -06:00
|
|
|
def test_lhs_empty(self):
|
|
|
|
stmts = [
|
|
|
|
Cat().eq(0)
|
|
|
|
]
|
|
|
|
|
|
|
|
groups = LHSGroupAnalyzer()(stmts)
|
|
|
|
self.assertEqual(list(groups.values()), [
|
|
|
|
])
|
|
|
|
|
2018-12-21 23:50:32 -07:00
|
|
|
|
2018-12-23 19:17:28 -07:00
|
|
|
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 )
|
|
|
|
)
|
|
|
|
)
|
|
|
|
""")
|
|
|
|
|
2019-06-04 04:19:54 -06:00
|
|
|
def test_lhs_empty(self):
|
|
|
|
stmts = [
|
|
|
|
Cat().eq(0)
|
|
|
|
]
|
|
|
|
|
|
|
|
self.assertRepr(LHSGroupFilter(SignalSet())(stmts), "()")
|
|
|
|
|
2018-12-21 23:50:32 -07:00
|
|
|
|
2018-12-13 01:57:14 -07:00
|
|
|
class ResetInserterTestCase(FHDLTestCase):
|
2018-12-13 01:39:02 -07:00
|
|
|
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)
|
|
|
|
)
|
2018-12-14 13:58:29 -07:00
|
|
|
f.add_driver(self.s1, "sync")
|
2018-12-13 01:39:02 -07:00
|
|
|
|
|
|
|
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),
|
|
|
|
)
|
2018-12-13 04:01:03 -07:00
|
|
|
f.add_domains(ClockDomain("sync"))
|
2018-12-14 13:58:29 -07:00
|
|
|
f.add_driver(self.s1, "sync")
|
|
|
|
f.add_driver(self.s2, "pix")
|
2018-12-13 01:39:02 -07:00
|
|
|
|
|
|
|
f = ResetInserter({"pix": self.c1})(f)
|
|
|
|
self.assertRepr(f.statements, """
|
|
|
|
(
|
|
|
|
(eq (sig s1) (const 1'd1))
|
2018-12-13 11:00:05 -07:00
|
|
|
(eq (sig s2) (const 1'd0))
|
2018-12-13 01:39:02 -07:00
|
|
|
(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)
|
|
|
|
)
|
2018-12-14 13:58:29 -07:00
|
|
|
f.add_driver(self.s2, "sync")
|
2018-12-13 01:39:02 -07:00
|
|
|
|
|
|
|
f = ResetInserter(self.c1)(f)
|
|
|
|
self.assertRepr(f.statements, """
|
|
|
|
(
|
2018-12-13 11:00:05 -07:00
|
|
|
(eq (sig s2) (const 1'd0))
|
2018-12-13 01:39:02 -07:00
|
|
|
(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)
|
|
|
|
)
|
2018-12-14 13:58:29 -07:00
|
|
|
f.add_driver(self.s3, "sync")
|
2018-12-13 01:39:02 -07:00
|
|
|
|
|
|
|
f = ResetInserter(self.c1)(f)
|
|
|
|
self.assertRepr(f.statements, """
|
|
|
|
(
|
2018-12-13 11:00:05 -07:00
|
|
|
(eq (sig s3) (const 1'd0))
|
2018-12-13 01:39:02 -07:00
|
|
|
(switch (sig c1)
|
|
|
|
(case 1 )
|
|
|
|
)
|
|
|
|
)
|
|
|
|
""")
|
|
|
|
|
|
|
|
|
2019-08-12 07:37:18 -06:00
|
|
|
class EnableInserterTestCase(FHDLTestCase):
|
2018-12-13 01:39:02 -07:00
|
|
|
def setUp(self):
|
|
|
|
self.s1 = Signal()
|
|
|
|
self.s2 = Signal()
|
|
|
|
self.s3 = Signal()
|
|
|
|
self.c1 = Signal()
|
|
|
|
|
2019-08-12 07:37:18 -06:00
|
|
|
def test_enable_default(self):
|
2018-12-13 01:39:02 -07:00
|
|
|
f = Fragment()
|
|
|
|
f.add_statements(
|
|
|
|
self.s1.eq(1)
|
|
|
|
)
|
2018-12-14 13:58:29 -07:00
|
|
|
f.add_driver(self.s1, "sync")
|
2018-12-13 01:39:02 -07:00
|
|
|
|
2019-08-12 07:37:18 -06:00
|
|
|
f = EnableInserter(self.c1)(f)
|
2018-12-13 01:39:02 -07:00
|
|
|
self.assertRepr(f.statements, """
|
|
|
|
(
|
|
|
|
(eq (sig s1) (const 1'd1))
|
|
|
|
(switch (sig c1)
|
|
|
|
(case 0 (eq (sig s1) (sig s1)))
|
|
|
|
)
|
|
|
|
)
|
|
|
|
""")
|
|
|
|
|
2019-08-12 07:37:18 -06:00
|
|
|
def test_enable_cd(self):
|
2018-12-13 01:39:02 -07:00
|
|
|
f = Fragment()
|
|
|
|
f.add_statements(
|
|
|
|
self.s1.eq(1),
|
|
|
|
self.s2.eq(0),
|
|
|
|
)
|
2018-12-14 13:58:29 -07:00
|
|
|
f.add_driver(self.s1, "sync")
|
|
|
|
f.add_driver(self.s2, "pix")
|
2018-12-13 01:39:02 -07:00
|
|
|
|
2019-08-12 07:37:18 -06:00
|
|
|
f = EnableInserter({"pix": self.c1})(f)
|
2018-12-13 01:39:02 -07:00
|
|
|
self.assertRepr(f.statements, """
|
|
|
|
(
|
|
|
|
(eq (sig s1) (const 1'd1))
|
2018-12-13 11:00:05 -07:00
|
|
|
(eq (sig s2) (const 1'd0))
|
2018-12-13 01:39:02 -07:00
|
|
|
(switch (sig c1)
|
|
|
|
(case 0 (eq (sig s2) (sig s2)))
|
|
|
|
)
|
|
|
|
)
|
|
|
|
""")
|
2018-12-13 01:45:10 -07:00
|
|
|
|
2019-08-12 07:37:18 -06:00
|
|
|
def test_enable_subfragment(self):
|
2018-12-13 01:45:10 -07:00
|
|
|
f1 = Fragment()
|
|
|
|
f1.add_statements(
|
|
|
|
self.s1.eq(1)
|
|
|
|
)
|
2018-12-14 13:58:29 -07:00
|
|
|
f1.add_driver(self.s1, "sync")
|
2018-12-13 01:45:10 -07:00
|
|
|
|
|
|
|
f2 = Fragment()
|
|
|
|
f2.add_statements(
|
|
|
|
self.s2.eq(1)
|
|
|
|
)
|
2018-12-14 13:58:29 -07:00
|
|
|
f2.add_driver(self.s2, "sync")
|
2018-12-13 01:45:10 -07:00
|
|
|
f1.add_subfragment(f2)
|
|
|
|
|
2019-08-12 07:37:18 -06:00
|
|
|
f1 = EnableInserter(self.c1)(f1)
|
2018-12-13 01:45:10 -07:00
|
|
|
(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)))
|
|
|
|
)
|
|
|
|
)
|
|
|
|
""")
|
2019-04-09 18:23:11 -06:00
|
|
|
|
2019-08-12 07:37:18 -06:00
|
|
|
def test_enable_read_port(self):
|
2019-07-30 23:19:24 -06:00
|
|
|
mem = Memory(width=8, depth=4)
|
2019-08-12 07:37:18 -06:00
|
|
|
f = EnableInserter(self.c1)(mem.read_port(transparent=False)).elaborate(platform=None)
|
2019-07-30 23:19:24 -06:00
|
|
|
self.assertRepr(f.named_ports["EN"][0], """
|
|
|
|
(m (sig c1) (sig mem_r_en) (const 1'd0))
|
|
|
|
""")
|
|
|
|
|
2019-08-12 07:37:18 -06:00
|
|
|
def test_enable_write_port(self):
|
2019-07-30 23:19:24 -06:00
|
|
|
mem = Memory(width=8, depth=4)
|
2019-08-12 07:37:18 -06:00
|
|
|
f = EnableInserter(self.c1)(mem.write_port()).elaborate(platform=None)
|
2019-07-30 23:19:24 -06:00
|
|
|
self.assertRepr(f.named_ports["EN"][0], """
|
|
|
|
(m (sig c1) (cat (repl (slice (sig mem_w_en) 0:1) 8)) (const 8'd0))
|
|
|
|
""")
|
|
|
|
|
2019-04-09 18:23:11 -06:00
|
|
|
|
2019-04-21 02:52:57 -06:00
|
|
|
class _MockElaboratable(Elaboratable):
|
2019-04-09 18:23:11 -06:00
|
|
|
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()
|
2019-08-12 07:37:18 -06:00
|
|
|
te = EnableInserter(self.c1)(e)
|
2019-04-09 18:23:11 -06:00
|
|
|
|
|
|
|
self.assertIs(te.s1, e.s1)
|
|
|
|
|
|
|
|
def test_composition(self):
|
|
|
|
e = _MockElaboratable()
|
2019-08-12 07:37:18 -06:00
|
|
|
te1 = EnableInserter(self.c1)(e)
|
2019-04-09 18:23:11 -06:00
|
|
|
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)))
|
|
|
|
)
|
|
|
|
)
|
|
|
|
""")
|