hdl.ast: normalize case values to two's complement, not signed binary.
authorwhitequark <whitequark@whitequark.org>
Sat, 12 Dec 2020 12:42:12 +0000 (12:42 +0000)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Fri, 31 Dec 2021 15:24:29 +0000 (15:24 +0000)
This was an especially insidious bug because the minus character is
valid in case values but has a completely different meaning (wildcard
rather than sign).

Fixes #559.

nmigen/hdl/ast.py
tests/test_hdl_ast.py

index 165e67d8e9b78328e727aee50f21aefe35798964..5cdbf6ee3ae845bbdd969ede462276e6f4851c18 100644 (file)
@@ -1499,13 +1499,14 @@ class Switch(Statement):
                 keys = (keys,)
             # Map: 2 -> "0010"; "0010" -> "0010"
             new_keys = ()
+            key_mask = (1 << len(self.test)) - 1
             for key in keys:
                 if isinstance(key, str):
                     key = "".join(key.split()) # remove whitespace
                 elif isinstance(key, int):
-                    key = format(key, "b").rjust(len(self.test), "0")
+                    key = format(key & key_mask, "b").rjust(len(self.test), "0")
                 elif isinstance(key, Enum):
-                    key = format(key.value, "b").rjust(len(self.test), "0")
+                    key = format(key.value & key_mask, "b").rjust(len(self.test), "0")
                 else:
                     raise TypeError("Object {!r} cannot be used as a switch key"
                                     .format(key))
index 8565fad54427f18de9f7c46a9b8848599b2f24f3..af073a984fdc97fd55b88ccb7adac9226fde113e 100644 (file)
@@ -1107,3 +1107,29 @@ class InitialTestCase(FHDLTestCase):
     def test_initial(self):
         i = Initial()
         self.assertEqual(i.shape(), unsigned(1))
+
+
+class SwitchTestCase(FHDLTestCase):
+    def test_default_case(self):
+        s = Switch(Const(0), {None: []})
+        self.assertEqual(s.cases, {(): []})
+
+    def test_int_case(self):
+        s = Switch(Const(0, 8), {10: []})
+        self.assertEqual(s.cases, {("00001010",): []})
+
+    def test_int_neg_case(self):
+        s = Switch(Const(0, 8), {-10: []})
+        self.assertEqual(s.cases, {("11110110",): []})
+
+    def test_enum_case(self):
+        s = Switch(Const(0, UnsignedEnum), {UnsignedEnum.FOO: []})
+        self.assertEqual(s.cases, {("01",): []})
+
+    def test_str_case(self):
+        s = Switch(Const(0, 8), {"0000 11\t01": []})
+        self.assertEqual(s.cases, {("00001101",): []})
+
+    def test_two_cases(self):
+        s = Switch(Const(0, 8), {("00001111", 123): []})
+        self.assertEqual(s.cases, {("00001111", "01111011"): []})