Everything on this page runs on your laptop.

A 12-bit converter with capacitor mismatch, an analog trim array on the top capacitors, and a calibration routine that finds its own trim codes. Then the digital block that corrects offset and gain, checked against the model that specifies it at every input code.

Ask for the files, read them, run them, try to break them.

It needs Python and an open-source Verilog simulator, Icarus Verilog or Verilator. No licensed tool is required to run anything on this page.

Get the example Book a scoping conversation
1.435 to 0.112
peak DNL in LSB on the default part, six capacitors trimmed. FAIL to PASS.
MEASURED
97 % and 0 %
yield with the polarity check in place and with it bypassed. Both runs complete without an error.
MEASURED
20,480 of 20,480
exhaustive comparisons of the digital block against its model, bit-exact on every one
MEASURED

Every figure on this page is reproduced by running the example's code, and the command that reproduces it is given beside it.

The part measures its own error

At 12 bits with a 3 % unit-capacitor sigma, the top capacitor's error is about 1.4 LSB rms. A part like that misses a half-LSB linearity spec badly, and no amount of digital cleverness recovers the missing charge. The array itself has to be trimmed.

The calibration needs no external reference and no tester. For a binary-weighted array the ideal condition at every bit is that the capacitor equals the sum of all the lower capacitors plus the terminator, and the amount by which that fails is the linearity step at that transition. So the converter measures its own error with its own comparator, and trims until the residue is nulled.

Result on the default part, with six capacitors trimmed: peak DNL 1.435 to 0.112 LSB. FAIL to PASS.

MEASURED python sar_adc_trim_demo.py --trim-caps 6. The script's own default trims three capacitors, which nulls the three trimmed transitions and leaves the peak at an untrimmed one; six is where the whole part passes.

The question a designer asks

The question is how deep the trim has to go, and what that costs in area.

That is a yield curve, and it takes one sweep.

capacitors trimmedmean peak DNLworstyield at 0.5 LSB
01.975.792 %
30.712.0830 %
50.371.0878 %
60.250.6597 %
70.170.49100 %

MEASURED 150 parts per point, 1,350 simulated parts, nine depths, a few seconds. python sar_adc_trim_demo.py --sweep 150

What matters about the table is that asking the question cost nothing. When a sweep is free, you run the sweep instead of picking a number and defending it later.

A routine that completes, reports sensible codes, and makes the part worse

Adding trim capacitance moves a threshold. Which direction depends on wiring and on the sign convention in the trim control logic. If those two disagree, calibration drives every code the wrong way, and it completes normally, reports plausible trim codes, and hands you a worse part than you started with.

So the routine does not assume the polarity. It applies a known trim step, measures which direction reduces the residue, and takes the answer from the measurement.

97 %

yield at 0.5 LSB DNL with the polarity check in place

mean peak DNL 0.24 LSB

0 %

yield with the check bypassed

mean peak DNL 4.79 LSB

MEASURED 200 Monte Carlo parts each, six capacitors trimmed. Both runs complete without an error. python sar_adc_trim_demo.py --trim-caps 6 --mc 200, then the same with --break-sign.

Run it yourself with --break-sign and watch calibration succeed at making the part worse.

Half the transfer function wrong, and no plot would show it

The offset and gain correction is fixed-point arithmetic in SystemVerilog. It is specified by a model, so the model is the oracle: every input code goes through both, and they must agree exactly.

  • 20,480 comparisons across five operating points, 4,096 codes each, the entire input space.
  • Bit-exact on every one.

Then the negative control. Switch the RTL to truncate where the model rounds:

  • 2,005 of 4,096 codes disagree. Every error is exactly one LSB.

Half the transfer function is wrong. The maximum error is a single count. Nothing about it would look wrong on any plot you would normally draw. This is the failure the practice exists to catch, and an exhaustive comparison that takes a second to run catches it.

MEASURED check_rtl.py against adc_offset_gain_corr.sv, exit code 1 on any mismatch.

MATLAB or Python, your choice

The golden model ships as both a Python function and a MATLAB function, verified arithmetically identical over the full input space.

If your team has MATLAB, MATLAB is the target: your scripts, your conventions, your plots. If you would rather the flow needed no seat, Python is the target. Mixed teams are normal and fine.

One honest note for teams weighing Octave as a free substitute: base scripting and plotting port with modest effort, but Simulink and Fixed-Point Designer have no Octave equivalent, so anything built on fi() objects or a .slx model does not travel. Better to know that now than in week two.

What is on this page, and what is not

Runs on your laptopRuns on your tools
Converter model, mismatch, trim, yield sweeps (Python)Verilog-A and Verilog-AMS views
Digital correction RTL (SystemVerilog)OCEAN campaign scripts
Exhaustive RTL simulation (Icarus Verilog, Verilator)SKILL generation
Real-number behavioural viewTransistor-level benches on your PDK
Plots and reports

The right-hand column is the half that is tool-bound by nature, and it is what the on-premises deployment exists to produce. For that half the deliverable is a generator plus a checker that validates the output without running the tool: every function verified against an API whitelist built from your installed release, argument counts checked against real usage. A script that fails the check never reaches your simulator.

Code that passes the check calls no hallucinated functions, which is the failure that bites in practice. It can still be wrong in other ways.

One archive, sent by email on request.

sar_adc_trim_demo.py, adc_offset_gain_corr.sv, tb_adc_corr.sv, check_rtl.py, adc_corr_golden.m, selftest_matlab_twin.py, run_all.sh

Request the archive by email

The engineer still decides.

Every artefact here is paired with something mechanical, an exhaustive comparison, a known-answer probe, an API whitelist, because generated code is assumed wrong until a check says otherwise.

Book a scoping conversation
PJ Crawley LLC. Corp-to-corp. On site for the kickoff and as often as the work needs, remote in between.
phil@pjcrawleyllc.com