From: whitequark Date: Thu, 13 Dec 2018 03:30:39 +0000 (+0000) Subject: fhdl.ir: explain how port enumeration works. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=d19b28f23f336adf4095a72e36a07d68bfcc0927;p=nmigen.git fhdl.ir: explain how port enumeration works. --- diff --git a/nmigen/back/rtlil.py b/nmigen/back/rtlil.py index d2397f5..d73abaa 100644 --- a/nmigen/back/rtlil.py +++ b/nmigen/back/rtlil.py @@ -503,7 +503,11 @@ def convert_fragment(builder, fragment, name, clock_domains): def convert(fragment, ports=[], clock_domains={}): - fragment, ins, outs = fragment.prepare(ports, clock_domains) + fragment = xfrm.ResetInserter({ + cd.name: cd.reset for cd in clock_domains.values() if cd.reset is not None + })(fragment) + + ins, outs = fragment._propagate_ports(ports, clock_domains) builder = _Builder() convert_fragment(builder, fragment, "top", clock_domains) diff --git a/nmigen/fhdl/ir.py b/nmigen/fhdl/ir.py index 8de3388..6646a37 100644 --- a/nmigen/fhdl/ir.py +++ b/nmigen/fhdl/ir.py @@ -50,12 +50,9 @@ class Fragment: assert isinstance(subfragment, Fragment) self.subfragments.append((subfragment, name)) - def prepare(self, ports, clock_domains): - from .xfrm import ResetInserter - - resets = {cd.name: cd.reset for cd in clock_domains.values() if cd.reset is not None} - frag = ResetInserter(resets)(self) - + def _propagate_ports(self, ports, clock_domains): + # Collect all signals we're driving (on LHS of statements), and signals we're using + # (on RHS of statements, or in clock domains). self_driven = union(s._lhs_signals() for s in self.statements) self_used = union(s._rhs_signals() for s in self.statements) for cd_name, _ in self.iter_sync(): @@ -64,16 +61,27 @@ class Fragment: if cd.reset is not None: self_used.add(cd.reset) + # Our input ports are all the signals we're using but not driving. This is an over- + # approximation: some of these signals may be driven by our subfragments. ins = self_used - self_driven + # Our output ports are all the signals we're asked to provide that we're driving. This is + # an underapproximation: some of these signals may be driven by subfragments. outs = ports & self_driven - for n, (subfrag, name) in enumerate(frag.subfragments): - subfrag, sub_ins, sub_outs = subfrag.prepare(ports=self_used | ports, + # Go through subfragments and refine our approximation for ports. + for subfrag, name in self.subfragments: + # Always ask subfragments to provide all signals we're using and signals we're asked + # to provide. If the subfragment is not driving it, it will silently ignore it. + sub_ins, sub_outs = subfrag._propagate_ports(ports=self_used | ports, clock_domains=clock_domains) - frag.subfragments[n] = (subfrag, name) + # Refine the input port approximation: if a subfragment is driving a signal, + # it is definitely not our input. ins -= sub_outs + # Refine the output port approximation: if a subfragment is driving a signal, + # and we're asked to provide it, we can provide it now. outs |= ports & sub_outs - frag.add_ports(ins, outs) + # We've computed the precise set of input and output ports. + self.add_ports(ins, outs) - return frag, ins, outs + return ins, outs