@@ -68,6 +68,23 @@ static crb_interval msg3_vrb_to_crb(const cell_configuration& cell_cfg, vrb_inte
6868 cell_cfg.ul_cfg_common .init_ul_bwp .generic_params .crbs .start ());
6969}
7070
71+ class ra_scheduler ::msg3_harq_timeout_notifier final : public harq_timeout_notifier
72+ {
73+ public:
74+ msg3_harq_timeout_notifier (std::vector<pending_msg3_t >& pending_msg3s_) : pending_msg3s(pending_msg3s_) {}
75+
76+ void on_harq_timeout (du_ue_index_t ue_idx, bool is_dl, bool ack) override
77+ {
78+ srsran_sanity_check (pending_msg3s[ue_idx].busy (), " timeout called but HARQ entity does not exist" );
79+
80+ // Delete Msg3 HARQ entity to make it available again.
81+ pending_msg3s[ue_idx].msg3_harq_ent .reset ();
82+ }
83+
84+ private:
85+ std::vector<pending_msg3_t >& pending_msg3s;
86+ };
87+
7188// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
7289
7390ra_scheduler::ra_scheduler (const scheduler_ra_expert_config& sched_cfg_,
@@ -87,6 +104,7 @@ ra_scheduler::ra_scheduler(const scheduler_ra_expert_config& sched_cfg_,
87104 band_helper::get_duplex_mode(cell_cfg.band),
88105 cell_cfg.ul_cfg_common.init_ul_bwp.rach_cfg_common->rach_cfg_generic.prach_config_index)
89106 .format)),
107+ msg3_harqs(MAX_NOF_MSG3, 1 , std::make_unique<msg3_harq_timeout_notifier>(pending_msg3s)),
90108 pending_msg3s(MAX_NOF_MSG3)
91109{
92110 // Precompute RAR PDSCH and DCI PDUs.
@@ -171,8 +189,7 @@ void ra_scheduler::precompute_msg3_pdus()
171189 crb_interval{0 , prbs_tbs.nof_prbs },
172190 i,
173191 sched_cfg.msg3_mcs_index ,
174- msg3_rv,
175- dummy_h_ul);
192+ msg3_rv);
176193
177194 // Note: RNTI will be overwritten later.
178195 build_pusch_f0_0_tc_rnti (msg3_data[i].pusch ,
@@ -246,17 +263,19 @@ void ra_scheduler::handle_rach_indication_impl(const rach_indication_message& ms
246263 prach_preamble.time_advance .to_Ta (get_ul_bwp_cfg ().scs )});
247264
248265 // Check if TC-RNTI value to be scheduled is already under use
249- if (not pending_msg3s[to_value (prach_preamble.tc_rnti ) % MAX_NOF_MSG3].harq .empty ()) {
266+ unsigned msg3_ring_idx = to_value (prach_preamble.tc_rnti ) % MAX_NOF_MSG3;
267+ auto & msg3_entry = pending_msg3s[msg3_ring_idx];
268+ if (msg3_entry.busy ()) {
250269 logger.warning (" PRACH ignored, as the allocated TC-RNTI={} is already under use" , prach_preamble.tc_rnti );
251270 continue ;
252271 }
253272
254273 // Store TC-RNTI of the preamble.
255274 rar_req->tc_rntis .emplace_back (prach_preamble.tc_rnti );
256275
257- // Store Msg3 to allocate .
258- pending_msg3s[ to_value (prach_preamble. tc_rnti ) % MAX_NOF_MSG3]. preamble = prach_preamble;
259- pending_msg3s[ to_value (prach_preamble. tc_rnti ) % MAX_NOF_MSG3]. msg3_harq_logger . set_rnti ( prach_preamble.tc_rnti );
276+ // Store Msg3 request and create a HARQ entity of 1 UL HARQ .
277+ msg3_entry. preamble = prach_preamble;
278+ msg3_entry. msg3_harq_ent = msg3_harqs. add_ue ( to_du_ue_index (msg3_ring_idx), prach_preamble.tc_rnti , 1 , 1 );
260279 }
261280 }
262281}
@@ -274,41 +293,51 @@ void ra_scheduler::handle_pending_crc_indications_impl(cell_resource_allocator&
274293
275294 for (const ul_crc_indication& crc_ind : new_crc_inds) {
276295 for (const ul_crc_pdu_indication& crc : crc_ind.crcs ) {
277- srsran_assert (crc.ue_index == INVALID_DU_UE_INDEX, " Msg3 HARQ CRCs cannot have a ueId assigned yet" );
296+ srsran_assert (crc.ue_index == INVALID_DU_UE_INDEX, " Msg3 HARQ CRCs cannot have a ue index assigned yet" );
278297 auto & pending_msg3 = pending_msg3s[to_value (crc.rnti ) % MAX_NOF_MSG3];
279- if (pending_msg3.preamble .tc_rnti != crc.rnti ) {
280- logger.warning (" Invalid UL CRC, cell={}, rnti={}, h_id={}. Cause: Nonexistent rnti. " ,
298+ if (pending_msg3.preamble .tc_rnti != crc.rnti or pending_msg3. msg3_harq_ent . empty () ) {
299+ logger.warning (" Invalid UL CRC, cell={}, rnti={}, h_id={}. Cause: Nonexistent tc- rnti" ,
281300 cell_cfg.cell_index ,
282301 crc.rnti ,
283302 crc.harq_id );
284303 continue ;
285304 }
286- if (pending_msg3.harq .id != crc.harq_id ) {
287- logger.warning (" Invalid UL CRC, cell={}, rnti={}, h_id={}. Cause: HARQ-Ids do not match ({} != {})" ,
305+
306+ // See TS38.321, 5.4.2.1 - "For UL transmission with UL grant in RA Response, HARQ process identifier 0 is used."
307+ harq_id_t h_id = to_harq_id (0 );
308+ std::optional<ul_harq_process_handle> h_ul = pending_msg3.msg3_harq_ent .ul_harq (h_id);
309+ if (not h_ul.has_value () or crc.harq_id != h_id) {
310+ logger.warning (" Invalid UL CRC, cell={}, rnti={}, h_id={}. Cause: HARQ-Id 0 must be used in Msg3" ,
288311 cell_cfg.cell_index ,
289312 crc.rnti ,
290- crc.harq_id ,
291- crc.harq_id ,
292- pending_msg3.harq .id );
313+ crc.harq_id );
293314 continue ;
294315 }
295- pending_msg3.harq .crc_info (crc.tb_crc_success );
316+
317+ // Handle CRC info.
318+ h_ul->ul_crc_info (crc.tb_crc_success );
319+ if (h_ul->empty ()) {
320+ // Deallocate Msg3 entry.
321+ pending_msg3.msg3_harq_ent .reset ();
322+ }
296323 }
297324 }
298325
299326 // Allocate pending Msg3 retransmissions.
300- for (auto & pending_msg3 : pending_msg3s) {
301- if (not pending_msg3.harq .empty ()) {
302- pending_msg3.harq .slot_indication (res_alloc.slot_tx ());
303- if (pending_msg3.harq .has_pending_retx ()) {
304- schedule_msg3_retx (res_alloc, pending_msg3);
305- }
306- }
327+ // Note: pending_ul_retxs size will change in this iteration, so we prefetch the next iterator.
328+ auto pending_ul_retxs = msg3_harqs.pending_ul_retxs ();
329+ for (auto it = pending_ul_retxs.begin (); it != pending_ul_retxs.end ();) {
330+ ul_harq_process_handle h_ul = *it;
331+ ++it;
332+ schedule_msg3_retx (res_alloc, pending_msg3s[h_ul.ue_index ()]);
307333 }
308334}
309335
310336void ra_scheduler::run_slot (cell_resource_allocator& res_alloc)
311337{
338+ // Update Msg3 HARQ state.
339+ msg3_harqs.slot_indication (res_alloc.slot_tx ());
340+
312341 // Handle pending CRCs, which may lead to Msg3 reTxs.
313342 handle_pending_crc_indications_impl (res_alloc);
314343
@@ -648,10 +677,12 @@ void ra_scheduler::fill_rar_grant(cell_resource_allocator& res_alloc,
648677 const vrb_interval vrbs = msg3_crb_to_vrb (cell_cfg, msg3_candidate.crbs );
649678
650679 auto & pending_msg3 = pending_msg3s[to_value (rar_request.tc_rntis [i]) % MAX_NOF_MSG3];
651- srsran_sanity_check (pending_msg3.harq . empty (), " Pending Msg3 should not have been added if HARQ is busy " );
680+ srsran_sanity_check (pending_msg3.busy (), " Pending Msg3 entry should have been reserved when RACH was received " );
652681
653682 // Allocate Msg3 UL HARQ
654- pending_msg3.harq .new_tx (msg3_alloc.slot , sched_cfg.max_nof_msg3_harq_retxs );
683+ std::optional<ul_harq_process_handle> h_ul =
684+ pending_msg3.msg3_harq_ent .alloc_ul_harq (msg3_alloc.slot , sched_cfg.max_nof_msg3_harq_retxs );
685+ srsran_sanity_check (h_ul.has_value (), " Pending Msg3 HARQ must be available when RAR is allocated" );
655686
656687 // Add MAC SDU with UL grant (Msg3) in RAR PDU.
657688 rar_ul_grant& msg3_info = rar.grants .emplace_back ();
@@ -683,7 +714,7 @@ void ra_scheduler::fill_rar_grant(cell_resource_allocator& res_alloc,
683714 pusch.pusch_cfg .new_data = true ;
684715
685716 // Store parameters used in HARQ.
686- pending_msg3. harq . save_alloc_params (ul_harq_sched_context {dci_ul_rnti_config_type::tc_rnti_f0_0}, pusch.pusch_cfg );
717+ h_ul-> save_grant_params (ul_harq_alloc_context {dci_ul_rnti_config_type::tc_rnti_f0_0}, pusch.pusch_cfg );
687718 }
688719}
689720
@@ -703,6 +734,10 @@ void ra_scheduler::schedule_msg3_retx(cell_resource_allocator& res_alloc, pendin
703734 return ;
704735 }
705736
737+ ul_harq_process_handle h_ul = msg3_ctx.msg3_harq_ent .ul_harq (to_harq_id (0 )).value ();
738+ srsran_sanity_check (h_ul.has_pending_retx (), " schedule_msg3_retx called when HARQ has no pending reTx" );
739+ const ul_harq_process_handle::grant_params& last_harq_params = h_ul.get_grant_params ();
740+
706741 const span<const pusch_time_domain_resource_allocation> pusch_td_alloc_list =
707742 get_pusch_time_domain_resource_table (get_pusch_cfg ());
708743 for (unsigned pusch_td_res_index = 0 ; pusch_td_res_index != pusch_td_alloc_list.size (); ++pusch_td_res_index) {
@@ -713,8 +748,7 @@ void ra_scheduler::schedule_msg3_retx(cell_resource_allocator& res_alloc, pendin
713748 NOF_OFDM_SYM_PER_SLOT_NORMAL_CP - cell_cfg.get_nof_ul_symbol_per_slot (pusch_alloc.slot );
714749 // If it is a retx, we need to ensure we use a time_domain_resource with the same number of symbols as used for
715750 // the first transmission.
716- const bool sym_length_match_prev_grant_for_retx =
717- pusch_td_cfg.symbols .length () == msg3_ctx.harq .last_tx_params ().nof_symbols ;
751+ const bool sym_length_match_prev_grant_for_retx = pusch_td_cfg.symbols .length () == last_harq_params.nof_symbols ;
718752 if (not cell_cfg.is_ul_enabled (pusch_alloc.slot ) or pusch_td_cfg.symbols .start () < start_ul_symbols or
719753 !sym_length_match_prev_grant_for_retx) {
720754 // Not possible to schedule Msg3s in this TDD slot.
@@ -728,7 +762,7 @@ void ra_scheduler::schedule_msg3_retx(cell_resource_allocator& res_alloc, pendin
728762 }
729763
730764 // Try to reuse previous HARQ PRBs.
731- const vrb_interval msg3_vrbs = msg3_ctx. harq . last_tx_params () .rbs .type1 ();
765+ const vrb_interval msg3_vrbs = last_harq_params .rbs .type1 ();
732766 grant_info grant;
733767 grant.scs = bwp_ul_cmn.scs ;
734768 grant.symbols = pusch_td_cfg.symbols ;
@@ -764,7 +798,10 @@ void ra_scheduler::schedule_msg3_retx(cell_resource_allocator& res_alloc, pendin
764798 pusch_alloc.ul_res_grid .fill (grant);
765799
766800 // Allocate new retx in the HARQ.
767- msg3_ctx.harq .new_retx (pusch_alloc.slot );
801+ if (not h_ul.new_retx (pusch_alloc.slot )) {
802+ logger.warning (" tc-rnti={}: Failed to allocate reTx for Msg3" , msg3_ctx.preamble .tc_rnti );
803+ continue ;
804+ }
768805
769806 // Fill DCI.
770807 static constexpr uint8_t msg3_rv = 0 ;
@@ -773,24 +810,23 @@ void ra_scheduler::schedule_msg3_retx(cell_resource_allocator& res_alloc, pendin
773810 cell_cfg.ul_cfg_common .init_ul_bwp .generic_params ,
774811 grant.crbs ,
775812 pusch_td_res_index,
776- msg3_ctx.harq .last_tx_params ().mcs ,
777- msg3_rv,
778- msg3_ctx.harq );
813+ last_harq_params.mcs ,
814+ msg3_rv);
779815
780816 // Fill PUSCH.
781817 ul_sched_info& ul_info = pusch_alloc.result .ul .puschs .emplace_back ();
782818 ul_info.context .ue_index = INVALID_DU_UE_INDEX;
783819 ul_info.context .ss_id = cell_cfg.dl_cfg_common .init_dl_bwp .pdcch_common .ra_search_space_id ;
784820 ul_info.context .k2 = k2;
785- ul_info.context .nof_retxs = msg3_ctx. harq . tb (). nof_retxs ;
821+ ul_info.context .nof_retxs = h_ul. nof_retxs () ;
786822 ul_info.pusch_cfg = msg3_data[pusch_td_res_index].pusch ;
787823 ul_info.pusch_cfg .rnti = msg3_ctx.preamble .tc_rnti ;
788824 ul_info.pusch_cfg .rbs = msg3_vrbs;
789825 ul_info.pusch_cfg .rv_index = pdcch->dci .tc_rnti_f0_0 .redundancy_version ;
790826 ul_info.pusch_cfg .new_data = false ;
791827
792828 // Store parameters used in HARQ.
793- msg3_ctx. harq . save_alloc_params (ul_harq_sched_context {dci_ul_rnti_config_type::tc_rnti_f0_0}, ul_info.pusch_cfg );
829+ h_ul. save_grant_params (ul_harq_alloc_context {dci_ul_rnti_config_type::tc_rnti_f0_0}, ul_info.pusch_cfg );
794830
795831 // successful allocation. Exit loop.
796832 break ;
0 commit comments