hdl.dsl: make referencing undefined FSM states an error.

Before this commit, doing something like:

    with m.FSM():
        with m.State("FOO"):
            m.next = "bAR"
        with m.State("BAR"):
            m.next = "FOO"

would silently create an empty state `bAR` and get stuck in it until
the module is reset. This was done intentionally (in Migen, this code
would in fact miscompile), but in retrospect was clearly a bad idea;
it turns typos into bugs, while in the rare case that branching to
a completely empty state is desired, it is trivial to define one.

Fixes #315.
This commit is contained in:
whitequark 2020-02-06 17:47:46 +00:00
parent 97cc78a3db
commit a1c58633e6
2 changed files with 13 additions and 2 deletions

View file

@ -375,6 +375,10 @@ class Module(_ModuleBuilderRoot, Elaboratable):
self._ctrl_context = "FSM"
self.domain._depth += 1
yield fsm
for state_name in fsm_data["encoding"]:
if state_name not in fsm_data["states"]:
raise NameError("FSM state '{}' is referenced but not defined"
.format(state_name))
finally:
self.domain._depth -= 1
self._ctrl_context = None
@ -386,7 +390,7 @@ class Module(_ModuleBuilderRoot, Elaboratable):
src_loc = tracer.get_src_loc(src_loc_at=1)
fsm_data = self._get_ctrl("FSM")
if name in fsm_data["states"]:
raise SyntaxError("FSM state '{}' is already defined".format(name))
raise NameError("FSM state '{}' is already defined".format(name))
if name not in fsm_data["encoding"]:
fsm_data["encoding"][name] = len(fsm_data["encoding"])
try:

View file

@ -582,12 +582,19 @@ class DSLTestCase(FHDLTestCase):
with m.FSM(domain="comb"):
pass
def test_FSM_wrong_undefined(self):
m = Module()
with self.assertRaises(NameError,
msg="FSM state 'FOO' is referenced but not defined"):
with m.FSM() as fsm:
fsm.ongoing("FOO")
def test_FSM_wrong_redefined(self):
m = Module()
with m.FSM():
with m.State("FOO"):
pass
with self.assertRaises(SyntaxError,
with self.assertRaises(NameError,
msg="FSM state 'FOO' is already defined"):
with m.State("FOO"):
pass