* ============================================================================= * power_core_load_step.cir * TensorRail-Mini — 1.2 V FPGA Core Rail Load-Step Transient * ============================================================================= * * What this simulation proves * --------------------------- * When the ECP5 systolic array starts a tile computation it switches ~200 mA * of additional dynamic current on the monitored 1.2 V core rail. This file answers * three bring-up questions: * * 1. Does the 1.2 V rail stay inside the ±5 % window (1.14 V – 1.26 V) * during the transient? * 2. How quickly does the loop recover (target: < 50 µs)? * 3. Is the peak inductor current within the 3 A rating of L2? * * Approach: behavioural average model * ------------------------------------ * A switching regulator is modelled as a voltage-controlled voltage source * (VCVS) acting as a proportional-integral error amplifier driving an * average switch-node voltage. This is the standard "behavioural average * model" taught in Middlebrook & Ćuk (1976) and is accurate for frequencies * well below Fsw/10 = 150 kHz for a 1.5 MHz switcher. No vendor SPICE model * is needed. The model captures: * - DC regulation (Vout → Vfb = Vref via the resistor divider) * - Loop bandwidth and phase margin (set by the compensator pole/zero) * - Inductor current and output voltage ripple * * It does NOT capture: * - Switching noise / EMI * - Gate-drive losses * - Subharmonic oscillation (needs cycle-by-cycle model) * * Hardware mapping * ---------------- * Regulator : TPS62130ARGTR 3 A synchronous buck, Fsw = 1.5 MHz * Vin : 3.3 V (output of U1 3.3 V buck on carrier) * Vout : 1.2 V (FPGA VCCCORE / VCCAUX rail) * Inductor : L2 1.5 µH Würth 744043150, DCR ≈ 55 mΩ, Isat = 3 A * Bulk cap : C9 100 µF Murata GRM32ER61A107ME20L, ESR ≈ 5 mΩ * HF bypass : C10 400 nF (4 × 100 nF 0402 X7R), ESR ≈ 12.5 mΩ * FB divider : R5 = 103 kΩ (top), R6 = 100 kΩ (bottom) * → Vfb = Vout × R6/(R5+R6) = 1.2 × 100/203 = 0.5911 V ≈ Vref = 0.590 V * Load : ECP5-85F idle ≈ 50 mA; active 4×4 tile model ≈ 250 mA * → step magnitude: +200 mA in ~1 µs * * Run with ngspice (batch mode): * ngspice -b power_core_load_step.cir * * Or interactively: * ngspice power_core_load_step.cir * * Output: simulation/power_core_load_step.raw (ngspice binary RAW format) * View with: ngspice -b power_core_load_step.cir (then open .raw) * or import into Python with: pip install PySpice * or open directly in KiCad's built-in waveform viewer. * ============================================================================= .title TensorRail-Mini VCCCORE 1.2V Load-Step Transient * ── Transient analysis ──────────────────────────────────────────────────────── * 20 ns timestep, 300 µs total. The load step hits at t = 50 µs. * Recovery should complete by ~100 µs; remainder shows steady-state ripple. .tran 20n 300u 0 20n * ── Supply rail ────────────────────────────────────────────────────────────── * The 1.2 V core regulator is fed from the carrier's +3V3 rail generated by U1. * USB-C VBUS and the D1 input diode are validated at the board level; this model * starts at U2's input because it focuses on FPGA core-rail transient response. Vin vin 0 DC 3.3 * ── Feedback reference ─────────────────────────────────────────────────────── * TPS62130 internal bandgap reference: 590 mV (typical from datasheet §8.3). Vref vref 0 DC 0.590 * ── Feedback divider (R5 top = 103 kΩ, R6 bottom = 100 kΩ) ────────────────── * Vfb = Vout × R6 / (R5 + R6) = Vout × 100k / 203k = Vout × 0.4926 * At Vout = 1.2 V: Vfb = 0.5911 V → error ≈ +1 mV from Vref (< 0.2 % → OK) * * These are the actual BOM values (E96 series) from hardware/bom.csv. * Changing R5 or R6 changes Vout; this is the first thing to verify on a new * board (Phase 1, step 2 of docs/bringup_plan.md). Rfbt vout vsense 103k ; R5 top divider Rfbb vsense 0 100k ; R6 bottom divider * ── Compensator / error amplifier ──────────────────────────────────────────── * Type-II (PI + lead) compensator modelled as a Laplace-domain VCVS. * * Transfer function: Gc(s) = Aol × (1 + s/ωz) / ( s × (1 + s/ωp2) ) * * Aol = 1000 (60 dB open-loop gain, typical OTA) * ωz = 2π × 30 kHz zero (cancels LC double-pole at ~24 kHz) * 1/s = true integrating pole at DC (zero steady-state error) * ωp2 = 2π × 1 MHz HF roll-off pole (above Fsw/2, for stability) * * Denominator form: s × (1 + s/ωp2) — pure 1/s integrator times one HF pole. * Using 3.14159265358979 for π for full double-precision accuracy. * * The LAPLACE keyword requires ngspice 36+ (XSpice Laplace block). Eea verr 0 LAPLACE { V(vref) - V(vsense) } + { 1000.0 * (1.0 + s/(2*3.14159265358979*30000)) + / ( s * (1.0 + s/(2*3.14159265358979*1e6)) ) } * ── Duty cycle: D = Verr clamped to [0.02, 0.95] ──────────────────────────── * In steady state D = Vout/Vin = 1.2/3.3 = 0.36. * Clamp prevents the model from demanding impossible duty cycles during transients. Eduty duty 0 VALUE { MAX(0.02, MIN(0.95, V(verr) / 5.0)) } * ── Average switch-node voltage ─────────────────────────────────────────────── * Vsw_avg = Vin × D (first-order average model of the half-bridge) * This is the key behavioural approximation: the switching waveform is replaced * by its average, which is valid for f << Fsw/10 = 150 kHz. Esw vsw 0 VALUE { V(vin) * V(duty) } * ── Output inductor: 1.5 µH, DCR ≈ 55 mΩ ─────────────────────────────────── * L2: Würth Elektronik 744043150. Matches hardware/bom.csv and PCB footprint. * Saturation current = 3 A; the .measure below checks iL_pk < 3 A. L2 vsw vil 1.5u RL2 vil vout 55m * ── Output capacitor bank ──────────────────────────────────────────────────── * * C9: 100 µF Murata GRM32ER61A107ME20L (X5R, 1210 package) * ESR ≈ 5 mΩ (measured at 100 kHz, Murata SimSurfing) * ESL ≈ 0.8 nH (estimated for 1210 via-mounted cap) * * Modelled as a series RLC branch to vout: * Cbulk in series with RCbulk (ESR) and LCbulk (ESL) * C_bulk vout vc_esr 100u RC_bulk vc_esr vc_esl 5m LC_bulk vc_esl 0 800p * C10: 4 × 100 nF 0402 X7R HF bypass caps placed close to FPGA balls. * Parallel combination: 400 nF total. * Parallel ESR: 50 mΩ / 4 = 12.5 mΩ per combined network. * These dominate the high-frequency (> 1 MHz) impedance of the rail. * C_hf vout vc_hf 400n RC_hf vc_hf 0 12.5m * ── PCB parasitic path from regulator to FPGA VCCCORE balls ───────────────── * Short 1.0 mm wide, 5 mm long power trace in 1 oz copper on 1.6 mm FR4. * R_trace ≈ 0.5 mΩ/sq × 5 sq = 2.5 mΩ (negligible but included for realism) * L_via ≈ 150 pH per via pair (standard 0.3 mm via in 1.6 mm FR4) * Lbrd vout vfpga 150p ; via inductance Rbrd vfpga vcore 2.5m ; copper trace resistance * vcore is the voltage at the FPGA VCCCORE pad — the node we care about. * ── Load model: ECP5-85F VCCCORE current ───────────────────────────────────── * * Idle current (reset, PLL stable, no logic toggling): Iidle ≈ 50 mA * Source: ECP5 datasheet §DC Characteristics, ICCCORE typical at fclk = 0. * * Active current (4×4 systolic array tile computation at 50 MHz): Iactive ≈ 250 mA * This is an educational bring-up estimate for the small 4×4 fabric. Real * dynamic current depends on switching activity, operand values, and PVT corner. * * Load step: * t = 0 → 50 µs : Iidle = 50 mA (post-reset settling) * t = 50 µs → 200 µs: Iactive = 250 mA (tile computation begins) * t = 200 µs → 300 µs: Iidle = 50 mA (tile done, array idle) * Rise/fall time : 1 µs (bench-load friendly edge for correlation) * * The 1 µs edge is intentionally slower than internal FPGA switching so that the * model can be compared against a practical active-load bring-up test. Idc vcore 0 DC 0.050 ; quiescent 50 mA always present Istep vcore 0 PWL ; piecewise-linear load step + 0n 0 + 50u 0 + 51u 0.200 ; +200 mA in 1 µs (0 → 50 µs idle, step at 50 µs) + 200u 0.200 + 201u 0 ; step off over 1 µs + 300u 0 * ── Measurements ───────────────────────────────────────────────────────────── * All measurements reference vcore (the FPGA-side node), not vout, * to include PCB parasitic drop. * Pre-step nominal voltage (average over a quiet 30 µs window) .measure tran vcore_nom AVG V(vcore) FROM=10u TO=40u * Worst-case undershoot after load step .measure tran vcore_min MIN V(vcore) FROM=50u TO=80u * Worst-case overshoot after load step removes .measure tran vcore_max MAX V(vcore) FROM=200u TO=230u * Steady-state regulated voltage under load .measure tran vcore_ss AVG V(vcore) FROM=150u TO=195u * Peak inductor current during transient .measure tran iL_pk MAX I(L2) FROM=50u TO=80u * Derived: undershoot and overshoot magnitudes .measure tran undershoot PARAM { vcore_nom - vcore_min } .measure tran overshoot PARAM { vcore_max - vcore_nom } * Settling time: first crossing back above 98 % of nominal after undershoot * (1.176 V = 1.2 V × 0.98 = lower edge of ±2 % window) .measure tran t_settle WHEN V(vcore) = 1.176 CROSS = 2 * ── Pass / fail criteria ───────────────────────────────────────────────────── * These thresholds map directly to Phase 1 of docs/bringup_plan.md. * * undershoot < 60 mV → vcore stays above 1.14 V (ECP5 Vmin for VCCCORE) * overshoot < 60 mV → vcore stays below 1.26 V * t_settle < 50 µs → loop bandwidth adequate for 50 MHz tile cadence * iL_pk < 3.0 A → within L2 saturation current rating * * If any criterion fails in simulation, increase C9 (output bulk cap) or * adjust compensator zero frequency (Eea above) before ordering PCBs. * ── Solver options ──────────────────────────────────────────────────────────── * GEAR integration is more stable for stiff circuits (large L/C ratio). * RELTOL = 1e-4 is tighter than the default (1e-3) to resolve < 1 mV features. .options RELTOL=1e-4 ABSTOL=1e-9 VNTOL=1e-6 .options METHOD=GEAR .options MAXSTEP=50n * ── Probed signals ──────────────────────────────────────────────────────────── .probe tran V(vcore) V(vout) V(vsw) I(L2) V(duty) * ── .control block ──────────────────────────────────────────────────────────── * The .control block is executed by ngspice's built-in interpreter after the * netlist is read. It: * 1. Runs the transient analysis defined above. * 2. Prints the .measure results to stdout for easy CI/CD parsing. * 3. Writes the full waveform database to power_core_load_step.raw so that * the results can be inspected in a waveform viewer without re-running. * * To use interactively: ngspice power_core_load_step.cir * To use in batch CI: ngspice -b power_core_load_step.cir * .control * Run the transient simulation defined in the netlist section above. run * ── Print measured values ─────────────────────────────────────────────── echo "" echo "=== TensorRail-Mini VCCCORE Load-Step Results ===" echo "" print vcore_nom print vcore_min print vcore_ss print undershoot print overshoot print t_settle print iL_pk echo "" * ── Pass/fail checks ──────────────────────────────────────────────────── * ngspice's 'if' supports basic comparisons on measured values. * Threshold values match docs/bringup_plan.md Phase 1 criteria. if undershoot > 0.060 echo "FAIL: undershoot exceeds 60 mV — rail dips below 1.14 V" else echo "PASS: undershoot < 60 mV" end if overshoot > 0.060 echo "FAIL: overshoot exceeds 60 mV — rail exceeds 1.26 V" else echo "PASS: overshoot < 60 mV" end if t_settle > 50e-6 echo "FAIL: settling time exceeds 50 µs" else echo "PASS: settling time < 50 µs" end if iL_pk > 3.0 echo "FAIL: peak inductor current exceeds 3.0 A rating of L2" else echo "PASS: peak inductor current < 3.0 A" end echo "" * ── Save waveforms ────────────────────────────────────────────────────── * writefile writes the currently active plot (the tran1 result of 'run') * to an ngspice binary RAW file. The path is relative to the working * directory from which ngspice is invoked. Run from repo root: * ngspice -b simulation/power_core_load_step.cir * produces simulation/power_core_load_step.raw. * * The RAW file can be opened with: * - ngnutmeg (ngspice companion viewer) * - KiCad 7+ built-in SPICE viewer (File → Open → *.raw) * - Python: pip install PySpice; from PySpice.Spice.RawFile import RawFile write simulation/power_core_load_step.raw V(vcore) V(vout) I(L2) V(duty) echo "Wrote: simulation/power_core_load_step.raw" echo "" .endc .end