Files
openairinterface5g/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c
T

960 lines
36 KiB
C

/*
* 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 ra_procedures.c
* \brief Routines for UE MAC-layer Random Access procedures (TS 38.321, Release 15)
* \author R. Knopp, Navid Nikaein, Guido Casati
* \date 2019
* \version 0.1
* \company Eurecom
* \email: knopp@eurecom.fr navid.nikaein@eurecom.fr, guido.casati@iis.fraunhofer.de
* \note
* \warning
*/
/* RRC */
#include "RRC/NR_UE/rrc_proto.h"
/* MAC */
#include "LAYER2/NR_MAC_COMMON/nr_mac_extern.h"
#include "NR_MAC_COMMON/nr_mac.h"
#include "LAYER2/NR_MAC_UE/mac_proto.h"
#include <executables/softmodem-common.h>
#include "openair2/LAYER2/RLC/rlc.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, mac->ssb_measurements.ssb_rsrp_dBm);
int16_t ra_preamble_rx_power = (int16_t)(ra->prach_resources.ra_PREAMBLE_RECEIVED_TARGET_POWER + pathloss);
return min(ra->prach_resources.RA_PCMAX, ra_preamble_rx_power);
}
// 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)
// todo:
// - check if carrier to use is explicitly signalled then do (1) RA CARRIER SELECTION (SUL, NUL) (2) set PCMAX (currently hardcoded to 0)
void init_RA(NR_UE_MAC_INST_t *mac,
NR_PRACH_RESOURCES_t *prach_resources,
NR_RACH_ConfigCommon_t *nr_rach_ConfigCommon,
NR_RACH_ConfigGeneric_t *rach_ConfigGeneric,
NR_RACH_ConfigDedicated_t *rach_ConfigDedicated)
{
mac->state = UE_PERFORMING_RA;
RA_config_t *ra = &mac->ra;
ra->RA_active = true;
ra->ra_PreambleIndex = -1;
ra->RA_usedGroupA = 1;
ra->RA_RAPID_found = 0;
ra->preambleTransMax = 0;
ra->first_Msg3 = true;
ra->starting_preamble_nb = 0;
ra->RA_backoff_cnt = 0;
ra->RA_window_cnt = -1;
fapi_nr_config_request_t *cfg = &mac->phy_config.config_req;
prach_resources->RA_PREAMBLE_BACKOFF = 0;
AssertFatal(nr_rach_ConfigCommon, "Cannot handle scenario without nr_rach_ConfigCommon\n");
NR_SubcarrierSpacing_t prach_scs = *nr_rach_ConfigCommon->msg1_SubcarrierSpacing;
int n_prbs = get_N_RA_RB(prach_scs, mac->current_UL_BWP->scs);
int start_prb = rach_ConfigGeneric->msg1_FrequencyStart + mac->current_UL_BWP->BWPStart;
// PRACH shall be as specified for QPSK modulated DFT-s-OFDM of equivalent RB allocation (38.101-1)
prach_resources->RA_PCMAX = nr_get_Pcmax(mac->p_Max,
mac->nr_band,
mac->frame_type,
mac->frequency_range,
2,
false,
prach_scs,
cfg->carrier_config.dl_grid_size[prach_scs],
true,
n_prbs,
start_prb);
prach_resources->RA_PREAMBLE_TRANSMISSION_COUNTER = 1;
prach_resources->RA_PREAMBLE_POWER_RAMPING_COUNTER = 1;
prach_resources->POWER_OFFSET_2STEP_RA = 0;
prach_resources->RA_SCALING_FACTOR_BI = 1;
if (rach_ConfigDedicated) {
if (rach_ConfigDedicated->cfra){
LOG_I(NR_MAC, "Initialization of 2-step contention-free random access procedure\n");
prach_resources->RA_TYPE = RA_2STEP;
ra->cfra = 1;
} else if (rach_ConfigDedicated->ext1){
if (rach_ConfigDedicated->ext1->cfra_TwoStep_r16){
LOG_I(NR_MAC, "Setting RA type to 2-step...\n");
prach_resources->RA_TYPE = RA_2STEP;
ra->cfra = 1;
} else {
LOG_E(NR_MAC, "Config not handled\n");
}
} else {
LOG_E(NR_MAC, "Config not handled\n");
}
} else {
LOG_I(NR_MAC, "Initialization of 4-step contention-based random access procedure\n");
prach_resources->RA_TYPE = RA_4STEP;
ra->cfra = 0;
}
switch (rach_ConfigGeneric->powerRampingStep){ // in dB
case 0:
prach_resources->RA_PREAMBLE_POWER_RAMPING_STEP = 0;
break;
case 1:
prach_resources->RA_PREAMBLE_POWER_RAMPING_STEP = 2;
break;
case 2:
prach_resources->RA_PREAMBLE_POWER_RAMPING_STEP = 4;
break;
case 3:
prach_resources->RA_PREAMBLE_POWER_RAMPING_STEP = 6;
break;
}
switch (rach_ConfigGeneric->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;
}
if (nr_rach_ConfigCommon->ext1) {
if (nr_rach_ConfigCommon->ext1->ra_PrioritizationForAccessIdentity_r16){
LOG_D(MAC, "Missing implementation for Access Identity initialization procedures\n");
}
}
}
/* TS 38.321 subclause 7.3 - return DELTA_PREAMBLE values in dB */
static int8_t nr_get_DELTA_PREAMBLE(NR_UE_MAC_INST_t *mac, int CC_id, uint16_t prach_format)
{
NR_RACH_ConfigCommon_t *nr_rach_ConfigCommon = mac->current_UL_BWP->rach_ConfigCommon;
NR_SubcarrierSpacing_t scs = *nr_rach_ConfigCommon->msg1_SubcarrierSpacing;
int prach_sequence_length = nr_rach_ConfigCommon->prach_RootSequenceIndex.present - 1;
uint8_t prachConfigIndex, mu;
AssertFatal(CC_id == 0, "Transmission on secondary CCs is not supported yet\n");
// SCS configuration from msg1_SubcarrierSpacing and table 4.2-1 in TS 38.211
switch (scs){
case NR_SubcarrierSpacing_kHz15:
mu = 0;
break;
case NR_SubcarrierSpacing_kHz30:
mu = 1;
break;
case NR_SubcarrierSpacing_kHz60:
mu = 2;
break;
case NR_SubcarrierSpacing_kHz120:
mu = 3;
break;
case NR_SubcarrierSpacing_kHz240:
mu = 4;
break;
case NR_SubcarrierSpacing_kHz480_v1700:
mu = 5;
break;
case NR_SubcarrierSpacing_kHz960_v1700:
mu = 6;
break;
case NR_SubcarrierSpacing_spare1:
mu = 7;
break;
default:
AssertFatal(1 == 0,"Unknown msg1_SubcarrierSpacing %lu\n", scs);
}
// Preamble formats given by prach_ConfigurationIndex and tables 6.3.3.2-2 and 6.3.3.2-2 in TS 38.211
prachConfigIndex = nr_rach_ConfigCommon->rach_ConfigGeneric.prach_ConfigurationIndex;
if (prach_sequence_length == 0) {
AssertFatal(prach_format < 4, "Illegal PRACH format %d for sequence length 839\n", prach_format);
switch (prach_format) {
// long preamble formats
case 0:
case 3:
return 0;
case 1:
return -3;
case 2:
return -6;
}
} else {
switch (prach_format) { // short preamble formats
case 0:
case 3:
return 8 + 3*mu;
case 1:
case 4:
case 8:
return 5 + 3*mu;
case 2:
case 5:
return 3 + 3*mu;
case 6:
return 3*mu;
case 7:
return 5 + 3*mu;
default:
AssertFatal(1 == 0, "[UE %d] ue_procedures.c: FATAL, Illegal preambleFormat %d, prachConfigIndex %d\n",
mac->ue_id,
prach_format,
prachConfigIndex);
}
}
return 0;
}
// TS 38.321 subclause 5.1.3 - RA preamble transmission - ra_PREAMBLE_RECEIVED_TARGET_POWER configuration
// Measurement units:
// - preambleReceivedTargetPower dBm (-202..-60, 2 dBm granularity)
// - delta_preamble dB
// - RA_PREAMBLE_POWER_RAMPING_STEP dB
// - POWER_OFFSET_2STEP_RA dB
// returns receivedTargerPower in dBm
static int nr_get_Po_NOMINAL_PUSCH(NR_UE_MAC_INST_t *mac, NR_PRACH_RESOURCES_t *prach_resources, uint8_t CC_id)
{
int8_t receivedTargerPower;
int8_t delta_preamble;
NR_RACH_ConfigCommon_t *nr_rach_ConfigCommon = mac->current_UL_BWP->rach_ConfigCommon;
long preambleReceivedTargetPower = nr_rach_ConfigCommon->rach_ConfigGeneric.preambleReceivedTargetPower;
delta_preamble = nr_get_DELTA_PREAMBLE(mac, CC_id, prach_resources->prach_format);
receivedTargerPower = preambleReceivedTargetPower +
delta_preamble +
(prach_resources->RA_PREAMBLE_POWER_RAMPING_COUNTER - 1) * prach_resources->RA_PREAMBLE_POWER_RAMPING_STEP +
prach_resources->POWER_OFFSET_2STEP_RA;
LOG_D(MAC, "ReceivedTargerPower is %d dBm \n", receivedTargerPower);
return receivedTargerPower;
}
void ssb_rach_config(RA_config_t *ra, NR_PRACH_RESOURCES_t *prach_resources, NR_RACH_ConfigCommon_t *nr_rach_ConfigCommon)
{
// Determine the SSB to RACH mapping ratio
// =======================================
NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR ssb_perRACH_config = nr_rach_ConfigCommon->ssb_perRACH_OccasionAndCB_PreamblesPerSSB->present;
bool 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
int total_preambles_per_ssb;
uint8_t ssb_nb_in_ro;
int numberOfRA_Preambles = 64;
switch (ssb_perRACH_config){
case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_oneEighth:
multiple_ssb_per_ro = false;
ssb_rach_ratio = 8;
ra->cb_preambles_per_ssb = 4 * (nr_rach_ConfigCommon->ssb_perRACH_OccasionAndCB_PreamblesPerSSB->choice.oneEighth + 1);
break;
case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_oneFourth:
multiple_ssb_per_ro = false;
ssb_rach_ratio = 4;
ra->cb_preambles_per_ssb = 4 * (nr_rach_ConfigCommon->ssb_perRACH_OccasionAndCB_PreamblesPerSSB->choice.oneFourth + 1);
break;
case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_oneHalf:
multiple_ssb_per_ro = false;
ssb_rach_ratio = 2;
ra->cb_preambles_per_ssb = 4 * (nr_rach_ConfigCommon->ssb_perRACH_OccasionAndCB_PreamblesPerSSB->choice.oneHalf + 1);
break;
case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_one:
multiple_ssb_per_ro = true;
ssb_rach_ratio = 1;
ra->cb_preambles_per_ssb = 4 * (nr_rach_ConfigCommon->ssb_perRACH_OccasionAndCB_PreamblesPerSSB->choice.one + 1);
break;
case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_two:
multiple_ssb_per_ro = true;
ssb_rach_ratio = 2;
ra->cb_preambles_per_ssb = 4 * (nr_rach_ConfigCommon->ssb_perRACH_OccasionAndCB_PreamblesPerSSB->choice.two + 1);
break;
case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_four:
multiple_ssb_per_ro = true;
ssb_rach_ratio = 4;
ra->cb_preambles_per_ssb = nr_rach_ConfigCommon->ssb_perRACH_OccasionAndCB_PreamblesPerSSB->choice.four;
break;
case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_eight:
multiple_ssb_per_ro = true;
ssb_rach_ratio = 8;
ra->cb_preambles_per_ssb = nr_rach_ConfigCommon->ssb_perRACH_OccasionAndCB_PreamblesPerSSB->choice.eight;
break;
case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_sixteen:
multiple_ssb_per_ro = true;
ssb_rach_ratio = 16;
ra->cb_preambles_per_ssb = nr_rach_ConfigCommon->ssb_perRACH_OccasionAndCB_PreamblesPerSSB->choice.sixteen;
break;
default:
AssertFatal(1 == 0, "Unsupported ssb_perRACH_config %d\n", ssb_perRACH_config);
}
if (nr_rach_ConfigCommon->totalNumberOfRA_Preambles)
numberOfRA_Preambles = *(nr_rach_ConfigCommon->totalNumberOfRA_Preambles);
// Compute the proper Preamble selection params according to the selected SSB and the ssb_perRACH_OccasionAndCB_PreamblesPerSSB configuration
if ((true == multiple_ssb_per_ro) && (ssb_rach_ratio > 1)) {
total_preambles_per_ssb = numberOfRA_Preambles / ssb_rach_ratio;
ssb_nb_in_ro = ra->ssb_nb_in_ro;
ra->starting_preamble_nb = total_preambles_per_ssb * ssb_nb_in_ro;
} else {
total_preambles_per_ssb = numberOfRA_Preambles;
ra->starting_preamble_nb = 0;
}
}
// This routine implements RA preamble configuration according to
// section 5.1 (Random Access procedure) of 3GPP TS 38.321 version 16.2.1 Release 16
void ra_preambles_config(NR_PRACH_RESOURCES_t *prach_resources, NR_UE_MAC_INST_t *mac, int16_t dl_pathloss){
int messageSizeGroupA = 0;
int sizeOfRA_PreamblesGroupA = 0;
int messagePowerOffsetGroupB = 0;
int PLThreshold = 0;
long deltaPreamble_Msg3 = 0;
uint8_t noGroupB = 0;
// 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);
}
RA_config_t *ra = &mac->ra;
NR_RACH_ConfigCommon_t *setup = mac->current_UL_BWP->rach_ConfigCommon;
NR_RACH_ConfigGeneric_t *rach_ConfigGeneric = &setup->rach_ConfigGeneric;
if (mac->current_UL_BWP->msg3_DeltaPreamble) {
deltaPreamble_Msg3 = (*mac->current_UL_BWP->msg3_DeltaPreamble) * 2; // dB
LOG_D(MAC, "In %s: deltaPreamble_Msg3 set to %ld\n", __FUNCTION__, deltaPreamble_Msg3);
}
if (!setup->groupBconfigured) {
noGroupB = 1;
LOG_D(MAC, "In %s:%d: preambles group B is not configured...\n", __FUNCTION__, __LINE__);
} else {
// RA preambles group B is configured
// - Random Access Preambles group B is configured for 4-step RA type
// - Defining the number of RA preambles in RA Preamble Group A for each SSB
LOG_D(MAC, "In %s:%d: preambles group B is configured...\n", __FUNCTION__, __LINE__);
sizeOfRA_PreamblesGroupA = setup->groupBconfigured->numberOfRA_PreamblesGroupA;
switch (setup->groupBconfigured->ra_Msg3SizeGroupA){
/* - Threshold to determine the groups of RA preambles */
case 0:
messageSizeGroupA = 56;
break;
case 1:
messageSizeGroupA = 144;
break;
case 2:
messageSizeGroupA = 208;
break;
case 3:
messageSizeGroupA = 256;
break;
case 4:
messageSizeGroupA = 282;
break;
case 5:
messageSizeGroupA = 480;
break;
case 6:
messageSizeGroupA = 640;
break;
case 7:
messageSizeGroupA = 800;
break;
case 8:
messageSizeGroupA = 1000;
break;
case 9:
messageSizeGroupA = 72;
break;
default:
AssertFatal(1 == 0, "Unknown ra_Msg3SizeGroupA %lu\n", setup->groupBconfigured->ra_Msg3SizeGroupA);
/* todo cases 10 -15*/
}
/* Power offset for preamble selection in dB */
messagePowerOffsetGroupB = -9999;
switch (setup->groupBconfigured->messagePowerOffsetGroupB){
case 0:
messagePowerOffsetGroupB = -9999;
break;
case 1:
messagePowerOffsetGroupB = 0;
break;
case 2:
messagePowerOffsetGroupB = 5;
break;
case 3:
messagePowerOffsetGroupB = 8;
break;
case 4:
messagePowerOffsetGroupB = 10;
break;
case 5:
messagePowerOffsetGroupB = 12;
break;
case 6:
messagePowerOffsetGroupB = 15;
break;
case 7:
messagePowerOffsetGroupB = 18;
break;
default:
AssertFatal(1 == 0,"Unknown messagePowerOffsetGroupB %lu\n", setup->groupBconfigured->messagePowerOffsetGroupB);
}
PLThreshold = prach_resources->RA_PCMAX - rach_ConfigGeneric->preambleReceivedTargetPower - deltaPreamble_Msg3 - messagePowerOffsetGroupB;
}
/* Msg3 has not been transmitted yet */
if (ra->first_Msg3) {
if(ra->ra_PreambleIndex < 0 || ra->ra_PreambleIndex > 63) {
if (noGroupB) {
// use Group A preamble
ra->ra_PreambleIndex = ra->starting_preamble_nb + (rand_r(&seed) % ra->cb_preambles_per_ssb);
ra->RA_usedGroupA = 1;
} else if ((ra->Msg3_size < messageSizeGroupA) && (dl_pathloss > PLThreshold)) {
// Group B is configured and RA preamble Group A is used
// - todo add condition on CCCH_sdu_size for initiation by CCCH
ra->ra_PreambleIndex = ra->starting_preamble_nb + (rand_r(&seed) % sizeOfRA_PreamblesGroupA);
ra->RA_usedGroupA = 1;
} else {
// Group B preamble is configured and used
// the first sizeOfRA_PreamblesGroupA RA preambles belong to RA Preambles Group A
// the remaining belong to RA Preambles Group B
ra->ra_PreambleIndex = ra->starting_preamble_nb + sizeOfRA_PreamblesGroupA + (rand_r(&seed) % (ra->cb_preambles_per_ssb - sizeOfRA_PreamblesGroupA));
ra->RA_usedGroupA = 0;
}
}
} else { // Msg3 is being retransmitted
if (ra->RA_usedGroupA && noGroupB) {
ra->ra_PreambleIndex = ra->starting_preamble_nb + (rand_r(&seed) % ra->cb_preambles_per_ssb);
} else if (ra->RA_usedGroupA && !noGroupB){
ra->ra_PreambleIndex = ra->starting_preamble_nb + (rand_r(&seed) % sizeOfRA_PreamblesGroupA);
} else {
ra->ra_PreambleIndex = ra->starting_preamble_nb + sizeOfRA_PreamblesGroupA + (rand_r(&seed) % (ra->cb_preambles_per_ssb - sizeOfRA_PreamblesGroupA));
}
}
}
// This routine implements Section 5.1.2 (UE Random Access Resource Selection)
// and Section 5.1.3 (Random Access Preamble Transmission) from 3GPP TS 38.321
// - currently the PRACH preamble is set through RRC configuration for 4-step CFRA mode
// todo:
// - determine next available PRACH occasion
// -- if RA initiated for SI request and ra_AssociationPeriodIndex and si-RequestPeriod are configured
// -- else if SSB is selected above
// -- else if CSI-RS is selected above
// - switch initialisation cases
// -- RA initiated by beam failure recovery operation (subclause 5.17 TS 38.321)
// --- SSB selection, set ra_PreambleIndex
// -- RA initiated by PDCCH: ra_preamble_index provided by PDCCH && ra_PreambleIndex != 0b000000
// --- set PREAMBLE_INDEX to ra_preamble_index
// --- select the SSB signalled by PDCCH
// -- RA initiated for SI request:
// --- SSB selection, set ra_PreambleIndex
// - condition on notification of suspending power ramping counter from lower layer (5.1.3 TS 38.321)
// - check if SSB or CSI-RS have not changed since the selection in the last RA Preamble tranmission
// - Contention-based RA preamble selection:
// -- selection of SSB with SS-RSRP above rsrp-ThresholdSSB else select any SSB
void nr_get_prach_resources(NR_UE_MAC_INST_t *mac,
int CC_id,
uint8_t gNB_id,
NR_PRACH_RESOURCES_t *prach_resources,
NR_RACH_ConfigDedicated_t *rach_ConfigDedicated)
{
RA_config_t *ra = &mac->ra;
NR_RACH_ConfigCommon_t *nr_rach_ConfigCommon = mac->current_UL_BWP->rach_ConfigCommon;
LOG_D(MAC, "Getting PRACH resources frame (first_Msg3 %d)\n", ra->first_Msg3);
if (rach_ConfigDedicated) {
if (rach_ConfigDedicated->cfra){
uint8_t cfra_ssb_resource_idx = 0;
ra->ra_PreambleIndex = rach_ConfigDedicated->cfra->resources.choice.ssb->ssb_ResourceList.list.array[cfra_ssb_resource_idx]->ra_PreambleIndex;
LOG_D(MAC, "Selected RA preamble index %d for contention-free random access procedure for SSB with Id %d\n",
ra->ra_PreambleIndex,
cfra_ssb_resource_idx);
}
} else {
/* TODO: This controls the tx_power of UE and the ramping procedure of RA of UE. Later we
can abstract this, perhaps in the proxy. But for the time being lets leave it as below. */
int16_t dl_pathloss = !get_softmodem_params()->emulate_l1 ? compute_nr_SSB_PL(mac, mac->ssb_measurements.ssb_rsrp_dBm) : 0;
ssb_rach_config(ra, prach_resources, nr_rach_ConfigCommon);
ra_preambles_config(prach_resources, mac, dl_pathloss);
LOG_D(MAC, "[RAPROC] - Selected RA preamble index %d for contention-based random access procedure... \n", ra->ra_PreambleIndex);
}
if (prach_resources->RA_PREAMBLE_TRANSMISSION_COUNTER > 1)
prach_resources->RA_PREAMBLE_POWER_RAMPING_COUNTER++;
prach_resources->ra_PREAMBLE_RECEIVED_TARGET_POWER = nr_get_Po_NOMINAL_PUSCH(mac, prach_resources, CC_id);
}
// TbD: RA_attempt_number not used
void nr_Msg1_transmitted(NR_UE_MAC_INST_t *mac)
{
RA_config_t *ra = &mac->ra;
ra->ra_state = nrRA_WAIT_RAR;
ra->RA_attempt_number++;
}
void nr_Msg3_transmitted(NR_UE_MAC_INST_t *mac, uint8_t CC_id, frame_t frameP, slot_t slotP, uint8_t gNB_id)
{
RA_config_t *ra = &mac->ra;
NR_RACH_ConfigCommon_t *nr_rach_ConfigCommon = mac->current_UL_BWP->rach_ConfigCommon;
long mu = mac->current_UL_BWP->scs;
int subframes_per_slot = nr_slots_per_frame[mu] / 10;
// start contention resolution timer
int RA_contention_resolution_timer_subframes = (nr_rach_ConfigCommon->ra_ContentionResolutionTimer + 1) << 3;
// timer step 1 slot and timer target given by ra_ContentionResolutionTimer
nr_timer_setup(&ra->contention_resolution_timer, RA_contention_resolution_timer_subframes * subframes_per_slot, 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 with PDU coming from higher layers
*(NR_MAC_SUBHEADER_FIXED *)pdu = (NR_MAC_SUBHEADER_FIXED){.LCID = UL_SCH_LCID_CCCH};
pdu += sizeof(NR_MAC_SUBHEADER_FIXED);
tbs_size_t len = mac_rlc_data_req(mac->ue_id,
mac->ue_id,
0,
0,
ENB_FLAG_NO,
MBMS_FLAG_NO,
0, // SRB0 for messages sent in MSG3
TBS_max - sizeof(NR_MAC_SUBHEADER_FIXED), /* size of mac_ce above */
(char *)pdu,
0,
0);
AssertFatal(len > 0, "no data for Msg.3\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_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 (ra->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!\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(TBS_max, sizeof(uint8_t));
memcpy(ra->Msg3_buffer, buf, sizeof(uint8_t) * TBS_max);
}
/**
* Function: handles Random Access Preamble Initialization (5.1.1 TS 38.321)
* handles Random Access Response reception (5.1.4 TS 38.321)
* Note: In SA mode the Msg3 contains a CCCH SDU, therefore no C-RNTI MAC CE is transmitted.
*
* @prach_resources pointer to PRACH resources
* @prach_pdu pointer to FAPI UL PRACH PDU
* @mod_id module ID
* @CC_id CC ID
* @frame current UL TX frame
* @gNB_id gNB ID
* @nr_slot_tx current UL TX slot
*/
void nr_ue_get_rach(NR_UE_MAC_INST_t *mac, int CC_id, frame_t frame, uint8_t gNB_id, int nr_slot_tx)
{
RA_config_t *ra = &mac->ra;
NR_RACH_ConfigDedicated_t *rach_ConfigDedicated = ra->rach_ConfigDedicated;
NR_PRACH_RESOURCES_t *prach_resources = &ra->prach_resources;
// Delay init RA procedure to allow the convergence of the IIR filter on PRACH noise measurements at gNB side
if (ra->ra_state == nrRA_UE_IDLE) {
if ((mac->first_sync_frame > -1 || get_softmodem_params()->do_ra || get_softmodem_params()->nsa) &&
((MAX_FRAME_NUMBER + frame - mac->first_sync_frame) % MAX_FRAME_NUMBER) > 150) {
ra->ra_state = nrRA_GENERATE_PREAMBLE;
} else {
LOG_D(NR_MAC,"PRACH Condition not met: ra state %d, frame %d, sync_frame %d\n", ra->ra_state, frame, mac->first_sync_frame);
return;
}
}
LOG_D(NR_MAC, "[UE %d][%d.%d]: ra_state %d, RA_active %d\n", mac->ue_id, frame, nr_slot_tx, ra->ra_state, ra->RA_active);
if (ra->ra_state > nrRA_UE_IDLE && ra->ra_state < nrRA_SUCCEEDED) {
if (!ra->RA_active) {
NR_RACH_ConfigCommon_t *setup = mac->current_UL_BWP->rach_ConfigCommon;
NR_RACH_ConfigGeneric_t *rach_ConfigGeneric = &setup->rach_ConfigGeneric;
init_RA(mac, &ra->prach_resources, setup, rach_ConfigGeneric, ra->rach_ConfigDedicated);
// 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
int size_sdu = 0;
// Concerning the C-RNTI MAC CE, it has to be included if the UL transmission (Msg3) is not being made for the CCCH logical channel.
// Therefore it has been assumed that this event only occurs only when RA is done and it is not SA mode.
if (get_softmodem_params()->nsa) {
uint8_t mac_sdus[34*1056];
uint16_t sdu_lengths[NB_RB_MAX] = {0};
int TBS_bytes = 848;
int mac_ce_len = 0;
unsigned short post_padding = 1;
// fill ulsch_buffer with random data
for (int i = 0; i < TBS_bytes; i++){
mac_sdus[i] = (unsigned char) (rand()&0xff);
}
//Sending SDUs with size 1
//Initialize elements of sdu_lengths
sdu_lengths[0] = TBS_bytes - 3 - post_padding - mac_ce_len;
size_sdu += sdu_lengths[0];
if (size_sdu > 0) {
memcpy(ra->cont_res_id, mac_sdus, sizeof(uint8_t) * 6);
ra->Msg3_size = size_sdu + sizeof(NR_MAC_SUBHEADER_SHORT) + sizeof(NR_MAC_SUBHEADER_SHORT);
}
} else if (!get_softmodem_params()->sa) {
uint8_t temp_pdu[16] = {0};
size_sdu = nr_write_ce_ulsch_pdu(temp_pdu, mac, 0, &(mac->crnti), NULL, NULL, NULL);
ra->Msg3_size = size_sdu;
}
} else if (ra->RA_window_cnt != -1) { // RACH is active
LOG_D(MAC, "[%d.%d] RA is active: RA window count %d, RA backoff count %d\n", frame, nr_slot_tx, ra->RA_window_cnt, ra->RA_backoff_cnt);
if (ra->RA_BI_found){
prach_resources->RA_PREAMBLE_BACKOFF = prach_resources->RA_SCALING_FACTOR_BI * ra->RA_backoff_indicator;
} else {
prach_resources->RA_PREAMBLE_BACKOFF = 0;
}
if (ra->RA_window_cnt >= 0 && ra->RA_RAPID_found == 1) {
if(ra->cfra) {
// Reset RA_active flag: it disables Msg3 retransmission (8.3 of TS 38.213)
nr_ra_succeeded(mac, gNB_id, frame, nr_slot_tx);
}
} else if (ra->RA_window_cnt == 0 && !ra->RA_RAPID_found) {
LOG_W(MAC, "[UE %d][%d:%d] RAR reception failed \n", mac->ue_id, frame, nr_slot_tx);
nr_ra_failed(mac, CC_id, prach_resources, frame, nr_slot_tx);
} else if (ra->RA_window_cnt > 0) {
LOG_D(MAC, "[UE %d][%d.%d]: RAR not received yet (RA window count %d) \n", mac->ue_id, frame, nr_slot_tx, ra->RA_window_cnt);
// Fill in preamble and PRACH resources
ra->RA_window_cnt--;
if (ra->ra_state == nrRA_GENERATE_PREAMBLE) {
nr_get_prach_resources(mac, CC_id, gNB_id, prach_resources, rach_ConfigDedicated);
}
} else if (ra->RA_backoff_cnt > 0) {
LOG_D(MAC, "[UE %d][%d.%d]: RAR not received yet (RA backoff count %d) \n", mac->ue_id, frame, nr_slot_tx, ra->RA_backoff_cnt);
ra->RA_backoff_cnt--;
if ((ra->RA_backoff_cnt > 0 && ra->ra_state == nrRA_GENERATE_PREAMBLE) || ra->RA_backoff_cnt == 0) {
nr_get_prach_resources(mac, CC_id, gNB_id, prach_resources, rach_ConfigDedicated);
}
}
}
}
if (is_nr_timer_active(ra->contention_resolution_timer)) {
nr_ue_contention_resolution(mac, CC_id, frame, nr_slot_tx, prach_resources);
}
}
void nr_get_RA_window(NR_UE_MAC_INST_t *mac)
{
RA_config_t *ra = &mac->ra;
NR_RACH_ConfigCommon_t *setup = mac->current_UL_BWP->rach_ConfigCommon;
AssertFatal(&setup->rach_ConfigGeneric != NULL, "In %s: FATAL! rach_ConfigGeneric is NULL...\n", __FUNCTION__);
NR_RACH_ConfigGeneric_t *rach_ConfigGeneric = &setup->rach_ConfigGeneric;
int ra_ResponseWindow = rach_ConfigGeneric->ra_ResponseWindow;
int mu = mac->current_DL_BWP->scs;
ra->RA_window_cnt = ra->RA_offset * nr_slots_per_frame[mu]; // taking into account the 2 frames gap introduced by OAI gNB
switch (ra_ResponseWindow) {
case NR_RACH_ConfigGeneric__ra_ResponseWindow_sl1:
ra->RA_window_cnt += 1;
break;
case NR_RACH_ConfigGeneric__ra_ResponseWindow_sl2:
ra->RA_window_cnt += 2;
break;
case NR_RACH_ConfigGeneric__ra_ResponseWindow_sl4:
ra->RA_window_cnt += 4;
break;
case NR_RACH_ConfigGeneric__ra_ResponseWindow_sl8:
ra->RA_window_cnt += 8;
break;
case NR_RACH_ConfigGeneric__ra_ResponseWindow_sl10:
ra->RA_window_cnt += 10;
break;
case NR_RACH_ConfigGeneric__ra_ResponseWindow_sl20:
ra->RA_window_cnt += 20;
break;
case NR_RACH_ConfigGeneric__ra_ResponseWindow_sl40:
ra->RA_window_cnt += 40;
break;
case NR_RACH_ConfigGeneric__ra_ResponseWindow_sl80:
ra->RA_window_cnt += 80;
break;
}
}
////////////////////////////////////////////////////////////////////////////
/////////* Random Access Contention Resolution (5.1.35 TS 38.321) */////////
////////////////////////////////////////////////////////////////////////////
// Handling contention resolution timer
// WIP todo:
// - beam failure recovery
// - RA completed
void nr_ue_contention_resolution(NR_UE_MAC_INST_t *mac, int cc_id, frame_t frame, int slot, NR_PRACH_RESOURCES_t *prach_resources)
{
RA_config_t *ra = &mac->ra;
if (nr_timer_expired(ra->contention_resolution_timer)) {
ra->t_crnti = 0;
nr_timer_stop(&ra->contention_resolution_timer);
// Signal PHY to quit RA procedure
LOG_E(MAC, "[UE %d] CB-RA: Contention resolution timer has expired, RA procedure has failed...\n", mac->ue_id);
nr_ra_failed(mac, cc_id, prach_resources, frame, slot);
}
}
// 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 uint8_t gNB_index, const frame_t frame, const int slot)
{
RA_config_t *ra = &mac->ra;
if (ra->cfra) {
LOG_I(MAC, "[UE %d][%d.%d][RAPROC] RA procedure succeeded. CF-RA: RAR successfully received.\n", mac->ue_id, frame, slot);
ra->RA_window_cnt = -1;
} else {
LOG_A(MAC, "[UE %d][%d.%d][RAPROC] RA procedure succeeded. CB-RA: Contention Resolution is successful.\n", mac->ue_id, frame, slot);
nr_timer_stop(&ra->contention_resolution_timer);
mac->crnti = ra->t_crnti;
ra->t_crnti = 0;
LOG_D(MAC, "[UE %d][%d.%d] CB-RA: cleared contention resolution timer...\n", mac->ue_id, frame, slot);
}
LOG_D(MAC, "[UE %d] clearing RA_active flag...\n", mac->ue_id);
ra->RA_active = false;
ra->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, frame, true);
}
// Handling failure of RA procedure @ 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_failed(NR_UE_MAC_INST_t *mac, uint8_t CC_id, NR_PRACH_RESOURCES_t *prach_resources, frame_t frame, int slot)
{
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);
}
ra->first_Msg3 = true;
ra->ra_PreambleIndex = -1;
ra->ra_state = nrRA_UE_IDLE;
prach_resources->RA_PREAMBLE_TRANSMISSION_COUNTER++;
if (prach_resources->RA_PREAMBLE_TRANSMISSION_COUNTER == ra->preambleTransMax + 1){
LOG_D(MAC, "[UE %d][%d.%d] Maximum number of RACH attempts (%d) reached, selecting backoff time...\n",
mac->ue_id,
frame,
slot,
ra->preambleTransMax);
ra->RA_backoff_cnt = rand_r(&seed) % (prach_resources->RA_PREAMBLE_BACKOFF + 1);
prach_resources->RA_PREAMBLE_TRANSMISSION_COUNTER = 1;
prach_resources->RA_PREAMBLE_POWER_RAMPING_STEP += 2; // 2 dB increment
prach_resources->ra_PREAMBLE_RECEIVED_TARGET_POWER = nr_get_Po_NOMINAL_PUSCH(mac, prach_resources, CC_id);
} else {
// Resetting RA window
nr_get_RA_window(mac);
}
}
void schedule_RA_after_SR_failure(NR_UE_MAC_INST_t *mac)
{
LOG_W(NR_MAC, "Triggering new RA procedure for UE with RNTI %x\n", mac->crnti);
mac->state = UE_SYNC;
reset_ra(mac, false);
mac->ra.msg3_C_RNTI = true;
// release PUCCH for all Serving Cells;
// release SRS for all Serving Cells;
release_PUCCH_SRS(mac);
// clear any configured downlink assignments and uplink grants;
int scs = mac->current_UL_BWP->scs;
if (mac->dl_config_request)
memset(mac->dl_config_request, 0, sizeof(*mac->dl_config_request));
if (mac->ul_config_request)
clear_ul_config_request(mac, scs);
// clear any PUSCH resources for semi-persistent CSI reporting
// TODO we don't have semi-persistent CSI reporting
}
void prepare_msg4_feedback(NR_UE_MAC_INST_t *mac, int pid, int ack_nack)
{
NR_UE_HARQ_STATUS_t *current_harq = &mac->dl_harq_info[pid];
int sched_slot = current_harq->ul_slot;
int sched_frame = current_harq->ul_frame;
mac->nr_ue_emul_l1.num_harqs = 1;
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;
if (get_softmodem_params()->emulate_l1) {
mac->nr_ue_emul_l1.harq[pid].active = true;
mac->nr_ue_emul_l1.harq[pid].active_dl_harq_sfn = sched_frame;
mac->nr_ue_emul_l1.harq[pid].active_dl_harq_slot = sched_slot;
}
fapi_nr_ul_config_request_pdu_t *pdu = lockGet_ul_config(mac, sched_frame, sched_slot, FAPI_NR_UL_CONFIG_TYPE_PUCCH);
if (!pdu)
return;
int ret = nr_ue_configure_pucch(mac, sched_slot, sched_frame, mac->ra.t_crnti, &pucch, &pdu->pucch_config_pdu);
if (ret != 0)
remove_ul_config_last_item(pdu);
release_ul_config(pdu, false);
}