back.rtlil: emit \src attributes for processes via Switch and Assign.

The locations are unfortunately not very precise, but they provide
some improvement over status quo.
This commit is contained in:
whitequark 2019-07-03 16:27:54 +00:00
parent e351e27206
commit 82903e493a
4 changed files with 57 additions and 13 deletions

View file

@ -55,9 +55,9 @@ class _Bufferer:
self._append("{}attribute \\{} {}\n",
" " * indent, name, int(value))
def _src(self, src):
def _src(self, src, **kwargs):
if src:
self.attribute("src", src)
self.attribute("src", src, **kwargs)
class _Builder(_Namer, _Bufferer):
@ -142,7 +142,7 @@ class _ProcessBuilder(_Bufferer):
self.src = src
def __enter__(self):
self._src(self.src)
self._src(self.src, indent=1)
self._append(" process {}\n", self.name)
return self
@ -222,6 +222,10 @@ def src(src_loc):
return "{}:{}".format(file, line)
def srcs(src_locs):
return "|".join(sorted(map(src, src_locs)))
class LegalizeValue(Exception):
def __init__(self, value, branches):
self.value = value
@ -579,6 +583,34 @@ class _LHSValueCompiler(_ValueCompiler):
raise TypeError # :nocov:
class _StatementLocator(xfrm.StatementVisitor):
def __init__(self):
self.src_locs = set()
def on_Assign(self, stmt):
self.src_locs.add(stmt.src_loc)
def on_Switch(self, stmt):
self.src_locs.add(stmt.src_loc)
for stmts in stmt.cases.values():
self.on_statements(stmts)
def on_ignored(self, stmt):
pass
on_Assert = on_ignored
on_Assume = on_ignored
def on_statements(self, stmts):
for stmt in stmts:
self.on_statement(stmt)
def __call__(self, stmt):
self.on_statement(stmt)
src_locs, self.src_locs = self.src_locs, set()
return src_locs
class _StatementCompiler(xfrm.StatementVisitor):
def __init__(self, state, rhs_compiler, lhs_compiler):
self.state = state
@ -689,6 +721,7 @@ def convert_fragment(builder, fragment, hierarchy):
compiler_state = _ValueCompilerState(module)
rhs_compiler = _RHSValueCompiler(compiler_state)
lhs_compiler = _LHSValueCompiler(compiler_state)
stmt_locator = _StatementLocator()
stmt_compiler = _StatementCompiler(compiler_state, rhs_compiler, lhs_compiler)
verilog_trigger = None
@ -778,8 +811,10 @@ def convert_fragment(builder, fragment, hierarchy):
for group, group_signals in lhs_grouper.groups().items():
lhs_group_filter = xfrm.LHSGroupFilter(group_signals)
group_stmts = lhs_group_filter(fragment.statements)
with module.process(name="$group_{}".format(group)) as process:
with module.process(name="$group_{}".format(group),
src=srcs(stmt_locator(group_stmts))) as process:
with process.case() as case:
# For every signal in comb domain, assign \sig$next to the reset value.
# For every signal in sync domains, assign \sig$next to the current
@ -796,7 +831,7 @@ def convert_fragment(builder, fragment, hierarchy):
# Convert statements into decision trees.
stmt_compiler._case = case
stmt_compiler._has_rhs = False
stmt_compiler(lhs_group_filter(fragment.statements))
stmt_compiler(group_stmts)
# Verilog `always @*` blocks will not run if `*` does not match anything, i.e.
# if the implicit sensitivity list is empty. We check this while translating,

View file

@ -178,7 +178,7 @@ class Value(metaclass=ABCMeta):
Assign
Assignment statement that can be used in combinatorial or synchronous context.
"""
return Assign(self, value)
return Assign(self, value, src_loc_at=1)
@abstractmethod
def shape(self):
@ -975,7 +975,9 @@ class Statement:
@final
class Assign(Statement):
def __init__(self, lhs, rhs):
def __init__(self, lhs, rhs, src_loc_at=0):
self.src_loc = tracer.get_src_loc(src_loc_at)
self.lhs = Value.wrap(lhs)
self.rhs = Value.wrap(rhs)
@ -1027,7 +1029,9 @@ class Assume(Property):
# @final
class Switch(Statement):
def __init__(self, test, cases):
def __init__(self, test, cases, src_loc_at=0):
self.src_loc = tracer.get_src_loc(src_loc_at)
self.test = Value.wrap(test)
self.cases = OrderedDict()
for keys, stmts in cases.items():

View file

@ -304,6 +304,10 @@ class Module(_ModuleBuilderRoot, Elaboratable):
raise SyntaxError("`m.next = <...>` is only permitted inside an FSM state")
def _pop_ctrl(self):
# FIXME: the src_loc extraction unfortunately doesn't work very well here; src_loc_at=3 is
# correct, but the resulting src_loc points at the *last* line of the `with` block.
# Unfortunately, it is not clear how this can be fixed.
name, data = self._ctrl_stack.pop()
if name == "If":
@ -323,12 +327,12 @@ class Module(_ModuleBuilderRoot, Elaboratable):
match = None
cases[match] = if_case
self._statements.append(Switch(Cat(tests), cases))
self._statements.append(Switch(Cat(tests), cases, src_loc_at=3))
if name == "Switch":
switch_test, switch_cases = data["test"], data["cases"]
self._statements.append(Switch(switch_test, switch_cases))
self._statements.append(Switch(switch_test, switch_cases, src_loc_at=3))
if name == "FSM":
fsm_signal, fsm_reset, fsm_encoding, fsm_decoding, fsm_states = \
@ -342,7 +346,8 @@ class Module(_ModuleBuilderRoot, Elaboratable):
fsm_decoding.update((n, s) for s, n in fsm_encoding.items())
fsm_signal.decoder = lambda n: "{}/{}".format(fsm_decoding[n], n)
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_at=3))
def _add_statement(self, assigns, domain, depth, compat_mode=False):
def domain_name(domain):

View file

@ -211,8 +211,8 @@ class StatementVisitor(metaclass=ABCMeta):
new_stmt.src_loc = stmt.src_loc
return new_stmt
def __call__(self, value):
return self.on_statement(value)
def __call__(self, stmt):
return self.on_statement(stmt)
class StatementTransformer(StatementVisitor):