From c24673798a3cfd26c5481a9c488c16a7e269a604 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 23 Jul 2025 11:54:23 +0200 Subject: [PATCH] Handle TDD pattern 2 in UL TDA allocation Add the possibility to use UL slots in TDD pattern 2. For simplicity, we require the mixed slot to be of the same symbols in pattern 1 & 2. For UL slots, simply use the right amount of UL TDAs, depending on which number of UL slots among pattern 1 & 2 is bigger. One complication is the TDA for the mixed slot. The current code assumes that k2 has to be equal or larger than (number of DL slots +1) (because the spec says min k2=2, but we might want to schedule DSUUU). Then, there could be for instance the following cases: - only pattern 1: DSUUU DSUUU ...: the mixed slot has to be reached an entire period in advance - pattern 1 & 2: DSUUU DDDDD: normal mixed slot k2, e.g., like for 7DS2U - pattern 1 & 2: DSUUU DDDSU: the mixed slot has to be reached N_ul2+N_dl1+1 slots in advance I can't think of more cases right now. To avoid unscheduled slots, I put an assertion (which could also trivially be taken out). --- openair2/GNB_APP/gnb_config.c | 4 -- openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c | 45 +++++++++++--------- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index 57086db62a..c2e56e7eb2 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -1206,10 +1206,6 @@ static NR_ServingCellConfigCommon_t *get_scc_config(configmodule_interface_t *cf struct NR_TDD_UL_DL_ConfigCommon *tdd = scc->tdd_UL_DL_ConfigurationCommon; tdd->pattern2 = calloc_or_fail(1, sizeof(*tdd->pattern2)); *scc->tdd_UL_DL_ConfigurationCommon->pattern2 = p2; - AssertFatal(p2.nrofUplinkSlots ^ scc->tdd_UL_DL_ConfigurationCommon->pattern1.nrofUplinkSlots, - "UL slots in pattern1 (%ld) and pattern2 (%ld) are mutually exclusive (e.g. DDDFUU DDDD, DDDD DDDFUU)\n", - scc->tdd_UL_DL_ConfigurationCommon->pattern1.nrofUplinkSlots, - p2.nrofUplinkSlots); } struct NR_FrequencyInfoDL *frequencyInfoDL = scc->downlinkConfigCommon->frequencyInfoDL; LOG_I(RRC, diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c b/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c index 2203c0b2b7..e4becc1da3 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c +++ b/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c @@ -1102,44 +1102,49 @@ void nr_rrc_config_ul_tda(NR_ServingCellConfigCommon_t *scc, int min_fb_delay, i } if (scc->tdd_UL_DL_ConfigurationCommon) { - int ul_symb = 0; NR_TDD_UL_DL_Pattern_t *p1 = &scc->tdd_UL_DL_ConfigurationCommon->pattern1; + int ul_symb = p1->nrofUplinkSymbols; NR_TDD_UL_DL_Pattern_t *p2 = scc->tdd_UL_DL_ConfigurationCommon->pattern2; if (p2 && p2->nrofUplinkSymbols) - AssertFatal(p2->nrofUplinkSymbols == p1->nrofUplinkSymbols, - "nrofDownlinkSymbols in pattern1 %ld and pattern2 %ld must be the same in current implementation\n", - p1->nrofUplinkSymbols, + AssertFatal(p2->nrofUplinkSymbols == ul_symb, + "nrofDownlinkSymbols in pattern1 %d and pattern2 %ld must be the same in current implementation\n", + ul_symb, p2->nrofUplinkSymbols); - if (p1->nrofUplinkSymbols) { - ul_symb = p1->nrofUplinkSymbols; - } else if (p2) { - ul_symb = p1->nrofUplinkSymbols; - } + AssertFatal(!p2 || p2->nrofDownlinkSlots >= p2->nrofUplinkSlots, + "nrofDownlinkSlots must be larger than nrofUplinkSlots for pattern2 in current implementation\n"); - DevAssert(p2 == NULL); - int N_dl = p1->nrofDownlinkSlots; - int N_ul = p1->nrofUplinkSlots; + int N_dl1 = p1->nrofDownlinkSlots; + int N_ul1 = p1->nrofUplinkSlots; + int N_dl2 = p2 ? p2->nrofDownlinkSlots : 0; + int N_ul2 = p2 ? p2->nrofUplinkSlots : 0; 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); // to be reached from suitable DL slot + long mixed_sliv = get_SLIV(NR_NUMBER_OF_SYMBOLS_PER_SLOT - ul_symb, ul_symb - 1); + if (ul_symb > 1 && (k2 <= N_dl1 || (p2 && N_ul2 == 0))) { + // UL TDA index 2 for mixed slot (TDD) (no pattern 2 and enough DL slots, or pattern2 only DL) + tda = set_TimeDomainResourceAllocation(k2, mixed_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 + } else if (ul_symb > 1 && !p2 && k2 > N_dl1) { // k2 > N_Dl1 + // UL TDA for mixed slot without pattern 2 and less DL slots than k2 + tda = set_TimeDomainResourceAllocation(nb_slots_per_period, mixed_sliv); // to be reached from last DL (mixed) slot, if any asn1cSeqAdd(&tda_list->list, tda); + } else if (ul_symb > 1 && N_ul2 > 0 && k2 > N_dl1) { + // we have pattern 2 with UL, and less p1 DL slots than k2 + tda = set_TimeDomainResourceAllocation(N_dl1 + N_ul2 + 1, mixed_sliv); + asn1cSeqAdd(&tda_list->list, tda); + } else { + AssertFatal(ul_symb == 0, "unhandled case of mixed slot: N_dl1 %d N_ul1 %d N_dl2 %d N_ul2 %d\n", N_dl1, N_ul1, N_dl2, N_ul2); } // make TDA for UL slots that are not reachable within k2/min_rxtxtime + int N_ul = max(N_ul1, N_ul2); 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); + AssertFatal(N_dl1 >= k2 - 1, "cannot fulfil TDD pattern: N_dl1 %d, k2 %d\n", N_dl1, k2); for (int i = k2 + 1; i <= N_ul; ++i) { tda = set_TimeDomainResourceAllocation(i, get_SLIV(0, 13)); asn1cSeqAdd(&tda_list->list, tda);