Remove everything deprecated in nmigen 0.1.
[nmigen.git] / nmigen / test / test_hdl_dsl.py
index bbcae25426ca66729a6f7594e8e9b7dcdf00e2ef..8ebd18dfa958a72a2d14c466db92a1156055e570 100644 (file)
@@ -1,9 +1,12 @@
+# nmigen: UnusedElaboratable=no
+
 from collections import OrderedDict
+from enum import Enum
 
 from ..hdl.ast import *
 from ..hdl.cd import *
 from ..hdl.dsl import *
-from .tools import *
+from .utils import *
 
 
 class DSLTestCase(FHDLTestCase):
@@ -74,7 +77,7 @@ class DSLTestCase(FHDLTestCase):
     def test_d_asgn_wrong(self):
         m = Module()
         with self.assertRaises(SyntaxError,
-                msg="Only assignments, asserts, and assumes may be appended to d.sync"):
+                msg="Only assignments and property checks may be appended to d.sync"):
             m.d.sync += Switch(self.s1, {})
 
     def test_comb_wrong(self):
@@ -95,6 +98,13 @@ class DSLTestCase(FHDLTestCase):
                 msg="'Module' object has no attribute 'nonexistentattr'"):
             m.nonexistentattr
 
+    def test_d_suspicious(self):
+        m = Module()
+        with self.assertWarns(SyntaxWarning,
+                msg="Using '<module>.d.submodules' would add statements to clock domain "
+                    "'submodules'; did you mean <module>.submodules instead?"):
+            m.d.submodules += []
+
     def test_clock_signal(self):
         m = Module()
         m.d.comb += ClockSignal("pix").eq(ClockSignal())
@@ -113,6 +123,24 @@ class DSLTestCase(FHDLTestCase):
         )
         """)
 
+    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):
@@ -156,7 +184,7 @@ class DSLTestCase(FHDLTestCase):
             (switch (cat (sig s1) (sig s2))
                 (case -1 (eq (sig c1) (const 1'd1)))
                 (case 1- (eq (sig c2) (const 1'd0)))
-                (case -- (eq (sig c3) (const 1'd1)))
+                (default (eq (sig c3) (const 1'd1)))
             )
         )
         """)
@@ -216,7 +244,7 @@ class DSLTestCase(FHDLTestCase):
                         (case 1 (eq (sig c2) (const 1'd1)))
                     )
                 )
-                (case -
+                (default
                     (eq (sig c3) (const 1'd1))
                 )
             )
@@ -250,6 +278,28 @@ class DSLTestCase(FHDLTestCase):
         )
         """)
 
+    def test_If_signed_suspicious(self):
+        m = Module()
+        with self.assertWarns(SyntaxWarning,
+                msg="Signed values in If/Elif conditions usually result from inverting Python "
+                    "booleans with ~, which leads to unexpected results: ~True is -2, which is "
+                    "truthful. Replace `~flag` with `not flag`. (If this is a false positive, "
+                    "silence this warning with `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.assertWarns(SyntaxWarning,
+                msg="Signed values in If/Elif conditions usually result from inverting Python "
+                    "booleans with ~, which leads to unexpected results: ~True is -2, which is "
+                    "truthful. Replace `~flag` with `not flag`. (If this is a false positive, "
+                    "silence this warning with `m.If(x)` → `m.If(x.bool())`.)"):
+            with m.Elif(~True):
+                pass
+
     def test_Switch(self):
         m = Module()
         with m.Switch(self.w1):
@@ -267,7 +317,7 @@ class DSLTestCase(FHDLTestCase):
         )
         """)
 
-    def test_Switch_default(self):
+    def test_Switch_default_Case(self):
         m = Module()
         with m.Switch(self.w1):
             with m.Case(3):
@@ -279,7 +329,24 @@ class DSLTestCase(FHDLTestCase):
         (
             (switch (sig w1)
                 (case 0011 (eq (sig c1) (const 1'd1)))
-                (case ---- (eq (sig c2) (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)))
             )
         )
         """)
@@ -298,16 +365,33 @@ class DSLTestCase(FHDLTestCase):
         )
         """)
 
+    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):
         m = Module()
         with m.Switch(self.w1):
             with self.assertRaises(SyntaxError,
-                    msg="Case value '--' must have the same width as test (which is 4)"):
+                    msg="Case pattern '--' must have the same width as switch value (which is 4)"):
                 with m.Case("--"):
                     pass
             with self.assertWarns(SyntaxWarning,
-                    msg="Case value '10110' is wider than test (which has width 4); comparison "
-                        "will never be true"):
+                    msg="Case pattern '10110' is wider than switch value (which has width 4); "
+                        "comparison will never be true"):
                 with m.Case(0b10110):
                     pass
         self.assertRepr(m._statements, """
@@ -316,6 +400,22 @@ class DSLTestCase(FHDLTestCase):
         )
         """)
 
+    def test_Case_bits_wrong(self):
+        m = Module()
+        with m.Switch(self.w1):
+            with self.assertRaises(SyntaxError,
+                    msg="Case pattern 'abc' must consist of 0, 1, and - (don't care) bits"):
+                with m.Case("abc"):
+                    pass
+
+    def test_Case_pattern_wrong(self):
+        m = Module()
+        with m.Switch(self.w1):
+            with self.assertRaises(SyntaxError,
+                    msg="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.assertRaises(SyntaxError,
@@ -327,7 +427,8 @@ class DSLTestCase(FHDLTestCase):
         m = Module()
         with m.Switch(self.s1):
             with self.assertRaises(SyntaxError,
-                    msg="If is not permitted inside of Switch"):
+                    msg="If is not permitted directly inside of Switch; "
+                        "it is permitted inside of Switch Case"):
                 with m.If(self.s2):
                     pass
 
@@ -368,7 +469,7 @@ class DSLTestCase(FHDLTestCase):
             "(sig b)": "sync",
         })
 
-        frag = m.lower(platform=None)
+        frag = m.elaborate(platform=None)
         fsm  = frag.find_generated("fsm")
         self.assertIsInstance(fsm.state, Signal)
         self.assertEqual(fsm.encoding, OrderedDict({
@@ -431,6 +532,21 @@ class DSLTestCase(FHDLTestCase):
         )
         """)
 
+    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.assertRaises(ValueError,
+                msg="FSM may not be driven by the 'comb' domain"):
+            with m.FSM(domain="comb"):
+                pass
+
     def test_FSM_wrong_redefined(self):
         m = Module()
         with m.FSM():
@@ -454,6 +570,17 @@ class DSLTestCase(FHDLTestCase):
             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.assertRaises(SyntaxError,
+                    msg="If is not permitted directly inside of FSM; "
+                        "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):
@@ -472,30 +599,68 @@ class DSLTestCase(FHDLTestCase):
         m1 = Module()
         m2 = Module()
         m1.submodules += m2
-        self.assertEqual(m1._submodules, [(m2, None)])
+        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._submodules, [(m2, None), (m3, None)])
+        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._submodules, [(m2, "foo")])
+        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.assertRaises(TypeError,
-                msg="Trying to add '1', which does not implement .get_fragment(), as a submodule"):
+                msg="Trying to add 1, which does not implement .elaborate(), as a submodule"):
             m.submodules.foo = 1
         with self.assertRaises(TypeError,
-                msg="Trying to add '1', which does not implement .get_fragment(), as a submodule"):
+                msg="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.assertRaises(NameError, msg="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.assertRaises(AttributeError, msg="No submodule named 'foo' exists"):
+            m2 = m1.submodules.foo
+        with self.assertRaises(AttributeError, msg="No submodule named 'foo' exists"):
+            m2 = m1.submodules["foo"]
+
     def test_domain_named_implicit(self):
         m = Module()
         m.domains += ClockDomain("sync")
@@ -515,7 +680,7 @@ class DSLTestCase(FHDLTestCase):
         m2.d.sync += self.c3.eq(self.s3)
         m1.submodules.foo = m2
 
-        f1 = m1.lower(platform=None)
+        f1 = m1.elaborate(platform=None)
         self.assertRepr(f1.statements, """
         (
             (eq (sig c1) (sig s1))