mirror of
https://gitlab.eurecom.fr/oai/openairinterface5g.git
synced 2026-07-13 04:30:28 +00:00
Compare commits
7 Commits
hol-delay
...
tx_symbol_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a02ce6aaf1 | ||
|
|
28dddbfa1e | ||
|
|
d0b6bf2039 | ||
|
|
45f3905692 | ||
|
|
d96f26a753 | ||
|
|
3a7967e76f | ||
|
|
d5620c7ec1 |
@@ -187,10 +187,49 @@ sudo sysctl -w net.core.rmem_default=62500000
|
||||
sudo ethtool -G enp1s0f0 tx 4096 rx 4096
|
||||
```
|
||||
|
||||
### 6.2 Real-time performance workarounds
|
||||
### 6.2 Real-time performance
|
||||
|
||||
#### 6.2.1 System tuning
|
||||
In order to get an optimal real-time behavior, a few tunings can be performed on the host system:
|
||||
- The use of isolated cores for the softmodem prevents competitions on the usage of core between the softmodem and other processes.
|
||||
Core isolation is enabled through the kernel command line. **Warning: modifying the kernel command line can harm the OS behavior. Proceed with caution.**
|
||||
Refer to the [OAI 7.2 Fronthaul Interface Tutorial](./ORAN_FHI7.2_Tutorial.md) for examples.
|
||||
- Enable Performance Mode `sudo cpupower idle-set -D 0`
|
||||
- If you get real-time problems on heavy UL traffic, reduce the maximum UL MCS using an additional command-line switch: `--MACRLCs.[0].ul_max_mcs 14`.
|
||||
- You can also reduce the number of LDPC decoder iterations, which will make the LDPC decoder take less time: `--L1s.[0].max_ldpc_iterations 4`.
|
||||
- Read more on system tuning in the [dedicated document on performance tuning](./tuning_and_security.md).
|
||||
|
||||
#### 6.2.2 Softmodem tuning
|
||||
The way the NR softmodem uses the computing ressource can be configured.
|
||||
It can have a significant effect on the performance and real-time behavior:
|
||||
- The L1 TX and L1 RX threads are the two main threads executing the L1 RX and L1 TX pipelines.
|
||||
These threads are ideally assigned to two dedicated cores. To be dedicated, the cores should be isolated in the kernel parameters and not be assigned elsewhere.
|
||||
They can be assigned to specified cores with options `--L1s.[0].L1_tx_thread_core` and `--L1s.[0].L1_rx_thread_core` followed by a core id.
|
||||
- The thread pool is a group of processor cores over which some baseband processing worker cores execute.
|
||||
It is configured by providing a list of core ids after option `--thread-pool`.
|
||||
`-1` can also be passed instead of a core id in order to use a floating core.
|
||||
By default, the thread pool is 8 floating cores.
|
||||
- PDSCH generation (i.e., layer mapping and precoding) is by default executed in the L1 TX thread but can be multithreaded using the thread pool.
|
||||
This is enabled by option `--tx-sym` followed by the number of symbols that should be processed in each thread.
|
||||
|
||||
#### 6.2.3 Workarounds
|
||||
If the real-time performance remains bad after tuning the system and softmodem,
|
||||
some workarounds allow to lower the computing demand at the cost of lower network performance:
|
||||
- If you get real-time problems on heavy UL traffic, reduce the maximum UL MCS using an additional command-line switch: `--MACRLCs.[0].ul_max_mcs 14`.
|
||||
This comes at the cost of a lower spectral efficiency (i.e., less data for the same radio resource).
|
||||
- You can also reduce the number of LDPC decoder iterations, which will make the LDPC decoder take less time: `--L1s.[0].max_ldpc_iterations 4`.
|
||||
The default number of LDPC iterations is 5. Lowering the number of iteration comes at the cost of more unsuccessful transmissions.
|
||||
OAI offers multiple implementation of LDPC coding, including offloading to an accelerator, the number of LDPC iteration should be chosen accordingly.
|
||||
|
||||
#### 6.2.4 List of behaviors
|
||||
Here is a **non-exhaustive** list of known behavior related to real-time performance:
|
||||
- On some AMD EPYC series processors with Zen architecture (at least every Zen4, Zen4c, Zen5 and Zen5c based processors experience this behavior),
|
||||
the processor is made of multiple dies holding one or multiple core complexes which are groups of cores with an L3 cache.
|
||||
This means that cores from different core complexes do not share the same L3 cache and communication between these cores implies inter L3 cache communication
|
||||
within a die or, even worse, between dies, which has a cost in term of latency.
|
||||
Depending on the system configuration, the NUMA topology may reflect this physical topology, which can induce even further latency for inter core complex communication.
|
||||
The softmodem is sensitive to this latency and its performance can be harmed if it uses cores across the border of dies or core complexes,
|
||||
especially when multithreading of PDSCH generation is enabled (argument of `--tx-sym` is superior to 0).
|
||||
We recomend to allocate cores to the softmodem on such machine with awareness of the topology.
|
||||
Ideally, the softmodem should use only one core complex or one die if it doesn't hold on one core complex.
|
||||
|
||||
### 6.3 Uplink issues related with noise on the DC carriers
|
||||
|
||||
|
||||
@@ -342,6 +342,7 @@ void init_gNB_Tpool(int inst)
|
||||
gNB_L1_proc_t *proc = &gNB->proc;
|
||||
// PUSCH symbols per thread need to be calculated by how many threads we have
|
||||
gNB->num_pusch_symbols_per_thread = 1;
|
||||
gNB->num_pdsch_symbols_per_thread = get_softmodem_params()->num_pdsch_symbols_per_thread;
|
||||
// ULSCH decoding threadpool
|
||||
initTpool(get_softmodem_params()->threadPoolConfig, &gNB->threadPool, cpumeas(CPUMEAS_GETSTATE));
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ extern "C"
|
||||
example: -1,3 launches two working threads one floating, the second set on core 3\n\
|
||||
default 8 floating threads\n\
|
||||
use N for no pool (runs in calling thread) recommended with rfsim.\n"
|
||||
#define CONFIG_HLP_TX_SYM "number of symbols processed per PDSCH generation thread\n"
|
||||
#define CONFIG_HLP_CALUER "set UE RX calibration\n"
|
||||
#define CONFIG_HLP_CALUERM ""
|
||||
#define CONFIG_HLP_CALUERB ""
|
||||
@@ -107,6 +108,7 @@ extern "C"
|
||||
/*-----------------------------------------------------------------------------------------------------------------------------------------------------*/
|
||||
#define RF_CONFIG_FILE softmodem_params.rf_config_file
|
||||
#define TP_CONFIG softmodem_params.threadPoolConfig
|
||||
#define TX_SYM softmodem_params.num_pdsch_symbols_per_thread
|
||||
#define CONTINUOUS_TX softmodem_params.continuous_tx
|
||||
#define PHY_TEST softmodem_params.phy_test
|
||||
#define DO_RA softmodem_params.do_ra
|
||||
@@ -132,6 +134,7 @@ extern int usrp_tx_thread;
|
||||
#define CMDLINE_PARAMS_DESC { \
|
||||
{"rf-config-file", CONFIG_HLP_RFCFGF, 0, .strptr=&RF_CONFIG_FILE, .defstrval=NULL, TYPE_STRING, 0}, \
|
||||
{"thread-pool", CONFIG_HLP_TPOOL, 0, .strptr=&TP_CONFIG, .defstrval="-1,-1,-1,-1,-1,-1,-1,-1", TYPE_STRING, 0}, \
|
||||
{"tx-sym", CONFIG_HLP_TX_SYM, 0, .iptr=&TX_SYM, .defintval=0, TYPE_INT, 0}, \
|
||||
{"phy-test", CONFIG_HLP_PHYTST, PARAMFLAG_BOOL, .iptr=&PHY_TEST, .defintval=0, TYPE_INT, 0}, \
|
||||
{"do-ra", CONFIG_HLP_DORA, PARAMFLAG_BOOL, .iptr=&DO_RA, .defintval=0, TYPE_INT, 0}, \
|
||||
{"sl-mode", CONFIG_HLP_SL_MODE, 0, .u8ptr=&SL_MODE, .defintval=0, TYPE_UINT8, 0}, \
|
||||
@@ -196,6 +199,7 @@ extern int usrp_tx_thread;
|
||||
{ .s5 = { NULL } }, \
|
||||
{ .s5 = { NULL } }, \
|
||||
{ .s5 = { NULL } }, \
|
||||
{ .s5 = { NULL } }, \
|
||||
{ .s3a = { config_checkstr_assign_integer, \
|
||||
{"MONOLITHIC", "PNF", "VNF", "AERIAL","UE_STUB_PNF","UE_STUB_OFFNET","STANDALONE_PNF"}, \
|
||||
{NFAPI_MONOLITHIC, NFAPI_MODE_PNF, NFAPI_MODE_VNF, NFAPI_MODE_AERIAL,NFAPI_UE_STUB_PNF,NFAPI_UE_STUB_OFFNET,NFAPI_MODE_STANDALONE_PNF}, \
|
||||
@@ -294,6 +298,7 @@ typedef struct {
|
||||
//THREAD_STRUCT thread_struct;
|
||||
char *rf_config_file;
|
||||
char *threadPoolConfig;
|
||||
int num_pdsch_symbols_per_thread;
|
||||
int phy_test;
|
||||
int do_ra;
|
||||
uint8_t sl_mode;
|
||||
|
||||
@@ -337,22 +337,22 @@ static inline void neg_dmrs(c16_t *in, c16_t *out, int sz)
|
||||
*out++ = i % 2 ? (c16_t){-in[i].r, -in[i].i} : in[i];
|
||||
}
|
||||
|
||||
static inline int do_onelayer(NR_DL_FRAME_PARMS *frame_parms,
|
||||
int slot,
|
||||
const nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15,
|
||||
int layer,
|
||||
c16_t *output,
|
||||
c16_t *txl_start,
|
||||
int start_sc,
|
||||
int symbol_sz,
|
||||
int l_symbol,
|
||||
uint16_t dlPtrsSymPos,
|
||||
int n_ptrs,
|
||||
int amp,
|
||||
int16_t amp_dmrs,
|
||||
int l_prime,
|
||||
nfapi_nr_dmrs_type_e dmrs_Type,
|
||||
c16_t *dmrs_start)
|
||||
static inline void do_onelayer(NR_DL_FRAME_PARMS *frame_parms,
|
||||
int slot,
|
||||
const nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15,
|
||||
int layer,
|
||||
c16_t *output,
|
||||
c16_t *txl_start,
|
||||
int start_sc,
|
||||
int symbol_sz,
|
||||
int l_symbol,
|
||||
uint16_t dlPtrsSymPos,
|
||||
int n_ptrs,
|
||||
int amp,
|
||||
int16_t amp_dmrs,
|
||||
int l_prime,
|
||||
nfapi_nr_dmrs_type_e dmrs_Type,
|
||||
c16_t *dmrs_start)
|
||||
{
|
||||
c16_t *txl = txl_start;
|
||||
const uint sz = rel15->rbSize * NR_NB_SC_PER_RB;
|
||||
@@ -450,7 +450,7 @@ static inline int do_onelayer(NR_DL_FRAME_PARMS *frame_parms,
|
||||
txl += no_ptrs_dmrs_case(output + start_sc, txl, amp, upper_limit);
|
||||
txl += no_ptrs_dmrs_case(output, txl, amp, remaining_re);
|
||||
} // no DMRS/PTRS in symbol
|
||||
return txl - txl_start;
|
||||
return;
|
||||
}
|
||||
|
||||
static inline void do_txdataF(c16_t **txdataF,
|
||||
@@ -504,7 +504,7 @@ static inline void do_txdataF(c16_t **txdataF,
|
||||
subCarrier -= symbol_sz;
|
||||
}
|
||||
} else { // non-unitary Precoding
|
||||
AssertFatal(frame_parms->nb_antennas_tx > 1, "No precoding can be done with a single antenna port\n");
|
||||
AssertFatal(frame_parms->nb_antennas_tx > 1, "No precoding can be done with a single antenna port\n"); // TODO replace by nb_tx_ant?
|
||||
// get the precoding matrix weights:
|
||||
nfapi_nr_pm_pdu_t *pmi_pdu = &gNB->gNB_config.pmi_list.pmi_pdu[pmi - 1]; // pmi 0 is identity matrix
|
||||
AssertFatal(pmi == pmi_pdu->pm_idx, "PMI %d doesn't match to the one in precoding matrix %d\n", pmi, pmi_pdu->pm_idx);
|
||||
@@ -548,28 +548,158 @@ static inline void do_txdataF(c16_t **txdataF,
|
||||
rb += rb_step;
|
||||
} // RB loop: while(rb < rel15->rbSize)
|
||||
}
|
||||
static int do_one_dlsch(unsigned char *input_ptr, PHY_VARS_gNB *gNB, NR_gNB_DLSCH_t *dlsch, int slot)
|
||||
|
||||
typedef struct pdschSymbolProc_s {
|
||||
PHY_VARS_gNB *gNB;
|
||||
NR_DL_FRAME_PARMS *frame_parms;
|
||||
const nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15;
|
||||
unsigned int slot;
|
||||
unsigned int startSymbol;
|
||||
unsigned int numSymbols;
|
||||
task_ans_t *ans;
|
||||
unsigned int layerSz2;
|
||||
unsigned int dlPtrsSymPos;
|
||||
unsigned int n_ptrs;
|
||||
unsigned int re_beginning_of_symbol[14];
|
||||
c16_t *tx_layers[4];
|
||||
int nb_tx_ant;
|
||||
c16_t **txdataF;
|
||||
time_stats_t dlsch_resource_mapping_stats;
|
||||
time_stats_t dlsch_precoding_stats;
|
||||
} pdschSymbolProc_t;
|
||||
|
||||
static void nr_pdsch_symbol_processing(void *arg)
|
||||
{
|
||||
pdschSymbolProc_t *rdata = (pdschSymbolProc_t *)arg;
|
||||
|
||||
PHY_VARS_gNB *gNB = rdata->gNB;
|
||||
NR_DL_FRAME_PARMS *frame_parms = rdata->frame_parms;
|
||||
const nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = rdata->rel15;
|
||||
int slot = rdata->slot;
|
||||
c16_t *tx_layers[rel15->nrOfLayers];
|
||||
for (int l = 0; l < rel15->nrOfLayers; l++)
|
||||
tx_layers[l] = rdata->tx_layers[l];
|
||||
const int nb_re_dmrs = rel15->numDmrsCdmGrpsNoData * (rel15->dmrsConfigType == NFAPI_NR_DMRS_TYPE1 ? 6 : 4);
|
||||
const int n_dmrs = (rel15->BWPStart + rel15->rbStart + rel15->rbSize) * nb_re_dmrs;
|
||||
// Loop Over OFDM symbols:
|
||||
c16_t mod_dmrs[(n_dmrs + 63) & ~63] __attribute__((aligned(64)));
|
||||
const uint32_t txdataF_offset = slot * frame_parms->samples_per_slot_wCP;
|
||||
const int symbol_sz = frame_parms->ofdm_symbol_size;
|
||||
|
||||
c16_t **txdataF = rdata->txdataF;
|
||||
uint16_t start_sc = frame_parms->first_carrier_offset + (rel15->rbStart + rel15->BWPStart) * NR_NB_SC_PER_RB;
|
||||
if (start_sc >= symbol_sz)
|
||||
start_sc -= symbol_sz;
|
||||
|
||||
#ifdef DEBUG_DLSCH_MAPPING
|
||||
printf("slot %d PDSCH resource mapping started (start SC %d\tstart symbol %d\tnum symbols %d\tN_PRB %d,nb_layers %d)\n",
|
||||
rdata->slot,
|
||||
start_sc,
|
||||
rdata->startSymbol,
|
||||
rdata->numSymbols,
|
||||
rel15->rbSize,
|
||||
rel15->nrOfLayers);
|
||||
#endif
|
||||
for (int l_symbol = rdata->startSymbol; l_symbol < rdata->startSymbol + rdata->numSymbols; l_symbol++) {
|
||||
start_meas(&rdata->dlsch_resource_mapping_stats);
|
||||
int l_prime = 0; // single symbol layer 0
|
||||
int l_overline = get_l0(rel15->dlDmrsSymbPos);
|
||||
|
||||
#ifdef DEBUG_DLSCH_MAPPING
|
||||
printf("PDSCH resource mapping symbol %d\n", l_symbol);
|
||||
#endif
|
||||
/// DMRS QPSK modulation
|
||||
if ((rel15->dlDmrsSymbPos & (1 << l_symbol))) { // DMRS time occasion
|
||||
// The reference point for is subcarrier -1 of the lowest-numbered resource block in CORESET 0 if the corresponding
|
||||
// PDCCH is associated with CORESET -1 and Type0-PDCCH common search space and is addressed to SI-RNTI
|
||||
// 2GPP TS 38.211 V15.8.0 Section 7.4.1.1.2 Mapping to physical resources
|
||||
if (l_symbol == (l_overline + 1)) // take into account the double DMRS symbols
|
||||
l_prime = 1;
|
||||
else if (l_symbol > (l_overline + 1)) { // new DMRS pair
|
||||
l_overline = l_symbol;
|
||||
l_prime = 0;
|
||||
}
|
||||
#ifdef DEBUG_DLSCH_MAPPING
|
||||
printf("dlDmrsScramblingId %d, SCID %d slot %d l_symbol %d\n", rel15->dlDmrsScramblingId, rel15->SCID, slot, l_symbol);
|
||||
#endif
|
||||
const uint32_t *gold = nr_gold_pdsch(frame_parms->N_RB_DL,
|
||||
frame_parms->symbols_per_slot,
|
||||
rel15->dlDmrsScramblingId,
|
||||
rel15->SCID,
|
||||
slot,
|
||||
l_symbol);
|
||||
// Qm = 1 as DMRS is QPSK modulated
|
||||
nr_modulation(gold, n_dmrs * DMRS_MOD_ORDER, DMRS_MOD_ORDER, (int16_t *)mod_dmrs);
|
||||
|
||||
#ifdef DEBUG_DLSCH_MAPPING
|
||||
printf("DMRS modulation (symbol %d, %d symbols, type %d):\n", l_symbol, n_dmrs, dmrs_Type);
|
||||
for (int i = 0; i < n_dmrs / 2; i += 8) {
|
||||
for (int j = 0; j < 8; j++) {
|
||||
printf("%d %d\t", mod_dmrs[i + j].r, mod_dmrs[i + j].i);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
uint32_t dmrs_idx = rel15->rbStart;
|
||||
if (rel15->refPoint == 0)
|
||||
dmrs_idx += rel15->BWPStart;
|
||||
dmrs_idx *= rel15->dmrsConfigType == NFAPI_NR_DMRS_TYPE1 ? 6 : 4;
|
||||
c16_t txdataF_precoding[rel15->nrOfLayers][symbol_sz] __attribute__((aligned(64)));
|
||||
for (int layer = 0; layer < rel15->nrOfLayers; layer++) {
|
||||
do_onelayer(frame_parms,
|
||||
slot,
|
||||
rel15,
|
||||
layer,
|
||||
txdataF_precoding[layer],
|
||||
tx_layers[layer] + rdata->re_beginning_of_symbol[l_symbol],
|
||||
start_sc,
|
||||
symbol_sz,
|
||||
l_symbol,
|
||||
rdata->dlPtrsSymPos,
|
||||
rdata->n_ptrs,
|
||||
gNB->TX_AMP,
|
||||
min((double)gNB->TX_AMP * sqrt(rel15->numDmrsCdmGrpsNoData), INT16_MAX),
|
||||
l_prime,
|
||||
rel15->dmrsConfigType,
|
||||
mod_dmrs + dmrs_idx);
|
||||
} // layer loop
|
||||
stop_meas(&rdata->dlsch_resource_mapping_stats);
|
||||
|
||||
start_meas(&rdata->dlsch_precoding_stats);
|
||||
for (int ant = 0; ant < rdata->nb_tx_ant; ant++) {
|
||||
const size_t txdataF_offset_per_symbol = l_symbol * symbol_sz + txdataF_offset;
|
||||
do_txdataF(txdataF, symbol_sz, txdataF_precoding, gNB, rel15, ant, start_sc, txdataF_offset_per_symbol);
|
||||
}
|
||||
stop_meas(&rdata->dlsch_precoding_stats);
|
||||
}
|
||||
// Task running in // completed
|
||||
completed_task_ans(rdata->ans);
|
||||
}
|
||||
|
||||
static int do_one_dlsch(unsigned char *input_ptr,
|
||||
PHY_VARS_gNB *gNB,
|
||||
NR_gNB_DLSCH_t *dlsch,
|
||||
int nb_beams,
|
||||
int nb_tx_ant,
|
||||
c16_t *txdataF[nb_beams][nb_tx_ant],
|
||||
int slot)
|
||||
{
|
||||
const int16_t amp = gNB->TX_AMP;
|
||||
NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms;
|
||||
|
||||
time_stats_t *dlsch_scrambling_stats = &gNB->dlsch_scrambling_stats;
|
||||
time_stats_t *dlsch_modulation_stats = &gNB->dlsch_modulation_stats;
|
||||
const nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &dlsch->pdsch_pdu->pdsch_pdu_rel15;
|
||||
const int layerSz = frame_parms->N_RB_DL * NR_SYMBOLS_PER_SLOT * NR_NB_SC_PER_RB;
|
||||
const int symbol_sz=frame_parms->ofdm_symbol_size;
|
||||
const int dmrs_Type = rel15->dmrsConfigType;
|
||||
const int nb_re_dmrs = rel15->numDmrsCdmGrpsNoData * (rel15->dmrsConfigType == NFAPI_NR_DMRS_TYPE1 ? 6 : 4);
|
||||
const int16_t amp_dmrs = min((double)amp * sqrt(rel15->numDmrsCdmGrpsNoData), INT16_MAX); // 3GPP TS 38.214 Section 4.1: Table 4.1-1
|
||||
LOG_D(PHY,
|
||||
"pdsch: BWPStart %d, BWPSize %d, rbStart %d, rbsize %d\n",
|
||||
rel15->BWPStart,
|
||||
rel15->BWPSize,
|
||||
rel15->rbStart,
|
||||
rel15->rbSize);
|
||||
const int n_dmrs = (rel15->BWPStart + rel15->rbStart + rel15->rbSize) * nb_re_dmrs;
|
||||
const int n_dmrs = rel15->rbSize * nb_re_dmrs;
|
||||
|
||||
const int dmrs_symbol_map = rel15->dlDmrsSymbPos; // single DMRS: 010000100 Double DMRS 110001100
|
||||
const int xOverhead = 0;
|
||||
const int nb_re =
|
||||
(12 * rel15->NrOfSymbols - nb_re_dmrs * get_num_dmrs(rel15->dlDmrsSymbPos) - xOverhead) * rel15->rbSize * rel15->nrOfLayers;
|
||||
@@ -644,24 +774,9 @@ static int do_one_dlsch(unsigned char *input_ptr, PHY_VARS_gNB *gNB, NR_gNB_DLSC
|
||||
start_meas(&gNB->dlsch_pdsch_generation_stats);
|
||||
/// Resource mapping
|
||||
// Non interleaved VRB to PRB mapping
|
||||
uint16_t start_sc = frame_parms->first_carrier_offset + (rel15->rbStart + rel15->BWPStart) * NR_NB_SC_PER_RB;
|
||||
if (start_sc >= symbol_sz)
|
||||
start_sc -= symbol_sz;
|
||||
|
||||
const uint32_t txdataF_offset = slot * frame_parms->samples_per_slot_wCP;
|
||||
#ifdef DEBUG_DLSCH_MAPPING
|
||||
printf("PDSCH resource mapping started (start SC %d\tstart symbol %d\tN_PRB %d\tnb_re %d,nb_layers %d)\n",
|
||||
start_sc,
|
||||
rel15->StartSymbolIndex,
|
||||
rel15->rbSize,
|
||||
nb_re,
|
||||
rel15->nrOfLayers);
|
||||
#endif
|
||||
|
||||
AssertFatal(n_dmrs, "n_dmrs can't be 0\n");
|
||||
// make a large enough tail to process all re with SIMD regardless a garbadge filler
|
||||
c16_t mod_dmrs[(n_dmrs+63)&~63] __attribute__((aligned(64)));
|
||||
unsigned int re_beginning_of_symbol = 0;
|
||||
|
||||
start_meas(&gNB->dlsch_layer_mapping_stats);
|
||||
int layerSz2 = (layerSz + 63) & ~63;
|
||||
@@ -685,84 +800,70 @@ static int do_one_dlsch(unsigned char *input_ptr, PHY_VARS_gNB *gNB, NR_gNB_DLSC
|
||||
slot,
|
||||
frame_parms->symbols_per_slot,
|
||||
bitmap);
|
||||
|
||||
c16_t **txdataF = gNB->common_vars.txdataF[beam_nb];
|
||||
stop_meas(&gNB->dlsch_layer_mapping_stats);
|
||||
// Loop Over OFDM symbols:
|
||||
for (int l_symbol = rel15->StartSymbolIndex; l_symbol < rel15->StartSymbolIndex + rel15->NrOfSymbols; l_symbol++) {
|
||||
start_meas(&gNB->dlsch_resource_mapping_stats);
|
||||
int l_prime = 0; // single symbol layer 0
|
||||
int l_overline = get_l0(rel15->dlDmrsSymbPos);
|
||||
|
||||
#ifdef DEBUG_DLSCH_MAPPING
|
||||
printf("PDSCH resource mapping symbol %d\n", l_symbol);
|
||||
#endif
|
||||
/// DMRS QPSK modulation
|
||||
if ((dmrs_symbol_map & (1 << l_symbol))) { // DMRS time occasion
|
||||
// The reference point for is subcarrier -1 of the lowest-numbered resource block in CORESET 0 if the corresponding
|
||||
// PDCCH is associated with CORESET -1 and Type0-PDCCH common search space and is addressed to SI-RNTI
|
||||
// 2GPP TS 38.211 V15.8.0 Section 7.4.1.1.2 Mapping to physical resources
|
||||
if (l_symbol == (l_overline + 1)) // take into account the double DMRS symbols
|
||||
l_prime = 1;
|
||||
else if (l_symbol > (l_overline + 1)) { // new DMRS pair
|
||||
l_overline = l_symbol;
|
||||
l_prime = 0;
|
||||
// spawn symbol threads
|
||||
|
||||
int nb_tasks = 1;
|
||||
int num_pdsch_symbols_per_task = rel15->NrOfSymbols;
|
||||
if (gNB->num_pdsch_symbols_per_thread > 0) {
|
||||
// symbol processing in thread pool enabled
|
||||
num_pdsch_symbols_per_task = gNB->num_pdsch_symbols_per_thread;
|
||||
nb_tasks = rel15->NrOfSymbols / num_pdsch_symbols_per_task;
|
||||
if ((rel15->NrOfSymbols % num_pdsch_symbols_per_task) > 0)
|
||||
nb_tasks++;
|
||||
}
|
||||
pdschSymbolProc_t arr[nb_tasks];
|
||||
task_ans_t ans;
|
||||
init_task_ans(&ans, nb_tasks);
|
||||
int sz_arr = 0;
|
||||
unsigned int re_beginning_of_symbol = 0;
|
||||
int res = 0;
|
||||
for (int l_symbol = rel15->StartSymbolIndex; l_symbol < rel15->StartSymbolIndex + rel15->NrOfSymbols;
|
||||
l_symbol += num_pdsch_symbols_per_task) {
|
||||
pdschSymbolProc_t *rdata = &arr[sz_arr];
|
||||
rdata->ans = &ans;
|
||||
++sz_arr;
|
||||
|
||||
rdata->gNB = gNB;
|
||||
rdata->frame_parms = frame_parms;
|
||||
rdata->rel15 = rel15;
|
||||
rdata->slot = slot;
|
||||
rdata->startSymbol = l_symbol;
|
||||
res = rel15->NrOfSymbols - (l_symbol - rel15->StartSymbolIndex);
|
||||
if (res >= num_pdsch_symbols_per_task)
|
||||
rdata->numSymbols = num_pdsch_symbols_per_task;
|
||||
else
|
||||
rdata->numSymbols = res;
|
||||
rdata->layerSz2 = layerSz2;
|
||||
rdata->dlPtrsSymPos = dlPtrsSymPos;
|
||||
rdata->n_ptrs = n_ptrs;
|
||||
rdata->nb_tx_ant = nb_tx_ant;
|
||||
rdata->txdataF = txdataF[beam_nb];
|
||||
for (int s = l_symbol; s < l_symbol + rdata->numSymbols; s++) {
|
||||
rdata->re_beginning_of_symbol[s] = re_beginning_of_symbol;
|
||||
re_beginning_of_symbol += rel15->rbSize * NR_NB_SC_PER_RB;
|
||||
if (n_ptrs > 0 && is_ptrs_symbol(s, dlPtrsSymPos)) {
|
||||
re_beginning_of_symbol -= n_ptrs;
|
||||
} else if (rel15->dlDmrsSymbPos & (1 << s)) {
|
||||
re_beginning_of_symbol -= n_dmrs;
|
||||
}
|
||||
#ifdef DEBUG_DLSCH_MAPPING
|
||||
printf("dlDmrsScramblingId %d, SCID %d slot %d l_symbol %d\n", rel15->dlDmrsScramblingId, rel15->SCID, slot, l_symbol);
|
||||
#endif
|
||||
const uint32_t *gold = nr_gold_pdsch(frame_parms->N_RB_DL,
|
||||
frame_parms->symbols_per_slot,
|
||||
rel15->dlDmrsScramblingId,
|
||||
rel15->SCID,
|
||||
slot,
|
||||
l_symbol);
|
||||
// Qm = 1 as DMRS is QPSK modulated
|
||||
nr_modulation(gold, n_dmrs * DMRS_MOD_ORDER, DMRS_MOD_ORDER, (int16_t *)mod_dmrs);
|
||||
|
||||
#ifdef DEBUG_DLSCH_MAPPING
|
||||
printf("DMRS modulation (symbol %d, %d symbols, type %d):\n", l_symbol, n_dmrs, dmrs_Type);
|
||||
for (int i = 0; i < n_dmrs / 2; i += 8) {
|
||||
for (int j = 0; j < 8; j++) {
|
||||
printf("%d %d\t", mod_dmrs[i + j].r, mod_dmrs[i + j].i);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
uint32_t dmrs_idx = rel15->rbStart;
|
||||
if (rel15->refPoint == 0)
|
||||
dmrs_idx += rel15->BWPStart;
|
||||
dmrs_idx *= dmrs_Type == NFAPI_NR_DMRS_TYPE1 ? 6 : 4;
|
||||
c16_t txdataF_precoding[rel15->nrOfLayers][symbol_sz] __attribute__((aligned(64)));
|
||||
int layer_sz = 0;
|
||||
for (int layer = 0; layer < rel15->nrOfLayers; layer++) {
|
||||
layer_sz = do_onelayer(frame_parms,
|
||||
slot,
|
||||
rel15,
|
||||
layer,
|
||||
txdataF_precoding[layer],
|
||||
tx_layers[layer] + re_beginning_of_symbol,
|
||||
start_sc,
|
||||
symbol_sz,
|
||||
l_symbol,
|
||||
dlPtrsSymPos,
|
||||
n_ptrs,
|
||||
amp,
|
||||
amp_dmrs,
|
||||
l_prime,
|
||||
dmrs_Type,
|
||||
mod_dmrs + dmrs_idx);
|
||||
} // layer loop
|
||||
re_beginning_of_symbol += layer_sz;
|
||||
stop_meas(&gNB->dlsch_resource_mapping_stats);
|
||||
|
||||
start_meas(&gNB->dlsch_precoding_stats);
|
||||
for (int ant = 0; ant < frame_parms->nb_antennas_tx; ant++) {
|
||||
const size_t txdataF_offset_per_symbol = l_symbol * symbol_sz + txdataF_offset;
|
||||
do_txdataF(txdataF, symbol_sz, txdataF_precoding, gNB, rel15, ant, start_sc, txdataF_offset_per_symbol);
|
||||
reset_meas(&rdata->dlsch_resource_mapping_stats);
|
||||
reset_meas(&rdata->dlsch_precoding_stats);
|
||||
for (int l = 0; l < rel15->nrOfLayers; l++)
|
||||
rdata->tx_layers[l] = tx_layers[l];
|
||||
if (l_symbol < rel15->StartSymbolIndex + rel15->NrOfSymbols - num_pdsch_symbols_per_task) {
|
||||
task_t t = {.func = &nr_pdsch_symbol_processing, .args = rdata};
|
||||
pushTpool(&gNB->threadPool, t);
|
||||
} else {
|
||||
nr_pdsch_symbol_processing(rdata);
|
||||
}
|
||||
stop_meas(&gNB->dlsch_precoding_stats);
|
||||
}
|
||||
join_task_ans(&ans);
|
||||
for (int i = 0; i < nb_tasks; i++) {
|
||||
merge_meas(&gNB->dlsch_resource_mapping_stats, &arr[i].dlsch_resource_mapping_stats);
|
||||
merge_meas(&gNB->dlsch_precoding_stats, &arr[i].dlsch_precoding_stats);
|
||||
}
|
||||
stop_meas(&gNB->dlsch_pdsch_generation_stats);
|
||||
/* output and its parts for each dlsch should be aligned on 64 bytes (or 8 * 64 bits)
|
||||
@@ -772,7 +873,14 @@ static int do_one_dlsch(unsigned char *input_ptr, PHY_VARS_gNB *gNB, NR_gNB_DLSC
|
||||
return ((size_output_tb + 511) >> 9) << 6;
|
||||
}
|
||||
|
||||
void nr_generate_pdsch(PHY_VARS_gNB *gNB, int n_dlsch, NR_gNB_DLSCH_t *dlsch_array, int frame, int slot)
|
||||
void nr_generate_pdsch(PHY_VARS_gNB *gNB,
|
||||
int n_dlsch,
|
||||
NR_gNB_DLSCH_t *dlsch_array,
|
||||
int nb_beams,
|
||||
int nb_tx_ant,
|
||||
c16_t *txdataF[nb_beams][nb_tx_ant],
|
||||
int frame,
|
||||
int slot)
|
||||
{
|
||||
NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms;
|
||||
time_stats_t *dlsch_encoding_stats = &gNB->dlsch_encoding_stats;
|
||||
@@ -849,7 +957,13 @@ void nr_generate_pdsch(PHY_VARS_gNB *gNB, int n_dlsch, NR_gNB_DLSCH_t *dlsch_arr
|
||||
|
||||
unsigned char *output_ptr = output;
|
||||
for (int i = 0; i < n_dlsch; i++) {
|
||||
output_ptr += do_one_dlsch(output_ptr, gNB, &dlsch_array[i], slot);
|
||||
output_ptr += do_one_dlsch(output_ptr,
|
||||
gNB,
|
||||
&dlsch_array[i],
|
||||
nb_beams,
|
||||
nb_tx_ant,
|
||||
txdataF,
|
||||
slot);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,14 @@
|
||||
|
||||
#include "PHY/defs_gNB.h"
|
||||
|
||||
void nr_generate_pdsch(PHY_VARS_gNB *gNB, int n_dlsch, NR_gNB_DLSCH_t *dlsch_array, int frame, int slot);
|
||||
void nr_generate_pdsch(PHY_VARS_gNB *gNB,
|
||||
int n_dlsch,
|
||||
NR_gNB_DLSCH_t *dlsch_array,
|
||||
int nb_beams,
|
||||
int nb_tx_ant,
|
||||
c16_t *txdataF[nb_beams][nb_tx_ant],
|
||||
int frame,
|
||||
int slot);
|
||||
|
||||
int nr_dlsch_encoding(PHY_VARS_gNB *gNB,
|
||||
int n_dlsch,
|
||||
|
||||
@@ -497,6 +497,7 @@ typedef struct PHY_VARS_gNB_s {
|
||||
notifiedFIFO_t L1_rx_out;
|
||||
tpool_t threadPool;
|
||||
int num_pusch_symbols_per_thread;
|
||||
int num_pdsch_symbols_per_thread;
|
||||
int dmrs_num_antennas_per_thread;
|
||||
pthread_t L1_rx_thread;
|
||||
int L1_rx_thread_core;
|
||||
|
||||
@@ -325,10 +325,23 @@ void phy_procedures_gNB_TX(PHY_VARS_gNB *gNB,
|
||||
}
|
||||
}
|
||||
|
||||
c16_t *txdataF[gNB->common_vars.num_beams_period][cfg->carrier_config.num_tx_ant.value];
|
||||
for (int i = 0; i < gNB->common_vars.num_beams_period; i++) {
|
||||
for (int aa = 0; aa < cfg->carrier_config.num_tx_ant.value; aa++)
|
||||
txdataF[i][aa] = (c16_t*)malloc16_clear(fp->samples_per_frame_wCP * sizeof(c16_t));
|
||||
}
|
||||
|
||||
if (num_pdsch > 0) {
|
||||
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_GENERATE_DLSCH,1);
|
||||
LOG_D(PHY, "PDSCH generation started (%d) in frame %d.%d\n", num_pdsch, frame, slot);
|
||||
nr_generate_pdsch(gNB, num_pdsch, gNB->dlsch, frame, slot);
|
||||
nr_generate_pdsch(gNB,
|
||||
num_pdsch,
|
||||
gNB->dlsch,
|
||||
gNB->common_vars.num_beams_period,
|
||||
cfg->carrier_config.num_tx_ant.value,
|
||||
txdataF,
|
||||
frame,
|
||||
slot);
|
||||
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_GENERATE_DLSCH,0);
|
||||
}
|
||||
|
||||
@@ -338,13 +351,14 @@ void phy_procedures_gNB_TX(PHY_VARS_gNB *gNB,
|
||||
for (int aa = 0; aa < cfg->carrier_config.num_tx_ant.value; aa++) {
|
||||
if (gNB->phase_comp) {
|
||||
apply_nr_rotation_TX(fp,
|
||||
&gNB->common_vars.txdataF[i][aa][txdataF_offset],
|
||||
&txdataF[i][aa][txdataF_offset],
|
||||
fp->symbol_rotation[0],
|
||||
slot,
|
||||
fp->N_RB_DL,
|
||||
0,
|
||||
fp->Ncp == EXTENDED ? 12 : 14);
|
||||
}
|
||||
free_and_zero(txdataF[i][aa]);
|
||||
T(T_GNB_PHY_DL_OUTPUT_SIGNAL,
|
||||
T_INT(0),
|
||||
T_INT(frame),
|
||||
|
||||
@@ -394,6 +394,7 @@ int main(int argc, char **argv)
|
||||
uint8_t dlsch_threads = 0;
|
||||
int chest_type[2] = {0};
|
||||
uint8_t max_ldpc_iterations = 5;
|
||||
int num_pdsch_symbols_per_thread = 0;
|
||||
if ((uniqCfg = load_configmodule(argc, argv, CONFIG_ENABLECMDLINEONLY)) == 0) {
|
||||
exit_fun("[NR_DLSIM] Error, configuration module init failed\n");
|
||||
}
|
||||
@@ -584,6 +585,10 @@ int main(int argc, char **argv)
|
||||
gNBthreads[sizeof(gNBthreads)-1]=0;
|
||||
break;
|
||||
|
||||
case 'Y':
|
||||
num_pdsch_symbols_per_thread = atoi(optarg);
|
||||
break;
|
||||
|
||||
case 'Z' :
|
||||
filename_csv = strdup(optarg);
|
||||
AssertFatal(filename_csv != NULL, "strdup() error: errno %d\n", errno);
|
||||
@@ -653,6 +658,7 @@ int main(int argc, char **argv)
|
||||
printf("-T Enable PTRS, arguments list L_PTRS{0,1,2} K_PTRS{2,4}, e.g. -T 2 0 2 \n");
|
||||
printf("-U Change DMRS Config, arguments list DMRS TYPE{0=A,1=B} DMRS AddPos{0:2} DMRS ConfType{1:2}, e.g. -U 3 0 2 1 \n");
|
||||
printf("-X gNB thread pool configuration, n => no threads\n");
|
||||
printf("-Y Number of symbols processed per PDSCH generation thread\n");
|
||||
printf("-Z Output filename (.csv format) for stats\n");
|
||||
exit (-1);
|
||||
break;
|
||||
@@ -686,6 +692,7 @@ int main(int argc, char **argv)
|
||||
|
||||
AssertFatal((gNB->if_inst = NR_IF_Module_init(0)) != NULL, "Cannot register interface");
|
||||
gNB->if_inst->NR_PHY_config_req = nr_phy_config_request;
|
||||
gNB->num_pdsch_symbols_per_thread = num_pdsch_symbols_per_thread;
|
||||
|
||||
NR_ServingCellConfigCommon_t *scc = calloc(1,sizeof(*scc));;
|
||||
prepare_scc(scc);
|
||||
|
||||
Reference in New Issue
Block a user