/*
 * 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/phy_extern.h"
#include "PHY/CODING/coding_extern.h"
#include "PHY/CODING/coding_defs.h"
#include "PHY/CODING/lte_interleaver_inline.h"
#include "PHY/CODING/nrLDPC_decoder/nrLDPC_decoder.h"
#include "PHY/CODING/nrLDPC_decoder/nrLDPC_types.h"
#include "PHY/NR_TRANSPORT/nr_transport_common_proto.h"
#include "PHY/NR_TRANSPORT/nr_ulsch.h"
#include "PHY/NR_TRANSPORT/nr_dlsch.h"
#include "SCHED_NR/sched_nr.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 PRINT_CRC_CHECK

static uint64_t nb_total_decod =0;
static uint64_t nb_error_decod =0;

//extern double cpuf;

void free_gNB_ulsch(NR_gNB_ULSCH_t **ulschptr) {
  int i,r;
  NR_gNB_ULSCH_t *ulsch = *ulschptr;

  if (ulsch) {
    for (i=0; i<NR_MAX_ULSCH_HARQ_PROCESSES; i++) {

      if (ulsch->harq_processes[i]) {
        if (ulsch->harq_processes[i]->b) {
          free16(ulsch->harq_processes[i]->b,MAX_NR_ULSCH_PAYLOAD_BYTES);
          ulsch->harq_processes[i]->b = NULL;
        }
        for (r=0; r<MAX_NUM_NR_ULSCH_SEGMENTS; r++) {
          free16(ulsch->harq_processes[i]->c[r],(8448)*sizeof(uint8_t));
          ulsch->harq_processes[i]->c[r] = NULL;
        }
        for (r=0; r<MAX_NUM_NR_ULSCH_SEGMENTS; r++) {
          if (ulsch->harq_processes[i]->d[r]) {
            free16(ulsch->harq_processes[i]->d[r],(68*384)*sizeof(int16_t));
            ulsch->harq_processes[i]->d[r] = NULL;
          }
        }
        for (r=0; r<(MAX_NUM_NR_ULSCH_SEGMENTS); r++) {
          if (ulsch->harq_processes[i]->p_nrLDPC_procBuf[r]){
            nrLDPC_free_mem(ulsch->harq_processes[i]->p_nrLDPC_procBuf[r]);
            ulsch->harq_processes[i]->p_nrLDPC_procBuf[r] = NULL;
          }
        }
        free16(ulsch->harq_processes[i],sizeof(NR_UL_gNB_HARQ_t));
        ulsch->harq_processes[i] = NULL;
      }
    }
    free16(ulsch,sizeof(NR_gNB_ULSCH_t));
    *ulschptr = NULL;
  }
}


NR_gNB_ULSCH_t *new_gNB_ulsch(uint8_t max_ldpc_iterations,uint8_t N_RB_UL, uint8_t abstraction_flag)
{

  NR_gNB_ULSCH_t *ulsch;
  uint8_t exit_flag = 0,i,r;
  unsigned char bw_scaling =1;

  switch (N_RB_UL) {
    case 106:
      bw_scaling =2;
    break;

    default:
      bw_scaling =1;
    break;
  }

  ulsch = (NR_gNB_ULSCH_t *)malloc16(sizeof(NR_gNB_ULSCH_t));

  if (ulsch) {

    memset(ulsch,0,sizeof(NR_gNB_ULSCH_t));

    ulsch->max_ldpc_iterations = max_ldpc_iterations;
    ulsch->Mlimit = 4;

    for (i=0; i<NR_MAX_ULSCH_HARQ_PROCESSES; i++) {

      ulsch->harq_processes[i] = (NR_UL_gNB_HARQ_t *)malloc16(sizeof(NR_UL_gNB_HARQ_t));

      if (ulsch->harq_processes[i]) {

        memset(ulsch->harq_processes[i],0,sizeof(NR_UL_gNB_HARQ_t));

        ulsch->harq_processes[i]->b = (uint8_t*)malloc16(MAX_NR_ULSCH_PAYLOAD_BYTES/bw_scaling);

        if (ulsch->harq_processes[i]->b)
          memset(ulsch->harq_processes[i]->b,0,MAX_NR_ULSCH_PAYLOAD_BYTES/bw_scaling);
        else
          exit_flag=3;

        if (abstraction_flag == 0) {
          for (r=0; r<MAX_NUM_NR_ULSCH_SEGMENTS/bw_scaling; r++) {

            ulsch->harq_processes[i]->p_nrLDPC_procBuf[r] = nrLDPC_init_mem();

            ulsch->harq_processes[i]->c[r] = (uint8_t*)malloc16(8448*sizeof(uint8_t));

            if (ulsch->harq_processes[i]->c[r])
              memset(ulsch->harq_processes[i]->c[r],0,8448*sizeof(uint8_t));
            else
              exit_flag=2;

            ulsch->harq_processes[i]->d[r] = (int16_t*)malloc16((68*384)*sizeof(int16_t));

            if (ulsch->harq_processes[i]->d[r])
              memset(ulsch->harq_processes[i]->d[r],0,(68*384)*sizeof(int16_t));
            else
              exit_flag=2;
          }
        }
      } else {
        exit_flag=1;
      }
    }

    if (exit_flag==0)
      return(ulsch);
  }

  printf("new_gNB_ulsch with size %zu: exit_flag = %u\n",sizeof(NR_UL_gNB_HARQ_t), exit_flag);
  free_gNB_ulsch(&ulsch);
  return(NULL);
}

void clean_gNB_ulsch(NR_gNB_ULSCH_t *ulsch)
{
  unsigned char i, j;

  if (ulsch) {
    ulsch->harq_mask = 0;
    ulsch->bundling = 0;
    ulsch->beta_offset_cqi_times8 = 0;
    ulsch->beta_offset_ri_times8 = 0;
    ulsch->beta_offset_harqack_times8 = 0;
    ulsch->Msg3_active = 0;
    ulsch->Msg3_flag = 0;
    ulsch->Msg3_subframe = 0;
    ulsch->Msg3_frame = 0;
    ulsch->rnti = 0;
    ulsch->rnti_type = 0;
    ulsch->cyclicShift = 0;
    ulsch->cooperation_flag = 0;
    ulsch->Mlimit = 0;
    ulsch->max_ldpc_iterations = 0;
    ulsch->last_iteration_cnt = 0;
    ulsch->num_active_cba_groups = 0;
    for (i=0;i<NUM_MAX_CBA_GROUP;i++) ulsch->cba_rnti[i] = 0;
    for (i=0;i<NR_MAX_SLOTS_PER_FRAME;i++) ulsch->harq_process_id[i] = 0;

    for (i=0; i<NR_MAX_ULSCH_HARQ_PROCESSES; i++) {
      if (ulsch->harq_processes[i]){
        /// Nfapi ULSCH PDU
        //nfapi_nr_ul_config_ulsch_pdu ulsch_pdu;
        ulsch->harq_processes[i]->frame=0;
        ulsch->harq_processes[i]->subframe=0;
        ulsch->harq_processes[i]->round=0;
        ulsch->harq_processes[i]->TPC=0;
        ulsch->harq_processes[i]->mimo_mode=0;
        ulsch->harq_processes[i]->dci_alloc=0;
        ulsch->harq_processes[i]->rar_alloc=0;
        ulsch->harq_processes[i]->status=0;
        ulsch->harq_processes[i]->subframe_scheduling_flag=0;
        ulsch->harq_processes[i]->subframe_cba_scheduling_flag=0;
        ulsch->harq_processes[i]->phich_active=0;
        ulsch->harq_processes[i]->phich_ACK=0;
        ulsch->harq_processes[i]->previous_first_rb=0;
        ulsch->harq_processes[i]->handled=0;
        ulsch->harq_processes[i]->delta_TF=0;

        ulsch->harq_processes[i]->TBS=0;
        /// Pointer to the payload (38.212 V15.4.0 section 5.1)
        //uint8_t *b;
        ulsch->harq_processes[i]->B=0;
        /// Pointers to code blocks after code block segmentation and CRC attachment (38.212 V15.4.0 section 5.2.2)
        //uint8_t *c[MAX_NUM_NR_ULSCH_SEGMENTS];
        ulsch->harq_processes[i]->K=0;
        ulsch->harq_processes[i]->F=0;
        ulsch->harq_processes[i]->C=0;
        /// Pointers to code blocks after LDPC coding (38.212 V15.4.0 section 5.3.2)
        //int16_t *d[MAX_NUM_NR_ULSCH_SEGMENTS];
        /// LDPC processing buffer
        //t_nrLDPC_procBuf* p_nrLDPC_procBuf[MAX_NUM_NR_ULSCH_SEGMENTS];
        ulsch->harq_processes[i]->Z=0;
        /// code blocks after bit selection in rate matching for LDPC code (38.212 V15.4.0 section 5.4.2.1)
        //int16_t e[MAX_NUM_NR_DLSCH_SEGMENTS][3*8448];
        ulsch->harq_processes[i]->E=0;


        ulsch->harq_processes[i]->n_DMRS=0;
        ulsch->harq_processes[i]->n_DMRS2=0;
        ulsch->harq_processes[i]->previous_n_DMRS=0;


        ulsch->harq_processes[i]->cqi_crc_status=0;
        for (j=0;j<MAX_CQI_BYTES;j++) ulsch->harq_processes[i]->o[j]=0;
        ulsch->harq_processes[i]->uci_format=0;
        ulsch->harq_processes[i]->Or1=0;
        ulsch->harq_processes[i]->Or2=0;
        ulsch->harq_processes[i]->o_RI[0]=0; ulsch->harq_processes[i]->o_RI[1]=0;
        ulsch->harq_processes[i]->O_RI=0;
        ulsch->harq_processes[i]->o_ACK[0]=0; ulsch->harq_processes[i]->o_ACK[1]=0;
        ulsch->harq_processes[i]->o_ACK[2]=0; ulsch->harq_processes[i]->o_ACK[3]=0;
        ulsch->harq_processes[i]->O_ACK=0;
        ulsch->harq_processes[i]->V_UL_DAI=0;
        /// "q" sequences for CQI/PMI (for definition see 36-212 V8.6 2009-03, p.27)
        //int8_t q[MAX_CQI_PAYLOAD];
        ulsch->harq_processes[i]->o_RCC=0;
        /// coded and interleaved CQI bits
        //int8_t o_w[(MAX_CQI_BITS+8)*3];
        /// coded CQI bits
        //int8_t o_d[96+((MAX_CQI_BITS+8)*3)];
        for (j=0;j<MAX_ACK_PAYLOAD;j++) ulsch->harq_processes[i]->q_ACK[j]=0;
        for (j=0;j<MAX_RI_PAYLOAD;j++) ulsch->harq_processes[i]->q_RI[j]=0;
        /// Temporary h sequence to flag PUSCH_x/PUSCH_y symbols which are not scrambled
        //uint8_t h[MAX_NUM_CHANNEL_BITS];
        /// soft bits for each received segment ("w"-sequence)(for definition see 36-212 V8.6 2009-03, p.15)
        //int16_t w[MAX_NUM_ULSCH_SEGMENTS][3*(6144+64)];
      }
    }
  }
}

#ifdef PRINT_CRC_CHECK
  static uint32_t prnt_crc_cnt = 0;
#endif

uint32_t nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB,
                           uint8_t UE_id,
                           short *ulsch_llr,
                           NR_DL_FRAME_PARMS *frame_parms,
                           uint32_t frame,
                           uint16_t nb_symb_sch,
                           uint8_t nr_tti_rx,
                           uint8_t harq_pid,
                           uint8_t is_crnti)
{

  uint32_t A,E;
  uint32_t G;
  uint32_t ret,offset;
  int32_t no_iteration_ldpc, length_dec;
  uint32_t r,r_offset=0,Kr=8424,Kr_bytes,K_bytes_F,err_flag=0;
  uint8_t crc_type;
  int8_t llrProcBuf[OAI_UL_LDPC_MAX_NUM_LLR] __attribute__ ((aligned(32)));

#ifdef PRINT_CRC_CHECK
  prnt_crc_cnt++;
#endif
  

  NR_gNB_ULSCH_t                       *ulsch                 = phy_vars_gNB->ulsch[UE_id][0];
  NR_UL_gNB_HARQ_t                     *harq_process          = ulsch->harq_processes[harq_pid];
  nfapi_nr_ul_config_ulsch_pdu_rel15_t *nfapi_ulsch_pdu_rel15 = &harq_process->ulsch_pdu.ulsch_pdu_rel15;
  
  t_nrLDPC_dec_params decParams;
  t_nrLDPC_dec_params* p_decParams    = &decParams;
  t_nrLDPC_time_stats procTime;
  t_nrLDPC_time_stats* p_procTime     = &procTime ;
  if (!harq_process) {
    printf("ulsch_decoding.c: NULL harq_process pointer\n");
    return (ulsch->max_ldpc_iterations + 1);
  }
  t_nrLDPC_procBuf** p_nrLDPC_procBuf = harq_process->p_nrLDPC_procBuf;

  int16_t  z [68*384];
  int8_t   l [68*384];
  uint8_t  kc=255;
  uint8_t  Ilbrm        = 0;
  uint32_t Tbslbrm     = 950984;
  double   Coderate    = 0.0;
  
  // ------------------------------------------------------------------
  uint16_t nb_rb          = nfapi_ulsch_pdu_rel15->number_rbs;
  uint16_t number_symbols = nfapi_ulsch_pdu_rel15->number_symbols;
  uint8_t Qm              = nfapi_ulsch_pdu_rel15->Qm;
  uint16_t R               = nfapi_ulsch_pdu_rel15->R;
  uint8_t mcs             = nfapi_ulsch_pdu_rel15->mcs;
  uint8_t n_layers        = nfapi_ulsch_pdu_rel15->n_layers;
  uint8_t nb_re_dmrs      = nfapi_ulsch_pdu_rel15->nb_re_dmrs;
  uint8_t length_dmrs     = nfapi_ulsch_pdu_rel15->length_dmrs;
  // ------------------------------------------------------------------

  uint32_t i,j;

  __m128i *pv = (__m128i*)&z;
  __m128i *pl = (__m128i*)&l;
  
  
   if (!ulsch_llr) {
    printf("ulsch_decoding.c: NULL ulsch_llr pointer\n");
    return (ulsch->max_ldpc_iterations + 1);
  }

  if (!frame_parms) {
    printf("ulsch_decoding.c: NULL frame_parms pointer\n");
    return (ulsch->max_ldpc_iterations + 1);
  }

  // harq_process->trials[nfapi_ulsch_pdu_rel15->round]++;
  harq_process->TBS = nr_compute_tbs(Qm, R, nb_rb, number_symbols, nb_re_dmrs*length_dmrs, 0, n_layers);

  A   = harq_process->TBS;
  ret = ulsch->max_ldpc_iterations + 1;

  G = nr_get_G(nb_rb, number_symbols, nb_re_dmrs, length_dmrs, Qm, n_layers);

  LOG_D(PHY,"ULSCH Decoding, harq_pid %d TBS %d G %d mcs %d Nl %d nb_symb_sch %d nb_rb %d\n",harq_pid,A,G, mcs, n_layers, nb_symb_sch,nb_rb);

  if (harq_process->round == 0) {

    // This is a new packet, so compute quantities regarding segmentation
    harq_process->B = A+24;

    if (R<1024)
      Coderate = (float) R /(float) 1024;
    else
      Coderate = (float) R /(float) 2048;

    if ((A <=292) || ((A<=3824) && (Coderate <= 0.6667)) || Coderate <= 0.25){
      p_decParams->BG = 2;
      if (Coderate < 0.3333) {
      p_decParams->R = 15;
      kc = 52;
    }
    else if (Coderate <0.6667) {
      p_decParams->R = 13;
      kc = 32;
    }
    else {
      p_decParams->R = 23;
      kc = 17;
    }
  } else {
    p_decParams->BG = 1;
    if (Coderate < 0.6667) {
      p_decParams->R = 13;
      kc = 68;
    }
    else if (Coderate <0.8889) {
      p_decParams->R = 23;
      kc = 35;
    }
    else {
      p_decParams->R = 89;
      kc = 27;
    }
  }

    // [hna] Perform nr_segmenation with input and output set to NULL to calculate only (B, C, K, Z, F)
    nr_segmentation(NULL,
                    NULL,
                    harq_process->B,
                    &harq_process->C,
                    &harq_process->K,
                    &harq_process->Z, // [hna] Z is Zc
                    &harq_process->F,
                    p_decParams->BG);

#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 nl %d \n", harq_process->K, harq_process->C, harq_process->Z, harq_process->Nl);
#endif
  }
  p_decParams->Z = harq_process->Z;


  p_decParams->numMaxIter = ulsch->max_ldpc_iterations;
  p_decParams->outMode= 0;

  err_flag = 0;
  r_offset = 0;

  unsigned char bw_scaling =1;

  switch (frame_parms->N_RB_UL) {

    case 106:
      bw_scaling =2;
      break;

    default:
      bw_scaling =1;
      break;
  }

  if (harq_process->C > MAX_NUM_NR_ULSCH_SEGMENTS/bw_scaling) {
    LOG_E(PHY,"Illegal harq_process->C %d > %d\n",harq_process->C,MAX_NUM_NR_ULSCH_SEGMENTS/bw_scaling);
    return (ulsch->max_ldpc_iterations + 1);
  }
#ifdef DEBUG_ULSCH_DECODING
  printf("Segmentation: C %d, K %d\n",harq_process->C,harq_process->K);
#endif

  //opp_enabled=1;

  Kr = harq_process->K;
  Kr_bytes = Kr>>3;

  K_bytes_F = Kr_bytes-(harq_process->F>>3);

  for (r=0; r<harq_process->C; r++) {
    E = nr_get_E(G, harq_process->C, nfapi_ulsch_pdu_rel15->Qm, nfapi_ulsch_pdu_rel15->n_layers, r);

#if gNB_TIMING_TRACE
    start_meas(ulsch_deinterleaving_stats);
#endif

    ////////////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////// nr_deinterleaving_ldpc ///////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////////////////

    //////////////////////////// ulsch_llr =====> harq_process->e //////////////////////////////

    nr_deinterleaving_ldpc(E,
                           nfapi_ulsch_pdu_rel15->Qm,
                           harq_process->e[r],
                           ulsch_llr+r_offset);

    //for (int i =0; i<16; i++)
    //          printf("rx output deinterleaving w[%d]= %d r_offset %d\n", i,harq_process->w[r][i], r_offset);

#if gNB_TIMING_TRACE
    stop_meas(ulsch_deinterleaving_stats);
#endif

#if gNB_TIMING_TRACE
    start_meas(ulsch_rate_unmatching_stats);
#endif

#ifdef DEBUG_ULSCH_DECODING
    LOG_D(PHY,"HARQ_PID %d Rate Matching Segment %d (coded bits %d,unpunctured/repeated bits %d, TBS %d, mod_order %d, nb_rb %d, Nl %d, rv %d, round %d)...\n",
          harq_pid,r, G,
          Kr*3,
          harq_process->TBS,
          nfapi_ulsch_pdu_rel15->Qm,
          nfapi_ulsch_pdu_rel15->number_rbs,
          nfapi_ulsch_pdu_rel15->n_layers,
          nfapi_ulsch_pdu_rel15->rv,
          harq_process->round);
#endif
    //////////////////////////////////////////////////////////////////////////////////////////


    //////////////////////////////////////////////////////////////////////////////////////////
    //////////////////////////////// nr_rate_matching_ldpc_rx ////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////////////////

    ///////////////////////// harq_process->e =====> harq_process->d /////////////////////////

    Tbslbrm = nr_compute_tbslbrm(0,nb_rb,nfapi_ulsch_pdu_rel15->n_layers,harq_process->C);

    if (nr_rate_matching_ldpc_rx(Ilbrm,
                                 Tbslbrm,
                                 p_decParams->BG,
                                 p_decParams->Z,
                                 harq_process->d[r],
                                 harq_process->e[r],
                                 harq_process->C,
                                 nfapi_ulsch_pdu_rel15->rv,
                                 (harq_process->round==0)?1:0,
                                 E)==-1) {
#if gNB_TIMING_TRACE
      stop_meas(ulsch_rate_unmatching_stats);
#endif
      LOG_E(PHY,"ulsch_decoding.c: Problem in rate_matching\n");
      return (ulsch->max_ldpc_iterations + 1);
    } else {
#if gNB_TIMING_TRACE
      stop_meas(ulsch_rate_unmatching_stats);
#endif
    }

    r_offset += E;

#ifdef DEBUG_ULSCH_DECODING
    if (r==0) {
      write_output("decoder_llr.m","decllr",ulsch_llr,G,1,0);
      write_output("decoder_in.m","dec",&harq_process->d[0][0],(3*8*Kr_bytes)+12,1,0);
    }

    printf("decoder input(segment %u) :",r);
    int i; 
    for (i=0;i<(3*8*Kr_bytes)+12;i++)
      printf("%d : %d\n",i,harq_process->d[r][i]);
    printf("\n");
#endif


    //    printf("Clearing c, %p\n",harq_process->c[r]);
    memset(harq_process->c[r],0,Kr_bytes);

    //    printf("done\n");
    if (harq_process->C == 1) {
      crc_type = CRC24_A;
      length_dec = harq_process->B;
    }
    else {
      crc_type = CRC24_B;
      length_dec = (harq_process->B+24*harq_process->C)/harq_process->C;
    }

    if (err_flag == 0) {

#if gNB_TIMING_TRACE
      start_meas(ulsch_turbo_decoding_stats);
#endif

      //LOG_E(PHY,"AbsSubframe %d.%d Start LDPC segment %d/%d A %d ",frame%1024,nr_tti_rx,r,harq_process->C-1, A);

      
      memset(pv,0,2*harq_process->Z*sizeof(int16_t));
      memset((pv+K_bytes_F),127,harq_process->F*sizeof(int16_t));

      for (i=((2*p_decParams->Z)>>3), j = 0; i < K_bytes_F; i++, j++) {
        pv[i]= _mm_loadu_si128((__m128i*)(&harq_process->d[r][8*j]));
      }

      AssertFatal(kc!=255,"");
      for (i=Kr_bytes,j=K_bytes_F-((2*p_decParams->Z)>>3); i < ((kc*p_decParams->Z)>>3); i++, j++) {
        pv[i]= _mm_loadu_si128((__m128i*)(&harq_process->d[r][8*j]));
      }
    
      for (i=0, j=0; j < ((kc*p_decParams->Z)>>4);  i+=2, j++) {
        pl[j] = _mm_packs_epi16(pv[i],pv[i+1]);
      }

      //////////////////////////////////////////////////////////////////////////////////////////


      //////////////////////////////////////////////////////////////////////////////////////////
      ///////////////////////////////////// nrLDPC_decoder /////////////////////////////////////
      //////////////////////////////////////////////////////////////////////////////////////////

      ////////////////////////////////// pl =====> llrProcBuf //////////////////////////////////

      no_iteration_ldpc = nrLDPC_decoder(p_decParams,
                                         (int8_t*)&pl[0],
                                         llrProcBuf,
                                         p_nrLDPC_procBuf[r],
                                         p_procTime);

      if (check_crc((uint8_t*)llrProcBuf,length_dec,harq_process->F,crc_type)) {
  #ifdef PRINT_CRC_CHECK
        //if (prnt_crc_cnt % 10 == 0)
          LOG_I(PHY, "Segment %d CRC OK\n",r);
  #endif
        ret = no_iteration_ldpc;
      } else {
  #ifdef PRINT_CRC_CHECK
        //if (prnt_crc_cnt%10 == 0)
          LOG_I(PHY, "CRC NOK\n");
  #endif
        ret = ulsch->max_ldpc_iterations + 1;
      }

      nb_total_decod++;

      if (no_iteration_ldpc > ulsch->max_ldpc_iterations){
        nb_error_decod++;
      }
      
      for (int m=0; m < Kr>>3; m ++) {
        harq_process->c[r][m]= (uint8_t) llrProcBuf[m];
      }

#ifdef DEBUG_ULSCH_DECODING
      //printf("output decoder %d %d %d %d %d \n", harq_process->c[r][0], harq_process->c[r][1], harq_process->c[r][2],harq_process->c[r][3], harq_process->c[r][4]);
      for (int k=0;k<A>>3;k++)
       printf("output decoder [%d] =  0x%02x \n", k, harq_process->c[r][k]);
      printf("no_iterations_ldpc %d (ret %u)\n",no_iteration_ldpc,ret);
      //write_output("dec_output.m","dec0",harq_process->c[0],Kr_bytes,1,4);
#endif

#if gNB_TIMING_TRACE
      stop_meas(ulsch_turbo_decoding_stats);
#endif
    }

    if ((err_flag == 0) && (ret >= (ulsch->max_ldpc_iterations + 1))) {
      // a Code segment is in error so break;
      LOG_D(PHY,"AbsSubframe %d.%d CRC failed, segment %d/%d \n",frame%1024,nr_tti_rx,r,harq_process->C-1);
      err_flag = 1;
    }
    //////////////////////////////////////////////////////////////////////////////////////////
  }

  int32_t frame_rx_prev = frame;
  int32_t tti_rx_prev = nr_tti_rx - 1;
  if (tti_rx_prev < 0) {
    frame_rx_prev--;
    tti_rx_prev += 10*frame_parms->ttis_per_subframe;
  }
  frame_rx_prev = frame_rx_prev%1024;

  if (err_flag == 1) {

#ifdef gNB_DEBUG_TRACE
    LOG_I(PHY,"[gNB %d] ULSCH: Setting NAK for SFN/SF %d/%d (pid %d, status %d, round %d, TBS %d) Kr %d r %d\n",
          phy_vars_gNB->Mod_id, frame, nr_tti_rx, harq_pid,harq_process->status, harq_process->round,harq_process->TBS,Kr,r);
#endif

    // harq_process->harq_ack.ack = 0;
    // harq_process->harq_ack.harq_id = harq_pid;
    // harq_process->harq_ack.send_harq_status = 1;
    // harq_process->errors[harq_process->round]++;
    //harq_process->round++;

    if (harq_process->round >= ulsch->Mlimit) {
      harq_process->status = SCH_IDLE;
      harq_process->round  = 0;
      harq_process->handled  = 0;
      ulsch->harq_mask &= ~(1 << harq_pid);
    }

    if(is_crnti) {
      LOG_D(PHY,"[gNB %d] ULSCH: Setting NACK for nr_tti_rx %d (pid %d, pid status %d, round %d/Max %d, TBS %d)\n",
            phy_vars_gNB->Mod_id,nr_tti_rx,harq_pid,harq_process->status,harq_process->round,ulsch->Mlimit,harq_process->TBS);
    }

    harq_process->handled  = 1;
    return (ulsch->max_ldpc_iterations + 1);

  } else {

#ifdef gNB_DEBUG_TRACE
    LOG_I(PHY,"[gNB %d] ULSCH: Setting ACK for nr_tti_rx %d TBS %d\n",
          phy_vars_gNB->Mod_id,nr_tti_rx,harq_process->TBS);
#endif

    harq_process->status = SCH_IDLE;
    harq_process->round  = 0;
    // harq_process->handled  = 0;
    ulsch->harq_mask  &= ~(1 << harq_pid);
    // harq_process->harq_ack.ack = 1;
    // harq_process->harq_ack.harq_id = harq_pid;
    // harq_process->harq_ack.send_harq_status = 1;

    if(is_crnti)
    {
      LOG_D(PHY,"[gNB %d] ULSCH: Setting ACK for nr_tti_rx %d (pid %d, round %d, TBS %d)\n",phy_vars_gNB->Mod_id,nr_tti_rx,harq_pid,harq_process->round,harq_process->TBS);
    }
  }

  // Reassembly of Transport block here
  offset = 0;
  Kr = harq_process->K;
  Kr_bytes = Kr>>3;

  for (r=0; r<harq_process->C; r++) {

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

#ifdef DEBUG_ULSCH_DECODING
    printf("Segment %u : Kr= %u bytes\n",r,Kr_bytes);
    printf("copied %d bytes to b sequence (harq_pid %d)\n",
           (Kr_bytes - (harq_process->F>>3)-((harq_process->C>1)?3:0)),harq_pid);
    printf("b[0] = %x,c[%d] = %x\n",
           harq_process->b[offset],
           harq_process->F>>3,
           harq_process->c[r]);
#endif

  }

  ulsch->last_iteration_cnt = ret;

  return(ret);
}
