"""Level-1 (square-law) sizing pipeline: discrete W/L via OR-Tools CP-SAT.
Used for the card-less ``generic`` technology. Reuses the shared preprocessing
(:mod:`circuitgenome.sizer.shared.preprocess`) and metric evaluation
(:mod:`circuitgenome.sizer.shared.metrics`); the discrete geometry search is built
in :mod:`.constraints`.
"""
from __future__ import annotations
from ortools.sat.python import cp_model
from circuitgenome.recognizer.models import (
FunctionalBlockRecognitionResult,
ParsedNetlist,
SubcircuitRecognitionResult,
)
from circuitgenome.synthesizer.models import TopologyTemplate
from ..shared.device_model import Level1Model
from ..shared.metrics import _evaluate_metrics
from ..shared.models import SizingResult, SizingSpec, TechParams, TransistorSizing
from ..shared.preprocess import (
assign_ids,
check_topology_match,
compute_requirements,
deduplicate_devices,
extract_slot_resistors,
extract_slot_transistors,
size_load_resistors,
)
from .constraints import build_model
[docs]
def size_level1(
parsed: ParsedNetlist,
sr_result: SubcircuitRecognitionResult,
fbr_result: FunctionalBlockRecognitionResult,
topology: TopologyTemplate,
tech: TechParams,
spec: SizingSpec,
*,
time_limit_s: float = 30.0,
) -> SizingResult:
"""Size a circuit with the Level-1 square-law model + CP-SAT geometry search."""
slot_transistors = extract_slot_transistors(fbr_result)
topology_warnings = check_topology_match(slot_transistors, topology.name)
all_transistors = deduplicate_devices(slot_transistors)
ids_map = assign_ids(slot_transistors, all_transistors, spec)
# Size resistor loads (deterministic) and model them in the first-stage Rout.
resistors = size_load_resistors(extract_slot_resistors(fbr_result), spec, tech)
gd_load_r = (1.0 / min(resistors.values())) if resistors else 0.0
# Level-1 square-law model; discrete W/L via CP-SAT.
dev_model = Level1Model(tech)
gm_req_map, vod_max_map, cc_pf, cc2_pf, gm_ceiling_warnings = compute_requirements(
slot_transistors, all_transistors, ids_map, tech, spec, dev_model, gd_load_r
)
all_warnings = topology_warnings + gm_ceiling_warnings
cp_mdl, W_vars, L_vars = build_model(
all_transistors, slot_transistors, ids_map, gm_req_map, vod_max_map, tech
)
solver = cp_model.CpSolver()
solver.parameters.max_time_in_seconds = time_limit_s
status = solver.solve(cp_mdl)
status_name = solver.status_name(status)
if status not in (cp_model.OPTIMAL, cp_model.FEASIBLE):
return SizingResult(
transistors={},
cc_pf=cc_pf,
metrics={},
margins={},
solver_status=status_name,
cc2_pf=cc2_pf,
warnings=all_warnings,
resistors=resistors,
)
# Extract solution: convert integer step-units back to µm.
w_step = tech.width.step
l_step = tech.length.step
transistor_sizing = {}
for ref, (device, _slot) in all_transistors.items():
w_um = solver.value(W_vars[ref]) * w_step
l_um = solver.value(L_vars[ref]) * l_step
ids_a = ids_map[ref]
transistor_sizing[ref] = TransistorSizing(
ref=ref, w_um=w_um, l_um=l_um, ids_a=ids_a,
vgs_v=dev_model.vgs(device.type, w_um, l_um, ids_a),
vds_sat_v=dev_model.vds_sat(device.type, w_um, l_um, ids_a),
)
metrics, margins = _evaluate_metrics(
transistor_sizing, slot_transistors, cc_pf, tech, spec, dev_model,
cc2_pf=cc2_pf, gd_load_r=gd_load_r,
)
return SizingResult(
transistors=transistor_sizing,
cc_pf=cc_pf,
metrics=metrics,
margins=margins,
solver_status=status_name,
cc2_pf=cc2_pf,
warnings=all_warnings,
resistors=resistors,
)