Skip to content

Commit 8f4e270

Browse files
committed
sched: implementation of NTN HARQ history
1 parent d125d73 commit 8f4e270

File tree

2 files changed

+211
-12
lines changed

2 files changed

+211
-12
lines changed

lib/scheduler/cell/cell_harq_manager.cpp

Lines changed: 198 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,144 @@ class noop_harq_timeout_notifier : public harq_timeout_notifier
2525
}
2626
};
2727

28+
template <bool IsDl>
29+
class base_ntn_harq_history
30+
{
31+
using harq_impl_type = std::conditional_t<IsDl, dl_harq_process_impl, ul_harq_process_impl>;
32+
33+
public:
34+
base_ntn_harq_history(cell_harq_repository<IsDl>& parent_, unsigned ntn_cs_koffset_) :
35+
harq_pool(parent_), ntn_cs_koffset(ntn_cs_koffset_)
36+
{
37+
srsran_assert(ntn_cs_koffset > 0 and ntn_cs_koffset <= NTN_CELL_SPECIFIC_KOFFSET_MAX, "Invalid NTN koffset");
38+
unsigned ring_size = get_allocator_ring_size_gt_min(ntn_cs_koffset);
39+
history.resize(ring_size);
40+
}
41+
42+
void slot_indication(slot_point sl_tx)
43+
{
44+
last_sl_ind = sl_tx;
45+
// If there are no allocations, we do not let last_sl_ack get further away from last_sl_ind, to avoid ambiguity.
46+
if (not last_sl_ack.valid() or last_sl_ack < last_sl_ind - 1) {
47+
last_sl_ack = last_sl_ind - 1;
48+
}
49+
50+
// Clear old entries.
51+
unsigned idx = get_index(sl_tx - 1);
52+
history[idx].clear();
53+
}
54+
55+
void save_harq_newtx_info(const harq_impl_type& h)
56+
{
57+
unsigned idx = get_index(h.slot_ack);
58+
history[idx].emplace_back(h);
59+
60+
if (h.slot_ack > last_sl_ack) {
61+
last_sl_ack = h.slot_ack;
62+
}
63+
}
64+
65+
void save_harq_retx_info(const harq_impl_type& h, slot_point last_slot_ack)
66+
{
67+
// Clear previous HARQ transmission so it does not count in pending bytes.
68+
unsigned last_idx = get_index(last_slot_ack);
69+
for (unsigned i = 0; i != history[last_idx].size(); ++i) {
70+
if (history[last_idx][i].ue_idx == h.ue_idx and history[last_idx][i].h_id == h.h_id) {
71+
history[last_idx][i].status = harq_state_t::empty;
72+
}
73+
}
74+
75+
save_harq_newtx_info(h);
76+
}
77+
78+
protected:
79+
unsigned get_index(slot_point sl) const { return mod((sl + ntn_cs_koffset).to_uint()); }
80+
unsigned mod(unsigned idx) const { return idx % history.size(); }
81+
82+
cell_harq_repository<IsDl>& harq_pool;
83+
unsigned ntn_cs_koffset;
84+
std::vector<std::vector<harq_impl_type>> history;
85+
86+
slot_point last_sl_ind;
87+
slot_point last_sl_ack;
88+
};
89+
2890
} // namespace
2991

92+
namespace srsran::harq_utils {
93+
94+
/// Handles the DL HARQ information in the case NTN mode.
95+
class ntn_dl_harq_alloc_history : public base_ntn_harq_history<true>
96+
{
97+
public:
98+
using base_ntn_harq_history<true>::base_ntn_harq_history;
99+
100+
std::optional<dl_harq_process_handle> find_dl_harq(du_ue_index_t ue_idx, slot_point uci_slot, unsigned harq_bit_idx)
101+
{
102+
unsigned idx = get_index(uci_slot);
103+
for (dl_harq_process_impl& h_impl : history[idx]) {
104+
if (h_impl.ue_idx == ue_idx and h_impl.status == harq_state_t::waiting_ack and h_impl.slot_ack == uci_slot and
105+
h_impl.harq_bit_idx == harq_bit_idx) {
106+
return dl_harq_process_handle{harq_pool, h_impl};
107+
}
108+
}
109+
return std::nullopt;
110+
}
111+
};
112+
113+
/// Handles the UL HARQ information in the case NTN mode.
114+
class ntn_ul_harq_alloc_history : public base_ntn_harq_history<false>
115+
{
116+
public:
117+
using base_ntn_harq_history<false>::base_ntn_harq_history;
118+
119+
std::optional<ul_harq_process_handle> find_ul_harq(du_ue_index_t ue_idx, slot_point pusch_slot)
120+
{
121+
unsigned idx = get_index(pusch_slot);
122+
for (ul_harq_process_impl& h_impl : history[idx]) {
123+
if (h_impl.ue_idx == ue_idx and h_impl.status == harq_state_t::waiting_ack and h_impl.slot_ack == pusch_slot) {
124+
return ul_harq_process_handle{harq_pool, h_impl};
125+
}
126+
}
127+
return std::nullopt;
128+
}
129+
130+
unsigned sum_pending_ul_tbs(du_ue_index_t ue_idx) const
131+
{
132+
if (last_sl_ack < last_sl_ind) {
133+
return 0;
134+
}
135+
unsigned sum = 0;
136+
for (unsigned idx = get_index(last_sl_ind), last_idx = get_index(last_sl_ack + 1); idx != last_idx;
137+
idx = mod(idx + 1)) {
138+
for (const ul_harq_process_impl& h : history[idx]) {
139+
if (h.ue_idx == ue_idx and h.status != harq_state_t::empty) {
140+
sum += h.prev_tx_params.tbs_bytes;
141+
break;
142+
}
143+
}
144+
}
145+
return sum;
146+
}
147+
};
148+
149+
} // namespace srsran::harq_utils
150+
151+
// In NTN case, we timeout the HARQ since we need to reuse the process before the PUSCH arrives.
152+
static const unsigned NTN_ACK_WAIT_TIMEOUT = 1;
153+
30154
template <bool IsDl>
31155
cell_harq_repository<IsDl>::cell_harq_repository(unsigned max_ues,
32156
unsigned max_ack_wait_timeout,
33157
unsigned max_harqs_per_ue_,
158+
unsigned ntn_cs_koffset,
34159
harq_timeout_notifier& timeout_notifier_,
35160
srslog::basic_logger& logger_) :
36-
max_ack_wait_in_slots(max_ack_wait_timeout),
161+
max_ack_wait_in_slots(ntn_cs_koffset == 0 ? max_ack_wait_timeout : NTN_ACK_WAIT_TIMEOUT),
37162
max_harqs_per_ue(max_harqs_per_ue_),
38163
timeout_notifier(timeout_notifier_),
39-
logger(logger_)
164+
logger(logger_),
165+
alloc_hist(ntn_cs_koffset > 0 ? std::make_unique<harq_alloc_history>(*this, ntn_cs_koffset) : nullptr)
40166
{
41167
// Reserve space in advance for UEs and their HARQs.
42168
ues.resize(max_ues);
@@ -48,6 +174,11 @@ cell_harq_repository<IsDl>::cell_harq_repository(unsigned max_ues,
48174
harq_timeout_wheel.resize(get_allocator_ring_size_gt_min(max_ack_wait_timeout + get_max_slot_ul_alloc_delay(0)));
49175
}
50176

177+
template <bool IsDl>
178+
cell_harq_repository<IsDl>::~cell_harq_repository()
179+
{
180+
}
181+
51182
template <bool IsDl>
52183
void cell_harq_repository<IsDl>::slot_indication(slot_point sl_tx)
53184
{
@@ -147,6 +278,11 @@ typename cell_harq_repository<IsDl>::harq_type* cell_harq_repository<IsDl>::allo
147278
h.slot_ack_timeout = sl_ack + max_ack_wait_in_slots;
148279
harq_timeout_wheel[h.slot_ack_timeout.to_uint() % harq_timeout_wheel.size()].push_front(&h);
149280

281+
if (is_ntn_mode()) {
282+
// In NTN mode, save HARQ info separately.
283+
alloc_hist->save_harq_newtx_info(h);
284+
}
285+
150286
return &h;
151287
}
152288

@@ -195,6 +331,11 @@ void cell_harq_repository<IsDl>::handle_ack(harq_type& h, bool ack)
195331
}
196332
}
197333

334+
if (is_ntn_mode()) {
335+
// In NTN mode, ACK info does not affect the timeout/retx containers.
336+
return;
337+
}
338+
198339
if (ack or h.nof_retxs >= h.max_nof_harq_retxs) {
199340
// If the HARQ process is ACKed or the maximum number of retransmissions has been reached, we can deallocate the
200341
// HARQ process.
@@ -234,15 +375,22 @@ bool cell_harq_repository<IsDl>::handle_new_retx(harq_type& h, slot_point sl_tx,
234375
// Remove HARQ from pending Retx list.
235376
harq_pending_retx_list.pop(&h);
236377

237-
h.status = harq_state_t::waiting_ack;
238-
h.slot_tx = sl_tx;
239-
h.slot_ack = sl_ack;
240-
h.ack_on_timeout = false;
378+
slot_point prev_sl_ack = h.slot_ack;
379+
h.status = harq_state_t::waiting_ack;
380+
h.slot_tx = sl_tx;
381+
h.slot_ack = sl_ack;
382+
h.ack_on_timeout = false;
241383
h.nof_retxs++;
242384

243385
// Add HARQ to the timeout list.
244386
h.slot_ack_timeout = sl_ack + max_ack_wait_in_slots;
245387
harq_timeout_wheel[h.slot_ack_timeout.to_uint() % harq_timeout_wheel.size()].push_front(&h);
388+
389+
if (is_ntn_mode()) {
390+
// In NTN mode, save HARQ info separately.
391+
alloc_hist->save_harq_retx_info(h, prev_sl_ack);
392+
}
393+
246394
return true;
247395
}
248396

@@ -301,6 +449,12 @@ cell_harq_repository<IsDl>::find_ue_harq_in_state(du_ue_index_t ue_idx, harq_uti
301449
return nullptr;
302450
}
303451

452+
template <bool IsDl>
453+
bool cell_harq_repository<IsDl>::is_ntn_mode() const
454+
{
455+
return alloc_hist != nullptr;
456+
}
457+
304458
template <bool IsDl>
305459
typename cell_harq_repository<IsDl>::harq_type*
306460
cell_harq_repository<IsDl>::find_ue_harq_in_state(du_ue_index_t ue_idx, harq_utils::harq_state_t state)
@@ -336,12 +490,14 @@ template class harq_utils::base_harq_process_handle<false>;
336490
cell_harq_manager::cell_harq_manager(unsigned max_ues,
337491
unsigned max_harqs_per_ue_,
338492
std::unique_ptr<harq_timeout_notifier> notifier,
339-
unsigned max_ack_wait_timeout) :
493+
unsigned max_ack_wait_timeout,
494+
unsigned ntn_cs_koffset) :
340495
max_harqs_per_ue(max_harqs_per_ue_),
341-
timeout_notifier(notifier != nullptr ? std::move(notifier) : std::make_unique<noop_harq_timeout_notifier>()),
496+
timeout_notifier(notifier != nullptr and ntn_cs_koffset == 0 ? std::move(notifier)
497+
: std::make_unique<noop_harq_timeout_notifier>()),
342498
logger(srslog::fetch_basic_logger("SCHED")),
343-
dl(max_ues, max_ack_wait_timeout, max_harqs_per_ue, *timeout_notifier, logger),
344-
ul(max_ues, max_ack_wait_timeout, max_harqs_per_ue, *timeout_notifier, logger)
499+
dl(max_ues, max_ack_wait_timeout, max_harqs_per_ue, ntn_cs_koffset, *timeout_notifier, logger),
500+
ul(max_ues, max_ack_wait_timeout, max_harqs_per_ue, ntn_cs_koffset, *timeout_notifier, logger)
345501
{
346502
}
347503

@@ -463,6 +619,12 @@ dl_harq_process_handle::status_update dl_harq_process_handle::dl_ack_info(mac_ha
463619
// Case: This is not the last PUCCH HARQ-ACK that is expected for this HARQ process.
464620
impl->pucch_ack_to_receive--;
465621
impl->ack_on_timeout = impl->chosen_ack == mac_harq_ack_report_status::ack;
622+
623+
if (harq_repo->is_ntn_mode()) {
624+
// Timeouts don't need to be updated in NTN mode.
625+
return status_update::no_update;
626+
}
627+
466628
// We reduce the HARQ process timeout to receive the next HARQ-ACK. This is done because the two HARQ-ACKs should
467629
// arrive almost simultaneously, and in case the second goes missing, we don't want to block the HARQ for too long.
468630
auto& wheel = harq_repo->harq_timeout_wheel;
@@ -679,6 +841,11 @@ std::optional<ul_harq_process_handle> unique_ue_harq_entity::find_ul_harq_waitin
679841

680842
std::optional<dl_harq_process_handle> unique_ue_harq_entity::find_dl_harq(slot_point uci_slot, uint8_t harq_bit_idx)
681843
{
844+
if (cell_harq_mgr->dl.alloc_hist != nullptr) {
845+
// NTN mode.
846+
return cell_harq_mgr->dl.alloc_hist->find_dl_harq(ue_index, uci_slot, harq_bit_idx);
847+
}
848+
682849
std::vector<dl_harq_process_impl>& dl_harqs = cell_harq_mgr->dl.ues[ue_index].harqs;
683850
for (dl_harq_process_impl& h : dl_harqs) {
684851
if (h.status == harq_utils::harq_state_t::waiting_ack and h.slot_ack == uci_slot and
@@ -691,6 +858,11 @@ std::optional<dl_harq_process_handle> unique_ue_harq_entity::find_dl_harq(slot_p
691858

692859
std::optional<ul_harq_process_handle> unique_ue_harq_entity::find_ul_harq(slot_point pusch_slot)
693860
{
861+
if (cell_harq_mgr->ul.alloc_hist != nullptr) {
862+
// NTN mode.
863+
return cell_harq_mgr->ul.alloc_hist->find_ul_harq(ue_index, pusch_slot);
864+
}
865+
694866
std::vector<ul_harq_process_impl>& ul_harqs = cell_harq_mgr->ul.ues[ue_index].harqs;
695867
for (ul_harq_process_impl& h : ul_harqs) {
696868
if (h.status == harq_utils::harq_state_t::waiting_ack and h.slot_tx == pusch_slot) {
@@ -713,3 +885,19 @@ void unique_ue_harq_entity::uci_sched_failed(slot_point uci_slot)
713885
}
714886
}
715887
}
888+
889+
unsigned unique_ue_harq_entity::total_ul_bytes_waiting_crc() const
890+
{
891+
if (cell_harq_mgr->ul.is_ntn_mode()) {
892+
return cell_harq_mgr->ul.alloc_hist->sum_pending_ul_tbs(ue_index);
893+
}
894+
895+
unsigned harq_bytes = 0;
896+
for (unsigned i = 0; i != nof_ul_harqs(); ++i) {
897+
if (get_ul_ue().harqs[i].status != harq_utils::harq_state_t::empty) {
898+
harq_bytes += get_ul_ue().harqs[i].prev_tx_params.tbs_bytes;
899+
}
900+
}
901+
902+
return harq_bytes;
903+
}

lib/scheduler/cell/cell_harq_manager.h

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,13 @@ struct ul_harq_process_impl : public base_harq_process {
117117
alloc_params prev_tx_params;
118118
};
119119

120+
class ntn_dl_harq_alloc_history;
121+
class ntn_ul_harq_alloc_history;
122+
120123
template <bool IsDl>
121124
struct cell_harq_repository {
122-
using harq_type = std::conditional_t<IsDl, dl_harq_process_impl, ul_harq_process_impl>;
125+
using harq_type = std::conditional_t<IsDl, dl_harq_process_impl, ul_harq_process_impl>;
126+
using harq_alloc_history = std::conditional_t<IsDl, ntn_dl_harq_alloc_history, ntn_ul_harq_alloc_history>;
123127

124128
struct ue_harq_entity_impl {
125129
std::vector<harq_type> harqs;
@@ -129,8 +133,10 @@ struct cell_harq_repository {
129133
cell_harq_repository(unsigned max_ues,
130134
unsigned max_ack_wait_in_slots,
131135
unsigned max_harqs_per_ue,
136+
unsigned ntn_cs_koffset,
132137
harq_timeout_notifier& timeout_notifier_,
133138
srslog::basic_logger& logger_);
139+
~cell_harq_repository();
134140

135141
/// Time interval, in slots, before the HARQ process assumes that the ACK/CRC went missing.
136142
const unsigned max_ack_wait_in_slots;
@@ -144,6 +150,7 @@ struct cell_harq_repository {
144150
std::vector<ue_harq_entity_impl> ues;
145151
intrusive_double_linked_list<harq_type> harq_pending_retx_list;
146152
std::vector<intrusive_double_linked_list<harq_type>> harq_timeout_wheel;
153+
std::unique_ptr<harq_alloc_history> alloc_hist;
147154

148155
void slot_indication(slot_point sl_tx);
149156
void handle_harq_ack_timeout(harq_type& h, slot_point sl_tx);
@@ -157,6 +164,7 @@ struct cell_harq_repository {
157164
void cancel_retxs(harq_type& h);
158165
harq_type* find_ue_harq_in_state(du_ue_index_t ue_idx, harq_utils::harq_state_t state);
159166
const harq_type* find_ue_harq_in_state(du_ue_index_t ue_idx, harq_utils::harq_state_t state) const;
167+
bool is_ntn_mode() const;
160168
};
161169

162170
template <bool IsDl>
@@ -376,7 +384,8 @@ class cell_harq_manager
376384
cell_harq_manager(unsigned max_ues = MAX_NOF_DU_UES,
377385
unsigned max_harqs_per_ue = MAX_NOF_HARQS,
378386
std::unique_ptr<harq_timeout_notifier> notifier = nullptr,
379-
unsigned max_ack_wait_timeout = DEFAULT_ACK_TIMEOUT_SLOTS);
387+
unsigned max_ack_wait_timeout = DEFAULT_ACK_TIMEOUT_SLOTS,
388+
unsigned ntn_cs_koffset = 0);
380389

381390
/// Update slot, and checks if there are HARQ processes that have reached maxReTx with no ACK
382391
void slot_indication(slot_point sl_tx);
@@ -517,6 +526,8 @@ class unique_ue_harq_entity
517526
return 0;
518527
}
519528

529+
unsigned total_ul_bytes_waiting_crc() const;
530+
520531
private:
521532
dl_harq_ent_impl& get_dl_ue() { return cell_harq_mgr->dl.ues[ue_index]; }
522533
const dl_harq_ent_impl& get_dl_ue() const { return cell_harq_mgr->dl.ues[ue_index]; }

0 commit comments

Comments
 (0)