mirror of
https://gitlab.eurecom.fr/oai/openairinterface5g.git
synced 2026-07-13 04:30:28 +00:00
1. Beam index: So far rxdataF and txdataF had beam number as first index to separate freq domain samples of antenna ports when mulitple beams are served in a slot. This works fine when the beam is applied to entire symbol / slot but not suitable for digital beamforming. This commit removes the beam number index from the data buffers and the buffers hold all the antenna ports in the first dimension. The antenna port index to be used for each UE in a MU-MIMO or concurrent analog beam scenario is signalled by L2 via dedicated fields in the FAPI PDU. 2. Beam ID array changes: - The gNB and RU struct has a 2D array to hold beam IDs for all symbols in a frame and all antenna ports. The order is now changes to first: symbol index and second: antenna port because the RU access all ports' beam IDs for each symbol / slot at a time. This ordering is optimal for maximizing cache hits. - The type is now changed from int to uint16_t. So far the L1 had to deduce the beam number index (done in beam_index_allocation()) from the beam ID sent by L2 and this is done by initializing the beam_id array to -1 which denoted no beam id set. Now since L2 explicitely passes the port indices, there is no need for denoting if a beam id is assigned or not to a slot in L1. And uint16_t matches with the type used by FAPI to carry beam ID. Since the type is uin16_t, beam_id array is initialized to 0 and the check for -1 when passing beam ID 0 to xran is also removed. 3. Number of antenna ports: The removal of beam index from rxdataF and txdataF buffers resulted in all antenna ports to be in a single vector. The number of logical ports passed to L1 is not N1*N2*XP*number of beams per period. The buffer initialization function is updated to reflect this change. DAS reference config file is updated in accordance with these changes. 4. oaioran.c: Remove the check of beam ID = -1 for setting nPrbElm = 0. This is a hack for LiteON to supposedely improve performance according to Mario. If it's true then a proper fix would be to pass allocation information from L1 and use it instead of beam ID. Signed-off-by: Sakthivel Velumani <s.velumani@northeastern.edu>
334 lines
11 KiB
C
334 lines
11 KiB
C
/*
|
|
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
|
*/
|
|
|
|
/*!
|
|
* \brief Implementation of RU procedures
|
|
*/
|
|
|
|
#include "PHY/defs_gNB.h"
|
|
#include "common/platform_types.h"
|
|
#include "sched_nr.h"
|
|
#include "PHY/MODULATION/modulation_common.h"
|
|
#include "PHY/MODULATION/nr_modulation.h"
|
|
#include "openair1/PHY/defs_nr_common.h"
|
|
#include "common/utils/LOG/log.h"
|
|
#include "common/utils/system.h"
|
|
|
|
#include "T.h"
|
|
|
|
#include "assertions.h"
|
|
|
|
#include <time.h>
|
|
|
|
// RU OFDM Modulator gNodeB
|
|
// OFDM modulation core routine, generates a first_symbol to first_symbol+num_symbols on a particular slot and TX antenna port
|
|
void nr_feptx0(RU_t *ru, int tti_tx, int first_symbol, int num_symbols, int aa)
|
|
{
|
|
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
|
|
|
|
unsigned int slot_offset,slot_offsetF;
|
|
int slot = tti_tx;
|
|
|
|
if (aa == 0 && first_symbol == 0)
|
|
start_meas(&ru->ofdm_mod_stats);
|
|
slot_offset = get_samples_slot_timestamp(fp, slot);
|
|
slot_offsetF = first_symbol * fp->ofdm_symbol_size;
|
|
|
|
int abs_first_symbol = slot * fp->symbols_per_slot;
|
|
|
|
for (int idx_sym = abs_first_symbol; idx_sym < abs_first_symbol + first_symbol; idx_sym++)
|
|
slot_offset += (idx_sym % (0x7 << fp->numerology_index)) ? fp->nb_prefix_samples : fp->nb_prefix_samples0;
|
|
|
|
slot_offset += fp->ofdm_symbol_size * first_symbol;
|
|
|
|
LOG_D(PHY,
|
|
"SFN/SF:RU:TX:%d/%d aa %d Generating slot %d (first_symbol %d num_symbols %d) slot_offset %d, slot_offsetF %d\n",
|
|
ru->proc.frame_tx,
|
|
ru->proc.tti_tx,
|
|
aa,
|
|
slot,
|
|
first_symbol,
|
|
num_symbols,
|
|
slot_offset,
|
|
slot_offsetF);
|
|
|
|
if (fp->Ncp == 1) {
|
|
PHY_ofdm_mod(&ru->common.txdataF_BF[aa][slot_offsetF],
|
|
(int*)&ru->common.txdata[aa][slot_offset],
|
|
fp->ofdm_symbol_size,
|
|
num_symbols,
|
|
fp->nb_prefix_samples,
|
|
CYCLIC_PREFIX);
|
|
} else {
|
|
if (fp->numerology_index != 0) {
|
|
|
|
if (!(slot%(fp->slots_per_subframe/2))&&(first_symbol==0)) { // case where first symbol in slot has longer prefix
|
|
PHY_ofdm_mod(&ru->common.txdataF_BF[aa][slot_offsetF],
|
|
(int*)&ru->common.txdata[aa][slot_offset],
|
|
fp->ofdm_symbol_size,
|
|
1,
|
|
fp->nb_prefix_samples0,
|
|
CYCLIC_PREFIX);
|
|
|
|
PHY_ofdm_mod(&ru->common.txdataF_BF[aa][slot_offsetF+fp->ofdm_symbol_size],
|
|
(int*)&ru->common.txdata[aa][slot_offset+fp->nb_prefix_samples0+fp->ofdm_symbol_size],
|
|
fp->ofdm_symbol_size,
|
|
num_symbols-1,
|
|
fp->nb_prefix_samples,
|
|
CYCLIC_PREFIX);
|
|
}
|
|
else { // all symbols in slot have shorter prefix
|
|
PHY_ofdm_mod(&ru->common.txdataF_BF[aa][slot_offsetF],
|
|
(int*)&ru->common.txdata[aa][slot_offset],
|
|
fp->ofdm_symbol_size,
|
|
num_symbols,
|
|
fp->nb_prefix_samples,
|
|
CYCLIC_PREFIX);
|
|
}
|
|
} // numerology_index!=0
|
|
else { //numerology_index == 0
|
|
for (int idx_sym = abs_first_symbol; idx_sym < abs_first_symbol+num_symbols; idx_sym++) {
|
|
if (idx_sym % 0x7) {
|
|
PHY_ofdm_mod(&ru->common.txdataF_BF[aa][slot_offsetF],
|
|
(int*)&ru->common.txdata[aa][slot_offset],
|
|
fp->ofdm_symbol_size,
|
|
1,
|
|
fp->nb_prefix_samples,
|
|
CYCLIC_PREFIX);
|
|
slot_offset += fp->nb_prefix_samples+fp->ofdm_symbol_size;
|
|
slot_offsetF += fp->ofdm_symbol_size;
|
|
}
|
|
else {
|
|
PHY_ofdm_mod(&ru->common.txdataF_BF[aa][slot_offsetF],
|
|
(int*)&ru->common.txdata[aa][slot_offset],
|
|
fp->ofdm_symbol_size,
|
|
1,
|
|
fp->nb_prefix_samples0,
|
|
CYCLIC_PREFIX);
|
|
slot_offset += fp->nb_prefix_samples0+fp->ofdm_symbol_size;
|
|
slot_offsetF += fp->ofdm_symbol_size;
|
|
}
|
|
} // for(idx_symbol..
|
|
} // numerology 0
|
|
}
|
|
|
|
if (aa == 0 && first_symbol == 0)
|
|
stop_meas(&ru->ofdm_mod_stats);
|
|
}
|
|
|
|
// RU FEP TX OFDM modulation, single-thread
|
|
void nr_feptx_ofdm(RU_t *ru,int frame_tx,int tti_tx)
|
|
{
|
|
nfapi_nr_config_request_scf_t *cfg = &ru->gNB_list[0]->gNB_config;
|
|
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
|
|
|
|
unsigned int aa=0;
|
|
int slot_sizeF = fp->ofdm_symbol_size * fp->symbols_per_slot;
|
|
int slot = tti_tx;
|
|
int *txdata = &ru->common.txdata[aa][get_samples_slot_timestamp(fp, slot)];
|
|
|
|
if (nr_slot_select(cfg,frame_tx,slot) == NR_UPLINK_SLOT)
|
|
return;
|
|
|
|
nr_feptx0(ru, slot, 0, fp->symbols_per_slot, aa);
|
|
|
|
LOG_D(PHY,
|
|
"feptx_ofdm (TXPATH): frame %d, slot %d: txp (time %p) %d dB, txp (freq) %d dB\n",
|
|
frame_tx,
|
|
slot,
|
|
txdata,
|
|
dB_fixed(signal_energy((int32_t *)txdata, get_samples_per_slot(slot, fp))),
|
|
dB_fixed(signal_energy_nodc((c16_t *)ru->common.txdataF_BF[aa], 2 * slot_sizeF)));
|
|
}
|
|
|
|
void nr_feptx_prec(RU_t *ru, int frame_tx, int slot_tx)
|
|
{
|
|
PHY_VARS_gNB **gNB_list = ru->gNB_list;
|
|
AssertFatal(ru->num_gNB == 1, "Cannot handle more than 1 gNB\n");
|
|
PHY_VARS_gNB *gNB = gNB_list[0];
|
|
nfapi_nr_config_request_scf_t *cfg = &ru->gNB_list[0]->gNB_config;
|
|
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
|
|
start_meas(&ru->precoding_stats);
|
|
|
|
// Copy beam ID assigned to all ports in this slot
|
|
if (gNB->common_vars.analog_bf) {
|
|
for (int i = 0; i < fp->symbols_per_slot; i++) {
|
|
memcpy(ru->common.beam_id[slot_tx * fp->symbols_per_slot + i],
|
|
gNB->common_vars.beam_id[slot_tx * fp->symbols_per_slot + i],
|
|
(ru->nb_tx) * sizeof(**ru->common.beam_id));
|
|
}
|
|
}
|
|
|
|
if (nr_slot_select(cfg,frame_tx,slot_tx) == NR_UPLINK_SLOT)
|
|
return;
|
|
|
|
// If there is no digital beamforming we just need to copy the data to RU
|
|
if (ru->config.dbt_config.num_dig_beams == 0 || ru->gNB_list[0]->common_vars.analog_bf) {
|
|
for (int i = 0; i < fp->nb_antennas_tx; ++i) {
|
|
memcpy(ru->common.txdataF_BF[i], gNB->common_vars.txdataF[i], fp->samples_per_slot_wCP * sizeof(int32_t));
|
|
}
|
|
} else {
|
|
AssertFatal(false, "This needs to be fixed by using appropriate beams from config\n");
|
|
}
|
|
stop_meas(&ru->precoding_stats);
|
|
}
|
|
|
|
// core routine for FEP TX, called from threads in RU TX thread-pool
|
|
void nr_feptx(void *arg)
|
|
{
|
|
feptx_cmd_t *feptx = (feptx_cmd_t *)arg;
|
|
|
|
RU_t *ru = feptx->ru;
|
|
int slot = feptx->slot;
|
|
int aa = feptx->aid;
|
|
int startSymbol = feptx->startSymbol;
|
|
int numSymbols = feptx->numSymbols;
|
|
|
|
if (aa == 0)
|
|
start_meas(&ru->precoding_stats);
|
|
|
|
// If there is no digital beamforming we just need to copy the data to RU
|
|
if (ru->config.dbt_config.num_dig_beams == 0 || ru->gNB_list[0]->common_vars.analog_bf) {
|
|
// FFT shift
|
|
const NR_DL_FRAME_PARMS *fp = &ru->gNB_list[0]->frame_parms;
|
|
fft_shift(ru->gNB_list[0]->common_vars.txdataF[aa],
|
|
fp->ofdm_symbol_size,
|
|
fp->N_RB_DL,
|
|
(c16_t *)ru->common.txdataF_BF[aa],
|
|
fp->ofdm_symbol_size,
|
|
startSymbol,
|
|
numSymbols);
|
|
} else {
|
|
AssertFatal(false, "This needs to be fixed by using appropriate beams from config\n");
|
|
}
|
|
|
|
if (aa == 0)
|
|
stop_meas(&ru->precoding_stats);
|
|
|
|
////////////FEPTX////////////
|
|
nr_feptx0(ru, slot, startSymbol, numSymbols, aa);
|
|
|
|
// Task completed in //
|
|
completed_task_ans(feptx->ans);
|
|
}
|
|
|
|
// RU FEP TX using thread-pool
|
|
void nr_feptx_tp(RU_t *ru, int frame_tx, int slot)
|
|
{
|
|
nfapi_nr_config_request_scf_t *cfg = &ru->gNB_list[0]->gNB_config;
|
|
if (nr_slot_select(cfg, frame_tx, slot) == NR_UPLINK_SLOT)
|
|
return;
|
|
start_meas(&ru->ofdm_total_stats);
|
|
|
|
const NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
|
|
int nt = fp->nb_antennas_tx;
|
|
size_t const sz = nt + (ru->half_slot_parallelization > 0) * nt;
|
|
feptx_cmd_t arr[sz];
|
|
task_ans_t ans;
|
|
init_task_ans(&ans, sz);
|
|
|
|
// Copy beam IDs
|
|
if (ru->gNB_list[0]->common_vars.analog_bf) {
|
|
for (uint_fast16_t i = 0; i < fp->symbols_per_slot; i++)
|
|
memcpy(ru->common.beam_id[slot * fp->symbols_per_slot + i],
|
|
ru->gNB_list[0]->common_vars.beam_id[slot * fp->symbols_per_slot + i],
|
|
(ru->nb_tx) * sizeof(**ru->common.beam_id));
|
|
}
|
|
|
|
int nbfeptx = 0;
|
|
for (int aid = 0; aid < nt; aid++) {
|
|
feptx_cmd_t *feptx_cmd = &arr[nbfeptx];
|
|
feptx_cmd->ans = &ans;
|
|
feptx_cmd->aid = aid;
|
|
feptx_cmd->ru = ru;
|
|
feptx_cmd->slot = slot;
|
|
feptx_cmd->startSymbol = 0;
|
|
feptx_cmd->numSymbols =
|
|
(ru->half_slot_parallelization > 0) ? ru->nr_frame_parms->symbols_per_slot >> 1 : ru->nr_frame_parms->symbols_per_slot;
|
|
|
|
task_t t = {.func = nr_feptx, .args = feptx_cmd};
|
|
pushTpool(ru->threadPool, t);
|
|
nbfeptx++;
|
|
if (ru->half_slot_parallelization > 0) {
|
|
feptx_cmd_t *feptx_cmd = &arr[nbfeptx];
|
|
feptx_cmd->ans = &ans;
|
|
feptx_cmd->aid = aid;
|
|
feptx_cmd->ru = ru;
|
|
feptx_cmd->slot = slot;
|
|
feptx_cmd->startSymbol = ru->nr_frame_parms->symbols_per_slot >> 1;
|
|
feptx_cmd->numSymbols = ru->nr_frame_parms->symbols_per_slot >> 1;
|
|
|
|
task_t t = {.func = nr_feptx, .args = feptx_cmd};
|
|
pushTpool(ru->threadPool, t);
|
|
nbfeptx++;
|
|
}
|
|
}
|
|
join_task_ans(&ans);
|
|
|
|
stop_meas(&ru->ofdm_total_stats);
|
|
}
|
|
|
|
// core RX FEP routine, called by threads in RU thread-pool
|
|
void nr_fep(void *arg)
|
|
{
|
|
feprx_cmd_t *feprx_cmd = (feprx_cmd_t *)arg;
|
|
int slot = feprx_cmd->slot;
|
|
int startSymbol = feprx_cmd->startSymbol;
|
|
int endSymbol = feprx_cmd->endSymbol;
|
|
|
|
for (int l = startSymbol; l <= endSymbol; l++)
|
|
nr_symbol_fep_ul(feprx_cmd->fp,
|
|
feprx_cmd->rxdata,
|
|
&feprx_cmd->rxdataF[l * feprx_cmd->fp->ofdm_symbol_size],
|
|
l,
|
|
slot,
|
|
feprx_cmd->sample_offet);
|
|
|
|
completed_task_ans(feprx_cmd->ans);
|
|
}
|
|
|
|
// RU RX FEP using thread-pool
|
|
void nr_fep_tp(RU_t *ru, int slot)
|
|
{
|
|
int nbfeprx = 0;
|
|
start_meas(&ru->ofdm_demod_stats);
|
|
|
|
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
|
|
int nt = fp->nb_antennas_rx;
|
|
int tasks_per_slot = (ru->half_slot_parallelization > 0) ? 2 : 1;
|
|
size_t const sz = nt * tasks_per_slot;
|
|
feprx_cmd_t arr[sz];
|
|
task_ans_t ans;
|
|
init_task_ans(&ans, sz);
|
|
int rxdataF_offset = (slot % RU_RX_SLOT_DEPTH) * fp->symbols_per_slot * fp->ofdm_symbol_size;
|
|
|
|
int symbols_per_task = fp->symbols_per_slot / tasks_per_slot;
|
|
|
|
for (int task_idx = 0; task_idx < tasks_per_slot; task_idx++) {
|
|
int start_symbol = task_idx * symbols_per_task;
|
|
int end_symbol = (task_idx + 1) * symbols_per_task - 1;
|
|
if (task_idx == tasks_per_slot - 1)
|
|
end_symbol = fp->symbols_per_slot - 1;
|
|
|
|
for (int aid = 0; aid < nt; aid++) {
|
|
feprx_cmd_t *feprx_cmd = &arr[nbfeprx];
|
|
feprx_cmd->ans = &ans;
|
|
feprx_cmd->fp = fp;
|
|
feprx_cmd->slot = ru->proc.tti_rx;
|
|
feprx_cmd->startSymbol = start_symbol;
|
|
feprx_cmd->endSymbol = end_symbol;
|
|
feprx_cmd->rxdata = (const c16_t *)ru->common.rxdata[aid];
|
|
feprx_cmd->rxdataF = (c16_t *)&ru->common.rxdataF[aid][rxdataF_offset];
|
|
feprx_cmd->sample_offet = ru->N_TA_offset;
|
|
|
|
task_t t = {.func = nr_fep, .args = feprx_cmd};
|
|
pushTpool(ru->threadPool, t);
|
|
nbfeprx++;
|
|
}
|
|
}
|
|
join_task_ans(&ans);
|
|
|
|
stop_meas(&ru->ofdm_demod_stats);
|
|
}
|