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 interfaces
master
Andre Puschmann 4 years ago committed by David Rupprecht
parent d04a19f8bc
commit d135ae4d29

@ -150,6 +150,17 @@ struct sr_cfg_nr_t {
sr_cfg_item_nr_t item[SRSRAN_MAX_MAX_NR_OF_SR_CFG_PER_CELL_GROUP];
};
struct bsr_cfg_nr_t {
// mandatory BSR config
int periodic_timer;
int retx_timer;
// SR specific configs for logical channel
bool sr_delay_timer_enabled;
int sr_delay_timer;
bool sr_mask; // Indicates whether SR masking is configured for this logical channel
};
struct mac_cfg_t {
// Default constructor with default values as in 36.331 9.2.2
mac_cfg_t() { set_defaults(); }

@ -99,10 +99,10 @@ class mac_interface_rrc_nr
{
public:
// Config calls that return SRSRAN_SUCCESS or SRSRAN_ERROR
virtual void setup_lcid(const srsran::logical_channel_config_t& config) = 0;
virtual void set_config(const srsran::bsr_cfg_t& bsr_cfg) = 0;
virtual int32_t set_config(const srsran::sr_cfg_nr_t& sr_cfg) = 0;
virtual void set_config(const srsran::rach_nr_cfg_t& rach_cfg) = 0;
virtual int setup_lcid(const srsran::logical_channel_config_t& config) = 0;
virtual int set_config(const srsran::bsr_cfg_nr_t& bsr_cfg) = 0;
virtual int set_config(const srsran::sr_cfg_nr_t& sr_cfg) = 0;
virtual void set_config(const srsran::rach_nr_cfg_t& rach_cfg) = 0;
// RRC triggers MAC ra procedure
virtual void start_ra_procedure() = 0;

@ -21,16 +21,11 @@
namespace srsue {
class rrc_interface_mac_common
{
public:
virtual void ra_problem() = 0;
};
class rrc_interface_mac : public rrc_interface_mac_common
class rrc_interface_mac
{
public:
virtual void ra_completed() = 0;
virtual void ra_problem() = 0;
virtual void release_pucch_srs() = 0;
};

@ -88,9 +88,10 @@ public:
uint32_t write_subpdu(const uint8_t* start_);
private:
uint32_t sizeof_ce(uint32_t lcid, bool is_ul);
// Used by BSR procedure to determine size of BSR types
static uint32_t sizeof_ce(uint32_t lcid, bool is_ul);
private:
// protected:
uint32_t lcid = 0;
int header_length = 0;

@ -23,11 +23,12 @@
#include "srsran/interfaces/mac_interface_types.h"
#include "srsran/mac/pdu.h"
#include "srsran/srslog/srslog.h"
#include "srsue/hdr/stack/mac_common/mux_base.h"
#include <mutex>
namespace srsue {
class mux
class mux : private mux_base
{
public:
explicit mux(srslog::basic_logger& logger);
@ -56,15 +57,12 @@ public:
void print_logical_channel_state(const std::string& info);
private:
bool has_logical_channel(const uint32_t& lcid);
bool pdu_move_to_msg3(uint32_t pdu_sz);
uint32_t allocate_sdu(uint32_t lcid, srsran::sch_pdu* pdu, int max_sdu_sz);
bool sched_sdu(srsran::logical_channel_config_t* ch, int* sdu_space, int max_sdu_sz);
const static int MAX_NOF_SUBHEADERS = 20;
std::vector<srsran::logical_channel_config_t> logical_channels;
// Mutex for exclusive access
std::mutex mutex;

@ -19,6 +19,7 @@
#include "proc_sr.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 */
@ -88,11 +89,11 @@ private:
std::map<uint32_t, lcid_t> lcgs[NOF_LCG]; // groups LCID in LCG
uint32_t find_max_priority_lcg_with_data();
typedef enum { NONE, REGULAR, PADDING, PERIODIC } triggered_bsr_type_t;
triggered_bsr_type_t triggered_bsr_type = NONE;
bsr_trigger_type_t triggered_bsr_type = NONE;
void print_state();
void set_trigger(triggered_bsr_type_t new_trigger);
void set_trigger(bsr_trigger_type_t new_trigger);
void update_new_data();
void update_old_buffer();
bool check_highest_channel();
@ -100,7 +101,7 @@ private:
bool check_any_channel();
uint32_t get_buffer_state_lcg(uint32_t lcg);
bool generate_bsr(bsr_t* bsr, uint32_t nof_padding_bytes);
char* bsr_type_tostring(triggered_bsr_type_t type);
char* bsr_type_tostring(bsr_trigger_type_t type);
char* bsr_format_tostring(bsr_format_t format);
srsran::timer_handler::unique_timer timer_periodic;

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

@ -14,14 +14,13 @@
#define SRSUE_MAC_NR_H
#include "mac_nr_interfaces.h"
#include "proc_bsr_nr.h"
#include "proc_ra_nr.h"
#include "proc_sr_nr.h"
#include "srsran/common/block_queue.h"
#include "srsran/common/mac_pcap.h"
#include "srsran/interfaces/mac_interface_types.h"
#include "srsran/interfaces/ue_nr_interfaces.h"
#include "srsran/interfaces/ue_rlc_interfaces.h"
#include "srsran/mac/mac_sch_pdu_nr.h"
#include "srsran/srslog/srslog.h"
#include "srsue/hdr/stack/mac_nr/mux_nr.h"
#include "srsue/hdr/stack/ue_stack_base.h"
@ -33,13 +32,16 @@ class rlc_interface_mac;
struct mac_nr_args_t {
};
class mac_nr final : public mac_interface_phy_nr, public mac_interface_rrc_nr, public mac_interface_proc_ra_nr
class mac_nr final : public mac_interface_phy_nr,
public mac_interface_rrc_nr,
public mac_interface_proc_ra_nr,
public mac_interface_mux_nr
{
public:
mac_nr(srsran::ext_task_sched_handle task_sched_);
~mac_nr();
int init(const mac_nr_args_t& args_, phy_interface_mac_nr* phy, rlc_interface_mac* rlc);
int init(const mac_nr_args_t& args_, phy_interface_mac_nr* phy, rlc_interface_mac* rlc, rrc_interface_mac* rrc_);
void stop();
void reset();
@ -67,18 +69,17 @@ public:
void get_metrics(mac_metrics_t* metrics);
/// Interface for RRC (RRC -> MAC)
void setup_lcid(const srsran::logical_channel_config_t& config);
void set_config(const srsran::bsr_cfg_t& bsr_cfg);
int32_t set_config(const srsran::sr_cfg_nr_t& sr_cfg);
void set_config(const srsran::rach_nr_cfg_t& rach_cfg);
void set_contention_id(const uint64_t ue_identity);
bool set_crnti(const uint16_t crnti);
void start_ra_procedure();
/// procedure ra nr interface
int setup_lcid(const srsran::logical_channel_config_t& config);
int set_config(const srsran::bsr_cfg_nr_t& bsr_cfg);
int set_config(const srsran::sr_cfg_nr_t& sr_cfg);
void set_config(const srsran::rach_nr_cfg_t& rach_cfg);
void set_contention_id(const uint64_t ue_identity);
bool set_crnti(const uint16_t crnti);
void start_ra_procedure();
/// procedure ra nr interface + mux
uint64_t get_contention_id();
uint16_t get_c_rnti();
void set_c_rnti(uint64_t c_rnti_);
uint16_t get_crnti();
void msg3_flush() { mux.msg3_flush(); }
bool msg3_is_transmitted() { return mux.msg3_is_transmitted(); }
@ -107,12 +108,12 @@ private:
bool is_paging_opportunity();
bool has_crnti();
uint16_t get_crnti();
bool is_valid_crnti(const uint16_t crnti);
/// Interaction with rest of the stack
phy_interface_mac_nr* phy = nullptr;
rlc_interface_mac* rlc = nullptr;
rrc_interface_mac* rrc = nullptr;
srsran::ext_task_sched_handle task_sched;
srsran::mac_pcap* pcap = nullptr;
@ -124,9 +125,6 @@ private:
uint16_t c_rnti = SRSRAN_INVALID_RNTI;
uint64_t contention_id = 0;
static constexpr uint32_t MIN_RLC_PDU_LEN =
5; ///< minimum bytes that need to be available in a MAC PDU for attempting to add another RLC SDU
srsran::block_queue<srsran::unique_byte_buffer_t>
pdu_queue; ///< currently only DCH PDUs supported (add BCH, PCH, etc)
@ -136,17 +134,17 @@ private:
srsran::mac_sch_pdu_nr rx_pdu;
/// Tx buffer
srsran::mac_sch_pdu_nr tx_pdu;
srsran::unique_byte_buffer_t tx_buffer = nullptr;
srsran::unique_byte_buffer_t ul_harq_buffer = nullptr; // store PDU generated from MUX
srsran::unique_byte_buffer_t rlc_buffer = nullptr;
srsran_softbuffer_tx_t softbuffer_tx = {}; /// UL HARQ (temporal)
srsran::task_multiqueue::queue_handle stack_task_dispatch_queue;
// MAC Uplink-related procedures
proc_ra_nr proc_ra;
proc_sr_nr proc_sr;
mux_nr mux;
proc_ra_nr proc_ra;
proc_sr_nr proc_sr;
proc_bsr_nr proc_bsr;
mux_nr mux;
};
} // namespace srsue

@ -24,8 +24,8 @@ class mac_interface_proc_ra_nr
public:
// Functions for identity handling, e.g., contention id and c-rnti
virtual uint64_t get_contention_id() = 0;
virtual uint16_t get_c_rnti() = 0;
virtual void set_c_rnti(uint64_t c_rnti) = 0;
virtual uint16_t get_crnti() = 0;
virtual bool set_crnti(uint16_t c_rnti) = 0;
// Functions for msg3 manipulation which shall be transparent to the procedure
virtual bool msg3_is_transmitted() = 0;
@ -34,6 +34,16 @@ public:
virtual bool msg3_is_empty() = 0;
};
/**
* @brief Interface from MAC NR parent class to mux ubclass
*/
class mac_interface_mux_nr
{
public:
// MUX can query MAC for current C-RNTI for Msg3 transmission
virtual uint16_t get_crnti() = 0;
};
} // namespace srsue
#endif // SRSUE_MAC_NR_INTERFACES_H

@ -13,19 +13,25 @@
#ifndef SRSUE_MUX_NR_H
#define SRSUE_MUX_NR_H
#include "mac_nr_interfaces.h"
#include "proc_bsr_nr.h"
#include "srsran/common/byte_buffer.h"
#include "srsran/common/common.h"
#include "srsran/mac/mac_sch_pdu_nr.h"
#include "srsran/srslog/srslog.h"
#include "srsran/srsran.h"
#include "srsue/hdr/stack/mac_common/mux_base.h"
#include <mutex>
namespace srsue {
class mux_nr
class mux_nr final : mux_base, public mux_interface_bsr_nr
{
public:
explicit mux_nr(srslog::basic_logger& logger);
explicit mux_nr(mac_interface_mux_nr& mac_, srslog::basic_logger& logger);
~mux_nr(){};
void reset();
int32_t init();
int32_t init(rlc_interface_mac* rlc_);
void step();
@ -36,12 +42,39 @@ public:
bool msg3_is_pending();
bool msg3_is_empty();
// MAC interface
int setup_lcid(const srsran::logical_channel_config_t& config);
// Interface of UL HARQ
srsran::unique_byte_buffer_t get_pdu(uint32_t max_pdu_len);
// Interface for BSR procedure
void generate_bsr_mac_ce();
private:
// internal helper methods
// ctor configured members
mac_interface_mux_nr& mac;
rlc_interface_mac* rlc = nullptr;
srslog::basic_logger& logger;
// Msg3 related
srsran::unique_byte_buffer_t msg3_buff = nullptr;
typedef enum { none, pending, transmitted } msg3_state_t;
msg3_state_t msg3_state = none;
static constexpr uint32_t MIN_RLC_PDU_LEN =
5; ///< minimum bytes that need to be available in a MAC PDU for attempting to add another RLC SDU
srsran::unique_byte_buffer_t rlc_buff = nullptr;
srsran::mac_sch_pdu_nr tx_pdu;
// Mutex for exclusive access
std::mutex mutex;
};
} // namespace srsue
#endif // SRSUE_MUX_NR_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

@ -28,10 +28,10 @@ namespace srsue {
class proc_ra_nr
{
public:
proc_ra_nr(srslog::basic_logger& logger_);
proc_ra_nr(mac_interface_proc_ra_nr& mac_, srslog::basic_logger& logger_);
~proc_ra_nr(){};
void init(phy_interface_mac_nr* phy_h_, mac_interface_proc_ra_nr* mac_, srsran::ext_task_sched_handle* task_sched_);
void init(phy_interface_mac_nr* phy_h_, srsran::ext_task_sched_handle* task_sched_);
void set_config(const srsran::rach_nr_cfg_t& rach_cfg);
bool is_contention_resolution();
@ -51,9 +51,9 @@ public:
void reset();
private:
mac_interface_proc_ra_nr& mac;
srslog::basic_logger& logger;
phy_interface_mac_nr* phy = nullptr;
mac_interface_proc_ra_nr* mac = nullptr;
srsran::ext_task_sched_handle* task_sched = nullptr;
srsran::task_multiqueue::queue_handle task_queue;

@ -50,6 +50,7 @@ struct rrc_nr_metrics_t {};
class rrc_nr final : public rrc_interface_phy_nr,
public rrc_interface_pdcp,
public rrc_interface_rlc,
public rrc_interface_mac,
public rrc_nr_interface_rrc,
public srsran::timer_callback
{
@ -100,6 +101,11 @@ public:
// RLC interface
void max_retx_attempted() final;
// MAC interface
void ra_completed() final;
void ra_problem() final;
void release_pucch_srs() final;
// PDCP interface
void write_pdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) final;
void write_pdu_bcch_bch(srsran::unique_byte_buffer_t pdu) final;

@ -6,6 +6,7 @@
# the distribution.
#
add_subdirectory(mac_common)
add_subdirectory(mac)
add_subdirectory(rrc)
add_subdirectory(upper)

@ -7,4 +7,5 @@
#
set(SOURCES demux.cc dl_harq.cc mac.cc mux.cc proc_bsr.cc proc_phr.cc proc_ra.cc proc_sr.cc ul_harq.cc)
add_library(srsue_mac STATIC ${SOURCES})
add_library(srsue_mac STATIC ${SOURCES})
target_link_libraries(srsue_mac srsue_mac_common)

@ -70,74 +70,17 @@ bool mux::is_pending_any_sdu()
return false;
}
bool mux::has_logical_channel(const uint32_t& lcid)
{
for (auto& channel : logical_channels) {
if (channel.lcid == lcid) {
return true;
}
}
return false;
}
bool priority_compare(const logical_channel_config_t& u1, const logical_channel_config_t& u2)
{
return u1.priority <= u2.priority;
}
// This is called by RRC (stack thread) during bearer addition
void mux::setup_lcid(const logical_channel_config_t& config)
{
std::lock_guard<std::mutex> lock(mutex);
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) {
logger.warning("LCID %d and %d have same priority.", channel.lcid, config.lcid);
}
}
} 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);
mux_base::setup_lcid(config);
}
// mutex should be hold by caller
void mux::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);
}
logger.debug("%s", logline.c_str());
mux_base::print_logical_channel_state(info);
}
srsran::ul_sch_lcid bsr_format_convert(bsr_proc::bsr_format_t format)

@ -49,10 +49,11 @@ void bsr_proc::print_state()
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_type_tostring(triggered_bsr_type), str);
logger.info(
"BSR: triggered_bsr_type=%s, LCID QUEUE status: %s", bsr_trigger_type_tostring(triggered_bsr_type), str);
}
void bsr_proc::set_trigger(srsue::bsr_proc::triggered_bsr_type_t new_trigger)
void bsr_proc::set_trigger(bsr_trigger_type_t new_trigger)
{
triggered_bsr_type = new_trigger;
@ -300,21 +301,6 @@ void bsr_proc::step(uint32_t tti)
update_old_buffer();
}
char* bsr_proc::bsr_type_tostring(triggered_bsr_type_t type)
{
switch (type) {
case bsr_proc::NONE:
return (char*)"none";
case bsr_proc::REGULAR:
return (char*)"Regular";
case bsr_proc::PADDING:
return (char*)"Padding";
case bsr_proc::PERIODIC:
return (char*)"Periodic";
}
return (char*)"unknown";
}
char* bsr_proc::bsr_format_tostring(bsr_format_t format)
{
switch (format) {

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

@ -6,6 +6,6 @@
# the distribution.
#
set(SOURCES mac_nr.cc proc_ra_nr.cc proc_sr_nr.cc mux_nr.cc)
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 srsran_mac)
target_link_libraries(srsue_mac_nr srsue_mac_common srsran_mac)

@ -11,6 +11,7 @@
*/
#include "srsue/hdr/stack/mac_nr/mac_nr.h"
#include "srsran/interfaces/ue_rlc_interfaces.h"
#include "srsran/mac/mac_rar_pdu_nr.h"
#include "srsue/hdr/stack/mac_nr/proc_ra_nr.h"
@ -19,9 +20,10 @@ namespace srsue {
mac_nr::mac_nr(srsran::ext_task_sched_handle task_sched_) :
task_sched(task_sched_),
logger(srslog::fetch_basic_logger("MAC")),
proc_ra(logger),
proc_ra(*this, logger),
proc_sr(logger),
mux(logger),
proc_bsr(logger),
mux(*this, logger),
pcap(nullptr)
{}
@ -30,18 +32,29 @@ mac_nr::~mac_nr()
stop();
}
int mac_nr::init(const mac_nr_args_t& args_, phy_interface_mac_nr* phy_, rlc_interface_mac* rlc_)
int mac_nr::init(const mac_nr_args_t& args_,
phy_interface_mac_nr* phy_,
rlc_interface_mac* rlc_,
rrc_interface_mac* rrc_)
{
args = args_;
phy = phy_;
rlc = rlc_;
rrc = rrc_;
// Create Stack task dispatch queue
stack_task_dispatch_queue = task_sched.make_task_queue();
proc_ra.init(phy, this, &task_sched);
// Init MAC sub procedures
proc_ra.init(phy, &task_sched);
proc_sr.init(&proc_ra, phy, rrc);
if (mux.init() != SRSRAN_SUCCESS) {
if (proc_bsr.init(&proc_sr, &mux, rlc, &task_sched) != SRSRAN_SUCCESS) {
logger.error("Couldn't initialize BSR procedure.");
return SRSRAN_ERROR;
}
if (mux.init(rlc) != SRSRAN_SUCCESS) {
logger.error("Couldn't initialize mux unit.");
return SRSRAN_ERROR;
}
@ -52,12 +65,8 @@ int mac_nr::init(const mac_nr_args_t& args_, phy_interface_mac_nr* phy_, rlc_int
return SRSRAN_ERROR;
}
tx_buffer = srsran::make_byte_buffer();
if (tx_buffer == nullptr) {
return SRSRAN_ERROR;
}
rlc_buffer = srsran::make_byte_buffer();
if (rlc_buffer == nullptr) {
ul_harq_buffer = srsran::make_byte_buffer();
if (ul_harq_buffer == nullptr) {
return SRSRAN_ERROR;
}
@ -224,67 +233,26 @@ void mac_nr::new_grant_ul(const uint32_t cc_idx, const mac_nr_grant_ul_t& grant,
proc_ra.pdcch_to_crnti();
}
// Let BSR know there is a new grant, might have to send a BSR
proc_bsr.new_grant_ul(grant.tbs);
// TODO: add proper UL-HARQ
// The code below assumes a single HARQ entity, no retx, every Tx is always a new transmission
ul_harq_buffer = mux.get_pdu(grant.tbs);
// fill TB action (goes into UL harq eventually)
action->tb.payload = tx_buffer.get();
action->tb.payload = ul_harq_buffer.get(); // pass handle to PDU to PHY
action->tb.enabled = true;
action->tb.rv = 0;
action->tb.softbuffer = &softbuffer_tx;
srsran_softbuffer_tx_reset(&softbuffer_tx);
// Pack MAC PDU
get_ul_data(grant, action->tb.payload);
metrics[cc_idx].tx_pkts++;
}
void mac_nr::get_ul_data(const mac_nr_grant_ul_t& grant, srsran::byte_buffer_t* phy_tx_pdu)
{
// initialize MAC PDU
phy_tx_pdu->clear();
tx_pdu.init_tx(phy_tx_pdu, grant.tbs / 8U, true);
if (mux.msg3_is_pending()) {
// If message 3 is pending pack message 3 for uplink transmission
// Use the CRNTI which is provided in the RRC reconfiguration (only for DC mode maybe other)
tx_pdu.add_crnti_ce(c_rnti);
srsran::mac_sch_subpdu_nr::lcg_bsr_t sbsr = {};
sbsr.lcg_id = 0;
sbsr.buffer_size = 1;
tx_pdu.add_sbsr_ce(sbsr);
logger.info("Generated msg3 with RNTI 0x%x", c_rnti);
mux.msg3_transmitted();
} else {
// Pack normal UL data PDU
while (tx_pdu.get_remaing_len() >= MIN_RLC_PDU_LEN) {
// read RLC PDU
rlc_buffer->clear();
uint8_t* rd = rlc_buffer->msg;
int pdu_len = 0;
pdu_len = rlc->read_pdu(4, rd, tx_pdu.get_remaing_len() - 2);
// Add SDU if RLC has something to tx
if (pdu_len > 0) {
rlc_buffer->N_bytes = pdu_len;
logger.info(rlc_buffer->msg, rlc_buffer->N_bytes, "Read %d B from RLC", rlc_buffer->N_bytes);
// add to MAC PDU and pack
if (tx_pdu.add_sdu(4, rlc_buffer->msg, rlc_buffer->N_bytes) != SRSRAN_SUCCESS) {
logger.error("Error packing MAC PDU");
}
} else {
break;
}
}
}
// 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);
// store PCAP
if (pcap) {
pcap->write_ul_crnti_nr(phy_tx_pdu->msg, phy_tx_pdu->N_bytes, grant.rnti, grant.pid, grant.tti);
pcap->write_ul_crnti_nr(ul_harq_buffer->msg, ul_harq_buffer->N_bytes, grant.rnti, grant.pid, grant.tti);
}
metrics[cc_idx].tx_pkts++;
}
void mac_nr::timer_expired(uint32_t timer_id)
@ -292,8 +260,18 @@ void mac_nr::timer_expired(uint32_t timer_id)
// not implemented
}
void mac_nr::setup_lcid(const srsran::logical_channel_config_t& config)
int mac_nr::setup_lcid(const srsran::logical_channel_config_t& config)
{
if (mux.setup_lcid(config) != SRSRAN_SUCCESS) {
logger.error("Couldn't register logical channel at MUX unit.");
return SRSRAN_ERROR;
}
if (proc_bsr.setup_lcid(config.lcid, config.lcg, config.priority) != SRSRAN_SUCCESS) {
logger.error("Couldn't register logical channel at BSR procedure.");
return SRSRAN_ERROR;
}
logger.info("Logical Channel Setup: LCID=%d, LCG=%d, priority=%d, PBR=%d, BSD=%dms, bucket_size=%d",
config.lcid,
config.lcg,
@ -301,17 +279,16 @@ void mac_nr::setup_lcid(const srsran::logical_channel_config_t& config)
config.PBR,
config.BSD,
config.bucket_size);
// mux_unit.setup_lcid(config);
// bsr_procedure.setup_lcid(config.lcid, config.lcg, config.priority);
return SRSRAN_SUCCESS;
}
void mac_nr::set_config(const srsran::bsr_cfg_t& bsr_cfg)
int mac_nr::set_config(const srsran::bsr_cfg_nr_t& bsr_cfg)
{
logger.info("BSR config periodic timer %d retx timer %d", bsr_cfg.periodic_timer, bsr_cfg.retx_timer);
logger.warning("Not handling BSR config yet");
return proc_bsr.set_config(bsr_cfg);
}
int32_t mac_nr::set_config(const srsran::sr_cfg_nr_t& sr_cfg)
int mac_nr::set_config(const srsran::sr_cfg_nr_t& sr_cfg)
{
return proc_sr.set_config(sr_cfg);
}
@ -378,9 +355,7 @@ void mac_nr::handle_pdu(srsran::unique_byte_buffer_t pdu)
subpdu.get_c_rnti(),
subpdu.get_lcid(),
subpdu.get_sdu_length());
if (subpdu.get_lcid() == 4) {
rlc->write_pdu(subpdu.get_lcid(), subpdu.get_sdu(), subpdu.get_sdu_length());
}
rlc->write_pdu(subpdu.get_lcid(), subpdu.get_sdu(), subpdu.get_sdu_length());
}
}
@ -389,16 +364,6 @@ uint64_t mac_nr::get_contention_id()
return 0xdeadbeef; // TODO when rebased on PR
}
uint16_t mac_nr::get_c_rnti()
{
return c_rnti;
}
void mac_nr::set_c_rnti(uint64_t c_rnti_)
{
c_rnti = c_rnti_;
}
// TODO same function as for mac_eutra
bool mac_nr::is_in_window(uint32_t tti, int* start, int* len)
{

@ -12,21 +12,90 @@
#include "srsue/hdr/stack/mac_nr/mux_nr.h"
#include "srsran/common/buffer_pool.h"
#include "srsran/interfaces/ue_rlc_interfaces.h"
namespace srsue {
mux_nr::mux_nr(srslog::basic_logger& logger_) : logger(logger_){};
mux_nr::mux_nr(mac_interface_mux_nr& mac_, srslog::basic_logger& logger_) : mac(mac_), logger(logger_) {}
int32_t mux_nr::init()
int32_t mux_nr::init(rlc_interface_mac* rlc_)
{
rlc = rlc_;
msg3_buff = srsran::make_byte_buffer();
if (msg3_buff == nullptr) {
return SRSRAN_ERROR;
}
rlc_buff = srsran::make_byte_buffer();
if (rlc_buff == nullptr) {
return SRSRAN_ERROR;
}
return SRSRAN_SUCCESS;
}
int mux_nr::setup_lcid(const srsran::logical_channel_config_t& config)
{
std::lock_guard<std::mutex> lock(mutex);
return mux_base::setup_lcid(config);
}
srsran::unique_byte_buffer_t mux_nr::get_pdu(uint32_t max_pdu_len)
{
// initialize MAC PDU
srsran::unique_byte_buffer_t phy_tx_pdu = srsran::make_byte_buffer();
if (phy_tx_pdu == nullptr) {
return nullptr;
}
tx_pdu.init_tx(phy_tx_pdu.get(), max_pdu_len, true);
if (msg3_is_pending()) {
// If message 3 is pending pack message 3 for uplink transmission
// Use the CRNTI which is provided in the RRC reconfiguration (only for DC mode maybe other)
tx_pdu.add_crnti_ce(mac.get_crnti());
srsran::mac_sch_subpdu_nr::lcg_bsr_t sbsr = {};
sbsr.lcg_id = 0;
sbsr.buffer_size = 1;
tx_pdu.add_sbsr_ce(sbsr);
logger.info("Generated msg3 with RNTI 0x%x", mac.get_crnti());
msg3_transmitted();
} else {
// Pack normal UL data PDU
// TODO: Add proper priority handling
for (const auto& lc : logical_channels) {
while (tx_pdu.get_remaing_len() >= MIN_RLC_PDU_LEN) {
// read RLC PDU
rlc_buff->clear();
uint8_t* rd = rlc_buff->msg;
int pdu_len = 0;
pdu_len = rlc->read_pdu(lc.lcid, rd, tx_pdu.get_remaing_len() - 2);
// Add SDU if RLC has something to tx
if (pdu_len > 0) {
rlc_buff->N_bytes = pdu_len;
logger.info(rlc_buff->msg, rlc_buff->N_bytes, "Read %d B from RLC", rlc_buff->N_bytes);
// add to MAC PDU and pack
if (tx_pdu.add_sdu(lc.lcid, rlc_buff->msg, rlc_buff->N_bytes) != SRSRAN_SUCCESS) {
logger.error("Error packing MAC PDU");
}
} else {
break;
}
}
}
}
// 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);
return phy_tx_pdu;
}
void mux_nr::msg3_flush()
{
msg3_buff->clear();
@ -58,4 +127,6 @@ bool mux_nr::msg3_is_empty()
return msg3_buff->N_bytes == 0;
}
void mux_nr::generate_bsr_mac_ce() {}
} // 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

@ -30,14 +30,11 @@ uint32_t backoff_table_nr[16] = {0, 10, 20, 30, 40, 60, 80, 120, 160, 240, 320,
// Table 7.6-1: DELTA_PREAMBLE values long
int delta_preamble_db_table_nr[5] = {0, -3, -6, 0};
proc_ra_nr::proc_ra_nr(srslog::basic_logger& logger_) : logger(logger_) {}
proc_ra_nr::proc_ra_nr(mac_interface_proc_ra_nr& mac_, srslog::basic_logger& logger_) : mac(mac_), logger(logger_) {}
void proc_ra_nr::init(phy_interface_mac_nr* phy_,
mac_interface_proc_ra_nr* mac_,
srsran::ext_task_sched_handle* task_sched_)
void proc_ra_nr::init(phy_interface_mac_nr* phy_, srsran::ext_task_sched_handle* task_sched_)
{
phy = phy_;
mac = mac_;
task_sched = task_sched_;
task_queue = task_sched->make_task_queue();
prach_send_timer = task_sched->get_unique_timer();
@ -144,7 +141,7 @@ void proc_ra_nr::timer_expired(uint32_t timer_id)
// 5.1.2 Random Access Resource selection
void proc_ra_nr::ra_procedure_initialization()
{
mac->msg3_flush();
mac.msg3_flush();
preamble_power_ramping_step = rach_cfg.powerRampingStep;
scaling_factor_bi = 1;
preambleTransMax = rach_cfg.preambleTransMax;
@ -206,7 +203,7 @@ void proc_ra_nr::ra_response_reception(const mac_interface_phy_nr::mac_nr_grant_
// reset all parameters that are used before rar
rar_rnti = SRSRAN_INVALID_RNTI;
mac->msg3_prepare();
mac.msg3_prepare();
current_ta = subpdu.get_ta();
}
}
@ -260,8 +257,8 @@ void proc_ra_nr::ra_completion()
srsran::enum_to_text(state_str_nr, (uint32_t)ra_state_t::MAX_RA_STATES, WAITING_FOR_COMPLETION));
return;
}
srsran::console("Random Access Complete. c-rnti=0x%x, ta=%d\n", mac->get_c_rnti(), current_ta);
logger.info("Random Access Complete. c-rnti=0x%x, ta=%d", mac->get_c_rnti(), current_ta);
srsran::console("Random Access Complete. c-rnti=0x%x, ta=%d\n", mac.get_crnti(), current_ta);
logger.info("Random Access Complete. c-rnti=0x%x, ta=%d", mac.get_crnti(), current_ta);
temp_rnti = SRSRAN_INVALID_RNTI;
reset();
}

@ -557,10 +557,12 @@ bool rrc_nr::apply_mac_cell_group(const mac_cell_group_cfg_s& mac_cell_group_cfg
if (mac_cell_group_cfg.bsr_cfg_present) {
logger.debug("Handling MAC BSR config");
srsran::bsr_cfg_t bsr_cfg;
srsran::bsr_cfg_nr_t bsr_cfg = {};
bsr_cfg.periodic_timer = mac_cell_group_cfg.bsr_cfg.periodic_bsr_timer.to_number();
bsr_cfg.retx_timer = mac_cell_group_cfg.bsr_cfg.retx_bsr_timer.to_number();
mac->set_config(bsr_cfg);
if (mac->set_config(bsr_cfg) != SRSRAN_SUCCESS) {
return false;
}
}
if (mac_cell_group_cfg.tag_cfg_present) {
@ -1235,6 +1237,11 @@ bool rrc_nr::apply_radio_bearer_cfg(const radio_bearer_cfg_s& radio_bearer_cfg)
// RLC interface
void rrc_nr::max_retx_attempted() {}
// MAC interface
void rrc_nr::ra_completed() {}
void rrc_nr::ra_problem() {}
void rrc_nr::release_pucch_srs() {}
// STACK interface
void rrc_nr::cell_search_completed(const rrc_interface_phy_lte::cell_search_ret_t& cs_ret, const phy_cell_t& found_cell)
{}

@ -199,7 +199,7 @@ int ue_stack_lte::init(const stack_args_t& args_)
nas.init(usim.get(), &rrc, gw, args.nas);
mac_nr_args_t mac_nr_args = {};
mac_nr.init(mac_nr_args, phy_nr, &rlc);
mac_nr.init(mac_nr_args, phy_nr, &rlc, &rrc_nr);
rrc_nr.init(phy_nr, &mac_nr, &rlc, &pdcp, gw, &rrc, usim.get(), task_sched.get_timer_handler(), nullptr, args.rrc_nr);
rrc.init(phy, &mac, &rlc, &pdcp, &nas, usim.get(), gw, &rrc_nr, args.rrc);

@ -67,7 +67,7 @@ int ue_stack_nr::init(const stack_args_t& args_)
pdcp_logger.set_hex_dump_max_size(args.log.pdcp_hex_limit);
mac_nr_args_t mac_args = {};
mac->init(mac_args, phy, rlc.get());
mac->init(mac_args, phy, rlc.get(), rrc.get());
rlc->init(pdcp.get(), rrc.get(), task_sched.get_timer_handler(), 0 /* RB_ID_SRB0 */);
pdcp->init(rlc.get(), rrc.get(), gw);

@ -8,4 +8,8 @@
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_test(proc_ra_nr_test proc_ra_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)

@ -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;
}

@ -53,8 +53,12 @@ class dummy_mac : public mac_interface_proc_ra_nr
{
public:
uint64_t get_contention_id() { return 0xdeadbeaf; }
uint16_t get_c_rnti() { return crnti; }
void set_c_rnti(uint64_t c_rnti) { crnti = c_rnti; }
uint16_t get_crnti() { return crnti; }
bool set_crnti(uint16_t c_rnti)
{
crnti = c_rnti;
return true;
}
bool msg3_is_transmitted() { return true; }
void msg3_flush() {}
@ -79,9 +83,9 @@ int main()
srsran::task_scheduler task_sched{5, 2};
srsran::ext_task_sched_handle ext_task_sched_h(&task_sched);
proc_ra_nr proc_ra_nr(mac_logger);
proc_ra_nr proc_ra_nr(dummy_mac, mac_logger);
proc_ra_nr.init(&dummy_phy, &dummy_mac, &ext_task_sched_h);
proc_ra_nr.init(&dummy_phy, &ext_task_sched_h);
TESTASSERT(proc_ra_nr.is_rar_opportunity(1) == false);
srsran::rach_nr_cfg_t rach_cfg;
rach_cfg.powerRampingStep = 4;

Loading…
Cancel
Save