aioscilloscope / Hardware / Inside the FPGA

Inside the Rigol DHO/MHO FPGA

Every sample these scopes show passes through one FPGA: it trains the ADC lanes, triggers, filters, records and streams the result to the processor. NovaOS drives Rigol's own FPGA image with an engine we wrote ourselves, so we see the FPGA from the host side: its registers, its frames and its own clock. This page is what that shows.

CH1The parts

DHO800 / DHO900MHO900, code 1MHO900, codes 2–7
FPGAXilinx Zynq XC7Z015Xilinx Kintex-7 XC7K160TFudan FMK230T, marked RIGOL RT6888IF
How we knowverified bitstream ID code 0x0373B093verified in the firmware, ID code 0x0364C093verified on our MHO934: header part FMK230T, ID code 0x02236143
Logic (datasheet)46,200 LUTs, 95 block RAMs, 160 DSP slices, a hard PCIe block101,400 LUTs, 325 block RAMs, 600 DSP slicesabout 145,000 LUTs inferred from its frame count
Stock image usesunknown not decodable with open tools yet92 % of LUTs, 59 % of flip-flops, 52 % of block RAM, 62 % of DSP decodedunknown no open database for this die
Loads fromits own QSPI flash: a factory golden image at power-on, then the update imagea file on the SD card, streamed in by the RK3399 (slave serial over SPI) at every boot; no FPGA flash
LinkPCIe Gen2 ×2PCIe Gen2 ×4
Sample clockTI LMX2582, 1.25 GHzTI LMK04826, 2 GHz per ADC
Internal state machine clock6.4 ns (156.25 MHz)4 ns (250 MHz)

The DHO's multiboot

The Zynq has a processor side as well as its logic, and Rigol uses it only to boot. At power-on it loads the golden image the factory wrote at the bottom of its flash. The RK3399 then sends a command over a 10 MHz SPI link to the Zynq's processor side, which reboots the chip into the update image that every firmware install writes 4 MB higher. A third slot holds a build used only for the memory calibration. The processor side runs a small bare-metal command server, no Linux. The update image carries a build stamp the scope reads back from a register: on our DHO924S it says 21 July 2023, variant E. verified

The MHO's file load

The MHO has no FPGA flash. At every boot the RK3399 pulses the FPGA's program line, waits for its init line, streams the whole bitstream (8.3 MB for the FMK230T) over SPI, and checks the done line. Stock waits a fixed 500 ms after power-on; NovaOS watches the init line and is ready in 24 ms. Because nothing is written to the board, a reboot always starts from the file. verified

CH2The interfaces

CH3The blocks, as our driver sees them

Each of these is a set of registers our engine programs, with a behaviour we have checked on the unit.

What is not in this FPGA: the waveform display. The stock software counts and draws waveforms on the processor and GPU; so does NovaOS.

CH4Measured: re-arm, ceiling, read-out

Timeline of one acquisition batch on the DHO's FPGA The FPGA records a sequence of frames. Each frame is the record itself followed by about 23.8 microseconds of re-arm before the next trigger can be taken. After the last frame the FPGA plays the frames out of its memory over PCIe to the host; while it plays, it records nothing. Then the host arms the next sequence. FPGA … recre-arm≈ 23.8 µs play out + PCIe read: no recording ~130–145 MSa/s from the FPGA's memory 5.3 µs (20 ns/div) to 59 µs (2 µs/div) per frame one record sequence: N frames, ≤ ~41,000 frames/s host reads the batch next sequence recording re-arm (measured from time tags) play-out over PCIe not to scale
One batch on the DHO's FPGA, as measured on our DHO924S running NovaOS on Rigol's own FPGA image.
WhatMeasuredHow
Re-arm per frame, inside a sequence~23.8 µs beyond the post-trigger record (about 3,720 cycles of the 6.4 ns clock)difference of consecutive frames' 48-bit time tags; the same at 1 and 10 kpts, 1 and 10 MHz triggers
Ceiling of a record sequence~41,000 frames/s, at any depth10 MHz trigger, 20 ns/div, from the time tags
Play-out from FPGA memory~130–145 MSa/s, whatever is senta 6.25 Mpt window takes 43 ms; 97 compressed points cost as much as 6,250 raw
Play + PCIe read per frame5.3 µs (528 B), 24.6 µs (5 KB), 59 µs (12.5 KB)the engine's batch timer
Waveforms a second, every one counted~31,000 at 20 ns/div, ~10,000 at 2 µs/divNovaOS's fast acquisition with the density display on
Compressed play-out+10 to 11 % at 2 µs/divthe FPGA reads the whole window anyway, so the transfer was never the limit
MHO play-outabout 360–420 MSa/s preliminaryuntriggered frames only; a cabled signal will pin it down

Settings we tried that do not shorten the re-arm, each changed alone: the second state-machine timer used by UltraAcquire, which bounds a sequence's length, not a frame's; the anti-alias setting; the acquisition delay and pre-trigger words, which match stock's. The one setting left untried is the sinc interpolator, which adds work rather than removing it.

Rigol's software and ours

Stock runs Sparrow, Rigol's Android app (com.rigol.scope) on Android 7.1, with a native acquisition library that drives the FPGA through the XDMA driver, waits through about 15 seconds of fixed delays to bring the DHO's FPGA up, and reads the sample stream about 25 times a second. NovaOS replaces all of that with its own engine, written in Rust, that drives the same FPGA image with the same register settings: it brings the DHO's FPGA up in 2 seconds by probing for readiness, and counts every acquired waveform into the density display. The FPGA image itself is Rigol's in both cases; NovaOS reads it from the scope at run time and never distributes or changes it.

How we found this, so you can check it

What our own FPGA design could change

Rigol's FPGA is dense and complete; its limits are in getting data out. These are estimates, not results:

Credits and sources

Measured on our own DHO924S and MHO934 in October 2026. Corrections and measurements welcome: [email protected].