Bias Construction

Demand-driven bias-generation construction for enumerate_circuits().

The bias generator is not an enumerated module variant: it is constructed per combination from what the other slots actually consume on each bias rail. This replaces the enumerate-then-filter-then-prune chain that the fixed bias_generation variants needed (issues #99/#101/#102): every consumed rail gets exactly the leg its consumer requires, so flavor mismatches, unused legs, and the redundant rail-7 diode cannot arise in the first place.

Each consumed rail is classified into a kind – the type system this module is built around:

  • gate_vdd – a consumer MOSFET gate whose source sits at vdd (a PMOS current source). The rail must sit one |V_GSP| below vdd, and the leg’s diode-connected PMOS is the mirror master of its consumers: the sizer sets the consumer current by W/L ratio instead of hoping a voltage matches.

  • gate_gnd – mirror image: consumer gate with source at gnd (NMOS current sink); NMOS diode leg one V_GSN above gnd.

  • current_source – the consumer brings its own NMOS reference diode (a current-mirror tail’s mirror diode). The rail is a current interface: the leg is a bare PMOS mirror sourcing the reference current into the consumer’s diode. A voltage-style leg here would either duplicate the consumer’s diode (parallel diodes splitting the reference current) or fight it (the measured 22x rail-7 contention of issue #99).

  • current_sink – mirror image for a PMOS-diode consumer: bare NMOS mirror sinking the reference current.

  • cascode_gnd – an NMOS cascode gate: consumer gate on the rail with its source on an internal node (a stacked device below). The rail must sit at the cascode’s V_GS plus the saturation floor of the stack under it, so the leg is a diode-connected NMOS (the tracking V_GS part) on a small floor resistor (out = V_GSN + I*R), both sized per-rail by the sizer from the consumer stack (issue #99’s parked cascode class).

  • cascode_vdd – mirror image for PMOS cascode gates: diode-connected PMOS on a resistor to vdd (out = vdd - |V_GSP| - I*R).

  • tunable – no structurally implied level: conflicting demands on a shared rail. The leg is a PMOS mirror into a resistor (out = I_leg * R), per-rail tunable by the sizer (issue #100).

The leg templates and the multi-reference core (NMOS master on the ibias pin, plus a pref branch deriving the PMOS-side mirror reference, emitted only when a leg needs it) live in config/bias_legs.yaml – see its header for the electrical layout and the template net-name contract.

Both the demand analysis and the assembly are structural (actual device terminal references, no YAML tags), so new consumer variants are classified correctly without code changes. Construction must run after prune_cmfb() / prune_tail_current(), so emptied placeholder slots demand nothing.

circuitgenome.synthesizer.bias_construction.PREF_KINDS = frozenset({'cascode_gnd', 'current_source', 'gate_gnd', 'tunable'})

Rail kinds whose legs mirror from the PMOS-side reference gate. Present for documentation; the pref branch is actually emitted on a structural check (an instantiated leg terminal referencing pref).

circuitgenome.synthesizer.bias_construction.rail_flavor_from_diode(devices, node)[source]

Return the supply a diode-connected MOSFET on node pins it to.

A diode-connected device (d == g == node) ties the node one V_GS away from whatever its source ultimately rests on, and its channel type names that supply: PMOS conducts from vdd ("vdd"), NMOS to gnd ("gnd"). The channel type is used rather than the source terminal so stacked references (a cascode tail’s d == g == bias device whose source is an internal node of the stack) resolve correctly. Returns None when no diode-connected MOSFET sits on node.

Parameters:
Return type:

str | None

circuitgenome.synthesizer.bias_construction.required_rail_kinds(topology, variant_map)[source]

Return {rail_index: kind} for every bias rail actually consumed.

A rail is consumed when any device terminal of the slot’s variant references the port the topology wires to net_bias1..``net_bias8`` (declared-but-unwired optional ports are ignored, exactly like the retired needed_bias_outputs). The kind combines the per-device votes of _device_votes() across all consuming slots:

  • a diode vote wins over gate votes: the consumer’s own diode makes the rail a current interface, and any mirror gates riding on it (the tail’s output device) are slaved to that diode, not to the leg;

  • a single gate kind is taken as-is;

  • a single cascode kind (and no diode/gate votes) is taken as-is – a supply-referenced gate sharing the rail would need one V_GS where the cascode needs V_GS plus a floor, so that mix is a conflict;

  • conflicting votes fall back to tunable – the honest “no single structural level implied” answer.

Unconsumed rails are absent from the result and get no leg at all.

Parameters:
Return type:

dict[int, str]

circuitgenome.synthesizer.bias_construction.construct_bias_generation(topology, variant_map, library)[source]

Assemble the bias-generation variant this combination needs.

Emits the master reference, one leg per consumed rail (template chosen by required_rail_kinds(), with the per-leg nets out/mid rewritten to out{i}/mid{i} and each ref suffixed with the rail index), and the pref branch when any instantiated leg references the pref net. The result is a normal ModuleVariant (name constructed_bias) – downstream wiring, serialization, and the recognizer treat it like any other variant. pref (and the pref branch’s prefsrc/ncasc, and the cascode legs’ mid{i}) are not ports, so they resolve to slot-internal nets.

Parameters:
Return type:

ModuleVariant