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", self._append("{}attribute \\{} {}\n",
" " * indent, name, int(value)) " " * indent, name, int(value))
def _src(self, src): def _src(self, src, **kwargs):
if src: if src:
self.attribute("src", src) self.attribute("src", src, **kwargs)
class _Builder(_Namer, _Bufferer): class _Builder(_Namer, _Bufferer):
@ -142,7 +142,7 @@ class _ProcessBuilder(_Bufferer):
self.src = src self.src = src
def __enter__(self): def __enter__(self):
self._src(self.src) self._src(self.src, indent=1)
self._append(" process {}\n", self.name) self._append(" process {}\n", self.name)
return self return self
@ -222,6 +222,10 @@ def src(src_loc):
return "{}:{}".format(file, line) return "{}:{}".format(file, line)
def srcs(src_locs):
return "|".join(sorted(map(src, src_locs)))
class LegalizeValue(Exception): class LegalizeValue(Exception):
def __init__(self, value, branches): def __init__(self, value, branches):
self.value = value self.value = value
@ -579,6 +583,34 @@ class _LHSValueCompiler(_ValueCompiler):
raise TypeError # :nocov: 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): class _StatementCompiler(xfrm.StatementVisitor):
def __init__(self, state, rhs_compiler, lhs_compiler): def __init__(self, state, rhs_compiler, lhs_compiler):
self.state = state self.state = state
@ -689,6 +721,7 @@ def convert_fragment(builder, fragment, hierarchy):
compiler_state = _ValueCompilerState(module) compiler_state = _ValueCompilerState(module)
rhs_compiler = _RHSValueCompiler(compiler_state) rhs_compiler = _RHSValueCompiler(compiler_state)
lhs_compiler = _LHSValueCompiler(compiler_state) lhs_compiler = _LHSValueCompiler(compiler_state)
stmt_locator = _StatementLocator()
stmt_compiler = _StatementCompiler(compiler_state, rhs_compiler, lhs_compiler) stmt_compiler = _StatementCompiler(compiler_state, rhs_compiler, lhs_compiler)
verilog_trigger = None verilog_trigger = None
@ -778,8 +811,10 @@ def convert_fragment(builder, fragment, hierarchy):
for group, group_signals in lhs_grouper.groups().items(): for group, group_signals in lhs_grouper.groups().items():
lhs_group_filter = xfrm.LHSGroupFilter(group_signals) 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: with process.case() as case:
# For every signal in comb domain, assign \sig$next to the reset value. # 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 # 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. # Convert statements into decision trees.
stmt_compiler._case = case stmt_compiler._case = case
stmt_compiler._has_rhs = False 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. # 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, # if the implicit sensitivity list is empty. We check this while translating,

View file

@ -178,7 +178,7 @@ class Value(metaclass=ABCMeta):
Assign Assign
Assignment statement that can be used in combinatorial or synchronous context. Assignment statement that can be used in combinatorial or synchronous context.
""" """
return Assign(self, value) return Assign(self, value, src_loc_at=1)
@abstractmethod @abstractmethod
def shape(self): def shape(self):
@ -975,7 +975,9 @@ class Statement:
@final @final
class Assign(Statement): 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.lhs = Value.wrap(lhs)
self.rhs = Value.wrap(rhs) self.rhs = Value.wrap(rhs)
@ -1027,7 +1029,9 @@ class Assume(Property):
# @final # @final
class Switch(Statement): 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.test = Value.wrap(test)
self.cases = OrderedDict() self.cases = OrderedDict()
for keys, stmts in cases.items(): 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") raise SyntaxError("`m.next = <...>` is only permitted inside an FSM state")
def _pop_ctrl(self): 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() name, data = self._ctrl_stack.pop()
if name == "If": if name == "If":
@ -323,12 +327,12 @@ class Module(_ModuleBuilderRoot, Elaboratable):
match = None match = None
cases[match] = if_case 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": if name == "Switch":
switch_test, switch_cases = data["test"], data["cases"] 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": if name == "FSM":
fsm_signal, fsm_reset, fsm_encoding, fsm_decoding, fsm_states = \ 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_decoding.update((n, s) for s, n in fsm_encoding.items())
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_at=3))
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):

View file

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