diff --git a/ci-scripts/conf_files/gnb.band78.tm1.fr1.106PRB.usrpb210.conf b/ci-scripts/conf_files/gnb.band78.tm1.fr1.106PRB.usrpb210.conf
index 259a2466ad862387335d729315b5c5580011002e..258618e7f0644e9e8a983350b9d9b93099b8b8aa 100644
--- a/ci-scripts/conf_files/gnb.band78.tm1.fr1.106PRB.usrpb210.conf
+++ b/ci-scripts/conf_files/gnb.band78.tm1.fr1.106PRB.usrpb210.conf
@@ -1,7 +1,6 @@
 Active_gNBs = ( "gNB-Eurecom-5GNRBox");
 # Asn1_verbosity, choice in: none, info, annoying
 Asn1_verbosity = "none";
-Num_Threads_PUSCH = 8
 
 
 gNBs =
@@ -251,6 +250,7 @@ L1s = (
       {
   num_cc = 1;
   tr_n_preference = "local_mac";
+  pusch_proc_threads = 8;
         }  
 );
 
diff --git a/ci-scripts/xml_files/gnb_nr_ue_usrp_run.xml b/ci-scripts/xml_files/gnb_nr_ue_usrp_run.xml
index ed134f478c56bbe460b8e3fe8829954427fc3fd1..b1e9697be2f0e9cba104a7d15ceef8041ec4df8c 100644
--- a/ci-scripts/xml_files/gnb_nr_ue_usrp_run.xml
+++ b/ci-scripts/xml_files/gnb_nr_ue_usrp_run.xml
@@ -52,7 +52,7 @@
         <testCase id="090102">
                 <class>Initialize_OAI_UE</class>
                 <desc>Initialize NR UE USRP</desc>
-		<Initialize_OAI_UE_args>--phy-test --usrp-args "addr=192.168.30.2,second_addr=192.168.50.2,clock_source=external,time_source=external" --ue-rxgain 75  --rrc_config_path . --dlsch-parallel 4 </Initialize_OAI_UE_args>
+		<Initialize_OAI_UE_args>--phy-test --usrp-args "addr=192.168.30.2,second_addr=192.168.50.2,clock_source=external,time_source=external" --ue-rxgain 50  --rrc_config_path . --dlsch-parallel 4 </Initialize_OAI_UE_args>
 		<air_interface>NR</air_interface>
         </testCase>
 
diff --git a/doc/SW_archi.md b/doc/SW_archi.md
index 412dad402d90816ad53c5f48428824f6407052dd..d92f8021488fb30b6a2b3fb7d209fc733b92347d 100644
--- a/doc/SW_archi.md
+++ b/doc/SW_archi.md
@@ -186,7 +186,7 @@ development], for FR2 does not exist yet.
   3)  allocate a CCE for the UE (and return if it is not possible)
   4)  Calculate DMRS stuff (nr_save_pusch_fields()) and the TBS.
   5)  Mark used resources in vrb_map_UL.
-* loop through all users: get a free HARQ PID using select_ul_harq_pid() and
+* loop through all users: get a free HARQ PID and
   update statistics. Fill nFAPI structures directly for PUSCH, and call
   config_uldci() and fill_dci_pdu_rel15() for DCI filling and PDCCH messages.
 
diff --git a/executables/nr-gnb.c b/executables/nr-gnb.c
index 7966c5b6ce358f5ce06cd07216f1cebf48d06c08..e4b644e1a3677747a5b6b1c96e48897c6bedb19b 100644
--- a/executables/nr-gnb.c
+++ b/executables/nr-gnb.c
@@ -873,10 +873,7 @@ void init_gNB_proc(int inst) {
   gNB->threadPool = (tpool_t*)malloc(sizeof(tpool_t));
   gNB->respDecode = (notifiedFIFO_t*) malloc(sizeof(notifiedFIFO_t));
   int numCPU = sysconf(_SC_NPROCESSORS_ONLN);
-  uint32_t num_threads_pusch;
-  paramdef_t PUSCHThreads[] = NUM_THREADS_DESC;
-  config_get( PUSCHThreads,sizeof(PUSCHThreads)/sizeof(paramdef_t),NULL);
-  int threadCnt = min(numCPU, num_threads_pusch);
+  int threadCnt = min(numCPU, gNB->pusch_proc_threads);
   char ul_pool[80];
   sprintf(ul_pool,"-1");
   int s_offset = 0;
diff --git a/executables/nr-ue.c b/executables/nr-ue.c
index 6833f2fed3fc000eb06f9025bf36adce553b7e5e..6be23582b18983c76ef2bc954bb8dd6eb01e1000 100644
--- a/executables/nr-ue.c
+++ b/executables/nr-ue.c
@@ -642,8 +642,7 @@ void *UE_thread(void *arg) {
       UE->rx_offset_diff = computeSamplesShift(UE);
       readBlockSize=get_readBlockSize(slot_nr, &UE->frame_parms) -
                     UE->rx_offset_diff;
-      writeBlockSize=UE->frame_parms.get_samples_per_slot((slot_nr + DURATION_RX_TO_TX - RX_NB_TH) % nb_slot_frame, &UE->frame_parms)-
-                     UE->rx_offset_diff;
+      writeBlockSize=UE->frame_parms.get_samples_per_slot((slot_nr + DURATION_RX_TO_TX - RX_NB_TH) % nb_slot_frame, &UE->frame_parms)- UE->rx_offset_diff;
     }
 
     AssertFatal(readBlockSize ==
@@ -702,14 +701,28 @@ void *UE_thread(void *arg) {
       timing_advance = UE->timing_advance;
     }
 
-    AssertFatal( writeBlockSize ==
+    int flags = 0;
+    int slot_tx_usrp = slot_nr + DURATION_RX_TO_TX - RX_NB_TH;
+    uint8_t tdd_period = mac->phy_config.config_req.tdd_table.tdd_period_in_slots;
+    uint8_t num_UL_slots = mac->scc->tdd_UL_DL_ConfigurationCommon->pattern1.nrofUplinkSlots +
+                           (mac->scc->tdd_UL_DL_ConfigurationCommon->pattern1.nrofUplinkSymbols!=0);
+    uint8_t first_tx_slot = tdd_period - num_UL_slots;
+    if (slot_tx_usrp%tdd_period==first_tx_slot)
+      flags=2;
+    else     if (slot_tx_usrp%tdd_period==first_tx_slot+num_UL_slots-1)
+      flags = 3;
+    else     if (slot_tx_usrp%tdd_period>first_tx_slot)
+      flags = 1;
+
+    if (flags || IS_SOFTMODEM_RFSIM)
+      AssertFatal( writeBlockSize ==
                  UE->rfdevice.trx_write_func(&UE->rfdevice,
-                     writeTimestamp,
-                     txp,
-                     writeBlockSize,
-                     UE->frame_parms.nb_antennas_tx,
-                     1),"");
-
+					       writeTimestamp,
+					       txp,
+					       writeBlockSize,
+					       UE->frame_parms.nb_antennas_tx,
+					       flags),"");
+    
     for (int i=0; i<UE->frame_parms.nb_antennas_tx; i++)
       memset(txp[i], 0, writeBlockSize);
 
@@ -717,7 +730,7 @@ void *UE_thread(void *arg) {
     msgToPush->key=slot_nr;
     pushTpool(&(get_nrUE_params()->Tpool), msgToPush);
 
-    if ( IS_SOFTMODEM_RFSIM || IS_SOFTMODEM_NOS1) {  //getenv("RFSIMULATOR")
+    if (IS_SOFTMODEM_RFSIM) {  //getenv("RFSIMULATOR")
       // FixMe: Wait previous thread is done, because race conditions seems too bad
       // in case of actual RF board, the overlap between threads mitigate the issue
       // We must receive one message, that proves the slot processing is done
diff --git a/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h b/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h
index da8490160211f17ef2a94f976628c13e9ee7fcc2..9df9be4cbd4b656be44f4fb282a06cb8dff5148e 100644
--- a/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h
+++ b/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h
@@ -1019,6 +1019,7 @@ typedef struct
 typedef struct 
 {
   uint8_t tdd_period;//DL UL Transmission Periodicity. Value:0: ms0p5 1: ms0p625 2: ms1 3: ms1p25 4: ms2 5: ms2p5 6: ms5 7: ms10 8: ms3 9: ms4
+  uint8_t tdd_period_in_slots;
   fapi_nr_max_tdd_periodicity_t* max_tdd_periodicity_list;
 
 } fapi_nr_tdd_table_t;
diff --git a/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h b/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h
index c254e1a0282c0971038a83aeabc7c60f1f41a213..12748b7a9efc86f02dac31cae851e078bb99f91a 100644
--- a/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h
+++ b/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h
@@ -630,30 +630,30 @@ typedef struct {
 
 typedef struct {
   // The RNTI used for identifying the UE when receiving the PDU Value: 1 -> 65535.
-  uint16_t RNTI[MAX_DCI_CORESET];
+  uint16_t RNTI;
   // For a UE-specific search space it equals the higher-layer parameter PDCCH-DMRSScrambling-ID if configured,
   // otherwise it should be set to the phy cell ID. [TS38.211, sec 7.3.2.3] Value: 0->65535
-  uint16_t ScramblingId[MAX_DCI_CORESET];
+  uint16_t ScramblingId;
   // For a UE-specific search space where PDCCH-DMRSScrambling- ID is configured This param equals the CRNTI.
   // Otherwise, it should be set to 0. [TS38.211, sec 7.3.2.3] Value: 0 -> 65535 
-  uint16_t ScramblingRNTI[MAX_DCI_CORESET];
+  uint16_t ScramblingRNTI;
   // CCE start Index used to send the DCI Value: 0->135
-  uint8_t CceIndex[MAX_DCI_CORESET];
+  uint8_t CceIndex;
   // Aggregation level used [TS38.211, sec 7.3.2.1] Value: 1,2,4,8,16
-  uint8_t AggregationLevel[MAX_DCI_CORESET];
+  uint8_t AggregationLevel;
   // Precoding and Beamforming structure See Table 3-43
-  nfapi_nr_tx_precoding_and_beamforming_t precodingAndBeamforming[MAX_DCI_CORESET];
+  nfapi_nr_tx_precoding_and_beamforming_t precodingAndBeamforming;
   // PDCCH power value used for PDCCH Format 1_0 with CRC scrambled by SI-RNTI, PI-RNTI or RA-RNTI.
   // This is ratio of SSB/PBCH EPRE to PDCCH and PDCCH DMRS EPRE [TS38.213, sec 4.1]
   // Value :0->17 Report title: 5G FAPI: PHY API Specification Issue date: 29 June 2019 Version: 222.10.17 68 Field Type Description representing -8 to 8 dB in 1dB steps
-  uint8_t beta_PDCCH_1_0[MAX_DCI_CORESET];
+  uint8_t beta_PDCCH_1_0;
   // PDCCH power value used for all other PDCCH Formats.
   // This is ratio of SSB/PBCH block EPRE to PDCCH and PDCCH DMRS EPRE [TS38.214, sec 4.1] Values: 0: -3dB,1: 0dB,2: 3dB,3: 6dB
-  uint8_t powerControlOffsetSS[MAX_DCI_CORESET];
+  uint8_t powerControlOffsetSS;
   // The total DCI length (in bits) including padding bits [TS38.212 sec 7.3.1] Range 0->DCI_PAYLOAD_BYTE_LEN*8
-  uint16_t PayloadSizeBits[MAX_DCI_CORESET];
+  uint16_t PayloadSizeBits;
   // DCI payload, where the actual size is defined by PayloadSizeBits. The bit order is as following bit0-bit7 are mapped to first byte of MSB - LSB
-  uint8_t Payload[MAX_DCI_CORESET][DCI_PAYLOAD_BYTE_LEN]; 
+  uint8_t Payload[DCI_PAYLOAD_BYTE_LEN];
 
 } nfapi_nr_dl_dci_pdu_t;
 
@@ -698,7 +698,7 @@ typedef struct {
   ///Number of DCIs in this CORESET.Value: 0->MaxDciPerSlot
   uint16_t numDlDci;
   ///DL DCI PDU
-  nfapi_nr_dl_dci_pdu_t dci_pdu;
+  nfapi_nr_dl_dci_pdu_t dci_pdu[MAX_DCI_CORESET];
 }  nfapi_nr_dl_tti_pdcch_pdu_rel15_t;
 
 typedef struct {
diff --git a/openair1/PHY/INIT/nr_init.c b/openair1/PHY/INIT/nr_init.c
index 5621e2a7962ca95f32ff08c3981e06eec81ceadc..bd6b944fef0be35645e323502ca0f0984d5e8a6a 100644
--- a/openair1/PHY/INIT/nr_init.c
+++ b/openair1/PHY/INIT/nr_init.c
@@ -250,9 +250,7 @@ int phy_init_nr_gNB(PHY_VARS_gNB *gNB,
 
   int N_RB_UL = cfg->carrier_config.ul_grid_size[cfg->ssb_config.scs_common.value].value;
 
-  printf("Before ULSCH init : %p\n",gNB->dlsch[0][0]->harq_processes[0]);
   for (int ULSCH_id=0; ULSCH_id<NUMBER_OF_NR_ULSCH_MAX; ULSCH_id++) {
-    printf("ULSCH_id %d : %p\n",ULSCH_id,gNB->dlsch[0][0]->harq_processes[0]);
     pusch_vars[ULSCH_id] = (NR_gNB_PUSCH *)malloc16_clear( sizeof(NR_gNB_PUSCH) );
     pusch_vars[ULSCH_id]->rxdataF_ext           = (int32_t **)malloc16(Prx*sizeof(int32_t *) );
     pusch_vars[ULSCH_id]->rxdataF_ext2          = (int32_t **)malloc16(Prx*sizeof(int32_t *) );
@@ -269,7 +267,6 @@ int phy_init_nr_gNB(PHY_VARS_gNB *gNB,
     pusch_vars[ULSCH_id]->ul_ch_magb            = (int32_t **)malloc16(Prx*sizeof(int32_t *) );
     pusch_vars[ULSCH_id]->rho                   = (int32_t **)malloc16_clear(Prx*sizeof(int32_t*) );
 
-    printf("ULSCH_id %d (before rx antenna alloc) : %p\n",ULSCH_id,gNB->dlsch[0][0]->harq_processes[0]);
     for (i=0; i<Prx; i++) {
       pusch_vars[ULSCH_id]->rxdataF_ext[i]           = (int32_t *)malloc16_clear( sizeof(int32_t)*N_RB_UL*12*fp->symbols_per_slot );
       pusch_vars[ULSCH_id]->rxdataF_ext2[i]          = (int32_t *)malloc16_clear( sizeof(int32_t)*N_RB_UL*12*fp->symbols_per_slot );
@@ -286,16 +283,13 @@ int phy_init_nr_gNB(PHY_VARS_gNB *gNB,
       pusch_vars[ULSCH_id]->ul_ch_magb[i]            = (int32_t *)malloc16_clear( fp->symbols_per_slot*sizeof(int32_t)*N_RB_UL*12 );
       pusch_vars[ULSCH_id]->rho[i]                   = (int32_t *)malloc16_clear( sizeof(int32_t)*(fp->N_RB_UL*12*7*2) );
     }
-    printf("ULSCH_id %d (before llr alloc) : %p\n",ULSCH_id,gNB->dlsch[0][0]->harq_processes[0]);
     pusch_vars[ULSCH_id]->llr = (int16_t *)malloc16_clear( (8*((3*8*6144)+12))*sizeof(int16_t) ); // [hna] 6144 is LTE and (8*((3*8*6144)+12)) is not clear
-    printf("ULSCH_id %d (after llr alloc) : %p\n",ULSCH_id,gNB->dlsch[0][0]->harq_processes[0]);
     pusch_vars[ULSCH_id]->ul_valid_re_per_slot  = (int16_t *)malloc16_clear( sizeof(int16_t)*fp->symbols_per_slot);
   } //ulsch_id
 /*
   for (ulsch_id=0; ulsch_id<NUMBER_OF_UE_MAX; ulsch_id++)
     gNB->UE_stats_ptr[ulsch_id] = &gNB->UE_stats[ulsch_id];
 */
-  printf("After ULSCH init : %p\n",gNB->dlsch[0][0]->harq_processes[0]);
   return (0);
 }
 
diff --git a/openair1/PHY/NR_TRANSPORT/nr_dci.c b/openair1/PHY/NR_TRANSPORT/nr_dci.c
index 4db2648ebf30b58f5d2bdc022f72307f0bd98012..469415908f6d4b58b2eecf04d6c160a9953199ff 100644
--- a/openair1/PHY/NR_TRANSPORT/nr_dci.c
+++ b/openair1/PHY/NR_TRANSPORT/nr_dci.c
@@ -95,6 +95,7 @@ void nr_generate_dci(PHY_VARS_gNB *gNB,
      * in frequency: the first subcarrier is obtained by adding the first CRB overlapping the SSB and the rb_offset for coreset 0
      * or the rb_offset for other coresets
      * in time: by its first slot and its first symbol*/
+    const nfapi_nr_dl_dci_pdu_t *dci_pdu = &pdcch_pdu_rel15->dci_pdu[d];
 
     cset_start_symb = pdcch_pdu_rel15->StartSymbolIndex;
     cset_nsymb = pdcch_pdu_rel15->DurationSymbols;
@@ -103,7 +104,7 @@ void nr_generate_dci(PHY_VARS_gNB *gNB,
     LOG_D(PHY, "Coreset starting subcarrier %d on symbol %d (%d symbols)\n", cset_start_sc, cset_start_symb, cset_nsymb);
     // DMRS length is per OFDM symbol
     uint32_t dmrs_length = n_rb*6; //2(QPSK)*3(per RB)*6(REG per CCE)
-    uint32_t encoded_length = pdcch_pdu_rel15->dci_pdu.AggregationLevel[d]*108; //2(QPSK)*9(per RB)*6(REG per CCE)
+    uint32_t encoded_length = dci_pdu->AggregationLevel*108; //2(QPSK)*9(per RB)*6(REG per CCE)
     LOG_D(PHY, "DMRS length per symbol %d\t DCI encoded length %d (precoder_granularity %d,reg_mapping %d)\n", dmrs_length, encoded_length,pdcch_pdu_rel15->precoderGranularity,pdcch_pdu_rel15->CceRegMappingType);
     dmrs_length += rb_offset*6; // To accommodate more DMRS symbols in case of rb offset
       
@@ -125,19 +126,19 @@ void nr_generate_dci(PHY_VARS_gNB *gNB,
     // CRC attachment + Scrambling + Channel coding + Rate matching
     uint32_t encoder_output[NR_MAX_DCI_SIZE_DWORD];
 
-    uint16_t n_RNTI = pdcch_pdu_rel15->dci_pdu.RNTI[d];
-    uint16_t Nid    = pdcch_pdu_rel15->dci_pdu.ScramblingId[d];
-    uint16_t scrambling_RNTI = pdcch_pdu_rel15->dci_pdu.ScramblingRNTI[d];
+    uint16_t n_RNTI = dci_pdu->RNTI;
+    uint16_t Nid    = dci_pdu->ScramblingId;
+    uint16_t scrambling_RNTI = dci_pdu->ScramblingRNTI;
 
     t_nrPolar_params *currentPtr = nr_polar_params(NR_POLAR_DCI_MESSAGE_TYPE, 
-						   pdcch_pdu_rel15->dci_pdu.PayloadSizeBits[d], 
-						   pdcch_pdu_rel15->dci_pdu.AggregationLevel[d],
+						   dci_pdu->PayloadSizeBits,
+						   dci_pdu->AggregationLevel,
 						   0,NULL);
-    polar_encoder_fast((uint64_t*)pdcch_pdu_rel15->dci_pdu.Payload[d], (void*)encoder_output, n_RNTI,1,currentPtr);
+    polar_encoder_fast((uint64_t*)dci_pdu->Payload, (void*)encoder_output, n_RNTI,1,currentPtr);
 #ifdef DEBUG_CHANNEL_CODING
-    printf("polar rnti %x,length %d, L %d\n",n_RNTI, pdcch_pdu_rel15->dci_pdu.PayloadSizeBits[d],pdcch_pdu_rel15->dci_pdu.AggregationLevel[d]);
+    printf("polar rnti %x,length %d, L %d\n",n_RNTI, dci_pdu->PayloadSizeBits,pdcch_pdu_rel15->dci_pdu.AggregationLevel[d]);
     printf("DCI PDU: [0]->0x%lx \t [1]->0x%lx\n",
-	   ((uint64_t*)pdcch_pdu_rel15->dci_pdu.Payload[d])[0], ((uint64_t*)pdcch_pdu_rel15->dci_pdu.Payload[d])[1]);
+	   ((uint64_t*)dci_pdu->Payload)[0], ((uint64_t*)dci_pdu->Payload)[1]);
     printf("Encoded Payload (length:%d dwords):\n", encoded_length>>5);
     
     for (int i=0; i<encoded_length>>5; i++)
@@ -173,7 +174,7 @@ void nr_generate_dci(PHY_VARS_gNB *gNB,
     int reg_list_index = 0;
     int reg_list_order[NR_MAX_PDCCH_AGG_LEVEL] = {};
     for (int p = 0; p < NR_MAX_PDCCH_AGG_LEVEL; p++) {
-      for(int p2 = 0; p2 < pdcch_pdu_rel15->dci_pdu.AggregationLevel[d]; p2++) {
+      for(int p2 = 0; p2 < dci_pdu->AggregationLevel; p2++) {
         if(gNB->cce_list[d][p2].reg_list[0].reg_idx == p * NR_NB_REG_PER_CCE) {
           reg_list_order[reg_list_index] = p2;
           reg_list_index++;
@@ -183,7 +184,7 @@ void nr_generate_dci(PHY_VARS_gNB *gNB,
     }
 
     /*Mapping the encoded DCI along with the DMRS */
-    for (int cce_count = 0; cce_count < pdcch_pdu_rel15->dci_pdu.AggregationLevel[d]; cce_count ++) {
+    for (int cce_count = 0; cce_count < dci_pdu->AggregationLevel; cce_count ++) {
 
       int8_t cce_idx = reg_list_order[cce_count];
 
@@ -239,9 +240,10 @@ void nr_generate_dci(PHY_VARS_gNB *gNB,
       } // reg_in_cce_idx
     } // cce_count
 
-    LOG_D(PHY, "DCI: payloadSize = %d | payload = %llx\n",
-           *pdcch_pdu_rel15->dci_pdu.PayloadSizeBits,*(unsigned long long*)pdcch_pdu_rel15->dci_pdu.Payload);
-
+    LOG_D(PHY,
+          "DCI: payloadSize = %d | payload = %llx\n",
+          dci_pdu->PayloadSizeBits,
+          *(unsigned long long *)dci_pdu->Payload);
   } // for (int d=0;d<pdcch_pdu_rel15->numDlDci;d++)
 }
 
diff --git a/openair1/PHY/NR_TRANSPORT/nr_dci_tools.c b/openair1/PHY/NR_TRANSPORT/nr_dci_tools.c
index 2b64a3062cc6f449ad126c5a80cf91e7c2f3bbaf..3814c782952a7d6dbe8ac2007fb039b2d360fb61 100644
--- a/openair1/PHY/NR_TRANSPORT/nr_dci_tools.c
+++ b/openair1/PHY/NR_TRANSPORT/nr_dci_tools.c
@@ -142,7 +142,7 @@ void nr_fill_cce_list(PHY_VARS_gNB *gNB, uint8_t m,  nfapi_nr_dl_tti_pdcch_pdu_r
   AssertFatal(N_reg > 0,"N_reg cannot be 0\n");
 
   for (int d=0;d<pdcch_pdu_rel15->numDlDci;d++) {
-    int  L = pdcch_pdu_rel15->dci_pdu.AggregationLevel[d];
+    int  L = pdcch_pdu_rel15->dci_pdu[d].AggregationLevel;
 
     if (pdcch_pdu_rel15->CoreSetType == NFAPI_NR_CSET_CONFIG_MIB_SIB1)
       AssertFatal(L>=4, "Invalid aggregation level for SIB1 configured PDCCH %d\n", L);
@@ -153,10 +153,10 @@ void nr_fill_cce_list(PHY_VARS_gNB *gNB, uint8_t m,  nfapi_nr_dl_tti_pdcch_pdu_r
       C = N_reg/(bsize*R);
     }
     
-    LOG_D(PHY, "CCE list generation for candidate %d: bundle size %d ilv size %d CceIndex %d\n", m, bsize, R, pdcch_pdu_rel15->dci_pdu.CceIndex[d]);
+    LOG_D(PHY, "CCE list generation for candidate %d: bundle size %d ilv size %d CceIndex %d\n", m, bsize, R, pdcch_pdu_rel15->dci_pdu[d].CceIndex);
     for (uint8_t cce_idx=0; cce_idx<L; cce_idx++) {
       cce = &gNB->cce_list[d][cce_idx];
-      cce->cce_idx = pdcch_pdu_rel15->dci_pdu.CceIndex[d] + cce_idx;
+      cce->cce_idx = pdcch_pdu_rel15->dci_pdu[d].CceIndex + cce_idx;
       LOG_D(PHY, "cce_idx %d\n", cce->cce_idx);
       
       if (pdcch_pdu_rel15->CceRegMappingType == NFAPI_NR_CCE_REG_MAPPING_INTERLEAVED) {
@@ -236,25 +236,24 @@ void nr_fill_dci(PHY_VARS_gNB *gNB,
 
   for (int i=0;i<pdcch_pdu_rel15->numDlDci;i++) {
 
-    //uint64_t *dci_pdu = (uint64_t*)pdcch_pdu_rel15->dci_pdu.Payload[i];
+    //uint64_t *dci_pdu = (uint64_t*)pdcch_pdu_rel15->dci_pdu[i].Payload;
 
-    int dlsch_id = find_nr_dlsch(pdcch_pdu_rel15->dci_pdu.RNTI[i],gNB,SEARCH_EXIST_OR_FREE);
+    int dlsch_id = find_nr_dlsch(pdcch_pdu_rel15->dci_pdu[i].RNTI,gNB,SEARCH_EXIST_OR_FREE);
     if( (dlsch_id<0) || (dlsch_id>=NUMBER_OF_NR_DLSCH_MAX) ){
-      LOG_E(PHY,"illegal dlsch_id found!!! rnti %04x dlsch_id %d\n",(unsigned int)pdcch_pdu_rel15->dci_pdu.RNTI[i],dlsch_id);
+      LOG_E(PHY,"illegal dlsch_id found!!! rnti %04x dlsch_id %d\n",(unsigned int)pdcch_pdu_rel15->dci_pdu[i].RNTI,dlsch_id);
       return;
     }
     
     dlsch = gNB->dlsch[dlsch_id][0];
-    int num_slots_tdd = (gNB->frame_parms.slots_per_frame)>>(7-gNB->gNB_config.tdd_table.tdd_period.value);
-    int harq_pid = slot % num_slots_tdd;
+    int harq_pid = 0;
 
     dlsch->slot_tx[slot]             = 1;
-    dlsch->harq_ids[frame%2][slot]   = harq_pid;
+    dlsch->harq_ids[frame % 2][slot] = 0;
     AssertFatal(harq_pid < 8 && harq_pid >= 0,
 		"illegal harq_pid %d\n",harq_pid);
     
     dlsch->harq_mask                |= (1<<harq_pid);
-    dlsch->rnti                      = pdcch_pdu_rel15->dci_pdu.RNTI[i];
+    dlsch->rnti                      = pdcch_pdu_rel15->dci_pdu[i].RNTI;
     
     //    nr_fill_cce_list(gNB,0);  
     /*
@@ -300,7 +299,7 @@ void nr_fill_ul_dci(PHY_VARS_gNB *gNB,
 
   for (int i=0;i<pdcch_pdu_rel15->numDlDci;i++) {
 
-    //uint64_t *dci_pdu = (uint64_t*)pdcch_pdu_rel15->dci_pdu.Payload[i];
+    //uint64_t *dci_pdu = (uint64_t*)pdcch_pdu_rel15->dci_pdu[i].Payload;
 
     // if there's no DL DCI then generate CCE list
     //    nr_fill_cce_list(gNB,0);  
diff --git a/openair1/PHY/NR_TRANSPORT/nr_dlsch.c b/openair1/PHY/NR_TRANSPORT/nr_dlsch.c
index e3627b93445533a0d342ea4a6918b83d7995e52b..1cce5e117465f125538e57f48f1cd2d6f3fdf8a3 100644
--- a/openair1/PHY/NR_TRANSPORT/nr_dlsch.c
+++ b/openair1/PHY/NR_TRANSPORT/nr_dlsch.c
@@ -136,8 +136,7 @@ uint8_t nr_generate_pdsch(PHY_VARS_gNB *gNB,
     dlsch = gNB->dlsch[dlsch_id][0];
     if (dlsch->slot_tx[slot] == 0) continue;
 
-    int harq_pid = dlsch->harq_ids[frame%2][slot];
-    NR_DL_gNB_HARQ_t *harq = dlsch->harq_processes[harq_pid];
+    NR_DL_gNB_HARQ_t *harq = &dlsch->harq_process;
     nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &harq->pdsch_pdu.pdsch_pdu_rel15;
     uint32_t scrambled_output[NR_MAX_NB_CODEWORDS][NR_MAX_PDSCH_ENCODED_LENGTH>>5];
     int16_t **mod_symbs = (int16_t**)dlsch->mod_symbs;
@@ -421,15 +420,8 @@ void dump_pdsch_stats(PHY_VARS_gNB *gNB) {
 
   for (int i=0;i<NUMBER_OF_NR_SCH_STATS_MAX;i++)
     if (gNB->dlsch_stats[i].rnti > 0)
-      LOG_I(PHY,"DLSCH RNTI %x: round_trials %d(%1.1e):%d(%1.1e):%d(%1.1e):%d, current_Qm %d, current_RI %d, total_bytes TX %d\n",
+      LOG_D(PHY,"DLSCH RNTI %x: current_Qm %d, current_RI %d, total_bytes TX %d\n",
 	    gNB->dlsch_stats[i].rnti,
-	    gNB->dlsch_stats[i].round_trials[0],
-	    (double)gNB->dlsch_stats[i].round_trials[1]/gNB->dlsch_stats[i].round_trials[0],
-	    gNB->dlsch_stats[i].round_trials[1],
-	    (double)gNB->dlsch_stats[i].round_trials[2]/gNB->dlsch_stats[i].round_trials[0],
-	    gNB->dlsch_stats[i].round_trials[2],
-	    (double)gNB->dlsch_stats[i].round_trials[3]/gNB->dlsch_stats[i].round_trials[0],
-	    gNB->dlsch_stats[i].round_trials[3],
 	    gNB->dlsch_stats[i].current_Qm,
 	    gNB->dlsch_stats[i].current_RI,
 	    gNB->dlsch_stats[i].total_bytes_tx);
diff --git a/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c b/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c
index cb395ff4aff5850306c85a728bef4aaeb9e1c405..e6544bbe0c2a0115028d2fe1a9011cafd328dd74 100644
--- a/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c
+++ b/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c
@@ -51,7 +51,6 @@
 
 void free_gNB_dlsch(NR_gNB_DLSCH_t **dlschptr, uint16_t N_RB)
 {
-  int i;
   int r;
 
   NR_gNB_DLSCH_t *dlsch = *dlschptr;
@@ -63,76 +62,61 @@ void free_gNB_dlsch(NR_gNB_DLSCH_t **dlschptr, uint16_t N_RB)
       a_segments = a_segments*N_RB;
       a_segments = a_segments/273 +1;
     }  
-
-
-
+    
+    
+    
 #ifdef DEBUG_DLSCH_FREE
     LOG_D(PHY,"Freeing dlsch %p\n",dlsch);
 #endif
-
-    for (i=0; i<dlsch->Mdlharq; i++) {
-#ifdef DEBUG_DLSCH_FREE
-      LOG_D(PHY,"Freeing dlsch process %d\n",i);
-#endif
-
-      if (dlsch->harq_processes[i]) {
-#ifdef DEBUG_DLSCH_FREE
-        LOG_D(PHY,"Freeing dlsch process %d (%p)\n",i,dlsch->harq_processes[i]);
-#endif
-
-        if (dlsch->harq_processes[i]->b) {
-          free16(dlsch->harq_processes[i]->b,a_segments*1056);
-          dlsch->harq_processes[i]->b = NULL;
+    NR_DL_gNB_HARQ_t *harq = &dlsch->harq_process;
+    if (harq->b) {
+      free16(harq->b, a_segments * 1056);
+      harq->b = NULL;
 #ifdef DEBUG_DLSCH_FREE
-          LOG_D(PHY,"Freeing dlsch process %d b (%p)\n",i,dlsch->harq_processes[i]->b);
+      LOG_D(PHY, "Freeing harq->b (%p)\n", harq->b);
 #endif
-        }
 
-        if (dlsch->harq_processes[i]->e) {
-          free16(dlsch->harq_processes[i]->e,14*N_RB*12*8);
-          dlsch->harq_processes[i]->e = NULL;
+      if (harq->e) {
+        free16(harq->e, 14 * N_RB * 12 * 8);
+        harq->e = NULL;
 #ifdef DEBUG_DLSCH_FREE
-          printf("Freeing dlsch process %d e (%p)\n",i,dlsch->harq_processes[i]->e);
+        printf("Freeing dlsch process %d e (%p)\n", i, harq->e);
 #endif
-        }
+      }
 
-        if (dlsch->harq_processes[i]->f) {
-          free16(dlsch->harq_processes[i]->f,14*N_RB*12*8);
-          dlsch->harq_processes[i]->f = NULL;
+      if (harq->f) {
+        free16(harq->f, 14 * N_RB * 12 * 8);
+        harq->f = NULL;
 #ifdef DEBUG_DLSCH_FREE
-          printf("Freeing dlsch process %d f (%p)\n",i,dlsch->harq_processes[i]->f);
+        printf("Freeing dlsch process %d f (%p)\n", i, harq->f);
 #endif
-        }
+      }
 
 #ifdef DEBUG_DLSCH_FREE
-        LOG_D(PHY,"Freeing dlsch process %d c (%p)\n",i,dlsch->harq_processes[i]->c);
+      LOG_D(PHY, "Freeing dlsch process %d c (%p)\n", i, harq->c);
 #endif
 
-        for (r=0; r<a_segments; r++) {
-
+      for (r = 0; r < a_segments; r++) {
 #ifdef DEBUG_DLSCH_FREE
-          LOG_D(PHY,"Freeing dlsch process %d c[%d] (%p)\n",i,r,dlsch->harq_processes[i]->c[r]);
+        LOG_D(PHY, "Freeing dlsch process %d c[%d] (%p)\n", i, r, harq->c[r]);
 #endif
 
-          if (dlsch->harq_processes[i]->c[r]) {
-            free16(dlsch->harq_processes[i]->c[r],1056);
-            dlsch->harq_processes[i]->c[r] = NULL;
-          }
-          if (dlsch->harq_processes[i]->d[r]) {
-            free16(dlsch->harq_processes[i]->d[r],3*8448);
-            dlsch->harq_processes[i]->d[r] = NULL;
-          }
-
-	    }
-	free16(dlsch->harq_processes[i],sizeof(NR_DL_gNB_HARQ_t));
-	dlsch->harq_processes[i] = NULL;
+        if (harq->c[r]) {
+          free16(harq->c[r], 1056);
+          harq->c[r] = NULL;
+        }
+        if (harq->d[r]) {
+          free16(harq->d[r], 3 * 8448);
+          harq->d[r] = NULL;
+        }
       }
+      free16(harq, sizeof(NR_DL_gNB_HARQ_t));
+      harq = NULL;
     }
-
-    free16(dlsch,sizeof(NR_gNB_DLSCH_t));
-    *dlschptr = NULL;
   }
 
+  free16(dlsch, sizeof(NR_gNB_DLSCH_t));
+  *dlschptr = NULL;
 }
 
 NR_gNB_DLSCH_t *new_gNB_dlsch(NR_DL_FRAME_PARMS *frame_parms,
@@ -142,9 +126,7 @@ NR_gNB_DLSCH_t *new_gNB_dlsch(NR_DL_FRAME_PARMS *frame_parms,
                               uint8_t  abstraction_flag,
                               uint16_t N_RB)
 {
-
-  NR_gNB_DLSCH_t *dlsch;
-  unsigned char exit_flag = 0,i,r,aa,layer;
+  unsigned char i,r,aa,layer;
   int re;
   uint16_t a_segments = MAX_NUM_NR_DLSCH_SEGMENTS;  //number of segments to be allocated
 
@@ -155,127 +137,74 @@ NR_gNB_DLSCH_t *new_gNB_dlsch(NR_DL_FRAME_PARMS *frame_parms,
 
   uint16_t dlsch_bytes = a_segments*1056;  // allocated bytes per segment
 
-  
-  dlsch = (NR_gNB_DLSCH_t *)malloc16(sizeof(NR_gNB_DLSCH_t));
+  NR_gNB_DLSCH_t *dlsch = malloc16(sizeof(NR_gNB_DLSCH_t));
+  AssertFatal(dlsch, "cannot allocate dlsch\n");
 
-  if (dlsch) {
-    bzero(dlsch,sizeof(NR_gNB_DLSCH_t));
-    dlsch->Kmimo = Kmimo;
-    dlsch->Mdlharq = Mdlharq;
-    dlsch->Mlimit = 4;
-    dlsch->Nsoft = Nsoft;
-
-    for (layer=0; layer<NR_MAX_NB_LAYERS; layer++) {
-      dlsch->ue_spec_bf_weights[layer] = (int32_t**)malloc16(64*sizeof(int32_t*));
-
-      for (aa=0; aa<64; aa++) {
-         dlsch->ue_spec_bf_weights[layer][aa] = (int32_t *)malloc16(OFDM_SYMBOL_SIZE_COMPLEX_SAMPLES*sizeof(int32_t));
-         for (re=0;re<OFDM_SYMBOL_SIZE_COMPLEX_SAMPLES; re++) {
-           dlsch->ue_spec_bf_weights[layer][aa][re] = 0x00007fff;
-         }
-      }
-
-      dlsch->txdataF[layer] = (int32_t *)malloc16((NR_MAX_PDSCH_ENCODED_LENGTH/NR_MAX_NB_LAYERS)*sizeof(int32_t)); // NR_MAX_NB_LAYERS is already included in NR_MAX_PDSCH_ENCODED_LENGTH
-    }
-
-    for (int q=0; q<NR_MAX_NB_CODEWORDS; q++)
-      dlsch->mod_symbs[q] = (int32_t *)malloc16(NR_MAX_PDSCH_ENCODED_LENGTH*sizeof(int32_t));
-
-     dlsch->calib_dl_ch_estimates = (int32_t**)malloc16(64*sizeof(int32_t*));
-     for (aa=0; aa<64; aa++) {
-       dlsch->calib_dl_ch_estimates[aa] = (int32_t *)malloc16(OFDM_SYMBOL_SIZE_COMPLEX_SAMPLES*sizeof(int32_t));
-
-     }
-
-    for (i=0; i<20; i++) {
-      dlsch->harq_ids[0][i] = 0;
-      dlsch->harq_ids[1][i] = 0;
-    }
-
-    for (i=0; i<Mdlharq; i++) {
-      dlsch->harq_processes[i] = (NR_DL_gNB_HARQ_t *)malloc16(sizeof(NR_DL_gNB_HARQ_t));
-      LOG_T(PHY, "Required mem size %d  dlsch->harq_processes[%d] %p\n",
-    		  dlsch_bytes, i,dlsch->harq_processes[i]);
-
-      if (dlsch->harq_processes[i]) {
-        bzero(dlsch->harq_processes[i],sizeof(NR_DL_gNB_HARQ_t));
-        //    dlsch->harq_processes[i]->first_tx=1;
-        dlsch->harq_processes[i]->b = (unsigned char*)malloc16(dlsch_bytes);
-        dlsch->harq_processes[i]->pdu = (uint8_t*)malloc16(dlsch_bytes);
-        if (dlsch->harq_processes[i]->pdu) {
-          bzero(dlsch->harq_processes[i]->pdu,dlsch_bytes);
-          nr_emulate_dlsch_payload(dlsch->harq_processes[i]->pdu, (dlsch_bytes)>>3);
-        } else {
-          LOG_D(PHY,"Can't allocate PDU\n");
-          exit_flag=1;
-        }
-
-        if (dlsch->harq_processes[i]->b) {
-          bzero(dlsch->harq_processes[i]->b,dlsch_bytes);
-        } else {
-          LOG_D(PHY,"Can't get b\n");
-          exit_flag=1;
-        }
+  bzero(dlsch,sizeof(NR_gNB_DLSCH_t));
+  dlsch->Kmimo = Kmimo;
+  dlsch->Mdlharq = Mdlharq;
+  dlsch->Mlimit = 4;
+  dlsch->Nsoft = Nsoft;
+  
+  for (layer=0; layer<NR_MAX_NB_LAYERS; layer++) {
+    dlsch->ue_spec_bf_weights[layer] = (int32_t **)malloc16(64 * sizeof(int32_t *));
 
-        if (abstraction_flag==0) {
-          for (r=0; r<a_segments; r++) {
-            // account for filler in first segment and CRCs for multiple segment case
-            // [hna] 8448 is the maximum CB size in NR
-            //       68*348 = 68*(maximum size of Zc)
-            //       In section 5.3.2 in 38.212, the for loop is up to N + 2*Zc (maximum size of N is 66*Zc, therefore 68*Zc)
-            dlsch->harq_processes[i]->c[r] = (uint8_t*)malloc16(8448);
-            dlsch->harq_processes[i]->d[r] = (uint8_t*)malloc16(68*384); //max size for coded output
-            if (dlsch->harq_processes[i]->c[r]) {
-              bzero(dlsch->harq_processes[i]->c[r],8448);
-            } else {
-              LOG_D(PHY,"Can't get c\n");
-              exit_flag=2;
-            }
-            if (dlsch->harq_processes[i]->d[r]) {
-              bzero(dlsch->harq_processes[i]->d[r],(3*8448));
-            } else {
-              LOG_D(PHY,"Can't get d\n");
-              exit_flag=2;
-            }
-          }
-          dlsch->harq_processes[i]->e = (uint8_t*)malloc16(14*N_RB*12*8);
-          if (dlsch->harq_processes[i]->e) {
-            bzero(dlsch->harq_processes[i]->e,14*N_RB*12*8);
-          } else {
-            printf("Can't get e\n");
-            exit_flag=1;
-          }
-          dlsch->harq_processes[i]->f = (uint8_t*)malloc16(14*N_RB*12*8);
-          if (dlsch->harq_processes[i]->f) {
-            bzero(dlsch->harq_processes[i]->f,14*N_RB*12*8);
-          } else {
-            printf("Can't get f\n");
-            exit_flag=1;
-          }
-        }
-      } else {
-        LOG_D(PHY,"Can't get harq_p %d\n",i);
-        exit_flag=3;
+    for (aa = 0; aa < 64; aa++) {
+      dlsch->ue_spec_bf_weights[layer][aa] = (int32_t *)malloc16(OFDM_SYMBOL_SIZE_COMPLEX_SAMPLES * sizeof(int32_t));
+      for (re = 0; re < OFDM_SYMBOL_SIZE_COMPLEX_SAMPLES; re++) {
+        dlsch->ue_spec_bf_weights[layer][aa][re] = 0x00007fff;
       }
     }
 
-    if (exit_flag==0) {
-      for (i=0; i<Mdlharq; i++) {
-        dlsch->harq_processes[i]->round=0;
-      }
-
-      return(dlsch);
-    }
+    dlsch->txdataF[layer] =
+        (int32_t *)malloc16((NR_MAX_PDSCH_ENCODED_LENGTH / NR_MAX_NB_LAYERS)
+                            * sizeof(int32_t)); // NR_MAX_NB_LAYERS is already included in NR_MAX_PDSCH_ENCODED_LENGTH
   }
 
-  LOG_D(PHY,"new_gNB_dlsch exit flag %d, size of  %ld\n",
-	exit_flag, sizeof(NR_gNB_DLSCH_t));
+  for (int q = 0; q < NR_MAX_NB_CODEWORDS; q++)
+    dlsch->mod_symbs[q] = (int32_t *)malloc16(NR_MAX_PDSCH_ENCODED_LENGTH * sizeof(int32_t));
 
-  free_gNB_dlsch(&dlsch,N_RB);
+  dlsch->calib_dl_ch_estimates = (int32_t **)malloc16(64 * sizeof(int32_t *));
+  for (aa = 0; aa < 64; aa++) {
+    dlsch->calib_dl_ch_estimates[aa] = (int32_t *)malloc16(OFDM_SYMBOL_SIZE_COMPLEX_SAMPLES * sizeof(int32_t));
+  }
 
-  return(NULL);
+  for (i = 0; i < 20; i++) {
+    dlsch->harq_ids[0][i] = 0;
+    dlsch->harq_ids[1][i] = 0;
+  }
 
+  NR_DL_gNB_HARQ_t *harq = &dlsch->harq_process;
+  bzero(harq, sizeof(NR_DL_gNB_HARQ_t));
+
+  harq->b = malloc16(dlsch_bytes);
+  AssertFatal(harq->b, "cannot allocate memory for harq->b\n");
+  harq->pdu = malloc16(dlsch_bytes);
+  AssertFatal(harq->pdu, "cannot allocate memory for harq->pdu\n");
+  bzero(harq->pdu, dlsch_bytes);
+  nr_emulate_dlsch_payload(harq->pdu, (dlsch_bytes) >> 3);
+  bzero(harq->b, dlsch_bytes);
+
+  for (r = 0; r < a_segments; r++) {
+    // account for filler in first segment and CRCs for multiple segment case
+    // [hna] 8448 is the maximum CB size in NR
+    //       68*348 = 68*(maximum size of Zc)
+    //       In section 5.3.2 in 38.212, the for loop is up to N + 2*Zc (maximum size of N is 66*Zc, therefore 68*Zc)
+    harq->c[r] = malloc16(8448);
+    AssertFatal(harq->c[r], "cannot allocate harq->c[%d]\n", r);
+    harq->d[r] = malloc16(68 * 384);
+    AssertFatal(harq->d[r], "cannot allocate harq->d[%d]\n", r); // max size for coded output
+    bzero(harq->c[r], 8448);
+    bzero(harq->d[r], (3 * 8448));
+    harq->e = malloc16(14 * N_RB * 12 * 8);
+    AssertFatal(harq->e, "cannot allocate harq->e\n");
+    bzero(harq->e, 14 * N_RB * 12 * 8);
+    harq->f = malloc16(14 * N_RB * 12 * 8);
+    AssertFatal(harq->f, "cannot allocate harq->f\n");
+    bzero(harq->f, 14 * N_RB * 12 * 8);
+  }
 
+  return(dlsch);
 }
 
 void clean_gNB_dlsch(NR_gNB_DLSCH_t *dlsch)
@@ -284,28 +213,21 @@ void clean_gNB_dlsch(NR_gNB_DLSCH_t *dlsch)
   unsigned char Mdlharq;
   unsigned char i,j,r;
 
-  if (dlsch) {
-    Mdlharq = dlsch->Mdlharq;
-    dlsch->rnti = 0;
-    dlsch->active = 0;
-
-    for (i=0; i<10; i++) {
-      dlsch->harq_ids[0][i] = Mdlharq;
-      dlsch->harq_ids[1][i] = Mdlharq;
-    }
-    for (i=0; i<Mdlharq; i++) {
-      if (dlsch->harq_processes[i]) {
-        //  dlsch->harq_processes[i]->Ndi    = 0;
-        //dlsch->harq_processes[i]->status = 0;
-        dlsch->harq_processes[i]->round  = 0;
-
-	for (j=0; j<96; j++)
-	  for (r=0; r<MAX_NUM_NR_DLSCH_SEGMENTS; r++)
-	    if (dlsch->harq_processes[i]->d[r])
-	      dlsch->harq_processes[i]->d[r][j] = NR_NULL;
+  AssertFatal(dlsch!=NULL,"dlsch is null\n");
+  Mdlharq = dlsch->Mdlharq;
+  dlsch->rnti = 0;
+  dlsch->active = 0;
+  NR_DL_gNB_HARQ_t *harq=&dlsch->harq_process;
 
-      }
-    }
+  for (i=0; i<10; i++) {
+    dlsch->harq_ids[0][i] = Mdlharq;
+    dlsch->harq_ids[1][i] = Mdlharq;
+  }
+  for (i=0; i<Mdlharq; i++) {
+    for (j=0; j<96; j++)
+      for (r=0; r<MAX_NUM_NR_DLSCH_SEGMENTS; r++)
+        if (harq->d[r])
+          harq->d[r][j] = NR_NULL;
   }
 }
 
@@ -322,13 +244,12 @@ int nr_dlsch_encoding(PHY_VARS_gNB *gNB,
 
   unsigned int G;
   unsigned int crc=1;
-  uint8_t harq_pid = dlsch->harq_ids[frame%2][slot];
-  AssertFatal(harq_pid<8 && harq_pid>=0,"illegal harq_pid %d\b",harq_pid);
-  nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &dlsch->harq_processes[harq_pid]->pdsch_pdu.pdsch_pdu_rel15;
+  NR_DL_gNB_HARQ_t *harq = &dlsch->harq_process;
+  nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &harq->pdsch_pdu.pdsch_pdu_rel15;
   uint16_t nb_rb = rel15->rbSize;
   uint8_t nb_symb_sch = rel15->NrOfSymbols;
   uint32_t A, Kb, F=0;
-  uint32_t *Zc = &dlsch->harq_processes[harq_pid]->Z;
+  uint32_t *Zc = &dlsch->harq_process.Z;
   uint8_t mod_order = rel15->qamModOrder[0];
   uint16_t Kr=0,r;
   uint32_t r_offset=0;
@@ -347,8 +268,7 @@ int nr_dlsch_encoding(PHY_VARS_gNB *gNB,
   float Coderate = 0.0;
   uint8_t Nl = 4;
 
-  dlsch->harq_processes[harq_pid]->round = nr_rv_round_map[rel15->rvIndex[0]];
-
+  
   VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_gNB_DLSCH_ENCODING, VCD_FUNCTION_IN);
 
   A = rel15->TBSize[0]<<3;
@@ -367,174 +287,157 @@ int nr_dlsch_encoding(PHY_VARS_gNB *gNB,
   }
 
   if (stats) {
-    stats->round_trials[dlsch->harq_processes[harq_pid]->round]++;
     stats->rnti = dlsch->rnti;
-    if (dlsch->harq_processes[harq_pid]->round == 0){
-      stats->total_bytes_tx += rel15->TBSize[0];
-      stats->current_RI   = rel15->nrOfLayers;
-      stats->current_Qm   = rel15->qamModOrder[0];
-    }
+    stats->total_bytes_tx += rel15->TBSize[0];
+    stats->current_RI   = rel15->nrOfLayers;
+    stats->current_Qm   = rel15->qamModOrder[0];
   }
   G = nr_get_G(nb_rb, nb_symb_sch, nb_re_dmrs, length_dmrs,mod_order,rel15->nrOfLayers);
 
   LOG_D(PHY,"dlsch coding A %d G %d mod_order %d\n", A,G, mod_order);
 
-  //  if (dlsch->harq_processes[harq_pid]->Ndi == 1) {  // this is a new packet
-  if (dlsch->harq_processes[harq_pid]->round == 0) {  // this is a new packet
-#ifdef DEBUG_DLSCH_CODING
-  LOG_D(PHY,"encoding thinks this is a new packet \n");
-#endif
-  /*    
-    int i;
-    LOG_D(PHY,"dlsch (tx): \n");
-    for (i=0;i<(A>>3);i++)
-      LOG_D(PHY,"%02x\n",a[i]);
-    LOG_D(PHY,"\n");
-  */
-
-    if (A > 3824) {
-      // Add 24-bit crc (polynomial A) to payload
-      crc = crc24a(a,A)>>8;
-      a[A>>3] = ((uint8_t*)&crc)[2];
-      a[1+(A>>3)] = ((uint8_t*)&crc)[1];
-      a[2+(A>>3)] = ((uint8_t*)&crc)[0];
-      //printf("CRC %x (A %d)\n",crc,A);
-      //printf("a0 %d a1 %d a2 %d\n", a[A>>3], a[1+(A>>3)], a[2+(A>>3)]);
+  if (A > 3824) {
+    // Add 24-bit crc (polynomial A) to payload
+    crc = crc24a(a,A)>>8;
+    a[A>>3] = ((uint8_t*)&crc)[2];
+    a[1+(A>>3)] = ((uint8_t*)&crc)[1];
+    a[2+(A>>3)] = ((uint8_t*)&crc)[0];
+    //printf("CRC %x (A %d)\n",crc,A);
+    //printf("a0 %d a1 %d a2 %d\n", a[A>>3], a[1+(A>>3)], a[2+(A>>3)]);
+    
+    harq->B = A+24;
+    //    harq->b = a;
+
+    AssertFatal((A / 8) + 4 <= MAX_NR_DLSCH_PAYLOAD_BYTES,
+                "A %d is too big (A/8+4 = %d > %d)\n",
+                A,
+                (A / 8) + 4,
+                MAX_NR_DLSCH_PAYLOAD_BYTES);
+
+    memcpy(harq->b, a, (A / 8) + 4); // why is this +4 if the CRC is only 3 bytes?
+  }
+  else {
+    // Add 16-bit crc (polynomial A) to payload
+    crc = crc16(a,A)>>16;
+    a[A>>3] = ((uint8_t*)&crc)[1];
+    a[1+(A>>3)] = ((uint8_t*)&crc)[0];
+    //printf("CRC %x (A %d)\n",crc,A);
+    //printf("a0 %d a1 %d \n", a[A>>3], a[1+(A>>3)]);
+    
+    harq->B = A+16;
+    //    harq->b = a;
+
+    AssertFatal((A / 8) + 3 <= MAX_NR_DLSCH_PAYLOAD_BYTES,
+                "A %d is too big (A/8+3 = %d > %d)\n",
+                A,
+                (A / 8) + 3,
+                MAX_NR_DLSCH_PAYLOAD_BYTES);
+
+    memcpy(harq->b, a, (A / 8) + 3); // using 3 bytes to mimic the case of 24 bit crc
+  }
+  if (R<1000)
+    Coderate = (float) R /(float) 1024;
+  else  // to scale for mcs 20 and 26 in table 5.1.3.1-2 which are decimal and input 2* in nr_tbs_tools
+    Coderate = (float) R /(float) 2048;
   
-      dlsch->harq_processes[harq_pid]->B = A+24;
-      //    dlsch->harq_processes[harq_pid]->b = a;
-   
-      AssertFatal((A/8)+4 <= MAX_NR_DLSCH_PAYLOAD_BYTES,"A %d is too big (A/8+4 = %d > %d)\n",A,(A/8)+4,MAX_NR_DLSCH_PAYLOAD_BYTES);
-
-      memcpy(dlsch->harq_processes[harq_pid]->b,a,(A/8)+4);  // why is this +4 if the CRC is only 3 bytes?
-    }
-    else {
-      // Add 16-bit crc (polynomial A) to payload
-      crc = crc16(a,A)>>16;
-      a[A>>3] = ((uint8_t*)&crc)[1];
-      a[1+(A>>3)] = ((uint8_t*)&crc)[0];
-      //printf("CRC %x (A %d)\n",crc,A);
-      //printf("a0 %d a1 %d \n", a[A>>3], a[1+(A>>3)]);
+  if ((A <=292) || ((A<=3824) && (Coderate <= 0.6667)) || Coderate <= 0.25)
+    harq->BG = 2;
+  else
+    harq->BG = 1;
   
-      dlsch->harq_processes[harq_pid]->B = A+16;
-      //    dlsch->harq_processes[harq_pid]->b = a;
-   
-      AssertFatal((A/8)+3 <= MAX_NR_DLSCH_PAYLOAD_BYTES,"A %d is too big (A/8+3 = %d > %d)\n",A,(A/8)+3,MAX_NR_DLSCH_PAYLOAD_BYTES);
-
-      memcpy(dlsch->harq_processes[harq_pid]->b,a,(A/8)+3);  // using 3 bytes to mimic the case of 24 bit crc
-    }
-    if (R<1000)
-      Coderate = (float) R /(float) 1024;
-    else  // to scale for mcs 20 and 26 in table 5.1.3.1-2 which are decimal and input 2* in nr_tbs_tools
-      Coderate = (float) R /(float) 2048;
-
-    if ((A <=292) || ((A<=3824) && (Coderate <= 0.6667)) || Coderate <= 0.25)
-		dlsch->harq_processes[harq_pid]->BG = 2;
-    else
-		dlsch->harq_processes[harq_pid]->BG = 1;
-
-    start_meas(dlsch_segmentation_stats);
-    Kb = nr_segmentation(dlsch->harq_processes[harq_pid]->b,
-		         dlsch->harq_processes[harq_pid]->c,
-		         dlsch->harq_processes[harq_pid]->B,
-		         &dlsch->harq_processes[harq_pid]->C,
-		         &dlsch->harq_processes[harq_pid]->K,
-		         Zc, 
-		         &dlsch->harq_processes[harq_pid]->F,
-             dlsch->harq_processes[harq_pid]->BG);
-    stop_meas(dlsch_segmentation_stats);
-    F = dlsch->harq_processes[harq_pid]->F;
-
-    Kr = dlsch->harq_processes[harq_pid]->K;
+  start_meas(dlsch_segmentation_stats);
+  Kb = nr_segmentation(harq->b, harq->c, harq->B, &harq->C, &harq->K, Zc, &harq->F, harq->BG);
+  stop_meas(dlsch_segmentation_stats);
+  F = harq->F;
+  
+  Kr = harq->K;
 #ifdef DEBUG_DLSCH_CODING
-    uint16_t Kr_bytes;
-    Kr_bytes = Kr>>3;
+  uint16_t Kr_bytes;
+  Kr_bytes = Kr>>3;
 #endif
 
-    //printf("segment Z %d k %d Kr %d BG %d C %d\n", *Zc,dlsch->harq_processes[harq_pid]->K,Kr,BG,dlsch->harq_processes[harq_pid]->C);
-
-    for (r=0; r<dlsch->harq_processes[harq_pid]->C; r++) {
-      //d_tmp[r] = &dlsch->harq_processes[harq_pid]->d[r][0];
-      //channel_input[r] = &dlsch->harq_processes[harq_pid]->d[r][0];
+  //printf("segment Z %d k %d Kr %d BG %d C %d\n", *Zc,harq->K,Kr,BG,harq->C);
+  
+  for (r=0; r<harq->C; r++) {
+    //d_tmp[r] = &harq->d[r][0];
+    //channel_input[r] = &harq->d[r][0];
 #ifdef DEBUG_DLSCH_CODING
-      LOG_D(PHY,"Encoder: B %d F %d \n",dlsch->harq_processes[harq_pid]->B, dlsch->harq_processes[harq_pid]->F);
-      LOG_D(PHY,"start ldpc encoder segment %d/%d\n",r,dlsch->harq_processes[harq_pid]->C);
-      LOG_D(PHY,"input %d %d %d %d %d \n", dlsch->harq_processes[harq_pid]->c[r][0], dlsch->harq_processes[harq_pid]->c[r][1], dlsch->harq_processes[harq_pid]->c[r][2],dlsch->harq_processes[harq_pid]->c[r][3], dlsch->harq_processes[harq_pid]->c[r][4]);
-      for (int cnt =0 ; cnt < 22*(*Zc)/8; cnt ++){
-      LOG_D(PHY,"%d ", dlsch->harq_processes[harq_pid]->c[r][cnt]);
-      }
-      LOG_D(PHY,"\n");
-
-#endif
-      //ldpc_encoder_orig((unsigned char*)dlsch->harq_processes[harq_pid]->c[r],dlsch->harq_processes[harq_pid]->d[r],*Zc,Kb,Kr,BG,0);
-      //ldpc_encoder_optim((unsigned char*)dlsch->harq_processes[harq_pid]->c[r],(unsigned char*)&dlsch->harq_processes[harq_pid]->d[r][0],*Zc,Kb,Kr,BG,NULL,NULL,NULL,NULL);
+    LOG_D(PHY,"Encoder: B %d F %d \n",harq->B, harq->F);
+    LOG_D(PHY,"start ldpc encoder segment %d/%d\n",r,harq->C);
+    LOG_D(PHY,"input %d %d %d %d %d \n", harq->c[r][0], harq->c[r][1], harq->c[r][2],harq->c[r][3], harq->c[r][4]);
+    for (int cnt =0 ; cnt < 22*(*Zc)/8; cnt ++){
+      LOG_D(PHY,"%d ", harq->c[r][cnt]);
     }
-    encoder_implemparams_t impp;
-    impp.n_segments=dlsch->harq_processes[harq_pid]->C;
-    impp.tprep = tprep;
-    impp.tinput = tinput;
-    impp.tparity = tparity;
-    impp.toutput = toutput;
-
-    for(int j=0;j<(dlsch->harq_processes[harq_pid]->C/8+1);j++) {
-      impp.macro_num=j;
-      nrLDPC_encoder(dlsch->harq_processes[harq_pid]->c,dlsch->harq_processes[harq_pid]->d,*Zc,Kb,Kr,dlsch->harq_processes[harq_pid]->BG,&impp);
-    }
-
-
-#ifdef DEBUG_DLSCH_CODING
-      write_output("enc_input0.m","enc_in0",&dlsch->harq_processes[harq_pid]->c[0][0],Kr_bytes,1,4);
-      write_output("enc_output0.m","enc0",&dlsch->harq_processes[harq_pid]->d[0][0],(3*8*Kr_bytes)+12,1,4);
+    LOG_D(PHY,"\n");
+    
 #endif
-
+    //ldpc_encoder_orig((unsigned char*)harq->c[r],harq->d[r],*Zc,Kb,Kr,BG,0);
+    //ldpc_encoder_optim((unsigned char*)harq->c[r],(unsigned char*)&harq->d[r][0],*Zc,Kb,Kr,BG,NULL,NULL,NULL,NULL);
   }
+  encoder_implemparams_t impp;
+  impp.n_segments=harq->C;
+  impp.tprep = tprep;
+  impp.tinput = tinput;
+  impp.tparity = tparity;
+  impp.toutput = toutput;
+  
+  for(int j=0;j<(harq->C/8+1);j++) {
+    impp.macro_num=j;
+    nrLDPC_encoder(harq->c,harq->d,*Zc,Kb,Kr,harq->BG,&impp);
+  }
+  
 
-    F = dlsch->harq_processes[harq_pid]->F;
-
-    Kr = dlsch->harq_processes[harq_pid]->K;
-  for (r=0; r<dlsch->harq_processes[harq_pid]->C; r++) {
-
+#ifdef DEBUG_DLSCH_CODING
+  write_output("enc_input0.m","enc_in0",&harq->c[0][0],Kr_bytes,1,4);
+  write_output("enc_output0.m","enc0",&harq->d[0][0],(3*8*Kr_bytes)+12,1,4);
+#endif
+ 
+  F = harq->F;
+  
+  Kr = harq->K;
+  for (r=0; r<harq->C; r++) {
+    
     if (F>0) {
       for (int k=(Kr-F-2*(*Zc)); k<Kr-2*(*Zc); k++) {
 	// writing into positions d[r][k-2Zc] as in clause 5.3.2 step 2) in 38.212
-        dlsch->harq_processes[harq_pid]->d[r][k] = NR_NULL;
+        harq->d[r][k] = NR_NULL;
 	//if (k<(Kr-F+8))
-	//printf("r %d filler bits [%d] = %d \n", r,k, dlsch->harq_processes[harq_pid]->d[r][k]);
+	//printf("r %d filler bits [%d] = %d \n", r,k, harq->d[r][k]);
       }
     }
-
-
-
+    
+    
+    
 #ifdef DEBUG_DLSCH_CODING
-  LOG_D(PHY,"rvidx in encoding = %d\n", rel15->rvIndex[0]);
+    LOG_D(PHY,"rvidx in encoding = %d\n", rel15->rvIndex[0]);
 #endif
-
-    E = nr_get_E(G, dlsch->harq_processes[harq_pid]->C, mod_order, rel15->nrOfLayers, r);
-
+    
+    E = nr_get_E(G, harq->C, mod_order, rel15->nrOfLayers, r);
+    
     //#ifdef DEBUG_DLSCH_CODING
     LOG_D(PHY,"Rate Matching, Code segment %d/%d (coded bits (G) %u, E %d, Filler bits %d, Filler offset %d mod_order %d, nb_rb %d)...\n",
 	  r,
-	  dlsch->harq_processes[harq_pid]->C,
+	  harq->C,
 	  G,
 	  E,
 	  F,
 	  Kr-F-2*(*Zc),
 	  mod_order,nb_rb);
-
+    
     // for tbslbrm calculation according to 5.4.2.1 of 38.212
     if (rel15->nrOfLayers < Nl)
       Nl = rel15->nrOfLayers;
-
+    
     Tbslbrm = nr_compute_tbslbrm(rel15->mcsTable[0],nb_rb,Nl);
-
+    
     start_meas(dlsch_rate_matching_stats);
     nr_rate_matching_ldpc(Ilbrm,
                           Tbslbrm,
-                          dlsch->harq_processes[harq_pid]->BG,
+                          harq->BG,
                           *Zc,
-                          dlsch->harq_processes[harq_pid]->d[r],
-                          dlsch->harq_processes[harq_pid]->e+r_offset,
-                          dlsch->harq_processes[harq_pid]->C,
+                          harq->d[r],
+                          harq->e+r_offset,
+                          harq->C,
                           F,
                           Kr-F-2*(*Zc),
                           rel15->rvIndex[0],
@@ -542,22 +445,22 @@ int nr_dlsch_encoding(PHY_VARS_gNB *gNB,
     stop_meas(dlsch_rate_matching_stats);
 #ifdef DEBUG_DLSCH_CODING
     for (int i =0; i<16; i++)
-      printf("output ratematching e[%d]= %d r_offset %u\n", i,dlsch->harq_processes[harq_pid]->e[i+r_offset], r_offset);
+      printf("output ratematching e[%d]= %d r_offset %u\n", i,harq->e[i+r_offset], r_offset);
 #endif
 
     start_meas(dlsch_interleaving_stats);
     nr_interleaving_ldpc(E,
 			 mod_order,
-			 dlsch->harq_processes[harq_pid]->e+r_offset,
-			 dlsch->harq_processes[harq_pid]->f+r_offset);
+			 harq->e+r_offset,
+			 harq->f+r_offset);
     stop_meas(dlsch_interleaving_stats);
 
 #ifdef DEBUG_DLSCH_CODING
     for (int i =0; i<16; i++)
-      printf("output interleaving f[%d]= %d r_offset %u\n", i,dlsch->harq_processes[harq_pid]->f[i+r_offset], r_offset);
+      printf("output interleaving f[%d]= %d r_offset %u\n", i,harq->f[i+r_offset], r_offset);
 
-    if (r==dlsch->harq_processes[harq_pid]->C-1)
-      write_output("enc_output.m","enc",dlsch->harq_processes[harq_pid]->f,G,1,4);
+    if (r==harq->C-1)
+      write_output("enc_output.m","enc",harq->f,G,1,4);
 #endif
 
     r_offset += E;
diff --git a/openair1/PHY/NR_TRANSPORT/nr_dlsch_tools.c b/openair1/PHY/NR_TRANSPORT/nr_dlsch_tools.c
index 5ce2c45a7dd94f4dcd4a133190fe2fdbd37b5416..05d6c4c112bbe35ccc906425545ca63d25a85989 100644
--- a/openair1/PHY/NR_TRANSPORT/nr_dlsch_tools.c
+++ b/openair1/PHY/NR_TRANSPORT/nr_dlsch_tools.c
@@ -289,12 +289,12 @@ void nr_fill_dlsch(PHY_VARS_gNB *gNB,
   AssertFatal( (dlsch_id>=0) && (dlsch_id<NUMBER_OF_NR_DLSCH_MAX),
               "illegal or no dlsch_id found!!! rnti %04x dlsch_id %d\n",rel15->rnti,dlsch_id);
   NR_gNB_DLSCH_t  *dlsch = gNB->dlsch[dlsch_id][0];
-  NR_DL_gNB_HARQ_t **harq  = dlsch->harq_processes;
+  NR_DL_gNB_HARQ_t *harq  = &dlsch->harq_process;
   /// DLSCH struct
-  memcpy((void*)&harq[dlsch->harq_ids[frame%2][slot]]->pdsch_pdu, (void*)pdsch_pdu, sizeof(nfapi_nr_dl_tti_pdsch_pdu));
+  memcpy((void*)&harq->pdsch_pdu, (void*)pdsch_pdu, sizeof(nfapi_nr_dl_tti_pdsch_pdu));
   gNB->num_pdsch_rnti[slot]++;
   AssertFatal(sdu!=NULL,"sdu is null\n");
-  harq[dlsch->harq_ids[frame%2][slot]]->pdu = sdu;
+  harq->pdu = sdu;
 
 
 }
diff --git a/openair1/PHY/NR_TRANSPORT/pucch_rx.c b/openair1/PHY/NR_TRANSPORT/pucch_rx.c
index 83a66b55fb2c9d9e1709c07c6a47db8701084628..2d1fdff4be6d0c0803ae1ab9d75cdb4b7500e704 100644
--- a/openair1/PHY/NR_TRANSPORT/pucch_rx.c
+++ b/openair1/PHY/NR_TRANSPORT/pucch_rx.c
@@ -393,6 +393,7 @@ void nr_decode_pucch0(PHY_VARS_gNB *gNB,
   // first bit of bitmap for sr presence and second bit for acknack presence
   uci_pdu->pduBitmap = pucch_pdu->sr_flag | ((pucch_pdu->bit_len_harq>0)<<1);
   uci_pdu->pucch_format = 0; // format 0
+  uci_pdu->rnti = pucch_pdu->rnti;
   uci_pdu->ul_cqi = cqi;
   uci_pdu->timing_advance = 0xffff; // currently not valid
   uci_pdu->rssi = 1280 - (10*dB_fixed(32767*32767)-dB_fixed_times10(signal_energy_nodc(&rxdataF[0][pucch_pdu->start_symbol_index*frame_parms->ofdm_symbol_size+re_offset],12)));
diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_decoding.c b/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_decoding.c
index b9245640c79d319215ad13ce1605719e0c66439a..429bd9a836857abedbad4d99aec1880ebf60f704 100644
--- a/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_decoding.c
+++ b/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_decoding.c
@@ -203,7 +203,7 @@ NR_UE_DLSCH_t *new_nr_ue_dlsch(uint8_t Kmimo,uint8_t Mdlharq,uint32_t Nsoft,uint
       return(dlsch);
   }
 
-  LOG_I(PHY,"new_ue_dlsch with size %zu: exit_flag = %u\n",sizeof(NR_DL_UE_HARQ_t), exit_flag);
+  LOG_D(PHY,"new_ue_dlsch with size %zu: exit_flag = %u\n",sizeof(NR_DL_UE_HARQ_t), exit_flag);
   free_nr_ue_dlsch(&dlsch,N_RB_DL);
 
   return(NULL);
@@ -577,7 +577,7 @@ uint32_t nr_dlsch_decoding(PHY_VARS_NR_UE *phy_vars_ue,
 
       // Fixme: correct type is unsigned, but nrLDPC_decoder and all called behind use signed int
       if (check_crc((uint8_t*)llrProcBuf,length_dec,harq_process->F,crc_type)) {
-        LOG_I(PHY,"Segment %u CRC OK\n\033[0m",r);
+        LOG_D(PHY,"Segment %u CRC OK\n\033[0m",r);
         if (r==0) {
           for (int i=0;i<10;i++) LOG_D(PHY,"byte %d : %x\n",i,((uint8_t*)llrProcBuf)[i]);
         }
@@ -617,13 +617,13 @@ uint32_t nr_dlsch_decoding(PHY_VARS_NR_UE *phy_vars_ue,
     
 
     if ((err_flag == 0) && (ret>=(1+dlsch->max_ldpc_iterations))) {// a Code segment is in error so break;
-      LOG_I(PHY,"AbsSubframe %d.%d CRC failed, segment %d/%d \n",frame%1024,nr_slot_rx,r,harq_process->C-1);
+      LOG_D(PHY,"AbsSubframe %d.%d CRC failed, segment %d/%d \n",frame%1024,nr_slot_rx,r,harq_process->C-1);
       err_flag = 1;
     }
   }
 
   if (err_flag == 1) {
-    LOG_I(PHY,"[UE %d] DLSCH: Setting NAK for SFN/SF %d/%d (pid %d, status %d, round %d, TBS %d, mcs %d) Kr %d r %d harq_process->round %d\n",
+    LOG_D(PHY,"[UE %d] DLSCH: Setting NAK for SFN/SF %d/%d (pid %d, status %d, round %d, TBS %d, mcs %d) Kr %d r %d harq_process->round %d\n",
         phy_vars_ue->Mod_id, frame, nr_slot_rx, harq_pid,harq_process->status, harq_process->round,harq_process->TBS,harq_process->mcs,Kr,r,harq_process->round);
 
     harq_process->harq_ack.ack = 0;
@@ -639,7 +639,7 @@ uint32_t nr_dlsch_decoding(PHY_VARS_NR_UE *phy_vars_ue,
 
     if(is_crnti)
     {
-    LOG_I(PHY,"[UE %d] DLSCH: Setting NACK for nr_slot_rx %d (pid %d, pid status %d, round %d/Max %d, TBS %d)\n",
+    LOG_D(PHY,"[UE %d] DLSCH: Setting NACK for nr_slot_rx %d (pid %d, pid status %d, round %d/Max %d, TBS %d)\n",
                phy_vars_ue->Mod_id,nr_slot_rx,harq_pid,harq_process->status,harq_process->round,dlsch->Mdlharq,harq_process->TBS);
     }
 
@@ -813,6 +813,9 @@ uint32_t  nr_dlsch_decoding_mthread(PHY_VARS_NR_UE *phy_vars_ue,
   nb_rb = harq_process->nb_rb;
   harq_process->trials[harq_process->round]++;
 
+  // HARQ stats
+  phy_vars_ue->dl_stats[harq_process->round]++;
+
   uint16_t nb_rb_oh = 0; // it was not computed at UE side even before and set to 0 in nr_compute_tbs
 
   harq_process->TBS = nr_compute_tbs(harq_process->Qm,harq_process->R,nb_rb,nb_symb_sch,nb_re_dmrs*length_dmrs, nb_rb_oh, 0, harq_process->Nl);
@@ -826,7 +829,7 @@ uint32_t  nr_dlsch_decoding_mthread(PHY_VARS_NR_UE *phy_vars_ue,
 
   G = harq_process->G;
 
-  LOG_I(PHY,"DLSCH Decoding main, harq_pid %d TBS %d G %d, nb_re_dmrs %d, length_dmrs %d  mcs %d Nl %d nb_symb_sch %d nb_rb %d\n",harq_pid,A,G, nb_re_dmrs, length_dmrs, harq_process->mcs, harq_process->Nl, nb_symb_sch,nb_rb);
+  LOG_D(PHY,"DLSCH Decoding main, harq_pid %d TBS %d G %d, nb_re_dmrs %d, length_dmrs %d  mcs %d Nl %d nb_symb_sch %d nb_rb %d\n",harq_pid,A,G, nb_re_dmrs, length_dmrs, harq_process->mcs, harq_process->Nl, nb_symb_sch,nb_rb);
 
   proc->decoder_main_available = 1;
   proc->decoder_thread_available = 0;
@@ -905,7 +908,7 @@ uint32_t  nr_dlsch_decoding_mthread(PHY_VARS_NR_UE *phy_vars_ue,
     return((1+dlsch->max_ldpc_iterations));
   }
   if (LOG_DEBUGFLAG(DEBUG_DLSCH_DECOD))
-    LOG_I(PHY,"Segmentation: C %d, K %d\n",harq_process->C,harq_process->K);
+    LOG_D(PHY,"Segmentation: C %d, K %d\n",harq_process->C,harq_process->K);
 
 
   notifiedFIFO_elt_t *res_dl;
@@ -1079,7 +1082,7 @@ uint32_t  nr_dlsch_decoding_mthread(PHY_VARS_NR_UE *phy_vars_ue,
 #if UE_TIMING_TRACE
       start_meas(dlsch_turbo_decoding_stats);
 #endif
-      LOG_I(PHY,"mthread AbsSubframe %d.%d Start LDPC segment %d/%d \n",frame%1024,nr_slot_rx,r,harq_process->C-1);
+      LOG_D(PHY,"mthread AbsSubframe %d.%d Start LDPC segment %d/%d \n",frame%1024,nr_slot_rx,r,harq_process->C-1);
 
       /*for (int cnt =0; cnt < (kc-2)*p_decParams->Z; cnt++){
         inv_d[cnt] = (1)*harq_process->d[r][cnt];
@@ -1119,7 +1122,7 @@ uint32_t  nr_dlsch_decoding_mthread(PHY_VARS_NR_UE *phy_vars_ue,
         ret = 2;
       }
       else {
-        LOG_I(PHY,"CRC NOK\n");
+        LOG_D(PHY,"CRC NOK\n");
         ret = 1+dlsch->max_ldpc_iterations;
       }
 
@@ -1160,7 +1163,7 @@ uint32_t  nr_dlsch_decoding_mthread(PHY_VARS_NR_UE *phy_vars_ue,
 
 
     if ((err_flag == 0) && (ret>=(1+dlsch->max_ldpc_iterations))) {// a Code segment is in error so break;
-      LOG_I(PHY,"AbsSubframe %d.%d CRC failed, segment %d/%d \n",frame%1024,nr_slot_rx,r,harq_process->C-1);
+      LOG_D(PHY,"AbsSubframe %d.%d CRC failed, segment %d/%d \n",frame%1024,nr_slot_rx,r,harq_process->C-1);
       err_flag = 1;
     }
   //} //loop r
@@ -1181,7 +1184,7 @@ uint32_t  nr_dlsch_decoding_mthread(PHY_VARS_NR_UE *phy_vars_ue,
     }
     if(is_crnti)
     {
-    LOG_I(PHY,"[UE %d] DLSCH: Setting NACK for nr_slot_rx %d (pid %d, pid status %d, round %d/Max %d, TBS %d)\n",
+    LOG_D(PHY,"[UE %d] DLSCH: Setting NACK for nr_slot_rx %d (pid %d, pid status %d, round %d/Max %d, TBS %d)\n",
                phy_vars_ue->Mod_id,nr_slot_rx,harq_pid,harq_process->status,harq_process->round,dlsch->Mlimit,harq_process->TBS);
     }
 
@@ -1332,7 +1335,7 @@ void nr_dlsch_decoding_process(void *arg)
 
   p_nrLDPC_procBuf = harq_process->p_nrLDPC_procBuf[r];
   nb_symb_sch = harq_process->nb_symbols;
-  LOG_I(PHY,"dlsch decoding process frame %d slot %d segment %d r %u nb symb %d \n", frame, proc->nr_slot_rx, proc->num_seg, r, harq_process->nb_symbols);
+  LOG_D(PHY,"dlsch decoding process frame %d slot %d segment %d r %u nb symb %d \n", frame, proc->nr_slot_rx, proc->num_seg, r, harq_process->nb_symbols);
 
 
   nb_rb = harq_process->nb_rb;
@@ -1351,7 +1354,7 @@ void nr_dlsch_decoding_process(void *arg)
   harq_process->G = nr_get_G(nb_rb, nb_symb_sch, nb_re_dmrs, length_dmrs, harq_process->Qm,harq_process->Nl);
   G = harq_process->G;
 
-  LOG_I(PHY,"DLSCH Decoding process, harq_pid %d TBS %d G %d mcs %d Nl %d nb_symb_sch %d nb_rb %d\n",harq_pid,A,G, harq_process->mcs, harq_process->Nl, nb_symb_sch,nb_rb);
+  LOG_D(PHY,"DLSCH Decoding process, harq_pid %d TBS %d G %d mcs %d Nl %d nb_symb_sch %d nb_rb %d\n",harq_pid,A,G, harq_process->mcs, harq_process->Nl, nb_symb_sch,nb_rb);
 
   if ((harq_process->R)<1024)
     Coderate = (float) (harq_process->R) /(float) 1024;
@@ -1571,12 +1574,12 @@ void nr_dlsch_decoding_process(void *arg)
           ret = 2;
         }
         else {
-          LOG_I(PHY,"Segment %u CRC NOK\n",r);
+          LOG_D(PHY,"Segment %u CRC NOK\n",r);
           ret = 1+dlsch->max_ldpc_iterations;
         }
 
     if (no_iteration_ldpc > 10)
-      LOG_I(PHY,"Error number of iteration LPDC %d\n", no_iteration_ldpc);
+      LOG_D(PHY,"Error number of iteration LPDC %d\n", no_iteration_ldpc);
 
 
     for (int m=0; m < Kr>>3; m ++)
diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h
index 7b33b2040fe33a503e90c60736d834d39d7a7cd8..b7923adb0b2098734ab79623bb9cc997e6a777a0 100644
--- a/openair1/PHY/defs_gNB.h
+++ b/openair1/PHY/defs_gNB.h
@@ -94,8 +94,6 @@ typedef struct {
   uint32_t frame;
   /// Subframe where current HARQ round was sent
   uint32_t subframe;
-  /// Index of current HARQ round for this DLSCH
-  uint8_t round;
   /// MIMO mode for this DLSCH
   MIMO_mode_t mimo_mode;
   /// Concatenated sequences
@@ -138,8 +136,8 @@ typedef struct {
 } NR_gNB_SCH_STATS_t;
 
 typedef struct {
-  /// Pointers to 16 HARQ processes for the DLSCH
-  NR_DL_gNB_HARQ_t *harq_processes[NR_MAX_NB_HARQ_PROCESSES];
+  /// Pointers to variables related to DLSCH harq process
+  NR_DL_gNB_HARQ_t harq_process;
   /// TX buffers for UE-spec transmission (antenna ports 5 or 7..14, prior to precoding)
   int32_t *txdataF[NR_MAX_NB_LAYERS];
   /// Modulated symbols buffer
@@ -834,6 +832,7 @@ typedef struct PHY_VARS_gNB_s {
   notifiedFIFO_t *respDecode;
   tpool_t *threadPool;
   int nbDecode;
+  uint8_t pusch_proc_threads;
 
 } PHY_VARS_gNB;
 
diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c
index d67d7c8efa02be11241f17a87061342dc715c088..c648810bcef8cbed003d4880ce9f9f40944651ee 100644
--- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c
+++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c
@@ -201,10 +201,10 @@ void phy_procedures_gNB_TX(PHY_VARS_gNB *gNB,
     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", gNB->num_pdsch_rnti[slot],frame,slot);
     nr_generate_pdsch(gNB,frame, slot);
-    if ((frame&127) == 0) dump_pdsch_stats(gNB);
     VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_GENERATE_DLSCH,0);
   }
 
+  if ((frame&127) == 0) dump_pdsch_stats(gNB);
 
   //apply the OFDM symbol rotation here
   apply_nr_rotation(fp,(int16_t*) &gNB->common_vars.txdataF[0][txdataF_offset],slot,0,fp->Ncp==EXTENDED?12:14,fp->ofdm_symbol_size);
diff --git a/openair1/SCHED_NR_UE/pucch_uci_ue_nr.c b/openair1/SCHED_NR_UE/pucch_uci_ue_nr.c
index b1fd2a4df805f4e820a712e185a136307f56b2f4..edb070f8735c479614fc10bb1362501e078976d1 100644
--- a/openair1/SCHED_NR_UE/pucch_uci_ue_nr.c
+++ b/openair1/SCHED_NR_UE/pucch_uci_ue_nr.c
@@ -780,19 +780,15 @@ uint8_t get_downlink_ack(PHY_VARS_NR_UE *ue, uint8_t gNB_id,  UE_nr_rxtx_proc_t
 
           if (harq_status->ack == DL_ACKNACK_NO_SET) {
             LOG_E(PHY,"PUCCH Downlink acknowledgment has not been set : at line %d in function %s of file %s \n", LINE_FILE , __func__, FILE_NAME);
-            return (0);
           }
           else if (harq_status->vDAI_DL == DL_DAI_NO_SET) {
             LOG_E(PHY,"PUCCH Downlink DAI has not been set : at line %d in function %s of file %s \n", LINE_FILE , __func__, FILE_NAME);
-            return (0);
           }
           else if (harq_status->vDAI_DL > NR_DL_MAX_DAI) {
             LOG_E(PHY,"PUCCH Downlink DAI has an invalid value : at line %d in function %s of file %s \n", LINE_FILE , __func__, FILE_NAME);
-            return (0);
           }
           else if (harq_status->send_harq_status == 0) {
-            LOG_E(PHY,"PUCCH Downlink ack can not be transmitted : at line %d in function %s of file %s \n", LINE_FILE , __func__, FILE_NAME);
-            return(0);
+            LOG_D(PHY,"PUCCH Downlink ack can not be transmitted : at line %d in function %s of file %s \n", LINE_FILE , __func__, FILE_NAME);
           }
           else {
 
@@ -808,6 +804,8 @@ uint8_t get_downlink_ack(PHY_VARS_NR_UE *ue, uint8_t gNB_id,  UE_nr_rxtx_proc_t
             number_harq_feedback++;
             ack_data[code_word][dai_current - 1] = harq_status->ack;
             dai[code_word][dai_current - 1] = dai_current;
+            harq_status->slot_for_feedback_ack = NR_MAX_SLOTS_PER_FRAME;
+            harq_status->send_harq_status = 0;
           }
           if (do_reset == TRUE) {
             init_downlink_harq_status(ue->dlsch[thread_idx][gNB_id][code_word]->harq_processes[dl_harq_pid]);
diff --git a/openair1/SIMULATION/NR_PHY/dlschsim.c b/openair1/SIMULATION/NR_PHY/dlschsim.c
index c60bc8dd9f3237adb9c207325bb4fdaab8844e0d..061866b12c08f8140e74c2227a484b1fc10cf0d6 100644
--- a/openair1/SIMULATION/NR_PHY/dlschsim.c
+++ b/openair1/SIMULATION/NR_PHY/dlschsim.c
@@ -427,7 +427,7 @@ int main(int argc, char **argv)
 	UE->dlsch_ra[0] = new_nr_ue_dlsch(1, 1, Nsoft, 5, N_RB_DL, 0);
 	unsigned char harq_pid = 0; //dlsch->harq_ids[subframe];
 	NR_gNB_DLSCH_t *dlsch = gNB->dlsch[0][0];
-	nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &dlsch->harq_processes[harq_pid]->pdsch_pdu.pdsch_pdu_rel15;
+	nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &dlsch->harq_process.pdsch_pdu.pdsch_pdu_rel15;
 	//time_stats_t *rm_stats, *te_stats, *i_stats;
 	uint8_t is_crnti = 0, llr8_flag = 0;
 	unsigned int TBS = 8424;
@@ -519,7 +519,7 @@ int main(int argc, char **argv)
 
 				//if (i<16)
 				//   printf("encoder output f[%d] = %d\n",i,dlsch->harq_processes[0]->f[i]);
-				if (dlsch->harq_processes[0]->f[i] == 0)
+				if (dlsch->harq_process.f[i] == 0)
 					modulated_input[i] = 1.0;        ///sqrt(2);  //QPSK
 				else
 					modulated_input[i] = -1.0;        ///sqrt(2);
@@ -547,7 +547,7 @@ int main(int argc, char **argv)
 				else
 					channel_output_uncoded[i] = 0;
 
-				if (channel_output_uncoded[i] != dlsch->harq_processes[harq_pid]->f[i])
+				if (channel_output_uncoded[i] != dlsch->harq_process.f[i])
 					errors_bit_uncoded = errors_bit_uncoded + 1;
 			}
 
diff --git a/openair1/SIMULATION/NR_PHY/dlsim.c b/openair1/SIMULATION/NR_PHY/dlsim.c
index d8eb8849a31ee6aeff50670f21cee8f556cc00ae..9581038283f993ce18d738ed7f84d1eef344c9f6 100644
--- a/openair1/SIMULATION/NR_PHY/dlsim.c
+++ b/openair1/SIMULATION/NR_PHY/dlsim.c
@@ -186,8 +186,7 @@ void update_dmrs_config(NR_CellGroupConfig_t *scg,PHY_VARS_NR_UE *ue, int8_t* dm
 int g_mcsIndex = -1, g_mcsTableIdx = 0, g_rbStart = -1, g_rbSize = -1;
 void nr_dlsim_preprocessor(module_id_t module_id,
                            frame_t frame,
-                           sub_frame_t slot,
-                           int num_slots_per_tdd) {
+                           sub_frame_t slot) {
   NR_UE_info_t *UE_info = &RC.nrmac[module_id]->UE_info;
   AssertFatal(UE_info->num_UEs == 1, "can have only a single UE\n");
   NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[0];
@@ -203,15 +202,25 @@ void nr_dlsim_preprocessor(module_id_t module_id,
       sched_ctrl->active_bwp, sched_ctrl->search_space, 1 /* dedicated */);
   sched_ctrl->cce_index = 0;
 
-  /* set "any" value for PUCCH (simulator evaluates PDSCH only) */
-  sched_ctrl->pucch_sched_idx = 0;
-  sched_ctrl->pucch_occ_idx = 0;
-
   sched_ctrl->rbStart = g_rbStart;
   sched_ctrl->rbSize = g_rbSize;
   sched_ctrl->mcs = g_mcsIndex;
   sched_ctrl->time_domain_allocation = 2;
   sched_ctrl->mcsTableIdx = g_mcsTableIdx;
+  /* the simulator assumes the HARQ PID is equal to the slot number */
+  sched_ctrl->dl_harq_pid = slot;
+  /* The scheduler uses lists to track whether a HARQ process is
+   * free/busy/awaiting retransmission, and updates the HARQ process states.
+   * However, in the simulation, we never get ack or nack for any HARQ process,
+   * thus the list and HARQ states don't match what the scheduler expects.
+   * Therefore, below lines just "repair" everything so that the scheduler
+   * won't remark that there is no HARQ feedback */
+  sched_ctrl->feedback_dl_harq.head = -1; // always overwrite feedback HARQ process
+  if (sched_ctrl->harq_processes[slot].round == 0) // depending on round set in simulation ...
+    add_front_nr_list(&sched_ctrl->available_dl_harq, slot); // ... make PID available
+  else
+    add_front_nr_list(&sched_ctrl->retrans_dl_harq, slot);   // ... make PID retransmission
+  sched_ctrl->harq_processes[slot].is_waiting = false;
   AssertFatal(sched_ctrl->rbStart >= 0, "invalid rbStart %d\n", sched_ctrl->rbStart);
   AssertFatal(sched_ctrl->rbSize > 0, "invalid rbSize %d\n", sched_ctrl->rbSize);
   AssertFatal(sched_ctrl->mcs >= 0, "invalid sched_ctrl->mcs %d\n", sched_ctrl->mcs);
@@ -671,6 +680,9 @@ int main(int argc, char **argv)
   rrc_mac_config_req_gNB(0,0,1,pusch_tgt_snrx10,pucch_tgt_snrx10,NULL,1,secondaryCellGroup->spCellConfig->reconfigurationWithSync->newUE_Identity,secondaryCellGroup);
   phy_init_nr_gNB(gNB,0,0);
   N_RB_DL = gNB->frame_parms.N_RB_DL;
+  NR_UE_info_t *UE_info = &RC.nrmac[0]->UE_info;
+  UE_info->num_UEs=1;
+
   // stub to configure frame_parms
   //  nr_phy_config_request_sim(gNB,N_RB_DL,N_RB_DL,mu,Nid_cell,SSB_positions);
   // call MAC to configure common parameters
@@ -836,10 +848,10 @@ int main(int argc, char **argv)
   scheduled_response.thread_id = UE_proc.thread_id;
 
   nr_ue_phy_config_request(&UE_mac->phy_config);
-  NR_UE_info_t *UE_info = &RC.nrmac[0]->UE_info;
   //NR_COMMON_channels_t *cc = RC.nrmac[0]->common_channels;
   snrRun = 0;
 
+
   for (SNR = snr0; SNR < snr1; SNR += .2) {
 
     varArray_t *table_tx=initVarArray(1000,sizeof(double));
@@ -885,7 +897,7 @@ int main(int argc, char **argv)
       NR_DL_UE_HARQ_t *UE_harq_process = dlsch0->harq_processes[harq_pid];
 
       NR_gNB_DLSCH_t *gNB_dlsch = gNB->dlsch[0][0];
-      nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &gNB_dlsch->harq_processes[slot]->pdsch_pdu.pdsch_pdu_rel15;
+      nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &gNB_dlsch->harq_process.pdsch_pdu.pdsch_pdu_rel15;
       
       UE_harq_process->harq_ack.ack = 0;
       round = 0;
@@ -901,15 +913,11 @@ int main(int argc, char **argv)
 
 
         UE_info->UE_sched_ctrl[0].harq_processes[harq_pid].round = round;
-        gNB->dlsch[0][0]->harq_processes[harq_pid]->round = round;
         for (int i=0; i<MAX_NUM_CORESET; i++)
           gNB_mac->UE_info.num_pdcch_cand[0][i] = 0;
       
         if (css_flag == 0) {
-          const uint8_t slots_per_frame[5] = {10, 20, 40, 80, 160};
-          const NR_TDD_UL_DL_Pattern_t *tdd_pattern = &scc->tdd_UL_DL_ConfigurationCommon->pattern1;
-          const int num_slots_per_tdd = slots_per_frame[*scc->ssbSubcarrierSpacing] >> (7 - tdd_pattern->dl_UL_TransmissionPeriodicity);
-          nr_schedule_ue_spec(0, frame, slot, num_slots_per_tdd);
+          nr_schedule_ue_spec(0, frame, slot);
         } else {
           nr_schedule_css_dlsch_phytest(0,frame,slot);
         }
@@ -1053,8 +1061,8 @@ int main(int argc, char **argv)
       
       for (i = 0; i < available_bits; i++) {
 	
-	if(((gNB_dlsch->harq_processes[harq_pid]->f[i] == 0) && (UE_llr[i] <= 0)) || 
-	   ((gNB_dlsch->harq_processes[harq_pid]->f[i] == 1) && (UE_llr[i] >= 0)))
+	if(((gNB_dlsch->harq_process.f[i] == 0) && (UE_llr[i] <= 0)) || 
+	   ((gNB_dlsch->harq_process.f[i] == 1) && (UE_llr[i] >= 0)))
 	  {
 	    if(errors_scrambling == 0) {
 	      LOG_D(PHY,"\n");
@@ -1067,7 +1075,7 @@ int main(int argc, char **argv)
       for (i = 0; i < TBS; i++) {
 
 	estimated_output_bit[i] = (UE_harq_process->b[i/8] & (1 << (i & 7))) >> (i & 7);
-	test_input_bit[i]       = (gNB_dlsch->harq_processes[harq_pid]->b[i / 8] & (1 << (i & 7))) >> (i & 7); // Further correct for multiple segments
+	test_input_bit[i]       = (gNB_dlsch->harq_process.b[i / 8] & (1 << (i & 7))) >> (i & 7); // Further correct for multiple segments
 	
 	if (estimated_output_bit[i] != test_input_bit[i]) {
 	  if(errors_bit == 0)
@@ -1107,9 +1115,9 @@ int main(int argc, char **argv)
     if (print_perf==1) {
       printf("\ngNB TX function statistics (per %d us slot, NPRB %d, mcs %d, TBS %d, Kr %d (Zc %d))\n",
 	     1000>>*scc->ssbSubcarrierSpacing, g_rbSize, g_mcsIndex,
-	     gNB->dlsch[0][0]->harq_processes[0]->pdsch_pdu.pdsch_pdu_rel15.TBSize[0]<<3,
-	     gNB->dlsch[0][0]->harq_processes[0]->K,
-	     gNB->dlsch[0][0]->harq_processes[0]->K/((gNB->dlsch[0][0]->harq_processes[0]->pdsch_pdu.pdsch_pdu_rel15.TBSize[0]<<3)>3824?22:10));
+	     gNB->dlsch[0][0]->harq_process.pdsch_pdu.pdsch_pdu_rel15.TBSize[0]<<3,
+	     gNB->dlsch[0][0]->harq_process.K,
+	     gNB->dlsch[0][0]->harq_process.K/((gNB->dlsch[0][0]->harq_process.pdsch_pdu.pdsch_pdu_rel15.TBSize[0]<<3)>3824?22:10));
       printDistribution(&gNB->phy_proc_tx,table_tx,"PHY proc tx");
       printStatIndent2(&gNB->dlsch_encoding_stats,"DLSCH encoding time");
       printStatIndent3(&gNB->dlsch_segmentation_stats,"DLSCH segmentation time");
diff --git a/openair2/GNB_APP/L1_nr_paramdef.h b/openair2/GNB_APP/L1_nr_paramdef.h
index a5a808c2811ad21684cabbedb572ea03fc810518..67be560778b9848ffe29e254d75f788988b06be6 100644
--- a/openair2/GNB_APP/L1_nr_paramdef.h
+++ b/openair2/GNB_APP/L1_nr_paramdef.h
@@ -46,6 +46,7 @@
 #define CONFIG_STRING_L1_LOCAL_N_PORTD                     "local_n_portd"
 #define CONFIG_STRING_L1_REMOTE_N_PORTD                    "remote_n_portd"
 #define CONFIG_STRING_L1_TRANSPORT_N_PREFERENCE            "tr_n_preference"
+#define CONFIG_STRING_L1_PUSCH_PROC_THREADS                "pusch_proc_threads"
 
 /*----------------------------------------------------------------------------------------------------------------------------------------------------*/
 /*                                            L1 configuration parameters                                                                             */
@@ -61,6 +62,7 @@
 {CONFIG_STRING_L1_REMOTE_N_PORTC,                    NULL,      0,         uptr:NULL,           defintval:50030,           TYPE_UINT,     0},         \
 {CONFIG_STRING_L1_LOCAL_N_PORTD,                     NULL,      0,         uptr:NULL,           defintval:50031,           TYPE_UINT,     0},         \
 {CONFIG_STRING_L1_REMOTE_N_PORTD,                    NULL,      0,         uptr:NULL,           defintval:50031,           TYPE_UINT,     0},         \
+{CONFIG_STRING_L1_PUSCH_PROC_THREADS,                NULL,      0,         uptr:NULL,           defintval:1,               TYPE_UINT,     0} \
 }
 #define L1_CC_IDX                                          0
 #define L1_TRANSPORT_N_PREFERENCE_IDX                      1
@@ -71,6 +73,7 @@
 #define L1_REMOTE_N_PORTC_IDX                              6
 #define L1_LOCAL_N_PORTD_IDX                               7
 #define L1_REMOTE_N_PORTD_IDX                              8
+#define L1_PUSCH_PROC_THREADS                              9
 
 /*----------------------------------------------------------------------------------------------------------------------------------------------------*/
 #endif
diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c
index d830e51f4da0abfe3623f9ab18480fbf79a17f4f..0c056d27b6a4e0d5a4daada9e3e1021b4ad660cf 100644
--- a/openair2/GNB_APP/gnb_config.c
+++ b/openair2/GNB_APP/gnb_config.c
@@ -51,7 +51,8 @@
 #include "nfapi_vnf.h"
 #include "nfapi_pnf.h"
 
-#include "L1_paramdef.h"
+//#include "L1_paramdef.h"
+#include "L1_nr_paramdef.h"
 #include "MACRLC_paramdef.h"
 #include "common/config/config_userapi.h"
 //#include "RRC_config_tools.h"
@@ -399,6 +400,8 @@ void RCconfig_NR_L1(void) {
 	RC.gNB[j]->Mod_id  = j;
       }
 
+      RC.gNB[j]->pusch_proc_threads = *(L1_ParamList.paramarray[j][L1_PUSCH_PROC_THREADS].uptr);
+
       if(strcmp(*(L1_ParamList.paramarray[j][L1_TRANSPORT_N_PREFERENCE_IDX].strptr), "local_mac") == 0) {
         //sf_ahead = 2; // Need 4 subframe gap between RX and TX
       }else if (strcmp(*(L1_ParamList.paramarray[j][L1_TRANSPORT_N_PREFERENCE_IDX].strptr), "nfapi") == 0) {
diff --git a/openair2/GNB_APP/gnb_paramdef.h b/openair2/GNB_APP/gnb_paramdef.h
index 981f2ba193d726934ea45921ccefac80f6a71b1a..f1fe0c97c9128be73e3758fe0e0eacfcac73eeb2 100644
--- a/openair2/GNB_APP/gnb_paramdef.h
+++ b/openair2/GNB_APP/gnb_paramdef.h
@@ -82,7 +82,6 @@ typedef enum {
 /* global parameters, not under a specific section   */
 #define GNB_CONFIG_STRING_ASN1_VERBOSITY                   "Asn1_verbosity"
 #define GNB_CONFIG_STRING_ACTIVE_GNBS                      "Active_gNBs"
-#define GNB_CONFIG_PUSCH_THREADS                           "Num_Threads_PUSCH"
 /*--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
 /*                                            global configuration parameters                                                                                   */
 /*   optname                                   helpstr   paramflags    XXXptr        defXXXval                                        type           numelt     */
@@ -92,10 +91,6 @@ typedef enum {
 {GNB_CONFIG_STRING_ACTIVE_GNBS,                NULL,     0,        uptr:NULL,   defstrval:NULL, 				   TYPE_STRINGLIST,  0}    \
 }
 
-#define NUM_THREADS_DESC { \
-{GNB_CONFIG_PUSCH_THREADS,                     NULL,     0,        uptr:&num_threads_pusch,   defuintval:1, 				   TYPE_UINT,  0}    \
-}
-
 #define GNB_ASN1_VERBOSITY_IDX                     0
 #define GNB_ACTIVE_GNBS_IDX                        1
 
diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_mac.h b/openair2/LAYER2/NR_MAC_COMMON/nr_mac.h
index 1c8c510ec6b96d2aa0b7e0c797403241e6510804..381dce11b2cddce03c9633c17add5dc24d3ad7e6 100644
--- a/openair2/LAYER2/NR_MAC_COMMON/nr_mac.h
+++ b/openair2/LAYER2/NR_MAC_COMMON/nr_mac.h
@@ -119,22 +119,22 @@ typedef NR_BSR_SHORT NR_BSR_SHORT_TRUNCATED;
 
 // Long BSR for all logical channel group ID
 typedef struct {
-  uint8_t Buffer_size7: 8;
-  uint8_t Buffer_size6: 8;
-  uint8_t Buffer_size5: 8;
-  uint8_t Buffer_size4: 8;
-  uint8_t Buffer_size3: 8;
-  uint8_t Buffer_size2: 8;
-  uint8_t Buffer_size1: 8;
-  uint8_t Buffer_size0: 8;
-  uint8_t LcgID0: 1;
-  uint8_t LcgID1: 1;
-  uint8_t LcgID2: 1;
-  uint8_t LcgID3: 1;
-  uint8_t LcgID4: 1;
-  uint8_t LcgID5: 1;
-  uint8_t LcgID6: 1;
-  uint8_t LcgID7: 1;
+  uint8_t LcgID0: 1;        // octet 1 [0]
+  uint8_t LcgID1: 1;        // octet 1 [1]
+  uint8_t LcgID2: 1;        // octet 1 [2]
+  uint8_t LcgID3: 1;        // octet 1 [3]
+  uint8_t LcgID4: 1;        // octet 1 [4]
+  uint8_t LcgID5: 1;        // octet 1 [5]
+  uint8_t LcgID6: 1;        // octet 1 [6]
+  uint8_t LcgID7: 1;        // octet 1 [7]
+  uint8_t Buffer_size0: 8;  // octet 2 [7:0]
+  uint8_t Buffer_size1: 8;  // octet 3 [7:0]
+  uint8_t Buffer_size2: 8;  // octet 4 [7:0]
+  uint8_t Buffer_size3: 8;  // octet 5 [7:0]
+  uint8_t Buffer_size4: 8;  // octet 6 [7:0]
+  uint8_t Buffer_size5: 8;  // octet 7 [7:0]
+  uint8_t Buffer_size6: 8;  // octet 8 [7:0]
+  uint8_t Buffer_size7: 8;  // octet 9 [7:0]
 } __attribute__ ((__packed__)) NR_BSR_LONG;
 
 typedef NR_BSR_LONG NR_BSR_LONG_TRUNCATED;
diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c
index c8bba7907a3b3e3534ab0aa8b1bef2e54bf69d5f..8dc261df1da1efacbd340ea239777a5be74a1670 100644
--- a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c
+++ b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c
@@ -2382,9 +2382,9 @@ uint8_t get_K_ptrs(uint16_t nrb0, uint16_t nrb1, uint16_t N_RB) {
 
 // Set the transform precoding status according to 6.1.3 of 3GPP TS 38.214 version 16.3.0 Release 16:
 // - "UE procedure for applying transform precoding on PUSCH"
-uint8_t get_transformPrecoding(NR_ServingCellConfigCommon_t *scc,
-                               NR_PUSCH_Config_t *pusch_config,
-                               NR_BWP_Uplink_t *ubwp,
+uint8_t get_transformPrecoding(const NR_ServingCellConfigCommon_t *scc,
+                               const NR_PUSCH_Config_t *pusch_config,
+                               const NR_BWP_Uplink_t *ubwp,
                                uint8_t *dci_format,
                                int rnti_type,
                                uint8_t configuredGrant){
@@ -2416,8 +2416,8 @@ uint8_t get_transformPrecoding(NR_ServingCellConfigCommon_t *scc,
   return -1;
 }
 
-uint16_t nr_dci_size(NR_ServingCellConfigCommon_t *scc,
-                     NR_CellGroupConfig_t *secondaryCellGroup,
+uint16_t nr_dci_size(const NR_ServingCellConfigCommon_t *scc,
+                     const NR_CellGroupConfig_t *secondaryCellGroup,
                      dci_pdu_rel15_t *dci_pdu,
                      nr_dci_format_t format,
 		     nr_rnti_type_t rnti_type,
diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h
index 7534035cdc09918b25e763fef94a196936dfb933..58ecd54733244486b1e3ba726b7e718fd74ed50c 100644
--- a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h
+++ b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h
@@ -53,8 +53,8 @@ int is_nr_DL_slot(NR_ServingCellConfigCommon_t *scc,slot_t slotP);
 
 int is_nr_UL_slot(NR_ServingCellConfigCommon_t *scc,slot_t slotP);
 
-uint16_t nr_dci_size(NR_ServingCellConfigCommon_t *scc,
-                     NR_CellGroupConfig_t *secondaryCellGroup,
+uint16_t nr_dci_size(const NR_ServingCellConfigCommon_t *scc,
+                     const NR_CellGroupConfig_t *secondaryCellGroup,
                      dci_pdu_rel15_t *dci_pdu,
                      nr_dci_format_t format,
 		     nr_rnti_type_t rnti_type,
@@ -150,9 +150,9 @@ uint8_t get_num_dmrs_symbols(NR_PDSCH_Config_t *pdsch_Config,int dmrs_TypeA_Posi
 @param    rnti_type        rnti type
 @param    configuredGrant  indicates whether a configured grant was received or not
 @returns                   transformPrecoding value */
-uint8_t get_transformPrecoding(NR_ServingCellConfigCommon_t *scc,
-                               NR_PUSCH_Config_t *pusch_config,
-                               NR_BWP_Uplink_t *ubwp,
+uint8_t get_transformPrecoding(const NR_ServingCellConfigCommon_t *scc,
+                               const NR_PUSCH_Config_t *pusch_config,
+                               const NR_BWP_Uplink_t *ubwp,
                                uint8_t *dci_format,
                                int rnti_type,
                                uint8_t configuredGrant);
diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c
index 2565c8e73c3446e40a6e94a6d16e3da93e3d2837..f6bd5436bc239fd132054f066ba9e762be47058d 100755
--- a/openair2/LAYER2/NR_MAC_UE/config_ue.c
+++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c
@@ -82,6 +82,7 @@ int set_tdd_config_nr_ue(fapi_nr_config_request_t *cfg,
   }
 
   int nb_slots_per_period = ((1<<mu) * NR_NUMBER_OF_SUBFRAMES_PER_FRAME)/nb_periods_per_frame;
+  cfg->tdd_table.tdd_period_in_slots = nb_slots_per_period;
 
   if ( (nrofDownlinkSymbols + nrofUplinkSymbols) == 0 )
     AssertFatal(nb_slots_per_period == (nrofDownlinkSlots + nrofUplinkSlots),
diff --git a/openair2/LAYER2/NR_MAC_UE/mac_defs.h b/openair2/LAYER2/NR_MAC_UE/mac_defs.h
index 3bd9f021c86b7ec605d0b01c468ebce2414c3246..dd0aa15c52a0c40b3236db207d70ddcb424faa1e 100755
--- a/openair2/LAYER2/NR_MAC_UE/mac_defs.h
+++ b/openair2/LAYER2/NR_MAC_UE/mac_defs.h
@@ -350,6 +350,8 @@ typedef struct {
   RA_config_t ra;
   /// SSB index from MIB decoding
   uint8_t mib_ssb;
+  /// Last NDI of UL HARQ processes
+  uint8_t UL_ndi[NR_MAX_HARQ_PROCESSES];
 
   ////	FAPI-like interface message
   fapi_nr_ul_config_request_t *ul_config_request;
diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c
index 8a329f52694d880d5f081d19d438a8cfa8619cdf..64067e9d60df8b978b2885bf04c2bc7cb3468de5 100644
--- a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c
+++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c
@@ -2046,7 +2046,7 @@ uint16_t nr_generate_ulsch_pdu(uint8_t *sdus_payload,
   if (crnti) {
     // MAC CE fixed subheader
     mac_pdu_ptr->R = 0;
-    mac_pdu_ptr->LCID = CRNTI;
+    mac_pdu_ptr->LCID = UL_SCH_LCID_C_RNTI;
     mac_pdu_ptr++;
 
     // C-RNTI MAC CE (2 octets)
diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c
index 0a6af58dee912181a2c8abc1e26afc4da8103e1a..d6e1cba6df103f133a755501b0fb9258c0f12d05 100644
--- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c
+++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c
@@ -889,7 +889,8 @@ NR_UE_L2_STATE_t nr_ue_scheduler(nr_downlink_indication_t *dl_info, nr_uplink_in
 
           uint16_t TBS_bytes = ulcfg_pdu->pusch_config_pdu.pusch_data.tb_size;
 
-          if (IS_SOFTMODEM_NOS1){
+          // Push data from MAC to PHY only when NDI toggles
+          if (IS_SOFTMODEM_NOS1 && (mac->UL_ndi[ulcfg_pdu->pusch_config_pdu.pusch_data.harq_process_id] != ulcfg_pdu->pusch_config_pdu.pusch_data.new_data_indicator)){
             // Getting IP traffic to be transmitted
             data_existing = nr_ue_get_sdu(mod_id,
                                           cc_id,
@@ -901,6 +902,7 @@ NR_UE_L2_STATE_t nr_ue_scheduler(nr_downlink_indication_t *dl_info, nr_uplink_in
                                           &access_mode);
           }
 
+          mac->UL_ndi[ulcfg_pdu->pusch_config_pdu.pusch_data.harq_process_id] = ulcfg_pdu->pusch_config_pdu.pusch_data.new_data_indicator;
           //Random traffic to be transmitted if there is no IP traffic available for this Tx opportunity
           if (!IS_SOFTMODEM_NOS1 || !data_existing) {
             //Use zeros for the header bytes in noS1 mode, in order to make sure that the LCID is not valid
diff --git a/openair2/LAYER2/NR_MAC_gNB/config.c b/openair2/LAYER2/NR_MAC_gNB/config.c
index bd6a8f3384186e0cbef47c1c24999c13c0b19db0..6aa906b82b196b48f34be24f501a542744d4bbac 100644
--- a/openair2/LAYER2/NR_MAC_gNB/config.c
+++ b/openair2/LAYER2/NR_MAC_gNB/config.c
@@ -346,10 +346,18 @@ int rrc_mac_config_req_gNB(module_id_t Mod_idP,
     }
 
     RC.nrmac[Mod_idP]->common_channels[0].vrb_map_UL =
-        calloc(n * 275, sizeof(uint16_t));
+        calloc(n * MAX_BWP_SIZE, sizeof(uint16_t));
     AssertFatal(RC.nrmac[Mod_idP]->common_channels[0].vrb_map_UL,
                 "could not allocate memory for RC.nrmac[]->common_channels[0].vrb_map_UL\n");
 
+    for (int i = 0; i < MAX_NUM_BWP; ++i) {
+      RC.nrmac[Mod_idP]->pucch_index_used[i] =
+        calloc(n, sizeof(*RC.nrmac[Mod_idP]->pucch_index_used));
+      AssertFatal(RC.nrmac[Mod_idP]->pucch_index_used[i],
+                  "could not allocate memory for RC.nrmac[]->pucch_index_used[%d]\n",
+                  i);
+    }
+
     LOG_I(MAC,"Configuring common parameters from NR ServingCellConfig\n");
 
     config_common(Mod_idP,
@@ -389,22 +397,7 @@ int rrc_mac_config_req_gNB(module_id_t Mod_idP,
 
     NR_UE_info_t *UE_info = &RC.nrmac[Mod_idP]->UE_info;
     if (add_ue == 1 && get_softmodem_params()->phy_test) {
-      const int UE_id = add_new_nr_ue(Mod_idP,rnti);
-      UE_info->secondaryCellGroup[UE_id] = secondaryCellGroup;
-      compute_csi_bitlen (secondaryCellGroup, UE_info, UE_id);
-      struct NR_ServingCellConfig__downlinkBWP_ToAddModList *bwpList =
-          secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList;
-      AssertFatal(bwpList->list.count == 1,
-                  "downlinkBWP_ToAddModList has %d BWP!\n",
-                  bwpList->list.count);
-      const int bwp_id = 1;
-      UE_info->UE_sched_ctrl[UE_id].active_bwp = bwpList->list.array[bwp_id - 1];
-      struct NR_UplinkConfig__uplinkBWP_ToAddModList *ubwpList =
-          secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList;
-      AssertFatal(ubwpList->list.count == 1,
-                  "uplinkBWP_ToAddModList has %d BWP!\n",
-                  ubwpList->list.count);
-      UE_info->UE_sched_ctrl[UE_id].active_ubwp = ubwpList->list.array[bwp_id - 1];
+      const int UE_id = add_new_nr_ue(Mod_idP, rnti, secondaryCellGroup);
       LOG_I(PHY,"Added new UE_id %d/%x with initial secondaryCellGroup\n",UE_id,rnti);
     } else if (add_ue == 1 && !get_softmodem_params()->phy_test) {
       /* TODO: should check for free RA process */
diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c
index 18be96fee89cbd76980375229c8555ac56b24b75..d5dbbe5ddfcae5b78d80114b916a134ae8ce152c 100644
--- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c
+++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c
@@ -59,24 +59,31 @@ void clear_mac_stats(gNB_MAC_INST *gNB) {
   memset((void*)gNB->UE_info.mac_stats,0,MAX_MOBILES_PER_GNB*sizeof(NR_mac_stats_t));
 }
 
-void dump_mac_stats(gNB_MAC_INST *gNB) {
-
+void dump_mac_stats(gNB_MAC_INST *gNB)
+{
   NR_UE_info_t *UE_info = &gNB->UE_info;
-  NR_mac_stats_t *stats;
-  int lc_id;
-
-  for (int UE_id=0;UE_id<MAX_MOBILES_PER_GNB;UE_id++) {
-    if (UE_info->active[UE_id]) {
-      LOG_I(MAC, "UE %x\n", UE_info->rnti[UE_id]);
-      stats = &UE_info->mac_stats[UE_id];
-      LOG_I(MAC,"dlsch_rounds %d/%d/%d/%d, dlsch_errors %d\n",stats->dlsch_rounds[0],stats->dlsch_rounds[1],stats->dlsch_rounds[2],stats->dlsch_rounds[3],stats->dlsch_errors);
-      LOG_I(MAC,"dlsch_total_bytes %d\n",stats->dlsch_total_bytes);
-      LOG_I(MAC,"ulsch_rounds %d/%d/%d/%d, ulsch_errors %d\n",stats->ulsch_rounds[0],stats->ulsch_rounds[1],stats->ulsch_rounds[2],stats->ulsch_rounds[3],stats->ulsch_errors);
-      LOG_I(MAC,"ulsch_total_bytes_scheduled %d, ulsch_total_bytes_received %d\n",stats->ulsch_total_bytes_scheduled,stats->ulsch_total_bytes_rx);
-      for (lc_id=0;lc_id<63;lc_id++) {
-	if (stats->lc_bytes_tx[lc_id]>0) LOG_I(MAC,"LCID %d : %d bytes TX\n",lc_id,stats->lc_bytes_tx[lc_id]);
-	if (stats->lc_bytes_rx[lc_id]>0) LOG_I(MAC,"LCID %d : %d bytes RX\n",lc_id,stats->lc_bytes_rx[lc_id]);
-      }
+  int num = 1;
+  for (int UE_id = UE_info->list.head; UE_id >= 0; UE_id = UE_info->list.next[UE_id]) {
+    LOG_I(MAC, "UE ID %d RNTI %04x (%d/%d)\n", UE_id, UE_info->rnti[UE_id], num++, UE_info->num_UEs);
+    const NR_mac_stats_t *stats = &UE_info->mac_stats[UE_id];
+    LOG_I(MAC, "UE %d: dlsch_rounds %d/%d/%d/%d, dlsch_errors %d\n",
+          UE_id,
+          stats->dlsch_rounds[0], stats->dlsch_rounds[1],
+          stats->dlsch_rounds[2], stats->dlsch_rounds[3], stats->dlsch_errors);
+    LOG_I(MAC, "UE %d: dlsch_total_bytes %d\n", UE_id, stats->dlsch_total_bytes);
+    LOG_I(MAC, "UE %d: ulsch_rounds %d/%d/%d/%d, ulsch_errors %d\n",
+          UE_id,
+          stats->ulsch_rounds[0], stats->ulsch_rounds[1],
+          stats->ulsch_rounds[2], stats->ulsch_rounds[3], stats->ulsch_errors);
+    LOG_I(MAC,
+          "UE %d: ulsch_total_bytes_scheduled %d, ulsch_total_bytes_received %d\n",
+          UE_id,
+          stats->ulsch_total_bytes_scheduled, stats->ulsch_total_bytes_rx);
+    for (int lc_id = 0; lc_id < 63; lc_id++) {
+      if (stats->lc_bytes_tx[lc_id] > 0)
+        LOG_I(MAC, "UE %d: LCID %d: %d bytes TX\n", UE_id, lc_id, stats->lc_bytes_tx[lc_id]);
+      if (stats->lc_bytes_rx[lc_id] > 0)
+        LOG_I(MAC, "UE %d: LCID %d: %d bytes RX\n", UE_id, lc_id, stats->lc_bytes_rx[lc_id]);
     }
   }
 }
@@ -89,6 +96,7 @@ void clear_nr_nfapi_information(gNB_MAC_INST * gNB,
   const int num_slots = nr_slots_per_frame[*scc->ssbSubcarrierSpacing];
 
   nfapi_nr_dl_tti_request_t    *DL_req = &gNB->DL_req[0];
+  nfapi_nr_dl_tti_pdcch_pdu_rel15_t ***pdcch = (nfapi_nr_dl_tti_pdcch_pdu_rel15_t ***)gNB->pdcch_pdu_idx[CC_idP];
   nfapi_nr_ul_tti_request_t    *future_ul_tti_req =
       &gNB->UL_tti_req_ahead[CC_idP][(slotP + num_slots - 1) % num_slots];
   nfapi_nr_ul_dci_request_t    *UL_dci_req = &gNB->UL_dci_req[0];
@@ -103,6 +111,7 @@ void clear_nr_nfapi_information(gNB_MAC_INST * gNB,
     DL_req[CC_idP].dl_tti_request_body.nPDUs             = 0;
     DL_req[CC_idP].dl_tti_request_body.nGroup            = 0;
     //DL_req[CC_idP].dl_tti_request_body.transmission_power_pcfich           = 6000;
+    memset(pdcch, 0, sizeof(**pdcch) * MAX_NUM_BWP * MAX_NUM_CORESET);
 
     UL_dci_req[CC_idP].SFN                         = frameP;
     UL_dci_req[CC_idP].Slot                        = slotP;
@@ -299,11 +308,9 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP,
  
   int nb_periods_per_frame;
 
-  const int UE_id = 0;
   const int bwp_id = 1;
 
   gNB_MAC_INST *gNB = RC.nrmac[module_idP];
-  NR_UE_info_t *UE_info = &gNB->UE_info;
   NR_COMMON_channels_t *cc = gNB->common_channels;
   NR_ServingCellConfigCommon_t        *scc     = cc->ServingCellConfigCommon;
   NR_TDD_UL_DL_Pattern_t *tdd_pattern = &scc->tdd_UL_DL_ConfigurationCommon->pattern1;
@@ -360,23 +367,27 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP,
     nr_rrc_trigger(&ctxt, 0 /*CC_id*/, frame, slot >> *scc->ssbSubcarrierSpacing);
   }
 
-  const uint64_t dlsch_in_slot_bitmap = (1 << 1) | (1 << 3);
-  const uint64_t ulsch_in_slot_bitmap = (1 << 8);
+#define BIT(x) (1 << (x))
+  const uint64_t dlsch_in_slot_bitmap = BIT( 1) | BIT( 2) | BIT( 3) | BIT( 4) | BIT( 5) | BIT( 6)
+                                      | BIT(11) | BIT(12) | BIT(13) | BIT(14) | BIT(15) | BIT(16);
+  const uint64_t ulsch_in_slot_bitmap = BIT( 8) | BIT(18);
 
   memset(RC.nrmac[module_idP]->cce_list[bwp_id][0],0,MAX_NUM_CCE*sizeof(int)); // coreset0
   memset(RC.nrmac[module_idP]->cce_list[bwp_id][1],0,MAX_NUM_CCE*sizeof(int)); // coresetid 1
-  for (int i=0; i<MAX_NUM_CORESET; i++)
-    RC.nrmac[module_idP]->UE_info.num_pdcch_cand[UE_id][i] = 0;
+  NR_UE_info_t *UE_info = &RC.nrmac[module_idP]->UE_info;
+  for (int UE_id = UE_info->list.head; UE_id >= 0; UE_id = UE_info->list.next[UE_id])
+    for (int i=0; i<MAX_NUM_CORESET; i++)
+      UE_info->num_pdcch_cand[UE_id][i] = 0;
   for (int CC_id = 0; CC_id < MAX_NUM_CCs; CC_id++) {
     //mbsfn_status[CC_id] = 0;
 
     // clear vrb_maps
-    memset(cc[CC_id].vrb_map, 0, sizeof(uint16_t) * 275);
+    memset(cc[CC_id].vrb_map, 0, sizeof(uint16_t) * MAX_BWP_SIZE);
     // clear last scheduled slot's content (only)!
     const int num_slots = nr_slots_per_frame[*scc->ssbSubcarrierSpacing];
     const int last_slot = (slot + num_slots - 1) % num_slots;
     uint16_t *vrb_map_UL = cc[CC_id].vrb_map_UL;
-    memset(&vrb_map_UL[last_slot * 275], 0, sizeof(uint16_t) * 275);
+    memset(&vrb_map_UL[last_slot * MAX_BWP_SIZE], 0, sizeof(uint16_t) * MAX_BWP_SIZE);
 
     clear_nr_nfapi_information(RC.nrmac[module_idP], CC_id, frame, slot);
   }
@@ -410,9 +421,9 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP,
   // This schedule SR
   // TODO
 
-  // This schedule CSI measurement reporting
-  if (UE_info->active[UE_id])
-    nr_csi_meas_reporting(module_idP, UE_id, frame, slot, num_slots_per_tdd, nr_ulmix_slots, nr_slots_per_frame[*scc->ssbSubcarrierSpacing]);
+  // Schedule CSI measurement reporting: check in slot 0 for the whole frame
+  if (slot == 0)
+    nr_csi_meas_reporting(module_idP, frame, slot);
 
   // This schedule RA procedure if not in phy_test mode
   // Otherwise already consider 5G already connected
@@ -421,19 +432,16 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP,
   }
 
   // This schedules the DCI for Uplink and subsequently PUSCH
-  if (slot < 10) {
+  {
     nr_schedule_ulsch(module_idP, frame, slot, num_slots_per_tdd, nr_ulmix_slots, ulsch_in_slot_bitmap);
   }
 
   // This schedules the DCI for Downlink and PDSCH
-  if (is_xlsch_in_slot(dlsch_in_slot_bitmap, slot % num_slots_per_tdd)
-      && slot < 10) {
-    nr_schedule_ue_spec(module_idP, frame, slot, num_slots_per_tdd);
-  }
+  if (is_xlsch_in_slot(dlsch_in_slot_bitmap, slot))
+    nr_schedule_ue_spec(module_idP, frame, slot);
 
 
-  if (UE_info->active[UE_id])
-    nr_schedule_pucch(module_idP, UE_id, nr_ulmix_slots, frame, slot);
+  nr_schedule_pucch(module_idP, frame, slot);
 
   stop_meas(&RC.nrmac[module_idP]->eNB_scheduler);
   
diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c
index 09eff5c056ab8cd2a17482e548c18df89e35b2e7..b914e80d23bc04ebd13e8ba268bc9df1dc9633b2 100644
--- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c
+++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c
@@ -645,7 +645,7 @@ void nr_add_msg3(module_id_t module_idP, int CC_id, frame_t frameP, sub_frame_t
   }
 
   uint16_t *vrb_map_UL =
-      &RC.nrmac[module_idP]->common_channels[CC_id].vrb_map_UL[ra->Msg3_slot * 275];
+      &RC.nrmac[module_idP]->common_channels[CC_id].vrb_map_UL[ra->Msg3_slot * MAX_BWP_SIZE];
   for (int i = 0; i < ra->msg3_nb_rb; ++i) {
     AssertFatal(!vrb_map_UL[i + ra->msg3_first_rb],
                 "RB %d in %4d.%2d is already taken, cannot allocate Msg3!\n",
@@ -766,7 +766,7 @@ void nr_generate_Msg2(module_id_t module_idP,
                       sub_frame_t slotP)
 {
 
-  int dci_formats[2], rnti_types[2], mcsIndex;
+  int mcsIndex;
   int startSymbolAndLength = 0, StartSymbolIndex = -1, NrOfSymbols = 14, StartSymbolIndex_tmp, NrOfSymbols_tmp, x_Overhead, time_domain_assignment = 0;
   gNB_MAC_INST                      *nr_mac = RC.nrmac[module_idP];
   NR_COMMON_channels_t                  *cc = &nr_mac->common_channels[CC_id];
@@ -793,27 +793,55 @@ void nr_generate_Msg2(module_id_t module_idP,
   if ((ra->Msg2_frame == frameP) && (ra->Msg2_slot == slotP)) {
 
     nfapi_nr_dl_tti_request_body_t *dl_req = &nr_mac->DL_req[CC_id].dl_tti_request_body;
+    // Checking if the DCI allocation is feasible in current subframe
+    if (dl_req->nPDUs > NFAPI_NR_MAX_DL_TTI_PDUS - 2) {
+      LOG_I(MAC, "[RAPROC] Subframe %d: FAPI DL structure is full, skip scheduling UE %d\n", slotP, RA_rnti);
+      return;
+    }
+
+    uint8_t nr_of_candidates, aggregation_level;
+    find_aggregation_candidates(&aggregation_level, &nr_of_candidates, ss);
+    NR_BWP_Downlink_t *bwp = ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[ra->bwp_id - 1];
+    NR_ControlResourceSet_t *coreset = get_coreset(bwp, ss, 0 /* common */);
+    int CCEIndex = allocate_nr_CCEs(nr_mac,
+                                    bwp,
+                                    coreset,
+                                    aggregation_level,
+                                    0, // Y
+                                    0, // m
+                                    nr_of_candidates);
+
+    if (CCEIndex < 0) {
+      LOG_E(MAC, "%s(): cannot find free CCE for RA RNTI %04x!\n", __func__, ra->rnti);
+      return;
+    }
+
     nfapi_nr_pdu_t *tx_req = &nr_mac->TX_req[CC_id].pdu_list[nr_mac->TX_req[CC_id].Number_of_PDUs];
 
-    nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdcch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs];
-    memset((void*)dl_tti_pdcch_pdu,0,sizeof(nfapi_nr_dl_tti_request_pdu_t));
-    dl_tti_pdcch_pdu->PDUType = NFAPI_NR_DL_TTI_PDCCH_PDU_TYPE;
-    dl_tti_pdcch_pdu->PDUSize = (uint8_t)(2+sizeof(nfapi_nr_dl_tti_pdcch_pdu));
+    /* look up the PDCCH PDU for this CC, BWP, and CORESET. If it does not
+     * exist, create it. This is especially important if we have multiple RAs,
+     * and the DLSCH has to reuse them, so we need to mark them */
+    const int bwpid = bwp->bwp_Id;
+    const int coresetid = coreset->controlResourceSetId;
+    nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15 = nr_mac->pdcch_pdu_idx[CC_id][bwpid][coresetid];
+    if (!pdcch_pdu_rel15) {
+      nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdcch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs];
+      memset(dl_tti_pdcch_pdu, 0, sizeof(nfapi_nr_dl_tti_request_pdu_t));
+      dl_tti_pdcch_pdu->PDUType = NFAPI_NR_DL_TTI_PDCCH_PDU_TYPE;
+      dl_tti_pdcch_pdu->PDUSize = (uint8_t)(2 + sizeof(nfapi_nr_dl_tti_pdcch_pdu));
+      dl_req->nPDUs += 1;
+      pdcch_pdu_rel15 = &dl_tti_pdcch_pdu->pdcch_pdu.pdcch_pdu_rel15;
+      nr_configure_pdcch(pdcch_pdu_rel15, ss, coreset, scc, bwp);
+      nr_mac->pdcch_pdu_idx[CC_id][bwpid][coresetid] = pdcch_pdu_rel15;
+    }
 
-    nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdsch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs+1];
+    nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdsch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs];
     memset((void *)dl_tti_pdsch_pdu,0,sizeof(nfapi_nr_dl_tti_request_pdu_t));
     dl_tti_pdsch_pdu->PDUType = NFAPI_NR_DL_TTI_PDSCH_PDU_TYPE;
     dl_tti_pdsch_pdu->PDUSize = (uint8_t)(2+sizeof(nfapi_nr_dl_tti_pdsch_pdu));
-
-    nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15 = &dl_tti_pdcch_pdu->pdcch_pdu.pdcch_pdu_rel15;
+    dl_req->nPDUs+=1;
     nfapi_nr_dl_tti_pdsch_pdu_rel15_t *pdsch_pdu_rel15 = &dl_tti_pdsch_pdu->pdsch_pdu.pdsch_pdu_rel15;
 
-    // Checking if the DCI allocation is feasible in current subframe
-    if (dl_req->nPDUs == NFAPI_NR_MAX_DL_TTI_PDUS) {
-      LOG_I(MAC, "[RAPROC] Subframe %d: FAPI DL structure is full, skip scheduling UE %d\n", slotP, RA_rnti);
-      return;
-    }
-
     LOG_I(MAC,"[gNB %d] [RAPROC] CC_id %d Frame %d, slotP %d: Generating RAR DCI, state %d\n", module_idP, CC_id, frameP, slotP, ra->state);
 
     // This code from this point on will not work on initialBWP or CORESET0
@@ -824,7 +852,6 @@ void nr_generate_Msg2(module_id_t module_idP,
                 ra->crnti);
     AssertFatal(ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count == 1,
       "downlinkBWP_ToAddModList has %d BWP!\n", ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count);
-    NR_BWP_Downlink_t *bwp = ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[ra->bwp_id - 1];
     NR_BWP_Uplink_t *ubwp=ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[ra->bwp_id-1];
 
     LOG_I(MAC, "[RAPROC] Scheduling common search space DCI type 1 dlBWP BW %d\n", dci10_bw);
@@ -837,7 +864,11 @@ void nr_generate_Msg2(module_id_t module_idP,
 
     pdsch_pdu_rel15->pduBitmap = 0;
     pdsch_pdu_rel15->rnti = RA_rnti;
-    pdsch_pdu_rel15->pduIndex = 0;
+    /* SCF222: PDU index incremented for each PDSCH PDU sent in TX control
+     * message. This is used to associate control information to data and is
+     * reset every slot. */
+    const int pduindex = nr_mac->pdu_index[CC_id]++;
+    pdsch_pdu_rel15->pduIndex = pduindex;
 
 
     pdsch_pdu_rel15->BWPSize  = NRRIV2BW(bwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE);
@@ -887,65 +918,61 @@ void nr_generate_Msg2(module_id_t module_idP,
     pdsch_pdu_rel15->NrOfSymbols      = NrOfSymbols;
     pdsch_pdu_rel15->dlDmrsSymbPos = fill_dmrs_mask(NULL, scc->dmrs_TypeA_Position, NrOfSymbols);
 
-    dci_pdu_rel15_t dci_pdu_rel15[MAX_DCI_CORESET];
-    dci_pdu_rel15[0].frequency_domain_assignment.val = PRBalloc_to_locationandbandwidth0(pdsch_pdu_rel15->rbSize,
+    /* Fill PDCCH DL DCI PDU */
+    nfapi_nr_dl_dci_pdu_t *dci_pdu = &pdcch_pdu_rel15->dci_pdu[pdcch_pdu_rel15->numDlDci];
+    pdcch_pdu_rel15->numDlDci++;
+    dci_pdu->RNTI = RA_rnti;
+    /* TODO: remove next line */
+    AssertFatal(ss->searchSpaceType->present != NR_SearchSpace__searchSpaceType_PR_ue_Specific,
+                "shouldn't the RA SS be common?\n");
+    dci_pdu->ScramblingId = *scc->physCellId;
+    dci_pdu->ScramblingRNTI = 0;
+    dci_pdu->AggregationLevel = aggregation_level;
+    dci_pdu->CceIndex = CCEIndex;
+    dci_pdu->beta_PDCCH_1_0 = 0;
+    dci_pdu->powerControlOffsetSS = 1;
+
+    dci_pdu_rel15_t dci_payload;
+    dci_payload.frequency_domain_assignment.val = PRBalloc_to_locationandbandwidth0(pdsch_pdu_rel15->rbSize,
 										     pdsch_pdu_rel15->rbStart,dci10_bw);
-    dci_pdu_rel15[0].time_domain_assignment.val = time_domain_assignment;
-    dci_pdu_rel15[0].vrb_to_prb_mapping.val = 0;
-    dci_pdu_rel15[0].mcs = pdsch_pdu_rel15->mcsIndex[0];
-    dci_pdu_rel15[0].tb_scaling = 0;
-
-    LOG_I(MAC, "[RAPROC] DCI type 1 payload: freq_alloc %d (%d,%d,%d), time_alloc %d, vrb to prb %d, mcs %d tb_scaling %d \n",
-	  dci_pdu_rel15[0].frequency_domain_assignment.val,
-	  pdsch_pdu_rel15->rbStart,
-	  pdsch_pdu_rel15->rbSize,
-	  dci10_bw,
-	  dci_pdu_rel15[0].time_domain_assignment.val,
-	  dci_pdu_rel15[0].vrb_to_prb_mapping.val,
-	  dci_pdu_rel15[0].mcs,
-	  dci_pdu_rel15[0].tb_scaling);
-
-    uint8_t nr_of_candidates, aggregation_level;
-    find_aggregation_candidates(&aggregation_level, &nr_of_candidates, ss);
-    NR_ControlResourceSet_t *coreset = get_coreset(bwp, ss, 0 /* common */);
-    int CCEIndex = allocate_nr_CCEs(nr_mac,
-                                    bwp,
-                                    coreset,
-                                    aggregation_level,
-                                    0, // Y
-                                    0, // m
-                                    nr_of_candidates);
-
-    if (CCEIndex < 0) {
-      LOG_E(MAC, "%s(): cannot find free CCE for RA RNTI %04x!\n", __func__, ra->rnti);
-      return;
-    }
-    nr_configure_pdcch(nr_mac,
-                       pdcch_pdu_rel15,
-                       RA_rnti,
-                       ss,
-                       coreset,
-                       scc,
-                       bwp,
-                       aggregation_level,
-                       CCEIndex);
+    dci_payload.time_domain_assignment.val = time_domain_assignment;
+    dci_payload.vrb_to_prb_mapping.val = 0;
+    dci_payload.mcs = pdsch_pdu_rel15->mcsIndex[0];
+    dci_payload.tb_scaling = 0;
+
+    LOG_I(MAC,
+          "[RAPROC] DCI type 1 payload: freq_alloc %d (%d,%d,%d), time_alloc %d, vrb to prb %d, mcs %d tb_scaling %d \n",
+          dci_payload.frequency_domain_assignment.val,
+          pdsch_pdu_rel15->rbStart,
+          pdsch_pdu_rel15->rbSize,
+          dci10_bw,
+          dci_payload.time_domain_assignment.val,
+          dci_payload.vrb_to_prb_mapping.val,
+          dci_payload.mcs,
+          dci_payload.tb_scaling);
 
     LOG_I(MAC, "Frame %d: Subframe %d : Adding common DL DCI for RA_RNTI %x\n", frameP, slotP, RA_rnti);
 
-    dci_formats[0] = NR_DL_DCI_FORMAT_1_0;
-    rnti_types[0] = NR_RNTI_RA;
-
-    LOG_I(MAC, "[RAPROC] DCI params: rnti %d, rnti_type %d, dci_format %d coreset params: FreqDomainResource %llx, start_symbol %d  n_symb %d\n",
-      pdcch_pdu_rel15->dci_pdu.RNTI[0],
-      rnti_types[0],
-      dci_formats[0],
-      (unsigned long long)pdcch_pdu_rel15->FreqDomainResource,
-      pdcch_pdu_rel15->StartSymbolIndex,
-      pdcch_pdu_rel15->DurationSymbols);
-
-    fill_dci_pdu_rel15(scc,ra->secondaryCellGroup,pdcch_pdu_rel15, &dci_pdu_rel15[0], dci_formats, rnti_types,dci10_bw,ra->bwp_id);
-
-    dl_req->nPDUs+=2;
+    const int dci_format = NR_DL_DCI_FORMAT_1_0;
+    const int rnti_type = NR_RNTI_RA;
+
+    LOG_I(MAC,
+          "[RAPROC] DCI params: rnti %d, rnti_type %d, dci_format %d coreset params: FreqDomainResource %llx, start_symbol %d  n_symb %d\n",
+          pdcch_pdu_rel15->dci_pdu[0].RNTI,
+          rnti_type,
+          dci_format,
+          (unsigned long long)pdcch_pdu_rel15->FreqDomainResource,
+          pdcch_pdu_rel15->StartSymbolIndex,
+          pdcch_pdu_rel15->DurationSymbols);
+
+    fill_dci_pdu_rel15(scc,
+                       ra->secondaryCellGroup,
+                       dci_pdu,
+                       &dci_payload,
+                       dci_format,
+                       rnti_type,
+                       dci10_bw,
+                       ra->bwp_id);
 
     // Program UL processing for Msg3
     nr_get_Msg3alloc(module_idP, CC_id, scc, ubwp, slotP, frameP, ra);
@@ -955,11 +982,11 @@ void nr_generate_Msg2(module_id_t module_idP,
     LOG_I(MAC,"[gNB %d][RAPROC] Frame %d, Subframe %d: RA state %d\n", module_idP, frameP, slotP, ra->state);
 
     x_Overhead = 0;
-    nr_get_tbs_dl(&dl_tti_pdsch_pdu->pdsch_pdu, x_Overhead, pdsch_pdu_rel15->numDmrsCdmGrpsNoData, dci_pdu_rel15[0].tb_scaling);
+    nr_get_tbs_dl(&dl_tti_pdsch_pdu->pdsch_pdu, x_Overhead, pdsch_pdu_rel15->numDmrsCdmGrpsNoData, dci_payload.tb_scaling);
 
     // DL TX request
     tx_req->PDU_length = pdsch_pdu_rel15->TBSize[0];
-    tx_req->PDU_index = nr_mac->pdu_index[CC_id]++;
+    tx_req->PDU_index = pduindex;
     tx_req->num_TLV = 1;
     tx_req->TLVs[0].length = 8;
     nr_mac->TX_req[CC_id].SFN = frameP;
diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c
index 66766535e84de0e67861d0267b243d3e5e0a4a81..1de46aaea4504707b4f1305c78c165cb76aadff6 100644
--- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c
+++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c
@@ -126,7 +126,12 @@ void schedule_nr_mib(module_id_t module_idP, frame_t frameP, sub_frame_t slotP,
               mib_sdu_length);
 
         if ((frameP & 1023) < 80){
-          LOG_I(MAC,"[gNB %d] Frame %d : MIB->BCH  CC_id %d, Received %d bytes\n",module_idP, frameP, CC_id, mib_sdu_length);
+          LOG_D(MAC,
+                "[gNB %d] Frame %d : MIB->BCH  CC_id %d, Received %d bytes\n",
+                module_idP,
+                frameP,
+                CC_id,
+                mib_sdu_length);
         }
 
         dl_config_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs];
@@ -331,13 +336,19 @@ void nr_fill_nfapi_dl_sib1_pdu(int Mod_idP,
   memset((void*)dl_tti_pdcch_pdu,0,sizeof(nfapi_nr_dl_tti_request_pdu_t));
   dl_tti_pdcch_pdu->PDUType = NFAPI_NR_DL_TTI_PDCCH_PDU_TYPE;
   dl_tti_pdcch_pdu->PDUSize = (uint8_t)(2+sizeof(nfapi_nr_dl_tti_pdcch_pdu));
+  dl_req->nPDUs += 1;
+  nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15 = &dl_tti_pdcch_pdu->pdcch_pdu.pdcch_pdu_rel15;
+  nr_configure_pdcch(pdcch_pdu_rel15,
+                     gNB_mac->sched_ctrlCommon->search_space,
+                     gNB_mac->sched_ctrlCommon->coreset,
+                     scc,
+                     bwp);
 
-  nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdsch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs+1];
+  nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdsch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs];
   memset((void*)dl_tti_pdsch_pdu,0,sizeof(nfapi_nr_dl_tti_request_pdu_t));
   dl_tti_pdsch_pdu->PDUType = NFAPI_NR_DL_TTI_PDSCH_PDU_TYPE;
   dl_tti_pdsch_pdu->PDUSize = (uint8_t)(2+sizeof(nfapi_nr_dl_tti_pdsch_pdu));
-
-  nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15 = &dl_tti_pdcch_pdu->pdcch_pdu.pdcch_pdu_rel15;
+  dl_req->nPDUs += 1;
   nfapi_nr_dl_tti_pdsch_pdu_rel15_t *pdsch_pdu_rel15 = &dl_tti_pdsch_pdu->pdsch_pdu.pdsch_pdu_rel15;
 
   pdcch_pdu_rel15->CoreSetType = NFAPI_NR_CSET_CONFIG_MIB_SIB1;
@@ -385,48 +396,50 @@ void nr_fill_nfapi_dl_sib1_pdu(int Mod_idP,
 
   LOG_D(MAC,"dlDmrsSymbPos = 0x%x\n", pdsch_pdu_rel15->dlDmrsSymbPos);
 
-  dci_pdu_rel15_t dci_pdu_rel15[MAX_DCI_CORESET];
-  memset(dci_pdu_rel15, 0, sizeof(dci_pdu_rel15_t) * MAX_DCI_CORESET);
+  /* Fill PDCCH DL DCI PDU */
+  nfapi_nr_dl_dci_pdu_t *dci_pdu = &pdcch_pdu_rel15->dci_pdu[pdcch_pdu_rel15->numDlDci];
+  pdcch_pdu_rel15->numDlDci++;
+  dci_pdu->RNTI = SI_RNTI;
+  dci_pdu->ScramblingId = *scc->physCellId;
+  dci_pdu->ScramblingRNTI = 0;
+  dci_pdu->AggregationLevel = gNB_mac->sched_ctrlCommon->aggregation_level;
+  dci_pdu->CceIndex = gNB_mac->sched_ctrlCommon->cce_index;
+  dci_pdu->beta_PDCCH_1_0 = 0;
+  dci_pdu->powerControlOffsetSS = 1;
 
-  dci_pdu_rel15[0].bwp_indicator.val = gNB_mac->sched_ctrlCommon->active_bwp->bwp_Id;
+  /* DCI payload */
+  dci_pdu_rel15_t dci_payload;
+  memset(&dci_payload, 0, sizeof(dci_pdu_rel15_t));
 
-  // frequency domain assignment
-  dci_pdu_rel15[0].frequency_domain_assignment.val =
-      PRBalloc_to_locationandbandwidth0(pdsch_pdu_rel15->rbSize,
-                                        pdsch_pdu_rel15->rbStart,
-                                        gNB_mac->type0_PDCCH_CSS_config.num_rbs);
-
-  dci_pdu_rel15[0].time_domain_assignment.val = gNB_mac->sched_ctrlCommon->time_domain_allocation;
-  dci_pdu_rel15[0].mcs = gNB_mac->sched_ctrlCommon->mcs;
-  dci_pdu_rel15[0].rv = pdsch_pdu_rel15->rvIndex[0];
-  dci_pdu_rel15[0].harq_pid = 0;
-  dci_pdu_rel15[0].ndi = 0;
-  dci_pdu_rel15[0].dai[0].val = 0;
-  dci_pdu_rel15[0].tpc = 0; // table 7.2.1-1 in 38.213
-  dci_pdu_rel15[0].pucch_resource_indicator = 0;
-  dci_pdu_rel15[0].pdsch_to_harq_feedback_timing_indicator.val = 0;
-  dci_pdu_rel15[0].antenna_ports.val = 0;
-  dci_pdu_rel15[0].dmrs_sequence_initialization.val = pdsch_pdu_rel15->SCID;
-
-  nr_configure_pdcch(gNB_mac,
-                     pdcch_pdu_rel15,
-                     SI_RNTI,
-                     gNB_mac->sched_ctrlCommon->search_space,
-                     gNB_mac->sched_ctrlCommon->coreset,
-                     scc,
-                     bwp,
-                     gNB_mac->sched_ctrlCommon->aggregation_level,
-                     gNB_mac->sched_ctrlCommon->cce_index);
-
-  int dci_formats[2];
-  int rnti_types[2];
-
-  dci_formats[0]  = NR_DL_DCI_FORMAT_1_0;
-  rnti_types[0]   = NR_RNTI_SI;
+  dci_payload.bwp_indicator.val = gNB_mac->sched_ctrlCommon->active_bwp->bwp_Id;
 
-  fill_dci_pdu_rel15(scc,secondaryCellGroup,pdcch_pdu_rel15,dci_pdu_rel15,dci_formats,rnti_types,pdsch_pdu_rel15->BWPSize,gNB_mac->sched_ctrlCommon->active_bwp->bwp_Id);
-
-  dl_req->nPDUs += 2;
+  // frequency domain assignment
+  dci_payload.frequency_domain_assignment.val = PRBalloc_to_locationandbandwidth0(
+      pdsch_pdu_rel15->rbSize, pdsch_pdu_rel15->rbStart, gNB_mac->type0_PDCCH_CSS_config.num_rbs);
+
+  dci_payload.time_domain_assignment.val = gNB_mac->sched_ctrlCommon->time_domain_allocation;
+  dci_payload.mcs = gNB_mac->sched_ctrlCommon->mcs;
+  dci_payload.rv = pdsch_pdu_rel15->rvIndex[0];
+  dci_payload.harq_pid = 0;
+  dci_payload.ndi = 0;
+  dci_payload.dai[0].val = 0;
+  dci_payload.tpc = 0; // table 7.2.1-1 in 38.213
+  dci_payload.pucch_resource_indicator = 0;
+  dci_payload.pdsch_to_harq_feedback_timing_indicator.val = 0;
+  dci_payload.antenna_ports.val = 0;
+  dci_payload.dmrs_sequence_initialization.val = pdsch_pdu_rel15->SCID;
+
+  int dci_format = NR_DL_DCI_FORMAT_1_0;
+  int rnti_type = NR_RNTI_SI;
+
+  fill_dci_pdu_rel15(scc,
+                     secondaryCellGroup,
+                     &pdcch_pdu_rel15->dci_pdu[pdcch_pdu_rel15->numDlDci - 1],
+                     &dci_payload,
+                     dci_format,
+                     rnti_type,
+                     pdsch_pdu_rel15->BWPSize,
+                     gNB_mac->sched_ctrlCommon->active_bwp->bwp_Id);
 
   LOG_D(MAC,"BWPSize: %i\n", pdcch_pdu_rel15->BWPSize);
   LOG_D(MAC,"BWPStart: %i\n", pdcch_pdu_rel15->BWPStart);
@@ -504,4 +517,4 @@ void schedule_nr_sib1(module_id_t module_idP, frame_t frameP, sub_frame_t slotP)
     gNB_mac->TX_req[CC_id].SFN = frameP;
     gNB_mac->TX_req[CC_id].Slot = slotP;
   }
-}
\ No newline at end of file
+}
diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c
index 4110af07edaef19f2cea0a4ee773352e5bb4d2bc..4d491b28434edd5c2f649e4ad1d9cef42b56b03f 100644
--- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c
+++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c
@@ -56,27 +56,22 @@
 #define WORD 32
 //#define SIZE_OF_POINTER sizeof (void *)
 
-int nr_generate_dlsch_pdu(module_id_t module_idP,
-                          NR_UE_sched_ctrl_t *ue_sched_ctl,
-                          unsigned char *sdus_payload,
+// Compute and write all MAC CEs and subheaders, and return number of written
+// bytes
+int nr_write_ce_dlsch_pdu(module_id_t module_idP,
+                          const NR_UE_sched_ctrl_t *ue_sched_ctl,
                           unsigned char *mac_pdu,
-                          unsigned char num_sdus,
-                          unsigned short *sdu_lengths,
-                          unsigned char *sdu_lcids,
                           unsigned char drx_cmd,
-                          unsigned char *ue_cont_res_id,
-                          unsigned short post_padding) {
+                          unsigned char *ue_cont_res_id)
+{
   gNB_MAC_INST *gNB = RC.nrmac[module_idP];
   NR_MAC_SUBHEADER_FIXED *mac_pdu_ptr = (NR_MAC_SUBHEADER_FIXED *) mac_pdu;
-  unsigned char *dlsch_buffer_ptr = sdus_payload;
   uint8_t last_size = 0;
   int offset = 0, mac_ce_size, i, timing_advance_cmd, tag_id = 0;
   // MAC CEs
   uint8_t mac_header_control_elements[16], *ce_ptr;
   ce_ptr = &mac_header_control_elements[0];
 
-  // 1) Compute MAC CE and related subheaders
-
   // DRX command subheader (MAC CE size 0)
   if (drx_cmd != 255) {
     mac_pdu_ptr->R = 0;
@@ -301,42 +296,6 @@ int nr_generate_dlsch_pdu(module_id_t module_idP,
     }
   }
 
-  // 2) Generation of DLSCH MAC subPDUs including subheaders and MAC SDUs
-  for (i = 0; i < num_sdus; i++) {
-
-    if (sdu_lengths[i] < 256) {
-      ((NR_MAC_SUBHEADER_SHORT *) mac_pdu_ptr)->R = 0;
-      ((NR_MAC_SUBHEADER_SHORT *) mac_pdu_ptr)->F = 0;
-      ((NR_MAC_SUBHEADER_SHORT *) mac_pdu_ptr)->LCID = sdu_lcids[i];
-      ((NR_MAC_SUBHEADER_SHORT *) mac_pdu_ptr)->L = (unsigned char) sdu_lengths[i];
-      last_size = 2;
-    } else {
-      ((NR_MAC_SUBHEADER_LONG *) mac_pdu_ptr)->R = 0;
-      ((NR_MAC_SUBHEADER_LONG *) mac_pdu_ptr)->F = 1;
-      ((NR_MAC_SUBHEADER_LONG *) mac_pdu_ptr)->LCID = sdu_lcids[i];
-      ((NR_MAC_SUBHEADER_LONG *) mac_pdu_ptr)->L1 = ((unsigned short) sdu_lengths[i] >> 8) & 0xff;
-      ((NR_MAC_SUBHEADER_LONG *) mac_pdu_ptr)->L2 = (unsigned short) sdu_lengths[i] & 0xff;
-      last_size = 3;
-    }
-
-    mac_pdu_ptr += last_size;
-    // 3) cycle through SDUs, compute each relevant and place dlsch_buffer in
-    memcpy((void *) mac_pdu_ptr, (void *) dlsch_buffer_ptr, sdu_lengths[i]);
-    dlsch_buffer_ptr += sdu_lengths[i];
-    mac_pdu_ptr += sdu_lengths[i];
-    LOG_D(MAC, "Generate DLSCH header num sdu %d len header %d len sdu %d -> offset %ld\n", num_sdus, last_size, sdu_lengths[i], (unsigned char *)mac_pdu_ptr - mac_pdu);
-  }
-
-  // 4) Compute final offset for padding
-  if (post_padding > 0) {
-    ((NR_MAC_SUBHEADER_FIXED *) mac_pdu_ptr)->R = 0;
-    ((NR_MAC_SUBHEADER_FIXED *) mac_pdu_ptr)->LCID = DL_SCH_LCID_PADDING;
-    mac_pdu_ptr++;
-    LOG_D(MAC, "Generate Padding -> offset %ld\n", (unsigned char *)mac_pdu_ptr - mac_pdu);
-  } else {
-    // no MAC subPDU with padding
-  }
-
   // compute final offset
   offset = ((unsigned char *) mac_pdu_ptr - mac_pdu);
   //printf("Offset %d \n", ((unsigned char *) mac_pdu_ptr - mac_pdu));
@@ -372,123 +331,236 @@ uint8_t getN_PRB_DMRS(NR_BWP_Downlink_t *bwp, int numDmrsCdmGrpsNoData) {
   }
 }
 
-void nr_simple_dlsch_preprocessor(module_id_t module_id,
-                                  frame_t frame,
-                                  sub_frame_t slot,
-                                  int num_slots_per_tdd) {
-  NR_UE_info_t *UE_info = &RC.nrmac[module_id]->UE_info;
+void nr_store_dlsch_buffer(module_id_t module_id,
+                           frame_t frame,
+                           sub_frame_t slot) {
 
-  AssertFatal(UE_info->num_UEs <= 1,
-              "%s() cannot handle more than one UE, but found %d\n",
-              __func__,
-              UE_info->num_UEs);
-  if (UE_info->num_UEs == 0)
-    return;
+  NR_UE_info_t *UE_info = &RC.nrmac[module_id]->UE_info;
 
-  const int UE_id = 0;
-  const int CC_id = 0;
+  for (int UE_id = UE_info->list.head; UE_id >= 0; UE_id = UE_info->list.next[UE_id]) {
+    NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id];
 
-  NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id];
+    sched_ctrl->num_total_bytes = 0;
+    const int lcid = DL_SCH_LCID_DTCH;
+    const uint16_t rnti = UE_info->rnti[UE_id];
+    sched_ctrl->rlc_status[lcid] = mac_rlc_status_ind(module_id,
+                                                      rnti,
+                                                      module_id,
+                                                      frame,
+                                                      slot,
+                                                      ENB_FLAG_YES,
+                                                      MBMS_FLAG_NO,
+                                                      lcid,
+                                                      0,
+                                                      0);
+    sched_ctrl->num_total_bytes += sched_ctrl->rlc_status[lcid].bytes_in_buffer;
+    LOG_D(MAC,
+          "[%s][%d.%d], DTCH%d->DLSCH, RLC status %d bytes TA %d\n",
+          __func__,
+          frame,
+          slot,
+          lcid,
+          sched_ctrl->rlc_status[lcid].bytes_in_buffer,
+          sched_ctrl->ta_apply);
+  }
+}
 
-  /* Retrieve amount of data to send for this UE */
-  sched_ctrl->num_total_bytes = 0;
-  const int lcid = DL_SCH_LCID_DTCH;
-  const rnti_t rnti = UE_info->rnti[UE_id];
-  sched_ctrl->rlc_status[lcid] = mac_rlc_status_ind(module_id,
-                                                    rnti,
-                                                    module_id,
-                                                    frame,
-                                                    slot,
-                                                    ENB_FLAG_YES,
-                                                    MBMS_FLAG_NO,
-                                                    lcid,
-                                                    0,
-                                                    0);
-  sched_ctrl->num_total_bytes += sched_ctrl->rlc_status[lcid].bytes_in_buffer;
-  if (sched_ctrl->num_total_bytes == 0
-      && !sched_ctrl->ta_apply) /* If TA should be applied, give at least one RB */
-    return;
+bool allocate_retransmission(module_id_t module_id,
+                             uint8_t *rballoc_mask,
+                             int *n_rb_sched,
+                             int UE_id,
+                             int current_harq_pid){
+  NR_UE_sched_ctrl_t *sched_ctrl = &RC.nrmac[module_id]->UE_info.UE_sched_ctrl[UE_id];
+  NR_UE_ret_info_t *retInfo = &sched_ctrl->retInfo[current_harq_pid];
+  const uint16_t bwpSize = NRRIV2BW(sched_ctrl->active_bwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE);
+  int rbStart = NRRIV2PRBOFFSET(sched_ctrl->active_bwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE);
 
-  LOG_D(MAC, "[%s][%d.%d], DTCH%d->DLSCH, RLC status %d bytes TA %d\n",
-        __FUNCTION__,
-        frame,
-        slot,
-        lcid,
-        sched_ctrl->rlc_status[lcid].bytes_in_buffer,
-        sched_ctrl->ta_apply);
-
-  /* Find a free CCE */
-  const int target_ss = NR_SearchSpace__searchSpaceType_PR_ue_Specific;
-  sched_ctrl->search_space = get_searchspace(sched_ctrl->active_bwp, target_ss);
-  uint8_t nr_of_candidates;
-  find_aggregation_candidates(&sched_ctrl->aggregation_level,
-                              &nr_of_candidates,
-                              sched_ctrl->search_space);
-  sched_ctrl->coreset = get_coreset(sched_ctrl->active_bwp, sched_ctrl->search_space, 1 /*dedicated*/);
-  int cid = sched_ctrl->coreset->controlResourceSetId;
-  const uint16_t Y = UE_info->Y[UE_id][cid][slot];
-  const int m = UE_info->num_pdcch_cand[UE_id][cid];
-  sched_ctrl->cce_index = allocate_nr_CCEs(RC.nrmac[module_id],
-                                           sched_ctrl->active_bwp,
-                                           sched_ctrl->coreset,
-                                           sched_ctrl->aggregation_level,
-                                           Y,
-                                           m,
-                                           nr_of_candidates);
-  if (sched_ctrl->cce_index < 0) {
-    LOG_E(MAC, "%s(): could not find CCE for UE %d\n", __func__, UE_id);
-    return;
+  sched_ctrl->time_domain_allocation = retInfo->time_domain_allocation;
+
+  /* ensure that there is a free place for RB allocation */
+  int rbSize = 0;
+  while (rbSize < retInfo->rbSize) {
+    rbStart += rbSize; /* last iteration rbSize was not enough, skip it */
+    rbSize = 0;
+    while (rbStart < bwpSize && !rballoc_mask[rbStart]) rbStart++;
+    if (rbStart >= bwpSize) {
+      LOG_D(MAC,
+            "cannot allocate retransmission for UE %d/RNTI %04x: no resources\n",
+            UE_id,
+            RC.nrmac[module_id]->UE_info.rnti[UE_id]);
+      return false;
+    }
+    while (rbStart + rbSize < bwpSize
+           && rballoc_mask[rbStart + rbSize]
+           && rbSize < retInfo->rbSize)
+      rbSize++;
   }
-  UE_info->num_pdcch_cand[UE_id][cid]++;
+  sched_ctrl->rbSize = retInfo->rbSize;
+  sched_ctrl->rbStart = rbStart;
 
-  /* Find PUCCH occasion */
-  nr_acknack_scheduling(module_id,
-                        UE_id,
-                        frame,
-                        slot,
-                        num_slots_per_tdd,
-                        &sched_ctrl->pucch_sched_idx,
-                        &sched_ctrl->pucch_occ_idx);
+  /* MCS etc: just reuse from previous scheduling opportunity */
+  sched_ctrl->mcsTableIdx = retInfo->mcsTableIdx;
+  sched_ctrl->mcs = retInfo->mcs;
+  sched_ctrl->numDmrsCdmGrpsNoData = retInfo->numDmrsCdmGrpsNoData;
 
-  AssertFatal(sched_ctrl->pucch_sched_idx >= 0, "no uplink slot for PUCCH found!\n");
+  /* retransmissions: directly allocate */
+  *n_rb_sched -= sched_ctrl->rbSize;
+  for (int rb = 0; rb < sched_ctrl->rbSize; rb++)
+    rballoc_mask[rb+sched_ctrl->rbStart] = 0;
+  return true;
+}
 
-  uint16_t *vrb_map = RC.nrmac[module_id]->common_channels[CC_id].vrb_map;
-  // for now HARQ PID is fixed and should be the same as in post-processor
-  const int current_harq_pid = slot % num_slots_per_tdd;
-  NR_UE_harq_t *harq = &sched_ctrl->harq_processes[current_harq_pid];
-  NR_UE_ret_info_t *retInfo = &sched_ctrl->retInfo[current_harq_pid];
-  const uint16_t bwpSize = NRRIV2BW(sched_ctrl->active_bwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE);
-  int rbStart = NRRIV2PRBOFFSET(sched_ctrl->active_bwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE);
+float thr_ue[MAX_MOBILES_PER_GNB];
 
-  if (harq->round != 0) { /* retransmission */
-    sched_ctrl->time_domain_allocation = retInfo->time_domain_allocation;
+void pf_dl(module_id_t module_id,
+           frame_t frame,
+           sub_frame_t slot,
+           NR_list_t *UE_list,
+           int n_rb_sched,
+           uint8_t *rballoc_mask,
+           int max_num_ue) {
 
-    /* ensure that there is a free place for RB allocation */
-    int rbSize = 0;
-    while (rbSize < retInfo->rbSize) {
-      rbStart += rbSize; /* last iteration rbSize was not enough, skip it */
-      rbSize = 0;
-      while (rbStart < bwpSize && vrb_map[rbStart]) rbStart++;
-      if (rbStart >= bwpSize) {
-        LOG_E(MAC,
-              "cannot allocate retransmission for UE %d/RNTI %04x: no resources\n",
+  NR_UE_info_t *UE_info = &RC.nrmac[module_id]->UE_info;
+  float coeff_ue[MAX_MOBILES_PER_GNB];
+  // UEs that could be scheduled
+  int ue_array[MAX_MOBILES_PER_GNB];
+  NR_list_t UE_sched = { .head = -1, .next = ue_array, .tail = -1, .len = MAX_MOBILES_PER_GNB };
+
+  /* Loop UE_info->list to check retransmission */
+  for (int UE_id = UE_list->head; UE_id >= 0; UE_id = UE_list->next[UE_id]) {
+    NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id];
+    sched_ctrl->search_space = get_searchspace(sched_ctrl->active_bwp, NR_SearchSpace__searchSpaceType_PR_ue_Specific);
+    sched_ctrl->coreset = get_coreset(sched_ctrl->active_bwp, sched_ctrl->search_space, 1 /* dedicated */);
+    /* get the PID of a HARQ process awaiting retrnasmission, or -1 otherwise */
+    sched_ctrl->dl_harq_pid = sched_ctrl->retrans_dl_harq.head;
+    const rnti_t rnti = UE_info->rnti[UE_id];
+
+    /* Calculate Throughput */
+    const float a = 0.0005f; // corresponds to 200ms window
+    const uint32_t b = UE_info->mac_stats[UE_id].dlsch_current_bytes;
+    thr_ue[UE_id] = (1 - a) * thr_ue[UE_id] + a * b;
+
+    /* retransmission */
+    if (sched_ctrl->dl_harq_pid >= 0) {
+      /* Find a free CCE */
+      bool freeCCE = find_free_CCE(module_id, slot, UE_id);
+      if (!freeCCE){
+        LOG_D(MAC, "%4d.%2d could not find CCE for DL DCI retransmission UE %d/RNTI %04x\n", frame, slot, UE_id, rnti);
+        continue;
+      }
+      /* Find PUCCH occasion: if it fails, undo CCE allocation (undoing PUCCH
+       * allocation after CCE alloc fail would be more complex) */
+      const bool alloc = nr_acknack_scheduling(module_id, UE_id, frame, slot);
+      if (!alloc) {
+        LOG_W(MAC,
+              "%s(): could not find PUCCH for UE %d/%04x@%d.%d\n",
+              __func__,
               UE_id,
-              rnti);
+              rnti,
+              frame,
+              slot);
+        int cid = sched_ctrl->coreset->controlResourceSetId;
+        UE_info->num_pdcch_cand[UE_id][cid]--;
+        int *cce_list = RC.nrmac[module_id]->cce_list[sched_ctrl->active_bwp->bwp_Id][cid];
+        for (int i = 0; i < sched_ctrl->aggregation_level; i++)
+          cce_list[sched_ctrl->cce_index + i] = 0;
         return;
       }
-      while (rbStart + rbSize < bwpSize
-             && !vrb_map[rbStart + rbSize]
-             && rbSize < retInfo->rbSize)
-        rbSize++;
+      /* Allocate retransmission */
+      bool r = allocate_retransmission(module_id, rballoc_mask, &n_rb_sched, UE_id, sched_ctrl->dl_harq_pid);
+      if (!r) {
+        LOG_D(MAC, "%4d.%2d retransmission can NOT be allocated\n", frame, slot);
+        continue;
+      }
+      /* reduce max_num_ue once we are sure UE can be allocated, i.e., has CCE */
+      max_num_ue--;
+      if (max_num_ue < 0) return;
+    } else {
+      /* Check DL buffer and skip this UE if no bytes and no TA necessary */
+      if (sched_ctrl->num_total_bytes == 0 && frame != (sched_ctrl->ta_frame + 10) % 1024)
+        continue;
+
+      /* Calculate coeff */
+      sched_ctrl->time_domain_allocation = 2;
+      sched_ctrl->mcsTableIdx = 0;
+      sched_ctrl->mcs = 9;
+      sched_ctrl->numDmrsCdmGrpsNoData = 1;
+      uint8_t N_PRB_DMRS =
+              getN_PRB_DMRS(sched_ctrl->active_bwp, sched_ctrl->numDmrsCdmGrpsNoData);
+      int nrOfSymbols = getNrOfSymbols(sched_ctrl->active_bwp,
+                                       sched_ctrl->time_domain_allocation);
+      uint32_t tbs = nr_compute_tbs(nr_get_Qm_dl(sched_ctrl->mcs, sched_ctrl->mcsTableIdx),
+                                    nr_get_code_rate_dl(sched_ctrl->mcs, sched_ctrl->mcsTableIdx),
+                                    1,  // rbSize
+                                    nrOfSymbols,
+                                    N_PRB_DMRS,  // FIXME // This should be multiplied by the
+                                    // number of dmrs symbols
+                                    0 /* N_PRB_oh, 0 for initialBWP */, 0 /* tb_scaling */,
+                                    1 /* nrOfLayers */)
+                     >> 3;
+      coeff_ue[UE_id] = (float) tbs / thr_ue[UE_id];
+      LOG_D(MAC,"b %d, thr_ue[%d] %f, tbs %d, coeff_ue[%d] %f\n",
+            b, UE_id, thr_ue[UE_id], tbs, UE_id, coeff_ue[UE_id]);
+      /* Create UE_sched list for UEs eligible for new transmission*/
+      add_tail_nr_list(&UE_sched, UE_id);
     }
-    sched_ctrl->rbSize = retInfo->rbSize;
-    sched_ctrl->rbStart = rbStart;
+  }
 
-    /* MCS etc: just reuse from previous scheduling opportunity */
-    sched_ctrl->mcsTableIdx = retInfo->mcsTableIdx;
-    sched_ctrl->mcs = retInfo->mcs;
-    sched_ctrl->numDmrsCdmGrpsNoData = retInfo->numDmrsCdmGrpsNoData;
-  } else {
+  /* Loop UE_sched to find max coeff and allocate transmission */
+  while (max_num_ue > 0 && n_rb_sched > 0 && UE_sched.head >= 0) {
+
+    /* Find max coeff from UE_sched*/
+    int *max = &UE_sched.head; /* assume head is max */
+    int *p = &UE_sched.next[*max];
+    while (*p >= 0) {
+      /* if the current one has larger coeff, save for later */
+      if (coeff_ue[*p] > coeff_ue[*max])
+        max = p;
+      p = &UE_sched.next[*p];
+    }
+    /* remove the max one: do not use remove_nr_list() it goes through the
+     * whole list every time. Note that UE_sched.tail might not be set
+     * correctly anymore */
+    const int UE_id = *max;
+    p = &UE_sched.next[*max];
+    *max = UE_sched.next[*max];
+    *p = -1;
+
+    NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id];
+    const uint16_t rnti = UE_info->rnti[UE_id];
+    const uint16_t bwpSize = NRRIV2BW(sched_ctrl->active_bwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE);
+    int rbStart = NRRIV2PRBOFFSET(sched_ctrl->active_bwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE);
+
+    /* Find a free CCE */
+    bool freeCCE = find_free_CCE(module_id, slot, UE_id);
+    if (!freeCCE) {
+      LOG_D(MAC, "%4d.%2d could not find CCE for DL DCI UE %d/RNTI %04x\n", frame, slot, UE_id, rnti);
+      continue;
+    }
+    /* reduce max_num_ue once we are sure UE can be allocated, i.e., has CCE */
+    max_num_ue--;
+    if (max_num_ue < 0) return;
+
+    /* Find PUCCH occasion: if it fails, undo CCE allocation (undoing PUCCH
+    * allocation after CCE alloc fail would be more complex) */
+    const bool alloc = nr_acknack_scheduling(module_id, UE_id, frame, slot);
+    if (!alloc) {
+      LOG_W(MAC,
+            "%s(): could not find PUCCH for UE %d/%04x@%d.%d\n",
+            __func__,
+            UE_id,
+            rnti,
+            frame,
+            slot);
+      int cid = sched_ctrl->coreset->controlResourceSetId;
+      UE_info->num_pdcch_cand[UE_id][cid]--;
+      int *cce_list = RC.nrmac[module_id]->cce_list[sched_ctrl->active_bwp->bwp_Id][cid];
+      for (int i = 0; i < sched_ctrl->aggregation_level; i++)
+        cce_list[sched_ctrl->cce_index + i] = 0;
+      return;
+    }
+
+    /* Allocate transmission */
     // Time-domain allocation
     sched_ctrl->time_domain_allocation = 2;
 
@@ -498,20 +570,22 @@ void nr_simple_dlsch_preprocessor(module_id_t module_id,
     sched_ctrl->numDmrsCdmGrpsNoData = 1;
 
     // Freq-demain allocation
-    while (rbStart < bwpSize && vrb_map[rbStart]) rbStart++;
+    while (rbStart < bwpSize && !rballoc_mask[rbStart]) rbStart++;
 
-    uint8_t N_PRB_DMRS =
+    const uint8_t N_PRB_DMRS =
         getN_PRB_DMRS(sched_ctrl->active_bwp, sched_ctrl->numDmrsCdmGrpsNoData);
-    int nrOfSymbols = getNrOfSymbols(sched_ctrl->active_bwp,
-                                     sched_ctrl->time_domain_allocation);
-    uint8_t N_DMRS_SLOT = get_num_dmrs_symbols(sched_ctrl->active_bwp->bwp_Dedicated->pdsch_Config->choice.setup,
-                                               RC.nrmac[module_id]->common_channels->ServingCellConfigCommon->dmrs_TypeA_Position ,
-                                               nrOfSymbols);
+    const int nrOfSymbols = getNrOfSymbols(sched_ctrl->active_bwp,
+        sched_ctrl->time_domain_allocation);
+    const NR_ServingCellConfigCommon_t *scc = RC.nrmac[module_id]->common_channels->ServingCellConfigCommon;
+    const uint8_t N_DMRS_SLOT = get_num_dmrs_symbols(
+        sched_ctrl->active_bwp->bwp_Dedicated->pdsch_Config->choice.setup,
+        scc->dmrs_TypeA_Position,
+        nrOfSymbols);
 
     int rbSize = 0;
+    uint32_t TBS = 0;
     const int oh = 2 + (sched_ctrl->num_total_bytes >= 256)
                  + 2 * (frame == (sched_ctrl->ta_frame + 10) % 1024);
-    uint32_t TBS = 0;
     do {
       rbSize++;
       TBS = nr_compute_tbs(nr_get_Qm_dl(sched_ctrl->mcs, sched_ctrl->mcsTableIdx),
@@ -523,31 +597,74 @@ void nr_simple_dlsch_preprocessor(module_id_t module_id,
                            0 /* tb_scaling */,
                            1 /* nrOfLayers */)
             >> 3;
-    } while (rbStart + rbSize < bwpSize && !vrb_map[rbStart + rbSize] && TBS < sched_ctrl->num_total_bytes + oh);
+    } while (rbStart + rbSize < bwpSize && rballoc_mask[rbStart + rbSize] && TBS < sched_ctrl->num_total_bytes + oh);
     sched_ctrl->rbSize = rbSize;
     sched_ctrl->rbStart = rbStart;
+
+    /* transmissions: directly allocate */
+    n_rb_sched -= sched_ctrl->rbSize;
+    for (int rb = 0; rb < sched_ctrl->rbSize; rb++)
+      rballoc_mask[rb+sched_ctrl->rbStart] = 0;
   }
+}
 
-  /* mark the corresponding RBs as used */
-  for (int rb = 0; rb < sched_ctrl->rbSize; rb++)
-    vrb_map[rb + sched_ctrl->rbStart] = 1;
+void nr_simple_dlsch_preprocessor(module_id_t module_id,
+                                  frame_t frame,
+                                  sub_frame_t slot) {
+  NR_UE_info_t *UE_info = &RC.nrmac[module_id]->UE_info;
+
+  if (UE_info->num_UEs == 0)
+    return;
+
+  const int CC_id = 0;
+
+
+  /* Get bwpSize from the first UE */
+  int UE_id = UE_info->list.head;
+  NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id];
+  const uint16_t bwpSize = NRRIV2BW(sched_ctrl->active_bwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE);
+
+  uint16_t *vrb_map = RC.nrmac[module_id]->common_channels[CC_id].vrb_map;
+  uint8_t rballoc_mask[bwpSize];
+  int n_rb_sched = 0;
+  for (int i = 0; i < bwpSize; i++) {
+    // calculate mask: init with "NOT" vrb_map:
+    // if any RB in vrb_map is blocked (1), the current RBG will be 0
+    rballoc_mask[i] = !vrb_map[i];
+    n_rb_sched += rballoc_mask[i];
+  }
+
+  /* Retrieve amount of data to send for this UE */
+  nr_store_dlsch_buffer(module_id, frame, slot);
+
+  /* proportional fair scheduling algorithm */
+  pf_dl(module_id,
+        frame,
+        slot,
+        &UE_info->list,
+        n_rb_sched,
+        rballoc_mask,
+        2);
 }
 
 void nr_schedule_ue_spec(module_id_t module_id,
                          frame_t frame,
-                         sub_frame_t slot,
-                         int num_slots_per_tdd) {
+                         sub_frame_t slot) {
   gNB_MAC_INST *gNB_mac = RC.nrmac[module_id];
 
   /* PREPROCESSOR */
-  gNB_mac->pre_processor_dl(module_id, frame, slot, num_slots_per_tdd);
+  gNB_mac->pre_processor_dl(module_id, frame, slot);
 
+  const int CC_id = 0;
+  NR_ServingCellConfigCommon_t *scc = gNB_mac->common_channels[CC_id].ServingCellConfigCommon;
   NR_UE_info_t *UE_info = &gNB_mac->UE_info;
 
-  const int CC_id = 0;
-  NR_UE_list_t *UE_list = &UE_info->list;
+  nfapi_nr_dl_tti_request_body_t *dl_req = &gNB_mac->DL_req[CC_id].dl_tti_request_body;
+
+  NR_list_t *UE_list = &UE_info->list;
   for (int UE_id = UE_list->head; UE_id >= 0; UE_id = UE_list->next[UE_id]) {
     NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id];
+    UE_info->mac_stats[UE_id].dlsch_current_bytes = 0;
 
     /* update TA and set ta_apply every 10 frames.
      * Possible improvement: take the periodicity from input file.
@@ -578,10 +695,13 @@ void nr_schedule_ue_spec(module_id_t module_id,
 
     uint8_t N_PRB_DMRS =
         getN_PRB_DMRS(sched_ctrl->active_bwp, sched_ctrl->numDmrsCdmGrpsNoData);
-
     uint8_t N_DMRS_SLOT = get_num_dmrs_symbols(sched_ctrl->active_bwp->bwp_Dedicated->pdsch_Config->choice.setup,
                                                RC.nrmac[module_id]->common_channels->ServingCellConfigCommon->dmrs_TypeA_Position ,
                                                nrOfSymbols);
+    const nfapi_nr_dmrs_type_e dmrsConfigType = getDmrsConfigType(sched_ctrl->active_bwp);
+    const int nrOfLayers = 1;
+    const uint16_t R = nr_get_code_rate_dl(sched_ctrl->mcs, sched_ctrl->mcsTableIdx);
+    const uint8_t Qm = nr_get_Qm_dl(sched_ctrl->mcs, sched_ctrl->mcsTableIdx);
     const uint32_t TBS =
         nr_compute_tbs(nr_get_Qm_dl(sched_ctrl->mcs, sched_ctrl->mcsTableIdx),
                        nr_get_code_rate_dl(sched_ctrl->mcs, sched_ctrl->mcsTableIdx),
@@ -590,36 +710,228 @@ void nr_schedule_ue_spec(module_id_t module_id,
                        N_PRB_DMRS * N_DMRS_SLOT,
                        0 /* N_PRB_oh, 0 for initialBWP */,
                        0 /* tb_scaling */,
-                       1 /* nrOfLayers */)
+                       nrOfLayers)
         >> 3;
 
-    const int current_harq_pid = slot % num_slots_per_tdd;
+    int8_t current_harq_pid = sched_ctrl->dl_harq_pid;
+    if (current_harq_pid < 0) {
+      /* PP has not selected a specific HARQ Process, get a new one */
+      current_harq_pid = sched_ctrl->available_dl_harq.head;
+      AssertFatal(current_harq_pid >= 0,
+                  "no free HARQ process available for UE %d\n",
+                  UE_id);
+      remove_front_nr_list(&sched_ctrl->available_dl_harq);
+      sched_ctrl->dl_harq_pid = current_harq_pid;
+    } else {
+      /* PP selected a specific HARQ process. Check whether it will be a new
+       * transmission or a retransmission, and remove from the corresponding
+       * list */
+      if (sched_ctrl->harq_processes[current_harq_pid].round == 0)
+        remove_nr_list(&sched_ctrl->available_dl_harq, current_harq_pid);
+      else
+        remove_nr_list(&sched_ctrl->retrans_dl_harq, current_harq_pid);
+    }
     NR_UE_harq_t *harq = &sched_ctrl->harq_processes[current_harq_pid];
-    NR_sched_pucch *pucch = &sched_ctrl->sched_pucch[sched_ctrl->pucch_sched_idx][sched_ctrl->pucch_occ_idx];
+    DevAssert(!harq->is_waiting);
+    add_tail_nr_list(&sched_ctrl->feedback_dl_harq, current_harq_pid);
+    NR_sched_pucch_t *pucch = &sched_ctrl->sched_pucch[0];
     harq->feedback_slot = pucch->ul_slot;
-    harq->is_waiting = 1;
+    harq->is_waiting = true;
     UE_info->mac_stats[UE_id].dlsch_rounds[harq->round]++;
 
-    LOG_D(MAC, "%4d.%2d RNTI %04x start %d RBS %d MCS %d TBS %d HARQ PID %d round %d NDI %d\n",
-          frame, slot, rnti, sched_ctrl->rbStart, sched_ctrl->rbSize, sched_ctrl->mcs,
-          TBS, current_harq_pid, harq->round, harq->ndi);
-
-    nfapi_nr_dl_tti_request_body_t *dl_req = &gNB_mac->DL_req[CC_id].dl_tti_request_body;
-    nr_fill_nfapi_dl_pdu(module_id,
-                         dl_req,
-                         rnti,
-                         UE_info->secondaryCellGroup[UE_id],
-                         sched_ctrl,
-                         pucch,
-                         getDmrsConfigType(sched_ctrl->active_bwp),
-                         nr_get_code_rate_dl(sched_ctrl->mcs, sched_ctrl->mcsTableIdx),
-                         nr_get_Qm_dl(sched_ctrl->mcs, sched_ctrl->mcsTableIdx),
-                         TBS,
-                         startSymbolIndex,
-                         nrOfSymbols,
-                         current_harq_pid,
-                         harq->ndi,
-                         harq->round);
+    LOG_D(MAC,
+          "%4d.%2d RNTI %04x start %d RBs %d startSymbol %d nb_symbsol %d MCS %d TBS %d HARQ PID %d round %d NDI %d\n",
+          frame,
+          slot,
+          rnti,
+          sched_ctrl->rbStart,
+          sched_ctrl->rbSize,
+          startSymbolIndex,
+          nrOfSymbols,
+          sched_ctrl->mcs,
+          TBS,
+          current_harq_pid,
+          harq->round,
+          harq->ndi);
+
+    NR_BWP_Downlink_t *bwp = sched_ctrl->active_bwp;
+    AssertFatal(bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList,
+                "searchSpacesToAddModList is null\n");
+    AssertFatal(bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList->list.count > 0,
+                "searchSPacesToAddModList is empty\n");
+
+    /* look up the PDCCH PDU for this CC, BWP, and CORESET. If it does not
+     * exist, create it */
+    const int bwpid = sched_ctrl->active_bwp->bwp_Id;
+    const int coresetid = sched_ctrl->coreset->controlResourceSetId;
+    nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu = gNB_mac->pdcch_pdu_idx[CC_id][bwpid][coresetid];
+    if (!pdcch_pdu) {
+      nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdcch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs];
+      memset(dl_tti_pdcch_pdu, 0, sizeof(nfapi_nr_dl_tti_request_pdu_t));
+      dl_tti_pdcch_pdu->PDUType = NFAPI_NR_DL_TTI_PDCCH_PDU_TYPE;
+      dl_tti_pdcch_pdu->PDUSize = (uint8_t)(2+sizeof(nfapi_nr_dl_tti_pdcch_pdu));
+      dl_req->nPDUs += 1;
+      pdcch_pdu = &dl_tti_pdcch_pdu->pdcch_pdu.pdcch_pdu_rel15;
+      nr_configure_pdcch(pdcch_pdu, sched_ctrl->search_space, sched_ctrl->coreset, scc, bwp);
+      gNB_mac->pdcch_pdu_idx[CC_id][bwpid][coresetid] = pdcch_pdu;
+    }
+
+    nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdsch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs];
+    memset(dl_tti_pdsch_pdu, 0, sizeof(nfapi_nr_dl_tti_request_pdu_t));
+    dl_tti_pdsch_pdu->PDUType = NFAPI_NR_DL_TTI_PDSCH_PDU_TYPE;
+    dl_tti_pdsch_pdu->PDUSize = (uint8_t)(2+sizeof(nfapi_nr_dl_tti_pdsch_pdu));
+    dl_req->nPDUs += 1;
+    nfapi_nr_dl_tti_pdsch_pdu_rel15_t *pdsch_pdu = &dl_tti_pdsch_pdu->pdsch_pdu.pdsch_pdu_rel15;
+
+    pdsch_pdu->pduBitmap = 0;
+    pdsch_pdu->rnti = rnti;
+    /* SCF222: PDU index incremented for each PDSCH PDU sent in TX control
+     * message. This is used to associate control information to data and is
+     * reset every slot. */
+    const int pduindex = gNB_mac->pdu_index[CC_id]++;
+    pdsch_pdu->pduIndex = pduindex;
+
+    // BWP
+    pdsch_pdu->BWPSize  = NRRIV2BW(bwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE);
+    pdsch_pdu->BWPStart = NRRIV2PRBOFFSET(bwp->bwp_Common->genericParameters.locationAndBandwidth,MAX_BWP_SIZE);
+    pdsch_pdu->SubcarrierSpacing = bwp->bwp_Common->genericParameters.subcarrierSpacing;
+    if (bwp->bwp_Common->genericParameters.cyclicPrefix)
+      pdsch_pdu->CyclicPrefix = *bwp->bwp_Common->genericParameters.cyclicPrefix;
+    else
+      pdsch_pdu->CyclicPrefix = 0;
+
+    // Codeword information
+    pdsch_pdu->NrOfCodewords = 1;
+    pdsch_pdu->targetCodeRate[0] = R;
+    pdsch_pdu->qamModOrder[0] = Qm;
+    pdsch_pdu->mcsIndex[0] = sched_ctrl->mcs;
+    pdsch_pdu->mcsTable[0] = sched_ctrl->mcsTableIdx;
+    pdsch_pdu->rvIndex[0] = nr_rv_round_map[harq->round];
+    pdsch_pdu->TBSize[0] = TBS;
+
+    pdsch_pdu->dataScramblingId = *scc->physCellId;
+    pdsch_pdu->nrOfLayers = nrOfLayers;
+    pdsch_pdu->transmissionScheme = 0;
+    pdsch_pdu->refPoint = 0; // Point A
+
+    // DMRS
+    pdsch_pdu->dlDmrsSymbPos =
+        fill_dmrs_mask(bwp->bwp_Dedicated->pdsch_Config->choice.setup,
+                       scc->dmrs_TypeA_Position,
+                       nrOfSymbols);
+    pdsch_pdu->dmrsConfigType = dmrsConfigType;
+    pdsch_pdu->dlDmrsScramblingId = *scc->physCellId;
+    pdsch_pdu->SCID = 0;
+    pdsch_pdu->numDmrsCdmGrpsNoData = sched_ctrl->numDmrsCdmGrpsNoData;
+    pdsch_pdu->dmrsPorts = 1;
+
+    // Pdsch Allocation in frequency domain
+    pdsch_pdu->resourceAlloc = 1;
+    pdsch_pdu->rbStart = sched_ctrl->rbStart;
+    pdsch_pdu->rbSize = sched_ctrl->rbSize;
+    pdsch_pdu->VRBtoPRBMapping = 1; // non-interleaved, check if this is ok for initialBWP
+
+    // Resource Allocation in time domain
+    pdsch_pdu->StartSymbolIndex = startSymbolIndex;
+    pdsch_pdu->NrOfSymbols = nrOfSymbols;
+
+    /* Check and validate PTRS values */
+    struct NR_SetupRelease_PTRS_DownlinkConfig *phaseTrackingRS =
+        bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS;
+    if (phaseTrackingRS) {
+      bool valid_ptrs_setup = set_dl_ptrs_values(phaseTrackingRS->choice.setup,
+                                                 pdsch_pdu->rbSize,
+                                                 pdsch_pdu->mcsIndex[0],
+                                                 pdsch_pdu->mcsTable[0],
+                                                 &pdsch_pdu->PTRSFreqDensity,
+                                                 &pdsch_pdu->PTRSTimeDensity,
+                                                 &pdsch_pdu->PTRSPortIndex,
+                                                 &pdsch_pdu->nEpreRatioOfPDSCHToPTRS,
+                                                 &pdsch_pdu->PTRSReOffset,
+                                                 pdsch_pdu->NrOfSymbols);
+      if (valid_ptrs_setup)
+        pdsch_pdu->pduBitmap |= 0x1; // Bit 0: pdschPtrs - Indicates PTRS included (FR2)
+    }
+
+    /* Fill PDCCH DL DCI PDU */
+    nfapi_nr_dl_dci_pdu_t *dci_pdu = &pdcch_pdu->dci_pdu[pdcch_pdu->numDlDci];
+    pdcch_pdu->numDlDci++;
+    dci_pdu->RNTI = rnti;
+    if (sched_ctrl->coreset->pdcch_DMRS_ScramblingID &&
+        sched_ctrl->search_space->searchSpaceType->present == NR_SearchSpace__searchSpaceType_PR_ue_Specific) {
+      dci_pdu->ScramblingId = *sched_ctrl->coreset->pdcch_DMRS_ScramblingID;
+      dci_pdu->ScramblingRNTI = rnti;
+    } else {
+      dci_pdu->ScramblingId = *scc->physCellId;
+      dci_pdu->ScramblingRNTI = 0;
+    }
+    dci_pdu->AggregationLevel = sched_ctrl->aggregation_level;
+    dci_pdu->CceIndex = sched_ctrl->cce_index;
+    dci_pdu->beta_PDCCH_1_0 = 0;
+    dci_pdu->powerControlOffsetSS = 1;
+
+    /* DCI payload */
+    dci_pdu_rel15_t dci_payload;
+    memset(&dci_payload, 0, sizeof(dci_pdu_rel15_t));
+    // bwp indicator
+    const int n_dl_bwp = UE_info->secondaryCellGroup[UE_id]->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count;
+      AssertFatal(n_dl_bwp == 1,
+          "downlinkBWP_ToAddModList has %d BWP!\n",
+          n_dl_bwp);
+    // as per table 7.3.1.1.2-1 in 38.212
+    dci_payload.bwp_indicator.val = n_dl_bwp < 4 ? bwp->bwp_Id : bwp->bwp_Id - 1;
+    AssertFatal(bwp->bwp_Dedicated->pdsch_Config->choice.setup->resourceAllocation == NR_PDSCH_Config__resourceAllocation_resourceAllocationType1,
+                "Only frequency resource allocation type 1 is currently supported\n");
+    dci_payload.frequency_domain_assignment.val =
+        PRBalloc_to_locationandbandwidth0(
+            pdsch_pdu->rbSize,
+            pdsch_pdu->rbStart,
+            pdsch_pdu->BWPSize);
+    dci_payload.time_domain_assignment.val = sched_ctrl->time_domain_allocation;
+    dci_payload.mcs = sched_ctrl->mcs;
+    dci_payload.rv = pdsch_pdu->rvIndex[0];
+    dci_payload.harq_pid = current_harq_pid;
+    dci_payload.ndi = harq->ndi;
+    dci_payload.dai[0].val = (pucch->dai_c-1)&3;
+    dci_payload.tpc = sched_ctrl->tpc1; // TPC for PUCCH: table 7.2.1-1 in 38.213
+    dci_payload.pucch_resource_indicator = pucch->resource_indicator;
+    dci_payload.pdsch_to_harq_feedback_timing_indicator.val = pucch->timing_indicator; // PDSCH to HARQ TI
+    dci_payload.antenna_ports.val = 0;  // nb of cdm groups w/o data 1 and dmrs port 0
+    dci_payload.dmrs_sequence_initialization.val = pdsch_pdu->SCID;
+    LOG_D(MAC,
+          "%4d.%2d DCI type 1 payload: freq_alloc %d (%d,%d,%d), "
+          "time_alloc %d, vrb to prb %d, mcs %d tb_scaling %d ndi %d rv %d\n",
+          frame,
+          slot,
+          dci_payload.frequency_domain_assignment.val,
+          pdsch_pdu->rbStart,
+          pdsch_pdu->rbSize,
+          pdsch_pdu->BWPSize,
+          dci_payload.time_domain_assignment.val,
+          dci_payload.vrb_to_prb_mapping.val,
+          dci_payload.mcs,
+          dci_payload.tb_scaling,
+          dci_payload.ndi,
+          dci_payload.rv);
+
+    const long f = sched_ctrl->search_space->searchSpaceType->choice.ue_Specific->dci_Formats;
+    const int dci_format = f ? NR_DL_DCI_FORMAT_1_1 : NR_DL_DCI_FORMAT_1_0;
+    const int rnti_type = NR_RNTI_C;
+
+    fill_dci_pdu_rel15(scc,
+                       UE_info->secondaryCellGroup[UE_id],
+                       dci_pdu,
+                       &dci_payload,
+                       dci_format,
+                       rnti_type,
+                       pdsch_pdu->BWPSize,
+                       bwp->bwp_Id);
+
+    LOG_D(MAC,
+          "coreset params: FreqDomainResource %llx, start_symbol %d  n_symb %d\n",
+          (unsigned long long)pdcch_pdu->FreqDomainResource,
+          pdcch_pdu->StartSymbolIndex,
+          pdcch_pdu->DurationSymbols);
 
     NR_UE_ret_info_t *retInfo = &sched_ctrl->retInfo[current_harq_pid];
     if (harq->round != 0) { /* retransmission */
@@ -645,8 +957,9 @@ void nr_schedule_ue_spec(module_id_t module_id,
               retInfo->mcs,
               retInfo->numDmrsCdmGrpsNoData);
       /* we do not have to do anything, since we do not require to get data
-       * from RLC, encode MAC CEs, or copy data to FAPI structures */
-      LOG_W(MAC,
+       * from RLC or encode MAC CEs. The TX_req structure is filled below 
+       * or copy data to FAPI structures */
+      LOG_D(MAC,
             "%d.%2d DL retransmission UE %d/RNTI %04x HARQ PID %d round %d NDI %d\n",
             frame,
             slot,
@@ -655,111 +968,121 @@ void nr_schedule_ue_spec(module_id_t module_id,
             current_harq_pid,
             harq->round,
             harq->ndi);
+      AssertFatal(harq->tb_size == TBS,
+                  "UE %d mismatch between scheduled TBS and buffered TB for HARQ PID %d\n",
+                  UE_id,
+                  current_harq_pid);
     } else { /* initial transmission */
 
       LOG_D(MAC, "[%s] Initial HARQ transmission in %d.%d\n", __FUNCTION__, frame, slot);
-      /* reserve space for timing advance of UE if necessary,
-       * nr_generate_dlsch_pdu() checks for ta_apply and add TA CE if necessary */
-      const int ta_len = (sched_ctrl->ta_apply) ? 2 : 0;
-
-      /* Get RLC data */
-      int header_length_total = 0;
-      int header_length_last = 0;
-      int sdu_length_total = 0;
-      int num_sdus = 0;
-      uint16_t sdu_lengths[NB_RB_MAX] = {0};
-      uint8_t mac_sdus[MAX_NR_DLSCH_PAYLOAD_BYTES];
-      unsigned char sdu_lcids[NB_RB_MAX] = {0};
-      const int lcid = DL_SCH_LCID_DTCH;
-      if (sched_ctrl->num_total_bytes > 0) {
-        /* this is the data from the RLC we would like to request (e.g., only
-         * some bytes for first LC and some more from a second one */
-        const rlc_buffer_occupancy_t ndata = sched_ctrl->rlc_status[lcid].bytes_in_buffer;
-        /* this is the maximum data we can transport based on TBS minus headers */
-        const int mindata = min(ndata, TBS - ta_len - header_length_total - sdu_length_total -  2 - (ndata >= 256));
-        LOG_D(MAC,
-              "[gNB %d][USER-PLANE DEFAULT DRB] Frame %d : DTCH->DLSCH, Requesting "
-              "%d bytes from RLC (lcid %d total hdr len %d), TBS: %d \n \n",
-              module_id,
-              frame,
-              mindata,
-              lcid,
-              header_length_total,
-              TBS);
 
-        sdu_lengths[num_sdus] = mac_rlc_data_req(module_id,
-            rnti,
-            module_id,
-            frame,
-            ENB_FLAG_YES,
-            MBMS_FLAG_NO,
-            lcid,
-            mindata,
-            (char *)&mac_sdus[sdu_length_total],
-            0,
-            0);
+      harq->tb_size = TBS;
+      uint8_t *buf = (uint8_t *) harq->tb;
 
-        LOG_D(MAC,
-              "[gNB %d][USER-PLANE DEFAULT DRB] Got %d bytes for DTCH %d \n",
-              module_id,
-              sdu_lengths[num_sdus],
-              lcid);
-
-        sdu_lcids[num_sdus] = lcid;
-        sdu_length_total += sdu_lengths[num_sdus];
-        header_length_last = 1 + 1 + (sdu_lengths[num_sdus] >= 128);
-        header_length_total += header_length_last;
-        num_sdus++;
+      /* first, write all CEs that might be there */
+      int written = nr_write_ce_dlsch_pdu(module_id,
+                                          sched_ctrl,
+                                          (unsigned char *)buf,
+                                          255, // no drx
+                                          NULL); // contention res id
+      buf += written;
+      int size = TBS - written;
+      DevAssert(size >= 0);
+
+      /* next, get RLC data */
+
+      const int lcid = DL_SCH_LCID_DTCH;
+      int dlsch_total_bytes = 0;
+      if (sched_ctrl->num_total_bytes > 0) {
+        tbs_size_t len = 0;
+        while (size > 3) {
+          // we do not know how much data we will get from RLC, i.e., whether it
+          // will be longer than 256B or not. Therefore, reserve space for long header, then
+          // fetch data, then fill real length
+          NR_MAC_SUBHEADER_LONG *header = (NR_MAC_SUBHEADER_LONG *) buf;
+          buf += 3;
+          size -= 3;
+
+          /* limit requested number of bytes to what preprocessor specified, or
+           * such that TBS is full */
+          const rlc_buffer_occupancy_t ndata = min(sched_ctrl->rlc_status[lcid].bytes_in_buffer, size);
+          len = mac_rlc_data_req(module_id,
+                                 rnti,
+                                 module_id,
+                                 frame,
+                                 ENB_FLAG_YES,
+                                 MBMS_FLAG_NO,
+                                 lcid,
+                                 ndata,
+                                 (char *)buf,
+                                 0,
+                                 0);
+
+          LOG_D(MAC,
+                "%4d.%2d RNTI %04x: %d bytes from DTCH %d (ndata %d, remaining size %d)\n",
+                frame,
+                slot,
+                rnti,
+                len,
+                lcid,
+                ndata,
+                size);
+          if (len == 0)
+            break;
+
+          header->R = 0;
+          header->F = 1;
+          header->LCID = lcid;
+          header->L1 = (len >> 8) & 0xff;
+          header->L2 = len & 0xff;
+          size -= len;
+          buf += len;
+          dlsch_total_bytes += len;
+        }
+        if (len == 0) {
+          /* RLC did not have data anymore, mark buffer as unused */
+          buf -= 3;
+          size += 3;
+        }
       }
       else if (get_softmodem_params()->phy_test || get_softmodem_params()->do_ra) {
-        LOG_D(MAC, "Configuring DL_TX in %d.%d: random data\n", frame, slot);
+        /* we will need the large header, phy-test typically allocates all
+         * resources and fills to the last byte below */
+        NR_MAC_SUBHEADER_LONG *header = (NR_MAC_SUBHEADER_LONG *) buf;
+        buf += 3;
+        size -= 3;
+        DevAssert(size > 0);
+        LOG_D(MAC, "Configuring DL_TX in %d.%d: TBS %d with %d B of random data\n", frame, slot, TBS, size);
         // fill dlsch_buffer with random data
-        for (int i = 0; i < TBS; i++)
-          mac_sdus[i] = (unsigned char) (lrand48()&0xff);
-        sdu_lcids[0] = 0x3f; // DRB
-        sdu_lengths[0] = TBS - ta_len - 3;
-        header_length_total += 2 + (sdu_lengths[0] >= 256);
-        sdu_length_total += sdu_lengths[0];
-        num_sdus +=1;
+        for (int i = 0; i < size; i++)
+          buf[i] = lrand48() & 0xff;
+        header->R = 0;
+        header->F = 1;
+        header->LCID = DL_SCH_LCID_PADDING;
+        header->L1 = (size >> 8) & 0xff;
+        header->L2 = size & 0xff;
+        size -= size;
+        buf += size;
+        dlsch_total_bytes += size;
       }
 
-      UE_info->mac_stats[UE_id].dlsch_total_bytes += TBS;
-      UE_info->mac_stats[UE_id].lc_bytes_tx[lcid] += sdu_length_total;
-
-      const int post_padding = TBS > header_length_total + sdu_length_total + ta_len;
-
-      const int ntx_req = gNB_mac->TX_req[CC_id].Number_of_PDUs;
-      nfapi_nr_pdu_t *tx_req = &gNB_mac->TX_req[CC_id].pdu_list[ntx_req];
-      /* pointer to directly generate the PDU into the nFAPI structure */
-      uint32_t *buf = tx_req->TLVs[0].value.direct;
-
-      const int offset = nr_generate_dlsch_pdu(
-          module_id,
-          sched_ctrl,
-          (unsigned char *)mac_sdus,
-          (unsigned char *)buf,
-          num_sdus, // num_sdus
-          sdu_lengths,
-          sdu_lcids,
-          255, // no drx
-          NULL, // contention res id
-          post_padding);
-
-      // Padding: fill remainder of DLSCH with 0
-      if (post_padding > 0) {
-        for (int j = 0; j < TBS - offset; j++)
-          buf[offset + j] = 0;
+      // Add padding header and zero rest out if there is space left
+      if (size > 0) {
+        NR_MAC_SUBHEADER_FIXED *padding = (NR_MAC_SUBHEADER_FIXED *) buf;
+        padding->R = 0;
+        padding->LCID = DL_SCH_LCID_PADDING;
+        size -= 1;
+        buf += 1;
+        while (size > 0) {
+          *buf = 0;
+          buf += 1;
+          size -= 1;
+        }
       }
 
-      /* the buffer has been filled by nr_generate_dlsch_pdu(), below we simply
-       * fill the remaining information */
-      tx_req->PDU_length = TBS;
-      tx_req->PDU_index  = gNB_mac->pdu_index[0]++;
-      tx_req->num_TLV = 1;
-      tx_req->TLVs[0].length = TBS + 2;
-      gNB_mac->TX_req[CC_id].Number_of_PDUs++;
-      gNB_mac->TX_req[CC_id].SFN = frame;
-      gNB_mac->TX_req[CC_id].Slot = slot;
+      UE_info->mac_stats[UE_id].dlsch_total_bytes += TBS;
+      UE_info->mac_stats[UE_id].dlsch_current_bytes = TBS;
+      UE_info->mac_stats[UE_id].lc_bytes_tx[lcid] += dlsch_total_bytes;
 
       retInfo->rbSize = sched_ctrl->rbSize;
       retInfo->time_domain_allocation = sched_ctrl->time_domain_allocation;
@@ -779,21 +1102,20 @@ void nr_schedule_ue_spec(module_id_t module_id,
       }
 
       T(T_GNB_MAC_DL_PDU_WITH_DATA, T_INT(module_id), T_INT(CC_id), T_INT(rnti),
-        T_INT(frame), T_INT(slot), T_INT(current_harq_pid), T_BUFFER(buf, TBS));
-
-#if defined(ENABLE_MAC_PAYLOAD_DEBUG)
-      if (frame%100 == 0) {
-        LOG_I(MAC,
-              "%d.%d, first 10 payload bytes, TBS size: %d \n",
-              frame,
-              slot,
-              TBS);
-        for(int i = 0; i < 10; i++)
-          LOG_I(MAC, "byte %d: %x\n", i, ((uint8_t *) buf)[i]);
-      }
-#endif
+        T_INT(frame), T_INT(slot), T_INT(current_harq_pid), T_BUFFER(harq->tb, TBS));
     }
 
+    const int ntx_req = gNB_mac->TX_req[CC_id].Number_of_PDUs;
+    nfapi_nr_pdu_t *tx_req = &gNB_mac->TX_req[CC_id].pdu_list[ntx_req];
+    tx_req->PDU_length = TBS;
+    tx_req->PDU_index  = pduindex;
+    tx_req->num_TLV = 1;
+    tx_req->TLVs[0].length = TBS + 2;
+    memcpy(tx_req->TLVs[0].value.direct, harq->tb, TBS);
+    gNB_mac->TX_req[CC_id].Number_of_PDUs++;
+    gNB_mac->TX_req[CC_id].SFN = frame;
+    gNB_mac->TX_req[CC_id].Slot = slot;
+
     /* mark UE as scheduled */
     sched_ctrl->rbSize = 0;
   }
diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c
index 5707f2c75a74754668d20e155801d713585a1b4f..8dbe8728f3aee9841fd0d22e01c2b695f415dde4 100644
--- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c
+++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c
@@ -255,9 +255,10 @@ void nr_schedule_css_dlsch_phytest(module_id_t   module_idP,
 /* schedules whole bandwidth for first user, all the time */
 void nr_preprocessor_phytest(module_id_t module_id,
                              frame_t frame,
-                             sub_frame_t slot,
-                             int num_slots_per_tdd)
+                             sub_frame_t slot)
 {
+  if (slot != 1)
+    return;
   NR_UE_info_t *UE_info = &RC.nrmac[module_id]->UE_info;
   const int UE_id = 0;
   const int CC_id = 0;
@@ -328,14 +329,25 @@ void nr_preprocessor_phytest(module_id_t module_id,
               __func__,
               UE_id);
 
-  nr_acknack_scheduling(module_id,
-                        UE_id,
-                        frame,
-                        slot,
-                        num_slots_per_tdd,
-                        &sched_ctrl->pucch_sched_idx,
-                        &sched_ctrl->pucch_occ_idx);
-  AssertFatal(sched_ctrl->pucch_sched_idx >= 0, "no uplink slot for PUCCH found!\n");
+  const bool alloc = nr_acknack_scheduling(module_id, UE_id, frame, slot);
+  if (!alloc) {
+    LOG_D(MAC,
+          "%s(): could not find PUCCH for UE %d/%04x@%d.%d\n",
+          __func__,
+          UE_id,
+          rnti,
+          frame,
+          slot);
+    UE_info->num_pdcch_cand[UE_id][cid]--;
+    int *cce_list = RC.nrmac[module_id]->cce_list[sched_ctrl->active_bwp->bwp_Id][cid];
+    for (int i = 0; i < sched_ctrl->aggregation_level; i++)
+      cce_list[sched_ctrl->cce_index + i] = 0;
+    return;
+  }
+
+  AssertFatal(alloc,
+              "could not find uplink slot for PUCCH (RNTI %04x@%d.%d)!\n",
+              rnti, frame, slot);
 
   sched_ctrl->rbStart = rbStart;
   sched_ctrl->rbSize = rbSize;
@@ -350,13 +362,15 @@ void nr_preprocessor_phytest(module_id_t module_id,
   }
   sched_ctrl->mcs = 9;
   sched_ctrl->numDmrsCdmGrpsNoData = 1;
+  /* get the PID of a HARQ process awaiting retransmission, or -1 otherwise */
+  sched_ctrl->dl_harq_pid = sched_ctrl->retrans_dl_harq.head;
 
   /* mark the corresponding RBs as used */
   for (int rb = 0; rb < sched_ctrl->rbSize; rb++)
     vrb_map[rb + sched_ctrl->rbStart] = 1;
 }
 
-void nr_ul_preprocessor_phytest(module_id_t module_id,
+bool nr_ul_preprocessor_phytest(module_id_t module_id,
                                 frame_t frame,
                                 sub_frame_t slot,
                                 int num_slots_per_tdd,
@@ -372,7 +386,7 @@ void nr_ul_preprocessor_phytest(module_id_t module_id,
               __func__,
               UE_info->num_UEs);
   if (UE_info->num_UEs == 0)
-    return;
+    return false;
 
   const int UE_id = 0;
   const int CC_id = 0;
@@ -387,15 +401,15 @@ void nr_ul_preprocessor_phytest(module_id_t module_id,
               tda,
               tdaList->list.count);
   int K2 = get_K2(sched_ctrl->active_ubwp, tda, mu);
-  const int sched_frame = frame + (slot + K2 >= num_slots_per_tdd);
-  const int sched_slot = (slot + K2) % num_slots_per_tdd;
+  const int sched_frame = frame + (slot + K2 >= nr_slots_per_frame[mu]);
+  const int sched_slot = (slot + K2) % nr_slots_per_frame[mu];
   /* check if slot is UL, and that slot is 8 (assuming K2=6 because of UE
    * limitations).  Note that if K2 or the TDD configuration is changed, below
    * conditions might exclude each other and never be true */
   if (!(is_xlsch_in_slot(ulsch_in_slot_bitmap, sched_slot) && sched_slot == 8))
-    return;
+    return false;
 
-  const int bw = NRRIV2BW(sched_ctrl->active_ubwp->bwp_Common->genericParameters.locationAndBandwidth, 275);
+  const int bw = NRRIV2BW(sched_ctrl->active_ubwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE);
   uint16_t rbStart = 0;
   uint16_t rbSize = 50; /* due to OAI UE limitations */
   if (rbSize>bw)
@@ -411,7 +425,7 @@ void nr_ul_preprocessor_phytest(module_id_t module_id,
             frame,
             slot,
             i);
-      return;
+      return false;
     }
   }
 
@@ -438,7 +452,7 @@ void nr_ul_preprocessor_phytest(module_id_t module_id,
                                            nr_of_candidates);
   if (sched_ctrl->cce_index < 0) {
     LOG_E(MAC, "%s(): CCE list not empty, couldn't schedule PUSCH\n", __func__);
-    return;
+    return false;
   }
   UE_info->num_pdcch_cand[UE_id][cid]++;
 
@@ -465,6 +479,8 @@ void nr_ul_preprocessor_phytest(module_id_t module_id,
   sched_pusch->mcs = mcs;
   sched_pusch->rbStart = rbStart;
   sched_pusch->rbSize = rbSize;
+  /* get the PID of a HARQ process awaiting retransmission, or -1 for "any new" */
+  sched_pusch->ul_harq_pid = sched_ctrl->retrans_ul_harq.head;
 
   /* Calculate TBS from MCS */
   sched_pusch->R = nr_get_code_rate_ul(mcs, ps->mcs_table);
@@ -487,4 +503,5 @@ void nr_ul_preprocessor_phytest(module_id_t module_id,
   /* mark the corresponding RBs as used */
   for (int rb = rbStart; rb < rbStart + rbSize; rb++)
     vrb_map_UL[rb] = 1;
+  return true;
 }
diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c
index 0ef79084ae97c87ac71474a026612b2e4a84bc2a..20da8fa30aaa520b3e44a22549de780c03f0043f 100644
--- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c
+++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c
@@ -502,259 +502,34 @@ void nr_configure_css_dci_initial(nfapi_nr_dl_tti_pdcch_pdu_rel15_t* pdcch_pdu,
 
 }
 
-void nr_fill_nfapi_dl_pdu(int Mod_idP,
-                          nfapi_nr_dl_tti_request_body_t *dl_req,
-                          rnti_t rnti,
-                          NR_CellGroupConfig_t *secondaryCellGroup,
-                          NR_UE_sched_ctrl_t *sched_ctrl,
-                          NR_sched_pucch *pucch_sched,
-                          nfapi_nr_dmrs_type_e dmrsConfigType,
-                          uint16_t R,
-                          uint8_t Qm,
-                          uint32_t TBS,
-                          int StartSymbolIndex,
-                          int NrOfSymbols,
-                          int harq_pid,
-                          int ndi,
-                          int round) {
-  gNB_MAC_INST                        *nr_mac  = RC.nrmac[Mod_idP];
-  NR_COMMON_channels_t                *cc      = nr_mac->common_channels;
-  NR_ServingCellConfigCommon_t        *scc     = cc->ServingCellConfigCommon;
-
-  const int bwp_id = sched_ctrl->active_bwp->bwp_Id;
-  const int nrOfLayers = 1;
-  const int mcs = sched_ctrl->mcs;
-  const int mcs_table_index = sched_ctrl->mcsTableIdx;
-  bool valid_ptrs_setup = false;
-
-  AssertFatal(secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count == 1,
-	      "downlinkBWP_ToAddModList has %d BWP!\n",
-	      secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count);
-  NR_BWP_Downlink_t *bwp=secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.array[bwp_id-1];
-
-  AssertFatal(bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList!=NULL,"searchPsacesToAddModList is null\n");
-  AssertFatal(bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList->list.count>0,
-              "searchPsacesToAddModList is empty\n");
-
-  nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdcch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs];
-  memset((void*)dl_tti_pdcch_pdu,0,sizeof(nfapi_nr_dl_tti_request_pdu_t));
-  dl_tti_pdcch_pdu->PDUType = NFAPI_NR_DL_TTI_PDCCH_PDU_TYPE;
-  dl_tti_pdcch_pdu->PDUSize = (uint8_t)(2+sizeof(nfapi_nr_dl_tti_pdcch_pdu));
-
-  nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdsch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs+1];
-  memset((void*)dl_tti_pdsch_pdu,0,sizeof(nfapi_nr_dl_tti_request_pdu_t));
-  dl_tti_pdsch_pdu->PDUType = NFAPI_NR_DL_TTI_PDSCH_PDU_TYPE;
-  dl_tti_pdsch_pdu->PDUSize = (uint8_t)(2+sizeof(nfapi_nr_dl_tti_pdsch_pdu));
-
-  nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15 = &dl_tti_pdcch_pdu->pdcch_pdu.pdcch_pdu_rel15;
-  nfapi_nr_dl_tti_pdsch_pdu_rel15_t *pdsch_pdu_rel15 = &dl_tti_pdsch_pdu->pdsch_pdu.pdsch_pdu_rel15;
-
-
-  pdsch_pdu_rel15->pduBitmap = 0;
-  pdsch_pdu_rel15->rnti = rnti;
-  pdsch_pdu_rel15->pduIndex = nr_mac->pdu_index[0]++;
-
-  // BWP
-  pdsch_pdu_rel15->BWPSize  = NRRIV2BW(bwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE);
-  pdsch_pdu_rel15->BWPStart = NRRIV2PRBOFFSET(bwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE);
-  pdsch_pdu_rel15->SubcarrierSpacing = bwp->bwp_Common->genericParameters.subcarrierSpacing;
-  if (bwp->bwp_Common->genericParameters.cyclicPrefix)
-    pdsch_pdu_rel15->CyclicPrefix = *bwp->bwp_Common->genericParameters.cyclicPrefix;
-  else
-    pdsch_pdu_rel15->CyclicPrefix = 0;
-
-  pdsch_pdu_rel15->NrOfCodewords = 1;
-  pdsch_pdu_rel15->targetCodeRate[0] = nr_get_code_rate_dl(mcs, mcs_table_index);
-  pdsch_pdu_rel15->qamModOrder[0] = 2;
-  pdsch_pdu_rel15->mcsIndex[0] = mcs;
-  pdsch_pdu_rel15->mcsTable[0] = mcs_table_index;
-  pdsch_pdu_rel15->rvIndex[0] = nr_rv_round_map[round];
-  pdsch_pdu_rel15->dataScramblingId = *scc->physCellId;
-  pdsch_pdu_rel15->nrOfLayers = nrOfLayers;
-  pdsch_pdu_rel15->transmissionScheme = 0;
-  pdsch_pdu_rel15->refPoint = 0; // Point A
-  pdsch_pdu_rel15->dmrsConfigType = dmrsConfigType;
-  pdsch_pdu_rel15->dlDmrsScramblingId = *scc->physCellId;
-  pdsch_pdu_rel15->SCID = 0;
-  pdsch_pdu_rel15->numDmrsCdmGrpsNoData = sched_ctrl->numDmrsCdmGrpsNoData;
-  pdsch_pdu_rel15->dmrsPorts = 1;
-  pdsch_pdu_rel15->resourceAlloc = 1;
-  pdsch_pdu_rel15->rbStart = sched_ctrl->rbStart;
-  pdsch_pdu_rel15->rbSize = sched_ctrl->rbSize;
-  pdsch_pdu_rel15->VRBtoPRBMapping = 1; // non-interleaved, check if this is ok for initialBWP
-  pdsch_pdu_rel15->targetCodeRate[0] = R;
-  pdsch_pdu_rel15->qamModOrder[0] = Qm;
-  pdsch_pdu_rel15->TBSize[0] = TBS;
-  pdsch_pdu_rel15->mcsTable[0] = sched_ctrl->mcsTableIdx;
-  pdsch_pdu_rel15->StartSymbolIndex = StartSymbolIndex;
-  pdsch_pdu_rel15->NrOfSymbols      = NrOfSymbols;
-
-  pdsch_pdu_rel15->dlDmrsSymbPos =
-      fill_dmrs_mask(bwp->bwp_Dedicated->pdsch_Config->choice.setup,
-                     scc->dmrs_TypeA_Position,
-                     pdsch_pdu_rel15->NrOfSymbols);
-
-  /* Check and validate PTRS values */
-  if(bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS != NULL) {
-    valid_ptrs_setup = set_dl_ptrs_values(bwp->bwp_Dedicated->pdsch_Config->choice.setup->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup,
-                                          pdsch_pdu_rel15->rbSize, pdsch_pdu_rel15->mcsIndex[0],
-                                          pdsch_pdu_rel15->mcsTable[0],
-                                          &pdsch_pdu_rel15->PTRSFreqDensity,&pdsch_pdu_rel15->PTRSTimeDensity,
-                                          &pdsch_pdu_rel15->PTRSPortIndex,&pdsch_pdu_rel15->nEpreRatioOfPDSCHToPTRS,
-                                          &pdsch_pdu_rel15->PTRSReOffset, pdsch_pdu_rel15->NrOfSymbols);
-    if(valid_ptrs_setup==true) {
-      pdsch_pdu_rel15->pduBitmap |=0x1;
-    }
-  }
-
-  dci_pdu_rel15_t dci_pdu_rel15[MAX_DCI_CORESET];
-  memset(dci_pdu_rel15, 0, sizeof(dci_pdu_rel15_t) * MAX_DCI_CORESET);
-
-  // bwp indicator
-  int n_dl_bwp = secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list.count;
-  if (n_dl_bwp < 4)
-    dci_pdu_rel15[0].bwp_indicator.val = bwp_id;
-  else
-    dci_pdu_rel15[0].bwp_indicator.val = bwp_id - 1; // as per table 7.3.1.1.2-1 in 38.212
-  // frequency domain assignment
-  if (bwp->bwp_Dedicated->pdsch_Config->choice.setup->resourceAllocation==NR_PDSCH_Config__resourceAllocation_resourceAllocationType1)
-    dci_pdu_rel15[0].frequency_domain_assignment.val =
-        PRBalloc_to_locationandbandwidth0(
-            pdsch_pdu_rel15->rbSize,
-            pdsch_pdu_rel15->rbStart,
-            NRRIV2BW(bwp->bwp_Common->genericParameters.locationAndBandwidth,
-                     MAX_BWP_SIZE));
-  else
-    AssertFatal(1==0,"Only frequency resource allocation type 1 is currently supported\n");
-  // time domain assignment: row index used instead of SLIV
-  dci_pdu_rel15[0].time_domain_assignment.val = sched_ctrl->time_domain_allocation;
-  // mcs and rv
-  dci_pdu_rel15[0].mcs = mcs;
-  dci_pdu_rel15[0].rv = pdsch_pdu_rel15->rvIndex[0];
-  // harq pid and ndi
-  dci_pdu_rel15[0].harq_pid = harq_pid;
-  dci_pdu_rel15[0].ndi = ndi;
-  // DAI
-  dci_pdu_rel15[0].dai[0].val = (pucch_sched->dai_c-1)&3;
-
-  // TPC for PUCCH
-  dci_pdu_rel15[0].tpc = sched_ctrl->tpc1; // table 7.2.1-1 in 38.213
-  // PUCCH resource indicator
-  dci_pdu_rel15[0].pucch_resource_indicator = pucch_sched->resource_indicator;
-  // PDSCH to HARQ TI
-  dci_pdu_rel15[0].pdsch_to_harq_feedback_timing_indicator.val = pucch_sched->timing_indicator;
-  // antenna ports
-  dci_pdu_rel15[0].antenna_ports.val = 0;  // nb of cdm groups w/o data 1 and dmrs port 0
-  // dmrs sequence initialization
-  dci_pdu_rel15[0].dmrs_sequence_initialization.val = pdsch_pdu_rel15->SCID;
-  LOG_D(MAC,
-        "[gNB scheduler phytest] DCI type 1 payload: freq_alloc %d (%d,%d,%d), "
-        "time_alloc %d, vrb to prb %d, mcs %d tb_scaling %d ndi %d rv %d\n",
-        dci_pdu_rel15[0].frequency_domain_assignment.val,
-        pdsch_pdu_rel15->rbStart,
-        pdsch_pdu_rel15->rbSize,
-        NRRIV2BW(bwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE),
-        dci_pdu_rel15[0].time_domain_assignment.val,
-        dci_pdu_rel15[0].vrb_to_prb_mapping.val,
-        dci_pdu_rel15[0].mcs,
-        dci_pdu_rel15[0].tb_scaling,
-        dci_pdu_rel15[0].ndi,
-        dci_pdu_rel15[0].rv);
-
-  nr_configure_pdcch(nr_mac,
-                     pdcch_pdu_rel15,
-                     rnti,
-                     sched_ctrl->search_space,
-                     sched_ctrl->coreset,
-                     scc,
-                     bwp,
-                     sched_ctrl->aggregation_level,
-                     sched_ctrl->cce_index);
-
-  int dci_formats[2];
-  int rnti_types[2];
-
-  if (sched_ctrl->search_space->searchSpaceType->choice.ue_Specific->dci_Formats)
-    dci_formats[0]  = NR_DL_DCI_FORMAT_1_1;
-  else
-    dci_formats[0]  = NR_DL_DCI_FORMAT_1_0;
-
-  rnti_types[0]   = NR_RNTI_C;
-
-  fill_dci_pdu_rel15(scc,secondaryCellGroup,pdcch_pdu_rel15,dci_pdu_rel15,dci_formats,rnti_types,pdsch_pdu_rel15->BWPSize,bwp_id);
-
-  LOG_D(MAC,
-        "DCI params: rnti %x, rnti_type %d, dci_format %d\n",
-        pdcch_pdu_rel15->dci_pdu.RNTI[0],
-        rnti_types[0],
-        dci_formats[0]);
-  LOG_D(MAC,
-        "coreset params: FreqDomainResource %llx, start_symbol %d  n_symb %d\n",
-        (unsigned long long)pdcch_pdu_rel15->FreqDomainResource,
-        pdcch_pdu_rel15->StartSymbolIndex,
-        pdcch_pdu_rel15->DurationSymbols);
-
-  LOG_D(MAC,
-        "DLSCH PDU: start PRB %d n_PRB %d start symbol %d nb_symbols %d "
-        "nb_layers %d nb_codewords %d mcs %d TBS: %d\n",
-        pdsch_pdu_rel15->rbStart,
-        pdsch_pdu_rel15->rbSize,
-        pdsch_pdu_rel15->StartSymbolIndex,
-        pdsch_pdu_rel15->NrOfSymbols,
-        pdsch_pdu_rel15->nrOfLayers,
-        pdsch_pdu_rel15->NrOfCodewords,
-        pdsch_pdu_rel15->mcsIndex[0],
-        TBS);
-
-  dl_req->nPDUs += 2;
-}
-
-void config_uldci(NR_BWP_Uplink_t *ubwp,
-                  nfapi_nr_pusch_pdu_t *pusch_pdu,
-                  nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15,
+void config_uldci(const NR_BWP_Uplink_t *ubwp,
+                  const nfapi_nr_pusch_pdu_t *pusch_pdu,
                   dci_pdu_rel15_t *dci_pdu_rel15,
-                  int *dci_formats,
-                  int time_domain_assignment, uint8_t tpc,
-                  int n_ubwp, int bwp_id) {
+                  int dci_format,
+                  int time_domain_assignment,
+                  uint8_t tpc,
+                  int n_ubwp,
+                  int bwp_id) {
   const int bw = NRRIV2BW(ubwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE);
-  switch (dci_formats[(pdcch_pdu_rel15->numDlDci) - 1]) {
+  dci_pdu_rel15->frequency_domain_assignment.val =
+      PRBalloc_to_locationandbandwidth0(pusch_pdu->rb_size, pusch_pdu->rb_start, bw);
+  dci_pdu_rel15->time_domain_assignment.val = time_domain_assignment;
+  dci_pdu_rel15->frequency_hopping_flag.val = pusch_pdu->frequency_hopping;
+  dci_pdu_rel15->mcs = pusch_pdu->mcs_index;
+  dci_pdu_rel15->ndi = pusch_pdu->pusch_data.new_data_indicator;
+  dci_pdu_rel15->rv = pusch_pdu->pusch_data.rv_index;
+  dci_pdu_rel15->harq_pid = pusch_pdu->pusch_data.harq_process_id;
+  dci_pdu_rel15->tpc = tpc;
+  AssertFatal(ubwp->bwp_Dedicated->pusch_Config->choice.setup->resourceAllocation == NR_PUSCH_Config__resourceAllocation_resourceAllocationType1,
+              "Only frequency resource allocation type 1 is currently supported\n");
+  switch (dci_format) {
     case NR_UL_DCI_FORMAT_0_0:
-      dci_pdu_rel15->frequency_domain_assignment.val =
-          PRBalloc_to_locationandbandwidth0(pusch_pdu->rb_size, pusch_pdu->rb_start, bw);
-
-      dci_pdu_rel15->time_domain_assignment.val = time_domain_assignment;
-      dci_pdu_rel15->frequency_hopping_flag.val = pusch_pdu->frequency_hopping;
-      dci_pdu_rel15->mcs = pusch_pdu->mcs_index;
-
       dci_pdu_rel15->format_indicator = 0;
-      dci_pdu_rel15->ndi = pusch_pdu->pusch_data.new_data_indicator;
-      dci_pdu_rel15->rv = pusch_pdu->pusch_data.rv_index;
-      dci_pdu_rel15->harq_pid = pusch_pdu->pusch_data.harq_process_id;
-      dci_pdu_rel15->tpc = tpc;
       break;
     case NR_UL_DCI_FORMAT_0_1:
-      dci_pdu_rel15->ndi = pusch_pdu->pusch_data.new_data_indicator;
-      dci_pdu_rel15->rv = pusch_pdu->pusch_data.rv_index;
-      dci_pdu_rel15->harq_pid = pusch_pdu->pusch_data.harq_process_id;
-      dci_pdu_rel15->frequency_hopping_flag.val = pusch_pdu->frequency_hopping;
       dci_pdu_rel15->dai[0].val = 0; //TODO
-      // bwp indicator
-      if (n_ubwp < 4)
-        dci_pdu_rel15->bwp_indicator.val = bwp_id;
-      else
-        dci_pdu_rel15->bwp_indicator.val = bwp_id - 1; // as per table 7.3.1.1.2-1 in 38.212
-      // frequency domain assignment
-      AssertFatal(ubwp->bwp_Dedicated->pusch_Config->choice.setup->resourceAllocation
-                      == NR_PUSCH_Config__resourceAllocation_resourceAllocationType1,
-                  "Only frequency resource allocation type 1 is currently supported\n");
-      dci_pdu_rel15->frequency_domain_assignment.val =
-          PRBalloc_to_locationandbandwidth0(pusch_pdu->rb_size, pusch_pdu->rb_start, bw);
-      // time domain assignment
-      dci_pdu_rel15->time_domain_assignment.val = time_domain_assignment;
-      // mcs
-      dci_pdu_rel15->mcs = pusch_pdu->mcs_index;
-      // tpc command for pusch
-      dci_pdu_rel15->tpc = tpc;
+      // bwp indicator as per table 7.3.1.1.2-1 in 38.212
+      dci_pdu_rel15->bwp_indicator.val = n_ubwp < 4 ? bwp_id : bwp_id - 1;
       // SRS resource indicator
       if (ubwp->bwp_Dedicated->pusch_Config->choice.setup->txConfig != NULL) {
         AssertFatal(*ubwp->bwp_Dedicated->pusch_Config->choice.setup->txConfig == NR_PUSCH_Config__txConfig_codebook,
@@ -771,10 +546,8 @@ void config_uldci(NR_BWP_Uplink_t *ubwp,
   }
 
   LOG_D(MAC,
-        "%s() ULDCI type 0 payload: PDCCH CCEIndex %d, freq_alloc %d, "
-        "time_alloc %d, freq_hop_flag %d, mcs %d tpc %d ndi %d rv %d\n",
+        "%s() ULDCI type 0 payload: freq_alloc %d, time_alloc %d, freq_hop_flag %d, mcs %d tpc %d ndi %d rv %d\n",
         __func__,
-        pdcch_pdu_rel15->dci_pdu.CceIndex[pdcch_pdu_rel15->numDlDci],
         dci_pdu_rel15->frequency_domain_assignment.val,
         dci_pdu_rel15->time_domain_assignment.val,
         dci_pdu_rel15->frequency_hopping_flag.val,
@@ -784,15 +557,12 @@ void config_uldci(NR_BWP_Uplink_t *ubwp,
         dci_pdu_rel15->rv);
 }
 
-void nr_configure_pdcch(gNB_MAC_INST *nr_mac,
-                        nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu,
-                        uint16_t rnti,
+void nr_configure_pdcch(nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu,
                         NR_SearchSpace_t *ss,
                         NR_ControlResourceSet_t *coreset,
                         NR_ServingCellConfigCommon_t *scc,
-                        NR_BWP_Downlink_t *bwp,
-                        uint8_t aggregation_level,
-                        int CCEIndex) {
+                        NR_BWP_Downlink_t *bwp)
+{
   if (bwp) { // This is not the InitialBWP
     pdcch_pdu->BWPSize  = NRRIV2BW(bwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE);
     pdcch_pdu->BWPStart = NRRIV2PRBOFFSET(bwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE);
@@ -847,27 +617,6 @@ void nr_configure_pdcch(gNB_MAC_INST *nr_mac,
 
     //precoderGranularity
     pdcch_pdu->precoderGranularity = coreset->precoderGranularity;
-
-    pdcch_pdu->dci_pdu.RNTI[pdcch_pdu->numDlDci]=rnti;
-
-    if (coreset->pdcch_DMRS_ScramblingID != NULL &&
-        ss->searchSpaceType->present == NR_SearchSpace__searchSpaceType_PR_ue_Specific) {
-      pdcch_pdu->dci_pdu.ScramblingId[pdcch_pdu->numDlDci] = *coreset->pdcch_DMRS_ScramblingID;
-      pdcch_pdu->dci_pdu.ScramblingRNTI[pdcch_pdu->numDlDci]=rnti;
-    }
-    else {
-      pdcch_pdu->dci_pdu.ScramblingId[pdcch_pdu->numDlDci] = *scc->physCellId;
-      pdcch_pdu->dci_pdu.ScramblingRNTI[pdcch_pdu->numDlDci]=0;
-    }
-
-    pdcch_pdu->dci_pdu.AggregationLevel[pdcch_pdu->numDlDci] = aggregation_level;
-    pdcch_pdu->dci_pdu.CceIndex[pdcch_pdu->numDlDci] = CCEIndex;
-
-    if (ss->searchSpaceType->choice.ue_Specific->dci_Formats==NR_SearchSpace__searchSpaceType__ue_Specific__dci_Formats_formats0_0_And_1_0)
-      pdcch_pdu->dci_pdu.beta_PDCCH_1_0[pdcch_pdu->numDlDci]=0;
-
-    pdcch_pdu->dci_pdu.powerControlOffsetSS[pdcch_pdu->numDlDci]=1;
-    pdcch_pdu->numDlDci++;
   }
   else { // this is for InitialBWP
     AssertFatal(1==0,"Fill in InitialBWP PDCCH configuration\n");
@@ -1084,7 +833,7 @@ void nr_configure_pucch(nfapi_nr_pucch_pdu_t* pucch_pdu,
 }
 
 
-void prepare_dci(NR_CellGroupConfig_t *secondaryCellGroup,
+void prepare_dci(const NR_CellGroupConfig_t *secondaryCellGroup,
                  dci_pdu_rel15_t *dci_pdu_rel15,
                  nr_dci_format_t format,
                  int bwp_id) {
@@ -1160,591 +909,505 @@ void prepare_dci(NR_CellGroupConfig_t *secondaryCellGroup,
 }
 
 
-void fill_dci_pdu_rel15(NR_ServingCellConfigCommon_t *scc,
-                        NR_CellGroupConfig_t *secondaryCellGroup,
-                        nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15,
+void fill_dci_pdu_rel15(const NR_ServingCellConfigCommon_t *scc,
+                        const NR_CellGroupConfig_t *secondaryCellGroup,
+                        nfapi_nr_dl_dci_pdu_t *pdcch_dci_pdu,
                         dci_pdu_rel15_t *dci_pdu_rel15,
-                        int *dci_formats,
-                        int *rnti_types,
+                        int dci_format,
+                        int rnti_type,
                         int N_RB,
                         int bwp_id) {
-
-  uint8_t fsize=0, pos=0;
-
-  for (int d=0;d<pdcch_pdu_rel15->numDlDci;d++) {
-
-    uint64_t *dci_pdu = (uint64_t *)pdcch_pdu_rel15->dci_pdu.Payload[d];
-    int dci_size = nr_dci_size(scc,secondaryCellGroup,&dci_pdu_rel15[d],dci_formats[d],rnti_types[d],N_RB,bwp_id);
-    pdcch_pdu_rel15->dci_pdu.PayloadSizeBits[d] = dci_size;
-    AssertFatal(pdcch_pdu_rel15->dci_pdu.PayloadSizeBits[d]<=64, "DCI sizes above 64 bits not yet supported");
-
-    if(dci_formats[d]==NR_DL_DCI_FORMAT_1_1 || dci_formats[d]==NR_UL_DCI_FORMAT_0_1)
-      prepare_dci(secondaryCellGroup,&dci_pdu_rel15[d],dci_formats[d],bwp_id);
-    
-    /// Payload generation
-    switch(dci_formats[d]) {
-    case NR_DL_DCI_FORMAT_1_0:
-      switch(rnti_types[d]) {
-      case NR_RNTI_RA:
-	// Freq domain assignment
-	fsize = (int)ceil( log2( (N_RB*(N_RB+1))>>1 ) );
-	pos=fsize;
-	*dci_pdu |= (((uint64_t)dci_pdu_rel15->frequency_domain_assignment.val&((1<<fsize)-1)) << (dci_size-pos));
-	LOG_D(MAC,"frequency-domain assignment %d (%d bits) N_RB_BWP %d=> %d (0x%lx)\n",dci_pdu_rel15->frequency_domain_assignment.val,fsize,N_RB,dci_size-pos,*dci_pdu);
-	// Time domain assignment
-	pos+=4;
-	*dci_pdu |= (((uint64_t)dci_pdu_rel15->time_domain_assignment.val&0xf) << (dci_size-pos));
-	LOG_D(MAC,"time-domain assignment %d  (3 bits)=> %d (0x%lx)\n",dci_pdu_rel15->time_domain_assignment.val,dci_size-pos,*dci_pdu);
-	// VRB to PRB mapping
-	
-	pos++;
-	*dci_pdu |= ((uint64_t)dci_pdu_rel15->vrb_to_prb_mapping.val&0x1)<<(dci_size-pos);
-	LOG_D(MAC,"vrb to prb mapping %d  (1 bits)=> %d (0x%lx)\n",dci_pdu_rel15->vrb_to_prb_mapping.val,dci_size-pos,*dci_pdu);
-	// MCS
-	pos+=5;
-	*dci_pdu |= ((uint64_t)dci_pdu_rel15->mcs&0x1f)<<(dci_size-pos);
+  uint8_t fsize = 0, pos = 0;
+
+  uint64_t *dci_pdu = (uint64_t *)pdcch_dci_pdu->Payload;
+  int dci_size = nr_dci_size(scc, secondaryCellGroup, dci_pdu_rel15, dci_format, rnti_type, N_RB, bwp_id);
+  pdcch_dci_pdu->PayloadSizeBits = dci_size;
+  AssertFatal(dci_size <= 64, "DCI sizes above 64 bits not yet supported");
+
+  if (dci_format == NR_DL_DCI_FORMAT_1_1 || dci_format == NR_UL_DCI_FORMAT_0_1)
+    prepare_dci(secondaryCellGroup, dci_pdu_rel15, dci_format, bwp_id);
+
+  /// Payload generation
+  switch (dci_format) {
+  case NR_DL_DCI_FORMAT_1_0:
+    switch (rnti_type) {
+    case NR_RNTI_RA:
+      // Freq domain assignment
+      fsize = (int)ceil(log2((N_RB * (N_RB + 1)) >> 1));
+      pos = fsize;
+      *dci_pdu |= (((uint64_t)dci_pdu_rel15->frequency_domain_assignment.val & ((1 << fsize) - 1)) << (dci_size - pos));
+      LOG_D(MAC,
+            "frequency-domain assignment %d (%d bits) N_RB_BWP %d=> %d (0x%lx)\n",
+            dci_pdu_rel15->frequency_domain_assignment.val,
+            fsize,
+            N_RB,
+            dci_size - pos,
+            *dci_pdu);
+      // Time domain assignment
+      pos += 4;
+      *dci_pdu |= (((uint64_t)dci_pdu_rel15->time_domain_assignment.val & 0xf) << (dci_size - pos));
+      LOG_D(MAC,
+            "time-domain assignment %d  (3 bits)=> %d (0x%lx)\n",
+            dci_pdu_rel15->time_domain_assignment.val,
+            dci_size - pos,
+            *dci_pdu);
+      // VRB to PRB mapping
+      pos++;
+      *dci_pdu |= ((uint64_t)dci_pdu_rel15->vrb_to_prb_mapping.val & 0x1) << (dci_size - pos);
+      LOG_D(MAC,
+            "vrb to prb mapping %d  (1 bits)=> %d (0x%lx)\n",
+            dci_pdu_rel15->vrb_to_prb_mapping.val,
+            dci_size - pos,
+            *dci_pdu);
+      // MCS
+      pos += 5;
+      *dci_pdu |= ((uint64_t)dci_pdu_rel15->mcs & 0x1f) << (dci_size - pos);
 #ifdef DEBUG_FILL_DCI
-	LOG_I(MAC,"mcs %d  (5 bits)=> %d (0x%lx)\n",dci_pdu_rel15->mcs,dci_size-pos,*dci_pdu);
+      LOG_I(MAC, "mcs %d  (5 bits)=> %d (0x%lx)\n", dci_pdu_rel15->mcs, dci_size - pos, *dci_pdu);
 #endif
-	// TB scaling
-	pos+=2;
-	*dci_pdu |= ((uint64_t)dci_pdu_rel15->tb_scaling&0x3)<<(dci_size-pos);
+      // TB scaling
+      pos += 2;
+      *dci_pdu |= ((uint64_t)dci_pdu_rel15->tb_scaling & 0x3) << (dci_size - pos);
 #ifdef DEBUG_FILL_DCI
-	LOG_I(MAC,"tb_scaling %d  (2 bits)=> %d (0x%lx)\n",dci_pdu_rel15->tb_scaling,dci_size-pos,*dci_pdu);
+      LOG_I(MAC, "tb_scaling %d  (2 bits)=> %d (0x%lx)\n", dci_pdu_rel15->tb_scaling, dci_size - pos, *dci_pdu);
 #endif
-	break;
-	
-      case NR_RNTI_C:
-	
-	// indicating a DL DCI format 1bit
-	pos++;
-	*dci_pdu |= ((uint64_t)dci_pdu_rel15->format_indicator&1)<<(dci_size-pos);
-	LOG_D(MAC,"Format indicator %d (%d bits) N_RB_BWP %d => %d (0x%lx)\n",dci_pdu_rel15->format_indicator,1,N_RB,dci_size-pos,*dci_pdu);
-	
-	// Freq domain assignment (275rb >> fsize = 16)
-	fsize = (int)ceil( log2( (N_RB*(N_RB+1))>>1 ) );
-	pos+=fsize;
-	*dci_pdu |= (((uint64_t)dci_pdu_rel15->frequency_domain_assignment.val&((1<<fsize)-1)) << (dci_size-pos));
-	
-	LOG_D(MAC,"Freq domain assignment %d (%d bits)=> %d (0x%lx)\n",dci_pdu_rel15->frequency_domain_assignment.val,fsize,dci_size-pos,*dci_pdu);
-	
-	uint16_t is_ra = 1;
-	for (int i=0; i<fsize; i++)
-	  if (!((dci_pdu_rel15->frequency_domain_assignment.val>>i)&1)) {
-	    is_ra = 0;
-	    break;
-	  }
-	if (is_ra) //fsize are all 1  38.212 p86
-	  {
-	    // ra_preamble_index 6 bits
-	    pos+=6;
-	    *dci_pdu |= ((dci_pdu_rel15->ra_preamble_index&0x3f)<<(dci_size-pos));
-	    
-	    // UL/SUL indicator  1 bit
-	    pos++;
-	    *dci_pdu |= (dci_pdu_rel15->ul_sul_indicator.val&1)<<(dci_size-pos);
-	    
-	    // SS/PBCH index  6 bits
-	    pos+=6;
-	    *dci_pdu |= ((dci_pdu_rel15->ss_pbch_index&0x3f)<<(dci_size-pos));
-	    
-	    //  prach_mask_index  4 bits
-	    pos+=4;
-	    *dci_pdu |= ((dci_pdu_rel15->prach_mask_index&0xf)<<(dci_size-pos));
-	    
-	  }  //end if
-	
-	else {
-	  
-	  // Time domain assignment 4bit
-	  
-	  pos+=4;
-	  *dci_pdu |= ((dci_pdu_rel15->time_domain_assignment.val&0xf) << (dci_size-pos));
-	  LOG_D(MAC,"Time domain assignment %d (%d bits)=> %d (0x%lx)\n",dci_pdu_rel15->time_domain_assignment.val,4,dci_size-pos,*dci_pdu);
-	  
-	  // VRB to PRB mapping  1bit
-	  pos++;
-	  *dci_pdu |= (dci_pdu_rel15->vrb_to_prb_mapping.val&1)<<(dci_size-pos);
-	  LOG_D(MAC,"VRB to PRB %d (%d bits)=> %d (0x%lx)\n",dci_pdu_rel15->vrb_to_prb_mapping.val,1,dci_size-pos,*dci_pdu);
-	  
-	  // MCS 5bit  //bit over 32, so dci_pdu ++
-	  pos+=5;
-	  *dci_pdu |= (dci_pdu_rel15->mcs&0x1f)<<(dci_size-pos);
-	  LOG_D(MAC,"MCS %d (%d bits)=> %d (0x%lx)\n",dci_pdu_rel15->mcs,5,dci_size-pos,*dci_pdu);
-	  
-	  // New data indicator 1bit
-	  pos++;
-	  *dci_pdu |= (dci_pdu_rel15->ndi&1)<<(dci_size-pos);
-	  LOG_D(MAC,"NDI %d (%d bits)=> %d (0x%lx)\n",dci_pdu_rel15->ndi,1,dci_size-pos,*dci_pdu);
-	  
-	  // Redundancy version  2bit
-	  pos+=2;
-	  *dci_pdu |= (dci_pdu_rel15->rv&0x3)<<(dci_size-pos);
-	  LOG_D(MAC,"RV %d (%d bits)=> %d (0x%lx)\n",dci_pdu_rel15->rv,2,dci_size-pos,*dci_pdu);
-	  
-	  // HARQ process number  4bit
-	  pos+=4;
-	  *dci_pdu  |= ((dci_pdu_rel15->harq_pid&0xf)<<(dci_size-pos));
-	  LOG_D(MAC,"HARQ_PID %d (%d bits)=> %d (0x%lx)\n",dci_pdu_rel15->harq_pid,4,dci_size-pos,*dci_pdu);
-	  
-	  // Downlink assignment index  2bit
-	  pos+=2;
-	  *dci_pdu |= ((dci_pdu_rel15->dai[0].val&3)<<(dci_size-pos));
-	  LOG_D(MAC,"DAI %d (%d bits)=> %d (0x%lx)\n",dci_pdu_rel15->dai[0].val,2,dci_size-pos,*dci_pdu);
-	  
-	  // TPC command for scheduled PUCCH  2bit
-	  pos+=2;
-	  *dci_pdu |= ((dci_pdu_rel15->tpc&3)<<(dci_size-pos));
-	  LOG_D(MAC,"TPC %d (%d bits)=> %d (0x%lx)\n",dci_pdu_rel15->tpc,2,dci_size-pos,*dci_pdu);
-	  
-	  // PUCCH resource indicator  3bit
-	  pos+=3;
-	  *dci_pdu |= ((dci_pdu_rel15->pucch_resource_indicator&0x7)<<(dci_size-pos));
-	  LOG_D(MAC,"PUCCH RI %d (%d bits)=> %d (0x%lx)\n",dci_pdu_rel15->pucch_resource_indicator,3,dci_size-pos,*dci_pdu);
-	  
-	  // PDSCH-to-HARQ_feedback timing indicator 3bit
-	  pos+=3;
-	  *dci_pdu |= ((dci_pdu_rel15->pdsch_to_harq_feedback_timing_indicator.val&0x7)<<(dci_size-pos));
-	  LOG_D(MAC,"PDSCH to HARQ TI %d (%d bits)=> %d (0x%lx)\n",dci_pdu_rel15->pdsch_to_harq_feedback_timing_indicator.val,3,dci_size-pos,*dci_pdu);
-	  
-	} //end else
-	break;
-	
-      case NR_RNTI_P:
-	
-	// Short Messages Indicator – 2 bits
-	for (int i=0; i<2; i++)
-	  *dci_pdu |= (((uint64_t)dci_pdu_rel15->short_messages_indicator>>(1-i))&1)<<(dci_size-pos++);
-	// Short Messages – 8 bits
-	for (int i=0; i<8; i++)
-	  *dci_pdu |= (((uint64_t)dci_pdu_rel15->short_messages>>(7-i))&1)<<(dci_size-pos++);
-	// Freq domain assignment 0-16 bit
-	fsize = (int)ceil( log2( (N_RB*(N_RB+1))>>1 ) );
-	for (int i=0; i<fsize; i++)
-	  *dci_pdu |= (((uint64_t)dci_pdu_rel15->frequency_domain_assignment.val>>(fsize-i-1))&1)<<(dci_size-pos++);
-	// Time domain assignment 4 bit
-	for (int i=0; i<4; i++)
-	  *dci_pdu |= (((uint64_t)dci_pdu_rel15->time_domain_assignment.val>>(3-i))&1)<<(dci_size-pos++);
-	// VRB to PRB mapping 1 bit
-	*dci_pdu |= ((uint64_t)dci_pdu_rel15->vrb_to_prb_mapping.val&1)<<(dci_size-pos++);
-	// MCS 5 bit
-	for (int i=0; i<5; i++)
-	  *dci_pdu |= (((uint64_t)dci_pdu_rel15->mcs>>(4-i))&1)<<(dci_size-pos++);
-	
-	// TB scaling 2 bit
-	for (int i=0; i<2; i++)
-	  *dci_pdu |= (((uint64_t)dci_pdu_rel15->tb_scaling>>(1-i))&1)<<(dci_size-pos++);
-	
-	
-	break;
-	
-      case NR_RNTI_SI:
-         pos=1;
-
-        // Freq domain assignment 0-16 bit
-        fsize = (int)ceil( log2( (N_RB*(N_RB+1))>>1 ) );
-        for (int i=0; i<fsize; i++)
-          *dci_pdu |= (((uint64_t)dci_pdu_rel15->frequency_domain_assignment.val>>(fsize-1-i))&1)<<(dci_size-pos++);
-
-        // Time domain assignment 4 bit
-        for (int i=0; i<4; i++)
-          *dci_pdu |= (((uint64_t)dci_pdu_rel15->time_domain_assignment.val>>(3-i))&1)<<(dci_size-pos++);
-
-        // VRB to PRB mapping 1 bit
-        *dci_pdu |= ((uint64_t)dci_pdu_rel15->vrb_to_prb_mapping.val&1)<<(dci_size-pos++);
+      break;
 
+    case NR_RNTI_C:
+      // indicating a DL DCI format 1bit
+      pos++;
+      *dci_pdu |= ((uint64_t)dci_pdu_rel15->format_indicator & 1) << (dci_size - pos);
+      LOG_D(MAC,
+            "Format indicator %d (%d bits) N_RB_BWP %d => %d (0x%lx)\n",
+            dci_pdu_rel15->format_indicator,
+            1,
+            N_RB,
+            dci_size - pos,
+            *dci_pdu);
+      // Freq domain assignment (275rb >> fsize = 16)
+      fsize = (int)ceil(log2((N_RB * (N_RB + 1)) >> 1));
+      pos += fsize;
+      *dci_pdu |= (((uint64_t)dci_pdu_rel15->frequency_domain_assignment.val & ((1 << fsize) - 1)) << (dci_size - pos));
+      LOG_D(MAC,
+            "Freq domain assignment %d (%d bits)=> %d (0x%lx)\n",
+            dci_pdu_rel15->frequency_domain_assignment.val,
+            fsize,
+            dci_size - pos,
+            *dci_pdu);
+      uint16_t is_ra = 1;
+      for (int i = 0; i < fsize; i++) {
+        if (!((dci_pdu_rel15->frequency_domain_assignment.val >> i) & 1)) {
+          is_ra = 0;
+          break;
+        }
+      }
+      if (is_ra) { // fsize are all 1  38.212 p86
+        // ra_preamble_index 6 bits
+        pos += 6;
+        *dci_pdu |= ((dci_pdu_rel15->ra_preamble_index & 0x3f) << (dci_size - pos));
+        // UL/SUL indicator  1 bit
+        pos++;
+        *dci_pdu |= (dci_pdu_rel15->ul_sul_indicator.val & 1) << (dci_size - pos);
+        // SS/PBCH index  6 bits
+        pos += 6;
+        *dci_pdu |= ((dci_pdu_rel15->ss_pbch_index & 0x3f) << (dci_size - pos));
+        //  prach_mask_index  4 bits
+        pos += 4;
+        *dci_pdu |= ((dci_pdu_rel15->prach_mask_index & 0xf) << (dci_size - pos));
+      } else {
+        // Time domain assignment 4bit
+        pos += 4;
+        *dci_pdu |= ((dci_pdu_rel15->time_domain_assignment.val & 0xf) << (dci_size - pos));
+        LOG_D(MAC,
+              "Time domain assignment %d (%d bits)=> %d (0x%lx)\n",
+              dci_pdu_rel15->time_domain_assignment.val,
+              4,
+              dci_size - pos,
+              *dci_pdu);
+        // VRB to PRB mapping  1bit
+        pos++;
+        *dci_pdu |= (dci_pdu_rel15->vrb_to_prb_mapping.val & 1) << (dci_size - pos);
+        LOG_D(MAC,
+              "VRB to PRB %d (%d bits)=> %d (0x%lx)\n",
+              dci_pdu_rel15->vrb_to_prb_mapping.val,
+              1,
+              dci_size - pos,
+              *dci_pdu);
         // MCS 5bit  //bit over 32, so dci_pdu ++
-        for (int i=0; i<5; i++)
-          *dci_pdu |= (((uint64_t)dci_pdu_rel15->mcs>>(4-i))&1)<<(dci_size-pos++);
-
-        // Redundancy version  2 bit
-        for (int i=0; i<2; i++)
-          *dci_pdu |= (((uint64_t)dci_pdu_rel15->rv>>(1-i))&1)<<(dci_size-pos++);
-
-        // System information indicator 1bit
-        *dci_pdu |= ((uint64_t)dci_pdu_rel15->system_info_indicator&1)<<(dci_size-pos++);
+        pos += 5;
+        *dci_pdu |= (dci_pdu_rel15->mcs & 0x1f) << (dci_size - pos);
+        LOG_D(MAC, "MCS %d (%d bits)=> %d (0x%lx)\n", dci_pdu_rel15->mcs, 5, dci_size - pos, *dci_pdu);
+        // New data indicator 1bit
+        pos++;
+        *dci_pdu |= (dci_pdu_rel15->ndi & 1) << (dci_size - pos);
+        LOG_D(MAC, "NDI %d (%d bits)=> %d (0x%lx)\n", dci_pdu_rel15->ndi, 1, dci_size - pos, *dci_pdu);
+        // Redundancy version  2bit
+        pos += 2;
+        *dci_pdu |= (dci_pdu_rel15->rv & 0x3) << (dci_size - pos);
+        LOG_D(MAC, "RV %d (%d bits)=> %d (0x%lx)\n", dci_pdu_rel15->rv, 2, dci_size - pos, *dci_pdu);
+        // HARQ process number  4bit
+        pos += 4;
+        *dci_pdu |= ((dci_pdu_rel15->harq_pid & 0xf) << (dci_size - pos));
+        LOG_D(MAC, "HARQ_PID %d (%d bits)=> %d (0x%lx)\n", dci_pdu_rel15->harq_pid, 4, dci_size - pos, *dci_pdu);
+        // Downlink assignment index  2bit
+        pos += 2;
+        *dci_pdu |= ((dci_pdu_rel15->dai[0].val & 3) << (dci_size - pos));
+        LOG_D(MAC, "DAI %d (%d bits)=> %d (0x%lx)\n", dci_pdu_rel15->dai[0].val, 2, dci_size - pos, *dci_pdu);
+        // TPC command for scheduled PUCCH  2bit
+        pos += 2;
+        *dci_pdu |= ((dci_pdu_rel15->tpc & 3) << (dci_size - pos));
+        LOG_D(MAC, "TPC %d (%d bits)=> %d (0x%lx)\n", dci_pdu_rel15->tpc, 2, dci_size - pos, *dci_pdu);
+        // PUCCH resource indicator  3bit
+        pos += 3;
+        *dci_pdu |= ((dci_pdu_rel15->pucch_resource_indicator & 0x7) << (dci_size - pos));
+        LOG_D(MAC,
+              "PUCCH RI %d (%d bits)=> %d (0x%lx)\n",
+              dci_pdu_rel15->pucch_resource_indicator,
+              3,
+              dci_size - pos,
+              *dci_pdu);
+        // PDSCH-to-HARQ_feedback timing indicator 3bit
+        pos += 3;
+        *dci_pdu |= ((dci_pdu_rel15->pdsch_to_harq_feedback_timing_indicator.val & 0x7) << (dci_size - pos));
+        LOG_D(MAC,
+              "PDSCH to HARQ TI %d (%d bits)=> %d (0x%lx)\n",
+              dci_pdu_rel15->pdsch_to_harq_feedback_timing_indicator.val,
+              3,
+              dci_size - pos,
+              *dci_pdu);
+      } // end else
+      break;
 
-        // reserved 15 bits
+    case NR_RNTI_P:
+      // Short Messages Indicator – 2 bits
+      for (int i = 0; i < 2; i++)
+        *dci_pdu |= (((uint64_t)dci_pdu_rel15->short_messages_indicator >> (1 - i)) & 1) << (dci_size - pos++);
+      // Short Messages – 8 bits
+      for (int i = 0; i < 8; i++)
+        *dci_pdu |= (((uint64_t)dci_pdu_rel15->short_messages >> (7 - i)) & 1) << (dci_size - pos++);
+      // Freq domain assignment 0-16 bit
+      fsize = (int)ceil(log2((N_RB * (N_RB + 1)) >> 1));
+      for (int i = 0; i < fsize; i++)
+        *dci_pdu |= (((uint64_t)dci_pdu_rel15->frequency_domain_assignment.val >> (fsize - i - 1)) & 1) << (dci_size - pos++);
+      // Time domain assignment 4 bit
+      for (int i = 0; i < 4; i++)
+        *dci_pdu |= (((uint64_t)dci_pdu_rel15->time_domain_assignment.val >> (3 - i)) & 1) << (dci_size - pos++);
+      // VRB to PRB mapping 1 bit
+      *dci_pdu |= ((uint64_t)dci_pdu_rel15->vrb_to_prb_mapping.val & 1) << (dci_size - pos++);
+      // MCS 5 bit
+      for (int i = 0; i < 5; i++)
+        *dci_pdu |= (((uint64_t)dci_pdu_rel15->mcs >> (4 - i)) & 1) << (dci_size - pos++);
+      // TB scaling 2 bit
+      for (int i = 0; i < 2; i++)
+        *dci_pdu |= (((uint64_t)dci_pdu_rel15->tb_scaling >> (1 - i)) & 1) << (dci_size - pos++);
+      break;
 
-        LOG_D(PHY,"dci_size = %i\n", dci_size);
-        LOG_D(PHY,"fsize = %i\n", fsize);
-        LOG_D(PHY,"dci_pdu_rel15->frequency_domain_assignment.val = %i\n", dci_pdu_rel15->frequency_domain_assignment.val);
-        LOG_D(PHY,"dci_pdu_rel15->time_domain_assignment.val = %i\n", dci_pdu_rel15->time_domain_assignment.val);
-        LOG_D(PHY,"dci_pdu_rel15->vrb_to_prb_mapping.val = %i\n", dci_pdu_rel15->vrb_to_prb_mapping.val);
-        LOG_D(PHY,"dci_pdu_rel15->mcs = %i\n", dci_pdu_rel15->mcs);
-        LOG_D(PHY,"dci_pdu_rel15->rv = %i\n", dci_pdu_rel15->rv);
-        LOG_D(PHY,"dci_pdu_rel15->system_info_indicator = %i\n", dci_pdu_rel15->system_info_indicator);
+    case NR_RNTI_SI:
+      pos = 1;
+      // Freq domain assignment 0-16 bit
+      fsize = (int)ceil(log2((N_RB * (N_RB + 1)) >> 1));
+      LOG_D(PHY, "fsize = %i\n", fsize);
+      for (int i = 0; i < fsize; i++)
+        *dci_pdu |= (((uint64_t)dci_pdu_rel15->frequency_domain_assignment.val >> (fsize - i - 1)) & 1) << (dci_size - pos++);
+      LOG_D(PHY, "dci_pdu_rel15->frequency_domain_assignment.val = %i\n", dci_pdu_rel15->frequency_domain_assignment.val);
+      // Time domain assignment 4 bit
+      for (int i = 0; i < 4; i++)
+        *dci_pdu |= (((uint64_t)dci_pdu_rel15->time_domain_assignment.val >> (3 - i)) & 1) << (dci_size - pos++);
+      LOG_D(PHY, "dci_pdu_rel15->time_domain_assignment.val = %i\n", dci_pdu_rel15->time_domain_assignment.val);
+      // VRB to PRB mapping 1 bit
+      *dci_pdu |= ((uint64_t)dci_pdu_rel15->vrb_to_prb_mapping.val & 1) << (dci_size - pos++);
+      LOG_D(PHY, "dci_pdu_rel15->vrb_to_prb_mapping.val = %i\n", dci_pdu_rel15->vrb_to_prb_mapping.val);
+      // MCS 5bit  //bit over 32, so dci_pdu ++
+      for (int i = 0; i < 5; i++)
+        *dci_pdu |= (((uint64_t)dci_pdu_rel15->mcs >> (4 - i)) & 1) << (dci_size - pos++);
+      LOG_D(PHY, "dci_pdu_rel15->mcs = %i\n", dci_pdu_rel15->mcs);
+      // Redundancy version  2bit
+      for (int i = 0; i < 2; i++)
+        *dci_pdu |= (((uint64_t)dci_pdu_rel15->rv >> (1 - i)) & 1) << (dci_size - pos++);
+      LOG_D(PHY, "dci_pdu_rel15->rv = %i\n", dci_pdu_rel15->rv);
+      // System information indicator 1bit
+      *dci_pdu |= ((uint64_t)dci_pdu_rel15->system_info_indicator&1)<<(dci_size-pos++);
+      LOG_D(PHY, "dci_pdu_rel15->system_info_indicator = %i\n", dci_pdu_rel15->system_info_indicator);
+      break;
 
-	break;
-	
-      case NR_RNTI_TC:
-	// indicating a DL DCI format 1bit
-	*dci_pdu |= ((uint64_t)dci_pdu_rel15->format_indicator&1)<<(dci_size-pos++);
-	// Freq domain assignment 0-16 bit
-	fsize = (int)ceil( log2( (N_RB*(N_RB+1))>>1 ) );
-	for (int i=0; i<fsize; i++)
-	  *dci_pdu |= (((uint64_t)dci_pdu_rel15->frequency_domain_assignment.val>>(fsize-i-1))&1)<<(dci_size-pos++);
-	// Time domain assignment 4 bit
-	for (int i=0; i<4; i++)
-	  *dci_pdu |= (((uint64_t)dci_pdu_rel15->time_domain_assignment.val>>(3-i))&1)<<(dci_size-pos++);
-	// VRB to PRB mapping 1 bit
-	*dci_pdu |= ((uint64_t)dci_pdu_rel15->vrb_to_prb_mapping.val&1)<<(dci_size-pos++);
-	// MCS 5bit  //bit over 32, so dci_pdu ++
-	for (int i=0; i<5; i++)
-	  *dci_pdu |= (((uint64_t)dci_pdu_rel15->mcs>>(4-i))&1)<<(dci_size-pos++);
-	// New data indicator 1bit
-	*dci_pdu |= ((uint64_t)dci_pdu_rel15->ndi&1)<<(dci_size-pos++);
-	// Redundancy version  2bit
-	for (int i=0; i<2; i++)
-	  *dci_pdu |= (((uint64_t)dci_pdu_rel15->rv>>(1-i))&1)<<(dci_size-pos++);
-	// HARQ process number  4bit
-	for (int i=0; i<4; i++)
-	  *dci_pdu  |= (((uint64_t)dci_pdu_rel15->harq_pid>>(3-i))&1)<<(dci_size-pos++);
-	
-	// Downlink assignment index – 2 bits
-	for (int i=0; i<2; i++)
-	  *dci_pdu  |= (((uint64_t)dci_pdu_rel15->dai[0].val>>(1-i))&1)<<(dci_size-pos++);
-	
-	// TPC command for scheduled PUCCH – 2 bits
-	for (int i=0; i<2; i++)
-	  *dci_pdu  |= (((uint64_t)dci_pdu_rel15->tpc>>(1-i))&1)<<(dci_size-pos++);
-	
-	
-	//      LOG_D(MAC, "DCI PDU: [0]->0x%08llx \t [1]->0x%08llx \t [2]->0x%08llx \t [3]->0x%08llx\n",
-	//	    dci_pdu[0], dci_pdu[1], dci_pdu[2], dci_pdu[3]);
-	
-	
-	// PDSCH-to-HARQ_feedback timing indicator – 3 bits
-	for (int i=0; i<3; i++)
-	  *dci_pdu  |= (((uint64_t)dci_pdu_rel15->pdsch_to_harq_feedback_timing_indicator.val>>(2-i))&1)<<(dci_size-pos++);
-	
-	break;
-      }
+    case NR_RNTI_TC:
+      // indicating a DL DCI format 1bit
+      *dci_pdu |= ((uint64_t)dci_pdu_rel15->format_indicator & 1) << (dci_size - pos++);
+      // Freq domain assignment 0-16 bit
+      fsize = (int)ceil(log2((N_RB * (N_RB + 1)) >> 1));
+      for (int i = 0; i < fsize; i++)
+        *dci_pdu |= (((uint64_t)dci_pdu_rel15->frequency_domain_assignment.val >> (fsize - i - 1)) & 1) << (dci_size - pos++);
+      // Time domain assignment 4 bit
+      for (int i = 0; i < 4; i++)
+        *dci_pdu |= (((uint64_t)dci_pdu_rel15->time_domain_assignment.val >> (3 - i)) & 1) << (dci_size - pos++);
+      // VRB to PRB mapping 1 bit
+      *dci_pdu |= ((uint64_t)dci_pdu_rel15->vrb_to_prb_mapping.val & 1) << (dci_size - pos++);
+      // MCS 5bit  //bit over 32, so dci_pdu ++
+      for (int i = 0; i < 5; i++)
+        *dci_pdu |= (((uint64_t)dci_pdu_rel15->mcs >> (4 - i)) & 1) << (dci_size - pos++);
+      // New data indicator 1bit
+      *dci_pdu |= ((uint64_t)dci_pdu_rel15->ndi & 1) << (dci_size - pos++);
+      // Redundancy version  2bit
+      for (int i = 0; i < 2; i++)
+        *dci_pdu |= (((uint64_t)dci_pdu_rel15->rv >> (1 - i)) & 1) << (dci_size - pos++);
+      // HARQ process number  4bit
+      for (int i = 0; i < 4; i++)
+        *dci_pdu |= (((uint64_t)dci_pdu_rel15->harq_pid >> (3 - i)) & 1) << (dci_size - pos++);
+      // Downlink assignment index – 2 bits
+      for (int i = 0; i < 2; i++)
+        *dci_pdu |= (((uint64_t)dci_pdu_rel15->dai[0].val >> (1 - i)) & 1) << (dci_size - pos++);
+      // TPC command for scheduled PUCCH – 2 bits
+      for (int i = 0; i < 2; i++)
+        *dci_pdu |= (((uint64_t)dci_pdu_rel15->tpc >> (1 - i)) & 1) << (dci_size - pos++);
+      // PDSCH-to-HARQ_feedback timing indicator – 3 bits
+      for (int i = 0; i < 3; i++)
+        *dci_pdu |= (((uint64_t)dci_pdu_rel15->pdsch_to_harq_feedback_timing_indicator.val >> (2 - i)) & 1) << (dci_size - pos++);
       break;
-      
-    case NR_UL_DCI_FORMAT_0_0:
-      switch(rnti_types[d])
-	{
-	case NR_RNTI_C:
-	  // indicating a DL DCI format 1bit
-	  *dci_pdu |= ((uint64_t)dci_pdu_rel15->format_indicator&1)<<(dci_size-pos++);
-	  // Freq domain assignment  max 16 bit
-	  fsize = (int)ceil( log2( (N_RB*(N_RB+1))>>1 ) );
-	  for (int i=0; i<fsize; i++)
-	    *dci_pdu |= ((dci_pdu_rel15->frequency_domain_assignment.val>>(fsize-i-1))&1)<<(dci_size-pos++);
-	  // Time domain assignment 4bit
-	  for (int i=0; i<4; i++)
-	    *dci_pdu |= (((uint64_t)dci_pdu_rel15->time_domain_assignment.val>>(3-i))&1)<<(dci_size-pos++);
-	  // Frequency hopping flag – 1 bit
-	  *dci_pdu |= ((uint64_t)dci_pdu_rel15->frequency_hopping_flag.val&1)<<(dci_size-pos++);
-	  // MCS  5 bit
-	  for (int i=0; i<5; i++)
-	    *dci_pdu |= (((uint64_t)dci_pdu_rel15->mcs>>(4-i))&1)<<(dci_size-pos++);
-	  // New data indicator 1bit
-	  *dci_pdu |= ((uint64_t)dci_pdu_rel15->ndi&1)<<(dci_size-pos++);
-	  // Redundancy version  2bit
-	  for (int i=0; i<2; i++)
-	    *dci_pdu |= (((uint64_t)dci_pdu_rel15->rv>>(1-i))&1)<<(dci_size-pos++);
-	  // HARQ process number  4bit
-	  for (int i=0; i<4; i++)
-	    *dci_pdu  |= (((uint64_t)dci_pdu_rel15->harq_pid>>(3-i))&1)<<(dci_size-pos++);
-	  
-	  // TPC command for scheduled PUSCH – 2 bits
-	  for (int i=0; i<2; i++)
-	    *dci_pdu |= (((uint64_t)dci_pdu_rel15->tpc>>(1-i))&1)<<(dci_size-pos++);
-	  
-	  // Padding bits
-	  for(int a = pos;a<32;a++)
-	    *dci_pdu |= ((uint64_t)dci_pdu_rel15->padding&1)<<(dci_size-pos++);
-	  
-	  // UL/SUL indicator – 1 bit
-	  /* commented for now (RK): need to get this from BWP descriptor
-	  if (cfg->pucch_config.pucch_GroupHopping.value)
-	    *dci_pdu |= ((uint64_t)dci_pdu_rel15->ul_sul_indicator.val&1)<<(dci_size-pos++);
-	    */
-	  break;
-	  
-	case NFAPI_NR_RNTI_TC:
-	  
-	  // indicating a DL DCI format 1bit
-	  *dci_pdu |= (dci_pdu_rel15->format_indicator&1)<<(dci_size-pos++);
-	  // Freq domain assignment  max 16 bit
-	  fsize = (int)ceil( log2( (N_RB*(N_RB+1))>>1 ) );
-	  for (int i=0; i<fsize; i++)
-	    *dci_pdu |= ((dci_pdu_rel15->frequency_domain_assignment.val>>(fsize-i-1))&1)<<(dci_size-pos++);
-	  // Time domain assignment 4bit
-	  for (int i=0; i<4; i++)
-	    *dci_pdu |= (((uint64_t)dci_pdu_rel15->time_domain_assignment.val>>(3-i))&1)<<(dci_size-pos++);
-	  // Frequency hopping flag – 1 bit
-	  *dci_pdu |= ((uint64_t)dci_pdu_rel15->frequency_hopping_flag.val&1)<<(dci_size-pos++);
-	  // MCS  5 bit
-	  for (int i=0; i<5; i++)
-	    *dci_pdu |= (((uint64_t)dci_pdu_rel15->mcs>>(4-i))&1)<<(dci_size-pos++);
-	  // New data indicator 1bit
-	  *dci_pdu |= ((uint64_t)dci_pdu_rel15->ndi&1)<<(dci_size-pos++);
-	  // Redundancy version  2bit
-	  for (int i=0; i<2; i++)
-	    *dci_pdu |= (((uint64_t)dci_pdu_rel15->rv>>(1-i))&1)<<(dci_size-pos++);
-	  // HARQ process number  4bit
-	  for (int i=0; i<4; i++)
-	    *dci_pdu  |= (((uint64_t)dci_pdu_rel15->harq_pid>>(3-i))&1)<<(dci_size-pos++);
-	  
-	  // TPC command for scheduled PUSCH – 2 bits
-	  for (int i=0; i<2; i++)
-	    *dci_pdu |= (((uint64_t)dci_pdu_rel15->tpc>>(1-i))&1)<<(dci_size-pos++);
-	  
-	  // Padding bits
-	  for(int a = pos;a<32;a++)
-	    *dci_pdu |= ((uint64_t)dci_pdu_rel15->padding&1)<<(dci_size-pos++);
-	  
-	  // UL/SUL indicator – 1 bit
-	  /*
-	    commented for now (RK): need to get this information from BWP descriptor
-	    if (cfg->pucch_config.pucch_GroupHopping.value)
-	    *dci_pdu |= ((uint64_t)dci_pdu_rel15->ul_sul_indicator.val&1)<<(dci_size-pos++);
-	    */
-	  break;
-	  
-	    }
+    }
+    break;
+
+  case NR_UL_DCI_FORMAT_0_0:
+    switch (rnti_type) {
+    case NR_RNTI_C:
+      // indicating a DL DCI format 1bit
+      *dci_pdu |= ((uint64_t)dci_pdu_rel15->format_indicator & 1) << (dci_size - pos++);
+      // Freq domain assignment  max 16 bit
+      fsize = (int)ceil(log2((N_RB * (N_RB + 1)) >> 1));
+      for (int i = 0; i < fsize; i++)
+        *dci_pdu |= ((dci_pdu_rel15->frequency_domain_assignment.val >> (fsize - i - 1)) & 1) << (dci_size - pos++);
+      // Time domain assignment 4bit
+      for (int i = 0; i < 4; i++)
+        *dci_pdu |= (((uint64_t)dci_pdu_rel15->time_domain_assignment.val >> (3 - i)) & 1) << (dci_size - pos++);
+      // Frequency hopping flag – 1 bit
+      *dci_pdu |= ((uint64_t)dci_pdu_rel15->frequency_hopping_flag.val & 1) << (dci_size - pos++);
+      // MCS  5 bit
+      for (int i = 0; i < 5; i++)
+        *dci_pdu |= (((uint64_t)dci_pdu_rel15->mcs >> (4 - i)) & 1) << (dci_size - pos++);
+      // New data indicator 1bit
+      *dci_pdu |= ((uint64_t)dci_pdu_rel15->ndi & 1) << (dci_size - pos++);
+      // Redundancy version  2bit
+      for (int i = 0; i < 2; i++)
+        *dci_pdu |= (((uint64_t)dci_pdu_rel15->rv >> (1 - i)) & 1) << (dci_size - pos++);
+      // HARQ process number  4bit
+      for (int i = 0; i < 4; i++)
+        *dci_pdu |= (((uint64_t)dci_pdu_rel15->harq_pid >> (3 - i)) & 1) << (dci_size - pos++);
+      // TPC command for scheduled PUSCH – 2 bits
+      for (int i = 0; i < 2; i++)
+        *dci_pdu |= (((uint64_t)dci_pdu_rel15->tpc >> (1 - i)) & 1) << (dci_size - pos++);
+      // Padding bits
+      for (int a = pos; a < 32; a++)
+        *dci_pdu |= ((uint64_t)dci_pdu_rel15->padding & 1) << (dci_size - pos++);
+      // UL/SUL indicator – 1 bit
+      /* commented for now (RK): need to get this from BWP descriptor
+      if (cfg->pucch_config.pucch_GroupHopping.value)
+        *dci_pdu |=
+      ((uint64_t)dci_pdu_rel15->ul_sul_indicator.val&1)<<(dci_size-pos++);
+        */
       break;
 
-    case NR_UL_DCI_FORMAT_0_1:
-      switch(rnti_types[d])
-	{
-	case NR_RNTI_C:
-          // Indicating a DL DCI format 1bit
-          pos=1;
-          *dci_pdu |= ((uint64_t)dci_pdu_rel15->format_indicator&0x1)<<(dci_size-pos);
-
-          // Carrier indicator
-          pos+=dci_pdu_rel15->carrier_indicator.nbits;
-          *dci_pdu |= ((uint64_t)dci_pdu_rel15->carrier_indicator.val&((1<<dci_pdu_rel15->carrier_indicator.nbits)-1))<<(dci_size-pos);
-
-          // UL/SUL Indicator
-          pos+=dci_pdu_rel15->ul_sul_indicator.nbits;
-          *dci_pdu |= ((uint64_t)dci_pdu_rel15->ul_sul_indicator.val&((1<<dci_pdu_rel15->ul_sul_indicator.nbits)-1))<<(dci_size-pos);
-
-          // BWP indicator
-          pos+=dci_pdu_rel15->bwp_indicator.nbits;
-          *dci_pdu |= ((uint64_t)dci_pdu_rel15->bwp_indicator.val&((1<<dci_pdu_rel15->bwp_indicator.nbits)-1))<<(dci_size-pos);
-
-          // Frequency domain resource assignment
-          pos+=dci_pdu_rel15->frequency_domain_assignment.nbits;
-          *dci_pdu |= ((uint64_t)dci_pdu_rel15->frequency_domain_assignment.val&((1<<dci_pdu_rel15->frequency_domain_assignment.nbits)-1)) << (dci_size-pos);
-
-          // Time domain resource assignment
-          pos+=dci_pdu_rel15->time_domain_assignment.nbits;
-          *dci_pdu |= ((uint64_t)dci_pdu_rel15->time_domain_assignment.val&((1<<dci_pdu_rel15->time_domain_assignment.nbits)-1)) << (dci_size-pos);
-
-          // Frequency hopping
-          pos+=dci_pdu_rel15->frequency_hopping_flag.nbits;
-          *dci_pdu |= ((uint64_t)dci_pdu_rel15->frequency_hopping_flag.val&((1<<dci_pdu_rel15->frequency_hopping_flag.nbits)-1)) << (dci_size-pos);
-
-          // MCS 5bit
-          pos+=5;
-          *dci_pdu |= ((uint64_t)dci_pdu_rel15->mcs&0x1f)<<(dci_size-pos);
-
-          // New data indicator 1bit
-          pos+=1;
-          *dci_pdu |= ((uint64_t)dci_pdu_rel15->ndi&0x1)<<(dci_size-pos);
-
-          // Redundancy version  2bit
-          pos+=2;
-          *dci_pdu |= ((uint64_t)dci_pdu_rel15->rv&0x3)<<(dci_size-pos);
-
-          // HARQ process number  4bit
-          pos+=4;
-          *dci_pdu |= ((uint64_t)dci_pdu_rel15->harq_pid&0xf)<<(dci_size-pos);
-
-          // 1st Downlink assignment index
-          pos+=dci_pdu_rel15->dai[0].nbits;
-          *dci_pdu |= ((uint64_t)dci_pdu_rel15->dai[0].val&((1<<dci_pdu_rel15->dai[0].nbits)-1))<<(dci_size-pos);
-
-          // 2nd Downlink assignment index
-          pos+=dci_pdu_rel15->dai[1].nbits;
-          *dci_pdu |= ((uint64_t)dci_pdu_rel15->dai[1].val&((1<<dci_pdu_rel15->dai[1].nbits)-1))<<(dci_size-pos);
-
-          // TPC command for scheduled PUSCH  2bit
-          pos+=2;
-          *dci_pdu |= ((uint64_t)dci_pdu_rel15->tpc&0x3)<<(dci_size-pos);
-
-          // SRS resource indicator
-          pos+=dci_pdu_rel15->srs_resource_indicator.nbits;
-          *dci_pdu |= ((uint64_t)dci_pdu_rel15->srs_resource_indicator.val&((1<<dci_pdu_rel15->srs_resource_indicator.nbits)-1))<<(dci_size-pos);
-
-          // Precoding info and n. of layers
-          pos+=dci_pdu_rel15->precoding_information.nbits;
-          *dci_pdu |= ((uint64_t)dci_pdu_rel15->precoding_information.val&((1<<dci_pdu_rel15->precoding_information.nbits)-1))<<(dci_size-pos);
-
-          // Antenna ports
-          pos+=dci_pdu_rel15->antenna_ports.nbits;
-          *dci_pdu |= ((uint64_t)dci_pdu_rel15->antenna_ports.val&((1<<dci_pdu_rel15->antenna_ports.nbits)-1))<<(dci_size-pos);
-
-          // SRS request
-          pos+=dci_pdu_rel15->srs_request.nbits;
-          *dci_pdu |= ((uint64_t)dci_pdu_rel15->srs_request.val&((1<<dci_pdu_rel15->srs_request.nbits)-1))<<(dci_size-pos);
-
-          // CSI request
-          pos+=dci_pdu_rel15->csi_request.nbits;
-          *dci_pdu |= ((uint64_t)dci_pdu_rel15->csi_request.val&((1<<dci_pdu_rel15->csi_request.nbits)-1))<<(dci_size-pos);
-
-          // CBG transmission information
-          pos+=dci_pdu_rel15->cbgti.nbits;
-          *dci_pdu |= ((uint64_t)dci_pdu_rel15->cbgti.val&((1<<dci_pdu_rel15->cbgti.nbits)-1))<<(dci_size-pos);
-
-          // PTRS DMRS association
-          pos+=dci_pdu_rel15->ptrs_dmrs_association.nbits;
-          *dci_pdu |= ((uint64_t)dci_pdu_rel15->ptrs_dmrs_association.val&((1<<dci_pdu_rel15->ptrs_dmrs_association.nbits)-1))<<(dci_size-pos);
-
-          // Beta offset indicator
-          pos+=dci_pdu_rel15->beta_offset_indicator.nbits;
-          *dci_pdu |= ((uint64_t)dci_pdu_rel15->beta_offset_indicator.val&((1<<dci_pdu_rel15->beta_offset_indicator.nbits)-1))<<(dci_size-pos);
-
-          // DMRS sequence initialization
-          pos+=dci_pdu_rel15->dmrs_sequence_initialization.nbits;
-          *dci_pdu |= ((uint64_t)dci_pdu_rel15->dmrs_sequence_initialization.val&((1<<dci_pdu_rel15->dmrs_sequence_initialization.nbits)-1))<<(dci_size-pos);
-
-          // UL-SCH indicator
-          pos+=1;
-          *dci_pdu |= ((uint64_t)dci_pdu_rel15->ulsch_indicator&0x1)<<(dci_size-pos);
-
-	  break;
-	    }
+    case NFAPI_NR_RNTI_TC:
+      // indicating a DL DCI format 1bit
+      *dci_pdu |= (dci_pdu_rel15->format_indicator & 1) << (dci_size - pos++);
+      // Freq domain assignment  max 16 bit
+      fsize = (int)ceil(log2((N_RB * (N_RB + 1)) >> 1));
+      for (int i = 0; i < fsize; i++)
+        *dci_pdu |= ((dci_pdu_rel15->frequency_domain_assignment.val >> (fsize - i - 1)) & 1) << (dci_size - pos++);
+      // Time domain assignment 4bit
+      for (int i = 0; i < 4; i++)
+        *dci_pdu |= (((uint64_t)dci_pdu_rel15->time_domain_assignment.val >> (3 - i)) & 1) << (dci_size - pos++);
+      // Frequency hopping flag – 1 bit
+      *dci_pdu |= ((uint64_t)dci_pdu_rel15->frequency_hopping_flag.val & 1) << (dci_size - pos++);
+      // MCS  5 bit
+      for (int i = 0; i < 5; i++)
+        *dci_pdu |= (((uint64_t)dci_pdu_rel15->mcs >> (4 - i)) & 1) << (dci_size - pos++);
+      // New data indicator 1bit
+      *dci_pdu |= ((uint64_t)dci_pdu_rel15->ndi & 1) << (dci_size - pos++);
+      // Redundancy version  2bit
+      for (int i = 0; i < 2; i++)
+        *dci_pdu |= (((uint64_t)dci_pdu_rel15->rv >> (1 - i)) & 1) << (dci_size - pos++);
+      // HARQ process number  4bit
+      for (int i = 0; i < 4; i++)
+        *dci_pdu |= (((uint64_t)dci_pdu_rel15->harq_pid >> (3 - i)) & 1) << (dci_size - pos++);
+      // TPC command for scheduled PUSCH – 2 bits
+      for (int i = 0; i < 2; i++)
+        *dci_pdu |= (((uint64_t)dci_pdu_rel15->tpc >> (1 - i)) & 1) << (dci_size - pos++);
+      // Padding bits
+      for (int a = pos; a < 32; a++)
+        *dci_pdu |= ((uint64_t)dci_pdu_rel15->padding & 1) << (dci_size - pos++);
+      // UL/SUL indicator – 1 bit
+      /*
+        commented for now (RK): need to get this information from BWP descriptor
+        if (cfg->pucch_config.pucch_GroupHopping.value)
+        *dci_pdu |=
+        ((uint64_t)dci_pdu_rel15->ul_sul_indicator.val&1)<<(dci_size-pos++);
+        */
       break;
+    }
+    break;
 
-    case NR_DL_DCI_FORMAT_1_1:
+  case NR_UL_DCI_FORMAT_0_1:
+    switch (rnti_type) {
+    case NR_RNTI_C:
       // Indicating a DL DCI format 1bit
-      pos=1;
-      *dci_pdu |= ((uint64_t)dci_pdu_rel15->format_indicator&0x1)<<(dci_size-pos);
-
+      pos = 1;
+      *dci_pdu |= ((uint64_t)dci_pdu_rel15->format_indicator & 0x1) << (dci_size - pos);
       // Carrier indicator
-      pos+=dci_pdu_rel15->carrier_indicator.nbits;
-      *dci_pdu |= ((uint64_t)dci_pdu_rel15->carrier_indicator.val&((1<<dci_pdu_rel15->carrier_indicator.nbits)-1))<<(dci_size-pos);
-
+      pos += dci_pdu_rel15->carrier_indicator.nbits;
+      *dci_pdu |= ((uint64_t)dci_pdu_rel15->carrier_indicator.val & ((1 << dci_pdu_rel15->carrier_indicator.nbits) - 1)) << (dci_size - pos);
+      // UL/SUL Indicator
+      pos += dci_pdu_rel15->ul_sul_indicator.nbits;
+      *dci_pdu |= ((uint64_t)dci_pdu_rel15->ul_sul_indicator.val & ((1 << dci_pdu_rel15->ul_sul_indicator.nbits) - 1)) << (dci_size - pos);
       // BWP indicator
-      pos+=dci_pdu_rel15->bwp_indicator.nbits;
-      *dci_pdu |= ((uint64_t)dci_pdu_rel15->bwp_indicator.val&((1<<dci_pdu_rel15->bwp_indicator.nbits)-1))<<(dci_size-pos);
-
+      pos += dci_pdu_rel15->bwp_indicator.nbits;
+      *dci_pdu |= ((uint64_t)dci_pdu_rel15->bwp_indicator.val & ((1 << dci_pdu_rel15->bwp_indicator.nbits) - 1)) << (dci_size - pos);
       // Frequency domain resource assignment
-      pos+=dci_pdu_rel15->frequency_domain_assignment.nbits;
-      *dci_pdu |= ((uint64_t)dci_pdu_rel15->frequency_domain_assignment.val&((1<<dci_pdu_rel15->frequency_domain_assignment.nbits)-1)) << (dci_size-pos);
-
+      pos += dci_pdu_rel15->frequency_domain_assignment.nbits;
+      *dci_pdu |= ((uint64_t)dci_pdu_rel15->frequency_domain_assignment.val & ((1 << dci_pdu_rel15->frequency_domain_assignment.nbits) - 1)) << (dci_size - pos);
       // Time domain resource assignment
-      pos+=dci_pdu_rel15->time_domain_assignment.nbits;
-      *dci_pdu |= ((uint64_t)dci_pdu_rel15->time_domain_assignment.val&((1<<dci_pdu_rel15->time_domain_assignment.nbits)-1)) << (dci_size-pos);
-
-      // VRB-to-PRB mapping
-      pos+=dci_pdu_rel15->vrb_to_prb_mapping.nbits;
-      *dci_pdu |= ((uint64_t)dci_pdu_rel15->vrb_to_prb_mapping.val&((1<<dci_pdu_rel15->vrb_to_prb_mapping.nbits)-1))<<(dci_size-pos);
-      
-      // PRB bundling size indicator
-      pos+=dci_pdu_rel15->prb_bundling_size_indicator.nbits;
-      *dci_pdu |= ((uint64_t)dci_pdu_rel15->prb_bundling_size_indicator.val&((1<<dci_pdu_rel15->prb_bundling_size_indicator.nbits)-1))<<(dci_size-pos);
-
-      // Rate matching indicator
-      pos+=dci_pdu_rel15->rate_matching_indicator.nbits;
-      *dci_pdu |= ((uint64_t)dci_pdu_rel15->rate_matching_indicator.val&((1<<dci_pdu_rel15->rate_matching_indicator.nbits)-1))<<(dci_size-pos);
-
-      // ZP CSI-RS trigger
-      pos+=dci_pdu_rel15->zp_csi_rs_trigger.nbits;
-      *dci_pdu |= ((uint64_t)dci_pdu_rel15->zp_csi_rs_trigger.val&((1<<dci_pdu_rel15->zp_csi_rs_trigger.nbits)-1)) << (dci_size-pos);
-
-      //TB1
+      pos += dci_pdu_rel15->time_domain_assignment.nbits;
+      *dci_pdu |= ((uint64_t)dci_pdu_rel15->time_domain_assignment.val & ((1 << dci_pdu_rel15->time_domain_assignment.nbits) - 1)) << (dci_size - pos);
+      // Frequency hopping
+      pos += dci_pdu_rel15->frequency_hopping_flag.nbits;
+      *dci_pdu |= ((uint64_t)dci_pdu_rel15->frequency_hopping_flag.val & ((1 << dci_pdu_rel15->frequency_hopping_flag.nbits) - 1)) << (dci_size - pos);
       // MCS 5bit
-      pos+=5;
-      *dci_pdu |= ((uint64_t)dci_pdu_rel15->mcs&0x1f)<<(dci_size-pos);
-      
-      // New data indicator 1bit
-      pos+=1;
-      *dci_pdu |= ((uint64_t)dci_pdu_rel15->ndi&0x1)<<(dci_size-pos);
-      
-      // Redundancy version  2bit
-      pos+=2;
-      *dci_pdu |= ((uint64_t)dci_pdu_rel15->rv&0x3)<<(dci_size-pos);
-
-      //TB2
-      // MCS 5bit
-      pos+=dci_pdu_rel15->mcs2.nbits;
-      *dci_pdu |= ((uint64_t)dci_pdu_rel15->mcs2.val&((1<<dci_pdu_rel15->mcs2.nbits)-1))<<(dci_size-pos);
-      
+      pos += 5;
+      *dci_pdu |= ((uint64_t)dci_pdu_rel15->mcs & 0x1f) << (dci_size - pos);
       // New data indicator 1bit
-      pos+=dci_pdu_rel15->ndi2.nbits;
-      *dci_pdu |= ((uint64_t)dci_pdu_rel15->ndi2.val&((1<<dci_pdu_rel15->ndi2.nbits)-1))<<(dci_size-pos);
-      
+      pos += 1;
+      *dci_pdu |= ((uint64_t)dci_pdu_rel15->ndi & 0x1) << (dci_size - pos);
       // Redundancy version  2bit
-      pos+=dci_pdu_rel15->rv2.nbits;
-      *dci_pdu |= ((uint64_t)dci_pdu_rel15->rv2.val&((1<<dci_pdu_rel15->rv2.nbits)-1))<<(dci_size-pos);
-
+      pos += 2;
+      *dci_pdu |= ((uint64_t)dci_pdu_rel15->rv & 0x3) << (dci_size - pos);
       // HARQ process number  4bit
-      pos+=4;
-      *dci_pdu |= ((uint64_t)dci_pdu_rel15->harq_pid&0xf)<<(dci_size-pos);
-
-      // Downlink assignment index
-      pos+=dci_pdu_rel15->dai[0].nbits;
-      *dci_pdu |= ((uint64_t)dci_pdu_rel15->dai[0].val&((1<<dci_pdu_rel15->dai[0].nbits)-1))<<(dci_size-pos);
-
-      // TPC command for scheduled PUCCH  2bit
-      pos+=2;
-      *dci_pdu |= ((uint64_t)dci_pdu_rel15->tpc&0x3)<<(dci_size-pos);
-      
-      // PUCCH resource indicator  3bit
-      pos+=3;
-      *dci_pdu |= ((uint64_t)dci_pdu_rel15->pucch_resource_indicator&0x7)<<(dci_size-pos);
-
-      // PDSCH-to-HARQ_feedback timing indicator
-      pos+=dci_pdu_rel15->pdsch_to_harq_feedback_timing_indicator.nbits;
-      *dci_pdu |= ((uint64_t)dci_pdu_rel15->pdsch_to_harq_feedback_timing_indicator.val&((1<<dci_pdu_rel15->pdsch_to_harq_feedback_timing_indicator.nbits)-1))<<(dci_size-pos);
-
+      pos += 4;
+      *dci_pdu |= ((uint64_t)dci_pdu_rel15->harq_pid & 0xf) << (dci_size - pos);
+      // 1st Downlink assignment index
+      pos += dci_pdu_rel15->dai[0].nbits;
+      *dci_pdu |= ((uint64_t)dci_pdu_rel15->dai[0].val & ((1 << dci_pdu_rel15->dai[0].nbits) - 1)) << (dci_size - pos);
+      // 2nd Downlink assignment index
+      pos += dci_pdu_rel15->dai[1].nbits;
+      *dci_pdu |= ((uint64_t)dci_pdu_rel15->dai[1].val & ((1 << dci_pdu_rel15->dai[1].nbits) - 1)) << (dci_size - pos);
+      // TPC command for scheduled PUSCH  2bit
+      pos += 2;
+      *dci_pdu |= ((uint64_t)dci_pdu_rel15->tpc & 0x3) << (dci_size - pos);
+      // SRS resource indicator
+      pos += dci_pdu_rel15->srs_resource_indicator.nbits;
+      *dci_pdu |= ((uint64_t)dci_pdu_rel15->srs_resource_indicator.val & ((1 << dci_pdu_rel15->srs_resource_indicator.nbits) - 1)) << (dci_size - pos);
+      // Precoding info and n. of layers
+      pos += dci_pdu_rel15->precoding_information.nbits;
+      *dci_pdu |= ((uint64_t)dci_pdu_rel15->precoding_information.val & ((1 << dci_pdu_rel15->precoding_information.nbits) - 1)) << (dci_size - pos);
       // Antenna ports
-      pos+=dci_pdu_rel15->antenna_ports.nbits;
-      *dci_pdu |= ((uint64_t)dci_pdu_rel15->antenna_ports.val&((1<<dci_pdu_rel15->antenna_ports.nbits)-1))<<(dci_size-pos);
-
-      // TCI
-      pos+=dci_pdu_rel15->transmission_configuration_indication.nbits;
-      *dci_pdu |= ((uint64_t)dci_pdu_rel15->transmission_configuration_indication.val&((1<<dci_pdu_rel15->transmission_configuration_indication.nbits)-1))<<(dci_size-pos);
-
+      pos += dci_pdu_rel15->antenna_ports.nbits;
+      *dci_pdu |= ((uint64_t)dci_pdu_rel15->antenna_ports.val & ((1 << dci_pdu_rel15->antenna_ports.nbits) - 1)) << (dci_size - pos);
       // SRS request
-      pos+=dci_pdu_rel15->srs_request.nbits;
-      *dci_pdu |= ((uint64_t)dci_pdu_rel15->srs_request.val&((1<<dci_pdu_rel15->srs_request.nbits)-1))<<(dci_size-pos);
-
+      pos += dci_pdu_rel15->srs_request.nbits;
+      *dci_pdu |= ((uint64_t)dci_pdu_rel15->srs_request.val & ((1 << dci_pdu_rel15->srs_request.nbits) - 1)) << (dci_size - pos);
+      // CSI request
+      pos += dci_pdu_rel15->csi_request.nbits;
+      *dci_pdu |= ((uint64_t)dci_pdu_rel15->csi_request.val & ((1 << dci_pdu_rel15->csi_request.nbits) - 1)) << (dci_size - pos);
       // CBG transmission information
-      pos+=dci_pdu_rel15->cbgti.nbits;
-      *dci_pdu |= ((uint64_t)dci_pdu_rel15->cbgti.val&((1<<dci_pdu_rel15->cbgti.nbits)-1))<<(dci_size-pos);
-
-      // CBG flushing out information
-      pos+=dci_pdu_rel15->cbgfi.nbits;
-      *dci_pdu |= ((uint64_t)dci_pdu_rel15->cbgfi.val&((1<<dci_pdu_rel15->cbgfi.nbits)-1))<<(dci_size-pos);
-
-      // DMRS sequence init
-      pos+=1;
-      *dci_pdu |= ((uint64_t)dci_pdu_rel15->dmrs_sequence_initialization.val&0x1)<<(dci_size-pos);
+      pos += dci_pdu_rel15->cbgti.nbits;
+      *dci_pdu |= ((uint64_t)dci_pdu_rel15->cbgti.val & ((1 << dci_pdu_rel15->cbgti.nbits) - 1)) << (dci_size - pos);
+      // PTRS DMRS association
+      pos += dci_pdu_rel15->ptrs_dmrs_association.nbits;
+      *dci_pdu |= ((uint64_t)dci_pdu_rel15->ptrs_dmrs_association.val & ((1 << dci_pdu_rel15->ptrs_dmrs_association.nbits) - 1)) << (dci_size - pos);
+      // Beta offset indicator
+      pos += dci_pdu_rel15->beta_offset_indicator.nbits;
+      *dci_pdu |= ((uint64_t)dci_pdu_rel15->beta_offset_indicator.val & ((1 << dci_pdu_rel15->beta_offset_indicator.nbits) - 1)) << (dci_size - pos);
+      // DMRS sequence initialization
+      pos += dci_pdu_rel15->dmrs_sequence_initialization.nbits;
+      *dci_pdu |= ((uint64_t)dci_pdu_rel15->dmrs_sequence_initialization.val & ((1 << dci_pdu_rel15->dmrs_sequence_initialization.nbits) - 1)) << (dci_size - pos);
+      // UL-SCH indicator
+      pos += 1;
+      *dci_pdu |= ((uint64_t)dci_pdu_rel15->ulsch_indicator & 0x1) << (dci_size - pos);
+      break;
     }
-    LOG_D(MAC, "DCI index %d has %d bits and the payload is %lx\n", d, dci_size, *dci_pdu);
-  }
-}
-
-  
-    /*
-      int nr_is_dci_opportunity(nfapi_nr_search_space_t search_space,
-      nfapi_nr_coreset_t coreset,
-      uint16_t frame,
-      uint16_t slot,
-      nfapi_nr_config_request_scf_t cfg) {
-      
-      AssertFatal(search_space.coreset_id==coreset.coreset_id, "Invalid association of coreset(%d) and search space(%d)\n",
-      search_space.search_space_id, coreset.coreset_id);
-      
-      uint8_t is_dci_opportunity=0;
-      uint16_t Ks=search_space.slot_monitoring_periodicity;
-      uint16_t Os=search_space.slot_monitoring_offset;
-      uint8_t Ts=search_space.duration;
-      
-      if (((frame*get_spf(&cfg) + slot - Os)%Ks)<Ts)
-    is_dci_opportunity=1;
+    break;
 
-  return is_dci_opportunity;
+  case NR_DL_DCI_FORMAT_1_1:
+    // Indicating a DL DCI format 1bit
+    pos = 1;
+    *dci_pdu |= ((uint64_t)dci_pdu_rel15->format_indicator & 0x1) << (dci_size - pos);
+    // Carrier indicator
+    pos += dci_pdu_rel15->carrier_indicator.nbits;
+    *dci_pdu |= ((uint64_t)dci_pdu_rel15->carrier_indicator.val & ((1 << dci_pdu_rel15->carrier_indicator.nbits) - 1)) << (dci_size - pos);
+    // BWP indicator
+    pos += dci_pdu_rel15->bwp_indicator.nbits;
+    *dci_pdu |= ((uint64_t)dci_pdu_rel15->bwp_indicator.val & ((1 << dci_pdu_rel15->bwp_indicator.nbits) - 1)) << (dci_size - pos);
+    // Frequency domain resource assignment
+    pos += dci_pdu_rel15->frequency_domain_assignment.nbits;
+    *dci_pdu |= ((uint64_t)dci_pdu_rel15->frequency_domain_assignment.val & ((1 << dci_pdu_rel15->frequency_domain_assignment.nbits) - 1)) << (dci_size - pos);
+    // Time domain resource assignment
+    pos += dci_pdu_rel15->time_domain_assignment.nbits;
+    *dci_pdu |= ((uint64_t)dci_pdu_rel15->time_domain_assignment.val & ((1 << dci_pdu_rel15->time_domain_assignment.nbits) - 1)) << (dci_size - pos);
+    // VRB-to-PRB mapping
+    pos += dci_pdu_rel15->vrb_to_prb_mapping.nbits;
+    *dci_pdu |= ((uint64_t)dci_pdu_rel15->vrb_to_prb_mapping.val & ((1 << dci_pdu_rel15->vrb_to_prb_mapping.nbits) - 1)) << (dci_size - pos);
+    // PRB bundling size indicator
+    pos += dci_pdu_rel15->prb_bundling_size_indicator.nbits;
+    *dci_pdu |= ((uint64_t)dci_pdu_rel15->prb_bundling_size_indicator.val & ((1 << dci_pdu_rel15->prb_bundling_size_indicator.nbits) - 1)) << (dci_size - pos);
+    // Rate matching indicator
+    pos += dci_pdu_rel15->rate_matching_indicator.nbits;
+    *dci_pdu |= ((uint64_t)dci_pdu_rel15->rate_matching_indicator.val & ((1 << dci_pdu_rel15->rate_matching_indicator.nbits) - 1)) << (dci_size - pos);
+    // ZP CSI-RS trigger
+    pos += dci_pdu_rel15->zp_csi_rs_trigger.nbits;
+    *dci_pdu |= ((uint64_t)dci_pdu_rel15->zp_csi_rs_trigger.val & ((1 << dci_pdu_rel15->zp_csi_rs_trigger.nbits) - 1)) << (dci_size - pos);
+    // TB1
+    // MCS 5bit
+    pos += 5;
+    *dci_pdu |= ((uint64_t)dci_pdu_rel15->mcs & 0x1f) << (dci_size - pos);
+    // New data indicator 1bit
+    pos += 1;
+    *dci_pdu |= ((uint64_t)dci_pdu_rel15->ndi & 0x1) << (dci_size - pos);
+    // Redundancy version  2bit
+    pos += 2;
+    *dci_pdu |= ((uint64_t)dci_pdu_rel15->rv & 0x3) << (dci_size - pos);
+    // TB2
+    // MCS 5bit
+    pos += dci_pdu_rel15->mcs2.nbits;
+    *dci_pdu |= ((uint64_t)dci_pdu_rel15->mcs2.val & ((1 << dci_pdu_rel15->mcs2.nbits) - 1)) << (dci_size - pos);
+    // New data indicator 1bit
+    pos += dci_pdu_rel15->ndi2.nbits;
+    *dci_pdu |= ((uint64_t)dci_pdu_rel15->ndi2.val & ((1 << dci_pdu_rel15->ndi2.nbits) - 1)) << (dci_size - pos);
+    // Redundancy version  2bit
+    pos += dci_pdu_rel15->rv2.nbits;
+    *dci_pdu |= ((uint64_t)dci_pdu_rel15->rv2.val & ((1 << dci_pdu_rel15->rv2.nbits) - 1)) << (dci_size - pos);
+    // HARQ process number  4bit
+    pos += 4;
+    *dci_pdu |= ((uint64_t)dci_pdu_rel15->harq_pid & 0xf) << (dci_size - pos);
+    // Downlink assignment index
+    pos += dci_pdu_rel15->dai[0].nbits;
+    *dci_pdu |= ((uint64_t)dci_pdu_rel15->dai[0].val & ((1 << dci_pdu_rel15->dai[0].nbits) - 1)) << (dci_size - pos);
+    // TPC command for scheduled PUCCH  2bit
+    pos += 2;
+    *dci_pdu |= ((uint64_t)dci_pdu_rel15->tpc & 0x3) << (dci_size - pos);
+    // PUCCH resource indicator  3bit
+    pos += 3;
+    *dci_pdu |= ((uint64_t)dci_pdu_rel15->pucch_resource_indicator & 0x7) << (dci_size - pos);
+    // PDSCH-to-HARQ_feedback timing indicator
+    pos += dci_pdu_rel15->pdsch_to_harq_feedback_timing_indicator.nbits;
+    *dci_pdu |= ((uint64_t)dci_pdu_rel15->pdsch_to_harq_feedback_timing_indicator.val & ((1 << dci_pdu_rel15->pdsch_to_harq_feedback_timing_indicator.nbits) - 1)) << (dci_size - pos);
+    // Antenna ports
+    pos += dci_pdu_rel15->antenna_ports.nbits;
+    *dci_pdu |= ((uint64_t)dci_pdu_rel15->antenna_ports.val & ((1 << dci_pdu_rel15->antenna_ports.nbits) - 1)) << (dci_size - pos);
+    // TCI
+    pos += dci_pdu_rel15->transmission_configuration_indication.nbits;
+    *dci_pdu |= ((uint64_t)dci_pdu_rel15->transmission_configuration_indication.val & ((1 << dci_pdu_rel15->transmission_configuration_indication.nbits) - 1)) << (dci_size - pos);
+    // SRS request
+    pos += dci_pdu_rel15->srs_request.nbits;
+    *dci_pdu |= ((uint64_t)dci_pdu_rel15->srs_request.val & ((1 << dci_pdu_rel15->srs_request.nbits) - 1)) << (dci_size - pos);
+    // CBG transmission information
+    pos += dci_pdu_rel15->cbgti.nbits;
+    *dci_pdu |= ((uint64_t)dci_pdu_rel15->cbgti.val & ((1 << dci_pdu_rel15->cbgti.nbits) - 1)) << (dci_size - pos);
+    // CBG flushing out information
+    pos += dci_pdu_rel15->cbgfi.nbits;
+    *dci_pdu |= ((uint64_t)dci_pdu_rel15->cbgfi.val & ((1 << dci_pdu_rel15->cbgfi.nbits) - 1)) << (dci_size - pos);
+    // DMRS sequence init
+    pos += 1;
+    *dci_pdu |= ((uint64_t)dci_pdu_rel15->dmrs_sequence_initialization.val & 0x1) << (dci_size - pos);
+  }
+  LOG_D(MAC, "DCI has %d bits and the payload is %lx\n", dci_size, *dci_pdu);
 }
-*/
 
 int get_spf(nfapi_nr_config_request_scf_t *cfg) {
 
@@ -1779,35 +1442,105 @@ int extract_length(int startSymbolAndLength) {
 /*
  * Dump the UL or DL UE_info into LOG_T(MAC)
  */
-void dump_nr_ue_list(NR_UE_list_t *listP) {
+void dump_nr_list(NR_list_t *listP)
+{
   for (int j = listP->head; j >= 0; j = listP->next[j])
-    LOG_T(MAC, "DL list node %d => %d\n", j, listP->next[j]);
+    LOG_T(MAC, "NR list node %d => %d\n", j, listP->next[j]);
+}
+
+/*
+ * Create a new NR_list
+ */
+void create_nr_list(NR_list_t *list, int len)
+{
+  list->head = -1;
+  list->next = calloc(len, sizeof(*list->next));
+  AssertFatal(list, "cannot calloc() memory for NR_list_t->next\n");
+  for (int i = 0; i < len; ++i)
+    list->next[i] = -1;
+  list->tail = -1;
+  list->len = len;
+}
+
+/*
+ * Destroy an NR_list
+ */
+void destroy_nr_list(NR_list_t *list)
+{
+  free(list->next);
 }
 
 /*
- * Add a UE to NR_UE_list listP
+ * Add an ID to an NR_list at the end, traversing the whole list. Note:
+ * add_tail_nr_list() is a faster alternative, but this implementation ensures
+ * we do not add an existing ID.
  */
-inline void add_nr_ue_list(NR_UE_list_t *listP, int UE_id) {
+void add_nr_list(NR_list_t *listP, int id)
+{
   int *cur = &listP->head;
   while (*cur >= 0) {
-    AssertFatal(*cur != UE_id, "UE_id %d already in NR_UE_list!\n", UE_id);
+    AssertFatal(*cur != id, "id %d already in NR_UE_list!\n", id);
     cur = &listP->next[*cur];
   }
-  *cur = UE_id;
+  *cur = id;
+  if (listP->next[id] < 0)
+    listP->tail = id;
 }
 
 /*
- * Remove a UE from NR_UE_list listP
+ * Remove an ID from an NR_list
  */
-static inline void remove_nr_ue_list(NR_UE_list_t *listP, int UE_id) {
+void remove_nr_list(NR_list_t *listP, int id)
+{
   int *cur = &listP->head;
-  while (*cur != -1 && *cur != UE_id)
+  int *prev = &listP->head;
+  while (*cur != -1 && *cur != id) {
+    prev = cur;
     cur = &listP->next[*cur];
-  AssertFatal(*cur != -1, "UE %d not found in UE_list\n", UE_id);
+  }
+  AssertFatal(*cur != -1, "ID %d not found in UE_list\n", id);
   int *next = &listP->next[*cur];
   *cur = listP->next[*cur];
   *next = -1;
-  return; 
+  listP->tail = *prev >= 0 && listP->next[*prev] >= 0 ? listP->tail : *prev;
+}
+
+/*
+ * Add an ID to the tail of the NR_list in O(1). Note that there is
+ * corresponding remove_tail_nr_list(), as we cannot set the tail backwards and
+ * therefore need to go through the whole list (use remove_nr_list())
+ */
+void add_tail_nr_list(NR_list_t *listP, int id)
+{
+  int *last = listP->tail < 0 ? &listP->head : &listP->next[listP->tail];
+  *last = id;
+  listP->next[id] = -1;
+  listP->tail = id;
+}
+
+/*
+ * Add an ID to the front of the NR_list in O(1)
+ */
+void add_front_nr_list(NR_list_t *listP, int id)
+{
+  const int ohead = listP->head;
+  listP->head = id;
+  listP->next[id] = ohead;
+  if (listP->tail < 0)
+    listP->tail = id;
+}
+
+/*
+ * Remove an ID from the front of the NR_list in O(1)
+ */
+void remove_front_nr_list(NR_list_t *listP)
+{
+  AssertFatal(listP->head >= 0, "Nothing to remove\n");
+  const int ohead = listP->head;
+  listP->head = listP->next[ohead];
+  listP->next[ohead] = -1;
+  if (listP->head < 0)
+    listP->tail = -1;
 }
 
 int find_nr_UE_id(module_id_t mod_idP, rnti_t rntiP)
@@ -1860,20 +1593,35 @@ int find_nr_RA_id(module_id_t mod_idP, int CC_idP, rnti_t rntiP) {
   return -1;
 }
 
-//------------------------------------------------------------------------------
-int add_new_nr_ue(module_id_t mod_idP, rnti_t rntiP){
+int get_nrofHARQ_ProcessesForPDSCH(e_NR_PDSCH_ServingCellConfig__nrofHARQ_ProcessesForPDSCH n)
+{
+  switch (n) {
+  case NR_PDSCH_ServingCellConfig__nrofHARQ_ProcessesForPDSCH_n2:
+    return 2;
+  case NR_PDSCH_ServingCellConfig__nrofHARQ_ProcessesForPDSCH_n4:
+    return 4;
+  case NR_PDSCH_ServingCellConfig__nrofHARQ_ProcessesForPDSCH_n6:
+    return 6;
+  case NR_PDSCH_ServingCellConfig__nrofHARQ_ProcessesForPDSCH_n10:
+    return 10;
+  case NR_PDSCH_ServingCellConfig__nrofHARQ_ProcessesForPDSCH_n12:
+    return 12;
+  case NR_PDSCH_ServingCellConfig__nrofHARQ_ProcessesForPDSCH_n16:
+    return 16;
+  default:
+    return 8;
+  }
+}
 
+//------------------------------------------------------------------------------
+int add_new_nr_ue(module_id_t mod_idP, rnti_t rntiP, NR_CellGroupConfig_t *secondaryCellGroup)
+{
   NR_UE_info_t *UE_info = &RC.nrmac[mod_idP]->UE_info;
-  NR_COMMON_channels_t *cc = RC.nrmac[mod_idP]->common_channels;
-  NR_ServingCellConfigCommon_t *scc = cc->ServingCellConfigCommon;
-  int num_slots_ul = scc->tdd_UL_DL_ConfigurationCommon->pattern1.nrofUplinkSlots;
-  if (scc->tdd_UL_DL_ConfigurationCommon->pattern1.nrofUplinkSymbols>0)
-    num_slots_ul++;
-  LOG_W(MAC, "[gNB %d] Adding UE with rnti %x (num_UEs %d)\n",
+  LOG_I(MAC, "[gNB %d] Adding UE with rnti %x (num_UEs %d)\n",
         mod_idP,
         rntiP,
         UE_info->num_UEs);
-  dump_nr_ue_list(&UE_info->list);
+  dump_nr_list(&UE_info->list);
 
   for (int i = 0; i < MAX_MOBILES_PER_GNB; i++) {
     if (UE_info->active[i])
@@ -1883,38 +1631,65 @@ int add_new_nr_ue(module_id_t mod_idP, rnti_t rntiP){
     UE_info->num_UEs++;
     UE_info->active[UE_id] = true;
     UE_info->rnti[UE_id] = rntiP;
-    add_nr_ue_list(&UE_info->list, UE_id);
+    UE_info->secondaryCellGroup[UE_id] = secondaryCellGroup;
+    add_nr_list(&UE_info->list, UE_id);
+    memset(&UE_info->mac_stats[UE_id], 0, sizeof(NR_mac_stats_t));
     set_Y(UE_info->Y[UE_id], rntiP);
-    memset((void *) &UE_info->UE_sched_ctrl[UE_id],
-           0,
-           sizeof(NR_UE_sched_ctrl_t));
-    UE_info->UE_sched_ctrl[UE_id].ta_frame = 0;
-    UE_info->UE_sched_ctrl[UE_id].ta_update = 31;
-    UE_info->UE_sched_ctrl[UE_id].ta_apply = false;
-    UE_info->UE_sched_ctrl[UE_id].ul_rssi = 0;
+    compute_csi_bitlen(secondaryCellGroup, UE_info, UE_id);
+    NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id];
+    memset(sched_ctrl, 0, sizeof(*sched_ctrl));
+    sched_ctrl->ta_frame = 0;
+    sched_ctrl->ta_update = 31;
+    sched_ctrl->ta_apply = false;
+    sched_ctrl->ul_rssi = 0;
     /* set illegal time domain allocation to force recomputation of all fields */
-    UE_info->UE_sched_ctrl[UE_id].pusch_save.time_domain_allocation = -1;
-    UE_info->UE_sched_ctrl[UE_id].sched_pucch = (NR_sched_pucch **)malloc(num_slots_ul*sizeof(NR_sched_pucch *));
-    for (int s=0; s<num_slots_ul;s++)
-      UE_info->UE_sched_ctrl[UE_id].sched_pucch[s] = (NR_sched_pucch *)malloc(2*sizeof(NR_sched_pucch));
-
-    for (int k=0; k<num_slots_ul; k++) {
-      for (int l=0; l<2; l++)
-        memset((void *) &UE_info->UE_sched_ctrl[UE_id].sched_pucch[k][l],
-               0,
-               sizeof(NR_sched_pucch));
-    }
+    sched_ctrl->pusch_save.time_domain_allocation = -1;
+    const NR_ServingCellConfig_t *servingCellConfig = secondaryCellGroup->spCellConfig->spCellConfigDedicated;
+
+    /* Set default BWPs */
+    const struct NR_ServingCellConfig__downlinkBWP_ToAddModList *bwpList = servingCellConfig->downlinkBWP_ToAddModList;
+    AssertFatal(bwpList->list.count == 1,
+                "downlinkBWP_ToAddModList has %d BWP!\n",
+                bwpList->list.count);
+    const int bwp_id = 1;
+    sched_ctrl->active_bwp = bwpList->list.array[bwp_id - 1];
+    const struct NR_UplinkConfig__uplinkBWP_ToAddModList *ubwpList = servingCellConfig->uplinkConfig->uplinkBWP_ToAddModList;
+    AssertFatal(ubwpList->list.count == 1,
+                "uplinkBWP_ToAddModList has %d BWP!\n",
+                ubwpList->list.count);
+    sched_ctrl->active_ubwp = ubwpList->list.array[bwp_id - 1];
+
+    /* get Number of HARQ processes for this UE */
+    AssertFatal(servingCellConfig->pdsch_ServingCellConfig->present == NR_SetupRelease_PDSCH_ServingCellConfig_PR_setup,
+                "no pdsch-ServingCellConfig found for UE %d\n",
+                UE_id);
+    const NR_PDSCH_ServingCellConfig_t *pdsch = servingCellConfig->pdsch_ServingCellConfig->choice.setup;
+    const int nrofHARQ = pdsch->nrofHARQ_ProcessesForPDSCH ?
+        get_nrofHARQ_ProcessesForPDSCH(*pdsch->nrofHARQ_ProcessesForPDSCH) : 8;
+    // add all available DL HARQ processes for this UE
+    create_nr_list(&sched_ctrl->available_dl_harq, nrofHARQ);
+    for (int harq = 0; harq < nrofHARQ; harq++)
+      add_tail_nr_list(&sched_ctrl->available_dl_harq, harq);
+    create_nr_list(&sched_ctrl->feedback_dl_harq, nrofHARQ);
+    create_nr_list(&sched_ctrl->retrans_dl_harq, nrofHARQ);
+
+    // add all available UL HARQ processes for this UE
+    create_nr_list(&sched_ctrl->available_ul_harq, 16);
+    for (int harq = 0; harq < 16; harq++)
+      add_tail_nr_list(&sched_ctrl->available_ul_harq, harq);
+    create_nr_list(&sched_ctrl->feedback_ul_harq, 16);
+    create_nr_list(&sched_ctrl->retrans_ul_harq, 16);
     LOG_I(MAC, "gNB %d] Add NR UE_id %d : rnti %x\n",
           mod_idP,
           UE_id,
           rntiP);
-    dump_nr_ue_list(&UE_info->list);
+    dump_nr_list(&UE_info->list);
     return (UE_id);
   }
 
   // printf("MAC: cannot add new UE for rnti %x\n", rntiP);
   LOG_E(MAC, "error in add_new_ue(), could not find space in UE_info, Dumping UE list\n");
-  dump_nr_ue_list(&UE_info->list);
+  dump_nr_list(&UE_info->list);
   return -1;
 }
 
@@ -1942,11 +1717,14 @@ void mac_remove_nr_ue(module_id_t mod_id, rnti_t rnti)
     UE_info->num_UEs--;
     UE_info->active[UE_id] = FALSE;
     UE_info->rnti[UE_id] = 0;
-    remove_nr_ue_list(&UE_info->list, UE_id);
-    free(UE_info->UE_sched_ctrl[UE_id].sched_pucch);
-    memset((void *) &UE_info->UE_sched_ctrl[UE_id],
-           0,
-           sizeof(NR_UE_sched_ctrl_t));
+    remove_nr_list(&UE_info->list, UE_id);
+    NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id];
+    destroy_nr_list(&sched_ctrl->available_dl_harq);
+    destroy_nr_list(&sched_ctrl->feedback_dl_harq);
+    destroy_nr_list(&sched_ctrl->retrans_dl_harq);
+    destroy_nr_list(&sched_ctrl->available_ul_harq);
+    destroy_nr_list(&sched_ctrl->feedback_ul_harq);
+    destroy_nr_list(&sched_ctrl->retrans_ul_harq);
     LOG_I(MAC, "[gNB %d] Remove NR UE_id %d : rnti %x\n",
           mod_id,
           UE_id,
@@ -2024,6 +1802,30 @@ void get_pdsch_to_harq_feedback(int Mod_idP,
 }
 
 
+bool find_free_CCE(module_id_t module_id,
+                   sub_frame_t slot,
+                   int UE_id){
+  NR_UE_sched_ctrl_t *sched_ctrl = &RC.nrmac[module_id]->UE_info.UE_sched_ctrl[UE_id];
+  uint8_t nr_of_candidates;
+  find_aggregation_candidates(&sched_ctrl->aggregation_level,
+                              &nr_of_candidates,
+                              sched_ctrl->search_space);
+  const int cid = sched_ctrl->coreset->controlResourceSetId;
+  const uint16_t Y = RC.nrmac[module_id]->UE_info.Y[UE_id][cid][slot];
+  const int m = RC.nrmac[module_id]->UE_info.num_pdcch_cand[UE_id][cid];
+  sched_ctrl->cce_index = allocate_nr_CCEs(RC.nrmac[module_id],
+                                           sched_ctrl->active_bwp,
+                                           sched_ctrl->coreset,
+                                           sched_ctrl->aggregation_level,
+                                           Y,
+                                           m,
+                                           nr_of_candidates);
+  if (sched_ctrl->cce_index < 0)
+    return false;
+
+  RC.nrmac[module_id]->UE_info.num_pdcch_cand[UE_id][cid]++;
+  return true;
+}
 
 /*void fill_nfapi_coresets_and_searchspaces(NR_CellGroupConfig_t *cg,
 					  nfapi_nr_coreset_t *coreset,
diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c
index ec5195ec5beacf7c19ff9f40ed5f483da4f4e115..f0a20d8e6f78c14aa86f410fc197e28922a90217 100644
--- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c
+++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c
@@ -34,64 +34,72 @@
 
 extern RAN_CONTEXT_t RC;
 
+void nr_fill_nfapi_pucch(module_id_t mod_id,
+                         frame_t frame,
+                         sub_frame_t slot,
+                         const NR_sched_pucch_t *pucch,
+                         int UE_id)
+{
+  NR_UE_info_t *UE_info = &RC.nrmac[mod_id]->UE_info;
+
+  nfapi_nr_ul_tti_request_t *future_ul_tti_req =
+      &RC.nrmac[mod_id]->UL_tti_req_ahead[0][pucch->ul_slot];
+  AssertFatal(future_ul_tti_req->SFN == pucch->frame
+              && future_ul_tti_req->Slot == pucch->ul_slot,
+              "future UL_tti_req's frame.slot %d.%d does not match PUCCH %d.%d\n",
+              future_ul_tti_req->SFN,
+              future_ul_tti_req->Slot,
+              pucch->frame,
+              pucch->ul_slot);
+  future_ul_tti_req->pdus_list[future_ul_tti_req->n_pdus].pdu_type = NFAPI_NR_UL_CONFIG_PUCCH_PDU_TYPE;
+  future_ul_tti_req->pdus_list[future_ul_tti_req->n_pdus].pdu_size = sizeof(nfapi_nr_pucch_pdu_t);
+  nfapi_nr_pucch_pdu_t *pucch_pdu = &future_ul_tti_req->pdus_list[future_ul_tti_req->n_pdus].pucch_pdu;
+  memset(pucch_pdu, 0, sizeof(nfapi_nr_pucch_pdu_t));
+  future_ul_tti_req->n_pdus += 1;
+
+  LOG_D(MAC,
+        "%4d.%2d Scheduling pucch reception in %4d.%2d: bits SR %d, ACK %d, CSI %d on res %d\n",
+        frame,
+        slot,
+        pucch->frame,
+        pucch->ul_slot,
+        pucch->sr_flag,
+        pucch->dai_c,
+        pucch->csi_bits,
+        pucch->resource_indicator);
+
+  NR_ServingCellConfigCommon_t *scc = RC.nrmac[mod_id]->common_channels->ServingCellConfigCommon;
+  nr_configure_pucch(pucch_pdu,
+                     scc,
+                     UE_info->UE_sched_ctrl[UE_id].active_ubwp,
+                     UE_info->rnti[UE_id],
+                     pucch->resource_indicator,
+                     pucch->csi_bits,
+                     pucch->dai_c,
+                     pucch->sr_flag);
+}
+
 void nr_schedule_pucch(int Mod_idP,
-                       int UE_id,
-                       int nr_ulmix_slots,
                        frame_t frameP,
                        sub_frame_t slotP) {
   NR_UE_info_t *UE_info = &RC.nrmac[Mod_idP]->UE_info;
-  AssertFatal(UE_info->active[UE_id],"Cannot find UE_id %d is not active\n",UE_id);
+  const NR_list_t *UE_list = &UE_info->list;
 
-  for (int k=0; k<nr_ulmix_slots; k++) {
-    for (int l=0; l<2; l++) {
-      NR_sched_pucch *curr_pucch = &UE_info->UE_sched_ctrl[UE_id].sched_pucch[k][l];
+  for (int UE_id = UE_list->head; UE_id >= 0; UE_id = UE_list->next[UE_id]) {
+    NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id];
+    const int n = sizeof(sched_ctrl->sched_pucch) / sizeof(*sched_ctrl->sched_pucch);
+    for (int i = 0; i < n; i++) {
+      NR_sched_pucch_t *curr_pucch = &UE_info->UE_sched_ctrl[UE_id].sched_pucch[i];
       const uint16_t O_ack = curr_pucch->dai_c;
       const uint16_t O_csi = curr_pucch->csi_bits;
-      const uint8_t O_sr = 0; // no SR in PUCCH implemented for now
+      const uint8_t O_sr = curr_pucch->sr_flag;
       if (O_ack + O_csi + O_sr == 0
           || frameP != curr_pucch->frame
           || slotP != curr_pucch->ul_slot)
         continue;
 
-      nfapi_nr_ul_tti_request_t *future_ul_tti_req =
-          &RC.nrmac[Mod_idP]->UL_tti_req_ahead[0][curr_pucch->ul_slot];
-      AssertFatal(future_ul_tti_req->SFN == curr_pucch->frame
-                  && future_ul_tti_req->Slot == curr_pucch->ul_slot,
-                  "future UL_tti_req's frame.slot %d.%d does not match PUCCH %d.%d\n",
-                  future_ul_tti_req->SFN,
-                  future_ul_tti_req->Slot,
-                  curr_pucch->frame,
-                  curr_pucch->ul_slot);
-      future_ul_tti_req->pdus_list[future_ul_tti_req->n_pdus].pdu_type = NFAPI_NR_UL_CONFIG_PUCCH_PDU_TYPE;
-      future_ul_tti_req->pdus_list[future_ul_tti_req->n_pdus].pdu_size = sizeof(nfapi_nr_pucch_pdu_t);
-      nfapi_nr_pucch_pdu_t *pucch_pdu = &future_ul_tti_req->pdus_list[future_ul_tti_req->n_pdus].pucch_pdu;
-      memset(pucch_pdu, 0, sizeof(nfapi_nr_pucch_pdu_t));
-      future_ul_tti_req->n_pdus += 1;
-
-      LOG_D(MAC,
-            "%4d.%2d Scheduling pucch reception in %4d.%2d: bits SR %d, ACK %d, CSI %d, k %d l %d\n",
-            frameP,
-            slotP,
-            curr_pucch->frame,
-            curr_pucch->ul_slot,
-            O_sr,
-            O_ack,
-            O_csi,
-            k, l);
-
-      NR_ServingCellConfigCommon_t *scc = RC.nrmac[Mod_idP]->common_channels->ServingCellConfigCommon;
-      nr_configure_pucch(pucch_pdu,
-                         scc,
-                         UE_info->UE_sched_ctrl[UE_id].active_ubwp,
-                         UE_info->rnti[UE_id],
-                         curr_pucch->resource_indicator,
-                         O_csi,
-                         O_ack,
-                         O_sr);
-
-      memset(&UE_info->UE_sched_ctrl[UE_id].sched_pucch[k][l],
-             0,
-             sizeof(NR_sched_pucch));
+      nr_fill_nfapi_pucch(Mod_idP, frameP, slotP, curr_pucch, UE_id);
+      memset(curr_pucch, 0, sizeof(*curr_pucch));
     }
   }
 }
@@ -172,107 +180,136 @@ void compute_csi_bitlen (NR_CellGroupConfig_t *secondaryCellGroup, NR_UE_info_t
 }
 
 
-uint16_t nr_get_csi_bitlen(int Mod_idP,
-                           int UE_id,
-                           uint8_t csi_report_id) {
-
-  uint16_t csi_bitlen =0;
-  NR_UE_info_t *UE_info = &RC.nrmac[Mod_idP]->UE_info;
-  CRI_SSBRI_RSRP_bitlen_t * CSI_report_bitlen = NULL;
-
-  CSI_report_bitlen = &(UE_info->csi_report_template[UE_id][csi_report_id].CSI_report_bitlen[0]);
-  csi_bitlen = ((CSI_report_bitlen->cri_ssbri_bitlen * CSI_report_bitlen->nb_ssbri_cri) +
-               CSI_report_bitlen->rsrp_bitlen +(CSI_report_bitlen->diff_rsrp_bitlen *
-               (CSI_report_bitlen->nb_ssbri_cri -1 )) *UE_info->csi_report_template[UE_id][csi_report_id].nb_of_csi_ssb_report);
-
-  return csi_bitlen;
+uint16_t nr_get_csi_bitlen(const nr_csi_report_t *csi_report)
+{
+  const CRI_SSBRI_RSRP_bitlen_t *bitlen = &csi_report->CSI_report_bitlen[0];
+  return bitlen->cri_ssbri_bitlen * bitlen->nb_ssbri_cri
+         + bitlen->rsrp_bitlen
+         + bitlen->diff_rsrp_bitlen * (bitlen->nb_ssbri_cri - 1) * csi_report->nb_of_csi_ssb_report;
 }
 
 
 void nr_csi_meas_reporting(int Mod_idP,
-                           int UE_id,
                            frame_t frame,
-                           sub_frame_t slot,
-                           int slots_per_tdd,
-                           int ul_slots,
-                           int n_slots_frame) {
+                           sub_frame_t slot)
+{
+  NR_ServingCellConfigCommon_t *scc =
+      RC.nrmac[Mod_idP]->common_channels->ServingCellConfigCommon;
+  const int n_slots_frame = nr_slots_per_frame[*scc->ssbSubcarrierSpacing];
 
   NR_UE_info_t *UE_info = &RC.nrmac[Mod_idP]->UE_info;
-  NR_sched_pucch *curr_pucch;
-  NR_PUCCH_ResourceSet_t *pucchresset;
-  NR_CSI_ReportConfig_t *csirep;
-  NR_CellGroupConfig_t *secondaryCellGroup = UE_info->secondaryCellGroup[UE_id];
-  NR_CSI_MeasConfig_t *csi_measconfig = secondaryCellGroup->spCellConfig->spCellConfigDedicated->csi_MeasConfig->choice.setup;
-  NR_BWP_Uplink_t *ubwp=secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list.array[0];
-  NR_PUCCH_Config_t *pucch_Config = ubwp->bwp_Dedicated->pucch_Config->choice.setup;
-
-  AssertFatal(csi_measconfig->csi_ReportConfigToAddModList->list.count>0,"NO CSI report configuration available");
-
-  for (int csi_report_id = 0; csi_report_id < csi_measconfig->csi_ReportConfigToAddModList->list.count; csi_report_id++){
-
-    csirep = csi_measconfig->csi_ReportConfigToAddModList->list.array[csi_report_id];
-
-    AssertFatal(csirep->reportConfigType.choice.periodic!=NULL,"Only periodic CSI reporting is implemented currently");
-    int period, offset, sched_slot;
-    csi_period_offset(csirep,&period,&offset);
-    sched_slot = (period+offset)%n_slots_frame;
-    // prepare to schedule csi measurement reception according to 5.2.1.4 in 38.214
-    // preparation is done in first slot of tdd period
-    if ( (frame%(period/n_slots_frame)==(offset/n_slots_frame)) && (slot==((sched_slot/slots_per_tdd)*slots_per_tdd))) {
-
-      // we are scheduling pucch for csi in the first pucch occasion (this comes before ack/nack)
-      curr_pucch = &UE_info->UE_sched_ctrl[UE_id].sched_pucch[(sched_slot%slots_per_tdd)-slots_per_tdd+ul_slots][0];
-
-      NR_PUCCH_CSI_Resource_t *pucchcsires = csirep->reportConfigType.choice.periodic->pucch_CSI_ResourceList.list.array[0];
-
-      int found = -1;
-      pucchresset = pucch_Config->resourceSetToAddModList->list.array[1]; // set with formats >1
-      int n_list = pucchresset->resourceList.list.count;
-      for (int i=0; i<n_list; i++) {
-        if (*pucchresset->resourceList.list.array[i] == pucchcsires->pucch_Resource)
-          found = i;
-      }
-      AssertFatal(found>-1,"CSI resource not found among PUCCH resources");
-
-      curr_pucch->resource_indicator = found;
-
-      n_list = pucch_Config->resourceToAddModList->list.count;
+  NR_list_t *UE_list = &UE_info->list;
+  for (int UE_id = UE_list->head; UE_id >= 0; UE_id = UE_list->next[UE_id]) {
+    const NR_CellGroupConfig_t *secondaryCellGroup = UE_info->secondaryCellGroup[UE_id];
+    NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id];
+    const NR_CSI_MeasConfig_t *csi_measconfig = secondaryCellGroup->spCellConfig->spCellConfigDedicated->csi_MeasConfig->choice.setup;
+    AssertFatal(csi_measconfig->csi_ReportConfigToAddModList->list.count > 0,
+                "NO CSI report configuration available");
+    NR_PUCCH_Config_t *pucch_Config = sched_ctrl->active_ubwp->bwp_Dedicated->pucch_Config->choice.setup;
+
+    for (int csi_report_id = 0; csi_report_id < csi_measconfig->csi_ReportConfigToAddModList->list.count; csi_report_id++){
+      const NR_CSI_ReportConfig_t *csirep = csi_measconfig->csi_ReportConfigToAddModList->list.array[csi_report_id];
+
+      AssertFatal(csirep->reportConfigType.choice.periodic,
+                  "Only periodic CSI reporting is implemented currently\n");
+      int period, offset;
+      csi_period_offset(csirep, &period, &offset);
+      const int sched_slot = (period + offset) % n_slots_frame;
+      // prepare to schedule csi measurement reception according to 5.2.1.4 in 38.214
+      // preparation is done in first slot of tdd period
+      if (frame % (period / n_slots_frame) != offset / n_slots_frame)
+        continue;
+      LOG_D(MAC, "CSI in frame %d slot %d\n", frame, sched_slot);
+
+      const NR_PUCCH_CSI_Resource_t *pucchcsires = csirep->reportConfigType.choice.periodic->pucch_CSI_ResourceList.list.array[0];
+      const NR_PUCCH_ResourceSet_t *pucchresset = pucch_Config->resourceSetToAddModList->list.array[1]; // set with formats >1
+      const int n = pucchresset->resourceList.list.count;
+      int res_index = 0;
+      for (; res_index < n; res_index++)
+        if (*pucchresset->resourceList.list.array[res_index] == pucchcsires->pucch_Resource)
+          break;
+      AssertFatal(res_index < n,
+                  "CSI resource not found among PUCCH resources\n");
+
+      // find free PUCCH that is in order with possibly existing PUCCH
+      // schedulings (other CSI, SR)
+      NR_sched_pucch_t *curr_pucch = &sched_ctrl->sched_pucch[2];
+      AssertFatal(curr_pucch->csi_bits == 0
+                  && !curr_pucch->sr_flag
+                  && curr_pucch->dai_c == 0,
+                  "PUCCH not free at index 2 for UE %04x\n",
+                  UE_info->rnti[UE_id]);
+      curr_pucch->frame = frame;
+      curr_pucch->ul_slot = sched_slot;
+      curr_pucch->resource_indicator = res_index;
+      curr_pucch->csi_bits +=
+          nr_get_csi_bitlen(&UE_info->csi_report_template[UE_id][csi_report_id]);
 
       // going through the list of PUCCH resources to find the one indexed by resource_id
-      for (int i=0; i<n_list; i++) {
-        NR_PUCCH_Resource_t *pucchres = pucch_Config->resourceToAddModList->list.array[i];
-        if (pucchres->pucch_ResourceId == *pucchresset->resourceList.list.array[found]) {
-          switch(pucchres->format.present){
-            case NR_PUCCH_Resource__format_PR_format2:
-              if (pucch_Config->format2->choice.setup->simultaneousHARQ_ACK_CSI == NULL)
-                curr_pucch->simultaneous_harqcsi = false;
-              else
-                curr_pucch->simultaneous_harqcsi = true;
-              break;
-            case NR_PUCCH_Resource__format_PR_format3:
-              if (pucch_Config->format3->choice.setup->simultaneousHARQ_ACK_CSI == NULL)
-                curr_pucch->simultaneous_harqcsi = false;
-              else
-                curr_pucch->simultaneous_harqcsi = true;
-              break;
-            case NR_PUCCH_Resource__format_PR_format4:
-              if (pucch_Config->format4->choice.setup->simultaneousHARQ_ACK_CSI == NULL)
-                curr_pucch->simultaneous_harqcsi = false;
-              else
-                curr_pucch->simultaneous_harqcsi = true;
-              break;
-          default:
-            AssertFatal(1==0,"Invalid PUCCH format type");
-          }
+      uint16_t *vrb_map_UL = &RC.nrmac[Mod_idP]->common_channels[0].vrb_map_UL[sched_slot * MAX_BWP_SIZE];
+      const int m = pucch_Config->resourceToAddModList->list.count;
+      for (int j = 0; j < m; j++) {
+        NR_PUCCH_Resource_t *pucchres = pucch_Config->resourceToAddModList->list.array[j];
+        if (pucchres->pucch_ResourceId != *pucchresset->resourceList.list.array[res_index])
+          continue;
+        int start = pucchres->startingPRB;
+        int len = 1;
+        uint64_t mask = 0;
+        switch(pucchres->format.present){
+          case NR_PUCCH_Resource__format_PR_format2:
+            len = pucchres->format.choice.format2->nrofPRBs;
+            mask = ((1 << pucchres->format.choice.format2->nrofSymbols) - 1) << pucchres->format.choice.format2->startingSymbolIndex;
+            curr_pucch->simultaneous_harqcsi = pucch_Config->format2->choice.setup->simultaneousHARQ_ACK_CSI;
+            break;
+          case NR_PUCCH_Resource__format_PR_format3:
+            len = pucchres->format.choice.format3->nrofPRBs;
+            mask = ((1 << pucchres->format.choice.format3->nrofSymbols) - 1) << pucchres->format.choice.format3->startingSymbolIndex;
+            curr_pucch->simultaneous_harqcsi = pucch_Config->format3->choice.setup->simultaneousHARQ_ACK_CSI;
+            break;
+          case NR_PUCCH_Resource__format_PR_format4:
+            mask = ((1 << pucchres->format.choice.format4->nrofSymbols) - 1) << pucchres->format.choice.format4->startingSymbolIndex;
+            curr_pucch->simultaneous_harqcsi = pucch_Config->format4->choice.setup->simultaneousHARQ_ACK_CSI;
+            break;
+        default:
+          AssertFatal(0, "Invalid PUCCH format type\n");
+        }
+        // verify resources are free
+        for (int i = start; i < start + len; ++i) {
+          vrb_map_UL[i] |= mask;
         }
+        AssertFatal(!curr_pucch->simultaneous_harqcsi,
+                    "UE %04x has simultaneous HARQ/CSI configured, but we don't support that\n",
+                    UE_info->rnti[UE_id]);
       }
-      curr_pucch->csi_bits += nr_get_csi_bitlen(Mod_idP,UE_id,csi_report_id); // TODO function to compute CSI meas report bit size
-      curr_pucch->frame = frame;
-      curr_pucch->ul_slot = sched_slot;
     }
   }
 }
 
+static void handle_dl_harq(module_id_t mod_id,
+                           int UE_id,
+                           int8_t harq_pid,
+                           bool success)
+{
+  NR_UE_info_t *UE_info = &RC.nrmac[mod_id]->UE_info;
+  NR_UE_harq_t *harq = &UE_info->UE_sched_ctrl[UE_id].harq_processes[harq_pid];
+  harq->feedback_slot = -1;
+  harq->is_waiting = false;
+  if (success) {
+    add_tail_nr_list(&UE_info->UE_sched_ctrl[UE_id].available_dl_harq, harq_pid);
+    harq->round = 0;
+    harq->ndi ^= 1;
+  } else if (harq->round == MAX_HARQ_ROUNDS) {
+    add_tail_nr_list(&UE_info->UE_sched_ctrl[UE_id].available_dl_harq, harq_pid);
+    harq->round = 0;
+    harq->ndi ^= 1;
+    NR_mac_stats_t *stats = &UE_info->mac_stats[UE_id];
+    stats->dlsch_errors++;
+    LOG_D(MAC, "retransmission error for UE %d (total %d)\n", UE_id, stats->dlsch_errors);
+  } else {
+    add_tail_nr_list(&UE_info->UE_sched_ctrl[UE_id].retrans_dl_harq, harq_pid);
+    harq->round++;
+  }
+}
 
 void handle_nr_uci_pucch_0_1(module_id_t mod_id,
                              frame_t frame,
@@ -292,50 +329,36 @@ void handle_nr_uci_pucch_0_1(module_id_t mod_id,
                                 uci_01->ul_cqi,
                                 30);
 
-  // TODO
-  int max_harq_rounds = 4; // TODO define macro
+  NR_ServingCellConfigCommon_t *scc = RC.nrmac[mod_id]->common_channels->ServingCellConfigCommon;
+  const int num_slots = nr_slots_per_frame[*scc->ssbSubcarrierSpacing];
   if (((uci_01->pduBitmap >> 1) & 0x01)) {
-    // handle harq
-    int harq_idx_s = 0;
-
     // iterate over received harq bits
     for (int harq_bit = 0; harq_bit < uci_01->harq->num_harq; harq_bit++) {
-      // search for the right harq process
-      for (int harq_idx = harq_idx_s; harq_idx < NR_MAX_NB_HARQ_PROCESSES; harq_idx++) {
-        // if the gNB received ack with a good confidence
-        if ((slot - 1) == sched_ctrl->harq_processes[harq_idx].feedback_slot) {
-          sched_ctrl->harq_processes[harq_idx].feedback_slot = -1;
-          if ((uci_01->harq->harq_list[harq_bit].harq_value == 1) &&
-              (uci_01->harq->harq_confidence_level == 0)) {
-            // toggle NDI and reset round
-            sched_ctrl->harq_processes[harq_idx].ndi ^= 1;
-            sched_ctrl->harq_processes[harq_idx].round = 0;
-          }
-          else
-            sched_ctrl->harq_processes[harq_idx].round++;
-          sched_ctrl->harq_processes[harq_idx].is_waiting = 0;
-          harq_idx_s = harq_idx + 1;
-          // if the max harq rounds was reached
-          if (sched_ctrl->harq_processes[harq_idx].round == max_harq_rounds) {
-            sched_ctrl->harq_processes[harq_idx].ndi ^= 1;
-            sched_ctrl->harq_processes[harq_idx].round = 0;
-            UE_info->mac_stats[UE_id].dlsch_errors++;
-          }
-          break;
-        }
-        // if feedback slot processing is aborted
-        else if (sched_ctrl->harq_processes[harq_idx].feedback_slot != -1
-                 && (slot - 1) > sched_ctrl->harq_processes[harq_idx].feedback_slot
-                 && sched_ctrl->harq_processes[harq_idx].is_waiting) {
-          sched_ctrl->harq_processes[harq_idx].feedback_slot = -1;
-          sched_ctrl->harq_processes[harq_idx].round++;
-          if (sched_ctrl->harq_processes[harq_idx].round == max_harq_rounds) {
-            sched_ctrl->harq_processes[harq_idx].ndi ^= 1;
-            sched_ctrl->harq_processes[harq_idx].round = 0;
-          }
-          sched_ctrl->harq_processes[harq_idx].is_waiting = 0;
-        }
+      const uint8_t harq_value = uci_01->harq->harq_list[harq_bit].harq_value;
+      const uint8_t harq_confidence = uci_01->harq->harq_confidence_level;
+      const int feedback_slot = (slot - 1 + num_slots) % num_slots;
+      /* In case of realtime problems: we can only identify a HARQ process by
+       * timing. If the HARQ process's feedback_slot is not the one we
+       * expected, we assume that processing has been aborted and we need to
+       * skip this HARQ process, which is what happens in the loop below. If
+       * you don't experience real-time problems, you might simply revert the
+       * commit that introduced these changes. */
+      int8_t pid = sched_ctrl->feedback_dl_harq.head;
+      DevAssert(pid >= 0);
+      while (sched_ctrl->harq_processes[pid].feedback_slot != feedback_slot) {
+        LOG_W(MAC,
+              "expected feedback slot %d, but found %d instead\n",
+              sched_ctrl->harq_processes[pid].feedback_slot,
+              feedback_slot);
+        remove_front_nr_list(&sched_ctrl->feedback_dl_harq);
+        handle_dl_harq(mod_id, UE_id, pid, 0);
+        pid = sched_ctrl->feedback_dl_harq.head;
+        DevAssert(pid >= 0);
       }
+      remove_front_nr_list(&sched_ctrl->feedback_dl_harq);
+      NR_UE_harq_t *harq = &sched_ctrl->harq_processes[pid];
+      DevAssert(harq->is_waiting);
+      handle_dl_harq(mod_id, UE_id, pid, harq_value == 1 && harq_confidence == 0);
     }
   }
 }
@@ -358,130 +381,243 @@ void handle_nr_uci_pucch_2_3_4(module_id_t mod_id,
                                 uci_234->ul_cqi,
                                 30);
 
-  // TODO
-  int max_harq_rounds = 4; // TODO define macro
+  NR_ServingCellConfigCommon_t *scc = RC.nrmac[mod_id]->common_channels->ServingCellConfigCommon;
+  const int num_slots = nr_slots_per_frame[*scc->ssbSubcarrierSpacing];
   if ((uci_234->pduBitmap >> 1) & 0x01) {
-    int harq_idx_s = 0;
-    int acknack;
-
     // iterate over received harq bits
     for (int harq_bit = 0; harq_bit < uci_234->harq.harq_bit_len; harq_bit++) {
-      acknack = ((uci_234->harq.harq_payload[harq_bit>>3])>>harq_bit)&0x01;
-      for (int harq_idx = harq_idx_s; harq_idx < NR_MAX_NB_HARQ_PROCESSES-1; harq_idx++) {
-        // if the gNB received ack with a good confidence or if the max harq rounds was reached
-        if ((slot - 1) == sched_ctrl->harq_processes[harq_idx].feedback_slot) {
-          // TODO add some confidence level for when there is no CRC
-          sched_ctrl->harq_processes[harq_idx].feedback_slot = -1;
-          if ((uci_234->harq.harq_crc != 1) && acknack) {
-            // toggle NDI and reset round
-            sched_ctrl->harq_processes[harq_idx].ndi ^= 1;
-            sched_ctrl->harq_processes[harq_idx].round = 0;
-          }
-          else
-            sched_ctrl->harq_processes[harq_idx].round++;
-          sched_ctrl->harq_processes[harq_idx].is_waiting = 0;
-          harq_idx_s = harq_idx + 1;
-          // if the max harq rounds was reached
-          if (sched_ctrl->harq_processes[harq_idx].round == max_harq_rounds) {
-            sched_ctrl->harq_processes[harq_idx].ndi ^= 1;
-            sched_ctrl->harq_processes[harq_idx].round = 0;
-            UE_info->mac_stats[UE_id].dlsch_errors++;
-          }
-          break;
-        }
-        // if feedback slot processing is aborted
-        else if (sched_ctrl->harq_processes[harq_idx].feedback_slot != -1
-                 && (slot - 1) > sched_ctrl->harq_processes[harq_idx].feedback_slot
-                 && sched_ctrl->harq_processes[harq_idx].is_waiting) {
-          sched_ctrl->harq_processes[harq_idx].feedback_slot = -1;
-          sched_ctrl->harq_processes[harq_idx].round++;
-          if (sched_ctrl->harq_processes[harq_idx].round == max_harq_rounds) {
-            sched_ctrl->harq_processes[harq_idx].ndi ^= 1;
-            sched_ctrl->harq_processes[harq_idx].round = 0;
-          }
-          sched_ctrl->harq_processes[harq_idx].is_waiting = 0;
-        }
+      const int acknack = ((uci_234->harq.harq_payload[harq_bit >> 3]) >> harq_bit) & 0x01;
+      const int feedback_slot = (slot - 1 + num_slots) % num_slots;
+      /* In case of realtime problems: we can only identify a HARQ process by
+       * timing. If the HARQ process's feedback_slot is not the one we
+       * expected, we assume that processing has been aborted and we need to
+       * skip this HARQ process, which is what happens in the loop below. If
+       * you don't experience real-time problems, you might simply revert the
+       * commit that introduced these changes. */
+      int8_t pid = sched_ctrl->feedback_dl_harq.head;
+      DevAssert(pid >= 0);
+      while (sched_ctrl->harq_processes[pid].feedback_slot != feedback_slot) {
+        LOG_W(MAC,
+              "expected feedback slot %d, but found %d instead\n",
+              sched_ctrl->harq_processes[pid].feedback_slot,
+              feedback_slot);
+        remove_front_nr_list(&sched_ctrl->feedback_dl_harq);
+        handle_dl_harq(mod_id, UE_id, pid, 0);
+        pid = sched_ctrl->feedback_dl_harq.head;
+        DevAssert(pid >= 0);
       }
+      remove_front_nr_list(&sched_ctrl->feedback_dl_harq);
+      NR_UE_harq_t *harq = &sched_ctrl->harq_processes[pid];
+      DevAssert(harq->is_waiting);
+      handle_dl_harq(mod_id, UE_id, pid, uci_234->harq.harq_crc != 1 && acknack);
     }
   }
 }
 
 
 // function to update pucch scheduling parameters in UE list when a USS DL is scheduled
-void nr_acknack_scheduling(int Mod_idP,
+bool nr_acknack_scheduling(int mod_id,
                            int UE_id,
-                           frame_t frameP,
-                           sub_frame_t slotP,
-                           int slots_per_tdd,
-                           int *pucch_id,
-                           int *pucch_occ) {
-
-  NR_ServingCellConfigCommon_t *scc = RC.nrmac[Mod_idP]->common_channels->ServingCellConfigCommon;
-  NR_UE_info_t *UE_info = &RC.nrmac[Mod_idP]->UE_info;
-  NR_sched_pucch *curr_pucch;
-  int max_acknacks,pucch_res,first_ul_slot_tdd,k,i,l;
-  uint8_t pdsch_to_harq_feedback[8];
-  int found = 0;
-  int nr_ulmix_slots = scc->tdd_UL_DL_ConfigurationCommon->pattern1.nrofUplinkSlots;
-  if (scc->tdd_UL_DL_ConfigurationCommon->pattern1.nrofUplinkSymbols!=0)
-    nr_ulmix_slots++;
-
-  bool csi_pres=false;
-  for (k=0; k<nr_ulmix_slots; k++) {
-    if(UE_info->UE_sched_ctrl[UE_id].sched_pucch[k][0].csi_bits>0)
-      csi_pres=true;
+                           frame_t frame,
+                           sub_frame_t slot)
+{
+  const NR_ServingCellConfigCommon_t *scc = RC.nrmac[mod_id]->common_channels->ServingCellConfigCommon;
+  const int n_slots_frame = nr_slots_per_frame[*scc->ssbSubcarrierSpacing];
+  const NR_TDD_UL_DL_Pattern_t *tdd = &scc->tdd_UL_DL_ConfigurationCommon->pattern1;
+  const int nr_ulmix_slots = tdd->nrofUplinkSlots + (tdd->nrofUplinkSymbols != 0);
+  const int nr_mix_slots = tdd->nrofDownlinkSymbols != 0 || tdd->nrofUplinkSymbols != 0;
+  const int nr_slots_period = tdd->nrofDownlinkSlots + tdd->nrofUplinkSlots + nr_mix_slots;
+  const int first_ul_slot_tdd = tdd->nrofDownlinkSlots + nr_slots_period * (slot / nr_slots_period);
+  const int CC_id = 0;
+
+  AssertFatal(slot < first_ul_slot_tdd + (tdd->nrofUplinkSymbols != 0),
+              "cannot handle multiple TDD periods (yet): slot %d first_ul_slot_tdd %d nrofUplinkSlots %ld\n",
+              slot,
+              first_ul_slot_tdd,
+              tdd->nrofUplinkSlots);
+
+  /* for the moment, we consider:
+   * * only pucch_sched[0] holds HARQ (and SR)
+   * * we do not multiplex with CSI, which is always in pucch_sched[2]
+   * * SR uses format 0 and is allocated in the first UL (mixed) slot (and not
+   *   later)
+   * * that the PUCCH resource set 0 (for up to 2 bits) points to the first N
+   *   PUCCH resources, where N is the number of resources in the PUCCH
+   *   resource set. This is used in pucch_index_used, which counts the used
+   *   resources by index, and not by their ID! */
+  NR_UE_sched_ctrl_t *sched_ctrl = &RC.nrmac[mod_id]->UE_info.UE_sched_ctrl[UE_id];
+  NR_sched_pucch_t *pucch = &sched_ctrl->sched_pucch[0];
+  AssertFatal(pucch->csi_bits == 0,
+              "%s(): csi_bits %d in sched_pucch[0]\n",
+              __func__,
+              pucch->csi_bits);
+
+  const int max_acknacks = 2;
+  AssertFatal(pucch->dai_c + pucch->sr_flag <= max_acknacks,
+              "illegal number of bits in PUCCH of UE %d\n",
+              UE_id);
+  /* if the currently allocated PUCCH of this UE is full, allocate it */
+  if (pucch->sr_flag + pucch->dai_c == max_acknacks) {
+    /* advance the UL slot information in PUCCH by one so we won't schedule in
+     * the same slot again */
+    const int f = pucch->frame;
+    const int s = pucch->ul_slot;
+    nr_fill_nfapi_pucch(mod_id, frame, slot, pucch, UE_id);
+    memset(pucch, 0, sizeof(*pucch));
+    pucch->frame = s == n_slots_frame - 1 ? (f + 1) % 1024 : f;
+    pucch->ul_slot = (s + 1) % n_slots_frame;
+    // we assume that only two indices over the array sched_pucch exist
+    const NR_sched_pucch_t *csi_pucch = &sched_ctrl->sched_pucch[2];
+    // skip the CSI PUCCH if it is present and if in the next frame/slot
+    if (csi_pucch->csi_bits > 0
+        && csi_pucch->frame == pucch->frame
+        && csi_pucch->ul_slot == pucch->ul_slot) {
+      AssertFatal(!csi_pucch->simultaneous_harqcsi,
+                  "%s(): %d.%d cannot handle simultaneous_harqcsi, but found for UE %d\n",
+                  __func__,
+                  pucch->frame,
+                  pucch->ul_slot,
+                  UE_id);
+      nr_fill_nfapi_pucch(mod_id, frame, slot, csi_pucch, UE_id);
+      pucch->frame = s >= n_slots_frame - 2 ?  (f + 1) % 1024 : f;
+      pucch->ul_slot = (s + 2) % n_slots_frame;
+    }
   }
 
-  // As a preference always schedule ack nacks in PUCCH0 (max 2 per slots)
-  // Unless there is CSI meas reporting scheduled in the period to avoid conflicts in the same slot
-  if (csi_pres)
-    max_acknacks=10;
-  else
-    max_acknacks=2;
+  /* if the UE's next PUCCH occasion is after the possible UL slots (within the
+   * same frame) or wrapped around to the next frame, then we assume there is
+   * no possible PUCCH allocation anymore */
+  if ((pucch->frame == frame
+       && (pucch->ul_slot >= first_ul_slot_tdd + nr_ulmix_slots))
+      || (pucch->frame == frame + 1))
+    return false;
 
   // this is hardcoded for now as ue specific
   NR_SearchSpace__searchSpaceType_PR ss_type = NR_SearchSpace__searchSpaceType_PR_ue_Specific;
-  get_pdsch_to_harq_feedback(Mod_idP,UE_id,ss_type,pdsch_to_harq_feedback);
-
-  // for each possible ul or mixed slot
-  for (k=0; k<nr_ulmix_slots; k++) {
-    for (l=0; l<1; l++) { // scheduling 2 PUCCH in a single slot does not work with the phone, currently
-      curr_pucch = &UE_info->UE_sched_ctrl[UE_id].sched_pucch[k][l];
-      //if it is possible to schedule acknack in current pucch (no exclusive csi pucch)
-      if ((curr_pucch->csi_bits == 0) || (curr_pucch->simultaneous_harqcsi==true)) {
-        // if there is free room in current pucch structure
-        if (curr_pucch->dai_c<max_acknacks) {
-          pucch_res = get_pucch_resource(UE_info,UE_id,k,l);
-          if (pucch_res>-1){
-            curr_pucch->resource_indicator = pucch_res;
-            curr_pucch->frame = frameP;
-            // first pucch occasion in first UL or MIXED slot
-            first_ul_slot_tdd = scc->tdd_UL_DL_ConfigurationCommon->pattern1.nrofDownlinkSlots;
-            i = 0;
-            while (i<8 && found == 0)  {  // look if timing indicator is among allowed values
-              if (pdsch_to_harq_feedback[i]==(first_ul_slot_tdd+k)-(slotP % slots_per_tdd))
-                found = 1;
-              if (found == 0) i++;
-            }
-            if (found == 1) {
-              // computing slot in which pucch is scheduled
-              curr_pucch->dai_c++;
-              curr_pucch->ul_slot = first_ul_slot_tdd + k + (slotP - (slotP % slots_per_tdd));
-              curr_pucch->timing_indicator = i; // index in the list of timing indicators
-              *pucch_id = k;
-              *pucch_occ = l;
-              return;
-            }
-          }
-        }
-      }
+  uint8_t pdsch_to_harq_feedback[8];
+  get_pdsch_to_harq_feedback(mod_id, UE_id, ss_type, pdsch_to_harq_feedback);
+
+  /* there is a scheduled SR or HARQ. Check whether we can use it for this
+   * ACKNACK */
+  if (pucch->sr_flag + pucch->dai_c > 0) {
+    /* this UE already has a PUCCH occasion */
+    DevAssert(pucch->frame == frame);
+
+    // Find the right timing_indicator value.
+    int i = 0;
+    while (i < 8) {
+      if (pdsch_to_harq_feedback[i] == pucch->ul_slot - slot)
+        break;
+      ++i;
+    }
+    if (i >= 8) {
+      // we cannot reach this timing anymore, allocate and try again
+      const int f = pucch->frame;
+      const int s = pucch->ul_slot;
+      const int n_slots_frame = nr_slots_per_frame[*scc->ssbSubcarrierSpacing];
+      nr_fill_nfapi_pucch(mod_id, frame, slot, pucch, UE_id);
+      memset(pucch, 0, sizeof(*pucch));
+      pucch->frame = s == n_slots_frame - 1 ? (f + 1) % 1024 : f;
+      pucch->ul_slot = (s + 1) % n_slots_frame;
+      return nr_acknack_scheduling(mod_id, UE_id, frame, slot);
+    }
+
+    pucch->timing_indicator = i;
+    pucch->dai_c++;
+    // retain old resource indicator, and we are good
+    return true;
+  }
+
+  /* we need to find a new PUCCH occasion */
+
+  NR_PUCCH_Config_t *pucch_Config = sched_ctrl->active_ubwp->bwp_Dedicated->pucch_Config->choice.setup;
+  DevAssert(pucch_Config->resourceToAddModList->list.count > 0);
+  DevAssert(pucch_Config->resourceSetToAddModList->list.count > 0);
+  const int n_res = pucch_Config->resourceSetToAddModList->list.array[0]->resourceList.list.count;
+  int *pucch_index_used = RC.nrmac[mod_id]->pucch_index_used[sched_ctrl->active_ubwp->bwp_Id];
+
+  /* if time information is outdated (e.g., last PUCCH occasion in last frame),
+   * set to first possible UL occasion in this frame. Note that if such UE is
+   * scheduled a lot and used all AckNacks, pucch->frame might have been
+   * wrapped around to next frame */
+  if (frame != pucch->frame || pucch->ul_slot < first_ul_slot_tdd) {
+    DevAssert(pucch->sr_flag + pucch->dai_c == 0);
+    AssertFatal(frame + 1 != pucch->frame,
+                "frame wrap around not handled in %s() yet\n",
+                __func__);
+    pucch->frame = frame;
+    pucch->ul_slot = first_ul_slot_tdd;
+  }
+
+  // increase to first slot in which PUCCH resources are available
+  while (pucch_index_used[pucch->ul_slot] >= n_res) {
+    pucch->ul_slot++;
+    /* if there is no free resource anymore, abort search */
+    if ((pucch->frame == frame
+         && pucch->ul_slot >= first_ul_slot_tdd + nr_ulmix_slots)
+        || (pucch->frame == frame + 1)) {
+      LOG_E(MAC,
+            "%4d.%2d no free PUCCH resources anymore while searching for UE %d\n",
+            frame,
+            slot,
+            UE_id);
+      return false;
     }
   }
-  AssertFatal(1==0,"No Uplink slot available in accordance to allowed timing indicator\n");
+
+  // advance ul_slot if it is not reachable by UE
+  pucch->ul_slot = max(pucch->ul_slot, slot + pdsch_to_harq_feedback[0]);
+
+  // Find the right timing_indicator value.
+  int i = 0;
+  while (i < 8) {
+    if (pdsch_to_harq_feedback[i] == pucch->ul_slot - slot)
+      break;
+    ++i;
+  }
+  if (i >= 8) {
+    LOG_W(MAC,
+          "%4d.%2d could not find pdsch_to_harq_feedback for UE %d: earliest "
+          "ack slot %d\n",
+          frame,
+          slot,
+          UE_id,
+          pucch->ul_slot);
+    return false;
+  }
+  pucch->timing_indicator = i; // index in the list of timing indicators
+
+  pucch->dai_c++;
+  const int pucch_res = pucch_index_used[pucch->ul_slot];
+  pucch->resource_indicator = pucch_res;
+  pucch_index_used[pucch->ul_slot] += 1;
+  AssertFatal(pucch_index_used[pucch->ul_slot] <= n_res,
+              "UE %d in %4d.%2d: pucch_index_used is %d (%d available)\n",
+              UE_id,
+              pucch->frame,
+              pucch->ul_slot,
+              pucch_index_used[pucch->ul_slot],
+              n_res);
+
+  /* verify that at that slot and symbol, resources are free. We only do this
+   * for initialCyclicShift 0 (we assume it always has that one), so other
+   * initialCyclicShifts can overlap with ICS 0!*/
+  const NR_PUCCH_Resource_t *resource =
+      pucch_Config->resourceToAddModList->list.array[pucch_res];
+  DevAssert(resource->format.present == NR_PUCCH_Resource__format_PR_format0);
+  if (resource->format.choice.format0->initialCyclicShift == 0) {
+    uint16_t *vrb_map_UL = &RC.nrmac[mod_id]->common_channels[CC_id].vrb_map_UL[pucch->ul_slot * MAX_BWP_SIZE];
+    const uint16_t symb = 1 << resource->format.choice.format0->startingSymbolIndex;
+    AssertFatal((vrb_map_UL[resource->startingPRB] & symb) == 0,
+                "symbol %x is not free for PUCCH alloc in vrb_map_UL at RB %ld and slot %d\n",
+                symb, resource->startingPRB, pucch->ul_slot);
+    vrb_map_UL[resource->startingPRB] |= symb;
+  }
+  return true;
 }
 
 
-void csi_period_offset(NR_CSI_ReportConfig_t *csirep,
+void csi_period_offset(const NR_CSI_ReportConfig_t *csirep,
                        int *period, int *offset) {
 
     NR_CSI_ReportPeriodicityAndOffset_PR p_and_o = csirep->reportConfigType.choice.periodic->reportSlotConfig.present;
@@ -532,24 +668,6 @@ void csi_period_offset(NR_CSI_ReportConfig_t *csirep,
     }
 }
 
-
-int get_pucch_resource(NR_UE_info_t *UE_info,int UE_id,int k,int l) {
-
-  // to be updated later, for now simple implementation
-  // use the second allocation just in case there is csi in the first
-  // in that case use second resource (for a different symbol) see 9.2 in 38.213
-  if (l==1) {
-    if (UE_info->UE_sched_ctrl[UE_id].sched_pucch[k][0].csi_bits==0)
-      return -1;
-    else
-      return 1;
-  }
-  else
-    return 0;
-
-}
-
-
 uint16_t compute_pucch_prb_size(uint8_t format,
                                 uint8_t nr_prbs,
                                 uint16_t O_tot,
diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c
index 1911eb07fb980f6d1f963318902ff5f63e4a02b0..6a266d006f7f95d0d0cf875d3fb2be6c9907b988 100644
--- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c
+++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c
@@ -33,6 +33,33 @@
 #include "executables/softmodem-common.h"
 #include "common/utils/nr/nr_common.h"
 
+//38.321 Table 6.1.3.1-1
+const uint32_t NR_SHORT_BSR_TABLE[32] = {
+    0,    10,    14,    20,    28,     38,     53,     74,
+  102,   142,   198,   276,   384,    535,    745,   1038,
+ 1446,  2014,  2806,  3909,  5446,   7587,  10570,  14726,
+20516, 28581, 39818, 55474, 77284, 107669, 150000, 300000
+};
+
+//38.321 Table 6.1.3.1-2
+const uint32_t NR_LONG_BSR_TABLE[256] ={
+       0,       10,       11,       12,       13,       14,       15,       16,       17,       18,       19,       20,       22,       23,        25,         26,
+      28,       30,       32,       34,       36,       38,       40,       43,       46,       49,       52,       55,       59,       62,        66,         71,
+      75,       80,       85,       91,       97,      103,      110,      117,      124,      132,      141,      150,      160,      170,       181,        193,
+     205,      218,      233,      248,      264,      281,      299,      318,      339,      361,      384,      409,      436,      464,       494,        526,
+     560,      597,      635,      677,      720,      767,      817,      870,      926,      987,     1051,     1119,     1191,     1269,      1351,       1439,
+    1532,     1631,     1737,     1850,     1970,     2098,     2234,     2379,     2533,     2698,     2873,     3059,     3258,     3469,      3694,       3934,
+    4189,     4461,     4751,     5059,     5387,     5737,     6109,     6506,     6928,     7378,     7857,     8367,     8910,     9488,     10104,      10760,
+   11458,    12202,    12994,    13838,    14736,    15692,    16711,    17795,    18951,    20181,    21491,    22885,    24371,    25953,     27638,      29431,
+   31342,    33376,    35543,    37850,    40307,    42923,    45709,    48676,    51836,    55200,    58784,    62599,    66663,    70990,     75598,      80505,
+   85730,    91295,    97221,   103532,   110252,   117409,   125030,   133146,   141789,   150992,   160793,   171231,   182345,   194182,    206786,     220209,
+  234503,   249725,   265935,   283197,   301579,   321155,   342002,   364202,   387842,   413018,   439827,   468377,   498780,   531156,    565634,     602350,
+  641449,   683087,   727427,   774645,   824928,   878475,   935498,   996222,  1060888,  1129752,  1203085,  1281179,  1364342,  1452903,   1547213,    1647644,
+ 1754595,  1868488,  1989774,  2118933,  2256475,  2402946,  2558924,  2725027,  2901912,  3090279,  3290873,  3504487,  3731968,  3974215,   4232186,    4506902,
+ 4799451,  5110989,  5442750,  5796046,  6172275,  6572925,  6999582,  7453933,  7937777,  8453028,  9001725,  9586039, 10208280, 10870913,  11576557,   12328006,
+13128233, 13980403, 14887889, 15854280, 16883401, 17979324, 19146385, 20389201, 21712690, 23122088, 24622972, 26221280, 27923336, 29735875,  31666069,   33721553,
+35910462, 38241455, 40723756, 43367187, 46182206, 49179951, 52372284, 55771835, 59392055, 63247269, 67352729, 71724679, 76380419, 81338368, 162676736, 4294967295
+};
 
 void nr_process_mac_pdu(
     module_id_t module_idP,
@@ -51,6 +78,13 @@ void nr_process_mac_pdu(
     uint16_t mac_ce_len, mac_subheader_len, mac_sdu_len;
 
 
+    NR_UE_info_t *UE_info = &RC.nrmac[module_idP]->UE_info;
+    int UE_id = find_nr_UE_id(module_idP, rnti);
+    if (UE_id == -1) {
+      LOG_E(MAC, "%s() UE_id == -1\n",__func__);
+      return;
+    }
+    NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id];
     //  For both DL/UL-SCH
     //  Except:
     //   - UL/DL-SCH: fixed-size MAC CE(known by LCID)
@@ -84,6 +118,9 @@ void nr_process_mac_pdu(
 
         LOG_D(MAC, "LCID received at gNB side: %d \n", rx_lcid);
 
+        unsigned char *ce_ptr;
+        int n_Lcg = 0;
+
         switch(rx_lcid){
             //  MAC CE
 
@@ -97,21 +134,26 @@ void nr_process_mac_pdu(
         case UL_SCH_LCID_CONFIGURED_GRANT_CONFIRMATION:
                 // 38.321 Ch6.1.3.7
                 break;
-        case UL_SCH_LCID_S_BSR:
-        	//38.321 section 6.1.3.1
-        	//fixed length
-        	mac_ce_len =1;
-        	/* Extract short BSR value */
-        	break;
 
+        case UL_SCH_LCID_S_BSR:
         case UL_SCH_LCID_S_TRUNCATED_BSR:
         	//38.321 section 6.1.3.1
         	//fixed length
         	mac_ce_len =1;
-        	/* Extract short truncated BSR value */
+        	/* Extract short BSR value */
+               ce_ptr = &pdu_ptr[mac_subheader_len];
+               NR_BSR_SHORT *bsr_s = (NR_BSR_SHORT *) ce_ptr;
+               sched_ctrl->estimated_ul_buffer = 0;
+               sched_ctrl->estimated_ul_buffer = NR_SHORT_BSR_TABLE[bsr_s->Buffer_size];
+               LOG_D(MAC, "SHORT BSR, LCG ID %d, BS Index %d, BS value < %d, est buf %d\n",
+                     bsr_s->LcgID,
+                     bsr_s->Buffer_size,
+                     NR_SHORT_BSR_TABLE[bsr_s->Buffer_size],
+                     sched_ctrl->estimated_ul_buffer);
         	break;
 
         case UL_SCH_LCID_L_BSR:
+        case UL_SCH_LCID_L_TRUNCATED_BSR:
         	//38.321 section 6.1.3.1
         	//variable length
         	mac_ce_len |= (uint16_t)((NR_MAC_SUBHEADER_SHORT *)pdu_ptr)->L;
@@ -121,20 +163,26 @@ void nr_process_mac_pdu(
         		mac_subheader_len = 3;
         	}
         	/* Extract long BSR value */
-        	break;
+               ce_ptr = &pdu_ptr[mac_subheader_len];
+               NR_BSR_LONG *bsr_l = (NR_BSR_LONG *) ce_ptr;
+               sched_ctrl->estimated_ul_buffer = 0;
 
-        case UL_SCH_LCID_L_TRUNCATED_BSR:
-        	//38.321 section 6.1.3.1
-        	//variable length
-        	mac_ce_len |= (uint16_t)((NR_MAC_SUBHEADER_SHORT *)pdu_ptr)->L;
-        	mac_subheader_len = 2;
-        	if(((NR_MAC_SUBHEADER_SHORT *)pdu_ptr)->F){
-        		mac_ce_len |= (uint16_t)(((NR_MAC_SUBHEADER_LONG *)pdu_ptr)->L2)<<8;
-        		mac_subheader_len = 3;
-        	}
-        	/* Extract long truncated BSR value */
-        	break;
+               n_Lcg = bsr_l->LcgID7 + bsr_l->LcgID6 + bsr_l->LcgID5 + bsr_l->LcgID4 +
+                       bsr_l->LcgID3 + bsr_l->LcgID2 + bsr_l->LcgID1 + bsr_l->LcgID0;
 
+               LOG_D(MAC, "LONG BSR, LCG ID(7-0) %d/%d/%d/%d/%d/%d/%d/%d\n",
+                     bsr_l->LcgID7, bsr_l->LcgID6, bsr_l->LcgID5, bsr_l->LcgID4,
+                     bsr_l->LcgID3, bsr_l->LcgID2, bsr_l->LcgID1, bsr_l->LcgID0);
+
+               for (int n = 0; n < n_Lcg; n++){
+                 LOG_D(MAC, "LONG BSR, %d/%d (n/n_Lcg), BS Index %d, BS value < %d",
+                       n, n_Lcg, pdu_ptr[mac_subheader_len + 1 + n],
+                       NR_LONG_BSR_TABLE[pdu_ptr[mac_subheader_len + 1 + n]]);
+                 sched_ctrl->estimated_ul_buffer +=
+                       NR_LONG_BSR_TABLE[pdu_ptr[mac_subheader_len + 1 + n]];
+               }
+
+        	break;
 
         case UL_SCH_LCID_C_RNTI:
         	//38.321 section 6.1.3.2
@@ -230,17 +278,28 @@ void nr_process_mac_pdu(
                                  1,
                                  NULL);
 
+                /* Updated estimated buffer when receiving data */
+                if (sched_ctrl->estimated_ul_buffer >= mac_sdu_len)
+                  sched_ctrl->estimated_ul_buffer -= mac_sdu_len;
+                else
+                  sched_ctrl->estimated_ul_buffer = 0;
+
             break;
 
         default:
-        	return;
-        	break;
+          LOG_D(MAC, "Received unknown MAC header (LCID = 0x%02x)\n", rx_lcid);
+          return;
+          break;
         }
         pdu_ptr += ( mac_subheader_len + mac_ce_len + mac_sdu_len );
         pdu_len -= ( mac_subheader_len + mac_ce_len + mac_sdu_len );
 
         if (pdu_len < 0) {
           LOG_E(MAC, "%s() residual mac pdu length < 0!, pdu_len: %d\n", __func__, pdu_len);
+          LOG_E(MAC, "MAC PDU ");
+          for (int i = 0; i < 20; i++) // Only printf 1st - 20nd bytes
+            printf("%02x ", pdu_ptr[i]);
+          printf("\n");
           return;
         }
     }
@@ -259,45 +318,51 @@ void handle_nr_ul_harq(module_id_t mod_id,
   NR_UE_info_t *UE_info = &RC.nrmac[mod_id]->UE_info;
   NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id];
 
-  int max_harq_rounds = 4; // TODO define macro
-  uint8_t hrq_id = crc_pdu->harq_id;
-  NR_UE_ul_harq_t *cur_harq = &sched_ctrl->ul_harq_processes[hrq_id];
-  if (cur_harq->state==ACTIVE_SCHED) {
-    if (!crc_pdu->tb_crc_status) {
-      cur_harq->ndi ^= 1;
-      cur_harq->round = 0;
-      cur_harq->state = INACTIVE; // passed -> make inactive. can be used by scheduder for next grant
-      LOG_D(MAC,
-            "Ulharq id %d crc passed for RNTI %04x\n",
-            hrq_id,
-            crc_pdu->rnti);
-    } else {
-      cur_harq->round++;
-      cur_harq->state = ACTIVE_NOT_SCHED;
-      LOG_D(MAC,
-            "Ulharq id %d crc failed for RNTI %04x\n",
-            hrq_id,
-            crc_pdu->rnti);
-    }
-
-    if (!(cur_harq->round<max_harq_rounds)) {
-      cur_harq->ndi ^= 1;
-      cur_harq->state = INACTIVE; // failed after 4 rounds -> make inactive
-      cur_harq->round = 0;
-      LOG_D(MAC,
-            "RNTI %04x: Ulharq id %d crc failed in all rounds\n",
-            crc_pdu->rnti,
-            hrq_id);
-      UE_info->mac_stats[UE_id].ulsch_errors++;
-    }
-    return;
-  } else
+  int8_t harq_pid = sched_ctrl->feedback_ul_harq.head;
+  while (crc_pdu->harq_id != harq_pid || harq_pid < 0) {
     LOG_W(MAC,
-          "Incorrect ULSCH HARQ PID %d or invalid state %d for RNTI %04x "
-          "(ignore this warning for RA)\n",
-          hrq_id,
-          cur_harq->state,
+          "Unexpected ULSCH HARQ PID %d (have %d) for RNTI %04x (ignore this warning for RA)\n",
+          crc_pdu->harq_id,
+          harq_pid,
+          crc_pdu->rnti);
+    if (harq_pid < 0)
+      return;
+
+    remove_front_nr_list(&sched_ctrl->feedback_ul_harq);
+    sched_ctrl->ul_harq_processes[harq_pid].round++;
+    add_tail_nr_list(&sched_ctrl->retrans_ul_harq, harq_pid);
+    harq_pid = sched_ctrl->feedback_ul_harq.head;
+  }
+  remove_front_nr_list(&sched_ctrl->feedback_ul_harq);
+  NR_UE_ul_harq_t *harq = &sched_ctrl->ul_harq_processes[harq_pid];
+  DevAssert(harq->is_waiting);
+  harq->feedback_slot = -1;
+  harq->is_waiting = false;
+  if (!crc_pdu->tb_crc_status) {
+    harq->ndi ^= 1;
+    harq->round = 0;
+    LOG_D(MAC,
+          "Ulharq id %d crc passed for RNTI %04x\n",
+          harq_pid,
+          crc_pdu->rnti);
+    add_tail_nr_list(&sched_ctrl->available_ul_harq, harq_pid);
+  } else if (harq->round == MAX_HARQ_ROUNDS) {
+    harq->ndi ^= 1;
+    harq->round = 0;
+    LOG_D(MAC,
+          "RNTI %04x: Ulharq id %d crc failed in all rounds\n",
+          crc_pdu->rnti,
+          harq_pid);
+    UE_info->mac_stats[UE_id].ulsch_errors++;
+    add_tail_nr_list(&sched_ctrl->available_ul_harq, harq_pid);
+  } else {
+    harq->round++;
+    LOG_D(MAC,
+          "Ulharq id %d crc failed for RNTI %04x\n",
+          harq_pid,
           crc_pdu->rnti);
+    add_tail_nr_list(&sched_ctrl->retrans_ul_harq, harq_pid);
+  }
 }
 
 /*
@@ -313,25 +378,21 @@ void nr_rx_sdu(const module_id_t gnb_mod_idP,
                const uint16_t timing_advance,
                const uint8_t ul_cqi,
                const uint16_t rssi){
-  int current_rnti = 0, UE_id = -1, harq_pid = 0;
-  gNB_MAC_INST *gNB_mac = NULL;
-  NR_UE_info_t *UE_info = NULL;
-  NR_UE_sched_ctrl_t *UE_scheduling_control = NULL;
-
-  current_rnti = rntiP;
-  UE_id = find_nr_UE_id(gnb_mod_idP, current_rnti);
-  gNB_mac = RC.nrmac[gnb_mod_idP];
-  UE_info = &gNB_mac->UE_info;
-  int target_snrx10 = gNB_mac->pusch_target_snrx10;
-
-  if (sduP != NULL) {
-    T(T_GNB_MAC_UL_PDU_WITH_DATA, T_INT(gnb_mod_idP), T_INT(CC_idP),
-      T_INT(rntiP), T_INT(frameP), T_INT(slotP), T_INT(-1) /* harq_pid */,
-      T_BUFFER(sduP, sdu_lenP));
-  }
+  gNB_MAC_INST *gNB_mac = RC.nrmac[gnb_mod_idP];
+  NR_UE_info_t *UE_info = &gNB_mac->UE_info;
+
+  const int current_rnti = rntiP;
+  const int UE_id = find_nr_UE_id(gnb_mod_idP, current_rnti);
+  const int target_snrx10 = gNB_mac->pusch_target_snrx10;
 
   if (UE_id != -1) {
-    UE_scheduling_control = &(UE_info->UE_sched_ctrl[UE_id]);
+    NR_UE_sched_ctrl_t *UE_scheduling_control = &UE_info->UE_sched_ctrl[UE_id];
+    const int8_t harq_pid = UE_scheduling_control->feedback_ul_harq.head;
+
+    if (sduP)
+      T(T_GNB_MAC_UL_PDU_WITH_DATA, T_INT(gnb_mod_idP), T_INT(CC_idP),
+        T_INT(rntiP), T_INT(frameP), T_INT(slotP), T_INT(harq_pid),
+        T_BUFFER(sduP, sdu_lenP));
 
     UE_info->mac_stats[UE_id].ulsch_total_bytes_rx += sdu_lenP;
     LOG_D(MAC, "[gNB %d][PUSCH %d] CC_id %d %d.%d Received ULSCH sdu from PHY (rnti %x, UE_id %d) ul_cqi %d sduP %p\n",
@@ -371,15 +432,30 @@ void nr_rx_sdu(const module_id_t gnb_mod_idP,
 
     if (sduP != NULL){
       LOG_D(MAC, "Received PDU at MAC gNB \n");
+
+      UE_scheduling_control->sched_ul_bytes -= UE_info->mac_stats[UE_id].ulsch_current_bytes;
+      if (UE_scheduling_control->sched_ul_bytes < 0)
+        UE_scheduling_control->sched_ul_bytes = 0;
+
       nr_process_mac_pdu(gnb_mod_idP, current_rnti, CC_idP, frameP, sduP, sdu_lenP);
     }
     else {
-
+      NR_UE_ul_harq_t *cur_harq = &UE_scheduling_control->ul_harq_processes[harq_pid];
+      /* reduce sched_ul_bytes when cur_harq->round == 3 */
+      if (cur_harq->round == 3){
+        UE_scheduling_control->sched_ul_bytes -= UE_info->mac_stats[UE_id].ulsch_current_bytes;
+        if (UE_scheduling_control->sched_ul_bytes < 0)
+          UE_scheduling_control->sched_ul_bytes = 0;
+      }
     }
   } else {
     if (!sduP) // check that CRC passed
       return;
 
+    T(T_GNB_MAC_UL_PDU_WITH_DATA, T_INT(gnb_mod_idP), T_INT(CC_idP),
+      T_INT(rntiP), T_INT(frameP), T_INT(slotP), T_INT(-1) /* harq_pid */,
+      T_BUFFER(sduP, sdu_lenP));
+
     /* we don't know this UE (yet). Check whether there is a ongoing RA (Msg 3)
      * and check the corresponding UE's RNTI match, in which case we activate
      * it. */
@@ -396,21 +472,8 @@ void nr_rx_sdu(const module_id_t gnb_mod_idP,
               current_rnti);
         continue;
       }
-      const int UE_id = add_new_nr_ue(gnb_mod_idP, ra->rnti);
-      UE_info->secondaryCellGroup[UE_id] = ra->secondaryCellGroup;
-      compute_csi_bitlen(ra->secondaryCellGroup, UE_info, UE_id);
+      const int UE_id = add_new_nr_ue(gnb_mod_idP, ra->rnti, ra->secondaryCellGroup);
       UE_info->UE_beam_index[UE_id] = ra->beam_id;
-      struct NR_ServingCellConfig__downlinkBWP_ToAddModList *bwpList = ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList;
-      AssertFatal(bwpList->list.count == 1,
-                  "downlinkBWP_ToAddModList has %d BWP!\n",
-                  bwpList->list.count);
-      const int bwp_id = 1;
-      UE_info->UE_sched_ctrl[UE_id].active_bwp = bwpList->list.array[bwp_id - 1];
-      struct NR_UplinkConfig__uplinkBWP_ToAddModList *ubwpList = ra->secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList;
-      AssertFatal(ubwpList->list.count == 1,
-                  "uplinkBWP_ToAddModList has %d BWP!\n",
-                  ubwpList->list.count);
-      UE_info->UE_sched_ctrl[UE_id].active_ubwp = ubwpList->list.array[bwp_id - 1];
       LOG_I(MAC,
             "[gNB %d][RAPROC] PUSCH with TC_RNTI %x received correctly, "
             "adding UE MAC Context UE_id %d/RNTI %04x\n",
@@ -446,30 +509,256 @@ long get_K2(NR_BWP_Uplink_t *ubwp, int time_domain_assignment, int mu) {
     return 3;
 }
 
-int8_t select_ul_harq_pid(NR_UE_sched_ctrl_t *sched_ctrl) {
-  const uint8_t max_ul_harq_pids = 3; // temp: for testing
-  // schedule active harq processes
-  for (uint8_t hrq_id = 0; hrq_id < max_ul_harq_pids; hrq_id++) {
-    NR_UE_ul_harq_t *cur_harq = &sched_ctrl->ul_harq_processes[hrq_id];
-    if (cur_harq->state == ACTIVE_NOT_SCHED) {
-      LOG_D(MAC, "Found ulharq id %d, scheduling it for retransmission\n", hrq_id);
-      return hrq_id;
+int next_list_entry_looped(NR_list_t *list, int UE_id)
+{
+  if (UE_id < 0)
+    return list->head;
+  return list->next[UE_id] < 0 ? list->head : list->next[UE_id];
+}
+
+float ul_thr_ue[MAX_MOBILES_PER_GNB];
+int bsr0ue = -1;
+void pf_ul(module_id_t module_id,
+           frame_t frame,
+           sub_frame_t slot,
+           int num_slots_per_tdd,
+           NR_list_t *UE_list,
+           int n_rb_sched,
+           uint8_t *rballoc_mask,
+           int max_num_ue) {
+
+  const int CC_id = 0;
+  const int tda = 1;
+  NR_ServingCellConfigCommon_t *scc = RC.nrmac[module_id]->common_channels[CC_id].ServingCellConfigCommon;
+  NR_UE_info_t *UE_info = &RC.nrmac[module_id]->UE_info;
+  const int min_rb = 5;
+  float coeff_ue[MAX_MOBILES_PER_GNB];
+  // UEs that could be scheduled
+  int ue_array[MAX_MOBILES_PER_GNB];
+  NR_list_t UE_sched = { .head = -1, .next = ue_array, .tail = -1, .len = MAX_MOBILES_PER_GNB };
+
+  /* Hack: currently, we do not have SR, and need to schedule UEs continuously.
+   * To keep the wasted resources low, we switch UEs to be scheduled in a
+   * round-robin fashion below, and only schedule a UE with BSR=0 if it is the
+   * selected one */
+  bsr0ue = next_list_entry_looped(UE_list, bsr0ue);
+
+  /* Loop UE_list to calculate throughput and coeff */
+  for (int UE_id = UE_list->head; UE_id >= 0; UE_id = UE_list->next[UE_id]) {
+    NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id];
+    int rbStart = NRRIV2PRBOFFSET(sched_ctrl->active_bwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE);
+    const uint16_t bwpSize = NRRIV2BW(sched_ctrl->active_ubwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE);
+
+    /* Calculate throughput */
+    const float a = 0.0005f; // corresponds to 200ms window
+    const uint32_t b = UE_info->mac_stats[UE_id].ulsch_current_bytes;
+    ul_thr_ue[UE_id] = (1 - a) * ul_thr_ue[UE_id] + a * b;
+
+    /* Save PUSCH field */
+    /* we want to avoid a lengthy deduction of DMRS and other parameters in
+     * every TTI if we can save it, so check whether dci_format, TDA, or
+     * num_dmrs_cdm_grps_no_data has changed and only then recompute */
+    sched_ctrl->sched_pusch.time_domain_allocation = tda;
+    sched_ctrl->search_space = get_searchspace(sched_ctrl->active_bwp, NR_SearchSpace__searchSpaceType_PR_ue_Specific);
+    sched_ctrl->coreset = get_coreset(sched_ctrl->active_bwp, sched_ctrl->search_space, 1 /* dedicated */);
+    const long f = sched_ctrl->search_space->searchSpaceType->choice.ue_Specific->dci_Formats;
+    const int dci_format = f ? NR_UL_DCI_FORMAT_0_1 : NR_UL_DCI_FORMAT_0_0;
+    const uint8_t num_dmrs_cdm_grps_no_data = 1;
+    NR_sched_pusch_save_t *ps = &sched_ctrl->pusch_save;
+    if (ps->time_domain_allocation != tda
+        || ps->dci_format != dci_format
+        || ps->num_dmrs_cdm_grps_no_data != num_dmrs_cdm_grps_no_data)
+      nr_save_pusch_fields(scc, sched_ctrl->active_ubwp, dci_format, tda, num_dmrs_cdm_grps_no_data, ps);
+
+    /* Check if retransmission is necessary */
+    sched_ctrl->sched_pusch.ul_harq_pid = sched_ctrl->retrans_ul_harq.head;
+    if (sched_ctrl->sched_pusch.ul_harq_pid >= 0) {
+      /* RETRANSMISSION: Allocate retransmission*/
+      /* Find free CCE */
+      bool freeCCE = find_free_CCE(module_id, slot, UE_id);
+      if (!freeCCE) {
+        LOG_D(MAC, "%4d.%2d no free CCE for retransmission UL DCI UE %04x\n", frame, slot, UE_info->rnti[UE_id]);
+        continue;
+      }
+      /* reduce max_num_ue once we are sure UE can be allocated, i.e., has CCE */
+      max_num_ue--;
+      if (max_num_ue < 0)
+        return;
+
+      /* Save shced_frame and sched_slot before overwrite by previous PUSCH filed */
+      NR_UE_ul_harq_t *cur_harq = &sched_ctrl->ul_harq_processes[sched_ctrl->sched_pusch.ul_harq_pid];
+      cur_harq->sched_pusch.frame = sched_ctrl->sched_pusch.frame;
+      cur_harq->sched_pusch.slot = sched_ctrl->sched_pusch.slot;
+      /* Get previous PSUCH filed info */
+      sched_ctrl->sched_pusch = cur_harq->sched_pusch;
+      NR_sched_pusch_t *sched_pusch = &sched_ctrl->sched_pusch;
+      LOG_D(MAC, "%4d.%2d Allocate UL retransmission UE %d/RNTI %04x sched %4d.%2d (%d RBs)\n",
+            frame, slot, UE_id, UE_info->rnti[UE_id],
+            sched_pusch->frame, sched_pusch->slot,
+            sched_pusch->rbSize);
+
+      while (rbStart < bwpSize && !rballoc_mask[rbStart]) rbStart++;
+      if (rbStart + sched_pusch->rbSize >= bwpSize) {
+        LOG_W(MAC, "cannot allocate UL data for UE %d/RNTI %04x: no resources\n",
+              UE_id, UE_info->rnti[UE_id]);
+        return;
+      }
+      sched_pusch->rbStart = rbStart;
+      /* no need to recompute the TBS, it will be the same */
+
+      /* Mark the corresponding RBs as used */
+      n_rb_sched -= sched_pusch->rbSize;
+      for (int rb = 0; rb < sched_ctrl->sched_pusch.rbSize; rb++)
+        rballoc_mask[rb + sched_ctrl->sched_pusch.rbStart] = 0;
+
+      continue;
+    }
+
+    /* Calculate TBS from MCS */
+    NR_sched_pusch_t *sched_pusch = &sched_ctrl->sched_pusch;
+    const int mcs = 9;
+    sched_pusch->mcs = mcs;
+    sched_pusch->R = nr_get_code_rate_ul(mcs, ps->mcs_table);
+    sched_pusch->Qm = nr_get_Qm_ul(mcs, ps->mcs_table);
+    if (ps->pusch_Config->tp_pi2BPSK
+        && ((ps->mcs_table == 3 && mcs < 2) || (ps->mcs_table == 4 && mcs < 6))) {
+      sched_pusch->R >>= 1;
+      sched_pusch->Qm <<= 1;
+    }
+
+    /* Check BSR and schedule UE if it is zero to avoid starvation, since we do
+     * not have SR (yet) */
+    if (sched_ctrl->estimated_ul_buffer - sched_ctrl->sched_ul_bytes <= 0) {
+      if (UE_id != bsr0ue)
+        continue;
+      /* if no data, pre-allocate 5RB */
+      bool freeCCE = find_free_CCE(module_id, slot, UE_id);
+      if (!freeCCE) {
+        LOG_D(MAC, "%4d.%2d no free CCE for UL DCI UE %04x (BSR 0)\n", frame, slot, UE_info->rnti[UE_id]);
+        continue;
+      }
+      /* reduce max_num_ue once we are sure UE can be allocated, i.e., has CCE */
+      max_num_ue--;
+      if (max_num_ue < 0)
+        return;
+
+      while (rbStart < bwpSize && !rballoc_mask[rbStart]) rbStart++;
+      if (rbStart + min_rb >= bwpSize) {
+        LOG_W(MAC, "cannot allocate UL data for UE %d/RNTI %04x: no resources\n",
+              UE_id, UE_info->rnti[UE_id]);
+        return;
+      }
+      sched_pusch->rbStart = rbStart;
+      sched_pusch->rbSize = min_rb;
+      sched_pusch->tb_size = nr_compute_tbs(sched_pusch->Qm,
+                                            sched_pusch->R,
+                                            sched_pusch->rbSize,
+                                            ps->nrOfSymbols,
+                                            ps->N_PRB_DMRS * ps->num_dmrs_symb,
+                                            0, // nb_rb_oh
+                                            0,
+                                            1 /* NrOfLayers */)
+                             >> 3;
+
+      /* Mark the corresponding RBs as used */
+      n_rb_sched -= sched_pusch->rbSize;
+      for (int rb = 0; rb < sched_ctrl->sched_pusch.rbSize; rb++)
+        rballoc_mask[rb + sched_ctrl->sched_pusch.rbStart] = 0;
+
+      continue;
     }
+
+    /* Create UE_sched for UEs eligibale for new data transmission*/
+    add_tail_nr_list(&UE_sched, UE_id);
+
+    /* Calculate coefficient*/
+    const uint32_t tbs = nr_compute_tbs(sched_pusch->Qm,
+                                        sched_pusch->R,
+                                        1, // rbSize
+                                        ps->nrOfSymbols,
+                                        ps->N_PRB_DMRS * ps->num_dmrs_symb,
+                                        0, // nb_rb_oh
+                                        0,
+                                        1 /* NrOfLayers */)
+                          >> 3;
+    coeff_ue[UE_id] = (float) tbs / ul_thr_ue[UE_id];
+    LOG_D(MAC,"b %d, ul_thr_ue[%d] %f, tbs %d, coeff_ue[%d] %f\n",
+          b, UE_id, ul_thr_ue[UE_id], tbs, UE_id, coeff_ue[UE_id]);
   }
 
-  // schedule new harq processes
-  for (uint8_t hrq_id=0; hrq_id < max_ul_harq_pids; hrq_id++) {
-    NR_UE_ul_harq_t *cur_harq = &sched_ctrl->ul_harq_processes[hrq_id];
-    if (cur_harq->state == INACTIVE) {
-      LOG_D(MAC, "Found new ulharq id %d, scheduling it\n", hrq_id);
-      return hrq_id;
+
+  /* Loop UE_sched to find max coeff and allocate transmission */
+  while (UE_sched.head >= 0 && max_num_ue> 0 && n_rb_sched > 0) {
+    /* Find max coeff */
+    int *max = &UE_sched.head; /* Find max coeff: assume head is max */
+    int *p = &UE_sched.next[*max];
+    while (*p >= 0) {
+      /* Find max coeff: if the current one has larger coeff, save for later */
+      if (coeff_ue[*p] > coeff_ue[*max])
+        max = p;
+      p = &UE_sched.next[*p];
     }
+    /* Find max coeff: remove the max one: do not use remove_nr_list() since it
+     * goes through the whole list every time. Note that UE_sched.tail might
+     * not be set correctly anymore */
+    const int UE_id = *max;
+    p = &UE_sched.next[*max];
+    *max = UE_sched.next[*max];
+    *p = -1;
+
+    bool freeCCE = find_free_CCE(module_id, slot, UE_id);
+    if (!freeCCE) {
+      LOG_D(MAC, "%4d.%2d no free CCE for UL DCI UE %04x\n", frame, slot, UE_info->rnti[UE_id]);
+      continue;
+    }
+
+    /* reduce max_num_ue once we are sure UE can be allocated, i.e., has CCE */
+    max_num_ue--;
+    if (max_num_ue < 0)
+      return;
+
+    NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id];
+    int rbStart = NRRIV2PRBOFFSET(sched_ctrl->active_bwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE);
+    const uint16_t bwpSize = NRRIV2BW(sched_ctrl->active_ubwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE);
+    NR_sched_pusch_t *sched_pusch = &sched_ctrl->sched_pusch;
+
+
+    while (rbStart < bwpSize && !rballoc_mask[rbStart]) rbStart++;
+    sched_pusch->rbStart = rbStart;
+    if (rbStart + min_rb >= bwpSize) {
+      LOG_W(MAC, "cannot allocate UL data for UE %d/RNTI %04x: no resources\n",
+            UE_id, UE_info->rnti[UE_id]);
+      return;
+    }
+
+    /* Calculate the current scheduling bytes */
+    const int B = cmax(sched_ctrl->estimated_ul_buffer - sched_ctrl->sched_ul_bytes, 0);
+    uint16_t rbSize = min_rb - 1;
+    do {
+      rbSize++;
+      sched_pusch->rbSize = rbSize;
+      sched_pusch->tb_size = nr_compute_tbs(sched_pusch->Qm,
+                                            sched_pusch->R,
+                                            sched_pusch->rbSize,
+                                            sched_ctrl->pusch_save.nrOfSymbols,
+                                            sched_ctrl->pusch_save.N_PRB_DMRS * sched_ctrl->pusch_save.num_dmrs_symb,
+                                            0, // nb_rb_oh
+                                            0,
+                                            1 /* NrOfLayers */)
+                             >> 3;
+    } while (rbStart + rbSize < bwpSize && rballoc_mask[rbStart+rbSize] &&
+             sched_pusch->tb_size < B);
+    LOG_D(MAC,"rbSize %d, TBS %d, est buf %d, sched_ul %d, B %d\n",
+          rbSize, sched_pusch->tb_size, sched_ctrl->estimated_ul_buffer, sched_ctrl->sched_ul_bytes, B);
+
+    /* Mark the corresponding RBs as used */
+    n_rb_sched -= sched_pusch->rbSize;
+    for (int rb = 0; rb < sched_ctrl->sched_pusch.rbSize; rb++)
+      rballoc_mask[rb + sched_ctrl->sched_pusch.rbStart] = 0;
   }
-  LOG_E(MAC, "All UL HARQ processes are busy. Cannot schedule ULSCH\n");
-  return -1;
 }
 
-void nr_simple_ulsch_preprocessor(module_id_t module_id,
+bool nr_simple_ulsch_preprocessor(module_id_t module_id,
                                   frame_t frame,
                                   sub_frame_t slot,
                                   int num_slots_per_tdd,
@@ -480,17 +769,14 @@ void nr_simple_ulsch_preprocessor(module_id_t module_id,
   const int mu = scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.subcarrierSpacing;
   NR_UE_info_t *UE_info = &nr_mac->UE_info;
 
-  AssertFatal(UE_info->num_UEs <= 1,
-              "%s() cannot handle more than one UE, but found %d\n",
-              __func__,
-              UE_info->num_UEs);
   if (UE_info->num_UEs == 0)
-    return;
+    return false;
 
-  const int UE_id = 0;
   const int CC_id = 0;
-  NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id];
 
+  /* NOT support different K2 in here, Get the K2 for first UE */
+  int UE_id = UE_info->list.head;
+  NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id];
   const int tda = 1;
   const struct NR_PUSCH_TimeDomainResourceAllocationList *tdaList =
     sched_ctrl->active_ubwp->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList;
@@ -498,94 +784,59 @@ void nr_simple_ulsch_preprocessor(module_id_t module_id,
               "time domain assignment %d >= %d\n",
               tda,
               tdaList->list.count);
+
   int K2 = get_K2(sched_ctrl->active_ubwp, tda, mu);
-  const int sched_frame = frame + (slot + K2 >= num_slots_per_tdd);
-  const int sched_slot = (slot + K2) % num_slots_per_tdd;
+  const int sched_frame = frame + (slot + K2 >= nr_slots_per_frame[mu]);
+  const int sched_slot = (slot + K2) % nr_slots_per_frame[mu];
   if (!is_xlsch_in_slot(ulsch_in_slot_bitmap, sched_slot))
-    return;
-
-  /* get first, largest unallocated region */
-  uint16_t *vrb_map_UL =
-      &RC.nrmac[module_id]->common_channels[CC_id].vrb_map_UL[sched_slot * 275];
-  uint16_t rbStart = 0;
-  while (vrb_map_UL[rbStart]) rbStart++;
-  const uint16_t bwpSize = NRRIV2BW(sched_ctrl->active_ubwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE);
-  uint16_t rbSize = 1;
-  while (rbStart + rbSize < bwpSize && !vrb_map_UL[rbStart+rbSize])
-    rbSize++;
+    return false;
 
   sched_ctrl->sched_pusch.slot = sched_slot;
   sched_ctrl->sched_pusch.frame = sched_frame;
 
-  const int target_ss = NR_SearchSpace__searchSpaceType_PR_ue_Specific;
-  sched_ctrl->search_space = get_searchspace(sched_ctrl->active_bwp, target_ss);
-  uint8_t nr_of_candidates;
-  find_aggregation_candidates(&sched_ctrl->aggregation_level,
-                              &nr_of_candidates,
-                              sched_ctrl->search_space);
-  sched_ctrl->coreset = get_coreset(
-      sched_ctrl->active_bwp, sched_ctrl->search_space, 1 /* dedicated */);
-  const int cid = sched_ctrl->coreset->controlResourceSetId;
-  const uint16_t Y = UE_info->Y[UE_id][cid][slot];
-  const int m = UE_info->num_pdcch_cand[UE_id][cid];
-  sched_ctrl->cce_index = allocate_nr_CCEs(RC.nrmac[module_id],
-                                           sched_ctrl->active_bwp,
-                                           sched_ctrl->coreset,
-                                           sched_ctrl->aggregation_level,
-                                           Y,
-                                           m,
-                                           nr_of_candidates);
-  if (sched_ctrl->cce_index < 0) {
-    LOG_E(MAC, "%s(): CCE list not empty, couldn't schedule PUSCH\n", __func__);
-    return;
-  }
-  UE_info->num_pdcch_cand[UE_id][cid]++;
-
-  sched_ctrl->sched_pusch.time_domain_allocation = tda;
-  const long f = sched_ctrl->search_space->searchSpaceType->choice.ue_Specific->dci_Formats;
-  const int dci_format = f ? NR_UL_DCI_FORMAT_0_1 : NR_UL_DCI_FORMAT_0_0;
-  const uint8_t num_dmrs_cdm_grps_no_data = 1;
-  /* we want to avoid a lengthy deduction of DMRS and other parameters in
-   * every TTI if we can save it, so check whether dci_format, TDA, or
-   * num_dmrs_cdm_grps_no_data has changed and only then recompute */
-  NR_sched_pusch_save_t *ps = &sched_ctrl->pusch_save;
-  if (ps->time_domain_allocation != tda
-      || ps->dci_format != dci_format
-      || ps->num_dmrs_cdm_grps_no_data != num_dmrs_cdm_grps_no_data)
-    nr_save_pusch_fields(scc,
-                         sched_ctrl->active_ubwp,
-                         dci_format,
-                         tda,
-                         num_dmrs_cdm_grps_no_data,
-                         ps);
-
-  const int mcs = 9;
-  NR_sched_pusch_t *sched_pusch = &sched_ctrl->sched_pusch;
-  sched_pusch->mcs = mcs;
-  sched_pusch->rbStart = rbStart;
-  sched_pusch->rbSize = rbSize;
-
-  /* Calculate TBS from MCS */
-  sched_pusch->R = nr_get_code_rate_ul(mcs, ps->mcs_table);
-  sched_pusch->Qm = nr_get_Qm_ul(mcs, ps->mcs_table);
-  if (ps->pusch_Config->tp_pi2BPSK
-      && ((ps->mcs_table == 3 && mcs < 2) || (ps->mcs_table == 4 && mcs < 6))) {
-    sched_pusch->R >>= 1;
-    sched_pusch->Qm <<= 1;
+  /* Confirm all the UE have same K2 as the first UE */
+  for (UE_id = UE_info->list.next[UE_id]; UE_id >= 0; UE_id = UE_info->list.next[UE_id]) {
+    NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id];
+    AssertFatal(K2 == get_K2(sched_ctrl->active_ubwp, tda, mu),
+                "Different K2, %d(UE%d) != %ld(UE%d)\n", K2, 0, get_K2(sched_ctrl->active_ubwp, tda, mu), UE_id);
+    sched_ctrl->sched_pusch.slot = sched_slot;
+    sched_ctrl->sched_pusch.frame = sched_frame;
   }
-  sched_pusch->tb_size = nr_compute_tbs(sched_pusch->Qm,
-                                        sched_pusch->R,
-                                        sched_pusch->rbSize,
-                                        ps->nrOfSymbols,
-                                        ps->N_PRB_DMRS * ps->num_dmrs_symb,
-                                        0, // nb_rb_oh
-                                        0,
-                                        1 /* NrOfLayers */)
-                         >> 3;
 
-  /* mark the corresponding RBs as used */
-  for (int rb = 0; rb < sched_ctrl->sched_pusch.rbSize; rb++)
-    vrb_map_UL[rb + sched_ctrl->sched_pusch.rbStart] = 1;
+  /* Change vrb_map_UL to rballoc_mask */
+  uint16_t *vrb_map_UL =
+      &RC.nrmac[module_id]->common_channels[CC_id].vrb_map_UL[sched_slot * MAX_BWP_SIZE];
+  const uint16_t bwpSize = NRRIV2BW(sched_ctrl->active_ubwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE);
+  int st = 0, e = 0, len = 0;
+  for (int i = 0; i < bwpSize; i++) {
+    while (vrb_map_UL[i] == 1)
+      i++;
+    st = i;
+    while (vrb_map_UL[i] == 0)
+      i++;
+    if (i - st > len) {
+      len = i - st;
+      e = i - 1;
+    }
+  }
+  st = e - len + 1;
+
+  uint8_t rballoc_mask[bwpSize];
+
+  /* Calculate mask: if any RB in vrb_map_UL is blocked (1), the current RB will be 0 */
+  for (int i = 0; i < bwpSize; i++)
+    rballoc_mask[i] = i >= st && i <= e;
+
+  /* proportional fair scheduling algorithm */
+  pf_ul(module_id,
+        frame,
+        slot,
+        num_slots_per_tdd,
+        &UE_info->list,
+        len,
+        rballoc_mask,
+        2);
+  return true;
 }
 
 void nr_schedule_ulsch(module_id_t module_id,
@@ -594,14 +845,32 @@ void nr_schedule_ulsch(module_id_t module_id,
                        int num_slots_per_tdd,
                        int ul_slots,
                        uint64_t ulsch_in_slot_bitmap) {
-  RC.nrmac[module_id]->pre_processor_ul(
+  /* Uplink data ONLY can be scheduled when the current slot is downlink slot,
+   * because we have to schedule the DCI0 first before schedule uplink data */
+  if (is_xlsch_in_slot(ulsch_in_slot_bitmap, slot)) {
+    LOG_D(MAC, "Current slot %d is NOT DL slot, cannot schedule DCI0 for UL data\n", slot);
+    return;
+  }
+  bool do_sched = RC.nrmac[module_id]->pre_processor_ul(
       module_id, frame, slot, num_slots_per_tdd, ulsch_in_slot_bitmap);
+  if (!do_sched)
+    return;
+
+  const int CC_id = 0;
+  nfapi_nr_ul_dci_request_t *ul_dci_req = &RC.nrmac[module_id]->UL_dci_req[CC_id];
+  ul_dci_req->SFN = frame;
+  ul_dci_req->Slot = slot;
+  /* a PDCCH PDU groups DCIs per BWP and CORESET. Save a pointer to each
+   * allocated PDCCH so we can easily allocate UE's DCIs independent of any
+   * CORESET order */
+  nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_bwp_coreset[MAX_NUM_BWP][MAX_NUM_CORESET] = {0};
 
   NR_ServingCellConfigCommon_t *scc = RC.nrmac[module_id]->common_channels[0].ServingCellConfigCommon;
   NR_UE_info_t *UE_info = &RC.nrmac[module_id]->UE_info;
-  const NR_UE_list_t *UE_list = &UE_info->list;
+  const NR_list_t *UE_list = &UE_info->list;
   for (int UE_id = UE_list->head; UE_id >= 0; UE_id = UE_list->next[UE_id]) {
     NR_UE_sched_ctrl_t *sched_ctrl = &UE_info->UE_sched_ctrl[UE_id];
+    UE_info->mac_stats[UE_id].ulsch_current_bytes = 0;
     /* dynamic PUSCH values (RB alloc, MCS, hence R, Qm, TBS) that change in
      * every TTI are pre-populated by the preprocessor and used below */
     NR_sched_pusch_t *sched_pusch = &sched_ctrl->sched_pusch;
@@ -610,11 +879,29 @@ void nr_schedule_ulsch(module_id_t module_id,
 
     uint16_t rnti = UE_info->rnti[UE_id];
 
-    int8_t harq_id = select_ul_harq_pid(sched_ctrl);
-    if (harq_id < 0) return;
+    int8_t harq_id = sched_pusch->ul_harq_pid;
+    if (harq_id < 0) {
+      /* PP has not selected a specific HARQ Process, get a new one */
+      harq_id = sched_ctrl->available_ul_harq.head;
+      AssertFatal(harq_id >= 0,
+                  "no free HARQ process available for UE %d\n",
+                  UE_id);
+      remove_front_nr_list(&sched_ctrl->available_ul_harq);
+      sched_pusch->ul_harq_pid = harq_id;
+    } else {
+      /* PP selected a specific HARQ process. Check whether it will be a new
+       * transmission or a retransmission, and remove from the corresponding
+       * list */
+      if (sched_ctrl->ul_harq_processes[harq_id].round == 0)
+        remove_nr_list(&sched_ctrl->available_ul_harq, harq_id);
+      else
+        remove_nr_list(&sched_ctrl->retrans_ul_harq, harq_id);
+    }
     NR_UE_ul_harq_t *cur_harq = &sched_ctrl->ul_harq_processes[harq_id];
-    cur_harq->state = ACTIVE_SCHED;
-    cur_harq->last_tx_slot = sched_pusch->slot;
+    DevAssert(!cur_harq->is_waiting);
+    add_tail_nr_list(&sched_ctrl->feedback_ul_harq, harq_id);
+    cur_harq->feedback_slot = sched_pusch->slot;
+    cur_harq->is_waiting = true;
 
     int rnti_types[2] = { NR_RNTI_C, 0 };
 
@@ -627,6 +914,9 @@ void nr_schedule_ulsch(module_id_t module_id,
     UE_info->mac_stats[UE_id].ulsch_rounds[cur_harq->round]++;
     if (cur_harq->round == 0) {
       UE_info->mac_stats[UE_id].ulsch_total_bytes_scheduled += sched_pusch->tb_size;
+      /* Save information on MCS, TBS etc for the current initial transmission
+       * so we have access to it when retransmitting */
+      cur_harq->sched_pusch = *sched_pusch;
     } else {
       LOG_D(MAC,
             "%d.%2d UL retransmission RNTI %04x sched %d.%2d HARQ PID %d round %d NDI %d\n",
@@ -639,6 +929,8 @@ void nr_schedule_ulsch(module_id_t module_id,
             cur_harq->round,
             cur_harq->ndi);
     }
+    UE_info->mac_stats[UE_id].ulsch_current_bytes = sched_pusch->tb_size;
+    sched_ctrl->sched_ul_bytes += sched_pusch->tb_size;
 
     LOG_D(MAC,
           "%4d.%2d RNTI %04x UL sched %4d.%2d start %d RBS %d MCS %d TBS %d HARQ PID %d round %d NDI %d\n",
@@ -781,51 +1073,59 @@ void nr_schedule_ulsch(module_id_t module_id,
       pusch_pdu->pdu_bit_map &= ~PUSCH_PDU_BITMAP_PUSCH_PTRS; // disable PUSCH PTRS
     }
 
-    nfapi_nr_ul_dci_request_t *ul_dci_req = &RC.nrmac[module_id]->UL_dci_req[0];
-    ul_dci_req->SFN = frame;
-    ul_dci_req->Slot = slot;
-    nfapi_nr_ul_dci_request_pdus_t *ul_dci_request_pdu = &ul_dci_req->ul_dci_pdu_list[ul_dci_req->numPdus];
-    memset(ul_dci_request_pdu, 0, sizeof(nfapi_nr_ul_dci_request_pdus_t));
-    ul_dci_request_pdu->PDUType = NFAPI_NR_DL_TTI_PDCCH_PDU_TYPE;
-    ul_dci_request_pdu->PDUSize = (uint8_t)(2+sizeof(nfapi_nr_dl_tti_pdcch_pdu));
-    nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15 = &ul_dci_request_pdu->pdcch_pdu.pdcch_pdu_rel15;
-    ul_dci_req->numPdus += 1;
+    /* look up the PDCCH PDU for this BWP and CORESET. If it does not exist,
+     * create it */
+    const int bwpid = sched_ctrl->active_bwp->bwp_Id;
+    const int coresetid = sched_ctrl->coreset->controlResourceSetId;
+    nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu = pdcch_pdu_bwp_coreset[bwpid][coresetid];
+    if (!pdcch_pdu) {
+      nfapi_nr_ul_dci_request_pdus_t *ul_dci_request_pdu = &ul_dci_req->ul_dci_pdu_list[ul_dci_req->numPdus];
+      memset(ul_dci_request_pdu, 0, sizeof(nfapi_nr_ul_dci_request_pdus_t));
+      ul_dci_request_pdu->PDUType = NFAPI_NR_DL_TTI_PDCCH_PDU_TYPE;
+      ul_dci_request_pdu->PDUSize = (uint8_t)(2+sizeof(nfapi_nr_dl_tti_pdcch_pdu));
+      pdcch_pdu = &ul_dci_request_pdu->pdcch_pdu.pdcch_pdu_rel15;
+      ul_dci_req->numPdus += 1;
+      nr_configure_pdcch(pdcch_pdu, sched_ctrl->search_space, sched_ctrl->coreset, scc, sched_ctrl->active_bwp);
+      pdcch_pdu_bwp_coreset[bwpid][coresetid] = pdcch_pdu;
+    }
 
     LOG_D(MAC,"Configuring ULDCI/PDCCH in %d.%d\n", frame,slot);
 
-    nr_configure_pdcch(RC.nrmac[0],
-                       pdcch_pdu_rel15,
-                       rnti,
-                       sched_ctrl->search_space,
-                       sched_ctrl->coreset,
-                       scc,
-                       sched_ctrl->active_bwp,
-                       sched_ctrl->aggregation_level,
-                       sched_ctrl->cce_index);
-
-    dci_pdu_rel15_t dci_pdu_rel15[MAX_DCI_CORESET];
-    memset(dci_pdu_rel15, 0, sizeof(dci_pdu_rel15));
+    /* Fill PDCCH DL DCI PDU */
+    nfapi_nr_dl_dci_pdu_t *dci_pdu = &pdcch_pdu->dci_pdu[pdcch_pdu->numDlDci];
+    pdcch_pdu->numDlDci++;
+    dci_pdu->RNTI = rnti;
+    if (sched_ctrl->coreset->pdcch_DMRS_ScramblingID &&
+        sched_ctrl->search_space->searchSpaceType->present == NR_SearchSpace__searchSpaceType_PR_ue_Specific) {
+      dci_pdu->ScramblingId = *sched_ctrl->coreset->pdcch_DMRS_ScramblingID;
+      dci_pdu->ScramblingRNTI = rnti;
+    } else {
+      dci_pdu->ScramblingId = *scc->physCellId;
+      dci_pdu->ScramblingRNTI = 0;
+    }
+    dci_pdu->AggregationLevel = sched_ctrl->aggregation_level;
+    dci_pdu->CceIndex = sched_ctrl->cce_index;
+    dci_pdu->beta_PDCCH_1_0 = 0;
+    dci_pdu->powerControlOffsetSS = 1;
+
+    dci_pdu_rel15_t uldci_payload;
+    memset(&uldci_payload, 0, sizeof(uldci_payload));
     NR_CellGroupConfig_t *secondaryCellGroup = UE_info->secondaryCellGroup[UE_id];
     const int n_ubwp = secondaryCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->uplinkBWP_ToAddModList->list.count;
-    // NOTE: below functions assume that dci_formats is an array corresponding
-    // to all UL DCIs in the PDCCH, but for us it is a simple int. So before
-    // having multiple UEs, the below need to be changed (IMO the functions
-    // should fill for one DCI only and not handle all of them).
     config_uldci(sched_ctrl->active_ubwp,
                  pusch_pdu,
-                 pdcch_pdu_rel15,
-                 &dci_pdu_rel15[0],
-                 &ps->dci_format,
+                 &uldci_payload,
+                 ps->dci_format,
                  ps->time_domain_allocation,
                  UE_info->UE_sched_ctrl[UE_id].tpc0,
                  n_ubwp,
                  sched_ctrl->active_bwp->bwp_Id);
     fill_dci_pdu_rel15(scc,
                        secondaryCellGroup,
-                       pdcch_pdu_rel15,
-                       dci_pdu_rel15,
-                       &ps->dci_format,
-                       rnti_types,
+                       dci_pdu,
+                       &uldci_payload,
+                       ps->dci_format,
+                       rnti_types[0],
                        pusch_pdu->bwp_size,
                        sched_ctrl->active_bwp->bwp_Id);
 
diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h
index a8dbec98f588fca335d3c7b1635495ac9a726bc3..26d29f9412815a4e778887a9cd909869bb1cda10 100644
--- a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h
+++ b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h
@@ -64,21 +64,9 @@ void clear_nr_nfapi_information(gNB_MAC_INST * gNB,
 void gNB_dlsch_ulsch_scheduler(module_id_t module_idP,
 			       frame_t frame_rxP, sub_frame_t slot_rxP);
 
-int nr_generate_dlsch_pdu(module_id_t Mod_idP,
-                          NR_UE_sched_ctrl_t *ue_sched_ctl,
-                          unsigned char *sdus_payload,
-                          unsigned char *mac_pdu,
-                          unsigned char num_sdus,
-                          unsigned short *sdu_lengths,
-                          unsigned char *sdu_lcids,
-                          unsigned char drx_cmd,
-                          unsigned char *ue_cont_res_id,
-                          unsigned short post_padding);
-
 void nr_schedule_ue_spec(module_id_t module_id,
                          frame_t frame,
-                         sub_frame_t slot,
-                         int num_slots_per_tdd);
+                         sub_frame_t slot);
 
 void schedule_control_sib1(module_id_t module_id,
                            int CC_id,
@@ -92,8 +80,7 @@ void schedule_nr_sib1(module_id_t module_idP, frame_t frameP, sub_frame_t subfra
 /* \brief default preprocessor */
 void nr_simple_dlsch_preprocessor(module_id_t module_id,
                                   frame_t frame,
-                                  sub_frame_t slot,
-                                  int num_slots_per_tdd);
+                                  sub_frame_t slot);
 
 void schedule_nr_mib(module_id_t module_idP, frame_t frameP, sub_frame_t subframeP, uint8_t slots_per_frame);
 
@@ -105,7 +92,7 @@ void nr_schedule_ulsch(module_id_t module_id,
                        int ul_slots,
                        uint64_t ulsch_in_slot_bitmap);
 
-void nr_simple_ulsch_preprocessor(module_id_t module_id,
+bool nr_simple_ulsch_preprocessor(module_id_t module_id,
                                   frame_t frame,
                                   sub_frame_t slot,
                                   int num_slots_per_tdd,
@@ -159,11 +146,10 @@ uint16_t nr_mac_compute_RIV(uint16_t N_RB_DL, uint16_t RBstart, uint16_t Lcrbs);
  * freq resources */
 void nr_preprocessor_phytest(module_id_t module_id,
                              frame_t frame,
-                             sub_frame_t slot,
-                             int num_slots_per_tdd);
+                             sub_frame_t slot);
 /* \brief UL preprocessor for phytest: schedules UE_id 0 with fixed MCS on a
  * fixed set of resources */
-void nr_ul_preprocessor_phytest(module_id_t module_id,
+bool nr_ul_preprocessor_phytest(module_id_t module_id,
                                 frame_t frame,
                                 sub_frame_t slot,
                                 int num_slots_per_tdd,
@@ -173,22 +159,6 @@ void nr_schedule_css_dlsch_phytest(module_id_t   module_idP,
                                    frame_t       frameP,
                                    sub_frame_t   subframeP);
 
-void nr_fill_nfapi_dl_pdu(int Mod_id,
-                          nfapi_nr_dl_tti_request_body_t *dl_req,
-                          rnti_t rnti,
-                          NR_CellGroupConfig_t *secondaryCellGroup,
-                          NR_UE_sched_ctrl_t *sched_ctrl,
-                          NR_sched_pucch *pucch_sched,
-                          nfapi_nr_dmrs_type_e dmrsConfigType,
-                          uint16_t R,
-                          uint8_t Qm,
-                          uint32_t tbs,
-                          int StartSymbolIndex,
-                          int NrOfSymbols,
-                          int harq_pid,
-                          int ndi,
-                          int round);
-
 void handle_nr_uci_pucch_0_1(module_id_t mod_id,
                              frame_t frame,
                              sub_frame_t slot,
@@ -199,40 +169,30 @@ void handle_nr_uci_pucch_2_3_4(module_id_t mod_id,
                                const nfapi_nr_uci_pucch_pdu_format_2_3_4_t *uci_234);
 
 
-void config_uldci(NR_BWP_Uplink_t *ubwp,
-                  nfapi_nr_pusch_pdu_t *pusch_pdu,
-                  nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15,
+void config_uldci(const NR_BWP_Uplink_t *ubwp,
+                  const nfapi_nr_pusch_pdu_t *pusch_pdu,
                   dci_pdu_rel15_t *dci_pdu_rel15,
-                  int *dci_formats,
-                  int time_domain_assignment, uint8_t tpc,
-                  int n_ubwp, int bwp_id);
+                  int dci_format,
+                  int time_domain_assignment,
+                  uint8_t tpc,
+                  int n_ubwp,
+                  int bwp_id);
 
 void nr_schedule_pucch(int Mod_idP,
-                       int UE_id,
-                       int nr_ulmix_slots,
                        frame_t frameP,
                        sub_frame_t slotP);
 
-void csi_period_offset(NR_CSI_ReportConfig_t *csirep,
+void csi_period_offset(const NR_CSI_ReportConfig_t *csirep,
                        int *period, int *offset);
 
 void nr_csi_meas_reporting(int Mod_idP,
-                           int UE_id,
                            frame_t frameP,
-                           sub_frame_t slotP,
-                           int slots_per_tdd,
-                           int ul_slots,
-                           int n_slots_frame);
+                           sub_frame_t slotP);
 
-void nr_acknack_scheduling(int Mod_idP,
+bool nr_acknack_scheduling(int Mod_idP,
                            int UE_id,
                            frame_t frameP,
-                           sub_frame_t slotP,
-                           int slots_per_tdd,
-                           int *pucch_id,
-                           int *pucch_occ);
-
-int get_pucch_resource(NR_UE_info_t *UE_info,int UE_id,int k,int l);
+                           sub_frame_t slotP);
 
 void get_pdsch_to_harq_feedback(int Mod_idP,
                                 int UE_id,
@@ -271,26 +231,22 @@ void find_search_space(int ss_type,
                        NR_BWP_Downlink_t *bwp,
                        NR_SearchSpace_t *ss);
 
-void nr_configure_pdcch(gNB_MAC_INST *nr_mac,
-                        nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu,
-                        uint16_t rnti,
+void nr_configure_pdcch(nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu,
                         NR_SearchSpace_t *ss,
                         NR_ControlResourceSet_t *coreset,
                         NR_ServingCellConfigCommon_t *scc,
-                        NR_BWP_Downlink_t *bwp,
-                        uint8_t aggregation_level,
-                        int CCEIndex);
+                        NR_BWP_Downlink_t *bwp);
 
-void fill_dci_pdu_rel15(NR_ServingCellConfigCommon_t *scc,
-                        NR_CellGroupConfig_t *secondaryCellGroup,
-                        nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15,
+void fill_dci_pdu_rel15(const NR_ServingCellConfigCommon_t *scc,
+                        const NR_CellGroupConfig_t *secondaryCellGroup,
+                        nfapi_nr_dl_dci_pdu_t *pdcch_dci_pdu,
                         dci_pdu_rel15_t *dci_pdu_rel15,
-                        int *dci_formats,
-                        int *rnti_types,
+                        int dci_formats,
+                        int rnti_types,
                         int N_RB,
                         int bwp_id);
 
-void prepare_dci(NR_CellGroupConfig_t *secondaryCellGroup,
+void prepare_dci(const NR_CellGroupConfig_t *secondaryCellGroup,
                  dci_pdu_rel15_t *dci_pdu_rel15,
                  nr_dci_format_t format,
                  int bwp_id);
@@ -337,14 +293,21 @@ int NRRIV2BW(int locationAndBandwidth,int N_RB);
 
 int NRRIV2PRBOFFSET(int locationAndBandwidth,int N_RB);
 
-void dump_nr_ue_list(NR_UE_list_t *listP);
-void add_nr_ue_list(NR_UE_list_t *listP, int UE_id);
+/* Functions to manage an NR_list_t */
+void dump_nr_list(NR_list_t *listP);
+void create_nr_list(NR_list_t *listP, int len);
+void destroy_nr_list(NR_list_t *list);
+void add_nr_list(NR_list_t *listP, int id);
+void remove_nr_list(NR_list_t *listP, int id);
+void add_tail_nr_list(NR_list_t *listP, int id);
+void add_front_nr_list(NR_list_t *listP, int id);
+void remove_front_nr_list(NR_list_t *listP);
 
 int find_nr_UE_id(module_id_t mod_idP, rnti_t rntiP);
 
 int find_nr_RA_id(module_id_t mod_idP, int CC_idP, rnti_t rntiP);
 
-int add_new_nr_ue(module_id_t mod_idP, rnti_t rntiP);
+int add_new_nr_ue(module_id_t mod_idP, rnti_t rntiP, NR_CellGroupConfig_t *secondaryCellGroup);
 
 void mac_remove_nr_ue(module_id_t mod_id, rnti_t rnti);
 
@@ -438,4 +401,5 @@ int16_t ssb_index_from_prach(module_id_t module_idP,
 
 void find_SSB_and_RO_available(module_id_t module_idP);
 
+bool find_free_CCE(module_id_t module_id, sub_frame_t slot, int UE_id);
 #endif /*__LAYER2_NR_MAC_PROTO_H__*/
diff --git a/openair2/LAYER2/NR_MAC_gNB/main.c b/openair2/LAYER2/NR_MAC_gNB/main.c
index 78a0dbb649ddbd8af9646e644b9fd60836d53bf5..8dc1303801a349378d3180dd23b6f3dfc45951f4 100644
--- a/openair2/LAYER2/NR_MAC_gNB/main.c
+++ b/openair2/LAYER2/NR_MAC_gNB/main.c
@@ -113,18 +113,9 @@ void mac_top_init_gNB(void)
     
     UE_info = &nrmac->UE_info;
     UE_info->num_UEs = 0;
-    UE_info->list.head = -1;
+    create_nr_list(&UE_info->list, MAX_MOBILES_PER_GNB);
     for (list_el = 0; list_el < MAX_MOBILES_PER_GNB; list_el++) {
-      UE_info->list.next[list_el] = -1;
       UE_info->active[list_el] = false;
-      for (int list_harq = 0; list_harq < NR_MAX_NB_HARQ_PROCESSES; list_harq++) {
-        UE_info->UE_sched_ctrl[list_el].harq_processes[list_harq].round = 0;
-        UE_info->UE_sched_ctrl[list_el].harq_processes[list_harq].ndi = 0;
-        UE_info->UE_sched_ctrl[list_el].harq_processes[list_harq].is_waiting = 0;
-        UE_info->UE_sched_ctrl[list_el].ul_harq_processes[list_harq].round = 0;
-        UE_info->UE_sched_ctrl[list_el].ul_harq_processes[list_harq].ndi = 0;
-        UE_info->UE_sched_ctrl[list_el].ul_harq_processes[list_harq].state = 0;
-      }
     }
   }
 
diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h
index 5f14a9db78702f8b0003952781c99e5869f2dc28..bca974a00f08644028788cbc97a99cc67844c293 100644
--- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h
+++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h
@@ -75,10 +75,20 @@
 #define MAX_NUM_BWP 2
 #define MAX_NUM_CORESET 2
 #define MAX_NUM_CCE 90
+#define MAX_HARQ_ROUNDS 4
 /*!\brief Maximum number of random access process */
 #define NR_NB_RA_PROC_MAX 4
 #define MAX_NUM_OF_SSB 64
 
+/*! \brief NR_list_t is a "list" (of users, HARQ processes, slices, ...).
+ * Especially useful in the scheduler and to keep "classes" of users. */
+typedef struct {
+  int head;
+  int *next;
+  int tail;
+  int len;
+} NR_list_t;
+
 typedef enum {
   RA_IDLE = 0,
   Msg2 = 1,
@@ -282,7 +292,7 @@ typedef struct NR_sched_pucch {
   uint8_t dai_c;
   uint8_t timing_indicator;
   uint8_t resource_indicator;
-} NR_sched_pucch;
+} NR_sched_pucch_t;
 
 /* this struct is a helper: as long as the TDA and DCI format remain the same
  * over the same uBWP and search space, there is no need to recalculate all
@@ -326,13 +336,20 @@ typedef struct NR_sched_pusch {
   uint16_t R;
   uint8_t Qm;
   uint32_t tb_size;
+
+  /// UL HARQ PID to use for this UE, or -1 for "any new"
+  int8_t ul_harq_pid;
 } NR_sched_pusch_t;
 
 typedef struct NR_UE_harq {
-  uint8_t is_waiting;
+  bool is_waiting;
   uint8_t ndi;
   uint8_t round;
   uint16_t feedback_slot;
+
+  /* Transport block to be sent using this HARQ process */
+  uint32_t tb[16384];
+  uint32_t tb_size;
 } NR_UE_harq_t;
 
 typedef struct NR_UE_old_sched {
@@ -350,10 +367,13 @@ typedef enum {
 } NR_UL_harq_states_t;
 
 typedef struct NR_UE_ul_harq {
+  bool is_waiting;
   uint8_t ndi;
   uint8_t round;
-  uint16_t last_tx_slot;
-  NR_UL_harq_states_t state;
+  uint16_t feedback_slot;
+
+  /// sched_pusch keeps information on MCS etc used for the initial transmission
+  NR_sched_pusch_t sched_pusch;
 } NR_UE_ul_harq_t;
 
 
@@ -387,10 +407,12 @@ typedef struct {
   /// the currently active BWP in UL
   NR_BWP_Uplink_t *active_ubwp;
 
-  NR_sched_pucch **sched_pucch;
-  /// selected PUCCH index, if scheduled
-  int pucch_sched_idx;
-  int pucch_occ_idx;
+  /// PUCCH scheduling information. Array of three, we assume for the moment:
+  /// HARQ in the first field, SR in second, CSI in third (as fixed by RRC
+  /// conf., i.e. if actually present).  The order is important for
+  /// nr_acknack_scheduling()!
+  NR_sched_pucch_t sched_pucch[3];
+
   NR_sched_pusch_save_t pusch_save;
   NR_sched_pusch_t sched_pusch;
 
@@ -404,6 +426,11 @@ typedef struct {
   uint16_t rbSize;
   uint16_t rbStart;
 
+  /// uplink bytes that are currently scheduled
+  int sched_ul_bytes;
+  /// estimation of the UL buffer size
+  int estimated_ul_buffer;
+
   // time-domain allocation for scheduled RBs
   int time_domain_allocation;
 
@@ -414,6 +441,8 @@ typedef struct {
 
   /// Retransmission-related information
   NR_UE_ret_info_t retInfo[NR_MAX_NB_HARQ_PROCESSES];
+  /// DL HARQ PID to use for this UE, or -1 for "any new"
+  int8_t dl_harq_pid;
 
   uint16_t ta_frame;
   int16_t ta_update;
@@ -421,8 +450,22 @@ typedef struct {
   uint8_t tpc0;
   uint8_t tpc1;
   uint16_t ul_rssi;
+  /// information about every HARQ process
   NR_UE_harq_t harq_processes[NR_MAX_NB_HARQ_PROCESSES];
+  /// HARQ processes that are free
+  NR_list_t available_dl_harq;
+  /// HARQ processes that await feedback
+  NR_list_t feedback_dl_harq;
+  /// HARQ processes that await retransmission
+  NR_list_t retrans_dl_harq;
+  /// information about every UL HARQ process
   NR_UE_ul_harq_t ul_harq_processes[NR_MAX_NB_HARQ_PROCESSES];
+  /// UL HARQ processes that are free
+  NR_list_t available_ul_harq;
+  /// UL HARQ processes that await feedback
+  NR_list_t feedback_ul_harq;
+  /// UL HARQ processes that await retransmission
+  NR_list_t retrans_ul_harq;
   int dummy;
   NR_UE_mac_ce_ctrl_t UE_mac_ce_ctrl;// MAC CE related information
 } NR_UE_sched_ctrl_t;
@@ -438,22 +481,15 @@ typedef struct {
   int dlsch_rounds[8];
   int dlsch_errors;
   int dlsch_total_bytes;
+  int dlsch_current_bytes;
   int ulsch_rounds[8];
   int ulsch_errors;
   int ulsch_total_bytes_scheduled;
   int ulsch_total_bytes_rx;
+  int ulsch_current_bytes;
 } NR_mac_stats_t;
 
 
-
-/*! \brief UNR_E_list_t is a "list" of users within UE_info_t. Especial useful in
- * the scheduler and to keep "classes" of users. */
-typedef struct {
-  int head;
-  int next[MAX_MOBILES_PER_GNB];
-} NR_UE_list_t;
-
-
 /*! \brief UE list used by gNB to order UEs/CC for scheduling*/
 #define MAX_CSI_REPORTCONFIG 48
 typedef struct {
@@ -461,7 +497,7 @@ typedef struct {
   nr_csi_report_t csi_report_template[MAX_MOBILES_PER_GNB][MAX_CSI_REPORTCONFIG];
   NR_UE_sched_ctrl_t UE_sched_ctrl[MAX_MOBILES_PER_GNB];
   NR_mac_stats_t mac_stats[MAX_MOBILES_PER_GNB];
-  NR_UE_list_t list;
+  NR_list_t list;
   int num_UEs;
 
   bool active[MAX_MOBILES_PER_GNB];
@@ -477,9 +513,8 @@ typedef struct {
 
 typedef void (*nr_pp_impl_dl)(module_id_t mod_id,
                               frame_t frame,
-                              sub_frame_t slot,
-                              int num_slots_per_tdd);
-typedef void (*nr_pp_impl_ul)(module_id_t mod_id,
+                              sub_frame_t slot);
+typedef bool (*nr_pp_impl_ul)(module_id_t mod_id,
                               frame_t frame,
                               sub_frame_t slot,
                               int num_slots_per_tdd,
@@ -510,6 +545,10 @@ typedef struct gNB_MAC_INST_s {
   nfapi_nr_config_request_scf_t     config[NFAPI_CC_MAX];
   /// NFAPI DL Config Request Structure
   nfapi_nr_dl_tti_request_t         DL_req[NFAPI_CC_MAX];
+  /// a PDCCH PDU groups DCIs per BWP and CORESET. The following structure
+  /// keeps pointers to PDCCH PDUs within DL_req so that we can easily track
+  /// PDCCH PDUs per CC/BWP/CORESET
+  nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_idx[NFAPI_CC_MAX][MAX_NUM_BWP][MAX_NUM_CORESET];
   /// NFAPI UL TTI Request Structure, simple pointer into structure
   /// UL_tti_req_ahead for current frame/slot
   nfapi_nr_ul_tti_request_t        *UL_tti_req[NFAPI_CC_MAX];
@@ -550,6 +589,10 @@ typedef struct gNB_MAC_INST_s {
   time_stats_t schedule_pch;
   /// CCE lists
   int cce_list[MAX_NUM_BWP][MAX_NUM_CORESET][MAX_NUM_CCE];
+  /// PUCCH: keep track of the resources has already been used by saving the
+  /// highest index not yet been used in a given slot. Dynamically allocated
+  /// so we can have it for every slot as a function of the numerology
+  int *pucch_index_used[MAX_NUM_BWP];
 
   /// DL preprocessor for differentiated scheduling
   nr_pp_impl_dl pre_processor_dl;
diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c b/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c
index 0e6fd614a03b032836f664708db4716a0e48fa20..6195a91ea26037c7585768b6fde262223d24c8ee 100644
--- a/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c
+++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c
@@ -220,7 +220,7 @@ static void *enb_tun_read_thread(void *_)
       exit(1);
     }
 
-printf("\n\n\n########## nas_sock_fd read returns len %d\n", len);
+    LOG_D(PDCP, "%s(): nas_sock_fd read returns len %d\n", __func__, len);
 
     nr_pdcp_manager_lock(nr_pdcp_ue_manager);
     rnti = nr_pdcp_get_first_rnti(nr_pdcp_ue_manager);
@@ -264,7 +264,7 @@ static void *ue_tun_read_thread(void *_)
       exit(1);
     }
 
-printf("\n\n\n########## nas_sock_fd read returns len %d\n", len);
+    LOG_D(PDCP, "%s(): nas_sock_fd read returns len %d\n", __func__, len);
 
     nr_pdcp_manager_lock(nr_pdcp_ue_manager);
     rnti = nr_pdcp_get_first_rnti(nr_pdcp_ue_manager);
diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_entity_am.c b/openair2/LAYER2/nr_rlc/nr_rlc_entity_am.c
index 804f1e8a743e2451b33804b23993e0bc40e4b6f4..1d4efc1e264b19a25b5b5ffc4feb11d6ba933249 100644
--- a/openair2/LAYER2/nr_rlc/nr_rlc_entity_am.c
+++ b/openair2/LAYER2/nr_rlc/nr_rlc_entity_am.c
@@ -1581,7 +1581,7 @@ void nr_rlc_entity_am_recv_sdu(nr_rlc_entity_t *_entity,
   }
 
   if (entity->tx_size + size > entity->tx_maxsize) {
-    LOG_D(RLC, "%s:%d:%s: warning: SDU rejected, SDU buffer full\n",
+    LOG_E(RLC, "%s:%d:%s: warning: SDU rejected, SDU buffer full\n",
           __FILE__, __LINE__, __FUNCTION__);
     return;
   }
diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c b/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c
index d3503742ad03819b2b46e8ab03a79723719cc8b4..9c139624ebddeb846e6c4d26546e4f80c24556fd 100644
--- a/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c
+++ b/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c
@@ -668,8 +668,8 @@ static void add_srb(int rnti, struct LTE_SRB_ToAddMod *s)
     poll_byte = -1;
     max_retx_threshold = 8;
     sn_field_length = 12;
-    nr_rlc_am = new_nr_rlc_entity_am(100000,
-                                     100000,
+    nr_rlc_am = new_nr_rlc_entity_am(10000000,
+                                     10000000,
                                      deliver_sdu, ue,
                                      successful_delivery, ue,
                                      max_retx_reached, ue,
@@ -751,8 +751,8 @@ static void add_drb_am(int rnti, struct NR_DRB_ToAddMod *s, NR_RLC_BearerConfig_
   if (ue->drb[drb_id-1] != NULL) {
     LOG_W(RLC, "%s:%d:%s: DRB %d already exists for UE with RNTI %d, do nothing\n", __FILE__, __LINE__, __FUNCTION__, drb_id, rnti);
   } else {
-    nr_rlc_am = new_nr_rlc_entity_am(100000,
-                                     100000,
+    nr_rlc_am = new_nr_rlc_entity_am(10000000,
+                                     10000000,
                                      deliver_sdu, ue,
                                      successful_delivery, ue,
                                      max_retx_reached, ue,
@@ -823,8 +823,8 @@ static void add_drb_um(int rnti, struct NR_DRB_ToAddMod *s, NR_RLC_BearerConfig_
   if (ue->drb[drb_id-1] != NULL) {
     LOG_W(RLC, "DEBUG add_drb_um %s:%d:%s: warning DRB %d already exist for ue %d, do nothing\n", __FILE__, __LINE__, __FUNCTION__, drb_id, rnti);
   } else {
-    nr_rlc_um = new_nr_rlc_entity_um(1000000,
-                                     1000000,
+    nr_rlc_um = new_nr_rlc_entity_um(100000000,
+                                     100000000,
                                      deliver_sdu, ue,
                                      t_reassembly,
                                      sn_field_length);
diff --git a/openair2/NETWORK_DRIVER/LITE/device.c b/openair2/NETWORK_DRIVER/LITE/device.c
index 854c3bcc997b07b4231b088a2eabd9215d8e6f42..59340784c1878dba848f180317bcb82227bd80db 100644
--- a/openair2/NETWORK_DRIVER/LITE/device.c
+++ b/openair2/NETWORK_DRIVER/LITE/device.c
@@ -316,7 +316,11 @@ void oai_nw_drv_change_rx_flags(struct net_device *dev, int flags)
 }
 
 //---------------------------------------------------------------------------
+#if  LINUX_VERSION_CODE >= KERNEL_VERSION(5,6,0)
+void oai_nw_drv_tx_timeout(struct net_device *dev, unsigned int txqueue)
+#else
 void oai_nw_drv_tx_timeout(struct net_device *dev)
+#endif
 {
   //---------------------------------------------------------------------------
   // Transmitter timeout, serious problems.
diff --git a/openair2/NETWORK_DRIVER/MESH/device.c b/openair2/NETWORK_DRIVER/MESH/device.c
index 181899cd6eca3f7a14f6c5cbe9e283e6ff268ec9..47c68466bdc77a0eb65c2936dab0d38e864ae0a1 100644
--- a/openair2/NETWORK_DRIVER/MESH/device.c
+++ b/openair2/NETWORK_DRIVER/MESH/device.c
@@ -297,10 +297,10 @@ int nas_change_mtu(struct net_device *dev, int mtu)
 }
 
 //---------------------------------------------------------------------------
-#if  LINUX_VERSION_CODE < KERNEL_VERSION(5,7,0)
-void nas_tx_timeout(struct net_device *dev)
+#if  LINUX_VERSION_CODE >= KERNEL_VERSION(5,6,0)
+void nas_tx_timeout(struct net_device *dev, unsigned int txqueue)
 #else
-void nas_tx_timeout(struct net_device *dev, unsigned int x)
+void nas_tx_timeout(struct net_device *dev)
 #endif
 {
   //---------------------------------------------------------------------------
diff --git a/openair2/NETWORK_DRIVER/UE_IP/device.c b/openair2/NETWORK_DRIVER/UE_IP/device.c
index 32f9442747d3445c067a84ab716f0260c68c6df6..63184eb6da60ff04fbde92ce2e4217a07fc28593 100644
--- a/openair2/NETWORK_DRIVER/UE_IP/device.c
+++ b/openair2/NETWORK_DRIVER/UE_IP/device.c
@@ -302,7 +302,12 @@ void ue_ip_change_rx_flags(struct net_device *dev_pP, int flagsP) {
 }
 
 //---------------------------------------------------------------------------
-void ue_ip_tx_timeout(struct net_device *dev_pP) {
+#if  LINUX_VERSION_CODE >= KERNEL_VERSION(5,6,0)
+void ue_ip_tx_timeout(struct net_device *dev_pP, unsigned int txqueue)
+#else
+void ue_ip_tx_timeout(struct net_device *dev_pP)
+#endif
+{
   //---------------------------------------------------------------------------
   // Transmitter timeout, serious problems.
   ue_ip_priv_t *priv_p =  netdev_priv(dev_pP);
diff --git a/openair2/NR_PHY_INTERFACE/NR_IF_Module.c b/openair2/NR_PHY_INTERFACE/NR_IF_Module.c
index 7f98dd6a3ebf113050d8a557f8ce76c4a17486ee..b5cda6ecd63e2d19f8af88f85c86b466391b59b0 100644
--- a/openair2/NR_PHY_INTERFACE/NR_IF_Module.c
+++ b/openair2/NR_PHY_INTERFACE/NR_IF_Module.c
@@ -104,6 +104,9 @@ void handle_nr_uci(NR_UL_IND_t *UL_info)
   }
 
   UL_info->uci_ind.num_ucis = 0;
+  // mark corresponding PUCCH resources as free
+  // NOTE: we just assume it is BWP ID 1, to be revised for multiple BWPs
+  RC.nrmac[mod_id]->pucch_index_used[1][slot] = 0;
 }
 
 
diff --git a/openair2/RRC/NR/rrc_gNB_reconfig.c b/openair2/RRC/NR/rrc_gNB_reconfig.c
index adf3dfe2a8e81b888f0ccc029e76134a971eec17..aebdc2f1cc9b260dda194bbae0fcc96e3461ab87 100644
--- a/openair2/RRC/NR/rrc_gNB_reconfig.c
+++ b/openair2/RRC/NR/rrc_gNB_reconfig.c
@@ -1177,7 +1177,8 @@ void fill_default_secondaryCellGroup(NR_ServingCellConfigCommon_t *servingcellco
  secondaryCellGroup->spCellConfig->spCellConfigDedicated->pdsch_ServingCellConfig->choice.setup = pdsch_servingcellconfig;
  pdsch_servingcellconfig->codeBlockGroupTransmission = NULL;
  pdsch_servingcellconfig->xOverhead = NULL;
- pdsch_servingcellconfig->nrofHARQ_ProcessesForPDSCH = NULL;
+ pdsch_servingcellconfig->nrofHARQ_ProcessesForPDSCH = calloc(1, sizeof(*pdsch_servingcellconfig->nrofHARQ_ProcessesForPDSCH));
+ *pdsch_servingcellconfig->nrofHARQ_ProcessesForPDSCH = NR_PDSCH_ServingCellConfig__nrofHARQ_ProcessesForPDSCH_n16;
  pdsch_servingcellconfig->pucch_Cell= NULL;
  pdsch_servingcellconfig->ext1=calloc(1,sizeof(*pdsch_servingcellconfig->ext1));
  pdsch_servingcellconfig->ext1->maxMIMO_Layers = calloc(1,sizeof(*pdsch_servingcellconfig->ext1->maxMIMO_Layers));
diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf
index 1cd3b01b6c7aa6bfde7052ce0fb15b11a409f923..760689f653f19bb50f79afd99850d53a46784437 100644
--- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf
+++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/gnb.band78.tm1.106PRB.usrpn300.conf
@@ -1,7 +1,6 @@
 Active_gNBs = ( "gNB-Eurecom-5GNRBox");
 # Asn1_verbosity, choice in: none, info, annoying
 Asn1_verbosity = "none";
-Num_Threads_PUSCH = 8;
 
 gNBs =
 (
@@ -234,6 +233,7 @@ L1s = (
     	{
 	num_cc = 1;
 	tr_n_preference = "local_mac";
+  pusch_proc_threads = 8;
         }  
 );
 
@@ -246,7 +246,7 @@ RUs = (
          att_rx         = 0;
          bands          = [7];
          max_pdschReferenceSignalPower = -27;
-         max_rxgain                    = 75;
+         max_rxgain                    = 50;
          eNB_instances  = [0];
          ##beamforming 1x2 matrix: 1 layer x 2 antennas
          bf_weights = [0x00007fff, 0x0000];
diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/testing_gnb.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/testing_gnb.conf
index e34b26150092b902660cdf8b9fb43ef7595e82ff..555af5835d368647559077be618610c792dfe52a 100644
--- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/testing_gnb.conf
+++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/testing_gnb.conf
@@ -1,7 +1,6 @@
 Active_gNBs = ( "gNB-Eurecom-5GNRBox");
 # Asn1_verbosity, choice in: none, info, annoying
 Asn1_verbosity = "none";
-Num_Threads_PUSCH = 8;
 
 gNBs =
 (
@@ -250,6 +249,7 @@ L1s = (
     	{
 	num_cc = 1;
 	tr_n_preference = "local_mac";
+  pusch_proc_threads = 8;
         }  
 );