Files
openairinterface5g/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c
T
francescomani 98618a7dc8 The network must configure BWPs with consecutive IDs from 1
... so if we want to have 1 BWP configured at any given time we need to
reconfigure BWP 1 every time. This means that since the gNB has more
than 1 BWPs (for use for different UEs), that there is a "gNB BWP ID"
(as used by the gNB), which can be any BWP ID X, and one "UE BWP ID",
which is always 1. This commit implements this distinction.
2025-12-03 17:20:11 +01:00

866 lines
41 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 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_bch.c
* \brief procedures related to gNB for the BCH transport channel
* \author Navid Nikaein and Raymond Knopp, WEI-TAI CHEN
* \date 2010 - 2014, 2018
* \email: navid.nikaein@eurecom.fr, kroempa@gmail.com
* \version 1.0
* \company Eurecom, NTUST
* @ingroup _mac
*/
#include "assertions.h"
#include "NR_MAC_gNB/nr_mac_gNB.h"
#include "NR_MAC_gNB/mac_proto.h"
#include "common/utils/LOG/log.h"
#include "common/utils/LOG/vcd_signal_dumper.h"
#include "UTIL/OPT/opt.h"
#include "common/utils/nr/nr_common.h"
#define ENABLE_MAC_PAYLOAD_DEBUG
#include "common/ran_context.h"
#include "executables/softmodem-common.h"
static void schedule_ssb(frame_t frame,
slot_t slot,
NR_ServingCellConfigCommon_t *scc,
nfapi_nr_dl_tti_request_body_t *dl_req,
int i_ssb,
int beam_index,
uint8_t scoffset,
uint16_t offset_pointa,
uint32_t payload)
{
nfapi_nr_dl_tti_request_pdu_t *dl_config_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs];
memset((void *) dl_config_pdu, 0, sizeof(nfapi_nr_dl_tti_request_pdu_t));
dl_config_pdu->PDUType = NFAPI_NR_DL_TTI_SSB_PDU_TYPE;
dl_config_pdu->PDUSize = 2 + sizeof(nfapi_nr_dl_tti_ssb_pdu_rel15_t);
AssertFatal(scc->physCellId != NULL,"ServingCellConfigCommon->physCellId is null\n");
AssertFatal(*scc->physCellId < 1008 && *scc->physCellId >=0, "5G physicall cell id out of range: %ld\n", *scc->physCellId);
dl_config_pdu->ssb_pdu.ssb_pdu_rel15.PhysCellId = *scc->physCellId;
dl_config_pdu->ssb_pdu.ssb_pdu_rel15.BetaPss = 0;
dl_config_pdu->ssb_pdu.ssb_pdu_rel15.SsbBlockIndex = i_ssb;
AssertFatal(scc->downlinkConfigCommon,"scc->downlinkConfigCommonL is null\n");
AssertFatal(scc->downlinkConfigCommon->frequencyInfoDL,"scc->downlinkConfigCommon->frequencyInfoDL is null\n");
AssertFatal(scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencySSB,
"scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencySSB is null\n");
AssertFatal(scc->downlinkConfigCommon->frequencyInfoDL->frequencyBandList.list.count == 1,
"Frequency Band list does not have 1 element (%d)\n",
scc->downlinkConfigCommon->frequencyInfoDL->frequencyBandList.list.count);
AssertFatal(scc->ssbSubcarrierSpacing,"ssbSubcarrierSpacing is null\n");
AssertFatal(scc->downlinkConfigCommon->frequencyInfoDL->frequencyBandList.list.array[0], "band is null\n");
dl_config_pdu->ssb_pdu.ssb_pdu_rel15.SsbSubcarrierOffset = scoffset; //kSSB
dl_config_pdu->ssb_pdu.ssb_pdu_rel15.ssbOffsetPointA = offset_pointa;
dl_config_pdu->ssb_pdu.ssb_pdu_rel15.bchPayloadFlag = 1;
dl_config_pdu->ssb_pdu.ssb_pdu_rel15.bchPayload = payload;
dl_config_pdu->ssb_pdu.ssb_pdu_rel15.precoding_and_beamforming.num_prgs = 1;
dl_config_pdu->ssb_pdu.ssb_pdu_rel15.precoding_and_beamforming.prg_size = 20; //SSB is always 20RBs
dl_config_pdu->ssb_pdu.ssb_pdu_rel15.precoding_and_beamforming.dig_bf_interfaces = 1;
dl_config_pdu->ssb_pdu.ssb_pdu_rel15.precoding_and_beamforming.prgs_list[0].pm_idx = 0;
dl_config_pdu->ssb_pdu.ssb_pdu_rel15.precoding_and_beamforming.prgs_list[0].dig_bf_interface_list[0].beam_idx = beam_index;
dl_req->nPDUs++;
LOG_D(MAC,"Scheduling ssb %d at frame %d and slot %d\n", i_ssb, frame, slot);
}
static void fill_ssb_vrb_map(NR_COMMON_channels_t *cc,
int rbStart,
int ssb_subcarrier_offset,
uint16_t symStart,
int CC_id,
int beam)
{
AssertFatal(*cc->ServingCellConfigCommon->ssbSubcarrierSpacing !=
NR_SubcarrierSpacing_kHz240,
"240kHZ subcarrier won't work with current VRB map because a single SSB might be across 2 slots\n");
uint16_t *vrb_map = cc[CC_id].vrb_map[beam];
const int extra_prb = ssb_subcarrier_offset > 0;
for (int rb = 0; rb < 20 + extra_prb; rb++)
vrb_map[rbStart + rb] = SL_to_bitmap(symStart % NR_NUMBER_OF_SYMBOLS_PER_SLOT, 4);
}
static int encode_mib(NR_BCCH_BCH_Message_t *mib, frame_t frame, uint8_t *buffer, int buf_size)
{
int encode_size = 3;
AssertFatal(buf_size >= encode_size, "buffer of size %d too small, need 3 bytes\n", buf_size);
int encoded = encode_MIB_NR(mib, frame, buffer, encode_size);
DevAssert(encoded == encode_size);
return encode_size;
}
void schedule_nr_mib(module_id_t module_idP, frame_t frameP, slot_t slotP, nfapi_nr_dl_tti_request_t *DL_req)
{
gNB_MAC_INST *gNB = RC.nrmac[module_idP];
/* already mutex protected: held in gNB_dlsch_ulsch_scheduler() */
nfapi_nr_dl_tti_request_body_t *dl_req;
for (int CC_id = 0; CC_id < MAX_NUM_CCs; CC_id++) {
NR_COMMON_channels_t *cc= &gNB->common_channels[CC_id];
const NR_MIB_t *mib = cc->mib->message.choice.mib;
NR_ServingCellConfigCommon_t *scc = cc->ServingCellConfigCommon;
const int slots_per_frame = gNB->frame_structure.numb_slots_frame;
dl_req = &DL_req->dl_tti_request_body;
// get MIB every 8 frames
if(((slotP == 0) && (frameP & 7) == 0) ||
gNB->first_MIB) {
int mib_sdu_length = encode_mib(cc->mib, frameP, cc->MIB_pdu, sizeof(cc->MIB_pdu));
// flag to avoid sending an empty MIB in the first frames of execution since gNB doesn't get at the beginning in frame 0 slot 0
gNB->first_MIB = false;
LOG_D(MAC,
"[gNB %d] Frame %d : MIB->BCH CC_id %d, Received %d bytes\n",
module_idP,
frameP,
CC_id,
mib_sdu_length);
}
uint32_t mib_pdu = (*(uint32_t *)cc->MIB_pdu) & ((1 << 24) - 1);
int8_t ssb_period = *scc->ssb_periodicityServingCell;
uint8_t ssb_frame_periodicity = 1; // every how many frames SSB are generated
if (ssb_period > 1) // 0 is every half frame
ssb_frame_periodicity = 1 << (ssb_period -1);
if (!(frameP % ssb_frame_periodicity) && ((slotP < (slots_per_frame >> 1)) || (ssb_period == 0))) {
// schedule SSB only for given frames according to SSB periodicity
// and in first half frame unless periodicity of 5ms
int rel_slot;
if (ssb_period == 0) // scheduling every half frame
rel_slot = slotP % (slots_per_frame >> 1);
else
rel_slot = slotP;
NR_SubcarrierSpacing_t scs = *scc->ssbSubcarrierSpacing;
const long band = *scc->downlinkConfigCommon->frequencyInfoDL->frequencyBandList.list.array[0];
const uint16_t offset_pointa = gNB->ssb_OffsetPointA;
uint8_t ssbSubcarrierOffset = gNB->ssb_SubcarrierOffset;
const BIT_STRING_t *shortBitmap = &scc->ssb_PositionsInBurst->choice.shortBitmap;
const BIT_STRING_t *mediumBitmap = &scc->ssb_PositionsInBurst->choice.mediumBitmap;
const BIT_STRING_t *longBitmap = &scc->ssb_PositionsInBurst->choice.longBitmap;
switch (scc->ssb_PositionsInBurst->present) {
case 1:
// short bitmap (<3GHz) max 4 SSBs
for (int i_ssb = 0; i_ssb < 4; i_ssb++) {
if ((shortBitmap->buf[0] >> (7 - i_ssb)) & 0x01) {
uint16_t ssb_start_symbol = get_ssb_start_symbol(band, scs, i_ssb);
// if start symbol is in current slot, schedule current SSB, fill VRB map and call get_type0_PDCCH_CSS_config_parameters
if ((ssb_start_symbol / 14) == rel_slot) {
int beam_index = get_fapi_beamforming_index(gNB, i_ssb);
NR_beam_alloc_t beam = beam_allocation_procedure(&gNB->beam_info, frameP, slotP, beam_index, slots_per_frame);
AssertFatal(beam.idx >= 0, "Cannot allocate SSB %d in any available beam\n", i_ssb);
const int prb_offset = offset_pointa >> scs;
schedule_ssb(frameP, slotP, scc, dl_req, i_ssb, beam_index, ssbSubcarrierOffset, offset_pointa, mib_pdu);
fill_ssb_vrb_map(cc, prb_offset, ssbSubcarrierOffset, ssb_start_symbol, CC_id, beam.idx);
if (IS_SA_MODE(get_softmodem_params())) {
get_type0_PDCCH_CSS_config_parameters(&gNB->type0_PDCCH_CSS_config[i_ssb],
frameP,
mib,
slots_per_frame,
ssbSubcarrierOffset,
ssb_start_symbol,
scs,
FR1,
band,
i_ssb,
ssb_frame_periodicity,
prb_offset);
gNB->type0_PDCCH_CSS_config[i_ssb].active = true;
}
}
}
}
break;
case 2:
// medium bitmap (<6GHz) max 8 SSBs
for (int i_ssb = 0; i_ssb < 8; i_ssb++) {
if ((mediumBitmap->buf[0] >> (7 - i_ssb)) & 0x01) {
uint16_t ssb_start_symbol = get_ssb_start_symbol(band, scs, i_ssb);
// if start symbol is in current slot, schedule current SSB, fill VRB map and call get_type0_PDCCH_CSS_config_parameters
if ((ssb_start_symbol / 14) == rel_slot) {
int beam_index = get_fapi_beamforming_index(gNB, i_ssb);
NR_beam_alloc_t beam = beam_allocation_procedure(&gNB->beam_info, frameP, slotP, beam_index, slots_per_frame);
AssertFatal(beam.idx >= 0, "Cannot allocate SSB %d in any available beam\n", i_ssb);
const int prb_offset = offset_pointa >> scs;
schedule_ssb(frameP, slotP, scc, dl_req, i_ssb, beam_index, ssbSubcarrierOffset, offset_pointa, mib_pdu);
fill_ssb_vrb_map(cc, prb_offset, ssbSubcarrierOffset, ssb_start_symbol, CC_id, beam.idx);
if (IS_SA_MODE(get_softmodem_params())) {
get_type0_PDCCH_CSS_config_parameters(&gNB->type0_PDCCH_CSS_config[i_ssb],
frameP,
mib,
slots_per_frame,
ssbSubcarrierOffset,
ssb_start_symbol,
scs,
FR1,
band,
i_ssb,
ssb_frame_periodicity,
prb_offset);
gNB->type0_PDCCH_CSS_config[i_ssb].active = true;
}
}
}
}
break;
case 3:
// long bitmap FR2 max 64 SSBs
for (int i_ssb = 0; i_ssb < 64; i_ssb++) {
if ((longBitmap->buf[i_ssb / 8] >> (7 - (i_ssb % 8))) & 0x01) {
uint16_t ssb_start_symbol = get_ssb_start_symbol(band, scs, i_ssb);
// if start symbol is in current slot, schedule current SSB, fill VRB map and call get_type0_PDCCH_CSS_config_parameters
if ((ssb_start_symbol / 14) == rel_slot) {
int beam_index = get_fapi_beamforming_index(gNB, i_ssb);
NR_beam_alloc_t beam = beam_allocation_procedure(&gNB->beam_info, frameP, slotP, beam_index, slots_per_frame);
AssertFatal(beam.idx >= 0, "Cannot allocate SSB %d in any available beam\n", i_ssb);
const int prb_offset = offset_pointa >> (scs-2); // reference 60kHz
schedule_ssb(frameP, slotP, scc, dl_req, i_ssb, beam_index, ssbSubcarrierOffset, offset_pointa, mib_pdu);
fill_ssb_vrb_map(cc, prb_offset, ssbSubcarrierOffset >> (scs - 2), ssb_start_symbol, CC_id, beam.idx);
if (IS_SA_MODE(get_softmodem_params())) {
get_type0_PDCCH_CSS_config_parameters(&gNB->type0_PDCCH_CSS_config[i_ssb],
frameP,
mib,
slots_per_frame,
ssbSubcarrierOffset,
ssb_start_symbol,
scs,
FR2,
band,
i_ssb,
ssb_frame_periodicity,
prb_offset);
gNB->type0_PDCCH_CSS_config[i_ssb].active = true;
}
}
}
}
break;
default:
AssertFatal(0,"SSB bitmap size value %d undefined (allowed values 1,2,3)\n",
scc->ssb_PositionsInBurst->present);
}
}
}
}
static void update_rb_mcs_tbs(NR_sched_pdsch_t *pdsch, uint32_t num_total_bytes, uint16_t *vrb_map)
{
const NR_tda_info_t *tda_info = &pdsch->tda_info;
// Calculate number of PRB_DMRS
uint8_t N_PRB_DMRS = pdsch->dmrs_parms.N_PRB_DMRS;
LOG_D(MAC, "dlDmrsSymbPos %x\n", pdsch->dmrs_parms.dl_dmrs_symb_pos);
int mcsTableIdx = 0;
const uint16_t slbitmap = SL_to_bitmap(tda_info->startSymbolIndex, tda_info->nrOfSymbols);
int bwpSize = pdsch->bwp_info.bwpSize;
int bwpStart = pdsch->bwp_info.bwpStart;
int rbStop = bwpSize - 1;
while (pdsch->rbStart < rbStop) {
if (vrb_map[pdsch->rbStart + bwpStart] & slbitmap)
pdsch->rbStart++;
else {
int max_rbSize = 0;
while (pdsch->rbStart + max_rbSize <= rbStop && !(vrb_map[pdsch->rbStart + max_rbSize + bwpStart] & slbitmap))
max_rbSize++;
bool res = false;
while (res == false && pdsch->mcs < 10) {
pdsch->Qm = nr_get_Qm_dl(pdsch->mcs, mcsTableIdx);
pdsch->R = nr_get_code_rate_dl(pdsch->mcs, mcsTableIdx);
res = nr_find_nb_rb(pdsch->Qm,
pdsch->R,
1, // no transform precoding for DL
1, // single layer
tda_info->nrOfSymbols,
pdsch->dmrs_parms.N_PRB_DMRS * pdsch->dmrs_parms.N_DMRS_SLOT,
num_total_bytes,
1, // min_rbSize
max_rbSize,
&pdsch->tb_size,
&pdsch->rbSize);
if (!res)
pdsch->mcs++;
}
break;
}
}
AssertFatal(pdsch->tb_size >= num_total_bytes,
"Couldn't allocate enough resources for %d bytes in SIB PDSCH (rbStart %d, rbSize %d, bwpSize %d)\n",
num_total_bytes,
pdsch->rbStart,
pdsch->rbSize,
bwpSize);
LOG_D(NR_MAC,
"mcs=%i, startSymbolIndex = %i, nrOfSymbols = %i, rbSize = %i, TBS = %i, dmrs_length %d, N_PRB_DMRS = %d, mappingtype = %d\n",
pdsch->mcs,
tda_info->startSymbolIndex,
tda_info->nrOfSymbols,
pdsch->rbSize,
pdsch->tb_size,
pdsch->dmrs_parms.N_DMRS_SLOT,
N_PRB_DMRS,
tda_info->mapping_type);
}
static NR_sched_pdsch_t schedule_control_sib1(gNB_MAC_INST *gNB_mac,
int CC_id,
NR_sched_pdcch_t *pdcch,
NR_Type0_PDCCH_CSS_config_t *type0_PDCCH_CSS_config,
int time_domain_allocation,
NR_pdsch_dmrs_t *dmrs_parms,
NR_tda_info_t *tda_info,
uint8_t candidate_idx,
int beam,
uint16_t num_total_bytes)
{
AssertFatal(gNB_mac->sched_ctrlCommon, "sched_ctrlCommon is NULL\n");
NR_COMMON_channels_t *cc = &gNB_mac->common_channels[CC_id];
uint16_t *vrb_map = cc->vrb_map[beam];
NR_sched_pdsch_t pdsch = {
.bwp_info = get_pdsch_bwp_start_size(gNB_mac, NULL),
.time_domain_allocation = time_domain_allocation,
.dmrs_parms = *dmrs_parms,
.tda_info = *tda_info,
.nrOfLayers = 1,
.pm_index = 0,
.mcs = 0, // starting from mcs 0
};
gNB_mac->sched_ctrlCommon->num_total_bytes = num_total_bytes;
uint8_t nr_of_candidates = 0;
for (int i=0; i<3; i++) {
find_aggregation_candidates(&gNB_mac->sched_ctrlCommon->aggregation_level, &nr_of_candidates, gNB_mac->sched_ctrlCommon->search_space,4<<i);
if (nr_of_candidates>0) break; // choosing the lower value of aggregation level available
}
AssertFatal(nr_of_candidates>0,"nr_of_candidates is 0\n");
gNB_mac->sched_ctrlCommon->cce_index = find_pdcch_candidate(gNB_mac,
CC_id,
gNB_mac->sched_ctrlCommon->aggregation_level,
nr_of_candidates,
beam,
pdcch,
gNB_mac->sched_ctrlCommon->coreset,
0);
AssertFatal(gNB_mac->sched_ctrlCommon->cce_index >= 0, "Could not find CCE for coreset0\n");
update_rb_mcs_tbs(&pdsch, gNB_mac->sched_ctrlCommon->num_total_bytes, vrb_map);
// Mark the corresponding RBs as used
fill_pdcch_vrb_map(gNB_mac,
CC_id,
pdcch,
gNB_mac->sched_ctrlCommon->cce_index,
gNB_mac->sched_ctrlCommon->aggregation_level,
beam);
for (int rb = 0; rb < pdsch.rbSize; rb++) {
vrb_map[rb + type0_PDCCH_CSS_config->cset_start_rb] |= SL_to_bitmap(tda_info->startSymbolIndex, tda_info->nrOfSymbols);
}
return pdsch;
}
static void nr_fill_nfapi_dl_SIB_pdu(gNB_MAC_INST *gNB_mac,
NR_sched_pdsch_t *pdsch,
NR_sched_pdcch_t *pdcch,
NR_SearchSpace_t *search_space,
NR_ControlResourceSet_t *coreset,
int aggregation_level,
int cce_index,
nfapi_nr_dl_tti_request_body_t *dl_req,
int pdu_index,
NR_Type0_PDCCH_CSS_config_t *type0_PDCCH_CSS_config,
bool is_sib1,
int beam_index)
{
NR_COMMON_channels_t *cc = gNB_mac->common_channels;
NR_ServingCellConfigCommon_t *scc = cc->ServingCellConfigCommon;
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(*dl_tti_pdcch_pdu));
dl_tti_pdcch_pdu->PDUType = NFAPI_NR_DL_TTI_PDCCH_PDU_TYPE;
dl_tti_pdcch_pdu->PDUSize = (uint16_t)(4+sizeof(nfapi_nr_dl_tti_pdcch_pdu));
dl_req->nPDUs += 1;
nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15 = &dl_tti_pdcch_pdu->pdcch_pdu.pdcch_pdu_rel15;
nr_configure_pdcch(pdcch_pdu_rel15, coreset, pdcch);
nfapi_nr_dl_tti_request_pdu_t *dl_tti_pdsch_pdu = &dl_req->dl_tti_pdu_list[dl_req->nPDUs];
memset(dl_tti_pdsch_pdu, 0, sizeof(*dl_tti_pdcch_pdu));
dl_tti_pdsch_pdu->PDUType = NFAPI_NR_DL_TTI_PDSCH_PDU_TYPE;
dl_tti_pdsch_pdu->PDUSize = (uint16_t)(4+sizeof(nfapi_nr_dl_tti_pdsch_pdu));
dl_req->nPDUs += 1;
nfapi_nr_dl_tti_pdsch_pdu_rel15_t *pdsch_pdu_rel15 = prepare_pdsch_pdu(dl_tti_pdsch_pdu,
gNB_mac,
NULL,
pdsch,
NULL,
is_sib1,
0,
SI_RNTI,
beam_index,
1,
pdu_index);
LOG_D(NR_MAC,
"OtherSI:bwpStart %d, bwpSize %d, rbStart %d, rbSize %d, dlDmrsSymbPos = 0x%x\n",
pdsch_pdu_rel15->BWPStart,
pdsch_pdu_rel15->BWPSize,
pdsch_pdu_rel15->rbStart,
pdsch_pdu_rel15->rbSize,
pdsch_pdu_rel15->dlDmrsSymbPos);
/* Fill PDCCH DL DCI PDU */
nfapi_nr_dl_dci_pdu_t *dci_pdu = prepare_dci_pdu(pdcch_pdu_rel15,
scc,
search_space,
coreset,
aggregation_level,
cce_index,
beam_index,
SI_RNTI);
pdcch_pdu_rel15->numDlDci++;
/* DCI payload */
int dci_format = NR_DL_DCI_FORMAT_1_0;
int rnti_type = TYPE_SI_RNTI_;
dci_pdu_rel15_t dci_payload = prepare_dci_dl_payload(gNB_mac,
NULL,
rnti_type,
NR_SearchSpace__searchSpaceType_PR_common,
pdsch_pdu_rel15,
pdsch,
NULL,
0,
0,
is_sib1);
fill_dci_pdu_rel15(NULL,
NULL,
NULL,
dci_pdu,
&dci_payload,
dci_format,
rnti_type,
search_space,
coreset,
0,
gNB_mac->cset0_bwp_size);
LOG_D(MAC,
"BWPSize: %3i, BWPStart: %3i, SubcarrierSpacing: %i, CyclicPrefix: %i, StartSymbolIndex: %i, DurationSymbols: %i, "
"CceRegMappingType: %i, RegBundleSize: %i, InterleaverSize: %i, CoreSetType: %i, ShiftIndex: %i, precoderGranularity: %i, "
"numDlDci: %i\n",
pdcch_pdu_rel15->BWPSize,
pdcch_pdu_rel15->BWPStart,
pdcch_pdu_rel15->SubcarrierSpacing,
pdcch_pdu_rel15->CyclicPrefix,
pdcch_pdu_rel15->StartSymbolIndex,
pdcch_pdu_rel15->DurationSymbols,
pdcch_pdu_rel15->CceRegMappingType,
pdcch_pdu_rel15->RegBundleSize,
pdcch_pdu_rel15->InterleaverSize,
pdcch_pdu_rel15->CoreSetType,
pdcch_pdu_rel15->ShiftIndex,
pdcch_pdu_rel15->precoderGranularity,
pdcch_pdu_rel15->numDlDci);
}
void schedule_nr_sib1(module_id_t module_idP,
frame_t frameP,
slot_t slotP,
nfapi_nr_dl_tti_request_t *DL_req,
nfapi_nr_tx_data_request_t *TX_req)
{
/* already mutex protected: held in gNB_dlsch_ulsch_scheduler() */
// TODO: Get these values from RRC
const int CC_id = 0;
uint8_t candidate_idx = 0;
gNB_MAC_INST *gNB_mac = RC.nrmac[module_idP];
NR_ServingCellConfigCommon_t *scc = gNB_mac->common_channels[CC_id].ServingCellConfigCommon;
int time_domain_allocation = gNB_mac->radio_config.sib1_tda;
int L_max;
switch (scc->ssb_PositionsInBurst->present) {
case 1:
L_max = 4;
break;
case 2:
L_max = 8;
break;
case 3:
L_max = 64;
break;
default:
AssertFatal(0,"SSB bitmap size value %d undefined (allowed values 1,2,3)\n",
scc->ssb_PositionsInBurst->present);
}
for (int i = 0; i < L_max; i++) {
NR_Type0_PDCCH_CSS_config_t *type0_PDCCH_CSS_config = &gNB_mac->type0_PDCCH_CSS_config[i];
if((frameP % 2 == type0_PDCCH_CSS_config->sfn_c) &&
(slotP == type0_PDCCH_CSS_config->n_0) &&
(type0_PDCCH_CSS_config->num_rbs > 0) &&
(type0_PDCCH_CSS_config->active == true)) {
AssertFatal(is_dl_slot(slotP, &gNB_mac->frame_structure),
"Trying to schedule SIB1 in slot %d which is not DL. Check searchSpaceZero configuration.\n",
slotP);
const int n_slots_frame = gNB_mac->frame_structure.numb_slots_frame;
int beam_index = get_fapi_beamforming_index(gNB_mac, i);
NR_beam_alloc_t beam = beam_allocation_procedure(&gNB_mac->beam_info, frameP, slotP, beam_index, n_slots_frame);
AssertFatal(beam.idx >= 0, "Cannot allocate SIB1 corresponding to SSB %d in any available beam\n", i);
LOG_D(NR_MAC,"(%d.%d) SIB1 transmission: ssb_index %d\n", frameP, slotP, type0_PDCCH_CSS_config->ssb_index);
default_table_type_t table_type = get_default_table_type(type0_PDCCH_CSS_config->type0_pdcch_ss_mux_pattern);
// assuming normal CP
NR_tda_info_t tda_info = get_info_from_tda_tables(table_type, time_domain_allocation, scc->dmrs_TypeA_Position, true);
AssertFatal((tda_info.startSymbolIndex + tda_info.nrOfSymbols) < 14,
"SIB1 TDA %d would cause overlap with CSI-RS. Please select a different SIB1 TDA.\n",
time_domain_allocation);
NR_pdsch_dmrs_t dmrs_parms = get_dl_dmrs_params(scc, NULL, &tda_info, 1);
NR_sched_pdcch_t sched_pdcch = set_pdcch_structure(NULL,
gNB_mac->sched_ctrlCommon->search_space,
gNB_mac->sched_ctrlCommon->coreset,
scc,
NULL,
type0_PDCCH_CSS_config);
NR_COMMON_channels_t *cc = &gNB_mac->common_channels[0];
// Configure sched_ctrlCommon for SIB1
NR_sched_pdsch_t sched_pdsch = schedule_control_sib1(gNB_mac,
CC_id,
&sched_pdcch,
type0_PDCCH_CSS_config,
time_domain_allocation,
&dmrs_parms,
&tda_info,
candidate_idx,
beam.idx,
cc->sib1_bcch_length);
nfapi_nr_dl_tti_request_body_t *dl_req = &DL_req->dl_tti_request_body;
int pdu_index = gNB_mac->pdu_index[0]++;
nr_fill_nfapi_dl_SIB_pdu(gNB_mac,
&sched_pdsch,
&sched_pdcch,
gNB_mac->sched_ctrlCommon->search_space,
gNB_mac->sched_ctrlCommon->coreset,
gNB_mac->sched_ctrlCommon->aggregation_level,
gNB_mac->sched_ctrlCommon->cce_index,
dl_req,
pdu_index,
type0_PDCCH_CSS_config,
true,
beam_index);
const int ntx_req = TX_req->Number_of_PDUs;
nfapi_nr_pdu_t *tx_req = &TX_req->pdu_list[ntx_req];
// Data to be transmitted
memcpy(tx_req->TLVs[0].value.direct, cc->sib1_bcch_pdu, sched_pdsch.tb_size);
tx_req->PDU_index = pdu_index;
tx_req->num_TLV = 1;
tx_req->TLVs[0].length = sched_pdsch.tb_size;
tx_req->PDU_length = compute_PDU_length(tx_req->num_TLV, tx_req->TLVs[0].length);
TX_req->Number_of_PDUs++;
TX_req->SFN = frameP;
TX_req->Slot = slotP;
type0_PDCCH_CSS_config->active = false;
T(T_GNB_MAC_DL_PDU_WITH_DATA,
T_INT(module_idP),
T_INT(CC_id),
T_INT(0xffff),
T_INT(frameP),
T_INT(slotP),
T_INT(0 /* harq_pid */),
T_BUFFER(cc->sib1_bcch_pdu, cc->sib1_bcch_length));
}
}
}
struct NR_SchedulingInfo2_r17 *find_sib19_sched_info(const struct NR_SI_SchedulingInfo_v1700 *si_schedulinginfo2_r17)
{
for (int i = 0; i < si_schedulinginfo2_r17->schedulingInfoList2_r17.list.count; i++) {
const struct NR_SIB_Mapping_v1700 *sib_MappingInfo_r17 =
&si_schedulinginfo2_r17->schedulingInfoList2_r17.list.array[i]->sib_MappingInfo_r17;
for (int j = 0; j < sib_MappingInfo_r17->list.count; j++) {
if (sib_MappingInfo_r17->list.array[j]->sibType_r17.choice.type1_r17
== NR_SIB_TypeInfo_v1700__sibType_r17__type1_r17_sibType19) {
return si_schedulinginfo2_r17->schedulingInfoList2_r17.list.array[i];
}
}
}
return NULL;
}
static void other_sib_sched_control(module_id_t module_idP,
frame_t frame,
slot_t slot,
int beam_index,
NR_SearchSpace_t *ss,
nfapi_nr_dl_tti_request_t *DL_req,
nfapi_nr_tx_data_request_t *TX_req,
int payload_idx)
{
gNB_MAC_INST *gNB_mac = RC.nrmac[module_idP];
AssertFatal(is_dl_slot(slot, &gNB_mac->frame_structure),
"Trying to schedule otherSIB in slot %d which is not DL. Wrong Configuration\n",
slot);
NR_ServingCellConfigCommon_t *scc = gNB_mac->common_channels[0].ServingCellConfigCommon;
int n_slots_frame = gNB_mac->frame_structure.numb_slots_frame;
NR_beam_alloc_t beam = beam_allocation_procedure(&gNB_mac->beam_info, frame, slot, beam_index, n_slots_frame);
AssertFatal(beam.idx >= 0, "Cannot allocate otherSIB corresponding for SSB number %d in any available beam\n", beam_index);
LOG_D(NR_MAC, "(%d.%d) otherSIB payload %d transmission for ssb number %d\n", frame, slot, payload_idx, beam_index);
NR_COMMON_channels_t *cc = &gNB_mac->common_channels[0];
NR_Type0_PDCCH_CSS_config_t *type0_PDCCH_CSS_config = &gNB_mac->type0_PDCCH_CSS_config[cc->ssb_index[beam_index]];
AssertFatal(gNB_mac->sched_ctrlCommon, "sched_ctrlCommon is NULL\n");
NR_PDSCH_ConfigCommon_t *pdsch_ConfigCommon = scc->downlinkConfigCommon->initialDownlinkBWP->pdsch_ConfigCommon->choice.setup;
int time_domain_allocation = 1;
NR_tda_info_t tda_info = set_tda_info_from_list(pdsch_ConfigCommon->pdsch_TimeDomainAllocationList, time_domain_allocation);
LOG_D(NR_MAC,"tda_info.startSymbolIndex: %d, tda_info.nrOfSymbols: %d\n", tda_info.startSymbolIndex, tda_info.nrOfSymbols);
NR_pdsch_dmrs_t dmrs_parms = get_dl_dmrs_params(scc, NULL, &tda_info, 1);
uint8_t aggregation_level = 0;
uint8_t nr_of_candidates = 0;
for (int i = 0; i < 5; i++) {
find_aggregation_candidates(&aggregation_level, &nr_of_candidates, ss, 16 >> i);
if (nr_of_candidates > 0)
break; // choosing the higher value of aggregation level available
}
AssertFatal(nr_of_candidates > 0, "nr_of_candidates is 0\n");
AssertFatal(ss->controlResourceSetId, "ss->controlResourceSetId is NULL\n");
NR_ControlResourceSet_t *coreset = get_coreset(gNB_mac, scc, NULL, *ss->controlResourceSetId);
if (!gNB_mac->sched_pdcch_otherSI) {
gNB_mac->sched_pdcch_otherSI = calloc(1, sizeof(*gNB_mac->sched_pdcch_otherSI));
*gNB_mac->sched_pdcch_otherSI = set_pdcch_structure(gNB_mac, ss, coreset, scc, NULL, type0_PDCCH_CSS_config);
}
int cce_index = find_pdcch_candidate(gNB_mac,
0,
aggregation_level,
nr_of_candidates,
beam.idx,
gNB_mac->sched_pdcch_otherSI,
coreset,
0);
AssertFatal(cce_index >= 0, "Could not find CCE for otherSIB DCI\n");
// Mark the corresponding RBs as used
fill_pdcch_vrb_map(gNB_mac, 0, gNB_mac->sched_pdcch_otherSI, cce_index, aggregation_level, beam.idx);
NR_sched_pdsch_t sched_pdsch_otherSI = {0};
sched_pdsch_otherSI.time_domain_allocation = time_domain_allocation;
sched_pdsch_otherSI.bwp_info = get_pdsch_bwp_start_size(gNB_mac, NULL);
sched_pdsch_otherSI.dmrs_parms = dmrs_parms;
sched_pdsch_otherSI.tda_info = tda_info;
sched_pdsch_otherSI.nrOfLayers = 1;
sched_pdsch_otherSI.pm_index = 0;
sched_pdsch_otherSI.mcs = 0; // starting from mcs 0
uint16_t *vrb_map = cc->vrb_map[beam.idx];
uint8_t *sib_bcch_pdu = cc->other_sib_bcch_pdu[payload_idx];
int num_total_bytes = cc->other_sib_bcch_length[payload_idx];
update_rb_mcs_tbs(&sched_pdsch_otherSI, num_total_bytes, vrb_map);
for (int rb = 0; rb < sched_pdsch_otherSI.rbSize; rb++) {
vrb_map[rb + type0_PDCCH_CSS_config->cset_start_rb] |= SL_to_bitmap(tda_info.startSymbolIndex, tda_info.nrOfSymbols);
}
int pdu_index = gNB_mac->pdu_index[0]++;
nfapi_nr_dl_tti_request_body_t *dl_req = &DL_req->dl_tti_request_body;
nr_fill_nfapi_dl_SIB_pdu(gNB_mac,
&sched_pdsch_otherSI,
gNB_mac->sched_pdcch_otherSI,
ss,
coreset,
aggregation_level,
cce_index,
dl_req,
pdu_index,
type0_PDCCH_CSS_config,
false,
beam_index);
const int ntx_req = TX_req->Number_of_PDUs;
nfapi_nr_pdu_t *tx_req = &TX_req->pdu_list[ntx_req];
// Data to be transmitted
memcpy(tx_req->TLVs[0].value.direct, sib_bcch_pdu, sched_pdsch_otherSI.tb_size);
tx_req->PDU_index = pdu_index;
tx_req->num_TLV = 1;
tx_req->TLVs[0].length = sched_pdsch_otherSI.tb_size;
tx_req->PDU_length = compute_PDU_length(tx_req->num_TLV, tx_req->TLVs[0].length);
TX_req->Number_of_PDUs++;
TX_req->SFN = frame;
TX_req->Slot = slot;
T(T_GNB_MAC_DL_PDU_WITH_DATA,
T_INT(module_idP),
T_INT(0),
T_INT(SI_RNTI),
T_INT(frame),
T_INT(slot),
T_INT(0), // harq_pid
T_BUFFER(sib_bcch_pdu, num_total_bytes));
}
// This function verifies if we need to schedule otherSIB in frame and slot
static bool test_other_sib_sched_occasion(int window_pos,
int window_len,
int period,
int n_slots_frame,
int frame,
int slot,
int rel_frame,
int rel_slot)
{
int x = (window_pos - 1) * window_len;
int T = 8 << period;
int test_frame = (frame - rel_frame) % MAX_FRAME_NUMBER;
int si_slot = (x % n_slots_frame) + rel_slot;
bool res = ((test_frame % T) != (x / n_slots_frame)) || (si_slot != slot);
return res;
}
void schedule_nr_other_sib(module_id_t module_idP,
frame_t frame,
slot_t slot,
nfapi_nr_dl_tti_request_t *DL_req,
nfapi_nr_tx_data_request_t *TX_req)
{
gNB_MAC_INST *gNB_mac = RC.nrmac[module_idP];
NR_COMMON_channels_t *cc = &gNB_mac->common_channels[0];
NR_SIB1_t *sib1 = cc->sib1->message.choice.c1->choice.systemInformationBlockType1;
NR_SI_SchedulingInfo_t *schedInfo = sib1->si_SchedulingInfo;
if (!schedInfo)
return;
NR_ServingCellConfigCommon_t *scc = gNB_mac->common_channels[0].ServingCellConfigCommon;
NR_PDCCH_ConfigCommon_t *pdcch_common = scc->downlinkConfigCommon->initialDownlinkBWP->pdcch_ConfigCommon->choice.setup;
NR_SearchSpaceId_t *ss_id = pdcch_common->searchSpaceOtherSystemInformation;
AssertFatal(ss_id, "searchSpaceOtherSystemInformation not present\n");
NR_SearchSpace_t *ss = NULL;
for (int i = 0; i < pdcch_common->commonSearchSpaceList->list.count; i++) {
if (pdcch_common->commonSearchSpaceList->list.array[i]->searchSpaceId == *ss_id)
ss = pdcch_common->commonSearchSpaceList->list.array[i];
}
AssertFatal(ss, "searchSpaceOtherSystemInformation not found\n");
NR_SI_SchedulingInfo_v1700_t *schedInfo17 = NULL;
if (sib1->nonCriticalExtension) {
NR_SIB1_v1610_IEs_t *sib1_v1610 = sib1->nonCriticalExtension;
if (sib1_v1610 && sib1_v1610->nonCriticalExtension && sib1_v1610->nonCriticalExtension->nonCriticalExtension) {
NR_SIB1_v1700_IEs_t *sib1_v17 = sib1_v1610->nonCriticalExtension->nonCriticalExtension;
schedInfo17 = sib1_v17->si_SchedulingInfo_v1700;
}
}
int n_slots_frame = gNB_mac->frame_structure.numb_slots_frame;
int window_length_sl = 5 << schedInfo->si_WindowLength;
int window_length_f = (window_length_sl / n_slots_frame) + ((window_length_sl % n_slots_frame) > 0);
int num_ssb = cc->num_active_ssb;
// number of PDCCH monitoring occasions in SI-window
int period, offset;
get_monitoring_period_offset(ss, &period, &offset);
// The UE assumes that, in the SI window, PDCCH for an SI message is transmitted
// in at least one PDCCH monitoring occasion corresponding to each transmitted SSB
// frame and slot relative to start of window
// The [x×N+K]th PDCCH monitoring occasion(s) for SI message in SI-window corresponds to the Kth transmitted SSB
// in our implmentation we transmit only once per SSB per window so N = 1
// Section 5.2.2.3.2 of 38.331
int temp_slot = offset % n_slots_frame;
int temp_frame = offset / n_slots_frame;
int rel_slot[num_ssb];
int rel_frame[num_ssb];
int ssb = 0;
while (ssb < num_ssb) {
AssertFatal(temp_frame < window_length_f, "Couldn't fit %d SSB in window length of %d slots\n", num_ssb, window_length_sl);
if (is_dl_slot(temp_slot, &gNB_mac->frame_structure)) {
rel_slot[ssb] = temp_slot;
rel_frame[ssb] = temp_frame;
ssb++;
}
temp_frame += (temp_slot + period) / n_slots_frame;
temp_slot = (temp_slot + period) % n_slots_frame;
}
for (int ssb = 0; ssb < num_ssb; ssb++) {
for (int i = 0; i < schedInfo->schedulingInfoList.list.count; i++) {
NR_SchedulingInfo_t *schedulingInfo = schedInfo->schedulingInfoList.list.array[i];
if (test_other_sib_sched_occasion(i + 1,
window_length_sl,
schedulingInfo->si_Periodicity,
n_slots_frame,
frame,
slot,
rel_frame[ssb],
rel_slot[ssb]))
continue;
other_sib_sched_control(module_idP, frame, slot, ssb, ss, DL_req, TX_req, 0);
}
if (!schedInfo17)
continue;
for (int i = 0; i < schedInfo17->schedulingInfoList2_r17.list.count; i++) {
NR_SchedulingInfo2_r17_t *schedulingInfo2_r17 = schedInfo17->schedulingInfoList2_r17.list.array[i];
if (test_other_sib_sched_occasion(schedulingInfo2_r17->si_WindowPosition_r17,
window_length_sl,
schedulingInfo2_r17->si_Periodicity_r17,
n_slots_frame,
frame,
slot,
rel_frame[ssb],
rel_slot[ssb]))
continue;
other_sib_sched_control(module_idP, frame, slot, ssb, ss, DL_req, TX_req, 1);
}
}
}