Configuring ADRV9009 for XXX.XX MHz Clock: A Step-by-Step Guide

Configuring ADRV9009 for XXX.XX MHz Clock: A Step-by-Step Guide

Configuring ADRV9009 for 245.76 MHz Clock: Step-by-Step Guide

Learn how to modify ADRV9009 reference code to run at 245.76 MHz device clock instead of the default 122.88 MHz. Covers custom talInit structure, FIR coefficients, header declarations, QPLL configuration, and main function changes.

we needed to verify high-bandwidth RF transceiver operation, requiring the ADRV9009 to be configured for a 245.76 MHz clock. After spending considerable time modifying the official reference code, I documented the process below.

1. Modify the Default Configuration Structure

The official SDK example code defaults to 122.88 MHz, which is determined by the talInit structure in talise_config.c. To make adjustments easier, we'll create a new structure talInit2 with 245.76 MHz parameters.

taliseInit_t talInit2 = {
    /* SPI settings */
    .spiSettings =
    {
        .MSBFirst            = 1,  /* 1 = MSBFirst, 0 = LSBFirst */
        .enSpiStreaming      = 0,  /* Not implemented in ADIs platform layer. SW feature to improve SPI throughput */
        .autoIncAddrUp       = 1,  /* Not implemented in ADIs platform layer. For SPI Streaming, set address increment direction. 1= next addr = addr+1, 0:addr=addr-1 */
        .fourWireMode        = 1,  /* 1: Use 4-wire SPI, 0: 3-wire SPI (SDIO pin is bidirectional). NOTE: ADI's FPGA platform always uses 4-wire mode */
        .cmosPadDrvStrength  = TAL_CMOSPAD_DRV_2X /* Drive strength of CMOS pads when used as outputs (SDIO, SDO, GP_INTERRUPT, GPIO 1, GPIO 0) */
    },

    /* Rx settings */
    .rx =
    {
        .rxProfile =
        {
            .rxFir =
            {
                .gain_dB = -6,                /* filter gain */
                .numFirCoefs = 48,            /* number of coefficients in the FIR filter */
                .coefs = &rxFirCoefs2[0]
            },
            .rxFirDecimation = 2,            /* Rx FIR decimation (1,2,4) */
            .rxDec5Decimation = 4,            /* Decimation of Dec5 or Dec4 filter (5,4) */
            .rhb1Decimation = 2,            /* RX Half band 1 decimation (1 or 2) */
            .rxOutputRate_kHz = 122880,            /* Rx IQ data rate in kHz */
            .rfBandwidth_Hz = 100000000,    /* The Rx RF passband bandwidth for the profile */
            .rxBbf3dBCorner_kHz = 100000,    /* Rx BBF 3dB corner in kHz */
            .rxAdcProfile = {265, 146, 181, 90, 1280, 366, 1257, 27, 1258, 17, 718, 39, 48, 46, 27, 161, 0, 0, 0, 0, 40, 0, 7, 6, 42, 0, 7, 6, 42, 0, 25, 27, 0, 0, 25, 27, 0, 0, 165, 44, 31, 905},            /* pointer to custom ADC profile */
            .rxDdcMode = TAL_RXDDC_BYPASS,   /* Rx DDC mode */
            .rxNcoShifterCfg =
            {
                .bandAInputBandWidth_kHz = 0,
                .bandAInputCenterFreq_kHz = 0,
                .bandANco1Freq_kHz = 0,
                .bandANco2Freq_kHz = 0,
                .bandBInputBandWidth_kHz = 0,
                .bandBInputCenterFreq_kHz = 0,
                .bandBNco1Freq_kHz = 0,
                .bandBNco2Freq_kHz = 0
            }
        },
        .framerSel = TAL_FRAMER_A,            /* Rx JESD204b framer configuration */
        .rxGainCtrl =
        {
            .gainMode = TAL_MGC,            /* taliserxGainMode_t gainMode */
            .rx1GainIndex = 255,            /* uint8_t rx1GainIndex */
            .rx2GainIndex = 255,            /* uint8_t rx2GainIndex */
            .rx1MaxGainIndex = 255,            /* uint8_t rx1MaxGainIndex */
            .rx1MinGainIndex = 195,            /* uint8_t rx1MinGainIndex */
            .rx2MaxGainIndex = 255,            /* uint8_t rx2MaxGainIndex */
            .rx2MinGainIndex = 195            /* uint8_t rx2MinGainIndex */
        },
        .rxChannels = TAL_RX1RX2,                /* The desired Rx Channels to enable during initialization */
    },

    /* Tx settings */
    .tx =
    {
        .txProfile =
        {
            .dacDiv = 1,                        /* The divider used to generate the DAC clock */
            .txFir =
            {
                .gain_dB = 6,                        /* filter gain */
                .numFirCoefs = 80,                    /* number of coefficients in the FIR filter */
                .coefs = &txFirCoefs2[0]
            },
            .txFirInterpolation = 2,                    /* The Tx digital FIR filter interpolation (1,2,4) */
            .thb1Interpolation = 2,                    /* Tx Halfband1 filter interpolation (1,2) */
            .thb2Interpolation = 2,                    /* Tx Halfband2 filter interpolation (1,2)*/
            .thb3Interpolation = 2,                    /* Tx Halfband3 filter interpolation (1,2) */
            .txInt5Interpolation = 1,                    /* Tx Int5 filter interpolation (1,5) */
            .txInputRate_kHz = 122880,                    /* Primary Signal BW */
            .primarySigBandwidth_Hz = 50000000,    /* The Rx RF passband bandwidth for the profile */
            .rfBandwidth_Hz = 100000000,            /* The Tx RF passband bandwidth for the profile */
            .txDac3dBCorner_kHz = 187000,                /* The DAC filter 3dB corner in kHz */
            .txBbf3dBCorner_kHz = 56000,                /* The BBF 3dB corner in kHz */
            .loopBackAdcProfile = {265, 146, 181, 90, 1280, 366, 1257, 27, 1258, 17, 718, 39, 48, 46, 27, 161, 0, 0, 0, 0, 40, 0, 7, 6, 42, 0, 7, 6, 42, 0, 25, 27, 0, 0, 25, 27, 0, 0, 165, 44, 31, 905}
        },
        .deframerSel = TAL_DEFRAMER_A,                    /* Talise JESD204b deframer config for the Tx data path */
        .txChannels = TAL_TX1TX2,                            /* The desired Tx channels to enable during initialization */
        .txAttenStepSize = TAL_TXATTEN_0P05_DB,            /* Tx Attenuation step size */
        .tx1Atten_mdB = 0,                            /* Initial Tx1 Attenuation */
        .tx2Atten_mdB = 0,                            /* Initial Tx2 Attenuation */
        .disTxDataIfPllUnlock = TAL_TXDIS_TX_RAMP_DOWN_TO_ZERO    /* Options to disable the transmit data when the RFPLL unlocks. */
    },

    /* ObsRx settings */
    .obsRx =
    {
        .orxProfile =
        {
            .rxFir =
            {
                .gain_dB = -6,                /* filter gain */
                .numFirCoefs = 48,            /* number of coefficients in the FIR filter */
                .coefs = &obsrxFirCoefs2[0]
            },
            .rxFirDecimation = 2,            /* Rx FIR decimation (1,2,4) */
            .rxDec5Decimation = 4,            /* Decimation of Dec5 or Dec4 filter (5,4) */
            .rhb1Decimation = 2,            /* RX Half band 1 decimation (1 or 2) */
            .orxOutputRate_kHz = 122880,            /* Rx IQ data rate in kHz */
            .rfBandwidth_Hz = 100000000,    /* The Rx RF passband bandwidth for the profile */
            .rxBbf3dBCorner_kHz = 225000,    /* Rx BBF 3dB corner in kHz */
            .orxLowPassAdcProfile = {265, 146, 181, 90, 1280, 366, 1257, 27, 1258, 17, 718, 39, 48, 46, 27, 161, 0, 0, 0, 0, 40, 0, 7, 6, 42, 0, 7, 6, 42, 0, 25, 27, 0, 0, 25, 27, 0, 0, 165, 44, 31, 905},
            .orxBandPassAdcProfile = {265, 146, 181, 90, 1280, 366, 1257, 27, 1258, 17, 718, 39, 48, 46, 27, 161, 0, 0, 0, 0, 40, 0, 7, 6, 42, 0, 7, 6, 42, 0, 25, 27, 0, 0, 25, 27, 0, 0, 165, 44, 31, 905},
            .orxDdcMode = TAL_ORXDDC_DISABLED,   /* ORx DDC mode */
            .orxMergeFilter  = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
        },
        .orxGainCtrl =
        {
            .gainMode = TAL_MGC,
            .orx1GainIndex = 255,
            .orx2GainIndex = 255,
            .orx1MaxGainIndex = 255,
            .orx1MinGainIndex = 195,
            .orx2MaxGainIndex = 255,
            .orx2MinGainIndex = 195
        },
        .framerSel = TAL_FRAMER_B,                /* ObsRx JESD204b framer configuration */
        .obsRxChannelsEnable = TAL_ORX1ORX2,        /* The desired ObsRx Channels to enable during initialization */
        .obsRxLoSource = TAL_OBSLO_RF_PLL                /* The ORx mixers can use the TX_PLL */
    },

    /* Digital Clock Settings */
    .clocks =
    {
        .deviceClock_kHz = 245760,            /* CLKPLL and device reference clock frequency in kHz */
        .clkPllVcoFreq_kHz = 9830400,        /* CLKPLL VCO frequency in kHz */
        .clkPllHsDiv = TAL_HSDIV_2P5,            /* CLKPLL high speed clock divider */
        .rfPllUseExternalLo = 0,                /* 1= Use external LO for RF PLL, 0 = use internal LO generation for RF PLL */
        .rfPllPhaseSyncMode = TAL_RFPLLMCS_NOSYNC                /* RFPLL MCS (Phase sync) mode */
    },

    /* JESD204B settings */
    .jesd204Settings =
    {
        /* Framer A settings */
        .framerA =
        {
            .bankId = 1,                    /* JESD204B Configuration Bank ID -extension to Device ID (Valid 0..15) */
            .deviceId = 0,                    /* JESD204B Configuration Device ID - link identification number. (Valid 0..255) */
            .lane0Id = 0,                    /* JESD204B Configuration starting Lane ID.  If more than one lane used, each lane will increment from the Lane0 ID. (Valid 0..31) */
            .M = 4,                            /* number of ADCs (0, 2, or 4) - 2 ADCs per receive chain */
            .K = 32,                        /* number of frames in a multiframe (default=32), F*K must be a multiple of 4. (F=2*M/numberOfLanes) */
            .F = 4,                            /* F (number of bytes per frame) */
            .Np = 16,                            /* Np (converter sample resolution) */
            .scramble = 1,                    /* scrambling off if framerScramble= 0, if framerScramble>0 scramble is enabled. */
            .externalSysref = 1,            /* 0=use internal SYSREF, 1= use external SYSREF */
            .serializerLanesEnabled = 0x03,    /* serializerLanesEnabled - bit per lane, [0] = Lane0 enabled, [1] = Lane1 enabled */
            .serializerLaneCrossbar = 0xE4,    /* serializerLaneCrossbar */
            .lmfcOffset = 31,                /* lmfcOffset - LMFC offset value for deterministic latency setting */
            .newSysrefOnRelink = 0,            /* newSysrefOnRelink */
            .syncbInSelect = 0,                /* syncbInSelect; */
            .overSample = 0,                    /* 1=overSample, 0=bitRepeat */
            .syncbInLvdsMode = 1,
            .syncbInLvdsPnInvert = 0,
            .enableManualLaneXbar = 0 /* 0=auto, 1=manual */
        },
        /* Framer B settings */
        .framerB =
        {
            .bankId = 0,                    /* JESD204B Configuration Bank ID -extension to Device ID (Valid 0..15) */
            .deviceId = 0,                    /* JESD204B Configuration Device ID - link identification number. (Valid 0..255) */
            .lane0Id = 0,                    /* JESD204B Configuration starting Lane ID.  If more than one lane used, each lane will increment from the Lane0 ID. (Valid 0..31) */
            .M = 2,                            /* number of ADCs (0, 2, or 4) - 2 ADCs per receive chain */
            .K = 32,                        /* number of frames in a multiframe (default=32), F*K must be a multiple of 4. (F=2*M/numberOfLanes) */
            .F = 2,                            /* F (number of bytes per frame) */
            .Np = 16,                            /* Np (converter sample resolution) */
            .scramble = 1,                    /* scrambling off if framerScramble= 0, if framerScramble>0 scramble is enabled. */
            .externalSysref = 1,            /* 0=use internal SYSREF, 1= use external SYSREF */
            .serializerLanesEnabled = 0x0C,    /* serializerLanesEnabled - bit per lane, [0] = Lane0 enabled, [1] = Lane1 enabled */
            .serializerLaneCrossbar = 0xE4,    /* serializerLaneCrossbar */
            .lmfcOffset = 31,                /* lmfcOffset - LMFC offset value for deterministic latency setting */
            .newSysrefOnRelink = 0,            /* newSysrefOnRelink */
            .syncbInSelect = 1,                /* syncbInSelect; */
            .overSample = 0,                    /* 1=overSample, 0=bitRepeat */
            .syncbInLvdsMode = 1,
            .syncbInLvdsPnInvert = 0,
            .enableManualLaneXbar = 0 /* 0=auto, 1=manual */
        },
        /* Deframer A settings */
        .deframerA =
        {
            .bankId = 0,                    /* bankId extension to Device ID (Valid 0..15) */
            .deviceId = 0,                    /* deviceId  link identification number. (Valid 0..255) */
            .lane0Id = 0,                    /* lane0Id Lane0 ID. (Valid 0..31) */
            .M = 4,                            /* M  number of DACss (0, 2, or 4) - 2 DACs per transmit chain */
            .K = 32,                        /* K  #frames in a multiframe (default=32), F*K=multiple of 4. (F=2*M/numberOfLanes) */
            .scramble = 1,                    /* scramble  scrambling off if scramble= 0 */
            .externalSysref = 1,            /* externalSysref  0= use internal SYSREF, 1= external SYSREF */
            .deserializerLanesEnabled = 0x0F,    /* deserializerLanesEnabled  bit per lane, [0] = Lane0 enabled */
            .deserializerLaneCrossbar = 0xE4,    /* deserializerLaneCrossbar */
            .lmfcOffset = 17,                /* lmfcOffset     LMFC offset value to adjust deterministic latency */
            .newSysrefOnRelink = 0,            /* newSysrefOnRelink */
            .syncbOutSelect = 0,                /* SYNCBOUT0/1 select */
            .Np = 16,                /* Np (converter sample resolution) */
            .syncbOutLvdsMode = 1,
            .syncbOutLvdsPnInvert = 0,
            .syncbOutCmosSlewRate = 0,
            .syncbOutCmosDriveLevel = 0,
            .enableManualLaneXbar = 0 /* 0=auto, 1=manual */
        },
        /* Deframer B settings */
        .deframerB =
        {
            .bankId = 0,                    /* bankId extension to Device ID (Valid 0..15) */
            .deviceId = 0,                    /* deviceId  link identification number. (Valid 0..255) */
            .lane0Id = 0,                    /* lane0Id Lane0 ID. (Valid 0..31) */
            .M = 0,                            /* M  number of DACss (0, 2, or 4) - 2 DACs per transmit chain */
            .K = 32,                        /* K  #frames in a multiframe (default=32), F*K=multiple of 4. (F=2*M/numberOfLanes) */
            .scramble = 1,                    /* scramble  scrambling off if scramble= 0 */
            .externalSysref = 1,            /* externalSysref  0= use internal SYSREF, 1= external SYSREF */
            .deserializerLanesEnabled = 0x00,    /* deserializerLanesEnabled  bit per lane, [0] = Lane0 enabled */
            .deserializerLaneCrossbar = 0xE4,    /* deserializerLaneCrossbar */
            .lmfcOffset = 0,                /* lmfcOffset     LMFC offset value to adjust deterministic latency */
            .newSysrefOnRelink = 0,            /* newSysrefOnRelink */
            .syncbOutSelect = 1,                /* SYNCBOUT0/1 select */
            .Np = 16,                /* Np (converter sample resolution) */
            .syncbOutLvdsMode = 1,
            .syncbOutLvdsPnInvert = 0,
            .syncbOutCmosSlewRate = 0,
            .syncbOutCmosDriveLevel = 0,
            .enableManualLaneXbar = 0 /* 0=auto, 1=manual */
        },
        .serAmplitude = 15,                    /* Serializer amplitude setting. Default = 15. Range is 0..15 */
        .serPreEmphasis = 1,                /* Serializer pre-emphasis setting. Default = 1 Range is 0..4 */
        .serInvertLanePolarity = 0,            /* Serializer Lane PN inversion select. Default = 0. Where, bit[0] = 1 will invert lane [0], bit[1] = 1 will invert lane 1, etc. */
        .desInvertLanePolarity = 0,            /* Deserializer Lane PN inversion select.  bit[0] = 1 Invert PN of Lane 0, bit[1] = Invert PN of Lane 1, etc */
        .desEqSetting = 1,                    /* Deserializer Equalizer setting. Applied to all deserializer lanes. Range is 0..4 */
        .sysrefLvdsMode = 1,                /* Use LVDS inputs on Talise for SYSREF */
        .sysrefLvdsPnInvert = 0              /*0= Do not PN invert SYSREF */
    }
};

//Only needs to be called if user wants to setup AGC parameters
static taliseAgcCfg_t rxAgcCtrl = {
    4,
    255,
    195,
    255,
    195,
    30720,  /* AGC gain update time in us (125us-250us - based on IQ data rate - set for 125us @ 245.76 Mhz) */
    10,
    10,
    16,
    0,
    1,
    0,
    0,
    0,
    1,
    31,
    246,
    4,
    1,          /*!<1- bit field to enable the multiple time constants in AGC loop for fast attack and fast recovery to max gain. */
    /* agcPower */
    {
        1,      /*!<1-bit field, enables the Rx power measurement block. */
        1,      /*!<1-bit field, allows using Rx PFIR for power measurement. */
        0,      /*!<1-bit field, allows to use the output of the second digital offset block in the Rx datapath for power measurement. */
        9,      /*!

2. Add 245 MHz Filter Coefficients

At the beginning of the configuration file, add the relevant 245 MHz filter parameters:

int16_t txFirCoefs[20] = {33, -77, 123, -158, 171, -112, -155, 1040, -3011, 20121, -3011, 1040, -155, -112, 171, -158, 123, -77, 33, 0};

int16_t rxFirCoefs[48] = {-7, -23, 33, 50, -70, -110, 144, 205, -259, -356, 437, 581, -698, -916, 1082, 1415, -1655, -2209, 2567, 3615, -4351, -7169, 9329, 31129, 31129, 9329, -7169, -4351, 3615, 2567, -2209, -1655, 1415, 1082, -916, -698, 581, 437, -356, -259, 205, 144, -110, -70, 50,  33, -23, -7};

int16_t obsrxFirCoefs[48] = {-7, -21, 31, 48, -67, -106, 124, 164, -275, -334, 440, 552, -694, -872, 1069, 1351, -1633, -2111, 2541, 3477, -4295, -6877, 9433, 30825, 30825, 9433, -6877, -4295, 3477, 2541, -2111, -1633, 1351, 1069, -872, -694, 552, 440, -334, -275, 164, 124, -106, -67, 48, 31, -21, 7};

3. Add External Declaration in talise_config.h

In the talise_config.h file, add an external reference declaration for the new structure:

/**
 * \file adrv9009/profiles/tx_bw100_ir122p88_rx_bw100_or122p88_orx_bw100_or122p88_dc122p88/talise_config.h
 * \brief Talise configuration external declarations
 *
 * \brief Contains structure definitions for tal_config.c
 *
 * Copyright 2015-2017 Analog Devices Inc.
 * Released under the AD9378-AD9379 API license, for more information see the "LICENSE.txt" file in this zip file.
 *
 * The top level structure taliseDevice_t talDevice uses keyword
 * extern to allow the application layer main() to have visibility
 * to these settings.
 */

#ifndef TAL_INIT_H_
#define TAL_INIT_H_

#ifdef __cplusplus
extern "C" {
#endif

extern taliseDevice_t talDevice;
extern taliseInit_t talInit;
extern taliseInit_t talInit2;

#ifdef __cplusplus
}
#endif

#endif

4. Modify Transceiver Configuration in app_transceiver.c

In the transfer configuration function, modify the transceiver (XCVR) type. The relevant structure is:

struct adxcvr_init rx_adxcvr_init = {
    "rx_adxcvr",
    RX_XCVR_BASEADDR,
    0,
    3,
    0,
    1,
    rx_lane_rate_khz,
    device_clock,
};

// Definition of the configuration structure
struct adxcvr_init {
    const char *name;
    uint32_t base;
    uint32_t sys_clk_sel;
    uint32_t out_clk_sel;
    bool cpll_enable;
    bool lpm_enable;
    uint32_t lane_rate_khz;
    uint32_t ref_rate_khz;
};
Why QPLL? Because this is a high-speed transceiver rate, CPLL is not suitable. We must use QPLL for frequency multiplication. The underlying source code for the configuration is shown below — changing cpll_enable to 0 enables QPLL operation.
if (xcvr->cpll_enable)
    ret = xilinx_xcvr_calc_cpll_config(&xcvr->xlx_xcvr, parent_rate, rate,
                       &cpll_conf, &out_div);
else
    ret = xilinx_xcvr_calc_qpll_config(&xcvr->xlx_xcvr, parent_rate, rate,
                       &qpll_conf, &out_div);

5. Modify the Main Function in headless.c

Finally, in the main function of headless.c, change the configuration structure reference from talInit to talInit2:

Note: The original article mentions "254.76 MHz" in the conclusion, but the correct target frequency based on the configuration parameters is 245.76 MHz. This has been corrected throughout this guide.

Quick Project Validation

If you want to shorten development cycles and accelerate project validation, explore our SDR product portfolio:



Previous post

Leave a comment