PTM 180nm PMOS-pass LDO regulator simulation, sizing, and analysis skill. Use this skill whenever the user wants to: (1) simulate or re-run LDO DC/AC/noise/transient analyses in ngspice, (2) plot loop gain, PSRR, output impedance, load-step response, noise PSD, or transistor operating points, (3) compute or verify initial transistor sizing from specs (Vin, Vout, Iload, Cload, Vref), (4) iteratively adjust device sizes to meet specs (output accuracy ±1%, phase margin, PSRR, load/line regulation, noise, offset), (5) apply theoretical formulas for GBW, zero/pole frequencies, PSRR, load regulation, noise, or offset, (6) perform trade-off analysis between competing specs, (7) learn LDO topology, compensation theory, or sizing methodology. Technology: PTM 180nm BSIM3v3 (NMOS/PMOS, Lmin=180nm, Wmin=220nm, VDD up to 3.3V). Requires: ngspice on PATH, Python 3 + numpy/matplotlib/scipy.
Technology: PTM 180nm, BSIM3v3 Level 8 (NMOS / PMOS), Lmin = 180 nm, Wmin = 220 nm.
Dependencies: ngspice on PATH, Python 3 + numpy, matplotlib, scipy.
LDO/
├── SKILL.md
├── scripts/ ← run from here (cd LDO/scripts)
│ ├── ngspice_common.py — paths, ngspice runner, parser, template renderer
│ ├── ldo_common.py — circuit params, DUT rendering, cap/resistance helpers
│ │
│ ├── simulate_ldo_dc.py — backend: line & load regulation
│ ├── simulate_ldo_ac.py — backend: loop gain, PSRR, Zout
│ ├── simulate_ldo_noise.py — backend: output noise PSD, 1/f corner, Vn_rms
│ ├── simulate_ldo_tran.py — backend: load-step transient, V_drop, t_rec
│ ├── simulate_ldo_op.py — backend: transistor op-points (gm/ID methodology)
│ │
│ ├── plot_ldo_dc.py — plot: line & load regulation curves
│ ├── plot_ldo_ac.py — plot: PSRR, Zout, loop gain (Bode)
│ ├── plot_ldo_noise.py — plot: noise PSD with 1/f corner annotation
│ ├── plot_ldo_tran.py — plot: VOUT load-step waveform
│ ├── plot_ldo_op.py — plot: gm/ID, gm·ro, fT bar charts
│ │
│ ├── run_ldo.py ← MAIN entry (DC+AC+noise+tran in parallel, then OP)
│ ├── run_ldo_dc.py — standalone: DC only
│ ├── run_ldo_ac.py — standalone: AC only
│ ├── run_ldo_noise.py — standalone: noise only
│ ├── run_ldo_tran.py — standalone: transient only
│ │
│ ├── run_rcomp_sweep.py — sweep R_COMP → PM, GBW
│ ├── run_ccomp_sweep.py — sweep C_COMP → PM, GBW
│ ├── run_cout_sweep.py — sweep C_OUT → PM, GBW
│ ├── run_error_amp_design.py — gm/ID-based error-amp sizing script
│ └── run_auto_design.py — automated design iteration loop
│
├── assets/
│ ├── netlist/ ← SPICE templates (*.cir.tmpl)
│ └── models/ptm180.lib ← PTM 180nm BSIM3v3 (NMOS / PMOS)
│
└── references/ ← read for theory / design guidance
├── 01_topology.md
├── 02_stability.md
├── 03_psrr.md
├── 04_noise.md
└── model_params.md
All generated files (logs, plots, netlists) go to WORK/ at the repo root.
Override with env-var ANALOG_WORK_DIR.
| User asks about | Action |
|---|---|
| Full simulation (all specs) | run_ldo.py |
| DC only (line/load regulation) | run_ldo_dc.py |
| AC only (loop gain, PSRR, Zout) | run_ldo_ac.py |
| Noise only | run_ldo_noise.py |
| Transient load step only | run_ldo_tran.py |
| Transistor op-points | simulate_ldo_op.simulate_op() |
| Phase margin vs R_COMP | run_rcomp_sweep.py |
| Phase margin vs C_COMP | run_ccomp_sweep.py |
| Phase margin vs C_OUT | run_cout_sweep.py |
| gm/ID-based error-amp sizing | run_error_amp_design.py |
| Initial sizing from specs | Apply Initial Sizing section below, then edit ldo_common.py |
| Topology / compensation theory | Read references/01_topology.md, 02_stability.md |
| PSRR theory | Read references/03_psrr.md |
| Noise theory | Read references/04_noise.md |
| Model parameters | Read references/model_params.md |
cd LDO/scripts
python run_ldo.py # All analyses in parallel (~10 s)
Key outputs in WORK/:
| File | Contents |
|---|---|
plots/ldo_dc.png | Line & load regulation curves |
plots/ldo_ac.png | Loop gain (Bode), PSRR, Zout |
plots/ldo_noise.png | Output noise PSD, 1/f corner |
plots/ldo_tran.png | Load-step VOUT waveform |
plots/ldo_op.png | gm/ID, gm·ro, fT bar charts |
logs/ldo_report.txt | Consolidated numeric metrics |
PMOS-pass LDO with Miller-compensated two-stage error amplifier.
VIN ──► M2 (pass, PMOS) ──────────────────────────► VOUT
▲ gate=net3 │
│ R0 (R_FB_TOP)
┌────┴───────────────────────────┐ │
│ Error Amplifier │ net30 (V_fb)
│ │ │
│ M3/M4 (PMOS current mirror) │ R1 (R_FB_BOT)
│ ▲ ▲ │ │
│ M0(VREF) M1(V_fb) ←──── ┘ GND
│ NMOS diff pair (non-inv / inv)
│ │
│ M5 (tail mirror, 10×) ─── M6 (diode ref, 1×) ←── I_bias
└────────────────────────────────────────────────────────────
Compensation: R2 (R_COMP) + C0 (C_COMP) from net3 → VOUT
→ introduces left-half-plane zero: fz = 1/(2π·R2·C0)
Bypass: C1 (large, ≈1F) on ibias node; C2 (C_OUT) on VOUT
Topology constraints:
Editable globals: all in LDO/scripts/ldo_common.py.
Given user inputs: Vref, Vin, Vout, Iload, Cload (default 1 µF).
From regulation condition Vout = Vref × (R0 + R1) / R1:
R0 / R1 = Vout/Vref − 1
Select I_div = 1% × Iload (current through divider)
→ R0 + R1 = Vout / I_div = Vout × 100 / Iload
→ R1 = Vref / I_div
→ R0 = (Vout − Vref) / I_div
M2 must supply full Iload. Start with minimum L for low Vdrop:
| Parameter | Value |
|---|---|
| L | Lmin = 180 nm |
| W per finger | 10 × Wmin = 2.2 µm |
| fingers | 10 |
| multiplier | 100 |
Total W = 10 × 10 × 100 × Wmin = 22 mm. DC iteration rule (see Step 7): adjust multiplier to achieve Vout accuracy ±1%.
Ibias = Iload / 1000 (e.g. 100 µA for Iload = 100 mA)
I_tail = Ibias × N_mirror (default N = 10, so I_tail = Iload/100)
| Parameter | M6 (ref, 1×) | M5 (mirror, 10×) |
|---|---|---|
| L | 5 × Lmin = 900 nm | same as M6 |
| W per finger | 10 × Wmin = 2.2 µm | same as M6 |
| fingers | 8 | 8 |
| multiplier | 1 | 10 |
Large L reduces current mismatch (ΔI/I ∝ 1/L for channel-length modulation). Bypass capacitor on ibias node: C1 = 1 F (large ideal cap, suppresses AC ripple).
Long L for high intrinsic gain (gm·ro) and good matching:
| Parameter | Value |
|---|---|
| L | 2 × Lmin = 360 nm |
| W per finger | Wmin = 0.22 µm |
| fingers | 8 |
| multiplier | 10 |
Very long L for high ro (high Av1) and good matching:
| Parameter | Value |
|---|---|
| L | 4 × Lmin = 720 nm |
| W per finger | 2 × Wmin = 0.44 µm |
| fingers | 8 |
| multiplier | 10 |
Cc = Cload / 5000 (initial value; adjust for stability)
Rz: choose so that ωz ≈ GBW (see Stability section below)
Set C_COMP = Cc, R_COMP = Rz in ldo_common.py.
Run python run_ldo_dc.py. Requirement: |Vout_sim − Vout_target| / Vout_target ≤ 1%.
| Condition | Action |
|---|---|
| Vout_sim < 0.99 × Vout_target | Increase M2 fingers (more current drive) |
| Vout_sim > 1.01 × Vout_target | Decrease M2 fingers (less current drive) |
β = R1 / (R0 + R1) = Vref / Vout (feedback factor)
Av1 = gm1 × (ro1 ∥ ro3) (error-amp DC gain)
gm = √(2 · µ · Cox · W/L · Id) → adjust mainly via W; L if needed
ro ∝ 1 / (λ · Id) → adjust via L (longer L → larger ro)
Loop Gain (DC):
T0 = β × Av1 × gm2 × ro2
GBW (Gain-Bandwidth Product):
GBW ≈ β × gm1 / Cc
Load Regulation (output impedance at low freq):
Zout(s) ≈ (1 + s·Cc·(ro1∥ro3)) / (β · Av1 · gm2)
PSRR (supply rejection):
PSRR(s) ≈ [gm2·(ro1∥ro3)·s·Cc + 1/ro2] / (β · Av1 · gm2)
| Frequency | Expression | Tuning |
|---|---|---|
| ωp1 (dominant) | ≈ 1 / (T0 · ro2 · CL) | set by CL and loop gain |
| ωp2 (output) | ≈ gm2 / CL | ↑gm2 or ↓CL to push out |
| ωz (compensation zero) | ≈ −1 / [(Rz − 1/gm2) · Cc] | set Rz to place ωz ≈ GBW |
| ωp3 (parasitic) | ≈ −1 / (Rz · CS) | target ωp3 ≥ 2 × GBW |
where CS = (Cn·Cc·CL) / (Cn·Cc + Cn·CL + Cc·CL), Cn = gate cap of M2.
Stability target: ωz ≈ GBW and ωp3 ≥ 2 × GBW.
Thermal noise:
Vn²_out = (1/β²) × [4kT(R0∥R1) + 8kTγ·(gm1 + gm4) / gm1²]
1/f (flicker) noise (dominant at low frequency):
Vn²_out = (2K / β²·Cox·f) × [1/(W1·L1) + (1/(W4·L4))·(gm4/gm1)²]
≈ (2K / β²·Cox·f) × [1/(W1·L1) + µp·L1/(µn·W1·L4²)]
Low-frequency: 1/f noise dominates. High-frequency: thermal noise dominates; compare relative magnitudes.
Primary knob: M2 multiplier (fingers).
| Vout too low | Increase M2 multiplier |
|---|---|
| Vout too high | Decrease M2 multiplier |
Re-run run_ldo_dc.py after each change.
Zout(s) ≈ (1 + s·Cc·(ro1∥ro3)) / (β · Av1 · gm2)
| Frequency | Primary actions | Secondary |
|---|---|---|
| Low freq | ↑Av1 (↑W1 or ↓L1); ↑gm2 (↑W2 or ↓L2) | — |
| High freq | ↑gm1 (↑W1); ↑gm2 (↑W2 or ↓L2); ↓Cc | — |
| Frequency | Primary actions | Secondary |
|---|---|---|
| Low freq | ↑Av1 (↑W1 or ↓L1); ↑gm2·ro2 (↑W2 or ↓L2) | ↑gm3 (↑W3); ↑ro5 (↓L5/L6) |
| High freq | ↑gm1 (↑W1); ↓Cc | — |
Target: PM ≥ 45° (≥ 60° recommended). Tune ωz and ωp3.
Reading the Bode plot (loop gain phase vs frequency):
| Action | Effect |
|---|---|
| Adjust Rz (primary) | Move ωz to ≈ GBW |
| ↓ Cgg_M2 = reduce W2 and L2 proportionally | Push ωp3 higher |
| ↓ W1 (↓gm1) | ↓GBW → more margin, at cost of regulation bandwidth |
| ↑ Cc (secondary) | ↓GBW → more margin; degrades PSRR and load regulation |
PSRR ≈ [gm2·(ro1∥ro3)·s·Cc + 1/ro2] / (β · Av1 · gm2)
| Action | Effect |
|---|---|
| ↑ Av1 (↑W1 or ↓L1) | ↑ DC PSRR |
| ↑ gm2 (↑W2) | ↑ DC PSRR |
| ↑ ro2 (↑L2) | ↑ DC PSRR |
| ↑ Cc | ↑ high-freq PSRR (but ↓ load regulation at high freq) |
| Frequency | Primary actions |
|---|---|
| Low freq (1/f) | ↑ W1 (best with L1 also ↑ proportionally); ↑ L4 |
| High freq (thermal) | ↑ gm1 (↑W1); ↓ gm4 (↓W4 or ↑L4) |
| Mismatch source | Mitigation |
|---|---|
| Diff pair M0/M1 | ↑ W1 (area ↑) |
| Current mirror M3/M4 | ↑ L4 (channel-length modulation mismatch ↓) |
| Feedback resistors R0/R1 | Increase resistor area (wider, longer poly) |
Primary rule: ↑W1 and ↑L4 simultaneously reduce both offset and noise.
Inspect simulation results: identify which spec has the least margin (closest to failing) and which has excess margin before adjusting.
| Adjustment | ✓ Improves | ✗ Degrades |
|---|---|---|
| ↑ W1 (diff pair wider) | Load reg (LF/HF), Line reg, PSRR, Noise, Offset | PM (GBW↑ → less phase) |
| ↓ L1 (diff pair shorter) | gm1↑, Load reg (LF), Line reg (LF) | gm·ro↓ (Av1↓), Noise (1/f↑), Offset |
| ↑ W2 (pass transistor wider) | Output accuracy, Load/Line reg, PSRR | ωp3↓ (Cgg2↑ → PM↓) |
| ↓ W2 & ↓ L2 (same ratio) | ωp3↑ (PM↑) | gm2↓ (regulation↓) |
| ↑ L4 (load mirror longer) | Av1↑, Noise (1/f↓), Offset, PSRR | Speed (ro4↑ but slower pole) |
| ↑ Cc | PM (GBW↓) | PSRR (↓), Load reg (HF↓) |
| ↓ Cc | PSRR, Load reg (HF) | PM↓ |
| ↑ Rz | ωz↑ → adjust PM | ωp3↓ (CS path) |
| ↓ Rz | ωp3↑ | ωz↓ → may lose PM correction |
Decision workflow:
run_ldo.py, read ldo_report.txt.| Variable | Device | Meaning |
|---|---|---|
W_M2_UM, L_M2_NM, M_M2 | M2 pass | W/finger [µm], L [nm], multiplier |
W_M0_UM, L_M0_NM, M_M0 | M0 diff (+) | same convention |
W_M1_UM, L_M1_NM, M_M1 | M1 diff (−) | keep = M0 |
W_M3_UM, L_M3_NM, M_M3 | M3 load (out) | keep = M4 |
W_M4_UM, L_M4_NM, M_M4 | M4 load (diode) | keep = M3 |
W_M5_UM, L_M5_NM, M_M5 | M5 tail mirror | keep L = L_M6 |
W_M6_UM, L_M6_NM, M_M6 | M6 bias ref | M6 mult = 1 always |
R_FB_TOP | R0 | top feedback resistor [Ω] |
R_FB_BOT | R1 | bottom feedback resistor [Ω] |
R_COMP | Rz | compensation zero resistor [Ω] |
C_COMP | Cc | compensation capacitor [F] |
C_OUT | CL | output decoupling capacitor [F] |
IBIAS_UA | I1 | bias current source [µA] |
WORK/ — never inside the skill package.Agg backend — never pop up figures; always plt.close(fig) after savefig().spath() helper).ThreadPoolExecutor for independent simulations.| File | Topic |
|---|---|
references/01_topology.md | Circuit topology, feedback loop, compensation |
references/02_stability.md | Pole-zero analysis, PM, Bode plot reading |
references/03_psrr.md | PSRR mechanism, frequency dependence |
references/04_noise.md | Thermal & 1/f noise models, optimization |
references/model_params.md | PTM 180nm BSIM3v3 parameters (vth0, Cox, u0 …) |