From b6bedf1216950d8dcda01503d6530e8a9fe93d87 Mon Sep 17 00:00:00 2001 From: Romain Beurdouche Date: Thu, 6 Mar 2025 13:03:14 +0000 Subject: [PATCH 1/2] fix(nrLDPC_decoder): fix numIter increment The logs of `./nr_ulsim -n100 -m19 -s12 -S12 -z2 -L4` were showing that sometimes the decoding of one segment was failing while the CRC check succeeded. This was happening because the iteration counter `numIter` was not properly managed so that when the last iteration was successful to decode the segment still `numIter` at the exit was equal to `numMaxIter` plus 1 so that decoding was considered to have failed. In other words the last iteration if it occured was always wasted. This commit offers to solve this problem by simply moving the iteration counter increment at the end of the iteration loop while remaining careful at fixing the tests relying on this counter. --- openair1/PHY/CODING/nrLDPC_decoder/nrLDPC_decoder.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openair1/PHY/CODING/nrLDPC_decoder/nrLDPC_decoder.c b/openair1/PHY/CODING/nrLDPC_decoder/nrLDPC_decoder.c index fcee71c151..742c0311d8 100644 --- a/openair1/PHY/CODING/nrLDPC_decoder/nrLDPC_decoder.c +++ b/openair1/PHY/CODING/nrLDPC_decoder/nrLDPC_decoder.c @@ -552,8 +552,6 @@ static inline uint32_t nrLDPC_decoder_core(int8_t* p_llr, uint32_t numIter = 1; int32_t pcRes = 1; // pcRes is 0 if the ldpc decoder is succesful while ((numIter <= numMaxIter) && (pcRes != 0)) { - // Increase iteration counter - numIter++; if (check_abort(ab)) { numIter = numMaxIter + 2; break; @@ -847,7 +845,7 @@ static inline uint32_t nrLDPC_decoder_core(int8_t* p_llr, pcRes = nrLDPC_cnProcPc_BG2(p_lut, cnProcBuf, cnProcBufRes, Z); NR_LDPC_PROFILER_DETAIL(stop_meas(&p_profiler->cnProcPc)); } else { - if (numIter > 2) { + if (numIter > 1) { int8_t llrOut[NR_LDPC_MAX_NUM_LLR] __attribute__((aligned(64))) = {0}; int8_t* p_llrOut = outMode == nrLDPC_outMode_LLRINT8 ? p_out : llrOut; nrLDPC_llrRes2llrOut(p_lut, p_llrOut, llrRes, Z, BG); @@ -861,6 +859,8 @@ static inline uint32_t nrLDPC_decoder_core(int8_t* p_llr, } } } + // Increase iteration counter + numIter++; } if (!p_decParams->check_crc) { int8_t llrOut[NR_LDPC_MAX_NUM_LLR] __attribute__((aligned(64))) = {0}; From 6b660e40917a459a4f591c00f80a920df350ff06 Mon Sep 17 00:00:00 2001 From: Romain Beurdouche Date: Mon, 10 Mar 2025 14:44:51 +0000 Subject: [PATCH 2/2] fix(nrLDPC_decoder): Cleaning LDPC iteration accounting in NR The idea is to make LDPC iteration accounting more standard from a C coding perspective by making two changes: 1. Now the iteration counter starts at 0 for the first iteration and goes up to max_ldpc_iterations - 1 for the last iteration. If it is equal to max_ldpc_iterations, it means that decoding failed. 2. A chain of useless returns in the UE DLSCH decoding call chain was removed. --- .../nrLDPC_coding_segment_decoder.c | 2 +- .../nrLDPC_coding_xdma/nrLDPC_coding_xdma.c | 10 +++---- .../CODING/nrLDPC_decoder/nrLDPC_decoder.c | 10 +++---- .../PHY/NR_UE_TRANSPORT/nr_dlsch_decoding.c | 28 +++++++++---------- .../NR_UE_TRANSPORT/nr_transport_proto_ue.h | 16 +++++------ .../PHY/NR_UE_TRANSPORT/nr_transport_ue.h | 2 +- openair1/PHY/defs_gNB.h | 2 +- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 4 +-- openair1/SCHED_NR_UE/phy_procedures_nr_ue.c | 13 +++------ openair1/SIMULATION/NR_PHY/dlschsim.c | 19 ++++++------- openair1/SIMULATION/NR_PHY/dlsim.c | 2 +- openair1/SIMULATION/NR_PHY/ulsim.c | 2 +- 12 files changed, 52 insertions(+), 58 deletions(-) diff --git a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_segment/nrLDPC_coding_segment_decoder.c b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_segment/nrLDPC_coding_segment_decoder.c index 9b36197e45..c7bb47c581 100644 --- a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_segment/nrLDPC_coding_segment_decoder.c +++ b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_segment/nrLDPC_coding_segment_decoder.c @@ -218,7 +218,7 @@ static void nr_process_decode_segment(void *arg) int decodeIterations = LDPCdecoder(p_decoderParms, 0, 0, 0, l, llrProcBuf, p_procTime, rdata->abort_decode); - if (decodeIterations <= p_decoderParms->numMaxIter) { + if (decodeIterations < p_decoderParms->numMaxIter) { memcpy(rdata->c, llrProcBuf, K >> 3); *rdata->decodeSuccess = true; } else { diff --git a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_xdma/nrLDPC_coding_xdma.c b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_xdma/nrLDPC_coding_xdma.c index 7960215ab7..b23db3781d 100644 --- a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_xdma/nrLDPC_coding_xdma.c +++ b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_xdma/nrLDPC_coding_xdma.c @@ -277,8 +277,8 @@ int decoder_xdma(nrLDPC_TB_decoding_parameters_t *TB_params, int frame_rx, int s for (uint32_t job = 0; job < num_threads_prepare; job++) { args_fpga_decode_prepare_t *args = &arr[job]; - if (args->no_iteration_ldpc > TB_params->max_ldpc_iterations) - no_iteration_ldpc = TB_params->max_ldpc_iterations + 1; + if (args->no_iteration_ldpc >= TB_params->max_ldpc_iterations) + no_iteration_ldpc = TB_params->max_ldpc_iterations; } // launch decode with FPGA @@ -306,12 +306,12 @@ int decoder_xdma(nrLDPC_TB_decoding_parameters_t *TB_params, int frame_rx, int s #ifdef DEBUG_CRC LOG_I(PHY, "segment %d CRC NOK\n", r); #endif - no_iteration_ldpc = TB_params->max_ldpc_iterations + 1; + no_iteration_ldpc = TB_params->max_ldpc_iterations; } for (int i = 0; i < out_CBoffset; i++) { segment_params->c[i] = multi_outdata[i + r * out_CBoffset]; } - segment_params->decodeSuccess = (no_iteration_ldpc <= TB_params->max_ldpc_iterations); + segment_params->decodeSuccess = (no_iteration_ldpc < TB_params->max_ldpc_iterations); if (segment_params->decodeSuccess) { *TB_params->processedSegments = *TB_params->processedSegments + 1; } @@ -396,7 +396,7 @@ void nr_ulsch_FPGA_decoding_prepare_blocks(void *args) == -1) { stop_meas(&segment_params->ts_rate_unmatch); LOG_E(PHY, "ulsch_decoding.c: Problem in rate_matching\n"); - no_iteration_ldpc = max_ldpc_iterations + 1; + no_iteration_ldpc = max_ldpc_iterations; arguments->no_iteration_ldpc = no_iteration_ldpc; return; } else { diff --git a/openair1/PHY/CODING/nrLDPC_decoder/nrLDPC_decoder.c b/openair1/PHY/CODING/nrLDPC_decoder/nrLDPC_decoder.c index 742c0311d8..e2ed7fce10 100644 --- a/openair1/PHY/CODING/nrLDPC_decoder/nrLDPC_decoder.c +++ b/openair1/PHY/CODING/nrLDPC_decoder/nrLDPC_decoder.c @@ -187,7 +187,7 @@ int32_t LDPCdecoder(t_nrLDPC_dec_params* p_decParams, // Launch LDPC decoder core for one segment int numIter = nrLDPC_decoder_core(p_llr, p_out, numLLR, p_lut, p_decParams, p_profiler, ab); - if (numIter > p_decParams->numMaxIter) { + if (numIter >= p_decParams->numMaxIter) { LOG_D(PHY, "set abort: %d, %d\n", numIter, p_decParams->numMaxIter); set_abort(ab, true); } @@ -549,11 +549,11 @@ static inline uint32_t nrLDPC_decoder_core(int8_t* p_llr, // estimated after only one iteration // First iteration finished - uint32_t numIter = 1; + uint32_t numIter = 0; int32_t pcRes = 1; // pcRes is 0 if the ldpc decoder is succesful - while ((numIter <= numMaxIter) && (pcRes != 0)) { + while ((numIter < numMaxIter) && (pcRes != 0)) { if (check_abort(ab)) { - numIter = numMaxIter + 2; + numIter = numMaxIter; break; } // CN processing @@ -845,7 +845,7 @@ static inline uint32_t nrLDPC_decoder_core(int8_t* p_llr, pcRes = nrLDPC_cnProcPc_BG2(p_lut, cnProcBuf, cnProcBufRes, Z); NR_LDPC_PROFILER_DETAIL(stop_meas(&p_profiler->cnProcPc)); } else { - if (numIter > 1) { + if (numIter > 0) { int8_t llrOut[NR_LDPC_MAX_NUM_LLR] __attribute__((aligned(64))) = {0}; int8_t* p_llrOut = outMode == nrLDPC_outMode_LLRINT8 ? p_out : llrOut; nrLDPC_llrRes2llrOut(p_lut, p_llrOut, llrRes, Z, BG); diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_decoding.c b/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_decoding.c index a3138f5d7f..e6b4503e1b 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_decoding.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_decoding.c @@ -59,14 +59,14 @@ void nr_dlsch_unscrambling(int16_t *llr, uint32_t size, uint8_t q, uint32_t Nid, /*! \brief Prepare necessary parameters for nrLDPC_coding_interface */ -uint32_t nr_dlsch_decoding(PHY_VARS_NR_UE *phy_vars_ue, - const UE_nr_rxtx_proc_t *proc, - NR_UE_DLSCH_t *dlsch, - int16_t **dlsch_llr, - uint8_t **b, - int *G, - int nb_dlsch, - uint8_t *DLSCH_ids) +void nr_dlsch_decoding(PHY_VARS_NR_UE *phy_vars_ue, + const UE_nr_rxtx_proc_t *proc, + NR_UE_DLSCH_t *dlsch, + int16_t **dlsch_llr, + uint8_t **b, + int *G, + int nb_dlsch, + uint8_t *DLSCH_ids) { nrLDPC_TB_decoding_parameters_t TBs[nb_dlsch]; memset(TBs, 0, sizeof(TBs)); @@ -100,7 +100,7 @@ uint32_t nr_dlsch_decoding(PHY_VARS_NR_UE *phy_vars_ue, if (!harq_process) { LOG_E(PHY, "dlsch_decoding_slot.c: NULL harq_process pointer\n"); - return dlsch[DLSCH_id].max_ldpc_iterations + 1; + return; } nrLDPC_TB_decoding_parameters_t *TB_parameters = &TBs[pdsch_id]; @@ -164,7 +164,7 @@ uint32_t nr_dlsch_decoding(PHY_VARS_NR_UE *phy_vars_ue, if (harq_process->C > MAX_NUM_NR_DLSCH_SEGMENTS_PER_LAYER * TB_parameters->nb_layers) { LOG_E(PHY, "nr_segmentation.c: too many segments %d, A %d\n", harq_process->C, TB_parameters->A); - return dlsch[DLSCH_id].max_ldpc_iterations + 1; + return; } if (LOG_DEBUGFLAG(DEBUG_DLSCH_DECOD) && (!slot_parameters.frame % 100)) @@ -242,7 +242,8 @@ uint32_t nr_dlsch_decoding(PHY_VARS_NR_UE *phy_vars_ue, int ret_decoder = phy_vars_ue->nrLDPC_coding_interface.nrLDPC_coding_decoder(&slot_parameters); if (ret_decoder != 0) { - return dlsch->max_ldpc_iterations + 1; + LOG_E(PHY, "nrLDPC_coding_decoder returned an error: %d\n", ret_decoder); + return; } // post decode @@ -316,11 +317,11 @@ uint32_t nr_dlsch_decoding(PHY_VARS_NR_UE *phy_vars_ue, if (harq_process->decodeResult) { LOG_D(PHY, "DLSCH received ok \n"); harq_process->status = SCH_IDLE; - dlsch->last_iteration_cnt = dlsch->max_ldpc_iterations; + dlsch->last_iteration_cnt = dlsch->max_ldpc_iterations - 1; } else { LOG_D(PHY, "DLSCH received nok \n"); kpiStructure.nb_nack++; - dlsch->last_iteration_cnt = dlsch->max_ldpc_iterations + 1; + dlsch->last_iteration_cnt = dlsch->max_ldpc_iterations; UEdumpScopeData(phy_vars_ue, proc->nr_slot_rx, proc->frame_rx, "DLSCH_NACK"); } @@ -355,5 +356,4 @@ uint32_t nr_dlsch_decoding(PHY_VARS_NR_UE *phy_vars_ue, } - return dlsch[0].last_iteration_cnt; } diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h index 0ca616ba34..bb1a6c03df 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h @@ -81,14 +81,14 @@ void nr_conjch0_mult_ch1(int *ch0, @param[in] DLSCH_ids array of active downlink shared channels @returns 0 on success, 1 on unsuccessful decoding */ -uint32_t nr_dlsch_decoding(PHY_VARS_NR_UE *phy_vars_ue, - const UE_nr_rxtx_proc_t *proc, - NR_UE_DLSCH_t *dlsch, - short **dlsch_llr, - uint8_t **b, - int *G, - int nb_dlsch, - uint8_t *DLSCH_ids); +void nr_dlsch_decoding(PHY_VARS_NR_UE *phy_vars_ue, + const UE_nr_rxtx_proc_t *proc, + NR_UE_DLSCH_t *dlsch, + short **dlsch_llr, + uint8_t **b, + int *G, + int nb_dlsch, + uint8_t *DLSCH_ids); /** \brief This is the alternative top-level entry point for ULSCH encoding in UE. It handles all the HARQ processes in only one call. The routine first diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_ue.h b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_ue.h index 419988a961..07917b9075 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_ue.h +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_ue.h @@ -147,7 +147,7 @@ typedef struct { /// Maximum number of LDPC iterations uint8_t max_ldpc_iterations; /// number of iterations used in last turbo decoding - uint8_t last_iteration_cnt; + int8_t last_iteration_cnt; /// bit mask of PT-RS ofdm symbol indicies uint16_t ptrs_symbols; // PTRS symbol index, to be updated every PTRS symbol within a slot. diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index eb0a1acc0e..47d41db44b 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -239,7 +239,7 @@ typedef struct { /// Maximum number of LDPC iterations uint8_t max_ldpc_iterations; /// number of iterations used in last LDPC decoding - uint8_t last_iteration_cnt; + int8_t last_iteration_cnt; /// Status Flag indicating for this ULSCH bool active; /// Flag to indicate that the UL configuration has been handled. Used to remove a stale ULSCH when frame wraps around diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index 1484d44ddf..4f1f8ff010 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -424,7 +424,7 @@ static int nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, boo LOG_D(PHY, "ULSCH received ok \n"); ulsch->active = false; ulsch_harq->round = 0; - ulsch->last_iteration_cnt = ulsch->max_ldpc_iterations; // Setting to max_ldpc_iterations is sufficient given that this variable is only used for checking for failure + ulsch->last_iteration_cnt = ulsch->max_ldpc_iterations - 1; // Setting to max_ldpc_iterations - 1 is sufficient given that this variable is only used for checking for failure } else { LOG_D(PHY, "[gNB %d] ULSCH: Setting NAK for SFN/SF %d/%d (pid %d, ndi %d, status %d, round %d, RV %d, prb_start %d, prb_size %d, " @@ -444,7 +444,7 @@ static int nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, boo gNBdumpScopeData(gNB, ulsch->slot, ulsch->frame, "ULSCH_NACK"); ulsch->handled = 1; LOG_D(PHY, "ULSCH %d in error\n",ULSCH_id); - ulsch->last_iteration_cnt = ulsch->max_ldpc_iterations + 1; // Setting to max_ldpc_iterations + 1 is sufficient given that this variable is only used for checking for failure + ulsch->last_iteration_cnt = ulsch->max_ldpc_iterations; // Setting to max_ldpc_iterations is sufficient given that this variable is only used for checking for failure } } diff --git a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c index b57bf4eff3..283673a6df 100644 --- a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c +++ b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c @@ -686,20 +686,18 @@ static uint32_t compute_csi_rm_unav_res(fapi_nr_dl_config_dlsch_pdu_rel15_t *dls /*! \brief Process the whole DLSCH slot */ -static bool nr_ue_dlsch_procedures(PHY_VARS_NR_UE *ue, +static void nr_ue_dlsch_procedures(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, NR_UE_DLSCH_t dlsch[2], int16_t *llr[2]) { if (dlsch[0].active == false) { LOG_E(PHY, "DLSCH should be active when calling this function\n"); - return true; + return; } - bool dec = false; int harq_pid = dlsch[0].dlsch_config.harq_process_nbr; int frame_rx = proc->frame_rx; int nr_slot_rx = proc->nr_slot_rx; - uint32_t ret = UINT32_MAX; NR_DL_UE_HARQ_t *dl_harq0 = &ue->dl_harq_processes[0][harq_pid]; NR_DL_UE_HARQ_t *dl_harq1 = &ue->dl_harq_processes[1][harq_pid]; uint16_t dmrs_len = get_num_dmrs(dlsch[0].dlsch_config.dlDmrsSymbPos); @@ -732,7 +730,7 @@ static bool nr_ue_dlsch_procedures(PHY_VARS_NR_UE *ue, const int ack_nack_slot_and_frame = (proc->nr_slot_rx + dlsch[0].dlsch_config.k1_feedback) + proc->frame_rx * ue->frame_parms.slots_per_frame; dynamic_barrier_join(&ue->process_slot_tx_barriers[ack_nack_slot_and_frame % NUM_PROCESS_SLOT_TX_BARRIERS]); - return false; + return; } int G[2]; @@ -765,11 +763,9 @@ static bool nr_ue_dlsch_procedures(PHY_VARS_NR_UE *ue, } start_meas_nr_ue_phy(ue, DLSCH_DECODING_STATS); - ret = nr_dlsch_decoding(ue, proc, dlsch, llr, p_b, G, nb_dlsch, DLSCH_ids); + nr_dlsch_decoding(ue, proc, dlsch, llr, p_b, G, nb_dlsch, DLSCH_ids); stop_meas_nr_ue_phy(ue, DLSCH_DECODING_STATS); - if (ret < ue->max_ldpc_iterations + 1) dec = true; - int ind_type = -1; switch (dlsch[0].rnti_type) { case TYPE_RA_RNTI_: @@ -829,7 +825,6 @@ static bool nr_ue_dlsch_procedures(PHY_VARS_NR_UE *ue, else if (ue->phy_sim_dlsch_b && is_cw1_active == ACTIVE) memcpy(ue->phy_sim_dlsch_b, p_b[1], dlsch_bytes); - return dec; } static bool is_ssb_index_transmitted(const PHY_VARS_NR_UE *ue, const int index) diff --git a/openair1/SIMULATION/NR_PHY/dlschsim.c b/openair1/SIMULATION/NR_PHY/dlschsim.c index de5c598015..977970c9f7 100644 --- a/openair1/SIMULATION/NR_PHY/dlschsim.c +++ b/openair1/SIMULATION/NR_PHY/dlschsim.c @@ -123,7 +123,6 @@ int main(int argc, char **argv) NR_DL_FRAME_PARMS *frame_parms; double sigma; unsigned char qbits = 8; - int ret; int loglvl = OAILOG_WARNING; uint8_t dlsch_threads = 0; float target_error_rate = 0.01; @@ -577,18 +576,18 @@ int main(int argc, char **argv) short *p_channel_output_fixed = channel_output_fixed; uint8_t *p_b = b; int available_bits_array[1] = { available_bits }; - ret = nr_dlsch_decoding(UE, - &proc, - dlsch0_ue, - &p_channel_output_fixed, - &p_b, - available_bits_array, - 1, - DLSCH_ids); + nr_dlsch_decoding(UE, + &proc, + dlsch0_ue, + &p_channel_output_fixed, + &p_b, + available_bits_array, + 1, + DLSCH_ids); vcd_signal_dumper_dump_function_by_name(VCD_SIGNAL_DUMPER_FUNCTIONS_DLSCH_DECODING0, VCD_FUNCTION_OUT); - if (ret > dlsch0_ue->max_ldpc_iterations) + if (dlsch0_ue->last_iteration_cnt > dlsch0_ue->max_ldpc_iterations) n_errors++; //count errors diff --git a/openair1/SIMULATION/NR_PHY/dlsim.c b/openair1/SIMULATION/NR_PHY/dlsim.c index d6227e7b7d..a74a80d7bc 100644 --- a/openair1/SIMULATION/NR_PHY/dlsim.c +++ b/openair1/SIMULATION/NR_PHY/dlsim.c @@ -1163,7 +1163,7 @@ printf("%d\n", slot); //---------------------- count errors ---------------------- //---------------------------------------------------------- - if (dlsch0->last_iteration_cnt >= dlsch0->max_ldpc_iterations+1) + if (dlsch0->last_iteration_cnt >= dlsch0->max_ldpc_iterations) n_errors[round]++; int16_t *UE_llr = (int16_t*)UE->phy_sim_pdsch_llr; diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c index 946274a7bd..adec328bc4 100644 --- a/openair1/SIMULATION/NR_PHY/ulsim.c +++ b/openair1/SIMULATION/NR_PHY/ulsim.c @@ -1420,7 +1420,7 @@ int main(int argc, char *argv[]) 0); } - if ((ulsch_gNB->last_iteration_cnt >= ulsch_gNB->max_ldpc_iterations + 1) || ul_proc_error == 1) { + if ((ulsch_gNB->last_iteration_cnt >= ulsch_gNB->max_ldpc_iterations) || ul_proc_error == 1) { error_flag = 1; n_errors[round]++; crc_status = 1;