Files
openairinterface5g/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c
T
Jaroslava Fiedlerova 2ca61b02f1 Merge remote-tracking branch 'origin/count_bits_with_mask' into integration_2025_w30 (!3532)
Count bits in variable with mask

Adding a function to count bit using a mask to set starting and ending point
in the variable
2025-07-23 11:07:49 +02:00

2183 lines
95 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 gNB_scheduler_RA.c
* \brief primitives used for random access
* \author Guido Casati
* \date 2019
* \email: guido.casati@iis.fraunhofer.de
* \version
*/
#include "common/platform_types.h"
#include "uper_decoder.h"
/* MAC */
#include "nr_mac_gNB.h"
#include "NR_MAC_gNB/mac_proto.h"
#include "openair2/COMMON/mac_messages_types.h"
/* Utils */
#include "common/utils/LOG/log.h"
#include "common/utils/LOG/vcd_signal_dumper.h"
#include "common/utils/nr/nr_common.h"
#include "UTIL/OPT/opt.h"
/* rlc */
#include "openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h"
#include <executables/softmodem-common.h>
static void nr_fill_rar(uint8_t Mod_idP, NR_UE_info_t *UE, uint8_t *dlsch_buffer, nfapi_nr_pusch_pdu_t *pusch_pdu);
static const float ssb_per_rach_occasion[8] = {0.125, 0.25, 0.5, 1, 2, 4, 8};
static int16_t ssb_index_from_prach(module_id_t module_idP,
frame_t frameP,
slot_t slotP,
uint16_t preamble_index,
uint8_t freq_index,
uint8_t symbol)
{
gNB_MAC_INST *gNB = RC.nrmac[module_idP];
NR_COMMON_channels_t *cc = &gNB->common_channels[0];
NR_ServingCellConfigCommon_t *scc = cc->ServingCellConfigCommon;
nfapi_nr_config_request_scf_t *cfg = &RC.nrmac[module_idP]->config[0];
NR_RACH_ConfigCommon_t *rach_ConfigCommon = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup;
uint8_t config_index = rach_ConfigCommon->rach_ConfigGeneric.prach_ConfigurationIndex;
uint8_t fdm = cfg->prach_config.num_prach_fd_occasions.value;
uint8_t total_RApreambles = MAX_NUM_NR_PRACH_PREAMBLES;
if (rach_ConfigCommon->totalNumberOfRA_Preambles != NULL)
total_RApreambles = *rach_ConfigCommon->totalNumberOfRA_Preambles;
float num_ssb_per_RO = ssb_per_rach_occasion[cfg->prach_config.ssb_per_rach.value];
uint16_t start_symbol_index = 0;
uint8_t temp_start_symbol = 0;
uint16_t RA_sfn_index = -1;
uint16_t prach_occasion_id = -1;
uint8_t num_active_ssb = cc->num_active_ssb;
NR_MsgA_ConfigCommon_r16_t *msgacc = NULL;
if (scc->uplinkConfigCommon->initialUplinkBWP->ext1 && scc->uplinkConfigCommon->initialUplinkBWP->ext1->msgA_ConfigCommon_r16)
msgacc = scc->uplinkConfigCommon->initialUplinkBWP->ext1->msgA_ConfigCommon_r16->choice.setup;
const int ul_mu = scc->uplinkConfigCommon->frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing;
const int mu = nr_get_prach_or_ul_mu(msgacc, rach_ConfigCommon, ul_mu);
frequency_range_t freq_range = get_freq_range_from_arfcn(scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencyPointA);
get_nr_prach_sched_from_info(cc->prach_info, config_index, frameP, slotP, mu, freq_range, &RA_sfn_index, cc->frame_type);
uint8_t index = 0, slot_index = 0;
for (slot_index = 0; slot_index < cc->prach_info.N_RA_slot; slot_index++) {
if (cc->prach_info.N_RA_slot <= 1) { // 1 PRACH slot in a subframe
if((mu == 1) || (mu == 3))
slot_index = 1; // For scs = 30khz and 120khz
}
for (int i = 0; i < cc->prach_info.N_t_slot; i++) {
temp_start_symbol = (cc->prach_info.start_symbol + i * cc->prach_info.N_dur + 14 * slot_index) % 14;
if(symbol == temp_start_symbol) {
start_symbol_index = i;
break;
}
}
}
if (cc->prach_info.N_RA_slot <= 1) { // 1 PRACH slot in a subframe
if((mu == 1) || (mu == 3))
slot_index = 0; // For scs = 30khz and 120khz
}
int config_period = cc->prach_info.x;
// prach_occasion_id = subframe_index * N_t_slot * N_RA_slot * fdm + N_RA_slot_index * N_t_slot * fdm + freq_index + fdm * start_symbol_index;
prach_occasion_id =
(((frameP % (cc->max_association_period * config_period)) / config_period) * cc->total_prach_occasions_per_config_period)
+ (RA_sfn_index + slot_index) * cc->prach_info.N_t_slot * fdm + start_symbol_index * fdm + freq_index;
//one SSB have more than one continuous RO
if(num_ssb_per_RO <= 1)
index = (int) (prach_occasion_id / (int)(1 / num_ssb_per_RO)) % num_active_ssb;
//one RO is shared by one or more SSB
else if (num_ssb_per_RO > 1) {
index = (prach_occasion_id * (int)num_ssb_per_RO) % num_active_ssb;
for(int j = 0; j < num_ssb_per_RO; j++) {
if(preamble_index < ((j + 1) * cc->cb_preambles_per_ssb))
index = index + j;
}
}
LOG_D(NR_MAC, "Frame %d, Slot %d: Prach Occasion id = %d ssb per RO = %f number of active SSB %u index = %d fdm %u symbol index %u freq_index %u total_RApreambles %u\n",
frameP, slotP, prach_occasion_id, num_ssb_per_RO, num_active_ssb, index, fdm, start_symbol_index, freq_index, total_RApreambles);
return index;
}
//Compute Total active SSBs and RO available
void find_SSB_and_RO_available(gNB_MAC_INST *nrmac)
{
/* already mutex protected through nr_mac_config_scc() */
//NR_SCHED_ENSURE_LOCKED(&nrmac->sched_lock);
NR_COMMON_channels_t *cc = &nrmac->common_channels[0];
NR_ServingCellConfigCommon_t *scc = cc->ServingCellConfigCommon;
nfapi_nr_config_request_scf_t *cfg = &nrmac->config[0];
NR_RACH_ConfigCommon_t *rach_ConfigCommon = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup;
uint8_t config_index = rach_ConfigCommon->rach_ConfigGeneric.prach_ConfigurationIndex;
uint16_t unused_RA_occasion, repetition = 0;
uint8_t num_active_ssb = 0;
struct NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB *ssb_perRACH_OccasionAndCB_PreamblesPerSSB = rach_ConfigCommon->ssb_perRACH_OccasionAndCB_PreamblesPerSSB;
switch (ssb_perRACH_OccasionAndCB_PreamblesPerSSB->present) {
case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_oneEighth:
cc->cb_preambles_per_ssb = 4 * (ssb_perRACH_OccasionAndCB_PreamblesPerSSB->choice.oneEighth + 1);
break;
case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_oneFourth:
cc->cb_preambles_per_ssb = 4 * (ssb_perRACH_OccasionAndCB_PreamblesPerSSB->choice.oneFourth + 1);
break;
case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_oneHalf:
cc->cb_preambles_per_ssb = 4 * (ssb_perRACH_OccasionAndCB_PreamblesPerSSB->choice.oneHalf + 1);
break;
case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_one:
cc->cb_preambles_per_ssb = 4 * (ssb_perRACH_OccasionAndCB_PreamblesPerSSB->choice.one + 1);
break;
case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_two:
cc->cb_preambles_per_ssb = 4 * (ssb_perRACH_OccasionAndCB_PreamblesPerSSB->choice.two + 1);
break;
case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_four:
cc->cb_preambles_per_ssb = ssb_perRACH_OccasionAndCB_PreamblesPerSSB->choice.four;
break;
case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_eight:
cc->cb_preambles_per_ssb = ssb_perRACH_OccasionAndCB_PreamblesPerSSB->choice.eight;
break;
case NR_RACH_ConfigCommon__ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR_sixteen:
cc->cb_preambles_per_ssb = ssb_perRACH_OccasionAndCB_PreamblesPerSSB->choice.sixteen;
break;
default:
AssertFatal(1 == 0, "Unsupported ssb_perRACH_config %d\n", ssb_perRACH_OccasionAndCB_PreamblesPerSSB->present);
break;
}
// prach is scheduled according to configuration index and tables 6.3.3.2.2 to 6.3.3.2.4
frequency_range_t freq_range = get_freq_range_from_arfcn(scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencyPointA);
nr_prach_info_t prach_info = get_nr_prach_occasion_info_from_index(config_index, freq_range, cc->frame_type);
float num_ssb_per_RO = ssb_per_rach_occasion[cfg->prach_config.ssb_per_rach.value];
uint8_t fdm = cfg->prach_config.num_prach_fd_occasions.value;
uint64_t L_ssb = (((uint64_t) cfg->ssb_table.ssb_mask_list[0].ssb_mask.value) << 32) | cfg->ssb_table.ssb_mask_list[1].ssb_mask.value;
uint32_t total_RA_occasions = prach_info.N_RA_sfn * prach_info.N_t_slot * prach_info.N_RA_slot * fdm;
for(int i = 0; i < 64; i++) {
if ((L_ssb >> (63 - i)) & 0x01) { // only if the bit of L_ssb at current ssb index is 1
cc->ssb_index[num_active_ssb] = i;
num_active_ssb++;
}
}
cc->total_prach_occasions_per_config_period = total_RA_occasions;
for (int i = 1; (1 << (i - 1)) <= prach_info.max_association_period; i++) {
cc->max_association_period = (1 << (i - 1));
total_RA_occasions = total_RA_occasions * cc->max_association_period;
if(total_RA_occasions >= (int) (num_active_ssb / num_ssb_per_RO)) {
repetition = (uint16_t)((total_RA_occasions * num_ssb_per_RO) / num_active_ssb);
break;
}
}
unused_RA_occasion = total_RA_occasions - (int)((num_active_ssb * repetition) / num_ssb_per_RO);
cc->total_prach_occasions = total_RA_occasions - unused_RA_occasion;
cc->num_active_ssb = num_active_ssb;
cc->prach_info = prach_info;
LOG_D(NR_MAC,
"Total available RO %d, num of active SSB %d: unused RO = %d association_period %u N_RA_sfn %u "
"total_prach_occasions_per_config_period %u\n",
cc->total_prach_occasions,
cc->num_active_ssb,
unused_RA_occasion,
cc->max_association_period,
prach_info.N_RA_sfn,
cc->total_prach_occasions_per_config_period);
}
static void schedule_nr_MsgA_pusch(NR_UplinkConfigCommon_t *uplinkConfigCommon,
gNB_MAC_INST *nr_mac,
module_id_t module_idP,
frame_t frameP,
slot_t slotP,
nfapi_nr_prach_pdu_t *prach_pdu,
uint16_t dmrs_TypeA_Position,
NR_PhysCellId_t physCellId)
{
NR_SCHED_ENSURE_LOCKED(&nr_mac->sched_lock);
NR_MsgA_PUSCH_Resource_r16_t *msgA_PUSCH_Resource = uplinkConfigCommon->initialUplinkBWP->ext1->msgA_ConfigCommon_r16->choice
.setup->msgA_PUSCH_Config_r16->msgA_PUSCH_ResourceGroupA_r16;
const int n_slots_frame = nr_mac->frame_structure.numb_slots_frame;
slot_t msgA_pusch_slot = (slotP + msgA_PUSCH_Resource->msgA_PUSCH_TimeDomainOffset_r16) % n_slots_frame;
frame_t msgA_pusch_frame = (frameP + ((slotP + msgA_PUSCH_Resource->msgA_PUSCH_TimeDomainOffset_r16) / n_slots_frame)) % 1024;
int index = ul_buffer_index((int)msgA_pusch_frame, (int)msgA_pusch_slot, n_slots_frame, nr_mac->UL_tti_req_ahead_size);
nfapi_nr_ul_tti_request_t *UL_tti_req = &nr_mac[module_idP].UL_tti_req_ahead[0][index];
UL_tti_req->SFN = msgA_pusch_frame;
UL_tti_req->Slot = msgA_pusch_slot;
AssertFatal(is_ul_slot(msgA_pusch_slot, &nr_mac->frame_structure),
"Slot %d is not an Uplink slot, invalid msgA_PUSCH_TimeDomainOffset_r16 %ld\n",
msgA_pusch_slot,
msgA_PUSCH_Resource->msgA_PUSCH_TimeDomainOffset_r16);
UL_tti_req->pdus_list[UL_tti_req->n_pdus].pdu_type = NFAPI_NR_UL_CONFIG_PUSCH_PDU_TYPE;
UL_tti_req->pdus_list[UL_tti_req->n_pdus].pdu_size = sizeof(nfapi_nr_pusch_pdu_t);
nfapi_nr_pusch_pdu_t *pusch_pdu = &UL_tti_req->pdus_list[UL_tti_req->n_pdus].pusch_pdu;
memset(pusch_pdu, 0, sizeof(nfapi_nr_pusch_pdu_t));
rnti_t ra_rnti = nr_get_ra_rnti(prach_pdu->prach_start_symbol, slotP, prach_pdu->num_ra, 0);
// Fill PUSCH PDU
pusch_pdu->pdu_bit_map = PUSCH_PDU_BITMAP_PUSCH_DATA;
pusch_pdu->rnti = ra_rnti;
pusch_pdu->handle = 0;
pusch_pdu->rb_size = msgA_PUSCH_Resource->nrofPRBs_PerMsgA_PO_r16;
pusch_pdu->mcs_table = 0;
pusch_pdu->frequency_hopping =
msgA_PUSCH_Resource->msgA_IntraSlotFrequencyHopping_r16 ? *msgA_PUSCH_Resource->msgA_IntraSlotFrequencyHopping_r16 : 0;
pusch_pdu->dmrs_ports = 1; // 6.2.2 in 38.214 only port 0 to be used
AssertFatal(msgA_PUSCH_Resource->startSymbolAndLengthMsgA_PO_r16,
"Only SLIV based on startSymbolAndLengthMsgA_PO_r16 implemented\n");
int S = 0;
int L = 0;
SLIV2SL(*msgA_PUSCH_Resource->startSymbolAndLengthMsgA_PO_r16, &S, &L);
pusch_pdu->start_symbol_index = S;
pusch_pdu->nr_of_symbols = L;
pusch_pdu->pusch_data.new_data_indicator = 1;
pusch_pdu->nrOfLayers = 1;
pusch_pdu->num_dmrs_cdm_grps_no_data = L <= 2 ? 1 : 2; // no data in dmrs symbols as in 6.2.2 in 38.214
pusch_pdu->ul_dmrs_symb_pos = get_l_prime(3, 0, pusch_dmrs_pos2, pusch_len1, 10, dmrs_TypeA_Position);
pusch_pdu->transform_precoding = *uplinkConfigCommon->initialUplinkBWP->ext1->msgA_ConfigCommon_r16->choice.setup->msgA_PUSCH_Config_r16->msgA_TransformPrecoder_r16;
pusch_pdu->rb_bitmap[0] = 0;
pusch_pdu->rb_start = msgA_PUSCH_Resource->frequencyStartMsgA_PUSCH_r16; // rb_start depends on the RO
int locationAndBandwidth = uplinkConfigCommon->initialUplinkBWP->genericParameters.locationAndBandwidth;
pusch_pdu->bwp_size = NRRIV2BW(locationAndBandwidth, MAX_BWP_SIZE);
pusch_pdu->bwp_start = NRRIV2PRBOFFSET(locationAndBandwidth, MAX_BWP_SIZE);
pusch_pdu->subcarrier_spacing = 0;
pusch_pdu->cyclic_prefix = 0;
pusch_pdu->uplink_frequency_shift_7p5khz = 0;
pusch_pdu->vrb_to_prb_mapping = 0;
pusch_pdu->dmrs_config_type = 0;
pusch_pdu->data_scrambling_id = 0;
if (msgA_PUSCH_Resource->msgA_DMRS_Config_r16.msgA_ScramblingID0_r16) {
pusch_pdu->ul_dmrs_scrambling_id = *msgA_PUSCH_Resource->msgA_DMRS_Config_r16.msgA_ScramblingID0_r16;
} else {
pusch_pdu->ul_dmrs_scrambling_id = physCellId;
}
pusch_pdu->scid =
0; // DMRS sequence initialization [TS38.211, sec 6.4.1.1.1]. Should match what is sent in DCI 0_1, otherwise set to 0.
pusch_pdu->pusch_identity = 0;
pusch_pdu->resource_alloc = 1; // type 1
pusch_pdu->tx_direct_current_location = 0;
pusch_pdu->mcs_index = msgA_PUSCH_Resource->msgA_MCS_r16;
pusch_pdu->qam_mod_order = nr_get_Qm_dl(pusch_pdu->mcs_index, pusch_pdu->mcs_table);
int num_dmrs_symb = count_bits64_with_mask(pusch_pdu->ul_dmrs_symb_pos, 10, 3);
AssertFatal(pusch_pdu->mcs_index <= 28, "Exceeding MCS limit for MsgA PUSCH\n");
int R = nr_get_code_rate_ul(pusch_pdu->mcs_index, pusch_pdu->mcs_table);
pusch_pdu->target_code_rate = R;
int TBS = nr_compute_tbs(pusch_pdu->qam_mod_order,
R,
pusch_pdu->rb_size,
pusch_pdu->nr_of_symbols,
num_dmrs_symb * 12, // nb dmrs set for no data in dmrs symbol
0, // nb_rb_oh
0, // to verify tb scaling
pusch_pdu->nrOfLayers)
>> 3;
pusch_pdu->pusch_data.tb_size = TBS;
pusch_pdu->maintenance_parms_v3.ldpcBaseGraph = get_BG(TBS << 3, R);
LOG_D(NR_MAC, "Scheduling MsgA PUSCH in %d.%d\n", msgA_pusch_frame, msgA_pusch_slot);
UL_tti_req->n_pdus += 1;
}
static void fill_vrb(const frame_t frame,
const slot_t slot,
int nb_rb,
int beam_idx,
int vrb_size,
int slots_frame,
int rb_start,
int start_symb,
int num_symb,
NR_COMMON_channels_t *cc)
{
const int index = ul_buffer_index(frame, slot, slots_frame, vrb_size);
uint16_t *vrb_map_UL = &cc->vrb_map_UL[beam_idx][index * MAX_BWP_SIZE];
for (int i = 0; i < nb_rb; ++i) {
AssertFatal(
!(vrb_map_UL[rb_start + i] & SL_to_bitmap(start_symb, num_symb)),
"PRACH resources are already occupied!\n");
vrb_map_UL[rb_start + i] |= SL_to_bitmap(start_symb, num_symb);
}
}
void schedule_nr_prach(module_id_t module_idP, frame_t frameP, slot_t slotP)
{
gNB_MAC_INST *gNB = RC.nrmac[module_idP];
/* already mutex protected: held in gNB_dlsch_ulsch_scheduler() */
NR_SCHED_ENSURE_LOCKED(&gNB->sched_lock);
NR_COMMON_channels_t *cc = gNB->common_channels;
NR_ServingCellConfigCommon_t *scc = cc->ServingCellConfigCommon;
NR_BWP_UplinkCommon_t *initialUplinkBWP = scc->uplinkConfigCommon->initialUplinkBWP;
NR_RACH_ConfigCommon_t *rach_ConfigCommon = initialUplinkBWP->rach_ConfigCommon->choice.setup;
NR_MsgA_ConfigCommon_r16_t *msgacc = NULL;
if (initialUplinkBWP->ext1 && initialUplinkBWP->ext1->msgA_ConfigCommon_r16)
msgacc = initialUplinkBWP->ext1->msgA_ConfigCommon_r16->choice.setup;
int slots_frame = gNB->frame_structure.numb_slots_frame;
int index = ul_buffer_index(frameP, slotP, slots_frame, gNB->UL_tti_req_ahead_size);
nfapi_nr_ul_tti_request_t *UL_tti_req = &RC.nrmac[module_idP]->UL_tti_req_ahead[0][index];
nfapi_nr_config_request_scf_t *cfg = &RC.nrmac[module_idP]->config[0];
if (is_ul_slot(slotP, &RC.nrmac[module_idP]->frame_structure)) {
const NR_RACH_ConfigGeneric_t *rach_ConfigGeneric = &rach_ConfigCommon->rach_ConfigGeneric;
uint8_t config_index = rach_ConfigGeneric->prach_ConfigurationIndex;
int slot_index = 0;
uint16_t prach_occasion_id = -1;
int bwp_start = NRRIV2PRBOFFSET(initialUplinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE);
uint8_t fdm = cfg->prach_config.num_prach_fd_occasions.value;
const int ul_mu = scc->uplinkConfigCommon->frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing;
const int mu = nr_get_prach_or_ul_mu(msgacc, rach_ConfigCommon, ul_mu);
// prach is scheduled according to configuration index and tables 6.3.3.2.2 to 6.3.3.2.4
uint16_t RA_sfn_index = -1;
frequency_range_t freq_range = get_freq_range_from_arfcn(scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencyPointA);
if (get_nr_prach_sched_from_info(cc->prach_info, config_index, frameP, slotP, mu, freq_range, &RA_sfn_index, cc->frame_type)) {
uint16_t format0 = cc->prach_info.format & 0xff; // first column of format from table
uint16_t format1 = (cc->prach_info.format >> 8) & 0xff; // second column of format from table
if (cc->prach_info.N_RA_slot > 1) { // more than 1 PRACH slot in a subframe
if (slotP % 2 == 1)
slot_index = 1;
else
slot_index = 0;
} else if (cc->prach_info.N_RA_slot <= 1) { // 1 PRACH slot in a subframe
slot_index = 0;
}
UL_tti_req->SFN = frameP;
UL_tti_req->Slot = slotP;
UL_tti_req->rach_present = 1;
NR_beam_alloc_t beam = {0};
uint32_t N_t_slot = cc->prach_info.N_t_slot;
uint32_t start_symb = cc->prach_info.start_symbol;
for (int fdm_index = 0; fdm_index < fdm; fdm_index++) { // one structure per frequency domain occasion
AssertFatal(UL_tti_req->n_pdus < sizeof(UL_tti_req->pdus_list) / sizeof(UL_tti_req->pdus_list[0]),
"Invalid UL_tti_req->n_pdus %d\n",
UL_tti_req->n_pdus);
UL_tti_req->pdus_list[UL_tti_req->n_pdus].pdu_type = NFAPI_NR_UL_CONFIG_PRACH_PDU_TYPE;
UL_tti_req->pdus_list[UL_tti_req->n_pdus].pdu_size = sizeof(nfapi_nr_prach_pdu_t);
nfapi_nr_prach_pdu_t *prach_pdu = &UL_tti_req->pdus_list[UL_tti_req->n_pdus].prach_pdu;
memset(prach_pdu, 0, sizeof(nfapi_nr_prach_pdu_t));
UL_tti_req->n_pdus += 1;
int num_td_occ = 0;
for (int td_index = 0; td_index < N_t_slot; td_index++) {
uint32_t config_period = cc->prach_info.x;
prach_occasion_id = (((frameP % (cc->max_association_period * config_period))/config_period) * cc->total_prach_occasions_per_config_period) +
(RA_sfn_index + slot_index) * N_t_slot * fdm + td_index * fdm + fdm_index;
if (prach_occasion_id >= cc->total_prach_occasions) // to be confirmed: unused occasion?
continue;
num_td_occ++;
float num_ssb_per_RO = ssb_per_rach_occasion[cfg->prach_config.ssb_per_rach.value];
int beam_index = 0;
if(num_ssb_per_RO <= 1) {
// ordered ssb number
int n_ssb = (int) (prach_occasion_id / (int)(1 / num_ssb_per_RO)) % cc->num_active_ssb;
// fapi beam index
beam_index = get_fapi_beamforming_index(gNB, cc->ssb_index[n_ssb]);
// multi-beam allocation structure
beam = beam_allocation_procedure(&gNB->beam_info, frameP, slotP, beam_index, slots_frame);
AssertFatal(beam.idx >= 0, "Cannot allocate PRACH corresponding to %d SSB transmitted in any available beam\n", n_ssb + 1);
} else {
int first_ssb_index = (prach_occasion_id * (int)num_ssb_per_RO) % cc->num_active_ssb;
for(int j = first_ssb_index; j < first_ssb_index + num_ssb_per_RO; j++) {
// fapi beam index
beam_index = get_fapi_beamforming_index(gNB, cc->ssb_index[j]);
// multi-beam allocation structure
beam = beam_allocation_procedure(&gNB->beam_info, frameP, slotP, beam_index, slots_frame);
AssertFatal(beam.idx >= 0, "Cannot allocate PRACH corresponding to SSB %d in any available beam\n", j);
}
}
if(num_td_occ == 1) {
// filling the prach fapi structure
prach_pdu->phys_cell_id = *scc->physCellId;
prach_pdu->prach_start_symbol = start_symb;
prach_pdu->num_ra = fdm_index;
prach_pdu->num_cs = get_NCS(rach_ConfigGeneric->zeroCorrelationZoneConfig,
format0,
rach_ConfigCommon->restrictedSetConfig);
// SCF PRACH PDU format field does not consider A1/B1 etc. possibilities
// We added 9 = A1/B1 10 = A2/B2 11 A3/B3
if (format1!=0xff) {
switch(format0) {
case 0xa1:
prach_pdu->prach_format = 11;
break;
case 0xa2:
prach_pdu->prach_format = 12;
break;
case 0xa3:
prach_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) {
case 0:
prach_pdu->prach_format = 0;
break;
case 1:
prach_pdu->prach_format = 1;
break;
case 2:
prach_pdu->prach_format = 2;
break;
case 3:
prach_pdu->prach_format = 3;
break;
case 0xa1:
prach_pdu->prach_format = 4;
break;
case 0xa2:
prach_pdu->prach_format = 5;
break;
case 0xa3:
prach_pdu->prach_format = 6;
break;
case 0xb1:
prach_pdu->prach_format = 7;
break;
case 0xb4:
prach_pdu->prach_format = 8;
break;
case 0xc0:
prach_pdu->prach_format = 9;
break;
case 0xc2:
prach_pdu->prach_format = 10;
break;
default:
AssertFatal(1==0,"Invalid PRACH format");
}
}
if (initialUplinkBWP->ext1 && initialUplinkBWP->ext1->msgA_ConfigCommon_r16) {
if (gNB->UE_info.connected_ue_list[0] == NULL)
schedule_nr_MsgA_pusch(scc->uplinkConfigCommon,
gNB,
module_idP,
frameP,
slotP,
prach_pdu,
scc->dmrs_TypeA_Position,
*scc->physCellId);
}
}
prach_pdu->num_prach_ocas = num_td_occ;
prach_pdu->beamforming.num_prgs = 0;
prach_pdu->beamforming.prg_size = 0;
prach_pdu->beamforming.dig_bf_interface = num_td_occ;
prach_pdu->beamforming.prgs_list[0].dig_bf_interface_list[num_td_occ - 1].beam_idx = beam_index;
LOG_D(NR_MAC,
"Frame %d, Slot %d: Prach Occasion id = %u fdm index = %u start symbol = %u slot index = %u subframe index = %u \n",
frameP,
slotP,
prach_occasion_id,
prach_pdu->num_ra,
prach_pdu->prach_start_symbol,
slot_index,
RA_sfn_index);
}
if (num_td_occ == 0) // no TD PRACH occasion -> no PRACH PDU
UL_tti_req->n_pdus -= 1;
else
gNB->num_scheduled_prach_rx += num_td_occ;
}
// block resources in vrb_map_UL
const int mu_pusch = scc->uplinkConfigCommon->frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing;
const int16_t n_ra_rb = get_N_RA_RB(cfg->prach_config.prach_sub_c_spacing.value, mu_pusch);
// mark PRBs as occupied for current and future slots if prach extends beyond current slot
int total_prach_slots;
uint32_t N_dur = cc->prach_info.N_dur;
if (format0 < 4) {
N_dur = 14; // number of PRACH symbols in PRACH slot
total_prach_slots = get_long_prach_dur(format0, mu_pusch);
AssertFatal(slotP + total_prach_slots - 1 < slots_frame, "PRACH cannot extend across frames\n");
} else {
total_prach_slots = 1;
}
// reserve PRBs occupied by PRACH in all PRACH slot.
for (int i = 0; i < total_prach_slots; i++) {
fill_vrb(frameP,
slotP + i,
n_ra_rb * fdm,
beam.idx,
gNB->vrb_map_UL_size,
slots_frame,
bwp_start + rach_ConfigGeneric->msg1_FrequencyStart,
start_symb,
N_t_slot * N_dur,
cc);
}
}
}
}
int nr_fill_successrar(const NR_UE_sched_ctrl_t *ue_sched_ctl,
rnti_t crnti,
const unsigned char *ue_cont_res_id,
uint8_t resource_indicator,
uint8_t timing_indicator,
unsigned char *mac_pdu,
int mac_pdu_length)
{
LOG_D(NR_MAC, "mac_pdu_length = %d\n", mac_pdu_length);
int timing_advance_cmd = ue_sched_ctl->ta_update;
// TS 38.321 - Figure 6.1.5a-1: BI MAC subheader
NR_RA_HEADER_BI_MSGB *bi = (NR_RA_HEADER_BI_MSGB *)&mac_pdu[mac_pdu_length];
mac_pdu_length += sizeof(NR_RA_HEADER_BI_MSGB);
bi->E = 1;
bi->T1 = 0;
bi->T2 = 0;
bi->R = 0;
bi->BI = 0; // BI = 0, 5ms
// TS 38.321 - Figure 6.1.5a-3: SuccessRAR MAC subheader
NR_RA_HEADER_SUCCESS_RAR_MSGB *SUCESS_RAR_header = (NR_RA_HEADER_SUCCESS_RAR_MSGB *)&mac_pdu[mac_pdu_length];
mac_pdu_length += sizeof(NR_RA_HEADER_SUCCESS_RAR_MSGB);
SUCESS_RAR_header->E = 0;
SUCESS_RAR_header->T1 = 0;
SUCESS_RAR_header->T2 = 1;
SUCESS_RAR_header->S = 0;
SUCESS_RAR_header->R = 0;
// TS 38.321 - Figure 6.2.3a-2: successRAR
NR_MAC_SUCCESS_RAR *successRAR = (NR_MAC_SUCCESS_RAR *)&mac_pdu[mac_pdu_length];
mac_pdu_length += sizeof(NR_MAC_SUCCESS_RAR);
successRAR->CONT_RES_1 = ue_cont_res_id[0];
successRAR->CONT_RES_2 = ue_cont_res_id[1];
successRAR->CONT_RES_3 = ue_cont_res_id[2];
successRAR->CONT_RES_4 = ue_cont_res_id[3];
successRAR->CONT_RES_5 = ue_cont_res_id[4];
successRAR->CONT_RES_6 = ue_cont_res_id[5];
successRAR->R = 0;
successRAR->CH_ACESS_CPEXT = 1;
successRAR->TPC = ue_sched_ctl->tpc0;
successRAR->HARQ_FTI = timing_indicator;
successRAR->PUCCH_RI = resource_indicator;
successRAR->TA1 = (uint8_t)(timing_advance_cmd >> 8); // 4 MSBs of timing advance;
successRAR->TA2 = (uint8_t)(timing_advance_cmd & 0xff); // 8 LSBs of timing advance;
successRAR->CRNTI_1 = (uint8_t)(crnti >> 8); // 8 MSBs of rnti
successRAR->CRNTI_2 = (uint8_t)(crnti & 0xff); // 8 LSBs of rnti
LOG_D(NR_MAC,
"successRAR: Contention Resolution ID 0x%02x%02x%02x%02x%02x%02x R 0x%01x CH_ACESS_CPEXT 0x%02x TPC 0x%02x HARQ_FTI 0x%03x "
"PUCCH_RI 0x%04x TA 0x%012x CRNTI 0x%04x\n",
successRAR->CONT_RES_1,
successRAR->CONT_RES_2,
successRAR->CONT_RES_3,
successRAR->CONT_RES_4,
successRAR->CONT_RES_5,
successRAR->CONT_RES_6,
successRAR->R,
successRAR->CH_ACESS_CPEXT,
successRAR->TPC,
successRAR->HARQ_FTI,
successRAR->PUCCH_RI,
timing_advance_cmd,
crnti);
LOG_D(NR_MAC, "mac_pdu_length = %d\n", mac_pdu_length);
return mac_pdu_length;
}
/** @brief find UE with RA process for given preamble */
static NR_UE_info_t *get_existing_ra(gNB_MAC_INST *nr_mac, uint16_t preamble_index)
{
UE_iterator(nr_mac->UE_info.access_ue_list, UE) {
NR_RA_t *ra = UE->ra;
for (int i = 0; i < ra->preambles.num_preambles; ++i) {
if (ra->preambles.preamble_list[i] == preamble_index)
return UE;
}
}
return NULL;
}
/** @brief add UE to list of UEs doing RA.
*
* Remove with nr_release_ra_UE(). */
bool add_new_UE_RA(gNB_MAC_INST *nr_mac, NR_UE_info_t *UE)
{
DevAssert(UE->ra); // a UE in the acess_ue_list needs to have an RA process
return add_UE_to_list(NR_NB_RA_PROC_MAX, nr_mac->UE_info.access_ue_list, UE);
}
static uint8_t nr_get_msg3_tpc(uint32_t preamble_power)
{
// TODO not sure how to implement TPC for MSG3 to be sent in RAR
// maybe using preambleReceivedTargetPower as a term of comparison
// in any case OAI L1 sets this as invalid
// and Aerial report doesn't seem to be reliable (not matching preambleReceivedTargetPower)
// so for now we feedback 0dB TPC
return 3; // it means 0dB
}
static unsigned int get_slot_RA(const NR_ServingCellConfigCommon_t *scc,
const NR_RACH_ConfigCommon_t *rach_ConfigCommon,
const NR_MsgA_ConfigCommon_r16_t *msgacc,
frame_type_t frame_type,
int slot)
{
uint8_t index = rach_ConfigCommon->rach_ConfigGeneric.prach_ConfigurationIndex;
uint16_t prach_format =
get_nr_prach_format_from_index(index, scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencyPointA, frame_type);
unsigned int slot_RA;
if ((prach_format & 0xff) < 4) {
const int ul_mu = scc->uplinkConfigCommon->frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing;
const int mu = nr_get_prach_or_ul_mu(msgacc, rach_ConfigCommon, ul_mu);
unsigned int slots_per_sf = (1 << mu);
slot_RA = slot / slots_per_sf;
} else {
slot_RA = slot;
}
return slot_RA;
}
void nr_initiate_ra_proc(module_id_t module_idP,
int CC_id,
frame_t frame,
int slot,
uint16_t preamble_index,
uint8_t freq_index,
uint8_t symbol,
int16_t timing_offset,
uint32_t preamble_power)
{
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_INITIATE_RA_PROC, 1);
gNB_MAC_INST *nr_mac = RC.nrmac[module_idP];
NR_SCHED_LOCK(&nr_mac->sched_lock);
/* check if preamble exists (NSA, HO cases) */
NR_UE_info_t *UE = get_existing_ra(nr_mac, preamble_index);
if ((get_softmodem_params()->nsa || get_softmodem_params()->do_ra) && !UE) {
/* we are in NSA, but no UE has been configured => we ignore it */
LOG_W(NR_MAC, "random access with preamble %d: no pre-configured RA process found\n", preamble_index);
NR_SCHED_UNLOCK(&nr_mac->sched_lock);
return;
}
if (!UE) {
/* in CBRA: we don't know this UE yet. There might be CFRA (e.g., HO IN SA)
* where we know the UE */
rnti_t rnti;
bool rnti_found = nr_mac_get_new_rnti(&nr_mac->UE_info, &rnti);
if (!rnti_found) {
LOG_E(NR_MAC, "initialisation random access: no more available RNTIs for new UE\n");
NR_SCHED_UNLOCK(&nr_mac->sched_lock);
return;
}
UE = get_new_nr_ue_inst(&nr_mac->UE_info.uid_allocator, rnti, NULL);
if (!add_new_UE_RA(nr_mac, UE)) {
LOG_E(NR_MAC, "FAILURE: %4d.%2d initiating RA procedure for preamble index %d: no free RA process\n", frame, slot, preamble_index);
delete_nr_ue_data(UE, NULL, &nr_mac->UE_info.uid_allocator);
NR_SCHED_UNLOCK(&nr_mac->sched_lock);
return;
}
}
NR_RA_t *ra = UE->ra;
ra->preamble_frame = frame;
ra->preamble_slot = slot;
ra->preamble_index = preamble_index;
ra->timing_offset = timing_offset;
ra->msg3_TPC = nr_get_msg3_tpc(preamble_power);
NR_COMMON_channels_t *cc = &nr_mac->common_channels[CC_id];
NR_ServingCellConfigCommon_t *scc = cc->ServingCellConfigCommon;
// 3GPP TS 38.321 Section 5.1.3(a) 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.
NR_RACH_ConfigCommon_t *rach_ConfigCommon = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup;
NR_MsgA_ConfigCommon_r16_t *msgacc = NULL;
if (scc->uplinkConfigCommon->initialUplinkBWP->ext1 && scc->uplinkConfigCommon->initialUplinkBWP->ext1->msgA_ConfigCommon_r16)
msgacc = scc->uplinkConfigCommon->initialUplinkBWP->ext1->msgA_ConfigCommon_r16->choice.setup;
uint8_t ul_carrier_id = 0; // 0 for NUL 1 for SUL
uint32_t slot_RA = get_slot_RA(scc, rach_ConfigCommon, msgacc, cc->frame_type, slot);
ra->RA_rnti = nr_get_ra_rnti(symbol, slot_RA, freq_index, ul_carrier_id);
if (msgacc) {
ra->ra_type = RA_2_STEP;
ra->ra_state = nrRA_WAIT_MsgA_PUSCH;
ra->MsgB_rnti = nr_get_MsgB_rnti(symbol, slot_RA, freq_index, ul_carrier_id);
} else {
ra->ra_type = RA_4_STEP;
ra->ra_state = nrRA_Msg2;
}
LOG_A(NR_MAC, "%d.%d UE RA-RNTI %04x TC-RNTI %04x: initiating RA procedure\n", frame, slot, ra->RA_rnti, UE->rnti);
// Configure RA BWP
configure_UE_BWP(nr_mac, scc, UE, true, NR_SearchSpace__searchSpaceType_PR_common, -1, -1);
// return current SSB order in the list of tranmitted SSBs
int n_ssb = ssb_index_from_prach(module_idP, frame, slot, preamble_index, freq_index, symbol);
UE->UE_beam_index = get_fapi_beamforming_index(nr_mac, cc->ssb_index[n_ssb]);
NR_SCHED_UNLOCK(&nr_mac->sched_lock);
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_INITIATE_RA_PROC, 0);
}
static void start_ra_contention_resolution_timer(NR_RA_t *ra, const long ra_ContentionResolutionTimer, const int K2, const int scs)
{
// 3GPP TS 38.331 Section 6.3.2 Radio resource control information elements
// ra-ContentionResolutionTimer ENUMERATED {sf8, sf16, sf24, sf32, sf40, sf48, sf56, sf64}
// The initial value for the contention resolution timer.
// Value sf8 corresponds to 8 subframes, value sf16 corresponds to 16 subframes, and so on.
// We add 2 * K2 because the timer runs from Msg2 transmission till Msg4 ACK reception
ra->contention_resolution_timer = ((((int)ra_ContentionResolutionTimer + 1) * 8) << scs) + 2 * K2;
LOG_D(NR_MAC,
"Starting RA Contention Resolution timer with %d ms + 2 * %d K2 (%d slots) duration\n",
((int)ra_ContentionResolutionTimer + 1) * 8,
K2,
ra->contention_resolution_timer);
}
static void nr_generate_Msg3_retransmission(module_id_t module_idP,
int CC_id,
frame_t frame,
slot_t slot,
NR_UE_info_t *UE,
nfapi_nr_ul_dci_request_t *ul_dci_req)
{
gNB_MAC_INST *nr_mac = RC.nrmac[module_idP];
NR_RA_t *ra = UE->ra;
NR_COMMON_channels_t *cc = &nr_mac->common_channels[CC_id];
NR_ServingCellConfigCommon_t *scc = cc->ServingCellConfigCommon;
NR_UE_UL_BWP_t *ul_bwp = &UE->current_UL_BWP;
NR_UE_ServingCell_Info_t *sc_info = &UE->sc_info;
NR_SearchSpace_t *ss = UE->UE_sched_ctrl.search_space;
NR_ControlResourceSet_t *coreset = UE->UE_sched_ctrl.coreset;
AssertFatal(coreset, "Coreset cannot be null for RA-Msg3 retransmission\n");
const int coresetid = coreset->controlResourceSetId;
NR_tda_info_t tda_info = get_ul_tda_info(ul_bwp,
coresetid,
NR_SearchSpace__searchSpaceType_PR_common,
TYPE_TC_RNTI_,
ra->Msg3_tda_id);
int slots_frame = nr_mac->frame_structure.numb_slots_frame;
uint16_t K2 = tda_info.k2 + get_NTN_Koffset(scc);
const int sched_frame = (frame + (slot + K2) / slots_frame) % MAX_FRAME_NUMBER;
const int sched_slot = (slot + K2) % slots_frame;
if (is_ul_slot(sched_slot, &nr_mac->frame_structure)) {
NR_beam_alloc_t beam_ul = beam_allocation_procedure(&nr_mac->beam_info, sched_frame, sched_slot, UE->UE_beam_index, slots_frame);
if (beam_ul.idx < 0)
return;
NR_beam_alloc_t beam_dci = beam_allocation_procedure(&nr_mac->beam_info, frame, slot, UE->UE_beam_index, slots_frame);
if (beam_dci.idx < 0) {
reset_beam_status(&nr_mac->beam_info, sched_frame, sched_slot, UE->UE_beam_index, slots_frame, beam_ul.new_beam);
return;
}
int buffer_index = ul_buffer_index(sched_frame, sched_slot, slots_frame, nr_mac->vrb_map_UL_size);
uint16_t *vrb_map_UL = &nr_mac->common_channels[CC_id].vrb_map_UL[beam_ul.idx][buffer_index * MAX_BWP_SIZE];
NR_pusch_dmrs_t dmrs_info = get_ul_dmrs_params(scc, ul_bwp, &tda_info, 1);
int num_dmrs_symb = count_bits64_with_mask(dmrs_info.ul_dmrs_symb_pos, tda_info.startSymbolIndex, tda_info.nrOfSymbols);
int TBS = 0, mcsindex = 0, R = 0, Qm = 0;
while(TBS < 7) { // TBS for msg3 is 7 bytes (except for RRCResumeRequest1 currently not implemented)
mcsindex++;
AssertFatal(mcsindex <= 28, "Exceeding MCS limit for Msg3\n");
R = nr_get_code_rate_ul(mcsindex, ul_bwp->mcs_table);
Qm = nr_get_Qm_ul(mcsindex, ul_bwp->mcs_table);
TBS = nr_compute_tbs(Qm,
R,
ra->msg3_nb_rb,
tda_info.nrOfSymbols,
num_dmrs_symb * 12, // nb dmrs set for no data in dmrs symbol
0, //nb_rb_oh
0, // to verify tb scaling
1) >> 3;
}
NR_sched_pusch_t sched_pusch = {
.bwp_info = get_pusch_bwp_start_size(UE),
.tb_size = TBS,
.rbSize = ra->msg3_nb_rb,
.R = R,
.Qm = Qm,
.mcs = mcsindex,
.nrOfLayers = 1,
.tda_info = tda_info,
.dmrs_info = dmrs_info,
};
int rbStart = 0;
for (int i = 0; (i < ra->msg3_nb_rb) && (rbStart <= (sched_pusch.bwp_info.bwpSize - ra->msg3_nb_rb)); i++) {
if (vrb_map_UL[rbStart + sched_pusch.bwp_info.bwpStart + i] & SL_to_bitmap(tda_info.startSymbolIndex, tda_info.nrOfSymbols)) {
rbStart += i;
i = 0;
}
}
if (rbStart > (sched_pusch.bwp_info.bwpSize - ra->msg3_nb_rb)) {
// cannot find free vrb_map for msg3 retransmission in this slot
return;
}
sched_pusch.rbStart = rbStart;
LOG_I(NR_MAC,
"%4d%2d: RA RNTI %04x CC_id %d Scheduling retransmission of Msg3 in (%d,%d)\n",
frame,
slot,
UE->rnti,
CC_id,
sched_frame,
sched_slot);
buffer_index = ul_buffer_index(sched_frame, sched_slot, slots_frame, nr_mac->UL_tti_req_ahead_size);
nfapi_nr_ul_tti_request_t *future_ul_tti_req = &nr_mac->UL_tti_req_ahead[CC_id][buffer_index];
AssertFatal(future_ul_tti_req->SFN == sched_frame
&& future_ul_tti_req->Slot == sched_slot,
"future UL_tti_req's frame.slot %d.%d does not match PUSCH %d.%d\n",
future_ul_tti_req->SFN,
future_ul_tti_req->Slot,
sched_frame,
sched_slot);
AssertFatal(future_ul_tti_req->n_pdus <
sizeof(future_ul_tti_req->pdus_list) / sizeof(future_ul_tti_req->pdus_list[0]),
"Invalid future_ul_tti_req->n_pdus %d\n", future_ul_tti_req->n_pdus);
future_ul_tti_req->pdus_list[future_ul_tti_req->n_pdus].pdu_type = NFAPI_NR_UL_CONFIG_PUSCH_PDU_TYPE;
future_ul_tti_req->pdus_list[future_ul_tti_req->n_pdus].pdu_size = sizeof(nfapi_nr_pusch_pdu_t);
nfapi_nr_pusch_pdu_t *pusch_pdu = prepare_pusch_pdu(future_ul_tti_req,
UE,
scc,
&sched_pusch,
get_transformPrecoding(ul_bwp, NR_UL_DCI_FORMAT_0_0, 0),
0,
ra->msg3_round,
ul_bwp->pusch_Config && ul_bwp->pusch_Config->frequencyHopping,
UE->rnti);
future_ul_tti_req->n_pdus += 1;
// generation of DCI 0_0 to schedule msg3 retransmission
nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15 = nr_mac->pdcch_pdu_idx[CC_id][coresetid];
if (!pdcch_pdu_rel15) {
nfapi_nr_ul_dci_request_pdus_t *ul_dci_request_pdu = &ul_dci_req->ul_dci_pdu_list[ul_dci_req->numPdus];
memset(ul_dci_request_pdu, 0, sizeof(nfapi_nr_ul_dci_request_pdus_t));
ul_dci_request_pdu->PDUType = NFAPI_NR_DL_TTI_PDCCH_PDU_TYPE;
ul_dci_request_pdu->PDUSize = (uint8_t)(2+sizeof(nfapi_nr_dl_tti_pdcch_pdu));
pdcch_pdu_rel15 = &ul_dci_request_pdu->pdcch_pdu.pdcch_pdu_rel15;
ul_dci_req->numPdus += 1;
nr_configure_pdcch(pdcch_pdu_rel15, coreset, &UE->UE_sched_ctrl.sched_pdcch);
nr_mac->pdcch_pdu_idx[CC_id][coresetid] = pdcch_pdu_rel15;
}
uint8_t aggregation_level;
int CCEIndex = get_cce_index(nr_mac,
CC_id, slot, 0,
&aggregation_level,
beam_dci.idx,
ss,
coreset,
&UE->UE_sched_ctrl.sched_pdcch,
true,
0);
if (CCEIndex < 0) {
LOG_E(NR_MAC, "UE %04x cannot find free CCE!\n", UE->rnti);
return;
}
// Fill PDCCH DL DCI PDU
nfapi_nr_dl_dci_pdu_t *dci_pdu = prepare_dci_pdu(pdcch_pdu_rel15,
scc,
ss,
coreset,
aggregation_level,
CCEIndex,
UE->UE_beam_index,
UE->rnti);
pdcch_pdu_rel15->numDlDci++;
dci_pdu_rel15_t uldci_payload = {0};
config_uldci(sc_info,
pusch_pdu,
&uldci_payload,
NULL,
NULL,
ra->Msg3_tda_id,
ra->msg3_TPC,
1, // Not toggling NDI in msg3 retransmissions
ul_bwp,
ss->searchSpaceType->present);
// Reset TPC to 0 dB to not request new gain multiple times before computing new value for SNR
ra->msg3_TPC = 1;
fill_dci_pdu_rel15(sc_info,
&UE->current_DL_BWP,
ul_bwp,
dci_pdu,
&uldci_payload,
NR_UL_DCI_FORMAT_0_0,
TYPE_TC_RNTI_,
ul_bwp->bwp_id,
ss,
coreset,
0, // parameter not needed for DCI 0_0
nr_mac->cset0_bwp_size);
// Mark the corresponding RBs as used
fill_pdcch_vrb_map(nr_mac,
CC_id,
&UE->UE_sched_ctrl.sched_pdcch,
CCEIndex,
aggregation_level,
beam_dci.idx);
for (int rb = 0; rb < ra->msg3_nb_rb; rb++) {
vrb_map_UL[rbStart + sched_pusch.bwp_info.bwpStart + rb] |= SL_to_bitmap(tda_info.startSymbolIndex, tda_info.nrOfSymbols);
}
// Restart RA contention resolution timer in Msg3 retransmission slot (current slot + K2)
// 3GPP TS 38.321 Section 5.1.5 Contention Resolution
start_ra_contention_resolution_timer(
ra,
scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->ra_ContentionResolutionTimer,
K2,
ul_bwp->scs);
// reset state to wait msg3
ra->ra_state = nrRA_WAIT_Msg3;
ra->Msg3_frame = sched_frame;
ra->Msg3_slot = sched_slot;
}
}
static bool get_feasible_msg3_tda(const NR_ServingCellConfigCommon_t *scc,
int mu_delta,
const NR_PUSCH_TimeDomainResourceAllocationList_t *tda_list,
int frame,
int slot,
NR_RA_t *ra,
NR_beam_info_t *beam_info,
int ue_beam_idx,
const frame_structure_t *fs)
{
DevAssert(tda_list != NULL);
const int NTN_gNB_Koffset = get_NTN_Koffset(scc);
int slots_per_frame = fs->numb_slots_frame;
for (int i = 0; i < tda_list->list.count; i++) {
// check if it is UL
long k2 = *tda_list->list.array[i]->k2 + NTN_gNB_Koffset;
int abs_slot = slot + k2 + mu_delta;
int temp_frame = (frame + (abs_slot / slots_per_frame)) & 1023;
int temp_slot = abs_slot % slots_per_frame; // msg3 slot according to 8.3 in 38.213
if (fs->frame_type == TDD && !is_ul_slot(temp_slot, fs))
continue;
const tdd_bitmap_t *tdd_slot_bitmap = fs->period_cfg.tdd_slot_bitmap;
int s = get_slot_idx_in_period(temp_slot, fs);
// check if enough symbols in case of mixed slot
bool is_mixed = is_mixed_slot(s, fs);
// if the mixed slot has not enough symbols, skip
if (is_mixed && tdd_slot_bitmap[s].num_ul_symbols < 3)
continue;
uint16_t slot_mask =
is_mixed ? SL_to_bitmap(NR_NUMBER_OF_SYMBOLS_PER_SLOT - tdd_slot_bitmap[s].num_ul_symbols,
tdd_slot_bitmap[s].num_ul_symbols)
: 0x3fff;
long startSymbolAndLength = tda_list->list.array[i]->startSymbolAndLength;
int start, nr;
SLIV2SL(startSymbolAndLength, &start, &nr);
uint16_t msg3_mask = SL_to_bitmap(start, nr);
LOG_D(NR_MAC, "Check Msg3 TDA %d for slot %d: k2 %ld, S %d L %d\n", i, temp_slot, k2, start, nr);
/* if this start and length of this TDA cannot be fulfilled, skip */
if ((slot_mask & msg3_mask) != msg3_mask)
continue;
// check if it is possible to allocate MSG3 in a beam in this slot
NR_beam_alloc_t beam = beam_allocation_procedure(beam_info, temp_frame, temp_slot, ue_beam_idx, slots_per_frame);
if (beam.idx < 0)
continue;
// is in mixed slot with more or equal than 3 symbols, or UL slot
ra->Msg3_frame = temp_frame;
ra->Msg3_slot = temp_slot;
ra->Msg3_tda_id = i;
ra->Msg3_beam = beam;
return true;
}
return false;
}
static bool nr_get_Msg3alloc(gNB_MAC_INST *mac, int CC_id, int current_slot, frame_t current_frame, NR_UE_info_t *UE)
{
NR_RA_t *ra = UE->ra;
DevAssert(ra->Msg3_tda_id >= 0 && ra->Msg3_tda_id < 16);
uint16_t msg3_nb_rb = max(8, mac->min_grant_prb); // sdu has 6 or 8 bytes
NR_UE_UL_BWP_t *ul_bwp = &UE->current_UL_BWP;
NR_UE_ServingCell_Info_t *sc_info = &UE->sc_info;
const NR_PUSCH_TimeDomainResourceAllocationList_t *pusch_TimeDomainAllocationList = ul_bwp->tdaList_Common;
int startSymbolAndLength = pusch_TimeDomainAllocationList->list.array[ra->Msg3_tda_id]->startSymbolAndLength;
SLIV2SL(startSymbolAndLength, &ra->msg3_startsymb, &ra->msg3_nbSymb);
const int buffer_index = ul_buffer_index(ra->Msg3_frame,
ra->Msg3_slot,
mac->frame_structure.numb_slots_frame,
mac->vrb_map_UL_size);
uint16_t *vrb_map_UL = &mac->common_channels[CC_id].vrb_map_UL[ra->Msg3_beam.idx][buffer_index * MAX_BWP_SIZE];
int bwpSize = sc_info->initial_ul_BWPSize;
int bwpStart = sc_info->initial_ul_BWPStart;
if (bwpSize != ul_bwp->BWPSize || bwpStart != ul_bwp->BWPStart) {
int act_bwp_start = ul_bwp->BWPStart;
int act_bwp_size = ul_bwp->BWPSize;
if (!((bwpStart >= act_bwp_start) && ((bwpStart+bwpSize) <= (act_bwp_start+act_bwp_size))))
bwpStart = act_bwp_start;
}
/* search msg3_nb_rb free RBs */
int rbSize = 0;
int rbStart = 0;
while (rbSize < msg3_nb_rb) {
rbStart += rbSize; /* last iteration rbSize was not enough, skip it */
rbSize = 0;
while (rbStart < bwpSize && (vrb_map_UL[rbStart + bwpStart] & SL_to_bitmap(ra->msg3_startsymb, ra->msg3_nbSymb)))
rbStart++;
if (rbStart + msg3_nb_rb > bwpSize) {
LOG_D(NR_MAC, "No space to allocate Msg 3\n");
return false;
}
while (rbStart + rbSize < bwpSize
&& !(vrb_map_UL[rbStart + bwpStart + rbSize] & SL_to_bitmap(ra->msg3_startsymb, ra->msg3_nbSymb)) && rbSize < msg3_nb_rb)
rbSize++;
}
ra->msg3_nb_rb = msg3_nb_rb;
ra->msg3_first_rb = rbStart;
ra->msg3_bwp_start = bwpStart;
LOG_I(NR_MAC,
"UE %04x: Msg3 scheduled at %d.%d (%d.%d TDA %u) start %d RBs %d\n",
UE->rnti,
ra->Msg3_frame,
ra->Msg3_slot,
current_frame,
current_slot,
ra->Msg3_tda_id,
ra->msg3_first_rb,
ra->msg3_nb_rb);
return true;
}
static void nr_add_msg3(module_id_t module_idP, int CC_id, frame_t frameP, slot_t slotP, NR_UE_info_t *UE, uint8_t *RAR_pdu)
{
gNB_MAC_INST *mac = RC.nrmac[module_idP];
NR_COMMON_channels_t *cc = &mac->common_channels[CC_id];
NR_ServingCellConfigCommon_t *scc = cc->ServingCellConfigCommon;
NR_UE_UL_BWP_t *ul_bwp = &UE->current_UL_BWP;
NR_UE_ServingCell_Info_t *sc_info = &UE->sc_info;
NR_RA_t *ra = UE->ra;
if (ra->ra_state == nrRA_gNB_IDLE) {
LOG_W(NR_MAC,"RA is not active for RA %X. skipping msg3 scheduling\n", UE->rnti);
return;
}
const uint16_t mask = SL_to_bitmap(ra->msg3_startsymb, ra->msg3_nbSymb);
int slots_frame = mac->frame_structure.numb_slots_frame;
int buffer_index = ul_buffer_index(ra->Msg3_frame, ra->Msg3_slot, slots_frame, mac->vrb_map_UL_size);
uint16_t *vrb_map_UL = &RC.nrmac[module_idP]->common_channels[CC_id].vrb_map_UL[ra->Msg3_beam.idx][buffer_index * MAX_BWP_SIZE];
for (int i = 0; i < ra->msg3_nb_rb; ++i) {
AssertFatal(!(vrb_map_UL[i + ra->msg3_first_rb + ra->msg3_bwp_start] & mask),
"RB %d in %4d.%2d is already taken, cannot allocate Msg3!\n",
i + ra->msg3_first_rb,
ra->Msg3_frame,
ra->Msg3_slot);
vrb_map_UL[i + ra->msg3_first_rb + ra->msg3_bwp_start] |= mask;
}
LOG_D(NR_MAC, "UE %04x: %d.%d RA is active, Msg3 in (%d,%d)\n", UE->rnti, frameP, slotP, ra->Msg3_frame, ra->Msg3_slot);
buffer_index = ul_buffer_index(ra->Msg3_frame, ra->Msg3_slot, slots_frame, mac->UL_tti_req_ahead_size);
nfapi_nr_ul_tti_request_t *future_ul_tti_req = &RC.nrmac[module_idP]->UL_tti_req_ahead[CC_id][buffer_index];
AssertFatal(future_ul_tti_req->SFN == ra->Msg3_frame
&& future_ul_tti_req->Slot == ra->Msg3_slot,
"future UL_tti_req's frame.slot %d.%d does not match PUSCH %d.%d\n",
future_ul_tti_req->SFN,
future_ul_tti_req->Slot,
ra->Msg3_frame,
ra->Msg3_slot);
future_ul_tti_req->pdus_list[future_ul_tti_req->n_pdus].pdu_type = NFAPI_NR_UL_CONFIG_PUSCH_PDU_TYPE;
future_ul_tti_req->pdus_list[future_ul_tti_req->n_pdus].pdu_size = sizeof(nfapi_nr_pusch_pdu_t);
NR_tda_info_t tda_info = get_ul_tda_info(ul_bwp, 0, NR_SearchSpace__searchSpaceType_PR_common, TYPE_TC_RNTI_, ra->Msg3_tda_id);
NR_pusch_dmrs_t dmrs_info = get_ul_dmrs_params(scc, ul_bwp, &tda_info, 1);
int num_dmrs_symb = count_bits64_with_mask(dmrs_info.ul_dmrs_symb_pos, tda_info.startSymbolIndex, tda_info.nrOfSymbols);
int TBS = 0, mcsindex = 0, R = 0, Qm = 0;
while(TBS < 7) { // TBS for msg3 is 7 bytes (except for RRCResumeRequest1 currently not implemented)
mcsindex++;
AssertFatal(mcsindex <= 28, "Exceeding MCS limit for Msg3\n");
R = nr_get_code_rate_ul(mcsindex, ul_bwp->mcs_table);
Qm = nr_get_Qm_ul(mcsindex, ul_bwp->mcs_table);
TBS = nr_compute_tbs(Qm,
R,
ra->msg3_nb_rb,
tda_info.nrOfSymbols,
num_dmrs_symb * 12, // nb dmrs set for no data in dmrs symbol
0, //nb_rb_oh
0, // to verify tb scaling
1) >> 3;
}
NR_sched_pusch_t sched_pusch = {
.bwp_info.bwpSize = sc_info->initial_ul_BWPSize,
.bwp_info.bwpStart = ra->msg3_bwp_start,
.rbSize = ra->msg3_nb_rb,
.rbStart = ra->msg3_first_rb,
.tb_size = TBS,
.R = R,
.Qm = Qm,
.mcs = mcsindex,
.nrOfLayers = 1,
.tda_info = tda_info,
.dmrs_info = dmrs_info
};
LOG_D(NR_MAC,
"UE %04x: %d.%d Adding Msg3 UL Config Request for (%d,%d) : (%d,%d,%d)\n",
UE->rnti,
frameP,
slotP,
ra->Msg3_frame,
ra->Msg3_slot,
ra->msg3_nb_rb,
ra->msg3_first_rb,
ra->msg3_round);
nfapi_nr_pusch_pdu_t *pusch_pdu = prepare_pusch_pdu(future_ul_tti_req,
UE,
scc,
&sched_pusch,
get_transformPrecoding(ul_bwp, NR_UL_DCI_FORMAT_0_0, 0),
0,
0,
ul_bwp->pusch_Config && ul_bwp->pusch_Config->frequencyHopping,
UE->rnti);
future_ul_tti_req->n_pdus += 1;
// calling function to fill rar message
nr_fill_rar(module_idP, UE, RAR_pdu, pusch_pdu);
}
static bool check_msg2_monitoring(const NR_SearchSpace_t *ss, int slots_per_frame, int current_frame, int current_slot)
{
// check if the slot is not among the PDCCH monitored ones (38.213 10.1)
int monitoring_slot_period, monitoring_offset;
get_monitoring_period_offset(ss, &monitoring_slot_period, &monitoring_offset);
if ((current_frame * slots_per_frame + current_slot - monitoring_offset) % monitoring_slot_period != 0)
return false;
return true;
}
static int get_response_window(e_NR_RACH_ConfigGeneric__ra_ResponseWindow response_window)
{
int slots;
switch (response_window) {
case NR_RACH_ConfigGeneric__ra_ResponseWindow_sl1:
slots = 1;
break;
case NR_RACH_ConfigGeneric__ra_ResponseWindow_sl2:
slots = 2;
break;
case NR_RACH_ConfigGeneric__ra_ResponseWindow_sl4:
slots = 4;
break;
case NR_RACH_ConfigGeneric__ra_ResponseWindow_sl8:
slots = 8;
break;
case NR_RACH_ConfigGeneric__ra_ResponseWindow_sl10:
slots = 10;
break;
case NR_RACH_ConfigGeneric__ra_ResponseWindow_sl20:
slots = 20;
break;
case NR_RACH_ConfigGeneric__ra_ResponseWindow_sl40:
slots = 40;
break;
case NR_RACH_ConfigGeneric__ra_ResponseWindow_sl80:
slots = 80;
break;
default:
AssertFatal(false, "Invalid response window value %d\n", response_window);
}
return slots;
}
static bool msg2_in_response_window(int rach_frame,
int rach_slot,
int n_slots_frame,
long rrc_ra_ResponseWindow,
int current_frame,
int current_slot)
{
int window_slots = get_response_window(rrc_ra_ResponseWindow);
if (window_slots > n_slots_frame)
LOG_E(NR_MAC, "RA-ResponseWindow need to be configured to a value lower than or equal to 10 ms\n");
int abs_rach = n_slots_frame * rach_frame + rach_slot;
int abs_now = n_slots_frame * current_frame + current_slot;
int diff = (n_slots_frame * 1024 + abs_now - abs_rach) % (n_slots_frame * 1024);
bool in_window = diff <= window_slots;
if (!in_window) {
LOG_W(NR_MAC,
"exceeded RA window: preamble at %d.%2d now %d.%d (diff %d), ra_ResponseWindow %ld/%d slots\n",
rach_frame,
rach_slot,
current_frame,
current_slot,
diff,
rrc_ra_ResponseWindow,
window_slots);
}
return in_window;
}
static void prepare_dl_pdus(gNB_MAC_INST *nr_mac,
NR_UE_info_t *UE,
NR_sched_pdsch_t *sched_pdsch,
nfapi_nr_dl_tti_request_body_t *dl_req,
NR_sched_pucch_t *pucch,
nr_rnti_type_t rnti_type,
int aggregation_level,
int CCEIndex,
int ndi,
int current_harq_pid,
int CC_id,
int rnti,
int round,
int tb_scaling,
int pduindex)
{
// look up the PDCCH PDU for this CC, BWP, and CORESET. If it does not exist, create it. This is especially
// important if we have multiple RAs, and the DLSCH has to reuse them, so we need to mark them
NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
NR_ControlResourceSet_t *coreset = sched_ctrl->coreset;
const int coresetid = coreset->controlResourceSetId;
nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15 = nr_mac->pdcch_pdu_idx[CC_id][coresetid];
if (!pdcch_pdu_rel15) {
nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdcch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs];
memset(dl_tti_pdcch_pdu, 0, sizeof(nfapi_nr_dl_tti_request_pdu_t));
dl_tti_pdcch_pdu->PDUType = NFAPI_NR_DL_TTI_PDCCH_PDU_TYPE;
dl_tti_pdcch_pdu->PDUSize = (uint8_t)(2 + sizeof(nfapi_nr_dl_tti_pdcch_pdu));
dl_req->nPDUs += 1;
pdcch_pdu_rel15 = &dl_tti_pdcch_pdu->pdcch_pdu.pdcch_pdu_rel15;
nr_configure_pdcch(pdcch_pdu_rel15, coreset, &sched_ctrl->sched_pdcch);
nr_mac->pdcch_pdu_idx[CC_id][coresetid] = pdcch_pdu_rel15;
}
nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdsch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs];
memset((void *)dl_tti_pdsch_pdu, 0, sizeof(nfapi_nr_dl_tti_request_pdu_t));
dl_tti_pdsch_pdu->PDUType = NFAPI_NR_DL_TTI_PDSCH_PDU_TYPE;
dl_tti_pdsch_pdu->PDUSize = (uint8_t)(2 + sizeof(nfapi_nr_dl_tti_pdsch_pdu));
dl_req->nPDUs += 1;
NR_COMMON_channels_t *cc = &nr_mac->common_channels[CC_id];
NR_ServingCellConfigCommon_t *scc = cc->ServingCellConfigCommon;
NR_UE_DL_BWP_t *dl_bwp = &UE->current_DL_BWP;
nfapi_nr_dl_tti_pdsch_pdu_rel15_t *pdsch_pdu_rel15 = prepare_pdsch_pdu(dl_tti_pdsch_pdu,
nr_mac,
UE,
sched_pdsch,
dl_bwp->pdsch_Config,
false,
round,
rnti,
UE->UE_beam_index,
1,
pduindex);
/* Fill PDCCH DL DCI PDU */
nfapi_nr_dl_dci_pdu_t *dci_pdu = prepare_dci_pdu(pdcch_pdu_rel15,
scc,
sched_ctrl->search_space,
coreset,
aggregation_level,
CCEIndex,
UE->UE_beam_index,
rnti);
pdcch_pdu_rel15->numDlDci++;
dci_pdu_rel15_t dci_payload = prepare_dci_dl_payload(nr_mac,
UE,
rnti_type,
sched_ctrl->search_space->searchSpaceType->present,
pdsch_pdu_rel15,
sched_pdsch,
pucch,
current_harq_pid,
tb_scaling,
false);
LOG_D(NR_MAC,
"DCI 1_0 payload: freq_alloc %d (%d,%d,%d), time_alloc %d, vrb to prb %d, mcs %d tb_scaling %d pucchres %d harqtiming %d\n",
dci_payload.frequency_domain_assignment.val,
pdsch_pdu_rel15->rbStart,
pdsch_pdu_rel15->rbSize,
pdsch_pdu_rel15->BWPSize,
dci_payload.time_domain_assignment.val,
dci_payload.vrb_to_prb_mapping.val,
dci_payload.mcs,
dci_payload.tb_scaling,
dci_payload.pucch_resource_indicator,
dci_payload.pdsch_to_harq_feedback_timing_indicator.val);
LOG_D(NR_MAC,
"DCI params: rnti 0x%x, rnti_type %d, dci_format %d coreset params: FreqDomainResource %llx, start_symbol %d "
"n_symb %d, BWPsize %d\n",
dci_pdu->RNTI,
rnti_type,
NR_DL_DCI_FORMAT_1_0,
(unsigned long long)pdcch_pdu_rel15->FreqDomainResource,
pdcch_pdu_rel15->StartSymbolIndex,
pdcch_pdu_rel15->DurationSymbols,
pdsch_pdu_rel15->BWPSize);
fill_dci_pdu_rel15(&UE->sc_info,
dl_bwp,
&UE->current_UL_BWP,
dci_pdu,
&dci_payload,
NR_DL_DCI_FORMAT_1_0,
rnti_type,
dl_bwp->bwp_id,
sched_ctrl->search_space,
coreset,
0, // parameter not needed for DCI 1_0
nr_mac->cset0_bwp_size);
LOG_D(NR_MAC, "BWPSize: %i\n", pdcch_pdu_rel15->BWPSize);
LOG_D(NR_MAC, "BWPStart: %i\n", pdcch_pdu_rel15->BWPStart);
LOG_D(NR_MAC, "SubcarrierSpacing: %i\n", pdcch_pdu_rel15->SubcarrierSpacing);
LOG_D(NR_MAC, "CyclicPrefix: %i\n", pdcch_pdu_rel15->CyclicPrefix);
LOG_D(NR_MAC, "StartSymbolIndex: %i\n", pdcch_pdu_rel15->StartSymbolIndex);
LOG_D(NR_MAC, "DurationSymbols: %i\n", pdcch_pdu_rel15->DurationSymbols);
for (int n = 0; n < 6; n++)
LOG_D(NR_MAC, "FreqDomainResource[%i]: %x\n", n, pdcch_pdu_rel15->FreqDomainResource[n]);
LOG_D(NR_MAC, "CceRegMappingType: %i\n", pdcch_pdu_rel15->CceRegMappingType);
LOG_D(NR_MAC, "RegBundleSize: %i\n", pdcch_pdu_rel15->RegBundleSize);
LOG_D(NR_MAC, "InterleaverSize: %i\n", pdcch_pdu_rel15->InterleaverSize);
LOG_D(NR_MAC, "CoreSetType: %i\n", pdcch_pdu_rel15->CoreSetType);
LOG_D(NR_MAC, "ShiftIndex: %i\n", pdcch_pdu_rel15->ShiftIndex);
LOG_D(NR_MAC, "precoderGranularity: %i\n", pdcch_pdu_rel15->precoderGranularity);
LOG_D(NR_MAC, "numDlDci: %i\n", pdcch_pdu_rel15->numDlDci);
}
static void nr_generate_Msg2(module_id_t module_idP,
int CC_id,
frame_t frameP,
slot_t slotP,
NR_UE_info_t *UE,
nfapi_nr_dl_tti_request_t *DL_req,
nfapi_nr_tx_data_request_t *TX_req)
{
gNB_MAC_INST *nr_mac = RC.nrmac[module_idP];
// no DL -> cannot send Msg2
if (!is_dl_slot(slotP, &nr_mac->frame_structure)) {
return;
}
NR_COMMON_channels_t *cc = &nr_mac->common_channels[CC_id];
NR_UE_DL_BWP_t *dl_bwp = &UE->current_DL_BWP;
NR_ServingCellConfigCommon_t *scc = cc->ServingCellConfigCommon;
NR_RA_t *ra = UE->ra;
long rrc_ra_ResponseWindow =
scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->rach_ConfigGeneric.ra_ResponseWindow;
const int n_slots_frame = nr_mac->frame_structure.numb_slots_frame;
if (!msg2_in_response_window(ra->preamble_frame, ra->preamble_slot, n_slots_frame, rrc_ra_ResponseWindow, frameP, slotP)) {
LOG_E(NR_MAC, "UE RA-RNTI %04x TC-RNTI %04x: exceeded RA window, cannot schedule Msg2\n", ra->RA_rnti, UE->rnti);
nr_release_ra_UE(nr_mac, UE->rnti);
return;
}
NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
NR_SearchSpace_t *ss = sched_ctrl->search_space;
if (!check_msg2_monitoring(ss, n_slots_frame, frameP, slotP)) {
LOG_E(NR_MAC, "UE RA-RNTI %04x TC-RNTI %04x: Msg2 not monitored by UE\n", ra->RA_rnti, UE->rnti);
return;
}
NR_beam_alloc_t beam = beam_allocation_procedure(&nr_mac->beam_info, frameP, slotP, UE->UE_beam_index, n_slots_frame);
if (beam.idx < 0)
return;
const NR_UE_UL_BWP_t *ul_bwp = &UE->current_UL_BWP;
bool ret = get_feasible_msg3_tda(scc,
get_delta_for_k2(ul_bwp->scs),
ul_bwp->tdaList_Common,
frameP,
slotP,
ra,
&nr_mac->beam_info,
UE->UE_beam_index,
&nr_mac->frame_structure);
if (!ret || ra->Msg3_tda_id > 15) {
LOG_D(NR_MAC, "UE RNTI %04x %d.%d: infeasible Msg3 TDA\n", UE->rnti, frameP, slotP);
reset_beam_status(&nr_mac->beam_info, frameP, slotP, UE->UE_beam_index, n_slots_frame, beam.new_beam);
return;
}
int mcsIndex = -1; // initialization value
int rbStart = 0;
int rbSize = 8;
bwp_info_t bwp_info = get_pdsch_bwp_start_size(nr_mac, UE);
NR_ControlResourceSet_t *coreset = sched_ctrl->coreset;
AssertFatal(coreset, "Coreset cannot be null for RA-Msg2\n");
const int coresetid = coreset->controlResourceSetId;
// Calculate number of symbols
int time_domain_assignment = get_dl_tda(nr_mac, slotP);
NR_Type0_PDCCH_CSS_config_t *type0_PDCCH_CSS_config = &nr_mac->type0_PDCCH_CSS_config[cc->ssb_index[UE->UE_beam_index]];
int mux_pattern = type0_PDCCH_CSS_config ? type0_PDCCH_CSS_config->type0_pdcch_ss_mux_pattern : 1;
NR_tda_info_t tda_info = get_dl_tda_info(dl_bwp,
ss->searchSpaceType->present,
time_domain_assignment,
scc->dmrs_TypeA_Position,
mux_pattern,
TYPE_RA_RNTI_,
coresetid,
false);
if (!tda_info.valid_tda)
return;
uint16_t *vrb_map = cc[CC_id].vrb_map[beam.idx];
for (int i = 0; (i < rbSize) && (rbStart <= (bwp_info.bwpSize - rbSize)); i++) {
if (vrb_map[bwp_info.bwpStart + rbStart + i] & SL_to_bitmap(tda_info.startSymbolIndex, tda_info.nrOfSymbols)) {
rbStart += i;
i = 0;
}
}
if (rbStart > (bwp_info.bwpSize - rbSize)) {
LOG_W(NR_MAC, "Cannot find free vrb_map for RA RNTI %04x!\n", ra->RA_rnti);
reset_beam_status(&nr_mac->beam_info, ra->Msg3_frame, ra->Msg3_slot, UE->UE_beam_index, n_slots_frame, ra->Msg3_beam.new_beam);
reset_beam_status(&nr_mac->beam_info, frameP, slotP, UE->UE_beam_index, n_slots_frame, beam.new_beam);
return;
}
// Checking if the DCI allocation is feasible in current subframe
nfapi_nr_dl_tti_request_body_t *dl_req = &DL_req->dl_tti_request_body;
if (dl_req->nPDUs > NFAPI_NR_MAX_DL_TTI_PDUS - 2) {
LOG_W(NR_MAC, "UE %04x: %d.%d FAPI DL structure is full\n", UE->rnti, frameP, slotP);
reset_beam_status(&nr_mac->beam_info, ra->Msg3_frame, ra->Msg3_slot, UE->UE_beam_index, n_slots_frame, ra->Msg3_beam.new_beam);
reset_beam_status(&nr_mac->beam_info, frameP, slotP, UE->UE_beam_index, n_slots_frame, beam.new_beam);
return;
}
uint8_t aggregation_level;
int CCEIndex = get_cce_index(nr_mac, CC_id, slotP, 0, &aggregation_level, beam.idx, ss, coreset, &sched_ctrl->sched_pdcch, true, 0);
if (CCEIndex < 0) {
LOG_W(NR_MAC, "UE %04x: %d.%d cannot find free CCE for Msg2!\n", UE->rnti, frameP, slotP);
reset_beam_status(&nr_mac->beam_info, ra->Msg3_frame, ra->Msg3_slot, UE->UE_beam_index, n_slots_frame, ra->Msg3_beam.new_beam);
reset_beam_status(&nr_mac->beam_info, frameP, slotP, UE->UE_beam_index, n_slots_frame, beam.new_beam);
return;
}
bool msg3_ret = nr_get_Msg3alloc(nr_mac, CC_id, slotP, frameP, UE);
if (!msg3_ret) {
reset_beam_status(&nr_mac->beam_info, ra->Msg3_frame, ra->Msg3_slot, UE->UE_beam_index, n_slots_frame, ra->Msg3_beam.new_beam);
reset_beam_status(&nr_mac->beam_info, frameP, slotP, UE->UE_beam_index, n_slots_frame, beam.new_beam);
return;
}
LOG_D(NR_MAC, "Msg2 startSymbolIndex.nrOfSymbols %d.%d\n", tda_info.startSymbolIndex, tda_info.nrOfSymbols);
// Distance calculation according to SCF222.10.02 RACH.indication (table 3-74) and 38.213 4.2/38.211 4.3.1
// T_c according to 38.211 4.1
float T_c_ns = 0.509;
int numerology = ul_bwp->scs;
float rtt_ns = T_c_ns * 16 * 64 / (1 << numerology) * ra->timing_offset;
float speed_of_light_in_meters_per_second = 299792458.0f;
float distance_in_meters = speed_of_light_in_meters_per_second * rtt_ns / 1000 / 1000 / 1000 / 2;
LOG_A(NR_MAC,
"UE %04x: %d.%d Generating RA-Msg2 DCI, RA RNTI 0x%x, state %d, preamble_index(RAPID) %d, "
"timing_offset = %d (estimated distance %.1f [m])\n",
UE->rnti,
frameP,
slotP,
ra->RA_rnti,
ra->ra_state,
ra->preamble_index,
ra->timing_offset,
distance_in_meters);
// SCF222: PDU index incremented for each PDSCH PDU sent in TX control message. This is used to associate control
// information to data and is reset every slot.
const int pduindex = nr_mac->pdu_index[CC_id]++;
uint8_t mcsTableIdx = dl_bwp->mcsTableIdx;
NR_pdsch_dmrs_t dmrs_parms = get_dl_dmrs_params(scc, dl_bwp, &tda_info, 1);
uint8_t tb_scaling = 0;
int R, Qm;
uint32_t TBS = 0;
while (TBS < 9) { // min TBS for RAR is 9 bytes
mcsIndex++;
R = nr_get_code_rate_dl(mcsIndex, mcsTableIdx);
Qm = nr_get_Qm_dl(mcsIndex, mcsTableIdx);
TBS = nr_compute_tbs(Qm,
R,
rbSize,
tda_info.nrOfSymbols,
dmrs_parms.N_PRB_DMRS * dmrs_parms.N_DMRS_SLOT,
0, // overhead
tb_scaling, // tb scaling
1)
>> 3; // layers
}
NR_sched_pdsch_t sched_pdsch = {
.R = R,
.Qm = Qm,
.mcs = mcsIndex,
.time_domain_allocation = time_domain_assignment,
.nrOfLayers = 1,
.dmrs_parms = dmrs_parms,
.tda_info = tda_info,
.pm_index = 0,
.tb_size = TBS,
.bwp_info = bwp_info,
.rbStart = rbStart,
.rbSize = rbSize
};
prepare_dl_pdus(nr_mac,
UE,
&sched_pdsch,
dl_req,
NULL,
TYPE_RA_RNTI_,
aggregation_level,
CCEIndex,
0,
0,
CC_id,
ra->RA_rnti,
0,
tb_scaling,
pduindex);
// DL TX request
nfapi_nr_pdu_t *tx_req = &TX_req->pdu_list[TX_req->Number_of_PDUs];
// Program UL processing for Msg3
nr_add_msg3(module_idP, CC_id, frameP, slotP, UE, (uint8_t *)&tx_req->TLVs[0].value.direct[0]);
// Start RA contention resolution timer in Msg3 transmission slot (current slot + K2)
// 3GPP TS 38.321 Section 5.1.5 Contention Resolution
start_ra_contention_resolution_timer(
ra,
scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->ra_ContentionResolutionTimer,
*ul_bwp->tdaList_Common->list.array[ra->Msg3_tda_id]->k2 + get_NTN_Koffset(scc),
ul_bwp->scs);
LOG_D(NR_MAC,
"UE %04x: %d.%d: Setting RA-Msg3 reception (%s) for SFN.Slot %d.%d\n",
UE->rnti,
frameP,
slotP,
ra->cfra ? "CFRA" : "CBRA",
ra->Msg3_frame,
ra->Msg3_slot);
LOG_A(NR_MAC, "%d.%d Send RAR to RA-RNTI %04x\n", frameP, slotP, ra->RA_rnti);
tx_req->PDU_index = pduindex;
tx_req->num_TLV = 1;
tx_req->TLVs[0].length = TBS;
tx_req->PDU_length = compute_PDU_length(tx_req->num_TLV, TBS);
TX_req->SFN = frameP;
TX_req->Number_of_PDUs++;
TX_req->Slot = slotP;
T(T_GNB_MAC_DL_RAR_PDU_WITH_DATA,
T_INT(module_idP),
T_INT(CC_id),
T_INT(ra->RA_rnti),
T_INT(frameP),
T_INT(slotP),
T_INT(0),
T_BUFFER(&tx_req->TLVs[0].value.direct[0], tx_req->TLVs[0].length));
// Mark the corresponding symbols RBs as used
fill_pdcch_vrb_map(nr_mac, CC_id, &sched_ctrl->sched_pdcch, CCEIndex, aggregation_level, beam.idx);
for (int rb = 0; rb < rbSize; rb++) {
vrb_map[bwp_info.bwpStart + rb + rbStart] |= SL_to_bitmap(tda_info.startSymbolIndex, tda_info.nrOfSymbols);
}
ra->ra_state = nrRA_WAIT_Msg3;
}
static void nr_generate_Msg4_MsgB(module_id_t module_idP,
int CC_id,
frame_t frameP,
slot_t slotP,
NR_UE_info_t *UE,
nfapi_nr_dl_tti_request_t *DL_req,
nfapi_nr_tx_data_request_t *TX_req)
{
gNB_MAC_INST *nr_mac = RC.nrmac[module_idP];
NR_COMMON_channels_t *cc = &nr_mac->common_channels[CC_id];
NR_UE_DL_BWP_t *dl_bwp = &UE->current_DL_BWP;
// if it is a DL slot, if the RA is in MSG4 state
if (is_dl_slot(slotP, &nr_mac->frame_structure)) {
NR_ServingCellConfigCommon_t *scc = cc->ServingCellConfigCommon;
NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
NR_SearchSpace_t *ss = sched_ctrl->search_space;
NR_RA_t *ra = UE->ra;
const char *ra_type_str = ra->ra_type == RA_2_STEP ? "MsgB" : "Msg4";
NR_ControlResourceSet_t *coreset = sched_ctrl->coreset;
AssertFatal(coreset != NULL, "Coreset cannot be null for RA %s\n", ra_type_str);
uint16_t mac_sdu_length = 0;
/* get the PID of a HARQ process awaiting retrnasmission, or -1 otherwise */
int current_harq_pid = sched_ctrl->retrans_dl_harq.head;
logical_chan_id_t lcid = DL_SCH_LCID_CCCH;
if (current_harq_pid < 0) {
// Check for data on SRB0 (RRCSetup)
mac_rlc_status_resp_t srb_status = nr_mac_rlc_status_ind(UE->rnti, frameP, lcid);
if (srb_status.bytes_in_buffer == 0) {
lcid = DL_SCH_LCID_DCCH;
// Check for data on SRB1 (RRCReestablishment, RRCReconfiguration)
srb_status = nr_mac_rlc_status_ind(UE->rnti, frameP, lcid);
}
// Need to wait until data for Msg4 is ready
if (srb_status.bytes_in_buffer == 0)
return;
mac_sdu_length = srb_status.bytes_in_buffer;
}
const int n_slots_frame = nr_mac->frame_structure.numb_slots_frame;
NR_beam_alloc_t beam = beam_allocation_procedure(&nr_mac->beam_info, frameP, slotP, UE->UE_beam_index, n_slots_frame);
if (beam.idx < 0)
return;
// get CCEindex, needed also for PUCCH and then later for PDCCH
uint8_t aggregation_level;
int CCEIndex = get_cce_index(nr_mac,
CC_id, slotP, 0,
&aggregation_level,
beam.idx,
ss,
coreset,
&sched_ctrl->sched_pdcch,
true,
0);
if (CCEIndex < 0) {
LOG_E(NR_MAC, "Cannot find free CCE for RA RNTI 0x%04x!\n", UE->rnti);
reset_beam_status(&nr_mac->beam_info, frameP, slotP, UE->UE_beam_index, n_slots_frame, beam.new_beam);
return;
}
// Checking if the DCI allocation is feasible in current subframe
nfapi_nr_dl_tti_request_body_t *dl_req = &DL_req->dl_tti_request_body;
if (dl_req->nPDUs > NFAPI_NR_MAX_DL_TTI_PDUS - 2) {
LOG_I(NR_MAC, "UE %04x: %d.%d FAPI DL structure is full\n", UE->rnti, frameP, slotP);
reset_beam_status(&nr_mac->beam_info, frameP, slotP, UE->UE_beam_index, n_slots_frame, beam.new_beam);
return;
}
uint8_t time_domain_assignment = get_dl_tda(nr_mac, slotP);
NR_Type0_PDCCH_CSS_config_t *type0_PDCCH_CSS_config = &nr_mac->type0_PDCCH_CSS_config[cc->ssb_index[UE->UE_beam_index]];
int mux_pattern = type0_PDCCH_CSS_config ? type0_PDCCH_CSS_config->type0_pdcch_ss_mux_pattern : 1;
NR_tda_info_t msg4_tda = get_dl_tda_info(dl_bwp,
ss->searchSpaceType->present,
time_domain_assignment,
scc->dmrs_TypeA_Position,
mux_pattern,
TYPE_TC_RNTI_,
coreset->controlResourceSetId,
false);
if (!msg4_tda.valid_tda)
return;
NR_pdsch_dmrs_t dmrs_info = get_dl_dmrs_params(scc, dl_bwp, &msg4_tda, 1);
bwp_info_t bwp_info = get_pdsch_bwp_start_size(nr_mac, UE);
uint8_t mcsTableIdx = dl_bwp->mcsTableIdx;
uint8_t mcsIndex = 0;
int rbStart = 0;
int rbSize = 0;
uint8_t tb_scaling = 0;
uint32_t tb_size = 0;
uint16_t pdu_length;
if(current_harq_pid >= 0) { // in case of retransmission
NR_UE_harq_t *harq = &sched_ctrl->harq_processes[current_harq_pid];
DevAssert(!harq->is_waiting);
pdu_length = harq->tb_size;
} else {
uint8_t subheader_len = (mac_sdu_length < 256) ? sizeof(NR_MAC_SUBHEADER_SHORT) : sizeof(NR_MAC_SUBHEADER_LONG);
pdu_length = mac_sdu_length + subheader_len + 13; // 13 is contention resolution length of msgB. It is divided into 3 parts:
// BI MAC subheader (1 Oct) + SuccessRAR MAC subheader (1 Oct) + SuccessRAR (11 Oct)
}
// increase PRBs until we get to BWPSize or TBS is bigger than MAC PDU size
do {
if(rbSize < bwp_info.bwpSize)
rbSize++;
else
mcsIndex++;
LOG_D(NR_MAC,"Calling nr_compute_tbs with N_PRB_DMRS %d, N_DMRS_SLOT %d\n",dmrs_info.N_PRB_DMRS,dmrs_info.N_DMRS_SLOT);
tb_size = nr_compute_tbs(nr_get_Qm_dl(mcsIndex, mcsTableIdx),
nr_get_code_rate_dl(mcsIndex, mcsTableIdx),
rbSize,
msg4_tda.nrOfSymbols,
dmrs_info.N_PRB_DMRS * dmrs_info.N_DMRS_SLOT,
0,
tb_scaling,1) >> 3;
} while (tb_size < pdu_length && mcsIndex<=28);
AssertFatal(tb_size >= pdu_length, "Cannot allocate %s\n", ra_type_str);
int i = 0;
uint16_t *vrb_map = cc[CC_id].vrb_map[beam.idx];
while ((i < rbSize) && (rbStart + rbSize <= bwp_info.bwpSize)) {
if (vrb_map[bwp_info.bwpStart + rbStart + i]&SL_to_bitmap(msg4_tda.startSymbolIndex, msg4_tda.nrOfSymbols)) {
rbStart += i+1;
i = 0;
} else {
i++;
}
}
if (rbStart > (bwp_info.bwpSize - rbSize)) {
LOG_E(NR_MAC, "Cannot find free vrb_map for RNTI %04x!\n", UE->rnti);
reset_beam_status(&nr_mac->beam_info, frameP, slotP, UE->UE_beam_index, n_slots_frame, beam.new_beam);
return;
}
int r_pucch = nr_get_pucch_resource(coreset, UE->current_UL_BWP.pucch_Config, CCEIndex);
LOG_D(NR_MAC, "Msg4 r_pucch %d CCEIndex %d\n", r_pucch, CCEIndex);
int alloc = nr_acknack_scheduling(nr_mac, UE, frameP, slotP, UE->UE_beam_index, r_pucch, 1);
if (alloc < 0) {
LOG_D(NR_MAC,"Couldn't find a pucch allocation for ack nack (msg4) in frame %d slot %d\n", frameP, slotP);
reset_beam_status(&nr_mac->beam_info, frameP, slotP, UE->UE_beam_index, n_slots_frame, beam.new_beam);
return;
}
// HARQ management
if (current_harq_pid < 0) {
AssertFatal(sched_ctrl->available_dl_harq.head >= 0,
"UE context not initialized: no HARQ processes found\n");
current_harq_pid = sched_ctrl->available_dl_harq.head;
remove_front_nr_list(&sched_ctrl->available_dl_harq);
}
NR_UE_harq_t *harq = &sched_ctrl->harq_processes[current_harq_pid];
NR_sched_pucch_t *pucch = &sched_ctrl->sched_pucch[alloc];
DevAssert(!harq->is_waiting);
add_tail_nr_list(&sched_ctrl->feedback_dl_harq, current_harq_pid);
harq->feedback_slot = pucch->ul_slot;
harq->feedback_frame = pucch->frame;
harq->is_waiting = true;
ra->harq_pid = current_harq_pid;
UE->mac_stats.dl.rounds[harq->round]++;
harq->tb_size = tb_size;
uint8_t *buf = allocate_transportBlock_buffer(&harq->transportBlock, tb_size);
// Bytes to be transmitted
if (harq->round == 0) {
uint16_t mac_pdu_length = 0;
if (ra->ra_type == RA_4_STEP) {
// UE Contention Resolution Identity MAC CE
mac_pdu_length = nr_write_ce_dlsch_pdu(module_idP, sched_ctrl, buf, 255, ra->cont_res_id);
} else if (ra->ra_type == RA_2_STEP) {
mac_pdu_length = nr_fill_successrar(sched_ctrl,
UE->rnti,
ra->cont_res_id,
pucch->resource_indicator,
pucch->timing_indicator,
buf,
mac_pdu_length);
} else {
AssertFatal(false, "RA type %d not implemented!\n", ra->ra_type);
}
uint8_t buffer[CCCH_SDU_SIZE];
uint8_t mac_subheader_len = sizeof(NR_MAC_SUBHEADER_SHORT);
// Get RLC data on the SRB (RRCSetup, RRCReestablishment)
mac_sdu_length = nr_mac_rlc_data_req(module_idP, UE->rnti, true, lcid, CCCH_SDU_SIZE, (char *)buffer);
if (mac_sdu_length < 256) {
((NR_MAC_SUBHEADER_SHORT *)&buf[mac_pdu_length])->R = 0;
((NR_MAC_SUBHEADER_SHORT *)&buf[mac_pdu_length])->F = 0;
((NR_MAC_SUBHEADER_SHORT *)&buf[mac_pdu_length])->LCID = lcid;
((NR_MAC_SUBHEADER_SHORT *)&buf[mac_pdu_length])->L = mac_sdu_length;
ra->mac_pdu_length = mac_pdu_length + mac_sdu_length + sizeof(NR_MAC_SUBHEADER_SHORT);
} else {
mac_subheader_len = sizeof(NR_MAC_SUBHEADER_LONG);
((NR_MAC_SUBHEADER_LONG *)&buf[mac_pdu_length])->R = 0;
((NR_MAC_SUBHEADER_LONG *)&buf[mac_pdu_length])->F = 1;
((NR_MAC_SUBHEADER_LONG *)&buf[mac_pdu_length])->LCID = lcid;
((NR_MAC_SUBHEADER_LONG *)&buf[mac_pdu_length])->L = htons(mac_sdu_length);
ra->mac_pdu_length = mac_pdu_length + mac_sdu_length + sizeof(NR_MAC_SUBHEADER_LONG);
}
memcpy(&buf[mac_pdu_length + mac_subheader_len], buffer, mac_sdu_length);
}
rnti_t rnti = ra->ra_type == RA_4_STEP ? UE->rnti : ra->MsgB_rnti;
NR_sched_pdsch_t sched_pdsch = {
.R = nr_get_code_rate_dl(mcsIndex, mcsTableIdx),
.Qm = nr_get_Qm_dl(mcsIndex, mcsTableIdx),
.mcs = mcsIndex,
.time_domain_allocation = time_domain_assignment,
.nrOfLayers = 1,
.dmrs_parms = dmrs_info,
.tda_info = msg4_tda,
.pm_index = 0,
.tb_size = tb_size,
.bwp_info = bwp_info,
.rbStart = rbStart,
.rbSize = rbSize
};
const int pduindex = nr_mac->pdu_index[CC_id]++;
prepare_dl_pdus(nr_mac,
UE,
&sched_pdsch,
dl_req,
pucch,
TYPE_TC_RNTI_,
aggregation_level,
CCEIndex,
harq->ndi,
current_harq_pid,
CC_id,
rnti,
harq->round,
tb_scaling,
pduindex);
// Reset TPC to 0 dB to not request new gain multiple times before computing new value for SNR
sched_ctrl->tpc1 = 1;
// Add padding header and zero rest out if there is space left
if (ra->mac_pdu_length < harq->tb_size) {
NR_MAC_SUBHEADER_FIXED *padding = (NR_MAC_SUBHEADER_FIXED *) &buf[ra->mac_pdu_length];
padding->R = 0;
padding->LCID = DL_SCH_LCID_PADDING;
for(int k = ra->mac_pdu_length+1; k<harq->tb_size; k++) {
buf[k] = 0;
}
}
T(T_GNB_MAC_DL_PDU_WITH_DATA, T_INT(module_idP), T_INT(CC_id), T_INT(UE->rnti),
T_INT(frameP), T_INT(slotP), T_INT(current_harq_pid), T_BUFFER(harq->transportBlock.buf, harq->tb_size));
// DL TX request
nfapi_nr_pdu_t *tx_req = &TX_req->pdu_list[TX_req->Number_of_PDUs];
memcpy(tx_req->TLVs[0].value.direct, harq->transportBlock.buf, sizeof(uint8_t) * harq->tb_size);
tx_req->PDU_index = pduindex;
tx_req->num_TLV = 1;
tx_req->TLVs[0].length = harq->tb_size;
tx_req->PDU_length = compute_PDU_length(tx_req->num_TLV, tx_req->TLVs[0].length);
TX_req->SFN = frameP;
TX_req->Number_of_PDUs++;
TX_req->Slot = slotP;
// Mark the corresponding symbols and RBs as used
fill_pdcch_vrb_map(nr_mac,
CC_id,
&sched_ctrl->sched_pdcch,
CCEIndex,
aggregation_level,
beam.idx);
for (int rb = 0; rb < rbSize; rb++) {
vrb_map[bwp_info.bwpStart + rb + rbStart] |= SL_to_bitmap(msg4_tda.startSymbolIndex, msg4_tda.nrOfSymbols);
}
ra->ra_state = nrRA_WAIT_Msg4_MsgB_ACK;
LOG_I(NR_MAC,
"UE %04x Generate %s: feedback at %4d.%2d, payload %d bytes, next state nrRA_WAIT_Msg4_MsgB_ACK\n",
UE->rnti,
ra_type_str,
pucch->frame,
pucch->ul_slot,
harq->tb_size);
}
}
bool nr_check_Msg4_MsgB_Ack(module_id_t module_id, frame_t frame, slot_t slot, NR_UE_info_t *UE, bool success)
{
NR_RA_t *ra = UE->ra;
const char *ra_type_str = ra->ra_type == RA_2_STEP ? "MsgB" : "Msg4";
const int current_harq_pid = ra->harq_pid;
NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
NR_UE_harq_t *harq = &sched_ctrl->harq_processes[current_harq_pid];
LOG_D(NR_MAC, "ue rnti 0x%04x, round %d, frame %d %d, harq id %d\n", UE->rnti, harq->round, frame, slot, current_harq_pid);
if (harq->round == 0) {
if (success) {
gNB_MAC_INST *nr_mac = RC.nrmac[module_id];
NR_ServingCellConfigCommon_t *scc = nr_mac->common_channels[0].ServingCellConfigCommon;
LOG_A(NR_MAC,
"%4d.%2d UE %04x: Received Ack of %s. CBRA procedure succeeded (%s)\n",
frame,
slot,
UE->rnti,
ra_type_str,
UE->CellGroup ? "UE Connected" : "UE Rejected");
if (UE->CellGroup) {
// we configure the UE using common search space with DCIX0 while waiting for a reconfiguration
configure_UE_BWP(nr_mac, scc, UE, false, NR_SearchSpace__searchSpaceType_PR_common, -1, -1);
transition_ra_connected_nr_ue(nr_mac, UE);
} else { // in case of UE reject
nr_release_ra_UE(nr_mac, UE->rnti);
return true;
}
} else {
LOG_I(NR_MAC, "%4d.%2d UE %04x: RA Procedure failed at %s!\n", frame, slot, UE->rnti, ra_type_str);
nr_mac_trigger_ul_failure(sched_ctrl, UE->current_DL_BWP.scs);
}
if (sched_ctrl->retrans_dl_harq.head >= 0) {
remove_nr_list(&sched_ctrl->retrans_dl_harq, current_harq_pid);
}
} else {
LOG_I(NR_MAC, "(UE %04x) Received Nack in %s, preparing retransmission!\n", UE->rnti, ra_type_str);
ra->ra_state = ra->ra_type == RA_4_STEP ? nrRA_Msg4 : nrRA_MsgB;
}
return false;
}
/////////////////////////////////////
// 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|//
/////////////////////////////////////
// WIP
// todo:
// - handle MAC RAR BI subheader
// - sending only 1 RAR subPDU
// - UL Grant: hardcoded CSI, TPC, time alloc
// - padding
static void nr_fill_rar(uint8_t Mod_idP, NR_UE_info_t *UE, uint8_t *dlsch_buffer, nfapi_nr_pusch_pdu_t *pusch_pdu)
{
NR_RA_t *ra = UE->ra;
LOG_D(NR_MAC,
"[gNB] Generate RAR MAC PDU frame %d slot %d preamble index %u TA command %d \n",
ra->Msg2_frame,
ra->Msg2_slot,
ra->preamble_index,
ra->timing_offset);
NR_RA_HEADER_BI *rarbi = (NR_RA_HEADER_BI *) dlsch_buffer;
NR_RA_HEADER_RAPID *rarh = (NR_RA_HEADER_RAPID *) (dlsch_buffer + 1);
NR_MAC_RAR *rar = (NR_MAC_RAR *) (dlsch_buffer + 2);
unsigned char csi_req = 0;
/// E/T/R/R/BI subheader ///
// E = 1, MAC PDU includes another MAC sub-PDU (RAPID)
// T = 0, Back-off indicator subheader
// R = 2, Reserved
// BI = 0, 5ms
rarbi->E = 1;
rarbi->T = 0;
rarbi->R = 0;
rarbi->BI = 0;
/// E/T/RAPID subheader ///
// E = 0, one only RAR, first and last
// T = 1, RAPID
rarh->E = 0;
rarh->T = 1;
rarh->RAPID = ra->preamble_index;
/// RAR MAC payload ///
rar->R = 0;
// TA command
rar->TA1 = (uint8_t) (ra->timing_offset >> 5); // 7 MSBs of timing advance
rar->TA2 = (uint8_t) (ra->timing_offset & 0x1f); // 5 LSBs of timing advance
// TC-RNTI
rar->TCRNTI_1 = (uint8_t) (UE->rnti >> 8); // 8 MSBs of rnti
rar->TCRNTI_2 = (uint8_t) (UE->rnti & 0xff); // 8 LSBs of rnti
// UL grant
if (pusch_pdu->frequency_hopping)
AssertFatal(1==0,"PUSCH with frequency hopping currently not supported");
int bwp_size = pusch_pdu->bwp_size;
int prb_alloc = PRBalloc_to_locationandbandwidth0(ra->msg3_nb_rb, ra->msg3_first_rb, bwp_size);
int valid_bits = 14;
int f_alloc = prb_alloc & ((1 << valid_bits) - 1);
uint32_t ul_grant = csi_req | (ra->msg3_TPC << 1) | (pusch_pdu->mcs_index << 4) | (ra->Msg3_tda_id << 8) | (f_alloc << 12) | (pusch_pdu->frequency_hopping << 26);
rar->UL_GRANT_1 = (uint8_t) (ul_grant >> 24) & 0x07;
rar->UL_GRANT_2 = (uint8_t) (ul_grant >> 16) & 0xff;
rar->UL_GRANT_3 = (uint8_t) (ul_grant >> 8) & 0xff;
rar->UL_GRANT_4 = (uint8_t) ul_grant & 0xff;
#ifdef DEBUG_RAR
LOG_I(NR_MAC, "rarh->E = 0x%x\n", rarh->E);
LOG_I(NR_MAC, "rarh->T = 0x%x\n", rarh->T);
LOG_I(NR_MAC, "rarh->RAPID = 0x%x (%i)\n", rarh->RAPID, rarh->RAPID);
LOG_I(NR_MAC, "rar->R = 0x%x\n", rar->R);
LOG_I(NR_MAC, "rar->TA1 = 0x%x\n", rar->TA1);
LOG_I(NR_MAC, "rar->TA2 = 0x%x\n", rar->TA2);
LOG_I(NR_MAC, "rar->UL_GRANT_1 = 0x%x\n", rar->UL_GRANT_1);
LOG_I(NR_MAC, "rar->UL_GRANT_2 = 0x%x\n", rar->UL_GRANT_2);
LOG_I(NR_MAC, "rar->UL_GRANT_3 = 0x%x\n", rar->UL_GRANT_3);
LOG_I(NR_MAC, "rar->UL_GRANT_4 = 0x%x\n", rar->UL_GRANT_4);
LOG_I(NR_MAC, "rar->TCRNTI_1 = 0x%x\n", rar->TCRNTI_1);
LOG_I(NR_MAC, "rar->TCRNTI_2 = 0x%x\n", rar->TCRNTI_2);
#endif
LOG_D(NR_MAC,
"In %s: Transmitted 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__,
rar->UL_GRANT_3 & 0x0f,
(rar->UL_GRANT_3 >> 4) | (rar->UL_GRANT_2 << 4) | ((rar->UL_GRANT_1 & 0x03) << 12),
rar->TA2 + (rar->TA1 << 5),
rar->UL_GRANT_4 >> 4,
rar->UL_GRANT_1 >> 2,
ra->msg3_TPC,
csi_req,
rar->TCRNTI_2 + (rar->TCRNTI_1 << 8));
// resetting msg3 TPC to 0dB for possible retransmissions
ra->msg3_TPC = 1;
}
/** @brief remove the UE with RNTI rnti from list of UEs doing RA.
*
* The corresponding function to add is add_new_UE_RA(). */
void nr_release_ra_UE(gNB_MAC_INST *mac, rnti_t rnti)
{
NR_UEs_t *UE_info = &mac->UE_info;
NR_SCHED_LOCK(&UE_info->mutex);
NR_UE_info_t *UE = remove_UE_from_list(NR_NB_RA_PROC_MAX, UE_info->access_ue_list, rnti);
NR_SCHED_UNLOCK(&UE_info->mutex);
if (UE) {
delete_nr_ue_data(UE, mac->common_channels, &UE_info->uid_allocator);
} else {
LOG_W(NR_MAC,"Call to release RA UE with rnti %04x, but not existing\n", rnti);
}
}
void nr_schedule_RA(module_id_t module_idP,
frame_t frameP,
slot_t slotP,
nfapi_nr_ul_dci_request_t *ul_dci_req,
nfapi_nr_dl_tti_request_t *DL_req,
nfapi_nr_tx_data_request_t *TX_req)
{
gNB_MAC_INST *mac = RC.nrmac[module_idP];
/* already mutex protected: held in gNB_dlsch_ulsch_scheduler() */
NR_SCHED_ENSURE_LOCKED(&mac->sched_lock);
start_meas(&mac->schedule_ra);
for (int CC_id = 0; CC_id < MAX_NUM_CCs; CC_id++) {
UE_iterator(mac->UE_info.access_ue_list, UE) {
NR_RA_t *ra = UE->ra;
if (ra->ra_state != nrRA_gNB_IDLE)
LOG_D(NR_MAC, "UE %04x frame.slot %d.%d RA state: %d\n", UE->rnti, frameP, slotP, ra->ra_state);
// Check RA Contention Resolution timer (TODO check this procedure)
if (ra->ra_type == RA_4_STEP && ra->ra_state > nrRA_WAIT_Msg3) {
ra->contention_resolution_timer--;
if (ra->contention_resolution_timer == 0) {
LOG_W(NR_MAC, "(%d.%d) RA Contention Resolution timer expired for UE 0x%04x, RA procedure failed...\n", frameP, slotP, UE->rnti);
bool requested = false;
if (UE->CellGroup) // request if we did not reject the UE
requested = nr_mac_request_release_ue(mac, UE->rnti);
if (!requested)
nr_release_ra_UE(mac, UE->rnti);
continue;
}
}
switch (ra->ra_state) {
case nrRA_Msg2:
nr_generate_Msg2(module_idP, CC_id, frameP, slotP, UE, DL_req, TX_req);
break;
case nrRA_Msg3_retransmission:
nr_generate_Msg3_retransmission(module_idP, CC_id, frameP, slotP, UE, ul_dci_req);
break;
case nrRA_Msg4:
case nrRA_MsgB:
nr_generate_Msg4_MsgB(module_idP, CC_id, frameP, slotP, UE, DL_req, TX_req);
break;
default:
break;
}
}
}
stop_meas(&mac->schedule_ra);
}