This commit is contained in:
2026-01-08 09:17:26 +00:00
parent 85fef10860
commit cf4ca86169
35 changed files with 2493 additions and 118 deletions
@@ -433,14 +433,14 @@ static bool allocate_dl_retransmission(gNB_MAC_INST *nr_mac,
NR_UE_DL_BWP_t *dl_bwp = &UE->current_DL_BWP;
NR_UE_UL_BWP_t *ul_bwp = &UE->current_UL_BWP;
NR_sched_pdsch_t new_sched = sched_ctrl->harq_processes[current_harq_pid].sched_pdsch;
int layers = get_dl_nrOfLayers(sched_ctrl, dl_bwp->dci_format);
int pm_index = get_pm_index(nr_mac, UE, dl_bwp->dci_format, layers, nr_mac->radio_config.pdsch_AntennaPorts.XP);
int layers = get_dl_nrOfLayers(nr_mac, sched_ctrl, dl_bwp->dci_format);
// If the RI changed between current rtx and a previous transmission
// we need to verify if it is not decreased
// othwise it wouldn't be possible to transmit the same TBS
layers = (layers < new_sched.nrOfLayers) ? layers : new_sched.nrOfLayers;
pm_index = (layers < new_sched.nrOfLayers) ? pm_index : new_sched.pm_index;
// Recalculate PMI with the final layer count to ensure consistency
int pm_index = (layers < new_sched.nrOfLayers) ? get_pm_index(nr_mac, UE, dl_bwp->dci_format, layers, nr_mac->radio_config.pdsch_AntennaPorts.XP) : new_sched.pm_index;
const int coresetid = sched_ctrl->coreset->controlResourceSetId;
const int tda = get_dl_tda(nr_mac, slot);
@@ -703,7 +703,7 @@ static void pf_dl(gNB_MAC_INST *mac,
sched_ctrl->dl_bler_stats.mcs = selected_mcs;
} else
selected_mcs = get_mcs_from_bler(bo, stats, &sched_ctrl->dl_bler_stats, max_mcs, frame);
int l = get_dl_nrOfLayers(sched_ctrl, current_BWP->dci_format);
int l = get_dl_nrOfLayers(mac, sched_ctrl, current_BWP->dci_format);
const uint8_t Qm = nr_get_Qm_dl(selected_mcs, current_BWP->mcsTableIdx);
const uint16_t R = nr_get_code_rate_dl(selected_mcs, current_BWP->mcsTableIdx);
uint32_t tbs = nr_compute_tbs(Qm,
@@ -848,7 +848,7 @@ static void pf_dl(gNB_MAC_INST *mac,
sched_ctrl->cce_index = CCEIndex;
fill_pdcch_vrb_map(mac, CC_id, &sched_ctrl->sched_pdcch, CCEIndex, sched_ctrl->aggregation_level, beam.idx);
int l = get_dl_nrOfLayers(sched_ctrl, dl_bwp->dci_format);
int l = get_dl_nrOfLayers(mac, sched_ctrl, dl_bwp->dci_format);
NR_sched_pdsch_t sched_pdsch = {
// rbSize below
.rbStart = rbStart,
@@ -906,6 +906,32 @@ static void nr_dlsch_preprocessor(gNB_MAC_INST *mac, post_process_pdsch_t *pp_pd
NR_ServingCellConfigCommon_t *scc = mac->common_channels[0].ServingCellConfigCommon;
int bw = scc->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth;
// O-RAN MPLANE-TRX-CONTROL: Scale available resources based on active antennas
// This simulates MIMO capacity reduction when antennas are muted
if (mac->antenna_state) {
int active_tx = nr_get_active_tx_antenna_count(mac->antenna_state);
int configured_tx = mac->antenna_state->num_tx_antennas;
// If all TX antennas are muted, skip DL user scheduling entirely
// (SSB/SIB1 are still transmitted by PHY but no user data)
if (active_tx == 0) {
static int mute_log_counter = 0;
if (mute_log_counter++ % 10000 == 0)
LOG_W(NR_MAC, "[ANTENNA_MUTE] All TX antennas muted, skipping DL user scheduling\n");
return;
}
if (active_tx < configured_tx && configured_tx > 0) {
// Scale bandwidth proportionally to active antenna ratio
int scaled_bw = (bw * active_tx) / configured_tx;
if (scaled_bw < 5) scaled_bw = 5; // Minimum for SSB
static int log_counter = 0;
if (log_counter++ % 1000 == 0) // Log every 1000 calls to avoid spam
LOG_I(NR_MAC, "[ANTENNA_MUTE] Scaling DL capacity: %d/%d antennas -> %d/%d PRBs\n",
active_tx, configured_tx, scaled_bw, bw);
bw = scaled_bw;
}
}
int num_beams = mac->beam_info.beam_allocation ? mac->beam_info.beams_per_period : 1;
int n_rb_sched[num_beams];
for (int i = 0; i < num_beams; i++)
@@ -999,7 +1025,22 @@ nfapi_nr_dl_tti_pdsch_pdu_rel15_t *prepare_pdsch_pdu(nfapi_nr_dl_tti_request_pdu
pdsch_pdu->precodingAndBeamforming.num_prgs = 1;
pdsch_pdu->precodingAndBeamforming.prg_size = pdsch_pdu->rbSize;
pdsch_pdu->precodingAndBeamforming.dig_bf_interfaces = 1;
pdsch_pdu->precodingAndBeamforming.prgs_list[0].pm_idx = sched_pdsch->pm_index;
// Defensive check: if pm_index > 0, verify it's in the valid range for nrOfLayers
// to prevent PHY crash from layer/PMI mismatch
int pm_idx = sched_pdsch->pm_index;
if (pm_idx > 0 && mac) {
int layers = sched_pdsch->nrOfLayers;
int prev_layers_size = 0;
for (int i = 1; i < layers; i++)
prev_layers_size += mac->precoding_matrix_size[i - 1];
int max_valid = prev_layers_size + mac->precoding_matrix_size[layers - 1];
if (pm_idx < 1 + prev_layers_size || pm_idx > max_valid) {
LOG_E(NR_MAC, "PM index %d invalid for %d layers (valid range: %d-%d), forcing identity matrix\n",
pm_idx, layers, 1 + prev_layers_size, max_valid);
pm_idx = 0; // Use identity matrix to prevent crash
}
}
pdsch_pdu->precodingAndBeamforming.prgs_list[0].pm_idx = pm_idx;
pdsch_pdu->precodingAndBeamforming.prgs_list[0].dig_bf_interface_list[0].beam_idx = beam_index;
return pdsch_pdu;
}
@@ -53,6 +53,9 @@
#include "SIMULATION/TOOLS/sim.h" // for taus
// Forward declaration for RLF handler defined in mac_rrc_dl_handler.c
void gnb_rlf_handler(int rnti);
#define ENABLE_MAC_PAYLOAD_DEBUG
#define DEBUG_gNB_SCHEDULER 1
@@ -127,14 +130,28 @@ static void determine_aggregation_level_search_order(int agg_level_search_order[
static int nr_mac_interrupt_ue_transmission(gNB_MAC_INST *mac, NR_UE_info_t *UE, int slots, int slots_per_frame);
uint8_t get_dl_nrOfLayers(const NR_UE_sched_ctrl_t *sched_ctrl, const nr_dci_format_t dci_format)
uint8_t get_dl_nrOfLayers(const gNB_MAC_INST *mac, const NR_UE_sched_ctrl_t *sched_ctrl, const nr_dci_format_t dci_format)
{
// TODO check this but it should be enough for now
// if there is not csi report activated RI is 0 from initialization
int requested_layers;
if(dci_format == NR_DL_DCI_FORMAT_1_0)
return 1;
requested_layers = 1;
else
return sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.ri + 1;
requested_layers = sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.ri + 1;
// O-RAN MPLANE-TRX-CONTROL: cap layers to active TX antennas
if (mac && mac->antenna_state) {
int effective = nr_get_effective_mimo_layers(mac->antenna_state, requested_layers);
// Ensure at least 1 layer (scheduler should skip if all antennas muted)
if (effective < 1) effective = 1;
if (effective < requested_layers) {
LOG_D(NR_MAC, "[ANTENNA_MUTE] Capping layers from %d to %d due to muted antennas\n",
requested_layers, effective);
}
return effective;
}
return requested_layers;
}
int get_ul_nrOfLayers(const NR_UE_sched_ctrl_t *sched_ctrl, const nr_dci_format_t dci_format)
@@ -193,7 +210,30 @@ uint16_t get_pm_index(const gNB_MAC_INST *nrmac,
{
if (dci_format == NR_DL_DCI_FORMAT_1_0 || nrmac->identity_pm || xp_pdsch_antenna_ports == 1)
return 0; //identity matrix (basic 5G configuration handled by PMI report is with XP antennas)
const NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
// The UE's PMI report (x1/x2) was calculated for RI+1 layers.
// If we're using a different number of layers, the PMI values are invalid.
// Use identity matrix in this case to avoid layer/PMI mismatch assertion.
int reported_ri_layers = sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.ri + 1;
if (layers != reported_ri_layers) {
LOG_D(NR_MAC, "PMI mismatch: using %d layers but RI report is for %d layers, using identity matrix\n",
layers, reported_ri_layers);
return 0;
}
// O-RAN MPLANE-TRX-CONTROL: When antennas are muted, the UE's PMI report
// is based on the full antenna array and may be invalid. Use identity matrix.
if (nrmac->antenna_state) {
int active_tx = nr_get_active_tx_antenna_count(nrmac->antenna_state);
int configured_tx = nrmac->antenna_state->num_tx_antennas;
if (active_tx < configured_tx) {
LOG_D(NR_MAC, "[ANTENNA_MUTE] Using identity matrix: %d/%d TX antennas active\n",
active_tx, configured_tx);
return 0; // Use identity matrix when any antennas are muted
}
}
const int report_id = sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.csi_report_id;
const nr_csi_report_t *csi_report = &UE->csi_report_template[report_id];
const int N1 = csi_report->N1;
@@ -215,9 +255,10 @@ uint16_t get_pm_index(const gNB_MAC_INST *nrmac,
// order of matrices depends on layers to be transmitted
// elements from 1 to n for 1 layer
// elements from n+1 to m for 2 layers etc.
if (antenna_ports == 2)
return 1 + prev_layers_size + x2; // 0 for identity matrix
else {
int pm_index;
if (antenna_ports == 2) {
pm_index = 1 + prev_layers_size + x2; // 0 for identity matrix
} else {
int idx = layers - 1;
// the order of i1x in X1 report needs to be verified
// the standard is not very clear (Table 6.3.1.1.2-7 in 38.212)
@@ -246,8 +287,18 @@ uint16_t get_pm_index(const gNB_MAC_INST *nrmac,
get_K1_K2(N1, N2, &K1, &K2, layers);
// computing precoding matrix index according to rule set in allocation function init_codebook_gNB
lay_index = i2 + (i11 * max_i2) + (i12 * max_i2 * N1 * O1) + (k1 * max_i2 * N1 * O1 * N2 * O2) + (k2 * max_i2 * N1 * O1 * N2 * O2 * K1);
return 1 + prev_layers_size + lay_index;
pm_index = 1 + prev_layers_size + lay_index;
}
// Bounds check: ensure pm_index is within valid range for the specified layers
// The valid range for 'layers' is [1 + prev_layers_size, 1 + prev_layers_size + precoding_matrix_size[layers-1] - 1]
int max_valid_pm = prev_layers_size + nrmac->precoding_matrix_size[layers - 1];
if (pm_index < 1 || pm_index > max_valid_pm) {
LOG_W(NR_MAC, "PM index %d out of range [1, %d] for %d layers, using identity matrix\n",
pm_index, max_valid_pm, layers);
return 0;
}
return pm_index;
}
// look-up table for AMC. Based on BLER vs SNR curves from the nr_dlsim simulation
@@ -436,16 +487,24 @@ static NR_SearchSpace_t *get_searchspace(NR_ServingCellConfigCommon_t *scc,
AssertFatal(false, "Couldn't find an adequate SearchSpace for target SearchSpace %d in BWP_DownlinkDedicated\n", target_ss);
}
// If looking for UE-specific SearchSpace but no dedicated BWP is configured,
// fall back to common SearchSpace (type 0 or 1)
NR_SearchSpace__searchSpaceType_PR fallback_ss = target_ss;
if (target_ss == NR_SearchSpace__searchSpaceType_PR_ue_Specific) {
LOG_D(NR_MAC, "UE-specific SearchSpace requested but no dedicated BWP configured, falling back to common SearchSpace\n");
fallback_ss = NR_SearchSpace__searchSpaceType_PR_common;
}
const int n = scc->downlinkConfigCommon->initialDownlinkBWP->pdcch_ConfigCommon->choice.setup->commonSearchSpaceList->list.count;
for (int i = 0; i < n; i++) {
NR_SearchSpace_t *ss =
scc->downlinkConfigCommon->initialDownlinkBWP->pdcch_ConfigCommon->choice.setup->commonSearchSpaceList->list.array[i];
if (ss->searchSpaceType->present == target_ss) {
if (ss->searchSpaceType->present == fallback_ss) {
AssertFatal(ss->controlResourceSetId, "searchSpaceId %ld has a NULL controlResourceSetId\n", ss->searchSpaceId);
return ss;
}
}
AssertFatal(false, "Couldn't find an adequate SearchSpace for target SearchSpace %d in ServingCellConfigCommon\n", target_ss);
AssertFatal(false, "Couldn't find an adequate SearchSpace for target SearchSpace %d (fallback %d) in ServingCellConfigCommon\n", target_ss, fallback_ss);
}
/// @brief Get CORESET resource block parameters from frequency domain resources bitmap.
@@ -3827,6 +3886,8 @@ bool prepare_initial_ul_rrc_message(gNB_MAC_INST *mac, NR_UE_info_t *UE)
const NR_RLC_BearerConfig_t *bearer = cellGroupConfig->rlc_BearerToAddModList->list.array[0];
DevAssert(bearer->servedRadioBearer->choice.srb_Identity == srb_id);
nr_rlc_add_srb(UE->rnti, bearer->servedRadioBearer->choice.srb_Identity, bearer);
// Register RLF handler for SRB1 to handle max retransmissions
nr_rlc_set_rlf_handler(UE->rnti, gnb_rlf_handler);
int priority = bearer->mac_LogicalChannelConfig->ul_SpecificParameters->priority;
nr_lc_config_t c = {.lcid = bearer->logicalChannelIdentity, .priority = priority};
nr_mac_add_lcid(&UE->UE_sched_ctrl, &c);
@@ -540,7 +540,7 @@ static void evaluate_sinr_report(gNB_MAC_INST *nrmac,
stats->num_sinr_meas++;
const int mcs_table = UE->current_DL_BWP.mcsTableIdx;
const int nrOfLayers = get_dl_nrOfLayers(sched_ctrl, UE->current_DL_BWP.dci_format);
const int nrOfLayers = get_dl_nrOfLayers(nrmac, sched_ctrl, UE->current_DL_BWP.dci_format);
sched_ctrl->dl_max_mcs = get_mcs_from_SINRx10(mcs_table, sinr_report->SINRx10[0], nrOfLayers);
LOG_D(MAC, "Reported SSB-SINR = %d.%d, dl_max_mcs %d\n", sinr_report->SINRx10[0] / 10, sinr_report->SINRx10[0] % 10, sched_ctrl->dl_max_mcs);
@@ -2662,6 +2662,27 @@ static void nr_ulsch_preprocessor(gNB_MAC_INST *nr_mac, post_process_pusch_t *pp
int num_beams = nr_mac->beam_info.beam_allocation ? nr_mac->beam_info.beams_per_period : 1;
int bw = scc->uplinkConfigCommon->frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth;
// O-RAN MPLANE-TRX-CONTROL: Scale UL resources based on active RX antennas
// This simulates receive diversity reduction when antennas are muted
if (nr_mac->antenna_state) {
int active_rx = nr_get_active_rx_antenna_count(nr_mac->antenna_state);
int configured_rx = nr_mac->antenna_state->num_rx_antennas;
// If all RX antennas are muted, skip UL user scheduling entirely
if (active_rx == 0) {
static int mute_log_counter = 0;
if (mute_log_counter++ % 10000 == 0)
LOG_W(NR_MAC, "[ANTENNA_MUTE] All RX antennas muted, skipping UL user scheduling\n");
return;
}
if (active_rx < configured_rx && configured_rx > 0) {
int scaled_bw = (bw * active_rx) / configured_rx;
if (scaled_bw < 5) scaled_bw = 5;
LOG_D(NR_MAC, "[ANTENNA_MUTE] Scaling UL capacity: %d/%d antennas -> %d/%d PRBs\n",
active_rx, configured_rx, scaled_bw, bw);
bw = scaled_bw;
}
}
int average_agg_level = 4; // TODO find a better estimation
int max_dci = bw / (average_agg_level * NR_NB_REG_PER_CCE);
+1 -1
View File
@@ -398,7 +398,7 @@ uint16_t get_pm_index(const gNB_MAC_INST *nrmac,
int get_mcs_from_SINRx10(int mcs_table, int SINRx10, int Nl);
uint8_t get_mcs_from_cqi(int mcs_table, int cqi_table, int cqi_idx);
uint8_t get_dl_nrOfLayers(const NR_UE_sched_ctrl_t *sched_ctrl, const nr_dci_format_t dci_format);
uint8_t get_dl_nrOfLayers(const gNB_MAC_INST *mac, const NR_UE_sched_ctrl_t *sched_ctrl, const nr_dci_format_t dci_format);
int get_ul_nrOfLayers(const NR_UE_sched_ctrl_t *sched_ctrl, const nr_dci_format_t dci_format);
void free_sched_pucch_list(NR_UE_sched_ctrl_t *sched_ctrl);
@@ -53,6 +53,34 @@ static instance_t get_f1_gtp_instance(void)
return inst->gtpInst;
}
/**
* @brief RLF handler for gNB: called when RLC max retransmissions reached
* @param rnti RNTI of the UE experiencing RLF
*/
void gnb_rlf_handler(int rnti)
{
if (RC.nrmac == NULL || RC.nrmac[0] == NULL) {
LOG_E(RLC, "UE %04x: RLF detected but MAC not initialized\n", rnti);
return;
}
gNB_MAC_INST *mac = RC.nrmac[0];
NR_UE_info_t *UE = find_nr_UE(&mac->UE_info, rnti);
if (UE == NULL) {
LOG_W(RLC, "UE %04x: RLF detected but UE not found in connected list\n", rnti);
return;
}
LOG_W(NR_MAC, "UE %04x: RLF detected on SRB1, requesting UE release\n", rnti);
// Request UE release through proper channels
bool requested = nr_mac_request_release_ue(mac, rnti);
if (!requested) {
LOG_W(NR_MAC, "UE %04x: Could not request release, releasing directly\n", rnti);
nr_mac_release_ue(mac, rnti);
}
}
static int drb_gtpu_create(instance_t instance,
uint32_t ue_id,
int incoming_id,
@@ -247,6 +275,10 @@ static int handle_ue_context_srbs_setup(NR_UE_info_t *UE,
const f1ap_srb_to_setup_t *srb = &req_srbs[i];
NR_RLC_BearerConfig_t *rlc_BearerConfig = get_bearerconfig_from_srb(srb, rlc_config);
nr_rlc_add_srb(UE->rnti, srb->id, rlc_BearerConfig);
// Register RLF handler for SRB to handle max retransmissions
if (srb->id == 1) { // SRB1
nr_rlc_set_rlf_handler(UE->rnti, gnb_rlf_handler);
}
int priority = rlc_BearerConfig->mac_LogicalChannelConfig->ul_SpecificParameters->priority;
nr_lc_config_t c = {.lcid = rlc_BearerConfig->logicalChannelIdentity, .priority = priority};
+5
View File
@@ -79,6 +79,7 @@
/* PHY */
#include "time_meas.h"
#include "PHY/NR_TRANSPORT/nr_antenna_state.h"
/* Interface */
#include "nfapi_nr_interface_scf.h"
@@ -971,6 +972,10 @@ typedef struct gNB_MAC_INST_s {
nr_mac_config_t radio_config;
nr_rlc_configuration_t rlc_config;
/// O-RAN antenna state for energy saving (MPLANE-TRX-CONTROL)
/// Points to PHY antenna state for dynamic layer adaptation
nr_antenna_state_t *antenna_state;
NR_UE_sched_ctrl_t *sched_ctrlCommon;
NR_sched_pdcch_t *sched_pdcch_otherSI;
uint16_t cset0_bwp_start;