Refactor TDA allocation: scheduler can take advantage of multiple k2

Add multiple TDAs with k2 larger than the min_rxtxtime, to potentially
allow to allocate all available slots. For this, nr_rrc_config_ul_tda()
inspects the TDD pattern, and allocates as many TDAs as necessary. It
also sorts the TDA list, with smaller k2 and larger symbol lengths
first.

This in turn is now used by get_num_ul_tda() to use a k2 and the slot
configuration to find a suitable TDA, assuming a list as sorted in
nr_rrc_config_ul_tda().

Note that since get_num_ul_tda() does only inspect the available UL
slots, there might be overlap (e.g., when scheduling SRS), which will be
fixed in a follow-up commit.

Also, TDD pattern 2 is not handled here yet, and a follow-up commit will
fix it.
This commit is contained in:
Robert Schmidt
2025-08-14 12:50:03 +02:00
parent 3102068e71
commit cf8099fab1
3 changed files with 96 additions and 32 deletions
@@ -38,45 +38,58 @@
//#define SRS_IND_DEBUG
/* \brief Get the number of UL TDAs that could be used in frame and slot, reachable
/* \brief Get the number of UL TDAs that could be used in slot, reachable
* via specific k2. The output parameter first_idx is a pointer to the first
* suitable TDA, and the function returns the number of suitable TDAs, or 0. */
int get_num_ul_tda(gNB_MAC_INST *nrmac, int frame, int slot, int k2, const NR_tda_info_t **first_idx)
int get_num_ul_tda(gNB_MAC_INST *nrmac, int slot, int k2, const NR_tda_info_t **first_idx)
{
/* we assume that this function is mutex-protected from outside */
NR_SCHED_ENSURE_LOCKED(&nrmac->sched_lock);
/* there is a mixed slot only when in TDD */
frame_structure_t *fs = &nrmac->frame_structure;
const frame_structure_t *fs = &nrmac->frame_structure;
const int slot_period = slot % fs->numb_slots_period;
const tdd_bitmap_t *bm = &fs->period_cfg.tdd_slot_bitmap[slot_period];
/* For some reason, we only store the number of symbols if it's mixed */
const int num_ul_symbols = bm->slot_type == TDD_NR_MIXED_SLOT ? bm->num_ul_symbols : 14;
const uint16_t ul_bitmap = SL_to_bitmap(14 - num_ul_symbols, num_ul_symbols);
/* TODO: we assume currently that all UL TDA have same k2, check below */
NR_tda_info_t *tda_info = seq_arr_at(&nrmac->ul_tda, 0);
if (!tda_info->valid_tda || tda_info->k2 != k2)
*first_idx = NULL;
FOR_EACH_SEQ_ARR(NR_tda_info_t *, tda, &nrmac->ul_tda) {
DevAssert(tda->valid_tda);
// nr_rrc_config_ul_tda() orders by k2, so skip smaller and return for
// bigger ones
if (tda->k2 < k2)
continue;
if (tda->k2 > k2)
break; // there won't be a suitable k2 anymore
uint16_t tda_bitmap = SL_to_bitmap(tda->startSymbolIndex, tda->nrOfSymbols);
// nr_rrc_config_ul_tda() assures TDAs are from largest to smallest symbol number.
// check that this TDA fits entirely into this slot's mask (slot might be
// smaller in mixed slots)., A mixed slot TDA would be checked last, so a
// full slot TDA will be used for a full UL slot.
if ((tda_bitmap & ul_bitmap) == tda_bitmap) { // if TDA fits entiry
*first_idx = tda;
break;
}
}
if (*first_idx == NULL) /* nothing fit */
return 0;
if (fs->frame_type == TDD) {
// if there is uplink symbols in mixed slot
int s = get_slot_idx_in_period(slot, fs);
tdd_bitmap_t *tdd_slot_bitmap = fs->period_cfg.tdd_slot_bitmap;
if ((tdd_slot_bitmap[s].num_ul_symbols > 1) && is_mixed_slot(s, fs)) {
*first_idx = seq_arr_at(&nrmac->ul_tda, 2); // mixed slot, all symbols of mixed minus last
return 1;
}
NR_tda_info_t *end_it = seq_arr_next(&nrmac->ul_tda, *first_idx);
while (end_it != seq_arr_end(&nrmac->ul_tda) && end_it->k2 == k2) {
/* the following TDAs should all fit as long as the k2 is the same */
uint16_t tda_bitmap = SL_to_bitmap(end_it->startSymbolIndex, end_it->nrOfSymbols);
AssertFatal((tda_bitmap & ul_bitmap) == tda_bitmap,
"TDA should fit inside slot, but is not the case for k2 %ld bitmap 0x%04x\n",
end_it->k2,
tda_bitmap);
end_it = seq_arr_next(&nrmac->ul_tda, end_it);
}
// Avoid slots with the SRS
UE_iterator(nrmac->UE_info.connected_ue_list, UE) {
NR_sched_srs_t sched_srs = UE->UE_sched_ctrl.sched_srs;
if(sched_srs.srs_scheduled && sched_srs.frame == frame && sched_srs.slot == slot) {
*first_idx = seq_arr_at(&nrmac->ul_tda, 1); // symbols 0--12
return 1;
}
}
// symbols 0--13, if FDD or not mixed slot in TDD, for now use default TDA
// (TODO handle CSI-RS slots)
*first_idx = seq_arr_at(&nrmac->ul_tda, 0);
return 1;
ptrdiff_t diff = seq_arr_dist(&nrmac->ul_tda, *first_idx, end_it);
AssertFatal(diff > 0 && diff <= seq_arr_size(&nrmac->ul_tda), "dist %ld\n", diff);
return diff;
}
bwp_info_t get_pusch_bwp_start_size(NR_UE_info_t *UE)
@@ -2607,7 +2620,7 @@ static void nr_ulsch_preprocessor(gNB_MAC_INST *nr_mac, post_process_pusch_t *pp
int k2 = fs_get_diff(fs, *next, current) - koffset;
DevAssert(k2 > 0);
const NR_tda_info_t *tda_info = NULL;
int n_tda = get_num_ul_tda(nr_mac, next->f, next->s, k2, &tda_info);
int n_tda = get_num_ul_tda(nr_mac, next->s, k2, &tda_info);
if (n_tda == 0) /* no TDA fulfills this */
break;
/* TODO: get_num_ul_tda() might indicate multiple TDAs to use (e.g., one
+1 -1
View File
@@ -417,7 +417,7 @@ void free_sched_pucch_list(NR_UE_sched_ctrl_t *sched_ctrl);
bool add_UE_to_list(int list_size, NR_UE_info_t *list[list_size], NR_UE_info_t *UE);
NR_UE_info_t *remove_UE_from_list(int list_size, NR_UE_info_t *list[list_size], rnti_t rnti);
int get_dl_tda(const gNB_MAC_INST *nrmac, int slot);
int get_num_ul_tda(gNB_MAC_INST *nrmac, int frame, int slot, int k2, const NR_tda_info_t **first_idx);
int get_num_ul_tda(gNB_MAC_INST *nrmac, int slot, int k2, const NR_tda_info_t **first_idx);
int get_cce_index(const gNB_MAC_INST *nrmac,
const int CC_id,
+53 -2
View File
@@ -1058,6 +1058,29 @@ static struct NR_PUSCH_TimeDomainResourceAllocation *set_TimeDomainResourceAlloc
return puschTdrAlloc;
}
static int tda_cmp(const void *tda_a, const void *tda_b)
{
const NR_PUSCH_TimeDomainResourceAllocation_t *a = *(const NR_PUSCH_TimeDomainResourceAllocation_t **)tda_a;
const NR_PUSCH_TimeDomainResourceAllocation_t *b = *(const NR_PUSCH_TimeDomainResourceAllocation_t **)tda_b;
DevAssert(a->k2 && b->k2);
if (*a->k2 < *b->k2)
return -1; // smaller first
if (*a->k2 > *b->k2)
return 1;
// same k2: order from big to small TDA number
int as, al, bs, bl;
SLIV2SL(a->startSymbolAndLength, &as, &al);
SLIV2SL(b->startSymbolAndLength, &bs, &bl);
if (al < bl)
return 1; // bigger first
if (al > bl)
return -1;
return 0;
}
/* \brief Set up a list of time domain allocations as suitable for the TDD
* pattern. This will be used by get_num_ul_tda(), which requires a specific
* ordering, hence we qsort() the list at the end according to tda_cmp(). */
void nr_rrc_config_ul_tda(NR_ServingCellConfigCommon_t *scc, int min_fb_delay)
{
NR_PUSCH_TimeDomainResourceAllocationList_t *tda_list =
@@ -1065,6 +1088,7 @@ void nr_rrc_config_ul_tda(NR_ServingCellConfigCommon_t *scc, int min_fb_delay)
AssertFatal(tda_list->list.count == 0, "already have pusch_TimeDomainAllocationList members\n");
const int k2 = min_fb_delay;
const int mu = scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.subcarrierSpacing;
// UL TDA index 0 is basic slot configuration starting in symbol 0 til the last but one symbol
NR_PUSCH_TimeDomainResourceAllocation_t *tda;
@@ -1089,13 +1113,40 @@ void nr_rrc_config_ul_tda(NR_ServingCellConfigCommon_t *scc, int min_fb_delay)
} else if (p2) {
ul_symb = p1->nrofUplinkSymbols;
}
if (ul_symb>1) {
DevAssert(p2 == NULL);
int N_dl = p1->nrofDownlinkSlots;
int N_ul = p1->nrofUplinkSlots;
int tdd_period_idx = get_tdd_period_idx(scc->tdd_UL_DL_ConfigurationCommon);
int nb_periods_per_frame = get_nb_periods_per_frame(tdd_period_idx);
int nb_slots_per_period = ((1 << mu) * 10) / nb_periods_per_frame;
// make TDA for the mixed slot
if (ul_symb > 1 && k2 <= N_dl) {
// UL TDA index 2 for mixed slot (TDD)
long sliv = get_SLIV(NR_NUMBER_OF_SYMBOLS_PER_SLOT - ul_symb, ul_symb - 1);
tda = set_TimeDomainResourceAllocation(k2, sliv);
tda = set_TimeDomainResourceAllocation(k2, sliv); // to be reached from suitable DL slot
asn1cSeqAdd(&tda_list->list, tda);
} else if (ul_symb > 1) { // k2 > N_Dl
long sliv = get_SLIV(NR_NUMBER_OF_SYMBOLS_PER_SLOT - ul_symb, ul_symb - 1);
tda = set_TimeDomainResourceAllocation(nb_slots_per_period, sliv); // to be reached from last DL (mixed) slot, if any
asn1cSeqAdd(&tda_list->list, tda);
}
// make TDA for UL slots that are not reachable within k2/min_rxtxtime
if (N_ul > k2) {
// enforce that with k2, we could reach all but one slot (which is mixed
// and taken into account above
AssertFatal(N_dl >= k2 - 1, "cannot fulfil TDD pattern: N_dl %d, k2 %d\n", N_dl, k2);
for (int i = k2 + 1; i <= N_ul; ++i) {
tda = set_TimeDomainResourceAllocation(i, get_SLIV(0, 13));
asn1cSeqAdd(&tda_list->list, tda);
}
}
}
AssertFatal(tda_list->list.count <= 16, "cannot reach all UL slots with current TDD configuration\n");
qsort(tda_list->list.array, tda_list->list.count, sizeof(tda_list->list.array), tda_cmp);
}
static void set_dl_DataToUL_ACK(NR_PUCCH_Config_t *pucch_Config, int min_feedback_time, NR_SubcarrierSpacing_t subcarrierSpacing)