Files
openairinterface5g/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c
T
Robert Schmidt 5951caa91e Avoid concurrent write while filling RX/CRC.indication
We observe these asserts while running concurrent UL traffic on many
UEs:

  Assertion (crc->rnti == rx->rnti) failed!
  In handle_nr_ulsch() ../../../openair2/NR_PHY_INTERFACE/NR_IF_Module.c:193
  mis-match between CRC RNTI 7221 and RX RNTI 35cf

CRC and RX indication need to be pairwise for one UE, when here we have
each for different UE. However, investigation seemed to indicate that
both arrays for CRC and RX indication contained only one PDU; it seemed
the RNTI changed while writing the CRC/RX.indication messages in
nr_fill_indication().

In this patch, we change the logic to mark a ULSCH free AFTER calling
nr_fill_indication(). It is difficult to say if this makes the assertion
disappear, but preliminary tests did not exhibit the same failure
states.

Closes #773
2024-04-04 19:00:57 +02:00

470 lines
18 KiB
C

/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.0 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
/*! \file PHY/NR_TRANSPORT/nr_ulsch_decoding.c
* \brief Top-level routines for decoding LDPC (ULSCH) transport channels from 38.212, V15.4.0 2018-12
* \author Ahmed Hussein
* \date 2019
* \version 0.1
* \company Fraunhofer IIS
* \email: ahmed.hussein@iis.fraunhofer.de
* \note
* \warning
*/
// [from gNB coding]
#include "PHY/defs_gNB.h"
#include "PHY/CODING/coding_extern.h"
#include "PHY/CODING/coding_defs.h"
#include "PHY/CODING/lte_interleaver_inline.h"
#include "PHY/CODING/nrLDPC_extern.h"
#include "PHY/NR_TRANSPORT/nr_transport_common_proto.h"
#include "PHY/NR_TRANSPORT/nr_transport_proto.h"
#include "PHY/NR_TRANSPORT/nr_ulsch.h"
#include "PHY/NR_TRANSPORT/nr_dlsch.h"
#include "SCHED_NR/sched_nr.h"
#include "SCHED_NR/fapi_nr_l1.h"
#include "defs.h"
#include "common/utils/LOG/vcd_signal_dumper.h"
#include "common/utils/LOG/log.h"
#include <syscall.h>
//#define DEBUG_ULSCH_DECODING
//#define gNB_DEBUG_TRACE
#define OAI_UL_LDPC_MAX_NUM_LLR 27000//26112 // NR_LDPC_NCOL_BG1*NR_LDPC_ZMAX = 68*384
//#define DEBUG_CRC
#ifdef DEBUG_CRC
#define PRINT_CRC_CHECK(a) a
#else
#define PRINT_CRC_CHECK(a)
#endif
//extern double cpuf;
void free_gNB_ulsch(NR_gNB_ULSCH_t *ulsch, uint16_t N_RB_UL)
{
uint16_t a_segments = MAX_NUM_NR_ULSCH_SEGMENTS_PER_LAYER*NR_MAX_NB_LAYERS; //number of segments to be allocated
if (N_RB_UL != 273) {
a_segments = a_segments*N_RB_UL;
a_segments = a_segments/273 +1;
}
if (ulsch->harq_process) {
if (ulsch->harq_process->b) {
free_and_zero(ulsch->harq_process->b);
ulsch->harq_process->b = NULL;
}
for (int r = 0; r < a_segments; r++) {
free_and_zero(ulsch->harq_process->c[r]);
free_and_zero(ulsch->harq_process->d[r]);
}
free_and_zero(ulsch->harq_process->c);
free_and_zero(ulsch->harq_process->d);
free_and_zero(ulsch->harq_process->d_to_be_cleared);
free_and_zero(ulsch->harq_process);
ulsch->harq_process = NULL;
}
}
NR_gNB_ULSCH_t new_gNB_ulsch(uint8_t max_ldpc_iterations, uint16_t N_RB_UL)
{
uint16_t a_segments = MAX_NUM_NR_ULSCH_SEGMENTS_PER_LAYER*NR_MAX_NB_LAYERS; //number of segments to be allocated
if (N_RB_UL != 273) {
a_segments = a_segments*N_RB_UL;
a_segments = a_segments/273 +1;
}
uint32_t ulsch_bytes = a_segments * 1056; // allocated bytes per segment
NR_gNB_ULSCH_t ulsch = {0};
ulsch.max_ldpc_iterations = max_ldpc_iterations;
ulsch.harq_pid = -1;
ulsch.active = false;
NR_UL_gNB_HARQ_t *harq = malloc16_clear(sizeof(*harq));
init_abort(&harq->abort_decode);
ulsch.harq_process = harq;
harq->b = malloc16_clear(ulsch_bytes * sizeof(*harq->b));
harq->c = malloc16_clear(a_segments * sizeof(*harq->c));
harq->d = malloc16_clear(a_segments * sizeof(*harq->d));
for (int r = 0; r < a_segments; r++) {
harq->c[r] = malloc16_clear(8448 * sizeof(*harq->c[r]));
harq->d[r] = malloc16_clear(68 * 384 * sizeof(*harq->d[r]));
}
harq->d_to_be_cleared = calloc(a_segments, sizeof(bool));
AssertFatal(harq->d_to_be_cleared != NULL, "out of memory\n");
return(ulsch);
}
static void nr_processULSegment(void *arg)
{
ldpcDecode_t *rdata = (ldpcDecode_t *)arg;
NR_UL_gNB_HARQ_t *ulsch_harq = rdata->ulsch_harq;
t_nrLDPC_dec_params *p_decoderParms = &rdata->decoderParms;
const int Kr = ulsch_harq->K;
const int Kr_bytes = Kr >> 3;
const int K_bits_F = Kr - ulsch_harq->F;
const int r = rdata->segment_r;
const int A = rdata->A;
const int E = rdata->E;
const int Qm = rdata->Qm;
const int rv_index = rdata->rv_index;
const int r_offset = rdata->r_offset;
const uint8_t kc = rdata->Kc;
short *ulsch_llr = rdata->ulsch_llr;
const int max_ldpc_iterations = p_decoderParms->numMaxIter;
int8_t llrProcBuf[OAI_UL_LDPC_MAX_NUM_LLR] __attribute__((aligned(32)));
t_nrLDPC_time_stats procTime = {0};
t_nrLDPC_time_stats *p_procTime = &procTime;
////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////// nr_deinterleaving_ldpc ///////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////// ulsch_llr =====> ulsch_harq->e //////////////////////////////
/// code blocks after bit selection in rate matching for LDPC code (38.212 V15.4.0 section 5.4.2.1)
int16_t harq_e[E];
nr_deinterleaving_ldpc(E, Qm, harq_e, ulsch_llr + r_offset);
// for (int i =0; i<16; i++)
// printf("rx output deinterleaving w[%d]= %d r_offset %d\n", i,ulsch_harq->w[r][i], r_offset);
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////// nr_rate_matching_ldpc_rx ////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
///////////////////////// ulsch_harq->e =====> ulsch_harq->d /////////////////////////
if (nr_rate_matching_ldpc_rx(rdata->tbslbrm,
p_decoderParms->BG,
p_decoderParms->Z,
ulsch_harq->d[r],
harq_e,
ulsch_harq->C,
rv_index,
ulsch_harq->d_to_be_cleared[r],
E,
ulsch_harq->F,
Kr - ulsch_harq->F - 2 * (p_decoderParms->Z))
== -1) {
LOG_E(PHY, "ulsch_decoding.c: Problem in rate_matching\n");
rdata->decodeIterations = max_ldpc_iterations + 1;
return;
}
ulsch_harq->d_to_be_cleared[r] = false;
memset(ulsch_harq->c[r], 0, Kr_bytes);
p_decoderParms->crc_type = crcType(ulsch_harq->C, A);
p_decoderParms->E = lenWithCrc(ulsch_harq->C, A);
// start_meas(&phy_vars_gNB->ulsch_ldpc_decoding_stats);
// set first 2*Z_c bits to zeros
int16_t z[68 * 384 + 16] __attribute__((aligned(16)));
memset(z, 0, 2 * ulsch_harq->Z * sizeof(*z));
// set Filler bits
memset(z + K_bits_F, 127, ulsch_harq->F * sizeof(*z));
// Move coded bits before filler bits
memcpy(z + 2 * ulsch_harq->Z, ulsch_harq->d[r], (K_bits_F - 2 * ulsch_harq->Z) * sizeof(*z));
// skip filler bits
memcpy(z + Kr, ulsch_harq->d[r] + (Kr - 2 * ulsch_harq->Z), (kc * ulsch_harq->Z - Kr) * sizeof(*z));
// Saturate coded bits before decoding into 8 bits values
simde__m128i *pv = (simde__m128i *)&z;
int8_t l[68 * 384 + 16] __attribute__((aligned(16)));
simde__m128i *pl = (simde__m128i *)&l;
for (int i = 0, j = 0; j < ((kc * ulsch_harq->Z) >> 4) + 1; i += 2, j++) {
pl[j] = simde_mm_packs_epi16(pv[i], pv[i + 1]);
}
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////// nrLDPC_decoder /////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////// pl =====> llrProcBuf //////////////////////////////////
rdata->decodeIterations =
ldpc_interface.LDPCdecoder(p_decoderParms, 0, 0, 0, l, llrProcBuf, p_procTime, &ulsch_harq->abort_decode);
if (rdata->decodeIterations <= p_decoderParms->numMaxIter)
memcpy(ulsch_harq->c[r],llrProcBuf, Kr>>3);
}
int decode_offload(PHY_VARS_gNB *phy_vars_gNB,
uint8_t ULSCH_id,
short *ulsch_llr,
nfapi_nr_pusch_pdu_t *pusch_pdu,
t_nrLDPC_dec_params *decParams,
uint8_t harq_pid,
uint32_t G)
{
NR_gNB_ULSCH_t *ulsch = &phy_vars_gNB->ulsch[ULSCH_id];
NR_UL_gNB_HARQ_t *harq_process = ulsch->harq_process;
int16_t z_ol[LDPC_MAX_CB_SIZE] __attribute__((aligned(16)));
int8_t l_ol[LDPC_MAX_CB_SIZE] __attribute__((aligned(16)));
uint8_t Qm = pusch_pdu->qam_mod_order;
uint8_t n_layers = pusch_pdu->nrOfLayers;
const int Kr = harq_process->K;
const int Kr_bytes = Kr >> 3;
uint32_t A = (harq_process->TBS) << 3;
const int kc = decParams->BG == 2 ? 52 : 68;
ulsch->max_ldpc_iterations = 20;
int decodeIterations = 2;
int r_offset = 0, offset = 0;
for (int r = 0; r < harq_process->C; r++) {
int E = nr_get_E(G, harq_process->C, Qm, n_layers, r);
memset(harq_process->c[r], 0, Kr_bytes);
decParams->R = nr_get_R_ldpc_decoder(pusch_pdu->pusch_data.rv_index,
E,
decParams->BG,
decParams->Z,
&harq_process->llrLen,
harq_process->round);
memcpy(z_ol, ulsch_llr + r_offset, E * sizeof(short));
simde__m128i *pv_ol128 = (simde__m128i *)&z_ol;
simde__m128i *pl_ol128 = (simde__m128i *)&l_ol;
for (int i = 0, j = 0; j < ((kc * harq_process->Z) >> 4) + 1; i += 2, j++) {
pl_ol128[j] = simde_mm_packs_epi16(pv_ol128[i], pv_ol128[i + 1]);
}
decParams->E = E;
decParams->rv = pusch_pdu->pusch_data.rv_index;
decParams->F = harq_process->F;
decParams->Qm = Qm;
decodeIterations =
ldpc_interface_offload
.LDPCdecoder(decParams, harq_pid, ULSCH_id, r, (int8_t *)&pl_ol128[0], (int8_t *)harq_process->c[r], NULL, NULL);
if (decodeIterations < 0) {
LOG_E(PHY, "ulsch_decoding.c: Problem in LDPC decoder offload\n");
return -1;
}
bool decodeSuccess = check_crc((uint8_t *)harq_process->c[r], lenWithCrc(harq_process->C, A), crcType(harq_process->C, A));
if (decodeSuccess) {
memcpy(harq_process->b + offset, harq_process->c[r], Kr_bytes - (harq_process->F >> 3) - ((harq_process->C > 1) ? 3 : 0));
offset += (Kr_bytes - (harq_process->F >> 3) - ((harq_process->C > 1) ? 3 : 0));
harq_process->processedSegments++;
} else {
LOG_D(PHY, "uplink segment error %d/%d\n", r, harq_process->C);
LOG_D(PHY, "ULSCH %d in error\n", ULSCH_id);
}
r_offset += E;
}
bool crc_valid = false;
if (harq_process->processedSegments == harq_process->C) {
// When the number of code blocks is 1 (C = 1) and ulsch_harq->processedSegments = 1, we can assume a good TB because of the
// CRC check made by the LDPC for early termination, so, no need to perform CRC check twice for a single code block
crc_valid = true;
if (harq_process->C > 1) {
crc_valid = check_crc(harq_process->b, lenWithCrc(1, A), crcType(1, A));
}
}
if (crc_valid) {
LOG_D(PHY, "ULSCH: Setting ACK for slot %d TBS %d\n", ulsch->slot, harq_process->TBS);
nr_fill_indication(phy_vars_gNB, ulsch->frame, ulsch->slot, ULSCH_id, harq_pid, 0, 0);
ulsch->active = false;
harq_process->round = 0;
} else {
LOG_D(PHY,
"[gNB %d] ULSCH: Setting NAK for SFN/SF %d/%d (pid %d, status %d, round %d, TBS %d)\n",
phy_vars_gNB->Mod_id,
ulsch->frame,
ulsch->slot,
harq_pid,
ulsch->active,
harq_process->round,
harq_process->TBS);
nr_fill_indication(phy_vars_gNB, ulsch->frame, ulsch->slot, ULSCH_id, harq_pid, 1, 0);
ulsch->handled = 1;
decodeIterations = ulsch->max_ldpc_iterations + 1;
LOG_D(PHY, "ULSCH %d in error\n", ULSCH_id);
}
ulsch->last_iteration_cnt = decodeIterations;
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_gNB_ULSCH_DECODING,0);
return 0;
}
int nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB,
uint8_t ULSCH_id,
short *ulsch_llr,
NR_DL_FRAME_PARMS *frame_parms,
nfapi_nr_pusch_pdu_t *pusch_pdu,
uint32_t frame,
uint8_t nr_tti_rx,
uint8_t harq_pid,
uint32_t G)
{
if (!ulsch_llr) {
LOG_E(PHY, "ulsch_decoding.c: NULL ulsch_llr pointer\n");
return -1;
}
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_gNB_ULSCH_DECODING, 1);
NR_gNB_ULSCH_t *ulsch = &phy_vars_gNB->ulsch[ULSCH_id];
NR_gNB_PUSCH *pusch = &phy_vars_gNB->pusch_vars[ULSCH_id];
NR_UL_gNB_HARQ_t *harq_process = ulsch->harq_process;
if (!harq_process) {
LOG_E(PHY, "ulsch_decoding.c: NULL harq_process pointer\n");
return -1;
}
// ------------------------------------------------------------------
const uint16_t nb_rb = pusch_pdu->rb_size;
const uint8_t Qm = pusch_pdu->qam_mod_order;
const uint8_t mcs = pusch_pdu->mcs_index;
const uint8_t n_layers = pusch_pdu->nrOfLayers;
// ------------------------------------------------------------------
harq_process->processedSegments = 0;
harq_process->TBS = pusch_pdu->pusch_data.tb_size;
t_nrLDPC_dec_params decParams = {.check_crc = check_crc};
decParams.BG = pusch_pdu->maintenance_parms_v3.ldpcBaseGraph;
const uint32_t A = (harq_process->TBS) << 3;
NR_gNB_PHY_STATS_t *stats = get_phy_stats(phy_vars_gNB, ulsch->rnti);
if (stats) {
stats->frame = frame;
stats->ulsch_stats.round_trials[harq_process->round]++;
for (int aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) {
stats->ulsch_stats.power[aarx] = dB_fixed_x10(pusch->ulsch_power[aarx]);
stats->ulsch_stats.noise_power[aarx] = dB_fixed_x10(pusch->ulsch_noise_power[aarx]);
}
if (!harq_process->harq_to_be_cleared) {
stats->ulsch_stats.current_Qm = Qm;
stats->ulsch_stats.current_RI = n_layers;
stats->ulsch_stats.total_bytes_tx += harq_process->TBS;
}
}
LOG_D(PHY,
"ULSCH Decoding, harq_pid %d rnti %x TBS %d G %d mcs %d Nl %d nb_rb %d, Qm %d, Coderate %f RV %d round %d new RX %d\n",
harq_pid,
ulsch->rnti,
A,
G,
mcs,
n_layers,
nb_rb,
Qm,
pusch_pdu->target_code_rate / 10240.0f,
pusch_pdu->pusch_data.rv_index,
harq_process->round,
harq_process->harq_to_be_cleared);
// [hna] Perform nr_segmenation with input and output set to NULL to calculate only (C, K, Z, F)
nr_segmentation(NULL,
NULL,
lenWithCrc(1, A), // size in case of 1 segment
&harq_process->C,
&harq_process->K,
&harq_process->Z, // [hna] Z is Zc
&harq_process->F,
decParams.BG);
uint16_t a_segments = MAX_NUM_NR_ULSCH_SEGMENTS_PER_LAYER * n_layers; // number of segments to be allocated
if (harq_process->C > a_segments) {
LOG_E(PHY, "nr_segmentation.c: too many segments %d, A %d\n", harq_process->C, A);
return(-1);
}
if (nb_rb != 273) {
a_segments = a_segments*nb_rb;
a_segments = a_segments/273 +1;
}
if (harq_process->C > a_segments) {
LOG_E(PHY,"Illegal harq_process->C %d > %d\n",harq_process->C,a_segments);
return -1;
}
#ifdef DEBUG_ULSCH_DECODING
printf("ulsch decoding nr segmentation Z %d\n", harq_process->Z);
if (!frame % 100)
printf("K %d C %d Z %d \n", harq_process->K, harq_process->C, harq_process->Z);
printf("Segmentation: C %d, K %d\n",harq_process->C,harq_process->K);
#endif
decParams.Z = harq_process->Z;
decParams.numMaxIter = ulsch->max_ldpc_iterations;
decParams.outMode = 0;
decParams.setCombIn = !harq_process->harq_to_be_cleared;
if (harq_process->harq_to_be_cleared) {
for (int r = 0; r < harq_process->C; r++)
harq_process->d_to_be_cleared[r] = true;
harq_process->harq_to_be_cleared = false;
}
if (phy_vars_gNB->ldpc_offload_flag)
return decode_offload(phy_vars_gNB, ULSCH_id, ulsch_llr, pusch_pdu, &decParams, harq_pid, G);
uint32_t offset = 0, r_offset = 0;
set_abort(&harq_process->abort_decode, false);
for (int r = 0; r < harq_process->C; r++) {
int E = nr_get_E(G, harq_process->C, Qm, n_layers, r);
union ldpcReqUnion id = {.s = {ulsch->rnti, frame, nr_tti_rx, 0, 0}};
notifiedFIFO_elt_t *req = newNotifiedFIFO_elt(sizeof(ldpcDecode_t), id.p, &phy_vars_gNB->respDecode, &nr_processULSegment);
ldpcDecode_t *rdata = (ldpcDecode_t *)NotifiedFifoData(req);
decParams.R = nr_get_R_ldpc_decoder(pusch_pdu->pusch_data.rv_index,
E,
decParams.BG,
decParams.Z,
&harq_process->llrLen,
harq_process->round);
rdata->gNB = phy_vars_gNB;
rdata->ulsch_harq = harq_process;
rdata->decoderParms = decParams;
rdata->ulsch_llr = ulsch_llr;
rdata->Kc = decParams.BG == 2 ? 52 : 68;
rdata->harq_pid = harq_pid;
rdata->segment_r = r;
rdata->nbSegments = harq_process->C;
rdata->E = E;
rdata->A = A;
rdata->Qm = Qm;
rdata->r_offset = r_offset;
rdata->Kr_bytes = harq_process->K >> 3;
rdata->rv_index = pusch_pdu->pusch_data.rv_index;
rdata->offset = offset;
rdata->ulsch = ulsch;
rdata->ulsch_id = ULSCH_id;
rdata->tbslbrm = pusch_pdu->maintenance_parms_v3.tbSizeLbrmBytes;
pushTpool(&phy_vars_gNB->threadPool, req);
LOG_D(PHY, "Added a block to decode, in pipe: %d\n", r);
r_offset += E;
offset += ((harq_process->K >> 3) - (harq_process->F >> 3) - ((harq_process->C > 1) ? 3 : 0));
}
return harq_process->C;
}