mirror of
https://gitlab.eurecom.fr/oai/openairinterface5g.git
synced 2026-07-13 04:30:28 +00:00
Refactor DL scheduler into staged pipeline with pluggable function pointers
Assisted By Claude Code:Opus-4.6 Signed-off-by: Maxime Elkael <m.elkael@northeastern.edu>
This commit is contained in:
@@ -1271,6 +1271,7 @@ set (MAC_NR_SRC
|
||||
${NR_GNB_MAC_DIR}/gNB_scheduler.c
|
||||
${NR_GNB_MAC_DIR}/gNB_scheduler_bch.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_primitives.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)
|
||||
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);
|
||||
|
||||
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;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \brief Default pluggable policy functions for the DL scheduler pipeline.
|
||||
*
|
||||
* These are the built-in implementations behind the function pointers
|
||||
* (dl_ri_pmi_select, dl_tda_select, dl_beam_select, dl_mcs_select, dl_rb_alloc, dl_lcid_alloc)
|
||||
* wired up at MAC init time. They can be replaced at runtime by external
|
||||
* scheduler plug-ins without touching the core scheduling loop in
|
||||
* gNB_scheduler_dlsch.c.
|
||||
*/
|
||||
|
||||
#include "common/utils/nr/nr_common.h"
|
||||
#include "gNB_scheduler_dlsch_default_policies.h"
|
||||
/*MAC*/
|
||||
#include "NR_MAC_COMMON/nr_mac.h"
|
||||
#include "NR_MAC_gNB/nr_mac_gNB.h"
|
||||
#include "LAYER2/NR_MAC_gNB/mac_proto.h"
|
||||
#include "openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h"
|
||||
|
||||
/*TAG*/
|
||||
#include "NR_TAG-Id.h"
|
||||
|
||||
/*Softmodem params*/
|
||||
#include "executables/softmodem-common.h"
|
||||
#include "../../../nfapi/oai_integration/vendor_ext.h"
|
||||
|
||||
// Default RI/PMI selector: reads rank and PMI from CSI feedback for new-tx,
|
||||
// or from HARQ process state for retx.
|
||||
void nr_dl_ri_pmi_select_default(const gNB_MAC_INST *mac, nr_dl_candidate_t *candidates, int n_candidates)
|
||||
{
|
||||
FOR_EACH_CANDIDATE(cand, candidates, n_candidates)
|
||||
{
|
||||
NR_UE_sched_ctrl_t *sched_ctrl = &cand->UE->UE_sched_ctrl;
|
||||
NR_UE_DL_BWP_t *dl_bwp = &cand->UE->current_DL_BWP;
|
||||
if (cand->is_retx) {
|
||||
cand->sched_pdsch.nrOfLayers = sched_ctrl->harq_processes[cand->retx_harq_pid].sched_pdsch.nrOfLayers;
|
||||
cand->sched_pdsch.pm_index =
|
||||
get_pm_index(mac, cand->UE, dl_bwp->dci_format, cand->sched_pdsch.nrOfLayers, mac->radio_config.pdsch_AntennaPorts.XP);
|
||||
} else {
|
||||
cand->sched_pdsch.nrOfLayers = cand->csi_ri + 1;
|
||||
cand->sched_pdsch.pm_index = cand->csi_pm_index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Default TDA selector: picks the slot-wide TDA index from get_dl_tda(),
|
||||
// then resolves tda_info per candidate using each UE's own BWP / search
|
||||
// space / coreset. Marks invalids with skipped=true.
|
||||
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 tda = get_dl_tda(mac, slot);
|
||||
AssertFatal(tda >= 0, "Unable to find PDSCH time domain allocation in list\n");
|
||||
const NR_ServingCellConfigCommon_t *scc = mac->common_channels[0].ServingCellConfigCommon;
|
||||
|
||||
int n_valid = 0;
|
||||
FOR_EACH_CANDIDATE(cand, candidates, n_candidates)
|
||||
{
|
||||
if (cand->skipped)
|
||||
continue;
|
||||
NR_UE_info_t *UE = cand->UE;
|
||||
NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
|
||||
NR_UE_DL_BWP_t *dl_bwp = &UE->current_DL_BWP;
|
||||
int coresetid = sched_ctrl->coreset->controlResourceSetId;
|
||||
NR_tda_info_t tda_info = get_dl_tda_info(dl_bwp,
|
||||
sched_ctrl->search_space->searchSpaceType->present,
|
||||
tda,
|
||||
scc->dmrs_TypeA_Position,
|
||||
1,
|
||||
TYPE_C_RNTI_,
|
||||
coresetid,
|
||||
false);
|
||||
if (!tda_info.valid_tda) {
|
||||
cand->skipped = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* For retransmissions with a changed TDA, refit rbSize to preserve TBS */
|
||||
if (cand->is_retx) {
|
||||
const NR_sched_pdsch_t *orig = &sched_ctrl->harq_processes[cand->retx_harq_pid].sched_pdsch;
|
||||
bool tda_changed =
|
||||
tda_info.startSymbolIndex != orig->tda_info.startSymbolIndex || tda_info.nrOfSymbols != orig->tda_info.nrOfSymbols;
|
||||
if (tda_changed) {
|
||||
uint16_t new_rbSize = check_dl_retx_feasibility(cand, tda, &tda_info, scc, dl_bwp->BWPSize);
|
||||
if (!new_rbSize) {
|
||||
cand->skipped = true;
|
||||
continue;
|
||||
}
|
||||
cand->retx_rbSize = new_rbSize;
|
||||
}
|
||||
}
|
||||
|
||||
cand->sched_pdsch.time_domain_allocation = tda;
|
||||
cand->sched_pdsch.tda_info = tda_info;
|
||||
cand->alloc_slbitmap = SL_to_bitmap(tda_info.startSymbolIndex, tda_info.nrOfSymbols);
|
||||
n_valid++;
|
||||
}
|
||||
return n_valid;
|
||||
}
|
||||
|
||||
static int compare_dl_pf_ptrs(const void *a, const void *b)
|
||||
{
|
||||
const nr_dl_candidate_t *ca = *(const nr_dl_candidate_t *const *)a;
|
||||
const nr_dl_candidate_t *cb = *(const nr_dl_candidate_t *const *)b;
|
||||
/* retx first (INFINITY weight), then highest PF weight */
|
||||
float wa = ca->is_retx ? INFINITY : dl_pf_weight(ca->current_mcs, ca->mcs_table, ca->sched_pdsch.nrOfLayers, ca->avg_throughput);
|
||||
float wb = cb->is_retx ? INFINITY : dl_pf_weight(cb->current_mcs, cb->mcs_table, cb->sched_pdsch.nrOfLayers, cb->avg_throughput);
|
||||
return (wa < wb) - (wa > wb);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
/* Build pointer array sorted by PF priority so retx and high-priority UEs claim beams first. */
|
||||
nr_dl_candidate_t *order[MAX_MOBILES_PER_GNB];
|
||||
int n_active = 0;
|
||||
FOR_EACH_CANDIDATE(cand, candidates, n_candidates)
|
||||
if (!cand->skipped)
|
||||
order[n_active++] = cand;
|
||||
qsort(order, n_active, sizeof(*order), compare_dl_pf_ptrs);
|
||||
|
||||
int n_valid = 0;
|
||||
for (int i = 0; i < n_active; i++) {
|
||||
nr_dl_candidate_t *cand = order[i];
|
||||
NR_beam_alloc_t beam = beam_allocation_procedure(beam_info, frame, slot, cand->alloc_beam_dir, slots_per_frame);
|
||||
if (beam.idx < 0) {
|
||||
cand->skipped = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
cand->alloc_beam_idx = beam.idx;
|
||||
cand->alloc_new_beam = beam.new_beam;
|
||||
n_valid++;
|
||||
}
|
||||
return n_valid;
|
||||
}
|
||||
|
||||
void nr_dl_mcs_select_default(const gNB_MAC_INST *mac, nr_dl_candidate_t *candidates, int n_candidates)
|
||||
{
|
||||
const NR_bler_options_t *bo = &mac->dl_bler;
|
||||
FOR_EACH_CANDIDATE(cand, candidates, n_candidates)
|
||||
{
|
||||
int mcs;
|
||||
if (cand->is_retx) {
|
||||
mcs = cand->current_mcs; /* retx MCS is fixed by the HARQ round */
|
||||
} else if (bo->harq_round_max == 1) {
|
||||
mcs = max(bo->min_mcs, min(bo->max_mcs, cand->max_mcs));
|
||||
} else if (!cand->bler_updated) {
|
||||
mcs = cand->current_mcs;
|
||||
} else {
|
||||
mcs = nr_adapt_mcs_from_bler(cand->current_mcs,
|
||||
bo->min_mcs,
|
||||
cand->max_mcs,
|
||||
cand->bler,
|
||||
bo->lower,
|
||||
bo->upper,
|
||||
cand->last_num_sched);
|
||||
}
|
||||
cand->sched_pdsch.mcs = mcs;
|
||||
/* Persist for all candidates — BLER-based MCS ramps even for UEs the
|
||||
* policy rejects this slot (failed CCE, no free RBs, etc.). */
|
||||
if (!cand->is_retx)
|
||||
cand->UE->UE_sched_ctrl.dl_bler_stats.mcs = mcs;
|
||||
}
|
||||
}
|
||||
|
||||
static int compare_dl_pf_rb_ptrs(const void *a, const void *b)
|
||||
{
|
||||
const nr_dl_candidate_t *ca = *(const nr_dl_candidate_t *const *)a;
|
||||
const nr_dl_candidate_t *cb = *(const nr_dl_candidate_t *const *)b;
|
||||
/* retx first, then highest PF weight (uses sched_pdsch.mcs, which is set by mcs_select) */
|
||||
float wa =
|
||||
ca->is_retx ? INFINITY : dl_pf_weight(ca->sched_pdsch.mcs, ca->mcs_table, ca->sched_pdsch.nrOfLayers, ca->avg_throughput);
|
||||
float wb =
|
||||
cb->is_retx ? INFINITY : dl_pf_weight(cb->sched_pdsch.mcs, cb->mcs_table, cb->sched_pdsch.nrOfLayers, cb->avg_throughput);
|
||||
return (wa < wb) - (wa > wb);
|
||||
}
|
||||
|
||||
int nr_dl_proportional_fair(const nr_dl_sched_params_t *params, nr_dl_candidate_t *candidates, int n_candidates)
|
||||
{
|
||||
const int min_rbSize = 5;
|
||||
int n_scheduled = 0;
|
||||
|
||||
/* Build pointer array sorted by PF priority (retx first, then highest weight) */
|
||||
nr_dl_candidate_t *order[MAX_MOBILES_PER_GNB];
|
||||
int n_active = 0;
|
||||
FOR_EACH_CANDIDATE(cand, candidates, n_candidates)
|
||||
if (!cand->skipped)
|
||||
order[n_active++] = cand;
|
||||
qsort(order, n_active, sizeof(*order), compare_dl_pf_rb_ptrs);
|
||||
|
||||
/* Phase 1: HARQ retransmissions (highest priority, exact RBs) */
|
||||
for (int j = 0; j < n_active; j++) {
|
||||
nr_dl_candidate_t *cand = order[j];
|
||||
if (!cand->is_retx)
|
||||
continue;
|
||||
|
||||
int needed_rbs = cand->retx_rbSize;
|
||||
uint16_t *vrb_map = params->vrb_map[cand->alloc_beam_idx];
|
||||
int rbStart, rbSize;
|
||||
if (!get_rb_alloc(needed_rbs,
|
||||
cand->bwp_size,
|
||||
cand->bwp_start,
|
||||
cand->bwp_size,
|
||||
vrb_map,
|
||||
cand->alloc_slbitmap,
|
||||
&rbStart,
|
||||
&rbSize))
|
||||
continue;
|
||||
|
||||
COMMIT_ALLOC(params, cand, rbStart, needed_rbs, cand->sched_pdsch.mcs, n_scheduled);
|
||||
}
|
||||
|
||||
/* Phase 2: No-data UEs (TA command or beam switch MAC CE, no RLC data) */
|
||||
for (int j = 0; j < n_active; j++) {
|
||||
nr_dl_candidate_t *cand = order[j];
|
||||
if (cand->is_retx || cand->pending_bytes > 0)
|
||||
continue;
|
||||
|
||||
uint16_t *vrb_map = params->vrb_map[cand->alloc_beam_idx];
|
||||
int rbStart, rbSize;
|
||||
if (!get_rb_alloc(min_rbSize,
|
||||
cand->bwp_size,
|
||||
cand->bwp_start,
|
||||
cand->bwp_size,
|
||||
vrb_map,
|
||||
cand->alloc_slbitmap,
|
||||
&rbStart,
|
||||
&rbSize))
|
||||
continue;
|
||||
|
||||
COMMIT_ALLOC(params, cand, rbStart, min_rbSize, cand->sched_pdsch.mcs, n_scheduled);
|
||||
}
|
||||
|
||||
/* Phase 3: New data UEs — PF priority order, largest free block */
|
||||
for (int j = 0; j < n_active; j++) {
|
||||
nr_dl_candidate_t *cand = order[j];
|
||||
if (cand->is_retx || cand->pending_bytes == 0)
|
||||
continue;
|
||||
|
||||
int rbStart;
|
||||
uint16_t *vrb_map = params->vrb_map[cand->alloc_beam_idx];
|
||||
int max_rbSize = find_largest_free_block(vrb_map, cand->alloc_slbitmap, cand->bwp_start, cand->bwp_size, &rbStart);
|
||||
if (max_rbSize < min_rbSize)
|
||||
continue;
|
||||
|
||||
int mcs = cand->sched_pdsch.mcs;
|
||||
uint8_t Qm = nr_get_Qm_dl(mcs, cand->mcs_table);
|
||||
uint16_t R = nr_get_code_rate_dl(mcs, cand->mcs_table);
|
||||
NR_pdsch_dmrs_t dmrs = get_dl_dmrs_params(params->mac->common_channels->ServingCellConfigCommon,
|
||||
&cand->UE->current_DL_BWP,
|
||||
&cand->sched_pdsch.tda_info,
|
||||
cand->sched_pdsch.nrOfLayers);
|
||||
const int oh = 3 * 4 + (cand->UE->UE_sched_ctrl.ta_apply ? 2 : 0);
|
||||
uint32_t tbs;
|
||||
uint16_t rbSize;
|
||||
nr_find_nb_rb(Qm,
|
||||
R,
|
||||
1,
|
||||
cand->sched_pdsch.nrOfLayers,
|
||||
cand->sched_pdsch.tda_info.nrOfSymbols,
|
||||
dmrs.N_PRB_DMRS * dmrs.N_DMRS_SLOT,
|
||||
cand->pending_bytes + oh,
|
||||
min_rbSize,
|
||||
max_rbSize,
|
||||
&tbs,
|
||||
&rbSize);
|
||||
|
||||
COMMIT_ALLOC(params, cand, rbStart, rbSize, mcs, n_scheduled);
|
||||
}
|
||||
|
||||
return n_scheduled;
|
||||
}
|
||||
|
||||
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])
|
||||
{
|
||||
(void)mac;
|
||||
(void)tbs_available;
|
||||
memset(lcid_alloc, 0, NR_MAX_NUM_LCID * sizeof(int));
|
||||
for (int lcid = 0; lcid < NR_MAX_NUM_LCID; lcid++)
|
||||
lcid_alloc[lcid] = candidate->pending_bytes_per_lcid[lcid];
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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)
|
||||
if (!is_xlsch_in_slot(dlsch_slot_bitmap, dlsch_slot_modval, slot_period))
|
||||
return;
|
||||
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_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
|
||||
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)
|
||||
target_dl_Nl)
|
||||
>> 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 */
|
||||
for (int rb = 0; rb < sched_pdsch.rbSize; rb++)
|
||||
|
||||
@@ -702,6 +702,34 @@ bool nr_find_nb_rb(uint16_t Qm,
|
||||
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,
|
||||
int rbSize_max,
|
||||
int bwpStart,
|
||||
@@ -811,41 +839,44 @@ NR_pusch_dmrs_t get_ul_dmrs_params(const NR_ServingCellConfigCommon_t *scc,
|
||||
|
||||
#define BLER_UPDATE_FRAME 10
|
||||
#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)
|
||||
{
|
||||
int mcs = current_mcs;
|
||||
if (bler < bler_lower && mcs < max_mcs && num_sched > 3)
|
||||
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,
|
||||
int max_mcs,
|
||||
frame_t frame)
|
||||
{
|
||||
int diff = frame - bler_stats->last_frame;
|
||||
if (diff < 0) // wrap around
|
||||
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)
|
||||
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_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;
|
||||
|
||||
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->mcs = new_mcs;
|
||||
bler_stats->last_num_sched = num_dl_sched;
|
||||
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",
|
||||
frame, old_mcs, new_mcs, num_dl_sched, num_dl_retx, bler_window, bler_stats->bler);
|
||||
return new_mcs;
|
||||
LOG_D(MAC,
|
||||
"frame %4d BLER update (num_sched %d, num_retx %d, BLER wnd %.3f avg %.6f)\n",
|
||||
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,
|
||||
@@ -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 *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->rnti = rnti;
|
||||
UE->CellGroup = CellGroup;
|
||||
|
||||
@@ -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);
|
||||
|
||||
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,
|
||||
@@ -574,6 +578,10 @@ static void evaluate_rsrp_report(NR_UE_info_t *UE,
|
||||
// including ssb rsrp in mac stats
|
||||
stats->cumul_rsrp += rsrp_report->r[0].RSRP;
|
||||
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)
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#define __LAYER2_NR_MAC_PROTO_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 "common/ngran_types.h"
|
||||
#include "openair2/LAYER2/nr_pdcp/nr_pdcp_configuration.h"
|
||||
@@ -60,8 +61,8 @@ void nr_schedule_ue_spec(module_id_t module_id,
|
||||
nfapi_nr_dl_tti_request_t *DL_req,
|
||||
nfapi_nr_tx_data_request_t *TX_req);
|
||||
|
||||
/* \brief default DL preprocessor init routine, returns preprocessor to call */
|
||||
nr_pp_impl_dl nr_init_dlsch_preprocessor();
|
||||
/* \brief default DL preprocessor */
|
||||
void nr_dlsch_preprocessor(gNB_MAC_INST *mac, post_process_pdsch_t *pp_pdsch);
|
||||
|
||||
void schedule_nr_sib1(module_id_t module_idP,
|
||||
frame_t frameP,
|
||||
@@ -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
|
||||
* \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,
|
||||
int rbSize_max,
|
||||
int bwpStart,
|
||||
@@ -434,12 +437,45 @@ bool get_rb_alloc(int rbSize_min,
|
||||
int *rbStart_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
|
||||
* for the activity-guard threshold and the lower/upper hysteresis. */
|
||||
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);
|
||||
|
||||
bool update_bler_stats(const NR_bler_options_t *bler_options,
|
||||
const NR_mac_dir_stats_t *stats,
|
||||
NR_bler_stats_t *bler_stats,
|
||||
int max_mcs,
|
||||
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);
|
||||
void UL_tti_req_ahead_initialization(gNB_MAC_INST *gNB, int n, int CCid, frame_t frameP, int slotP);
|
||||
uint64_t get_ssb_bitmap(const NR_ServingCellConfigCommon_t *scc);
|
||||
@@ -495,7 +531,11 @@ void prepare_du_configuration_update(gNB_MAC_INST *mac,
|
||||
|
||||
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);
|
||||
|
||||
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
|
||||
float pucch_snr_diff = (pucch_snr * 10.0f - sched_ctrl->pucch_pc.target_snrx10) / 10.0f;
|
||||
output = st_append(output,
|
||||
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->pucch0_DTX,
|
||||
pucch_snr,
|
||||
@@ -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,
|
||||
UE->current_DL_BWP.mcsTableIdx,
|
||||
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) {
|
||||
stats->num_rsrp_meas = 0;
|
||||
stats->cumul_rsrp = 0;
|
||||
@@ -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);
|
||||
|
||||
RC.nrmac[i]->dl_lcid_alloc = nr_dl_lcid_alloc_default;
|
||||
|
||||
if (get_softmodem_params()->phy_test) {
|
||||
RC.nrmac[i]->pre_processor_dl = nr_preprocessor_phytest;
|
||||
RC.nrmac[i]->pre_processor_ul = nr_ul_preprocessor_phytest;
|
||||
} 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]->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)
|
||||
threadCreate(&RC.nrmac[i]->stats_thread,
|
||||
|
||||
@@ -523,6 +523,7 @@ typedef struct NR_bler_stats {
|
||||
float bler;
|
||||
uint8_t mcs;
|
||||
uint64_t rounds[8];
|
||||
int last_num_sched; // scheduling count at last BLER update (for activity guard)
|
||||
} NR_bler_stats_t;
|
||||
|
||||
//
|
||||
@@ -714,6 +715,8 @@ typedef struct NR_mac_dir_stats {
|
||||
uint32_t total_rbs_retx;
|
||||
uint32_t num_mac_sdu;
|
||||
uint32_t current_rbs;
|
||||
uint64_t prev_sdu_bytes;
|
||||
frame_t last_goodput_frame;
|
||||
} NR_mac_dir_stats_t;
|
||||
|
||||
typedef struct NR_mac_stats {
|
||||
@@ -784,8 +787,14 @@ typedef struct NR_UE_info {
|
||||
measgap_config_t measgap_config;
|
||||
// UE selected 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 dl_thr_ue;
|
||||
float dl_thr_ue_display; ///< slow EWMA for stable display (alpha=0.001)
|
||||
long pdsch_HARQ_ACK_Codebook;
|
||||
bool is_redcap;
|
||||
bool reestablish_rlc;
|
||||
@@ -822,7 +831,8 @@ typedef struct {
|
||||
nr_beam_mode_t beam_mode;
|
||||
} 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 {
|
||||
/// current frame
|
||||
@@ -846,6 +856,76 @@ typedef struct {
|
||||
nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_coreset[MAX_NUM_CORESET];
|
||||
} 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 */
|
||||
struct gNB_MAC_INST_s;
|
||||
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_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_resp_t *setup_resp;
|
||||
uint32_t gnb_id; // associated gNB's ID, not used in DU itself
|
||||
@@ -962,6 +1084,17 @@ typedef struct gNB_MAC_INST_s {
|
||||
/// UL preprocessor for differentiated scheduling
|
||||
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_rlc_configuration_t rlc_config;
|
||||
|
||||
@@ -977,7 +1110,7 @@ typedef struct gNB_MAC_INST_s {
|
||||
uint16_t min_grant_prb;
|
||||
bool identity_pm;
|
||||
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];
|
||||
|
||||
/// dedicate UL TDA, common for all UEs
|
||||
|
||||
Reference in New Issue
Block a user