back.rtlil: emit \src attributes for processes via Switch and Assign.
authorwhitequark <whitequark@whitequark.org>
Wed, 3 Jul 2019 16:27:54 +0000 (16:27 +0000)
committerwhitequark <whitequark@whitequark.org>
Wed, 3 Jul 2019 16:27:54 +0000 (16:27 +0000)
The locations are unfortunately not very precise, but they provide
some improvement over status quo.

nmigen/back/rtlil.py
nmigen/hdl/ast.py
nmigen/hdl/dsl.py
nmigen/hdl/xfrm.py

index 0a789a2ded1f592101ba756eed5544a1ce5008d9..3a6777c81d9f9c7d8f84e08de8323947f3b51b57 100644 (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,
index 7a2623515dd28fd98fd24b0c3e3632559ad350c1..622c0fd04659886e98531d7835453f74f817cad9 100644 (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():
index a3beeefe9e112ea64406e3c1573b45656f0046e9..5c39482f5977eae50317a0cb75d0d919a82988c5 100644 (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):
index 53b3344a4a5acd1f586f0c419f3d469ef4d5f195..93e5d0a6cb5569af1abbe7724f3b6127331b8900 100644 (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):