Merge branch 'next' into agpl_next

master
Codebot 3 years ago committed by SRS codebot
commit e79aa61a9a

@ -102,7 +102,8 @@ enum class pdcp_t_reordering_t {
ms2250 = 2250,
ms2500 = 2500,
ms2750 = 2750,
ms3000 = 3000
ms3000 = 3000,
infinity = -1
};
// Taken from PDCP-Config (TS 38.331 version 15.2.1)
@ -122,7 +123,7 @@ enum class pdcp_discard_timer_t {
ms500 = 500,
ms750 = 750,
ms1500 = 1500,
infinity = 0
infinity = -1
};
class pdcp_config_t

@ -104,6 +104,7 @@ public:
bool configure(const rlc_config_t& cfg_) final;
uint32_t read_pdu(uint8_t* payload, uint32_t nof_bytes) final;
void handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) final;
void handle_nack(const rlc_status_nack_t& nack, std::set<uint32_t>& retx_sn_set);
void reestablish() final;
void stop() final;

@ -99,6 +99,8 @@ class rlc_am_nr_status_pdu_t
private:
/// Stored SN size required to compute the packed size
rlc_am_nr_sn_size_t sn_size = rlc_am_nr_sn_size_t::nulltype;
/// Stored modulus to determine continuous sequences across SN overflows
uint32_t mod_nr = cardinality(rlc_am_nr_sn_size_t::nulltype);
/// Internal NACK container; keep in sync with packed_size_
std::vector<rlc_status_nack_t> nacks_ = {};
/// Stores the current packed size; sync on each change of nacks_
@ -119,6 +121,7 @@ public:
rlc_am_nr_status_pdu_t(rlc_am_nr_sn_size_t sn_size);
void reset();
bool is_continuous_sequence(const rlc_status_nack_t& left, const rlc_status_nack_t& right) const;
void push_nack(const rlc_status_nack_t& nack);
const std::vector<rlc_status_nack_t>& get_nacks() const { return nacks_; }
uint32_t get_packed_size() const { return packed_size; }

@ -174,6 +174,8 @@ public:
// NACK helper (for LTE and NR)
struct rlc_status_nack_t {
const static uint16_t so_end_of_sdu = 0xFFFF;
uint32_t nack_sn; // Sequence Number (SN) of first missing SDU
bool has_so; // NACKs continuous sequence of bytes [so_start..so_end]
uint16_t so_start; // First missing byte in SDU with SN=nack_sn
@ -190,6 +192,15 @@ struct rlc_status_nack_t {
has_nack_range = false;
nack_range = 0;
}
bool equals(const rlc_status_nack_t& other) const
{
return nack_sn == other.nack_sn && has_so == other.has_so && so_start == other.so_start && so_end == other.so_end &&
has_nack_range == other.has_nack_range && nack_range == other.nack_range;
}
bool operator==(const rlc_status_nack_t& other) const { return equals(other); }
bool operator!=(const rlc_status_nack_t& other) const { return not equals(other); }
};
// STATUS PDU

@ -8,6 +8,7 @@
#ifndef FMT_CORE_H_
#define FMT_CORE_H_
#include <array>
#include <cstdio> // std::FILE
#include <cstring>
#include <functional>

@ -134,6 +134,12 @@ int make_rlc_config_t(const rlc_cfg_c& asn1_type, uint8_t bearer_id, rlc_config_
default:
break;
}
rlc_cfg.am_nr.t_poll_retx = asn1_type.am().ul_am_rlc.t_poll_retx.to_number();
rlc_cfg.am_nr.poll_pdu = asn1_type.am().ul_am_rlc.poll_pdu.to_number();
rlc_cfg.am_nr.poll_byte = asn1_type.am().ul_am_rlc.poll_byte.to_number();
rlc_cfg.am_nr.max_retx_thresh = asn1_type.am().ul_am_rlc.max_retx_thres.to_number();
rlc_cfg.am_nr.t_reassembly = asn1_type.am().dl_am_rlc.t_reassembly.to_number();
rlc_cfg.am_nr.t_status_prohibit = asn1_type.am().dl_am_rlc.t_status_prohibit.to_number();
break;
case rlc_cfg_c::types_opts::um_bi_dir:
rlc_cfg = rlc_config_t::default_rlc_um_nr_config();
@ -157,6 +163,7 @@ int make_rlc_config_t(const rlc_cfg_c& asn1_type, uint8_t bearer_id, rlc_config_
default:
break;
}
rlc_cfg.um_nr.t_reassembly_ms = asn1_type.um_bi_dir().dl_um_rlc.t_reassembly.to_number();
break;
case rlc_cfg_c::types_opts::um_uni_dir_dl:
asn1::log_warning("NR RLC type %s is not supported", asn1_type.type().to_string());
@ -245,14 +252,119 @@ srsran::pdcp_config_t make_drb_pdcp_config_t(const uint8_t bearer_id, bool is_ue
}
}
pdcp_t_reordering_t t_reordering = pdcp_t_reordering_t::ms500;
pdcp_t_reordering_t t_reordering = pdcp_t_reordering_t::infinity;
if (pdcp_cfg.t_reordering_present) {
switch (pdcp_cfg.t_reordering.to_number()) {
case 0:
t_reordering = pdcp_t_reordering_t::ms0;
break;
default:
case 1:
t_reordering = pdcp_t_reordering_t::ms1;
break;
case 2:
t_reordering = pdcp_t_reordering_t::ms2;
break;
case 4:
t_reordering = pdcp_t_reordering_t::ms4;
break;
case 5:
t_reordering = pdcp_t_reordering_t::ms5;
break;
case 8:
t_reordering = pdcp_t_reordering_t::ms8;
break;
case 10:
t_reordering = pdcp_t_reordering_t::ms10;
break;
case 15:
t_reordering = pdcp_t_reordering_t::ms15;
break;
case 20:
t_reordering = pdcp_t_reordering_t::ms20;
break;
case 30:
t_reordering = pdcp_t_reordering_t::ms30;
break;
case 40:
t_reordering = pdcp_t_reordering_t::ms40;
break;
case 50:
t_reordering = pdcp_t_reordering_t::ms50;
break;
case 60:
t_reordering = pdcp_t_reordering_t::ms60;
break;
case 80:
t_reordering = pdcp_t_reordering_t::ms80;
break;
case 100:
t_reordering = pdcp_t_reordering_t::ms100;
break;
case 120:
t_reordering = pdcp_t_reordering_t::ms120;
break;
case 140:
t_reordering = pdcp_t_reordering_t::ms140;
break;
case 160:
t_reordering = pdcp_t_reordering_t::ms160;
break;
case 180:
t_reordering = pdcp_t_reordering_t::ms180;
break;
case 200:
t_reordering = pdcp_t_reordering_t::ms200;
break;
case 220:
t_reordering = pdcp_t_reordering_t::ms220;
break;
case 240:
t_reordering = pdcp_t_reordering_t::ms240;
break;
case 260:
t_reordering = pdcp_t_reordering_t::ms260;
break;
case 280:
t_reordering = pdcp_t_reordering_t::ms280;
break;
case 300:
t_reordering = pdcp_t_reordering_t::ms300;
break;
case 500:
t_reordering = pdcp_t_reordering_t::ms500;
break;
case 750:
t_reordering = pdcp_t_reordering_t::ms750;
break;
case 1000:
t_reordering = pdcp_t_reordering_t::ms1000;
break;
case 1250:
t_reordering = pdcp_t_reordering_t::ms1250;
break;
case 1500:
t_reordering = pdcp_t_reordering_t::ms1500;
break;
case 1750:
t_reordering = pdcp_t_reordering_t::ms1750;
break;
case 2000:
t_reordering = pdcp_t_reordering_t::ms2000;
break;
case 2250:
t_reordering = pdcp_t_reordering_t::ms2250;
break;
case 2500:
t_reordering = pdcp_t_reordering_t::ms2500;
break;
case 2750:
t_reordering = pdcp_t_reordering_t::ms2750;
break;
case 3000:
t_reordering = pdcp_t_reordering_t::ms3000;
break;
default:
t_reordering = pdcp_t_reordering_t::ms50;
}
}

@ -120,7 +120,12 @@ int pdcp::add_bearer(uint32_t lcid, const pdcp_config_t& cfg)
if (cfg.rat == srsran::srsran_rat_t::lte) {
entity.reset(new pdcp_entity_lte{rlc, rrc, gw, task_sched, logger, lcid});
} else if (cfg.rat == srsran::srsran_rat_t::nr) {
#ifdef USE_PDCP_NR
#pragma message "Compiling with PDCP NR entity"
entity.reset(new pdcp_entity_nr{rlc, rrc, gw, task_sched, logger, lcid});
#else
entity.reset(new pdcp_entity_lte{rlc, rrc, gw, task_sched, logger, lcid});
#endif
}
if (not entity->configure(cfg)) {

@ -73,6 +73,12 @@ bool pdcp_entity_nr::configure(const pdcp_config_t& cnfg_)
reordering_timer.set(static_cast<uint32_t>(cfg.t_reordering), *reordering_fnc);
}
active = true;
logger.info("%s PDCP-NR entity configured. SN_LEN=%d, Discard timer %d, Re-ordering timer %d, RAT=%s",
rb_name,
cfg.sn_len,
cfg.discard_timer,
cfg.t_reordering,
to_string(cfg.rat));
return true;
}

@ -32,7 +32,6 @@
namespace srsran {
const static uint32_t max_tx_queue_size = 256;
const static uint32_t so_end_of_sdu = 0xFFFF;
/****************************************************************************
* RLC AM NR entity
@ -837,43 +836,86 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes)
std::set<uint32_t> retx_sn_set; // Set of PDU SNs added for retransmission (no duplicates)
for (uint32_t nack_idx = 0; nack_idx < status.nacks.size(); nack_idx++) {
if (status.nacks[nack_idx].has_nack_range) {
RlcWarning("Handling NACK ranges is not yet implemented. Ignoring NACK across %d SDU(s) starting from SN=%d",
status.nacks[nack_idx].nack_range,
status.nacks[nack_idx].nack_sn);
continue;
}
if (tx_mod_base_nr(st.tx_next_ack) <= tx_mod_base_nr(status.nacks[nack_idx].nack_sn) &&
tx_mod_base_nr(status.nacks[nack_idx].nack_sn) <= tx_mod_base_nr(st.tx_next)) {
RlcDebug("Handling NACK for SN=%d", status.nacks[nack_idx].nack_sn);
auto nack = status.nacks[nack_idx];
uint32_t nack_sn = nack.nack_sn;
if (tx_window->has_sn(nack_sn)) {
auto& pdu = (*tx_window)[nack_sn];
for (uint32_t range_sn = status.nacks[nack_idx].nack_sn;
range_sn < status.nacks[nack_idx].nack_sn + status.nacks[nack_idx].nack_range;
range_sn++) {
rlc_status_nack_t nack = {};
nack.nack_sn = range_sn;
if (status.nacks[nack_idx].has_so) {
if (range_sn == status.nacks[nack_idx].nack_sn) {
// First SN
nack.has_so = true;
nack.so_start = status.nacks[nack_idx].so_start;
nack.so_end = rlc_status_nack_t::so_end_of_sdu;
} else if (range_sn == (status.nacks[nack_idx].nack_sn + status.nacks[nack_idx].nack_range - 1)) {
// Last SN
nack.has_so = true;
// This might be first+last item at the same time, so don't change so_start here
nack.so_end = status.nacks[nack_idx].so_end;
}
}
handle_nack(nack, retx_sn_set);
}
} else {
handle_nack(status.nacks[nack_idx], retx_sn_set);
}
}
// Process retx_count and inform upper layers if needed
for (uint32_t retx_sn : retx_sn_set) {
auto& pdu = (*tx_window)[retx_sn];
// Increment retx_count
if (pdu.retx_count == RETX_COUNT_NOT_STARTED) {
// Set retx_count = 0 on first RE-transmission of associated SDU (38.322 Sec. 5.3.2)
pdu.retx_count = 0;
} else {
// Increment otherwise
pdu.retx_count++;
}
// Inform upper layers if needed
check_sn_reached_max_retx(retx_sn);
}
// Notify PDCP
if (not notify_info_vec.empty()) {
parent->pdcp->notify_delivery(parent->lcid, notify_info_vec);
}
notify_info_vec.clear();
}
void rlc_am_nr_tx::handle_nack(const rlc_status_nack_t& nack, std::set<uint32_t>& retx_sn_set)
{
if (tx_mod_base_nr(st.tx_next_ack) <= tx_mod_base_nr(nack.nack_sn) &&
tx_mod_base_nr(nack.nack_sn) <= tx_mod_base_nr(st.tx_next)) {
RlcDebug("Handling NACK for SN=%d", nack.nack_sn);
if (tx_window->has_sn(nack.nack_sn)) {
auto& pdu = (*tx_window)[nack.nack_sn];
if (nack.has_so) {
// NACK'ing missing bytes in SDU segment.
// Retransmit all SDU segments within those missing bytes.
if (pdu.segment_list.empty()) {
RlcError("Received NACK with SO, but there is no segment information. SN=%d", nack_sn);
RlcError("Received NACK with SO, but there is no segment information. SN=%d", nack.nack_sn);
}
bool segment_found = false;
for (const rlc_amd_tx_pdu_nr::pdu_segment& segm : pdu.segment_list) {
if (segm.so >= nack.so_start && segm.so <= nack.so_end) {
if (not retx_queue->has_sn(nack_sn, segm.so)) {
if (not retx_queue->has_sn(nack.nack_sn, segm.so)) {
rlc_amd_retx_nr_t& retx = retx_queue->push();
retx.sn = nack_sn;
retx.sn = nack.nack_sn;
retx.is_segment = true;
retx.so_start = segm.so;
retx.current_so = segm.so;
retx.segment_length = segm.payload_len;
retx_sn_set.insert(nack_sn);
retx_sn_set.insert(nack.nack_sn);
RlcInfo("Scheduled RETX of SDU segment SN=%d, so_start=%d, segment_length=%d",
retx.sn,
retx.so_start,
retx.segment_length);
} else {
RlcInfo("Skip already scheduled RETX of SDU segment SN=%d, so_start=%d, segment_length=%d",
nack_sn,
nack.nack_sn,
segm.so,
segm.payload_len);
}
@ -882,34 +924,33 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes)
}
if (!segment_found) {
RlcWarning("Could not find segment for NACK_SN=%d. SO_start=%d, SO_end=%d",
status.nacks[nack_idx].nack_sn,
nack.nack_sn,
nack.so_start,
nack.so_end);
for (const rlc_amd_tx_pdu_nr::pdu_segment& segm : pdu.segment_list) {
RlcDebug(
"Segments for SN=%d. SO=%d, SO_end=%d", status.nacks[nack_idx].nack_sn, segm.so, segm.payload_len);
RlcDebug("Segments for SN=%d. SO=%d, SO_end=%d", nack.nack_sn, segm.so, segm.payload_len);
}
}
} else {
// NACK'ing full SDU.
// add to retx queue if it's not already there
if (not retx_queue->has_sn(nack_sn)) {
if (not retx_queue->has_sn(nack.nack_sn)) {
// Have we segmented the SDU already?
if ((*tx_window)[nack_sn].segment_list.empty()) {
if ((*tx_window)[nack.nack_sn].segment_list.empty()) {
rlc_amd_retx_nr_t& retx = retx_queue->push();
retx.sn = nack_sn;
retx.sn = nack.nack_sn;
retx.is_segment = false;
retx.so_start = 0;
retx.current_so = 0;
retx.segment_length = pdu.sdu_buf->N_bytes;
retx_sn_set.insert(nack_sn);
retx_sn_set.insert(nack.nack_sn);
RlcInfo("Scheduled RETX of SDU SN=%d", retx.sn);
} else {
RlcInfo("Scheduled RETX of SDU SN=%d", nack_sn);
retx_sn_set.insert(nack_sn);
for (auto segm : (*tx_window)[nack_sn].segment_list) {
RlcInfo("Scheduled RETX of SDU SN=%d", nack.nack_sn);
retx_sn_set.insert(nack.nack_sn);
for (auto segm : (*tx_window)[nack.nack_sn].segment_list) {
rlc_amd_retx_nr_t& retx = retx_queue->push();
retx.sn = nack_sn;
retx.sn = nack.nack_sn;
retx.is_segment = true;
retx.so_start = segm.so;
retx.current_so = segm.so;
@ -919,48 +960,22 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes)
}
} else {
RlcInfo("RETX queue already has NACK_SN. SDU SN=%d, Tx_Next_Ack=%d, Tx_Next=%d",
status.nacks[nack_idx].nack_sn,
nack.nack_sn,
st.tx_next_ack,
st.tx_next);
}
}
} else {
RlcInfo("TX window does not contain NACK_SN. SDU SN=%d, Tx_Next_Ack=%d, Tx_Next=%d",
status.nacks[nack_idx].nack_sn,
nack.nack_sn,
st.tx_next_ack,
st.tx_next);
} // TX window containts NACK SN
} else {
RlcInfo("RETX not in expected range. SDU SN=%d, Tx_Next_Ack=%d, Tx_Next=%d",
status.nacks[nack_idx].nack_sn,
st.tx_next_ack,
st.tx_next);
RlcInfo(
"RETX not in expected range. SDU SN=%d, Tx_Next_Ack=%d, Tx_Next=%d", nack.nack_sn, st.tx_next_ack, st.tx_next);
} // NACK SN within expected range
} // NACK loop
// Process retx_count and inform upper layers if needed
for (uint32_t retx_sn : retx_sn_set) {
auto& pdu = (*tx_window)[retx_sn];
// Increment retx_count
if (pdu.retx_count == RETX_COUNT_NOT_STARTED) {
// Set retx_count = 0 on first RE-transmission of associated SDU (38.322 Sec. 5.3.2)
pdu.retx_count = 0;
} else {
// Increment otherwise
pdu.retx_count++;
}
// Inform upper layers if needed
check_sn_reached_max_retx(retx_sn);
}
// Notify PDCP
if (not notify_info_vec.empty()) {
parent->pdcp->notify_delivery(parent->lcid, notify_info_vec);
}
notify_info_vec.clear();
}
/**
* Helper to check if a SN has reached the max reTx threshold
*
@ -1736,7 +1751,7 @@ uint32_t rlc_am_nr_rx::get_status_pdu(rlc_am_nr_status_pdu_t* status, uint32_t m
nack.nack_sn = i;
nack.has_so = true;
nack.so_start = last_so;
nack.so_end = so_end_of_sdu;
nack.so_end = rlc_status_nack_t::so_end_of_sdu;
status->push_nack(nack);
RlcDebug(
"Final segment missing. NACK_SN=%d. SO_start=%d, SO_end=%d", nack.nack_sn, nack.so_start, nack.so_end);

@ -28,7 +28,8 @@ namespace srsran {
* Container implementation for pack/unpack functions
***************************************************************************/
rlc_am_nr_status_pdu_t::rlc_am_nr_status_pdu_t(rlc_am_nr_sn_size_t sn_size) : sn_size(sn_size)
rlc_am_nr_status_pdu_t::rlc_am_nr_status_pdu_t(rlc_am_nr_sn_size_t sn_size) :
sn_size(sn_size), mod_nr(cardinality(sn_size))
{
nacks_.reserve(RLC_AM_NR_TYP_NACKS);
}
@ -41,10 +42,85 @@ void rlc_am_nr_status_pdu_t::reset()
packed_size_ = rlc_am_nr_status_pdu_sizeof_header_ack_sn;
}
bool rlc_am_nr_status_pdu_t::is_continuous_sequence(const rlc_status_nack_t& left, const rlc_status_nack_t& right) const
{
// SN must be continuous
if (right.nack_sn != ((left.has_nack_range ? left.nack_sn + left.nack_range : (left.nack_sn + 1)) % mod_nr)) {
return false;
}
// Segments on left side (if present) must reach the end of sdu
if (left.has_so && left.so_end != rlc_status_nack_t::so_end_of_sdu) {
return false;
}
// Segments on right side (if present) must start from the beginning
if (right.has_so && right.so_start != 0) {
return false;
}
return true;
}
void rlc_am_nr_status_pdu_t::push_nack(const rlc_status_nack_t& nack)
{
if (nacks_.size() == 0) {
nacks_.push_back(nack);
packed_size_ += nack_size(nack);
return;
}
rlc_status_nack_t& prev = nacks_.back();
if (is_continuous_sequence(prev, nack) == false) {
nacks_.push_back(nack);
packed_size_ += nack_size(nack);
return;
}
// expand previous NACK
// subtract size of previous NACK (add updated size later)
packed_size_ -= nack_size(prev);
// enable and update NACK range
if (nack.has_nack_range == true) {
if (prev.has_nack_range == true) {
// [NACK range][NACK range]
prev.nack_range += nack.nack_range;
} else {
// [NACK SDU][NACK range]
prev.nack_range = nack.nack_range + 1;
prev.has_nack_range = true;
}
} else {
if (prev.has_nack_range == true) {
// [NACK range][NACK SDU]
prev.nack_range++;
} else {
// [NACK SDU][NACK SDU]
prev.nack_range = 2;
prev.has_nack_range = true;
}
}
// enable and update segment offsets (if required)
if (nack.has_so == true) {
if (prev.has_so == false) {
// [NACK SDU][NACK segm]
prev.has_so = true;
prev.so_start = 0;
}
// [NACK SDU][NACK segm] or [NACK segm][NACK segm]
prev.so_end = nack.so_end;
} else {
if (prev.has_so == true) {
// [NACK segm][NACK SDU]
prev.so_end = rlc_status_nack_t::so_end_of_sdu;
}
// [NACK segm][NACK SDU] or [NACK SDU][NACK SDU]
}
// add updated size
packed_size_ += nack_size(prev);
}
bool rlc_am_nr_status_pdu_t::trim(uint32_t max_packed_size)

File diff suppressed because it is too large Load Diff

@ -2471,6 +2471,193 @@ bool poll_retx_expiry(rlc_am_nr_sn_size_t sn_size)
return SRSRAN_SUCCESS;
}
int rx_nack_range_no_so_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);
std::string str = "Rx NACK range test (" + std::to_string(to_number(sn_size)) + " bit SN)";
test_delimit_logger delimiter(str.c_str());
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());
auto rlc_cnfg = rlc_config_t::default_rlc_am_nr_config(to_number(sn_size));
rlc_cnfg.am_nr.t_poll_retx = -1;
if (not rlc1.configure(rlc_cnfg)) {
return -1;
}
// after configuring entity
TESTASSERT(0 == rlc1.get_buffer_state());
int n_sdu_bufs = 5;
int n_pdu_bufs = 15;
// Push 5 SDUs into RLC1
std::vector<unique_byte_buffer_t> sdu_bufs(n_sdu_bufs);
constexpr uint32_t payload_size = 3; // Give the SDU the size of 3 bytes
uint32_t header_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3;
for (int i = 0; i < n_sdu_bufs; i++) {
sdu_bufs[i] = srsran::make_byte_buffer();
sdu_bufs[i]->msg[0] = i; // Write the index into the buffer
sdu_bufs[i]->N_bytes = payload_size; // Give each buffer a size of 3 bytes
sdu_bufs[i]->md.pdcp_sn = i; // PDCP SN for notifications
rlc1.write_sdu(std::move(sdu_bufs[i]));
}
uint32_t expected_buffer_state = (header_size + payload_size) * n_sdu_bufs;
TESTASSERT_EQ(expected_buffer_state, rlc1.get_buffer_state());
constexpr uint32_t so_size = 2;
constexpr uint32_t segment_size = 1;
uint32_t pdu_size_first = header_size + segment_size;
uint32_t pdu_size_continued = header_size + so_size + segment_size;
// Read 15 PDUs from RLC1
std::vector<unique_byte_buffer_t> pdu_bufs(n_pdu_bufs);
for (int i = 0; i < n_pdu_bufs; i++) {
// First also test buffer state
uint32_t remaining_total_bytes = (payload_size * n_sdu_bufs) - (i * segment_size);
uint32_t remaining_full_sdus = remaining_total_bytes / payload_size;
uint32_t remaining_seg_bytes = remaining_total_bytes % payload_size;
uint32_t buffer_state_full_sdus = (header_size + payload_size) * remaining_full_sdus;
uint32_t buffer_state_seg_sdu = remaining_seg_bytes == 0 ? 0 : (header_size + so_size + remaining_seg_bytes);
expected_buffer_state = buffer_state_full_sdus + buffer_state_seg_sdu;
TESTASSERT_EQ(expected_buffer_state, rlc1.get_buffer_state());
pdu_bufs[i] = srsran::make_byte_buffer();
if (i == 0 || i == 3 || i == 6 || i == 9 || i == 12) {
// First segment, no SO
uint32_t len = rlc1.read_pdu(pdu_bufs[i]->msg, pdu_size_first); // 2 bytes for header + 1 byte payload
pdu_bufs[i]->N_bytes = len;
TESTASSERT_EQ(pdu_size_first, len);
} else {
// Middle or last segment, SO present
uint32_t len = rlc1.read_pdu(pdu_bufs[i]->msg, pdu_size_continued); // 4 bytes for header + 1 byte payload
pdu_bufs[i]->N_bytes = len;
TESTASSERT_EQ(pdu_size_continued, len);
}
}
// Deliver dummy status report with nack range betwen PDU 6 and 10.
rlc_am_nr_status_pdu_t status(sn_size);
rlc_status_nack_t nack = {};
nack.nack_sn = 1;
nack.has_nack_range = true;
nack.nack_range = 3;
status.push_nack(nack);
byte_buffer_t status_pdu;
rlc_am_nr_write_status_pdu(status, sn_size, &status_pdu);
rlc1.write_pdu(status_pdu.msg, status_pdu.N_bytes);
TESTASSERT_EQ(3 * pdu_size_first + 6 * pdu_size_continued, rlc1.get_buffer_state());
return SRSRAN_SUCCESS;
}
int rx_nack_range_with_so_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);
std::string str = "Rx NACK range test (" + std::to_string(to_number(sn_size)) + " bit SN)";
test_delimit_logger delimiter(str.c_str());
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());
auto rlc_cnfg = rlc_config_t::default_rlc_am_nr_config(to_number(sn_size));
rlc_cnfg.am_nr.t_poll_retx = -1;
if (not rlc1.configure(rlc_cnfg)) {
return -1;
}
// after configuring entity
TESTASSERT(0 == rlc1.get_buffer_state());
int n_sdu_bufs = 5;
int n_pdu_bufs = 15;
// Push 5 SDUs into RLC1
std::vector<unique_byte_buffer_t> sdu_bufs(n_sdu_bufs);
constexpr uint32_t payload_size = 3; // Give the SDU the size of 3 bytes
uint32_t header_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3;
for (int i = 0; i < n_sdu_bufs; i++) {
sdu_bufs[i] = srsran::make_byte_buffer();
sdu_bufs[i]->msg[0] = i; // Write the index into the buffer
sdu_bufs[i]->N_bytes = payload_size; // Give each buffer a size of 3 bytes
sdu_bufs[i]->md.pdcp_sn = i; // PDCP SN for notifications
rlc1.write_sdu(std::move(sdu_bufs[i]));
}
uint32_t expected_buffer_state = (header_size + payload_size) * n_sdu_bufs;
TESTASSERT_EQ(expected_buffer_state, rlc1.get_buffer_state());
constexpr uint32_t so_size = 2;
constexpr uint32_t segment_size = 1;
uint32_t pdu_size_first = header_size + segment_size;
uint32_t pdu_size_continued = header_size + so_size + segment_size;
// Read 15 PDUs from RLC1
std::vector<unique_byte_buffer_t> pdu_bufs(n_pdu_bufs);
for (int i = 0; i < n_pdu_bufs; i++) {
// First also test buffer state
uint32_t remaining_total_bytes = (payload_size * n_sdu_bufs) - (i * segment_size);
uint32_t remaining_full_sdus = remaining_total_bytes / payload_size;
uint32_t remaining_seg_bytes = remaining_total_bytes % payload_size;
uint32_t buffer_state_full_sdus = (header_size + payload_size) * remaining_full_sdus;
uint32_t buffer_state_seg_sdu = remaining_seg_bytes == 0 ? 0 : (header_size + so_size + remaining_seg_bytes);
expected_buffer_state = buffer_state_full_sdus + buffer_state_seg_sdu;
TESTASSERT_EQ(expected_buffer_state, rlc1.get_buffer_state());
pdu_bufs[i] = srsran::make_byte_buffer();
if (i == 0 || i == 3 || i == 6 || i == 9 || i == 12) {
// First segment, no SO
uint32_t len = rlc1.read_pdu(pdu_bufs[i]->msg, pdu_size_first); // 2 bytes for header + 1 byte payload
pdu_bufs[i]->N_bytes = len;
TESTASSERT_EQ(pdu_size_first, len);
} else {
// Middle or last segment, SO present
uint32_t len = rlc1.read_pdu(pdu_bufs[i]->msg, pdu_size_continued); // 4 bytes for header + 1 byte payload
pdu_bufs[i]->N_bytes = len;
TESTASSERT_EQ(pdu_size_continued, len);
}
}
// Deliver dummy status report with nack range betwen PDU 6 and 10.
rlc_am_nr_status_pdu_t status(sn_size);
rlc_status_nack_t nack = {};
nack.nack_sn = 1;
nack.has_nack_range = true;
nack.nack_range = 3;
nack.has_so = true;
nack.so_start = 2;
nack.so_end = 0;
status.push_nack(nack);
byte_buffer_t status_pdu;
rlc_am_nr_write_status_pdu(status, sn_size, &status_pdu);
rlc1.write_pdu(status_pdu.msg, status_pdu.N_bytes);
TESTASSERT_EQ(2 * pdu_size_first + 3 * pdu_size_continued, rlc1.get_buffer_state());
return SRSRAN_SUCCESS;
}
int main()
{
// Setup the log message spy to intercept error and warning log entries from RLC
@ -2515,6 +2702,8 @@ int main()
TESTASSERT(poll_byte(sn_size) == SRSRAN_SUCCESS);
TESTASSERT(poll_retx(sn_size) == SRSRAN_SUCCESS);
TESTASSERT(poll_retx_expiry(sn_size) == SRSRAN_SUCCESS);
TESTASSERT(rx_nack_range_no_so_test(sn_size) == SRSRAN_SUCCESS);
TESTASSERT(rx_nack_range_with_so_test(sn_size) == SRSRAN_SUCCESS);
}
return SRSRAN_SUCCESS;
}

@ -95,6 +95,40 @@ qci_config = (
);
// 5G Section
// srb1_5g_config = {
// rlc_config = {
// ul_am = {
// sn_field_len = 12;
// t_poll_retx = 50;
// poll_pdu = 4;
// poll_byte = 3000;
// max_retx_thres = 4;
// };
// dl_am = {
// sn_field_len = 12;
// t_reassembly = 50;
// t_status_prohibit = 50;
// };
// };
// }
// srb2_5g_config = {
// rlc_config = {
// ul_am = {
// sn_field_len = 12;
// t_poll_retx = 50;
// poll_pdu = 4;
// poll_byte = 3000;
// max_retx_thres = 4;
// };
// dl_am = {
// sn_field_len = 12;
// t_reassembly = 50;
// t_status_prohibit = 50;
// };
// };
// }
five_qi_config = (
{
five_qi = 7;

@ -644,6 +644,87 @@ int field_qci::parse(libconfig::Setting& root)
return 0;
}
int field_5g_srb::parse(libconfig::Setting& root)
{
// Parse RLC AM section
asn1::rrc_nr::rlc_cfg_c* rlc_cfg = &cfg.rlc_cfg;
if (root.exists("ul_am") && root.exists("dl_am")) {
rlc_cfg->set_am();
cfg.present = true;
}
// RLC-UM must not exist in this section
if (root.exists("ul_um") || root.exists("dl_um")) {
ERROR("Error SRBs must be AM.");
return SRSRAN_ERROR;
}
// Parse RLC-AM section
if (root.exists("ul_am")) {
asn1::rrc_nr::ul_am_rlc_s& ul_am_rlc = rlc_cfg->am().ul_am_rlc;
// SN length
field_asn1_enum_number<asn1::rrc_nr::sn_field_len_am_e> rlc_sn_size_ul("sn_field_len", &ul_am_rlc.sn_field_len);
if (rlc_sn_size_ul.parse(root["ul_am"]) == SRSRAN_ERROR) {
ul_am_rlc.sn_field_len_present = false;
} else {
ul_am_rlc.sn_field_len_present = true;
}
field_asn1_enum_number<asn1::rrc_nr::t_poll_retx_e> t_poll_retx("t_poll_retx", &ul_am_rlc.t_poll_retx);
if (t_poll_retx.parse(root["ul_am"])) {
ERROR("Error can't find t_poll_retx in section ul_am");
return SRSRAN_ERROR;
}
field_asn1_enum_number<asn1::rrc_nr::poll_pdu_e> poll_pdu("poll_pdu", &ul_am_rlc.poll_pdu);
if (poll_pdu.parse(root["ul_am"])) {
ERROR("Error can't find poll_pdu in section ul_am");
return SRSRAN_ERROR;
}
field_asn1_enum_number<asn1::rrc_nr::poll_byte_e> poll_byte("poll_byte", &ul_am_rlc.poll_byte);
if (poll_byte.parse(root["ul_am"])) {
ERROR("Error can't find poll_byte in section ul_am");
return SRSRAN_ERROR;
}
field_asn1_enum_number<asn1::rrc_nr::ul_am_rlc_s::max_retx_thres_e_> max_retx_thresh("max_retx_thres",
&ul_am_rlc.max_retx_thres);
if (max_retx_thresh.parse(root["ul_am"])) {
ERROR("Error can't find max_retx_thresh in section ul_am");
return SRSRAN_ERROR;
}
}
if (root.exists("dl_am")) {
asn1::rrc_nr::dl_am_rlc_s& dl_am_rlc = rlc_cfg->am().dl_am_rlc;
// SN length
field_asn1_enum_number<asn1::rrc_nr::sn_field_len_am_e> rlc_sn_size_ul("sn_field_len", &dl_am_rlc.sn_field_len);
if (rlc_sn_size_ul.parse(root["dl_am"]) == SRSRAN_ERROR) {
dl_am_rlc.sn_field_len_present = false;
} else {
dl_am_rlc.sn_field_len_present = true;
}
field_asn1_enum_number<asn1::rrc_nr::t_reassembly_e> t_reassembly("t_reassembly", &dl_am_rlc.t_reassembly);
if (t_reassembly.parse(root["dl_am"])) {
ERROR("Error can't find t_reordering in section dl_am");
return SRSRAN_ERROR;
}
field_asn1_enum_number<asn1::rrc_nr::t_status_prohibit_e> t_status_prohibit("t_status_prohibit",
&dl_am_rlc.t_status_prohibit);
if (t_status_prohibit.parse(root["dl_am"])) {
ERROR("Error can't find t_status_prohibit in section dl_am");
return SRSRAN_ERROR;
}
}
return 0;
}
int field_five_qi::parse(libconfig::Setting& root)
{
uint32_t nof_five_qi = (uint32_t)root.getLength();
@ -2360,6 +2441,22 @@ int parse_rb(all_args_t* args_, rrc_cfg_t* rrc_cfg_, rrc_nr_cfg_t* rrc_nr_cfg_)
parser::section qci("qci_config");
qci.add_field(new field_qci(rrc_cfg_->qci_cfg));
parser::section srb1_5g("srb1_5g_config");
bool srb1_5g_present = false;
srb1_5g.set_optional(&srb1_5g_present);
parser::section srb1_5g_rlc_cfg("rlc_config");
srb1_5g.add_subsection(&srb1_5g_rlc_cfg);
srb1_5g_rlc_cfg.add_field(new field_5g_srb(rrc_nr_cfg_->srb1_cfg));
parser::section srb2_5g("srb2_5g_config");
bool srb2_5g_present = false;
srb2_5g.set_optional(&srb2_5g_present);
parser::section srb2_5g_rlc_cfg("rlc_config");
srb2_5g.add_subsection(&srb2_5g_rlc_cfg);
srb2_5g_rlc_cfg.add_field(new field_5g_srb(rrc_nr_cfg_->srb2_cfg));
parser::section five_qi("five_qi_config");
five_qi.add_field(new field_five_qi(rrc_nr_cfg_->five_qi_cfg));
@ -2368,6 +2465,8 @@ int parse_rb(all_args_t* args_, rrc_cfg_t* rrc_cfg_, rrc_nr_cfg_t* rrc_nr_cfg_)
p.add_section(&srb1);
p.add_section(&srb2);
p.add_section(&qci);
p.add_section(&srb1_5g);
p.add_section(&srb2_5g);
p.add_section(&five_qi);
int ret = p.parse();
@ -2377,6 +2476,8 @@ int parse_rb(all_args_t* args_, rrc_cfg_t* rrc_cfg_, rrc_nr_cfg_t* rrc_nr_cfg_)
if (not srb2_present) {
rrc_cfg_->srb2_cfg.rlc_cfg.set_default_value();
}
rrc_nr_cfg_->srb1_cfg.present = srb1_5g_present;
rrc_nr_cfg_->srb2_cfg.present = srb1_5g_present;
return ret;
}

@ -202,6 +202,18 @@ private:
std::map<uint32_t, rrc_cfg_qci_t>& cfg;
};
class field_5g_srb final : public parser::field_itf
{
public:
explicit field_5g_srb(srb_5g_cfg_t& cfg_) : cfg(cfg_) {}
const char* get_name() override { return "field_5g_srb"; }
int parse(Setting& root) override;
private:
srb_5g_cfg_t& cfg;
};
class field_five_qi final : public parser::field_itf
{
public:

@ -18,7 +18,7 @@
# and at http://www.gnu.org/licenses/.
#
add_library(test_helpers test_helpers.cc)
add_library(test_helpers STATIC test_helpers.cc)
target_link_libraries(test_helpers srsenb_rrc srsenb_common rrc_asn1 rrc_nr_asn1 s1ap_asn1 srsran_common enb_cfg_parser ${LIBCONFIGPP_LIBRARIES})
add_executable(rrc_meascfg_test rrc_meascfg_test.cc)

@ -54,6 +54,11 @@ struct rrc_cell_cfg_nr_t {
typedef std::vector<rrc_cell_cfg_nr_t> rrc_cell_list_nr_t;
struct srb_5g_cfg_t {
bool present = false;
asn1::rrc_nr::rlc_cfg_c rlc_cfg;
};
struct rrc_nr_cfg_five_qi_t {
bool configured = false;
asn1::rrc_nr::pdcp_cfg_s pdcp_cfg;
@ -68,6 +73,9 @@ struct rrc_nr_cfg_t {
uint16_t mnc;
bool is_standalone;
srb_5g_cfg_t srb1_cfg;
srb_5g_cfg_t srb2_cfg;
std::map<uint32_t, rrc_nr_cfg_five_qi_t> five_qi_cfg;
std::array<srsran::CIPHERING_ALGORITHM_ID_NR_ENUM, srsran::CIPHERING_ALGORITHM_ID_NR_N_ITEMS> nea_preference_list;

@ -20,7 +20,7 @@
set_directory_properties(PROPERTIES LABELS "sched;nr")
add_library(sched_nr_test_suite sched_nr_common_test.cc sched_nr_ue_ded_test_suite.cc sched_nr_sim_ue.cc)
add_library(sched_nr_test_suite STATIC sched_nr_common_test.cc sched_nr_ue_ded_test_suite.cc sched_nr_sim_ue.cc)
target_link_libraries(sched_nr_test_suite srsgnb_mac srsran_common rrc_nr_asn1)
add_executable(sched_nr_parallel_test sched_nr_parallel_test.cc)

@ -956,6 +956,21 @@ void fill_srb(const rrc_nr_cfg_t& cfg, srsran::nr_srb srb_id, asn1::rrc_nr::rlc_
out.served_radio_bearer_present = true;
out.served_radio_bearer.set_srb_id() = (uint8_t)srb_id;
if (srb_id == srsran::nr_srb::srb1) {
if (cfg.srb1_cfg.present) {
out.rlc_cfg_present = true;
out.rlc_cfg = cfg.srb1_cfg.rlc_cfg;
} else {
out.rlc_cfg_present = false;
}
} else if (srb_id == srsran::nr_srb::srb2) {
if (cfg.srb2_cfg.present) {
out.rlc_cfg_present = true;
out.rlc_cfg = cfg.srb2_cfg.rlc_cfg;
} else {
out.rlc_cfg_present = false;
}
} else {
out.rlc_cfg_present = true;
auto& ul_am = out.rlc_cfg.set_am().ul_am_rlc;
ul_am.sn_field_len_present = true;
@ -969,6 +984,7 @@ void fill_srb(const rrc_nr_cfg_t& cfg, srsran::nr_srb srb_id, asn1::rrc_nr::rlc_
dl_am.sn_field_len.value = asn1::rrc_nr::sn_field_len_am_opts::size12;
dl_am.t_reassembly.value = t_reassembly_opts::ms35;
dl_am.t_status_prohibit.value = asn1::rrc_nr::t_status_prohibit_opts::ms0;
}
// mac-LogicalChannelConfig -- Cond LCH-Setup
out.mac_lc_ch_cfg_present = true;

@ -1418,14 +1418,31 @@ int rrc_nr::ue::update_rlc_bearers(const asn1::rrc_nr::cell_group_cfg_s& cell_gr
// Add/Mod RLC radio bearers
for (const rlc_bearer_cfg_s& rb : cell_group_diff.rlc_bearer_to_add_mod_list) {
srsran::rlc_config_t rlc_cfg;
uint8_t rb_id = rb.served_radio_bearer.type().value == rlc_bearer_cfg_s::served_radio_bearer_c_::types_opts::drb_id
? rb.served_radio_bearer.drb_id()
: rb.served_radio_bearer.srb_id();
uint8_t rb_id = 0;
if (rb.served_radio_bearer.type().value == rlc_bearer_cfg_s::served_radio_bearer_c_::types_opts::srb_id) {
rb_id = rb.served_radio_bearer.srb_id();
if (not rb.rlc_cfg_present) {
rlc_cfg = srsran::rlc_config_t::default_rlc_am_nr_config();
} else {
if (srsran::make_rlc_config_t(rb.rlc_cfg, rb_id, &rlc_cfg) != SRSRAN_SUCCESS) {
logger.error("Failed to build RLC config");
// TODO: HANDLE
return SRSRAN_ERROR;
}
}
} else {
rb_id = rb.served_radio_bearer.drb_id();
if (not rb.rlc_cfg_present) {
logger.error("No RLC config for DRB");
// TODO: HANDLE
return SRSRAN_ERROR;
}
if (srsran::make_rlc_config_t(rb.rlc_cfg, rb_id, &rlc_cfg) != SRSRAN_SUCCESS) {
logger.error("Failed to build RLC config");
// TODO: HANDLE
return SRSRAN_ERROR;
}
}
parent->rlc->add_bearer(rnti, rb.lc_ch_id, rlc_cfg);
}

@ -18,7 +18,7 @@
# and at http://www.gnu.org/licenses/.
#
add_library(rrc_nr_test_helpers rrc_nr_test_helpers.cc)
add_library(rrc_nr_test_helpers STATIC rrc_nr_test_helpers.cc)
add_executable(rrc_nr_test rrc_nr_test.cc)
target_link_libraries(rrc_nr_test srsgnb_rrc srsgnb_rrc_config_utils srsran_common rrc_nr_asn1 rrc_nr_test_helpers srsgnb_mac ${ATOMIC_LIBS})

@ -1070,6 +1070,9 @@ bool rrc_nr::apply_rlc_add_mod(const rlc_bearer_cfg_s& rlc_bearer_cfg)
logger.error("Failed to build RLC config");
return false;
}
} else if (not is_drb) {
logger.debug("Using default RLC configs for SRB%d", srb_id);
rlc_cfg = rlc_config_t::default_rlc_am_nr_config();
} else {
logger.error("In RLC bearer cfg does not contain rlc cfg");
return false;

Loading…
Cancel
Save