lib,rlc_am_nr: added pollBYTE support and a unit test for it

master
Pedro Alvarez 3 years ago
parent eaa8fff6a0
commit 1d1e6dd832

@ -122,7 +122,7 @@ public:
uint32_t build_status_pdu(byte_buffer_t* payload, uint32_t nof_bytes);
// Polling
uint8_t get_pdu_poll(bool is_retx);
uint8_t get_pdu_poll(bool is_retx, uint32_t sdu_bytes);
void stop() final;

@ -195,7 +195,7 @@ uint32_t rlc_am_nr_tx::build_new_pdu(uint8_t* payload, uint32_t nof_bytes)
// Prepare header
rlc_am_nr_pdu_header_t hdr = {};
hdr.dc = RLC_DC_FIELD_DATA_PDU;
hdr.p = get_pdu_poll(false);
hdr.p = get_pdu_poll(false, tx_sdu->N_bytes);
hdr.si = rlc_nr_si_field_t::full_sdu;
hdr.sn_size = cfg.tx_sn_field_length;
hdr.sn = st.tx_next;
@ -249,10 +249,12 @@ uint32_t rlc_am_nr_tx::build_new_sdu_segment(rlc_amd_tx_pdu_nr& tx_pdu, uint8_t*
return 0;
}
uint32_t segment_payload_len = nof_bytes - min_hdr_size;
// Prepare header
rlc_am_nr_pdu_header_t hdr = {};
hdr.dc = RLC_DC_FIELD_DATA_PDU;
hdr.p = get_pdu_poll(false);
hdr.p = get_pdu_poll(false, segment_payload_len);
hdr.si = rlc_nr_si_field_t::first_segment;
hdr.sn_size = cfg.tx_sn_field_length;
hdr.sn = st.tx_next;
@ -268,7 +270,6 @@ uint32_t rlc_am_nr_tx::build_new_sdu_segment(rlc_amd_tx_pdu_nr& tx_pdu, uint8_t*
}
// Copy PDU to payload
uint32_t segment_payload_len = nof_bytes - hdr_len;
srsran_assert((hdr_len + segment_payload_len) <= nof_bytes, "Error calculating hdr_len and segment_payload_len");
memcpy(&payload[hdr_len], tx_pdu.sdu_buf->msg, segment_payload_len);
@ -354,7 +355,7 @@ uint32_t rlc_am_nr_tx::build_continuation_sdu_segment(rlc_amd_tx_pdu_nr& tx_pdu,
// Prepare header
rlc_am_nr_pdu_header_t hdr = {};
hdr.dc = RLC_DC_FIELD_DATA_PDU;
hdr.p = get_pdu_poll(false);
hdr.p = get_pdu_poll(false, segment_payload_len);
hdr.si = si;
hdr.sn_size = cfg.tx_sn_field_length;
hdr.sn = st.tx_next;
@ -505,7 +506,7 @@ rlc_am_nr_tx::build_retx_pdu_without_segmentation(rlc_amd_retx_nr_t& retx, uint8
current_so = retx.current_so;
}
rlc_am_nr_pdu_header_t new_header = tx_pdu.header;
new_header.p = get_pdu_poll(true);
new_header.p = get_pdu_poll(true, 0);
new_header.si = si;
new_header.so = current_so;
uint32_t hdr_len = rlc_am_nr_write_data_pdu_header(new_header, payload);
@ -598,7 +599,7 @@ uint32_t rlc_am_nr_tx::build_retx_pdu_with_segmentation(rlc_amd_retx_nr_t& retx,
// Write header
rlc_am_nr_pdu_header_t hdr = tx_pdu.header;
hdr.p = get_pdu_poll(true);
hdr.p = get_pdu_poll(true, 0);
hdr.so = retx.current_so;
hdr.si = si;
uint32_t hdr_len = rlc_am_nr_write_data_pdu_header(hdr, payload);
@ -905,16 +906,25 @@ void rlc_am_nr_tx::get_buffer_state(uint32_t& n_bytes_new, uint32_t& n_bytes_pri
* Check whether the polling bit needs to be set, as specified in
* TS 38.322, section 5.3.3.2
*/
uint8_t rlc_am_nr_tx::get_pdu_poll(bool is_retx)
uint8_t rlc_am_nr_tx::get_pdu_poll(bool is_retx, uint32_t sdu_bytes)
{
/* For each AMD PDU or AMD PDU segment that has not been previoulsy tranmitted:
* - increment PDU_WITHOUT_POLL by one;
* - increment BYTE_WITHOUT_POLL by every new byte of Data field element that it maps to the Data field of the AMD
* PDU;
* - if PDU_WITHOUT_POLL >= pollPDU; or
* - if BYTE_WITHOUT_POLL >= pollByte:
* - include a poll in the AMD PDU as described below.
*/
uint8_t poll = 0;
if (!is_retx) {
st.pdu_without_poll++;
st.byte_without_poll += sdu_bytes;
if (cfg.poll_pdu > 0) {
if (st.pdu_without_poll >= (uint32_t)cfg.poll_pdu) {
poll = 1;
st.pdu_without_poll = 0;
} else {
st.pdu_without_poll++;
if (st.pdu_without_poll >= (uint32_t)cfg.poll_pdu || st.byte_without_poll >= (uint32_t)cfg.poll_byte) {
poll = 1;
st.pdu_without_poll = 0;
st.byte_without_poll = 0;
}
}
}

@ -483,7 +483,7 @@ int basic_segmentation_test(rlc_am_nr_sn_size_t sn_size)
// Push 1 SDU into RLC1
unique_byte_buffer_t sdu;
constexpr uint32_t payload_size = 3; // Give the SDU the size of 3 bytes
sdu = srsran::make_byte_buffer();
sdu = srsran::make_byte_buffer();
TESTASSERT(nullptr != sdu);
sdu->msg[0] = 0; // Write the index into the buffer
sdu->N_bytes = payload_size; // Give the SDU the size of 3 bytes
@ -532,7 +532,7 @@ int basic_segmentation_test(rlc_am_nr_sn_size_t sn_size)
TESTASSERT_EQ(n_pdus, metrics2.num_rx_pdus); // 3 PDUs
TESTASSERT_EQ(0, metrics2.num_tx_pdu_bytes);
TESTASSERT_EQ(total_rx_pdu_bytes, metrics2.num_rx_pdu_bytes); // 1 PDU (No SO) + 2 PDUs (with SO)
TESTASSERT_EQ(0, metrics2.num_lost_sdus); // No lost SDUs
TESTASSERT_EQ(0, metrics2.num_lost_sdus); // No lost SDUs
// Check state
rlc_am_nr_tx_state_t state1_tx = tx1->get_tx_state();
@ -714,13 +714,13 @@ int segment_retx_test(rlc_am_nr_sn_size_t sn_size)
TESTASSERT_EQ(15, metrics2.num_rx_sdu_bytes); // 5 SDUs, 3 bytes each
TESTASSERT_EQ(0, metrics2.num_lost_sdus);
// SDU metrics
TESTASSERT_EQ(2, metrics2.num_tx_pdus); // Two status PDUs
TESTASSERT_EQ(7, metrics2.num_rx_pdus); // 7 PDUs (8 tx'ed, but one was lost)
TESTASSERT_EQ(2, metrics2.num_tx_pdus); // Two status PDUs
TESTASSERT_EQ(7, metrics2.num_rx_pdus); // 7 PDUs (8 tx'ed, but one was lost)
TESTASSERT_EQ(total_tx_pdu_bytes2, metrics2.num_tx_pdu_bytes);
TESTASSERT_EQ(total_rx_pdu_bytes2,
metrics2.num_rx_pdu_bytes); // 2 Bytes * (NBUFFS-1) (header size) + (NBUFFS-1) * 3 (data)
// 3 (1 retx no SO) + 2 * 5 (2 retx with SO) = 33
TESTASSERT_EQ(0, metrics2.num_lost_sdus); // No lost SDUs
metrics2.num_rx_pdu_bytes); // 2 Bytes * (NBUFFS-1) (header size) + (NBUFFS-1) * 3 (data)
// 3 (1 retx no SO) + 2 * 5 (2 retx with SO) = 33
TESTASSERT_EQ(0, metrics2.num_lost_sdus); // No lost SDUs
// Check state
rlc_am_nr_rx_state_t state2_rx = rx2->get_rx_state();
@ -1114,9 +1114,9 @@ int discard_test(rlc_am_nr_sn_size_t sn_size)
rlc_am_tester tester;
timer_handler timers(8);
auto& test_logger = srslog::fetch_basic_logger("TESTER ");
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);
auto& test_logger = srslog::fetch_basic_logger("TESTER ");
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);
test_delimit_logger delimiter("discard test (%d bit SN)", to_number(sn_size));
srslog::fetch_basic_logger("RLC_AM_1").set_hex_dump_max_size(100);
@ -1211,6 +1211,111 @@ int discard_test(rlc_am_nr_sn_size_t sn_size)
return SRSRAN_SUCCESS;
}
// This test checks the correct functioning of RLC TX polling bit setting
int poll_test_poll_pdu()
{
rlc_am_tester tester;
timer_handler timers(8);
auto& test_logger = srslog::fetch_basic_logger("TESTER ");
test_delimit_logger delimiter("poll test pollPDU");
srslog::fetch_basic_logger("RLC_AM_1").set_hex_dump_max_size(100);
rlc_config_t rlc_cnfg = {};
rlc_cnfg.rat = srsran_rat_t::nr;
rlc_cnfg.rlc_mode = rlc_mode_t::am;
rlc_cnfg.am_nr.poll_pdu = 4;
rlc_cnfg.am_nr.poll_byte = 3000;
rlc_cnfg.am_nr.t_status_prohibit = 8;
rlc_cnfg.am_nr.max_retx_thresh = 8;
rlc_cnfg.am_nr.t_reassembly = 35;
// Test p bit set on new TX with PollPDU
{
rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers);
if (not rlc1.configure(rlc_cnfg)) {
return SRSRAN_ERROR;
}
// pollPDU == 4
uint32_t num_tx_sdus = 6;
for (uint32_t i = 0; i < num_tx_sdus; ++i) {
// Write SDU
unique_byte_buffer_t sdu = srsran::make_byte_buffer();
TESTASSERT(sdu != nullptr);
sdu->N_bytes = 1;
sdu->md.pdcp_sn = i;
rlc1.write_sdu(std::move(sdu));
}
uint32_t num_tx_pdus = 6;
for (uint32_t i = 0; i < num_tx_pdus; ++i) {
unique_byte_buffer_t pdu = srsran::make_byte_buffer();
TESTASSERT(pdu != nullptr);
pdu->N_bytes = rlc1.read_pdu(pdu->msg, 3);
rlc_am_nr_pdu_header_t hdr;
rlc_am_nr_read_data_pdu_header(pdu.get(), rlc_am_nr_sn_size_t::size18bits, &hdr);
if (i != 3 && i != 5) { // P bit set for PollPDU and for empty TX queue
TESTASSERT_EQ(0, hdr.p);
} else {
TESTASSERT_EQ(1, hdr.p);
}
}
}
return SRSRAN_SUCCESS;
}
// Test p bit set on new TX with PollBYTE
int poll_test_poll_byte()
{
rlc_am_tester tester;
timer_handler timers(8);
auto& test_logger = srslog::fetch_basic_logger("TESTER ");
test_delimit_logger delimiter("poll test pollBYTE");
srslog::fetch_basic_logger("RLC_AM_1").set_hex_dump_max_size(100);
rlc_config_t rlc_cnfg = {};
rlc_cnfg.rat = srsran_rat_t::nr;
rlc_cnfg.rlc_mode = rlc_mode_t::am;
rlc_cnfg.am_nr.poll_pdu = 4;
rlc_cnfg.am_nr.poll_byte = 3000;
rlc_cnfg.am_nr.t_status_prohibit = 8;
rlc_cnfg.am_nr.max_retx_thresh = 8;
rlc_cnfg.am_nr.t_reassembly = 35;
rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers);
if (not rlc1.configure(rlc_cnfg)) {
return SRSRAN_ERROR;
}
// pollByte == 3000
uint32_t num_tx_sdus = 4;
for (uint32_t i = 0; i < num_tx_sdus; ++i) {
// Write SDU
unique_byte_buffer_t sdu = srsran::make_byte_buffer();
TESTASSERT(sdu != nullptr);
sdu->N_bytes = i == 0 ? 2999 : 1;
sdu->md.pdcp_sn = i;
rlc1.write_sdu(std::move(sdu));
}
uint32_t num_tx_pdus = num_tx_sdus;
for (uint32_t i = 0; i < num_tx_pdus; ++i) {
unique_byte_buffer_t pdu = srsran::make_byte_buffer();
TESTASSERT(pdu != nullptr);
uint32_t nof_bytes = i == 0 ? 3001 : 3;
pdu->N_bytes = rlc1.read_pdu(pdu->msg, nof_bytes);
TESTASSERT_EQ(nof_bytes, pdu->N_bytes);
rlc_am_nr_pdu_header_t hdr;
rlc_am_nr_read_data_pdu_header(pdu.get(), rlc_am_nr_sn_size_t::size18bits, &hdr);
if (i != 1 && i != 3) {
TESTASSERT_EQ(0, hdr.p);
} else {
TESTASSERT_EQ(1, hdr.p);
}
}
return SRSRAN_SUCCESS;
}
int main()
{
// Setup the log message spy to intercept error and warning log entries from RLC
@ -1250,5 +1355,12 @@ int main()
TESTASSERT(max_retx_lost_sdu_test(sns) == SRSRAN_SUCCESS); // Fixme
TESTASSERT(max_retx_lost_segments_test(sns) == SRSRAN_SUCCESS); // Fixme
TESTASSERT(discard_test(sns) == SRSRAN_SUCCESS); // Fixme
TESTASSERT(poll_test_poll_pdu() == SRSRAN_SUCCESS);
TESTASSERT(poll_test_poll_byte() == SRSRAN_SUCCESS);
// Test p bit *not* set on RETX with PollPDU
// Test p bit *not* set on RETX with PollBYTE
// Test p bit set on empty TX queue and empty retx queue
// Test p bit *not* set on empty TX queue and empty retx queue
// Test p bit set on window stall
return SRSRAN_SUCCESS;
}

Loading…
Cancel
Save