@@ -112,13 +112,80 @@ static unsigned compute_max_nof_rbs_per_ue_per_slot(const ue_repository&
112112 return (bwp_crb_limits.length () / nof_ues_to_be_scheduled_per_slot);
113113}
114114
115+ // / \brief Algorithm to select next UE to allocate in a time-domain RR fashion
116+ // / \param[in] ue_db map of "slot_ue"
117+ // / \param[in] next_ue_index UE index with the highest priority to be allocated.
118+ // / \param[in] alloc_ue callable with signature "alloc_outcome(ue&, pusch_time_domain_resource_index)" that returns the
119+ // / UE allocation outcome.
120+ // / \return Index of next UE to allocate.
121+ template <typename AllocUEFunc>
122+ du_ue_index_t round_robin_ul_apply (const ue_repository& ue_db, du_ue_index_t next_ue_index, const AllocUEFunc& alloc_ue)
123+ {
124+ if (ue_db.empty ()) {
125+ return next_ue_index;
126+ }
127+ auto it = ue_db.lower_bound (next_ue_index);
128+ bool first_alloc = true ;
129+
130+ // NOTE: All UEs use the same PUSCH Time Domain Resource configuration.
131+ const ue& ref_u = **ue_db.begin ();
132+
133+ unsigned max_pusch_time_domain_resource_index = 1 ;
134+ if (ref_u.nof_cells () > 0 ) {
135+ const ue_cell_configuration& ue_cfg = ref_u.get_pcell ().cfg ();
136+ const auto * ss_info =
137+ ue_cfg.find_search_space (ue_cfg.cfg_dedicated ().init_dl_bwp .pdcch_cfg ->search_spaces .back ().get_id ());
138+ if (ss_info == nullptr ) {
139+ return next_ue_index;
140+ }
141+
142+ // [Implementation-defined] For UL heavy TDD configuration we allow multiple UL PDCCH allocations in the same slot
143+ // for same UE but with different k2 values. In other cases (DL heavy) k2 value which is less than or equal to
144+ // minimum value of k1(s) is used. Assumes that first entry in the PUSCH Time Domain resource list contains the k2
145+ // value which is less than or equal to minimum value of k1(s).
146+ if (ue_cfg.cell_cfg_common .is_tdd () and
147+ (nof_full_ul_slots_per_tdd_period (ue_cfg.cell_cfg_common .tdd_cfg_common .value ()) >
148+ nof_full_dl_slots_per_tdd_period (ue_cfg.cell_cfg_common .tdd_cfg_common .value ()))) {
149+ max_pusch_time_domain_resource_index = ss_info->pusch_time_domain_list .size ();
150+ }
151+ }
152+
153+ for (unsigned pusch_td_res_idx = 0 ; pusch_td_res_idx < max_pusch_time_domain_resource_index; ++pusch_td_res_idx) {
154+ for (unsigned count = 0 ; count < ue_db.size (); ++count, ++it) {
155+ if (it == ue_db.end ()) {
156+ // wrap-around
157+ it = ue_db.begin ();
158+ }
159+ const ue& u = **it;
160+ const alloc_outcome alloc_result = alloc_ue (u, pusch_td_res_idx);
161+ if (alloc_result == alloc_outcome::skip_slot) {
162+ // Grid allocator directed policy to stop allocations for this slot.
163+ return next_ue_index;
164+ }
165+
166+ if (alloc_result == alloc_outcome::success) {
167+ if (first_alloc) {
168+ // Mark the next UE to be the first UE to get allocated in the following slot.
169+ // It is important that we equally distribute the opportunity to be the first UE being allocated in a slot for
170+ // all UEs. Otherwise, we could end up in a situation, where a UE is always the last one to be allocated and
171+ // can only use the RBs that were left from the previous UE allocations.
172+ next_ue_index = to_du_ue_index ((unsigned )u.ue_index + 1U );
173+ first_alloc = false ;
174+ }
175+ }
176+ }
177+ }
178+
179+ return next_ue_index;
180+ }
181+
115182// / \brief Algorithm to select next UE to allocate in a time-domain RR fashion
116183// / \param[in] ue_db map of "slot_ue"
117184// / \param[in] next_ue_index UE index with the highest priority to be allocated.
118185// / \param[in] alloc_ue callable with signature "bool(ue&)" that returns true if UE allocation was successful.
119186// / \return Index of next UE to allocate.
120187template <typename AllocUEFunc>
121- du_ue_index_t round_robin_apply (const ue_repository& ue_db, du_ue_index_t next_ue_index, const AllocUEFunc& alloc_ue)
188+ du_ue_index_t round_robin_dl_apply (const ue_repository& ue_db, du_ue_index_t next_ue_index, const AllocUEFunc& alloc_ue)
122189{
123190 if (ue_db.empty ()) {
124191 return next_ue_index;
@@ -252,7 +319,15 @@ static alloc_outcome alloc_ul_ue(const ue& u,
252319 bool is_retx,
253320 bool schedule_sr_only,
254321 srslog::basic_logger& logger,
322+ unsigned pusch_td_res_idx,
255323 optional<unsigned > ul_new_tx_max_nof_rbs_per_ue_per_slot = {})
324+ // static alloc_outcome alloc_ul_ue(const ue& u,
325+ // const ue_resource_grid_view& res_grid,
326+ // ue_pusch_allocator& pusch_alloc,
327+ // bool is_retx,
328+ // bool schedule_sr_only,
329+ // srslog::basic_logger& logger,
330+ // optional<unsigned> ul_new_tx_max_nof_rbs_per_ue_per_slot = {})
256331{
257332 unsigned pending_newtx_bytes = 0 ;
258333 if (not is_retx) {
@@ -270,11 +345,6 @@ static alloc_outcome alloc_ul_ue(const ue& u,
270345 const ue_cell& ue_cc = u.get_cell (to_ue_cell_index (i));
271346 const slot_point pdcch_slot = res_grid.get_pdcch_slot (ue_cc.cell_index );
272347
273- // UE is already allocated resources.
274- if (res_grid.has_ue_ul_pdcch (ue_cc.cell_index , u.crnti )) {
275- return alloc_outcome::skip_ue;
276- }
277-
278348 const cell_configuration& cell_cfg_common = res_grid.get_cell_cfg_common (ue_cc.cell_index );
279349 const ul_harq_process* h = is_retx ? ue_cc.harqs .find_pending_ul_retx () : ue_cc.harqs .find_empty_ul_harq ();
280350 if (h == nullptr ) {
@@ -308,12 +378,14 @@ static alloc_outcome alloc_ul_ue(const ue& u,
308378 continue ;
309379 }
310380
311- // - [Implementation-defined] k2 value which is less than or equal to minimum value of k1(s) is used.
312- // Assumes that first entry in the PUSCH Time Domain resource list contains the k2 value which is less than or
313- // equal to minimum value of k1(s).
314- const unsigned time_res = 0 ;
315- const pusch_time_domain_resource_allocation& pusch_td = ss->pusch_time_domain_list [time_res];
316- const slot_point pusch_slot = pdcch_slot + pusch_td.k2 + cell_cfg_common.ntn_cs_koffset ;
381+ const pusch_time_domain_resource_allocation& pusch_td = ss->pusch_time_domain_list [pusch_td_res_idx];
382+
383+ // UE is already allocated PUSCH.
384+ if (res_grid.has_ue_ul_grant (ue_cc.cell_index , u.crnti , pusch_td.k2 )) {
385+ return alloc_outcome::skip_ue;
386+ }
387+
388+ const slot_point pusch_slot = pdcch_slot + pusch_td.k2 + cell_cfg_common.ntn_cs_koffset ;
317389 const unsigned start_ul_symbols =
318390 NOF_OFDM_SYM_PER_SLOT_NORMAL_CP - cell_cfg_common.get_nof_ul_symbol_per_slot (pusch_slot);
319391 // If it is a retx, we need to ensure we use a time_domain_resource with the same number of symbols as used for
@@ -401,7 +473,7 @@ static alloc_outcome alloc_ul_ue(const ue& u,
401473 h->id ,
402474 ue_grant_crbs,
403475 pusch_td.symbols ,
404- time_res ,
476+ pusch_td_res_idx ,
405477 ss->cfg ->get_id (),
406478 aggr_lvl,
407479 mcs_prbs.mcs });
@@ -433,7 +505,7 @@ void scheduler_time_rr::dl_sched(ue_pdsch_allocator& pdsch_alloc,
433505 auto retx_ue_function = [this , &res_grid, &pdsch_alloc](const ue& u) {
434506 return alloc_dl_ue (u, res_grid, pdsch_alloc, true , logger);
435507 };
436- next_dl_ue_index = round_robin_apply (ues, next_dl_ue_index, retx_ue_function);
508+ next_dl_ue_index = round_robin_dl_apply (ues, next_dl_ue_index, retx_ue_function);
437509
438510 // Then, schedule new transmissions.
439511 const unsigned dl_new_tx_max_nof_rbs_per_ue_per_slot =
@@ -442,7 +514,7 @@ void scheduler_time_rr::dl_sched(ue_pdsch_allocator& pdsch_alloc,
442514 auto tx_ue_function = [this , &res_grid, &pdsch_alloc, dl_new_tx_max_nof_rbs_per_ue_per_slot](const ue& u) {
443515 return alloc_dl_ue (u, res_grid, pdsch_alloc, false , logger, dl_new_tx_max_nof_rbs_per_ue_per_slot);
444516 };
445- next_dl_ue_index = round_robin_apply (ues, next_dl_ue_index, tx_ue_function);
517+ next_dl_ue_index = round_robin_dl_apply (ues, next_dl_ue_index, tx_ue_function);
446518 }
447519}
448520
@@ -451,24 +523,26 @@ void scheduler_time_rr::ul_sched(ue_pusch_allocator& pusch_alloc,
451523 const ue_repository& ues)
452524{
453525 // First schedule UL data re-transmissions.
454- auto data_retx_ue_function = [this , &res_grid, &pusch_alloc](const ue& u) {
455- return alloc_ul_ue (u, res_grid, pusch_alloc, true , false , logger);
526+ auto data_retx_ue_function = [this , &res_grid, &pusch_alloc](const ue& u, unsigned pusch_td_res_idx ) {
527+ return alloc_ul_ue (u, res_grid, pusch_alloc, true , false , logger, pusch_td_res_idx );
456528 };
457- next_ul_ue_index = round_robin_apply (ues, next_ul_ue_index, data_retx_ue_function);
529+ next_ul_ue_index = round_robin_ul_apply (ues, next_ul_ue_index, data_retx_ue_function);
458530
459531 // Then, schedule all pending SR.
460- auto sr_ue_function = [this , &res_grid, &pusch_alloc](const ue& u) {
461- return alloc_ul_ue (u, res_grid, pusch_alloc, false , true , logger);
532+ auto sr_ue_function = [this , &res_grid, &pusch_alloc](const ue& u, unsigned pusch_td_res_idx ) {
533+ return alloc_ul_ue (u, res_grid, pusch_alloc, false , true , logger, pusch_td_res_idx );
462534 };
463- next_ul_ue_index = round_robin_apply (ues, next_ul_ue_index, sr_ue_function);
535+ next_ul_ue_index = round_robin_ul_apply (ues, next_ul_ue_index, sr_ue_function);
464536
465537 // Finally, schedule UL data new transmissions.
466538 const unsigned ul_new_tx_max_nof_rbs_per_ue_per_slot =
467539 compute_max_nof_rbs_per_ue_per_slot (ues, false , res_grid, expert_cfg);
468540 if (ul_new_tx_max_nof_rbs_per_ue_per_slot > 0 ) {
469- auto data_tx_ue_function = [this , &res_grid, &pusch_alloc, ul_new_tx_max_nof_rbs_per_ue_per_slot](const ue& u) {
470- return alloc_ul_ue (u, res_grid, pusch_alloc, false , false , logger, ul_new_tx_max_nof_rbs_per_ue_per_slot);
471- };
472- next_ul_ue_index = round_robin_apply (ues, next_ul_ue_index, data_tx_ue_function);
541+ auto data_tx_ue_function =
542+ [this , &res_grid, &pusch_alloc, ul_new_tx_max_nof_rbs_per_ue_per_slot](const ue& u, unsigned pusch_td_res_idx) {
543+ return alloc_ul_ue (
544+ u, res_grid, pusch_alloc, false , false , logger, pusch_td_res_idx, ul_new_tx_max_nof_rbs_per_ue_per_slot);
545+ };
546+ next_ul_ue_index = round_robin_ul_apply (ues, next_ul_ue_index, data_tx_ue_function);
473547 }
474548}
0 commit comments