AD9088 no-OS Example Project
See projects/ad9088 (doxygen) for the Doxygen documentation.
Supported Evaluation Boards
AD9084-FMCA-EBZ (Rev. C), carrying either the AD9084 or the AD9088
Overview
The AD9088 and AD9084 are Apollo MxFE (mixed-signal front end) devices that combine wideband RF DACs, wideband RF ADCs, and a configurable digital datapath in a single package. The AD9088 is an octal 16-bit, 16 GSPS RF DAC with an octal 12-bit, 8 GSPS RF ADC (8T8R); the AD9084 is a quad 16-bit, 28 GSPS RF DAC with a quad 12-bit, 20 GSPS RF ADC (4T4R). Each device is organized as two independent sides, and each side carries its own converters, coarse and fine digital up/down converters, and JESD204C link.
The datapath is not fixed in hardware. It is described by a device profile image that the driver loads over SPI at start-up, together with signed core firmware for the on-chip processor. The profile sets the JESD204 mode, converter counts, decimation and interpolation ratios, and sample rates, so the same silicon can present very different link geometries. This is why the profile chosen at build time has to match the HDL design loaded on the FPGA.
Host control is over a SPI bus. Sample data moves over JESD204C links to the FPGA, where the ADI HDL reference design terminates them in JESD204 transport-layer cores and moves samples to and from system memory with AXI DMA.
Applications
Multiband, multimode digital radios
3G/4G/5G massive MIMO base stations
Wideband phased-array radar and electronic warfare
Electronic test and measurement equipment
Wideband satellite communications
Hardware Specifications
Power Supply Requirements
Power the evaluation board and the VCU118 as described in their respective user guides. The firmware makes no assumptions about the supply arrangement beyond the board being powered and its clocks running before the AD9088 is initialized.
Clocking and Synchronization Devices
Three support devices share the clock-generation SPI bus and are brought up by this project before the Apollo itself:
Device |
SPI CS |
Function |
Setting |
|---|---|---|---|
ADF4382 |
0 |
Device clock PLL, drives the Apollo device clock |
20 GHz |
HMC7044 |
1 |
Clock distribution for the FPGA reference clocks |
2.5 GHz |
ADF4030 |
4 |
JESD204 SYSREF provider, BSYNC source for MCS |
2.5 GHz |
All three run from the board's 125 MHz reference. The HMC7044 figure is its PLL2 frequency and the ADF4030 figure its VCO frequency; SYSREF itself is 9.765625 MHz.
The ADF4030 is the JESD204 SYSREF provider in this design. It supplies the SYSREF pulse that subclass 1 deterministic latency depends on, and it drives the BSYNC line that the multi-chip synchronization (MCS) calibration measures against.
The Apollo itself sits on a second SPI bus at chip select 0, and its reset is driven from an AXI GPIO.
No-OS Supported Examples
This project is organized around the no-OS variant based build flow.
Selecting a variant at build time (--variant <name>) chooses which
application is compiled. The platform main() is a thin dispatcher that
calls example_main(), provided by the selected example. Shared
initialization data is defined in
src/common,
and platform-specific macros and extra init parameters are in
src/platform.
Basic Example
Build with --variant basic_example.
The basic example performs the full bring-up and stops there. It initializes
the ADF4382, HMC7044, and ADF4030, binds the multi-chip synchronization
callbacks, brings up the AXI DMAC, transceiver, and JESD204 link-layer cores,
then initializes the AD9088 itself — loading the core firmware and the device
profile over SPI. It builds the JESD204 topology and runs the link state
machine, reads back both link-layer cores' status, and prints
Project configured when the link is up.
It then exercises the NCO control API as a read-back check, setting and reading four frequencies on side A's first datapath: the receive and transmit coarse NCOs at 125 MHz and the receive and transmit fine NCOs at 40.2 MHz. Each round trip is printed as it is made. No sample data is moved.
Use this variant to confirm that the SPI buses, profile, clocking, and JESD204 link are all healthy before involving the datapath.
DMA Example
Build with --variant dma_example.
The DMA example performs the same bring-up as the basic example, then moves sample data in both directions over a cabled loopback. It transmits a tone and captures into memory; it does not analyse the capture. What it reports is where the capture landed, so the data can be pulled out and examined with an external tool.
Once the link is up it works out the capture geometry from the links the profile actually has in use. Both sides share one transport layer in this HDL build, so a capture holds every side at once — side A's converters first, side B's after them — and the example prints each side's window along with the total sample count and width. A link carrying more than four converters per side is rejected rather than captured half-wrong, and a transmit link is required to carry its converters in I/Q pairs.
It then tunes every coarse and fine NCO on every side in use to the same frequency — the coarse ones to an eighth of the DAC rate, the fine ones to 0 Hz. Tuning transmit and receive identically makes the two frequency translations cancel, so a tone arrives at the frequency it was sent at. Every datapath on the side is tuned, not just the one carrying the first I/Q pair: a datapath left on the profile's own frequencies would lose its tone outside the passband while the rest of the capture looked fine. The example checks that transmit and receive ended up matched and fails if they did not.
The transmit side tiles a shared sine table across its buffer and replays it cyclically through the AXI DMAC and the transmit data offload. The transfer is sized to the smaller of the buffer and the offload's own memory, then floored to a whole number of table passes so the cyclic wrap leaves no phase discontinuity — which matters because the capture is shorter than the replay and can start anywhere in it.
The receive data offload is reset and re-armed immediately before the capture. This design synthesizes it with auto-bringup enabled and the receive instance in one-shot mode, so by capture time it holds a fill from before the link existed and its store phase is long over; without the re-arm the capture returns that stale fill or times out.
The example finishes by parking in an idle loop with the link still up, so the capture buffer stays intact and readable from a debugger.
A coaxial cable between the DAC output and the ADC input is what makes the captured tone the transmitted one. Without it the capture still runs and reports normally — it just contains noise.
Capture depth is ADC_BUFFER_SAMPLES (16384 samples per converter) in
src/platform/xilinx/parameters.h.
If multi-chip synchronization leaves the receiver out of sync, the example
brings it back before deriving anything from the link. This is controlled by
RX_LINK_RECOVER in
src/examples/dma/dma_example.c.
Selecting the Device Profile
Both variants embed a device profile image in the ELF. It is selected by the
CONFIG_AD9088_PROFILE Kconfig string and defaults to
204C_M4_L8_NP16_20p0_4x2.bin. A bare file name is resolved relative to
drivers/rf-transceiver/ad9088/firmware;
an absolute path is used as given, so a profile generated outside the tree
works too. Whichever image is picked, it is embedded under a fixed name, so the
symbols the driver looks for never change.
The profile must match the HDL design programmed into the FPGA. A mismatch
between the link geometry the profile describes and the one the HDL implements
shows up as a JESD204 link that never reaches LINK_RUNNING.
The file name encodes the configuration. For
204C_M4_L8_NP16_20p0_4x2.bin:
Field |
Meaning |
|---|---|
|
JESD204C |
|
converters per link |
|
lanes per link |
|
bits per sample |
|
converter sample rate in GSPS (20.0) |
|
datapath configuration for the fine/coarse up and down converters |
One profile ships with the driver: 204C_M4_L8_NP16_20p0_4x2.bin. Any other
profile has to be supplied as a file, either copied into the firmware directory
or referenced by absolute path.
The variant defconfigs do not set CONFIG_AD9088_PROFILE, so a build uses
the Kconfig default unless it is changed. In an already configured build
directory (see Build Command), change it with menuconfig and rebuild:
cmake --build build/ad9088-dma_example-vcu118 --target menuconfig
cmake --build build/ad9088-dma_example-vcu118 --target ad9088
The selection is kept in the build directory and survives rebuilds. To make it
part of a variant instead, add the symbol to the variant's defconfig
(basic_example.conf or dma_example.conf) and rebuild:
CONFIG_AD9088_PROFILE="/abs/path/to/my_profile.bin"
Removing the line again does not restore the default in an existing build
directory, since the value is already stored there. Reconfigure with
--fresh to start from the defconfigs again:
cmake -B build/ad9088-dma_example-vcu118 --preset vcu118 --fresh \
-DPROJECT_DEFCONFIG=ad9088/dma_example.conf \
-DHARDWARE=/abs/path/to/system_top.xsa
No-OS Supported Platforms
Xilinx
Used Hardware
The FPGA design comes from the ADI HDL repository, project
ad9084_ebz,
carrier vcu118. Build it there and export system_top.xsa, which the
no-OS build consumes through --hardware.
Connections
Seat the evaluation board on the VCU118 FMC+ connector (J22).
Power the boards as described in their user guides.
Connect the VCU118 USB UART bridge to the host PC. The console runs at 115200 baud, 8N1.
Connect the VCU118 JTAG USB port to the host PC for programming.
For the DMA example, connect a coaxial cable between the DAC output and the ADC input on the evaluation board. The basic example does not need it.
The clock-generation devices (ADF4382, HMC7044, ADF4030) and the Apollo are wired on the evaluation board and need no external connections; their chip selects are listed in Clocking and Synchronization Devices.
Build Command
The Xilinx platform uses the CMake/Ninja build system. Available variants:
basic_example, dma_example. Available boards: vcu118.
For toolchain setup and prerequisites, see the Xilinx CMake build guide.
The build drives the Vitis Python CLI (vitis -s) and was tested with Vitis
2025.1. Vitis 2022.x does not provide that CLI and fails at configure time.
HARDWARE must be an absolute path: the toolchain and the flash target
read it from different working directories.
# Source the Vitis environment (sets XILINX_VITIS and adds tools to PATH)
source /path/to/Vitis/settings64.sh
# PowerShell (Windows) equivalent:
# $env:XILINX_VITIS = "<C:\path\to\Vitis>"
cd no-OS
# configure (replace the variant as needed)
cmake -B build/ad9088-dma_example-vcu118 --preset vcu118 \
-DPROJECT_DEFCONFIG=ad9088/dma_example.conf \
-DHARDWARE=/abs/path/to/system_top.xsa
# build
cmake --build build/ad9088-dma_example-vcu118 --target ad9088
# program the bitstream and run the ELF over JTAG
cmake --build build/ad9088-dma_example-vcu118 --target flash
The ELF is written to
build/ad9088-<variant>-vcu118/build/ad9088.elf.
Xilinx uses its own JTAG flow through the Vitis tools, so the flash target
needs no PROBE setting. The same build directory also carries erase,
debug, debug_server, and menuconfig targets.
tools/scripts/no_os_build.py wraps the three commands above if you prefer
a single invocation:
python tools/scripts/no_os_build.py build \
--project ad9088 --variant dma_example --board vcu118 \
--hardware /abs/path/to/system_top.xsa
# list the valid project/variant/board combinations
python tools/scripts/no_os_build.py list --project ad9088