Two kinds of numbers get mixed up in LDO reviews. Givens come from the system and do not move with topology or silicon: supply range, load range, temperature range, lifetime, and the output capacitor with its ESR window. Design outcomes are what the review actually signs off: dropout, stability, transient, PSRR, accuracy, Iq and the rest. Every check below measures an outcome against a limit, swept across the givens.
| Placeholder | Meaning | Used in expressions as |
|---|---|---|
$DUT | LDO instance name in the testbench | IS("/$DUT/$DUT_OUTPUT") terminal currents |
$DUT_POWER | Input pin and net (VIN) | VS("/$DUT_POWER") |
$DUT_GND | Ground pin and net | Noise reference, IGND |
$DUT_OUTPUT | Output pin and net (VOUT) | VS, VT, VF, VN of "/$DUT_OUTPUT" |
VOUT_NOM, iload, vin | Design variables | Sweep variables and spec normalization |
One corner set is defined once per setup and enabled for every test, so a failure in one check can be traced to the same corner in the others. It has five corners, one per process, and each corner carries three supply and three temperature values, so a full run is 5 × 3 × 3 = 45 points plus nominal.
| Dimension | Values | How it is set |
|---|---|---|
| Process (P) | TT, SS, FF, SF, FS | Model sections swapped per corner. Core and 3.3 V MOSFETs take tt/ss/ff/sf/fs. Resistors and MIM caps follow SS and FF, and stay typical at SF and FS. Every other section stays nominal. |
| Voltage (V) | VIN typ −10%, typ, +10% | Corner variable vin on the $DUT_POWER source |
| Temperature (T) | −40, 25, 125 °C | Corner variable temperature |
A corner cannot usefully set a variable that the test sweeps: the sweep overrides it, and the three values become identical runs. Dropout and line regulation sweep VIN, and line transient drives VIN with its own stimulus, so they take a P T set. Thermal shutdown sweeps temperature, so it takes a P V set. Offset Monte Carlo runs at the worst PVT point found by the other checks. The PVT corners column in the full table lists the set for each check.
ldo_pvt_corners.il (section 5) adds the set to an ADE XL or ADE Assembler setup and enables it for every test. For the P T or P V checks, call ldoPvtCorners(sdb models ?vary '(P T) ?tests '("LDO_Dropout") ?suffix "_PT") and leave those tests out of the P V T set.
Every check runs against the shared PVT corner set. The PVT corners column says which of process (P), voltage (V) and temperature (T) apply; a check that sweeps VIN or temperature itself drops that dimension. Sim runs is the rough count for one pass, 45 for the full P V T set. The bar is on a log scale so the Monte Carlo row does not flatten the rest. Click a check to jump to its detail.
| # | Check | Tier | Analysis | Example spec | PVT corners | Sim runs |
|---|
Each check shows the testbench with the driven nets in orange and the measured nets in blue, the characterization charts with the red spec limit, a worst-case table across corners, and the ADE XL output expressions. Curves are illustrative models shaped like a 1.2 V, 300 mA LDO; replace them with your CSV data. For readability the charts show five representative points of the shared PVT set: TT 25 °C, SS and FF at −40 and 125 °C.
DC sweep of the load current from 1 mA to 300 mA at VIN = 1.8 V, across the shared PVT set (5 processes × VIN 1.62, 1.8, 1.98 V × −40, 25, 125 °C = 45 points). The chart and tables show five representative corners. Two limits apply: the static slope (load regulation, mV/A) and the total VOUT error over the whole load range. The numbers below are illustrative, shaped like a real 1.2 V, 300 mA LDO, so the page is ready to take your CSV.
Slope is load regulation; vertical offset is the DC error from offset and divider mismatch. Hot corners sit lowest and slope most because loop gain drops as the pass device heats up.
| Rank | Corner | VOUT at 1 mA | VOUT at 300 mA | Load reg (spec ≤ 20 mV/A) | VOUT error (spec ≤ 1.5%) | Result |
|---|
| Statistic | Load reg (mV/A) | VOUT error (%) |
|---|---|---|
| Typical (TT 25°C) | 12.0 | 0.30 |
| Mean of corners | 12.2 | 0.68 |
| Max | 16.0 | 1.10 |
| Std dev of corners | 2.8 | 0.33 |
| Worst margin to spec | 4.0 (20%) | 0.40 (27%) |
| Output | Expression | Spec |
|---|---|---|
VOUT | VS("/$DUT_OUTPUT") | plot only |
load_reg_mV_per_A | 1000*(value(VS("/$DUT_OUTPUT") 1m) - value(VS("/$DUT_OUTPUT") 300m))/(300m - 1m) | < 20 |
VOUT_err_pct | 100*ymax(abs(VS("/$DUT_OUTPUT") - VAR("VOUT_NOM")))/VAR("VOUT_NOM") | < 1.5 |
headroom_V | value(VS("/$DUT_POWER") - VS("/$DUT_OUTPUT") 300m) | info |
ldo_loadreg_setup.il and ldo_pvt_corners.il in the same directory.ldo_loadreg_setup.il: library, testbench cell, setup view, the four net names, and the operating point. VIN_VAL is also the typical VIN for the ±10% corners.load("ldo_loadreg_setup.il"). With PVT t it also adds the shared corner set.LDO_LoadReg runs nominal plus the 45 PVT points, with pass/fail against the two specs.ldo_pvt_corners.il and load() it on its own. It enables the corners for every test in the setup.ldo_loadreg_setup.il uses the ADE Assembler mae* API, which cannot open a classic ADE XL view (data.sdb, ASSEMBLER-8036). Build the test in an Assembler (maestro) view, or convert it with maestro-to-adexl. ldo_pvt_corners.il uses the axl* corner API and works on both view types. The script prints the DC analysis it ended up with. Check that it shows sweep "Design Variable" and incrType "Linear", because without them ADE netlists a single operating point.;; ====================================================================
;; ldo_loadreg_setup.il
;; Adds an LDO load-regulation test to an existing ADE XL / ADE Assembler
;; (maestro) setup whose testbench schematic already has the LDO hooked up.
;;
;; How to use
;; 1. Edit the values in sections 1 to 3 below.
;; 2. Close the setup view in the GUI (or open it read-only) so this
;; script can open it for editing.
;; 3. In the CIW: load("ldo_loadreg_setup.il")
;;
;; Testbench assumptions
;; - A DC current sink from DUT_OUTPUT to ground, value = ILOAD_VAR
;; - A DC voltage source on DUT_POWER, value = VIN_VAR
;; - Output cap and ESR already on the schematic
;;
;; Needs the mae* SKILL API: IC6.1.8 with ADE Assembler, or IC23 / IC25.
;; mae* cannot open a legacy ADE XL (data.sdb) view; build the test in an
;; Assembler (maestro) view. ldo_pvt_corners.il works on both.
;; Not tested on your install; check the printed DC analysis settings.
;; ====================================================================
ldoLoadregDir = let((f) f = get_filename(piport)
if(f && rindex(f "/") substring(f 1 strlen(f) - strlen(rindex(f "/"))) "."))
let((
;; ---------- 1. Where the setup lives ----------
(LIB "myLib") ; library holding the adexl / maestro view
(CELL "tb_ldo") ; testbench cell
(VIEW "adexl") ; "adexl" or "maestro"
(TB_VIEW "schematic") ; testbench schematic view
(TEST "LDO_LoadReg") ; name of the test this script adds
;; ---------- 2. Net and instance names ----------
(DUT "I0") ; $DUT LDO instance in the testbench
(DUT_POWER "VIN") ; $DUT_POWER net on the LDO input pin
(DUT_GND "GND") ; $DUT_GND net on the LDO ground pin
(DUT_OUTPUT "VOUT") ; $DUT_OUTPUT net on the LDO output pin
(ILOAD_VAR "iload") ; design variable on the DC load sink
(VIN_VAR "vin") ; design variable on the VIN source
;; ---------- 3. Operating point and spec limits ----------
(VIN_VAL "1.8")
(VOUT_NOM "1.2")
(ILOAD_MIN "1m")
(ILOAD_MAX "300m")
(NPTS "31") ; number of steps; the sweep has NPTS+1 points
(SPEC_LOADREG "20") ; load regulation, mV/A, max
(SPEC_ERR "1.5") ; VOUT error over load, %, max
(PVT t) ; t = add the shared PVT corners (ldo_pvt_corners.il)
(TEMPS "-40 25 125") ; corner temperatures; VIN corners are VIN_VAL -10% / +10%
(RUN t) ; t = set up and run, nil = set up only
sess vs vp dcOpts outs hist ok pvtFile)
;; ---------- open the setup ----------
sess = maeOpenSetup(LIB CELL VIEW ?mode "a")
unless(sess
error("ldo_loadreg: cannot open %s/%s/%s for editing. Close it in the GUI and retry. A legacy ADE XL view (data.sdb) cannot be opened by mae* (ASSEMBLER-8036); use an ADE Assembler (maestro) view.\n"
LIB CELL VIEW))
when(member(TEST maeGetSetup(?session sess))
error("ldo_loadreg: test %s already exists. Change TEST or delete that test first.\n" TEST))
;; ---------- test and design variables ----------
maeCreateTest(TEST ?lib LIB ?cell CELL ?view TB_VIEW ?simulator "spectre" ?session sess)
maeSetVar(VIN_VAR VIN_VAL ?session sess)
maeSetVar("VOUT_NOM" VOUT_NOM ?session sess)
maeSetVar(ILOAD_VAR ILOAD_MIN ?session sess)
;; ---------- DC sweep of the load current ----------
;; "sweep"/"designVar" make it a design-variable sweep, and "incrType" plus
;; "stepTypeLin" make "lin" a step count. Without them ADE stores the keys but
;; netlists a bare op point (no param=/start=/stop= on the dc line).
dcOpts = `(("saveOppoint" t) ("sweep" "Design Variable")
("designVar" ,ILOAD_VAR) ("param" ,ILOAD_VAR)
("rangeType" "Start-Stop") ("start" ,ILOAD_MIN) ("stop" ,ILOAD_MAX)
("incrType" "Linear") ("stepTypeLin" "Number of Steps") ("lin" ,NPTS))
;; argument order changed between releases, so try both forms
ok = errset(maeSetAnalysis(TEST "dc" ?enable t ?options dcOpts ?session sess))
unless(ok && car(ok)
ok = errset(maeSetAnalysis("dc" ?enable t ?options dcOpts ?session sess)))
unless(ok && car(ok)
printf("ldo_loadreg: could not set the DC sweep. Set it by hand: sweep %s from %s to %s.\n"
ILOAD_VAR ILOAD_MIN ILOAD_MAX))
printf("ldo_loadreg: DC analysis now reads %L\n"
car(errset(maeGetAnalysis(TEST "dc" ?session sess))))
;; ---------- outputs and specs ----------
vs = sprintf(nil "VS(\"/%s\")" DUT_OUTPUT)
vp = sprintf(nil "VS(\"/%s\")" DUT_POWER)
outs = list(
list("VOUT" vs nil nil)
list("VOUT_light" sprintf(nil "value(%s %s)" vs ILOAD_MIN) nil nil)
list("VOUT_heavy" sprintf(nil "value(%s %s)" vs ILOAD_MAX) nil nil)
list("load_reg_mV_per_A" sprintf(nil "1000*(value(%s %s) - value(%s %s))/(%s - %s)"
vs ILOAD_MIN vs ILOAD_MAX ILOAD_MAX ILOAD_MIN)
"lt" SPEC_LOADREG)
list("VOUT_err_pct" sprintf(nil "100*ymax(abs(%s - VAR(\"VOUT_NOM\")))/VAR(\"VOUT_NOM\")" vs)
"lt" SPEC_ERR)
list("headroom_V" sprintf(nil "value(%s - %s %s)" vp vs ILOAD_MAX) nil nil))
foreach(o outs
maeAddOutput(nth(0 o) TEST ?expr nth(1 o) ?session sess)
when(nth(2 o)
maeSetSpec(nth(0 o) TEST ?lt nth(3 o) ?session sess)))
;; ---------- shared PVT corners, enabled for every test ----------
when(PVT
pvtFile = strcat(ldoLoadregDir "/ldo_pvt_corners.il")
unless(isFile(pvtFile)
error("ldo_loadreg: PVT is t but %s is missing. Put ldo_pvt_corners.il beside this file.\n" pvtFile))
ldoPvtLibOnly = t
load(pvtFile)
ldoPvtLibOnly = nil
ldoPvtCorners(axlGetMainSetupDB(sess)
maeGetEnvOption(TEST ?option "modelFiles" ?session sess)
?vinVar VIN_VAR ?vinTyp evalstring(VIN_VAL) ?temps TEMPS))
;; ---------- save, then optionally run ----------
maeSaveSetup(?lib LIB ?cell CELL ?view VIEW ?session sess)
printf("ldo_loadreg: test %s saved in %s/%s/%s (DUT %s, GND net %s).\n"
TEST LIB CELL VIEW DUT DUT_GND)
when(RUN
hist = maeRunSimulation(?session sess)
maeWaitUntilDone('All)
printf("ldo_loadreg: run finished, history %L. Open the view to see pass/fail per corner.\n" hist)
errset(maeExportOutputView(?session sess
?fileName strcat("./" TEST "_results.csv")
?view "Detail")))
sess)
;; ====================================================================
;; ldo_pvt_corners.il
;; Adds the LDO LDR PVT corner set to an ADE XL (adexl) or ADE Assembler
;; (maestro) setup and enables it for every test in that setup, so all
;; 15 checks run against the same corners.
;;
;; Process TT SS FF SF FS (model sections swapped per corner)
;; Voltage VIN_TYP x (0.9 1 1.1)
;; Temp -40 25 125 C
;; = 5 corners, 5 x 3 x 3 = 45 points
;;
;; How to use
;; 1. Edit section 1 below.
;; 2. Close the setup view in the GUI so this script can open it.
;; 3. In the CIW: load("ldo_pvt_corners.il")
;;
;; ldo_loadreg_setup.il calls ldoPvtCorners() itself when PVT is t.
;; Checks that sweep VIN (#2 Dropout, line_reg in #8) or temperature
;; (#13 Thermal shutdown) cannot also take that corner variable: pass
;; ?vary '(P T) or '(P V) and ?tests for those, with a ?suffix so the
;; names do not clash. See the table on the LDR page.
;; ====================================================================
;; ---------- process corners: name, then section swaps ----------
;; Each swap is (nominal section corner section). Sections not listed
;; stay nominal. This model file has no ss/ff-only resistor or MIM split
;; for SF/FS, so those keep tt_res / tt_mim.
ldoPvtProcess = '(
("TT" ("tt" "tt") ("tt_3v" "tt_3v"))
("SS" ("tt" "ss") ("tt_3v" "ss_3v") ("tt_res" "ss_res") ("tt_mim" "ss_mim"))
("FF" ("tt" "ff") ("tt_3v" "ff_3v") ("tt_res" "ff_res") ("tt_mim" "ff_mim"))
("SF" ("tt" "sf") ("tt_3v" "sf_3v"))
("FS" ("tt" "fs") ("tt_3v" "fs_3v")))
;; sdb setup DB handle: axlGetMainSetupDB(session)
;; models the test's nominal model list, (("file" "section") ...)
;; tests tests to enable the corners for; nil = every test in sdb
;; vary which of P V T the corners change
procedure(ldoPvtCorners(sdb models @key (vinVar "vin") (vinTyp 1.8) (vinTol 0.1)
(temps "-40 25 125") (vary '(P V T)) (tests nil) (suffix ""))
let((allTests names c m mf sec swap vinList i)
allTests = cadr(axlGetTests(sdb))
unless(tests tests = allTests)
vinList = sprintf(nil "%g %g %g" vinTyp*(1-vinTol) vinTyp vinTyp*(1+vinTol))
foreach(p ldoPvtProcess
c = axlPutCorner(sdb strcat(car(p) suffix))
axlSetEnabled(c t)
names = cons(strcat(car(p) suffix) names)
when(memq('T vary) axlPutVar(c "temperature" temps))
when(memq('V vary) axlPutVar(c vinVar vinList))
i = 0
foreach(fs models
sec = cadr(fs)
when(memq('P vary) && (swap = assoc(sec cdr(p))) sec = cadr(swap))
mf = car(last(parseString(car(fs) "/")))
m = axlPutModel(c if(i == 0 mf sprintf(nil "%s:%d" mf i)))
axlSetModelFile(m car(fs))
axlSetModelSection(m sprintf(nil "\"%s\"" sec))
axlSetModelTest(m "All")
axlSetModelBlock(m "Global")
axlSetEnabled(m t)
i++)
foreach(tn allTests axlSetCornerTestEnabled(c tn if(member(tn tests) t nil))))
printf("ldoPvtCorners: %L, vin %s, temp %s, vary %L, tests %L\n"
reverse(names) vinList temps vary tests)
reverse(names)))
;; Legacy ADE XL keeps each test's model list in
;; <view>/test_states/<lib>/<cell>/<sim>/<test>_active/modelSetup
procedure(ldoPvtSubdirs(d)
setof(x getDirFiles(d) !member(x '("." "..")) && isDir(strcat(d "/" x))))
procedure(ldoPvtStateModels(viewDir test)
let((root f p models)
root = strcat(viewDir "/test_states")
foreach(l ldoPvtSubdirs(root)
foreach(c ldoPvtSubdirs(strcat(root "/" l))
foreach(sim ldoPvtSubdirs(strcat(root "/" l "/" c))
f = strcat(root "/" l "/" c "/" sim "/" test "_active/modelSetup")
when(!models && isFile(f)
p = infile(f) models = car(lineread(p)) close(p)))))
models))
;; ---------- standalone run ----------
;; Skipped when ldo_loadreg_setup.il loads this file for the procedure.
unless(boundp('ldoPvtLibOnly) && ldoPvtLibOnly
let((
;; ---------- 1. Where the setup lives and the operating point ----------
(LIB "myLib")
(CELL "tb_ldo")
(VIEW "adexl") ; "adexl" or "maestro"
(VIN_VAR "vin") ; design variable on the VIN source
(VIN_TYP 1.8) ; typical VIN; corners use -10% / typ / +10%
(TEMPS "-40 25 125")
sess sdb tests models axl)
axl = (VIEW == "adexl")
if(axl then
;; legacy ADE XL: maeOpenSetup refuses data.sdb (ASSEMBLER-8036)
sess = axlCreateSession(sprintf(nil "ldoPvt%d" random(100000)))
sdb = car(errset(axlSetMainSetupDBLCV(sess LIB CELL VIEW)))
else
sess = maeOpenSetup(LIB CELL VIEW ?mode "a")
sdb = sess && axlGetMainSetupDB(sess))
unless(sdb
error("ldoPvtCorners: cannot open %s/%s/%s for editing. Close it in the GUI and retry.\n"
LIB CELL VIEW))
tests = cadr(axlGetTests(sdb))
unless(tests error("ldoPvtCorners: %s/%s/%s has no tests.\n" LIB CELL VIEW))
;; nominal models come from the first test; every test in one setup
;; normally shares the PDK model list
models = if(axl
ldoPvtStateModels(ddGetObj(LIB CELL VIEW)~>readPath car(tests))
maeGetEnvOption(car(tests) ?option "modelFiles" ?session sess))
unless(models error("ldoPvtCorners: test %s has no model files.\n" car(tests)))
ldoPvtCorners(sdb models ?vinVar VIN_VAR ?vinTyp VIN_TYP ?temps TEMPS)
if(axl then
axlCommitSetupDB(sdb)
axlCloseSetupDB(sdb)
axlCloseSession(sess)
else
maeSaveSetup(?session sess)
maeCloseSession(?session sess))
printf("ldoPvtCorners: saved %s/%s/%s. Reopen it and check the Corners setup.\n" LIB CELL VIEW)
t))
Chart data on this page is illustrative. Figures and text from the linked documents remain the property of their owners.