From cf8099fab19c242996bc32bf0f551d943429245b Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 23 Jul 2025 10:50:45 +0200 Subject: [PATCH] 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. --- .../LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 71 +++++++++++-------- openair2/LAYER2/NR_MAC_gNB/mac_proto.h | 2 +- openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c | 55 +++++++++++++- 3 files changed, 96 insertions(+), 32 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index 93db0d1945..4f55f73db1 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -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 diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h index 91c0969dde..9caa78bf7e 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h @@ -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, diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c b/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c index d780cf4fe7..22d3b55fa4 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c +++ b/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c @@ -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)