Different threads (e.g., RRC, DU tasks) might access the scheduler at the same time as the thread that drivers the scheduler itself (ru_thread). To avoid data races, introduce this mutex. Most functions of the scheduler are only used internally and have been marked static in the previous commit. The remaining ones, in this commit, either lock the scheduler mutex sched_lock, or a comment has been added of the assumption of how such function is to be locked (e.g., if it might be called from different places, or if it is called by a function that already locks the scheduler).
1292 lines
59 KiB
C
1292 lines
59 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_uci.c
|
|
* \brief MAC procedures related to UCI
|
|
* \date 2020
|
|
* \version 1.0
|
|
* \company Eurecom
|
|
*/
|
|
|
|
#include <softmodem-common.h>
|
|
#include "NR_MAC_gNB/nr_mac_gNB.h"
|
|
#include "NR_MAC_COMMON/nr_mac_extern.h"
|
|
#include "NR_MAC_gNB/mac_proto.h"
|
|
#include "common/ran_context.h"
|
|
#include "common/utils/nr/nr_common.h"
|
|
#include "nfapi/oai_integration/vendor_ext.h"
|
|
|
|
extern RAN_CONTEXT_t RC;
|
|
|
|
|
|
|
|
static void nr_fill_nfapi_pucch(gNB_MAC_INST *nrmac,
|
|
frame_t frame,
|
|
sub_frame_t slot,
|
|
const NR_sched_pucch_t *pucch,
|
|
NR_UE_info_t* UE)
|
|
{
|
|
|
|
const int index = ul_buffer_index(pucch->frame, pucch->ul_slot, UE->current_UL_BWP.scs, nrmac->UL_tti_req_ahead_size);
|
|
nfapi_nr_ul_tti_request_t *future_ul_tti_req = &nrmac->UL_tti_req_ahead[0][index];
|
|
if (future_ul_tti_req->SFN != pucch->frame || future_ul_tti_req->Slot != pucch->ul_slot)
|
|
LOG_W(MAC,
|
|
"Current %d.%d : future UL_tti_req's frame.slot %4d.%2d does not match PUCCH %4d.%2d\n",
|
|
frame,slot,
|
|
future_ul_tti_req->SFN,
|
|
future_ul_tti_req->Slot,
|
|
pucch->frame,
|
|
pucch->ul_slot);
|
|
|
|
// n_pdus is number of pdus, so, in the array, it is the index of the next free element
|
|
if (future_ul_tti_req->n_pdus >= sizeofArray(future_ul_tti_req->pdus_list) ) {
|
|
LOG_E(NR_MAC,"future_ul_tti_req->n_pdus %d is full, slot: %d, sr flag %d dropping request\n",
|
|
future_ul_tti_req->n_pdus, pucch->ul_slot, pucch->sr_flag);
|
|
return;
|
|
}
|
|
future_ul_tti_req->pdus_list[future_ul_tti_req->n_pdus].pdu_type = NFAPI_NR_UL_CONFIG_PUCCH_PDU_TYPE;
|
|
future_ul_tti_req->pdus_list[future_ul_tti_req->n_pdus].pdu_size = sizeof(nfapi_nr_pucch_pdu_t);
|
|
nfapi_nr_pucch_pdu_t *pucch_pdu = &future_ul_tti_req->pdus_list[future_ul_tti_req->n_pdus].pucch_pdu;
|
|
memset(pucch_pdu, 0, sizeof(nfapi_nr_pucch_pdu_t));
|
|
future_ul_tti_req->n_pdus += 1;
|
|
|
|
LOG_D(NR_MAC,
|
|
"%s %4d.%2d Scheduling pucch reception in %4d.%2d: bits SR %d, DAI %d, CSI %d on res %d\n",
|
|
pucch->dai_c>0 ? "pucch_acknack" : "",
|
|
frame,
|
|
slot,
|
|
pucch->frame,
|
|
pucch->ul_slot,
|
|
pucch->sr_flag,
|
|
pucch->dai_c,
|
|
pucch->csi_bits,
|
|
pucch->resource_indicator);
|
|
NR_COMMON_channels_t * common_ch=nrmac->common_channels;
|
|
NR_ServingCellConfigCommon_t *scc = common_ch->ServingCellConfigCommon;
|
|
|
|
LOG_D(NR_MAC,"%4d.%2d Calling nr_configure_pucch (pucch_Config %p,r_pucch %d) pucch to be scheduled in %4d.%2d\n",
|
|
frame,slot,UE->current_UL_BWP.pucch_Config,pucch->r_pucch,pucch->frame,pucch->ul_slot);
|
|
|
|
nr_configure_pucch(pucch_pdu,
|
|
scc,
|
|
UE,
|
|
pucch->resource_indicator,
|
|
pucch->csi_bits,
|
|
pucch->dai_c,
|
|
pucch->sr_flag,
|
|
pucch->r_pucch);
|
|
}
|
|
|
|
#define MIN_RSRP_VALUE -141
|
|
#define MAX_NUM_SSB 128
|
|
#define MAX_SSB_SCHED 8
|
|
#define L1_RSRP_HYSTERIS 10 //considering 10 dBm as hysterisis for avoiding frequent SSB Beam Switching. !Fixme provide exact value if any
|
|
//#define L1_DIFF_RSRP_STEP_SIZE 2
|
|
|
|
int ssb_index_sorted[MAX_NUM_SSB] = {0};
|
|
int ssb_rsrp_sorted[MAX_NUM_SSB] = {0};
|
|
|
|
//Measured RSRP Values Table 10.1.16.1-1 from 36.133
|
|
//Stored all the upper limits[Max RSRP Value of corresponding index]
|
|
//stored -1 for invalid values
|
|
static const int L1_SSB_CSI_RSRP_measReport_mapping_38133_10_1_6_1_1[128] = {
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0 - 9
|
|
-1, -1, -1, -1, -1, -1, INT_MIN, -140, -139, -138, // 10 - 19
|
|
-137, -136, -135, -134, -133, -132, -131, -130, -129, -128, // 20 - 29
|
|
-127, -126, -125, -124, -123, -122, -121, -120, -119, -118, // 30 - 39
|
|
-117, -116, -115, -114, -113, -112, -111, -110, -109, -108, // 40 - 49
|
|
-107, -106, -105, -104, -103, -102, -101, -100, -99, -98, // 50 - 59
|
|
-97, -96, -95, -94, -93, -92, -91, -90, -89, -88, // 60 - 69
|
|
-87, -86, -85, -84, -83, -82, -81, -80, -79, -78, // 70 - 79
|
|
-77, -76, -75, -74, -73, -72, -71, -70, -69, -68, // 80 - 89
|
|
-67, -66, -65, -64, -63, -62, -61, -60, -59, -58, // 90 - 99
|
|
-57, -56, -55, -54, -53, -52, -51, -50, -49, -48, // 100 - 109
|
|
-47, -46, -45, -44, INT_MAX, -1, -1, -1, -1, -1, // 110 - 119
|
|
-1, -1, -1, -1, -1, -1, -1, -1 // 120 - 127
|
|
};
|
|
|
|
//Differential RSRP values Table 10.1.6.1-2 from 36.133
|
|
//Stored the upper limits[MAX RSRP Value]
|
|
static const int diff_rsrp_ssb_csi_meas_10_1_6_1_2[16] = {
|
|
0,
|
|
-2,
|
|
-4,
|
|
-6,
|
|
-8,
|
|
-10,
|
|
-12,
|
|
-14,
|
|
-16,
|
|
-18, // 0 - 9
|
|
-20,
|
|
-22,
|
|
-24,
|
|
-26,
|
|
-28,
|
|
-30 // 10 - 15
|
|
};
|
|
|
|
static int get_pucch_index(int frame, int slot, int n_slots_frame, const NR_TDD_UL_DL_Pattern_t *tdd, int sched_pucch_size)
|
|
{
|
|
// PUCCH structures are indexed by slot in the PUCCH period determined by sched_pucch_size number of UL slots
|
|
// this functions return the index to the structure for slot passed to the function
|
|
const int first_ul_slot_period = tdd ? get_first_ul_slot(tdd->nrofDownlinkSlots, tdd->nrofDownlinkSymbols, tdd->nrofUplinkSymbols) : 0;
|
|
const int n_ul_slots_period = tdd ? tdd->nrofUplinkSlots + (tdd->nrofUplinkSymbols > 0 ? 1 : 0) : n_slots_frame;
|
|
const int nr_slots_period = tdd ? n_slots_frame / get_nb_periods_per_frame(tdd->dl_UL_TransmissionPeriodicity) : n_slots_frame;
|
|
const int n_ul_slots_frame = n_slots_frame / nr_slots_period * n_ul_slots_period;
|
|
// (frame * n_ul_slots_frame) adds up the number of UL slots in the previous frames
|
|
const int frame_start = frame * n_ul_slots_frame;
|
|
// ((slot / nr_slots_period) * n_ul_slots_period) adds up the number of UL slots in the previous TDD periods of this frame
|
|
const int ul_period_start = (slot / nr_slots_period) * n_ul_slots_period;
|
|
// ((slot % nr_slots_period) - first_ul_slot_period) gives the progressive number of the slot in this TDD period
|
|
const int ul_period_slot = (slot % nr_slots_period) - first_ul_slot_period;
|
|
// the sum gives the index of current UL slot in the frame which is normalized wrt sched_pucch_size
|
|
return (frame_start + ul_period_start + ul_period_slot) % sched_pucch_size;
|
|
|
|
}
|
|
|
|
void nr_schedule_pucch(gNB_MAC_INST *nrmac,
|
|
frame_t frameP,
|
|
sub_frame_t slotP)
|
|
{
|
|
/* already mutex protected: held in gNB_dlsch_ulsch_scheduler() */
|
|
NR_SCHED_ENSURE_LOCKED(&nrmac->sched_lock);
|
|
|
|
if (!is_xlsch_in_slot(nrmac->ulsch_slot_bitmap[slotP / 64], slotP))
|
|
return;
|
|
|
|
UE_iterator(nrmac->UE_info.list, UE) {
|
|
NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
|
|
NR_UE_UL_BWP_t *ul_bwp = &UE->current_UL_BWP;
|
|
const int n_slots_frame = nr_slots_per_frame[ul_bwp->scs];
|
|
const NR_ServingCellConfigCommon_t *scc = nrmac->common_channels[0].ServingCellConfigCommon;
|
|
const NR_TDD_UL_DL_Pattern_t *tdd = scc->tdd_UL_DL_ConfigurationCommon ? &scc->tdd_UL_DL_ConfigurationCommon->pattern1 : NULL;
|
|
AssertFatal(tdd || nrmac->common_channels[0].frame_type == FDD, "Dynamic TDD not handled yet\n");
|
|
const int pucch_index = get_pucch_index(frameP, slotP, n_slots_frame, tdd, sched_ctrl->sched_pucch_size);
|
|
NR_sched_pucch_t *curr_pucch = &UE->UE_sched_ctrl.sched_pucch[pucch_index];
|
|
if (!curr_pucch->active)
|
|
continue;
|
|
DevAssert(frameP == curr_pucch->frame && slotP == curr_pucch->ul_slot);
|
|
|
|
const uint16_t O_ack = curr_pucch->dai_c;
|
|
const uint16_t O_csi = curr_pucch->csi_bits;
|
|
const uint8_t O_sr = curr_pucch->sr_flag;
|
|
LOG_D(NR_MAC,"Scheduling PUCCH[%d] RX for UE %04x in %4d.%2d O_ack %d, O_sr %d, O_csi %d\n",
|
|
pucch_index,UE->rnti,curr_pucch->frame,curr_pucch->ul_slot,O_ack,O_sr,O_csi);
|
|
nr_fill_nfapi_pucch(nrmac, frameP, slotP, curr_pucch, UE);
|
|
memset(curr_pucch, 0, sizeof(*curr_pucch));
|
|
|
|
}
|
|
}
|
|
|
|
void nr_csi_meas_reporting(int Mod_idP,
|
|
frame_t frame,
|
|
sub_frame_t slot)
|
|
{
|
|
/* already mutex protected: held in gNB_dlsch_ulsch_scheduler() */
|
|
gNB_MAC_INST *nrmac = RC.nrmac[Mod_idP];
|
|
NR_SCHED_ENSURE_LOCKED(&nrmac->sched_lock);
|
|
|
|
UE_iterator(nrmac->UE_info.list, UE ) {
|
|
NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
|
|
NR_UE_UL_BWP_t *ul_bwp = &UE->current_UL_BWP;
|
|
const int n_slots_frame = nr_slots_per_frame[ul_bwp->scs];
|
|
if ((sched_ctrl->rrc_processing_timer > 0) || (sched_ctrl->ul_failure==1 && get_softmodem_params()->phy_test==0)) {
|
|
continue;
|
|
}
|
|
const NR_CSI_MeasConfig_t *csi_measconfig = ul_bwp->csi_MeasConfig;
|
|
if (!csi_measconfig) continue;
|
|
AssertFatal(csi_measconfig->csi_ReportConfigToAddModList->list.count > 0,
|
|
"NO CSI report configuration available");
|
|
NR_PUCCH_Config_t *pucch_Config = ul_bwp->pucch_Config;
|
|
|
|
for (int csi_report_id = 0; csi_report_id < csi_measconfig->csi_ReportConfigToAddModList->list.count; csi_report_id++){
|
|
NR_CSI_ReportConfig_t *csirep = csi_measconfig->csi_ReportConfigToAddModList->list.array[csi_report_id];
|
|
|
|
AssertFatal(csirep->reportConfigType.choice.periodic,
|
|
"Only periodic CSI reporting is implemented currently\n");
|
|
|
|
const NR_PUCCH_CSI_Resource_t *pucchcsires = csirep->reportConfigType.choice.periodic->pucch_CSI_ResourceList.list.array[0];
|
|
if(pucchcsires->uplinkBandwidthPartId != ul_bwp->bwp_id)
|
|
continue;
|
|
|
|
// we schedule CSI reporting max_fb_time slots in advance
|
|
int period, offset;
|
|
csi_period_offset(csirep, NULL, &period, &offset);
|
|
const int sched_slot = (slot + ul_bwp->max_fb_time) % n_slots_frame;
|
|
const int sched_frame = (frame + ((slot + ul_bwp->max_fb_time) / n_slots_frame)) % 1024;
|
|
// prepare to schedule csi measurement reception according to 5.2.1.4 in 38.214
|
|
if ((sched_frame*n_slots_frame + sched_slot - offset)%period != 0)
|
|
continue;
|
|
|
|
AssertFatal(is_xlsch_in_slot(nrmac->ulsch_slot_bitmap[sched_slot / 64], sched_slot), "CSI reporting slot %d is not set for an uplink slot\n", sched_slot);
|
|
LOG_D(NR_MAC, "CSI reporting in frame %d slot %d CSI report ID %ld\n", sched_frame, sched_slot, csirep->reportConfigId);
|
|
|
|
const NR_PUCCH_ResourceSet_t *pucchresset = pucch_Config->resourceSetToAddModList->list.array[1]; // set with formats >1
|
|
const int n = pucchresset->resourceList.list.count;
|
|
int res_index = 0;
|
|
for (; res_index < n; res_index++)
|
|
if (*pucchresset->resourceList.list.array[res_index] == pucchcsires->pucch_Resource)
|
|
break;
|
|
AssertFatal(res_index < n,
|
|
"CSI pucch resource %ld not found among PUCCH resources\n", pucchcsires->pucch_Resource);
|
|
|
|
const NR_ServingCellConfigCommon_t *scc = nrmac->common_channels[0].ServingCellConfigCommon;
|
|
const NR_TDD_UL_DL_Pattern_t *tdd = scc->tdd_UL_DL_ConfigurationCommon ? &scc->tdd_UL_DL_ConfigurationCommon->pattern1 : NULL;
|
|
AssertFatal(tdd || nrmac->common_channels[0].frame_type == FDD, "Dynamic TDD not handled yet\n");
|
|
const int pucch_index = get_pucch_index(sched_frame, sched_slot, n_slots_frame, tdd, sched_ctrl->sched_pucch_size);
|
|
NR_sched_pucch_t *curr_pucch = &sched_ctrl->sched_pucch[pucch_index];
|
|
AssertFatal(curr_pucch->active == false, "CSI structure is scheduled in advance. It should be free!\n");
|
|
curr_pucch->r_pucch = -1;
|
|
curr_pucch->frame = sched_frame;
|
|
curr_pucch->ul_slot = sched_slot;
|
|
curr_pucch->resource_indicator = res_index;
|
|
curr_pucch->csi_bits += nr_get_csi_bitlen(UE->csi_report_template, csi_report_id);
|
|
curr_pucch->active = true;
|
|
|
|
int bwp_start = ul_bwp->BWPStart;
|
|
|
|
// going through the list of PUCCH resources to find the one indexed by resource_id
|
|
const int index = ul_buffer_index(sched_frame, sched_slot, ul_bwp->scs, nrmac->vrb_map_UL_size);
|
|
uint16_t *vrb_map_UL = &nrmac->common_channels[0].vrb_map_UL[index * MAX_BWP_SIZE];
|
|
const int m = pucch_Config->resourceToAddModList->list.count;
|
|
for (int j = 0; j < m; j++) {
|
|
NR_PUCCH_Resource_t *pucchres = pucch_Config->resourceToAddModList->list.array[j];
|
|
if (pucchres->pucch_ResourceId != *pucchresset->resourceList.list.array[res_index])
|
|
continue;
|
|
int start = pucchres->startingPRB;
|
|
int len = 1;
|
|
uint64_t mask = 0;
|
|
switch(pucchres->format.present){
|
|
case NR_PUCCH_Resource__format_PR_format2:
|
|
len = pucchres->format.choice.format2->nrofPRBs;
|
|
mask = SL_to_bitmap(pucchres->format.choice.format2->startingSymbolIndex, pucchres->format.choice.format2->nrofSymbols);
|
|
curr_pucch->simultaneous_harqcsi = pucch_Config->format2->choice.setup->simultaneousHARQ_ACK_CSI;
|
|
LOG_D(NR_MAC,"%d.%d Allocating PUCCH format 2, startPRB %d, nPRB %d, simulHARQ %d, num_bits %d\n", sched_frame, sched_slot,start,len,curr_pucch->simultaneous_harqcsi,curr_pucch->csi_bits);
|
|
break;
|
|
case NR_PUCCH_Resource__format_PR_format3:
|
|
len = pucchres->format.choice.format3->nrofPRBs;
|
|
mask = SL_to_bitmap(pucchres->format.choice.format3->startingSymbolIndex, pucchres->format.choice.format3->nrofSymbols);
|
|
curr_pucch->simultaneous_harqcsi = pucch_Config->format3->choice.setup->simultaneousHARQ_ACK_CSI;
|
|
break;
|
|
case NR_PUCCH_Resource__format_PR_format4:
|
|
mask = SL_to_bitmap(pucchres->format.choice.format4->startingSymbolIndex, pucchres->format.choice.format4->nrofSymbols);
|
|
curr_pucch->simultaneous_harqcsi = pucch_Config->format4->choice.setup->simultaneousHARQ_ACK_CSI;
|
|
break;
|
|
default:
|
|
AssertFatal(0, "Invalid PUCCH format type\n");
|
|
}
|
|
// verify resources are free
|
|
for (int i = start; i < start + len; ++i) {
|
|
if((vrb_map_UL[i+bwp_start] & mask) != 0) {
|
|
LOG_E(NR_MAC, "%4d.%2d VRB MAP in %4d.%2d not free. Can't schedule CSI reporting on PUCCH.\n", frame, slot, sched_frame, sched_slot);
|
|
memset(curr_pucch, 0, sizeof(*curr_pucch));
|
|
}
|
|
else
|
|
vrb_map_UL[i+bwp_start] |= mask;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void handle_dl_harq(NR_UE_info_t * UE,
|
|
int8_t harq_pid,
|
|
bool success,
|
|
int harq_round_max)
|
|
{
|
|
NR_UE_harq_t *harq = &UE->UE_sched_ctrl.harq_processes[harq_pid];
|
|
harq->feedback_slot = -1;
|
|
harq->is_waiting = false;
|
|
if (success) {
|
|
add_tail_nr_list(&UE->UE_sched_ctrl.available_dl_harq, harq_pid);
|
|
harq->round = 0;
|
|
harq->ndi ^= 1;
|
|
} else if (harq->round >= harq_round_max - 1) {
|
|
abort_nr_dl_harq(UE, harq_pid);
|
|
LOG_D(NR_MAC, "retransmission error for UE %04x (total %"PRIu64")\n", UE->rnti, UE->mac_stats.dl.errors);
|
|
} else {
|
|
LOG_D(PHY,"NACK for: pid %d, ue %04x\n",harq_pid, UE->rnti);
|
|
add_tail_nr_list(&UE->UE_sched_ctrl.retrans_dl_harq, harq_pid);
|
|
harq->round++;
|
|
}
|
|
}
|
|
|
|
static int checkTargetSSBInFirst64TCIStates_pdschConfig(int ssb_index_t, NR_UE_info_t *UE)
|
|
{
|
|
const NR_PDSCH_Config_t *pdsch_Config = UE->current_DL_BWP.pdsch_Config;
|
|
int nb_tci_states = pdsch_Config ? pdsch_Config->tci_StatesToAddModList->list.count : 0;
|
|
NR_TCI_State_t *tci =NULL;
|
|
|
|
for(int i=0; i<nb_tci_states && i<64; i++) {
|
|
tci = (NR_TCI_State_t *)pdsch_Config->tci_StatesToAddModList->list.array[i];
|
|
|
|
if(tci != NULL) {
|
|
if(tci->qcl_Type1.referenceSignal.present == NR_QCL_Info__referenceSignal_PR_ssb) {
|
|
if(tci->qcl_Type1.referenceSignal.choice.ssb == ssb_index_t)
|
|
return tci->tci_StateId; // returned TCI state ID
|
|
}
|
|
// if type2 is configured
|
|
else if(tci->qcl_Type2 != NULL && tci->qcl_Type2->referenceSignal.present == NR_QCL_Info__referenceSignal_PR_ssb) {
|
|
if(tci->qcl_Type2->referenceSignal.choice.ssb == ssb_index_t)
|
|
return tci->tci_StateId; // returned TCI state ID
|
|
} else LOG_I(NR_MAC,"SSB index is not found in first 64 TCI states of TCI_statestoAddModList[%d]", i);
|
|
}
|
|
}
|
|
|
|
// tci state not identified in first 64 TCI States of PDSCH Config
|
|
return -1;
|
|
}
|
|
|
|
static int checkTargetSSBInTCIStates_pdcchConfig(int ssb_index_t, NR_UE_info_t *UE)
|
|
{
|
|
NR_TCI_State_t *tci =NULL;
|
|
NR_TCI_StateId_t *tci_id = NULL;
|
|
NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
|
|
NR_ControlResourceSet_t *coreset = sched_ctrl->coreset;
|
|
int flag = 0;
|
|
int tci_stateID = -1;
|
|
const NR_PDSCH_Config_t *pdsch_Config = UE->current_DL_BWP.pdsch_Config;
|
|
int nb_tci_states = pdsch_Config ? pdsch_Config->tci_StatesToAddModList->list.count : 0;
|
|
for(int i=0; i<nb_tci_states && i<128; i++) {
|
|
tci = (NR_TCI_State_t *)pdsch_Config->tci_StatesToAddModList->list.array[i];
|
|
|
|
if(tci != NULL && tci->qcl_Type1.referenceSignal.present == NR_QCL_Info__referenceSignal_PR_ssb) {
|
|
if(tci->qcl_Type1.referenceSignal.choice.ssb == ssb_index_t) {
|
|
flag = 1;
|
|
tci_stateID = tci->tci_StateId;
|
|
break;
|
|
} else if(tci->qcl_Type2 != NULL && tci->qcl_Type2->referenceSignal.present == NR_QCL_Info__referenceSignal_PR_ssb) {
|
|
flag = 1;
|
|
tci_stateID = tci->tci_StateId;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(flag != 0 && tci_stateID != -1 && coreset != NULL) {
|
|
for(i=0; i<64 && i<coreset->tci_StatesPDCCH_ToAddList->list.count; i++) {
|
|
tci_id = coreset->tci_StatesPDCCH_ToAddList->list.array[i];
|
|
|
|
if(tci_id != NULL && *tci_id == tci_stateID)
|
|
return tci_stateID;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Need to implement once configuration is received
|
|
return -1;
|
|
}
|
|
|
|
//returns the measured RSRP value (upper limit)
|
|
static int get_measured_rsrp(uint8_t index)
|
|
{
|
|
//if index is invalid returning minimum rsrp -140
|
|
if(index <= 15 || index >= 114)
|
|
return MIN_RSRP_VALUE;
|
|
|
|
return L1_SSB_CSI_RSRP_measReport_mapping_38133_10_1_6_1_1[index];
|
|
}
|
|
|
|
//returns the differential RSRP value (upper limit)
|
|
static int get_diff_rsrp(uint8_t index, int strongest_rsrp) {
|
|
if(strongest_rsrp != -1) {
|
|
return strongest_rsrp + diff_rsrp_ssb_csi_meas_10_1_6_1_2[index];
|
|
} else
|
|
return MIN_RSRP_VALUE;
|
|
}
|
|
|
|
//identifies the target SSB Beam index
|
|
//keeps the required date for PDCCH and PDSCH TCI state activation/deactivation CE consutruction globally
|
|
//handles triggering of PDCCH and PDSCH MAC CEs
|
|
static void tci_handling(NR_UE_info_t *UE, frame_t frame, slot_t slot)
|
|
{
|
|
int strongest_ssb_rsrp = 0;
|
|
int cqi_idx = 0;
|
|
int curr_ssb_beam_index = 0; //ToDo: yet to know how to identify the serving ssb beam index
|
|
uint8_t target_ssb_beam_index = curr_ssb_beam_index;
|
|
uint8_t is_triggering_ssb_beam_switch =0;
|
|
uint8_t ssb_idx = 0;
|
|
int pdsch_bwp_id = 0;
|
|
int ssb_index[MAX_NUM_SSB] = {0};
|
|
int ssb_rsrp[MAX_NUM_SSB] = {0};
|
|
uint8_t idx = 0;
|
|
NR_UE_DL_BWP_t *dl_bwp = &UE->current_DL_BWP;
|
|
NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
|
|
|
|
uint8_t nr_ssbri_cri = 0;
|
|
uint8_t nb_of_csi_ssb_report = UE->csi_report_template[cqi_idx].nb_of_csi_ssb_report;
|
|
int better_rsrp_reported = -140-(-0); /*minimum_measured_RSRP_value - minimum_differntail_RSRP_value*///considering the minimum RSRP value as better RSRP initially
|
|
uint8_t diff_rsrp_idx = 0;
|
|
uint8_t i, j;
|
|
|
|
//bwp indicator
|
|
int n_dl_bwp = dl_bwp->n_dl_bwp;
|
|
const int bwp_id = dl_bwp->bwp_id;
|
|
if (n_dl_bwp < 4)
|
|
pdsch_bwp_id = bwp_id;
|
|
else
|
|
pdsch_bwp_id = bwp_id - 1; // as per table 7.3.1.1.2-1 in 38.212
|
|
|
|
/*Example:
|
|
CRI_SSBRI: 1 2 3 4| 5 6 7 8| 9 10 1 2|
|
|
nb_of_csi_ssb_report = 3 //3 sets as above
|
|
nr_ssbri_cri = 4 //each set has 4 elements
|
|
storing ssb indexes in ssb_index array as ssb_index[0] = 1 .. ssb_index[4] = 5
|
|
ssb_rsrp[0] = strongest rsrp in first set, ssb_rsrp[4] = strongest rsrp in second set, ..
|
|
idx: resource set index
|
|
*/
|
|
|
|
nr_ssbri_cri = sched_ctrl->CSI_report.ssb_cri_report.nr_ssbri_cri;
|
|
//extracting the ssb indexes
|
|
for (ssb_idx = 0; ssb_idx < nr_ssbri_cri; ssb_idx++) {
|
|
ssb_index[idx * nb_of_csi_ssb_report + ssb_idx] = sched_ctrl->CSI_report.ssb_cri_report.CRI_SSBRI[ssb_idx];
|
|
}
|
|
|
|
//if strongest measured RSRP is configured
|
|
strongest_ssb_rsrp = get_measured_rsrp(sched_ctrl->CSI_report.ssb_cri_report.RSRP);
|
|
ssb_rsrp[idx * nb_of_csi_ssb_report] = strongest_ssb_rsrp;
|
|
LOG_D(NR_MAC,"ssb_rsrp = %d\n",strongest_ssb_rsrp);
|
|
|
|
//if current ssb rsrp is greater than better rsrp
|
|
if(ssb_rsrp[idx * nb_of_csi_ssb_report] > better_rsrp_reported) {
|
|
better_rsrp_reported = ssb_rsrp[idx * nb_of_csi_ssb_report];
|
|
target_ssb_beam_index = idx * nb_of_csi_ssb_report;
|
|
}
|
|
|
|
for(diff_rsrp_idx =1; diff_rsrp_idx < nr_ssbri_cri; diff_rsrp_idx++) {
|
|
ssb_rsrp[idx * nb_of_csi_ssb_report + diff_rsrp_idx] = get_diff_rsrp(sched_ctrl->CSI_report.ssb_cri_report.diff_RSRP[diff_rsrp_idx-1], strongest_ssb_rsrp);
|
|
|
|
//if current reported rsrp is greater than better rsrp
|
|
if(ssb_rsrp[idx * nb_of_csi_ssb_report + diff_rsrp_idx] > better_rsrp_reported) {
|
|
better_rsrp_reported = ssb_rsrp[idx * nb_of_csi_ssb_report + diff_rsrp_idx];
|
|
target_ssb_beam_index = idx * nb_of_csi_ssb_report + diff_rsrp_idx;
|
|
}
|
|
}
|
|
|
|
if(ssb_index[target_ssb_beam_index] != ssb_index[curr_ssb_beam_index] && ssb_rsrp[target_ssb_beam_index] > ssb_rsrp[curr_ssb_beam_index]) {
|
|
if( ssb_rsrp[target_ssb_beam_index] - ssb_rsrp[curr_ssb_beam_index] > L1_RSRP_HYSTERIS) {
|
|
is_triggering_ssb_beam_switch = 1;
|
|
LOG_D(NR_MAC, "Triggering ssb beam switching using tci\n");
|
|
}
|
|
}
|
|
|
|
if(is_triggering_ssb_beam_switch) {
|
|
//filling pdcch tci state activativation mac ce structure fields
|
|
sched_ctrl->UE_mac_ce_ctrl.pdcch_state_ind.is_scheduled = 1;
|
|
//OAI currently focusing on Non CA usecase hence 0 is considered as serving
|
|
//cell id
|
|
sched_ctrl->UE_mac_ce_ctrl.pdcch_state_ind.servingCellId = 0; //0 for PCell as 38.331 v15.9.0 page 353 //serving cell id for which this MAC CE applies
|
|
sched_ctrl->UE_mac_ce_ctrl.pdcch_state_ind.coresetId = 0; //coreset id for which the TCI State id is being indicated
|
|
|
|
/* 38.321 v15.8.0 page 66
|
|
TCI State ID: This field indicates the TCI state identified by TCI-StateId as specified in TS 38.331 [5] applicable
|
|
to the Control Resource Set identified by CORESET ID field.
|
|
If the field of CORESET ID is set to 0,
|
|
this field indicates a TCI-StateId for a TCI state of the first 64 TCI-states configured by tci-States-ToAddModList and tciStates-ToReleaseList in the PDSCH-Config in the active BWP.
|
|
If the field of CORESET ID is set to the other value than 0,
|
|
this field indicates a TCI-StateId configured by tci-StatesPDCCH-ToAddList and tciStatesPDCCH-ToReleaseList in the controlResourceSet identified by the indicated CORESET ID.
|
|
The length of the field is 7 bits
|
|
*/
|
|
if(sched_ctrl->UE_mac_ce_ctrl.pdcch_state_ind.coresetId == 0) {
|
|
int tci_state_id = checkTargetSSBInFirst64TCIStates_pdschConfig(ssb_index[target_ssb_beam_index], UE);
|
|
|
|
if( tci_state_id != -1)
|
|
sched_ctrl->UE_mac_ce_ctrl.pdcch_state_ind.tciStateId = tci_state_id;
|
|
else {
|
|
//identify the best beam within first 64 TCI States of PDSCH
|
|
//Config TCI-states-to-addModList
|
|
int flag = 0;
|
|
|
|
for(i =0; ssb_index_sorted[i]!=0; i++) {
|
|
tci_state_id = checkTargetSSBInFirst64TCIStates_pdschConfig(ssb_index_sorted[i],UE) ;
|
|
|
|
if(tci_state_id != -1 && ssb_rsrp_sorted[i] > ssb_rsrp[curr_ssb_beam_index] && ssb_rsrp_sorted[i] - ssb_rsrp[curr_ssb_beam_index] > L1_RSRP_HYSTERIS) {
|
|
sched_ctrl->UE_mac_ce_ctrl.pdcch_state_ind.tciStateId = tci_state_id;
|
|
flag = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(flag == 0 || ssb_rsrp_sorted[i] < ssb_rsrp[curr_ssb_beam_index] || ssb_rsrp_sorted[i] - ssb_rsrp[curr_ssb_beam_index] < L1_RSRP_HYSTERIS) {
|
|
sched_ctrl->UE_mac_ce_ctrl.pdcch_state_ind.is_scheduled = 0;
|
|
}
|
|
}
|
|
} else {
|
|
int tci_state_id = checkTargetSSBInTCIStates_pdcchConfig(ssb_index[target_ssb_beam_index], UE);
|
|
|
|
if (tci_state_id !=-1)
|
|
sched_ctrl->UE_mac_ce_ctrl.pdcch_state_ind.tciStateId = tci_state_id;
|
|
else {
|
|
//identify the best beam within CORESET/PDCCH
|
|
////Config TCI-states-to-addModList
|
|
int flag = 0;
|
|
|
|
for(i =0; ssb_index_sorted[i]!=0; i++) {
|
|
tci_state_id = checkTargetSSBInTCIStates_pdcchConfig(ssb_index_sorted[i], UE);
|
|
|
|
if( tci_state_id != -1 && ssb_rsrp_sorted[i] > ssb_rsrp[curr_ssb_beam_index] && ssb_rsrp_sorted[i] - ssb_rsrp[curr_ssb_beam_index] > L1_RSRP_HYSTERIS) {
|
|
sched_ctrl->UE_mac_ce_ctrl.pdcch_state_ind.tciStateId = tci_state_id;
|
|
flag = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(flag == 0 || ssb_rsrp_sorted[i] < ssb_rsrp[curr_ssb_beam_index] || ssb_rsrp_sorted[i] - ssb_rsrp[curr_ssb_beam_index] < L1_RSRP_HYSTERIS) {
|
|
sched_ctrl->UE_mac_ce_ctrl.pdcch_state_ind.is_scheduled = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
sched_ctrl->UE_mac_ce_ctrl.pdcch_state_ind.tci_present_inDCI = sched_ctrl->coreset ?
|
|
sched_ctrl->coreset->tci_PresentInDCI : NULL;
|
|
|
|
//filling pdsch tci state activation deactivation mac ce structure fields
|
|
if(sched_ctrl->UE_mac_ce_ctrl.pdcch_state_ind.tci_present_inDCI) {
|
|
sched_ctrl->UE_mac_ce_ctrl.pdsch_TCI_States_ActDeact.is_scheduled = 1;
|
|
/*
|
|
Serving Cell ID: This field indicates the identity of the Serving Cell for which the MAC CE applies
|
|
Considering only PCell exists. Serving cell index of PCell is always 0, hence configuring 0
|
|
*/
|
|
sched_ctrl->UE_mac_ce_ctrl.pdsch_TCI_States_ActDeact.servingCellId = 0;
|
|
/*
|
|
BWP ID: This field indicates a DL BWP for which the MAC CE applies as the codepoint of the DCI bandwidth
|
|
part indicator field as specified in TS 38.212
|
|
*/
|
|
sched_ctrl->UE_mac_ce_ctrl.pdsch_TCI_States_ActDeact.bwpId = pdsch_bwp_id;
|
|
|
|
/*
|
|
* TODO ssb_rsrp_sort() API yet to code to find 8 best beams, rrc configuration
|
|
* is required
|
|
*/
|
|
for(i = 0; i<8; i++) {
|
|
sched_ctrl->UE_mac_ce_ctrl.pdsch_TCI_States_ActDeact.tciStateActDeact[i] = i;
|
|
}
|
|
|
|
sched_ctrl->UE_mac_ce_ctrl.pdsch_TCI_States_ActDeact.highestTciStateActivated = 8;
|
|
|
|
for(i = 0, j =0; i<MAX_TCI_STATES; i++) {
|
|
if(sched_ctrl->UE_mac_ce_ctrl.pdsch_TCI_States_ActDeact.tciStateActDeact[i]) {
|
|
sched_ctrl->UE_mac_ce_ctrl.pdsch_TCI_States_ActDeact.codepoint[j] = i;
|
|
j++;
|
|
}
|
|
}
|
|
}//tci_presentInDCI
|
|
}//is-triggering_beam_switch
|
|
} // tci handling
|
|
|
|
static uint8_t pickandreverse_bits(uint8_t *payload, uint16_t bitlen, uint8_t start_bit)
|
|
{
|
|
uint8_t rev_bits = 0;
|
|
for (int i=0; i<bitlen; i++)
|
|
rev_bits |= ((payload[(start_bit+i)/8]>>((start_bit+i)%8))&0x01)<<(bitlen-i-1);
|
|
return rev_bits;
|
|
}
|
|
|
|
static void evaluate_rsrp_report(NR_UE_info_t *UE,
|
|
NR_UE_sched_ctrl_t *sched_ctrl,
|
|
uint8_t csi_report_id,
|
|
uint8_t *payload,
|
|
int *cumul_bits,
|
|
NR_CSI_ReportConfig__reportQuantity_PR reportQuantity_type)
|
|
{
|
|
nr_csi_report_t *csi_report = &UE->csi_report_template[csi_report_id];
|
|
uint8_t cri_ssbri_bitlen = csi_report->CSI_report_bitlen.cri_ssbri_bitlen;
|
|
uint16_t curr_payload;
|
|
|
|
/*! As per the spec 38.212 and table: 6.3.1.1.2-12 in a single UCI sequence we can have multiple CSI_report
|
|
* the number of CSI_report will depend on number of CSI resource sets that are configured in CSI-ResourceConfig RRC IE
|
|
* From spec 38.331 from the IE CSI-ResourceConfig for SSB RSRP reporting we can configure only one resource set
|
|
* From spec 38.214 section 5.2.1.2 For periodic and semi-persistent CSI Resource Settings, the number of CSI-RS Resource Sets configured is limited to S=1
|
|
*/
|
|
|
|
/** from 38.214 sec 5.2.1.4.2
|
|
- if the UE is configured with the higher layer parameter groupBasedBeamReporting set to 'disabled', the UE is
|
|
not required to update measurements for more than 64 CSI-RS and/or SSB resources, and the UE shall report in
|
|
a single report nrofReportedRS (higher layer configured) different CRI or SSBRI for each report setting
|
|
|
|
- if the UE is configured with the higher layer parameter groupBasedBeamReporting set to 'enabled', the UE is not
|
|
required to update measurements for more than 64 CSI-RS and/or SSB resources, and the UE shall report in a
|
|
single reporting instance two different CRI or SSBRI for each report setting, where CSI-RS and/or SSB
|
|
resources can be received simultaneously by the UE either with a single spatial domain receive filter, or with
|
|
multiple simultaneous spatial domain receive filter
|
|
*/
|
|
|
|
sched_ctrl->CSI_report.ssb_cri_report.nr_ssbri_cri = csi_report->CSI_report_bitlen.nb_ssbri_cri;
|
|
|
|
for (int csi_ssb_idx = 0; csi_ssb_idx < sched_ctrl->CSI_report.ssb_cri_report.nr_ssbri_cri ; csi_ssb_idx++) {
|
|
curr_payload = pickandreverse_bits(payload, cri_ssbri_bitlen, *cumul_bits);
|
|
|
|
if (NR_CSI_ReportConfig__reportQuantity_PR_ssb_Index_RSRP == reportQuantity_type) {
|
|
sched_ctrl->CSI_report.ssb_cri_report.CRI_SSBRI[csi_ssb_idx] =
|
|
*(csi_report->SSB_Index_list[cri_ssbri_bitlen>0?((curr_payload)&~(~1<<(cri_ssbri_bitlen-1))):cri_ssbri_bitlen]);
|
|
LOG_D(MAC,"SSB_index = %d\n",sched_ctrl->CSI_report.ssb_cri_report.CRI_SSBRI[csi_ssb_idx]);
|
|
}
|
|
else {
|
|
sched_ctrl->CSI_report.ssb_cri_report.CRI_SSBRI[csi_ssb_idx] =
|
|
*(csi_report->CSI_Index_list[cri_ssbri_bitlen>0?((curr_payload)&~(~1<<(cri_ssbri_bitlen-1))):cri_ssbri_bitlen]);
|
|
LOG_D(MAC,"CSI-RS Resource Indicator = %d\n",sched_ctrl->CSI_report.ssb_cri_report.CRI_SSBRI[csi_ssb_idx]);
|
|
}
|
|
*cumul_bits += cri_ssbri_bitlen;
|
|
|
|
}
|
|
|
|
curr_payload = pickandreverse_bits(payload, 7, *cumul_bits);
|
|
sched_ctrl->CSI_report.ssb_cri_report.RSRP = curr_payload & 0x7f;
|
|
*cumul_bits += 7;
|
|
|
|
for (int diff_rsrp_idx =0; diff_rsrp_idx < sched_ctrl->CSI_report.ssb_cri_report.nr_ssbri_cri - 1; diff_rsrp_idx++ ) {
|
|
curr_payload = pickandreverse_bits(payload, 4, *cumul_bits);
|
|
sched_ctrl->CSI_report.ssb_cri_report.diff_RSRP[diff_rsrp_idx] = curr_payload & 0x0f;
|
|
*cumul_bits += 4;
|
|
}
|
|
csi_report->nb_of_csi_ssb_report++;
|
|
int strongest_ssb_rsrp = get_measured_rsrp(sched_ctrl->CSI_report.ssb_cri_report.RSRP);
|
|
NR_mac_stats_t *stats = &UE->mac_stats;
|
|
// including ssb rsrp in mac stats
|
|
stats->cumul_rsrp += strongest_ssb_rsrp;
|
|
stats->num_rsrp_meas++;
|
|
}
|
|
|
|
static void evaluate_cri_report(uint8_t *payload, uint8_t cri_bitlen, int cumul_bits, NR_UE_sched_ctrl_t *sched_ctrl)
|
|
{
|
|
uint8_t temp_cri = pickandreverse_bits(payload, cri_bitlen, cumul_bits);
|
|
sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.cri = temp_cri;
|
|
}
|
|
|
|
static int evaluate_ri_report(uint8_t *payload,
|
|
uint8_t ri_bitlen,
|
|
uint8_t ri_restriction,
|
|
int cumul_bits,
|
|
NR_UE_sched_ctrl_t *sched_ctrl)
|
|
{
|
|
uint8_t ri_index = pickandreverse_bits(payload, ri_bitlen, cumul_bits);
|
|
int count=0;
|
|
for (int i=0; i<8; i++) {
|
|
if ((ri_restriction>>i)&0x01) {
|
|
if(count == ri_index) {
|
|
sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.ri = i;
|
|
LOG_D(MAC,"CSI Reported Rank %d\n", i+1);
|
|
return i;
|
|
}
|
|
count++;
|
|
}
|
|
}
|
|
AssertFatal(1==0, "Decoded ri %d does not correspond to any valid value in ri_restriction %d\n",ri_index,ri_restriction);
|
|
}
|
|
|
|
static void evaluate_cqi_report(uint8_t *payload,
|
|
nr_csi_report_t *csi_report,
|
|
int cumul_bits,
|
|
uint8_t ri,
|
|
NR_UE_info_t *UE,
|
|
uint8_t cqi_Table)
|
|
{
|
|
|
|
NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
|
|
|
|
//TODO sub-band CQI report not yet implemented
|
|
int cqi_bitlen = csi_report->csi_meas_bitlen.cqi_bitlen[ri];
|
|
|
|
uint8_t temp_cqi = pickandreverse_bits(payload, 4, cumul_bits);
|
|
|
|
// NR_CSI_ReportConfig__cqi_Table_table1 = 0
|
|
// NR_CSI_ReportConfig__cqi_Table_table2 = 1
|
|
// NR_CSI_ReportConfig__cqi_Table_table3 = 2
|
|
sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.cqi_table = cqi_Table;
|
|
sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.wb_cqi_1tb = temp_cqi;
|
|
LOG_D(MAC,"Wide-band CQI for the first TB %d\n", temp_cqi);
|
|
if (cqi_bitlen > 4) {
|
|
temp_cqi = pickandreverse_bits(payload, 4, cumul_bits);
|
|
sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.wb_cqi_2tb = temp_cqi;
|
|
LOG_D(MAC,"Wide-band CQI for the second TB %d\n", temp_cqi);
|
|
}
|
|
|
|
// TODO for wideband case and multiple TB
|
|
const int cqi_idx = sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.wb_cqi_1tb;
|
|
const int mcs_table = UE->current_DL_BWP.mcsTableIdx;
|
|
sched_ctrl->dl_max_mcs = get_mcs_from_cqi(mcs_table, cqi_Table, cqi_idx);
|
|
}
|
|
|
|
static uint8_t evaluate_pmi_report(uint8_t *payload,
|
|
nr_csi_report_t *csi_report,
|
|
int cumul_bits,
|
|
uint8_t ri,
|
|
NR_UE_sched_ctrl_t *sched_ctrl)
|
|
{
|
|
int x1_bitlen = csi_report->csi_meas_bitlen.pmi_x1_bitlen[ri];
|
|
int x2_bitlen = csi_report->csi_meas_bitlen.pmi_x2_bitlen[ri];
|
|
int tot_bitlen = x1_bitlen + x2_bitlen;
|
|
|
|
//in case of 2 port CSI configuration x1 is empty and the information bits are in x2
|
|
int temp_pmi = pickandreverse_bits(payload, tot_bitlen, cumul_bits);
|
|
|
|
sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.pmi_x1 = temp_pmi&((1<<x1_bitlen)-1);
|
|
sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.pmi_x2 = (temp_pmi>>x1_bitlen)&((1<<x2_bitlen)-1);
|
|
LOG_D(MAC,"PMI Report: X1 %d X2 %d\n",
|
|
sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.pmi_x1,
|
|
sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.pmi_x2);
|
|
|
|
return tot_bitlen;
|
|
}
|
|
|
|
static int evaluate_li_report(uint8_t *payload,
|
|
nr_csi_report_t *csi_report,
|
|
int cumul_bits,
|
|
uint8_t ri,
|
|
NR_UE_sched_ctrl_t *sched_ctrl)
|
|
{
|
|
int li_bitlen = csi_report->csi_meas_bitlen.li_bitlen[ri];
|
|
|
|
if (li_bitlen>0) {
|
|
int temp_li = pickandreverse_bits(payload, li_bitlen, cumul_bits);
|
|
LOG_D(MAC,"LI %d\n",temp_li);
|
|
sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.li = temp_li;
|
|
}
|
|
return li_bitlen;
|
|
}
|
|
|
|
static void skip_zero_padding(int *cumul_bits, nr_csi_report_t *csi_report, uint8_t ri, uint16_t max_bitlen)
|
|
{
|
|
// actual number of reported bits depends on the reported rank
|
|
// zero padding bits are added to have a predetermined max bit length to decode
|
|
|
|
uint16_t reported_bitlen = csi_report->csi_meas_bitlen.cri_bitlen+
|
|
csi_report->csi_meas_bitlen.ri_bitlen+
|
|
csi_report->csi_meas_bitlen.li_bitlen[ri]+
|
|
csi_report->csi_meas_bitlen.cqi_bitlen[ri]+
|
|
csi_report->csi_meas_bitlen.pmi_x1_bitlen[ri]+
|
|
csi_report->csi_meas_bitlen.pmi_x2_bitlen[ri];
|
|
|
|
*cumul_bits+=(max_bitlen-reported_bitlen);
|
|
}
|
|
|
|
static void extract_pucch_csi_report(NR_CSI_MeasConfig_t *csi_MeasConfig,
|
|
const nfapi_nr_uci_pucch_pdu_format_2_3_4_t *uci_pdu,
|
|
frame_t frame,
|
|
slot_t slot,
|
|
NR_UE_info_t *UE,
|
|
NR_ServingCellConfigCommon_t *scc)
|
|
{
|
|
/** From Table 6.3.1.1.2-3: RI, LI, CQI, and CRI of codebookType=typeI-SinglePanel */
|
|
uint8_t *payload = uci_pdu->csi_part1.csi_part1_payload;
|
|
uint16_t bitlen = uci_pdu->csi_part1.csi_part1_bit_len;
|
|
NR_CSI_ReportConfig__reportQuantity_PR reportQuantity_type = NR_CSI_ReportConfig__reportQuantity_PR_NOTHING;
|
|
NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
|
|
NR_UE_UL_BWP_t *ul_bwp = &UE->current_UL_BWP;
|
|
NR_UE_DL_BWP_t *dl_bwp = &UE->current_DL_BWP;
|
|
const int n_slots_frame = nr_slots_per_frame[ul_bwp->scs];
|
|
int cumul_bits = 0;
|
|
int r_index = -1;
|
|
for (int csi_report_id = 0; csi_report_id < csi_MeasConfig->csi_ReportConfigToAddModList->list.count; csi_report_id++ ) {
|
|
nr_csi_report_t *csi_report = &UE->csi_report_template[csi_report_id];
|
|
csi_report->nb_of_csi_ssb_report = 0;
|
|
uint8_t cri_bitlen = 0;
|
|
uint8_t ri_bitlen = 0;
|
|
uint8_t li_bitlen = 0;
|
|
uint8_t pmi_bitlen = 0;
|
|
NR_CSI_ReportConfig_t *csirep = csi_MeasConfig->csi_ReportConfigToAddModList->list.array[csi_report_id];
|
|
uint8_t cqi_table = (dl_bwp->dci_format == NR_DL_DCI_FORMAT_1_1 && csirep->cqi_Table) ? *csirep->cqi_Table : NR_CSI_ReportConfig__cqi_Table_table1;
|
|
const NR_PUCCH_CSI_Resource_t *pucchcsires = csirep->reportConfigType.choice.periodic->pucch_CSI_ResourceList.list.array[0];
|
|
if(pucchcsires->uplinkBandwidthPartId != ul_bwp->bwp_id)
|
|
continue;
|
|
int period, offset;
|
|
csi_period_offset(csirep, NULL, &period, &offset);
|
|
// verify if report with current id has been scheduled for this frame and slot
|
|
if ((n_slots_frame*frame + slot - offset)%period == 0) {
|
|
reportQuantity_type = csi_report->reportQuantity_type;
|
|
LOG_D(MAC,"SFN/SF:%d/%d reportQuantity type = %d\n",frame,slot,reportQuantity_type);
|
|
switch(reportQuantity_type){
|
|
case NR_CSI_ReportConfig__reportQuantity_PR_cri_RSRP:
|
|
evaluate_rsrp_report(UE,sched_ctrl,csi_report_id,payload,&cumul_bits,reportQuantity_type);
|
|
break;
|
|
case NR_CSI_ReportConfig__reportQuantity_PR_ssb_Index_RSRP:
|
|
evaluate_rsrp_report(UE,sched_ctrl,csi_report_id,payload,&cumul_bits,reportQuantity_type);
|
|
break;
|
|
case NR_CSI_ReportConfig__reportQuantity_PR_cri_RI_CQI:
|
|
cri_bitlen = csi_report->csi_meas_bitlen.cri_bitlen;
|
|
if(cri_bitlen)
|
|
evaluate_cri_report(payload,cri_bitlen,cumul_bits,sched_ctrl);
|
|
cumul_bits += cri_bitlen;
|
|
ri_bitlen = csi_report->csi_meas_bitlen.ri_bitlen;
|
|
if(ri_bitlen)
|
|
r_index = evaluate_ri_report(payload,ri_bitlen,csi_report->csi_meas_bitlen.ri_restriction,cumul_bits,sched_ctrl);
|
|
cumul_bits += ri_bitlen;
|
|
if (r_index != -1)
|
|
skip_zero_padding(&cumul_bits,csi_report,r_index,bitlen);
|
|
evaluate_cqi_report(payload,csi_report,cumul_bits,r_index,UE,cqi_table);
|
|
break;
|
|
case NR_CSI_ReportConfig__reportQuantity_PR_cri_RI_PMI_CQI:
|
|
cri_bitlen = csi_report->csi_meas_bitlen.cri_bitlen;
|
|
if(cri_bitlen)
|
|
evaluate_cri_report(payload,cri_bitlen,cumul_bits,sched_ctrl);
|
|
cumul_bits += cri_bitlen;
|
|
ri_bitlen = csi_report->csi_meas_bitlen.ri_bitlen;
|
|
if(ri_bitlen)
|
|
r_index = evaluate_ri_report(payload,ri_bitlen,csi_report->csi_meas_bitlen.ri_restriction,cumul_bits,sched_ctrl);
|
|
cumul_bits += ri_bitlen;
|
|
if (r_index != -1)
|
|
skip_zero_padding(&cumul_bits,csi_report,r_index,bitlen);
|
|
pmi_bitlen = evaluate_pmi_report(payload,csi_report,cumul_bits,r_index,sched_ctrl);
|
|
sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.csi_report_id = csi_report_id;
|
|
cumul_bits += pmi_bitlen;
|
|
evaluate_cqi_report(payload,csi_report,cumul_bits,r_index,UE,cqi_table);
|
|
break;
|
|
case NR_CSI_ReportConfig__reportQuantity_PR_cri_RI_LI_PMI_CQI:
|
|
cri_bitlen = csi_report->csi_meas_bitlen.cri_bitlen;
|
|
if(cri_bitlen)
|
|
evaluate_cri_report(payload,cri_bitlen,cumul_bits,sched_ctrl);
|
|
cumul_bits += cri_bitlen;
|
|
ri_bitlen = csi_report->csi_meas_bitlen.ri_bitlen;
|
|
if(ri_bitlen)
|
|
r_index = evaluate_ri_report(payload,ri_bitlen,csi_report->csi_meas_bitlen.ri_restriction,cumul_bits,sched_ctrl);
|
|
cumul_bits += ri_bitlen;
|
|
li_bitlen = evaluate_li_report(payload,csi_report,cumul_bits,r_index,sched_ctrl);
|
|
cumul_bits += li_bitlen;
|
|
if (r_index != -1)
|
|
skip_zero_padding(&cumul_bits,csi_report,r_index,bitlen);
|
|
pmi_bitlen = evaluate_pmi_report(payload,csi_report,cumul_bits,r_index,sched_ctrl);
|
|
sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.csi_report_id = csi_report_id;
|
|
cumul_bits += pmi_bitlen;
|
|
evaluate_cqi_report(payload,csi_report,cumul_bits,r_index,UE,cqi_table);
|
|
break;
|
|
default:
|
|
AssertFatal(1==0, "Invalid or not supported CSI measurement report\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static NR_UE_harq_t *find_harq(frame_t frame, sub_frame_t slot, NR_UE_info_t * UE, int harq_round_max)
|
|
{
|
|
/* In case of realtime problems: we can only identify a HARQ process by
|
|
* timing. If the HARQ process's feedback_frame/feedback_slot is not the one we
|
|
* expected, we assume that processing has been aborted and we need to
|
|
* skip this HARQ process, which is what happens in the loop below.
|
|
* Similarly, we might be "in advance", in which case we need to skip
|
|
* this result. */
|
|
NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
|
|
int8_t pid = sched_ctrl->feedback_dl_harq.head;
|
|
if (pid < 0)
|
|
return NULL;
|
|
NR_UE_harq_t *harq = &sched_ctrl->harq_processes[pid];
|
|
/* old feedbacks we missed: mark for retransmission */
|
|
while (harq->feedback_frame != frame
|
|
|| (harq->feedback_frame == frame && harq->feedback_slot < slot)) {
|
|
LOG_W(NR_MAC,
|
|
"expected HARQ pid %d feedback at %4d.%2d, but is at %4d.%2d instead (HARQ feedback is in the past)\n",
|
|
pid,
|
|
harq->feedback_frame,
|
|
harq->feedback_slot,
|
|
frame,
|
|
slot);
|
|
remove_front_nr_list(&sched_ctrl->feedback_dl_harq);
|
|
handle_dl_harq(UE, pid, 0, harq_round_max);
|
|
pid = sched_ctrl->feedback_dl_harq.head;
|
|
if (pid < 0)
|
|
return NULL;
|
|
harq = &sched_ctrl->harq_processes[pid];
|
|
}
|
|
/* feedbacks that we wait for in the future: don't do anything */
|
|
if (harq->feedback_slot > slot) {
|
|
LOG_W(NR_MAC,
|
|
"expected HARQ pid %d feedback at %4d.%2d, but is at %4d.%2d instead (HARQ feedback is in the future)\n",
|
|
pid,
|
|
harq->feedback_frame,
|
|
harq->feedback_slot,
|
|
frame,
|
|
slot);
|
|
return NULL;
|
|
}
|
|
return harq;
|
|
}
|
|
|
|
void handle_nr_uci_pucch_0_1(module_id_t mod_id,
|
|
frame_t frame,
|
|
sub_frame_t slot,
|
|
const nfapi_nr_uci_pucch_pdu_format_0_1_t *uci_01)
|
|
{
|
|
gNB_MAC_INST *nrmac = RC.nrmac[mod_id];
|
|
NR_SCHED_LOCK(&nrmac->sched_lock);
|
|
NR_UE_info_t * UE = find_nr_UE(&nrmac->UE_info, uci_01->rnti);
|
|
if (!UE) {
|
|
LOG_E(NR_MAC, "%s(): unknown RNTI %04x in PUCCH UCI\n", __func__, uci_01->rnti);
|
|
NR_SCHED_UNLOCK(&nrmac->sched_lock);
|
|
return;
|
|
}
|
|
NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
|
|
|
|
if (((uci_01->pduBitmap >> 1) & 0x01)) {
|
|
// iterate over received harq bits
|
|
for (int harq_bit = 0; harq_bit < uci_01->harq->num_harq; harq_bit++) {
|
|
const uint8_t harq_value = uci_01->harq->harq_list[harq_bit].harq_value;
|
|
const uint8_t harq_confidence = uci_01->harq->harq_confidence_level;
|
|
NR_UE_harq_t *harq = find_harq(frame, slot, UE, nrmac->dl_bler.harq_round_max);
|
|
if (!harq) {
|
|
LOG_E(NR_MAC, "Oh no! Could not find a harq in %s!\n", __FUNCTION__);
|
|
break;
|
|
}
|
|
DevAssert(harq->is_waiting);
|
|
const int8_t pid = sched_ctrl->feedback_dl_harq.head;
|
|
remove_front_nr_list(&sched_ctrl->feedback_dl_harq);
|
|
LOG_D(NR_MAC,"%4d.%2d bit %d pid %d ack/nack %d\n",frame, slot, harq_bit,pid,harq_value);
|
|
handle_dl_harq(UE, pid, harq_value == 0 && harq_confidence == 0, nrmac->dl_bler.harq_round_max);
|
|
if (harq_confidence == 1) UE->mac_stats.pucch0_DTX++;
|
|
}
|
|
|
|
// tpc (power control) only if we received AckNack
|
|
if (uci_01->harq->harq_confidence_level==0)
|
|
sched_ctrl->tpc1 = nr_get_tpc(nrmac->pucch_target_snrx10, uci_01->ul_cqi, 30);
|
|
else
|
|
sched_ctrl->tpc1 = 3;
|
|
sched_ctrl->pucch_snrx10 = uci_01->ul_cqi * 5 - 640;
|
|
|
|
free(uci_01->harq->harq_list);
|
|
free(uci_01->harq);
|
|
}
|
|
|
|
// check scheduling request result, confidence_level == 0 is good
|
|
if (uci_01->pduBitmap & 0x1) {
|
|
if (uci_01->sr->sr_indication && uci_01->sr->sr_confidence_level == 0 && uci_01->ul_cqi >= 148) {
|
|
// SR detected with SNR >= 10dB
|
|
sched_ctrl->SR |= true;
|
|
LOG_D(NR_MAC, "SR UE %04x ul_cqi %d\n", uci_01->rnti, uci_01->ul_cqi);
|
|
}
|
|
free(uci_01->sr);
|
|
}
|
|
NR_SCHED_UNLOCK(&nrmac->sched_lock);
|
|
}
|
|
|
|
void handle_nr_uci_pucch_2_3_4(module_id_t mod_id,
|
|
frame_t frame,
|
|
sub_frame_t slot,
|
|
const nfapi_nr_uci_pucch_pdu_format_2_3_4_t *uci_234)
|
|
{
|
|
gNB_MAC_INST *nrmac = RC.nrmac[mod_id];
|
|
NR_SCHED_LOCK(&nrmac->sched_lock);
|
|
|
|
NR_UE_info_t * UE = find_nr_UE(&nrmac->UE_info, uci_234->rnti);
|
|
if (!UE) {
|
|
NR_SCHED_UNLOCK(&nrmac->sched_lock);
|
|
LOG_E(NR_MAC, "%s(): unknown RNTI %04x in PUCCH UCI\n", __func__, uci_234->rnti);
|
|
return;
|
|
}
|
|
|
|
NR_CSI_MeasConfig_t *csi_MeasConfig = UE->current_UL_BWP.csi_MeasConfig;
|
|
if (csi_MeasConfig==NULL) {
|
|
NR_SCHED_UNLOCK(&nrmac->sched_lock);
|
|
return;
|
|
}
|
|
NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
|
|
|
|
// tpc (power control)
|
|
// TODO PUCCH2 SNR computation is not correct -> ignore the following
|
|
//sched_ctrl->tpc1 = nr_get_tpc(RC.nrmac[mod_id]->pucch_target_snrx10,
|
|
// uci_234->ul_cqi,
|
|
// 30);
|
|
//sched_ctrl->pucch_snrx10 = uci_234->ul_cqi * 5 - 640;
|
|
|
|
// TODO: handle SR
|
|
if (uci_234->pduBitmap & 0x1) {
|
|
free(uci_234->sr.sr_payload);
|
|
}
|
|
|
|
if ((uci_234->pduBitmap >> 1) & 0x01) {
|
|
// iterate over received harq bits
|
|
for (int harq_bit = 0; harq_bit < uci_234->harq.harq_bit_len; harq_bit++) {
|
|
const int acknack = ((uci_234->harq.harq_payload[harq_bit >> 3]) >> harq_bit) & 0x01;
|
|
NR_UE_harq_t *harq = find_harq(frame, slot, UE, RC.nrmac[mod_id]->dl_bler.harq_round_max);
|
|
if (!harq)
|
|
break;
|
|
DevAssert(harq->is_waiting);
|
|
const int8_t pid = sched_ctrl->feedback_dl_harq.head;
|
|
remove_front_nr_list(&sched_ctrl->feedback_dl_harq);
|
|
handle_dl_harq(UE, pid, uci_234->harq.harq_crc != 1 && acknack, nrmac->dl_bler.harq_round_max);
|
|
}
|
|
free(uci_234->harq.harq_payload);
|
|
}
|
|
if ((uci_234->pduBitmap >> 2) & 0x01) {
|
|
//API to parse the csi report and store it into sched_ctrl
|
|
extract_pucch_csi_report(csi_MeasConfig, uci_234, frame, slot, UE, nrmac->common_channels->ServingCellConfigCommon);
|
|
//TCI handling function
|
|
tci_handling(UE,frame, slot);
|
|
free(uci_234->csi_part1.csi_part1_payload);
|
|
}
|
|
if ((uci_234->pduBitmap >> 3) & 0x01) {
|
|
//@TODO:Handle CSI Report 2
|
|
// nothing to free (yet)
|
|
}
|
|
NR_SCHED_UNLOCK(&nrmac->sched_lock);
|
|
}
|
|
|
|
static void set_pucch_allocation(const NR_UE_UL_BWP_t *ul_bwp, const int r_pucch, const int bwp_size, NR_sched_pucch_t *pucch)
|
|
{
|
|
if(r_pucch<0){
|
|
const NR_PUCCH_Resource_t *resource = ul_bwp->pucch_Config->resourceToAddModList->list.array[0];
|
|
DevAssert(resource->format.present == NR_PUCCH_Resource__format_PR_format0);
|
|
pucch->second_hop_prb = resource->secondHopPRB!= NULL ? *resource->secondHopPRB : 0;
|
|
pucch->nr_of_symb = resource->format.choice.format0->nrofSymbols;
|
|
pucch->start_symb = resource->format.choice.format0->startingSymbolIndex;
|
|
pucch->prb_start = resource->startingPRB;
|
|
}
|
|
else{
|
|
int rsetindex = *ul_bwp->pucch_ConfigCommon->pucch_ResourceCommon;
|
|
set_r_pucch_parms(rsetindex,
|
|
r_pucch,
|
|
bwp_size,
|
|
&pucch->prb_start,
|
|
&pucch->second_hop_prb,
|
|
&pucch->nr_of_symb,
|
|
&pucch->start_symb);
|
|
}
|
|
}
|
|
|
|
static bool test_pucch0_vrb_occupation(const NR_sched_pucch_t *pucch, uint16_t *vrb_map_UL, const int bwp_start, const int bwp_size)
|
|
{
|
|
// We assume initial cyclic shift is always 0 so different pucch resources can't overlap
|
|
|
|
// verifying occupation of PRBs for ACK/NACK on dedicated pucch
|
|
for (int l=0; l<pucch->nr_of_symb; l++) {
|
|
uint16_t symb = SL_to_bitmap(pucch->start_symb+l, 1);
|
|
int prb;
|
|
if (l==1 && pucch->second_hop_prb != 0)
|
|
prb = pucch->second_hop_prb;
|
|
else
|
|
prb = pucch->prb_start;
|
|
if ((vrb_map_UL[bwp_start+prb] & symb) != 0) {
|
|
return false;
|
|
break;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static void set_pucch0_vrb_occupation(const NR_sched_pucch_t *pucch, uint16_t *vrb_map_UL, const int bwp_start)
|
|
{
|
|
for (int l=0; l<pucch->nr_of_symb; l++) {
|
|
uint16_t symb = SL_to_bitmap(pucch->start_symb+l, 1);
|
|
int prb;
|
|
if (l==1 && pucch->second_hop_prb != 0)
|
|
prb = pucch->second_hop_prb;
|
|
else
|
|
prb = pucch->prb_start;
|
|
vrb_map_UL[bwp_start+prb] |= symb;
|
|
}
|
|
}
|
|
|
|
// this function returns an index to NR_sched_pucch structure
|
|
// if the function returns -1 it was not possible to schedule acknack
|
|
int nr_acknack_scheduling(gNB_MAC_INST *mac,
|
|
NR_UE_info_t *UE,
|
|
frame_t frame,
|
|
sub_frame_t slot,
|
|
int r_pucch,
|
|
int is_common)
|
|
{
|
|
/* we assume that this function is mutex-protected from outside. Since it is
|
|
* called often, don't try to lock every time */
|
|
|
|
const int CC_id = 0;
|
|
const int minfbtime = mac->minRXTXTIMEpdsch;
|
|
const NR_ServingCellConfigCommon_t *scc = mac->common_channels[CC_id].ServingCellConfigCommon;
|
|
const NR_UE_UL_BWP_t *ul_bwp = &UE->current_UL_BWP;
|
|
const int n_slots_frame = nr_slots_per_frame[ul_bwp->scs];
|
|
const NR_TDD_UL_DL_Pattern_t *tdd = scc->tdd_UL_DL_ConfigurationCommon ? &scc->tdd_UL_DL_ConfigurationCommon->pattern1 : NULL;
|
|
AssertFatal(tdd || mac->common_channels[CC_id].frame_type == FDD, "Dynamic TDD not handled yet\n");
|
|
const int nr_slots_period = tdd ? n_slots_frame / get_nb_periods_per_frame(tdd->dl_UL_TransmissionPeriodicity) : n_slots_frame;
|
|
const int first_ul_slot_period = tdd ? get_first_ul_slot(tdd->nrofDownlinkSlots, tdd->nrofDownlinkSymbols, tdd->nrofUplinkSymbols) : 0;
|
|
|
|
NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
|
|
NR_PUCCH_Config_t *pucch_Config = ul_bwp->pucch_Config;
|
|
|
|
const int bwp_start = ul_bwp->BWPStart;
|
|
const int bwp_size = ul_bwp->BWPSize;
|
|
|
|
nr_dci_format_t dci_format = NR_DL_DCI_FORMAT_1_0;
|
|
if(is_common == 0)
|
|
dci_format = UE->current_DL_BWP.dci_format;
|
|
|
|
uint8_t pdsch_to_harq_feedback[8];
|
|
int fb_size = get_pdsch_to_harq_feedback(pucch_Config, dci_format, pdsch_to_harq_feedback);
|
|
|
|
for (int f = 0; f < fb_size; f++) {
|
|
// can't schedule ACKNACK before minimum feedback time
|
|
if(pdsch_to_harq_feedback[f] < minfbtime)
|
|
continue;
|
|
const int pucch_slot = (slot + pdsch_to_harq_feedback[f]) % n_slots_frame;
|
|
// check if the slot is UL
|
|
if(pucch_slot%nr_slots_period < first_ul_slot_period)
|
|
continue;
|
|
const int pucch_frame = (frame + ((slot + pdsch_to_harq_feedback[f]) / n_slots_frame)) & 1023;
|
|
// we store PUCCH resources according to slot, TDD configuration and size of the vector containing PUCCH structures
|
|
const int pucch_index = get_pucch_index(pucch_frame, pucch_slot, n_slots_frame, tdd, sched_ctrl->sched_pucch_size);
|
|
NR_sched_pucch_t *curr_pucch = &sched_ctrl->sched_pucch[pucch_index];
|
|
if (curr_pucch->active &&
|
|
curr_pucch->frame == pucch_frame &&
|
|
curr_pucch->ul_slot == pucch_slot) { // if there is already a PUCCH in given frame and slot
|
|
LOG_D(NR_MAC, "pucch_acknack DL %4d.%2d, UL_ACK %4d.%2d Bits already in current PUCCH: DAI_C %d CSI %d\n",
|
|
frame, slot, pucch_frame, pucch_slot, curr_pucch->dai_c, curr_pucch->csi_bits);
|
|
// we can't schedule if short pucch is already full
|
|
if (curr_pucch->csi_bits == 0 &&
|
|
curr_pucch->dai_c == 2)
|
|
continue;
|
|
// if there is CSI but simultaneous HARQ+CSI is disable we can't schedule
|
|
if(curr_pucch->csi_bits > 0 &&
|
|
!curr_pucch->simultaneous_harqcsi)
|
|
continue;
|
|
// TODO we can't schedule more than 11 bits in PUCCH2 for now
|
|
if (curr_pucch->csi_bits + curr_pucch->dai_c >= 10)
|
|
continue;
|
|
|
|
// otherwise we can schedule in this active PUCCH
|
|
// no need to check VRB occupation because already done when PUCCH has been activated
|
|
curr_pucch->timing_indicator = f;
|
|
curr_pucch->dai_c++;
|
|
LOG_D(NR_MAC, "DL %4d.%2d, UL_ACK %4d.%2d Scheduling ACK/NACK in PUCCH %d with timing indicator %d DAI %d CSI %d\n",
|
|
frame,slot,curr_pucch->frame,curr_pucch->ul_slot,pucch_index,f,curr_pucch->dai_c,curr_pucch->csi_bits);
|
|
return pucch_index; // index of current PUCCH structure
|
|
}
|
|
else if (curr_pucch->active) {
|
|
AssertFatal(1==0, "This shouldn't happen! curr_pucch frame.slot %d.%d not matching with computed frame.slot %d.%d\n",
|
|
curr_pucch->frame, curr_pucch->ul_slot, pucch_frame, pucch_slot);
|
|
}
|
|
else { // unoccupied occasion
|
|
// checking if in ul_slot the resources potentially to be assigned to this PUCCH are available
|
|
set_pucch_allocation(ul_bwp, r_pucch, bwp_size, curr_pucch);
|
|
const int index = ul_buffer_index(pucch_frame, pucch_slot, ul_bwp->scs, mac->vrb_map_UL_size);
|
|
uint16_t *vrb_map_UL = &mac->common_channels[CC_id].vrb_map_UL[index * MAX_BWP_SIZE];
|
|
bool ret = test_pucch0_vrb_occupation(curr_pucch,
|
|
vrb_map_UL,
|
|
bwp_start,
|
|
bwp_size);
|
|
if(!ret) {
|
|
LOG_D(NR_MAC, "DL %4d.%2d, UL_ACK %4d.%2d PRB resources for this occasion are already occupied, move to the following occasion\n",
|
|
frame, slot, pucch_frame, pucch_slot);
|
|
continue;
|
|
}
|
|
// allocating a new PUCCH structure for this occasion
|
|
curr_pucch->active = true;
|
|
curr_pucch->frame = pucch_frame;
|
|
curr_pucch->ul_slot = pucch_slot;
|
|
curr_pucch->timing_indicator = f; // index in the list of timing indicators
|
|
curr_pucch->dai_c++;
|
|
curr_pucch->resource_indicator = 0; // each UE has dedicated PUCCH resources
|
|
curr_pucch->r_pucch=r_pucch;
|
|
|
|
LOG_D(NR_MAC, "DL %4d.%2d, UL_ACK %4d.%2d Scheduling ACK/NACK in PUCCH %d with timing indicator %d DAI %d\n",
|
|
frame, slot, curr_pucch->frame, curr_pucch->ul_slot, pucch_index, f, curr_pucch->dai_c);
|
|
|
|
// blocking resources for current PUCCH in VRB map
|
|
set_pucch0_vrb_occupation(curr_pucch, vrb_map_UL, bwp_start);
|
|
|
|
return pucch_index; // index of current PUCCH structure
|
|
}
|
|
}
|
|
LOG_D(NR_MAC, "DL %4d.%2d, Couldn't find scheduling occasion for this HARQ process\n", frame, slot);
|
|
return -1;
|
|
}
|
|
|
|
|
|
void nr_sr_reporting(gNB_MAC_INST *nrmac, frame_t SFN, sub_frame_t slot)
|
|
{
|
|
/* already mutex protected: held in gNB_dlsch_ulsch_scheduler() */
|
|
NR_SCHED_ENSURE_LOCKED(&nrmac->sched_lock);
|
|
|
|
if (!is_xlsch_in_slot(nrmac->ulsch_slot_bitmap[slot / 64], slot))
|
|
return;
|
|
const int CC_id = 0;
|
|
UE_iterator(nrmac->UE_info.list, UE) {
|
|
NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
|
|
NR_UE_UL_BWP_t *ul_bwp = &UE->current_UL_BWP;
|
|
const int n_slots_frame = nr_slots_per_frame[ul_bwp->scs];
|
|
if (sched_ctrl->ul_failure==1 || sched_ctrl->rrc_processing_timer>0) continue;
|
|
NR_PUCCH_Config_t *pucch_Config = ul_bwp->pucch_Config;
|
|
|
|
if (!pucch_Config || !pucch_Config->schedulingRequestResourceToAddModList)
|
|
continue;
|
|
|
|
AssertFatal(pucch_Config->schedulingRequestResourceToAddModList->list.count>0,"NO SR configuration available");
|
|
|
|
for (int SR_resource_id = 0; SR_resource_id < pucch_Config->schedulingRequestResourceToAddModList->list.count;SR_resource_id++) {
|
|
NR_SchedulingRequestResourceConfig_t *SchedulingRequestResourceConfig = pucch_Config->schedulingRequestResourceToAddModList->list.array[SR_resource_id];
|
|
|
|
int SR_period; int SR_offset;
|
|
|
|
find_period_offset_SR(SchedulingRequestResourceConfig,&SR_period,&SR_offset);
|
|
// convert to int to avoid underflow of uint
|
|
int sfn_sf = SFN * n_slots_frame + slot;
|
|
LOG_D(NR_MAC,"SR_resource_id %d: SR_period %d, SR_offset %d\n",SR_resource_id,SR_period,SR_offset);
|
|
if ((sfn_sf - SR_offset) % SR_period != 0)
|
|
continue;
|
|
LOG_D(NR_MAC, "%4d.%2d Scheduling Request UE %04x identified\n", SFN, slot, UE->rnti);
|
|
NR_PUCCH_ResourceId_t *PucchResourceId = SchedulingRequestResourceConfig->resource;
|
|
|
|
int idx = -1;
|
|
NR_PUCCH_ResourceSet_t *pucchresset = pucch_Config->resourceSetToAddModList->list.array[0]; // set with formats 0,1
|
|
int n_list = pucchresset->resourceList.list.count;
|
|
for (int i=0; i<n_list; i++) {
|
|
if (*pucchresset->resourceList.list.array[i] == *PucchResourceId )
|
|
idx = i;
|
|
}
|
|
AssertFatal(idx>-1,"SR resource not found among PUCCH resources");
|
|
|
|
const NR_ServingCellConfigCommon_t *scc = nrmac->common_channels[CC_id].ServingCellConfigCommon;
|
|
const NR_TDD_UL_DL_Pattern_t *tdd = scc->tdd_UL_DL_ConfigurationCommon ? &scc->tdd_UL_DL_ConfigurationCommon->pattern1 : NULL;
|
|
AssertFatal(tdd || nrmac->common_channels[CC_id].frame_type == FDD, "Dynamic TDD not handled yet\n");
|
|
const int pucch_index = get_pucch_index(SFN, slot, n_slots_frame, tdd, sched_ctrl->sched_pucch_size);
|
|
NR_sched_pucch_t *curr_pucch = &sched_ctrl->sched_pucch[pucch_index];
|
|
|
|
if (curr_pucch->active &&
|
|
curr_pucch->frame == SFN &&
|
|
curr_pucch->ul_slot == slot &&
|
|
curr_pucch->resource_indicator == idx)
|
|
curr_pucch->sr_flag = true;
|
|
else if (curr_pucch->active) {
|
|
AssertFatal(1==0, "This shouldn't happen! curr_pucch frame.slot %d.%d not matching with SR function frame.slot %d.%d\n",
|
|
curr_pucch->frame, curr_pucch->ul_slot, SFN, slot);
|
|
continue;
|
|
}
|
|
else {
|
|
const int index = ul_buffer_index(SFN, slot, ul_bwp->scs, nrmac->vrb_map_UL_size);
|
|
uint16_t *vrb_map_UL = &nrmac->common_channels[CC_id].vrb_map_UL[index * MAX_BWP_SIZE];
|
|
const int bwp_start = ul_bwp->BWPStart;
|
|
const int bwp_size = ul_bwp->BWPSize;
|
|
set_pucch_allocation(ul_bwp, -1, bwp_size, curr_pucch);
|
|
bool ret = test_pucch0_vrb_occupation(curr_pucch,
|
|
vrb_map_UL,
|
|
bwp_start,
|
|
bwp_size);
|
|
if (!ret) {
|
|
LOG_E(NR_MAC,"Cannot schedule SR. PRBs not available\n");
|
|
continue;
|
|
}
|
|
curr_pucch->frame = SFN;
|
|
curr_pucch->ul_slot = slot;
|
|
curr_pucch->sr_flag = true;
|
|
curr_pucch->resource_indicator = idx;
|
|
curr_pucch->r_pucch = -1;
|
|
curr_pucch->active = true;
|
|
set_pucch0_vrb_occupation(curr_pucch, vrb_map_UL, bwp_start);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|