back.rtlil: explain how RTLIL conversion works.

This commit is contained in:
whitequark 2018-12-13 03:22:01 +00:00
parent bfd0011aee
commit f86ec1e7ef

View file

@ -212,20 +212,20 @@ class _ValueTransformer(xfrm.ValueTransformer):
def __init__(self, rtlil): def __init__(self, rtlil):
self.rtlil = rtlil self.rtlil = rtlil
self.wires = ast.ValueDict() self.wires = ast.ValueDict()
self.ports = ast.ValueDict()
self.driven = ast.ValueDict() self.driven = ast.ValueDict()
self.ports = ast.ValueDict()
self.is_lhs = False self.is_lhs = False
self.sub_name = None self.sub_name = None
def add_driven(self, signal, sync):
self.driven[signal] = sync
def add_port(self, signal, kind=None): def add_port(self, signal, kind=None):
if signal in self.driven: if signal in self.driven:
self.ports[signal] = (len(self.ports), "output") self.ports[signal] = (len(self.ports), "output")
else: else:
self.ports[signal] = (len(self.ports), "input") self.ports[signal] = (len(self.ports), "input")
def add_driven(self, signal, sync):
self.driven[signal] = sync
@contextmanager @contextmanager
def lhs(self): def lhs(self):
try: try:
@ -394,19 +394,26 @@ def convert_fragment(builder, fragment, name, clock_domains):
with builder.module(name) as module: with builder.module(name) as module:
xformer = _ValueTransformer(module) xformer = _ValueTransformer(module)
# Register all signals driven in the current fragment. This must be done first, as it
# affects further codegen; e.g. whether sig$next signals will be generated and used.
for cd_name, signal in fragment.iter_drivers(): for cd_name, signal in fragment.iter_drivers():
xformer.add_driven(signal, sync=cd_name is not None) xformer.add_driven(signal, sync=cd_name is not None)
# Register all signals used as ports in the current fragment. The wires are lazily
# generated, so registering ports eagerly ensures they get correct direction qualifiers.
for signal in fragment.ports: for signal in fragment.ports:
xformer.add_port(signal) xformer.add_port(signal)
# Make sure clocks and resets get sensible names, by eagerly converting them outside # Transform all clocks clocks and resets eagerly and outside of any hierarchy, to make
# of any hierarchy. # sure they get sensible (non-prefixed) names. This does not affect semantics.
for cd_name, _ in fragment.iter_sync(): for cd_name, _ in fragment.iter_sync():
cd = clock_domains[cd_name] cd = clock_domains[cd_name]
xformer(cd.clk) xformer(cd.clk)
xformer(cd.reset) xformer(cd.reset)
# Transform all subfragments to their respective cells. Transforming signals connected
# to their ports into wires eagerly makes sure they get sensible (prefixed with submodule
# name) names.
for subfragment, sub_name in fragment.subfragments: for subfragment, sub_name in fragment.subfragments:
sub_name, sub_port_map = \ sub_name, sub_port_map = \
convert_fragment(builder, subfragment, sub_name, clock_domains) convert_fragment(builder, subfragment, sub_name, clock_domains)
@ -417,6 +424,8 @@ def convert_fragment(builder, fragment, name, clock_domains):
with module.process() as process: with module.process() as process:
with process.case() as case: 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 cd_name, signal in fragment.iter_drivers(): for cd_name, signal in fragment.iter_drivers():
if cd_name is None: if cd_name is None:
prev_value = xformer(ast.Const(signal.reset, signal.nbits)) prev_value = xformer(ast.Const(signal.reset, signal.nbits))
@ -425,6 +434,7 @@ def convert_fragment(builder, fragment, name, clock_domains):
with xformer.lhs(): with xformer.lhs():
case.assign(xformer(signal), prev_value) case.assign(xformer(signal), prev_value)
# Convert statements into decision trees.
def _convert_stmts(case, stmts): def _convert_stmts(case, stmts):
for stmt in stmts: for stmt in stmts:
if isinstance(stmt, ast.Assign): if isinstance(stmt, ast.Assign):
@ -433,6 +443,7 @@ def convert_fragment(builder, fragment, name, clock_domains):
if lhs_bits == rhs_bits: if lhs_bits == rhs_bits:
rhs_sigspec = xformer(stmt.rhs) rhs_sigspec = xformer(stmt.rhs)
else: else:
# In RTLIL, LHS and RHS of assignment must have exactly same width.
rhs_sigspec = xformer.match_shape( rhs_sigspec = xformer.match_shape(
stmt.rhs, lhs_bits, rhs_sign) stmt.rhs, lhs_bits, rhs_sign)
with xformer.lhs(): with xformer.lhs():
@ -450,11 +461,18 @@ def convert_fragment(builder, fragment, name, clock_domains):
_convert_stmts(case, fragment.statements) _convert_stmts(case, 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. Note that this assigns \sig,
# not \sig$next.
with process.sync("init") as sync: with process.sync("init") as sync:
for cd_name, signal in fragment.iter_sync(): for cd_name, signal in fragment.iter_sync():
sync.update(xformer(signal), sync.update(xformer(signal),
xformer(ast.Const(signal.reset, signal.nbits))) xformer(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`.
for cd_name, signals in fragment.iter_domains(): for cd_name, signals in fragment.iter_domains():
triggers = [] triggers = []
if cd_name is None: if cd_name is None:
@ -470,10 +488,13 @@ def convert_fragment(builder, fragment, name, clock_domains):
for trigger in triggers: for trigger in triggers:
with process.sync(*trigger) as sync: with process.sync(*trigger) as sync:
for signal in signals: for signal in signals:
xformer(signal) rhs_sigspec = xformer(signal)
wire_curr, wire_next = xformer.wires[signal] with xformer.lhs():
sync.update(wire_curr, wire_next) sync.update(xformer(signal), rhs_sigspec)
# Finally, collect the names we've given to our ports in RTLIL, and correlate these with
# the signals represented by these ports. If we are a submodule, this will be necessary
# to create a cell for us in the parent module.
port_map = OrderedDict() port_map = OrderedDict()
for signal in fragment.ports: for signal in fragment.ports:
port_map[xformer(signal)] = signal port_map[xformer(signal)] = signal