diff --git a/CMakeLists.txt b/CMakeLists.txt index 06ec93a72..716b1be35 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,7 +30,7 @@ endif(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) ######################################################################## # Project setup ######################################################################## -cmake_minimum_required(VERSION 2.6) +cmake_minimum_required(VERSION 3.10) project( SRSRAN ) message( STATUS "CMAKE_SYSTEM: " ${CMAKE_SYSTEM} ) message( STATUS "CMAKE_SYSTEM_PROCESSOR: " ${CMAKE_SYSTEM_PROCESSOR} ) diff --git a/lib/include/srsran/adt/optional_array.h b/lib/include/srsran/adt/optional_array.h index 88262fa14..8fcd3d660 100644 --- a/lib/include/srsran/adt/optional_array.h +++ b/lib/include/srsran/adt/optional_array.h @@ -41,7 +41,8 @@ protected: template class iterator_impl { - using It = iterator_impl; + using It = iterator_impl; + using parent_t = typename std::conditional::value, const base_t, base_t>::type; public: using iterator_category = std::forward_iterator_tag; @@ -51,7 +52,7 @@ protected: using reference = Obj&; iterator_impl() = default; - iterator_impl(base_t* parent_, size_t idx_) : parent(parent_), idx(idx_) + iterator_impl(parent_t* parent_, size_t idx_) : parent(parent_), idx(idx_) { if (idx < parent->vec.size() and not parent->contains(idx)) { ++(*this); @@ -80,8 +81,8 @@ protected: private: friend base_t; - base_t* parent = nullptr; - size_t idx = std::numeric_limits::max(); + parent_t* parent = nullptr; + size_t idx = std::numeric_limits::max(); }; size_t nof_elems = 0; diff --git a/lib/include/srsran/asn1/rrc_nr_utils.h b/lib/include/srsran/asn1/rrc_nr_utils.h index 4a7d11406..6887ec660 100644 --- a/lib/include/srsran/asn1/rrc_nr_utils.h +++ b/lib/include/srsran/asn1/rrc_nr_utils.h @@ -64,9 +64,13 @@ struct nzp_csi_rs_res_s; struct pdsch_serving_cell_cfg_s; struct freq_info_dl_s; struct serving_cell_cfg_common_s; +struct serving_cell_cfg_common_sib_s; struct serving_cell_cfg_s; struct pdcch_cfg_common_s; struct pdcch_cfg_s; +struct pdsch_cfg_common_s; +struct pucch_cfg_common_s; +struct pusch_cfg_common_s; struct mib_s; struct srb_to_add_mod_s; @@ -133,17 +137,23 @@ bool make_pdsch_cfg_from_serv_cell(const asn1::rrc_nr::serving_cell_cfg_s& serv_ bool make_csi_cfg_from_serv_cell(const asn1::rrc_nr::serving_cell_cfg_s& serv_cell, srsran_csi_hl_cfg_t* csi_hl); bool make_duplex_cfg_from_serv_cell(const asn1::rrc_nr::serving_cell_cfg_common_s& serv_cell, srsran_duplex_config_nr_t* duplex_cfg); -bool fill_phy_pdcch_cfg_common(const asn1::rrc_nr::pdcch_cfg_common_s& pdcch_cfg, srsran_pdcch_cfg_nr_t* pdcch); +void fill_phy_pdcch_cfg_common(const asn1::rrc_nr::pdcch_cfg_common_s& pdcch_cfg, srsran_pdcch_cfg_nr_t* pdcch); bool fill_phy_pdcch_cfg(const asn1::rrc_nr::pdcch_cfg_s& pdcch_cfg, srsran_pdcch_cfg_nr_t* pdcch); +bool fill_phy_pdsch_cfg_common(const asn1::rrc_nr::pdsch_cfg_common_s& pdsch_cfg, srsran_sch_hl_cfg_nr_t* pdsch); +void fill_phy_pucch_cfg_common(const asn1::rrc_nr::pucch_cfg_common_s& pucch_cfg, srsran_pucch_nr_common_cfg_t* pucch); +bool fill_phy_pusch_cfg_common(const asn1::rrc_nr::pusch_cfg_common_s& pusch_cfg, srsran_sch_hl_cfg_nr_t* pusch); +void fill_phy_carrier_cfg(const asn1::rrc_nr::serving_cell_cfg_common_sib_s& serv_cell_cfg, + srsran_carrier_nr_t* carrier_nr); +void fill_phy_ssb_cfg(const asn1::rrc_nr::serving_cell_cfg_common_sib_s& serv_cell_cfg, phy_cfg_nr_t::ssb_cfg_t* ssb); /*************************** * MAC Config **************************/ logical_channel_config_t make_mac_logical_channel_cfg_t(uint8_t lcid, const asn1::rrc_nr::lc_ch_cfg_s& asn1_type); -rach_nr_cfg_t make_mac_rach_cfg(const asn1::rrc_nr::rach_cfg_common_s& asn1_type); -bool make_mac_phr_cfg_t(const asn1::rrc_nr::phr_cfg_s& asn1_type, phr_cfg_nr_t* phr_cfg_nr); -bool make_mac_dl_harq_cfg_nr_t(const asn1::rrc_nr::pdsch_serving_cell_cfg_s& asn1_type, - dl_harq_cfg_nr_t* out_dl_harq_cfg_nr); +void make_mac_rach_cfg(const asn1::rrc_nr::rach_cfg_common_s& asn1_type, rach_cfg_nr_t* rach_cfg_nr); +bool make_mac_phr_cfg_t(const asn1::rrc_nr::phr_cfg_s& asn1_type, phr_cfg_nr_t* phr_cfg_nr); +bool make_mac_dl_harq_cfg_nr_t(const asn1::rrc_nr::pdsch_serving_cell_cfg_s& asn1_type, + dl_harq_cfg_nr_t* out_dl_harq_cfg_nr); /*************************** * RLC Config **************************/ diff --git a/lib/include/srsran/common/interfaces_common.h b/lib/include/srsran/common/interfaces_common.h index b7be668ad..dbb167ac1 100644 --- a/lib/include/srsran/common/interfaces_common.h +++ b/lib/include/srsran/common/interfaces_common.h @@ -67,14 +67,6 @@ struct rf_args_t { std::array ch_tx_bands; }; -struct vnf_args_t { - std::string type; - std::string bind_addr; - uint16_t bind_port; - std::string log_level; - int log_hex_limit; -}; - class srsran_gw_config_t { public: @@ -89,8 +81,6 @@ public: virtual uint32_t read_pdu(uint32_t lcid, uint8_t* payload, uint32_t requested_bytes) = 0; }; -class stack_interface_phy_nr -{}; } // namespace srsran diff --git a/lib/include/srsran/interfaces/enb_interfaces.h b/lib/include/srsran/interfaces/enb_interfaces.h index 990e05aff..7b140cfdd 100644 --- a/lib/include/srsran/interfaces/enb_interfaces.h +++ b/lib/include/srsran/interfaces/enb_interfaces.h @@ -23,8 +23,6 @@ #include "srsran/common/interfaces_common.h" #include "srsran/srsran.h" -#include "srsran/interfaces/enb_rrc_interfaces.h" - #ifndef SRSRAN_ENB_INTERFACES_H #define SRSRAN_ENB_INTERFACES_H diff --git a/lib/include/srsran/interfaces/enb_rrc_interface_mac.h b/lib/include/srsran/interfaces/enb_rrc_interface_mac.h new file mode 100644 index 000000000..f8a806d90 --- /dev/null +++ b/lib/include/srsran/interfaces/enb_rrc_interface_mac.h @@ -0,0 +1,39 @@ +/** + * + * \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 SRSRAN_ENB_RRC_INTERFACE_MAC_H +#define SRSRAN_ENB_RRC_INTERFACE_MAC_H + +#include "srsenb/hdr/stack/mac/sched_interface.h" + +namespace srsenb { + +/// RRC interface for MAC +class rrc_interface_mac +{ +public: + /* Radio Link failure */ + virtual int add_user(uint16_t rnti, const sched_interface::ue_cfg_t& init_ue_cfg) = 0; + virtual void upd_user(uint16_t new_rnti, uint16_t old_rnti) = 0; + virtual void set_activity_user(uint16_t rnti) = 0; + virtual void set_radiolink_dl_state(uint16_t rnti, bool crc_res) = 0; + virtual void set_radiolink_ul_state(uint16_t rnti, bool crc_res) = 0; + virtual bool is_paging_opportunity(uint32_t tti_tx_dl, uint32_t* payload_len) = 0; + virtual void read_pdu_pcch(uint32_t tti_tx_dl, uint8_t* payload, uint32_t payload_size) = 0; + + ///< Provide packed SIB to MAC (buffer is managed by RRC) + virtual uint8_t* read_pdu_bcch_dlsch(const uint8_t enb_cc_idx, const uint32_t sib_index) = 0; +}; + +} // namespace srsenb + +#endif // SRSRAN_ENB_RRC_INTERFACE_MAC_H diff --git a/lib/include/srsran/interfaces/enb_rrc_interface_pdcp.h b/lib/include/srsran/interfaces/enb_rrc_interface_pdcp.h new file mode 100644 index 000000000..600c663a5 --- /dev/null +++ b/lib/include/srsran/interfaces/enb_rrc_interface_pdcp.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 SRSRAN_ENB_RRC_INTERFACE_PDCP_H +#define SRSRAN_ENB_RRC_INTERFACE_PDCP_H + +#include "srsran/common/byte_buffer.h" + +namespace srsenb { + +/// RRC interface for PDCP +class rrc_interface_pdcp +{ +public: + virtual void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) = 0; + virtual void notify_pdcp_integrity_error(uint16_t rnti, uint32_t lcid) = 0; +}; + +} // namespace srsenb + +#endif // SRSRAN_ENB_RRC_INTERFACE_PDCP_H diff --git a/lib/include/srsran/interfaces/enb_rrc_interface_rlc.h b/lib/include/srsran/interfaces/enb_rrc_interface_rlc.h new file mode 100644 index 000000000..baa5aca04 --- /dev/null +++ b/lib/include/srsran/interfaces/enb_rrc_interface_rlc.h @@ -0,0 +1,31 @@ +/** + * + * \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 SRSRAN_ENB_RRC_INTERFACE_RLC_H +#define SRSRAN_ENB_RRC_INTERFACE_RLC_H + +#include "srsran/common/byte_buffer.h" + +namespace srsenb { + +/// RRC interface for RLC +class rrc_interface_rlc +{ +public: + virtual void max_retx_attempted(uint16_t rnti) = 0; + virtual void protocol_failure(uint16_t rnti) = 0; + virtual void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t sdu) = 0; +}; + +} // namespace srsenb + +#endif // SRSRAN_ENB_RRC_INTERFACE_RLC_H diff --git a/lib/include/srsran/interfaces/enb_rrc_interfaces.h b/lib/include/srsran/interfaces/enb_rrc_interface_s1ap.h similarity index 75% rename from lib/include/srsran/interfaces/enb_rrc_interfaces.h rename to lib/include/srsran/interfaces/enb_rrc_interface_s1ap.h index dc58cda30..75e2f87e2 100644 --- a/lib/include/srsran/interfaces/enb_rrc_interfaces.h +++ b/lib/include/srsran/interfaces/enb_rrc_interface_s1ap.h @@ -19,7 +19,6 @@ * */ -#include "srsenb/hdr/stack/mac/sched_interface.h" #include "srsran/asn1/s1ap_utils.h" #include "srsran/interfaces/enb_rrc_interface_types.h" @@ -95,40 +94,6 @@ public: virtual void set_erab_status(uint16_t rnti, const asn1::s1ap::bearers_subject_to_status_transfer_list_l& erabs) = 0; }; -/// RRC interface for RLC -class rrc_interface_rlc -{ -public: - virtual void max_retx_attempted(uint16_t rnti) = 0; - virtual void protocol_failure(uint16_t rnti) = 0; - virtual void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t sdu) = 0; -}; - -/// RRC interface for MAC -class rrc_interface_mac -{ -public: - /* Radio Link failure */ - virtual int add_user(uint16_t rnti, const sched_interface::ue_cfg_t& init_ue_cfg) = 0; - virtual void upd_user(uint16_t new_rnti, uint16_t old_rnti) = 0; - virtual void set_activity_user(uint16_t rnti) = 0; - virtual void set_radiolink_dl_state(uint16_t rnti, bool crc_res) = 0; - virtual void set_radiolink_ul_state(uint16_t rnti, bool crc_res) = 0; - virtual bool is_paging_opportunity(uint32_t tti_tx_dl, uint32_t* payload_len) = 0; - virtual void read_pdu_pcch(uint32_t tti_tx_dl, uint8_t* payload, uint32_t payload_size) = 0; - - ///< Provide packed SIB to MAC (buffer is managed by RRC) - virtual uint8_t* read_pdu_bcch_dlsch(const uint8_t enb_cc_idx, const uint32_t sib_index) = 0; -}; - -/// RRC interface for PDCP -class rrc_interface_pdcp -{ -public: - virtual void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) = 0; - virtual void notify_pdcp_integrity_error(uint16_t rnti, uint32_t lcid) = 0; -}; - } // namespace srsenb #endif // SRSRAN_ENB_RRC_INTERFACES_H diff --git a/lib/include/srsran/interfaces/gnb_interfaces.h b/lib/include/srsran/interfaces/gnb_interfaces.h index 8309afd09..f5afce70f 100644 --- a/lib/include/srsran/interfaces/gnb_interfaces.h +++ b/lib/include/srsran/interfaces/gnb_interfaces.h @@ -28,9 +28,9 @@ #include "srsran/common/security.h" #include "srsran/interfaces/pdcp_interface_types.h" #include "srsran/interfaces/rlc_interface_types.h" -#include "srsran/interfaces/rrc_interface_types.h" // EUTRA interfaces that are used unmodified -#include "srsran/interfaces/enb_rrc_interfaces.h" +#include "srsran/interfaces/enb_rrc_interface_pdcp.h" +#include "srsran/interfaces/enb_rrc_interface_rlc.h" namespace srsenb { @@ -305,7 +305,7 @@ public: virtual void rach_detected(const rach_info_t& rach_info) = 0; }; -class stack_interface_phy_nr : public mac_interface_phy_nr, public srsran::stack_interface_phy_nr +class stack_interface_phy_nr : public mac_interface_phy_nr {}; } // namespace srsenb diff --git a/lib/include/srsran/interfaces/mac_interface_types.h b/lib/include/srsran/interfaces/mac_interface_types.h index a648ddaaf..a101c08d4 100644 --- a/lib/include/srsran/interfaces/mac_interface_types.h +++ b/lib/include/srsran/interfaces/mac_interface_types.h @@ -133,7 +133,7 @@ struct rach_cfg_t { }; // 38.321 5.1.1 Not complete yet -struct rach_nr_cfg_t { +struct rach_cfg_nr_t { uint32_t prach_ConfigurationIndex; int PreambleReceivedTargetPower; uint32_t preambleTransMax; @@ -141,7 +141,7 @@ struct rach_nr_cfg_t { uint32_t ra_responseWindow; uint32_t ra_ContentionResolutionTimer; - rach_nr_cfg_t() { reset(); } + rach_cfg_nr_t() { reset(); } void reset() { prach_ConfigurationIndex = 0; @@ -224,6 +224,33 @@ struct mac_cfg_t { int time_alignment_timer = -1; }; +struct mac_cfg_nr_t { + // Default constructor with default values as in 36.331 9.2.2 + mac_cfg_nr_t() { set_defaults(); } + + void set_defaults() + { + rach_cfg.reset(); + sr_cfg.reset(); + set_mac_main_cfg_default(); + } + + void set_mac_main_cfg_default() + { + bsr_cfg.reset(); + phr_cfg.reset(); + harq_cfg.reset(); + time_alignment_timer = -1; + } + + bsr_cfg_t bsr_cfg; + phr_cfg_nr_t phr_cfg; + sr_cfg_t sr_cfg; + rach_cfg_nr_t rach_cfg; + ul_harq_cfg_t harq_cfg; + int time_alignment_timer = -1; +}; + } // namespace srsran #endif // SRSRAN_MAC_INTERFACE_TYPES_H diff --git a/lib/include/srsran/interfaces/ue_nr_interfaces.h b/lib/include/srsran/interfaces/ue_nr_interfaces.h index 8bbbf1dc5..6edbd1543 100644 --- a/lib/include/srsran/interfaces/ue_nr_interfaces.h +++ b/lib/include/srsran/interfaces/ue_nr_interfaces.h @@ -36,8 +36,23 @@ class rrc_interface_phy_nr public: virtual void in_sync() = 0; virtual void out_of_sync() = 0; - virtual void run_tti(const uint32_t tti) = 0; virtual void set_phy_config_complete(bool status) = 0; + + /** + * @brief Describes a cell search result + */ + struct cell_search_result_t { + bool cell_found = false; + uint32_t pci = 0; ///< Physical Cell Identifier + srsran_pbch_msg_nr_t pbch_msg; ///< Packed PBCH message for the upper layers + srsran_csi_trs_measurements_t measurements = {}; ///< Measurements from SSB block + }; + + /** + * @brief Informs RRC about cell search process completion + * @param result Cell search result completion + */ + virtual void cell_search_found_cell(const cell_search_result_t& result) = 0; }; class mac_interface_phy_nr @@ -93,8 +108,6 @@ public: tb_ul_t tb; // only single TB in UL } tb_action_ul_t; - virtual int sf_indication(const uint32_t tti) = 0; ///< TODO: rename to slot indication - // Query the MAC for the current RNTI to look for struct sched_rnti_t { uint16_t id; @@ -162,7 +175,7 @@ public: 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 int set_config(const srsran::dl_harq_cfg_nr_t& dl_hrq_cfg) = 0; - virtual void set_config(const srsran::rach_nr_cfg_t& rach_cfg) = 0; + virtual void set_config(const srsran::rach_cfg_nr_t& rach_cfg_nr) = 0; virtual int add_tag_config(const srsran::tag_cfg_nr_t& tag_cfg) = 0; virtual int set_config(const srsran::phr_cfg_nr_t& phr_cfg) = 0; virtual int remove_tag_config(const uint32_t tag_id) = 0; @@ -218,20 +231,12 @@ struct phy_args_nr_t { class phy_interface_mac_nr { public: - typedef struct { - uint32_t tti; - uint32_t tb_len; - uint8_t* data; // always a pointer in our case - } tx_request_t; // MAC informs PHY about UL grant included in RAR PDU - virtual int set_ul_grant(uint32_t rar_slot_idx, - std::array packed_ul_grant, - uint16_t rnti, - srsran_rnti_type_t rnti_type) = 0; - - // MAC instructs PHY to transmit MAC TB at the given TTI - virtual int tx_request(const tx_request_t& request) = 0; + virtual int set_rar_grant(uint32_t rar_slot_idx, + std::array packed_ul_grant, + uint16_t rnti, + srsran_rnti_type_t rnti_type) = 0; /// Instruct PHY to send PRACH in the next occasion. virtual void send_prach(const uint32_t prach_occasion, @@ -256,13 +261,70 @@ class phy_interface_rrc_nr { public: virtual bool set_config(const srsran::phy_cfg_nr_t& cfg) = 0; + + /** + * @brief Describe the possible NR standalone physical layer possible states + */ + typedef enum { + PHY_NR_STATE_IDLE = 0, ///< There is no process going on + PHY_NR_STATE_CELL_SEARCH, ///< Cell search is currently in progress + PHY_NR_STATE_CELL_SELECT, ///< Cell selection is in progress or it is camped on a cell + PHY_NR_STATE_CAMPING + } phy_nr_state_t; + + /** + * @brief Retrieves the physical layer state + * @return + */ + virtual phy_nr_state_t get_state() = 0; + + /** + * @brief Stops the ongoing process and transitions to IDLE + */ + virtual void reset_nr() = 0; + + /** + * @brief Describes cell search arguments + */ + struct cell_search_args_t { + double srate_hz; + double center_freq_hz; + double ssb_freq_hz; + srsran_subcarrier_spacing_t ssb_scs; + srsran_ssb_patern_t ssb_pattern; + srsran_duplex_mode_t duplex_mode; + }; + + /** + * @brief Start cell search + * @param args Cell Search arguments + * @return true if the physical layer started successfully the cell search process + */ + virtual bool start_cell_search(const cell_search_args_t& req) = 0; + + /** + * @brief Describes cell select arguments + */ + struct cell_select_args_t { + srsran_ssb_cfg_t ssb_cfg; + srsran_carrier_nr_t carrier; + }; + + /** + * @brief Start cell search + * @param args Cell Search arguments + * @return true if the physical layer started successfully the cell search process + */ + virtual bool start_cell_select(const cell_select_args_t& req) = 0; }; // Combined interface for PHY to access stack (MAC and RRC) -class stack_interface_phy_nr : public mac_interface_phy_nr, - public rrc_interface_phy_nr, - public srsran::stack_interface_phy_nr -{}; +class stack_interface_phy_nr : public mac_interface_phy_nr, public rrc_interface_phy_nr +{ +public: + /* Indicate new TTI */ + virtual void run_tti(const uint32_t tti, const uint32_t tti_jump) = 0; +}; // Combined interface for stack (MAC and RRC) to access PHY class phy_interface_stack_nr : public phy_interface_mac_nr, public phy_interface_rrc_nr diff --git a/lib/include/srsran/interfaces/ue_phy_interfaces.h b/lib/include/srsran/interfaces/ue_phy_interfaces.h index e84196515..23f915bf9 100644 --- a/lib/include/srsran/interfaces/ue_phy_interfaces.h +++ b/lib/include/srsran/interfaces/ue_phy_interfaces.h @@ -60,7 +60,7 @@ struct phy_args_t { uint32_t nof_lte_carriers = 1; uint32_t nof_nr_carriers = 0; - uint32_t nr_max_nof_prb = 106; + uint32_t nr_max_nof_prb = 52; uint32_t nof_rx_ant = 1; std::string equalizer_mode = "mmse"; int cqi_max = 15; @@ -105,7 +105,6 @@ struct phy_args_t { srsran::channel::args_t dl_channel_args; srsran::channel::args_t ul_channel_args; - srsran::vnf_args_t vnf_args; }; /* RAT agnostic Interface MAC -> PHY */ diff --git a/lib/include/srsran/interfaces/ue_rrc_interfaces.h b/lib/include/srsran/interfaces/ue_rrc_interfaces.h index 80ce3a8de..d05aba602 100644 --- a/lib/include/srsran/interfaces/ue_rrc_interfaces.h +++ b/lib/include/srsran/interfaces/ue_rrc_interfaces.h @@ -25,6 +25,7 @@ #include "phy_interface_types.h" #include "rrc_interface_types.h" #include "srsran/asn1/asn1_utils.h" +#include "srsran/asn1/rrc_nr.h" #include "srsran/common/byte_buffer.h" #include "srsran/common/tti_point.h" @@ -108,19 +109,13 @@ public: class rrc_nr_interface_rrc { public: - virtual int get_eutra_nr_capabilities(srsran::byte_buffer_t* eutra_nr_caps) = 0; - virtual int get_nr_capabilities(srsran::byte_buffer_t* nr_cap) = 0; - virtual void phy_set_cells_to_meas(uint32_t carrier_freq_r15) = 0; - virtual void phy_meas_stop() = 0; - virtual bool rrc_reconfiguration(bool endc_release_and_add_r15, - bool nr_secondary_cell_group_cfg_r15_present, - asn1::dyn_octstring nr_secondary_cell_group_cfg_r15, - bool sk_counter_r15_present, - uint32_t sk_counter_r15, - bool nr_radio_bearer_cfg1_r15_present, - asn1::dyn_octstring nr_radio_bearer_cfg1_r15) = 0; - virtual void rrc_release() = 0; - virtual bool is_config_pending() = 0; + virtual int get_eutra_nr_capabilities(srsran::byte_buffer_t* eutra_nr_caps) = 0; + virtual int get_nr_capabilities(srsran::byte_buffer_t* nr_cap) = 0; + virtual void phy_set_cells_to_meas(uint32_t carrier_freq_r15) = 0; + virtual void phy_meas_stop() = 0; + virtual bool rrc_reconfiguration(bool endc_release_and_add_r15, const asn1::rrc_nr::rrc_recfg_s& rrc_nr_reconf) = 0; + virtual void rrc_release() = 0; + virtual bool is_config_pending() = 0; }; class rrc_nr_interface_nas_5g diff --git a/lib/include/srsran/mac/mac_sch_pdu_nr.h b/lib/include/srsran/mac/mac_sch_pdu_nr.h index c8bfa03e5..33e4262c2 100644 --- a/lib/include/srsran/mac/mac_sch_pdu_nr.h +++ b/lib/include/srsran/mac/mac_sch_pdu_nr.h @@ -107,6 +107,7 @@ public: static const uint8_t ue_con_res_id_len = 6; typedef std::array ue_con_res_id_t; ue_con_res_id_t get_ue_con_res_id_ce(); + uint64_t get_ue_con_res_id_ce_packed(); // setters void set_sdu(const uint32_t lcid_, const uint8_t* payload_, const uint32_t len_); diff --git a/lib/include/srsran/phy/common/phy_common_nr.h b/lib/include/srsran/phy/common/phy_common_nr.h index bfac21fe4..79ba0cfce 100644 --- a/lib/include/srsran/phy/common/phy_common_nr.h +++ b/lib/include/srsran/phy/common/phy_common_nr.h @@ -396,8 +396,9 @@ typedef struct SRSRAN_API { #define SRSRAN_DEFAULT_CARRIER_NR \ { \ - .pci = 500, .dl_center_frequency_hz = 3.5e9, .ul_center_frequency_hz = 3.5e9, .ssb_center_freq_hz = 3.5e9, \ - .offset_to_carrier = 0, .scs = srsran_subcarrier_spacing_15kHz, .nof_prb = 52, .start = 0, .max_mimo_layers = 1 \ + .pci = 500, .dl_center_frequency_hz = 117000 * 30e3, .ul_center_frequency_hz = 117000 * 30e3, \ + .ssb_center_freq_hz = 3.5e9, .offset_to_carrier = 0, .scs = srsran_subcarrier_spacing_15kHz, .nof_prb = 52, \ + .start = 0, .max_mimo_layers = 1 \ } /** diff --git a/lib/include/srsran/phy/sync/ssb.h b/lib/include/srsran/phy/sync/ssb.h index d640ff898..a98cef6fd 100644 --- a/lib/include/srsran/phy/sync/ssb.h +++ b/lib/include/srsran/phy/sync/ssb.h @@ -125,9 +125,10 @@ typedef struct SRSRAN_API { * @note if pbch.crc is true, SSB transmission is found and decoded. Otherwise, no SSB transmission has been decoded */ typedef struct { - uint32_t N_id; ///< Most suitable physical cell identifier - uint32_t t_offset; ///< Time offset in the input samples - srsran_pbch_msg_nr_t pbch_msg; ///< Physical broadcast channel message of the most suitable SSB candidate + uint32_t N_id; ///< Most suitable physical cell identifier + uint32_t t_offset; ///< Time offset in the input samples + srsran_pbch_msg_nr_t pbch_msg; ///< Physical broadcast channel message of the most suitable SSB candidate + srsran_csi_trs_measurements_t measurements; ///< Measurements } srsran_ssb_search_res_t; /** diff --git a/lib/include/srsran/rlc/rlc_am_nr.h b/lib/include/srsran/rlc/rlc_am_nr.h index 2e1f9587d..ce4b5db45 100644 --- a/lib/include/srsran/rlc/rlc_am_nr.h +++ b/lib/include/srsran/rlc/rlc_am_nr.h @@ -109,10 +109,15 @@ public: void stop() final; + bool inside_tx_window(uint32_t sn); + private: rlc_am* parent = nullptr; rlc_am_nr_rx* rx = nullptr; + uint32_t mod_nr = 4096; + inline uint32_t tx_mod_base_nr(uint32_t sn) const; + /**************************************************************************** * Configurable parameters * Ref: 3GPP TS 38.322 v16.2.0 Section 7.4 @@ -131,10 +136,39 @@ private: public: // Getters/Setters + void set_tx_state(const rlc_am_nr_tx_state_t& st_) { st = st_; } // This should only be used for testing. rlc_am_nr_tx_state_t get_tx_state() { return st; } // This should only be used for testing. uint32_t get_tx_window_size() { return tx_window.size(); } // This should only be used for testing. }; +/**************************************************************************** + * RX State Variables + * Ref: 3GPP TS 38.322 v16.2.0 Section 7.1 + ***************************************************************************/ +struct rlc_am_nr_rx_state_t { + /* + * RX_Next: This state variable holds the value of the SN following the last in-sequence completely received RLC + * SDU, and it serves as the lower edge of the receiving window. It is initially set to 0, and is updated whenever + * the AM RLC entity receives an RLC SDU with SN = RX_Next. + */ + uint32_t rx_next = 0; + /* + * RX_Next_Status_Trigger: This state variable holds the value of the SN following the SN of the RLC SDU which + * triggered t-Reassembly. + */ + uint32_t rx_next_status_trigger = 0; + /* + * RX_Next_Highest: This state variable holds the highest possible value of the SN which can be indicated by + *"ACK_SN" when a STATUS PDU needs to be constructed. It is initially set to 0. + */ + uint32_t rx_highest_status = 0; + /* + * RX_Next_Highest: This state variable holds the value of the SN following the SN of the RLC SDU with the + * highest SN among received RLC SDUs. It is initially set to 0. + */ + uint32_t rx_next_highest = 0; +}; + // Receiver sub-class class rlc_am_nr_rx : public rlc_am::rlc_am_base_rx { @@ -175,38 +209,15 @@ private: rlc_am_nr_tx* tx = nullptr; byte_buffer_pool* pool = nullptr; + uint32_t mod_nr = 4096; + uint32_t rx_mod_base_nr(uint32_t sn) const; + // RX Window rlc_ringbuffer_t rx_window; // Mutexes std::mutex mutex; - /**************************************************************************** - * State Variables - * Ref: 3GPP TS 38.322 v16.2.0 Section 7.1 - ***************************************************************************/ - /* - * RX_Next: This state variable holds the value of the SN following the last in-sequence completely received RLC - * SDU, and it serves as the lower edge of the receiving window. It is initially set to 0, and is updated whenever - * the AM RLC entity receives an RLC SDU with SN = RX_Next. - */ - uint32_t rx_next = 0; - /* - * RX_Next_Status_Trigger: This state variable holds the value of the SN following the SN of the RLC SDU which - * triggered t-Reassembly. - */ - uint32_t rx_next_status_trigger = 0; - /* - * RX_Next_Highest: This state variable holds the highest possible value of the SN which can be indicated by - *"ACK_SN" when a STATUS PDU needs to be constructed. It is initially set to 0. - */ - uint32_t rx_highest_status = 0; - /* - * RX_Next_Highest: This state variable holds the value of the SN following the SN of the RLC SDU with the - * highest SN among received RLC SDUs. It is initially set to 0. - */ - uint32_t rx_next_highest = 0; - /**************************************************************************** * Rx timers * Ref: 3GPP TS 38.322 v16.2.0 Section 7.3 @@ -219,6 +230,18 @@ private: * Ref: 3GPP TS 38.322 v16.2.0 Section 7.4 ***************************************************************************/ rlc_am_nr_config_t cfg = {}; + + /**************************************************************************** + * Tx state variables + * Ref: 3GPP TS 38.322 v16.2.0 Section 7.1 + ***************************************************************************/ + struct rlc_am_nr_rx_state_t st = {}; + +public: + // Getters/Setters + void set_rx_state(const rlc_am_nr_rx_state_t& st_) { st = st_; } // This should only be used for testing. + rlc_am_nr_rx_state_t get_rx_state() { return st; } // This should only be used for testing. + uint32_t get_rx_window_size() { return rx_window.size(); } // This should only be used for testing. }; } // namespace srsran diff --git a/lib/include/srsran/srsran.h b/lib/include/srsran/srsran.h index 912e44f11..52c507be3 100644 --- a/lib/include/srsran/srsran.h +++ b/lib/include/srsran/srsran.h @@ -122,6 +122,7 @@ extern "C" { #include "srsran/phy/ue/ue_dl_nr.h" #include "srsran/phy/ue/ue_mib.h" #include "srsran/phy/ue/ue_sync.h" +#include "srsran/phy/ue/ue_sync_nr.h" #include "srsran/phy/ue/ue_ul.h" #include "srsran/phy/ue/ue_ul_nr.h" diff --git a/lib/include/srsran/support/srsran_assert.h b/lib/include/srsran/support/srsran_assert.h index 3e27f1608..e9436fb16 100644 --- a/lib/include/srsran/support/srsran_assert.h +++ b/lib/include/srsran/support/srsran_assert.h @@ -65,6 +65,15 @@ */ #define srsran_assert(condition, fmt, ...) srsran_assert_ifdef(ASSERTS_ENABLED, condition, fmt, ##__VA_ARGS__) +/** + * Specialization of "srsran_assert_ifdef" for the SANITY_CHECKS_ENABLED flag + */ +#ifndef NDEBUG +#define SANITY_CHECKS_ENABLED +#endif +#define srsran_sanity_check(condition, fmt, ...) \ + srsran_assert_ifdef(SANITY_CHECKS_ENABLED, condition, fmt, ##__VA_ARGS__) + #ifdef STOP_ON_WARNING #define srsran_expect(condition, fmt, ...) srsran_assert(condition, fmt, ##__VA_ARGS__) diff --git a/lib/src/asn1/asn1_utils.cc b/lib/src/asn1/asn1_utils.cc index aab688740..20dfdb89d 100644 --- a/lib/src/asn1/asn1_utils.cc +++ b/lib/src/asn1/asn1_utils.cc @@ -153,7 +153,7 @@ SRSASN_CODE bit_ref::pack(uint64_t val, uint32_t n_bits) uint64_t mask; while (n_bits > 0) { if (ptr >= max_ptr) { - log_error("Buffer size limit was achieved"); + log_error("pack: Buffer size limit was achieved"); return SRSASN_ERROR_ENCODE_FAIL; } mask = ((1ul << n_bits) - 1ul); @@ -185,7 +185,7 @@ SRSASN_CODE unpack_bits(T& val, Ptr& ptr, uint8_t& offset, const uint8_t* max_pt val = 0; while (n_bits > 0) { if (ptr >= max_ptr) { - log_error("Buffer size limit was achieved"); + log_error("unpack_bits: Buffer size limit was achieved"); return SRSASN_ERROR_DECODE_FAIL; } if ((uint32_t)(8 - offset) > n_bits) { @@ -205,35 +205,35 @@ SRSASN_CODE unpack_bits(T& val, Ptr& ptr, uint8_t& offset, const uint8_t* max_pt } template SRSASN_CODE - unpack_bits(bool& val, uint8_t*& ptr, uint8_t& offset, const uint8_t* max_ptr, uint32_t n_bits); +unpack_bits(bool& val, uint8_t*& ptr, uint8_t& offset, const uint8_t* max_ptr, uint32_t n_bits); template SRSASN_CODE unpack_bits(bool& val, const uint8_t*& ptr, uint8_t& offset, const uint8_t* max_ptr, uint32_t n_bits); template SRSASN_CODE - unpack_bits(uint8_t& val, uint8_t*& ptr, uint8_t& offset, const uint8_t* max_ptr, uint32_t n_bits); +unpack_bits(uint8_t& val, uint8_t*& ptr, uint8_t& offset, const uint8_t* max_ptr, uint32_t n_bits); template SRSASN_CODE unpack_bits(uint8_t& val, const uint8_t*& ptr, uint8_t& offset, const uint8_t* max_ptr, uint32_t n_bits); template SRSASN_CODE - unpack_bits(uint16_t& val, uint8_t*& ptr, uint8_t& offset, const uint8_t* max_ptr, uint32_t n_bits); +unpack_bits(uint16_t& val, uint8_t*& ptr, uint8_t& offset, const uint8_t* max_ptr, uint32_t n_bits); template SRSASN_CODE unpack_bits(uint16_t& val, const uint8_t*& ptr, uint8_t& offset, const uint8_t* max_ptr, uint32_t n_bits); template SRSASN_CODE - unpack_bits(uint32_t& val, uint8_t*& ptr, uint8_t& offset, const uint8_t* max_ptr, uint32_t n_bits); +unpack_bits(uint32_t& val, uint8_t*& ptr, uint8_t& offset, const uint8_t* max_ptr, uint32_t n_bits); template SRSASN_CODE unpack_bits(uint32_t& val, const uint8_t*& ptr, uint8_t& offset, const uint8_t* max_ptr, uint32_t n_bits); template SRSASN_CODE - unpack_bits(uint64_t& val, uint8_t*& ptr, uint8_t& offset, const uint8_t* max_ptr, uint32_t n_bits); +unpack_bits(uint64_t& val, uint8_t*& ptr, uint8_t& offset, const uint8_t* max_ptr, uint32_t n_bits); template SRSASN_CODE unpack_bits(uint64_t& val, const uint8_t*& ptr, uint8_t& offset, @@ -249,7 +249,7 @@ SRSASN_CODE bit_ref_impl::unpack_bytes(uint8_t* buf, uint32_t n_bytes) if (offset == 0) { // Aligned case if (ptr + n_bytes > max_ptr) { - log_error("Buffer size limit was achieved"); + log_error("unpack_bytes (aligned): Buffer size limit was achieved"); return SRSASN_ERROR_DECODE_FAIL; } memcpy(buf, ptr, n_bytes); @@ -257,7 +257,7 @@ SRSASN_CODE bit_ref_impl::unpack_bytes(uint8_t* buf, uint32_t n_bytes) } else { // Unaligned case if (ptr + n_bytes >= max_ptr) { - log_error("Buffer size limit was achieved"); + log_error("unpack_bytes (unaligned): Buffer size limit was achieved"); return SRSASN_ERROR_DECODE_FAIL; } for (uint32_t i = 0; i < n_bytes; ++i) { @@ -273,7 +273,7 @@ SRSASN_CODE bit_ref_impl::align_bytes() if (offset == 0) return SRSASN_SUCCESS; if (ptr >= max_ptr) { - log_error("Buffer size limit was achieved"); + log_error("align_bytes: Buffer size limit was achieved"); return SRSASN_ERROR_DECODE_FAIL; } offset = 0; @@ -289,7 +289,7 @@ SRSASN_CODE bit_ref_impl::advance_bits(uint32_t n_bits) uint32_t bytes_offset = floorf((offset + n_bits) / 8.0f); if (ptr + bytes_required > max_ptr) { - log_error("Buffer size limit was achieved"); + log_error("advance_bytes: Buffer size limit was achieved"); return SRSASN_ERROR_DECODE_FAIL; } ptr += bytes_offset; @@ -315,7 +315,7 @@ SRSASN_CODE bit_ref::pack_bytes(const uint8_t* buf, uint32_t n_bytes) return SRSASN_SUCCESS; } if (ptr + n_bytes >= max_ptr) { - log_error("Buffer size limit was achieved"); + log_error("pack_bytes: Buffer size limit was achieved"); return SRSASN_ERROR_ENCODE_FAIL; } if (offset == 0) { @@ -335,7 +335,7 @@ SRSASN_CODE bit_ref::align_bytes_zero() if (offset == 0) return SRSASN_SUCCESS; if (ptr >= max_ptr) { - log_error("Buffer size limit was achieved"); + log_error("align_bytes_zero: Buffer size limit was achieved"); return SRSASN_ERROR_ENCODE_FAIL; } auto mask = static_cast(256u - (1u << (8u - offset))); @@ -849,7 +849,7 @@ pack_integer(bit_ref& bref, uint16_t n, uint16_t lb, uint16_t ub, bool template SRSASN_CODE pack_integer(bit_ref& bref, uint32_t n, uint32_t lb, uint32_t ub, bool has_ext, bool aligned); template SRSASN_CODE - pack_integer(bit_ref& bref, uint64_t n, uint64_t lb, uint64_t ub, bool has_ext, bool aligned); +pack_integer(bit_ref& bref, uint64_t n, uint64_t lb, uint64_t ub, bool has_ext, bool aligned); template SRSASN_CODE pack_integer(bit_ref& bref, int8_t n, int8_t lb, int8_t ub, bool has_ext, bool aligned); template SRSASN_CODE pack_integer(bit_ref& bref, int16_t n, int16_t lb, int16_t ub, bool has_ext, bool aligned); diff --git a/lib/src/asn1/rrc_nr_utils.cc b/lib/src/asn1/rrc_nr_utils.cc index 1d4e01c7e..a9de80344 100644 --- a/lib/src/asn1/rrc_nr_utils.cc +++ b/lib/src/asn1/rrc_nr_utils.cc @@ -99,16 +99,14 @@ bool make_mac_phr_cfg_t(const phr_cfg_s& asn1_type, phr_cfg_nr_t* phr_cfg_nr) return true; } -rach_nr_cfg_t make_mac_rach_cfg(const rach_cfg_common_s& asn1_type) +void make_mac_rach_cfg(const rach_cfg_common_s& asn1_type, rach_cfg_nr_t* rach_cfg_nr) { - rach_nr_cfg_t rach_nr_cfg = {}; - rach_nr_cfg.powerRampingStep = asn1_type.rach_cfg_generic.pwr_ramp_step.to_number(); - rach_nr_cfg.ra_responseWindow = asn1_type.rach_cfg_generic.ra_resp_win.to_number(); - rach_nr_cfg.prach_ConfigurationIndex = asn1_type.rach_cfg_generic.prach_cfg_idx; - rach_nr_cfg.PreambleReceivedTargetPower = asn1_type.rach_cfg_generic.preamb_rx_target_pwr; - rach_nr_cfg.preambleTransMax = asn1_type.rach_cfg_generic.preamb_trans_max.to_number(); - rach_nr_cfg.ra_ContentionResolutionTimer = asn1_type.ra_contention_resolution_timer.to_number(); - return rach_nr_cfg; + rach_cfg_nr->powerRampingStep = asn1_type.rach_cfg_generic.pwr_ramp_step.to_number(); + rach_cfg_nr->ra_responseWindow = asn1_type.rach_cfg_generic.ra_resp_win.to_number(); + rach_cfg_nr->prach_ConfigurationIndex = asn1_type.rach_cfg_generic.prach_cfg_idx; + rach_cfg_nr->PreambleReceivedTargetPower = asn1_type.rach_cfg_generic.preamb_rx_target_pwr; + rach_cfg_nr->preambleTransMax = asn1_type.rach_cfg_generic.preamb_trans_max.to_number(); + rach_cfg_nr->ra_ContentionResolutionTimer = asn1_type.ra_contention_resolution_timer.to_number(); }; int make_rlc_config_t(const rlc_cfg_c& asn1_type, uint8_t bearer_id, rlc_config_t* cfg_out) @@ -1633,7 +1631,7 @@ bool fill_phy_pdcch_cfg(const asn1::rrc_nr::pdcch_cfg_s& pdcch_cfg, srsran_pdcch return true; } -bool fill_phy_pdcch_cfg_common(const asn1::rrc_nr::pdcch_cfg_common_s& pdcch_cfg, srsran_pdcch_cfg_nr_t* pdcch) +void fill_phy_pdcch_cfg_common(const asn1::rrc_nr::pdcch_cfg_common_s& pdcch_cfg, srsran_pdcch_cfg_nr_t* pdcch) { if (pdcch_cfg.common_ctrl_res_set_present) { pdcch->coreset_present[pdcch_cfg.common_ctrl_res_set.ctrl_res_set_id] = true; @@ -1649,10 +1647,91 @@ bool fill_phy_pdcch_cfg_common(const asn1::rrc_nr::pdcch_cfg_common_s& pdcch_cfg } } } +} +void fill_phy_pucch_cfg_common(const asn1::rrc_nr::pucch_cfg_common_s& pucch_cfg, srsran_pucch_nr_common_cfg_t* pucch) +{ + if (pucch_cfg.pucch_res_common_present) { + pucch->resource_common = pucch_cfg.pucch_res_common; + } + if (pucch_cfg.hop_id_present) { + pucch->hopping_id_present = true; + pucch->hopping_id = pucch_cfg.hop_id; + } + if (pucch_cfg.p0_nominal_present) { + pucch->p0_nominal = pucch_cfg.p0_nominal; + } + + switch (pucch_cfg.pucch_group_hop) { + case pucch_cfg_common_s::pucch_group_hop_opts::enable: + pucch->group_hopping = SRSRAN_PUCCH_NR_GROUP_HOPPING_ENABLE; + break; + case pucch_cfg_common_s::pucch_group_hop_opts::disable: + pucch->group_hopping = SRSRAN_PUCCH_NR_GROUP_HOPPING_DISABLE; + break; + default: + pucch->group_hopping = SRSRAN_PUCCH_NR_GROUP_HOPPING_NEITHER; + break; + } +} + +bool fill_phy_pdsch_cfg_common(const asn1::rrc_nr::pdsch_cfg_common_s& pdsch_cfg, srsran_sch_hl_cfg_nr_t* pdsch) +{ + for (uint32_t i = 0; i < pdsch_cfg.pdsch_time_domain_alloc_list.size(); i++) { + srsran_sch_time_ra_t common_time_ra; + if (make_phy_common_time_ra(pdsch_cfg.pdsch_time_domain_alloc_list[i], &common_time_ra) == true) { + pdsch->common_time_ra[i] = common_time_ra; + pdsch->nof_common_time_ra = i + 1; + } else { + asn1::log_warning("Warning while building common_time_ra structure"); + return false; + } + } return true; } +bool fill_phy_pusch_cfg_common(const asn1::rrc_nr::pusch_cfg_common_s& pusch_cfg, srsran_sch_hl_cfg_nr_t* pusch) +{ + for (uint32_t i = 0; i < pusch_cfg.pusch_time_domain_alloc_list.size(); i++) { + srsran_sch_time_ra_t common_time_ra; + if (make_phy_common_time_ra(pusch_cfg.pusch_time_domain_alloc_list[i], &common_time_ra) == true) { + pusch->common_time_ra[i] = common_time_ra; + pusch->nof_common_time_ra = i + 1; + } else { + asn1::log_warning("Warning while building common_time_ra structure"); + return false; + } + } + return true; +} + +void fill_phy_carrier_cfg(const asn1::rrc_nr::serving_cell_cfg_common_sib_s& serv_cell_cfg, + srsran_carrier_nr_t* out_carrier_nr) +{ + // TODO: Currently ony one carrier is supported + auto& freq_info_dl = serv_cell_cfg.dl_cfg_common.freq_info_dl; + out_carrier_nr->offset_to_carrier = freq_info_dl.scs_specific_carrier_list[0].offset_to_carrier; + out_carrier_nr->scs = make_subcarrier_spacing(freq_info_dl.scs_specific_carrier_list[0].subcarrier_spacing); + out_carrier_nr->nof_prb = freq_info_dl.scs_specific_carrier_list[0].carrier_bw; + + auto& freq_info_ul = serv_cell_cfg.ul_cfg_common.freq_info_ul; + srsran::srsran_band_helper bands; + out_carrier_nr->ul_center_frequency_hz = bands.get_center_freq_from_abs_freq_point_a( + freq_info_ul.scs_specific_carrier_list[0].carrier_bw, freq_info_ul.absolute_freq_point_a); +} + +void fill_phy_ssb_cfg(const asn1::rrc_nr::serving_cell_cfg_common_sib_s& serv_cell_cfg, + phy_cfg_nr_t::ssb_cfg_t* out_ssb) +{ + out_ssb->periodicity_ms = serv_cell_cfg.ssb_periodicity_serving_cell.to_number(); + + if (serv_cell_cfg.ssb_positions_in_burst.group_presence_present) { + make_ssb_positions_in_burst(serv_cell_cfg.ssb_positions_in_burst.group_presence, out_ssb->position_in_burst); + } else { + make_ssb_positions_in_burst(serv_cell_cfg.ssb_positions_in_burst.in_one_group, out_ssb->position_in_burst); + } +} + /************************** * Asn1 Obj Id *************************/ diff --git a/lib/src/common/phy_cfg_nr.cc b/lib/src/common/phy_cfg_nr.cc index d3c149b58..aa3195d62 100644 --- a/lib/src/common/phy_cfg_nr.cc +++ b/lib/src/common/phy_cfg_nr.cc @@ -26,7 +26,6 @@ namespace srsran { srsran_dci_cfg_nr_t phy_cfg_nr_t::get_dci_cfg() const - { srsran_dci_cfg_nr_t dci_cfg = {}; diff --git a/lib/src/common/phy_cfg_nr_default.cc b/lib/src/common/phy_cfg_nr_default.cc index ec00b2525..a48cae949 100644 --- a/lib/src/common/phy_cfg_nr_default.cc +++ b/lib/src/common/phy_cfg_nr_default.cc @@ -181,6 +181,9 @@ void phy_cfg_nr_default_t::make_pdsch_default(srsran_sch_hl_cfg_nr_t& pdsch) pdsch.common_time_ra[0].sliv = srsran_ra_type2_to_riv(SRSRAN_NSYMB_PER_SLOT_NR - 1, 1, SRSRAN_NSYMB_PER_SLOT_NR); pdsch.nof_common_time_ra = 1; + // Set contiguous PRBs as default + pdsch.alloc = srsran_resource_alloc_type1; + // Setup PDSCH DMRS type A position pdsch.typeA_pos = srsran_dmrs_sch_typeA_pos_2; } diff --git a/lib/src/mac/mac_sch_pdu_nr.cc b/lib/src/mac/mac_sch_pdu_nr.cc index 81ec7aad8..6ee052720 100644 --- a/lib/src/mac/mac_sch_pdu_nr.cc +++ b/lib/src/mac/mac_sch_pdu_nr.cc @@ -341,6 +341,17 @@ mac_sch_subpdu_nr::ue_con_res_id_t mac_sch_subpdu_nr::get_ue_con_res_id_ce() return id; } +uint64_t mac_sch_subpdu_nr::get_ue_con_res_id_ce_packed() +{ + if (!parent->is_ulsch() && lcid == CON_RES_ID) { + const uint8_t* payload = sdu.ptr(); + return le64toh(((uint64_t)payload[5]) | (((uint64_t)payload[4]) << 8) | (((uint64_t)payload[3]) << 16) | + (((uint64_t)payload[2]) << 24) | (((uint64_t)payload[1]) << 32) | (((uint64_t)payload[0]) << 40)); + } else { + return 0; + } +} + uint32_t mac_sch_subpdu_nr::sizeof_ce(uint32_t lcid, bool is_ul) { if (is_ul) { diff --git a/lib/src/phy/channel/test/awgn_channel_test.c b/lib/src/phy/channel/test/awgn_channel_test.c index c013dc2fd..4a7f43619 100644 --- a/lib/src/phy/channel/test/awgn_channel_test.c +++ b/lib/src/phy/channel/test/awgn_channel_test.c @@ -286,6 +286,9 @@ clean_exit: if (output_buffer) { free(output_buffer); } + if (help_buffer) { + free(help_buffer); + } #ifdef ENABLE_GUI if (fft_out) { diff --git a/lib/src/phy/sync/ssb.c b/lib/src/phy/sync/ssb.c index d01f3157c..33c084c24 100644 --- a/lib/src/phy/sync/ssb.c +++ b/lib/src/phy/sync/ssb.c @@ -1130,10 +1130,17 @@ int srsran_ssb_search(srsran_ssb_t* q, const cf_t* in, uint32_t nof_samples, srs return SRSRAN_ERROR; } + srsran_csi_trs_measurements_t measurements = {}; + if (ssb_measure(q, ssb_grid, N_id, &measurements) < SRSRAN_SUCCESS) { + ERROR("Error measuring"); + return SRSRAN_ERROR; + } + // Save result - res->N_id = N_id; - res->t_offset = t_offset; - res->pbch_msg = pbch_msg; + res->N_id = N_id; + res->t_offset = t_offset; + res->pbch_msg = pbch_msg; + res->measurements = measurements; return SRSRAN_SUCCESS; } diff --git a/lib/src/phy/ue/test/gen_ack_test.c b/lib/src/phy/ue/test/gen_ack_test.c index dc24ba043..f86a160d5 100644 --- a/lib/src/phy/ue/test/gen_ack_test.c +++ b/lib/src/phy/ue/test/gen_ack_test.c @@ -47,7 +47,7 @@ int fdd_tests(uint32_t max_cc) ue_dl.cell.frame_type = SRSRAN_FDD; for (uint32_t nof_cc = 1; nof_cc <= max_cc; nof_cc++) { - for (uint32_t nof_tb = 1; nof_tb <= SRSRAN_MAX_CODEWORDS; nof_tb++) { + for (uint8_t nof_tb = 1; nof_tb <= SRSRAN_MAX_CODEWORDS; nof_tb++) { for (uint32_t nof_active_cc = 1; nof_active_cc <= nof_cc; nof_active_cc++) { for (uint32_t nof_active_tb = 1; nof_active_tb <= nof_tb; nof_active_tb++) { srsran_pdsch_ack_t ack_info = {}; @@ -62,7 +62,7 @@ int fdd_tests(uint32_t max_cc) ack_info.cc[cc_idx].m[0].present = cc_idx < nof_active_cc; ack_info.cc[cc_idx].m[0].resource.n_cce = cc_idx + 1; if (ack_info.cc[cc_idx].m[0].present) { - for (uint32_t j = 0; j < nof_tb; j++) { + for (uint8_t j = 0; j < nof_tb; j++) { ack_info.cc[cc_idx].m[0].value[j] = j < nof_active_tb ? 1 : 2; } } else { diff --git a/lib/src/phy/ue/ue_ul_nr.c b/lib/src/phy/ue/ue_ul_nr.c index 437074ac0..e49509b96 100644 --- a/lib/src/phy/ue/ue_ul_nr.c +++ b/lib/src/phy/ue/ue_ul_nr.c @@ -142,9 +142,12 @@ int srsran_ue_ul_nr_encode_pusch(srsran_ue_ul_nr_t* q, // Generate signal srsran_ofdm_tx_sf(&q->ifft); - // Scale iFFT output to compensate for iFFT amplification (due to FFTW implementation). - float scaling = 1 / sqrtf(q->ifft.cfg.symbol_sz); - srsran_vec_sc_prod_cfc(q->ifft.cfg.out_buffer, scaling, q->ifft.cfg.out_buffer, q->ifft.sf_sz); + // Normalise to peak + uint32_t max_idx = srsran_vec_max_abs_ci(q->ifft.cfg.out_buffer, q->ifft.sf_sz); + float max_peak = cabsf(q->ifft.cfg.out_buffer[max_idx]); + if (isnormal(max_peak)) { + srsran_vec_sc_prod_cfc(q->ifft.cfg.out_buffer, 0.99f / max_peak, q->ifft.cfg.out_buffer, q->ifft.sf_sz); + } // Apply frequency offset if (isnormal(q->freq_offset_hz)) { diff --git a/lib/src/rlc/rlc_am_lte.cc b/lib/src/rlc/rlc_am_lte.cc index 5c485f92e..0b9b35475 100644 --- a/lib/src/rlc/rlc_am_lte.cc +++ b/lib/src/rlc/rlc_am_lte.cc @@ -402,23 +402,28 @@ void rlc_am_lte_tx::retransmit_pdu(uint32_t sn) bool rlc_am_lte_tx::poll_required() { if (cfg.poll_pdu > 0 && pdu_without_poll > static_cast(cfg.poll_pdu)) { + logger->debug("Poll required. Cause: PDU_WITHOUT_POLL > pollPdu."); return true; } if (cfg.poll_byte > 0 && byte_without_poll > static_cast(cfg.poll_byte)) { + logger->debug("Poll required. Cause: BYTE_WITHOUT_POLL > pollByte."); return true; } if (poll_retx_timer.is_valid() && poll_retx_timer.is_expired()) { // re-arming of timer is handled by caller + logger->debug("Poll required. Cause: t-PollRetransmission expired."); return true; } if (tx_window.size() >= RLC_AM_WINDOW_SIZE) { + logger->debug("Poll required. Cause: TX window full."); return true; } if (tx_sdu_queue.size() == 0 && retx_queue.empty()) { + logger->debug("Poll required. Cause: Empty TX and ReTX queues."); return true; } @@ -514,6 +519,7 @@ int rlc_am_lte_tx::build_retx_pdu(uint8_t* payload, uint32_t nof_bytes) byte_without_poll = 0; if (poll_retx_timer.is_valid()) { // re-arm timer (will be stopped when status PDU is received) + logger->debug("%s re-arming retx timer", RB_NAME); poll_retx_timer.run(); } } @@ -565,17 +571,7 @@ int rlc_am_lte_tx::build_segment(uint8_t* payload, uint32_t nof_bytes, rlc_amd_r new_header.lsf = 0; new_header.so = retx.so_start; new_header.N_li = 0; - new_header.p = 0; - if (poll_required()) { - logger->debug("%s setting poll bit to request status", RB_NAME); - new_header.p = 1; - // vt_s won't change for reTx, so don't update poll_sn - pdu_without_poll = 0; - byte_without_poll = 0; - if (poll_retx_timer.is_valid()) { - poll_retx_timer.run(); - } - } + new_header.p = 0; // Poll Requriments are done later after updating RETX queue uint32_t head_len = 0; uint32_t pdu_space = 0; @@ -681,6 +677,18 @@ int rlc_am_lte_tx::build_segment(uint8_t* payload, uint32_t nof_bytes, rlc_amd_r retx_queue.front().so_start = retx.so_end; } + // Check POLL requeriments for segment + if (poll_required()) { + logger->debug("%s setting poll bit to request status", RB_NAME); + new_header.p = 1; + // vt_s won't change for reTx, so don't update poll_sn + pdu_without_poll = 0; + byte_without_poll = 0; + if (poll_retx_timer.is_valid()) { + poll_retx_timer.run(); + } + } + // Write header and pdu uint8_t* ptr = payload; rlc_am_write_data_pdu_header(&new_header, &ptr); diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index aac68b851..23c3c029a 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -29,9 +29,6 @@ #include #define RLC_AM_NR_WINDOW_SIZE 2048 -#define MOD_NR 4096 -#define RX_MOD_BASE_NR(x) (((x)-rx_next) % MOD_NR) -//#define TX_MOD_BASE_NR(x) (((x)-vt_a) % MOD_NR) namespace srsran { @@ -61,6 +58,9 @@ bool rlc_am_nr_tx::configure(const rlc_config_t& cfg_) return false; } */ + + mod_nr = (cfg.tx_sn_field_length == rlc_am_nr_sn_size_t::size12bits) ? 4096 : 262144; + tx_enabled = true; return true; @@ -390,6 +390,11 @@ uint8_t rlc_am_nr_tx::get_pdu_poll() return poll; } +bool rlc_am_nr_tx::do_status() +{ + return rx->get_do_status(); +} + void rlc_am_nr_tx::reestablish() { stop(); @@ -404,12 +409,20 @@ bool rlc_am_nr_tx::sdu_queue_is_full() void rlc_am_nr_tx::empty_queue() {} -bool rlc_am_nr_tx::do_status() +void rlc_am_nr_tx::stop() {} +/* + * Window helpers + */ +uint32_t rlc_am_nr_tx::tx_mod_base_nr(uint32_t sn) const { - return rx->get_do_status(); + return (sn - st.tx_next_ack) % mod_nr; } -void rlc_am_nr_tx::stop() {} +bool rlc_am_nr_tx::inside_tx_window(uint32_t sn) +{ + // TX_Next_Ack <= SN < TX_Next_Ack + AM_Window_Size + return tx_mod_base_nr(sn) < RLC_AM_NR_WINDOW_SIZE; +} /**************************************************************************** * Rx subclass implementation @@ -438,6 +451,7 @@ bool rlc_am_nr_rx::configure(const rlc_config_t& cfg_) logger->info("Configured reassembly timer. t-Reassembly=%d ms", cfg.t_reassembly); } + mod_nr = (cfg.rx_sn_field_length == rlc_am_nr_sn_size_t::size12bits) ? 4096 : 262144; return true; } @@ -462,8 +476,8 @@ void rlc_am_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes) logger->info("%s SN=%d outside rx window [%d:%d] - discarding", parent->rb_name, header.sn, - rx_next, - rx_next + RLC_AM_NR_WINDOW_SIZE); + st.rx_next, + st.rx_next + RLC_AM_NR_WINDOW_SIZE); return; } @@ -518,8 +532,8 @@ void rlc_am_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes) // 5.2.3.2.3 Actions when an AMD PDU is placed in the reception buffer // Update Rx_Next_Highest - if (RX_MOD_BASE_NR(header.sn) >= RX_MOD_BASE_NR(rx_next_highest)) { - rx_next_highest = (header.sn + 1) % MOD; + if (rx_mod_base_nr(header.sn) >= rx_mod_base_nr(st.rx_next_highest)) { + st.rx_next_highest = (header.sn + 1) % MOD; } // Update RX_Highest_Status @@ -528,10 +542,10 @@ void rlc_am_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes) * - update RX_Highest_Status to the SN of the first RLC SDU with SN > current RX_Highest_Status for which not * all bytes have been received. */ - if (RX_MOD_BASE_NR(header.sn) == RX_MOD_BASE_NR(rx_highest_status)) { + if (rx_mod_base_nr(header.sn) == rx_mod_base_nr(st.rx_highest_status)) { uint32_t sn_upd = 0; - uint32_t window_top = rx_next + RLC_AM_WINDOW_SIZE; - for (sn_upd = rx_highest_status; sn_upd < window_top; ++sn_upd) { + uint32_t window_top = st.rx_next + RLC_AM_WINDOW_SIZE; + for (sn_upd = st.rx_highest_status; sn_upd < window_top; ++sn_upd) { if (rx_window.has_sn(sn_upd)) { if (not rx_window[sn_upd].fully_received) { break; // first SDU not fully received @@ -542,7 +556,7 @@ void rlc_am_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes) } // Update to the SN of the first SDU with missing bytes. // If it not exists, update to the end of the rx_window. - rx_highest_status = sn_upd; + st.rx_highest_status = sn_upd; } /* @@ -550,10 +564,10 @@ void rlc_am_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes) * - update RX_Next to the SN of the first RLC SDU with SN > current RX_Next for which not all bytes * have been received. */ - if (RX_MOD_BASE_NR(header.sn) == RX_MOD_BASE_NR(rx_next)) { + if (rx_mod_base_nr(header.sn) == rx_mod_base_nr(st.rx_next)) { uint32_t sn_upd = 0; - uint32_t window_top = rx_next + RLC_AM_WINDOW_SIZE; - for (sn_upd = rx_next; sn_upd < window_top; ++sn_upd) { + uint32_t window_top = st.rx_next + RLC_AM_WINDOW_SIZE; + for (sn_upd = st.rx_next; sn_upd < window_top; ++sn_upd) { if (rx_window.has_sn(sn_upd)) { if (not rx_window[sn_upd].fully_received) { break; // first SDU not fully received @@ -567,7 +581,7 @@ void rlc_am_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes) } // Update to the SN of the first SDU with missing bytes. // If it not exists, update to the end of the rx_window. - rx_next = sn_upd; + st.rx_next = sn_upd; } if (reassembly_timer.is_running()) { @@ -589,26 +603,20 @@ void rlc_am_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes) * - set RX_Next_Status_Trigger to RX_Next_Highest. */ bool restart_reassembly_timer = false; - if (rx_next_highest > rx_next + 1) { + if (st.rx_next_highest > st.rx_next + 1) { restart_reassembly_timer = true; } - if (rx_next_highest == rx_next + 1 && - rx_window[rx_next + 1].fully_received == false) { // TODO: does the last by need to be received? + if (st.rx_next_highest == st.rx_next + 1 && + rx_window[st.rx_next + 1].fully_received == false) { // TODO: does the last by need to be received? restart_reassembly_timer = true; } if (restart_reassembly_timer) { reassembly_timer.run(); - rx_next_status_trigger = rx_next_highest; + st.rx_next_status_trigger = st.rx_next_highest; } } } -bool rlc_am_nr_rx::inside_rx_window(uint32_t sn) -{ - return (RX_MOD_BASE_NR(sn) >= RX_MOD_BASE_NR(rx_next)) && - (RX_MOD_BASE_NR(sn) < RX_MOD_BASE_NR(rx_next + RLC_AM_NR_WINDOW_SIZE)); -} - /* * Status PDU */ @@ -620,13 +628,12 @@ uint32_t rlc_am_nr_rx::get_status_pdu(rlc_am_nr_status_pdu_t* status, uint32_t m } status->N_nack = 0; - status->ack_sn = rx_next; // Start with the lower end of the window + status->ack_sn = st.rx_next; // Start with the lower end of the window byte_buffer_t tmp_buf; - uint32_t len; uint32_t i = status->ack_sn; - while (RX_MOD_BASE_NR(i) <= RX_MOD_BASE_NR(rx_highest_status)) { - if (rx_window.has_sn(i) || i == rx_highest_status) { + while (rx_mod_base_nr(i) <= rx_mod_base_nr(st.rx_highest_status)) { + if (rx_window.has_sn(i) || i == st.rx_highest_status) { // only update ACK_SN if this SN has been received, or if we reached the maximum possible SN status->ack_sn = i; } else { @@ -683,22 +690,23 @@ void rlc_am_nr_rx::timer_expired(uint32_t timeout_id) * - start t-Reassembly; * - set RX_Next_Status_Trigger to RX_Next_Highest. */ - for (uint32_t tmp_sn = rx_next_status_trigger; tmp_sn < rx_next_status_trigger + RLC_AM_WINDOW_SIZE; tmp_sn++) { + for (uint32_t tmp_sn = st.rx_next_status_trigger; tmp_sn < st.rx_next_status_trigger + RLC_AM_WINDOW_SIZE; + tmp_sn++) { if (not rx_window.has_sn(tmp_sn) || not rx_window[tmp_sn].fully_received) { - rx_highest_status = tmp_sn; + st.rx_highest_status = tmp_sn; break; } } bool restart_reassembly_timer = false; - if (rx_next_highest > rx_highest_status + 1) { + if (st.rx_next_highest > st.rx_highest_status + 1) { restart_reassembly_timer = true; } - if (rx_next_highest == rx_highest_status + 1 && not rx_window[rx_next_highest].fully_received) { + if (st.rx_next_highest == st.rx_highest_status + 1 && not rx_window[st.rx_next_highest].fully_received) { restart_reassembly_timer = true; } if (restart_reassembly_timer) { reassembly_timer.run(); - rx_next_status_trigger = rx_next_highest; + st.rx_next_status_trigger = st.rx_next_highest; } /* 5.3.4 Status reporting: @@ -720,6 +728,20 @@ void rlc_am_nr_rx::write_to_upper_layers(uint32_t lcid, unique_byte_buffer_t sdu parent->metrics.num_rx_sdu_bytes += nof_bytes; } +/* + * Window Helpers + */ +uint32_t rlc_am_nr_rx::rx_mod_base_nr(uint32_t sn) const +{ + return (sn - st.rx_next) % mod_nr; +} + +bool rlc_am_nr_rx::inside_rx_window(uint32_t sn) +{ + // RX_Next <= SN < RX_Next + AM_Window_Size + return rx_mod_base_nr(sn) < RLC_AM_NR_WINDOW_SIZE; +} + /* * Metrics */ @@ -734,14 +756,14 @@ uint32_t rlc_am_nr_rx::get_rx_buffered_bytes() } /* - * Helpers + * Debug Helpers */ void rlc_am_nr_rx::debug_state() { logger->debug("RX entity state: Rx_Next %d, Rx_Next_Status_Trigger %d, Rx_Highest_Status %d, Rx_Next_Highest", - rx_next, - rx_next_status_trigger, - rx_highest_status, - rx_next_highest); + st.rx_next, + st.rx_next_status_trigger, + st.rx_highest_status, + st.rx_next_highest); } } // namespace srsran diff --git a/lib/test/asn1/rrc_nr_utils_test.cc b/lib/test/asn1/rrc_nr_utils_test.cc index 843021405..83540120c 100644 --- a/lib/test/asn1/rrc_nr_utils_test.cc +++ b/lib/test/asn1/rrc_nr_utils_test.cc @@ -67,13 +67,14 @@ int test_mac_rach_common_config() rach_common_config_asn1.to_json(jw); srslog::fetch_basic_logger("RRC").info("MAC NR RACH Common config: \n %s", jw.to_string().c_str()); - rach_nr_cfg_t rach_nr_cfg = make_mac_rach_cfg(rach_common_config_asn1); - TESTASSERT(rach_nr_cfg.ra_responseWindow == 10); - TESTASSERT(rach_nr_cfg.ra_ContentionResolutionTimer == 64); - TESTASSERT(rach_nr_cfg.prach_ConfigurationIndex == 160); - TESTASSERT(rach_nr_cfg.PreambleReceivedTargetPower == -110); - TESTASSERT(rach_nr_cfg.preambleTransMax == 7); - TESTASSERT(rach_nr_cfg.powerRampingStep == 4); + rach_cfg_nr_t rach_cfg_nr = {}; + make_mac_rach_cfg(rach_common_config_asn1, &rach_cfg_nr); + TESTASSERT(rach_cfg_nr.ra_responseWindow == 10); + TESTASSERT(rach_cfg_nr.ra_ContentionResolutionTimer == 64); + TESTASSERT(rach_cfg_nr.prach_ConfigurationIndex == 160); + TESTASSERT(rach_cfg_nr.PreambleReceivedTargetPower == -110); + TESTASSERT(rach_cfg_nr.preambleTransMax == 7); + TESTASSERT(rach_cfg_nr.powerRampingStep == 4); return SRSRAN_SUCCESS; } @@ -677,6 +678,182 @@ int make_phy_nzp_csi_rs_resource_test() return SRSRAN_SUCCESS; } +int fill_phy_pdsch_cfg_common_test() +{ + // "pdsch-ConfigCommon": + // "setup": + // "pdsch-TimeDomainAllocationList": [ + // "mappingType": "typeA", + // "startSymbolAndLength": 40 + // ] + + asn1::rrc_nr::pdsch_cfg_common_s pdsch_cfg = {}; + pdsch_cfg.pdsch_time_domain_alloc_list_present = true; + pdsch_cfg.pdsch_time_domain_alloc_list.resize(1); + pdsch_cfg.pdsch_time_domain_alloc_list[0].map_type = + asn1::rrc_nr::pdsch_time_domain_res_alloc_s::map_type_opts::options::type_a; + pdsch_cfg.pdsch_time_domain_alloc_list[0].k0_present = false; + pdsch_cfg.pdsch_time_domain_alloc_list[0].start_symbol_and_len = 40; + + srsran_sch_hl_cfg_nr_t pdsch; + fill_phy_pdsch_cfg_common(pdsch_cfg, &pdsch); + + TESTASSERT(pdsch.nof_common_time_ra == 1); + TESTASSERT(pdsch.common_time_ra[0].k == 0); + TESTASSERT(pdsch.common_time_ra[0].mapping_type == srsran_sch_mapping_type_A); + TESTASSERT(pdsch.common_time_ra[0].sliv == 40); + + return SRSRAN_SUCCESS; +} + +int fill_phy_pucch_cfg_common_test() +{ + // "pucch-ConfigCommon": + // "setup": + // "pucch-ResourceCommon": 11, + // "pucch-GroupHopping": "neither", + // "p0-nominal": -90 + + asn1::rrc_nr::pucch_cfg_common_s pucch_cfg = {}; + pucch_cfg.pucch_res_common_present = true; + pucch_cfg.pucch_res_common = 11; + pucch_cfg.hop_id_present = false; + pucch_cfg.p0_nominal_present = true; + pucch_cfg.p0_nominal = -90; + pucch_cfg.pucch_group_hop = pucch_cfg_common_s::pucch_group_hop_opts::neither; + + srsran_pucch_nr_common_cfg_t pucch = {}; + fill_phy_pucch_cfg_common(pucch_cfg, &pucch); + + TESTASSERT(pucch.resource_common == 11); + TESTASSERT(pucch.p0_nominal == -90); + TESTASSERT(pucch.group_hopping == SRSRAN_PUCCH_NR_GROUP_HOPPING_NEITHER); + + return SRSRAN_SUCCESS; +} + +int fill_phy_pusch_cfg_common_test() +{ + // "pusch-ConfigCommon": + // "setup": { + // "pusch-TimeDomainAllocationList": [ + // "k2": 4, + // "mappingType": "typeA", + // "startSymbolAndLength": 27 + // ], + // "p0-NominalWithGrant": -76 + + asn1::rrc_nr::pusch_cfg_common_s pusch_cfg = {}; + pusch_cfg.pusch_time_domain_alloc_list_present = true; + pusch_cfg.pusch_time_domain_alloc_list.resize(1); + pusch_cfg.pusch_time_domain_alloc_list[0].map_type = + asn1::rrc_nr::pusch_time_domain_res_alloc_s::map_type_opts::options::type_a; + pusch_cfg.pusch_time_domain_alloc_list[0].k2_present = true; + pusch_cfg.pusch_time_domain_alloc_list[0].k2 = 4; + pusch_cfg.pusch_time_domain_alloc_list[0].start_symbol_and_len = 27; + pusch_cfg.p0_nominal_with_grant_present = true; + pusch_cfg.p0_nominal_with_grant = -76; + + srsran_sch_hl_cfg_nr_t pusch; + fill_phy_pusch_cfg_common(pusch_cfg, &pusch); + + TESTASSERT(pusch.nof_common_time_ra == 1); + TESTASSERT(pusch.common_time_ra[0].k == 4); + TESTASSERT(pusch.common_time_ra[0].mapping_type == srsran_sch_mapping_type_A); + TESTASSERT(pusch.common_time_ra[0].sliv == 27); + + return SRSRAN_SUCCESS; +} + +int fill_phy_carrier_cfg_test() +{ + // "frequencyInfoDL": + // "frequencyBandList": [ + // "freqBandIndicatorNR": 3 + // ], + // "offsetToPointA": 13, + // "scs-SpecificCarrierList": [ + // "offsetToCarrier": 0, + // "subcarrierSpacing": "kHz15", + // "carrierBandwidth": 52 + // ] + // + // ... + // + // "frequencyInfoUL": + // "frequencyBandList": [ + // "freqBandIndicatorNR": 3 + // ], + // "absoluteFrequencyPointA": 348564, + // "scs-SpecificCarrierList": [ + // "offsetToCarrier": 0, + // "subcarrierSpacing": "kHz15", + // "carrierBandwidth": 52 + // ], + // "p-Max": 10 + + asn1::rrc_nr::serving_cell_cfg_common_sib_s serv_cell_cfg = {}; + serv_cell_cfg.dl_cfg_common.freq_info_dl.freq_band_list.resize(1); + serv_cell_cfg.dl_cfg_common.freq_info_dl.freq_band_list[0].freq_band_ind_nr_present = true; + serv_cell_cfg.dl_cfg_common.freq_info_dl.freq_band_list[0].freq_band_ind_nr = 3; + serv_cell_cfg.dl_cfg_common.freq_info_dl.offset_to_point_a = 13; + serv_cell_cfg.dl_cfg_common.freq_info_dl.scs_specific_carrier_list.resize(1); + serv_cell_cfg.dl_cfg_common.freq_info_dl.scs_specific_carrier_list[0].offset_to_carrier = 0; + serv_cell_cfg.dl_cfg_common.freq_info_dl.scs_specific_carrier_list[0].subcarrier_spacing = + asn1::rrc_nr::subcarrier_spacing_opts::options::khz15; + serv_cell_cfg.dl_cfg_common.freq_info_dl.scs_specific_carrier_list[0].carrier_bw = 52; + + serv_cell_cfg.ul_cfg_common.freq_info_ul.freq_band_list_present = true; + serv_cell_cfg.ul_cfg_common.freq_info_ul.freq_band_list.resize(1); + serv_cell_cfg.ul_cfg_common.freq_info_ul.freq_band_list[0].freq_band_ind_nr_present = true; + serv_cell_cfg.ul_cfg_common.freq_info_ul.freq_band_list[0].freq_band_ind_nr = 3; + serv_cell_cfg.ul_cfg_common.freq_info_ul.absolute_freq_point_a_present = true; + serv_cell_cfg.ul_cfg_common.freq_info_ul.absolute_freq_point_a = 348564; + serv_cell_cfg.ul_cfg_common.freq_info_ul.scs_specific_carrier_list.resize(1); + serv_cell_cfg.ul_cfg_common.freq_info_ul.scs_specific_carrier_list[0].offset_to_carrier = 0; + serv_cell_cfg.ul_cfg_common.freq_info_ul.scs_specific_carrier_list[0].subcarrier_spacing = + asn1::rrc_nr::subcarrier_spacing_opts::options::khz15; + serv_cell_cfg.ul_cfg_common.freq_info_ul.scs_specific_carrier_list[0].carrier_bw = 52; + serv_cell_cfg.ul_cfg_common.freq_info_ul.p_max_present = true; + serv_cell_cfg.ul_cfg_common.freq_info_ul.p_max = 10; + + srsran_carrier_nr_t carrier_nr = {}; + fill_phy_carrier_cfg(serv_cell_cfg, &carrier_nr); + + TESTASSERT(carrier_nr.offset_to_carrier == 0); + TESTASSERT(carrier_nr.scs == srsran_subcarrier_spacing_15kHz); + TESTASSERT(carrier_nr.nof_prb == 52); + TESTASSERT(carrier_nr.ul_center_frequency_hz == 1747.5e6); + + return SRSRAN_SUCCESS; +} + +int fill_phy_ssb_cfg_test() +{ + // "ssb-PositionsInBurst": + // "inOneGroup": "10000000" + // "ssb-PeriodicityServingCell": "ms20", + + asn1::rrc_nr::serving_cell_cfg_common_sib_s serv_cell_cfg = {}; + serv_cell_cfg.ssb_periodicity_serving_cell = + asn1::rrc_nr::serving_cell_cfg_common_sib_s::ssb_periodicity_serving_cell_opts::options::ms20; + serv_cell_cfg.ssb_positions_in_burst.group_presence_present = false; + serv_cell_cfg.ssb_positions_in_burst.in_one_group.from_number(128); + + phy_cfg_nr_t::ssb_cfg_t ssb = {}; + fill_phy_ssb_cfg(serv_cell_cfg, &ssb); + + TESTASSERT(ssb.periodicity_ms == 20); + + uint64_t position_in_burst = 0; + for (uint64_t i = 0; i < 8; i++) { + position_in_burst = position_in_burst << 1 | ssb.position_in_burst[i]; + } + TESTASSERT(position_in_burst == 128); + + return SRSRAN_SUCCESS; +} + int main() { auto& asn1_logger = srslog::fetch_basic_logger("ASN1", false); @@ -705,6 +882,11 @@ int main() TESTASSERT(make_phy_pusch_scaling_test() == SRSRAN_SUCCESS); TESTASSERT(make_phy_zp_csi_rs_resource_test() == SRSRAN_SUCCESS); TESTASSERT(make_phy_nzp_csi_rs_resource_test() == SRSRAN_SUCCESS); + TESTASSERT(fill_phy_pdsch_cfg_common_test() == SRSRAN_SUCCESS); + TESTASSERT(fill_phy_pucch_cfg_common_test() == SRSRAN_SUCCESS); + TESTASSERT(fill_phy_pusch_cfg_common_test() == SRSRAN_SUCCESS); + TESTASSERT(fill_phy_carrier_cfg_test() == SRSRAN_SUCCESS); + TESTASSERT(fill_phy_ssb_cfg_test() == SRSRAN_SUCCESS); srslog::flush(); printf("Success\n"); diff --git a/lib/test/rlc/rlc_am_lte_test.cc b/lib/test/rlc/rlc_am_lte_test.cc index 542547f4f..a6a840879 100644 --- a/lib/test/rlc/rlc_am_lte_test.cc +++ b/lib/test/rlc/rlc_am_lte_test.cc @@ -3622,6 +3622,150 @@ bool discard_test() return SRSRAN_SUCCESS; } + +// This test checks wether re-transmissions are triggered correctly in case the t-PollRetranmission expires. +// It checks if the poll retx timer is re-armed upon receiving an ACK for POLL_SN +bool poll_retx_expiry_test() +{ + rlc_config_t config = rlc_config_t::default_rlc_am_config(); + // [I] SRB1 configured: t_poll_retx=65, poll_pdu=-1, poll_byte=-1, max_retx_thresh=6, t_reordering=55, + // t_status_prohibit=0 + config.am.t_poll_retx = 65; + config.am.poll_pdu = -1; + config.am.poll_byte = -1; + config.am.max_retx_thresh = 6; + config.am.t_reordering = 55; + config.am.t_status_prohibit = 55; + +#if HAVE_PCAP + rlc_pcap pcap; + pcap.open("rlc_am_poll_rext_expiry_test.pcap", config); + rlc_am_tester tester(&pcap); +#else + rlc_am_tester tester(NULL); +#endif + + srsran::timer_handler timers(8); + + rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + + srslog::fetch_basic_logger("RLC_AM_1").set_hex_dump_max_size(100); + srslog::fetch_basic_logger("RLC_AM_2").set_hex_dump_max_size(100); + srslog::fetch_basic_logger("RLC").set_hex_dump_max_size(100); + + if (not rlc1.configure(config)) { + return -1; + } + + if (not rlc2.configure(config)) { + return -1; + } + + // [I] SRB1 Tx SDU (135 B, tx_sdu_queue_len=1) + // [I] SRB1 Tx PDU SN=3 (91 B) + // [I] SRB1 Tx PDU SN=4 (48 B) + { + // Initial Tx + uint32_t num_tx_pdus = 1; + for (uint32_t i = 0; i < num_tx_pdus; ++i) { + // Write SDU + unique_byte_buffer_t sdu = srsran::make_byte_buffer(); + TESTASSERT(sdu != nullptr); + sdu->N_bytes = 135; + for (uint32_t k = 0; k < sdu->N_bytes; ++k) { + sdu->msg[k] = i; // Write the index into the buffer + } + sdu->md.pdcp_sn = i; + rlc1.write_sdu(std::move(sdu)); + } + unique_byte_buffer_t pdu1 = srsran::make_byte_buffer(); + TESTASSERT(pdu1 != nullptr); + pdu1->N_bytes = rlc1.read_pdu(pdu1->msg, 91); + + unique_byte_buffer_t pdu2 = srsran::make_byte_buffer(); + TESTASSERT(pdu2 != nullptr); + pdu2->N_bytes = rlc1.read_pdu(pdu2->msg, 48); + + // Deliver PDU2 to RLC2. PDU1 is lost + rlc2.write_pdu(pdu2->msg, pdu2->N_bytes); + } + + // Step timers until t-PollRetransmission timer expires on RLC1 + // t-Reordering timer also will expire on RLC2, so we can get an status report. + // [I] SRB1 Schedule SN=3 for reTx + for (int cnt = 0; cnt < 65; cnt++) { + timers.step_all(); + } + + uint32_t status_size = rlc2.get_buffer_state(); + srslog::flush(); + TESTASSERT(4 == status_size); + + // Read status PDU from RLC2 + unique_byte_buffer_t status_buf = srsran::make_byte_buffer(); + TESTASSERT(status_buf != nullptr); + int len = rlc2.read_pdu(status_buf->msg, status_size); + status_buf->N_bytes = len; + + TESTASSERT(0 == rlc2.get_buffer_state()); + + // Assert status is correct + rlc_status_pdu_t status_check = {}; + rlc_am_read_status_pdu(status_buf->msg, status_buf->N_bytes, &status_check); + TESTASSERT(status_check.ack_sn == 2); // 2 is the SN after the largest SN received. + TESTASSERT(status_check.N_nack == 1); // 1 PDU lost + TESTASSERT(rlc_am_is_valid_status_pdu(status_check)); + + // [I] SRB1 Retx PDU segment SN=3 [so=0] (83 B) (attempt 2/6) + { + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + TESTASSERT(pdu != nullptr); + pdu->N_bytes = rlc1.read_pdu(pdu->msg, 83); + } + + // [I] SRB1 Retx PDU segment SN=3 [so=79] (14 B) (attempt 2/6) + { + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + TESTASSERT(pdu != nullptr); + pdu->N_bytes = rlc1.read_pdu(pdu->msg, 79); + } + + // Deliver status PDU after ReTX to RLC1. This should restart t-PollRetransmission + TESTASSERT_EQ(false, rlc1.has_data()); + rlc1.write_pdu(status_buf->msg, status_buf->N_bytes); + TESTASSERT_EQ(true, rlc1.has_data()); + + // [I] SRB1 Retx PDU segment SN=3 [so=0] (83 B) (attempt 3/6) (received a NACK and retx...) + // [I] SRB1 Retx PDU segment SN=3 [so=79] (14 B) (attempt 3/6) + { + unique_byte_buffer_t pdu1 = srsran::make_byte_buffer(); + TESTASSERT(pdu1 != nullptr); + pdu1->N_bytes = rlc1.read_pdu(pdu1->msg, 83); + + unique_byte_buffer_t pdu2 = srsran::make_byte_buffer(); + TESTASSERT(pdu2 != nullptr); + pdu2->N_bytes = rlc1.read_pdu(pdu2->msg, 14); + } + + TESTASSERT_EQ(false, rlc1.has_data()); + + // Step timers until t-PollRetransmission timer expires on RLC1 + // [I] SRB1 Schedule SN=3 for reTx + + for (int cnt = 0; cnt < 66; cnt++) { + timers.step_all(); + } + TESTASSERT_EQ(true, rlc1.has_data()); + srslog::fetch_basic_logger("TEST").info("t-Poll Retransmssion successfully restarted."); + +#if HAVE_PCAP + pcap.close(); +#endif + + return SRSRAN_SUCCESS; +} + int main(int argc, char** argv) { // Setup the log message spy to intercept error and warning log entries from RLC @@ -3827,5 +3971,9 @@ int main(int argc, char** argv) exit(-1); }; + if (poll_retx_expiry_test()) { + printf("poll_retx_expiry_test failed\n"); + exit(-1); + }; return SRSRAN_SUCCESS; } diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index a858bdbf6..b94aeba8a 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -60,6 +60,69 @@ int basic_test_tx(rlc_am* rlc, byte_buffer_t pdu_bufs[NBUFS]) return SRSRAN_SUCCESS; } +/* + * Test the limits of the TX/RX window checkers + * + * This will test + */ +int window_checker_test() +{ + rlc_am_tester tester; + timer_handler timers(8); + + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + test_delimit_logger delimiter("window checkers"); + rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + + rlc_am_nr_tx* tx = dynamic_cast(rlc1.get_tx()); + rlc_am_nr_rx* rx = dynamic_cast(rlc1.get_rx()); + + if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config())) { + return SRSRAN_ERROR; + } + + { + // RLC1 RX_NEXT == 0 and RLC2 TX_NEXT_ACK == 0 + uint32_t sn_inside_below = 0; + uint32_t sn_inside_above = 2047; + uint32_t sn_outside_below = 4095; + uint32_t sn_outside_above = 2048; + TESTASSERT_EQ(true, rx->inside_rx_window(sn_inside_below)); + TESTASSERT_EQ(true, rx->inside_rx_window(sn_inside_above)); + TESTASSERT_EQ(false, rx->inside_rx_window(sn_outside_below)); + TESTASSERT_EQ(false, rx->inside_rx_window(sn_outside_above)); + TESTASSERT_EQ(true, tx->inside_tx_window(sn_inside_below)); + TESTASSERT_EQ(true, tx->inside_tx_window(sn_inside_above)); + TESTASSERT_EQ(false, tx->inside_tx_window(sn_outside_below)); + TESTASSERT_EQ(false, tx->inside_tx_window(sn_outside_above)); + } + + rlc_am_nr_rx_state_t rx_st = {}; + rx_st.rx_next = 4095; + rlc_am_nr_tx_state_t tx_st = {}; + tx_st.tx_next_ack = 4095; + + rx->set_rx_state(rx_st); + tx->set_tx_state(tx_st); + + { + // RX_NEXT == 4095 TX_NEXT_ACK == 4095 + uint32_t sn_inside_below = 0; + uint32_t sn_inside_above = 2046; + uint32_t sn_outside_below = 4094; + uint32_t sn_outside_above = 2048; + TESTASSERT_EQ(true, rx->inside_rx_window(sn_inside_below)); + TESTASSERT_EQ(true, rx->inside_rx_window(sn_inside_above)); + TESTASSERT_EQ(false, rx->inside_rx_window(sn_outside_below)); + TESTASSERT_EQ(false, rx->inside_rx_window(sn_outside_above)); + TESTASSERT_EQ(true, tx->inside_tx_window(sn_inside_below)); + TESTASSERT_EQ(true, tx->inside_tx_window(sn_inside_above)); + TESTASSERT_EQ(false, tx->inside_tx_window(sn_outside_below)); + TESTASSERT_EQ(false, tx->inside_tx_window(sn_outside_above)); + } + return SRSRAN_SUCCESS; +} + /* * Test the transmission and acknowledgement of 5 SDUs. * @@ -312,6 +375,7 @@ int main(int argc, char** argv) // start log backend srslog::init(); + TESTASSERT(window_checker_test() == SRSRAN_SUCCESS); TESTASSERT(basic_test() == SRSRAN_SUCCESS); TESTASSERT(lost_pdu_test() == SRSRAN_SUCCESS); diff --git a/srsenb/hdr/phy/phy_interfaces.h b/srsenb/hdr/phy/phy_interfaces.h index e2cd0b00c..d5b8db4ff 100644 --- a/srsenb/hdr/phy/phy_interfaces.h +++ b/srsenb/hdr/phy/phy_interfaces.h @@ -65,8 +65,6 @@ struct phy_args_t { bool extended_cp = false; srsran::channel::args_t dl_channel_args; srsran::channel::args_t ul_channel_args; - - srsran::vnf_args_t vnf_args; }; struct phy_cfg_t { diff --git a/srsenb/hdr/stack/rrc/rrc.h b/srsenb/hdr/stack/rrc/rrc.h index 520447adb..6236c8f65 100644 --- a/srsenb/hdr/stack/rrc/rrc.h +++ b/srsenb/hdr/stack/rrc/rrc.h @@ -34,7 +34,10 @@ #include "srsran/common/stack_procedure.h" #include "srsran/common/task_scheduler.h" #include "srsran/common/timeout.h" -#include "srsran/interfaces/enb_rrc_interfaces.h" +#include "srsran/interfaces/enb_rrc_interface_mac.h" +#include "srsran/interfaces/enb_rrc_interface_pdcp.h" +#include "srsran/interfaces/enb_rrc_interface_rlc.h" +#include "srsran/interfaces/enb_rrc_interface_s1ap.h" #include "srsran/interfaces/enb_x2_interfaces.h" #include "srsran/srslog/srslog.h" #include @@ -49,14 +52,6 @@ class phy_interface_rrc_lte; class paging_manager; -static const char rrc_state_text[RRC_STATE_N_ITEMS][100] = {"IDLE", - "WAIT FOR CON SETUP COMPLETE", - "WAIT FOR SECURITY MODE COMPLETE", - "WAIT FOR UE CAPABILITIY INFORMATION", - "WAIT FOR CON RECONF COMPLETE", - "RRC CONNECTED", - "RELEASE REQUEST"}; - class rrc final : public rrc_interface_pdcp, public rrc_interface_mac, public rrc_interface_rlc, diff --git a/srsenb/src/main.cc b/srsenb/src/main.cc index cf13cfc81..4c436a7b7 100644 --- a/srsenb/src/main.cc +++ b/srsenb/src/main.cc @@ -270,13 +270,6 @@ void parse_args(all_args_t* args, int argc, char* argv[]) ("scheduler.nr_pdsch_mcs", bpo::value(&args->nr_stack.mac.sched_cfg.fixed_dl_mcs)->default_value(28), "Fixed NR DL MCS (-1 for dynamic).") ("scheduler.nr_pusch_mcs", bpo::value(&args->nr_stack.mac.sched_cfg.fixed_ul_mcs)->default_value(28), "Fixed NR UL MCS (-1 for dynamic).") ("expert.nr_pusch_max_its", bpo::value(&args->phy.nr_pusch_max_its)->default_value(10), "Maximum number of LDPC iterations for NR.") - - // VNF params - ("vnf.type", bpo::value(&args->phy.vnf_args.type)->default_value("gnb"), "VNF instance type [gnb,ue].") - ("vnf.addr", bpo::value(&args->phy.vnf_args.bind_addr)->default_value("localhost"), "Address to bind VNF interface.") - ("vnf.port", bpo::value(&args->phy.vnf_args.bind_port)->default_value(3333), "Bind port.") - ("log.vnf_level", bpo::value(&args->phy.vnf_args.log_level), "VNF log level.") - ("log.vnf_hex_limit", bpo::value(&args->phy.vnf_args.log_hex_limit), "VNF log hex dump limit.") ; // Positional options - config file location diff --git a/srsenb/src/stack/mac/mac.cc b/srsenb/src/stack/mac/mac.cc index 46f2b15ed..ba2c15289 100644 --- a/srsenb/src/stack/mac/mac.cc +++ b/srsenb/src/stack/mac/mac.cc @@ -29,7 +29,7 @@ #include "srsran/common/time_prof.h" #include "srsran/interfaces/enb_phy_interfaces.h" #include "srsran/interfaces/enb_rlc_interfaces.h" -#include "srsran/interfaces/enb_rrc_interfaces.h" +#include "srsran/interfaces/enb_rrc_interface_mac.h" #include "srsran/srslog/event_trace.h" // #define WRITE_SIB_PCAP @@ -894,9 +894,9 @@ int mac::get_mch_sched(uint32_t tti, bool is_mcch, dl_sched_list_t& dl_sched_res int requested_bytes = (mcs_data.tbs / 8 > (int)mch.mtch_sched[mtch_index].lcid_buffer_size) ? (mch.mtch_sched[mtch_index].lcid_buffer_size) : ((mcs_data.tbs / 8) - 2); - int bytes_received = ue_db[SRSRAN_MRNTI]->read_pdu(current_lcid, mtch_payload_buffer, requested_bytes); - mch.pdu[0].lcid = current_lcid; - mch.pdu[0].nbytes = bytes_received; + int bytes_received = ue_db[SRSRAN_MRNTI]->read_pdu(current_lcid, mtch_payload_buffer, requested_bytes); + mch.pdu[0].lcid = current_lcid; + mch.pdu[0].nbytes = bytes_received; mch.mtch_sched[0].mtch_payload = mtch_payload_buffer; dl_sched_res->pdsch[0].dci.rnti = SRSRAN_MRNTI; if (bytes_received) { diff --git a/srsenb/src/stack/mac/sched_carrier.cc b/srsenb/src/stack/mac/sched_carrier.cc index 477fa6cea..65e1ccde4 100644 --- a/srsenb/src/stack/mac/sched_carrier.cc +++ b/srsenb/src/stack/mac/sched_carrier.cc @@ -25,7 +25,7 @@ #include "srsenb/hdr/stack/mac/schedulers/sched_time_rr.h" #include "srsran/common/standard_streams.h" #include "srsran/common/string_helpers.h" -#include "srsran/interfaces/enb_rrc_interfaces.h" +#include "srsran/interfaces/enb_rrc_interface_mac.h" namespace srsenb { diff --git a/srsenb/src/stack/mac/ue.cc b/srsenb/src/stack/mac/ue.cc index 7a67ef1b3..8d421bb10 100644 --- a/srsenb/src/stack/mac/ue.cc +++ b/srsenb/src/stack/mac/ue.cc @@ -28,7 +28,7 @@ #include "srsran/common/string_helpers.h" #include "srsran/interfaces/enb_phy_interfaces.h" #include "srsran/interfaces/enb_rlc_interfaces.h" -#include "srsran/interfaces/enb_rrc_interfaces.h" +#include "srsran/interfaces/enb_rrc_interface_mac.h" namespace srsenb { diff --git a/srsenb/src/stack/s1ap/s1ap.cc b/srsenb/src/stack/s1ap/s1ap.cc index 99d3e8d61..340d27ec5 100644 --- a/srsenb/src/stack/s1ap/s1ap.cc +++ b/srsenb/src/stack/s1ap/s1ap.cc @@ -25,7 +25,7 @@ #include "srsran/common/enb_events.h" #include "srsran/common/int_helpers.h" #include "srsran/common/standard_streams.h" -#include "srsran/interfaces/enb_rrc_interfaces.h" +#include "srsran/interfaces/enb_rrc_interface_s1ap.h" #include //for inet_ntop() #include diff --git a/srsenb/src/stack/upper/pdcp.cc b/srsenb/src/stack/upper/pdcp.cc index 3e4ced87b..bf597f6f0 100644 --- a/srsenb/src/stack/upper/pdcp.cc +++ b/srsenb/src/stack/upper/pdcp.cc @@ -23,7 +23,7 @@ #include "srsenb/hdr/common/common_enb.h" #include "srsran/interfaces/enb_gtpu_interfaces.h" #include "srsran/interfaces/enb_rlc_interfaces.h" -#include "srsran/interfaces/enb_rrc_interfaces.h" +#include "srsran/interfaces/enb_rrc_interface_pdcp.h" namespace srsenb { diff --git a/srsenb/src/stack/upper/rlc.cc b/srsenb/src/stack/upper/rlc.cc index 194af80d9..5fb0d7de7 100644 --- a/srsenb/src/stack/upper/rlc.cc +++ b/srsenb/src/stack/upper/rlc.cc @@ -23,7 +23,7 @@ #include "srsenb/hdr/common/common_enb.h" #include "srsran/interfaces/enb_mac_interfaces.h" #include "srsran/interfaces/enb_pdcp_interfaces.h" -#include "srsran/interfaces/enb_rrc_interfaces.h" +#include "srsran/interfaces/enb_rrc_interface_rlc.h" namespace srsenb { diff --git a/srsenb/test/common/dummy_classes.h b/srsenb/test/common/dummy_classes.h index 98b9de557..b7d63e16e 100644 --- a/srsenb/test/common/dummy_classes.h +++ b/srsenb/test/common/dummy_classes.h @@ -27,7 +27,7 @@ #include "srsran/interfaces/enb_mac_interfaces.h" #include "srsran/interfaces/enb_phy_interfaces.h" #include "srsran/interfaces/enb_rlc_interfaces.h" -#include "srsran/interfaces/enb_rrc_interfaces.h" +#include "srsran/interfaces/enb_rrc_interface_s1ap.h" #include "srsran/interfaces/enb_s1ap_interfaces.h" namespace srsenb { diff --git a/srsenb/test/mac/sched_test_common.h b/srsenb/test/mac/sched_test_common.h index b3ff3c2d9..3b9491843 100644 --- a/srsenb/test/mac/sched_test_common.h +++ b/srsenb/test/mac/sched_test_common.h @@ -25,7 +25,7 @@ #include "sched_sim_ue.h" #include "sched_test_utils.h" #include "srsenb/hdr/stack/mac/sched.h" -#include "srsran/interfaces/enb_rrc_interfaces.h" +#include "srsran/interfaces/enb_rrc_interface_mac.h" #include "srsran/srslog/srslog.h" #include diff --git a/srsgnb/hdr/stack/mac/sched_nr_bwp.h b/srsgnb/hdr/stack/mac/sched_nr_bwp.h index 5820431b3..411d3ad32 100644 --- a/srsgnb/hdr/stack/mac/sched_nr_bwp.h +++ b/srsgnb/hdr/stack/mac/sched_nr_bwp.h @@ -50,10 +50,10 @@ public: private: struct pending_rar_t { - uint16_t ra_rnti = 0; - slot_point prach_slot; - slot_interval rar_win; - srsran::bounded_vector msg3_grant; + uint16_t ra_rnti = 0; + slot_point prach_slot; + slot_interval rar_win; + srsran::bounded_vector msg3_grant; }; alloc_result diff --git a/srsgnb/hdr/stack/mac/sched_nr_cfg.h b/srsgnb/hdr/stack/mac/sched_nr_cfg.h index 6faf5f743..34efb7368 100644 --- a/srsgnb/hdr/stack/mac/sched_nr_cfg.h +++ b/srsgnb/hdr/stack/mac/sched_nr_cfg.h @@ -29,10 +29,7 @@ namespace srsenb { -const static size_t SCHED_NR_MAX_USERS = SRSENB_MAX_UES; -const static size_t SCHED_NR_NOF_SUBFRAMES = 10; -const static size_t SCHED_NR_NOF_HARQS = 16; -static const size_t MAX_NOF_AGGR_LEVELS = 5; +static const size_t MAX_NOF_AGGR_LEVELS = 5; namespace sched_nr_impl { @@ -45,6 +42,7 @@ using pusch_t = mac_interface_phy_nr::pusch_t; using pucch_t = mac_interface_phy_nr::pucch_t; using pdcch_dl_list_t = srsran::bounded_vector; using pdcch_ul_list_t = srsran::bounded_vector; +using pdsch_list_t = srsran::bounded_vector; using pucch_list_t = srsran::bounded_vector; using pusch_list_t = srsran::bounded_vector; using nzp_csi_rs_list = srsran::bounded_vector; @@ -105,19 +103,23 @@ struct bwp_params_t { bwp_params_t(const cell_cfg_t& cell, const sched_args_t& sched_cfg_, uint32_t cc, uint32_t bwp_id); - const prb_bitmap& used_prbs(uint32_t ss_id, srsran_dci_format_nr_t dci_fmt) const + prb_interval coreset_prb_range(uint32_t cs_id) const { return coresets[cs_id].prb_limits; } + prb_interval dci_fmt_1_0_prb_lims(uint32_t cs_id) const { return coresets[cs_id].dci_1_0_prb_limits; } + bwp_rb_bitmap dci_fmt_1_0_excluded_prbs(uint32_t cs_id) const { return coresets[cs_id].usable_common_ss_prb_mask; } + + const srsran_search_space_t* get_ss(uint32_t ss_id) const { - if (used_common_prb_masks.contains(ss_id)) { - if (dci_fmt == srsran_dci_format_nr_1_0) { - return used_common_prb_masks[ss_id]; - } - } - return cached_empty_prb_mask; + return cfg.pdcch.search_space_present[ss_id] ? &cfg.pdcch.search_space[ss_id] : nullptr; } private: - prb_bitmap cached_empty_prb_mask; - srsran::optional_vector used_common_prb_masks; + bwp_rb_bitmap cached_empty_prb_mask; + struct coreset_cached_params { + prb_interval prb_limits; + prb_interval dci_1_0_prb_limits; /// See TS 38.214, section 5.1.2.2 + bwp_rb_bitmap usable_common_ss_prb_mask; + }; + srsran::optional_vector coresets; }; /// Structure packing a single cell config params, and sched args @@ -157,6 +159,16 @@ public: const srsran::phy_cfg_nr_t& phy() const { return cfg_->phy_cfg; } const bwp_params_t& active_bwp() const { return *bwp_cfg; } + /// Get SearchSpace based on SearchSpaceId + const srsran_search_space_t* get_ss(uint32_t ss_id) const + { + if (phy().pdcch.search_space_present[ss_id]) { + // UE-dedicated SearchSpace + return &bwp_cfg->cfg.pdcch.search_space[ss_id]; + } + return nullptr; + } + srsran::const_span cce_pos_list(uint32_t search_id, uint32_t slot_idx, uint32_t aggr_idx) const { if (cce_positions_list.size() > ss_id_to_cce_idx[search_id]) { @@ -180,12 +192,18 @@ public: int fixed_pdsch_mcs() const { return bwp_cfg->sched_cfg.fixed_dl_mcs; } int fixed_pusch_mcs() const { return bwp_cfg->sched_cfg.fixed_ul_mcs; } + const srsran_dci_cfg_nr_t& get_dci_cfg() const { return cached_dci_cfg; } + + int find_ss_id(srsran_dci_format_nr_t dci_fmt) const; + private: const ue_cfg_t* cfg_ = nullptr; const bwp_params_t* bwp_cfg = nullptr; + // derived std::vector cce_positions_list; std::array ss_id_to_cce_idx; + srsran_dci_cfg_nr_t cached_dci_cfg; }; } // namespace sched_nr_impl diff --git a/srsgnb/hdr/stack/mac/sched_nr_grant_allocator.h b/srsgnb/hdr/stack/mac/sched_nr_grant_allocator.h index 0b5cbbdf2..4b647e893 100644 --- a/srsgnb/hdr/stack/mac/sched_nr_grant_allocator.h +++ b/srsgnb/hdr/stack/mac/sched_nr_grant_allocator.h @@ -26,6 +26,7 @@ #include "sched_nr_helpers.h" #include "sched_nr_interface.h" #include "sched_nr_pdcch.h" +#include "sched_nr_sch.h" #include "sched_nr_ue.h" #include "srsenb/hdr/stack/mac/sched_common.h" @@ -37,9 +38,6 @@ using dl_sched_rar_info_t = sched_nr_interface::rar_info_t; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -const static size_t MAX_CORESET_PER_BWP = 3; -using slot_coreset_list = std::array, MAX_CORESET_PER_BWP>; - using pdsch_list_t = srsran::bounded_vector; using sched_rar_list_t = sched_nr_interface::sched_rar_list_t; using pucch_list_t = srsran::bounded_vector; @@ -56,26 +54,20 @@ struct bwp_slot_grid { uint32_t slot_idx = 0; const bwp_params_t* cfg = nullptr; - bwp_rb_bitmap dl_prbs; - bwp_rb_bitmap ul_prbs; - dl_sched_res_t dl; - ul_sched_t ul; - slot_coreset_list coresets; - harq_ack_list_t pending_acks; + dl_sched_res_t dl; + ul_sched_t ul; + harq_ack_list_t pending_acks; + bwp_pdcch_allocator pdcchs; /// slot PDCCH resource allocator + pdsch_allocator pdschs; /// slot PDSCH resource allocator + pusch_allocator puschs; /// slot PUSCH resource allocator srsran::unique_pool_ptr rar_softbuffer; - bwp_slot_grid() = default; explicit bwp_slot_grid(const bwp_params_t& bwp_params, uint32_t slot_idx_); void reset(); bool is_dl() const { return cfg->slots[slot_idx].is_dl; } bool is_ul() const { return cfg->slots[slot_idx].is_ul; } - - prb_bitmap used_prbs(uint32_t ss_id, srsran_dci_format_nr_t dci_fmt) const - { - return dl_prbs.prbs() | cfg->used_prbs(ss_id, dci_fmt); - } }; struct bwp_res_grid { @@ -113,8 +105,8 @@ public: uint32_t aggr_idx, prb_interval interv, srsran::const_span pending_rars); - alloc_result alloc_pdsch(slot_ue& ue, prb_grant dl_grant); - alloc_result alloc_pusch(slot_ue& ue, prb_grant dl_mask); + alloc_result alloc_pdsch(slot_ue& ue, uint32_t ss_id, const prb_grant& dl_grant); + alloc_result alloc_pusch(slot_ue& ue, const prb_grant& grant); slot_point get_pdcch_tti() const { return pdcch_slot; } slot_point get_tti_rx() const { return pdcch_slot - TX_ENB_DELAY; } @@ -122,14 +114,17 @@ public: const bwp_slot_grid& tx_slot_grid() const { return bwp_grid[pdcch_slot]; } bwp_slot_grid& tx_slot_grid() { return bwp_grid[pdcch_slot]; } + prb_bitmap occupied_dl_prbs(slot_point sl_tx, uint32_t ss_id, srsran_dci_format_nr_t dci_fmt) const + { + return bwp_grid[sl_tx].pdschs.occupied_prbs(ss_id, dci_fmt); + } + const prb_bitmap& occupied_ul_prbs(slot_point sl_tx) const { return bwp_grid[sl_tx].puschs.occupied_prbs(); } + srslog::basic_logger& logger; const bwp_params_t& cfg; private: - alloc_result - verify_pdsch_space(bwp_slot_grid& pdsch_grid, bwp_slot_grid& pdcch_grid, bwp_slot_grid* uci_grid = nullptr) const; - alloc_result verify_pusch_space(bwp_slot_grid& pusch_grid, bwp_slot_grid* pdcch_grid = nullptr) const; - alloc_result verify_ue_cfg(const ue_carrier_params_t& ue_cfg, harq_proc* harq) const; + alloc_result verify_uci_space(const bwp_slot_grid& uci_grid) const; bwp_res_grid& bwp_grid; @@ -137,6 +132,8 @@ private: slot_ue_map_t& slot_ues; }; +prb_grant find_optimal_dl_grant(bwp_slot_allocator& slot_alloc, const slot_ue& ue, uint32_t ss_id); + } // namespace sched_nr_impl } // namespace srsenb diff --git a/srsgnb/hdr/stack/mac/sched_nr_harq.h b/srsgnb/hdr/stack/mac/sched_nr_harq.h index 146d5ac3b..b0db22ca8 100644 --- a/srsgnb/hdr/stack/mac/sched_nr_harq.h +++ b/srsgnb/hdr/stack/mac/sched_nr_harq.h @@ -57,8 +57,6 @@ public: bool clear_if_maxretx(slot_point slot_rx); void reset(); - bool new_tx(slot_point slot_tx, slot_point slot_ack, const prb_grant& grant, uint32_t mcs, uint32_t max_retx); - bool new_retx(slot_point slot_tx, slot_point slot_ack, const prb_grant& grant); bool new_retx(slot_point slot_tx, slot_point slot_ack); // NOTE: Has to be used before first tx is dispatched @@ -67,7 +65,10 @@ public: const uint32_t pid; -private: +protected: + bool new_tx(slot_point slot_tx, slot_point slot_ack, const prb_grant& grant, uint32_t mcs, uint32_t max_retx); + bool new_retx(slot_point slot_tx, slot_point slot_ack, const prb_grant& grant); + struct tb_t { bool active = false; bool ack_state = false; @@ -92,9 +93,18 @@ public: tx_harq_softbuffer& get_softbuffer() { return *softbuffer; } srsran::unique_byte_buffer_t* get_tx_pdu() { return &pdu; } - bool new_tx(slot_point slot_tx, slot_point slot_ack, const prb_grant& grant, uint32_t mcs, uint32_t max_retx); + bool new_tx(slot_point slot_tx, + slot_point slot_ack, + const prb_grant& grant, + uint32_t mcs, + uint32_t max_retx, + srsran_dci_dl_nr_t& dci); + + bool new_retx(slot_point slot_tx, slot_point slot_ack, const prb_grant& grant, srsran_dci_dl_nr_t& dci); private: + void fill_dci(srsran_dci_dl_nr_t& dci); + srsran::unique_pool_ptr softbuffer; srsran::unique_byte_buffer_t pdu; }; @@ -106,6 +116,10 @@ public: harq_proc(id_), softbuffer(harq_softbuffer_pool::get_instance().get_rx(nprb)) {} + bool new_tx(slot_point slot_tx, const prb_grant& grant, uint32_t mcs, uint32_t max_retx, srsran_dci_ul_nr_t& dci); + + bool new_retx(slot_point slot_tx, const prb_grant& grant, srsran_dci_ul_nr_t& dci); + rx_harq_softbuffer& get_softbuffer() { return *softbuffer; } bool set_tbs(uint32_t tbs) @@ -115,6 +129,8 @@ public: } private: + void fill_dci(srsran_dci_ul_nr_t& dci); + srsran::unique_pool_ptr softbuffer; }; diff --git a/srsgnb/hdr/stack/mac/sched_nr_helpers.h b/srsgnb/hdr/stack/mac/sched_nr_helpers.h index 4b1efd06e..15f6d39f4 100644 --- a/srsgnb/hdr/stack/mac/sched_nr_helpers.h +++ b/srsgnb/hdr/stack/mac/sched_nr_helpers.h @@ -33,35 +33,29 @@ class slot_ue; class ul_harq_proc; struct bwp_res_grid; -/// In case of Common SearchSpace, not all PRBs might be available -void reduce_to_dl_coreset_bw(const bwp_params_t& bwp_cfg, - uint32_t ss_id, - srsran_dci_format_nr_t dci_fmt, - prb_grant& grant); - -bool fill_dci_sib(prb_interval interv, - uint32_t sib_idx, - uint32_t si_ntx, - const bwp_params_t& bwp_cfg, - srsran_dci_dl_nr_t& dci); - -bool fill_dci_rar(prb_interval interv, uint16_t ra_rnti, const bwp_params_t& bwp_cfg, srsran_dci_dl_nr_t& dci); - -bool fill_dci_msg3(const slot_ue& ue, const bwp_params_t& bwp_cfg, srsran_dci_ul_nr_t& dci); - -/// Generate PDCCH DL DCI fields -void fill_dl_dci_ue_fields(const slot_ue& ue, - const bwp_params_t& bwp_cfg, - uint32_t ss_id, - srsran_dci_location_t dci_pos, - srsran_dci_dl_nr_t& dci); - -/// Generate PDCCH UL DCI fields -void fill_ul_dci_ue_fields(const slot_ue& ue, - const bwp_params_t& bwp_cfg, - uint32_t ss_id, - srsran_dci_location_t dci_pos, - srsran_dci_ul_nr_t& dci); +/// Helper function to verify if RNTI type can be placed in specified search space +/// Based on 38.213, Section 10.1 +inline bool is_rnti_type_valid_in_search_space(srsran_rnti_type_t rnti_type, srsran_search_space_type_t ss_type) +{ + switch (ss_type) { + case srsran_search_space_type_common_0: // fall-through + case srsran_search_space_type_common_0A: // Other SIBs + return rnti_type == srsran_rnti_type_si; + case srsran_search_space_type_common_1: + return rnti_type == srsran_rnti_type_ra or rnti_type == srsran_rnti_type_tc or + /* in case of Pcell -> */ rnti_type == srsran_rnti_type_c; + case srsran_search_space_type_common_2: + return rnti_type == srsran_rnti_type_p; + case srsran_search_space_type_common_3: + return rnti_type == srsran_rnti_type_c; // TODO: Fix + case srsran_search_space_type_ue: + return rnti_type == srsran_rnti_type_c or rnti_type == srsran_rnti_type_cs or + rnti_type == srsran_rnti_type_sp_csi; + default: + break; + } + return false; +} /// Log UE state for slot being scheduled void log_sched_slot_ues(srslog::basic_logger& logger, diff --git a/srsgnb/hdr/stack/mac/sched_nr_interface.h b/srsgnb/hdr/stack/mac/sched_nr_interface.h index 99fd42fe5..1df560532 100644 --- a/srsgnb/hdr/stack/mac/sched_nr_interface.h +++ b/srsgnb/hdr/stack/mac/sched_nr_interface.h @@ -22,6 +22,7 @@ #ifndef SRSRAN_SCHED_NR_INTERFACE_H #define SRSRAN_SCHED_NR_INTERFACE_H +#include "srsenb/hdr/stack/mac/common/sched_config.h" #include "srsran/adt/bounded_bitset.h" #include "srsran/adt/bounded_vector.h" #include "srsran/adt/optional.h" diff --git a/srsgnb/hdr/stack/mac/sched_nr_interface_utils.h b/srsgnb/hdr/stack/mac/sched_nr_interface_utils.h index 50d90f877..d68ecfcab 100644 --- a/srsgnb/hdr/stack/mac/sched_nr_interface_utils.h +++ b/srsgnb/hdr/stack/mac/sched_nr_interface_utils.h @@ -27,7 +27,7 @@ namespace srsenb { -// Helpers to handle PHY struct types +//////////////////////////////////// Search Space Helpers //////////////////////////////////////////// /// Get a range of active search spaces in a PDCCH configuration inline srsran::split_optional_span view_active_search_spaces(srsran_pdcch_cfg_nr_t& pdcch) @@ -40,6 +40,36 @@ view_active_search_spaces(const srsran_pdcch_cfg_nr_t& pdcch) return srsran::split_optional_span{pdcch.search_space, pdcch.search_space_present}; } +inline bool contains_dci_format(const srsran_search_space_t& ss, srsran_dci_format_nr_t dci_fmt) +{ + auto is_dci_fmt = [dci_fmt](const srsran_dci_format_nr_t& f) { return f == dci_fmt; }; + return std::any_of(&ss.formats[0], &ss.formats[ss.nof_formats], is_dci_fmt); +} + +//////////////////////////////////// CORESET Helpers //////////////////////////////////////////// + +/// Get a range of active coresets in a PDCCH configuration +inline srsran::split_optional_span view_active_coresets(srsran_pdcch_cfg_nr_t& pdcch) +{ + return srsran::split_optional_span{pdcch.coreset, pdcch.coreset_present}; +} +inline srsran::split_optional_span view_active_coresets(const srsran_pdcch_cfg_nr_t& pdcch) +{ + return srsran::split_optional_span{pdcch.coreset, pdcch.coreset_present}; +} + +/// Get number of CCEs available in CORESET for PDCCH +uint32_t coreset_nof_cces(const srsran_coreset_t& coreset); + +//////////////////////////////////// Sched Output Helpers //////////////////////////////////////////// + +inline bool operator==(srsran_dci_location_t lhs, srsran_dci_location_t rhs) +{ + return lhs.ncce == rhs.ncce and lhs.L == rhs.L; +} + +//////////////////////////////////// UE configuration Helpers //////////////////////////////////////////// + srsran::phy_cfg_nr_t get_common_ue_phy_cfg(const sched_nr_interface::cell_cfg_t& cfg); } // namespace srsenb diff --git a/srsgnb/hdr/stack/mac/sched_nr_pdcch.h b/srsgnb/hdr/stack/mac/sched_nr_pdcch.h index da93ae335..88ed8dd05 100644 --- a/srsgnb/hdr/stack/mac/sched_nr_pdcch.h +++ b/srsgnb/hdr/stack/mac/sched_nr_pdcch.h @@ -22,6 +22,7 @@ #ifndef SRSRAN_SCHED_NR_PDCCH_H #define SRSRAN_SCHED_NR_PDCCH_H +#include "srsenb/hdr/stack/mac/sched_common.h" #include "srsgnb/hdr/stack/mac/sched_nr_cfg.h" #include "srsran/adt/bounded_bitset.h" #include "srsran/adt/bounded_vector.h" @@ -32,41 +33,34 @@ namespace srsenb { namespace sched_nr_impl { +/// Helper function to fill DCI with BWP params +void fill_dci_from_cfg(const bwp_params_t& bwp_cfg, srsran_dci_dl_nr_t& dci); +void fill_dci_from_cfg(const bwp_params_t& bwp_cfg, srsran_dci_ul_nr_t& dci); + using coreset_bitmap = srsran::bounded_bitset; -enum class pdcch_grant_type_t { sib, rar, dl_data, ul_data }; - -class slot_ue; - class coreset_region { public: - coreset_region(const bwp_params_t& bwp_cfg_, - uint32_t coreset_id_, - uint32_t slot_idx, - pdcch_dl_list_t& pdcch_dl_list, - pdcch_ul_list_t& pdcch_ul_list); + coreset_region(const bwp_params_t& bwp_cfg_, uint32_t coreset_id_, uint32_t slot_idx); void reset(); - /** - * Allocates DCI space in PDCCH, avoiding in the process collisions with other users - * @param pdcch_grant_type_t allocation type (e.g. DL data, UL data, SIB) - * @param aggr_idx Aggregation level index (0..4) - * @param user UE object or null in case of broadcast/RAR/paging allocation - * @return if the allocation was successful - */ - bool alloc_dci(pdcch_grant_type_t alloc_type, - uint32_t aggr_idx, - uint32_t search_space_id, - const ue_carrier_params_t* user = nullptr); + bool alloc_pdcch(srsran_rnti_type_t rnti_type, + bool is_dl, + uint32_t aggr_idx, + uint32_t search_space_id, + const ue_carrier_params_t* user, + srsran_dci_ctx_t& dci); - void rem_last_dci(); + void rem_last_pdcch(); uint32_t get_td_symbols() const { return coreset_cfg->duration; } uint32_t get_freq_resources() const { return nof_freq_res; } uint32_t nof_cces() const { return nof_freq_res * get_td_symbols(); } size_t nof_allocs() const { return dfs_tree.size(); } + void print_allocations(fmt::memory_buffer& fmtbuf) const; + private: const srsran_coreset_t* coreset_cfg; uint32_t coreset_id; @@ -80,13 +74,11 @@ private: struct alloc_record { uint32_t aggr_idx; uint32_t ss_id; - uint32_t idx; - pdcch_grant_type_t alloc_type; + srsran_dci_ctx_t* dci; + bool is_dl; const ue_carrier_params_t* ue; }; srsran::bounded_vector dci_list; - pdcch_dl_list_t& pdcch_dl_list; - pdcch_ul_list_t& pdcch_ul_list; // DFS decision tree of PDCCH grants struct tree_node { @@ -105,6 +97,117 @@ private: bool get_next_dfs(); }; +using pdcch_dl_alloc_result = srsran::expected; +using pdcch_ul_alloc_result = srsran::expected; + +/** + * Class to handle the allocation of REs for a BWP PDCCH in a specific slot + */ +class bwp_pdcch_allocator +{ +public: + bwp_pdcch_allocator(const bwp_params_t& bwp_cfg_, + uint32_t slot_idx, + pdcch_dl_list_t& pdcch_dl_list, + pdcch_ul_list_t& pdcch_ul_list); + + /** + * Clear current slot allocations + */ + void reset(); + + /** + * Allocates RE space for RAR DCI in PDCCH, avoiding in the process collisions with other PDCCH allocations + * Fills DCI context with RAR PDCCH allocation information + * @param ra_rnti RA-RNTI of RAR allocation + * @param aggr_idx Aggregation level index (0..4) + * @return PDCCH object with dci context filled if the allocation was successful. nullptr otherwise + */ + pdcch_dl_alloc_result alloc_rar_pdcch(uint16_t ra_rnti, uint32_t aggr_idx); + + /** + * Allocates RE space for SI DCI in PDCCH, avoiding in the process collisions with other PDCCH allocations + * Fills DCI context with SI PDCCH allocation information + * @param ss_id Search space ID + * @param aggr_idx Aggregation level index (0..4) + * @return PDCCH object with dci context filled if the allocation was successful. nullptr otherwise + */ + pdcch_dl_alloc_result alloc_si_pdcch(uint32_t ss_id, uint32_t aggr_idx); + + /** + * Allocates RE space for UE DL DCI in PDCCH, avoiding in the process collisions with other PDCCH allocations + * Fills DCI context with PDCCH allocation information + * @param rnti_type type of UE RNTI (e.g. C, TC) + * @param ss_id Search space ID + * @param aggr_idx Aggregation level index (0..4) + * @param user UE object parameters + * @return PDCCH object with dci context filled if the allocation was successful. nullptr otherwise + */ + pdcch_dl_alloc_result + alloc_dl_pdcch(srsran_rnti_type_t rnti_type, uint32_t ss_id, uint32_t aggr_idx, const ue_carrier_params_t& user); + + /** + * @brief Allocates RE space for UL DCI in PDCCH, avoiding in the process collisions with other PDCCH allocations + * Fills DCI context with PDCCH allocation information + * @param ss_id Search space ID + * @param aggr_idx Aggregation level index (0..4) + * @param user UE object parameters + * @return PDCCH object with dci context filled if the allocation was successful. nullptr otherwise + */ + pdcch_ul_alloc_result alloc_ul_pdcch(uint32_t ss_id, uint32_t aggr_idx, const ue_carrier_params_t& user); + + /** + * Cancel and remove last PDCCH allocation. It should only be called once after each alloc_dl_pdcch/alloc_ul_pdcch + */ + void cancel_last_pdcch(); + + /// Returns the number of PDCCH allocations made in the slot + uint32_t nof_allocations() const; + + /// Number of CCEs in given coreset + uint32_t nof_cces(uint32_t coreset_id) const; + + void print_allocations(fmt::memory_buffer& fmtbuf) const; + std::string print_allocations() const; + +private: + using slot_coreset_list = srsran::optional_array; + + pdcch_dl_alloc_result alloc_dl_pdcch_common(srsran_rnti_type_t rnti_type, + uint16_t rnti, + uint32_t ss_id, + uint32_t aggr_idx, + srsran_dci_format_nr_t dci_fmt, + const ue_carrier_params_t* user = nullptr); + + /// Helper function to verify valid inputs + alloc_result check_args_valid(srsran_rnti_type_t rnti_type, + uint16_t rnti, + uint32_t ss_id, + uint32_t aggr_idx, + srsran_dci_format_nr_t dci_fmt, + const ue_carrier_params_t* user, + bool is_dl) const; + + /// Fill DCI context of allocated PDCCH + void fill_dci_ctx_common(srsran_dci_ctx_t& dci, + srsran_rnti_type_t rnti_type, + uint16_t rnti, + const srsran_search_space_t& ss, + srsran_dci_format_nr_t dci_fmt, + const ue_carrier_params_t* ue); + + // args + const bwp_params_t& bwp_cfg; + srslog::basic_logger& logger; + const uint32_t slot_idx; + + pdcch_dl_list_t& pdcch_dl_list; + pdcch_ul_list_t& pdcch_ul_list; + slot_coreset_list coresets; + const srsran_dci_ctx_t* pending_dci = nullptr; /// Saves last PDCCH allocation, in case it needs to be aborted +}; + } // namespace sched_nr_impl } // namespace srsenb diff --git a/srsgnb/hdr/stack/mac/sched_nr_rb.h b/srsgnb/hdr/stack/mac/sched_nr_rb.h index d3091d17e..f7dacea42 100644 --- a/srsgnb/hdr/stack/mac/sched_nr_rb.h +++ b/srsgnb/hdr/stack/mac/sched_nr_rb.h @@ -207,6 +207,23 @@ public: uint32_t prb_to_rbg_idx(uint32_t prb_idx) const; + bwp_rb_bitmap& operator|=(const bwp_rb_bitmap& other) + { + prbs_ |= other.prbs_; + rbgs_ |= other.rbgs_; + return *this; + } + bwp_rb_bitmap& operator|=(const rbg_bitmap& other) + { + add(other); + return *this; + } + bwp_rb_bitmap& operator|=(const prb_bitmap& other) + { + add(other); + return *this; + } + private: prb_bitmap prbs_; rbg_bitmap rbgs_; @@ -219,6 +236,12 @@ private: void add_rbgs_to_prbs(const rbg_bitmap& grant); }; +template +bwp_rb_bitmap operator|(const bwp_rb_bitmap& lhs, const Other& rhs) +{ + return bwp_rb_bitmap(lhs) |= rhs; +} + inline prb_interval find_next_empty_interval(const prb_bitmap& mask, size_t start_prb_idx = 0, size_t last_prb_idx = SRSRAN_MAX_PRB_NR) { diff --git a/srsgnb/hdr/stack/mac/sched_nr_sch.h b/srsgnb/hdr/stack/mac/sched_nr_sch.h new file mode 100644 index 000000000..9dd7d39a9 --- /dev/null +++ b/srsgnb/hdr/stack/mac/sched_nr_sch.h @@ -0,0 +1,180 @@ +/** + * + * \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 SRSRAN_SCHED_NR_SCH_H +#define SRSRAN_SCHED_NR_SCH_H + +#include "srsenb/hdr/stack/mac/sched_common.h" +#include "srsgnb/hdr/stack/mac/sched_nr_cfg.h" + +namespace srsenb { + +namespace sched_nr_impl { + +using pdsch_alloc_result = srsran::expected; + +class pdsch_allocator +{ +public: + pdsch_allocator(const bwp_params_t& cfg_, uint32_t sl_index, pdsch_list_t& pdsch_lst); + + /// Get available RBGs for allocation + rbg_bitmap occupied_rbgs() const + { + // Note: in case, RBGs are used, dci format is not 1_0 + return dl_prbs.rbgs(); + } + /// Get available PRBs for allocation + prb_bitmap occupied_prbs(uint32_t ss_id, srsran_dci_format_nr_t dci_fmt) const + { + if (dci_fmt == srsran_dci_format_nr_1_0) { + const srsran_search_space_t* ss = bwp_cfg.get_ss(ss_id); + if (ss != nullptr and SRSRAN_SEARCH_SPACE_IS_COMMON(ss->type)) { + return (dl_prbs | bwp_cfg.dci_fmt_1_0_excluded_prbs(ss->coreset_id)).prbs(); + } + } + return dl_prbs.prbs(); + } + + /// Verifies if the input arguments are valid for an SI allocation and grant doesnt collide with other grants + alloc_result is_si_grant_valid(uint32_t ss_id, const prb_grant& grant) const; + + /// Verifies if the input arguments are valid for an RAR allocation and grant doesnt collide with other grants + alloc_result is_rar_grant_valid(const prb_grant& grant) const; + + /// Verifies if the input arguments are valid for an UE allocation and grant doesnt collide with other grants + alloc_result is_ue_grant_valid(const ue_carrier_params_t& ue, + uint32_t ss_id, + srsran_dci_format_nr_t dci_fmt, + const prb_grant& grant) const; + + /** + * @brief Tries to allocate UE PDSCH grant. Ensures that there are no collisions with other previous PDSCH allocations + * @param ss_id[in] SearchSpaceId used for allocation + * @param dci_fmt[in] Chosen DL DCI format + * @param grant[in] PRBs used for the grant + * @param ue[in] UE carrier parameters + * @param dci[out] DCI where frequency_assignment and time_assignment get stored. + * @return pdsch_t* of allocated PDSCH in case of success. alloc_result error code in case of failure + */ + pdsch_alloc_result alloc_ue_pdsch(uint32_t ss_id, + srsran_dci_format_nr_t dci_fmt, + const prb_grant& grant, + const ue_carrier_params_t& ue, + srsran_dci_dl_nr_t& dci); + + /// Similar to alloc_ue_pdsch, but it doesn't verify if input parameters are valid + pdsch_t& alloc_ue_pdsch_unchecked(uint32_t ss_id, + srsran_dci_format_nr_t dci_fmt, + const prb_grant& grant, + const ue_carrier_params_t& ue, + srsran_dci_dl_nr_t& dci); + + /** + * @brief Tries to allocate SI PDSCH grant. Ensures that there are no collisions with other previous PDSCH allocations + * @param ss_id[in] SearchSpaceId used for allocation + * @param grant[in] PRBs used for the grant + * @param dci[out] DCI where frequency_assignment and time_assignment get stored. + * @return pdsch_t* of allocated PDSCH in case of success. alloc_result error code in case of failure + */ + pdsch_alloc_result alloc_si_pdsch(uint32_t ss_id, const prb_grant& grant, srsran_dci_dl_nr_t& dci); + /// Similar to alloc_si_pdsch, but it doesn't verify if input parameters are valid + pdsch_t& alloc_si_pdsch_unchecked(uint32_t ss_id, const prb_grant& grant, srsran_dci_dl_nr_t& dci); + + /** + * @brief Tries to allocate RAR PDSCH grant. Ensures that there are no collisions with other previous PDSCH + * allocations + * @param grant[in] PRBs used for the grant + * @param dci[out] DCI where frequency_assignment and time_assignment get stored. + * @return pdsch_t* of allocated PDSCH in case of success. alloc_result error code in case of failure + */ + pdsch_alloc_result alloc_rar_pdsch(const prb_grant& grant, srsran_dci_dl_nr_t& dci); + /// Similar to alloc_rar_pdsch, but it doesn't verify if input parameters are valid + pdsch_t& alloc_rar_pdsch_unchecked(const prb_grant& grant, srsran_dci_dl_nr_t& dci); + + /// Cancel last PDSCH allocation + void cancel_last_pdsch(); + + /// Clear all PDSCHs + void reset(); + +private: + alloc_result is_grant_valid_common(srsran_search_space_type_t ss_type, + srsran_dci_format_nr_t dci_fmt, + uint32_t coreset_id, + const prb_grant& grant) const; + pdsch_t& alloc_pdsch_unchecked(uint32_t coreset_id, + srsran_search_space_type_t ss_type, + srsran_dci_format_nr_t dci_fmt, + const prb_grant& grant, + srsran_dci_dl_nr_t& dci); + + const bwp_params_t& bwp_cfg; + uint32_t slot_idx = 0; + + pdsch_list_t& pdschs; + bwp_rb_bitmap dl_prbs; +}; + +using pusch_alloc_result = srsran::expected; + +class pusch_allocator +{ +public: + pusch_allocator(const bwp_params_t& cfg_, uint32_t sl_index, pusch_list_t& pusch_lst); + + /// Get available RBGs for allocation + const rbg_bitmap& occupied_rbgs() const { return ul_prbs.rbgs(); } + /// Get available PRBs for allocation + const prb_bitmap& occupied_prbs() const { return ul_prbs.prbs(); } + + alloc_result has_grant_space(uint32_t nof_grants = 1, bool verbose = true) const; + + /// Checks if provided PDSCH arguments produce a valid PDSCH that fits into cell PRBs and does not collide with other + /// allocations + alloc_result is_grant_valid(srsran_search_space_type_t ss_type, const prb_grant& grant, bool verbose = true) const; + + /** + * @brief Tries to allocate PDSCH grant. Ensures that there are no collisions with other previous PDSCH allocations + * @param ss_type[in] PDCCH chosen search space type + * @param grant[in] PRBs used for the grant + * @param pdcch[out] DCI where frequency_assignment and time_assignment get stored. + * @return pdsch_t object pointer in case of success. alloc_result error code in case of failure + */ + pusch_alloc_result + alloc_pusch(const srsran_search_space_type_t ss_type, const prb_grant& grant, srsran_dci_ul_nr_t& dci); + + /** + * @brief Allocates PDSCH grant without verifying for collisions. Useful to avoid redundant is_grant_valid(...) calls + * @param dci_ctx[in] PDCCH DL DCI context information + * @param grant[in] PRBs used for the grant + * @param pdcch[out] DCI where frequency and time assignment get stored. + */ + pusch_t& alloc_pusch_unchecked(const prb_grant& grant, srsran_dci_ul_nr_t& dci); + + void cancel_last_pusch(); + + void reset(); + +private: + const bwp_params_t& bwp_cfg; + uint32_t slot_idx = 0; + + pusch_list_t& puschs; + bwp_rb_bitmap ul_prbs; +}; + +} // namespace sched_nr_impl + +} // namespace srsenb + +#endif // SRSRAN_SCHED_NR_SCH_H diff --git a/srsgnb/hdr/stack/mac/sched_nr_ue.h b/srsgnb/hdr/stack/mac/sched_nr_ue.h index eba5a995e..52c28b7d9 100644 --- a/srsgnb/hdr/stack/mac/sched_nr_ue.h +++ b/srsgnb/hdr/stack/mac/sched_nr_ue.h @@ -74,10 +74,12 @@ public: uint32_t cc = SRSRAN_MAX_CARRIERS; ue_buffer_manager* parent = nullptr; }; +}; -private: - /// Update of buffers is mutexed when carrier aggreg. is in place - std::mutex mutex; +/// Class containing context of UE that is common to all carriers +struct ue_context_common { + uint32_t pending_dl_bytes = 0; + uint32_t pending_ul_bytes = 0; }; class slot_ue; @@ -88,6 +90,7 @@ public: ue_carrier(uint16_t rnti, const ue_cfg_t& cfg, const cell_params_t& cell_params_, + const ue_context_common& ctxt, const ue_buffer_manager::pdu_builder& pdu_builder_); void set_cfg(const ue_cfg_t& ue_cfg); @@ -111,6 +114,9 @@ public: // metrics mac_ue_metrics_t metrics = {}; + // common context + const ue_context_common& common_ctxt; + private: friend class slot_ue; @@ -136,7 +142,7 @@ public: /// UE state feedback void ul_bsr(uint32_t lcg, uint32_t bsr_val) { buffers.ul_bsr(lcg, bsr_val); } - void ul_sr_info() { last_sr_slot = last_pdcch_slot - TX_ENB_DELAY; } + void ul_sr_info() { last_sr_slot = last_tx_slot - TX_ENB_DELAY; } bool has_ca() const { @@ -155,9 +161,9 @@ private: ue_cfg_t ue_cfg; - slot_point last_pdcch_slot; - slot_point last_sr_slot; - int ul_pending_bytes = 0, dl_pending_bytes = 0; + slot_point last_tx_slot; + slot_point last_sr_slot; + ue_context_common common_ctxt; ue_buffer_manager buffers; }; @@ -166,25 +172,29 @@ class slot_ue { public: slot_ue() = default; - explicit slot_ue(ue_carrier& ue, slot_point slot_tx_, uint32_t dl_pending_bytes, uint32_t ul_pending_bytes); + explicit slot_ue(ue_carrier& ue, slot_point slot_tx_); slot_ue(slot_ue&&) noexcept = default; slot_ue& operator=(slot_ue&&) noexcept = default; bool empty() const { return ue == nullptr; } void release() { ue = nullptr; } const ue_carrier_params_t& cfg() const { return ue->bwp_cfg; } - const ue_carrier_params_t& operator*() const { return ue->bwp_cfg; } const ue_carrier_params_t* operator->() const { return &ue->bwp_cfg; } - // mutable interface to ue_carrier state + /// Find available HARQs dl_harq_proc* find_empty_dl_harq() { return ue->harq_ent.find_empty_dl_harq(); } ul_harq_proc* find_empty_ul_harq() { return ue->harq_ent.find_empty_ul_harq(); } + /// Build PDU with MAC CEs and MAC SDUs void build_pdu(uint32_t rem_bytes, sched_nr_interface::dl_pdu_t& pdu) { ue->pdu_builder.alloc_subpdus(rem_bytes, pdu); } + /// Channel Information Getters + uint32_t dl_cqi() const { return ue->dl_cqi; } + uint32_t ul_cqi() const { return ue->ul_cqi; } + // UE parameters common to all sectors uint32_t dl_bytes = 0, ul_bytes = 0; diff --git a/srsgnb/hdr/stack/mac/ue_nr.h b/srsgnb/hdr/stack/mac/ue_nr.h index 56e70daa3..50e0cf266 100644 --- a/srsgnb/hdr/stack/mac/ue_nr.h +++ b/srsgnb/hdr/stack/mac/ue_nr.h @@ -51,7 +51,7 @@ public: virtual ~ue_nr(); void reset(); - void ue_cfg(const sched_interface::ue_cfg_t& ue_cfg); + void ue_cfg(const sched_nr_interface::ue_cfg_t& ue_cfg); void set_tti(uint32_t tti); uint16_t get_rnti() const { return rnti; } diff --git a/srsgnb/hdr/stack/rrc/rrc_nr.h b/srsgnb/hdr/stack/rrc/rrc_nr.h index 07c504dc2..9c1bef2f8 100644 --- a/srsgnb/hdr/stack/rrc/rrc_nr.h +++ b/srsgnb/hdr/stack/rrc/rrc_nr.h @@ -35,7 +35,6 @@ #include "srsran/common/timeout.h" #include "srsran/interfaces/enb_pdcp_interfaces.h" #include "srsran/interfaces/enb_rlc_interfaces.h" -#include "srsran/interfaces/enb_rrc_interfaces.h" #include "srsran/interfaces/enb_x2_interfaces.h" #include "srsran/interfaces/gnb_interfaces.h" #include "srsran/interfaces/gnb_mac_interfaces.h" diff --git a/srsgnb/src/stack/mac/CMakeLists.txt b/srsgnb/src/stack/mac/CMakeLists.txt index efe88e38f..0eefa7671 100644 --- a/srsgnb/src/stack/mac/CMakeLists.txt +++ b/srsgnb/src/stack/mac/CMakeLists.txt @@ -26,6 +26,7 @@ set(SOURCES mac_nr.cc sched_nr_grant_allocator.cc sched_nr_harq.cc sched_nr_pdcch.cc + sched_nr_sch.cc sched_nr_cfg.cc sched_nr_helpers.cc sched_nr_bwp.cc @@ -39,4 +40,4 @@ add_library(srsgnb_mac STATIC ${SOURCES}) target_link_libraries(srsgnb_mac srsenb_mac_common srsran_mac) include_directories(${PROJECT_SOURCE_DIR}) -add_subdirectory(test) \ No newline at end of file +add_subdirectory(test) diff --git a/srsgnb/src/stack/mac/sched_nr_bwp.cc b/srsgnb/src/stack/mac/sched_nr_bwp.cc index a1585704b..dbc183c1a 100644 --- a/srsgnb/src/stack/mac/sched_nr_bwp.cc +++ b/srsgnb/src/stack/mac/sched_nr_bwp.cc @@ -34,8 +34,8 @@ alloc_result ra_sched::allocate_pending_rar(bwp_slot_allocator& slot_grid, const pending_rar_t& rar, uint32_t& nof_grants_alloc) { const uint32_t rar_aggr_level = 2; - prb_bitmap prbs = slot_grid.res_grid()[slot_grid.get_pdcch_tti()].used_prbs(bwp_cfg->cfg.pdcch.ra_search_space.id, - srsran_dci_format_nr_1_0); + prb_bitmap prbs = slot_grid.res_grid()[slot_grid.get_pdcch_tti()].pdschs.occupied_prbs( + bwp_cfg->cfg.pdcch.ra_search_space.id, srsran_dci_format_nr_1_0); alloc_result ret = alloc_result::other_cause; srsran::const_span msg3_grants{rar.msg3_grant}; diff --git a/srsgnb/src/stack/mac/sched_nr_cfg.cc b/srsgnb/src/stack/mac/sched_nr_cfg.cc index 48137534a..b9d31a720 100644 --- a/srsgnb/src/stack/mac/sched_nr_cfg.cc +++ b/srsgnb/src/stack/mac/sched_nr_cfg.cc @@ -53,14 +53,34 @@ bwp_params_t::bwp_params_t(const cell_cfg_t& cell, const sched_args_t& sched_cfg cc(cc_), bwp_id(bwp_id_), cfg(cell.bwps[bwp_id_]), - logger(srslog::fetch_basic_logger(sched_cfg_.logger_name)) + logger(srslog::fetch_basic_logger(sched_cfg_.logger_name)), + cached_empty_prb_mask(cell.bwps[bwp_id_].rb_width, + cell.bwps[bwp_id_].start_rb, + cell.bwps[bwp_id_].pdsch.rbg_size_cfg_1) { srsran_assert(cfg.pdcch.ra_search_space_present, "BWPs without RA search space not supported"); const uint32_t ra_coreset_id = cfg.pdcch.ra_search_space.coreset_id; P = get_P(cfg.rb_width, cfg.pdsch.rbg_size_cfg_1); N_rbg = get_nof_rbgs(cfg.rb_width, cfg.start_rb, cfg.pdsch.rbg_size_cfg_1); - cached_empty_prb_mask.resize(cfg.rb_width); + + for (const srsran_coreset_t& cs : view_active_coresets(cfg.pdcch)) { + coresets.emplace(cs.id); + uint32_t rb_start = srsran_coreset_start_rb(&cs); + coresets[cs.id].prb_limits = prb_interval{rb_start, rb_start + srsran_coreset_get_bw(&cs)}; + coresets[cs.id].usable_common_ss_prb_mask = cached_empty_prb_mask; + + // TS 38.214, 5.1.2.2 - For DCI format 1_0 and common search space, lowest RB of the CORESET is the RB index = 0 + coresets[cs.id].usable_common_ss_prb_mask |= prb_interval(0, rb_start); + coresets[cs.id].dci_1_0_prb_limits = prb_interval{rb_start, cfg.rb_width}; + + // TS 38.214, 5.1.2.2.2 - when DCI format 1_0, common search space and CORESET#0 is configured for the cell, + // RA type 1 allocs shall be within the CORESET#0 region + if (cfg.pdcch.coreset_present[0]) { + coresets[cs.id].dci_1_0_prb_limits = coresets[cs.id].prb_limits; + coresets[cs.id].usable_common_ss_prb_mask |= prb_interval(coresets[cs.id].prb_limits.stop(), cfg.rb_width); + } + } // Derive params of individual slots uint32_t nof_slots = SRSRAN_NSLOTS_PER_FRAME_NR(cfg.numerology_idx); @@ -117,16 +137,6 @@ bwp_params_t::bwp_params_t(const cell_cfg_t& cell, const sched_args_t& sched_cfg ss_cce_list[sl][agg_idx].resize(n); } } - - if (SRSRAN_SEARCH_SPACE_IS_COMMON(ss.type)) { - used_common_prb_masks.emplace(ss_id, cached_empty_prb_mask); - uint32_t coreset_start = srsran_coreset_start_rb(&cfg.pdcch.coreset[ss.coreset_id]); - used_common_prb_masks[ss_id].fill(0, coreset_start, true); - if (ss.coreset_id == 0) { - uint32_t coreset0_bw = srsran_coreset_get_bw(&cfg.pdcch.coreset[0]); - used_common_prb_masks[ss_id].fill(coreset_start + coreset0_bw, cfg.rb_width, true); - } - } } } @@ -149,7 +159,7 @@ sched_params_t::sched_params_t(const sched_args_t& sched_cfg_) : sched_cfg(sched /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ue_carrier_params_t::ue_carrier_params_t(uint16_t rnti_, const bwp_params_t& bwp_cfg_, const ue_cfg_t& uecfg_) : - rnti(rnti_), cc(bwp_cfg_.cc), cfg_(&uecfg_), bwp_cfg(&bwp_cfg_) + rnti(rnti_), cc(bwp_cfg_.cc), cfg_(&uecfg_), bwp_cfg(&bwp_cfg_), cached_dci_cfg(uecfg_.phy_cfg.get_dci_cfg()) { std::fill(ss_id_to_cce_idx.begin(), ss_id_to_cce_idx.end(), SRSRAN_UE_DL_NR_MAX_NOF_SEARCH_SPACE); const auto& pdcch = phy().pdcch; @@ -166,5 +176,29 @@ ue_carrier_params_t::ue_carrier_params_t(uint16_t rnti_, const bwp_params_t& bwp } } +int ue_carrier_params_t::find_ss_id(srsran_dci_format_nr_t dci_fmt) const +{ + static const uint32_t aggr_idx = 2; // TODO: Make it dynamic + static const srsran_rnti_type_t rnti_type = srsran_rnti_type_c; // TODO: Use TC-RNTI for Msg4 + + auto active_ss_lst = view_active_search_spaces(phy().pdcch); + + for (const srsran_search_space_t& ss : active_ss_lst) { + // Prioritize UE-dedicated SearchSpaces + if (ss.type == srsran_search_space_type_ue and ss.nof_candidates[aggr_idx] > 0 and + contains_dci_format(ss, dci_fmt) and is_rnti_type_valid_in_search_space(rnti_type, ss.type)) { + return ss.id; + } + } + // Search Common SearchSpaces + for (const srsran_search_space_t& ss : active_ss_lst) { + if (SRSRAN_SEARCH_SPACE_IS_COMMON(ss.type) and ss.nof_candidates[aggr_idx] > 0 and + contains_dci_format(ss, dci_fmt) and is_rnti_type_valid_in_search_space(rnti_type, ss.type)) { + return ss.id; + } + } + return -1; +} + } // namespace sched_nr_impl } // namespace srsenb \ No newline at end of file diff --git a/srsgnb/src/stack/mac/sched_nr_grant_allocator.cc b/srsgnb/src/stack/mac/sched_nr_grant_allocator.cc index 475719e02..f4e72e486 100644 --- a/srsgnb/src/stack/mac/sched_nr_grant_allocator.cc +++ b/srsgnb/src/stack/mac/sched_nr_grant_allocator.cc @@ -40,7 +40,7 @@ candidate_ss_list_t find_ss(const srsran_pdcch_cfg_nr_t& pdcch, auto contains_dci_fmt = [prio_dcis, aggr_idx](const srsran_search_space_t& ss) { if (ss.nof_candidates[aggr_idx] > 0 and ss.nof_formats > 0) { for (uint32_t i = 0; i < prio_dcis.size(); ++i) { - for (uint32_t j = 0; j < prio_dcis.size(); ++j) { + for (uint32_t j = 0; j < ss.nof_formats; ++j) { if (ss.formats[j] == prio_dcis[i]) { return true; } @@ -85,38 +85,24 @@ candidate_ss_list_t find_ss(const srsran_pdcch_cfg_nr_t& pdcch, //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bwp_slot_grid::bwp_slot_grid(const bwp_params_t& bwp_cfg_, uint32_t slot_idx_) : - dl_prbs(bwp_cfg_.cfg.rb_width, bwp_cfg_.cfg.start_rb, bwp_cfg_.cfg.pdsch.rbg_size_cfg_1), - ul_prbs(bwp_cfg_.cfg.rb_width, bwp_cfg_.cfg.start_rb, bwp_cfg_.cfg.pdsch.rbg_size_cfg_1), slot_idx(slot_idx_), cfg(&bwp_cfg_), + pdcchs(bwp_cfg_, slot_idx_, dl.phy.pdcch_dl, dl.phy.pdcch_ul), + pdschs(bwp_cfg_, slot_idx_, dl.phy.pdsch), + puschs(bwp_cfg_, slot_idx_, ul.pusch), rar_softbuffer(harq_softbuffer_pool::get_instance().get_tx(bwp_cfg_.cfg.rb_width)) -{ - for (uint32_t cs_idx = 0; cs_idx < SRSRAN_UE_DL_NR_MAX_NOF_CORESET; ++cs_idx) { - if (cfg->cfg.pdcch.coreset_present[cs_idx]) { - uint32_t cs_id = cfg->cfg.pdcch.coreset[cs_idx].id; - coresets[cs_id].emplace(*cfg, cs_id, slot_idx_, dl.phy.pdcch_dl, dl.phy.pdcch_ul); - } - } -} +{} void bwp_slot_grid::reset() { - for (auto& coreset : coresets) { - if (coreset.has_value()) { - coreset->reset(); - } - } - dl_prbs.reset(); - ul_prbs.reset(); + pdcchs.reset(); + pdschs.reset(); + puschs.reset(); dl.phy.ssb.clear(); dl.phy.nzp_csi_rs.clear(); - dl.phy.pdcch_dl.clear(); - dl.phy.pdcch_ul.clear(); - dl.phy.pdsch.clear(); dl.data.clear(); dl.rar.clear(); dl.sib_idxs.clear(); - ul.pusch.clear(); ul.pucch.clear(); pending_acks.clear(); } @@ -124,7 +110,7 @@ void bwp_slot_grid::reset() bwp_res_grid::bwp_res_grid(const bwp_params_t& bwp_cfg_) : cfg(&bwp_cfg_) { for (uint32_t sl = 0; sl < slots.capacity(); ++sl) { - slots.emplace_back(*cfg, sl % static_cast(SRSRAN_NSLOTS_PER_FRAME_NR(0u))); + slots.emplace_back(*cfg, sl % static_cast(SRSRAN_NSLOTS_PER_FRAME_NR(bwp_cfg_.cell_cfg.carrier.scs))); } } @@ -140,43 +126,42 @@ alloc_result bwp_slot_allocator::alloc_si(uint32_t aggr_idx, const prb_interval& prbs, tx_harq_softbuffer& softbuffer) { + static const uint32_t ss_id = 0; + static const srsran_dci_format_nr_t dci_fmt = srsran_dci_format_nr_1_0; + bwp_slot_grid& bwp_pdcch_slot = bwp_grid[pdcch_slot]; - alloc_result ret = verify_pdsch_space(bwp_pdcch_slot, bwp_pdcch_slot); + + // Verify there is space in PDSCH + alloc_result ret = bwp_pdcch_slot.pdschs.is_si_grant_valid(ss_id, prbs); if (ret != alloc_result::success) { return ret; } - if (bwp_pdcch_slot.dl_prbs.collides(prbs)) { - return alloc_result::sch_collision; - } - const uint32_t coreset_id = 0; - const uint32_t ss_id = 0; - if (not bwp_pdcch_slot.coresets[coreset_id]->alloc_dci(pdcch_grant_type_t::sib, aggr_idx, ss_id)) { - logger.warning("SCHED: Cannot allocate SIB1 due to lack of PDCCH space."); - return alloc_result::no_cch_space; + // Allocate PDCCH + auto pdcch_result = bwp_pdcch_slot.pdcchs.alloc_si_pdcch(ss_id, aggr_idx); + if (pdcch_result.is_error()) { + logger.warning("SCHED: Cannot allocate SIB due to lack of PDCCH space."); + return pdcch_result.error(); } + pdcch_dl_t& pdcch = *pdcch_result.value(); + + // Allocate PDSCH (no need to verify again if there is space in PDSCH) + pdsch_t& pdsch = bwp_pdcch_slot.pdschs.alloc_si_pdsch_unchecked(ss_id, prbs, pdcch.dci); - // RAR allocation successful. - bwp_pdcch_slot.dl_prbs |= prbs; // Generate DCI for SIB - pdcch_dl_t& pdcch = bwp_pdcch_slot.dl.phy.pdcch_dl.back(); pdcch.dci_cfg.coreset0_bw = srsran_coreset_get_bw(&cfg.cfg.pdcch.coreset[0]); - if (not fill_dci_sib(prbs, si_idx, si_ntx, *bwp_grid.cfg, pdcch.dci)) { - // Cancel on-going PDCCH allocation - bwp_pdcch_slot.coresets[coreset_id]->rem_last_dci(); - return alloc_result::invalid_coderate; - } + pdcch.dci.mcs = 5; + pdcch.dci.rv = 0; + pdcch.dci.sii = si_idx == 0 ? 0 : 1; // Generate PDSCH - bwp_pdcch_slot.dl.phy.pdsch.emplace_back(); - pdsch_t& pdsch = bwp_pdcch_slot.dl.phy.pdsch.back(); srsran_slot_cfg_t slot_cfg; slot_cfg.idx = pdcch_slot.to_uint(); int code = srsran_ra_dl_dci_to_grant_nr( &cfg.cell_cfg.carrier, &slot_cfg, &cfg.cfg.pdsch, &pdcch.dci, &pdsch.sch, &pdsch.sch.grant); if (code != SRSRAN_SUCCESS) { logger.warning("Error generating SIB PDSCH grant."); - bwp_pdcch_slot.coresets[coreset_id]->rem_last_dci(); + bwp_pdcch_slot.pdcchs.cancel_last_pdcch(); bwp_pdcch_slot.dl.phy.pdsch.pop_back(); return alloc_result::other_cause; } @@ -193,28 +178,18 @@ alloc_result bwp_slot_allocator::alloc_rar_and_msg3(uint16_t prb_interval interv, srsran::const_span pending_rachs) { - static const uint32_t msg3_nof_prbs = 3, m = 0; + static const uint32_t msg3_nof_prbs = 3, m = 0; + static const srsran_dci_format_nr_t dci_fmt = srsran_dci_format_nr_1_0; bwp_slot_grid& bwp_pdcch_slot = bwp_grid[pdcch_slot]; slot_point msg3_slot = pdcch_slot + cfg.pusch_ra_list[m].msg3_delay; bwp_slot_grid& bwp_msg3_slot = bwp_grid[msg3_slot]; - alloc_result ret = verify_pusch_space(bwp_msg3_slot, nullptr); + + // Verify there is space in PDSCH for RAR + alloc_result ret = bwp_pdcch_slot.pdschs.is_rar_grant_valid(interv); if (ret != alloc_result::success) { return ret; } - ret = verify_pdsch_space(bwp_pdcch_slot, bwp_pdcch_slot); - if (ret != alloc_result::success) { - return ret; - } - if (not bwp_pdcch_slot.dl.phy.ssb.empty()) { - // TODO: support concurrent PDSCH and SSB - logger.debug("SCHED: skipping RAR allocation. Cause: concurrent PDSCH and SSB not yet supported"); - return alloc_result::no_sch_space; - } - if (pending_rachs.size() > bwp_pdcch_slot.dl.rar.capacity() - bwp_pdcch_slot.dl.rar.size()) { - logger.error("SCHED: Trying to allocate too many Msg3 grants in a single slot (%zd)", pending_rachs.size()); - return alloc_result::invalid_grant_params; - } for (auto& rach : pending_rachs) { auto ue_it = slot_ues.find(rach.temp_crnti); if (ue_it == slot_ues.end()) { @@ -223,54 +198,51 @@ alloc_result bwp_slot_allocator::alloc_rar_and_msg3(uint16_t return alloc_result::no_rnti_opportunity; } } - - // Check DL RB collision - if (bwp_pdcch_slot.dl_prbs.collides(interv)) { - logger.debug("SCHED: Provided RBG mask collides with allocation previously made."); - return alloc_result::sch_collision; + srsran_sanity_check(not bwp_pdcch_slot.dl.rar.full(), "The #RARs should be below #PDSCHs"); + if (not bwp_pdcch_slot.dl.phy.ssb.empty()) { + // TODO: support concurrent PDSCH and SSB + logger.debug("SCHED: skipping RAR allocation. Cause: concurrent PDSCH and SSB not yet supported"); + return alloc_result::no_sch_space; } + // Verify there is space in PUSCH for Msg3s + ret = bwp_msg3_slot.puschs.has_grant_space(pending_rachs.size()); + if (ret != alloc_result::success) { + return ret; + } // Check Msg3 RB collision - uint32_t total_ul_nof_prbs = msg3_nof_prbs * pending_rachs.size(); - uint32_t total_ul_nof_rbgs = srsran::ceil_div(total_ul_nof_prbs, get_P(bwp_grid.nof_prbs(), false)); - prb_interval msg3_rbs = find_empty_interval_of_length(bwp_msg3_slot.ul_prbs.prbs(), total_ul_nof_rbgs); - if (msg3_rbs.length() < total_ul_nof_rbgs) { + uint32_t total_msg3_nof_prbs = msg3_nof_prbs * pending_rachs.size(); + prb_interval all_msg3_rbs = + find_empty_interval_of_length(bwp_msg3_slot.puschs.occupied_prbs(), total_msg3_nof_prbs, 0); + if (all_msg3_rbs.length() < total_msg3_nof_prbs) { logger.debug("SCHED: No space in PUSCH for Msg3."); return alloc_result::sch_collision; } - // Find PDCCH position - const uint32_t coreset_id = cfg.cfg.pdcch.ra_search_space.coreset_id; - const uint32_t search_space_id = cfg.cfg.pdcch.ra_search_space.id; - if (not bwp_pdcch_slot.coresets[coreset_id]->alloc_dci(pdcch_grant_type_t::rar, aggr_idx, search_space_id, nullptr)) { + // Allocate PDCCH position for RAR + auto pdcch_result = bwp_pdcch_slot.pdcchs.alloc_rar_pdcch(ra_rnti, aggr_idx); + if (pdcch_result.is_error()) { // Could not find space in PDCCH - logger.debug("SCHED: No space in PDCCH for DL tx."); - return alloc_result::no_cch_space; + return pdcch_result.error(); } + pdcch_dl_t& pdcch = *pdcch_result.value(); + pdcch.dci_cfg = slot_ues[pending_rachs[0].temp_crnti]->get_dci_cfg(); + pdcch.dci.mcs = 5; - // RAR allocation successful. - bwp_pdcch_slot.dl_prbs |= interv; - // Generate DCI for RAR with given RA-RNTI - pdcch_dl_t& pdcch = bwp_pdcch_slot.dl.phy.pdcch_dl.back(); - if (not fill_dci_rar(interv, ra_rnti, *bwp_grid.cfg, pdcch.dci)) { - // Cancel on-going PDCCH allocation - bwp_pdcch_slot.coresets[coreset_id]->rem_last_dci(); - return alloc_result::invalid_coderate; - } - auto& phy_cfg = slot_ues[pending_rachs[0].temp_crnti]->phy(); - pdcch.dci_cfg = phy_cfg.get_dci_cfg(); - // Generate RAR PDSCH + // Allocate RAR PDSCH + pdsch_t& pdsch = bwp_pdcch_slot.pdschs.alloc_rar_pdsch_unchecked(interv, pdcch.dci); + + // Fill RAR PDSCH content // TODO: Properly fill Msg3 grants - bwp_pdcch_slot.dl.phy.pdsch.emplace_back(); - pdsch_t& pdsch = bwp_pdcch_slot.dl.phy.pdsch.back(); srsran_slot_cfg_t slot_cfg; slot_cfg.idx = pdcch_slot.to_uint(); - bool success = phy_cfg.get_pdsch_cfg(slot_cfg, pdcch.dci, pdsch.sch); - srsran_assert(success, "Error converting DCI to grant"); + int code = srsran_ra_dl_dci_to_grant_nr( + &cfg.cell_cfg.carrier, &slot_cfg, &cfg.cfg.pdsch, &pdcch.dci, &pdsch.sch, &pdsch.sch.grant); + srsran_assert(code == SRSRAN_SUCCESS, "Error converting DCI to grant"); pdsch.sch.grant.tb[0].softbuffer.tx = bwp_pdcch_slot.rar_softbuffer->get(); // Generate Msg3 grants in PUSCH - uint32_t last_msg3 = msg3_rbs.start(); + uint32_t last_msg3 = all_msg3_rbs.start(); const int mcs = 0, max_harq_msg3_retx = 4; slot_cfg.idx = msg3_slot.to_uint(); bwp_pdcch_slot.dl.rar.emplace_back(); @@ -282,42 +254,59 @@ alloc_result bwp_slot_allocator::alloc_rar_and_msg3(uint16_t rar_out.grants.emplace_back(); auto& rar_grant = rar_out.grants.back(); rar_grant.data = grant; + fill_dci_from_cfg(cfg, rar_grant.msg3_dci); + // Fill Msg3 DCI context + rar_grant.msg3_dci.ctx.coreset_id = pdcch.dci.ctx.coreset_id; + rar_grant.msg3_dci.ctx.rnti_type = srsran_rnti_type_tc; + rar_grant.msg3_dci.ctx.rnti = ue->rnti; + rar_grant.msg3_dci.ctx.ss_type = srsran_search_space_type_rar; + rar_grant.msg3_dci.ctx.format = srsran_dci_format_nr_rar; + + // Allocate Msg3 PUSCH allocation prb_interval msg3_interv{last_msg3, last_msg3 + msg3_nof_prbs}; last_msg3 += msg3_nof_prbs; - ue.h_ul = ue.find_empty_ul_harq(); - success = ue.h_ul->new_tx(msg3_slot, msg3_slot, msg3_interv, mcs, max_harq_msg3_retx); - srsran_assert(success, "Failed to allocate Msg3"); - fill_dci_msg3(ue, *bwp_grid.cfg, rar_grant.msg3_dci); + pusch_t& pusch = bwp_msg3_slot.puschs.alloc_pusch_unchecked(msg3_interv, rar_grant.msg3_dci); - // Generate PUSCH - bwp_msg3_slot.ul.pusch.emplace_back(); - pusch_t& pusch = bwp_msg3_slot.ul.pusch.back(); - success = ue->phy().get_pusch_cfg(slot_cfg, rar_grant.msg3_dci, pusch.sch); + // Allocate UL HARQ + ue.h_ul = ue.find_empty_ul_harq(); + srsran_sanity_check(ue.h_ul != nullptr, "Failed to allocate Msg3"); + bool success = ue.h_ul->new_tx(msg3_slot, msg3_interv, mcs, max_harq_msg3_retx, rar_grant.msg3_dci); + srsran_sanity_check(success, "Failed to allocate Msg3"); + + // Generate PUSCH content + success = ue->phy().get_pusch_cfg(slot_cfg, rar_grant.msg3_dci, pusch.sch); srsran_assert(success, "Error converting DCI to PUSCH grant"); pusch.sch.grant.tb[0].softbuffer.rx = ue.h_ul->get_softbuffer().get(); ue.h_ul->set_tbs(pusch.sch.grant.tb[0].tbs); } - bwp_msg3_slot.ul_prbs.add(msg3_rbs); return alloc_result::success; } // ue is the UE (1 only) that will be allocated // func computes the grant allocation for this UE -alloc_result bwp_slot_allocator::alloc_pdsch(slot_ue& ue, prb_grant dl_grant) +alloc_result bwp_slot_allocator::alloc_pdsch(slot_ue& ue, uint32_t ss_id, const prb_grant& dl_grant) { - static const uint32_t aggr_idx = 2; - static const std::array dci_fmt_list{srsran_dci_format_nr_1_1, srsran_dci_format_nr_1_0}; + static const uint32_t aggr_idx = 2; + static const srsran_dci_format_nr_t dci_fmt = srsran_dci_format_nr_1_0; + static const srsran_rnti_type_t rnti_type = srsran_rnti_type_c; bwp_slot_grid& bwp_pdcch_slot = bwp_grid[ue.pdcch_slot]; bwp_slot_grid& bwp_pdsch_slot = bwp_grid[ue.pdsch_slot]; bwp_slot_grid& bwp_uci_slot = bwp_grid[ue.uci_slot]; // UCI : UL control info - alloc_result result = verify_pdsch_space(bwp_pdsch_slot, bwp_pdcch_slot, &bwp_uci_slot); + + // Verify there is space in PDSCH + alloc_result ret = bwp_pdcch_slot.pdschs.is_ue_grant_valid(ue.cfg(), ss_id, dci_fmt, dl_grant); + if (ret != alloc_result::success) { + return ret; + } + + alloc_result result = verify_uci_space(bwp_uci_slot); if (result != alloc_result::success) { return result; } - result = verify_ue_cfg(ue.cfg(), ue.h_dl); - if (result != alloc_result::success) { + if (ue.h_dl == nullptr) { + logger.warning("SCHED: Trying to allocate rnti=0x%x with no available DL HARQs", ue->rnti); return result; } if (not bwp_pdsch_slot.dl.phy.ssb.empty()) { @@ -325,125 +314,91 @@ alloc_result bwp_slot_allocator::alloc_pdsch(slot_ue& ue, prb_grant dl_grant) logger.debug("SCHED: skipping PDSCH allocation. Cause: concurrent PDSCH and SSB not yet supported"); return alloc_result::no_sch_space; } - if (bwp_pdsch_slot.dl_prbs.collides(dl_grant)) { - return alloc_result::sch_collision; - } - // Find space in PUCCH + // Check space in PUCCH/PUSCH for UCI // TODO - // Choose SearchSpace + DCI format - srsran_rnti_type_t rnti_type = - ue->ue_cfg().ue_bearers[1].direction == mac_lc_ch_cfg_t::IDLE ? srsran_rnti_type_tc : srsran_rnti_type_c; - // Choose the ss_id the highest number of candidates - candidate_ss_list_t ss_candidates = find_ss(ue->phy().pdcch, aggr_idx, rnti_type, dci_fmt_list); - if (ss_candidates.empty()) { - // Could not find space in PDCCH - logger.warning("SCHED: No PDCCH candidates for any of the rnti=0x%x search spaces", ue->rnti); - return alloc_result::no_cch_space; - } - const srsran_search_space_t& ss = *ss_candidates[0]; - // Find space and allocate PDCCH - uint32_t coreset_id = ss.coreset_id; - if (not bwp_pdcch_slot.coresets[coreset_id]->alloc_dci(pdcch_grant_type_t::dl_data, aggr_idx, ss.id, &ue.cfg())) { + auto pdcch_result = bwp_pdcch_slot.pdcchs.alloc_dl_pdcch(rnti_type, ss_id, aggr_idx, ue.cfg()); + if (pdcch_result.is_error()) { // Could not find space in PDCCH - return alloc_result::no_cch_space; + return pdcch_result.error(); } + pdcch_dl_t& pdcch = *pdcch_result.value(); + pdcch.dci_cfg = ue->get_dci_cfg(); + pdcch.dci.pucch_resource = 0; + pdcch.dci.dai = std::count_if(bwp_uci_slot.pending_acks.begin(), + bwp_uci_slot.pending_acks.end(), + [&ue](const harq_ack_t& p) { return p.res.rnti == ue->rnti; }); + pdcch.dci.dai %= 4; - // Update PRB grant based on the start and end of CORESET RBs - reduce_to_dl_coreset_bw(cfg, ss.id, srsran_dci_format_nr_1_0, dl_grant); + // Allocate PDSCH + pdsch_t& pdsch = bwp_pdcch_slot.pdschs.alloc_ue_pdsch_unchecked(ss_id, dci_fmt, dl_grant, ue.cfg(), pdcch.dci); // Allocate HARQ int mcs = ue->fixed_pdsch_mcs(); if (ue.h_dl->empty()) { - bool ret = ue.h_dl->new_tx(ue.pdsch_slot, ue.uci_slot, dl_grant, mcs, 4); - srsran_assert(ret, "Failed to allocate DL HARQ"); + bool success = ue.h_dl->new_tx(ue.pdsch_slot, ue.uci_slot, dl_grant, mcs, 4, pdcch.dci); + srsran_assert(success, "Failed to allocate DL HARQ"); } else { - bool ret = ue.h_dl->new_retx(ue.pdsch_slot, ue.uci_slot, dl_grant); - mcs = ue.h_dl->mcs(); - srsran_assert(ret, "Failed to allocate DL HARQ retx"); + bool success = ue.h_dl->new_retx(ue.pdsch_slot, ue.uci_slot, dl_grant, pdcch.dci); + mcs = ue.h_dl->mcs(); + srsran_assert(success, "Failed to allocate DL HARQ retx"); } - // Allocation Successful - - const static float max_R = 0.93; + srsran_slot_cfg_t slot_cfg; + slot_cfg.idx = ue.pdsch_slot.to_uint(); + const static float max_R = 0.95; while (true) { - // Generate PDCCH - pdcch_dl_t& pdcch = bwp_pdcch_slot.dl.phy.pdcch_dl.back(); - fill_dl_dci_ue_fields(ue, *bwp_grid.cfg, ss.id, pdcch.dci.ctx.location, pdcch.dci); - pdcch.dci.pucch_resource = 0; - pdcch.dci.dai = std::count_if(bwp_uci_slot.pending_acks.begin(), - bwp_uci_slot.pending_acks.end(), - [&ue](const harq_ack_t& p) { return p.res.rnti == ue->rnti; }); - pdcch.dci.dai %= 4; - pdcch.dci_cfg = ue->phy().get_dci_cfg(); - - // Generate PUCCH - bwp_uci_slot.pending_acks.emplace_back(); - bwp_uci_slot.pending_acks.back().phy_cfg = &ue->phy(); - srsran_assert(ue->phy().get_pdsch_ack_resource(pdcch.dci, bwp_uci_slot.pending_acks.back().res), - "Error getting ack resource"); - // Generate PDSCH - bwp_pdsch_slot.dl_prbs |= dl_grant; - bwp_pdsch_slot.dl.phy.pdsch.emplace_back(); - pdsch_t& pdsch = bwp_pdsch_slot.dl.phy.pdsch.back(); - srsran_slot_cfg_t slot_cfg; - slot_cfg.idx = ue.pdsch_slot.to_uint(); - bool ret = ue->phy().get_pdsch_cfg(slot_cfg, pdcch.dci, pdsch.sch); - srsran_assert(ret, "Error converting DCI to grant"); - - pdsch.sch.grant.tb[0].softbuffer.tx = ue.h_dl->get_softbuffer().get(); - pdsch.data[0] = ue.h_dl->get_tx_pdu()->get(); - if (ue.h_dl->nof_retx() == 0) { - ue.h_dl->set_tbs(pdsch.sch.grant.tb[0].tbs); // update HARQ with correct TBS - } else { + bool success = ue->phy().get_pdsch_cfg(slot_cfg, pdcch.dci, pdsch.sch); + srsran_assert(success, "Error converting DCI to grant"); + if (ue.h_dl->nof_retx() != 0) { srsran_assert(pdsch.sch.grant.tb[0].tbs == (int)ue.h_dl->tbs(), "The TBS did not remain constant in retx"); } - if (ue.h_dl->nof_retx() > 0 or bwp_pdsch_slot.dl.phy.pdsch.back().sch.grant.tb[0].R_prime < max_R or mcs <= 0) { + if (ue.h_dl->nof_retx() > 0 or pdsch.sch.grant.tb[0].R_prime < max_R or mcs <= 0) { break; } // Decrease MCS if first tx and rate is too high mcs--; - ue.h_dl->set_mcs(mcs); - bwp_pdsch_slot.dl.phy.pdsch.pop_back(); - bwp_uci_slot.pending_acks.pop_back(); + pdcch.dci.mcs = mcs; } if (mcs == 0) { - logger.warning("Couldn't find mcs that leads to R<0.9"); + logger.warning("Couldn't find mcs that leads to R<0.95"); } + ue.h_dl->set_mcs(mcs); + ue.h_dl->set_tbs(pdsch.sch.grant.tb[0].tbs); // set HARQ TBS + pdsch.sch.grant.tb[0].softbuffer.tx = ue.h_dl->get_softbuffer().get(); + pdsch.data[0] = ue.h_dl->get_tx_pdu()->get(); // Select scheduled LCIDs and update UE buffer state bwp_pdsch_slot.dl.data.emplace_back(); ue.build_pdu(ue.h_dl->tbs(), bwp_pdsch_slot.dl.data.back()); + // Generate PUCCH + bwp_uci_slot.pending_acks.emplace_back(); + bwp_uci_slot.pending_acks.back().phy_cfg = &ue->phy(); + srsran_assert(ue->phy().get_pdsch_ack_resource(pdcch.dci, bwp_uci_slot.pending_acks.back().res), + "Error getting ack resource"); + return alloc_result::success; } -alloc_result bwp_slot_allocator::alloc_pusch(slot_ue& ue, prb_grant ul_grant) +alloc_result bwp_slot_allocator::alloc_pusch(slot_ue& ue, const prb_grant& ul_grant) { static const uint32_t aggr_idx = 2; static const std::array dci_fmt_list{srsran_dci_format_nr_0_1, srsran_dci_format_nr_0_0}; + static const srsran_rnti_type_t rnti_type = srsran_rnti_type_c; - auto& bwp_pdcch_slot = bwp_grid[ue.pdcch_slot]; - auto& bwp_pusch_slot = bwp_grid[ue.pusch_slot]; - alloc_result ret = verify_pusch_space(bwp_pusch_slot, &bwp_pdcch_slot); - if (ret != alloc_result::success) { - return ret; - } - ret = verify_ue_cfg(ue.cfg(), ue.h_ul); - if (ret != alloc_result::success) { - return ret; - } - pdcch_ul_list_t& pdcchs = bwp_pdcch_slot.dl.phy.pdcch_ul; - if (bwp_pusch_slot.ul_prbs.collides(ul_grant)) { - return alloc_result::sch_collision; + auto& bwp_pdcch_slot = bwp_grid[ue.pdcch_slot]; + auto& bwp_pusch_slot = bwp_grid[ue.pusch_slot]; + + if (ue.h_ul == nullptr) { + logger.warning("SCHED: Trying to allocate rnti=0x%x with no available UL HARQs", ue->rnti); + return alloc_result::no_rnti_opportunity; } // Choose SearchSpace + DCI format - srsran_rnti_type_t rnti_type = - ue->ue_cfg().ue_bearers[1].direction == mac_lc_ch_cfg_t::IDLE ? srsran_rnti_type_tc : srsran_rnti_type_c; candidate_ss_list_t ss_candidates = find_ss(ue->phy().pdcch, aggr_idx, rnti_type, dci_fmt_list); if (ss_candidates.empty()) { // Could not find space in PDCCH @@ -452,32 +407,35 @@ alloc_result bwp_slot_allocator::alloc_pusch(slot_ue& ue, prb_grant ul_grant) } const srsran_search_space_t& ss = *ss_candidates[0]; - uint32_t coreset_id = ss.coreset_id; - if (not bwp_pdcch_slot.coresets[coreset_id].value().alloc_dci( - pdcch_grant_type_t::ul_data, aggr_idx, ss.id, &ue.cfg())) { + // Verify if PUSCH allocation is valid + alloc_result ret = bwp_pusch_slot.puschs.is_grant_valid(ss.type, ul_grant); + if (ret != alloc_result::success) { + return ret; + } + + auto pdcch_result = bwp_pdcch_slot.pdcchs.alloc_ul_pdcch(ss.id, aggr_idx, ue.cfg()); + if (pdcch_result.is_error()) { // Could not find space in PDCCH - return alloc_result::no_cch_space; + return pdcch_result.error(); } // Allocation Successful + pdcch_ul_t& pdcch = *pdcch_result.value(); + pdcch.dci_cfg = ue->get_dci_cfg(); + + // Allocate PUSCH + pusch_t& pusch = bwp_pusch_slot.puschs.alloc_pusch_unchecked(ul_grant, pdcch.dci); if (ue.h_ul->empty()) { int mcs = ue->fixed_pusch_mcs(); - bool success = ue.h_ul->new_tx(ue.pusch_slot, ue.pusch_slot, ul_grant, mcs, ue->ue_cfg().maxharq_tx); + bool success = ue.h_ul->new_tx(ue.pusch_slot, ul_grant, mcs, ue->ue_cfg().maxharq_tx, pdcch.dci); srsran_assert(success, "Failed to allocate UL HARQ"); } else { - bool success = ue.h_ul->new_retx(ue.pusch_slot, ue.pusch_slot, ul_grant); + bool success = ue.h_ul->new_retx(ue.pusch_slot, ul_grant, pdcch.dci); srsran_assert(success, "Failed to allocate UL HARQ retx"); } - // Generate PDCCH - pdcch_ul_t& pdcch = pdcchs.back(); - fill_ul_dci_ue_fields(ue, *bwp_grid.cfg, ss.id, pdcch.dci.ctx.location, pdcch.dci); - pdcch.dci_cfg = ue->phy().get_dci_cfg(); - // Generate PUSCH - bwp_pusch_slot.ul_prbs |= ul_grant; - bwp_pusch_slot.ul.pusch.emplace_back(); - pusch_t& pusch = bwp_pusch_slot.ul.pusch.back(); + // Generate PUSCH content srsran_slot_cfg_t slot_cfg; slot_cfg.idx = ue.pusch_slot.to_uint(); pusch.pid = ue.h_ul->pid; @@ -493,67 +451,26 @@ alloc_result bwp_slot_allocator::alloc_pusch(slot_ue& ue, prb_grant ul_grant) return alloc_result::success; } -alloc_result bwp_slot_allocator::verify_pdsch_space(bwp_slot_grid& pdsch_grid, - bwp_slot_grid& pdcch_grid, - bwp_slot_grid* uci_grid) const +alloc_result bwp_slot_allocator::verify_uci_space(const bwp_slot_grid& uci_grid) const { - if (not pdsch_grid.is_dl() or not pdcch_grid.is_dl()) { - logger.warning("SCHED: Trying to allocate PDSCH in TDD non-DL slot index=%d", pdsch_grid.slot_idx); - return alloc_result::no_sch_space; - } - if (pdcch_grid.dl.phy.pdcch_dl.full()) { - logger.warning("SCHED: Maximum number of DL PDCCH allocations reached"); - return alloc_result::no_cch_space; - } - if (pdsch_grid.dl.phy.pdsch.full()) { - logger.warning("SCHED: Maximum number of DL PDSCH grants reached"); - return alloc_result::no_sch_space; - } - if (uci_grid != nullptr) { - if (uci_grid->pending_acks.full()) { - logger.warning("SCHED: No space for ACK."); - return alloc_result::no_grant_space; - } - } - return alloc_result::success; -} - -alloc_result bwp_slot_allocator::verify_pusch_space(bwp_slot_grid& pusch_grid, bwp_slot_grid* pdcch_grid) const -{ - if (not pusch_grid.is_ul()) { - logger.warning("SCHED: Trying to allocate PUSCH in TDD non-UL slot index=%d", pusch_grid.slot_idx); - return alloc_result::no_sch_space; - } - if (pdcch_grid != nullptr) { - // DCI needed - if (not pdcch_grid->is_dl()) { - logger.warning("SCHED: Trying to allocate PDCCH in TDD non-DL slot index=%d", pdcch_grid->slot_idx); - return alloc_result::no_sch_space; - } - if (pdcch_grid->dl.phy.pdcch_ul.full()) { - logger.warning("SCHED: Maximum number of PUSCH allocations reached"); - return alloc_result::no_grant_space; - } - } - if (pusch_grid.ul.pusch.full()) { - logger.warning("SCHED: Maximum number of PUSCH allocations reached"); + if (uci_grid.pending_acks.full()) { + logger.warning("SCHED: No space for ACK."); return alloc_result::no_grant_space; } return alloc_result::success; } -alloc_result bwp_slot_allocator::verify_ue_cfg(const ue_carrier_params_t& ue_cfg, harq_proc* harq) const +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +prb_grant find_optimal_dl_grant(bwp_slot_allocator& slot_alloc, const slot_ue& ue, uint32_t ss_id) { - if (ue_cfg.active_bwp().bwp_id != cfg.bwp_id) { - logger.warning( - "SCHED: Trying to allocate rnti=0x%x in inactive BWP id=%d", ue_cfg.rnti, ue_cfg.active_bwp().bwp_id); - return alloc_result::no_rnti_opportunity; - } - if (harq == nullptr) { - logger.warning("SCHED: Trying to allocate rnti=0x%x with no available HARQs", ue_cfg.rnti); - return alloc_result::no_rnti_opportunity; - } - return alloc_result::success; + static const srsran_dci_format_nr_t dci_fmt = srsran_dci_format_nr_1_0; // TODO: Support more DCI formats + + prb_bitmap used_prb_mask = slot_alloc.occupied_dl_prbs(ue.pdsch_slot, ss_id, dci_fmt); + + prb_interval prb_interv = find_empty_interval_of_length(used_prb_mask, used_prb_mask.size(), 0); + + return prb_interv; } } // namespace sched_nr_impl diff --git a/srsgnb/src/stack/mac/sched_nr_harq.cc b/srsgnb/src/stack/mac/sched_nr_harq.cc index 18ce0a659..a2ac420a5 100644 --- a/srsgnb/src/stack/mac/sched_nr_harq.cc +++ b/srsgnb/src/stack/mac/sched_nr_harq.cc @@ -124,14 +124,76 @@ dl_harq_proc::dl_harq_proc(uint32_t id_, uint32_t nprb) : harq_proc(id_), softbuffer(harq_softbuffer_pool::get_instance().get_tx(nprb)), pdu(srsran::make_byte_buffer()) {} -bool dl_harq_proc::new_tx(slot_point slot_tx, - slot_point slot_ack, - const prb_grant& grant, - uint32_t mcs, - uint32_t max_retx) +void dl_harq_proc::fill_dci(srsran_dci_dl_nr_t& dci) { - if (harq_proc::new_tx(slot_tx, slot_ack, grant, mcs, max_retx)) { + const static uint32_t rv_idx[4] = {0, 2, 3, 1}; + + dci.pid = pid; + dci.ndi = ndi(); + dci.mcs = mcs(); + dci.rv = rv_idx[nof_retx() % 4]; + if (dci.ctx.format == srsran_dci_format_nr_1_0) { + dci.harq_feedback = (slot_ack - slot_tx) - 1; + } else { + dci.harq_feedback = slot_tx.to_uint(); + } +} + +bool dl_harq_proc::new_tx(slot_point slot_tx, + slot_point slot_ack, + const prb_grant& grant, + uint32_t mcs_, + uint32_t max_retx, + srsran_dci_dl_nr_t& dci) +{ + const static uint32_t rv_idx[4] = {0, 2, 3, 1}; + + if (harq_proc::new_tx(slot_tx, slot_ack, grant, mcs_, max_retx)) { pdu->clear(); + fill_dci(dci); + return true; + } + return false; +} + +bool dl_harq_proc::new_retx(slot_point slot_tx, slot_point slot_ack, const prb_grant& grant, srsran_dci_dl_nr_t& dci) +{ + if (harq_proc::new_retx(slot_tx, slot_ack, grant)) { + fill_dci(dci); + return true; + } + return false; +} + +void ul_harq_proc::fill_dci(srsran_dci_ul_nr_t& dci) +{ + const static uint32_t rv_idx[4] = {0, 2, 3, 1}; + + dci.pid = pid; + dci.ndi = ndi(); + dci.mcs = mcs(); + dci.rv = rv_idx[nof_retx() % 4]; +} + +bool ul_harq_proc::new_tx(slot_point slot_tx, + const prb_grant& grant, + uint32_t mcs_, + uint32_t max_retx, + srsran_dci_ul_nr_t& dci) +{ + const static uint32_t rv_idx[4] = {0, 2, 3, 1}; + + if (harq_proc::new_tx(slot_tx, slot_tx, grant, mcs_, max_retx)) { + fill_dci(dci); + return true; + } + return false; +} + +bool ul_harq_proc::new_retx(slot_point slot_tx, const prb_grant& grant, srsran_dci_ul_nr_t& dci) +{ + if (harq_proc::new_retx(slot_tx, slot_tx, grant)) { + fill_dci(dci); return true; } return false; diff --git a/srsgnb/src/stack/mac/sched_nr_helpers.cc b/srsgnb/src/stack/mac/sched_nr_helpers.cc index a5182e12a..a4ad81bdb 100644 --- a/srsgnb/src/stack/mac/sched_nr_helpers.cc +++ b/srsgnb/src/stack/mac/sched_nr_helpers.cc @@ -28,168 +28,6 @@ namespace srsenb { namespace sched_nr_impl { -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void reduce_to_dl_coreset_bw(const bwp_params_t& bwp_cfg, - uint32_t ss_id, - srsran_dci_format_nr_t dci_fmt, - prb_grant& grant) -{ - const srsran_search_space_t& ss = - dci_fmt == srsran_dci_format_nr_rar ? bwp_cfg.cfg.pdcch.ra_search_space : bwp_cfg.cfg.pdcch.search_space[ss_id]; - if (not SRSRAN_SEARCH_SPACE_IS_COMMON(ss.type)) { - return; - } - uint32_t rb_start = 0, nof_prbs = bwp_cfg.nof_prb(); - if (dci_fmt == srsran_dci_format_nr_1_0) { - rb_start = srsran_coreset_start_rb(&bwp_cfg.cfg.pdcch.coreset[ss.coreset_id]); - } - if (ss.coreset_id == 0) { - nof_prbs = srsran_coreset_get_bw(&bwp_cfg.cfg.pdcch.coreset[0]); - } - grant &= prb_interval{rb_start, rb_start + nof_prbs}; -} - -void fill_dci_common(const bwp_params_t& bwp_cfg, srsran_dci_dl_nr_t& dci) -{ - dci.bwp_id = bwp_cfg.bwp_id; - dci.cc_id = bwp_cfg.cc; - dci.tpc = 1; - dci.coreset0_bw = bwp_cfg.cfg.pdcch.coreset_present[0] ? srsran_coreset_get_bw(&bwp_cfg.cfg.pdcch.coreset[0]) : 0; -} - -void fill_dci_common(const bwp_params_t& bwp_cfg, srsran_dci_ul_nr_t& dci) -{ - dci.bwp_id = bwp_cfg.bwp_id; - dci.cc_id = bwp_cfg.cc; - dci.tpc = 1; -} - -template -void fill_dci_harq(const slot_ue& ue, DciDlOrUl& dci) -{ - const static uint32_t rv_idx[4] = {0, 2, 3, 1}; - - harq_proc* h = std::is_same::value ? static_cast(ue.h_dl) - : static_cast(ue.h_ul); - - dci.pid = h->pid; - dci.ndi = h->ndi(); - dci.mcs = h->mcs(); - dci.rv = rv_idx[h->nof_retx() % 4]; -} - -void fill_dci_grant(const bwp_params_t& bwp_cfg, const prb_grant& grant, srsran_dci_dl_nr_t& dci) -{ - dci.time_domain_assigment = 0; - if (grant.is_alloc_type0()) { - srsran_assert(not SRSRAN_SEARCH_SPACE_IS_COMMON(dci.ctx.ss_type), "AllocType0 for common search space"); - dci.freq_domain_assigment = grant.rbgs().to_uint64(); - } else { - uint32_t rb_start = 0, nof_prb = bwp_cfg.nof_prb(); - if (dci.ctx.format == srsran_dci_format_nr_1_0 && SRSRAN_SEARCH_SPACE_IS_COMMON(dci.ctx.ss_type)) { - rb_start = dci.ctx.coreset_start_rb; - } - if (dci.ctx.coreset_id == 0 and SRSRAN_SEARCH_SPACE_IS_COMMON(dci.ctx.ss_type)) { - nof_prb = dci.coreset0_bw; - } - srsran_assert(grant.prbs().start() >= rb_start, "Invalid PRB index=%d < %d", grant.prbs().start(), rb_start); - uint32_t grant_start = grant.prbs().start() - rb_start; - dci.freq_domain_assigment = srsran_ra_nr_type1_riv(nof_prb, grant_start, grant.prbs().length()); - } -} - -void fill_dci_grant(const bwp_params_t& bwp_cfg, const prb_grant& grant, srsran_dci_ul_nr_t& dci) -{ - dci.time_domain_assigment = 0; - if (grant.is_alloc_type0()) { - dci.freq_domain_assigment = grant.rbgs().to_uint64(); - } else { - uint32_t nof_prb = bwp_cfg.nof_prb(); - dci.freq_domain_assigment = srsran_ra_nr_type1_riv(nof_prb, grant.prbs().start(), grant.prbs().length()); - } -} - -void fill_rar_dci_context(const bwp_params_t& bwp_cfg, uint16_t ra_rnti, srsran_dci_ctx_t& dci_ctx) -{ - uint32_t cs_id = bwp_cfg.cfg.pdcch.ra_search_space.coreset_id; - - dci_ctx.format = srsran_dci_format_nr_1_0; - dci_ctx.ss_type = srsran_search_space_type_common_1; - dci_ctx.rnti_type = srsran_rnti_type_ra; - dci_ctx.rnti = ra_rnti; - dci_ctx.coreset_id = cs_id; - dci_ctx.coreset_start_rb = srsran_coreset_start_rb(&bwp_cfg.cfg.pdcch.coreset[cs_id]); -} - -bool fill_dci_rar(prb_interval interv, uint16_t ra_rnti, const bwp_params_t& bwp_cfg, srsran_dci_dl_nr_t& dci) -{ - fill_rar_dci_context(bwp_cfg, ra_rnti, dci.ctx); - - dci.mcs = 5; - fill_dci_common(bwp_cfg, dci); - fill_dci_grant(bwp_cfg, interv, dci); - // TODO: Fill - - return true; -} - -bool fill_dci_msg3(const slot_ue& ue, const bwp_params_t& bwp_cfg, srsran_dci_ul_nr_t& msg3_dci) -{ - // Fill DCI context - msg3_dci.ctx.coreset_id = ue->phy().pdcch.ra_search_space.coreset_id; - msg3_dci.ctx.rnti_type = srsran_rnti_type_tc; - msg3_dci.ctx.rnti = ue->rnti; - msg3_dci.ctx.ss_type = srsran_search_space_type_rar; - if (ue.h_ul->nof_retx() == 0) { - msg3_dci.ctx.format = srsran_dci_format_nr_rar; - } else { - msg3_dci.ctx.format = srsran_dci_format_nr_0_0; - } - - // Fill DCI content - fill_dci_common(bwp_cfg, msg3_dci); - fill_dci_harq(ue, msg3_dci); - fill_dci_grant(bwp_cfg, ue.h_ul->prbs(), msg3_dci); - - return true; -} - -void fill_dl_dci_ue_fields(const slot_ue& ue, - const bwp_params_t& bwp_cfg, - uint32_t ss_id, - srsran_dci_location_t dci_pos, - srsran_dci_dl_nr_t& dci) -{ - // Note: DCI location may not be the final one, as scheduler may rellocate the UE PDCCH. However, the remaining DCI - // params are independent of the exact DCI location - bool ret = ue->phy().get_dci_ctx_pdsch_rnti_c(ss_id, dci_pos, ue->rnti, dci.ctx); - srsran_assert(ret, "Invalid DL DCI format"); - - fill_dci_common(bwp_cfg, dci); - fill_dci_harq(ue, dci); - fill_dci_grant(bwp_cfg, ue.h_dl->prbs(), dci); - if (dci.ctx.format == srsran_dci_format_nr_1_0) { - dci.harq_feedback = (ue.uci_slot - ue.pdsch_slot) - 1; - } else { - dci.harq_feedback = ue.pdsch_slot.slot_idx(); - } -} - -void fill_ul_dci_ue_fields(const slot_ue& ue, - const bwp_params_t& bwp_cfg, - uint32_t ss_id, - srsran_dci_location_t dci_pos, - srsran_dci_ul_nr_t& dci) -{ - bool ret = ue->phy().get_dci_ctx_pusch_rnti_c(ss_id, dci_pos, ue->rnti, dci.ctx); - srsran_assert(ret, "Invalid DL DCI format"); - - fill_dci_common(bwp_cfg, dci); - fill_dci_harq(ue, dci); - fill_dci_grant(bwp_cfg, ue.h_ul->prbs(), dci); -} - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void log_sched_slot_ues(srslog::basic_logger& logger, slot_point pdcch_slot, uint32_t cc, const slot_ue_map_t& slot_ues) @@ -205,7 +43,14 @@ void log_sched_slot_ues(srslog::basic_logger& logger, slot_point pdcch_slot, uin for (const auto& ue_pair : slot_ues) { auto& ue = ue_pair->second; - fmt::format_to(fmtbuf, "{}{{rnti=0x{:x}, dl_bs={}, ul_bs={}}}", use_comma, ue->rnti, ue.dl_bytes, ue.ul_bytes); + fmt::format_to(fmtbuf, "{}{{rnti=0x{:x}", use_comma, ue->rnti); + if (ue.dl_active) { + fmt::format_to(fmtbuf, ", dl_bs={}", ue.dl_bytes); + } + if (ue.ul_active) { + fmt::format_to(fmtbuf, ", ul_bs={}", ue.ul_bytes); + } + fmt::format_to(fmtbuf, "}}"); use_comma = ", "; } diff --git a/srsgnb/src/stack/mac/sched_nr_interface_utils.cc b/srsgnb/src/stack/mac/sched_nr_interface_utils.cc index f62853ef2..4c3fd7502 100644 --- a/srsgnb/src/stack/mac/sched_nr_interface_utils.cc +++ b/srsgnb/src/stack/mac/sched_nr_interface_utils.cc @@ -23,6 +23,13 @@ namespace srsenb { +uint32_t coreset_nof_cces(const srsran_coreset_t& coreset) +{ + const bool* res_active = &coreset.freq_resources[0]; + uint32_t nof_freq_res = std::count(res_active, res_active + SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE, true); + return nof_freq_res * coreset.duration; +} + srsran::phy_cfg_nr_t get_common_ue_phy_cfg(const sched_nr_interface::cell_cfg_t& cfg) { srsran::phy_cfg_nr_t ue_phy_cfg; diff --git a/srsgnb/src/stack/mac/sched_nr_pdcch.cc b/srsgnb/src/stack/mac/sched_nr_pdcch.cc index fb7ebf4e0..2bf95682d 100644 --- a/srsgnb/src/stack/mac/sched_nr_pdcch.cc +++ b/srsgnb/src/stack/mac/sched_nr_pdcch.cc @@ -20,20 +20,56 @@ */ #include "srsgnb/hdr/stack/mac/sched_nr_pdcch.h" +#include "srsgnb/hdr/stack/mac/sched_nr_helpers.h" +#include "srsran/common/string_helpers.h" namespace srsenb { namespace sched_nr_impl { -coreset_region::coreset_region(const bwp_params_t& bwp_cfg_, - uint32_t coreset_id_, - uint32_t slot_idx_, - pdcch_dl_list_t& dl_list_, - pdcch_ul_list_t& ul_list_) : +template +void log_pdcch_alloc_failure(srslog::log_channel& log_ch, + srsran_rnti_type_t rnti_type, + uint32_t ss_id, + uint16_t rnti, + const char* cause_fmt, + Args&&... args) +{ + if (not log_ch.enabled()) { + return; + } + + // Log PDCCH allocation failure + fmt::memory_buffer fmtbuf; + fmt::format_to(fmtbuf, + "SCHED: Failure to allocate PDCCH for {}-rnti=0x{:x}, SS#{}. Cause: ", + srsran_rnti_type_str_short(rnti_type), + rnti, + ss_id); + fmt::format_to(fmtbuf, cause_fmt, std::forward(args)...); + log_ch("%s", srsran::to_c_str(fmtbuf)); +} + +void fill_dci_from_cfg(const bwp_params_t& bwp_cfg, srsran_dci_dl_nr_t& dci) +{ + dci.bwp_id = bwp_cfg.bwp_id; + dci.cc_id = bwp_cfg.cc; + dci.tpc = 1; + dci.coreset0_bw = bwp_cfg.cfg.pdcch.coreset_present[0] ? bwp_cfg.coreset_prb_range(0).length() : 0; +} + +void fill_dci_from_cfg(const bwp_params_t& bwp_cfg, srsran_dci_ul_nr_t& dci) +{ + dci.bwp_id = bwp_cfg.bwp_id; + dci.cc_id = bwp_cfg.cc; + dci.tpc = 1; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +coreset_region::coreset_region(const bwp_params_t& bwp_cfg_, uint32_t coreset_id_, uint32_t slot_idx_) : coreset_cfg(&bwp_cfg_.cfg.pdcch.coreset[coreset_id_]), coreset_id(coreset_id_), slot_idx(slot_idx_), - pdcch_dl_list(dl_list_), - pdcch_ul_list(ul_list_), rar_cce_list(bwp_cfg_.rar_cce_list), common_cce_list(bwp_cfg_.common_cce_list) { @@ -42,7 +78,7 @@ coreset_region::coreset_region(const bwp_params_t& bwp_cfg_, srsran_assert(get_td_symbols() <= SRSRAN_CORESET_DURATION_MAX, "Possible number of time-domain OFDM symbols in CORESET must be within {1,2,3}"); srsran_assert(nof_freq_res <= bwp_cfg_.cell_cfg.carrier.nof_prb, - "The number of frequency resources=%d of coreset_id=%d exceeds BWP bandwidth", + "The number of frequency resources=%d of CORESET#%d exceeds BWP bandwidth", nof_freq_res, coreset_id); } @@ -52,33 +88,24 @@ void coreset_region::reset() dfs_tree.clear(); saved_dfs_tree.clear(); dci_list.clear(); - pdcch_dl_list.clear(); - pdcch_ul_list.clear(); } -bool coreset_region::alloc_dci(pdcch_grant_type_t alloc_type, - uint32_t aggr_idx, - uint32_t search_space_id, - const ue_carrier_params_t* user) +bool coreset_region::alloc_pdcch(srsran_rnti_type_t rnti_type, + bool is_dl, + uint32_t aggr_idx, + uint32_t search_space_id, + const ue_carrier_params_t* user, + srsran_dci_ctx_t& dci) { - srsran_assert(aggr_idx <= 4, "Invalid DCI aggregation level=%d", 1U << aggr_idx); - srsran_assert((user == nullptr) xor - (alloc_type == pdcch_grant_type_t::dl_data or alloc_type == pdcch_grant_type_t::ul_data), - "UE should be only provided for DL or UL data allocations"); saved_dfs_tree.clear(); alloc_record record; - record.ue = user; - record.aggr_idx = aggr_idx; - record.ss_id = search_space_id; - record.alloc_type = alloc_type; - if (record.alloc_type == pdcch_grant_type_t::ul_data) { - record.idx = pdcch_ul_list.size(); - pdcch_ul_list.emplace_back(); - } else { - record.idx = pdcch_dl_list.size(); - pdcch_dl_list.emplace_back(); - } + record.dci = &dci; + record.ue = user; + record.aggr_idx = aggr_idx; + record.ss_id = search_space_id; + record.is_dl = is_dl; + record.dci->rnti_type = rnti_type; // Try to allocate grant. If it fails, attempt the same grant, but using a different permutation of past grant DCI // positions @@ -96,25 +123,15 @@ bool coreset_region::alloc_dci(pdcch_grant_type_t alloc_type, // Revert steps to initial state, before dci record allocation was attempted dfs_tree.swap(saved_dfs_tree); - if (record.alloc_type == pdcch_grant_type_t::ul_data) { - pdcch_ul_list.pop_back(); - } else { - pdcch_dl_list.pop_back(); - } return false; } -void coreset_region::rem_last_dci() +void coreset_region::rem_last_pdcch() { srsran_assert(not dci_list.empty(), "%s called when no PDCCH have yet been allocated", __FUNCTION__); // Remove DCI record dfs_tree.pop_back(); - if (dci_list.back().alloc_type == pdcch_grant_type_t::ul_data) { - pdcch_ul_list.pop_back(); - } else { - pdcch_dl_list.pop_back(); - } dci_list.pop_back(); } @@ -171,13 +188,7 @@ bool coreset_region::alloc_dfs_node(const alloc_record& record, uint32_t start_d // Allocation successful node.total_mask |= node.current_mask; alloc_dfs.push_back(node); - if (record.alloc_type == pdcch_grant_type_t::ul_data) { - pdcch_ul_t& pdcch_ul = pdcch_ul_list[record.idx]; - pdcch_ul.dci.ctx.location = node.dci_pos; - } else { - pdcch_dl_t& pdcch_dl = pdcch_dl_list[record.idx]; - pdcch_dl.dci.ctx.location = node.dci_pos; - } + record.dci->location = node.dci_pos; return true; } @@ -186,19 +197,324 @@ bool coreset_region::alloc_dfs_node(const alloc_record& record, uint32_t start_d srsran::span coreset_region::get_cce_loc_table(const alloc_record& record) const { - switch (record.alloc_type) { - case pdcch_grant_type_t::dl_data: - case pdcch_grant_type_t::ul_data: - return record.ue->cce_pos_list(record.ss_id, slot_idx, record.aggr_idx); - case pdcch_grant_type_t::rar: + switch (record.dci->rnti_type) { + case srsran_rnti_type_ra: return rar_cce_list[slot_idx][record.aggr_idx]; - case pdcch_grant_type_t::sib: + case srsran_rnti_type_si: return common_cce_list[record.ss_id][slot_idx][record.aggr_idx]; + case srsran_rnti_type_c: + case srsran_rnti_type_tc: + case srsran_rnti_type_mcs_c: + case srsran_rnti_type_sp_csi: + return record.ue->cce_pos_list(record.ss_id, slot_idx, record.aggr_idx); default: + srsran_terminate("Invalid RNTI type=%s", srsran_rnti_type_str(record.dci->rnti_type)); break; } return {}; } +void coreset_region::print_allocations(fmt::memory_buffer& fmtbuf) const +{ + if (not dci_list.empty()) { + fmt::format_to(fmtbuf, "CORESET#{} (#CCEs={}):\n", coreset_id, nof_cces()); + } + for (const alloc_record& dci : dci_list) { + fmt::format_to(fmtbuf, + " {}-rnti=0x{:x}: ({}, {})\n", + srsran_rnti_type_str_short(dci.dci->rnti_type), + dci.dci->rnti, + dci.dci->location.ncce, + dci.dci->location.L); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bwp_pdcch_allocator::bwp_pdcch_allocator(const bwp_params_t& bwp_cfg_, + uint32_t slot_idx_, + pdcch_dl_list_t& dl_pdcchs, + pdcch_ul_list_t& ul_pdcchs) : + bwp_cfg(bwp_cfg_), pdcch_dl_list(dl_pdcchs), pdcch_ul_list(ul_pdcchs), slot_idx(slot_idx_), logger(bwp_cfg_.logger) +{ + for (uint32_t cs_idx = 0; cs_idx < SRSRAN_UE_DL_NR_MAX_NOF_CORESET; ++cs_idx) { + if (bwp_cfg.cfg.pdcch.coreset_present[cs_idx]) { + uint32_t cs_id = bwp_cfg.cfg.pdcch.coreset[cs_idx].id; + coresets.emplace(cs_id, bwp_cfg, cs_id, slot_idx); + } + } +} + +void bwp_pdcch_allocator::fill_dci_ctx_common(srsran_dci_ctx_t& dci, + srsran_rnti_type_t rnti_type, + uint16_t rnti, + const srsran_search_space_t& ss, + srsran_dci_format_nr_t dci_fmt, + const ue_carrier_params_t* ue) +{ + // Note: Location is filled by coreset_region class. + dci.ss_type = ss.type; + dci.coreset_id = ss.coreset_id; + const srsran_coreset_t* coreset = + ue == nullptr ? &bwp_cfg.cfg.pdcch.coreset[ss.coreset_id] : &ue->phy().pdcch.coreset[ss.coreset_id]; + dci.coreset_start_rb = srsran_coreset_start_rb(coreset); + dci.rnti_type = rnti_type; + dci.rnti = rnti; + dci.format = dci_fmt; +} + +pdcch_dl_alloc_result bwp_pdcch_allocator::alloc_rar_pdcch(uint16_t ra_rnti, uint32_t aggr_idx) +{ + srsran_assert(bwp_cfg.cfg.pdcch.ra_search_space_present, "Allocating RAR PDCCH in BWP without RA SearchSpace"); + return alloc_dl_pdcch_common( + srsran_rnti_type_ra, ra_rnti, bwp_cfg.cfg.pdcch.ra_search_space.id, aggr_idx, srsran_dci_format_nr_1_0, nullptr); +} + +pdcch_dl_alloc_result bwp_pdcch_allocator::alloc_si_pdcch(uint32_t ss_id, uint32_t aggr_idx) +{ + return alloc_dl_pdcch_common(srsran_rnti_type_si, SRSRAN_SIRNTI, ss_id, aggr_idx, srsran_dci_format_nr_1_0, nullptr); +} + +pdcch_dl_alloc_result bwp_pdcch_allocator::alloc_dl_pdcch(srsran_rnti_type_t rnti_type, + uint32_t ss_id, + uint32_t aggr_idx, + const ue_carrier_params_t& user) +{ + static const srsran_dci_format_nr_t dci_fmt = srsran_dci_format_nr_1_0; // TODO: make it configurable + srsran_assert(rnti_type == srsran_rnti_type_c or rnti_type == srsran_rnti_type_tc, + "Invalid RNTI type=%s for UE-specific PDCCH", + srsran_rnti_type_str_short(rnti_type)); + return alloc_dl_pdcch_common(rnti_type, user.rnti, ss_id, aggr_idx, dci_fmt, &user); +} + +pdcch_dl_alloc_result bwp_pdcch_allocator::alloc_dl_pdcch_common(srsran_rnti_type_t rnti_type, + uint16_t rnti, + uint32_t ss_id, + uint32_t aggr_idx, + srsran_dci_format_nr_t dci_fmt, + const ue_carrier_params_t* user) +{ + alloc_result r = check_args_valid(rnti_type, rnti, ss_id, aggr_idx, dci_fmt, user, true); + if (r != alloc_result::success) { + return {r}; + } + const srsran_search_space_t& ss = + (user == nullptr) + ? (rnti_type == srsran_rnti_type_ra ? bwp_cfg.cfg.pdcch.ra_search_space : *bwp_cfg.get_ss(ss_id)) + : *user->get_ss(ss_id); + + // Add new DL PDCCH to sched result + pdcch_dl_list.emplace_back(); + + bool success = + coresets[ss.coreset_id].alloc_pdcch(rnti_type, true, aggr_idx, ss_id, user, pdcch_dl_list.back().dci.ctx); + + if (not success) { + // Remove failed PDCCH allocation + pdcch_dl_list.pop_back(); + + // Log PDCCH allocation failure + srslog::log_channel& ch = user == nullptr ? logger.warning : logger.debug; + log_pdcch_alloc_failure(ch, rnti_type, ss_id, rnti, "No available PDCCH position"); + + return {alloc_result::no_cch_space}; + } + + // PDCCH allocation was successful + pdcch_dl_t& pdcch = pdcch_dl_list.back(); + + // Fill DCI with semi-static config + fill_dci_from_cfg(bwp_cfg, pdcch.dci); + + // Fill DCI context information + fill_dci_ctx_common(pdcch.dci.ctx, rnti_type, rnti, ss, dci_fmt, user); + + // register last PDCCH coreset, in case it needs to be aborted + pending_dci = &pdcch.dci.ctx; + + return {&pdcch}; +} + +pdcch_ul_alloc_result +bwp_pdcch_allocator::alloc_ul_pdcch(uint32_t ss_id, uint32_t aggr_idx, const ue_carrier_params_t& user) +{ + static const srsran_dci_format_nr_t dci_fmt = srsran_dci_format_nr_0_0; // TODO: make it configurable + alloc_result r = check_args_valid(srsran_rnti_type_c, user.rnti, ss_id, aggr_idx, dci_fmt, &user, false); + if (r != alloc_result::success) { + return {r}; + } + const srsran_search_space_t& ss = *user.get_ss(ss_id); + + // Add new UL PDCCH to sched result + pdcch_ul_list.emplace_back(); + + bool success = coresets[ss.coreset_id].alloc_pdcch( + srsran_rnti_type_c, false, aggr_idx, ss_id, &user, pdcch_ul_list.back().dci.ctx); + + if (not success) { + // Remove failed PDCCH allocation + pdcch_ul_list.pop_back(); + + // Log PDCCH allocation failure + log_pdcch_alloc_failure(logger.debug, srsran_rnti_type_c, ss_id, user.rnti, "No available PDCCH position"); + + return {alloc_result::no_cch_space}; + } + + // PDCCH allocation was successful + pdcch_ul_t& pdcch = pdcch_ul_list.back(); + + // Fill DCI with semi-static config + fill_dci_from_cfg(bwp_cfg, pdcch.dci); + + // Fill DCI context information + fill_dci_ctx_common(pdcch.dci.ctx, srsran_rnti_type_c, user.rnti, ss, dci_fmt, &user); + + // register last PDCCH coreset, in case it needs to be aborted + pending_dci = &pdcch.dci.ctx; + + return {&pdcch}; +} + +void bwp_pdcch_allocator::cancel_last_pdcch() +{ + srsran_assert(pending_dci != nullptr, "Trying to abort PDCCH allocation that does not exist"); + uint32_t cs_id = pending_dci->coreset_id; + + if (&pdcch_dl_list.back().dci.ctx == pending_dci) { + pdcch_dl_list.pop_back(); + } else if (&pdcch_ul_list.back().dci.ctx == pending_dci) { + pdcch_ul_list.pop_back(); + } else { + logger.error("Invalid DCI context provided to be removed"); + return; + } + coresets[cs_id].rem_last_pdcch(); + pending_dci = nullptr; +} + +void bwp_pdcch_allocator::reset() +{ + pending_dci = nullptr; + pdcch_dl_list.clear(); + pdcch_ul_list.clear(); + for (coreset_region& coreset : coresets) { + coreset.reset(); + } +} + +uint32_t bwp_pdcch_allocator::nof_allocations() const +{ + uint32_t count = 0; + for (const coreset_region& coreset : coresets) { + count += coreset.nof_allocs(); + } + return count; +} + +uint32_t bwp_pdcch_allocator::nof_cces(uint32_t coreset_id) const +{ + return coresets[coreset_id].nof_cces(); +} + +alloc_result bwp_pdcch_allocator::check_args_valid(srsran_rnti_type_t rnti_type, + uint16_t rnti, + uint32_t ss_id, + uint32_t aggr_idx, + srsran_dci_format_nr_t dci_fmt, + const ue_carrier_params_t* user, + bool is_dl) const +{ + srsran_assert(ss_id < SRSRAN_UE_DL_NR_MAX_NOF_SEARCH_SPACE, "Invalid SearchSpace#%d", ss_id); + srsran_assert( + aggr_idx < SRSRAN_SEARCH_SPACE_NOF_AGGREGATION_LEVELS_NR, "Invalid aggregation level index=%d", aggr_idx); + + // DL must be active in given slot + if (not bwp_cfg.slots[slot_idx].is_dl) { + log_pdcch_alloc_failure(logger.error, rnti_type, ss_id, rnti, "DL is disabled for slot={}", slot_idx); + return alloc_result::no_cch_space; + } + + // Verify SearchSpace validity + const srsran_search_space_t* ss = + (user == nullptr) + ? (rnti_type == srsran_rnti_type_ra ? &bwp_cfg.cfg.pdcch.ra_search_space : bwp_cfg.get_ss(ss_id)) + : user->get_ss(ss_id); + if (ss == nullptr) { + // Couldn't find SearchSpace + log_pdcch_alloc_failure(logger.error, rnti_type, ss_id, rnti, "SearchSpace has not been configured"); + return alloc_result::invalid_grant_params; + } + if (ss->nof_candidates[aggr_idx] == 0) { + // No valid DCI position candidates given aggregation level + log_pdcch_alloc_failure( + logger.warning, rnti_type, ss_id, rnti, "Chosen SearchSpace doesn't have CCE candidates for L={}", aggr_idx); + return alloc_result::invalid_grant_params; + } + if (not is_rnti_type_valid_in_search_space(rnti_type, ss->type)) { + // RNTI type doesnt match SearchSpace type + log_pdcch_alloc_failure(logger.warning, + rnti_type, + ss_id, + rnti, + "Chosen SearchSpace type \"{}\" does not match rnti_type.", + srsran_ss_type_str(ss->type)); + return alloc_result::invalid_grant_params; + } + auto dci_fmt_equal = [dci_fmt](srsran_dci_format_nr_t f) { return f == dci_fmt; }; + if (std::none_of(&ss->formats[0], &ss->formats[ss->nof_formats], dci_fmt_equal)) { + log_pdcch_alloc_failure(logger.warning, + rnti_type, + ss_id, + rnti, + "Chosen SearchSpace does not support chosen dci format={}", + srsran_dci_format_nr_string(dci_fmt)); + return alloc_result::invalid_grant_params; + } + + if (is_dl) { + if (pdcch_dl_list.full()) { + log_pdcch_alloc_failure( + logger.warning, rnti_type, ss_id, rnti, "Maximum number of allocations={} reached", pdcch_dl_list.size()); + return alloc_result::no_cch_space; + } + } else if (pdcch_ul_list.full()) { + log_pdcch_alloc_failure( + logger.warning, rnti_type, ss_id, rnti, "Maximum number of UL allocations={} reached", pdcch_ul_list.size()); + return alloc_result::no_cch_space; + } + + if (user != nullptr) { + if (user->active_bwp().bwp_id != bwp_cfg.bwp_id) { + log_pdcch_alloc_failure(logger.warning, + rnti_type, + ss_id, + rnti, + "Trying to allocate BWP#{} which is inactive for the UE.", + user->active_bwp().bwp_id); + return alloc_result::no_rnti_opportunity; + } + } + + srsran_sanity_check(pdcch_dl_list.size() + pdcch_ul_list.size() == nof_allocations(), "Invalid PDCCH state"); + return alloc_result::success; +} + +void bwp_pdcch_allocator::print_allocations(fmt::memory_buffer& fmtbuf) const +{ + fmt::format_to( + fmtbuf, "PDCCH allocations: ({} active coresets):{}\n", coresets.size(), nof_allocations() == 0 ? " None" : ""); + for (const coreset_region& cs : coresets) { + cs.print_allocations(fmtbuf); + } +} + +std::string bwp_pdcch_allocator::print_allocations() const +{ + fmt::memory_buffer fmtbuf; + print_allocations(fmtbuf); + return fmt::to_string(fmtbuf); +} + } // namespace sched_nr_impl } // namespace srsenb diff --git a/srsgnb/src/stack/mac/sched_nr_sch.cc b/srsgnb/src/stack/mac/sched_nr_sch.cc new file mode 100644 index 000000000..e378e364d --- /dev/null +++ b/srsgnb/src/stack/mac/sched_nr_sch.cc @@ -0,0 +1,362 @@ +/** + * + * \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 "srsgnb/hdr/stack/mac/sched_nr_sch.h" +#include "srsran/common/string_helpers.h" + +namespace srsenb { +namespace sched_nr_impl { + +template +void log_alloc_failure(srslog::log_channel& log_ch, const char* cause_fmt, Args&&... args) +{ + if (not log_ch.enabled()) { + return; + } + + // Log allocation failure + fmt::memory_buffer fmtbuf; + fmt::format_to(fmtbuf, "SCHED: Failure to allocate PDSCH. Cause: "); + fmt::format_to(fmtbuf, cause_fmt, std::forward(args)...); + log_ch("%s", srsran::to_c_str(fmtbuf)); +} + +pdsch_allocator::pdsch_allocator(const bwp_params_t& cfg_, uint32_t slot_index, pdsch_list_t& pdsch_lst) : + bwp_cfg(cfg_), + slot_idx(slot_index), + pdschs(pdsch_lst), + dl_prbs(bwp_cfg.cfg.rb_width, bwp_cfg.cfg.start_rb, bwp_cfg.cfg.pdsch.rbg_size_cfg_1) +{} + +void pdsch_allocator::reset() +{ + pdschs.clear(); + dl_prbs.reset(); +} + +alloc_result pdsch_allocator::is_grant_valid_common(srsran_search_space_type_t ss_type, + srsran_dci_format_nr_t dci_fmt, + uint32_t coreset_id, + const prb_grant& grant) const +{ + // DL must be active in given slot + if (not bwp_cfg.slots[slot_idx].is_dl) { + log_alloc_failure(bwp_cfg.logger.error, "DL is disabled for slot={}", slot_idx); + return alloc_result::no_sch_space; + } + + // No space in Scheduler PDSCH output list + if (pdschs.full()) { + log_alloc_failure(bwp_cfg.logger.warning, "Maximum number of PDSCHs={} reached.", pdschs.size()); + return alloc_result::no_sch_space; + } + + // TS 38.214, 5.1.2.2 - "The UE shall assume that when the scheduling grant is received with DCI format 1_0, then + // downlink resource allocation type 1 is used." + if (dci_fmt == srsran_dci_format_nr_1_0 and not grant.is_alloc_type1()) { + log_alloc_failure(bwp_cfg.logger.warning, "DL Resource Allocation type 1 must be used in case of DCI format 1_0."); + return alloc_result::invalid_grant_params; + } + + // TS 38.214 - 5.1.2.2 - For DCI format 1_0 and Common Search Space, the list of available PRBs is limited by the + // rb_start and bandwidth of the coreset + if (dci_fmt == srsran_dci_format_nr_1_0 and SRSRAN_SEARCH_SPACE_IS_COMMON(ss_type)) { + // Grant PRBs do not collide with CORESET PRB limits (in case of common SearchSpace) + if (bwp_cfg.dci_fmt_1_0_excluded_prbs(coreset_id).collides(grant)) { + log_alloc_failure( + bwp_cfg.logger.debug, "Provided PRB grant={:x} falls outside common CORESET PRB boundaries.", grant); + return alloc_result::sch_collision; + } + } + + // Grant PRBs do not collide with previous PDSCH allocations + if (dl_prbs.collides(grant)) { + log_alloc_failure( + bwp_cfg.logger.debug, "Provided PRB grant={:x} collides with allocations previously made.", grant); + return alloc_result::sch_collision; + } + + return alloc_result::success; +} + +alloc_result pdsch_allocator::is_si_grant_valid(uint32_t ss_id, const prb_grant& grant) const +{ + // Verify SearchSpace validity + const srsran_search_space_t* ss = bwp_cfg.get_ss(ss_id); + if (ss == nullptr) { + // Couldn't find SearchSpace + log_alloc_failure(bwp_cfg.logger.error, "SearchSpace has not been configured."); + return alloc_result::invalid_grant_params; + } + return is_grant_valid_common(ss->type, srsran_dci_format_nr_1_0, ss->coreset_id, grant); +} + +alloc_result pdsch_allocator::is_rar_grant_valid(const prb_grant& grant) const +{ + srsran_sanity_check(bwp_cfg.cfg.pdcch.ra_search_space_present, + "Attempting RAR allocation in BWP with no raSearchSpace"); + return is_grant_valid_common(bwp_cfg.cfg.pdcch.ra_search_space.type, + srsran_dci_format_nr_1_0, + bwp_cfg.cfg.pdcch.ra_search_space.coreset_id, + grant); +} + +alloc_result pdsch_allocator::is_ue_grant_valid(const ue_carrier_params_t& ue, + uint32_t ss_id, + srsran_dci_format_nr_t dci_fmt, + const prb_grant& grant) const +{ + const srsran_search_space_t* ss = ue.get_ss(ss_id); + if (ss == nullptr) { + // Couldn't find SearchSpace + log_alloc_failure(bwp_cfg.logger.error, "rnti=0x%x,SearchSpaceId={} has not been configured.", ue.rnti, ss_id); + return alloc_result::invalid_grant_params; + } + alloc_result ret = is_grant_valid_common(ss->type, dci_fmt, ss->coreset_id, grant); + if (ret != alloc_result::success) { + return ret; + } + + // TS 38.214, 5.1.2.2 - "the UE shall use the downlink frequency resource allocation type as defined by the higher + // layer parameter resourceAllocation" + if (ue.phy().pdsch.alloc != srsran_resource_alloc_dynamic) { + if ((ue.phy().pdsch.alloc == srsran_resource_alloc_type0) != grant.is_alloc_type0()) { + log_alloc_failure(bwp_cfg.logger.warning, + "UE rnti=0x{:x} PDSCH RA configuration type {} doesn't match grant type", + ue.rnti, + grant.is_alloc_type0() ? 0 : 1); + return alloc_result::invalid_grant_params; + } + } + + return alloc_result::success; +} + +pdsch_alloc_result pdsch_allocator::alloc_si_pdsch(uint32_t ss_id, const prb_grant& grant, srsran_dci_dl_nr_t& dci) +{ + alloc_result code = is_si_grant_valid(ss_id, grant); + if (code != alloc_result::success) { + return code; + } + return {&alloc_si_pdsch_unchecked(ss_id, grant, dci)}; +} + +pdsch_t& pdsch_allocator::alloc_si_pdsch_unchecked(uint32_t ss_id, const prb_grant& grant, srsran_dci_dl_nr_t& dci) +{ + // Verify SearchSpace validity + const srsran_search_space_t* ss = bwp_cfg.get_ss(ss_id); + srsran_sanity_check(ss != nullptr, "SearchSpace has not been configured"); + return alloc_pdsch_unchecked(ss->coreset_id, ss->type, srsran_dci_format_nr_1_0, grant, dci); +} + +pdsch_alloc_result pdsch_allocator::alloc_rar_pdsch(const prb_grant& grant, srsran_dci_dl_nr_t& dci) +{ + alloc_result code = is_rar_grant_valid(grant); + if (code != alloc_result::success) { + return code; + } + return {&alloc_rar_pdsch_unchecked(grant, dci)}; +} + +pdsch_t& pdsch_allocator::alloc_rar_pdsch_unchecked(const prb_grant& grant, srsran_dci_dl_nr_t& dci) +{ + // TS 38.213, 8.2 - "In response to a PRACH transmission, a UE attempts to detect a DCI format 1_0" + const static srsran_dci_format_nr_t dci_fmt = srsran_dci_format_nr_1_0; + + return alloc_pdsch_unchecked( + bwp_cfg.cfg.pdcch.ra_search_space.coreset_id, bwp_cfg.cfg.pdcch.ra_search_space.type, dci_fmt, grant, dci); +} + +pdsch_alloc_result pdsch_allocator::alloc_ue_pdsch(uint32_t ss_id, + srsran_dci_format_nr_t dci_fmt, + const prb_grant& grant, + const ue_carrier_params_t& ue, + srsran_dci_dl_nr_t& dci) +{ + alloc_result code = is_ue_grant_valid(ue, ss_id, dci_fmt, grant); + if (code != alloc_result::success) { + return code; + } + return {&alloc_ue_pdsch_unchecked(ss_id, dci_fmt, grant, ue, dci)}; +} + +pdsch_t& pdsch_allocator::alloc_ue_pdsch_unchecked(uint32_t ss_id, + srsran_dci_format_nr_t dci_fmt, + const prb_grant& grant, + const ue_carrier_params_t& ue, + srsran_dci_dl_nr_t& dci) +{ + const srsran_search_space_t* ss = ue.get_ss(ss_id); + srsran_sanity_check(ss != nullptr, "SearchSpace has not been configured"); + return alloc_pdsch_unchecked(ss->coreset_id, ss->type, dci_fmt, grant, dci); +} + +pdsch_t& pdsch_allocator::alloc_pdsch_unchecked(uint32_t coreset_id, + srsran_search_space_type_t ss_type, + srsran_dci_format_nr_t dci_fmt, + const prb_grant& grant, + srsran_dci_dl_nr_t& out_dci) +{ + // Create new PDSCH entry in output PDSCH list + pdschs.emplace_back(); + pdsch_t& pdsch = pdschs.back(); + + // Register allocated PRBs in accumulated bitmap + dl_prbs |= grant; + + // Fill DCI with PDSCH freq/time allocation information + out_dci.time_domain_assigment = 0; + if (grant.is_alloc_type0()) { + out_dci.freq_domain_assigment = grant.rbgs().to_uint64(); + } else { + uint32_t rb_start = grant.prbs().start(), nof_prb = bwp_cfg.nof_prb(); + if (SRSRAN_SEARCH_SPACE_IS_COMMON(ss_type)) { + prb_interval lims = bwp_cfg.coreset_prb_range(coreset_id); + if (dci_fmt == srsran_dci_format_nr_1_0) { + srsran_sanity_check(rb_start >= lims.start(), "Invalid PRB grant"); + rb_start -= lims.start(); + } + if (coreset_id == 0) { + nof_prb = lims.length(); + } + } + srsran_sanity_check(rb_start + grant.prbs().length() <= nof_prb, "Invalid PRB grant"); + out_dci.freq_domain_assigment = srsran_ra_nr_type1_riv(nof_prb, rb_start, grant.prbs().length()); + } + + return pdsch; +} + +void pdsch_allocator::cancel_last_pdsch() +{ + srsran_assert(not pdschs.empty(), "Trying to abort PDSCH allocation that does not exist"); + pdschs.pop_back(); + // TODO: clear bitmap allocated RBs +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +template +void log_pusch_alloc_failure(srslog::log_channel& log_ch, const char* cause_fmt, Args&&... args) +{ + if (not log_ch.enabled()) { + return; + } + + // Log allocation failure + fmt::memory_buffer fmtbuf; + fmt::format_to(fmtbuf, "SCHED: Failure to allocate PUSCH. Cause: "); + fmt::format_to(fmtbuf, cause_fmt, std::forward(args)...); + log_ch("%s", srsran::to_c_str(fmtbuf)); +} + +pusch_allocator::pusch_allocator(const bwp_params_t& cfg_, uint32_t sl_index, pusch_list_t& pusch_lst) : + bwp_cfg(cfg_), + slot_idx(sl_index), + puschs(pusch_lst), + ul_prbs(bwp_cfg.cfg.rb_width, bwp_cfg.cfg.start_rb, bwp_cfg.cfg.pdsch.rbg_size_cfg_1) +{} + +void pusch_allocator::reset() +{ + puschs.clear(); + ul_prbs.reset(); +} + +alloc_result pusch_allocator::has_grant_space(uint32_t nof_grants, bool verbose) const +{ + // UL must be active in given slot + if (not bwp_cfg.slots[slot_idx].is_ul) { + if (verbose) { + log_pusch_alloc_failure(bwp_cfg.logger.error, "UL is disabled for slot={}", slot_idx); + } + return alloc_result::no_sch_space; + } + + // No space in Scheduler PDSCH output list + if (puschs.size() + nof_grants > puschs.capacity()) { + if (verbose) { + log_pusch_alloc_failure(bwp_cfg.logger.warning, "Maximum number of PUSCHs={} reached.", puschs.capacity()); + } + return alloc_result::no_sch_space; + } + + return alloc_result::success; +} + +alloc_result +pusch_allocator::is_grant_valid(srsran_search_space_type_t ss_type, const prb_grant& grant, bool verbose) const +{ + alloc_result ret = has_grant_space(1, verbose); + if (ret != alloc_result::success) { + return ret; + } + + if (SRSRAN_SEARCH_SPACE_IS_COMMON(ss_type)) { + // In case of common SearchSpaces, the PRBs must be contiguous + if (grant.is_alloc_type0()) { + log_pusch_alloc_failure(bwp_cfg.logger.warning, "AllocType0 not allowed in common SearchSpace."); + return alloc_result::invalid_grant_params; + } + } + + // Grant PRBs do not collide with previous PDSCH allocations + if (ul_prbs.collides(grant)) { + if (verbose) { + log_pusch_alloc_failure(bwp_cfg.logger.debug, "SCHED: Provided UL PRB mask collides with previous allocations."); + } + return alloc_result::sch_collision; + } + + return alloc_result::success; +} + +pusch_alloc_result +pusch_allocator::alloc_pusch(const srsran_search_space_type_t ss_type, const prb_grant& grant, srsran_dci_ul_nr_t& dci) +{ + alloc_result code = is_grant_valid(ss_type, grant); + if (code != alloc_result::success) { + return code; + } + + return {&alloc_pusch_unchecked(grant, dci)}; +} + +pusch_t& pusch_allocator::alloc_pusch_unchecked(const prb_grant& grant, srsran_dci_ul_nr_t& out_dci) +{ + // Create new PUSCH entry in output PUSCH list + puschs.emplace_back(); + pusch_t& pusch = puschs.back(); + + // Register allocated PRBs in accumulated bitmap + ul_prbs |= grant; + + // Fill DCI with PUSCH freq/time allocation information + out_dci.time_domain_assigment = 0; + if (grant.is_alloc_type0()) { + out_dci.freq_domain_assigment = grant.rbgs().to_uint64(); + } else { + uint32_t nof_prb = bwp_cfg.nof_prb(); + out_dci.freq_domain_assigment = srsran_ra_nr_type1_riv(nof_prb, grant.prbs().start(), grant.prbs().length()); + } + + return pusch; +} + +void pusch_allocator::cancel_last_pusch() +{ + srsran_assert(not puschs.empty(), "Trying to abort PUSCH allocation that does not exist"); + puschs.pop_back(); +} + +} // namespace sched_nr_impl +} // namespace srsenb \ No newline at end of file diff --git a/srsgnb/src/stack/mac/sched_nr_signalling.cc b/srsgnb/src/stack/mac/sched_nr_signalling.cc index 64bedfc29..db15002b7 100644 --- a/srsgnb/src/stack/mac/sched_nr_signalling.cc +++ b/srsgnb/src/stack/mac/sched_nr_signalling.cc @@ -111,32 +111,6 @@ void sched_dl_signalling(bwp_slot_allocator& bwp_alloc) /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool fill_dci_sib(prb_interval interv, - uint32_t sib_id, - uint32_t si_ntx, - const bwp_params_t& bwp_cfg, - srsran_dci_dl_nr_t& dci) -{ - dci.mcs = 5; - dci.ctx.format = srsran_dci_format_nr_1_0; - dci.ctx.ss_type = srsran_search_space_type_common_0; - dci.ctx.rnti_type = srsran_rnti_type_si; - dci.ctx.rnti = SRSRAN_SIRNTI; - dci.ctx.coreset_id = 0; - dci.ctx.coreset_start_rb = bwp_cfg.cfg.pdcch.coreset[0].offset_rb; - dci.coreset0_bw = srsran_coreset_get_bw(&bwp_cfg.cfg.pdcch.coreset[0]); - dci.freq_domain_assigment = - srsran_ra_nr_type1_riv(srsran_coreset_get_bw(&bwp_cfg.cfg.pdcch.coreset[0]), interv.start(), interv.length()); - dci.time_domain_assigment = 0; - dci.tpc = 1; - dci.bwp_id = bwp_cfg.bwp_id; - dci.cc_id = bwp_cfg.cc; - dci.rv = 0; - dci.sii = sib_id == 0 ? 0 : 1; - - return true; -} - si_sched::si_sched(const bwp_params_t& bwp_cfg_) : bwp_cfg(&bwp_cfg_), logger(srslog::fetch_basic_logger(bwp_cfg_.sched_cfg.logger_name)) { @@ -158,9 +132,10 @@ void si_sched::run_slot(bwp_slot_allocator& bwp_alloc) // TODO: provide proper config return; } - const uint32_t si_aggr_level = 2; - slot_point sl_pdcch = bwp_alloc.get_pdcch_tti(); - const prb_bitmap& prbs = bwp_alloc.res_grid()[sl_pdcch].dl_prbs.prbs(); + const uint32_t si_aggr_level = 2; + const uint32_t ss_id = 0; + slot_point sl_pdcch = bwp_alloc.get_pdcch_tti(); + prb_bitmap prbs = bwp_alloc.res_grid()[sl_pdcch].pdschs.occupied_prbs(ss_id, srsran_dci_format_nr_1_0); // Update SI windows uint32_t N = bwp_cfg->slots.size(); diff --git a/srsgnb/src/stack/mac/sched_nr_time_rr.cc b/srsgnb/src/stack/mac/sched_nr_time_rr.cc index 7e47df775..fab4e206d 100644 --- a/srsgnb/src/stack/mac/sched_nr_time_rr.cc +++ b/srsgnb/src/stack/mac/sched_nr_time_rr.cc @@ -24,6 +24,13 @@ namespace srsenb { namespace sched_nr_impl { +/** + * @brief Algorithm to select next UE to allocate in a time-domain RR fashion + * @param ue_db map of "slot_ue" + * @param rr_count starting index to select next UE + * @param p callable with signature "bool(slot_ue&)" that returns true if UE allocation was successful + * @return true if a UE was allocated + */ template bool round_robin_apply(slot_ue_map_t& ue_db, uint32_t rr_count, Predicate p) { @@ -34,6 +41,7 @@ bool round_robin_apply(slot_ue_map_t& ue_db, uint32_t rr_count, Predicate p) std::advance(it, (rr_count % ue_db.size())); for (uint32_t count = 0; count < ue_db.size(); ++count, ++it) { if (it == ue_db.end()) { + // wrap-around it = ue_db.begin(); } if (p(it->second)) { @@ -46,28 +54,35 @@ bool round_robin_apply(slot_ue_map_t& ue_db, uint32_t rr_count, Predicate p) void sched_nr_time_rr::sched_dl_users(slot_ue_map_t& ue_db, bwp_slot_allocator& slot_alloc) { // Start with retxs - if (round_robin_apply(ue_db, slot_alloc.get_pdcch_tti().to_uint(), [&slot_alloc](slot_ue& ue) { - if (ue.h_dl != nullptr and ue.h_dl->has_pending_retx(slot_alloc.get_tti_rx())) { - alloc_result res = slot_alloc.alloc_pdsch(ue, ue.h_dl->prbs()); - if (res == alloc_result::success) { - return true; - } - } - return false; - })) { - return; - } - - // Move on to new txs - round_robin_apply(ue_db, slot_alloc.get_pdcch_tti().to_uint(), [&slot_alloc](slot_ue& ue) { - if (ue.dl_bytes > 0 and ue.h_dl != nullptr and ue.h_dl->empty()) { - alloc_result res = slot_alloc.alloc_pdsch(ue, prb_interval{0, slot_alloc.cfg.cfg.rb_width}); + auto retx_ue_function = [&slot_alloc](slot_ue& ue) { + if (ue.h_dl != nullptr and ue.h_dl->has_pending_retx(slot_alloc.get_tti_rx())) { + alloc_result res = slot_alloc.alloc_pdsch(ue, ue->find_ss_id(srsran_dci_format_nr_1_0), ue.h_dl->prbs()); if (res == alloc_result::success) { return true; } } return false; - }); + }; + if (round_robin_apply(ue_db, slot_alloc.get_pdcch_tti().to_uint(), retx_ue_function)) { + return; + } + + // Move on to new txs + auto newtx_ue_function = [&slot_alloc](slot_ue& ue) { + if (ue.dl_bytes > 0 and ue.h_dl != nullptr and ue.h_dl->empty()) { + int ss_id = ue->find_ss_id(srsran_dci_format_nr_1_0); + if (ss_id < 0) { + return false; + } + prb_grant prbs = find_optimal_dl_grant(slot_alloc, ue, ss_id); + alloc_result res = slot_alloc.alloc_pdsch(ue, ss_id, prbs); + if (res == alloc_result::success) { + return true; + } + } + return false; + }; + round_robin_apply(ue_db, slot_alloc.get_pdcch_tti().to_uint(), newtx_ue_function); } void sched_nr_time_rr::sched_ul_users(slot_ue_map_t& ue_db, bwp_slot_allocator& slot_alloc) diff --git a/srsgnb/src/stack/mac/sched_nr_ue.cc b/srsgnb/src/stack/mac/sched_nr_ue.cc index 8cd818f4c..bcd870213 100644 --- a/srsgnb/src/stack/mac/sched_nr_ue.cc +++ b/srsgnb/src/stack/mac/sched_nr_ue.cc @@ -20,7 +20,7 @@ */ #include "srsgnb/hdr/stack/mac/sched_nr_ue.h" -#include "srsgnb/hdr/stack/mac/sched_nr_pdcch.h" +#include "srsgnb/hdr/stack/mac/sched_nr_helpers.h" #include "srsran/common/string_helpers.h" #include "srsran/mac/mac_sch_pdu_nr.h" @@ -62,8 +62,7 @@ void ue_buffer_manager::pdu_builder::alloc_subpdus(uint32_t rem_bytes, sched_nr_ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -slot_ue::slot_ue(ue_carrier& ue_, slot_point slot_tx_, uint32_t dl_pending_bytes, uint32_t ul_pending_bytes) : - ue(&ue_), pdcch_slot(slot_tx_) +slot_ue::slot_ue(ue_carrier& ue_, slot_point slot_tx_) : ue(&ue_), pdcch_slot(slot_tx_) { const uint32_t k0 = 0; pdsch_slot = pdcch_slot + k0; @@ -74,17 +73,17 @@ slot_ue::slot_ue(ue_carrier& ue_, slot_point slot_tx_, uint32_t dl_pending_bytes const srsran_duplex_config_nr_t& tdd_cfg = ue->cell_params.cfg.duplex; - dl_active = srsran_duplex_nr_is_dl(&tdd_cfg, 0, pdsch_slot.slot_idx()); + dl_active = ue->cell_params.bwps[0].slots[pdsch_slot.slot_idx()].is_dl; if (dl_active) { - dl_bytes = dl_pending_bytes; + dl_bytes = ue->common_ctxt.pending_dl_bytes; h_dl = ue->harq_ent.find_pending_dl_retx(); if (h_dl == nullptr) { h_dl = ue->harq_ent.find_empty_dl_harq(); } } - ul_active = srsran_duplex_nr_is_ul(&tdd_cfg, 0, pusch_slot.slot_idx()); + ul_active = ue->cell_params.bwps[0].slots[pusch_slot.slot_idx()].is_ul; if (ul_active) { - ul_bytes = ul_pending_bytes; + ul_bytes = ue->common_ctxt.pending_ul_bytes; h_ul = ue->harq_ent.find_pending_ul_retx(); if (h_ul == nullptr) { h_ul = ue->harq_ent.find_empty_ul_harq(); @@ -97,6 +96,7 @@ slot_ue::slot_ue(ue_carrier& ue_, slot_point slot_tx_, uint32_t dl_pending_bytes ue_carrier::ue_carrier(uint16_t rnti_, const ue_cfg_t& uecfg_, const cell_params_t& cell_params_, + const ue_context_common& ctxt, const ue_buffer_manager::pdu_builder& pdu_builder_) : rnti(rnti_), cc(cell_params_.cc), @@ -104,6 +104,7 @@ ue_carrier::ue_carrier(uint16_t rnti_, bwp_cfg(rnti_, cell_params_.bwps[0], uecfg_), cell_params(cell_params_), pdu_builder(pdu_builder_), + common_ctxt(ctxt), harq_ent(rnti_, cell_params_.nof_prb(), SCHED_NR_MAX_HARQ, cell_params_.bwps[0].logger) {} @@ -151,8 +152,11 @@ void ue::set_cfg(const ue_cfg_t& cfg) for (auto& ue_cc_cfg : cfg.carriers) { if (ue_cc_cfg.active) { if (carriers[ue_cc_cfg.cc] == nullptr) { - carriers[ue_cc_cfg.cc].reset(new ue_carrier( - rnti, ue_cfg, sched_cfg.cells[ue_cc_cfg.cc], ue_buffer_manager::pdu_builder{ue_cc_cfg.cc, buffers})); + carriers[ue_cc_cfg.cc].reset(new ue_carrier(rnti, + ue_cfg, + sched_cfg.cells[ue_cc_cfg.cc], + common_ctxt, + ue_buffer_manager::pdu_builder{ue_cc_cfg.cc, buffers})); } else { carriers[ue_cc_cfg.cc]->set_cfg(ue_cfg); } @@ -184,7 +188,7 @@ void ue::rlc_buffer_state(uint32_t lcid, uint32_t newtx, uint32_t priotx) void ue::new_slot(slot_point pdcch_slot) { - last_pdcch_slot = pdcch_slot; + last_tx_slot = pdcch_slot; for (std::unique_ptr& cc : carriers) { if (cc != nullptr) { @@ -194,18 +198,18 @@ void ue::new_slot(slot_point pdcch_slot) // Compute pending DL/UL bytes for {rnti, pdcch_slot} if (sched_cfg.sched_cfg.auto_refill_buffer) { - dl_pending_bytes = 1000000; - ul_pending_bytes = 1000000; + common_ctxt.pending_dl_bytes = 1000000; + common_ctxt.pending_ul_bytes = 1000000; } else { - dl_pending_bytes = buffers.get_dl_tx_total(); - ul_pending_bytes = buffers.get_bsr(); + common_ctxt.pending_dl_bytes = buffers.get_dl_tx_total(); + common_ctxt.pending_ul_bytes = buffers.get_bsr(); for (auto& ue_cc_cfg : ue_cfg.carriers) { auto& cc = carriers[ue_cc_cfg.cc]; if (cc != nullptr) { // Discount UL HARQ pending bytes to BSR for (uint32_t pid = 0; pid < cc->harq_ent.nof_ul_harqs(); ++pid) { if (not cc->harq_ent.ul_harq(pid).empty()) { - ul_pending_bytes -= cc->harq_ent.ul_harq(pid).tbs(); + common_ctxt.pending_ul_bytes -= std::min(cc->harq_ent.ul_harq(pid).tbs(), common_ctxt.pending_ul_bytes); if (last_sr_slot.valid() and cc->harq_ent.ul_harq(pid).harq_slot_tx() > last_sr_slot) { last_sr_slot.clear(); } @@ -213,10 +217,9 @@ void ue::new_slot(slot_point pdcch_slot) } } } - ul_pending_bytes = std::max(0, ul_pending_bytes); - if (ul_pending_bytes == 0 and last_sr_slot.valid()) { + if (common_ctxt.pending_ul_bytes == 0 and last_sr_slot.valid()) { // If unanswered SR is pending - ul_pending_bytes = 512; + common_ctxt.pending_ul_bytes = 512; } } } @@ -224,7 +227,7 @@ void ue::new_slot(slot_point pdcch_slot) slot_ue ue::make_slot_ue(slot_point pdcch_slot, uint32_t cc) { srsran_assert(carriers[cc] != nullptr, "make_slot_ue() called for inexistent rnti=0x%x,cc=%d", rnti, cc); - return slot_ue(*carriers[cc], pdcch_slot, dl_pending_bytes, ul_pending_bytes); + return slot_ue(*carriers[cc], pdcch_slot); } } // namespace sched_nr_impl diff --git a/srsgnb/src/stack/mac/test/CMakeLists.txt b/srsgnb/src/stack/mac/test/CMakeLists.txt index 4a9b3903d..42450a8a3 100644 --- a/srsgnb/src/stack/mac/test/CMakeLists.txt +++ b/srsgnb/src/stack/mac/test/CMakeLists.txt @@ -18,6 +18,8 @@ # and at http://www.gnu.org/licenses/. # +set_directory_properties(PROPERTIES LABELS "sched;nr") + add_library(sched_nr_test_suite sched_nr_common_test.cc sched_nr_ue_ded_test_suite.cc sched_nr_sim_ue.cc) target_link_libraries(sched_nr_test_suite srsgnb_mac srsran_common) @@ -38,6 +40,14 @@ target_link_libraries(sched_nr_prb_test ${Boost_LIBRARIES}) add_nr_test(sched_nr_prb_test sched_nr_prb_test) +add_executable(sched_nr_pdcch_test sched_nr_pdcch_test.cc) +target_link_libraries(sched_nr_pdcch_test srsgnb_mac sched_nr_test_suite srsran_common) +add_nr_test(sched_nr_pdcch_test sched_nr_pdcch_test) + +add_executable(sched_nr_sch_test sched_nr_sch_test.cc) +target_link_libraries(sched_nr_sch_test srsgnb_mac sched_nr_test_suite srsran_common) +add_nr_test(sched_nr_sch_test sched_nr_sch_test) + add_executable(sched_nr_rar_test sched_nr_rar_test.cc) target_link_libraries(sched_nr_rar_test srsgnb_mac sched_nr_test_suite srsran_common) add_nr_test(sched_nr_rar_test sched_nr_rar_test) diff --git a/srsgnb/src/stack/mac/test/sched_nr_cfg_generators.h b/srsgnb/src/stack/mac/test/sched_nr_cfg_generators.h index 0a707dabf..58545a371 100644 --- a/srsgnb/src/stack/mac/test/sched_nr_cfg_generators.h +++ b/srsgnb/src/stack/mac/test/sched_nr_cfg_generators.h @@ -39,6 +39,19 @@ inline srsran_coreset_t get_default_coreset0(uint32_t nof_prb) return coreset; } +inline srsran_search_space_t get_default_search_space0() +{ + srsran_search_space_t ss{}; + ss.coreset_id = 0; + ss.nof_formats = 1; + ss.formats[0] = srsran_dci_format_nr_1_0; + ss.type = srsran_search_space_type_common_0; + ss.id = 0; + ss.nof_candidates[2] = 1; + ss.duration = 1; + return ss; +} + inline sched_nr_interface::cell_cfg_t get_default_cell_cfg( const srsran::phy_cfg_nr_t& phy_cfg = srsran::phy_cfg_nr_default_t{srsran::phy_cfg_nr_default_t::reference_cfg_t{}}) { @@ -117,6 +130,64 @@ inline sched_nr_interface::ue_cfg_t get_default_ue_cfg( return uecfg; } +inline sched_nr_interface::cell_cfg_t get_default_sa_cell_cfg_common() +{ + srsran::phy_cfg_nr_default_t::reference_cfg_t ref; + ref.duplex = srsran::phy_cfg_nr_default_t::reference_cfg_t::R_DUPLEX_FDD; + sched_nr_interface::cell_cfg_t cell_cfg = get_default_cell_cfg(srsran::phy_cfg_nr_default_t{ref}); + cell_cfg.bwps[0].pdcch.coreset_present[0] = true; + cell_cfg.bwps[0].pdcch.coreset[0] = get_default_coreset0(52); + cell_cfg.bwps[0].pdcch.coreset[0].offset_rb = 1; + cell_cfg.bwps[0].pdcch.search_space_present[0] = true; + cell_cfg.bwps[0].pdcch.search_space[0] = get_default_search_space0(); + cell_cfg.bwps[0].pdcch.coreset_present[1] = false; + cell_cfg.bwps[0].pdcch.search_space[1].coreset_id = 0; + cell_cfg.bwps[0].pdcch.search_space[1].type = srsran_search_space_type_common_1; + cell_cfg.bwps[0].pdcch.search_space[1].nof_candidates[2] = 1; + cell_cfg.bwps[0].pdcch.search_space[1].nof_formats = 2; + cell_cfg.bwps[0].pdcch.search_space[1].formats[0] = srsran_dci_format_nr_1_0; + cell_cfg.bwps[0].pdcch.search_space[1].formats[1] = srsran_dci_format_nr_0_0; + cell_cfg.bwps[0].pdcch.ra_search_space = cell_cfg.bwps[0].pdcch.search_space[1]; + return cell_cfg; +} + +// Generate default UE-dedicated CORESET config +inline srsran_coreset_t get_default_ue_specific_coreset(uint32_t id, uint32_t pci) +{ + srsran_coreset_t coreset = {}; + coreset.id = id; + coreset.mapping_type = srsran_coreset_mapping_type_non_interleaved; + coreset.duration = 1; + for (uint32_t i = 0; i < 8; ++i) { + coreset.freq_resources[i] = true; + } + coreset.dmrs_scrambling_id_present = false; + coreset.precoder_granularity = srsran_coreset_precoder_granularity_reg_bundle; + coreset.interleaver_size = srsran_coreset_bundle_size_n2; + coreset.reg_bundle_size = srsran_coreset_bundle_size_n6; + coreset.shift_index = pci; + coreset.offset_rb = 0; + return coreset; +} + +inline srsran_search_space_t get_default_ue_specific_search_space(uint32_t id, uint32_t coreset_id) +{ + srsran_search_space_t ss = {}; + ss.id = id; + ss.coreset_id = coreset_id; + ss.duration = 1; + ss.type = srsran_search_space_type_ue; + ss.nof_formats = 2; + ss.formats[0] = srsran_dci_format_nr_1_0; + ss.formats[1] = srsran_dci_format_nr_0_0; + ss.nof_candidates[0] = 2; + ss.nof_candidates[1] = 2; + ss.nof_candidates[2] = 2; + ss.nof_candidates[3] = 1; + ss.nof_candidates[4] = 0; + return ss; +} + } // namespace srsenb #endif // SRSRAN_SCHED_NR_CFG_GENERATORS_H diff --git a/srsgnb/src/stack/mac/test/sched_nr_common_test.cc b/srsgnb/src/stack/mac/test/sched_nr_common_test.cc index 1432e876c..293bd0411 100644 --- a/srsgnb/src/stack/mac/test/sched_nr_common_test.cc +++ b/srsgnb/src/stack/mac/test/sched_nr_common_test.cc @@ -21,29 +21,96 @@ #include "sched_nr_common_test.h" #include "srsgnb/hdr/stack/mac/sched_nr_cfg.h" +#include "srsgnb/hdr/stack/mac/sched_nr_helpers.h" #include "srsran/support/srsran_test.h" namespace srsenb { +using namespace sched_nr_impl; + +void test_dci_ctx_consistency(const srsran_pdcch_cfg_nr_t& pdcch_cfg, const srsran_dci_ctx_t& dci_ctx) +{ + TESTASSERT(dci_ctx.coreset_id < SRSRAN_UE_DL_NR_MAX_NOF_CORESET); + TESTASSERT(pdcch_cfg.coreset_present[dci_ctx.coreset_id]); + const srsran_coreset_t& coreset = pdcch_cfg.coreset[dci_ctx.coreset_id]; + + // DCI location + TESTASSERT(dci_ctx.location.L < SRSRAN_SEARCH_SPACE_NOF_AGGREGATION_LEVELS_NR); + TESTASSERT(dci_ctx.location.ncce + (1U << dci_ctx.location.L) <= coreset_nof_cces(coreset)); + // RNTI type, SearchSpace type, DCI format + TESTASSERT(sched_nr_impl::is_rnti_type_valid_in_search_space(dci_ctx.rnti_type, dci_ctx.ss_type)); + switch (dci_ctx.rnti_type) { + case srsran_rnti_type_si: + TESTASSERT_EQ(srsran_dci_format_nr_1_0, dci_ctx.format); + TESTASSERT_EQ(srsran_search_space_type_common_0, dci_ctx.ss_type); + TESTASSERT_EQ(SRSRAN_SIRNTI, dci_ctx.rnti); + break; + case srsran_rnti_type_ra: + TESTASSERT_EQ(dci_ctx.format, srsran_dci_format_nr_1_0); + TESTASSERT_EQ(dci_ctx.ss_type, srsran_search_space_type_common_1); + TESTASSERT(pdcch_cfg.ra_search_space_present); + TESTASSERT_EQ(pdcch_cfg.ra_search_space.coreset_id, dci_ctx.coreset_id); + TESTASSERT(pdcch_cfg.ra_search_space.nof_candidates[dci_ctx.location.L] > 0); + break; + case srsran_rnti_type_tc: + TESTASSERT_EQ(srsran_dci_format_nr_1_0, dci_ctx.format); + TESTASSERT_EQ(srsran_search_space_type_common_1, dci_ctx.ss_type); + break; + case srsran_rnti_type_c: + TESTASSERT(dci_ctx.format == srsran_dci_format_nr_1_0 or dci_ctx.format == srsran_dci_format_nr_1_1 or + dci_ctx.format == srsran_dci_format_nr_0_0 or dci_ctx.format == srsran_dci_format_nr_0_1); + break; + default: + srsran_terminate("rnti type=%d not supported", dci_ctx.rnti_type); + } + // CORESET position + TESTASSERT_EQ(srsran_coreset_start_rb(&coreset), dci_ctx.coreset_start_rb); +} + +void test_pdcch_collisions(const srsran_pdcch_cfg_nr_t& pdcch_cfg, + srsran::const_span dl_pdcchs, + srsran::const_span ul_pdcchs) +{ + srsran::optional_vector coreset_bitmaps; + for (const srsran_coreset_t& coreset : view_active_coresets(pdcch_cfg)) { + coreset_bitmaps.emplace(coreset.id, coreset_bitmap(coreset_nof_cces(coreset))); + } + + for (const pdcch_dl_t& pdcch : dl_pdcchs) { + coreset_bitmap& total_bitmap = coreset_bitmaps[pdcch.dci.ctx.coreset_id]; + coreset_bitmap alloc(total_bitmap.size()); + alloc.fill(pdcch.dci.ctx.location.ncce, pdcch.dci.ctx.location.ncce + (1U << pdcch.dci.ctx.location.L)); + TESTASSERT((alloc & total_bitmap).none()); + total_bitmap |= alloc; + } + for (const pdcch_ul_t& pdcch : ul_pdcchs) { + coreset_bitmap& total_bitmap = coreset_bitmaps[pdcch.dci.ctx.coreset_id]; + coreset_bitmap alloc(total_bitmap.size()); + alloc.fill(pdcch.dci.ctx.location.ncce, pdcch.dci.ctx.location.ncce + (1U << pdcch.dci.ctx.location.L)); + TESTASSERT((alloc & total_bitmap).none()); + total_bitmap |= alloc; + } +} + void test_dl_pdcch_consistency(const sched_nr_interface::cell_cfg_t& cell_cfg, srsran::const_span dl_pdcchs) { for (const auto& pdcch : dl_pdcchs) { TESTASSERT(pdcch.dci.bwp_id < cell_cfg.bwps.size()); const srsran_pdcch_cfg_nr_t& pdcch_cfg = cell_cfg.bwps[pdcch.dci.bwp_id].pdcch; - TESTASSERT(pdcch_cfg.coreset_present[pdcch.dci.ctx.coreset_id]); + test_dci_ctx_consistency(pdcch_cfg, pdcch.dci.ctx); - if (pdcch.dci.ctx.rnti_type == srsran_rnti_type_ra) { - TESTASSERT_EQ(pdcch.dci.ctx.format, srsran_dci_format_nr_1_0); - TESTASSERT_EQ(pdcch.dci.ctx.ss_type, srsran_search_space_type_common_1); - TESTASSERT(pdcch.dci.ctx.location.L < SRSRAN_SEARCH_SPACE_NOF_AGGREGATION_LEVELS_NR); + const srsran_coreset_t& coreset = pdcch_cfg.coreset[pdcch.dci.ctx.coreset_id]; + if (pdcch.dci.ctx.coreset_id == 0) { + TESTASSERT_EQ(srsran_coreset_get_bw(&pdcch_cfg.coreset[0]), pdcch.dci.coreset0_bw); + } - // check consistency with cell_cfg - TESTASSERT(pdcch_cfg.ra_search_space_present); - TESTASSERT_EQ(pdcch_cfg.ra_search_space.coreset_id, pdcch.dci.ctx.coreset_id); - TESTASSERT(pdcch_cfg.ra_search_space.nof_candidates[pdcch.dci.ctx.location.L] > 0); - } else if (pdcch.dci.ctx.rnti_type == srsran_rnti_type_c) { - TESTASSERT(pdcch.dci.ctx.format == srsran_dci_format_nr_1_0 or pdcch.dci.ctx.format == srsran_dci_format_nr_1_1); + switch (pdcch.dci.ctx.rnti_type) { + case srsran_rnti_type_si: + TESTASSERT(pdcch.dci.sii != 0 or pdcch.dci.ctx.coreset_id == 0); // sii=0 must go in CORESET#0 + break; + default: + break; } } } diff --git a/srsgnb/src/stack/mac/test/sched_nr_common_test.h b/srsgnb/src/stack/mac/test/sched_nr_common_test.h index 945fba068..c27315bc5 100644 --- a/srsgnb/src/stack/mac/test/sched_nr_common_test.h +++ b/srsgnb/src/stack/mac/test/sched_nr_common_test.h @@ -27,6 +27,14 @@ namespace srsenb { +/// Test DCI context consistency +void test_dci_ctx_consistency(const srsran_pdcch_cfg_nr_t& pdcch_cfg, const srsran_dci_ctx_t& dci); + +/// Test PDCCH collisions +void test_pdcch_collisions(const srsran_pdcch_cfg_nr_t& pdcch_cfg, + srsran::const_span dl_pdcchs, + srsran::const_span ul_pddchs); + void test_dl_pdcch_consistency(const sched_nr_interface::cell_cfg_t& cell_cfg, srsran::const_span dl_pdcch); void test_pdsch_consistency(srsran::const_span dl_pdcch); diff --git a/srsgnb/src/stack/mac/test/sched_nr_parallel_test.cc b/srsgnb/src/stack/mac/test/sched_nr_parallel_test.cc index cae98ba88..c132bca96 100644 --- a/srsgnb/src/stack/mac/test/sched_nr_parallel_test.cc +++ b/srsgnb/src/stack/mac/test/sched_nr_parallel_test.cc @@ -51,8 +51,8 @@ public: bool is_dl_slot = srsran_duplex_nr_is_dl(&cell_params[cc_out.res.cc].cfg.duplex, 0, current_slot_tx.slot_idx()); if (is_dl_slot) { - if (cc_out.res.dl->phy.ssb.empty()) { - TESTASSERT(slot_ctxt.ue_db.empty() or cc_out.res.dl->phy.pdcch_dl.size() == 1); + if (cc_out.res.dl->phy.ssb.empty() and not slot_ctxt.ue_db.empty()) { + TESTASSERT(slot_ctxt.ue_db.empty() or cc_out.res.dl->phy.pdcch_dl.size() >= 1); } else { TESTASSERT(cc_out.res.dl->phy.pdcch_dl.size() == 0); } @@ -95,6 +95,7 @@ void run_sched_nr_test(uint32_t nof_workers) slot_point slot_tx = slot_rx + TX_ENB_DELAY; if (slot_rx.to_uint() == 9) { sched_nr_interface::ue_cfg_t uecfg = get_default_ue_cfg(nof_sectors); + uecfg.ue_bearers[1].direction = mac_lc_ch_cfg_t::BOTH; tester.add_user(rnti, uecfg, slot_rx, 0); } tester.run_slot(slot_tx); diff --git a/srsgnb/src/stack/mac/test/sched_nr_pdcch_test.cc b/srsgnb/src/stack/mac/test/sched_nr_pdcch_test.cc new file mode 100644 index 000000000..bb38005d1 --- /dev/null +++ b/srsgnb/src/stack/mac/test/sched_nr_pdcch_test.cc @@ -0,0 +1,309 @@ +/** + * + * \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 "sched_nr_cfg_generators.h" +#include "sched_nr_common_test.h" +#include "srsgnb/hdr/stack/mac/sched_nr_interface_utils.h" +#include "srsgnb/hdr/stack/mac/sched_nr_pdcch.h" +#include "srsran/common/test_common.h" + +namespace srsenb { + +using namespace sched_nr_impl; + +/** + * Test for the case CORESET#0 is active. + * Given only one PDCCH candidate position is supported, only one PDCCH allocation should take place per slot + * The test additionally verifies that the DCI context content is correct for each PDCCH allocation + */ +void test_coreset0_cfg() +{ + const uint32_t aggr_idx = 2; + + srsran::test_delimit_logger delimiter{"Test PDCCH Allocation in CORESET#0"}; + + sched_nr_impl::cell_cfg_t cell_cfg = get_default_sa_cell_cfg_common(); + sched_nr_interface::sched_args_t sched_args; + bwp_params_t bwp_params{cell_cfg, sched_args, 0, 0}; + + // UE config + ue_cfg_t uecfg = get_rach_ue_cfg(0); + uecfg.phy_cfg.pdcch = cell_cfg.bwps[0].pdcch; // Starts without UE-specific PDCCH + ue_carrier_params_t ue_cc{0x46, bwp_params, uecfg}; + + pdcch_dl_list_t dl_pdcchs; + pdcch_ul_list_t ul_pdcchs; + pdcch_dl_alloc_result dl_pdcch_result; + pdcch_ul_alloc_result ul_pdcch_result; + pdcch_dl_t* dl_pdcch = nullptr; + pdcch_ul_t* ul_pdcch = nullptr; + + bwp_pdcch_allocator pdcch_sched(bwp_params, 0, dl_pdcchs, ul_pdcchs); + for (const srsran_coreset_t& cs : view_active_coresets(cell_cfg.bwps[0].pdcch)) { + // Verify nof CCEs is correctly computed + TESTASSERT_EQ(coreset_nof_cces(cs), pdcch_sched.nof_cces(cs.id)); + } + + // Slot with SIB1 + TESTASSERT_EQ(0, pdcch_sched.nof_allocations()); + + // SIB1 allocation should be successful + dl_pdcch_result = pdcch_sched.alloc_si_pdcch(0, aggr_idx); + TESTASSERT(dl_pdcch_result.has_value()); + dl_pdcch = dl_pdcch_result.value(); + TESTASSERT_EQ(1, pdcch_sched.nof_allocations()); + TESTASSERT_EQ(srsran_rnti_type_si, dl_pdcch->dci.ctx.rnti_type); + TESTASSERT_EQ(0, dl_pdcch->dci.ctx.coreset_id); + test_dci_ctx_consistency(bwp_params.cfg.pdcch, dl_pdcch->dci.ctx); + + // No space for RAR, UE PDSCH/PUSCH + TESTASSERT(pdcch_sched.alloc_rar_pdcch(0x2, aggr_idx).error() == alloc_result::no_cch_space); + TESTASSERT(pdcch_sched.alloc_dl_pdcch(srsran_rnti_type_c, 1, aggr_idx, ue_cc).error() == alloc_result::no_cch_space); + TESTASSERT(pdcch_sched.alloc_ul_pdcch(1, aggr_idx, ue_cc).error() == alloc_result::no_cch_space); + + srslog::fetch_basic_logger("TEST").info("%s", pdcch_sched.print_allocations()); + + // Slot with RAR + pdcch_sched.reset(); + + // RAR allocation should be successful + dl_pdcch_result = pdcch_sched.alloc_rar_pdcch(0x2, aggr_idx); + TESTASSERT(dl_pdcch_result.has_value()); + dl_pdcch = dl_pdcch_result.value(); + TESTASSERT_EQ(1, pdcch_sched.nof_allocations()); + TESTASSERT_EQ(srsran_rnti_type_ra, dl_pdcch->dci.ctx.rnti_type); + TESTASSERT_EQ(0, dl_pdcch->dci.ctx.coreset_id); + TESTASSERT_EQ(srsran_search_space_type_common_1, dl_pdcch->dci.ctx.ss_type); + test_dci_ctx_consistency(bwp_params.cfg.pdcch, dl_pdcch->dci.ctx); + + // No space for RAR, UE PDSCH/PUSCH + TESTASSERT(pdcch_sched.alloc_dl_pdcch(srsran_rnti_type_c, 1, aggr_idx, ue_cc).error() == alloc_result::no_cch_space); + TESTASSERT(pdcch_sched.alloc_ul_pdcch(1, aggr_idx, ue_cc).error() == alloc_result::no_cch_space); + + srslog::fetch_basic_logger("TEST").info("%s", pdcch_sched.print_allocations()); + + // Slot with DL PDSCH + pdcch_sched.reset(); + + // 1st PDCCH allocation for DL should be successful + dl_pdcch_result = pdcch_sched.alloc_dl_pdcch(srsran_rnti_type_c, 1, aggr_idx, ue_cc); + TESTASSERT(dl_pdcch_result.has_value()); + dl_pdcch = dl_pdcch_result.value(); + TESTASSERT_EQ(1, pdcch_sched.nof_allocations()); + TESTASSERT_EQ(srsran_rnti_type_c, dl_pdcch->dci.ctx.rnti_type); + TESTASSERT_EQ(0u, dl_pdcch->dci.ctx.coreset_id); + TESTASSERT_EQ(srsran_search_space_type_common_1, dl_pdcch->dci.ctx.ss_type); + test_dci_ctx_consistency(bwp_params.cfg.pdcch, dl_pdcch->dci.ctx); + + // No space for 2nd PDCCH allocation + TESTASSERT(pdcch_sched.alloc_ul_pdcch(1, aggr_idx, ue_cc).error() == alloc_result::no_cch_space); + + srslog::fetch_basic_logger("TEST").info("%s", pdcch_sched.print_allocations()); + + // Slot with UL PDSCH + pdcch_sched.reset(); + + // 1st PDCCH allocation for UL should be successful + ul_pdcch_result = pdcch_sched.alloc_ul_pdcch(1, aggr_idx, ue_cc); + TESTASSERT(ul_pdcch_result.has_value()); + ul_pdcch = ul_pdcch_result.value(); + TESTASSERT_EQ(1, pdcch_sched.nof_allocations()); + TESTASSERT_EQ(srsran_rnti_type_c, ul_pdcch->dci.ctx.rnti_type); + TESTASSERT_EQ(0u, ul_pdcch->dci.ctx.coreset_id); + TESTASSERT_EQ(srsran_search_space_type_common_1, ul_pdcch->dci.ctx.ss_type); + test_dci_ctx_consistency(bwp_params.cfg.pdcch, ul_pdcch->dci.ctx); + + // No space for 2nd PDCCH allocation + TESTASSERT(pdcch_sched.alloc_dl_pdcch(srsran_rnti_type_c, 1, aggr_idx, ue_cc).error() == alloc_result::no_cch_space); + + srslog::fetch_basic_logger("TEST").info("%s", pdcch_sched.print_allocations()); +} + +/** + * Test for the case CORESET#2 is active. + * The PDCCH allocator should find enough space to fit SIB1/RAR (in CORESET#0) and UE-dedicated PDCCHs in (CORESET#2) + * The test additionally verifies that the DCI context content is correct for each PDCCH allocation and there are no + * collisions between PDCCH CCE allocations + */ +void test_coreset2_cfg() +{ + const uint32_t aggr_idx = 2; + + srsran::test_delimit_logger delimiter{"Test PDCCH Allocation in CORESET#0 and CORESET#2"}; + + sched_nr_impl::cell_cfg_t cell_cfg = get_default_sa_cell_cfg_common(); + cell_cfg.bwps[0].pdcch.search_space_present[2] = true; + cell_cfg.bwps[0].pdcch.search_space[2] = get_default_ue_specific_search_space(2, 2); + cell_cfg.bwps[0].pdcch.coreset_present[2] = true; + cell_cfg.bwps[0].pdcch.coreset[2] = get_default_ue_specific_coreset(2, cell_cfg.carrier.pci); + sched_nr_interface::sched_args_t sched_args; + bwp_params_t bwp_params{cell_cfg, sched_args, 0, 0}; + + // UE config + ue_cfg_t uecfg = get_rach_ue_cfg(0); + uecfg.phy_cfg = get_common_ue_phy_cfg(cell_cfg); + uecfg.phy_cfg.pdcch = cell_cfg.bwps[0].pdcch; // Starts with UE-specific PDCCH + ue_carrier_params_t ue_cc{0x46, bwp_params, uecfg}; + + pdcch_dl_list_t dl_pdcchs; + pdcch_ul_list_t ul_pdcchs; + pdcch_dl_t* dl_pdcch = nullptr; + pdcch_ul_t* ul_pdcch = nullptr; + + bwp_pdcch_allocator pdcch_sched(bwp_params, 0, dl_pdcchs, ul_pdcchs); + for (const srsran_coreset_t& cs : view_active_coresets(cell_cfg.bwps[0].pdcch)) { + // Verify nof CCEs is correctly computed + TESTASSERT_EQ(coreset_nof_cces(cs), pdcch_sched.nof_cces(cs.id)); + } + + // Slot with SIB1 + DL PDCCH and UL PDCCH + TESTASSERT_EQ(0, pdcch_sched.nof_allocations()); + + // SIB1 allocation should be successful + dl_pdcch = pdcch_sched.alloc_si_pdcch(0, aggr_idx).value(); + TESTASSERT(dl_pdcch != nullptr); + TESTASSERT_EQ(1, pdcch_sched.nof_allocations()); + TESTASSERT_EQ(srsran_rnti_type_si, dl_pdcch->dci.ctx.rnti_type); + TESTASSERT_EQ(0, dl_pdcch->dci.ctx.coreset_id); + srsran_dci_location_t expected_loc{aggr_idx, 0}; + TESTASSERT(dl_pdcch->dci.ctx.location == expected_loc); + test_dci_ctx_consistency(bwp_params.cfg.pdcch, dl_pdcch->dci.ctx); + + // No space for RAR or PDSCH in SS#1 + TESTASSERT(pdcch_sched.alloc_rar_pdcch(0x2, aggr_idx).error() == alloc_result::no_cch_space); + TESTASSERT(pdcch_sched.alloc_dl_pdcch(srsran_rnti_type_c, 1, aggr_idx, ue_cc).error() == alloc_result::no_cch_space); + TESTASSERT(pdcch_sched.alloc_ul_pdcch(1, aggr_idx, ue_cc).error() == alloc_result::no_cch_space); + + // there is space for UE DL PDCCH in SS#2 + dl_pdcch = pdcch_sched.alloc_dl_pdcch(srsran_rnti_type_c, 2, aggr_idx, ue_cc).value(); + TESTASSERT(dl_pdcch != nullptr); + TESTASSERT_EQ(2, pdcch_sched.nof_allocations()); + TESTASSERT_EQ(srsran_rnti_type_c, dl_pdcch->dci.ctx.rnti_type); + TESTASSERT_EQ(2u, dl_pdcch->dci.ctx.coreset_id); + TESTASSERT_EQ(srsran_search_space_type_ue, dl_pdcch->dci.ctx.ss_type); + expected_loc = srsran_dci_location_t{aggr_idx, 0}; + TESTASSERT(dl_pdcch->dci.ctx.location == expected_loc); + test_dci_ctx_consistency(bwp_params.cfg.pdcch, dl_pdcch->dci.ctx); + + // there is space for UE UL PDCCH in SS#2 + ul_pdcch = pdcch_sched.alloc_ul_pdcch(2, aggr_idx, ue_cc).value(); + TESTASSERT(ul_pdcch != nullptr); + TESTASSERT_EQ(3, pdcch_sched.nof_allocations()); + TESTASSERT_EQ(srsran_rnti_type_c, ul_pdcch->dci.ctx.rnti_type); + TESTASSERT_EQ(srsran_dci_format_nr_0_0, ul_pdcch->dci.ctx.format); + TESTASSERT_EQ(2u, ul_pdcch->dci.ctx.coreset_id); + TESTASSERT_EQ(srsran_search_space_type_ue, ul_pdcch->dci.ctx.ss_type); + expected_loc = srsran_dci_location_t{aggr_idx, 4}; + TESTASSERT(ul_pdcch->dci.ctx.location == expected_loc); + test_dci_ctx_consistency(bwp_params.cfg.pdcch, ul_pdcch->dci.ctx); + + // No space for 3rd PDCCH allocation in SS#2 + TESTASSERT(pdcch_sched.alloc_dl_pdcch(srsran_rnti_type_c, 2, aggr_idx, ue_cc).error() == alloc_result::no_cch_space); + + // Verify there are no PDCCH collisions + TESTASSERT_EQ(3, pdcch_sched.nof_allocations()); + TESTASSERT_EQ(2, dl_pdcchs.size()); + TESTASSERT_EQ(1, ul_pdcchs.size()); + test_pdcch_collisions(bwp_params.cfg.pdcch, dl_pdcchs, ul_pdcchs); + + srslog::fetch_basic_logger("TEST").info("%s", pdcch_sched.print_allocations()); + + // Verify all coresets are correctly cleaned up + pdcch_sched.reset(); + TESTASSERT_EQ(0, pdcch_sched.nof_allocations()); + TESTASSERT_EQ(0, dl_pdcchs.size()); + TESTASSERT_EQ(0, ul_pdcchs.size()); +} + +void test_invalid_params() +{ + const uint32_t aggr_idx = 2; + + srsran::test_delimit_logger delimiter{"Test PDCCH Allocation with Invalid Arguments"}; + + sched_nr_impl::cell_cfg_t cell_cfg = get_default_sa_cell_cfg_common(); + cell_cfg.bwps[0].pdcch.search_space_present[2] = true; + cell_cfg.bwps[0].pdcch.search_space[2] = get_default_ue_specific_search_space(2, 2); + cell_cfg.bwps[0].pdcch.coreset_present[2] = true; + cell_cfg.bwps[0].pdcch.coreset[2] = get_default_ue_specific_coreset(2, cell_cfg.carrier.pci); + cell_cfg.bwps[0].pdcch.search_space_present[3] = true; + cell_cfg.bwps[0].pdcch.search_space[3] = get_default_ue_specific_search_space(3, 2); + cell_cfg.bwps[0].pdcch.search_space[3].nof_formats = 1; // only DL + cell_cfg.bwps[0].pdcch.search_space[3].formats[0] = srsran_dci_format_nr_1_0; + sched_nr_interface::sched_args_t sched_args; + bwp_params_t bwp_params{cell_cfg, sched_args, 0, 0}; + + // UE config + ue_cfg_t uecfg = get_rach_ue_cfg(0); + uecfg.phy_cfg = get_common_ue_phy_cfg(cell_cfg); + uecfg.phy_cfg.pdcch = cell_cfg.bwps[0].pdcch; // Starts with UE-specific PDCCH + ue_carrier_params_t ue_cc{0x46, bwp_params, uecfg}; + + pdcch_dl_list_t dl_pdcchs; + pdcch_ul_list_t ul_pdcchs; + pdcch_dl_alloc_result dl_res; + pdcch_ul_alloc_result ul_res; + + bwp_pdcch_allocator pdcch_sched(bwp_params, 0, dl_pdcchs, ul_pdcchs); + + // Slot with SIB1 + DL PDCCH and UL PDCCH + TESTASSERT_EQ(0, pdcch_sched.nof_allocations()); + + // Pass UE search space for SI alloc + dl_res = pdcch_sched.alloc_si_pdcch(2, aggr_idx); + TESTASSERT(dl_res.is_error() and dl_res.error() == alloc_result::invalid_grant_params); + + // Pass aggregation index for which there are no candidates + dl_res = pdcch_sched.alloc_si_pdcch(2, 4); + TESTASSERT(dl_res.is_error() and dl_res.error() == alloc_result::invalid_grant_params); + + // SearchSpace must exist + dl_res = pdcch_sched.alloc_dl_pdcch(srsran_rnti_type_c, 4, aggr_idx, ue_cc); + TESTASSERT(dl_res.is_error() and dl_res.error() == alloc_result::invalid_grant_params); + + // TC-RNTI cannot be allocated in Common SearchSpace Type1 + dl_res = pdcch_sched.alloc_dl_pdcch(srsran_rnti_type_tc, 2, aggr_idx, ue_cc); + TESTASSERT(dl_res.is_error() and dl_res.error() == alloc_result::invalid_grant_params); + + // C-RNTI cannot be allocated in Common SearchSpace Type0 + dl_res = pdcch_sched.alloc_dl_pdcch(srsran_rnti_type_c, 0, aggr_idx, ue_cc); + TESTASSERT(dl_res.is_error() and dl_res.error() == alloc_result::invalid_grant_params); + + // UL allocation cannot be made in SearchSpace without DCI format 0_0 + ul_res = pdcch_sched.alloc_ul_pdcch(3, aggr_idx, ue_cc); + TESTASSERT(ul_res.is_error() and ul_res.error() == alloc_result::invalid_grant_params); + + // Success case + TESTASSERT(pdcch_sched.nof_allocations() == 0); + ul_res = pdcch_sched.alloc_ul_pdcch(2, aggr_idx, ue_cc); + TESTASSERT(ul_res.has_value() and ul_res.value()->dci.ctx.format == srsran_dci_format_nr_0_0); + TESTASSERT(pdcch_sched.nof_allocations() == 1); +} + +} // namespace srsenb + +int main() +{ + auto& test_logger = srslog::fetch_basic_logger("TEST"); + test_logger.set_level(srslog::basic_levels::info); + auto& mac_nr_logger = srslog::fetch_basic_logger("MAC-NR"); + mac_nr_logger.set_level(srslog::basic_levels::debug); + auto& pool_logger = srslog::fetch_basic_logger("POOL"); + pool_logger.set_level(srslog::basic_levels::debug); + + // Start the log backend. + srslog::init(); + + srsenb::test_coreset0_cfg(); + srsenb::test_coreset2_cfg(); + srsenb::test_invalid_params(); +} \ No newline at end of file diff --git a/srsgnb/src/stack/mac/test/sched_nr_rar_test.cc b/srsgnb/src/stack/mac/test/sched_nr_rar_test.cc index d175ac1be..2b40549f8 100644 --- a/srsgnb/src/stack/mac/test/sched_nr_rar_test.cc +++ b/srsgnb/src/stack/mac/test/sched_nr_rar_test.cc @@ -113,7 +113,7 @@ void test_single_prach() result = run_slot(); if (bwpparams.slots[current_slot.slot_idx()].is_dl and bwpparams.slots[(current_slot + bwpparams.pusch_ra_list[0].msg3_delay).slot_idx()].is_ul) { - TESTASSERT_EQ(result->dl.phy.pdcch_dl.size(), 1); + TESTASSERT_EQ(1, result->dl.phy.pdcch_dl.size()); const auto& pdcch = result->dl.phy.pdcch_dl[0]; TESTASSERT_EQ(pdcch.dci.ctx.rnti, ra_rnti); TESTASSERT_EQ(pdcch.dci.ctx.rnti_type, srsran_rnti_type_ra); diff --git a/srsgnb/src/stack/mac/test/sched_nr_sch_test.cc b/srsgnb/src/stack/mac/test/sched_nr_sch_test.cc new file mode 100644 index 000000000..d27f3d466 --- /dev/null +++ b/srsgnb/src/stack/mac/test/sched_nr_sch_test.cc @@ -0,0 +1,482 @@ +/** + * + * \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 "sched_nr_cfg_generators.h" +#include "srsgnb/hdr/stack/mac/sched_nr_interface_utils.h" +#include "srsgnb/hdr/stack/mac/sched_nr_sch.h" +#include "srsran/common/test_common.h" +extern "C" { +#include "srsran/phy/common/sliv.h" +} + +namespace srsenb { + +using namespace sched_nr_impl; + +sched_nr_interface::cell_cfg_t get_cell_cfg() +{ + sched_nr_impl::cell_cfg_t cell_cfg = get_default_sa_cell_cfg_common(); + cell_cfg.bwps[0].pdcch.search_space_present[2] = true; + cell_cfg.bwps[0].pdcch.search_space[2] = get_default_ue_specific_search_space(2, 2); + cell_cfg.bwps[0].pdcch.coreset_present[2] = true; + cell_cfg.bwps[0].pdcch.coreset[2] = get_default_ue_specific_coreset(2, cell_cfg.carrier.pci); + return cell_cfg; +} + +sched_nr_interface::ue_cfg_t get_ue_cfg(const sched_nr_interface::cell_cfg_t& cell_cfg) +{ + ue_cfg_t uecfg = get_rach_ue_cfg(0); + uecfg.phy_cfg = get_common_ue_phy_cfg(cell_cfg); + uecfg.phy_cfg.pdcch = cell_cfg.bwps[0].pdcch; // Starts with UE-specific PDCCH + return uecfg; +} + +srsran_dci_ctx_t generate_dci_ctx(const srsran_pdcch_cfg_nr_t& pdcch, + uint32_t ss_id, + srsran_rnti_type_t rnti_type, + uint16_t rnti, + srsran_dci_format_nr_t dci_fmt = srsran_dci_format_nr_1_0) +{ + const srsran_search_space_t& ss = pdcch.search_space[ss_id]; + const srsran_coreset_t& cs = pdcch.coreset[ss.coreset_id]; + + srsran_dci_ctx_t ctx; + ctx.location = {2, 4}; + ctx.ss_type = ss.type; + ctx.coreset_id = ss.coreset_id; + ctx.coreset_start_rb = srsran_coreset_start_rb(&cs); + ctx.rnti_type = rnti_type; + ctx.format = dci_fmt; + ctx.rnti = rnti; + return ctx; +} + +void test_dci_freq_assignment(const bwp_params_t& bwp_params, prb_interval grant, const pdcch_dl_t& pdcch) +{ + // Compute BWP PRB limits + prb_interval lims{0, bwp_params.nof_prb()}; + if (SRSRAN_SEARCH_SPACE_IS_COMMON(pdcch.dci.ctx.ss_type) and pdcch.dci.ctx.format == srsran_dci_format_nr_1_0) { + lims = bwp_params.dci_fmt_1_0_prb_lims(pdcch.dci.ctx.coreset_id); + } + + // RB indexing should start from the first PRB of CORESET + uint32_t expected_freq_assignment = + srsran_ra_nr_type1_riv(lims.length(), grant.start() - lims.start(), grant.length()); + TESTASSERT_EQ(expected_freq_assignment, pdcch.dci.freq_domain_assigment); + + uint32_t st, len; + srsran_sliv_to_s_and_l(lims.length(), pdcch.dci.freq_domain_assigment, &st, &len); + prb_interval allocated_prbs{st + lims.start(), st + lims.start() + len}; + TESTASSERT(allocated_prbs == grant); +} + +void test_si() +{ + srsran::test_delimit_logger delimiter{"Test PDSCH SI Allocation"}; + + static const uint32_t ss_id = 0; + + // Create Cell and UE configs + sched_nr_impl::cell_cfg_t cell_cfg = get_cell_cfg(); + sched_nr_interface::sched_args_t sched_args; + bwp_params_t bwp_params{cell_cfg, sched_args, 0, 0}; + + pdsch_list_t pdschs; + pdsch_alloc_result alloc_res; + + pdsch_allocator pdsch_sched(bwp_params, 0, pdschs); + + pdcch_dl_t pdcch; + pdcch.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, ss_id, srsran_rnti_type_si, SRSRAN_SIRNTI); + + uint32_t min_prb = bwp_params.dci_fmt_1_0_prb_lims(pdcch.dci.ctx.coreset_id).start(); + uint32_t max_prb = bwp_params.dci_fmt_1_0_prb_lims(pdcch.dci.ctx.coreset_id).stop(); + + std::array grant_list = { + prb_interval{2, 4}, prb_interval{min_prb, max_prb}, prb_interval{0, bwp_params.nof_prb()}}; + + for (uint32_t i = 0; i < grant_list.size(); ++i) { + pdsch_sched.reset(); + TESTASSERT_EQ(0, pdschs.size()); + + prb_interval grant = grant_list[i]; + + bool success_expected = grant.start() >= min_prb and grant.stop() <= max_prb; + + alloc_result check_ret = pdsch_sched.is_si_grant_valid(ss_id, grant); + prb_bitmap avail_prbs = pdsch_sched.occupied_prbs(ss_id, srsran_dci_format_nr_1_0); + TESTASSERT_EQ((int)min_prb, avail_prbs.find_lowest(0, avail_prbs.size(), false)); + TESTASSERT_EQ((int)max_prb, avail_prbs.find_lowest(min_prb, avail_prbs.size(), true)); + + printf("Attempt %d should be %ssuccessful\n", i, success_expected ? "" : "un"); + alloc_res = pdsch_sched.alloc_si_pdsch(ss_id, grant, pdcch.dci); + if (success_expected) { + // SIB1 allocation doesnt go outside CORESET#0 BW + TESTASSERT(alloc_res.has_value()); + TESTASSERT_EQ(1, pdschs.size()); + TESTASSERT(&pdschs.back() == alloc_res.value()); + TESTASSERT_EQ(0, pdcch.dci.time_domain_assigment); + + TESTASSERT(not avail_prbs.any(grant.start(), grant.stop())); + + test_dci_freq_assignment(bwp_params, grant, pdcch); + } else { + TESTASSERT(alloc_res.is_error()); + TESTASSERT(check_ret == alloc_res.error()); + TESTASSERT_EQ(0, pdschs.size()); + TESTASSERT(avail_prbs.any(grant.start(), grant.stop())); + } + } +} + +void test_rar() +{ + srsran::test_delimit_logger delimiter{"Test PDSCH RAR Allocation"}; + static const uint32_t ss_id = 1; + + // Create Cell and UE configs + sched_nr_impl::cell_cfg_t cell_cfg = get_cell_cfg(); + sched_nr_interface::sched_args_t sched_args; + bwp_params_t bwp_params{cell_cfg, sched_args, 0, 0}; + + pdsch_list_t pdschs; + pdsch_alloc_result alloc_res; + + pdsch_allocator pdsch_sched(bwp_params, 0, pdschs); + + pdcch_dl_t pdcch; + pdcch.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, ss_id, srsran_rnti_type_ra, 0x2); + + uint32_t min_prb = bwp_params.dci_fmt_1_0_prb_lims(pdcch.dci.ctx.coreset_id).start(); + uint32_t max_prb = bwp_params.dci_fmt_1_0_prb_lims(pdcch.dci.ctx.coreset_id).stop(); + + std::array grant_list = { + prb_interval{2, 4}, prb_interval{min_prb, max_prb}, prb_interval{0, bwp_params.nof_prb()}}; + + for (uint32_t i = 0; i < grant_list.size(); ++i) { + pdsch_sched.reset(); + TESTASSERT_EQ(0, pdschs.size()); + + prb_interval grant = grant_list[i]; + + bool success_expected = grant.start() >= min_prb and grant.stop() <= max_prb; + + alloc_result check_ret = pdsch_sched.is_rar_grant_valid(grant); + prb_bitmap avail_prbs = pdsch_sched.occupied_prbs(ss_id, srsran_dci_format_nr_1_0); + TESTASSERT_EQ((int)min_prb, avail_prbs.find_lowest(0, avail_prbs.size(), false)); + TESTASSERT_EQ((int)max_prb, avail_prbs.find_lowest(min_prb, avail_prbs.size(), true)); + + printf("Attempt %d should be %ssuccessful\n", i, success_expected ? "" : "un"); + alloc_res = pdsch_sched.alloc_rar_pdsch(grant, pdcch.dci); + if (success_expected) { + // SIB1 allocation doesnt go outside CORESET#0 BW + TESTASSERT(alloc_res.has_value()); + TESTASSERT_EQ(1, pdschs.size()); + TESTASSERT(&pdschs.back() == alloc_res.value()); + TESTASSERT_EQ(0, pdcch.dci.time_domain_assigment); + + TESTASSERT(not avail_prbs.any(grant.start(), grant.stop())); + + test_dci_freq_assignment(bwp_params, grant, pdcch); + } else { + TESTASSERT(alloc_res.is_error()); + TESTASSERT(check_ret == alloc_res.error()); + TESTASSERT_EQ(0, pdschs.size()); + TESTASSERT(avail_prbs.any(grant.start(), grant.stop())); + } + } +} + +void test_ue_pdsch() +{ + srsran::test_delimit_logger delimiter{"Test PDSCH UE Allocation"}; + + // Create Cell and UE configs + sched_nr_impl::cell_cfg_t cell_cfg = get_cell_cfg(); + sched_nr_impl::ue_cfg_t uecfg = get_ue_cfg(cell_cfg); + sched_nr_interface::sched_args_t sched_args; + bwp_params_t bwp_params{cell_cfg, sched_args, 0, 0}; + ue_carrier_params_t ue_cc{0x4601, bwp_params, uecfg}; + + pdsch_list_t pdschs; + pdsch_alloc_result alloc_res; + + pdsch_allocator pdsch_sched(bwp_params, 0, pdschs); + + pdcch_dl_t pdcch_common, pdcch_ue; + pdcch_common.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, 1, srsran_rnti_type_c, 0x4601); + pdcch_ue.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, 2, srsran_rnti_type_c, 0x4601); + + prb_interval lims_common = bwp_params.dci_fmt_1_0_prb_lims(pdcch_common.dci.ctx.coreset_id); + prb_interval lims_ue{0, bwp_params.nof_prb()}; + + std::array, 4> grant_list = {std::make_pair(1, prb_interval{2, 4}), + std::make_pair(1, lims_common), + std::make_pair(1, lims_ue), + std::make_pair(2, lims_common)}; + + for (uint32_t i = 0; i < grant_list.size(); ++i) { + pdsch_sched.reset(); + TESTASSERT_EQ(0, pdschs.size()); + + auto g = grant_list[i]; + uint32_t ss_id = g.first; + prb_interval grant = g.second; + prb_interval lims = ss_id == 1 ? lims_common : lims_ue; + pdcch_dl_t& pdcch = ss_id == 1 ? pdcch_common : pdcch_ue; + + bool success_expected = grant.start() >= lims.start() and grant.stop() <= lims.stop(); + + alloc_result check_ret = pdsch_sched.is_ue_grant_valid(ue_cc, ss_id, srsran_dci_format_nr_1_0, grant); + prb_bitmap avail_prbs = pdsch_sched.occupied_prbs(ss_id, srsran_dci_format_nr_1_0); + int pos = avail_prbs.find_lowest(0, avail_prbs.size(), false); + TESTASSERT_EQ((int)lims.start(), pos); + pos = avail_prbs.find_lowest(lims.start(), avail_prbs.size(), true); + TESTASSERT_EQ((int)lims.stop(), (pos < 0 ? (int)avail_prbs.size() : pos)); + + printf("Attempt %d should be %ssuccessful\n", i, success_expected ? "" : "un"); + alloc_res = pdsch_sched.alloc_ue_pdsch(ss_id, srsran_dci_format_nr_1_0, grant, ue_cc, pdcch.dci); + TESTASSERT(success_expected == alloc_res.has_value()); + if (success_expected) { + // SIB1 allocation doesnt go outside CORESET#0 BW + TESTASSERT_EQ(1, pdschs.size()); + TESTASSERT(&pdschs.back() == alloc_res.value()); + TESTASSERT_EQ(0, pdcch.dci.time_domain_assigment); + + TESTASSERT(not avail_prbs.any(grant.start(), grant.stop())); + + test_dci_freq_assignment(bwp_params, grant, pdcch); + } else { + TESTASSERT(check_ret == alloc_res.error()); + TESTASSERT_EQ(0, pdschs.size()); + TESTASSERT(avail_prbs.any(grant.start(), grant.stop())); + } + } +} + +void test_pdsch_fail() +{ + srsran::test_delimit_logger delimiter{"Test PDSCH Allocation Failure"}; + + // Create Cell and UE configs + sched_nr_impl::cell_cfg_t cell_cfg = get_cell_cfg(); + sched_nr_impl::ue_cfg_t uecfg = get_ue_cfg(cell_cfg); + sched_nr_interface::sched_args_t sched_args; + bwp_params_t bwp_params{cell_cfg, sched_args, 0, 0}; + ue_carrier_params_t ue_cc{0x4601, bwp_params, uecfg}; + + pdsch_list_t pdschs; + pdsch_alloc_result alloc_res; + + pdsch_allocator pdsch_sched(bwp_params, 0, pdschs); + + pdcch_dl_t pdcch_common, pdcch_ue, pdcch_rar, pdcch_si, pdcch; + pdcch_si.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, 0, srsran_rnti_type_si, SRSRAN_SIRNTI); + pdcch_rar.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, 1, srsran_rnti_type_ra, 0x2); + pdcch_common.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, 1, srsran_rnti_type_c, 0x4601); + pdcch_ue.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, 2, srsran_rnti_type_c, 0x4601); + + // Allocations of type 0 are not compatible with DCI format 1_0 + rbg_bitmap rbgs(bwp_params.N_rbg); + rbgs.set(1); + rbgs.set(3); + prb_grant grant_type0 = rbgs; + TESTASSERT_EQ(alloc_result::invalid_grant_params, pdsch_sched.alloc_si_pdsch(0, grant_type0, pdcch_si.dci).error()); + TESTASSERT_EQ(alloc_result::invalid_grant_params, pdsch_sched.alloc_rar_pdsch(grant_type0, pdcch_rar.dci).error()); + TESTASSERT_EQ(alloc_result::invalid_grant_params, + pdsch_sched.alloc_ue_pdsch(1, srsran_dci_format_nr_1_0, grant_type0, ue_cc, pdcch.dci).error()); + TESTASSERT_EQ(alloc_result::invalid_grant_params, + pdsch_sched.alloc_ue_pdsch(2, srsran_dci_format_nr_1_0, grant_type0, ue_cc, pdcch.dci).error()); + + // Resource Allocation type must be compatible with UE PDSCH configuration + TESTASSERT_EQ(alloc_result::invalid_grant_params, + pdsch_sched.alloc_ue_pdsch(2, srsran_dci_format_nr_1_1, grant_type0, ue_cc, pdcch.dci).error()); + + // Allocations of DCI format 1_0 should start from CORESET first RB and their BW should be limited by CORESET#0 BW + prb_grant grant_type1 = prb_interval{0, bwp_params.coreset_prb_range(0).stop()}; + TESTASSERT(pdsch_sched.alloc_ue_pdsch(1, srsran_dci_format_nr_1_0, grant_type1, ue_cc, pdcch.dci).is_error()); + grant_type1 = prb_interval{bwp_params.coreset_prb_range(0).start(), bwp_params.nof_prb()}; + TESTASSERT(pdsch_sched.alloc_ue_pdsch(1, srsran_dci_format_nr_1_0, grant_type1, ue_cc, pdcch.dci).is_error()); + TESTASSERT(pdsch_sched.alloc_ue_pdsch(2, srsran_dci_format_nr_1_0, grant_type1, ue_cc, pdcch.dci).has_value()); + + // PRB collisions are detected + TESTASSERT(pdsch_sched.alloc_ue_pdsch(2, srsran_dci_format_nr_1_0, prb_interval{5, 6}, ue_cc, pdcch.dci).is_error()); +} + +void test_multi_pdsch() +{ + srsran::test_delimit_logger delimiter{"Test Multiple PDSCH Allocations"}; + + // Create Cell and UE configs + sched_nr_impl::cell_cfg_t cell_cfg = get_cell_cfg(); + sched_nr_impl::ue_cfg_t uecfg = get_ue_cfg(cell_cfg); + sched_nr_interface::sched_args_t sched_args; + bwp_params_t bwp_params{cell_cfg, sched_args, 0, 0}; + ue_carrier_params_t ue_cc{0x4601, bwp_params, uecfg}; + ue_carrier_params_t ue_cc2{0x4602, bwp_params, uecfg}; + + pdsch_list_t pdschs; + pdsch_alloc_result alloc_res; + + pdsch_allocator pdsch_sched(bwp_params, 0, pdschs); + + pdcch_dl_t pdcch_sib, pdcch_common, pdcch_ue; + pdcch_sib.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, 0, srsran_rnti_type_si, SRSRAN_SIRNTI); + pdcch_common.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, 1, srsran_rnti_type_c, 0x4601); + pdcch_ue.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, 2, srsran_rnti_type_c, 0x4602); + + // Allocate SIB1 + uint32_t ss_id = 0; + pdcch_dl_t* pdcch = &pdcch_sib; + prb_bitmap used_prbs = pdsch_sched.occupied_prbs(ss_id, srsran_dci_format_nr_1_0); + fmt::print("No allocations yet. Occupied PRBs for common SearchSpace: {:b}\n", used_prbs); + uint32_t sib1_grant_size = 4; + prb_bitmap sib_prbs = ~used_prbs; + int first_prb = sib_prbs.find_lowest(0, sib_prbs.size(), true); + prb_interval sib_grant{(uint32_t)first_prb, sib1_grant_size}; + TESTASSERT_EQ(alloc_result::success, pdsch_sched.is_si_grant_valid(ss_id, sib_grant)); + alloc_res = pdsch_sched.alloc_si_pdsch(ss_id, sib_grant, pdcch->dci); + TESTASSERT(alloc_res.has_value()); + test_dci_freq_assignment(bwp_params, sib_grant, *pdcch); + prb_bitmap used_prbs_sib1 = pdsch_sched.occupied_prbs(ss_id, srsran_dci_format_nr_1_0); + TESTASSERT_EQ(used_prbs_sib1.count(), used_prbs.count() + sib_grant.length()); + TESTASSERT_EQ(alloc_result::sch_collision, pdsch_sched.is_si_grant_valid(ss_id, sib_grant)); + + prb_bitmap last_prb_bitmap(used_prbs.size()); + last_prb_bitmap.fill(sib_grant.start(), sib_grant.stop()); + fmt::print("SIB1 allocated. Occupied PRBs:\n{:b} -> {:b}\n", last_prb_bitmap, used_prbs_sib1); + + // Allocate UE in common SearchSpace + ss_id = 1; + pdcch = &pdcch_common; + prb_bitmap ue_prbs = ~used_prbs_sib1; + first_prb = ue_prbs.find_lowest(0, ue_prbs.size(), true); + uint32_t ue_grant_size = 10; + prb_interval ue_grant{(uint32_t)first_prb, ue_grant_size}; + TESTASSERT_EQ(alloc_result::success, pdsch_sched.is_ue_grant_valid(ue_cc, ss_id, srsran_dci_format_nr_1_0, ue_grant)); + alloc_res = pdsch_sched.alloc_ue_pdsch(ss_id, srsran_dci_format_nr_1_0, ue_grant, ue_cc, pdcch->dci); + TESTASSERT(alloc_res.has_value()); + test_dci_freq_assignment(bwp_params, ue_grant, *pdcch); + prb_bitmap used_prbs_ue = pdsch_sched.occupied_prbs(ss_id, srsran_dci_format_nr_1_0); + TESTASSERT_EQ(used_prbs_ue.count(), used_prbs_sib1.count() + ue_grant.length()); + TESTASSERT_EQ(alloc_result::sch_collision, + pdsch_sched.is_ue_grant_valid(ue_cc, ss_id, srsran_dci_format_nr_1_0, ue_grant)); + + last_prb_bitmap.reset(); + last_prb_bitmap.fill(ue_grant.start(), ue_grant.stop()); + fmt::print("C-RNTI allocated in Common SearchSpace. Occupied PRBs:\n{:b} -> {:b}\n", last_prb_bitmap, used_prbs_ue); + + // Allocate UE in UE SearchSpace + ss_id = 2; + pdcch = &pdcch_ue; + used_prbs = pdsch_sched.occupied_prbs(ss_id, srsran_dci_format_nr_1_0); + prb_interval ue_grant2 = find_empty_interval_of_length(used_prbs, used_prbs_ue.size(), 0); + TESTASSERT_EQ(bwp_params.nof_prb(), ue_grant2.stop()); + TESTASSERT_EQ(alloc_result::success, + pdsch_sched.is_ue_grant_valid(ue_cc, ss_id, srsran_dci_format_nr_1_0, ue_grant2)); + alloc_res = pdsch_sched.alloc_ue_pdsch(ss_id, srsran_dci_format_nr_1_0, ue_grant2, ue_cc, pdcch->dci); + TESTASSERT(alloc_res.has_value()); + test_dci_freq_assignment(bwp_params, ue_grant2, *pdcch); + prb_bitmap used_prbs_ue2 = pdsch_sched.occupied_prbs(ss_id, srsran_dci_format_nr_1_0); + TESTASSERT_EQ(used_prbs_ue2.count(), used_prbs.count() + ue_grant2.length()); + TESTASSERT_EQ(alloc_result::sch_collision, + pdsch_sched.is_ue_grant_valid(ue_cc, ss_id, srsran_dci_format_nr_1_0, ue_grant2)); + + last_prb_bitmap.reset(); + last_prb_bitmap.fill(ue_grant2.start(), ue_grant2.stop()); + fmt::print("C-RNTI allocated in UE-dedicated common SearchSpace. Occupied PRBs:\n{:b} -> {:b}\n", + last_prb_bitmap, + used_prbs_ue2); + + TESTASSERT_EQ(3, pdschs.size()); + pdsch_sched.reset(); + TESTASSERT_EQ(0, pdschs.size()); + TESTASSERT_EQ(0, pdsch_sched.occupied_prbs(2, srsran_dci_format_nr_1_0).count()); +} + +void test_multi_pusch() +{ + srsran::test_delimit_logger delimiter{"Test Multiple PUSCH Allocations"}; + + // Create Cell and UE configs + sched_nr_impl::cell_cfg_t cell_cfg = get_cell_cfg(); + sched_nr_impl::ue_cfg_t uecfg = get_ue_cfg(cell_cfg); + sched_nr_interface::sched_args_t sched_args; + bwp_params_t bwp_params{cell_cfg, sched_args, 0, 0}; + ue_carrier_params_t ue_cc{0x4601, bwp_params, uecfg}; + ue_carrier_params_t ue_cc2{0x4602, bwp_params, uecfg}; + + pusch_list_t puschs; + pusch_alloc_result alloc_res; + + pusch_allocator pusch_sched(bwp_params, 0, puschs); + + pdcch_ul_t pdcch_ue1, pdcch_ue2; + pdcch_ue1.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, 1, srsran_rnti_type_c, 0x4601); + pdcch_ue2.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, 2, srsran_rnti_type_c, 0x4602); + + // Allocate UE in common SearchSpace + uint32_t ss_id = 1; + pdcch_ul_t* pdcch = &pdcch_ue1; + prb_bitmap used_prbs = pusch_sched.occupied_prbs(); + uint32_t ue_grant_size = 10; + prb_interval ue_grant = find_empty_interval_of_length(used_prbs, ue_grant_size); + TESTASSERT_EQ(alloc_result::success, pusch_sched.is_grant_valid(srsran_search_space_type_common_1, ue_grant)); + alloc_res = pusch_sched.alloc_pusch(pdcch->dci.ctx.ss_type, ue_grant, pdcch->dci); + TESTASSERT(alloc_res.has_value()); + prb_bitmap used_prbs_ue1 = pusch_sched.occupied_prbs(); + TESTASSERT_EQ(used_prbs_ue1.count(), used_prbs.count() + ue_grant.length()); + TESTASSERT_EQ(alloc_result::sch_collision, + pusch_sched.is_grant_valid(srsran_search_space_type_common_1, ue_grant, false)); + + prb_bitmap last_prb_bitmap(used_prbs.size()); + last_prb_bitmap.fill(ue_grant.start(), ue_grant.stop()); + fmt::print("C-RNTI allocated in Common SearchSpace. Occupied PRBs:\n{:b} -> {:b}\n", last_prb_bitmap, used_prbs_ue1); + + // Allocate UE in dedicated SearchSpace + ss_id = 2; + pdcch = &pdcch_ue2; + used_prbs = pusch_sched.occupied_prbs(); + prb_interval ue2_grant = find_empty_interval_of_length(used_prbs, used_prbs.size()); + TESTASSERT_EQ(alloc_result::success, pusch_sched.is_grant_valid(srsran_search_space_type_ue, ue2_grant)); + alloc_res = pusch_sched.alloc_pusch(pdcch->dci.ctx.ss_type, ue2_grant, pdcch->dci); + TESTASSERT(alloc_res.has_value()); + prb_bitmap used_prbs_ue2 = pusch_sched.occupied_prbs(); + TESTASSERT_EQ(used_prbs_ue2.count(), used_prbs.count() + ue2_grant.length()); + TESTASSERT_EQ(alloc_result::sch_collision, pusch_sched.is_grant_valid(srsran_search_space_type_ue, ue2_grant, false)); + + last_prb_bitmap.reset(); + last_prb_bitmap.fill(ue2_grant.start(), ue2_grant.stop()); + fmt::print("C-RNTI allocated in Common SearchSpace. Occupied PRBs:\n{:b} -> {:b}\n", last_prb_bitmap, used_prbs_ue2); +} + +} // namespace srsenb + +int main() +{ + auto& test_logger = srslog::fetch_basic_logger("TEST"); + test_logger.set_level(srslog::basic_levels::info); + auto& mac_nr_logger = srslog::fetch_basic_logger("MAC-NR"); + mac_nr_logger.set_level(srslog::basic_levels::debug); + auto& pool_logger = srslog::fetch_basic_logger("POOL"); + pool_logger.set_level(srslog::basic_levels::debug); + + // Start the log backend. + srslog::init(); + + srsenb::test_si(); + srsenb::test_rar(); + srsenb::test_ue_pdsch(); + srsenb::test_pdsch_fail(); + srsenb::test_multi_pdsch(); + srsenb::test_multi_pusch(); +} \ No newline at end of file diff --git a/srsgnb/src/stack/mac/test/sched_nr_sim_ue.cc b/srsgnb/src/stack/mac/test/sched_nr_sim_ue.cc index c0c9cf8a1..252fb34c1 100644 --- a/srsgnb/src/stack/mac/test/sched_nr_sim_ue.cc +++ b/srsgnb/src/stack/mac/test/sched_nr_sim_ue.cc @@ -87,7 +87,11 @@ void sched_nr_ue_sim::update_dl_harqs(const sched_nr_cc_result_view& cc_out) h.ndi = data.dci.ndi; h.first_slot_tx = cc_out.slot; h.dci_loc = data.dci.ctx.location; - h.tbs = 100; // TODO + for (const sched_nr_impl::pdsch_t& pdsch : cc_out.dl->phy.pdsch) { + if (pdsch.sch.grant.rnti == data.dci.ctx.rnti) { + h.tbs = pdsch.sch.grant.tb[0].tbs / 8u; + } + } } else { // it is retx h.nof_retxs++; @@ -238,6 +242,12 @@ void sched_nr_base_tester::user_cfg(uint16_t rnti, const sched_nr_interface::ue_ sched_ptr->ue_cfg(rnti, ue_cfg_); } +void sched_nr_base_tester::add_rlc_dl_bytes(uint16_t rnti, uint32_t lcid, uint32_t pdu_size_bytes) +{ + TESTASSERT(ue_db.count(rnti) > 0); + dl_buffer_state_diff(rnti, lcid, pdu_size_bytes); +} + void sched_nr_base_tester::run_slot(slot_point slot_tx) { srsran_assert(not stopped.load(std::memory_order_relaxed), "Running scheduler when it has already been stopped"); @@ -322,6 +332,83 @@ void sched_nr_base_tester::process_results() for (auto& u : ue_db) { u.second.update(cc_out); } + + // Update scheduler buffers + update_sched_buffer_state(cc_out); + } +} + +void sched_nr_base_tester::dl_buffer_state_diff(uint16_t rnti, uint32_t lcid, int newtx) +{ + auto& lch = gnb_ue_db[rnti].logical_channels[lcid]; + lch.rlc_unacked = std::max(0, (int)lch.rlc_unacked + newtx); + update_sched_buffer_state(rnti); + logger.debug("STATUS: rnti=0x%x, lcid=%d DL buffer state is (unacked=%d, newtx=%d)", + rnti, + lcid, + lch.rlc_unacked, + lch.rlc_newtx); +} + +void sched_nr_base_tester::dl_buffer_state_diff(uint16_t rnti, int diff_bs) +{ + if (diff_bs == 0) { + return; + } + if (diff_bs > 0) { + const auto& ue_bearers = ue_db.at(rnti).get_ctxt().ue_cfg.ue_bearers; + for (int i = ue_bearers.size() - 1; i >= 0; --i) { + if (ue_bearers[i].is_dl()) { + dl_buffer_state_diff(rnti, i, diff_bs); + return; + } + } + srsran_terminate("Updating UE RLC buffer state but no bearers are active"); + } + for (auto& lch : gnb_ue_db[rnti].logical_channels) { + if (not ue_db.at(rnti).get_ctxt().ue_cfg.ue_bearers[lch.first].is_dl()) { + continue; + } + int max_diff = -std::min((int)lch.second.rlc_unacked, -diff_bs); + if (max_diff != 0) { + dl_buffer_state_diff(rnti, lch.first, max_diff); + diff_bs -= max_diff; + } + } +} + +void sched_nr_base_tester::update_sched_buffer_state(uint16_t rnti) +{ + auto& u = ue_db.at(rnti); + for (auto& lch : gnb_ue_db[rnti].logical_channels) { + int newtx = lch.second.rlc_unacked; + for (auto& cc : u.get_ctxt().cc_list) { + if (newtx <= 0) { + break; + } + for (auto& dl_h : cc.dl_harqs) { + int tbs = dl_h.active ? dl_h.tbs : 0; + newtx = std::max(0, newtx - tbs); + } + } + if (newtx != (int)lch.second.rlc_newtx) { + sched_ptr->dl_buffer_state(rnti, lch.first, newtx, 0); + lch.second.rlc_newtx = newtx; + logger.debug("STATUS: rnti=0x%x, lcid=%d DL buffer state is (unacked=%d, newtx=%d)", + rnti, + lch.first, + lch.second.rlc_unacked, + lch.second.rlc_newtx); + } + } +} + +void sched_nr_base_tester::update_sched_buffer_state(const sched_nr_cc_result_view& cc_out) +{ + for (auto& dl : cc_out.dl->phy.pdcch_dl) { + if (dl.dci.ctx.rnti_type == srsran_rnti_type_c or dl.dci.ctx.rnti_type == srsran_rnti_type_tc) { + update_sched_buffer_state(dl.dci.ctx.rnti); + } } } @@ -368,8 +455,13 @@ int sched_nr_base_tester::apply_slot_events(sim_nr_ue_ctxt_t& ue_ctxt, const ue_ auto& h = ue_ctxt.cc_list[enb_cc_idx].dl_harqs[ack.pid]; if (ack.ack) { - logger.info( - "DL ACK rnti=0x%x slot_dl_tx=%u cc=%d pid=%d", ue_ctxt.rnti, h.last_slot_tx.to_uint(), enb_cc_idx, ack.pid); + logger.info("EVENT: DL ACK rnti=0x%x slot_dl_tx=%u cc=%d pid=%d, tbs=%d", + ue_ctxt.rnti, + h.last_slot_tx.to_uint(), + enb_cc_idx, + ack.pid, + h.tbs); + dl_buffer_state_diff(ue_ctxt.rnti, -(int)h.tbs); } // update scheduler @@ -394,7 +486,7 @@ int sched_nr_base_tester::apply_slot_events(sim_nr_ue_ctxt_t& ue_ctxt, const ue_ if (h.is_msg3) { logger.info("STATUS: rnti=0x%x received Msg3", ue_ctxt.rnti); - sched_ptr->dl_buffer_state(ue_ctxt.rnti, 0, 150, 0); // Schedule RRC setup + dl_buffer_state_diff(ue_ctxt.rnti, 0, 150); // Schedule RRC setup } } } diff --git a/srsgnb/src/stack/mac/test/sched_nr_sim_ue.h b/srsgnb/src/stack/mac/test/sched_nr_sim_ue.h index 4fb7da1ce..cea78d66f 100644 --- a/srsgnb/src/stack/mac/test/sched_nr_sim_ue.h +++ b/srsgnb/src/stack/mac/test/sched_nr_sim_ue.h @@ -138,6 +138,8 @@ public: void user_cfg(uint16_t rnti, const sched_nr_interface::ue_cfg_t& ue_cfg_); + void add_rlc_dl_bytes(uint16_t rnti, uint32_t lcid, uint32_t pdu_size_bytes); + srsran::const_span get_cell_params() { return cell_params; } // configurable by simulator concrete implementation @@ -150,6 +152,11 @@ protected: void generate_cc_result(uint32_t cc); sim_nr_enb_ctxt_t get_enb_ctxt() const; + void dl_buffer_state_diff(uint16_t rnti, uint32_t lcid, int newtx); + void dl_buffer_state_diff(uint16_t rnti, int newtx); + void update_sched_buffer_state(uint16_t rnti); + void update_sched_buffer_state(const sched_nr_cc_result_view& cc_out); + int set_default_slot_events(const sim_nr_ue_ctxt_t& ue_ctxt, ue_nr_slot_events& pending_events); int apply_slot_events(sim_nr_ue_ctxt_t& ue_ctxt, const ue_nr_slot_events& events); @@ -164,8 +171,19 @@ protected: std::vector > cc_workers; + // UE context from the UE's point-of-view std::map ue_db; + // gNB point-of-view of UE state + struct gnb_ue_ctxt { + struct channel_ctxt { + uint32_t rlc_unacked = 0; + uint32_t rlc_newtx = 0; + }; + std::map logical_channels; + }; + std::map gnb_ue_db; + // slot-specific slot_point current_slot_tx; std::chrono::steady_clock::time_point slot_start_tp; diff --git a/srsgnb/src/stack/mac/test/sched_nr_test.cc b/srsgnb/src/stack/mac/test/sched_nr_test.cc index 6b16923cd..6866beab3 100644 --- a/srsgnb/src/stack/mac/test/sched_nr_test.cc +++ b/srsgnb/src/stack/mac/test/sched_nr_test.cc @@ -26,6 +26,50 @@ namespace srsenb { +class sched_tester : public sched_nr_base_tester +{ +public: + using sched_nr_base_tester::sched_nr_base_tester; + + void process_slot_result(const sim_nr_enb_ctxt_t& enb_ctxt, srsran::const_span cc_out) override + { + for (auto& cc : cc_out) { + for (auto& pdsch : cc.res.dl->phy.pdsch) { + if (pdsch.sch.grant.rnti_type == srsran_rnti_type_c or pdsch.sch.grant.rnti_type == srsran_rnti_type_tc) { + ue_metrics[pdsch.sch.grant.rnti].nof_dl_txs++; + ue_metrics[pdsch.sch.grant.rnti].nof_dl_bytes += pdsch.sch.grant.tb[0].tbs / 8u; + } + } + for (auto& pusch : cc.res.ul->pusch) { + if (pusch.sch.grant.rnti_type == srsran_rnti_type_c or pusch.sch.grant.rnti_type == srsran_rnti_type_tc) { + ue_metrics[pusch.sch.grant.rnti].nof_ul_txs++; + ue_metrics[pusch.sch.grant.rnti].nof_ul_bytes += pusch.sch.grant.tb[0].tbs / 8u; + } + } + } + } + + void print_results() + { + srslog::flush(); + fmt::print("SCHED UE metrics:\n"); + for (auto& u : ue_metrics) { + fmt::print(" 0x{:x}: nof_txs=({}, {}), nof_bytes=({}, {})\n", + u.first, + u.second.nof_dl_txs, + u.second.nof_ul_txs, + u.second.nof_dl_bytes, + u.second.nof_ul_bytes); + } + } + + struct sched_ue_metrics { + uint32_t nof_dl_txs = 0, nof_ul_txs = 0; + uint64_t nof_dl_bytes = 0, nof_ul_bytes = 0; + }; + std::map ue_metrics; +}; + struct sched_event_t { uint32_t slot_count; std::function run; @@ -45,7 +89,13 @@ sched_event_t ue_cfg(uint32_t slot_count, uint16_t rnti, const sched_nr_ue_cfg_t return sched_event_t{slot_count, task}; } -void run_sched_nr_test() +sched_event_t add_rlc_dl_bytes(uint32_t slot_count, uint16_t rnti, uint32_t lcid, uint32_t pdu_size) +{ + auto task = [rnti, pdu_size, lcid](sched_nr_base_tester& tester) { tester.add_rlc_dl_bytes(rnti, lcid, pdu_size); }; + return sched_event_t{slot_count, task}; +} + +void test_sched_nr_no_data() { uint32_t max_nof_ttis = 1000, nof_sectors = 1; uint16_t rnti = 0x4601; @@ -54,8 +104,8 @@ void run_sched_nr_test() cfg.auto_refill_buffer = false; std::vector cells_cfg = get_default_cells_cfg(nof_sectors); - std::string test_name = "Serialized Test"; - sched_nr_base_tester tester(cfg, cells_cfg, test_name); + std::string test_name = "Test with no data"; + sched_tester tester(cfg, cells_cfg, test_name); /* Set events */ std::deque events; @@ -76,6 +126,55 @@ void run_sched_nr_test() // call sched tester.run_slot(slot_tx); } + + tester.print_results(); + + // Since DL buffers were not externally updated, we should only see Msg4 as DL tx + TESTASSERT_EQ(1, tester.ue_metrics[rnti].nof_dl_txs); + // Since UL buffers were not externally updated, we should only see Msg3 as UL tx + TESTASSERT_EQ(1, tester.ue_metrics[rnti].nof_ul_txs); +} + +void test_sched_nr_data() +{ + uint32_t max_nof_ttis = 1000, nof_sectors = 1; + uint16_t rnti = 0x4601; + uint32_t nof_dl_bytes_to_tx = 2e6; + + sched_nr_interface::sched_args_t cfg; + cfg.auto_refill_buffer = false; + std::vector cells_cfg = get_default_cells_cfg(nof_sectors); + + std::string test_name = "Test with data"; + sched_tester tester(cfg, cells_cfg, test_name); + + /* Set events */ + std::deque events; + events.push_back(add_user(9, rnti, 0)); + events.push_back(ue_cfg(20, rnti, get_default_ue_cfg(1))); + events.push_back(add_rlc_dl_bytes(50, rnti, 0, nof_dl_bytes_to_tx)); + + /* Run Test */ + for (uint32_t nof_slots = 0; nof_slots < max_nof_ttis; ++nof_slots) { + slot_point slot_rx(0, nof_slots % 10240); + slot_point slot_tx = slot_rx + TX_ENB_DELAY; + + // run events + while (not events.empty() and events.front().slot_count <= nof_slots) { + events.front().run(tester); + events.pop_front(); + } + + // call sched + tester.run_slot(slot_tx); + } + + tester.print_results(); + + TESTASSERT(tester.ue_metrics[rnti].nof_dl_txs > 1); + TESTASSERT(tester.ue_metrics[rnti].nof_dl_bytes >= nof_dl_bytes_to_tx); + // Since UL buffers were not externally updated, we should only see Msg3 as UL tx + TESTASSERT_EQ(1, tester.ue_metrics[rnti].nof_ul_txs); } } // namespace srsenb @@ -92,5 +191,6 @@ int main() // Start the log backend. srslog::init(); - srsenb::run_sched_nr_test(); + srsenb::test_sched_nr_no_data(); + srsenb::test_sched_nr_data(); } diff --git a/srsgnb/src/stack/mac/ue_nr.cc b/srsgnb/src/stack/mac/ue_nr.cc index fc6ca3e15..009be299c 100644 --- a/srsgnb/src/stack/mac/ue_nr.cc +++ b/srsgnb/src/stack/mac/ue_nr.cc @@ -58,7 +58,7 @@ void ue_nr::reset() nof_failures = 0; } -void ue_nr::ue_cfg(const sched_interface::ue_cfg_t& ue_cfg) +void ue_nr::ue_cfg(const sched_nr_interface::ue_cfg_t& ue_cfg) { // nop } diff --git a/srsgnb/src/stack/rrc/test/CMakeLists.txt b/srsgnb/src/stack/rrc/test/CMakeLists.txt index 41dbd63d9..b05348e7f 100644 --- a/srsgnb/src/stack/rrc/test/CMakeLists.txt +++ b/srsgnb/src/stack/rrc/test/CMakeLists.txt @@ -21,5 +21,9 @@ add_library(rrc_nr_test_helpers rrc_nr_test_helpers.cc) add_executable(rrc_nr_test rrc_nr_test.cc) -target_link_libraries(rrc_nr_test srsgnb_rrc srsgnb_rrc_config_utils srsran_common rrc_nr_asn1 rrc_asn1 rrc_nr_test_helpers ${ATOMIC_LIBS}) -add_test(rrc_nr_test rrc_nr_test) \ No newline at end of file +target_link_libraries(rrc_nr_test srsgnb_rrc srsgnb_rrc_config_utils srsran_common rrc_nr_asn1 rrc_nr_test_helpers ${ATOMIC_LIBS}) +add_test(rrc_nr_test rrc_nr_test) + +add_executable(rrc_nr_core_test rrc_nr_core_test.cc) +target_link_libraries(rrc_nr_core_test srsgnb_rrc srsgnb_rrc_config_utils srsran_common rrc_nr_asn1 test_helpers rrc_nr_test_helpers srsgnb_ngap ngap_nr_asn1 srsran_gtpu srsenb_upper ${SCTP_LIBRARIES} ${ATOMIC_LIBS} ${Boost_LIBRARIES}) + diff --git a/srsgnb/src/stack/rrc/test/rrc_nr_core_test.cc b/srsgnb/src/stack/rrc/test/rrc_nr_core_test.cc new file mode 100644 index 000000000..18c464a04 --- /dev/null +++ b/srsgnb/src/stack/rrc/test/rrc_nr_core_test.cc @@ -0,0 +1,224 @@ +/** + * + * \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 "rrc_nr_test_helpers.h" +#include "srsenb/hdr/enb.h" +#include "srsenb/hdr/stack/upper/gtpu.h" +#include "srsenb/test/rrc/test_helpers.h" +#include "srsgnb/hdr/stack/ngap/ngap.h" +#include "srsgnb/hdr/stack/rrc/rrc_nr_config_utils.h" +#include "srsgnb/src/stack/mac/test/sched_nr_cfg_generators.h" +#include "srsran/common/network_utils.h" +#include "srsran/common/test_common.h" +#include "srsran/interfaces/gnb_rrc_nr_interfaces.h" +#include "srsran/upper/gtpu.h" +#include +#include + +#include + +using namespace asn1::rrc_nr; +namespace bpo = boost::program_options; + +namespace srsenb { // namespace srsenb + +void parse_args(ngap_args_t* ngap_args, int argc, char* argv[]) +{ + // temporary helpers for conversion + std::string config_file; + std::string gnb_id{}; + std::string gnb_cell_id{}; + std::string gnb_tac{}; + std::string gnb_mcc{}; + std::string gnb_mnc{}; + + // Command line only options + bpo::options_description general("General options"); + + general.add_options()("help,h", "Produce help message"); + + // Command line or config file options + bpo::options_description common("Configuration options"); + + // clang-format off + common.add_options() + + ("gnb_id", bpo::value(&gnb_id)->default_value("0x0"), "gnb ID") + ("name", bpo::value(&ngap_args->gnb_name)->default_value("srsgnb01"), "gnb Name") + ("cell_id", bpo::value(&gnb_cell_id)->default_value("0x0"), "Cell ID") + ("tac", bpo::value(&gnb_tac)->default_value("0x0"), "Tracking Area Code") + ("mcc", bpo::value(&gnb_mcc)->default_value("001"), "Mobile Country Code") + ("mnc", bpo::value(&gnb_mnc)->default_value("01"), "Mobile Network Code") + ("amf_addr", bpo::value(&ngap_args->amf_addr)->default_value("127.0.0.1"), "IP address of AMF for NG connection") + ("n1c_bind_addr", bpo::value(&ngap_args->ngc_bind_addr)->default_value("192.168.3.1"), "Local IP address to bind for NGAP connection") + ("gtp_bind_addr", bpo::value(&ngap_args->gtp_bind_addr)->default_value("192.168.3.1"), "Local IP address to bind for GTP connection"); + + bpo::options_description position("Positional options"); + + // clang-format on + bpo::positional_options_description p{}; + p.add("config_file", -1); + + bpo::options_description cmdline_options; + cmdline_options.add(common).add(position).add(general); + + bpo::variables_map vm{}; + + try { + bpo::store(bpo::command_line_parser(argc, argv).options(cmdline_options).positional(p).run(), vm); + bpo::notify(vm); + } catch (bpo::error& e) { + std::cerr << e.what() << std::endl; + exit(1); + } + + if (vm.count("help")) { + std::cout << common << std::endl << general << std::endl; + exit(0); + } + + // Convert hex strings + { + std::stringstream sstr{}; + sstr << std::hex << vm["gnb_id"].as(); + sstr >> ngap_args->gnb_id; + } + { + std::stringstream sstr{}; + sstr << std::hex << vm["cell_id"].as(); + uint16_t tmp; // Need intermediate uint16_t as uint8_t is treated as char + sstr >> tmp; + ngap_args->cell_id = tmp; + } + { + std::stringstream sstr{}; + sstr << std::hex << vm["tac"].as(); + sstr >> ngap_args->tac; + } + + // Convert MCC/MNC strings + if (!srsran::string_to_mcc(gnb_mcc, &ngap_args->mcc)) { + std::cout << "Error parsing mcc:" << gnb_mcc << " - must be a 3-digit string." << std::endl; + } + if (!srsran::string_to_mnc(gnb_mnc, &ngap_args->mnc)) { + std::cout << "Error parsing mnc:" << gnb_mnc << " - must be a 2 or 3-digit string." << std::endl; + } +} + +void test_rrc_sa_ngap_integration(ngap_args_t ngap_args) +{ + // This takes the existing RRC-NR tests and exercises the NGAP integration with a real core network. + // The test currently runs down untill the RRCReconfiguration. + srsran::task_scheduler task_sched; + + phy_nr_dummy phy_obj; + mac_nr_dummy mac_obj; + rlc_nr_rrc_tester rlc_obj; + pdcp_nr_rrc_tester pdcp_obj; + rrc_nr rrc_obj(&task_sched); + enb_bearer_manager bearer_mapper; + + // NGAP Setup + auto& ngap_logger = srslog::fetch_basic_logger("NGAP"); + ngap_logger.set_level(srslog::basic_levels::debug); + ngap_logger.set_hex_dump_max_size(-1); + auto& gtpu_logger = srslog::fetch_basic_logger("GTPU"); + gtpu_logger.set_level(srslog::basic_levels::debug); + gtpu_logger.set_hex_dump_max_size(-1); + + srsran::socket_manager rx_sockets; + srsenb::ngap ngap_obj(&task_sched, ngap_logger, &rx_sockets); + srsenb::gtpu gtpu_obj(&task_sched, gtpu_logger, &rx_sockets); + + gtpu_args_t gtpu_args; + gtpu_args.embms_enable = false; + gtpu_args.mme_addr = ngap_args.amf_addr; + gtpu_args.gtp_bind_addr = ngap_args.gtp_bind_addr; + TESTASSERT(gtpu_obj.init(gtpu_args, &pdcp_obj) == SRSRAN_SUCCESS); + TESTASSERT(ngap_obj.init(ngap_args, &rrc_obj, >pu_obj) == SRSRAN_SUCCESS); + task_sched.run_next_task(); + + // set cfg + rrc_nr_cfg_t rrc_cfg_nr = rrc_nr_cfg_t{}; + rrc_cfg_nr.cell_list.emplace_back(); + generate_default_nr_cell(rrc_cfg_nr.cell_list[0]); + rrc_cfg_nr.cell_list[0].phy_cell.carrier.pci = 500; + rrc_cfg_nr.cell_list[0].dl_arfcn = 368500; + rrc_cfg_nr.cell_list[0].band = 3; + rrc_cfg_nr.cell_list[0].phy_cell.carrier.nof_prb = 52; + rrc_cfg_nr.cell_list[0].duplex_mode = SRSRAN_DUPLEX_MODE_FDD; + rrc_cfg_nr.is_standalone = true; + set_derived_nr_cell_params(rrc_cfg_nr.is_standalone, rrc_cfg_nr.cell_list[0]); + srsran_assert(check_rrc_nr_cfg_valid(rrc_cfg_nr) == SRSRAN_SUCCESS, "Invalid RRC NR configuration"); + + TESTASSERT(rrc_obj.init(rrc_cfg_nr, &phy_obj, &mac_obj, &rlc_obj, &pdcp_obj, &ngap_obj, bearer_mapper, nullptr) == + SRSRAN_SUCCESS); + + sched_nr_ue_cfg_t uecfg = get_default_ue_cfg(1); + uecfg.phy_cfg.pdcch = rrc_cfg_nr.cell_list[0].phy_cell.pdcch; + uecfg.phy_cfg.pdcch.search_space_present[2] = false; + TESTASSERT_SUCCESS(rrc_obj.add_user(0x4601, uecfg)); + + // RRCSetupComplete triggers NGAP Initial UE Message with NAS-PDU: Registration Request + ngap_rrc_tester ngap_dummy; + test_rrc_nr_connection_establishment(task_sched, rrc_obj, rlc_obj, mac_obj, ngap_dummy, 0x4601); + task_sched.run_next_task(); + + // ULInformationTransfer -> UplinkNASTransport(NAS Authentication Response) + srsran::unique_byte_buffer_t auth_resp_pdu; + auth_resp_pdu = srsran::make_byte_buffer(); + asn1::bit_ref bref_ar{auth_resp_pdu->data(), auth_resp_pdu->get_tailroom()}; + ul_dcch_msg_s ul_dcch_msg_auth_resp; + + ul_dcch_msg_auth_resp.msg.set_c1().set_ul_info_transfer().crit_exts.set_ul_info_transfer(); + ul_dcch_msg_auth_resp.msg.c1().ul_info_transfer().crit_exts.ul_info_transfer().ded_nas_msg_present = true; + ul_dcch_msg_auth_resp.msg.c1().ul_info_transfer().crit_exts.ul_info_transfer().ded_nas_msg.from_string( + "7e00572d10db165fffdb7b74c326e3fc3f154117fe"); + + TESTASSERT_SUCCESS(ul_dcch_msg_auth_resp.pack(bref_ar)); + auth_resp_pdu->N_bytes = bref_ar.distance_bytes(); + rrc_obj.write_pdu(0x4601, 1, std::move(auth_resp_pdu)); + task_sched.run_next_task(); + + // ULInformationTransfer -> UplinkNASTransport(NAS Security Mode Complete) + srsran::unique_byte_buffer_t sec_complete_pdu; + sec_complete_pdu = srsran::make_byte_buffer(); + asn1::bit_ref bref_smc{sec_complete_pdu->data(), sec_complete_pdu->get_tailroom()}; + ul_dcch_msg_s ul_dcch_msg_smc; + ul_dcch_msg_smc.msg.set_c1().set_ul_info_transfer().crit_exts.set_ul_info_transfer(); + ul_dcch_msg_smc.msg.c1().ul_info_transfer().crit_exts.ul_info_transfer().ded_nas_msg_present = true; + ul_dcch_msg_smc.msg.c1().ul_info_transfer().crit_exts.ul_info_transfer().ded_nas_msg.from_string( + "7e046b3737af017e005e7700093535940096783351f37100237e004179000d0100f11000000000103254760810030000002e02e0602f0201" + "01530100"); + TESTASSERT_SUCCESS(ul_dcch_msg_smc.pack(bref_smc)); + sec_complete_pdu->N_bytes = bref_smc.distance_bytes(); + rrc_obj.write_pdu(0x4601, 1, std::move(sec_complete_pdu)); + task_sched.run_next_task(); + + test_rrc_nr_security_mode_cmd(task_sched, rrc_obj, pdcp_obj, 0x4601); +} +} // namespace srsenb + +int main(int argc, char** argv) +{ + auto& logger = srslog::fetch_basic_logger("ASN1"); + logger.set_level(srslog::basic_levels::info); + auto& rrc_logger = srslog::fetch_basic_logger("RRC-NR"); + rrc_logger.set_level(srslog::basic_levels::debug); + + srslog::init(); + ngap_args_t ngap_args = {}; + srsenb::parse_args(&ngap_args, argc, argv); + srsenb::test_rrc_sa_ngap_integration(ngap_args); + + return SRSRAN_SUCCESS; +} \ No newline at end of file diff --git a/srsue/hdr/phy/dummy_phy.h b/srsue/hdr/phy/dummy_phy.h new file mode 100644 index 000000000..6073abaed --- /dev/null +++ b/srsue/hdr/phy/dummy_phy.h @@ -0,0 +1,76 @@ +/** + * + * \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 SRSRAN_DUMMY_PHY_H +#define SRSRAN_DUMMY_PHY_H + +#include "srsran/interfaces/phy_interface_types.h" +#include "srsue/hdr/phy/ue_phy_base.h" + +namespace srsue { + +class dummy_phy final : public ue_phy_base, public phy_interface_stack_lte +{ +public: + // ue_phy_base + std::string get_type() final { return "dummy_phy"; } + void stop() final {} + void wait_initialize() final {} + bool is_initialized() final { return false; } + void start_plot() final {} + void get_metrics(const srsran::srsran_rat_t& rat, phy_metrics_t* m) final {} + + // phy_interface_stack_lte + void prach_send(uint32_t preamble_idx, int allowed_subframe, float target_power_dbm, float ta_base_sec = 0.0f) final + {} + prach_info_t prach_get_info() final { return {}; } + + /* Indicates the transmission of a SR signal in the next opportunity */ + void sr_send() final {} + int sr_last_tx_tti() final { return 0; } + + void set_mch_period_stop(uint32_t stop) final {} + bool set_config(const srsran::phy_cfg_t& config, uint32_t cc_idx = 0) final { return false; } + bool set_scell(srsran_cell_t cell_info, uint32_t cc_idx, uint32_t earfcn) final { return false; } + void set_config_tdd(srsran_tdd_config_t& tdd_config) final {} + void set_config_mbsfn_sib2(srsran::mbsfn_sf_cfg_t* cfg_list, uint32_t nof_cfgs) final {} + void set_config_mbsfn_sib13(const srsran::sib13_t& sib13) final {} + void set_config_mbsfn_mcch(const srsran::mcch_msg_t& mcch) final {} + + void deactivate_scells() final {} + + /* Measurements interface */ + void set_cells_to_meas(uint32_t earfcn, const std::set& pci) final {} + void meas_stop() final {} + + /* Cell search and selection procedures */ + bool cell_search() final { return false; } + bool cell_select(phy_cell_t cell) final { return false; } + bool cell_is_camping() final { return false; } + + /* Time advance commands */ + void set_timeadv_rar(uint32_t tti, uint32_t ta_cmd) final {} + void set_timeadv(uint32_t tti, uint32_t ta_cmd) final {} + + /* Activate / Disactivate SCell*/ + void set_activation_deactivation_scell(uint32_t cmd, uint32_t tti) final {} + + /* Sets RAR dci payload */ + void set_rar_grant(uint8_t grant_payload[SRSRAN_RAR_GRANT_LEN], uint16_t rnti) final {} + + uint32_t get_current_tti() final { return 0; } + + float get_phr() final { return 0.0; } + float get_pathloss_db() final { return 0.0; } +}; +} // namespace srsue +#endif // SRSRAN_DUMMY_PHY_H diff --git a/srsue/hdr/phy/nr/cell_search.h b/srsue/hdr/phy/nr/cell_search.h new file mode 100644 index 000000000..7333cd7a9 --- /dev/null +++ b/srsue/hdr/phy/nr/cell_search.h @@ -0,0 +1,61 @@ +/** + * + * \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_CELL_SEARCH_H +#define SRSUE_CELL_SEARCH_H + +#include "srsran/interfaces/radio_interfaces.h" +#include "srsran/interfaces/ue_nr_interfaces.h" +#include "srsran/srsran.h" + +namespace srsue { +namespace nr { +class cell_search +{ +public: + struct args_t { + double max_srate_hz; + srsran_subcarrier_spacing_t ssb_min_scs = srsran_subcarrier_spacing_15kHz; + }; + + struct cfg_t { + double srate_hz; + double center_freq_hz; + double ssb_freq_hz; + srsran_subcarrier_spacing_t ssb_scs; + srsran_ssb_patern_t ssb_pattern; + srsran_duplex_mode_t duplex_mode; + }; + + struct ret_t { + enum { CELL_FOUND = 1, CELL_NOT_FOUND = 0, ERROR = -1 } result; + srsran_ssb_search_res_t ssb_res; + }; + + cell_search(srslog::basic_logger& logger); + ~cell_search(); + + bool init(const args_t& args); + + bool start(const cfg_t& cfg); + ret_t run_slot(const cf_t* buffer, uint32_t slot_sz); + + void reset(); + +private: + srslog::basic_logger& logger; + srsran_ssb_t ssb = {}; +}; +} // namespace nr +} // namespace srsue + +#endif // SRSUE_CELL_SEARCH_H diff --git a/srsue/hdr/phy/nr/slot_sync.h b/srsue/hdr/phy/nr/slot_sync.h new file mode 100644 index 000000000..f4b8aa5a5 --- /dev/null +++ b/srsue/hdr/phy/nr/slot_sync.h @@ -0,0 +1,62 @@ +/** + * + * \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_SLOT_SYNC_H +#define SRSUE_SLOT_SYNC_H + +#include "srsran/interfaces/radio_interfaces.h" +#include "srsran/interfaces/ue_nr_interfaces.h" +#include "srsran/radio/rf_buffer.h" +#include "srsran/radio/rf_timestamp.h" +#include "srsran/srsran.h" + +namespace srsue { +namespace nr { +class slot_sync +{ +public: + struct args_t { + double max_srate_hz = 1.92e6; + uint32_t nof_rx_channels = 1; + srsran_subcarrier_spacing_t ssb_min_scs = srsran_subcarrier_spacing_15kHz; + bool disable_cfo = false; + float pbch_dmrs_thr = 0.0f; ///< PBCH DMRS correlation detection threshold (0 means auto) + float cfo_alpha = 0.0f; ///< CFO averaging alpha (0 means auto) + int thread_priority = -1; + }; + + slot_sync(srslog::basic_logger& logger); + ~slot_sync(); + + bool init(const args_t& args, stack_interface_phy_nr* stack_, srsran::radio_interface_phy* radio_); + + int recv_callback(srsran::rf_buffer_t& rf_buffer, srsran_timestamp_t* timestamp); + void run_stack_tti(); + +private: + const static int MIN_TTI_JUMP = 1; ///< Time gap reported to stack after receiving subframe + const static int MAX_TTI_JUMP = 1000; ///< Maximum time gap tolerance in RF stream metadata + enum { SEARCHING = 0, CAMPING } state = SEARCHING; + srslog::basic_logger& logger; + stack_interface_phy_nr* stack = nullptr; + srsran::radio_interface_phy* radio = nullptr; + srsran::rf_timestamp_t last_rx_time; + srsran_ue_sync_nr_t ue_sync_nr = {}; + srsran_timestamp_t stack_tti_ts_new = {}; + srsran_timestamp_t stack_tti_ts = {}; + bool forced_rx_time_init = true; // Rx time sync after first receive from radio + uint32_t tti = 0; +}; +} // namespace nr +} // namespace srsue + +#endif // SRSUE_SLOT_SYNC_H diff --git a/srsue/hdr/phy/nr/sync_sa.h b/srsue/hdr/phy/nr/sync_sa.h new file mode 100644 index 000000000..1ac04b379 --- /dev/null +++ b/srsue/hdr/phy/nr/sync_sa.h @@ -0,0 +1,122 @@ +/** + * + * \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_SYNC_NR_SA_H +#define SRSUE_SYNC_NR_SA_H + +#include "cell_search.h" +#include "slot_sync.h" +#include "srsran/common/threads.h" +#include "srsran/interfaces/radio_interfaces.h" +#include "srsran/interfaces/ue_nr_interfaces.h" +#include "srsran/radio/rf_buffer.h" +#include "srsran/radio/rf_timestamp.h" +#include "srsran/srslog/logger.h" +#include "srsran/srsran.h" +#include "srsue/hdr/phy/sync_state.h" +#include "worker_pool.h" +#include +#include +#include + +namespace srsue { +namespace nr { + +/** + * @brief NR Standalone synchronization class + */ +class sync_sa : public srsran::thread, public srsran::phy_common_interface +{ +public: + struct args_t { + double srate_hz = 61.44e6; + srsran_subcarrier_spacing_t ssb_min_scs = srsran_subcarrier_spacing_15kHz; + uint32_t nof_rx_channels = 1; + bool disable_cfo = false; + float pbch_dmrs_thr = 0.0f; ///< PBCH DMRS correlation detection threshold (0 means auto) + float cfo_alpha = 0.0f; ///< CFO averaging alpha (0 means auto) + int thread_priority = 1; + + cell_search::args_t get_cell_search() const + { + cell_search::args_t ret = {}; + ret.max_srate_hz = srate_hz; + return ret; + } + + slot_sync::args_t get_slot_sync() const + { + slot_sync::args_t ret = {}; + ret.max_srate_hz = srate_hz; + ret.nof_rx_channels = nof_rx_channels; + ret.ssb_min_scs = ssb_min_scs; + + return ret; + } + }; + + sync_sa(srslog::basic_logger& logger, worker_pool& workers_); + ~sync_sa(); + + bool init(const args_t& args_, stack_interface_phy_nr* stack_, srsran::radio_interface_phy* radio_); + bool reset(); + void stop(); + sync_state::state_t get_state(); + + // The following methods control the SYNC state machine + void cell_go_idle(); + cell_search::ret_t cell_search_run(const cell_search::cfg_t& cfg); + bool cell_select_run(const phy_interface_rrc_nr::cell_select_args_t& req); + + void worker_end(const worker_context_t& w_ctx, const bool& tx_enable, srsran::rf_buffer_t& buffer) override; + +private: + stack_interface_phy_nr* stack = nullptr; ///< Stand-Alone RRC interface + srsran::radio_interface_phy* radio = nullptr; ///< Radio object + srslog::basic_logger& logger; ///< General PHY logger + worker_pool& workers; + + // FSM that manages RRC commands for cell search/select/sync procedures + std::mutex rrc_mutex; + enum { PROC_IDLE = 0, PROC_SELECT_RUNNING, PROC_SEARCH_RUNNING } rrc_proc_state = PROC_IDLE; + sync_state phy_state; + + std::atomic running = {false}; + cf_t* rx_buffer = nullptr; + uint32_t slot_sz = 0; ///< Subframe size (1-ms) + uint32_t tti = 0; + srsran::tti_semaphore tti_semaphore; + srsran::rf_timestamp_t last_rx_time; + bool is_pending_tx_end = false; + uint32_t cell_search_nof_trials = 0; + const static uint32_t cell_search_max_trials = 100; + + cell_search::ret_t cs_ret; + cell_search searcher; + slot_sync slot_synchronizer; + + // FSM States + bool wait_idle(); + void run_state_idle(); + void run_state_cell_search(); + void run_state_cell_select(); + void run_state_cell_camping(); + + int radio_recv_fnc(srsran::rf_buffer_t& data, srsran_timestamp_t* rx_time); + void run_stack_tti(); + void run_thread() override; +}; + +} // namespace nr +} // namespace srsue + +#endif // SRSUE_SYNC_NR_SA_H diff --git a/srsue/hdr/phy/nr/worker_pool.h b/srsue/hdr/phy/nr/worker_pool.h index 5c806f75b..cb69e84cf 100644 --- a/srsue/hdr/phy/nr/worker_pool.h +++ b/srsue/hdr/phy/nr/worker_pool.h @@ -29,7 +29,7 @@ namespace srsue { namespace nr { -class worker_pool : public srsue::phy_interface_stack_nr +class worker_pool { private: srslog::basic_logger& logger; @@ -49,7 +49,7 @@ private: public: sf_worker* operator[](std::size_t pos) { return workers.at(pos).get(); } - worker_pool(uint32_t max_workers); + worker_pool(srslog::basic_logger& logger_, uint32_t max_workers); bool init(const phy_args_nr_t& args_, srsran::phy_common_interface& common, stack_interface_phy_nr* stack_, int prio); sf_worker* wait_worker(uint32_t tti); void start_worker(sf_worker* w); @@ -57,16 +57,15 @@ public: 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; - int set_ul_grant(uint32_t rx_tti, - std::array array, - uint16_t rnti, - srsran_rnti_type_t rnti_type) override; - bool set_config(const srsran::phy_cfg_nr_t& cfg) override; - bool has_valid_sr_resource(uint32_t sr_id) override; - void clear_pending_grants() override; + const float ta_base_sec = 0.0f); + int set_rar_grant(uint32_t rx_tti, + std::array array, + uint16_t rnti, + srsran_rnti_type_t rnti_type); + bool set_config(const srsran::phy_cfg_nr_t& cfg); + bool has_valid_sr_resource(uint32_t sr_id); + void clear_pending_grants(); void get_metrics(phy_metrics_t& m); - int tx_request(const tx_request_t& request) override; /** * @brief Sets external CFO to compensate UL signal frequency offset diff --git a/srsue/hdr/phy/phy.h b/srsue/hdr/phy/phy.h index 61fc89ee3..dc78705ad 100644 --- a/srsue/hdr/phy/phy.h +++ b/srsue/hdr/phy/phy.h @@ -34,53 +34,25 @@ #include "srsran/srsran.h" #include "srsue/hdr/phy/lte/worker_pool.h" #include "srsue/hdr/phy/nr/worker_pool.h" -#include "srsue/hdr/phy/ue_lte_phy_base.h" -#include "srsue/hdr/phy/ue_nr_phy_base.h" +#include "srsue/hdr/phy/ue_phy_base.h" #include "sync.h" namespace srsue { typedef _Complex float cf_t; -class phy_cmd_proc : public srsran::thread -{ -public: - phy_cmd_proc() : thread("PHY_CMD") { start(); } - - ~phy_cmd_proc() { stop(); } - - void add_cmd(std::function cmd) { cmd_queue.push(cmd); } - - void stop() - { - if (running) { - add_cmd([this]() { running = false; }); - wait_thread_finish(); - } - } - -private: - void run_thread() - { - std::function cmd; - while (running) { - cmd = cmd_queue.wait_pop(); - cmd(); - } - } - bool running = true; - // Queue for commands - srsran::block_queue > cmd_queue; -}; - -class phy final : public ue_lte_phy_base, public ue_nr_phy_base, public srsran::thread +class phy final : public ue_phy_base, + public phy_interface_stack_lte, + public phy_interface_stack_nr, + public srsran::phy_interface_radio, + public srsran::thread { public: explicit phy() : logger_phy(srslog::fetch_basic_logger("PHY")), logger_phy_lib(srslog::fetch_basic_logger("PHY_LIB")), lte_workers(MAX_WORKERS), - nr_workers(MAX_WORKERS), + nr_workers(logger_phy, MAX_WORKERS), common(logger_phy), sfsync(logger_phy, logger_phy_lib), prach_buffer(logger_phy), @@ -89,16 +61,13 @@ public: ~phy() final { stop(); } - // Init defined in base class - int init(const phy_args_t& args_) final; - // Init for LTE PHYs - int init(const phy_args_t& args_, stack_interface_phy_lte* stack_, srsran::radio_interface_phy* radio_) final; + int init(const phy_args_t& args_, stack_interface_phy_lte* stack_, srsran::radio_interface_phy* radio_); void stop() final; void wait_initialize() final; - bool is_initiated(); + bool is_initialized() final; void get_metrics(const srsran::srsran_rat_t& rat, phy_metrics_t* m) final; void srsran_phy_logger(phy_logger_level_t log_level, char* str); @@ -172,20 +141,24 @@ public: std::string get_type() final { return "lte_soft"; } - int init(const phy_args_nr_t& args_, stack_interface_phy_nr* stack_, srsran::radio_interface_phy* radio_) final; + /********** NR INTERFACE ********************/ + int init(const phy_args_nr_t& args_, stack_interface_phy_nr* stack_, srsran::radio_interface_phy* radio_); bool set_config(const srsran::phy_cfg_nr_t& cfg) final; - int set_ul_grant(uint32_t rx_tti, - std::array packed_ul_grant, - uint16_t rnti, - srsran_rnti_type_t rnti_type) final; 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) final; - int tx_request(const tx_request_t& request) final; - void set_earfcn(std::vector earfcns) final; + void set_earfcn(std::vector earfcns); bool has_valid_sr_resource(uint32_t sr_id) final; void clear_pending_grants() final; + int set_rar_grant(uint32_t rar_slot_idx, + std::array packed_ul_grant, + uint16_t rnti, + srsran_rnti_type_t rnti_type) final; + phy_nr_state_t get_state() override { return PHY_NR_STATE_IDLE; }; + void reset_nr() override{}; + bool start_cell_search(const cell_search_args_t& req) override { return false; }; + bool start_cell_select(const cell_select_args_t& req) override { return false; }; private: void run_thread() final; diff --git a/srsue/hdr/phy/phy_common.h b/srsue/hdr/phy/phy_common.h index b64f29666..4d83ce738 100644 --- a/srsue/hdr/phy/phy_common.h +++ b/srsue/hdr/phy/phy_common.h @@ -24,7 +24,9 @@ #include "phy_metrics.h" #include "srsran/adt/circular_array.h" +#include "srsran/common/block_queue.h" #include "srsran/common/gen_mch_tables.h" +#include "srsran/common/threads.h" #include "srsran/common/tti_sempahore.h" #include "srsran/interfaces/phy_common_interface.h" #include "srsran/interfaces/phy_interface_types.h" @@ -55,6 +57,37 @@ public: virtual void set_cfo(float cfo) = 0; }; +class phy_cmd_proc : public srsran::thread +{ +public: + phy_cmd_proc() : thread("PHY_CMD") { start(); } + + ~phy_cmd_proc() { stop(); } + + void add_cmd(std::function cmd) { cmd_queue.push(cmd); } + + void stop() + { + if (running) { + add_cmd([this]() { running = false; }); + wait_thread_finish(); + } + } + +private: + void run_thread() + { + std::function cmd; + while (running) { + cmd = cmd_queue.wait_pop(); + cmd(); + } + } + bool running = true; + // Queue for commands + srsran::block_queue > cmd_queue; +}; + /* Subclass that manages variables common to all workers */ class phy_common : public srsran::phy_common_interface { diff --git a/srsue/hdr/phy/phy_nr_sa.h b/srsue/hdr/phy/phy_nr_sa.h new file mode 100644 index 000000000..6f5532968 --- /dev/null +++ b/srsue/hdr/phy/phy_nr_sa.h @@ -0,0 +1,99 @@ +/** + * + * \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_PHY_NR_SA_H +#define SRSUE_PHY_NR_SA_H + +#include "phy_common.h" +#include "srsran/interfaces/ue_nr_interfaces.h" +#include "srsue/hdr/phy/nr/sync_sa.h" +#include "srsue/hdr/phy/ue_phy_base.h" + +namespace srsue { + +/** + * @brief NR Standalone PHY + */ +class phy_nr_sa final : public ue_phy_base, public phy_interface_stack_nr, public srsran::phy_interface_radio +{ +public: + phy_nr_sa(const char* logname); + ~phy_nr_sa() final { stop(); } + + int init(const phy_args_nr_t& args_, stack_interface_phy_nr* stack_, srsran::radio_interface_phy* radio_); + void wait_initialize() final; + bool is_initialized() final; + void stop() final; + void reset_nr() final; + + void start_plot() final {} + + void radio_overflow() final {} + void radio_failure() final {} + + std::string get_type() final { return "nr_soft"; } + + int set_rar_grant(uint32_t rar_slot_idx, + std::array packed_ul_grant, + uint16_t rnti, + srsran_rnti_type_t rnti_type) final; + void send_prach(const uint32_t prach_occasion, + const int preamble_index, + const float preamble_received_target_power, + const float ta_base_sec) final; + void set_earfcn(std::vector earfcns); + bool has_valid_sr_resource(uint32_t sr_id) final; + void clear_pending_grants() final; + bool set_config(const srsran::phy_cfg_nr_t& cfg) final; + + phy_nr_state_t get_state() final; + bool start_cell_search(const cell_search_args_t& req) final; + bool start_cell_select(const cell_select_args_t& req) final; + + void get_metrics(const srsran::srsran_rat_t& rat, phy_metrics_t* m) final{}; + void srsran_phy_logger(phy_logger_level_t log_level, char* str); + +private: + srslog::basic_logger& logger; + srslog::basic_logger& logger_phy_lib; + + nr::worker_pool workers; + nr::sync_sa sync; + + srsran::phy_cfg_nr_t config_nr = {}; + phy_args_nr_t args = {}; + srsran_carrier_nr_t selected_cell = {}; + + srsran::radio_interface_phy* radio = nullptr; + stack_interface_phy_nr* stack = nullptr; + + std::atomic is_configured = {false}; + + // Since cell_search/cell_select operations take a lot of time, we use another queue to process the other commands + // in parallel and avoid accumulating in the queue + phy_cmd_proc cmd_worker_cell, cmd_worker; + + // Run initialization functions in the background + void init_background(); + std::thread init_thread; + + const static int SF_RECV_THREAD_PRIO = 0; + const static int WORKERS_THREAD_PRIO = 2; + const static int MAX_WORKERS = 4; + const static int DEFAULT_WORKERS = 4; + + static void set_default_args(phy_args_nr_t& args); + bool check_args(const phy_args_nr_t& args); +}; + +} // namespace srsue +#endif // SRSUE_PHY_NR_SA_H diff --git a/srsue/hdr/phy/sync_state.h b/srsue/hdr/phy/sync_state.h index 9f3dd042a..8102f847b 100644 --- a/srsue/hdr/phy/sync_state.h +++ b/srsue/hdr/phy/sync_state.h @@ -22,6 +22,9 @@ #ifndef SRSUE_SYNC_STATE_H #define SRSUE_SYNC_STATE_H +#include +#include + namespace srsue { class sync_state @@ -67,6 +70,12 @@ public: next_state = SFN_SYNC; } + state_t get_state() + { + std::lock_guard lock(mutex); + return cur_state; + } + /* Functions to be called from outside the STM thread to instruct the STM to switch state. * The functions change the state and wait until it has changed it. * diff --git a/srsue/hdr/phy/ue_lte_phy_base.h b/srsue/hdr/phy/ue_lte_phy_base.h deleted file mode 100644 index 2a3134676..000000000 --- a/srsue/hdr/phy/ue_lte_phy_base.h +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -/****************************************************************************** - * File: ue_lte_phy_base.h - * Description: Base class for UE LTE PHYs. - *****************************************************************************/ - -#ifndef SRSUE_UE_LTE_PHY_BASE_H -#define SRSUE_UE_LTE_PHY_BASE_H - -#include "srsran/interfaces/radio_interfaces.h" -#include "srsue/hdr/phy/ue_phy_base.h" - -namespace srsue { - -class stack_interface_phy_lte; - -class ue_lte_phy_base : public ue_phy_base, public phy_interface_stack_lte, public srsran::phy_interface_radio -{ -public: - ue_lte_phy_base(){}; - virtual ~ue_lte_phy_base(){}; - - virtual std::string get_type() = 0; - - virtual int init(const phy_args_t& args_) = 0; - virtual int init(const phy_args_t& args_, stack_interface_phy_lte* stack_, srsran::radio_interface_phy* radio_) = 0; - virtual void stop() = 0; - - virtual void wait_initialize() = 0; - virtual void start_plot() = 0; - - virtual void get_metrics(const srsran::srsran_rat_t& rat, phy_metrics_t* m) = 0; -}; - -} // namespace srsue - -#endif // SRSUE_UE_LTE_PHY_BASE_H diff --git a/srsue/hdr/phy/ue_nr_phy_base.h b/srsue/hdr/phy/ue_nr_phy_base.h deleted file mode 100644 index 04bde236f..000000000 --- a/srsue/hdr/phy/ue_nr_phy_base.h +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -/****************************************************************************** - * File: ue_nr_phy_base.h - * Description: Base class for UE NR PHYs. - *****************************************************************************/ - -#ifndef SRSUE_UE_NR_PHY_BASE_H -#define SRSUE_UE_NR_PHY_BASE_H - -#include "srsran/interfaces/radio_interfaces.h" -#include "srsran/interfaces/ue_nr_interfaces.h" -#include "srsue/hdr/phy/ue_phy_base.h" - -namespace srsue { - -class ue_nr_phy_base : public phy_interface_stack_nr -{ -public: - ue_nr_phy_base(){}; - virtual ~ue_nr_phy_base() {} - - virtual std::string get_type() = 0; - - virtual int init(const phy_args_t& args_) = 0; - virtual int init(const phy_args_nr_t& args_, stack_interface_phy_nr* stack_, srsran::radio_interface_phy* radio_) = 0; - virtual void stop() = 0; - - virtual void set_earfcn(std::vector earfcns) = 0; - - virtual void get_metrics(const srsran::srsran_rat_t& rat, phy_metrics_t* m) = 0; -}; - -} // namespace srsue - -#endif // SRSUE_UE_NR_PHY_BASE_H \ No newline at end of file diff --git a/srsue/hdr/phy/ue_phy_base.h b/srsue/hdr/phy/ue_phy_base.h index 23f6fb605..2c33d78dd 100644 --- a/srsue/hdr/phy/ue_phy_base.h +++ b/srsue/hdr/phy/ue_phy_base.h @@ -40,11 +40,10 @@ public: virtual std::string get_type() = 0; - virtual int init(const phy_args_t& args_) = 0; - virtual void stop() = 0; virtual void wait_initialize() = 0; + virtual bool is_initialized() = 0; virtual void start_plot() = 0; virtual void get_metrics(const srsran::srsran_rat_t& rat, phy_metrics_t* m) = 0; diff --git a/srsue/hdr/stack/mac_nr/demux_nr.h b/srsue/hdr/stack/mac_nr/demux_nr.h index f77ca1d8a..31fad3ced 100644 --- a/srsue/hdr/stack/mac_nr/demux_nr.h +++ b/srsue/hdr/stack/mac_nr/demux_nr.h @@ -49,19 +49,24 @@ public: void process_pdus(); /// Called by MAC to process received PDUs // HARQ interface - void push_pdu(srsran::unique_byte_buffer_t pdu, uint32_t tti); + void push_pdu(srsran::unique_byte_buffer_t pdu, uint32_t tti); + void push_pdu_temp_crnti(srsran::unique_byte_buffer_t pdu, uint32_t tti); + uint64_t get_received_crueid(); private: // internal helpers - void handle_pdu(srsran::unique_byte_buffer_t pdu); + void handle_pdu(srsran::mac_sch_pdu_nr& pdu_buffer, srsran::unique_byte_buffer_t pdu); srslog::basic_logger& logger; rlc_interface_mac* rlc = nullptr; + uint64_t received_crueid = 0; + ///< currently only DCH PDUs supported (add BCH, PCH, etc) srsran::block_queue pdu_queue; srsran::mac_sch_pdu_nr rx_pdu; + srsran::mac_sch_pdu_nr rx_pdu_tcrnti; }; } // namespace srsue diff --git a/srsue/hdr/stack/mac_nr/mac_nr.h b/srsue/hdr/stack/mac_nr/mac_nr.h index f4ebee4e6..a2826e394 100644 --- a/srsue/hdr/stack/mac_nr/mac_nr.h +++ b/srsue/hdr/stack/mac_nr/mac_nr.h @@ -87,7 +87,7 @@ public: 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_config(const srsran::rach_cfg_nr_t& rach_cfg_nr); int set_config(const srsran::dl_harq_cfg_nr_t& dl_hrq_cfg); void set_contention_id(const uint64_t ue_identity); bool set_crnti(const uint16_t crnti); @@ -97,7 +97,7 @@ public: void start_ra_procedure(); /// Interface for internal procedures (RA, MUX, HARQ) - uint64_t get_contention_id(); + bool received_contention_id(uint64_t rx_contention_id); uint16_t get_crnti(); uint16_t get_temp_crnti(); uint16_t get_csrnti() { return SRSRAN_INVALID_RNTI; }; // SPS not supported @@ -159,6 +159,7 @@ private: std::atomic started = {false}; ue_rnti rntis; // thread-safe helper to store RNTIs, contention ID, etc + bool contention_res_successful; std::array metrics = {}; diff --git a/srsue/hdr/stack/mac_nr/mac_nr_interfaces.h b/srsue/hdr/stack/mac_nr/mac_nr_interfaces.h index 105dc06f6..54056b667 100644 --- a/srsue/hdr/stack/mac_nr/mac_nr_interfaces.h +++ b/srsue/hdr/stack/mac_nr/mac_nr_interfaces.h @@ -33,7 +33,6 @@ 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_crnti() = 0; virtual bool set_crnti(uint16_t c_rnti) = 0; @@ -89,6 +88,9 @@ public: // MAC also provides Temp C-RNTI (through RA proc) virtual uint16_t get_temp_crnti() = 0; + // HARQ can query MAC for current C-RNTI + virtual bool received_contention_id(uint64_t rx_contention_id) = 0; + // MAC provides the Currently Scheduled RNTI (for SPS) virtual uint16_t get_csrnti() = 0; }; @@ -100,7 +102,9 @@ class demux_interface_harq_nr { public: /// Inform demux unit about a newly decoded TB. - virtual void push_pdu(srsran::unique_byte_buffer_t pdu, uint32_t tti) = 0; + virtual void push_pdu(srsran::unique_byte_buffer_t pdu, uint32_t tti) = 0; + virtual void push_pdu_temp_crnti(srsran::unique_byte_buffer_t pdu, uint32_t tti) = 0; + virtual uint64_t get_received_crueid() = 0; }; } // namespace srsue diff --git a/srsue/hdr/stack/mac_nr/proc_ra_nr.h b/srsue/hdr/stack/mac_nr/proc_ra_nr.h index fbc4f9f50..855db5780 100644 --- a/srsue/hdr/stack/mac_nr/proc_ra_nr.h +++ b/srsue/hdr/stack/mac_nr/proc_ra_nr.h @@ -41,7 +41,7 @@ public: ~proc_ra_nr(){}; 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); + void set_config(const srsran::rach_cfg_nr_t& rach_cfg_nr); bool is_contention_resolution(); bool is_rar_opportunity(uint32_t tti); @@ -49,6 +49,7 @@ public: uint16_t get_rar_rnti(); bool has_temp_crnti(); uint16_t get_temp_crnti(); + void received_contention_resolution(bool is_successful); // PHY interfaces void prach_sent(uint32_t tti, uint32_t s_id, uint32_t t_id, uint32_t f_id, uint32_t ul_carrier_id); @@ -73,9 +74,10 @@ private: int ra_window_length = -1, ra_window_start = -1; uint16_t rar_rnti = SRSRAN_INVALID_RNTI; uint16_t temp_crnti = SRSRAN_INVALID_RNTI; + uint16_t transmitted_crnti = SRSRAN_INVALID_RNTI; std::mutex mutex; - srsran::rach_nr_cfg_t rach_cfg = {}; + srsran::rach_cfg_nr_t rach_cfg = {}; bool configured = false; enum ra_state_t { @@ -119,8 +121,7 @@ private: void ra_resource_selection(); void ra_preamble_transmission(); void ra_response_reception(const mac_interface_phy_nr::tb_action_dl_result_t& tb); - void ra_contention_resolution(); - void ra_contention_resolution(uint64_t rx_contention_id); + void ra_contention_resolution(bool received_con_res_matches_ue_id); void ra_completion(); void ra_error(); }; diff --git a/srsue/hdr/stack/rrc_nr/rrc_nr.h b/srsue/hdr/stack/rrc_nr/rrc_nr.h index ced4cc1e8..d2f5858e2 100644 --- a/srsue/hdr/stack/rrc_nr/rrc_nr.h +++ b/srsue/hdr/stack/rrc_nr/rrc_nr.h @@ -90,16 +90,19 @@ public: asn1::dyn_octstring oct, const T& msg, const std::string& msg_type); + + void run_tti(uint32_t tti); + // PHY interface void in_sync() final; void out_of_sync() final; + void cell_search_found_cell(const cell_search_result_t& result) final{}; // RLC interface void max_retx_attempted() final; void protocol_failure() final; // MAC interface - void run_tti(uint32_t tti) final; void ra_completed() final; void ra_problem() final; void release_pucch_srs() final; @@ -124,13 +127,7 @@ public: int get_nr_capabilities(srsran::byte_buffer_t* eutra_nr_caps); void phy_meas_stop(); void phy_set_cells_to_meas(uint32_t carrier_freq_r15); - bool rrc_reconfiguration(bool endc_release_and_add_r15, - bool nr_secondary_cell_group_cfg_r15_present, - asn1::dyn_octstring nr_secondary_cell_group_cfg_r15, - bool sk_counter_r15_present, - uint32_t sk_counter_r15, - bool nr_radio_bearer_cfg1_r15_present, - asn1::dyn_octstring nr_radio_bearer_cfg1_r15); + bool rrc_reconfiguration(bool endc_release_and_add_r15, const asn1::rrc_nr::rrc_recfg_s& rrc_nr_reconf); void rrc_release(); bool configure_sk_counter(uint16_t sk_counter); bool is_config_pending(); @@ -140,8 +137,20 @@ public: void set_phy_config_complete(bool status) final; private: + // parsers + void decode_pdu_bcch_dlsch(srsran::unique_byte_buffer_t pdu); + void decode_dl_ccch(srsran::unique_byte_buffer_t pdu); + void decode_dl_dcch(uint32_t lcid, srsran::unique_byte_buffer_t pdu); // senders + void send_setup_request(srsran::nr_establishment_cause_t cause); + void send_con_setup_complete(srsran::unique_byte_buffer_t nas_msg); void send_ul_info_transfer(srsran::unique_byte_buffer_t nas_msg); + void send_ul_ccch_msg(const asn1::rrc_nr::ul_ccch_msg_s& msg); + void send_ul_dcch_msg(uint32_t lcid, const asn1::rrc_nr::ul_dcch_msg_s& msg); + + // helpers + void handle_sib1(const asn1::rrc_nr::sib1_s& sib1); + bool handle_rrc_setup(const asn1::rrc_nr::rrc_setup_s& setup); srsran::task_sched_handle task_sched; struct cmd_msg_t { @@ -168,6 +177,10 @@ private: meas_cell_list meas_cells; + // PLMN + bool plmn_is_selected = false; + srsran::unique_byte_buffer_t dedicated_info_nas; + const uint32_t sim_measurement_timer_duration_ms = 250; uint32_t sim_measurement_carrier_freq_r15; srsran::timer_handler::unique_timer sim_measurement_timer; @@ -182,7 +195,9 @@ private: const static char* rrc_nr_state_text[RRC_NR_STATE_N_ITEMS]; rrc_nr_state_t state = RRC_NR_STATE_IDLE; - // Stores the state of the PHy configuration setting + uint8_t transaction_id = 0; + + // Stores the state of the PHY configuration setting enum { PHY_CFG_STATE_NONE = 0, PHY_CFG_STATE_APPLY_SP_CELL, @@ -228,9 +243,16 @@ private: typedef enum { mcg_srb1, en_dc_srb3, nr } reconf_initiator_t; // RRC procedures + enum class cell_search_result_t { changed_cell, same_cell, no_cell }; + class cell_selection_proc; + class connection_setup_proc; class connection_reconf_no_ho_proc; + class setup_request_proc; - srsran::proc_t conn_recfg_proc; + srsran::proc_t cell_selector; + srsran::proc_t conn_setup_proc; + srsran::proc_t conn_recfg_proc; + srsran::proc_t setup_req_proc; srsran::proc_manager_list_t callback_list; }; diff --git a/srsue/hdr/stack/rrc_nr/rrc_nr_procedures.h b/srsue/hdr/stack/rrc_nr/rrc_nr_procedures.h index f6801d7cb..6aee55100 100644 --- a/srsue/hdr/stack/rrc_nr/rrc_nr_procedures.h +++ b/srsue/hdr/stack/rrc_nr/rrc_nr_procedures.h @@ -31,18 +31,88 @@ namespace srsue { * Procedures *******************************/ +class rrc_nr::cell_selection_proc +{ +public: + using cell_selection_complete_ev = srsran::proc_result_t; + + explicit cell_selection_proc(rrc_nr& parent_); + srsran::proc_outcome_t init(); + srsran::proc_outcome_t step(); + cell_search_result_t get_result() const { return cell_search_ret; } + static const char* name() { return "Cell Selection"; } + srsran::proc_outcome_t react(const bool& event); + void then(const srsran::proc_result_t& proc_result) const; + +private: + bool is_serv_cell_suitable() const; + srsran::proc_outcome_t set_proc_complete(); + + // consts + rrc_nr& rrc_handle; + meas_cell_list& meas_cells; + + // state variables + enum class search_state_t { cell_selection, serv_cell_camp, cell_config, cell_search }; + cell_search_result_t cell_search_ret; + search_state_t state; + srsran::proc_future_t cell_search_fut; + srsran::proc_future_t serv_cell_cfg_fut; + phy_cell_t init_serv_cell; +}; + +class rrc_nr::setup_request_proc +{ +public: + explicit setup_request_proc(rrc_nr& parent_); + srsran::proc_outcome_t init(srsran::nr_establishment_cause_t cause_, + srsran::unique_byte_buffer_t dedicated_info_nas_); + srsran::proc_outcome_t step(); + void then(const srsran::proc_state_t& result); + srsran::proc_outcome_t react(const cell_selection_proc::cell_selection_complete_ev& e); + static const char* name() { return "Setup Request"; } + +private: + // const + rrc_nr& rrc_handle; + srslog::basic_logger& logger; + // args + srsran::nr_establishment_cause_t cause; + srsran::unique_byte_buffer_t dedicated_info_nas; + + // state variables + enum class state_t { cell_selection, config_serving_cell, wait_t300 } state; + cell_search_result_t cell_search_ret; + srsran::proc_future_t serv_cfg_fut; +}; + +class rrc_nr::connection_setup_proc +{ +public: + explicit connection_setup_proc(rrc_nr& parent_); + srsran::proc_outcome_t init(const asn1::rrc_nr::radio_bearer_cfg_s radio_bearer_cfg_, + const asn1::rrc_nr::cell_group_cfg_s cell_group_, + srsran::unique_byte_buffer_t dedicated_info_nas_); + srsran::proc_outcome_t step() { return srsran::proc_outcome_t::yield; } + static const char* name() { return "Connection Setup"; } + srsran::proc_outcome_t react(const bool& config_complete); + void then(const srsran::proc_state_t& result); + +private: + // const + rrc_nr& rrc_handle; + srslog::basic_logger& logger; + // args + srsran::unique_byte_buffer_t dedicated_info_nas; +}; + class rrc_nr::connection_reconf_no_ho_proc { public: - explicit connection_reconf_no_ho_proc(rrc_nr* parent_); - srsran::proc_outcome_t init(const reconf_initiator_t initiator_, - const bool endc_release_and_add_r15, - const bool nr_secondary_cell_group_cfg_r15_present, - const asn1::dyn_octstring nr_secondary_cell_group_cfg_r15, - const bool sk_counter_r15_present, - const uint32_t sk_counter_r15, - const bool nr_radio_bearer_cfg1_r15_present, - const asn1::dyn_octstring nr_radio_bearer_cfg1_r15); + explicit connection_reconf_no_ho_proc(rrc_nr& parent_); + srsran::proc_outcome_t init(const reconf_initiator_t initiator_, + const bool endc_release_and_add_r15, + const asn1::rrc_nr::rrc_recfg_s& rrc_nr_reconf); srsran::proc_outcome_t step() { return srsran::proc_outcome_t::yield; } static const char* name() { return "NR Connection Reconfiguration"; } srsran::proc_outcome_t react(const bool& config_complete); @@ -50,7 +120,7 @@ public: private: // const - rrc_nr* rrc_ptr = nullptr; + rrc_nr& rrc_handle; reconf_initiator_t initiator; asn1::rrc_nr::rrc_recfg_s rrc_recfg; asn1::rrc_nr::cell_group_cfg_s cell_group_cfg; diff --git a/srsue/hdr/stack/ue_stack_base.h b/srsue/hdr/stack/ue_stack_base.h index d4749e96f..17221b312 100644 --- a/srsue/hdr/stack/ue_stack_base.h +++ b/srsue/hdr/stack/ue_stack_base.h @@ -71,9 +71,11 @@ typedef struct { rrc_nr_args_t rrc_nr; std::string ue_category_str; nas_args_t nas; + nas_5g_args_t nas_5g; gw_args_t gw; uint32_t sync_queue_size; // Max allowed difference between PHY and Stack clocks (in TTI) bool have_tti_time_stats; + bool attach_on_nr; } stack_args_t; class ue_stack_base diff --git a/srsue/hdr/stack/ue_stack_lte.h b/srsue/hdr/stack/ue_stack_lte.h index 7980a8a49..0fd6924c7 100644 --- a/srsue/hdr/stack/ue_stack_lte.h +++ b/srsue/hdr/stack/ue_stack_lte.h @@ -44,6 +44,7 @@ #include "srsue/hdr/ue_metrics_interface.h" #include "ue_stack_base.h" #include "upper/nas.h" +#include "upper/nas_5g.h" #include "upper/usim.h" #include #include @@ -132,8 +133,10 @@ public: void run_tti(uint32_t tti, uint32_t tti_jump) final; + // RRC interface for NR PHY + void cell_search_found_cell(const cell_search_result_t& result) final {} + // MAC Interface for NR PHY - int sf_indication(const uint32_t tti) final { return SRSRAN_SUCCESS; } void tb_decoded(const uint32_t cc_idx, const mac_nr_grant_dl_t& grant, mac_interface_phy_nr::tb_action_dl_result_t result) final @@ -153,11 +156,6 @@ public: mac_nr.new_grant_ul(cc_idx, grant, action); } - void run_tti(const uint32_t tti) final - { - // ignored, timing will be handled by EUTRA - } - void prach_sent(uint32_t tti, uint32_t s_id, uint32_t t_id, uint32_t f_id, uint32_t ul_carrier_id) final { mac_nr.prach_sent(tti, s_id, t_id, f_id, ul_carrier_id); @@ -238,6 +236,7 @@ private: srsran::pdcp pdcp_nr; srsue::rrc_nr rrc_nr; srsue::nas nas; + srsue::nas_5g nas_5g; std::unique_ptr usim; ue_bearer_manager bearers; // helper to manage mapping between EPS and radio bearers diff --git a/srsue/hdr/stack/ue_stack_nr.h b/srsue/hdr/stack/ue_stack_nr.h index 10190d0d7..e08f484c3 100644 --- a/srsue/hdr/stack/ue_stack_nr.h +++ b/srsue/hdr/stack/ue_stack_nr.h @@ -81,17 +81,15 @@ public: // RRC interface for PHY void in_sync() final; void out_of_sync() final; - void run_tti(const uint32_t tti) final; - void set_phy_config_complete(bool status) override; + void set_phy_config_complete(bool status) final; + void cell_search_found_cell(const cell_search_result_t& result) final; + + void run_tti(uint32_t tti, uint32_t tti_jump) final; // MAC interface for PHY sched_rnti_t get_dl_sched_rnti_nr(const uint32_t tti) final { return mac->get_dl_sched_rnti_nr(tti); } sched_rnti_t get_ul_sched_rnti_nr(const uint32_t tti) final { return mac->get_ul_sched_rnti_nr(tti); } - int sf_indication(const uint32_t tti) final - { - run_tti(tti); - return SRSRAN_SUCCESS; - } + void tb_decoded(const uint32_t cc_idx, const mac_nr_grant_dl_t& grant, tb_action_dl_result_t result) final { mac->tb_decoded(cc_idx, grant, std::move(result)); diff --git a/srsue/hdr/ue.h b/srsue/hdr/ue.h index 12d9667b5..53e224ce9 100644 --- a/srsue/hdr/ue.h +++ b/srsue/hdr/ue.h @@ -114,6 +114,7 @@ public: private: // UE consists of a radio, a PHY and a stack element std::unique_ptr phy; + std::unique_ptr dummy_phy; std::unique_ptr radio; std::unique_ptr stack; std::unique_ptr gw_inst; diff --git a/srsue/src/main.cc b/srsue/src/main.cc index b41c32167..8310219cd 100644 --- a/srsue/src/main.cc +++ b/srsue/src/main.cc @@ -133,7 +133,7 @@ static int parse_args(all_args_t* args, int argc, char* argv[]) ("rat.nr.bands", bpo::value(&args->stack.rrc_nr.supported_bands_nr_str)->default_value("78"), "Supported NR bands") ("rat.nr.nof_carriers", bpo::value(&args->phy.nof_nr_carriers)->default_value(0), "Number of NR carriers") - ("rat.nr.max_nof_prb", bpo::value(&args->phy.nr_max_nof_prb)->default_value(106), "Maximum NR carrier bandwidth in PRB") + ("rat.nr.max_nof_prb", bpo::value(&args->phy.nr_max_nof_prb)->default_value(52), "Maximum NR carrier bandwidth in PRB") ("rrc.feature_group", bpo::value(&args->stack.rrc.feature_group)->default_value(0xe6041000), "Hex value of the featureGroupIndicators field in the" "UECapabilityInformation message. Default 0xe6041000") @@ -477,10 +477,6 @@ static int parse_args(all_args_t* args, int argc, char* argv[]) bpo::value(&args->stack.have_tti_time_stats)->default_value(true), "Calculate TTI execution statistics") - // NR params - ("vnf.type", bpo::value(&args->phy.vnf_args.type)->default_value("ue"), "VNF instance type [gnb,ue]") - ("vnf.addr", bpo::value(&args->phy.vnf_args.bind_addr)->default_value("localhost"), "Address to bind VNF interface") - ("vnf.port", bpo::value(&args->phy.vnf_args.bind_port)->default_value(3334), "Bind port") ; // Positional options - config file location diff --git a/srsue/src/phy/nr/cell_search.cc b/srsue/src/phy/nr/cell_search.cc new file mode 100644 index 000000000..e4b14b556 --- /dev/null +++ b/srsue/src/phy/nr/cell_search.cc @@ -0,0 +1,83 @@ +/** + * + * \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/phy/nr/cell_search.h" +#include "srsran/common/band_helper.h" +#include "srsran/common/buffer_pool.h" +#include "srsran/radio/rf_buffer.h" +#include "srsran/radio/rf_timestamp.h" + +namespace srsue { +namespace nr { +cell_search::cell_search(srslog::basic_logger& logger_) : logger(logger_) {} + +cell_search::~cell_search() +{ + srsran_ssb_free(&ssb); +} + +bool cell_search::init(const args_t& args) +{ + // Prepare SSB initialization arguments + srsran_ssb_args_t ssb_args = {}; + ssb_args.max_srate_hz = args.max_srate_hz; + ssb_args.min_scs = args.ssb_min_scs; + ssb_args.enable_search = true; + ssb_args.enable_decode = true; + + // Initialise SSB + if (srsran_ssb_init(&ssb, &ssb_args) < SRSRAN_SUCCESS) { + logger.error("Cell search: Error initiating SSB"); + return false; + } + + return true; +} + +bool cell_search::start(const cfg_t& cfg) +{ + // Prepare SSB configuration + srsran_ssb_cfg_t ssb_cfg = {}; + ssb_cfg.srate_hz = cfg.srate_hz; + ssb_cfg.center_freq_hz = cfg.center_freq_hz; + ssb_cfg.ssb_freq_hz = cfg.ssb_freq_hz; + ssb_cfg.scs = cfg.ssb_scs; + ssb_cfg.pattern = cfg.ssb_pattern; + ssb_cfg.duplex_mode = cfg.duplex_mode; + + // Configure SSB + if (srsran_ssb_set_cfg(&ssb, &ssb_cfg) < SRSRAN_SUCCESS) { + logger.error("Cell search: Error setting SSB configuration"); + return false; + } + return true; +} + +cell_search::ret_t cell_search::run_slot(const cf_t* buffer, uint32_t slot_sz) +{ + cell_search::ret_t ret = {}; + + // Search for SSB + if (srsran_ssb_search(&ssb, buffer, slot_sz + ssb.ssb_sz, &ret.ssb_res) < SRSRAN_SUCCESS) { + logger.error("Error occurred searching SSB"); + ret.result = ret_t::ERROR; + } else if (ret.ssb_res.measurements.snr_dB >= -10.0f and ret.ssb_res.pbch_msg.crc) { + // Consider the SSB is found and decoded if the PBCH CRC matched + ret.result = ret_t::CELL_FOUND; + } else { + ret.result = ret_t::CELL_NOT_FOUND; + } + return ret; +} + +} // namespace nr +} // namespace srsue \ No newline at end of file diff --git a/srsue/src/phy/nr/slot_sync.cc b/srsue/src/phy/nr/slot_sync.cc new file mode 100644 index 000000000..31161d456 --- /dev/null +++ b/srsue/src/phy/nr/slot_sync.cc @@ -0,0 +1,131 @@ +/** + * + * \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/phy/nr/slot_sync.h" + +namespace srsue { +namespace nr { + +slot_sync::slot_sync(srslog::basic_logger& logger_) : logger(logger_) {} + +slot_sync::~slot_sync() +{ + srsran_ue_sync_nr_free(&ue_sync_nr); +} + +int sync_sa_recv_callback(void* ptr, cf_t** buffer, uint32_t nsamples, srsran_timestamp_t* ts) +{ + if (ptr == nullptr) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + slot_sync* sync = (slot_sync*)ptr; + srsran::rf_buffer_t rf_buffer(buffer, nsamples); + + return sync->recv_callback(rf_buffer, ts); +} + +bool slot_sync::init(const args_t& args, stack_interface_phy_nr* stack_, srsran::radio_interface_phy* radio_) +{ + stack = stack_; + radio = radio_; + + srsran_ue_sync_nr_args_t ue_sync_nr_args = {}; + ue_sync_nr_args.max_srate_hz = args.max_srate_hz; + ue_sync_nr_args.min_scs = args.ssb_min_scs; + ue_sync_nr_args.nof_rx_channels = args.nof_rx_channels; + ue_sync_nr_args.disable_cfo = args.disable_cfo; + ue_sync_nr_args.pbch_dmrs_thr = args.pbch_dmrs_thr; + ue_sync_nr_args.cfo_alpha = args.cfo_alpha; + + if (srsran_ue_sync_nr_init(&ue_sync_nr, &ue_sync_nr_args) < SRSRAN_SUCCESS) { + logger.error("Error initiating UE SYNC NR object"); + return false; + } + + return true; +} + +int slot_sync::recv_callback(srsran::rf_buffer_t& data, srsran_timestamp_t* rx_time) +{ + // This function is designed for being called from the UE sync object which will pass a null rx_time in case + // receive dummy samples. So, rf_timestamp points at dummy timestamp in case rx_time is not provided + srsran::rf_timestamp_t dummy_ts = {}; + srsran::rf_timestamp_t& rf_timestamp = (rx_time == nullptr) ? dummy_ts : last_rx_time; + + // Receive + if (not radio->rx_now(data, rf_timestamp)) { + return SRSRAN_ERROR; + } + + srsran_timestamp_t dummy_flat_ts = {}; + + // Load flat timestamp + if (rx_time == nullptr) { + rx_time = &dummy_flat_ts; + } + *rx_time = rf_timestamp.get(0); + + // Save RF timestamp for the stack + stack_tti_ts_new = rf_timestamp.get(0); + + // Run stack if the sync state is not in camping + if (state == SEARCHING) { + logger.debug("run_stack_tti: from recv"); + run_stack_tti(); + } + + logger.debug("SYNC: received %d samples from radio", data.get_nof_samples()); + + return data.get_nof_samples(); +} + +void slot_sync::run_stack_tti() +{ // check timestamp reset + if (forced_rx_time_init || srsran_timestamp_iszero(&stack_tti_ts) || + srsran_timestamp_compare(&stack_tti_ts_new, &stack_tti_ts) < 0) { + if (srsran_timestamp_compare(&stack_tti_ts_new, &stack_tti_ts) < 0) { + logger.warning("SYNC: radio time seems to be going backwards (rx_time=%f, tti_ts=%f)", + srsran_timestamp_real(&stack_tti_ts_new), + srsran_timestamp_real(&stack_tti_ts)); + // time-stamp will be set to rx time below and run_tti() will be called with MIN_TTI_JUMP + } + + // init tti_ts with last rx time + logger.debug("SYNC: Setting initial TTI time to %f", srsran_timestamp_real(&stack_tti_ts_new)); + srsran_timestamp_copy(&stack_tti_ts, &stack_tti_ts_new); + forced_rx_time_init = false; + } + + // Advance stack in time + if (srsran_timestamp_compare(&stack_tti_ts_new, &stack_tti_ts) >= 0) { + srsran_timestamp_t temp = {}; + srsran_timestamp_copy(&temp, &stack_tti_ts_new); + srsran_timestamp_sub(&temp, stack_tti_ts.full_secs, stack_tti_ts.frac_secs); + int32_t tti_jump = static_cast(srsran_timestamp_uint64(&temp, 1e3)); + tti_jump = SRSRAN_MAX(tti_jump, MIN_TTI_JUMP); + if (tti_jump > MAX_TTI_JUMP) { + logger.warning("SYNC: TTI jump of %d limited to %d", tti_jump, int(MAX_TTI_JUMP)); + tti_jump = SRSRAN_MIN(tti_jump, MAX_TTI_JUMP); + } + + // Run stack + logger.debug("run_stack_tti: calling stack tti=%d, tti_jump=%d", tti, tti_jump); + stack->run_tti(tti, tti_jump); + logger.debug("run_stack_tti: stack called"); + } + + // update timestamp + srsran_timestamp_copy(&stack_tti_ts, &stack_tti_ts_new); +} + +} // namespace nr +} // namespace srsue \ No newline at end of file diff --git a/srsue/src/phy/nr/worker_pool.cc b/srsue/src/phy/nr/worker_pool.cc index 0168ee8f8..ba5f1aad5 100644 --- a/srsue/src/phy/nr/worker_pool.cc +++ b/srsue/src/phy/nr/worker_pool.cc @@ -24,7 +24,7 @@ namespace srsue { namespace nr { -worker_pool::worker_pool(uint32_t max_workers) : pool(max_workers), logger(srslog::fetch_basic_logger("PHY-NR")) {} +worker_pool::worker_pool(srslog::basic_logger& logger_, uint32_t max_workers) : pool(max_workers), logger(logger_) {} bool worker_pool::init(const phy_args_nr_t& args, srsran::phy_common_interface& common, @@ -167,10 +167,10 @@ void worker_pool::send_prach(const uint32_t prach_occasion, } // called from Stack thread when processing RAR PDU -int worker_pool::set_ul_grant(uint32_t rar_slot_idx, - std::array packed_ul_grant, - uint16_t rnti, - srsran_rnti_type_t rnti_type) +int worker_pool::set_rar_grant(uint32_t rar_slot_idx, + std::array packed_ul_grant, + uint16_t rnti, + srsran_rnti_type_t rnti_type) { // Copy DCI bits and setup DCI context srsran_dci_msg_nr_t dci_msg = {}; @@ -303,10 +303,5 @@ void worker_pool::get_metrics(phy_metrics_t& m) phy_state.get_metrics(m); } -int worker_pool::tx_request(const phy_interface_mac_nr::tx_request_t& request) -{ - return 0; -} - } // namespace nr } // namespace srsue diff --git a/srsue/src/phy/phy.cc b/srsue/src/phy/phy.cc index 5f4ad696c..1c99219f7 100644 --- a/srsue/src/phy/phy.cc +++ b/srsue/src/phy/phy.cc @@ -101,18 +101,11 @@ bool phy::check_args(const phy_args_t& args_) int phy::init(const phy_args_t& args_, stack_interface_phy_lte* stack_, srsran::radio_interface_phy* radio_) { + std::unique_lock lock(config_mutex); + stack = stack_; radio = radio_; - init(args_); - - return SRSRAN_SUCCESS; -} - -int phy::init(const phy_args_t& args_) -{ - std::unique_lock lock(config_mutex); - args = args_; // Force frequency if given as argument @@ -133,12 +126,12 @@ int phy::init(const phy_args_t& args_) logger_phy.set_hex_dump_max_size(args.log.phy_hex_limit); if (!check_args(args)) { - return false; + return SRSRAN_ERROR; } is_configured = false; start(); - return true; + return SRSRAN_SUCCESS; } // Initializes PHY in a thread @@ -168,7 +161,7 @@ void phy::wait_initialize() } } -bool phy::is_initiated() +bool phy::is_initialized() { return is_configured; } @@ -457,7 +450,7 @@ void phy::start_plot() bool phy::set_config(const srsran::phy_cfg_t& config_, uint32_t cc_idx) { - if (!is_initiated()) { + if (!is_initialized()) { fprintf(stderr, "Error calling set_config(): PHY not initialized\n"); return false; } @@ -493,7 +486,7 @@ bool phy::set_config(const srsran::phy_cfg_t& config_, uint32_t cc_idx) bool phy::set_scell(srsran_cell_t cell_info, uint32_t cc_idx, uint32_t earfcn) { - if (!is_initiated()) { + if (!is_initialized()) { fprintf(stderr, "Error calling set_config(): PHY not initialized\n"); return false; } @@ -640,12 +633,12 @@ int phy::init(const phy_args_nr_t& args_, stack_interface_phy_nr* stack_, srsran return SRSRAN_SUCCESS; } -int phy::set_ul_grant(uint32_t rar_slot_idx, - std::array packed_ul_grant, - uint16_t rnti, - srsran_rnti_type_t rnti_type) +int phy::set_rar_grant(uint32_t rar_slot_idx, + std::array packed_ul_grant, + uint16_t rnti, + srsran_rnti_type_t rnti_type) { - return nr_workers.set_ul_grant(rar_slot_idx, packed_ul_grant, rnti, rnti_type); + return nr_workers.set_rar_grant(rar_slot_idx, packed_ul_grant, rnti, rnti_type); } void phy::send_prach(const uint32_t prach_occasion, @@ -656,11 +649,6 @@ void phy::send_prach(const uint32_t prach_occasion, nr_workers.send_prach(prach_occasion, preamble_index, preamble_received_target_power); } -int phy::tx_request(const phy_interface_mac_nr::tx_request_t& request) -{ - return 0; -} - void phy::set_earfcn(std::vector earfcns) { // Do nothing diff --git a/srsue/src/phy/phy_nr_sa.cc b/srsue/src/phy/phy_nr_sa.cc new file mode 100644 index 000000000..e7cc2448c --- /dev/null +++ b/srsue/src/phy/phy_nr_sa.cc @@ -0,0 +1,260 @@ +/** + * + * \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/phy/phy_nr_sa.h" +#include "srsran/common/standard_streams.h" +#include "srsran/srsran.h" + +namespace srsue { + +static void srsran_phy_handler(phy_logger_level_t log_level, void* ctx, char* str) +{ + phy_nr_sa* r = (phy_nr_sa*)ctx; + r->srsran_phy_logger(log_level, str); +} + +void phy_nr_sa::srsran_phy_logger(phy_logger_level_t log_level, char* str) +{ + switch (log_level) { + case LOG_LEVEL_INFO_S: + logger_phy_lib.info(" %s", str); + break; + case LOG_LEVEL_DEBUG_S: + logger_phy_lib.debug(" %s", str); + break; + case LOG_LEVEL_ERROR_S: + logger_phy_lib.error(" %s", str); + break; + default: + break; + } +} + +void phy_nr_sa::set_default_args(phy_args_nr_t& args_) +{ + args_.nof_phy_threads = DEFAULT_WORKERS; + // TODO +} + +bool phy_nr_sa::check_args(const phy_args_nr_t& args_) +{ + if (args_.nof_phy_threads > MAX_WORKERS) { + srsran::console("Error in PHY args: nof_phy_threads must be 1, 2 or 3\n"); + return false; + } + return true; +} + +phy_nr_sa::phy_nr_sa(const char* logname) : + logger(srslog::fetch_basic_logger(logname)), + logger_phy_lib(srslog::fetch_basic_logger("PHY_LIB")), + sync(logger, workers), + workers(logger, 4) +{} + +int phy_nr_sa::init(const phy_args_nr_t& args_, stack_interface_phy_nr* stack_, srsran::radio_interface_phy* radio_) +{ + args = args_; + stack = stack_; + radio = radio_; + + // Add PHY lib log + auto lib_log_level = srslog::str_to_basic_level(args.log.phy_lib_level); + logger_phy_lib.set_level(lib_log_level); + logger_phy_lib.set_hex_dump_max_size(args.log.phy_hex_limit); + if (lib_log_level != srslog::basic_levels::none) { + srsran_phy_log_register_handler(this, srsran_phy_handler); + } + + // set default logger + logger.set_level(srslog::str_to_basic_level(args.log.phy_level)); + logger.set_hex_dump_max_size(args.log.phy_hex_limit); + + logger.set_context(0); + + if (!check_args(args)) { + return SRSRAN_ERROR; + } + + is_configured = false; + std::thread t([this]() { init_background(); }); + init_thread = std::move(t); + + return SRSRAN_SUCCESS; +} + +void phy_nr_sa::init_background() +{ + nr::sync_sa::args_t sync_args = {}; + sync_args.srate_hz = srsran_sampling_freq_hz(args.max_nof_prb); + if (not sync.init(sync_args, stack, radio)) { + logger.error("Error initialising SYNC"); + return; + } + workers.init(args, sync, stack, WORKERS_THREAD_PRIO); + + is_configured = true; +} + +void phy_nr_sa::stop() +{ + cmd_worker.stop(); + cmd_worker_cell.stop(); + if (is_configured) { + sync.stop(); + workers.stop(); + is_configured = false; + } +} + +bool phy_nr_sa::is_initialized() +{ + return is_configured; +} + +void phy_nr_sa::wait_initialize() +{ + init_thread.join(); +} + +phy_interface_rrc_nr::phy_nr_state_t phy_nr_sa::get_state() +{ + { + switch (sync.get_state()) { + case sync_state::state_t::IDLE: + return phy_interface_rrc_nr::PHY_NR_STATE_IDLE; + case sync_state::state_t::CELL_SEARCH: + return phy_interface_rrc_nr::PHY_NR_STATE_CELL_SEARCH; + case sync_state::state_t::SFN_SYNC: + return phy_interface_rrc_nr::PHY_NR_STATE_CELL_SELECT; + case sync_state::state_t::CAMPING: + return phy_interface_rrc_nr::PHY_NR_STATE_CAMPING; + } + } + return phy_interface_rrc_nr::PHY_NR_STATE_IDLE; +} + +void phy_nr_sa::reset_nr() +{ + sync.reset(); +} + +// This function executes one part of the procedure immediately and returns to continue in the background. +// When it returns, the caller thread can expect the PHY to have switched to IDLE and have stopped all DL/UL/PRACH +// processing. +// It will perform cell search procedure in the background and will signal stack with function cell_search_found_cell() +// when finished +bool phy_nr_sa::start_cell_search(const cell_search_args_t& req) +{ + // TODO: verify arguments are valid before starting procedure + + logger.info("Cell Search: Going to IDLE"); + sync.cell_go_idle(); + + cmd_worker_cell.add_cmd([this, req]() { + // Prepare cell search configuration from the request + nr::cell_search::cfg_t cfg = {}; + cfg.srate_hz = req.srate_hz; + cfg.center_freq_hz = req.center_freq_hz; + cfg.ssb_freq_hz = req.ssb_freq_hz; + cfg.ssb_scs = req.ssb_scs; + cfg.ssb_pattern = req.ssb_pattern; + cfg.duplex_mode = req.duplex_mode; + + // Request cell search to lower synchronization instance. + nr::cell_search::ret_t ret = sync.cell_search_run(cfg); + + // Pass result to stack + rrc_interface_phy_nr::cell_search_result_t rrc_cs_ret = {}; + rrc_cs_ret.cell_found = ret.result == nr::cell_search::ret_t::CELL_FOUND; + if (rrc_cs_ret.cell_found) { + rrc_cs_ret.pci = ret.ssb_res.N_id; + rrc_cs_ret.pbch_msg = ret.ssb_res.pbch_msg; + rrc_cs_ret.measurements = ret.ssb_res.measurements; + } + stack->cell_search_found_cell(rrc_cs_ret); + }); + + return true; +} + +// This function executes one part of the procedure immediately and returns to continue in the background. +// When it returns, the caller thread can expect the PHY to have switched to IDLE and have stopped all DL/UL/PRACH +// processing. +// It will perform cell search procedure in the background and will signal stack with function cell_search_found_cell() +// when finished +bool phy_nr_sa::start_cell_select(const cell_select_args_t& req) +{ + // TODO: verify arguments are valid before starting procedure + + logger.info("Cell Select: Going to IDLE"); + sync.cell_go_idle(); + + selected_cell = req.carrier; + + cmd_worker_cell.add_cmd([this, req]() { + + // Request cell search to lower synchronization instance. + sync.cell_select_run(req); + }); + + return true; +} + +bool phy_nr_sa::has_valid_sr_resource(uint32_t sr_id) +{ + return workers.has_valid_sr_resource(sr_id); +} + +void phy_nr_sa::clear_pending_grants() +{ + workers.clear_pending_grants(); +} + +void phy_nr_sa::send_prach(const uint32_t prach_occasion, + const int preamble_index, + const float preamble_received_target_power, + const float ta_base_sec) +{ + workers.send_prach(prach_occasion, preamble_index, preamble_received_target_power); +} + +int phy_nr_sa::set_rar_grant(uint32_t rar_slot_idx, + std::array packed_ul_grant, + uint16_t rnti, + srsran_rnti_type_t rnti_type) +{ + return workers.set_rar_grant(rar_slot_idx, packed_ul_grant, rnti, rnti_type); +} + +bool phy_nr_sa::set_config(const srsran::phy_cfg_nr_t& cfg) +{ + // Stash NR configuration + config_nr = cfg; + + // Setup carrier configuration asynchronously + cmd_worker.add_cmd([this]() { + + // Set UE configuration + bool ret = workers.set_config(config_nr); + + // Notify PHY config completion + if (stack != nullptr) { + stack->set_phy_config_complete(ret); + } + + return ret; + }); + return true; +} + +} // namespace srsue \ No newline at end of file diff --git a/srsue/src/phy/sync_sa.cc b/srsue/src/phy/sync_sa.cc new file mode 100644 index 000000000..e554a05a1 --- /dev/null +++ b/srsue/src/phy/sync_sa.cc @@ -0,0 +1,327 @@ +/** + * + * \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/phy/nr/sync_sa.h" +#include "srsran/radio/rf_buffer.h" + +namespace srsue { +namespace nr { +sync_sa::sync_sa(srslog::basic_logger& logger_, worker_pool& workers_) : + logger(logger_), workers(workers_), slot_synchronizer(logger_), searcher(logger_), srsran::thread("SYNC") +{} + +sync_sa::~sync_sa() {} + +bool sync_sa::init(const args_t& args, stack_interface_phy_nr* stack_, srsran::radio_interface_phy* radio_) +{ + stack = stack_; + radio = radio_; + slot_sz = (uint32_t)(args.srate_hz / 1000.0f); + + // Initialise cell search internal object + if (not searcher.init(args.get_cell_search())) { + logger.error("Error initialising cell searcher"); + return false; + } + + // Initialise slot synchronizer object + if (not slot_synchronizer.init(args.get_slot_sync(), stack, radio)) { + logger.error("Error initialising slot synchronizer"); + return false; + } + + // Cell bandwidth must be provided at init so set now sampling rate + radio->set_rx_srate(args.srate_hz); + radio->set_tx_srate(args.srate_hz); + + // Compute subframe size + slot_sz = (uint32_t)(args.srate_hz / 1000.0f); + + // Allocate receive buffer + rx_buffer = srsran_vec_cf_malloc(2 * slot_sz); + if (rx_buffer == nullptr) { + logger.error("Error allocating buffer"); + return false; + } + + // Thread control + running = true; + start(args.thread_priority); + + // If reached here it was successful + return true; +} + +void sync_sa::stop() +{ + running = false; + wait_thread_finish(); + radio->reset(); +} + +bool sync_sa::reset() +{ + // Wait worker pool to finish any processing + tti_semaphore.wait_all(); + + return true; +} + +void sync_sa::cell_go_idle() +{ + std::unique_lock ul(rrc_mutex); + phy_state.go_idle(); +} + +bool sync_sa::wait_idle() +{ + // Wait for SYNC thread to transition to IDLE (max. 2000ms) + if (!phy_state.wait_idle(100)) { + return false; + } + + // Reset UE sync. Attention: doing this reset when the FSM is NOT IDLE can cause PSS/SSS out-of-sync + //... + + // Wait for workers to finish PHY processing + tti_semaphore.wait_all(); + + // As workers have finished, make sure the Tx burst is ended + radio->tx_end(); + + return phy_state.is_idle(); +} + +cell_search::ret_t sync_sa::cell_search_run(const cell_search::cfg_t& cfg) +{ + std::unique_lock ul(rrc_mutex); + + cs_ret = {}; + cs_ret.result = cell_search::ret_t::ERROR; + + // Wait the FSM to transition to IDLE + if (!wait_idle()) { + logger.error("Cell Search: SYNC thread didn't transition to IDLE after 100 ms\n"); + return cs_ret; + } + + rrc_proc_state = PROC_SEARCH_RUNNING; + + // Configure searcher without locking state for avoiding stalling the Rx stream + logger.info("Cell search: starting in center frequency %.2f and SSB frequency %.2f with subcarrier spacing of %s", + cfg.center_freq_hz / 1e6, + cfg.ssb_freq_hz / 1e6, + srsran_subcarrier_spacing_to_str(cfg.ssb_scs)); + + if (not searcher.start(cfg)) { + logger.error("Sync: failed to start cell search"); + return cs_ret; + } + + // Zero receive buffer + srsran_vec_zero(rx_buffer, slot_sz); + + logger.info("Cell Search: Running Cell search state"); + cell_search_nof_trials = 0; + phy_state.run_cell_search(); + + rrc_proc_state = PROC_IDLE; + + return cs_ret; +} + +bool sync_sa::cell_select_run(const phy_interface_rrc_nr::cell_select_args_t& req) +{ + std::unique_lock ul(rrc_mutex); + + // Wait the FSM to transition to IDLE + if (!wait_idle()) { + logger.error("Cell Search: SYNC thread didn't transition to IDLE after 100 ms\n"); + return false; + } + + rrc_proc_state = PROC_SELECT_RUNNING; + + // tune radio + logger.info("Tuning Rx channel %d to %.2f MHz", 0, req.carrier.dl_center_frequency_hz / 1e6); + radio->set_rx_freq(0, req.carrier.dl_center_frequency_hz); + logger.info("Tuning Tx channel %d to %.2f MHz", 0, req.carrier.ul_center_frequency_hz / 1e6); + radio->set_tx_freq(0, req.carrier.ul_center_frequency_hz); + + // SFN synchronization + phy_state.run_sfn_sync(); + if (phy_state.is_camping()) { + logger.info("Cell Select: SFN synchronized. CAMPING..."); + } else { + logger.info("Cell Select: Could not synchronize SFN"); + } + + rrc_proc_state = PROC_IDLE; + return true; +} + +sync_state::state_t sync_sa::get_state() +{ + return phy_state.get_state(); +} + +void sync_sa::run_state_idle() +{ +#define test 0 + if (radio->is_init() && test) { + logger.debug("Discarding samples and sending tx_end"); + srsran::rf_buffer_t rf_buffer = {}; + rf_buffer.set_nof_samples(slot_sz); + rf_buffer.set(0, rx_buffer); + if (not slot_synchronizer.recv_callback(rf_buffer, last_rx_time.get_ptr(0))) { + logger.error("SYNC: receiving from radio\n"); + } + radio->tx_end(); + } else { + logger.debug("Sleeping 1 s"); + sleep(1); + } +} + +void sync_sa::run_state_cell_search() +{ + // Receive samples + srsran::rf_buffer_t rf_buffer = {}; + rf_buffer.set_nof_samples(slot_sz); + rf_buffer.set(0, rx_buffer); + if (not slot_synchronizer.recv_callback(rf_buffer, last_rx_time.get_ptr(0))) { + logger.error("SYNC: receiving from radio\n"); + } + + // Run Searcher + cs_ret = searcher.run_slot(rx_buffer, slot_sz); + if (cs_ret.result < 0) { + logger.error("Failed to run searcher. Transitioning to IDLE..."); + } + + cell_search_nof_trials++; + + // Leave CELL_SEARCH state if error or success and transition to IDLE + if (cs_ret.result || cell_search_nof_trials >= cell_search_max_trials) { + phy_state.state_exit(); + } +} + +void sync_sa::run_state_cell_select() +{ + // TODO + tti = 10240 - 4; + phy_state.state_exit(); +} + +void sync_sa::run_state_cell_camping() +{ + nr::sf_worker* nr_worker = nullptr; + nr_worker = workers.wait_worker(tti); + if (nr_worker == nullptr) { + running = false; + return; + } + + // Receive samples + srsran::rf_buffer_t rf_buffer = {}; + rf_buffer.set_nof_samples(slot_sz); + rf_buffer.set(0, nr_worker->get_buffer(0, 0)); + if (not slot_synchronizer.recv_callback(rf_buffer, last_rx_time.get_ptr(0))) { + logger.error("SYNC: receiving from radio\n"); + } + + srsran::phy_common_interface::worker_context_t context; + context.sf_idx = tti; + context.worker_ptr = nr_worker; + context.last = true; // Set last if standalone + last_rx_time.add(FDD_HARQ_DELAY_DL_MS * 1e-3); + context.tx_time.copy(last_rx_time); + + nr_worker->set_context(context); + + // NR worker needs to be launched first, phy_common::worker_end expects first the NR worker and the LTE worker. + tti_semaphore.push(nr_worker); + workers.start_worker(nr_worker); + + tti = TTI_ADD(tti, 1); +} + +void sync_sa::run_thread() +{ + while (running.load(std::memory_order_relaxed)) { + logger.set_context(tti); + + logger.debug("SYNC: state=%s, tti=%d", phy_state.to_string(), tti); + + switch (phy_state.run_state()) { + case sync_state::IDLE: + run_state_idle(); + break; + case sync_state::CELL_SEARCH: + run_state_cell_search(); + break; + case sync_state::SFN_SYNC: + run_state_cell_select(); + break; + case sync_state::CAMPING: + run_state_cell_camping(); + break; + } + // Advance stack TTI +#ifdef useradio + slot_synchronizer.run_stack_tti(); +#else + stack->run_tti(tti, 1); +#endif + } +} +void sync_sa::worker_end(const srsran::phy_common_interface::worker_context_t& w_ctx, + const bool& tx_enable, + srsran::rf_buffer_t& tx_buffer) +{ + // Wait for the green light to transmit in the current TTI + tti_semaphore.wait(w_ctx.worker_ptr); + + // Add current time alignment + srsran::rf_timestamp_t tx_time = w_ctx.tx_time; // get transmit time from the last worker + // todo: tx_time.sub((double)ta.get_sec()); + + // Check if any worker had a transmission + if (tx_enable) { + // Actual baseband transmission + radio->tx(tx_buffer, tx_time); + } else { + if (radio->is_continuous_tx()) { + if (is_pending_tx_end) { + radio->tx_end(); + is_pending_tx_end = false; + } else { + if (!radio->get_is_start_of_burst()) { + // TODO + /* + zeros_multi.set_nof_samples(buffer.get_nof_samples()); + radio->tx(zeros_multi, tx_time); + */ + } + } + } else { + radio->tx_end(); + } + } + + // Allow next TTI to transmit + tti_semaphore.release(); +} + +} // namespace nr +} // namespace srsue \ No newline at end of file diff --git a/srsue/src/phy/test/CMakeLists.txt b/srsue/src/phy/test/CMakeLists.txt index d782a3abd..012f2d442 100644 --- a/srsue/src/phy/test/CMakeLists.txt +++ b/srsue/src/phy/test/CMakeLists.txt @@ -82,4 +82,15 @@ target_link_libraries(nr_cell_search_rf srsran_phy srsran_radio ${CMAKE_THREAD_LIBS_INIT} + ${Boost_LIBRARIES}) + +# RF based Usage example: nr_sa_cell_search_test --phy.log.level=info --stack.log.level=info --duration=10000 --freq_dl=3.67536e9 --rf.freq_offset=10e3 --rf.rx_gain=90 +add_executable(nr_sa_cell_search_test nr_sa_cell_search_test.cc) +target_link_libraries(nr_sa_cell_search_test + srsue_phy + srsran_common + srsran_phy + srsran_radio + rrc_nr_asn1 + ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES}) \ No newline at end of file diff --git a/srsue/src/phy/test/gnb_emulator.h b/srsue/src/phy/test/gnb_emulator.h new file mode 100644 index 000000000..497f079ed --- /dev/null +++ b/srsue/src/phy/test/gnb_emulator.h @@ -0,0 +1,108 @@ +/** + * + * \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 SRSRAN_GNB_EMULATOR_H +#define SRSRAN_GNB_EMULATOR_H + +#include +#include +#include +#include + +class gnb_emulator +{ +private: + uint32_t sf_len = 0; + srsran_carrier_nr_t carrier = {}; + srsran_ssb_t ssb = {}; + srsran::channel channel; + std::vector buffer; + srslog::basic_logger& logger = srslog::fetch_basic_logger("GNB-EMULATOR"); + +public: + struct args_t { + double srate_hz; + srsran_carrier_nr_t carrier; + srsran_subcarrier_spacing_t ssb_scs; + srsran_ssb_patern_t ssb_pattern; + uint32_t ssb_periodicity_ms; + srsran_duplex_mode_t duplex_mode; + srsran::channel::args_t channel; + std::string log_level = "warning"; + }; + + gnb_emulator(const args_t& args) : channel(args.channel, 1, srslog::fetch_basic_logger("GNB-EMULATOR")) + { + logger.set_level(srslog::str_to_basic_level(args.log_level)); + + srsran_assert( + std::isnormal(args.srate_hz) and args.srate_hz > 0, "Invalid sampling rate (%.2f MHz)", args.srate_hz); + + // Initialise internals + sf_len = args.srate_hz / 1000; + carrier = args.carrier; + buffer.resize(sf_len); + + srsran_ssb_args_t ssb_args = {}; + ssb_args.enable_encode = true; + srsran_assert(srsran_ssb_init(&ssb, &ssb_args) == SRSRAN_SUCCESS, "SSB initialisation failed"); + + srsran_ssb_cfg_t ssb_cfg = {}; + ssb_cfg.srate_hz = args.srate_hz; + ssb_cfg.center_freq_hz = args.carrier.dl_center_frequency_hz; + ssb_cfg.ssb_freq_hz = args.carrier.ssb_center_freq_hz; + ssb_cfg.scs = args.ssb_scs; + ssb_cfg.pattern = args.ssb_pattern; + ssb_cfg.duplex_mode = args.duplex_mode; + ssb_cfg.periodicity_ms = args.ssb_periodicity_ms; + srsran_assert(srsran_ssb_set_cfg(&ssb, &ssb_cfg) == SRSRAN_SUCCESS, "SSB set config failed"); + + // Configure channel + channel.set_srate((uint32_t)args.srate_hz); + } + + int work(uint32_t sf_idx, cf_t* baseband_buffer, const srsran::rf_timestamp_t& ts) + { + logger.set_context(sf_idx); + + // Zero buffer + srsran_vec_cf_zero(buffer.data(), sf_len); + + // Check if SSB needs to be sent + if (srsran_ssb_send(&ssb, sf_idx)) { + // Prepare PBCH message + srsran_pbch_msg_nr_t msg = {}; + + // Add SSB + if (srsran_ssb_add(&ssb, carrier.pci, &msg, buffer.data(), buffer.data()) < SRSRAN_SUCCESS) { + logger.error("Error adding SSB"); + return SRSRAN_ERROR; + } + } + + // Run channel + cf_t* in[SRSRAN_MAX_CHANNELS] = {}; + cf_t* out[SRSRAN_MAX_CHANNELS] = {}; + in[0] = buffer.data(); + out[0] = buffer.data(); + channel.run(in, out, sf_len, ts.get(0)); + + // Add buffer to baseband buffer + srsran_vec_sum_ccc(baseband_buffer, buffer.data(), baseband_buffer, sf_len); + + return SRSRAN_SUCCESS; + } + + ~gnb_emulator() { srsran_ssb_free(&ssb); } +}; + +#endif // SRSRAN_GNB_EMULATOR_H diff --git a/srsue/src/phy/test/gnb_rf_emulator.h b/srsue/src/phy/test/gnb_rf_emulator.h new file mode 100644 index 000000000..36c0af036 --- /dev/null +++ b/srsue/src/phy/test/gnb_rf_emulator.h @@ -0,0 +1,142 @@ +/** + * + * \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 SRSRAN_GNB_RF_EMULATOR_H +#define SRSRAN_GNB_RF_EMULATOR_H + +#include "gnb_emulator.h" +#include +#include +#include +#include +#include + +class gnb_rf_emulator final : public srsran::radio_interface_phy +{ +private: + const uint32_t BUFFER_SIZE_SF = 10; + const std::string LOGNAME = "RF"; + uint32_t sf_len = 0; + srsran_ringbuffer_t ringbuffer = {}; + uint32_t slot_idx = 0; + std::atomic running = {true}; + srsran::rf_timestamp_t ts_write = {}; + std::vector buffer; + std::vector > gnb_vector; + + void run_async_slot() + { + // Early return if not running + if (not running) { + return; + } + + // Zero slot buffer + srsran_vec_cf_zero(buffer.data(), sf_len); + + for (std::shared_ptr& gnb : gnb_vector) { + srsran_assert(gnb->work(slot_idx, buffer.data(), ts_write) == SRSRAN_SUCCESS, "Failed to run gNb emulator"); + } + + // Write slot samples in ringbuffer + srsran_assert(srsran_ringbuffer_write(&ringbuffer, buffer.data(), (int)sizeof(cf_t) * sf_len) > SRSRAN_SUCCESS, + "Error writing in ringbuffer"); + + // Increment time + ts_write.add(0.001f); + } + +public: + struct args_t { + double srate_hz; + srsran_carrier_nr_t base_carrier; + srsran_subcarrier_spacing_t ssb_scs; + srsran_ssb_patern_t ssb_pattern; + uint32_t ssb_periodicity_ms; + srsran_duplex_mode_t duplex_mode; + std::set pci_list; + }; + + gnb_rf_emulator(const args_t& args) + { + srsran_assert( + std::isnormal(args.srate_hz) and args.srate_hz > 0, "Invalid sampling rate (%.2f MHz)", args.srate_hz); + + sf_len = args.srate_hz / 1000; + + for (uint32_t pci : args.pci_list) { + gnb_emulator::args_t gnb_args = {}; + gnb_args.srate_hz = args.srate_hz; + gnb_args.carrier = args.base_carrier; + gnb_args.carrier.pci = pci; + gnb_args.ssb_scs = args.ssb_scs; + gnb_args.ssb_pattern = args.ssb_pattern; + gnb_args.ssb_periodicity_ms = args.ssb_periodicity_ms; + gnb_args.duplex_mode = args.duplex_mode; + + gnb_vector.emplace_back(std::make_shared(gnb_args)); + } + + srsran_assert(srsran_ringbuffer_init(&ringbuffer, sizeof(cf_t) * BUFFER_SIZE_SF * sf_len) >= SRSRAN_SUCCESS, + "Ringbuffer initialisation failed"); + + buffer.resize(BUFFER_SIZE_SF * sf_len); + } + ~gnb_rf_emulator() = default; + void tx_end() override {} + bool tx(srsran::rf_buffer_interface& tx_buffer, const srsran::rf_timestamp_interface& tx_time) override + { + return false; + } + bool rx_now(srsran::rf_buffer_interface& rx_buffer, srsran::rf_timestamp_interface& rxd_time) override + { + int nbytes = (int)(sizeof(cf_t) * rx_buffer.get_nof_samples()); + cf_t* temp_buffer = rx_buffer.get(0); + + // If the buffer is invalid, use internal temporal buffer + if (temp_buffer == nullptr) { + temp_buffer = buffer.data(); + } + + // As long as there are not enough samples + while (srsran_ringbuffer_status(&ringbuffer) < nbytes and running) { + run_async_slot(); + } + + if (not running) { + return true; + } + + srsran_assert(srsran_ringbuffer_read(&ringbuffer, temp_buffer, nbytes) >= SRSRAN_SUCCESS, + "Error reading from ringbuffer"); + + return true; + } + void set_tx_freq(const uint32_t& carrier_idx, const double& freq) override {} + void set_rx_freq(const uint32_t& carrier_idx, const double& freq) override {} + void release_freq(const uint32_t& carrier_idx) override {} + void set_tx_gain(const float& gain) override {} + void set_rx_gain_th(const float& gain) override {} + void set_rx_gain(const float& gain) override {} + void set_tx_srate(const double& srate) override {} + void set_rx_srate(const double& srate) override {} + void set_channel_rx_offset(uint32_t ch, int32_t offset_samples) override {} + double get_freq_offset() override { return 0; } + float get_rx_gain() override { return 0; } + bool is_continuous_tx() override { return false; } + bool get_is_start_of_burst() override { return false; } + bool is_init() override { return false; } + void reset() override { running = false; } + srsran_rf_info_t* get_info() override { return nullptr; } +}; + +#endif // SRSRAN_GNB_RF_EMULATOR_H diff --git a/srsue/src/phy/test/nr_sa_cell_search_test.cc b/srsue/src/phy/test/nr_sa_cell_search_test.cc new file mode 100644 index 000000000..ad6048119 --- /dev/null +++ b/srsue/src/phy/test/nr_sa_cell_search_test.cc @@ -0,0 +1,333 @@ +/** + * + * \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 "gnb_rf_emulator.h" +#include "srsran/asn1/rrc_nr.h" +#include "srsran/common/band_helper.h" +#include "srsran/common/crash_handler.h" +#include "srsran/common/string_helpers.h" +#include "srsue/hdr/phy/phy_nr_sa.h" +#include "test/phy/dummy_ue_stack.h" +#include +#include +#include + +class throttled_dummy_stack : public ue_dummy_stack +{ +private: + bool pending_tti = false; + std::mutex pending_tti_mutex; + std::condition_variable pending_tti_cvar; + std::atomic running = {true}; + +public: + throttled_dummy_stack(const ue_dummy_stack::args_t& args, srsue::phy_interface_stack_nr& phy) : + ue_dummy_stack(args, phy) + {} + void wait_tti() override + { + // Wait for tick + std::unique_lock lock(pending_tti_mutex); + while (not pending_tti and running) { + pending_tti_cvar.wait(lock); + } + + // Let the tick proceed + pending_tti = false; + pending_tti_cvar.notify_all(); + } + + void tick() + { + // Wait for TTI to get processed + std::unique_lock lock(pending_tti_mutex); + while (pending_tti) { + pending_tti_cvar.wait(lock); + } + + // Let the TTI proceed + pending_tti = true; + pending_tti_cvar.notify_all(); + } + + void stop() + { + running = false; + pending_tti_cvar.notify_all(); + } +}; + +struct args_t { + // Generic parameters + double srate_hz = 11.52e6; + srsran_carrier_nr_t base_carrier = SRSRAN_DEFAULT_CARRIER_NR; + srsran_ssb_patern_t ssb_pattern = SRSRAN_SSB_PATTERN_A; + srsran_subcarrier_spacing_t ssb_scs = srsran_subcarrier_spacing_15kHz; + srsran_duplex_mode_t duplex_mode = SRSRAN_DUPLEX_MODE_FDD; + uint32_t duration_ms = 1000; + std::string phy_log_level = "warning"; + std::string stack_log_level = "warning"; + + // Simulation parameters + uint32_t sim_ssb_periodicity_ms = 10; + std::set sim_cell_pci; + + // RF parameters + std::string rf_device_name = "auto"; + std::string rf_device_args = "auto"; + std::string rf_log_level = "info"; + float rf_rx_gain_dB = 20.0f; + float rf_freq_offset_Hz = 0.0f; + + void set_ssb_from_band() + { + srsran::srsran_band_helper bands; + + // Deduce band number + uint16_t band = bands.get_band_from_dl_freq_Hz(base_carrier.dl_center_frequency_hz); + srsran_assert(band != UINT16_MAX, "Invalid band"); + + // Deduce point A in Hz + double pointA_Hz = + bands.get_abs_freq_point_a_from_center_freq(base_carrier.nof_prb, base_carrier.dl_center_frequency_hz); + + // Deduce DL center frequency ARFCN + uint32_t pointA_arfcn = bands.freq_to_nr_arfcn(pointA_Hz); + srsran_assert(pointA_arfcn != 0, "Invalid frequency"); + + // Select a valid SSB subcarrier spacing + ssb_scs = bands.get_ssb_scs(band); + + // Deduce SSB center frequency ARFCN + uint32_t ssb_arfcn = bands.get_abs_freq_ssb_arfcn(band, ssb_scs, pointA_arfcn); + srsran_assert(ssb_arfcn, "Invalid SSB center frequency"); + + duplex_mode = bands.get_duplex_mode(band); + ssb_pattern = bands.get_ssb_pattern(band, ssb_scs); + base_carrier.ssb_center_freq_hz = bands.nr_arfcn_to_freq(ssb_arfcn); + } +}; + +// shorten boost program options namespace +namespace bpo = boost::program_options; + +static void pci_list_parse_helper(std::string& list_str, std::set& list) +{ + if (list_str == "all") { + // Add all possible cells + for (int i = 0; i < 504; i++) { + list.insert(i); + } + } else if (list_str == "none") { + // Do nothing + } else if (not list_str.empty()) { + // Remove spaces from neightbour cell list + list_str = srsran::string_remove_char(list_str, ' '); + + // Add cell to known cells + srsran::string_parse_list(list_str, ',', list); + } +} + +int parse_args(int argc, char** argv, args_t& args) +{ + int ret = SRSRAN_SUCCESS; + + std::string simulation_cell_list = ""; + + bpo::options_description options("General options"); + bpo::options_description phy("Physical layer options"); + bpo::options_description stack("Stack options"); + bpo::options_description over_the_air("Mode 1: Over the air options (Default)"); + bpo::options_description simulation("Mode 2: Simulation options (enabled if simulation_cell_list is not empty)"); + + // clang-format off + over_the_air.add_options() + ("rf.device_name", bpo::value(&args.rf_device_name)->default_value(args.rf_device_name), "RF Device Name") + ("rf.device_args", bpo::value(&args.rf_device_args)->default_value(args.rf_device_args), "RF Device arguments") + ("rf.log_level", bpo::value(&args.rf_log_level)->default_value(args.rf_log_level), "RF Log level (none, warning, info, debug)") + ("rf.rx_gain", bpo::value(&args.rf_rx_gain_dB)->default_value(args.rf_rx_gain_dB), "RF Receiver gain in dB") + ("rf.freq_offset", bpo::value(&args.rf_freq_offset_Hz)->default_value(args.rf_freq_offset_Hz), "RF Frequency offset") + ; + + simulation.add_options() + ("sim.pci_list", bpo::value(&simulation_cell_list)->default_value(simulation_cell_list), "Comma separated PCI cell list to simulate") + ("sim.bw", bpo::value(&args.base_carrier.nof_prb)->default_value(args.base_carrier.nof_prb), "Carrier bandwidth in RB") + ("sim.ssb_period", bpo::value(&args.sim_ssb_periodicity_ms)->default_value(args.sim_ssb_periodicity_ms), "SSB period in ms") + ; + + phy.add_options() + ("phy.srate", bpo::value(&args.srate_hz)->default_value(args.srate_hz), "Sampling Rate in Hz") + ("phy.log.level", bpo::value(&args.phy_log_level)->default_value(args.phy_log_level), "Physical layer logging level") + ; + + stack.add_options() + ("stack.log.level", bpo::value(&args.stack_log_level)->default_value(args.stack_log_level), "Stack logging level") + ; + + options.add(over_the_air).add(simulation).add(phy).add(stack).add_options() + ("help,h", "Show this message") + ("duration", bpo::value(&args.duration_ms)->default_value(args.duration_ms), "Duration of the test in milli-seconds") + ("freq_dl", bpo::value(&args.base_carrier.dl_center_frequency_hz)->default_value(args.base_carrier.dl_center_frequency_hz), "Carrier center frequency in Hz") + ; + // clang-format on + + bpo::variables_map vm; + try { + bpo::store(bpo::command_line_parser(argc, argv).options(options).run(), vm); + bpo::notify(vm); + } catch (bpo::error& e) { + std::cerr << e.what() << std::endl; + ret = SRSRAN_ERROR; + } + + // help option was given or error - print usage and exit + if (vm.count("help") || ret) { + std::cout << "Usage: " << argv[0] << " [OPTIONS] config_file" << std::endl << std::endl; + std::cout << options << std::endl << std::endl; + ret = SRSRAN_ERROR; + } + + // Parse PCI lists + pci_list_parse_helper(simulation_cell_list, args.sim_cell_pci); + + args.set_ssb_from_band(); + + return ret; +} + +class dummy_ue +{ +private: + throttled_dummy_stack stack; + srsue::phy_nr_sa phy; + +public: + struct args_t { + srsue::phy_args_nr_t phy; + ue_dummy_stack::args_t stack; + }; + dummy_ue(const args_t& args, srsran::radio_interface_phy* radio) : stack(args.stack, phy), phy("PHY") + { + srsran_assert(phy.init(args.phy, &stack, radio), "Failed to initialise PHY"); + } + + bool start_cell_search(const srsue::phy_interface_stack_nr::cell_search_args_t& args) + { + return phy.start_cell_search(args); + } + + void run_tti() { stack.tick(); } + void stop() + { + // First transition PHY to IDLE + phy.reset_nr(); + + // Make sure PHY transitioned to IDLE + // ... + + // Stop stack, it will let PHY free run + stack.stop(); + + // Stop PHY + phy.stop(); + } +}; + +int main(int argc, char** argv) +{ + srsran_debug_handle_crash(argc, argv); + + // Parse Test arguments + args_t args; + srsran_assert(parse_args(argc, argv, args) == SRSRAN_SUCCESS, "Failed to parse arguments"); + + // Initialise logging infrastructure + srslog::init(); + + // Radio can be constructed from different options + std::shared_ptr radio = nullptr; + + // Build radio + if (not args.sim_cell_pci.empty()) { + // Create Radio as gNb emulator if the device RF name is not defined + gnb_rf_emulator::args_t gnb_args = {}; + gnb_args.srate_hz = args.srate_hz; + gnb_args.base_carrier = args.base_carrier; + gnb_args.ssb_pattern = args.ssb_pattern; + gnb_args.ssb_periodicity_ms = args.sim_ssb_periodicity_ms; + gnb_args.ssb_scs = args.ssb_scs; + gnb_args.duplex_mode = args.duplex_mode; + gnb_args.pci_list = args.sim_cell_pci; + + radio = std::make_shared(gnb_args); + } else { + // Create an actual radio based on RF + srsran::rf_args_t rf_args = {}; + rf_args.type = "multi"; + rf_args.log_level = args.rf_log_level; + rf_args.srate_hz = args.srate_hz; + rf_args.rx_gain = args.rf_rx_gain_dB; + rf_args.nof_carriers = 1; + rf_args.nof_antennas = 1; + rf_args.device_args = args.rf_device_args; + rf_args.device_name = args.rf_device_name; + rf_args.freq_offset = args.rf_freq_offset_Hz; + + // Instantiate + std::shared_ptr r = std::make_shared(); + srsran_assert(r->init(rf_args, nullptr) == SRSRAN_SUCCESS, "Failed Radio initialisation"); + + // Move to base pointer + radio = std::move(r); + + // Set sampling rate + radio->set_rx_srate(args.srate_hz); + + // Set DL center frequency + radio->set_rx_freq(0, args.base_carrier.dl_center_frequency_hz); + + // Set Rx gain + radio->set_rx_gain(args.rf_rx_gain_dB); + } + + // Create dummy UE + dummy_ue::args_t ue_args = {}; + ue_args.phy.log.phy_level = args.phy_log_level; + ue_args.stack.log_level = args.stack_log_level; + dummy_ue ue(ue_args, radio.get()); + + // Transition PHY to cell search + srsue::phy_nr_sa::cell_search_args_t cell_search_req = {}; + cell_search_req.srate_hz = args.srate_hz; + cell_search_req.center_freq_hz = args.base_carrier.dl_center_frequency_hz; + cell_search_req.ssb_freq_hz = args.base_carrier.ssb_center_freq_hz; + cell_search_req.ssb_scs = args.ssb_scs; + cell_search_req.ssb_pattern = args.ssb_pattern; + cell_search_req.duplex_mode = args.duplex_mode; + srsran_assert(ue.start_cell_search(cell_search_req), "Failed cell search start"); + + for (uint32_t i = 0; i < args.duration_ms; i++) { + ue.run_tti(); + } + + // Tear down UE + ue.stop(); + + // Stop Radio + radio->reset(); + + // Erase radio + radio = nullptr; + + return 0; +} \ No newline at end of file diff --git a/srsue/src/stack/mac_nr/demux_nr.cc b/srsue/src/stack/mac_nr/demux_nr.cc index 4620a9e53..e7ed4ae0c 100644 --- a/srsue/src/stack/mac_nr/demux_nr.cc +++ b/srsue/src/stack/mac_nr/demux_nr.cc @@ -36,41 +36,61 @@ int32_t demux_nr::init(rlc_interface_mac* rlc_) return SRSRAN_SUCCESS; } +uint64_t demux_nr::get_received_crueid() +{ + return received_crueid; +} + // Enqueues PDU and returns quickly void demux_nr::push_pdu(srsran::unique_byte_buffer_t pdu, uint32_t tti) { pdu_queue.push(std::move(pdu)); } +/* Demultiplexing of MAC PDU associated with a Temporal C-RNTI. The PDU will + * remain in buffer until demultiplex_pending_pdu() is called. + * This features is provided to enable the Random Access Procedure to decide + * whether the PDU shall pass to upper layers or not, which depends on the + * Contention Resolution result. + * + * Warning: this function does some processing here assuming ACK deadline is not an + * issue here because Temp C-RNTI messages have small payloads + */ +void demux_nr::push_pdu_temp_crnti(srsran::unique_byte_buffer_t pdu, uint32_t tti) +{ + received_crueid = 0; + handle_pdu(rx_pdu_tcrnti, std::move(pdu)); +} + void demux_nr::process_pdus() { while (not pdu_queue.empty()) { srsran::unique_byte_buffer_t pdu = pdu_queue.wait_pop(); - handle_pdu(std::move(pdu)); + handle_pdu(rx_pdu, std::move(pdu)); } } /// Handling of DLSCH PDUs only -void demux_nr::handle_pdu(srsran::unique_byte_buffer_t pdu) +void demux_nr::handle_pdu(srsran::mac_sch_pdu_nr& pdu_buffer, srsran::unique_byte_buffer_t pdu) { logger.debug(pdu->msg, pdu->N_bytes, "Handling MAC PDU (%d B)", pdu->N_bytes); - rx_pdu.init_rx(); - if (rx_pdu.unpack(pdu->msg, pdu->N_bytes) != SRSRAN_SUCCESS) { + pdu_buffer.init_rx(); + if (pdu_buffer.unpack(pdu->msg, pdu->N_bytes) != SRSRAN_SUCCESS) { return; } if (logger.info.enabled()) { fmt::memory_buffer str_buffer; - rx_pdu.to_string(str_buffer); + pdu_buffer.to_string(str_buffer); logger.info("%s", srsran::to_c_str(str_buffer)); } - for (uint32_t i = 0; i < rx_pdu.get_num_subpdus(); ++i) { - srsran::mac_sch_subpdu_nr subpdu = rx_pdu.get_subpdu(i); + for (uint32_t i = 0; i < pdu_buffer.get_num_subpdus(); ++i) { + srsran::mac_sch_subpdu_nr subpdu = pdu_buffer.get_subpdu(i); logger.debug("Handling subPDU %d/%d: rnti=0x%x lcid=%d, sdu_len=%d", i + 1, - rx_pdu.get_num_subpdus(), + pdu_buffer.get_num_subpdus(), subpdu.get_c_rnti(), subpdu.get_lcid(), subpdu.get_sdu_length()); @@ -84,7 +104,8 @@ void demux_nr::handle_pdu(srsran::unique_byte_buffer_t pdu) logger.info("Timing Advance CE not implemented."); break; case srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::CON_RES_ID: - logger.info("Contention Resolution CE not implemented."); + received_crueid = subpdu.get_ue_con_res_id_ce_packed(); + logger.info("Received Contention Resolution ID 0x%lx\n", subpdu.get_ue_con_res_id_ce_packed()); break; default: if (subpdu.is_sdu()) { diff --git a/srsue/src/stack/mac_nr/dl_harq_nr.cc b/srsue/src/stack/mac_nr/dl_harq_nr.cc index ed4e86efb..829c44aa7 100644 --- a/srsue/src/stack/mac_nr/dl_harq_nr.cc +++ b/srsue/src/stack/mac_nr/dl_harq_nr.cc @@ -234,6 +234,8 @@ void dl_harq_entity_nr::dl_harq_process_nr::tb_decoded(const mac_nr_grant_dl_t& if (grant.rnti == harq_entity->mac->get_temp_crnti()) { logger.debug("Delivering PDU=%d bytes to Dissassemble and Demux unit (Temporal C-RNTI) not implemented", grant.tbs); + harq_entity->demux_unit->push_pdu_temp_crnti(std::move(result.payload), grant.tti); + result.ack = harq_entity->mac->received_contention_id(harq_entity->demux_unit->get_received_crueid()); } else { logger.debug("Delivering PDU=%d bytes to Dissassemble and Demux unit", grant.tbs); harq_entity->demux_unit->push_pdu(std::move(result.payload), grant.tti); diff --git a/srsue/src/stack/mac_nr/mac_nr.cc b/srsue/src/stack/mac_nr/mac_nr.cc index d0cbc6695..bd3ad6af5 100644 --- a/srsue/src/stack/mac_nr/mac_nr.cc +++ b/srsue/src/stack/mac_nr/mac_nr.cc @@ -330,6 +330,11 @@ void mac_nr::tb_decoded(const uint32_t cc_idx, const mac_nr_grant_dl_t& grant, t dl_harq.at(cc_idx)->tb_decoded(grant, std::move(result)); } + + // If proc ra is in contention resolution (RA connection request procedure) + if (proc_ra.is_contention_resolution() && grant.rnti == get_temp_crnti()) { + proc_ra.received_contention_resolution(contention_res_successful); + } } void mac_nr::new_grant_ul(const uint32_t cc_idx, const mac_nr_grant_ul_t& grant, tb_action_ul_t* action) @@ -347,7 +352,7 @@ void mac_nr::new_grant_ul(const uint32_t cc_idx, const mac_nr_grant_ul_t& grant, // Clear UL action *action = {}; - // if proc ra is in contention resolution and c_rnti == grant.c_rnti resolve contention resolution + // if proc ra is in contention resolution and c_rnti == grant.c_rnti (RA connection resume procedure) if (proc_ra.is_contention_resolution() && grant.rnti == get_crnti()) { proc_ra.pdcch_to_crnti(); } @@ -441,7 +446,7 @@ int mac_nr::set_config(const srsran::sr_cfg_nr_t& sr_cfg) return proc_sr.set_config(sr_cfg); } -void mac_nr::set_config(const srsran::rach_nr_cfg_t& rach_cfg) +void mac_nr::set_config(const srsran::rach_cfg_nr_t& rach_cfg) { proc_ra.set_config(rach_cfg); } @@ -534,9 +539,10 @@ void mac_nr::process_pdus() demux.process_pdus(); } -uint64_t mac_nr::get_contention_id() +bool mac_nr::received_contention_id(uint64_t rx_contention_id) { - return 0xdeadbeef; // TODO when rebased on PR + contention_res_successful = rntis.get_contention_id() == rx_contention_id; + return contention_res_successful; } // TODO same function as for mac_eutra diff --git a/srsue/src/stack/mac_nr/mux_nr.cc b/srsue/src/stack/mac_nr/mux_nr.cc index 18da2f802..b6c24f664 100644 --- a/srsue/src/stack/mac_nr/mux_nr.cc +++ b/srsue/src/stack/mac_nr/mux_nr.cc @@ -77,59 +77,61 @@ srsran::unique_byte_buffer_t mux_nr::get_pdu(uint32_t max_pdu_len) logger.debug("Building new MAC PDU (%d B)", max_pdu_len); tx_pdu.init_tx(phy_tx_pdu.get(), max_pdu_len, true); - if (msg3_is_pending()) { - // only C-RNTI or CCCH SDU can be transmitted in Msg3 - if (mac.has_crnti()) { - tx_pdu.add_crnti_ce(mac.get_crnti()); - } - // TODO: add CCCH check + if (msg3_is_pending() && mac.has_crnti()) { + tx_pdu.add_crnti_ce(mac.get_crnti()); msg3_transmitted(); - } else { - // Pack normal UL data PDU - int32_t remaining_len = tx_pdu.get_remaing_len(); // local variable to reserve space for CEs + } - if (add_bsr_ce == sbsr_ce) { - // reserve space for SBSR - remaining_len -= 2; - } + // Pack normal UL data PDU + int32_t remaining_len = tx_pdu.get_remaing_len(); // local variable to reserve space for CEs - // First add MAC SDUs - for (const auto& lc : logical_channels) { - // TODO: Add proper priority handling - logger.debug("Adding SDUs for LCID=%d (max %d B)", lc.lcid, remaining_len); - while (remaining_len >= MIN_RLC_PDU_LEN) { - // read RLC PDU - rlc_buff->clear(); - uint8_t* rd = rlc_buff->msg; + if (add_bsr_ce == sbsr_ce) { + // reserve space for SBSR + remaining_len -= 2; + } - // Determine space for RLC - int32_t subpdu_header_len = (remaining_len >= srsran::mac_sch_subpdu_nr::MAC_SUBHEADER_LEN_THRESHOLD ? 3 : 2); + // First add MAC SDUs + for (const auto& lc : logical_channels) { + // TODO: Add proper priority handling + logger.debug("Adding SDUs for LCID=%d (max %d B)", lc.lcid, remaining_len); + while (remaining_len >= MIN_RLC_PDU_LEN) { + // read RLC PDU + rlc_buff->clear(); + uint8_t* rd = rlc_buff->msg; - // Read PDU from RLC (account for subPDU header) - int pdu_len = rlc->read_pdu(lc.lcid, rd, remaining_len - subpdu_header_len); + // Determine space for RLC + int32_t subpdu_header_len = (remaining_len >= srsran::mac_sch_subpdu_nr::MAC_SUBHEADER_LEN_THRESHOLD ? 3 : 2); - if (pdu_len > remaining_len) { - logger.error("Can't add SDU of %d B. Available space %d B", pdu_len, remaining_len); - break; - } else { - // Add SDU if RLC has something to tx - if (pdu_len > 0) { - rlc_buff->N_bytes = pdu_len; - logger.debug(rlc_buff->msg, rlc_buff->N_bytes, "Read %d B from RLC", rlc_buff->N_bytes); + // Read PDU from RLC (account for subPDU header) + int pdu_len = rlc->read_pdu(lc.lcid, rd, remaining_len - subpdu_header_len); - // 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"); - break; - } - } else { - // couldn't read PDU from RLC + if (pdu_len > remaining_len) { + logger.error("Can't add SDU of %d B. Available space %d B", pdu_len, remaining_len); + break; + } else { + // Add SDU if RLC has something to tx + if (pdu_len > 0) { + rlc_buff->N_bytes = pdu_len; + logger.debug(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"); break; } - remaining_len -= (pdu_len + subpdu_header_len); - logger.debug("%d B remaining PDU", remaining_len); + if (lc.lcid == 0 && msg3_is_pending()) { + // TODO: + msg3_transmitted(); + } + + } else { + // couldn't read PDU from RLC + break; } + + remaining_len -= (pdu_len + subpdu_header_len); + logger.debug("%d B remaining PDU", remaining_len); } } } diff --git a/srsue/src/stack/mac_nr/proc_ra_nr.cc b/srsue/src/stack/mac_nr/proc_ra_nr.cc index 5baebc9c4..2cf3352a0 100644 --- a/srsue/src/stack/mac_nr/proc_ra_nr.cc +++ b/srsue/src/stack/mac_nr/proc_ra_nr.cc @@ -53,7 +53,7 @@ void proc_ra_nr::init(phy_interface_mac_nr* phy_, srsran::ext_task_sched_handle* } /* Sets a new configuration. The configuration is applied by initialization() function */ -void proc_ra_nr::set_config(const srsran::rach_nr_cfg_t& rach_cfg_) +void proc_ra_nr::set_config(const srsran::rach_cfg_nr_t& rach_cfg_) { if (state != IDLE) { logger.warning("Wrong state for ra reponse reception %s (expected state %s)", @@ -136,6 +136,12 @@ uint16_t proc_ra_nr::get_temp_crnti() return temp_crnti; } +void proc_ra_nr::received_contention_resolution(bool is_successful) +{ + std::lock_guard lock(mutex); + ra_contention_resolution(is_successful); +} + void proc_ra_nr::timer_expired(uint32_t timer_id) { if (prach_send_timer.id() == timer_id) { @@ -221,8 +227,11 @@ void proc_ra_nr::ra_response_reception(const mac_interface_phy_nr::tb_action_dl_ logger.debug("PROC RA NR: Setting UL grant and prepare Msg3"); temp_crnti = subpdu.get_temp_crnti(); + // Save transmitted C-RNTI (if any) + transmitted_crnti = mac.get_crnti(); + // Set Temporary-C-RNTI if provided, otherwise C-RNTI is ok - phy->set_ul_grant(tb.rx_slot_idx, subpdu.get_ul_grant(), temp_crnti, srsran_rnti_type_ra); + phy->set_rar_grant(tb.rx_slot_idx, subpdu.get_ul_grant(), temp_crnti, srsran_rnti_type_ra); // reset all parameters that are used before rar rar_rnti = SRSRAN_INVALID_RNTI; @@ -246,7 +255,7 @@ void proc_ra_nr::ra_response_reception(const mac_interface_phy_nr::tb_action_dl_ // TS 38.321 Section 5.1.5 2 ways to resolve contention resolution // if the C-RNTI MAC CE was included in Msg3: (only this one is implemented) -void proc_ra_nr::ra_contention_resolution() +void proc_ra_nr::ra_contention_resolution(bool received_con_res_matches_ue_id) { if (state != WAITING_FOR_CONTENTION_RESOLUTION) { logger.warning( @@ -255,8 +264,12 @@ void proc_ra_nr::ra_contention_resolution() srsran::enum_to_text(state_str_nr, (uint32_t)ra_state_t::MAX_RA_STATES, WAITING_FOR_CONTENTION_RESOLUTION)); return; } - if (started_by == initiators_t::RRC || started_by == initiators_t::MAC) { - logger.info("PDCCH to C-RNTI received with a new UL grant of transmission"); + if (started_by == initiators_t::RRC || started_by == initiators_t::MAC || received_con_res_matches_ue_id) { + if (received_con_res_matches_ue_id) { + logger.info("Received CONRES ID matches transmitted UE ID\n"); + } else { + logger.info("PDCCH to C-RNTI received with a new UL grant of transmission"); + } contention_resolution_timer.stop(); state = WAITING_FOR_COMPLETION; ra_completion(); @@ -265,22 +278,8 @@ void proc_ra_nr::ra_contention_resolution() } } -// or else if the CCCH SDU was included in Msg3 and the PDCCH transmission is addressed to its TEMPORARY_C-RNTI: -void proc_ra_nr::ra_contention_resolution(uint64_t rx_contention_id) -{ - if (state != WAITING_FOR_CONTENTION_RESOLUTION) { - logger.warning( - "Wrong state for ra contention resolution by phy %s (expected state %s)", - srsran::enum_to_text(state_str_nr, (uint32_t)ra_state_t::MAX_RA_STATES, state), - srsran::enum_to_text(state_str_nr, (uint32_t)ra_state_t::MAX_RA_STATES, WAITING_FOR_CONTENTION_RESOLUTION)); - return; - } - // TODO -} - void proc_ra_nr::ra_completion() { - std::lock_guard lock(mutex); if (state != WAITING_FOR_COMPLETION) { logger.warning("Wrong state for ra completion by phy %s (expected state %s)", srsran::enum_to_text(state_str_nr, (uint32_t)ra_state_t::MAX_RA_STATES, state), @@ -289,6 +288,9 @@ void proc_ra_nr::ra_completion() } 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); + if (!transmitted_crnti) { + mac.set_crnti(temp_crnti); + } temp_crnti = SRSRAN_INVALID_RNTI; mac.rrc_ra_completed(); reset(); @@ -381,7 +383,7 @@ void proc_ra_nr::handle_rar_pdu(mac_interface_phy_nr::tb_action_dl_result_t& res // Called from PHY thread, defer actions therefore. void proc_ra_nr::pdcch_to_crnti() { - task_queue.push([this]() { ra_contention_resolution(); }); + task_queue.push([this]() { ra_contention_resolution(false); }); } bool proc_ra_nr::is_contention_resolution() @@ -396,6 +398,7 @@ void proc_ra_nr::reset() prach_send_timer.stop(); rar_timeout_timer.stop(); contention_resolution_timer.stop(); + transmitted_crnti = SRSRAN_INVALID_RNTI; } } // namespace srsue diff --git a/srsue/src/stack/mac_nr/test/mac_nr_test.cc b/srsue/src/stack/mac_nr/test/mac_nr_test.cc index 373d8ac6e..9a7017a80 100644 --- a/srsue/src/stack/mac_nr/test/mac_nr_test.cc +++ b/srsue/src/stack/mac_nr/test/mac_nr_test.cc @@ -44,11 +44,10 @@ public: 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(uint32_t rar_slot_idx, - std::array, - uint16_t rnti, - srsran_rnti_type_t rnti_type) override + int set_rar_grant(uint32_t rar_slot_idx, + std::array, + uint16_t rnti, + srsran_rnti_type_t rnti_type) override { return 0; } diff --git a/srsue/src/stack/mac_nr/test/proc_ra_nr_test.cc b/srsue/src/stack/mac_nr/test/proc_ra_nr_test.cc index 38615c539..8c849fa88 100644 --- a/srsue/src/stack/mac_nr/test/proc_ra_nr_test.cc +++ b/srsue/src/stack/mac_nr/test/proc_ra_nr_test.cc @@ -38,14 +38,6 @@ public: 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(uint32_t rar_slot_idx, - 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_) { @@ -56,6 +48,14 @@ public: bool has_valid_sr_resource(uint32_t sr_id) override { return false; } void clear_pending_grants() override {} + int set_rar_grant(uint32_t rar_slot_idx, + std::array packed_ul_grant, + uint16_t rnti, + srsran_rnti_type_t rnti_type) override + { + return -1; + } + private: uint32_t prach_occasion = 0; uint32_t preamble_index = 0; @@ -100,7 +100,7 @@ int proc_ra_normal_test() 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; + srsran::rach_cfg_nr_t rach_cfg; rach_cfg.powerRampingStep = 4; rach_cfg.prach_ConfigurationIndex = 16; rach_cfg.PreambleReceivedTargetPower = -110; @@ -162,7 +162,7 @@ int proc_ra_timeout_test() 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; + srsran::rach_cfg_nr_t rach_cfg; rach_cfg.powerRampingStep = 4; rach_cfg.prach_ConfigurationIndex = 16; rach_cfg.PreambleReceivedTargetPower = -110; diff --git a/srsue/src/stack/mac_nr/test/proc_sr_nr_test.cc b/srsue/src/stack/mac_nr/test/proc_sr_nr_test.cc index 8d9be3a63..afbd1aafe 100644 --- a/srsue/src/stack/mac_nr/test/proc_sr_nr_test.cc +++ b/srsue/src/stack/mac_nr/test/proc_sr_nr_test.cc @@ -39,11 +39,10 @@ public: 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(uint32_t rar_slot_idx, - std::array, - uint16_t rnti, - srsran_rnti_type_t rnti_type) override + int set_rar_grant(uint32_t rar_slot_idx, + std::array, + uint16_t rnti, + srsran_rnti_type_t rnti_type) override { return 0; } diff --git a/srsue/src/stack/rrc/rrc.cc b/srsue/src/stack/rrc/rrc.cc index 2c5ce575c..cd753016a 100644 --- a/srsue/src/stack/rrc/rrc.cc +++ b/srsue/src/stack/rrc/rrc.cc @@ -788,13 +788,10 @@ bool rrc::nr_reconfiguration_proc(const rrc_conn_recfg_r8_ies_s& rx_recfg, bool* return true; } - bool endc_release_and_add_r15 = false; - bool nr_secondary_cell_group_cfg_r15_present = false; - asn1::dyn_octstring nr_secondary_cell_group_cfg_r15; - bool sk_counter_r15_present = false; - uint32_t sk_counter_r15 = 0; - bool nr_radio_bearer_cfg1_r15_present = false; - asn1::dyn_octstring nr_radio_bearer_cfg1_r15; + bool endc_release_and_add_r15 = false; + + asn1::rrc_nr::rrc_recfg_s rrc_nr_reconf = {}; + rrc_nr_reconf.crit_exts.set_rrc_recfg(); switch (rrc_conn_recfg_v1510_ies->nr_cfg_r15.type()) { case setup_opts::options::release: @@ -803,8 +800,29 @@ bool rrc::nr_reconfiguration_proc(const rrc_conn_recfg_r8_ies_s& rx_recfg, bool* case setup_opts::options::setup: endc_release_and_add_r15 = rrc_conn_recfg_v1510_ies->nr_cfg_r15.setup().endc_release_and_add_r15; if (rrc_conn_recfg_v1510_ies->nr_cfg_r15.setup().nr_secondary_cell_group_cfg_r15_present) { - nr_secondary_cell_group_cfg_r15_present = true; - nr_secondary_cell_group_cfg_r15 = rrc_conn_recfg_v1510_ies->nr_cfg_r15.setup().nr_secondary_cell_group_cfg_r15; + asn1::cbit_ref bref0(rrc_conn_recfg_v1510_ies->nr_cfg_r15.setup().nr_secondary_cell_group_cfg_r15.data(), + rrc_conn_recfg_v1510_ies->nr_cfg_r15.setup().nr_secondary_cell_group_cfg_r15.size()); + + asn1::rrc_nr::rrc_recfg_s secondary_cell_group_r15; + if (secondary_cell_group_r15.unpack(bref0) != SRSASN_SUCCESS) { + logger.error("Could not unpack secondary cell group r15."); + return false; + } + + if (secondary_cell_group_r15.crit_exts.rrc_recfg().secondary_cell_group_present) { + asn1::cbit_ref bref1(secondary_cell_group_r15.crit_exts.rrc_recfg().secondary_cell_group.data(), + secondary_cell_group_r15.crit_exts.rrc_recfg().secondary_cell_group.size()); + + asn1::rrc_nr::cell_group_cfg_s cell_group_cfg; + if (cell_group_cfg.unpack(bref1) != SRSASN_SUCCESS) { + logger.error("Could not unpack secondary cell group config."); + return false; + } + + rrc_nr_reconf.crit_exts.rrc_recfg().secondary_cell_group_present = true; + rrc_nr_reconf.crit_exts.rrc_recfg().secondary_cell_group = + secondary_cell_group_r15.crit_exts.rrc_recfg().secondary_cell_group; + } } break; default: @@ -812,22 +830,29 @@ bool rrc::nr_reconfiguration_proc(const rrc_conn_recfg_r8_ies_s& rx_recfg, bool* break; } if (rrc_conn_recfg_v1510_ies->sk_counter_r15_present) { - sk_counter_r15_present = true; - sk_counter_r15 = rrc_conn_recfg_v1510_ies->sk_counter_r15; + rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext_present = true; + rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.non_crit_ext_present = true; + rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.non_crit_ext.non_crit_ext_present = true; + rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.non_crit_ext.non_crit_ext.sk_counter_present = true; + rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.non_crit_ext.non_crit_ext.sk_counter = + rrc_conn_recfg_v1510_ies->sk_counter_r15; } if (rrc_conn_recfg_v1510_ies->nr_radio_bearer_cfg1_r15_present) { - nr_radio_bearer_cfg1_r15_present = true; - nr_radio_bearer_cfg1_r15 = rrc_conn_recfg_v1510_ies->nr_radio_bearer_cfg1_r15; + rrc_nr_reconf.crit_exts.rrc_recfg().radio_bearer_cfg_present = true; + asn1::rrc_nr::radio_bearer_cfg_s radio_bearer_conf = {}; + asn1::cbit_ref bref(rrc_conn_recfg_v1510_ies->nr_radio_bearer_cfg1_r15.data(), + rrc_conn_recfg_v1510_ies->nr_radio_bearer_cfg1_r15.size()); + if (radio_bearer_conf.unpack(bref) != SRSASN_SUCCESS) { + logger.error("Could not unpack radio bearer config."); + return false; + } + + rrc_nr_reconf.crit_exts.rrc_recfg().radio_bearer_cfg = radio_bearer_conf; } *has_5g_nr_reconfig = true; - return rrc_nr->rrc_reconfiguration(endc_release_and_add_r15, - nr_secondary_cell_group_cfg_r15_present, - nr_secondary_cell_group_cfg_r15, - sk_counter_r15_present, - sk_counter_r15, - nr_radio_bearer_cfg1_r15_present, - nr_radio_bearer_cfg1_r15); + + return rrc_nr->rrc_reconfiguration(endc_release_and_add_r15, rrc_nr_reconf); } /******************************************************************************* * diff --git a/srsue/src/stack/rrc/test/rrc_meas_test.cc b/srsue/src/stack/rrc/test/rrc_meas_test.cc index 19efcbeea..10c444a85 100644 --- a/srsue/src/stack/rrc/test/rrc_meas_test.cc +++ b/srsue/src/stack/rrc/test/rrc_meas_test.cc @@ -180,13 +180,7 @@ public: int get_nr_capabilities(srsran::byte_buffer_t* nr_cap) override { return SRSRAN_SUCCESS; }; void phy_set_cells_to_meas(uint32_t carrier_freq_r15) override{}; void phy_meas_stop() override{}; - bool rrc_reconfiguration(bool endc_release_and_add_r15, - bool nr_secondary_cell_group_cfg_r15_present, - asn1::dyn_octstring nr_secondary_cell_group_cfg_r15, - bool sk_counter_r15_present, - uint32_t sk_counter_r15, - bool nr_radio_bearer_cfg1_r15_present, - asn1::dyn_octstring nr_radio_bearer_cfg1_r15) override + bool rrc_reconfiguration(bool endc_release_and_add_r15, const asn1::rrc_nr::rrc_recfg_s& rrc_nr_reconf) override { return false; } diff --git a/srsue/src/stack/rrc_nr/rrc_nr.cc b/srsue/src/stack/rrc_nr/rrc_nr.cc index 22894a59f..a8493d8d4 100644 --- a/srsue/src/stack/rrc_nr/rrc_nr.cc +++ b/srsue/src/stack/rrc_nr/rrc_nr.cc @@ -21,6 +21,7 @@ #include "srsue/hdr/stack/rrc_nr/rrc_nr.h" #include "srsran/common/band_helper.h" +#include "srsran/common/phy_cfg_nr_default.h" #include "srsran/common/security.h" #include "srsran/common/standard_streams.h" #include "srsran/interfaces/ue_pdcp_interfaces.h" @@ -36,7 +37,13 @@ namespace srsue { const char* rrc_nr::rrc_nr_state_text[] = {"IDLE", "CONNECTED", "CONNECTED-INACTIVE"}; rrc_nr::rrc_nr(srsran::task_sched_handle task_sched_) : - logger(srslog::fetch_basic_logger("RRC-NR")), task_sched(task_sched_), conn_recfg_proc(this), meas_cells(task_sched_) + logger(srslog::fetch_basic_logger("RRC-NR")), + task_sched(task_sched_), + conn_recfg_proc(*this), + conn_setup_proc(*this), + setup_req_proc(*this), + cell_selector(*this), + meas_cells(task_sched_) {} rrc_nr::~rrc_nr() = default; @@ -62,6 +69,8 @@ int rrc_nr::init(phy_interface_rrc_nr* phy_, stack = stack_; args = args_; + plmn_is_selected = true; // short-cut SA test + running = true; sim_measurement_timer = task_sched.get_unique_timer(); return SRSRAN_SUCCESS; @@ -207,9 +216,187 @@ void rrc_nr::out_of_sync() {} void rrc_nr::run_tti(uint32_t tti) {} // PDCP interface -void rrc_nr::write_pdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) {} +void rrc_nr::write_pdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) +{ + printf("RRC received PDU\n"); + logger.debug("RX PDU, LCID: %d", lcid); + switch (static_cast(lcid)) { + case nr_srb::srb0: + decode_dl_ccch(std::move(pdu)); + break; + case nr_srb::srb1: + case nr_srb::srb2: + decode_dl_dcch(lcid, std::move(pdu)); + break; + default: + logger.error("RX PDU with invalid bearer id: %d", lcid); + break; + } +} + +void rrc_nr::decode_dl_ccch(unique_byte_buffer_t pdu) +{ + asn1::cbit_ref bref(pdu->msg, pdu->N_bytes); + asn1::rrc_nr::dl_ccch_msg_s dl_ccch_msg; + if (dl_ccch_msg.unpack(bref) != asn1::SRSASN_SUCCESS or + dl_ccch_msg.msg.type().value != dl_ccch_msg_type_c::types_opts::c1) { + logger.error(pdu->msg, pdu->N_bytes, "Failed to unpack DL-CCCH message (%d B)", pdu->N_bytes); + return; + } + log_rrc_message( + get_rb_name(srb_to_lcid(nr_srb::srb0)), Rx, pdu.get(), dl_ccch_msg, dl_ccch_msg.msg.c1().type().to_string()); + + dl_ccch_msg_type_c::c1_c_* c1 = &dl_ccch_msg.msg.c1(); + switch (dl_ccch_msg.msg.c1().type().value) { + // case dl_ccch_msg_type_c::c1_c_::types::rrc_reject: { + // // 5.3.3.8 + // rrc_conn_reject_r8_ies_s* reject_r8 = &c1->rrc_reject().crit_exts.c1().rrc_conn_reject_r8(); + // logger.info("Received ConnectionReject. Wait time: %d", reject_r8->wait_time); + // srsran::console("Received ConnectionReject. Wait time: %d\n", reject_r8->wait_time); + + // t300.stop(); + + // if (reject_r8->wait_time) { + // nas->set_barring(srsran::barring_t::all); + // t302.set(reject_r8->wait_time * 1000, [this](uint32_t tid) { timer_expired(tid); }); + // t302.run(); + // } else { + // // Perform the actions upon expiry of T302 if wait time is zero + // nas->set_barring(srsran::barring_t::none); + // start_go_idle(); + // } + // } break; + case dl_ccch_msg_type_c::c1_c_::types::rrc_setup: { + transaction_id = c1->rrc_setup().rrc_transaction_id; + rrc_setup_s rrc_setup_copy = c1->rrc_setup(); + task_sched.defer_task([this, rrc_setup_copy]() { handle_rrc_setup(rrc_setup_copy); }); + break; + } + + default: + logger.error("The provided DL-CCCH message type is not recognized"); + break; + } +} + +void rrc_nr::decode_dl_dcch(uint32_t lcid, unique_byte_buffer_t pdu) +{ + asn1::cbit_ref bref(pdu->msg, pdu->N_bytes); + asn1::rrc_nr::dl_dcch_msg_s dl_dcch_msg; + if (dl_dcch_msg.unpack(bref) != asn1::SRSASN_SUCCESS or + dl_dcch_msg.msg.type().value != dl_dcch_msg_type_c::types_opts::c1) { + logger.error(pdu->msg, pdu->N_bytes, "Failed to unpack DL-DCCH message (%d B)", pdu->N_bytes); + return; + } + log_rrc_message(get_rb_name(lcid), Rx, pdu.get(), dl_dcch_msg, dl_dcch_msg.msg.c1().type().to_string()); +} + void rrc_nr::write_pdu_bcch_bch(srsran::unique_byte_buffer_t pdu) {} -void rrc_nr::write_pdu_bcch_dlsch(srsran::unique_byte_buffer_t pdu) {} +void rrc_nr::write_pdu_bcch_dlsch(srsran::unique_byte_buffer_t pdu) +{ + decode_pdu_bcch_dlsch(std::move(pdu)); +} + +void rrc_nr::decode_pdu_bcch_dlsch(srsran::unique_byte_buffer_t pdu) +{ + // Stop BCCH search after successful reception of 1 BCCH block + // mac->bcch_stop_rx(); + + bcch_dl_sch_msg_s dlsch_msg; + asn1::cbit_ref dlsch_bref(pdu->msg, pdu->N_bytes); + asn1::SRSASN_CODE err = dlsch_msg.unpack(dlsch_bref); + + if (err != asn1::SRSASN_SUCCESS or dlsch_msg.msg.type().value != bcch_dl_sch_msg_type_c::types_opts::c1) { + logger.error(pdu->msg, pdu->N_bytes, "Could not unpack BCCH DL-SCH message (%d B).", pdu->N_bytes); + return; + } + + log_rrc_message("BCCH-DLSCH", Rx, pdu.get(), dlsch_msg, dlsch_msg.msg.c1().type().to_string()); + + if (dlsch_msg.msg.c1().type() == bcch_dl_sch_msg_type_c::c1_c_::types::sib_type1) { + logger.info("Processing SIB1 (1/1)"); + handle_sib1(dlsch_msg.msg.c1().sib_type1()); + } +} + +void rrc_nr::handle_sib1(const sib1_s& sib1) +{ + logger.info("SIB1 received, CellID=%d", meas_cells.serving_cell().get_cell_id() & 0xfff); + + // clang-format off + // unhandled fields: + // - cellSelectionInfo + // - cellAccessRelatedInfo + // - connEstFailureControl + // - servingCellConfigCommon: + // - downlinkConfigCommon.frequencyInfoDL.frequencyBandList + // - downlinkConfigCommon.frequencyInfoDL.offsetToPointA + // - downlinkConfigCommon.initialDownlinkBWP.genericParameters + // - downlinkConfigCommon.initialDownlinkBWP.pdcch-ConfigCommon.commonSearchSpaceList.searchSpaceSIB1 + // - downlinkConfigCommon.initialDownlinkBWP.pdcch-ConfigCommon.commonSearchSpaceList.search_space_other_sys_info + // - downlinkConfigCommon.initialDownlinkBWP.pdcch-ConfigCommon.commonSearchSpaceList.paging_search_space + // - downlinkConfigCommon.bcch-Config + // - downlinkConfigCommon.pcch-Config + // - uplinkConfigCommon.frequencyInfoUL.frequencyBandList + // - uplinkConfigCommon.frequencyInfoUL.p_max + // - uplinkConfigCommon.initialUplinkBWP.genericParameters + // - uplinkConfigCommon.initialUplinkBWP.rach-ConfigCommon.rach-ConfigGeneric.msg1-FDM + // - uplinkConfigCommon.initialUplinkBWP.rach-ConfigCommon.ssb_per_rach_occasion_and_cb_preambs_per_ssb + // - uplinkConfigCommon.initialUplinkBWP.rach-ConfigCommon.restricted_set_cfg + // - uplinkConfigCommon.initialUplinkBWP.pusch-ConfigCommon.pusch-TimeDomainResourceAllocationList.p0-NominalWithGrant + // - ss-PBCH-BlockPower + // - ue-TimersAndConstants + // clang-format on + + // Apply RACH and timeAlginmentTimer configuration + mac_cfg_nr_t mac_cfg = {}; + make_mac_rach_cfg(sib1.serving_cell_cfg_common.ul_cfg_common.init_ul_bwp.rach_cfg_common.setup(), &mac_cfg.rach_cfg); + mac_cfg.time_alignment_timer = sib1.serving_cell_cfg_common.ul_cfg_common.time_align_timer_common.to_number(); + + mac->set_config(mac_cfg.rach_cfg); + + // Apply PDSCH Config Common + if (sib1.serving_cell_cfg_common.dl_cfg_common.init_dl_bwp.pdsch_cfg_common.setup() + .pdsch_time_domain_alloc_list_present) { + if (not fill_phy_pdsch_cfg_common(sib1.serving_cell_cfg_common.dl_cfg_common.init_dl_bwp.pdsch_cfg_common.setup(), + &phy_cfg.pdsch)) { + logger.warning("Could not set PDSCH config."); + } + } + + // Apply PUSCH Config Common + if (not fill_phy_pusch_cfg_common(sib1.serving_cell_cfg_common.ul_cfg_common.init_ul_bwp.pusch_cfg_common.setup(), + &phy_cfg.pusch)) { + logger.warning("Could not set PUSCH config."); + } + + // Apply PUCCH Config Common + fill_phy_pucch_cfg_common(sib1.serving_cell_cfg_common.ul_cfg_common.init_ul_bwp.pucch_cfg_common.setup(), + &phy_cfg.pucch.common); + + // Apply RACH Config Common + if (not make_phy_rach_cfg(sib1.serving_cell_cfg_common.ul_cfg_common.init_ul_bwp.rach_cfg_common.setup(), + &phy_cfg.prach)) { + logger.warning("Could not set phy rach config."); + return; + } + + // Apply PDCCH Config Common + fill_phy_pdcch_cfg_common(sib1.serving_cell_cfg_common.dl_cfg_common.init_dl_bwp.pdcch_cfg_common.setup(), + &phy_cfg.pdcch); + + // Apply Carrier Config + fill_phy_carrier_cfg(sib1.serving_cell_cfg_common, &phy_cfg.carrier); + + // Apply SSB Config + fill_phy_ssb_cfg(sib1.serving_cell_cfg_common, &phy_cfg.ssb); + + if (not phy->set_config(phy_cfg)) { + logger.warning("Could not set phy config."); + return; + } +} + void rrc_nr::write_pdu_pcch(srsran::unique_byte_buffer_t pdu) {} void rrc_nr::write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) {} void rrc_nr::notify_pdcp_integrity_error(uint32_t lcid) {} @@ -230,8 +417,100 @@ bool rrc_nr::is_connected() return false; } -int rrc_nr::connection_request(srsran::nr_establishment_cause_t cause, srsran::unique_byte_buffer_t sdu) +int rrc_nr::connection_request(srsran::nr_establishment_cause_t cause, srsran::unique_byte_buffer_t dedicated_info_nas_) { + // TODO: + // Assume cell has been found and SSB with MIB has been decoded + srsran::phy_cfg_nr_default_t::reference_cfg_t cfg = {}; + cfg.carrier = srsran::phy_cfg_nr_default_t::reference_cfg_t::R_CARRIER_CUSTOM_10MHZ; + cfg.duplex = srsran::phy_cfg_nr_default_t::reference_cfg_t::R_DUPLEX_FDD; + phy_cfg = srsran::phy_cfg_nr_default_t{srsran::phy_cfg_nr_default_t::reference_cfg_t{cfg}}; + + // Carrier configuration + phy_cfg.ssb.periodicity_ms = 10; + phy_cfg.carrier.ssb_center_freq_hz = 1842.05e6; + phy_cfg.carrier.dl_center_frequency_hz = 1842.5e6; + phy_cfg.carrier.ul_center_frequency_hz = 1747.5e6; + + // PRACH configuration + phy_cfg.prach.num_ra_preambles = 8; + phy_cfg.prach.config_idx = 0; + phy_cfg.prach.root_seq_idx = 1; + phy_cfg.prach.zero_corr_zone = 0; + phy_cfg.prach.is_nr = true; + phy_cfg.prach.freq_offset = 1; + + srsran::rach_cfg_nr_t rach_cfg = {}; + rach_cfg.prach_ConfigurationIndex = 0; + rach_cfg.preambleTransMax = 7; + rach_cfg.ra_responseWindow = 10; + rach_cfg.ra_ContentionResolutionTimer = 64; + mac->set_config(rach_cfg); + + srsran::dl_harq_cfg_nr_t harq_cfg = {}; + harq_cfg.nof_procs = 8; + mac->set_config(harq_cfg); + + // Setup SRB0, 1 and 2 + for (int i = 0; i < 3; i++) { + logical_channel_config_t lch = {}; + lch.lcid = i; + lch.priority = i + 1; + mac->setup_lcid(lch); + } + + // Coreset0 configuration + // Get pointA and SSB absolute frequencies + double pointA_abs_freq_Hz = phy_cfg.carrier.dl_center_frequency_hz - + phy_cfg.carrier.nof_prb * SRSRAN_NRE * SRSRAN_SUBC_SPACING_NR(phy_cfg.carrier.scs) / 2; + double ssb_abs_freq_Hz = phy_cfg.carrier.ssb_center_freq_hz; + // Calculate integer SSB to pointA frequency offset in Hz + uint32_t ssb_pointA_freq_offset_Hz = + (ssb_abs_freq_Hz > pointA_abs_freq_Hz) ? (uint32_t)(ssb_abs_freq_Hz - pointA_abs_freq_Hz) : 0; + + if (srsran_coreset_zero(phy_cfg.carrier.pci, + ssb_pointA_freq_offset_Hz, + phy_cfg.ssb.scs, + phy_cfg.carrier.scs, + 6, + &phy_cfg.pdcch.coreset[0])) { + fprintf(stderr, "Error generating coreset0\n"); + } + phy_cfg.pdcch.coreset_present[0] = true; + + // RAR SS + phy_cfg.pdcch.ra_search_space_present = true; + phy_cfg.pdcch.ra_search_space.coreset_id = 0; + phy_cfg.pdcch.ra_search_space.duration = 1; + phy_cfg.pdcch.ra_search_space.type = srsran_search_space_type_common_1; + phy_cfg.pdcch.ra_search_space.nof_formats = 1; + phy_cfg.pdcch.ra_search_space.formats[1] = srsran_dci_format_nr_1_0; + phy_cfg.pdcch.ra_search_space.nof_candidates[0] = 0; + phy_cfg.pdcch.ra_search_space.nof_candidates[1] = 0; + phy_cfg.pdcch.ra_search_space.nof_candidates[2] = 1; + phy_cfg.pdcch.ra_search_space.nof_candidates[3] = 0; + phy_cfg.pdcch.ra_search_space.nof_candidates[4] = 0; + + // common1 SS + phy_cfg.pdcch.search_space_present[0] = true; + phy_cfg.pdcch.search_space[0].coreset_id = 0; + phy_cfg.pdcch.search_space[0].duration = 1; + phy_cfg.pdcch.search_space[0].nof_candidates[0] = 0; + phy_cfg.pdcch.search_space[0].nof_candidates[1] = 0; + phy_cfg.pdcch.search_space[0].nof_candidates[2] = 1; + phy_cfg.pdcch.search_space[0].nof_candidates[3] = 0; + phy_cfg.pdcch.search_space[0].nof_candidates[4] = 0; + phy_cfg.pdcch.search_space[0].type = srsran_search_space_type_common_1; + phy_cfg.pdcch.search_space[0].nof_formats = 2; + phy_cfg.pdcch.search_space[0].formats[0] = srsran_dci_format_nr_0_0; + phy_cfg.pdcch.search_space[0].formats[1] = srsran_dci_format_nr_1_0; + phy_cfg.pdcch.search_space_present[1] = false; + + if (not setup_req_proc.launch(cause, std::move(dedicated_info_nas_))) { + logger.error("Failed to initiate setup request procedure"); + return SRSRAN_ERROR; + } + callback_list.add_proc(setup_req_proc); return SRSRAN_SUCCESS; } @@ -251,6 +530,103 @@ void rrc_nr::send_ul_info_transfer(unique_byte_buffer_t nas_msg) logger.warning("%s not implemented yet.", __FUNCTION__); } +void rrc_nr::send_setup_request(srsran::nr_establishment_cause_t cause) +{ + logger.debug("Preparing RRC Setup Request"); + + // Prepare SetupRequest packet + ul_ccch_msg_s ul_ccch_msg; + rrc_setup_request_ies_s* rrc_setup_req = &ul_ccch_msg.msg.set_c1().set_rrc_setup_request().rrc_setup_request; + + // TODO: implement ng_minus5_g_s_tmsi_part1 + rrc_setup_req->ue_id.set_random_value(); + // TODO use proper RNG + uint64_t random_id = 0; + for (uint i = 0; i < 5; i++) { // fill random ID bytewise, 40 bits = 5 bytes + random_id |= ((uint64_t)rand() & 0xFF) << i * 8; + } + rrc_setup_req->ue_id.random_value().from_number(random_id); + rrc_setup_req->establishment_cause = (establishment_cause_opts::options)cause; + + send_ul_ccch_msg(ul_ccch_msg); +} + +void rrc_nr::send_ul_ccch_msg(const asn1::rrc_nr::ul_ccch_msg_s& msg) +{ + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + if (pdu == nullptr) { + logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return; + } + + asn1::bit_ref bref(pdu->msg, pdu->get_tailroom()); + msg.pack(bref); + bref.align_bytes_zero(); + pdu->N_bytes = (uint32_t)bref.distance_bytes(pdu->msg); + pdu->set_timestamp(); + + // Set UE contention resolution ID in MAC + uint64_t uecri = 0; + uint8_t* ue_cri_ptr = (uint8_t*)&uecri; + uint32_t nbytes = 6; + for (uint32_t i = 0; i < nbytes; i++) { + ue_cri_ptr[nbytes - i - 1] = pdu->msg[i]; + } + + logger.debug("Setting UE contention resolution ID: %" PRIu64 "", uecri); + mac->set_contention_id(uecri); + + uint32_t lcid = 0; + log_rrc_message(get_rb_name(lcid), Tx, pdu.get(), msg, msg.msg.c1().type().to_string()); + + rlc->write_sdu(lcid, std::move(pdu)); +} + +void rrc_nr::send_ul_dcch_msg(uint32_t lcid, const ul_dcch_msg_s& msg) +{ + // Reset and reuse sdu buffer if provided + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + if (pdu == nullptr) { + logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return; + } + + asn1::bit_ref bref(pdu->msg, pdu->get_tailroom()); + msg.pack(bref); + bref.align_bytes_zero(); + pdu->N_bytes = (uint32_t)bref.distance_bytes(pdu->msg); + pdu->set_timestamp(); + + if (msg.msg.type() == ul_dcch_msg_type_c::types_opts::options::c1) { + log_rrc_message(get_rb_name(lcid), Tx, pdu.get(), msg, msg.msg.c1().type().to_string()); + } + + pdcp->write_sdu(lcid, std::move(pdu)); +} + +void rrc_nr::send_con_setup_complete(srsran::unique_byte_buffer_t nas_msg) +{ + logger.debug("Preparing RRC Connection Setup Complete"); + + // Prepare ConnectionSetupComplete packet + asn1::rrc_nr::ul_dcch_msg_s ul_dcch_msg; + rrc_setup_complete_ies_s* rrc_setup_complete = + &ul_dcch_msg.msg.set_c1().set_rrc_setup_complete().crit_exts.set_rrc_setup_complete(); + + ul_dcch_msg.msg.c1().rrc_setup_complete().rrc_transaction_id = transaction_id; + + rrc_setup_complete->sel_plmn_id = 1; + rrc_setup_complete->registered_amf_present = false; + rrc_setup_complete->guami_type_present = false; + rrc_setup_complete->s_nssai_list_present = false; + rrc_setup_complete->ng_minus5_g_s_tmsi_value_present = false; + + rrc_setup_complete->ded_nas_msg.resize(nas_msg->N_bytes); + memcpy(rrc_setup_complete->ded_nas_msg.data(), nas_msg->msg, nas_msg->N_bytes); + + send_ul_dcch_msg(srb_to_lcid(nr_srb::srb1), ul_dcch_msg); +} + // EUTRA-RRC interface int rrc_nr::get_eutra_nr_capabilities(srsran::byte_buffer_t* eutra_nr_caps_pdu) { @@ -381,22 +757,9 @@ int rrc_nr::get_eutra_nr_capabilities(srsran::byte_buffer_t* eutra_nr_caps_pdu) return SRSRAN_SUCCESS; } -bool rrc_nr::rrc_reconfiguration(bool endc_release_and_add_r15, - bool nr_secondary_cell_group_cfg_r15_present, - asn1::dyn_octstring nr_secondary_cell_group_cfg_r15, - bool sk_counter_r15_present, - uint32_t sk_counter_r15, - bool nr_radio_bearer_cfg1_r15_present, - asn1::dyn_octstring nr_radio_bearer_cfg1_r15) +bool rrc_nr::rrc_reconfiguration(bool endc_release_and_add_r15, const asn1::rrc_nr::rrc_recfg_s& rrc_nr_reconf) { - if (not conn_recfg_proc.launch(reconf_initiator_t::mcg_srb1, - endc_release_and_add_r15, - nr_secondary_cell_group_cfg_r15_present, - nr_secondary_cell_group_cfg_r15, - sk_counter_r15_present, - sk_counter_r15, - nr_radio_bearer_cfg1_r15_present, - nr_radio_bearer_cfg1_r15)) { + if (not conn_recfg_proc.launch(reconf_initiator_t::mcg_srb1, endc_release_and_add_r15, rrc_nr_reconf)) { logger.error("Unable to launch NR RRC reconfiguration procedure"); return false; } else { @@ -539,6 +902,7 @@ bool rrc_nr::apply_rlc_add_mod(const rlc_bearer_cfg_s& rlc_bearer_cfg) } return true; } + bool rrc_nr::apply_mac_cell_group(const mac_cell_group_cfg_s& mac_cell_group_cfg) { if (mac_cell_group_cfg.sched_request_cfg_present) { @@ -962,8 +1326,9 @@ bool rrc_nr::apply_ul_common_cfg(const asn1::rrc_nr::ul_cfg_common_s& ul_cfg_com if (ul_cfg_common.init_ul_bwp_present) { if (ul_cfg_common.init_ul_bwp.rach_cfg_common_present) { if (ul_cfg_common.init_ul_bwp.rach_cfg_common.type() == setup_release_c::types_opts::setup) { - rach_nr_cfg_t rach_nr_cfg = make_mac_rach_cfg(ul_cfg_common.init_ul_bwp.rach_cfg_common.setup()); - mac->set_config(rach_nr_cfg); + rach_cfg_nr_t rach_cfg_nr = {}; + make_mac_rach_cfg(ul_cfg_common.init_ul_bwp.rach_cfg_common.setup(), &rach_cfg_nr); + mac->set_config(rach_cfg_nr); // Make the RACH configuration for PHY if (not make_phy_rach_cfg(ul_cfg_common.init_ul_bwp.rach_cfg_common.setup(), &phy_cfg.prach)) { @@ -1252,7 +1617,6 @@ bool rrc_nr::apply_sp_cell_cfg(const sp_cell_cfg_s& sp_cell_cfg) } } else { logger.warning("Reconfig with with sync not present"); - return false; } // Dedicated config @@ -1345,7 +1709,6 @@ bool rrc_nr::apply_sp_cell_cfg(const sp_cell_cfg_s& sp_cell_cfg) } } else { logger.warning("Option pdsch_serving_cell_cfg not present"); - return false; } if (sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg_present) { @@ -1359,7 +1722,6 @@ bool rrc_nr::apply_sp_cell_cfg(const sp_cell_cfg_s& sp_cell_cfg) } } else { logger.warning("Option csi_meas_cfg in spCellConfigDedicated not present"); - return false; } } else { @@ -1551,6 +1913,38 @@ bool rrc_nr::apply_radio_bearer_cfg(const radio_bearer_cfg_s& radio_bearer_cfg) } return true; } + +bool rrc_nr::handle_rrc_setup(const rrc_setup_s& setup) +{ + // Unpack masterCellGroup into container + asn1::cbit_ref bref_cg(setup.crit_exts.rrc_setup().master_cell_group.data(), + setup.crit_exts.rrc_setup().master_cell_group.size()); + + asn1::rrc_nr::cell_group_cfg_s cell_group; + if (cell_group.unpack(bref_cg) != asn1::SRSASN_SUCCESS) { + logger.error("Could not unpack master cell group config."); + return false; + } + asn1::json_writer js; + cell_group.to_json(js); + logger.debug("Containerized MasterCellGroup: %s", js.to_string().c_str()); + + // Must enter CONNECT before stopping T300 + state = RRC_NR_STATE_CONNECTED; + // t300.stop(); + // t302.stop(); + srsran::console("RRC Connected\n"); + + // defer transmission of Setup Complete until PHY reconfiguration has been completed + if (not conn_setup_proc.launch( + setup.crit_exts.rrc_setup().radio_bearer_cfg, cell_group, std::move(dedicated_info_nas))) { + logger.error("Failed to initiate connection setup procedure"); + return false; + } + callback_list.add_proc(conn_setup_proc); + return true; +} + // RLC interface void rrc_nr::max_retx_attempted() {} void rrc_nr::protocol_failure() {} @@ -1562,6 +1956,7 @@ void rrc_nr::ra_completed() phy->set_config(phy_cfg); phy_cfg_state = PHY_CFG_STATE_RA_COMPLETED; } + void rrc_nr::ra_problem() { rrc_eutra->nr_scg_failure_information(scg_failure_cause_t::random_access_problem); diff --git a/srsue/src/stack/rrc_nr/rrc_nr_procedures.cc b/srsue/src/stack/rrc_nr/rrc_nr_procedures.cc index 794d566f0..5843d125b 100644 --- a/srsue/src/stack/rrc_nr/rrc_nr_procedures.cc +++ b/srsue/src/stack/rrc_nr/rrc_nr_procedures.cc @@ -22,10 +22,10 @@ #include "srsue/hdr/stack/rrc_nr/rrc_nr_procedures.h" #include "srsran/common/standard_streams.h" -#define Error(fmt, ...) rrc_ptr->logger.error("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) -#define Warning(fmt, ...) rrc_ptr->logger.warning("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) -#define Info(fmt, ...) rrc_ptr->logger.info("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) -#define Debug(fmt, ...) rrc_ptr->logger.debug("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) +#define Error(fmt, ...) rrc_handle.logger.error("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) +#define Warning(fmt, ...) rrc_handle.logger.warning("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) +#define Info(fmt, ...) rrc_handle.logger.info("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) +#define Debug(fmt, ...) rrc_handle.logger.debug("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) using namespace asn1::rrc_nr; using namespace asn1; @@ -33,93 +33,60 @@ using namespace srsran; namespace srsue { -rrc_nr::connection_reconf_no_ho_proc::connection_reconf_no_ho_proc(rrc_nr* parent_) : rrc_ptr(parent_), initiator(nr) {} +rrc_nr::connection_reconf_no_ho_proc::connection_reconf_no_ho_proc(rrc_nr& parent_) : rrc_handle(parent_), initiator(nr) +{} -proc_outcome_t rrc_nr::connection_reconf_no_ho_proc::init(const reconf_initiator_t initiator_, - const bool endc_release_and_add_r15, - const bool nr_secondary_cell_group_cfg_r15_present, - const asn1::dyn_octstring nr_secondary_cell_group_cfg_r15, - const bool sk_counter_r15_present, - const uint32_t sk_counter_r15, - const bool nr_radio_bearer_cfg1_r15_present, - const asn1::dyn_octstring nr_radio_bearer_cfg1_r15) +proc_outcome_t rrc_nr::connection_reconf_no_ho_proc::init(const reconf_initiator_t initiator_, + const bool endc_release_and_add_r15, + const asn1::rrc_nr::rrc_recfg_s& rrc_nr_reconf) { Info("Starting..."); initiator = initiator_; - rrc_recfg_s rrc_recfg; - cell_group_cfg_s cell_group_cfg; - radio_bearer_cfg_s radio_bearer_cfg; - asn1::SRSASN_CODE err; - - if (nr_secondary_cell_group_cfg_r15_present) { - cbit_ref bref(nr_secondary_cell_group_cfg_r15.data(), nr_secondary_cell_group_cfg_r15.size()); - err = rrc_recfg.unpack(bref); - if (err != asn1::SRSASN_SUCCESS) { - Error("Could not unpack NR reconfiguration message."); - return proc_outcome_t::error; - } - #if 0 - rrc_ptr->log_rrc_message( - "RRC NR Reconfiguration", Rx, nr_secondary_cell_group_cfg_r15, rrc_recfg, "NR Secondary Cell Group Cfg R15"); + asn1::json_writer js; + rrc_nr_reconf.to_json(js); + Debug("RRC NR Reconfiguration: %s", js.to_string().c_str()); #endif - if (rrc_recfg.crit_exts.type() != asn1::rrc_nr::rrc_recfg_s::crit_exts_c_::types::rrc_recfg) { + + if (rrc_nr_reconf.crit_exts.rrc_recfg().secondary_cell_group_present) { + if (rrc_nr_reconf.crit_exts.type() != asn1::rrc_nr::rrc_recfg_s::crit_exts_c_::types::rrc_recfg) { Error("Reconfiguration does not contain Secondary Cell Group Config"); return proc_outcome_t::error; } - if (not rrc_recfg.crit_exts.rrc_recfg().secondary_cell_group_present) { - Error("Reconfiguration does not contain Secondary Cell Group Config"); - return proc_outcome_t::error; - } + cbit_ref bref0(rrc_nr_reconf.crit_exts.rrc_recfg().secondary_cell_group.data(), + rrc_nr_reconf.crit_exts.rrc_recfg().secondary_cell_group.size()); - cbit_ref bref0(rrc_recfg.crit_exts.rrc_recfg().secondary_cell_group.data(), - rrc_recfg.crit_exts.rrc_recfg().secondary_cell_group.size()); - - err = cell_group_cfg.unpack(bref0); - if (err != asn1::SRSASN_SUCCESS) { - Error("Could not unpack cell group message message."); + cell_group_cfg_s cell_group_cfg; + if (cell_group_cfg.unpack(bref0) != asn1::SRSASN_SUCCESS) { + Error("Could not unpack secondary cell group config."); return proc_outcome_t::error; } #if 0 - rrc_ptr->log_rrc_message("RRC NR Reconfiguration", - Rx, - rrc_recfg.crit_exts.rrc_recfg().secondary_cell_group, - cell_group_cfg, - "Secondary Cell Group Config"); + asn1::json_writer js1; + cell_group_cfg.to_json(js1); + Debug("Secondary Cell Group: %s", js1.to_string().c_str()); #endif Info("Applying Cell Group Cfg"); - if (!rrc_ptr->apply_cell_group_cfg(cell_group_cfg)) { + if (!rrc_handle.apply_cell_group_cfg(cell_group_cfg)) { return proc_outcome_t::error; } } - if (sk_counter_r15_present) { - Info("Applying Cell Group Cfg"); - if (!rrc_ptr->configure_sk_counter((uint16_t)sk_counter_r15)) { + if (rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.non_crit_ext.non_crit_ext.sk_counter_present) { + Info("Applying sk counter"); + if (!rrc_handle.configure_sk_counter( + (uint16_t)rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.non_crit_ext.non_crit_ext.sk_counter)) { return proc_outcome_t::error; } } - if (nr_radio_bearer_cfg1_r15_present) { - cbit_ref bref1(nr_radio_bearer_cfg1_r15.data(), nr_radio_bearer_cfg1_r15.size()); - - err = radio_bearer_cfg.unpack(bref1); - if (err != asn1::SRSASN_SUCCESS) { - Error("Could not unpack radio bearer config."); - return proc_outcome_t::error; - } - -#if 0 - rrc_ptr->log_rrc_message( - "RRC NR Reconfiguration", Rx, nr_radio_bearer_cfg1_r15, radio_bearer_cfg, "Radio Bearer Config R15"); -#endif - + if (rrc_nr_reconf.crit_exts.rrc_recfg().radio_bearer_cfg_present) { Info("Applying Radio Bearer Cfg"); - if (!rrc_ptr->apply_radio_bearer_cfg(radio_bearer_cfg)) { + if (!rrc_handle.apply_radio_bearer_cfg(rrc_nr_reconf.crit_exts.rrc_recfg().radio_bearer_cfg)) { return proc_outcome_t::error; } } @@ -149,12 +116,12 @@ void rrc_nr::connection_reconf_no_ho_proc::then(const srsran::proc_state_t& resu if (result.is_success()) { Info("Finished %s successfully", name()); srsran::console("RRC NR reconfiguration successful.\n"); - rrc_ptr->rrc_eutra->nr_rrc_con_reconfig_complete(true); + rrc_handle.rrc_eutra->nr_rrc_con_reconfig_complete(true); } else { // 5.3.5.8.2 Inability to comply with RRCReconfiguration switch (initiator) { case reconf_initiator_t::mcg_srb1: - rrc_ptr->rrc_eutra->nr_notify_reconfiguration_failure(); + rrc_handle.rrc_eutra->nr_notify_reconfiguration_failure(); break; default: Warning("Reconfiguration failure not implemented for initiator %d", initiator); @@ -166,4 +133,258 @@ void rrc_nr::connection_reconf_no_ho_proc::then(const srsran::proc_state_t& resu return; } +/************************************** + * RRC Setup Request Procedure + *************************************/ + +rrc_nr::setup_request_proc::setup_request_proc(rrc_nr& parent_) : + rrc_handle(parent_), logger(srslog::fetch_basic_logger("RRC")) +{} + +proc_outcome_t rrc_nr::setup_request_proc::init(srsran::nr_establishment_cause_t cause_, + srsran::unique_byte_buffer_t dedicated_info_nas_) +{ + cause = cause_; + dedicated_info_nas = std::move(dedicated_info_nas_); + + if (!rrc_handle.plmn_is_selected) { + Error("Trying to connect but PLMN not selected."); + return proc_outcome_t::error; + } + + if (rrc_handle.state != RRC_NR_STATE_IDLE) { + logger.warning("Requested RRC connection establishment while not in IDLE"); + return proc_outcome_t::error; + } + + // TODO: add T302 handling + + Info("Initiation of Connection establishment procedure"); + + cell_search_ret = cell_search_result_t::no_cell; + + state = state_t::cell_selection; + if (rrc_handle.cell_selector.is_idle()) { + // No one is running cell selection + if (not rrc_handle.cell_selector.launch()) { + Error("Failed to initiate cell selection procedure..."); + return proc_outcome_t::error; + } + rrc_handle.callback_list.add_proc(rrc_handle.cell_selector); + } else { + Info("Cell selection proc already on-going. Wait for its result"); + } + return proc_outcome_t::yield; +} + +proc_outcome_t rrc_nr::setup_request_proc::step() +{ + if (state == state_t::cell_selection) { + // NOTE: cell selection will signal back with an event trigger + return proc_outcome_t::yield; + } + + if (state == state_t::config_serving_cell) { + // TODO: start serving cell config and start T300 + + rrc_handle.phy_cfg_state = PHY_CFG_STATE_APPLY_SP_CELL; + rrc_handle.phy->set_config(rrc_handle.phy_cfg); + + // Send setup request message to lower layers + rrc_handle.send_setup_request(cause); + + Info("Waiting for RRCSetup/Reject or expiry"); + state = state_t::wait_t300; + return step(); + + } else if (state == state_t::wait_t300) { + // TODO: add T300 waiting + } + + return proc_outcome_t::error; +} + +void rrc_nr::setup_request_proc::then(const srsran::proc_state_t& result) +{ + if (result.is_error()) { + logger.warning("Could not establish connection. Deallocating dedicatedInfoNAS PDU"); + dedicated_info_nas.reset(); + rrc_handle.dedicated_info_nas.reset(); + } else { + Info("Finished connection request procedure successfully."); + } + // TODO: signal back to NAS + // rrc_handle.nas->connection_request_completed(result.is_success()); +} + +srsran::proc_outcome_t rrc_nr::setup_request_proc::react(const cell_selection_proc::cell_selection_complete_ev& e) +{ + if (state != state_t::cell_selection) { + // ignore if we are not expecting an cell selection result + return proc_outcome_t::yield; + } + if (e.is_error()) { + return proc_outcome_t::error; + } + cell_search_ret = *e.value(); + // .. and SI acquisition + // TODO @ismagom use appropiate PHY interface + if (true /*rrc_handle.phy->cell_is_camping()*/) { + // TODO: Set default configurations + // rrc_handle.set_phy_default(); + // rrc_handle.set_mac_default(); + + // CCCH configuration applied already at start + // timeAlignmentCommon applied in configure_serving_cell + + Info("Configuring serving cell..."); + state = state_t::config_serving_cell; + + // Skip SI acquisition + return step(); + } else { + switch (cell_search_ret) { + case cell_search_result_t::same_cell: + logger.warning("Did not reselect cell but serving cell is out-of-sync."); + break; + case cell_search_result_t::changed_cell: + logger.warning("Selected a new cell but could not camp on. Setting out-of-sync."); + break; + default: + logger.warning("Could not find any suitable cell to connect"); + } + return proc_outcome_t::error; + } +} + +/****************************************** + * Connection Setup Procedure + *****************************************/ + +// Simple procedure mainly do defer the transmission of the SetupComplete until all PHY reconfiguration are done +rrc_nr::connection_setup_proc::connection_setup_proc(srsue::rrc_nr& parent_) : + rrc_handle(parent_), logger(srslog::fetch_basic_logger("RRC")) +{} + +srsran::proc_outcome_t rrc_nr::connection_setup_proc::init(const asn1::rrc_nr::radio_bearer_cfg_s radio_bearer_cfg_, + const asn1::rrc_nr::cell_group_cfg_s cell_group_, + srsran::unique_byte_buffer_t dedicated_info_nas_) +{ + Info("Starting..."); + + // if (dedicated_info_nas_.get() == nullptr) { + // logger.error("Connection Setup Failed, no dedicatedInfoNAS available"); + // return proc_outcome_t::error; + // } + + dedicated_info_nas = std::move(dedicated_info_nas_); + + // Apply the Radio Bearer configuration + if (!rrc_handle.apply_radio_bearer_cfg(radio_bearer_cfg_)) { + return proc_outcome_t::error; + } + + // Apply the Cell Group configuration + if (!rrc_handle.apply_cell_group_cfg(cell_group_)) { + return proc_outcome_t::error; + } + + return proc_outcome_t::yield; +} + +srsran::proc_outcome_t rrc_nr::connection_setup_proc::react(const bool& config_complete) +{ + if (not config_complete) { + logger.error("Connection Setup Failed"); + return proc_outcome_t::error; + } + + rrc_handle.send_con_setup_complete(std::move(dedicated_info_nas)); + return proc_outcome_t::success; +} + +void rrc_nr::connection_setup_proc::then(const srsran::proc_state_t& result) +{ + if (result.is_success()) { + logger.info("Finished %s successfully", name()); + return; + } +} + +/************************************** + * Basic Cell Selection Procedure + *************************************/ + +rrc_nr::cell_selection_proc::cell_selection_proc(rrc_nr& parent_) : + rrc_handle(parent_), meas_cells(rrc_handle.meas_cells) +{} + +/// Verifies if serving cell passes selection criteria, UE is camping, and required SIBs were obtained +bool rrc_nr::cell_selection_proc::is_serv_cell_suitable() const +{ + // TODO: add selection criteria + return true; +} + +/// Called on procedure exit to set result +proc_outcome_t rrc_nr::cell_selection_proc::set_proc_complete() +{ + if (is_serv_cell_suitable()) { + cell_search_ret = is_same_cell(init_serv_cell, meas_cells.serving_cell()) ? cell_search_result_t::same_cell + : cell_search_result_t::changed_cell; + return proc_outcome_t::success; + } + cell_search_ret = cell_search_result_t::no_cell; + return proc_outcome_t::error; +} + +proc_outcome_t rrc_nr::cell_selection_proc::init() +{ + init_serv_cell = meas_cells.serving_cell().phy_cell; + + // TODO: add full cell selection + phy_interface_rrc_nr::cell_select_args_t cell_cfg = {}; + cell_cfg.carrier = rrc_handle.phy_cfg.carrier; + cell_cfg.ssb_cfg = rrc_handle.phy_cfg.get_ssb_cfg(); + rrc_handle.phy->start_cell_select(cell_cfg); + + // Skip cell selection if serving cell is suitable and there are no stronger neighbours in same earfcn + if (is_serv_cell_suitable()) { + Debug("Skipping cell selection procedure as there are no stronger neighbours in same EARFCN."); + return set_proc_complete(); + } + + return set_proc_complete(); +} + +proc_outcome_t rrc_nr::cell_selection_proc::step() +{ + switch (state) { + case search_state_t::cell_selection: + // this state waits for phy event + return proc_outcome_t::yield; + case search_state_t::serv_cell_camp: + // this state waits for phy event + return proc_outcome_t::yield; + case search_state_t::cell_config: + // return step_cell_config(); + return proc_outcome_t::yield; + case search_state_t::cell_search: + // return step_cell_search(); + return proc_outcome_t::yield; + } + return proc_outcome_t::error; +} + +void rrc_nr::cell_selection_proc::then(const srsran::proc_result_t& proc_result) const +{ + Info("Completed with %s.", proc_result.is_success() ? "success" : "failure"); + // Inform Connection Request Procedure + rrc_handle.task_sched.defer_task([this, proc_result]() { + if (rrc_handle.setup_req_proc.is_busy()) { + rrc_handle.setup_req_proc.trigger(proc_result); + } + }); +} + } // namespace srsue diff --git a/srsue/src/stack/rrc_nr/test/ue_rrc_nr_test.cc b/srsue/src/stack/rrc_nr/test/ue_rrc_nr_test.cc index dbe1a8ee1..d9978b944 100644 --- a/srsue/src/stack/rrc_nr/test/ue_rrc_nr_test.cc +++ b/srsue/src/stack/rrc_nr/test/ue_rrc_nr_test.cc @@ -25,13 +25,18 @@ #include "srsran/interfaces/ue_pdcp_interfaces.h" #include "srsran/interfaces/ue_rlc_interfaces.h" #include "srsran/interfaces/ue_usim_interfaces.h" +#include "srsue/hdr/stack/rrc/rrc.h" #include "srsue/hdr/stack/rrc_nr/rrc_nr.h" using namespace srsue; class dummy_phy : public phy_interface_rrc_nr { - bool set_config(const srsran::phy_cfg_nr_t& cfg) { return true; } + bool set_config(const srsran::phy_cfg_nr_t& cfg) override { return true; } + phy_nr_state_t get_state() override { return PHY_NR_STATE_IDLE; }; + void reset_nr() override{}; + bool start_cell_search(const cell_search_args_t& req) override { return false; }; + bool start_cell_select(const cell_select_args_t& req) override { return false; }; }; class dummy_mac : public mac_interface_rrc_nr @@ -41,7 +46,7 @@ class dummy_mac : public mac_interface_rrc_nr int set_config(const srsran::bsr_cfg_nr_t& bsr_cfg) { return SRSRAN_SUCCESS; } int set_config(const srsran::sr_cfg_nr_t& sr_cfg) { return SRSRAN_SUCCESS; } int set_config(const srsran::dl_harq_cfg_nr_t& dl_hrq_cfg) { return SRSRAN_SUCCESS; } - void set_config(const srsran::rach_nr_cfg_t& rach_cfg) {} + void set_config(const srsran::rach_cfg_nr_t& rach_cfg) {} int add_tag_config(const srsran::tag_cfg_nr_t& tag_cfg) { return SRSRAN_SUCCESS; } int set_config(const srsran::phr_cfg_nr_t& phr_cfg) { return SRSRAN_SUCCESS; } int remove_tag_config(const uint32_t tag_id) { return SRSRAN_SUCCESS; } @@ -67,7 +72,15 @@ class dummy_rlc : public rlc_interface_rrc bool has_bearer(uint32_t lcid) { return true; } bool has_data(const uint32_t lcid) { return true; } bool is_suspended(const uint32_t lcid) { return true; } - void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu) {} + void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu) + { + last_lcid = lcid; + last_sdu = std::move(sdu); + } + +public: + uint32_t last_lcid = 99; + srsran::unique_byte_buffer_t last_sdu; }; class dummy_pdcp : public pdcp_interface_rrc @@ -119,7 +132,6 @@ class dummy_stack : public stack_interface_rrc int rrc_nr_cap_request_test() { - srslog::init(); srslog::basic_logger& logger = srslog::fetch_basic_logger("RRC-NR"); logger.set_level(srslog::basic_levels::debug); logger.set_hex_dump_max_size(-1); @@ -157,9 +169,8 @@ int rrc_nr_cap_request_test() return SRSRAN_SUCCESS; } -int rrc_nr_reconfig_test() +int rrc_nsa_reconfig_tdd_test() { - srslog::init(); srslog::basic_logger& logger = srslog::fetch_basic_logger("RRC-NR"); logger.set_level(srslog::basic_levels::debug); logger.set_hex_dump_max_size(-1); @@ -187,38 +198,336 @@ int rrc_nr_reconfig_test() &dummy_stack, rrc_nr_args) == SRSRAN_SUCCESS); - uint8_t nr_secondary_cell_group_cfg_r15_bytes[] = { - 0x08, 0x81, 0x19, 0x5c, 0x40, 0xb1, 0x42, 0x7e, 0x08, 0x30, 0xf3, 0x20, 0x3e, 0x00, 0x80, 0x34, 0x1e, 0x00, 0x80, - 0x02, 0xe8, 0x5b, 0x98, 0xc0, 0x06, 0x93, 0x5a, 0x40, 0x04, 0xd2, 0x6b, 0x00, 0x00, 0x00, 0x00, 0xcd, 0x8d, 0xb2, - 0x45, 0xe2, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x1b, 0x82, 0x21, 0x00, 0x00, 0x44, 0x04, 0x00, 0xd0, - 0x1a, 0xe2, 0x00, 0x00, 0x01, 0x98, 0x71, 0xb6, 0x48, 0x95, 0x00, 0x20, 0x07, 0xb7, 0x25, 0x58, 0xf0, 0x00, 0x00, - 0x13, 0x8c, 0x21, 0xb8, 0x83, 0x69, 0x92, 0xa0, 0xb8, 0x75, 0x01, 0x08, 0x1c, 0x0c, 0x00, 0x30, 0x78, 0x00, 0x03, - 0x49, 0xa9, 0xe0, 0x07, 0xb7, 0x25, 0x58, 0x00, 0x25, 0x06, 0xa0, 0x00, 0x80, 0xe0, 0x12, 0xd8, 0x0c, 0x88, 0x03, - 0x70, 0x84, 0x20, 0x00, 0x11, 0x11, 0x6d, 0x00, 0x00, 0x00, 0x12, 0x08, 0x00, 0x00, 0x83, 0xa6, 0x02, 0x66, 0xaa, - 0xe9, 0x28, 0x38, 0x00, 0x20, 0x81, 0x84, 0x0a, 0x18, 0x39, 0x38, 0x81, 0x22, 0x85, 0x8c, 0x1a, 0x38, 0x78, 0xfc, - 0x00, 0x00, 0x66, 0x02, 0x18, 0x10, 0x00, 0xcc, 0x04, 0xb0, 0x40, 0x01, 0x98, 0x0a, 0x60, 0xc0, 0x03, 0x30, 0x16, - 0xc2, 0x00, 0x06, 0x60, 0x31, 0x85, 0x00, 0x0c, 0xc0, 0x6b, 0x0c, 0x00, 0x19, 0x80, 0xe6, 0x1c, 0x00, 0x33, 0x21, - 0x40, 0x31, 0x00, 0x01, 0x72, 0x58, 0x62, 0x40, 0x02, 0xe4, 0xb2, 0xc5, 0x00, 0x05, 0xc9, 0x69, 0x8b, 0x00, 0x0b, - 0x92, 0xdb, 0x18, 0x00, 0x17, 0x25, 0xc6, 0x34, 0x00, 0x2e, 0x4b, 0xac, 0x70, 0x00, 0x5c, 0x97, 0x98, 0xf0, 0x00, - 0xcd, 0x85, 0x07, 0x95, 0xe5, 0x79, 0x43, 0x01, 0xe4, 0x07, 0x23, 0x45, 0x67, 0x89, 0x7d, 0x42, 0x10, 0x84, 0x00, - 0x0c, 0xd0, 0x1a, 0x41, 0x07, 0x82, 0xb8, 0x03, 0x04, 0x28, 0x01, 0x63, 0xff, 0x4a, 0x52, 0x63, 0x18, 0xdc, 0xa0, - 0x50, 0x00, 0x08, 0x72, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x12, 0x00, 0x00, 0x00, 0x04, 0x8a, 0x80}; + uint8_t msg[] = { + 0x20, 0x12, 0xaa, 0x00, 0x02, 0x00, 0x80, 0x23, 0x00, 0x01, 0xfb, 0x54, 0x94, 0x10, 0x43, 0xc6, 0x40, 0x62, 0x04, + 0x40, 0x60, 0xae, 0x20, 0x58, 0xe0, 0x3e, 0xa4, 0x1d, 0x02, 0x60, 0x19, 0x00, 0x82, 0x28, 0x01, 0x64, 0x29, 0xdc, + 0x6f, 0xa3, 0x49, 0xad, 0x40, 0x02, 0x69, 0x35, 0x89, 0x00, 0x00, 0x00, 0x66, 0xc6, 0xd9, 0x22, 0x51, 0x00, 0xff, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x8d, 0xc1, 0x10, 0x80, 0x01, 0x24, 0x42, 0x00, 0x68, 0x0a, 0x36, 0x00, 0x9a, 0x4d, + 0x62, 0x40, 0x00, 0x00, 0x19, 0xb8, 0xdb, 0x24, 0x48, 0x01, 0x00, 0x04, 0x17, 0x12, 0x8c, 0x78, 0x01, 0x25, 0x18, + 0x83, 0x70, 0xc6, 0xe3, 0xa2, 0x47, 0x01, 0x80, 0x22, 0x07, 0x03, 0x00, 0x10, 0x1e, 0x23, 0x00, 0xd2, 0x4b, 0x81, + 0xb5, 0x00, 0x02, 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0xe1, 0x10, 0x40, 0x00, 0x92, 0x22, 0x4a, 0x00, 0x00, + 0x90, 0x40, 0x00, 0x04, 0x0d, 0x3a, 0x00, 0x08, 0x02, 0x91, 0x8a, 0x92, 0x42, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x4e, 0x04, 0x08, 0x10, 0x20, 0x40, 0x81, 0x02, 0x08, 0x00, 0x00, 0x21, 0x40, 0x00, 0x23, 0x34, 0x1c, + 0x01, 0x0c, 0xc8, 0x50, 0x09, 0x08, 0x60, 0x51, 0x00, 0xab, 0x2a, 0x22, 0x24, 0x40, 0x02, 0x90, 0x5f, 0xfd, 0x29, + 0x49, 0x8c, 0x63, 0x62, 0x45, 0x6a, 0x00, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x04, 0x10, 0x00, 0x71, 0x00, 0x04, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x8a, 0x04, 0x94, 0x0b, 0xc3, 0xe0, 0x06, 0x80, 0x40, 0x00}; - asn1::dyn_octstring nr_secondary_cell_group_cfg_r15; - nr_secondary_cell_group_cfg_r15.resize(sizeof(nr_secondary_cell_group_cfg_r15_bytes)); - memcpy(nr_secondary_cell_group_cfg_r15.data(), - nr_secondary_cell_group_cfg_r15_bytes, - sizeof(nr_secondary_cell_group_cfg_r15_bytes)); + asn1::cbit_ref bref(msg, sizeof(msg)); + asn1::rrc::dl_dcch_msg_s dl_dcch_msg; + + TESTASSERT(dl_dcch_msg.unpack(bref) == asn1::SRSASN_SUCCESS); + TESTASSERT(dl_dcch_msg.msg.type().value == dl_dcch_msg_type_c::types_opts::c1); + + dl_dcch_msg_type_c::c1_c_* c1 = &dl_dcch_msg.msg.c1(); + rrc_conn_recfg_r8_ies_s rx_recfg = c1->rrc_conn_recfg().crit_exts.c1().rrc_conn_recfg_r8(); + + const asn1::rrc::rrc_conn_recfg_v1510_ies_s rrc_conn_recfg_v1510_ies = + rx_recfg.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext; + + bool endc_release_and_add_r15 = false; + + asn1::rrc_nr::rrc_recfg_s rrc_nr_reconf = {}; + rrc_nr_reconf.crit_exts.set_rrc_recfg(); + + TESTASSERT(rrc_conn_recfg_v1510_ies.nr_cfg_r15.type() == setup_opts::options::setup); + + endc_release_and_add_r15 = rrc_conn_recfg_v1510_ies.nr_cfg_r15.setup().endc_release_and_add_r15; + + TESTASSERT(rrc_conn_recfg_v1510_ies.nr_cfg_r15.setup().nr_secondary_cell_group_cfg_r15_present); + + asn1::cbit_ref bref0(rrc_conn_recfg_v1510_ies.nr_cfg_r15.setup().nr_secondary_cell_group_cfg_r15.data(), + rrc_conn_recfg_v1510_ies.nr_cfg_r15.setup().nr_secondary_cell_group_cfg_r15.size()); + + asn1::rrc_nr::rrc_recfg_s secondary_cell_group_r15; + TESTASSERT(secondary_cell_group_r15.unpack(bref0) == asn1::SRSASN_SUCCESS); + + TESTASSERT(secondary_cell_group_r15.crit_exts.rrc_recfg().secondary_cell_group_present); + asn1::cbit_ref bref1(secondary_cell_group_r15.crit_exts.rrc_recfg().secondary_cell_group.data(), + secondary_cell_group_r15.crit_exts.rrc_recfg().secondary_cell_group.size()); + + asn1::rrc_nr::cell_group_cfg_s cell_group_cfg; + cell_group_cfg.unpack(bref1); + + rrc_nr_reconf.crit_exts.rrc_recfg().secondary_cell_group_present = true; + rrc_nr_reconf.crit_exts.rrc_recfg().secondary_cell_group = + secondary_cell_group_r15.crit_exts.rrc_recfg().secondary_cell_group; + + TESTASSERT(rrc_conn_recfg_v1510_ies.sk_counter_r15_present); + rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext_present = true; + rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.non_crit_ext_present = true; + rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.non_crit_ext.non_crit_ext_present = true; + rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.non_crit_ext.non_crit_ext.sk_counter_present = true; + rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.non_crit_ext.non_crit_ext.sk_counter = + rrc_conn_recfg_v1510_ies.sk_counter_r15; + + TESTASSERT(rrc_conn_recfg_v1510_ies.nr_radio_bearer_cfg1_r15_present); + rrc_nr_reconf.crit_exts.rrc_recfg().radio_bearer_cfg_present = true; + asn1::rrc_nr::radio_bearer_cfg_s radio_bearer_conf = {}; + asn1::cbit_ref bref2(rrc_conn_recfg_v1510_ies.nr_radio_bearer_cfg1_r15.data(), + rrc_conn_recfg_v1510_ies.nr_radio_bearer_cfg1_r15.size()); + TESTASSERT(radio_bearer_conf.unpack(bref2) == asn1::SRSASN_SUCCESS); + + rrc_nr_reconf.crit_exts.rrc_recfg().radio_bearer_cfg = radio_bearer_conf; + + rrc_nr.rrc_reconfiguration(endc_release_and_add_r15, rrc_nr_reconf); - asn1::dyn_octstring nr_radio_bearer_cfg1_r15; - rrc_nr.rrc_reconfiguration(true, true, nr_secondary_cell_group_cfg_r15, false, 0, false, nr_radio_bearer_cfg1_r15); task_sched.run_pending_tasks(); return SRSRAN_SUCCESS; } +int rrc_nsa_reconfig_fdd_test() +{ + srslog::basic_logger& logger = srslog::fetch_basic_logger("RRC-NR"); + logger.set_level(srslog::basic_levels::debug); + logger.set_hex_dump_max_size(-1); + srsran::task_scheduler task_sched{512, 100}; + srsran::task_sched_handle task_sched_handle(&task_sched); + rrc_nr rrc_nr(task_sched_handle); + + dummy_phy dummy_phy; + dummy_mac dummy_mac; + dummy_rlc dummy_rlc; + dummy_pdcp dummy_pdcp; + dummy_gw dummy_gw; + dummy_eutra dummy_eutra; + dummy_sim dummy_sim; + dummy_stack dummy_stack; + rrc_nr_args_t rrc_nr_args; + TESTASSERT(rrc_nr.init(&dummy_phy, + &dummy_mac, + &dummy_rlc, + &dummy_pdcp, + &dummy_gw, + &dummy_eutra, + &dummy_sim, + task_sched.get_timer_handler(), + &dummy_stack, + rrc_nr_args) == SRSRAN_SUCCESS); + + uint8_t msg[] = { + 0x20, 0x12, 0xaa, 0x00, 0x02, 0x00, 0x80, 0x23, 0x00, 0x01, 0xfb, 0x54, 0x94, 0x10, 0x43, 0xc6, 0x40, 0x62, 0x04, + 0x40, 0x60, 0xae, 0x20, 0x58, 0xe0, 0x3e, 0xa4, 0x1d, 0x02, 0x60, 0x19, 0x00, 0x82, 0x28, 0x01, 0x64, 0x29, 0xdc, + 0x6f, 0xa3, 0x49, 0xad, 0x40, 0x02, 0x69, 0x35, 0x89, 0x00, 0x00, 0x00, 0x66, 0xc6, 0xd9, 0x22, 0x51, 0x00, 0xff, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x8d, 0xc1, 0x10, 0x80, 0x01, 0x24, 0x42, 0x00, 0x68, 0x0a, 0x36, 0x00, 0x9a, 0x4d, + 0x62, 0x40, 0x00, 0x00, 0x19, 0xb8, 0xdb, 0x24, 0x48, 0x01, 0x00, 0x04, 0x17, 0x12, 0x8c, 0x78, 0x01, 0x25, 0x18, + 0x83, 0x70, 0xc6, 0xe3, 0xa2, 0x47, 0x01, 0x80, 0x22, 0x07, 0x03, 0x00, 0x10, 0x1e, 0x23, 0x00, 0xd2, 0x4b, 0x81, + 0xb5, 0x00, 0x02, 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0xe1, 0x10, 0x40, 0x00, 0x92, 0x22, 0x4a, 0x00, 0x00, + 0x90, 0x40, 0x00, 0x04, 0x0d, 0x3a, 0x00, 0x08, 0x02, 0x91, 0x8a, 0x92, 0x42, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x4e, 0x04, 0x08, 0x10, 0x20, 0x40, 0x81, 0x02, 0x08, 0x00, 0x00, 0x21, 0x40, 0x00, 0x23, 0x34, 0x1c, + 0x01, 0x0c, 0xc8, 0x50, 0x09, 0x08, 0x60, 0x51, 0x00, 0xab, 0x2a, 0x22, 0x24, 0x40, 0x02, 0x90, 0x5f, 0xfd, 0x29, + 0x49, 0x8c, 0x63, 0x62, 0x45, 0x6a, 0x00, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x04, 0x10, 0x00, 0x71, 0x00, 0x04, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x8a, 0x04, 0x94, 0x0b, 0xc3, 0xe0, 0x06, 0x80, 0x40, 0x00}; + + asn1::cbit_ref bref(msg, sizeof(msg)); + asn1::rrc::dl_dcch_msg_s dl_dcch_msg; + + TESTASSERT(dl_dcch_msg.unpack(bref) == asn1::SRSASN_SUCCESS); + TESTASSERT(dl_dcch_msg.msg.type().value == dl_dcch_msg_type_c::types_opts::c1); + + dl_dcch_msg_type_c::c1_c_* c1 = &dl_dcch_msg.msg.c1(); + rrc_conn_recfg_r8_ies_s rx_recfg = c1->rrc_conn_recfg().crit_exts.c1().rrc_conn_recfg_r8(); + + const asn1::rrc::rrc_conn_recfg_v1510_ies_s rrc_conn_recfg_v1510_ies = + rx_recfg.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext; + + bool endc_release_and_add_r15 = false; + + asn1::rrc_nr::rrc_recfg_s rrc_nr_reconf = {}; + rrc_nr_reconf.crit_exts.set_rrc_recfg(); + + TESTASSERT(rrc_conn_recfg_v1510_ies.nr_cfg_r15.type() == setup_opts::options::setup); + + endc_release_and_add_r15 = rrc_conn_recfg_v1510_ies.nr_cfg_r15.setup().endc_release_and_add_r15; + + TESTASSERT(rrc_conn_recfg_v1510_ies.nr_cfg_r15.setup().nr_secondary_cell_group_cfg_r15_present); + + asn1::cbit_ref bref0(rrc_conn_recfg_v1510_ies.nr_cfg_r15.setup().nr_secondary_cell_group_cfg_r15.data(), + rrc_conn_recfg_v1510_ies.nr_cfg_r15.setup().nr_secondary_cell_group_cfg_r15.size()); + + asn1::rrc_nr::rrc_recfg_s secondary_cell_group_r15; + TESTASSERT(secondary_cell_group_r15.unpack(bref0) == asn1::SRSASN_SUCCESS); + + TESTASSERT(secondary_cell_group_r15.crit_exts.rrc_recfg().secondary_cell_group_present); + asn1::cbit_ref bref1(secondary_cell_group_r15.crit_exts.rrc_recfg().secondary_cell_group.data(), + secondary_cell_group_r15.crit_exts.rrc_recfg().secondary_cell_group.size()); + + asn1::rrc_nr::cell_group_cfg_s cell_group_cfg; + TESTASSERT(cell_group_cfg.unpack(bref1) == asn1::SRSASN_SUCCESS); + + rrc_nr_reconf.crit_exts.rrc_recfg().secondary_cell_group_present = true; + rrc_nr_reconf.crit_exts.rrc_recfg().secondary_cell_group = + secondary_cell_group_r15.crit_exts.rrc_recfg().secondary_cell_group; + + TESTASSERT(rrc_conn_recfg_v1510_ies.sk_counter_r15_present); + rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext_present = true; + rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.non_crit_ext_present = true; + rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.non_crit_ext.non_crit_ext_present = true; + rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.non_crit_ext.non_crit_ext.sk_counter_present = true; + rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.non_crit_ext.non_crit_ext.sk_counter = + rrc_conn_recfg_v1510_ies.sk_counter_r15; + + TESTASSERT(rrc_conn_recfg_v1510_ies.nr_radio_bearer_cfg1_r15_present); + rrc_nr_reconf.crit_exts.rrc_recfg().radio_bearer_cfg_present = true; + asn1::rrc_nr::radio_bearer_cfg_s radio_bearer_conf = {}; + asn1::cbit_ref bref2(rrc_conn_recfg_v1510_ies.nr_radio_bearer_cfg1_r15.data(), + rrc_conn_recfg_v1510_ies.nr_radio_bearer_cfg1_r15.size()); + TESTASSERT(radio_bearer_conf.unpack(bref2) == asn1::SRSASN_SUCCESS); + + rrc_nr_reconf.crit_exts.rrc_recfg().radio_bearer_cfg = radio_bearer_conf; + + rrc_nr.rrc_reconfiguration(endc_release_and_add_r15, rrc_nr_reconf); + + task_sched.run_pending_tasks(); + return SRSRAN_SUCCESS; +} + +int rrc_nr_setup_request_test() +{ + srslog::basic_logger& logger = srslog::fetch_basic_logger("RRC-NR"); + logger.set_level(srslog::basic_levels::debug); + logger.set_hex_dump_max_size(-1); + srsran::task_scheduler task_sched{512, 100}; + srsran::task_sched_handle task_sched_handle(&task_sched); + rrc_nr rrc_nr(task_sched_handle); + srsran::byte_buffer_t caps; + + dummy_phy dummy_phy; + dummy_mac dummy_mac; + dummy_rlc dummy_rlc; + dummy_pdcp dummy_pdcp; + dummy_gw dummy_gw; + dummy_eutra dummy_eutra; + dummy_sim dummy_sim; + dummy_stack dummy_stack; + rrc_nr_args_t rrc_nr_args; + + rrc_nr_args.supported_bands_nr.push_back(78); + + TESTASSERT(rrc_nr.init(&dummy_phy, + &dummy_mac, + &dummy_rlc, + &dummy_pdcp, + &dummy_gw, + &dummy_eutra, + &dummy_sim, + task_sched.get_timer_handler(), + &dummy_stack, + rrc_nr_args) == SRSRAN_SUCCESS); + rrc_nr.connection_request(srsran::nr_establishment_cause_t::mt_Access, nullptr); + task_sched.run_pending_tasks(); + + TESTASSERT(dummy_rlc.last_lcid == 0); // SRB0 transmission + TESTASSERT(dummy_rlc.last_sdu->N_bytes == 6); // RRC Setup Request is 6 Bytes long + + return SRSRAN_SUCCESS; +} + +int rrc_nr_sib1_decoding_test() +{ + srslog::basic_logger& logger = srslog::fetch_basic_logger("RRC-NR"); + logger.set_level(srslog::basic_levels::debug); + logger.set_hex_dump_max_size(-1); + srsran::task_scheduler task_sched{512, 100}; + srsran::task_sched_handle task_sched_handle(&task_sched); + rrc_nr rrc_nr(task_sched_handle); + + dummy_phy dummy_phy; + dummy_mac dummy_mac; + dummy_rlc dummy_rlc; + dummy_pdcp dummy_pdcp; + dummy_gw dummy_gw; + dummy_eutra dummy_eutra; + dummy_sim dummy_sim; + dummy_stack dummy_stack; + rrc_nr_args_t rrc_nr_args; + TESTASSERT(rrc_nr.init(&dummy_phy, + &dummy_mac, + &dummy_rlc, + &dummy_pdcp, + &dummy_gw, + &dummy_eutra, + &dummy_sim, + task_sched.get_timer_handler(), + &dummy_stack, + rrc_nr_args) == SRSRAN_SUCCESS); + + uint8_t msg[] = {0x74, 0x81, 0x01, 0x70, 0x10, 0x40, 0x04, 0x02, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x33, 0x60, 0x38, + 0x05, 0x01, 0x00, 0x40, 0x1a, 0x00, 0x00, 0x06, 0x6c, 0x6d, 0x92, 0x21, 0xf3, 0x70, 0x40, 0x20, + 0x00, 0x00, 0x80, 0x80, 0x00, 0x41, 0x06, 0x80, 0xa0, 0x90, 0x9c, 0x20, 0x08, 0x55, 0x19, 0x40, + 0x00, 0x00, 0x33, 0xa1, 0xc6, 0xd9, 0x22, 0x40, 0x00, 0x00, 0x20, 0xb8, 0x94, 0x63, 0xc0, 0x09, + 0x28, 0x44, 0x1b, 0x7e, 0xad, 0x8e, 0x1d, 0x00, 0x9e, 0x2d, 0xa3, 0x0a}; + + srsran::unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + memcpy(pdu->msg, msg, sizeof(msg)); + pdu->N_bytes = sizeof(msg); + + rrc_nr.write_pdu_bcch_dlsch(std::move(pdu)); + task_sched.run_pending_tasks(); + + return SRSRAN_SUCCESS; +} + +int rrc_nr_setup_test() +{ + srslog::basic_logger& logger = srslog::fetch_basic_logger("RRC-NR"); + logger.set_level(srslog::basic_levels::debug); + logger.set_hex_dump_max_size(-1); + srsran::task_scheduler task_sched{512, 100}; + srsran::task_sched_handle task_sched_handle(&task_sched); + rrc_nr rrc_nr(task_sched_handle); + + dummy_phy dummy_phy; + dummy_mac dummy_mac; + dummy_rlc dummy_rlc; + dummy_pdcp dummy_pdcp; + dummy_gw dummy_gw; + dummy_eutra dummy_eutra; + dummy_sim dummy_sim; + dummy_stack dummy_stack; + rrc_nr_args_t rrc_nr_args; + TESTASSERT(rrc_nr.init(&dummy_phy, + &dummy_mac, + &dummy_rlc, + &dummy_pdcp, + &dummy_gw, + &dummy_eutra, + &dummy_sim, + task_sched.get_timer_handler(), + &dummy_stack, + rrc_nr_args) == SRSRAN_SUCCESS); + + uint8_t msg[] = {0x20, 0x40, 0x04, 0x05, 0x9a, 0xe0, 0x05, 0x80, 0x08, 0x8b, 0xd7, 0x63, 0x80, 0x83, 0x0f, 0x00, 0x03, + 0xa0, 0x10, 0x45, 0x41, 0xc2, 0x0a, 0x20, 0x92, 0x40, 0x0c, 0xa8, 0x00, 0x17, 0xf8, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x37, 0x08, 0x82, 0x00, 0x04, 0x91, 0x12, 0x50, 0x00, 0x04, 0x82, 0x00, 0x00, 0x20, 0x69, + 0x84, 0x0c, 0x55, 0x92, 0x10, 0x70, 0x00, 0x41, 0x03, 0x08, 0x14, 0x30, 0x72, 0x71, 0x02, 0x45, 0x0b, + 0x18, 0x34, 0x70, 0xf2, 0x38, 0x01, 0x98, 0x00, 0x85, 0x00, 0xc0, 0x8c, 0xc0, 0x05, 0x28, 0x06, 0x08, + 0x66, 0x00, 0x31, 0x40, 0x30, 0x63, 0x30, 0x01, 0x0a, 0x03, 0x84, 0x19, 0x80, 0x0a, 0x50, 0x1c, 0x28, + 0xcc, 0x00, 0x62, 0x80, 0xe1, 0x86, 0x60, 0x02, 0x14, 0x0b, 0x0e, 0x33, 0x00, 0x14, 0xa0, 0x58, 0x80, + 0x08, 0xc9, 0x04, 0x31, 0x20, 0x11, 0x92, 0x09, 0x62, 0x80, 0x23, 0x24, 0x14, 0xc5, 0x80, 0x46, 0x48, + 0x2d, 0x8c, 0x00, 0x8c, 0x90, 0x63, 0x1a, 0x01, 0x19, 0x20, 0xd6, 0x38, 0x02, 0x32, 0x41, 0xcc, 0x78, + 0xc8, 0x02, 0x82, 0x19, 0x01, 0x98, 0x00, 0xc5, 0x02, 0xc8, 0x8c, 0x80, 0x28, 0x25, 0x02, 0x42, 0x18, + 0x14, 0x40, 0x20, 0x91, 0x00, 0x0a, 0x41, 0x7f, 0xf4, 0xa5, 0x26, 0x31, 0x8d, 0x80}; + + srsran::unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + memcpy(pdu->msg, msg, sizeof(msg)); + pdu->N_bytes = sizeof(msg); + + rrc_nr.write_pdu(0, std::move(pdu)); + task_sched.run_pending_tasks(); + + return SRSRAN_SUCCESS; +} + int main(int argc, char** argv) { + srslog::init(); + TESTASSERT(rrc_nr_cap_request_test() == SRSRAN_SUCCESS); - TESTASSERT(rrc_nr_reconfig_test() == SRSRAN_SUCCESS); + TESTASSERT(rrc_nsa_reconfig_tdd_test() == SRSRAN_SUCCESS); + TESTASSERT(rrc_nsa_reconfig_fdd_test() == SRSRAN_SUCCESS); + TESTASSERT(rrc_nr_setup_request_test() == SRSRAN_SUCCESS); + TESTASSERT(rrc_nr_sib1_decoding_test() == SRSRAN_SUCCESS); + TESTASSERT(rrc_nr_setup_test() == SRSRAN_SUCCESS); + return SRSRAN_SUCCESS; } diff --git a/srsue/src/stack/ue_stack_lte.cc b/srsue/src/stack/ue_stack_lte.cc index fe222eb21..6f54aae1a 100644 --- a/srsue/src/stack/ue_stack_lte.cc +++ b/srsue/src/stack/ue_stack_lte.cc @@ -57,6 +57,7 @@ ue_stack_lte::ue_stack_lte() : pdcp(&task_sched, "PDCP"), pdcp_nr(&task_sched, "PDCP-NR"), nas(srslog::fetch_basic_logger("NAS", false), &task_sched), + nas_5g(srslog::fetch_basic_logger("NAS5G", false), &task_sched), thread("STACK"), task_sched(512, 64), tti_tprof("tti_tprof", "STCK", TTI_STAT_PERIOD) @@ -227,6 +228,10 @@ int ue_stack_lte::init(const stack_args_t& args_) phy_nr, &mac_nr, &rlc_nr, &pdcp_nr, gw, &rrc, usim.get(), task_sched.get_timer_handler(), this, args.rrc_nr); rrc.init(phy, &mac, &rlc, &pdcp, &nas, usim.get(), gw, &rrc_nr, args.rrc); + args.nas_5g.ia5g = "0,1,2,3"; + args.nas_5g.ea5g = "0,1,2,3"; + nas_5g.init(usim.get(), &rrc_nr, gw, args.nas_5g); + running = true; start(STACK_MAIN_THREAD_PRIO); @@ -247,6 +252,7 @@ void ue_stack_lte::stop_impl() usim->stop(); nas.stop(); + nas_5g.stop(); rrc.stop(); rlc.stop(); @@ -270,8 +276,14 @@ void ue_stack_lte::stop_impl() bool ue_stack_lte::switch_on() { if (running) { - stack_logger.info("Triggering NAS switch on\n"); - if (!ue_task_queue.try_push([this]() { nas.switch_on(); })) { + stack_logger.info("Triggering NAS switch on"); + if (!ue_task_queue.try_push([this]() { + if (args.attach_on_nr) { + nas_5g.switch_on(); + } else { + nas.switch_on(); + } + })) { stack_logger.error("Triggering NAS switch on: ue_task_queue is full\n"); } } else { @@ -486,6 +498,7 @@ void ue_stack_lte::run_tti_impl(uint32_t tti, uint32_t tti_jump) rrc.run_tti(); rrc_nr.run_tti(tti); nas.run_tti(); + nas_5g.run_tti(); if (args.have_tti_time_stats) { std::chrono::nanoseconds dur = tti_tprof.stop(); diff --git a/srsue/src/stack/ue_stack_nr.cc b/srsue/src/stack/ue_stack_nr.cc index 4e1f25eeb..b8a4d79d7 100644 --- a/srsue/src/stack/ue_stack_nr.cc +++ b/srsue/src/stack/ue_stack_nr.cc @@ -194,7 +194,7 @@ void ue_stack_nr::out_of_sync() // pending_tasks.push(sync_task_queue, task_t{[this](task_t*) { rrc.out_of_sync(); }}); } -void ue_stack_nr::run_tti(uint32_t tti) +void ue_stack_nr::run_tti(uint32_t tti, uint32_t tti_jump) { sync_task_queue.push([this, tti]() { run_tti_impl(tti); }); } diff --git a/srsue/src/test/ttcn3/hdr/lte_ttcn3_phy.h b/srsue/src/test/ttcn3/hdr/lte_ttcn3_phy.h index 729e62e98..f906dcbd3 100644 --- a/srsue/src/test/ttcn3/hdr/lte_ttcn3_phy.h +++ b/srsue/src/test/ttcn3/hdr/lte_ttcn3_phy.h @@ -25,7 +25,7 @@ #include "srsran/common/task_scheduler.h" #include "srsran/interfaces/ue_interfaces.h" #include "srsran/interfaces/ue_phy_interfaces.h" -#include "srsue/hdr/phy/ue_lte_phy_base.h" +#include "srsue/hdr/phy/ue_phy_base.h" #include "srsue/hdr/ue.h" #include "ttcn3_interfaces.h" #include @@ -35,7 +35,7 @@ using namespace srsran; namespace srsue { -class lte_ttcn3_phy : public ue_lte_phy_base +class lte_ttcn3_phy : public ue_phy_base, public phy_interface_stack_lte { public: void set_cells_to_meas(uint32_t earfcn, const std::set& pci) override; @@ -51,12 +51,9 @@ public: int init(const phy_args_t& args_, stack_interface_phy_lte* stack_, syssim_interface_phy* syssim_); - int init(const phy_args_t& args_, stack_interface_phy_lte* stack_, srsran::radio_interface_phy* radio_) override; - - // ue_phy_base interface - int init(const phy_args_t& args_) override; void stop() override; void wait_initialize() override; + bool is_initialized() override; void start_plot() override; void get_metrics(const srsran::srsran_rat_t& rat, phy_metrics_t* m) override; std::string get_type() override; @@ -105,10 +102,6 @@ public: void new_grant_ul(mac_interface_phy_lte::mac_grant_ul_t ul_mac_grant); void new_tb(const srsue::mac_interface_phy_lte::mac_grant_dl_t, const uint8_t* data); - // Radio interface - void radio_overflow() override; - void radio_failure() override; - void run_tti(); private: diff --git a/srsue/src/test/ttcn3/src/lte_ttcn3_phy.cc b/srsue/src/test/ttcn3/src/lte_ttcn3_phy.cc index 5cb64bc97..ca34e0f27 100644 --- a/srsue/src/test/ttcn3/src/lte_ttcn3_phy.cc +++ b/srsue/src/test/ttcn3/src/lte_ttcn3_phy.cc @@ -36,17 +36,6 @@ int lte_ttcn3_phy::init(const phy_args_t& args_, stack_interface_phy_lte* stack_ stack = stack_; syssim = syssim_; - return init(args_); -} - -int lte_ttcn3_phy::init(const phy_args_t& args_, stack_interface_phy_lte* stack_, srsran::radio_interface_phy* radio_) -{ - return init(args_); -} - -// ue_phy_base interface -int lte_ttcn3_phy::init(const phy_args_t& args_) -{ logger.set_level(srslog::str_to_basic_level(args_.log.phy_level)); logger.set_hex_dump_max_size(-1); @@ -55,6 +44,11 @@ int lte_ttcn3_phy::init(const phy_args_t& args_) void lte_ttcn3_phy::stop(){}; +bool lte_ttcn3_phy::is_initialized() +{ + return true; +} + void lte_ttcn3_phy::wait_initialize() {} void lte_ttcn3_phy::start_plot() {} @@ -344,16 +338,6 @@ void lte_ttcn3_phy::new_tb(const srsue::mac_interface_phy_lte::mac_grant_dl_t dl stack->tb_decoded(cc_idx, dl_grant, dl_ack); } -void lte_ttcn3_phy::radio_overflow() -{ - logger.debug("%s not implemented.", __FUNCTION__); -} - -void lte_ttcn3_phy::radio_failure() -{ - logger.debug("%s not implemented.", __FUNCTION__); -} - // Calling function set_tti() is holding mutex void lte_ttcn3_phy::run_tti() { diff --git a/srsue/src/ue.cc b/srsue/src/ue.cc index 32a67b345..32f8fd61d 100644 --- a/srsue/src/ue.cc +++ b/srsue/src/ue.cc @@ -26,7 +26,9 @@ #include "srsran/radio/radio.h" #include "srsran/radio/radio_null.h" #include "srsran/srsran.h" +#include "srsue/hdr/phy/dummy_phy.h" #include "srsue/hdr/phy/phy.h" +#include "srsue/hdr/phy/phy_nr_sa.h" #include "srsue/hdr/stack/ue_stack_lte.h" #include "srsue/hdr/stack/ue_stack_nr.h" #include @@ -75,30 +77,12 @@ int ue::init(const all_args_t& args_) return SRSRAN_ERROR; } - std::unique_ptr lte_phy = std::unique_ptr(new srsue::phy); - if (!lte_phy) { - srsran::console("Error creating LTE PHY instance.\n"); - return SRSRAN_ERROR; - } - std::unique_ptr lte_radio = std::unique_ptr(new srsran::radio); if (!lte_radio) { srsran::console("Error creating radio multi instance.\n"); return SRSRAN_ERROR; } - // init layers - if (lte_radio->init(args.rf, lte_phy.get())) { - srsran::console("Error initializing radio.\n"); - return SRSRAN_ERROR; - } - - // from here onwards do not exit immediately if something goes wrong as sub-layers may already use interfaces - if (lte_phy->init(args.phy, lte_stack.get(), lte_radio.get())) { - srsran::console("Error initializing PHY.\n"); - ret = SRSRAN_ERROR; - } - srsue::phy_args_nr_t phy_args_nr = {}; phy_args_nr.max_nof_prb = args.phy.nr_max_nof_prb; phy_args_nr.rf_channel_offset = args.phy.nof_lte_carriers; @@ -107,14 +91,68 @@ int ue::init(const all_args_t& args_) phy_args_nr.worker_cpu_mask = args.phy.worker_cpu_mask; phy_args_nr.log = args.phy.log; phy_args_nr.store_pdsch_ko = args.phy.nr_store_pdsch_ko; - if (lte_phy->init(phy_args_nr, lte_stack.get(), lte_radio.get())) { - srsran::console("Error initializing NR PHY.\n"); - ret = SRSRAN_ERROR; - } - if (lte_stack->init(args.stack, lte_phy.get(), lte_phy.get(), gw_ptr.get())) { - srsran::console("Error initializing stack.\n"); - ret = SRSRAN_ERROR; + // init layers + if (args.phy.nof_lte_carriers == 0) { + // SA mode + std::unique_ptr nr_phy = std::unique_ptr(new srsue::phy_nr_sa("PHY-SA")); + if (!nr_phy) { + srsran::console("Error creating NR PHY instance.\n"); + return SRSRAN_ERROR; + } + + // In SA mode, pass the NR SA phy to the radio + if (lte_radio->init(args.rf, nr_phy.get())) { + srsran::console("Error initializing radio.\n"); + return SRSRAN_ERROR; + } + if (nr_phy->init(phy_args_nr, lte_stack.get(), lte_radio.get())) { + srsran::console("Error initializing PHY NR SA.\n"); + ret = SRSRAN_ERROR; + } + + std::unique_ptr dummy_lte_phy = std::unique_ptr(new srsue::dummy_phy); + if (!dummy_lte_phy) { + srsran::console("Error creating dummy LTE PHY instance.\n"); + return SRSRAN_ERROR; + } + + // In SA mode, pass NR PHY pointer to stack + args.stack.attach_on_nr = true; + if (lte_stack->init(args.stack, dummy_lte_phy.get(), nr_phy.get(), gw_ptr.get())) { + srsran::console("Error initializing stack.\n"); + ret = SRSRAN_ERROR; + } + phy = std::move(nr_phy); + dummy_phy = std::move(dummy_lte_phy); + } else { + // LTE or NSA mode + std::unique_ptr lte_phy = std::unique_ptr(new srsue::phy); + if (!lte_phy) { + srsran::console("Error creating LTE PHY instance.\n"); + return SRSRAN_ERROR; + } + + if (lte_radio->init(args.rf, lte_phy.get())) { + srsran::console("Error initializing radio.\n"); + return SRSRAN_ERROR; + } + // from here onwards do not exit immediately if something goes wrong as sub-layers may already use interfaces + if (lte_phy->init(args.phy, lte_stack.get(), lte_radio.get())) { + srsran::console("Error initializing PHY.\n"); + ret = SRSRAN_ERROR; + } + if (args.phy.nof_nr_carriers > 0) { + if (lte_phy->init(phy_args_nr, lte_stack.get(), lte_radio.get())) { + srsran::console("Error initializing NR PHY.\n"); + ret = SRSRAN_ERROR; + } + } + if (lte_stack->init(args.stack, lte_phy.get(), lte_phy.get(), gw_ptr.get())) { + srsran::console("Error initializing stack.\n"); + ret = SRSRAN_ERROR; + } + phy = std::move(lte_phy); } if (gw_ptr->init(args.gw, lte_stack.get())) { @@ -125,7 +163,6 @@ int ue::init(const all_args_t& args_) // move ownership stack = std::move(lte_stack); gw_inst = std::move(gw_ptr); - phy = std::move(lte_phy); radio = std::move(lte_radio); if (phy) { @@ -133,7 +170,6 @@ int ue::init(const all_args_t& args_) phy->wait_initialize(); srsran::console("done!\n"); } - return ret; } diff --git a/test/phy/CMakeLists.txt b/test/phy/CMakeLists.txt index 4869d31b2..c16bbeded 100644 --- a/test/phy/CMakeLists.txt +++ b/test/phy/CMakeLists.txt @@ -40,6 +40,7 @@ if (RF_FOUND AND ENABLE_SRSUE AND ENABLE_SRSENB) target_link_libraries(nr_phy_test srsue_phy srsran_common + rrc_nr_asn1 srsran_phy srsran_radio srsenb_phy diff --git a/test/phy/dummy_ue_phy.h b/test/phy/dummy_ue_phy.h new file mode 100644 index 000000000..2830ca229 --- /dev/null +++ b/test/phy/dummy_ue_phy.h @@ -0,0 +1,65 @@ +/** + * + * \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 SRSRAN_DUMMY_UE_PHY_H +#define SRSRAN_DUMMY_UE_PHY_H + +#include "srsue/hdr/phy/nr/worker_pool.h" + +class ue_dummy_phy : public srsue::phy_interface_stack_nr +{ +public: + ue_dummy_phy(const char* logname, uint32_t max_workers) : + logger(srslog::fetch_basic_logger(logname)), workers(logger, max_workers) + {} + bool init(const srsue::phy_args_nr_t& args_, + srsran::phy_common_interface& common, + srsue::stack_interface_phy_nr* stack_, + int prio) + { + return workers.init(args_, common, stack_, prio); + } + void stop() { workers.stop(); } + srsue::nr::sf_worker* wait_worker(uint32_t tti) { return workers.wait_worker(tti); } + void start_worker(srsue::nr::sf_worker* w) { workers.start_worker(w); } + void get_metrics(srsue::phy_metrics_t& m) { return workers.get_metrics(m); } + + int set_rar_grant(uint32_t rar_slot_idx, + std::array packed_ul_grant, + uint16_t rnti, + srsran_rnti_type_t rnti_type) override + { + return workers.set_rar_grant(rar_slot_idx, packed_ul_grant, rnti, rnti_type); + } + 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 + {} + + bool has_valid_sr_resource(uint32_t sr_id) override { return workers.has_valid_sr_resource(sr_id); } + void clear_pending_grants() override { return workers.clear_pending_grants(); } + + bool set_config(const srsran::phy_cfg_nr_t& cfg) override { return workers.set_config(cfg); } + phy_nr_state_t get_state() override { return PHY_NR_STATE_IDLE; } + void reset_nr() override {} + + bool start_cell_search(const cell_search_args_t& req) override { return false; } + + bool start_cell_select(const cell_select_args_t& req) override { return false; } + +private: + srslog::basic_logger& logger; + srsue::nr::worker_pool workers; +}; + +#endif // SRSRAN_DUMMY_UE_PHY_H diff --git a/test/phy/dummy_ue_stack.h b/test/phy/dummy_ue_stack.h index 5b969e237..95916d538 100644 --- a/test/phy/dummy_ue_stack.h +++ b/test/phy/dummy_ue_stack.h @@ -22,7 +22,10 @@ #ifndef SRSRAN_DUMMY_UE_STACK_H #define SRSRAN_DUMMY_UE_STACK_H -#include +#include "dummy_rx_harq_proc.h" +#include "dummy_tx_harq_proc.h" +#include "srsran/asn1/rrc_nr.h" +#include "srsran/interfaces/ue_nr_interfaces.h" class ue_dummy_stack : public srsue::stack_interface_phy_nr { @@ -37,6 +40,7 @@ public: }; private: + srslog::basic_logger& logger = srslog::fetch_basic_logger("UE-STCK"); std::mutex rnti_mutex; srsran_random_t random_gen = srsran_random_init(0x1323); srsran_rnti_type_t dl_rnti_type = srsran_rnti_type_c; @@ -55,20 +59,29 @@ private: public: struct args_t { - uint16_t rnti = 0x1234; ///< C-RNTI for PUSCH and PDSCH transmissions - uint32_t sr_period = 0; ///< Indicates positive SR period in number of opportunities. Set to 0 to disable. - uint32_t prach_period = 0; ///< Requests PHY to transmit PRACH periodically in frames. Set to 0 to disable. + uint16_t rnti = 0x1234; ///< C-RNTI for PUSCH and PDSCH transmissions + uint32_t sr_period = 0; ///< Indicates positive SR period in number of opportunities. Set to 0 to disable. + uint32_t prach_period = 0; ///< Requests PHY to transmit PRACH periodically in frames. Set to 0 to disable. + std::string log_level = "warning"; }; ue_dummy_stack(const args_t& args, srsue::phy_interface_stack_nr& phy_) : rnti(args.rnti), sr_period(args.sr_period), prach_period(args.prach_period), phy(phy_) { + logger.set_level(srslog::str_to_basic_level(args.log_level)); valid = true; } ~ue_dummy_stack() { srsran_random_free(random_gen); } + + virtual void wait_tti() + { + // Do nothing + } void in_sync() override {} void out_of_sync() override {} - void run_tti(const uint32_t tti) override + void run_tti(const uint32_t tti, const uint32_t tti_jump) override { + wait_tti(); + // Run PRACH if (prach_period != 0) { uint32_t slot_idx = tti % SRSRAN_NSLOTS_PER_FRAME_NR(srsran_subcarrier_spacing_15kHz); @@ -80,7 +93,6 @@ public: } } } - int sf_indication(const uint32_t tti) override { return 0; } sched_rnti_t get_dl_sched_rnti_nr(const uint32_t tti) override { std::unique_lock lock(rnti_mutex); @@ -133,9 +145,37 @@ public: metrics_t get_metrics() { return metrics; } - void set_phy_config_complete(bool status) override - { + void set_phy_config_complete(bool status) override {} + void cell_search_found_cell(const cell_search_result_t& result) override + { + if (result.cell_found) { + // Unpack MIB with ASN1 + asn1::rrc_nr::mib_s mib_asn1; + asn1::cbit_ref cbit(result.pbch_msg.payload, SRSRAN_PBCH_MSG_NR_SZ); + mib_asn1.unpack(cbit); + + // Convert MIB to JSON + asn1::json_writer json; + mib_asn1.to_json(json); + + // Unpack MIB with C lib + srsran_mib_nr_t mib_c = {}; + srsran_pbch_msg_nr_mib_unpack(&result.pbch_msg, &mib_c); + + // Convert MIB from C lib to info + std::array mib_info = {}; + srsran_pbch_msg_nr_mib_info(&mib_c, mib_info.data(), (uint32_t)mib_info.size()); + + // Convert CSI to string + std::array csi_info = {}; + srsran_csi_meas_info_short(&result.measurements, csi_info.data(), (uint32_t)csi_info.size()); + + logger.info( + "Cell found pci=%d %s %s ASN1: %s", result.pci, mib_info.data(), csi_info.data(), json.to_string().c_str()); + } else { + logger.info("Cell not found\n"); + } } }; diff --git a/test/phy/test_bench.h b/test/phy/test_bench.h index 1dc9c45e3..115d123aa 100644 --- a/test/phy/test_bench.h +++ b/test/phy/test_bench.h @@ -23,6 +23,7 @@ #define SRSRAN_TEST_BENCH_H #include "dummy_phy_common.h" +#include "dummy_ue_phy.h" #include "srsenb/hdr/phy/nr/worker_pool.h" #include "srsue/hdr/phy/nr/worker_pool.h" @@ -39,7 +40,7 @@ private: srsenb::nr::worker_pool gnb_phy; phy_common gnb_phy_com; ue_dummy_stack ue_stack; - srsue::nr::worker_pool ue_phy; + ue_dummy_phy ue_phy; phy_common ue_phy_com; bool initialised = false; uint32_t sf_sz = 0; @@ -79,7 +80,7 @@ public: gnb_stack(args.gnb_stack), gnb_phy(gnb_phy_com, gnb_stack, srslog::get_default_sink(), args.gnb_phy.nof_phy_threads), ue_stack(args.ue_stack, ue_phy), - ue_phy(args.ue_phy.nof_phy_threads), + ue_phy("PHY", args.ue_phy.nof_phy_threads), ue_phy_com(phy_common::args_t(args.srate_hz, args.buffer_sz_ms, args.nof_channels), srslog::fetch_basic_logger(UE_PHY_COM_LOG_NAME, srslog::get_default_sink(), false)), @@ -207,7 +208,7 @@ public: ue_worker->set_context(ue_context); // Run UE stack - ue_stack.run_tti(slot_idx); + ue_stack.run_tti(slot_idx, 1); // Start UE work ue_phy_com.push_semaphore(ue_worker);