Compare commits

...

7 Commits

Author SHA1 Message Date
Romain Beurdouche
a02ce6aaf1 WIP: feat(nr_dlsch): local txdataF 2026-02-26 14:09:18 +00:00
Romain Beurdouche
28dddbfa1e fix(nr_dlsch): time meas in the TX symbol processing tasks were not thread safe 2026-02-26 11:53:27 +01:00
Romain Beurdouche
d0b6bf2039 feat(NR_SA_Tutorial_COTS_UE): rework real-time performance section
Rework the real-time performance section of documentation
to include explanation on the `--tx-sym` option
and on the AMD EPYC core complex behavior
2026-02-24 14:50:41 +01:00
Romain Beurdouche
45f3905692 feat(nr_dlsch): option to provide the number of symbols per thread
* Add option `--tx-sym` for the softmodems and `-Y` for `nr_dlsim`
  to provide the number of symbols processed per thread.
  It defaults to 0 which makes that every symbols are processed in one thread.
* The last symbol processing task is processed in the L1 TX thread.
2026-02-05 16:30:20 +01:00
Romain Beurdouche
d96f26a753 fix(nr_dlsch): symbol offset calculation
There was an indexing error in the calculation of the symbol offset `re_beginning_of_symbol`.
The symbol was tested to hold PTRS or DMRS based on the index of the
first symbol processed in the task and not based on the index of the
symbol as it should be.
Then PDSCH generation was not working properly for more than one symbol
per task.
2026-02-05 11:54:57 +01:00
Robert Schmidt
3a7967e76f Reformat 2026-02-05 11:54:57 +01:00
Raymond Knopp
d5620c7ec1 added thread-pool support for TX symbol processing
removed warnings in nr_dlsch.c and changed return of do_onelayer
2026-02-05 11:54:57 +01:00
8 changed files with 309 additions and 121 deletions

View File

@@ -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

View File

@@ -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));

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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,

View File

@@ -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;

View File

@@ -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),

View File

@@ -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);