mirror of https://github.com/pvnis/srsRAN_4G.git
proc_bsr: initial BSR procedure for NR
* refactor some common methods to mac_common.cc * add common mux_base class * move UL PDU generation to mux class * add logical channel registration to MAC, MUX, BSR * add initial proc BSR * add basic MAC NR test * rework MAC interfacesmaster
parent
d04a19f8bc
commit
d135ae4d29
@ -0,0 +1,30 @@
|
||||
/**
|
||||
*
|
||||
* \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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SRSUE_MAC_COMMON_H
|
||||
#define SRSUE_MAC_COMMON_H
|
||||
|
||||
/**
|
||||
* @brief Common definitions/interfaces between LTE/NR MAC components
|
||||
*
|
||||
* @remark: So far only the trigger types are identical. The BSR report type and LCID mapping is implemented in RAT
|
||||
* specialications.
|
||||
*/
|
||||
namespace srsue {
|
||||
|
||||
// BSR trigger are common between LTE and NR
|
||||
typedef enum { NONE, REGULAR, PADDING, PERIODIC } bsr_trigger_type_t;
|
||||
char* bsr_trigger_type_tostring(bsr_trigger_type_t type);
|
||||
|
||||
} // namespace srsue
|
||||
|
||||
#endif // SRSUE_MAC_COMMON_H
|
@ -0,0 +1,102 @@
|
||||
/**
|
||||
*
|
||||
* \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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SRSUE_MUX_BASE_H
|
||||
#define SRSUE_MUX_BASE_H
|
||||
|
||||
#include "srsran/interfaces/mac_interface_types.h"
|
||||
|
||||
namespace srsue {
|
||||
|
||||
/**
|
||||
* @brief Common base class for UE MUX unit for 4G and 5G RAT
|
||||
*
|
||||
*/
|
||||
class mux_base
|
||||
{
|
||||
public:
|
||||
int setup_lcid(const srsran::logical_channel_config_t& config)
|
||||
{
|
||||
if (has_logical_channel(config.lcid)) {
|
||||
// update settings
|
||||
for (auto& channel : logical_channels) {
|
||||
if (channel.lcid == config.lcid) {
|
||||
channel = config;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// warn user if there is another LCID with same prio
|
||||
for (auto& channel : logical_channels) {
|
||||
if (channel.priority == config.priority && channel.lcid != config.lcid) {
|
||||
srslog::fetch_basic_logger("MAC").error("LCID %d and %d have same priority.", channel.lcid, config.lcid);
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// add new entry
|
||||
logical_channels.push_back(config);
|
||||
}
|
||||
|
||||
// sort according to priority (increasing is lower priority)
|
||||
std::sort(logical_channels.begin(), logical_channels.end(), priority_compare);
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
void print_logical_channel_state(const std::string& info)
|
||||
{
|
||||
std::string logline = info;
|
||||
|
||||
for (auto& channel : logical_channels) {
|
||||
logline += "\n";
|
||||
logline += "- lcid=";
|
||||
logline += std::to_string(channel.lcid);
|
||||
logline += ", lcg=";
|
||||
logline += std::to_string(channel.lcg);
|
||||
logline += ", prio=";
|
||||
logline += std::to_string(channel.priority);
|
||||
logline += ", Bj=";
|
||||
logline += std::to_string(channel.Bj);
|
||||
logline += ", PBR=";
|
||||
logline += std::to_string(channel.PBR);
|
||||
logline += ", BSD=";
|
||||
logline += std::to_string(channel.BSD);
|
||||
logline += ", buffer_len=";
|
||||
logline += std::to_string(channel.buffer_len);
|
||||
logline += ", sched_len=";
|
||||
logline += std::to_string(channel.sched_len);
|
||||
}
|
||||
srslog::fetch_basic_logger("MAC").debug("%s", logline.c_str());
|
||||
}
|
||||
|
||||
protected:
|
||||
static bool priority_compare(const srsran::logical_channel_config_t& u1, const srsran::logical_channel_config_t& u2)
|
||||
{
|
||||
return u1.priority <= u2.priority;
|
||||
}
|
||||
|
||||
bool has_logical_channel(const uint32_t& lcid)
|
||||
{
|
||||
for (auto& channel : logical_channels) {
|
||||
if (channel.lcid == lcid) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<srsran::logical_channel_config_t> logical_channels;
|
||||
};
|
||||
|
||||
} // namespace srsue
|
||||
|
||||
#endif // SRSUE_MUX_BASE_H
|
@ -0,0 +1,128 @@
|
||||
/**
|
||||
*
|
||||
* \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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SRSUE_PROC_BSR_NR_H
|
||||
#define SRSUE_PROC_BSR_NR_H
|
||||
|
||||
#include <map>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "proc_sr_nr.h"
|
||||
#include "srsran/common/task_scheduler.h"
|
||||
#include "srsran/srslog/srslog.h"
|
||||
#include "srsue/hdr/stack/mac_common/mac_common.h"
|
||||
|
||||
/* Buffer status report procedure */
|
||||
|
||||
namespace srsue {
|
||||
|
||||
class rlc_interface_mac;
|
||||
|
||||
// BSR interface for MUX
|
||||
class bsr_interface_mux_nr
|
||||
{
|
||||
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;
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief BSR procedure for NR according to 3GPP TS 38.321 version 15.3.0
|
||||
*
|
||||
* @remark: So far only class scelleton.
|
||||
*/
|
||||
class proc_bsr_nr : public srsran::timer_callback, public bsr_interface_mux_nr
|
||||
{
|
||||
public:
|
||||
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 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);
|
||||
|
||||
private:
|
||||
const static int QUEUE_STATUS_PERIOD_MS = 1000;
|
||||
|
||||
std::mutex mutex;
|
||||
|
||||
srsran::ext_task_sched_handle* task_sched = nullptr;
|
||||
srslog::basic_logger& logger;
|
||||
rlc_interface_mac* rlc = nullptr;
|
||||
mux_interface_bsr_nr* mux = nullptr;
|
||||
proc_sr_nr* sr = nullptr;
|
||||
|
||||
srsran::bsr_cfg_nr_t bsr_cfg = {};
|
||||
|
||||
bool initiated = false;
|
||||
|
||||
const static int MAX_NOF_LCG = 8;
|
||||
|
||||
typedef struct {
|
||||
int priority;
|
||||
uint32_t old_buffer;
|
||||
uint32_t new_buffer;
|
||||
} lcid_t;
|
||||
|
||||
std::map<uint32_t, lcid_t> lcgs[MAX_NOF_LCG]; // groups LCID in LCG
|
||||
|
||||
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_any_channel();
|
||||
uint32_t get_buffer_state_lcg(uint32_t lcg);
|
||||
bool generate_bsr(bsr_t* bsr, uint32_t nof_padding_bytes);
|
||||
|
||||
uint32_t find_max_priority_lcg_with_data();
|
||||
|
||||
srsran::timer_handler::unique_timer timer_periodic;
|
||||
srsran::timer_handler::unique_timer timer_retx;
|
||||
srsran::timer_handler::unique_timer timer_queue_status_print;
|
||||
};
|
||||
|
||||
} // namespace srsue
|
||||
|
||||
#endif // SRSUE_PROC_BSR_NR_H
|
@ -0,0 +1,10 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
set(SOURCES mac_common.cc)
|
||||
add_library(srsue_mac_common STATIC ${SOURCES})
|
@ -0,0 +1,32 @@
|
||||
/**
|
||||
*
|
||||
* \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 "srsue/hdr/stack/mac_common/mac_common.h"
|
||||
|
||||
namespace srsue {
|
||||
|
||||
char* bsr_trigger_type_tostring(bsr_trigger_type_t type)
|
||||
{
|
||||
switch (type) {
|
||||
case bsr_trigger_type_t::NONE:
|
||||
return (char*)"none";
|
||||
case bsr_trigger_type_t::REGULAR:
|
||||
return (char*)"Regular";
|
||||
case bsr_trigger_type_t::PADDING:
|
||||
return (char*)"Padding";
|
||||
case bsr_trigger_type_t::PERIODIC:
|
||||
return (char*)"Periodic";
|
||||
}
|
||||
return (char*)"unknown";
|
||||
}
|
||||
|
||||
} // namespace srsue
|
@ -0,0 +1,322 @@
|
||||
/**
|
||||
*
|
||||
* \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 "srsue/hdr/stack/mac_nr/proc_bsr_nr.h"
|
||||
#include "srsran/interfaces/ue_rlc_interfaces.h"
|
||||
#include "srsran/mac/mac_sch_pdu_nr.h"
|
||||
|
||||
namespace srsue {
|
||||
|
||||
int32_t proc_bsr_nr::init(proc_sr_nr* sr_,
|
||||
mux_interface_bsr_nr* mux_,
|
||||
rlc_interface_mac* rlc_,
|
||||
srsran::ext_task_sched_handle* task_sched_)
|
||||
{
|
||||
rlc = rlc_;
|
||||
mux = mux_;
|
||||
sr = sr_;
|
||||
task_sched = task_sched_;
|
||||
|
||||
timer_periodic = task_sched->get_unique_timer();
|
||||
timer_retx = task_sched->get_unique_timer();
|
||||
timer_queue_status_print = task_sched->get_unique_timer();
|
||||
|
||||
reset();
|
||||
|
||||
// Print periodically the LCID queue status
|
||||
auto queue_status_print_task = [this](uint32_t tid) {
|
||||
print_state();
|
||||
timer_queue_status_print.run();
|
||||
};
|
||||
timer_queue_status_print.set(QUEUE_STATUS_PERIOD_MS, queue_status_print_task);
|
||||
timer_queue_status_print.run();
|
||||
|
||||
initiated = true;
|
||||
|
||||
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;
|
||||
|
||||
// Trigger SR always when Regular BSR is triggered in the current TTI. Will be cancelled if a grant is received
|
||||
if (triggered_bsr_type == REGULAR) {
|
||||
logger.debug("BSR: Triggering SR procedure");
|
||||
sr->start();
|
||||
}
|
||||
}
|
||||
|
||||
void proc_bsr_nr::reset()
|
||||
{
|
||||
timer_periodic.stop();
|
||||
timer_retx.stop();
|
||||
|
||||
triggered_bsr_type = NONE;
|
||||
}
|
||||
|
||||
int proc_bsr_nr::set_config(const srsran::bsr_cfg_nr_t& bsr_cfg_)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
|
||||
bsr_cfg = bsr_cfg_;
|
||||
|
||||
if (bsr_cfg_.periodic_timer > 0) {
|
||||
timer_periodic.set(bsr_cfg_.periodic_timer, [this](uint32_t tid) { timer_expired(tid); });
|
||||
logger.info("BSR: Configured timer periodic %d ms", bsr_cfg_.periodic_timer);
|
||||
}
|
||||
if (bsr_cfg_.retx_timer > 0) {
|
||||
timer_retx.set(bsr_cfg_.retx_timer, [this](uint32_t tid) { timer_expired(tid); });
|
||||
logger.info("BSR: Configured timer reTX %d ms", bsr_cfg_.retx_timer);
|
||||
}
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
/* Process Periodic BSR */
|
||||
void proc_bsr_nr::timer_expired(uint32_t timer_id)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
|
||||
// periodicBSR-Timer
|
||||
if (timer_id == timer_periodic.id()) {
|
||||
if (triggered_bsr_type == NONE) {
|
||||
set_trigger(PERIODIC);
|
||||
logger.debug("BSR: Triggering Periodic BSR");
|
||||
}
|
||||
// retxBSR-Timer
|
||||
} else if (timer_id == timer_retx.id()) {
|
||||
// Enable reTx of SR only if periodic timer is not infinity
|
||||
logger.debug("BSR: Timer BSR reTX expired, periodic=%d, channel=%d", bsr_cfg.periodic_timer, check_any_channel());
|
||||
// Triger Regular BSR if UE has available data for transmission on any channel
|
||||
if (check_any_channel()) {
|
||||
set_trigger(REGULAR);
|
||||
logger.debug("BSR: Triggering BSR reTX");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Checks if only one logical channel has data avaiable for Tx
|
||||
bool proc_bsr_nr::check_new_data()
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
// TODO: add BSR generation
|
||||
bool send_bsr = false;
|
||||
return send_bsr;
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
|
||||
if (not initiated) {
|
||||
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()) {
|
||||
logger.debug("BSR: Triggering Regular BSR tti=%d", tti);
|
||||
set_trigger(REGULAR);
|
||||
}
|
||||
|
||||
update_old_buffer();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
// TODO: restart retxBSR-Timer
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
|
||||
// TODO: get correct values from mac_sch_pdu_nr
|
||||
const uint32_t SBSR_CE_SUBHEADER_LEN = 1;
|
||||
const uint32_t LBSR_CE_SUBHEADER_LEN = 1;
|
||||
// if the number of padding bits is equal to or larger than the size of the Short BSR plus its subheader but smaller
|
||||
// than the size of the Long BSR plus its subheader
|
||||
if (nof_padding_bytes >= SBSR_CE_SUBHEADER_LEN + srsran::mac_sch_subpdu_nr::sizeof_ce(SHORT_BSR, true) &&
|
||||
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);
|
||||
set_trigger(NONE);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
// Now add it
|
||||
lcgs[new_lcg][lcid].priority = priority;
|
||||
lcgs[new_lcg][lcid].old_buffer = 0;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
return max_idx;
|
||||
}
|
||||
|
||||
} // namespace srsue
|
@ -0,0 +1,251 @@
|
||||
/**
|
||||
*
|
||||
* \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 "srsran/test/ue_test_interfaces.h"
|
||||
#include "srsue/hdr/stack/mac_nr/mac_nr.h"
|
||||
|
||||
using namespace srsue;
|
||||
|
||||
#define HAVE_PCAP 0
|
||||
#define UE_ID 0
|
||||
|
||||
static std::unique_ptr<srsran::mac_pcap> pcap_handle = nullptr;
|
||||
|
||||
class dummy_phy : public phy_interface_mac_nr
|
||||
{
|
||||
public:
|
||||
dummy_phy() {}
|
||||
void send_prach(const uint32_t prach_occasion_,
|
||||
const int preamble_index_,
|
||||
const float preamble_received_target_power_,
|
||||
const float ta_base_sec_ = 0.0f) override
|
||||
{
|
||||
prach_occasion = prach_occasion_;
|
||||
preamble_index = preamble_index_;
|
||||
preamble_received_target_power = preamble_received_target_power_;
|
||||
}
|
||||
int tx_request(const tx_request_t& request) override { return 0; }
|
||||
int set_ul_grant(std::array<uint8_t, SRSRAN_RAR_UL_GRANT_NBITS>, uint16_t rnti, srsran_rnti_type_t rnti_type) override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void get_last_send_prach(uint32_t* prach_occasion_, uint32_t* preamble_index_, int* preamble_received_target_power_)
|
||||
{
|
||||
*prach_occasion_ = prach_occasion;
|
||||
*preamble_index_ = preamble_index;
|
||||
*preamble_received_target_power_ = preamble_received_target_power;
|
||||
}
|
||||
void sr_send(uint32_t sr_id) override {}
|
||||
|
||||
private:
|
||||
uint32_t prach_occasion = 0;
|
||||
uint32_t preamble_index = 0;
|
||||
int preamble_received_target_power = 0;
|
||||
};
|
||||
|
||||
class rrc_dummy : public rrc_interface_mac
|
||||
{
|
||||
public:
|
||||
rrc_dummy() {}
|
||||
virtual void ra_completed() {}
|
||||
virtual void ra_problem() {}
|
||||
virtual void release_pucch_srs() {}
|
||||
};
|
||||
|
||||
class stack_dummy : public stack_test_dummy
|
||||
{
|
||||
public:
|
||||
void init(mac_nr* mac_, phy_interface_mac_nr* phy_)
|
||||
{
|
||||
mac_h = mac_;
|
||||
phy_h = phy_;
|
||||
}
|
||||
void run_tti(uint32_t tti)
|
||||
{
|
||||
mac_h->run_tti(tti);
|
||||
// flush all events
|
||||
stack_test_dummy::run_tti();
|
||||
}
|
||||
|
||||
private:
|
||||
phy_interface_mac_nr* phy_h = nullptr;
|
||||
mac_nr* mac_h = nullptr;
|
||||
};
|
||||
|
||||
// TODO: refactor to common test dummy components
|
||||
class rlc_dummy : public srsue::rlc_dummy_interface
|
||||
{
|
||||
public:
|
||||
rlc_dummy() : received_bytes(0) {}
|
||||
bool has_data_locked(const uint32_t lcid) final { return ul_queues[lcid] > 0; }
|
||||
uint32_t get_buffer_state(const uint32_t lcid) final { return ul_queues[lcid]; }
|
||||
int read_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) final
|
||||
{
|
||||
if (!read_enable || nof_bytes < read_min) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (read_len > 0 && read_len < (int32_t)nof_bytes) {
|
||||
nof_bytes = read_len;
|
||||
}
|
||||
|
||||
uint32_t len = SRSRAN_MIN(ul_queues[lcid], nof_bytes);
|
||||
|
||||
// set payload bytes to LCID so we can check later if the scheduling was correct
|
||||
memset(payload, lcid > 0 ? lcid : 0xf, len);
|
||||
|
||||
// remove from UL queue
|
||||
ul_queues[lcid] -= len;
|
||||
|
||||
return len;
|
||||
};
|
||||
void write_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) final
|
||||
{
|
||||
logger.debug(payload, nof_bytes, "Received %d B on LCID %d", nof_bytes, lcid);
|
||||
received_bytes += nof_bytes;
|
||||
}
|
||||
|
||||
void write_sdu(uint32_t lcid, uint32_t nof_bytes) { ul_queues[lcid] += nof_bytes; }
|
||||
uint32_t get_received_bytes() { return received_bytes; }
|
||||
|
||||
void disable_read() { read_enable = false; }
|
||||
void set_read_len(uint32_t len) { read_len = len; }
|
||||
void set_read_min(uint32_t len) { read_min = len; }
|
||||
void reset_queues()
|
||||
{
|
||||
for (auto& q : ul_queues) {
|
||||
q.second = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool read_enable = true;
|
||||
int32_t read_len = -1; // read all
|
||||
uint32_t read_min = 0; // minimum "grant size" for read_pdu() to return data
|
||||
uint32_t received_bytes;
|
||||
srslog::basic_logger& logger = srslog::fetch_basic_logger("RLC");
|
||||
// UL queues where key is LCID and value the queue length
|
||||
std::map<uint32_t, uint32_t> ul_queues;
|
||||
};
|
||||
|
||||
// TODO: Add test
|
||||
int msg3_test()
|
||||
{
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
// Basic PDU generation test
|
||||
int mac_nr_ul_logical_channel_prioritization_test1()
|
||||
{
|
||||
// PDU layout (20B in total)
|
||||
// - 2 B MAC subheader for SCH LCID=4
|
||||
// - 10 B sduPDU
|
||||
// - 1 B subheader padding
|
||||
// - 7 B padding
|
||||
const uint8_t tv[] = {0x04, 0x0a, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
||||
0x04, 0x04, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
// 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);
|
||||
|
||||
// write dummy data to DRB2
|
||||
rlc.write_sdu(4, 10);
|
||||
|
||||
// run TTI to setup Bj, BSR should be generated
|
||||
stack.run_tti(0);
|
||||
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 = 20;
|
||||
int cc_idx = 0;
|
||||
|
||||
// Send grant to MAC and get action for this TB, 0x
|
||||
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, tv, sizeof(tv)) == 0);
|
||||
}
|
||||
|
||||
// make sure MAC PDU thread picks up before stopping
|
||||
stack.run_tti(0);
|
||||
mac.stop();
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
#if HAVE_PCAP
|
||||
pcap_handle = std::unique_ptr<srsran::mac_pcap>(new srsran::mac_pcap());
|
||||
pcap_handle->open("mac_test_nr.pcap");
|
||||
#endif
|
||||
|
||||
auto& mac_logger = srslog::fetch_basic_logger("MAC");
|
||||
mac_logger.set_level(srslog::basic_levels::debug);
|
||||
mac_logger.set_hex_dump_max_size(-1);
|
||||
srslog::init();
|
||||
|
||||
TESTASSERT(msg3_test() == SRSRAN_SUCCESS);
|
||||
TESTASSERT(mac_nr_ul_logical_channel_prioritization_test1() == SRSRAN_SUCCESS);
|
||||
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
Loading…
Reference in New Issue