phy: do pusch rx beamforming

Signed-off-by: Sakthivel Velumani <s.velumani@northeastern.edu>
This commit is contained in:
Sakthivel Velumani
2026-04-29 21:19:00 +00:00
parent 609abd70f1
commit 68e95df555
8 changed files with 96 additions and 42 deletions

View File

@@ -216,6 +216,7 @@ static size_t dump_L1_meas_stats(PHY_VARS_gNB *gNB, RU_t *ru, char *output, size
output += print_meas_log(&gNB->dlsch_modulation_stats, "DLSCH modulation", NULL, NULL, output, end - output);
output += print_meas_log(&gNB->dlsch_pdsch_generation_stats, "PDSCH generation", NULL, NULL, output, end - output);
output += print_meas_log(&gNB->phy_proc_rx, "L1 Rx processing", NULL, NULL, output, end - output);
output += print_meas_log(&gNB->pusch_rx_beamforming, "PUSCH Rx Beamforming", NULL, NULL, output, end - output);
output += print_meas_log(&gNB->ts_deinterleave, "UL segment deinterleaving", NULL, NULL, output, end - output);
output += print_meas_log(&gNB->ts_rate_unmatch, "UL segment rate recovery", NULL, NULL, output, end - output);
output += print_meas_log(&gNB->ts_ldpc_decode, "UL segments decoding", NULL, NULL, output, end - output);
@@ -282,6 +283,7 @@ void *nrL1_stats_thread(void *param) {
reset_meas(&gNB->ts_ldpc_decode);
reset_meas(&gNB->ul_indication_stats);
reset_meas(&gNB->slot_indication_stats);
reset_meas(&gNB->pusch_rx_beamforming);
reset_meas(&gNB->rx_pusch_stats);
reset_meas(&gNB->dlsch_scrambling_stats);
reset_meas(&gNB->dlsch_modulation_stats);

View File

@@ -32,7 +32,6 @@ typedef struct puschAntennaProc_s {
unsigned char symbol;
unsigned short bwp_start_subcarrier;
int aarx;
uint16_t ant_port_start;
int numAntennas;
const nfapi_nr_pusch_pdu_t *pusch_pdu;
int *max_ch;
@@ -94,28 +93,19 @@ static void nr_pusch_antenna_processing(void *arg)
c16_t **ul_ch_estimates = (c16_t **)pusch_vars->ul_ch_estimates;
NR_DL_FRAME_PARMS *frame_parms = rdata->frame_parms;
const int symbolSize = frame_parms->ofdm_symbol_size;
const int slot_offset = (Ns % RU_RX_SLOT_DEPTH) * frame_parms->symbols_per_slot * symbolSize;
const int delta = get_delta(p, pusch_pdu->dmrs_config_type);
const int symbol_offset = symbolSize * symbol;
const int k0 = bwp_start_subcarrier;
const int nb_rb_pusch = pusch_pdu->rb_size;
const int aa_start = rdata->ant_port_start;
const uint8_t num_sp_streams = rdata->pusch_pdu->param_v4.numSpatialStreamIndices;
for (int antenna = aarx; antenna < aarx + numAntennas; antenna++) {
c16_t ul_ls_est[symbolSize] __attribute__((aligned(32)));
memset(ul_ls_est, 0, sizeof(c16_t) * symbolSize);
c16_t *rxdataF = (c16_t *)&rdata->rxdataF[aa_start + antenna][symbol_offset + slot_offset];
c16_t *rxdataF = (c16_t *)&rdata->rxdataF[antenna][symbol_offset];
c16_t *ul_ch = &ul_ch_estimates[nl * num_sp_streams + antenna][symbol_offset];
memset(ul_ch, 0, sizeof(*ul_ch) * symbolSize);
LOG_D(PHY,
"symbol_offset %d, slot_offset %d, OFDM size %d, Ns = %d, k0 = %d, symbol %d\n",
symbol_offset,
slot_offset,
symbolSize,
Ns,
k0,
symbol);
LOG_D(PHY, "symbol_offset %d, OFDM size %d, Ns = %d, k0 = %d, symbol %d\n", symbol_offset, symbolSize, Ns, k0, symbol);
#ifdef DEBUG_PUSCH
LOG_I(PHY, "symbol_offset %d, delta %d\n", symbol_offset, delta);
@@ -448,12 +438,12 @@ static void nr_pusch_antenna_processing(void *arg)
}
int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB,
c16_t **rxdataF,
unsigned char Ns,
int nl,
unsigned short p,
unsigned char symbol,
NR_gNB_PUSCH *pusch_vars,
uint16_t ant_port_start,
unsigned short bwp_start_subcarrier,
const nfapi_nr_pusch_pdu_t *pusch_pdu,
int *max_ch,
@@ -577,11 +567,10 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB,
rdata->nest_count = &nest_count_arr[rdata->aarx];
rdata->noise_amp2 = &noise_amp2_arr[rdata->aarx];
rdata->delay = &delay_arr[rdata->aarx];
rdata->ant_port_start = ant_port_start;
rdata->frame_parms = fp;
rdata->pusch_vars = pusch_vars;
rdata->chest_freq = gNB->chest_freq;
rdata->rxdataF = gNB->common_vars.rxdataF;
rdata->rxdataF = rxdataF;
rdata->scope = gNB->scopeData;
rdata->ans = &ans;
rdata->pusch_ch_est_dmrs_pos_slot_mem = pusch_ch_est_dmrs_pos_slot_mem;

View File

@@ -25,12 +25,12 @@
*/
int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB,
c16_t **rxdataF,
unsigned char Ns,
int nl,
unsigned short p,
unsigned char symbol,
NR_gNB_PUSCH *pusch_vars,
uint16_t ant_port_start,
unsigned short bwp_start_subcarrier,
const nfapi_nr_pusch_pdu_t *pusch_pdu,
int *max_ch,

View File

@@ -817,7 +817,6 @@ static void inner_rx(PHY_VARS_gNB *gNB,
const nfapi_nr_pusch_pdu_t *rel15_ul,
c16_t **rxF,
int16_t **llr,
int soffset,
int symbol,
int output_shift,
uint32_t nvar,
@@ -847,9 +846,9 @@ static void inner_rx(PHY_VARS_gNB *gNB,
(c16_t *)pusch_vars->ul_ch_estimates[aatx * nb_rx_ant + aarx],
rxFext[aarx],
chFext[aatx][aarx],
soffset+(symbol * frame_parms->ofdm_symbol_size),
symbol * frame_parms->ofdm_symbol_size,
dmrs_symbol * frame_parms->ofdm_symbol_size,
dmrs_symbol_flag,
dmrs_symbol_flag,
rel15_ul,
frame_parms);
#if T_TRACER
@@ -965,11 +964,11 @@ typedef struct puschSymbolProc_s {
int slot;
int startSymbol;
int numSymbols;
c16_t **rxdataF;
int16_t *llr;
int16_t *scramblingSequence;
uint32_t nvar;
// TODO: Remove assumption of contiguous ports after DAS is properly handled in beamforming
uint16_t ant_port_start;
task_ans_t *ans;
c16_t *pusch_ch_est_dmrs_interpl_slot_mem;
c16_t *rxFext_slot_mem;
@@ -987,7 +986,6 @@ static void nr_pusch_symbol_processing(void *arg)
for (int symbol = rdata->startSymbol; symbol < rdata->startSymbol + rdata->numSymbols; symbol++) {
if (pusch_vars->ul_valid_re_per_slot[symbol] == 0)
continue;
int soffset = (slot % RU_RX_SLOT_DEPTH) * frame_parms->symbols_per_slot * frame_parms->ofdm_symbol_size;
int buffer_length = ceil_mod(pusch_vars->ul_valid_re_per_slot[symbol] * NR_NB_SC_PER_RB, 16);
int16_t llrs[rel15_ul->nrOfLayers][ceil_mod(buffer_length * rel15_ul->qam_mod_order, 64)];
int16_t *llrss[rel15_ul->nrOfLayers];
@@ -999,9 +997,8 @@ static void nr_pusch_symbol_processing(void *arg)
frame_parms,
pusch_vars,
rel15_ul,
gNB->common_vars.rxdataF + rdata->ant_port_start,
rdata->rxdataF,
llrss,
soffset,
symbol,
pusch_vars->log2_maxh,
rdata->nvar,
@@ -1062,8 +1059,6 @@ int nr_rx_pusch_tp(PHY_VARS_gNB *gNB,
uint8_t slot)
{
NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms;
const nfapi_nr_spatial_stream_index_t *p = &rel15_ul->param_v4;
uint16_t ant_port_start = p->numSpatialStreamIndices > 0 ? p->spatialStreamIndices[0] : 0;
uint32_t bwp_start_subcarrier = (rel15_ul->rb_start + rel15_ul->bwp_start) * NR_NB_SC_PER_RB;
LOG_D(PHY,"pusch %d.%d : bwp_start_subcarrier %d, rb_start %d, first_carrier_offset %d\n", frame,slot,bwp_start_subcarrier, rel15_ul->rb_start, frame_parms->first_carrier_offset);
@@ -1102,6 +1097,45 @@ int nr_rx_pusch_tp(PHY_VARS_gNB *gNB,
memset(rxFext_slot_mem, 0, sizeof(c16_t) * buffer_length_slot * 1 * 1);
#endif
//----------------------------------------------------------
//------------------- Beamforming --------------------------
//----------------------------------------------------------
start_meas(&gNB->pusch_rx_beamforming);
const nfapi_nr_spatial_stream_index_t *p = &rel15_ul->param_v4;
const nfapi_nr_ul_beamforming_t *bf = &rel15_ul->beamforming;
const bool do_bf = (bf->dig_bf_interface > 0) ? !IS_BIT_SET(bf->prgs_list[0].dig_bf_interface_list[0].beam_idx, 15) : false;
const int num_sp_streams = do_bf ? bf->dig_bf_interface : p->numSpatialStreamIndices;
const int num_sc = frame_parms->samples_per_slot_wCP;
c16_t *rxdataF[num_sp_streams];
c16_t *toFree = NULL;
if (do_bf) {
// Allocate buffer for DBF output
c16_t *bf_out[num_sp_streams];
toFree = calloc(1, num_sp_streams * num_sc * sizeof(*toFree));
for (int i = 0; i < num_sp_streams; i++)
bf_out[i] = &toFree[i * num_sc];
// temporary array to hold bb ports buffer
c16_t *bf_in[frame_parms->nb_antennas_rx];
for (int i = 0; i < frame_parms->nb_antennas_rx; i++)
bf_in[i] =
gNB->common_vars.rxdataF[i] + (slot % RU_RX_SLOT_DEPTH) * frame_parms->symbols_per_slot * frame_parms->ofdm_symbol_size;
// beamform symbols
for (int sym = rel15_ul->start_symbol_index; sym < rel15_ul->start_symbol_index + rel15_ul->nr_of_symbols; sym++) {
const int sc_start = sym * frame_parms->ofdm_symbol_size + ((rel15_ul->bwp_start + rel15_ul->rb_start) * NR_NB_SC_PER_RB);
const int num_sc = rel15_ul->rb_size * NR_NB_SC_PER_RB;
nr_rx_beamforming(&gNB->gNB_config.dbt_config, bf, bf_in, bf_out, frame_parms->nb_antennas_rx, sc_start, num_sc);
}
// point BF output buffer to use by following PUSCH functions
for (int i = 0; i < num_sp_streams; i++)
rxdataF[i] = bf_out[i];
} else {
// No BF. point to input buffer
for (int i = 0; i < num_sp_streams; i++)
rxdataF[i] = gNB->common_vars.rxdataF[p->spatialStreamIndices[i]]
+ (slot % RU_RX_SLOT_DEPTH) * frame_parms->symbols_per_slot * frame_parms->ofdm_symbol_size;
}
stop_meas(&gNB->pusch_rx_beamforming);
//----------------------------------------------------------
//------------------- Channel estimation -------------------
//----------------------------------------------------------
@@ -1117,12 +1151,12 @@ int nr_rx_pusch_tp(PHY_VARS_gNB *gNB,
for (int nl = 0; nl < rel15_ul->nrOfLayers; nl++) {
uint32_t nvar_tmp = 0;
nr_pusch_channel_estimation(gNB,
rxdataF,
slot,
nl,
get_dmrs_port(nl, rel15_ul->dmrs_ports),
symbol,
pusch_vars,
ant_port_start,
bwp_start_subcarrier,
rel15_ul,
&max_ch,
@@ -1134,7 +1168,6 @@ int nr_rx_pusch_tp(PHY_VARS_gNB *gNB,
}
}
const uint8_t num_sp_streams = rel15_ul->param_v4.numSpatialStreamIndices;
nvar /= (rel15_ul->nr_of_symbols * rel15_ul->nrOfLayers * num_sp_streams);
allocCast2D(n0_subband_power,
@@ -1146,26 +1179,24 @@ int nr_rx_pusch_tp(PHY_VARS_gNB *gNB,
int start_sc = (rel15_ul->bwp_start + rel15_ul->rb_start) * NR_NB_SC_PER_RB;
for (int aa_pusch = 0; aa_pusch < num_sp_streams; aa_pusch++) {
const int aarx = ant_port_start + aa_pusch;
DevAssert(aarx < sizeofArray(pusch_vars->ulsch_power));
DevAssert(aa_pusch < sizeofArray(pusch_vars->ulsch_power));
pusch_vars->ulsch_power[aa_pusch] = 0;
pusch_vars->ulsch_noise_power[aa_pusch] = 0;
int64_t symb_energy = 0;
for (uint8_t symbol = rel15_ul->start_symbol_index; symbol < end_symbol; symbol++) {
int offset0 = ((slot % RU_RX_SLOT_DEPTH) * frame_parms->symbols_per_slot + symbol) * frame_parms->ofdm_symbol_size;
int offset = offset0 + start_sc;
c16_t *ul_ch = &gNB->common_vars.rxdataF[aarx][offset];
c16_t *ul_ch = &rxdataF[aa_pusch][start_sc + symbol * frame_parms->ofdm_symbol_size];
symb_energy += signal_energy_nodc(ul_ch, rel15_ul->rb_size * NR_NB_SC_PER_RB);
}
pusch_vars->ulsch_power[aa_pusch] += (symb_energy / rel15_ul->nr_of_symbols);
const int n0_abs_port_idx = p->spatialStreamIndices[aa_pusch];
pusch_vars->ulsch_noise_power[aa_pusch] +=
average_u32(&n0_subband_power[aarx][rel15_ul->bwp_start + rel15_ul->rb_start], rel15_ul->rb_size);
average_u32(&n0_subband_power[n0_abs_port_idx][rel15_ul->bwp_start + rel15_ul->rb_start], rel15_ul->rb_size);
LOG_D(PHY,
"aa %d, bwp_start%d, rb_start %d, rb_size %d: ulsch_power %d, ulsch_noise_power %d\n",
aarx,
aa_pusch,
rel15_ul->bwp_start,
rel15_ul->rb_start,
rel15_ul->rb_size,
@@ -1237,10 +1268,6 @@ int nr_rx_pusch_tp(PHY_VARS_gNB *gNB,
nb_re_pusch,
meas_symbol);
// extract the first dmrs for the channel level computation
// extract the data in the OFDM frame, to the start of the array
int soffset = (slot % RU_RX_SLOT_DEPTH) * frame_parms->symbols_per_slot * frame_parms->ofdm_symbol_size;
nb_re_pusch = ceil_mod(nb_re_pusch, 16);
int dmrs_symbol;
if (gNB->chest_time == 0)
@@ -1254,13 +1281,13 @@ int nr_rx_pusch_tp(PHY_VARS_gNB *gNB,
c16_t temp_rxFext[num_sp_streams][buffer_length] __attribute__((aligned(32)));
for (int aarx = 0; aarx < num_sp_streams; aarx++)
for (int nl = 0; nl < rel15_ul->nrOfLayers; nl++)
nr_ulsch_extract_rbs(gNB->common_vars.rxdataF[ant_port_start + aarx],
nr_ulsch_extract_rbs(rxdataF[aarx],
(c16_t *)pusch_vars->ul_ch_estimates[nl * num_sp_streams + aarx],
temp_rxFext[aarx],
(c16_t *)&ul_ch_estimates_ext[nl * num_sp_streams + aarx][meas_symbol * nb_re_pusch],
soffset + meas_symbol * frame_parms->ofdm_symbol_size,
meas_symbol * frame_parms->ofdm_symbol_size,
dmrs_symbol * frame_parms->ofdm_symbol_size,
(rel15_ul->ul_dmrs_symb_pos >> meas_symbol) & 0x01,
(rel15_ul->ul_dmrs_symb_pos >> meas_symbol) & 0x01,
rel15_ul,
frame_parms);
@@ -1333,7 +1360,7 @@ int nr_rx_pusch_tp(PHY_VARS_gNB *gNB,
rdata->llr = pusch_vars->llr;
rdata->scramblingSequence = scramblingSequence;
rdata->nvar = nvar;
rdata->ant_port_start = ant_port_start;
rdata->rxdataF = rxdataF;
rdata->rxFext_slot_mem = rxFext_slot_mem;
rdata->pusch_ch_est_dmrs_interpl_slot_mem = pusch_ch_est_dmrs_interpl_slot_mem;
@@ -1384,6 +1411,9 @@ int nr_rx_pusch_tp(PHY_VARS_gNB *gNB,
#endif
join_task_ans(&ans);
// No longer needed.
free(toFree);
stop_meas(&gNB->rx_pusch_symbol_processing_stats);
// Copy the data to the scope. This cannot be performed in one call to gNBscopeCopy because the data is not contiguous in the

View File

@@ -441,6 +441,7 @@ typedef struct PHY_VARS_gNB_s {
time_stats_t ulsch_deinterleaving_stats;
time_stats_t ulsch_channel_estimation_stats;
time_stats_t pusch_channel_estimation_antenna_processing_stats;
time_stats_t pusch_rx_beamforming;
time_stats_t ulsch_llr_stats;
time_stats_t rx_srs_stats;
time_stats_t generate_srs_stats;

View File

@@ -57,6 +57,29 @@ void update_grid_info(struct nr_grid *grid,
grid[port].num_sections++;
}
void nr_rx_beamforming(const nfapi_nr_dbt_pdu_t *dbt,
const nfapi_nr_ul_beamforming_t *b,
c16_t **in,
c16_t **out,
int num_rx,
int sc_offset,
int num_sc)
{
AssertFatal(b->dig_bf_interface > 0, "Beam ID must be provided\n");
const uint8_t num_log_ports = b->dig_bf_interface;
for (int l = 0; l < num_log_ports; l++) {
const uint16_t beam_id = b->prgs_list[0].dig_bf_interface_list[l].beam_idx;
AssertFatal(!IS_BIT_SET(beam_id, 15), "LoPHY beamforming bit is set!\n");
AssertFatal(beam_id == dbt->dig_beam_list[beam_id].beam_idx, "Beam ID not consistent with DBT\n");
AssertFatal(dbt->num_txrus == num_rx, "Number of baseband ports don't match with DBT\n");
for (int b = 0; b < num_rx; b++) {
const c16_t wt = dbt->dig_beam_list[beam_id].txru_list[b];
nr_beamformer_simd(in[b] + sc_offset, wt, num_sc, out[b] + sc_offset);
}
}
}
void nr_common_signal_procedures(PHY_VARS_gNB *gNB, int frame, int slot, const nfapi_nr_dl_tti_ssb_pdu *ssb_pdu)
{
NR_DL_FRAME_PARMS *fp = &gNB->frame_parms;

View File

@@ -36,4 +36,11 @@ void update_grid_info(struct nr_grid *grid,
uint16_t num_rb,
uint16_t start_symb,
uint16_t num_symb);
void nr_rx_beamforming(const nfapi_nr_dbt_pdu_t *dbt,
const nfapi_nr_ul_beamforming_t *b,
c16_t **in,
c16_t **out,
int num_rx,
int sc_offset,
int num_sc);
#endif

View File

@@ -1181,6 +1181,7 @@ int main(int argc, char *argv[])
reset_meas(&gNB->ts_deinterleave);
reset_meas(&gNB->ts_rate_unmatch);
reset_meas(&gNB->ts_ldpc_decode);
reset_meas(&gNB->pusch_rx_beamforming);
reset_meas(&gNB->ulsch_channel_estimation_stats);
reset_meas(&gNB->pusch_channel_estimation_antenna_processing_stats);
reset_meas(&gNB->rx_srs_stats);
@@ -1825,6 +1826,7 @@ int main(int argc, char *argv[])
printf("\ngNB RX\n");
printDistribution(&gNB->phy_proc_rx,table_rx, "Total PHY proc rx");
printStatIndent(&gNB->rx_pusch_stats, "RX PUSCH time");
printStatIndent2(&gNB->pusch_rx_beamforming, "PUSCH Beamforming");
printStatIndent2(&gNB->ulsch_channel_estimation_stats, "ULSCH channel estimation time");
printStatIndent3(&gNB->pusch_channel_estimation_antenna_processing_stats, "Antenna Processing time");
printStatIndent2(&gNB->rx_pusch_init_stats, "RX PUSCH Initialization time");