diff --git a/cmake_targets/CMakeLists.txt b/cmake_targets/CMakeLists.txt
index 0b9d9250171303ff90e010b54bcdd66f6873dfd4..4a9add6663de94e7670027d12bd2218adb26d9d2 100644
--- a/cmake_targets/CMakeLists.txt
+++ b/cmake_targets/CMakeLists.txt
@@ -2109,10 +2109,10 @@ set (MAC_NR_SRC_UE
   ${NR_UE_MAC_DIR}/mac_vars.c
   ${NR_UE_MAC_DIR}/main_ue_nr.c
   ${NR_UE_MAC_DIR}/nr_ue_procedures.c
+  ${NR_UE_MAC_DIR}/nr_ue_scheduler.c
   ${NR_UE_MAC_DIR}/nr_ue_dci_configuration.c
   ${NR_UE_MAC_DIR}/nr_l1_helpers.c
   ${NR_UE_MAC_DIR}/nr_ra_procedures.c
-  ${NR_UE_MAC_DIR}/rar_tools_nrUE.c
 )
 
 set (ENB_APP_SRC
diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c
index e2a9fb26684d439c6aefdda2fdfb72bc1bb8a42e..e8b094a5712c601046a771503fffe688340b2401 100644
--- a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c
+++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c
@@ -58,13 +58,9 @@
 /* utils */
 #include "assertions.h"
 #include "asn1_conversions.h"
-#include "SIMULATION/TOOLS/sim.h" // for taus
 #include "common/utils/LOG/log.h"
 #include "common/utils/LOG/vcd_signal_dumper.h"
 
-static prach_association_pattern_t prach_assoc_pattern;
-static ssb_list_info_t ssb_list;
-
 //#define ENABLE_MAC_PAYLOAD_DEBUG 1
 //#define DEBUG_EXTRACT_DCI
 //#define DEBUG_RAR
@@ -91,637 +87,6 @@ int get_rnti_type(NR_UE_MAC_INST_t *mac, uint16_t rnti){
     LOG_D(MAC, "In %s: returning rnti_type %s \n", __FUNCTION__, rnti_types[rnti_type]);
 
     return rnti_type;
-}
-
-// Build the list of all the valid RACH occasions in the maximum association pattern period according to the PRACH config
-static void build_ro_list(NR_ServingCellConfigCommon_t *scc, uint8_t unpaired) {
-
-  int x,y; // PRACH Configuration Index table variables used to compute the valid frame numbers
-  int y2;  // PRACH Configuration Index table additional variable used to compute the valid frame numbers
-  uint8_t slot_shift_for_map;
-  uint8_t map_shift;
-  boolean_t even_slot_invalid;
-  int64_t s_map;
-  uint8_t prach_conf_start_symbol; // Starting symbol of the PRACH occasions in the PRACH slot
-  uint8_t N_t_slot; // Number of PRACH occasions in a 14-symbols PRACH slot
-  uint8_t N_dur; // Duration of a PRACH occasion (nb of symbols)
-  uint8_t frame; // Maximum is NB_FRAMES_IN_MAX_ASSOCIATION_PATTERN_PERIOD
-  uint8_t slot; // Maximum is the number of slots in a frame @ SCS 240kHz
-  uint16_t format = 0xffff;
-  uint8_t format2 = 0xff;
-  int nb_fdm;
-
-  uint8_t config_index, mu;
-  uint32_t pointa;
-  int msg1_FDM;
-
-  uint8_t prach_conf_period_idx;
-  uint8_t nb_of_frames_per_prach_conf_period;
-  uint8_t prach_conf_period_frame_idx;
-  int64_t *prach_config_info_p;
-
-  NR_RACH_ConfigCommon_t *setup = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup;
-  NR_FrequencyInfoDL_t *frequencyInfoDL = scc->downlinkConfigCommon->frequencyInfoDL;
-  NR_RACH_ConfigGeneric_t *rach_ConfigGeneric = &setup->rach_ConfigGeneric;
-
-  config_index = rach_ConfigGeneric->prach_ConfigurationIndex;
-
-  if (setup->msg1_SubcarrierSpacing)
-    mu = *setup->msg1_SubcarrierSpacing;
-  else
-    mu = frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing;
-
-  pointa = frequencyInfoDL->absoluteFrequencyPointA;
-  msg1_FDM = rach_ConfigGeneric->msg1_FDM;
-
-  switch (msg1_FDM){
-    case 0:
-    case 1:
-    case 2:
-    case 3:
-      nb_fdm = 1 << msg1_FDM;
-      break;
-    default:
-      AssertFatal(1 == 0, "Unknown msg1_FDM from rach_ConfigGeneric %d\n", msg1_FDM);
-  }
-
-  // Create the PRACH occasions map
-  // ==============================
-  // WIP: For now assume no rejected PRACH occasions because of conflict with SSB or TDD_UL_DL_ConfigurationCommon schedule
-
-  // Identify the proper PRACH Configuration Index table according to the operating frequency
-  LOG_D(MAC,"Pointa %u, mu = %u, PRACH config index  = %u, unpaired = %u\n", pointa, mu, config_index, unpaired);
-
-  prach_config_info_p = get_prach_config_info(pointa, config_index, unpaired);
-
-  if (pointa > 2016666) { //FR2
-
-    x = prach_config_info_p[2];
-    y = prach_config_info_p[3];
-    y2 = prach_config_info_p[4];
-
-    s_map = prach_config_info_p[5];
-
-    prach_conf_start_symbol = prach_config_info_p[6];
-    N_t_slot = prach_config_info_p[8];
-    N_dur = prach_config_info_p[9];
-    if (prach_config_info_p[1] != -1)
-      format2 = (uint8_t) prach_config_info_p[1];
-    format = ((uint8_t) prach_config_info_p[0]) | (format2<<8);
-
-    slot_shift_for_map = mu-2;
-    if ( (mu == 3) && (prach_config_info_p[7] == 1) )
-      even_slot_invalid = true;
-    else
-      even_slot_invalid = false;
-  }
-  else { // FR1
-    x = prach_config_info_p[2];
-    y = prach_config_info_p[3];
-    y2 = y;
-
-    s_map = prach_config_info_p[4];
-
-    prach_conf_start_symbol = prach_config_info_p[5];
-    N_t_slot = prach_config_info_p[7];
-    N_dur = prach_config_info_p[8];
-    if (prach_config_info_p[1] != -1)
-      format2 = (uint8_t) prach_config_info_p[1];
-    format = ((uint8_t) prach_config_info_p[0]) | (format2<<8);
-
-    slot_shift_for_map = mu;
-    if ( (mu == 1) && (prach_config_info_p[6] <= 1) )
-      // no prach in even slots @ 30kHz for 1 prach per subframe
-      even_slot_invalid = true;
-    else
-      even_slot_invalid = false;
-  } // FR2 / FR1
-
-  prach_assoc_pattern.nb_of_prach_conf_period_in_max_period = MAX_NB_PRACH_CONF_PERIOD_IN_ASSOCIATION_PATTERN_PERIOD / x;
-  nb_of_frames_per_prach_conf_period = x;
-
-  LOG_D(MAC,"nb_of_prach_conf_period_in_max_period %d\n", prach_assoc_pattern.nb_of_prach_conf_period_in_max_period);
-
-  // Fill in the PRACH occasions table for every slot in every frame in every PRACH configuration periods in the maximum association pattern period
-  // ----------------------------------------------------------------------------------------------------------------------------------------------
-  // ----------------------------------------------------------------------------------------------------------------------------------------------
-  // For every PRACH configuration periods
-  // -------------------------------------
-  for (prach_conf_period_idx=0; prach_conf_period_idx<prach_assoc_pattern.nb_of_prach_conf_period_in_max_period; prach_conf_period_idx++) {
-    prach_assoc_pattern.prach_conf_period_list[prach_conf_period_idx].nb_of_prach_occasion = 0;
-    prach_assoc_pattern.prach_conf_period_list[prach_conf_period_idx].nb_of_frame = nb_of_frames_per_prach_conf_period;
-    prach_assoc_pattern.prach_conf_period_list[prach_conf_period_idx].nb_of_slot = nr_slots_per_frame[mu];
-
-    LOG_D(MAC,"PRACH Conf Period Idx %d\n", prach_conf_period_idx);
-
-    // For every frames in a PRACH configuration period
-    // ------------------------------------------------
-    for (prach_conf_period_frame_idx=0; prach_conf_period_frame_idx<nb_of_frames_per_prach_conf_period; prach_conf_period_frame_idx++) {
-      frame = (prach_conf_period_idx * nb_of_frames_per_prach_conf_period) + prach_conf_period_frame_idx;
-
-      LOG_D(MAC,"PRACH Conf Period Frame Idx %d - Frame %d\n", prach_conf_period_frame_idx, frame);
-      // Is it a valid frame for this PRACH configuration index? (n_sfn mod x = y)
-      if ( (frame%x)==y || (frame%x)==y2 ) {
-
-        // For every slot in a frame
-        // -------------------------
-        for (slot=0; slot<nr_slots_per_frame[mu]; slot++) {
-          // Is it a valid slot?
-          map_shift = slot >> slot_shift_for_map; // in PRACH configuration index table slots are numbered wrt 60kHz
-          if ( (s_map>>map_shift)&0x01 ) {
-            // Valid slot
-
-            // Additionally, for 30kHz/120kHz, we must check for the n_RA_Slot param also
-            if ( even_slot_invalid && (slot%2 == 0) )
-                continue; // no prach in even slots @ 30kHz/120kHz for 1 prach per 60khz slot/subframe
-
-            // We're good: valid frame and valid slot
-            // Compute all the PRACH occasions in the slot
-
-            uint8_t n_prach_occ_in_time;
-            uint8_t n_prach_occ_in_freq;
-
-            prach_assoc_pattern.prach_conf_period_list[prach_conf_period_idx].prach_occasion_slot_map[prach_conf_period_frame_idx][slot].nb_of_prach_occasion_in_time = N_t_slot;
-            prach_assoc_pattern.prach_conf_period_list[prach_conf_period_idx].prach_occasion_slot_map[prach_conf_period_frame_idx][slot].nb_of_prach_occasion_in_freq = nb_fdm;
-
-            for (n_prach_occ_in_time=0; n_prach_occ_in_time<N_t_slot; n_prach_occ_in_time++) {
-              uint8_t start_symbol = prach_conf_start_symbol + n_prach_occ_in_time * N_dur;
-              LOG_D(MAC,"PRACH Occ in time %d\n", n_prach_occ_in_time);
-
-              for (n_prach_occ_in_freq=0; n_prach_occ_in_freq<nb_fdm; n_prach_occ_in_freq++) {
-                prach_occasion_info_t *prach_occasion_p = &prach_assoc_pattern.prach_conf_period_list[prach_conf_period_idx].prach_occasion_slot_map[prach_conf_period_frame_idx][slot].prach_occasion[n_prach_occ_in_time][n_prach_occ_in_freq];
-
-                prach_occasion_p->start_symbol = start_symbol;
-                prach_occasion_p->fdm = n_prach_occ_in_freq;
-                prach_occasion_p->frame = frame;
-                prach_occasion_p->slot = slot;
-                prach_occasion_p->format = format;
-                prach_assoc_pattern.prach_conf_period_list[prach_conf_period_idx].nb_of_prach_occasion++;
-
-                LOG_D(MAC,"Adding a PRACH occasion: frame %u, slot-symbol %d-%d, occ_in_time-occ_in-freq %d-%d, nb ROs in conf period %d, for this slot: RO# in time %d, RO# in freq %d\n",
-                    frame, slot, start_symbol, n_prach_occ_in_time, n_prach_occ_in_freq, prach_assoc_pattern.prach_conf_period_list[prach_conf_period_idx].nb_of_prach_occasion,
-                    prach_assoc_pattern.prach_conf_period_list[prach_conf_period_idx].prach_occasion_slot_map[prach_conf_period_frame_idx][slot].nb_of_prach_occasion_in_time,
-                    prach_assoc_pattern.prach_conf_period_list[prach_conf_period_idx].prach_occasion_slot_map[prach_conf_period_frame_idx][slot].nb_of_prach_occasion_in_freq);
-              } // For every freq in the slot
-            } // For every time occasions in the slot
-          } // Valid slot?
-        } // For every slots in a frame
-      } // Valid frame?
-    } // For every frames in a prach configuration period
-  } // For every prach configuration periods in the maximum association pattern period (160ms)
-}
-
-// Build the list of all the valid/transmitted SSBs according to the config
-static void build_ssb_list(NR_ServingCellConfigCommon_t *scc) {
-
-  // Create the list of transmitted SSBs
-  // ===================================
-  BIT_STRING_t *ssb_bitmap;
-  uint64_t ssb_positionsInBurst;
-  uint8_t ssb_idx = 0;
-
-  switch (scc->ssb_PositionsInBurst->present) {
-    case NR_ServingCellConfigCommon__ssb_PositionsInBurst_PR_shortBitmap:
-      ssb_bitmap = &scc->ssb_PositionsInBurst->choice.shortBitmap;
-
-      ssb_positionsInBurst = BIT_STRING_to_uint8(ssb_bitmap);
-      LOG_D(MAC,"SSB config: SSB_positions_in_burst 0x%lx\n", ssb_positionsInBurst);
-
-      for (uint8_t bit_nb=3; bit_nb<=3; bit_nb--) {
-        // If SSB is transmitted
-        if ((ssb_positionsInBurst>>bit_nb) & 0x01) {
-          ssb_list.nb_tx_ssb++;
-          ssb_list.tx_ssb[ssb_idx].transmitted = true;
-          LOG_D(MAC,"SSB idx %d transmitted\n", ssb_idx);
-        }
-        ssb_idx++;
-      }
-      break;
-    case NR_ServingCellConfigCommon__ssb_PositionsInBurst_PR_mediumBitmap:
-      ssb_bitmap = &scc->ssb_PositionsInBurst->choice.mediumBitmap;
-
-      ssb_positionsInBurst = BIT_STRING_to_uint8(ssb_bitmap);
-      LOG_D(MAC,"SSB config: SSB_positions_in_burst 0x%lx\n", ssb_positionsInBurst);
-
-      for (uint8_t bit_nb=7; bit_nb<=7; bit_nb--) {
-        // If SSB is transmitted
-        if ((ssb_positionsInBurst>>bit_nb) & 0x01) {
-          ssb_list.nb_tx_ssb++;
-          ssb_list.tx_ssb[ssb_idx].transmitted = true;
-          LOG_D(MAC,"SSB idx %d transmitted\n", ssb_idx);
-        }
-        ssb_idx++;
-      }
-      break;
-    case NR_ServingCellConfigCommon__ssb_PositionsInBurst_PR_longBitmap:
-      ssb_bitmap = &scc->ssb_PositionsInBurst->choice.longBitmap;
-
-      ssb_positionsInBurst = BIT_STRING_to_uint64(ssb_bitmap);
-      LOG_D(MAC,"SSB config: SSB_positions_in_burst 0x%lx\n", ssb_positionsInBurst);
-
-      for (uint8_t bit_nb=63; bit_nb<=63; bit_nb--) {
-        // If SSB is transmitted
-        if ((ssb_positionsInBurst>>bit_nb) & 0x01) {
-          ssb_list.nb_tx_ssb++;
-          ssb_list.tx_ssb[ssb_idx].transmitted = true;
-          LOG_D(MAC,"SSB idx %d transmitted\n", ssb_idx);
-        }
-        ssb_idx++;
-      }
-      break;
-    default:
-      AssertFatal(false,"ssb_PositionsInBurst not present\n");
-      break;
-  }
-}
-
-// Map the transmitted SSBs to the ROs and create the association pattern according to the config
-static void map_ssb_to_ro(NR_ServingCellConfigCommon_t *scc) {
-
-  // Map SSBs to PRACH occasions
-  // ===========================
-  // WIP: Assumption: No PRACH occasion is rejected because of a conflict with SSBs or TDD_UL_DL_ConfigurationCommon schedule
-  NR_RACH_ConfigCommon_t *setup = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup;
-  NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR ssb_perRACH_config = setup->ssb_perRACH_OccasionAndCB_PreamblesPerSSB->present;
-
-  boolean_t multiple_ssb_per_ro; // true if more than one or exactly one SSB per RACH occasion, false if more than one RO per SSB
-  uint8_t ssb_rach_ratio; // Nb of SSBs per RACH or RACHs per SSB
-  uint16_t required_nb_of_prach_occasion; // Nb of RACH occasions required to map all the SSBs
-  uint8_t required_nb_of_prach_conf_period; // Nb of PRACH configuration periods required to map all the SSBs
-
-  // Determine the SSB to RACH mapping ratio
-  // =======================================
-  switch (ssb_perRACH_config){
-    case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_oneEighth:
-      multiple_ssb_per_ro = false;
-      ssb_rach_ratio = 8;
-      break;
-    case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_oneFourth:
-      multiple_ssb_per_ro = false;
-      ssb_rach_ratio = 4;
-      break;
-    case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_oneHalf:
-      multiple_ssb_per_ro = false;
-      ssb_rach_ratio = 2;
-      break;
-    case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_one:
-      multiple_ssb_per_ro = true;
-      ssb_rach_ratio = 1;
-      break;
-    case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_two:
-      multiple_ssb_per_ro = true;
-      ssb_rach_ratio = 2;
-      break;
-    case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_four:
-      multiple_ssb_per_ro = true;
-      ssb_rach_ratio = 4;
-      break;
-    case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_eight:
-      multiple_ssb_per_ro = true;
-      ssb_rach_ratio = 8;
-      break;
-    case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_sixteen:
-      multiple_ssb_per_ro = true;
-      ssb_rach_ratio = 16;
-      break;
-    default:
-      AssertFatal(1 == 0, "Unsupported ssb_perRACH_config %d\n", ssb_perRACH_config);
-      break;
-  }
-  LOG_D(MAC,"SSB rach ratio %d, Multiple SSB per RO %d\n", ssb_rach_ratio, multiple_ssb_per_ro);
-
-  // Evaluate the number of PRACH configuration periods required to map all the SSBs and set the association period
-  // ==============================================================================================================
-  // WIP: Assumption for now is that all the PRACH configuration periods within a maximum association pattern period have the same number of PRACH occasions
-  //      (No PRACH occasions are conflicting with SSBs nor TDD_UL_DL_ConfigurationCommon schedule)
-  //      There is only one possible association period which can contain up to 16 PRACH configuration periods
-  LOG_D(MAC,"Evaluate the number of PRACH configuration periods required to map all the SSBs and set the association period\n");
-  if (true == multiple_ssb_per_ro) {
-    required_nb_of_prach_occasion = ((ssb_list.nb_tx_ssb-1) + ssb_rach_ratio) / ssb_rach_ratio;
-  }
-  else {
-    required_nb_of_prach_occasion = ssb_list.nb_tx_ssb * ssb_rach_ratio;
-  }
-
-  required_nb_of_prach_conf_period = ((required_nb_of_prach_occasion-1) + prach_assoc_pattern.prach_conf_period_list[0].nb_of_prach_occasion) / prach_assoc_pattern.prach_conf_period_list[0].nb_of_prach_occasion;
-
-  if (required_nb_of_prach_conf_period == 1) {
-    prach_assoc_pattern.prach_association_period_list[0].nb_of_prach_conf_period = 1;
-  }
-  else if (required_nb_of_prach_conf_period == 2) {
-    prach_assoc_pattern.prach_association_period_list[0].nb_of_prach_conf_period = 2;
-  }
-  else if (required_nb_of_prach_conf_period <= 4) {
-    prach_assoc_pattern.prach_association_period_list[0].nb_of_prach_conf_period = 4;
-  }
-  else if (required_nb_of_prach_conf_period <= 8) {
-    prach_assoc_pattern.prach_association_period_list[0].nb_of_prach_conf_period = 8;
-  }
-  else if (required_nb_of_prach_conf_period <= 16) {
-    prach_assoc_pattern.prach_association_period_list[0].nb_of_prach_conf_period = 16;
-  }
-  else {
-    AssertFatal(1 == 0, "Invalid number of PRACH config periods within an association period %d\n", required_nb_of_prach_conf_period);
-  }
-
-  prach_assoc_pattern.nb_of_assoc_period = 1; // WIP: only one possible association period
-  prach_assoc_pattern.prach_association_period_list[0].nb_of_frame = prach_assoc_pattern.prach_association_period_list[0].nb_of_prach_conf_period * prach_assoc_pattern.prach_conf_period_list[0].nb_of_frame;
-  prach_assoc_pattern.nb_of_frame = prach_assoc_pattern.prach_association_period_list[0].nb_of_frame;
-
-  LOG_D(MAC,"Assoc period %d, Nb of frames in assoc period %d\n",
-        prach_assoc_pattern.prach_association_period_list[0].nb_of_prach_conf_period,
-        prach_assoc_pattern.prach_association_period_list[0].nb_of_frame);
-
-  // Proceed to the SSB to RO mapping
-  // ================================
-  uint8_t association_period_idx; // Association period index within the association pattern
-  uint8_t ssb_idx = 0;
-  uint8_t prach_configuration_period_idx; // PRACH Configuration period index within the association pattern
-  prach_conf_period_t *prach_conf_period_p;
-
-  // Map all the association periods within the association pattern period
-  LOG_D(MAC,"Proceed to the SSB to RO mapping\n");
-  for (association_period_idx=0; association_period_idx<prach_assoc_pattern.nb_of_assoc_period; association_period_idx++) {
-    uint8_t n_prach_conf=0; // PRACH Configuration period index within the association period
-    uint8_t frame=0;
-    uint8_t slot=0;
-    uint8_t ro_in_time=0;
-    uint8_t ro_in_freq=0;
-
-    // Set the starting PRACH Configuration period index in the association_pattern map for this particular association period
-    prach_configuration_period_idx = 0;  // WIP: only one possible association period so the starting PRACH configuration period is automatically 0
-
-    // Check if we need to map multiple SSBs per RO or multiple ROs per SSB
-    if (true == multiple_ssb_per_ro) {
-      // --------------------
-      // --------------------
-      // Multiple SSBs per RO
-      // --------------------
-      // --------------------
-
-      // WIP: For the moment, only map each SSB idx once per association period if configuration is multiple SSBs per RO
-      //      this is true if no PRACH occasions are conflicting with SSBs nor TDD_UL_DL_ConfigurationCommon schedule
-      ssb_idx = 0;
-
-      // Go through the list of PRACH config periods within this association period
-      for (n_prach_conf=0; n_prach_conf<prach_assoc_pattern.prach_association_period_list[association_period_idx].nb_of_prach_conf_period; n_prach_conf++, prach_configuration_period_idx++) {
-        // Build the association period with its association PRACH Configuration indexes
-        prach_conf_period_p = &prach_assoc_pattern.prach_conf_period_list[prach_configuration_period_idx];
-        prach_assoc_pattern.prach_association_period_list[association_period_idx].prach_conf_period_list[n_prach_conf] = prach_conf_period_p;
-
-        // Go through all the ROs within the PRACH config period
-        for (frame=0; frame<prach_conf_period_p->nb_of_frame; frame++) {
-          for (slot=0; slot<prach_conf_period_p->nb_of_slot; slot++) {
-            for (ro_in_time=0; ro_in_time<prach_conf_period_p->prach_occasion_slot_map[frame][slot].nb_of_prach_occasion_in_time; ro_in_time++) {
-              for (ro_in_freq=0; ro_in_freq<prach_conf_period_p->prach_occasion_slot_map[frame][slot].nb_of_prach_occasion_in_freq; ro_in_freq++) {
-                prach_occasion_info_t *ro_p = &prach_conf_period_p->prach_occasion_slot_map[frame][slot].prach_occasion[ro_in_time][ro_in_freq];
-
-                // Go through the list of transmitted SSBs and map the required amount of SSBs to this RO
-                // WIP: For the moment, only map each SSB idx once per association period if configuration is multiple SSBs per RO
-                //      this is true if no PRACH occasions are conflicting with SSBs nor TDD_UL_DL_ConfigurationCommon schedule
-                for (; ssb_idx<MAX_NB_SSB; ssb_idx++) {
-                  // Map only the transmitted ssb_idx
-                  if (true == ssb_list.tx_ssb[ssb_idx].transmitted) {
-                    ro_p->mapped_ssb_idx[ro_p->nb_mapped_ssb] = ssb_idx;
-                    ro_p->nb_mapped_ssb++;
-                    ssb_list.tx_ssb[ssb_idx].mapped_ro[ssb_list.tx_ssb[ssb_idx].nb_mapped_ro] = ro_p;
-                    ssb_list.tx_ssb[ssb_idx].nb_mapped_ro++;
-                    AssertFatal(MAX_NB_RO_PER_SSB_IN_ASSOCIATION_PATTERN > ssb_list.tx_ssb[ssb_idx].nb_mapped_ro,"Too many mapped ROs (%d) to a single SSB\n", ssb_list.tx_ssb[ssb_idx].nb_mapped_ro);
-
-                    LOG_D(MAC,"Mapped ssb_idx %u to RO slot-symbol %u-%u, %u-%u-%u/%u\n", ssb_idx, ro_p->slot, ro_p->start_symbol, slot, ro_in_time, ro_in_freq, prach_conf_period_p->prach_occasion_slot_map[frame][slot].nb_of_prach_occasion_in_freq);
-                    LOG_D(MAC,"Nb mapped ROs for this ssb idx: in the association period only %u\n", ssb_list.tx_ssb[ssb_idx].nb_mapped_ro);
-
-                    // If all the required SSBs are mapped to this RO, exit the loop of SSBs
-                    if (ro_p->nb_mapped_ssb == ssb_rach_ratio) {
-                      ssb_idx++;
-                      break;
-                    }
-                  } // if ssb_idx is transmitted
-                } // for ssb_idx
-
-                // Exit the loop of ROs if there is no more SSB to map
-                if (MAX_NB_SSB == ssb_idx) break;
-              } // for ro_in_freq
-
-              // Exit the loop of ROs if there is no more SSB to map
-              if (MAX_NB_SSB == ssb_idx) break;
-            } // for ro_in_time
-
-            // Exit the loop of slots if there is no more SSB to map
-            if (MAX_NB_SSB == ssb_idx) break;
-          } // for slot
-
-          // Exit the loop frames if there is no more SSB to map
-          if (MAX_NB_SSB == ssb_idx) break;
-        } // for frame
-
-        // Exit the loop of PRACH configurations if there is no more SSB to map
-        if (MAX_NB_SSB == ssb_idx) break;
-      } // for n_prach_conf
-
-      // WIP: note that there is no re-mapping of the SSBs within the association period since there is no invalid ROs in the PRACH config periods that would create this situation
-
-    } // if multiple_ssbs_per_ro
-
-    else {
-      // --------------------
-      // --------------------
-      // Multiple ROs per SSB
-      // --------------------
-      // --------------------
-
-      n_prach_conf = 0;
-
-      // Go through the list of transmitted SSBs
-      for (ssb_idx=0; ssb_idx<MAX_NB_SSB; ssb_idx++) {
-        uint8_t nb_mapped_ro_in_association_period=0; // Reset the nb of mapped ROs for the new SSB index
-
-        // Map only the transmitted ssb_idx
-        if (true == ssb_list.tx_ssb[ssb_idx].transmitted) {
-
-          // Map all the required ROs to this SSB
-          // Go through the list of PRACH config periods within this association period
-          for (; n_prach_conf<prach_assoc_pattern.prach_association_period_list[association_period_idx].nb_of_prach_conf_period; n_prach_conf++, prach_configuration_period_idx++) {
-
-            // Build the association period with its association PRACH Configuration indexes
-            prach_conf_period_p = &prach_assoc_pattern.prach_conf_period_list[prach_configuration_period_idx];
-            prach_assoc_pattern.prach_association_period_list[association_period_idx].prach_conf_period_list[n_prach_conf] = prach_conf_period_p;
-
-            for (; frame<prach_conf_period_p->nb_of_frame; frame++) {
-              for (; slot<prach_conf_period_p->nb_of_slot; slot++) {
-                for (; ro_in_time<prach_conf_period_p->prach_occasion_slot_map[frame][slot].nb_of_prach_occasion_in_time; ro_in_time++) {
-                  for (; ro_in_freq<prach_conf_period_p->prach_occasion_slot_map[frame][slot].nb_of_prach_occasion_in_freq; ro_in_freq++) {
-                    prach_occasion_info_t *ro_p = &prach_conf_period_p->prach_occasion_slot_map[frame][slot].prach_occasion[ro_in_time][ro_in_freq];
-
-                    ro_p->mapped_ssb_idx[0] = ssb_idx;
-                    ro_p->nb_mapped_ssb = 1;
-                    ssb_list.tx_ssb[ssb_idx].mapped_ro[ssb_list.tx_ssb[ssb_idx].nb_mapped_ro] = ro_p;
-                    ssb_list.tx_ssb[ssb_idx].nb_mapped_ro++;
-                    AssertFatal(MAX_NB_RO_PER_SSB_IN_ASSOCIATION_PATTERN > ssb_list.tx_ssb[ssb_idx].nb_mapped_ro,"Too many mapped ROs (%d) to a single SSB\n", ssb_list.tx_ssb[ssb_idx].nb_mapped_ro);
-                    nb_mapped_ro_in_association_period++;
-
-                    LOG_D(MAC,"Mapped ssb_idx %u to RO slot-symbol %u-%u, %u-%u-%u/%u\n", ssb_idx, ro_p->slot, ro_p->start_symbol, slot, ro_in_time, ro_in_freq, prach_conf_period_p->prach_occasion_slot_map[frame][slot].nb_of_prach_occasion_in_freq);
-                    LOG_D(MAC,"Nb mapped ROs for this ssb idx: in the association period only %u / total %u\n", ssb_list.tx_ssb[ssb_idx].nb_mapped_ro, nb_mapped_ro_in_association_period);
-
-                    // Exit the loop if this SSB has been mapped to all the required ROs
-                    // WIP: Assuming that ssb_rach_ratio equals the maximum nb of times a given ssb_idx is mapped within an association period:
-                    //      this is true if no PRACH occasions are conflicting with SSBs nor TDD_UL_DL_ConfigurationCommon schedule
-                    if (nb_mapped_ro_in_association_period == ssb_rach_ratio) {
-                      ro_in_freq++;
-                      break;
-                    }
-                  } // for ro_in_freq
-
-                  // Exit the loop if this SSB has been mapped to all the required ROs
-                  if (nb_mapped_ro_in_association_period == ssb_rach_ratio) {
-                    break;
-                  }
-                  else ro_in_freq = 0; // else go to the next time symbol in that slot and reset the freq index
-                } // for ro_in_time
-
-                // Exit the loop if this SSB has been mapped to all the required ROs
-                if (nb_mapped_ro_in_association_period == ssb_rach_ratio) {
-                  break;
-                }
-                else ro_in_time = 0; // else go to the next slot in that PRACH config period and reset the symbol index
-              } // for slot
-
-              // Exit the loop if this SSB has been mapped to all the required ROs
-              if (nb_mapped_ro_in_association_period == ssb_rach_ratio) {
-                break;
-              }
-              else slot = 0; // else go to the next frame in that PRACH config period and reset the slot index
-            } // for frame
-
-            // Exit the loop if this SSB has been mapped to all the required ROs
-            if (nb_mapped_ro_in_association_period == ssb_rach_ratio) {
-              break;
-            }
-            else frame = 0; // else go to the next PRACH config period in that association period and reset the frame index
-          } // for n_prach_conf
-
-        } // if ssb_idx is transmitted
-      } // for ssb_idx
-    } // else if multiple_ssbs_per_ro
-
-  } // for association_period_index
-}
-
-// Returns a RACH occasion if any matches the SSB idx, the frame and the slot
-static int get_nr_prach_info_from_ssb_index(uint8_t ssb_idx,
-                                            int frame,
-                                            int slot,
-                                            prach_occasion_info_t **prach_occasion_info_pp) {
-
-  ssb_info_t *ssb_info_p;
-  prach_occasion_slot_t *prach_occasion_slot_p = NULL;
-
-  *prach_occasion_info_pp = NULL;
-
-  // Search for a matching RO slot in the SSB_to_RO map
-  // A valid RO slot will match:
-  //      - ssb_idx mapped to one of the ROs in that RO slot
-  //      - exact slot number
-  //      - frame offset
-  ssb_info_p = &ssb_list.tx_ssb[ssb_idx];
-  for (uint8_t n_mapped_ro=0; n_mapped_ro<ssb_info_p->nb_mapped_ro; n_mapped_ro++) {
-    if ((slot == ssb_info_p->mapped_ro[n_mapped_ro]->slot) &&
-        (ssb_info_p->mapped_ro[n_mapped_ro]->frame == (frame % prach_assoc_pattern.nb_of_frame))) {
-
-      uint8_t prach_config_period_nb = ssb_info_p->mapped_ro[n_mapped_ro]->frame / prach_assoc_pattern.prach_conf_period_list[0].nb_of_frame;
-      uint8_t frame_nb_in_prach_config_period = ssb_info_p->mapped_ro[n_mapped_ro]->frame % prach_assoc_pattern.prach_conf_period_list[0].nb_of_frame;
-      prach_occasion_slot_p = &prach_assoc_pattern.prach_conf_period_list[prach_config_period_nb].prach_occasion_slot_map[frame_nb_in_prach_config_period][slot];
-    }
-  }
-
-  // If there is a matching RO slot in the SSB_to_RO map
-  if (NULL != prach_occasion_slot_p)
-  {
-    // A random RO mapped to the SSB index should be selected in the slot
-
-    // First count the number of times the SSB index is found in that RO
-    uint8_t nb_mapped_ssb = 0;
-
-    for (int ro_in_time=0; ro_in_time < prach_occasion_slot_p->nb_of_prach_occasion_in_time; ro_in_time++) {
-      for (int ro_in_freq=0; ro_in_freq < prach_occasion_slot_p->nb_of_prach_occasion_in_freq; ro_in_freq++) {
-        prach_occasion_info_t *prach_occasion_info_p = &prach_occasion_slot_p->prach_occasion[ro_in_time][ro_in_freq];
-
-        for (uint8_t ssb_nb=0; ssb_nb<prach_occasion_info_p->nb_mapped_ssb; ssb_nb++) {
-          if (prach_occasion_info_p->mapped_ssb_idx[ssb_nb] == ssb_idx) {
-            nb_mapped_ssb++;
-          }
-        }
-      }
-    }
-
-    // Choose a random SSB nb
-    uint8_t random_ssb_nb = 0;
-
-    random_ssb_nb = ((taus()) % nb_mapped_ssb);
-
-    // Select the RO according to the chosen random SSB nb
-    nb_mapped_ssb=0;
-    for (int ro_in_time=0; ro_in_time < prach_occasion_slot_p->nb_of_prach_occasion_in_time; ro_in_time++) {
-      for (int ro_in_freq=0; ro_in_freq < prach_occasion_slot_p->nb_of_prach_occasion_in_freq; ro_in_freq++) {
-        prach_occasion_info_t *prach_occasion_info_p = &prach_occasion_slot_p->prach_occasion[ro_in_time][ro_in_freq];
-
-        for (uint8_t ssb_nb=0; ssb_nb<prach_occasion_info_p->nb_mapped_ssb; ssb_nb++) {
-          if (prach_occasion_info_p->mapped_ssb_idx[ssb_nb] == ssb_idx) {
-            if (nb_mapped_ssb == random_ssb_nb) {
-              *prach_occasion_info_pp = prach_occasion_info_p;
-              return 1;
-            }
-            else {
-              nb_mapped_ssb++;
-            }
-          }
-        }
-      }
-    }
-  }
-
-  return 0;
-}
-
-// Build the SSB to RO mapping upon RRC configuration update
-void build_ssb_to_ro_map(NR_ServingCellConfigCommon_t *scc, uint8_t unpaired){
-
-  // Clear all the lists and maps
-  memset(&prach_assoc_pattern, 0, sizeof(prach_association_pattern_t));
-  memset(&ssb_list, 0, sizeof(ssb_list_info_t));
-
-  // Build the list of all the valid RACH occasions in the maximum association pattern period according to the PRACH config
-  LOG_D(MAC,"Build RO list\n");
-  build_ro_list(scc, unpaired);
-
-  // Build the list of all the valid/transmitted SSBs according to the config
-  LOG_D(MAC,"Build SSB list\n");
-  build_ssb_list(scc);
-
-  // Map the transmitted SSBs to the ROs and create the association pattern according to the config
-  LOG_D(MAC,"Map SSB to RO\n");
-  map_ssb_to_ro(scc);
-  LOG_D(MAC,"Map SSB to RO done\n");
-}
-
-void fill_scheduled_response(nr_scheduled_response_t *scheduled_response,
-                             fapi_nr_dl_config_request_t *dl_config,
-                             fapi_nr_ul_config_request_t *ul_config,
-                             fapi_nr_tx_request_t *tx_request,
-                             module_id_t mod_id,
-                             int cc_id,
-                             frame_t frame,
-                             int slot,
-                             int thread_id){
-
-  scheduled_response->dl_config  = dl_config;
-  scheduled_response->ul_config  = ul_config;
-  scheduled_response->tx_request = tx_request;
-  scheduled_response->module_id  = mod_id;
-  scheduled_response->CC_id      = cc_id;
-  scheduled_response->frame      = frame;
-  scheduled_response->slot       = slot;
-  scheduled_response->thread_id  = thread_id;
 
 }
 
@@ -1123,485 +488,6 @@ uint32_t get_ssb_frame(uint32_t test){
   return test;
 }
 
-void fill_ul_config(fapi_nr_ul_config_request_t *ul_config, frame_t frame_tx, int slot_tx, uint8_t pdu_type){
-
-  ul_config->ul_config_list[ul_config->number_pdus].pdu_type = pdu_type;
-  ul_config->slot = slot_tx;
-  ul_config->sfn = frame_tx;
-  ul_config->number_pdus++;
-
-  LOG_D(MAC, "In %s: Set config request for UL transmission in [%d.%d], number of UL PDUs: %d\n", __FUNCTION__, ul_config->sfn, ul_config->slot, ul_config->number_pdus);
-
-}
-
-/*
- * This function returns the slot offset K2 corresponding to a given time domain
- * indication value from RRC configuration.
- */
-long get_k2(NR_UE_MAC_INST_t *mac, uint8_t time_domain_ind) {
-  long k2 = -1;
-  // Get K2 from RRC configuration
-  NR_PUSCH_Config_t *pusch_config=mac->ULbwp[0]->bwp_Dedicated->pusch_Config->choice.setup;
-  NR_PUSCH_TimeDomainResourceAllocationList_t *pusch_TimeDomainAllocationList = NULL;
-  if (pusch_config->pusch_TimeDomainAllocationList) {
-    pusch_TimeDomainAllocationList = pusch_config->pusch_TimeDomainAllocationList->choice.setup;
-  }
-  else if (mac->ULbwp[0]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList) {
-    pusch_TimeDomainAllocationList = mac->ULbwp[0]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList;
-  }
-  if (pusch_TimeDomainAllocationList) {
-    if (time_domain_ind >= pusch_TimeDomainAllocationList->list.count) {
-      LOG_E(MAC, "time_domain_ind %d >= pusch->TimeDomainAllocationList->list.count %d\n",
-            time_domain_ind, pusch_TimeDomainAllocationList->list.count);
-      return -1;
-    }
-    k2 = *pusch_TimeDomainAllocationList->list.array[time_domain_ind]->k2;
-  }
-  
-  LOG_D(MAC, "get_k2(): k2 is %ld\n", k2);
-  return k2;
-}
-
-/*
- * This function returns the UL config corresponding to a given UL slot
- * from MAC instance .
- */
-fapi_nr_ul_config_request_t *get_ul_config_request(NR_UE_MAC_INST_t *mac, int slot) {
-  //Check if request to access ul_config is for a UL slot
-  if (is_nr_UL_slot(mac->scc, slot) == 0) {
-    LOG_W(MAC, "Slot %d is not a UL slot. %s called for wrong slot!!!\n", slot, __FUNCTION__);
-    return NULL;
-  }
-  
-  // Calculate the index of the UL slot in mac->ul_config_request list. This is
-  // based on the TDD pattern (slot configuration period) and number of UL+mixed
-  // slots in the period. TS 38.213 Sec 11.1
-  int mu = mac->ULbwp[0]->bwp_Common->genericParameters.subcarrierSpacing;
-  NR_TDD_UL_DL_Pattern_t *tdd_pattern = &mac->scc->tdd_UL_DL_ConfigurationCommon->pattern1;
-  const int num_slots_per_tdd = nr_slots_per_frame[mu] >> (7 - tdd_pattern->dl_UL_TransmissionPeriodicity);
-  const int num_slots_ul = tdd_pattern->nrofUplinkSlots + (tdd_pattern->nrofUplinkSymbols!=0);
-  int index = (slot + num_slots_ul - num_slots_per_tdd) % num_slots_per_tdd;
-
-  LOG_D(MAC, "In %s slots per tdd %d, num_slots_ul %d, index %d\n", __FUNCTION__,
-                num_slots_per_tdd,
-                num_slots_ul,
-                index);
-
-  return &mac->ul_config_request[index];
-}
-
-
-// Performs :
-// 1. TODO: Call RRC for link status return to PHY
-// 2. TODO: Perform SR/BSR procedures for scheduling feedback
-// 3. TODO: Perform PHR procedures
-NR_UE_L2_STATE_t nr_ue_scheduler(nr_downlink_indication_t *dl_info, nr_uplink_indication_t *ul_info){
-
-  uint32_t search_space_mask = 0;
-
-  if (dl_info){
-
-    module_id_t mod_id    = dl_info->module_id;
-    uint32_t gNB_index    = dl_info->gNB_index;
-    int cc_id             = dl_info->cc_id;
-    frame_t rx_frame      = dl_info->frame;
-    slot_t rx_slot        = dl_info->slot;
-    NR_UE_MAC_INST_t *mac = get_mac_inst(mod_id);
-
-    fapi_nr_dl_config_request_t *dl_config = &mac->dl_config_request;
-    nr_scheduled_response_t scheduled_response;
-    nr_dcireq_t dcireq;
-
-    // check type0 from 38.213 13 if we have no CellGroupConfig
-    // TODO: implementation to be completed
-    if (mac->scg == NULL) {
-
-      if(dl_info->ssb_index != -1){
-
-        if(mac->type0_pdcch_ss_mux_pattern == 1){
-          //  38.213 chapter 13
-          if((mac->type0_pdcch_ss_sfn_c == SFN_C_MOD_2_EQ_0) && !(rx_frame & 0x1) && (rx_slot == mac->type0_pdcch_ss_n_c)){
-            search_space_mask = search_space_mask | type0_pdcch;
-            mac->type0_pdcch_consecutive_slots = mac->type0_pdcch_dci_config.coreset.duration;
-          }
-          if((mac->type0_pdcch_ss_sfn_c == SFN_C_MOD_2_EQ_1) && (rx_frame & 0x1) && (rx_slot == mac->type0_pdcch_ss_n_c)){
-            search_space_mask = search_space_mask | type0_pdcch;
-            mac->type0_pdcch_consecutive_slots = mac->type0_pdcch_dci_config.coreset.duration;
-          }
-        }
-        if(mac->type0_pdcch_ss_mux_pattern == 2){
-          //  38.213 Table 13-13, 13-14
-          if((rx_frame == get_ssb_frame(rx_frame)) && (rx_slot == mac->type0_pdcch_ss_n_c)){
-            search_space_mask = search_space_mask | type0_pdcch;
-            mac->type0_pdcch_consecutive_slots = mac->type0_pdcch_dci_config.coreset.duration;
-          }
-        }
-        if(mac->type0_pdcch_ss_mux_pattern == 3){
-          //  38.213 Table 13-15
-          if((rx_frame == get_ssb_frame(rx_frame)) && (rx_slot == mac->type0_pdcch_ss_n_c)){
-            search_space_mask = search_space_mask | type0_pdcch;
-            mac->type0_pdcch_consecutive_slots = mac->type0_pdcch_dci_config.coreset.duration;
-          }
-        }
-      } // ssb_index != -1
-
-      // Type0 PDCCH search space
-      if((search_space_mask & type0_pdcch) || ( mac->type0_pdcch_consecutive_slots != 0 )){
-        mac->type0_pdcch_consecutive_slots = mac->type0_pdcch_consecutive_slots - 1;
-
-        dl_config->dl_config_list[dl_config->number_pdus].dci_config_pdu.dci_config_rel15 = mac->type0_pdcch_dci_config;
-        dl_config->dl_config_list[dl_config->number_pdus].pdu_type = FAPI_NR_DL_CONFIG_TYPE_DCI;
-
-        /*
-        dl_config->dl_config_list[dl_config->number_pdus].dci_config_pdu.dci_config_rel15.rnti = 0xaaaa;        //      to be set
-        dl_config->dl_config_list[dl_config->number_pdus].dci_config_pdu.dci_config_rel15.N_RB_BWP = 106;       //      to be set
-
-        LOG_I(MAC,"nr_ue_scheduler Type0 PDCCH with rnti %x, BWP %d\n",
-        dl_config->dl_config_list[dl_config->number_pdus].dci_config_pdu.dci_config_rel15.rnti,
-        dl_config->dl_config_list[dl_config->number_pdus].dci_config_pdu.dci_config_rel15.N_RB_BWP);
-        */
-        dl_config->number_pdus = dl_config->number_pdus + 1;
-
-        fill_scheduled_response(&scheduled_response, dl_config, NULL, NULL, mod_id, cc_id, rx_frame, rx_slot, dl_info->thread_id);
-        if(mac->if_module != NULL && mac->if_module->scheduled_response != NULL)
-          mac->if_module->scheduled_response(&scheduled_response);
-      }
-    } else { // we have an scg
-
-      dcireq.module_id = mod_id;
-      dcireq.gNB_index = gNB_index;
-      dcireq.cc_id     = cc_id;
-      dcireq.frame     = rx_frame;
-      dcireq.slot      = rx_slot;
-      dcireq.dl_config_req.number_pdus = 0;
-      nr_ue_dcireq(&dcireq); //to be replaced with function pointer later
-
-      fill_scheduled_response(&scheduled_response, &dcireq.dl_config_req, NULL, NULL, mod_id, cc_id, rx_frame, rx_slot, dl_info->thread_id);
-      if(mac->if_module != NULL && mac->if_module->scheduled_response != NULL){
-        mac->if_module->scheduled_response(&scheduled_response);
-      }
-
-      /*
-        if(search_space_mask & type0a_pdcch){
-        }
-        
-        if(search_space_mask & type1_pdcch){
-        }
-
-        if(search_space_mask & type2_pdcch){
-        }
-
-        if(search_space_mask & type3_pdcch){
-        }
-      */
-    }
-  } else if (ul_info) {
-
-    int cc_id             = ul_info->cc_id;
-    frame_t rx_frame      = ul_info->frame_rx;
-    slot_t rx_slot        = ul_info->slot_rx;
-    frame_t frame_tx      = ul_info->frame_tx;
-    slot_t slot_tx        = ul_info->slot_tx;
-    module_id_t mod_id    = ul_info->module_id;
-    uint8_t access_mode   = SCHEDULED_ACCESS;
-
-    NR_UE_MAC_INST_t *mac = get_mac_inst(mod_id);
-    RA_config_t *ra       = &mac->ra;
-
-    fapi_nr_ul_config_request_t *ul_config = get_ul_config_request(mac, slot_tx);
-
-    // Schedule ULSCH only if the current frame and slot match those in ul_config_req
-    // AND if a UL grant (UL DCI or Msg3) has been received (as indicated by num_pdus)
-    if ((ul_info->slot_tx == ul_config->slot && ul_info->frame_tx == ul_config->sfn) && ul_config->number_pdus > 0){
-
-      LOG_D(MAC, "In %s:[%d.%d]: number of UL PDUs: %d with UL transmission in [%d.%d]\n", __FUNCTION__, frame_tx, slot_tx, ul_config->number_pdus, ul_config->sfn, ul_config->slot);
-
-      uint8_t ulsch_input_buffer[MAX_ULSCH_PAYLOAD_BYTES];
-      uint8_t data_existing = 0;
-      nr_scheduled_response_t scheduled_response;
-      fapi_nr_tx_request_t tx_req;
-
-      for (int j = 0; j < ul_config->number_pdus; j++) {
-
-        fapi_nr_ul_config_request_pdu_t *ulcfg_pdu = &ul_config->ul_config_list[j];
-
-        if (ulcfg_pdu->pdu_type == FAPI_NR_UL_CONFIG_TYPE_PUSCH) {
-
-          uint16_t TBS_bytes = ulcfg_pdu->pusch_config_pdu.pusch_data.tb_size;
-
-          if (IS_SOFTMODEM_NOS1){
-            // Getting IP traffic to be transmitted
-            data_existing = nr_ue_get_sdu(mod_id,
-                                          cc_id,
-                                          frame_tx,
-                                          slot_tx,
-                                          0,
-                                          ulsch_input_buffer,
-                                          TBS_bytes,
-                                          &access_mode);
-          }
-
-          //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
-            //and block this traffic from being forwarded to the upper layers at the gNB
-            LOG_D(PHY, "In %s: Random data to be transmitted: TBS_bytes %d \n", __FUNCTION__, TBS_bytes);
-
-            //Give the first byte a dummy value (a value not corresponding to any valid LCID based on 38.321, Table 6.2.1-2)
-            //in order to distinguish the PHY random packets at the MAC layer of the gNB receiver from the normal packets that should
-            //have a valid LCID (nr_process_mac_pdu function)
-            ulsch_input_buffer[0] = 0x31;
-
-            for (int i = 1; i < TBS_bytes; i++) {
-              ulsch_input_buffer[i] = (unsigned char) rand();
-              //printf(" input encoder a[%d]=0x%02x\n",i,harq_process_ul_ue->a[i]);
-            }
-          }
-
-          #ifdef DEBUG_MAC_PDU
-          LOG_D(PHY, "Is data existing ?: %d \n", data_existing);
-          LOG_I(PHY, "Printing MAC PDU to be encoded, TBS is: %d \n", TBS_bytes);
-          for (i = 0; i < TBS_bytes; i++) {
-            printf("%02x", ulsch_input_buffer[i]);
-          }
-          printf("\n");
-          #endif
-
-          // Config UL TX PDU
-          tx_req.slot = slot_tx;
-          tx_req.sfn = frame_tx;
-          tx_req.number_of_pdus++;
-          tx_req.tx_request_body[0].pdu_length = TBS_bytes;
-          tx_req.tx_request_body[0].pdu_index = j;
-          tx_req.tx_request_body[0].pdu = ulsch_input_buffer;
-
-          if (ra->ra_state != RA_SUCCEEDED && !ra->cfra){
-            nr_Msg3_transmitted(ul_info->module_id, ul_info->cc_id, ul_info->frame_tx, ul_info->gNB_index);
-          }
-
-        }
-      }
-
-      fill_scheduled_response(&scheduled_response, NULL, ul_config, &tx_req, mod_id, cc_id, rx_frame, rx_slot, ul_info->thread_id);
-      if(mac->if_module != NULL && mac->if_module->scheduled_response != NULL){
-        mac->if_module->scheduled_response(&scheduled_response);
-      }
-    }
-  }
-
-  return UE_CONNECTION_OK;
-
-}
-
-// PUSCH scheduler:
-// - Calculate the slot in which ULSCH should be scheduled. This is current slot + K2,
-// - where K2 is the offset between the slot in which UL DCI is received and the slot
-// - in which ULSCH should be scheduled. K2 is configured in RRC configuration.  
-// PUSCH Msg3 scheduler:
-// - scheduled by RAR UL grant according to 8.3 of TS 38.213
-// Note: Msg3 tx in the uplink symbols of mixed slot
-int nr_ue_pusch_scheduler(NR_UE_MAC_INST_t *mac,
-                          uint8_t is_Msg3,
-                          frame_t current_frame,
-                          int current_slot,
-                          frame_t *frame_tx,
-                          int *slot_tx,
-                          uint8_t tda_id){
-
-  int delta = 0;
-  NR_BWP_Uplink_t *ubwp = mac->ULbwp[0];
-  // Get the numerology to calculate the Tx frame and slot
-  int mu = ubwp->bwp_Common->genericParameters.subcarrierSpacing;
-  struct NR_PUSCH_TimeDomainResourceAllocationList *pusch_TimeDomainAllocationList = ubwp->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList;
-  // k2 as per 3GPP TS 38.214 version 15.9.0 Release 15 ch 6.1.2.1.1
-  // PUSCH time domain resource allocation is higher layer configured from uschTimeDomainAllocationList in either pusch-ConfigCommon
-  uint8_t k2;
-
-  if (is_Msg3) {
-    k2 = *pusch_TimeDomainAllocationList->list.array[tda_id]->k2;
-
-    switch (mu) {
-      case 0:
-        delta = 2;
-        break;
-      case 1:
-        delta = 3;
-        break;
-      case 2:
-        delta = 4;
-        break;
-      case 3:
-        delta = 6;
-        break;
-    }
-
-    *slot_tx = (current_slot + k2 + delta) % nr_slots_per_frame[mu];
-    if (current_slot + k2 + delta > nr_slots_per_frame[mu]){
-      *frame_tx = (current_frame + 1) % 1024;
-    } else {
-      *frame_tx = current_frame;
-    }
-
-  } else {
-
-    // Get slot offset K2 which will be used to calculate TX slot
-    k2 = get_k2(mac, tda_id);
-    if (k2 < 0) { // This can happen when a false DCI is received
-      return -1;
-    }
-
-    // Calculate TX slot and frame
-    *slot_tx = (current_slot + k2) % nr_slots_per_frame[mu];
-    *frame_tx = ((current_slot + k2) > nr_slots_per_frame[mu]) ? (current_frame + 1) % 1024 : current_frame;
-
-  }
-
-  LOG_D(MAC, "In %s: currently at [%d.%d] UL transmission in [%d.%d] (k2 %d delta %d)\n", __FUNCTION__, current_frame, current_slot, *frame_tx, *slot_tx, k2, delta);
-
-  return 0;
-
-}
-
-// This function schedules the PRACH according to prach_ConfigurationIndex and TS 38.211, tables 6.3.3.2.x
-// PRACH formats 9, 10, 11 are corresponding to dual PRACH format configurations A1/B1, A2/B2, A3/B3.
-// - todo:
-// - Partial configuration is actually already stored in (fapi_nr_prach_config_t) &mac->phy_config.config_req->prach_config
-void nr_ue_prach_scheduler(module_id_t module_idP, frame_t frameP, sub_frame_t slotP, int thread_id) {
-
-  uint16_t format, format0, format1, ncs;
-  int is_nr_prach_slot;
-  prach_occasion_info_t *prach_occasion_info_p;
-
-  NR_UE_MAC_INST_t *mac = get_mac_inst(module_idP);
-  RA_config_t *ra = &mac->ra;
-
-  //fapi_nr_ul_config_request_t *ul_config = get_ul_config_request(mac, slotP);
-  fapi_nr_ul_config_request_t *ul_config = &mac->ul_config_request[0];
-  fapi_nr_ul_config_prach_pdu *prach_config_pdu;
-  fapi_nr_config_request_t *cfg = &mac->phy_config.config_req;
-  fapi_nr_prach_config_t *prach_config = &cfg->prach_config;
-  nr_scheduled_response_t scheduled_response;
-
-  NR_ServingCellConfigCommon_t *scc = mac->scc;
-  NR_RACH_ConfigCommon_t *setup = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup;
-  NR_RACH_ConfigGeneric_t *rach_ConfigGeneric = &setup->rach_ConfigGeneric;
-
-  ra->RA_offset = 2; // to compensate the rx frame offset at the gNB
-  ra->generate_nr_prach = 0; // Reset flag for PRACH generation
-
-  if (is_nr_UL_slot(scc, slotP)) {
-
-    // WIP Need to get the proper selected ssb_idx
-    //     Initial beam selection functionality is not available yet
-    uint8_t selected_gnb_ssb_idx = 0;
-
-    // Get any valid PRACH occasion in the current slot for the selected SSB index
-    is_nr_prach_slot = get_nr_prach_info_from_ssb_index(selected_gnb_ssb_idx,
-                                                       (int)frameP,
-                                                       (int)slotP,
-                                                        &prach_occasion_info_p);
-
-    if (is_nr_prach_slot && ra->ra_state == RA_UE_IDLE) {
-      AssertFatal(NULL != prach_occasion_info_p,"PRACH Occasion Info not returned in a valid NR Prach Slot\n");
-
-      ra->generate_nr_prach = 1;
-
-      format = prach_occasion_info_p->format;
-      format0 = format & 0xff;        // single PRACH format
-      format1 = (format >> 8) & 0xff; // dual PRACH format
-
-      ul_config->sfn = frameP;
-      ul_config->slot = slotP;
-
-      ul_config->ul_config_list[ul_config->number_pdus].pdu_type = FAPI_NR_UL_CONFIG_TYPE_PRACH;
-      prach_config_pdu = &ul_config->ul_config_list[ul_config->number_pdus].prach_config_pdu;
-      memset(prach_config_pdu, 0, sizeof(fapi_nr_ul_config_prach_pdu));
-      ul_config->number_pdus += 1;
-      LOG_D(PHY, "In %s: (%p) %d UL PDUs:\n", __FUNCTION__, ul_config, ul_config->number_pdus);
-
-      ncs = get_NCS(rach_ConfigGeneric->zeroCorrelationZoneConfig, format0, setup->restrictedSetConfig);
-
-      prach_config_pdu->phys_cell_id = *scc->physCellId;
-      prach_config_pdu->num_prach_ocas = 1;
-      prach_config_pdu->prach_slot = prach_occasion_info_p->slot;
-      prach_config_pdu->prach_start_symbol = prach_occasion_info_p->start_symbol;
-      prach_config_pdu->num_ra = prach_occasion_info_p->fdm;
-
-      prach_config_pdu->num_cs = ncs;
-      prach_config_pdu->root_seq_id = prach_config->num_prach_fd_occasions_list[prach_occasion_info_p->fdm].prach_root_sequence_index;
-      prach_config_pdu->restricted_set = prach_config->restricted_set_config;
-      prach_config_pdu->freq_msg1 = prach_config->num_prach_fd_occasions_list[prach_occasion_info_p->fdm].k1;
-
-      LOG_D(MAC,"Selected RO Frame %u, Slot %u, Symbol %u, Fdm %u\n", frameP, prach_config_pdu->prach_slot, prach_config_pdu->prach_start_symbol, prach_config_pdu->num_ra);
-
-      // Search which SSB is mapped in the RO (among all the SSBs mapped to this RO)
-      for (prach_config_pdu->ssb_nb_in_ro=0; prach_config_pdu->ssb_nb_in_ro<prach_occasion_info_p->nb_mapped_ssb; prach_config_pdu->ssb_nb_in_ro++) {
-        if (prach_occasion_info_p->mapped_ssb_idx[prach_config_pdu->ssb_nb_in_ro] == selected_gnb_ssb_idx)
-          break;
-      }
-      AssertFatal(prach_config_pdu->ssb_nb_in_ro<prach_occasion_info_p->nb_mapped_ssb, "%u not found in the mapped SSBs to the PRACH occasion", selected_gnb_ssb_idx);
-
-      if (format1 != 0xff) {
-        switch(format0) { // dual PRACH format
-          case 0xa1:
-            prach_config_pdu->prach_format = 11;
-            break;
-          case 0xa2:
-            prach_config_pdu->prach_format = 12;
-            break;
-          case 0xa3:
-            prach_config_pdu->prach_format = 13;
-            break;
-        default:
-          AssertFatal(1 == 0, "Only formats A1/B1 A2/B2 A3/B3 are valid for dual format");
-        }
-      } else {
-        switch(format0) { // single PRACH format
-          case 0:
-            prach_config_pdu->prach_format = 0;
-            break;
-          case 1:
-            prach_config_pdu->prach_format = 1;
-            break;
-          case 2:
-            prach_config_pdu->prach_format = 2;
-            break;
-          case 3:
-            prach_config_pdu->prach_format = 3;
-            break;
-          case 0xa1:
-            prach_config_pdu->prach_format = 4;
-            break;
-          case 0xa2:
-            prach_config_pdu->prach_format = 5;
-            break;
-          case 0xa3:
-            prach_config_pdu->prach_format = 6;
-            break;
-          case 0xb1:
-            prach_config_pdu->prach_format = 7;
-            break;
-          case 0xb4:
-            prach_config_pdu->prach_format = 8;
-            break;
-          case 0xc0:
-            prach_config_pdu->prach_format = 9;
-            break;
-          case 0xc2:
-            prach_config_pdu->prach_format = 10;
-            break;
-          default:
-            AssertFatal(1 == 0, "Invalid PRACH format");
-        }
-      } // if format1
-    } // is_nr_prach_slot
-
-    fill_scheduled_response(&scheduled_response, NULL, ul_config, NULL, module_idP, 0 /*TBR fix*/, frameP, slotP, thread_id);
-    if(mac->if_module != NULL && mac->if_module->scheduled_response != NULL)
-      mac->if_module->scheduled_response(&scheduled_response);
-  } // if is_nr_UL_slot
-}
-
 /*
  * This code contains all the functions needed to process all dci fields.
  * These tables and functions are going to be called by function nr_ue_process_dci
@@ -3501,123 +2387,200 @@ uint16_t nr_generate_ulsch_pdu(uint8_t *sdus_payload,
   return offset;
 }
 
-uint8_t
-nr_ue_get_sdu(module_id_t module_idP, int CC_id, frame_t frameP,
-           sub_frame_t subframe, uint8_t eNB_index,
-           uint8_t *ulsch_buffer, uint16_t buflen, uint8_t *access_mode) {
-  uint8_t total_rlc_pdu_header_len = 0;
-  int16_t buflen_remain = 0;
-  uint8_t lcid = 0;
-  uint16_t sdu_lengths[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
-  uint8_t sdu_lcids[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
-  uint16_t payload_offset = 0, num_sdus = 0;
-  uint8_t ulsch_sdus[MAX_ULSCH_PAYLOAD_BYTES];
-  uint16_t sdu_length_total = 0;
-  //unsigned short post_padding = 0;
-  NR_UE_MAC_INST_t *mac = get_mac_inst(module_idP);
+/////////////////////////////////////
+//    Random Access Response PDU   //
+//         TS 38.213 ch 8.2        //
+//        TS 38.321 ch 6.2.3       //
+/////////////////////////////////////
+//| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |// bit-wise
+//| E | T |       R A P I D       |//
+//| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |//
+//| R |           T A             |//
+//|       T A         |  UL grant |//
+//|            UL grant           |//
+//|            UL grant           |//
+//|            UL grant           |//
+//|         T C - R N T I         |//
+//|         T C - R N T I         |//
+/////////////////////////////////////
+//       UL grant  (27 bits)       //
+/////////////////////////////////////
+//| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |// bit-wise
+//|-------------------|FHF|F_alloc|//
+//|        Freq allocation        |//
+//|    F_alloc    |Time allocation|//
+//|      MCS      |     TPC   |CSI|//
+/////////////////////////////////////
+// TbD WIP Msg3 development ongoing
+// - apply UL grant freq alloc & time alloc as per 8.2 TS 38.213
+// - apply tpc command
+// WIP fix:
+// - time domain indication hardcoded to 0 for k2 offset
+// - extend TS 38.213 ch 8.3 Msg3 PUSCH
+// - b buffer
+// - ulsch power offset
+// - optimize: mu_pusch, j and table_6_1_2_1_1_2_time_dom_res_alloc_A are already defined in nr_ue_procedures
+int nr_ue_process_rar(nr_downlink_indication_t *dl_info, NR_UL_TIME_ALIGNMENT_t *ul_time_alignment, int pdu_id){
+
+  module_id_t mod_id       = dl_info->module_id;
+  frame_t frame            = dl_info->frame;
+  int slot                 = dl_info->slot;
+  int cc_id                = dl_info->cc_id;
+  uint8_t gNB_id           = dl_info->gNB_index;
+  NR_UE_MAC_INST_t *mac    = get_mac_inst(mod_id);
+  RA_config_t *ra          = &mac->ra;
+  uint8_t n_subPDUs        = 0;  // number of RAR payloads
+  uint8_t n_subheaders     = 0;  // number of MAC RAR subheaders
+  uint8_t *dlsch_buffer    = dl_info->rx_ind->rx_indication_body[pdu_id].pdsch_pdu.pdu;
+  uint8_t is_Msg3          = 1;
+  frame_t frame_tx         = 0;
+  int slot_tx              = 0;
+  uint16_t rnti            = 0;
+  int ret                  = 0;
+  NR_RA_HEADER_RAPID *rarh = (NR_RA_HEADER_RAPID *) dlsch_buffer; // RAR subheader pointer
+  NR_MAC_RAR *rar          = (NR_MAC_RAR *) (dlsch_buffer + 1);   // RAR subPDU pointer
+  uint8_t preamble_index   = get_ra_PreambleIndex(mod_id, cc_id, gNB_id); //prach_resources->ra_PreambleIndex;
+
+  LOG_D(MAC, "In %s:[%d.%d]: [UE %d][RAPROC] invoking MAC for received RAR (current preamble %d)\n", __FUNCTION__, frame, slot, mod_id, preamble_index);
+
+  while (1) {
+    n_subheaders++;
+    if (rarh->T == 1) {
+      n_subPDUs++;
+      LOG_D(MAC, "[UE %d][RAPROC] Got RAPID RAR subPDU\n", mod_id);
+    } else {
+      n_subPDUs++;
+      ra->RA_backoff_indicator = table_7_2_1[((NR_RA_HEADER_BI *)rarh)->BI];
+      ra->RA_BI_found = 1;
+      LOG_D(MAC, "[UE %d][RAPROC] Got BI RAR subPDU %d\n", mod_id, ra->RA_backoff_indicator);
+    }
+    if (rarh->RAPID == preamble_index) {
+      LOG_I(MAC, "[UE %d][RAPROC][%d.%d] Found RAR with the intended RAPID %d\n", mod_id, frame, slot, rarh->RAPID);
+      rar = (NR_MAC_RAR *) (dlsch_buffer + n_subheaders + (n_subPDUs - 1) * sizeof(NR_MAC_RAR));
+      ra->RA_RAPID_found = 1;
+      break;
+    }
+    if (rarh->E == 0) {
+      LOG_W(PHY,"[UE %d][RAPROC] Received RAR preamble (%d) doesn't match the intended RAPID...\n", mod_id, preamble_index);
+      break;
+    } else {
+      rarh += sizeof(NR_MAC_RAR) + 1;
+    }
+  }
 
-  rlc_buffer_occupancy_t lcid_buffer_occupancy_old =
-    0, lcid_buffer_occupancy_new = 0;
-  LOG_D(MAC,
-        "[UE %d] MAC PROCESS UL TRANSPORT BLOCK at frame%d subframe %d TBS=%d\n",
-        module_idP, frameP, subframe, buflen);
-  AssertFatal(CC_id == 0,
-              "Transmission on secondary CCs is not supported yet\n");
-
-  // Check for DCCH first
-  // TO DO: Multiplex in the order defined by the logical channel prioritization
-  for (lcid = UL_SCH_LCID_SRB1;
-       lcid < NR_MAX_NUM_LCID; lcid++) {
-
-      lcid_buffer_occupancy_old = mac_rlc_get_buffer_occupancy_ind(module_idP, mac->crnti, eNB_index, frameP, subframe, ENB_FLAG_NO, lcid);
-      lcid_buffer_occupancy_new = lcid_buffer_occupancy_old;
-
-      if(lcid_buffer_occupancy_new){
-
-        buflen_remain =
-          buflen - (total_rlc_pdu_header_len + sdu_length_total + MAX_RLC_SDU_SUBHEADER_SIZE);
-        LOG_D(MAC,
-              "[UE %d] Frame %d : UL-DXCH -> ULSCH, RLC %d has %d bytes to "
-              "send (Transport Block size %d SDU Length Total %d , mac header len %d, buflen_remain %d )\n", //BSR byte before Tx=%d
-              module_idP, frameP, lcid, lcid_buffer_occupancy_new,
-              buflen, sdu_length_total,
-              total_rlc_pdu_header_len, buflen_remain); // ,nr_ue_mac_inst->scheduling_info.BSR_bytes[nr_ue_mac_inst->scheduling_info.LCGID[lcid]]
-
-        while(buflen_remain > 0 && lcid_buffer_occupancy_new){
-
-        sdu_lengths[num_sdus] = mac_rlc_data_req(module_idP,
-                                mac->crnti,
-                                eNB_index,
-                                frameP,
-                                ENB_FLAG_NO,
-                                MBMS_FLAG_NO,
-                                lcid,
-                                buflen_remain,
-                                (char *)&ulsch_sdus[sdu_length_total],0,
-                                0);
-
-        AssertFatal(buflen_remain >= sdu_lengths[num_sdus],
-                    "LCID=%d RLC has segmented %d bytes but MAC has max=%d\n",
-                    lcid, sdu_lengths[num_sdus], buflen_remain);
-
-        if (sdu_lengths[num_sdus]) {
-          sdu_length_total += sdu_lengths[num_sdus];
-          sdu_lcids[num_sdus] = lcid;
-
-          total_rlc_pdu_header_len += MAX_RLC_SDU_SUBHEADER_SIZE; //rlc_pdu_header_len_last;
-
-          //Update number of SDU
-          num_sdus++;
-        }
+  #ifdef DEBUG_RAR
+  LOG_D(MAC, "[DEBUG_RAR] (%d,%d) number of RAR subheader %d; number of RAR pyloads %d\n", frame, slot, n_subheaders, n_subPDUs);
+  LOG_D(MAC, "[DEBUG_RAR] Received RAR (%02x|%02x.%02x.%02x.%02x.%02x.%02x) for preamble %d/%d\n", *(uint8_t *) rarh, rar[0], rar[1], rar[2], rar[3], rar[4], rar[5], rarh->RAPID, preamble_index);
+  #endif
 
-        /* Get updated BO after multiplexing this PDU */
-        lcid_buffer_occupancy_new = mac_rlc_get_buffer_occupancy_ind(module_idP,
-                                                                     mac->crnti,
-                                                                     eNB_index,
-                                                                     frameP,
-                                                                     subframe,
-                                                                     ENB_FLAG_NO,
-                                                                     lcid);
-        buflen_remain =
-                  buflen - (total_rlc_pdu_header_len + sdu_length_total + MAX_RLC_SDU_SUBHEADER_SIZE);
-        }
-  }
+  if (ra->RA_RAPID_found) {
 
-}
+    RAR_grant_t rar_grant;
 
-  // Generate ULSCH PDU
-  if (num_sdus>0) {
-  payload_offset = nr_generate_ulsch_pdu(ulsch_sdus,
-                                         ulsch_buffer,  // mac header
-                                         num_sdus,  // num sdus
-                                         sdu_lengths, // sdu length
-                                         sdu_lcids, // sdu lcid
-                                         0, // power_headroom
-                                         mac->crnti, // crnti
-                                         0, // truncated_bsr
-                                         0, // short_bsr
-                                         0, // long_bsr
-                                         0, // post_padding 
-                                         buflen);  // TBS in bytes
-  }
-  else
-    return 0;
+    unsigned char tpc_command;
+#ifdef DEBUG_RAR
+    unsigned char csi_req;
+#endif
+
+  // TC-RNTI
+  ra->t_crnti = rar->TCRNTI_2 + (rar->TCRNTI_1 << 8);
 
-  // Padding: fill remainder of ULSCH with 0
-  if (buflen - payload_offset > 0){
-      for (int j = payload_offset; j < buflen; j++)
-        ulsch_buffer[j] = 0;
+  // TA command
+  ul_time_alignment->apply_ta = 1;
+  ul_time_alignment->ta_command = rar->TA2 + (rar->TA1 << 5);
+
+#ifdef DEBUG_RAR
+  // CSI
+  csi_req = (unsigned char) (rar->UL_GRANT_4 & 0x01);
+#endif
+
+  // TPC
+  tpc_command = (unsigned char) ((rar->UL_GRANT_4 >> 1) & 0x07);
+  switch (tpc_command){
+    case 0:
+      ra->Msg3_TPC = -6;
+      break;
+    case 1:
+      ra->Msg3_TPC = -4;
+      break;
+    case 2:
+      ra->Msg3_TPC = -2;
+      break;
+    case 3:
+      ra->Msg3_TPC = 0;
+      break;
+    case 4:
+      ra->Msg3_TPC = 2;
+      break;
+    case 5:
+      ra->Msg3_TPC = 4;
+      break;
+    case 6:
+      ra->Msg3_TPC = 6;
+      break;
+    case 7:
+      ra->Msg3_TPC = 8;
+      break;
   }
+    // MCS
+    rar_grant.mcs = (unsigned char) (rar->UL_GRANT_4 >> 4);
+    // time alloc
+    rar_grant.Msg3_t_alloc = (unsigned char) (rar->UL_GRANT_3 & 0x07);
+    // frequency alloc
+    rar_grant.Msg3_f_alloc = (uint16_t) ((rar->UL_GRANT_3 >> 4) | (rar->UL_GRANT_2 << 4) | ((rar->UL_GRANT_1 & 0x03) << 12));
+    // frequency hopping
+    rar_grant.freq_hopping = (unsigned char) (rar->UL_GRANT_1 >> 2);
+    // TC-RNTI
+    if (ra->t_crnti) {
+      rnti = ra->t_crnti;
+    } else {
+      rnti = mac->crnti;
+    }
+
+  #ifdef DEBUG_RAR
+  LOG_D(MAC, "In %s:[%d.%d]: [UE %d] Received RAR with t_alloc %d f_alloc %d ta_command %d mcs %d freq_hopping %d tpc_command %d csi_req %d t_crnti %x \n",
+    __FUNCTION__,
+    frame,
+    slot,
+    mod_id,
+    rar_grant.Msg3_t_alloc,
+    rar_grant.Msg3_f_alloc,
+    ul_time_alignment->ta_command,
+    rar_grant.mcs,
+    rar_grant.freq_hopping,
+    tpc_command,
+    csi_req,
+    ra->t_crnti);
+  #endif
+
+    // Schedule Msg3
+    ret = nr_ue_pusch_scheduler(mac, is_Msg3, frame, slot, &frame_tx, &slot_tx, rar_grant.Msg3_t_alloc);
+
+    if (ret != -1){
+
+      fapi_nr_ul_config_request_t *ul_config = get_ul_config_request(mac, slot_tx);
+
+      if (!ul_config) {
+        LOG_W(MAC, "In %s: ul_config request is NULL. Probably due to unexpected UL DCI in frame.slot %d.%d. Ignoring DCI!\n", __FUNCTION__, frame, slot);
+        return -1;
+      }
+
+      nfapi_nr_ue_pusch_pdu_t *pusch_config_pdu = &ul_config->ul_config_list[ul_config->number_pdus].pusch_config_pdu;
+
+      fill_ul_config(ul_config, frame_tx, slot_tx, FAPI_NR_UL_CONFIG_TYPE_PUSCH);
+
+      // Config Msg3 PDU
+      nr_config_pusch_pdu(mac, pusch_config_pdu, NULL, &rar_grant, rnti, NULL);
+
+    }
+
+  } else {
+
+    ra->t_crnti = 0;
+    ul_time_alignment->ta_command = (0xffff);
 
-#if defined(ENABLE_MAC_PAYLOAD_DEBUG)
-  LOG_I(MAC, "Printing UL MAC payload UE side, payload_offset: %d \n", payload_offset);
-  for (int i = 0; i < buflen ; i++) {
-    //harq_process_ul_ue->a[i] = (unsigned char) rand();
-    //printf("a[%d]=0x%02x\n",i,harq_process_ul_ue->a[i]);
-    printf("%02x ",(unsigned char)ulsch_buffer[i]);
   }
-  printf("\n");
-#endif
 
-  return 1;
-}
+  return ret;
+
+}
\ No newline at end of file
diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c
new file mode 100644
index 0000000000000000000000000000000000000000..aa64ac10b3c6a94115ec3a5a6d9c62cdeed60982
--- /dev/null
+++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c
@@ -0,0 +1,1811 @@
+/*
+ * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The OpenAirInterface Software Alliance licenses this file to You under
+ * the OAI Public License, Version 1.1  (the "License"); you may not use this file
+ * except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.openairinterface.org/?page_id=698
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *-------------------------------------------------------------------------------
+ * For more information about the OpenAirInterface (OAI) Software Alliance:
+ *      contact@openairinterface.org
+ */
+
+/* \file        nr_ue_scheduler.c
+ * \brief       Routines for UE scheduling
+ * \author      Guido Casati
+ * \date        Jan 2021
+ * \version     0.1
+ * \company     Fraunhofer IIS
+ * \email       guido.casati@iis.fraunhofer.de
+ */
+
+#include <stdio.h>
+#include <math.h>
+
+/* exe */
+#include <common/utils/nr/nr_common.h>
+
+/* RRC*/
+#include "RRC/NR_UE/rrc_proto.h"
+#include "NR_RACH-ConfigCommon.h"
+#include "NR_RACH-ConfigGeneric.h"
+#include "NR_FrequencyInfoDL.h"
+#include "NR_PDCCH-ConfigCommon.h"
+
+/* MAC */
+#include "NR_MAC_COMMON/nr_mac.h"
+#include "NR_MAC_UE/mac_proto.h"
+#include "NR_MAC_UE/mac_extern.h"
+
+/* utils */
+#include "assertions.h"
+#include "asn1_conversions.h"
+#include "SIMULATION/TOOLS/sim.h" // for taus
+
+static prach_association_pattern_t prach_assoc_pattern;
+static ssb_list_info_t ssb_list;
+
+void fill_ul_config(fapi_nr_ul_config_request_t *ul_config, frame_t frame_tx, int slot_tx, uint8_t pdu_type){
+
+  ul_config->ul_config_list[ul_config->number_pdus].pdu_type = pdu_type;
+  ul_config->slot = slot_tx;
+  ul_config->sfn = frame_tx;
+  ul_config->number_pdus++;
+
+  LOG_D(MAC, "In %s: Set config request for UL transmission in [%d.%d], number of UL PDUs: %d\n", __FUNCTION__, ul_config->sfn, ul_config->slot, ul_config->number_pdus);
+
+}
+
+void fill_scheduled_response(nr_scheduled_response_t *scheduled_response,
+                             fapi_nr_dl_config_request_t *dl_config,
+                             fapi_nr_ul_config_request_t *ul_config,
+                             fapi_nr_tx_request_t *tx_request,
+                             module_id_t mod_id,
+                             int cc_id,
+                             frame_t frame,
+                             int slot,
+                             int thread_id){
+
+  scheduled_response->dl_config  = dl_config;
+  scheduled_response->ul_config  = ul_config;
+  scheduled_response->tx_request = tx_request;
+  scheduled_response->module_id  = mod_id;
+  scheduled_response->CC_id      = cc_id;
+  scheduled_response->frame      = frame;
+  scheduled_response->slot       = slot;
+  scheduled_response->thread_id  = thread_id;
+
+}
+
+/*
+ * This function returns the slot offset K2 corresponding to a given time domain
+ * indication value from RRC configuration.
+ */
+long get_k2(NR_UE_MAC_INST_t *mac, uint8_t time_domain_ind) {
+  long k2 = -1;
+  // Get K2 from RRC configuration
+  NR_PUSCH_Config_t *pusch_config=mac->ULbwp[0]->bwp_Dedicated->pusch_Config->choice.setup;
+  NR_PUSCH_TimeDomainResourceAllocationList_t *pusch_TimeDomainAllocationList = NULL;
+  if (pusch_config->pusch_TimeDomainAllocationList) {
+    pusch_TimeDomainAllocationList = pusch_config->pusch_TimeDomainAllocationList->choice.setup;
+  }
+  else if (mac->ULbwp[0]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList) {
+    pusch_TimeDomainAllocationList = mac->ULbwp[0]->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList;
+  }
+  if (pusch_TimeDomainAllocationList) {
+    if (time_domain_ind >= pusch_TimeDomainAllocationList->list.count) {
+      LOG_E(MAC, "time_domain_ind %d >= pusch->TimeDomainAllocationList->list.count %d\n",
+            time_domain_ind, pusch_TimeDomainAllocationList->list.count);
+      return -1;
+    }
+    k2 = *pusch_TimeDomainAllocationList->list.array[time_domain_ind]->k2;
+  }
+  
+  LOG_D(MAC, "get_k2(): k2 is %ld\n", k2);
+  return k2;
+}
+
+/*
+ * This function returns the UL config corresponding to a given UL slot
+ * from MAC instance .
+ */
+fapi_nr_ul_config_request_t *get_ul_config_request(NR_UE_MAC_INST_t *mac, int slot) {
+  //Check if request to access ul_config is for a UL slot
+  if (is_nr_UL_slot(mac->scc, slot) == 0) {
+    LOG_W(MAC, "Slot %d is not a UL slot. %s called for wrong slot!!!\n", slot, __FUNCTION__);
+    return NULL;
+  }
+  
+  // Calculate the index of the UL slot in mac->ul_config_request list. This is
+  // based on the TDD pattern (slot configuration period) and number of UL+mixed
+  // slots in the period. TS 38.213 Sec 11.1
+  int mu = mac->ULbwp[0]->bwp_Common->genericParameters.subcarrierSpacing;
+  NR_TDD_UL_DL_Pattern_t *tdd_pattern = &mac->scc->tdd_UL_DL_ConfigurationCommon->pattern1;
+  const int num_slots_per_tdd = nr_slots_per_frame[mu] >> (7 - tdd_pattern->dl_UL_TransmissionPeriodicity);
+  const int num_slots_ul = tdd_pattern->nrofUplinkSlots + (tdd_pattern->nrofUplinkSymbols!=0);
+  int index = (slot + num_slots_ul - num_slots_per_tdd) % num_slots_per_tdd;
+
+  LOG_D(MAC, "In %s slots per tdd %d, num_slots_ul %d, index %d\n", __FUNCTION__,
+                num_slots_per_tdd,
+                num_slots_ul,
+                index);
+
+  return &mac->ul_config_request[index];
+}
+
+void ul_layers_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_config_pdu, dci_pdu_rel15_t *dci) {
+
+  fapi_nr_pusch_config_dedicated_t *pusch_config_dedicated = &mac->phy_config.config_req.ul_bwp_dedicated.pusch_config_dedicated;
+
+  /* PRECOD_NBR_LAYERS */
+  if ((pusch_config_dedicated->tx_config == tx_config_nonCodebook));
+  // 0 bits if the higher layer parameter txConfig = nonCodeBook
+
+  if ((pusch_config_dedicated->tx_config == tx_config_codebook)){
+
+    uint8_t n_antenna_port = 0; //FIXME!!!
+
+    if (n_antenna_port == 1); // 1 antenna port and the higher layer parameter txConfig = codebook 0 bits
+
+    if (n_antenna_port == 4){ // 4 antenna port and the higher layer parameter txConfig = codebook
+
+      // Table 7.3.1.1.2-2: transformPrecoder=disabled and maxRank = 2 or 3 or 4
+      if ((pusch_config_dedicated->transform_precoder == transform_precoder_disabled)
+        && ((pusch_config_dedicated->max_rank == 2) ||
+        (pusch_config_dedicated->max_rank == 3) ||
+        (pusch_config_dedicated->max_rank == 4))){
+
+        if (pusch_config_dedicated->codebook_subset == codebook_subset_fullyAndPartialAndNonCoherent) {
+          pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][0];
+          pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][1];
+        }
+
+        if (pusch_config_dedicated->codebook_subset == codebook_subset_partialAndNonCoherent){
+          pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][2];
+          pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][3];
+        }
+
+        if (pusch_config_dedicated->codebook_subset == codebook_subset_nonCoherent){
+          pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][4];
+          pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][5];
+        }
+      }
+
+      // Table 7.3.1.1.2-3: transformPrecoder= enabled, or transformPrecoder=disabled and maxRank = 1
+      if (((pusch_config_dedicated->transform_precoder == transform_precoder_enabled)
+        || (pusch_config_dedicated->transform_precoder == transform_precoder_disabled))
+        && (pusch_config_dedicated->max_rank == 1)){
+
+        if (pusch_config_dedicated->codebook_subset == codebook_subset_fullyAndPartialAndNonCoherent) {
+          pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][6];
+          pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][7];
+        }
+
+        if (pusch_config_dedicated->codebook_subset == codebook_subset_partialAndNonCoherent){
+          pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][8];
+          pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][9];
+        }
+
+        if (pusch_config_dedicated->codebook_subset == codebook_subset_nonCoherent){
+          pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][10];
+          pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][11];
+        }
+      }
+    }
+
+    if (n_antenna_port == 4){ // 2 antenna port and the higher layer parameter txConfig = codebook
+      // Table 7.3.1.1.2-4: transformPrecoder=disabled and maxRank = 2
+      if ((pusch_config_dedicated->transform_precoder == transform_precoder_disabled) && (pusch_config_dedicated->max_rank == 2)){
+
+        if (pusch_config_dedicated->codebook_subset == codebook_subset_fullyAndPartialAndNonCoherent) {
+          pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][12];
+          pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][13];
+        }
+
+        if (pusch_config_dedicated->codebook_subset == codebook_subset_nonCoherent){
+          pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][14];
+          pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][15];
+        }
+
+      }
+
+      // Table 7.3.1.1.2-5: transformPrecoder= enabled, or transformPrecoder= disabled and maxRank = 1
+      if (((pusch_config_dedicated->transform_precoder == transform_precoder_enabled)
+        || (pusch_config_dedicated->transform_precoder == transform_precoder_disabled))
+        && (pusch_config_dedicated->max_rank == 1)){
+
+        if (pusch_config_dedicated->codebook_subset == codebook_subset_fullyAndPartialAndNonCoherent) {
+          pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][16];
+          pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][17];
+        }
+
+        if (pusch_config_dedicated->codebook_subset == codebook_subset_nonCoherent){
+          pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][18];
+          pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][19];
+        }
+
+      }
+    }
+  }
+}
+
+// todo: this function shall be reviewed completely because of the many comments left by the author
+void ul_ports_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_config_pdu, dci_pdu_rel15_t *dci) {
+
+  /* ANTENNA_PORTS */
+  uint8_t rank = 0; // We need to initialize rank FIXME!!!
+  fapi_nr_pusch_config_dedicated_t *pusch_config_dedicated = &mac->phy_config.config_req.ul_bwp_dedicated.pusch_config_dedicated;
+
+  if ((pusch_config_dedicated->transform_precoder == transform_precoder_enabled) &&
+    (pusch_config_dedicated->dmrs_ul_for_pusch_mapping_type_a.dmrs_type == 1) &&
+    (pusch_config_dedicated->dmrs_ul_for_pusch_mapping_type_a.max_length == 1)) { // tables 7.3.1.1.2-6
+      pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2; //TBC
+      pusch_config_pdu->dmrs_ports = dci->antenna_ports.val; //TBC
+  }
+
+  if ((pusch_config_dedicated->transform_precoder == transform_precoder_enabled) &&
+    (pusch_config_dedicated->dmrs_ul_for_pusch_mapping_type_a.dmrs_type == 1) &&
+    (pusch_config_dedicated->dmrs_ul_for_pusch_mapping_type_a.max_length == 2)) { // tables 7.3.1.1.2-7
+
+    pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2; //TBC
+    pusch_config_pdu->dmrs_ports = (dci->antenna_ports.val > 3)?(dci->antenna_ports.val-4):(dci->antenna_ports.val); //TBC
+    //pusch_config_pdu->n_front_load_symb = (dci->antenna_ports > 3)?2:1; //FIXME
+  }
+
+  if ((pusch_config_dedicated->transform_precoder == transform_precoder_disabled) &&
+    (pusch_config_dedicated->dmrs_ul_for_pusch_mapping_type_a.dmrs_type == 1) &&
+    (pusch_config_dedicated->dmrs_ul_for_pusch_mapping_type_a.max_length == 1)) { // tables 7.3.1.1.2-8/9/10/11
+
+    if (rank == 1) {
+      pusch_config_pdu->num_dmrs_cdm_grps_no_data = (dci->antenna_ports.val > 1)?2:1; //TBC
+      pusch_config_pdu->dmrs_ports = (dci->antenna_ports.val > 1)?(dci->antenna_ports.val-2):(dci->antenna_ports.val); //TBC
+    }
+
+    if (rank == 2){
+      pusch_config_pdu->num_dmrs_cdm_grps_no_data = (dci->antenna_ports.val > 0)?2:1; //TBC
+      pusch_config_pdu->dmrs_ports = 0; //FIXME
+      //pusch_config_pdu->dmrs_ports[0] = (dci->antenna_ports > 1)?(dci->antenna_ports > 2 ?0:2):0;
+      //pusch_config_pdu->dmrs_ports[1] = (dci->antenna_ports > 1)?(dci->antenna_ports > 2 ?2:3):1;
+    }
+
+    if (rank == 3){
+      pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2; //TBC
+      pusch_config_pdu->dmrs_ports = 0; //FIXME
+      //pusch_config_pdu->dmrs_ports[0] = 0;
+      //pusch_config_pdu->dmrs_ports[1] = 1;
+      //pusch_config_pdu->dmrs_ports[2] = 2;
+    }
+
+    if (rank == 4){
+      pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2; //TBC
+      pusch_config_pdu->dmrs_ports = 0; //FIXME
+      //pusch_config_pdu->dmrs_ports[0] = 0;
+      //pusch_config_pdu->dmrs_ports[1] = 1;
+      //pusch_config_pdu->dmrs_ports[2] = 2;
+      //pusch_config_pdu->dmrs_ports[3] = 3;
+    }
+  }
+
+  if ((pusch_config_dedicated->transform_precoder == transform_precoder_disabled) &&
+    (pusch_config_dedicated->dmrs_ul_for_pusch_mapping_type_a.dmrs_type == 1) &&
+    (pusch_config_dedicated->dmrs_ul_for_pusch_mapping_type_a.max_length == 2)) { // tables 7.3.1.1.2-12/13/14/15
+
+    if (rank == 1){
+      pusch_config_pdu->num_dmrs_cdm_grps_no_data = (dci->antenna_ports.val > 1)?2:1; //TBC
+      pusch_config_pdu->dmrs_ports = (dci->antenna_ports.val > 1)?(dci->antenna_ports.val > 5 ?(dci->antenna_ports.val-6):(dci->antenna_ports.val-2)):dci->antenna_ports.val; //TBC
+      //pusch_config_pdu->n_front_load_symb = (dci->antenna_ports.val > 6)?2:1; //FIXME
+    }
+
+    if (rank == 2){
+      pusch_config_pdu->num_dmrs_cdm_grps_no_data = (dci->antenna_ports.val > 0)?2:1; //TBC
+      pusch_config_pdu->dmrs_ports = 0; //FIXME
+      //pusch_config_pdu->dmrs_ports[0] = table_7_3_1_1_2_13[dci->antenna_ports.val][1];
+      //pusch_config_pdu->dmrs_ports[1] = table_7_3_1_1_2_13[dci->antenna_ports.val][2];
+      //pusch_config_pdu->n_front_load_symb = (dci->antenna_ports.val > 3)?2:1; // FIXME
+    }
+
+    if (rank == 3){
+      pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2; //TBC
+      pusch_config_pdu->dmrs_ports = 0; //FIXME
+      //pusch_config_pdu->dmrs_ports[0] = table_7_3_1_1_2_14[dci->antenna_ports.val][1];
+      //pusch_config_pdu->dmrs_ports[1] = table_7_3_1_1_2_14[dci->antenna_ports.val][2];
+      //pusch_config_pdu->dmrs_ports[2] = table_7_3_1_1_2_14[dci->antenna_ports.val][3];
+      //pusch_config_pdu->n_front_load_symb = (dci->antenna_ports.val > 1)?2:1; //FIXME
+    }
+
+    if (rank == 4){
+      pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2; //TBC
+      pusch_config_pdu->dmrs_ports = 0; //FIXME
+      //pusch_config_pdu->dmrs_ports[0] = table_7_3_1_1_2_15[dci->antenna_ports.val][1];
+      //pusch_config_pdu->dmrs_ports[1] = table_7_3_1_1_2_15[dci->antenna_ports.val][2];
+      //pusch_config_pdu->dmrs_ports[2] = table_7_3_1_1_2_15[dci->antenna_ports.val][3];
+      //pusch_config_pdu->dmrs_ports[3] = table_7_3_1_1_2_15[dci->antenna_ports.val][4];
+      //pusch_config_pdu->n_front_load_symb = (dci->antenna_ports.val > 1)?2:1; //FIXME
+    }
+  }
+
+  if ((pusch_config_dedicated->transform_precoder == transform_precoder_disabled) &&
+    (pusch_config_dedicated->dmrs_ul_for_pusch_mapping_type_a.dmrs_type == 2) &&
+    (pusch_config_dedicated->dmrs_ul_for_pusch_mapping_type_a.max_length == 1)) { // tables 7.3.1.1.2-16/17/18/19
+
+    if (rank == 1){
+      pusch_config_pdu->num_dmrs_cdm_grps_no_data = (dci->antenna_ports.val > 1)?((dci->antenna_ports.val > 5)?3:2):1; //TBC
+      pusch_config_pdu->dmrs_ports = (dci->antenna_ports.val > 1)?(dci->antenna_ports.val > 5 ?(dci->antenna_ports.val-6):(dci->antenna_ports.val-2)):dci->antenna_ports.val; //TBC
+    }
+
+    if (rank == 2){
+      pusch_config_pdu->num_dmrs_cdm_grps_no_data = (dci->antenna_ports.val > 0)?((dci->antenna_ports.val > 2)?3:2):1; //TBC
+      pusch_config_pdu->dmrs_ports = 0; //FIXME
+      //pusch_config_pdu->dmrs_ports[0] = table_7_3_1_1_2_17[dci->antenna_ports.val][1];
+      //pusch_config_pdu->dmrs_ports[1] = table_7_3_1_1_2_17[dci->antenna_ports.val][2];
+    }
+
+    if (rank == 3){
+      pusch_config_pdu->num_dmrs_cdm_grps_no_data = (dci->antenna_ports.val > 0)?3:2; //TBC
+      pusch_config_pdu->dmrs_ports = 0; //FIXME
+      //pusch_config_pdu->dmrs_ports[0] = table_7_3_1_1_2_18[dci->antenna_ports.val][1];
+      //pusch_config_pdu->dmrs_ports[1] = table_7_3_1_1_2_18[dci->antenna_ports.val][2];
+      //pusch_config_pdu->dmrs_ports[2] = table_7_3_1_1_2_18[dci->antenna_ports.val][3];
+    }
+
+    if (rank == 4){
+      pusch_config_pdu->num_dmrs_cdm_grps_no_data = dci->antenna_ports.val + 2; //TBC
+      pusch_config_pdu->dmrs_ports = 0; //FIXME
+      //pusch_config_pdu->dmrs_ports[0] = 0;
+      //pusch_config_pdu->dmrs_ports[1] = 1;
+      //pusch_config_pdu->dmrs_ports[2] = 2;
+      //pusch_config_pdu->dmrs_ports[3] = 3;
+    }
+  }
+
+  if ((pusch_config_dedicated->transform_precoder == transform_precoder_disabled) &&
+    (pusch_config_dedicated->dmrs_ul_for_pusch_mapping_type_a.dmrs_type == 2) &&
+    (pusch_config_dedicated->dmrs_ul_for_pusch_mapping_type_a.max_length == 2)) { // tables 7.3.1.1.2-20/21/22/23
+
+    if (rank == 1){
+      pusch_config_pdu->num_dmrs_cdm_grps_no_data = table_7_3_1_1_2_20[dci->antenna_ports.val][0]; //TBC
+      pusch_config_pdu->dmrs_ports = table_7_3_1_1_2_20[dci->antenna_ports.val][1]; //TBC
+      //pusch_config_pdu->n_front_load_symb = table_7_3_1_1_2_20[dci->antenna_ports.val][2]; //FIXME
+    }
+
+    if (rank == 2){
+      pusch_config_pdu->num_dmrs_cdm_grps_no_data = table_7_3_1_1_2_21[dci->antenna_ports.val][0]; //TBC
+      pusch_config_pdu->dmrs_ports = 0; //FIXME
+      //pusch_config_pdu->dmrs_ports[0] = table_7_3_1_1_2_21[dci->antenna_ports.val][1];
+      //pusch_config_pdu->dmrs_ports[1] = table_7_3_1_1_2_21[dci->antenna_ports.val][2];
+      //pusch_config_pdu->n_front_load_symb = table_7_3_1_1_2_21[dci->antenna_ports.val][3]; //FIXME
+      }
+
+    if (rank == 3){
+      pusch_config_pdu->num_dmrs_cdm_grps_no_data = table_7_3_1_1_2_22[dci->antenna_ports.val][0]; //TBC
+      pusch_config_pdu->dmrs_ports = 0; //FIXME
+      //pusch_config_pdu->dmrs_ports[0] = table_7_3_1_1_2_22[dci->antenna_ports.val][1];
+      //pusch_config_pdu->dmrs_ports[1] = table_7_3_1_1_2_22[dci->antenna_ports.val][2];
+      //pusch_config_pdu->dmrs_ports[2] = table_7_3_1_1_2_22[dci->antenna_ports.val][3];
+      //pusch_config_pdu->n_front_load_symb = table_7_3_1_1_2_22[dci->antenna_ports.val][4]; //FIXME
+    }
+
+    if (rank == 4){
+      pusch_config_pdu->num_dmrs_cdm_grps_no_data = table_7_3_1_1_2_23[dci->antenna_ports.val][0]; //TBC
+      pusch_config_pdu->dmrs_ports = 0; //FIXME
+      //pusch_config_pdu->dmrs_ports[0] = table_7_3_1_1_2_23[dci->antenna_ports.val][1];
+      //pusch_config_pdu->dmrs_ports[1] = table_7_3_1_1_2_23[dci->antenna_ports.val][2];
+      //pusch_config_pdu->dmrs_ports[2] = table_7_3_1_1_2_23[dci->antenna_ports.val][3];
+      //pusch_config_pdu->dmrs_ports[3] = table_7_3_1_1_2_23[dci->antenna_ports.val][4];
+      //pusch_config_pdu->n_front_load_symb = table_7_3_1_1_2_23[dci->antenna_ports.val][5]; //FIXME
+    }
+  }
+}
+
+// Configuration of Msg3 PDU according to clauses:
+// - 8.3 of 3GPP TS 38.213 version 16.3.0 Release 16
+// - 6.1.2.2 of TS 38.214
+// - 6.1.3 of TS 38.214
+// - 6.2.2 of TS 38.214
+// - 6.1.4.2 of TS 38.214
+// - 6.4.1.1.1 of TS 38.211
+// - 6.3.1.7 of 38.211
+int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac,
+                        nfapi_nr_ue_pusch_pdu_t *pusch_config_pdu,
+                        dci_pdu_rel15_t *dci,
+                        RAR_grant_t *rar_grant,
+                        uint16_t rnti,
+                        uint8_t *dci_format){
+
+  int f_alloc;
+  int mask;
+  int StartSymbolIndex;
+  int NrOfSymbols;
+  uint8_t nb_dmrs_re_per_rb;
+
+  uint16_t        l_prime_mask = 1;
+  uint16_t number_dmrs_symbols = 0;
+  int                N_PRB_oh  = 0;
+
+  NR_ServingCellConfigCommon_t *scc = mac->scc;
+  int rnti_type = get_rnti_type(mac, rnti);
+
+  // Common configuration
+  pusch_config_pdu->dmrs_config_type = pusch_dmrs_type1;
+  pusch_config_pdu->pdu_bit_map      = PUSCH_PDU_BITMAP_PUSCH_DATA;
+  pusch_config_pdu->nrOfLayers       = 1;
+  pusch_config_pdu->rnti             = rnti;
+
+  if (rar_grant) {
+
+    // Note: for Msg3 or MsgA PUSCH transmission the N_PRB_oh is always set to 0
+
+    NR_BWP_Uplink_t *ubwp = mac->ULbwp[0];
+    NR_BWP_UplinkDedicated_t *ibwp = mac->scg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP;
+    NR_PUSCH_Config_t *pusch_Config = ibwp->pusch_Config->choice.setup;
+    int startSymbolAndLength = ubwp->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList->list.array[rar_grant->Msg3_t_alloc]->startSymbolAndLength;
+
+    // active BWP start
+    int abwp_start = NRRIV2PRBOFFSET(ubwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE);
+    int abwp_size = NRRIV2BW(ubwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE);
+
+    // initial BWP start
+    int ibwp_start = NRRIV2PRBOFFSET(scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE);
+    int ibwp_size = NRRIV2BW(scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE);
+
+    // BWP start selection according to 8.3 of TS 38.213
+    pusch_config_pdu->bwp_size = ibwp_size;
+    if ((ibwp_start < abwp_start) || (ibwp_size > abwp_size))
+      pusch_config_pdu->bwp_start = abwp_start;
+    else
+      pusch_config_pdu->bwp_start = ibwp_start;
+
+    //// Resource assignment from RAR
+    // Frequency domain allocation according to 8.3 of TS 38.213
+    if (ibwp_size < 180)
+      mask = (1 << ((int) ceil(log2((ibwp_size*(ibwp_size+1))>>1)))) - 1;
+    else
+      mask = (1 << (28 - (int)(ceil(log2((ibwp_size*(ibwp_size+1))>>1))))) - 1;
+    f_alloc = rar_grant->Msg3_f_alloc & mask;
+    nr_ue_process_dci_freq_dom_resource_assignment(pusch_config_pdu, NULL, ibwp_size, 0, f_alloc);
+
+    // virtual resource block to physical resource mapping for Msg3 PUSCH (6.3.1.7 in 38.211)
+    pusch_config_pdu->rb_start += ibwp_start - abwp_start;
+
+    // Time domain allocation
+    SLIV2SL(startSymbolAndLength, &StartSymbolIndex, &NrOfSymbols);
+    pusch_config_pdu->start_symbol_index = StartSymbolIndex;
+    pusch_config_pdu->nr_of_symbols = NrOfSymbols;
+
+    #ifdef DEBUG_MSG3
+    LOG_D(MAC, "In %s BWP assignment (BWP (start %d, size %d) \n", __FUNCTION__, pusch_config_pdu->bwp_start, pusch_config_pdu->bwp_size);
+    #endif
+
+    // MCS
+    pusch_config_pdu->mcs_index = rar_grant->mcs;
+    // Frequency hopping
+    pusch_config_pdu->frequency_hopping = rar_grant->freq_hopping;
+
+    // DM-RS configuration according to 6.2.2 UE DM-RS transmission procedure in 38.214
+    pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2;
+    pusch_config_pdu->dmrs_ports = 1;
+
+    // DMRS sequence initialization [TS 38.211, sec 6.4.1.1.1].
+    // Should match what is sent in DCI 0_1, otherwise set to 0.
+    pusch_config_pdu->scid = 0;
+
+    // Transform precoding according to 6.1.3 UE procedure for applying transform precoding on PUSCH in 38.214
+    pusch_config_pdu->transform_precoding = get_transformPrecoding(scc, pusch_Config, NULL, NULL, NR_RNTI_RA, 0); // TBR fix rnti and take out
+
+    // Resource allocation in frequency domain according to 6.1.2.2 in TS 38.214
+    pusch_config_pdu->resource_alloc = pusch_Config->resourceAllocation;
+
+    //// Completing PUSCH PDU
+    pusch_config_pdu->mcs_table = 0;
+    pusch_config_pdu->cyclic_prefix = 0;
+    pusch_config_pdu->data_scrambling_id = *scc->physCellId;
+    pusch_config_pdu->ul_dmrs_scrambling_id = *scc->physCellId;
+    pusch_config_pdu->subcarrier_spacing = ubwp->bwp_Common->genericParameters.subcarrierSpacing;
+    pusch_config_pdu->vrb_to_prb_mapping = 0;
+    pusch_config_pdu->uplink_frequency_shift_7p5khz = 0;
+    //Optional Data only included if indicated in pduBitmap
+    pusch_config_pdu->pusch_data.rv_index = 0;  // 8.3 in 38.213
+    pusch_config_pdu->pusch_data.harq_process_id = 0;
+    pusch_config_pdu->pusch_data.new_data_indicator = 1; // new data
+    pusch_config_pdu->pusch_data.num_cb = 0;
+
+  } else if (dci) {
+
+    int target_ss;
+    uint8_t  ptrs_time_density;
+    uint8_t  ptrs_freq_density;
+    nfapi_nr_ue_ptrs_ports_t ptrs_ports_list;
+    uint16_t n_RB_ULBWP = NRRIV2BW(mac->ULbwp[0]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE);
+    fapi_nr_pusch_config_dedicated_t *pusch_config_dedicated = &mac->phy_config.config_req.ul_bwp_dedicated.pusch_config_dedicated;
+    NR_PUSCH_Config_t *pusch_Config = mac->ULbwp[0]->bwp_Dedicated->pusch_Config->choice.setup;
+
+    // These should come from RRC config!!!
+    uint8_t  ptrs_mcs1          = 2;
+    uint8_t  ptrs_mcs2          = 4;
+    uint8_t  ptrs_mcs3          = 10;
+    uint16_t n_rb0              = 25;
+    uint16_t n_rb1              = 75;
+
+    /* Transform precoding */
+    if (rnti_type != NR_RNTI_CS || (rnti_type == NR_RNTI_CS && dci->ndi == 1)) {
+      pusch_config_pdu->transform_precoding = get_transformPrecoding(scc, pusch_Config, NULL, dci_format, rnti_type, 0);
+    }
+
+    /*DCI format-related configuration*/
+    if (*dci_format == NR_UL_DCI_FORMAT_0_0) {
+
+      target_ss = NR_SearchSpace__searchSpaceType_PR_common;
+
+    } else if (*dci_format == NR_UL_DCI_FORMAT_0_1) {
+
+      /* BANDWIDTH_PART_IND */
+      config_bwp_ue(mac, &dci->bwp_indicator.val, dci_format);
+      target_ss = NR_SearchSpace__searchSpaceType_PR_ue_Specific;
+      ul_layers_config(mac, pusch_config_pdu, dci);
+      ul_ports_config(mac, pusch_config_pdu, dci);
+
+    } else {
+
+      LOG_E(MAC, "In %s: UL grant from DCI format %d is not handled...\n", __FUNCTION__, *dci_format);
+      return -1;
+
+    }
+
+    /* IDENTIFIER_DCI_FORMATS */
+    /* FREQ_DOM_RESOURCE_ASSIGNMENT_UL */
+    if (nr_ue_process_dci_freq_dom_resource_assignment(pusch_config_pdu, NULL, n_RB_ULBWP, 0, dci->frequency_domain_assignment.val) < 0){
+      return -1;
+    }
+    /* TIME_DOM_RESOURCE_ASSIGNMENT */
+    if (nr_ue_process_dci_time_dom_resource_assignment(mac, pusch_config_pdu, NULL, dci->time_domain_assignment.val) < 0) {
+      return -1;
+    }
+
+    /* FREQ_HOPPING_FLAG */
+    if ((pusch_config_dedicated->resource_allocation != 0) && (pusch_config_dedicated->frequency_hopping !=0)){
+      pusch_config_pdu->frequency_hopping = dci->frequency_hopping_flag.val;
+    }
+
+    /* MCS */
+    pusch_config_pdu->mcs_index = dci->mcs;
+
+    /* MCS TABLE */
+    if (pusch_config_pdu->transform_precoding == transform_precoder_disabled) {
+      pusch_config_pdu->mcs_table = get_pusch_mcs_table(pusch_Config->mcs_Table, 0, *dci_format, rnti_type, target_ss, false);
+    } else {
+      pusch_config_pdu->mcs_table = get_pusch_mcs_table(pusch_Config->mcs_TableTransformPrecoder, 1, *dci_format, rnti_type, target_ss, false);
+    }
+
+    /* NDI */
+    pusch_config_pdu->pusch_data.new_data_indicator = dci->ndi;
+    /* RV */
+    pusch_config_pdu->pusch_data.rv_index = dci->rv;
+    /* HARQ_PROCESS_NUMBER */
+    pusch_config_pdu->pusch_data.harq_process_id = dci->harq_pid;
+    /* TPC_PUSCH */
+    // according to TS 38.213 Table Table 7.1.1-1
+    if (dci->tpc == 0) {
+      pusch_config_pdu->absolute_delta_PUSCH = -4;
+    }
+    if (dci->tpc == 1) {
+      pusch_config_pdu->absolute_delta_PUSCH = -1;
+    }
+    if (dci->tpc == 2) {
+      pusch_config_pdu->absolute_delta_PUSCH = 1;
+    }
+    if (dci->tpc == 3) {
+      pusch_config_pdu->absolute_delta_PUSCH = 4;
+    }
+
+    ptrs_time_density  = get_L_ptrs(ptrs_mcs1, ptrs_mcs2, ptrs_mcs3, pusch_config_pdu->mcs_index, pusch_config_pdu->mcs_table);
+    ptrs_freq_density  = get_K_ptrs(n_rb0, n_rb1, pusch_config_pdu->rb_size);
+    // PTRS ports configuration
+    // TbD: ptrs_dmrs_port and ptrs_port_index are not initialised!
+    ptrs_ports_list.ptrs_re_offset = 0;
+
+    /* DMRS */
+    l_prime_mask = get_l_prime(pusch_config_pdu->nr_of_symbols, typeB, pusch_dmrs_pos0, pusch_len1);
+    pusch_config_pdu->num_dmrs_cdm_grps_no_data = 1;
+
+    // Num PRB Overhead from PUSCH-ServingCellConfig
+    if (mac->scg->spCellConfig->spCellConfigDedicated->uplinkConfig->pusch_ServingCellConfig->choice.setup->xOverhead == NULL) {
+      N_PRB_oh = 0;
+    } else {
+      N_PRB_oh = *mac->scg->spCellConfig->spCellConfigDedicated->uplinkConfig->pusch_ServingCellConfig->choice.setup->xOverhead;
+    }
+
+    /* PTRS */
+    pusch_config_pdu->pusch_ptrs.ptrs_time_density = ptrs_time_density;
+    pusch_config_pdu->pusch_ptrs.ptrs_freq_density = ptrs_freq_density;
+    pusch_config_pdu->pusch_ptrs.ptrs_ports_list   = &ptrs_ports_list;
+
+    if (1 << pusch_config_pdu->pusch_ptrs.ptrs_time_density >= pusch_config_pdu->nr_of_symbols) {
+      pusch_config_pdu->pdu_bit_map &= ~PUSCH_PDU_BITMAP_PUSCH_PTRS; // disable PUSCH PTRS
+    }
+
+  }
+
+  LOG_D(MAC, "In %s: received UL grant (rb_start %d, rb_size %d, start_symbol_index %d, nr_of_symbols %d) for RNTI type %s \n",
+    __FUNCTION__,
+    pusch_config_pdu->rb_start,
+    pusch_config_pdu->rb_size,
+    pusch_config_pdu->start_symbol_index,
+    pusch_config_pdu->nr_of_symbols,
+    rnti_types[rnti_type]);
+
+  pusch_config_pdu->ul_dmrs_symb_pos = l_prime_mask << pusch_config_pdu->start_symbol_index;;
+  pusch_config_pdu->target_code_rate = nr_get_code_rate_ul(pusch_config_pdu->mcs_index, pusch_config_pdu->mcs_table);
+  pusch_config_pdu->qam_mod_order = nr_get_Qm_ul(pusch_config_pdu->mcs_index, pusch_config_pdu->mcs_table);
+
+  if (pusch_config_pdu->target_code_rate == 0 || pusch_config_pdu->qam_mod_order == 0) {
+    LOG_W(MAC, "In %s: Invalid code rate or Mod order, likely due to unexpected UL DCI. Ignoring DCI! \n", __FUNCTION__);
+    return -1;
+  }
+
+  get_num_re_dmrs(pusch_config_pdu, &nb_dmrs_re_per_rb, &number_dmrs_symbols);
+
+  // Compute TBS
+  pusch_config_pdu->pusch_data.tb_size = nr_compute_tbs(pusch_config_pdu->qam_mod_order,
+                                                        pusch_config_pdu->target_code_rate,
+                                                        pusch_config_pdu->rb_size,
+                                                        pusch_config_pdu->nr_of_symbols,
+                                                        nb_dmrs_re_per_rb*number_dmrs_symbols,
+                                                        N_PRB_oh,
+                                                        0, // TBR to verify tb scaling
+                                                        pusch_config_pdu->nrOfLayers)/8;
+
+  return 0;
+
+}
+
+// Performs :
+// 1. TODO: Call RRC for link status return to PHY
+// 2. TODO: Perform SR/BSR procedures for scheduling feedback
+// 3. TODO: Perform PHR procedures
+NR_UE_L2_STATE_t nr_ue_scheduler(nr_downlink_indication_t *dl_info, nr_uplink_indication_t *ul_info){
+
+  uint32_t search_space_mask = 0;
+
+  if (dl_info){
+
+    module_id_t mod_id    = dl_info->module_id;
+    uint32_t gNB_index    = dl_info->gNB_index;
+    int cc_id             = dl_info->cc_id;
+    frame_t rx_frame      = dl_info->frame;
+    slot_t rx_slot        = dl_info->slot;
+    NR_UE_MAC_INST_t *mac = get_mac_inst(mod_id);
+
+    fapi_nr_dl_config_request_t *dl_config = &mac->dl_config_request;
+    nr_scheduled_response_t scheduled_response;
+    nr_dcireq_t dcireq;
+
+    // check type0 from 38.213 13 if we have no CellGroupConfig
+    // TODO: implementation to be completed
+    if (mac->scg == NULL) {
+
+      if(dl_info->ssb_index != -1){
+
+        if(mac->type0_pdcch_ss_mux_pattern == 1){
+          //  38.213 chapter 13
+          if((mac->type0_pdcch_ss_sfn_c == SFN_C_MOD_2_EQ_0) && !(rx_frame & 0x1) && (rx_slot == mac->type0_pdcch_ss_n_c)){
+            search_space_mask = search_space_mask | type0_pdcch;
+            mac->type0_pdcch_consecutive_slots = mac->type0_pdcch_dci_config.coreset.duration;
+          }
+          if((mac->type0_pdcch_ss_sfn_c == SFN_C_MOD_2_EQ_1) && (rx_frame & 0x1) && (rx_slot == mac->type0_pdcch_ss_n_c)){
+            search_space_mask = search_space_mask | type0_pdcch;
+            mac->type0_pdcch_consecutive_slots = mac->type0_pdcch_dci_config.coreset.duration;
+          }
+        }
+        if(mac->type0_pdcch_ss_mux_pattern == 2){
+          //  38.213 Table 13-13, 13-14
+          if((rx_frame == get_ssb_frame(rx_frame)) && (rx_slot == mac->type0_pdcch_ss_n_c)){
+            search_space_mask = search_space_mask | type0_pdcch;
+            mac->type0_pdcch_consecutive_slots = mac->type0_pdcch_dci_config.coreset.duration;
+          }
+        }
+        if(mac->type0_pdcch_ss_mux_pattern == 3){
+          //  38.213 Table 13-15
+          if((rx_frame == get_ssb_frame(rx_frame)) && (rx_slot == mac->type0_pdcch_ss_n_c)){
+            search_space_mask = search_space_mask | type0_pdcch;
+            mac->type0_pdcch_consecutive_slots = mac->type0_pdcch_dci_config.coreset.duration;
+          }
+        }
+      } // ssb_index != -1
+
+      // Type0 PDCCH search space
+      if((search_space_mask & type0_pdcch) || ( mac->type0_pdcch_consecutive_slots != 0 )){
+        mac->type0_pdcch_consecutive_slots = mac->type0_pdcch_consecutive_slots - 1;
+
+        dl_config->dl_config_list[dl_config->number_pdus].dci_config_pdu.dci_config_rel15 = mac->type0_pdcch_dci_config;
+        dl_config->dl_config_list[dl_config->number_pdus].pdu_type = FAPI_NR_DL_CONFIG_TYPE_DCI;
+
+        /*
+        dl_config->dl_config_list[dl_config->number_pdus].dci_config_pdu.dci_config_rel15.rnti = 0xaaaa;        //      to be set
+        dl_config->dl_config_list[dl_config->number_pdus].dci_config_pdu.dci_config_rel15.N_RB_BWP = 106;       //      to be set
+
+        LOG_I(MAC,"nr_ue_scheduler Type0 PDCCH with rnti %x, BWP %d\n",
+        dl_config->dl_config_list[dl_config->number_pdus].dci_config_pdu.dci_config_rel15.rnti,
+        dl_config->dl_config_list[dl_config->number_pdus].dci_config_pdu.dci_config_rel15.N_RB_BWP);
+        */
+        dl_config->number_pdus = dl_config->number_pdus + 1;
+
+        fill_scheduled_response(&scheduled_response, dl_config, NULL, NULL, mod_id, cc_id, rx_frame, rx_slot, dl_info->thread_id);
+        if(mac->if_module != NULL && mac->if_module->scheduled_response != NULL)
+          mac->if_module->scheduled_response(&scheduled_response);
+      }
+    } else { // we have an scg
+
+      dcireq.module_id = mod_id;
+      dcireq.gNB_index = gNB_index;
+      dcireq.cc_id     = cc_id;
+      dcireq.frame     = rx_frame;
+      dcireq.slot      = rx_slot;
+      dcireq.dl_config_req.number_pdus = 0;
+      nr_ue_dcireq(&dcireq); //to be replaced with function pointer later
+
+      fill_scheduled_response(&scheduled_response, &dcireq.dl_config_req, NULL, NULL, mod_id, cc_id, rx_frame, rx_slot, dl_info->thread_id);
+      if(mac->if_module != NULL && mac->if_module->scheduled_response != NULL){
+        mac->if_module->scheduled_response(&scheduled_response);
+      }
+
+      /*
+        if(search_space_mask & type0a_pdcch){
+        }
+        
+        if(search_space_mask & type1_pdcch){
+        }
+
+        if(search_space_mask & type2_pdcch){
+        }
+
+        if(search_space_mask & type3_pdcch){
+        }
+      */
+    }
+  } else if (ul_info) {
+
+    int cc_id             = ul_info->cc_id;
+    frame_t rx_frame      = ul_info->frame_rx;
+    slot_t rx_slot        = ul_info->slot_rx;
+    frame_t frame_tx      = ul_info->frame_tx;
+    slot_t slot_tx        = ul_info->slot_tx;
+    module_id_t mod_id    = ul_info->module_id;
+    uint8_t access_mode   = SCHEDULED_ACCESS;
+
+    NR_UE_MAC_INST_t *mac = get_mac_inst(mod_id);
+    RA_config_t *ra       = &mac->ra;
+
+    fapi_nr_ul_config_request_t *ul_config = get_ul_config_request(mac, slot_tx);
+
+    // Schedule ULSCH only if the current frame and slot match those in ul_config_req
+    // AND if a UL grant (UL DCI or Msg3) has been received (as indicated by num_pdus)
+    if ((ul_info->slot_tx == ul_config->slot && ul_info->frame_tx == ul_config->sfn) && ul_config->number_pdus > 0){
+
+      LOG_D(MAC, "In %s:[%d.%d]: number of UL PDUs: %d with UL transmission in [%d.%d]\n", __FUNCTION__, frame_tx, slot_tx, ul_config->number_pdus, ul_config->sfn, ul_config->slot);
+
+      uint8_t ulsch_input_buffer[MAX_ULSCH_PAYLOAD_BYTES];
+      uint8_t data_existing = 0;
+      nr_scheduled_response_t scheduled_response;
+      fapi_nr_tx_request_t tx_req;
+
+      for (int j = 0; j < ul_config->number_pdus; j++) {
+
+        fapi_nr_ul_config_request_pdu_t *ulcfg_pdu = &ul_config->ul_config_list[j];
+
+        if (ulcfg_pdu->pdu_type == FAPI_NR_UL_CONFIG_TYPE_PUSCH) {
+
+          uint16_t TBS_bytes = ulcfg_pdu->pusch_config_pdu.pusch_data.tb_size;
+
+          if (IS_SOFTMODEM_NOS1){
+            // Getting IP traffic to be transmitted
+            data_existing = nr_ue_get_sdu(mod_id,
+                                          cc_id,
+                                          frame_tx,
+                                          slot_tx,
+                                          0,
+                                          ulsch_input_buffer,
+                                          TBS_bytes,
+                                          &access_mode);
+          }
+
+          //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
+            //and block this traffic from being forwarded to the upper layers at the gNB
+            LOG_D(PHY, "In %s: Random data to be transmitted: TBS_bytes %d \n", __FUNCTION__, TBS_bytes);
+
+            //Give the first byte a dummy value (a value not corresponding to any valid LCID based on 38.321, Table 6.2.1-2)
+            //in order to distinguish the PHY random packets at the MAC layer of the gNB receiver from the normal packets that should
+            //have a valid LCID (nr_process_mac_pdu function)
+            ulsch_input_buffer[0] = 0x31;
+
+            for (int i = 1; i < TBS_bytes; i++) {
+              ulsch_input_buffer[i] = (unsigned char) rand();
+              //printf(" input encoder a[%d]=0x%02x\n",i,harq_process_ul_ue->a[i]);
+            }
+          }
+
+          #ifdef DEBUG_MAC_PDU
+          LOG_D(PHY, "Is data existing ?: %d \n", data_existing);
+          LOG_I(PHY, "Printing MAC PDU to be encoded, TBS is: %d \n", TBS_bytes);
+          for (i = 0; i < TBS_bytes; i++) {
+            printf("%02x", ulsch_input_buffer[i]);
+          }
+          printf("\n");
+          #endif
+
+          // Config UL TX PDU
+          tx_req.slot = slot_tx;
+          tx_req.sfn = frame_tx;
+          tx_req.number_of_pdus++;
+          tx_req.tx_request_body[0].pdu_length = TBS_bytes;
+          tx_req.tx_request_body[0].pdu_index = j;
+          tx_req.tx_request_body[0].pdu = ulsch_input_buffer;
+
+          if (ra->ra_state != RA_SUCCEEDED && !ra->cfra){
+            nr_Msg3_transmitted(ul_info->module_id, ul_info->cc_id, ul_info->frame_tx, ul_info->gNB_index);
+          }
+
+        }
+      }
+
+      fill_scheduled_response(&scheduled_response, NULL, ul_config, &tx_req, mod_id, cc_id, rx_frame, rx_slot, ul_info->thread_id);
+      if(mac->if_module != NULL && mac->if_module->scheduled_response != NULL){
+        mac->if_module->scheduled_response(&scheduled_response);
+      }
+    }
+  }
+
+  return UE_CONNECTION_OK;
+
+}
+
+// PUSCH scheduler:
+// - Calculate the slot in which ULSCH should be scheduled. This is current slot + K2,
+// - where K2 is the offset between the slot in which UL DCI is received and the slot
+// - in which ULSCH should be scheduled. K2 is configured in RRC configuration.  
+// PUSCH Msg3 scheduler:
+// - scheduled by RAR UL grant according to 8.3 of TS 38.213
+// Note: Msg3 tx in the uplink symbols of mixed slot
+int nr_ue_pusch_scheduler(NR_UE_MAC_INST_t *mac,
+                          uint8_t is_Msg3,
+                          frame_t current_frame,
+                          int current_slot,
+                          frame_t *frame_tx,
+                          int *slot_tx,
+                          uint8_t tda_id){
+
+  int delta = 0;
+  NR_BWP_Uplink_t *ubwp = mac->ULbwp[0];
+  // Get the numerology to calculate the Tx frame and slot
+  int mu = ubwp->bwp_Common->genericParameters.subcarrierSpacing;
+  struct NR_PUSCH_TimeDomainResourceAllocationList *pusch_TimeDomainAllocationList = ubwp->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList;
+  // k2 as per 3GPP TS 38.214 version 15.9.0 Release 15 ch 6.1.2.1.1
+  // PUSCH time domain resource allocation is higher layer configured from uschTimeDomainAllocationList in either pusch-ConfigCommon
+  uint8_t k2;
+
+  if (is_Msg3) {
+    k2 = *pusch_TimeDomainAllocationList->list.array[tda_id]->k2;
+
+    switch (mu) {
+      case 0:
+        delta = 2;
+        break;
+      case 1:
+        delta = 3;
+        break;
+      case 2:
+        delta = 4;
+        break;
+      case 3:
+        delta = 6;
+        break;
+    }
+
+    *slot_tx = (current_slot + k2 + delta) % nr_slots_per_frame[mu];
+    if (current_slot + k2 + delta > nr_slots_per_frame[mu]){
+      *frame_tx = (current_frame + 1) % 1024;
+    } else {
+      *frame_tx = current_frame;
+    }
+
+  } else {
+
+    // Get slot offset K2 which will be used to calculate TX slot
+    k2 = get_k2(mac, tda_id);
+    if (k2 < 0) { // This can happen when a false DCI is received
+      return -1;
+    }
+
+    // Calculate TX slot and frame
+    *slot_tx = (current_slot + k2) % nr_slots_per_frame[mu];
+    *frame_tx = ((current_slot + k2) > nr_slots_per_frame[mu]) ? (current_frame + 1) % 1024 : current_frame;
+
+  }
+
+  LOG_D(MAC, "In %s: currently at [%d.%d] UL transmission in [%d.%d] (k2 %d delta %d)\n", __FUNCTION__, current_frame, current_slot, *frame_tx, *slot_tx, k2, delta);
+
+  return 0;
+
+}
+
+// Build the list of all the valid RACH occasions in the maximum association pattern period according to the PRACH config
+static void build_ro_list(NR_ServingCellConfigCommon_t *scc, uint8_t unpaired) {
+
+  int x,y; // PRACH Configuration Index table variables used to compute the valid frame numbers
+  int y2;  // PRACH Configuration Index table additional variable used to compute the valid frame numbers
+  uint8_t slot_shift_for_map;
+  uint8_t map_shift;
+  boolean_t even_slot_invalid;
+  int64_t s_map;
+  uint8_t prach_conf_start_symbol; // Starting symbol of the PRACH occasions in the PRACH slot
+  uint8_t N_t_slot; // Number of PRACH occasions in a 14-symbols PRACH slot
+  uint8_t N_dur; // Duration of a PRACH occasion (nb of symbols)
+  uint8_t frame; // Maximum is NB_FRAMES_IN_MAX_ASSOCIATION_PATTERN_PERIOD
+  uint8_t slot; // Maximum is the number of slots in a frame @ SCS 240kHz
+  uint16_t format = 0xffff;
+  uint8_t format2 = 0xff;
+  int nb_fdm;
+
+  uint8_t config_index, mu;
+  uint32_t pointa;
+  int msg1_FDM;
+
+  uint8_t prach_conf_period_idx;
+  uint8_t nb_of_frames_per_prach_conf_period;
+  uint8_t prach_conf_period_frame_idx;
+  int64_t *prach_config_info_p;
+
+  NR_RACH_ConfigCommon_t *setup = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup;
+  NR_FrequencyInfoDL_t *frequencyInfoDL = scc->downlinkConfigCommon->frequencyInfoDL;
+  NR_RACH_ConfigGeneric_t *rach_ConfigGeneric = &setup->rach_ConfigGeneric;
+
+  config_index = rach_ConfigGeneric->prach_ConfigurationIndex;
+
+  if (setup->msg1_SubcarrierSpacing)
+    mu = *setup->msg1_SubcarrierSpacing;
+  else
+    mu = frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing;
+
+  pointa = frequencyInfoDL->absoluteFrequencyPointA;
+  msg1_FDM = rach_ConfigGeneric->msg1_FDM;
+
+  switch (msg1_FDM){
+    case 0:
+    case 1:
+    case 2:
+    case 3:
+      nb_fdm = 1 << msg1_FDM;
+      break;
+    default:
+      AssertFatal(1 == 0, "Unknown msg1_FDM from rach_ConfigGeneric %d\n", msg1_FDM);
+  }
+
+  // Create the PRACH occasions map
+  // ==============================
+  // WIP: For now assume no rejected PRACH occasions because of conflict with SSB or TDD_UL_DL_ConfigurationCommon schedule
+
+  // Identify the proper PRACH Configuration Index table according to the operating frequency
+  LOG_D(MAC,"Pointa %u, mu = %u, PRACH config index  = %u, unpaired = %u\n", pointa, mu, config_index, unpaired);
+
+  prach_config_info_p = get_prach_config_info(pointa, config_index, unpaired);
+
+  if (pointa > 2016666) { //FR2
+
+    x = prach_config_info_p[2];
+    y = prach_config_info_p[3];
+    y2 = prach_config_info_p[4];
+
+    s_map = prach_config_info_p[5];
+
+    prach_conf_start_symbol = prach_config_info_p[6];
+    N_t_slot = prach_config_info_p[8];
+    N_dur = prach_config_info_p[9];
+    if (prach_config_info_p[1] != -1)
+      format2 = (uint8_t) prach_config_info_p[1];
+    format = ((uint8_t) prach_config_info_p[0]) | (format2<<8);
+
+    slot_shift_for_map = mu-2;
+    if ( (mu == 3) && (prach_config_info_p[7] == 1) )
+      even_slot_invalid = true;
+    else
+      even_slot_invalid = false;
+  }
+  else { // FR1
+    x = prach_config_info_p[2];
+    y = prach_config_info_p[3];
+    y2 = y;
+
+    s_map = prach_config_info_p[4];
+
+    prach_conf_start_symbol = prach_config_info_p[5];
+    N_t_slot = prach_config_info_p[7];
+    N_dur = prach_config_info_p[8];
+    if (prach_config_info_p[1] != -1)
+      format2 = (uint8_t) prach_config_info_p[1];
+    format = ((uint8_t) prach_config_info_p[0]) | (format2<<8);
+
+    slot_shift_for_map = mu;
+    if ( (mu == 1) && (prach_config_info_p[6] <= 1) )
+      // no prach in even slots @ 30kHz for 1 prach per subframe
+      even_slot_invalid = true;
+    else
+      even_slot_invalid = false;
+  } // FR2 / FR1
+
+  prach_assoc_pattern.nb_of_prach_conf_period_in_max_period = MAX_NB_PRACH_CONF_PERIOD_IN_ASSOCIATION_PATTERN_PERIOD / x;
+  nb_of_frames_per_prach_conf_period = x;
+
+  LOG_D(MAC,"nb_of_prach_conf_period_in_max_period %d\n", prach_assoc_pattern.nb_of_prach_conf_period_in_max_period);
+
+  // Fill in the PRACH occasions table for every slot in every frame in every PRACH configuration periods in the maximum association pattern period
+  // ----------------------------------------------------------------------------------------------------------------------------------------------
+  // ----------------------------------------------------------------------------------------------------------------------------------------------
+  // For every PRACH configuration periods
+  // -------------------------------------
+  for (prach_conf_period_idx=0; prach_conf_period_idx<prach_assoc_pattern.nb_of_prach_conf_period_in_max_period; prach_conf_period_idx++) {
+    prach_assoc_pattern.prach_conf_period_list[prach_conf_period_idx].nb_of_prach_occasion = 0;
+    prach_assoc_pattern.prach_conf_period_list[prach_conf_period_idx].nb_of_frame = nb_of_frames_per_prach_conf_period;
+    prach_assoc_pattern.prach_conf_period_list[prach_conf_period_idx].nb_of_slot = nr_slots_per_frame[mu];
+
+    LOG_D(MAC,"PRACH Conf Period Idx %d\n", prach_conf_period_idx);
+
+    // For every frames in a PRACH configuration period
+    // ------------------------------------------------
+    for (prach_conf_period_frame_idx=0; prach_conf_period_frame_idx<nb_of_frames_per_prach_conf_period; prach_conf_period_frame_idx++) {
+      frame = (prach_conf_period_idx * nb_of_frames_per_prach_conf_period) + prach_conf_period_frame_idx;
+
+      LOG_D(MAC,"PRACH Conf Period Frame Idx %d - Frame %d\n", prach_conf_period_frame_idx, frame);
+      // Is it a valid frame for this PRACH configuration index? (n_sfn mod x = y)
+      if ( (frame%x)==y || (frame%x)==y2 ) {
+
+        // For every slot in a frame
+        // -------------------------
+        for (slot=0; slot<nr_slots_per_frame[mu]; slot++) {
+          // Is it a valid slot?
+          map_shift = slot >> slot_shift_for_map; // in PRACH configuration index table slots are numbered wrt 60kHz
+          if ( (s_map>>map_shift)&0x01 ) {
+            // Valid slot
+
+            // Additionally, for 30kHz/120kHz, we must check for the n_RA_Slot param also
+            if ( even_slot_invalid && (slot%2 == 0) )
+                continue; // no prach in even slots @ 30kHz/120kHz for 1 prach per 60khz slot/subframe
+
+            // We're good: valid frame and valid slot
+            // Compute all the PRACH occasions in the slot
+
+            uint8_t n_prach_occ_in_time;
+            uint8_t n_prach_occ_in_freq;
+
+            prach_assoc_pattern.prach_conf_period_list[prach_conf_period_idx].prach_occasion_slot_map[prach_conf_period_frame_idx][slot].nb_of_prach_occasion_in_time = N_t_slot;
+            prach_assoc_pattern.prach_conf_period_list[prach_conf_period_idx].prach_occasion_slot_map[prach_conf_period_frame_idx][slot].nb_of_prach_occasion_in_freq = nb_fdm;
+
+            for (n_prach_occ_in_time=0; n_prach_occ_in_time<N_t_slot; n_prach_occ_in_time++) {
+              uint8_t start_symbol = prach_conf_start_symbol + n_prach_occ_in_time * N_dur;
+              LOG_D(MAC,"PRACH Occ in time %d\n", n_prach_occ_in_time);
+
+              for (n_prach_occ_in_freq=0; n_prach_occ_in_freq<nb_fdm; n_prach_occ_in_freq++) {
+                prach_occasion_info_t *prach_occasion_p = &prach_assoc_pattern.prach_conf_period_list[prach_conf_period_idx].prach_occasion_slot_map[prach_conf_period_frame_idx][slot].prach_occasion[n_prach_occ_in_time][n_prach_occ_in_freq];
+
+                prach_occasion_p->start_symbol = start_symbol;
+                prach_occasion_p->fdm = n_prach_occ_in_freq;
+                prach_occasion_p->frame = frame;
+                prach_occasion_p->slot = slot;
+                prach_occasion_p->format = format;
+                prach_assoc_pattern.prach_conf_period_list[prach_conf_period_idx].nb_of_prach_occasion++;
+
+                LOG_D(MAC,"Adding a PRACH occasion: frame %u, slot-symbol %d-%d, occ_in_time-occ_in-freq %d-%d, nb ROs in conf period %d, for this slot: RO# in time %d, RO# in freq %d\n",
+                    frame, slot, start_symbol, n_prach_occ_in_time, n_prach_occ_in_freq, prach_assoc_pattern.prach_conf_period_list[prach_conf_period_idx].nb_of_prach_occasion,
+                    prach_assoc_pattern.prach_conf_period_list[prach_conf_period_idx].prach_occasion_slot_map[prach_conf_period_frame_idx][slot].nb_of_prach_occasion_in_time,
+                    prach_assoc_pattern.prach_conf_period_list[prach_conf_period_idx].prach_occasion_slot_map[prach_conf_period_frame_idx][slot].nb_of_prach_occasion_in_freq);
+              } // For every freq in the slot
+            } // For every time occasions in the slot
+          } // Valid slot?
+        } // For every slots in a frame
+      } // Valid frame?
+    } // For every frames in a prach configuration period
+  } // For every prach configuration periods in the maximum association pattern period (160ms)
+}
+
+// Build the list of all the valid/transmitted SSBs according to the config
+static void build_ssb_list(NR_ServingCellConfigCommon_t *scc) {
+
+  // Create the list of transmitted SSBs
+  // ===================================
+  BIT_STRING_t *ssb_bitmap;
+  uint64_t ssb_positionsInBurst;
+  uint8_t ssb_idx = 0;
+
+  switch (scc->ssb_PositionsInBurst->present) {
+    case NR_ServingCellConfigCommon__ssb_PositionsInBurst_PR_shortBitmap:
+      ssb_bitmap = &scc->ssb_PositionsInBurst->choice.shortBitmap;
+
+      ssb_positionsInBurst = BIT_STRING_to_uint8(ssb_bitmap);
+      LOG_D(MAC,"SSB config: SSB_positions_in_burst 0x%lx\n", ssb_positionsInBurst);
+
+      for (uint8_t bit_nb=3; bit_nb<=3; bit_nb--) {
+        // If SSB is transmitted
+        if ((ssb_positionsInBurst>>bit_nb) & 0x01) {
+          ssb_list.nb_tx_ssb++;
+          ssb_list.tx_ssb[ssb_idx].transmitted = true;
+          LOG_D(MAC,"SSB idx %d transmitted\n", ssb_idx);
+        }
+        ssb_idx++;
+      }
+      break;
+    case NR_ServingCellConfigCommon__ssb_PositionsInBurst_PR_mediumBitmap:
+      ssb_bitmap = &scc->ssb_PositionsInBurst->choice.mediumBitmap;
+
+      ssb_positionsInBurst = BIT_STRING_to_uint8(ssb_bitmap);
+      LOG_D(MAC,"SSB config: SSB_positions_in_burst 0x%lx\n", ssb_positionsInBurst);
+
+      for (uint8_t bit_nb=7; bit_nb<=7; bit_nb--) {
+        // If SSB is transmitted
+        if ((ssb_positionsInBurst>>bit_nb) & 0x01) {
+          ssb_list.nb_tx_ssb++;
+          ssb_list.tx_ssb[ssb_idx].transmitted = true;
+          LOG_D(MAC,"SSB idx %d transmitted\n", ssb_idx);
+        }
+        ssb_idx++;
+      }
+      break;
+    case NR_ServingCellConfigCommon__ssb_PositionsInBurst_PR_longBitmap:
+      ssb_bitmap = &scc->ssb_PositionsInBurst->choice.longBitmap;
+
+      ssb_positionsInBurst = BIT_STRING_to_uint64(ssb_bitmap);
+      LOG_D(MAC,"SSB config: SSB_positions_in_burst 0x%lx\n", ssb_positionsInBurst);
+
+      for (uint8_t bit_nb=63; bit_nb<=63; bit_nb--) {
+        // If SSB is transmitted
+        if ((ssb_positionsInBurst>>bit_nb) & 0x01) {
+          ssb_list.nb_tx_ssb++;
+          ssb_list.tx_ssb[ssb_idx].transmitted = true;
+          LOG_D(MAC,"SSB idx %d transmitted\n", ssb_idx);
+        }
+        ssb_idx++;
+      }
+      break;
+    default:
+      AssertFatal(false,"ssb_PositionsInBurst not present\n");
+      break;
+  }
+}
+
+// Map the transmitted SSBs to the ROs and create the association pattern according to the config
+static void map_ssb_to_ro(NR_ServingCellConfigCommon_t *scc) {
+
+  // Map SSBs to PRACH occasions
+  // ===========================
+  // WIP: Assumption: No PRACH occasion is rejected because of a conflict with SSBs or TDD_UL_DL_ConfigurationCommon schedule
+  NR_RACH_ConfigCommon_t *setup = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup;
+  NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR ssb_perRACH_config = setup->ssb_perRACH_OccasionAndCB_PreamblesPerSSB->present;
+
+  boolean_t multiple_ssb_per_ro; // true if more than one or exactly one SSB per RACH occasion, false if more than one RO per SSB
+  uint8_t ssb_rach_ratio; // Nb of SSBs per RACH or RACHs per SSB
+  uint16_t required_nb_of_prach_occasion; // Nb of RACH occasions required to map all the SSBs
+  uint8_t required_nb_of_prach_conf_period; // Nb of PRACH configuration periods required to map all the SSBs
+
+  // Determine the SSB to RACH mapping ratio
+  // =======================================
+  switch (ssb_perRACH_config){
+    case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_oneEighth:
+      multiple_ssb_per_ro = false;
+      ssb_rach_ratio = 8;
+      break;
+    case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_oneFourth:
+      multiple_ssb_per_ro = false;
+      ssb_rach_ratio = 4;
+      break;
+    case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_oneHalf:
+      multiple_ssb_per_ro = false;
+      ssb_rach_ratio = 2;
+      break;
+    case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_one:
+      multiple_ssb_per_ro = true;
+      ssb_rach_ratio = 1;
+      break;
+    case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_two:
+      multiple_ssb_per_ro = true;
+      ssb_rach_ratio = 2;
+      break;
+    case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_four:
+      multiple_ssb_per_ro = true;
+      ssb_rach_ratio = 4;
+      break;
+    case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_eight:
+      multiple_ssb_per_ro = true;
+      ssb_rach_ratio = 8;
+      break;
+    case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_sixteen:
+      multiple_ssb_per_ro = true;
+      ssb_rach_ratio = 16;
+      break;
+    default:
+      AssertFatal(1 == 0, "Unsupported ssb_perRACH_config %d\n", ssb_perRACH_config);
+      break;
+  }
+  LOG_D(MAC,"SSB rach ratio %d, Multiple SSB per RO %d\n", ssb_rach_ratio, multiple_ssb_per_ro);
+
+  // Evaluate the number of PRACH configuration periods required to map all the SSBs and set the association period
+  // ==============================================================================================================
+  // WIP: Assumption for now is that all the PRACH configuration periods within a maximum association pattern period have the same number of PRACH occasions
+  //      (No PRACH occasions are conflicting with SSBs nor TDD_UL_DL_ConfigurationCommon schedule)
+  //      There is only one possible association period which can contain up to 16 PRACH configuration periods
+  LOG_D(MAC,"Evaluate the number of PRACH configuration periods required to map all the SSBs and set the association period\n");
+  if (true == multiple_ssb_per_ro) {
+    required_nb_of_prach_occasion = ((ssb_list.nb_tx_ssb-1) + ssb_rach_ratio) / ssb_rach_ratio;
+  }
+  else {
+    required_nb_of_prach_occasion = ssb_list.nb_tx_ssb * ssb_rach_ratio;
+  }
+
+  required_nb_of_prach_conf_period = ((required_nb_of_prach_occasion-1) + prach_assoc_pattern.prach_conf_period_list[0].nb_of_prach_occasion) / prach_assoc_pattern.prach_conf_period_list[0].nb_of_prach_occasion;
+
+  if (required_nb_of_prach_conf_period == 1) {
+    prach_assoc_pattern.prach_association_period_list[0].nb_of_prach_conf_period = 1;
+  }
+  else if (required_nb_of_prach_conf_period == 2) {
+    prach_assoc_pattern.prach_association_period_list[0].nb_of_prach_conf_period = 2;
+  }
+  else if (required_nb_of_prach_conf_period <= 4) {
+    prach_assoc_pattern.prach_association_period_list[0].nb_of_prach_conf_period = 4;
+  }
+  else if (required_nb_of_prach_conf_period <= 8) {
+    prach_assoc_pattern.prach_association_period_list[0].nb_of_prach_conf_period = 8;
+  }
+  else if (required_nb_of_prach_conf_period <= 16) {
+    prach_assoc_pattern.prach_association_period_list[0].nb_of_prach_conf_period = 16;
+  }
+  else {
+    AssertFatal(1 == 0, "Invalid number of PRACH config periods within an association period %d\n", required_nb_of_prach_conf_period);
+  }
+
+  prach_assoc_pattern.nb_of_assoc_period = 1; // WIP: only one possible association period
+  prach_assoc_pattern.prach_association_period_list[0].nb_of_frame = prach_assoc_pattern.prach_association_period_list[0].nb_of_prach_conf_period * prach_assoc_pattern.prach_conf_period_list[0].nb_of_frame;
+  prach_assoc_pattern.nb_of_frame = prach_assoc_pattern.prach_association_period_list[0].nb_of_frame;
+
+  LOG_D(MAC,"Assoc period %d, Nb of frames in assoc period %d\n",
+        prach_assoc_pattern.prach_association_period_list[0].nb_of_prach_conf_period,
+        prach_assoc_pattern.prach_association_period_list[0].nb_of_frame);
+
+  // Proceed to the SSB to RO mapping
+  // ================================
+  uint8_t association_period_idx; // Association period index within the association pattern
+  uint8_t ssb_idx = 0;
+  uint8_t prach_configuration_period_idx; // PRACH Configuration period index within the association pattern
+  prach_conf_period_t *prach_conf_period_p;
+
+  // Map all the association periods within the association pattern period
+  LOG_D(MAC,"Proceed to the SSB to RO mapping\n");
+  for (association_period_idx=0; association_period_idx<prach_assoc_pattern.nb_of_assoc_period; association_period_idx++) {
+    uint8_t n_prach_conf=0; // PRACH Configuration period index within the association period
+    uint8_t frame=0;
+    uint8_t slot=0;
+    uint8_t ro_in_time=0;
+    uint8_t ro_in_freq=0;
+
+    // Set the starting PRACH Configuration period index in the association_pattern map for this particular association period
+    prach_configuration_period_idx = 0;  // WIP: only one possible association period so the starting PRACH configuration period is automatically 0
+
+    // Check if we need to map multiple SSBs per RO or multiple ROs per SSB
+    if (true == multiple_ssb_per_ro) {
+      // --------------------
+      // --------------------
+      // Multiple SSBs per RO
+      // --------------------
+      // --------------------
+
+      // WIP: For the moment, only map each SSB idx once per association period if configuration is multiple SSBs per RO
+      //      this is true if no PRACH occasions are conflicting with SSBs nor TDD_UL_DL_ConfigurationCommon schedule
+      ssb_idx = 0;
+
+      // Go through the list of PRACH config periods within this association period
+      for (n_prach_conf=0; n_prach_conf<prach_assoc_pattern.prach_association_period_list[association_period_idx].nb_of_prach_conf_period; n_prach_conf++, prach_configuration_period_idx++) {
+        // Build the association period with its association PRACH Configuration indexes
+        prach_conf_period_p = &prach_assoc_pattern.prach_conf_period_list[prach_configuration_period_idx];
+        prach_assoc_pattern.prach_association_period_list[association_period_idx].prach_conf_period_list[n_prach_conf] = prach_conf_period_p;
+
+        // Go through all the ROs within the PRACH config period
+        for (frame=0; frame<prach_conf_period_p->nb_of_frame; frame++) {
+          for (slot=0; slot<prach_conf_period_p->nb_of_slot; slot++) {
+            for (ro_in_time=0; ro_in_time<prach_conf_period_p->prach_occasion_slot_map[frame][slot].nb_of_prach_occasion_in_time; ro_in_time++) {
+              for (ro_in_freq=0; ro_in_freq<prach_conf_period_p->prach_occasion_slot_map[frame][slot].nb_of_prach_occasion_in_freq; ro_in_freq++) {
+                prach_occasion_info_t *ro_p = &prach_conf_period_p->prach_occasion_slot_map[frame][slot].prach_occasion[ro_in_time][ro_in_freq];
+
+                // Go through the list of transmitted SSBs and map the required amount of SSBs to this RO
+                // WIP: For the moment, only map each SSB idx once per association period if configuration is multiple SSBs per RO
+                //      this is true if no PRACH occasions are conflicting with SSBs nor TDD_UL_DL_ConfigurationCommon schedule
+                for (; ssb_idx<MAX_NB_SSB; ssb_idx++) {
+                  // Map only the transmitted ssb_idx
+                  if (true == ssb_list.tx_ssb[ssb_idx].transmitted) {
+                    ro_p->mapped_ssb_idx[ro_p->nb_mapped_ssb] = ssb_idx;
+                    ro_p->nb_mapped_ssb++;
+                    ssb_list.tx_ssb[ssb_idx].mapped_ro[ssb_list.tx_ssb[ssb_idx].nb_mapped_ro] = ro_p;
+                    ssb_list.tx_ssb[ssb_idx].nb_mapped_ro++;
+                    AssertFatal(MAX_NB_RO_PER_SSB_IN_ASSOCIATION_PATTERN > ssb_list.tx_ssb[ssb_idx].nb_mapped_ro,"Too many mapped ROs (%d) to a single SSB\n", ssb_list.tx_ssb[ssb_idx].nb_mapped_ro);
+
+                    LOG_D(MAC,"Mapped ssb_idx %u to RO slot-symbol %u-%u, %u-%u-%u/%u\n", ssb_idx, ro_p->slot, ro_p->start_symbol, slot, ro_in_time, ro_in_freq, prach_conf_period_p->prach_occasion_slot_map[frame][slot].nb_of_prach_occasion_in_freq);
+                    LOG_D(MAC,"Nb mapped ROs for this ssb idx: in the association period only %u\n", ssb_list.tx_ssb[ssb_idx].nb_mapped_ro);
+
+                    // If all the required SSBs are mapped to this RO, exit the loop of SSBs
+                    if (ro_p->nb_mapped_ssb == ssb_rach_ratio) {
+                      ssb_idx++;
+                      break;
+                    }
+                  } // if ssb_idx is transmitted
+                } // for ssb_idx
+
+                // Exit the loop of ROs if there is no more SSB to map
+                if (MAX_NB_SSB == ssb_idx) break;
+              } // for ro_in_freq
+
+              // Exit the loop of ROs if there is no more SSB to map
+              if (MAX_NB_SSB == ssb_idx) break;
+            } // for ro_in_time
+
+            // Exit the loop of slots if there is no more SSB to map
+            if (MAX_NB_SSB == ssb_idx) break;
+          } // for slot
+
+          // Exit the loop frames if there is no more SSB to map
+          if (MAX_NB_SSB == ssb_idx) break;
+        } // for frame
+
+        // Exit the loop of PRACH configurations if there is no more SSB to map
+        if (MAX_NB_SSB == ssb_idx) break;
+      } // for n_prach_conf
+
+      // WIP: note that there is no re-mapping of the SSBs within the association period since there is no invalid ROs in the PRACH config periods that would create this situation
+
+    } // if multiple_ssbs_per_ro
+
+    else {
+      // --------------------
+      // --------------------
+      // Multiple ROs per SSB
+      // --------------------
+      // --------------------
+
+      n_prach_conf = 0;
+
+      // Go through the list of transmitted SSBs
+      for (ssb_idx=0; ssb_idx<MAX_NB_SSB; ssb_idx++) {
+        uint8_t nb_mapped_ro_in_association_period=0; // Reset the nb of mapped ROs for the new SSB index
+
+        // Map only the transmitted ssb_idx
+        if (true == ssb_list.tx_ssb[ssb_idx].transmitted) {
+
+          // Map all the required ROs to this SSB
+          // Go through the list of PRACH config periods within this association period
+          for (; n_prach_conf<prach_assoc_pattern.prach_association_period_list[association_period_idx].nb_of_prach_conf_period; n_prach_conf++, prach_configuration_period_idx++) {
+
+            // Build the association period with its association PRACH Configuration indexes
+            prach_conf_period_p = &prach_assoc_pattern.prach_conf_period_list[prach_configuration_period_idx];
+            prach_assoc_pattern.prach_association_period_list[association_period_idx].prach_conf_period_list[n_prach_conf] = prach_conf_period_p;
+
+            for (; frame<prach_conf_period_p->nb_of_frame; frame++) {
+              for (; slot<prach_conf_period_p->nb_of_slot; slot++) {
+                for (; ro_in_time<prach_conf_period_p->prach_occasion_slot_map[frame][slot].nb_of_prach_occasion_in_time; ro_in_time++) {
+                  for (; ro_in_freq<prach_conf_period_p->prach_occasion_slot_map[frame][slot].nb_of_prach_occasion_in_freq; ro_in_freq++) {
+                    prach_occasion_info_t *ro_p = &prach_conf_period_p->prach_occasion_slot_map[frame][slot].prach_occasion[ro_in_time][ro_in_freq];
+
+                    ro_p->mapped_ssb_idx[0] = ssb_idx;
+                    ro_p->nb_mapped_ssb = 1;
+                    ssb_list.tx_ssb[ssb_idx].mapped_ro[ssb_list.tx_ssb[ssb_idx].nb_mapped_ro] = ro_p;
+                    ssb_list.tx_ssb[ssb_idx].nb_mapped_ro++;
+                    AssertFatal(MAX_NB_RO_PER_SSB_IN_ASSOCIATION_PATTERN > ssb_list.tx_ssb[ssb_idx].nb_mapped_ro,"Too many mapped ROs (%d) to a single SSB\n", ssb_list.tx_ssb[ssb_idx].nb_mapped_ro);
+                    nb_mapped_ro_in_association_period++;
+
+                    LOG_D(MAC,"Mapped ssb_idx %u to RO slot-symbol %u-%u, %u-%u-%u/%u\n", ssb_idx, ro_p->slot, ro_p->start_symbol, slot, ro_in_time, ro_in_freq, prach_conf_period_p->prach_occasion_slot_map[frame][slot].nb_of_prach_occasion_in_freq);
+                    LOG_D(MAC,"Nb mapped ROs for this ssb idx: in the association period only %u / total %u\n", ssb_list.tx_ssb[ssb_idx].nb_mapped_ro, nb_mapped_ro_in_association_period);
+
+                    // Exit the loop if this SSB has been mapped to all the required ROs
+                    // WIP: Assuming that ssb_rach_ratio equals the maximum nb of times a given ssb_idx is mapped within an association period:
+                    //      this is true if no PRACH occasions are conflicting with SSBs nor TDD_UL_DL_ConfigurationCommon schedule
+                    if (nb_mapped_ro_in_association_period == ssb_rach_ratio) {
+                      ro_in_freq++;
+                      break;
+                    }
+                  } // for ro_in_freq
+
+                  // Exit the loop if this SSB has been mapped to all the required ROs
+                  if (nb_mapped_ro_in_association_period == ssb_rach_ratio) {
+                    break;
+                  }
+                  else ro_in_freq = 0; // else go to the next time symbol in that slot and reset the freq index
+                } // for ro_in_time
+
+                // Exit the loop if this SSB has been mapped to all the required ROs
+                if (nb_mapped_ro_in_association_period == ssb_rach_ratio) {
+                  break;
+                }
+                else ro_in_time = 0; // else go to the next slot in that PRACH config period and reset the symbol index
+              } // for slot
+
+              // Exit the loop if this SSB has been mapped to all the required ROs
+              if (nb_mapped_ro_in_association_period == ssb_rach_ratio) {
+                break;
+              }
+              else slot = 0; // else go to the next frame in that PRACH config period and reset the slot index
+            } // for frame
+
+            // Exit the loop if this SSB has been mapped to all the required ROs
+            if (nb_mapped_ro_in_association_period == ssb_rach_ratio) {
+              break;
+            }
+            else frame = 0; // else go to the next PRACH config period in that association period and reset the frame index
+          } // for n_prach_conf
+
+        } // if ssb_idx is transmitted
+      } // for ssb_idx
+    } // else if multiple_ssbs_per_ro
+
+  } // for association_period_index
+}
+
+// Returns a RACH occasion if any matches the SSB idx, the frame and the slot
+static int get_nr_prach_info_from_ssb_index(uint8_t ssb_idx,
+                                            int frame,
+                                            int slot,
+                                            prach_occasion_info_t **prach_occasion_info_pp) {
+
+  ssb_info_t *ssb_info_p;
+  prach_occasion_slot_t *prach_occasion_slot_p = NULL;
+
+  *prach_occasion_info_pp = NULL;
+
+  // Search for a matching RO slot in the SSB_to_RO map
+  // A valid RO slot will match:
+  //      - ssb_idx mapped to one of the ROs in that RO slot
+  //      - exact slot number
+  //      - frame offset
+  ssb_info_p = &ssb_list.tx_ssb[ssb_idx];
+  for (uint8_t n_mapped_ro=0; n_mapped_ro<ssb_info_p->nb_mapped_ro; n_mapped_ro++) {
+    if ((slot == ssb_info_p->mapped_ro[n_mapped_ro]->slot) &&
+        (ssb_info_p->mapped_ro[n_mapped_ro]->frame == (frame % prach_assoc_pattern.nb_of_frame))) {
+
+      uint8_t prach_config_period_nb = ssb_info_p->mapped_ro[n_mapped_ro]->frame / prach_assoc_pattern.prach_conf_period_list[0].nb_of_frame;
+      uint8_t frame_nb_in_prach_config_period = ssb_info_p->mapped_ro[n_mapped_ro]->frame % prach_assoc_pattern.prach_conf_period_list[0].nb_of_frame;
+      prach_occasion_slot_p = &prach_assoc_pattern.prach_conf_period_list[prach_config_period_nb].prach_occasion_slot_map[frame_nb_in_prach_config_period][slot];
+    }
+  }
+
+  // If there is a matching RO slot in the SSB_to_RO map
+  if (NULL != prach_occasion_slot_p)
+  {
+    // A random RO mapped to the SSB index should be selected in the slot
+
+    // First count the number of times the SSB index is found in that RO
+    uint8_t nb_mapped_ssb = 0;
+
+    for (int ro_in_time=0; ro_in_time < prach_occasion_slot_p->nb_of_prach_occasion_in_time; ro_in_time++) {
+      for (int ro_in_freq=0; ro_in_freq < prach_occasion_slot_p->nb_of_prach_occasion_in_freq; ro_in_freq++) {
+        prach_occasion_info_t *prach_occasion_info_p = &prach_occasion_slot_p->prach_occasion[ro_in_time][ro_in_freq];
+
+        for (uint8_t ssb_nb=0; ssb_nb<prach_occasion_info_p->nb_mapped_ssb; ssb_nb++) {
+          if (prach_occasion_info_p->mapped_ssb_idx[ssb_nb] == ssb_idx) {
+            nb_mapped_ssb++;
+          }
+        }
+      }
+    }
+
+    // Choose a random SSB nb
+    uint8_t random_ssb_nb = 0;
+
+    random_ssb_nb = ((taus()) % nb_mapped_ssb);
+
+    // Select the RO according to the chosen random SSB nb
+    nb_mapped_ssb=0;
+    for (int ro_in_time=0; ro_in_time < prach_occasion_slot_p->nb_of_prach_occasion_in_time; ro_in_time++) {
+      for (int ro_in_freq=0; ro_in_freq < prach_occasion_slot_p->nb_of_prach_occasion_in_freq; ro_in_freq++) {
+        prach_occasion_info_t *prach_occasion_info_p = &prach_occasion_slot_p->prach_occasion[ro_in_time][ro_in_freq];
+
+        for (uint8_t ssb_nb=0; ssb_nb<prach_occasion_info_p->nb_mapped_ssb; ssb_nb++) {
+          if (prach_occasion_info_p->mapped_ssb_idx[ssb_nb] == ssb_idx) {
+            if (nb_mapped_ssb == random_ssb_nb) {
+              *prach_occasion_info_pp = prach_occasion_info_p;
+              return 1;
+            }
+            else {
+              nb_mapped_ssb++;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  return 0;
+}
+
+// Build the SSB to RO mapping upon RRC configuration update
+void build_ssb_to_ro_map(NR_ServingCellConfigCommon_t *scc, uint8_t unpaired){
+
+  // Clear all the lists and maps
+  memset(&prach_assoc_pattern, 0, sizeof(prach_association_pattern_t));
+  memset(&ssb_list, 0, sizeof(ssb_list_info_t));
+
+  // Build the list of all the valid RACH occasions in the maximum association pattern period according to the PRACH config
+  LOG_D(MAC,"Build RO list\n");
+  build_ro_list(scc, unpaired);
+
+  // Build the list of all the valid/transmitted SSBs according to the config
+  LOG_D(MAC,"Build SSB list\n");
+  build_ssb_list(scc);
+
+  // Map the transmitted SSBs to the ROs and create the association pattern according to the config
+  LOG_D(MAC,"Map SSB to RO\n");
+  map_ssb_to_ro(scc);
+  LOG_D(MAC,"Map SSB to RO done\n");
+}
+
+// This function schedules the PRACH according to prach_ConfigurationIndex and TS 38.211, tables 6.3.3.2.x
+// PRACH formats 9, 10, 11 are corresponding to dual PRACH format configurations A1/B1, A2/B2, A3/B3.
+// - todo:
+// - Partial configuration is actually already stored in (fapi_nr_prach_config_t) &mac->phy_config.config_req->prach_config
+void nr_ue_prach_scheduler(module_id_t module_idP, frame_t frameP, sub_frame_t slotP, int thread_id) {
+
+  uint16_t format, format0, format1, ncs;
+  int is_nr_prach_slot;
+  prach_occasion_info_t *prach_occasion_info_p;
+
+  NR_UE_MAC_INST_t *mac = get_mac_inst(module_idP);
+  RA_config_t *ra = &mac->ra;
+
+  //fapi_nr_ul_config_request_t *ul_config = get_ul_config_request(mac, slotP);
+  fapi_nr_ul_config_request_t *ul_config = &mac->ul_config_request[0];
+  fapi_nr_ul_config_prach_pdu *prach_config_pdu;
+  fapi_nr_config_request_t *cfg = &mac->phy_config.config_req;
+  fapi_nr_prach_config_t *prach_config = &cfg->prach_config;
+  nr_scheduled_response_t scheduled_response;
+
+  NR_ServingCellConfigCommon_t *scc = mac->scc;
+  NR_RACH_ConfigCommon_t *setup = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup;
+  NR_RACH_ConfigGeneric_t *rach_ConfigGeneric = &setup->rach_ConfigGeneric;
+
+  ra->RA_offset = 2; // to compensate the rx frame offset at the gNB
+  ra->generate_nr_prach = 0; // Reset flag for PRACH generation
+
+  if (is_nr_UL_slot(scc, slotP)) {
+
+    // WIP Need to get the proper selected ssb_idx
+    //     Initial beam selection functionality is not available yet
+    uint8_t selected_gnb_ssb_idx = 0;
+
+    // Get any valid PRACH occasion in the current slot for the selected SSB index
+    is_nr_prach_slot = get_nr_prach_info_from_ssb_index(selected_gnb_ssb_idx,
+                                                       (int)frameP,
+                                                       (int)slotP,
+                                                        &prach_occasion_info_p);
+
+    if (is_nr_prach_slot && ra->ra_state == RA_UE_IDLE) {
+      AssertFatal(NULL != prach_occasion_info_p,"PRACH Occasion Info not returned in a valid NR Prach Slot\n");
+
+      ra->generate_nr_prach = 1;
+
+      format = prach_occasion_info_p->format;
+      format0 = format & 0xff;        // single PRACH format
+      format1 = (format >> 8) & 0xff; // dual PRACH format
+
+      ul_config->sfn = frameP;
+      ul_config->slot = slotP;
+
+      ul_config->ul_config_list[ul_config->number_pdus].pdu_type = FAPI_NR_UL_CONFIG_TYPE_PRACH;
+      prach_config_pdu = &ul_config->ul_config_list[ul_config->number_pdus].prach_config_pdu;
+      memset(prach_config_pdu, 0, sizeof(fapi_nr_ul_config_prach_pdu));
+      ul_config->number_pdus += 1;
+      LOG_D(PHY, "In %s: (%p) %d UL PDUs:\n", __FUNCTION__, ul_config, ul_config->number_pdus);
+
+      ncs = get_NCS(rach_ConfigGeneric->zeroCorrelationZoneConfig, format0, setup->restrictedSetConfig);
+
+      prach_config_pdu->phys_cell_id = *scc->physCellId;
+      prach_config_pdu->num_prach_ocas = 1;
+      prach_config_pdu->prach_slot = prach_occasion_info_p->slot;
+      prach_config_pdu->prach_start_symbol = prach_occasion_info_p->start_symbol;
+      prach_config_pdu->num_ra = prach_occasion_info_p->fdm;
+
+      prach_config_pdu->num_cs = ncs;
+      prach_config_pdu->root_seq_id = prach_config->num_prach_fd_occasions_list[prach_occasion_info_p->fdm].prach_root_sequence_index;
+      prach_config_pdu->restricted_set = prach_config->restricted_set_config;
+      prach_config_pdu->freq_msg1 = prach_config->num_prach_fd_occasions_list[prach_occasion_info_p->fdm].k1;
+
+      LOG_D(MAC,"Selected RO Frame %u, Slot %u, Symbol %u, Fdm %u\n", frameP, prach_config_pdu->prach_slot, prach_config_pdu->prach_start_symbol, prach_config_pdu->num_ra);
+
+      // Search which SSB is mapped in the RO (among all the SSBs mapped to this RO)
+      for (prach_config_pdu->ssb_nb_in_ro=0; prach_config_pdu->ssb_nb_in_ro<prach_occasion_info_p->nb_mapped_ssb; prach_config_pdu->ssb_nb_in_ro++) {
+        if (prach_occasion_info_p->mapped_ssb_idx[prach_config_pdu->ssb_nb_in_ro] == selected_gnb_ssb_idx)
+          break;
+      }
+      AssertFatal(prach_config_pdu->ssb_nb_in_ro<prach_occasion_info_p->nb_mapped_ssb, "%u not found in the mapped SSBs to the PRACH occasion", selected_gnb_ssb_idx);
+
+      if (format1 != 0xff) {
+        switch(format0) { // dual PRACH format
+          case 0xa1:
+            prach_config_pdu->prach_format = 11;
+            break;
+          case 0xa2:
+            prach_config_pdu->prach_format = 12;
+            break;
+          case 0xa3:
+            prach_config_pdu->prach_format = 13;
+            break;
+        default:
+          AssertFatal(1 == 0, "Only formats A1/B1 A2/B2 A3/B3 are valid for dual format");
+        }
+      } else {
+        switch(format0) { // single PRACH format
+          case 0:
+            prach_config_pdu->prach_format = 0;
+            break;
+          case 1:
+            prach_config_pdu->prach_format = 1;
+            break;
+          case 2:
+            prach_config_pdu->prach_format = 2;
+            break;
+          case 3:
+            prach_config_pdu->prach_format = 3;
+            break;
+          case 0xa1:
+            prach_config_pdu->prach_format = 4;
+            break;
+          case 0xa2:
+            prach_config_pdu->prach_format = 5;
+            break;
+          case 0xa3:
+            prach_config_pdu->prach_format = 6;
+            break;
+          case 0xb1:
+            prach_config_pdu->prach_format = 7;
+            break;
+          case 0xb4:
+            prach_config_pdu->prach_format = 8;
+            break;
+          case 0xc0:
+            prach_config_pdu->prach_format = 9;
+            break;
+          case 0xc2:
+            prach_config_pdu->prach_format = 10;
+            break;
+          default:
+            AssertFatal(1 == 0, "Invalid PRACH format");
+        }
+      } // if format1
+    } // is_nr_prach_slot
+
+    fill_scheduled_response(&scheduled_response, NULL, ul_config, NULL, module_idP, 0 /*TBR fix*/, frameP, slotP, thread_id);
+    if(mac->if_module != NULL && mac->if_module->scheduled_response != NULL)
+      mac->if_module->scheduled_response(&scheduled_response);
+  } // if is_nr_UL_slot
+}
+
+uint8_t
+nr_ue_get_sdu(module_id_t module_idP, int CC_id, frame_t frameP,
+           sub_frame_t subframe, uint8_t eNB_index,
+           uint8_t *ulsch_buffer, uint16_t buflen, uint8_t *access_mode) {
+  uint8_t total_rlc_pdu_header_len = 0;
+  int16_t buflen_remain = 0;
+  uint8_t lcid = 0;
+  uint16_t sdu_lengths[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+  uint8_t sdu_lcids[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+  uint16_t payload_offset = 0, num_sdus = 0;
+  uint8_t ulsch_sdus[MAX_ULSCH_PAYLOAD_BYTES];
+  uint16_t sdu_length_total = 0;
+  //unsigned short post_padding = 0;
+  NR_UE_MAC_INST_t *mac = get_mac_inst(module_idP);
+
+  rlc_buffer_occupancy_t lcid_buffer_occupancy_old =
+    0, lcid_buffer_occupancy_new = 0;
+  LOG_D(MAC,
+        "[UE %d] MAC PROCESS UL TRANSPORT BLOCK at frame%d subframe %d TBS=%d\n",
+        module_idP, frameP, subframe, buflen);
+  AssertFatal(CC_id == 0,
+              "Transmission on secondary CCs is not supported yet\n");
+
+  // Check for DCCH first
+  // TO DO: Multiplex in the order defined by the logical channel prioritization
+  for (lcid = UL_SCH_LCID_SRB1;
+       lcid < NR_MAX_NUM_LCID; lcid++) {
+
+      lcid_buffer_occupancy_old = mac_rlc_get_buffer_occupancy_ind(module_idP, mac->crnti, eNB_index, frameP, subframe, ENB_FLAG_NO, lcid);
+      lcid_buffer_occupancy_new = lcid_buffer_occupancy_old;
+
+      if(lcid_buffer_occupancy_new){
+
+        buflen_remain =
+          buflen - (total_rlc_pdu_header_len + sdu_length_total + MAX_RLC_SDU_SUBHEADER_SIZE);
+        LOG_D(MAC,
+              "[UE %d] Frame %d : UL-DXCH -> ULSCH, RLC %d has %d bytes to "
+              "send (Transport Block size %d SDU Length Total %d , mac header len %d, buflen_remain %d )\n", //BSR byte before Tx=%d
+              module_idP, frameP, lcid, lcid_buffer_occupancy_new,
+              buflen, sdu_length_total,
+              total_rlc_pdu_header_len, buflen_remain); // ,nr_ue_mac_inst->scheduling_info.BSR_bytes[nr_ue_mac_inst->scheduling_info.LCGID[lcid]]
+
+        while(buflen_remain > 0 && lcid_buffer_occupancy_new){
+
+        sdu_lengths[num_sdus] = mac_rlc_data_req(module_idP,
+                                mac->crnti,
+                                eNB_index,
+                                frameP,
+                                ENB_FLAG_NO,
+                                MBMS_FLAG_NO,
+                                lcid,
+                                buflen_remain,
+                                (char *)&ulsch_sdus[sdu_length_total],0,
+                                0);
+
+        AssertFatal(buflen_remain >= sdu_lengths[num_sdus],
+                    "LCID=%d RLC has segmented %d bytes but MAC has max=%d\n",
+                    lcid, sdu_lengths[num_sdus], buflen_remain);
+
+        if (sdu_lengths[num_sdus]) {
+          sdu_length_total += sdu_lengths[num_sdus];
+          sdu_lcids[num_sdus] = lcid;
+
+          total_rlc_pdu_header_len += MAX_RLC_SDU_SUBHEADER_SIZE; //rlc_pdu_header_len_last;
+
+          //Update number of SDU
+          num_sdus++;
+        }
+
+        /* Get updated BO after multiplexing this PDU */
+        lcid_buffer_occupancy_new = mac_rlc_get_buffer_occupancy_ind(module_idP,
+                                                                     mac->crnti,
+                                                                     eNB_index,
+                                                                     frameP,
+                                                                     subframe,
+                                                                     ENB_FLAG_NO,
+                                                                     lcid);
+        buflen_remain =
+                  buflen - (total_rlc_pdu_header_len + sdu_length_total + MAX_RLC_SDU_SUBHEADER_SIZE);
+        }
+  }
+
+}
+
+  // Generate ULSCH PDU
+  if (num_sdus>0) {
+  payload_offset = nr_generate_ulsch_pdu(ulsch_sdus,
+                                         ulsch_buffer,  // mac header
+                                         num_sdus,  // num sdus
+                                         sdu_lengths, // sdu length
+                                         sdu_lcids, // sdu lcid
+                                         0, // power_headroom
+                                         mac->crnti, // crnti
+                                         0, // truncated_bsr
+                                         0, // short_bsr
+                                         0, // long_bsr
+                                         0, // post_padding 
+                                         buflen);  // TBS in bytes
+  }
+  else
+          return 0;
+
+  // Padding: fill remainder of ULSCH with 0
+  if (buflen - payload_offset > 0){
+          for (int j = payload_offset; j < buflen; j++)
+                  ulsch_buffer[j] = 0;
+  }
+
+#if defined(ENABLE_MAC_PAYLOAD_DEBUG)
+  LOG_I(MAC, "Printing UL MAC payload UE side, payload_offset: %d \n", payload_offset);
+  for (int i = 0; i < buflen ; i++) {
+          //harq_process_ul_ue->a[i] = (unsigned char) rand();
+          //printf("a[%d]=0x%02x\n",i,harq_process_ul_ue->a[i]);
+          printf("%02x ",(unsigned char)ulsch_buffer[i]);
+  }
+  printf("\n");
+#endif
+
+  return 1;
+}
diff --git a/openair2/LAYER2/NR_MAC_UE/rar_tools_nrUE.c b/openair2/LAYER2/NR_MAC_UE/rar_tools_nrUE.c
deleted file mode 100644
index 577ba475afdb5eca2cfbf273b0f2d2a0b7f3e285..0000000000000000000000000000000000000000
--- a/openair2/LAYER2/NR_MAC_UE/rar_tools_nrUE.c
+++ /dev/null
@@ -1,776 +0,0 @@
-/*
- * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The OpenAirInterface Software Alliance licenses this file to You under
- * the OAI Public License, Version 1.1  (the "License"); you may not use this file
- * except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.openairinterface.org/?page_id=698
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *-------------------------------------------------------------------------------
- * For more information about the OpenAirInterface (OAI) Software Alliance:
- *      contact@openairinterface.org
- */
-
-/*! \file rar_tools_nrUE.c
- * \brief RA tools for NR UE
- * \author Guido Casati
- * \date 2019
- * \version 1.0
- * @ingroup _mac
-
- */
-
-/* Sim */
-#include "SIMULATION/TOOLS/sim.h"
-
-/* Utils */
-#include "common/utils/LOG/log.h"
-#include "OCG.h"
-#include "OCG_extern.h"
-#include "UTIL/OPT/opt.h"
-
-/* Common */
-#include "common/ran_context.h"
-
-/* PHY */
-#include "openair1/SCHED_NR_UE/defs.h"
-
-/* MAC */
-#include "NR_MAC_UE/mac_proto.h"
-#include "NR_MAC_UE/mac_extern.h"
-#include "NR_MAC_COMMON/nr_mac_extern.h"
-#include <common/utils/nr/nr_common.h>
-
-// #define DEBUG_RAR
-// #define DEBUG_MSG3
-
-void ul_layers_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_config_pdu, dci_pdu_rel15_t *dci) {
-
-  fapi_nr_pusch_config_dedicated_t *pusch_config_dedicated = &mac->phy_config.config_req.ul_bwp_dedicated.pusch_config_dedicated;
-
-  /* PRECOD_NBR_LAYERS */
-  if ((pusch_config_dedicated->tx_config == tx_config_nonCodebook));
-  // 0 bits if the higher layer parameter txConfig = nonCodeBook
-
-  if ((pusch_config_dedicated->tx_config == tx_config_codebook)){
-
-    uint8_t n_antenna_port = 0; //FIXME!!!
-
-    if (n_antenna_port == 1); // 1 antenna port and the higher layer parameter txConfig = codebook 0 bits
-
-    if (n_antenna_port == 4){ // 4 antenna port and the higher layer parameter txConfig = codebook
-
-      // Table 7.3.1.1.2-2: transformPrecoder=disabled and maxRank = 2 or 3 or 4
-      if ((pusch_config_dedicated->transform_precoder == transform_precoder_disabled)
-        && ((pusch_config_dedicated->max_rank == 2) ||
-        (pusch_config_dedicated->max_rank == 3) ||
-        (pusch_config_dedicated->max_rank == 4))){
-
-        if (pusch_config_dedicated->codebook_subset == codebook_subset_fullyAndPartialAndNonCoherent) {
-          pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][0];
-          pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][1];
-        }
-
-        if (pusch_config_dedicated->codebook_subset == codebook_subset_partialAndNonCoherent){
-          pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][2];
-          pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][3];
-        }
-
-        if (pusch_config_dedicated->codebook_subset == codebook_subset_nonCoherent){
-          pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][4];
-          pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][5];
-        }
-      }
-
-      // Table 7.3.1.1.2-3: transformPrecoder= enabled, or transformPrecoder=disabled and maxRank = 1
-      if (((pusch_config_dedicated->transform_precoder == transform_precoder_enabled)
-        || (pusch_config_dedicated->transform_precoder == transform_precoder_disabled))
-        && (pusch_config_dedicated->max_rank == 1)){
-
-        if (pusch_config_dedicated->codebook_subset == codebook_subset_fullyAndPartialAndNonCoherent) {
-          pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][6];
-          pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][7];
-        }
-
-        if (pusch_config_dedicated->codebook_subset == codebook_subset_partialAndNonCoherent){
-          pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][8];
-          pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][9];
-        }
-
-        if (pusch_config_dedicated->codebook_subset == codebook_subset_nonCoherent){
-          pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][10];
-          pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][11];
-        }
-      }
-    }
-
-    if (n_antenna_port == 4){ // 2 antenna port and the higher layer parameter txConfig = codebook
-      // Table 7.3.1.1.2-4: transformPrecoder=disabled and maxRank = 2
-      if ((pusch_config_dedicated->transform_precoder == transform_precoder_disabled) && (pusch_config_dedicated->max_rank == 2)){
-
-        if (pusch_config_dedicated->codebook_subset == codebook_subset_fullyAndPartialAndNonCoherent) {
-          pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][12];
-          pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][13];
-        }
-
-        if (pusch_config_dedicated->codebook_subset == codebook_subset_nonCoherent){
-          pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][14];
-          pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][15];
-        }
-
-      }
-
-      // Table 7.3.1.1.2-5: transformPrecoder= enabled, or transformPrecoder= disabled and maxRank = 1
-      if (((pusch_config_dedicated->transform_precoder == transform_precoder_enabled)
-        || (pusch_config_dedicated->transform_precoder == transform_precoder_disabled))
-        && (pusch_config_dedicated->max_rank == 1)){
-
-        if (pusch_config_dedicated->codebook_subset == codebook_subset_fullyAndPartialAndNonCoherent) {
-          pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][16];
-          pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][17];
-        }
-
-        if (pusch_config_dedicated->codebook_subset == codebook_subset_nonCoherent){
-          pusch_config_pdu->nrOfLayers = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][18];
-          pusch_config_pdu->transform_precoding = table_7_3_1_1_2_2_3_4_5[dci->precoding_information.val][19];
-        }
-
-      }
-    }
-  }
-}
-
-// todo: this function shall be reviewed completely because of the many comments left by the author
-void ul_ports_config(NR_UE_MAC_INST_t * mac, nfapi_nr_ue_pusch_pdu_t *pusch_config_pdu, dci_pdu_rel15_t *dci) {
-
-  /* ANTENNA_PORTS */
-  uint8_t rank = 0; // We need to initialize rank FIXME!!!
-  fapi_nr_pusch_config_dedicated_t *pusch_config_dedicated = &mac->phy_config.config_req.ul_bwp_dedicated.pusch_config_dedicated;
-
-  if ((pusch_config_dedicated->transform_precoder == transform_precoder_enabled) &&
-    (pusch_config_dedicated->dmrs_ul_for_pusch_mapping_type_a.dmrs_type == 1) &&
-    (pusch_config_dedicated->dmrs_ul_for_pusch_mapping_type_a.max_length == 1)) { // tables 7.3.1.1.2-6
-      pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2; //TBC
-      pusch_config_pdu->dmrs_ports = dci->antenna_ports.val; //TBC
-  }
-
-  if ((pusch_config_dedicated->transform_precoder == transform_precoder_enabled) &&
-    (pusch_config_dedicated->dmrs_ul_for_pusch_mapping_type_a.dmrs_type == 1) &&
-    (pusch_config_dedicated->dmrs_ul_for_pusch_mapping_type_a.max_length == 2)) { // tables 7.3.1.1.2-7
-
-    pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2; //TBC
-    pusch_config_pdu->dmrs_ports = (dci->antenna_ports.val > 3)?(dci->antenna_ports.val-4):(dci->antenna_ports.val); //TBC
-    //pusch_config_pdu->n_front_load_symb = (dci->antenna_ports > 3)?2:1; //FIXME
-  }
-
-  if ((pusch_config_dedicated->transform_precoder == transform_precoder_disabled) &&
-    (pusch_config_dedicated->dmrs_ul_for_pusch_mapping_type_a.dmrs_type == 1) &&
-    (pusch_config_dedicated->dmrs_ul_for_pusch_mapping_type_a.max_length == 1)) { // tables 7.3.1.1.2-8/9/10/11
-
-    if (rank == 1) {
-      pusch_config_pdu->num_dmrs_cdm_grps_no_data = (dci->antenna_ports.val > 1)?2:1; //TBC
-      pusch_config_pdu->dmrs_ports = (dci->antenna_ports.val > 1)?(dci->antenna_ports.val-2):(dci->antenna_ports.val); //TBC
-    }
-
-    if (rank == 2){
-      pusch_config_pdu->num_dmrs_cdm_grps_no_data = (dci->antenna_ports.val > 0)?2:1; //TBC
-      pusch_config_pdu->dmrs_ports = 0; //FIXME
-      //pusch_config_pdu->dmrs_ports[0] = (dci->antenna_ports > 1)?(dci->antenna_ports > 2 ?0:2):0;
-      //pusch_config_pdu->dmrs_ports[1] = (dci->antenna_ports > 1)?(dci->antenna_ports > 2 ?2:3):1;
-    }
-
-    if (rank == 3){
-      pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2; //TBC
-      pusch_config_pdu->dmrs_ports = 0; //FIXME
-      //pusch_config_pdu->dmrs_ports[0] = 0;
-      //pusch_config_pdu->dmrs_ports[1] = 1;
-      //pusch_config_pdu->dmrs_ports[2] = 2;
-    }
-
-    if (rank == 4){
-      pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2; //TBC
-      pusch_config_pdu->dmrs_ports = 0; //FIXME
-      //pusch_config_pdu->dmrs_ports[0] = 0;
-      //pusch_config_pdu->dmrs_ports[1] = 1;
-      //pusch_config_pdu->dmrs_ports[2] = 2;
-      //pusch_config_pdu->dmrs_ports[3] = 3;
-    }
-  }
-
-  if ((pusch_config_dedicated->transform_precoder == transform_precoder_disabled) &&
-    (pusch_config_dedicated->dmrs_ul_for_pusch_mapping_type_a.dmrs_type == 1) &&
-    (pusch_config_dedicated->dmrs_ul_for_pusch_mapping_type_a.max_length == 2)) { // tables 7.3.1.1.2-12/13/14/15
-
-    if (rank == 1){
-      pusch_config_pdu->num_dmrs_cdm_grps_no_data = (dci->antenna_ports.val > 1)?2:1; //TBC
-      pusch_config_pdu->dmrs_ports = (dci->antenna_ports.val > 1)?(dci->antenna_ports.val > 5 ?(dci->antenna_ports.val-6):(dci->antenna_ports.val-2)):dci->antenna_ports.val; //TBC
-      //pusch_config_pdu->n_front_load_symb = (dci->antenna_ports.val > 6)?2:1; //FIXME
-    }
-
-    if (rank == 2){
-      pusch_config_pdu->num_dmrs_cdm_grps_no_data = (dci->antenna_ports.val > 0)?2:1; //TBC
-      pusch_config_pdu->dmrs_ports = 0; //FIXME
-      //pusch_config_pdu->dmrs_ports[0] = table_7_3_1_1_2_13[dci->antenna_ports.val][1];
-      //pusch_config_pdu->dmrs_ports[1] = table_7_3_1_1_2_13[dci->antenna_ports.val][2];
-      //pusch_config_pdu->n_front_load_symb = (dci->antenna_ports.val > 3)?2:1; // FIXME
-    }
-
-    if (rank == 3){
-      pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2; //TBC
-      pusch_config_pdu->dmrs_ports = 0; //FIXME
-      //pusch_config_pdu->dmrs_ports[0] = table_7_3_1_1_2_14[dci->antenna_ports.val][1];
-      //pusch_config_pdu->dmrs_ports[1] = table_7_3_1_1_2_14[dci->antenna_ports.val][2];
-      //pusch_config_pdu->dmrs_ports[2] = table_7_3_1_1_2_14[dci->antenna_ports.val][3];
-      //pusch_config_pdu->n_front_load_symb = (dci->antenna_ports.val > 1)?2:1; //FIXME
-    }
-
-    if (rank == 4){
-      pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2; //TBC
-      pusch_config_pdu->dmrs_ports = 0; //FIXME
-      //pusch_config_pdu->dmrs_ports[0] = table_7_3_1_1_2_15[dci->antenna_ports.val][1];
-      //pusch_config_pdu->dmrs_ports[1] = table_7_3_1_1_2_15[dci->antenna_ports.val][2];
-      //pusch_config_pdu->dmrs_ports[2] = table_7_3_1_1_2_15[dci->antenna_ports.val][3];
-      //pusch_config_pdu->dmrs_ports[3] = table_7_3_1_1_2_15[dci->antenna_ports.val][4];
-      //pusch_config_pdu->n_front_load_symb = (dci->antenna_ports.val > 1)?2:1; //FIXME
-    }
-  }
-
-  if ((pusch_config_dedicated->transform_precoder == transform_precoder_disabled) &&
-    (pusch_config_dedicated->dmrs_ul_for_pusch_mapping_type_a.dmrs_type == 2) &&
-    (pusch_config_dedicated->dmrs_ul_for_pusch_mapping_type_a.max_length == 1)) { // tables 7.3.1.1.2-16/17/18/19
-
-    if (rank == 1){
-      pusch_config_pdu->num_dmrs_cdm_grps_no_data = (dci->antenna_ports.val > 1)?((dci->antenna_ports.val > 5)?3:2):1; //TBC
-      pusch_config_pdu->dmrs_ports = (dci->antenna_ports.val > 1)?(dci->antenna_ports.val > 5 ?(dci->antenna_ports.val-6):(dci->antenna_ports.val-2)):dci->antenna_ports.val; //TBC
-    }
-
-    if (rank == 2){
-      pusch_config_pdu->num_dmrs_cdm_grps_no_data = (dci->antenna_ports.val > 0)?((dci->antenna_ports.val > 2)?3:2):1; //TBC
-      pusch_config_pdu->dmrs_ports = 0; //FIXME
-      //pusch_config_pdu->dmrs_ports[0] = table_7_3_1_1_2_17[dci->antenna_ports.val][1];
-      //pusch_config_pdu->dmrs_ports[1] = table_7_3_1_1_2_17[dci->antenna_ports.val][2];
-    }
-
-    if (rank == 3){
-      pusch_config_pdu->num_dmrs_cdm_grps_no_data = (dci->antenna_ports.val > 0)?3:2; //TBC
-      pusch_config_pdu->dmrs_ports = 0; //FIXME
-      //pusch_config_pdu->dmrs_ports[0] = table_7_3_1_1_2_18[dci->antenna_ports.val][1];
-      //pusch_config_pdu->dmrs_ports[1] = table_7_3_1_1_2_18[dci->antenna_ports.val][2];
-      //pusch_config_pdu->dmrs_ports[2] = table_7_3_1_1_2_18[dci->antenna_ports.val][3];
-    }
-
-    if (rank == 4){
-      pusch_config_pdu->num_dmrs_cdm_grps_no_data = dci->antenna_ports.val + 2; //TBC
-      pusch_config_pdu->dmrs_ports = 0; //FIXME
-      //pusch_config_pdu->dmrs_ports[0] = 0;
-      //pusch_config_pdu->dmrs_ports[1] = 1;
-      //pusch_config_pdu->dmrs_ports[2] = 2;
-      //pusch_config_pdu->dmrs_ports[3] = 3;
-    }
-  }
-
-  if ((pusch_config_dedicated->transform_precoder == transform_precoder_disabled) &&
-    (pusch_config_dedicated->dmrs_ul_for_pusch_mapping_type_a.dmrs_type == 2) &&
-    (pusch_config_dedicated->dmrs_ul_for_pusch_mapping_type_a.max_length == 2)) { // tables 7.3.1.1.2-20/21/22/23
-
-    if (rank == 1){
-      pusch_config_pdu->num_dmrs_cdm_grps_no_data = table_7_3_1_1_2_20[dci->antenna_ports.val][0]; //TBC
-      pusch_config_pdu->dmrs_ports = table_7_3_1_1_2_20[dci->antenna_ports.val][1]; //TBC
-      //pusch_config_pdu->n_front_load_symb = table_7_3_1_1_2_20[dci->antenna_ports.val][2]; //FIXME
-    }
-
-    if (rank == 2){
-      pusch_config_pdu->num_dmrs_cdm_grps_no_data = table_7_3_1_1_2_21[dci->antenna_ports.val][0]; //TBC
-      pusch_config_pdu->dmrs_ports = 0; //FIXME
-      //pusch_config_pdu->dmrs_ports[0] = table_7_3_1_1_2_21[dci->antenna_ports.val][1];
-      //pusch_config_pdu->dmrs_ports[1] = table_7_3_1_1_2_21[dci->antenna_ports.val][2];
-      //pusch_config_pdu->n_front_load_symb = table_7_3_1_1_2_21[dci->antenna_ports.val][3]; //FIXME
-      }
-
-    if (rank == 3){
-      pusch_config_pdu->num_dmrs_cdm_grps_no_data = table_7_3_1_1_2_22[dci->antenna_ports.val][0]; //TBC
-      pusch_config_pdu->dmrs_ports = 0; //FIXME
-      //pusch_config_pdu->dmrs_ports[0] = table_7_3_1_1_2_22[dci->antenna_ports.val][1];
-      //pusch_config_pdu->dmrs_ports[1] = table_7_3_1_1_2_22[dci->antenna_ports.val][2];
-      //pusch_config_pdu->dmrs_ports[2] = table_7_3_1_1_2_22[dci->antenna_ports.val][3];
-      //pusch_config_pdu->n_front_load_symb = table_7_3_1_1_2_22[dci->antenna_ports.val][4]; //FIXME
-    }
-
-    if (rank == 4){
-      pusch_config_pdu->num_dmrs_cdm_grps_no_data = table_7_3_1_1_2_23[dci->antenna_ports.val][0]; //TBC
-      pusch_config_pdu->dmrs_ports = 0; //FIXME
-      //pusch_config_pdu->dmrs_ports[0] = table_7_3_1_1_2_23[dci->antenna_ports.val][1];
-      //pusch_config_pdu->dmrs_ports[1] = table_7_3_1_1_2_23[dci->antenna_ports.val][2];
-      //pusch_config_pdu->dmrs_ports[2] = table_7_3_1_1_2_23[dci->antenna_ports.val][3];
-      //pusch_config_pdu->dmrs_ports[3] = table_7_3_1_1_2_23[dci->antenna_ports.val][4];
-      //pusch_config_pdu->n_front_load_symb = table_7_3_1_1_2_23[dci->antenna_ports.val][5]; //FIXME
-    }
-  }
-}
-
-// Configuration of Msg3 PDU according to clauses:
-// - 8.3 of 3GPP TS 38.213 version 16.3.0 Release 16
-// - 6.1.2.2 of TS 38.214
-// - 6.1.3 of TS 38.214
-// - 6.2.2 of TS 38.214
-// - 6.1.4.2 of TS 38.214
-// - 6.4.1.1.1 of TS 38.211
-// - 6.3.1.7 of 38.211
-int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac,
-                        nfapi_nr_ue_pusch_pdu_t *pusch_config_pdu,
-                        dci_pdu_rel15_t *dci,
-                        RAR_grant_t *rar_grant,
-                        uint16_t rnti,
-                        uint8_t *dci_format){
-
-  int f_alloc;
-  int mask;
-  int StartSymbolIndex;
-  int NrOfSymbols;
-  uint8_t nb_dmrs_re_per_rb;
-
-  uint16_t        l_prime_mask = 1;
-  uint16_t number_dmrs_symbols = 0;
-  int                N_PRB_oh  = 0;
-
-  NR_ServingCellConfigCommon_t *scc = mac->scc;
-  int rnti_type = get_rnti_type(mac, rnti);
-
-  // Common configuration
-  pusch_config_pdu->dmrs_config_type = pusch_dmrs_type1;
-  pusch_config_pdu->pdu_bit_map      = PUSCH_PDU_BITMAP_PUSCH_DATA;
-  pusch_config_pdu->nrOfLayers       = 1;
-  pusch_config_pdu->rnti             = rnti;
-
-  if (rar_grant) {
-
-    // Note: for Msg3 or MsgA PUSCH transmission the N_PRB_oh is always set to 0
-
-    NR_BWP_Uplink_t *ubwp = mac->ULbwp[0];
-    NR_BWP_UplinkDedicated_t *ibwp = mac->scg->spCellConfig->spCellConfigDedicated->uplinkConfig->initialUplinkBWP;
-    NR_PUSCH_Config_t *pusch_Config = ibwp->pusch_Config->choice.setup;
-    int startSymbolAndLength = ubwp->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList->list.array[rar_grant->Msg3_t_alloc]->startSymbolAndLength;
-
-    // active BWP start
-    int abwp_start = NRRIV2PRBOFFSET(ubwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE);
-    int abwp_size = NRRIV2BW(ubwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE);
-
-    // initial BWP start
-    int ibwp_start = NRRIV2PRBOFFSET(scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE);
-    int ibwp_size = NRRIV2BW(scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE);
-
-    // BWP start selection according to 8.3 of TS 38.213
-    pusch_config_pdu->bwp_size = ibwp_size;
-    if ((ibwp_start < abwp_start) || (ibwp_size > abwp_size))
-      pusch_config_pdu->bwp_start = abwp_start;
-    else
-      pusch_config_pdu->bwp_start = ibwp_start;
-
-    //// Resource assignment from RAR
-    // Frequency domain allocation according to 8.3 of TS 38.213
-    if (ibwp_size < 180)
-      mask = (1 << ((int) ceil(log2((ibwp_size*(ibwp_size+1))>>1)))) - 1;
-    else
-      mask = (1 << (28 - (int)(ceil(log2((ibwp_size*(ibwp_size+1))>>1))))) - 1;
-    f_alloc = rar_grant->Msg3_f_alloc & mask;
-    nr_ue_process_dci_freq_dom_resource_assignment(pusch_config_pdu, NULL, ibwp_size, 0, f_alloc);
-
-    // virtual resource block to physical resource mapping for Msg3 PUSCH (6.3.1.7 in 38.211)
-    pusch_config_pdu->rb_start += ibwp_start - abwp_start;
-
-    // Time domain allocation
-    SLIV2SL(startSymbolAndLength, &StartSymbolIndex, &NrOfSymbols);
-    pusch_config_pdu->start_symbol_index = StartSymbolIndex;
-    pusch_config_pdu->nr_of_symbols = NrOfSymbols;
-
-    #ifdef DEBUG_MSG3
-    LOG_D(MAC, "In %s BWP assignment (BWP (start %d, size %d) \n", __FUNCTION__, pusch_config_pdu->bwp_start, pusch_config_pdu->bwp_size);
-    #endif
-
-    // MCS
-    pusch_config_pdu->mcs_index = rar_grant->mcs;
-    // Frequency hopping
-    pusch_config_pdu->frequency_hopping = rar_grant->freq_hopping;
-
-    // DM-RS configuration according to 6.2.2 UE DM-RS transmission procedure in 38.214
-    pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2;
-    pusch_config_pdu->dmrs_ports = 1;
-
-    // DMRS sequence initialization [TS 38.211, sec 6.4.1.1.1].
-    // Should match what is sent in DCI 0_1, otherwise set to 0.
-    pusch_config_pdu->scid = 0;
-
-    // Transform precoding according to 6.1.3 UE procedure for applying transform precoding on PUSCH in 38.214
-    pusch_config_pdu->transform_precoding = get_transformPrecoding(scc, pusch_Config, NULL, NULL, NR_RNTI_RA, 0); // TBR fix rnti and take out
-
-    // Resource allocation in frequency domain according to 6.1.2.2 in TS 38.214
-    pusch_config_pdu->resource_alloc = pusch_Config->resourceAllocation;
-
-    //// Completing PUSCH PDU
-    pusch_config_pdu->mcs_table = 0;
-    pusch_config_pdu->cyclic_prefix = 0;
-    pusch_config_pdu->data_scrambling_id = *scc->physCellId;
-    pusch_config_pdu->ul_dmrs_scrambling_id = *scc->physCellId;
-    pusch_config_pdu->subcarrier_spacing = ubwp->bwp_Common->genericParameters.subcarrierSpacing;
-    pusch_config_pdu->vrb_to_prb_mapping = 0;
-    pusch_config_pdu->uplink_frequency_shift_7p5khz = 0;
-    //Optional Data only included if indicated in pduBitmap
-    pusch_config_pdu->pusch_data.rv_index = 0;  // 8.3 in 38.213
-    pusch_config_pdu->pusch_data.harq_process_id = 0;
-    pusch_config_pdu->pusch_data.new_data_indicator = 1; // new data
-    pusch_config_pdu->pusch_data.num_cb = 0;
-
-  } else if (dci) {
-
-    int target_ss;
-    uint8_t  ptrs_time_density;
-    uint8_t  ptrs_freq_density;
-    nfapi_nr_ue_ptrs_ports_t ptrs_ports_list;
-    uint16_t n_RB_ULBWP = NRRIV2BW(mac->ULbwp[0]->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE);
-    fapi_nr_pusch_config_dedicated_t *pusch_config_dedicated = &mac->phy_config.config_req.ul_bwp_dedicated.pusch_config_dedicated;
-    NR_PUSCH_Config_t *pusch_Config = mac->ULbwp[0]->bwp_Dedicated->pusch_Config->choice.setup;
-
-    // These should come from RRC config!!!
-    uint8_t  ptrs_mcs1          = 2;
-    uint8_t  ptrs_mcs2          = 4;
-    uint8_t  ptrs_mcs3          = 10;
-    uint16_t n_rb0              = 25;
-    uint16_t n_rb1              = 75;
-
-    /* Transform precoding */
-    if (rnti_type != NR_RNTI_CS || (rnti_type == NR_RNTI_CS && dci->ndi == 1)) {
-      pusch_config_pdu->transform_precoding = get_transformPrecoding(scc, pusch_Config, NULL, dci_format, rnti_type, 0);
-    }
-
-    /*DCI format-related configuration*/
-    if (*dci_format == NR_UL_DCI_FORMAT_0_0) {
-
-      target_ss = NR_SearchSpace__searchSpaceType_PR_common;
-
-    } else if (*dci_format == NR_UL_DCI_FORMAT_0_1) {
-
-      /* BANDWIDTH_PART_IND */
-      config_bwp_ue(mac, &dci->bwp_indicator.val, dci_format);
-      target_ss = NR_SearchSpace__searchSpaceType_PR_ue_Specific;
-      ul_layers_config(mac, pusch_config_pdu, dci);
-      ul_ports_config(mac, pusch_config_pdu, dci);
-
-    } else {
-
-      LOG_E(MAC, "In %s: UL grant from DCI format %d is not handled...\n", __FUNCTION__, *dci_format);
-      return -1;
-
-    }
-
-    /* IDENTIFIER_DCI_FORMATS */
-    /* FREQ_DOM_RESOURCE_ASSIGNMENT_UL */
-    if (nr_ue_process_dci_freq_dom_resource_assignment(pusch_config_pdu, NULL, n_RB_ULBWP, 0, dci->frequency_domain_assignment.val) < 0){
-      return -1;
-    }
-    /* TIME_DOM_RESOURCE_ASSIGNMENT */
-    if (nr_ue_process_dci_time_dom_resource_assignment(mac, pusch_config_pdu, NULL, dci->time_domain_assignment.val) < 0) {
-      return -1;
-    }
-
-    /* FREQ_HOPPING_FLAG */
-    if ((pusch_config_dedicated->resource_allocation != 0) && (pusch_config_dedicated->frequency_hopping !=0)){
-      pusch_config_pdu->frequency_hopping = dci->frequency_hopping_flag.val;
-    }
-
-    /* MCS */
-    pusch_config_pdu->mcs_index = dci->mcs;
-
-    /* MCS TABLE */
-    if (pusch_config_pdu->transform_precoding == transform_precoder_disabled) {
-      pusch_config_pdu->mcs_table = get_pusch_mcs_table(pusch_Config->mcs_Table, 0, *dci_format, rnti_type, target_ss, false);
-    } else {
-      pusch_config_pdu->mcs_table = get_pusch_mcs_table(pusch_Config->mcs_TableTransformPrecoder, 1, *dci_format, rnti_type, target_ss, false);
-    }
-
-    /* NDI */
-    pusch_config_pdu->pusch_data.new_data_indicator = dci->ndi;
-    /* RV */
-    pusch_config_pdu->pusch_data.rv_index = dci->rv;
-    /* HARQ_PROCESS_NUMBER */
-    pusch_config_pdu->pusch_data.harq_process_id = dci->harq_pid;
-    /* TPC_PUSCH */
-    // according to TS 38.213 Table Table 7.1.1-1
-    if (dci->tpc == 0) {
-      pusch_config_pdu->absolute_delta_PUSCH = -4;
-    }
-    if (dci->tpc == 1) {
-      pusch_config_pdu->absolute_delta_PUSCH = -1;
-    }
-    if (dci->tpc == 2) {
-      pusch_config_pdu->absolute_delta_PUSCH = 1;
-    }
-    if (dci->tpc == 3) {
-      pusch_config_pdu->absolute_delta_PUSCH = 4;
-    }
-
-    ptrs_time_density  = get_L_ptrs(ptrs_mcs1, ptrs_mcs2, ptrs_mcs3, pusch_config_pdu->mcs_index, pusch_config_pdu->mcs_table);
-    ptrs_freq_density  = get_K_ptrs(n_rb0, n_rb1, pusch_config_pdu->rb_size);
-    // PTRS ports configuration
-    // TbD: ptrs_dmrs_port and ptrs_port_index are not initialised!
-    ptrs_ports_list.ptrs_re_offset = 0;
-
-    /* DMRS */
-    l_prime_mask = get_l_prime(pusch_config_pdu->nr_of_symbols, typeB, pusch_dmrs_pos0, pusch_len1);
-    pusch_config_pdu->num_dmrs_cdm_grps_no_data = 1;
-
-    // Num PRB Overhead from PUSCH-ServingCellConfig
-    if (mac->scg->spCellConfig->spCellConfigDedicated->uplinkConfig->pusch_ServingCellConfig->choice.setup->xOverhead == NULL) {
-      N_PRB_oh = 0;
-    } else {
-      N_PRB_oh = *mac->scg->spCellConfig->spCellConfigDedicated->uplinkConfig->pusch_ServingCellConfig->choice.setup->xOverhead;
-    }
-
-    /* PTRS */
-    pusch_config_pdu->pusch_ptrs.ptrs_time_density = ptrs_time_density;
-    pusch_config_pdu->pusch_ptrs.ptrs_freq_density = ptrs_freq_density;
-    pusch_config_pdu->pusch_ptrs.ptrs_ports_list   = &ptrs_ports_list;
-
-    if (1 << pusch_config_pdu->pusch_ptrs.ptrs_time_density >= pusch_config_pdu->nr_of_symbols) {
-      pusch_config_pdu->pdu_bit_map &= ~PUSCH_PDU_BITMAP_PUSCH_PTRS; // disable PUSCH PTRS
-    }
-
-  }
-
-  LOG_D(MAC, "In %s: received UL grant (rb_start %d, rb_size %d, start_symbol_index %d, nr_of_symbols %d) for RNTI type %s \n",
-    __FUNCTION__,
-    pusch_config_pdu->rb_start,
-    pusch_config_pdu->rb_size,
-    pusch_config_pdu->start_symbol_index,
-    pusch_config_pdu->nr_of_symbols,
-    rnti_types[rnti_type]);
-
-  pusch_config_pdu->ul_dmrs_symb_pos = l_prime_mask << pusch_config_pdu->start_symbol_index;;
-  pusch_config_pdu->target_code_rate = nr_get_code_rate_ul(pusch_config_pdu->mcs_index, pusch_config_pdu->mcs_table);
-  pusch_config_pdu->qam_mod_order = nr_get_Qm_ul(pusch_config_pdu->mcs_index, pusch_config_pdu->mcs_table);
-
-  if (pusch_config_pdu->target_code_rate == 0 || pusch_config_pdu->qam_mod_order == 0) {
-    LOG_W(MAC, "In %s: Invalid code rate or Mod order, likely due to unexpected UL DCI. Ignoring DCI! \n", __FUNCTION__);
-    return -1;
-  }
-
-  get_num_re_dmrs(pusch_config_pdu, &nb_dmrs_re_per_rb, &number_dmrs_symbols);
-
-  // Compute TBS
-  pusch_config_pdu->pusch_data.tb_size = nr_compute_tbs(pusch_config_pdu->qam_mod_order,
-                                                        pusch_config_pdu->target_code_rate,
-                                                        pusch_config_pdu->rb_size,
-                                                        pusch_config_pdu->nr_of_symbols,
-                                                        nb_dmrs_re_per_rb*number_dmrs_symbols,
-                                                        N_PRB_oh,
-                                                        0, // TBR to verify tb scaling
-                                                        pusch_config_pdu->nrOfLayers)/8;
-
-  return 0;
-
-}
-
-/////////////////////////////////////
-//    Random Access Response PDU   //
-//         TS 38.213 ch 8.2        //
-//        TS 38.321 ch 6.2.3       //
-/////////////////////////////////////
-//| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |// bit-wise
-//| E | T |       R A P I D       |//
-//| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |//
-//| R |           T A             |//
-//|       T A         |  UL grant |//
-//|            UL grant           |//
-//|            UL grant           |//
-//|            UL grant           |//
-//|         T C - R N T I         |//
-//|         T C - R N T I         |//
-/////////////////////////////////////
-//       UL grant  (27 bits)       //
-/////////////////////////////////////
-//| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |// bit-wise
-//|-------------------|FHF|F_alloc|//
-//|        Freq allocation        |//
-//|    F_alloc    |Time allocation|//
-//|      MCS      |     TPC   |CSI|//
-/////////////////////////////////////
-// TbD WIP Msg3 development ongoing
-// - apply UL grant freq alloc & time alloc as per 8.2 TS 38.213
-// - apply tpc command
-// WIP fix:
-// - time domain indication hardcoded to 0 for k2 offset
-// - extend TS 38.213 ch 8.3 Msg3 PUSCH
-// - b buffer
-// - ulsch power offset
-// - optimize: mu_pusch, j and table_6_1_2_1_1_2_time_dom_res_alloc_A are already defined in nr_ue_procedures
-int nr_ue_process_rar(nr_downlink_indication_t *dl_info, NR_UL_TIME_ALIGNMENT_t *ul_time_alignment, int pdu_id){
-
-  module_id_t mod_id       = dl_info->module_id;
-  frame_t frame            = dl_info->frame;
-  int slot                 = dl_info->slot;
-  int cc_id                = dl_info->cc_id;
-  uint8_t gNB_id           = dl_info->gNB_index;
-  NR_UE_MAC_INST_t *mac    = get_mac_inst(mod_id);
-  RA_config_t *ra          = &mac->ra;
-  uint8_t n_subPDUs        = 0;  // number of RAR payloads
-  uint8_t n_subheaders     = 0;  // number of MAC RAR subheaders
-  uint8_t *dlsch_buffer    = dl_info->rx_ind->rx_indication_body[pdu_id].pdsch_pdu.pdu;
-  uint8_t is_Msg3          = 1;
-  frame_t frame_tx         = 0;
-  int slot_tx              = 0;
-  uint16_t rnti            = 0;
-  int ret                  = 0;
-  NR_RA_HEADER_RAPID *rarh = (NR_RA_HEADER_RAPID *) dlsch_buffer; // RAR subheader pointer
-  NR_MAC_RAR *rar          = (NR_MAC_RAR *) (dlsch_buffer + 1);   // RAR subPDU pointer
-  uint8_t preamble_index   = get_ra_PreambleIndex(mod_id, cc_id, gNB_id); //prach_resources->ra_PreambleIndex;
-
-  LOG_D(MAC, "In %s:[%d.%d]: [UE %d][RAPROC] invoking MAC for received RAR (current preamble %d)\n", __FUNCTION__, frame, slot, mod_id, preamble_index);
-
-  while (1) {
-    n_subheaders++;
-    if (rarh->T == 1) {
-      n_subPDUs++;
-      LOG_D(MAC, "[UE %d][RAPROC] Got RAPID RAR subPDU\n", mod_id);
-    } else {
-      n_subPDUs++;
-      ra->RA_backoff_indicator = table_7_2_1[((NR_RA_HEADER_BI *)rarh)->BI];
-      ra->RA_BI_found = 1;
-      LOG_D(MAC, "[UE %d][RAPROC] Got BI RAR subPDU %d\n", mod_id, ra->RA_backoff_indicator);
-    }
-    if (rarh->RAPID == preamble_index) {
-      LOG_I(MAC, "[UE %d][RAPROC][%d.%d] Found RAR with the intended RAPID %d\n", mod_id, frame, slot, rarh->RAPID);
-      rar = (NR_MAC_RAR *) (dlsch_buffer + n_subheaders + (n_subPDUs - 1) * sizeof(NR_MAC_RAR));
-      ra->RA_RAPID_found = 1;
-      break;
-    }
-    if (rarh->E == 0) {
-      LOG_W(PHY,"[UE %d][RAPROC] Received RAR preamble (%d) doesn't match the intended RAPID...\n", mod_id, preamble_index);
-      break;
-    } else {
-      rarh += sizeof(NR_MAC_RAR) + 1;
-    }
-  }
-
-  #ifdef DEBUG_RAR
-  LOG_D(MAC, "[DEBUG_RAR] (%d,%d) number of RAR subheader %d; number of RAR pyloads %d\n", frame, slot, n_subheaders, n_subPDUs);
-  LOG_D(MAC, "[DEBUG_RAR] Received RAR (%02x|%02x.%02x.%02x.%02x.%02x.%02x) for preamble %d/%d\n", *(uint8_t *) rarh, rar[0], rar[1], rar[2], rar[3], rar[4], rar[5], rarh->RAPID, preamble_index);
-  #endif
-
-  if (ra->RA_RAPID_found) {
-
-    RAR_grant_t rar_grant;
-
-    unsigned char tpc_command;
-#ifdef DEBUG_RAR
-    unsigned char csi_req;
-#endif
-
-  // TC-RNTI
-  ra->t_crnti = rar->TCRNTI_2 + (rar->TCRNTI_1 << 8);
-
-  // TA command
-  ul_time_alignment->apply_ta = 1;
-  ul_time_alignment->ta_command = rar->TA2 + (rar->TA1 << 5);
-
-#ifdef DEBUG_RAR
-  // CSI
-  csi_req = (unsigned char) (rar->UL_GRANT_4 & 0x01);
-#endif
-
-  // TPC
-  tpc_command = (unsigned char) ((rar->UL_GRANT_4 >> 1) & 0x07);
-  switch (tpc_command){
-    case 0:
-      ra->Msg3_TPC = -6;
-      break;
-    case 1:
-      ra->Msg3_TPC = -4;
-      break;
-    case 2:
-      ra->Msg3_TPC = -2;
-      break;
-    case 3:
-      ra->Msg3_TPC = 0;
-      break;
-    case 4:
-      ra->Msg3_TPC = 2;
-      break;
-    case 5:
-      ra->Msg3_TPC = 4;
-      break;
-    case 6:
-      ra->Msg3_TPC = 6;
-      break;
-    case 7:
-      ra->Msg3_TPC = 8;
-      break;
-  }
-    // MCS
-    rar_grant.mcs = (unsigned char) (rar->UL_GRANT_4 >> 4);
-    // time alloc
-    rar_grant.Msg3_t_alloc = (unsigned char) (rar->UL_GRANT_3 & 0x07);
-    // frequency alloc
-    rar_grant.Msg3_f_alloc = (uint16_t) ((rar->UL_GRANT_3 >> 4) | (rar->UL_GRANT_2 << 4) | ((rar->UL_GRANT_1 & 0x03) << 12));
-    // frequency hopping
-    rar_grant.freq_hopping = (unsigned char) (rar->UL_GRANT_1 >> 2);
-    // TC-RNTI
-    if (ra->t_crnti) {
-      rnti = ra->t_crnti;
-    } else {
-      rnti = mac->crnti;
-    }
-
-  #ifdef DEBUG_RAR
-  LOG_D(MAC, "In %s:[%d.%d]: [UE %d] Received RAR with t_alloc %d f_alloc %d ta_command %d mcs %d freq_hopping %d tpc_command %d csi_req %d t_crnti %x \n",
-    __FUNCTION__,
-    frame,
-    slot,
-    mod_id,
-    rar_grant.Msg3_t_alloc,
-    rar_grant.Msg3_f_alloc,
-    ul_time_alignment->ta_command,
-    rar_grant.mcs,
-    rar_grant.freq_hopping,
-    tpc_command,
-    csi_req,
-    ra->t_crnti);
-  #endif
-
-    // Schedule Msg3
-    ret = nr_ue_pusch_scheduler(mac, is_Msg3, frame, slot, &frame_tx, &slot_tx, rar_grant.Msg3_t_alloc);
-
-    if (ret != -1){
-
-      fapi_nr_ul_config_request_t *ul_config = get_ul_config_request(mac, slot_tx);
-
-      if (!ul_config) {
-        LOG_W(MAC, "In %s: ul_config request is NULL. Probably due to unexpected UL DCI in frame.slot %d.%d. Ignoring DCI!\n", __FUNCTION__, frame, slot);
-        return -1;
-      }
-
-      nfapi_nr_ue_pusch_pdu_t *pusch_config_pdu = &ul_config->ul_config_list[ul_config->number_pdus].pusch_config_pdu;
-
-      fill_ul_config(ul_config, frame_tx, slot_tx, FAPI_NR_UL_CONFIG_TYPE_PUSCH);
-
-      // Config Msg3 PDU
-      nr_config_pusch_pdu(mac, pusch_config_pdu, NULL, &rar_grant, rnti, NULL);
-
-    }
-
-  } else {
-
-    ra->t_crnti = 0;
-    ul_time_alignment->ta_command = (0xffff);
-
-  }
-
-  return ret;
-
-}