hdl.{ast,dsl}, back.{pysim,rtlil}: allow multiple case values.
This means that instead of:
with m.Case(0b00):
<body>
with m.Case(0b01):
<body>
it is legal to write:
with m.Case(0b00, 0b01):
<body>
with no change in semantics, and slightly nicer RTLIL or Verilog
output.
Fixes #103.
This commit is contained in:
parent
48d4ee4031
commit
32446831b4
6 changed files with 73 additions and 55 deletions
|
|
@ -1019,20 +1019,27 @@ class Switch(Statement):
|
|||
def __init__(self, test, cases):
|
||||
self.test = Value.wrap(test)
|
||||
self.cases = OrderedDict()
|
||||
for key, stmts in cases.items():
|
||||
if isinstance(key, (bool, int)):
|
||||
key = "{:0{}b}".format(key, len(self.test))
|
||||
elif isinstance(key, str):
|
||||
pass
|
||||
elif key is None:
|
||||
pass
|
||||
else:
|
||||
raise TypeError("Object '{!r}' cannot be used as a switch key"
|
||||
.format(key))
|
||||
assert key is None or len(key) == len(self.test)
|
||||
for keys, stmts in cases.items():
|
||||
# Map: None -> (); key -> (key,); (key...) -> (key...)
|
||||
if keys is None:
|
||||
keys = ()
|
||||
if not isinstance(keys, tuple):
|
||||
keys = (keys,)
|
||||
# Map: 2 -> "0010"; "0010" -> "0010"
|
||||
new_keys = ()
|
||||
for key in keys:
|
||||
if isinstance(key, (bool, int)):
|
||||
key = "{:0{}b}".format(key, len(self.test))
|
||||
elif isinstance(key, str):
|
||||
pass
|
||||
else:
|
||||
raise TypeError("Object '{!r}' cannot be used as a switch key"
|
||||
.format(key))
|
||||
assert len(key) == len(self.test)
|
||||
new_keys = (*new_keys, key)
|
||||
if not isinstance(stmts, Iterable):
|
||||
stmts = [stmts]
|
||||
self.cases[key] = Statement.wrap(stmts)
|
||||
self.cases[new_keys] = Statement.wrap(stmts)
|
||||
|
||||
def _lhs_signals(self):
|
||||
signals = union((s._lhs_signals() for ss in self.cases.values() for s in ss),
|
||||
|
|
@ -1045,11 +1052,16 @@ class Switch(Statement):
|
|||
return self.test._rhs_signals() | signals
|
||||
|
||||
def __repr__(self):
|
||||
cases = ["(default {})".format(" ".join(map(repr, stmts)))
|
||||
if key is None else
|
||||
"(case {} {})".format(key, " ".join(map(repr, stmts)))
|
||||
for key, stmts in self.cases.items()]
|
||||
return "(switch {!r} {})".format(self.test, " ".join(cases))
|
||||
def case_repr(keys, stmts):
|
||||
stmts_repr = " ".join(map(repr, stmts))
|
||||
if keys == ():
|
||||
return "(default {})".format(stmts_repr)
|
||||
elif len(keys) == 1:
|
||||
return "(case {} {})".format(keys[0], stmts_repr)
|
||||
else:
|
||||
return "(case ({}) {})".format(" ".join(keys), stmts_repr)
|
||||
case_reprs = [case_repr(keys, stmts) for keys, stmts in self.cases.items()]
|
||||
return "(switch {!r} {})".format(self.test, " ".join(case_reprs))
|
||||
|
||||
|
||||
@final
|
||||
|
|
|
|||
|
|
@ -214,27 +214,31 @@ class Module(_ModuleBuilderRoot, Elaboratable):
|
|||
self._pop_ctrl()
|
||||
|
||||
@contextmanager
|
||||
def Case(self, value=None):
|
||||
def Case(self, *values):
|
||||
self._check_context("Case", context="Switch")
|
||||
switch_data = self._get_ctrl("Switch")
|
||||
if value is None:
|
||||
value = "-" * len(switch_data["test"])
|
||||
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"])))
|
||||
omit_case = False
|
||||
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 never be true"
|
||||
.format(value, len(switch_data["test"])), SyntaxWarning, stacklevel=3)
|
||||
omit_case = True
|
||||
new_values = ()
|
||||
for value in values:
|
||||
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 never be true"
|
||||
.format(value, len(switch_data["test"])),
|
||||
SyntaxWarning, stacklevel=3)
|
||||
continue
|
||||
new_values = (*new_values, value)
|
||||
try:
|
||||
_outer_case, self._statements = self._statements, []
|
||||
self._ctrl_context = None
|
||||
yield
|
||||
self._flush_ctrl()
|
||||
if not omit_case:
|
||||
switch_data["cases"][value] = self._statements
|
||||
# If none of the provided cases can possibly be true, omit this branch completely.
|
||||
# This needs to be differentiated from no cases being provided in the first place,
|
||||
# which means the branch will always match.
|
||||
if not (values and not new_values):
|
||||
switch_data["cases"][new_values] = self._statements
|
||||
finally:
|
||||
self._ctrl_context = "Switch"
|
||||
self._statements = _outer_case
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue