Verified · reproducibleAnalog / mixed-signal

Mabrains Analog_blocks — sky130 bandgap / LDO / OTA

Open-source sky130 analog IP from an analog design-services firm, carried through to layout with DRC/LVS scripts and datasheets: Banba-style bandgap references (1.8 V, 5 V, self-biased), Miller and folded-cascode LDOs, and Miller / folded-cascode OTAs. References and LDOs are power-management IP — the class a company like TI ships by the thousand.

commit d67887asource

Method: ngspice-42 with the real sky130 tt device models. Two passes: a CIRCUIT pass (operating point, a startup-latch deletion test, loop-polarity tracing, loop-stability AC analysis, mirror/ratio/compensation checks) and a NETLIST-INTEGRITY pass that reads every committed netlist as text and checks what a simulator will not tell you — a subcircuit naming the same formal port twice, and an instance passing a different number of nodes than its subcircuit declares. The circuit pass found nothing to report and that result is kept below; the netlist pass is where these findings come from, each reproduced by changing exactly one line and re-running. For scope: the same structural check run over 229 committed netlists across nine open-source sky130 repositories surfaced the two defects below (across nine files) and no others — so this is a targeted result, not a scattershot one. Each finding below survived two independent adversarial refutations — one tracing the logic, one checking intent and documentation — before publication. Reproduce any of it against the commit above.

#1Design defect

A duplicated port name ties the supply to ground: the pre-layout bandgap testbench measures a dead circuit

Analog_Blocks/Bandgap/Netlists/Testbench/Bandgap1.8v_2_meas.spice:2, 106-109
x1 0 Vref Vdd Bandgap_2          ; line 2 — actuals are (GND, Vref, VDD)
...
.subckt Bandgap_2  VDD Vref VDD  ; line 106 — formal 1 and formal 3 are BOTH "VDD"
*.ipin VDD                       ; the schematic's own pin list says
*.ipin GND                       ;   the three pins are VDD, GND, Vref —
*.opin Vref                      ;   so formal 1 should be GND, not VDD

Why it is wrong

The subcircuit declares three formal ports and names two of them VDD. SPICE binds positionally, so the FIRST formal named VDD takes the first actual — node 0 — and the third actual (the real supply Vdd) has nowhere to land and is silently discarded. As this deck instantiates it, every device in the block has its supply rail tied to ground. The intent is not in doubt: the pin comments immediately below list the three pins as VDD, GND, Vref, and the post-layout sibling of this very deck (Bandgap1.8v_2_meas_post.spice) declares the same block correctly as `.subckt Bandgap VDD Vref GND`. The root cause is upstream of the netlist — in Schematics/BGR1.8v_2/Bandgap_2.sym, whose two supply pins are both named VDD while the symbol’s drawn label reads GND — so the netlister faithfully exported a symbol that was already wrong.

Reproduction scenario

Run the file exactly as committed. The block draws no current and produces no output: V(Vref) = 6.6e-18 V and I(Vsup) = 0.000 A — not "wrong by a few percent" but identically zero, because the supply node is ground. The deck’s own .meas statements report the failure rather than hiding it: the reference measures -2.2e-164 and both PSRR measurements fail outright. To be clear about scope: this is the pre-layout measurement deck, and the CIRCUIT is fine — the repository’s own plots show a working ~1.02 V reference. What is broken is the committed deck’s ability to reproduce them.

Confirmed by

ngspice-42 with the real sky130 tt models, reproduced by hand. As committed: V(Vref) = 6.6e-18 V, I(Vsup) = 0.000 A. Positive control — change only that one port name so the list reads `.subckt Bandgap_2 GND Vref VDD`, touching nothing else: V(Vref) = 1.0049 V drawing 126.6 µA (ngspice prints -1.26571e-04 A by source sign convention), with a -40…120 °C sweep spanning 0.9871–1.0067 V. So the circuit works and the port list is the whole defect. ngspice-42 reports it silently: across the entire run there is no warning, no error and no mention of a duplicate port. Note the subcircuit’s internal GND is NOT floating here — ngspice aliases gnd to node 0 — so the miswired supply is the sole fault, which is why a one-line port-list edit is a complete repair. Matching the post-layout deck’s convention instead (`.subckt Bandgap_2 VDD Vref GND` with the instance reordered) is the tidier fix.

Why it matters

Stated fairly: this repository’s run script (scripts/Bandgap_script.bash) has the line invoking this deck commented out and runs the post-layout deck instead, and the README says plainly "this is not the final design" — so nobody is shipping silicon off this file. It is published because the FAILURE MODE is the instructive one, and it is the mode that does ship: not a crash, but a clean run producing plausible numbers from a block that is switched off. A duplicated formal port survives schematic review, survives waveform inspection, and draws no complaint from the simulator — it is caught by something that reads the netlist as text, or not at all. That is the class of defect that quietly invalidates a sign-off deck at any company moving IP between flows.

#2Design risk

The 1.8 V bandgap’s OTA repeats a port name across eight files — the ground connection is silently dropped

Analog_Blocks/Bandgap/Netlists/Design/BGR_1.8v/Bandgap1.8v.spice:26, 33
x1 VDD Vref net3 net2 net5 GND Bandgap1.8v_OTA   ; line 26 — 6th actual is GND
.subckt Bandgap1.8v_OTA  Vdd Ibias Vn Vp Vhigh Vn ; line 33 — formals 3 and 6 are both "Vn"
*.ipin Gnd                                        ; a Gnd pin is declared, but no port carries it
XM3 net2 net2 Gnd Gnd  ...                        ; and the body's NMOS sit on that Gnd

Why it is wrong

Formal 3 and formal 6 are both named Vn, and the body’s NMOS devices (XM3, XM4, XM8) reference a node called Gnd that appears nowhere in the port list. The block’s own pin declarations do include a Gnd pin — they are listed in a different order than the ports, so they establish that a ground port was intended to exist, not which position it occupies. Either way the port list cannot be right: it names one node twice and omits a node the body uses. As written, Vn binds to the third actual (net3) and the sixth actual (GND) is discarded. The same malformed subcircuit is carried into eight committed netlists — the standalone 1.8 V bandgap and its testbench, a vendored copy inside each of the two 1.8 V LDOs, both LDO designs, and both LDO testbenches.

Reproduction scenario

Under ngspice the defect is MASKED: at the operating point tested the netlist produces results identical to the corrected one, because ngspice happens to apply TWO separate leniencies at once — it binds a repeated formal to the first occurrence and silently drops the extra connection, and it treats a node named `gnd` as an alias for node 0, so the body’s Gnd reaches real ground even though no port carries it there. The exposure is that correctness here rests on both of those tool-specific behaviours holding. We did not test other simulators and make no claim about what any particular one does; the honest statement is that a netlist naming one port twice and relying on an implicit ground alias is not portable, and its behaviour elsewhere has to be established rather than assumed.

Confirmed by

ngspice-42, runs differing by one line. As committed: Vref = 1.004027 V. With the sixth formal corrected to Gnd: Vref = 1.004027 V — identical to seven digits, which is the proof that ngspice is masking the defect rather than the netlist being right. Binding order was established separately on a minimal case: `.subckt dut a b a` driven by three sources shows the first formal taking the first actual while the third actual draws exactly 0.000 A, silently unconnected and unwarned. A sensitivity run — NOT a prediction about any named tool — shows what is at stake if the repeated formal resolved the other way: putting GND on the OTA input moves the reference to 0.848 V, a 16 % shift.

Why it matters

A latent defect, reported as one: it is not breaking the design in the simulator the project uses, and we say so plainly — the repository is a 2022 demo whose README states "this is not the final design". It is worth publishing because IP does not stay in one simulator, and a netlist whose correctness depends on two undocumented leniencies of one engine is a portability liability rather than a working file. The mistake is already replicated across eight committed netlists, which is how a one-character slip in a symbol becomes a fleet-wide assumption — and the fix belongs in Schematics/BGR1.8v/Bandgap1.8v_OTA.sym, not in the eight exports.

What held up

The same run, reported in full — a finding list is only trustworthy alongside what it cleared.

Worth stating plainly, because it is half of what we found: the CIRCUIT design holds up. We came looking for the classic reference defect — a self-biased bandgap with no startup circuit, able to latch in the zero-current state — and it is not present. The decisive test: force the bandgap into its dead state (all core nodes at 0, bias node at VDD) with the supply already up; with the XM1/XM2/XM3 startup trio in place the loop recovers to Vref = 1.04 V, and with the trio deleted it sticks at 0.47 V and never starts. So the startup circuit is present and provably necessary. The BJT ratio (1:8), the matched load resistors, the negative loop polarity in all four LDOs, the feedback-divider ratios against their stated targets, diff-pair/mirror symmetry, and the Miller / feed-forward compensation all check out. We also ran the project’s own loop-stability deck on the Miller 1.8 V LDO rather than trusting the schematic: 57.7 dB DC loop gain, 14.3 kHz unity-gain crossover, and 86° of phase margin — a comfortably compensated loop, where anything under about 45° would have been the finding. (Read that number carefully if you reproduce it: ngspice reports vp() in radians unless you set units=degrees, and the raw 1.507 it prints is 86°, not 1.5°.) The two findings above are not circuit-design mistakes — they are netlist-integrity mistakes in the files that ship that good design, which is exactly why they survived human review.

Get this on your design — privately
Same flow, your RTL or analog netlist, findings delivered to you rather than published.

A paid pilot hunt returns confirmed findings on a module you choose, each reproducible on your own code. Design-services firms resell the report; startups de-risk before tapeout. Pay for proof.

Start a pilot
Mabrains Analog_blocks — sky130 bandgap / LDO / OTA — analog verification report — ActGen