Post Go back to editing

Issues in getting RF output for NO-OS project

Category: Software

Hi,

I am working on HDL and NO-OS project. I have successfully built the project in two different environments: Vivado/SDK 2019.1 and Vivado/Vitis 2022.2.

The Problem:
Upon launching the code on the hardware, the initialization sequence appears successful. The Vitis/SDK serial terminal shows the expected prints and initialization logs. However, when monitoring the output via a Signal Analyzer at the desired carrier frequency, there is no signal detected (only LO leakage is seen at the carrier frequency).

Hardware Setup:

  • Carrier Board: [ZCU102]

  • RF Transceiver: [ADRV9002]

Observations:

  1. Software: The No-OS state machine completes without returning error codes.

  2. RF Output: No signal at the SMA connectors. I have verified the cables and the signal analyzer settings.

    I have attached the headless main.c files for both versions and the corresponding Vitis terminal logs. I am concerned that I may be missing a critical function call required to transition the signal from the 'Ready' state to Transmitting. Kindly look into it.

    /***************************************************************************//**
     *   @file   headless.c
     *   @brief  adrv9002 main project file.
     *   @author Darius Berghe (darius.berghe@analog.com)
    ********************************************************************************
     * Copyright 2020(c) Analog Devices, Inc.
     *
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions are met:
     *  - Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     *  - Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in
     *    the documentation and/or other materials provided with the
     *    distribution.
     *  - Neither the name of Analog Devices, Inc. nor the names of its
     *    contributors may be used to endorse or promote products derived
     *    from this software without specific prior written permission.
     *  - The use of this software may or may not infringe the patent rights
     *    of one or more patent holders.  This license does not release you
     *    from the requirement that you obtain separate licenses from these
     *    patent holders to use this software.
     *  - Use of the software either in source or binary form, must be run
     *    on or directly connected to an Analog Devices Inc. component.
     *
     * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR
     * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT,
     * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT,
     * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR
     * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
     * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    *******************************************************************************/
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #include "xil_cache.h"
    
    #include "error.h"
    #include "util.h"
    #include "spi.h"
    
    #include "axi_adc_core.h"
    #include "axi_dac_core.h"
    #include "axi_dmac.h"
    
    #include "parameters.h"
    
    #ifdef IIO_SUPPORT
    #include "app_iio.h"
    #include "xil_cache.h"
    #endif
    
    #include "adrv9002.h"
    #include "adi_adrv9001.h"
    #include "adi_adrv9001_arm.h"
    #include "adi_adrv9001_radio.h"
    #include "adi_adrv9001_tx.h"
    
    int get_sampling_frequency(struct axi_adc *dev, uint32_t chan,
    			   uint64_t *sampling_freq_hz)
    {
    	if (!dev || !sampling_freq_hz)
    		return -EINVAL;
    
    	*sampling_freq_hz =
    		adrv9002_init_get()->rx.rxChannelCfg[chan].profile.rxOutputRate_Hz;
    	return SUCCESS;
    }
    
    int main(void)
    {
    	int ret;
    	struct adi_common_ApiVersion api_version;
    	struct adi_adrv9001_ArmVersion arm_version;
    	struct adi_adrv9001_SiliconVersion silicon_version;
    	struct adrv9002_rf_phy phy;
    
    	struct axi_adc_init rx1_adc_init = {
    		"axi-adrv9002-rx-lpc",
    		RX1_ADC_BASEADDR,
    #ifndef ADRV9002_RX2TX2
    		ADRV9001_NUM_SUBCHANNELS,
    #else
    		ADRV9001_NUM_CHANNELS,
    #endif
    	};
    
    	struct axi_dac_channel  tx1_dac_channels[2];
    	tx1_dac_channels[0].sel = AXI_DAC_DATA_SEL_DMA;
    	tx1_dac_channels[1].sel = AXI_DAC_DATA_SEL_DMA;
    
    	struct axi_dac_init tx1_dac_init = {
    		"axi-adrv9002-tx-lpc",
    		TX1_DAC_BASEADDR,
    #ifndef ADRV9002_RX2TX2
    		ADRV9001_NUM_SUBCHANNELS,
    #else
    		ADRV9001_NUM_CHANNELS,
    #endif
    		tx1_dac_channels,
    	};
    
    #ifndef ADRV9002_RX2TX2
    	struct axi_adc_init rx2_adc_init = {
    		"axi-adrv9002-rx2-lpc",
    		RX2_ADC_BASEADDR,
    		ADRV9001_NUM_SUBCHANNELS,
    	};
    
    	struct axi_dac_channel  tx2_dac_channels[2];
    	tx2_dac_channels[0].sel = AXI_DAC_DATA_SEL_DMA;
    	tx2_dac_channels[1].sel = AXI_DAC_DATA_SEL_DMA;
    
    	struct axi_dac_init tx2_dac_init = {
    		"axi-adrv9002-tx2-lpc",
    		TX2_DAC_BASEADDR,
    		ADRV9001_NUM_SUBCHANNELS,
    		tx2_dac_channels,
    	};
    #endif
    	struct axi_dmac_init rx1_dmac_init = {
    		"rx_dmac",
    		RX1_DMA_BASEADDR,
    		DMA_DEV_TO_MEM,
    		0
    	};
    
    	struct axi_dmac_init tx1_dmac_init = {
    		"tx_dmac",
    		TX1_DMA_BASEADDR,
    		DMA_MEM_TO_DEV,
    		DMA_CYCLIC,
    	};
    
    #ifndef ADRV9002_RX2TX2
    	struct axi_dmac_init rx2_dmac_init = {
    		"rx_dmac",
    		RX2_DMA_BASEADDR,
    		DMA_DEV_TO_MEM,
    		0
    	};
    
    	struct axi_dmac_init tx2_dmac_init = {
    		"tx_dmac",
    		TX2_DMA_BASEADDR,
    		DMA_MEM_TO_DEV,
    		DMA_CYCLIC,
    	};
    #endif
    
    	Xil_ICacheEnable();
    	Xil_DCacheEnable();
    
    	printf("Hello\n");
    
    	memset(&phy, 0, sizeof(struct adrv9002_rf_phy));
    
    #if defined(ADRV9002_RX2TX2)
    	phy.rx2tx2 = true;
    #endif
    
    	ret = adrv9002_setup(&phy, adrv9002_init_get());
    	if (ret)
    		return ret;
    
    	adi_adrv9001_ApiVersion_Get(phy.adrv9001, &api_version);
    	adi_adrv9001_arm_Version(phy.adrv9001, &arm_version);
    	adi_adrv9001_SiliconVersion_Get(phy.adrv9001, &silicon_version);
    
    	printf("%s Rev %d.%d, Firmware %u.%u.%u.%u API version: %u.%u.%u successfully initialized\n",
    	       "ADRV9002", silicon_version.major, silicon_version.minor,
    	       arm_version.majorVer, arm_version.minorVer,
    	       arm_version.maintVer, arm_version.rcVer, api_version.major,
    	       api_version.minor, api_version.patch);
    
    	/* Initialize the ADC/DAC cores */
    	ret = axi_adc_init(&phy.rx1_adc, &rx1_adc_init);
    	if (ret) {
    		printf("axi_adc_init() failed with status %d\n", ret);
    		goto error;
    	}
    
    	ret = axi_dac_init(&phy.tx1_dac, &tx1_dac_init);
    	if (ret) {
    		printf("axi_dac_init() failed with status %d\n", ret);
    		goto error;
    	}
    	phy.tx1_dac->clock_hz = adrv9002_init_get()->tx.txProfile[0].txInputRate_Hz;
    #ifndef ADRV9002_RX2TX2
    	ret = axi_adc_init(&phy.rx2_adc, &rx2_adc_init);
    	if (ret) {
    		printf("axi_adc_init() failed with status %d\n", ret);
    		goto error;
    	}
    
    	ret = axi_dac_init(&phy.tx2_dac, &tx2_dac_init);
    	if (ret) {
    		printf("axi_dac_init() failed with status %d\n", ret);
    		goto error;
    	}
    	phy.tx2_dac->clock_hz = adrv9002_init_get()->tx.txProfile[1].txInputRate_Hz;
    #endif
    
    	/* Post AXI DAC/ADC setup, digital interface tuning */
    	ret = adrv9002_post_setup(&phy);
    	if (ret) {
    		printf("adrv9002_post_setup() failed with status %d\n", ret);
    		goto error;
    	}
    
    	/* TODO: Remove this when it gets fixed in the API. */
    	adi_adrv9001_Radio_Channel_ToState(phy.adrv9001, ADI_RX,
    					   ADI_CHANNEL_1, ADI_ADRV9001_CHANNEL_PRIMED);
    	adi_adrv9001_Radio_Channel_ToState(phy.adrv9001, ADI_RX,
    					   ADI_CHANNEL_1, ADI_ADRV9001_CHANNEL_RF_ENABLED);
    	adi_adrv9001_Radio_Channel_ToState(phy.adrv9001, ADI_RX,
    					   ADI_CHANNEL_2, ADI_ADRV9001_CHANNEL_PRIMED);
    	adi_adrv9001_Radio_Channel_ToState(phy.adrv9001, ADI_RX,
    					   ADI_CHANNEL_2, ADI_ADRV9001_CHANNEL_RF_ENABLED);
    
    	/* Initialize the AXI DMA Controller cores */
    	ret = axi_dmac_init(&phy.tx1_dmac, &tx1_dmac_init);
    	if (ret) {
    		printf("axi_dmac_init() failed with status %d\n", ret);
    		goto error;
    	}
    
    	ret = axi_dmac_init(&phy.rx1_dmac, &rx1_dmac_init);
    	if (ret) {
    		printf("axi_dmac_init() failed with status %d\n", ret);
    		goto error;
    	}
    #ifndef ADRV9002_RX2TX2
    	ret = axi_dmac_init(&phy.tx2_dmac, &tx2_dmac_init);
    	if (ret) {
    		printf("axi_dmac_init() failed with status %d\n", ret);
    		goto error;
    	}
    
    	ret = axi_dmac_init(&phy.rx2_dmac, &rx2_dmac_init);
    	if (ret) {
    		printf("axi_dmac_init() failed with status %d\n", ret);
    		goto error;
    	}
    #endif
    
    	//adi_adrv9001_Radio_Channel_ToRfEnabled(&phy.adrv9001, 1, 0x2);
    	//adi_adrv9001_Tx_AttenuationMode_Set(&phy.adrv9001,0x1, 1);
    	//adi_adrv9001_Tx_Attenuation_Set(&phy.adrv9001, 0x1, 20000 );
    
    
    #ifdef DAC_DMA_EXAMPLE
    	axi_dac_load_custom_data(phy.tx1_dac, sine_lut_iq,
    				 ARRAY_SIZE(sine_lut_iq),
    				 DAC1_DDR_BASEADDR);
    #ifndef ADRV9002_RX2TX2
    	axi_dac_load_custom_data(phy.tx2_dac, sine_lut_iq,
    				 ARRAY_SIZE(sine_lut_iq),
    				 DAC2_DDR_BASEADDR);
    #endif
    	Xil_DCacheFlush();
    
    	axi_dmac_transfer(phy.tx1_dmac, DAC1_DDR_BASEADDR, sizeof(sine_lut_iq));
    #ifndef ADRV9002_RX2TX2
    	axi_dmac_transfer(phy.tx2_dmac, DAC2_DDR_BASEADDR, sizeof(sine_lut_iq));
    #endif
    
    	mdelay(1000);
    
    	/* Transfer 16384 samples from ADC to MEM */
    	axi_dmac_transfer(phy.rx1_dmac,
    			  ADC1_DDR_BASEADDR,
    			  16384 * /* nr of samples */
    #ifndef ADRV9002_RX2TX2
    			  ADRV9001_NUM_SUBCHANNELS * /* rx1 i/q */
    #else
    			  ADRV9001_NUM_CHANNELS * /* rx1 i/q, rx2 i/q*/
    #endif
    			  2 /* bytes per sample */);
    	Xil_DCacheInvalidateRange(ADC1_DDR_BASEADDR,
    				  16384 * /* nr of samples */
    #ifndef ADRV9002_RX2TX2
    				  ADRV9001_NUM_SUBCHANNELS * /* rx1 i/q */
    #else
    				  ADRV9001_NUM_CHANNELS * /* rx1 i/q, rx2 i/q*/
    #endif
    				  2 /* bytes per sample */);
    #ifndef ADRV9002_RX2TX2
    	axi_dmac_transfer(phy.rx2_dmac,
    			  ADC2_DDR_BASEADDR,
    			  16384 * /* nr of samples */
    			  ADRV9001_NUM_SUBCHANNELS * /* nr of channels */
    			  2 /* bytes per sample */);
    	Xil_DCacheInvalidateRange(ADC2_DDR_BASEADDR,
    				  16384 * /* nr of samples */
    				  ADRV9001_NUM_SUBCHANNELS * /* nr of channels */
    				  2 /* bytes per sample */);
    #endif
    #endif
    
    #ifdef IIO_SUPPORT
    	printf("The board accepts libiio clients connections through the serial backend.\n");
    
    	struct iio_axi_adc_init_param iio_axi_adc1_init_par = {
    		.rx_adc = phy.rx1_adc,
    		.rx_dmac = phy.rx1_dmac,
    		.dcache_invalidate_range = (void (*)(uint32_t, uint32_t))Xil_DCacheInvalidateRange,
    		.get_sampling_frequency = get_sampling_frequency,
    	};
    
    	struct iio_axi_dac_init_param iio_axi_dac1_init_par = {
    		.tx_dac = phy.tx1_dac,
    		.tx_dmac = phy.tx1_dmac,
    		.dcache_flush_range = (void (*)(uint32_t, uint32_t))Xil_DCacheFlushRange,
    	};
    #ifndef ADRV9002_RX2TX2
    	struct iio_axi_adc_init_param iio_axi_adc2_init_par = {
    		.rx_adc = phy.rx2_adc,
    		.rx_dmac = phy.rx2_dmac,
    		.dcache_invalidate_range = (void (*)(uint32_t, uint32_t))Xil_DCacheInvalidateRange,
    		.get_sampling_frequency = get_sampling_frequency,
    	};
    
    	struct iio_axi_dac_init_param iio_axi_dac2_init_par = {
    		.tx_dac = phy.tx2_dac,
    		.tx_dmac = phy.tx2_dmac,
    		.dcache_flush_range = (void (*)(uint32_t, uint32_t))Xil_DCacheFlushRange,
    	};
    	ret = iio_server_init(&iio_axi_adc1_init_par,
    			      &iio_axi_adc2_init_par,
    			      &iio_axi_dac1_init_par,
    			      &iio_axi_dac2_init_par);
    #else
    	ret = iio_server_init(&iio_axi_adc1_init_par,
    			      NULL,
    			      &iio_axi_dac1_init_par,
    			      NULL);
    #endif
    #endif
    	printf("Bye\n");
    
    
    
    
    
    error:
    	adi_adrv9001_HwClose(phy.adrv9001);
    	axi_adc_remove(phy.rx1_adc);
    	axi_dac_remove(phy.tx1_dac);
    	axi_adc_remove(phy.rx2_adc);
    	axi_dac_remove(phy.tx2_dac);
    	axi_dmac_remove(phy.rx1_dmac);
    	axi_dmac_remove(phy.tx1_dmac);
    	axi_dmac_remove(phy.rx2_dmac);
    	axi_dmac_remove(phy.tx2_dmac);
    	return ret;
    }
    


    /***************************************************************************//**
     *   @file   headless.c
     *   @brief  adrv9002 main project file.
     *   @author Darius Berghe (darius.berghe@analog.com)
    ********************************************************************************
     * Copyright 2020(c) Analog Devices, Inc.
     *
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions are met:
     *  - Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     *  - Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in
     *    the documentation and/or other materials provided with the
     *    distribution.
     *  - Neither the name of Analog Devices, Inc. nor the names of its
     *    contributors may be used to endorse or promote products derived
     *    from this software without specific prior written permission.
     *  - The use of this software may or may not infringe the patent rights
     *    of one or more patent holders.  This license does not release you
     *    from the requirement that you obtain separate licenses from these
     *    patent holders to use this software.
     *  - Use of the software either in source or binary form, must be run
     *    on or directly connected to an Analog Devices Inc. component.
     *
     * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR
     * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT,
     * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT,
     * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR
     * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
     * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    *******************************************************************************/
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <inttypes.h>
    #include <stdint.h>
    
    #ifdef XILINX_PLATFORM
    #include "xil_cache.h"
    #endif /* XILINX_PLATFORM */
    
    #include "no_os_error.h"
    #include "no_os_util.h"
    #include "no_os_spi.h"
    
    #include "axi_adc_core.h"
    #include "axi_dac_core.h"
    #include "axi_dmac.h"
    
    #include "parameters.h"
    
    #ifdef IIO_SUPPORT
    #include "iio_app.h"
    #include "iio_axi_adc.h"
    #include "iio_axi_dac.h"
    #include "xilinx_uart.h"
    #endif
    
    #include "adrv9002.h"
    #include "adi_adrv9001.h"
    #include "adi_adrv9001_arm.h"
    #include "adi_adrv9001_radio.h"
    #include "adi_adrv9001_profileutil.h"
    //#include "Navassa_CMOS_profile.h"
    #include "Navassa_LVDS_profile.h"
    
    /* ADC/DAC Buffers */
    #if defined(DMA_EXAMPLE) || defined(IIO_SUPPORT)
    static uint32_t dac_buffers[IIO_DEV_COUNT][DAC_BUFFER_SAMPLES]
    __attribute__((aligned));
    static uint16_t adc_buffers[IIO_DEV_COUNT][ADC_BUFFER_SAMPLES]
    __attribute__((aligned));
    #endif
    
    uint64_t sampling_freq;
    
    int get_sampling_frequency(struct axi_adc *dev, uint32_t chan,
    			   uint64_t *sampling_freq_hz)
    {
    	if (!dev || !sampling_freq_hz)
    		return -EINVAL;
    
    	*sampling_freq_hz = sampling_freq;
    	return 0;
    }
    
    static struct adi_adrv9001_SpiSettings spiSettings = {
    	.msbFirst = 1,
    	.enSpiStreaming = 0,
    	.autoIncAddrUp = 1,
    	.fourWireMode = 1,
    	.cmosPadDrvStrength = ADI_ADRV9001_CMOSPAD_DRV_STRONG,
    };
    
    struct adi_adrv9001_SpiSettings *adrv9002_spi_settings_get(void)
    {
    	return &spiSettings;
    }
    
    enum adi_adrv9001_SsiType adrv9002_ssi_type_detect(struct adrv9002_rf_phy *phy)
    {
    	enum adi_adrv9001_SsiType ssi, ssi2;
    	char *ssi_str[3] = {
    		"[SSI Disabled]",
    		"CMOS",
    		"LVDS"
    	};
    
    	ssi = adrv9002_axi_ssi_type_get(phy);
    
    	ssi2 = phy->curr_profile->rx.rxChannelCfg[0].profile.rxSsiConfig.ssiType;
    	if (ssi != ssi2) {
    		printf("SSI mismatch: detected %s in HDL and %s in profile.\n", ssi_str[ssi],
    		       ssi_str[ssi2]);
    		return ADI_ADRV9001_SSI_TYPE_DISABLE;
    	}
    
    	return ssi;
    }
    
    static struct adi_adrv9001_GainControlCfg agc_defaults = {
    	.peakWaitTime = 4,
    	.maxGainIndex = ADI_ADRV9001_RX_GAIN_INDEX_MAX,
    	.minGainIndex = ADI_ADRV9001_RX_GAIN_INDEX_MIN,
    	.gainUpdateCounter = 11520,
    	.attackDelay_us = 10,
    	.lowThreshPreventGainInc = false,
    	.slowLoopSettlingDelay = 16,
    	.changeGainIfThreshHigh = 3,
    	.agcMode = 1,
    	.resetOnRxon = false,
    	.resetOnRxonGainIndex = ADI_ADRV9001_RX_GAIN_INDEX_MAX,
    	.enableSyncPulseForGainCounter = false,
    	.enableFastRecoveryLoop = false,
    	.power = {
    		.powerEnableMeasurement = true,
    		.underRangeHighPowerThresh = 10,
    		.underRangeLowPowerThresh = 4,
    		.underRangeHighPowerGainStepRecovery = 2,
    		.underRangeLowPowerGainStepRecovery = 4,
    		.powerMeasurementDuration = 10,
    		.powerMeasurementDelay = 2,
    		.rxTddPowerMeasDuration = 0,
    		.rxTddPowerMeasDelay = 0,
    		.overRangeHighPowerThresh = 0,
    		.overRangeLowPowerThresh = 7,
    		.overRangeHighPowerGainStepAttack = 4,
    		.overRangeLowPowerGainStepAttack = 4,
    		.feedback_inner_high_inner_low = ADI_ADRV9001_GPIO_PIN_CRUMB_UNASSIGNED,
    		.feedback_apd_high_apd_low = ADI_ADRV9001_GPIO_PIN_CRUMB_UNASSIGNED,
    	},
    	.peak = {
    		.agcUnderRangeLowInterval = 50,
    		.agcUnderRangeMidInterval = 2,
    		.agcUnderRangeHighInterval = 4,
    		.apdHighThresh = 21,
    		.apdLowThresh = 12,
    		.apdUpperThreshPeakExceededCount = 6,
    		.apdLowerThreshPeakExceededCount = 3,
    		.apdGainStepAttack = 2,
    		.apdGainStepRecovery = 0,
    		.enableHbOverload = true,
    		.hbOverloadDurationCount = 1,
    		.hbOverloadThreshCount = 1,
    		.hbHighThresh = 13044,
    		.hbUnderRangeLowThresh = 5826,
    		.hbUnderRangeMidThresh = 8230,
    		.hbUnderRangeHighThresh = 7335,
    		.hbUpperThreshPeakExceededCount = 6,
    		.hbUnderRangeHighThreshExceededCount = 3,
    		.hbGainStepHighRecovery = 2,
    		.hbGainStepLowRecovery = 6,
    		.hbGainStepMidRecovery = 4,
    		.hbGainStepAttack = 2,
    		.hbOverloadPowerMode = 0,
    		.hbUnderRangeMidThreshExceededCount = 3,
    		.hbUnderRangeLowThreshExceededCount = 3,
    		.feedback_apd_low_hb_low = ADI_ADRV9001_GPIO_PIN_CRUMB_UNASSIGNED,
    		.feedback_apd_high_hb_high = ADI_ADRV9001_GPIO_PIN_CRUMB_UNASSIGNED,
    	},
    };
    
    #ifdef IIO_SUPPORT
    
    static int32_t iio_run(struct iio_axi_adc_init_param *adc_pars,
    		       struct iio_axi_dac_init_param *dac_pars)
    {
    	struct iio_axi_adc_desc *adcs[IIO_DEV_COUNT];
    	struct iio_axi_dac_desc *dacs[IIO_DEV_COUNT];
    	struct iio_data_buffer iio_dac_buffers[IIO_DEV_COUNT];
    	struct iio_data_buffer iio_adc_buffers[IIO_DEV_COUNT];
    	struct iio_device *iio_descs[IIO_DEV_COUNT * 2];
    	struct iio_app_device app_devices[IIO_DEV_COUNT * 2] = {0};
    	struct xil_uart_init_param platform_uart_init_par = {
    		.type = UART_PS,
    		.irq_id = UART_IRQ_ID
    	};
    
    	struct no_os_uart_init_param iio_uart_ip = {
    		.device_id = UART_DEVICE_ID,
    		.irq_id = UART_IRQ_ID,
    		.baud_rate = UART_BAUDRATE,
    		.size = NO_OS_UART_CS_8,
    		.parity = NO_OS_UART_PAR_NO,
    		.stop = NO_OS_UART_STOP_1_BIT,
    		.extra = &platform_uart_init_par,
    		.platform_ops = &xil_uart_ops
    	};
    
    	struct iio_app_desc *app;
    	struct iio_app_init_param app_init_param = { 0 };
    	int32_t i, ret;
    	int32_t a; // linear iterator for iio_descs and app_devices flat arrays
    
    	for (i = 0; i < IIO_DEV_COUNT; i++) {
    		/* ADC setup */
    		iio_adc_buffers[i].buff = adc_buffers[i];
    		iio_adc_buffers[i].size = sizeof(adc_buffers[i]);
    		ret = iio_axi_adc_init(&adcs[i], &adc_pars[i]);
    		if (ret < 0)
    			return ret;
    		a = 2 * i;
    		iio_axi_adc_get_dev_descriptor(adcs[i], &iio_descs[a]);
    		app_devices[a].name = adc_pars[i].rx_adc->name;
    		app_devices[a].dev = adcs[i];
    		app_devices[a].dev_descriptor = iio_descs[a];
    		app_devices[a].read_buff = &iio_adc_buffers[i];
    
    		/* DAC setup */
    		iio_dac_buffers[i].buff = dac_buffers[i];
    		iio_dac_buffers[i].size = sizeof(dac_buffers[i]);
    		ret = iio_axi_dac_init(&dacs[i], &dac_pars[i]);
    		if (ret < 0)
    			return ret;
    		a = 2 * i + 1;
    		iio_axi_dac_get_dev_descriptor(dacs[i], &iio_descs[a]);
    		app_devices[a].name = dac_pars[i].tx_dac->name;
    		app_devices[a].dev = dacs[i];
    		app_devices[a].dev_descriptor = iio_descs[a];
    		app_devices[a].write_buff = &iio_dac_buffers[i];
    	}
    
    	app_init_param.devices = app_devices;
    	app_init_param.nb_devices = NO_OS_ARRAY_SIZE(app_devices);
    	app_init_param.uart_init_params = iio_uart_ip;
    
    	ret = iio_app_init(&app, app_init_param);
    	if (ret)
    		return ret;
    
    	return iio_app_run(app);
    }
    #endif
    
    int main(void)
    {
    	int ret;
    	struct adi_common_ApiVersion api_version;
    	struct adi_adrv9001_ArmVersion arm_version;
    	struct adi_adrv9001_SiliconVersion silicon_version;
    	struct adi_adrv9001_Device adrv9001_device = {0};
    	struct adrv9002_chip_info chip = {0};
    	struct adrv9002_rf_phy phy = {0};
    	unsigned int c;
    
    	struct axi_adc_init rx1_adc_init = {
    		.name = "axi-adrv9002-rx-lpc",
    		.base = RX1_ADC_BASEADDR,
    		.num_channels = ADRV9001_I_Q_CHANNELS,
    	};
    
    	struct axi_dac_channel  tx1_dac_channels[2];
    	tx1_dac_channels[0].sel = AXI_DAC_DATA_SEL_DDS ;// AXI_DAC_DATA_SEL_DMA;
    	tx1_dac_channels[1].sel = AXI_DAC_DATA_SEL_DDS;
    
    	struct axi_dac_init tx1_dac_init = {
    		.name = "axi-adrv9002-tx-lpc",
    		.base = TX1_DAC_BASEADDR,
    		.num_channels = ADRV9001_I_Q_CHANNELS,
    		.channels = tx1_dac_channels,
    		.rate = 3
    	};
    
    #ifndef ADRV9002_RX2TX2
    	struct axi_adc_init rx2_adc_init = {
    		.name = "axi-adrv9002-rx2-lpc",
    		.base = RX2_ADC_BASEADDR,
    		.num_channels = ADRV9001_I_Q_CHANNELS,
    	};
    
    	struct axi_dac_channel  tx2_dac_channels[2];
    	tx2_dac_channels[0].sel = AXI_DAC_DATA_SEL_DMA;
    	tx2_dac_channels[1].sel = AXI_DAC_DATA_SEL_DMA;
    
    	struct axi_dac_init tx2_dac_init = {
    		.name = "axi-adrv9002-tx2-lpc",
    		.base = TX2_DAC_BASEADDR,
    		.num_channels = ADRV9001_I_Q_CHANNELS,
    		.channels = tx2_dac_channels,
    		.rate = 3
    	};
    #endif
    	struct axi_dmac_init rx1_dmac_init = {
    		"rx_dmac",
    		RX1_DMA_BASEADDR,
    		IRQ_DISABLED
    	};
    
    	struct axi_dmac_init tx1_dmac_init = {
    		"tx_dmac",
    		TX1_DMA_BASEADDR,
    		IRQ_DISABLED
    	};
    
    #ifndef ADRV9002_RX2TX2
    	struct axi_dmac_init rx2_dmac_init = {
    		"rx_dmac",
    		RX2_DMA_BASEADDR,
    		IRQ_DISABLED
    	};
    
    	struct axi_dmac_init tx2_dmac_init = {
    		"tx_dmac",
    		TX2_DMA_BASEADDR,
    		IRQ_DISABLED
    	};
    #endif
    
    #ifdef XILINX_PLATFORM
    	Xil_ICacheEnable();
    	Xil_DCacheEnable();
    #endif /* XILINX_PLATFORM */
    
    	printf("Hello\n");
    
    #if defined(ADRV9002_RX2TX2)
    	phy.rx2tx2 = true;
    #endif
    
    	phy.adrv9001 = &adrv9001_device;
    
    	/* ADRV9002 */
    	chip.cmos_profile = "Navassa_CMOS_profile.json";
    	chip.lvd_profile = "Navassa_LVDS_profile.json";
    	chip.name = "adrv9002-phy";
    	chip.n_tx = ADRV9002_CHANN_MAX;
    
    	phy.chip = &chip;
    
    	ret = adi_adrv9001_profileutil_Parse(phy.adrv9001, &phy.profile,
    					     (char *)json_profile, strlen(json_profile));
    	if (ret)
    		goto error;
    
    	phy.curr_profile = &phy.profile;
    
    	sampling_freq = phy.curr_profile->rx.rxChannelCfg[0].profile.rxOutputRate_Hz;
    	printf("%" PRIu64 "\n", sampling_freq);
    
    	/* Initialize the ADC/DAC cores */
    	ret = axi_adc_init_begin(&phy.rx1_adc, &rx1_adc_init);
    	if (ret) {
    		printf("axi_adc_init_begin() failed with status %d\n", ret);
    		goto error;
    	}
    
    	ret = axi_dac_init_begin(&phy.tx1_dac, &tx1_dac_init);
    	if (ret) {
    		printf("axi_dac_init_begin() failed with status %d\n", ret);
    		goto error;
    	}
    #ifndef ADRV9002_RX2TX2
    	ret = axi_adc_init_begin(&phy.rx2_adc, &rx2_adc_init);
    	if (ret) {
    		printf("axi_adc_init_begin() failed with status %d\n", ret);
    		goto error;
    	}
    
    	ret = axi_dac_init_begin(&phy.tx2_dac, &tx2_dac_init);
    	if (ret) {
    		printf("axi_dac_init_begin() failed with status %d\n", ret);
    		goto error;
    	}
    #endif
    
    	phy.ssi_type = adrv9002_ssi_type_detect(&phy);
    	if (phy.ssi_type == ADI_ADRV9001_SSI_TYPE_DISABLE)
    		goto error;
    
    	/* Initialize AGC */
    	for (c = 0; c < ADRV9002_CHANN_MAX; c++) {
    		phy.rx_channels[c].agc = agc_defaults;
    	}
    
    	ret = adrv9002_setup(&phy);
    	if (ret)
    		return ret;
    
    	adi_adrv9001_ApiVersion_Get(phy.adrv9001, &api_version);
    	adi_adrv9001_arm_Version(phy.adrv9001, &arm_version);
    	adi_adrv9001_SiliconVersion_Get(phy.adrv9001, &silicon_version);
    
    	printf("%s Rev %d.%d, Firmware %u.%u.%u.%u API version: %u.%u.%u successfully initialized\n",
    	       "ADRV9002", silicon_version.major, silicon_version.minor,
    	       arm_version.majorVer, arm_version.minorVer,
    	       arm_version.maintVer, arm_version.rcVer, api_version.major,
    	       api_version.minor, api_version.patch);
    
    	/* Post AXI DAC/ADC setup, digital interface tuning */
    	ret = adrv9002_post_setup(&phy);
    	if (ret) {
    		printf("adrv9002_post_setup() failed with status %d\n", ret);
    		goto error;
    	}
    
    	/* Finalize the ADC/DAC cores initialization */
    	ret = axi_adc_init_finish(phy.rx1_adc);
    	if (ret) {
    		printf("axi_adc_init_finish() failed with status %d\n", ret);
    		goto error;
    	}
    
    	ret = axi_dac_init_finish(phy.tx1_dac);
    	if (ret) {
    		printf("axi_dac_init_finish() failed with status %d\n", ret);
    		goto error;
    	}
    	phy.tx1_dac->clock_hz = phy.curr_profile->tx.txProfile[0].txInputRate_Hz;
    #ifndef ADRV9002_RX2TX2
    	ret = axi_adc_init_finish(phy.rx2_adc);
    	if (ret) {
    		printf("axi_adc_init_finish() failed with status %d\n", ret);
    		goto error;
    	}
    
    	ret = axi_dac_init_finish(phy.tx2_dac);
    	if (ret) {
    		printf("axi_dac_init_finish() failed with status %d\n", ret);
    		goto error;
    	}
    	phy.tx2_dac->clock_hz = phy.curr_profile->tx.txProfile[1].txInputRate_Hz;
    #endif
    
    	/* Initialize the AXI DMA Controller cores */
    	ret = axi_dmac_init(&phy.tx1_dmac, &tx1_dmac_init);
    	if (ret) {
    		printf("axi_dmac_init() failed with status %d\n", ret);
    		goto error;
    	}
    
    	ret = axi_dmac_init(&phy.rx1_dmac, &rx1_dmac_init);
    	if (ret) {
    		printf("axi_dmac_init() failed with status %d\n", ret);
    		goto error;
    	}
    #ifndef ADRV9002_RX2TX2
    	ret = axi_dmac_init(&phy.tx2_dmac, &tx2_dmac_init);
    	if (ret) {
    		printf("axi_dmac_init() failed with status %d\n", ret);
    		goto error;
    	}
    
    	ret = axi_dmac_init(&phy.rx2_dmac, &rx2_dmac_init);
    	if (ret) {
    		printf("axi_dmac_init() failed with status %d\n", ret);
    		goto error;
    	}
    #endif
    
    #ifdef DMA_EXAMPLE
    	axi_dac_load_custom_data(phy.tx1_dac, sine_lut_iq,
    				 NO_OS_ARRAY_SIZE(sine_lut_iq),
    				 (uintptr_t)dac_buffers[0]);
    #ifndef ADRV9002_RX2TX2
    	axi_dac_load_custom_data(phy.tx2_dac, sine_lut_iq,
    				 NO_OS_ARRAY_SIZE(sine_lut_iq),
    				 (uintptr_t)dac_buffers[1]);
    #endif
    #ifdef XILINX_PLATFORM
    	Xil_DCacheFlush();
    #endif /* XILINX_PLATFORM */
    
    	struct axi_dma_transfer transfer1 = {
    		// Number of bytes to write/read
    		.size = sizeof(sine_lut_iq),
    		// Transfer done flag
    		.transfer_done = 0,
    		// Signal transfer mode
    		.cyclic = CYCLIC,
    		// Address of data source
    		.src_addr = (uintptr_t)dac_buffers[0],
    		// Address of data destination
    		.dest_addr = 0
    	};
    	axi_dmac_transfer_start(phy.tx1_dmac, &transfer1);
    #ifndef ADRV9002_RX2TX2
    	struct axi_dma_transfer transfer2 = {
    		// Number of bytes to write/read
    		.size = sizeof(sine_lut_iq),
    		// Transfer done flag
    		.transfer_done = 0,
    		// Signal transfer mode
    		.cyclic = CYCLIC,
    		// Address of data source
    		.src_addr = (uintptr_t)dac_buffers[1],
    		// Address of data destination
    		.dest_addr = 0
    	};
    	axi_dmac_transfer_start(phy.tx2_dmac, &transfer2);
    #endif
    
    #ifdef XILINX_PLATFORM
    	Xil_DCacheInvalidateRange((uintptr_t)adc_buffers[0], sizeof(sine_lut_iq));
    #ifndef ADRV9002_RX2TX2
    	Xil_DCacheInvalidateRange((uintptr_t)adc_buffers[1], sizeof(sine_lut_iq));
    #endif
    #endif /* XILINX_PLATFORM */
    
    	no_os_mdelay(1000);
    
    	struct axi_dma_transfer read_transfer1 = {
    		// Number of bytes to write/read
    		.size = ADC_BUFFER_SAMPLES * ADRV9001_I_Q_CHANNELS * 2, /* nr of samples * rx1 i/q, rx2 i/q * bytes per sample */
    		// Transfer done flag
    		.transfer_done = 0,
    		// Signal transfer mode
    		.cyclic = NO,
    		// Address of data source
    		.src_addr = 0,
    		// Address of data destination
    		.dest_addr = (uintptr_t)adc_buffers[0]
    	};
    	/* Transfer ADC_BUFFER_SAMPLES samples from ADC to MEM */
    #ifdef ADRV9002_RX2TX2
    	axi_adc_update_active_channels(phy.rx1_adc, 0xf);
    #else
    	axi_adc_update_active_channels(phy.rx1_adc, 0x3);
    #endif
    	axi_dmac_transfer_start(phy.rx1_dmac, &read_transfer1);
    	ret = axi_dmac_transfer_wait_completion(phy.rx1_dmac, 500);
    	if(ret)
    		return ret;
    #ifdef XILINX_PLATFORM
    	Xil_DCacheInvalidateRange((uintptr_t)adc_buffers[0],
    				  ADC_BUFFER_SAMPLES * /* nr of samples */
    				  ADRV9001_I_Q_CHANNELS * /* rx1 i/q, rx2 i/q*/
    				  2 /* bytes per sample */);
    #endif /* XILINX_PLATFORM */
    #ifndef ADRV9002_RX2TX2
    	struct axi_dma_transfer read_transfer2 = {
    		// Number of bytes to write/read
    		.size = ADC_BUFFER_SAMPLES * ADRV9001_I_Q_CHANNELS * 2, /* nr of samples * rx1 i/q, rx2 i/q * bytes per sample */
    		// Transfer done flag
    		.transfer_done = 0,
    		// Signal transfer mode
    		.cyclic = NO,
    		// Address of data source
    		.src_addr = 0,
    		// Address of data destination
    		.dest_addr = (uintptr_t)adc_buffers[1]
    	};
    	axi_adc_update_active_channels(phy.rx2_adc, 0x3);
    	axi_dmac_transfer_start(phy.rx2_dmac,&read_transfer2);
    	ret = axi_dmac_transfer_wait_completion(phy.rx2_dmac, 500);
    	if(ret)
    		return ret;
    #ifdef XILINX_PLATFORM
    	Xil_DCacheInvalidateRange((uintptr_t)adc_buffers[1],
    				  ADC_BUFFER_SAMPLES * /* nr of samples */
    				  ADRV9001_I_Q_CHANNELS * /* nr of channels */
    				  2 /* bytes per sample */);
    #endif /* XILINX_PLATFORM */
    	printf("DMA_EXAMPLE: address=%#lx samples=%lu channels=%u bits=%lu\n",
    	       (uintptr_t)adc_buffers[1], ADC_BUFFER_SAMPLES * rx2_adc_init.num_channels,
    	       rx2_adc_init.num_channels, 8 * sizeof(adc_buffers[1][0]));
    #endif
    	printf("DMA_EXAMPLE: address=%#lx samples=%lu channels=%u bits=%lu\n",
    	       (uintptr_t)adc_buffers[0], ADC_BUFFER_SAMPLES * rx1_adc_init.num_channels,
    	       rx1_adc_init.num_channels, 8 * sizeof(adc_buffers[0][0]));
    #endif
    
    #ifdef IIO_SUPPORT
    	struct iio_axi_adc_init_param iio_axi_adcs_init_par[] = {{
    			.rx_adc = phy.rx1_adc,
    			.rx_dmac = phy.rx1_dmac,
    #ifdef XILINX_PLATFORM
    			.dcache_invalidate_range = (void (*)(uint32_t, uint32_t))Xil_DCacheInvalidateRange,
    #endif /* XILINX_PLATFORM */
    			.get_sampling_frequency = get_sampling_frequency,
    		},
    #ifndef ADRV9002_RX2TX2
    		{
    			.rx_adc = phy.rx2_adc,
    			.rx_dmac = phy.rx2_dmac,
    #ifdef XILINX_PLATFORM
    			.dcache_invalidate_range = (void (*)(uint32_t, uint32_t))Xil_DCacheInvalidateRange,
    #endif /* XILINX_PLATFORM */
    			.get_sampling_frequency = get_sampling_frequency,
    		}
    #endif
    	};
    
    	struct iio_axi_dac_init_param iio_axi_dacs_init_par[] = {{
    			.tx_dac = phy.tx1_dac,
    			.tx_dmac = phy.tx1_dmac,
    #ifdef XILINX_PLATFORM
    			.dcache_flush_range = (void (*)(uint32_t, uint32_t))Xil_DCacheFlushRange,
    #endif /* XILINX_PLATFORM */
    		},
    #ifndef ADRV9002_RX2TX2
    		{
    			.tx_dac = phy.tx2_dac,
    			.tx_dmac = phy.tx2_dmac,
    #ifdef XILINX_PLATFORM
    			.dcache_flush_range = (void (*)(uint32_t, uint32_t))Xil_DCacheFlushRange,
    #endif /* XILINX_PLATFORM */
    		}
    #endif
    	};
    
    	ret = iio_run(iio_axi_adcs_init_par, iio_axi_dacs_init_par);
    	if (ret < 0) {
    		printf("iio_run() failed with status %d\n", ret);
    		goto error;
    	}
    #endif
    	printf("Bye\n");
    
    error:
    	adi_adrv9001_HwClose(phy.adrv9001);
    	axi_adc_remove(phy.rx1_adc);
    	axi_dac_remove(phy.tx1_dac);
    	axi_adc_remove(phy.rx2_adc);
    	axi_dac_remove(phy.tx2_dac);
    	axi_dmac_remove(phy.rx1_dmac);
    	axi_dmac_remove(phy.tx1_dmac);
    	axi_dmac_remove(phy.rx2_dmac);
    	axi_dmac_remove(phy.tx2_dmac);
    	return ret;
    }
    


  • Hello,

    Regarding the 2019.1 example:

    Both TX channels are left in PRIMED state after adrv9002_setup(), so the RF output paths are not enabled. Please try transitioning the TX channels to RF_ENABLED after adrv9002_post_setup().

    For TDD mode, RX and TX cannot be in RF_ENABLED simultaneously. Since in the 2019.1 version RX was explicitly moved to RF_ENABLED, it must first be brought back to PRIMED before TX can go to RF_ENABLED:

    /* Bring RX back to PRIMED before enabling TX (TDD constraint) */
    adi_adrv9001_Radio_Channel_ToState(phy.adrv9001, ADI_RX,
                       ADI_CHANNEL_1, ADI_ADRV9001_CHANNEL_PRIMED);
    adi_adrv9001_Radio_Channel_ToState(phy.adrv9001, ADI_RX,
                       ADI_CHANNEL_2, ADI_ADRV9001_CHANNEL_PRIMED);
    /* Now enable TX */
    adi_adrv9001_Radio_Channel_ToState(phy.adrv9001, ADI_TX,
                       ADI_CHANNEL_1, ADI_ADRV9001_CHANNEL_RF_ENABLED);
    adi_adrv9001_Radio_Channel_ToState(phy.adrv9001, ADI_TX,
                       ADI_CHANNEL_2, ADI_ADRV9001_CHANNEL_RF_ENABLED);

    The cyclic DMA (DMA_CYCLIC) keeps TX active indefinitely without needing a while(1), so no further changes are needed there.

    Regarding the 2022.2 example:

    RX is already in PRIMED after adrv9002_setup() (TDD leaves all channels in PRIMED), so just add the TX RF_ENABLED calls directly — no RX step needed:

    /* Enable TX */
    adi_adrv9001_Radio_Channel_ToState(phy.adrv9001, ADI_TX,
                       ADI_CHANNEL_1, ADI_ADRV9001_CHANNEL_RF_ENABLED);
    adi_adrv9001_Radio_Channel_ToState(phy.adrv9001, ADI_TX,
                       ADI_CHANNEL_2, ADI_ADRV9001_CHANNEL_RF_ENABLED);

    Additionally, after initialization the code falls through directly to printf("Bye\n") because neither DMA_EXAMPLE nor IIO_SUPPORT is defined, meaning the program exits before any sustained RF output is possible. Add at minimum a while(1) loop after the TX state transitions to keep the program running:

    while(1);
    

    Alternatively, define DMA_EXAMPLE and switch TX back to AXI_DAC_DATA_SEL_DMA to use the cyclic DMA approach as in the 2019.1 version.

  • Hi  ,

    I have integrated a DMA example into headless.c. Below are my observations regarding its performance and behavior:

    1) I have integrated DMA example by including '#define DMA_EXAMPLE' in headless.c file.

    2) In the terminal , prints obtained as-
        'DMA_EXAMPLE: address=0x14a910 samples=32768 channels=2 bits=16'

    3) The carrier frequency is set at 2GHz. On enabling DMA_EXAMPLE, the spectrum analyzer shows a  multi-carrier or modulated signal characterized by a series of equidistant spectral peaks. Positioned at   2.000 188 GHz with a power level of -16.74 dBm.

    4) Without enabling DMA_EXAMPLE, the spike at 2 GHz is indicating a LO leakage with a power level of   -73.93 dbm.

      I have attached the terminal prints and the spectrum analyzer captures comparing the system's output  with and without the  DMA_EXAMPLE enabled. I would appreciate your feedback on these observations   and  guidance on how to  proceed with the integration.

    DMA_EXAMPLE enabled:



    DMA_EXAMPLE not enabled:



    Terminal_Prints:





    regards,

    Anurag

  • Hello,

    Thank you for the detailed observations and captures. Your results confirm that the TX output now functions correctly. Here is an explanation of what you are seeing:

    1. DMA_EXAMPLE not enabled:

    When the transmitter is initialized, but not sending data, a small fraction of the LO signal leaks to the RF output. This appears as a weak spike at the carrier frequency, which is 2 GHz in your case. At -73.93 dBm this is normal hw behavior.

    2. DMA_EXAMPLE enabled:

    The increase in power at the carrier when DMA_EXAMPLE is enabled, confirms that TX RF output is active and working correctly.

    The "multi-carrier" pattern of equidistant spectral peaks is expected and is a direct consequence of how the DMA_EXAMPLE works: it transmits a fixed 1024-sample sine lookup table in a continuous loop via cyclic DMA. Because this finite block repeats identically every 1024 samples, the resulting signal is periodic and procudes a discrete set of equally spaced spectral lines. This is by design. The purpose of the DMA_EXAMPLE is only to verify that the TX data path from memory to the antenna is functional, not to produce a pure output.

    For a clean single tone, build with IIOD=y and connect using IIO Oscilloscope. In the DAC Data Manager, select DDS Mode, One CW Tone, then configure the tone Frequency, Scale, and Phase for TX1 and TX2. The DDS uses a hardware phase accumulator (NCO) and produces a single precise spectral line, unlike the equidistant comb produced by the DMA example.

    Please let us know if this works for you.

  • Hi  
     
    For a clean single tone, I have installed IIO Oscilloscope and Libiio in my system. Regarding changes in the headless.c file, I have included #define IIO_SUPPORT at the top , but while building the project i'm getting some issues of unresolved inclusion for #include iio_app.h. After adding the missing files manually , it further throws the error saying undefined references. I am attaching the ss of the issue, kindly look into it and suggest the methods to resolve it. Also , please let me know how to integrate through iio oscilloscope.

    regards,

    Anurag

  • Hello,

    Please do not add #define IIO_SUPPORT manually to headles.c. The IIO support flag must be passed to the build system, not hardcoded in the source file. Enabling IIO requires not just the define, but also additional library source files to be compiled and linked, while the build system handles this automatically. The correct build command is:

    make IIOD=y

    I recommend using the command line make from the project directory:

    cd projects/adrv9001
    make clean
    make IIOD=y

    This will:

    - Set -DIIO_SUPPORT as a compiler flag automatically

    - Include all the required IIO library sources

    - Link everything correctly.

    Let us know if this worked for you.

    -Stefan

  • Hi  

    I have successfully rebuilt the project using the make IIOD=y flag and launched the hardware. However, I am encountering an issue when attempting to interface with the IIO Oscilloscope.

    1. Connection Issue Details:

    • Method: Connecting via COMM port.

    • Configuration: Baud rate set to 921600.

    • Error: Upon clicking 'Refresh', the device fails to connect and returns the error: "Could not get IIO context."

    I have attached a screenshot of the IIO interface and the console prints for your reference. Could you please advise if there are specific boot sequences or environment variables required to initialize the IIO daemon correctly on this build?

    2. Headless.c Integration: Regarding the new headless.c file generated by the IIOD=y build:

    • In previous builds, we manually added TX_RFENABLED calls to enable the radio front-end.

    • Does the IIOD-enabled version require these same manual modifications, or is the RF path handled differently when the IIO daemon is active?
      Do we need to change the mode also (DDS/DMA).

    I have attached the current headless.c file. I would appreciate your guidance on the correct way to integrate the TX/RX enable calls within this new structure to ensure compatibility with the IIO Oscilloscope.

    Looking forward to your suggestions on the next steps.

    /***************************************************************************//**
     *   @file   headless.c
     *   @brief  adrv9002 main project file.
     *   @author Darius Berghe (darius.berghe@analog.com)
    ********************************************************************************
     * Copyright 2020(c) Analog Devices, Inc.
     *
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions are met:
     *  - Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     *  - Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in
     *    the documentation and/or other materials provided with the
     *    distribution.
     *  - Neither the name of Analog Devices, Inc. nor the names of its
     *    contributors may be used to endorse or promote products derived
     *    from this software without specific prior written permission.
     *  - The use of this software may or may not infringe the patent rights
     *    of one or more patent holders.  This license does not release you
     *    from the requirement that you obtain separate licenses from these
     *    patent holders to use this software.
     *  - Use of the software either in source or binary form, must be run
     *    on or directly connected to an Analog Devices Inc. component.
     *
     * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR
     * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT,
     * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT,
     * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR
     * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
     * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    *******************************************************************************/
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #ifdef XILINX_PLATFORM
    #include "xil_cache.h"
    #endif /* XILINX_PLATFORM */
    
    #include "no_os_error.h"
    #include "no_os_util.h"
    #include "no_os_spi.h"
    
    #include "axi_adc_core.h"
    #include "axi_dac_core.h"
    #include "axi_dmac.h"
    
    #include "parameters.h"
    
    #ifdef IIO_SUPPORT
    #include "iio_app.h"
    #include "iio_axi_adc.h"
    #include "iio_axi_dac.h"
    #include "xilinx_uart.h"
    #endif
    
    #include "adrv9002.h"
    #include "adi_adrv9001.h"
    #include "adi_adrv9001_arm.h"
    #include "adi_adrv9001_radio.h"
    #include "adi_adrv9001_profileutil.h"
    #include "Navassa_LVDS_profile.h"
    
    /* ADC/DAC Buffers */
    #if defined(DMA_EXAMPLE) || defined(IIO_SUPPORT)
    static uint32_t dac_buffers[IIO_DEV_COUNT][DAC_BUFFER_SAMPLES]
    __attribute__((aligned));
    static uint16_t adc_buffers[IIO_DEV_COUNT][ADC_BUFFER_SAMPLES]
    __attribute__((aligned));
    #endif
    
    uint64_t sampling_freq;
    
    int get_sampling_frequency(struct axi_adc *dev, uint32_t chan,
    			   uint64_t *sampling_freq_hz)
    {
    	if (!dev || !sampling_freq_hz)
    		return -EINVAL;
    
    	*sampling_freq_hz = sampling_freq;
    	return 0;
    }
    
    static struct adi_adrv9001_SpiSettings spiSettings = {
    	.msbFirst = 1,
    	.enSpiStreaming = 0,
    	.autoIncAddrUp = 1,
    	.fourWireMode = 1,
    	.cmosPadDrvStrength = ADI_ADRV9001_CMOSPAD_DRV_STRONG,
    };
    
    struct adi_adrv9001_SpiSettings *adrv9002_spi_settings_get(void)
    {
    	return &spiSettings;
    }
    
    enum adi_adrv9001_SsiType adrv9002_ssi_type_detect(struct adrv9002_rf_phy *phy)
    {
    	enum adi_adrv9001_SsiType ssi, ssi2;
    	char *ssi_str[3] = {
    		"[SSI Disabled]",
    		"CMOS",
    		"LVDS"
    	};
    
    	ssi = adrv9002_axi_ssi_type_get(phy);
    
    	ssi2 = phy->curr_profile->rx.rxChannelCfg[0].profile.rxSsiConfig.ssiType;
    	if (ssi != ssi2) {
    		printf("SSI mismatch: detected %s in HDL and %s in profile.\n", ssi_str[ssi],
    		       ssi_str[ssi2]);
    		return ADI_ADRV9001_SSI_TYPE_DISABLE;
    	}
    
    	return ssi;
    }
    
    static struct adi_adrv9001_GainControlCfg agc_defaults = {
    	.peakWaitTime = 4,
    	.maxGainIndex = ADI_ADRV9001_RX_GAIN_INDEX_MAX,
    	.minGainIndex = ADI_ADRV9001_RX_GAIN_INDEX_MIN,
    	.gainUpdateCounter = 11520,
    	.attackDelay_us = 10,
    	.lowThreshPreventGainInc = false,
    	.slowLoopSettlingDelay = 16,
    	.changeGainIfThreshHigh = 3,
    	.agcMode = 1,
    	.resetOnRxon = false,
    	.resetOnRxonGainIndex = ADI_ADRV9001_RX_GAIN_INDEX_MAX,
    	.enableSyncPulseForGainCounter = false,
    	.enableFastRecoveryLoop = false,
    	.power = {
    		.powerEnableMeasurement = true,
    		.underRangeHighPowerThresh = 10,
    		.underRangeLowPowerThresh = 4,
    		.underRangeHighPowerGainStepRecovery = 2,
    		.underRangeLowPowerGainStepRecovery = 4,
    		.powerMeasurementDuration = 10,
    		.powerMeasurementDelay = 2,
    		.rxTddPowerMeasDuration = 0,
    		.rxTddPowerMeasDelay = 0,
    		.overRangeHighPowerThresh = 0,
    		.overRangeLowPowerThresh = 7,
    		.overRangeHighPowerGainStepAttack = 4,
    		.overRangeLowPowerGainStepAttack = 4,
    		.feedback_inner_high_inner_low = ADI_ADRV9001_GPIO_PIN_CRUMB_UNASSIGNED,
    		.feedback_apd_high_apd_low = ADI_ADRV9001_GPIO_PIN_CRUMB_UNASSIGNED,
    	},
    	.peak = {
    		.agcUnderRangeLowInterval = 50,
    		.agcUnderRangeMidInterval = 2,
    		.agcUnderRangeHighInterval = 4,
    		.apdHighThresh = 21,
    		.apdLowThresh = 12,
    		.apdUpperThreshPeakExceededCount = 6,
    		.apdLowerThreshPeakExceededCount = 3,
    		.apdGainStepAttack = 2,
    		.apdGainStepRecovery = 0,
    		.enableHbOverload = true,
    		.hbOverloadDurationCount = 1,
    		.hbOverloadThreshCount = 1,
    		.hbHighThresh = 13044,
    		.hbUnderRangeLowThresh = 5826,
    		.hbUnderRangeMidThresh = 8230,
    		.hbUnderRangeHighThresh = 7335,
    		.hbUpperThreshPeakExceededCount = 6,
    		.hbUnderRangeHighThreshExceededCount = 3,
    		.hbGainStepHighRecovery = 2,
    		.hbGainStepLowRecovery = 6,
    		.hbGainStepMidRecovery = 4,
    		.hbGainStepAttack = 2,
    		.hbOverloadPowerMode = 0,
    		.hbUnderRangeMidThreshExceededCount = 3,
    		.hbUnderRangeLowThreshExceededCount = 3,
    		.feedback_apd_low_hb_low = ADI_ADRV9001_GPIO_PIN_CRUMB_UNASSIGNED,
    		.feedback_apd_high_hb_high = ADI_ADRV9001_GPIO_PIN_CRUMB_UNASSIGNED,
    	},
    };
    
    #ifdef IIO_SUPPORT
    
    static int32_t iio_run(struct iio_axi_adc_init_param *adc_pars,
    		       struct iio_axi_dac_init_param *dac_pars)
    {
    	struct iio_axi_adc_desc *adcs[IIO_DEV_COUNT];
    	struct iio_axi_dac_desc *dacs[IIO_DEV_COUNT];
    	struct iio_data_buffer iio_dac_buffers[IIO_DEV_COUNT];
    	struct iio_data_buffer iio_adc_buffers[IIO_DEV_COUNT];
    	struct iio_device *iio_descs[IIO_DEV_COUNT * 2];
    	struct iio_app_device app_devices[IIO_DEV_COUNT * 2] = {0};
    	struct xil_uart_init_param platform_uart_init_par = {
    		.type = UART_PS,
    		.irq_id = UART_IRQ_ID
    	};
    
    	struct no_os_uart_init_param iio_uart_ip = {
    		.device_id = UART_DEVICE_ID,
    		.irq_id = UART_IRQ_ID,
    		.baud_rate = UART_BAUDRATE,
    		.size = NO_OS_UART_CS_8,
    		.parity = NO_OS_UART_PAR_NO,
    		.stop = NO_OS_UART_STOP_1_BIT,
    		.extra = &platform_uart_init_par,
    		.platform_ops = &xil_uart_ops
    	};
    
    	struct iio_app_desc *app;
    	struct iio_app_init_param app_init_param = { 0 };
    	int32_t i, ret;
    	int32_t a; // linear iterator for iio_descs and app_devices flat arrays
    
    	for (i = 0; i < IIO_DEV_COUNT; i++) {
    		/* ADC setup */
    		iio_adc_buffers[i].buff = adc_buffers[i];
    		iio_adc_buffers[i].size = sizeof(adc_buffers[i]);
    		ret = iio_axi_adc_init(&adcs[i], &adc_pars[i]);
    		if (ret < 0)
    			return ret;
    		a = 2 * i;
    		iio_axi_adc_get_dev_descriptor(adcs[i], &iio_descs[a]);
    		app_devices[a].name = adc_pars[i].rx_adc->name;
    		app_devices[a].dev = adcs[i];
    		app_devices[a].dev_descriptor = iio_descs[a];
    		app_devices[a].read_buff = &iio_adc_buffers[i];
    
    		/* DAC setup */
    		iio_dac_buffers[i].buff = dac_buffers[i];
    		iio_dac_buffers[i].size = sizeof(dac_buffers[i]);
    		ret = iio_axi_dac_init(&dacs[i], &dac_pars[i]);
    		if (ret < 0)
    			return ret;
    		a = 2 * i + 1;
    		iio_axi_dac_get_dev_descriptor(dacs[i], &iio_descs[a]);
    		app_devices[a].name = dac_pars[i].tx_dac->name;
    		app_devices[a].dev = dacs[i];
    		app_devices[a].dev_descriptor = iio_descs[a];
    		app_devices[a].write_buff = &iio_dac_buffers[i];
    	}
    
    	app_init_param.devices = app_devices;
    	app_init_param.nb_devices = NO_OS_ARRAY_SIZE(app_devices);
    	app_init_param.uart_init_params = iio_uart_ip;
    
    	ret = iio_app_init(&app, app_init_param);
    	if (ret)
    		return ret;
    
    	return iio_app_run(app);
    }
    #endif
    
    int main(void)
    {
    	int ret;
    	struct adi_common_ApiVersion api_version;
    	struct adi_adrv9001_ArmVersion arm_version;
    	struct adi_adrv9001_SiliconVersion silicon_version;
    	struct adi_adrv9001_Device adrv9001_device = {0};
    	struct adrv9002_chip_info chip = {0};
    	struct adrv9002_rf_phy phy = {0};
    	unsigned int c;
    
    	struct axi_adc_init rx1_adc_init = {
    		.name = "axi-adrv9002-rx-lpc",
    		.base = RX1_ADC_BASEADDR,
    		.num_channels = ADRV9001_I_Q_CHANNELS,
    	};
    
    	struct axi_dac_channel  tx1_dac_channels[2];
    	tx1_dac_channels[0].sel = AXI_DAC_DATA_SEL_DMA;
    	tx1_dac_channels[1].sel = AXI_DAC_DATA_SEL_DMA;
    
    	struct axi_dac_init tx1_dac_init = {
    		.name = "axi-adrv9002-tx-lpc",
    		.base = TX1_DAC_BASEADDR,
    		.num_channels = ADRV9001_I_Q_CHANNELS,
    		.channels = tx1_dac_channels,
    		.rate = 3
    	};
    
    #ifndef ADRV9002_RX2TX2
    	struct axi_adc_init rx2_adc_init = {
    		.name = "axi-adrv9002-rx2-lpc",
    		.base = RX2_ADC_BASEADDR,
    		.num_channels = ADRV9001_I_Q_CHANNELS,
    	};
    
    	struct axi_dac_channel  tx2_dac_channels[2];
    	tx2_dac_channels[0].sel = AXI_DAC_DATA_SEL_DMA;
    	tx2_dac_channels[1].sel = AXI_DAC_DATA_SEL_DMA;
    
    	struct axi_dac_init tx2_dac_init = {
    		.name = "axi-adrv9002-tx2-lpc",
    		.base = TX2_DAC_BASEADDR,
    		.num_channels = ADRV9001_I_Q_CHANNELS,
    		.channels = tx2_dac_channels,
    		.rate = 3
    	};
    #endif
    	struct axi_dmac_init rx1_dmac_init = {
    		"rx_dmac",
    		RX1_DMA_BASEADDR,
    		IRQ_DISABLED
    	};
    
    	struct axi_dmac_init tx1_dmac_init = {
    		"tx_dmac",
    		TX1_DMA_BASEADDR,
    		IRQ_DISABLED
    	};
    
    #ifndef ADRV9002_RX2TX2
    	struct axi_dmac_init rx2_dmac_init = {
    		"rx_dmac",
    		RX2_DMA_BASEADDR,
    		IRQ_DISABLED
    	};
    
    	struct axi_dmac_init tx2_dmac_init = {
    		"tx_dmac",
    		TX2_DMA_BASEADDR,
    		IRQ_DISABLED
    	};
    #endif
    
    #ifdef XILINX_PLATFORM
    	Xil_ICacheEnable();
    	Xil_DCacheEnable();
    #endif /* XILINX_PLATFORM */
    
    	printf("Hello\n");
    
    #if defined(ADRV9002_RX2TX2)
    	phy.rx2tx2 = true;
    #endif
    
    	phy.adrv9001 = &adrv9001_device;
    
    	/* ADRV9002 */
    	chip.cmos_profile = "Navassa_CMOS_profile.json";
    	chip.lvd_profile = "Navassa_LVDS_profile.json";
    	chip.name = "adrv9002-phy";
    	chip.n_tx = ADRV9002_CHANN_MAX;
    
    	phy.chip = &chip;
    
    	ret = adi_adrv9001_profileutil_Parse(phy.adrv9001, &phy.profile,
    					     (char *)json_profile, strlen(json_profile));
    	if (ret)
    		goto error;
    
    	phy.curr_profile = &phy.profile;
    
    	sampling_freq = phy.curr_profile->rx.rxChannelCfg[0].profile.rxOutputRate_Hz;
    
    	/* Initialize the ADC/DAC cores */
    	ret = axi_adc_init_begin(&phy.rx1_adc, &rx1_adc_init);
    	if (ret) {
    		printf("axi_adc_init_begin() failed with status %d\n", ret);
    		goto error;
    	}
    
    	ret = axi_dac_init_begin(&phy.tx1_dac, &tx1_dac_init);
    	if (ret) {
    		printf("axi_dac_init_begin() failed with status %d\n", ret);
    		goto error;
    	}
    #ifndef ADRV9002_RX2TX2
    	ret = axi_adc_init_begin(&phy.rx2_adc, &rx2_adc_init);
    	if (ret) {
    		printf("axi_adc_init_begin() failed with status %d\n", ret);
    		goto error;
    	}
    
    	ret = axi_dac_init_begin(&phy.tx2_dac, &tx2_dac_init);
    	if (ret) {
    		printf("axi_dac_init_begin() failed with status %d\n", ret);
    		goto error;
    	}
    #endif
    
    	phy.ssi_type = adrv9002_ssi_type_detect(&phy);
    	if (phy.ssi_type == ADI_ADRV9001_SSI_TYPE_DISABLE)
    		goto error;
    
    	/* Initialize AGC */
    	for (c = 0; c < ADRV9002_CHANN_MAX; c++) {
    		phy.rx_channels[c].agc = agc_defaults;
    	}
    
    	ret = adrv9002_setup(&phy);
    	if (ret)
    		return ret;
    
    	adi_adrv9001_ApiVersion_Get(phy.adrv9001, &api_version);
    	adi_adrv9001_arm_Version(phy.adrv9001, &arm_version);
    	adi_adrv9001_SiliconVersion_Get(phy.adrv9001, &silicon_version);
    
    	printf("%s Rev %d.%d, Firmware %u.%u.%u.%u API version: %u.%u.%u successfully initialized\n",
    	       "ADRV9002", silicon_version.major, silicon_version.minor,
    	       arm_version.majorVer, arm_version.minorVer,
    	       arm_version.maintVer, arm_version.rcVer, api_version.major,
    	       api_version.minor, api_version.patch);
    
    	/* Post AXI DAC/ADC setup, digital interface tuning */
    	ret = adrv9002_post_setup(&phy);
    	if (ret) {
    		printf("adrv9002_post_setup() failed with status %d\n", ret);
    		goto error;
    	}
    
    	/* Finalize the ADC/DAC cores initialization */
    	ret = axi_adc_init_finish(phy.rx1_adc);
    	if (ret) {
    		printf("axi_adc_init_finish() failed with status %d\n", ret);
    		goto error;
    	}
    
    	ret = axi_dac_init_finish(phy.tx1_dac);
    	if (ret) {
    		printf("axi_dac_init_finish() failed with status %d\n", ret);
    		goto error;
    	}
    	phy.tx1_dac->clock_hz = phy.curr_profile->tx.txProfile[0].txInputRate_Hz;
    #ifndef ADRV9002_RX2TX2
    	ret = axi_adc_init_finish(phy.rx2_adc);
    	if (ret) {
    		printf("axi_adc_init_finish() failed with status %d\n", ret);
    		goto error;
    	}
    
    	ret = axi_dac_init_finish(phy.tx2_dac);
    	if (ret) {
    		printf("axi_dac_init_finish() failed with status %d\n", ret);
    		goto error;
    	}
    	phy.tx2_dac->clock_hz = phy.curr_profile->tx.txProfile[1].txInputRate_Hz;
    #endif
    
    	/* Initialize the AXI DMA Controller cores */
    	ret = axi_dmac_init(&phy.tx1_dmac, &tx1_dmac_init);
    	if (ret) {
    		printf("axi_dmac_init() failed with status %d\n", ret);
    		goto error;
    	}
    
    	ret = axi_dmac_init(&phy.rx1_dmac, &rx1_dmac_init);
    	if (ret) {
    		printf("axi_dmac_init() failed with status %d\n", ret);
    		goto error;
    	}
    #ifndef ADRV9002_RX2TX2
    	ret = axi_dmac_init(&phy.tx2_dmac, &tx2_dmac_init);
    	if (ret) {
    		printf("axi_dmac_init() failed with status %d\n", ret);
    		goto error;
    	}
    
    	ret = axi_dmac_init(&phy.rx2_dmac, &rx2_dmac_init);
    	if (ret) {
    		printf("axi_dmac_init() failed with status %d\n", ret);
    		goto error;
    	}
    #endif
    
    #ifdef DMA_EXAMPLE
    	axi_dac_load_custom_data(phy.tx1_dac, sine_lut_iq,
    				 NO_OS_ARRAY_SIZE(sine_lut_iq),
    				 (uintptr_t)dac_buffers[0]);
    #ifndef ADRV9002_RX2TX2
    	axi_dac_load_custom_data(phy.tx2_dac, sine_lut_iq,
    				 NO_OS_ARRAY_SIZE(sine_lut_iq),
    				 (uintptr_t)dac_buffers[1]);
    #endif
    #ifdef XILINX_PLATFORM
    	Xil_DCacheFlush();
    #endif /* XILINX_PLATFORM */
    
    	struct axi_dma_transfer transfer1 = {
    		// Number of bytes to write/read
    		.size = sizeof(sine_lut_iq),
    		// Transfer done flag
    		.transfer_done = 0,
    		// Signal transfer mode
    		.cyclic = CYCLIC,
    		// Address of data source
    		.src_addr = (uintptr_t)dac_buffers[0],
    		// Address of data destination
    		.dest_addr = 0
    	};
    	axi_dmac_transfer_start(phy.tx1_dmac, &transfer1);
    #ifndef ADRV9002_RX2TX2
    	struct axi_dma_transfer transfer2 = {
    		// Number of bytes to write/read
    		.size = sizeof(sine_lut_iq),
    		// Transfer done flag
    		.transfer_done = 0,
    		// Signal transfer mode
    		.cyclic = CYCLIC,
    		// Address of data source
    		.src_addr = (uintptr_t)dac_buffers[1],
    		// Address of data destination
    		.dest_addr = 0
    	};
    	axi_dmac_transfer_start(phy.tx2_dmac, &transfer2);
    #endif
    
    #ifdef XILINX_PLATFORM
    	Xil_DCacheInvalidateRange((uintptr_t)adc_buffers[0], sizeof(sine_lut_iq));
    #ifndef ADRV9002_RX2TX2
    	Xil_DCacheInvalidateRange((uintptr_t)adc_buffers[1], sizeof(sine_lut_iq));
    #endif
    #endif /* XILINX_PLATFORM */
    
    	no_os_mdelay(1000);
    
    	struct axi_dma_transfer read_transfer1 = {
    		// Number of bytes to write/read
    		.size = ADC_BUFFER_SAMPLES * ADRV9001_I_Q_CHANNELS * 2, /* nr of samples * rx1 i/q, rx2 i/q * bytes per sample */
    		// Transfer done flag
    		.transfer_done = 0,
    		// Signal transfer mode
    		.cyclic = NO,
    		// Address of data source
    		.src_addr = 0,
    		// Address of data destination
    		.dest_addr = (uintptr_t)adc_buffers[0]
    	};
    	/* Transfer ADC_BUFFER_SAMPLES samples from ADC to MEM */
    #ifdef ADRV9002_RX2TX2
    	axi_adc_update_active_channels(phy.rx1_adc, 0xf);
    #else
    	axi_adc_update_active_channels(phy.rx1_adc, 0x3);
    #endif
    	axi_dmac_transfer_start(phy.rx1_dmac, &read_transfer1);
    	ret = axi_dmac_transfer_wait_completion(phy.rx1_dmac, 500);
    	if(ret)
    		return ret;
    #ifdef XILINX_PLATFORM
    	Xil_DCacheInvalidateRange((uintptr_t)adc_buffers[0],
    				  ADC_BUFFER_SAMPLES * /* nr of samples */
    				  ADRV9001_I_Q_CHANNELS * /* rx1 i/q, rx2 i/q*/
    				  2 /* bytes per sample */);
    #endif /* XILINX_PLATFORM */
    #ifndef ADRV9002_RX2TX2
    	struct axi_dma_transfer read_transfer2 = {
    		// Number of bytes to write/read
    		.size = ADC_BUFFER_SAMPLES * ADRV9001_I_Q_CHANNELS * 2, /* nr of samples * rx1 i/q, rx2 i/q * bytes per sample */
    		// Transfer done flag
    		.transfer_done = 0,
    		// Signal transfer mode
    		.cyclic = NO,
    		// Address of data source
    		.src_addr = 0,
    		// Address of data destination
    		.dest_addr = (uintptr_t)adc_buffers[1]
    	};
    	axi_adc_update_active_channels(phy.rx2_adc, 0x3);
    	axi_dmac_transfer_start(phy.rx2_dmac,&read_transfer2);
    	ret = axi_dmac_transfer_wait_completion(phy.rx2_dmac, 500);
    	if(ret)
    		return ret;
    #ifdef XILINX_PLATFORM
    	Xil_DCacheInvalidateRange((uintptr_t)adc_buffers[1],
    				  ADC_BUFFER_SAMPLES * /* nr of samples */
    				  ADRV9001_I_Q_CHANNELS * /* nr of channels */
    				  2 /* bytes per sample */);
    #endif /* XILINX_PLATFORM */
    	printf("DMA_EXAMPLE: address=%#lx samples=%lu channels=%u bits=%lu\n",
    	       (uintptr_t)adc_buffers[1], ADC_BUFFER_SAMPLES * rx2_adc_init.num_channels,
    	       rx2_adc_init.num_channels, 8 * sizeof(adc_buffers[1][0]));
    #endif
    	printf("DMA_EXAMPLE: address=%#lx samples=%lu channels=%u bits=%lu\n",
    	       (uintptr_t)adc_buffers[0], ADC_BUFFER_SAMPLES * rx1_adc_init.num_channels,
    	       rx1_adc_init.num_channels, 8 * sizeof(adc_buffers[0][0]));
    #endif
    
    #ifdef IIO_SUPPORT
    	struct iio_axi_adc_init_param iio_axi_adcs_init_par[] = {{
    			.rx_adc = phy.rx1_adc,
    			.rx_dmac = phy.rx1_dmac,
    #ifdef XILINX_PLATFORM
    			.dcache_invalidate_range = (void (*)(uint32_t, uint32_t))Xil_DCacheInvalidateRange,
    #endif /* XILINX_PLATFORM */
    			.get_sampling_frequency = get_sampling_frequency,
    		},
    #ifndef ADRV9002_RX2TX2
    		{
    			.rx_adc = phy.rx2_adc,
    			.rx_dmac = phy.rx2_dmac,
    #ifdef XILINX_PLATFORM
    			.dcache_invalidate_range = (void (*)(uint32_t, uint32_t))Xil_DCacheInvalidateRange,
    #endif /* XILINX_PLATFORM */
    			.get_sampling_frequency = get_sampling_frequency,
    		}
    #endif
    	};
    
    	struct iio_axi_dac_init_param iio_axi_dacs_init_par[] = {{
    			.tx_dac = phy.tx1_dac,
    			.tx_dmac = phy.tx1_dmac,
    #ifdef XILINX_PLATFORM
    			.dcache_flush_range = (void (*)(uint32_t, uint32_t))Xil_DCacheFlushRange,
    #endif /* XILINX_PLATFORM */
    		},
    #ifndef ADRV9002_RX2TX2
    		{
    			.tx_dac = phy.tx2_dac,
    			.tx_dmac = phy.tx2_dmac,
    #ifdef XILINX_PLATFORM
    			.dcache_flush_range = (void (*)(uint32_t, uint32_t))Xil_DCacheFlushRange,
    #endif /* XILINX_PLATFORM */
    		}
    #endif
    	};
    
    	ret = iio_run(iio_axi_adcs_init_par, iio_axi_dacs_init_par);
    	if (ret < 0) {
    		printf("iio_run() failed with status %d\n", ret);
    		goto error;
    	}
    #endif
    	while(1){
    		no_os_mdelay(1000);
    	}
    	printf("Bye\n");
    
    error:
    	adi_adrv9001_HwClose(phy.adrv9001);
    	axi_adc_remove(phy.rx1_adc);
    	axi_dac_remove(phy.tx1_dac);
    	axi_adc_remove(phy.rx2_adc);
    	axi_dac_remove(phy.tx2_dac);
    	axi_dmac_remove(phy.rx1_dmac);
    	axi_dmac_remove(phy.tx1_dmac);
    	axi_dmac_remove(phy.rx2_dmac);
    	axi_dmac_remove(phy.tx2_dmac);
    	return ret;
    }
    






    regards,

    Anurag

  • Hello,

    In IIO Oscilloscope, you need to select the Serial Context radio button (not the default Scan/Discoverable mode), then set the baud rate to 921600 and select the correct COM port, then click Refresh. Please see the attached screenshot showing a working connection on my side.

    For further guidance on using IIO Oscilloscope (capturing ADC data, configuring the DAC DDS mode, etc.), all the necessary information is available in the project README and the IIO Oscilloscope wiki:

    This covers the scope of support we can provide through this channel for the standard example project. On this channel we do not support custom integration beyond the provided examples.

    Best regards

  • Hi  

    I have build the project with the IIOD functionality enabled using the following command: make CMOS_LVDS_N=0 IIOD=y

    Observed Behavior:

    1. Code Inactivity: In the source editor, the code blocks wrapped in #ifdef IIOD (or similar macros) remain greyed out, suggesting the macro is not being defined.

    2. Binary Consistency: The terminal output/logs during execution are identical to the non-IIOD build, and no IIOD-specific files or artifacts are being generated in the output directory.

    3. Build Logs: The build process does not seem to reflect the IIOD=y variable being passed to the compiler as a defined symbol.

    Could you please let me know:

    • Is the IIOD=y flag correctly mapped within the Makefile to a compiler definition?

    • Log Verification: Can you confirm if the following terminal prints [Attached] are mandatory for a successful IIOD initialization?

      Looking forward to your suggestions on the next steps.





    regards, 

    Anurag

  • Hello,

    1. Yes, IIOD=y is correctly mapped: it sets -DIIO_SUPPORT as the compiler define, not -DIIOD. The source code uses #ifdef IIO_SUPPORT, so looking for greyed-out #ifdef IIOD blocks in the IDE is misleading. IDE code highlighting is based on static analysis, not make command-line flags, and is not a reliable indicator of what is compiled.

    2. The CMOS_LVDS_N=0 flag is not recognized by this project's Makefile and is silently ignored. The correct command is

    make clean && make IIOD=y
    

    make clean is mandatory before switching build flags.

    3. Yes, the "Running IIOD server..." terminal print confirms successful IIOD initialization. If you see it, the build is correct.

    All of this is documented in the project README and the no-OS build guide. We recommend following the README step by step before reaching out.

    Best regards!

  • Hi  

    I have successfully built the project with TINYIIOD = y. The hardware is launching correctly, and I can successfully connect to the IIO Oscilloscope over the serial COMM port (921600 baud).

    Current Status:

    • Connectivity: The IIO Oscilloscope correctly identifies the ADRV9002 transceiver.

    • Configuration: Within the IIO Oscilloscope, I have configured the DDS to "One CW Tone" and adjusted the frequency/attenuation settings.

    • Observation: Despite the software showing an active state, I am observing no signal at the carrier frequency (or anywhere in the span) on the Spectrum Analyzer. The output remains at the noise floor.

    Specific Questions:

    1. RF Path Enablement: Does the TINYIIOD build handle the hardware power-up tracking and RF enabling automatically, or do I still need to manually call TX_RFENABLE APIs within headless.c?

    2. Calibration: Are there specific pre-conditions or calibration bitmasks that must be set in the IIO interface before the TX signal path becomes physically active?

    3. DDS Setup: Is there a specific sequence in the IIO Oscilloscope  required to push the DDS configuration to the hardware?

    Kindly look into it and Please let me know if any  logs or details needed to diagnose the lack of RF output.

    regards,

    Anurag

Before You Switch


Switching languages will make ADI Explorer unavailable. Resume your session by switching back to English and reopening ADI Explorer.