mirror of https://github.com/PentHertz/srsLTE.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 interfaces
This commit is contained in:
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,9 +99,9 @@ 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 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
|
||||
|
|
|
@ -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);
|
||||
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
|
||||
/// 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,8 +134,7 @@ 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)
|
||||
|
||||
|
@ -146,6 +143,7 @@ private:
|
|||
// MAC Uplink-related procedures
|
||||
proc_ra_nr proc_ra;
|
||||
proc_sr_nr proc_sr;
|
||||
proc_bsr_nr proc_bsr;
|
||||
mux_nr mux;
|
||||
};
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -8,3 +8,4 @@
|
|||
|
||||
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})
|
||||
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,76 +233,45 @@ 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);
|
||||
// store PCAP
|
||||
if (pcap) {
|
||||
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::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);
|
||||
|
||||
if (pcap) {
|
||||
pcap->write_ul_crnti_nr(phy_tx_pdu->msg, phy_tx_pdu->N_bytes, grant.rnti, grant.pid, grant.tti);
|
||||
}
|
||||
}
|
||||
|
||||
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,10 +355,8 @@ 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t mac_nr::get_contention_id()
|
||||
|
@ -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);
|
||||
|
||||
|
|
|
@ -9,3 +9,7 @@
|
|||
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(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…
Reference in New Issue