aioscilloscope / Notes / October 2026

ADC calibration: sixteen cores that must agree

The MHO900 reaches 4 GSa/s by interleaving two 12-bit ADCs of eight cores each. Any offset difference between the cores repeats every sixteen samples and shows up as spurs at multiples of fs/16. NovaOS equalises the cores at every start and keeps a self-calibration of its own; on our MHO934 the 2 GSa/s spurs measure 0.13 to 1.22 mVpk across restarts, against 2.3 to 8.1 mVpk on Rigol's firmware across boots. The factory calibration is read and never written.

CH1Where the spurs come from

Each of the two RT8847IQ ADCs has eight cores, and on one channel all sixteen take turns at 4 GSa/s. Every core has its own small offset and gain. A fixed offset per core is a pattern that repeats every sixteen samples, so its spectrum is a set of lines at k·fs/16. At 2 GSa/s a record keeps every second sample and the pattern lands at fs/8 and fs/4; at 500 MSa/s what is left is one line at fs/2, the difference between the two ADCs.

We measure it with every input open, CH1 at 1 V/div: three raw 10 kpt records, the mean of the samples at each of the sixteen phases, and the lines at k·fs/16 in millivolts peak. At 1 V/div one 12-bit code is 2.13 mV. One detail matters when comparing starts: a record does not always begin on the same core, so records come out circularly shifted against each other and must be aligned before their patterns are compared.

The FPGA has a correction block for this: sixteen lanes, each with a gain and two offsets, reloaded at every configuration above 500 MSa/s. Rigol's firmware fills it only from per-channel calibration files that its self-calibration writes when that item is chosen; the item is not part of its default calibration run. Our MHO934 came with none, so on Rigol's firmware the block runs at its neutral values, as traced on our unit.

CH2Every start moves the cores

At every power-on the ADCs run their own core trim, which calibrates the cores' gain and timing and also shifts each core's offset. On Rigol's firmware the 2 GSa/s lines measured 2.3 to 7.1 mVpk at fs/8 and 4.0 to 8.1 mVpk at fs/4 over four boots of the same unit. NovaOS runs the same start-up, and without anything further its lines sit inside that spread (4.7 to 6.1 and 4.9 to 7.0 mVpk over three starts). With the trim left out as a test, the pattern stayed put from start to start to within 0.39 to 0.65 mV rms, against 1.6 to 3.8 mV with it, which shows the trim is what moves it. The trim does useful work, so NovaOS keeps it and corrects after it.

The correction is a core equalisation at the end of every start. The FPGA has a measurement block that averages each ADC core's raw output. NovaOS reads all sixteen and trims each core's offset toward their common average, so a signal on the input shifts every core alike and leaves the result unchanged. Two things have to be right for it to work: the ADCs must be in the input mode of the channel being measured, because each input path has its own per-core offsets (up to about 40 codes apart in the factory tables), and the channel's offset DAC must first be set so the cores sit at mid-scale, which takes three or four measurements and about 0.1 s.

With no stored calibration, on our MHO934:

Line, 1 V/div, inputs openRigol's firmware, across bootsNovaOS, core equalisation, across starts
2 GSa/s, fs/44.0 to 8.1 mVpk0.65 to 1.7 mVpk
2 GSa/s, fs/82.3 to 7.1 mVpk3.6 to 4.6 mVpk
500 MSa/s, fs/20.9 to 5.3 mVpk0.23 to 1.02 mVpk

The fs/8 line stays because part of the pattern arises after the measurement block's tap. With the FPGA's second filter bypassed, a 4 GSa/s record shows per-core steps of up to ±5 codes while the measurement block reports all sixteen cores within a code. Those steps are the same at every start, which is what a stored calibration can remove.

Before making the equalisation the default we checked that it leaves real signals alone, with the built-in generator into CH2 at 2 and 4 GSa/s (sines at 10.37 and 23.7 MHz, a 1 MHz square). With and without it: amplitude 0.9684 and 0.9685 Vpp, THD −62.8 and −62.4 dB, rise time 9.73 ns in both. The static fs/8 line fell by 7 to 10 dB, to between 0.54 and 0.62 mVpk, and the images of the signal at k·fs/16 ± f stayed at −60 to −66 dBc.

CH3The self-calibration

NovaOS's self-calibration takes the same records Rigol's interleave calibration does: for each channel, the eleven 1 MΩ scales from 5 mV to 10 V/div and the eight 50 Ω scales from 5 mV to 1 V/div, each an 800,000-point record at 4 GSa/s with the input open, and from each the mean at every one of the sixteen phases. It adds checks of its own:

On our MHO934 a run takes 68 s for four channels. Fresh from a run, CH1's phase pattern at 4 GSa/s fell from 7.03 to 0.68 mV rms, and the 2 GSa/s lines from 4.73 and 5.34 to 0.36 and 0.36 mVpk. Across later restarts, with the core equalisation holding the pattern in place, the lines stayed at 0.46 to 1.22 mVpk at fs/8 and 0.13 to 0.92 mVpk at fs/4, and the open-input noise at 8.9 to 9.4 mV rms, against 10.5 to 11.8 mV on Rigol's firmware. NovaOS also loads the corrections at 500 MSa/s, where Rigol's firmware bypasses the block.

An MHO with no stored calibration offers to run one after 30 minutes of warm-up. It can also be started from the System sheet, the classic Utility menu, or over SCPI with :CALibration:STARt and :CALibration:STATus?.

CH4Whose calibration is whose

NovaOS reads every factory calibration file the unit carries and applies it: the vertical gain and offset tables, the offset DAC's step size, the ADC core parameters for each channel mode, the front end's zero and bandwidth trims, the logic probe thresholds and the generator's calibration. The calibration partition is mounted read-only, and nothing in NovaOS writes it. Rigol's firmware rewrites one of those files, the front end's bandwidth trims, at every start: two backups of our DHO924S taken a day apart differ in that file only in its checksum and save time.

NovaOS's own results live in its own state on the data partition, written atomically (a temporary file, a sync, a rename), and only when a user runs a calibration. At start the engine loads NovaOS's interleave calibration, else Rigol's file if the unit has one, else the neutral values, and logs which it took for each channel. The stored calibration is kept across NovaOS updates; we have checked that in emulation, and the check on a unit is still to do.

The DHO800/DHO900

The DHO has one RT8847IQ, eight cores at 1.25 GSa/s on one channel, and its FPGA has no interleave correction block like the MHO's. With the generator's 1 MHz sine on CH4 its fs/2 line measured −53 to −65 dBc across boots, and it moves when only the engine restarts, so it follows the engine's start-up. It also taught us how to test start-up changes. Comparing two images one after the other suggested the clock's load timing changed this line by 5 dB; twenty boots with four timings in rotation showed every timing across the same range. NovaOS on the DHO has its own self-calibration of each channel's zero and gain at 14 scales, inputs open, using the offset DAC stepped by two divisions as the gain reference, checked before it is kept and stored apart from the factory files. We have not run it on our bench unit yet, so there are no figures for it here.

Measured on our own MHO934 and DHO924S in October 2026. Every figure with its method is on NovaOS vs stock; the parts are on the MHO900 hardware page.