From 8c9fdf907f302d7e7d204ccd1d18feea97eb08dc Mon Sep 17 00:00:00 2001 From: whitequark Date: Mon, 8 Jul 2019 09:50:07 +0000 Subject: [PATCH] hdl.{dsl,mem,xfrm}: inject appropriate source locations. 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 | 10 ++++++++-- nmigen/hdl/dsl.py | 27 +++++++++++++++++---------- nmigen/hdl/mem.py | 18 +++++++++--------- nmigen/hdl/xfrm.py | 9 +++++++-- 4 files changed, 41 insertions(+), 23 deletions(-) diff --git a/nmigen/hdl/ast.py b/nmigen/hdl/ast.py index 9a88fa3..bf09077 100644 --- a/nmigen/hdl/ast.py +++ b/nmigen/hdl/ast.py @@ -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(): diff --git a/nmigen/hdl/dsl.py b/nmigen/hdl/dsl.py index 43da83a..bd6399a 100644 --- a/nmigen/hdl/dsl.py +++ b/nmigen/hdl/dsl.py @@ -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): diff --git a/nmigen/hdl/mem.py b/nmigen/hdl/mem.py index 45836cb..1ad7f42 100644 --- a/nmigen/hdl/mem.py +++ b/nmigen/hdl/mem.py @@ -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) diff --git a/nmigen/hdl/xfrm.py b/nmigen/hdl/xfrm.py index 298a449..05479ea 100644 --- a/nmigen/hdl/xfrm.py +++ b/nmigen/hdl/xfrm.py @@ -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)) -- 2.30.2