Remove everything deprecated in nmigen 0.1.
[nmigen.git] / nmigen / test / test_hdl_dsl.py
index 43f83d28813cd88065074dfbd2b7060d9c1b8122..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())
@@ -268,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):
@@ -285,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):
@@ -302,6 +334,23 @@ class DSLTestCase(FHDLTestCase):
         )
         """)
 
+    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):
@@ -316,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, """
@@ -334,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,
@@ -458,6 +540,13 @@ class DSLTestCase(FHDLTestCase):
         ()
         """)
 
+    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():
@@ -510,36 +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._submodules, [(m2, "foo")])
+        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 .elaborate(), 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 .elaborate(), 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")