NCO Configuration - NCO Configuration - 2.0 English - PG391

RFSoC DFE PRACH LogiCORE IP Product Guide (PG391)

Document ID
PG391
Release Date
2025-11-26
Version
2.0 English

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. You can configure the NCOs using the API. The core is configured to operate with two, four, eight or sixteen mixer clocks per sample. The sample frequency of the mixer and NCOs (fs ) is equal to the 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). FCW is calculated as follows:





The demodulation frequency f is within [-fs/2,+fs/2]. FCW is an unsigned integer. Consequently, fNCO must be set equal to f if f is positive, otherwise it must be set equal to f+fs. In the DFE PRACH core, Nbits is equal to 32. The core rounds the output of the 32-bit phase accumulator to 18 bits. Next, it applies an 18-bit phase offset and feeds the result to a CORDIC block. The CORDIC block converts it into a complex sinusoid with the corresponding frequency and phase.

The phase ramp has a 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.

Figure 1. Simple Single-modulus NCO Phase Accumulator

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 can 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. If the fractional part is rational, the FCW quantization error is canceled out by the (FCW+1) quantization error over the dual-modulus cycle. The effective FCW thus achieved is equal to FCWideal. This eliminates the frequency error.



The following figure illustrates this scheme.

Figure 2. Dual-modulus NCO Parameters

Two integers, T and S, encode the dual-modulus cycle. In a period of T fs cycles, the phase is incremented by FCW for S cycles, and FCW+1 for (T-S) cycles. The ratio S/T is computed as follows:



The core allows 32-bit precision for S, T, and FCW. If you do not require the dual-modulus functionality (because the FCW calculation is exact), set S and T to 0.

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. This 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 subcarrier spacing of the PRACH channel. The function XDfePrach_FreqCalculation is called automatically from XDfePrach_AddRC. This converts from the half subcarrier value set in the UserFreq field of the XDfePrach_NCO structure (center of PRACH channel) to the correct NCO FCW, single modulus, and dual modulus settings.

To force alternative FCW, single modulus and dual modulus, you must overwrite them in the XDfePrach_NCO structure after the call to XDfePrach_AddRC.

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 Channel_Freq = 100; // In half PRACH Subcarriers.

NCOCfg.UserFreq = Channel_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 subcarriers. This frequency offset (UserFreq field of the XDfePrach_NCO structure), represents the offset between the center of the CC bandwidth and the center of the PRACH channel. 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. The following code illustrates this.

/* 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 = Channel_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_AddRCtoRCCfgMB function:*/
XDfePrach_AddRCtoRCCfgMB(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_AddRCtoRCCfgMB 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);

You can also use the API to initialize the phase offset of the NCO to a known value. Initialization should be 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

All functions that manipulate the NCO parameters use the XDfePrach_NCO configuration structure.