diff --git a/lib/include/srsran/interfaces/mac_interface_types.h b/lib/include/srsran/interfaces/mac_interface_types.h index 836330366..e328476b9 100644 --- a/lib/include/srsran/interfaces/mac_interface_types.h +++ b/lib/include/srsran/interfaces/mac_interface_types.h @@ -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(); } diff --git a/lib/include/srsran/interfaces/ue_nr_interfaces.h b/lib/include/srsran/interfaces/ue_nr_interfaces.h index bada51663..0ffdf884f 100644 --- a/lib/include/srsran/interfaces/ue_nr_interfaces.h +++ b/lib/include/srsran/interfaces/ue_nr_interfaces.h @@ -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; diff --git a/lib/include/srsran/interfaces/ue_rrc_interfaces.h b/lib/include/srsran/interfaces/ue_rrc_interfaces.h index 3cfdd889b..16dbfd0b8 100644 --- a/lib/include/srsran/interfaces/ue_rrc_interfaces.h +++ b/lib/include/srsran/interfaces/ue_rrc_interfaces.h @@ -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; }; diff --git a/lib/include/srsran/mac/mac_sch_pdu_nr.h b/lib/include/srsran/mac/mac_sch_pdu_nr.h index bc03bca6b..dd196ff6b 100644 --- a/lib/include/srsran/mac/mac_sch_pdu_nr.h +++ b/lib/include/srsran/mac/mac_sch_pdu_nr.h @@ -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; diff --git a/srsue/hdr/stack/mac/mux.h b/srsue/hdr/stack/mac/mux.h index fa4bc25d9..dc7082f7a 100644 --- a/srsue/hdr/stack/mac/mux.h +++ b/srsue/hdr/stack/mac/mux.h @@ -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 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 logical_channels; - // Mutex for exclusive access std::mutex mutex; diff --git a/srsue/hdr/stack/mac/proc_bsr.h b/srsue/hdr/stack/mac/proc_bsr.h index f574ad454..8fa99505f 100644 --- a/srsue/hdr/stack/mac/proc_bsr.h +++ b/srsue/hdr/stack/mac/proc_bsr.h @@ -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 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; diff --git a/srsue/hdr/stack/mac_common/mac_common.h b/srsue/hdr/stack/mac_common/mac_common.h new file mode 100644 index 000000000..42cd47af7 --- /dev/null +++ b/srsue/hdr/stack/mac_common/mac_common.h @@ -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 diff --git a/srsue/hdr/stack/mac_common/mux_base.h b/srsue/hdr/stack/mac_common/mux_base.h new file mode 100644 index 000000000..6ddee4928 --- /dev/null +++ b/srsue/hdr/stack/mac_common/mux_base.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 logical_channels; +}; + +} // namespace srsue + +#endif // SRSUE_MUX_BASE_H \ No newline at end of file diff --git a/srsue/hdr/stack/mac_nr/mac_nr.h b/srsue/hdr/stack/mac_nr/mac_nr.h index 40f2e00ac..cb58f5d04 100644 --- a/srsue/hdr/stack/mac_nr/mac_nr.h +++ b/srsue/hdr/stack/mac_nr/mac_nr.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(); + 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 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 diff --git a/srsue/hdr/stack/mac_nr/mac_nr_interfaces.h b/srsue/hdr/stack/mac_nr/mac_nr_interfaces.h index 68a3d64dd..e35663b53 100644 --- a/srsue/hdr/stack/mac_nr/mac_nr_interfaces.h +++ b/srsue/hdr/stack/mac_nr/mac_nr_interfaces.h @@ -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 \ No newline at end of file diff --git a/srsue/hdr/stack/mac_nr/mux_nr.h b/srsue/hdr/stack/mac_nr/mux_nr.h index 3c80add9c..5a6c0c0d1 100644 --- a/srsue/hdr/stack/mac_nr/mux_nr.h +++ b/srsue/hdr/stack/mac_nr/mux_nr.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 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 diff --git a/srsue/hdr/stack/mac_nr/proc_bsr_nr.h b/srsue/hdr/stack/mac_nr/proc_bsr_nr.h new file mode 100644 index 000000000..3ac29661c --- /dev/null +++ b/srsue/hdr/stack/mac_nr/proc_bsr_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 +#include + +#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 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 diff --git a/srsue/hdr/stack/mac_nr/proc_ra_nr.h b/srsue/hdr/stack/mac_nr/proc_ra_nr.h index 88feb0f57..454a8b777 100644 --- a/srsue/hdr/stack/mac_nr/proc_ra_nr.h +++ b/srsue/hdr/stack/mac_nr/proc_ra_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; diff --git a/srsue/hdr/stack/rrc/rrc_nr.h b/srsue/hdr/stack/rrc/rrc_nr.h index 2e04a88b3..20cc18499 100644 --- a/srsue/hdr/stack/rrc/rrc_nr.h +++ b/srsue/hdr/stack/rrc/rrc_nr.h @@ -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; diff --git a/srsue/src/stack/CMakeLists.txt b/srsue/src/stack/CMakeLists.txt index e158bacea..c2834833e 100644 --- a/srsue/src/stack/CMakeLists.txt +++ b/srsue/src/stack/CMakeLists.txt @@ -6,6 +6,7 @@ # the distribution. # +add_subdirectory(mac_common) add_subdirectory(mac) add_subdirectory(rrc) add_subdirectory(upper) diff --git a/srsue/src/stack/mac/CMakeLists.txt b/srsue/src/stack/mac/CMakeLists.txt index 2d385bf9f..8f1e60171 100644 --- a/srsue/src/stack/mac/CMakeLists.txt +++ b/srsue/src/stack/mac/CMakeLists.txt @@ -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}) \ No newline at end of file +add_library(srsue_mac STATIC ${SOURCES}) +target_link_libraries(srsue_mac srsue_mac_common) \ No newline at end of file diff --git a/srsue/src/stack/mac/mux.cc b/srsue/src/stack/mac/mux.cc index 1cd4564ea..bc3986f04 100644 --- a/srsue/src/stack/mac/mux.cc +++ b/srsue/src/stack/mac/mux.cc @@ -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 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) diff --git a/srsue/src/stack/mac/proc_bsr.cc b/srsue/src/stack/mac/proc_bsr.cc index 8d360795e..a464b4318 100644 --- a/srsue/src/stack/mac/proc_bsr.cc +++ b/srsue/src/stack/mac/proc_bsr.cc @@ -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) { diff --git a/srsue/src/stack/mac_common/CMakeLists.txt b/srsue/src/stack/mac_common/CMakeLists.txt new file mode 100644 index 000000000..350278cbd --- /dev/null +++ b/srsue/src/stack/mac_common/CMakeLists.txt @@ -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}) \ No newline at end of file diff --git a/srsue/src/stack/mac_common/mac_common.cc b/srsue/src/stack/mac_common/mac_common.cc new file mode 100644 index 000000000..435bf3707 --- /dev/null +++ b/srsue/src/stack/mac_common/mac_common.cc @@ -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 diff --git a/srsue/src/stack/mac_nr/CMakeLists.txt b/srsue/src/stack/mac_nr/CMakeLists.txt index 90ccea98c..70f32152e 100644 --- a/srsue/src/stack/mac_nr/CMakeLists.txt +++ b/srsue/src/stack/mac_nr/CMakeLists.txt @@ -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) \ No newline at end of file +target_link_libraries(srsue_mac_nr srsue_mac_common srsran_mac) \ No newline at end of file diff --git a/srsue/src/stack/mac_nr/mac_nr.cc b/srsue/src/stack/mac_nr/mac_nr.cc index f84f69b4a..d752af8c0 100644 --- a/srsue/src/stack/mac_nr/mac_nr.cc +++ b/srsue/src/stack/mac_nr/mac_nr.cc @@ -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,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) { diff --git a/srsue/src/stack/mac_nr/mux_nr.cc b/srsue/src/stack/mac_nr/mux_nr.cc index f9225f394..c72162b36 100644 --- a/srsue/src/stack/mac_nr/mux_nr.cc +++ b/srsue/src/stack/mac_nr/mux_nr.cc @@ -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 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 diff --git a/srsue/src/stack/mac_nr/proc_bsr_nr.cc b/srsue/src/stack/mac_nr/proc_bsr_nr.cc new file mode 100644 index 000000000..9ebbe3aa2 --- /dev/null +++ b/srsue/src/stack/mac_nr/proc_bsr_nr.cc @@ -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 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 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::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::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::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::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::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::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 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 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 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 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::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 diff --git a/srsue/src/stack/mac_nr/proc_ra_nr.cc b/srsue/src/stack/mac_nr/proc_ra_nr.cc index b67c80e92..05023ba85 100644 --- a/srsue/src/stack/mac_nr/proc_ra_nr.cc +++ b/srsue/src/stack/mac_nr/proc_ra_nr.cc @@ -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(); } diff --git a/srsue/src/stack/rrc/rrc_nr.cc b/srsue/src/stack/rrc/rrc_nr.cc index 240a4e80c..c7a69c6e4 100644 --- a/srsue/src/stack/rrc/rrc_nr.cc +++ b/srsue/src/stack/rrc/rrc_nr.cc @@ -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) {} diff --git a/srsue/src/stack/ue_stack_lte.cc b/srsue/src/stack/ue_stack_lte.cc index af4627e05..12dc2d4b8 100644 --- a/srsue/src/stack/ue_stack_lte.cc +++ b/srsue/src/stack/ue_stack_lte.cc @@ -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); diff --git a/srsue/src/stack/ue_stack_nr.cc b/srsue/src/stack/ue_stack_nr.cc index 611f45a58..11761aaa2 100644 --- a/srsue/src/stack/ue_stack_nr.cc +++ b/srsue/src/stack/ue_stack_nr.cc @@ -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); diff --git a/srsue/test/mac_nr/CMakeLists.txt b/srsue/test/mac_nr/CMakeLists.txt index a1a2c6e53..62d6cfa1b 100644 --- a/srsue/test/mac_nr/CMakeLists.txt +++ b/srsue/test/mac_nr/CMakeLists.txt @@ -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) \ No newline at end of file +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) \ No newline at end of file diff --git a/srsue/test/mac_nr/mac_nr_test.cc b/srsue/test/mac_nr/mac_nr_test.cc new file mode 100644 index 000000000..55ac58980 --- /dev/null +++ b/srsue/test/mac_nr/mac_nr_test.cc @@ -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 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, 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 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 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(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; +} diff --git a/srsue/test/mac_nr/proc_ra_nr_test.cc b/srsue/test/mac_nr/proc_ra_nr_test.cc index 4f734b8eb..96b6fd409 100644 --- a/srsue/test/mac_nr/proc_ra_nr_test.cc +++ b/srsue/test/mac_nr/proc_ra_nr_test.cc @@ -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;