back.rtlil: do not emit $next wires for comb signals.
According to RTLIL semantics (that was undocumented before today), the only purpose of `sync always` is to enable inference of latches, because there is no other way to express them in terms of RTLIL processes without ending up with a combinatorial loop. But, nMigen specifically avoids latches, so this is not necessary. This change results in major improvements in Verilog readability. See also #98.
This commit is contained in:
parent
6b843b5be6
commit
dd5e513e42
|
@ -269,7 +269,7 @@ class _ValueCompilerState:
|
||||||
wire_curr = self.rtlil.wire(width=signal.nbits, name=wire_name,
|
wire_curr = self.rtlil.wire(width=signal.nbits, name=wire_name,
|
||||||
port_id=port_id, port_kind=port_kind,
|
port_id=port_id, port_kind=port_kind,
|
||||||
src=src(signal.src_loc))
|
src=src(signal.src_loc))
|
||||||
if signal in self.driven:
|
if signal in self.driven and self.driven[signal]:
|
||||||
wire_next = self.rtlil.wire(width=signal.nbits, name="$next" + wire_curr,
|
wire_next = self.rtlil.wire(width=signal.nbits, name="$next" + wire_curr,
|
||||||
src=src(signal.src_loc))
|
src=src(signal.src_loc))
|
||||||
else:
|
else:
|
||||||
|
@ -559,10 +559,10 @@ class _LHSValueCompiler(_ValueCompiler):
|
||||||
return self(value)
|
return self(value)
|
||||||
|
|
||||||
def on_Signal(self, value):
|
def on_Signal(self, value):
|
||||||
wire_curr, wire_next = self.s.resolve(value)
|
if value not in self.s.driven:
|
||||||
if wire_next is None:
|
|
||||||
raise ValueError("No LHS wire for non-driven signal {}".format(repr(value)))
|
raise ValueError("No LHS wire for non-driven signal {}".format(repr(value)))
|
||||||
return wire_next
|
wire_curr, wire_next = self.s.resolve(value)
|
||||||
|
return wire_next or wire_curr
|
||||||
|
|
||||||
def _prepare_value_for_Slice(self, value):
|
def _prepare_value_for_Slice(self, value):
|
||||||
assert isinstance(value, (ast.Signal, ast.Slice, ast.Cat, rec.Record))
|
assert isinstance(value, (ast.Signal, ast.Slice, ast.Cat, rec.Record))
|
||||||
|
@ -824,23 +824,24 @@ def convert_fragment(builder, fragment, hierarchy):
|
||||||
sync.update(verilog_trigger, "1'0")
|
sync.update(verilog_trigger, "1'0")
|
||||||
verilog_trigger_sync_emitted = True
|
verilog_trigger_sync_emitted = True
|
||||||
|
|
||||||
# For every signal in every domain, assign \sig to $next\sig. The sensitivity list,
|
# For every signal in every sync domain, assign \sig to $next\sig. The sensitivity
|
||||||
# however, differs between domains: for comb domains, it is `always`, for sync
|
# list, however, differs between domains: for domains with sync reset, it is
|
||||||
# domains with sync reset, it is `posedge clk`, for sync domains with async reset
|
# `posedge clk`, for sync domains with async reset it is `posedge clk or
|
||||||
# it is `posedge clk or posedge rst`.
|
# posedge rst`.
|
||||||
for domain, signals in fragment.drivers.items():
|
for domain, signals in fragment.drivers.items():
|
||||||
|
if domain is None:
|
||||||
|
continue
|
||||||
|
|
||||||
signals = signals & group_signals
|
signals = signals & group_signals
|
||||||
if not signals:
|
if not signals:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
cd = fragment.domains[domain]
|
||||||
|
|
||||||
triggers = []
|
triggers = []
|
||||||
if domain is None:
|
triggers.append(("posedge", compiler_state.resolve_curr(cd.clk)))
|
||||||
triggers.append(("always",))
|
if cd.async_reset:
|
||||||
else:
|
triggers.append(("posedge", compiler_state.resolve_curr(cd.rst)))
|
||||||
cd = fragment.domains[domain]
|
|
||||||
triggers.append(("posedge", compiler_state.resolve_curr(cd.clk)))
|
|
||||||
if cd.async_reset:
|
|
||||||
triggers.append(("posedge", compiler_state.resolve_curr(cd.rst)))
|
|
||||||
|
|
||||||
for trigger in triggers:
|
for trigger in triggers:
|
||||||
with process.sync(*trigger) as sync:
|
with process.sync(*trigger) as sync:
|
||||||
|
|
Loading…
Reference in a new issue