hdl.dsl: accept (but warn on) cases wider than switch test value.

Fixes #13.
This commit is contained in:
whitequark 2019-01-13 08:46:28 +00:00
parent cbf7bd6e31
commit 3083c1d6dd
3 changed files with 26 additions and 3 deletions

View file

@ -882,6 +882,7 @@ class Switch(Statement):
for key, stmts in cases.items(): for key, stmts in cases.items():
if isinstance(key, (bool, int)): if isinstance(key, (bool, int)):
key = "{:0{}b}".format(key, len(self.test)) key = "{:0{}b}".format(key, len(self.test))
assert len(key) <= len(self.test)
elif isinstance(key, str): elif isinstance(key, str):
assert len(key) == len(self.test) assert len(key) == len(self.test)
else: else:

View file

@ -1,6 +1,7 @@
from collections import OrderedDict, namedtuple from collections import OrderedDict, namedtuple
from collections.abc import Iterable from collections.abc import Iterable
from contextlib import contextmanager from contextlib import contextmanager
import warnings
from ..tools import flatten, bits_for from ..tools import flatten, bits_for
from .ast import * from .ast import *
@ -8,13 +9,17 @@ from .ir import *
from .xfrm import * from .xfrm import *
__all__ = ["Module", "SyntaxError"] __all__ = ["Module", "SyntaxError", "SyntaxWarning"]
class SyntaxError(Exception): class SyntaxError(Exception):
pass pass
class SyntaxWarning(Warning):
pass
class _ModuleBuilderProxy: class _ModuleBuilderProxy:
def __init__(self, builder, depth): def __init__(self, builder, depth):
object.__setattr__(self, "_builder", builder) object.__setattr__(self, "_builder", builder)
@ -195,7 +200,7 @@ class Module(_ModuleBuilderRoot):
@contextmanager @contextmanager
def Switch(self, test): def Switch(self, test):
self._check_context("Switch", context=None) 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: try:
self._ctrl_context = "Switch" self._ctrl_context = "Switch"
self.domain._depth += 1 self.domain._depth += 1
@ -211,9 +216,14 @@ class Module(_ModuleBuilderRoot):
switch_data = self._get_ctrl("Switch") switch_data = self._get_ctrl("Switch")
if value is None: if value is None:
value = "-" * len(switch_data["test"]) 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 {})" raise SyntaxError("Case value '{}' must have the same width as test (which is {})"
.format(value, len(switch_data["test"]))) .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: try:
_outer_case, self._statements = self._statements, [] _outer_case, self._statements = self._statements, []
self._ctrl_context = None self._ctrl_context = None

View file

@ -287,6 +287,18 @@ class DSLTestCase(FHDLTestCase):
msg="Case value '--' must have the same width as test (which is 4)"): msg="Case value '--' must have the same width as test (which is 4)"):
with m.Case("--"): with m.Case("--"):
pass 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): def test_Case_outside_Switch_wrong(self):
m = Module() m = Module()