Source code for circuitgenome.synthesizer.net_aliasing

"""
Net-merge pass for ``load`` ports declared ``alias_of`` another ``load`` port,
run by :func:`~circuitgenome.synthesizer.synthesizer.enumerate_circuits` after
``load``'s port-to-net map has been built.

``opamp_topologies.yaml`` assigns ``load.in1``/``in2`` (the folding nodes fed
by ``input_pair.out1``/``out2``) and ``load.out``/``out1``/``out2`` (the
load's actual output node(s), sensed by ``cmfb``/``second_stage*``/``comp*``
and -- in ``single_ended`` topologies -- the stage-output net) to *separate*
nets. This is correct for the 8 cascode ``load`` variants (4 folded-cascode,
2 self-biased telescopic, 2 wide-swing telescopic), where the folding node and
the cascode's output are distinct device terminals (e.g.
``mn1{d: out1, g: bias2, s: in1}``).

For the other 6 ``load`` variants (resistor/active/current-source loads),
``in1``/``in2`` and ``out1``/``out2`` *are* the same physical node -- declared
via ``out1: {alias_of: in1}`` / ``out2: {alias_of: in2}`` in
``opamp_modules.yaml``. :func:`compute_alias_net_rename` finds these
declarations and, for each one whose two assigned nets differ, computes a
rename merging them into a single net; :func:`apply_net_rename` then rewrites
every device's terminals (across all slots) accordingly -- restoring, for
these 6 variants, the single shared in/out node that their devices assume.

To extend: declare ``alias_of`` on any new ``load`` port whose net should be
merged with another -- no code changes needed here.
"""
from __future__ import annotations

from .models import Device, ModuleVariant


[docs] def compute_alias_net_rename( load_variant: ModuleVariant, load_port_net_map: dict[str, str], external_ports: list[str], ) -> dict[str, str]: """Return a ``{old_net: new_net}`` rename for *load_variant*'s ``alias_of`` ports. For each port ``p`` with ``p.alias_of = q``, if ``p`` and ``q`` were assigned different nets, merge them into one. The merge keeps whichever net is an external subcircuit port (so the ``.subckt`` port list stays intact); if neither (or both) are external, ``q``'s net is folded into ``p``'s. """ rename: dict[str, str] = {} for port in load_variant.ports: if port.alias_of is None: continue net_p = load_port_net_map.get(port.name) net_q = load_port_net_map.get(port.alias_of) if net_p is None or net_q is None or net_p == net_q: continue if net_q in external_ports: rename[net_p] = net_q else: rename[net_q] = net_p return rename
[docs] def apply_net_rename( devices: list[tuple[str, Device]], rename: dict[str, str], ) -> list[tuple[str, Device]]: """Return *devices* with every terminal net rewritten according to *rename*. Nets not present in *rename* are left unchanged. Applies across all slots' devices, not just ``load``'s. """ if not rename: return devices result = [] for ref, dev in devices: terminals = {term: rename.get(net, net) for term, net in dev.terminals.items()} result.append((ref, Device(ref=dev.ref, type=dev.type, terminals=terminals))) return result