Files
openairinterface5g/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c
2026-04-27 22:27:15 +02:00

1229 lines
51 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
*/
/*!
* \brief Routines for UE MAC-layer Random Access procedures (TS 38.321, Release 15)
*/
/* RRC */
#include "RRC/NR_UE/L2_interface_ue.h"
/* MAC */
#include "NR_MAC_COMMON/nr_mac.h"
#include "LAYER2/NR_MAC_UE/mac_proto.h"
#include <executables/softmodem-common.h>
#include "openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h"
int16_t get_prach_tx_power(NR_UE_MAC_INST_t *mac)
{
RA_config_t *ra = &mac->ra;
int16_t pathloss = compute_nr_SSB_PL(mac);
int16_t ra_preamble_rx_power = (int16_t)(ra->prach_resources.ra_preamble_rx_target_power + pathloss);
return min(ra->prach_resources.Pc_max, ra_preamble_rx_power);
}
static void set_preambleTransMax(RA_config_t *ra, long preambleTransMax)
{
switch (preambleTransMax) {
case 0:
ra->preambleTransMax = 3;
break;
case 1:
ra->preambleTransMax = 4;
break;
case 2:
ra->preambleTransMax = 5;
break;
case 3:
ra->preambleTransMax = 6;
break;
case 4:
ra->preambleTransMax = 7;
break;
case 5:
ra->preambleTransMax = 8;
break;
case 6:
ra->preambleTransMax = 10;
break;
case 7:
ra->preambleTransMax = 20;
break;
case 8:
ra->preambleTransMax = 50;
break;
case 9:
ra->preambleTransMax = 100;
break;
case 10:
ra->preambleTransMax = 200;
break;
default:
AssertFatal(false, "Invalid preambleTransMax\n");
}
}
static int get_Msg3SizeGroupA(long ra_Msg3SizeGroupA)
{
int bits = 0;
switch (ra_Msg3SizeGroupA) {
case NR_RACH_ConfigCommon__groupBconfigured__ra_Msg3SizeGroupA_b56:
bits = 56;
break;
case NR_RACH_ConfigCommon__groupBconfigured__ra_Msg3SizeGroupA_b144:
bits = 144;
break;
case NR_RACH_ConfigCommon__groupBconfigured__ra_Msg3SizeGroupA_b208:
bits = 208;
break;
case NR_RACH_ConfigCommon__groupBconfigured__ra_Msg3SizeGroupA_b256:
bits = 256;
break;
case NR_RACH_ConfigCommon__groupBconfigured__ra_Msg3SizeGroupA_b282:
bits = 282;
break;
case NR_RACH_ConfigCommon__groupBconfigured__ra_Msg3SizeGroupA_b480:
bits = 480;
break;
case NR_RACH_ConfigCommon__groupBconfigured__ra_Msg3SizeGroupA_b640:
bits = 640;
break;
case NR_RACH_ConfigCommon__groupBconfigured__ra_Msg3SizeGroupA_b800:
bits = 800;
break;
case NR_RACH_ConfigCommon__groupBconfigured__ra_Msg3SizeGroupA_b1000:
bits = 1000;
break;
case NR_RACH_ConfigCommon__groupBconfigured__ra_Msg3SizeGroupA_b72:
bits = 72;
break;
default:
AssertFatal(false, "Unknown ra-Msg3SizeGroupA %lu\n", ra_Msg3SizeGroupA);
}
return bits / 8; // returning bytes
}
static int get_messagePowerOffsetGroupB(long messagePowerOffsetGroupB)
{
int pow_offset = 0;
switch (messagePowerOffsetGroupB) {
case NR_RACH_ConfigCommon__groupBconfigured__messagePowerOffsetGroupB_minusinfinity:
pow_offset = INT_MIN;
break;
case NR_RACH_ConfigCommon__groupBconfigured__messagePowerOffsetGroupB_dB0:
pow_offset = 0;
break;
case NR_RACH_ConfigCommon__groupBconfigured__messagePowerOffsetGroupB_dB5:
pow_offset = 5;
break;
case NR_RACH_ConfigCommon__groupBconfigured__messagePowerOffsetGroupB_dB8:
pow_offset = 8;
break;
case NR_RACH_ConfigCommon__groupBconfigured__messagePowerOffsetGroupB_dB10:
pow_offset = 10;
break;
case NR_RACH_ConfigCommon__groupBconfigured__messagePowerOffsetGroupB_dB12:
pow_offset = 12;
break;
case NR_RACH_ConfigCommon__groupBconfigured__messagePowerOffsetGroupB_dB15:
pow_offset = 15;
break;
case NR_RACH_ConfigCommon__groupBconfigured__messagePowerOffsetGroupB_dB18:
pow_offset = 18;
break;
default:
AssertFatal(false, "Unknown messagePowerOffsetGroupB %lu\n", messagePowerOffsetGroupB);
}
return pow_offset;
}
static void select_preamble_group(NR_UE_MAC_INST_t *mac)
{
RA_config_t *ra = &mac->ra;
// TODO if the RA_TYPE is switched from 2-stepRA to 4-stepRA
if (!ra->Msg3_buffer) { // if Msg3 buffer is empty
NR_RACH_ConfigCommon_t *nr_rach_ConfigCommon = mac->current_UL_BWP->rach_ConfigCommon;
if (nr_rach_ConfigCommon && nr_rach_ConfigCommon->groupBconfigured) { // if Random Access Preambles group B is configured
struct NR_RACH_ConfigCommon__groupBconfigured *groupB = nr_rach_ConfigCommon->groupBconfigured;
// if the potential Msg3 size (UL data available for transmission plus MAC subheader(s) and,
// where required, MAC CEs) is greater than ra-Msg3SizeGroupA
// if the pathloss is less than PCMAX (of the Serving Cell performing the Random Access Procedure)
// preambleReceivedTargetPower msg3-DeltaPreamble messagePowerOffsetGroupB
int groupB_pow_offset = get_messagePowerOffsetGroupB(groupB->messagePowerOffsetGroupB);
int PLThreshold = ra->prach_resources.Pc_max - ra->preambleRxTargetPower - ra->msg3_deltaPreamble - groupB_pow_offset;
int pathloss = compute_nr_SSB_PL(mac);
// TODO if the Random Access procedure was initiated for the CCCH logical channel and the CCCH SDU size
// plus MAC subheader is greater than ra-Msg3SizeGroupA
if (ra->Msg3_size > get_Msg3SizeGroupA(groupB->ra_Msg3SizeGroupA) && pathloss < PLThreshold)
ra->RA_GroupA = false;
else
ra->RA_GroupA = true;
} else
ra->RA_GroupA = true;
}
// else if Msg3 is being retransmitted, we keep what used in first transmission of Msg3
}
ssb_ro_preambles_t get_ssb_ro_preambles_4step(struct NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB *config)
{
ssb_ro_preambles_t ret = {0};
switch (config->present) {
case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_oneEighth:
ret.ssb_per_ro = 0.125;
ret.preambles_per_ssb = (config->choice.oneEighth + 1) << 2;
break;
case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_oneFourth:
ret.ssb_per_ro = 0.25;
ret.preambles_per_ssb = (config->choice.oneFourth + 1) << 2;
break;
case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_oneHalf:
ret.ssb_per_ro = 0.5;
ret.preambles_per_ssb = (config->choice.oneHalf + 1) << 2;
break;
case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_one:
ret.ssb_per_ro = 1;
ret.preambles_per_ssb = (config->choice.one + 1) << 2;
break;
case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_two:
ret.ssb_per_ro = 2;
ret.preambles_per_ssb = (config->choice.two + 1) << 2;
break;
case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_four:
ret.ssb_per_ro = 4;
ret.preambles_per_ssb = config->choice.four;
break;
case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_eight:
ret.ssb_per_ro = 8;
ret.preambles_per_ssb = config->choice.eight;
break;
case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_sixteen:
ret.ssb_per_ro = 16;
ret.preambles_per_ssb = config->choice.sixteen;
break;
default:
AssertFatal(false, "Invalid ssb_perRACH_OccasionAndCB_PreamblesPerSSB\n");
}
LOG_D(NR_MAC, "SSB per RO %f preambles per SSB %d\n", ret.ssb_per_ro, ret.preambles_per_ssb);
return ret;
}
static ssb_ro_preambles_t get_ssb_ro_preambles_2step(struct NR_RACH_ConfigCommonTwoStepRA_r16__msgA_SSB_PerRACH_OccasionAndCB_PreamblesPerSSB_r16 *config)
{
ssb_ro_preambles_t ret = {0};
switch (config->present) {
case NR_RACH_ConfigCommonTwoStepRA_r16__msgA_SSB_PerRACH_OccasionAndCB_PreamblesPerSSB_r16_PR_oneEighth :
ret.ssb_per_ro = 0.125;
ret.preambles_per_ssb = (config->choice.oneEighth + 1) << 2;
break;
case NR_RACH_ConfigCommonTwoStepRA_r16__msgA_SSB_PerRACH_OccasionAndCB_PreamblesPerSSB_r16_PR_oneFourth :
ret.ssb_per_ro = 0.25;
ret.preambles_per_ssb = (config->choice.oneFourth + 1) << 2;
break;
case NR_RACH_ConfigCommonTwoStepRA_r16__msgA_SSB_PerRACH_OccasionAndCB_PreamblesPerSSB_r16_PR_oneHalf :
ret.ssb_per_ro = 0.5;
ret.preambles_per_ssb = (config->choice.oneHalf + 1) << 2;
break;
case NR_RACH_ConfigCommonTwoStepRA_r16__msgA_SSB_PerRACH_OccasionAndCB_PreamblesPerSSB_r16_PR_one :
ret.ssb_per_ro = 1;
ret.preambles_per_ssb = (config->choice.one + 1) << 2;
break;
case NR_RACH_ConfigCommonTwoStepRA_r16__msgA_SSB_PerRACH_OccasionAndCB_PreamblesPerSSB_r16_PR_two :
ret.ssb_per_ro = 2;
ret.preambles_per_ssb = (config->choice.two + 1) << 2;
break;
case NR_RACH_ConfigCommonTwoStepRA_r16__msgA_SSB_PerRACH_OccasionAndCB_PreamblesPerSSB_r16_PR_four :
ret.ssb_per_ro = 4;
ret.preambles_per_ssb = config->choice.four;
break;
case NR_RACH_ConfigCommonTwoStepRA_r16__msgA_SSB_PerRACH_OccasionAndCB_PreamblesPerSSB_r16_PR_eight :
ret.ssb_per_ro = 8;
ret.preambles_per_ssb = config->choice.eight;
break;
case NR_RACH_ConfigCommonTwoStepRA_r16__msgA_SSB_PerRACH_OccasionAndCB_PreamblesPerSSB_r16_PR_sixteen :
ret.ssb_per_ro = 16;
ret.preambles_per_ssb = config->choice.sixteen;
break;
default :
AssertFatal(false, "Invalid msgA_SSB_PerRACH_OccasionAndCB_PreamblesPerSSB_r16\n");
}
return ret;
}
static void config_preamble_index(NR_UE_MAC_INST_t *mac)
{
RA_config_t *ra = &mac->ra;
// Random seed generation
unsigned int seed;
if (IS_SOFTMODEM_IQPLAYER || IS_SOFTMODEM_IQRECORDER) {
// Overwrite seed with non-random seed for IQ player/recorder
seed = 1;
} else {
// & to truncate the int64_t and keep only the LSB bits, up to sizeof(int)
seed = (unsigned int)(rdtsc_oai() & ~0);
}
NR_RACH_ConfigCommon_t *nr_rach_ConfigCommon = mac->current_UL_BWP->rach_ConfigCommon;
int nb_of_preambles = 64;
bool groupBconfigured = false;
int preamb_ga = 0;
if (ra->ra_type == RA_4_STEP) {
ra->ssb_ro_config = mac->ssb_ro_preambles;
if (nr_rach_ConfigCommon->totalNumberOfRA_Preambles)
nb_of_preambles = *nr_rach_ConfigCommon->totalNumberOfRA_Preambles;
// Amongst the contention-based Random Access Preambles associated with an SSB the first numberOfRA-PreamblesGroupA
// included in groupBconfigured Random Access Preambles belong to Random Access Preambles group A.
// The remaining Random Access Preambles associated with the SSB belong to Random Access Preambles group B (if configured)
select_preamble_group(mac);
if (nr_rach_ConfigCommon->groupBconfigured) {
groupBconfigured = true;
preamb_ga = nr_rach_ConfigCommon->groupBconfigured->numberOfRA_PreamblesGroupA;
}
} else {
NR_RACH_ConfigCommonTwoStepRA_r16_t *twostep = &mac->current_UL_BWP->msgA_ConfigCommon_r16->rach_ConfigCommonTwoStepRA_r16;
AssertFatal(twostep->groupB_ConfiguredTwoStepRA_r16 == NULL, "GroupB preambles not supported for 2-step RA\n");
ra->RA_GroupA = true;
// The field is mandatory present if the 2-step random access type occasions are shared with 4-step random access type,
// otherwise the field is not present
bool sharedROs = twostep->msgA_CB_PreamblesPerSSB_PerSharedRO_r16 != NULL;
AssertFatal(sharedROs == false, "Shared ROs between 2- and 4-step RA not supported\n");
// For Type-2 random access procedure with separate configuration of PRACH occasions with Type-1 random access procedure
// configuration by msgA-SSB-PerRACH-OccasionAndCB-PreamblesPerSSB when provided;
// otherwise, by ssb-perRACH-OccasionAndCB-PreamblesPerSSB
if (twostep->msgA_SSB_PerRACH_OccasionAndCB_PreamblesPerSSB_r16)
ra->ssb_ro_config = get_ssb_ro_preambles_2step(twostep->msgA_SSB_PerRACH_OccasionAndCB_PreamblesPerSSB_r16);
else
ra->ssb_ro_config = mac->ssb_ro_preambles;
if (twostep->msgA_TotalNumberOfRA_Preambles_r16)
nb_of_preambles = *twostep->msgA_TotalNumberOfRA_Preambles_r16;
}
int groupOffset = 0;
if (groupBconfigured) {
AssertFatal(preamb_ga < nb_of_preambles, "Nb of preambles for groupA not compatible with total number of preambles\n");
if (!ra->RA_GroupA) { // groupB
groupOffset = preamb_ga;
nb_of_preambles = nb_of_preambles - preamb_ga;
} else {
nb_of_preambles = preamb_ga;
}
}
int pream_per_ssb = min(ra->ssb_ro_config.preambles_per_ssb, nb_of_preambles);
int rand_preamb = rand_r(&seed) % pream_per_ssb;
if (ra->ssb_ro_config.ssb_per_ro < 1)
ra->ra_PreambleIndex = groupOffset + rand_preamb;
else {
int ssb_pr_idx = mac->ssb_list.nb_ssb_per_index[mac->mib_ssb] % (int)ra->ssb_ro_config.ssb_per_ro;
ra->ra_PreambleIndex = groupOffset + (ssb_pr_idx * ra->ssb_ro_config.preambles_per_ssb) + rand_preamb;
}
}
static void configure_ra_preamble(NR_UE_MAC_INST_t *mac)
{
RA_config_t *ra = &mac->ra;
int ssb = -1; // init as not selected
ra->ro_mask_index = -1; // init as not selected
NR_RACH_ConfigDedicated_t *rach_ConfigDedicated = ra->rach_ConfigDedicated;
if (ra->ra_type == RA_4_STEP) {
// TODO if the Random Access procedure was initiated for SpCell beam failure recovery
// TODO if the Random Access procedure was initiated for SI request
if (ra->pdcch_order.active && ra->pdcch_order.preamble_index != 0xb000000) {
// set the PREAMBLE_INDEX to the signalled ra-PreambleIndex;
ra->ra_PreambleIndex = ra->pdcch_order.preamble_index;
// select the SSB signalled by PDCCH
ssb = ra->pdcch_order.ssb_index;
ra->ro_mask_index = ra->pdcch_order.prach_mask;
} else if (rach_ConfigDedicated && rach_ConfigDedicated->cfra) {
NR_CFRA_t *cfra = rach_ConfigDedicated->cfra;
AssertFatal(cfra->occasions == NULL, "Dedicated PRACH occasions for CFRA not supported\n");
AssertFatal(cfra->resources.present == NR_CFRA__resources_PR_ssb, "SSB-based CFRA supported only\n");
struct NR_CFRA__resources__ssb *ssb_list = cfra->resources.choice.ssb;
for (int i = 0; i < ssb_list->ssb_ResourceList.list.count; i++) {
NR_CFRA_SSB_Resource_t *res = ssb_list->ssb_ResourceList.list.array[i];
// TODO select an SSB with SS-RSRP above rsrp-ThresholdSSB amongst the associated SSBs
if (res->ssb == mac->mib_ssb) {
ssb = mac->mib_ssb;
// set the PREAMBLE_INDEX to a ra-PreambleIndex corresponding to the selected SSB
ra->ra_PreambleIndex = res->ra_PreambleIndex;
ra->ro_mask_index = ssb_list->ra_ssb_OccasionMaskIndex;
break;
}
}
} else { // for the contention-based Random Access preamble selection
// TODO if at least one of the SSBs with SS-RSRP above rsrp-ThresholdSSB is available
// else select any SSB
ssb = mac->mib_ssb;
config_preamble_index(mac);
}
} else { // 2-step RA
// if the contention-free 2-step RA type Resources associated with SSBs have been explicitly provided in rach-ConfigDedicated
// TODO and at least one SSB with SS-RSRP above msgA-RSRP-ThresholdSSB amongst the associated SSBs is available
if (ra->cfra) {
AssertFatal(rach_ConfigDedicated->ext1 && rach_ConfigDedicated->ext1->cfra_TwoStep_r16,
"Two-step CFRA should be configured here\n");
NR_CFRA_TwoStep_r16_t *cfra = rach_ConfigDedicated->ext1->cfra_TwoStep_r16;
AssertFatal(cfra->occasionsTwoStepRA_r16 == NULL, "Dedicated PRACH occasions for CFRA not supported\n");
for (int i = 0; i < cfra->resourcesTwoStep_r16.ssb_ResourceList.list.count; i++) {
NR_CFRA_SSB_Resource_t *res = cfra->resourcesTwoStep_r16.ssb_ResourceList.list.array[i];
if (res->ssb == mac->mib_ssb) {
ssb = mac->mib_ssb;
// set the PREAMBLE_INDEX to a ra-PreambleIndex corresponding to the selected SSB
ra->ra_PreambleIndex = res->ra_PreambleIndex;
ra->ro_mask_index = cfra->resourcesTwoStep_r16.ra_ssb_OccasionMaskIndex;
break;
}
}
} else { // for the contention-based Random Access Preamble selection
// TODO if at least one of the SSBs with SS-RSRP above msgA-RSRP-ThresholdSSB is available
// else select any SSB
ssb = mac->mib_ssb;
config_preamble_index(mac);
}
}
AssertFatal(ssb >= 0, "Something wrong! RA resource selection didn't set any SSB\n");
// setting the RA ssb value as the progressive number of SSB transmitted
// non-transmitted SSBs are not taken into account
int ssb_idx = mac->ssb_list.nb_ssb_per_index[ssb];
ra->new_ssb = ssb != ssb_idx ? true : false;
ra->ra_ssb = ssb_idx;
// TODO not sure how to handle the RO mask when it is limiting the RO occasions
AssertFatal(ra->ro_mask_index <= 0, "Handling of RACH occasion masking indication not implemented\n");
}
static bool check_mixed_slot_prach(frame_structure_t *fs, int slot, int start_prach, int end_prach)
{
bool is_mixed = is_mixed_slot(slot, fs);
if (is_mixed) {
tdd_bitmap_t *bitmap = &fs->period_cfg.tdd_slot_bitmap[slot % fs->numb_slots_period];
if (bitmap->num_ul_symbols == 0)
return false;
int ul_end = NR_SYMBOLS_PER_SLOT - 1;
int ul_start = NR_SYMBOLS_PER_SLOT - bitmap->num_ul_symbols;
if (start_prach < ul_start || end_prach > ul_end)
return false;
}
return true;
}
static void select_prach_occasion(RA_config_t *ra,
int nb_tx_ssb,
int n,
prach_occasion_info_t ra_occasions_period[n],
int num_ra_occasions_period)
{
unsigned int seed;
if (IS_SOFTMODEM_IQPLAYER || IS_SOFTMODEM_IQRECORDER) {
// Overwrite seed with non-random seed for IQ player/recorder
seed = 1;
} else {
// & to truncate the int64_t and keep only the LSB bits, up to sizeof(int)
seed = (unsigned int)(rdtsc_oai() & ~0);
}
int num_ros_per_ssb = 0;
int idx_ssb = 0;
int temp_idx = 0;
if (ra->ssb_ro_config.ssb_per_ro < 1) {
num_ros_per_ssb = (int)(1 / ra->ssb_ro_config.ssb_per_ro);
idx_ssb = (rand_r(&seed) % num_ros_per_ssb);
temp_idx = ra->ra_ssb * num_ros_per_ssb + idx_ssb;
} else {
int ssb_per_ro = nb_tx_ssb < ra->ssb_ro_config.ssb_per_ro ? nb_tx_ssb : ra->ssb_ro_config.ssb_per_ro;
num_ros_per_ssb = ra->association_periods * ssb_per_ro * num_ra_occasions_period / nb_tx_ssb;
idx_ssb = (rand_r(&seed) % num_ros_per_ssb);
int eq_ssb = ra->ra_ssb + (idx_ssb * nb_tx_ssb);
temp_idx = eq_ssb / ra->ssb_ro_config.ssb_per_ro;
}
int ro_index = temp_idx % num_ra_occasions_period;
int ass_period_idx = temp_idx / num_ra_occasions_period;
ra->sched_ro_info = ra_occasions_period[ro_index];
ra->sched_ro_info.association_period_idx = ass_period_idx;
}
static void configure_prach_occasions(NR_UE_MAC_INST_t *mac, int scs)
{
RA_config_t *ra = &mac->ra;
int num_ra_occasions_period = 0;
frame_structure_t *fs = &mac->frame_structure;
nr_prach_info_t prach_info = get_nr_prach_occasion_info_from_index(ra->ra_config_index, mac->frequency_range, fs->frame_type);
int max_num_occasions = prach_info.N_RA_sfn * prach_info.N_t_slot * prach_info.N_RA_slot * ra->num_fd_occasions;
prach_occasion_info_t ra_occasions_period[max_num_occasions];
// Number of PRACH slots within a subframe (or 60kHz slot) to be taken into account only for 30 and 120kHz
// as defined in 5.3.2 of 211
int prach_slots_in_sf = (scs == 1 || scs == 3) ? prach_info.N_RA_slot : 1;
uint64_t temp_s_map = prach_info.s_map;
int n_frames = prach_info.y2 == -1 ? 1 : 2;
for (int n = 0; n < n_frames; n++) {
int sf = 0;
for (int s = 0; s < prach_info.N_RA_sfn; s++) { // subframe/60kHz slot occasions in period
while (((temp_s_map >> sf) & 0x01) == 0)
sf++;
int sl = scs == 1 || scs == 3 ? sf * 2 : sf;
for (int i = 0; i < prach_slots_in_sf; i++) { // slot per subframe/60kHz slot
int add_slot = i;
if (scs == 1 || scs == 3) {
// if only 1 slot per subframe (or 60kHz slot) in case of 30 or 120kHz it's the odd one
// as defined in 5.3.2 of 211
if (((prach_info.format & 0xff) > 3) && prach_slots_in_sf == 1)
add_slot = 1;
}
int slot = sl + add_slot;
if (!is_ul_slot(slot, fs))
continue; // valid PRACH occasion only if slot is UL
for (int t = 0; t < prach_info.N_t_slot; t++) { // td occasions within a slot
// see 5.3.2 in 211
int temp_format = prach_info.format >> 8; // B1, B2 or B3 if A1/B1, A2/B2 or A3/B3 formats in tables
if (t != prach_info.N_t_slot - 1 || temp_format == 0xff)
temp_format = prach_info.format & 0xff;
int start_symbol = prach_info.start_symbol + t * prach_info.N_dur;
int end_symbol = start_symbol + prach_info.N_dur;
// valid occasion only if PRACH symbols are UL symbols in mixed slot
if (fs->frame_type == TDD && !check_mixed_slot_prach(fs, slot, start_symbol, end_symbol))
continue;
for (int f = 0; f < ra->num_fd_occasions; f++) { // fd occasions
ra_occasions_period[num_ra_occasions_period] =
(prach_occasion_info_t){.slot = slot,
.frame_info[0] = prach_info.x,
.frame_info[1] = n == 0 ? prach_info.y : prach_info.y2,
.start_symbol = start_symbol,
.fdm = f,
.format = temp_format};
LOG_D(NR_MAC,
"RA occasion %d: slot %d start symbol %d fd occasion %d\n",
num_ra_occasions_period,
slot,
start_symbol,
f);
num_ra_occasions_period++;
}
}
}
sf++;
}
}
int config_period = prach_info.x; // configuration period
ra->association_periods = 1;
int nb_eq_ssb = mac->ssb_list.nb_tx_ssb;
if (ra->ssb_ro_config.ssb_per_ro < 1)
nb_eq_ssb *= (int)(1 / ra->ssb_ro_config.ssb_per_ro);
int nb_eq_ro = num_ra_occasions_period;
if (ra->ssb_ro_config.ssb_per_ro > 1)
nb_eq_ro *= (int)ra->ssb_ro_config.ssb_per_ro;
while (nb_eq_ssb > nb_eq_ro) {
// not enough PRACH occasions -> need to increase association period
ra->association_periods <<= 1;
AssertFatal(ra->association_periods * config_period <= 16,
"Cannot find an association period for %d SSB and %d RO with %f SSB per RO\n",
mac->ssb_list.nb_tx_ssb,
num_ra_occasions_period,
ra->ssb_ro_config.ssb_per_ro);
nb_eq_ro <<= 1; // doubling the association period -> doubling ROs
}
LOG_D(NR_MAC, "PRACH configuration period %d association period %d\n", config_period, ra->association_periods);
select_prach_occasion(ra, mac->ssb_list.nb_tx_ssb, max_num_occasions, ra_occasions_period, num_ra_occasions_period);
const prach_occasion_info_t *pi = &ra->sched_ro_info;
LOG_I(NR_MAC,
"[UE %d] selected PRACH occasion: start_symbol %d fdm %d slot %d format %d\n",
mac->ue_id,
pi->start_symbol,
pi->fdm,
pi->slot,
pi->format);
}
/* TS 38.321 subclause 7.3 - return DELTA_PREAMBLE values in dB */
static int nr_get_delta_preamble(int scs, int prach_format)
{
int delta = 0;
switch (prach_format) {
case 0:
case 3:
delta = 0;
break;
case 1:
delta = -3;
break;
case 2:
delta = -6;
break;
case 0xa1:
case 0xb1:
delta = 8 + 3 * scs;
break;
case 0xa2:
case 0xb2:
case 0xc2:
delta = 5 + 3 * scs;
break;
case 0xa3:
case 0xb3:
delta = 3 + 3 * scs;
break;
case 0xb4:
delta = 3 * scs;
break;
case 0xc0:
delta = 11 + 3 * scs;
break;
default:
AssertFatal(false, "Invalid preamble format %x\n", prach_format);
}
return delta;
}
static int get_ra_preamble_rx_target_power(RA_config_t *ra, int scs)
{
NR_PRACH_RESOURCES_t *prach_resources = &ra->prach_resources;
int delta_preamble = nr_get_delta_preamble(scs, ra->sched_ro_info.format);
int tp = ra->preambleReceivedTargetPower_config + delta_preamble + prach_resources->power_offset_2step;
tp += (prach_resources->preamble_power_ramping_cnt - 1) * prach_resources->preamble_power_ramping_step;
return tp;
}
// 38.321 Sections 5.1.3 and .3a
static void ra_preamble_msga_transmission(RA_config_t *ra, int scs)
{
NR_PRACH_RESOURCES_t *prach_resources = &ra->prach_resources;
if (prach_resources->preamble_tx_counter > 1) {
// TODO if the notification of suspending power ramping counter has not been received from lower layers (not implemented)
// this happens if the L1 does not transmit PRACH of if the spacial filter changed (UE transmitting in another beam)
// TODO if LBT failure indication was not received from lower layers (LBT not implemented)
// if SSB or CSI-RS selected is not changed from the selection in the last Random Access Preamble transmission
if (!ra->new_ssb)
prach_resources->preamble_power_ramping_cnt++;
}
prach_resources->ra_preamble_rx_target_power = get_ra_preamble_rx_target_power(ra, scs);
// 3GPP TS 38.321 Section 5.1.3 says t_id for RA-RNTI depends on mu as specified in clause 5.3.2 in TS 38.211
// so mu = 0 for prach format < 4.
const int slot = ((ra->sched_ro_info.format & 0xff) < 4) ? ra->sched_ro_info.slot >> scs : ra->sched_ro_info.slot;
ra->ra_rnti = nr_get_ra_rnti(ra->sched_ro_info.start_symbol, slot, ra->sched_ro_info.fdm, 0);
if (ra->ra_type == RA_2_STEP)
ra->MsgB_rnti = nr_get_MsgB_rnti(ra->sched_ro_info.start_symbol, slot, ra->sched_ro_info.fdm, 0);
}
// 38.321 Section 5.1.2 Random Access Resource selection
void ra_resource_selection(NR_UE_MAC_INST_t *mac)
{
configure_ra_preamble(mac);
const NR_UE_UL_BWP_t *current_UL_BWP = mac->current_UL_BWP;
const int ul_mu = mac->current_UL_BWP->scs;
const int mu = nr_get_prach_or_ul_mu(mac->current_UL_BWP->msgA_ConfigCommon_r16, current_UL_BWP->rach_ConfigCommon, ul_mu);
configure_prach_occasions(mac, mu);
ra_preamble_msga_transmission(&mac->ra, mu);
}
static int nr_get_RA_window_2Step_v17(long msgB_ResponseWindow)
{
switch (msgB_ResponseWindow) {
case NR_RACH_ConfigGenericTwoStepRA_r16__ext1__msgB_ResponseWindow_v1700_sl240:
return 240;
break;
case NR_RACH_ConfigGenericTwoStepRA_r16__ext1__msgB_ResponseWindow_v1700_sl640:
return 640;
break;
case NR_RACH_ConfigGenericTwoStepRA_r16__ext1__msgB_ResponseWindow_v1700_sl960:
return 960;
break;
case NR_RACH_ConfigGenericTwoStepRA_r16__ext1__msgB_ResponseWindow_v1700_sl1280:
return 1280;
break;
case NR_RACH_ConfigGenericTwoStepRA_r16__ext1__msgB_ResponseWindow_v1700_sl1920:
return 1920;
break;
case NR_RACH_ConfigGenericTwoStepRA_r16__ext1__msgB_ResponseWindow_v1700_sl2560:
return 2590;
default:
AssertFatal(false, "illegal msgB_responseWindow value %ld\n", msgB_ResponseWindow);
break;
}
return 0;
}
static int nr_get_RA_window_2Step_v16(long msgB_ResponseWindow)
{
switch (msgB_ResponseWindow) {
case NR_RACH_ConfigGenericTwoStepRA_r16__msgB_ResponseWindow_r16_sl1:
return 1;
break;
case NR_RACH_ConfigGenericTwoStepRA_r16__msgB_ResponseWindow_r16_sl2:
return 2;
break;
case NR_RACH_ConfigGenericTwoStepRA_r16__msgB_ResponseWindow_r16_sl4:
return 4;
break;
case NR_RACH_ConfigGenericTwoStepRA_r16__msgB_ResponseWindow_r16_sl8:
return 8;
break;
case NR_RACH_ConfigGenericTwoStepRA_r16__msgB_ResponseWindow_r16_sl10:
return 10;
break;
case NR_RACH_ConfigGenericTwoStepRA_r16__msgB_ResponseWindow_r16_sl20:
return 20;
break;
case NR_RACH_ConfigGenericTwoStepRA_r16__msgB_ResponseWindow_r16_sl40:
return 40;
break;
case NR_RACH_ConfigGenericTwoStepRA_r16__msgB_ResponseWindow_r16_sl80:
return 80;
break;
case NR_RACH_ConfigGenericTwoStepRA_r16__msgB_ResponseWindow_r16_sl160:
return 160;
break;
case NR_RACH_ConfigGenericTwoStepRA_r16__msgB_ResponseWindow_r16_sl320:
return 360;
break;
default:
AssertFatal(false, "illegal msgB_responseWindow value %ld\n", msgB_ResponseWindow);
break;
}
return 0;
}
static int nr_get_RA_window_4Step_v16(long ra_ResponseWindow)
{
switch (ra_ResponseWindow) {
case NR_RACH_ConfigGeneric__ext1__ra_ResponseWindow_v1610_sl60:
return 60;
case NR_RACH_ConfigGeneric__ext1__ra_ResponseWindow_v1610_sl160:
return 160;
default:
AssertFatal(false, "illegal ra_ResponseWindow value %ld\n", ra_ResponseWindow);
}
return 0;
}
static int nr_get_RA_window_4Step_v17(long ra_ResponseWindow)
{
switch (ra_ResponseWindow) {
case NR_RACH_ConfigGeneric__ext2__ra_ResponseWindow_v1700_sl240:
return 240;
case NR_RACH_ConfigGeneric__ext2__ra_ResponseWindow_v1700_sl320:
return 320;
case NR_RACH_ConfigGeneric__ext2__ra_ResponseWindow_v1700_sl640:
return 640;
case NR_RACH_ConfigGeneric__ext2__ra_ResponseWindow_v1700_sl960:
return 960;
case NR_RACH_ConfigGeneric__ext2__ra_ResponseWindow_v1700_sl1280:
return 1280;
case NR_RACH_ConfigGeneric__ext2__ra_ResponseWindow_v1700_sl1920:
return 1920;
case NR_RACH_ConfigGeneric__ext2__ra_ResponseWindow_v1700_sl2560:
return 2590;
default:
AssertFatal(false, "illegal msgB_responseWindow value %ld\n", ra_ResponseWindow);
}
return 0;
}
static int nr_get_RA_window_4Step(long ra_ResponseWindow)
{
switch (ra_ResponseWindow) {
case NR_RACH_ConfigGeneric__ra_ResponseWindow_sl1:
return 1;
break;
case NR_RACH_ConfigGeneric__ra_ResponseWindow_sl2:
return 2;
break;
case NR_RACH_ConfigGeneric__ra_ResponseWindow_sl4:
return 4;
break;
case NR_RACH_ConfigGeneric__ra_ResponseWindow_sl8:
return 8;
break;
case NR_RACH_ConfigGeneric__ra_ResponseWindow_sl10:
return 10;
break;
case NR_RACH_ConfigGeneric__ra_ResponseWindow_sl20:
return 20;
break;
case NR_RACH_ConfigGeneric__ra_ResponseWindow_sl40:
return 40;
break;
case NR_RACH_ConfigGeneric__ra_ResponseWindow_sl80:
return 80;
break;
default:
AssertFatal(false, "illegal ra_ResponseWindow value %ld\n", ra_ResponseWindow);
break;
}
return 0;
}
static void setup_ra_response_window(NR_UE_MAC_INST_t *mac,
RA_config_t *ra,
NR_RACH_ConfigGeneric_t *configGeneric,
NR_RACH_ConfigGenericTwoStepRA_r16_t *twostep)
{
const int slots_per_frame = mac->frame_structure.numb_slots_frame;
int respwind_value = 0;
if (ra->ra_type == RA_2_STEP) {
long *msgB_ResponseWindow = twostep->msgB_ResponseWindow_r16;
if (msgB_ResponseWindow)
respwind_value = nr_get_RA_window_2Step_v16(*msgB_ResponseWindow);
if (twostep->ext1 && twostep->ext1->msgB_ResponseWindow_v1700) {
AssertFatal(msgB_ResponseWindow == NULL,
"The network does not configure msgB-ResponseWindow-r16 simultaneously with msgB-ResponseWindow-v1700\n");
msgB_ResponseWindow = twostep->ext1->msgB_ResponseWindow_v1700;
if (msgB_ResponseWindow)
respwind_value = nr_get_RA_window_2Step_v17(*msgB_ResponseWindow);
}
// The network configures a value lower than or equal to 40ms
int slots_40ms = 4 * slots_per_frame;
AssertFatal(respwind_value != 0 && respwind_value <= slots_40ms, "Invalid MSGB response window value\n");
} else {
AssertFatal(ra->ra_type == RA_4_STEP, "Invalid RA type\n");
long *response_window = NULL;
if (configGeneric->ext1 && configGeneric->ext1->ra_ResponseWindow_v1610)
respwind_value = nr_get_RA_window_4Step_v16(*configGeneric->ext1->ra_ResponseWindow_v1610);
if (configGeneric->ext2 && configGeneric->ext2->ra_ResponseWindow_v1700) {
AssertFatal(response_window == NULL, "ra_ResponseWindow_v1610 and ra_ResponseWindow_v1700 are mutually exclusive\n");
respwind_value = nr_get_RA_window_4Step_v17(*configGeneric->ext2->ra_ResponseWindow_v1700);
}
if (!response_window)
respwind_value = nr_get_RA_window_4Step(configGeneric->ra_ResponseWindow);
// The network configures a value lower than or equal to 10ms
AssertFatal(respwind_value != 0, "Invalid RAR response window value\n");
if (respwind_value > slots_per_frame)
LOG_E(NR_MAC, "RA-ResponseWindow need to be configured to a value lower than or equal to 10 ms\n");
}
const int ntn_ue_koffset = GET_NTN_UE_K_OFFSET(&mac->phy_config.config_req.ntn_config, mac->current_UL_BWP->scs);
ra->response_window_setup_time = respwind_value + ntn_ue_koffset;
}
// Random Access procedure initialization as per 5.1.1 and initialization of variables specific
// to Random Access type as specified in clause 5.1.1a (3GPP TS 38.321 version 16.2.1 Release 16)
bool init_RA(NR_UE_MAC_INST_t *mac)
{
RA_config_t *ra = &mac->ra;
LOG_D(NR_MAC, "Initialization of RA\n");
ra->ra_state = nrRA_GENERATE_PREAMBLE;
// TODO this piece of code is required to compute MSG3_size that is used by ra_preambles_config function
// Not a good implementation, it needs improvements
if (!IS_SA_MODE(get_softmodem_params()))
ra->Msg3_size = sizeof(uint16_t) + sizeof(NR_MAC_SUBHEADER_FIXED);
// Random acces procedure initialization
mac->state = UE_PERFORMING_RA;
ra->RA_active = true;
NR_PRACH_RESOURCES_t *prach_resources = &ra->prach_resources;
fapi_nr_config_request_t *cfg = &mac->phy_config.config_req;
// flush MSG3 buffer
free_and_zero(ra->Msg3_buffer);
// set the PREAMBLE_TRANSMISSION_COUNTER to 1
prach_resources->preamble_tx_counter = 1;
// set the PREAMBLE_POWER_RAMPING_COUNTER to 1
prach_resources->preamble_power_ramping_cnt = 1;
// set POWER_OFFSET_2STEP_RA to 0 dB
prach_resources->power_offset_2step = 0;
// perform the BWP operation as specified in clause 5.15
// if PRACH occasions are not configured for the active UL BWP
if (!mac->current_UL_BWP->rach_ConfigCommon) {
// switch the active UL BWP to BWP indicated by initialUplinkBWP
mac->current_UL_BWP = get_ul_bwp_structure(mac, 0, false);
// if the Serving Cell is an SpCell
// switch the active DL BWP to BWP indicated by initialDownlinkBWP
mac->current_DL_BWP = get_dl_bwp_structure(mac, 0, false);
} else {
// if the active DL BWP does not have the same bwp-Id as the active UL BWP
if (mac->current_UL_BWP->bwp_id != mac->current_DL_BWP->bwp_id) {
// switch the active DL BWP to the DL BWP with the same bwp-Id as the active UL BWP
mac->current_DL_BWP = get_dl_bwp_structure(mac, 0, false);
}
}
const NR_UE_UL_BWP_t *current_UL_BWP = mac->current_UL_BWP;
NR_RACH_ConfigCommon_t *nr_rach_ConfigCommon = current_UL_BWP->rach_ConfigCommon;
AssertFatal(nr_rach_ConfigCommon, "rach-ConfigCommon should be configured here\n");
// stop the bwp-InactivityTimer associated with the active DL BWP of this Serving Cell, if running
// TODO bwp-InactivityTimer not implemented
// if the carrier to use for the Random Access procedure is explicitly signalled (always the case for us)
// PRACH shall be as specified for QPSK modulated DFT-s-OFDM of equivalent RB allocation (38.101-1)
NR_SubcarrierSpacing_t prach_scs;
int scs_for_pcmax; // for long prach the UL BWP SCS is used for calculating RA_PCMAX
NR_RACH_ConfigGeneric_t *rach_ConfigGeneric = &nr_rach_ConfigCommon->rach_ConfigGeneric;
if (nr_rach_ConfigCommon->msg1_SubcarrierSpacing) {
prach_scs = *nr_rach_ConfigCommon->msg1_SubcarrierSpacing;
scs_for_pcmax = prach_scs;
} else {
const unsigned int index = rach_ConfigGeneric->prach_ConfigurationIndex;
const unsigned int unpaired = mac->phy_config.config_req.cell_config.frame_duplex_type;
const unsigned int format = get_format0(index, unpaired, mac->frequency_range);
prach_scs = get_delta_f_RA_long(format);
scs_for_pcmax = mac->current_UL_BWP->scs;
}
int n_prbs = get_N_RA_RB(prach_scs, current_UL_BWP->scs);
int start_prb = rach_ConfigGeneric->msg1_FrequencyStart + current_UL_BWP->BWPStart;
prach_resources->Pc_max = nr_get_Pcmax(mac->p_Max,
mac->nr_band,
mac->frame_structure.frame_type,
mac->frequency_range,
current_UL_BWP->channel_bandwidth,
2,
false,
scs_for_pcmax,
cfg->carrier_config.dl_grid_size[scs_for_pcmax],
true,
n_prbs,
start_prb);
// TODO if the Random Access procedure was initiated for SI request
// and the Random Access Resources for SI request have been explicitly provided by RRC
// TODO if the Random Access procedure was initiated for SpCell beam failure recovery
// and if the contention-free Random Access Resources for beam failure recovery request for 4-step RA type
// have been explicitly provided by RRC for the BWP selected for Random Access procedure
NR_RACH_ConfigCommonTwoStepRA_r16_t *twostep_conf = NULL;
NR_RACH_ConfigDedicated_t *rach_Dedicated = ra->rach_ConfigDedicated;
// if the Random Access procedure is initiated by PDCCH order
// and if the ra-PreambleIndex explicitly provided by PDCCH is not 0b000000
bool pdcch_order = (ra->pdcch_order.active && ra->pdcch_order.preamble_index != 0xb000000) ? true : false;
// or if the Random Access procedure was initiated for reconfiguration with sync and
// if the contention-free Random Access Resources for 4-step RA type have been explicitly provided
// in rach-ConfigDedicated for the BWP selected for Random Access procedure
if ((rach_Dedicated && rach_Dedicated->cfra) || pdcch_order) {
LOG_I(MAC, "Initialization of 4-Step CFRA procedure\n");
ra->ra_type = RA_4_STEP;
ra->cfra = true;
} else {
bool twostep_cfra = (rach_Dedicated && rach_Dedicated->ext1) ? (rach_Dedicated->ext1->cfra_TwoStep_r16 ? true : false) : false;
if (twostep_cfra) {
// if the Random Access procedure was initiated for reconfiguration with sync and
// if the contention-free Random Access Resources for 2-step RA type have been explicitly provided in rach-ConfigDedicated
LOG_I(MAC, "Initialization of 2-Step CFRA procedure\n");
ra->ra_type = RA_2_STEP;
ra->cfra = true;
} else {
bool twostep = false;
if (current_UL_BWP->msgA_ConfigCommon_r16) {
twostep_conf = &current_UL_BWP->msgA_ConfigCommon_r16->rach_ConfigCommonTwoStepRA_r16;
if (nr_rach_ConfigCommon) {
// if the BWP selected for Random Access procedure is configured with both 2- and 4-step RA type Random Access Resources
// and the RSRP of the downlink pathloss reference is above msgA-RSRP-Threshold
AssertFatal(twostep_conf->msgA_RSRP_Threshold_r16,
"msgA_RSRP_Threshold_r16 is mandatory present if both 2-step and 4-step random access types are configured\n");
// For thresholds the RSRP value is (IE value 156) dBm except for the IE 127 in which case the actual value is infinity
int rsrp_msga_thr = *twostep_conf->msgA_RSRP_Threshold_r16 - 156;
if (*twostep_conf->msgA_RSRP_Threshold_r16 != 127 && mac->ssb_measurements[mac->mib_ssb].ssb_rsrp_dBm > rsrp_msga_thr)
twostep = true;
} else {
// if the BWP selected for Random Access procedure is only configured with 2-step RA type Random Access
twostep = true;
}
}
if (twostep) {
LOG_I(MAC, "Initialization of 2-Step CBRA procedure\n");
ra->ra_type = RA_2_STEP;
ra->cfra = false;
} else {
LOG_I(MAC, "Initialization of 4-Step CBRA procedure\n");
ra->ra_type = RA_4_STEP;
ra->cfra = false;
}
}
}
// need to ask RRC to send the MSG3 data to transmit if needed (CBRA originating from upper layers)
if (!mac->msg3_C_RNTI && ra->cfra == false)
nr_mac_rrc_msg3_ind(mac->ue_id, 0, true);
NR_RACH_ConfigGenericTwoStepRA_r16_t *twostep_generic = twostep_conf ? &twostep_conf->rach_ConfigGenericTwoStepRA_r16 : NULL;
if (ra->ra_type == RA_2_STEP && twostep_generic && twostep_generic->msgA_PreamblePowerRampingStep_r16)
prach_resources->preamble_power_ramping_step = *twostep_generic->msgA_PreamblePowerRampingStep_r16 << 1;
else
prach_resources->preamble_power_ramping_step = rach_ConfigGeneric->powerRampingStep << 1;
ra->scaling_factor_bi = 1;
if (ra->ra_type == RA_2_STEP && twostep_generic && twostep_generic->msgA_PreambleReceivedTargetPower_r16)
ra->preambleReceivedTargetPower_config = *twostep_generic->msgA_PreambleReceivedTargetPower_r16;
else
ra->preambleReceivedTargetPower_config = rach_ConfigGeneric->preambleReceivedTargetPower;
if (ra->ra_type == RA_2_STEP && twostep_generic && twostep_generic->preambleTransMax_r16)
set_preambleTransMax(ra, *twostep_generic->preambleTransMax_r16);
else
set_preambleTransMax(ra, rach_ConfigGeneric->preambleTransMax);
if (ra->ra_type == RA_2_STEP && twostep_generic && twostep_generic->msgA_ZeroCorrelationZoneConfig_r16)
ra->zeroCorrelationZoneConfig = *twostep_generic->msgA_ZeroCorrelationZoneConfig_r16;
else
ra->zeroCorrelationZoneConfig = rach_ConfigGeneric->zeroCorrelationZoneConfig;
if (ra->ra_type == RA_2_STEP && twostep_conf && twostep_conf->msgA_RestrictedSetConfig_r16)
ra->restricted_set_config = *twostep_conf->msgA_RestrictedSetConfig_r16;
else
ra->restricted_set_config = nr_rach_ConfigCommon->restrictedSetConfig;
// TODO msgA-TransMax not implemented
// TODO ra-Prioritization not implemented
if (ra->ra_type == RA_2_STEP && twostep_generic && twostep_generic->msgA_PreambleReceivedTargetPower_r16)
ra->preambleRxTargetPower = *twostep_generic->msgA_PreambleReceivedTargetPower_r16;
else
ra->preambleRxTargetPower = rach_ConfigGeneric->preambleReceivedTargetPower;
if (ra->ra_type == RA_2_STEP
&& current_UL_BWP->msgA_ConfigCommon_r16
&& current_UL_BWP->msgA_ConfigCommon_r16->msgA_PUSCH_Config_r16
&& current_UL_BWP->msgA_ConfigCommon_r16->msgA_PUSCH_Config_r16->msgA_DeltaPreamble_r16)
ra->msg3_deltaPreamble = *current_UL_BWP->msgA_ConfigCommon_r16->msgA_PUSCH_Config_r16->msgA_DeltaPreamble_r16 << 1;
else
ra->msg3_deltaPreamble = mac->current_UL_BWP->msg3_DeltaPreamble ? *mac->current_UL_BWP->msg3_DeltaPreamble << 1 : 0;
ra->new_ssb = false;
if (ra->ra_type == RA_2_STEP && twostep_generic && twostep_generic->msgA_PRACH_ConfigurationIndex_r16)
ra->ra_config_index = *twostep_generic->msgA_PRACH_ConfigurationIndex_r16;
else {
if (rach_ConfigGeneric->ext1 && rach_ConfigGeneric->ext1->prach_ConfigurationIndex_v1610)
ra->ra_config_index = *rach_ConfigGeneric->ext1->prach_ConfigurationIndex_v1610;
else
ra->ra_config_index = rach_ConfigGeneric->prach_ConfigurationIndex;
}
if (ra->ra_type == RA_2_STEP && twostep_generic && twostep_generic->msgA_RO_FDM_r16)
ra->num_fd_occasions = 1 << *twostep_generic->msgA_RO_FDM_r16;
else
ra->num_fd_occasions = 1 << rach_ConfigGeneric->msg1_FDM;
setup_ra_response_window(mac, ra, rach_ConfigGeneric, twostep_generic);
return true;
}
void nr_Msg3_transmitted(NR_UE_MAC_INST_t *mac)
{
RA_config_t *ra = &mac->ra;
NR_RACH_ConfigCommon_t *nr_rach_ConfigCommon = mac->current_UL_BWP->rach_ConfigCommon;
const int ntn_ue_koffset = GET_NTN_UE_K_OFFSET(&mac->phy_config.config_req.ntn_config, mac->current_UL_BWP->scs);
const int slots_per_ms = mac->frame_structure.numb_slots_frame / 10;
// start contention resolution timer
const int RA_contention_resolution_timer_ms = (nr_rach_ConfigCommon->ra_ContentionResolutionTimer + 1) << 3;
const int RA_contention_resolution_timer_slots = RA_contention_resolution_timer_ms * slots_per_ms;
// timer step 1 slot and timer target given by ra_ContentionResolutionTimer + ta-Common-r17
nr_timer_setup(&ra->contention_resolution_timer, RA_contention_resolution_timer_slots + ntn_ue_koffset, 1);
nr_timer_start(&ra->contention_resolution_timer);
ra->ra_state = nrRA_WAIT_CONTENTION_RESOLUTION;
}
static uint8_t *fill_msg3_crnti_pdu(RA_config_t *ra, uint8_t *pdu, uint16_t crnti)
{
// RA triggered by UE MAC with C-RNTI in MAC CE
LOG_D(NR_MAC, "Generating MAC CE with C-RNTI for MSG3 %x\n", crnti);
*(NR_MAC_SUBHEADER_FIXED *)pdu = (NR_MAC_SUBHEADER_FIXED){.LCID = UL_SCH_LCID_C_RNTI, .R = 0};
pdu += sizeof(NR_MAC_SUBHEADER_FIXED);
// C-RNTI MAC CE (2 octets)
uint16_t rnti_pdu = ((crnti & 0xFF) << 8) | ((crnti >> 8) & 0xFF);
memcpy(pdu, &rnti_pdu, sizeof(rnti_pdu));
pdu += sizeof(rnti_pdu);
ra->t_crnti = crnti;
return pdu;
}
static uint8_t *fill_msg3_pdu_from_rlc(NR_UE_MAC_INST_t *mac, uint8_t *pdu, int TBS_max)
{
RA_config_t *ra = &mac->ra;
// regular Msg3/MsgA_PUSCH with PDU coming from higher layers
*(NR_MAC_SUBHEADER_FIXED *)pdu = (NR_MAC_SUBHEADER_FIXED){.LCID = UL_SCH_LCID_CCCH_48_BITS};
pdu += sizeof(NR_MAC_SUBHEADER_FIXED);
tbs_size_t len = nr_mac_rlc_data_req(mac->ue_id,
mac->ue_id,
false,
0, // SRB0 for messages sent in MSG3
TBS_max - sizeof(NR_MAC_SUBHEADER_FIXED), /* size of mac_ce above */
(char *)pdu);
AssertFatal(len > 0, "no data for Msg3/MsgA_PUSCH\n");
// UE Contention Resolution Identity
// Store the first 48 bits belonging to the uplink CCCH SDU within Msg3 to determine whether or not the
// Random Access Procedure has been successful after reception of Msg4
// We copy from persisted memory to another persisted memory
memcpy(ra->cont_res_id, pdu, sizeof(uint8_t) * 6);
pdu += len;
return pdu;
}
void nr_get_Msg3_MsgA_PUSCH_payload(NR_UE_MAC_INST_t *mac, uint8_t *buf, int TBS_max)
{
RA_config_t *ra = &mac->ra;
// we already stored MSG3 in the buffer, we can use that
if (ra->Msg3_buffer) {
memcpy(buf, ra->Msg3_buffer, sizeof(uint8_t) * TBS_max);
return;
}
uint8_t *pdu = buf;
if (mac->msg3_C_RNTI)
pdu = fill_msg3_crnti_pdu(ra, pdu, mac->crnti);
else
pdu = fill_msg3_pdu_from_rlc(mac, pdu, TBS_max);
AssertFatal(TBS_max >= pdu - buf, "Allocated resources are not enough for Msg3/MsgA_PUSCH!\n");
// Padding: fill remainder with 0
LOG_D(NR_MAC, "Remaining %ld bytes, filling with padding\n", pdu - buf);
while (pdu < buf + TBS_max - sizeof(NR_MAC_SUBHEADER_FIXED)) {
*(NR_MAC_SUBHEADER_FIXED *)pdu = (NR_MAC_SUBHEADER_FIXED){.LCID = UL_SCH_LCID_PADDING};
pdu += sizeof(NR_MAC_SUBHEADER_FIXED);
}
ra->Msg3_buffer = calloc_or_fail(TBS_max, sizeof(uint8_t));
memcpy(ra->Msg3_buffer, buf, sizeof(uint8_t) * TBS_max);
}
// Handlig successful RA completion @ MAC layer
// according to section 5 of 3GPP TS 38.321 version 16.2.1 Release 16
// todo:
// - complete handling of received contention-based RA preamble
void nr_ra_succeeded(NR_UE_MAC_INST_t *mac, const frame_t frame, const int slot)
{
RA_config_t *ra = &mac->ra;
if (ra->cfra) {
LOG_A(NR_MAC, "[UE %d][%d.%d][RAPROC] RA procedure succeeded. CFRA: RAR successfully received.\n", mac->ue_id, frame, slot);
} else {
LOG_A(NR_MAC,
"[UE %d][%d.%d][RAPROC] %d-Step RA procedure succeeded. CBRA: Contention Resolution is successful.\n",
mac->ue_id,
frame,
slot,
ra->ra_type == RA_2_STEP ? 2 : 4);
mac->crnti = ra->t_crnti;
ra->t_crnti = 0;
nr_timer_stop(&ra->contention_resolution_timer);
}
// rach-ConfigDedicated is one shot configuration to be used only for a specific instance of reconfiguration with sync
if (ra->rach_ConfigDedicated)
asn1cFreeStruc(asn_DEF_NR_RACH_ConfigDedicated, ra->rach_ConfigDedicated);
ra->RA_active = false;
mac->msg3_C_RNTI = false;
ra->ra_state = nrRA_SUCCEEDED;
mac->state = UE_CONNECTED;
free_and_zero(ra->Msg3_buffer);
nr_mac_rrc_ra_ind(mac->ue_id, true);
}
void nr_ra_backoff_setting(RA_config_t *ra)
{
// select a random backoff time according to a uniform distribution
// between 0 and the PREAMBLE_BACKOFF
uint32_t seed = (unsigned int)(rdtsc_oai() & ~0);
uint32_t random_backoff = ra->RA_backoff_limit ? rand_r(&seed) % ra->RA_backoff_limit : 0; // in slots
nr_timer_setup(&ra->RA_backoff_timer, random_backoff, 1);
nr_timer_start(&ra->RA_backoff_timer);
}
void nr_ra_contention_resolution_failed(NR_UE_MAC_INST_t *mac)
{
LOG_W(MAC, "[UE %d] Contention resolution failed\n", mac->ue_id);
RA_config_t *ra = &mac->ra;
// discard the TEMPORARY_C-RNTI
ra->t_crnti = 0;
// flush MSG3 buffer
free_and_zero(ra->Msg3_buffer);
// MSG3 with C-RNTI is a L2 procedure, we shouldn't send any indication to RRC
if (!mac->msg3_C_RNTI)
nr_mac_rrc_msg3_ind(mac->ue_id, 0, true);
NR_PRACH_RESOURCES_t *prach_resources = &ra->prach_resources;
prach_resources->preamble_tx_counter++;
if (prach_resources->preamble_tx_counter == ra->preambleTransMax + 1) {
// indicate a Random Access problem to upper layers
nr_mac_rrc_ra_ind(mac->ue_id, false);
} else {
// TODO handle msgA-TransMax (go back to 4-step if the threshold is reached)
// starting backoff time
nr_ra_backoff_setting(ra);
}
}
void nr_rar_not_successful(NR_UE_MAC_INST_t *mac)
{
LOG_W(MAC, "[UE %d] RAR reception failed\n", mac->ue_id);
RA_config_t *ra = &mac->ra;
NR_PRACH_RESOURCES_t *prach_resources = &ra->prach_resources;
prach_resources->preamble_tx_counter++;
bool ra_completed = false;
if (prach_resources->preamble_tx_counter == ra->preambleTransMax + 1) {
// if the Random Access Preamble is transmitted on the SpCell
// TODO to be verified, this means SA if I'm not mistaken
if (IS_SA_MODE(get_softmodem_params())) {
// indicate a Random Access problem to upper layers
nr_mac_rrc_ra_ind(mac->ue_id, false);
} else {
// if the Random Access Preamble is transmitted on an SCell:
// consider the Random Access procedure unsuccessfully completed.
ra_completed = true;
ra->ra_state = nrRA_UE_IDLE;
}
}
if (!ra_completed) {
nr_ra_backoff_setting(ra);
}
}
void trigger_MAC_UE_RA(NR_UE_MAC_INST_t *mac, dci_pdu_rel15_t *pdcch_order)
{
LOG_W(NR_MAC, "Triggering new RA procedure for UE with RNTI %x\n", mac->crnti);
mac->state = UE_PERFORMING_RA;
reset_ra(mac, false);
RA_config_t *ra = &mac->ra;
mac->msg3_C_RNTI = true;
if (pdcch_order) {
ra->pdcch_order.active = true;
ra->pdcch_order.preamble_index = pdcch_order->ra_preamble_index;
ra->pdcch_order.ssb_index = pdcch_order->ss_pbch_index;
ra->pdcch_order.prach_mask = pdcch_order->prach_mask_index;
}
}
void prepare_msg4_msgb_feedback(NR_UE_MAC_INST_t *mac, int pid, int ack_nack)
{
NR_UE_DL_HARQ_STATUS_t *current_harq = &mac->dl_harq_info[pid];
int sched_slot = current_harq->ul_slot;
int sched_frame = current_harq->ul_frame;
PUCCH_sched_t pucch = {.n_CCE = current_harq->n_CCE,
.N_CCE = current_harq->N_CCE,
.ack_payload = ack_nack,
.n_harq = 1};
current_harq->active = false;
current_harq->ack_received = false;
const NR_UE_UL_BWP_t *current_UL_BWP = mac->current_UL_BWP;
configure_initial_pucch(&pucch, current_harq->pucch_resource_indicator, current_UL_BWP->pucch_ConfigCommon->pucch_ResourceCommon);
RA_config_t *ra = &mac->ra;
ra->ra_pucch = calloc_or_fail(1, sizeof(*ra->ra_pucch));
ra->ra_pucch->pucch_sched = pucch;
ra->ra_pucch->sched_frame = sched_frame;
ra->ra_pucch->sched_slot = sched_slot;
}
void reset_ra(NR_UE_MAC_INST_t *nr_mac, bool free_prach)
{
RA_config_t *ra = &nr_mac->ra;
if (ra->rach_ConfigDedicated)
asn1cFreeStruc(asn_DEF_NR_RACH_ConfigDedicated, ra->rach_ConfigDedicated);
memset(ra, 0, sizeof(RA_config_t));
if (!free_prach)
return;
}