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_setupSerializers
orTALISE_setupDeserializers
– Configureserializer and deserializer settings such as pre‑emphasis and lane polarity.
TALISE_setupJesd204bFramer
orTALISE_setupJesd204bDeframer
–Program framer/deframer parameters including bank IDs and SYSREF routing.
adrv9009_jesd204_clks_enable
andadrv9009_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_setupRxAgc
andTALISE_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_getAgcPowerRegisters
provide 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_setTxAttenuation
andTALISE_getTxAttenuation
– Set orretrieve Tx attenuation levels by converting milli‑dB values into register settings.
TALISE_setTxAttenCtrlPin
andTALISE_getTxAttenCtrlPin
– ManageGPIO‑based controls for dynamic attenuation adjustments.
TALISE_setDacFullScale
– Program the Tx DAC’s full‑scale valuebefore the ARM firmware is loaded.
TALISE_txNcoShifterSet
andTALISE_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_programRxGainTable
andTALISE_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_setRxGainCtrlPin
andTALISE_getRxGainCtrlPin
– Allowexternal gain control via GPIO, and
TALISE_setGainTableExtCtrlPins
maps 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_getRxHd2Status
provide 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_v2
andTALISE_verifyArmChecksum
–Retrieve firmware version info and verify checksum integrity. Additional error handling functions such as
talGetArmErrorMessage
andtalArmCmdErrorHandler
ensure 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:
talSpiReadByte
andtalSpiWriteByte
– Handle single‑byte SPItransactions with built‑in timeout and retry logic.
talSpiReadBytes
andtalSpiWriteBytes
– Manage multi‑bytebuffer SPI transfers.
talSpiReadField
andtalSpiWriteField
– 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_setGpioOe
andTALISE_getGpioOe
– Set and read outputenable settings for low‑voltage GPIOs.
TALISE_setGpioSourceCtrl
andTALISE_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_setGpioMonitorOut
andTALISE_getGpioMonitorOut
– Routeinternal signals for external debugging.
TALISE_setGpIntMask
,TALISE_getGpIntMask
,TALISE_getGpIntStatus
, andTALISE_gpIntHandler
– Manage general purpose interrupts.
TALISE_setupAuxDacs
andTALISE_writeAuxDac
– Configure andupdate auxiliary DAC outputs.
TALISE_setAuxAdcPinModeGpio
,TALISE_getAuxAdcPinModeGpio
,TALISE_startAuxAdc
, andTALISE_readAuxAdc
– Set up and read from the auxiliary ADC.
TALISE_setSpi2Enable
andTALISE_getSpi2Enable
– Control thesecondary SPI interface used for Tx attenuation.
TALISE_getTemperature
– Retrieve on‑chip temperature sensor datavia the ARM processor, with
talGetGpioErrorMessage
providing 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_setRadioCtlPinMode
andTALISE_getRadioCtlPinMode
– Setand query radio control pin modes.
TALISE_setOrxLoCfg
andTALISE_getOrxLoCfg
– Configureobservation receiver LO settings.
TALISE_radioOn
,TALISE_radioOff
, andTALISE_getRadioState
– Control and monitor the overall radio power state.
TALISE_setRxTxEnable
andTALISE_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:
;