diff --git a/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c b/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c
index eba374dd275298ab5ee8323ca748ebdcd38827a4..de5e9415b1ea569c948559c6f552e3cdaee4de09 100644
--- a/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c
+++ b/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c
@@ -348,6 +348,8 @@ int nr_dlsch_encoding(unsigned char *a,
   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;
diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c
index 0241796252d06cf4c0f8cc686285b1ccf99092fb..dcfcbc5ce6f91659e6abd0db59287030de6249fc 100644
--- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c
+++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c
@@ -359,6 +359,7 @@ uint32_t nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB,
 
   VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_gNB_ULSCH_DECODING,1);
   harq_process->TBS = pusch_pdu->pusch_data.tb_size;
+  harq_process->round = nr_rv_round_map[pusch_pdu->pusch_data.rv_index];
 
   A   = (harq_process->TBS)<<3;
   ret = ulsch->max_ldpc_iterations + 1;
@@ -570,7 +571,6 @@ uint32_t nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB,
 
       AssertFatal(kc!=255,"");
       j+=(harq_process->F>>3);
-      //      for (i=Kr_bytes,j=K_bytes_F-((2*p_decParams->Z)>>3); i < ((kc*p_decParams->Z)>>3); i++, j++) {
       for (i=Kr_bytes; i < ((kc*p_decParams->Z)>>3); i++, j++) {
         pv[i]= _mm_loadu_si128((__m128i*)(&harq_process->d[r][8*j]));
       }
@@ -661,8 +661,8 @@ uint32_t nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB,
       harq_process->status = SCH_IDLE;
       harq_process->round  = 0;
       harq_process->handled  = 0;
+      ulsch->harq_mask &= ~(1 << harq_pid);
     }
-    ulsch->harq_mask &= ~(1 << harq_pid);
 
     //   LOG_D(PHY,"[gNB %d] ULSCH: Setting NACK for nr_tti_rx %d (pid %d, pid status %d, round %d/Max %d, TBS %d)\n",
     //         phy_vars_gNB->Mod_id,nr_tti_rx,harq_pid,harq_process->status,harq_process->round,ulsch->Mlimit,harq_process->TBS);
@@ -680,7 +680,7 @@ uint32_t nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB,
     harq_process->status = SCH_IDLE;
     harq_process->round  = 0;
     // harq_process->handled  = 0;
-    ulsch->harq_mask  |= (1 << harq_pid);
+    ulsch->harq_mask &= ~(1 << harq_pid);
     // harq_process->harq_ack.ack = 1;
     // harq_process->harq_ack.harq_id = harq_pid;
     // harq_process->harq_ack.send_harq_status = 1;
diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_decoding.c b/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_decoding.c
index ff3fc086ce53d4c72eeca2d087569dc1a63739d0..3070d1a79388eaa7132bc5846945c5bb99173940 100644
--- a/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_decoding.c
+++ b/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_decoding.c
@@ -277,7 +277,7 @@ uint32_t nr_dlsch_decoding(PHY_VARS_NR_UE *phy_vars_ue,
   __m128i *pl = (__m128i*)&l;
   
     vcd_signal_dumper_dump_function_by_name(VCD_SIGNAL_DUMPER_FUNCTIONS_DLSCH_SEGMENTATION, VCD_FUNCTION_IN);
-
+  
   //NR_DL_UE_HARQ_t *harq_process = dlsch->harq_processes[0];
 
   if (!dlsch_llr) {
@@ -657,7 +657,7 @@ uint32_t nr_dlsch_decoding(PHY_VARS_NR_UE *phy_vars_ue,
     harq_process->harq_ack.harq_id = harq_pid;
     harq_process->harq_ack.send_harq_status = 1;
     harq_process->errors[harq_process->round]++;
-    // harq_process->round++; // [hna] uncomment this line when HARQ is implemented
+    harq_process->round++; // [hna] uncomment this line when HARQ is implemented
 
     //    printf("Rate: [UE %d] DLSCH: Setting NACK for subframe %d (pid %d, round %d)\n",phy_vars_ue->Mod_id,subframe,harq_pid,harq_process->round);
     if (harq_process->round >= dlsch->Mlimit) {
diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h
index bde9ae03f15bd35249d62c17df5dd13bc6950b04..65ed57057f577f8d6bbe7ebd001ae1ebdb3d124f 100644
--- a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h
+++ b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h
@@ -1074,11 +1074,9 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE,
 */
 
 uint8_t nr_ue_pusch_common_procedures(PHY_VARS_NR_UE *UE,
-                                      uint8_t harq_pid,
                                       uint8_t slot,
-                                      uint8_t thread_id,
-                                      uint8_t gNB_id,
-                                      NR_DL_FRAME_PARMS *frame_parms);
+                                      NR_DL_FRAME_PARMS *frame_parms,
+                                      uint8_t Nl);
 
 
 
diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c
index 93add646fefb394923596b541df138478dd4d27f..c02228bc9f99abbe9805d238a152e6e77e01acb6 100644
--- a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c
+++ b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c
@@ -259,6 +259,7 @@ int nr_ulsch_encoding(NR_UE_ULSCH_t *ulsch,
   Ilbrm = 0;
   Tbslbrm = 950984; //max tbs
   Coderate = 0.0;
+  harq_process->round = nr_rv_round_map_ue[harq_process->pusch_pdu.pusch_data.rv_index];
 
 ///////////
 /////////////////////////////////////////////////////////////////////////////////////////  
diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c
index 93acda23f8b5d064b208851f560211a21aa1cad5..fbb30632f684483fd0d39560d84282fb6b3d25b4 100644
--- a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c
+++ b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c
@@ -476,17 +476,14 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE,
 
 
 uint8_t nr_ue_pusch_common_procedures(PHY_VARS_NR_UE *UE,
-                                      uint8_t harq_pid,
                                       uint8_t slot,
-                                      uint8_t thread_id,
-                                      uint8_t gNB_id,
-                                      NR_DL_FRAME_PARMS *frame_parms) {
+                                      NR_DL_FRAME_PARMS *frame_parms,
+                                      uint8_t Nl) {
 
   int tx_offset, ap;
   int32_t **txdata;
   int32_t **txdataF;
   int timing_advance;
-  uint8_t Nl = UE->ulsch[thread_id][gNB_id][0]->harq_processes[harq_pid]->pusch_pdu.nrOfLayers; // cw 0
 
   /////////////////////////IFFT///////////////////////
   ///////////
diff --git a/openair1/PHY/NR_UE_TRANSPORT/pucch_nr.c b/openair1/PHY/NR_UE_TRANSPORT/pucch_nr.c
index d2a8e64749852cca94a30c6355d4995df85291f8..b2ee4a2f3ba137cf1d7d7200fa328d7658ab0ea4 100644
--- a/openair1/PHY/NR_UE_TRANSPORT/pucch_nr.c
+++ b/openair1/PHY/NR_UE_TRANSPORT/pucch_nr.c
@@ -113,7 +113,7 @@ void nr_generate_pucch0(PHY_VARS_NR_UE *ue,
     // if frequency hopping is enabled n_hop = 1 for second hop. Not sure frequency hopping concerns format 0. FIXME!!!
     // if ((PUCCH_Frequency_Hopping == 1)&&(l == (nrofSymbols-1))) n_hop = 1;
     nr_group_sequence_hopping(pucch_GroupHopping,hoppingId,n_hop,nr_tti_tx,&u,&v); // calculating u and v value
-    alpha = nr_cyclic_shift_hopping(ue->pucch_config_common_nr->hoppingId,m0,mcs,l,startingSymbolIndex,nr_tti_tx);
+    alpha = nr_cyclic_shift_hopping(hoppingId,m0,mcs,l,startingSymbolIndex,nr_tti_tx);
 #ifdef DEBUG_NR_PUCCH_TX
     printf("\t [nr_generate_pucch0] sequence generation \tu=%d \tv=%d \talpha=%lf \t(for symbol l=%d)\n",u,v,alpha,l);
 #endif
diff --git a/openair1/PHY/defs_nr_common.h b/openair1/PHY/defs_nr_common.h
index 584962c55c6234486acd0d09d9318dc9a2faaf50..80e6c081171800f8c435dc55d4a6f2a12ac09808 100644
--- a/openair1/PHY/defs_nr_common.h
+++ b/openair1/PHY/defs_nr_common.h
@@ -111,6 +111,9 @@
 #define MAX_NUM_NR_CHANNEL_BITS (14*273*12*8)  // 14 symbols, 273 RB
 #define MAX_NUM_NR_RE (14*273*12)
 
+extern const uint8_t nr_rv_round_map[4]; 
+extern const uint8_t nr_rv_round_map_ue[4]; 
+
 typedef enum {
   NR_MU_0=0,
   NR_MU_1,
diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c
index 08d41837cdffab56a6425ca8efa09eb951f124eb..38ec74eeeb8b46da8e32c9d9f9d760c8ad5aa14c 100644
--- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c
+++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c
@@ -460,7 +460,8 @@ void phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx)
 	  for(uint8_t symbol = symbol_start; symbol < symbol_end; symbol++) {
 	    no_sig = nr_rx_pusch(gNB, ULSCH_id, frame_rx, slot_rx, symbol, harq_pid);
             if (no_sig) {
-              LOG_I(PHY, "ULSCH %d not received\n",ULSCH_id);
+              LOG_I(PHY, "PUSCH not detected in symbol %d\n",symbol);
+              nr_fill_indication(gNB,frame_rx, slot_rx, ULSCH_id, harq_pid, 1);
               return;
             }
 	  }
diff --git a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c
index e902977721748554960775008223805e2ab33d54..86c215ea5c05bbf1b75be80f7c6b5e8964c791dd 100644
--- a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c
+++ b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c
@@ -86,6 +86,8 @@ fifo_dump_emos_UE emos_dump_UE;
 
 char nr_mode_string[4][20] = {"NOT SYNCHED","PRACH","RAR","PUSCH"};
 
+const uint8_t nr_rv_round_map_ue[4] = {0, 2, 1, 3};
+
 extern double cpuf;
 
 /*
@@ -2250,20 +2252,18 @@ void phy_procedures_nrUE_TX(PHY_VARS_NR_UE *ue,
 */
 
    if (get_softmodem_params()->usim_test==0) {
-      LOG_D(PHY, "Sending PUCCH\n");
+      LOG_I(PHY, "Generating PUCCH\n");
       pucch_procedures_ue_nr(ue,
                              gNB_id,
                              proc,
-                             TRUE);
+                             FALSE);
    }
 
-    LOG_D(PHY, "Sending data \n");
+    LOG_I(PHY, "Sending Uplink data \n");
     nr_ue_pusch_common_procedures(ue,
-                                  harq_pid,
                                   slot_tx,
-                                  thread_id,
-                                  gNB_id,
-                                  &ue->frame_parms);
+                                  &ue->frame_parms,1);
+                                  //ue->ulsch[thread_id][gNB_id][0]->harq_processes[harq_pid]->pusch_pdu.nrOfLayers);
   }
   //LOG_M("txdata.m","txs",ue->common_vars.txdata[0],1228800,1,1);
 
@@ -3371,6 +3371,9 @@ void nr_ue_dlsch_procedures(PHY_VARS_NR_UE *ue,
       }
     }
 
+    // exit dlsch procedures as there are no active dlsch
+    if (is_cw0_active != ACTIVE && is_cw1_active != ACTIVE)
+      return;
 
     // start ldpc decode for CW 0
     dlsch0->harq_processes[harq_pid]->G = nr_get_G(dlsch0->harq_processes[harq_pid]->nb_rb,
diff --git a/openair1/SIMULATION/NR_PHY/pucchsim.c b/openair1/SIMULATION/NR_PHY/pucchsim.c
index 004a7b8de8ae2868346b82576b6d34036ffb76ed..7ec60a6dfd667eb04c6d968e8aae2470b8b13b11 100644
--- a/openair1/SIMULATION/NR_PHY/pucchsim.c
+++ b/openair1/SIMULATION/NR_PHY/pucchsim.c
@@ -55,6 +55,8 @@ int32_t uplink_frequency_offset[MAX_NUM_CCs][4];
 double cpuf;
 uint8_t nfapi_mode = 0;
 uint16_t NB_UE_INST = 1;
+uint8_t const nr_rv_round_map[4] = {0, 2, 1, 3};
+uint8_t const nr_rv_round_map_ue[4] = {0, 2, 1, 3};
 
 // needed for some functions
 PHY_VARS_NR_UE * PHY_vars_UE_g[1][1]={{NULL}};
diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c
index 7a99aef1ac1a8cdc522bf507618ea6a7ff758d58..5ad84c42d728a1ee14eb732dff8845bb3b49d4a0 100644
--- a/openair1/SIMULATION/NR_PHY/ulsim.c
+++ b/openair1/SIMULATION/NR_PHY/ulsim.c
@@ -104,7 +104,7 @@ int rrc_init_nr_global_param(void){return(0);}
 // needed for some functions
 uint16_t n_rnti = 0x1234;
 openair0_config_t openair0_cfg[MAX_CARDS];
-uint8_t round_rv_map[4] = {1, 0, 2, 3};
+//const uint8_t nr_rv_round_map[4] = {0, 2, 1, 3}; 
 
 int main(int argc, char **argv)
 {
@@ -148,6 +148,10 @@ int main(int argc, char **argv)
   cpuf = get_cpu_freq_GHz();
   int msg3_flag = 0;
   uint8_t rv_index = 0;
+  float roundStats[50];
+  float effRate; 
+  float eff_tp_check = 0.7;
+  uint8_t snrRun;
 
   UE_nr_rxtx_proc_t UE_proc;
   FILE *scg_fd=NULL;
@@ -276,6 +280,9 @@ int main(int argc, char **argv)
       printf("Setting SNR0 to %f\n", snr0);
       break;
       
+    case 't':
+      eff_tp_check = (float)atoi(optarg)/100;
+      break;
       /*
 	case 'r':
 	ricean_factor = pow(10,-.1*atof(optarg));
@@ -374,6 +381,7 @@ int main(int argc, char **argv)
       printf("-N Nid_cell\n");
       printf("-O oversampling factor (1,2,4,8,16)\n");
       printf("-R N_RB_DL\n");
+      printf("-t Acceptable effective throughput (in percentage)\n");
       printf("-S Ending SNR, runs from SNR0 to SNR1\n");
       printf("-P Print ULSCH performances\n");
       exit(-1);
@@ -559,6 +567,7 @@ int main(int argc, char **argv)
   uint8_t mcs_table = 0;
   uint16_t pdu_bit_map = PUSCH_PDU_BITMAP_PUSCH_DATA; // | PUSCH_PDU_BITMAP_PUSCH_PTRS;
   uint8_t max_rounds = 4;
+  uint8_t crc_status = 0;
 
   uint8_t length_dmrs = pusch_len1; // [hna] remove dmrs struct
   uint16_t l_prime_mask = get_l_prime(nb_symb_sch, typeB, pusch_dmrs_pos0, length_dmrs);  // [hna] remove dmrs struct
@@ -578,17 +587,23 @@ int main(int argc, char **argv)
   printf("\n");
 
   //for (int i=0;i<16;i++) printf("%f\n",gaussdouble(0.0,1.0));
+  snrRun = 0;
   for (SNR = snr0; SNR < snr1; SNR += snr_step) {
     varArray_t *table_rx=initVarArray(1000,sizeof(double));
+    int error_flag = 0;
+    n_false_positive = 0;
+    effRate = 0;
+    n_errors = 0;
     for (trial = 0; trial < n_trials; trial++) {
     uint8_t round = 0;
-    int error_flag;
-    gNB->ulsch[0][0]->harq_mask = 0;
 
-    while (round<max_rounds && !(gNB->ulsch[0][0]->harq_mask & 0x1)) {
+    crc_status = 1;
+    errors_scrambling  = 0;
+    errors_decoding    = 0;
+    while (round<max_rounds && crc_status) {
       ulsch_ue[0]->harq_processes[harq_pid]->round = round;
       gNB->ulsch[0][0]->harq_processes[harq_pid]->round = round;
-      rv_index = round_rv_map[round];
+      rv_index = nr_rv_round_map[round];
       reset_meas(&gNB->phy_proc_rx);
       reset_meas(&gNB->ulsch_decoding_stats);
       reset_meas(&gNB->ulsch_deinterleaving_stats);
@@ -732,7 +747,6 @@ int main(int argc, char **argv)
 
       if (input_fd == NULL) {
 
-        if (SNR==snr0) { 
 	  // set FAPI parameters for UE, put them in the scheduled response and call
 	  nr_ue_scheduled_response(&scheduled_response);
 	  
@@ -757,24 +771,14 @@ int main(int argc, char **argv)
 	  txlev_float = (double)txlev/scale; // output of signal_energy is fixed point representation
 	  
 	  
-	  //AWGN
-	}
       }	
       else n_trials = 1;
 
 
       sigma_dB = 10*log10(txlev_float)-SNR;
       sigma    = pow(10,sigma_dB/10);
-      printf("txlev_float %f, sigma_dB %f\n",10*log10(txlev_float),sigma_dB);
-
-      n_errors = 0;
-      n_false_positive = 0;
-
-      errors_scrambling  = 0;
-      errors_decoding    = 0;
+      if(n_trials==1) printf("txlev_float %f, sigma_dB %f\n",10*log10(txlev_float),sigma_dB);
 
-	
-	error_flag = 0;
 	  //----------------------------------------------------------
 	  //------------------------ add noise -----------------------
 	  //----------------------------------------------------------
@@ -841,8 +845,11 @@ int main(int argc, char **argv)
 	    gNB->ulsch[0][0]->max_ldpc_iterations+1) {
 	  error_flag = 1; 
 	  n_errors++;
-	}
-    printf("end of round %d rv_index %d\n",round, rv_index);
+    crc_status = 1;
+	} else {
+    crc_status = 0;
+  }
+    if(n_trials==1) printf("end of round %d rv_index %d\n",round, rv_index);
     round++;
     } // round
 	
@@ -892,12 +899,17 @@ int main(int argc, char **argv)
 	  if (n_trials==1)
 	    printf("\x1B[31m""[frame %d][trial %d]\tnumber of errors in decoding     = %u\n" "\x1B[0m", frame, trial, errors_decoding);
         } 
+        roundStats[snrRun] += ((float)round);
+        if (!crc_status) effRate += ((float)TBS)/round;
       } // trial loop
 
+      roundStats[snrRun]/=((float)n_trials);
+      effRate /= n_trials;
+
       printf("*****************************************\n");
       printf("SNR %f: n_errors (negative CRC) = %d/%d, false_positive %d/%d, errors_scrambling %u/%u\n", SNR, n_errors, n_trials, n_false_positive, n_trials, errors_scrambling, available_bits*n_trials);
       printf("\n");
-      printf("SNR %f: Channel BLER %e, Channel BER %e\n", SNR,(double)n_errors/n_trials,(double)errors_scrambling/available_bits/n_trials);
+      printf("SNR %f: Channel BLER %e, Channel BER %e Avg round %.2f, Eff Rate %.4f bits/slot, Eff Throughput %.2f, TBS %d bits/slot\n", SNR,(double)n_errors/n_trials,(double)errors_scrambling/available_bits/n_trials,roundStats[snrRun],effRate,effRate/TBS*100,TBS);
       printf("*****************************************\n");
       printf("\n");
  
@@ -925,6 +937,7 @@ int main(int argc, char **argv)
 	break;
       }
       
+    snrRun++;
   } // SNR loop
   printf("\n");
 
diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c
index eb22f158817cfe5a71b0664bb180afa5bd1a1741..c421fa1b070557cd0558f17442cb5a8267a89da3 100644
--- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c
+++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c
@@ -53,7 +53,10 @@
 #include "NR_SearchSpace.h"
 #include "NR_ControlResourceSet.h"
 
+#define UL_HARQ_PRINT
 extern RAN_CONTEXT_t RC;
+
+const uint8_t nr_rv_round_map[4] = {0, 2, 1, 3}; 
 //#define ENABLE_MAC_PAYLOAD_DEBUG 1
 
 //uint8_t mac_pdu[MAX_NR_DLSCH_PAYLOAD_BYTES];
@@ -269,7 +272,6 @@ int configure_fapi_dl_pdu(int Mod_idP,
   int TBS;
   int bwp_id=1;
   int UE_id = 0;
-  uint8_t rv_round_map[4] = {0, 2, 3, 1};
 
   NR_UE_list_t *UE_list = &RC.nrmac[Mod_idP]->UE_list;
 
@@ -315,7 +317,7 @@ int configure_fapi_dl_pdu(int Mod_idP,
   pdsch_pdu_rel15->qamModOrder[0] = 2;
   pdsch_pdu_rel15->mcsIndex[0] = mcs;
   pdsch_pdu_rel15->mcsTable[0] = 0;
-  pdsch_pdu_rel15->rvIndex[0] = (get_softmodem_params()->phy_test==1) ? 0 : rv_round_map[UE_list->UE_sched_ctrl[UE_id].harq_processes[current_harq_pid].round];
+  pdsch_pdu_rel15->rvIndex[0] = nr_rv_round_map[UE_list->UE_sched_ctrl[UE_id].harq_processes[current_harq_pid].round];
   pdsch_pdu_rel15->dataScramblingId = *scc->physCellId;
   pdsch_pdu_rel15->nrOfLayers = 1;    
   pdsch_pdu_rel15->transmissionScheme = 0;
@@ -774,6 +776,33 @@ void nr_schedule_uss_dlsch_phytest(module_id_t   module_idP,
 
 }
 
+uint8_t select_ul_harq_pid(NR_UE_sched_ctrl_t *sched_ctrl) {
+
+  uint8_t hrq_id;
+  uint8_t max_ul_harq_pids = 3; // temp: for testing
+  // schedule active harq processes
+  NR_UE_ul_harq_t cur_harq;
+  for (hrq_id=0; hrq_id < max_ul_harq_pids; hrq_id++) {
+    cur_harq = sched_ctrl->ul_harq_processes[hrq_id];
+    if (cur_harq.state==ACTIVE_NOT_SCHED) {
+#ifdef UL_HARQ_PRINT
+      printf("[SCHED] Found active ulharq id %d, scheduling it for retransmission\n",hrq_id); 
+#endif
+      return hrq_id;
+    }
+  }
+
+  // schedule new harq processes
+  for (hrq_id=0; hrq_id < max_ul_harq_pids; hrq_id++) {
+    cur_harq = sched_ctrl->ul_harq_processes[hrq_id];
+    if (cur_harq.state==INACTIVE) {
+#ifdef UL_HARQ_PRINT
+      printf("[SCHED] Found inactive ulharq id %d, scheduling it\n",hrq_id); 
+#endif
+      return hrq_id;
+    }
+  }
+}
 
 void schedule_fapi_ul_pdu(int Mod_idP,
                           frame_t frameP,
@@ -978,9 +1007,14 @@ void schedule_fapi_ul_pdu(int Mod_idP,
     //Pusch Allocation in frequency domain [TS38.214, sec 6.1.2.2]
     //Optional Data only included if indicated in pduBitmap
     // TODO from harq function as in pdsch
-    pusch_pdu->pusch_data.rv_index = 0;
-    pusch_pdu->pusch_data.harq_process_id = 0;
-    pusch_pdu->pusch_data.new_data_indicator = 1;
+    uint8_t harq_id = select_ul_harq_pid(&UE_list->UE_sched_ctrl[UE_id]);
+    NR_UE_ul_harq_t *cur_harq = &UE_list->UE_sched_ctrl[UE_id].ul_harq_processes[harq_id];
+    pusch_pdu->pusch_data.harq_process_id = harq_id;
+    pusch_pdu->pusch_data.new_data_indicator = cur_harq->ndi;
+    pusch_pdu->pusch_data.rv_index = nr_rv_round_map[cur_harq->round];
+
+    cur_harq->state = ACTIVE_SCHED;
+    cur_harq->last_tx_slot = pusch_sched->slot;
 
     uint8_t num_dmrs_symb = 0;
 
diff --git a/openair2/LAYER2/NR_MAC_gNB/main.c b/openair2/LAYER2/NR_MAC_gNB/main.c
index 56e020838d267847670fce5b0b9b5b864b439219..873deddbe7bb95aa9fe0f50a6fa018700aabf99d 100644
--- a/openair2/LAYER2/NR_MAC_gNB/main.c
+++ b/openair2/LAYER2/NR_MAC_gNB/main.c
@@ -117,6 +117,9 @@ void mac_top_init_gNB(void)
         UE_list->UE_sched_ctrl[list_el].harq_processes[list_harq].round = 0;
         UE_list->UE_sched_ctrl[list_el].harq_processes[list_harq].ndi = 0;
         UE_list->UE_sched_ctrl[list_el].harq_processes[list_harq].is_waiting = 0;
+        UE_list->UE_sched_ctrl[list_el].ul_harq_processes[list_harq].round = 0;
+        UE_list->UE_sched_ctrl[list_el].ul_harq_processes[list_harq].ndi = 0;
+        UE_list->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 303a3545d829c726d4f16bbb6e9e41c7bd62984b..35d62a17fb42b507618880c2648510fba276fc43 100644
--- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h
+++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h
@@ -267,6 +267,19 @@ typedef struct NR_UE_harq {
   uint16_t feedback_slot;
 } NR_UE_harq_t;
 
+typedef enum {
+  INACTIVE = 0,
+  ACTIVE_NOT_SCHED,
+  ACTIVE_SCHED
+} NR_UL_harq_states_t;
+
+typedef struct NR_UE_ul_harq {
+  uint8_t ndi;
+  uint8_t round;
+  uint16_t last_tx_slot;
+  NR_UL_harq_states_t state;
+} NR_UE_ul_harq_t;
+
 /*! \brief scheduling control information set through an API */
 typedef struct {
   uint64_t dlsch_in_slot_bitmap;  // static bitmap signaling which slot in a tdd period contains dlsch
@@ -277,6 +290,7 @@ typedef struct {
   int16_t ta_update;
   uint8_t current_harq_pid;
   NR_UE_harq_t harq_processes[NR_MAX_NB_HARQ_PROCESSES];
+  NR_UE_ul_harq_t ul_harq_processes[NR_MAX_NB_HARQ_PROCESSES];
   int dummy;
   NR_UE_mac_ce_ctrl_t UE_mac_ce_ctrl;// MAC CE related information
 } NR_UE_sched_ctrl_t;
diff --git a/openair2/NR_PHY_INTERFACE/NR_IF_Module.c b/openair2/NR_PHY_INTERFACE/NR_IF_Module.c
index ec969c674128694cf39bfcd412c805862258a70e..91697e713a3fbd7c3f726e84bb1e8ec8e320a4bc 100644
--- a/openair2/NR_PHY_INTERFACE/NR_IF_Module.c
+++ b/openair2/NR_PHY_INTERFACE/NR_IF_Module.c
@@ -41,6 +41,7 @@
 #include "executables/softmodem-common.h"
 
 #define MAX_IF_MODULES 100
+#define UL_HARQ_PRINT
 
 NR_IF_Module_t *if_inst[MAX_IF_MODULES];
 NR_Sched_Rsp_t Sched_INFO[MAX_IF_MODULES][MAX_NUM_CCs];
@@ -88,7 +89,7 @@ void handle_nr_uci(NR_UL_IND_t *UL_info, NR_UE_sched_ctrl_t *sched_ctrl) {
       case NFAPI_NR_UCI_PDCCH_PDU_TYPE: break;
 
       case NFAPI_NR_UCI_FORMAT_0_1_PDU_TYPE: {
-        if (get_softmodem_params()->phy_test == 0) {
+        //if (get_softmodem_params()->phy_test == 0) {
           nfapi_nr_uci_pucch_pdu_format_0_1_t *uci_pdu = &uci_list[i].pucch_pdu_format_0_1;
           // handle harq
           int harq_idx_s = 0;
@@ -120,7 +121,7 @@ void handle_nr_uci(NR_UL_IND_t *UL_info, NR_UE_sched_ctrl_t *sched_ctrl) {
               }
             }
           }
-        }
+        //}
         break;
       }
 
@@ -131,8 +132,43 @@ void handle_nr_uci(NR_UL_IND_t *UL_info, NR_UE_sched_ctrl_t *sched_ctrl) {
   UL_info->uci_ind.num_ucis = 0;
 }
 
+void handle_nr_ul_harq(uint16_t slot, NR_UE_sched_ctrl_t *sched_ctrl, uint8_t crc_status) {
 
-void handle_nr_ulsch(NR_UL_IND_t *UL_info) {
+  int max_harq_rounds = 4; // TODO define macro
+  for (uint8_t hrq_id = 0; hrq_id < NR_MAX_NB_HARQ_PROCESSES; hrq_id++) {
+    NR_UE_ul_harq_t *cur_harq = &sched_ctrl->ul_harq_processes[hrq_id];
+    if ((cur_harq->last_tx_slot == slot-1) && cur_harq->state==ACTIVE_SCHED) {
+      if (!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
+#ifdef UL_HARQ_PRINT
+        printf("[HARQ HANDLER] Ulharq id %d crc passed, freeing it for scheduler\n",hrq_id);
+#endif
+      } else {
+        cur_harq->round++;
+        cur_harq->state = ACTIVE_NOT_SCHED;
+#ifdef UL_HARQ_PRINT
+        printf("[HARQ HANDLER] Ulharq id %d crc failed, requesting retransmission\n",hrq_id);
+#endif
+      }
+
+      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;
+#ifdef UL_HARQ_PRINT
+        printf("[HARQ HANDLER] Ulharq id %d crc failed in all round, freeing it for scheduler\n",hrq_id);
+#endif
+      }
+      return;
+    }
+  }
+
+}
+
+
+void handle_nr_ulsch(NR_UL_IND_t *UL_info, NR_UE_sched_ctrl_t *sched_ctrl) {
   if(nfapi_mode == 1) {
     if (UL_info->crc_ind.number_crcs>0) {
       //LOG_D(PHY,"UL_info->crc_ind.crc_indication_body.number_of_crcs:%d CRC_IND:SFN/SF:%d\n", UL_info->crc_ind.crc_indication_body.number_of_crcs, NFAPI_SFNSF2DEC(UL_info->crc_ind.sfn_sf));
@@ -159,6 +195,8 @@ void handle_nr_ulsch(NR_UL_IND_t *UL_info) {
               UL_info->rx_ind.pdu_list[i].rnti) {
             LOG_D(PHY, "UL_info->crc_ind.crc_indication_body.crc_pdu_list[%d].crc_indication_rel8.crc_flag:%d\n", j, UL_info->crc_ind.crc_list[j].tb_crc_status);
 
+            handle_nr_ul_harq(UL_info->slot, sched_ctrl, UL_info->crc_ind.crc_list[j].tb_crc_status);
+
             if (UL_info->crc_ind.crc_list[j].tb_crc_status == 1) { // CRC error indication
               LOG_D(MAC,"Frame %d, Slot %d Calling rx_sdu (CRC error) \n",UL_info->frame,UL_info->slot);
 
@@ -233,7 +271,7 @@ void NR_UL_indication(NR_UL_IND_t *UL_info) {
   handle_nr_uci(UL_info, &mac->UE_list.UE_sched_ctrl[0]);
   // clear HI prior to handling ULSCH
   mac->UL_dci_req[CC_id].numPdus = 0;
-  handle_nr_ulsch(UL_info);
+  handle_nr_ulsch(UL_info, &mac->UE_list.UE_sched_ctrl[0]);
 
   if (nfapi_mode != 1) {
     if (ifi->CC_mask == ((1<<MAX_NUM_CCs)-1)) {