hdl.dsl: accept (but warn on) cases wider than switch test value.
authorwhitequark <cz@m-labs.hk>
Sun, 13 Jan 2019 08:46:28 +0000 (08:46 +0000)
committerwhitequark <cz@m-labs.hk>
Sun, 13 Jan 2019 08:46:28 +0000 (08:46 +0000)
Fixes #13.

nmigen/hdl/ast.py
nmigen/hdl/dsl.py
nmigen/test/test_hdl_dsl.py

index 56c61ca8e2c830d0025749378438bcd789e4f54e..c6dee0303ef6dde398cde12e2026621008d6ddd7 100644 (file)
@@ -882,6 +882,7 @@ class Switch(Statement):
         for key, stmts in cases.items():
             if isinstance(key, (bool, int)):
                 key = "{:0{}b}".format(key, len(self.test))
+                assert len(key) <= len(self.test)
             elif isinstance(key, str):
                 assert len(key) == len(self.test)
             else:
index 968f396a49fe8a47935cfaf7f8aabfc75315dd75..de0231b4649370c28e74fcfea3f8ec72992aa090 100644 (file)
@@ -1,6 +1,7 @@
 from collections import OrderedDict, namedtuple
 from collections.abc import Iterable
 from contextlib import contextmanager
+import warnings
 
 from ..tools import flatten, bits_for
 from .ast import *
@@ -8,13 +9,17 @@ from .ir import *
 from .xfrm import *
 
 
-__all__ = ["Module", "SyntaxError"]
+__all__ = ["Module", "SyntaxError", "SyntaxWarning"]
 
 
 class SyntaxError(Exception):
     pass
 
 
+class SyntaxWarning(Warning):
+    pass
+
+
 class _ModuleBuilderProxy:
     def __init__(self, builder, depth):
         object.__setattr__(self, "_builder", builder)
@@ -195,7 +200,7 @@ class Module(_ModuleBuilderRoot):
     @contextmanager
     def Switch(self, test):
         self._check_context("Switch", context=None)
-        switch_data = self._set_ctrl("Switch", {"test": test, "cases": OrderedDict()})
+        switch_data = self._set_ctrl("Switch", {"test": Value.wrap(test), "cases": OrderedDict()})
         try:
             self._ctrl_context = "Switch"
             self.domain._depth += 1
@@ -211,9 +216,14 @@ class Module(_ModuleBuilderRoot):
         switch_data = self._get_ctrl("Switch")
         if value is None:
             value = "-" * len(switch_data["test"])
-        if isinstance(value, str) and len(switch_data["test"]) != len(value):
+        if isinstance(value, str) and len(value) != len(switch_data["test"]):
             raise SyntaxError("Case value '{}' must have the same width as test (which is {})"
                               .format(value, len(switch_data["test"])))
+        if isinstance(value, int) and bits_for(value) > len(switch_data["test"]):
+            warnings.warn("Case value '{:b}' is wider than test (which has width {}); "
+                          "comparison will be made against truncated value"
+                          .format(value, len(switch_data["test"])), SyntaxWarning, stacklevel=3)
+            value &= (1 << len(switch_data["test"])) - 1
         try:
             _outer_case, self._statements = self._statements, []
             self._ctrl_context = None
index 2a33864d27557ef71d3f9ff959839ed979ccb4f3..3e3df1d1102cc825eb425c6558ea11f00641d742 100644 (file)
@@ -287,6 +287,18 @@ class DSLTestCase(FHDLTestCase):
                     msg="Case value '--' must have the same width as test (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 be made against truncated value"):
+                with m.Case(0b10110):
+                    pass
+        self.assertRepr(m._statements, """
+        (
+            (switch (sig w1)
+                (case 0110 )
+            )
+        )
+        """)
 
     def test_Case_outside_Switch_wrong(self):
         m = Module()