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 / DHO900
MHO900, code 1
MHO900, codes 2–7
FPGA
Xilinx Zynq XC7Z015
Xilinx Kintex-7 XC7K160T
Fudan FMK230T, marked RIGOL RT6888IF
How we know
verified bitstream ID code 0x0373B093
verified in the firmware, ID code 0x0364C093
verified on our MHO934: header part FMK230T, ID code 0x02236143
Logic (datasheet)
46,200 LUTs, 95 block RAMs, 160 DSP slices, a hard PCIe block
101,400 LUTs, 325 block RAMs, 600 DSP slices
about 145,000 LUTs inferred from its frame count
Stock image uses
unknown not decodable with open tools yet
92 % of LUTs, 59 % of flip-flops, 52 % of block RAM, 62 % of DSP decoded
unknown no open database for this die
Loads from
its own QSPI flash: a factory golden image at power-on, then the update image
a file on the SD card, streamed in by the RK3399 (slave serial over SPI) at every boot; no FPGA flash
Link
PCIe Gen2 ×2
PCIe Gen2 ×4
Sample clock
TI LMX2582, 1.25 GHz
TI LMK04826, 2 GHz per ADC
Internal state machine clock
6.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
PCIe with Xilinx XDMA. The FPGA is a PCIe endpoint built from Xilinx's XDMA core. One window maps the FPGA's registers into the processor's memory (32 KiB on the DHO, 16 MiB mapped on the MHO); one streaming channel carries the sample data from FPGA to host. The stock software polls; it uses no interrupts. verified
The ADC's own registers sit behind a serial bridge inside the FPGA: the host writes a word into an FPGA register and the FPGA shifts it out to the ADC. verified
Slow control stays on the processor. The clock chip, the four front-end chips, the range relays and the generator relays are driven by the RK3399 over bit-banged SPI and GPIO lines, not by the FPGA. An FPGA design of our own would inherit them unchanged. verified
Clocks. The sample clock comes from the LMX2582 (DHO) or LMK04826 (MHO), programmed by the RK3399 before the FPGA starts. The FPGA's time base runs from the sample clock: the DHO's time tags count 800 ps ticks. verified
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.
Lane capture. At start-up each ADC lane (28 on the DHO, 56 on the MHO) is swept through 32 delay steps against a training pattern and centred in its open window, then bit-slipped and the ADC cores aligned. A counter checks the ADC clock against the FPGA's own.
Interleave correction. The ADC cores' gains and offsets are trimmed in the FPGA. On the MHO a 16-lane correction block is reloaded at every configuration above 500 MSa/s.
Filters. A sinc interpolator (16 taps on the DHO, 24 on the MHO) and a second-stage FIR (64 taps on the MHO), plus hardware averaging and peak detect.
Trigger engine. 13 trigger types (edge, pulse, slope, video, pattern, duration, timeout, runt, window, delay, setup/hold, Nth edge, plus zone) and 9 serial-bus triggers (RS-232, I²C, SPI, CAN, FlexRay, LIN, I²S, MIL-STD-1553B, video), with A/B sequences and the 16 digital lines as sources.
Compression. When a record is longer than the screen needs, the FPGA sends a minimum and a maximum per bucket, in time order. We fed it a 10 MHz sine and got only the sine's extremes back: it keeps every peak and never drops samples to decimate.
Record and play. The host arms a sequence of N frames; the FPGA records them one per trigger into its memory and counts them. Then it "plays" them out through the streaming channel. One state machine does both, so the scope records or plays, never both at once.
The frame. Each played frame is a 16-byte header followed by 16-bit samples. The header carries the frame's trigger time as a 48-bit count of 800 ps ticks.
The rest. A frequency counter (0.8 ns resolution), a DVM that averages 228 samples, mask-test counters, the Bode sweep, the die temperature, and the generator's DDS (156.25 MHz on the DHO; two channels on the MHO sent over JESD204B).
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
One batch on the DHO's FPGA, as measured on our DHO924S running NovaOS on Rigol's own FPGA image.
What
Measured
How
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 depth
10 MHz trigger, 20 ns/div, from the time tags
Play-out from FPGA memory
~130–145 MSa/s, whatever is sent
a 6.25 Mpt window takes 43 ms; 97 compressed points cost as much as 6,250 raw
Play + PCIe read per frame
5.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/div
NovaOS's fast acquisition with the density display on
Compressed play-out
+10 to 11 % at 2 µs/div
the FPGA reads the whole window anyway, so the transfer was never the limit
MHO play-out
about 360–420 MSa/s preliminary
untriggered 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
Registers and frame headers through our own driver. Our engine maps the FPGA's register window and reads each frame's header from the stream, the same way any XDMA user can. Nothing here needed a debugger or JTAG.
Timing with the FPGA's own clock. Difference the 48-bit time tags of consecutive frames in one batch and you have the FPGA's frame period with no host jitter in it.
The bitstreams, with open tools. The configuration format is public (AMD/Xilinx UG470): walk the packets and you find each image's ID code, frame count and header. Project X-Ray has a database for the XC7K160T, so the MHO's K160T image decodes completely: resources, pins, the PCIe and memory settings. It has no database for the XC7Z015 yet, so the DHO's image stays undecoded until one is built with X-Ray's fuzzers. The FMK230T is not a Xilinx die and no open database describes it; its frame geometry can only be inferred from its K160T twin.
Boot logs and device trees, captured read-only from the stock system: which file loads, which PCIe link trains, which chip answers.
What we do not do: no JTAG, no changes to Rigol's FPGA image or its flash, and no Rigol code on this site.
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:
Explain the 23.8 µs first. Rigol quotes 1,000,000 waveforms a second for UltraAcquire, and a community build reports 100,000 at 10 kpts on the same image. Neither fits a 23.8 µs re-arm, so part of it may be how the image is driven. This is bench work, and it comes before any FPGA work.
Overlapped capture and read-out. Separate record and play engines, recording into one half of memory while the other plays: at 2 µs/div about three times today's rate (about 28,000–30,000 waveforms a second on the DHO). At full sample rate the DHO's memory is already about 94 % busy while recording, so overlap helps the slower timebases most.
Density in the FPGA. A histogram built from the ADC stream, as Tektronix's FastAcq does, bypassing the memory, the PCIe link and the processor: roughly 0.5 to 1 million waveforms a second with every one counted. On the XC7Z015 its block RAM is the hard limit.
Cheap additions: a trigger-attempt counter and a dead-time counter, so the screen can show the true capture ratio.
Where it starts: the MHO, because its FPGA loads from a file and a reboot restores Rigol's image. The K160T builds with AMD's free tools; the FMK230T has no vendor tool we can use, so it waits on an open toolchain.
Credits and sources
AMD/Xilinx: UG470 (7-series configuration), DS190 (Zynq-7000), DS180 (7-series), the XDMA core and driver
Project X-Ray (F4PGA), the open 7-series bitstream documentation
EEVblog forum threads on the DHO800/900 and the MHO900, especially dc101 on the MHO bitstreams and Norbert Kiszka on the DHO