build.run: implement SSH remote builds using Paramiko.
[nmigen.git] / nmigen / test / test_hdl_xfrm.py
index 94af1f8a89d8b65f2d68c24b3c8d0fbc71fdedb2..9ec6e1b0f4a48c22c482718a69ed59d9b8d365c4 100644 (file)
@@ -1,8 +1,11 @@
+# nmigen: UnusedElaboratable=no
+
 from ..hdl.ast import *
 from ..hdl.cd import *
 from ..hdl.ir import *
 from ..hdl.xfrm import *
-from .tools import *
+from ..hdl.mem import *
+from .utils import *
 
 
 class DomainRenamerTestCase(FHDLTestCase):
@@ -71,6 +74,24 @@ class DomainRenamerTestCase(FHDLTestCase):
             "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()
@@ -88,6 +109,16 @@ class DomainRenamerTestCase(FHDLTestCase):
             "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):
@@ -96,11 +127,12 @@ class DomainLowererTestCase(FHDLTestCase):
     def test_lower_clk(self):
         sync = ClockDomain()
         f = Fragment()
+        f.add_domains(sync)
         f.add_statements(
             self.s.eq(ClockSignal("sync"))
         )
 
-        f = DomainLowerer({"sync": sync})(f)
+        f = DomainLowerer()(f)
         self.assertRepr(f.statements, """
         (
             (eq (sig s) (sig clk))
@@ -110,11 +142,12 @@ class DomainLowererTestCase(FHDLTestCase):
     def test_lower_rst(self):
         sync = ClockDomain()
         f = Fragment()
+        f.add_domains(sync)
         f.add_statements(
             self.s.eq(ResetSignal("sync"))
         )
 
-        f = DomainLowerer({"sync": sync})(f)
+        f = DomainLowerer()(f)
         self.assertRepr(f.statements, """
         (
             (eq (sig s) (sig rst))
@@ -124,11 +157,12 @@ class DomainLowererTestCase(FHDLTestCase):
     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({"sync": sync})(f)
+        f = DomainLowerer()(f)
         self.assertRepr(f.statements, """
         (
             (eq (sig s) (const 1'd0))
@@ -136,38 +170,40 @@ class DomainLowererTestCase(FHDLTestCase):
         """)
 
     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({"pix": pix})(f)
+        f = DomainLowerer()(f)
         self.assertEqual(f.drivers, {
             None: SignalSet((pix.clk,)),
             "sync": SignalSet((pix.rst,))
         })
 
     def test_lower_wrong_domain(self):
-        sync = ClockDomain()
         f = Fragment()
         f.add_statements(
             self.s.eq(ClockSignal("xxx"))
         )
 
-        with self.assertRaises(DomainError,
-                msg="Signal (clk xxx) refers to nonexistent domain 'xxx'"):
-            DomainLowerer({"sync": sync})(f)
+        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.assertRaises(DomainError,
-                msg="Signal (rst sync) refers to reset of reset-less domain 'sync'"):
-            DomainLowerer({"sync": sync})(f)
+        with self.assertRaisesRegex(DomainError,
+                r"^Signal \(rst sync\) refers to reset of reset-less domain 'sync'$"):
+            DomainLowerer()(f)
 
 
 class SampleLowererTestCase(FHDLTestCase):
@@ -303,6 +339,15 @@ class LHSGroupAnalyzerTestCase(FHDLTestCase):
             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):
@@ -329,6 +374,13 @@ class LHSGroupFilterTestCase(FHDLTestCase):
         )
         """)
 
+    def test_lhs_empty(self):
+        stmts = [
+            Cat().eq(0)
+        ]
+
+        self.assertRepr(LHSGroupFilter(SignalSet())(stmts), "()")
+
 
 class ResetInserterTestCase(FHDLTestCase):
     def setUp(self):
@@ -410,21 +462,21 @@ class ResetInserterTestCase(FHDLTestCase):
         """)
 
 
-class CEInserterTestCase(FHDLTestCase):
+class EnableInserterTestCase(FHDLTestCase):
     def setUp(self):
         self.s1 = Signal()
         self.s2 = Signal()
         self.s3 = Signal()
         self.c1 = Signal()
 
-    def test_ce_default(self):
+    def test_enable_default(self):
         f = Fragment()
         f.add_statements(
             self.s1.eq(1)
         )
         f.add_driver(self.s1, "sync")
 
-        f = CEInserter(self.c1)(f)
+        f = EnableInserter(self.c1)(f)
         self.assertRepr(f.statements, """
         (
             (eq (sig s1) (const 1'd1))
@@ -434,7 +486,7 @@ class CEInserterTestCase(FHDLTestCase):
         )
         """)
 
-    def test_ce_cd(self):
+    def test_enable_cd(self):
         f = Fragment()
         f.add_statements(
             self.s1.eq(1),
@@ -443,7 +495,7 @@ class CEInserterTestCase(FHDLTestCase):
         f.add_driver(self.s1, "sync")
         f.add_driver(self.s2, "pix")
 
-        f = CEInserter({"pix": self.c1})(f)
+        f = EnableInserter({"pix": self.c1})(f)
         self.assertRepr(f.statements, """
         (
             (eq (sig s1) (const 1'd1))
@@ -454,7 +506,7 @@ class CEInserterTestCase(FHDLTestCase):
         )
         """)
 
-    def test_ce_subfragment(self):
+    def test_enable_subfragment(self):
         f1 = Fragment()
         f1.add_statements(
             self.s1.eq(1)
@@ -468,7 +520,7 @@ class CEInserterTestCase(FHDLTestCase):
         f2.add_driver(self.s2, "sync")
         f1.add_subfragment(f2)
 
-        f1 = CEInserter(self.c1)(f1)
+        f1 = EnableInserter(self.c1)(f1)
         (f2, _), = f1.subfragments
         self.assertRepr(f1.statements, """
         (
@@ -487,8 +539,22 @@ class CEInserterTestCase(FHDLTestCase):
         )
         """)
 
+    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:
+class _MockElaboratable(Elaboratable):
     def __init__(self):
         self.s1 = Signal()
 
@@ -508,13 +574,13 @@ class TransformedElaboratableTestCase(FHDLTestCase):
 
     def test_getattr(self):
         e = _MockElaboratable()
-        te = CEInserter(self.c1)(e)
+        te = EnableInserter(self.c1)(e)
 
         self.assertIs(te.s1, e.s1)
 
     def test_composition(self):
         e = _MockElaboratable()
-        te1 = CEInserter(self.c1)(e)
+        te1 = EnableInserter(self.c1)(e)
         te2 = ResetInserter(self.c2)(te1)
 
         self.assertIsInstance(te1, TransformedElaboratable)
@@ -532,3 +598,52 @@ class TransformedElaboratableTestCase(FHDLTestCase):
             )
         )
         """)
+
+
+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