JSON Circuit Description Format¶
Project Overview | Core Solver Overview | 中文版
Status: maintained reference. The loader and
schemas/circuit.schema.jsonare the source of truth. This document describes the user-facing contract and should be updated with either one.
Purpose¶
The JSON circuit description separates topology, sizes, bias, and analysis metadata
from Python source code. Swap circuits by changing JSON files instead of editing
circuitopt/ac_solver.py, circuitopt/noise_solver.py, or circuitopt/transient_solver.py.
Schema file:
Example files:
examples/single_stage.json
examples/resistor_load_stage.json
examples/afe_explore.json
examples/periodic_rc.json
Minimal Structure¶
A valid circuit JSON needs at minimum:
{
"solved": ["OUT"],
"rails": {
"VDD": "VDD",
"GND": 0.0,
"IN": "VIN"
},
"devices": [
{"name": "M1", "drain": "OUT", "gate": "IN", "source": "VDD", "W": 2000, "L": 80}
],
"bias": {
"VDD": 40.0,
"VIN": 25.0
},
"outputs": ["OUT"]
}
solved— nodes the solver must find voltages for.rails— known-voltage nodes. Values can be numeric constants or keys inbias.devices— list of transistor devices (currently PMOS_TFT; model type is determined by thedevice_modelfactory, default"pmos_tft").bias— DC voltages for rail references.outputs— nodes observed for AC/noise/transient results.
Field Reference¶
name¶
Optional. Circuit name for display and logging.
solved¶
Required. Unknown node list; also defines the MNA/DAE vector ordering.
- At least one node.
- No duplicate names.
- Every node in
outputsmust be insolved.
rails¶
Required. Known-voltage node map.
"GND": 0.0— GND is always 0 V."VDD": "VDD"— VDD voltage is read frombias["VDD"].- Every node referenced by a device port must appear in
solvedorrails.
devices¶
Required. Each active device is a three-terminal transistor (drain/gate/source). Model implementation is selected by the device_model factory (default "pmos_tft").
Object form (preferred):
Array shorthand:
If using array form, W/L must be supplied in sizes.
sizes¶
Optional. Per-device dimensions, useful for separating topology from sizing.
- If a device object already has
WandL,sizesis not needed. - If both are present,
sizesoverrides the embedded W/L. - Every device must ultimately have W and L.
nf¶
Optional. Number of fingers (multiplies drain current).
Global:
Per-device:
Device-level NF in the device object is overridden by top-level nf when both exist.
models¶
Required whenever devices is non-empty. Every MOS must explicitly bind its PDK,
model, process section, and geometry bin policy. Missing or partial bindings fail
during parsing; there is no default-PDK fallback.
"models": {
"M1": {"pdk": "sky130", "model": "nmos",
"section": "inherit", "bin": "auto", "extract_w": 24.0},
"M3": {"pdk": "sky130", "model": "pmos",
"section": "inherit", "bin": "auto", "vb": 1.8, "extract_w": 12.0}
}
pdkandmodelselect the registered process and device model.sectionis a fixed card section such astt/ss, orinheritwhen a PVT run is allowed to select the section.binis an exact model/bin name, orautofor unique geometry-based selection. An exact selector must match the resolved card bin.typeis obsolete and rejected.- Remaining keys are forwarded to the device constructor. For SKY130 devices:
vb(bulk bias, volts; default 0),extract_w(µm — select a bundled reference-width card while the native BSIM instance uses the actualW),temperature(kelvin; default 300.15),NF(int). bulk_railoptionally names the physical rail that suppliesvb. It is useful when several rails share the same DC voltage; source-power reporting otherwise resolves a source-tied bulk or conventionalGND/VSS/VDDrail.- FreePDK45 (
"freepdk45.nmos"/"freepdk45.pmos", plus the low-threshold flavors"freepdk45.nmos_vtl"/"freepdk45.pmos_vtl"binding theNMOS_VTL/PMOS_VTLcards — vth0 0.322 V vs 0.4106 V at nominal, the standard choice for comparator input pairs near the VTG threshold) directly parses the flat BSIM4 level-54 cards and evaluates them with the in-process Berkeley BSIM4.5 backend. The cards declareversion=4.0; this metadata field does not select a separate equation path in the bundled kernel, and native device/5T OTA results are regression-checked against ngspice. Device keys includevb(0 for NMOS, normally 1.0 V for PMOS),corner(nom/tt/ss/ff/sf/fs; defaultnom),temperature(kelvin),NF,M, and supported numeric BSIM4 instance parameters. The backend supplies full terminal current, conductance, charge, capacitance, and correlated noise to DC, AC, noise, transient, PSS, PAC, and PNoise.extract_wis accepted as a legacy hint but native devices always use their actual geometry. The optionalfreepdk45_ngspice.nmos/.pmosaliases retain the old cached-grid evaluator, while the full-circuit ngspice helpers provide an external oracle. Cards live underPDK_ROOT/freepdk45/; seeexamples/freepdk45_5t_ota.json(simple) andexamples/freepdk45_fd_ota.json(the fully differential OTA design case, docs/freepdk45_fd_ota_design.md). - TSMC28HPC+ (
"tsmc28hpcp.nmos"/"tsmc28hpcp.pmos") binds the 0.9 Vnch_mac/pch_maccore wrappers from the licensed 1d8 HSPICE deck. Usevb=0.9for a PMOS bulk tied to the core supply. Supported corners arett/ss/ff/sf/fs(nomaliasestt);temperatureis in kelvin andNFis passed natively to the foundry macro. The portable default model entry isPDK/tsmc28hpcp/models/hspice/cln28hpcp_1d8_elk_v1d0_2p2.l; overrides areTSMC28_MODEL_DIR, thenTSMC28_PDK_ROOT. The default model types use the internal HSPICE parser plus native Berkeley BSIM4.5 backend for DC, AC, noise, transient, PSS, PAC, and PNoise; they do not launch ngspice. Explicittsmc28hpcp_ngspice.nmos/.pmosaliases retain the complete-circuit ngspice oracle for regression comparisons. See TSMC28HPC+ Local Adapter. - A mixed circuit (some devices OTFT, some silicon) is valid — e.g. a complementary
silicon OTA binds NMOS/PMOS devices independently. See
examples/sky130_5t_ota.json. - SKY130 normal simulation uses bundled resolved cards and the native C BSIM4 backend. FreePDK45 and TSMC28HPC+ use their local model files with the same native backend. A C compiler is needed for the first backend build; ngspice is an optional oracle/card-generation tool. Missing prerequisites raise a clear error. See PDK Support Matrix.
bias¶
Optional but usually needed. Supplies numeric values for rails string references.
If a rail references a key not in bias, the solve will fail.
adc¶
Optional closed-loop SAR workflow configuration. The circuit itself still uses the
ordinary devices/capacitors/vsources fields. Differential bit_inputs and
bit_inputs_bar name CDAC PWL keys from MSB to LSB; decisions are read from the
physical transient comparator node. Run it with circuit-opt adc --vin, --sweep,
or --sine. See examples/freepdk45_sar3.json (static 5T comparator) and
examples/freepdk45_sar6.json (6-bit, static three-stage comparator: two
diode-loaded differential preamps into a 5T mirror stage; its nominal ramp is
the ideal 64-code staircase) for complete configurations.
adc.clock¶
Optional comparator strobe for a clocked dynamic (StrongARM) comparator. When
present, sar_input_waveforms generates the named waveform key: it rests at low
and pulses to high around every bit's decision_time, so a dynamic latch
precharges while the CDAC settles and evaluates at the decision instant. Drive the
clocked tail / output-reset devices from that key via transient_inputs. Omitting
the block reproduces the static-comparator behaviour (no clock waveform emitted, so
examples/freepdk45_sar3.json renders a byte-identical netlist).
"clock": {"input": "clk", "bar_input": "clkb", "high": 1.0, "low": 0.0,
"eval_before": 3e-9, "reset_hold": 1e-9}
input— required transient waveform key for the strobe.bar_input— optional key for the complemented strobe (high + low - clock), for latches whose reset devices need the opposite phase (double-tail second stages, PMOS-tail latches).high/low— asserted (evaluate) / deasserted (reset) levels [V]; defaulthigh = adc.vref,low = 0.eval_before— seconds before eachdecision_timethat the strobe rises (default0.3 * bit_period); must be< bit_period/2 - edge_timeso the tested CDAC bit has already switched when the latch samples.reset_hold— seconds after eachdecision_timebefore the strobe resets (default0.1 * bit_period).
adc.mismatch¶
Optional per-instance mismatch Monte-Carlo config for the FreePDK45 SAR
path, consumed by circuitopt.sar_mismatch_mc. Every sigma defaults to 0.0, so
omitting the block (or leaving sigmas zero) reproduces the nominal conversion.
"mismatch": {
"sigma_vth0": 5e-3, "w0": 1.0, "l0": 0.05,
"sigma_cu": 0.01, "c_unit": 1e-14,
"dnl_threshold": 0.5, "inl_threshold": 0.5
}
sigma_vth0— transistor threshold-voltage sigma [V] at the reference areaw0*l0; per device it scales assigma_vth0 / sqrt(W*L / (w0*l0))(Pelgrom area law) and is injected as the BSIM4 instance parameterdelvto.sigma_vth0_nmos/sigma_vth0_pmosoverride it per polarity.sigma_cu— CDAC unit-capacitor relative sigma atc_unit; a cap of valueCgets relative sigmasigma_cu / sqrt(C / c_unit)(binary-weighted caps are paralleled units and match better).dnl_threshold/inl_threshold— |DNL|/|INL| yield limits in LSB (default 0.5).sweep_points— optional per-trial sweep subsample (default: the full2**n_bitscode centers). Below full density, transition DNL/INL and missing codes are not measurable (rows report NaN) and the yield gates oncode_err_thresholdinstead — the screening mode for 12-bit-class resolutions, where a full ramp is 4096 conversions per trial. The CLI flag--sweep-pointsoverrides it.code_err_threshold— |code error| yield limit in LSB for subsampled sweeps (default 0.5, i.e. every sampled center must read its own code).
outputs¶
Optional but needed for AC/noise/transient. Supports single-ended or differential.
Single-ended:
Differential:
Differential output is computed as the first node minus the second (VOP - VON).
input_drives¶
Optional. AC small-signal gate drive, keyed by device name.
- Only meaningful for devices whose gates are on rails.
- Unlisted gates are treated as small-signal ground.
- For differential input, use
+0.5/-0.5for a unit differential amplitude.
load_caps¶
Optional. Fixed load capacitors, stamped into AC/noise/transient.
Array form:
Object form:
resistors¶
Optional. Two-terminal resistors between nodes a and b, resistance R (ohms, must
be positive). DC adds branch current (Va-Vb)/R. AC/noise stamps conductance 1/R.
Transient stamps conductance. Thermal noise PSD 4kT/R is included in dev_psd,
keyed by resistor name.
Array form: ["RL", "OUT", "GND", 4e6].
capacitors¶
Optional. Two-terminal capacitors between nodes a and b, value C (farads, must
be positive). DC is open-circuit. AC stamps admittance jωC. Transient uses backward
Euler companion model. Equivalent to load_caps; the difference is that capacitors
entries have names and follow netlist convention.
Array form: ["CL", "OUT", "GND", 2e-12].
current_sources¶
Optional. Ideal DC current sources. Current I (amps, can be negative) flows from
nplus to nminus inside the source — i.e. pulls I from nplus, injects I
into nminus. DC enters KCL. Open-circuit (noiseless) in small-signal AC/noise.
Constant current in transient.
Array form: ["IB", "VDD", "OUT", 1e-6].
vccs¶
Optional. Voltage‑controlled current sources. Output current flows p → q:
I = gm * (Vctrl_p - Vctrl_n). DC enters KCL; AC stamps into G matrix; noiseless
(ideal); instantaneous in transient with full Jacobian contribution.
Array form: ["G1", "OUT", "GND", "IN", "GND", 1e-4].
vsources¶
Optional. Ideal voltage sources, solved with true MNA: each source adds one
branch‑current unknown and one constraint row V_p − V_q = value, so the system grows
from n nodes to n_aug = n + m. value is a constant EMF (number) or a transient
input‑waveform key (string) for a time‑varying E(t).
Array form: ["V1", "IN", "GND", 2.0]. At least one of p, q must be a solved node
(a source between two rails is rejected).
- DC pins the node voltage exactly (the node stays in the solved set);
ac_solvereports the source currents underbranch_currents(sign:p → qthrough the source). - AC / Noise treat a DC source as a short (AC ground); the ideal source carries no
thermal noise. If the source name appears in
ac_drives, it acts as an AC stimulus. - Transient supports constant or waveform‑keyed
E(t). The compiled Rust fixed‑grid kernel handles the augmentedn_aug = n + msystem directly — a circuit with vsource (or VCVS/CCVS) branch‑current unknowns still reportsresult["rust_grid_solver"] is True. Native BSIM adaptive Gear2 also handles the augmented system; its LTE norm controls dynamic node voltages and deliberately excludes algebraic ideal-source branch currents. The generic OTFT adaptive path still requiresn_aug == n. There is no Python numeric fallback in production; the retirednumba_grid_solverresult key no longer exists (tests assert its absence). - PSS / PAC / PNoise are supported too: the shooting monodromy and the harmonic‑balance matrices are bordered with the branch‑current unknowns (PNoise forces its dense path when a source is present).
vcvs¶
Optional. Voltage‑controlled voltage sources. Output voltage V_p − V_q = mu * (V_cp − V_cn).
Each VCVS adds a branch‑current unknown (like an ideal voltage source) and a constraint
row with entries for the control nodes. Ideal / noiseless.
Array form: ["E1", "OUT", "GND", "INP", "INN", 100.0]. At least one of p, q must
be a solved node.
cccs¶
Optional. Current‑controlled current sources. Output current I_out = beta * I_ctrl
flows p → q. The control current I_ctrl is the branch current of a voltage source
(vsource / VCVS / CCVS) named by ctrl_name. Ideal / noiseless. Does NOT add a new
branch‑current unknown — it references an existing one.
Array form: ["F1", "OUT", "GND", "V1", 2.0]. ctrl_name must reference a vsource,
VCVS, or CCVS in the same topology.
ccvs¶
Optional. Current‑controlled voltage sources. Output voltage V_p − V_q = gamma * I_ctrl.
The control current I_ctrl is the branch current of a voltage source named by
ctrl_name. Each CCVS adds a branch‑current unknown. Ideal / noiseless.
Array form: ["H1", "OUT", "GND", "V1", 100.0]. At least one of p, q must be a
solved node. ctrl_name must reference a vsource, VCVS, or CCVS (which has a branch
current). CCCS and CCVS can cascade: a CCCS can control on a CCVS's branch current.
dc_guesses¶
Optional. DC initial guesses. Each entry can specify some or all solved nodes.
Provide multiple physically reasonable guesses for circuits with multistability or positive feedback.
aliases¶
Optional. Adds aliases to the DC operating point for compatibility with older code or report fields.
The returned dc_op includes both the original solved nodes and the aliases.
transient_inputs¶
Optional. Maps transient input waveform keys to device gates.
Call transient with:
ac_drives¶
Optional. Like input_drives, but drives a node instead of a device gate. Used
for testbench front-ends where the AC stimulus enters through a passive network
rather than directly at a transistor gate.
periodic¶
Optional. Default large-signal periodic excitation for PSS/PAC/PNoise and periodic transient dispatch.
"periodic": {
"frequency": 1000.0,
"n_points": 101,
"inputs": {
"vin": {"type": "constant", "value": "VIN"},
"clk": {"type": "pulse", "low": 0.0, "high": "VDD", "duty": 0.5,
"rise": 20e-6, "fall": 20e-6}
},
"node_inputs": {"VIN": "vin", "CLK": "clk"},
"current_inputs": [{"p": "VDD", "q": "OUT", "input": "iqinj"}],
"signed_devices": ["SW1", "SW2"]
}
Supported waveform forms:
- Number or bias key: constant waveform, e.g.
"VIN". constant/dc: constant waveform.sine/sin/cosine/cos: fields includedc,amplitude,phase,frequency, orharmonic.square: ideal square wave withlow,high,duty, anddelay.pulse: finite-edge periodic pulse with optionalriseandfall.pwl: periodic piecewise-linear waveform withtimesandvalues.
analyses¶
Optional. Unified analysis-dispatch configuration. Calling
circuitopt.analysis_dispatch.run_analysis_suite(spec) runs configured analyses in
the fixed order ac -> noise -> transient -> pss -> pac -> pnoise; PAC/PNoise
automatically reuse or create the required PSS result.
The authoritative option registry for transient / pss / pac / pnoise
lives in circuitopt.analysis_options. analysis_dispatch.py derives forwarded
solver kwargs and defaults from that registry, and the JSON schema is regression
tested against the same registry so new solver options do not silently drift.
Unknown keys in an analyses block are rejected with an error (a typo such as
max_sidebands for max_sideband is not silently ignored).
"analyses": {
"pss": {
"corner": "slow",
"integration_method": "gear2",
"adaptive": true,
"tstab_periods": 10,
"final_n_points": 3201,
"residual_tol": 1e-12,
"max_shooting_iters": 2,
"jacobian_reuse": true,
"analytic_jacobian": true
},
"pac": {
"freqs": [100.0, 1000.0],
"input_drive": {"vin": 1.0},
"analytic": true,
"max_sideband": 10,
"n_period_samples": 384,
"time_domain": false,
"td_integration": "gear2",
"td_n_period_samples": 768,
"lti_fast_path": true,
"cache_linearization": true,
"cache_forcing": true
},
"pnoise": {
"freqs": [100.0, 1000.0],
"input_drive": {"vin": 1.0},
"max_sideband": 0,
"n_period_samples": 32,
"lti_fast_path": true,
"cache_linearization": true,
"band": [100.0, 1000.0]
}
}
Grid and stimulus¶
| Key | Analyses | Meaning |
|---|---|---|
freqs |
ac, noise, pac, pnoise |
An explicit list of frequencies, or an object such as {"start": 1.0, "stop": 1e4, "num": 41, "scale": "log"}. |
input_drive |
pac, pnoise |
Small-signal complex amplitude per input. A complex value may be a number, [real, imag], or {"real": ..., "imag": ...}. |
corner |
all | "typical", "slow", "fast", or an explicit model-shift map. |
PAC and PNoise reuse a PSS orbit, so all three must run at the same corner. When
pss sets no corner, dispatch inherits the unique PAC/PNoise corner. A dependent
analysis that requests a corner different from an already-built PSS is an error.
PSS¶
| Key | Default | Meaning |
|---|---|---|
analytic_jacobian |
true |
Build the monodromy matrix in one orbit pass from the small-signal G(t)/C(t) stamps. false selects the original path, which needs n_state finite-difference period runs. |
jacobian_reuse |
true |
Reuse the Jacobian across shooting iterations with a Broyden update. Set false for difficult convergence or tight reference comparisons. |
jacobian_rebuild_interval |
— | Rebuild the Jacobian every N iterations, e.g. 2, instead of relying on the Broyden update alone. |
final_n_points |
— | Point count of the deterministic final grid; see Reproducible conversion. |
Adaptive timestepping¶
Applies to gear2 transient and PSS. Set "adaptive": true to enable LTE-controlled
adaptive timestepping; dispatch then forwards the options below. Pulse and square
periodic inputs get edge breakpoints inserted before the adaptive run.
| Key | Default | Meaning |
|---|---|---|
adaptive_reltol |
see adaptive_config.py |
Relative local-error tolerance. |
adaptive_vabstol |
see adaptive_config.py |
Absolute voltage error tolerance. |
adaptive_iabstol |
see adaptive_config.py |
Absolute current error tolerance. |
adaptive_max_steps |
see adaptive_config.py |
Step-count ceiling. |
adaptive_h0 |
— | Initial step size. |
cap_mode |
"charge" |
Capacitance operator. Only "charge" (id 0) and "average" (id 1) and their documented aliases are accepted. |
Native BSIM transient¶
Both options below default to zero, and both should be chosen by A/B against a run with much tighter step tolerances rather than by inspection.
| Key | Constraint | Meaning |
|---|---|---|
bsim_model_bypass_tolerance |
0 ≤ x ≤ newton_vtol, in volts |
A positive value enables the compact model's standard device bypass, and only inside the exclusive transient Newton loop. Validate with a circuit-level trajectory and signoff A/B before setting it. |
newton_error_fraction |
0 ≤ x ≤ 1, adaptive only (a fixed grid rejects it) |
Each node's Newton update converges below this fraction of its own step-error budget adaptive_reltol*abs(V) + adaptive_vabstol, instead of the single absolute newton_vtol that holds a rail node and a node at zero to the same number. Nodes near zero are therefore held tighter; large ones stop once their remaining error is negligible against the error the step controller already accepts. |
Reproducible PAC/PNoise conversion¶
Set final_n_points together with "adaptive": true and at least one stabilization
period. Adaptive gear2 then supplies only the warm start and its accepted grid is not
frozen; dispatch builds a deterministic final grid carrying the same pulse/square edge
breakpoints. Shooting, monodromy, profiling, and the returned orbit all use that grid.
PAC¶
| Key | Default | Meaning |
|---|---|---|
analytic |
true |
Analytic-adjoint harmonic balance: one adjoint linear solve per frequency on the orbit conversion matrix, with no extra transient runs. Set false only for the original finite-difference shooting path. |
max_sideband |
— | Harmonic-balance resolution. |
n_period_samples |
— | Harmonic-balance resolution. |
time_domain |
false |
Try the accelerated time-domain Floquet path first. Intended for rail-driven chopper-like circuits; unsupported topologies fall back to harmonic balance when analytic is true. |
td_integration |
— | BDF setting of the time-domain path. |
td_n_period_samples |
— | Grid setting of the time-domain path. |
PAC/PNoise caches¶
| Key | Default | Meaning |
|---|---|---|
lti_fast_path |
true |
Static-orbit LTI fast path. |
cache_linearization |
true |
Reuse linearization attached to the PSS result. |
cache_forcing |
true |
Reuse forcing attached to the PSS result. |
Set any of them to false to force fresh finite-difference or harmonic-balance work.
With the caches on, PNoise reuses the sampled G(t)/C(t), the HB blocks, and
identical-frequency adjoint solves from pss_result.
PNoise¶
| Key | Default | Meaning |
|---|---|---|
hb_solver |
"auto" |
"sparse" forces a sparse direct solve, "iterative" forces block-Jacobi preconditioned GMRES. "auto" keeps small matrices dense and switches only when the HB matrix is large and very sparse. |
compute_condition |
false |
PAC boundary-matrix condition diagnostics. Off by default because they require an SVD at every frequency; "profile": true or "debug": true also enable them. |
The JSON dispatch pnoise entry is the generic HB path. The chopper helper
pmos_chopper_pnoise(...) defaults to the TD-adjoint PNoise path for Cadence
alignment. Use that wrapper, or call circuitopt.pnoise_solver.pnoise_solve(...,
time_domain=True) directly, when the truncation-free chopper PNoise path is required.
signoff¶
Optional. Defines measurements and acceptance limits explicitly. This block is the only path that produces phase margin, settling time, integrated-noise, and saturation signoff measurements. They are not inferred from an arbitrary AC response, the last transient sample, the simulated noise span, or every MOS in the DUT.
"ac_drives": {"Vinj": 1.0},
"signoff": {
"measurements": {
"phase_margin": {
"analysis": "ac",
"injection_source": "Vinj",
"return_signal": {"RETURN_P": 1.0, "RETURN_N": -1.0},
"polarity": -1,
"return_scale": 0.5
},
"settling_time": {
"analysis": "transient",
"signal": {"OUTP": 1.0, "OUTN": -1.0},
"target": 0.45,
"start_time": 5e-9,
"end_time": 10e-9,
"tolerance": {"relative": 0.001, "reference": 0.9}
},
"noise": {
"analysis": "noise",
"band": [1e3, 50e6],
"references": ["input", "output"]
},
"saturation": {
"analysis": "ac",
"devices": ["M1", "M2", "M3", "M4"],
"minimum_headroom": 0.02
}
},
"constraints": {
"phase_margin": {"min": 60.0},
"settling_time": {"max": 5e-9},
"integrated_input_noise": {"max": 40e-6},
"saturation": {"equals": true}
}
}
For phase margin, injection_source must be a constant 0 V source breaking
between two solved nodes and must be the sole non-zero voltage-source entry in
ac_drives; return_signal is an explicit weighted sum of solved nodes. The
loop gain is the declared return signal divided by the injection source's
positive-terminal node voltage, with polarity and optional positive
return_scale. The reference signal is therefore fixed by the physical loop
break, so an ordinary amplifier transfer function cannot be mistaken for loop
gain.
Settling requires a fixed target and window. tolerance is either
{"absolute": <volts>} or {"relative": <ratio>, "reference": <volts>};
relative tolerance never silently uses the target or final sample as its
reference. The signal is settled at the earliest sample after start_time that
remains inside tolerance through end_time.
Noise band must lie inside the simulated noise frequency grid. Saturation
checks only the listed MOS devices and applies minimum_headroom in volts.
For a transient saturation check, set "analysis": "transient" and declare
named checkpoints, for example
[{"name": "static", "time": 0.0}, {"name": "settled", "time": 5e-9}].
The runner interpolates every solved node at each declared time and re-evaluates
the exact PDK-bound MOS operating regions; it does not reuse the initial AC
operating point or infer a checkpoint from the last sample.
Unknown nodes/devices, missing analyses, unsupported operating regions, and
ambiguous loop injection are signoff_configuration invalid results.
The unified signoff result is:
{
"status": "pass",
"measurements": {"phase_margin": {"value": 72.1, "unit": "deg", "status": "valid"}},
"constraints": {"phase_margin": {"observed": {}, "checks": {}, "passed": true}},
"passed": true,
"worst_case": {"measurement": "phase_margin", "passed": true, "normalized_margin": 0.201}
}
When no signoff block is present, the result has status:
"not_configured", passed: null, and still reports non-signoff gain, UGF, and
source-power measurements when AC was run.
For multi-testbench PVT aggregation, see
Signoff Campaigns. Campaign manifests have their own
strict schema at schemas/signoff_campaign.schema.json.
explore¶
Optional. Design-space exploration configuration — variables to sweep with ranges,
feasibility constraints (gain, BW, IRN, power, area), and optimization objectives.
Consumed by circuitopt.explore (sample → evaluate → constrain → Pareto-select),
circuitopt.dataset (sample → evaluate every candidate, no filtering — a labeled training
set), and circuitopt.optimize (screen a trained surrogate → verify the shortlist on the
solver).
"explore": {
"variables": {
"in_pair_W": {"min": 40000, "max": 90000, "targets": ["M7.W", "M8.W"]},
"VCM": {"min": 28.0, "max": 33.0}
},
"constraints": {"gain_dB": {"min": 20}, "bw_Hz": {"min": 100},
"irn_uV": {"max": 44.5}},
"objectives": {"area": "min", "power_uW": "min"},
"band": [0.05, 100.0],
"freqs": {"start": -2, "stop": 3, "num": 81}
}
variables— each entry needs numericmin/max.targetslets one variable drive several keys at once (matched/symmetric device pairs); it defaults to[<variable name>].round(decimals) snaps sampled values to a grid;introunds to a whole number (handy for W/L/NF).constraints— each metric needs aminand/ormaxbound. Known metrics:gain_dB,gain_peak_dB,bw_Hz,irn_uV,power_uW,area.objectives—{metric: "min" | "max"}; at least one is required.band—[f_lo, f_hi](Hz) for the band-integratedirn_uVmetric.freqs— the AC/noise analysis grid:{"start": <log10 Hz>, "stop": <log10 Hz>, "num": <points>}(logarithmic).
Target syntax — beyond "DEV.W" / "DEV.L" / "DEV.NF" (device sizes) and a
bare bias key, targets supports structural design axes (each rebuilds the circuit
per candidate; consumed by circuitopt.dataset/circuitopt.optimize, not by circuitopt.explore):
| Target | Axis | Notes |
|---|---|---|
"<CapName>.C" |
a named capacitor's value (F) | the capacitors entry needs a "name" |
"<ResName>.R" |
a named resistor's value (Ω) | the resistors entry needs a "name" |
"periodic.frequency" |
the periodic stimulus clock frequency | needs a periodic block |
"pvt0" / "pbeta0" |
continuous global process shift | routes into evaluate(corner=...); sampling this turns a discrete corner sweep into continuous-PVT training data |
See the models field above for binding a variable's target device to a non-default
PDK (e.g. sweeping a SKY130 device's W); the explore block itself doesn't change
— models and explore.variables compose freely.
Complete Examples¶
examples/single_stage.json # Single-transistor common-source (PMOS_TFT)
examples/resistor_load_stage.json # PMOS + resistive load + output cap + current source
examples/voltage_divider.json # Ideal voltage source (true MNA) — resistor divider
examples/vcvs_amplifier.json # VCVS amplifier — linear gain 100×
examples/sc_lpf.json # Switched-capacitor LPF (2-phase, PMOS switches + vsource clocks)
examples/afe_explore.json # 10-transistor AFE with explore config
examples/periodic_rc.json # Passive RC with PSS/PAC/PNoise dispatch
examples/sky130_5t_ota.json # Silicon SKY130 complementary 5T OTA — `models` block + explore/dataset/optimize
examples/freepdk45_sar3.json # FreePDK45 differential 3-bit SAR — full-charge .tran + ADC metrics
Load and run:
import numpy as np
from circuitopt.circuit_loader import load_circuit_json
from circuitopt.ac_solver import ac_solve
from circuitopt.noise_solver import noise_analysis
from circuitopt.transient_solver import transient
spec = load_circuit_json("examples/single_stage.json")
freqs = np.logspace(0, 4, 121)
ac = ac_solve(spec.sizes, spec.bias, freqs, topo=spec.topology, nf=spec.nf)
noise = noise_analysis(spec.sizes, spec.bias, freqs, topo=spec.topology, nf=spec.nf)
t = np.linspace(0, 1e-3, 100)
vin = np.full_like(t, spec.bias["VIN"])
tran = transient(spec.sizes, spec.bias, t, topo=spec.topology,
nf=spec.nf, inputs={"vin": vin})
Or run the analyses configured inside the JSON:
from circuitopt.analysis_dispatch import run_analysis_suite
from circuitopt.circuit_loader import load_circuit_json
spec = load_circuit_json("examples/periodic_rc.json")
results = run_analysis_suite(spec)
pac_gain = results["pac"]["gains"]
pnoise_irn = results["pnoise"]["irn_uV_band"]
Current Limitations¶
The JSON format is a local-solver circuit description, not a full SPICE netlist.
Supported:
- Three-terminal transistor devices (PMOS_TFT via
TransistorModelinterface). - Resistors, capacitors, ideal DC current sources, VCCS (voltage‑controlled current sources), VCVS (voltage‑controlled voltage sources), CCCS (current‑controlled current sources), CCVS (current‑controlled voltage sources), ideal voltage sources (true MNA).
- DC/AC/noise/transient shared topology (resistors include thermal noise; controlled sources and ideal voltage sources are ideal/noiseless).
- Single-ended or differential outputs.
- Fixed load capacitance.
- AC gate drive and node drive.
- Transient gate waveforms and node waveforms.
- Periodic PSS/PAC/PNoise dispatch from JSON.
- DC initial guesses.
Supported (model abstraction):
- Device model registry (
circuitopt/device_model.py) —TransistorModelABC + factory. New model types can be added without modifying solver code. - NMOS and PMOS across AT4000TG (PMOS-only), SKY130, FreePDK45, and TSMC28HPC+.
- Per-device model binding via the
modelsfield — mixed OTFT/silicon circuits with an explicit PDK, model, section, and geometry-bin selector for every MOS. Missing or partial bindings fail during parsing. - Silicon DC/AC/noise/transient; SKY130, FreePDK45, and TSMC28HPC+ use the internal native BSIM4 backend.
Not yet supported:
- ADC transient noise, layout parasitic extraction, and transistor-level SAR digital control.
- Multi-output simultaneous analysis.
- Hierarchical subcircuits.
- Arbitrary user-circuit SPICE netlist import. The internal HSPICE parser is currently scoped to supported local model-library elaboration.