Preprocessing

Model-independent pre-sizing: slot extraction, IDS assignment from KCL + spec.ibias, topology checks, load-resistor sizing, and the gm/VDS_sat requirements derived from the performance spec.

Shared by both the Level-1 analytical sizer and the gm/Id pipeline. Output conductances come through a DeviceModel, so the gm/Id path gets LUT-accurate gds while Level-1 reproduces λ·Id exactly.

circuitgenome.sizer.shared.preprocess.extract_slot_transistors(fbr_result)[source]

Return {slot_name: [mosfet_Device, …]} from the FBR assignments.

Parameters:

fbr_result (FunctionalBlockRecognitionResult)

Return type:

dict[str, list[Device]]

circuitgenome.sizer.shared.preprocess.extract_slot_resistors(fbr_result)[source]

Return {slot_name: [resistor_Device, …]} from the FBR assignments.

Parameters:

fbr_result (FunctionalBlockRecognitionResult)

Return type:

dict[str, list[Device]]

circuitgenome.sizer.shared.preprocess.size_load_resistors(slot_resistors, spec, tech)[source]

Size resistor-load devices so the first-stage output biases on.

Each load resistor carries the input-pair branch current ibias/2. The value is chosen so the DC drop places the first-stage output node at a level that turns the driven device on:

  • resistor to gnd (PMOS-input loads): V_node = vth_n + VovR = V_node / (ibias/2).

  • resistor from vdd (NMOS-input loads): drop = |vth_p| + VovR = drop / (ibias/2).

Only the load slot is sized; other resistor roles keep their netlist value. Returns {ref: ohms}.

Parameters:
Return type:

dict[str, float]

circuitgenome.sizer.shared.preprocess.check_topology_match(slot_transistors, topology_name)[source]

Warn when the netlist does not realise the chosen topology.

Every gain-stage slot of a valid circuit holds exactly one signal transistor. A stage slot with no signal device (e.g. bias-generator leftovers shoehorned into second_stage_p when a single-ended netlist is sized against a fully-differential topology) signals a --topology mismatch — which would otherwise silently drop the gain/PM/PSRR metrics.

Parameters:
Return type:

list[str]

circuitgenome.sizer.shared.preprocess.deduplicate_devices(slot_transistors)[source]

Return {ref: (Device, slot_name)} with each ref appearing once.

When a transistor appears in multiple slots (e.g. the tail mirror reference appears in both tail_current and bias_gen), the first slot encountered wins for the purpose of iDS assignment. Priority order: input_pair > load > tail_current > second_stage > bias_gen.

Parameters:

slot_transistors (dict[str, list[Device]])

Return type:

dict[str, tuple[Device, str]]

circuitgenome.sizer.shared.preprocess._cascode_load_current_plan(slot_transistors, spec)[source]

Per-device IDS for folded/telescopic cascode loads, from KCL at the fold.

The generic HALF_BIAS rule (ibias/n per same-type device) starves cascode loads: at the folding node the bottom sinks must absorb the input-pair branch current plus the cascode branch current, or the pair’s excess current has nowhere to go and the whole load rails. Structure, read from the assembled netlist:

  • folding devices — source on a supply rail, drain on an input-pair drain net. They carry ibias/2 (pair branch) + I_casc.

  • cascode devices — source on an input-pair drain net (stacked on the folding node). They and every other load device carry the cascode branch current I_casc, chosen as ibias/2.

  • telescopic (cascode devices but no folding devices): no folding node — the whole stack carries the pair branch current ibias/2.

Simple loads (nothing stacked on the pair drains) return {} and keep the generic rule.

Parameters:
Return type:

dict[str, float]

circuitgenome.sizer.shared.preprocess.assign_ids(slot_transistors, all_transistors, spec)[source]

Assign quiescent IDS to each transistor from KCL + spec.ibias.

Parameters:
Return type:

dict[str, float]

circuitgenome.sizer.shared.preprocess._first_stage_gain_factor(slot_transistors)[source]

First-stage transconductance/gain factor k_fs.

A differential pair tapped single-ended only delivers gm1·Rout1/2 (and a Miller loop transconductance of gm1/2) unless an active current-mirror load combines both branches to recover the full gm1·Rout1. So:

  • 1.0 — current-mirror load (a load device is diode-connected, g == d), or a fully-differential output (both branches feed the next stage), and

  • 0.5 — single-ended output with a resistor or plain current-source (non-mirror) load.

Applied to the first-stage gain and to gm1’s role in GBW/PM (not to the raw-device gm1 used for CMRR).

Parameters:

slot_transistors (dict[str, list[Device]])

Return type:

float

circuitgenome.sizer.shared.preprocess.compute_requirements(slot_transistors, all_transistors, ids_map, tech, spec, model, gd_load_r=0.0)[source]

Compute required gm and max VDS_sat per transistor; also Cc1 and Cc2.

Returns (gm_req_map, vod_max_map, cc_pf, cc2_pf, warnings) where warnings lists any gm requirement clamped to the weak-inversion ceiling (the spec cannot be met at this bias current). Output conductances come through model so the gm/Id path uses LUT-accurate gds; the Level-1 model reproduces the geometry-free λ·Id exactly.

Parameters:
Return type:

tuple[dict[str, float], dict[str, float], float | None, float | None, list[str]]