lib,rlc_am_nr: added processing of ACKs from status report

This commit is contained in:
Pedro Alvarez 2021-11-19 16:44:52 +00:00
parent ec93cc7238
commit b794593469
4 changed files with 70 additions and 8 deletions

View File

@ -191,6 +191,11 @@ public:
protected:
std::unique_ptr<rlc_am_base_tx> tx_base = {};
std::unique_ptr<rlc_am_base_rx> 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

View File

@ -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_am_nr_pdu_header_t>;
rlc_ringbuffer_t<rlc_amd_tx_pdu_nr, RLC_AM_WINDOW_SIZE> tx_window;
pdu_retx_queue<RLC_AM_WINDOW_SIZE> 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

View File

@ -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) {

View File

@ -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<rlc_am_nr_tx*>(rlc1.get_tx());
rlc_am_nr_rx* rx1 = dynamic_cast<rlc_am_nr_rx*>(rlc1.get_rx());
rlc_am_nr_tx* tx2 = dynamic_cast<rlc_am_nr_tx*>(rlc2.get_tx());
rlc_am_nr_rx* rx2 = dynamic_cast<rlc_am_nr_rx*>(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;