ADRV9009 no-OS Driver
Supported Devices
Overview
The ADRV9009 is a highly integrated radio frequency (RF) agile transceiver offering dual transmitters and receivers, integrated synthesizers, and digital signal processing functions. The IC delivers a versatile combination of high performance and low power consumption demanded by 3G, 4G, and 5G macro cell time division duplex (TDD) base station applications. The receive path consists of two independent, wide bandwidth, direct conversion receivers with state-of-the-art dynamic range, and it supports a wide bandwidth, time-shared observation receiver (ORx) for use in TDD applications. The transmitters use an innovative direct conversion modulator that achieves high modulation accuracy with exceptionally low noise, while the fully integrated phase-locked loop (PLL) provides high performance, low power, fractional-N RF frequency synthesis for the transmitter (Tx) and receiver (Rx) signal paths.
Applications
3G, 4G, and 5G TDD macrocell base stations
TDD active antenna systems
Massive multiple input, multiple output (MIMO)
Phased array radar
Electronic warfare
Military communications
Portable test equipment
Device Configuration
Device Initialization and Core Control
Initialization functions allocate and initialize core data structures, set up SPI, clocks, and JESD204B settings, and parse user-provided or default AGC parameters. Key routines include:
adrv9009_init– Allocates the RF PHY structure, copiesinitialization parameters, and invokes helper routines such as AGC parameter parsing.
adrv9009_setup– Performs low‑level hardware initializationincluding SPI configuration and clock descriptor setup for various sample clocks, while reading device revision and API versions.
adrv9009_post_setup– Finalizes ADC/DAC configuration byprogramming per‑channel conversion settings, updating DAC rate registers, and computing DAC clock frequency.
Additional helper macros (e.g., __adrv9009_of_get_param and ADRV9009_OF_PROP) are used to apply default values when necessary.
JESD204B Link Configuration and Management
This function group is dedicated to setting up and monitoring the high‑speed JESD204B serial interface that connects the transceiver with downstream processing. These routines configure serializer/deserializer hardware, manage framer/deframer parameters, and enable SYSREF signals for proper synchronization. Notable functions include:
adrv9009_jesd204_link_init– Initializes JESD204 links byassigning lane mappings and calculating lane rates.
TALISE_setupSerializersorTALISE_setupDeserializers– Configureserializer and deserializer settings such as pre‑emphasis and lane polarity.
TALISE_setupJesd204bFramerorTALISE_setupJesd204bDeframer–Program framer/deframer parameters including bank IDs and SYSREF routing.
adrv9009_jesd204_clks_enableandadrv9009_jesd204_link_enable– Enable SYSREF signals and activate the JESD204B links.
adrv9009_jesd204_setup_stage1 … stage5– Execute a multi‑stagestate machine covering multi‑chip sync, ARM/stream processor binary loading, PLL lock verification, and calibration triggering.
These routines combine low‑level SPI operations with integrated error handling to ensure robust JESD204B communication.
Automatic Gain Control (AGC) Configuration
AGC routines dynamically adjust signal strength to optimize performance under varying conditions. These functions set gain limits, adjust delay parameters, manage peak detection timing, and handle power measurement settings. Essential routines include:
TALISE_setupRxAgcandTALISE_resetRxAgc– Initialize and resetthe AGC for Rx channels by writing configuration values to device registers.
TALISE_setupDualBandRxAgc– Configure dual‑band AGC for Rxchannels when external LNAs are used, including setting 3.3V GPIO parameters for dual‑band LNA gains.
- Readback functions such as
TALISE_getAgcCtrlRegisters, TALISE_getAgcPeakRegisters, andTALISE_getAgcPowerRegistersprovide monitoring capabilities.
- Readback functions such as
TALISE_setRxAgcMinMaxGainIndex– Dynamically adjusts the gainrange for receive channels.
Transmit (Tx) Path Configuration
Tx configuration routines control output attenuation, DAC full‑scale settings, test tone generation via NCOs, and PA protection. This section enables fine‑grained manipulation of transmit signal characteristics. Key functions include:
TALISE_setTxAttenuationandTALISE_getTxAttenuation– Set orretrieve Tx attenuation levels by converting milli‑dB values into register settings.
TALISE_setTxAttenCtrlPinandTALISE_getTxAttenCtrlPin– ManageGPIO‑based controls for dynamic attenuation adjustments.
TALISE_setDacFullScale– Program the Tx DAC’s full‑scale valuebefore the ARM firmware is loaded.
TALISE_txNcoShifterSetandTALISE_enableTxNco– Configure theon‑chip NCO for generating test tones with precise phase control.
TALISE_setPaProtectionCfg,TALISE_getPaProtectionCfg, andTALISE_enablePaProtection– Set PA protection thresholds and monitor power levels to prevent overdriving.
TALISE_getTxSamplePower– Read and convert raw ADC values todBFS for transmit power monitoring.
Receive (Rx) Path and Data Formatting Configuration
Rx configuration involves setting gain tables, controlling gain modes, and formatting ADC output for subsequent processing. This section provides routines for both manual and automated gain adjustments, as well as for selecting data output formats. Featured functions include:
TALISE_programRxGainTableandTALISE_programOrxGainTable–Load gain table entries for normal and observation Rx channels, establishing front‑end, external, and digital gain settings.
TALISE_setRxManualGain,TALISE_getRxGain,TALISE_setObsRxManualGain, andTALISE_getObsRxGain– Enable and query manual gain modes.
TALISE_setRxGainControlMode– Select between manual gain controland various AGC modes.
TALISE_setRxDataFormat,TALISE_getRxDataFormat, andTALISE_getSlicerPosition– Define the digital data output format, whether floating‑point or integer with slicer bits.
TALISE_setRxGainCtrlPinandTALISE_getRxGainCtrlPin– Allowexternal gain control via GPIO, and
TALISE_setGainTableExtCtrlPinsmaps GPIO outputs to the current gain index.
Calibration and Tracking
Calibration routines optimize performance across both the Tx and Rx paths by correcting DC offsets, IQ imbalances, and phase misalignments. This section is divided into initialization and continuous tracking calibrations. Core functions include:
TALISE_runInitCals,TALISE_waitInitCals,TALISE_checkInitCalComplete,TALISE_abortInitCals, andTALISE_getInitCalStatus– Execute and verify the initial calibration process (including DC offset, LO leakage, and quadrature corrections).
TALISE_enableTrackingCals,TALISE_getEnabledTrackingCals,TALISE_getPendingTrackingCals,TALISE_rescheduleTrackingCal,TALISE_setAllTrackCalState, andTALISE_getAllTrackCalState– Manage continuous tracking calibrations to adapt to changing operational conditions.
- Status query functions like
TALISE_getTxLolStatus, TALISE_getTxQecStatus,TALISE_getRxQecStatus,TALISE_getOrxQecStatus, andTALISE_getRxHd2Statusprovide detailed metrics on calibration performance.
- Status query functions like
ARM Processor Interface
The Talise transceiver integrates an embedded ARM processor to offload tasks such as calibration, digital processing, and system monitoring. The ARM interface provides functions for memory transfer, command execution, and debugging. Key routines include:
TALISE_initArm,TALISE_writeArmProfile,TALISE_loadArmFromBinary, andTALISE_loadAdcProfiles– Initialize the ARM processor by loading firmware and configuration profiles, setting DMA, and verifying firmware integrity.
TALISE_readArmMem,TALISE_writeArmMem,TALISE_writeArmConfig, andTALISE_readArmConfig– Perform low‑level memory operations using DMA and auto‑increment mechanisms.
TALISE_sendArmCommand,TALISE_readArmCmdStatus, andTALISE_waitArmCmdStatus– Facilitate a command-control interface via the ARM mailbox.
TALISE_getArmVersion_v2andTALISE_verifyArmChecksum–Retrieve firmware version info and verify checksum integrity. Additional error handling functions such as
talGetArmErrorMessageandtalArmCmdErrorHandlerensure reliable ARM communication.
Hardware Abstraction Layer (HAL)
HAL functions provide a consistent interface for low‑level SPI operations and logging, abstracting the hardware details away from higher‑level configuration routines. These functions manage timeout settings, handle retries, and encapsulate register read/write operations. Core routines include:
talSpiReadByteandtalSpiWriteByte– Handle single‑byte SPItransactions with built‑in timeout and retry logic.
talSpiReadBytesandtalSpiWriteBytes– Manage multi‑bytebuffer SPI transfers.
talSpiReadFieldandtalSpiWriteField– Offer fine‑grainedaccess to specific bit fields within registers.
talWriteToLog– Integrate logging support via ADI HAL routines forerror and diagnostic tracking.
GPIO Configuration
GPIO configuration functions provide control over various pin operations used for SPI, external gain control, debug signal monitoring, and auxiliary interfacing. These functions are organized into logical groups:
Low Voltage & 3.3V GPIO Control
TALISE_setGpioOeandTALISE_getGpioOe– Set and read outputenable settings for low‑voltage GPIOs.
TALISE_setGpioSourceCtrlandTALISE_getGpioSourceCtrl–Configure multiplexer selections for low‑voltage GPIOs.
TALISE_setGpioPinLevel,TALISE_getGpioPinLevel, andTALISE_getGpioSetLevel– Drive and verify pin levels in bit‑bang mode; similar functions with the 3v3 prefix handle 3.3V GPIOs.
Monitor, Interrupt, and Auxiliary Interfaces
These functions cover the routing of debug signals, interrupt management, and auxiliary DAC/ADC operations. Key routines include:
TALISE_setGpioMonitorOutandTALISE_getGpioMonitorOut– Routeinternal signals for external debugging.
TALISE_setGpIntMask,TALISE_getGpIntMask,TALISE_getGpIntStatus, andTALISE_gpIntHandler– Manage general purpose interrupts.
TALISE_setupAuxDacsandTALISE_writeAuxDac– Configure andupdate auxiliary DAC outputs.
TALISE_setAuxAdcPinModeGpio,TALISE_getAuxAdcPinModeGpio,TALISE_startAuxAdc, andTALISE_readAuxAdc– Set up and read from the auxiliary ADC.
TALISE_setSpi2EnableandTALISE_getSpi2Enable– Control thesecondary SPI interface used for Tx attenuation.
TALISE_getTemperature– Retrieve on‑chip temperature sensor datavia the ARM processor, with
talGetGpioErrorMessageproviding human‑readable error descriptions.
Radio Control and Pin Management
Radio control functions integrate firmware streaming, power management, and frequency configuration to enable dynamic adaptation of the transceiver. These routines handle control pin settings, LO configuration, and mapping between transmit and observation channels. Featured functions include:
TALISE_loadStreamFromBinary– Load the stream processor’s firmwareimage.
TALISE_setArmGpioPins– Configure ARM‑assigned GPIOs fortime‑division duplex and radio control purposes.
TALISE_setRadioCtlPinModeandTALISE_getRadioCtlPinMode– Setand query radio control pin modes.
TALISE_setOrxLoCfgandTALISE_getOrxLoCfg– Configureobservation receiver LO settings.
TALISE_radioOn,TALISE_radioOff, andTALISE_getRadioState– Control and monitor the overall radio power state.
TALISE_setRxTxEnableandTALISE_getRxTxEnable– Manage signalpath enablement and channel mapping.
TALISE_setTxToOrxMapping– Map Tx channels to ORx channels forcalibration purposes.
Additional routines control RF PLL frequency, set loop filters, manage frequency hopping (e.g.,
TALISE_setFhmConfig,TALISE_setFhmMode,TALISE_setFhmHop, etc.), and adjust external LO outputs.
Driver Initialization Example
#include "no_os_error.h"
#include "no_os_alloc.h"
#include "no_os_delay.h"
#include "adrv9009.h"
#include "talise_types.h"
/* Platform-specific objects defined elsewhere */
extern struct no_os_spi_init_param adrv9009_spi_ip;
extern struct no_os_clk_desc *dev_clk;
extern taliseDevice_t *adrv9009_device_ptr;
/* Example RX AGC configuration parameters; populate with valid values */
taliseAgcCfg_t rxAgcCfg = {
.agcPeak = {
.agcUnderRangeLowInterval_ns = 205,
.agcUnderRangeMidInterval = 2,
.agcUnderRangeHighInterval = 4,
.apdHighThresh = 39,
.apdLowGainModeHighThresh = 36,
.apdLowThresh = 23,
.apdLowGainModeLowThresh = 19,
.apdUpperThreshPeakExceededCnt = 6,
.apdLowerThreshPeakExceededCnt = 3,
.apdGainStepAttack = 4,
.apdGainStepRecovery = 2
},
.agcPower = {
.powerEnableMeasurement = 1,
.powerUseRfirOut = 1,
.powerUseBBDC2 = 0,
.underRangeHighPowerThresh = 9,
.underRangeLowPowerThresh = 2,
.underRangeHighPowerGainStepRecovery= 4,
.underRangeLowPowerGainStepRecovery = 4,
.powerMeasurementDuration = 5,
.rx1TddPowerMeasDuration = 5,
.rx1TddPowerMeasDelay = 1,
.rx2TddPowerMeasDuration = 5,
.rx2TddPowerMeasDelay = 1,
.upper0PowerThresh = 2,
.upper1PowerThresh = 0,
.powerLogShift = 0
},
.agcPeakWaitTime = 4,
.agcRx1MaxGainIndex = 255,
.agcRx1MinGainIndex = 195,
.agcRx2MaxGainIndex = 255,
.agcRx2MinGainIndex = 195,
.agcGainUpdateCounter_us = 250,
.agcRx1AttackDelay = 10,
.agcRx2AttackDelay = 10,
.agcSlowLoopSettlingDelay = 16,
.agcLowThreshPreventGain = 0,
.agcChangeGainIfThreshHigh = 1,
.agcPeakThreshGainControlMode = 1,
.agcResetOnRxon = 0,
.agcEnableSyncPulseForGainCounter = 0,
.agcEnableIp3OptimizationThresh = 0,
.ip3OverRangeThresh = 31,
.ip3OverRangeThreshIndex = 246,
.ip3PeakExceededCnt = 4,
.agcEnableFastRecoveryLoop = 0
};
adrv9009_init_param init_param = {
.spi_init = &adrv9009_spi_ip,
.dev_clk = dev_clk,
.adrv9009_device = adrv9009_device_ptr,
.streamImageFile = "stream.bin",
.armImageFile = "arm.bin",
.rxAgcConfig_init_param = &rxAgcCfg,
.trx_lo_frequency = 2400000000ULL,
.tx_pa_protection = {
.tx1PowerThreshold = 4096,
.tx2PowerThreshold = 4096,
.tx1PeakThreshold = 128,
.tx2PeakThreshold = 128,
}
};
struct adrv9009_rf_phy *phy;
int ret;
/* Initialize the ADRV9009 device */
ret = adrv9009_init(&phy, &init_param);
if (ret) {
printf("ADRV9009 initialization failed: %d\n", ret);
goto init_error;
}
printf("ADRV9009 initialization successful\n");
/* Set AGC min/max gain index for RX1 */
ret = TALISE_setRxAgcMinMaxGainIndex(phy->talise, TAL_RX1, 195, 255);
if (ret) {
printf("Failed to set RX1 AGC min/max gain index: %d\n", ret);
goto init_error;
}
/* Read back AGC control registers */
taliseAgcCtrl_t agc_ctrl = {0};
ret = TALISE_getAgcCtrlRegisters(phy->talise, TAL_RX1, &agc_ctrl);
if (ret) {
printf("Failed to read RX1 AGC control registers: %d\n", ret);
goto init_error;
}
/* Reset AGC for RX1 */
ret = TALISE_resetRxAgc(phy->talise, TAL_RX1);
if (ret) {
printf("Failed to reset RX1 AGC: %d\n", ret);
goto init_error;
}
/* Enable radio */
ret = TALISE_radioOn(phy->talise);
if (ret) {
printf("Failed to turn radio on: %d\n", ret);
goto init_error;
}
/* Set Tx attenuation for channel 1 */
ret = TALISE_setTxAttenuation(phy->talise, TAL_TX1, 10000); /* 10 dB */
if (ret) {
printf("Failed to set TX1 attenuation: %d\n", ret);
goto init_error;
}
/* Get temperature */
int16_t temp = 0; /* Initialize to a default value */
ret = TALISE_getTemperature(phy->talise, &temp);
if (ret) {
printf("Failed to read temperature: %d\n", ret);
goto init_error;
}
printf("Device temperature: %d\n", temp);
printf("AGC configuration and queries successful\n");
goto exit;
init_error:
printf("Error encountered during ADRV9009 initialization or AGC configuration\n");
exit:
;