From 4edc5c2d3341281633de679880a60900f4ed3267 Mon Sep 17 00:00:00 2001 From: francescomani Date: Mon, 5 May 2025 16:31:06 +0200 Subject: [PATCH 1/4] fix tci_StatesPDCCH --- openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c b/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c index 35d4c95ece..cfb38cf53a 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c +++ b/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c @@ -296,13 +296,13 @@ static NR_ControlResourceSet_t *get_coreset_config(int bwp_id, int bwp_start, in // The ID space is used across the BWPs of a Serving Cell as per 38.331 coreset->controlResourceSetId = bwp_id + 1; - coreset->tci_StatesPDCCH_ToAddList=calloc(1,sizeof(*coreset->tci_StatesPDCCH_ToAddList)); - NR_TCI_StateId_t *tci[64]; - for (int i=0;i<64;i++) { - if ((ssb_bitmap>>(63-i))&0x01){ - tci[i]=calloc(1,sizeof(*tci[i])); - *tci[i] = i; - asn1cSeqAdd(&coreset->tci_StatesPDCCH_ToAddList->list,tci[i]); + coreset->tci_StatesPDCCH_ToAddList = calloc(1,sizeof(*coreset->tci_StatesPDCCH_ToAddList)); + int num_ssb = 0; + for (int i = 0; i < 64; i++) { + if ((ssb_bitmap >> (63 - i)) & 0x01) { + NR_TCI_StateId_t *tci = calloc(1, sizeof(*tci)); + *tci = num_ssb++; + asn1cSeqAdd(&coreset->tci_StatesPDCCH_ToAddList->list, tci); } } coreset->tci_StatesPDCCH_ToReleaseList = NULL; From 8d857ab7948ccd1f0b87527912601a5349c8401c Mon Sep 17 00:00:00 2001 From: francescomani Date: Fri, 9 May 2025 19:48:49 +0200 Subject: [PATCH 2/4] handling beam switching via TCI state indication MAC CE --- .../LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 27 ++++++++++------ .../NR_MAC_gNB/gNB_scheduler_primitives.c | 32 ++++++++++++++++--- .../LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c | 17 +++++++++- openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h | 15 ++++----- 4 files changed, 68 insertions(+), 23 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index 782721c4e2..c45bc585e4 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -71,8 +71,7 @@ int get_dl_tda(const gNB_MAC_INST *nrmac, int slot) return 0; // if FDD or not mixed slot in TDD, for now use default TDA } -// Compute and write all MAC CEs and subheaders, and return number of written -// bytes +// Compute and write all MAC CEs and subheaders, and return number of written bytes int nr_write_ce_dlsch_pdu(module_id_t module_idP, const NR_UE_sched_ctrl_t *ue_sched_ctl, unsigned char *mac_pdu, @@ -151,19 +150,20 @@ int nr_write_ce_dlsch_pdu(module_id_t module_idP, } //TS 38.321 Sec 6.1.3.15 TCI State indication for UE Specific PDCCH MAC CE SubPDU generation - if (ue_sched_ctl->UE_mac_ce_ctrl.pdcch_state_ind.is_scheduled) { + if (ue_sched_ctl->UE_mac_ce_ctrl.tci_state_ind.is_scheduled) { //filling subheader mac_pdu_ptr->R = 0; mac_pdu_ptr->LCID = DL_SCH_LCID_TCI_STATE_IND_UE_SPEC_PDCCH; mac_pdu_ptr++; //Creating the instance of CE structure - NR_TCI_PDCCH nr_UESpec_TCI_StateInd_PDCCH; - //filling the CE structre - nr_UESpec_TCI_StateInd_PDCCH.CoresetId1 = ((ue_sched_ctl->UE_mac_ce_ctrl.pdcch_state_ind.coresetId) & 0xF) >> 1; //extracting MSB 3 bits from LS nibble - nr_UESpec_TCI_StateInd_PDCCH.ServingCellId = (ue_sched_ctl->UE_mac_ce_ctrl.pdcch_state_ind.servingCellId) & 0x1F; //extracting LSB 5 Bits - nr_UESpec_TCI_StateInd_PDCCH.TciStateId = (ue_sched_ctl->UE_mac_ce_ctrl.pdcch_state_ind.tciStateId) & 0x7F; //extracting LSB 7 bits - nr_UESpec_TCI_StateInd_PDCCH.CoresetId2 = (ue_sched_ctl->UE_mac_ce_ctrl.pdcch_state_ind.coresetId) & 0x1; //extracting LSB 1 bit - LOG_D(NR_MAC, "NR MAC CE TCI state indication for UE Specific PDCCH = %d \n", nr_UESpec_TCI_StateInd_PDCCH.TciStateId); + const tciStateInd_t *tcisi = &ue_sched_ctl->UE_mac_ce_ctrl.tci_state_ind; + NR_TCI_PDCCH nr_UESpec_TCI_StateInd_PDCCH = { + .CoresetId1 = (tcisi->coresetId & 0xF) >> 1, + .ServingCellId = 0, // TODO this is likely ServingCellIndex, see 38.331 + .TciStateId = tcisi->tciStateId & 0x7F, + .CoresetId2 = tcisi->coresetId & 0x1, + }; + LOG_I(NR_MAC, "NR MAC CE TCI state indication for UE Specific PDCCH = %d \n", nr_UESpec_TCI_StateInd_PDCCH.TciStateId); mac_ce_size = sizeof(NR_TCI_PDCCH); // Copying bytes for MAC CEs to the mac pdu pointer memcpy((void *) mac_pdu_ptr, (void *)&nr_UESpec_TCI_StateInd_PDCCH, mac_ce_size); @@ -1254,6 +1254,8 @@ void post_process_dlsch(gNB_MAC_INST *nr_mac, post_process_pdsch_t *pdsch, NR_UE nr_mac->mac_stats.dl.used_prb_aggregate += sched_pdsch->rbSize; } else { /* initial transmission */ LOG_D(NR_MAC, "Initial HARQ transmission in %d.%d\n", frame, slot); + // Flag HARQ process to start TCI timer at ACK + harq->start_tci_timer = sched_ctrl->UE_mac_ce_ctrl.tci_state_ind.is_scheduled; uint8_t *buf = allocate_transportBlock_buffer(&harq->transportBlock, TBS); /* first, write all CEs that might be there */ int written = nr_write_ce_dlsch_pdu(module_id, @@ -1373,6 +1375,11 @@ void post_process_dlsch(gNB_MAC_INST *nr_mac, post_process_pdsch_t *pdsch, NR_UE * retransmissions */ harq->sched_pdsch.time_domain_allocation = sched_pdsch->time_domain_allocation; + // reset TCI state + if (sched_ctrl->UE_mac_ce_ctrl.tci_state_ind.is_scheduled) + sched_ctrl->UE_mac_ce_ctrl.tci_state_ind.is_scheduled = false; + + // ta command is sent, values are reset if (sched_ctrl->ta_apply) { sched_ctrl->ta_apply = false; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 4a7213ab34..7fcfb8dc74 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -3573,6 +3573,14 @@ void nr_mac_release_ue(gNB_MAC_INST *mac, int rnti) mac_remove_nr_ue(mac, rnti); } +static void beam_switching_procedure(NR_UE_info_t *UE) +{ + NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl; + int new_beam_index = sched_ctrl->UE_mac_ce_ctrl.tci_state_ind.tciStateId; + LOG_I(NR_MAC, "[UE %x] Switching to beam with ID %d (from %d)\n", UE->rnti, new_beam_index, UE->UE_beam_index); + UE->UE_beam_index = new_beam_index; +} + void nr_mac_update_timers(module_id_t module_id, frame_t frame, slot_t slot) { gNB_MAC_INST *mac = RC.nrmac[module_id]; @@ -3611,6 +3619,10 @@ void nr_mac_update_timers(module_id_t module_id, frame_t frame, slot_t slot) LOG_W(NR_MAC, "UE %04x UL failure after transmission timeout\n", UE->rnti); nr_mac_trigger_ul_failure(sched_ctrl, UE->current_DL_BWP.scs); } + if (nr_timer_tick(&sched_ctrl->tci_beam_switch)) { + nr_timer_stop(&sched_ctrl->tci_beam_switch); + beam_switching_procedure(UE); + } } } @@ -3742,14 +3754,26 @@ void beam_selection_procedures(gNB_MAC_INST *mac, NR_UE_info_t *UE) // do not perform beam procedures if there is no beam information if (mac->beam_info.beam_mode == NO_BEAM_MODE) return; - RSRP_report_t *rsrp_report = &UE->UE_sched_ctrl.CSI_report.ssb_rsrp_report; + NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl; + tciStateInd_t *tci = &sched_ctrl->UE_mac_ce_ctrl.tci_state_ind; + RSRP_report_t *rsrp_report = &sched_ctrl->CSI_report.ssb_rsrp_report; // simple beam switching algorithm -> we select beam with highest RSRP from CSI report int new_bf_index = get_beam_from_ssbidx(mac, rsrp_report->resource_id[0]); - if (UE->UE_beam_index == new_bf_index) + if (UE->UE_beam_index == new_bf_index) { + if (tci->is_scheduled) { + LOG_I(NR_MAC, "[UE %x] Stopping procedure to switch beam, old beam reported as best again\n", UE->rnti); + tci->is_scheduled = false; + } return; // no beam change needed + } - LOG_I(NR_MAC, "[UE %x] Switching to beam with ID %d (SSB number %d)\n", UE->rnti, new_bf_index, rsrp_report->resource_id[0]); - UE->UE_beam_index = new_bf_index; + LOG_I(NR_MAC, "[UE %x] Starting procedure to switch beam\n", UE->rnti); + // Start procedure to switch beam via + // TCI State Indication for UE-specific PDCCH + + tci->is_scheduled = true; + tci->coresetId = sched_ctrl->coreset->controlResourceSetId; + tci->tciStateId = new_bf_index; // assumption: this correspond to the TCI index } void send_initial_ul_rrc_message(int rnti, const uint8_t *sdu, sdu_size_t sdu_len, void *data) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c index bc29981928..d2fb50c92d 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c @@ -935,6 +935,13 @@ void handle_nr_uci_pucch_0_1(module_id_t mod_id, frame_t frame, slot_t slot, con LOG_D(NR_MAC,"%4d.%2d bit %d pid %d ack/nack %d\n",frame, slot, harq_bit,pid,harq_value); nr_mac_update_pdcch_closed_loop_adjust(sched_ctrl, harq_confidence != 0); bool success = harq_value == 0 && harq_confidence == 0; + // TCI state switch occurs at the first slot that is after slot n_+ T_HARQ + 3N_sf_slot (8.10.3 of 38.133) + if (success && harq->start_tci_timer) { + int slots = 3 * nrmac->frame_structure.numb_slots_frame / 10; + nr_timer_setup(&sched_ctrl->tci_beam_switch, slots, 1); + nr_timer_start(&sched_ctrl->tci_beam_switch); + harq->start_tci_timer = false; + } handle_dl_harq(nrmac, UE, pid, success, nrmac->dl_bler.harq_round_max); if (is_ra) { bool ue_rejected = nr_check_Msg4_MsgB_Ack(mod_id, frame, slot, UE, success); @@ -1009,7 +1016,15 @@ void handle_nr_uci_pucch_2_3_4(module_id_t mod_id, frame_t frame, slot_t slot, c 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, acknack); - handle_dl_harq(nrmac, UE, pid, uci_234->harq.harq_crc != 1 && acknack, nrmac->dl_bler.harq_round_max); + // TCI state switch occurs at the first slot that is after slot n_+ T_HARQ + 3N_sf_slot (8.10.3 of 38.133) + bool success = uci_234->harq.harq_crc != 1 && acknack; + if (success && harq->start_tci_timer) { + int slots = 3 * nrmac->frame_structure.numb_slots_frame / 10; + nr_timer_setup(&sched_ctrl->tci_beam_switch, slots, 1); + nr_timer_start(&sched_ctrl->tci_beam_switch); + harq->start_tci_timer = false; + } + handle_dl_harq(nrmac, UE, pid, success, nrmac->dl_bler.harq_round_max); } free(uci_234->harq.harq_payload); } diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index 5914c74a45..de5e0e1990 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -351,13 +351,11 @@ typedef struct csi_rs_im { uint8_t tci_state_id [ MAX_CSI_RESOURCE_SET ]; } csi_rs_im_t; -typedef struct pdcchStateInd { +typedef struct tciStateInd { bool is_scheduled; - uint8_t servingCellId; - uint8_t coresetId; - uint8_t tciStateId; - bool tci_present_inDCI; -} pdcchStateInd_t; + uint32_t coresetId; + uint32_t tciStateId; +} tciStateInd_t; typedef struct pucchSpatialRelation { bool is_scheduled; @@ -396,7 +394,7 @@ typedef struct pdschTciStatesActDeact { typedef struct UE_info { sp_zp_csirs_t sp_zp_csi_rs; csi_rs_im_t csi_im; - pdcchStateInd_t pdcch_state_ind; + tciStateInd_t tci_state_ind; pucchSpatialRelation_t pucch_spatial_relation; SPCSIReportingpucch_t SP_CSI_reporting_pucch; aperiodicCSI_triggerStateSelection_t aperi_CSI_trigger; @@ -516,7 +514,7 @@ typedef struct NR_UE_harq { /* Transport block to be sent using this HARQ process */ byte_array_t transportBlock; uint32_t tb_size; // size of currently stored TB - + bool start_tci_timer; /// sched_pdsch keeps information on MCS etc used for the initial transmission NR_sched_pdsch_t sched_pdsch; } NR_UE_harq_t; @@ -695,6 +693,7 @@ typedef struct { /// Timer for RRC processing procedures and transmission activity NR_timer_t transm_interrupt; + NR_timer_t tci_beam_switch; /// Timer for timeout before UE is set to UL failure (e.g., /// "TransmissionActionIndicator" handling From 7f1974b0f60bf99fe3c651c4a976e43e6e37b922 Mon Sep 17 00:00:00 2001 From: francescomani Date: Sun, 11 May 2025 09:37:00 +0200 Subject: [PATCH 3/4] schedule DLSCH immediatly if there is a MAC-CE for TCI indication to send --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index c45bc585e4..5dd7e89fec 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -609,6 +609,19 @@ static void ack_reconfig(gNB_MAC_INST *mac, NR_UE_info_t *UE) configure_UE_BWP(mac, scc, UE, false, NR_SearchSpace__searchSpaceType_PR_common, -1, -1); } +static bool dlsch_to_schedule(const NR_UE_sched_ctrl_t *sched_ctrl, int frame) +{ + /* Check DL buffer, TA to be sent and beam switch needed*/ + if (sched_ctrl->num_total_bytes > 0) + return true; + if (sched_ctrl->ta_apply == true) + return true; + if (sched_ctrl->UE_mac_ce_ctrl.tci_state_ind.is_scheduled) + return true; + // if none of the condition for dlsch to be scheduled are met + return false; +} + typedef struct UEsched_s { float coef; NR_UE_info_t * UE; @@ -705,8 +718,7 @@ static void pf_dl(gNB_MAC_INST *mac, update_dlsch_buffer(pp_pdsch->frame, pp_pdsch->slot, UE); - /* Check DL buffer and skip this UE if no bytes and no TA necessary */ - if (sched_ctrl->num_total_bytes == 0 && sched_ctrl->ta_apply == false) + if (!dlsch_to_schedule(sched_ctrl, frame)) continue; /* Calculate coeff */ From d2fec8241135eae0016279432a17545461e64fab Mon Sep 17 00:00:00 2001 From: francescomani Date: Mon, 10 Nov 2025 15:05:41 +0100 Subject: [PATCH 4/4] making TCI configuration optional --- openair2/GNB_APP/gnb_config.c | 2 + openair2/GNB_APP/gnb_paramdef.h | 50 +++++++------- .../NR_MAC_gNB/gNB_scheduler_primitives.c | 19 ++++-- openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h | 1 + openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c | 67 ++++++++++--------- 5 files changed, 79 insertions(+), 60 deletions(-) diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index 36ee2e4f0c..59787b2e4f 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -699,6 +699,7 @@ void RCconfig_verify(configmodule_interface_t *cfg, ngran_node_t node_type) verify_gnb_param_notset(gnbp, GNB_PDSCH_ANTENNAPORTS_XP_IDX, GNB_CONFIG_STRING_PDSCHANTENNAPORTS_XP); verify_gnb_param_notset(gnbp, GNB_PUSCH_ANTENNAPORTS_IDX, GNB_CONFIG_STRING_PUSCHANTENNAPORTS); verify_gnb_param_notset(gnbp, GNB_MINRXTXTIME_IDX, GNB_CONFIG_STRING_MINRXTXTIME); + verify_gnb_param_notset(gnbp, GNB_DO_TCI_IDX, GNB_CONFIG_STRING_DOTCI); verify_gnb_param_notset(gnbp, GNB_DO_CSIRS_IDX, GNB_CONFIG_STRING_DOCSIRS); verify_gnb_param_notset(gnbp, GNB_DO_SRS_IDX, GNB_CONFIG_STRING_DOSRS); verify_gnb_param_notset(gnbp, GNB_FORCE256QAMOFF_IDX, GNB_CONFIG_STRING_FORCE256QAMOFF); @@ -1435,6 +1436,7 @@ void RCconfig_nr_macrlc(configmodule_interface_t *cfg) } config.minRXTXTIME = *GNBParamList.paramarray[0][GNB_MINRXTXTIME_IDX].iptr; LOG_I(GNB_APP, "minTXRXTIME %d\n", config.minRXTXTIME); + config.do_TCI = *GNBParamList.paramarray[0][GNB_DO_TCI_IDX].iptr; config.do_CSIRS = *GNBParamList.paramarray[0][GNB_DO_CSIRS_IDX].iptr; config.do_SRS = *GNBParamList.paramarray[0][GNB_DO_SRS_IDX].iptr; config.max_num_rsrp = *GNBParamList.paramarray[0][GNB_LIMIT_RSRP_REPORT_IDX].iptr; diff --git a/openair2/GNB_APP/gnb_paramdef.h b/openair2/GNB_APP/gnb_paramdef.h index 14cd6ba3c8..eb90b2f1bf 100644 --- a/openair2/GNB_APP/gnb_paramdef.h +++ b/openair2/GNB_APP/gnb_paramdef.h @@ -114,6 +114,7 @@ typedef enum { #define GNB_CONFIG_STRING_PDSCHANTENNAPORTS_N2 "pdsch_AntennaPorts_N2" #define GNB_CONFIG_STRING_PDSCHANTENNAPORTS_XP "pdsch_AntennaPorts_XP" #define GNB_CONFIG_STRING_PUSCHANTENNAPORTS "pusch_AntennaPorts" +#define GNB_CONFIG_STRING_DOTCI "do_TCI" #define GNB_CONFIG_STRING_DOCSIRS "do_CSIRS" #define GNB_CONFIG_STRING_DOSRS "do_SRS" #define GNB_CONFIG_STRING_NRCELLID "nr_cellid" @@ -178,6 +179,7 @@ typedef enum { {GNB_CONFIG_STRING_PDSCHANTENNAPORTS_N2, "vert. log. antenna ports", 0, .iptr=NULL, .defintval=1, TYPE_INT, 0}, \ {GNB_CONFIG_STRING_PDSCHANTENNAPORTS_XP, "XP log. antenna ports", 0, .iptr=NULL, .defintval=1, TYPE_INT, 0}, \ {GNB_CONFIG_STRING_PUSCHANTENNAPORTS, NULL, 0, .iptr=NULL, .defintval=1, TYPE_INT, 0}, \ +{GNB_CONFIG_STRING_DOTCI, NULL, 0, .iptr=NULL, .defintval=0, TYPE_INT, 0}, \ {GNB_CONFIG_STRING_DOCSIRS, NULL, 0, .iptr=NULL, .defintval=0, TYPE_INT, 0}, \ {GNB_CONFIG_STRING_DOSRS, NULL, 0, .iptr=NULL, .defintval=0, TYPE_INT, 0}, \ {GNB_CONFIG_STRING_NRCELLID, NULL, 0, .u64ptr=NULL,.defint64val=1, TYPE_UINT64, 0}, \ @@ -223,29 +225,30 @@ typedef enum { #define GNB_PDSCH_ANTENNAPORTS_N2_IDX 14 #define GNB_PDSCH_ANTENNAPORTS_XP_IDX 15 #define GNB_PUSCH_ANTENNAPORTS_IDX 16 -#define GNB_DO_CSIRS_IDX 17 -#define GNB_DO_SRS_IDX 18 -#define GNB_NRCELLID_IDX 19 -#define GNB_MINRXTXTIME_IDX 20 -#define GNB_ULPRBBLACKLIST_IDX 21 -#define GNB_UMONDEFAULTDRB_IDX 22 -#define GNB_FORCE256QAMOFF_IDX 23 -#define GNB_ENABLE_SDAP_IDX 24 -#define GNB_DRBS 25 -#define GNB_GNB_DU_ID_IDX 26 -#define GNB_GNB_CU_UP_ID_IDX 27 -#define GNB_USE_DELTA_MCS_IDX 28 -#define GNB_FORCEUL256QAMOFF_IDX 29 -#define GNB_MAXMIMOLAYERS_IDX 30 -#define GNB_DISABLE_HARQ_IDX 31 -#define GNB_NUM_DL_HARQ_IDX 32 -#define GNB_NUM_UL_HARQ_IDX 33 -#define GNB_UESS_AGG_LEVEL_LIST_IDX 34 -#define GNB_CU_SIBS_IDX 35 -#define GNB_DU_SIBS_IDX 36 -#define GNB_CONFIG_REP_IDX 37 -#define GNB_1ST_ACTIVE_BWP_IDX 38 -#define GNB_LIMIT_RSRP_REPORT_IDX 39 +#define GNB_DO_TCI_IDX 17 +#define GNB_DO_CSIRS_IDX 18 +#define GNB_DO_SRS_IDX 19 +#define GNB_NRCELLID_IDX 20 +#define GNB_MINRXTXTIME_IDX 21 +#define GNB_ULPRBBLACKLIST_IDX 22 +#define GNB_UMONDEFAULTDRB_IDX 23 +#define GNB_FORCE256QAMOFF_IDX 24 +#define GNB_ENABLE_SDAP_IDX 25 +#define GNB_DRBS 26 +#define GNB_GNB_DU_ID_IDX 27 +#define GNB_GNB_CU_UP_ID_IDX 28 +#define GNB_USE_DELTA_MCS_IDX 29 +#define GNB_FORCEUL256QAMOFF_IDX 30 +#define GNB_MAXMIMOLAYERS_IDX 31 +#define GNB_DISABLE_HARQ_IDX 32 +#define GNB_NUM_DL_HARQ_IDX 33 +#define GNB_NUM_UL_HARQ_IDX 34 +#define GNB_UESS_AGG_LEVEL_LIST_IDX 35 +#define GNB_CU_SIBS_IDX 36 +#define GNB_DU_SIBS_IDX 37 +#define GNB_CONFIG_REP_IDX 38 +#define GNB_1ST_ACTIVE_BWP_IDX 39 +#define GNB_LIMIT_RSRP_REPORT_IDX 40 #define TRACKING_AREA_CODE_OKRANGE {0x0001,0xFFFD} #define NUM_DL_HARQ_OKVALUES {2,4,6,8,10,12,16,32} @@ -284,6 +287,7 @@ typedef enum { { .s5 = { NULL } }, \ { .s5 = { NULL } }, \ { .s5 = { NULL } }, \ + { .s5 = { NULL } }, \ { .s1 = { config_check_intval, NUM_DL_HARQ_OKVALUES,8 } }, \ { .s1 = { config_check_intval, NUM_UL_HARQ_OKVALUES,2 } }, \ { .s5 = { NULL } }, \ diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 7fcfb8dc74..921d85033f 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -3573,10 +3573,8 @@ void nr_mac_release_ue(gNB_MAC_INST *mac, int rnti) mac_remove_nr_ue(mac, rnti); } -static void beam_switching_procedure(NR_UE_info_t *UE) +static void beam_switching_procedure(NR_UE_info_t *UE, int new_beam_index) { - NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl; - int new_beam_index = sched_ctrl->UE_mac_ce_ctrl.tci_state_ind.tciStateId; LOG_I(NR_MAC, "[UE %x] Switching to beam with ID %d (from %d)\n", UE->rnti, new_beam_index, UE->UE_beam_index); UE->UE_beam_index = new_beam_index; } @@ -3621,7 +3619,7 @@ void nr_mac_update_timers(module_id_t module_id, frame_t frame, slot_t slot) } if (nr_timer_tick(&sched_ctrl->tci_beam_switch)) { nr_timer_stop(&sched_ctrl->tci_beam_switch); - beam_switching_procedure(UE); + beam_switching_procedure(UE, sched_ctrl->UE_mac_ce_ctrl.tci_state_ind.tciStateId); } } } @@ -3754,11 +3752,18 @@ void beam_selection_procedures(gNB_MAC_INST *mac, NR_UE_info_t *UE) // do not perform beam procedures if there is no beam information if (mac->beam_info.beam_mode == NO_BEAM_MODE) return; - NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl; - tciStateInd_t *tci = &sched_ctrl->UE_mac_ce_ctrl.tci_state_ind; - RSRP_report_t *rsrp_report = &sched_ctrl->CSI_report.ssb_rsrp_report; + // simple beam switching algorithm -> we select beam with highest RSRP from CSI report + NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl; + RSRP_report_t *rsrp_report = &sched_ctrl->CSI_report.ssb_rsrp_report; int new_bf_index = get_beam_from_ssbidx(mac, rsrp_report->resource_id[0]); + if (!mac->radio_config.do_TCI) { // if not TCI is configure we switch beam directly + if (UE->UE_beam_index != new_bf_index) + beam_switching_procedure(UE, new_bf_index); + return; + } + + tciStateInd_t *tci = &sched_ctrl->UE_mac_ce_ctrl.tci_state_ind; if (UE->UE_beam_index == new_bf_index) { if (tci->is_scheduled) { LOG_I(NR_MAC, "[UE %x] Stopping procedure to switch beam, old beam reported as best again\n", UE->rnti); diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index de5e0e1990..101aba3de2 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -191,6 +191,7 @@ typedef struct nr_mac_config_t { int minRXTXTIME; int do_CSIRS; int do_SRS; + int do_TCI; int max_num_rsrp; bool force_256qam_off; bool force_UL256qam_off; diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c b/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c index cfb38cf53a..1caa20acdc 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c +++ b/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c @@ -273,7 +273,7 @@ NR_SearchSpace_t *rrc_searchspace_config(bool is_common, return ss; } -static NR_ControlResourceSet_t *get_coreset_config(int bwp_id, int bwp_start, int bwp_size, uint64_t ssb_bitmap) +static NR_ControlResourceSet_t *get_coreset_config(int bwp_id, int bwp_start, int bwp_size, uint64_t ssb_bitmap, bool doTCI) { NR_ControlResourceSet_t *coreset = calloc(1, sizeof(*coreset)); AssertFatal(coreset != NULL, "out of memory\n"); @@ -296,13 +296,15 @@ static NR_ControlResourceSet_t *get_coreset_config(int bwp_id, int bwp_start, in // The ID space is used across the BWPs of a Serving Cell as per 38.331 coreset->controlResourceSetId = bwp_id + 1; - coreset->tci_StatesPDCCH_ToAddList = calloc(1,sizeof(*coreset->tci_StatesPDCCH_ToAddList)); - int num_ssb = 0; - for (int i = 0; i < 64; i++) { - if ((ssb_bitmap >> (63 - i)) & 0x01) { - NR_TCI_StateId_t *tci = calloc(1, sizeof(*tci)); - *tci = num_ssb++; - asn1cSeqAdd(&coreset->tci_StatesPDCCH_ToAddList->list, tci); + if (doTCI) { + coreset->tci_StatesPDCCH_ToAddList = calloc(1,sizeof(*coreset->tci_StatesPDCCH_ToAddList)); + int num_ssb = 0; + for (int i = 0; i < 64; i++) { + if ((ssb_bitmap >> (63 - i)) & 0x01) { + NR_TCI_StateId_t *tci = calloc(1, sizeof(*tci)); + *tci = num_ssb++; + asn1cSeqAdd(&coreset->tci_StatesPDCCH_ToAddList->list, tci); + } } } coreset->tci_StatesPDCCH_ToReleaseList = NULL; @@ -1672,7 +1674,11 @@ static NR_PTRS_DownlinkConfig_t *config_dlptrs(const nr_ptrs_config_t *ptrs) return ptrs_config; } -static NR_SetupRelease_PDSCH_Config_t *config_pdsch(uint64_t ssb_bitmap, int bwp_Id, int dl_antenna_ports, nr_ptrs_config_t *ptrs) +static NR_SetupRelease_PDSCH_Config_t *config_pdsch(uint64_t ssb_bitmap, + int bwp_Id, + int dl_antenna_ports, + bool do_TCI, + nr_ptrs_config_t *ptrs) { NR_SetupRelease_PDSCH_Config_t *setup_pdsch_Config = calloc(1,sizeof(*setup_pdsch_Config)); setup_pdsch_Config->present = NR_SetupRelease_PDSCH_Config_PR_setup; @@ -1705,19 +1711,21 @@ static NR_SetupRelease_PDSCH_Config_t *config_pdsch(uint64_t ssb_bitmap, int bwp pdsch_Config->prb_BundlingType.choice.staticBundling->bundleSize = calloc(1, sizeof(*pdsch_Config->prb_BundlingType.choice.staticBundling->bundleSize)); *pdsch_Config->prb_BundlingType.choice.staticBundling->bundleSize = NR_PDSCH_Config__prb_BundlingType__staticBundling__bundleSize_wideband; - int n_ssb = 0; - if (!pdsch_Config->tci_StatesToAddModList) - pdsch_Config->tci_StatesToAddModList=calloc(1,sizeof(*pdsch_Config->tci_StatesToAddModList)); - for (int i = 0; i < 64; i++) { - if (((ssb_bitmap >> (63 - i)) & 0x01) == 0) - continue; - asn1cSequenceAdd(pdsch_Config->tci_StatesToAddModList->list, NR_TCI_State_t, tcid); - tcid->tci_StateId = n_ssb++; - tcid->qcl_Type1.cell = NULL; - asn1cCallocOne(tcid->qcl_Type1.bwp_Id, bwp_Id); - tcid->qcl_Type1.referenceSignal.present = NR_QCL_Info__referenceSignal_PR_ssb; - tcid->qcl_Type1.referenceSignal.choice.ssb = i; - tcid->qcl_Type1.qcl_Type = NR_QCL_Info__qcl_Type_typeC; + if (do_TCI) { + int n_ssb = 0; + if (!pdsch_Config->tci_StatesToAddModList) + pdsch_Config->tci_StatesToAddModList=calloc(1,sizeof(*pdsch_Config->tci_StatesToAddModList)); + for (int i = 0; i < 64; i++) { + if (((ssb_bitmap >> (63 - i)) & 0x01) == 0) + continue; + asn1cSequenceAdd(pdsch_Config->tci_StatesToAddModList->list, NR_TCI_State_t, tcid); + tcid->tci_StateId = n_ssb++; + tcid->qcl_Type1.cell = NULL; + asn1cCallocOne(tcid->qcl_Type1.bwp_Id, bwp_Id); + tcid->qcl_Type1.referenceSignal.present = NR_QCL_Info__referenceSignal_PR_ssb; + tcid->qcl_Type1.referenceSignal.choice.ssb = i; + tcid->qcl_Type1.qcl_Type = NR_QCL_Info__qcl_Type_typeC; + } } return setup_pdsch_Config; } @@ -1751,7 +1759,7 @@ static NR_BWP_Downlink_t *config_downlinkBWP(const NR_ServingCellConfigCommon_t int bwp_size = NRRIV2BW(bwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); int bwp_start = NRRIV2PRBOFFSET(bwp->bwp_Common->genericParameters.locationAndBandwidth, MAX_BWP_SIZE); uint64_t ssb_bitmap = get_ssb_bitmap(scc); - NR_ControlResourceSet_t *coreset = get_coreset_config(bwp->bwp_Id, bwp_start, bwp_size, ssb_bitmap); + NR_ControlResourceSet_t *coreset = get_coreset_config(bwp->bwp_Id, bwp_start, bwp_size, ssb_bitmap, configuration->do_TCI); bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->commonControlResourceSet = coreset; bwp->bwp_Common->pdcch_ConfigCommon->choice.setup->searchSpaceZero=NULL; @@ -1803,9 +1811,8 @@ static NR_BWP_Downlink_t *config_downlinkBWP(const NR_ServingCellConfigCommon_t bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList = calloc(1,sizeof(*bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList)); bwp->bwp_Dedicated->pdcch_Config->choice.setup->controlResourceSetToAddModList = calloc(1,sizeof(*bwp->bwp_Dedicated->pdcch_Config->choice.setup->controlResourceSetToAddModList)); - // coreset2 is identical to coreset above, but reallocated to prevent double - // frees - NR_ControlResourceSet_t *coreset2 = get_coreset_config(bwp->bwp_Id, bwp_start, bwp_size, ssb_bitmap); + // coreset2 is identical to coreset above, but reallocated to prevent double frees + NR_ControlResourceSet_t *coreset2 = get_coreset_config(bwp->bwp_Id, bwp_start, bwp_size, ssb_bitmap, configuration->do_TCI); asn1cSeqAdd(&bwp->bwp_Dedicated->pdcch_Config->choice.setup->controlResourceSetToAddModList->list, coreset2); int rrc_num_agg_level_candidates[NUM_PDCCH_AGG_LEVELS]; int num_cces = get_coreset_num_cces(coreset2->frequencyDomainResources.buf, coreset2->duration); @@ -1821,7 +1828,7 @@ static NR_BWP_Downlink_t *config_downlinkBWP(const NR_ServingCellConfigCommon_t asn1cSeqAdd(&bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToAddModList->list, ss3); bwp->bwp_Dedicated->pdcch_Config->choice.setup->searchSpacesToReleaseList = NULL; - bwp->bwp_Dedicated->pdsch_Config = config_pdsch(ssb_bitmap, bwp->bwp_Id, dl_antenna_ports, configuration->ptrs); + bwp->bwp_Dedicated->pdsch_Config = config_pdsch(ssb_bitmap, bwp->bwp_Id, dl_antenna_ports, configuration->do_TCI, configuration->ptrs); set_dl_mcs_table(bwp->bwp_Common->genericParameters.subcarrierSpacing, force_256qam_off ? NULL : uecap, @@ -3336,7 +3343,7 @@ static NR_BWP_DownlinkDedicated_t *configure_initial_dl_bwp(const NR_ServingCell NR_BWP_t *genericParameters = &scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters; int bwp_size = NRRIV2BW(genericParameters->locationAndBandwidth, MAX_BWP_SIZE); int bwp_start = NRRIV2PRBOFFSET(genericParameters->locationAndBandwidth, MAX_BWP_SIZE); - NR_ControlResourceSet_t *coreset = get_coreset_config(0, bwp_start, bwp_size, bitmap); + NR_ControlResourceSet_t *coreset = get_coreset_config(0, bwp_start, bwp_size, bitmap, configuration->do_TCI); asn1cSeqAdd(&pdcch_Config->controlResourceSetToAddModList->list, coreset); int css_num_agg_level_candidates[NUM_PDCCH_AGG_LEVELS]; @@ -3356,7 +3363,7 @@ static NR_BWP_DownlinkDedicated_t *configure_initial_dl_bwp(const NR_ServingCell asn1cSeqAdd(&pdcch_Config->searchSpacesToAddModList->list, ss2); bwp_Dedicated->pdcch_Config->choice.setup = pdcch_Config; - bwp_Dedicated->pdsch_Config = config_pdsch(bitmap, 0, pdsch_AntennaPorts, configuration->ptrs); + bwp_Dedicated->pdsch_Config = config_pdsch(bitmap, 0, pdsch_AntennaPorts, configuration->do_TCI, configuration->ptrs); // we might call configuration of initial BWP for BWP switch when we already have UE capabilities set_dl_mcs_table(scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.subcarrierSpacing, configuration->force_256qam_off ? NULL : uecap, @@ -4015,7 +4022,7 @@ NR_CellGroupConfig_t *get_default_secondaryCellGroup(const NR_ServingCellConfigC configDedicated->initialDownlinkBWP = calloc(1, sizeof(*configDedicated->initialDownlinkBWP)); configDedicated->initialDownlinkBWP->pdcch_Config = NULL; configDedicated->initialDownlinkBWP->pdsch_Config = - config_pdsch(bitmap, 0, dl_antenna_ports, configuration->ptrs); + config_pdsch(bitmap, 0, dl_antenna_ports, configuration->do_TCI, configuration->ptrs); configDedicated->initialDownlinkBWP->sps_Config = NULL; // calloc(1,sizeof(struct NR_SetupRelease_SPS_Config)); configDedicated->initialDownlinkBWP->radioLinkMonitoringConfig = NULL;