Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: m-labs/nmigen
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 62b3e3661250
Choose a base ref
...
head repository: m-labs/nmigen
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 37f363e338ef
Choose a head ref
  • 3 commits
  • 5 files changed
  • 1 contributor

Commits on Jul 9, 2019

  1. hdl.{ast,dsl},back.rtlil: track source locations for switch cases.

    This is a very new Yosys feature, and will require a Yosys build
    newer than YosysHQ/yosys@93bc5aff.
    whitequark committed Jul 9, 2019
    Copy the full SHA
    00c5209 View commit details
  2. back.verilog: run proc_prune for much cleaner output.

    This is a very new Yosys feature, and will require a Yosys build
    newer than YosysHQ/yosys@44bcb7a1.
    whitequark committed Jul 9, 2019
    Copy the full SHA
    10e56c7 View commit details
  3. back.rtlil: add decodings to cases when switching on a signal.

    Fixes #134.
    whitequark committed Jul 9, 2019
    Copy the full SHA
    37f363e View commit details
Showing with 52 additions and 12 deletions.
  1. +16 −4 nmigen/back/rtlil.py
  2. +1 −0 nmigen/back/verilog.py
  3. +8 −2 nmigen/hdl/ast.py
  4. +25 −6 nmigen/hdl/dsl.py
  5. +2 −0 nmigen/hdl/xfrm.py
20 changes: 16 additions & 4 deletions nmigen/back/rtlil.py
Original file line number Diff line number Diff line change
@@ -196,7 +196,8 @@ def __enter__(self):
def __exit__(self, *args):
self._append("{}end\n", " " * self.indent)

def case(self, *values):
def case(self, *values, attrs={}, src=""):
self._attributes(attrs, src=src, indent=self.indent + 1)
if values == ():
self._append("{}case\n", " " * (self.indent + 1))
else:
@@ -602,10 +603,10 @@ def __init__(self, state, rhs_compiler, lhs_compiler):
self._has_rhs = False

@contextmanager
def case(self, switch, values):
def case(self, switch, values, attrs={}, src=""):
try:
old_case = self._case
with switch.case(*values) as self._case:
with switch.case(*values, attrs=attrs, src=src) as self._case:
yield
finally:
self._case = old_case
@@ -658,7 +659,18 @@ def on_Switch(self, stmt):

with self._case.switch(test_sigspec, src=src(stmt.src_loc)) as switch:
for values, stmts in stmt.cases.items():
with self.case(switch, values):
case_attrs = {}
if values in stmt.case_src_locs:
case_attrs["src"] = src(stmt.case_src_locs[values])
if isinstance(stmt.test, ast.Signal) and stmt.test.decoder:
decoded_values = []
for value in values:
if "-" in value:
decoded_values.append("<multiple>")
else:
decoded_values.append(stmt.test.decoder(int(value, 2)))
case_attrs["nmigen.decoding"] = "|".join(decoded_values)
with self.case(switch, values, attrs=case_attrs):
self.on_statements(stmts)

def on_statement(self, stmt):
1 change: 1 addition & 0 deletions nmigen/back/verilog.py
Original file line number Diff line number Diff line change
@@ -36,6 +36,7 @@ def convert(*args, strip_src=False, **kwargs):
read_ilang <<rtlil
{}
rtlil
proc_prune
proc_init
proc_arst
proc_dff
10 changes: 8 additions & 2 deletions nmigen/hdl/ast.py
Original file line number Diff line number Diff line change
@@ -1033,18 +1033,22 @@ class Assume(Property):

# @final
class Switch(Statement):
def __init__(self, test, cases, *, src_loc=None, src_loc_at=0):
def __init__(self, test, cases, *, src_loc=None, src_loc_at=0, case_src_locs={}):
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
# Switch is also a bit special in that its parts also have location information. It can't
# be automatically traced, so whatever constructs a Switch may optionally provide it.
self.case_src_locs = {}

self.test = Value.wrap(test)
self.cases = OrderedDict()
for keys, stmts in cases.items():
for orig_keys, stmts in cases.items():
# Map: None -> (); key -> (key,); (key...) -> (key...)
keys = orig_keys
if keys is None:
keys = ()
if not isinstance(keys, tuple):
@@ -1064,6 +1068,8 @@ def __init__(self, test, cases, *, src_loc=None, src_loc_at=0):
if not isinstance(stmts, Iterable):
stmts = [stmts]
self.cases[new_keys] = Statement.wrap(stmts)
if orig_keys in case_src_locs:
self.case_src_locs[new_keys] = case_src_locs[orig_keys]

def _lhs_signals(self):
signals = union((s._lhs_signals() for ss in self.cases.values() for s in ss),
31 changes: 25 additions & 6 deletions nmigen/hdl/dsl.py
Original file line number Diff line number Diff line change
@@ -161,10 +161,12 @@ def _set_ctrl(self, name, data):
@contextmanager
def If(self, cond):
self._check_context("If", context=None)
src_loc = tracer.get_src_loc(src_loc_at=1)
if_data = self._set_ctrl("If", {
"tests": [],
"bodies": [],
"src_loc": tracer.get_src_loc(src_loc_at=1),
"tests": [],
"bodies": [],
"src_loc": src_loc,
"src_locs": [],
})
try:
_outer_case, self._statements = self._statements, []
@@ -173,13 +175,15 @@ def If(self, cond):
self._flush_ctrl()
if_data["tests"].append(cond)
if_data["bodies"].append(self._statements)
if_data["src_locs"].append(src_loc)
finally:
self.domain._depth -= 1
self._statements = _outer_case

@contextmanager
def Elif(self, cond):
self._check_context("Elif", context=None)
src_loc = tracer.get_src_loc(src_loc_at=1)
if_data = self._get_ctrl("If")
if if_data is None:
raise SyntaxError("Elif without preceding If")
@@ -190,13 +194,15 @@ def Elif(self, cond):
self._flush_ctrl()
if_data["tests"].append(cond)
if_data["bodies"].append(self._statements)
if_data["src_locs"].append(src_loc)
finally:
self.domain._depth -= 1
self._statements = _outer_case

@contextmanager
def Else(self):
self._check_context("Else", context=None)
src_loc = tracer.get_src_loc(src_loc_at=1)
if_data = self._get_ctrl("If")
if if_data is None:
raise SyntaxError("Else without preceding If/Elif")
@@ -206,6 +212,7 @@ def Else(self):
yield
self._flush_ctrl()
if_data["bodies"].append(self._statements)
if_data["src_locs"].append(src_loc)
finally:
self.domain._depth -= 1
self._statements = _outer_case
@@ -218,6 +225,7 @@ def Switch(self, test):
"test": Value.wrap(test),
"cases": OrderedDict(),
"src_loc": tracer.get_src_loc(src_loc_at=1),
"case_src_locs": {},
})
try:
self._ctrl_context = "Switch"
@@ -231,6 +239,7 @@ def Switch(self, test):
@contextmanager
def Case(self, *values):
self._check_context("Case", context="Switch")
src_loc = tracer.get_src_loc(src_loc_at=1)
switch_data = self._get_ctrl("Switch")
new_values = ()
for value in values:
@@ -254,6 +263,7 @@ def Case(self, *values):
# which means the branch will always match.
if not (values and not new_values):
switch_data["cases"][new_values] = self._statements
switch_data["case_src_locs"][new_values] = src_loc
finally:
self._ctrl_context = "Switch"
self._statements = _outer_case
@@ -272,6 +282,7 @@ def FSM(self, reset=None, domain="sync", name="fsm"):
"decoding": OrderedDict(),
"states": OrderedDict(),
"src_loc": tracer.get_src_loc(src_loc_at=1),
"state_src_locs": {},
})
self._generated[name] = fsm = \
FSM(fsm_data["signal"], fsm_data["encoding"], fsm_data["decoding"])
@@ -287,6 +298,7 @@ def FSM(self, reset=None, domain="sync", name="fsm"):
@contextmanager
def State(self, name):
self._check_context("FSM State", context="FSM")
src_loc = tracer.get_src_loc(src_loc_at=1)
fsm_data = self._get_ctrl("FSM")
if name in fsm_data["states"]:
raise SyntaxError("FSM state '{}' is already defined".format(name))
@@ -298,6 +310,7 @@ def State(self, name):
yield
self._flush_ctrl()
fsm_data["states"][name] = self._statements
fsm_data["state_src_locs"][name] = src_loc
finally:
self._ctrl_context = "FSM"
self._statements = _outer_case
@@ -327,6 +340,7 @@ def _pop_ctrl(self):

if name == "If":
if_tests, if_bodies = data["tests"], data["bodies"]
if_src_locs = data["src_locs"]

tests, cases = [], OrderedDict()
for if_test, if_case in zip(if_tests + [None], if_bodies):
@@ -342,16 +356,20 @@ def _pop_ctrl(self):
match = None
cases[match] = if_case

self._statements.append(Switch(Cat(tests), cases, src_loc=src_loc))
self._statements.append(Switch(Cat(tests), cases,
src_loc=src_loc, case_src_locs=dict(zip(cases, if_src_locs))))

if name == "Switch":
switch_test, switch_cases = data["test"], data["cases"]
switch_case_src_locs = data["case_src_locs"]

self._statements.append(Switch(switch_test, switch_cases, src_loc=src_loc))
self._statements.append(Switch(switch_test, switch_cases,
src_loc=src_loc, case_src_locs=switch_case_src_locs))

if name == "FSM":
fsm_signal, fsm_reset, fsm_encoding, fsm_decoding, fsm_states = \
data["signal"], data["reset"], data["encoding"], data["decoding"], data["states"]
fsm_state_src_locs = data["state_src_locs"]
if not fsm_states:
return
fsm_signal.nbits = bits_for(len(fsm_encoding) - 1)
@@ -364,7 +382,8 @@ def _pop_ctrl(self):
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=src_loc))
src_loc=src_loc, case_src_locs={fsm_encoding[name]: fsm_state_src_locs[name]
for name in fsm_states}))

def _add_statement(self, assigns, domain, depth, compat_mode=False):
def domain_name(domain):
2 changes: 2 additions & 0 deletions nmigen/hdl/xfrm.py
Original file line number Diff line number Diff line change
@@ -216,6 +216,8 @@ def on_statement(self, stmt):
new_stmt = self.on_unknown_statement(stmt)
if isinstance(new_stmt, Statement) and self.replace_statement_src_loc(stmt, new_stmt):
new_stmt.src_loc = stmt.src_loc
if isinstance(new_stmt, Switch) and isinstance(stmt, Switch):
new_stmt.case_src_locs = stmt.case_src_locs
return new_stmt

def __call__(self, stmt):