hdl.{dsl,mem,xfrm}: inject appropriate source locations.
authorwhitequark <whitequark@whitequark.org>
Mon, 8 Jul 2019 09:50:07 +0000 (09:50 +0000)
committerwhitequark <whitequark@whitequark.org>
Mon, 8 Jul 2019 09:58:12 +0000 (09:58 +0000)
This primarily fixes the problem with source location precision in
Module (which used to trace locations from __exit__ of the context
managers, by which point everything interesting has been lost), but
also improves memory port and control inserter source locations.

On the sample of examples/basic/*.py, the only incorrectly inferred
remaining location is clk pointing to hdl/mem.py:166.

nmigen/hdl/ast.py
nmigen/hdl/dsl.py
nmigen/hdl/mem.py
nmigen/hdl/xfrm.py

index 9a88fa334d9fc15c4438f75d001c90a28ee8dfc3..bf09077552c9675636a502f8470bdb666bce9f17 100644 (file)
@@ -1029,8 +1029,14 @@ class Assume(Property):
 
 # @final
 class Switch(Statement):
-    def __init__(self, test, cases, *, src_loc_at=0):
-        super().__init__(src_loc_at=src_loc_at)
+    def __init__(self, test, cases, *, src_loc=None, src_loc_at=0):
+        if src_loc is None:
+            super().__init__(src_loc_at=src_loc_at)
+        else:
+            # 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.
+            self.src_loc = src_loc
+
         self.test  = Value.wrap(test)
         self.cases = OrderedDict()
         for keys, stmts in cases.items():
index 43da83a49aa2c0663f4c962ce90c622492b7ab97..bd6399a36692c8de9e1e9b94dab9cc377b593f17 100644 (file)
@@ -4,6 +4,7 @@ from contextlib import contextmanager
 import warnings
 
 from ..tools import flatten, bits_for, deprecated
+from .. import tracer
 from .ast import *
 from .ir import *
 from .xfrm import *
@@ -109,7 +110,7 @@ class FSM:
     def ongoing(self, name):
         if name not in self.encoding:
             self.encoding[name] = len(self.encoding)
-        return self.state == self.encoding[name]
+        return Operator("==", [self.state, self.encoding[name]], src_loc_at=0)
 
 
 class Module(_ModuleBuilderRoot, Elaboratable):
@@ -160,7 +161,11 @@ class Module(_ModuleBuilderRoot, Elaboratable):
     @contextmanager
     def If(self, cond):
         self._check_context("If", context=None)
-        if_data = self._set_ctrl("If", {"tests": [], "bodies": []})
+        if_data = self._set_ctrl("If", {
+            "tests":   [],
+            "bodies":  [],
+            "src_loc": tracer.get_src_loc(src_loc_at=1),
+        })
         try:
             _outer_case, self._statements = self._statements, []
             self.domain._depth += 1
@@ -209,7 +214,11 @@ class Module(_ModuleBuilderRoot, Elaboratable):
     @contextmanager
     def Switch(self, test):
         self._check_context("Switch", context=None)
-        switch_data = self._set_ctrl("Switch", {"test": Value.wrap(test), "cases": OrderedDict()})
+        switch_data = self._set_ctrl("Switch", {
+            "test":    Value.wrap(test),
+            "cases":   OrderedDict(),
+            "src_loc": tracer.get_src_loc(src_loc_at=1),
+        })
         try:
             self._ctrl_context = "Switch"
             self.domain._depth += 1
@@ -260,6 +269,7 @@ class Module(_ModuleBuilderRoot, Elaboratable):
             "encoding": OrderedDict(),
             "decoding": OrderedDict(),
             "states":   OrderedDict(),
+            "src_loc":  tracer.get_src_loc(src_loc_at=1),
         })
         self._generated[name] = fsm = \
             FSM(fsm_data["signal"], fsm_data["encoding"], fsm_data["decoding"])
@@ -310,11 +320,8 @@ 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()
+        src_loc = data["src_loc"]
 
         if name == "If":
             if_tests, if_bodies = data["tests"], data["bodies"]
@@ -333,12 +340,12 @@ class Module(_ModuleBuilderRoot, Elaboratable):
                     match = None
                 cases[match] = if_case
 
-            self._statements.append(Switch(Cat(tests), cases, src_loc_at=3))
+            self._statements.append(Switch(Cat(tests), cases, src_loc=src_loc))
 
         if name == "Switch":
             switch_test, switch_cases = data["test"], data["cases"]
 
-            self._statements.append(Switch(switch_test, switch_cases, src_loc_at=3))
+            self._statements.append(Switch(switch_test, switch_cases, src_loc=src_loc))
 
         if name == "FSM":
             fsm_signal, fsm_reset, fsm_encoding, fsm_decoding, fsm_states = \
@@ -355,7 +362,7 @@ class Module(_ModuleBuilderRoot, Elaboratable):
             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()),
-                src_loc_at=3))
+                src_loc=src_loc))
 
     def _add_statement(self, assigns, domain, depth, compat_mode=False):
         def domain_name(domain):
index 45836cbed588832585e87be2ae3bdd11f9635395..1ad7f420e994c82ee588e0002dd01e8c57ae1f1a 100644 (file)
@@ -84,11 +84,11 @@ class ReadPort(Elaboratable):
         self.transparent = transparent
 
         self.addr = Signal(max=memory.depth,
-                           name="{}_r_addr".format(memory.name))
+                           name="{}_r_addr".format(memory.name), src_loc_at=2)
         self.data = Signal(memory.width,
-                           name="{}_r_data".format(memory.name))
+                           name="{}_r_data".format(memory.name), src_loc_at=2)
         if self.domain != "comb" and not transparent:
-            self.en = Signal(name="{}_r_en".format(memory.name))
+            self.en = Signal(name="{}_r_en".format(memory.name), src_loc_at=2)
         else:
             self.en = Const(1)
 
@@ -149,11 +149,11 @@ class WritePort(Elaboratable):
         self.granularity  = granularity
 
         self.addr = Signal(max=memory.depth,
-                           name="{}_w_addr".format(memory.name))
+                           name="{}_w_addr".format(memory.name), src_loc_at=2)
         self.data = Signal(memory.width,
-                           name="{}_w_data".format(memory.name))
+                           name="{}_w_data".format(memory.name), src_loc_at=2)
         self.en   = Signal(memory.width // granularity,
-                           name="{}_w_en".format(memory.name))
+                           name="{}_w_en".format(memory.name), src_loc_at=2)
 
     def elaborate(self, platform):
         f = Instance("$memwr",
@@ -198,8 +198,8 @@ class DummyPort:
             name = tracer.get_var_name(depth=2, default="dummy")
 
         self.addr = Signal(addr_bits,
-                           name="{}_addr".format(name))
+                           name="{}_addr".format(name), src_loc_at=1)
         self.data = Signal(width,
-                           name="{}_data".format(name))
+                           name="{}_data".format(name), src_loc_at=1)
         self.en   = Signal(width // granularity,
-                           name="{}_en".format(name))
+                           name="{}_en".format(name), src_loc_at=1)
index 298a4490ac0932659fe6ac5871dfdf0addb5c0e2..05479ea98945d54613fed78a264fc6e6579d629d 100644 (file)
@@ -3,6 +3,7 @@ from collections import OrderedDict
 from collections.abc import Iterable
 
 from ..tools import flatten
+from .. import tracer
 from .ast import *
 from .ast import _StatementList
 from .cd import *
@@ -533,6 +534,7 @@ class LHSGroupFilter(SwitchCleaner):
 
 class _ControlInserter(FragmentTransformer):
     def __init__(self, controls):
+        self.src_loc = None
         if isinstance(controls, Value):
             controls = {"sync": controls}
         self.controls = OrderedDict(controls)
@@ -548,14 +550,17 @@ class _ControlInserter(FragmentTransformer):
     def _insert_control(self, fragment, domain, signals):
         raise NotImplementedError # :nocov:
 
+    def __call__(self, value):
+        self.src_loc = tracer.get_src_loc()
+        return super().__call__(value)
 
 class ResetInserter(_ControlInserter):
     def _insert_control(self, fragment, domain, signals):
         stmts = [s.eq(Const(s.reset, s.nbits)) for s in signals if not s.reset_less]
-        fragment.add_statements(Switch(self.controls[domain], {1: stmts}))
+        fragment.add_statements(Switch(self.controls[domain], {1: stmts}, src_loc=self.src_loc))
 
 
 class CEInserter(_ControlInserter):
     def _insert_control(self, fragment, domain, signals):
         stmts = [s.eq(s) for s in signals]
-        fragment.add_statements(Switch(self.controls[domain], {0: stmts}))
+        fragment.add_statements(Switch(self.controls[domain], {0: stmts}, src_loc=self.src_loc))