mac_nr: add support for periodic BSR reporting

building on the previous refactor this patch now adds support
for peridoic BSR reporting (using short BSR). It furthermore does
the following changes:

* add BSR packing
* add proc_bsr_nr unit test
* move mac_nr test code into test folder under src (needs to be done with other test code too)
master
Andre Puschmann 4 years ago
parent 7188603f2e
commit db496df1e6

@ -92,6 +92,7 @@ public:
void set_c_rnti(const uint16_t crnti_);
void set_se_phr(const uint8_t phr_, const uint8_t pcmax_);
void set_sbsr(const lcg_bsr_t bsr_);
void set_lbsr(const std::array<mac_sch_subpdu_nr::lcg_bsr_t, max_num_lcg_lbsr> bsr_);
uint32_t write_subpdu(const uint8_t* start_);
@ -127,11 +128,12 @@ public:
void init_rx(bool ulsch_ = false);
// Add SDU or CEs to PDU
// All functions will return SRSRAN_SUCCESS on success, and SRSLE_ERROR otherwise
// All functions will return SRSRAN_SUCCESS on success, and SRSRAN_ERROR otherwise
uint32_t add_sdu(const uint32_t lcid_, const uint8_t* payload_, const uint32_t len_);
uint32_t add_crnti_ce(const uint16_t crnti_);
uint32_t add_se_phr_ce(const uint8_t phr_, const uint8_t pcmax_);
uint32_t add_sbsr_ce(const mac_sch_subpdu_nr::lcg_bsr_t bsr_);
uint32_t add_lbsr_ce(const std::array<mac_sch_subpdu_nr::lcg_bsr_t, mac_sch_subpdu_nr::max_num_lcg_lbsr> bsr_);
uint32_t get_remaing_len();

@ -136,6 +136,9 @@ void mac_sch_subpdu_nr::set_sbsr(const lcg_bsr_t bsr_)
ce_write_buffer.at(0) = ((bsr_.lcg_id & 0x07) << 5) | (bsr_.buffer_size & 0x1f);
}
// Turn a subPDU into a long BSR with variable size
void mac_sch_subpdu_nr::set_lbsr(const std::array<mac_sch_subpdu_nr::lcg_bsr_t, max_num_lcg_lbsr> bsr_) {}
// Section 6.1.2
uint32_t mac_sch_subpdu_nr::write_subpdu(const uint8_t* start_)
{
@ -400,6 +403,14 @@ uint32_t mac_sch_pdu_nr::add_sbsr_ce(const mac_sch_subpdu_nr::lcg_bsr_t bsr_)
return add_sudpdu(ce);
}
uint32_t
mac_sch_pdu_nr::add_lbsr_ce(const std::array<mac_sch_subpdu_nr::lcg_bsr_t, mac_sch_subpdu_nr::max_num_lcg_lbsr> bsr_)
{
mac_sch_subpdu_nr ce(this);
ce.set_lbsr(bsr_);
return add_sudpdu(ce);
}
uint32_t mac_sch_pdu_nr::add_sudpdu(mac_sch_subpdu_nr& subpdu)
{
uint32_t subpdu_len = subpdu.get_total_length();

@ -88,6 +88,8 @@ private:
std::map<uint32_t, lcid_t> lcgs[NOF_LCG]; // groups LCID in LCG
mac_buffer_states_t old_buffer_state;
uint32_t find_max_priority_lcg_with_data();
bsr_trigger_type_t triggered_bsr_type = NONE;

@ -13,6 +13,10 @@
#ifndef SRSUE_MAC_COMMON_H
#define SRSUE_MAC_COMMON_H
#include "srsran/common/string_helpers.h"
#include "srsran/srslog/srslog.h"
#include <map>
/**
* @brief Common definitions/interfaces between LTE/NR MAC components
*
@ -25,6 +29,51 @@ namespace srsue {
typedef enum { NONE, REGULAR, PADDING, PERIODIC } bsr_trigger_type_t;
char* bsr_trigger_type_tostring(bsr_trigger_type_t type);
/// Helper class to store a snapshot of buffer states for all LCGs/LCIDs
class mac_buffer_states_t
{
public:
explicit mac_buffer_states_t() {}
void reset()
{
nof_lcids_with_data = 0;
nof_lcgs_with_data = 0;
last_non_zero_lcg = -1;
lcid_buffer_size.clear();
lcg_buffer_size.clear();
}
std::string to_string()
{
fmt::memory_buffer buffer;
fmt::format_to(buffer,
"nof_lcids_with_data={}, nof_lcgs_with_data={}, last_non_zero_lcg={} ",
nof_lcids_with_data,
nof_lcgs_with_data,
last_non_zero_lcg);
fmt::format_to(buffer, "[");
for (const auto& lcg : lcg_buffer_size) {
fmt::format_to(buffer, "lcg{}={}, ", lcg.first, lcg.second);
}
fmt::format_to(buffer, "] ");
fmt::format_to(buffer, "[");
for (const auto& lcid : lcid_buffer_size) {
fmt::format_to(buffer, "lcid{}={}, ", lcid.first, lcid.second);
}
fmt::format_to(buffer, "]");
return srsran::to_c_str(buffer);
}
std::map<uint32_t, uint32_t> lcid_buffer_size; // Buffer size entry for each LCID
std::map<uint32_t, uint32_t> lcg_buffer_size; // Entry for each LCG (sum of LCIDs of that LCG)
uint32_t nof_lcids_with_data = 0; // Is incremented when a LCID is found with data to transmit
uint32_t nof_lcgs_with_data = 0; // Is incremented when a LCG is found with data to transmit
int32_t last_non_zero_lcg = -1; // only valid if nof_lcgs_with_data is at least one
};
} // namespace srsue
#endif // SRSUE_MAC_COMMON_H

@ -22,6 +22,7 @@
#include "srsran/interfaces/mac_interface_types.h"
#include "srsran/interfaces/ue_nr_interfaces.h"
#include "srsran/srslog/srslog.h"
#include "srsue/hdr/stack/mac_common/mac_common.h"
#include "srsue/hdr/stack/mac_nr/mux_nr.h"
#include "srsue/hdr/stack/ue_stack_base.h"
@ -81,6 +82,9 @@ public:
uint64_t get_contention_id();
uint16_t get_crnti();
/// Interface for MUX
srsran::mac_sch_subpdu_nr::lcg_bsr_t generate_sbsr();
void msg3_flush() { mux.msg3_flush(); }
bool msg3_is_transmitted() { return mux.msg3_is_transmitted(); }
void msg3_prepare() { mux.msg3_prepare(); }
@ -109,6 +113,12 @@ private:
bool has_crnti();
bool is_valid_crnti(const uint16_t crnti);
std::vector<srsran::logical_channel_config_t> logical_channels; // stores the raw configs provide by upper layers
/// LCID and LCG related members and helper functions
void update_buffer_states();
mac_buffer_states_t mac_buffer_states;
/// Interaction with rest of the stack
phy_interface_mac_nr* phy = nullptr;
rlc_interface_mac* rlc = nullptr;

@ -14,6 +14,7 @@
#define SRSUE_MAC_NR_INTERFACES_H
#include "srsran/common/interfaces_common.h"
#include "srsran/mac/mac_sch_pdu_nr.h"
namespace srsue {
/**
@ -45,6 +46,9 @@ class mac_interface_mux_nr
public:
// MUX can query MAC for current C-RNTI for Msg3 transmission
virtual uint16_t get_crnti() = 0;
// MUX queries MAC to return LCG state for SBSR
virtual srsran::mac_sch_subpdu_nr::lcg_bsr_t generate_sbsr() = 0;
};
} // namespace srsue

@ -33,8 +33,6 @@ public:
void reset();
int32_t init(rlc_interface_mac* rlc_);
void step();
void msg3_flush();
void msg3_prepare();
void msg3_transmitted();
@ -49,7 +47,7 @@ public:
srsran::unique_byte_buffer_t get_pdu(uint32_t max_pdu_len);
// Interface for BSR procedure
void generate_bsr_mac_ce();
void generate_bsr_mac_ce(const bsr_interface_mux_nr::bsr_format_nr_t& format);
private:
// internal helper methods
@ -71,6 +69,8 @@ private:
srsran::mac_sch_pdu_nr tx_pdu;
enum { no_bsr, sbsr_ce, lbsr_ce } add_bsr_ce = no_bsr; /// BSR procedure requests MUX to add a BSR CE
// Mutex for exclusive access
std::mutex mutex;
};

@ -18,6 +18,7 @@
#include "proc_sr_nr.h"
#include "srsran/common/task_scheduler.h"
#include "srsran/mac/mac_sch_pdu_nr.h"
#include "srsran/srslog/srslog.h"
#include "srsue/hdr/stack/mac_common/mac_common.h"
@ -34,21 +35,15 @@ public:
// TS 38.321 Sec 6.1.3.1
typedef enum { SHORT_BSR, LONG_BSR, SHORT_TRUNC_BSR, LONG_TRUNC_BSR } bsr_format_nr_t;
// FIXME: this will be replaced
typedef struct {
bsr_format_nr_t format;
uint32_t buff_size[4];
} bsr_t;
/// MUX calls BSR to let it generate a padding BSR if there is space in PDU.
virtual bool generate_padding_bsr(uint32_t nof_padding_bytes, bsr_t* bsr) = 0;
/// MUX calls BSR to receive the buffer state of a single LCG.
virtual srsran::mac_sch_subpdu_nr::lcg_bsr_t generate_sbsr() = 0;
};
class mux_interface_bsr_nr
{
public:
/// Inform MUX unit to that a BSR needs to be generated in the next UL transmission.
virtual void generate_bsr_mac_ce() = 0;
virtual void generate_bsr_mac_ce(const bsr_interface_mux_nr::bsr_format_nr_t& format) = 0;
};
/**
@ -59,25 +54,24 @@ public:
class proc_bsr_nr : public srsran::timer_callback, public bsr_interface_mux_nr
{
public:
explicit proc_bsr_nr(srslog::basic_logger& logger) : logger(logger) {}
explicit proc_bsr_nr(srslog::basic_logger& logger_) : logger(logger_) {}
int init(proc_sr_nr* sr_proc,
mux_interface_bsr_nr* mux_,
rlc_interface_mac* rlc,
srsran::ext_task_sched_handle* task_sched_);
void step(uint32_t tti);
void step(uint32_t tti, const mac_buffer_states_t& new_buffer_state);
void reset();
int set_config(const srsran::bsr_cfg_nr_t& bsr_cfg);
int setup_lcid(uint32_t lcid, uint32_t lcg, uint32_t priority);
void timer_expired(uint32_t timer_id);
uint32_t get_buffer_state();
/// Called by MAC when an UL grant is received
void new_grant_ul(uint32_t grant_size);
// bool need_to_send_bsr();
bool generate_padding_bsr(uint32_t nof_padding_bytes, bsr_t* bsr);
void update_bsr_tti_end(const bsr_t* bsr);
/// MUX interface for BSR generation
srsran::mac_sch_subpdu_nr::lcg_bsr_t generate_sbsr();
bool generate_padding_bsr(uint32_t nof_padding_bytes);
private:
const static int QUEUE_STATUS_PERIOD_MS = 1000;
@ -94,27 +88,20 @@ private:
bool initiated = false;
const static int MAX_NOF_LCG = 8;
typedef struct {
int priority;
uint32_t old_buffer;
uint32_t new_buffer;
} lcid_t;
mac_buffer_states_t buffer_state;
std::map<uint32_t, lcid_t> lcgs[MAX_NOF_LCG]; // groups LCID in LCG
// map of LCGs and their priorities, key is the priority (sorted) and the value the LCG
std::map<uint32_t, uint32_t> lcg_priorities;
bsr_trigger_type_t triggered_bsr_type = NONE;
void print_state();
void set_trigger(bsr_trigger_type_t new_trigger);
void update_new_data();
void update_old_buffer();
bool check_highest_channel();
bool check_new_data();
bool check_new_data(const mac_buffer_states_t& new_buffer_state);
bool check_any_channel();
uint32_t get_buffer_state_lcg(uint32_t lcg);
bool generate_bsr(bsr_t* bsr, uint32_t nof_padding_bytes);
uint8_t buff_size_bytes_to_field(uint32_t buffer_size, bsr_format_nr_t format);
uint32_t find_max_priority_lcg_with_data();

@ -8,4 +8,6 @@
set(SOURCES mac_nr.cc proc_ra_nr.cc proc_bsr_nr.cc proc_sr_nr.cc mux_nr.cc)
add_library(srsue_mac_nr STATIC ${SOURCES})
target_link_libraries(srsue_mac_nr srsue_mac_common srsran_mac)
target_link_libraries(srsue_mac_nr srsue_mac_common srsran_mac)
add_subdirectory(test)

@ -99,6 +99,31 @@ void mac_nr::run_tti(const uint32_t tti)
{
// Step all procedures
logger.debug("Running MAC tti=%d", tti);
// Update state for all LCIDs/LCGs once so all procedures can use them
update_buffer_states();
proc_bsr.step(tti, mac_buffer_states);
proc_sr.step(tti);
}
void mac_nr::update_buffer_states()
{
// reset variables
mac_buffer_states.reset();
for (auto& channel : logical_channels) {
uint32_t buffer_len = rlc->get_buffer_state(channel.lcid);
if (buffer_len > 0) {
mac_buffer_states.nof_lcids_with_data++;
if (channel.lcg != mac_buffer_states.last_non_zero_lcg) {
mac_buffer_states.nof_lcgs_with_data++;
}
mac_buffer_states.last_non_zero_lcg = channel.lcg;
}
mac_buffer_states.lcid_buffer_size[channel.lcid] += buffer_len;
mac_buffer_states.lcg_buffer_size[channel.lcg] += buffer_len;
}
logger.info("%s", mac_buffer_states.to_string());
}
mac_interface_phy_nr::sched_rnti_t mac_nr::get_ul_sched_rnti_nr(const uint32_t tti)
@ -156,6 +181,11 @@ uint16_t mac_nr::get_crnti()
return c_rnti;
}
srsran::mac_sch_subpdu_nr::lcg_bsr_t mac_nr::generate_sbsr()
{
return proc_bsr.generate_sbsr();
}
void mac_nr::bch_decoded_ok(uint32_t tti, srsran::unique_byte_buffer_t payload)
{
// Send MIB to RLC
@ -285,6 +315,9 @@ int mac_nr::setup_lcid(const srsran::logical_channel_config_t& config)
config.BSD,
config.bucket_size);
// store full config
logical_channels.push_back(config);
return SRSRAN_SUCCESS;
}

@ -62,6 +62,10 @@ srsran::unique_byte_buffer_t mux_nr::get_pdu(uint32_t max_pdu_len)
msg3_transmitted();
} else {
// Pack normal UL data PDU
if (add_bsr_ce == sbsr_ce) {
tx_pdu.add_sbsr_ce(mac.generate_sbsr());
add_bsr_ce = no_bsr;
}
// TODO: Add proper priority handling
for (const auto& lc : logical_channels) {
@ -91,7 +95,7 @@ srsran::unique_byte_buffer_t mux_nr::get_pdu(uint32_t max_pdu_len)
// Pack PDU
tx_pdu.pack();
logger.info(phy_tx_pdu->msg, phy_tx_pdu->N_bytes, "Generated MAC PDU (%d B)", phy_tx_pdu->N_bytes);
logger.debug(phy_tx_pdu->msg, phy_tx_pdu->N_bytes, "Generated MAC PDU (%d B)", phy_tx_pdu->N_bytes);
return phy_tx_pdu;
}
@ -127,6 +131,17 @@ bool mux_nr::msg3_is_empty()
return msg3_buff->N_bytes == 0;
}
void mux_nr::generate_bsr_mac_ce() {}
void mux_nr::generate_bsr_mac_ce(const bsr_interface_mux_nr::bsr_format_nr_t& format)
{
switch (format) {
case bsr_interface_mux_nr::SHORT_BSR:
add_bsr_ce = sbsr_ce;
break;
case bsr_interface_mux_nr::LONG_BSR:
add_bsr_ce = lbsr_ce;
default:
logger.error("MUX can only be instructred to generate short or long BSRs.");
}
}
} // namespace srsue

@ -16,6 +16,68 @@
namespace srsue {
// TS 38.321, Table 6.1.3.1-1 Buffer size levels (in bytes) for 5-bit Buffer Size field, all values <= except marked
static const uint32_t buffer_size_levels_5bit_max_idx = 31;
static uint32_t buffer_size_levels_5bit[buffer_size_levels_5bit_max_idx + 1] = {
/* == */ 0, 10, 14, 20, 28, 38, 53, 74, 102, 142, 198,
276, 384, 535, 745, 1038, 1446, 2014, 2806, 3909, 5446, 7587,
10570, 14726, 20516, 28581, 39818, 55474, 77284, 107669, 150000, /* > */ 150000};
// TS 38.321, Table 6.1.3.1-2: Buffer size levels (in bytes) for 8-bit Buffer Size field, all values <= except marked
static const uint32_t buffer_size_levels_8bit_max_idx = 254;
static uint32_t buffer_size_levels_8bit[buffer_size_levels_8bit_max_idx + 1] = {
/* == */ 0, 10, 11, 12, 13,
14, 15, 16, 17, 18,
19, 20, 22, 23, 25,
26, 28, 30, 32, 34,
36, 38, 40, 43, 46,
49, 52, 55, 59, 62,
66, 71, 75, 80, 85,
91, 97, 103, 110, 117,
124, 132, 141, 150, 160,
170, 181, 193, 205, 218,
233, 248, 264, 281, 299,
318, 339, 361, 384, 409,
436, 464, 494, 526, 560,
597, 635, 677, 720, 767,
817, 870, 926, 987, 1051,
1119, 1191, 1269, 1351, 1439,
1532, 1631, 1737, 1850, 1970,
2098, 2234, 2379, 2533, 2698,
2873, 3059, 3258, 3469, 3694,
3934, 4189, 4461, 4751, 5059,
5387, 5737, 6109, 6506, 6928,
7378, 7857, 8367, 8910, 9488,
10104, 10760, 11458, 12202, 12994,
13838, 14736, 15692, 16711, 17795,
18951, 20181, 21491, 22885, 24371,
25953, 27638, 29431, 31342, 33376,
35543, 37850, 40307, 42923, 45709,
48676, 51836, 55200, 58784, 62599,
66663, 70990, 75598, 80505, 85730,
91295, 97221, 103532, 110252, 117409,
125030, 133146, 141789, 150992, 160793,
171231, 182345, 194182, 206786, 220209,
234503, 249725, 265935, 283197, 301579,
321155, 342002, 364202, 387842, 413018,
439827, 468377, 498780, 531156, 565634,
602350, 641449, 683087, 727427, 774645,
824928, 878475, 935498, 996222, 1060888,
1129752, 1203085, 1281179, 1364342, 1452903,
1547213, 1647644, 1754595, 1868488, 1989774,
2118933, 2256475, 2402946, 2558924, 2725027,
2901912, 3090279, 3290873, 3504487, 3731968,
3974215, 4232186, 4506902, 4799451, 5110989,
5442750, 5796046, 6172275, 6572925, 6999582,
7453933, 7937777, 8453028, 9001725, 9586039,
10208280, 10870913, 11576557, 12328006, 13128233,
13980403, 14887889, 15854280, 16883401, 17979324,
19146385, 20389201, 21712690, 23122088, 24622972,
26221280, 27923336, 29735875, 31666069, 33721553,
35910462, 38241455, 40723756, 43367187, 46182206,
49179951, 52372284, 55771835, 59392055, 63247269,
67352729, 71724679, 76380419, 81338368, /* > */ 81338368};
int32_t proc_bsr_nr::init(proc_sr_nr* sr_,
mux_interface_bsr_nr* mux_,
rlc_interface_mac* rlc_,
@ -34,7 +96,7 @@ int32_t proc_bsr_nr::init(proc_sr_nr* sr_,
// Print periodically the LCID queue status
auto queue_status_print_task = [this](uint32_t tid) {
print_state();
logger.debug("BSR: %s", buffer_state.to_string());
timer_queue_status_print.run();
};
timer_queue_status_print.set(QUEUE_STATUS_PERIOD_MS, queue_status_print_task);
@ -45,20 +107,6 @@ int32_t proc_bsr_nr::init(proc_sr_nr* sr_,
return SRSRAN_SUCCESS;
}
void proc_bsr_nr::print_state()
{
char str[128];
str[0] = '\0';
int n = 0;
for (auto& lcg : lcgs) {
for (auto& iter : lcg) {
n = srsran_print_check(str, 128, n, "%d: %d ", iter.first, iter.second.old_buffer);
}
}
logger.info(
"BSR: triggered_bsr_type=%s, LCID QUEUE status: %s", bsr_trigger_type_tostring(triggered_bsr_type), str);
}
void proc_bsr_nr::set_trigger(bsr_trigger_type_t new_trigger)
{
triggered_bsr_type = new_trigger;
@ -119,49 +167,17 @@ void proc_bsr_nr::timer_expired(uint32_t timer_id)
}
}
uint32_t proc_bsr_nr::get_buffer_state()
{
uint32_t buffer = 0;
for (int i = 0; i < MAX_NOF_LCG; i++) {
buffer += get_buffer_state_lcg(i);
}
return buffer;
}
// Checks if data is available for a channel with higher priority than others
bool proc_bsr_nr::check_highest_channel()
{
// TODO: move 4G implementation to base class or rewrite
for (int i = 0; i < MAX_NOF_LCG; i++) {
for (std::map<uint32_t, lcid_t>::iterator iter = lcgs[i].begin(); iter != lcgs[i].end(); ++iter) {
// If new data available
if (iter->second.new_buffer > iter->second.old_buffer) {
// Check if this LCID has higher priority than any other LCID ("belong to any LCG") for which data is already
// available for transmission
bool is_max_priority = true;
for (int j = 0; j < MAX_NOF_LCG; j++) {
for (std::map<uint32_t, lcid_t>::iterator iter2 = lcgs[j].begin(); iter2 != lcgs[j].end(); ++iter2) {
// No max prio LCG if prio isn't higher or LCID already had buffered data
if (iter2->second.priority <= iter->second.priority && (iter2->second.old_buffer > 0)) {
is_max_priority = false;
}
}
}
if (is_max_priority) {
logger.debug("BSR: New data for lcid=%d with maximum priority in lcg=%d", iter->first, i);
return true;
}
}
}
}
return false;
}
bool proc_bsr_nr::check_any_channel()
{
// TODO: move 4G implementation to base class or rewrite
for (int i = 0; i < MAX_NOF_LCG; i++) {
if (get_buffer_state_lcg(i)) {
for (const auto& lcg : buffer_state.lcg_buffer_size) {
if (lcg.second > 0) {
return true;
}
}
@ -169,66 +185,38 @@ bool proc_bsr_nr::check_any_channel()
}
// Checks if only one logical channel has data avaiable for Tx
bool proc_bsr_nr::check_new_data()
bool proc_bsr_nr::check_new_data(const mac_buffer_states_t& new_buffer_state)
{
// TODO: move 4G implementation to base class or rewrite
for (int i = 0; i < MAX_NOF_LCG; i++) {
// If there was no data available in any LCID belonging to this LCG
if (get_buffer_state_lcg(i) == 0) {
for (std::map<uint32_t, lcid_t>::iterator iter = lcgs[i].begin(); iter != lcgs[i].end(); ++iter) {
if (iter->second.new_buffer > 0) {
logger.debug("BSR: New data available for lcid=%d", iter->first);
return true;
}
}
for (const auto& lcg : buffer_state.lcg_buffer_size) {
if (lcg.second == 0 and new_buffer_state.lcg_buffer_size.at(lcg.first) > 0) {
logger.debug("BSR: New data available for LCG=%d", lcg.first);
return true;
}
}
return false;
}
void proc_bsr_nr::update_new_data()
{
// TODO: move 4G implementation to base class or rewrite
for (int i = 0; i < MAX_NOF_LCG; i++) {
for (std::map<uint32_t, lcid_t>::iterator iter = lcgs[i].begin(); iter != lcgs[i].end(); ++iter) {
iter->second.new_buffer = rlc->get_buffer_state(iter->first);
}
}
}
void proc_bsr_nr::update_old_buffer()
{
// TODO: move 4G implementation to base class or rewrite
for (int i = 0; i < MAX_NOF_LCG; i++) {
for (std::map<uint32_t, lcid_t>::iterator iter = lcgs[i].begin(); iter != lcgs[i].end(); ++iter) {
iter->second.old_buffer = iter->second.new_buffer;
}
}
}
uint32_t proc_bsr_nr::get_buffer_state_lcg(uint32_t lcg)
{
// TODO: move 4G implementation to base class or rewrite
uint32_t n = 0;
for (std::map<uint32_t, lcid_t>::iterator iter = lcgs[lcg].begin(); iter != lcgs[lcg].end(); ++iter) {
n += iter->second.old_buffer;
}
return n;
}
// Generate BSR
bool proc_bsr_nr::generate_bsr(bsr_t* bsr, uint32_t pdu_space)
srsran::mac_sch_subpdu_nr::lcg_bsr_t proc_bsr_nr::generate_sbsr()
{
// TODO: add BSR generation
bool send_bsr = false;
return send_bsr;
srsran::mac_sch_subpdu_nr::lcg_bsr_t sbsr = {};
sbsr.lcg_id = buffer_state.last_non_zero_lcg;
sbsr.buffer_size = buff_size_bytes_to_field(buffer_state.lcg_buffer_size.at(sbsr.lcg_id), SHORT_BSR);
triggered_bsr_type = NONE;
return sbsr;
}
// Called by MAC every TTI
// Checks if Regular BSR must be assembled, as defined in 5.4.5
// Padding BSR is assembled when called by mux_unit when UL dci is received
// Periodic BSR is triggered by the expiration of the timers
void proc_bsr_nr::step(uint32_t tti)
/**
* @brief Called by MAC every TTI with the current state of each LCID/LCGs
*
* Checks if Regular BSR must be assembled, as defined in 5.4.5.
* Padding BSR is assembled when explicitly called by MUX when UL DCI is received
* Periodic BSR is triggered by the expiration of the timers
*
* @param tti The current TTI
* @param new_buffer_state Buffer state of all LCID/LCGs at the start of the TTI
*
*/
void proc_bsr_nr::step(uint32_t tti, const mac_buffer_states_t& new_buffer_state_)
{
std::lock_guard<std::mutex> lock(mutex);
@ -236,30 +224,40 @@ void proc_bsr_nr::step(uint32_t tti)
return;
}
update_new_data();
// Regular BSR triggered if new data arrives or channel with high priority has new data
if (check_new_data() || check_highest_channel()) {
if (check_new_data(new_buffer_state_) || check_highest_channel()) {
logger.debug("BSR: Triggering Regular BSR tti=%d", tti);
set_trigger(REGULAR);
}
update_old_buffer();
// store buffer state for comparision in next TTI
buffer_state = new_buffer_state_;
}
void proc_bsr_nr::new_grant_ul(uint32_t grant_size)
{
std::lock_guard<std::mutex> lock(mutex);
if (triggered_bsr_type != NONE) {
// inform MUX we need to generate a BSR
mux->generate_bsr_mac_ce();
}
// Decide BSR type to be transmitted, state for all LCG/LCIDs has already been updated by step()
if (buffer_state.nof_lcgs_with_data > 1) {
// report Long BSR if more than one LCG has data to send
mux->generate_bsr_mac_ce(LONG_BSR);
} else {
// report Short BSR otherwise
mux->generate_bsr_mac_ce(SHORT_BSR);
}
// TODO: restart retxBSR-Timer
// 3> start or restart periodicBSR-Timer, except when all the generated BSRs are long or short Truncated BSRs
// TODO: add check if only truncated version can be included
timer_periodic.run();
// 3> start or restart retxBSR-Timer.
timer_retx.run();
}
}
// This function is called by MUX only if Regular BSR has not been triggered before
bool proc_bsr_nr::generate_padding_bsr(uint32_t nof_padding_bytes, bsr_t* bsr)
bool proc_bsr_nr::generate_padding_bsr(uint32_t nof_padding_bytes)
{
std::lock_guard<std::mutex> lock(mutex);
@ -272,7 +270,7 @@ bool proc_bsr_nr::generate_padding_bsr(uint32_t nof_padding_bytes, bsr_t* bsr)
nof_padding_bytes <= LBSR_CE_SUBHEADER_LEN + srsran::mac_sch_subpdu_nr::sizeof_ce(LONG_BSR, true)) {
// generate padding BSR
set_trigger(PADDING);
generate_bsr(bsr, nof_padding_bytes);
// generate_bsr(bsr, nof_padding_bytes);
set_trigger(NONE);
return true;
}
@ -283,40 +281,76 @@ bool proc_bsr_nr::generate_padding_bsr(uint32_t nof_padding_bytes, bsr_t* bsr)
int proc_bsr_nr::setup_lcid(uint32_t lcid, uint32_t new_lcg, uint32_t priority)
{
// TODO: move 4G implementation to base class
if (new_lcg > MAX_NOF_LCG) {
if (new_lcg > srsran::mac_sch_subpdu_nr::max_num_lcg_lbsr) {
logger.error("BSR: Invalid lcg=%d for lcid=%d", new_lcg, lcid);
return SRSRAN_ERROR;
}
std::lock_guard<std::mutex> lock(mutex);
// First see if it already exists and eliminate it
for (int i = 0; i < MAX_NOF_LCG; i++) {
if (lcgs[i].count(lcid)) {
lcgs[i].erase(lcid);
}
// Check that the new priority doesn't not already exist
if (lcg_priorities.find(priority) != lcg_priorities.end()) {
logger.error(
"BSR: Invalid config. Priority=%d already configured for lcg=%d", priority, lcg_priorities.at(priority));
return SRSRAN_ERROR;
}
// Now add it
lcgs[new_lcg][lcid].priority = priority;
lcgs[new_lcg][lcid].old_buffer = 0;
lcg_priorities[priority] = new_lcg;
return SRSRAN_SUCCESS;
}
uint32_t proc_bsr_nr::find_max_priority_lcg_with_data()
{
// TODO: move 4G implementation to base class or rewrite
int32_t max_prio = 99;
uint32_t max_idx = 0;
for (int i = 0; i < MAX_NOF_LCG; i++) {
for (std::map<uint32_t, lcid_t>::iterator iter = lcgs[i].begin(); iter != lcgs[i].end(); ++iter) {
if (iter->second.priority < max_prio && iter->second.old_buffer > 0) {
max_prio = iter->second.priority;
max_idx = i;
}
// iterate over LCGs in order of their priorities and check if there is one with data to transmit
for (const auto& lcg_prio : lcg_priorities) {
if (buffer_state.lcg_buffer_size.at(lcg_prio.second) > 0) {
return lcg_prio.second;
}
}
return max_idx;
return 0;
}
/** Converts the buffer size levels (in Bytes) to the 5 or 8-bit Buffer Size field
* @param buffer_size The actual buffer size level in Bytes
* @param format The BSR format that determines the buffer size field length
* @return uint8_t The buffer size field that will be used for the MAC PDU
*/
uint8_t proc_bsr_nr::buff_size_bytes_to_field(uint32_t buffer_size, bsr_format_nr_t format)
{
if (buffer_size == 0) {
}
switch (format) {
case SHORT_BSR:
case SHORT_TRUNC_BSR:
if (buffer_size > buffer_size_levels_5bit[buffer_size_levels_5bit_max_idx]) {
return buffer_size_levels_5bit_max_idx;
} else {
for (uint32_t i = 1; i < buffer_size_levels_5bit_max_idx; i++) {
if (buffer_size <= buffer_size_levels_5bit[i]) {
return i;
}
}
return buffer_size_levels_5bit_max_idx - 1;
}
break;
case LONG_BSR:
case LONG_TRUNC_BSR:
if (buffer_size > buffer_size_levels_8bit[buffer_size_levels_8bit_max_idx]) {
return buffer_size_levels_8bit_max_idx;
} else {
for (uint32_t i = 1; i < buffer_size_levels_8bit_max_idx; i++) {
if (buffer_size <= buffer_size_levels_8bit[i + 1]) {
return i + 1;
}
}
return buffer_size_levels_8bit_max_idx - 1;
}
break;
}
return 0;
}
} // namespace srsue

@ -10,6 +10,10 @@ add_executable(proc_ra_nr_test proc_ra_nr_test.cc)
target_link_libraries(proc_ra_nr_test srsue_mac_nr srsran_common)
add_test(proc_ra_nr_test proc_ra_nr_test)
add_executable(proc_bsr_nr_test proc_bsr_nr_test.cc)
target_link_libraries(proc_bsr_nr_test srsue_mac_nr srsran_common)
add_test(proc_bsr_nr_test proc_bsr_nr_test)
add_executable(mac_nr_test mac_nr_test.cc)
target_link_libraries(mac_nr_test srsue_mac_nr srsran_common)
add_test(mac_nr_test mac_nr_test)

@ -188,11 +188,6 @@ int mac_nr_ul_logical_channel_prioritization_test1()
mac.setup_lcid(channel);
}
srsran::bsr_cfg_nr_t bsr_cfg = {};
bsr_cfg.periodic_timer = 20;
bsr_cfg.retx_timer = 320;
TESTASSERT(mac.set_config(bsr_cfg) == SRSRAN_SUCCESS);
// write dummy data to DRB2
rlc.write_sdu(4, 10);
@ -233,6 +228,159 @@ int mac_nr_ul_logical_channel_prioritization_test1()
return SRSRAN_SUCCESS;
}
// Basic test for periodic BSR transmission
int mac_nr_ul_periodic_bsr_test()
{
// PDU layout (10 B in total)
// - SBSR
// - 6B LCID=4
const uint8_t tv1[] = {0x3d, 0xd1, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04};
// PDU layout (10 B in total)
// - 8B LCID=4
const uint8_t tv2[] = {0x04, 0x08, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04};
// dummy layers
dummy_phy phy;
rlc_dummy rlc;
rrc_dummy rrc;
stack_dummy stack;
// the actual MAC
mac_nr mac(&stack.task_sched);
mac_nr_args_t args = {};
mac.init(args, &phy, &rlc, &rrc);
stack.init(&mac, &phy);
const uint16_t crnti = 0x1001;
// generate config (default DRB2 config for EN-DC)
std::vector<srsran::logical_channel_config_t> lcids;
srsran::logical_channel_config_t config = {};
config.lcid = 4;
config.lcg = 6;
config.PBR = 0;
config.BSD = 1000; // 1000ms
config.priority = 11;
lcids.push_back(config);
// setup LCIDs in MAC
for (auto& channel : lcids) {
mac.setup_lcid(channel);
}
srsran::bsr_cfg_nr_t bsr_cfg = {};
bsr_cfg.periodic_timer = 20;
bsr_cfg.retx_timer = 320;
TESTASSERT(mac.set_config(bsr_cfg) == SRSRAN_SUCCESS);
// run TTI to establish LCGs old buffer states at BSR
uint32_t tti = 0;
stack.run_tti(tti++);
// write large amount of dummy data to DRB2
rlc.write_sdu(4, 2000);
// run TTI to setup Bj, BSR should be generated
stack.run_tti(tti++);
usleep(100);
// create UL action and grant and read MAC PDU
{
mac_interface_phy_nr::tb_action_ul_t ul_action = {};
mac_interface_phy_nr::mac_nr_grant_ul_t mac_grant = {};
mac_grant.rnti = crnti; // make sure MAC picks it up as valid UL grant
mac_grant.pid = 0;
mac_grant.rnti = 0x1001;
mac_grant.tti = 0;
mac_grant.tbs = 10;
int cc_idx = 0;
// Send grant to MAC and get action for this TB
mac.new_grant_ul(cc_idx, mac_grant, &ul_action);
// print generated PDU
srslog::fetch_basic_logger("MAC").info(
ul_action.tb.payload->msg, mac_grant.tbs, "Generated PDU (%d B)", mac_grant.tbs);
#if HAVE_PCAP
pcap_handle->write_ul_crnti_nr(
ul_action.tb.payload->msg, mac_grant.tbs, mac_grant.rnti, UE_ID, mac_grant.pid, mac_grant.tti);
#endif
TESTASSERT(memcmp(ul_action.tb.payload->msg, tv1, sizeof(tv1)) == 0);
}
// for the next 19 TTI, until the periodic BSR is triggered again, no BSR should be included in the MAC PDU
for (int i = 0; i < bsr_cfg.periodic_timer - 1; ++i) {
stack.run_tti(tti++);
usleep(100);
// create UL action and grant and read MAC PDU
{
mac_interface_phy_nr::tb_action_ul_t ul_action = {};
mac_interface_phy_nr::mac_nr_grant_ul_t mac_grant = {};
mac_grant.rnti = crnti; // make sure MAC picks it up as valid UL grant
mac_grant.pid = 0;
mac_grant.rnti = 0x1001;
mac_grant.tti = 0;
mac_grant.tbs = 10;
int cc_idx = 0;
// Send grant to MAC and get action for this TB
mac.new_grant_ul(cc_idx, mac_grant, &ul_action);
// print generated PDU
srslog::fetch_basic_logger("MAC").info(
ul_action.tb.payload->msg, mac_grant.tbs, "Generated PDU (%d B)", mac_grant.tbs);
#if HAVE_PCAP
pcap_handle->write_ul_crnti_nr(
ul_action.tb.payload->msg, mac_grant.tbs, mac_grant.rnti, UE_ID, mac_grant.pid, mac_grant.tti);
#endif
TESTASSERT(memcmp(ul_action.tb.payload->msg, tv2, sizeof(tv2)) == 0);
}
}
// run TTI to setup Bj, the same BSR should be generated again
stack.run_tti(tti++);
usleep(100);
// create UL action and grant and read MAC PDU
{
mac_interface_phy_nr::tb_action_ul_t ul_action = {};
mac_interface_phy_nr::mac_nr_grant_ul_t mac_grant = {};
mac_grant.rnti = crnti; // make sure MAC picks it up as valid UL grant
mac_grant.pid = 0;
mac_grant.rnti = 0x1001;
mac_grant.tti = 0;
mac_grant.tbs = 10;
int cc_idx = 0;
// Send grant to MAC and get action for this TB
mac.new_grant_ul(cc_idx, mac_grant, &ul_action);
// print generated PDU
srslog::fetch_basic_logger("MAC").info(
ul_action.tb.payload->msg, mac_grant.tbs, "Generated PDU (%d B)", mac_grant.tbs);
#if HAVE_PCAP
pcap_handle->write_ul_crnti_nr(
ul_action.tb.payload->msg, mac_grant.tbs, mac_grant.rnti, UE_ID, mac_grant.pid, mac_grant.tti);
#endif
TESTASSERT(memcmp(ul_action.tb.payload->msg, tv1, sizeof(tv1)) == 0);
}
// make sure MAC PDU thread picks up before stopping
stack.run_tti(tti++);
mac.stop();
return SRSRAN_SUCCESS;
}
int main()
{
#if HAVE_PCAP
@ -247,6 +395,7 @@ int main()
TESTASSERT(msg3_test() == SRSRAN_SUCCESS);
TESTASSERT(mac_nr_ul_logical_channel_prioritization_test1() == SRSRAN_SUCCESS);
TESTASSERT(mac_nr_ul_periodic_bsr_test() == SRSRAN_SUCCESS);
return SRSRAN_SUCCESS;
}

@ -0,0 +1,93 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2021 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#include "srsran/common/buffer_pool.h"
#include "srsran/common/common.h"
#include "srsran/common/test_common.h"
#include "srsue/hdr/stack/mac_nr/proc_bsr_nr.h"
using namespace srsue;
int sbsr_tests()
{
auto& mac_logger = srslog::fetch_basic_logger("MAC");
mac_logger.set_level(srslog::basic_levels::debug);
mac_logger.set_hex_dump_max_size(-1);
srsran::task_scheduler task_sched{5, 2};
srsran::ext_task_sched_handle ext_task_sched_h(&task_sched);
proc_bsr_nr proc(mac_logger);
proc.init(nullptr, nullptr, nullptr, &ext_task_sched_h);
srsran::bsr_cfg_nr_t bsr_cfg = {};
bsr_cfg.periodic_timer = 20;
bsr_cfg.retx_timer = 320;
TESTASSERT(proc.set_config(bsr_cfg) == SRSRAN_SUCCESS);
uint32_t tti = 0;
mac_buffer_states_t buffer_state;
buffer_state.last_non_zero_lcg = 1;
buffer_state.lcg_buffer_size[1] = 10;
proc.step(tti++, buffer_state);
// Buffer size == 10 should result in index 1 (<= 10)
srsran::mac_sch_subpdu_nr::lcg_bsr_t sbsr = proc.generate_sbsr();
TESTASSERT(sbsr.lcg_id == 1);
TESTASSERT(sbsr.buffer_size == 1);
buffer_state.last_non_zero_lcg = 1;
buffer_state.lcg_buffer_size[1] = 11;
proc.step(tti++, buffer_state);
// Buffer size == 11 should result in index 1
sbsr = proc.generate_sbsr();
TESTASSERT(sbsr.lcg_id == 1);
TESTASSERT(sbsr.buffer_size == 2);
buffer_state.last_non_zero_lcg = 1;
buffer_state.lcg_buffer_size[1] = 77285; // 77284 + 1
proc.step(tti++, buffer_state);
// Buffer size 77285 should result in index 29 (first value of that index)
sbsr = proc.generate_sbsr();
TESTASSERT(sbsr.lcg_id == 1);
TESTASSERT(sbsr.buffer_size == 29);
buffer_state.last_non_zero_lcg = 1;
buffer_state.lcg_buffer_size[1] = 150000;
proc.step(tti++, buffer_state);
// Buffer size 150000 should result in index 30
sbsr = proc.generate_sbsr();
TESTASSERT(sbsr.lcg_id == 1);
TESTASSERT(sbsr.buffer_size == 30);
buffer_state.last_non_zero_lcg = 1;
buffer_state.lcg_buffer_size[1] = 150001;
proc.step(tti++, buffer_state);
// Buffer size 150001 should result in index 31
sbsr = proc.generate_sbsr();
TESTASSERT(sbsr.lcg_id == 1);
TESTASSERT(sbsr.buffer_size == 31);
return SRSRAN_SUCCESS;
}
int main()
{
srslog::init();
TESTASSERT(sbsr_tests() == SRSRAN_SUCCESS);
return SRSRAN_SUCCESS;
}

@ -8,7 +8,6 @@
add_subdirectory(phy)
add_subdirectory(upper)
add_subdirectory(mac_nr)
if (ENABLE_TTCN3)
add_subdirectory(ttcn3)

Loading…
Cancel
Save