AD9081 no-OS Driver

See drivers/adc/ad9081 (doxygen) for the Doxygen documentation.

Supported Devices

Overview

The AD9081 is a mixed-signal front-end (MxFE) device featuring four 16-bit, 12 GSPS maximum sample rate RF digital-to-analog converter (DAC) cores and four 12-bit, 4 GSPS RF analog-to-digital converter (ADC) cores. The device is well suited for applications requiring both wideband ADCs and DACs to process signals that have wide instantaneous bandwidth.

The device features eight transmit and eight receive lanes that support 24.75 Gbps/lane JESD204C or 15.5 Gbps/lane JESD204B standards. An on-chip clock multiplier and digital signal processing (DSP) capability supports either wideband or multiband direct-to-RF applications. The DSP datapaths include eight fine complex DUCs and four coarse complex DUCs for transmit, eight fine complex DDCs and four coarse complex DDCs for receive, each with 48-bit NCO. The DSP datapaths can be bypassed to allow a direct connection between the converter cores and the JESD204 data transceiver port.

The AD9081 also features low latency loopback and frequency hopping modes targeted at phased array radar and electronic warfare applications, on-chip PLL with multichip synchronization, fast frequency hopping, receive AGC support, transmit DPD support, and programmable 192-tap PFIR filter for receive equalization. The device is packaged in a 15 mm x 15 mm, 324-ball BGA with 0.8 mm pitch.

Applications

  • Wireless communications infrastructure

  • Microwave point to point, E-band, and 5G mmWave

  • Broadband communications systems

  • DOCSIS 3.1 and 4.0 CMTS

  • Phased array radar and electronic warfare

  • Electronic test and measurement systems

Device Configuration

The AD9081 driver provides a high-level interface to the underlying ADI API for configuring and controlling the MxFE device. The driver functions are organized into logical categories based on their role in the device setup and operational pipeline.

Initialization

The initialization function ad9081_init allocates resources and performs the complete device bring-up sequence. It initializes the SPI interface and GPIO reset pin, sets up HAL function pointers and serializer/deserializer settings for JESD204 lanes, performs a hardware device reset, and validates the chip identification. After successful identification, it calls the internal ad9081_setup function to configure clocks, SYSREF, and data converter paths, and then registers the device with the JESD204 framework. The companion function ad9081_remove releases all allocated resources.

TX Path Configuration

The TX path is configured through the internal ad9081_setup_tx function, which is called during initialization. This function sets up the four DAC main datapaths and eight DAC channelizers by calling adi_ad9081_device_startup_tx with the interpolation rates, DAC-to-channel crossbar selections, and NCO frequency shifts specified in the initialization parameters. It also configures individual DAC channel gains, JESD RX LMFC delay compensation, and DAC full-scale output currents.

RX Path Configuration

The RX path is configured through the internal ad9081_setup_rx function, which sets up the four ADC main datapaths (coarse DDCs) and eight ADC channelizers (fine DDCs). It calls adi_ad9081_device_startup_rx with the decimation rates, CDDC and FDDC selections, NCO frequency shifts, and complex-to-real enable settings. The function also configures the JESD TX converter selection for each link and enables NCO synchronization when applicable.

Clock and SYSREF Configuration

The ad9081_setup function manages the top-level device configuration including SYSREF input coupling (AC or DC), CMOS input enable settings, and the device clock configuration via adi_ad9081_device_clk_config_set. SYSREF management ensures proper alignment of JESD204 data streams and maintains timing accuracy between converters and interfaces. The driver supports both continuous and one-shot SYSREF modes with configurable averaging.

GPIO Configuration

To configure GPIO pins on the AD9081, the driver utilizes ADI API functions. Functions like adi_ad9081_dac_gpio_as_sync1_out_set configure synchronization outputs, and adi_ad9081_device_nco_sync_gpio_set adjusts GPIO states for NCO synchronization. Configuration parameters are available within the ad9081_init_param structure, and GPIO descriptors are outlined in the ad9081_phy device structure.

Driver Initialization Example

#include "ad9081.h"
#include "app_clock.h"
#include "app_jesd.h"
#include "parameters.h"
#include "app_config.h"

struct no_os_clk app_clk[MULTIDEVICE_INSTANCE_COUNT];
struct no_os_clk jesd_clk[2];

struct link_init_param jrx_link_tx = {
    .device_id = 0,
    .octets_per_frame = AD9081_TX_JESD_F,
    .frames_per_multiframe = AD9081_TX_JESD_K,
    .samples_per_converter_per_frame = AD9081_TX_JESD_S,
    .high_density = AD9081_TX_JESD_HD,
    .converter_resolution = AD9081_TX_JESD_N,
    .bits_per_sample = AD9081_TX_JESD_NP,
    .converters_per_device = AD9081_TX_JESD_M,
    .control_bits_per_sample = AD9081_TX_JESD_CS,
    .lanes_per_device = AD9081_TX_JESD_L,
    .subclass = AD9081_TX_JESD_SUBCLASS,
    .link_mode = AD9081_TX_JESD_MODE,
    .dual_link = 0,
    .version = AD9081_TX_JESD_VERSION,
    .logical_lane_mapping = AD9081_TX_LOGICAL_LANE_MAPPING,
    .tpl_phase_adjust = AD9081_JRX_TPL_PHASE_ADJUST,
};

struct link_init_param jtx_link_rx = {
    .device_id = 0,
    .octets_per_frame = AD9081_RX_JESD_F,
    .frames_per_multiframe = AD9081_RX_JESD_K,
    .samples_per_converter_per_frame = AD9081_RX_JESD_S,
    .high_density = AD9081_RX_JESD_HD,
    .converter_resolution = AD9081_RX_JESD_N,
    .bits_per_sample = AD9081_RX_JESD_NP,
    .converters_per_device = AD9081_RX_JESD_M,
    .control_bits_per_sample = AD9081_RX_JESD_CS,
    .lanes_per_device = AD9081_RX_JESD_L,
    .subclass = AD9081_RX_JESD_SUBCLASS,
    .link_mode = AD9081_RX_JESD_MODE,
    .dual_link = 0,
    .version = AD9081_RX_JESD_VERSION,
    .logical_lane_mapping = AD9081_RX_LOGICAL_LANE_MAPPING,
    .link_converter_select = AD9081_RX_LINK_CONVERTER_SELECT,
};

struct ad9081_init_param phy_param = {
    .gpio_reset = &gpio_phy_resetb,
    .spi_init = &phy_spi_init_param,
    .dev_clk = &app_clk[0],
    .jesd_tx_clk = &jesd_clk[1],
    .jesd_rx_clk = &jesd_clk[0],
    .sysref_coupling_ac_en = 0,
    .multidevice_instance_count = 1,
    .jesd_sync_pins_01_swap_enable = false,
    .config_sync_0a_cmos_enable = false,
    .lmfc_delay_dac_clk_cycles = 0,
    .nco_sync_ms_extra_lmfc_num = 0,
    .nco_sync_direct_sysref_mode_enable = 0,
    .sysref_average_cnt_exp = 7,
    .continuous_sysref_mode_disable = 0,
    .tx_disable = false,
    .rx_disable = false,
    .dac_frequency_hz = AD9081_DAC_FREQUENCY,
    .tx_main_interpolation = AD9081_TX_MAIN_INTERPOLATION,
    .tx_main_nco_frequency_shift_hz = AD9081_TX_MAIN_NCO_SHIFT,
    .tx_dac_channel_crossbar_select = AD9081_TX_DAC_CHAN_CROSSBAR,
    .tx_maindp_dac_1x_non1x_crossbar_select = AD9081_TX_DAC_1X_NON1X_CROSSBAR,
    .tx_full_scale_current_ua = AD9081_TX_FSC,
    .tx_channel_interpolation = AD9081_TX_CHAN_INTERPOLATION,
    .tx_channel_nco_frequency_shift_hz = AD9081_TX_CHAN_NCO_SHIFT,
    .tx_channel_gain = AD9081_TX_CHAN_GAIN,
    .jrx_link_tx[0] = &jrx_link_tx,
    .jrx_link_tx[1] = NULL,
    .adc_frequency_hz = AD9081_ADC_FREQUENCY,
    .nyquist_zone = AD9081_ADC_NYQUIST_ZONE,
    .rx_main_nco_frequency_shift_hz = AD9081_RX_MAIN_NCO_SHIFT,
    .rx_main_decimation = AD9081_RX_MAIN_DECIMATION,
    .rx_main_complex_to_real_enable = {0, 0, 0, 0},
    .rx_main_enable = AD9081_RX_MAIN_ENABLE,
    .rx_channel_nco_frequency_shift_hz = AD9081_RX_CHAN_NCO_SHIFT,
    .rx_channel_decimation = AD9081_RX_CHAN_DECIMATION,
    .rx_channel_complex_to_real_enable = {0, 0, 0, 0, 0, 0, 0, 0},
    .rx_channel_enable = AD9081_RX_CHAN_ENABLE,
    .jtx_link_rx[0] = &jtx_link_rx,
    .jtx_link_rx[1] = NULL,
};

struct ad9081_phy *phy;
int32_t status;

status = app_clock_init(app_clk);
if (status != 0)
    return status;

status = app_jesd_init(jesd_clk, ADXCVR_REF_CLK_KHZ,
                       ADXCVR_RX_DEV_CLK_KHZ, ADXCVR_TX_DEV_CLK_KHZ,
                       ADXCVR_RX_LANE_CLK_KHZ, ADXCVR_TX_LANE_CLK_KHZ);
if (status != 0)
    return status;

status = ad9081_init(&phy, &phy_param);
if (status != 0)
    return status;