hdl.{ast,dsl},back.rtlil: track source locations for switch cases.
This is a very new Yosys feature, and will require a Yosys build newer than YosysHQ/yosys@93bc5aff.
This commit is contained in:
parent
62b3e36612
commit
00c5209a47
|
@ -196,7 +196,8 @@ class _SwitchBuilder(_ProxiedBuilder, _AttrBuilder):
|
||||||
def __exit__(self, *args):
|
def __exit__(self, *args):
|
||||||
self._append("{}end\n", " " * self.indent)
|
self._append("{}end\n", " " * self.indent)
|
||||||
|
|
||||||
def case(self, *values):
|
def case(self, *values, attrs={}, src=""):
|
||||||
|
self._attributes(attrs, src=src, indent=self.indent + 1)
|
||||||
if values == ():
|
if values == ():
|
||||||
self._append("{}case\n", " " * (self.indent + 1))
|
self._append("{}case\n", " " * (self.indent + 1))
|
||||||
else:
|
else:
|
||||||
|
@ -602,10 +603,10 @@ class _StatementCompiler(xfrm.StatementVisitor):
|
||||||
self._has_rhs = False
|
self._has_rhs = False
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def case(self, switch, values):
|
def case(self, switch, values, src=""):
|
||||||
try:
|
try:
|
||||||
old_case = self._case
|
old_case = self._case
|
||||||
with switch.case(*values) as self._case:
|
with switch.case(*values, src=src) as self._case:
|
||||||
yield
|
yield
|
||||||
finally:
|
finally:
|
||||||
self._case = old_case
|
self._case = old_case
|
||||||
|
@ -658,7 +659,11 @@ class _StatementCompiler(xfrm.StatementVisitor):
|
||||||
|
|
||||||
with self._case.switch(test_sigspec, src=src(stmt.src_loc)) as switch:
|
with self._case.switch(test_sigspec, src=src(stmt.src_loc)) as switch:
|
||||||
for values, stmts in stmt.cases.items():
|
for values, stmts in stmt.cases.items():
|
||||||
with self.case(switch, values):
|
if values in stmt.case_src_locs:
|
||||||
|
case_src = src(stmt.case_src_locs[values])
|
||||||
|
else:
|
||||||
|
case_src = ""
|
||||||
|
with self.case(switch, values, src=case_src):
|
||||||
self.on_statements(stmts)
|
self.on_statements(stmts)
|
||||||
|
|
||||||
def on_statement(self, stmt):
|
def on_statement(self, stmt):
|
||||||
|
|
|
@ -1033,18 +1033,22 @@ class Assume(Property):
|
||||||
|
|
||||||
# @final
|
# @final
|
||||||
class Switch(Statement):
|
class Switch(Statement):
|
||||||
def __init__(self, test, cases, *, src_loc=None, src_loc_at=0):
|
def __init__(self, test, cases, *, src_loc=None, src_loc_at=0, case_src_locs={}):
|
||||||
if src_loc is None:
|
if src_loc is None:
|
||||||
super().__init__(src_loc_at=src_loc_at)
|
super().__init__(src_loc_at=src_loc_at)
|
||||||
else:
|
else:
|
||||||
# Switch is a bit special in terms of location tracking because it is usually created
|
# Switch is a bit special in terms of location tracking because it is usually created
|
||||||
# long after the control has left the statement that directly caused its creation.
|
# long after the control has left the statement that directly caused its creation.
|
||||||
self.src_loc = src_loc
|
self.src_loc = src_loc
|
||||||
|
# Switch is also a bit special in that its parts also have location information. It can't
|
||||||
|
# be automatically traced, so whatever constructs a Switch may optionally provide it.
|
||||||
|
self.case_src_locs = {}
|
||||||
|
|
||||||
self.test = Value.wrap(test)
|
self.test = Value.wrap(test)
|
||||||
self.cases = OrderedDict()
|
self.cases = OrderedDict()
|
||||||
for keys, stmts in cases.items():
|
for orig_keys, stmts in cases.items():
|
||||||
# Map: None -> (); key -> (key,); (key...) -> (key...)
|
# Map: None -> (); key -> (key,); (key...) -> (key...)
|
||||||
|
keys = orig_keys
|
||||||
if keys is None:
|
if keys is None:
|
||||||
keys = ()
|
keys = ()
|
||||||
if not isinstance(keys, tuple):
|
if not isinstance(keys, tuple):
|
||||||
|
@ -1064,6 +1068,8 @@ class Switch(Statement):
|
||||||
if not isinstance(stmts, Iterable):
|
if not isinstance(stmts, Iterable):
|
||||||
stmts = [stmts]
|
stmts = [stmts]
|
||||||
self.cases[new_keys] = Statement.wrap(stmts)
|
self.cases[new_keys] = Statement.wrap(stmts)
|
||||||
|
if orig_keys in case_src_locs:
|
||||||
|
self.case_src_locs[new_keys] = case_src_locs[orig_keys]
|
||||||
|
|
||||||
def _lhs_signals(self):
|
def _lhs_signals(self):
|
||||||
signals = union((s._lhs_signals() for ss in self.cases.values() for s in ss),
|
signals = union((s._lhs_signals() for ss in self.cases.values() for s in ss),
|
||||||
|
|
|
@ -161,10 +161,12 @@ class Module(_ModuleBuilderRoot, Elaboratable):
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def If(self, cond):
|
def If(self, cond):
|
||||||
self._check_context("If", context=None)
|
self._check_context("If", context=None)
|
||||||
|
src_loc = tracer.get_src_loc(src_loc_at=1)
|
||||||
if_data = self._set_ctrl("If", {
|
if_data = self._set_ctrl("If", {
|
||||||
"tests": [],
|
"tests": [],
|
||||||
"bodies": [],
|
"bodies": [],
|
||||||
"src_loc": tracer.get_src_loc(src_loc_at=1),
|
"src_loc": src_loc,
|
||||||
|
"src_locs": [],
|
||||||
})
|
})
|
||||||
try:
|
try:
|
||||||
_outer_case, self._statements = self._statements, []
|
_outer_case, self._statements = self._statements, []
|
||||||
|
@ -173,6 +175,7 @@ class Module(_ModuleBuilderRoot, Elaboratable):
|
||||||
self._flush_ctrl()
|
self._flush_ctrl()
|
||||||
if_data["tests"].append(cond)
|
if_data["tests"].append(cond)
|
||||||
if_data["bodies"].append(self._statements)
|
if_data["bodies"].append(self._statements)
|
||||||
|
if_data["src_locs"].append(src_loc)
|
||||||
finally:
|
finally:
|
||||||
self.domain._depth -= 1
|
self.domain._depth -= 1
|
||||||
self._statements = _outer_case
|
self._statements = _outer_case
|
||||||
|
@ -180,6 +183,7 @@ class Module(_ModuleBuilderRoot, Elaboratable):
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def Elif(self, cond):
|
def Elif(self, cond):
|
||||||
self._check_context("Elif", context=None)
|
self._check_context("Elif", context=None)
|
||||||
|
src_loc = tracer.get_src_loc(src_loc_at=1)
|
||||||
if_data = self._get_ctrl("If")
|
if_data = self._get_ctrl("If")
|
||||||
if if_data is None:
|
if if_data is None:
|
||||||
raise SyntaxError("Elif without preceding If")
|
raise SyntaxError("Elif without preceding If")
|
||||||
|
@ -190,6 +194,7 @@ class Module(_ModuleBuilderRoot, Elaboratable):
|
||||||
self._flush_ctrl()
|
self._flush_ctrl()
|
||||||
if_data["tests"].append(cond)
|
if_data["tests"].append(cond)
|
||||||
if_data["bodies"].append(self._statements)
|
if_data["bodies"].append(self._statements)
|
||||||
|
if_data["src_locs"].append(src_loc)
|
||||||
finally:
|
finally:
|
||||||
self.domain._depth -= 1
|
self.domain._depth -= 1
|
||||||
self._statements = _outer_case
|
self._statements = _outer_case
|
||||||
|
@ -197,6 +202,7 @@ class Module(_ModuleBuilderRoot, Elaboratable):
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def Else(self):
|
def Else(self):
|
||||||
self._check_context("Else", context=None)
|
self._check_context("Else", context=None)
|
||||||
|
src_loc = tracer.get_src_loc(src_loc_at=1)
|
||||||
if_data = self._get_ctrl("If")
|
if_data = self._get_ctrl("If")
|
||||||
if if_data is None:
|
if if_data is None:
|
||||||
raise SyntaxError("Else without preceding If/Elif")
|
raise SyntaxError("Else without preceding If/Elif")
|
||||||
|
@ -206,6 +212,7 @@ class Module(_ModuleBuilderRoot, Elaboratable):
|
||||||
yield
|
yield
|
||||||
self._flush_ctrl()
|
self._flush_ctrl()
|
||||||
if_data["bodies"].append(self._statements)
|
if_data["bodies"].append(self._statements)
|
||||||
|
if_data["src_locs"].append(src_loc)
|
||||||
finally:
|
finally:
|
||||||
self.domain._depth -= 1
|
self.domain._depth -= 1
|
||||||
self._statements = _outer_case
|
self._statements = _outer_case
|
||||||
|
@ -218,6 +225,7 @@ class Module(_ModuleBuilderRoot, Elaboratable):
|
||||||
"test": Value.wrap(test),
|
"test": Value.wrap(test),
|
||||||
"cases": OrderedDict(),
|
"cases": OrderedDict(),
|
||||||
"src_loc": tracer.get_src_loc(src_loc_at=1),
|
"src_loc": tracer.get_src_loc(src_loc_at=1),
|
||||||
|
"case_src_locs": {},
|
||||||
})
|
})
|
||||||
try:
|
try:
|
||||||
self._ctrl_context = "Switch"
|
self._ctrl_context = "Switch"
|
||||||
|
@ -231,6 +239,7 @@ class Module(_ModuleBuilderRoot, Elaboratable):
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def Case(self, *values):
|
def Case(self, *values):
|
||||||
self._check_context("Case", context="Switch")
|
self._check_context("Case", context="Switch")
|
||||||
|
src_loc = tracer.get_src_loc(src_loc_at=1)
|
||||||
switch_data = self._get_ctrl("Switch")
|
switch_data = self._get_ctrl("Switch")
|
||||||
new_values = ()
|
new_values = ()
|
||||||
for value in values:
|
for value in values:
|
||||||
|
@ -254,6 +263,7 @@ class Module(_ModuleBuilderRoot, Elaboratable):
|
||||||
# which means the branch will always match.
|
# which means the branch will always match.
|
||||||
if not (values and not new_values):
|
if not (values and not new_values):
|
||||||
switch_data["cases"][new_values] = self._statements
|
switch_data["cases"][new_values] = self._statements
|
||||||
|
switch_data["case_src_locs"][new_values] = src_loc
|
||||||
finally:
|
finally:
|
||||||
self._ctrl_context = "Switch"
|
self._ctrl_context = "Switch"
|
||||||
self._statements = _outer_case
|
self._statements = _outer_case
|
||||||
|
@ -272,6 +282,7 @@ class Module(_ModuleBuilderRoot, Elaboratable):
|
||||||
"decoding": OrderedDict(),
|
"decoding": OrderedDict(),
|
||||||
"states": OrderedDict(),
|
"states": OrderedDict(),
|
||||||
"src_loc": tracer.get_src_loc(src_loc_at=1),
|
"src_loc": tracer.get_src_loc(src_loc_at=1),
|
||||||
|
"state_src_locs": {},
|
||||||
})
|
})
|
||||||
self._generated[name] = fsm = \
|
self._generated[name] = fsm = \
|
||||||
FSM(fsm_data["signal"], fsm_data["encoding"], fsm_data["decoding"])
|
FSM(fsm_data["signal"], fsm_data["encoding"], fsm_data["decoding"])
|
||||||
|
@ -287,6 +298,7 @@ class Module(_ModuleBuilderRoot, Elaboratable):
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def State(self, name):
|
def State(self, name):
|
||||||
self._check_context("FSM State", context="FSM")
|
self._check_context("FSM State", context="FSM")
|
||||||
|
src_loc = tracer.get_src_loc(src_loc_at=1)
|
||||||
fsm_data = self._get_ctrl("FSM")
|
fsm_data = self._get_ctrl("FSM")
|
||||||
if name in fsm_data["states"]:
|
if name in fsm_data["states"]:
|
||||||
raise SyntaxError("FSM state '{}' is already defined".format(name))
|
raise SyntaxError("FSM state '{}' is already defined".format(name))
|
||||||
|
@ -298,6 +310,7 @@ class Module(_ModuleBuilderRoot, Elaboratable):
|
||||||
yield
|
yield
|
||||||
self._flush_ctrl()
|
self._flush_ctrl()
|
||||||
fsm_data["states"][name] = self._statements
|
fsm_data["states"][name] = self._statements
|
||||||
|
fsm_data["state_src_locs"][name] = src_loc
|
||||||
finally:
|
finally:
|
||||||
self._ctrl_context = "FSM"
|
self._ctrl_context = "FSM"
|
||||||
self._statements = _outer_case
|
self._statements = _outer_case
|
||||||
|
@ -327,6 +340,7 @@ class Module(_ModuleBuilderRoot, Elaboratable):
|
||||||
|
|
||||||
if name == "If":
|
if name == "If":
|
||||||
if_tests, if_bodies = data["tests"], data["bodies"]
|
if_tests, if_bodies = data["tests"], data["bodies"]
|
||||||
|
if_src_locs = data["src_locs"]
|
||||||
|
|
||||||
tests, cases = [], OrderedDict()
|
tests, cases = [], OrderedDict()
|
||||||
for if_test, if_case in zip(if_tests + [None], if_bodies):
|
for if_test, if_case in zip(if_tests + [None], if_bodies):
|
||||||
|
@ -342,16 +356,20 @@ class Module(_ModuleBuilderRoot, Elaboratable):
|
||||||
match = None
|
match = None
|
||||||
cases[match] = if_case
|
cases[match] = if_case
|
||||||
|
|
||||||
self._statements.append(Switch(Cat(tests), cases, src_loc=src_loc))
|
self._statements.append(Switch(Cat(tests), cases,
|
||||||
|
src_loc=src_loc, case_src_locs=dict(zip(cases, if_src_locs))))
|
||||||
|
|
||||||
if name == "Switch":
|
if name == "Switch":
|
||||||
switch_test, switch_cases = data["test"], data["cases"]
|
switch_test, switch_cases = data["test"], data["cases"]
|
||||||
|
switch_case_src_locs = data["case_src_locs"]
|
||||||
|
|
||||||
self._statements.append(Switch(switch_test, switch_cases, src_loc=src_loc))
|
self._statements.append(Switch(switch_test, switch_cases,
|
||||||
|
src_loc=src_loc, case_src_locs=switch_case_src_locs))
|
||||||
|
|
||||||
if name == "FSM":
|
if name == "FSM":
|
||||||
fsm_signal, fsm_reset, fsm_encoding, fsm_decoding, fsm_states = \
|
fsm_signal, fsm_reset, fsm_encoding, fsm_decoding, fsm_states = \
|
||||||
data["signal"], data["reset"], data["encoding"], data["decoding"], data["states"]
|
data["signal"], data["reset"], data["encoding"], data["decoding"], data["states"]
|
||||||
|
fsm_state_src_locs = data["state_src_locs"]
|
||||||
if not fsm_states:
|
if not fsm_states:
|
||||||
return
|
return
|
||||||
fsm_signal.nbits = bits_for(len(fsm_encoding) - 1)
|
fsm_signal.nbits = bits_for(len(fsm_encoding) - 1)
|
||||||
|
@ -364,7 +382,8 @@ class Module(_ModuleBuilderRoot, Elaboratable):
|
||||||
fsm_signal.decoder = lambda n: "{}/{}".format(fsm_decoding[n], n)
|
fsm_signal.decoder = lambda n: "{}/{}".format(fsm_decoding[n], n)
|
||||||
self._statements.append(Switch(fsm_signal,
|
self._statements.append(Switch(fsm_signal,
|
||||||
OrderedDict((fsm_encoding[name], stmts) for name, stmts in fsm_states.items()),
|
OrderedDict((fsm_encoding[name], stmts) for name, stmts in fsm_states.items()),
|
||||||
src_loc=src_loc))
|
src_loc=src_loc, case_src_locs={fsm_encoding[name]: fsm_state_src_locs[name]
|
||||||
|
for name in fsm_states}))
|
||||||
|
|
||||||
def _add_statement(self, assigns, domain, depth, compat_mode=False):
|
def _add_statement(self, assigns, domain, depth, compat_mode=False):
|
||||||
def domain_name(domain):
|
def domain_name(domain):
|
||||||
|
|
|
@ -216,6 +216,8 @@ class StatementVisitor(metaclass=ABCMeta):
|
||||||
new_stmt = self.on_unknown_statement(stmt)
|
new_stmt = self.on_unknown_statement(stmt)
|
||||||
if isinstance(new_stmt, Statement) and self.replace_statement_src_loc(stmt, new_stmt):
|
if isinstance(new_stmt, Statement) and self.replace_statement_src_loc(stmt, new_stmt):
|
||||||
new_stmt.src_loc = stmt.src_loc
|
new_stmt.src_loc = stmt.src_loc
|
||||||
|
if isinstance(new_stmt, Switch) and isinstance(stmt, Switch):
|
||||||
|
new_stmt.case_src_locs = stmt.case_src_locs
|
||||||
return new_stmt
|
return new_stmt
|
||||||
|
|
||||||
def __call__(self, stmt):
|
def __call__(self, stmt):
|
||||||
|
|
Loading…
Reference in a new issue