The DFE PRACH core uses a dual-modulus CORDIC-based NCO to provide direct digital synthesis for a wide range of demodulation frequencies with zero frequency error. The NCOs can be configured via the API. The core is configured for operation with two, four, eight or sixteen mixer clocks per sample, the sample frequency of the mixer and NCOs (fs ) will be equal to clock frequency divided by the clocks per sample (fclk/CPS).
A simple N-bit single-modulus phase accumulator produces a ramp signal with an increment known as the Frequency Control Word (FCW), which is calculated as follows:
In the DFE PRACH core, Nbits is equal to 32. The output of the 32-bit phase accumulator is rounded to 18 bits. An 18-bit phase offset is then applied, and the result is fed to a CORDIC block which converts it into a complex sinusoid with the corresponding frequency and phase.
The phase ramp will have frequency of exactly fNCO when FCWideal is an integer and no rounding is required (FCW ≡ FCWideal). This situation is shown in the following figure, where Tclk is 1/fs and Tnco is 1/fNCO.
However, when FCWideal is not an integer, a frequency error results due to the quantization error in the value of FCW. For applications such as wireless communications with high accuracy and long runtime requirements, the dual modulus NCO can prevent this frequency error from occurring.
In a dual-modulus phase accumulator, the phase increment on any given fs clock cycle may take the value of FCW or FCW+1. The ratio of cycles which use FCW to cycles which use FCW+1 is equal to the fractional part of FCWideal. Provided that the fractional part is a rational quantity, the quantization error of FCW is exactly canceled out by the quantization error of FCW+1 over the dual-modulus cycle and the effective FCW thus achieved is equal to FCWideal. This eliminates the frequency error.
The scheme is illustrated in the following figure.
The dual-modulus cycle is encoded in two integers T and S. In a period of T fs cycles, the phase will increment by FCW for S cycles, and FCW+1 for (T-S) cycles. The ratio S/T is computed as follows:
The core allows S, T, and FCW to be specified to 32-bit precision. If the dual-modulus functionality is not required (because the FCW calculation is exact), S and T should be set to 0.
If operating in Dynamic Mode, these values correspond to the last three fields in the DCP, which allow the Dynamic request to specify the demodulation frequency.
In Static Mode, to fully specify the NCO frequency, the API includes the
structure XDfePrach_NCO
, which contains the fields
Frequency, FreqSingleModCount, and FreqDualModCount. In this structure: Frequency is the
value FCW as calculated above; FreqSingleModCount is the number of cycles for which FCW
applies(S); and FreqDualModCount is the number of cycles for which FCW+1
applies(T-S).
The frequency for PRACH extraction is often specified as an integer
multiple of half the sub-carrier spacing of the PRACH channel. The API allows the
extraction frequency to be specified as an integer number of half sub-carriers. The
function XDfePrach_FreqCalculation
is provided, which will convert from
the half sub-carrier definition to the correct NCO FCW, single modulus and dual modulus
settings.
The following source code example shows how to create a simple PRACH NCO configuration based on an integer number of half sub carriers.
/* Declare NCO configuration structure */
XDfePrach_NCO NCOCfg = { 0 };
double Demod_Freq = 100; //In half PRACH Subcarriers.
NCOCfg.UserFreq = Demod_Freq;
/*NCO Gain can also be specified */
NCOCfg.NcoGain = 1; // 0 = 0dB, 1 = -3dB, 2 = -6dB, 3 = -9dB.
In the above code, The NCOCfg
structure is given a frequency
offset based on a number of half PRACH sub-carriers. It is also possible to bypass the
requirement to use a number of half sub-carriers and directly specify the NCO's FCW,
Single Mod, and Dual Mod settings. This is illustrated in the following code.
/* Declare NCO configuration structure */
XDfePrach_NCO NCOCfg = { 0 };
double PRACH_FCW_OVERWRITE = 50; // 32Bit alternative FCW value
double PRACH_SINGLE_MOD_OVERWRITE = 4; // 32Bit alternative Single Mod value
double PRACH_DUAL_MOD_OVERWRITE = 1; // 32Bit alternative Dual Mod value
/*combined OVERWRITE constants give phase increment equivalent to 50.2 = 50+1/(1+4) */
/*original frequency being set:*/
NCOCfg.UserFreq = Demod_Freq;
/*Calling Frequency Calculation will calculate NCO settings based on RCCfg(RACH Config) and CCRate(sample rate)*/
XDfePrach_FreqCalculation(InstancePtr, RCId, CCId, RCCfg, &NcoCfg, CCRate);
cout << "FCW: " <<NcoCfg.Frequency<< ", Single Mod CC: "<< NcoCfg.FreqSingleModCount << "Dual Mod CC: "<< NcoCfg.FreqDualModCount<<endl;
/*A call to XDfePrach_FreqCalculation is embedded in the XDfePrach_AddRC function:*/
XDfePrach_AddRC(InstancePtr, RCId, RachChan, CCID, RCCfg, DdcCfg, NcoCfg,Schedule,NextCCCfg, BandId);
/*This means the above call to XDfePrach_FreqCalculation is usually unnecessary and is included for illustration. If an alternative demodulation frequency is required, the values in NcoCfg can be overwritten after calling XDfePrach_AddRC and prior to calling XDfePrach_SetNextCfg */
RCCfg->NcoCfg[RCId]->Frequency = PRACH_FCW_OVERWRITE;
RCCfg->NcoCfg[RCId]->FreqSingleModCount = PRACH_SINGLE_MOD_OVERWRITE;
RCCfg->NcoCfg[RCId]->FreqDualModCount = PRACH_DUAL_MOD_OVERWRITE;
/*Commit the overwritten frequency settings to the registers*/
XDfePrach_SetNextCfg(InstancePtr, &CCCfg, &RCCfg);
The phase offset of the NCO can also be initialized to a known value via the API. Initialization should be to an unsigned 18-bit number representing a fraction of 2π. It is also possible to apply a gain reduction to the output of the NCO mixer. Values of 0 dB, -3 dB, -6 dB, and -9 dB are available.
RCCfg->NcoCfg[RCId].PhaseOffset = 0x10000; // π/2 phase offset
RCCfg->NcoCfg[RCId].NcoGain = 2; // -6dB gain on the NCO output
The XDfePrach_NCO
structure is used by
all functions that manipulate the NCO parameters.