back.rtlil: split processes as finely as possible.

This makes simulation work correctly (by introducing delta cycles,
and therefore, making the overall Verilog simulation deterministic)
at the price of pessimizing mux trees generated by Yosys and Synplify
frontends, sometimes severely.
This commit is contained in:
whitequark 2018-12-22 10:03:16 +00:00
parent d29929912f
commit 59c7540aeb

View file

@ -286,7 +286,7 @@ class _ValueCompilerState:
del self.expansions[value]
class _ValueCompiler(xfrm.AbstractValueTransformer):
class _ValueCompiler(xfrm.ValueVisitor):
def __init__(self, state):
self.s = state
@ -520,12 +520,13 @@ class _LHSValueCompiler(_ValueCompiler):
raise TypeError # :nocov:
class _StatementCompiler(xfrm.AbstractStatementTransformer):
class _StatementCompiler(xfrm.StatementVisitor):
def __init__(self, state, rhs_compiler, lhs_compiler):
self.state = state
self.rhs_compiler = rhs_compiler
self.lhs_compiler = lhs_compiler
self._group = None
self._case = None
@contextmanager
@ -538,6 +539,12 @@ class _StatementCompiler(xfrm.AbstractStatementTransformer):
self._case = old_case
def on_Assign(self, stmt):
# The invariant provided by LHSGroupAnalyzer is that all signals that ever appear together
# on LHS are a part of the same group, so it is sufficient to check any of them.
any_lhs_signal = next(iter(stmt.lhs._lhs_signals()))
if any_lhs_signal not in self._group:
return
lhs_bits, lhs_sign = stmt.lhs.shape()
rhs_bits, rhs_sign = stmt.rhs.shape()
if lhs_bits == rhs_bits:
@ -654,11 +661,17 @@ def convert_fragment(builder, fragment, name, top):
module.cell(sub_type, name=sub_name, ports=sub_ports, params=sub_params)
with module.process() as process:
lhs_grouper = xfrm.LHSGroupAnalyzer()
lhs_grouper.on_statements(fragment.statements)
for group, group_signals in lhs_grouper.groups().items():
with module.process(name="$group_{}".format(group)) 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 value (\sig).
# For every signal in sync domains, assign \sig$next to the current
# value (\sig).
for domain, signal in fragment.iter_drivers():
if signal not in group_signals:
continue
if domain is None:
prev_value = ast.Const(signal.reset, signal.nbits)
else:
@ -666,21 +679,28 @@ def convert_fragment(builder, fragment, name, top):
case.assign(lhs_compiler(signal), rhs_compiler(prev_value))
# Convert statements into decision trees.
stmt_compiler._group = group_signals
stmt_compiler._case = case
stmt_compiler(fragment.statements)
# For every signal in the sync domain, assign \sig's initial value (which will end up
# as the \init reg attribute) to the reset value.
# For every signal in the sync domain, assign \sig's initial value (which will
# end up as the \init reg attribute) to the reset value.
with process.sync("init") as sync:
for domain, signal in fragment.iter_sync():
if signal not in group_signals:
continue
wire_curr, wire_next = compiler_state.resolve(signal)
sync.update(wire_curr, rhs_compiler(ast.Const(signal.reset, signal.nbits)))
# For every signal in every domain, assign \sig to \sig$next. The sensitivity list,
# however, differs between domains: for comb domains, it is `always`, for sync domains
# with sync reset, it is `posedge clk`, for sync domains with async rest it is
# `posedge clk or posedge rst`.
# however, differs between domains: for comb domains, it is `always`, for sync
# domains with sync reset, it is `posedge clk`, for sync domains with async reset
# it is `posedge clk or posedge rst`.
for domain, signals in fragment.drivers.items():
signals = signals & group_signals
if not signals:
continue
triggers = []
if domain is None:
triggers.append(("always",))