From b794593469d21d38e8d9cb2965839564d2576a2f Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Fri, 19 Nov 2021 16:44:52 +0000 Subject: [PATCH] lib,rlc_am_nr: added processing of ACKs from status report --- lib/include/srsran/rlc/rlc_am_base.h | 5 +++ lib/include/srsran/rlc/rlc_am_nr.h | 49 +++++++++++++++++++++++----- lib/src/rlc/rlc_am_nr.cc | 15 +++++++++ lib/test/rlc/rlc_am_nr_test.cc | 9 +++++ 4 files changed, 70 insertions(+), 8 deletions(-) diff --git a/lib/include/srsran/rlc/rlc_am_base.h b/lib/include/srsran/rlc/rlc_am_base.h index d088defa0..5cb54da77 100644 --- a/lib/include/srsran/rlc/rlc_am_base.h +++ b/lib/include/srsran/rlc/rlc_am_base.h @@ -191,6 +191,11 @@ public: protected: std::unique_ptr tx_base = {}; std::unique_ptr rx_base = {}; + +public: + // Getters for TX/RX entities. Useful for testing. + rlc_am_base_rx* get_rx() { return rx_base.get(); } + rlc_am_base_tx* get_tx() { return tx_base.get(); } }; } // namespace srsran diff --git a/lib/include/srsran/rlc/rlc_am_nr.h b/lib/include/srsran/rlc/rlc_am_nr.h index 43f7543c7..f41b72ad7 100644 --- a/lib/include/srsran/rlc/rlc_am_nr.h +++ b/lib/include/srsran/rlc/rlc_am_nr.h @@ -35,7 +35,41 @@ namespace srsran { class rlc_am_nr_tx; class rlc_am_nr_rx; -// Transmitter sub-class +/**************************************************************************** + * Tx state variables + * Ref: 3GPP TS 38.322 v10.0.0 Section 7.1 + ***************************************************************************/ +struct rlc_am_nr_tx_state_t { + /* + * TX_Next_Ack: This state variable holds the value of the SN of the next RLC SDU for which a positive + * acknowledgment is to be received in-sequence, and it serves as the lower edge of the transmitting window. It is + * initially set to 0, and is updated whenever the AM RLC entity receives a positive acknowledgment for an RLC SDU + * with SN = TX_Next_Ack. + */ + uint32_t tx_next_ack; + /* + * TX_Next: This state variable holds the value of the SN to be assigned for the next newly generated AMD PDU. It is + * initially set to 0, and is updated whenever the AM RLC entity constructs an AMD PDU with SN = TX_Next and + * contains an RLC SDU or the last segment of a RLC SDU. + */ + uint32_t tx_next; + /* + * POLL_SN: This state variable holds the value of the highest SN of the AMD PDU among the AMD PDUs submitted to + * lower layer when POLL_SN is set according to sub clause 5.3.3.2. It is initially set to 0. + */ + uint32_t poll_sn; + /* + * PDU_WITHOUT_POLL: This counter is initially set to 0. It counts the number of AMD PDUs sent since the most recent + * poll bit was transmitted. + */ + uint32_t pdu_without_poll; + /* + * BYTE_WITHOUT_POLL: This counter is initially set to 0. It counts the number of data bytes sent since the most + * recent poll bit was transmitted. + */ + uint32_t byte_without_poll; +}; + class rlc_am_nr_tx : public rlc_am::rlc_am_base_tx { public: @@ -80,17 +114,16 @@ private: * Tx state variables * Ref: 3GPP TS 38.322 v10.0.0 Section 7.1 ***************************************************************************/ - struct rlc_nr_tx_state_t { - uint32_t tx_next_ack; - uint32_t tx_next; - uint32_t poll_sn; - uint32_t pdu_without_poll; - uint32_t byte_without_poll; - } st = {}; + struct rlc_am_nr_tx_state_t st = {}; using rlc_amd_tx_pdu_nr = rlc_amd_tx_pdu; rlc_ringbuffer_t tx_window; pdu_retx_queue retx_queue; + +public: + // Getters/Setters + rlc_am_nr_tx_state_t get_tx_state() { return st; } // This should only be used for testing. + uint32_t get_tx_window_size() { return tx_window.size(); } // This should only be used for testing. }; // Receiver sub-class diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index a8753b30e..a5d971fdf 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -237,6 +237,21 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) * - consider the RLC SDU or the RLC SDU segment for which a negative acknowledgement was received for * retransmission. */ + // Process ACKs + uint32_t stop_sn = status.N_nack == 0 + ? st.tx_next + : status.nacks[0].nack_sn; // Stop processing ACKs at the first NACK, if it exists. + if (status.ack_sn >= st.tx_next_ack) { + for (uint32_t sn = st.tx_next_ack; sn < stop_sn; sn++) { + if (tx_window.has_sn(sn)) { + tx_window.remove_pdu(sn); + st.tx_next_ack = sn + 1; + } else { + logger->error("Missing ACKed SN from TX window"); + break; + } + } + } // Process N_acks for (uint32_t nack_idx = 0; nack_idx < status.N_nack; nack_idx++) { if (st.tx_next_ack <= status.nacks[nack_idx].nack_sn && status.nacks[nack_idx].nack_sn <= st.tx_next) { diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index ccc34f6cb..90561118e 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -72,6 +72,11 @@ int basic_test() rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + rlc_am_nr_tx* tx1 = dynamic_cast(rlc1.get_tx()); + rlc_am_nr_rx* rx1 = dynamic_cast(rlc1.get_rx()); + rlc_am_nr_tx* tx2 = dynamic_cast(rlc2.get_tx()); + rlc_am_nr_rx* rx2 = dynamic_cast(rlc2.get_rx()); + // before configuring entity TESTASSERT(0 == rlc1.get_buffer_state()); @@ -106,6 +111,10 @@ int basic_test() // Write status PDU to RLC1 rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + // Check TX_NEXT_ACK + rlc_am_nr_tx_state_t st = tx1->get_tx_state(); + TESTASSERT_EQ(5, st.tx_next_ack); + TESTASSERT_EQ(0, tx1->get_tx_window_size()); // Check statistics TESTASSERT(rx_is_tx(rlc1.get_metrics(), rlc2.get_metrics())); return SRSRAN_SUCCESS;