1446 lines
64 KiB
C
1446 lines
64 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 "LAYER2/MAC/mac.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)
|
|
{
|
|
nfapi_nr_ul_tti_request_t *future_ul_tti_req =
|
|
&nrmac->UL_tti_req_ahead[0][pucch->ul_slot];
|
|
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_acknak" : "",
|
|
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
|
|
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]
|
|
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
|
|
};
|
|
|
|
|
|
void nr_schedule_pucch(gNB_MAC_INST *nrmac,
|
|
frame_t frameP,
|
|
sub_frame_t slotP)
|
|
{
|
|
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;
|
|
const int n = sizeof(sched_ctrl->sched_pucch) / sizeof(*sched_ctrl->sched_pucch);
|
|
for (int i = 0; i < n; i++) {
|
|
NR_sched_pucch_t *curr_pucch = &UE->UE_sched_ctrl.sched_pucch[i];
|
|
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;
|
|
if (O_ack + O_csi + O_sr == 0
|
|
|| frameP != curr_pucch->frame
|
|
|| slotP != curr_pucch->ul_slot)
|
|
continue;
|
|
|
|
if (O_csi > 0)
|
|
LOG_D(NR_MAC,"Scheduling PUCCH[%d] RX for UE %04x in %4d.%2d O_ack %d, O_sr %d, O_csi %d\n",
|
|
i,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) {
|
|
|
|
UE_iterator(RC.nrmac[Mod_idP]->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;
|
|
|
|
int period, offset;
|
|
csi_period_offset(csirep, NULL, &period, &offset);
|
|
const int sched_slot = (period + offset) % n_slots_frame;
|
|
// prepare to schedule csi measurement reception according to 5.2.1.4 in 38.214
|
|
// preparation is done in first slot of tdd period
|
|
if (frame % (period / n_slots_frame) != offset / n_slots_frame)
|
|
continue;
|
|
LOG_D(NR_MAC, "CSI reporting in frame %d slot %d CSI report ID %ld\n", 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);
|
|
|
|
// find free PUCCH that is in order with possibly existing PUCCH
|
|
// schedulings (other CSI, SR)
|
|
NR_sched_pucch_t *curr_pucch = &sched_ctrl->sched_pucch[1];
|
|
AssertFatal(curr_pucch->csi_bits == 0
|
|
&& !curr_pucch->sr_flag
|
|
&& curr_pucch->dai_c == 0,
|
|
"PUCCH not free at index 1 for UE %04x\n",
|
|
UE->rnti);
|
|
curr_pucch->r_pucch = -1;
|
|
curr_pucch->frame = 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);
|
|
|
|
int bwp_start = ul_bwp->BWPStart;
|
|
|
|
// going through the list of PUCCH resources to find the one indexed by resource_id
|
|
uint16_t *vrb_map_UL = &RC.nrmac[Mod_idP]->common_channels[0].vrb_map_UL[sched_slot * 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", 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, 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++;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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)
|
|
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)
|
|
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
|
|
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
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
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++;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
void evaluate_cqi_report(uint8_t *payload,
|
|
nr_csi_report_t *csi_report,
|
|
int cumul_bits,
|
|
uint8_t ri,
|
|
NR_UE_info_t *UE,
|
|
long *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
|
|
if (cqi_Table)
|
|
sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.cqi_table = *cqi_Table;
|
|
else
|
|
AssertFatal(1==0,"CQI Table not present in RRC configuration\n");
|
|
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;
|
|
const int cqi_table = sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.cqi_table;
|
|
sched_ctrl->dl_max_mcs = get_mcs_from_cqi(mcs_table, cqi_table, cqi_idx);
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
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;
|
|
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];
|
|
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,csirep->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,csirep->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,csirep->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)
|
|
{
|
|
NR_UE_info_t * UE = find_nr_UE(&RC.nrmac[mod_id]->UE_info, uci_01->rnti);
|
|
if (!UE) {
|
|
LOG_E(NR_MAC, "%s(): unknown RNTI %04x in PUCCH UCI\n", __func__, uci_01->rnti);
|
|
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, RC.nrmac[mod_id]->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, RC.nrmac[mod_id]->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(RC.nrmac[mod_id]->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);
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
NR_UE_info_t * UE = find_nr_UE(&RC.nrmac[mod_id]->UE_info, uci_234->rnti);
|
|
if (!UE) {
|
|
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)
|
|
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, RC.nrmac[mod_id]->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, RC.nrmac[mod_id]->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)
|
|
}
|
|
}
|
|
|
|
bool test_acknack_vrb_occupation(NR_UE_sched_ctrl_t *sched_ctrl,
|
|
NR_sched_pucch_t *pucch,
|
|
uint16_t *vrb_map_UL,
|
|
const NR_ServingCellConfigCommon_t *scc,
|
|
NR_PUCCH_Config_t *pucch_Config,
|
|
int r_pucch,
|
|
int bwp_start,
|
|
int bwp_size) {
|
|
|
|
// We assume initial cyclic shift is always 0 so different pucch resources can't overlap
|
|
|
|
NR_sched_pucch_t *csi_pucch = &sched_ctrl->sched_pucch[1];
|
|
if (csi_pucch &&
|
|
csi_pucch->csi_bits > 0 &&
|
|
csi_pucch->frame == pucch->frame &&
|
|
csi_pucch->ul_slot == pucch->ul_slot &&
|
|
csi_pucch->simultaneous_harqcsi &&
|
|
(csi_pucch->csi_bits + csi_pucch->dai_c) < 11)
|
|
return true; // available resources for csi_pucch already verified
|
|
|
|
if(r_pucch<0){
|
|
const NR_PUCCH_Resource_t *resource = 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 = *scc->uplinkConfigCommon->initialUplinkBWP->pucch_ConfigCommon->choice.setup->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);
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
// this function returns an index to NR_sched_pucch structure
|
|
// currently this structure contains PUCCH0 at index 0 and PUCCH2 at index 1
|
|
// if the function returns -1 it was not possible to schedule acknack
|
|
// when current pucch is ready to be scheduled nr_fill_nfapi_pucch is called
|
|
int nr_acknack_scheduling(int mod_id,
|
|
NR_UE_info_t *UE,
|
|
frame_t frame,
|
|
sub_frame_t slot,
|
|
int r_pucch,
|
|
int is_common) {
|
|
const int CC_id = 0;
|
|
const int minfbtime = RC.nrmac[mod_id]->minRXTXTIMEpdsch;
|
|
const NR_ServingCellConfigCommon_t *scc = RC.nrmac[mod_id]->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 || RC.nrmac[mod_id]->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 next_ul_slot = tdd ? tdd->nrofDownlinkSlots + nr_slots_period * (slot / nr_slots_period) : slot + minfbtime;
|
|
const int first_ul_slot_period = tdd ? tdd->nrofDownlinkSlots : 0;
|
|
|
|
|
|
/* for the moment, we consider:
|
|
* * only pucch_sched[0] holds HARQ (and SR)
|
|
* * we do not multiplex with CSI, which is always in pucch_sched[2]
|
|
* * SR uses format 0 and is allocated in the first UL (mixed) slot (and not
|
|
* later)
|
|
* * each UE has dedicated PUCCH Format 0 resources, and we use index 0! */
|
|
NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
|
|
NR_PUCCH_Config_t *pucch_Config = ul_bwp->pucch_Config;
|
|
|
|
int bwp_start = ul_bwp->BWPStart;
|
|
int bwp_size = ul_bwp->BWPSize;
|
|
|
|
NR_sched_pucch_t *pucch = &sched_ctrl->sched_pucch[0];
|
|
LOG_D(NR_MAC, "In %s: %4d.%2d Trying to allocate pucch, current DAI %d\n", __FUNCTION__, frame, slot, pucch->dai_c);
|
|
|
|
pucch->r_pucch=r_pucch;
|
|
AssertFatal(pucch->csi_bits == 0,
|
|
"%s(): csi_bits %d in sched_pucch[0]\n",
|
|
__func__,
|
|
pucch->csi_bits);
|
|
|
|
/* if the currently allocated PUCCH of this UE is full, allocate it */
|
|
NR_sched_pucch_t *csi_pucch = &sched_ctrl->sched_pucch[1];
|
|
if (pucch->dai_c == 2) {
|
|
/* advance the UL slot information in PUCCH by one so we won't schedule in
|
|
* the same slot again */
|
|
const int f = pucch->frame;
|
|
const int s = pucch->ul_slot;
|
|
LOG_D(NR_MAC, "In %s: %4d.%2d DAI = 2 pucch currently in %4d.%2d, advancing by 1 slot\n", __FUNCTION__, frame, slot, f, s);
|
|
|
|
if (!(csi_pucch
|
|
&& csi_pucch->csi_bits > 0
|
|
&& csi_pucch->frame == f
|
|
&& csi_pucch->ul_slot == s))
|
|
nr_fill_nfapi_pucch(RC.nrmac[mod_id], frame, slot, pucch, UE);
|
|
|
|
memset(pucch, 0, sizeof(*pucch));
|
|
pucch->frame = s == n_slots_frame - 1 ? (f + 1) % 1024 : f;
|
|
if(((s + 1)%nr_slots_period) == 0)
|
|
pucch->ul_slot = (s + 1 + first_ul_slot_period) % n_slots_frame;
|
|
else
|
|
pucch->ul_slot = (s + 1) % n_slots_frame;
|
|
// we assume that only two indices over the array sched_pucch exist
|
|
// skip the CSI PUCCH if it is present and if in the next frame/slot
|
|
// and if we don't multiplex
|
|
csi_pucch->r_pucch=-1;
|
|
if (csi_pucch
|
|
&& csi_pucch->csi_bits > 0
|
|
&& csi_pucch->frame == pucch->frame
|
|
&& csi_pucch->ul_slot == pucch->ul_slot
|
|
&& !csi_pucch->simultaneous_harqcsi) {
|
|
LOG_D(NR_MAC,"Cannot multiplex csi_pucch for %d.%d\n",csi_pucch->frame,csi_pucch->ul_slot);
|
|
nr_fill_nfapi_pucch(RC.nrmac[mod_id], frame, slot, csi_pucch, UE);
|
|
|
|
memset(csi_pucch, 0, sizeof(*csi_pucch));
|
|
pucch->frame = pucch->ul_slot == n_slots_frame - 1 ? (pucch->frame + 1) % 1024 : pucch->frame;
|
|
if(((pucch->ul_slot + 1)%nr_slots_period) == 0)
|
|
pucch->ul_slot = (pucch->ul_slot + 1 + first_ul_slot_period) % n_slots_frame;
|
|
else
|
|
pucch->ul_slot = (pucch->ul_slot + 1) % n_slots_frame;
|
|
}
|
|
}
|
|
|
|
LOG_D(NR_MAC, "In %s: pucch_acknak 1. DL %4d.%2d, UL_ACK %4d.%2d, DAI_C %d\n", __FUNCTION__, frame, slot, pucch->frame, pucch->ul_slot, pucch->dai_c);
|
|
|
|
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 max_fb_time = 0;
|
|
get_pdsch_to_harq_feedback(pucch_Config, dci_format, &max_fb_time, pdsch_to_harq_feedback);
|
|
|
|
LOG_D(NR_MAC, "In %s: 1b. DL %4d.%2d, UL_ACK %4d.%2d, DAI_C %d\n", __FUNCTION__, frame,slot,pucch->frame,pucch->ul_slot,pucch->dai_c);
|
|
/* there is a HARQ. Check whether we can use it for this ACKNACK */
|
|
if (pucch->dai_c > 0) {
|
|
/* this UE already has a PUCCH occasion */
|
|
// Find the right timing_indicator value.
|
|
int i = 0;
|
|
while (i < 8) {
|
|
int diff = pucch->ul_slot - slot;
|
|
if (diff<0)
|
|
diff += n_slots_frame;
|
|
if (pdsch_to_harq_feedback[i] == diff &&
|
|
pdsch_to_harq_feedback[i] >= minfbtime)
|
|
break;
|
|
++i;
|
|
}
|
|
if (i >= 8) {
|
|
// we cannot reach this timing anymore, allocate and try again
|
|
const int f = pucch->frame;
|
|
const int s = pucch->ul_slot;
|
|
LOG_D(NR_MAC, "In %s: %4d.%2d DAI > 0, cannot reach timing for pucch in %4d.%2d, advancing slot by 1 and trying again\n", __FUNCTION__, frame, slot, f, s);
|
|
if (!(csi_pucch &&
|
|
csi_pucch->csi_bits > 0 &&
|
|
csi_pucch->frame == f &&
|
|
csi_pucch->ul_slot == s))
|
|
nr_fill_nfapi_pucch(RC.nrmac[mod_id], frame, slot, pucch, UE);
|
|
memset(pucch, 0, sizeof(*pucch));
|
|
pucch->frame = s == n_slots_frame - 1 ? (f + 1) % 1024 : f;
|
|
if(((s + 1)%nr_slots_period) == 0)
|
|
pucch->ul_slot = (s + 1 + first_ul_slot_period) % n_slots_frame;
|
|
else
|
|
pucch->ul_slot = (s + 1) % n_slots_frame;
|
|
return nr_acknack_scheduling(mod_id, UE, frame, slot, r_pucch,is_common);
|
|
}
|
|
|
|
pucch->timing_indicator = i;
|
|
pucch->dai_c++;
|
|
// if there is CSI in this slot update the HARQ information for that one too
|
|
if (csi_pucch &&
|
|
csi_pucch->csi_bits > 0 &&
|
|
csi_pucch->frame == pucch->frame &&
|
|
csi_pucch->ul_slot == pucch->ul_slot) {
|
|
csi_pucch->timing_indicator = i;
|
|
csi_pucch->dai_c++;
|
|
}
|
|
// retain old resource indicator, and we are good
|
|
LOG_D(NR_MAC, "In %s: %4d.%2d. DAI > 0, pucch allocated for %4d.%2d (index %d)\n", __FUNCTION__, frame,slot,pucch->frame,pucch->ul_slot,pucch->timing_indicator);
|
|
return 0;
|
|
}
|
|
|
|
LOG_D(NR_MAC, "In %s: %4d.%2d DAI = 0, looking for new pucch occasion\n", __FUNCTION__, frame, slot);
|
|
/* we need to find a new PUCCH occasion */
|
|
|
|
/*(Re)Inizialization of timing information*/
|
|
if ((pucch->frame == 0 && pucch->ul_slot == 0) ||
|
|
((pucch->frame*n_slots_frame + pucch->ul_slot) <
|
|
(frame*n_slots_frame + slot))) {
|
|
AssertFatal(pucch->sr_flag + pucch->dai_c == 0,
|
|
"expected no SR/AckNack for UE %04x in %4d.%2d, but has %d/%d for %4d.%2d\n",
|
|
UE->rnti, frame, slot, pucch->sr_flag, pucch->dai_c, pucch->frame, pucch->ul_slot);
|
|
const int s = next_ul_slot;
|
|
pucch->frame = s < n_slots_frame ? frame : (frame + 1) % 1024;
|
|
pucch->ul_slot = s % n_slots_frame;
|
|
}
|
|
|
|
// Find the right timing_indicator value.
|
|
int ind_found = -1;
|
|
// while we are within the feedback limits
|
|
uint16_t *vrb_map_UL;
|
|
while ((n_slots_frame + pucch->ul_slot - slot) % n_slots_frame <= max_fb_time) {
|
|
// checking if in ul_slot the resources potentially to be assigned to this PUCCH are available
|
|
vrb_map_UL = &RC.nrmac[mod_id]->common_channels[CC_id].vrb_map_UL[pucch->ul_slot * MAX_BWP_SIZE];
|
|
bool ret = test_acknack_vrb_occupation(sched_ctrl,
|
|
pucch,
|
|
vrb_map_UL,
|
|
scc,
|
|
pucch_Config,
|
|
r_pucch,
|
|
bwp_start,
|
|
bwp_size);
|
|
if (ret) {
|
|
int i = 0;
|
|
while (i < 8) {
|
|
LOG_D(NR_MAC,"pdsch_to_harq_feedback[%d] = %d (pucch->ul_slot %d - slot %d)\n",
|
|
i,pdsch_to_harq_feedback[i],pucch->ul_slot,slot);
|
|
int diff = pucch->ul_slot - slot;
|
|
if (diff<0)
|
|
diff += n_slots_frame;
|
|
if (pdsch_to_harq_feedback[i] == diff &&
|
|
pdsch_to_harq_feedback[i] >= minfbtime) {
|
|
ind_found = i;
|
|
break;
|
|
}
|
|
++i;
|
|
}
|
|
if (ind_found!=-1)
|
|
break;
|
|
}
|
|
// advance to the next ul slot
|
|
const int f = pucch->frame;
|
|
const int s = pucch->ul_slot;
|
|
pucch->frame = s == n_slots_frame - 1 ? (f + 1) % 1024 : f;
|
|
if(((s + 1)%nr_slots_period) == 0)
|
|
pucch->ul_slot = (s + 1 + first_ul_slot_period) % n_slots_frame;
|
|
else
|
|
pucch->ul_slot = (s + 1) % n_slots_frame;
|
|
}
|
|
if (ind_found==-1) {
|
|
LOG_D(NR_MAC,
|
|
"%4d.%2d could not find pdsch_to_harq_feedback for UE %04x: earliest "
|
|
"ack slot %d\n",
|
|
frame,
|
|
slot,
|
|
UE->rnti,
|
|
pucch->ul_slot);
|
|
return -1;
|
|
}
|
|
|
|
if (csi_pucch &&
|
|
csi_pucch->csi_bits > 0 &&
|
|
csi_pucch->frame == pucch->frame &&
|
|
csi_pucch->ul_slot == pucch->ul_slot) {
|
|
// skip the CSI PUCCH if it is present and if in the next frame/slot
|
|
// and if we don't multiplex
|
|
/* FIXME currently we support at most 11 bits in pucch2 so skip also in that case.
|
|
We need to set the limit to 10 because SR scheduling has been moved afterwards */
|
|
if(!csi_pucch->simultaneous_harqcsi
|
|
|| ((csi_pucch->csi_bits + csi_pucch->dai_c) >= 10)) {
|
|
LOG_D(NR_MAC,"Cannot multiplex csi_pucch %d +csi_pucch->dai_c %d for %d.%d\n",csi_pucch->csi_bits,csi_pucch->dai_c,csi_pucch->frame,csi_pucch->ul_slot);
|
|
nr_fill_nfapi_pucch(RC.nrmac[mod_id], frame, slot, csi_pucch, UE);
|
|
memset(csi_pucch, 0, sizeof(*csi_pucch));
|
|
/* advance the UL slot information in PUCCH by one so we won't schedule in
|
|
* the same slot again */
|
|
const int f = pucch->frame;
|
|
const int s = pucch->ul_slot;
|
|
memset(pucch, 0, sizeof(*pucch));
|
|
pucch->frame = s == n_slots_frame - 1 ? (f + 1) % 1024 : f;
|
|
if(((s + 1)%nr_slots_period) == 0)
|
|
pucch->ul_slot = (s + 1 + first_ul_slot_period) % n_slots_frame;
|
|
else
|
|
pucch->ul_slot = (s + 1) % n_slots_frame;
|
|
return nr_acknack_scheduling(mod_id, UE, frame, slot, r_pucch,is_common);
|
|
}
|
|
// multiplexing harq and csi in a pucch
|
|
else {
|
|
csi_pucch->timing_indicator = ind_found;
|
|
csi_pucch->dai_c++;
|
|
memset(pucch,0,sizeof(*pucch));
|
|
|
|
LOG_D(NR_MAC,"multiplexing csi_pucch %d +csi_pucch->dai_c %d for %d.%d\n",csi_pucch->csi_bits,csi_pucch->dai_c,csi_pucch->frame,csi_pucch->ul_slot);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
pucch->timing_indicator = ind_found; // index in the list of timing indicators
|
|
|
|
LOG_D(NR_MAC, "In %s: 2. DAI 0 DL %4d.%2d, UL_ACK %4d.%2d (index %d)\n", __FUNCTION__, frame,slot,pucch->frame,pucch->ul_slot,pucch->timing_indicator);
|
|
|
|
pucch->dai_c++;
|
|
pucch->resource_indicator = 0; // each UE has dedicated PUCCH resources
|
|
pucch->r_pucch=r_pucch;
|
|
|
|
vrb_map_UL = &RC.nrmac[mod_id]->common_channels[CC_id].vrb_map_UL[pucch->ul_slot * MAX_BWP_SIZE];
|
|
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;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
void nr_sr_reporting(gNB_MAC_INST *nrmac, frame_t SFN, sub_frame_t slot)
|
|
{
|
|
if (!is_xlsch_in_slot(nrmac->ulsch_slot_bitmap[slot / 64], slot))
|
|
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];
|
|
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_offest_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 identified\n", SFN, slot);
|
|
NR_PUCCH_ResourceId_t *PucchResourceId = SchedulingRequestResourceConfig->resource;
|
|
|
|
int found = -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 )
|
|
found = i;
|
|
}
|
|
AssertFatal(found>-1,"SR resource not found among PUCCH resources");
|
|
|
|
/* loop through nFAPI PUCCH messages: if the UEs is in there in this slot
|
|
* with the resource_indicator, it means we already allocated that PUCCH
|
|
* resource for AckNack (e.g., the UE has been scheduled often), and we
|
|
* just need to add the SR_flag. Otherwise, just allocate in the internal
|
|
* PUCCH resource, and nr_schedule_pucch() will handle the rest */
|
|
NR_PUCCH_Resource_t *pucch_res = pucch_Config->resourceToAddModList->list.array[found];
|
|
/* for the moment, can only handle SR on PUCCH Format 0 */
|
|
DevAssert(pucch_res->format.present == NR_PUCCH_Resource__format_PR_format0);
|
|
nfapi_nr_ul_tti_request_t *ul_tti_req = &nrmac->UL_tti_req_ahead[0][slot];
|
|
bool nfapi_allocated = false;
|
|
for (int i = 0; i < ul_tti_req->n_pdus; ++i) {
|
|
if (ul_tti_req->pdus_list[i].pdu_type != NFAPI_NR_UL_CONFIG_PUCCH_PDU_TYPE)
|
|
continue;
|
|
nfapi_nr_pucch_pdu_t *pdu = &ul_tti_req->pdus_list[i].pucch_pdu;
|
|
/* check that it is our PUCCH F0. Assuming there can be only one */
|
|
if (pdu->rnti == UE->rnti
|
|
&& pdu->format_type == 0 // does not use NR_PUCCH_Resource__format_PR_format0
|
|
&& pdu->initial_cyclic_shift == pucch_res->format.choice.format0->initialCyclicShift
|
|
&& pdu->nr_of_symbols == pucch_res->format.choice.format0->nrofSymbols
|
|
&& pdu->start_symbol_index == pucch_res->format.choice.format0->startingSymbolIndex) {
|
|
LOG_D(NR_MAC,"%4d.%2d adding SR_flag 1 to PUCCH format 0 nFAPI SR for RNTI %04x\n", SFN, slot, pdu->rnti);
|
|
pdu->sr_flag = 1;
|
|
nfapi_allocated = true;
|
|
break;
|
|
} else if (pdu->rnti == UE->rnti
|
|
&& pdu->format_type == 2 // does not use NR_PUCCH_Resource__format_PR_format0
|
|
&& pdu->nr_of_symbols == pucch_res->format.choice.format2->nrofSymbols
|
|
&& pdu->start_symbol_index == pucch_res->format.choice.format2->startingSymbolIndex) {
|
|
LOG_D(NR_MAC,"%4d.%2d adding SR_flag 1 to PUCCH format 2 nFAPI SR for RNTI %04x\n", SFN, slot, pdu->rnti);
|
|
pdu->sr_flag = 1;
|
|
nfapi_allocated = true;
|
|
break;
|
|
} else if (pdu->rnti == UE->rnti
|
|
&& pdu->format_type == 1 // does not use NR_PUCCH_Resource__format_PR_format0
|
|
&& pdu->nr_of_symbols == pucch_res->format.choice.format1->nrofSymbols
|
|
&& pdu->start_symbol_index == pucch_res->format.choice.format1->startingSymbolIndex) {
|
|
LOG_D(NR_MAC,"%4d.%2d adding SR_flag 1 to PUCCH format 1 nFAPI SR for RNTI %04x\n", SFN, slot, pdu->rnti);
|
|
pdu->sr_flag = 1;
|
|
nfapi_allocated = true;
|
|
break;
|
|
} else if (pdu->rnti == UE->rnti
|
|
&& pdu->format_type == 3 // does not use NR_PUCCH_Resource__format_PR_format0
|
|
&& pdu->nr_of_symbols == pucch_res->format.choice.format3->nrofSymbols
|
|
&& pdu->start_symbol_index == pucch_res->format.choice.format3->startingSymbolIndex) {
|
|
LOG_D(NR_MAC,"%4d.%2d adding SR_flag 1 to PUCCH format 3 nFAPI SR for RNTI %04x\n", SFN, slot, pdu->rnti);
|
|
pdu->sr_flag = 1;
|
|
nfapi_allocated = true;
|
|
break;
|
|
} else if (pdu->rnti == UE->rnti
|
|
&& pdu->format_type == 4 // does not use NR_PUCCH_Resource__format_PR_format0
|
|
&& pdu->nr_of_symbols == pucch_res->format.choice.format4->nrofSymbols
|
|
&& pdu->start_symbol_index == pucch_res->format.choice.format4->startingSymbolIndex) {
|
|
LOG_D(NR_MAC,"%4d.%2d adding SR_flag 1 to PUCCH format 4 nFAPI SR for RNTI %04x\n", SFN, slot, pdu->rnti);
|
|
pdu->sr_flag = 1;
|
|
nfapi_allocated = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (nfapi_allocated) // break scheduling resource loop, continue next UE
|
|
break;
|
|
|
|
/* we did not find it: check if current PUCCH is for the current slot, in
|
|
* which case we add the SR to it; otherwise, allocate SR separately */
|
|
NR_sched_pucch_t *curr_pucch = &sched_ctrl->sched_pucch[0];
|
|
if (curr_pucch->frame == SFN && curr_pucch->ul_slot == slot) {
|
|
if (curr_pucch->resource_indicator != found) {
|
|
LOG_W(NR_MAC, "%4d.%2d expected PUCCH in this slot to have resource indicator of SR (%d), skipping SR\n", SFN, slot, found);
|
|
continue;
|
|
}
|
|
curr_pucch->sr_flag = true;
|
|
} else {
|
|
NR_sched_pucch_t sched_sr = {
|
|
.frame = SFN,
|
|
.ul_slot = slot,
|
|
.sr_flag = true,
|
|
.resource_indicator = found,
|
|
.r_pucch = -1
|
|
};
|
|
nr_fill_nfapi_pucch(nrmac, SFN, slot, &sched_sr, UE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|