Post Go back to editing

How to Configure DPD correctly?

Hello, 

I have a ton of questions to ask about DPD configuration on the ADRV9025 chip. I am trying to configure DPD without the PA connected between the Tx-ORx path solely for testing purposes. I have gone through all the steps required in setting up DPD but there seems to be no signal in the ORx path. The  dpdErrorCode I get shifts between 0x3405 and 0x340E. 

My setup is as follows:

I am running a 2T-2R-2ORx setup utilizing TX3/TX2, RX3/RX2, and ORX3/ORX1. With this setup, I am running DPD on the TX2, connecting the output of the TX2 via a splitter to the ORX1 port and a spectrum analyzer (So as to monitor the TX2-ORX1 path).

For the DPD Tracking Cal Bring up Sequence, I followed the following steps. Note that I have supplied only the necessary snippets to avoid cluttering up this page:

1a. Program the device and run initial calibrations 

1b. Run TxQEC initial calibration after chip initialization (for some reason, the programming failed when I tried to run the tx_qec init cal as part of the POSTMCS during initialization, see code snippet)

recoveryAct = adi_init_tx_cals_run( adrv9025Device, 
                                    ADI_ADRV9025_TX2,  
                                    ( ADI_ADRV9025_TX_QEC_INIT | ADI_ADRV9025_INTERNAL_PATH_DELAY | ADI_ADRV9025_TX_LO_LEAKAGE_INTERNAL )                                        
                                ) ;

2. Disabled ORX channels mapped TX2 to ORX1 and called the adi_adrv9025_RadioCtrlCfgSet function, setting all control modes to SPI (see code snippet)

printf("*** Disabling ORx Channels ***\n") ;
recoveryAct = adi_adrv9025_RxTxEnableSet ( adrv9025Device, 0x06, 0x06 ) ;

rxChannelMaskGet = 0 ;
txChannelMaskGet = 0 ;
usleep(1) ;
recoveryAct = adi_adrv9025_RxTxEnableGet(adrv9025Device, &rxChannelMaskGet, &txChannelMaskGet) ;

recoveryAct = adi_adrv9025_TxToOrxMappingSet(device,ADI_ADRV9025_ORX1, ADI_ADRV9025_TX2);

adrv9025PostMcsInitInst.radioCtrlInit.radioCtrlModeCfg.orxRadioCtrlModeCfg.orxEnableMode = ADI_ADRV9025_ORX_EN_SPI_MODE; //ADI_ADRV9025_ORX_EN_SINGLE_CH_2PIN_MODE;
adrv9025PostMcsInitInst.radioCtrlInit.radioCtrlModeCfg.rxRadioCtrlModeCfg.rxEnableMode = ADI_ADRV9025_RX_EN_SPI_MODE ; //ADI_ADRV9025_RX_EN_PIN_MODE;
adrv9025PostMcsInitInst.radioCtrlInit.radioCtrlModeCfg.txRadioCtrlModeCfg.txEnableMode = ADI_ADRV9025_TX_EN_SPI_MODE ;//ADI_ADRV9025_TX_EN_PIN_MODE;
recoveryAct = adi_adrv9025_RadioCtrlCfgSet(adrv9025Device, &adrv9025PostMcsInitInst.radioCtrlInit.radioCtrlModeCfg);
	

3. Adjusted ORX1 gain, using gain index of 0xFF (see code snippet)

rx_gain.gainIndex       = 0xFF ;
rx_gain.rxChannelMask   = ADI_ADRV9025_ORX1 ;
    
recoveryAct = adi_adrv9025_RxGainSet  (device, &rx_gain, 1);

4. Run the external path delay for TX2 (see code snippet)

adrv9025PostMcsInitInst.initCals.calMask 		= 0x00200000; //external path delay
adrv9025PostMcsInitInst.initCals.channelMask 	= ADI_ADRV9025_TX2 ;
adrv9025PostMcsInitInst.initCals.warmBoot		= 0 ;
recoveryAct = adi_adrv9025_InitCalsRun(device, &adrv9025PostMcsInitInst.initCals);

recoveryAct = adi_adrv9025_InitCalsWait(device, 60000, &initCalsError);

5. Run the Tx external LOL init cals (see code snippet)

adrv9025PostMcsInitInst.initCals.calMask        = 0x00000800; //external LOL
adrv9025PostMcsInitInst.initCals.channelMask 	= ADI_ADRV9025_TX2 ;
adrv9025PostMcsInitInst.initCals.warmBoot		= 0 ;

recoveryAct = adi_adrv9025_InitCalsRun(device, &adrv9025PostMcsInitInst.initCals);

recoveryAct = adi_adrv9025_InitCalsWait(device, 60000, &initCalsError);

6. Configured the CFR settings (adi_adrv9025_CfrCtrlConfigSet,  adi_adrv9025_CfrCorrectionPulseWrite_v2, adi_adrv9025_CfrEnableSet, adi_adrv9025_CfrHardClipperConfigSet, see code snippet)

7. Run the CFR Init Cals (with 60s wait time passed to the adi_adrv9025_InitCalsWait function)

8. Loaded the PA model (see code snippet)

dpdModelConfigInst.txChannelMask = ADI_ADRV9025_TX2 ; 
dpdModelConfigInst.dpdNumFeatures = 22;

char* dpd_model = "/media/sddata/defaultDpdModel.txt"  ;
FILE *fp = fopen(dpd_model, "r");

for(int n = 0; n <dpdModelConfigInst.dpdNumFeatures ; n++)
{
    fscanf(fp, "%d %d %d %d %f %f",
    	&dpdModelConfigInst.dpdFeatures[n].i,
    	&dpdModelConfigInst.dpdFeatures[n].j,
    	&dpdModelConfigInst.dpdFeatures[n].k,
    	&dpdModelConfigInst.dpdFeatures[n].lut,
    	&dpdModelConfigInst.dpdFeatures[n].coeffReal,
    	&dpdModelConfigInst.dpdFeatures[n].coeffImaginary);
    
    printf("\n%d %d %d %d %f %f\n",
    	dpdModelConfigInst.dpdFeatures[n].i,
    	dpdModelConfigInst.dpdFeatures[n].j,
    	dpdModelConfigInst.dpdFeatures[n].k,
    	dpdModelConfigInst.dpdFeatures[n].lut,
    	dpdModelConfigInst.dpdFeatures[n].coeffReal,
    	dpdModelConfigInst.dpdFeatures[n].coeffImaginary);
}
adi_adrv9025_TxChannels_e tx_channels ;
fclose(fp);
recoveryAct = adi_adrv9025_DpdModelConfigSet(adrv9025Device, &dpdModelConfigInst);

--- My DPD MODEL ---

   0  0  0   0       0            0
   0  0  1   0       0            0
   0  0  2   0       0            0
   0  0  3   0       0            0
   0  0  4   0       0            0
   0  0  5   0       0            0
   1  1  1   2       0            0
   1  1  2   2       0            0
   1  1  3   2       0            0
   1  1  4   2       0            0
   1  1  5   2       0            0
   0  1  0   8       0            0
   0  1  1   8       0            0
   0  1  2   8       0            0
   0  1  3   8       0            0
   0  1  4   8       0            0
   0  1  5   8       0            0
   2  2  1  26       0            0
   2  2  2  26       0            0
   2  2  3  26       0            0
   2  2  4  26       0            0
   2  2  5  26       0            0

9. Asserted DPD Reset  (ADI_ADRV9025_DPD_RESET_FULL, see code snippet)

recoveryAct = adi_adrv9025_DpdReset(adrv9025Device, ADI_ADRV9025_TX2, ADI_ADRV9025_DPD_RESET_FULL);
if (recoveryAct != ADI_COMMON_ACT_NO_ACTION)
{
    printf("ERROR: : %s:%u has failed.\n", __func__, __LINE__);
    return recoveryAct;
}

10. Run DPD Tracking configuration (see code snippet)

int32_t MThreshold_dBFS                 = -21; 
int16_t DpdLowPowerThreshold_dBFS       = -36;
int16_t DpdLowPowerThresholdORx_dBFS    = -36;

int32_t temp                            = pow(10,(MThreshold_dBFS/20.0))*FULL_SCALE_CODE;
int32_t dpdMThreshold_val               = pow(temp,2);
int16_t minAvgSignalLevel_val           = pow(10,(DpdLowPowerThreshold_dBFS/20.0))*FULL_SCALE_CODE;
int16_t minAvgSignalLevelOrx_val        = pow(10,(DpdLowPowerThresholdORx_dBFS/20.0))*FULL_SCALE_CODE;

dpdTrackingConfigInst.txChannelMask                             = ADI_ADRV9025_TX2; 
dpdTrackingConfigInst.minAvgSignalLevel                         = minAvgSignalLevel_val;
dpdTrackingConfigInst.minAvgSignalLevelOrx                      = minAvgSignalLevelOrx_val;
dpdTrackingConfigInst.dpdMThreshold                             = temp ;
dpdTrackingConfigInst.dpdPeakSearchWindowSize                   = 65536;
dpdTrackingConfigInst.dpdIndirectRegularizationValue            = 30;
dpdTrackingConfigInst.dpdDirectRegularizationValue              = 30; 
dpdTrackingConfigInst.dpdSamples                                = 16384;
dpdTrackingConfigInst.dpdUpdateMode                             = ADI_ADRV9025_DPD_TRACKING_UPDATE_MODE_0;
dpdTrackingConfigInst.dpdIndirectRegularizationLowPowerValue    = 20;
dpdTrackingConfigInst.dpdFilterSel                              = 0; 
dpdTrackingConfigInst.enableDirectLearning                      = 0;
dpdTrackingConfigInst.dpdMu                                     = 50;

recoveryAct = adi_adrv9025_DpdTrackingConfigSet(adrv9025Device, &dpdTrackingConfigInst);

11. Setup closed-loop gain control configurations (for monitoring purposes. See code snippet)

adi_adrv9025_ClgcConfig_t clgc_config   ;
clgc_config.clgcEnableGainControl   = 0 ;
clgc_config.txChannelMask           = ADI_ADRV9025_TX2 ;

recoveryAct = adrv9025_ClgcConfigSet( adrv9025Device, &clgc_config, 1) ;

12. Enabled TX QEC and TX LOL tracking Calibrations (See code snippet)

recoveryAct = adi_adrv9025_TrackingCalsEnableSet (  adrv9025Device, 
                                                        ( ADI_ADRV9025_TRACK_TX2_QEC | ADI_ADRV9025_TRACK_TX2_LOL ), 
                                                        ADI_ADRV9025_TRACKING_CAL_ENABLE
                                                    );

13. Enabled DPD Tracking Calibration (See code snippet)

adi_adrv9025_TrackingCalsEnableSet (device, ADI_ADRV9025_TRACK_TX2_DPD, ADI_ADRV9025_TRACKING_CAL_ENABLE);

14. Enabled CLGC Tracking Calibration  (See code snippet)

recoveryAct = adi_adrv9025_TrackingCalsEnableSet (  adrv9025Device, 
                                                        ADI_ADRV9025_TRACK_TX2_CLGC, 
                                                        ADI_ADRV9025_TRACKING_CAL_ENABLE
                                                    );

15.  Started monitoring DPD tracking calibration (See code snippet)

//Wait for some time before getting the status
sleep(20);


adi_adrv9025_DpdStatus_t dpdStatus;
int32_t recoveryAction = 0;
recoveryAction = adi_adrv9025_DpdStatusGet (device, txChannel, &dpdStatus);

printf ("dpdErrorCode		:0x%x\n",dpdStatus.dpdErrorCode);
printf ("dpdPercentComplete	:%d\n",dpdStatus.dpdPercentComplete);
printf ("dpdPerformanceMetric	:%d\n",dpdStatus.dpdPerformanceMetric);
printf ("dpdIterCount		:%d\n",dpdStatus.dpdIterCount);
printf ("dpdUpdateCount		:%d\n",dpdStatus.dpdUpdateCount);

printf ("dpdSyncStatus		:%d\n",dpdStatus.dpdSyncStatus);


float meanTu = dpdStatus.dpdStatistics.dpdMeanTuPower;
float peakTu = dpdStatus.dpdStatistics.dpdPeakTuPower;
float meanTx = dpdStatus.dpdStatistics.dpdMeanTxPower;
float peakTx = dpdStatus.dpdStatistics.dpdPeakTxPower;
float meanOrx = dpdStatus.dpdStatistics.dpdMeanOrxPower;
float peakOrx = dpdStatus.dpdStatistics.dpdPeakOrxPower;

printf ("dpdModelTable		:%d\n",dpdStatus.dpdModelTable);


printf ("dpdMeanTuPower		:%f\n",20*log10(sqrt(meanTu) / 32768));
printf ("dpdPeakTuPower		:%f\n",(20*log10(sqrt(peakTu) / 32768)));
printf ("dpdMeanTxPower		:%f\n",(20*log10(sqrt(meanTx) / 32768)));
printf ("dpdPeakTxPower		:%f\n",20*log10(sqrt(peakTx) / 32768));
printf ("dpdMeanOrxPower		:%f\n",20*log10(sqrt(meanOrx) / 32768));
printf ("dpdPeakOrxPower		:%f\n",20*log10(sqrt(peakOrx) / 32768));
printf ("dpdDirectEvm		:%f\n",dpdStatus.dpdStatistics.dpdDirectEvm);
printf ("dpdIndirectEvm		:%f\n",dpdStatus.dpdStatistics.dpdIndirectEvm);
printf ("dpdSelectError		:%f\n",dpdStatus.dpdStatistics.dpdSelectError);
printf ("dpdIndirectError	:%f\n",dpdStatus.dpdStatistics.dpdIndirectError);
printf ("dpdErrorStatus0 (metrics:actions): X:X	:%d:%d\n",	dpdStatus.dpdErrorStatus0.dpdMetricsMask,
						    dpdStatus.dpdErrorStatus0.dpdActionMask);

printf ("dpdErrorStatus1 (metrics:actions)		:%d:%d\n", 	dpdStatus.dpdErrorStatus1.dpdMetricsMask,
							dpdStatus.dpdErrorStatus1.dpdActionMask);
printf ("dpdPersistentErrorStatus0 (metrics:actions)	:%d:%d\n", 	dpdStatus.dpdPersistentErrorStatus0.dpdMetricsMask,
							dpdStatus.dpdPersistentErrorStatus0.dpdActionMask);
printf ("dpdPersistentErrorStatus1 (metrics:actions)	:%d:%d\n", 	dpdStatus.dpdPersistentErrorStatus1.dpdMetricsMask,
							dpdStatus.dpdPersistentErrorStatus1.dpdActionMask);
printf ("reservedPM		:%d\n",dpdStatus.reservedPM);
printf ("reservedTP		:%d\n",dpdStatus.reservedTP);
printf ("reservedPR		:%d\n",dpdStatus.reservedPR);

16. Started monitoring CLGC status (See code snippet)

recoveryAct = adi_adrv9025_ClgcStatusGet(   adrv9025Device  ,
                                                ADI_ADRV9025_TX2,
                                                &clgc_status
                                            );

printf("******** CLGC Monitoring ****************\n")  ;
printf ("clgc_status.clgcLoopGain           : %f\n", clgc_status.clgcLoopGain) ;
printf ("clgc_status.clgcTxRmsPower           : %f\n", clgc_status.clgcTxRmsPower) ;
printf ("clgc_status.clgcOrxRmsPower           : %f\n", clgc_status.clgcOrxRmsPower) ;
printf ("clgc_status.activeTxAttenIndex           : 0x%x\n", clgc_status.activeTxAttenIndex) ;
printf ("clgc_status.activeOrxGainIndex           : 0x%x\n", clgc_status.activeOrxGainIndex) ;

---- And, here's the output of the above code: ----- 

*** Tx2 DPD Status ***
***************************************
Retrieving DPD status
dpdErrorCode :0x3405
dpdPercentComplete :0
dpdPerformanceMetric :0
dpdIterCount :18
dpdUpdateCount :0
dpdSyncStatus :4
dpdModelTable :0
dpdMeanTuPower :-14.280720
dpdPeakTuPower :-3.605944
dpdMeanTxPower :-inf
dpdPeakTxPower :-inf
dpdMeanOrxPower :-58.033522
dpdPeakOrxPower :-49.337814
dpdDirectEvm :4.338248
dpdIndirectEvm :nan
dpdSelectError :0.000000
dpdIndirectError :0.000000
dpdErrorStatus0 (metrics:actions): X:X :0:0
dpdErrorStatus1 (metrics:actions) :0:0
dpdPersistentErrorStatus0 (metrics:actions) :0:0
dpdPersistentErrorStatus1 (metrics:actions) :0:0
reservedPM :0
reservedTP :40249683
reservedPR :40249683
***************************************

*** Running Monitor CLGC tracking Calibration Status ***
******** CLGC Monitoring ****************
clgc_status.clgcLoopGain : 0.000000
clgc_status.clgcTxRmsPower : 0.000000
clgc_status.clgcOrxRmsPower : 0.000000
clgc_status.activeTxAttenIndex : 0x0
clgc_status.activeOrxGainIndex : 0x0
---------------- End of output ----------------------------
 
I am not sure what I am doing wrong here. Could it be because I have not connected a PA yet? 


One thing to add is that after asserting reset, the signal on the TX2-ORX1 path dies and never comes back up. Could it be that I am not performing reset correctly? 
[edited by: AlexAntwi at 2:44 AM (GMT -5) on 12 Nov 2021]