Segregate the LLR and channel compensation related common functions into different files

Signed-off-by: Rupanjali <rupanjali.srivastava@openairinterface.org>
This commit is contained in:
Rupanjali
2026-05-12 11:14:51 -04:00
parent d6b40a607f
commit 806f8d745b
7 changed files with 2698 additions and 2556 deletions

View File

@@ -1,6 +1,8 @@
# SPDX-License-Identifier: LicenseRef-CSSL-1.0
add_library(nr_phy_common src/nr_phy_common.c)
add_library(nr_phy_common src/nr_phy_common.c
src/nr_channel_compensation.c
src/nr_compute_llr.c)
target_link_libraries(nr_phy_common PRIVATE UTIL PHY_COMMON)
target_include_directories(nr_phy_common PUBLIC inc/)

View File

@@ -0,0 +1,47 @@
/*
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
*/
#ifndef __NR_CHANNEL_COMPENSATION__H__
#define __NR_CHANNEL_COMPENSATION__H__
#include "PHY/impl_defs_top.h"
/**
* @brief Common channel compensation function shared by DL (PDSCH) and UL (PUSCH) paths.
*
* Computes matched-filter output (rxComp) and channel magnitude arrays used for LLR
* computation. MRC across Rx antennas is performed inline: for each layer, contributions
* from all Rx antennas are accumulated into rxComp[layer * nb_rx_ant][symbol * buffer_length].
* Uses AVX2 (256-bit SIMD) for throughput.
*
* @param buffer_length Number of complex samples per symbol (must be a multiple of 8)
* @param nb_rx_ant Number of Rx antennas
* @param nb_layers Number of spatial layers
* @param rxFext Extracted received signal [nb_rx_ant][buffer_length]
* @param chFext Extracted channel estimates [nb_layers][nb_rx_ant][buffer_length]
* @param ch_maga Output magnitude array for threshold 'a' [nb_layers][buffer_length]
* @param ch_magb Output magnitude array for threshold 'b' [nb_layers][buffer_length]
* @param ch_magc Output magnitude array for threshold 'c' [nb_layers][buffer_length]
* @param rxComp Output compensated signal; row [l * nb_rx_ant] holds the MRC result
* for layer l at offset [symbol * buffer_length]
* @param rho Tx-correlation matrix [nb_layers][nb_layers][buffer_length], or NULL
* @param mod_order Modulation order (2=QPSK, 4=16QAM, 6=64QAM, 8=256QAM)
* @param symbol OFDM symbol index (used to compute offset into rxComp rows)
* @param output_shift Right-shift applied after each complex multiply
*/
void nr_channel_compensation(uint32_t buffer_length,
int nb_rx_ant,
int nb_layers,
c16_t rxFext[nb_rx_ant][buffer_length],
c16_t chFext[nb_layers][nb_rx_ant][buffer_length],
c16_t **ch_maga,
c16_t **ch_magb,
c16_t **ch_magc,
c16_t **rxComp,
c16_t (*rho)[nb_layers][buffer_length],
int mod_order,
uint32_t symbol,
uint32_t output_shift);
#endif /* __NR_CHANNEL_COMPENSATION__H__ */

View File

@@ -0,0 +1,141 @@
/*
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
*/
#ifndef __NR_COMPUTE_LLR__H__
#define __NR_COMPUTE_LLR__H__
#include "PHY/impl_defs_top.h"
/**
* @brief Per-layer LLR dispatch for single-stream or post-MMSE output.
*
* Selects the appropriate single-layer LLR function based on modulation order.
*
* @param rxdataF_comp Compensated received signal
* @param ch_mag Channel magnitude (threshold a), used for 16/64/256QAM
* @param ch_magb Channel magnitude (threshold b), used for 64/256QAM
* @param ch_magc Channel magnitude (threshold c), used for 256QAM
* @param llr Output LLR buffer
* @param nb_re Number of resource elements
* @param symbol OFDM symbol index (used only in AssertFatal message)
* @param mod_order Modulation order (2=QPSK, 4=16QAM, 6=64QAM, 8=256QAM)
*/
void nr_compute_llr(c16_t *rxdataF_comp,
c16_t *ch_mag,
c16_t *ch_magb,
c16_t *ch_magc,
int16_t *llr,
uint32_t nb_re,
uint8_t symbol,
uint8_t mod_order);
/**
* @brief 2-layer ML-LLR for QPSK×QPSK.
*
* Computes LLRs for stream 0 in the presence of interference from stream 1,
* both modulated with QPSK. Uses 128-bit SIMD on aarch64, 256-bit AVX2 elsewhere.
*
* @param stream0_in MF output for stream 0: y0' = h0'·y0
* @param stream1_in MF output for stream 1: y1' = h1'·y0
* @param stream0_out Output LLRs for stream 0
* @param rho01 Channel cross-correlation: rho01 = h0'·h1
* @param length Number of resource elements
*/
void nr_qpsk_llr_2layer(c16_t *stream0_in, c16_t *stream1_in,
int16_t *stream0_out, c16_t *rho01, uint32_t length);
/**
* @brief 2-layer ML-LLR for 16QAM×16QAM.
*
* @param stream0_in MF output for stream 0
* @param stream1_in MF output for stream 1
* @param ch_mag Channel magnitude for stream 0
* @param ch_mag_i Channel magnitude for stream 1
* @param stream0_out Output LLRs for stream 0
* @param rho01 Channel cross-correlation
* @param length Number of resource elements
*/
void nr_qam16_llr_2layer(c16_t *stream0_in, c16_t *stream1_in,
c16_t *ch_mag, c16_t *ch_mag_i,
int16_t *stream0_out, c16_t *rho01, uint32_t length);
/**
* @brief 2-layer ML-LLR for 64QAM×64QAM.
*
* @param stream0_in MF output for stream 0
* @param stream1_in MF output for stream 1
* @param ch_mag Channel magnitude (threshold a) for stream 0
* @param ch_mag_i Channel magnitude (threshold a) for stream 1
* @param stream0_out Output LLRs for stream 0
* @param rho01 Channel cross-correlation
* @param length Number of resource elements
*/
void nr_qam64_llr_2layer(c16_t *stream0_in, c16_t *stream1_in,
c16_t *ch_mag, c16_t *ch_mag_i,
int16_t *stream0_out, c16_t *rho01, uint32_t length);
/**
* @brief Compute ML LLRs for both streams of a 2-layer MIMO transmission.
*
* Dispatches to nr_qpsk_llr_2layer / nr_qam16_llr_2layer / nr_qam64_llr_2layer
* for each stream in sequence.
*
* @param rxdataF_comp0 Compensated received signal for layer 0
* @param rxdataF_comp1 Compensated received signal for layer 1
* @param ch_mag0 Channel magnitude for layer 0
* @param ch_mag1 Channel magnitude for layer 1
* @param llr_layers0 Output LLR buffer for layer 0
* @param llr_layers1 Output LLR buffer for layer 1
* @param rho0 Cross-correlation rho[0][1]
* @param rho1 Cross-correlation rho[1][0]
* @param nb_re Number of resource elements
* @param mod_order Modulation order (2=QPSK, 4=16QAM, 6=64QAM)
*/
void nr_compute_ML_llr(c16_t *rxdataF_comp0,
c16_t *rxdataF_comp1,
c16_t *ch_mag0,
c16_t *ch_mag1,
int16_t *llr_layers0,
int16_t *llr_layers1,
c16_t *rho0,
c16_t *rho1,
uint32_t nb_re,
uint8_t mod_order);
/**
* @brief MMSE equalizer for 2-layer MIMO, followed by per-layer LLR computation.
*
* Performs 2×2 MMSE equalization, updates ch_mag arrays with post-equalization
* effective SNR, and computes per-layer LLRs via nr_compute_llr.
*
* @param rxdataF_comp Compensated received signal [nl * nb_rx_ant][buffer_length]
* @param buffer_length Samples per symbol
* @param nb_rx_ant Number of Rx antennas
* @param ch_mag Channel magnitude threshold 'a' [2][buffer_length]
* @param ch_magb Channel magnitude threshold 'b' [2][buffer_length]
* @param ch_magc Channel magnitude threshold 'c' [2][buffer_length]
* @param ch_estimates_ext Channel estimates [2][nb_rx_ant][buffer_length]
* @param nb_rb Number of allocated resource blocks
* @param mod_order Modulation order
* @param shift Right-shift for internal fixed-point arithmetic
* @param symbol OFDM symbol index
* @param length Number of resource elements to process
* @param noise_var Noise variance estimate
* @return 0 on success
*/
uint8_t nr_mmse_2layers(c16_t **rxdataF_comp,
uint32_t buffer_length,
int nb_rx_ant,
c16_t **ch_mag,
c16_t **ch_magb,
c16_t **ch_magc,
c16_t ch_estimates_ext[][nb_rx_ant][buffer_length],
unsigned short nb_rb,
unsigned char mod_order,
int shift,
unsigned char symbol,
int length,
uint32_t noise_var);
#endif /* __NR_COMPUTE_LLR__H__ */

View File

@@ -388,80 +388,6 @@ bool check_rb_in_bitmap(const freq_alloc_bitmap_t *alloc, int rb);
freq_alloc_bitmap_t set_start_end_from_bitmap(int size, int alloc_size, const uint8_t bitmap[alloc_size]);
freq_alloc_bitmap_t set_bitmap_from_start_size(int start, int size);
/**
* @brief Common channel compensation function shared by DL (PDSCH) and UL (PUSCH) paths.
*
* Computes matched-filter output (rxComp) and channel magnitude arrays used for LLR
* computation. MRC across Rx antennas is performed inline: for each layer, contributions
* from all Rx antennas are accumulated into rxComp[layer * nb_rx_ant][symbol * buffer_length].
* Uses AVX2 (256-bit SIMD) for throughput.
*
* @param buffer_length Number of complex samples per symbol (must be a multiple of 8)
* @param nb_rx_ant Number of Rx antennas
* @param nb_layers Number of spatial layers
* @param rxFext Extracted received signal [nb_rx_ant][buffer_length]
* @param chFext Extracted channel estimates [nb_layers][nb_rx_ant][buffer_length]
* @param ch_maga Output magnitude array for threshold 'a' [nb_layers][buffer_length]
* @param ch_magb Output magnitude array for threshold 'b' [nb_layers][buffer_length]
* @param ch_magc Output magnitude array for threshold 'c' [nb_layers][buffer_length]
* @param rxComp Output compensated signal; row [l * nb_rx_ant] holds the MRC result
* for layer l at offset [symbol * buffer_length]
* @param rho Tx-correlation matrix [nb_layers][nb_layers][buffer_length], or NULL
* @param mod_order Modulation order (2=QPSK, 4=16QAM, 6=64QAM, 8=256QAM)
* @param symbol OFDM symbol index (used to compute offset into rxComp rows)
* @param output_shift Right-shift applied after each complex multiply
*/
void nr_channel_compensation(uint32_t buffer_length,
int nb_rx_ant,
int nb_layers,
c16_t rxFext[nb_rx_ant][buffer_length],
c16_t chFext[nb_layers][nb_rx_ant][buffer_length],
c16_t **ch_maga,
c16_t **ch_magb,
c16_t **ch_magc,
c16_t **rxComp,
c16_t (*rho)[nb_layers][buffer_length],
int mod_order,
uint32_t symbol,
uint32_t output_shift);
void nr_compute_llr(c16_t *rxdataF_comp,
c16_t *ch_mag,
c16_t *ch_magb,
c16_t *ch_magc,
int16_t *llr,
uint32_t nb_re,
uint8_t symbol,
uint8_t mod_order);
void nr_qpsk_llr_2layer(c16_t *stream0_in, c16_t *stream1_in,
int16_t *stream0_out, c16_t *rho01, uint32_t length);
void nr_qam16_llr_2layer(c16_t *stream0_in, c16_t *stream1_in,
c16_t *ch_mag, c16_t *ch_mag_i,
int16_t *stream0_out, c16_t *rho01, uint32_t length);
void nr_qam64_llr_2layer(c16_t *stream0_in, c16_t *stream1_in,
c16_t *ch_mag, c16_t *ch_mag_i,
int16_t *stream0_out, c16_t *rho01, uint32_t length);
void nr_compute_ML_llr(c16_t *rxdataF_comp0, c16_t *rxdataF_comp1,
c16_t *ch_mag0, c16_t *ch_mag1,
int16_t *llr_layers0, int16_t *llr_layers1,
c16_t *rho0, c16_t *rho1,
uint32_t nb_re, uint8_t mod_order);
uint8_t nr_mmse_2layers(c16_t **rxdataF_comp,
uint32_t buffer_length,
int nb_rx_ant,
c16_t **ch_mag,
c16_t **ch_magb,
c16_t **ch_magc,
c16_t ul_ch_estimates_ext[][nb_rx_ant][buffer_length],
unsigned short nb_rb,
unsigned char mod_order,
int shift,
unsigned char symbol,
int length,
uint32_t noise_var);
#include "nr_channel_compensation.h"
#include "nr_compute_llr.h"
#endif

View File

@@ -0,0 +1,114 @@
/*
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
*/
#include "nr_channel_compensation.h"
#include "bits.h"
#include <complex.h>
#include "PHY/sse_intrin.h"
#include "PHY/impl_defs_top.h"
#ifdef __aarch64__
#define USE_128BIT
#endif
void nr_channel_compensation(uint32_t buffer_length,
int nb_rx_ant,
int nb_layers,
c16_t rxFext[nb_rx_ant][buffer_length],
c16_t chFext[nb_layers][nb_rx_ant][buffer_length],
c16_t **ch_maga,
c16_t **ch_magb,
c16_t **ch_magc,
c16_t **rxComp,
c16_t (*rho)[nb_layers][buffer_length],
int mod_order,
uint32_t symbol,
uint32_t output_shift)
{
simde__m256i QAM_ampa_256 = simde_mm256_setzero_si256();
simde__m256i QAM_ampb_256 = simde_mm256_setzero_si256();
simde__m256i QAM_ampc_256 = simde_mm256_setzero_si256();
if (mod_order == 4) {
QAM_ampa_256 = simde_mm256_set1_epi16(QAM16_n1);
} else if (mod_order == 6) {
QAM_ampa_256 = simde_mm256_set1_epi16(QAM64_n1);
QAM_ampb_256 = simde_mm256_set1_epi16(QAM64_n2);
} else if (mod_order == 8) {
QAM_ampa_256 = simde_mm256_set1_epi16(QAM256_n1);
QAM_ampb_256 = simde_mm256_set1_epi16(QAM256_n2);
QAM_ampc_256 = simde_mm256_set1_epi16(QAM256_n3);
}
for (int aatx = 0; aatx < nb_layers; aatx++) {
simde__m256i *rxComp_256 = (simde__m256i *)&rxComp[aatx * nb_rx_ant][symbol * buffer_length];
simde__m256i *ch_maga_256 = (simde__m256i *)ch_maga[aatx];
simde__m256i *ch_magb_256 = (simde__m256i *)ch_magb[aatx];
simde__m256i *ch_magc_256 = (simde__m256i *)ch_magc[aatx];
// First Rx antenna: direct store — eliminates need to pre memset the output buffers
{
simde__m256i *rxF_256 = (simde__m256i *)rxFext[0];
simde__m256i *chF_256 = (simde__m256i *)chFext[aatx][0];
for (int i = 0; i < (int)(buffer_length >> 3); i++) {
rxComp_256[i] = oai_mm256_cpx_mult_conj(chF_256[i], rxF_256[i], output_shift);
if (mod_order > 2) {
simde__m256i mag = oai_mm256_smadd(chF_256[i], chF_256[i], output_shift);
mag = simde_mm256_packs_epi32(mag, mag);
mag = simde_mm256_unpacklo_epi16(mag, mag);
ch_maga_256[i] = simde_mm256_mulhrs_epi16(mag, QAM_ampa_256);
if (mod_order > 4)
ch_magb_256[i] = simde_mm256_mulhrs_epi16(mag, QAM_ampb_256);
if (mod_order > 6)
ch_magc_256[i] = simde_mm256_mulhrs_epi16(mag, QAM_ampc_256);
}
}
if (rho) {
for (int atx = 0; atx < nb_layers; atx++) {
simde__m256i *rho_256 = (simde__m256i *)rho[aatx][atx];
simde__m256i *chF2_256 = (simde__m256i *)chFext[atx][0];
for (int i = 0; i < (int)(buffer_length >> 3); i++)
rho_256[i] = oai_mm256_cpx_mult_conj(chF_256[i], chF2_256[i], output_shift);
}
}
}
// Remaining Rx antennas: accumulate (MRC)
for (int aarx = 1; aarx < nb_rx_ant; aarx++) {
simde__m256i *rxF_256 = (simde__m256i *)rxFext[aarx];
simde__m256i *chF_256 = (simde__m256i *)chFext[aatx][aarx];
for (int i = 0; i < (int)(buffer_length >> 3); i++) {
simde__m256i comp = oai_mm256_cpx_mult_conj(chF_256[i], rxF_256[i], output_shift);
rxComp_256[i] = simde_mm256_add_epi16(rxComp_256[i], comp);
if (mod_order > 2) {
simde__m256i mag = oai_mm256_smadd(chF_256[i], chF_256[i], output_shift);
mag = simde_mm256_packs_epi32(mag, mag);
mag = simde_mm256_unpacklo_epi16(mag, mag);
ch_maga_256[i] = simde_mm256_add_epi16(ch_maga_256[i], simde_mm256_mulhrs_epi16(mag, QAM_ampa_256));
if (mod_order > 4)
ch_magb_256[i] = simde_mm256_add_epi16(ch_magb_256[i], simde_mm256_mulhrs_epi16(mag, QAM_ampb_256));
if (mod_order > 6)
ch_magc_256[i] = simde_mm256_add_epi16(ch_magc_256[i], simde_mm256_mulhrs_epi16(mag, QAM_ampc_256));
}
}
if (rho) {
for (int atx = 0; atx < nb_layers; atx++) {
simde__m256i *rho_256 = (simde__m256i *)rho[aatx][atx];
simde__m256i *chF2_256 = (simde__m256i *)chFext[atx][aarx];
for (int i = 0; i < (int)(buffer_length >> 3); i++)
rho_256[i] = simde_mm256_adds_epi16(rho_256[i], oai_mm256_cpx_mult_conj(chF_256[i], chF2_256[i], output_shift));
}
}
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff