Skip to content

Testbenches

spicerack.testbenches has ready-made benches: you pass your design, a bench wraps it in the right stimulus and load, runs the analyses, and returns named metrics. Each one is checked against a closed-form answer in the test suite.

Running a bench

import spicerack as ps
from spicerack.testbenches import step_response

rc = ps.Subcircuit("rc", ["vin", "vout"])     # stands in for your amplifier
rc.R("1", "vin", "vout", 1e3)
rc.C("1", "vout", rc.gnd, 100e-12)            # tau = 100 ns

bench = step_response(ps, rc, period=4e-6, v_low=0.0, v_high=1.0)
m = bench.measure()                           # runs the transient, returns a dict
print(m["settle_rise_0p1pct_s"])              # about 6.9 * tau
print(bench.netlist("ngspice"))               # the deck it ran

Every bench takes ps and your Subcircuit first, then keyword arguments naming your nodes (input_node=, output_node=, supply_node=, ...) and the conditions (load, bias, sweep range). bench.measurements lists the metric names it returns. A bench either returns all of them or raises; it never returns a partial dict.

The benches marked "classic" below have no runner: run their testbench yourself and pass the result to metrics:

from spicerack.testbenches import amplifier_voltage_gain

bench = amplifier_voltage_gain(ps, rc, input_node="vin", output_node="vout")
ac = bench.testbench.ac(variation="dec", number_of_points=100, start_frequency=1e3, stop_frequency=1e9)
print(bench.metrics(ac)["bandwidth_hz"])      # 1/(2 pi R C), about 1.59 MHz

Catalog

Names in <> expand per spot frequency or step, such as psrr_db_at_1khz.

Amplifiers, loops, noise, ports, RF

Bench Metrics
opamp_open_loop a0_db, f_p1_hz, gbw_hz, ugf_hz, pm_unity_deg, gm_unity_db
loop_gain t0_db, ugf_hz, pm_deg, gm_db, phase_crossover_hz (Tian injection at a loop break you bring out as two ports)
step_response settling at 0.1% and 0.01% (rise and fall), overshoot, slew, v_final, static_error_pct
noise_spectrum output and input-referred density and band rms, 0.1 to 10 Hz peak-to-peak, kT/C-equivalent capacitance
cmrr cmrr_dc_db, cmrr_corner_hz, a_cm_db, cmrr_db_at_<f>
offset_icmr vos_v, icmr_low_v, icmr_high_v, bias and offset currents
port_impedance z_dc_ohm, r_parallel_ohm, c_parallel_f, z_peak_ohm, z_peak_hz, z_ohm_at_<f>
output_swing vout_min_v, vout_max_v, swing_pp_v, a_ol_peak_db
harmonic_distortion thd_db, thd_pct, hd2_dbc, hd3_dbc, sfdr_dbc, sndr_db
rf_two_port s11_db, s21_db, s12_db, s22_db, nf_db
rf_compression_ip3 gain_db, p1db_in_dbm, iip3_dbm, oip3_dbm, im3_slope
amplifier_voltage_gain (classic) gain_midband_db, bandwidth_hz, phase_margin_deg
amplifier_transimpedance (classic) transimpedance_ohm, tia_bandwidth_hz
amplifier_current_gain (classic) current_gain_midband, current_gain_bandwidth_hz
charge_amplifier (classic) q_injected, delta_vout, droop_rate

Regulators, references, current-mode, charge

Bench Metrics
supply_rejection psrr_dc_db, psrr_min_db, psrr_min_hz, psrr_db_at_<f>
regulator_line_sweep vout_v, line regulation (V/V and %/V), dropout_v, ground_current_a
regulator_load_sweep load_regulation_ohm, load_regulation_pct, quiescent_current_a, ground_current_max_load_a
regulator_step undershoot_v, overshoot_v, settling_up_s, settling_down_s, final_shift_v (load or line step)
supply_startup started, startup_time_s, startup_overshoot_v, inrush_peak_a (supply ramped from off)
regulator_loop_margins worst phase and gain margin over a load and C_out/ESR grid
current_mirror_output iout_a, ratio_error, rout_ohm, compliance_v, compliance_rout_v, vin_v
current_reference iref_27c_a, tempco_box_ppm_c, ptat_intercept_c, or supply sensitivity
transconductor_gm gm0_s, linear_range_v, iout_max_a, offset_v
switched_cap_transfer charge_gain, leak_factor, gain_error, settling_time_s
charge_pump_current i_up_a, i_dn_a, mismatch_pct_max, output_range_v
bandgap_reference (classic) vref, line_regulation_ppm_v
bandgap_tempco (classic) vref_27c, tempco_box_ppm_c, tempco_endpoint_ppm_c

average_power(tran, "Vdd", t0, t1, node="vdd") gives the mean power a supply delivers.

Data converters

Bench Metrics
adc_dynamic sndr_db, snr_db, thd_db, sfdr_dbc, enob_bits, enob_fs_bits, signal_dbfs, outlier_count
adc_static_linearity dnl_max_lsb, inl_max_lsb, missing_codes, offset_error_lsb, gain_error (ramp or sine histogram)
pipeline_stage_residue interstage_gain, gain_error, residue_max_v, threshold_error_max_v
comparator_threshold offset_v, hysteresis_v, chatter_count, resolution_v
comparator_delay delay at large and small overdrive, tau_regen_s, v_meta_v, decision_errors
comparator_kickback kick_charge_c, kick_peak_v, kick_residual_v
dac_transition settling_time_s, delay_s, slew_rate_v_per_s, glitch impulse (V s)
dac_dynamic sfdr_dbc, thd_db, sndr_db, enob_bits, fundamental_dbfs
dac_output_impedance zout_dc_ohm, zout_at_f_ohm, zout_corner_hz
sample_hold_dynamic sndr_db, sfdr_dbc, thd_db, sampled_gain_db
sample_hold_track_bandwidth, sample_hold_feedthrough track_bandwidth_hz; feedthrough_db
dac_static_linearity, adc_ramp, sample_hold (classic) DNL/INL and monotonicity; missing codes; acquisition, droop, pedestal

coherent_bin(M, target_fraction) picks an input frequency bin for a coherent FFT record.

Oscillators, PLLs, DLLs, clocks

Bench Metrics
oscillator_frequency frequency_hz, amplitude_pp_v, duty_cycle, startup_time_s
oscillator_sweep f_min_hz, f_max_hz, tuning_range_ratio, df_dv_hz_per_v (KVCO), monotonic
oscillator_jitter period and cycle-to-cycle jitter, phase noise at offsets (from a trnoise source you add)
crystal_negative_resistance negative_resistance_ohm, load_capacitance_f
pll_lock_time lock_time_s, frequency_overshoot_ratio, control_final_v, control_ripple_v
pll_loop_margins crossover_hz, phase_margin_deg, closed_loop_bw_hz, peaking_db
pll_reference_spur reference_spur_dbc, per-harmonic spurs
pfd_cp_transfer kpd_a_per_rad, static_offset_s, dead_zone_s, linearity_pct
delay_line_range, dll_lock delay range and kd_s_per_v; static phase error, lock cycles, false-lock harmonic
clock_buffer_timing tplh_s, tphl_s, dcd_s, duty_out, supply-induced jitter
pll_lock (classic) output_frequency_hz, frequency_error_ratio, control_final_v

Not measured, because SPICE cannot show them honestly: device phase noise and jitter on ngspice (it adds no device noise to a transient; inject trnoise instead), charge injection from a behavioural switch (it carries no charge), and start-up from a DC sweep (use supply_startup).

Corners and Monte Carlo

run_corners runs one bench per corner and seed and returns one row per run. build makes a fresh bench for a corner; measure runs it.

from spicerack.testbenches import CornerCase, run_corners

corners = [CornerCase("cold", temperature=-40), CornerCase("hot", temperature=125)]
rows = run_corners(lambda c: step_response(ps, rc, period=4e-6),
                   lambda bench: bench.measure(),
                   corners)
for row in rows:
    print(row["corner"], row.get("settle_rise_0p1pct_s"), row.get("error"))

A CornerCase sets temperature, parameters (.param values), model_libraries and vdd. pdk_corners(lib_path, sections, temperatures, vdds) builds the full grid for a PDK. seeds=N repeats each corner with .options seed=1..N, which varies the statistical (mismatch) models. A run that fails gets an "error" entry instead of metrics, so one bad corner does not lose the rest. For a yield bound from the pass count, use remeasure.yield_ci.

Checking specs

from spicerack.testbenches import ValidationRule, validate_metrics

report = validate_metrics(m, [
    ValidationRule("settles fast", "settle_rise_0p1pct_s", maximum=1e-6),
    ValidationRule("no overshoot", "overshoot_rise_pct", maximum=1.0),
])
print(report.passed)

Cell characterization

For Liberty tables of standard cells: input_capacitance(ps, dut, pin=, vdd=), delay_table(ps, dut, input_pin=, output_pin=, vdd=, slews=[...], loads=[...]) (NLDM delay and transition), and setup_time / hold_time (bisection on a passes(tran) -> bool you write).