Commit 8ed54d70 authored by Maxime's avatar Maxime Committed by Maxime Elkael

Refactor DL scheduler into staged pipeline with pluggable function pointers

Assisted By Claude Code:Opus-4.6
Signed-off-by: default avatarMaxime Elkael <m.elkael@northeastern.edu>
parent 6fc2d060
...@@ -1271,6 +1271,7 @@ set (MAC_NR_SRC ...@@ -1271,6 +1271,7 @@ set (MAC_NR_SRC
${NR_GNB_MAC_DIR}/gNB_scheduler.c ${NR_GNB_MAC_DIR}/gNB_scheduler.c
${NR_GNB_MAC_DIR}/gNB_scheduler_bch.c ${NR_GNB_MAC_DIR}/gNB_scheduler_bch.c
${NR_GNB_MAC_DIR}/gNB_scheduler_dlsch.c ${NR_GNB_MAC_DIR}/gNB_scheduler_dlsch.c
${NR_GNB_MAC_DIR}/gNB_scheduler_dlsch_default_policies.c
${NR_GNB_MAC_DIR}/gNB_scheduler_ulsch.c ${NR_GNB_MAC_DIR}/gNB_scheduler_ulsch.c
${NR_GNB_MAC_DIR}/gNB_scheduler_primitives.c ${NR_GNB_MAC_DIR}/gNB_scheduler_primitives.c
${NR_GNB_MAC_DIR}/gNB_scheduler_phytest.c ${NR_GNB_MAC_DIR}/gNB_scheduler_phytest.c
......
...@@ -269,7 +269,22 @@ void nr_dlsim_preprocessor(gNB_MAC_INST *nr_mac, post_process_pdsch_t *pp_pdsch) ...@@ -269,7 +269,22 @@ void nr_dlsim_preprocessor(gNB_MAC_INST *nr_mac, post_process_pdsch_t *pp_pdsch)
AssertFatal(sched_pdsch.mcs >= 0, "invalid mcs %d\n", sched_pdsch.mcs); AssertFatal(sched_pdsch.mcs >= 0, "invalid mcs %d\n", sched_pdsch.mcs);
AssertFatal(current_BWP->mcsTableIdx >= 0 && current_BWP->mcsTableIdx <= 2, "invalid mcsTableIdx %d\n", current_BWP->mcsTableIdx); AssertFatal(current_BWP->mcsTableIdx >= 0 && current_BWP->mcsTableIdx <= 2, "invalid mcsTableIdx %d\n", current_BWP->mcsTableIdx);
post_process_dlsch(nr_mac, pp_pdsch, UE_info, &sched_pdsch); nr_dl_candidate_t candidate = {
.UE = UE_info,
.rnti = UE_info->rnti,
.is_retx = sched_ctrl->harq_processes[sched_pdsch.dl_harq_pid].round > 0,
.retx_harq_pid = sched_pdsch.dl_harq_pid,
.pending_bytes = sched_ctrl->num_total_bytes,
.mcs_table = current_BWP->mcsTableIdx,
.bwp_start = sched_pdsch.bwp_info.bwpStart,
.bwp_size = sched_pdsch.bwp_info.bwpSize,
};
for (int i = 0; i < seq_arr_size(&sched_ctrl->lc_config); ++i) {
const nr_lc_config_t *c = seq_arr_at(&sched_ctrl->lc_config, i);
candidate.pending_bytes_per_lcid[c->lcid] = sched_ctrl->rlc_status[c->lcid].bytes_in_buffer;
}
post_process_dlsch(nr_mac, pp_pdsch, UE_info, &sched_pdsch, &candidate);
} }
nrUE_params_t nrUE_params; nrUE_params_t nrUE_params;
......
/*
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
*/
#ifndef GNB_SCHEDULER_DLSCH_DEFAULT_POLICIES_H
#define GNB_SCHEDULER_DLSCH_DEFAULT_POLICIES_H
#include "LAYER2/NR_MAC_gNB/nr_mac_gNB.h"
void nr_dl_ri_pmi_select_default(const gNB_MAC_INST *mac, nr_dl_candidate_t *candidates, int n_candidates);
void nr_dl_mcs_select_default(const gNB_MAC_INST *mac, nr_dl_candidate_t *candidates, int n_candidates);
int nr_dl_beam_select_default(NR_beam_info_t *beam_info,
const int16_t *beam_index_list,
nr_dl_candidate_t *candidates,
int n_candidates,
frame_t frame,
slot_t slot,
int slots_per_frame);
int nr_dl_tda_select_default(const gNB_MAC_INST *mac, nr_dl_candidate_t *candidates, int n_candidates, frame_t frame, slot_t slot);
int nr_dl_proportional_fair(const nr_dl_sched_params_t *params, nr_dl_candidate_t *candidates, int n_candidates);
void nr_dl_lcid_alloc_default(const gNB_MAC_INST *mac,
const nr_dl_candidate_t *candidate,
int tbs_available,
int lcid_alloc[NR_MAX_NUM_LCID]);
#endif /* GNB_SCHEDULER_DLSCH_DEFAULT_POLICIES_H */
...@@ -50,6 +50,8 @@ void nr_preprocessor_phytest(gNB_MAC_INST *mac, post_process_pdsch_t *pp_pdsch) ...@@ -50,6 +50,8 @@ void nr_preprocessor_phytest(gNB_MAC_INST *mac, post_process_pdsch_t *pp_pdsch)
if (!is_xlsch_in_slot(dlsch_slot_bitmap, dlsch_slot_modval, slot_period)) if (!is_xlsch_in_slot(dlsch_slot_bitmap, dlsch_slot_modval, slot_period))
return; return;
NR_UE_info_t *UE = mac->UE_info.connected_ue_list[0]; NR_UE_info_t *UE = mac->UE_info.connected_ue_list[0];
if (UE == NULL)
return;
NR_ServingCellConfigCommon_t *scc = mac->common_channels[0].ServingCellConfigCommon; NR_ServingCellConfigCommon_t *scc = mac->common_channels[0].ServingCellConfigCommon;
NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl; NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
NR_UE_DL_BWP_t *dl_bwp = &UE->current_DL_BWP; NR_UE_DL_BWP_t *dl_bwp = &UE->current_DL_BWP;
...@@ -176,7 +178,19 @@ void nr_preprocessor_phytest(gNB_MAC_INST *mac, post_process_pdsch_t *pp_pdsch) ...@@ -176,7 +178,19 @@ void nr_preprocessor_phytest(gNB_MAC_INST *mac, post_process_pdsch_t *pp_pdsch)
target_dl_Nl) target_dl_Nl)
>> 3; >> 3;
post_process_dlsch(mac, pp_pdsch, UE, &sched_pdsch); nr_dl_candidate_t candidate = {
.UE = UE,
.rnti = rnti,
.is_retx = sched_pdsch.dl_harq_pid >= 0,
.retx_harq_pid = sched_pdsch.dl_harq_pid,
.pending_bytes = sched_ctrl->num_total_bytes,
.mcs_table = dl_bwp->mcsTableIdx,
.bwp_start = sched_pdsch.bwp_info.bwpStart,
.bwp_size = sched_pdsch.bwp_info.bwpSize,
};
candidate.pending_bytes_per_lcid[lcid] = sched_ctrl->rlc_status[lcid].bytes_in_buffer;
post_process_dlsch(mac, pp_pdsch, UE, &sched_pdsch, &candidate);
/* mark the corresponding RBs as used */ /* mark the corresponding RBs as used */
for (int rb = 0; rb < sched_pdsch.rbSize; rb++) for (int rb = 0; rb < sched_pdsch.rbSize; rb++)
......
...@@ -702,6 +702,34 @@ bool nr_find_nb_rb(uint16_t Qm, ...@@ -702,6 +702,34 @@ bool nr_find_nb_rb(uint16_t Qm,
return *tbs >= bytes && *nb_rb <= nb_rb_max; return *tbs >= bytes && *nb_rb <= nb_rb_max;
} }
// Find the largest contiguous block of free RBs in the VRB map.
// Returns the block size, or 0 if no free RB is found. out_start is set to the
// first RB of the largest block when the returned size is nonzero.
int find_largest_free_block(const uint16_t *vrb_map, uint16_t slbitmap, int bwp_start, int bwp_size, int *out_start)
{
int best_start = 0, best_len = 0;
int cur_start = 0, cur_len = 0;
for (int rb = 0; rb < bwp_size; rb++) {
if (!(vrb_map[rb + bwp_start] & slbitmap)) {
if (cur_len == 0)
cur_start = rb;
cur_len++;
} else {
if (cur_len > best_len) {
best_start = cur_start;
best_len = cur_len;
}
cur_len = 0;
}
}
if (cur_len > best_len) {
best_start = cur_start;
best_len = cur_len;
}
*out_start = best_start;
return best_len;
}
bool get_rb_alloc(int rbSize_min, bool get_rb_alloc(int rbSize_min,
int rbSize_max, int rbSize_max,
int bwpStart, int bwpStart,
...@@ -811,41 +839,44 @@ NR_pusch_dmrs_t get_ul_dmrs_params(const NR_ServingCellConfigCommon_t *scc, ...@@ -811,41 +839,44 @@ NR_pusch_dmrs_t get_ul_dmrs_params(const NR_ServingCellConfigCommon_t *scc,
#define BLER_UPDATE_FRAME 10 #define BLER_UPDATE_FRAME 10
#define BLER_FILTER 0.9f #define BLER_FILTER 0.9f
int get_mcs_from_bler(const NR_bler_options_t *bler_options, int nr_adapt_mcs_from_bler(int current_mcs, int min_mcs, int max_mcs, float bler, float bler_lower, float bler_upper, int num_sched)
const NR_mac_dir_stats_t *stats, {
NR_bler_stats_t *bler_stats, int mcs = current_mcs;
int max_mcs, if (bler < bler_lower && mcs < max_mcs && num_sched > 3)
frame_t frame) mcs++;
else if (bler > bler_upper || num_sched <= 3) // above threshold or no activity
mcs--;
return max(min_mcs, min(mcs, max_mcs));
}
bool update_bler_stats(const NR_bler_options_t *bler_options,
const NR_mac_dir_stats_t *stats,
NR_bler_stats_t *bler_stats,
frame_t frame)
{ {
int diff = frame - bler_stats->last_frame; int diff = frame - bler_stats->last_frame;
if (diff < 0) // wrap around if (diff < 0) // wrap around
diff += 1024; diff += 1024;
max_mcs = min(max_mcs, bler_options->max_mcs);
const uint8_t old_mcs = min(bler_stats->mcs, max_mcs);
if (diff < BLER_UPDATE_FRAME) if (diff < BLER_UPDATE_FRAME)
return old_mcs; // no update return false;
// last update is longer than x frames ago
const int num_dl_sched = (int)(stats->rounds[0] - bler_stats->rounds[0]); const int num_dl_sched = (int)(stats->rounds[0] - bler_stats->rounds[0]);
const int num_dl_retx = (int)(stats->rounds[1] - bler_stats->rounds[1]); const int num_dl_retx = (int)(stats->rounds[1] - bler_stats->rounds[1]);
const float bler_window = num_dl_sched > 0 ? (float) num_dl_retx / num_dl_sched : bler_stats->bler; const float bler_window = num_dl_sched > 0 ? (float)num_dl_retx / num_dl_sched : bler_stats->bler;
bler_stats->bler = BLER_FILTER * bler_stats->bler + (1 - BLER_FILTER) * bler_window; bler_stats->bler = BLER_FILTER * bler_stats->bler + (1 - BLER_FILTER) * bler_window;
int new_mcs = old_mcs;
if (bler_stats->bler < bler_options->lower && old_mcs < max_mcs && num_dl_sched > 3)
new_mcs += 1;
else if (bler_stats->bler > bler_options->upper || num_dl_sched <= 3) // above threshold or no activity
new_mcs -= 1;
// else we are within threshold boundaries
new_mcs = max(new_mcs, bler_options->min_mcs);
bler_stats->last_frame = frame; bler_stats->last_frame = frame;
bler_stats->mcs = new_mcs; bler_stats->last_num_sched = num_dl_sched;
memcpy(bler_stats->rounds, stats->rounds, sizeof(stats->rounds)); memcpy(bler_stats->rounds, stats->rounds, sizeof(stats->rounds));
LOG_D(MAC, "frame %4d MCS %d -> %d (num_dl_sched %d, num_dl_retx %d, BLER wnd %.3f avg %.6f)\n", LOG_D(MAC,
frame, old_mcs, new_mcs, num_dl_sched, num_dl_retx, bler_window, bler_stats->bler); "frame %4d BLER update (num_sched %d, num_retx %d, BLER wnd %.3f avg %.6f)\n",
return new_mcs; frame,
num_dl_sched,
num_dl_retx,
bler_window,
bler_stats->bler);
return true;
} }
nfapi_nr_dl_dci_pdu_t *prepare_dci_pdu(nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu, nfapi_nr_dl_dci_pdu_t *prepare_dci_pdu(nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu,
...@@ -2989,6 +3020,8 @@ static void init_bler_stats(const NR_bler_options_t *bler_options, NR_bler_stats ...@@ -2989,6 +3020,8 @@ static void init_bler_stats(const NR_bler_options_t *bler_options, NR_bler_stats
NR_UE_info_t *get_new_nr_ue_inst(uid_allocator_t *uia, rnti_t rnti, NR_CellGroupConfig_t *CellGroup, const nr_mac_config_t *config) NR_UE_info_t *get_new_nr_ue_inst(uid_allocator_t *uia, rnti_t rnti, NR_CellGroupConfig_t *CellGroup, const nr_mac_config_t *config)
{ {
NR_UE_info_t *UE = calloc_or_fail(1, sizeof(NR_UE_info_t)); NR_UE_info_t *UE = calloc_or_fail(1, sizeof(NR_UE_info_t));
for (int i = 0; i < MAX_NUM_OF_SSB; i++)
UE->beam_rsrp[i] = UE->beam_sinr[i] = INT16_MIN;
UE->uid = uid_linear_allocator_new(uia); UE->uid = uid_linear_allocator_new(uia);
UE->rnti = rnti; UE->rnti = rnti;
UE->CellGroup = CellGroup; UE->CellGroup = CellGroup;
......
...@@ -501,6 +501,10 @@ static void evaluate_sinr_report(NR_UE_info_t *UE, ...@@ -501,6 +501,10 @@ static void evaluate_sinr_report(NR_UE_info_t *UE,
sched_ctrl->dl_max_mcs = get_mcs_from_SINRx10(mcs_table, sinr_report->r[0].SINRx10, nrOfLayers); sched_ctrl->dl_max_mcs = get_mcs_from_SINRx10(mcs_table, sinr_report->r[0].SINRx10, nrOfLayers);
LOG_D(MAC, "Reported SSB-SINR = %01f, dl_max_mcs %d\n", sinr_report->r[0].SINRx10 / 10.0, sched_ctrl->dl_max_mcs); LOG_D(MAC, "Reported SSB-SINR = %01f, dl_max_mcs %d\n", sinr_report->r[0].SINRx10 / 10.0, sched_ctrl->dl_max_mcs);
for (RSRP_report_t *r = sinr_report->r; r < sinr_report->r + sinr_report->nb; r++)
if (r->resource_id < MAX_NUM_OF_SSB)
UE->beam_sinr[r->resource_id] = r->SINRx10;
} }
static void evaluate_rsrp_report(NR_UE_info_t *UE, static void evaluate_rsrp_report(NR_UE_info_t *UE,
...@@ -574,6 +578,10 @@ static void evaluate_rsrp_report(NR_UE_info_t *UE, ...@@ -574,6 +578,10 @@ static void evaluate_rsrp_report(NR_UE_info_t *UE,
// including ssb rsrp in mac stats // including ssb rsrp in mac stats
stats->cumul_rsrp += rsrp_report->r[0].RSRP; stats->cumul_rsrp += rsrp_report->r[0].RSRP;
stats->num_rsrp_meas++; stats->num_rsrp_meas++;
for (RSRP_report_t *r = rsrp_report->r; r < rsrp_report->r + rsrp_report->nb; r++)
if (r->resource_id < MAX_NUM_OF_SSB)
UE->beam_rsrp[r->resource_id] = r->RSRP;
} }
static void evaluate_cri_report(uint8_t *payload, uint8_t cri_bitlen, int cumul_bits, NR_UE_sched_ctrl_t *sched_ctrl) static void evaluate_cri_report(uint8_t *payload, uint8_t cri_bitlen, int cumul_bits, NR_UE_sched_ctrl_t *sched_ctrl)
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#define __LAYER2_NR_MAC_PROTO_H__ #define __LAYER2_NR_MAC_PROTO_H__
#include "LAYER2/NR_MAC_gNB/nr_mac_gNB.h" #include "LAYER2/NR_MAC_gNB/nr_mac_gNB.h"
#include "LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch_default_policies.h"
#include "NR_TAG-Id.h" #include "NR_TAG-Id.h"
#include "common/ngran_types.h" #include "common/ngran_types.h"
#include "openair2/LAYER2/nr_pdcp/nr_pdcp_configuration.h" #include "openair2/LAYER2/nr_pdcp/nr_pdcp_configuration.h"
...@@ -60,8 +61,8 @@ void nr_schedule_ue_spec(module_id_t module_id, ...@@ -60,8 +61,8 @@ void nr_schedule_ue_spec(module_id_t module_id,
nfapi_nr_dl_tti_request_t *DL_req, nfapi_nr_dl_tti_request_t *DL_req,
nfapi_nr_tx_data_request_t *TX_req); nfapi_nr_tx_data_request_t *TX_req);
/* \brief default DL preprocessor init routine, returns preprocessor to call */ /* \brief default DL preprocessor */
nr_pp_impl_dl nr_init_dlsch_preprocessor(); void nr_dlsch_preprocessor(gNB_MAC_INST *mac, post_process_pdsch_t *pp_pdsch);
void schedule_nr_sib1(module_id_t module_idP, void schedule_nr_sib1(module_id_t module_idP,
frame_t frameP, frame_t frameP,
...@@ -425,6 +426,8 @@ bool nr_find_nb_rb(uint16_t Qm, ...@@ -425,6 +426,8 @@ bool nr_find_nb_rb(uint16_t Qm,
* \param rbSize_ptr Pointer returning the size of the found free block of RBs * \param rbSize_ptr Pointer returning the size of the found free block of RBs
* \return Indicates if a free block of RBs of the required size could be found and *rbStart_ptr and *rbSize_ptr are set accordingly * \return Indicates if a free block of RBs of the required size could be found and *rbStart_ptr and *rbSize_ptr are set accordingly
*/ */
int find_largest_free_block(const uint16_t *vrb_map, uint16_t slbitmap, int bwp_start, int bwp_size, int *out_start);
bool get_rb_alloc(int rbSize_min, bool get_rb_alloc(int rbSize_min,
int rbSize_max, int rbSize_max,
int bwpStart, int bwpStart,
...@@ -434,11 +437,44 @@ bool get_rb_alloc(int rbSize_min, ...@@ -434,11 +437,44 @@ bool get_rb_alloc(int rbSize_min,
int *rbStart_ptr, int *rbStart_ptr,
int *rbSize_ptr); int *rbSize_ptr);
int get_mcs_from_bler(const NR_bler_options_t *bler_options, /* Scalar core of the BLER -> MCS adaptation rule. Single source of truth
const NR_mac_dir_stats_t *stats, * for the activity-guard threshold and the lower/upper hysteresis. */
NR_bler_stats_t *bler_stats, int nr_adapt_mcs_from_bler(int current_mcs,
int max_mcs, int min_mcs,
frame_t frame); int max_mcs,
float bler,
float bler_lower,
float bler_upper,
int num_sched);
bool update_bler_stats(const NR_bler_options_t *bler_options,
const NR_mac_dir_stats_t *stats,
NR_bler_stats_t *bler_stats,
frame_t frame);
float dl_pf_weight(int mcs, int mcs_table, int nrOfLayers, float avg_throughput);
uint16_t check_dl_retx_feasibility(const nr_dl_candidate_t *cand,
int tda,
const NR_tda_info_t *tda_info,
const NR_ServingCellConfigCommon_t *scc,
uint16_t max_rbSize);
bool nr_dl_validate_cce_pucch(const nr_dl_sched_params_t *params, nr_dl_candidate_t *cand);
bool commit_alloc(const nr_dl_sched_params_t *params, nr_dl_candidate_t *cand);
// Use inside the policy loops: sets RB/MCS on candidate, validates CCE/PUCCH,
// marks scheduled; continues on failure, returns on max_num_ue.
#define COMMIT_ALLOC(params, cand, rb_start_, rb_size_, mcs_, n_sched) \
do { \
(cand)->sched_pdsch.rbStart = (rb_start_); \
(cand)->sched_pdsch.rbSize = (rb_size_); \
(cand)->sched_pdsch.mcs = (mcs_); \
if (!commit_alloc(params, cand)) \
continue; \
(cand)->scheduled = true; \
(n_sched)++; \
if ((n_sched) >= (params)->max_num_ue) \
return (n_sched); \
} while (0)
int ul_buffer_index(int frame, int slot, int slots_per_frame, int size); int ul_buffer_index(int frame, int slot, int slots_per_frame, int size);
void UL_tti_req_ahead_initialization(gNB_MAC_INST *gNB, int n, int CCid, frame_t frameP, int slotP); void UL_tti_req_ahead_initialization(gNB_MAC_INST *gNB, int n, int CCid, frame_t frameP, int slotP);
...@@ -495,7 +531,11 @@ void prepare_du_configuration_update(gNB_MAC_INST *mac, ...@@ -495,7 +531,11 @@ void prepare_du_configuration_update(gNB_MAC_INST *mac,
void nr_mac_clean_cellgroup(NR_CellGroupConfig_t *cell_group); void nr_mac_clean_cellgroup(NR_CellGroupConfig_t *cell_group);
void post_process_dlsch(gNB_MAC_INST *nr_mac, post_process_pdsch_t *pdsch, NR_UE_info_t *UE, NR_sched_pdsch_t *sched_pdsch); void post_process_dlsch(gNB_MAC_INST *nr_mac,
post_process_pdsch_t *pdsch,
NR_UE_info_t *UE,
NR_sched_pdsch_t *sched_pdsch,
const nr_dl_candidate_t *candidate);
void post_process_ulsch(gNB_MAC_INST *nr_mac, post_process_pusch_t *pusch, NR_UE_info_t *UE, NR_sched_pusch_t *sched_pusch); void post_process_ulsch(gNB_MAC_INST *nr_mac, post_process_pusch_t *pusch, NR_UE_info_t *UE, NR_sched_pusch_t *sched_pusch);
float nr_mac_get_snr(const nr_power_control_t *pc); float nr_mac_get_snr(const nr_power_control_t *pc);
......
...@@ -173,7 +173,8 @@ size_t dump_mac_stats(gNB_MAC_INST *gNB, char *output, size_t strlen, bool reset ...@@ -173,7 +173,8 @@ size_t dump_mac_stats(gNB_MAC_INST *gNB, char *output, size_t strlen, bool reset
float pucch_snr_diff = (pucch_snr * 10.0f - sched_ctrl->pucch_pc.target_snrx10) / 10.0f; float pucch_snr_diff = (pucch_snr * 10.0f - sched_ctrl->pucch_pc.target_snrx10) / 10.0f;
output = st_append(output, output = st_append(output,
end, end,
", dlsch_errors %"PRIu64", pucch0_DTX %d (SNR %.1f%+.1f dB), BLER %.5f MCS (%d) %d CCE fail %d\n", ", dlsch_errors %" PRIu64
", pucch0_DTX %d (SNR %.1f%+.1f dB), BLER %.5f MCS (%d) %d CCE fail %d, goodput %.2f Mbps\n",
stats->dl.errors, stats->dl.errors,
stats->pucch0_DTX, stats->pucch0_DTX,
pucch_snr, pucch_snr,
...@@ -181,7 +182,8 @@ size_t dump_mac_stats(gNB_MAC_INST *gNB, char *output, size_t strlen, bool reset ...@@ -181,7 +182,8 @@ size_t dump_mac_stats(gNB_MAC_INST *gNB, char *output, size_t strlen, bool reset
sched_ctrl->dl_bler_stats.bler, sched_ctrl->dl_bler_stats.bler,
UE->current_DL_BWP.mcsTableIdx, UE->current_DL_BWP.mcsTableIdx,
sched_ctrl->dl_bler_stats.mcs, sched_ctrl->dl_bler_stats.mcs,
sched_ctrl->dl_cce_fail); sched_ctrl->dl_cce_fail,
UE->dl_thr_ue_display / 1e6);
if (reset_rsrp) { if (reset_rsrp) {
stats->num_rsrp_meas = 0; stats->num_rsrp_meas = 0;
stats->cumul_rsrp = 0; stats->cumul_rsrp = 0;
...@@ -300,12 +302,19 @@ void mac_top_init_gNB(ngran_node_t node_type, ...@@ -300,12 +302,19 @@ void mac_top_init_gNB(ngran_node_t node_type,
uid_linear_allocator_init(&RC.nrmac[i]->UE_info.uid_allocator); uid_linear_allocator_init(&RC.nrmac[i]->UE_info.uid_allocator);
RC.nrmac[i]->dl_lcid_alloc = nr_dl_lcid_alloc_default;
if (get_softmodem_params()->phy_test) { if (get_softmodem_params()->phy_test) {
RC.nrmac[i]->pre_processor_dl = nr_preprocessor_phytest; RC.nrmac[i]->pre_processor_dl = nr_preprocessor_phytest;
RC.nrmac[i]->pre_processor_ul = nr_ul_preprocessor_phytest; RC.nrmac[i]->pre_processor_ul = nr_ul_preprocessor_phytest;
} else { } else {
RC.nrmac[i]->pre_processor_dl = nr_init_dlsch_preprocessor(); RC.nrmac[i]->pre_processor_dl = nr_dlsch_preprocessor;
RC.nrmac[i]->pre_processor_ul = nr_init_ulsch_preprocessor(); RC.nrmac[i]->pre_processor_ul = nr_init_ulsch_preprocessor();
RC.nrmac[i]->dl_ri_pmi_select = nr_dl_ri_pmi_select_default;
RC.nrmac[i]->dl_mcs_select = nr_dl_mcs_select_default;
RC.nrmac[i]->dl_beam_select = nr_dl_beam_select_default;
RC.nrmac[i]->dl_tda_select = nr_dl_tda_select_default;
RC.nrmac[i]->dl_rb_alloc = nr_dl_proportional_fair;
} }
if (!IS_SOFTMODEM_NOSTATS) if (!IS_SOFTMODEM_NOSTATS)
threadCreate(&RC.nrmac[i]->stats_thread, threadCreate(&RC.nrmac[i]->stats_thread,
......
...@@ -523,6 +523,7 @@ typedef struct NR_bler_stats { ...@@ -523,6 +523,7 @@ typedef struct NR_bler_stats {
float bler; float bler;
uint8_t mcs; uint8_t mcs;
uint64_t rounds[8]; uint64_t rounds[8];
int last_num_sched; // scheduling count at last BLER update (for activity guard)
} NR_bler_stats_t; } NR_bler_stats_t;
// //
...@@ -714,6 +715,8 @@ typedef struct NR_mac_dir_stats { ...@@ -714,6 +715,8 @@ typedef struct NR_mac_dir_stats {
uint32_t total_rbs_retx; uint32_t total_rbs_retx;
uint32_t num_mac_sdu; uint32_t num_mac_sdu;
uint32_t current_rbs; uint32_t current_rbs;
uint64_t prev_sdu_bytes;
frame_t last_goodput_frame;
} NR_mac_dir_stats_t; } NR_mac_dir_stats_t;
typedef struct NR_mac_stats { typedef struct NR_mac_stats {
...@@ -784,8 +787,14 @@ typedef struct NR_UE_info { ...@@ -784,8 +787,14 @@ typedef struct NR_UE_info {
measgap_config_t measgap_config; measgap_config_t measgap_config;
// UE selected beam index // UE selected beam index
uint16_t UE_beam_index; uint16_t UE_beam_index;
/// Per-SSB L1-RSRP table, indexed by SSB index (resource_id from CSI report).
/// INT16_MIN means no measurement received yet for that beam.
int16_t beam_rsrp[MAX_NUM_OF_SSB];
/// Per-SSB L1-SINR×10 table, same indexing. INT16_MIN means no data.
int16_t beam_sinr[MAX_NUM_OF_SSB];
float ul_thr_ue; float ul_thr_ue;
float dl_thr_ue; float dl_thr_ue;
float dl_thr_ue_display; ///< slow EWMA for stable display (alpha=0.001)
long pdsch_HARQ_ACK_Codebook; long pdsch_HARQ_ACK_Codebook;
bool is_redcap; bool is_redcap;
bool reestablish_rlc; bool reestablish_rlc;
...@@ -822,7 +831,8 @@ typedef struct { ...@@ -822,7 +831,8 @@ typedef struct {
nr_beam_mode_t beam_mode; nr_beam_mode_t beam_mode;
} NR_beam_info_t; } NR_beam_info_t;
#define UE_iterator(BaSe, VaR) for (NR_UE_info_t **VaR##pptr=BaSe, *VaR=*VaR##pptr; VaR; VaR=*(++VaR##pptr)) #define UE_iterator(BaSe, VaR) for (NR_UE_info_t **VaR##pptr = BaSe, *VaR = *VaR##pptr; VaR; VaR = *(++VaR##pptr))
#define FOR_EACH_CANDIDATE(VaR, ArR, N) for (__typeof__(*(ArR)) *VaR = (ArR); VaR < (ArR) + (N); VaR++)
typedef struct { typedef struct {
/// current frame /// current frame
...@@ -846,6 +856,76 @@ typedef struct { ...@@ -846,6 +856,76 @@ typedef struct {
nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_coreset[MAX_NUM_CORESET]; nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_coreset[MAX_NUM_CORESET];
} post_process_pusch_t; } post_process_pusch_t;
/* forward declarations for scheduling types */
struct gNB_MAC_INST_s;
typedef struct nr_dl_candidate nr_dl_candidate_t;
/// Scheduling context passed to the RB allocation policy.
/// Contains per-beam VRB maps so the policy can handle beam partitioning
/// internally (e.g. for MU-MIMO cross-beam scheduling).
typedef struct nr_dl_sched_params nr_dl_sched_params_t;
struct nr_dl_sched_params {
struct gNB_MAC_INST_s *mac; ///< MAC instance (for CCE/PUCCH validation)
int CC_id;
frame_t frame;
slot_t slot;
int num_beams; ///< number of beams
int max_num_ue; ///< max UEs to schedule
uint16_t *vrb_map[MAX_NUM_BEAM_PERIODS]; ///< per-beam VRB maps [275], mutable
int n_rb_avail[MAX_NUM_BEAM_PERIODS]; ///< available RBs per beam
int min_mcs; ///< minimum MCS from BLER config
float bler_lower; ///< BLER lower threshold (increase MCS if below)
float bler_upper; ///< BLER upper threshold (decrease MCS if above)
};
/// Per-UE scheduling candidate — read-only inputs for the policy function.
struct nr_dl_candidate {
/* ── UE identity / scheduling state (set by collect, never modified after) ── */
NR_UE_info_t *UE;
uint16_t rnti; ///< UE RNTI (convenience, avoids UE pointer dereference)
bool is_retx; ///< true = HARQ retransmission pending
int8_t retx_harq_pid; ///< HARQ PID for retx, -1 if none
int retx_rbSize; ///< RBs needed for retx, 0 for new tx
uint32_t pending_bytes; ///< total bytes waiting in RLC buffers
uint32_t pending_bytes_per_lcid[NR_MAX_NUM_LCID]; ///< per-LCID bytes waiting in RLC buffers
float avg_throughput; ///< EWMA goodput in bps (dl_thr_ue)
float bler; ///< current BLER estimate
int current_mcs; ///< current MCS state (retx: from HARQ, new tx: from BLER tracker)
int max_mcs; ///< max allowed MCS (config + UE capability)
int last_num_sched; ///< scheduled occasions in last BLER window
bool bler_updated; ///< true if BLER was refreshed this frame
int mcs_table; ///< MCS table index (from BWP config)
int bwp_start; ///< UE's BWP start
int bwp_size; ///< UE's BWP size
uint64_t fiveQI; ///< 5QI from first DRB's QoS config (0 if none)
int priority; ///< LC priority from first DRB (lower = higher priority, 0 if none)
nssai_t nssai; ///< slice/service type/differentiator from first DRB
bool skipped; ///< true if dropped by TDA/beam select (skip in downstream stages)
bool scheduled; ///< true if accepted by the RB-allocation policy
/* ── UE CSI observations (set by collect, read-only after) ─────────────── */
uint16_t cqi; ///< UE-reported wideband CQI
uint8_t csi_ri; ///< UE-reported rank indicator (0 = rank-1); forced to 0 for DCI 1_0
int csi_pm_index; ///< PM index derived from UE-reported PMI + antenna config
const int16_t *beam_rsrp; ///< per-SSB L1-RSRP; points into NR_UE_info_t::beam_rsrp. INT16_MIN = no data
const int16_t *beam_sinr; ///< per-SSB L1-SINR×10; same indexing. INT16_MIN = no data
/* ── gNB decisions (written by the named pipeline stage) ───────────────── */
/* Use NR_sched_pdsch_t for fields shared with HARQ/dispatch (mcs, rbStart,
* rbSize, nrOfLayers, pm_index, tda, tda_info, pucch_allocation).*/
NR_sched_pdsch_t sched_pdsch;
uint16_t alloc_slbitmap; ///< symbol bitmap derived from sched_pdsch.tda_info
/* dl_beam_select: beam selection */
int alloc_beam_dir; ///< beam direction index (initialised from UE->UE_beam_index; may be overridden)
int alloc_beam_idx; ///< hardware beam structure index, set by beam_allocation_procedure
bool alloc_new_beam; ///< true if beam alloc claimed a fresh slot (release via reset_beam_status if unscheduled)
/* commit_alloc: CCE/PUCCH validation */
int alloc_cce_index; ///< CCE index for PDCCH
int alloc_aggregation_level; ///< PDCCH aggregation level
NR_sched_pdcch_t alloc_sched_pdcch; ///< PDCCH scheduling info
};
/* forward declaration to use in nr_pp_impl_dl */ /* forward declaration to use in nr_pp_impl_dl */
struct gNB_MAC_INST_s; struct gNB_MAC_INST_s;
typedef struct gNB_MAC_INST_s gNB_MAC_INST; typedef struct gNB_MAC_INST_s gNB_MAC_INST;
...@@ -853,7 +933,49 @@ typedef struct gNB_MAC_INST_s gNB_MAC_INST; ...@@ -853,7 +933,49 @@ typedef struct gNB_MAC_INST_s gNB_MAC_INST;
typedef void (*nr_pp_impl_dl)(gNB_MAC_INST *nr_mac, post_process_pdsch_t *pp_pdsch); typedef void (*nr_pp_impl_dl)(gNB_MAC_INST *nr_mac, post_process_pdsch_t *pp_pdsch);
typedef void (*nr_pp_impl_ul)(gNB_MAC_INST *nr_mac, post_process_pusch_t *pp_pusch); typedef void (*nr_pp_impl_ul)(gNB_MAC_INST *nr_mac, post_process_pusch_t *pp_pusch);
typedef struct { /// RI/PMI selection: sets nrOfLayers and pm_index per candidate from CSI feedback.
/// For retransmissions, nrOfLayers must match the original transmission.
/// Custom implementations may use SRS reciprocity to override the UE's reported RI/PMI.
typedef void (*nr_dl_ri_pmi_select_fn)(const gNB_MAC_INST *mac, nr_dl_candidate_t *candidates, int n_candidates);
/// MCS adaptation: sets sched_pdsch.mcs from BLER state for every candidate.
/// Called for all candidates (including those that won't get scheduled) so
/// BLER-based MCS ramps even for UEs that fail CCE. Also persists the
/// decision to dl_bler_stats.mcs for continuity across slots.
typedef void (*nr_dl_mcs_select_fn)(const gNB_MAC_INST *mac, nr_dl_candidate_t *candidates, int n_candidates);
/// Beam allocation: assigns beam structure index to each candidate.
/// beam_index_list maps SSB id -> beam index (same as mac->beam_index_list);
/// custom implementations can use it with cand->beam_rsrp[] to pick the best beam.
typedef int (*nr_dl_beam_select_fn)(NR_beam_info_t *beam_info,
const int16_t *beam_index_list,
nr_dl_candidate_t *candidates,
int n_candidates,
frame_t frame,
slot_t slot,
int slots_per_frame);
/// TDA selection: assigns tda/tda_info/slbitmap per candidate.
/// Returns the number of candidates with a valid TDA (compacts invalids out).
typedef int (
*nr_dl_tda_select_fn)(const gNB_MAC_INST *mac, nr_dl_candidate_t *candidates, int n_candidates, frame_t frame, slot_t slot);
/// Scheduling policy: decides PRB + MCS for candidates across all beams.
/// The beam loop is inside the policy so it can do cross-beam scheduling (e.g. MU-MIMO).
/// Writes sched_pdsch.rbStart/rbSize/mcs and cce/pucch fields on scheduled candidates.
/// Sets candidate->scheduled = true for each accepted UE; returns the count.
typedef int (*nr_dl_rb_alloc_fn)(const nr_dl_sched_params_t *params, nr_dl_candidate_t *candidates, int n_candidates);
/// Per-LCID byte allocation: decides how many bytes each LCID gets within
/// the available TBS for an initial transmission. Called during MAC PDU
/// generation. Writes lcid_alloc[lcid] = max data bytes for that LCID.
/// The execution loop caps actual RLC requests to these budgets.
typedef void (*nr_dl_lcid_alloc_fn)(const gNB_MAC_INST *mac,
const nr_dl_candidate_t *candidate,
int tbs_available,
int lcid_alloc[NR_MAX_NUM_LCID]);
typedef struct f1_config_t {
f1ap_setup_req_t *setup_req; f1ap_setup_req_t *setup_req;
f1ap_setup_resp_t *setup_resp; f1ap_setup_resp_t *setup_resp;
uint32_t gnb_id; // associated gNB's ID, not used in DU itself uint32_t gnb_id; // associated gNB's ID, not used in DU itself
...@@ -962,6 +1084,17 @@ typedef struct gNB_MAC_INST_s { ...@@ -962,6 +1084,17 @@ typedef struct gNB_MAC_INST_s {
/// UL preprocessor for differentiated scheduling /// UL preprocessor for differentiated scheduling
nr_pp_impl_ul pre_processor_ul; nr_pp_impl_ul pre_processor_ul;
/// DL scheduling pipeline function pointers
nr_dl_ri_pmi_select_fn dl_ri_pmi_select;
nr_dl_tda_select_fn dl_tda_select;
nr_dl_beam_select_fn dl_beam_select;
nr_dl_mcs_select_fn dl_mcs_select;
nr_dl_rb_alloc_fn dl_rb_alloc;
nr_dl_lcid_alloc_fn dl_lcid_alloc;
/// Optional state persistence for scheduling policies.
void *sched_stateful_data;
nr_mac_config_t radio_config; nr_mac_config_t radio_config;
nr_rlc_configuration_t rlc_config; nr_rlc_configuration_t rlc_config;
...@@ -977,7 +1110,7 @@ typedef struct gNB_MAC_INST_s { ...@@ -977,7 +1110,7 @@ typedef struct gNB_MAC_INST_s {
uint16_t min_grant_prb; uint16_t min_grant_prb;
bool identity_pm; bool identity_pm;
int precoding_matrix_size[NR_MAX_NB_LAYERS]; int precoding_matrix_size[NR_MAX_NB_LAYERS];
int beam_index_list[MAX_NUM_OF_SSB]; int16_t beam_index_list[MAX_NUM_OF_SSB];
NR_sched_pdsch_t sib1_pdsch[MAX_NUM_OF_SSB]; NR_sched_pdsch_t sib1_pdsch[MAX_NUM_OF_SSB];
/// dedicate UL TDA, common for all UEs /// dedicate UL TDA, common for all UEs
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment