diff --git a/CMakeLists.txt b/CMakeLists.txt index 599dee8c4..1edbb4b75 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,6 +72,7 @@ option(ENABLE_GUI "Enable GUI (using srsGUI)" ON) option(ENABLE_UHD "Enable UHD" ON) option(ENABLE_BLADERF "Enable BladeRF" ON) option(ENABLE_SOAPYSDR "Enable SoapySDR" ON) +option(ENABLE_SKIQ "Enable Sidekiq SDK" ON) option(ENABLE_ZEROMQ "Enable ZeroMQ" ON) option(ENABLE_HARDSIM "Enable support for SIM cards" ON) @@ -223,6 +224,15 @@ if(ENABLE_UHD) endif(UHD_FOUND) endif(ENABLE_UHD) +# SKIQ +if (ENABLE_SKIQ) + find_package(SKIQ) + if(SKIQ_FOUND) + include_directories(${SKIQ_INCLUDE_DIRS}) + link_directories(${SKIQ_LIBRARY_DIRS}) + endif(SKIQ_FOUND) +endif (ENABLE_SKIQ) + # BladeRF if(ENABLE_BLADERF) find_package(bladeRF) @@ -255,12 +265,12 @@ if(ENABLE_TIMEPROF) add_definitions(-DENABLE_TIMEPROF) endif(ENABLE_TIMEPROF) -if(BLADERF_FOUND OR UHD_FOUND OR SOAPYSDR_FOUND OR ZEROMQ_FOUND) +if(BLADERF_FOUND OR UHD_FOUND OR SOAPYSDR_FOUND OR ZEROMQ_FOUND OR SKIQ_FOUND) set(RF_FOUND TRUE CACHE INTERNAL "RF frontend found") -else(BLADERF_FOUND OR UHD_FOUND OR SOAPYSDR_FOUND OR ZEROMQ_FOUND) +else(BLADERF_FOUND OR UHD_FOUND OR SOAPYSDR_FOUND OR ZEROMQ_FOUND OR SKIQ_FOUND) set(RF_FOUND FALSE CACHE INTERNAL "RF frontend found") add_definitions(-DDISABLE_RF) -endif(BLADERF_FOUND OR UHD_FOUND OR SOAPYSDR_FOUND OR ZEROMQ_FOUND) +endif(BLADERF_FOUND OR UHD_FOUND OR SOAPYSDR_FOUND OR ZEROMQ_FOUND OR SKIQ_FOUND) # Boost if(BUILD_STATIC) diff --git a/cmake/modules/FindSKIQ.cmake b/cmake/modules/FindSKIQ.cmake new file mode 100644 index 000000000..5d63fa47d --- /dev/null +++ b/cmake/modules/FindSKIQ.cmake @@ -0,0 +1,66 @@ +# +# 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(FindPkgConfig) +#PKG_CHECK_MODULES(SKIQ SKIQ) +IF(NOT SKIQ_FOUND) + +FIND_PATH( + SKIQ_INCLUDE_DIRS + NAMES sidekiq_api.h + HINTS $ENV{SKIQ_DIR}/inc + $ENV{SKIQ_DIR}/sidekiq_core/inc + PATHS /usr/local/include + /usr/include +) + +FIND_LIBRARY( + SKIQ_LIBRARY + NAMES sidekiq__x86_64.gcc + HINTS $ENV{SKIQ_DIR}/lib + PATHS /usr/local/lib + /usr/lib + /usr/lib/x86_64-linux-gnu + /usr/local/lib64 + /usr/local/lib32 +) + +FIND_LIBRARY( + SKIQ_LIBRARY_GLIB + NAMES libglib-2.0.a + HINTS $ENV{SKIQ_DIR}/lib/support/x86_64.gcc/usr/lib/epiq + PATHS /usr/local/lib + /usr/lib + /usr/lib/epiq + /usr/lib/x86_64-linux-gnu + /usr/local/lib64 + /usr/local/lib32 +) + +FIND_LIBRARY( + SKIQ_LIBRARY_USB + NAMES libusb-1.0.a + HINTS $ENV{SKIQ_DIR}/lib/support/x86_64.gcc/usr/lib/epiq + PATHS /usr/local/lib + /usr/lib + /usr/lib/epiq + /usr/lib/x86_64-linux-gnu + /usr/local/lib64 + /usr/local/lib32 +) + +set(SKIQ_LIBRARIES ${SKIQ_LIBRARY} ${SKIQ_LIBRARY_GLIB} ${SKIQ_LIBRARY_USB}) + +message(STATUS "SKIQ LIBRARIES " ${SKIQ_LIBRARIES}) +message(STATUS "SKIQ INCLUDE DIRS " ${SKIQ_INCLUDE_DIRS}) + +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(SKIQ DEFAULT_MSG SKIQ_LIBRARIES SKIQ_INCLUDE_DIRS) +MARK_AS_ADVANCED(SKIQ_LIBRARIES SKIQ_INCLUDE_DIRS) + +ENDIF(NOT SKIQ_FOUND) diff --git a/lib/include/srsran/asn1/rrc_nr_utils.h b/lib/include/srsran/asn1/rrc_nr_utils.h index 5b98e8b4a..c39d9915b 100644 --- a/lib/include/srsran/asn1/rrc_nr_utils.h +++ b/lib/include/srsran/asn1/rrc_nr_utils.h @@ -98,6 +98,9 @@ bool make_phy_max_code_rate(const asn1::rrc_nr::pucch_format_cfg_s& pucch_format bool make_phy_res_config(const asn1::rrc_nr::pucch_res_s& pucch_res, uint32_t format_2_max_code_rate, srsran_pucch_nr_resource_t* srsran_pucch_nr_resource); +bool make_phy_res_config(const srsran_pucch_nr_resource_t& in_pucch_res, + asn1::rrc_nr::pucch_res_s& out_pucch_res, + uint32_t pucch_res_id); bool make_phy_sr_resource(const asn1::rrc_nr::sched_request_res_cfg_s& sched_request_res_cfg, srsran_pucch_nr_sr_resource_t* srsran_pucch_nr_sr_resource); bool make_phy_pusch_alloc_type(const asn1::rrc_nr::pusch_cfg_s& pusch_cfg, diff --git a/lib/include/srsran/common/phy_cfg_nr_default.h b/lib/include/srsran/common/phy_cfg_nr_default.h index 65d8ac992..e3e494438 100644 --- a/lib/include/srsran/common/phy_cfg_nr_default.h +++ b/lib/include/srsran/common/phy_cfg_nr_default.h @@ -39,14 +39,31 @@ public: * - SSB: 5ms */ R_CARRIER_CUSTOM_10MHZ = 0, - } carrier = R_CARRIER_CUSTOM_10MHZ; + /** + * @brief Carrier reference configuration for 10MHz serving cell bandwidth + * - BW: 20 MHZ (106 PRB) + * - PCI: 500 + * - SCS: 15 kHz + * - SSB: 5ms + */ + R_CARRIER_CUSTOM_20MHZ, + R_CARRIER_COUNT + } carrier = R_CARRIER_CUSTOM_10MHZ; + const std::array R_CARRIER_STRING = {{"10MHz", "20MHz"}}; enum { /** * @brief TDD custom reference 5 slot DL and 5 slot UL */ R_TDD_CUSTOM_6_4 = 0, - } tdd = R_TDD_CUSTOM_6_4; + + /** + * @brief TDD pattern FR1.15-1 defined in TS38.101-4 Table A.1.2-1 + */ + R_TDD_FR1_15_1, + R_TDD_COUNT, + } tdd = R_TDD_CUSTOM_6_4; + const std::array R_TDD_STRING = {{"6D+4U", "FR1.15-1"}}; enum { /** @@ -65,7 +82,19 @@ public: * - No DMRS dedicated configuration */ R_PDSCH_DEFAULT = 0, - } pdsch = R_PDSCH_DEFAULT; + + /** + * @brief PDSCH parameters described in TS 38.101-4 Table 5.2.2.2.1-2 for the test described in table 5.2.2.2.1-3 + */ + R_PDSCH_TS38101_5_2_1, + + /** + * @brief Invalid PDSCH reference channel + */ + R_PDSCH_COUNT + + } pdsch = R_PDSCH_DEFAULT; + const std::array R_PDSCH_STRING = {{"default", "ts38101/5.2-1"}}; enum { /** @@ -107,6 +136,9 @@ public: */ R_PRACH_DEFAULT_LTE, } prach = R_PRACH_DEFAULT_LTE; + + reference_cfg_t() = default; + explicit reference_cfg_t(const std::string& args); }; phy_cfg_nr_default_t(const reference_cfg_t& reference_cfg); @@ -116,11 +148,13 @@ private: * Carrier make helper methods */ static void make_carrier_custom_10MHz(srsran_carrier_nr_t& carrier); + static void make_carrier_custom_20MHz(srsran_carrier_nr_t& carrier); /** * TDD make helper methods */ static void make_tdd_custom_6_4(srsran_tdd_config_nr_t& tdd); + static void make_tdd_fr1_15_1(srsran_tdd_config_nr_t& tdd); /** * PDCCH make helper methods @@ -131,6 +165,7 @@ private: * PDSCH make helper methods */ static void make_pdsch_default(srsran_sch_hl_cfg_nr_t& pdsch); + static void make_pdsch_2_1_1_tdd(const srsran_carrier_nr_t& carrier, srsran_sch_hl_cfg_nr_t& pdsch); /** * PUSCH make helper methods diff --git a/lib/include/srsran/common/slot_point.h b/lib/include/srsran/common/slot_point.h new file mode 100644 index 000000000..70477312e --- /dev/null +++ b/lib/include/srsran/common/slot_point.h @@ -0,0 +1,172 @@ +/** + * + * \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_SLOT_POINT_H +#define SRSRAN_SLOT_POINT_H + +#include "srsran/adt/interval.h" +#include "srsran/common/srsran_assert.h" + +namespace srsran { + +class slot_point +{ + uint32_t numerology_ : 3; + uint32_t count_ : 29; + + const static uint8_t NOF_NUMEROLOGIES = 5; + const static uint16_t NOF_SFNS = 1024; + const static uint8_t NOF_SUBFRAMES_PER_FRAME = 10; + + uint32_t nof_slots_per_hf() const { return nof_slots_per_frame() * NOF_SFNS; } + +public: + slot_point() : numerology_(NOF_NUMEROLOGIES), count_(0) {} + slot_point(uint8_t numerology, uint32_t count) : numerology_(numerology), count_(count) + { + srsran_assert(numerology < NOF_NUMEROLOGIES, "Invalid numerology idx=%d passed", (int)numerology); + srsran_assert(count < nof_slots_per_hf(), "Invalid slot count=%d passed", (int)count); + } + slot_point(uint8_t numerology, uint16_t sfn_val, uint8_t slot) : + numerology_(numerology), count_(slot + sfn_val * nof_slots_per_frame()) + { + srsran_assert(numerology < NOF_NUMEROLOGIES, "Invalid numerology idx=%d passed", (int)numerology); + srsran_assert(sfn_val < NOF_SFNS, "Invalid SFN=%d provided", (int)sfn_val); + srsran_assert(slot < nof_slots_per_frame(), + "Slot index=%d exceeds maximum number of slots=%d", + (int)slot, + (int)nof_slots_per_frame()); + } + + bool valid() const { return numerology_ < NOF_NUMEROLOGIES; } + uint8_t nof_slots_per_subframe() const { return 1U << numerology_; } + uint8_t nof_slots_per_frame() const { return nof_slots_per_subframe() * NOF_SUBFRAMES_PER_FRAME; } + + uint16_t sfn() const { return count_ / nof_slots_per_frame(); } + uint16_t subframe_idx() const { return slot_idx() / nof_slots_per_subframe(); } + uint8_t slot_idx() const { return count_ % nof_slots_per_frame(); } + uint8_t numerology_idx() const { return numerology_; } + uint32_t to_uint() const { return count_; } + explicit operator uint32_t() const { return count_; } + + void clear() { numerology_ = NOF_NUMEROLOGIES; } + + // operators + bool operator==(const slot_point& other) const { return other.count_ == count_ and other.numerology_ == numerology_; } + bool operator!=(const slot_point& other) const { return not(*this == other); } + bool operator<(const slot_point& other) const + { + srsran_assert(numerology_idx() == other.numerology_idx(), "Comparing slots of different numerologies"); + int a = static_cast(other.count_) - static_cast(count_); + if (a > 0) { + return (a < (int)nof_slots_per_hf() / 2); + } + return (a < -(int)nof_slots_per_hf() / 2); + } + bool operator<=(const slot_point& other) const { return (*this == other) or (*this < other); } + bool operator>=(const slot_point& other) const { return not(*this < other); } + bool operator>(const slot_point& other) const { return (*this != other) and *this >= other; } + + int32_t operator-(const slot_point& other) const + { + int a = static_cast(count_) - static_cast(other.count_); + if (a >= (int)nof_slots_per_hf() / 2) { + return a - nof_slots_per_hf(); + } + if (a < -(int)nof_slots_per_hf() / 2) { + return a + nof_slots_per_hf(); + } + return a; + } + slot_point& operator++() + { + count_++; + if (count_ == nof_slots_per_hf()) { + count_ = 0; + } + return *this; + } + slot_point operator++(int) + { + slot_point ret{*this}; + this-> operator++(); + return ret; + } + slot_point& operator+=(uint32_t jump) + { + count_ = (count_ + jump) % nof_slots_per_hf(); + return *this; + } + slot_point& operator-=(uint32_t jump) + { + int a = (static_cast(count_) - static_cast(jump)) % static_cast(nof_slots_per_hf()); + if (a < 0) { + a += nof_slots_per_hf(); + } + count_ = a; + return *this; + } + + bool is_in_interval(slot_point begin, slot_point end) const { return (*this >= begin and *this < end); } +}; +inline slot_point operator+(slot_point slot, uint32_t jump) +{ + slot += jump; + return slot; +} +inline slot_point operator+(uint32_t jump, slot_point slot) +{ + slot += jump; + return slot; +} +inline slot_point operator-(slot_point slot, uint32_t jump) +{ + slot -= jump; + return slot; +} +inline slot_point max(slot_point s1, slot_point s2) +{ + return s1 > s2 ? s1 : s2; +} +inline slot_point min(slot_point s1, slot_point s2) +{ + return s1 < s2 ? s1 : s2; +} + +using slot_interval = srsran::interval; + +} // namespace srsran + +namespace fmt { +template <> +struct formatter { + template + auto parse(ParseContext& ctx) -> decltype(ctx.begin()) + { + return ctx.begin(); + } + template + auto format(srsran::slot_point slot, FormatContext& ctx) -> decltype(std::declval().out()) + { + return format_to(ctx.out(), "{}/{}", slot.sfn(), slot.slot_idx()); + } +}; +} // namespace fmt + +namespace srsenb { + +using slot_point = srsran::slot_point; +using slot_interval = srsran::slot_interval; + +} // namespace srsenb + +#endif // SRSRAN_SLOT_POINT_H diff --git a/lib/include/srsran/common/test_pcap.h b/lib/include/srsran/common/test_pcap.h index b8a0a4c5d..65f4ce5f6 100644 --- a/lib/include/srsran/common/test_pcap.h +++ b/lib/include/srsran/common/test_pcap.h @@ -22,21 +22,59 @@ #ifndef SRSRAN_TEST_PCAP_H #define SRSRAN_TEST_PCAP_H -#if HAVE_PCAP +/** + * @brief Helper class for tests that wish to dump RRC, PDCP or MAC SDUs into PCAP files in order to inspect them with + * Wireshark. + * + * Depending on the layer of interest, the class adds the protocol header for the layers below so that Wireshark can + * disect them. For RRC for example, both PDCP and RLC AM dummy headers are added. + * + * There is an EUTRA and NR version for the helper methods. + * + */ + #include "srsran/common/mac_pcap.h" #include "srsran/mac/mac_sch_pdu_nr.h" static std::unique_ptr pcap_handle = nullptr; #define PCAP_CRNTI (0x1001) #define PCAP_TTI (666) -#endif namespace srsran { -int write_mac_sdu_nr(const uint32_t lcid, const uint8_t* payload, const uint32_t len); +/** + * @brief Writes a MAC SDU of a gives LCID for NR + * + * @param lcid The logical channel ID of the SDU + * @param payload Pointer to payload + * @param len Length + * @return int + */ +int write_mac_sdu_nr(const uint32_t lcid, const uint8_t* payload, const uint32_t len) +{ + if (pcap_handle) { + byte_buffer_t tx_buffer; + srsran::mac_sch_pdu_nr tx_pdu; + tx_pdu.init_tx(&tx_buffer, len + 10); + tx_pdu.add_sdu(lcid, payload, len); + tx_pdu.pack(); + pcap_handle->write_dl_crnti_nr(tx_buffer.msg, tx_buffer.N_bytes, PCAP_CRNTI, 0, PCAP_TTI); + return SRSRAN_SUCCESS; + } + return SRSRAN_ERROR; +} -int write_rlc_am_sdu_nr(const uint32_t lcid, const uint8_t* payload, const uint32_t len) +/** + * @brief Writes a PDCP SDU (e.g. RRC DL-DCCH PDU) + * + * Both PDCP and RLC AM header (dummy for SN=0) are added. + * + * @param lcid The logical channel ID of the SDU + * @param payload Pointer to payload + * @param len Length + * @return int + */ +int write_pdcp_sdu_nr(const uint32_t lcid, const uint8_t* payload, const uint32_t len) { -#if HAVE_PCAP if (pcap_handle) { byte_buffer_t mac_sdu; // Add dummy RLC AM PDU header @@ -52,23 +90,6 @@ int write_rlc_am_sdu_nr(const uint32_t lcid, const uint8_t* payload, const uint3 mac_sdu.N_bytes += len; return write_mac_sdu_nr(lcid, mac_sdu.msg, mac_sdu.N_bytes); } -#endif // HAVE_PCAP - return SRSRAN_ERROR; -} - -int write_mac_sdu_nr(const uint32_t lcid, const uint8_t* payload, const uint32_t len) -{ -#if HAVE_PCAP - if (pcap_handle) { - byte_buffer_t tx_buffer; - srsran::mac_sch_pdu_nr tx_pdu; - tx_pdu.init_tx(&tx_buffer, len + 10); - tx_pdu.add_sdu(lcid, payload, len); - tx_pdu.pack(); - pcap_handle->write_dl_crnti_nr(tx_buffer.msg, tx_buffer.N_bytes, PCAP_CRNTI, 0, PCAP_TTI); - return SRSRAN_SUCCESS; - } -#endif // HAVE_PCAP return SRSRAN_ERROR; } diff --git a/lib/include/srsran/common/thread_pool.h b/lib/include/srsran/common/thread_pool.h index a16a2af47..f962160dd 100644 --- a/lib/include/srsran/common/thread_pool.h +++ b/lib/include/srsran/common/thread_pool.h @@ -63,8 +63,8 @@ public: virtual void work_imp() = 0; private: - uint32_t my_id = 0; - thread_pool* my_parent = nullptr; + uint32_t my_id = 0; + thread_pool* my_parent = nullptr; std::atomic running = {true}; void run_thread(); @@ -73,22 +73,24 @@ public: bool is_stopped() const; }; - thread_pool(uint32_t nof_workers); - void init_worker(uint32_t id, worker*, uint32_t prio = 0, uint32_t mask = 255); - void stop(); - worker* wait_worker_id(uint32_t id); - worker* wait_worker(uint32_t tti); - worker* wait_worker_nb(uint32_t tti); - void start_worker(worker*); - void start_worker(uint32_t id); - worker* get_worker(uint32_t id); - uint32_t get_nof_workers(); + thread_pool(uint32_t nof_workers_, std::string id_ = ""); + void init_worker(uint32_t id, worker*, uint32_t prio = 0, uint32_t mask = 255); + void stop(); + worker* wait_worker_id(uint32_t id); + worker* wait_worker(uint32_t tti); + worker* wait_worker_nb(uint32_t tti); + void start_worker(worker*); + void start_worker(uint32_t id); + worker* get_worker(uint32_t id); + uint32_t get_nof_workers(); + std::string get_id(); private: bool find_finished_worker(uint32_t tti, uint32_t* id); typedef enum { STOP, IDLE, START_WORK, WORKER_READY, WORKING } worker_status; + std::string id; // id is prepended to every worker std::vector workers = {}; uint32_t nof_workers = 0; uint32_t max_workers = 0; diff --git a/lib/include/srsran/interfaces/gnb_interfaces.h b/lib/include/srsran/interfaces/gnb_interfaces.h index 836f8a353..63dc20573 100644 --- a/lib/include/srsran/interfaces/gnb_interfaces.h +++ b/lib/include/srsran/interfaces/gnb_interfaces.h @@ -30,6 +30,9 @@ #include "srsran/interfaces/rlc_interface_types.h" #include "srsran/interfaces/rrc_interface_types.h" #include "srsran/interfaces/sched_interface.h" +// EUTRA interfaces that are used unmodified +#include "srsran/interfaces/enb_mac_interfaces.h" +#include "srsran/interfaces/enb_rrc_interfaces.h" namespace srsenb { @@ -46,11 +49,9 @@ public: virtual uint16_t reserve_rnti() = 0; }; -class mac_interface_rlc_nr -{ -public: - virtual int rlc_buffer_state(uint16_t rnti, uint32_t lc_id, uint32_t tx_queue, uint32_t retx_queue) = 0; -}; +// NR interface is identical to EUTRA interface +class mac_interface_rlc_nr : public mac_interface_rlc +{}; /***************************** * RLC INTERFACES @@ -161,20 +162,18 @@ public: /// User management virtual int add_user(uint16_t rnti) = 0; }; -class rrc_interface_rlc_nr + +// NR interface is almost identical to EUTRA version +class rrc_interface_rlc_nr : public rrc_interface_rlc { public: - virtual void read_pdu_pcch(uint8_t* payload, uint32_t payload_size) = 0; - 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; - virtual const char* get_rb_name(uint32_t lcid) = 0; + virtual void read_pdu_pcch(uint8_t* payload, uint32_t payload_size) = 0; + virtual const char* get_rb_name(uint32_t lcid) = 0; }; -class rrc_interface_pdcp_nr + +// NR interface identical to EUTRA version +class rrc_interface_pdcp_nr : public 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; }; class phy_interface_rrc_nr @@ -232,8 +231,8 @@ public: }; struct pdsch_t { - srsran_sch_cfg_nr_t sch = {}; ///< PDSCH configuration - std::array data = {}; ///< Data pointer + srsran_sch_cfg_nr_t sch = {}; ///< PDSCH configuration + std::array data = {}; ///< Data pointer }; struct ssb_t { @@ -249,9 +248,8 @@ public: }; struct pusch_t { - uint32_t pid = 0; ///< HARQ process ID - srsran_sch_cfg_nr_t sch = {}; ///< PUSCH configuration - std::array data = {}; ///< Data pointer + uint32_t pid = 0; ///< HARQ process ID + srsran_sch_cfg_nr_t sch = {}; ///< PUSCH configuration }; /** @@ -281,14 +279,23 @@ public: }; struct pusch_info_t { - uint16_t rnti; - uint32_t pid = 0; ///< HARQ process ID + // Context + uint16_t rnti; ///< UE temporal identifier + uint32_t pid = 0; ///< HARQ process ID + + // SCH and UCI payload information srsran_pusch_res_nr_t pusch_data; srsran_uci_cfg_nr_t uci_cfg; ///< Provides UCI configuration, so stack does not need to keep the pending state - // ... add signal measurements here + + // Actual SCH PDU + srsran::unique_byte_buffer_t pdu = nullptr; + + // PUSCH signal measurements + // ... }; struct rach_info_t { + uint32_t slot_index; uint32_t preamble; uint32_t time_adv; }; @@ -297,21 +304,12 @@ public: virtual int get_dl_sched(const srsran_slot_cfg_t& slot_cfg, dl_sched_t& dl_sched) = 0; virtual int get_ul_sched(const srsran_slot_cfg_t& slot_cfg, ul_sched_t& ul_sched) = 0; virtual int pucch_info(const srsran_slot_cfg_t& slot_cfg, const pucch_info_t& pucch_info) = 0; - virtual int pusch_info(const srsran_slot_cfg_t& slot_cfg, const pusch_info_t& pusch_info) = 0; + virtual int pusch_info(const srsran_slot_cfg_t& slot_cfg, pusch_info_t& pusch_info) = 0; 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 -{ -public: - struct rx_data_ind_t { - uint32_t tti; - uint16_t rnti; - srsran::unique_byte_buffer_t tb; - }; - - virtual int rx_data_indication(rx_data_ind_t& grant) = 0; -}; +{}; } // namespace srsenb diff --git a/lib/include/srsran/interfaces/gnb_ngap_interfaces.h b/lib/include/srsran/interfaces/gnb_ngap_interfaces.h index 3ce15bcfd..cce13a29b 100644 --- a/lib/include/srsran/interfaces/gnb_ngap_interfaces.h +++ b/lib/include/srsran/interfaces/gnb_ngap_interfaces.h @@ -59,6 +59,7 @@ public: virtual bool user_release(uint16_t rnti, asn1::ngap_nr::cause_radio_network_e cause_radio) = 0; virtual bool is_amf_connected() = 0; virtual void ue_ctxt_setup_complete(uint16_t rnti) = 0; + virtual void ue_notify_rrc_reconf_complete(uint16_t rnti, bool outcome) = 0; }; } // namespace srsenb diff --git a/lib/include/srsran/interfaces/gnb_rrc_nr_interfaces.h b/lib/include/srsran/interfaces/gnb_rrc_nr_interfaces.h index ba5c29c66..fd53075a3 100644 --- a/lib/include/srsran/interfaces/gnb_rrc_nr_interfaces.h +++ b/lib/include/srsran/interfaces/gnb_rrc_nr_interfaces.h @@ -21,11 +21,18 @@ #ifndef SRSRAN_GNB_RRC_NR_INTERFACES_H #define SRSRAN_GNB_RRC_NR_INTERFACES_H +#include "srsran/asn1/ngap.h" +#include "srsran/common/byte_buffer.h" namespace srsenb { class rrc_interface_ngap_nr { public: + virtual int ue_set_security_cfg_key(uint16_t rnti, const asn1::fixed_bitstring<256, false, true>& key) = 0; + virtual int ue_set_bitrates(uint16_t rnti, const asn1::ngap_nr::ue_aggregate_maximum_bit_rate_s& rates) = 0; + virtual int ue_set_security_cfg_capabilities(uint16_t rnti, const asn1::ngap_nr::ue_security_cap_s& caps) = 0; + virtual int start_security_mode_procedure(uint16_t rnti) = 0; + virtual void write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu) = 0; }; } // namespace srsenb diff --git a/lib/include/srsran/interfaces/mac_interface_types.h b/lib/include/srsran/interfaces/mac_interface_types.h index a648ddaaf..986435b4d 100644 --- a/lib/include/srsran/interfaces/mac_interface_types.h +++ b/lib/include/srsran/interfaces/mac_interface_types.h @@ -140,6 +140,7 @@ struct rach_nr_cfg_t { uint32_t powerRampingStep; uint32_t ra_responseWindow; uint32_t ra_ContentionResolutionTimer; + bool skip_rar; rach_nr_cfg_t() { reset(); } void reset() @@ -149,6 +150,7 @@ struct rach_nr_cfg_t { powerRampingStep = 0; preambleTransMax = 0; ra_responseWindow = 0; + skip_rar = false; } }; diff --git a/lib/include/srsran/interfaces/phy_common_interface.h b/lib/include/srsran/interfaces/phy_common_interface.h index 3d06a3a8f..7333c770a 100644 --- a/lib/include/srsran/interfaces/phy_common_interface.h +++ b/lib/include/srsran/interfaces/phy_common_interface.h @@ -27,19 +27,69 @@ namespace srsran { +/** + * @brief Descibes a physical layer common interface + */ class phy_common_interface { +private: + std::mutex tx_mutex; ///< Protect Tx attributes + std::condition_variable tx_cvar; ///< Tx condition variable + bool tx_hold = false; ///< Hold threads until the signal is transmitted + +protected: + /** + * @brief Waits for the last worker to call `last_worker()` to prevent that the current SF worker is released and + * overwrites the transmit signal prior transmission + */ + void wait_last_worker() + { + std::unique_lock lock(tx_mutex); + tx_hold = true; + while (tx_hold) { + tx_cvar.wait(lock); + } + } + + /** + * @brief Notifies the last SF worker transmitted the baseband and all the workers waiting are released + */ + void last_worker() + { + std::unique_lock lock(tx_mutex); + tx_hold = false; + tx_cvar.notify_all(); + } + public: + /** + * @brief Describes a worker context + */ + struct worker_context_t { + uint32_t sf_idx = 0; ///< Subframe index + void* worker_ptr = nullptr; ///< Worker pointer for wait/release semaphore + bool last = false; ///< Indicates this worker is the last one in the sub-frame processing + srsran::rf_timestamp_t tx_time = {}; ///< Transmit time, used only by last worker + + void copy(const worker_context_t& other) + { + sf_idx = other.sf_idx; + worker_ptr = other.worker_ptr; + last = other.last; + tx_time.copy(other.tx_time); + } + + worker_context_t() = default; + worker_context_t(const worker_context_t& other) { copy(other); } + }; + /** * @brief Common PHY interface for workers to indicate they ended - * @param h Worker pointer used as unique identifier for synchronising Tx + * @param w_ctx Worker context * @param tx_enable Indicates whether the buffer has baseband samples to transmit * @param buffer Baseband buffer - * @param tx_time Transmit timestamp - * @param is_nr Indicates whether the worker is NR or not */ - virtual void - worker_end(void* h, bool tx_enable, srsran::rf_buffer_t& buffer, srsran::rf_timestamp_t& tx_time, bool is_nr) = 0; + virtual void worker_end(const worker_context_t& w_ctx, const bool& tx_enable, srsran::rf_buffer_t& buffer) = 0; }; } // namespace srsran diff --git a/lib/include/srsran/interfaces/rrc_interface_types.h b/lib/include/srsran/interfaces/rrc_interface_types.h index 0dd75ace6..431dadfbf 100644 --- a/lib/include/srsran/interfaces/rrc_interface_types.h +++ b/lib/include/srsran/interfaces/rrc_interface_types.h @@ -109,6 +109,24 @@ struct plmn_id_t { mcc_to_string(mcc_num, &mcc_str); return mcc_str + mnc_str; } + + std::string to_serving_network_name_string() const + { + char buff[50]; + std::string mcc_str, mnc_str; + uint16_t mnc_num, mcc_num; + bytes_to_mnc(&mnc[0], &mnc_num, nof_mnc_digits); + bytes_to_mcc(&mcc[0], &mcc_num); + mnc_to_string(mnc_num, &mnc_str); + mcc_to_string(mcc_num, &mcc_str); + if (mnc_str.size() == 2) { + mnc_str = "0" + mnc_str; + } + snprintf(buff, sizeof(buff), "5G:mnc%s.mcc%s.3gppnetwork.org", mnc_str.c_str(), mcc_str.c_str()); + std::string ssn_s = buff; + return ssn_s; + } + bool operator==(const plmn_id_t& other) const { return std::equal(&mcc[0], &mcc[3], &other.mcc[0]) and nof_mnc_digits == other.nof_mnc_digits and diff --git a/lib/include/srsran/interfaces/ue_nr_interfaces.h b/lib/include/srsran/interfaces/ue_nr_interfaces.h index 250f07c35..36137eed6 100644 --- a/lib/include/srsran/interfaces/ue_nr_interfaces.h +++ b/lib/include/srsran/interfaces/ue_nr_interfaces.h @@ -175,16 +175,17 @@ public: }; struct phy_args_nr_t { - uint32_t nof_carriers = 1; - uint32_t max_nof_prb = 106; - uint32_t nof_phy_threads = 3; - uint32_t worker_cpu_mask = 0; - srsran::phy_log_args_t log = {}; - srsran_ue_dl_nr_args_t dl = {}; - srsran_ue_ul_nr_args_t ul = {}; - std::set fixed_sr = {1}; - uint32_t fix_wideband_cqi = 15; // Set to a non-zero value for fixing the wide-band CQI report - bool store_pdsch_ko = false; + uint32_t rf_channel_offset = 0; ///< Specifies the RF channel the NR carrier shall fill + uint32_t nof_carriers = 1; + uint32_t max_nof_prb = 106; + uint32_t nof_phy_threads = 3; + uint32_t worker_cpu_mask = 0; + srsran::phy_log_args_t log = {}; + srsran_ue_dl_nr_args_t dl = {}; + srsran_ue_ul_nr_args_t ul = {}; + std::set fixed_sr = {1}; + uint32_t fix_wideband_cqi = 15; // Set to a non-zero value for fixing the wide-band CQI report + bool store_pdsch_ko = false; phy_args_nr_t() { diff --git a/lib/include/srsran/interfaces/ue_usim_interfaces.h b/lib/include/srsran/interfaces/ue_usim_interfaces.h index 30eeabaa0..93252e722 100644 --- a/lib/include/srsran/interfaces/ue_usim_interfaces.h +++ b/lib/include/srsran/interfaces/ue_usim_interfaces.h @@ -38,6 +38,12 @@ public: virtual bool get_imsi_vec(uint8_t* imsi_, uint32_t n) = 0; virtual bool get_imei_vec(uint8_t* imei_, uint32_t n) = 0; virtual bool get_home_plmn_id(srsran::plmn_id_t* home_plmn_id) = 0; + // Get the home mcc as bytes array + virtual bool get_home_mcc_bytes(uint8_t* mcc_, uint32_t n) = 0; + // Get the home mnc as byte array + virtual bool get_home_mnc_bytes(uint8_t* mnc_, uint32_t n) = 0; + // Get the home msin in bytes array encoded as bcd + virtual bool get_home_msin_bcd(uint8_t* msin_, uint32_t n) = 0; virtual auth_result_t generate_authentication_response(uint8_t* rand, uint8_t* autn_enb, uint16_t mcc, diff --git a/lib/include/srsran/phy/common/phy_common_nr.h b/lib/include/srsran/phy/common/phy_common_nr.h index e4b93a001..e647fa4de 100644 --- a/lib/include/srsran/phy/common/phy_common_nr.h +++ b/lib/include/srsran/phy/common/phy_common_nr.h @@ -56,7 +56,7 @@ extern "C" { /** * @brief Defines the symbol duration, including cyclic prefix */ -#define SRSRAN_SUBC_SPACING_NR(NUM) (15000U << (NUM)) +#define SRSRAN_SUBC_SPACING_NR(NUM) (15000U << (uint32_t)(NUM)) /** * @brief Defines the number of slots per SF. Defined by TS 38.211 v15.8.0 Table 4.3.2-1. diff --git a/lib/include/srsran/phy/io/filesource.h b/lib/include/srsran/phy/io/filesource.h index 0f6b20b69..71a2cf953 100644 --- a/lib/include/srsran/phy/io/filesource.h +++ b/lib/include/srsran/phy/io/filesource.h @@ -33,7 +33,7 @@ #define SRSRAN_FILESOURCE_H #include -#include +#include #include "srsran/config.h" #include "srsran/phy/io/format.h" diff --git a/lib/include/srsran/phy/phch/cqi.h b/lib/include/srsran/phy/phch/cqi.h index 25889128c..fbd1efdff 100644 --- a/lib/include/srsran/phy/phch/cqi.h +++ b/lib/include/srsran/phy/phch/cqi.h @@ -127,6 +127,7 @@ typedef struct SRSRAN_API { uint32_t scell_index; ///< Indicates the cell/carrier the measurement belongs, use 0 for PCell uint32_t L; uint32_t N; + uint32_t sb_idx; srsran_cqi_type_t type; uint32_t ri_len; } srsran_cqi_cfg_t; @@ -163,10 +164,27 @@ SRSRAN_API bool srsran_cqi_periodic_is_subband(const srsran_cqi_report_cfg_t* cf SRSRAN_API bool srsran_cqi_periodic_ri_send(const srsran_cqi_report_cfg_t* periodic_cfg, uint32_t tti, srsran_frame_type_t frame_type); +SRSRAN_API uint32_t srsran_cqi_periodic_sb_bw_part_idx(const srsran_cqi_report_cfg_t* cfg, + uint32_t tti, + uint32_t nof_prb, + srsran_frame_type_t frame_type); + SRSRAN_API int srsran_cqi_hl_get_no_subbands(int nof_prb); +/** + * @brief Returns the number of bits to index a bandwidth part (L) + * + * @remark Implemented according to TS 38.213 section 7.2.2 Periodic CSI Reporting using PUCCH, paragraph that refers to + * `L-bit label indexed in the order of increasing frequency, where L = ceil(log2(nof_prb/k/J))` + * + */ SRSRAN_API int srsran_cqi_hl_get_L(int nof_prb); +SRSRAN_API uint32_t srsran_cqi_get_sb_idx(uint32_t tti, + uint32_t subband_label, + const srsran_cqi_report_cfg_t* cqi_report_cfg, + const srsran_cell_t* cell); + SRSRAN_API uint8_t srsran_cqi_from_snr(float snr); SRSRAN_API float srsran_cqi_to_coderate(uint32_t cqi, bool use_alt_table); diff --git a/lib/include/srsran/phy/utils/bit.h b/lib/include/srsran/phy/utils/bit.h index 081f7806a..f23ad5541 100644 --- a/lib/include/srsran/phy/utils/bit.h +++ b/lib/include/srsran/phy/utils/bit.h @@ -85,6 +85,8 @@ SRSRAN_API void srsran_bit_unpack_l(uint64_t value, uint8_t** bits, int nof_bits SRSRAN_API void srsran_bit_unpack(uint32_t value, uint8_t** bits, int nof_bits); +SRSRAN_API void srsran_bit_unpack_lsb(uint32_t value, uint8_t** bits, int nof_bits); + SRSRAN_API void srsran_bit_fprint(FILE* stream, uint8_t* bits, int nof_bits); SRSRAN_API uint32_t srsran_bit_diff(const uint8_t* x, const uint8_t* y, int nbits); diff --git a/lib/include/srsran/phy/utils/simd.h b/lib/include/srsran/phy/utils/simd.h index fde7f4595..538b59a8a 100644 --- a/lib/include/srsran/phy/utils/simd.h +++ b/lib/include/srsran/phy/utils/simd.h @@ -1177,7 +1177,7 @@ typedef __m128 simd_sel_t; #else /* LV_HAVE_AVX2 */ #ifdef HAVE_NEON typedef int32x4_t simd_i_t; -typedef int32x4_t simd_sel_t; +typedef uint32x4_t simd_sel_t; #endif /* HAVE_NEON */ #endif /* LV_HAVE_SSE */ #endif /* LV_HAVE_AVX2 */ @@ -1309,7 +1309,7 @@ static inline simd_sel_t srsran_simd_f_max(simd_f_t a, simd_f_t b) return (simd_sel_t)_mm_cmpgt_ps(a, b); #else /* LV_HAVE_SSE */ #ifdef HAVE_NEON - return (simd_sel_t)vcgtq_f32(a, b); + return vcgtq_f32(a, b); #endif /* HAVE_NEON */ #endif /* LV_HAVE_SSE */ #endif /* LV_HAVE_AVX2 */ @@ -1328,7 +1328,7 @@ static inline simd_sel_t srsran_simd_f_min(simd_f_t a, simd_f_t b) return (simd_sel_t)_mm_cmplt_ps(a, b); #else /* LV_HAVE_SSE */ #ifdef HAVE_NEON - return (simd_sel_t)vcltq_f32(a, b); + return vcltq_f32(a, b); #endif /* HAVE_NEON */ #endif /* LV_HAVE_SSE */ #endif /* LV_HAVE_AVX2 */ @@ -1346,20 +1346,8 @@ static inline simd_f_t srsran_simd_f_select(simd_f_t a, simd_f_t b, simd_sel_t s #ifdef LV_HAVE_SSE return _mm_blendv_ps(a, b, selector); #else /* LV_HAVE_SSE */ -#ifdef HAVE_NEON // CURRENTLY USES GENERIC IMPLEMENTATION FOR NEON - float* a_ptr = (float*)&a; - float* b_ptr = (float*)&b; - simd_f_t ret; - int* sel = (int*)&selector; - float* c_ptr = (float*)&ret; - for (int i = 0; i < 4; i++) { - if (sel[i] == -1) { - c_ptr[i] = b_ptr[i]; - } else { - c_ptr[i] = a_ptr[i]; - } - } - return ret; +#ifdef HAVE_NEON + return vbslq_f32(selector, b, a); #endif /* HAVE_NEON */ #endif /* LV_HAVE_SSE */ #endif /* LV_HAVE_AVX2 */ @@ -1377,20 +1365,8 @@ static inline simd_i_t srsran_simd_i_select(simd_i_t a, simd_i_t b, simd_sel_t s #ifdef LV_HAVE_SSE return (__m128i)_mm_blendv_ps((__m128)a, (__m128)b, selector); #else /* LV_HAVE_SSE */ -#ifdef HAVE_NEON // CURRENTLY USES GENERIC IMPLEMENTATION FOR NEON - int* a_ptr = (int*)&a; - int* b_ptr = (int*)&b; - simd_i_t ret; - int* sel = (int*)&selector; - int* c_ptr = (int*)&ret; - for (int i = 0; i < 4; i++) { - if (sel[i] == -1) { - c_ptr[i] = b_ptr[i]; - } else { - c_ptr[i] = a_ptr[i]; - } - } - return ret; +#ifdef HAVE_NEON + return vbslq_s32(selector, b, a); #endif /* HAVE_NEON */ #endif /* LV_HAVE_SSE */ #endif /* LV_HAVE_AVX2 */ diff --git a/lib/include/srsran/phy/utils/vector.h b/lib/include/srsran/phy/utils/vector.h index 211c1c9b2..5dcecf909 100644 --- a/lib/include/srsran/phy/utils/vector.h +++ b/lib/include/srsran/phy/utils/vector.h @@ -52,6 +52,7 @@ extern "C" { #define SRSRAN_CEIL(NUM, DEN) (((NUM) + ((DEN)-1)) / (DEN)) #define SRSRAN_FLOOR(NUM, DEN) ((NUM) / (DEN)) #define SRSRAN_ROUND(NUM, DEN) ((uint32_t)round((double)(NUM) / (double)(DEN))) +#define SRSRAN_CEIL_LOG2(N) (((N) == 0) ? 0 : ceil(log2((double)(N)))) // Complex squared absolute value #define SRSRAN_CSQABS(X) (__real__(X) * __real__(X) + __imag__(X) * __imag__(X)) diff --git a/lib/include/srsran/radio/rf_buffer.h b/lib/include/srsran/radio/rf_buffer.h index 9c3ec63de..4ba95526f 100644 --- a/lib/include/srsran/radio/rf_buffer.h +++ b/lib/include/srsran/radio/rf_buffer.h @@ -121,6 +121,26 @@ public: { sample_buffer.at(logical_ch * nof_antennas + port_idx) = ptr; } + void set_combine(const uint32_t& channel_idx, cf_t* ptr) + { + if (sample_buffer.at(channel_idx) == nullptr) { + sample_buffer.at(channel_idx) = ptr; + } else if (ptr != nullptr) { + srsran_vec_sum_ccc(ptr, sample_buffer.at(channel_idx), sample_buffer.at(channel_idx), nof_samples); + } + } + void set_combine(const uint32_t& logical_ch, const uint32_t& port_idx, const uint32_t& nof_antennas, cf_t* ptr) + { + set_combine(logical_ch * nof_antennas + port_idx, ptr); + } + void set_combine(const rf_buffer_interface& other) + { + // Take the other number of samples always + set_nof_samples(other.get_nof_samples()); + for (uint32_t ch = 0; ch < SRSRAN_MAX_CHANNELS; ch++) { + set_combine(ch, other.get(ch)); + } + } void** to_void() override { return (void**)sample_buffer.data(); } cf_t** to_cf_t() override { return sample_buffer.data(); } uint32_t size() override { return nof_subframes * SRSRAN_SF_LEN_MAX; } diff --git a/lib/src/asn1/rrc_nr_utils.cc b/lib/src/asn1/rrc_nr_utils.cc index d7fc3b1e6..e6cc07033 100644 --- a/lib/src/asn1/rrc_nr_utils.cc +++ b/lib/src/asn1/rrc_nr_utils.cc @@ -741,6 +741,42 @@ bool make_phy_res_config(const pucch_res_s& pucch_res, return true; } +bool make_phy_res_config(const srsran_pucch_nr_resource_t& in_pucch_res, + asn1::rrc_nr::pucch_res_s& out_pucch_res, + uint32_t pucch_res_id) +{ + out_pucch_res.pucch_res_id = pucch_res_id; + out_pucch_res.start_prb = in_pucch_res.starting_prb; + + switch (in_pucch_res.format) { + case SRSRAN_PUCCH_NR_FORMAT_0: + asn1::log_warning("SRSRAN_PUCCH_NR_FORMAT_0 conversion not supported"); + return false; + case SRSRAN_PUCCH_NR_FORMAT_1: + out_pucch_res.format.set_format1(); + out_pucch_res.format.format1().init_cyclic_shift = in_pucch_res.initial_cyclic_shift; + out_pucch_res.format.format1().nrof_symbols = in_pucch_res.nof_symbols; + out_pucch_res.format.format1().start_symbol_idx = in_pucch_res.start_symbol_idx; + out_pucch_res.format.format1().time_domain_occ = in_pucch_res.time_domain_occ; + return true; + case SRSRAN_PUCCH_NR_FORMAT_2: + out_pucch_res.format.set_format2(); + out_pucch_res.format.format2().nrof_symbols = in_pucch_res.nof_symbols; + out_pucch_res.format.format2().start_symbol_idx = in_pucch_res.start_symbol_idx; + out_pucch_res.format.format2().nrof_prbs = in_pucch_res.nof_prb; + return true; + case SRSRAN_PUCCH_NR_FORMAT_3: + asn1::log_warning("SRSRAN_PUCCH_NR_FORMAT_3 conversion not supported"); + return true; + case SRSRAN_PUCCH_NR_FORMAT_4: + asn1::log_warning("SRSRAN_PUCCH_NR_FORMAT_4 conversion not supported"); + return false; + default: + asn1::log_warning("Invalid NR PUCCH format"); + } + return false; +} + bool make_phy_sr_resource(const sched_request_res_cfg_s& sched_request_res_cfg, srsran_pucch_nr_sr_resource_t* in_srsran_pucch_nr_sr_resource) { diff --git a/lib/src/common/mac_pcap.cc b/lib/src/common/mac_pcap.cc index 02f46a366..697870524 100644 --- a/lib/src/common/mac_pcap.cc +++ b/lib/src/common/mac_pcap.cc @@ -39,7 +39,7 @@ uint32_t mac_pcap::open(std::string filename_, uint32_t ue_id_) return SRSRAN_ERROR; } - // set DLT for selected RAT + // set UDP DLT dlt = UDP_DLT; pcap_file = DLT_PCAP_Open(dlt, filename_.c_str()); if (pcap_file == nullptr) { diff --git a/lib/src/common/phy_cfg_nr_default.cc b/lib/src/common/phy_cfg_nr_default.cc index e3998eeec..6a429c113 100644 --- a/lib/src/common/phy_cfg_nr_default.cc +++ b/lib/src/common/phy_cfg_nr_default.cc @@ -20,10 +20,59 @@ */ #include "srsran/common/phy_cfg_nr_default.h" +#include "srsran/common/string_helpers.h" #include "srsran/srsran.h" +#include +#include namespace srsran { +template +T inc(T v) +{ + return static_cast(static_cast(v) + 1); +} + +phy_cfg_nr_default_t::reference_cfg_t::reference_cfg_t(const std::string& args) +{ + std::list param_list = {}; + string_parse_list(args, ',', param_list); + for (std::string& e : param_list) { + std::list param = {}; + string_parse_list(e, '=', param); + + // Skip if size is invalid + if (param.size() != 2) { + srsran_terminate("Invalid reference argument '%s'", e.c_str()); + } + + if (param.front() == "carrier") { + for (carrier = R_CARRIER_CUSTOM_10MHZ; carrier < R_CARRIER_COUNT; carrier = inc(carrier)) { + if (R_CARRIER_STRING[carrier] == param.back()) { + break; + } + } + srsran_assert(carrier != R_CARRIER_COUNT, "Invalid carrier reference configuration '%s'", param.back().c_str()); + } else if (param.front() == "tdd") { + for (tdd = R_TDD_CUSTOM_6_4; tdd < R_TDD_COUNT; tdd = inc(tdd)) { + if (R_TDD_STRING[tdd] == param.back()) { + break; + } + } + srsran_assert(tdd != R_TDD_COUNT, "Invalid TDD reference configuration '%s'", param.back().c_str()); + } else if (param.front() == "pdsch") { + for (pdsch = R_PDSCH_DEFAULT; pdsch < R_PDSCH_COUNT; pdsch = inc(pdsch)) { + if (R_PDSCH_STRING[pdsch] == param.back()) { + break; + } + } + srsran_assert(pdsch != R_PDSCH_COUNT, "Invalid PDSCH reference configuration '%s'", param.back().c_str()); + } else { + srsran_terminate("Invalid %s reference component", param.front().c_str()); + } + } +} + void phy_cfg_nr_default_t::make_carrier_custom_10MHz(srsran_carrier_nr_t& carrier) { carrier.nof_prb = 52; @@ -35,6 +84,17 @@ void phy_cfg_nr_default_t::make_carrier_custom_10MHz(srsran_carrier_nr_t& carrie carrier.scs = srsran_subcarrier_spacing_15kHz; } +void phy_cfg_nr_default_t::make_carrier_custom_20MHz(srsran_carrier_nr_t& carrier) +{ + carrier.nof_prb = 106; + carrier.max_mimo_layers = 1; + carrier.pci = 500; + carrier.absolute_frequency_point_a = 633928; + carrier.absolute_frequency_ssb = 634176; + carrier.offset_to_carrier = 0; + carrier.scs = srsran_subcarrier_spacing_15kHz; +} + void phy_cfg_nr_default_t::make_tdd_custom_6_4(srsran_tdd_config_nr_t& tdd) { tdd.pattern1.period_ms = 10; @@ -47,6 +107,18 @@ void phy_cfg_nr_default_t::make_tdd_custom_6_4(srsran_tdd_config_nr_t& tdd) tdd.pattern2.period_ms = 0; } +void phy_cfg_nr_default_t::make_tdd_fr1_15_1(srsran_tdd_config_nr_t& tdd) +{ + tdd.pattern1.period_ms = 5; + tdd.pattern1.nof_dl_slots = 3; + tdd.pattern1.nof_dl_symbols = 10; + tdd.pattern1.nof_ul_slots = 1; + tdd.pattern1.nof_ul_symbols = 2; + + // Disable pattern 2 + tdd.pattern2.period_ms = 0; +} + void phy_cfg_nr_default_t::make_pdcch_custom_common_ss(srsran_pdcch_cfg_nr_t& pdcch, const srsran_carrier_nr_t& carrier) { // Configure CORESET ID 1 @@ -90,6 +162,91 @@ void phy_cfg_nr_default_t::make_pdsch_default(srsran_sch_hl_cfg_nr_t& pdsch) pdsch.typeA_pos = srsran_dmrs_sch_typeA_pos_2; } +void make_nzp_csi_rs_ts38101_table_5_2_1(const srsran_carrier_nr_t& carrier, srsran_csi_rs_nzp_set_t& trs) +{ + // Set defaults + trs = {}; + + trs.trs_info = true; + trs.count = 4; + + srsran_csi_rs_nzp_resource_t& res1 = trs.data[0]; + srsran_csi_rs_nzp_resource_t& res2 = trs.data[1]; + srsran_csi_rs_nzp_resource_t& res3 = trs.data[2]; + srsran_csi_rs_nzp_resource_t& res4 = trs.data[3]; + + res1.resource_mapping.frequency_domain_alloc[0] = true; + res2.resource_mapping.frequency_domain_alloc[0] = true; + res3.resource_mapping.frequency_domain_alloc[0] = true; + res4.resource_mapping.frequency_domain_alloc[0] = true; + + res1.resource_mapping.first_symbol_idx = 6; + res2.resource_mapping.first_symbol_idx = 10; + res3.resource_mapping.first_symbol_idx = 6; + res4.resource_mapping.first_symbol_idx = 10; + + res1.resource_mapping.nof_ports = 1; + res2.resource_mapping.nof_ports = 1; + res3.resource_mapping.nof_ports = 1; + res4.resource_mapping.nof_ports = 1; + + res1.resource_mapping.cdm = srsran_csi_rs_cdm_nocdm; + res2.resource_mapping.cdm = srsran_csi_rs_cdm_nocdm; + res3.resource_mapping.cdm = srsran_csi_rs_cdm_nocdm; + res4.resource_mapping.cdm = srsran_csi_rs_cdm_nocdm; + + res1.resource_mapping.density = srsran_csi_rs_resource_mapping_density_three; + res2.resource_mapping.density = srsran_csi_rs_resource_mapping_density_three; + res3.resource_mapping.density = srsran_csi_rs_resource_mapping_density_three; + res4.resource_mapping.density = srsran_csi_rs_resource_mapping_density_three; + + if (carrier.scs == srsran_subcarrier_spacing_15kHz) { + res1.periodicity.period = 20; + res2.periodicity.period = 20; + res3.periodicity.period = 20; + res4.periodicity.period = 20; + + res1.periodicity.offset = 10; + res2.periodicity.offset = 10; + res3.periodicity.offset = 11; + res4.periodicity.offset = 11; + } else if (carrier.scs == srsran_subcarrier_spacing_30kHz) { + res1.periodicity.period = 40; + res2.periodicity.period = 40; + res3.periodicity.period = 40; + res4.periodicity.period = 40; + + res1.periodicity.offset = 20; + res2.periodicity.offset = 20; + res3.periodicity.offset = 21; + res4.periodicity.offset = 21; + } else { + srsran_terminate("Invalid subcarrier spacing %d kHz", 15U << (uint32_t)carrier.scs); + } + + res1.resource_mapping.freq_band = {0, carrier.nof_prb}; + res2.resource_mapping.freq_band = {0, carrier.nof_prb}; + res3.resource_mapping.freq_band = {0, carrier.nof_prb}; + res4.resource_mapping.freq_band = {0, carrier.nof_prb}; +} + +void phy_cfg_nr_default_t::make_pdsch_2_1_1_tdd(const srsran_carrier_nr_t& carrier, srsran_sch_hl_cfg_nr_t& pdsch) +{ + // Select PDSCH time resource allocation + pdsch.common_time_ra[0].mapping_type = srsran_sch_mapping_type_A; + pdsch.common_time_ra[0].k = 0; + pdsch.common_time_ra[0].sliv = srsran_ra_type2_to_riv(SRSRAN_NSYMB_PER_SLOT_NR - 2, 2, SRSRAN_NSYMB_PER_SLOT_NR); + pdsch.nof_common_time_ra = 1; + + // Setup PDSCH DMRS + pdsch.typeA_pos = srsran_dmrs_sch_typeA_pos_2; + pdsch.dmrs_typeA.present = true; + pdsch.dmrs_typeA.additional_pos = srsran_dmrs_sch_add_pos_2; + + // Make default CSI-RS for tracking from TS38101 Table 5.2.1 + make_nzp_csi_rs_ts38101_table_5_2_1(carrier, pdsch.nzp_csi_rs_sets[0]); +} + void phy_cfg_nr_default_t::make_pusch_default(srsran_sch_hl_cfg_nr_t& pusch) { // Select PUSCH time resource allocation @@ -124,7 +281,7 @@ void phy_cfg_nr_default_t::make_pucch_custom_one(srsran_pucch_nr_hl_cfg_t& pucch resource_big.format = SRSRAN_PUCCH_NR_FORMAT_2; resource_big.nof_prb = 1; resource_big.nof_symbols = 2; - resource_big.start_symbol_idx = 0; + resource_big.start_symbol_idx = 12; // Resource for SR srsran_pucch_nr_resource_t resource_sr = {}; @@ -173,11 +330,14 @@ void phy_cfg_nr_default_t::make_harq_auto(srsran_harq_ack_cfg_hl_t& harq, { // Generate as many entries as DL slots harq.nof_dl_data_to_ul_ack = SRSRAN_MIN(tdd_cfg.pattern1.nof_dl_slots, SRSRAN_MAX_NOF_DL_DATA_TO_UL); + if (tdd_cfg.pattern1.nof_dl_symbols > 0) { + harq.nof_dl_data_to_ul_ack++; + } // Set PDSCH to ACK timing delay to 4 or more for (uint32_t n = 0; n < harq.nof_dl_data_to_ul_ack; n++) { // Set the first slots into the first UL slot - if (n < (harq.nof_dl_data_to_ul_ack - 4)) { + if (harq.nof_dl_data_to_ul_ack >= 4 and n < (harq.nof_dl_data_to_ul_ack - 4)) { harq.dl_data_to_ul_ack[n] = harq.nof_dl_data_to_ul_ack - n; continue; } @@ -189,7 +349,7 @@ void phy_cfg_nr_default_t::make_harq_auto(srsran_harq_ack_cfg_hl_t& harq, } // Otherwise set delay to the first UL slot of the next TDD period - harq.dl_data_to_ul_ack[n] = 2 * harq.nof_dl_data_to_ul_ack - n; + harq.dl_data_to_ul_ack[n] = (tdd_cfg.pattern1.period_ms + tdd_cfg.pattern1.nof_dl_slots) - n; } // Zero the rest @@ -204,7 +364,7 @@ void phy_cfg_nr_default_t::make_harq_auto(srsran_harq_ack_cfg_hl_t& harq, void phy_cfg_nr_default_t::make_prach_default_lte(srsran_prach_cfg_t& prach) { prach.config_idx = 0; - prach.freq_offset = 2; + prach.freq_offset = 4; prach.root_seq_idx = 0; prach.is_nr = true; } @@ -215,12 +375,22 @@ phy_cfg_nr_default_t::phy_cfg_nr_default_t(const reference_cfg_t& reference_cfg) case reference_cfg_t::R_CARRIER_CUSTOM_10MHZ: make_carrier_custom_10MHz(carrier); break; + case reference_cfg_t::R_CARRIER_CUSTOM_20MHZ: + make_carrier_custom_20MHz(carrier); + break; + case reference_cfg_t::R_CARRIER_COUNT: + srsran_terminate("Invalid carrier reference"); } switch (reference_cfg.tdd) { case reference_cfg_t::R_TDD_CUSTOM_6_4: make_tdd_custom_6_4(tdd); break; + case reference_cfg_t::R_TDD_FR1_15_1: + make_tdd_fr1_15_1(tdd); + break; + case reference_cfg_t::R_TDD_COUNT: + srsran_terminate("Invalid TDD reference"); } switch (reference_cfg.pdcch) { @@ -233,6 +403,11 @@ phy_cfg_nr_default_t::phy_cfg_nr_default_t(const reference_cfg_t& reference_cfg) case reference_cfg_t::R_PDSCH_DEFAULT: make_pdsch_default(pdsch); break; + case reference_cfg_t::R_PDSCH_TS38101_5_2_1: + make_pdsch_2_1_1_tdd(carrier, pdsch); + break; + case reference_cfg_t::R_PDSCH_COUNT: + srsran_terminate("Invalid PDSCH reference configuration"); } switch (reference_cfg.pusch) { diff --git a/lib/src/common/thread_pool.cc b/lib/src/common/thread_pool.cc index 3587b857a..11659a58b 100644 --- a/lib/src/common/thread_pool.cc +++ b/lib/src/common/thread_pool.cc @@ -50,7 +50,7 @@ void thread_pool::worker::setup(uint32_t id, thread_pool* parent, uint32_t prio, void thread_pool::worker::run_thread() { - set_name(std::string("WORKER") + std::to_string(my_id)); + set_name(my_parent->get_id() + std::string("WORKER") + std::to_string(my_id)); while (running.load(std::memory_order_relaxed)) { wait_to_start(); if (running.load(std::memory_order_relaxed)) { @@ -70,9 +70,9 @@ uint32_t thread_pool::worker::get_id() return my_id; } -thread_pool::thread_pool(uint32_t max_workers_) : workers(max_workers_), status(max_workers_), cvar_worker(max_workers_) +thread_pool::thread_pool(uint32_t max_workers_, std::string id_) : + workers(max_workers_), max_workers(max_workers_), status(max_workers_), cvar_worker(max_workers_), id(id_) { - max_workers = max_workers_; for (uint32_t i = 0; i < max_workers; i++) { workers[i] = NULL; status[i] = IDLE; @@ -262,6 +262,11 @@ uint32_t thread_pool::get_nof_workers() return nof_workers; } +std::string thread_pool::get_id() +{ + return id; +} + /************************************************************************** * task_thread_pool - uses a queue to enqueue callables, that start * once a worker is available diff --git a/lib/src/phy/common/phy_common_nr.c b/lib/src/phy/common/phy_common_nr.c index 7948615c9..fea3cf97b 100644 --- a/lib/src/phy/common/phy_common_nr.c +++ b/lib/src/phy/common/phy_common_nr.c @@ -168,6 +168,9 @@ srsran_mcs_table_t srsran_mcs_table_from_str(const char* str) static const uint32_t phy_common_nr_valid_symbol_sz[PHY_COMMON_NR_NOF_VALID_SYMB_SZ] = {128, 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096}; +static const uint32_t phy_common_nr_valid_std_symbol_sz[PHY_COMMON_NR_NOF_VALID_SYMB_SZ] = + {128, 256, 512, 1024, 1536, 2048, 4096}; + uint32_t srsran_min_symbol_sz_rb(uint32_t nof_prb) { uint32_t nof_re = nof_prb * SRSRAN_NRE; @@ -176,9 +179,14 @@ uint32_t srsran_min_symbol_sz_rb(uint32_t nof_prb) return 0; } + const uint32_t* symbol_table = phy_common_nr_valid_symbol_sz; + if (srsran_symbol_size_is_standard()) { + symbol_table = phy_common_nr_valid_std_symbol_sz; + } + for (uint32_t i = 0; i < PHY_COMMON_NR_NOF_VALID_SYMB_SZ; i++) { - if (phy_common_nr_valid_symbol_sz[i] > nof_re) { - return phy_common_nr_valid_symbol_sz[i]; + if (symbol_table[i] > nof_re) { + return symbol_table[i]; } } diff --git a/lib/src/phy/phch/cqi.c b/lib/src/phy/phch/cqi.c index bd6b05206..641c42064 100644 --- a/lib/src/phy/phch/cqi.c +++ b/lib/src/phy/phch/cqi.c @@ -257,7 +257,7 @@ cqi_format2_subband_tostring(srsran_cqi_cfg_t* cfg, srsran_cqi_ue_subband_t* msg n += snprintf(buff + n, buff_len - n, ", cqi=%d", msg->subband_cqi); n += snprintf(buff + n, buff_len - n, ", label=%d", msg->subband_label); - + n += snprintf(buff + n, buff_len - n, ", sb_idx=%d", cfg->sb_idx); return n; } @@ -456,7 +456,6 @@ static bool cqi_get_N_tdd(uint32_t I_cqi_pmi, uint32_t* N_p, uint32_t* N_offset) static bool cqi_send(uint32_t I_cqi_pmi, uint32_t tti, bool is_fdd, uint32_t H) { - uint32_t N_p = 0; uint32_t N_offset = 0; @@ -480,7 +479,6 @@ static bool cqi_send(uint32_t I_cqi_pmi, uint32_t tti, bool is_fdd, uint32_t H) static bool ri_send(uint32_t I_cqi_pmi, uint32_t I_ri, uint32_t tti, bool is_fdd) { - uint32_t M_ri = 0; int N_offset_ri = 0; uint32_t N_p = 0; @@ -553,23 +551,55 @@ static int cqi_hl_get_bwp_J(int nof_prb) { if (nof_prb < 7) { return 0; + } else if (nof_prb <= 10) { + return 1; } else if (nof_prb <= 26) { - return 4; + return 2; } else if (nof_prb <= 63) { - return 6; + return 3; } else if (nof_prb <= 110) { - return 8; + return 4; } else { return -1; } } -/* Returns the number of bits to index a bandwidth part (L) - * L = ceil(log2(nof_prb/k/J)) +/* Returns the number of subbands in the j-th bandwidth part */ +static int cqi_sb_get_Nj(uint32_t j, uint32_t nof_prb) +{ + uint32_t J = cqi_hl_get_bwp_J(nof_prb); + if (J == 1) { + return (uint32_t)ceil((float)nof_prb / cqi_hl_get_subband_size(nof_prb)); + } else { + // all bw parts have the same number of subbands except the last one + uint32_t Nj = (uint32_t)ceil((float)nof_prb / cqi_hl_get_subband_size(nof_prb) / J); + if (j < J - 1) { + return Nj; + } else { + return Nj - 1; + } + } +} + +uint32_t srsran_cqi_get_sb_idx(uint32_t tti, + uint32_t subband_label, + const srsran_cqi_report_cfg_t* cqi_report_cfg, + const srsran_cell_t* cell) +{ + uint32_t bw_part_idx = srsran_cqi_periodic_sb_bw_part_idx(cqi_report_cfg, tti, cell->nof_prb, cell->frame_type); + return subband_label + bw_part_idx * cqi_sb_get_Nj(bw_part_idx, cell->nof_prb); +} + int srsran_cqi_hl_get_L(int nof_prb) { - return (int)ceil((float)nof_prb / cqi_hl_get_subband_size(nof_prb) / cqi_hl_get_bwp_J(nof_prb)); + int subband_size = cqi_hl_get_subband_size(nof_prb); + int bwp_J = cqi_hl_get_bwp_J(nof_prb); + if (subband_size <= 0 || bwp_J <= 0) { + ERROR("Invalid parameters"); + return SRSRAN_ERROR; + } + return (int)SRSRAN_CEIL_LOG2((double)nof_prb / (subband_size * bwp_J)); } bool srsran_cqi_periodic_ri_send(const srsran_cqi_report_cfg_t* cfg, uint32_t tti, srsran_frame_type_t frame_type) @@ -583,19 +613,51 @@ bool srsran_cqi_periodic_send(const srsran_cqi_report_cfg_t* cfg, uint32_t tti, return cfg->periodic_configured && cqi_send(cfg->pmi_idx, tti, frame_type == SRSRAN_FDD, 1); } +uint32_t cqi_sb_get_H(const srsran_cqi_report_cfg_t* cfg, uint32_t nof_prb) +{ + uint32_t K = cfg->subband_wideband_ratio; + uint32_t J = cqi_hl_get_bwp_J(nof_prb); + uint32_t H = J * K + 1; + return H; +} + bool srsran_cqi_periodic_is_subband(const srsran_cqi_report_cfg_t* cfg, uint32_t tti, uint32_t nof_prb, srsran_frame_type_t frame_type) { - uint32_t K = cfg->subband_wideband_ratio; - uint32_t J = cqi_hl_get_bwp_J(nof_prb); - uint32_t H = J * K + 1; + uint32_t H = cqi_sb_get_H(cfg, nof_prb); // A periodic report is subband if it's a CQI opportunity and is not wideband return srsran_cqi_periodic_send(cfg, tti, frame_type) && !cqi_send(cfg->pmi_idx, tti, frame_type == SRSRAN_FDD, H); } +uint32_t srsran_cqi_periodic_sb_bw_part_idx(const srsran_cqi_report_cfg_t* cfg, + uint32_t tti, + uint32_t nof_prb, + srsran_frame_type_t frame_type) +{ + uint32_t H = cqi_sb_get_H(cfg, nof_prb); + uint32_t N_p = 0, N_offset = 0; + + if (frame_type == SRSRAN_FDD) { + if (!cqi_get_N_fdd(cfg->pmi_idx, &N_p, &N_offset)) { + return false; + } + } else { + if (!cqi_get_N_tdd(cfg->pmi_idx, &N_p, &N_offset)) { + return false; + } + } + + uint32_t x = ((tti - N_offset) / N_p) % H; + if (x > 0) { + return (x - 1) % cqi_hl_get_bwp_J(nof_prb); + } else { + return 0; + } +} + // CQI-to-Spectral Efficiency: 36.213 Table 7.2.3-1 static float cqi_to_coderate[16] = {0, 0.1523, diff --git a/lib/src/phy/phch/dci_nr.c b/lib/src/phy/phch/dci_nr.c index 2d5479431..786c81164 100644 --- a/lib/src/phy/phch/dci_nr.c +++ b/lib/src/phy/phch/dci_nr.c @@ -31,15 +31,13 @@ */ #define DCI_NR_MIN_SIZE 12 -#define CEIL_LOG2(N) (((N) == 0) ? 0 : ceil(log2((double)(N)))) - static uint32_t dci_nr_freq_resource_size_type1(uint32_t N) { if (N == 0) { return 0; } - return (int)CEIL_LOG2(N * (N + 1) / 2.0); + return (int)SRSRAN_CEIL_LOG2(N * (N + 1) / 2.0); } static uint32_t dci_nr_freq_resource_size(srsran_resource_alloc_t alloc_type, uint32_t N_RBG, uint32_t N_BWP_RB) @@ -66,7 +64,7 @@ static uint32_t dci_nr_bwp_id_size(uint32_t N_BWP_RRC) N_BWP = N_BWP_RRC + 1; } - return (int)CEIL_LOG2(N_BWP); + return (int)SRSRAN_CEIL_LOG2(N_BWP); } static uint32_t dci_nr_time_res_size(uint32_t nof_time_res) @@ -75,7 +73,7 @@ static uint32_t dci_nr_time_res_size(uint32_t nof_time_res) // 4 bits are necessary for PUSCH default time resource assigment (TS 38.214 Table 6.1.2.1.1-2) nof_time_res = SRSRAN_MAX_NOF_TIME_RA; } - return (uint32_t)CEIL_LOG2(nof_time_res); + return (uint32_t)SRSRAN_CEIL_LOG2(nof_time_res); } static uint32_t dci_nr_ptrs_size(const srsran_dci_cfg_nr_t* cfg) @@ -160,9 +158,9 @@ static uint32_t dci_nr_srs_id_size(const srsran_dci_cfg_nr_t* cfg) for (uint32_t k = 1; k < SRSRAN_MIN(cfg->nof_ul_layers, cfg->nof_srs); k++) { N += cfg->nof_srs / k; } - return (uint32_t)CEIL_LOG2(N); + return (uint32_t)SRSRAN_CEIL_LOG2(N); } - return (uint32_t)CEIL_LOG2(N_srs); + return (uint32_t)SRSRAN_CEIL_LOG2(N_srs); } // Determines DCI format 0_0 according to TS 38.212 clause 7.3.1.1.1 @@ -1249,7 +1247,7 @@ static uint32_t dci_nr_format_1_1_sizeof(const srsran_dci_cfg_nr_t* cfg, srsran_ } // ZP CSI-RS trigger - 0, 1, or 2 bits - count += (int)CEIL_LOG2(cfg->nof_aperiodic_zp + 1); + count += (int)SRSRAN_CEIL_LOG2(cfg->nof_aperiodic_zp + 1); // For transport block 1: // Modulation and coding scheme – 5 bits @@ -1292,7 +1290,7 @@ static uint32_t dci_nr_format_1_1_sizeof(const srsran_dci_cfg_nr_t* cfg, srsran_ count += 3; // PDSCH-to-HARQ_feedback timing indicator – 0, 1, 2, or 3 bits - count += (int)CEIL_LOG2(cfg->nof_dl_to_ul_ack); + count += (int)SRSRAN_CEIL_LOG2(cfg->nof_dl_to_ul_ack); // Antenna port(s) – 4, 5, or 6 bits count += dci_nr_dl_ports_size(cfg); @@ -1365,7 +1363,7 @@ static int dci_nr_format_1_1_pack(const srsran_dci_nr_t* q, const srsran_dci_dl_ } // ZP CSI-RS trigger - 0, 1, or 2 bits - srsran_bit_unpack(dci->zp_csi_rs_id, &y, CEIL_LOG2(cfg->nof_aperiodic_zp + 1)); + srsran_bit_unpack(dci->zp_csi_rs_id, &y, SRSRAN_CEIL_LOG2(cfg->nof_aperiodic_zp + 1)); // For transport block 1: // Modulation and coding scheme – 5 bits @@ -1408,7 +1406,7 @@ static int dci_nr_format_1_1_pack(const srsran_dci_nr_t* q, const srsran_dci_dl_ srsran_bit_unpack(dci->pucch_resource, &y, 3); // PDSCH-to-HARQ_feedback timing indicator – 0, 1, 2, or 3 bits - srsran_bit_unpack(dci->harq_feedback, &y, (int)CEIL_LOG2(cfg->nof_dl_to_ul_ack)); + srsran_bit_unpack(dci->harq_feedback, &y, (int)SRSRAN_CEIL_LOG2(cfg->nof_dl_to_ul_ack)); // Antenna port(s) – 4, 5, or 6 bits srsran_bit_unpack(dci->ports, &y, dci_nr_dl_ports_size(cfg)); @@ -1495,7 +1493,7 @@ static int dci_nr_format_1_1_unpack(const srsran_dci_nr_t* q, srsran_dci_msg_nr_ } // ZP CSI-RS trigger - 0, 1, or 2 bits - dci->zp_csi_rs_id = srsran_bit_pack(&y, CEIL_LOG2(cfg->nof_aperiodic_zp + 1)); + dci->zp_csi_rs_id = srsran_bit_pack(&y, SRSRAN_CEIL_LOG2(cfg->nof_aperiodic_zp + 1)); // For transport block 1: // Modulation and coding scheme – 5 bits @@ -1538,7 +1536,7 @@ static int dci_nr_format_1_1_unpack(const srsran_dci_nr_t* q, srsran_dci_msg_nr_ dci->pucch_resource = srsran_bit_pack(&y, 3); // PDSCH-to-HARQ_feedback timing indicator – 0, 1, 2, or 3 bits - dci->harq_feedback = srsran_bit_pack(&y, (int)CEIL_LOG2(cfg->nof_dl_to_ul_ack)); + dci->harq_feedback = srsran_bit_pack(&y, (int)SRSRAN_CEIL_LOG2(cfg->nof_dl_to_ul_ack)); // Antenna port(s) – 4, 5, or 6 bits dci->ports = srsran_bit_pack(&y, dci_nr_dl_ports_size(cfg)); @@ -1613,7 +1611,7 @@ dci_nr_format_1_1_to_str(const srsran_dci_nr_t* q, const srsran_dci_dl_nr_t* dci } // ZP CSI-RS trigger - 0, 1, or 2 bits - if (CEIL_LOG2(cfg->nof_aperiodic_zp + 1) > 0) { + if (SRSRAN_CEIL_LOG2(cfg->nof_aperiodic_zp + 1) > 0) { len = srsran_print_check(str, str_len, len, "zp_csi_rs_id=%d ", dci->zp_csi_rs_id); } diff --git a/lib/src/phy/phch/harq_ack.c b/lib/src/phy/phch/harq_ack.c index 68cf25df5..4751e32e5 100644 --- a/lib/src/phy/phch/harq_ack.c +++ b/lib/src/phy/phch/harq_ack.c @@ -299,6 +299,11 @@ int srsran_harq_ack_insert_m(srsran_pdsch_ack_nr_t* ack_info, const srsran_harq_ } srsran_harq_ack_cc_t* cc = &ack_info->cc[m->resource.scell_idx]; + if (cc->M >= SRSRAN_UCI_NR_MAX_M) { + ERROR("Accumulated M HARQ feedback exceeds maximum (%d)", SRSRAN_UCI_NR_MAX_M); + return SRSRAN_ERROR; + } + // Find insertion index uint32_t idx = cc->M; // Append at the end by default for (uint32_t i = 0; i < cc->M; i++) { @@ -312,7 +317,7 @@ int srsran_harq_ack_insert_m(srsran_pdsch_ack_nr_t* ack_info, const srsran_harq_ cc->M += 1; // Make space for insertion - for (uint32_t i = cc->M - 1; i > idx; i--) { + for (uint32_t i = cc->M - 1; i != idx; i--) { cc->m[i] = cc->m[i - 1]; } diff --git a/lib/src/phy/phch/pdcch_nr.c b/lib/src/phy/phch/pdcch_nr.c index 673a93347..c3aa13bf5 100644 --- a/lib/src/phy/phch/pdcch_nr.c +++ b/lib/src/phy/phch/pdcch_nr.c @@ -78,12 +78,19 @@ static int srsran_pdcch_nr_get_ncce(const srsran_coreset_t* coreset, return SRSRAN_ERROR; } + // Calculate CORESET bandiwth in physical resource blocks + uint32_t coreset_bw = srsran_coreset_get_bw(coreset); + // Every REG is 1PRB wide and a CCE is 6 REG. So, the number of N_CCE is a sixth of the bandwidth times the number of // symbols - uint32_t N_cce = srsran_coreset_get_bw(coreset) * coreset->duration / 6; + uint32_t N_cce = coreset_bw * coreset->duration / 6; if (N_cce < L) { - ERROR("Error number of CCE %d is lower than the aggregation level %d", N_cce, L); + ERROR("Error CORESET (total bandwidth of %d RBs and %d CCEs) cannot fit the aggregation level %d (%d)", + coreset_bw, + N_cce, + L, + aggregation_level); return SRSRAN_ERROR; } diff --git a/lib/src/phy/phch/ra_ul_nr.c b/lib/src/phy/phch/ra_ul_nr.c index f5c404730..3a4afdb4e 100644 --- a/lib/src/phy/phch/ra_ul_nr.c +++ b/lib/src/phy/phch/ra_ul_nr.c @@ -508,7 +508,7 @@ static int ra_ul_nr_pucch_resource_hl(const srsran_pucch_nr_hl_cfg_t* cfg, } else if (O_uci <= N3 && cfg->sets[2].nof_resources > 0) { resource_set_id = 2; } else if (cfg->sets[3].nof_resources == 0) { - ERROR("Invalid PUCCH resource configuration, N3=%d, O_uci=%d", N3, O_uci); + ERROR("Invalid PUCCH resource configuration, N2=%d, N3=%d, O_uci=%d", N2, N3, O_uci); return SRSRAN_ERROR; } else if (O_uci > SRSRAN_UCI_NR_MAX_NOF_BITS) { ERROR("The number of UCI bits (%d), exceeds the maximum (%d)", O_uci, SRSRAN_UCI_NR_MAX_NOF_BITS); diff --git a/lib/src/phy/phch/sch_nr.c b/lib/src/phy/phch/sch_nr.c index 3c36e9921..be2f5c2c8 100644 --- a/lib/src/phy/phch/sch_nr.c +++ b/lib/src/phy/phch/sch_nr.c @@ -558,6 +558,18 @@ static int sch_nr_decode(srsran_sch_nr_t* q, return SRSRAN_ERROR_INVALID_INPUTS; } + // Protect softbuffer access + if (!tb->softbuffer.rx) { + ERROR("Missing softbuffer!"); + return SRSRAN_ERROR; + } + + // Protect PDU access + if (!res->payload) { + ERROR("Missing payload pointer!"); + return SRSRAN_ERROR; + } + int8_t* input_ptr = e_bits; uint32_t nof_iter_sum = 0; diff --git a/lib/src/phy/phch/test/CMakeLists.txt b/lib/src/phy/phch/test/CMakeLists.txt index 4b9036ac3..2ea5b0185 100644 --- a/lib/src/phy/phch/test/CMakeLists.txt +++ b/lib/src/phy/phch/test/CMakeLists.txt @@ -124,7 +124,7 @@ target_link_libraries(pssch_pscch_file_test srsran_phy) # TM2 file tests add_lte_test(pssch_pscch_file_test_ideal_tm2_p100 pssch_pscch_file_test -p 100 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm2_p100_c335_s30.72e6.dat) -set_property(TEST pssch_pscch_file_test_ideal_tm2_p100 PROPERTY PASS_REGULAR_EXPRESSION "num_decoded_sci=2 num_decoded_tb=1") +set_property(TEST pssch_pscch_file_test_ideal_tm2_p100 PROPERTY PASS_REGULAR_EXPRESSION "num_decoded_sci=[2,3] num_decoded_tb=1") # TM4 file tests (first SF is sf_idx = 6 such that the PSSCH sf_idx=0) add_lte_test(pssch_pscch_file_test_ideal_tm4_p100 pssch_pscch_file_test -p 100 -t 4 -s 10 -n 10 -d -m 6 -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm4_p100_c335_size10_num10_cshift0_s30.72e6.dat) diff --git a/lib/src/phy/rf/CMakeLists.txt b/lib/src/phy/rf/CMakeLists.txt index a412b90fb..8bd3f899b 100644 --- a/lib/src/phy/rf/CMakeLists.txt +++ b/lib/src/phy/rf/CMakeLists.txt @@ -59,6 +59,13 @@ if(RF_FOUND) list(APPEND SOURCES_RF rf_soapy_imp.c) endif (SOAPYSDR_FOUND AND ENABLE_SOAPYSDR) + if(SKIQ_FOUND) + add_executable(skiq_pps_test skiq_pps_test.c) + target_link_libraries(skiq_pps_test ${SKIQ_LIBRARIES} rt pthread m) + add_definitions(-DENABLE_SIDEKIQ) + list(APPEND SOURCES_RF rf_skiq_imp.c rf_skiq_imp_card.c rf_skiq_imp_port.c) + endif(SKIQ_FOUND) + if (ZEROMQ_FOUND) add_definitions(-DENABLE_ZEROMQ) list(APPEND SOURCES_RF rf_zmq_imp.c rf_zmq_imp_tx.c rf_zmq_imp_rx.c) @@ -80,6 +87,10 @@ if(RF_FOUND) target_link_libraries(srsran_rf ${SOAPYSDR_LIBRARIES}) endif (SOAPYSDR_FOUND AND ENABLE_SOAPYSDR) + if(SKIQ_FOUND) + target_link_libraries(srsran_rf ${SKIQ_LIBRARIES} rt) + endif(SKIQ_FOUND) + if (ZEROMQ_FOUND) target_link_libraries(srsran_rf ${ZEROMQ_LIBRARIES}) add_executable(rf_zmq_test rf_zmq_test.c) diff --git a/lib/src/phy/rf/rf_dev.h b/lib/src/phy/rf/rf_dev.h index 647c7c5ba..ef136b894 100644 --- a/lib/src/phy/rf/rf_dev.h +++ b/lib/src/phy/rf/rf_dev.h @@ -225,6 +225,41 @@ static rf_dev_t dev_zmq = {"zmq", .srsran_rf_send_timed_multi = rf_zmq_send_timed_multi}; #endif +/* Define implementation for Sidekiq */ +#ifdef ENABLE_SIDEKIQ + +#include "rf_skiq_imp.h" + +static rf_dev_t dev_skiq = {.name = "Sidekiq", + .srsran_rf_devname = rf_skiq_devname, + .srsran_rf_start_rx_stream = rf_skiq_start_rx_stream, + .srsran_rf_stop_rx_stream = rf_skiq_stop_rx_stream, + .srsran_rf_flush_buffer = rf_skiq_flush_buffer, + .srsran_rf_has_rssi = rf_skiq_has_rssi, + .srsran_rf_get_rssi = rf_skiq_get_rssi, + .srsran_rf_suppress_stdout = rf_skiq_suppress_stdout, + .srsran_rf_register_error_handler = rf_skiq_register_error_handler, + .srsran_rf_open = rf_skiq_open, + .srsran_rf_open_multi = rf_skiq_open_multi, + .srsran_rf_close = rf_skiq_close, + .srsran_rf_set_rx_srate = rf_skiq_set_rx_srate, + .srsran_rf_set_tx_srate = rf_skiq_set_tx_srate, + .srsran_rf_set_rx_gain = rf_skiq_set_rx_gain, + .srsran_rf_set_tx_gain = rf_skiq_set_tx_gain, + .srsran_rf_set_tx_gain_ch = rf_skiq_set_tx_gain_ch, + .srsran_rf_set_rx_gain_ch = rf_skiq_set_rx_gain_ch, + .srsran_rf_get_rx_gain = rf_skiq_get_rx_gain, + .srsran_rf_get_tx_gain = rf_skiq_get_tx_gain, + .srsran_rf_get_info = rf_skiq_get_info, + .srsran_rf_set_rx_freq = rf_skiq_set_rx_freq, + .srsran_rf_set_tx_freq = rf_skiq_set_tx_freq, + .srsran_rf_get_time = rf_skiq_get_time, + .srsran_rf_recv_with_time = rf_skiq_recv_with_time, + .srsran_rf_recv_with_time_multi = rf_skiq_recv_with_time_multi, + .srsran_rf_send_timed = rf_skiq_send_timed, + .srsran_rf_send_timed_multi = rf_skiq_send_timed_multi}; +#endif + //#define ENABLE_DUMMY_DEV #ifdef ENABLE_DUMMY_DEV @@ -255,6 +290,9 @@ static rf_dev_t* available_devices[] = { #ifdef ENABLE_ZEROMQ &dev_zmq, #endif +#ifdef ENABLE_SIDEKIQ + &dev_skiq, +#endif #ifdef ENABLE_DUMMY_DEV &dev_dummy, #endif diff --git a/lib/src/phy/rf/rf_skiq_imp.c b/lib/src/phy/rf/rf_skiq_imp.c new file mode 100644 index 000000000..d1e9fe5a3 --- /dev/null +++ b/lib/src/phy/rf/rf_skiq_imp.c @@ -0,0 +1,947 @@ +/** + * + * \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 + +#include + +#include "rf_helper.h" +#include "rf_skiq_imp.h" +#include "rf_skiq_imp_card.h" + +/** + * According the document Sidekiq API 4.13.0 @ref AD9361TimestampSlip: + * Functions that will affect the timestamp: + * • skiq_write_rx_LO_freq() + * • skiq_write_rx_sample_rate_and_bandwidth() + * • skiq_write_tx_LO_freq() + * • skiq_run_tx_quadcal() + * • skiq_write_rx_freq_tune_mode() + * • skiq_write_tx_freq_tune_mode() + * Functions that will be affected by the timestamp slip: + * • skiq_read_last_1pps_timestamp() + * • skiq_receive() + * • skiq_transmit() + * • skiq_read_curr_rx_timestamp() + * • skiq_read_curr_tx_timestamp() + * + * The functions mentioned on the first group above can be divided in two groups. The first group are the ones that + * require restart the tx/rx streams of cards: + * • skiq_write_rx_sample_rate_and_bandwidth() + * + * The module assumes: + * - Tx and Rx sampling rates are equal + * - Tx/Rx shall be stopped during the configuration + * - skiq_stop_rx_streaming_multi_immediate can be called while skiq_receive is being executed + * - skiq_receive shall not be called while skiq_stop_rx_streaming_multi_immediate + * + * In order to update the sampling rate, the RF module shall: + * - Stop all cards Rx streams + * - Stop all cards Tx streams + * - Update Tx/Rx sample rates + * - Start Rx stream + * - enable Tx stream on the next transmission + * + * The second group do not require restarting the tx/rx streams. Indeed, they only affect to a single card and there is + * no interest on stalling the rest of cards stream. Because of this, the module shall suspend the affected card. + * • skiq_write_rx_LO_freq() + * • skiq_write_tx_LO_freq() + * + * The module assumes: + * - The Tx/Rx LO frequency is changed for a single card + * - The Tx/Rx is stalled only in selected card + * - The rest of cards shall keep operating without stalling their streams + * + * In order to update the Tx/Rx LO frequencies, the RF module shall: + * - Suspend the Tx/Rx streams of the card: + * - If receive port ring-buffer has samples, the module shall keep reading from ringbuffer; + * - Otherwise, the module shall not read from ring-buffer and write zeros in the receive buffer + * - Set the Tx/Rx LO frequency + * - Resume the reception + * + */ + +uint32_t rf_skiq_logging_level = SKIQ_LOG_INFO; + +typedef struct { + uint32_t nof_cards; + uint32_t nof_ports; + + rf_skiq_card_t cards[SKIQ_MAX_NUM_CARDS]; + + float cur_tx_gain; + + srsran_rf_info_t info; + + uint64_t next_tstamp; + + double current_srate_hz; + cf_t dummy_buffer[RF_SKIQ_DUMMY_BUFFER_SIZE]; + + pthread_mutex_t mutex_rx; ///< Makes sure receive function and sampling rate setter are not running simultaneously + +} rf_skiq_handler_t; + +void rf_skiq_suppress_stdout(void* h) +{ + SKIQ_RF_INFO("Suppressing stdout... lowering logging level to warning\n"); + rf_skiq_logging_level = SKIQ_LOG_WARNING; +} + +static bool rf_skiq_is_streaming(rf_skiq_handler_t* h) +{ + // If a single card is streaming, return true + for (uint32_t i = 0; i < h->nof_cards; i++) { + if (rf_skiq_card_is_streaming(&h->cards[i])) { + return true; + } + } + + return false; +} + +bool rf_skiq_check_synch(rf_skiq_handler_t* h) +{ + // Get first card system timestamp + int64_t ts0_sys = (int64_t)rf_skiq_card_read_sys_timestamp(&h->cards[0]); + int64_t ts0_rf = (int64_t)rf_skiq_card_read_rf_timestamp(&h->cards[0]); + SKIQ_RF_INFO(" ... Card 0 TS(sys/rf)=%ld/%ld\n", ts0_sys, ts0_rf); + + bool pass = true; + // Compare all the other card timestamps + for (uint32_t i = 1; i < h->nof_cards; i++) { + int64_t ts2_sys = (int64_t)rf_skiq_card_read_sys_timestamp(&h->cards[i]); + int64_t ts2_rf = (int64_t)rf_skiq_card_read_rf_timestamp(&h->cards[i]); + + // Use current sampling rate + double srate = h->current_srate_hz; + + // If the current srate was not set (zero, nan or Inf), read it back from the first card + if (!isnormal(srate)) { + uint32_t srate_int = 0; + if (skiq_read_rx_sample_rate(0, skiq_rx_hdl_A1, &srate_int, &srate)) { + ERROR("Error reading sampling rate\n"); + } + } + + // Make sure all cards system and RF timestamps are inside maximum allowed error window + bool card_pass = labs(ts2_sys - ts0_sys) < SKIQ_CARD_SYNC_MAX_ERROR; + card_pass = card_pass && labs(ts2_rf - ts0_rf) < (int64_t)(srate / 2); + + // It is enough that a card does not pass to fail the check + pass = pass && card_pass; + + SKIQ_RF_INFO(" ... Card %d TS(sys/rf)=(%ld/%ld) (%.4f/%.4f). %s\n", + i, + ts2_sys, + ts2_rf, + (double)labs(ts2_sys - ts0_sys) / (double)SKIQ_SYS_TIMESTAMP_FREQ, + (double)labs(ts2_rf - ts0_rf) / srate, + card_pass ? "Ok" : "KO"); + } + + return pass; +} + +/** + * Synchronizes single and multiple cards using the PPS signal. This function helper shall be used once at + * initialization. + * + * @param h SKIQ driver handler + * @return SRSRAN_SUCCESS if it is possible to synchronize boards, SRSRAN_ERROR otherwise + */ +static int rf_skiq_synch_cards(rf_skiq_handler_t* h) +{ + bool do_1pps = h->nof_cards > 1; //< PPS is required when more than one card is used + uint32_t trials = 0; //< Count PPS synchronization check trials + + // Try synchronizing timestamps of all cards up to 10 trials + do { + SKIQ_RF_INFO("Resetting system timestamp trial %d/%d\n", trials + 1, SKIQ_CARD_SYNCH_MAX_TRIALS); + + // Reset timestamp in next PPS + for (int i = 0; i < h->nof_cards; i++) { + // Sets the timestamps to the last Rx known time. + if (rf_skiq_card_update_timestamp( + &h->cards[i], do_1pps, h->cards->rx_ports->rb_tstamp_rem + h->current_srate_hz) != SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + } + + // It could be that one or more cards PPS reset was issued just after a PPS signal, so only if there is PPS + // (multiple cards), verifies that all cards are synchronised on the same PPS + if (!do_1pps) { + return SRSRAN_SUCCESS; + } + + // Wait for a second to pass + SKIQ_RF_INFO(" ... Waiting PPS to pass ...\n"); + sleep(1); + SKIQ_RF_INFO(" ... Checking:\n"); + + // Successful synchronization across boards! + if (rf_skiq_check_synch(h)) { + return SRSRAN_SUCCESS; + } + + // Increment trial count + trials++; + } while (trials < SKIQ_CARD_SYNCH_MAX_TRIALS); + + // All trials have been consumed without a Successful synchronization + ERROR("Error card synchronization failed\n"); + return SRSRAN_ERROR; +} + +void rf_skiq_register_error_handler(void* h_, srsran_rf_error_handler_t error_handler, void* arg) +{ + rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_; + + SKIQ_RF_INFO("Registering error handler...\n"); + + // Set error handler for each card + for (uint32_t i = 0; i < h->nof_cards; i++) { + rf_skiq_card_set_error_handler(&h->cards[i], error_handler, arg); + } +} + +const char* rf_skiq_devname(void* h) +{ + return "Sidekiq"; +} + +int rf_skiq_start_rx_stream(void* h_, bool now) +{ + rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_; + + rf_skiq_synch_cards(h); + + // Get current system timestamp, assume all cards are synchronized + uint64_t ts = rf_skiq_card_read_sys_timestamp(&h->cards[0]); + + // Advance a 10th of a second (100ms) + ts += SKIQ_SYS_TIMESTAMP_FREQ / 10; + + // Start streams for each card at the indicated timestamp... + for (uint32_t i = 0; i < h->nof_cards; i++) { + if (rf_skiq_card_start_rx_streaming(&h->cards[i], ts)) { + return SRSRAN_ERROR; + } + } + + return SRSRAN_SUCCESS; +} + +int rf_skiq_stop_rx_stream(void* h_) +{ + rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_; + + for (int i = 0; i < h->nof_cards; i++) { + rf_skiq_card_stop_rx_streaming(&h->cards[i]); + } + + return SRSRAN_SUCCESS; +} + +static int rf_skiq_send_end_of_burst(void* h_) +{ + rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_; + + for (int i = 0; i < h->nof_cards; i++) { + rf_skiq_card_end_of_burst(&h->cards[i]); + } + + return SRSRAN_SUCCESS; +} + +void rf_skiq_flush_buffer(void* h) +{ + SKIQ_RF_INFO("Flushing buffers...\n"); + + int n; + void* data[SKIQ_MAX_CHANNELS] = {}; + do { + n = rf_skiq_recv_with_time_multi(h, data, 1024, 0, NULL, NULL); + } while (n > 0); +} + +bool rf_skiq_has_rssi(void* h) +{ + return false; +} + +float rf_skiq_get_rssi(void* h) +{ + return 0.0f; +} + +int rf_skiq_open(char* args, void** h) +{ + return rf_skiq_open_multi(args, h, 1); +} + +void rf_skiq_log_msg(int32_t priority, const char* message) +{ + if (priority <= rf_skiq_logging_level) { + printf("%s", message); + } +} + +int rf_skiq_open_multi(char* args, void** h_, uint32_t nof_channels) +{ + // Check number of antennas bounds + if (nof_channels < SKIQ_MIN_CHANNELS || nof_channels > SKIQ_MAX_CHANNELS) { + ERROR("Number of channels (%d) not supported (%d-%d)\n", nof_channels, SKIQ_MIN_CHANNELS, SKIQ_MAX_CHANNELS); + return SRSRAN_ERROR_OUT_OF_BOUNDS; + } + + rf_skiq_handler_t* h = (rf_skiq_handler_t*)calloc(1, sizeof(rf_skiq_handler_t)); + if (!h) { + return SRSRAN_ERROR; + } + *h_ = h; + + // Parse main parameters + parse_uint32(args, "nof_cards", 0, &h->nof_cards); + parse_uint32(args, "nof_ports", 0, &h->nof_ports); + + char log_level[RF_PARAM_LEN] = "info"; + parse_string(args, "log_level", 0, log_level); + if (strcmp(log_level, "info") == 0) { + rf_skiq_logging_level = SKIQ_LOG_INFO; + } else if (strcmp(log_level, "debug") == 0) { + rf_skiq_logging_level = SKIQ_LOG_DEBUG; + } else if (strcmp(log_level, "warn") == 0) { + rf_skiq_logging_level = SKIQ_LOG_WARNING; + } else if (strcmp(log_level, "error") == 0) { + rf_skiq_logging_level = SKIQ_LOG_ERROR; + } else { + ERROR("Error log_level %s is undefined. Options: debug, info, warn and error\n", log_level); + return SRSRAN_ERROR; + } + + // Register Logger + skiq_register_logging(&rf_skiq_log_msg); + + // Get available cards + uint8_t nof_available_cards = 0; + uint8_t available_cards[SKIQ_MAX_NUM_CARDS] = {}; + if (skiq_get_cards(skiq_xport_type_auto, &nof_available_cards, available_cards)) { + ERROR("Getting available cards\n"); + return SRSRAN_ERROR; + } + + // + if (h->nof_cards == 0 && h->nof_ports == 0) { + if (nof_channels <= (uint32_t)nof_available_cards) { + // One channel per card + h->nof_cards = nof_channels; + h->nof_ports = 1; + } else if (nof_channels <= RF_SKIQ_MAX_PORTS_CARD) { + // One channel per port + h->nof_cards = 1; + h->nof_ports = nof_channels; + } else if (nof_channels % RF_SKIQ_MAX_PORTS_CARD == 0) { + // use all ports + h->nof_cards = nof_channels / RF_SKIQ_MAX_PORTS_CARD; + h->nof_ports = RF_SKIQ_MAX_PORTS_CARD; + } else if (nof_channels % nof_available_cards == 0) { + // use all cards + h->nof_cards = nof_available_cards; + h->nof_ports = nof_channels / nof_available_cards; + } else { + ERROR("Error deducing the number of cards and ports"); + } + } else if (h->nof_ports == 0 && nof_channels % h->nof_cards == 0) { + h->nof_ports = nof_channels / h->nof_cards; + } else if (h->nof_cards == 0 && nof_channels % h->nof_ports == 0) { + h->nof_cards = nof_channels / h->nof_ports; + } + + if (h->nof_cards == 0 || h->nof_cards > nof_available_cards) { + ERROR("Error invalid number of cards %d, available %d\n", h->nof_cards, nof_available_cards); + return SRSRAN_ERROR_OUT_OF_BOUNDS; + } + + if (h->nof_ports == 0 || h->nof_ports > RF_SKIQ_MAX_PORTS_CARD) { + ERROR("Error invalid number of cards %d, available %d\n", h->nof_cards, nof_available_cards); + return SRSRAN_ERROR_OUT_OF_BOUNDS; + } + + // Create default port options + rf_skiq_port_opts_t port_opts = {}; + port_opts.tx_rb_size = 2048; + port_opts.rx_rb_size = 2048; + port_opts.chan_mode = (h->nof_ports > 1) ? skiq_chan_mode_dual : skiq_chan_mode_single; + port_opts.stream_mode = skiq_rx_stream_mode_balanced; + + // Parse other options + parse_uint32(args, "tx_rb_size", 0, &port_opts.tx_rb_size); + parse_uint32(args, "rx_rb_size", 0, &port_opts.rx_rb_size); + parse_string(args, "mode", 0, port_opts.stream_mode_str); + + if (strlen(port_opts.stream_mode_str) > 0) { + if (strcmp(port_opts.stream_mode_str, "low_latency") == 0) { + port_opts.stream_mode = skiq_rx_stream_mode_low_latency; + } else if (strcmp(port_opts.stream_mode_str, "balanced") == 0) { + port_opts.stream_mode = skiq_rx_stream_mode_balanced; + } else if (strcmp(port_opts.stream_mode_str, "high_tput") == 0) { + port_opts.stream_mode = skiq_rx_stream_mode_high_tput; + } else { + ERROR("Invalid mode: %s; Valid modes are: low_latency, balanced, high_tput\n", port_opts.stream_mode_str); + return SRSRAN_ERROR; + } + } + + SKIQ_RF_INFO("Opening %d SKIQ cards with %d ports...\n", h->nof_cards, h->nof_ports); + + if (pthread_mutex_init(&h->mutex_rx, NULL)) { + ERROR("Error initialising mutex\n"); + return SRSRAN_ERROR; + } + + // Initialise driver + if (skiq_init(skiq_xport_type_auto, skiq_xport_init_level_full, available_cards, h->nof_cards)) { + ERROR("Unable to initialise libsidekiq driver\n"); + return SRSRAN_ERROR; + } + + // Initialise each card + for (uint32_t i = 0; i < h->nof_cards; i++) { + if (rf_skiq_card_init(&h->cards[i], available_cards[i], h->nof_ports, &port_opts)) { + return SRSRAN_ERROR; + } + } + + // Parse default frequencies + for (uint32_t i = 0, ch = 0; i < h->nof_cards; i++) { + for (uint32_t j = 0; j < h->nof_ports; j++, ch++) { + double tx_freq = 0.0; + parse_double(args, "tx_freq", ch, &tx_freq); + + if (isnormal(tx_freq)) { + rf_skiq_set_tx_freq(h, ch, tx_freq); + } + double rx_freq = 0.0; + parse_double(args, "rx_freq", ch, &rx_freq); + + if (isnormal(rx_freq)) { + rf_skiq_set_rx_freq(h, ch, rx_freq); + } + } + } + + // Set a default gain + rf_skiq_set_rx_gain(h, SKIQ_RX_GAIN_DEFAULT_dB); + + // Parse default sample rate + double srate_hz = 0.0; + parse_double(args, "srate", 0, &srate_hz); + srate_hz = isnormal(srate_hz) ? srate_hz : SKIQ_DEFAULT_SAMPLING_RATE_HZ; + + // Set a default sampling rate, default can be too low + rf_skiq_set_tx_srate(h, srate_hz); + rf_skiq_set_rx_srate(h, srate_hz); + + return SRSRAN_SUCCESS; +} + +int rf_skiq_close(void* h_) +{ + rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_; + + SKIQ_RF_INFO("Closing...\n"); + + // Ensure Tx/Rx streaming is stopped + rf_skiq_send_end_of_burst(h); + rf_skiq_stop_rx_stream(h); + + // Free all open cards + for (int i = 0; i < h->nof_cards; i++) { + rf_skiq_card_close(&h->cards[i]); + } + + // Close sidekiq SDK + skiq_exit(); + + pthread_mutex_destroy(&h->mutex_rx); + + // Deallocate object memory + if (h != NULL) { + free(h); + } + + return SRSRAN_SUCCESS; +} + +static double rf_skiq_set_srate_hz(rf_skiq_handler_t* h, double srate_hz) +{ + // If the sampling rate is not modified dont bother + if (h->current_srate_hz == srate_hz) { + return srate_hz; + } + SKIQ_RF_INFO("Setting sampling rate to %.2f MHz ...\n", srate_hz / 1e6); + + // Save streaming state + bool is_streaming = rf_skiq_is_streaming(h); + + // Stop streaming + SKIQ_RF_INFO(" ... Stop Tx/Rx streaming\n"); + rf_skiq_send_end_of_burst(h); + rf_skiq_stop_rx_stream(h); + + // Set sampling + SKIQ_RF_INFO(" ... Setting sampling rates to %.2f MHz\n", srate_hz / 1e6); + pthread_mutex_lock(&h->mutex_rx); + for (uint32_t i = 0; i < h->nof_cards; i++) { + rf_skiq_card_set_srate_hz(&h->cards[i], (uint32_t)srate_hz); + } + pthread_mutex_unlock(&h->mutex_rx); + + // Start streaming if it was started + if (is_streaming) { + SKIQ_RF_INFO(" ... Start Rx streaming\n"); + rf_skiq_start_rx_stream(h, true); + } + + // Update current sampling rate + h->current_srate_hz = srate_hz; + SKIQ_RF_INFO(" ... Done!\n"); + + return srate_hz; +} + +double rf_skiq_set_rx_srate(void* h, double sample_rate) +{ + return rf_skiq_set_srate_hz((rf_skiq_handler_t*)h, sample_rate); +} + +double rf_skiq_set_tx_srate(void* h, double sample_rate) +{ + return rf_skiq_set_srate_hz((rf_skiq_handler_t*)h, sample_rate); +} + +int rf_skiq_set_rx_gain(void* h_, double rx_gain) +{ + rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_; + + for (uint32_t i = 0; i < h->nof_cards; i++) { + rf_skiq_card_set_rx_gain_db(&h->cards[i], h->nof_ports, rx_gain); + } + + return SRSRAN_SUCCESS; +} + +int rf_skiq_set_tx_gain(void* h_, double tx_gain) +{ + rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_; + + for (uint32_t i = 0; i < h->nof_cards; i++) { + h->cur_tx_gain = rf_skiq_card_set_tx_gain_db(&h->cards[i], h->nof_ports, tx_gain); + } + + return SRSRAN_SUCCESS; +} + +int rf_skiq_set_tx_gain_ch(void* h_, uint32_t ch, double tx_gain) +{ + rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_; + uint32_t card_idx = ch / h->nof_ports; + uint32_t port_idx = ch % h->nof_ports; + + if (card_idx >= h->nof_cards || port_idx >= h->nof_ports) { + return SRSRAN_ERROR_OUT_OF_BOUNDS; + } + + tx_gain = rf_skiq_card_set_tx_gain_db(&h->cards[card_idx], port_idx, tx_gain); + + if (ch == 0) { + h->cur_tx_gain = tx_gain; + } + + return SRSRAN_SUCCESS; +} + +int rf_skiq_set_rx_gain_ch(void* h_, uint32_t ch, double rx_gain) +{ + rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_; + uint32_t card_idx = ch / h->nof_ports; + uint32_t port_idx = ch % h->nof_ports; + + if (card_idx >= h->nof_cards || port_idx >= h->nof_ports) { + return SRSRAN_ERROR_OUT_OF_BOUNDS; + } + + rx_gain = rf_skiq_card_set_rx_gain_db(&h->cards[card_idx], port_idx, rx_gain); + + if (ch == 0) { + h->cur_tx_gain = rx_gain; + } + + return SRSRAN_SUCCESS; +} + +double rf_skiq_get_rx_gain(void* h_) +{ + rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_; + + return h->cards[0].cur_rx_gain_db; +} + +double rf_skiq_get_tx_gain(void* h_) +{ + rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_; + return h->cur_tx_gain; +} + +srsran_rf_info_t* rf_skiq_get_info(void* h_) +{ + srsran_rf_info_t* ret = NULL; + rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_; + + if (h != NULL) { + ret = &h->info; + + rf_skiq_card_update_gain_table(&h->cards[0]); + + ret->min_tx_gain = 0.25 * (double)h->cards[0].param.tx_param->atten_quarter_db_max; + ret->max_tx_gain = 0.25 * (double)h->cards[0].param.tx_param->atten_quarter_db_min; + ret->min_rx_gain = h->cards[0].rx_gain_table_db[h->cards[0].param.rx_param[0].gain_index_min]; + ret->max_rx_gain = h->cards[0].rx_gain_table_db[h->cards[0].param.rx_param[0].gain_index_max]; + } + + return ret; +} + +double rf_skiq_set_rx_freq(void* h_, uint32_t ch, double freq) +{ + rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_; + + uint32_t card_idx = ch / h->nof_ports; + uint32_t port_idx = ch % h->nof_ports; + +#pragma message "TODO: The Rx stream needs to stop, RF timestamp shall be aligned with other cards and start again" + + if (card_idx < h->nof_cards && port_idx < h->nof_ports) { + return rf_skiq_card_set_rx_freq_hz(&h->cards[card_idx], port_idx, freq); + } + + return freq; +} + +double rf_skiq_set_tx_freq(void* h_, uint32_t ch, double freq) +{ + rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_; + + uint32_t card_idx = ch / h->nof_ports; + uint32_t port_idx = ch % h->nof_ports; + +#pragma message "TODO: The Rx stream needs to stop, RF timestamp shall be aligned with other cards and start again" + + if (card_idx < h->nof_cards && port_idx < h->nof_ports) { + return rf_skiq_card_set_tx_freq_hz(&h->cards[card_idx], port_idx, freq); + } + + return freq; +} + +void tstamp_to_time(rf_skiq_handler_t* h, uint64_t tstamp, time_t* secs, double* frac_secs) +{ + uint64_t srate_hz = (uint64_t)h->current_srate_hz; + + if (srate_hz == 0) { + ERROR("Warning: Sampling rate has not been set yet.\n"); + srate_hz = UINT64_MAX; + } + + if (secs) { + *secs = (time_t)tstamp / srate_hz; + } + if (frac_secs) { + uint64_t rem = tstamp % srate_hz; + *frac_secs = (double)rem / h->current_srate_hz; + } +} + +uint64_t time_to_tstamp(rf_skiq_handler_t* h, time_t secs, double frac_secs) +{ + return secs * h->current_srate_hz + frac_secs * h->current_srate_hz; +} + +void rf_skiq_get_time(void* h_, time_t* secs, double* frac_secs) +{ + rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_; + uint64_t tstamp = rf_skiq_card_read_rf_timestamp(&h->cards[0]); + tstamp_to_time(h, tstamp, secs, frac_secs); +} + +static int +rf_skiq_discard_rx_samples(rf_skiq_handler_t* h, uint32_t card, uint32_t port, uint32_t nsamples, uint64_t* ts_start) +{ + *ts_start = 0; + while (nsamples > 0) { + uint64_t ts = 0; + + // Receive in dummy buffer + int32_t n = rf_skiq_card_receive( + &h->cards[card], port, h->dummy_buffer, SRSRAN_MIN(nsamples, RF_SKIQ_DUMMY_BUFFER_SIZE), &ts); + + // Check for error + if (n < 0) { + ERROR("An error occurred discarding %d Rx samples for channel %d:%d\n", nsamples, card, port); + return n; + } + + // Save first timestamp + if (*ts_start == 0) { + *ts_start = ts; + } + + // Decrement pending samples + nsamples -= n; + } + + return nsamples; +} + +static int rf_skiq_synch_rx_ports(rf_skiq_handler_t* h) +{ + int64_t tstamp_min = INT64_MAX; + int64_t tstamp_max = 0; + + // no need to synchronize + if (h->nof_cards * h->nof_ports < 2) { + return SRSRAN_SUCCESS; + } + + // Find minimum and maximum next timestamps + for (uint32_t card = 0; card < h->nof_cards; card++) { + // Iterate for all ports + for (uint32_t port = 0; port < h->nof_ports; port++) { + int64_t ts = (int64_t)rf_skiq_card_get_rx_timestamp(&h->cards[card], port); + + // If the card is not streaming or suspended will return TS 0; so skip + if (ts == 0UL) { + continue; + } + + // Is minimum? + tstamp_min = SRSRAN_MIN(tstamp_min, ts); + + // Is maximum? + tstamp_max = SRSRAN_MAX(tstamp_max, ts); + } + } + + // Check if any synchronization is required + if (tstamp_max == tstamp_min) { + return SRSRAN_SUCCESS; + } + + // Align all channels to the maximum timestamp + for (uint32_t card = 0; card < h->nof_cards; card++) { + // Iterate for all ports + for (uint32_t port = 0; port < h->nof_ports; port++) { + uint64_t ts = rf_skiq_card_get_rx_timestamp(&h->cards[card], port); + + // If the card is not streaming or suspended will return TS 0; so skip + if (ts == 0UL) { + continue; + } + + // Calculate number of samples + int nsamples = (int)(tstamp_max - (int64_t)ts); + + // Skip port if negative or zero (possible if stream is enabled during this time) + if (nsamples <= 0) { + continue; + } + + // Too many samples, sign of some extreme error + if (nsamples > SKIQ_PORT_SYNC_MAX_GAP) { + ERROR("too many samples to align (%d) for channel %d:%d\n", nsamples, card, port); + return SRSRAN_ERROR; + } + + ts = 0; + if (rf_skiq_discard_rx_samples(h, card, port, nsamples, &ts) < SRSRAN_SUCCESS) { + ERROR("Error occurred during Rx streams alignment."); + return SRSRAN_ERROR; + } + } + } + + return SRSRAN_SUCCESS; +} + +int rf_skiq_recv_with_time(void* h, void* data, uint32_t nsamples, bool blocking, time_t* secs, double* frac_secs) +{ + return rf_skiq_recv_with_time_multi(h, &data, nsamples, blocking, secs, frac_secs); +} + +int rf_skiq_recv_with_time_multi(void* h_, + void** data_, + uint32_t nsamples, + bool blocking, + time_t* secs, + double* frac_secs) +{ + rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_; + uint64_t ts_start = 0; + cf_t** data = (cf_t**)data_; + + pthread_mutex_lock(&h->mutex_rx); + + // Perform channel synchronization + rf_skiq_synch_rx_ports(h); + + bool completed = false; + uint32_t count[SKIQ_MAX_CHANNELS] = {}; + + while (!completed) { + // Completion true by default + completed = true; + + // Iterate over cards + for (uint32_t card = 0, chan = 0; card < h->nof_cards; card++) { + // Iterate over ports + for (uint32_t port = 0; port < h->nof_ports; port++, chan++) { + // Calculate number of pending samples + uint32_t pending = nsamples - count[chan]; + + if (pending > 0) { + uint64_t ts = 0; + int n = 0; + + // If data is not provided... + if (data[chan] == NULL) { + // ... discard up to RF_SKIQ_DUMMY_BUFFER_SIZE samples + n = rf_skiq_card_receive( + &h->cards[card], port, h->dummy_buffer, SRSRAN_MIN(pending, RF_SKIQ_DUMMY_BUFFER_SIZE), &ts); + } else { + // ... read base-band + n = rf_skiq_card_receive(&h->cards[card], port, &data[chan][count[chan]], pending, &ts); + } + + // If error is detected, return it + if (n < SRSRAN_SUCCESS) { + pthread_mutex_unlock(&h->mutex_rx); + return n; + } + + // Save first valid timestamp + if (ts_start == 0) { + ts_start = ts; + } + + // Increment count for the channel + count[chan] += n; + + // Lower completed flag if at least a channel has not reach the target + if (count[chan] < nsamples) { + completed = false; + } + } + } + } + } + + pthread_mutex_unlock(&h->mutex_rx); + + // Convert u64 to srsran timestamp + tstamp_to_time(h, ts_start, secs, frac_secs); + + // No error, return number of received samples + return nsamples; +} + +int rf_skiq_send_timed(void* h, + void* data, + int nsamples, + time_t secs, + double frac_secs, + bool has_time_spec, + bool blocking, + bool is_start_of_burst, + bool is_end_of_burst) +{ + void* _data[SRSRAN_MAX_PORTS] = {}; + _data[0] = data; + + return rf_skiq_send_timed_multi( + h, _data, nsamples, secs, frac_secs, has_time_spec, blocking, is_start_of_burst, is_end_of_burst); +} + +int rf_skiq_send_timed_multi(void* h_, + void** data_, + int nsamples, + time_t secs, + double frac_secs, + bool has_time_spec, + bool blocking, + bool is_start_of_burst, + bool is_end_of_burst) +{ + rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_; + cf_t** data = (cf_t**)data_; + + // Force timestamp only if start of burst + if (is_start_of_burst) { + if (has_time_spec) { + h->next_tstamp = time_to_tstamp(h, secs, frac_secs); + SKIQ_RF_DEBUG("[Tx SOB] ts=%ld\n", h->next_tstamp); + } else { + h->next_tstamp = rf_skiq_card_read_rf_timestamp(&h->cards[0]); + h->next_tstamp += (uint64_t)round(h->current_srate_hz / 10); // increment a 10th of a second + } + } + + uint32_t rpm = 0; + bool completed = false; + uint32_t count[SKIQ_MAX_CHANNELS] = {}; + + while (!completed) { + // Completion true by default + completed = true; + + // Iterate all cards... + for (uint32_t card = 0, chan = 0; card < h->nof_cards; card++) { + // Iterate all ports... + for (uint32_t port = 0; port < h->nof_ports; port++, chan++) { + // Calculate number of pending samples + uint32_t pending = nsamples - count[chan]; + + if (pending > 0) { + uint64_t ts = h->next_tstamp + count[chan]; + cf_t* ptr = (data[chan] == NULL) ? NULL : &data[chan][count[chan]]; + int n = rf_skiq_card_send(&h->cards[card], port, ptr, pending, ts); + if (n > SRSRAN_SUCCESS) { + count[chan] += n; + } + + if (count[chan] < nsamples) { + completed = false; + } + } + } + } + } + + // Increment timestamp + h->next_tstamp += nsamples; + + if (is_end_of_burst) { + rf_skiq_send_end_of_burst(h); + } + + return (int)rpm; +} diff --git a/lib/src/phy/rf/rf_skiq_imp.h b/lib/src/phy/rf/rf_skiq_imp.h new file mode 100644 index 000000000..54a33dcbe --- /dev/null +++ b/lib/src/phy/rf/rf_skiq_imp.h @@ -0,0 +1,95 @@ +/** + * + * \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 +#include + +#include "srsran/config.h" +#include "srsran/phy/rf/rf.h" + +SRSRAN_API int rf_skiq_open(char* args, void** handler); + +SRSRAN_API int rf_skiq_open_multi(char* args, void** handler, uint32_t nof_rx_antennas); + +SRSRAN_API const char* rf_skiq_devname(void* h); + +SRSRAN_API int rf_skiq_close(void* h); + +SRSRAN_API int rf_skiq_start_rx_stream(void* h, bool now); + +SRSRAN_API int rf_skiq_start_rx_stream_nsamples(void* h, uint32_t nsamples); + +SRSRAN_API int rf_skiq_stop_rx_stream(void* h); + +SRSRAN_API void rf_skiq_flush_buffer(void* h); + +SRSRAN_API bool rf_skiq_has_rssi(void* h); + +SRSRAN_API float rf_skiq_get_rssi(void* h); + +SRSRAN_API void rf_skiq_set_master_clock_rate(void* h, double rate); + +SRSRAN_API bool rf_skiq_is_master_clock_dynamic(void* h); + +SRSRAN_API double rf_skiq_set_rx_srate(void* h, double freq); + +SRSRAN_API int rf_skiq_set_rx_gain(void* h, double gain); + +SRSRAN_API int rf_skiq_set_rx_gain_ch(void* h_, uint32_t ch, double rx_gain); + +SRSRAN_API double rf_skiq_get_rx_gain(void* h); + +SRSRAN_API double rf_skiq_get_tx_gain(void* h); + +SRSRAN_API srsran_rf_info_t* rf_skiq_get_info(void* h); + +SRSRAN_API void rf_skiq_suppress_stdout(void* h); + +SRSRAN_API void rf_skiq_register_error_handler(void* h, srsran_rf_error_handler_t error_handler, void* arg); + +SRSRAN_API double rf_skiq_set_rx_freq(void* h, uint32_t ch, double freq); + +SRSRAN_API int +rf_skiq_recv_with_time(void* h, void* data, uint32_t nsamples, bool blocking, time_t* secs, double* frac_secs); + +SRSRAN_API int +rf_skiq_recv_with_time_multi(void* h, void** data, uint32_t nsamples, bool blocking, time_t* secs, double* frac_secs); + +SRSRAN_API double rf_skiq_set_tx_srate(void* h, double freq); + +SRSRAN_API int rf_skiq_set_tx_gain(void* h, double gain); + +SRSRAN_API int rf_skiq_set_tx_gain_ch(void* h, uint32_t ch, double gain); + +SRSRAN_API double rf_skiq_set_tx_freq(void* h, uint32_t ch, double freq); + +SRSRAN_API void rf_skiq_get_time(void* h, time_t* secs, double* frac_secs); + +SRSRAN_API int rf_skiq_send_timed(void* h, + void* data, + int nsamples, + time_t secs, + double frac_secs, + bool has_time_spec, + bool blocking, + bool is_start_of_burst, + bool is_end_of_burst); + +SRSRAN_API int rf_skiq_send_timed_multi(void* h, + void* data[4], + int nsamples, + time_t secs, + double frac_secs, + bool has_time_spec, + bool blocking, + bool is_start_of_burst, + bool is_end_of_burst); diff --git a/lib/src/phy/rf/rf_skiq_imp_card.c b/lib/src/phy/rf/rf_skiq_imp_card.c new file mode 100644 index 000000000..f2a7c2aac --- /dev/null +++ b/lib/src/phy/rf/rf_skiq_imp_card.c @@ -0,0 +1,580 @@ +/** + * + * \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 "rf_skiq_imp_card.h" + +static void* reader_thread(void* arg) +{ + rf_skiq_card_t* q = (rf_skiq_card_t*)arg; + + while (q->state != RF_SKIQ_PORT_STATE_STOP) { + // Wait to leave idle state + if (q->state == RF_SKIQ_PORT_STATE_IDLE) { + SKIQ_RF_INFO("[Rx %d] IDLE\n", q->card); + pthread_mutex_lock(&q->mutex); + while (q->state == RF_SKIQ_PORT_STATE_IDLE) { + pthread_cond_wait(&q->cvar, &q->mutex); + } + pthread_mutex_unlock(&q->mutex); + SKIQ_RF_INFO("[Rx %d] %s\n", q->card, q->state == RF_SKIQ_PORT_STATE_STREAMING ? "STREAMING" : "STOP"); + } + + // Check exit condition + if (q->state == RF_SKIQ_PORT_STATE_STOP) { + break; + } + + skiq_rx_hdl_t curr_rx_hdl = 0; + skiq_rx_block_t* p_rx_block = NULL; + uint32_t len = 0; + skiq_rx_status_t rx_status = skiq_receive(q->card, &curr_rx_hdl, &p_rx_block, &len); + + switch (rx_status) { + case skiq_rx_status_success: + // Check Rx index boundary + if (p_rx_block->rfic_control >= q->param.rx_param->gain_index_min && + p_rx_block->rfic_control <= q->param.rx_param->gain_index_max) { + double new_rx_gain = q->rx_gain_table_db[p_rx_block->rfic_control]; + + // If the Rx index has changed, update gain + if (new_rx_gain != q->cur_rx_gain_db) { + SKIQ_RF_DEBUG("card %d index=%d; gain=%.2f/%.2f dB;\n", + q->card, + p_rx_block->rfic_control, + q->rx_gain_table_db[p_rx_block->rfic_control], + q->cur_rx_gain_db); + q->cur_rx_gain_db = new_rx_gain; + } + } + if (curr_rx_hdl < q->nof_ports && p_rx_block != NULL && len > SKIQ_RX_HEADER_SIZE_IN_BYTES) { + // Convert number of bytes into samples + uint32_t nsamples = len / 4 - SKIQ_RX_HEADER_SIZE_IN_WORDS; + + // Give block to the port + rf_skiq_rx_port_write(&q->rx_ports[curr_rx_hdl], p_rx_block, nsamples); + } else { + ERROR("Card %d received data with corrupted pointers\n", q->card); + } + break; + case skiq_rx_status_no_data: + // Do nothing + break; + case skiq_rx_status_error_generic: + ERROR("Error: Generic error occurred during skiq_receive.\n"); + break; + case skiq_rx_status_error_overrun: + ERROR("Error: overrun error occurred during skiq_receive.\n"); + break; + case skiq_rx_status_error_packet_malformed: + ERROR("Error: packet malformed error occurred during skiq_receive.\n"); + break; + case skiq_rx_status_error_card_not_active: + ERROR("Error: inactive card error occurred during skiq_receive.\n"); + break; + case skiq_rx_status_error_not_streaming: + ERROR("Error: not streaming card error occurred during skiq_receive.\n"); + break; + default: + ERROR("Error: the impossible happened during skiq_receive.\n"); + break; + } + } + + SKIQ_RF_INFO("Exiting reader thread!\n"); + + return NULL; +} + +int rf_skiq_card_update_gain_table(rf_skiq_card_t* q) +{ + for (uint8_t i = q->param.rx_param->gain_index_min; i <= q->param.rx_param->gain_index_max; i++) { + if (skiq_read_rx_cal_offset_by_gain_index(q->card, skiq_rx_hdl_A1, i, &q->rx_gain_table_db[i])) { + ERROR("Reading calibrated Rx gain index %d", i); + return SRSRAN_ERROR; + } + } + return SRSRAN_SUCCESS; +} + +int rf_skiq_card_init(rf_skiq_card_t* q, uint8_t card, uint8_t nof_ports, const rf_skiq_port_opts_t* opts) +{ + q->card = card; + q->nof_ports = nof_ports; + + // Reprogram FPGA to reset all states + if (skiq_prog_fpga_from_flash(card)) { + ERROR("Error programming card %d from flash\n", q->card); + return SRSRAN_ERROR; + } + + // Read card parameters + if (skiq_read_parameters(card, &q->param)) { + ERROR("Reading card %d param", card); + return SRSRAN_ERROR; + } + + // Check number of rx channels + if (q->param.rf_param.num_rx_channels < nof_ports) { + ERROR("Card %d does not support %d Rx channels", card, nof_ports); + return SRSRAN_ERROR; + } + + // Check number of tx channels + if (q->param.rf_param.num_tx_channels < nof_ports) { + ERROR("Card %d does not support %d Tx channels", card, nof_ports); + return SRSRAN_ERROR; + } + + // set a modest rx timeout + if (skiq_set_rx_transfer_timeout(card, 1000)) { + ERROR("Setting Rx transfer timeout"); + return SRSRAN_ERROR; + } + + // do not pack 12bit samples + if (skiq_write_iq_pack_mode(card, false)) { + ERROR("Setting Rx IQ pack mode"); + return SRSRAN_ERROR; + } + + // set the control output bits to include the gain + if (skiq_write_rfic_control_output_config( + card, RFIC_CONTROL_OUTPUT_MODE_GAIN_CONTROL_RXA1, RFIC_CONTROL_OUTPUT_MODE_GAIN_BITS) != 0) { + ERROR("Unable to configure card %d the RF IC control output (A1)", card); + return SRSRAN_ERROR; + } + + // set RX channel mode + if (skiq_write_chan_mode(card, q->mode)) { + ERROR("Setting card %d channel mode", card); + return SRSRAN_ERROR; + } + + // Select Rx streaming mode to low latency if the sampling rate is lower than 5MHz + if (skiq_write_rx_stream_mode(q->card, opts->stream_mode)) { + ERROR("Error setting Rx stream mode\n"); + return SRSRAN_ERROR; + } + + // initialise tx/rx ports + for (uint8_t i = 0; i < nof_ports; i++) { + if (rf_skiq_tx_port_init(&q->tx_ports[i], card, (skiq_tx_hdl_t)i, opts)) { + ERROR("Initiating card %d, Tx port %d", card, i); + return SRSRAN_ERROR; + } + + if (rf_skiq_rx_port_init(&q->rx_ports[i], card, (skiq_rx_hdl_t)i, opts)) { + ERROR("Initiating card %d, Rx port %d", card, i); + return SRSRAN_ERROR; + } + } + + if (pthread_mutex_init(&q->mutex, NULL)) { + ERROR("Initiating mutex"); + return SRSRAN_ERROR; + } + + if (pthread_cond_init(&q->cvar, NULL)) { + ERROR("Initiating cvar"); + return SRSRAN_ERROR; + } + + // Initialise thread parameters + pthread_attr_t attr; + struct sched_param param; + + param.sched_priority = sched_get_priority_max(SCHED_FIFO); + pthread_attr_init(&attr); + if (pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED)) { + ERROR("Error not enough privileges to set Scheduling priority\n"); + } + + if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO)) { + ERROR("Error not enough privileges to set Scheduling priority\n"); + } + + if (pthread_attr_setschedparam(&attr, ¶m)) { + ERROR("Error not enough privileges to set Scheduling priority\n"); + } + + // Launch thread + if (pthread_create(&q->thread, &attr, reader_thread, q)) { + ERROR("Error creating reader thread with attributes (Did you miss sudo?). Trying without attributes.\n"); + return SRSRAN_ERROR; + } + + // Rename thread + char thread_name[32] = {}; + if (snprintf(thread_name, sizeof(thread_name), "SKIQ Rx %d", q->card) > 0) { + pthread_setname_np(q->thread, thread_name); + } + + return SRSRAN_SUCCESS; +} + +void rf_skiq_card_set_error_handler(rf_skiq_card_t* q, srsran_rf_error_handler_t error_handler, void* arg) +{ + for (uint32_t i = 0; i < q->nof_ports; i++) { + rf_skiq_tx_port_set_error_handler(&q->tx_ports[i], error_handler, arg); + rf_skiq_rx_port_set_error_handler(&q->rx_ports[i], error_handler, arg); + } +} + +double rf_skiq_card_set_tx_gain_db(rf_skiq_card_t* q, uint32_t port_idx, double gain_db) +{ + double max_atten_dB = 0.25 * q->param.tx_param->atten_quarter_db_max; + double min_atten_dB = 0.25 * q->param.tx_param->atten_quarter_db_min; + + // Calculate attenuation: + // - 0dB attenuation -> Maximum gain; + // - 0dB gain -> Maximum attenuation; + double att_dB = max_atten_dB - gain_db; + + // Check gain range + if (att_dB < min_atten_dB || att_dB > max_atten_dB) { + ERROR("Error port %d:%d the selected gain (%.2f dB) is out of range (%.2f to %.2f dB).\n", + q->card, + port_idx, + gain_db, + min_atten_dB, + max_atten_dB); + } + + // Calculate attenuation index + uint16_t att_index = (uint16_t)floor(att_dB * 4); + + // Bound index + att_index = SRSRAN_MIN(SRSRAN_MAX(att_index, q->param.tx_param->atten_quarter_db_min), + q->param.tx_param->atten_quarter_db_max); + + // Calculate equivalent gain + double actual_gain_dB = max_atten_dB - att_index * 0.25; + + // Set gain per port + if (port_idx >= q->nof_ports) { + for (uint8_t i = 0; i < q->nof_ports; i++) { + if (skiq_write_tx_attenuation(q->card, (skiq_tx_hdl_t)i, att_index)) { + ERROR("Error setting card %d:%d Tx attenuation\n", q->card, i); + } + } + } else { + if (skiq_write_tx_attenuation(q->card, (skiq_tx_hdl_t)port_idx, att_index)) { + ERROR("Error setting card %d:%d Tx attenuation\n", q->card, port_idx); + } + } + + return actual_gain_dB; +} + +double rf_skiq_card_set_rx_gain_db(rf_skiq_card_t* q, uint32_t port_idx, double gain_db) +{ + // Find the nearest gain index in the table + int gain_idx = -1; + double gain_min_diff = INFINITY; + for (int i = q->param.rx_param->gain_index_min; i <= q->param.rx_param->gain_index_max; i++) { + double gain_diff = fabs(q->rx_gain_table_db[i] - gain_db); + if (gain_diff < gain_min_diff) { + gain_min_diff = gain_diff; + gain_idx = i; + } + } + + if (gain_idx >= 0) { + gain_db = q->rx_gain_table_db[gain_idx]; + if (port_idx < q->nof_ports) { + // Set single port gain + q->issued_rx_gain_db[port_idx] = gain_db; + skiq_write_rx_gain(q->card, (skiq_rx_hdl_t)port_idx, gain_idx); + } else { + // Set all gains + for (int i = 0; i < q->nof_ports; i++) { + q->issued_rx_gain_db[i] = gain_db; + skiq_write_rx_gain(q->card, (skiq_rx_hdl_t)i, gain_idx); + } + } + } + + return gain_db; +} + +int rf_skiq_card_update_timestamp(rf_skiq_card_t* q, bool use_1pps, uint64_t new_ts) +{ + if (use_1pps) { + // Read 1pps source + skiq_1pps_source_t pps_source = skiq_1pps_source_unavailable; + if (skiq_read_1pps_source(q->card, &pps_source)) { + ERROR("Error reading card %d 1PPS source\n", q->card); + return SRSRAN_ERROR; + } + + // Make sure the source is external + if (pps_source != skiq_1pps_source_external) { + ERROR("Error card %d is not configured with external 1PPS source\n", q->card); + return SRSRAN_ERROR; + } + + // Get last time a PPS was received + uint64_t ts_sys_1pps = 0; + if (skiq_read_last_1pps_timestamp(q->card, NULL, &ts_sys_1pps)) { + ERROR("Reading card %d last 1PPS timestamp", q->card); + return SRSRAN_ERROR; + } + + // Read current system time + uint64_t ts = 0; + if (skiq_read_curr_sys_timestamp(q->card, &ts)) { + ERROR("Reading card %d system timestamp", q->card); + return SRSRAN_ERROR; + } + + // Make sure a 1PPS was received less than 2 seconds ago + if (ts - ts_sys_1pps > 2 * SKIQ_SYS_TIMESTAMP_FREQ) { + ERROR("Error card %d last PPS was received %.1f seconds ago (%ld - %ld)\n", + q->card, + (double)(ts - ts_sys_1pps) / (double)SKIQ_SYS_TIMESTAMP_FREQ, + ts, + ts_sys_1pps); + return SRSRAN_ERROR; + } + + // Set a given time in the future, a 100th of a second (10ms) + ts += SKIQ_SYS_TIMESTAMP_FREQ / 100; + + // Order that all timestamps are reseted when next 1PPS signal is received + SKIQ_RF_INFO(" ... Resetting card %d system timestamp on next PPS\n", q->card); + if (skiq_write_timestamp_update_on_1pps(q->card, ts, new_ts)) { + ERROR("Error reseting card %d timestamp on 1 PPS", q->card); + return SRSRAN_ERROR; + } + } else { + // Simply, reset timestamp + SKIQ_RF_INFO(" ... Resetting card %d system timestamp now\n", q->card); + if (skiq_update_timestamps(q->card, new_ts)) { + ERROR("Error resetting card %d timestamp", q->card); + return SRSRAN_ERROR; + } + } + return SRSRAN_SUCCESS; +} + +uint64_t rf_skiq_card_read_sys_timestamp(rf_skiq_card_t* q) +{ + uint64_t ts = 0UL; + + if (skiq_read_curr_sys_timestamp(q->card, &ts)) { + ERROR("Reading card %d system timestamp", q->card); + } + + return ts; +} + +uint64_t rf_skiq_card_read_rf_timestamp(rf_skiq_card_t* q) +{ + uint64_t ts = 0UL; + + if (skiq_read_curr_rx_timestamp(q->card, skiq_rx_hdl_A1, &ts)) { + ERROR("Reading card %d system timestamp", q->card); + } + + return ts; +} + +int rf_skiq_card_start_rx_streaming(rf_skiq_card_t* q, uint64_t timestamp) +{ + // Wrong state + if (q->state == RF_SKIQ_PORT_STATE_STOP) { + ERROR("Error starting Rx stream: wrong state (%d)\n", q->state); + return SRSRAN_ERROR; + } + + // Already enabled + if (q->state == RF_SKIQ_PORT_STATE_STREAMING) { + SKIQ_RF_INFO("Rx streams in card %d have already started\n", q->card); + return SRSRAN_SUCCESS; + } + + // Make a list with Rx handlers + skiq_rx_hdl_t rx_hdl[RF_SKIQ_MAX_PORTS_CARD]; + for (uint8_t i = 0; i < RF_SKIQ_MAX_PORTS_CARD; i++) { + rx_hdl[i] = (skiq_rx_hdl_t)i; + } + + pthread_mutex_lock(&q->mutex); + + // Start all Rx in a row + if (skiq_start_rx_streaming_multi_on_trigger(q->card, rx_hdl, q->nof_ports, skiq_trigger_src_synced, timestamp)) { + ERROR("Failed to start card %d Rx streaming\n", q->card); + pthread_mutex_unlock(&q->mutex); + return SRSRAN_ERROR; + } + + // Update state and broadcast condition variable + q->state = RF_SKIQ_PORT_STATE_STREAMING; + pthread_cond_broadcast(&q->cvar); + pthread_mutex_unlock(&q->mutex); + + SKIQ_RF_INFO("Rx streams in card %d have started\n", q->card); + + return SRSRAN_SUCCESS; +} + +int rf_skiq_card_stop_rx_streaming(rf_skiq_card_t* q) +{ + if (q->state == RF_SKIQ_PORT_STATE_STOP) { + ERROR("Error stopping Rx stream: wrong state (%d)\n", q->state); + return SRSRAN_ERROR; + } + + // Avoid stop streaming if it was not started + if (q->state == RF_SKIQ_PORT_STATE_IDLE) { + return SRSRAN_ERROR; + } + + // Make a list with Tx/Rx handlers + skiq_rx_hdl_t rx_hdl[RF_SKIQ_MAX_PORTS_CARD]; + for (uint8_t i = 0; i < RF_SKIQ_MAX_PORTS_CARD; i++) { + rx_hdl[i] = (skiq_rx_hdl_t)i; + } + + pthread_mutex_lock(&q->mutex); + + // Update state and broadcast condition variable first + q->state = RF_SKIQ_PORT_STATE_IDLE; + pthread_cond_broadcast(&q->cvar); + + // Stop all Rx in a row + if (skiq_stop_rx_streaming_multi_immediate(q->card, rx_hdl, q->nof_ports)) { + ERROR("Failed to stop card %d Rx streaming\n", q->card); + pthread_mutex_unlock(&q->mutex); + return SRSRAN_ERROR; + } + + pthread_mutex_unlock(&q->mutex); + + SKIQ_RF_INFO("Rx streams in card %d have stopped\n", q->card); + + return SRSRAN_SUCCESS; +} + +void rf_skiq_card_end_of_burst(rf_skiq_card_t* q) +{ + for (uint32_t i = 0; i < q->nof_ports; i++) { + rf_skiq_tx_port_end_of_burst(&q->tx_ports[i]); + } +} + +int rf_skiq_card_set_srate_hz(rf_skiq_card_t* q, uint32_t srate_hz) +{ + for (uint8_t i = 0; i < q->nof_ports; i++) { + // Set transmitter sampling rate + if (skiq_write_tx_sample_rate_and_bandwidth(q->card, (skiq_tx_hdl_t)i, srate_hz, srate_hz)) { + ERROR("Setting Tx sampling rate\n"); + } + + // Set receiver sampling rate + if (skiq_write_rx_sample_rate_and_bandwidth(q->card, (skiq_rx_hdl_t)i, srate_hz, srate_hz)) { + ERROR("Setting Rx sampling rate\n"); + } + + rf_skiq_rx_port_reset(&q->rx_ports[i]); + } + + return SRSRAN_SUCCESS; +} + +double rf_skiq_card_set_tx_freq_hz(rf_skiq_card_t* q, uint32_t port_idx, double freq_hz) +{ + q->suspend = true; + rf_skiq_tx_port_set_lo(&q->tx_ports[port_idx], (uint64_t)freq_hz); + q->suspend = false; + + return freq_hz; +} + +double rf_skiq_card_set_rx_freq_hz(rf_skiq_card_t* q, uint32_t port_idx, double freq_hz) +{ + q->suspend = true; + rf_skiq_rx_port_set_lo(&q->rx_ports[port_idx], (uint64_t)freq_hz); + q->suspend = false; + + // Update gains for only port 0 + if (port_idx == 0) { + // Update gain table + rf_skiq_card_update_gain_table(q); + + // Set previous issued gain in dB for the new tables + rf_skiq_card_set_rx_gain_db(q, q->nof_ports, q->issued_rx_gain_db[port_idx]); + } + + return freq_hz; +} + +void rf_skiq_card_close(rf_skiq_card_t* q) +{ + SKIQ_RF_INFO("Closing card %d...\n", q->card); + + // Post stop state to reader thread + q->state = RF_SKIQ_PORT_STATE_STOP; + pthread_cond_broadcast(&q->cvar); + + // Wait for reader thread to finish + pthread_join(q->thread, NULL); + + for (uint8_t i = 0; i < q->nof_ports; i++) { + rf_skiq_rx_port_free(&q->rx_ports[i]); + rf_skiq_tx_port_free(&q->tx_ports[i]); + } + + pthread_cond_destroy(&q->cvar); + pthread_mutex_destroy(&q->mutex); + + // Unlocks all cards + if (skiq_disable_cards(&q->card, 1)) { + ERROR("Unable to disable card %d\n", q->card); + } +} + +int rf_skiq_card_receive(rf_skiq_card_t* q, uint32_t port_idx, cf_t* dst, uint32_t nsamples, uint64_t* ts) +{ + // If suspended and samples are not available, then set all to zero + if (q->suspend && rf_skiq_rx_port_available(&q->rx_ports[port_idx]) == 0) { + srsran_vec_cf_zero(dst, nsamples); + *ts = 0UL; + return nsamples; + } + + return rf_skiq_rx_port_read(&q->rx_ports[port_idx], dst, nsamples, ts); +} + +uint64_t rf_skiq_card_get_rx_timestamp(rf_skiq_card_t* q, uint32_t port_idx) +{ + if (q->suspend || q->rx_ports[port_idx].rb_overflow) { + return 0UL; + } + + return rf_skiq_rx_port_get_timestamp(&q->rx_ports[port_idx]); +} + +bool rf_skiq_card_is_streaming(rf_skiq_card_t* q) +{ + return q->state == RF_SKIQ_PORT_STATE_STREAMING && !q->suspend; +} + +int rf_skiq_card_send(rf_skiq_card_t* q, uint32_t port_idx, const cf_t* data, uint32_t nsamples, uint64_t timestamp) +{ + // If suspended, do not bother the transmitter + if (q->suspend) { + return nsamples; + } + + return rf_skiq_tx_port_send(&q->tx_ports[port_idx], data, nsamples, timestamp); +} diff --git a/lib/src/phy/rf/rf_skiq_imp_card.h b/lib/src/phy/rf/rf_skiq_imp_card.h new file mode 100644 index 000000000..b63b82290 --- /dev/null +++ b/lib/src/phy/rf/rf_skiq_imp_card.h @@ -0,0 +1,77 @@ +/** + * + * \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_LIB_SRC_PHY_RF_RF_SKIQ_IMP_CARD_H_ +#define SRSRAN_LIB_SRC_PHY_RF_RF_SKIQ_IMP_CARD_H_ + +#include "rf_skiq_imp_port.h" + +typedef struct { + uint8_t card; + uint8_t nof_ports; + skiq_chan_mode_t mode; + + skiq_param_t param; + rf_skiq_tx_port_t tx_ports[RF_SKIQ_MAX_PORTS_CARD]; + rf_skiq_rx_port_t rx_ports[RF_SKIQ_MAX_PORTS_CARD]; + + double rx_gain_table_db[UINT8_MAX + 1]; + double cur_rx_gain_db; + double issued_rx_gain_db[SRSRAN_MAX_PORTS]; + bool suspend; + + uint64_t start_rx_stream_ts; + rf_skiq_port_state_t state; + pthread_mutex_t mutex; ///< Protect concurrent access to start/stop rx stream and receive + pthread_cond_t cvar; + pthread_t thread; + +} rf_skiq_card_t; + +int rf_skiq_card_init(rf_skiq_card_t* q, uint8_t card, uint8_t nof_ports, const rf_skiq_port_opts_t* opts); + +void rf_skiq_card_set_error_handler(rf_skiq_card_t* q, srsran_rf_error_handler_t error_handler, void* arg); + +int rf_skiq_card_update_gain_table(rf_skiq_card_t* q); + +double rf_skiq_card_set_tx_gain_db(rf_skiq_card_t* q, uint32_t port_idx, double gain_db); + +double rf_skiq_card_set_rx_gain_db(rf_skiq_card_t* q, uint32_t port_idx, double gain_db); + +int rf_skiq_card_update_timestamp(rf_skiq_card_t* q, bool use_1pps, uint64_t new_ts); + +uint64_t rf_skiq_card_read_sys_timestamp(rf_skiq_card_t* q); + +uint64_t rf_skiq_card_read_rf_timestamp(rf_skiq_card_t* q); + +int rf_skiq_card_start_rx_streaming(rf_skiq_card_t* q, uint64_t timestamp); + +int rf_skiq_card_stop_rx_streaming(rf_skiq_card_t* q); + +void rf_skiq_card_end_of_burst(rf_skiq_card_t* q); + +int rf_skiq_card_set_srate_hz(rf_skiq_card_t* q, uint32_t srate_hz); + +double rf_skiq_card_set_tx_freq_hz(rf_skiq_card_t* q, uint32_t port_idx, double freq_hz); + +double rf_skiq_card_set_rx_freq_hz(rf_skiq_card_t* q, uint32_t port_idx, double freq_hz); + +void rf_skiq_card_close(rf_skiq_card_t* q); + +int rf_skiq_card_receive(rf_skiq_card_t* q, uint32_t port_idx, cf_t* dst, uint32_t nsamples, uint64_t* ts); + +uint64_t rf_skiq_card_get_rx_timestamp(rf_skiq_card_t* q, uint32_t port_idx); + +bool rf_skiq_card_is_streaming(rf_skiq_card_t* q); + +int rf_skiq_card_send(rf_skiq_card_t* q, uint32_t port_idx, const cf_t* data, uint32_t nsamples, uint64_t timestamp); + +#endif // SRSRAN_LIB_SRC_PHY_RF_RF_SKIQ_IMP_CARD_H_ diff --git a/lib/src/phy/rf/rf_skiq_imp_cfg.h b/lib/src/phy/rf/rf_skiq_imp_cfg.h new file mode 100644 index 000000000..aa98c5bd2 --- /dev/null +++ b/lib/src/phy/rf/rf_skiq_imp_cfg.h @@ -0,0 +1,110 @@ +/** + * + * \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_RF_SKIQ_IMP_CFG_H +#define SRSRAN_RF_SKIQ_IMP_CFG_H + +/** + * RF_SKIQ_MAX_PORTS_CARD sets the maximum number of ports per card + */ +#define RF_SKIQ_MAX_PORTS_CARD 2 + +/** + * SKIQ_CARD_SYNCH_MAX_TRIALS defines the maximum number of trials to synchronize multiple boards + */ +#define SKIQ_CARD_SYNCH_MAX_TRIALS 10 + +/** + * SKIQ_CARD_SYNC_MAX_ERROR sets the maximum number of system "ticks" error between boards during synchronization check. + * Consider the communication medium delay between the host and SidekIQ cards. + */ +#define SKIQ_CARD_SYNC_MAX_ERROR (SKIQ_SYS_TIMESTAMP_FREQ / 2) + +/** + * Maximum gap allowed in number of samples between ports + */ +#define SKIQ_PORT_SYNC_MAX_GAP (1024 * 1024) + +/** + * For checking the number of Tx lattes in the FPGA set the next line to the desired check period in number of blocks. + * Example: set to 1000 for checking it every 1000 blocks. + * WARNING: A low period may cause a reduction of performance in the Host-FPGA communication + */ +#define SKIQ_TX_LATES_CHECK_PERIOD (1920 * 10) + +/** + * Minimum number of channels that this RF device can reach + */ +#define SKIQ_MIN_CHANNELS (1) + +/** + * Maximum number of channels that this RF device can reach + */ +#define SKIQ_MAX_CHANNELS (SKIQ_MAX_NUM_CARDS * RF_SKIQ_MAX_PORTS_CARD) + +/** + * Dummy receive buffer size in samples + */ +#define RF_SKIQ_DUMMY_BUFFER_SIZE (1024) + +/** + * Magic word value as a ring buffer check + */ +#define SKIQ_RX_BUFFFER_MAGIC_WORD 0xABCD1234 + +/** + * Normalization value between fixed and floating point conversion + */ +#define SKIQ_NORM 2048.0 + +/** + * Default Rx gain in decibels (dB) + */ +#define SKIQ_RX_GAIN_DEFAULT_dB (+50.0f) + +/** + * Default sampling rate in samples per second (Hz) + */ +#define SKIQ_DEFAULT_SAMPLING_RATE_HZ (30.72e6) + +/** + * + */ +#define SKIQ_TX_PACKET_SIZE(N, MODE) (SKIQ_TX_PACKET_SIZE_INCREMENT_IN_WORDS * (N)-SKIQ_TX_HEADER_SIZE_IN_WORDS) + +/** + * SKIQ driver standard output MACRO + */ +extern uint32_t rf_skiq_logging_level; + +#define SKIQ_RF_INFO(...) \ + do { \ + if (rf_skiq_logging_level >= SKIQ_LOG_INFO) { \ + fprintf(stdout, "[SKIQ RF INFO] " __VA_ARGS__); \ + } \ + } while (false) + +#define SKIQ_RF_DEBUG(...) \ + do { \ + if (rf_skiq_logging_level >= SKIQ_LOG_DEBUG) { \ + fprintf(stdout, "[SKIQ RF INFO] " __VA_ARGS__); \ + } \ + } while (false) + +#define SKIQ_RF_ERROR(...) \ + do { \ + if (rf_skiq_logging_level >= SKIQ_LOG_ERROR) { \ + fprintf(stdout, "[SKIQ RF ERROR] " __VA_ARGS__); \ + } \ + } while (false) + +#endif // SRSLTE_RF_SKIQ_IMP_CFG_H diff --git a/lib/src/phy/rf/rf_skiq_imp_port.c b/lib/src/phy/rf/rf_skiq_imp_port.c new file mode 100644 index 000000000..e0376bf23 --- /dev/null +++ b/lib/src/phy/rf/rf_skiq_imp_port.c @@ -0,0 +1,576 @@ +/** + * + * \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 "rf_skiq_imp_port.h" + +static void rf_skiq_rx_port_handle_overflow(rf_skiq_rx_port_t* q) +{ + srsran_rf_error_t error = {}; + + error.type = SRSRAN_RF_ERROR_OVERFLOW; + if (q->error_handler) { + q->error_handler(q->error_handler_arg, error); + } else { + SKIQ_RF_INFO("Rx overflow detected in %d:%d\n", q->card, (int)q->hdl); + } +} + +#if SKIQ_TX_LATES_CHECK_PERIOD +static bool rf_skiq_tx_port_handle_late(rf_skiq_tx_port_t* q) +{ + // Get number of lattes from FPGA + uint32_t total_late = 0; + if (skiq_read_tx_num_late_timestamps(q->card, q->hdl, &total_late)) { + ERROR("Error reading lates from port %d:%d\n", q->card, (int)q->hdl); + } + + // Calculate number of late timestamps + uint32_t new_late = total_late; + + // Remove previous read value + if (new_late >= q->last_total_late) { + new_late = new_late - q->last_total_late; + } + + // Update latest value + q->last_total_late = total_late; + + // No late, do not report them + if (new_late == 0) { + return false; + } + + if (q->error_handler) { + srsran_rf_error_t error = {}; + error.type = SRSRAN_RF_ERROR_LATE; + error.opt = new_late; + q->error_handler(q->error_handler_arg, error); + } else { + SKIQ_RF_INFO("Port %d late events detected in %d:%d\n", new_late, q->card, (int)q->hdl); + } + + return true; +} +#endif // SKIQ_TX_LATES_CHECK_PERIOD + +static void* writer_thread(void* arg) +{ + uint64_t last_tx_ts = 0; + rf_skiq_tx_port_t* q = (rf_skiq_tx_port_t*)arg; + skiq_tx_block_t* p_tx_block = NULL; + + if (skiq_start_tx_streaming(q->card, q->hdl)) { + ERROR("Error starting Tx stream %d:%d\n", q->card, (int)q->hdl); + return NULL; + } + + q->state = RF_SKIQ_PORT_STATE_STREAMING; + + while (q->state != RF_SKIQ_PORT_STATE_STOP) { + // Read block from ring-buffer + int n = srsran_ringbuffer_read_block(&q->rb, (void**)&p_tx_block, q->p_block_nbytes, 1000); + + // Stop state is detected + if (q->state == RF_SKIQ_PORT_STATE_STOP) { + break; + } + + // If the ring buffer read resulted in timeout + if (n == SRSRAN_ERROR_TIMEOUT) { + continue; + } + + // Ignore blocks with TS=0 + if (p_tx_block->timestamp == 0) { + continue; + } + + // Check if the timestamp is the past (this can be caused by sample rate change) + if (last_tx_ts > p_tx_block->timestamp) { + + // Get current RF timestamp + uint64_t curr_tx_ts = 0UL; + skiq_read_curr_tx_timestamp(q->card, q->hdl, &curr_tx_ts); + + // Avoids giving a block to the FPGA that has already passed, otherwise it could hang forever + if (curr_tx_ts > p_tx_block->timestamp) { + SKIQ_RF_ERROR("[Tx %d:%d block] Tx block (ts=%ld) is in the past (last_tx_ts=%ld, curr_tx_ts=%ld), ignoring\n", + q->card, + (int)q->hdl, + q->p_tx_block->timestamp, + last_tx_ts, + curr_tx_ts); + continue; + } + } + last_tx_ts = p_tx_block->timestamp + q->block_size; + + // If the ring-buffer did not return with error code... + if (n > SRSRAN_SUCCESS) { + SKIQ_RF_DEBUG("[Tx %d:%d block] ts=%ld; nsamples=%d; last_tx_ts=%ld;\n", + q->card, + (int)q->hdl, + p_tx_block->timestamp, + q->block_size, + last_tx_ts); + + if (skiq_transmit(q->card, q->hdl, p_tx_block, NULL) < 0) { + ERROR("Error transmitting card %d\n", q->card); + q->state = RF_SKIQ_PORT_STATE_STOP; + } + +#if SKIQ_TX_LATES_CHECK_PERIOD + if (q->last_check_ts + SKIQ_TX_LATES_CHECK_PERIOD < p_tx_block->timestamp) { + // Handle late timestamps events + rf_skiq_tx_port_handle_late(q); + + // Update last check TS + q->last_check_ts = p_tx_block->timestamp; + } +#endif // SKIQ_TX_LATES_CHECK_PERIOD + } + } + + if (skiq_stop_tx_streaming(q->card, q->hdl)) { + ERROR("Error stopping Tx stream %d:%d\n", q->card, (int)q->hdl); + } + + SKIQ_RF_INFO("Exiting writer thread!\n"); + + return NULL; +} + +int rf_skiq_tx_port_init(rf_skiq_tx_port_t* q, uint8_t card, skiq_tx_hdl_t hdl, const rf_skiq_port_opts_t* opts) +{ + // Defines the block size in multiples of 256 words + uint32_t nof_blocks_per_packet = 4; + switch (opts->stream_mode) { + case skiq_rx_stream_mode_high_tput: + nof_blocks_per_packet = 8; + break; + case skiq_rx_stream_mode_low_latency: + nof_blocks_per_packet = 2; + break; + case skiq_rx_stream_mode_balanced: + default: + // Keep default value + break; + } + + q->card = card; + q->hdl = hdl; + q->block_size = SKIQ_TX_PACKET_SIZE(nof_blocks_per_packet, opts->chan_mode); + q->p_block_nbytes = q->block_size * 4 + SKIQ_TX_HEADER_SIZE_IN_BYTES; + + // configure the data flow mode to use timestamps + if (skiq_write_tx_data_flow_mode(card, hdl, skiq_tx_with_timestamps_data_flow_mode) != 0) { + ERROR("Setting Tx data flow mode"); + return SRSRAN_ERROR; + } + + // configure the transfer mode to synchronous + if (skiq_write_tx_transfer_mode(card, hdl, skiq_tx_transfer_mode_sync) != 0) { + ERROR("setting tx transfer mode"); + return SRSRAN_ERROR; + } + + // configure Tx block size + if (skiq_write_tx_block_size(card, hdl, q->block_size) != 0) { + ERROR("configuring Tx block size"); + return SRSRAN_ERROR; + } + + q->p_tx_block = skiq_tx_block_allocate(q->block_size); + if (q->p_tx_block == NULL) { + ERROR("Allocating Tx block"); + return SRSRAN_ERROR; + } + + // initialise ring buffer + if (srsran_ringbuffer_init(&q->rb, (int)(opts->tx_rb_size * q->p_block_nbytes))) { + ERROR("Initialising ringbuffer"); + return SRSRAN_ERROR; + } + + // Initialise thread parameters + pthread_attr_t attr; + struct sched_param param; + + param.sched_priority = sched_get_priority_max(SCHED_FIFO); + pthread_attr_init(&attr); + if (pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED)) { + ERROR("Error not enough privileges to set Scheduling priority\n"); + } + + if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO)) { + ERROR("Error not enough privileges to set Scheduling priority\n"); + } + + if (pthread_attr_setschedparam(&attr, ¶m)) { + ERROR("Error not enough privileges to set Scheduling priority\n"); + } + + // Launch thread + if (pthread_create(&q->thread, &attr, writer_thread, q)) { + ERROR("Error creating writer thread with attributes (Did you miss sudo?). Trying without attributes.\n"); + return SRSRAN_ERROR; + } + + // Rename thread + char thread_name[32] = {}; + if (snprintf(thread_name, sizeof(thread_name), "SKIQ Tx %d:%d", q->card, (int)q->hdl) > 0) { + pthread_setname_np(q->thread, thread_name); + } + + return SRSRAN_SUCCESS; +} + +void rf_skiq_tx_port_free(rf_skiq_tx_port_t* q) +{ + // Stop thread + q->state = RF_SKIQ_PORT_STATE_STOP; + + // Unlock ringbuffer + srsran_ringbuffer_write(&q->rb, (void*)q->p_tx_block, q->p_block_nbytes); + + // Wait thread to return + pthread_join(q->thread, NULL); + + if (q->p_tx_block) { + skiq_tx_block_free(q->p_tx_block); + } + srsran_ringbuffer_free(&q->rb); +} + +void rf_skiq_tx_port_end_of_burst(rf_skiq_tx_port_t* q) +{ + pthread_mutex_lock(&q->mutex); + + // Fill pending block if any, otherwise push a block with zeros + if (q->next_offset > 0) { + // Calculate pending samples to fill the block + uint32_t pending = q->block_size - q->next_offset; + + // Zero pending samples in the block + srsran_vec_i16_zero(&q->p_tx_block->data[q->next_offset * 2], 2 * pending); + + // Write block into the ring-buffer + srsran_ringbuffer_write_block(&q->rb, q->p_tx_block, q->p_block_nbytes); + + SKIQ_RF_DEBUG("[Tx EOB %d:%d] Padding offset=%d; n=%d; ts=%ld\n", + q->card, + (int)q->hdl, + q->next_offset, + pending, + q->p_tx_block->timestamp); + + // Reset next offset, so next transmission uses a new block + q->next_offset = 0; + } + + pthread_mutex_unlock(&q->mutex); +} + +int rf_skiq_tx_port_send(rf_skiq_tx_port_t* q, const cf_t* buffer, uint32_t nsamples, uint64_t ts) +{ + // Ignore transmission if the stream is not enabled + if (q->state != RF_SKIQ_PORT_STATE_STREAMING) { + return nsamples; + } + + // If no data and no block has started, early return + if (buffer == NULL && q->next_offset == 0) { + return nsamples; + } + + pthread_mutex_lock(&q->mutex); + + // Calculate destination where IQ shall be stored + int16_t* p_tx_iq = &q->p_tx_block->data[q->next_offset * 2]; + + // Calculate number of samples to take from buffer + nsamples = SRSRAN_MIN(nsamples, q->block_size - q->next_offset); + + // Set time stamp only if no offset + if (q->next_offset == 0) { + skiq_tx_set_block_timestamp(q->p_tx_block, ts); + } + + SKIQ_RF_DEBUG( + "[Tx %d:%d] Write offset=%d; nsamples=%d; ts=%ld\n", q->card, (int)q->hdl, q->next_offset, nsamples, ts); + + // Fill data ... + if (buffer == NULL) { + // ... with zeros + srsran_vec_i16_zero(p_tx_iq, 2 * nsamples); + } else { + // ... with samples, after conversion + srsran_vec_convert_conj_cs(buffer, SKIQ_NORM, p_tx_iq, nsamples); + } + q->next_offset += nsamples; + + // If the number of samples does not fill the block, return early + if (q->next_offset < q->block_size) { + pthread_mutex_unlock(&q->mutex); + return nsamples; + } + + if (srsran_ringbuffer_space(&q->rb) < q->p_block_nbytes * 2) { + ERROR("Tx buffer overflow\n"); + pthread_mutex_unlock(&q->mutex); + return nsamples; + } + + // Actual write in ring buffer + int n = srsran_ringbuffer_write_timed_block(&q->rb, q->p_tx_block, q->p_block_nbytes, 5); + + pthread_mutex_unlock(&q->mutex); + + // In case of error (e.g. timeout) return code + if (n < SRSRAN_SUCCESS) { + return n; + } + + // In case of number of bytes mismatch return error + if (n != q->p_block_nbytes) { + ERROR("Error writing in Tx buffer %d:%d\n", q->card, (int)q->hdl); + return SRSRAN_ERROR; + } + + // Reset offset only if the block was successfully written into the ring-buffer + q->next_offset = 0; + + // Return the number of samples writen in the buffer + return nsamples; +} + +void rf_skiq_tx_port_set_error_handler(rf_skiq_tx_port_t* q, srsran_rf_error_handler_t error_handler, void* arg) +{ + q->error_handler = error_handler; + q->error_handler_arg = arg; +} + +int rf_skiq_tx_port_set_lo(rf_skiq_tx_port_t* q, uint64_t lo_freq) +{ + // Skip setting LO frequency if it is not required + if (q->current_lo == lo_freq) { + return SRSRAN_SUCCESS; + } + + skiq_filt_t filt = (lo_freq < 3000000000UL) ? skiq_filt_0_to_3000_MHz : skiq_filt_3000_to_6000_MHz; + + if (skiq_write_tx_LO_freq(q->card, q->hdl, lo_freq)) { + ERROR("Setting card %d:%d Tx Lo frequency", q->card, (int)q->hdl); + return SRSRAN_ERROR; + } + + if (skiq_write_tx_filter_path(q->card, q->hdl, filt)) { + ERROR("Setting card %d:%d Tx filter", q->card, q->hdl); + return SRSRAN_ERROR; + } + + q->current_lo = lo_freq; + + return SRSRAN_SUCCESS; +} + +int rf_skiq_rx_port_init(rf_skiq_rx_port_t* q, uint8_t card, skiq_rx_hdl_t hdl, const rf_skiq_port_opts_t* opts) +{ + q->card = card; + q->hdl = hdl; + + // enabling DC offset correction can cause an IQ impairment + if (skiq_write_rx_dc_offset_corr(card, hdl, false)) { + ERROR("Setting RX DC offset correction"); + return SRSRAN_ERROR; + } + + // set rx gain mode + if (skiq_write_rx_gain_mode(card, hdl, skiq_rx_gain_manual)) { + ERROR("Setting RX gain mode"); + return SRSRAN_ERROR; + } + + // Rx block size in bytes + int32_t rx_block_size = skiq_read_rx_block_size(q->card, opts->stream_mode) - SKIQ_RX_HEADER_SIZE_IN_BYTES; + + // initialise ring buffer + if (srsran_ringbuffer_init(&q->rb, (int)(opts->rx_rb_size * rx_block_size))) { + ERROR("Initialising ringbuffer"); + return SRSRAN_ERROR; + } + + return SRSRAN_SUCCESS; +} + +void rf_skiq_rx_port_free(rf_skiq_rx_port_t* q) +{ + + srsran_ringbuffer_free(&q->rb); +} + +int rf_skiq_rx_port_available(rf_skiq_rx_port_t* q) +{ + return srsran_ringbuffer_status(&q->rb); +} + +int rf_skiq_rx_port_read(rf_skiq_rx_port_t* q, cf_t* dest, uint32_t nsamples, uint64_t* ts_start) +{ + // Detect start of new block + if (q->rb_read_rem == 0) { + skiq_header_t header = {}; + + // If ring-buffer overflow was detected... + if (q->rb_overflow) { + // Reset ring buffer + srsran_ringbuffer_reset(&q->rb); + + // Clear overflow flag + q->rb_overflow = false; + + // Set samples to zero + srsran_vec_cf_zero(dest, nsamples); + + // Set default timestamp + *ts_start = 0; + + // Since the buffer is empty, return the full amount of samples so it does not delay reception of other channels + return nsamples; + } + + // Read a packet. First the header + if (srsran_ringbuffer_read(&q->rb, &header, sizeof(skiq_header_t)) != sizeof(skiq_header_t)) { + ERROR("Error reading header from ring-buffer %d:%d corrupted\n", q->card, (int)q->hdl); + return SRSRAN_ERROR; + } + + // Check header magic word + if (header.magic != SKIQ_RX_BUFFFER_MAGIC_WORD) { + ERROR("Error ring-buffer %d:%d corrupted\n", q->card, (int)q->hdl); + return SRSRAN_ERROR; + } + + // Successful read + q->rb_read_rem = header.nsamples; + q->rb_tstamp_rem = header.tstamp; + } + + // Limit number of samples to the remainder of the stored packet + nsamples = SRSRAN_MIN(q->rb_read_rem, nsamples); + + // Read any remainder of a packet from the ring buffer + int n = srsran_ringbuffer_read_convert_conj(&q->rb, dest, SKIQ_NORM, nsamples); + + // Detect error in read + if (n < SRSRAN_SUCCESS) { + ERROR("Error reading packet remainder from %d:%d\n", q->card, (int)q->hdl); + return SRSRAN_ERROR; + } + + SKIQ_RF_DEBUG("[Rx %d:%d] Read nsamples=%d/%d; ts=%ld\n", q->card, (int)q->hdl, n, nsamples, q->rb_tstamp_rem); + + // Update timestamp + *ts_start = q->rb_tstamp_rem; + + // Update reminder + q->rb_read_rem -= n; + q->rb_tstamp_rem += n; + + // Return number of read samples + return n; +} + +uint64_t rf_skiq_rx_port_get_timestamp(rf_skiq_rx_port_t* q) +{ + return q->rb_tstamp_rem; +} + +int rf_skiq_rx_port_write(rf_skiq_rx_port_t* q, const skiq_rx_block_t* p_rx_block, uint32_t nsamples) +{ + // Prepare header + skiq_header_t header = {}; + header.magic = SKIQ_RX_BUFFFER_MAGIC_WORD; + header.tstamp = p_rx_block->rf_timestamp; + header.nsamples = nsamples; + + // Ignore block if the overflow flag has risen + if (q->rb_overflow) { + return nsamples; + } + + SKIQ_RF_DEBUG("[Rx %d:%d block] ts=%ld; nsamples=%d;\n", q->card, (int)q->hdl, header.tstamp, header.nsamples); + + // Check space in the ring-buffer prior to writing + if (srsran_ringbuffer_space(&q->rb) >= sizeof(skiq_header_t) + nsamples * 4) { + // Write header + if (srsran_ringbuffer_write_block(&q->rb, &header, sizeof(skiq_header_t)) != sizeof(skiq_header_t)) { + ERROR("Writing header in Rx buffer %d:%d!\n", q->card, (int)q->hdl); + return SRSRAN_ERROR; + } + + // Write IQ samples + if (srsran_ringbuffer_write_block(&q->rb, (uint8_t*)p_rx_block->data, (int)nsamples * 4) != nsamples * 4) { + ERROR("Writing base-band in Rx buffer %d:%d!\n", q->card, (int)q->hdl); + return SRSRAN_ERROR; + } + } else { + SKIQ_RF_INFO("Rx %d:%d ring-buffer overflow!\n", q->card, (int)q->hdl); + q->rb_overflow = true; + rf_skiq_rx_port_handle_overflow(q); + } + + // Process overload, call handle only for rising-edges + if (!q->rf_overflow && p_rx_block->overload) { + rf_skiq_rx_port_handle_overflow(q); + } + q->rf_overflow = p_rx_block->overload; + + return nsamples; +} + +void rf_skiq_rx_port_set_error_handler(rf_skiq_rx_port_t* q, srsran_rf_error_handler_t error_handler, void* arg) +{ + q->error_handler = error_handler; + q->error_handler_arg = arg; +} + +void rf_skiq_rx_port_reset(rf_skiq_rx_port_t* q) +{ + SKIQ_RF_INFO("Rx port %d:%d reset\n", q->card, (int)q->hdl); + q->rb_read_rem = 0; + q->rb_tstamp_rem = 0; + srsran_ringbuffer_reset(&q->rb); +} + +int rf_skiq_rx_port_set_lo(rf_skiq_rx_port_t* q, uint64_t lo_freq) +{ + // Skip setting LO frequency if it is not required + if (q->current_lo == lo_freq) { + return SRSRAN_SUCCESS; + } + + skiq_filt_t filt = (lo_freq < 3000000000UL) ? skiq_filt_0_to_3000_MHz : skiq_filt_3000_to_6000_MHz; + + if (skiq_write_rx_LO_freq(q->card, q->hdl, lo_freq)) { + ERROR("Setting card %d:%d Rx Lo frequency", q->card, (int)q->hdl); + return SRSRAN_ERROR; + } + + if (skiq_write_rx_preselect_filter_path(q->card, q->hdl, filt)) { + ERROR("Setting card %d:%d Rx filter", q->card, q->hdl); + return SRSRAN_ERROR; + } + + q->current_lo = lo_freq; + + return SRSRAN_SUCCESS; +} diff --git a/lib/src/phy/rf/rf_skiq_imp_port.h b/lib/src/phy/rf/rf_skiq_imp_port.h new file mode 100644 index 000000000..87deb8340 --- /dev/null +++ b/lib/src/phy/rf/rf_skiq_imp_port.h @@ -0,0 +1,100 @@ +/** + * + * \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_RF_SKIQ_IMP_PORT_H +#define SRSRAN_RF_SKIQ_IMP_PORT_H + +#include +#include + +#include "rf_helper.h" +#include "rf_skiq_imp_cfg.h" +#include "rf_skiq_imp_port.h" +#include "srsran/srsran.h" + +typedef struct { + uint32_t magic; + uint64_t tstamp; + uint32_t nsamples; +} skiq_header_t; + +typedef enum { + RF_SKIQ_PORT_STATE_IDLE = 0, + RF_SKIQ_PORT_STATE_STREAMING, + RF_SKIQ_PORT_STATE_STOP +} rf_skiq_port_state_t; + +typedef struct { + uint32_t tx_rb_size; + uint32_t rx_rb_size; + skiq_chan_mode_t chan_mode; + char stream_mode_str[RF_PARAM_LEN]; + skiq_rx_stream_mode_t stream_mode; +} rf_skiq_port_opts_t; + +typedef struct { + uint8_t card; + skiq_tx_hdl_t hdl; + skiq_tx_block_t* p_tx_block; + uint32_t p_block_nbytes; // Size in bytes including header + uint32_t block_size; // Size in words (samples) + uint32_t next_offset; // Number of samples remainder + srsran_ringbuffer_t rb; + rf_skiq_port_state_t state; + pthread_t thread; + pthread_mutex_t mutex; // Protects p_tx_block + + uint64_t current_lo; + + srsran_rf_error_handler_t error_handler; + void* error_handler_arg; + +#if SKIQ_TX_LATES_CHECK_PERIOD + uint64_t last_check_ts; + uint32_t last_total_late; + uint32_t last_total_underruns; +#endif // SKIQ_TX_LATES_CHECK_PERIOD +} rf_skiq_tx_port_t; + +int rf_skiq_tx_port_init(rf_skiq_tx_port_t* q, uint8_t card, skiq_tx_hdl_t hdl, const rf_skiq_port_opts_t* opts); +void rf_skiq_tx_port_free(rf_skiq_tx_port_t* q); +void rf_skiq_tx_port_end_of_burst(rf_skiq_tx_port_t* q); +int rf_skiq_tx_port_send(rf_skiq_tx_port_t* q, const cf_t* buffer, uint32_t nsamples, uint64_t ts); +void rf_skiq_tx_port_set_error_handler(rf_skiq_tx_port_t* q, srsran_rf_error_handler_t error_handler, void* arg); +int rf_skiq_tx_port_set_lo(rf_skiq_tx_port_t* q, uint64_t lo_freq); + +typedef struct { + uint8_t card; + skiq_rx_hdl_t hdl; + srsran_ringbuffer_t rb; + uint32_t rb_read_rem; + uint64_t rb_tstamp_rem; + bool rf_overflow; ///< Indicates an RF message was flagged with overflow + bool rb_overflow; ///< Indicates that ring-buffer is full and it needs to be flushed + + uint64_t current_lo; + + srsran_rf_error_handler_t error_handler; + void* error_handler_arg; +} rf_skiq_rx_port_t; + +int rf_skiq_rx_port_init(rf_skiq_rx_port_t* q, uint8_t card, skiq_rx_hdl_t hdl, const rf_skiq_port_opts_t* opts); +void rf_skiq_rx_port_free(rf_skiq_rx_port_t* q); +int rf_skiq_rx_port_available(rf_skiq_rx_port_t* q); +int rf_skiq_rx_port_read(rf_skiq_rx_port_t* q, cf_t* dest, uint32_t nsamples, uint64_t* ts_start); +uint64_t rf_skiq_rx_port_get_timestamp(rf_skiq_rx_port_t* q); +int rf_skiq_rx_port_write(rf_skiq_rx_port_t* q, const skiq_rx_block_t* p_rx_block, uint32_t nbytes); +void rf_skiq_rx_port_set_error_handler(rf_skiq_rx_port_t* q, srsran_rf_error_handler_t error_handler, void* arg); +void rf_skiq_rx_port_reset(rf_skiq_rx_port_t* q); +int rf_skiq_rx_port_set_lo(rf_skiq_rx_port_t* q, uint64_t lo_freq); + +#endif // SRSRAN_RF_SKIQ_IMP_PORT_H diff --git a/lib/src/phy/rf/rf_zmq_imp.c b/lib/src/phy/rf/rf_zmq_imp.c index 2dfe8717a..b8e23cb92 100644 --- a/lib/src/phy/rf/rf_zmq_imp.c +++ b/lib/src/phy/rf/rf_zmq_imp.c @@ -204,10 +204,10 @@ int rf_zmq_open_multi(char* args, void** h, uint32_t nof_channels) return SRSRAN_ERROR; } bzero(handler, sizeof(rf_zmq_handler_t)); - *h = handler; - handler->base_srate = ZMQ_BASERATE_DEFAULT_HZ; // Sample rate for 100 PRB cell + *h = handler; + handler->base_srate = ZMQ_BASERATE_DEFAULT_HZ; // Sample rate for 100 PRB cell pthread_mutex_lock(&handler->rx_gain_mutex); - handler->rx_gain = 0.0; + handler->rx_gain = 0.0; pthread_mutex_unlock(&handler->rx_gain_mutex); handler->info.max_rx_gain = ZMQ_MAX_GAIN_DB; handler->info.min_rx_gain = ZMQ_MIN_GAIN_DB; @@ -480,7 +480,7 @@ int rf_zmq_set_rx_gain(void* h, double gain) if (h) { rf_zmq_handler_t* handler = (rf_zmq_handler_t*)h; pthread_mutex_lock(&handler->rx_gain_mutex); - handler->rx_gain = gain; + handler->rx_gain = gain; pthread_mutex_unlock(&handler->rx_gain_mutex); } return SRSRAN_SUCCESS; @@ -507,7 +507,7 @@ double rf_zmq_get_rx_gain(void* h) if (h) { rf_zmq_handler_t* handler = (rf_zmq_handler_t*)h; pthread_mutex_lock(&handler->rx_gain_mutex); - ret = handler->rx_gain; + ret = handler->rx_gain; pthread_mutex_unlock(&handler->rx_gain_mutex); } return ret; @@ -626,23 +626,28 @@ int rf_zmq_recv_with_time_multi(void* h, void** data, uint32_t nsamples, bool bl // Map ports to data buffers according to the selected frequencies pthread_mutex_lock(&handler->rx_config_mutex); + bool mapped[SRSRAN_MAX_CHANNELS] = {}; // Mapped mask, set to true when the physical channel is used cf_t* buffers[SRSRAN_MAX_CHANNELS] = {}; // Buffer pointers, NULL if unmatched - for (uint32_t i = 0; i < handler->nof_channels; i++) { - bool mapped = false; - - // Find first matching frequency - for (uint32_t j = 0; j < handler->nof_channels && !mapped; j++) { - // Traverse all channels, break if mapped - if (buffers[j] == NULL && rf_zmq_rx_match_freq(&handler->receiver[j], handler->rx_freq_mhz[i])) { - // Available buffer and matched frequency with receiver - buffers[j] = (cf_t*)data[i]; - mapped = true; + + // For each logical channel... + for (uint32_t logical = 0; logical < handler->nof_channels; logical++) { + bool unmatched = true; + + // For each physical channel... + for (uint32_t physical = 0; physical < handler->nof_channels; physical++) { + // Consider a match if the physical channel is NOT mapped and the frequency match + if (!mapped[physical] && rf_zmq_rx_match_freq(&handler->receiver[physical], handler->rx_freq_mhz[logical])) { + // Not mapped and matched frequency with receiver + buffers[physical] = (cf_t*)data[logical]; + mapped[physical] = true; + unmatched = false; + break; } } // If no matching frequency found; set data to zeros - if (!mapped && data[i]) { - memset(data[i], 0, sizeof(cf_t) * nsamples); + if (unmatched) { + srsran_vec_zero(data[logical], nsamples); } } pthread_mutex_unlock(&handler->rx_config_mutex); @@ -840,17 +845,19 @@ int rf_zmq_send_timed_multi(void* h, // Map ports to data buffers according to the selected frequencies pthread_mutex_lock(&handler->tx_config_mutex); - cf_t* buffers[SRSRAN_MAX_CHANNELS] = {}; // Buffer pointers, NULL if unmatched - for (uint32_t i = 0; i < handler->nof_channels; i++) { - bool mapped = false; - - // Find first matching frequency - for (uint32_t j = 0; j < handler->nof_channels && !mapped; j++) { - // Traverse all channels, break if mapped - if (buffers[j] == NULL && rf_zmq_tx_match_freq(&handler->transmitter[j], handler->tx_freq_mhz[i])) { - // Available buffer and matched frequency with receiver - buffers[j] = (cf_t*)data[i]; - mapped = true; + bool mapped[SRSRAN_MAX_CHANNELS] = {}; // Mapped mask, set to true when the physical channel is used + cf_t* buffers[SRSRAN_MAX_CHANNELS] = {}; // Buffer pointers, NULL if unmatched or zero transmission + + // For each logical channel... + for (uint32_t logical = 0; logical < handler->nof_channels; logical++) { + // For each physical channel... + for (uint32_t physical = 0; physical < handler->nof_channels; physical++) { + // Consider a match if the physical channel is NOT mapped and the frequency match + if (!mapped[physical] && rf_zmq_tx_match_freq(&handler->transmitter[physical], handler->tx_freq_mhz[logical])) { + // Not mapped and matched frequency with receiver + buffers[physical] = (cf_t*)data[logical]; + mapped[physical] = true; + break; } } } diff --git a/lib/src/phy/rf/skiq_pps_test.c b/lib/src/phy/rf/skiq_pps_test.c new file mode 100644 index 000000000..5ebd058b5 --- /dev/null +++ b/lib/src/phy/rf/skiq_pps_test.c @@ -0,0 +1,133 @@ +/** + * + * \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 +#include +#include + +#define ERROR(...) fprintf(stderr, __VA_ARGS__); + +const static uint8_t nof_cards = 1; + +int main(int argc, char* argv[]) +{ + int ret = -1; + uint8_t card = 0; + + if (argc > 1) { + card = (uint8_t)strtol(argv[1], NULL, 10); + } + + uint8_t nof_available_cards = 0; + uint8_t available_cards[SKIQ_MAX_NUM_CARDS] = {}; + if (skiq_get_cards(skiq_xport_type_auto, &nof_available_cards, available_cards)) { + ERROR("Getting available cards\n"); + goto clean_exit; + } + + // Check number of cards bounds + if (nof_cards > nof_available_cards) { + ERROR("The number of cards (%d) exceeds available cards (%d)\n", nof_cards, nof_available_cards); + goto clean_exit; + } + + // Initialise driver + if (skiq_init(skiq_xport_type_auto, skiq_xport_init_level_full, &card, 1)) { + ERROR("Unable to initialise libsidekiq driver\n"); + goto clean_exit; + } + + // Programming FPGA from flash ensures the FPGA and transceiver are completely reseted + if (skiq_prog_fpga_from_flash(card)) { + ERROR("Error programming FPGA from flash\n"); + goto clean_exit; + } + + // Read 1pps source + skiq_1pps_source_t pps_source = skiq_1pps_source_unavailable; + if (skiq_read_1pps_source(card, &pps_source)) { + ERROR("Error reading card %d 1PPS source\n", card); + goto clean_exit; + } + + // Make sure the source is external + if (pps_source != skiq_1pps_source_external) { + ERROR("Error card %d is not configured with external 1PPS source\n", card); + goto clean_exit; + } + + // Sleeping 5 seconds + sleep(5); + + // Get last time a PPS was received + uint64_t ts_sys_1pps = 0; + if (skiq_read_last_1pps_timestamp(card, NULL, &ts_sys_1pps)) { + ERROR("Reading card %d last 1PPS timestamp", card); + goto clean_exit; + } + + // Read current system time + uint64_t ts = 0; + if (skiq_read_curr_sys_timestamp(card, &ts)) { + ERROR("Reading card %d system timestamp", card); + goto clean_exit; + } + + // Make sure a 1PPS was received less than 2 seconds ago + if (ts - ts_sys_1pps > 2 * SKIQ_SYS_TIMESTAMP_FREQ) { + ERROR("Error card %d last PPS was received %.1f seconds ago (ts=%ld)\n", + card, + (double)(ts - ts_sys_1pps) / (double)SKIQ_SYS_TIMESTAMP_FREQ, + ts_sys_1pps); + goto clean_exit; + } + + // Set a given time in the future, a 100th of a second (10ms) + ts += SKIQ_SYS_TIMESTAMP_FREQ / 100; + + // Reset timestamp in a near future on next PPS signal + if (skiq_write_timestamp_reset_on_1pps(card, ts)) { + ERROR("Error reseting card %d timestamp on 1 PPS", card); + goto clean_exit; + } + + // Give time to pass 1PPS + sleep(1); + + // Read current system time + if (skiq_read_curr_sys_timestamp(card, &ts)) { + ERROR("Reading card %d system timestamp", card); + goto clean_exit; + } + + // The current system timestamp should be below 2s + if (ts > 2 * SKIQ_SYS_TIMESTAMP_FREQ) { + ERROR("Timestamp of card %d is greater than 2 seconds (%.1fs)!\n", + card, + (double)ts / (double)SKIQ_SYS_TIMESTAMP_FREQ); + goto clean_exit; + } + + // Success + printf("Success!\n"); + ret = 0; + +clean_exit: + if (skiq_disable_cards(&card, 1)) { + ERROR("Unable to disable cards\n"); + } + + // Close sidekiq SDK + skiq_exit(); + + return ret; +} diff --git a/lib/src/phy/ue/test/CMakeLists.txt b/lib/src/phy/ue/test/CMakeLists.txt index ccd27785c..7c6c43959 100644 --- a/lib/src/phy/ue/test/CMakeLists.txt +++ b/lib/src/phy/ue/test/CMakeLists.txt @@ -54,4 +54,16 @@ if(RF_FOUND) else(SRSGUI_FOUND) add_definitions(-DDISABLE_GRAPHICS) endif(SRSGUI_FOUND) -endif(RF_FOUND) \ No newline at end of file +endif(RF_FOUND) + +add_executable(ue_dl_nr_file_test ue_dl_nr_file_test.c) +target_link_libraries(ue_dl_nr_file_test srsran_phy pthread) +foreach (n RANGE 0 9) + add_test(ue_dl_nr_pci1_rb25_n${n}_common_L1_ncce0 ue_dl_nr_file_test -f ${CMAKE_CURRENT_SOURCE_DIR}/ue_dl_nr_pci1_rb25_n${n}_common_L1_ncce0.dat -i 1 -P 25 -n ${n} -R 1234) + add_test(ue_dl_nr_pci1_rb25_n${n}_common_L1_ncce0 ue_dl_nr_file_test -f ${CMAKE_CURRENT_SOURCE_DIR}/ue_dl_nr_pci1_rb25_n${n}_common_L1_ncce0.dat -i 1 -P 25 -n ${n} -R 1234) + add_test(ue_dl_nr_pci1_rb25_n${n}_common_L1_ncce0 ue_dl_nr_file_test -f ${CMAKE_CURRENT_SOURCE_DIR}/ue_dl_nr_pci1_rb25_n${n}_common_L1_ncce0.dat -i 1 -P 25 -n ${n} -R 1234) + add_test(ue_dl_nr_pci1_rb25_n${n}_common_L1_ncce0 ue_dl_nr_file_test -f ${CMAKE_CURRENT_SOURCE_DIR}/ue_dl_nr_pci1_rb25_n${n}_common_L1_ncce0.dat -i 1 -P 25 -n ${n} -R 1234) + add_test(ue_dl_nr_pci1_rb25_n${n}_common_L1_ncce0 ue_dl_nr_file_test -f ${CMAKE_CURRENT_SOURCE_DIR}/ue_dl_nr_pci1_rb25_n${n}_common_L1_ncce0.dat -i 1 -P 25 -n ${n} -R 1234) + add_test(ue_dl_nr_pci1_rb25_n${n}_common_L1_ncce0 ue_dl_nr_file_test -f ${CMAKE_CURRENT_SOURCE_DIR}/ue_dl_nr_pci1_rb25_n${n}_common_L1_ncce0.dat -i 1 -P 25 -n ${n} -R 1234) +endforeach () +add_test(ue_dl_nr_pci500_rb52_n4_ra_L2_ncce0 ue_dl_nr_file_test -f ${CMAKE_CURRENT_SOURCE_DIR}/ue_dl_nr_pci500_rb52_n4_ra_L2_ncce0.dat -i 1 -P 52 -n 4 -R 7f) diff --git a/lib/src/phy/ue/test/ue_dl_nr_file_test.c b/lib/src/phy/ue/test/ue_dl_nr_file_test.c new file mode 100644 index 000000000..a3a58a2e2 --- /dev/null +++ b/lib/src/phy/ue/test/ue_dl_nr_file_test.c @@ -0,0 +1,286 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#include "srsran/phy/io/filesource.h" +#include "srsran/phy/phch/ra_nr.h" +#include "srsran/phy/ue/ue_dl_nr.h" +#include "srsran/phy/utils/debug.h" +#include + +static srsran_carrier_nr_t carrier = { + 501, // pci + 0, // absolute_frequency_ssb + 0, // absolute_frequency_point_a + 0, // offset_to_carrier + srsran_subcarrier_spacing_15kHz, // scs + 52, // nof_prb + 0, // start + 1 // max_mimo_layers +}; + +static char* filename = NULL; +static srsran_pdcch_cfg_nr_t pdcch_cfg = {}; +static srsran_sch_hl_cfg_nr_t pdsch_hl_cfg = {}; +static uint16_t rnti = 0x1234; +static srsran_rnti_type_t rnti_type = srsran_rnti_type_c; +static srsran_slot_cfg_t slot_cfg = {}; + +static srsran_softbuffer_rx_t softbuffer = {}; +static uint8_t* data = NULL; + +static void usage(char* prog) +{ + printf("Usage: %s [pTLR] \n", prog); + printf("\t-f File name [Default none]\n"); + printf("\t-P Number of BWP (Carrier) PRB [Default %d]\n", carrier.nof_prb); + printf("\t-i Physical cell identifier [Default %d]\n", carrier.pci); + printf("\t-n Slot index [Default %d]\n", slot_cfg.idx); + printf("\t-R RNTI in hexadecimal [Default 0x%x]\n", rnti); + printf("\t-T RNTI type (c, ra) [Default %s]\n", srsran_rnti_type_str(rnti_type)); + printf("\t-S Use standard rates [Default %s]\n", srsran_symbol_size_is_standard() ? "yes" : "no"); + + printf("\t-v [set srsran_verbose to debug, default none]\n"); +} + +static int parse_args(int argc, char** argv) +{ + int opt; + while ((opt = getopt(argc, argv, "fPivnSRT")) != -1) { + switch (opt) { + case 'f': + filename = argv[optind]; + break; + case 'P': + carrier.nof_prb = (uint32_t)strtol(argv[optind], NULL, 10); + break; + case 'i': + carrier.pci = (uint32_t)strtol(argv[optind], NULL, 10); + break; + case 'v': + srsran_verbose++; + break; + case 'n': + slot_cfg.idx = (uint32_t)strtol(argv[optind], NULL, 10); + break; + case 'R': + rnti = (uint16_t)strtol(argv[optind], NULL, 16); + break; + case 'T': + if (strcmp(argv[optind], "c") == 0) { + rnti_type = srsran_rnti_type_c; + } else if (strcmp(argv[optind], "ra") == 0) { + rnti_type = srsran_rnti_type_ra; + } else { + printf("Invalid RNTI type '%s'\n", argv[optind]); + usage(argv[0]); + return SRSRAN_ERROR; + } + break; + case 'S': + srsran_use_standard_symbol_size(true); + break; + default: + usage(argv[0]); + return SRSRAN_ERROR; + } + } + + return SRSRAN_SUCCESS; +} + +static int work_ue_dl(srsran_ue_dl_nr_t* ue_dl, srsran_slot_cfg_t* slot) +{ + // Run FFT + srsran_ue_dl_nr_estimate_fft(ue_dl, slot); + + // Blind search + srsran_dci_dl_nr_t dci_dl_rx = {}; + int nof_found_dci = srsran_ue_dl_nr_find_dl_dci(ue_dl, slot, rnti, rnti_type, &dci_dl_rx, 1); + if (nof_found_dci < SRSRAN_SUCCESS) { + ERROR("Error in blind search"); + return SRSRAN_ERROR; + } + + // Print PDCCH blind search candidates + for (uint32_t i = 0; i < ue_dl->pdcch_info_count; i++) { + const srsran_ue_dl_nr_pdcch_info_t* info = &ue_dl->pdcch_info[i]; + INFO("PDCCH: rnti=0x%x, crst_id=%d, ss_type=%d, ncce=%d, al=%d, EPRE=%+.2f, RSRP=%+.2f, corr=%.3f; " + "nof_bits=%d; crc=%s;", + info->dci_ctx.rnti, + info->dci_ctx.coreset_id, + info->dci_ctx.ss_type, + info->dci_ctx.location.ncce, + info->dci_ctx.location.L, + info->measure.epre_dBfs, + info->measure.rsrp_dBfs, + info->measure.norm_corr, + info->nof_bits, + info->result.crc ? "OK" : "KO"); + } + + if (nof_found_dci < 1) { + printf("No DCI found :'(\n"); + return SRSRAN_SUCCESS; + } + + char str[1024] = {}; + srsran_dci_dl_nr_to_str(&ue_dl->dci, &dci_dl_rx, str, (uint32_t)sizeof(str)); + printf("Found DCI: %s\n", str); + + // Convert DCI to PDSCH transmission + srsran_sch_cfg_nr_t pdsch_cfg = {}; + if (srsran_ra_dl_dci_to_grant_nr(&carrier, slot, &pdsch_hl_cfg, &dci_dl_rx, &pdsch_cfg, &pdsch_cfg.grant) < + SRSRAN_SUCCESS) { + ERROR("Error decoding PDSCH search"); + return SRSRAN_ERROR; + } + + srsran_sch_cfg_nr_info(&pdsch_cfg, str, (uint32_t)sizeof(str)); + printf("PDSCH: %s\n", str); + + // Set softbuffer + pdsch_cfg.grant.tb[0].softbuffer.rx = &softbuffer; + + // Prepare PDSCH result + srsran_pdsch_res_nr_t pdsch_res = {}; + pdsch_res.tb[0].payload = data; + + // Decode PDSCH + if (srsran_ue_dl_nr_decode_pdsch(ue_dl, slot, &pdsch_cfg, &pdsch_res) < SRSRAN_SUCCESS) { + ERROR("Error decoding PDSCH search"); + return SRSRAN_ERROR; + } + + return SRSRAN_SUCCESS; +} + +int main(int argc, char** argv) +{ + int ret = SRSRAN_ERROR; + srsran_ue_dl_nr_t ue_dl = {}; + cf_t* buffer[SRSRAN_MAX_PORTS] = {}; + + uint32_t sf_len = SRSRAN_SF_LEN_PRB(carrier.nof_prb); + buffer[0] = srsran_vec_cf_malloc(sf_len); + if (buffer[0] == NULL) { + ERROR("Error malloc"); + goto clean_exit; + } + + if (srsran_softbuffer_rx_init_guru(&softbuffer, SRSRAN_SCH_NR_MAX_NOF_CB_LDPC, SRSRAN_LDPC_MAX_LEN_ENCODED_CB) < + SRSRAN_SUCCESS) { + ERROR("Error init soft-buffer"); + goto clean_exit; + } + + data = srsran_vec_u8_malloc(SRSRAN_SLOT_MAX_NOF_BITS_NR); + if (data == NULL) { + ERROR("Error malloc"); + goto clean_exit; + } + + srsran_ue_dl_nr_args_t ue_dl_args = {}; + ue_dl_args.nof_rx_antennas = 1; + ue_dl_args.pdsch.sch.disable_simd = false; + ue_dl_args.pdsch.sch.decoder_use_flooded = false; + ue_dl_args.pdsch.measure_evm = true; + ue_dl_args.pdcch.disable_simd = false; + ue_dl_args.pdcch.measure_evm = true; + ue_dl_args.nof_max_prb = carrier.nof_prb; + + // Set default PDSCH configuration + if (parse_args(argc, argv) < SRSRAN_SUCCESS) { + goto clean_exit; + } + + // Check for filename + if (filename == NULL) { + ERROR("Filename was not provided"); + goto clean_exit; + } + + // Open filesource + srsran_filesource_t filesource = {}; + if (srsran_filesource_init(&filesource, filename, SRSRAN_COMPLEX_FLOAT_BIN) < SRSRAN_SUCCESS) { + ERROR("Error opening filesource"); + goto clean_exit; + } + + // Configure CORESET + srsran_coreset_t* coreset = &pdcch_cfg.coreset[1]; + pdcch_cfg.coreset_present[1] = true; + coreset->duration = 2; + for (uint32_t i = 0; i < SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE; i++) { + coreset->freq_resources[i] = i < carrier.nof_prb / 6; + } + + // Configure Search Space + srsran_search_space_t* search_space = &pdcch_cfg.search_space[0]; + pdcch_cfg.search_space_present[0] = true; + search_space->id = 0; + search_space->coreset_id = 1; + search_space->type = srsran_search_space_type_common_3; + search_space->formats[0] = srsran_dci_format_nr_0_0; + search_space->formats[1] = srsran_dci_format_nr_1_0; + search_space->nof_formats = 2; + for (uint32_t L = 0; L < SRSRAN_SEARCH_SPACE_NOF_AGGREGATION_LEVELS_NR; L++) { + search_space->nof_candidates[L] = srsran_pdcch_nr_max_candidates_coreset(coreset, L); + } + + // Configure RA search space + pdcch_cfg.ra_search_space_present = true; + pdcch_cfg.ra_search_space = *search_space; + pdcch_cfg.ra_search_space.type = srsran_search_space_type_common_1; + + if (srsran_ue_dl_nr_init(&ue_dl, buffer, &ue_dl_args)) { + ERROR("Error UE DL"); + goto clean_exit; + } + + if (srsran_ue_dl_nr_set_carrier(&ue_dl, &carrier)) { + ERROR("Error setting SCH NR carrier"); + goto clean_exit; + } + + // Read baseband from file + if (srsran_filesource_read(&filesource, buffer[0], (int)ue_dl.fft->sf_sz) < SRSRAN_SUCCESS) { + ERROR("Error reading baseband"); + goto clean_exit; + } + + srsran_dci_cfg_nr_t dci_cfg = {}; + dci_cfg.bwp_dl_initial_bw = carrier.nof_prb; + dci_cfg.bwp_ul_initial_bw = carrier.nof_prb; + dci_cfg.monitor_common_0_0 = true; + if (srsran_ue_dl_nr_set_pdcch_config(&ue_dl, &pdcch_cfg, &dci_cfg)) { + ERROR("Error setting CORESET"); + goto clean_exit; + } + + // Actual decode + work_ue_dl(&ue_dl, &slot_cfg); + + ret = SRSRAN_SUCCESS; + +clean_exit: + if (buffer[0] != NULL) { + free(buffer[0]); + } + if (data != NULL) { + free(data); + } + srsran_ue_dl_nr_free(&ue_dl); + srsran_filesource_free(&filesource); + srsran_softbuffer_rx_free(&softbuffer); + + return ret; +} diff --git a/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n0_common_L1_ncce0.dat b/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n0_common_L1_ncce0.dat new file mode 100644 index 000000000..2ddfae789 Binary files /dev/null and b/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n0_common_L1_ncce0.dat differ diff --git a/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n1_common_L1_ncce0.dat b/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n1_common_L1_ncce0.dat new file mode 100644 index 000000000..8374a76d5 Binary files /dev/null and b/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n1_common_L1_ncce0.dat differ diff --git a/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n2_common_L1_ncce0.dat b/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n2_common_L1_ncce0.dat new file mode 100644 index 000000000..3e5240db7 Binary files /dev/null and b/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n2_common_L1_ncce0.dat differ diff --git a/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n3_common_L1_ncce0.dat b/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n3_common_L1_ncce0.dat new file mode 100644 index 000000000..4f33583f3 Binary files /dev/null and b/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n3_common_L1_ncce0.dat differ diff --git a/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n4_common_L1_ncce0.dat b/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n4_common_L1_ncce0.dat new file mode 100644 index 000000000..d5cb3e0f0 Binary files /dev/null and b/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n4_common_L1_ncce0.dat differ diff --git a/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n5_common_L1_ncce0.dat b/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n5_common_L1_ncce0.dat new file mode 100644 index 000000000..6053e5be9 Binary files /dev/null and b/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n5_common_L1_ncce0.dat differ diff --git a/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n6_common_L1_ncce0.dat b/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n6_common_L1_ncce0.dat new file mode 100644 index 000000000..2d4ca1655 Binary files /dev/null and b/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n6_common_L1_ncce0.dat differ diff --git a/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n7_common_L1_ncce0.dat b/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n7_common_L1_ncce0.dat new file mode 100644 index 000000000..f6fab76bf Binary files /dev/null and b/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n7_common_L1_ncce0.dat differ diff --git a/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n8_common_L1_ncce0.dat b/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n8_common_L1_ncce0.dat new file mode 100644 index 000000000..8b709e9e2 Binary files /dev/null and b/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n8_common_L1_ncce0.dat differ diff --git a/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n9_common_L1_ncce0.dat b/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n9_common_L1_ncce0.dat new file mode 100644 index 000000000..2f6b974bb Binary files /dev/null and b/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n9_common_L1_ncce0.dat differ diff --git a/lib/src/phy/ue/test/ue_dl_nr_pci500_rb52_n4_ra_L2_ncce0.dat b/lib/src/phy/ue/test/ue_dl_nr_pci500_rb52_n4_ra_L2_ncce0.dat new file mode 100644 index 000000000..46ba0ffe5 Binary files /dev/null and b/lib/src/phy/ue/test/ue_dl_nr_pci500_rb52_n4_ra_L2_ncce0.dat differ diff --git a/lib/src/phy/ue/ue_dl.c b/lib/src/phy/ue/ue_dl.c index 65ad2209e..9a832b721 100644 --- a/lib/src/phy/ue/ue_dl.c +++ b/lib/src/phy/ue/ue_dl.c @@ -862,7 +862,8 @@ void srsran_ue_dl_gen_cqi_periodic(srsran_ue_dl_t* q, uci_data->cfg.cqi.ri_len = 1; uci_data->value.ri = cfg->last_ri; } else if (srsran_cqi_periodic_send(&cfg->cfg.cqi_report, tti, q->cell.frame_type)) { - if (cfg->cfg.cqi_report.format_is_subband) { + if (cfg->cfg.cqi_report.format_is_subband && + srsran_cqi_periodic_is_subband(&cfg->cfg.cqi_report, tti, q->cell.nof_prb, q->cell.frame_type)) { // TODO: Implement subband periodic reports uci_data->cfg.cqi.type = SRSRAN_CQI_TYPE_SUBBAND_UE; uci_data->value.cqi.subband_ue.subband_cqi = wideband_value; diff --git a/lib/src/phy/utils/bit.c b/lib/src/phy/utils/bit.c index fb82e38ac..7a79ba351 100644 --- a/lib/src/phy/utils/bit.c +++ b/lib/src/phy/utils/bit.c @@ -736,6 +736,22 @@ void srsran_bit_unpack(uint32_t value, uint8_t** bits, int nof_bits) *bits += nof_bits; } +/** + * Unpacks nof_bits from LSBs of value in LSB order to *bits. Advances pointer past unpacked bits. + * + * @param[in] value nof_bits lowest order bits will be unpacked in MSB order + * @param[in] nof_bits Number of bits to unpack + * @param[out] bits Points to buffer pointer. The buffer pointer will be advanced by nof_bits + */ +void srsran_bit_unpack_lsb(uint32_t value, uint8_t** bits, int nof_bits) +{ + int i; + for (i = 0; i < nof_bits; i++) { + (*bits)[nof_bits - i - 1] = (value >> (nof_bits - i - 1)) & 0x1; + } + *bits += nof_bits; +} + void srsran_bit_pack_vector(uint8_t* unpacked, uint8_t* packed, int nof_bits) { uint32_t i, nbytes; diff --git a/lib/src/phy/utils/vector_simd.c b/lib/src/phy/utils/vector_simd.c index 4a0e1e4e8..60f55ec9c 100644 --- a/lib/src/phy/utils/vector_simd.c +++ b/lib/src/phy/utils/vector_simd.c @@ -1386,7 +1386,7 @@ uint32_t srsran_vec_max_fi_simd(const float* x, const int len) simd_f_t a = srsran_simd_f_load(&x[i]); simd_sel_t res = srsran_simd_f_max(a, simd_max_values); simd_max_indexes = srsran_simd_i_select(simd_max_indexes, simd_indexes, res); - simd_max_values = (simd_f_t)srsran_simd_i_select((simd_i_t)simd_max_values, (simd_i_t)a, res); + simd_max_values = srsran_simd_f_select(simd_max_values, a, res); simd_indexes = srsran_simd_i_add(simd_indexes, simd_inc); } } else { @@ -1394,7 +1394,7 @@ uint32_t srsran_vec_max_fi_simd(const float* x, const int len) simd_f_t a = srsran_simd_f_loadu(&x[i]); simd_sel_t res = srsran_simd_f_max(a, simd_max_values); simd_max_indexes = srsran_simd_i_select(simd_max_indexes, simd_indexes, res); - simd_max_values = (simd_f_t)srsran_simd_i_select((simd_i_t)simd_max_values, (simd_i_t)a, res); + simd_max_values = srsran_simd_f_select(simd_max_values, a, res); simd_indexes = srsran_simd_i_add(simd_indexes, simd_inc); } } @@ -1444,7 +1444,7 @@ uint32_t srsran_vec_max_abs_fi_simd(const float* x, const int len) simd_f_t a = srsran_simd_f_abs(srsran_simd_f_load(&x[i])); simd_sel_t res = srsran_simd_f_max(a, simd_max_values); simd_max_indexes = srsran_simd_i_select(simd_max_indexes, simd_indexes, res); - simd_max_values = (simd_f_t)srsran_simd_i_select((simd_i_t)simd_max_values, (simd_i_t)a, res); + simd_max_values = srsran_simd_f_select(simd_max_values, a, res); simd_indexes = srsran_simd_i_add(simd_indexes, simd_inc); } } else { @@ -1452,7 +1452,7 @@ uint32_t srsran_vec_max_abs_fi_simd(const float* x, const int len) simd_f_t a = srsran_simd_f_abs(srsran_simd_f_loadu(&x[i])); simd_sel_t res = srsran_simd_f_max(a, simd_max_values); simd_max_indexes = srsran_simd_i_select(simd_max_indexes, simd_indexes, res); - simd_max_values = (simd_f_t)srsran_simd_i_select((simd_i_t)simd_max_values, (simd_i_t)a, res); + simd_max_values = srsran_simd_f_select(simd_max_values, a, res); simd_indexes = srsran_simd_i_add(simd_indexes, simd_inc); } } @@ -1511,7 +1511,7 @@ uint32_t srsran_vec_max_ci_simd(const cf_t* x, const int len) simd_sel_t res = srsran_simd_f_max(z1, simd_max_values); simd_max_indexes = srsran_simd_i_select(simd_max_indexes, simd_indexes, res); - simd_max_values = (simd_f_t)srsran_simd_i_select((simd_i_t)simd_max_values, (simd_i_t)z1, res); + simd_max_values = srsran_simd_f_select(simd_max_values, z1, res); simd_indexes = srsran_simd_i_add(simd_indexes, simd_inc); } } else { @@ -1527,7 +1527,7 @@ uint32_t srsran_vec_max_ci_simd(const cf_t* x, const int len) simd_sel_t res = srsran_simd_f_max(z1, simd_max_values); simd_max_indexes = srsran_simd_i_select(simd_max_indexes, simd_indexes, res); - simd_max_values = (simd_f_t)srsran_simd_i_select((simd_i_t)simd_max_values, (simd_i_t)z1, res); + simd_max_values = srsran_simd_f_select(simd_max_values, z1, res); simd_indexes = srsran_simd_i_add(simd_indexes, simd_inc); } } diff --git a/lib/src/radio/radio.cc b/lib/src/radio/radio.cc index ee2aad6e5..965513f67 100644 --- a/lib/src/radio/radio.cc +++ b/lib/src/radio/radio.cc @@ -539,10 +539,9 @@ bool radio::tx_dev(const uint32_t& device_idx, rf_buffer_interface& buffer, cons nof_samples = nof_samples - past_nsamples; // Subtracts the number of trimmed samples // Prints discarded samples - logger.debug("Detected RF overlap of %.1f us. Discarding %d samples. Power=%+.1f dBfs", + logger.debug("Detected RF overlap of %.1f us. Discarding %d samples.", srsran_timestamp_real(&ts_overlap) * 1.0e6, - past_nsamples, - srsran_convert_power_to_dB(srsran_vec_avg_power_cf(&buffer.get(0)[nof_samples], past_nsamples))); + past_nsamples); } else if (past_nsamples < 0 and not is_start_of_burst) { // if the gap is bigger than TX_MAX_GAP_ZEROS, stop burst diff --git a/lib/src/rlc/rlc_am_lte.cc b/lib/src/rlc/rlc_am_lte.cc index 5b1906c5f..a5084af11 100644 --- a/lib/src/rlc/rlc_am_lte.cc +++ b/lib/src/rlc/rlc_am_lte.cc @@ -617,6 +617,11 @@ void rlc_am_lte::rlc_am_lte_tx::retransmit_pdu(uint32_t sn) // select first PDU in tx window for retransmission rlc_amd_tx_pdu& pdu = tx_window[sn]; + + // increment retx counter and inform upper layers + pdu.retx_count++; + check_sn_reached_max_retx(sn); + logger.info("%s Schedule SN=%d for reTx", RB_NAME, pdu.rlc_sn); rlc_amd_retx_t& retx = retx_queue.push(); retx.is_segment = false; @@ -760,8 +765,6 @@ int rlc_am_lte::rlc_am_lte_tx::build_retx_pdu(uint8_t* payload, uint32_t nof_byt memcpy(ptr, tx_window[retx.sn].buf->msg, tx_window[retx.sn].buf->N_bytes); retx_queue.pop(); - tx_window[retx.sn].retx_count++; - check_sn_reached_max_retx(retx.sn); logger.info(payload, tx_window[retx.sn].buf->N_bytes, @@ -920,9 +923,6 @@ int rlc_am_lte::rlc_am_lte_tx::build_segment(uint8_t* payload, uint32_t nof_byte retx_queue.front().so_start = retx.so_end; } - tx_window[retx.sn].retx_count++; - check_sn_reached_max_retx(retx.sn); - // Write header and pdu uint8_t* ptr = payload; rlc_am_write_data_pdu_header(&new_header, &ptr); @@ -1228,7 +1228,13 @@ void rlc_am_lte::rlc_am_lte_tx::handle_control_pdu(uint8_t* payload, uint32_t no std::lock_guard lock(mutex); if (tx_window.has_sn(i)) { auto& pdu = tx_window[i]; + + // add to retx queue if it's not already there if (not retx_queue.has_sn(i)) { + // increment Retx counter and inform upper layers if needed + pdu.retx_count++; + check_sn_reached_max_retx(i); + rlc_amd_retx_t& retx = retx_queue.push(); srsran_expect(tx_window[i].rlc_sn == i, "Incorrect RLC SN=%d!=%d being accessed", tx_window[i].rlc_sn, i); retx.sn = i; diff --git a/lib/test/asn1/srsran_asn1_rrc_nr_test.cc b/lib/test/asn1/srsran_asn1_rrc_nr_test.cc index 24497e75c..819ec3f70 100644 --- a/lib/test/asn1/srsran_asn1_rrc_nr_test.cc +++ b/lib/test/asn1/srsran_asn1_rrc_nr_test.cc @@ -26,7 +26,9 @@ #define JSON_OUTPUT 0 #define HAVE_PCAP 0 +#if HAVE_PCAP #include "srsran/common/test_pcap.h" +#endif using namespace asn1; using namespace asn1::rrc_nr; @@ -375,12 +377,12 @@ int test_cell_group_config() // pack it again cell_group_cfg_s cell_group_cfg_pack; - cell_group_cfg_pack.sp_cell_cfg_present = true; - cell_group_cfg_pack.sp_cell_cfg.serv_cell_idx_present = true; + cell_group_cfg_pack.sp_cell_cfg_present = true; + cell_group_cfg_pack.sp_cell_cfg.serv_cell_idx_present = true; // SP Cell Dedicated config - cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded_present = true; - cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp_present = true; + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded_present = true; + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp_present = true; // PDCCH config cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp.pdcch_cfg_present = true; @@ -474,33 +476,180 @@ int test_cell_group_config() cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.first_active_dl_bwp_id = 1; cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.ul_cfg_present = true; + // UL config dedicated + // PUCCH + auto& ul_config = cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.ul_cfg; + ul_config.init_ul_bwp_present = true; + ul_config.init_ul_bwp.pucch_cfg_present = true; + ul_config.init_ul_bwp.pucch_cfg.set_setup(); + ul_config.init_ul_bwp.pucch_cfg.setup().format2_present = true; + ul_config.init_ul_bwp.pucch_cfg.setup().format2.set_setup(); + ul_config.init_ul_bwp.pucch_cfg.setup().format2.setup().max_code_rate_present = true; + ul_config.init_ul_bwp.pucch_cfg.setup().format2.setup().max_code_rate = pucch_max_code_rate_opts::zero_dot25; + + // SR resources + ul_config.init_ul_bwp.pucch_cfg.setup().sched_request_res_to_add_mod_list_present = true; + ul_config.init_ul_bwp.pucch_cfg.setup().sched_request_res_to_add_mod_list.resize(1); + auto& sr_res1 = ul_config.init_ul_bwp.pucch_cfg.setup().sched_request_res_to_add_mod_list[0]; + sr_res1.sched_request_res_id = 1; + sr_res1.sched_request_id = 0; + sr_res1.periodicity_and_offset_present = true; + sr_res1.periodicity_and_offset.set_sl40(); + sr_res1.periodicity_and_offset.sl40() = 7; + sr_res1.res_present = true; + sr_res1.res = 0; // only PUCCH resource we have defined so far + + // DL data + ul_config.init_ul_bwp.pucch_cfg.setup().dl_data_to_ul_ack_present = true; + ul_config.init_ul_bwp.pucch_cfg.setup().dl_data_to_ul_ack.resize(5); + ul_config.init_ul_bwp.pucch_cfg.setup().dl_data_to_ul_ack[0] = 8; + ul_config.init_ul_bwp.pucch_cfg.setup().dl_data_to_ul_ack[1] = 7; + ul_config.init_ul_bwp.pucch_cfg.setup().dl_data_to_ul_ack[2] = 6; + ul_config.init_ul_bwp.pucch_cfg.setup().dl_data_to_ul_ack[3] = 5; + ul_config.init_ul_bwp.pucch_cfg.setup().dl_data_to_ul_ack[4] = 4; + + // PUCCH resources (only one format1 for the moment) + ul_config.init_ul_bwp.pucch_cfg.setup().res_to_add_mod_list_present = true; + ul_config.init_ul_bwp.pucch_cfg.setup().res_to_add_mod_list.resize(1); + auto& pucch_res1 = ul_config.init_ul_bwp.pucch_cfg.setup().res_to_add_mod_list[0]; + pucch_res1.pucch_res_id = 0; + pucch_res1.start_prb = 0; + pucch_res1.format.set_format1(); + pucch_res1.format.format1().init_cyclic_shift = 0; + pucch_res1.format.format1().nrof_symbols = 14; + pucch_res1.format.format1().start_symbol_idx = 0; + pucch_res1.format.format1().time_domain_occ = 0; + + // PUSCH config + ul_config.init_ul_bwp.pusch_cfg_present = true; + ul_config.init_ul_bwp.pusch_cfg.set_setup(); + auto& pusch_cfg_ded = ul_config.init_ul_bwp.pusch_cfg.setup(); + pusch_cfg_ded.dmrs_ul_for_pusch_map_type_a_present = true; + pusch_cfg_ded.dmrs_ul_for_pusch_map_type_a.set_setup(); + pusch_cfg_ded.dmrs_ul_for_pusch_map_type_a.setup().dmrs_add_position_present = true; + pusch_cfg_ded.dmrs_ul_for_pusch_map_type_a.setup().dmrs_add_position = dmrs_ul_cfg_s::dmrs_add_position_opts::pos1; + // PUSH power control skipped + pusch_cfg_ded.res_alloc = pusch_cfg_s::res_alloc_opts::res_alloc_type1; + + // UCI + pusch_cfg_ded.uci_on_pusch_present = true; + pusch_cfg_ded.uci_on_pusch.set_setup(); + pusch_cfg_ded.uci_on_pusch.setup().beta_offsets_present = true; + pusch_cfg_ded.uci_on_pusch.setup().beta_offsets.set_semi_static(); + auto& beta_offset_semi_static = pusch_cfg_ded.uci_on_pusch.setup().beta_offsets.semi_static(); + beta_offset_semi_static.beta_offset_ack_idx1_present = true; + beta_offset_semi_static.beta_offset_ack_idx1 = 9; + beta_offset_semi_static.beta_offset_ack_idx2_present = true; + beta_offset_semi_static.beta_offset_ack_idx2 = 9; + beta_offset_semi_static.beta_offset_ack_idx3_present = true; + beta_offset_semi_static.beta_offset_ack_idx3 = 9; + beta_offset_semi_static.beta_offset_csi_part1_idx1_present = true; + beta_offset_semi_static.beta_offset_csi_part1_idx2_present = true; + beta_offset_semi_static.beta_offset_csi_part1_idx1 = 6; + beta_offset_semi_static.beta_offset_csi_part1_idx2 = 6; + beta_offset_semi_static.beta_offset_csi_part2_idx1_present = true; + beta_offset_semi_static.beta_offset_csi_part2_idx1 = 6; + beta_offset_semi_static.beta_offset_csi_part2_idx2_present = true; + beta_offset_semi_static.beta_offset_csi_part2_idx2 = 6; + pusch_cfg_ded.uci_on_pusch.setup().scaling = uci_on_pusch_s::scaling_opts::f1; + + ul_config.first_active_ul_bwp_id_present = true; + ul_config.first_active_ul_bwp_id = 0; + // Serving cell config (only to setup) cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.pdcch_serving_cell_cfg_present = true; cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.pdcch_serving_cell_cfg.set_setup(); cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.pdsch_serving_cell_cfg_present = true; cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.pdsch_serving_cell_cfg.set_setup(); + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.pdsch_serving_cell_cfg.setup().nrof_harq_processes_for_pdsch_present = + true; + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.pdsch_serving_cell_cfg.setup().nrof_harq_processes_for_pdsch = + pdsch_serving_cell_cfg_s::nrof_harq_processes_for_pdsch_opts::n16; cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg_present = true; cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.set_setup(); - cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync_present = true; + // nzp-CSI-RS Resource + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup().nzp_csi_rs_res_to_add_mod_list_present = true; + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup().nzp_csi_rs_res_to_add_mod_list.resize(1); + auto& nzp_csi_res = + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup().nzp_csi_rs_res_to_add_mod_list[0]; + nzp_csi_res.nzp_csi_rs_res_id = 0; + nzp_csi_res.res_map.freq_domain_alloc.set_row2(); + nzp_csi_res.res_map.freq_domain_alloc.row2().from_number(0b100000000000); + nzp_csi_res.res_map.nrof_ports = asn1::rrc_nr::csi_rs_res_map_s::nrof_ports_opts::p1; + nzp_csi_res.res_map.first_ofdm_symbol_in_time_domain = 4; + nzp_csi_res.res_map.cdm_type = asn1::rrc_nr::csi_rs_res_map_s::cdm_type_opts::no_cdm; + nzp_csi_res.res_map.density.set_one(); + nzp_csi_res.res_map.freq_band.start_rb = 0; + nzp_csi_res.res_map.freq_band.nrof_rbs = 52; + nzp_csi_res.pwr_ctrl_offset = 0; + // Skip pwr_ctrl_offset_ss_present + nzp_csi_res.scrambling_id = 0; + nzp_csi_res.periodicity_and_offset_present = true; + nzp_csi_res.periodicity_and_offset.set_slots80(); + nzp_csi_res.periodicity_and_offset.slots80() = 0; + // optional + nzp_csi_res.qcl_info_periodic_csi_rs_present = true; + nzp_csi_res.qcl_info_periodic_csi_rs = 0; + + // nzp-CSI-RS ResourceSet + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup().nzp_csi_rs_res_set_to_add_mod_list_present = + true; + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup().nzp_csi_rs_res_set_to_add_mod_list.resize(1); + auto& nzp_csi_res_set = + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup().nzp_csi_rs_res_set_to_add_mod_list[0]; + nzp_csi_res_set.nzp_csi_res_set_id = 0; + nzp_csi_res_set.nzp_csi_rs_res.resize(1); + nzp_csi_res_set.nzp_csi_rs_res[0] = 0; + // Skip TRS info + + // CSI report config + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup().csi_report_cfg_to_add_mod_list_present = true; + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup().csi_report_cfg_to_add_mod_list.resize(1); + auto& csi_report = + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup().csi_report_cfg_to_add_mod_list[0]; + csi_report.report_cfg_id = 0; + csi_report.res_for_ch_meas = 0; + csi_report.csi_im_res_for_interference_present = true; + csi_report.csi_im_res_for_interference = 1; + csi_report.report_cfg_type.set_periodic(); + csi_report.report_cfg_type.periodic().report_slot_cfg.set_slots80(); + csi_report.report_cfg_type.periodic().report_slot_cfg.slots80() = 8; + csi_report.report_cfg_type.periodic().pucch_csi_res_list.resize(1); + csi_report.report_cfg_type.periodic().pucch_csi_res_list[0].ul_bw_part_id = 0; + csi_report.report_cfg_type.periodic().pucch_csi_res_list[0].pucch_res = 17; + csi_report.report_quant.set_cri_ri_pmi_cqi(); + csi_report.report_freq_cfg_present = true; + csi_report.report_freq_cfg.cqi_format_ind_present = true; + csi_report.report_freq_cfg.cqi_format_ind = + asn1::rrc_nr::csi_report_cfg_s::report_freq_cfg_s_::cqi_format_ind_opts::wideband_cqi; + csi_report.time_restrict_for_ch_meass = asn1::rrc_nr::csi_report_cfg_s::time_restrict_for_ch_meass_opts::not_cfgured; + csi_report.time_restrict_for_interference_meass = + asn1::rrc_nr::csi_report_cfg_s::time_restrict_for_interference_meass_opts::not_cfgured; + csi_report.group_based_beam_report.set_disabled(); + csi_report.cqi_table = asn1::rrc_nr::csi_report_cfg_s::cqi_table_opts::table2; + csi_report.subband_size = asn1::rrc_nr::csi_report_cfg_s::subband_size_opts::value1; + + // Reconfig with Sync + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync_present = true; cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.new_ue_id = 17943; cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.smtc.release(); cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.t304 = recfg_with_sync_s::t304_opts::ms1000; - cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common_present = true; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common_present = true; cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ss_pbch_block_pwr = 0; cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dmrs_type_a_position = asn1::rrc_nr::serving_cell_cfg_common_s::dmrs_type_a_position_opts::pos2; - cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.pci_present = true; - cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.pci = 500; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.pci_present = true; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.pci = 500; cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ssb_subcarrier_spacing_present = true; cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ssb_subcarrier_spacing = subcarrier_spacing_opts::khz30; // DL config - cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common_present = true; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common_present = true; cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.freq_info_dl_present = true; cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.freq_info_dl .absolute_freq_ssb_present = true; @@ -600,7 +749,8 @@ int test_cell_group_config() .subcarrier_spacing = subcarrier_spacing_opts::khz15; // RACH config - cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.init_ul_bwp.rach_cfg_common_present=true; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.init_ul_bwp.rach_cfg_common_present = + true; auto& rach_cfg_common_pack = cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.init_ul_bwp.rach_cfg_common; @@ -714,7 +864,7 @@ int test_cell_group_config() packed_dcch.size(), json_writer3.to_string().c_str()); - srsran::write_rlc_am_sdu_nr(1, packed_dcch.data(), packed_dcch.size()); + srsran::write_pdcp_sdu_nr(1, packed_dcch.data(), packed_dcch.size()); #endif return SRSRAN_SUCCESS; diff --git a/lib/test/common/tti_point_test.cc b/lib/test/common/tti_point_test.cc index 9522da9d7..16162c0e5 100644 --- a/lib/test/common/tti_point_test.cc +++ b/lib/test/common/tti_point_test.cc @@ -19,6 +19,7 @@ * */ +#include "srsran/common/slot_point.h" #include "srsran/common/test_common.h" #include "srsran/common/tti_point.h" @@ -75,9 +76,53 @@ int test_tti_type() return SRSRAN_SUCCESS; } +void test_nr_slot_type() +{ + // TEST: constructors + srsran::slot_point slot1; + TESTASSERT(not slot1.valid()); + srsran::slot_point slot2{0, 1, 5}; + TESTASSERT(slot2.valid() and slot2.numerology_idx() == 0 and slot2.slot_idx() == 5 and slot2.slot_idx() == 5 and + slot2.sfn() == 1); + srsran::slot_point slot3{slot2}; + TESTASSERT(slot3 == slot2); + + // TEST: comparison and difference operators + slot1 = srsran::slot_point{0, 1, 5}; + slot2 = srsran::slot_point{0, 1, 5}; + TESTASSERT(slot1 == slot2 and slot1 <= slot2 and slot1 >= slot2); + slot1++; + TESTASSERT(slot1 != slot2 and slot1 >= slot2 and slot1 > slot2 and slot2 < slot1 and slot2 <= slot1); + TESTASSERT(slot1 - slot2 == 1 and slot2 - slot1 == -1); + slot1 = srsran::slot_point{0, 2, 5}; + TESTASSERT(slot1 != slot2 and slot1 >= slot2 and slot1 > slot2 and slot2 < slot1 and slot2 <= slot1); + TESTASSERT(slot1 - slot2 == 10 and slot2 - slot1 == -10); + slot1 = srsran::slot_point{0, 1023, 5}; + TESTASSERT(slot1 != slot2 and slot1 <= slot2 and slot1 < slot2 and slot2 > slot1 and slot2 >= slot1); + TESTASSERT(slot1 - slot2 == -20 and slot2 - slot1 == 20); + + // TEST: increment/decrement operators + slot1 = srsran::slot_point{0, 1, 5}; + slot2 = srsran::slot_point{0, 1, 5}; + TESTASSERT(slot1++ == slot2); + TESTASSERT(slot2 + 1 == slot1); + TESTASSERT(++slot2 == slot1); + slot1 = srsran::slot_point{0, 1, 5}; + slot2 = srsran::slot_point{0, 1, 5}; + TESTASSERT(slot1 - 100 == slot2 - 100); + TESTASSERT(((slot1 - 100000) + 100000) == slot1); + TESTASSERT((slot1 - 10240) == slot1); + TESTASSERT((slot1 - 100).slot_idx() == 5 and (slot1 - 100).sfn() == 1015); + TESTASSERT(((slot1 - 100) + 100) == slot1); + TESTASSERT(((slot1 - 1) + 1) == slot1); + + fmt::print("[ {}]", slot1); +} + int main() { srslog::init(); - TESTASSERT(test_tti_type() == SRSRAN_SUCCESS); + test_tti_type(); + test_nr_slot_type(); return 0; } diff --git a/lib/test/phy/CMakeLists.txt b/lib/test/phy/CMakeLists.txt index 1ded8db55..47b7c23c5 100644 --- a/lib/test/phy/CMakeLists.txt +++ b/lib/test/phy/CMakeLists.txt @@ -72,5 +72,5 @@ add_nr_test(phy_dl_nr_test_rvd phy_dl_nr_test -P 52 -p 52 -m 0 -R 0 52 1 100100100100 00000010000000) add_nr_test(phy_dl_nr_test_cfo_delay phy_dl_nr_test -P 52 -p 52 -m 27 -C 100.0 -D 4 -n 10) -add_nr_test(phy_dl_nr_test_52prb phy_dl_nr_test -P 52 -p 52 -m 27 -T 256qam -v -d 1 1 -n 10) -add_nr_test(phy_dl_nr_test_270prb phy_dl_nr_test -P 270 -p 270 -m 27 -T 256qam -v -d 1 1 -n 10) +add_nr_test(phy_dl_nr_test_52prb phy_dl_nr_test -P 52 -p 52 -m 27 -T 256qam -d 1 1 -n 10) +add_nr_test(phy_dl_nr_test_270prb phy_dl_nr_test -P 270 -p 270 -m 27 -T 256qam -d 1 1 -n 10) diff --git a/lib/test/phy/phy_dl_nr_test.c b/lib/test/phy/phy_dl_nr_test.c index c9252bfd3..3b532c668 100644 --- a/lib/test/phy/phy_dl_nr_test.c +++ b/lib/test/phy/phy_dl_nr_test.c @@ -396,10 +396,7 @@ int main(int argc, char** argv) if (data_tx[tb] == NULL) { continue; } - - for (uint32_t i = 0; i < pdsch_cfg.grant.tb[tb].tbs; i++) { - data_tx[tb][i] = (uint8_t)srsran_random_uniform_int_dist(rand_gen, 0, UINT8_MAX); - } + srsran_random_byte_vector(rand_gen, data_tx[tb], pdsch_cfg.grant.tb[tb].tbs/8); pdsch_cfg.grant.tb[tb].softbuffer.tx = &softbuffer_tx; } diff --git a/lib/test/rlc/rlc_am_test.cc b/lib/test/rlc/rlc_am_test.cc index 842d946e7..d9c7d334a 100644 --- a/lib/test/rlc/rlc_am_test.cc +++ b/lib/test/rlc/rlc_am_test.cc @@ -3588,7 +3588,7 @@ bool reestablish_test() } } - TESTASSERT(tester.sdus.size() == 17); + TESTASSERT(tester.sdus.size() == 18); srslog::fetch_basic_logger("TEST").info("Received %zd SDUs", tester.sdus.size()); diff --git a/srsenb/hdr/phy/lte/sf_worker.h b/srsenb/hdr/phy/lte/sf_worker.h index a3bfb14be..2ffa4601e 100644 --- a/srsenb/hdr/phy/lte/sf_worker.h +++ b/srsenb/hdr/phy/lte/sf_worker.h @@ -41,7 +41,7 @@ public: void init(phy_common* phy); cf_t* get_buffer_rx(uint32_t cc_idx, uint32_t antenna_idx); - void set_time(uint32_t tti_, const srsran::rf_timestamp_t& tx_time_); + void set_context(const srsran::phy_common_interface::worker_context_t& w_ctx); int add_rnti(uint16_t rnti, uint32_t cc_idx); void rem_rnti(uint16_t rnti); @@ -68,10 +68,9 @@ private: bool running = false; std::mutex work_mutex; - uint32_t tti_rx = 0, tti_tx_dl = 0, tti_tx_ul = 0; - srsran::rf_timestamp_t tx_time = {}; - - std::vector > cc_workers; + uint32_t tti_rx = 0, tti_tx_dl = 0, tti_tx_ul = 0; + std::vector > cc_workers; + srsran::phy_common_interface::worker_context_t context = {}; srsran_softbuffer_tx_t temp_mbsfn_softbuffer = {}; }; diff --git a/srsenb/hdr/phy/nr/slot_worker.h b/srsenb/hdr/phy/nr/slot_worker.h index f416fd76c..76b00cbda 100644 --- a/srsenb/hdr/phy/nr/slot_worker.h +++ b/srsenb/hdr/phy/nr/slot_worker.h @@ -45,6 +45,7 @@ public: uint32_t nof_max_prb = SRSRAN_MAX_PRB_NR; uint32_t nof_tx_ports = 1; uint32_t nof_rx_ports = 1; + uint32_t rf_port = 0; uint32_t pusch_max_nof_iter = 10; }; @@ -59,7 +60,7 @@ public: cf_t* get_buffer_rx(uint32_t antenna_idx); cf_t* get_buffer_tx(uint32_t antenna_idx); uint32_t get_buffer_len(); - void set_time(const uint32_t& tti, const srsran::rf_timestamp_t& timestamp); + void set_context(const srsran::phy_common_interface::worker_context_t& w_ctx); private: /** @@ -83,16 +84,18 @@ private: stack_interface_phy_nr& stack; srslog::basic_logger& logger; - uint32_t sf_len = 0; - uint32_t cell_index = 0; - srsran_slot_cfg_t dl_slot_cfg = {}; - srsran_slot_cfg_t ul_slot_cfg = {}; - srsran_pdcch_cfg_nr_t pdcch_cfg = {}; - srsran::rf_timestamp_t tx_time = {}; - srsran_gnb_dl_t gnb_dl = {}; - srsran_gnb_ul_t gnb_ul = {}; - std::vector tx_buffer; ///< Baseband transmit buffers - std::vector rx_buffer; ///< Baseband receive buffers + uint32_t sf_len = 0; + uint32_t cell_index = 0; + uint32_t rf_port = 0; + srsran_slot_cfg_t dl_slot_cfg = {}; + srsran_slot_cfg_t ul_slot_cfg = {}; + srsran::phy_common_interface::worker_context_t context = {}; + srsran_pdcch_cfg_nr_t pdcch_cfg = {}; + srsran_gnb_dl_t gnb_dl = {}; + srsran_gnb_ul_t gnb_ul = {}; + std::vector tx_buffer; ///< Baseband transmit buffers + std::vector rx_buffer; ///< Baseband receive buffers + std::mutex mutex; ///< Protect concurrent access from workers (and main process that inits the class) }; } // namespace nr diff --git a/srsenb/hdr/phy/nr/worker_pool.h b/srsenb/hdr/phy/nr/worker_pool.h index d3fb0edf8..2f3ec3f4b 100644 --- a/srsenb/hdr/phy/nr/worker_pool.h +++ b/srsenb/hdr/phy/nr/worker_pool.h @@ -50,6 +50,7 @@ private: void rach_detected(uint32_t tti, uint32_t primary_cc_idx, uint32_t preamble_idx, uint32_t time_adv) override { stack_interface_phy_nr::rach_info_t rach_info = {}; + rach_info.slot_index = tti; rach_info.preamble = preamble_idx; rach_info.time_adv = time_adv; @@ -91,6 +92,7 @@ private: uint32_t current_tti = 0; ///< Current TTI, read and write from same thread srslog::basic_logger& logger; prach_stack_adaptor_t prach_stack_adaptor; + uint32_t nof_prach_workers = 0; // Current configuration std::mutex common_cfg_mutex; @@ -100,6 +102,7 @@ private: public: struct args_t { uint32_t nof_phy_threads = 3; + uint32_t nof_prach_workers = 0; uint32_t prio = 52; uint32_t pusch_max_nof_iter = 10; srsran::phy_log_args_t log = {}; diff --git a/srsenb/hdr/phy/phy.h b/srsenb/hdr/phy/phy.h index f6d325f92..a1d84dea9 100644 --- a/srsenb/hdr/phy/phy.h +++ b/srsenb/hdr/phy/phy.h @@ -34,7 +34,10 @@ namespace srsenb { -class phy final : public enb_phy_base, public phy_interface_stack_lte, public srsran::phy_interface_radio +class phy final : public enb_phy_base, + public phy_interface_stack_lte, + public phy_interface_stack_nr, + public srsran::phy_interface_radio { public: phy(srslog::sink& log_sink); @@ -44,6 +47,11 @@ public: const phy_cfg_t& cfg, srsran::radio_interface_phy* radio_, stack_interface_phy_lte* stack_); + int init(const phy_args_t& args, + const phy_cfg_t& cfg, + srsran::radio_interface_phy* radio_, + stack_interface_phy_lte* stack_lte_, + stack_interface_phy_nr& stack_nr_); void stop() override; std::string get_type() override { return "lte"; }; @@ -70,6 +78,9 @@ public: void srsran_phy_logger(phy_logger_level_t log_level, char* str); + int init_nr(const phy_args_t& args, const phy_cfg_t& cfg, stack_interface_phy_nr& stack); + int set_common_cfg(const common_cfg_t& common_cfg) override; + private: srsran::phy_cfg_mbsfn_t mbsfn_config = {}; uint32_t nof_workers = 0; diff --git a/srsenb/hdr/phy/phy_common.h b/srsenb/hdr/phy/phy_common.h index feebbdbb0..c24144d92 100644 --- a/srsenb/hdr/phy/phy_common.h +++ b/srsenb/hdr/phy/phy_common.h @@ -66,11 +66,7 @@ public: * @param tx_time timestamp to transmit samples * @param is_nr flag is true if it is called from NR */ - void worker_end(void* tx_sem_id, - bool tx_enable, - srsran::rf_buffer_t& buffer, - srsran::rf_timestamp_t& tx_time, - bool is_nr) override; + void worker_end(const worker_context_t& w_ctx, const bool& tx_enable, srsran::rf_buffer_t& buffer) override; // Common objects phy_args_t params = {}; @@ -84,6 +80,9 @@ public: if (cc_idx < cell_list_lte.size()) { ret = cell_list_lte[cc_idx].cell.nof_prb; + } else if (cc_idx == 1 && !cell_list_nr.empty()) { + // for basic NSA config return width of first NR carrier + ret = cell_list_nr[0].carrier.nof_prb; } return ret; @@ -94,6 +93,9 @@ public: if (cc_idx < cell_list_lte.size()) { ret = cell_list_lte[cc_idx].cell.nof_ports; + } else if (cc_idx == 1 && !cell_list_nr.empty()) { + // one RF port for basic NSA config + ret = 1; } return ret; @@ -245,10 +247,9 @@ private: uint8_t mch_table[40] = {}; uint8_t mcch_table[10] = {}; uint32_t mch_period_stop = 0; + srsran::rf_buffer_t tx_buffer = {}; bool is_mch_subframe(srsran_mbsfn_cfg_t* cfg, uint32_t phy_tti); bool is_mcch_subframe(srsran_mbsfn_cfg_t* cfg, uint32_t phy_tti); - srsran::rf_buffer_t nr_tx_buffer; - bool nr_tx_buffer_ready = false; }; } // namespace srsenb diff --git a/srsenb/hdr/phy/phy_ue_db.h b/srsenb/hdr/phy/phy_ue_db.h index bae1f9942..9d77d2a11 100644 --- a/srsenb/hdr/phy/phy_ue_db.h +++ b/srsenb/hdr/phy/phy_ue_db.h @@ -392,6 +392,15 @@ public: const srsran_uci_cfg_t& uci_cfg, const srsran_uci_value_t& uci_value); + static void send_cqi_data(uint32_t tti, + uint16_t rnti, + uint32_t cqi_cc_idx, + const srsran_cqi_cfg_t& cqi_cfg, + const srsran_cqi_value_t& cqi_value, + const srsran_cqi_report_cfg_t& cqi_report_cfg, + const srsran_cell_t& cell, + stack_interface_phy_lte* stack); + /** * Set the latest UL Transport Block resource allocation for a given RNTI, eNb cell/carrier and UL HARQ process * identifier. diff --git a/srsenb/hdr/phy/txrx.h b/srsenb/hdr/phy/txrx.h index b44dab137..8fd1ebab2 100644 --- a/srsenb/hdr/phy/txrx.h +++ b/srsenb/hdr/phy/txrx.h @@ -40,10 +40,10 @@ public: bool init(stack_interface_phy_lte* stack_, srsran::radio_interface_phy* radio_handler, lte::worker_pool* lte_workers_, - nr::worker_pool* nr_workers_, phy_common* worker_com, prach_worker_pool* prach_, uint32_t prio); + bool set_nr_workers(nr::worker_pool* nr_workers_); void stop(); private: diff --git a/srsenb/hdr/stack/enb_stack_lte.h b/srsenb/hdr/stack/enb_stack_lte.h index 058787576..e95970955 100644 --- a/srsenb/hdr/stack/enb_stack_lte.h +++ b/srsenb/hdr/stack/enb_stack_lte.h @@ -44,13 +44,20 @@ namespace srsenb { -class enb_stack_lte final : public enb_stack_base, public stack_interface_phy_lte, public srsran::thread +class enb_stack_lte final : public enb_stack_base, + public stack_interface_phy_lte, + public stack_interface_phy_nr, + public srsran::thread { public: enb_stack_lte(srslog::sink& log_sink); ~enb_stack_lte() final; // eNB stack base interface + int init(const stack_args_t& args_, + const rrc_cfg_t& rrc_cfg_, + phy_interface_stack_lte* phy_, + phy_interface_stack_nr* phy_nr_); int init(const stack_args_t& args_, const rrc_cfg_t& rrc_cfg_, phy_interface_stack_lte* phy_); int init(const stack_args_t& args_, const rrc_cfg_t& rrc_cfg_); void stop() final; @@ -114,6 +121,26 @@ public: void toggle_padding() override { mac.toggle_padding(); } void tti_clock() override; + // mac_interface_phy_nr + int slot_indication(const srsran_slot_cfg_t& slot_cfg) override { return mac_nr.slot_indication(slot_cfg); } + int get_dl_sched(const srsran_slot_cfg_t& slot_cfg, srsenb::mac_interface_phy_nr::dl_sched_t& dl_sched) override + { + return mac_nr.get_dl_sched(slot_cfg, dl_sched); + } + int get_ul_sched(const srsran_slot_cfg_t& slot_cfg, srsenb::mac_interface_phy_nr::ul_sched_t& ul_sched) override + { + return mac_nr.get_ul_sched(slot_cfg, ul_sched); + } + int pucch_info(const srsran_slot_cfg_t& slot_cfg, const pucch_info_t& pucch_info) override + { + return mac_nr.pucch_info(slot_cfg, pucch_info); + } + int pusch_info(const srsran_slot_cfg_t& slot_cfg, pusch_info_t& pusch_info) override + { + return mac_nr.pusch_info(slot_cfg, pusch_info); + } + void rach_detected(const rach_info_t& rach_info) override { mac_nr.rach_detected(rach_info); } + private: static const int STACK_MAIN_THREAD_PRIO = 4; // thread loop @@ -163,6 +190,7 @@ private: // RAT-specific interfaces phy_interface_stack_lte* phy = nullptr; + phy_interface_stack_nr* phy_nr = nullptr; // state std::atomic started{false}; diff --git a/srsenb/hdr/stack/gnb_stack_nr.h b/srsenb/hdr/stack/gnb_stack_nr.h index 139570a08..32c689043 100644 --- a/srsenb/hdr/stack/gnb_stack_nr.h +++ b/srsenb/hdr/stack/gnb_stack_nr.h @@ -67,9 +67,6 @@ public: bool is_registered() override { return true; }; bool start_service_request() override { return true; }; - // PHY->MAC interface - int rx_data_indication(rx_data_ind_t& grant) override; - // Temporary GW interface void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu) override; bool has_active_radio_bearer(uint32_t eps_bearer_id) override; @@ -85,7 +82,7 @@ public: int get_dl_sched(const srsran_slot_cfg_t& slot_cfg, dl_sched_t& dl_sched) override; int get_ul_sched(const srsran_slot_cfg_t& slot_cfg, ul_sched_t& ul_sched) override; int pucch_info(const srsran_slot_cfg_t& slot_cfg, const pucch_info_t& pucch_info) override; - int pusch_info(const srsran_slot_cfg_t& slot_cfg, const pusch_info_t& pusch_info) override; + int pusch_info(const srsran_slot_cfg_t& slot_cfg, pusch_info_t& pusch_info) override; void rach_detected(const rach_info_t& rach_info) override; private: diff --git a/srsenb/hdr/stack/mac/mac_nr.h b/srsenb/hdr/stack/mac/mac_nr.h index bc8ecac28..8fef487ab 100644 --- a/srsenb/hdr/stack/mac/mac_nr.h +++ b/srsenb/hdr/stack/mac/mac_nr.h @@ -27,6 +27,7 @@ #include "srsenb/hdr/common/rnti_pool.h" #include "srsenb/hdr/stack/enb_stack_base.h" +#include "srsenb/hdr/stack/mac/nr/sched_nr.h" #include "srsenb/hdr/stack/mac/nr/ue_nr.h" #include "srsran/common/task_scheduler.h" #include "srsran/interfaces/enb_metrics_interface.h" @@ -36,6 +37,9 @@ namespace srsenb { struct mac_nr_args_t { + srsran::phy_cfg_nr_t phy_base_cfg = {}; + int fixed_dl_mcs = -1; + int fixed_ul_mcs = -1; srsenb::pcap_args_t pcap; }; @@ -64,16 +68,12 @@ public: int rlc_buffer_state(uint16_t rnti, uint32_t lc_id, uint32_t tx_queue, uint32_t retx_queue) override { return 0; } // Interface for PHY - int sf_indication(const uint32_t tti); - int rx_data_indication(stack_interface_phy_nr::rx_data_ind_t& grant); - void process_pdus(); - void rach_detected(const srsran_slot_cfg_t& slot_cfg, uint32_t enb_cc_idx, uint32_t preamble_idx, uint32_t time_adv); int slot_indication(const srsran_slot_cfg_t& slot_cfg) override; int get_dl_sched(const srsran_slot_cfg_t& slot_cfg, dl_sched_t& dl_sched) override; int get_ul_sched(const srsran_slot_cfg_t& slot_cfg, ul_sched_t& ul_sched) override; int pucch_info(const srsran_slot_cfg_t& slot_cfg, const pucch_info_t& pucch_info) override; - int pusch_info(const srsran_slot_cfg_t& slot_cfg, const pusch_info_t& pusch_info) override; + int pusch_info(const srsran_slot_cfg_t& slot_cfg, pusch_info_t& pusch_info) override; void rach_detected(const rach_info_t& rach_info) override; private: @@ -84,6 +84,9 @@ private: bool is_rnti_valid_unsafe(uint16_t rnti); bool is_rnti_active_unsafe(uint16_t rnti); + // handle UCI data from either PUCCH or PUSCH + bool handle_uci_data(const uint16_t rnti, const srsran_uci_cfg_nr_t& cfg, const srsran_uci_value_nr_t& value); + // PDU processing int handle_pdu(srsran::unique_byte_buffer_t pdu); @@ -103,6 +106,9 @@ private: std::atomic started = {false}; + const static uint32_t NUMEROLOGY_IDX = 0; /// only 15kHz supported at this stage + srsran::slot_point pdsch_slot, pusch_slot; + srsenb::sched_nr sched; srsenb::sched_interface::cell_cfg_t cfg = {}; // Map of active UEs diff --git a/srsenb/hdr/stack/mac/nr/sched_nr.h b/srsenb/hdr/stack/mac/nr/sched_nr.h index 25b766680..36ba6ec13 100644 --- a/srsenb/hdr/stack/mac/nr/sched_nr.h +++ b/srsenb/hdr/stack/mac/nr/sched_nr.h @@ -26,7 +26,7 @@ #include "sched_nr_interface.h" #include "sched_nr_ue.h" #include "srsran/adt/pool/cached_alloc.h" -#include "srsran/common/tti_point.h" +#include "srsran/common/slot_point.h" #include extern "C" { #include "srsran/config.h" @@ -50,15 +50,17 @@ public: int cell_cfg(srsran::const_span cell_list) override; void ue_cfg(uint16_t rnti, const ue_cfg_t& cfg) override; + int dl_rach_info(uint32_t cc, const dl_sched_rar_info_t& rar_info); + void dl_ack_info(uint16_t rnti, uint32_t cc, uint32_t pid, uint32_t tb_idx, bool ack) override; void ul_crc_info(uint16_t rnti, uint32_t cc, uint32_t pid, bool crc) override; - void ul_sr_info(tti_point tti_rx, uint16_t rnti) override; + void ul_sr_info(slot_point slot_rx, uint16_t rnti) override; - int get_dl_sched(tti_point pdsch_tti, uint32_t cc, dl_sched_t& result) override; - int get_ul_sched(tti_point pusch_tti, uint32_t cc, ul_sched_t& result) override; + int get_dl_sched(slot_point pdsch_tti, uint32_t cc, dl_sched_t& result) override; + int get_ul_sched(slot_point pusch_tti, uint32_t cc, ul_sched_t& result) override; private: - int generate_slot_result(tti_point pdcch_tti, uint32_t cc); + int generate_slot_result(slot_point pdcch_tti, uint32_t cc); void ue_cfg_impl(uint16_t rnti, const ue_cfg_t& cfg); // args diff --git a/srsenb/hdr/stack/mac/nr/sched_nr_cell.h b/srsenb/hdr/stack/mac/nr/sched_nr_cell.h index eb95ba789..2b967c16b 100644 --- a/srsenb/hdr/stack/mac/nr/sched_nr_cell.h +++ b/srsenb/hdr/stack/mac/nr/sched_nr_cell.h @@ -33,7 +33,7 @@ using dl_sched_rar_info_t = sched_nr_interface::dl_sched_rar_info_t; struct pending_rar_t { uint16_t ra_rnti = 0; - tti_point prach_tti; + slot_point prach_slot; srsran::bounded_vector msg3_grant; }; diff --git a/srsenb/hdr/stack/mac/nr/sched_nr_cfg.h b/srsenb/hdr/stack/mac/nr/sched_nr_cfg.h index 864892894..afc6b473a 100644 --- a/srsenb/hdr/stack/mac/nr/sched_nr_cfg.h +++ b/srsenb/hdr/stack/mac/nr/sched_nr_cfg.h @@ -45,6 +45,15 @@ using sched_cfg_t = sched_nr_interface::sched_cfg_t; using cell_cfg_t = sched_nr_interface::cell_cfg_t; using bwp_cfg_t = sched_nr_interface::bwp_cfg_t; +using pdcch_cce_pos_list = srsran::bounded_vector; +using bwp_cce_pos_list = std::array, SRSRAN_NOF_SF_X_FRAME>; +void get_dci_locs(const srsran_coreset_t& coreset, + const srsran_search_space_t& search_space, + uint16_t rnti, + bwp_cce_pos_list& cce_locs); + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + struct bwp_params { const uint32_t bwp_id; const uint32_t cc; @@ -65,6 +74,8 @@ struct bwp_params { std::vector pusch_ra_list; bwp_params(const cell_cfg_t& cell, const sched_cfg_t& sched_cfg_, uint32_t cc, uint32_t bwp_id); + + bwp_cce_pos_list rar_cce_list; }; struct sched_cell_params { @@ -88,17 +99,9 @@ struct sched_params { /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// using prb_bitmap = srsran::bounded_bitset; -using rbgmask_t = srsran::bounded_bitset; using pdcchmask_t = srsran::bounded_bitset; -using pdcch_cce_pos_list = srsran::bounded_vector; -using bwp_cce_pos_list = std::array, SRSRAN_NOF_SF_X_FRAME>; -void get_dci_locs(const srsran_coreset_t& coreset, - const srsran_search_space_t& search_space, - uint16_t rnti, - bwp_cce_pos_list& cce_locs); - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// using ue_cfg_t = sched_nr_interface::ue_cfg_t; diff --git a/srsenb/hdr/stack/mac/nr/sched_nr_harq.h b/srsenb/hdr/stack/mac/nr/sched_nr_harq.h index aeb7eccc8..33eef9f8c 100644 --- a/srsenb/hdr/stack/mac/nr/sched_nr_harq.h +++ b/srsenb/hdr/stack/mac/nr/sched_nr_harq.h @@ -24,7 +24,7 @@ #include "sched_nr_cfg.h" #include "srsenb/hdr/stack/mac/nr/harq_softbuffer.h" -#include "srsran/common/tti_point.h" +#include "srsran/common/slot_point.h" #include namespace srsenb { @@ -40,29 +40,37 @@ public: return std::all_of(tb.begin(), tb.end(), [](const tb_t& t) { return not t.active; }); } bool empty(uint32_t tb_idx) const { return not tb[tb_idx].active; } - bool has_pending_retx(tti_point tti_rx) const { return not empty() and not tb[0].ack_state and tti_ack <= tti_rx; } + bool has_pending_retx(slot_point slot_rx) const + { + return not empty() and not tb[0].ack_state and slot_ack <= slot_rx; + } uint32_t nof_retx() const { return tb[0].n_rtx; } uint32_t max_nof_retx() const { return max_retx; } uint32_t tbs() const { return tb[0].tbs; } uint32_t ndi() const { return tb[0].ndi; } uint32_t mcs() const { return tb[0].mcs; } const prb_grant& prbs() const { return prbs_; } - tti_point harq_tti_ack() const { return tti_ack; } + slot_point harq_slot_ack() const { return slot_ack; } bool ack_info(uint32_t tb_idx, bool ack); - void new_tti(tti_point tti_rx); + void new_slot(slot_point slot_rx); void reset(); - bool - new_tx(tti_point tti_tx, tti_point tti_ack, const prb_grant& grant, uint32_t mcs, uint32_t tbs, uint32_t max_retx); - bool new_retx(tti_point tti_tx, tti_point tti_ack, const prb_grant& grant); - bool new_retx(tti_point tti_tx, tti_point tti_ack); + bool new_tx(slot_point slot_tx, + slot_point slot_ack, + const prb_grant& grant, + uint32_t mcs, + uint32_t tbs, + 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); + + const uint32_t pid; +protected: // NOTE: Has to be used before first tx is dispatched bool set_tbs(uint32_t tbs); - const uint32_t pid; - private: struct tb_t { bool active = false; @@ -74,8 +82,8 @@ private: }; uint32_t max_retx = 1; - tti_point tti_tx; - tti_point tti_ack; + slot_point slot_tx; + slot_point slot_ack; prb_grant prbs_; std::array tb; }; @@ -83,20 +91,22 @@ private: class dl_harq_proc : public harq_proc { public: - dl_harq_proc(uint32_t id_, uint32_t nprb) : - harq_proc(id_), softbuffer(harq_softbuffer_pool::get_instance().get_tx(nprb)) - {} + dl_harq_proc(uint32_t id_, uint32_t nprb); tx_harq_softbuffer& get_softbuffer() { return *softbuffer; } + srsran::unique_byte_buffer_t* get_tx_pdu() { return &pdu; } + // clear and reset softbuffer and PDU for new tx bool set_tbs(uint32_t tbs) { softbuffer->reset(); + pdu->clear(); return harq_proc::set_tbs(tbs); } private: srsran::unique_pool_ptr softbuffer; + srsran::unique_byte_buffer_t pdu; }; class ul_harq_proc : public harq_proc @@ -122,18 +132,18 @@ class harq_entity { public: explicit harq_entity(uint32_t nprb, uint32_t nof_harq_procs = SCHED_NR_MAX_HARQ); - void new_tti(tti_point tti_rx_); + void new_slot(slot_point slot_rx_); void dl_ack_info(uint32_t pid, uint32_t tb_idx, bool ack) { dl_harqs[pid].ack_info(tb_idx, ack); } void ul_crc_info(uint32_t pid, bool ack) { ul_harqs[pid].ack_info(0, ack); } dl_harq_proc* find_pending_dl_retx() { - return find_dl([this](const dl_harq_proc& h) { return h.has_pending_retx(tti_rx); }); + return find_dl([this](const dl_harq_proc& h) { return h.has_pending_retx(slot_rx); }); } ul_harq_proc* find_pending_ul_retx() { - return find_ul([this](const ul_harq_proc& h) { return h.has_pending_retx(tti_rx); }); + return find_ul([this](const ul_harq_proc& h) { return h.has_pending_retx(slot_rx); }); } dl_harq_proc* find_empty_dl_harq() { @@ -158,7 +168,7 @@ private: return (it == ul_harqs.end()) ? nullptr : &(*it); } - tti_point tti_rx; + slot_point slot_rx; std::vector dl_harqs; std::vector ul_harqs; }; diff --git a/srsenb/hdr/stack/mac/nr/sched_nr_helpers.h b/srsenb/hdr/stack/mac/nr/sched_nr_helpers.h index a03060fa1..1dec88c18 100644 --- a/srsenb/hdr/stack/mac/nr/sched_nr_helpers.h +++ b/srsenb/hdr/stack/mac/nr/sched_nr_helpers.h @@ -27,11 +27,13 @@ namespace srsenb { namespace sched_nr_impl { +class slot_ue; + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool fill_dci_rar(prb_interval interv, const bwp_params& bwp_cfg, srsran_dci_dl_nr_t& dci); +bool fill_dci_rar(prb_interval interv, uint16_t ra_rnti, const bwp_params& bwp_cfg, srsran_dci_dl_nr_t& dci); -class slot_ue; +bool fill_dci_msg3(const slot_ue& ue, const bwp_params& bwp_cfg, srsran_dci_ul_nr_t& dci); /// Generate PDCCH DL DCI fields void fill_dl_dci_ue_fields(const slot_ue& ue, diff --git a/srsenb/hdr/stack/mac/nr/sched_nr_interface.h b/srsenb/hdr/stack/mac/nr/sched_nr_interface.h index 1a4f6f864..c3209ce01 100644 --- a/srsenb/hdr/stack/mac/nr/sched_nr_interface.h +++ b/srsenb/hdr/stack/mac/nr/sched_nr_interface.h @@ -27,7 +27,7 @@ #include "srsran/adt/optional.h" #include "srsran/adt/span.h" #include "srsran/common/phy_cfg_nr.h" -#include "srsran/common/tti_point.h" +#include "srsran/common/slot_point.h" #include "srsran/interfaces/gnb_interfaces.h" #include "srsran/phy/phch/dci_nr.h" @@ -72,7 +72,10 @@ public: srsran::bounded_vector bwps{1}; // idx0 for BWP-common }; - struct sched_cfg_t {}; + struct sched_cfg_t { + bool pdsch_enabled = true; + bool pusch_enabled = true; + }; struct ue_cc_cfg_t { bool active = false; @@ -90,11 +93,13 @@ public: ////// RACH ////// struct dl_sched_rar_info_t { - uint32_t preamble_idx; - uint32_t ta_cmd; - uint16_t temp_crnti; - uint32_t msg3_size; - uint32_t prach_tti; + uint32_t preamble_idx; + uint32_t ofdm_symbol_idx; + uint32_t freq_idx; + uint32_t ta_cmd; + uint16_t temp_crnti; + uint32_t msg3_size; + slot_point prach_slot; }; ///// Sched Result ///// @@ -105,12 +110,12 @@ public: virtual ~sched_nr_interface() = default; virtual int cell_cfg(srsran::const_span ue_cfg) = 0; virtual void ue_cfg(uint16_t rnti, const ue_cfg_t& ue_cfg) = 0; - virtual int get_dl_sched(tti_point tti_rx, uint32_t cc, dl_sched_t& result) = 0; - virtual int get_ul_sched(tti_point tti_rx, uint32_t cc, ul_sched_t& result) = 0; + virtual int get_dl_sched(slot_point slot_rx, uint32_t cc, dl_sched_t& result) = 0; + virtual int get_ul_sched(slot_point slot_rx, uint32_t cc, ul_sched_t& result) = 0; virtual void dl_ack_info(uint16_t rnti, uint32_t cc, uint32_t pid, uint32_t tb_idx, bool ack) = 0; virtual void ul_crc_info(uint16_t rnti, uint32_t cc, uint32_t pid, bool crc) = 0; - virtual void ul_sr_info(tti_point, uint16_t rnti) = 0; + virtual void ul_sr_info(slot_point, uint16_t rnti) = 0; }; } // namespace srsenb diff --git a/srsenb/hdr/stack/mac/nr/sched_nr_pdcch.h b/srsenb/hdr/stack/mac/nr/sched_nr_pdcch.h index d9871a50a..4c021b079 100644 --- a/srsenb/hdr/stack/mac/nr/sched_nr_pdcch.h +++ b/srsenb/hdr/stack/mac/nr/sched_nr_pdcch.h @@ -75,6 +75,7 @@ private: uint32_t coreset_id; uint32_t slot_idx; uint32_t nof_freq_res = 0; + const bwp_cce_pos_list& rar_cce_list; // List of PDCCH grants struct alloc_record { diff --git a/srsenb/hdr/stack/mac/nr/sched_nr_rb.h b/srsenb/hdr/stack/mac/nr/sched_nr_rb.h index 4f4a143e9..633b0935e 100644 --- a/srsenb/hdr/stack/mac/nr/sched_nr_rb.h +++ b/srsenb/hdr/stack/mac/nr/sched_nr_rb.h @@ -68,7 +68,7 @@ struct prb_grant { alloc.rbgs.~rbg_bitmap(); new (&alloc.interv) prb_interval(prbs); } else { - alloc.interv = alloc.interv; + alloc.interv = prbs; } return *this; } diff --git a/srsenb/hdr/stack/mac/nr/sched_nr_rb_grid.h b/srsenb/hdr/stack/mac/nr/sched_nr_rb_grid.h index f75a6a036..f2347e092 100644 --- a/srsenb/hdr/stack/mac/nr/sched_nr_rb_grid.h +++ b/srsenb/hdr/stack/mac/nr/sched_nr_rb_grid.h @@ -70,8 +70,8 @@ struct bwp_slot_grid { struct bwp_res_grid { bwp_res_grid(const bwp_params& bwp_cfg_); - bwp_slot_grid& operator[](tti_point tti) { return slots[tti.to_uint() % slots.capacity()]; }; - const bwp_slot_grid& operator[](tti_point tti) const { return slots[tti.to_uint() % slots.capacity()]; }; + bwp_slot_grid& operator[](slot_point tti) { return slots[tti.to_uint() % slots.capacity()]; }; + const bwp_slot_grid& operator[](slot_point tti) const { return slots[tti.to_uint() % slots.capacity()]; }; uint32_t id() const { return cfg->bwp_id; } uint32_t nof_prbs() const { return cfg->cfg.rb_width; } @@ -90,7 +90,7 @@ class bwp_slot_allocator public: explicit bwp_slot_allocator(bwp_res_grid& bwp_grid_); - void new_slot(tti_point pdcch_tti_) { pdcch_tti = pdcch_tti_; } + void new_slot(slot_point pdcch_slot_) { pdcch_slot = pdcch_slot_; } alloc_result alloc_rar_and_msg3(uint32_t aggr_idx, const pending_rar_t& rar, @@ -98,9 +98,9 @@ public: slot_ue_map_t& ues, uint32_t max_nof_grants); alloc_result alloc_pdsch(slot_ue& ue, const prb_grant& dl_grant); - alloc_result alloc_pusch(slot_ue& ue, const rbgmask_t& dl_mask); + alloc_result alloc_pusch(slot_ue& ue, const prb_grant& dl_mask); - tti_point get_pdcch_tti() const { return pdcch_tti; } + slot_point get_pdcch_tti() const { return pdcch_slot; } const bwp_res_grid& res_grid() const { return bwp_grid; } const bwp_params& cfg; @@ -111,7 +111,7 @@ private: srslog::basic_logger& logger; bwp_res_grid& bwp_grid; - tti_point pdcch_tti; + slot_point pdcch_slot; }; } // namespace sched_nr_impl diff --git a/srsenb/hdr/stack/mac/nr/sched_nr_ue.h b/srsenb/hdr/stack/mac/nr/sched_nr_ue.h index fcf8c93a4..a27cd0c45 100644 --- a/srsenb/hdr/stack/mac/nr/sched_nr_ue.h +++ b/srsenb/hdr/stack/mac/nr/sched_nr_ue.h @@ -39,27 +39,27 @@ class slot_ue { public: slot_ue() = default; - explicit slot_ue(uint16_t rnti_, tti_point tti_rx_, uint32_t cc); + explicit slot_ue(uint16_t rnti_, slot_point slot_rx_, uint32_t cc); slot_ue(slot_ue&&) noexcept = default; slot_ue& operator=(slot_ue&&) noexcept = default; bool empty() const { return rnti == SCHED_NR_INVALID_RNTI; } void release() { rnti = SCHED_NR_INVALID_RNTI; } - uint16_t rnti = SCHED_NR_INVALID_RNTI; - tti_point tti_rx; - uint32_t cc = SCHED_NR_MAX_CARRIERS; + uint16_t rnti = SCHED_NR_INVALID_RNTI; + slot_point slot_rx; + uint32_t cc = SCHED_NR_MAX_CARRIERS; // UE parameters common to all sectors - bool pending_sr; + bool pending_sr = false; // UE parameters that are sector specific const bwp_ue_cfg* cfg = nullptr; - tti_point pdcch_tti; - tti_point pdsch_tti; - tti_point pusch_tti; - tti_point uci_tti; - uint32_t dl_cqi; - uint32_t ul_cqi; + slot_point pdcch_slot; + slot_point pdsch_slot; + slot_point pusch_slot; + slot_point uci_slot; + uint32_t dl_cqi = 0; + uint32_t ul_cqi = 0; dl_harq_proc* h_dl = nullptr; ul_harq_proc* h_ul = nullptr; }; @@ -68,8 +68,8 @@ class ue_carrier { public: ue_carrier(uint16_t rnti, const ue_cfg_t& cfg, const sched_cell_params& cell_params_); - void new_tti(tti_point pdcch_tti, const ue_cfg_t& uecfg_); - slot_ue try_reserve(tti_point pdcch_tti); + void new_slot(slot_point pdcch_slot, const ue_cfg_t& uecfg_); + slot_ue try_reserve(slot_point pdcch_slot); const uint16_t rnti; const uint32_t cc; @@ -90,12 +90,12 @@ class ue public: ue(uint16_t rnti, const ue_cfg_t& cfg, const sched_params& sched_cfg_); - slot_ue try_reserve(tti_point pdcch_tti, uint32_t cc); + slot_ue try_reserve(slot_point pdcch_slot, uint32_t cc); void set_cfg(const ue_cfg_t& cfg); const ue_cfg_t& cfg() const { return ue_cfg; } - void ul_sr_info(tti_point tti_rx) { pending_sr = true; } + void ul_sr_info(slot_point slot_rx) { pending_sr = true; } bool has_ca() const { return ue_cfg.carriers.size() > 1; } uint32_t pcell_cc() const { return ue_cfg.carriers[0].cc; } diff --git a/srsenb/hdr/stack/mac/nr/sched_nr_worker.h b/srsenb/hdr/stack/mac/nr/sched_nr_worker.h index ddf533ab7..e5f8ea9f7 100644 --- a/srsenb/hdr/stack/mac/nr/sched_nr_worker.h +++ b/srsenb/hdr/stack/mac/nr/sched_nr_worker.h @@ -46,10 +46,12 @@ public: explicit slot_cc_worker(serv_cell_manager& sched); - void start(tti_point pdcch_tti, ue_map_t& ue_db_); + void start(slot_point pdcch_slot, ue_map_t& ue_db_); void run(); void finish(); - bool running() const { return tti_rx.is_valid(); } + bool running() const { return slot_rx.valid(); } + + void enqueue_cc_event(srsran::move_callback ev); /// Enqueue feedback directed at a given UE in a given cell void enqueue_cc_feedback(uint16_t rnti, feedback_callback_t fdbk); @@ -66,7 +68,7 @@ private: serv_cell_manager& cell; srslog::basic_logger& logger; - tti_point tti_rx; + slot_point slot_rx; bwp_slot_allocator bwp_alloc; // Process of UE cell-specific feedback @@ -74,8 +76,9 @@ private: uint16_t rnti; feedback_callback_t fdbk; }; - std::mutex feedback_mutex; - srsran::deque pending_feedback, tmp_feedback_to_run; + std::mutex feedback_mutex; + srsran::deque pending_feedback, tmp_feedback_to_run; + srsran::deque > pending_events, tmp_events_to_run; srsran::static_circular_map slot_ues; }; @@ -85,7 +88,7 @@ class sched_worker_manager struct slot_worker_ctxt { std::mutex slot_mutex; // lock of all workers of the same slot. std::condition_variable cvar; - tti_point tti_rx; + slot_point slot_rx; int nof_workers_waiting = 0; std::atomic worker_count{0}; // variable shared across slot_cc_workers std::vector workers; @@ -99,16 +102,17 @@ public: sched_worker_manager(sched_worker_manager&&) = delete; ~sched_worker_manager(); - void run_slot(tti_point tti_tx, uint32_t cc, dl_sched_t& dl_res, ul_sched_t& ul_res); + void run_slot(slot_point slot_tx, uint32_t cc, dl_sched_t& dl_res, ul_sched_t& ul_res); void enqueue_event(uint16_t rnti, srsran::move_callback ev); + void enqueue_cc_event(uint32_t cc, srsran::move_callback ev); void enqueue_cc_feedback(uint16_t rnti, uint32_t cc, slot_cc_worker::feedback_callback_t fdbk) { cc_worker_list[cc]->worker.enqueue_cc_feedback(rnti, std::move(fdbk)); } private: - bool save_sched_result(tti_point pdcch_tti, uint32_t cc, dl_sched_t& dl_res, ul_sched_t& ul_res); + bool save_sched_result(slot_point pdcch_slot, uint32_t cc, dl_sched_t& dl_res, ul_sched_t& ul_res); const sched_params& cfg; ue_map_t& ue_db; @@ -125,7 +129,7 @@ private: std::vector > slot_worker_ctxts; struct cc_context { std::condition_variable cvar; - bool waiting = false; + int waiting = 0; slot_cc_worker worker; cc_context(serv_cell_manager& sched) : worker(sched) {} @@ -133,7 +137,7 @@ private: std::mutex slot_mutex; std::condition_variable cvar; - tti_point current_tti; + slot_point current_slot; std::atomic worker_count{0}; // variable shared across slot_cc_workers std::vector > cc_worker_list; }; diff --git a/srsenb/hdr/stack/mac/nr/ue_nr.h b/srsenb/hdr/stack/mac/nr/ue_nr.h index c198ec5e5..ac621a4f2 100644 --- a/srsenb/hdr/stack/mac/nr/ue_nr.h +++ b/srsenb/hdr/stack/mac/nr/ue_nr.h @@ -57,13 +57,8 @@ public: void set_active(bool active) { active_state.store(active, std::memory_order_relaxed); } bool is_active() const { return active_state.load(std::memory_order_relaxed); } - uint8_t* generate_pdu(uint32_t enb_cc_idx, - uint32_t harq_pid, - uint32_t tb_idx, - const sched_interface::dl_sched_pdu_t pdu[sched_interface::MAX_RLC_PDU_LIST], - uint32_t nof_pdu_elems, - uint32_t grant_size); - int process_pdu(srsran::unique_byte_buffer_t pdu); + int generate_pdu(srsran::byte_buffer_t* pdu, uint32_t grant_size); + int process_pdu(srsran::unique_byte_buffer_t pdu); std::mutex metrics_mutex = {}; void metrics_read(mac_ue_metrics_t* metrics_); diff --git a/srsenb/hdr/stack/mac/sched_ue_ctrl/sched_dl_cqi.h b/srsenb/hdr/stack/mac/sched_ue_ctrl/sched_dl_cqi.h index 03ce1d91d..54a973fb5 100644 --- a/srsenb/hdr/stack/mac/sched_ue_ctrl/sched_dl_cqi.h +++ b/srsenb/hdr/stack/mac/sched_ue_ctrl/sched_dl_cqi.h @@ -177,7 +177,7 @@ public: /// Get CQI of given subband index int get_subband_cqi(uint32_t subband_index) const { - if (subband_cqi_enabled()) { + if (not subband_cqi_enabled()) { return get_wb_cqi_info(); } return bp_list[get_bp_index(subband_index)].last_feedback_tti.is_valid() ? subband_cqi[subband_index] : wb_cqi_avg; @@ -235,14 +235,11 @@ private: srsran::bounded_vector subband_cqi; }; -/// Get {RBG index, CQI} tuple which correspond to the set RBG with the lowest CQI -std::tuple find_min_cqi_rbg(const rbgmask_t& mask, const sched_dl_cqi& dl_cqi); - -/// Returns the same RBG mask, but with the RBGs of the subband with the lowest CQI reset -rbgmask_t remove_min_cqi_subband(const rbgmask_t& rbgmask, const sched_dl_cqi& dl_cqi); +/// Get {RBG indexs, CQI} tuple which correspond to the set RBG with the lowest CQI +rbgmask_t find_min_cqi_rbgs(const rbgmask_t& mask, const sched_dl_cqi& dl_cqi, int& cqi); -/// Returns the same RBG mask, but with the RBG with the lowest CQI reset -rbgmask_t remove_min_cqi_rbg(const rbgmask_t& rbgmask, const sched_dl_cqi& dl_cqi); +/// Returns the same RBG mask, but with the RBGs with the lowest CQI reset +rbgmask_t remove_min_cqi_rbgs(const rbgmask_t& rbgmask, const sched_dl_cqi& dl_cqi); } // namespace srsenb diff --git a/srsenb/hdr/stack/ngap/ngap.h b/srsenb/hdr/stack/ngap/ngap.h index d015dc9d1..1f9ca697b 100644 --- a/srsenb/hdr/stack/ngap/ngap.h +++ b/srsenb/hdr/stack/ngap/ngap.h @@ -46,9 +46,11 @@ namespace srsenb { class ngap : public ngap_interface_rrc_nr { public: + class ue; ngap(srsran::task_sched_handle task_sched_, srslog::basic_logger& logger, srsran::socket_manager_itf* rx_socket_handler); + ~ngap(); int init(const ngap_args_t& args_, rrc_interface_ngap_nr* rrc_); void stop(); @@ -63,7 +65,7 @@ public: srsran::unique_byte_buffer_t pdu, uint32_t s_tmsi); - void write_pdu(uint16_t rnti, srsran::unique_byte_buffer_t pdu){}; + void write_pdu(uint16_t rnti, srsran::unique_byte_buffer_t pdu); bool user_exists(uint16_t rnti) { return true; }; void user_mod(uint16_t old_rnti, uint16_t new_rnti){}; bool user_release(uint16_t rnti, asn1::ngap_nr::cause_radio_network_e cause_radio) { return true; }; @@ -72,6 +74,8 @@ public: srsran::optional ran_ue_ngap_id = {}, srsran::optional amf_ue_ngap_id = {}); void ue_ctxt_setup_complete(uint16_t rnti); + void ue_notify_rrc_reconf_complete(uint16_t rnti, bool outcome); + bool send_pdu_session_resource_setup_response(); // Stack interface bool @@ -88,7 +92,7 @@ private: static const int NONUE_STREAM_ID = 0; // args - rrc_interface_ngap_nr* rrc = nullptr; + rrc_interface_ngap_nr* rrc = nullptr; ngap_args_t args = {}; srslog::basic_logger& logger; srsran::task_sched_handle task_sched; @@ -107,22 +111,6 @@ private: asn1::ngap_nr::tai_s tai; asn1::ngap_nr::nr_cgi_s nr_cgi; - // Moved into NGAP class to avoid redifinition (Introduce new namespace?) - struct ue_ctxt_t { - static const uint32_t invalid_gnb_id = std::numeric_limits::max(); - - uint16_t rnti = SRSRAN_INVALID_RNTI; - uint32_t ran_ue_ngap_id = invalid_gnb_id; - srsran::optional amf_ue_ngap_id; - uint32_t gnb_cc_idx = 0; - struct timeval init_timestamp = {}; - - // AMF identifier - uint16_t amf_set_id; - uint8_t amf_pointer; - uint8_t amf_region_id; - }; - asn1::ngap_nr::ng_setup_resp_s ngsetupresponse; int build_tai_cgi(); @@ -131,34 +119,23 @@ private: bool sctp_send_ngap_pdu(const asn1::ngap_nr::ngap_pdu_c& tx_pdu, uint32_t rnti, const char* procedure_name); bool handle_ngap_rx_pdu(srsran::byte_buffer_t* pdu); - bool handle_successfuloutcome(const asn1::ngap_nr::successful_outcome_s& msg); - bool handle_unsuccessfuloutcome(const asn1::ngap_nr::unsuccessful_outcome_s& msg); - bool handle_initiatingmessage(const asn1::ngap_nr::init_msg_s& msg); - - bool handle_dlnastransport(const asn1::ngap_nr::dl_nas_transport_s& msg); - bool handle_ngsetupresponse(const asn1::ngap_nr::ng_setup_resp_s& msg); - bool handle_ngsetupfailure(const asn1::ngap_nr::ng_setup_fail_s& msg); - bool handle_initialctxtsetuprequest(const asn1::ngap_nr::init_context_setup_request_s& msg); - struct ue { - explicit ue(ngap* ngap_ptr_); - bool send_initialuemessage(asn1::ngap_nr::rrcestablishment_cause_e cause, - srsran::unique_byte_buffer_t pdu, - bool has_tmsi, - uint32_t s_tmsi = 0); - bool send_ulnastransport(srsran::unique_byte_buffer_t pdu); - bool was_uectxtrelease_requested() const { return release_requested; } - void ue_ctxt_setup_complete(); - - ue_ctxt_t ctxt = {}; - uint16_t stream_id = 1; + bool handle_successful_outcome(const asn1::ngap_nr::successful_outcome_s& msg); + bool handle_unsuccessful_outcome(const asn1::ngap_nr::unsuccessful_outcome_s& msg); + bool handle_initiating_message(const asn1::ngap_nr::init_msg_s& msg); + + // TS 38.413 - Section 8.6.2 - Downlink NAS Transport + bool handle_dl_nas_transport(const asn1::ngap_nr::dl_nas_transport_s& msg); + // TS 38.413 - Section 9.2.6.2 - NG Setup Response + bool handle_ng_setup_response(const asn1::ngap_nr::ng_setup_resp_s& msg); + // TS 38.413 - Section 9.2.6.3 - NG Setup Failure + bool handle_ng_setup_failure(const asn1::ngap_nr::ng_setup_fail_s& msg); + // TS 38.413 - Section 9.2.2.5 - UE Context Release Command + bool handle_ue_ctxt_release_cmd(const asn1::ngap_nr::ue_context_release_cmd_s& msg); + // TS 38.413 - Section 9.2.2.1 - Initial Context Setup Request + bool handle_initial_ctxt_setup_request(const asn1::ngap_nr::init_context_setup_request_s& msg); + // TS 38.413 - Section 9.2.1.1 - PDU Session Resource Setup Request + bool handle_pdu_session_resource_setup_request(const asn1::ngap_nr::pdu_session_res_setup_request_s& msg); - private: - // args - ngap* ngap_ptr; - - // state - bool release_requested = false; - }; class user_list { public: diff --git a/srsenb/hdr/stack/ngap/ngap_interfaces.h b/srsenb/hdr/stack/ngap/ngap_interfaces.h new file mode 100644 index 000000000..4740a8c2c --- /dev/null +++ b/srsenb/hdr/stack/ngap/ngap_interfaces.h @@ -0,0 +1,24 @@ +/** + * + * \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 SRSENB_NGAP_INTERFACES_H +#define SRSENB_NGAP_INTERFACES_H + +namespace srsenb { +class ngap_interface_ngap_proc +{ +public: + virtual bool send_initial_ctxt_setup_response() = 0; +}; +} // namespace srsenb + +#endif \ No newline at end of file diff --git a/srsenb/hdr/stack/ngap/ngap_ue.h b/srsenb/hdr/stack/ngap/ngap_ue.h new file mode 100644 index 000000000..99f203628 --- /dev/null +++ b/srsenb/hdr/stack/ngap/ngap_ue.h @@ -0,0 +1,66 @@ +/** + * + * \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 SRSENB_NGAP_UE_H +#define SRSENB_NGAP_UE_H + +#include "ngap.h" +#include "ngap_ue_proc.h" +#include "ngap_ue_utils.h" +#include "srsran/asn1/asn1_utils.h" +#include "srsran/asn1/ngap.h" +namespace srsenb { + +class ngap::ue : public ngap_interface_ngap_proc +{ +public: + explicit ue(ngap* ngap_ptr_, rrc_interface_ngap_nr* rrc_ptr_, srslog::basic_logger& logger_); + virtual ~ue(); + // TS 38.413 - Section 9.2.5.1 - Initial UE Message + bool send_initial_ue_message(asn1::ngap_nr::rrcestablishment_cause_e cause, + srsran::unique_byte_buffer_t pdu, + bool has_tmsi, + uint32_t s_tmsi = 0); + // TS 38.413 - Section 9.2.5.3 - Uplink NAS Transport + bool send_ul_nas_transport(srsran::unique_byte_buffer_t pdu); + // TS 38.413 - Section 9.2.2.2 - Initial Context Setup Response + bool send_initial_ctxt_setup_response(); + // TS 38.413 - Section 9.2.2.3 - Initial Context Setup Failure + bool send_initial_ctxt_setup_failure(asn1::ngap_nr::cause_c cause); + // TS 38.413 - Section 9.2.2.1 - Initial Context Setup Request + bool handle_initial_ctxt_setup_request(const asn1::ngap_nr::init_context_setup_request_s& msg); + // TS 38.413 - Section 9.2.2.5 - UE Context Release Command + bool handle_ue_ctxt_release_cmd(const asn1::ngap_nr::ue_context_release_cmd_s& msg); + + bool was_uectxtrelease_requested() const { return release_requested; } + void ue_ctxt_setup_complete(); + void notify_rrc_reconf_complete(const bool reconf_complete_outcome); + + srsran::proc_t initial_context_setup_proc; + srsran::proc_t ue_context_release_proc; + + ngap_ue_ctxt_t ctxt = {}; + uint16_t stream_id = 1; + +private: + // args + ngap* ngap_ptr; + rrc_interface_ngap_nr* rrc_ptr; + + // state + bool release_requested = false; + + // logger + srslog::basic_logger& logger; +}; + +} // namespace srsenb +#endif \ No newline at end of file diff --git a/srsenb/hdr/stack/ngap/ngap_ue_proc.h b/srsenb/hdr/stack/ngap/ngap_ue_proc.h new file mode 100644 index 000000000..9464fcd8e --- /dev/null +++ b/srsenb/hdr/stack/ngap/ngap_ue_proc.h @@ -0,0 +1,84 @@ +/** + * + * \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 SRSENB_NGAP_UE_PROC_H +#define SRSENB_NGAP_UE_PROC_H + +#include "ngap_interfaces.h" +#include "ngap_ue_utils.h" + +#include "srsran/asn1/asn1_utils.h" +#include "srsran/asn1/ngap.h" +#include "srsran/common/buffer_pool.h" +#include "srsran/common/stack_procedure.h" +#include "srsran/interfaces/gnb_rrc_nr_interfaces.h" + +#include +#include + +namespace srsenb { + +// TS 38.413 - Section 8.3 - UE Context Management Procedures + +// TS 38.413 - Section 8.3.1 - Initial Context Setup +class ngap_ue_initial_context_setup_proc +{ +public: + explicit ngap_ue_initial_context_setup_proc(ngap_interface_ngap_proc* parent_, + rrc_interface_ngap_nr* rrc_, + ngap_ue_ctxt_t* ue_ctxt); + srsran::proc_outcome_t init(const asn1::ngap_nr::init_context_setup_request_s& msg); + srsran::proc_outcome_t react(const bool security_mode_command_outcome); + srsran::proc_outcome_t step(); + static const char* name() { return "Initial Context Setup"; } + +private: + ngap_ue_ctxt_t* ue_ctxt; + ngap_interface_ngap_proc* parent = nullptr; + rrc_interface_ngap_nr* rrc = nullptr; + srslog::basic_logger& logger; +}; + +// TS 38.413 - Section 8.3.2 - UE Context Release Request (NG-RAN node initiated) +class ngap_ue_ue_context_release_proc +{ +public: + explicit ngap_ue_ue_context_release_proc(ngap_interface_ngap_proc* parent_, + rrc_interface_ngap_nr* rrc_, + ngap_ue_ctxt_t* ue_ctxt); + srsran::proc_outcome_t init(const asn1::ngap_nr::ue_context_release_cmd_s& msg); + srsran::proc_outcome_t step(); + static const char* name() { return "UE Context Release"; } + +private: + ngap_ue_ctxt_t* ue_ctxt; + ngap_interface_ngap_proc* parent = nullptr; + rrc_interface_ngap_nr* rrc = nullptr; + srslog::basic_logger& logger; +}; + +// TS 38.413 - Section 8.3.4 - UE Context Modification +class ngap_ue_ue_context_modification_proc +{ +public: + explicit ngap_ue_ue_context_modification_proc(ngap_interface_ngap_proc* parent_); + srsran::proc_outcome_t init(); + srsran::proc_outcome_t step(); + static const char* name() { return "UE Context Modification"; } + +private: + ngap_interface_ngap_proc* parent; +}; + +} // namespace srsenb + +#endif \ No newline at end of file diff --git a/srsenb/hdr/stack/ngap/ngap_ue_utils.h b/srsenb/hdr/stack/ngap/ngap_ue_utils.h new file mode 100644 index 000000000..f0067ef50 --- /dev/null +++ b/srsenb/hdr/stack/ngap/ngap_ue_utils.h @@ -0,0 +1,38 @@ +/** + * + * \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 SRSENB_NGAP_UE_UTILS_H +#define SRSENB_NGAP_UE_UTILS_H + +#include "srsenb/hdr/common/common_enb.h" +#include "srsran/adt/optional.h" +#include "srsran/common/common.h" +#include "srsran/phy/common/phy_common.h" + +namespace srsenb { + +struct ngap_ue_ctxt_t { + static const uint32_t invalid_gnb_id = std::numeric_limits::max(); + + uint16_t rnti = SRSRAN_INVALID_RNTI; + uint32_t ran_ue_ngap_id = invalid_gnb_id; + srsran::optional amf_ue_ngap_id; + uint32_t gnb_cc_idx = 0; + struct timeval init_timestamp = {}; + + // AMF identifier + uint16_t amf_set_id; + uint8_t amf_pointer; + uint8_t amf_region_id; +}; + +} // namespace srsenb +#endif \ No newline at end of file diff --git a/srsenb/hdr/stack/rrc/rrc_endc.h b/srsenb/hdr/stack/rrc/rrc_endc.h index a30390505..3993afb1b 100644 --- a/srsenb/hdr/stack/rrc/rrc_endc.h +++ b/srsenb/hdr/stack/rrc/rrc_endc.h @@ -52,12 +52,13 @@ public: rrc_endc(srsenb::rrc::ue* outer_ue); bool fill_conn_recfg(asn1::rrc::rrc_conn_recfg_r8_ies_s* conn_recfg); - void handle_ue_capabilities(const asn1::rrc::ue_eutra_cap_s& eutra_caps); + void handle_eutra_capabilities(const asn1::rrc::ue_eutra_cap_s& eutra_caps); void handle_ue_meas_report(const asn1::rrc::meas_report_s& msg); void handle_sgnb_addition_ack(const asn1::dyn_octstring& nr_secondary_cell_group_cfg_r15, const asn1::dyn_octstring& nr_radio_bearer_cfg1_r15); void handle_sgnb_addition_reject(); void handle_sgnb_addition_complete(); + bool is_endc_supported(); private: // Send SgNB addition request to gNB diff --git a/srsenb/hdr/stack/rrc/rrc_metrics.h b/srsenb/hdr/stack/rrc/rrc_metrics.h index c5a92788c..a6adbbbae 100644 --- a/srsenb/hdr/stack/rrc/rrc_metrics.h +++ b/srsenb/hdr/stack/rrc/rrc_metrics.h @@ -33,6 +33,7 @@ typedef enum { RRC_STATE_WAIT_FOR_CON_REEST_COMPLETE, RRC_STATE_WAIT_FOR_SECURITY_MODE_COMPLETE, RRC_STATE_WAIT_FOR_UE_CAP_INFO, + RRC_STATE_WAIT_FOR_UE_CAP_INFO_ENDC, /* only entered for UEs with NSA support */ RRC_STATE_WAIT_FOR_CON_RECONF_COMPLETE, RRC_STATE_REESTABLISHMENT_COMPLETE, RRC_STATE_REGISTERED, diff --git a/srsenb/hdr/stack/rrc/rrc_nr.h b/srsenb/hdr/stack/rrc/rrc_nr.h index ac55a56e8..84b53651a 100644 --- a/srsenb/hdr/stack/rrc/rrc_nr.h +++ b/srsenb/hdr/stack/rrc/rrc_nr.h @@ -71,6 +71,7 @@ public: rrc_nr_cfg_t update_default_cfg(const rrc_nr_cfg_t& rrc_cfg); int add_user(uint16_t rnti); int update_user(uint16_t new_rnti, uint16_t old_rnti); + void config_phy(); void config_mac(); int32_t generate_sibs(); int read_pdu_bcch_bch(const uint32_t tti, srsran::unique_byte_buffer_t& buffer) final; @@ -91,6 +92,13 @@ public: int sgnb_addition_request(uint16_t rnti); int sgnb_reconfiguration_complete(uint16_t rnti, asn1::dyn_octstring reconfig_response); + // Interfaces for NGAP + int ue_set_security_cfg_key(uint16_t rnti, const asn1::fixed_bitstring<256, false, true>& key); + int ue_set_bitrates(uint16_t rnti, const asn1::ngap_nr::ue_aggregate_maximum_bit_rate_s& rates); + int ue_set_security_cfg_capabilities(uint16_t rnti, const asn1::ngap_nr::ue_security_cap_s& caps); + int start_security_mode_procedure(uint16_t rnti); + void write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu); + class ue { public: diff --git a/srsenb/hdr/stack/rrc/rrc_ue.h b/srsenb/hdr/stack/rrc/rrc_ue.h index 7aab2cf3b..3cf4e160c 100644 --- a/srsenb/hdr/stack/rrc/rrc_ue.h +++ b/srsenb/hdr/stack/rrc/rrc_ue.h @@ -25,6 +25,7 @@ #include "mac_controller.h" #include "rrc.h" #include "srsran/adt/pool/batch_mem_pool.h" +#include "srsran/asn1/rrc/uecap.h" #include "srsran/interfaces/enb_phy_interfaces.h" #include "srsran/interfaces/pdcp_interface_types.h" @@ -86,7 +87,7 @@ public: bool phy_cfg_updated = true, srsran::const_byte_span nas_pdu = {}); void send_security_mode_command(); - void send_ue_cap_enquiry(); + void send_ue_cap_enquiry(const std::vector& rats); void send_ue_info_req(); void parse_ul_dcch(uint32_t lcid, srsran::unique_byte_buffer_t pdu); @@ -113,7 +114,7 @@ public: void handle_rrc_reconf_complete(asn1::rrc::rrc_conn_recfg_complete_s* msg, srsran::unique_byte_buffer_t pdu); void handle_security_mode_complete(asn1::rrc::security_mode_complete_s* msg); void handle_security_mode_failure(asn1::rrc::security_mode_fail_s* msg); - bool handle_ue_cap_info(asn1::rrc::ue_cap_info_s* msg); + int handle_ue_cap_info(asn1::rrc::ue_cap_info_s* msg); void handle_ue_init_ctxt_setup_req(const asn1::s1ap::init_context_setup_request_s& msg); bool handle_ue_ctxt_mod_req(const asn1::s1ap::ue_context_mod_request_s& msg); void handle_ue_info_resp(const asn1::rrc::ue_info_resp_r9_s& msg, srsran::unique_byte_buffer_t pdu); diff --git a/srsenb/src/CMakeLists.txt b/srsenb/src/CMakeLists.txt index e041ace70..63218daca 100644 --- a/srsenb/src/CMakeLists.txt +++ b/srsenb/src/CMakeLists.txt @@ -34,7 +34,7 @@ if (RPATH) endif (RPATH) add_library(enb_cfg_parser STATIC parser.cc enb_cfg_parser.cc) -target_link_libraries(enb_cfg_parser ${LIBCONFIGPP_LIBRARIES}) +target_link_libraries(enb_cfg_parser srsran_common ${LIBCONFIGPP_LIBRARIES}) add_executable(srsenb main.cc enb.cc metrics_stdout.cc metrics_csv.cc metrics_json.cc) diff --git a/srsenb/src/enb.cc b/srsenb/src/enb.cc index 4d75bc7d5..fd5fdae62 100644 --- a/srsenb/src/enb.cc +++ b/srsenb/src/enb.cc @@ -79,7 +79,7 @@ int enb::init(const all_args_t& args_) } if (ret == SRSRAN_SUCCESS) { - if (lte_stack->init(args.stack, rrc_cfg, lte_phy.get()) != SRSRAN_SUCCESS) { + if (lte_stack->init(args.stack, rrc_cfg, lte_phy.get(), lte_phy.get()) != SRSRAN_SUCCESS) { srsran::console("Error initializing stack.\n"); ret = SRSRAN_ERROR; } @@ -91,9 +91,9 @@ int enb::init(const all_args_t& args_) return SRSRAN_ERROR; } - // Only Init PHY if radio couldn't be initialized + // Only Init PHY if radio could be initialized if (ret == SRSRAN_SUCCESS) { - if (lte_phy->init(args.phy, phy_cfg, lte_radio.get(), lte_stack.get())) { + if (lte_phy->init(args.phy, phy_cfg, lte_radio.get(), lte_stack.get(), *lte_stack)) { srsran::console("Error initializing PHY.\n"); ret = SRSRAN_ERROR; } diff --git a/srsenb/src/enb_cfg_parser.cc b/srsenb/src/enb_cfg_parser.cc index 8893f5216..d3f1d74a5 100644 --- a/srsenb/src/enb_cfg_parser.cc +++ b/srsenb/src/enb_cfg_parser.cc @@ -22,6 +22,7 @@ #include "enb_cfg_parser.h" #include "srsenb/hdr/enb.h" #include "srsran/asn1/rrc_utils.h" +#include "srsran/common/band_helper.h" #include "srsran/common/multiqueue.h" #include "srsran/phy/common/phy_common.h" #include @@ -837,6 +838,8 @@ static int parse_cell_list(all_args_t* args, rrc_cfg_t* rrc_cfg, Setting& root) HANDLEPARSERCODE(parse_required_field(cell_cfg.pci, cellroot, "pci")); cell_cfg.pci = cell_cfg.pci % SRSRAN_NUM_PCI; HANDLEPARSERCODE(parse_required_field(cell_cfg.dl_earfcn, cellroot, "dl_earfcn")); + parse_required_field(cell_cfg.dl_freq_hz, cellroot, "dl_freq"); + parse_required_field(cell_cfg.ul_freq_hz, cellroot, "ul_freq"); parse_default_field(cell_cfg.ul_earfcn, cellroot, "ul_earfcn", 0u); // will be derived from DL EARFCN If not set parse_default_field( cell_cfg.root_seq_idx, cellroot, "root_seq_idx", rrc_cfg->sibs[1].sib2().rr_cfg_common.prach_cfg.root_seq_idx); @@ -1100,32 +1103,57 @@ int set_derived_args(all_args_t* args_, rrc_cfg_t* rrc_cfg_, phy_cfg_t* phy_cfg_ phy_cfg_->phy_cell_cfg.push_back(phy_cell_cfg); } + // Use helper class to derive NR carrier parameters + srsran::srsran_band_helper band_helper; + // Create NR dedicated cell configuration from RRC configuration for (auto it = rrc_cfg_->cell_list_nr.begin(); it != rrc_cfg_->cell_list_nr.end(); ++it) { auto& cfg = *it; phy_cell_cfg_nr_t phy_cell_cfg = {}; phy_cell_cfg.carrier.max_mimo_layers = cell_cfg_.nof_ports; - phy_cell_cfg.carrier.nof_prb = cell_cfg_.nof_prb; - phy_cell_cfg.carrier.pci = cfg.pci; - phy_cell_cfg.cell_id = cfg.cell_id; - phy_cell_cfg.root_seq_idx = cfg.root_seq_idx; - phy_cell_cfg.rf_port = cfg.rf_port; + switch (cell_cfg_.nof_prb) { + case 25: + phy_cell_cfg.carrier.nof_prb = 25; + break; + case 50: + phy_cell_cfg.carrier.nof_prb = 52; + break; + case 100: + phy_cell_cfg.carrier.nof_prb = 106; + break; + default: + ERROR("The only accepted number of PRB is: 25, 50, 100"); + return SRSRAN_ERROR; + } + phy_cell_cfg.carrier.pci = cfg.pci; + phy_cell_cfg.cell_id = cfg.cell_id; + phy_cell_cfg.root_seq_idx = cfg.root_seq_idx; + phy_cell_cfg.rf_port = cfg.rf_port; phy_cell_cfg.num_ra_preambles = rrc_cfg_->sibs[1].sib2().rr_cfg_common.rach_cfg_common.preamb_info.nof_ra_preambs.to_number(); if (cfg.dl_freq_hz > 0) { phy_cell_cfg.dl_freq_hz = cfg.dl_freq_hz; } else { - phy_cell_cfg.dl_freq_hz = 1e6 * srsran_band_fd(cfg.dl_earfcn); + phy_cell_cfg.dl_freq_hz = band_helper.nr_arfcn_to_freq(cfg.dl_earfcn); } if (cfg.ul_freq_hz > 0) { phy_cell_cfg.ul_freq_hz = cfg.ul_freq_hz; } else { + // auto-detect UL frequency if (cfg.ul_earfcn == 0) { - cfg.ul_earfcn = srsran_band_ul_earfcn(cfg.dl_earfcn); + // derive UL ARFCN from given DL ARFCN + uint16_t nr_band = band_helper.get_band_from_dl_freq_Hz(phy_cell_cfg.dl_freq_hz); + srsran_duplex_mode_t nr_duplex = band_helper.get_duplex_mode(nr_band); + if (nr_duplex == SRSRAN_DUPLEX_MODE_TDD) { + cfg.ul_earfcn = cfg.dl_earfcn; + } else { + ERROR("Can't derive UL ARFCN from DL ARFCN"); + return SRSRAN_ERROR; + } } - phy_cell_cfg.ul_freq_hz = 1e6 * srsran_band_fu(cfg.ul_earfcn); + phy_cell_cfg.ul_freq_hz = band_helper.nr_arfcn_to_freq(cfg.ul_earfcn); } phy_cfg_->phy_cell_cfg_nr.push_back(phy_cell_cfg); diff --git a/srsenb/src/phy/lte/sf_worker.cc b/srsenb/src/phy/lte/sf_worker.cc index 39a7fe828..f0eda324f 100644 --- a/srsenb/src/phy/lte/sf_worker.cc +++ b/srsenb/src/phy/lte/sf_worker.cc @@ -110,16 +110,16 @@ cf_t* sf_worker::get_buffer_rx(uint32_t cc_idx, uint32_t antenna_idx) return cc_workers[cc_idx]->get_buffer_rx(antenna_idx); } -void sf_worker::set_time(uint32_t tti_, const srsran::rf_timestamp_t& tx_time_) +void sf_worker::set_context(const srsran::phy_common_interface::worker_context_t& w_ctx) { - tti_rx = tti_; + tti_rx = w_ctx.sf_idx; tti_tx_dl = TTI_ADD(tti_rx, FDD_HARQ_DELAY_UL_MS); tti_tx_ul = TTI_RX_ACK(tti_rx); - tx_time.copy(tx_time_); + context.copy(w_ctx); for (auto& w : cc_workers) { - w->set_tti(tti_); + w->set_tti(w_ctx.sf_idx); } } @@ -156,14 +156,10 @@ void sf_worker::work_imp() // Get Transmission buffers srsran::rf_buffer_t tx_buffer = {}; - for (uint32_t cc = 0; cc < phy->get_nof_carriers_lte(); cc++) { - for (uint32_t ant = 0; ant < phy->get_nof_ports(0); ant++) { - tx_buffer.set(cc, ant, phy->get_nof_ports(0), cc_workers[cc]->get_buffer_tx(ant)); - } - } + tx_buffer.set_nof_samples(SRSRAN_SF_LEN_PRB(phy->get_nof_prb(0))); if (!running) { - phy->worker_end(this, true, tx_buffer, tx_time, false); + phy->worker_end(context, true, tx_buffer); return; } @@ -201,14 +197,14 @@ void sf_worker::work_imp() if (sf_type == SRSRAN_SF_NORM) { if (stack->get_dl_sched(tti_tx_dl, dl_grants) < 0) { Error("Getting DL scheduling from MAC"); - phy->worker_end(this, false, tx_buffer, tx_time, false); + phy->worker_end(context, true, tx_buffer); return; } } else { dl_grants[0].cfi = mbsfn_cfg.non_mbsfn_region_length; if (stack->get_mch_sched(tti_tx_dl, mbsfn_cfg.is_mcch, dl_grants)) { Error("Getting MCH packets from MAC"); - phy->worker_end(this, false, tx_buffer, tx_time, false); + phy->worker_end(context, true, tx_buffer); return; } } @@ -216,7 +212,7 @@ void sf_worker::work_imp() // Get UL scheduling for the TX TTI from MAC if (stack->get_ul_sched(tti_tx_ul, ul_grants_tx) < 0) { Error("Getting UL scheduling from MAC"); - phy->worker_end(this, false, tx_buffer, tx_time, false); + phy->worker_end(context, true, tx_buffer); return; } @@ -241,9 +237,15 @@ void sf_worker::work_imp() // Save grants phy->set_ul_grants(tti_tx_ul, ul_grants_tx); + // Set or combine RF ports + for (uint32_t cc = 0; cc < phy->get_nof_carriers_lte(); cc++) { + for (uint32_t ant = 0; ant < phy->get_nof_ports(0); ant++) { + tx_buffer.set_combine(phy->get_rf_port(cc), ant, phy->get_nof_ports(0), cc_workers[cc]->get_buffer_tx(ant)); + } + } + Debug("Sending to radio"); - tx_buffer.set_nof_samples(SRSRAN_SF_LEN_PRB(phy->get_nof_prb(0))); - phy->worker_end(this, true, tx_buffer, tx_time, false); + phy->worker_end(context, true, tx_buffer); #ifdef DEBUG_WRITE_FILE fwrite(signal_buffer_tx, SRSRAN_SF_LEN_PRB(phy->cell.nof_prb) * sizeof(cf_t), 1, f); diff --git a/srsenb/src/phy/nr/slot_worker.cc b/srsenb/src/phy/nr/slot_worker.cc index 4acc710eb..635efe4b2 100644 --- a/srsenb/src/phy/nr/slot_worker.cc +++ b/srsenb/src/phy/nr/slot_worker.cc @@ -20,7 +20,8 @@ */ #include "srsenb/hdr/phy/nr/slot_worker.h" -#include +#include "srsran/common/buffer_pool.h" +#include "srsran/common/common.h" namespace srsenb { namespace nr { @@ -34,13 +35,14 @@ slot_worker::slot_worker(srsran::phy_common_interface& common_, bool slot_worker::init(const args_t& args) { + std::lock_guard lock(mutex); + // Calculate subframe length sf_len = SRSRAN_SF_LEN_PRB_NR(args.nof_max_prb); // Copy common configurations cell_index = args.cell_index; - // FIXME: - // pdcch_cfg = args.pdcch_cfg; + rf_port = args.rf_port; // Allocate Tx buffers tx_buffer.resize(args.nof_tx_ports); @@ -52,7 +54,7 @@ bool slot_worker::init(const args_t& args) } } - // Allocate Tx buffers + // Allocate Rx buffers rx_buffer.resize(args.nof_rx_ports); for (uint32_t i = 0; i < args.nof_rx_ports; i++) { rx_buffer[i] = srsran_vec_cf_malloc(sf_len); @@ -112,6 +114,7 @@ slot_worker::~slot_worker() cf_t* slot_worker::get_buffer_rx(uint32_t antenna_idx) { + std::lock_guard lock(mutex); if (antenna_idx >= (uint32_t)rx_buffer.size()) { return nullptr; } @@ -121,6 +124,7 @@ cf_t* slot_worker::get_buffer_rx(uint32_t antenna_idx) cf_t* slot_worker::get_buffer_tx(uint32_t antenna_idx) { + std::lock_guard lock(mutex); if (antenna_idx >= (uint32_t)tx_buffer.size()) { return nullptr; } @@ -133,12 +137,12 @@ uint32_t slot_worker::get_buffer_len() return sf_len; } -void slot_worker::set_time(const uint32_t& tti, const srsran::rf_timestamp_t& timestamp) +void slot_worker::set_context(const srsran::phy_common_interface::worker_context_t& w_ctx) { - logger.set_context(tti); - ul_slot_cfg.idx = tti; - dl_slot_cfg.idx = TTI_ADD(tti, FDD_HARQ_DELAY_UL_MS); - tx_time.copy(timestamp); + logger.set_context(w_ctx.sf_idx); + ul_slot_cfg.idx = w_ctx.sf_idx; + dl_slot_cfg.idx = TTI_ADD(w_ctx.sf_idx, FDD_HARQ_DELAY_UL_MS); + context.copy(w_ctx); } bool slot_worker::work_ul() @@ -207,12 +211,18 @@ bool slot_worker::work_ul() // For each PUSCH... for (stack_interface_phy_nr::pusch_t& pusch : ul_sched.pusch) { - // Get payload PDU + // Prepare PUSCH stack_interface_phy_nr::pusch_info_t pusch_info = {}; pusch_info.uci_cfg = pusch.sch.uci; pusch_info.pid = pusch.pid; - pusch_info.pusch_data.tb[0].payload = pusch.data[0]; - pusch_info.pusch_data.tb[1].payload = pusch.data[1]; + pusch_info.rnti = pusch.sch.grant.rnti; + pusch_info.pdu = srsran::make_byte_buffer(); + if (pusch_info.pdu == nullptr) { + logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return false; + } + pusch_info.pdu->N_bytes = pusch.sch.grant.tb[0].tbs; + pusch_info.pusch_data.tb[0].payload = pusch_info.pdu->data(); // Decode PUSCH if (srsran_gnb_ul_get_pusch(&gnb_ul, &ul_slot_cfg, &pusch.sch, &pusch.sch.grant, &pusch_info.pusch_data) < @@ -299,8 +309,16 @@ bool slot_worker::work_dl() // Encode PDSCH for (stack_interface_phy_nr::pdsch_t& pdsch : dl_sched.pdsch) { + // convert MAC to PHY buffer data structures + uint8_t* data[SRSRAN_MAX_TB] = {}; + for (uint32_t i = 0; i < SRSRAN_MAX_TB; ++i) { + if (pdsch.data[i] != nullptr) { + data[i] = pdsch.data[i]->msg; + } + } + // Put PDSCH message - if (srsran_gnb_dl_pdsch_put(&gnb_dl, &dl_slot_cfg, &pdsch.sch, pdsch.data.data()) < SRSRAN_SUCCESS) { + if (srsran_gnb_dl_pdsch_put(&gnb_dl, &dl_slot_cfg, &pdsch.sch, data) < SRSRAN_SUCCESS) { logger.error("PDSCH: Error putting DL message"); return false; } @@ -309,7 +327,14 @@ bool slot_worker::work_dl() if (logger.info.enabled()) { std::array str = {}; srsran_gnb_dl_pdsch_info(&gnb_dl, &pdsch.sch, str.data(), (uint32_t)str.size()); - logger.info("PDSCH: cc=%d %s tti_tx=%d", cell_index, str.data(), dl_slot_cfg.idx); + + if (logger.debug.enabled()) { + std::array str_extra = {}; + srsran_sch_cfg_nr_info(&pdsch.sch, str_extra.data(), (uint32_t)str_extra.size()); + logger.info("PDSCH: cc=%d %s tti_tx=%d\n%s", cell_index, str.data(), dl_slot_cfg.idx, str_extra.data()); + } else { + logger.info("PDSCH: cc=%d %s tti_tx=%d", cell_index, str.data(), dl_slot_cfg.idx); + } } } @@ -335,27 +360,26 @@ void slot_worker::work_imp() stack.slot_indication(dl_slot_cfg); // Get Transmission buffers + uint32_t nof_ant = (uint32_t)tx_buffer.size(); srsran::rf_buffer_t tx_rf_buffer = {}; - for (uint32_t i = 0; i < (uint32_t)tx_buffer.size(); i++) { - tx_rf_buffer.set(i, tx_buffer[i]); - } - - // Set number of samples tx_rf_buffer.set_nof_samples(sf_len); + for (uint32_t a = 0; a < nof_ant; a++) { + tx_rf_buffer.set(rf_port, a, nof_ant, tx_buffer[a]); + } // Process uplink if (not work_ul()) { - common.worker_end(this, false, tx_rf_buffer, tx_time, true); + common.worker_end(context, false, tx_rf_buffer); return; } // Process downlink if (not work_dl()) { - common.worker_end(this, false, tx_rf_buffer, tx_time, true); + common.worker_end(context, false, tx_rf_buffer); return; } - common.worker_end(this, true, tx_rf_buffer, tx_time, true); + common.worker_end(context, true, tx_rf_buffer); } bool slot_worker::set_common_cfg(const srsran_carrier_nr_t& carrier, const srsran_pdcch_cfg_nr_t& pdcch_cfg_) { @@ -367,7 +391,10 @@ bool slot_worker::set_common_cfg(const srsran_carrier_nr_t& carrier, const srsra // Set gNb UL carrier if (srsran_gnb_ul_set_carrier(&gnb_ul, &carrier) < SRSRAN_SUCCESS) { - logger.error("Error setting UL carrier"); + logger.error("Error setting UL carrier (pci=%d, nof_prb=%d, max_mimo_layers=%d)", + carrier.pci, + carrier.nof_prb, + carrier.max_mimo_layers); return false; } diff --git a/srsenb/src/phy/nr/worker_pool.cc b/srsenb/src/phy/nr/worker_pool.cc index afdac4e72..4ed65dee4 100644 --- a/srsenb/src/phy/nr/worker_pool.cc +++ b/srsenb/src/phy/nr/worker_pool.cc @@ -27,7 +27,7 @@ worker_pool::worker_pool(srsran::phy_common_interface& common_, stack_interface_phy_nr& stack_, srslog::sink& log_sink_, uint32_t max_workers) : - pool(max_workers), + pool(max_workers, "NR-"), common(common_), stack(stack_), log_sink(log_sink_), @@ -39,6 +39,8 @@ worker_pool::worker_pool(srsran::phy_common_interface& common_, bool worker_pool::init(const args_t& args, const phy_cell_cfg_list_nr_t& cell_list) { + nof_prach_workers = args.nof_prach_workers; + // Configure logger srslog::basic_levels log_level = srslog::str_to_basic_level(args.log.phy_level); logger.set_level(log_level); @@ -59,6 +61,7 @@ bool worker_pool::init(const args_t& args, const phy_cell_cfg_list_nr_t& cell_li w_args.nof_max_prb = cell_list[cell_index].carrier.nof_prb; w_args.nof_tx_ports = cell_list[cell_index].carrier.max_mimo_layers; w_args.nof_rx_ports = cell_list[cell_index].carrier.max_mimo_layers; + w_args.rf_port = cell_list[cell_index].rf_port; w_args.pusch_max_nof_iter = args.pusch_max_nof_iter; if (not w->init(w_args)) { @@ -140,7 +143,7 @@ int worker_pool::set_common_cfg(const phy_interface_rrc_nr::common_cfg_t& common prach_cfg.is_nr = true; // Set the PRACH configuration - prach.init(0, cell, prach_cfg, &prach_stack_adaptor, logger, 0, 1); + prach.init(0, cell, prach_cfg, &prach_stack_adaptor, logger, 0, nof_prach_workers); prach.set_max_prach_offset_us(1000); // Save current configuration diff --git a/srsenb/src/phy/phy.cc b/srsenb/src/phy/phy.cc index 62060b49a..56a7b44ba 100644 --- a/srsenb/src/phy/phy.cc +++ b/srsenb/src/phy/phy.cc @@ -27,6 +27,7 @@ #include #include "srsenb/hdr/phy/phy.h" +#include "srsran/common/phy_cfg_nr_default.h" #include "srsran/common/threads.h" #define Error(fmt, ...) \ @@ -101,6 +102,25 @@ void phy::parse_common_config(const phy_cfg_t& cfg) workers_common.dmrs_pusch_cfg.sequence_hopping_en = cfg.pusch_cnfg.ul_ref_sigs_pusch.seq_hop_enabled; } +int phy::init(const phy_args_t& args, + const phy_cfg_t& cfg, + srsran::radio_interface_phy* radio_, + stack_interface_phy_lte* stack_lte_, + stack_interface_phy_nr& stack_nr_) +{ + if (init(args, cfg, radio_, stack_lte_) != SRSRAN_SUCCESS) { + phy_log.error("Couldn't initialize LTE PHY"); + return SRSRAN_ERROR; + } + + if (init_nr(args, cfg, stack_nr_) != SRSRAN_SUCCESS) { + phy_log.error("Couldn't initialize NR PHY"); + return SRSRAN_ERROR; + } + + return SRSRAN_SUCCESS; +} + int phy::init(const phy_args_t& args, const phy_cfg_t& cfg, srsran::radio_interface_phy* radio_, @@ -139,12 +159,6 @@ int phy::init(const phy_args_t& args, if (not cfg.phy_cell_cfg.empty()) { lte_workers.init(args, &workers_common, log_sink, WORKERS_THREAD_PRIO); } - if (not cfg.phy_cell_cfg_nr.empty()) { - // Not implemented - // nr_workers = std::unique_ptr( - // new nr::worker_pool(cfg.phy_cell_cfg_nr, workers_common, stack_, log_sink, MAX_WORKERS, - // WORKERS_THREAD_PRIO)); - } // For each carrier, initialise PRACH worker for (uint32_t cc = 0; cc < cfg.phy_cell_cfg.size(); cc++) { @@ -155,7 +169,7 @@ int phy::init(const phy_args_t& args, prach.set_max_prach_offset_us(args.max_prach_offset_us); // Warning this must be initialized after all workers have been added to the pool - tx_rx.init(stack_, radio, <e_workers, nr_workers.get(), &workers_common, &prach, SF_RECV_THREAD_PRIO); + tx_rx.init(stack_, radio, <e_workers, &workers_common, &prach, SF_RECV_THREAD_PRIO); initialized = true; @@ -309,4 +323,46 @@ void phy::start_plot() lte_workers[0]->start_plot(); } +int phy::init_nr(const phy_args_t& args, const phy_cfg_t& cfg, stack_interface_phy_nr& stack) +{ + if (cfg.phy_cell_cfg_nr.empty()) { + return SRSRAN_SUCCESS; + } + + nr_workers = std::unique_ptr(new nr::worker_pool(workers_common, stack, log_sink, MAX_WORKERS)); + + nr::worker_pool::args_t worker_args = {}; + worker_args.log.phy_level = args.log.phy_level; + worker_args.log.phy_hex_limit = args.log.phy_hex_limit; + + if (not nr_workers->init(worker_args, cfg.phy_cell_cfg_nr)) { + return SRSRAN_ERROR; + } + + tx_rx.set_nr_workers(nr_workers.get()); + + // perform initial config of PHY (during RRC init PHY isn't running yet) + static const srsran::phy_cfg_nr_t default_phy_cfg = + srsran::phy_cfg_nr_default_t{srsran::phy_cfg_nr_default_t::reference_cfg_t{}}; + srsenb::phy_interface_rrc_nr::common_cfg_t common_cfg = {}; + common_cfg.carrier = default_phy_cfg.carrier; + common_cfg.pdcch = default_phy_cfg.pdcch; + common_cfg.prach = default_phy_cfg.prach; + if (set_common_cfg(common_cfg) < SRSRAN_SUCCESS) { + phy_log.error("Couldn't set common PHY config"); + return SRSRAN_ERROR; + } + + return SRSRAN_SUCCESS; +} + +int phy::set_common_cfg(const phy_interface_rrc_nr::common_cfg_t& common_cfg) +{ + if (nr_workers.get() == nullptr) { + return SRSRAN_ERROR; + } + + return nr_workers->set_common_cfg(common_cfg); +} + } // namespace srsenb diff --git a/srsenb/src/phy/phy_common.cc b/srsenb/src/phy/phy_common.cc index c0e511b25..b33ecba34 100644 --- a/srsenb/src/phy/phy_common.cc +++ b/srsenb/src/phy/phy_common.cc @@ -113,42 +113,46 @@ void phy_common::set_ul_grants(uint32_t tti, const stack_interface_phy_lte::ul_s * Each worker uses this function to indicate that all processing is done and data is ready for transmission or * there is no transmission at all (tx_enable). In that case, the end of burst message will be sent to the radio */ -void phy_common::worker_end(void* tx_sem_id, - bool tx_enable, - srsran::rf_buffer_t& buffer, - srsran::rf_timestamp_t& tx_time, - bool is_nr) +void phy_common::worker_end(const worker_context_t& w_ctx, const bool& tx_enable, srsran::rf_buffer_t& buffer) { // Wait for the green light to transmit in the current TTI - semaphore.wait(tx_sem_id); + semaphore.wait(w_ctx.worker_ptr); - // If this is for NR, save Tx buffers... - if (is_nr) { - nr_tx_buffer = buffer; - nr_tx_buffer_ready = true; - semaphore.release(); - return; + // For combine buffer with previous buffers + if (tx_enable) { + tx_buffer.set_nof_samples(buffer.get_nof_samples()); + tx_buffer.set_combine(buffer); } - // ... otherwise, append NR base-band from saved buffer if available - if (nr_tx_buffer_ready) { - uint32_t j = 0; - for (uint32_t i = 0; i < SRSRAN_MAX_CHANNELS; i++) { - if (buffer.get(i) == nullptr) { - buffer.set(i, nr_tx_buffer.get(j)); - j++; - } + // If the current worker is not the last one, skip transmission + if (not w_ctx.last) { + // Release semaphore and let next worker to get in + semaphore.release(); + + // Wait for the last worker to finish + if (tx_enable) { + wait_last_worker(); } - nr_tx_buffer_ready = false; + + return; } + // Add current time alignment + srsran::rf_timestamp_t tx_time = w_ctx.tx_time; // get transmit time from the last worker + // Run DL channel emulator if created if (dl_channel) { - dl_channel->run(buffer.to_cf_t(), buffer.to_cf_t(), buffer.get_nof_samples(), tx_time.get(0)); + dl_channel->run(tx_buffer.to_cf_t(), tx_buffer.to_cf_t(), tx_buffer.get_nof_samples(), tx_time.get(0)); } // Always transmit on single radio - radio->tx(buffer, tx_time); + radio->tx(tx_buffer, tx_time); + + // Reset transmit buffer + tx_buffer = {}; + + // Notify this is the last worker + last_worker(); // Allow next TTI to transmit semaphore.release(); diff --git a/srsenb/src/phy/phy_ue_db.cc b/srsenb/src/phy/phy_ue_db.cc index 1e3d79292..14c046f7a 100644 --- a/srsenb/src/phy/phy_ue_db.cc +++ b/srsenb/src/phy/phy_ue_db.cc @@ -615,6 +615,45 @@ int phy_ue_db::fill_uci_cfg(uint32_t tti, return uci_required ? 1 : SRSRAN_SUCCESS; } +void phy_ue_db::send_cqi_data(uint32_t tti, + uint16_t rnti, + uint32_t cqi_cc_idx, + const srsran_cqi_cfg_t& cqi_cfg, + const srsran_cqi_value_t& cqi_value, + const srsran_cqi_report_cfg_t& cqi_report_cfg, + const srsran_cell_t& cell, + stack_interface_phy_lte* stack) +{ + uint8_t stack_value = 0; + switch (cqi_cfg.type) { + case SRSRAN_CQI_TYPE_WIDEBAND: + stack_value = cqi_value.wideband.wideband_cqi; + stack->cqi_info(tti, rnti, cqi_cc_idx, stack_value); + break; + case SRSRAN_CQI_TYPE_SUBBAND_UE: + stack_value = cqi_value.subband_ue.subband_cqi; + stack->sb_cqi_info(tti, + rnti, + cqi_cc_idx, + srsran_cqi_get_sb_idx(tti, cqi_value.subband_ue.subband_label, &cqi_report_cfg, &cell), + stack_value); + break; + case SRSRAN_CQI_TYPE_SUBBAND_HL: + stack_value = cqi_value.subband_hl.wideband_cqi_cw0; + // Todo: change interface + stack->cqi_info(tti, rnti, cqi_cc_idx, stack_value); + break; + case SRSRAN_CQI_TYPE_SUBBAND_UE_DIFF: + stack_value = cqi_value.subband_ue_diff.wideband_cqi; + stack->sb_cqi_info(tti, + rnti, + cqi_cc_idx, + cqi_value.subband_ue_diff.position_subband, + stack_value + cqi_value.subband_ue_diff.subband_diff_cqi); + break; + } +} + int phy_ue_db::send_uci_data(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, @@ -643,7 +682,8 @@ int phy_ue_db::send_uci_data(uint32_t tti, // Get ACK info srsran_pdsch_ack_t& pdsch_ack = ue.pdsch_ack[tti]; - srsran_enb_dl_get_ack(&cell_cfg_list->at(ue.cell_info[0].enb_cc_idx).cell, &uci_cfg, &uci_value, &pdsch_ack); + const srsran_cell_t& cell = cell_cfg_list->at(ue.cell_info[0].enb_cc_idx).cell; + srsran_enb_dl_get_ack(&cell, &uci_cfg, &uci_value, &pdsch_ack); // Iterate over the ACK information for (uint32_t ue_cc_idx = 0; ue_cc_idx < SRSRAN_MAX_CARRIERS; ue_cc_idx++) { @@ -672,22 +712,7 @@ int phy_ue_db::send_uci_data(uint32_t tti, if (uci_value.cqi.data_crc) { // Channel quality indicator itself if (uci_cfg.cqi.data_enable) { - uint8_t cqi_value = 0; - switch (uci_cfg.cqi.type) { - case SRSRAN_CQI_TYPE_WIDEBAND: - cqi_value = uci_value.cqi.wideband.wideband_cqi; - break; - case SRSRAN_CQI_TYPE_SUBBAND_UE: - cqi_value = uci_value.cqi.subband_ue.subband_cqi; - break; - case SRSRAN_CQI_TYPE_SUBBAND_HL: - cqi_value = uci_value.cqi.subband_hl.wideband_cqi_cw0; - break; - case SRSRAN_CQI_TYPE_SUBBAND_UE_DIFF: - cqi_value = uci_value.cqi.subband_ue_diff.wideband_cqi; - break; - } - stack->cqi_info(tti, rnti, cqi_cc_idx, cqi_value); + send_cqi_data(tti, rnti, cqi_cc_idx, uci_cfg.cqi, uci_value.cqi, ue.cell_info[0].phy_cfg.dl_cfg.cqi_report, cell, stack); } // Precoding Matrix indicator (TM4) diff --git a/srsenb/src/phy/prach_worker.cc b/srsenb/src/phy/prach_worker.cc index 5a857774b..d51332784 100644 --- a/srsenb/src/phy/prach_worker.cc +++ b/srsenb/src/phy/prach_worker.cc @@ -61,14 +61,18 @@ int prach_worker::init(const srsran_cell_t& cell_, #if defined(ENABLE_GUI) and ENABLE_PRACH_GUI char title[32] = {}; - snprintf(title, sizeof(title), "PRACH buffer %d", cc_idx); + snprintf(title, sizeof(title), "PRACH buffer %s %d", prach.is_nr ? "NR" : "LTE", cc_idx); sdrgui_init(); plot_real_init(&plot_real); plot_real_setTitle(&plot_real, title); plot_real_setXAxisAutoScale(&plot_real, true); plot_real_setYAxisAutoScale(&plot_real, true); - plot_real_addToWindowGrid(&plot_real, (char*)"PRACH", 0, cc_idx); + if (prach.is_nr) { + plot_real_addToWindowGrid(&plot_real, (char*)"PRACH-NR", 1, cc_idx); + } else { + plot_real_addToWindowGrid(&plot_real, (char*)"PRACH", 0, cc_idx); + } #endif // defined(ENABLE_GUI) and ENABLE_PRACH_GUI return 0; diff --git a/srsenb/src/phy/txrx.cc b/srsenb/src/phy/txrx.cc index 216efdd48..96a08e6bc 100644 --- a/srsenb/src/phy/txrx.cc +++ b/srsenb/src/phy/txrx.cc @@ -51,7 +51,6 @@ txrx::txrx(srslog::basic_logger& logger) : thread("TXRX"), logger(logger), runni bool txrx::init(stack_interface_phy_lte* stack_, srsran::radio_interface_phy* radio_h_, lte::worker_pool* lte_workers_, - nr::worker_pool* nr_workers_, phy_common* worker_com_, prach_worker_pool* prach_, uint32_t prio_) @@ -59,7 +58,6 @@ bool txrx::init(stack_interface_phy_lte* stack_, stack = stack_; radio_h = radio_h_; lte_workers = lte_workers_; - nr_workers = nr_workers_; worker_com = worker_com_; prach = prach_; running = true; @@ -74,6 +72,12 @@ bool txrx::init(stack_interface_phy_lte* stack_, return true; } +bool txrx::set_nr_workers(nr::worker_pool* nr_workers_) +{ + nr_workers = nr_workers_; + return true; +} + void txrx::stop() { if (running) { @@ -134,7 +138,7 @@ void txrx::run_thread() } nr::slot_worker* nr_worker = nullptr; - if (worker_com->get_nof_carriers_nr() > 0) { + if (nr_workers != nullptr and worker_com->get_nof_carriers_nr() > 0) { nr_worker = nr_workers->wait_worker(tti); if (nr_worker == nullptr) { running = false; @@ -160,7 +164,9 @@ void txrx::run_thread() // WARNING: // - The number of ports for all cells must be the same // - Only one NR cell is currently supported - buffer.set(rf_port, p, worker_com->get_nof_ports(0), nr_worker->get_buffer_rx(p)); + if (nr_worker != nullptr) { + buffer.set(rf_port, p, worker_com->get_nof_ports(0), nr_worker->get_buffer_rx(p)); + } } } } @@ -181,23 +187,40 @@ void txrx::run_thread() timestamp.get(0).frac_secs, lte_worker->get_id()); - lte_worker->set_time(tti, timestamp); - // Trigger prach worker execution for (uint32_t cc = 0; cc < worker_com->get_nof_carriers_lte(); cc++) { prach->new_tti(cc, tti, buffer.get(worker_com->get_rf_port(cc), 0, worker_com->get_nof_ports(0))); } - // Launch NR worker only if available + // Set NR worker context and start if (nr_worker != nullptr) { - nr_worker->set_time(tti, timestamp); + srsran::phy_common_interface::worker_context_t context; + context.sf_idx = tti; + context.worker_ptr = nr_worker; + context.last = (lte_worker == nullptr); // Set last if standalone + context.tx_time.copy(timestamp); + + nr_worker->set_context(context); + + // Start NR worker processing worker_com->semaphore.push(nr_worker); nr_workers->start_worker(nr_worker); } - // Trigger phy worker execution - worker_com->semaphore.push(lte_worker); - lte_workers->start_worker(lte_worker); + // Set LTE worker context and start + if (lte_worker != nullptr) { + srsran::phy_common_interface::worker_context_t context; + context.sf_idx = tti; + context.worker_ptr = lte_worker; + context.last = true; + context.tx_time.copy(timestamp); + + lte_worker->set_context(context); + + // Start LTE worker processing + worker_com->semaphore.push(lte_worker); + lte_workers->start_worker(lte_worker); + } // Advance stack in time stack->tti_clock(); diff --git a/srsenb/src/stack/enb_stack_lte.cc b/srsenb/src/stack/enb_stack_lte.cc index b56394f71..3b75ae2be 100644 --- a/srsenb/src/stack/enb_stack_lte.cc +++ b/srsenb/src/stack/enb_stack_lte.cc @@ -74,6 +74,19 @@ std::string enb_stack_lte::get_type() return "lte"; } +int enb_stack_lte::init(const stack_args_t& args_, + const rrc_cfg_t& rrc_cfg_, + phy_interface_stack_lte* phy_, + phy_interface_stack_nr* phy_nr_) +{ + phy_nr = phy_nr_; + if (init(args_, rrc_cfg_, phy_)) { + return SRSRAN_ERROR; + } + + return SRSRAN_SUCCESS; +} + int enb_stack_lte::init(const stack_args_t& args_, const rrc_cfg_t& rrc_cfg_, phy_interface_stack_lte* phy_) { phy = phy_; @@ -159,18 +172,22 @@ int enb_stack_lte::init(const stack_args_t& args_, const rrc_cfg_t& rrc_cfg_) // NR layers mac_nr_args_t mac_args = {}; + mac_args.fixed_dl_mcs = 28; + mac_args.fixed_ul_mcs = 10; mac_args.pcap = args.mac_pcap; - if (mac_nr.init(mac_args, nullptr, nullptr, &rlc_nr, &rrc_nr) != SRSRAN_SUCCESS) { + mac_args.pcap.filename = "/tmp/enb_mac_nr.pcap"; + if (mac_nr.init(mac_args, phy_nr, nullptr, &rlc_nr, &rrc_nr) != SRSRAN_SUCCESS) { stack_logger.error("Couldn't initialize MAC-NR"); return SRSRAN_ERROR; } rrc_nr_cfg_t rrc_cfg_nr = {}; - if (rrc_nr.init(rrc_cfg_nr, nullptr, &mac_nr, &rlc_nr, &pdcp_nr, nullptr, nullptr, &rrc) != SRSRAN_SUCCESS) { + if (rrc_nr.init(rrc_cfg_nr, phy_nr, &mac_nr, &rlc_nr, &pdcp_nr, nullptr, nullptr, &rrc) != SRSRAN_SUCCESS) { stack_logger.error("Couldn't initialize RRC-NR"); return SRSRAN_ERROR; } - // FIXME: Add RLC and PDCP + rlc_nr.init(&pdcp_nr, &rrc_nr, &mac_nr, task_sched.get_timer_handler()); + pdcp_nr.init(&rlc_nr, &rrc_nr, >pu); gtpu_args_t gtpu_args; gtpu_args.embms_enable = args.embms.enable; diff --git a/srsenb/src/stack/gnb_stack_nr.cc b/srsenb/src/stack/gnb_stack_nr.cc index aebd91d07..f42cd5176 100644 --- a/srsenb/src/stack/gnb_stack_nr.cc +++ b/srsenb/src/stack/gnb_stack_nr.cc @@ -158,11 +158,6 @@ bool gnb_stack_nr::get_metrics(srsenb::stack_metrics_t* metrics) return true; } -int gnb_stack_nr::rx_data_indication(rx_data_ind_t& grant) -{ - return m_mac->rx_data_indication(grant); -} - // Temporary GW interface void gnb_stack_nr::write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu) { @@ -189,7 +184,7 @@ int gnb_stack_nr::pucch_info(const srsran_slot_cfg_t& slot_cfg, const mac_interf { return m_mac->pucch_info(slot_cfg, pucch_info); } -int gnb_stack_nr::pusch_info(const srsran_slot_cfg_t& slot_cfg, const mac_interface_phy_nr::pusch_info_t& pusch_info) +int gnb_stack_nr::pusch_info(const srsran_slot_cfg_t& slot_cfg, mac_interface_phy_nr::pusch_info_t& pusch_info) { return m_mac->pusch_info(slot_cfg, pusch_info); } diff --git a/srsenb/src/stack/mac/nr/mac_nr.cc b/srsenb/src/stack/mac/nr/mac_nr.cc index 17932b51f..5988cd1a8 100644 --- a/srsenb/src/stack/mac/nr/mac_nr.cc +++ b/srsenb/src/stack/mac/nr/mac_nr.cc @@ -20,6 +20,7 @@ */ #include "srsenb/hdr/stack/mac/mac_nr.h" +#include "srsenb/test/mac/nr/sched_nr_cfg_generators.h" #include "srsran/common/buffer_pool.h" #include "srsran/common/log_helper.h" #include "srsran/common/rwlock_guard.h" @@ -33,7 +34,10 @@ namespace srsenb { mac_nr::mac_nr(srsran::task_sched_handle task_sched_) : - logger(srslog::fetch_basic_logger("MAC-NR")), task_sched(task_sched_) + logger(srslog::fetch_basic_logger("MAC-NR")), + task_sched(task_sched_), + sched(srsenb::sched_nr_interface::sched_cfg_t{}), + bcch_bch_payload(srsran::make_byte_buffer()) { stack_task_queue = task_sched.make_task_queue(); } @@ -61,10 +65,11 @@ int mac_nr::init(const mac_nr_args_t& args_, pcap->open(args.pcap.filename); } - bcch_bch_payload = srsran::make_byte_buffer(); - if (bcch_bch_payload == nullptr) { - return SRSRAN_ERROR; - } + // configure scheduler for 1 carrier + std::vector cells_cfg = srsenb::get_default_cells_cfg(1); + sched.cell_cfg(cells_cfg); + + detected_rachs.resize(cells_cfg.size()); logger.info("Started"); @@ -113,16 +118,14 @@ int mac_nr::cell_cfg(srsenb::sched_interface::cell_cfg_t* cell_cfg) return SRSRAN_SUCCESS; } -void mac_nr::rach_detected(const srsran_slot_cfg_t& slot_cfg, - uint32_t enb_cc_idx, - uint32_t preamble_idx, - uint32_t time_adv) +void mac_nr::rach_detected(const rach_info_t& rach_info) { static srsran::mutexed_tprof rach_tprof("rach_tprof", "MAC-NR", 1); - logger.set_context(slot_cfg.idx); + logger.set_context(rach_info.slot_index); auto rach_tprof_meas = rach_tprof.start(); - stack_task_queue.push([this, slot_cfg, enb_cc_idx, preamble_idx, time_adv, rach_tprof_meas]() mutable { + uint32_t enb_cc_idx = 0; + stack_task_queue.push([this, rach_info, enb_cc_idx, rach_tprof_meas]() mutable { uint16_t rnti = add_ue(enb_cc_idx); if (rnti == SRSRAN_INVALID_RNTI) { return; @@ -137,7 +140,10 @@ void mac_nr::rach_detected(const srsran_slot_cfg_t& slot_cfg, ++detected_rachs[enb_cc_idx]; // Add new user to the scheduler so that it can RX/TX SRB0 - // .. + srsenb::sched_nr_interface::ue_cfg_t ue_cfg = srsenb::get_default_ue_cfg(1); + ue_cfg.fixed_dl_mcs = args.fixed_dl_mcs; + ue_cfg.fixed_ul_mcs = args.fixed_ul_mcs; + sched.ue_cfg(rnti, ue_cfg); // Register new user in RRC if (rrc->add_user(rnti) == SRSRAN_ERROR) { @@ -146,19 +152,24 @@ void mac_nr::rach_detected(const srsran_slot_cfg_t& slot_cfg, } // Trigger scheduler RACH - // scheduler.dl_rach_info(enb_cc_idx, rar_info); - - logger.info("RACH: cc=%d, preamble=%d, offset=%d, temp_crnti=0x%x", - slot_cfg.idx, + srsenb::sched_nr_interface::dl_sched_rar_info_t rar_info = {}; + rar_info.preamble_idx = rach_info.preamble; + rar_info.temp_crnti = rnti; + rar_info.prach_slot = slot_point{NUMEROLOGY_IDX, rach_info.slot_index}; + // TODO: fill remaining fields as required + // sched.dl_rach_info(enb_cc_idx, rar_info); + + logger.info("RACH: slot=%d, cc=%d, preamble=%d, offset=%d, temp_crnti=0x%x", + rach_info.slot_index, enb_cc_idx, - preamble_idx, - time_adv, + rach_info.preamble, + rach_info.time_adv, rnti); - srsran::console("RACH: cc=%d, preamble=%d, offset=%d, temp_crnti=0x%x\n", - slot_cfg.idx, + srsran::console("RACH: slot=%d, cc=%d, preamble=%d, offset=%d, temp_crnti=0x%x\n", + rach_info.slot_index, enb_cc_idx, - preamble_idx, - time_adv, + rach_info.preamble, + rach_info.time_adv, rnti); }); } @@ -197,9 +208,6 @@ uint16_t mac_nr::add_ue(uint32_t enb_cc_idx) } } while (inserted_ue == nullptr); - // Set PCAP if available - // .. - return rnti; } @@ -260,22 +268,83 @@ int mac_nr::slot_indication(const srsran_slot_cfg_t& slot_cfg) int mac_nr::get_dl_sched(const srsran_slot_cfg_t& slot_cfg, dl_sched_t& dl_sched) { - return 0; + if (not pdsch_slot.valid()) { + pdsch_slot = srsran::slot_point{NUMEROLOGY_IDX, slot_cfg.idx}; + } else { + pdsch_slot++; + } + + int ret = sched.get_dl_sched(pdsch_slot, 0, dl_sched); + for (pdsch_t& pdsch : dl_sched.pdsch) { + for (auto& tb_data : pdsch.data) { + if (tb_data != nullptr) { + // TODO: exclude retx from packing + uint16_t rnti = pdsch.sch.grant.rnti; + srsran::rwlock_read_guard rw_lock(rwlock); + if (not is_rnti_active_unsafe(rnti)) { + continue; + } + ue_db[rnti]->generate_pdu(tb_data, pdsch.sch.grant.tb->tbs / 8); + + if (pcap != nullptr) { + uint32_t pid = 0; // TODO: get PID from PDCCH struct? + pcap->write_dl_crnti_nr(tb_data->msg, tb_data->N_bytes, rnti, pid, slot_cfg.idx); + } + } + } + } + return SRSRAN_SUCCESS; } + int mac_nr::get_ul_sched(const srsran_slot_cfg_t& slot_cfg, ul_sched_t& ul_sched) { - return 0; + if (not pusch_slot.valid()) { + pusch_slot = srsran::slot_point{NUMEROLOGY_IDX, slot_cfg.idx}; + } else { + pusch_slot++; + } + + return sched.get_ul_sched(pusch_slot, 0, ul_sched); } + int mac_nr::pucch_info(const srsran_slot_cfg_t& slot_cfg, const mac_interface_phy_nr::pucch_info_t& pucch_info) { - return 0; + if (not handle_uci_data(pucch_info.uci_data.cfg.pucch.rnti, pucch_info.uci_data.cfg, pucch_info.uci_data.value)) { + logger.error("Error handling UCI data from PUCCH reception"); + return SRSRAN_ERROR; + } + return SRSRAN_SUCCESS; } -int mac_nr::pusch_info(const srsran_slot_cfg_t& slot_cfg, const mac_interface_phy_nr::pusch_info_t& pusch_info) + +bool mac_nr::handle_uci_data(const uint16_t rnti, const srsran_uci_cfg_nr_t& cfg, const srsran_uci_value_nr_t& value) { - // FIXME: does the PUSCH info call include received PDUs? - uint16_t rnti = pusch_info.rnti; - srsran::unique_byte_buffer_t rx_pdu; - auto process_pdu_task = [this, rnti](srsran::unique_byte_buffer_t& pdu) { + // Process HARQ-ACK + for (uint32_t i = 0; i < cfg.ack.count; i++) { + const srsran_harq_ack_bit_t* ack_bit = &cfg.ack.bits[i]; + bool is_ok = (value.ack[i] == 1) and value.valid; + sched.dl_ack_info(rnti, 0, ack_bit->pid, 0, is_ok); + } + return true; +} + +int mac_nr::pusch_info(const srsran_slot_cfg_t& slot_cfg, mac_interface_phy_nr::pusch_info_t& pusch_info) +{ + uint16_t rnti = pusch_info.rnti; + + // Handle UCI data + if (not handle_uci_data(rnti, pusch_info.uci_cfg, pusch_info.pusch_data.uci)) { + logger.error("Error handling UCI data from PUCCH reception"); + return SRSRAN_ERROR; + } + + sched.ul_crc_info(rnti, 0, pusch_info.pid, pusch_info.pusch_data.tb[0].crc); + + if (pusch_info.pusch_data.tb[0].crc && pcap) { + pcap->write_ul_crnti_nr( + pusch_info.pdu->msg, pusch_info.pdu->N_bytes, pusch_info.rnti, pusch_info.pid, slot_cfg.idx); + } + + auto process_pdu_task = [this, rnti](srsran::unique_byte_buffer_t& pdu) { srsran::rwlock_read_guard lock(rwlock); if (is_rnti_active_unsafe(rnti)) { ue_db[rnti]->process_pdu(std::move(pdu)); @@ -283,11 +352,9 @@ int mac_nr::pusch_info(const srsran_slot_cfg_t& slot_cfg, const mac_interface_ph logger.debug("Discarding PDU rnti=0x%x", rnti); } }; - stack_task_queue.try_push(std::bind(process_pdu_task, std::move(rx_pdu))); + stack_task_queue.try_push(std::bind(process_pdu_task, std::move(pusch_info.pdu))); return SRSRAN_SUCCESS; } -void mac_nr::rach_detected(const mac_interface_phy_nr::rach_info_t& rach_info) {} - } // namespace srsenb diff --git a/srsenb/src/stack/mac/nr/sched_nr.cc b/srsenb/src/stack/mac/nr/sched_nr.cc index e50607b27..c12050231 100644 --- a/srsenb/src/stack/mac/nr/sched_nr.cc +++ b/srsenb/src/stack/mac/nr/sched_nr.cc @@ -43,40 +43,40 @@ public: } } - dl_sched_t& add_dl_result(tti_point tti, uint32_t cc) + dl_sched_t& add_dl_result(slot_point tti, uint32_t cc) { if (not has_dl_result(tti, cc)) { - results[tti.to_uint()][cc].tti_dl = tti; - results[tti.to_uint()][cc].dl_res = {}; + results[tti.to_uint()][cc].slot_dl = tti; + results[tti.to_uint()][cc].dl_res = {}; } return results[tti.to_uint()][cc].dl_res; } - ul_sched_t& add_ul_result(tti_point tti, uint32_t cc) + ul_sched_t& add_ul_result(slot_point tti, uint32_t cc) { if (not has_ul_result(tti, cc)) { - results[tti.to_uint()][cc].tti_ul = tti; - results[tti.to_uint()][cc].ul_res = {}; + results[tti.to_uint()][cc].slot_ul = tti; + results[tti.to_uint()][cc].ul_res = {}; } return results[tti.to_uint()][cc].ul_res; } - bool has_dl_result(tti_point tti, uint32_t cc) const { return results[tti.to_uint()][cc].tti_dl == tti; } + bool has_dl_result(slot_point tti, uint32_t cc) const { return results[tti.to_uint()][cc].slot_dl == tti; } - bool has_ul_result(tti_point tti, uint32_t cc) const { return results[tti.to_uint()][cc].tti_ul == tti; } + bool has_ul_result(slot_point tti, uint32_t cc) const { return results[tti.to_uint()][cc].slot_ul == tti; } - dl_sched_t pop_dl_result(tti_point tti, uint32_t cc) + dl_sched_t pop_dl_result(slot_point tti, uint32_t cc) { if (has_dl_result(tti, cc)) { - results[tti.to_uint()][cc].tti_dl.reset(); + results[tti.to_uint()][cc].slot_dl.clear(); return results[tti.to_uint()][cc].dl_res; } return {}; } - ul_sched_t pop_ul_result(tti_point tti, uint32_t cc) + ul_sched_t pop_ul_result(slot_point tti, uint32_t cc) { if (has_ul_result(tti, cc)) { - results[tti.to_uint()][cc].tti_ul.reset(); + results[tti.to_uint()][cc].slot_ul.clear(); return results[tti.to_uint()][cc].ul_res; } return {}; @@ -84,8 +84,8 @@ public: private: struct slot_result_t { - tti_point tti_dl; - tti_point tti_ul; + slot_point slot_dl; + slot_point slot_ul; dl_sched_t dl_res; ul_sched_t ul_res; }; @@ -95,7 +95,7 @@ private: /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -sched_nr::sched_nr(const sched_cfg_t& sched_cfg) : cfg(sched_cfg), logger(srslog::fetch_basic_logger("MAC")) {} +sched_nr::sched_nr(const sched_cfg_t& sched_cfg) : cfg(sched_cfg), logger(srslog::fetch_basic_logger("MAC-NR")) {} sched_nr::~sched_nr() {} @@ -135,7 +135,7 @@ void sched_nr::ue_cfg_impl(uint16_t rnti, const ue_cfg_t& uecfg) } /// Generate {tti,cc} scheduling decision -int sched_nr::generate_slot_result(tti_point pdcch_tti, uint32_t cc) +int sched_nr::generate_slot_result(slot_point pdcch_tti, uint32_t cc) { // Copy results to intermediate buffer dl_sched_t& dl_res = pending_results->add_dl_result(pdcch_tti, cc); @@ -147,16 +147,16 @@ int sched_nr::generate_slot_result(tti_point pdcch_tti, uint32_t cc) return SRSRAN_SUCCESS; } -int sched_nr::get_dl_sched(tti_point tti_tx, uint32_t cc, dl_sched_t& result) +int sched_nr::get_dl_sched(slot_point slot_tx, uint32_t cc, dl_sched_t& result) { - if (not pending_results->has_dl_result(tti_tx, cc)) { - generate_slot_result(tti_tx, cc); + if (not pending_results->has_dl_result(slot_tx, cc)) { + generate_slot_result(slot_tx, cc); } - result = pending_results->pop_dl_result(tti_tx, cc); + result = pending_results->pop_dl_result(slot_tx, cc); return SRSRAN_SUCCESS; } -int sched_nr::get_ul_sched(tti_point pusch_tti, uint32_t cc, ul_sched_t& result) +int sched_nr::get_ul_sched(slot_point pusch_tti, uint32_t cc, ul_sched_t& result) { if (not pending_results->has_ul_result(pusch_tti, cc)) { // sched result hasn't been generated @@ -167,6 +167,12 @@ int sched_nr::get_ul_sched(tti_point pusch_tti, uint32_t cc, ul_sched_t& result) return SRSRAN_SUCCESS; } +int sched_nr::dl_rach_info(uint32_t cc, const dl_sched_rar_info_t& rar_info) +{ + sched_workers->enqueue_cc_event(cc, [this, cc, rar_info]() { cells[cc]->bwps[0].ra.dl_rach_info(rar_info); }); + return SRSRAN_SUCCESS; +} + void sched_nr::dl_ack_info(uint16_t rnti, uint32_t cc, uint32_t pid, uint32_t tb_idx, bool ack) { sched_workers->enqueue_cc_feedback( @@ -178,9 +184,9 @@ void sched_nr::ul_crc_info(uint16_t rnti, uint32_t cc, uint32_t pid, bool crc) sched_workers->enqueue_cc_feedback(rnti, cc, [pid, crc](ue_carrier& ue_cc) { ue_cc.harq_ent.ul_crc_info(pid, crc); }); } -void sched_nr::ul_sr_info(tti_point tti_rx, uint16_t rnti) +void sched_nr::ul_sr_info(slot_point slot_rx, uint16_t rnti) { - sched_workers->enqueue_event(rnti, [this, rnti, tti_rx]() { ue_db[rnti]->ul_sr_info(tti_rx); }); + sched_workers->enqueue_event(rnti, [this, rnti, slot_rx]() { ue_db[rnti]->ul_sr_info(slot_rx); }); } #define VERIFY_INPUT(cond, msg, ...) \ diff --git a/srsenb/src/stack/mac/nr/sched_nr_cell.cc b/srsenb/src/stack/mac/nr/sched_nr_cell.cc index abc70ca7e..ba6a4a21c 100644 --- a/srsenb/src/stack/mac/nr/sched_nr_cell.cc +++ b/srsenb/src/stack/mac/nr/sched_nr_cell.cc @@ -64,9 +64,10 @@ alloc_result ra_sched::allocate_pending_rar(bwp_slot_allocator& slot_grid, void ra_sched::run_slot(bwp_slot_allocator& slot_grid, slot_ue_map_t& slot_ues) { static const uint32_t PRACH_RAR_OFFSET = 3; - tti_point pdcch_tti = slot_grid.get_pdcch_tti(); - tti_point msg3_tti = pdcch_tti + bwp_cfg->pusch_ra_list[0].msg3_delay; - if (not slot_grid.res_grid()[msg3_tti].is_ul) { + slot_point pdcch_slot = slot_grid.get_pdcch_tti(); + slot_point msg3_slot = pdcch_slot + bwp_cfg->pusch_ra_list[0].msg3_delay; + if (not slot_grid.res_grid()[pdcch_slot].is_dl or not slot_grid.res_grid()[msg3_slot].is_ul) { + // RAR only allowed if respective Msg3 slot is available for UL return; } @@ -76,16 +77,16 @@ void ra_sched::run_slot(bwp_slot_allocator& slot_grid, slot_ue_map_t& slot_ues) // In case of RAR outside RAR window: // - if window has passed, discard RAR // - if window hasn't started, stop loop, as RARs are ordered by TTI - tti_interval rar_window{rar.prach_tti + PRACH_RAR_OFFSET, - rar.prach_tti + PRACH_RAR_OFFSET + bwp_cfg->cfg.rar_window_size}; - if (not rar_window.contains(pdcch_tti)) { - if (pdcch_tti >= rar_window.stop()) { + slot_interval rar_window{rar.prach_slot + PRACH_RAR_OFFSET, + rar.prach_slot + PRACH_RAR_OFFSET + bwp_cfg->cfg.rar_window_size}; + if (not rar_window.contains(pdcch_slot)) { + if (pdcch_slot >= rar_window.stop()) { fmt::memory_buffer str_buffer; fmt::format_to(str_buffer, - "SCHED: Could not transmit RAR within the window (RA={}, Window={}, RAR={}", - rar.prach_tti, + "SCHED: Could not transmit RAR within the window Window={}, PRACH={}, RAR={}", rar_window, - pdcch_tti); + rar.prach_slot, + pdcch_slot); srsran::console("%s\n", srsran::to_c_str(str_buffer)); logger.warning("%s", srsran::to_c_str(str_buffer)); it = pending_rars.erase(it); @@ -122,24 +123,27 @@ void ra_sched::run_slot(bwp_slot_allocator& slot_grid, slot_ue_map_t& slot_ues) } } +/// See TS 38.321, 5.1.3 - RAP transmission int ra_sched::dl_rach_info(const dl_sched_rar_info_t& rar_info) { - logger.info("SCHED: New PRACH tti=%d, preamble=%d, temp_crnti=0x%x, ta_cmd=%d, msg3_size=%d", - rar_info.prach_tti, + logger.info("SCHED: New PRACH slot=%d, preamble=%d, temp_crnti=0x%x, ta_cmd=%d, msg3_size=%d", + rar_info.prach_slot.to_uint(), rar_info.preamble_idx, rar_info.temp_crnti, rar_info.ta_cmd, rar_info.msg3_size); - // RA-RNTI = 1 + t_id + f_id - // t_id = index of first subframe specified by PRACH (0<=t_id<10) - // f_id = index of the PRACH within subframe, in ascending order of freq domain (0<=f_id<6) (for FDD, f_id=0) - uint16_t ra_rnti = 1 + (uint16_t)(rar_info.prach_tti % 10u); + // RA-RNTI = 1 + s_id + 14 × t_id + 14 × 80 × f_id + 14 × 80 × 8 × ul_carrier_id + // s_id = index of the first OFDM symbol (0 <= s_id < 14) + // t_id = index of first slot of the PRACH (0 <= t_id < 80) + // f_id = index of the PRACH in the freq domain (0 <= f_id < 8) (for FDD, f_id=0) + // ul_carrier_id = 0 for NUL and 1 for SUL carrier + uint16_t ra_rnti = 1 + rar_info.ofdm_symbol_idx + 14 * rar_info.prach_slot.slot_idx() + 14 * 80 * rar_info.freq_idx; // find pending rar with same RA-RNTI for (pending_rar_t& r : pending_rars) { - if (r.prach_tti.to_uint() == rar_info.prach_tti and ra_rnti == r.ra_rnti) { - if (r.msg3_grant.size() >= sched_interface::MAX_RAR_LIST) { + if (r.prach_slot == rar_info.prach_slot and ra_rnti == r.ra_rnti) { + if (r.msg3_grant.full()) { logger.warning("PRACH ignored, as the the maximum number of RAR grants per tti has been reached"); return SRSRAN_ERROR; } @@ -150,8 +154,8 @@ int ra_sched::dl_rach_info(const dl_sched_rar_info_t& rar_info) // create new RAR pending_rar_t p; - p.ra_rnti = ra_rnti; - p.prach_tti = tti_point{rar_info.prach_tti}; + p.ra_rnti = ra_rnti; + p.prach_slot = rar_info.prach_slot; p.msg3_grant.push_back(rar_info); pending_rars.push_back(p); diff --git a/srsenb/src/stack/mac/nr/sched_nr_cfg.cc b/srsenb/src/stack/mac/nr/sched_nr_cfg.cc index 0d51a8b95..037a54c69 100644 --- a/srsenb/src/stack/mac/nr/sched_nr_cfg.cc +++ b/srsenb/src/stack/mac/nr/sched_nr_cfg.cc @@ -51,6 +51,20 @@ bwp_params::bwp_params(const cell_cfg_t& cell, const sched_cfg_t& sched_cfg_, ui srsran_assert(ret == SRSRAN_SUCCESS, "Failed to obtain RA config"); } srsran_assert(not pusch_ra_list.empty(), "Time-Domain Resource Allocation not valid"); + + for (uint32_t sl = 0; sl < SRSRAN_NOF_SF_X_FRAME; ++sl) { + for (uint32_t agg_idx = 0; agg_idx < MAX_NOF_AGGR_LEVELS; ++agg_idx) { + rar_cce_list[sl][agg_idx].resize(SRSRAN_SEARCH_SPACE_MAX_NOF_CANDIDATES_NR); + int n = srsran_pdcch_nr_locations_coreset(&cell_cfg.bwps[0].pdcch.coreset[0], + &cell_cfg.bwps[0].pdcch.ra_search_space, + 0, + agg_idx, + sl, + rar_cce_list[sl][agg_idx].data()); + srsran_assert(n >= 0, "Failed to configure RAR DCI locations"); + rar_cce_list[sl][agg_idx].resize(n); + } + } } sched_cell_params::sched_cell_params(uint32_t cc_, const cell_cfg_t& cell, const sched_cfg_t& sched_cfg_) : diff --git a/srsenb/src/stack/mac/nr/sched_nr_harq.cc b/srsenb/src/stack/mac/nr/sched_nr_harq.cc index 0dae2e23b..9066a3259 100644 --- a/srsenb/src/stack/mac/nr/sched_nr_harq.cc +++ b/srsenb/src/stack/mac/nr/sched_nr_harq.cc @@ -20,6 +20,7 @@ */ #include "srsenb/hdr/stack/mac/nr/sched_nr_harq.h" +#include "srsran/common/buffer_pool.h" namespace srsenb { namespace sched_nr_impl { @@ -36,9 +37,9 @@ bool harq_proc::ack_info(uint32_t tb_idx, bool ack) return true; } -void harq_proc::new_tti(tti_point tti_rx) +void harq_proc::new_slot(slot_point slot_rx) { - if (has_pending_retx(tti_rx) and nof_retx() + 1 >= max_nof_retx()) { + if (has_pending_retx(slot_rx) and nof_retx() + 1 >= max_nof_retx()) { tb[0].active = false; } } @@ -52,8 +53,8 @@ void harq_proc::reset() tb[0].tbs = std::numeric_limits::max(); } -bool harq_proc::new_tx(tti_point tti_tx_, - tti_point tti_ack_, +bool harq_proc::new_tx(slot_point slot_tx_, + slot_point slot_ack_, const prb_grant& grant, uint32_t mcs, uint32_t tbs, @@ -64,8 +65,8 @@ bool harq_proc::new_tx(tti_point tti_tx_, } reset(); max_retx = max_retx_; - tti_tx = tti_tx_; - tti_ack = tti_ack_; + slot_tx = slot_tx_; + slot_ack = slot_ack_; prbs_ = grant; tb[0].ndi = !tb[0].ndi; tb[0].mcs = mcs; @@ -83,32 +84,36 @@ bool harq_proc::set_tbs(uint32_t tbs) return true; } -bool harq_proc::new_retx(tti_point tti_tx_, tti_point tti_ack_, const prb_grant& grant) +bool harq_proc::new_retx(slot_point slot_tx_, slot_point slot_ack_, const prb_grant& grant) { if (grant.is_alloc_type0() != prbs_.is_alloc_type0() or (grant.is_alloc_type0() and grant.rbgs().count() != prbs_.rbgs().count()) or - (grant.is_alloc_type1() and grant.prbs().length() == prbs_.prbs().length())) { + (grant.is_alloc_type1() and grant.prbs().length() != prbs_.prbs().length())) { return false; } - if (new_retx(tti_tx_, tti_ack_)) { + if (new_retx(slot_tx_, slot_ack_)) { prbs_ = grant; return true; } return false; } -bool harq_proc::new_retx(tti_point tti_tx_, tti_point tti_ack_) +bool harq_proc::new_retx(slot_point slot_tx_, slot_point slot_ack_) { if (empty()) { return false; } - tti_tx = tti_tx_; - tti_ack = tti_ack_; + slot_tx = slot_tx_; + slot_ack = slot_ack_; tb[0].ack_state = false; tb[0].n_rtx++; return true; } +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()) +{} + harq_entity::harq_entity(uint32_t nprb, uint32_t nof_harq_procs) { // Create HARQs @@ -120,14 +125,14 @@ harq_entity::harq_entity(uint32_t nprb, uint32_t nof_harq_procs) } } -void harq_entity::new_tti(tti_point tti_rx_) +void harq_entity::new_slot(slot_point slot_rx_) { - tti_rx = tti_rx_; + slot_rx = slot_rx_; for (harq_proc& dl_h : dl_harqs) { - dl_h.new_tti(tti_rx); + dl_h.new_slot(slot_rx); } for (harq_proc& ul_h : ul_harqs) { - ul_h.new_tti(tti_rx); + ul_h.new_slot(slot_rx); } } diff --git a/srsenb/src/stack/mac/nr/sched_nr_helpers.cc b/srsenb/src/stack/mac/nr/sched_nr_helpers.cc index 146403fff..8eef03757 100644 --- a/srsenb/src/stack/mac/nr/sched_nr_helpers.cc +++ b/srsenb/src/stack/mac/nr/sched_nr_helpers.cc @@ -28,15 +28,6 @@ namespace sched_nr_impl { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool fill_dci_rar(prb_interval interv, const bwp_params& cell, srsran_dci_dl_nr_t& dci) -{ - dci.mcs = 5; - dci.ctx.format = srsran_dci_format_nr_1_0; - // TODO: Fill - - return true; -} - template void fill_dci_common(const slot_ue& ue, const bwp_params& bwp_cfg, DciDlOrUl& dci) { @@ -63,6 +54,36 @@ void fill_dci_common(const slot_ue& ue, const bwp_params& bwp_cfg, DciDlOrUl& dc dci.time_domain_assigment = 0; } +bool fill_dci_rar(prb_interval interv, uint16_t ra_rnti, const bwp_params& bwp_cfg, srsran_dci_dl_nr_t& dci) +{ + dci.mcs = 5; + dci.ctx.format = srsran_dci_format_nr_rar; + dci.ctx.ss_type = srsran_search_space_type_rar; + dci.ctx.rnti_type = srsran_rnti_type_ra; + dci.ctx.rnti = ra_rnti; + dci.ctx.coreset_id = bwp_cfg.cfg.pdcch.ra_search_space.coreset_id; + dci.freq_domain_assigment = srsran_ra_nr_type1_riv(bwp_cfg.cfg.rb_width, interv.start(), interv.length()); + // TODO: Fill + + return true; +} + +bool fill_dci_msg3(const slot_ue& ue, const bwp_params& bwp_cfg, srsran_dci_ul_nr_t& msg3_dci) +{ + msg3_dci.ctx.coreset_id = ue.cfg->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_common(ue, bwp_cfg, msg3_dci); + + return true; +} + void fill_dl_dci_ue_fields(const slot_ue& ue, const bwp_params& bwp_cfg, uint32_t ss_id, @@ -76,9 +97,9 @@ void fill_dl_dci_ue_fields(const slot_ue& ue, fill_dci_common(ue, bwp_cfg, dci); if (dci.ctx.format == srsran_dci_format_nr_1_0) { - dci.harq_feedback = ue.cfg->phy().harq_ack.dl_data_to_ul_ack[ue.pdsch_tti.sf_idx()] - 1; + dci.harq_feedback = ue.cfg->phy().harq_ack.dl_data_to_ul_ack[ue.pdsch_slot.slot_idx()] - 1; } else { - dci.harq_feedback = ue.pdsch_tti.sf_idx(); + dci.harq_feedback = ue.pdsch_slot.slot_idx(); } } diff --git a/srsenb/src/stack/mac/nr/sched_nr_pdcch.cc b/srsenb/src/stack/mac/nr/sched_nr_pdcch.cc index 8f4368222..5844213d2 100644 --- a/srsenb/src/stack/mac/nr/sched_nr_pdcch.cc +++ b/srsenb/src/stack/mac/nr/sched_nr_pdcch.cc @@ -34,7 +34,8 @@ coreset_region::coreset_region(const bwp_params& bwp_cfg_, coreset_id(coreset_id_), slot_idx(slot_idx_), pdcch_dl_list(dl_list_), - pdcch_ul_list(ul_list_) + pdcch_ul_list(ul_list_), + rar_cce_list(bwp_cfg_.rar_cce_list) { const bool* res_active = &coreset_cfg->freq_resources[0]; nof_freq_res = std::count(res_active, res_active + SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE, true); @@ -190,6 +191,8 @@ srsran::span coreset_region::get_cce_loc_table(const alloc_recor return record.ue->cfg->cce_pos_list(record.ss_id)[slot_idx][record.aggr_idx]; case pdcch_grant_type_t::ul_data: return record.ue->cfg->cce_pos_list(record.ss_id)[slot_idx][record.aggr_idx]; + case pdcch_grant_type_t::rar: + return rar_cce_list[slot_idx][record.aggr_idx]; default: break; } diff --git a/srsenb/src/stack/mac/nr/sched_nr_rb_grid.cc b/srsenb/src/stack/mac/nr/sched_nr_rb_grid.cc index 33ff84d8f..6391c82f3 100644 --- a/srsenb/src/stack/mac/nr/sched_nr_rb_grid.cc +++ b/srsenb/src/stack/mac/nr/sched_nr_rb_grid.cc @@ -81,9 +81,9 @@ alloc_result bwp_slot_allocator::alloc_rar_and_msg3(uint32_t { static const uint32_t msg3_nof_prbs = 3, m = 0; - bwp_slot_grid& bwp_pdcch_slot = bwp_grid[pdcch_tti]; - tti_point msg3_tti = pdcch_tti + cfg.pusch_ra_list[m].msg3_delay; - bwp_slot_grid& bwp_msg3_slot = bwp_grid[msg3_tti]; + 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); if (ret != alloc_result::success) { return ret; @@ -125,41 +125,37 @@ alloc_result bwp_slot_allocator::alloc_rar_and_msg3(uint32_t // Generate DCI for RAR pdcch_dl_t& pdcch = bwp_pdcch_slot.dl_pdcchs.back(); - if (not fill_dci_rar(interv, *bwp_grid.cfg, pdcch.dci)) { + if (not fill_dci_rar(interv, rar.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; } // Generate RAR PDSCH - bwp_pdcch_slot.dl_prbs.add(interv); + bwp_pdcch_slot.dl_prbs |= interv; // Generate Msg3 grants in PUSCH uint32_t last_msg3 = msg3_rbs.start(); const int mcs = 0, max_harq_msg3_retx = 4; int dai = 0; srsran_slot_cfg_t slot_cfg; - slot_cfg.idx = msg3_tti.sf_idx(); + slot_cfg.idx = msg3_slot.slot_idx(); for (const auto& grant : rar.msg3_grant) { - slot_ue& ue = ues[grant.temp_crnti]; - bool success = ue.h_ul->new_tx( - msg3_tti, msg3_tti, prb_interval{last_msg3, last_msg3 + msg3_nof_prbs}, mcs, 100, max_harq_msg3_retx); + slot_ue& ue = ues[grant.temp_crnti]; + prb_interval msg3_interv{last_msg3, last_msg3 + msg3_nof_prbs}; + bool success = ue.h_ul->new_tx(msg3_slot, msg3_slot, msg3_interv, mcs, 100, max_harq_msg3_retx); srsran_assert(success, "Failed to allocate Msg3"); last_msg3 += msg3_nof_prbs; - srsran_dci_ul_nr_t msg3_dci; // Create dummy Msg3 DCI - msg3_dci.ctx.coreset_id = 1; - msg3_dci.ctx.rnti_type = srsran_rnti_type_ra; - msg3_dci.ctx.ss_type = srsran_search_space_type_rar; - msg3_dci.ctx.format = srsran_dci_format_nr_0_0; - msg3_dci.cc_id = cfg.cc; - msg3_dci.bwp_id = cfg.bwp_id; - msg3_dci.rv = 0; - msg3_dci.mcs = 0; - msg3_dci.time_domain_assigment = dai++; + pdcch_ul_t msg3_pdcch; + fill_dci_msg3(ue, *bwp_grid.cfg, msg3_pdcch.dci); + msg3_pdcch.dci.time_domain_assigment = dai++; bwp_msg3_slot.puschs.emplace_back(); pusch_t& pusch = bwp_msg3_slot.puschs.back(); - success = ue.cfg->phy().get_pusch_cfg(slot_cfg, msg3_dci, pusch.sch); + success = ue.cfg->phy().get_pusch_cfg(slot_cfg, msg3_pdcch.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(); + if (ue.h_ul->nof_retx() > 0) { + bwp_pdcch_slot.ul_pdcchs.push_back(msg3_pdcch); + } } bwp_msg3_slot.ul_prbs.add(msg3_rbs); @@ -177,9 +173,9 @@ alloc_result bwp_slot_allocator::alloc_pdsch(slot_ue& ue, const prb_grant& dl_gr logger.warning("SCHED: Trying to allocate PDSCH for rnti=0x%x with no available HARQs", ue.rnti); return alloc_result::no_rnti_opportunity; } - bwp_slot_grid& bwp_pdcch_slot = bwp_grid[ue.pdcch_tti]; - bwp_slot_grid& bwp_pdsch_slot = bwp_grid[ue.pdsch_tti]; - bwp_slot_grid& bwp_uci_slot = bwp_grid[ue.uci_tti]; + 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]; if (not bwp_pdsch_slot.is_dl) { logger.warning("SCHED: Trying to allocate PDSCH in TDD non-DL slot index=%d", bwp_pdsch_slot.slot_idx); return alloc_result::no_sch_space; @@ -209,10 +205,10 @@ alloc_result bwp_slot_allocator::alloc_pdsch(slot_ue& ue, const prb_grant& dl_gr srsran_assert(ue.cfg->ue_cfg()->fixed_dl_mcs >= 0, "Dynamic MCS not yet supported"); int mcs = ue.cfg->ue_cfg()->fixed_dl_mcs; int tbs = 100; - bool ret = ue.h_dl->new_tx(ue.pdsch_tti, ue.uci_tti, dl_grant, mcs, tbs, 4); + bool ret = ue.h_dl->new_tx(ue.pdsch_slot, ue.uci_slot, dl_grant, mcs, tbs, 4); srsran_assert(ret, "Failed to allocate DL HARQ"); } else { - bool ret = ue.h_dl->new_retx(ue.pdsch_tti, ue.uci_tti, dl_grant); + bool ret = ue.h_dl->new_retx(ue.pdsch_slot, ue.uci_slot, dl_grant); srsran_assert(ret, "Failed to allocate DL HARQ retx"); } @@ -239,10 +235,11 @@ alloc_result bwp_slot_allocator::alloc_pdsch(slot_ue& ue, const prb_grant& dl_gr bwp_pdsch_slot.pdschs.emplace_back(); pdsch_t& pdsch = bwp_pdsch_slot.pdschs.back(); srsran_slot_cfg_t slot_cfg; - slot_cfg.idx = ue.pdsch_tti.sf_idx(); + slot_cfg.idx = ue.pdsch_slot.slot_idx(); bool ret = ue.cfg->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 { @@ -252,10 +249,10 @@ alloc_result bwp_slot_allocator::alloc_pdsch(slot_ue& ue, const prb_grant& dl_gr return alloc_result::success; } -alloc_result bwp_slot_allocator::alloc_pusch(slot_ue& ue, const rbg_bitmap& ul_mask) +alloc_result bwp_slot_allocator::alloc_pusch(slot_ue& ue, const prb_grant& ul_prbs) { - auto& bwp_pdcch_slot = bwp_grid[ue.pdcch_tti]; - auto& bwp_pusch_slot = bwp_grid[ue.pusch_tti]; + 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; @@ -265,9 +262,8 @@ alloc_result bwp_slot_allocator::alloc_pusch(slot_ue& ue, const rbg_bitmap& ul_m logger.warning("SCHED: Trying to allocate PUSCH for rnti=0x%x with no available HARQs", ue.rnti); return alloc_result::no_rnti_opportunity; } - pdcch_ul_list_t& pdcchs = bwp_pdcch_slot.ul_pdcchs; - const rbg_bitmap& pusch_mask = bwp_pusch_slot.ul_prbs.rbgs(); - if ((pusch_mask & ul_mask).any()) { + pdcch_ul_list_t& pdcchs = bwp_pdcch_slot.ul_pdcchs; + if (bwp_pusch_slot.ul_prbs.collides(ul_prbs)) { return alloc_result::sch_collision; } const uint32_t aggr_idx = 2, ss_id = 1; @@ -281,10 +277,11 @@ alloc_result bwp_slot_allocator::alloc_pusch(slot_ue& ue, const rbg_bitmap& ul_m srsran_assert(ue.cfg->ue_cfg()->fixed_ul_mcs >= 0, "Dynamic MCS not yet supported"); int mcs = ue.cfg->ue_cfg()->fixed_ul_mcs; int tbs = 100; - bool success = ue.h_ul->new_tx(ue.pusch_tti, ue.pusch_tti, ul_mask, mcs, tbs, ue.cfg->ue_cfg()->maxharq_tx); + bool success = ue.h_ul->new_tx(ue.pusch_slot, ue.pusch_slot, ul_prbs, mcs, tbs, ue.cfg->ue_cfg()->maxharq_tx); srsran_assert(success, "Failed to allocate UL HARQ"); } else { - srsran_assert(ue.h_ul->new_retx(ue.pusch_tti, ue.pusch_tti, ul_mask), "Failed to allocate UL HARQ retx"); + bool success = ue.h_ul->new_retx(ue.pusch_slot, ue.pusch_slot, ul_prbs); + srsran_assert(success, "Failed to allocate UL HARQ retx"); } // Allocation Successful @@ -293,11 +290,11 @@ alloc_result bwp_slot_allocator::alloc_pusch(slot_ue& ue, const rbg_bitmap& ul_m fill_ul_dci_ue_fields(ue, *bwp_grid.cfg, ss_id, pdcch.dci.ctx.location, pdcch.dci); pdcch.dci_cfg = ue.cfg->phy().get_dci_cfg(); // Generate PUSCH - bwp_pusch_slot.ul_prbs |= ul_mask; + bwp_pusch_slot.ul_prbs |= ul_prbs; bwp_pusch_slot.puschs.emplace_back(); pusch_t& pusch = bwp_pusch_slot.puschs.back(); srsran_slot_cfg_t slot_cfg; - slot_cfg.idx = ue.pusch_tti.sf_idx(); + slot_cfg.idx = ue.pusch_slot.to_uint(); bool success = ue.cfg->phy().get_pusch_cfg(slot_cfg, pdcch.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(); diff --git a/srsenb/src/stack/mac/nr/sched_nr_ue.cc b/srsenb/src/stack/mac/nr/sched_nr_ue.cc index 2a3660afc..0b5061d80 100644 --- a/srsenb/src/stack/mac/nr/sched_nr_ue.cc +++ b/srsenb/src/stack/mac/nr/sched_nr_ue.cc @@ -25,7 +25,7 @@ namespace srsenb { namespace sched_nr_impl { -slot_ue::slot_ue(uint16_t rnti_, tti_point tti_rx_, uint32_t cc_) : rnti(rnti_), tti_rx(tti_rx_), cc(cc_) {} +slot_ue::slot_ue(uint16_t rnti_, slot_point slot_rx_, uint32_t cc_) : rnti(rnti_), slot_rx(slot_rx_), cc(cc_) {} /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -37,41 +37,42 @@ ue_carrier::ue_carrier(uint16_t rnti_, const ue_cfg_t& uecfg_, const sched_cell_ harq_ent(cell_params_.nof_prb()) {} -void ue_carrier::new_tti(tti_point pdcch_tti, const ue_cfg_t& uecfg_) +void ue_carrier::new_slot(slot_point pdcch_slot, const ue_cfg_t& uecfg_) { if (bwp_cfg.ue_cfg() != &uecfg_) { bwp_cfg = bwp_ue_cfg(rnti, cell_params.bwps[0], uecfg_); } - harq_ent.new_tti(pdcch_tti - TX_ENB_DELAY); + harq_ent.new_slot(pdcch_slot - TX_ENB_DELAY); } -slot_ue ue_carrier::try_reserve(tti_point pdcch_tti) +slot_ue ue_carrier::try_reserve(slot_point pdcch_slot) { - tti_point tti_rx = pdcch_tti - TX_ENB_DELAY; + slot_point slot_rx = pdcch_slot - TX_ENB_DELAY; // copy cc-specific parameters and find available HARQs - slot_ue sfu(rnti, tti_rx, cc); + slot_ue sfu(rnti, slot_rx, cc); sfu.cfg = &bwp_cfg; - sfu.pdcch_tti = pdcch_tti; + sfu.pdcch_slot = pdcch_slot; const uint32_t k0 = 0; - sfu.pdsch_tti = sfu.pdcch_tti + k0; + sfu.pdsch_slot = sfu.pdcch_slot + k0; uint32_t k1 = - sfu.cfg->phy().harq_ack.dl_data_to_ul_ack[sfu.pdsch_tti.sf_idx() % sfu.cfg->phy().harq_ack.nof_dl_data_to_ul_ack]; - sfu.uci_tti = sfu.pdsch_tti + k1; - uint32_t k2 = bwp_cfg.active_bwp().pusch_ra_list[0].K; - sfu.pusch_tti = sfu.pdcch_tti + k2; - sfu.dl_cqi = dl_cqi; - sfu.ul_cqi = ul_cqi; + sfu.cfg->phy() + .harq_ack.dl_data_to_ul_ack[sfu.pdsch_slot.slot_idx() % sfu.cfg->phy().harq_ack.nof_dl_data_to_ul_ack]; + sfu.uci_slot = sfu.pdsch_slot + k1; + uint32_t k2 = bwp_cfg.active_bwp().pusch_ra_list[0].K; + sfu.pusch_slot = sfu.pdcch_slot + k2; + sfu.dl_cqi = dl_cqi; + sfu.ul_cqi = ul_cqi; const srsran_tdd_config_nr_t& tdd_cfg = cell_params.cell_cfg.tdd; - if (srsran_tdd_nr_is_dl(&tdd_cfg, 0, sfu.pdsch_tti.sf_idx())) { + if (srsran_tdd_nr_is_dl(&tdd_cfg, 0, sfu.pdsch_slot.slot_idx())) { // If DL enabled sfu.h_dl = harq_ent.find_pending_dl_retx(); if (sfu.h_dl == nullptr) { sfu.h_dl = harq_ent.find_empty_dl_harq(); } } - if (srsran_tdd_nr_is_ul(&tdd_cfg, 0, sfu.pusch_tti.sf_idx())) { + if (srsran_tdd_nr_is_ul(&tdd_cfg, 0, sfu.pusch_slot.slot_idx())) { // If UL enabled sfu.h_ul = harq_ent.find_pending_ul_retx(); if (sfu.h_ul == nullptr) { @@ -104,12 +105,12 @@ void ue::set_cfg(const ue_cfg_t& cfg) ue_cfg = cfg; } -slot_ue ue::try_reserve(tti_point pdcch_tti, uint32_t cc) +slot_ue ue::try_reserve(slot_point pdcch_slot, uint32_t cc) { if (carriers[cc] == nullptr) { return slot_ue(); } - slot_ue sfu = carriers[cc]->try_reserve(pdcch_tti); + slot_ue sfu = carriers[cc]->try_reserve(pdcch_slot); if (sfu.empty()) { return slot_ue(); } diff --git a/srsenb/src/stack/mac/nr/sched_nr_worker.cc b/srsenb/src/stack/mac/nr/sched_nr_worker.cc index 26d5fd13f..449b56179 100644 --- a/srsenb/src/stack/mac/nr/sched_nr_worker.cc +++ b/srsenb/src/stack/mac/nr/sched_nr_worker.cc @@ -29,6 +29,13 @@ slot_cc_worker::slot_cc_worker(serv_cell_manager& cc_sched) : cell(cc_sched), cfg(cc_sched.cfg), bwp_alloc(cc_sched.bwps[0].grid), logger(srslog::fetch_basic_logger("MAC")) {} +void slot_cc_worker::enqueue_cc_event(srsran::move_callback ev) +{ + std::lock_guard lock(feedback_mutex); + pending_events.emplace_back(); + pending_events.back() = std::move(ev); +} + void slot_cc_worker::enqueue_cc_feedback(uint16_t rnti, feedback_callback_t fdbk) { std::lock_guard lock(feedback_mutex); @@ -42,7 +49,13 @@ void slot_cc_worker::run_feedback(ue_map_t& ue_db) { std::lock_guard lock(feedback_mutex); tmp_feedback_to_run.swap(pending_feedback); + tmp_events_to_run.swap(pending_events); + } + + for (srsran::move_callback& ev : tmp_events_to_run) { + ev(); } + tmp_events_to_run.clear(); for (feedback_t& f : tmp_feedback_to_run) { if (ue_db.contains(f.rnti) and ue_db[f.rnti]->carriers[cfg.cc] != nullptr) { @@ -55,10 +68,10 @@ void slot_cc_worker::run_feedback(ue_map_t& ue_db) } /// Called at the beginning of TTI in a locked context, to reserve available UE resources -void slot_cc_worker::start(tti_point pdcch_tti, ue_map_t& ue_db) +void slot_cc_worker::start(slot_point pdcch_slot, ue_map_t& ue_db) { srsran_assert(not running(), "scheduler worker::start() called for active worker"); - tti_rx = pdcch_tti - TX_ENB_DELAY; + slot_rx = pdcch_slot - TX_ENB_DELAY; // Run pending cell feedback run_feedback(ue_db); @@ -71,15 +84,15 @@ void slot_cc_worker::start(tti_point pdcch_tti, ue_map_t& ue_db) continue; } - u.carriers[cfg.cc]->new_tti(pdcch_tti, u.cfg()); + u.carriers[cfg.cc]->new_slot(pdcch_slot, u.cfg()); - slot_ues.insert(rnti, u.try_reserve(pdcch_tti, cfg.cc)); + slot_ues.insert(rnti, u.try_reserve(pdcch_slot, cfg.cc)); if (slot_ues[rnti].empty()) { // Failed to generate slot UE because UE has no conditions for DL/UL tx slot_ues.erase(rnti); continue; } - // UE acquired successfully for scheduling in this {tti, cc} + // UE acquired successfully for scheduling in this {slot, cc} } } @@ -87,7 +100,7 @@ void slot_cc_worker::run() { srsran_assert(running(), "scheduler worker::run() called for non-active worker"); - bwp_alloc.new_slot(tti_rx + TX_ENB_DELAY); + bwp_alloc.new_slot(slot_rx + TX_ENB_DELAY); // Allocate pending RARs cell.bwps[0].ra.run_slot(bwp_alloc, slot_ues); @@ -101,7 +114,7 @@ void slot_cc_worker::run() // releases UE resources slot_ues.clear(); - tti_rx = {}; + slot_rx = {}; } void slot_cc_worker::finish() @@ -111,6 +124,9 @@ void slot_cc_worker::finish() void slot_cc_worker::alloc_dl_ues() { + if (not cfg.sched_cfg.pdsch_enabled) { + return; + } if (slot_ues.empty()) { return; } @@ -119,13 +135,15 @@ void slot_cc_worker::alloc_dl_ues() return; } - rbgmask_t dlmask(cfg.bwps[0].N_rbg); - dlmask.fill(0, dlmask.size(), true); - bwp_alloc.alloc_pdsch(ue, dlmask); + prb_interval prbs(0, cfg.bwps[0].N_rbg); + bwp_alloc.alloc_pdsch(ue, prbs); } void slot_cc_worker::alloc_ul_ues() { + if (not cfg.sched_cfg.pusch_enabled) { + return; + } if (slot_ues.empty()) { return; } @@ -134,14 +152,13 @@ void slot_cc_worker::alloc_ul_ues() return; } - rbgmask_t ulmask(cfg.bwps[0].N_rbg); - ulmask.fill(0, ulmask.size(), true); - bwp_alloc.alloc_pusch(ue, ulmask); + prb_interval prbs(0, cfg.bwps[0].N_rbg); + bwp_alloc.alloc_pusch(ue, prbs); } void slot_cc_worker::log_result() const { - const bwp_slot_grid& bwp_slot = cell.bwps[0].grid[tti_rx + TX_ENB_DELAY]; + const bwp_slot_grid& bwp_slot = cell.bwps[0].grid[slot_rx + TX_ENB_DELAY]; for (const pdcch_dl_t& pdcch : bwp_slot.dl_pdcchs) { fmt::memory_buffer fmtbuf; if (pdcch.dci.ctx.rnti_type == srsran_rnti_type_c) { @@ -155,8 +172,8 @@ void slot_cc_worker::log_result() const ue.h_dl->nof_retx(), srsran_dci_format_nr_string(pdcch.dci.ctx.format), pdcch.dci.dai, - ue.pdsch_tti, - ue.uci_tti); + ue.pdsch_slot, + ue.uci_slot); } else if (pdcch.dci.ctx.rnti_type == srsran_rnti_type_ra) { fmt::format_to(fmtbuf, "SCHED: DL RAR, cc={}", cell.cfg.cc); } else { @@ -177,7 +194,17 @@ void slot_cc_worker::log_result() const pdcch.dci.pid, ue.h_dl->nof_retx(), srsran_dci_format_nr_string(pdcch.dci.ctx.format), - ue.pusch_tti); + ue.pusch_slot); + } else if (pdcch.dci.ctx.rnti_type == srsran_rnti_type_tc) { + const slot_ue& ue = slot_ues[pdcch.dci.ctx.rnti]; + fmt::format_to(fmtbuf, + "SCHED: UL Msg3, cc={}, tc-rnti=0x{:x}, pid={}, nrtx={}, f={}, tti_pusch={}", + cell.cfg.cc, + ue.rnti, + pdcch.dci.pid, + ue.h_dl->nof_retx(), + srsran_dci_format_nr_string(pdcch.dci.ctx.format), + ue.pusch_slot); } else { fmt::format_to(fmtbuf, "SCHED: unknown rnti format"); } @@ -207,18 +234,23 @@ void sched_worker_manager::enqueue_event(uint16_t rnti, srsran::move_callback ev) +{ + cc_worker_list[cc]->worker.enqueue_cc_event(std::move(ev)); +} + +void sched_worker_manager::run_slot(slot_point slot_tx, uint32_t cc, dl_sched_t& dl_res, ul_sched_t& ul_res) { srsran::bounded_vector waiting_cvars; { std::unique_lock lock(slot_mutex); - while (current_tti.is_valid() and current_tti != tti_tx) { + while (current_slot.valid() and current_slot != slot_tx) { // Wait for previous slot to finish - cc_worker_list[cc]->waiting = true; + cc_worker_list[cc]->waiting++; cc_worker_list[cc]->cvar.wait(lock); - cc_worker_list[cc]->waiting = false; + cc_worker_list[cc]->waiting--; } - if (not current_tti.is_valid()) { + if (not current_slot.valid()) { /* First Worker to start slot */ // process non-cc specific feedback if pending (e.g. SRs, buffer updates, UE config) for UEs with CA @@ -235,10 +267,10 @@ void sched_worker_manager::run_slot(tti_point tti_tx, uint32_t cc, dl_sched_t& d } // mark the start of slot. awake remaining workers if locking on the mutex - current_tti = tti_tx; + current_slot = slot_tx; worker_count.store(static_cast(cc_worker_list.size()), std::memory_order_relaxed); for (auto& w : cc_worker_list) { - if (w->waiting) { + if (w->waiting > 0) { waiting_cvars.push_back(&w->cvar); } } @@ -260,25 +292,25 @@ void sched_worker_manager::run_slot(tti_point tti_tx, uint32_t cc, dl_sched_t& d } // process pending feedback and pre-cache UE state for slot decision - cc_worker_list[cc]->worker.start(tti_tx, ue_db); + cc_worker_list[cc]->worker.start(slot_tx, ue_db); - // Get {tti, cc} scheduling decision + // Get {slot, cc} scheduling decision cc_worker_list[cc]->worker.run(); // decrement the number of active workers int rem_workers = worker_count.fetch_sub(1, std::memory_order_release) - 1; - srsran_assert(rem_workers >= 0, "invalid number of calls to run_tti(tti, cc)"); + srsran_assert(rem_workers >= 0, "invalid number of calls to run_slot(slot, cc)"); if (rem_workers == 0) { /* Last Worker to finish slot */ // Signal the release of slot if it is the last worker that finished its own generation std::unique_lock lock(slot_mutex); - current_tti = {}; + current_slot = {}; // All the workers of the same slot have finished. Synchronize scheduling decisions with UEs state for (auto& c : cc_worker_list) { c->worker.finish(); - if (c->waiting) { + if (c->waiting > 0) { waiting_cvars.push_back(&c->cvar); } } @@ -291,13 +323,13 @@ void sched_worker_manager::run_slot(tti_point tti_tx, uint32_t cc, dl_sched_t& d } // Copy results to intermediate buffer - save_sched_result(tti_tx, cc, dl_res, ul_res); + save_sched_result(slot_tx, cc, dl_res, ul_res); } -bool sched_worker_manager::save_sched_result(tti_point pdcch_tti, uint32_t cc, dl_sched_t& dl_res, ul_sched_t& ul_res) +bool sched_worker_manager::save_sched_result(slot_point pdcch_slot, uint32_t cc, dl_sched_t& dl_res, ul_sched_t& ul_res) { // NOTE: Unlocked region - auto& bwp_slot = cells[cc]->bwps[0].grid[pdcch_tti]; + auto& bwp_slot = cells[cc]->bwps[0].grid[pdcch_slot]; dl_res.pdcch_dl = bwp_slot.dl_pdcchs; dl_res.pdcch_ul = bwp_slot.ul_pdcchs; @@ -318,7 +350,7 @@ bool sched_worker_manager::save_sched_result(tti_point pdcch_tti, uint32_t cc, d if (phy_cfg != nullptr) { srsran_slot_cfg_t slot_cfg{}; - slot_cfg.idx = pdcch_tti.sf_idx(); + slot_cfg.idx = pdcch_slot.slot_idx(); srsran_uci_cfg_nr_t uci_cfg = {}; srsran_assert(phy_cfg->get_uci_cfg(slot_cfg, ack, uci_cfg), "Error getting UCI CFG"); diff --git a/srsenb/src/stack/mac/nr/ue_nr.cc b/srsenb/src/stack/mac/nr/ue_nr.cc index bc0a00432..6d3f4e289 100644 --- a/srsenb/src/stack/mac/nr/ue_nr.cc +++ b/srsenb/src/stack/mac/nr/ue_nr.cc @@ -25,6 +25,7 @@ #include #include "srsenb/hdr/stack/mac/nr/ue_nr.h" +#include "srsran/common/buffer_pool.h" #include "srsran/common/string_helpers.h" #include "srsran/interfaces/gnb_interfaces.h" @@ -37,7 +38,13 @@ ue_nr::ue_nr(uint16_t rnti_, rlc_interface_mac* rlc_, phy_interface_stack_nr* phy_, srslog::basic_logger& logger_) : - rnti(rnti_), sched(sched_), rrc(rrc_), rlc(rlc_), phy(phy_), logger(logger_) + rnti(rnti_), + sched(sched_), + rrc(rrc_), + rlc(rlc_), + phy(phy_), + logger(logger_), + ue_rlc_buffer(srsran::make_byte_buffer()) {} ue_nr::~ue_nr() {} @@ -91,47 +98,35 @@ uint32_t ue_nr::read_pdu(uint32_t lcid, uint8_t* payload, uint32_t requested_byt return rlc->read_pdu(rnti, lcid, payload, requested_bytes); } -uint8_t* ue_nr::generate_pdu(uint32_t enb_cc_idx, - uint32_t harq_pid, - uint32_t tb_idx, - const sched_interface::dl_sched_pdu_t pdu[sched_interface::MAX_RLC_PDU_LIST], - uint32_t nof_pdu_elems, - uint32_t grant_size) +int ue_nr::generate_pdu(srsran::byte_buffer_t* pdu, uint32_t grant_size) { std::lock_guard lock(mutex); - uint8_t* ret = nullptr; - if (enb_cc_idx < SRSRAN_MAX_CARRIERS && harq_pid < SRSRAN_FDD_NOF_HARQ && tb_idx < SRSRAN_MAX_TB) { - srsran::byte_buffer_t* buffer = nullptr; // TODO: read from scheduler output - buffer->clear(); - - mac_pdu_dl.init_tx(buffer, grant_size); - - // read RLC PDU - ue_rlc_buffer->clear(); - int lcid = 4; - int pdu_len = rlc->read_pdu(rnti, lcid, ue_rlc_buffer->msg, grant_size - 2); - - // Only create PDU if RLC has something to tx - if (pdu_len > 0) { - logger.info("Adding MAC PDU for RNTI=%d", rnti); - ue_rlc_buffer->N_bytes = pdu_len; - logger.info(ue_rlc_buffer->msg, ue_rlc_buffer->N_bytes, "Read %d B from RLC", ue_rlc_buffer->N_bytes); - - // add to MAC PDU and pack - mac_pdu_dl.add_sdu(4, ue_rlc_buffer->msg, ue_rlc_buffer->N_bytes); - mac_pdu_dl.pack(); - } - - if (logger.info.enabled()) { - fmt::memory_buffer str_buffer; - // mac_pdu_dl.to_string(str_buffer); - logger.info("0x%x %s", rnti, srsran::to_c_str(str_buffer)); - } - } else { - logger.error( - "Invalid parameters calling generate_pdu: cc_idx=%d, harq_pid=%d, tb_idx=%d", enb_cc_idx, harq_pid, tb_idx); + + mac_pdu_dl.init_tx(pdu, grant_size); + + // read RLC PDU + ue_rlc_buffer->clear(); + int lcid = 4; + int pdu_len = rlc->read_pdu(rnti, lcid, ue_rlc_buffer->msg, grant_size - 2); + + // Only create PDU if RLC has something to tx + if (pdu_len > 0) { + logger.info("Adding MAC PDU for RNTI=%d", rnti); + ue_rlc_buffer->N_bytes = pdu_len; + logger.info(ue_rlc_buffer->msg, ue_rlc_buffer->N_bytes, "Read %d B from RLC", ue_rlc_buffer->N_bytes); + + // add to MAC PDU and pack + mac_pdu_dl.add_sdu(lcid, ue_rlc_buffer->msg, ue_rlc_buffer->N_bytes); + } + + mac_pdu_dl.pack(); + + if (logger.info.enabled()) { + fmt::memory_buffer str_buffer; + // mac_pdu_dl.to_string(str_buffer); + logger.info("0x%x %s", rnti, srsran::to_c_str(str_buffer)); } - return ret; + return SRSRAN_SUCCESS; } /******* METRICS interface ***************/ diff --git a/srsenb/src/stack/mac/sched_ue_ctrl/sched_dl_cqi.cc b/srsenb/src/stack/mac/sched_ue_ctrl/sched_dl_cqi.cc index fac10f6b7..23679b390 100644 --- a/srsenb/src/stack/mac/sched_ue_ctrl/sched_dl_cqi.cc +++ b/srsenb/src/stack/mac/sched_ue_ctrl/sched_dl_cqi.cc @@ -60,55 +60,47 @@ rbgmask_t sched_dl_cqi::get_optim_rbgmask(const rbgmask_t& dl_mask, uint32_t req return emptymask; } -std::tuple find_min_cqi_rbg(const rbgmask_t& mask, const sched_dl_cqi& dl_cqi) +rbgmask_t find_min_cqi_rbgs(const rbgmask_t& mask, const sched_dl_cqi& dl_cqi, int& min_cqi) { if (mask.none()) { - return std::make_tuple(mask.size(), -1); + min_cqi = -1; + return mask; } - int rbg = mask.find_lowest(0, mask.size()); if (not dl_cqi.subband_cqi_enabled()) { - return std::make_tuple(rbg, dl_cqi.get_wb_cqi_info()); + min_cqi = dl_cqi.get_wb_cqi_info(); + return mask; } - int min_cqi = std::numeric_limits::max(); - uint32_t min_rbg = mask.size(); + rbgmask_t min_mask(mask.size()); + int rbg = mask.find_lowest(0, mask.size()); + min_cqi = std::numeric_limits::max(); for (; rbg != -1; rbg = mask.find_lowest(rbg, mask.size())) { uint32_t sb = dl_cqi.rbg_to_sb_index(rbg); int cqi = dl_cqi.get_subband_cqi(sb); if (cqi < min_cqi) { min_cqi = cqi; - min_rbg = rbg; + min_mask.reset(); + min_mask.set(rbg); + } else if (cqi == min_cqi) { + min_mask.set(rbg); } - rbg = (int)srsran::ceil_div((sb + 1U) * mask.size(), dl_cqi.nof_subbands()); // skip to next subband index + rbg++; } - return min_cqi != std::numeric_limits::max() ? std::make_tuple(min_rbg, min_cqi) : std::make_tuple(0u, -1); -} - -rbgmask_t remove_min_cqi_subband(const rbgmask_t& rbgmask, const sched_dl_cqi& dl_cqi) -{ - std::tuple tup = find_min_cqi_rbg(rbgmask, dl_cqi); - if (std::get<1>(tup) < 0) { - return rbgmask_t(rbgmask.size()); - } - uint32_t sb = dl_cqi.rbg_to_sb_index(std::get<0>(tup)); - uint32_t rbg_begin = sb * rbgmask.size() / dl_cqi.nof_subbands(); - uint32_t rbg_end = srsran::ceil_div((sb + 1) * rbgmask.size(), dl_cqi.nof_subbands()); + min_cqi = min_cqi == std::numeric_limits::max() ? -1 : min_cqi; - rbgmask_t ret(rbgmask); - ret.fill(rbg_begin, rbg_end, false); - return ret; + return min_mask; } -rbgmask_t remove_min_cqi_rbg(const rbgmask_t& rbgmask, const sched_dl_cqi& dl_cqi) +rbgmask_t remove_min_cqi_rbgs(const rbgmask_t& rbgmask, const sched_dl_cqi& dl_cqi) { - std::tuple tup = find_min_cqi_rbg(rbgmask, dl_cqi); - if (std::get<1>(tup) < 0) { - return rbgmask_t(rbgmask.size()); + int min_cqi; + rbgmask_t minmask = find_min_cqi_rbgs(rbgmask, dl_cqi, min_cqi); + if (min_cqi < 0) { + return minmask; } - rbgmask_t ret(rbgmask); - ret.set(std::get<0>(tup), false); - return ret; + minmask = ~minmask & rbgmask; + return minmask; } } // namespace srsenb \ No newline at end of file diff --git a/srsenb/src/stack/mac/sched_ue_ctrl/sched_ue_cell.cc b/srsenb/src/stack/mac/sched_ue_ctrl/sched_ue_cell.cc index c3c985678..98420d3b6 100644 --- a/srsenb/src/stack/mac/sched_ue_ctrl/sched_ue_cell.cc +++ b/srsenb/src/stack/mac/sched_ue_ctrl/sched_ue_cell.cc @@ -188,6 +188,7 @@ int sched_ue_cell::set_dl_wb_cqi(tti_point tti_rx, uint32_t dl_cqi_) CHECK_VALID_CC("DL CQI"); dl_cqi_ctxt.cqi_wb_info(tti_rx, dl_cqi_); check_cc_activation(dl_cqi_); + logger.debug("SCHED: DL CQI cc=%d, cqi=%d", cell_cfg->enb_cc_idx, dl_cqi_); return SRSRAN_SUCCESS; } @@ -196,6 +197,7 @@ int sched_ue_cell::set_dl_sb_cqi(tti_point tti_rx, uint32_t sb_idx, uint32_t dl_ CHECK_VALID_CC("DL CQI"); dl_cqi_ctxt.cqi_sb_info(tti_rx, sb_idx, dl_cqi_); check_cc_activation(dl_cqi_); + logger.debug("SCHED: DL CQI cc=%d, sb_idx=%d, cqi=%d", cell_cfg->enb_cc_idx, sb_idx, dl_cqi_); return SRSRAN_SUCCESS; } @@ -284,8 +286,9 @@ int sched_ue_cell::get_ul_cqi() const int sched_ue_cell::get_dl_cqi(const rbgmask_t& rbgs) const { - float dl_cqi = std::get<1>(find_min_cqi_rbg(rbgs, dl_cqi_ctxt)); - return std::max(0, (int)std::min(dl_cqi + dl_cqi_coeff, 15.0f)); + int min_cqi; + find_min_cqi_rbgs(rbgs, dl_cqi_ctxt, min_cqi); + return std::max(0, (int)std::min(static_cast(min_cqi) + dl_cqi_coeff, 15.0f)); } int sched_ue_cell::get_dl_cqi() const @@ -565,12 +568,12 @@ bool find_optimal_rbgmask(const sched_ue_cell& ue_cell, // We start with largest RBG allocation and continue removing RBGs. However, there is no guarantee this is // going to be the optimal solution - // Subtract whole CQI subbands until objective is not met + // Subtract RBGs with lowest CQI until objective is not met // TODO: can be optimized rbgmask_t smaller_mask; tbs_info tb2; do { - smaller_mask = remove_min_cqi_subband(newtxmask, ue_cell.dl_cqi()); + smaller_mask = remove_min_cqi_rbgs(newtxmask, ue_cell.dl_cqi()); tb2 = compute_mcs_and_tbs_lower_bound(ue_cell, tti_tx_dl, smaller_mask, dci_format); if (tb2.tbs_bytes >= (int)req_bytes.stop() or tb.tbs_bytes <= tb2.tbs_bytes) { tb = tb2; @@ -580,14 +583,6 @@ bool find_optimal_rbgmask(const sched_ue_cell& ue_cell, if (tb.tbs_bytes <= (int)req_bytes.stop()) { return true; } - do { - smaller_mask = remove_min_cqi_rbg(newtxmask, ue_cell.dl_cqi()); - tb2 = compute_mcs_and_tbs_lower_bound(ue_cell, tti_tx_dl, smaller_mask, dci_format); - if (tb2.tbs_bytes >= (int)req_bytes.stop() or tb.tbs_bytes <= tb2.tbs_bytes) { - tb = tb2; - newtxmask = smaller_mask; - } - } while (tb2.tbs_bytes > (int)req_bytes.stop()); return true; } diff --git a/srsenb/src/stack/ngap/CMakeLists.txt b/srsenb/src/stack/ngap/CMakeLists.txt index fdd14feb4..a4d2ca9c6 100644 --- a/srsenb/src/stack/ngap/CMakeLists.txt +++ b/srsenb/src/stack/ngap/CMakeLists.txt @@ -18,5 +18,5 @@ # and at http://www.gnu.org/licenses/. # -set(SOURCES ngap.cc) +set(SOURCES ngap.cc ngap_ue.cc ngap_ue_proc.cc) add_library(srsgnb_ngap STATIC ${SOURCES}) diff --git a/srsenb/src/stack/ngap/ngap.cc b/srsenb/src/stack/ngap/ngap.cc index 338c5c742..be2265301 100644 --- a/srsenb/src/stack/ngap/ngap.cc +++ b/srsenb/src/stack/ngap/ngap.cc @@ -20,6 +20,7 @@ */ #include "srsenb/hdr/stack/ngap/ngap.h" +#include "srsenb/hdr/stack/ngap/ngap_ue.h" #include "srsran/common/int_helpers.h" using srsran::s1ap_mccmnc_to_plmn; @@ -111,6 +112,8 @@ ngap::ngap(srsran::task_sched_handle task_sched_, amf_task_queue = task_sched.make_task_queue(); } +ngap::~ngap() {} + int ngap::init(const ngap_args_t& args_, rrc_interface_ngap_nr* rrc_) { rrc = rrc_; @@ -213,7 +216,7 @@ void ngap::initial_ue(uint16_t rnti, asn1::ngap_nr::rrcestablishment_cause_e cause, srsran::unique_byte_buffer_t pdu) { - std::unique_ptr ue_ptr{new ue{this}}; + std::unique_ptr ue_ptr{new ue{this, rrc, logger}}; ue_ptr->ctxt.rnti = rnti; ue_ptr->ctxt.gnb_cc_idx = gnb_cc_idx; ue* u = users.add_user(std::move(ue_ptr)); @@ -221,7 +224,7 @@ void ngap::initial_ue(uint16_t rnti, logger.error("Failed to add rnti=0x%x", rnti); return; } - u->send_initialuemessage(cause, std::move(pdu), false); + u->send_initial_ue_message(cause, std::move(pdu), false); } void ngap::initial_ue(uint16_t rnti, @@ -230,7 +233,7 @@ void ngap::initial_ue(uint16_t rnti, srsran::unique_byte_buffer_t pdu, uint32_t s_tmsi) { - std::unique_ptr ue_ptr{new ue{this}}; + std::unique_ptr ue_ptr{new ue{this, rrc, logger}}; ue_ptr->ctxt.rnti = rnti; ue_ptr->ctxt.gnb_cc_idx = gnb_cc_idx; ue* u = users.add_user(std::move(ue_ptr)); @@ -238,7 +241,7 @@ void ngap::initial_ue(uint16_t rnti, logger.error("Failed to add rnti=0x%x", rnti); return; } - u->send_initialuemessage(cause, std::move(pdu), true, s_tmsi); + u->send_initial_ue_message(cause, std::move(pdu), true, s_tmsi); } void ngap::ue_ctxt_setup_complete(uint16_t rnti) @@ -250,6 +253,27 @@ void ngap::ue_ctxt_setup_complete(uint16_t rnti) u->ue_ctxt_setup_complete(); } +void ngap::ue_notify_rrc_reconf_complete(uint16_t rnti, bool outcome) +{ + ue* u = users.find_ue_rnti(rnti); + if (u == nullptr) { + return; + } + u->notify_rrc_reconf_complete(outcome); +} + +void ngap::write_pdu(uint16_t rnti, srsran::unique_byte_buffer_t pdu) +{ + logger.info(pdu->msg, pdu->N_bytes, "Received RRC SDU"); + + ue* u = users.find_ue_rnti(rnti); + if (u == nullptr) { + logger.info("The rnti=0x%x does not exist", rnti); + return; + } + u->send_ul_nas_transport(std::move(pdu)); +} + /********************************************************* * ngap::user_list class *********************************************************/ @@ -377,11 +401,11 @@ bool ngap::handle_ngap_rx_pdu(srsran::byte_buffer_t* pdu) switch (rx_pdu.type().value) { case ngap_pdu_c::types_opts::init_msg: - return handle_initiatingmessage(rx_pdu.init_msg()); + return handle_initiating_message(rx_pdu.init_msg()); case ngap_pdu_c::types_opts::successful_outcome: - return handle_successfuloutcome(rx_pdu.successful_outcome()); + return handle_successful_outcome(rx_pdu.successful_outcome()); case ngap_pdu_c::types_opts::unsuccessful_outcome: - return handle_unsuccessfuloutcome(rx_pdu.unsuccessful_outcome()); + return handle_unsuccessful_outcome(rx_pdu.unsuccessful_outcome()); default: logger.error("Unhandled PDU type %d", rx_pdu.type().value); return false; @@ -390,42 +414,44 @@ bool ngap::handle_ngap_rx_pdu(srsran::byte_buffer_t* pdu) return true; } -bool ngap::handle_initiatingmessage(const asn1::ngap_nr::init_msg_s& msg) +bool ngap::handle_initiating_message(const asn1::ngap_nr::init_msg_s& msg) { switch (msg.value.type().value) { case ngap_elem_procs_o::init_msg_c::types_opts::dl_nas_transport: - return handle_dlnastransport(msg.value.dl_nas_transport()); + return handle_dl_nas_transport(msg.value.dl_nas_transport()); case ngap_elem_procs_o::init_msg_c::types_opts::init_context_setup_request: - return handle_initialctxtsetuprequest(msg.value.init_context_setup_request()); + return handle_initial_ctxt_setup_request(msg.value.init_context_setup_request()); + case ngap_elem_procs_o::init_msg_c::types_opts::ue_context_release_cmd: + return handle_ue_ctxt_release_cmd(msg.value.ue_context_release_cmd()); default: logger.error("Unhandled initiating message: %s", msg.value.type().to_string()); } return true; } -bool ngap::handle_successfuloutcome(const successful_outcome_s& msg) +bool ngap::handle_successful_outcome(const successful_outcome_s& msg) { switch (msg.value.type().value) { case ngap_elem_procs_o::successful_outcome_c::types_opts::ng_setup_resp: - return handle_ngsetupresponse(msg.value.ng_setup_resp()); + return handle_ng_setup_response(msg.value.ng_setup_resp()); default: logger.error("Unhandled successful outcome message: %s", msg.value.type().to_string()); } return true; } -bool ngap::handle_unsuccessfuloutcome(const asn1::ngap_nr::unsuccessful_outcome_s& msg) +bool ngap::handle_unsuccessful_outcome(const asn1::ngap_nr::unsuccessful_outcome_s& msg) { switch (msg.value.type().value) { case ngap_elem_procs_o::unsuccessful_outcome_c::types_opts::ng_setup_fail: - return handle_ngsetupfailure(msg.value.ng_setup_fail()); + return handle_ng_setup_failure(msg.value.ng_setup_fail()); default: logger.error("Unhandled unsuccessful outcome message: %s", msg.value.type().to_string()); } return true; } -bool ngap::handle_ngsetupresponse(const asn1::ngap_nr::ng_setup_resp_s& msg) +bool ngap::handle_ng_setup_response(const asn1::ngap_nr::ng_setup_resp_s& msg) { ngsetupresponse = msg; amf_connected = true; @@ -436,7 +462,7 @@ bool ngap::handle_ngsetupresponse(const asn1::ngap_nr::ng_setup_resp_s& msg) return true; } -bool ngap::handle_ngsetupfailure(const asn1::ngap_nr::ng_setup_fail_s& msg) +bool ngap::handle_ng_setup_failure(const asn1::ngap_nr::ng_setup_fail_s& msg) { std::string cause = get_cause(msg.protocol_ies.cause.value); logger.error("NG Setup Failure. Cause: %s", cause.c_str()); @@ -444,14 +470,18 @@ bool ngap::handle_ngsetupfailure(const asn1::ngap_nr::ng_setup_fail_s& msg) return true; } -bool ngap::handle_dlnastransport(const asn1::ngap_nr::dl_nas_transport_s& msg) +bool ngap::handle_dl_nas_transport(const asn1::ngap_nr::dl_nas_transport_s& msg) { if (msg.ext) { logger.warning("Not handling NGAP message extension"); } ue* u = handle_ngapmsg_ue_id(msg.protocol_ies.ran_ue_ngap_id.value.value, msg.protocol_ies.amf_ue_ngap_id.value.value); + if (u == nullptr) { + logger.warning("Couldn't find user with ran_ue_ngap_id %d and %d", + msg.protocol_ies.ran_ue_ngap_id.value.value, + msg.protocol_ies.amf_ue_ngap_id.value.value); return false; } @@ -479,49 +509,79 @@ bool ngap::handle_dlnastransport(const asn1::ngap_nr::dl_nas_transport_s& msg) logger.warning("Not handling AllowedNSSAI"); } - // TODO: Pass NAS PDU once RRC interface is ready - /* srsran::unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + srsran::unique_byte_buffer_t pdu = srsran::make_byte_buffer(); if (pdu == nullptr) { logger.error("Fatal Error: Couldn't allocate buffer in ngap::run_thread()."); return false; } memcpy(pdu->msg, msg.protocol_ies.nas_pdu.value.data(), msg.protocol_ies.nas_pdu.value.size()); pdu->N_bytes = msg.protocol_ies.nas_pdu.value.size(); - rrc->write_dl_info(u->ctxt.rnti, std::move(pdu)); */ + rrc->write_dl_info(u->ctxt.rnti, std::move(pdu)); return true; } -bool ngap::handle_initialctxtsetuprequest(const asn1::ngap_nr::init_context_setup_request_s& msg) +bool ngap::handle_initial_ctxt_setup_request(const asn1::ngap_nr::init_context_setup_request_s& msg) { ue* u = handle_ngapmsg_ue_id(msg.protocol_ies.ran_ue_ngap_id.value.value, msg.protocol_ies.amf_ue_ngap_id.value.value); if (u == nullptr) { + logger.warning("Can not find UE"); return false; } - u->ctxt.amf_pointer = msg.protocol_ies.guami.value.amf_pointer.to_number(); - u->ctxt.amf_set_id = msg.protocol_ies.guami.value.amf_set_id.to_number(); - u->ctxt.amf_region_id = msg.protocol_ies.guami.value.amf_region_id.to_number(); + u->handle_initial_ctxt_setup_request(msg); - // Setup UE ctxt in RRC once interface is ready - /* if (not rrc->setup_ue_ctxt(u->ctxt.rnti, msg)) { - return false; - } */ + return true; +} - /* if (msg.protocol_ies.nas_pdu_present) { - srsran::unique_byte_buffer_t pdu = srsran::make_byte_buffer(); - if (pdu == nullptr) { - logger.error("Fatal Error: Couldn't allocate buffer in ngap::run_thread()."); - return false; - } - memcpy(pdu->msg, msg.protocol_ies.nas_pdu.value.data(), msg.protocol_ies.nas_pdu.value.size()); - pdu->N_bytes = msg.protocol_ies.nas_pdu.value.size(); - rrc->write_dl_info(u->ctxt.rnti, std::move(pdu)); - } */ +bool ngap::handle_ue_ctxt_release_cmd(const asn1::ngap_nr::ue_context_release_cmd_s& msg) +{ + // TODO: UE Context Release Command contains a list of ue_ngap_ids + + return true; +} +bool ngap::handle_pdu_session_resource_setup_request(const asn1::ngap_nr::pdu_session_res_setup_request_s& msg) +{ + // TODO + logger.warning("Not implemented yet"); return true; } +/******************************************************************************* +/* NGAP message senders +********************************************************************************/ + +bool ngap::send_error_indication(const asn1::ngap_nr::cause_c& cause, + srsran::optional ran_ue_ngap_id, + srsran::optional amf_ue_ngap_id) +{ + if (amf_connected == false) { + logger.warning("AMF not connected."); + return false; + } + + ngap_pdu_c tx_pdu; + tx_pdu.set_init_msg().load_info_obj(ASN1_NGAP_NR_ID_ERROR_IND); + auto& container = tx_pdu.init_msg().value.error_ind().protocol_ies; + + uint16_t rnti = SRSRAN_INVALID_RNTI; + container.ran_ue_ngap_id_present = ran_ue_ngap_id.has_value(); + if (ran_ue_ngap_id.has_value()) { + container.ran_ue_ngap_id.value = ran_ue_ngap_id.value(); + ue* user_ptr = users.find_ue_gnbid(ran_ue_ngap_id.value()); + rnti = user_ptr != nullptr ? user_ptr->ctxt.rnti : SRSRAN_INVALID_RNTI; + } + container.amf_ue_ngap_id_present = amf_ue_ngap_id.has_value(); + if (amf_ue_ngap_id.has_value()) { + container.amf_ue_ngap_id.value = amf_ue_ngap_id.value(); + } + + container.cause_present = true; + container.cause.value = cause; + + return sctp_send_ngap_pdu(tx_pdu, rnti, "Error Indication"); +} /******************************************************************************* /* NGAP connection helpers ********************************************************************************/ @@ -605,134 +665,6 @@ bool ngap::setup_ng() return sctp_send_ngap_pdu(pdu, 0, "ngSetupRequest"); } -/******************************************************************************* -/* NGAP message senders -********************************************************************************/ - -bool ngap::ue::send_initialuemessage(asn1::ngap_nr::rrcestablishment_cause_e cause, - srsran::unique_byte_buffer_t pdu, - bool has_tmsi, - uint32_t s_tmsi) -{ - if (not ngap_ptr->amf_connected) { - return false; - } - - ngap_pdu_c tx_pdu; - tx_pdu.set_init_msg().load_info_obj(ASN1_NGAP_NR_ID_INIT_UE_MSG); - init_ue_msg_ies_container& container = tx_pdu.init_msg().value.init_ue_msg().protocol_ies; - - // 5G-S-TMSI - if (has_tmsi) { - container.five_g_s_tmsi_present = true; - srsran::uint32_to_uint8(s_tmsi, container.five_g_s_tmsi.value.five_g_tmsi.data()); - container.five_g_s_tmsi.value.amf_set_id.from_number(ctxt.amf_set_id); - container.five_g_s_tmsi.value.amf_pointer.from_number(ctxt.amf_pointer); - } - - // RAN_UE_NGAP_ID - container.ran_ue_ngap_id.value = ctxt.ran_ue_ngap_id; - - // NAS_PDU - container.nas_pdu.value.resize(pdu->N_bytes); - memcpy(container.nas_pdu.value.data(), pdu->msg, pdu->N_bytes); - - // RRC Establishment Cause - container.rrcestablishment_cause.value = cause; - - // User Location Info - - // userLocationInformationNR - container.user_location_info.value.set_user_location_info_nr(); - container.user_location_info.value.user_location_info_nr().nr_cgi.nrcell_id = ngap_ptr->nr_cgi.nrcell_id; - container.user_location_info.value.user_location_info_nr().nr_cgi.plmn_id = ngap_ptr->nr_cgi.plmn_id; - container.user_location_info.value.user_location_info_nr().tai.plmn_id = ngap_ptr->tai.plmn_id; - container.user_location_info.value.user_location_info_nr().tai.tac = ngap_ptr->tai.tac; - - return ngap_ptr->sctp_send_ngap_pdu(tx_pdu, ctxt.rnti, "InitialUEMessage"); -} - -bool ngap::ue::send_ulnastransport(srsran::unique_byte_buffer_t pdu) -{ - if (not ngap_ptr->amf_connected) { - return false; - } - - ngap_pdu_c tx_pdu; - tx_pdu.set_init_msg().load_info_obj(ASN1_NGAP_NR_ID_UL_NAS_TRANSPORT); - asn1::ngap_nr::ul_nas_transport_ies_container& container = tx_pdu.init_msg().value.ul_nas_transport().protocol_ies; - - // AMF UE NGAP ID - container.amf_ue_ngap_id.value = ctxt.amf_ue_ngap_id.value(); - - // RAN UE NGAP ID - container.ran_ue_ngap_id.value = ctxt.ran_ue_ngap_id; - - // NAS PDU - container.nas_pdu.value.resize(pdu->N_bytes); - memcpy(container.nas_pdu.value.data(), pdu->msg, pdu->N_bytes); - - // User Location Info - // userLocationInformationNR - container.user_location_info.value.set_user_location_info_nr(); - container.user_location_info.value.user_location_info_nr().nr_cgi.nrcell_id = ngap_ptr->nr_cgi.nrcell_id; - container.user_location_info.value.user_location_info_nr().nr_cgi.plmn_id = ngap_ptr->nr_cgi.plmn_id; - container.user_location_info.value.user_location_info_nr().tai.plmn_id = ngap_ptr->tai.plmn_id; - container.user_location_info.value.user_location_info_nr().tai.tac = ngap_ptr->tai.tac; - - return ngap_ptr->sctp_send_ngap_pdu(tx_pdu, ctxt.rnti, "UplinkNASTransport"); -} - -void ngap::ue::ue_ctxt_setup_complete() -{ - ngap_pdu_c tx_pdu; - // Handle PDU Session List once RRC interface is ready - tx_pdu.set_successful_outcome().load_info_obj(ASN1_NGAP_NR_ID_INIT_CONTEXT_SETUP); - auto& container = tx_pdu.successful_outcome().value.init_context_setup_resp().protocol_ies; -} - -bool ngap::send_error_indication(const asn1::ngap_nr::cause_c& cause, - srsran::optional ran_ue_ngap_id, - srsran::optional amf_ue_ngap_id) -{ - if (not amf_connected) { - return false; - } - - ngap_pdu_c tx_pdu; - tx_pdu.set_init_msg().load_info_obj(ASN1_NGAP_NR_ID_ERROR_IND); - auto& container = tx_pdu.init_msg().value.error_ind().protocol_ies; - - uint16_t rnti = SRSRAN_INVALID_RNTI; - container.ran_ue_ngap_id_present = ran_ue_ngap_id.has_value(); - if (ran_ue_ngap_id.has_value()) { - container.ran_ue_ngap_id.value = ran_ue_ngap_id.value(); - ue* user_ptr = users.find_ue_gnbid(ran_ue_ngap_id.value()); - rnti = user_ptr != nullptr ? user_ptr->ctxt.rnti : SRSRAN_INVALID_RNTI; - } - container.amf_ue_ngap_id_present = amf_ue_ngap_id.has_value(); - if (amf_ue_ngap_id.has_value()) { - container.amf_ue_ngap_id.value = amf_ue_ngap_id.value(); - } - - container.cause_present = true; - container.cause.value = cause; - - return sctp_send_ngap_pdu(tx_pdu, rnti, "Error Indication"); -} - -/******************************************************************************* -/* ngap::ue Class -********************************************************************************/ - -ngap::ue::ue(ngap* ngap_ptr_) : ngap_ptr(ngap_ptr_) -{ - ctxt.ran_ue_ngap_id = ngap_ptr->next_gnb_ue_ngap_id++; - gettimeofday(&ctxt.init_timestamp, nullptr); - - stream_id = ngap_ptr->next_ue_stream_id; -} - /******************************************************************************* /* General helpers ********************************************************************************/ diff --git a/srsenb/src/stack/ngap/ngap_ue.cc b/srsenb/src/stack/ngap/ngap_ue.cc new file mode 100644 index 000000000..59b9f4737 --- /dev/null +++ b/srsenb/src/stack/ngap/ngap_ue.cc @@ -0,0 +1,208 @@ +/** + * + * \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 "srsenb/hdr/stack/ngap/ngap_ue.h" +#include "srsenb/hdr/stack/ngap/ngap.h" +#include "srsenb/hdr/stack/ngap/ngap_ue_proc.h" +#include "srsran/common/int_helpers.h" + +using namespace asn1::ngap_nr; + +namespace srsenb { + +/******************************************************************************* +/* ngap_ptr::ue Class +********************************************************************************/ + +ngap::ue::ue(ngap* ngap_ptr_, rrc_interface_ngap_nr* rrc_ptr_, srslog::basic_logger& logger_) : + logger(logger_), + ngap_ptr(ngap_ptr_), + rrc_ptr(rrc_ptr_), + initial_context_setup_proc(this, rrc_ptr, &ctxt), + ue_context_release_proc(this, rrc_ptr, &ctxt) +{ + ctxt.ran_ue_ngap_id = ngap_ptr->next_gnb_ue_ngap_id++; + gettimeofday(&ctxt.init_timestamp, nullptr); + stream_id = ngap_ptr->next_ue_stream_id; +} + +ngap::ue::~ue() {} + +/******************************************************************************* +/* NGAP message senders +********************************************************************************/ + +bool ngap::ue::send_initial_ue_message(asn1::ngap_nr::rrcestablishment_cause_e cause, + srsran::unique_byte_buffer_t pdu, + bool has_tmsi, + uint32_t s_tmsi) +{ + if (not ngap_ptr->amf_connected) { + logger.warning("AMF not connected"); + return false; + } + + ngap_pdu_c tx_pdu; + tx_pdu.set_init_msg().load_info_obj(ASN1_NGAP_NR_ID_INIT_UE_MSG); + init_ue_msg_ies_container& container = tx_pdu.init_msg().value.init_ue_msg().protocol_ies; + + // 5G-S-TMSI + if (has_tmsi) { + container.five_g_s_tmsi_present = true; + srsran::uint32_to_uint8(s_tmsi, container.five_g_s_tmsi.value.five_g_tmsi.data()); + container.five_g_s_tmsi.value.amf_set_id.from_number(ctxt.amf_set_id); + container.five_g_s_tmsi.value.amf_pointer.from_number(ctxt.amf_pointer); + } + + // RAN_UE_NGAP_ID + container.ran_ue_ngap_id.value = ctxt.ran_ue_ngap_id; + + // NAS_PDU + container.nas_pdu.value.resize(pdu->N_bytes); + memcpy(container.nas_pdu.value.data(), pdu->msg, pdu->N_bytes); + + // RRC Establishment Cause + container.rrcestablishment_cause.value = cause; + + // User Location Info + + // userLocationInformationNR + container.user_location_info.value.set_user_location_info_nr(); + container.user_location_info.value.user_location_info_nr().nr_cgi.nrcell_id = ngap_ptr->nr_cgi.nrcell_id; + container.user_location_info.value.user_location_info_nr().nr_cgi.plmn_id = ngap_ptr->nr_cgi.plmn_id; + container.user_location_info.value.user_location_info_nr().tai.plmn_id = ngap_ptr->tai.plmn_id; + container.user_location_info.value.user_location_info_nr().tai.tac = ngap_ptr->tai.tac; + + return ngap_ptr->sctp_send_ngap_pdu(tx_pdu, ctxt.rnti, "InitialUEMessage"); +} + +bool ngap::ue::send_ul_nas_transport(srsran::unique_byte_buffer_t pdu) +{ + if (not ngap_ptr->amf_connected) { + logger.warning("AMF not connected"); + return false; + } + + ngap_pdu_c tx_pdu; + tx_pdu.set_init_msg().load_info_obj(ASN1_NGAP_NR_ID_UL_NAS_TRANSPORT); + asn1::ngap_nr::ul_nas_transport_ies_container& container = tx_pdu.init_msg().value.ul_nas_transport().protocol_ies; + + // AMF UE NGAP ID + container.amf_ue_ngap_id.value = ctxt.amf_ue_ngap_id.value(); + + // RAN UE NGAP ID + container.ran_ue_ngap_id.value = ctxt.ran_ue_ngap_id; + + // NAS PDU + container.nas_pdu.value.resize(pdu->N_bytes); + memcpy(container.nas_pdu.value.data(), pdu->msg, pdu->N_bytes); + + // User Location Info + // userLocationInformationNR + container.user_location_info.value.set_user_location_info_nr(); + container.user_location_info.value.user_location_info_nr().nr_cgi.nrcell_id = ngap_ptr->nr_cgi.nrcell_id; + container.user_location_info.value.user_location_info_nr().nr_cgi.plmn_id = ngap_ptr->nr_cgi.plmn_id; + container.user_location_info.value.user_location_info_nr().tai.plmn_id = ngap_ptr->tai.plmn_id; + container.user_location_info.value.user_location_info_nr().tai.tac = ngap_ptr->tai.tac; + + return ngap_ptr->sctp_send_ngap_pdu(tx_pdu, ctxt.rnti, "UplinkNASTransport"); +} + +void ngap::ue::ue_ctxt_setup_complete() +{ + ngap_pdu_c tx_pdu; + // Handle PDU Session List once RRC interface is ready + tx_pdu.set_successful_outcome().load_info_obj(ASN1_NGAP_NR_ID_INIT_CONTEXT_SETUP); + auto& container = tx_pdu.successful_outcome().value.init_context_setup_resp().protocol_ies; +} + +void ngap::ue::notify_rrc_reconf_complete(const bool outcome) +{ + initial_context_setup_proc.trigger(outcome); +} + +bool ngap::ue::send_initial_ctxt_setup_response() +{ + if (not ngap_ptr->amf_connected) { + logger.warning("AMF not connected"); + return false; + } + + ngap_pdu_c tx_pdu; + tx_pdu.set_init_msg().load_info_obj(ASN1_NGAP_NR_ID_INIT_CONTEXT_SETUP); + init_context_setup_resp_s& container = tx_pdu.successful_outcome().value.init_context_setup_resp(); + + // AMF UE NGAP ID + container.protocol_ies.amf_ue_ngap_id.value = ctxt.amf_ue_ngap_id.value(); + + // RAN UE NGAP ID + container.protocol_ies.ran_ue_ngap_id.value = ctxt.ran_ue_ngap_id; + + /* // TODO: PDU Session Resource Setup Response List - Integrate PDU Session and Bearer management into NGAP + container.protocol_ies.pdu_session_res_setup_list_cxt_res_present = true; + + // Case PDU Session Resource Failed to Setup List + container.protocol_ies.pdu_session_res_failed_to_setup_list_cxt_res_present = true; */ + + return true; +} + +bool ngap::ue::send_initial_ctxt_setup_failure(cause_c cause) +{ + if (not ngap_ptr->amf_connected) { + logger.warning("AMF not connected"); + return false; + } + + ngap_pdu_c tx_pdu; + tx_pdu.set_init_msg().load_info_obj(ASN1_NGAP_NR_ID_INIT_CONTEXT_SETUP); + init_context_setup_fail_s& container = tx_pdu.unsuccessful_outcome().value.init_context_setup_fail(); + + // AMF UE NGAP ID + container.protocol_ies.amf_ue_ngap_id.value = ctxt.amf_ue_ngap_id.value(); + + // RAN UE NGAP ID + container.protocol_ies.ran_ue_ngap_id.value = ctxt.ran_ue_ngap_id; + + /* // TODO: PDU Session Resource Setup Response List - Integrate PDU Session and Bearer management into NGAP + container.protocol_ies.pdu_session_res_setup_list_cxt_res_present = true; + + // Case PDU Session Resource Failed to Setup List + container.protocol_ies.pdu_session_res_failed_to_setup_list_cxt_res_present = true; */ + + return true; +} + +/******************************************************************************* +/* NGAP message handler +********************************************************************************/ + +bool ngap::ue::handle_initial_ctxt_setup_request(const asn1::ngap_nr::init_context_setup_request_s& msg) +{ + if (not initial_context_setup_proc.launch(msg)) { + logger.error("Failed to start Initial Context Setup Procedure"); + return false; + } + return true; +} + +bool ngap::ue::handle_ue_ctxt_release_cmd(const asn1::ngap_nr::ue_context_release_cmd_s& msg) +{ + // TODO: Release UE context + if (not ue_context_release_proc.launch(msg)) { + logger.error("Failed to start UE Context Release Procedure"); + return false; + } + return true; +} + +} // namespace srsenb \ No newline at end of file diff --git a/srsenb/src/stack/ngap/ngap_ue_proc.cc b/srsenb/src/stack/ngap/ngap_ue_proc.cc new file mode 100644 index 000000000..8b113b098 --- /dev/null +++ b/srsenb/src/stack/ngap/ngap_ue_proc.cc @@ -0,0 +1,92 @@ +/** + * + * \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 "srsenb/hdr/stack/ngap/ngap_ue_proc.h" + +using namespace srsran; + +namespace srsenb { + +ngap_ue_initial_context_setup_proc::ngap_ue_initial_context_setup_proc(ngap_interface_ngap_proc* parent_, + rrc_interface_ngap_nr* rrc_, + ngap_ue_ctxt_t* ue_ctxt_) : + logger(srslog::fetch_basic_logger("NGAP UE")) +{ + parent = parent_; + rrc = rrc_; + ue_ctxt = ue_ctxt_; +}; + +proc_outcome_t ngap_ue_initial_context_setup_proc::init(const asn1::ngap_nr::init_context_setup_request_s& msg) +{ + ue_ctxt->amf_pointer = msg.protocol_ies.guami.value.amf_pointer.to_number(); + ue_ctxt->amf_set_id = msg.protocol_ies.guami.value.amf_set_id.to_number(); + ue_ctxt->amf_region_id = msg.protocol_ies.guami.value.amf_region_id.to_number(); + + if (msg.protocol_ies.ue_aggregate_maximum_bit_rate_present == true) { + rrc->ue_set_bitrates(ue_ctxt->rnti, msg.protocol_ies.ue_aggregate_maximum_bit_rate.value); + } + rrc->ue_set_security_cfg_capabilities(ue_ctxt->rnti, msg.protocol_ies.ue_security_cap.value); + rrc->ue_set_security_cfg_key(ue_ctxt->rnti, msg.protocol_ies.security_key.value); + rrc->start_security_mode_procedure(ue_ctxt->rnti); + + if (msg.protocol_ies.nas_pdu_present) { + srsran::unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + if (pdu == nullptr) { + logger.error("Fatal Error: Couldn't allocate buffer in ngap_ue_initial_context_setup_proc::init()."); + return proc_outcome_t::error; + } + memcpy(pdu->msg, msg.protocol_ies.nas_pdu.value.data(), msg.protocol_ies.nas_pdu.value.size()); + pdu->N_bytes = msg.protocol_ies.nas_pdu.value.size(); + rrc->write_dl_info(ue_ctxt->rnti, std::move(pdu)); + } + return proc_outcome_t::yield; +}; + +proc_outcome_t ngap_ue_initial_context_setup_proc::react(bool security_mode_command_outcome) +{ + if(security_mode_command_outcome == true) { + parent->send_initial_ctxt_setup_response(); + return proc_outcome_t::success; + } + // TODO: error handling if security mode command fails + return proc_outcome_t::error; +} + +proc_outcome_t ngap_ue_initial_context_setup_proc::step() +{ + return proc_outcome_t::yield; +} + +ngap_ue_ue_context_release_proc::ngap_ue_ue_context_release_proc(ngap_interface_ngap_proc* parent_, + rrc_interface_ngap_nr* rrc_, + ngap_ue_ctxt_t* ue_ctxt_) : + logger(srslog::fetch_basic_logger("NGAP UE")) +{ + parent = parent_; + rrc = rrc_; + ue_ctxt = ue_ctxt_; +}; + +proc_outcome_t ngap_ue_ue_context_release_proc::init(const asn1::ngap_nr::ue_context_release_cmd_s& msg) +{ + // ue_ngap_ids_c ue_ngap_ids = msg.protocol_ies.ue_ngap_ids.value; + // cause_c cause = msg.protocol_ies.cause.value; + return proc_outcome_t::success; +} + +proc_outcome_t ngap_ue_ue_context_release_proc::step() +{ + return proc_outcome_t::success; +} + +} // namespace srsenb \ No newline at end of file diff --git a/srsenb/src/stack/rrc/rrc_endc.cc b/srsenb/src/stack/rrc/rrc_endc.cc index 46d0ffeed..bd193d99a 100644 --- a/srsenb/src/stack/rrc/rrc_endc.cc +++ b/srsenb/src/stack/rrc/rrc_endc.cc @@ -169,8 +169,14 @@ bool rrc::ue::rrc_endc::fill_conn_recfg(asn1::rrc::rrc_conn_recfg_r8_ies_s* conn } //! Called when UE capabilities are received -void rrc::ue::rrc_endc::handle_ue_capabilities(const asn1::rrc::ue_eutra_cap_s& eutra_caps) +void rrc::ue::rrc_endc::handle_eutra_capabilities(const asn1::rrc::ue_eutra_cap_s& eutra_caps) { + // skip any further checks if eNB runs without NR cells + if (rrc_enb->cfg.cell_list_nr.empty()) { + Debug("Skipping UE capabilities. No NR cell configured."); + return; + } + // Only enabled ENDC support if UE caps have been exchanged and UE signals support if (eutra_caps.non_crit_ext_present) { if (eutra_caps.non_crit_ext.non_crit_ext_present) { @@ -281,4 +287,9 @@ void rrc::ue::rrc_endc::handle_sgnb_addition_complete() logger.info("Received SgNB addition complete for rnti=%d", rrc_ue->rnti); } +bool rrc::ue::rrc_endc::is_endc_supported() +{ + return endc_supported; +} + } // namespace srsenb diff --git a/srsenb/src/stack/rrc/rrc_mobility.cc b/srsenb/src/stack/rrc/rrc_mobility.cc index b4cd0008d..40383c656 100644 --- a/srsenb/src/stack/rrc/rrc_mobility.cc +++ b/srsenb/src/stack/rrc/rrc_mobility.cc @@ -984,6 +984,12 @@ void rrc::ue::rrc_mobility::handle_recfg_complete(wait_recfg_comp& s, const recf uint64_t target_eci = (rrc_enb->cfg.enb_id << 8u) + target_cell->cell_common->cell_cfg.cell_id; rrc_enb->s1ap->send_ho_notify(rrc_ue->rnti, target_eci); + + // Enable forwarding of GTPU SDUs coming from Source eNB Tunnel to PDCP + auto& fwd_tunnels = get_state()->pending_tunnels; + for (uint32_t teid : fwd_tunnels) { + rrc_enb->gtpu->set_tunnel_status(teid, true); + } } void rrc::ue::rrc_mobility::handle_status_transfer(s1_target_ho_st& s, const status_transfer_ev& erabs) @@ -1022,11 +1028,6 @@ void rrc::ue::rrc_mobility::handle_status_transfer(s1_target_ho_st& s, const sta rrc_enb->pdcp->set_bearer_state(rrc_ue->rnti, drb_it->lc_ch_id, drb_state); } - // Enable forwarding of GTPU SDUs coming from Source eNB Tunnel to PDCP - for (uint32_t teid : s.pending_tunnels) { - rrc_enb->gtpu->set_tunnel_status(teid, true); - } - // Check if there is any pending Reconfiguration Complete. If there is, self-trigger if (pending_recfg_complete.crit_exts.type().value != rrc_conn_recfg_complete_s::crit_exts_c_::types_opts::nulltype) { trigger(pending_recfg_complete); diff --git a/srsenb/src/stack/rrc/rrc_nr.cc b/srsenb/src/stack/rrc/rrc_nr.cc index 2c90ddcb8..52d0e39cd 100644 --- a/srsenb/src/stack/rrc/rrc_nr.cc +++ b/srsenb/src/stack/rrc/rrc_nr.cc @@ -23,6 +23,7 @@ #include "srsenb/hdr/common/common_enb.h" #include "srsran/asn1/rrc_nr_utils.h" #include "srsran/common/common_nr.h" +#include "srsran/common/phy_cfg_nr_default.h" using namespace asn1::rrc_nr; @@ -41,21 +42,17 @@ int rrc_nr::init(const rrc_nr_cfg_t& cfg_, gtpu_interface_rrc_nr* gtpu_, rrc_eutra_interface_rrc_nr* rrc_eutra_) { - phy = phy_; - mac = mac_; - rlc = rlc_; - pdcp = pdcp_; - ngap = ngap_; + phy = phy_; + mac = mac_; + rlc = rlc_; + pdcp = pdcp_; + ngap = ngap_; gtpu = gtpu_; rrc_eutra = rrc_eutra_; // TODO: overwriting because we are not passing config right now cfg = update_default_cfg(cfg_); - // config logging - logger.set_level(srslog::str_to_basic_level(cfg.log_level)); - logger.set_hex_dump_max_size(cfg.log_hex_limit); - // derived slot_dur_ms = 1; @@ -64,6 +61,8 @@ int rrc_nr::init(const rrc_nr_cfg_t& cfg_, return SRSRAN_ERROR; } + // TODO: PHY isn't initialized at this stage yet + // config_phy(); config_mac(); logger.info("Started"); @@ -157,10 +156,6 @@ rrc_nr_cfg_t rrc_nr::update_default_cfg(const rrc_nr_cfg_t& current) sib2.cell_resel_info_common.q_hyst.value = sib2_s::cell_resel_info_common_s_::q_hyst_opts::db5; // TODO: Fill SIB2 values - // set loglevel - cfg_default.log_level = "debug"; - cfg_default.log_hex_limit = 10000; - return cfg_default; } @@ -207,6 +202,20 @@ int rrc_nr::update_user(uint16_t new_rnti, uint16_t old_rnti) return SRSRAN_SUCCESS; } +void rrc_nr::config_phy() +{ + static const srsran::phy_cfg_nr_t default_phy_cfg = + srsran::phy_cfg_nr_default_t{srsran::phy_cfg_nr_default_t::reference_cfg_t{}}; + srsenb::phy_interface_rrc_nr::common_cfg_t common_cfg = {}; + common_cfg.carrier = default_phy_cfg.carrier; + common_cfg.pdcch = default_phy_cfg.pdcch; + common_cfg.prach = default_phy_cfg.prach; + if (phy->set_common_cfg(common_cfg) < SRSRAN_SUCCESS) { + logger.error("Couldn't set common PHY config"); + return; + } +} + void rrc_nr::config_mac() { // Fill MAC scheduler configuration for SIBs @@ -375,6 +384,28 @@ void rrc_nr::write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_ void rrc_nr::notify_pdcp_integrity_error(uint16_t rnti, uint32_t lcid) {} +/******************************************************************************* + NGAP interface +*******************************************************************************/ + +int rrc_nr::ue_set_security_cfg_key(uint16_t rnti, const asn1::fixed_bitstring<256, false, true>& key) +{ + return SRSRAN_SUCCESS; +} +int rrc_nr::ue_set_bitrates(uint16_t rnti, const asn1::ngap_nr::ue_aggregate_maximum_bit_rate_s& rates) +{ + return SRSRAN_SUCCESS; +} +int rrc_nr::ue_set_security_cfg_capabilities(uint16_t rnti, const asn1::ngap_nr::ue_security_cap_s& caps) +{ + return SRSRAN_SUCCESS; +} +int rrc_nr::start_security_mode_procedure(uint16_t rnti) +{ + return SRSRAN_SUCCESS; +} +void rrc_nr::write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu) {} + /******************************************************************************* Interface for EUTRA RRC *******************************************************************************/ @@ -420,9 +451,7 @@ int rrc_nr::sgnb_reconfiguration_complete(uint16_t eutra_rnti, asn1::dyn_octstri Every function in UE class is called from a mutex environment thus does not need extra protection. *******************************************************************************/ -rrc_nr::ue::ue(rrc_nr* parent_, uint16_t rnti_) : parent(parent_), rnti(rnti_) -{ -} +rrc_nr::ue::ue(rrc_nr* parent_, uint16_t rnti_) : parent(parent_), rnti(rnti_) {} void rrc_nr::ue::send_connection_setup() { @@ -491,10 +520,6 @@ int rrc_nr::ue::pack_secondary_cell_group_config(asn1::dyn_octstring& packed_sec pdcch_cfg_dedicated.setup().ctrl_res_set_to_add_mod_list[0].precoder_granularity = asn1::rrc_nr::ctrl_res_set_s::precoder_granularity_opts::same_as_reg_bundle; - cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.first_active_dl_bwp_id_present = true; - cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.first_active_dl_bwp_id = 1; - cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.ul_cfg_present = true; - // search spaces pdcch_cfg_dedicated.setup().search_spaces_to_add_mod_list_present = true; pdcch_cfg_dedicated.setup().search_spaces_to_add_mod_list.resize(1); @@ -550,7 +575,7 @@ int rrc_nr::ue::pack_secondary_cell_group_config(asn1::dyn_octstring& packed_sec pdsch_cfg_dedicated.setup().zp_csi_rs_res_to_add_mod_list.resize(1); pdsch_cfg_dedicated.setup().zp_csi_rs_res_to_add_mod_list[0].zp_csi_rs_res_id = 0; pdsch_cfg_dedicated.setup().zp_csi_rs_res_to_add_mod_list[0].res_map.freq_domain_alloc.set_row4(); - pdsch_cfg_dedicated.setup().zp_csi_rs_res_to_add_mod_list[0].res_map.freq_domain_alloc.row4().from_number(0x100); + pdsch_cfg_dedicated.setup().zp_csi_rs_res_to_add_mod_list[0].res_map.freq_domain_alloc.row4().from_number(0b100); pdsch_cfg_dedicated.setup().zp_csi_rs_res_to_add_mod_list[0].res_map.nrof_ports = asn1::rrc_nr::csi_rs_res_map_s::nrof_ports_opts::p4; @@ -568,14 +593,221 @@ int rrc_nr::ue::pack_secondary_cell_group_config(asn1::dyn_octstring& packed_sec pdsch_cfg_dedicated.setup().p_zp_csi_rs_res_set.set_setup(); pdsch_cfg_dedicated.setup().p_zp_csi_rs_res_set.setup().zp_csi_rs_res_set_id = 0; pdsch_cfg_dedicated.setup().p_zp_csi_rs_res_set.setup().zp_csi_rs_res_id_list.resize(1); - // pdsch_cfg_dedicated.setup().p_zp_csi_rs_res_set.setup().zp_csi_rs_res_id_list[0]= - // pdsch_cfg_dedicated.setup().p_zp_csi_rs_res_set.setup().zp_csi_rs_res_id_list + + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.first_active_dl_bwp_id_present = true; + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.first_active_dl_bwp_id = 1; + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.ul_cfg_present = true; + + // UL config dedicated + // PUCCH + auto& ul_config = cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.ul_cfg; + ul_config.init_ul_bwp_present = true; + ul_config.init_ul_bwp.pucch_cfg_present = true; + ul_config.init_ul_bwp.pucch_cfg.set_setup(); + ul_config.init_ul_bwp.pucch_cfg.setup().format2_present = true; + ul_config.init_ul_bwp.pucch_cfg.setup().format2.set_setup(); + ul_config.init_ul_bwp.pucch_cfg.setup().format2.setup().max_code_rate_present = true; + ul_config.init_ul_bwp.pucch_cfg.setup().format2.setup().max_code_rate = pucch_max_code_rate_opts::zero_dot25; + + // SR resources + ul_config.init_ul_bwp.pucch_cfg.setup().sched_request_res_to_add_mod_list_present = true; + ul_config.init_ul_bwp.pucch_cfg.setup().sched_request_res_to_add_mod_list.resize(1); + auto& sr_res1 = ul_config.init_ul_bwp.pucch_cfg.setup().sched_request_res_to_add_mod_list[0]; + sr_res1.sched_request_res_id = 1; + sr_res1.sched_request_id = 0; + sr_res1.periodicity_and_offset_present = true; + sr_res1.periodicity_and_offset.set_sl40(); + sr_res1.periodicity_and_offset.sl40() = 7; + sr_res1.res_present = true; + sr_res1.res = 2; // PUCCH resource for SR + + // DL data + ul_config.init_ul_bwp.pucch_cfg.setup().dl_data_to_ul_ack_present = true; + ul_config.init_ul_bwp.pucch_cfg.setup().dl_data_to_ul_ack.resize(6); + ul_config.init_ul_bwp.pucch_cfg.setup().dl_data_to_ul_ack[0] = 6; + ul_config.init_ul_bwp.pucch_cfg.setup().dl_data_to_ul_ack[1] = 5; + ul_config.init_ul_bwp.pucch_cfg.setup().dl_data_to_ul_ack[2] = 4; + ul_config.init_ul_bwp.pucch_cfg.setup().dl_data_to_ul_ack[3] = 4; + ul_config.init_ul_bwp.pucch_cfg.setup().dl_data_to_ul_ack[4] = 4; + ul_config.init_ul_bwp.pucch_cfg.setup().dl_data_to_ul_ack[5] = 4; + + // PUCCH Resource for format 1 + srsran_pucch_nr_resource_t resource_small = {}; + resource_small.starting_prb = 0; + resource_small.format = SRSRAN_PUCCH_NR_FORMAT_1; + resource_small.initial_cyclic_shift = 0; + resource_small.nof_symbols = 14; + resource_small.start_symbol_idx = 0; + resource_small.time_domain_occ = 0; + + // PUCCH Resource for format 2 + srsran_pucch_nr_resource_t resource_big = {}; + resource_big.starting_prb = 51; + resource_big.format = SRSRAN_PUCCH_NR_FORMAT_2; + resource_big.nof_prb = 1; + resource_big.nof_symbols = 2; + resource_big.start_symbol_idx = 12; + + // Resource for SR + srsran_pucch_nr_resource_t resource_sr = {}; + resource_sr.starting_prb = 51; + resource_sr.format = SRSRAN_PUCCH_NR_FORMAT_1; + resource_sr.initial_cyclic_shift = 0; + resource_sr.nof_symbols = 14; + resource_sr.start_symbol_idx = 0; + resource_sr.time_domain_occ = 0; + + // Make 3 possible resources + ul_config.init_ul_bwp.pucch_cfg.setup().res_to_add_mod_list_present = true; + ul_config.init_ul_bwp.pucch_cfg.setup().res_to_add_mod_list.resize(3); + if (not srsran::make_phy_res_config( + resource_small, ul_config.init_ul_bwp.pucch_cfg.setup().res_to_add_mod_list[0], 0)) { + parent->logger.warning("Failed to create 1-2 bit NR PUCCH resource"); + } + if (not srsran::make_phy_res_config( + resource_big, ul_config.init_ul_bwp.pucch_cfg.setup().res_to_add_mod_list[1], 1)) { + parent->logger.warning("Failed to create >2 bit NR PUCCH resource"); + } + if (not srsran::make_phy_res_config( + resource_big, ul_config.init_ul_bwp.pucch_cfg.setup().res_to_add_mod_list[2], 2)) { + parent->logger.warning("Failed to create SR NR PUCCH resource"); + } + + // Make 2 PUCCH resource sets + ul_config.init_ul_bwp.pucch_cfg.setup().res_set_to_add_mod_list_present = true; + ul_config.init_ul_bwp.pucch_cfg.setup().res_set_to_add_mod_list.resize(2); + + // Make PUCCH resource set for 1-2 bit + ul_config.init_ul_bwp.pucch_cfg.setup().res_set_to_add_mod_list[0].pucch_res_set_id = 0; + ul_config.init_ul_bwp.pucch_cfg.setup().res_set_to_add_mod_list[0].res_list.resize(8); + for (auto& e : ul_config.init_ul_bwp.pucch_cfg.setup().res_set_to_add_mod_list[0].res_list) { + e = 0; + } + + // Make PUCCH resource set for >2 bit + ul_config.init_ul_bwp.pucch_cfg.setup().res_set_to_add_mod_list[1].pucch_res_set_id = 1; + ul_config.init_ul_bwp.pucch_cfg.setup().res_set_to_add_mod_list[1].res_list.resize(8); + for (auto& e : ul_config.init_ul_bwp.pucch_cfg.setup().res_set_to_add_mod_list[1].res_list) { + e = 1; + } + + // PUSCH config + ul_config.init_ul_bwp.pusch_cfg_present = true; + ul_config.init_ul_bwp.pusch_cfg.set_setup(); + auto& pusch_cfg_ded = ul_config.init_ul_bwp.pusch_cfg.setup(); + pusch_cfg_ded.dmrs_ul_for_pusch_map_type_a_present = true; + pusch_cfg_ded.dmrs_ul_for_pusch_map_type_a.set_setup(); + pusch_cfg_ded.dmrs_ul_for_pusch_map_type_a.setup().dmrs_add_position_present = true; + pusch_cfg_ded.dmrs_ul_for_pusch_map_type_a.setup().dmrs_add_position = dmrs_ul_cfg_s::dmrs_add_position_opts::pos1; + // PUSH power control skipped + pusch_cfg_ded.res_alloc = pusch_cfg_s::res_alloc_opts::res_alloc_type1; + + // UCI + pusch_cfg_ded.uci_on_pusch_present = true; + pusch_cfg_ded.uci_on_pusch.set_setup(); + pusch_cfg_ded.uci_on_pusch.setup().beta_offsets_present = true; + pusch_cfg_ded.uci_on_pusch.setup().beta_offsets.set_semi_static(); + auto& beta_offset_semi_static = pusch_cfg_ded.uci_on_pusch.setup().beta_offsets.semi_static(); + beta_offset_semi_static.beta_offset_ack_idx1_present = true; + beta_offset_semi_static.beta_offset_ack_idx1 = 9; + beta_offset_semi_static.beta_offset_ack_idx2_present = true; + beta_offset_semi_static.beta_offset_ack_idx2 = 9; + beta_offset_semi_static.beta_offset_ack_idx3_present = true; + beta_offset_semi_static.beta_offset_ack_idx3 = 9; + beta_offset_semi_static.beta_offset_csi_part1_idx1_present = true; + beta_offset_semi_static.beta_offset_csi_part1_idx2_present = true; + beta_offset_semi_static.beta_offset_csi_part1_idx1 = 6; + beta_offset_semi_static.beta_offset_csi_part1_idx2 = 6; + beta_offset_semi_static.beta_offset_csi_part2_idx1_present = true; + beta_offset_semi_static.beta_offset_csi_part2_idx1 = 6; + beta_offset_semi_static.beta_offset_csi_part2_idx2_present = true; + beta_offset_semi_static.beta_offset_csi_part2_idx2 = 6; + pusch_cfg_ded.uci_on_pusch.setup().scaling = uci_on_pusch_s::scaling_opts::f1; + + ul_config.first_active_ul_bwp_id_present = true; + ul_config.first_active_ul_bwp_id = 0; + // Serving cell config (only to setup) + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.pdcch_serving_cell_cfg_present = true; + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.pdcch_serving_cell_cfg.set_setup(); + + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.pdsch_serving_cell_cfg_present = true; + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.pdsch_serving_cell_cfg.set_setup(); + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.pdsch_serving_cell_cfg.setup().nrof_harq_processes_for_pdsch_present = + true; + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.pdsch_serving_cell_cfg.setup().nrof_harq_processes_for_pdsch = + pdsch_serving_cell_cfg_s::nrof_harq_processes_for_pdsch_opts::n16; cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg_present = true; cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.set_setup(); + // nzp-CSI-RS Resource + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup().nzp_csi_rs_res_to_add_mod_list_present = true; + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup().nzp_csi_rs_res_to_add_mod_list.resize(1); + auto& nzp_csi_res = + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup().nzp_csi_rs_res_to_add_mod_list[0]; + nzp_csi_res.nzp_csi_rs_res_id = 0; + nzp_csi_res.res_map.freq_domain_alloc.set_row2(); + nzp_csi_res.res_map.freq_domain_alloc.row2().from_number(0b100000000000); + nzp_csi_res.res_map.nrof_ports = asn1::rrc_nr::csi_rs_res_map_s::nrof_ports_opts::p1; + nzp_csi_res.res_map.first_ofdm_symbol_in_time_domain = 4; + nzp_csi_res.res_map.cdm_type = asn1::rrc_nr::csi_rs_res_map_s::cdm_type_opts::no_cdm; + nzp_csi_res.res_map.density.set_one(); + nzp_csi_res.res_map.freq_band.start_rb = 0; + nzp_csi_res.res_map.freq_band.nrof_rbs = 52; + nzp_csi_res.pwr_ctrl_offset = 0; + // Skip pwr_ctrl_offset_ss_present + nzp_csi_res.scrambling_id = 0; + nzp_csi_res.periodicity_and_offset_present = true; + nzp_csi_res.periodicity_and_offset.set_slots80(); + nzp_csi_res.periodicity_and_offset.slots80() = 0; + // optional + nzp_csi_res.qcl_info_periodic_csi_rs_present = true; + nzp_csi_res.qcl_info_periodic_csi_rs = 0; + + // nzp-CSI-RS ResourceSet + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup().nzp_csi_rs_res_set_to_add_mod_list_present = + true; + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup().nzp_csi_rs_res_set_to_add_mod_list.resize(1); + auto& nzp_csi_res_set = + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup().nzp_csi_rs_res_set_to_add_mod_list[0]; + nzp_csi_res_set.nzp_csi_res_set_id = 0; + nzp_csi_res_set.nzp_csi_rs_res.resize(1); + nzp_csi_res_set.nzp_csi_rs_res[0] = 0; + // Skip TRS info + + // CSI report config + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup().csi_report_cfg_to_add_mod_list_present = true; + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup().csi_report_cfg_to_add_mod_list.resize(1); + auto& csi_report = + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup().csi_report_cfg_to_add_mod_list[0]; + csi_report.report_cfg_id = 0; + csi_report.res_for_ch_meas = 0; + csi_report.csi_im_res_for_interference_present = true; + csi_report.csi_im_res_for_interference = 1; + csi_report.report_cfg_type.set_periodic(); + csi_report.report_cfg_type.periodic().report_slot_cfg.set_slots80(); + csi_report.report_cfg_type.periodic().report_slot_cfg.slots80() = 8; + csi_report.report_cfg_type.periodic().pucch_csi_res_list.resize(1); + csi_report.report_cfg_type.periodic().pucch_csi_res_list[0].ul_bw_part_id = 0; + csi_report.report_cfg_type.periodic().pucch_csi_res_list[0].pucch_res = 0; // was 17 in orig PCAP + csi_report.report_quant.set_cri_ri_pmi_cqi(); + // Report freq config (optional) + csi_report.report_freq_cfg_present = true; + csi_report.report_freq_cfg.cqi_format_ind_present = true; + csi_report.report_freq_cfg.cqi_format_ind = + asn1::rrc_nr::csi_report_cfg_s::report_freq_cfg_s_::cqi_format_ind_opts::wideband_cqi; + csi_report.time_restrict_for_ch_meass = asn1::rrc_nr::csi_report_cfg_s::time_restrict_for_ch_meass_opts::not_cfgured; + csi_report.time_restrict_for_interference_meass = + asn1::rrc_nr::csi_report_cfg_s::time_restrict_for_interference_meass_opts::not_cfgured; + csi_report.group_based_beam_report.set_disabled(); + // Skip CQI table (optional) + csi_report.cqi_table_present = true; + csi_report.cqi_table = asn1::rrc_nr::csi_report_cfg_s::cqi_table_opts::table2; + csi_report.subband_size = asn1::rrc_nr::csi_report_cfg_s::subband_size_opts::value1; + + // Reconfig with Sync cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync_present = true; - cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.new_ue_id = 17943; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.new_ue_id = 0x4602; // first RNTI assigned to new UE cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.smtc.release(); cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.t304 = recfg_with_sync_s::t304_opts::ms1000; @@ -595,19 +827,25 @@ int rrc_nr::ue::pack_secondary_cell_group_config(asn1::dyn_octstring& packed_sec cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.freq_info_dl .absolute_freq_ssb_present = true; cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.freq_info_dl.absolute_freq_ssb = - 632640; + 634176; // TODO: calculate from actual DL ARFCN cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.freq_info_dl.freq_band_list .push_back(78); cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.freq_info_dl.absolute_freq_point_a = - 632316; + 633928; // TODO: calculate from actual DL ARFCN cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.freq_info_dl .scs_specific_carrier_list.resize(1); + + cell_group_cfg_pack.phys_cell_group_cfg_present = true; + cell_group_cfg_pack.phys_cell_group_cfg.pdsch_harq_ack_codebook = + phys_cell_group_cfg_s::pdsch_harq_ack_codebook_opts::dynamic_value; + auto& dl_carrier = cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.freq_info_dl .scs_specific_carrier_list[0]; dl_carrier.offset_to_carrier = 0; dl_carrier.subcarrier_spacing = subcarrier_spacing_opts::khz15; dl_carrier.carrier_bw = 52; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.init_dl_bwp_present = true; cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.init_dl_bwp.generic_params .location_and_bw = 14025; @@ -758,7 +996,7 @@ int rrc_nr::ue::pack_secondary_cell_group_config(asn1::dyn_octstring& packed_sec tdd_config.pattern1.dl_ul_tx_periodicity = asn1::rrc_nr::tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms10; tdd_config.pattern1.nrof_dl_slots = 6; tdd_config.pattern1.nrof_dl_symbols = 0; - tdd_config.pattern1.nrof_ul_slots = 3; + tdd_config.pattern1.nrof_ul_slots = 4; tdd_config.pattern1.nrof_ul_symbols = 0; // make sufficiant space diff --git a/srsenb/src/stack/rrc/rrc_ue.cc b/srsenb/src/stack/rrc/rrc_ue.cc index 7e89e16a5..0411c812e 100644 --- a/srsenb/src/stack/rrc/rrc_ue.cc +++ b/srsenb/src/stack/rrc/rrc_ue.cc @@ -368,15 +368,23 @@ void rrc::ue::parse_ul_dcch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) break; case ul_dcch_msg_type_c::c1_c_::types::security_mode_complete: handle_security_mode_complete(&ul_dcch_msg.msg.c1().security_mode_complete()); - send_ue_cap_enquiry(); + send_ue_cap_enquiry({asn1::rrc::rat_type_opts::options::eutra}); state = RRC_STATE_WAIT_FOR_UE_CAP_INFO; break; case ul_dcch_msg_type_c::c1_c_::types::security_mode_fail: handle_security_mode_failure(&ul_dcch_msg.msg.c1().security_mode_fail()); break; case ul_dcch_msg_type_c::c1_c_::types::ue_cap_info: - if (handle_ue_cap_info(&ul_dcch_msg.msg.c1().ue_cap_info())) { - send_connection_reconf(std::move(pdu)); + if (handle_ue_cap_info(&ul_dcch_msg.msg.c1().ue_cap_info()) == SRSRAN_SUCCESS) { + if (not parent->cfg.cell_list_nr.empty() && endc_handler->is_endc_supported() && + state == RRC_STATE_WAIT_FOR_UE_CAP_INFO) { + // request EUTRA-NR and NR capabilities as well + send_ue_cap_enquiry({asn1::rrc::rat_type_opts::options::eutra_nr, asn1::rrc::rat_type_opts::options::nr}); + state = RRC_STATE_WAIT_FOR_UE_CAP_INFO_ENDC; // avoid endless loop + } else { + // send RRC reconfiguration to complete procedure + send_connection_reconf(std::move(pdu)); + } } else { send_connection_reject(procedure_result_code::none); state = RRC_STATE_IDLE; @@ -920,7 +928,7 @@ void rrc::ue::handle_security_mode_failure(security_mode_fail_s* msg) /* * UE capabilities info */ -void rrc::ue::send_ue_cap_enquiry() +void rrc::ue::send_ue_cap_enquiry(const std::vector& rats) { dl_dcch_msg_s dl_dcch_msg; dl_dcch_msg.msg.set_c1().set_ue_cap_enquiry().crit_exts.set_c1().set_ue_cap_enquiry_r8(); @@ -928,28 +936,34 @@ void rrc::ue::send_ue_cap_enquiry() ue_cap_enquiry_s* enq = &dl_dcch_msg.msg.c1().ue_cap_enquiry(); enq->rrc_transaction_id = (uint8_t)((transaction_id++) % 4); - enq->crit_exts.c1().ue_cap_enquiry_r8().ue_cap_request.resize(1); - enq->crit_exts.c1().ue_cap_enquiry_r8().ue_cap_request[0].value = rat_type_e::eutra; + enq->crit_exts.c1().ue_cap_enquiry_r8().ue_cap_request.resize(rats.size()); + for (uint32_t i = 0; i < rats.size(); ++i) { + enq->crit_exts.c1().ue_cap_enquiry_r8().ue_cap_request[i].value = rats.at(i); + } send_dl_dcch(&dl_dcch_msg); } -bool rrc::ue::handle_ue_cap_info(ue_cap_info_s* msg) +/** + * @brief Handle the reception of UE capability information message + * + * @return int SRSRAN_SUCCESS if unpacking was ok. SRSRAN_ERROR otherwise + */ +int rrc::ue::handle_ue_cap_info(ue_cap_info_s* msg) { parent->logger.info("UECapabilityInformation transaction ID: %d", msg->rrc_transaction_id); ue_cap_info_r8_ies_s* msg_r8 = &msg->crit_exts.c1().ue_cap_info_r8(); for (uint32_t i = 0; i < msg_r8->ue_cap_rat_container_list.size(); i++) { if (msg_r8->ue_cap_rat_container_list[i].rat_type != rat_type_e::eutra) { - parent->logger.warning("Not handling UE capability information for RAT type %s", - msg_r8->ue_cap_rat_container_list[i].rat_type.to_string()); + // Not handling UE capability information for RATs other than EUTRA continue; } asn1::cbit_ref bref(msg_r8->ue_cap_rat_container_list[i].ue_cap_rat_container.data(), msg_r8->ue_cap_rat_container_list[i].ue_cap_rat_container.size()); if (eutra_capabilities.unpack(bref) != asn1::SRSASN_SUCCESS) { parent->logger.error("Failed to unpack EUTRA capabilities message"); - return false; + return SRSRAN_ERROR; } if (parent->logger.debug.enabled()) { asn1::json_writer js{}; @@ -962,7 +976,7 @@ bool rrc::ue::handle_ue_cap_info(ue_cap_info_s* msg) parent->logger.info("UE rnti: 0x%x category: %d", rnti, eutra_capabilities.ue_category); if (endc_handler) { - endc_handler->handle_ue_capabilities(eutra_capabilities); + endc_handler->handle_eutra_capabilities(eutra_capabilities); } } @@ -970,7 +984,7 @@ bool rrc::ue::handle_ue_cap_info(ue_cap_info_s* msg) srsran::unique_byte_buffer_t pdu = srsran::make_byte_buffer(); if (pdu == nullptr) { parent->logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); - return false; + return SRSRAN_ERROR; } asn1::bit_ref bref2{pdu->msg, pdu->get_tailroom()}; msg->pack(bref2); @@ -981,13 +995,13 @@ bool rrc::ue::handle_ue_cap_info(ue_cap_info_s* msg) bref2 = asn1::bit_ref{pdu->msg, pdu->get_tailroom()}; if (ue_rat_caps.pack(bref2) != asn1::SRSASN_SUCCESS) { parent->logger.error("Couldn't pack ue rat caps"); - return false; + return SRSRAN_ERROR; } pdu->N_bytes = bref2.distance_bytes(); parent->s1ap->send_ue_cap_info_indication(rnti, std::move(pdu)); } - return true; + return SRSRAN_SUCCESS; } /* diff --git a/srsenb/src/stack/upper/rlc.cc b/srsenb/src/stack/upper/rlc.cc index fa50e2194..da443476b 100644 --- a/srsenb/src/stack/upper/rlc.cc +++ b/srsenb/src/stack/upper/rlc.cc @@ -63,7 +63,7 @@ void rlc::get_metrics(rlc_metrics_t& m, const uint32_t nof_tti) void rlc::add_user(uint16_t rnti) { - pthread_rwlock_rdlock(&rwlock); + pthread_rwlock_wrlock(&rwlock); if (users.count(rnti) == 0) { auto obj = make_rnti_obj(rnti, logger.id().c_str()); obj->init(&users[rnti], diff --git a/srsenb/test/mac/nr/sched_nr_cfg_generators.h b/srsenb/test/mac/nr/sched_nr_cfg_generators.h index 226e299db..baea7a7d4 100644 --- a/srsenb/test/mac/nr/sched_nr_cfg_generators.h +++ b/srsenb/test/mac/nr/sched_nr_cfg_generators.h @@ -27,14 +27,14 @@ namespace srsenb { -srsran_coreset_t get_default_coreset0() +srsran_coreset_t get_default_coreset0(uint32_t nof_prb) { srsran_coreset_t coreset{}; coreset.id = 0; coreset.duration = 1; coreset.precoder_granularity = srsran_coreset_precoder_granularity_reg_bundle; for (uint32_t i = 0; i < SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE; ++i) { - coreset.freq_resources[i] = i < 8; + coreset.freq_resources[i] = i < (nof_prb / 6); } return coreset; } @@ -53,7 +53,7 @@ sched_nr_interface::cell_cfg_t get_default_cell_cfg(const srsran::phy_cfg_nr_t& cell_cfg.bwps[0].rb_width = phy_cfg.carrier.nof_prb; cell_cfg.bwps[0].pdcch.coreset_present[0] = true; - cell_cfg.bwps[0].pdcch.coreset[0] = get_default_coreset0(); + cell_cfg.bwps[0].pdcch.coreset[0] = get_default_coreset0(phy_cfg.carrier.nof_prb); cell_cfg.bwps[0].pdcch.search_space_present[0] = true; auto& ss = cell_cfg.bwps[0].pdcch.search_space[0]; ss.id = 0; diff --git a/srsenb/test/mac/nr/sched_nr_sim_ue.cc b/srsenb/test/mac/nr/sched_nr_sim_ue.cc index 0177147a3..9abddd40a 100644 --- a/srsenb/test/mac/nr/sched_nr_sim_ue.cc +++ b/srsenb/test/mac/nr/sched_nr_sim_ue.cc @@ -27,14 +27,14 @@ namespace srsenb { sched_nr_ue_sim::sched_nr_ue_sim(uint16_t rnti_, const sched_nr_interface::ue_cfg_t& ue_cfg_, - tti_point prach_tti_rx, + slot_point prach_slot_rx, uint32_t preamble_idx) : logger(srslog::fetch_basic_logger("MAC")) { - ctxt.rnti = rnti_; - ctxt.prach_tti_rx = prach_tti_rx; - ctxt.preamble_idx = preamble_idx; - ctxt.ue_cfg = ue_cfg_; + ctxt.rnti = rnti_; + ctxt.prach_slot_rx = prach_slot_rx; + ctxt.preamble_idx = preamble_idx; + ctxt.ue_cfg = ue_cfg_; ctxt.cc_list.resize(ue_cfg_.carriers.size()); for (auto& cc : ctxt.cc_list) { @@ -54,16 +54,16 @@ int sched_nr_ue_sim::update(const sched_nr_cc_output_res_t& cc_out) if (data.dci.ctx.rnti != ctxt.rnti) { continue; } - tti_point pdcch_tti = cc_out.tti; - uint32_t k1 = ctxt.ue_cfg.phy_cfg.harq_ack - .dl_data_to_ul_ack[pdcch_tti.sf_idx() % ctxt.ue_cfg.phy_cfg.harq_ack.nof_dl_data_to_ul_ack]; - tti_point uci_tti = pdcch_tti + k1; + slot_point pdcch_slot = cc_out.slot; + uint32_t k1 = ctxt.ue_cfg.phy_cfg.harq_ack + .dl_data_to_ul_ack[pdcch_slot.slot_idx() % ctxt.ue_cfg.phy_cfg.harq_ack.nof_dl_data_to_ul_ack]; + slot_point uci_slot = pdcch_slot + k1; - ctxt.cc_list[cc_out.cc].pending_acks[uci_tti.to_uint()]++; + ctxt.cc_list[cc_out.cc].pending_acks[uci_slot.to_uint()]++; } // clear up old slots - ctxt.cc_list[cc_out.cc].pending_acks[(cc_out.tti - 1).to_uint()] = 0; + ctxt.cc_list[cc_out.cc].pending_acks[(cc_out.slot - 1).to_uint()] = 0; return SRSRAN_SUCCESS; } @@ -79,21 +79,21 @@ void sched_nr_ue_sim::update_dl_harqs(const sched_nr_cc_output_res_t& cc_out) auto& h = ctxt.cc_list[cc].dl_harqs[data.dci.pid]; if (h.nof_txs == 0 or h.ndi != data.dci.ndi) { // It is newtx - h.nof_retxs = 0; - h.ndi = data.dci.ndi; - h.first_tti_tx = cc_out.tti; - h.dci_loc = data.dci.ctx.location; - h.tbs = 100; // TODO + h.nof_retxs = 0; + h.ndi = data.dci.ndi; + h.first_slot_tx = cc_out.slot; + h.dci_loc = data.dci.ctx.location; + h.tbs = 100; // TODO } else { // it is retx h.nof_retxs++; } - h.active = true; - h.last_tti_tx = cc_out.tti; - h.last_tti_ack = - h.last_tti_tx + + h.active = true; + h.last_slot_tx = cc_out.slot; + h.last_slot_ack = + h.last_slot_tx + ctxt.ue_cfg.phy_cfg.harq_ack - .dl_data_to_ul_ack[h.last_tti_tx.sf_idx() % ctxt.ue_cfg.phy_cfg.harq_ack.nof_dl_data_to_ul_ack]; + .dl_data_to_ul_ack[h.last_slot_tx.slot_idx() % ctxt.ue_cfg.phy_cfg.harq_ack.nof_dl_data_to_ul_ack]; h.nof_txs++; } } @@ -126,26 +126,26 @@ int sched_nr_sim_base::add_user(uint16_t rnti, const sched_nr_interface::ue_cfg_ TESTASSERT(ue_db.count(rnti) == 0); sched_ptr->ue_cfg(rnti, ue_cfg_); - ue_db.insert(std::make_pair(rnti, sched_nr_ue_sim(rnti, ue_cfg_, current_tti_tx, preamble_idx))); + ue_db.insert(std::make_pair(rnti, sched_nr_ue_sim(rnti, ue_cfg_, current_slot_tx, preamble_idx))); return SRSRAN_SUCCESS; } -void sched_nr_sim_base::new_slot(srsran::tti_point tti_tx) +void sched_nr_sim_base::new_slot(slot_point slot_tx) { std::unique_lock lock(mutex); while (cc_finished > 0) { cvar.wait(lock); } - logger.set_context(tti_tx.to_uint()); - mac_logger.set_context(tti_tx.to_uint()); - logger.info("---------------- TTI=%d ---------------", tti_tx.to_uint()); - current_tti_tx = tti_tx; - cc_finished = cell_params.size(); + logger.set_context(slot_tx.to_uint()); + mac_logger.set_context(slot_tx.to_uint()); + logger.info("---------------- TTI=%d ---------------", slot_tx.to_uint()); + current_slot_tx = slot_tx; + cc_finished = cell_params.size(); for (auto& ue : ue_db) { - ue_nr_tti_events events; - set_default_tti_events(ue.second.get_ctxt(), events); - set_external_tti_events(ue.second.get_ctxt(), events); - apply_tti_events(ue.second.get_ctxt(), events); + ue_nr_slot_events events; + set_default_slot_events(ue.second.get_ctxt(), events); + set_external_slot_events(ue.second.get_ctxt(), events); + apply_slot_events(ue.second.get_ctxt(), events); } } @@ -166,11 +166,11 @@ void sched_nr_sim_base::update(sched_nr_cc_output_res_t& cc_out) } } -int sched_nr_sim_base::set_default_tti_events(const sim_nr_ue_ctxt_t& ue_ctxt, ue_nr_tti_events& pending_events) +int sched_nr_sim_base::set_default_slot_events(const sim_nr_ue_ctxt_t& ue_ctxt, ue_nr_slot_events& pending_events) { pending_events.cc_list.clear(); pending_events.cc_list.resize(cell_params.size()); - pending_events.tti_rx = current_tti_tx; + pending_events.slot_rx = current_slot_tx; for (uint32_t enb_cc_idx = 0; enb_cc_idx < pending_events.cc_list.size(); ++enb_cc_idx) { auto& cc_feedback = pending_events.cc_list[enb_cc_idx]; @@ -181,13 +181,13 @@ int sched_nr_sim_base::set_default_tti_events(const sim_nr_ue_ctxt_t& ue_ctxt, u auto& ul_h = ue_ctxt.cc_list[enb_cc_idx].ul_harqs[pid]; // Set default DL ACK - if (dl_h.active and (dl_h.last_tti_ack) == current_tti_tx) { - cc_feedback.dl_acks.push_back(ue_nr_tti_events::ack_t{pid, true}); + if (dl_h.active and (dl_h.last_slot_ack) == current_slot_tx) { + cc_feedback.dl_acks.push_back(ue_nr_slot_events::ack_t{pid, true}); } // Set default UL ACK - if (ul_h.active and (ul_h.last_tti_tx + 8) == current_tti_tx) { - cc_feedback.ul_acks.emplace_back(ue_nr_tti_events::ack_t{pid, true}); + if (ul_h.active and (ul_h.last_slot_tx + 8) == current_slot_tx) { + cc_feedback.ul_acks.emplace_back(ue_nr_slot_events::ack_t{pid, true}); } // TODO: other CSI @@ -197,7 +197,7 @@ int sched_nr_sim_base::set_default_tti_events(const sim_nr_ue_ctxt_t& ue_ctxt, u return SRSRAN_SUCCESS; } -int sched_nr_sim_base::apply_tti_events(sim_nr_ue_ctxt_t& ue_ctxt, const ue_nr_tti_events& events) +int sched_nr_sim_base::apply_slot_events(sim_nr_ue_ctxt_t& ue_ctxt, const ue_nr_slot_events& events) { for (uint32_t enb_cc_idx = 0; enb_cc_idx < events.cc_list.size(); ++enb_cc_idx) { const auto& cc_feedback = events.cc_list[enb_cc_idx]; @@ -210,7 +210,7 @@ int sched_nr_sim_base::apply_tti_events(sim_nr_ue_ctxt_t& ue_ctxt, const ue_nr_t if (ack.ack) { logger.info( - "DL ACK rnti=0x%x tti_dl_tx=%u cc=%d pid=%d", ue_ctxt.rnti, h.last_tti_tx.to_uint(), enb_cc_idx, ack.pid); + "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); } // update scheduler @@ -227,11 +227,11 @@ int sched_nr_sim_base::apply_tti_events(sim_nr_ue_ctxt_t& ue_ctxt, const ue_nr_t if (ack.ack) { logger.info( - "UL ACK rnti=0x%x, tti_ul_tx=%u, cc=%d pid=%d", ue_ctxt.rnti, h.last_tti_tx.to_uint(), enb_cc_idx, h.pid); + "UL ACK rnti=0x%x, slot_ul_tx=%u, cc=%d pid=%d", ue_ctxt.rnti, h.last_slot_tx.to_uint(), enb_cc_idx, h.pid); } // // update scheduler - // if (sched_ptr->ul_crc_info(events.tti_rx.to_uint(), ue_ctxt.rnti, enb_cc_idx, cc_feedback.ul_ack) < 0) { + // if (sched_ptr->ul_crc_info(events.slot_rx.to_uint(), ue_ctxt.rnti, enb_cc_idx, cc_feedback.ul_ack) < 0) { // logger.error("The ACKed UL Harq pid=%d does not exist.", cc_feedback.ul_pid); // error_counter++; // } diff --git a/srsenb/test/mac/nr/sched_nr_sim_ue.h b/srsenb/test/mac/nr/sched_nr_sim_ue.h index 6e0b8b6ef..f382c5799 100644 --- a/srsenb/test/mac/nr/sched_nr_sim_ue.h +++ b/srsenb/test/mac/nr/sched_nr_sim_ue.h @@ -40,10 +40,10 @@ struct ue_nr_harq_ctxt_t { uint32_t riv = 0; srsran_dci_location_t dci_loc = {}; uint32_t tbs = 0; - tti_point last_tti_tx, first_tti_tx, last_tti_ack; + slot_point last_slot_tx, first_slot_tx, last_slot_ack; }; struct sched_nr_cc_output_res_t { - tti_point tti; + slot_point slot; uint32_t cc; const sched_nr_interface::dl_sched_t* dl_cc_result; const sched_nr_interface::ul_sched_t* ul_cc_result; @@ -55,7 +55,7 @@ struct ue_nr_cc_ctxt_t { srsran::circular_array pending_acks; }; -struct ue_nr_tti_events { +struct ue_nr_slot_events { struct ack_t { uint32_t pid; bool ack; @@ -65,14 +65,14 @@ struct ue_nr_tti_events { srsran::bounded_vector dl_acks; srsran::bounded_vector ul_acks; }; - srsran::tti_point tti_rx; + slot_point slot_rx; std::vector cc_list; }; struct sim_nr_ue_ctxt_t { uint16_t rnti; uint32_t preamble_idx; - srsran::tti_point prach_tti_rx; + slot_point prach_slot_rx; sched_nr_interface::ue_cfg_t ue_cfg; std::vector cc_list; @@ -92,7 +92,7 @@ class sched_nr_ue_sim public: sched_nr_ue_sim(uint16_t rnti_, const sched_nr_interface::ue_cfg_t& ue_cfg_, - tti_point prach_tti_rx, + slot_point prach_slot_rx, uint32_t preamble_idx); int update(const sched_nr_cc_output_res_t& cc_out); @@ -117,7 +117,7 @@ public: int add_user(uint16_t rnti, const sched_nr_interface::ue_cfg_t& ue_cfg_, uint32_t preamble_idx); - void new_slot(srsran::tti_point tti_tx); + void new_slot(slot_point slot_tx); void update(sched_nr_cc_output_res_t& cc_out); sched_nr_ue_sim& at(uint16_t rnti) { return ue_db.at(rnti); } @@ -140,10 +140,10 @@ public: } sched_nr* get_sched() { return sched_ptr.get(); } srsran::const_span get_cell_params() { return cell_params; } - tti_point get_tti_rx() const + slot_point get_slot_rx() const { std::lock_guard lock(mutex); - return current_tti_tx; + return current_slot_tx; } sim_nr_enb_ctxt_t get_enb_ctxt() const; @@ -152,11 +152,11 @@ public: std::map::iterator end() { return ue_db.end(); } // configurable by simulator concrete implementation - virtual void set_external_tti_events(const sim_nr_ue_ctxt_t& ue_ctxt, ue_nr_tti_events& pending_events) {} + virtual void set_external_slot_events(const sim_nr_ue_ctxt_t& ue_ctxt, ue_nr_slot_events& pending_events) {} private: - int set_default_tti_events(const sim_nr_ue_ctxt_t& ue_ctxt, ue_nr_tti_events& pending_events); - int apply_tti_events(sim_nr_ue_ctxt_t& ue_ctxt, const ue_nr_tti_events& events); + 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); std::string test_name; srslog::basic_logger& logger; @@ -164,8 +164,8 @@ private: std::unique_ptr sched_ptr; std::vector cell_params; - srsran::tti_point current_tti_tx; - int cc_finished = 0; + slot_point current_slot_tx; + int cc_finished = 0; std::map ue_db; diff --git a/srsenb/test/mac/nr/sched_nr_test.cc b/srsenb/test/mac/nr/sched_nr_test.cc index 1bbeca14a..dca575fd8 100644 --- a/srsenb/test/mac/nr/sched_nr_test.cc +++ b/srsenb/test/mac/nr/sched_nr_test.cc @@ -47,22 +47,22 @@ struct task_job_manager { explicit task_job_manager(int max_concurrent_slots = 4) : slot_counter(max_concurrent_slots) {} - void start_slot(tti_point tti, int nof_sectors) + void start_slot(slot_point slot, int nof_sectors) { std::unique_lock lock(mutex); - auto& sl = slot_counter[tti.to_uint() % slot_counter.size()]; + auto& sl = slot_counter[slot.to_uint() % slot_counter.size()]; while (sl.count > 0) { sl.cvar.wait(lock); } sl.count = nof_sectors; } - void finish_cc(tti_point tti, const dl_sched_t& dl_res, const sched_nr_interface::ul_sched_t& ul_res) + void finish_cc(slot_point slot, const dl_sched_t& dl_res, const sched_nr_interface::ul_sched_t& ul_res) { std::unique_lock lock(mutex); TESTASSERT(dl_res.pdcch_dl.size() <= 1); res_count++; pdsch_count += dl_res.pdcch_dl.size(); - auto& sl = slot_counter[tti.to_uint() % slot_counter.size()]; + auto& sl = slot_counter[slot.to_uint() % slot_counter.size()]; if (--sl.count == 0) { sl.cvar.notify_one(); } @@ -100,23 +100,23 @@ void sched_nr_cfg_serialized_test() sched_tester.add_user(0x46, uecfg, 0); std::vector count_per_cc(nof_sectors, 0); - for (uint32_t nof_ttis = 0; nof_ttis < max_nof_ttis; ++nof_ttis) { - tti_point tti_rx(nof_ttis % 10240); - tti_point tti_tx = tti_rx + TX_ENB_DELAY; - tasks.start_slot(tti_rx, nof_sectors); - sched_tester.new_slot(tti_tx); + 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; + tasks.start_slot(slot_rx, nof_sectors); + sched_tester.new_slot(slot_tx); for (uint32_t cc = 0; cc < cells_cfg.size(); ++cc) { sched_nr_interface::dl_sched_t dl_res; sched_nr_interface::ul_sched_t ul_res; auto tp1 = std::chrono::steady_clock::now(); - TESTASSERT(sched_tester.get_sched()->get_dl_sched(tti_tx, cc, dl_res) == SRSRAN_SUCCESS); - TESTASSERT(sched_tester.get_sched()->get_ul_sched(tti_tx, cc, ul_res) == SRSRAN_SUCCESS); + TESTASSERT(sched_tester.get_sched()->get_dl_sched(slot_tx, cc, dl_res) == SRSRAN_SUCCESS); + TESTASSERT(sched_tester.get_sched()->get_ul_sched(slot_tx, cc, ul_res) == SRSRAN_SUCCESS); auto tp2 = std::chrono::steady_clock::now(); count_per_cc[cc] += std::chrono::duration_cast(tp2 - tp1).count(); - sched_nr_cc_output_res_t out{tti_tx, cc, &dl_res, &ul_res}; + sched_nr_cc_output_res_t out{slot_tx, cc, &dl_res, &ul_res}; sched_tester.update(out); - tasks.finish_cc(tti_rx, dl_res, ul_res); - TESTASSERT(not srsran_tdd_nr_is_dl(&cells_cfg[cc].tdd, 0, (tti_tx).sf_idx()) or dl_res.pdcch_dl.size() == 1); + tasks.finish_cc(slot_rx, dl_res, ul_res); + TESTASSERT(not srsran_tdd_nr_is_dl(&cells_cfg[cc].tdd, 0, (slot_tx).slot_idx()) or dl_res.pdcch_dl.size() == 1); } } @@ -148,24 +148,24 @@ void sched_nr_cfg_parallel_cc_test() sched_tester.add_user(0x46, uecfg, 0); std::array, SRSRAN_MAX_CARRIERS> nano_count{}; - for (uint32_t nof_ttis = 0; nof_ttis < max_nof_ttis; ++nof_ttis) { - tti_point tti_rx(nof_ttis % 10240); - tti_point tti_tx = tti_rx + TX_ENB_DELAY; - tasks.start_slot(tti_tx, nof_sectors); - sched_tester.new_slot(tti_tx); + 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; + tasks.start_slot(slot_tx, nof_sectors); + sched_tester.new_slot(slot_tx); for (uint32_t cc = 0; cc < cells_cfg.size(); ++cc) { - srsran::get_background_workers().push_task([cc, tti_tx, &tasks, &sched_tester, &nano_count]() { + srsran::get_background_workers().push_task([cc, slot_tx, &tasks, &sched_tester, &nano_count]() { sched_nr_interface::dl_sched_t dl_res; sched_nr_interface::ul_sched_t ul_res; auto tp1 = std::chrono::steady_clock::now(); - TESTASSERT(sched_tester.get_sched()->get_dl_sched(tti_tx, cc, dl_res) == SRSRAN_SUCCESS); - TESTASSERT(sched_tester.get_sched()->get_ul_sched(tti_tx, cc, ul_res) == SRSRAN_SUCCESS); + TESTASSERT(sched_tester.get_sched()->get_dl_sched(slot_tx, cc, dl_res) == SRSRAN_SUCCESS); + TESTASSERT(sched_tester.get_sched()->get_ul_sched(slot_tx, cc, ul_res) == SRSRAN_SUCCESS); auto tp2 = std::chrono::steady_clock::now(); nano_count[cc].fetch_add(std::chrono::duration_cast(tp2 - tp1).count(), std::memory_order_relaxed); - sched_nr_cc_output_res_t out{tti_tx, cc, &dl_res, &ul_res}; + sched_nr_cc_output_res_t out{slot_tx, cc, &dl_res, &ul_res}; sched_tester.update(out); - tasks.finish_cc(tti_tx, dl_res, ul_res); + tasks.finish_cc(slot_tx, dl_res, ul_res); }); } } @@ -201,4 +201,4 @@ int main() srsenb::sched_nr_cfg_serialized_test(); srsenb::sched_nr_cfg_parallel_cc_test(); -} \ No newline at end of file +} diff --git a/srsenb/test/mac/nr/sched_nr_ue_ded_test_suite.cc b/srsenb/test/mac/nr/sched_nr_ue_ded_test_suite.cc index a4c3dc93c..5890c1d45 100644 --- a/srsenb/test/mac/nr/sched_nr_ue_ded_test_suite.cc +++ b/srsenb/test/mac/nr/sched_nr_ue_ded_test_suite.cc @@ -29,9 +29,9 @@ using namespace srsenb::sched_nr_impl; void test_dl_sched_result(const sim_nr_enb_ctxt_t& enb_ctxt, const sched_nr_cc_output_res_t& cc_out) { - tti_point pdcch_tti = cc_out.tti; - const pdcch_dl_list_t& pdcchs = cc_out.dl_cc_result->pdcch_dl; - const pdsch_list_t& pdschs = cc_out.dl_cc_result->pdsch; + slot_point pdcch_slot = cc_out.slot; + const pdcch_dl_list_t& pdcchs = cc_out.dl_cc_result->pdcch_dl; + const pdsch_list_t& pdschs = cc_out.dl_cc_result->pdsch; // Iterate over UE PDCCH allocations for (const pdcch_dl_t& pdcch : pdcchs) { @@ -40,7 +40,7 @@ void test_dl_sched_result(const sim_nr_enb_ctxt_t& enb_ctxt, const sched_nr_cc_o } const sim_nr_ue_ctxt_t& ue = *enb_ctxt.ue_db.at(pdcch.dci.ctx.rnti); uint32_t k1 = ue.ue_cfg.phy_cfg.harq_ack - .dl_data_to_ul_ack[pdcch_tti.sf_idx() % ue.ue_cfg.phy_cfg.harq_ack.nof_dl_data_to_ul_ack]; + .dl_data_to_ul_ack[pdcch_slot.slot_idx() % ue.ue_cfg.phy_cfg.harq_ack.nof_dl_data_to_ul_ack]; // CHECK: Carrier activation TESTASSERT(ue.ue_cfg.carriers[cc_out.cc].active); @@ -55,9 +55,9 @@ void test_dl_sched_result(const sim_nr_enb_ctxt_t& enb_ctxt, const sched_nr_cc_o if (pdcch.dci.ctx.format == srsran_dci_format_nr_1_0) { TESTASSERT(pdcch.dci.harq_feedback == k1 - 1); } else { - TESTASSERT(pdcch.dci.harq_feedback == pdcch_tti.sf_idx()); + TESTASSERT(pdcch.dci.harq_feedback == pdcch_slot.slot_idx()); } - TESTASSERT(ue.cc_list[cc_out.cc].pending_acks[(pdcch_tti + k1).to_uint()] % 4 == pdcch.dci.dai); + TESTASSERT(ue.cc_list[cc_out.cc].pending_acks[(pdcch_slot + k1).to_uint()] % 4 == pdcch.dci.dai); } for (const pdsch_t& pdsch : pdschs) { diff --git a/srsenb/test/mac/sched_dci_test.cc b/srsenb/test/mac/sched_dci_test.cc index 25b2be4d2..45ee5d93a 100644 --- a/srsenb/test/mac/sched_dci_test.cc +++ b/srsenb/test/mac/sched_dci_test.cc @@ -280,6 +280,43 @@ int test_min_mcs_tbs_specific() return SRSRAN_SUCCESS; } +void test_ul_mcs_tbs_derivation() +{ + uint32_t cqi = 15; + uint32_t max_mcs = 28; + + sched_cell_params_t cell_params; + prbmask_t prbs; + + auto compute_tbs_mcs = [&prbs, &cell_params, &max_mcs, &cqi](uint32_t Nprb, uint32_t prb_grant_size) { + sched_interface::cell_cfg_t cell_cfg = generate_default_cell_cfg(Nprb); + sched_interface::sched_args_t sched_args = {}; + cell_params.set_cfg(0, cell_cfg, sched_args); + prbs.resize(Nprb); + prbs.fill(2, prb_grant_size); + uint32_t req_bytes = 1000000; + uint32_t N_srs = 0; + uint32_t nof_symb = 2 * (SRSRAN_CP_NSYMB(cell_params.cfg.cell.cp) - 1) - N_srs; + uint32_t nof_re = nof_symb * prbs.count() * SRSRAN_NRE; + return compute_min_mcs_and_tbs_from_required_bytes( + prbs.count(), nof_re, cqi, max_mcs, req_bytes, true, false, false); + }; + + cqi = 0; + TESTASSERT(compute_tbs_mcs(25, 25 - 4).mcs == 0); + TESTASSERT(compute_tbs_mcs(50, 50 - 5).mcs == 0); + + cqi = 5; + TESTASSERT(compute_tbs_mcs(25, 25 - 4).mcs == 9); + TESTASSERT(compute_tbs_mcs(50, 50 - 5).mcs == 9); + + cqi = 15; + TESTASSERT(compute_tbs_mcs(25, 25 - 4).mcs == 23); + TESTASSERT(compute_tbs_mcs(50, 50 - 5).mcs == 23); + TESTASSERT(compute_tbs_mcs(75, 75 - 5).mcs == 24); + TESTASSERT(compute_tbs_mcs(100, 100 - 5).mcs == 23); +} + } // namespace srsenb int main() @@ -295,7 +332,8 @@ int main() TESTASSERT(srsenb::test_mcs_lookup_specific() == SRSRAN_SUCCESS); TESTASSERT(srsenb::test_mcs_tbs_consistency_all() == SRSRAN_SUCCESS); TESTASSERT(srsenb::test_min_mcs_tbs_specific() == SRSRAN_SUCCESS); + srsenb::test_ul_mcs_tbs_derivation(); printf("Success\n"); return 0; -} \ No newline at end of file +} diff --git a/srsenb/test/mac/sched_ue_cell_test.cc b/srsenb/test/mac/sched_ue_cell_test.cc index 89b7f90da..fb126d004 100644 --- a/srsenb/test/mac/sched_ue_cell_test.cc +++ b/srsenb/test/mac/sched_ue_cell_test.cc @@ -68,6 +68,73 @@ void test_neg_phr_scenario() TESTASSERT(tbinfo.tbs_bytes >= 10); } +void test_interferer_subband_cqi_scenario() +{ + uint32_t Nprb = 50; + sched_interface::cell_cfg_t cell_cfg = generate_default_cell_cfg(Nprb); + sched_interface::sched_args_t sched_cfg = {}; + sched_cell_params_t cell_params; + cell_params.set_cfg(0, cell_cfg, sched_cfg); + sched_interface::ue_cfg_t ue_cfg = generate_default_ue_cfg(); + + sched_ue_cell ue_cc(0x46, cell_params, tti_point(0)); + ue_cfg.supported_cc_list[0].dl_cfg.cqi_report.subband_wideband_ratio = 4; + ue_cfg.supported_cc_list[0].dl_cfg.cqi_report.periodic_configured = true; + ue_cc.set_ue_cfg(ue_cfg); + + TESTASSERT(ue_cc.dl_cqi().subband_cqi_enabled()); + TESTASSERT(ue_cc.dl_cqi().nof_bandwidth_parts() == 3); + TESTASSERT(ue_cc.dl_cqi().nof_subbands() == 9); + + ue_cc.set_dl_wb_cqi(tti_point{0}, 10); + ue_cc.set_dl_sb_cqi(tti_point{40}, 1, 15); + ue_cc.set_dl_sb_cqi(tti_point{80}, 3, 15); + ue_cc.set_dl_sb_cqi(tti_point{160}, 8, 0); // interferer in last BP + + rbgmask_t test_mask(cell_params.nof_rbgs); + test_mask.fill(0, 12); + + rbgmask_t rbgs(cell_params.nof_rbgs); + tbs_info tb; + rbgmask_t grant_mask(cell_params.nof_rbgs); + TESTASSERT(find_optimal_rbgmask(ue_cc, + tti_point{160 + TX_ENB_DELAY}, + rbgs, + SRSRAN_DCI_FORMAT1, + srsran::interval{0, 10000}, + tb, + grant_mask)); + TESTASSERT(grant_mask == test_mask); + + ue_cc.set_dl_wb_cqi(tti_point{0}, 15); + ue_cc.set_dl_sb_cqi(tti_point{40}, 1, 15); + ue_cc.set_dl_sb_cqi(tti_point{80}, 3, 15); + ue_cc.set_dl_sb_cqi(tti_point{160}, 8, 10); // interferer in last BP + TESTASSERT(find_optimal_rbgmask(ue_cc, + tti_point{160 + TX_ENB_DELAY}, + rbgs, + SRSRAN_DCI_FORMAT1, + srsran::interval{0, 10000}, + tb, + grant_mask)); + TESTASSERT(grant_mask == test_mask); + + ue_cc.set_dl_wb_cqi(tti_point{0}, 15); + ue_cc.set_dl_sb_cqi(tti_point{40}, 1, 15); + ue_cc.set_dl_sb_cqi(tti_point{80}, 3, 15); + ue_cc.set_dl_sb_cqi(tti_point{160}, 8, 14); // interferer in last BP + TESTASSERT(find_optimal_rbgmask(ue_cc, + tti_point{160 + TX_ENB_DELAY}, + rbgs, + SRSRAN_DCI_FORMAT1, + srsran::interval{0, 10000}, + tb, + grant_mask)); + test_mask.reset(); + test_mask.fill(0, cell_params.nof_rbgs); + TESTASSERT(grant_mask == test_mask); +} + int main() { srsenb::set_randseed(seed); @@ -80,6 +147,7 @@ int main() srslog::init(); test_neg_phr_scenario(); + test_interferer_subband_cqi_scenario(); srslog::flush(); diff --git a/srsenb/test/ngap/ngap_test.cc b/srsenb/test/ngap/ngap_test.cc index f2fcb33f9..aa0d45e62 100644 --- a/srsenb/test/ngap/ngap_test.cc +++ b/srsenb/test/ngap/ngap_test.cc @@ -71,6 +71,24 @@ struct amf_dummy { srsran::unique_byte_buffer_t last_sdu; }; +class rrc_nr_dummy : public rrc_interface_ngap_nr +{ +public: + int ue_set_security_cfg_key(uint16_t rnti, const asn1::fixed_bitstring<256, false, true>& key) + { + return SRSRAN_SUCCESS; + } + int ue_set_bitrates(uint16_t rnti, const asn1::ngap_nr::ue_aggregate_maximum_bit_rate_s& rates) + { + return SRSRAN_SUCCESS; + } + int ue_set_security_cfg_capabilities(uint16_t rnti, const asn1::ngap_nr::ue_security_cap_s& caps) + { + return SRSRAN_SUCCESS; + } + int start_security_mode_procedure(uint16_t rnti) { return SRSRAN_SUCCESS; } + void write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu) {} +}; struct dummy_socket_manager : public srsran::socket_manager_itf { dummy_socket_manager() : srsran::socket_manager_itf(srslog::fetch_basic_logger("TEST")) {} @@ -152,7 +170,8 @@ int main(int argc, char** argv) args.gtp_bind_addr = "127.0.0.100"; args.amf_addr = "127.0.0.1"; args.gnb_name = "srsgnb01"; - rrc_interface_ngap_nr rrc; + + rrc_nr_dummy rrc; ngap_obj.init(args, &rrc); // Start the log backend. diff --git a/srsenb/test/phy/enb_phy_test.cc b/srsenb/test/phy/enb_phy_test.cc index f09d51617..c0b7c8c91 100644 --- a/srsenb/test/phy/enb_phy_test.cc +++ b/srsenb/test/phy/enb_phy_test.cc @@ -1382,7 +1382,7 @@ public: } break; case change_state_flush: - if (tti_counter >= 2 * FDD_HARQ_DELAY_DL_MS + FDD_HARQ_DELAY_UL_MS) { + if (tti_counter >= 2 * (FDD_HARQ_DELAY_DL_MS + FDD_HARQ_DELAY_UL_MS)) { logger.warning("******* Cell rotation: Reconfigure *******"); std::array activation = {}; ///< Activation/Deactivation vector @@ -1489,6 +1489,10 @@ int parse_args(int argc, char** argv, phy_test_bench::args_t& args) int main(int argc, char** argv) { + // First of all, initialise crash handler - Crash files from ctest executions will be stored in + // srsenb/test/phy/srsRAN.backtrace.crash + srsran_debug_handle_crash(argc, argv); + phy_test_bench::args_t test_args; // Parse arguments @@ -1507,20 +1511,23 @@ int main(int argc, char** argv) if (not valid_cfg) { // Verify that phy returns with an error if provided an invalid configuration TESTASSERT(err_code != SRSRAN_SUCCESS); - return 0; + return SRSRAN_SUCCESS; } - TESTASSERT(err_code == SRSRAN_SUCCESS); // Run Simulation - for (uint32_t i = 0; i < test_args.duration; i++) { - TESTASSERT(test_bench->run_tti() >= SRSRAN_SUCCESS); + for (uint32_t i = 0; i < test_args.duration and err_code >= SRSRAN_SUCCESS; i++) { + err_code = test_bench->run_tti(); } test_bench->stop(); srslog::flush(); - std::cout << "Passed" << std::endl; + if (err_code >= SRSRAN_SUCCESS) { + std::cout << "Ok" << std::endl; + } else { + std::cout << "Error" << std::endl; + } - return SRSRAN_SUCCESS; + return err_code; } diff --git a/srsue/hdr/phy/lte/sf_worker.h b/srsue/hdr/phy/lte/sf_worker.h index 98c0b7d8f..f495dd968 100644 --- a/srsue/hdr/phy/lte/sf_worker.h +++ b/srsue/hdr/phy/lte/sf_worker.h @@ -51,8 +51,7 @@ public: /* Functions used by main PHY thread */ cf_t* get_buffer(uint32_t cc_idx, uint32_t antenna_idx); uint32_t get_buffer_len(); - void set_tti(uint32_t tti); - void set_tx_time(const srsran::rf_timestamp_t& tx_time); + void set_context(const srsran::phy_common_interface::worker_context_t& w_ctx); void set_prach(cf_t* prach_ptr, float prach_power); void set_cfo_unlocked(const uint32_t& cc_idx, float cfo); @@ -98,8 +97,7 @@ private: cf_t* prach_ptr = nullptr; float prach_power = 0; - uint32_t tti = 0; - srsran::rf_timestamp_t tx_time = {}; + srsran::phy_common_interface::worker_context_t context = {}; }; } // namespace lte diff --git a/srsue/hdr/phy/nr/sf_worker.h b/srsue/hdr/phy/nr/sf_worker.h index e42609261..fafb0b56a 100644 --- a/srsue/hdr/phy/nr/sf_worker.h +++ b/srsue/hdr/phy/nr/sf_worker.h @@ -48,8 +48,7 @@ public: /* Functions used by main PHY thread */ cf_t* get_buffer(uint32_t cc_idx, uint32_t antenna_idx); uint32_t get_buffer_len(); - void set_tti(uint32_t tti); - void set_tx_time(const srsran::rf_timestamp_t& tx_time_); + void set_context(const srsran::phy_common_interface::worker_context_t& w_ctx); int read_pdsch_d(cf_t* pdsch_d); void start_plot(); @@ -61,13 +60,14 @@ private: std::vector > cc_workers; - srsran::phy_common_interface& common; - state& phy_state; - srslog::basic_logger& logger; - srsran::rf_timestamp_t tx_time = {}; - uint32_t tti_rx = 0; - cf_t* prach_ptr = nullptr; - float prach_power = 0; + srsran::phy_common_interface& common; + state& phy_state; + srslog::basic_logger& logger; + srsran::rf_timestamp_t tx_time = {}; + uint32_t tti_rx = 0; + cf_t* prach_ptr = nullptr; + float prach_power = 0; + srsran::phy_common_interface::worker_context_t context = {}; }; } // namespace nr diff --git a/srsue/hdr/phy/nr/state.h b/srsue/hdr/phy/nr/state.h index b6058f44a..4daed4843 100644 --- a/srsue/hdr/phy/nr/state.h +++ b/srsue/hdr/phy/nr/state.h @@ -260,7 +260,7 @@ public: // Insert PDSCH transmission information if (srsran_harq_ack_insert_m(&ack, &ack_m) < SRSRAN_SUCCESS) { - ERROR("Error inserting ACK m value"); + ERROR("Error inserting ACK m value for Tx slot %d", tti_tx); } } diff --git a/srsue/hdr/phy/phy_common.h b/srsue/hdr/phy/phy_common.h index 7c7ee7823..ddbfaa058 100644 --- a/srsue/hdr/phy/phy_common.h +++ b/srsue/hdr/phy/phy_common.h @@ -139,11 +139,7 @@ public: srsran_pdsch_ack_resource_t resource); bool get_dl_pending_ack(srsran_ul_sf_cfg_t* sf, uint32_t cc_idx, srsran_pdsch_ack_cc_t* ack); - void worker_end(void* h, - bool tx_enable, - srsran::rf_buffer_t& buffer, - srsran::rf_timestamp_t& tx_time, - bool is_nr) override; + void worker_end(const worker_context_t& w_ctx, const bool& tx_enable, srsran::rf_buffer_t& buffer) override; void set_cell(const srsran_cell_t& c); @@ -380,8 +376,8 @@ private: bool is_mcch_subframe(srsran_mbsfn_cfg_t* cfg, uint32_t phy_tti); // NR carriers buffering synchronization, LTE workers are in charge of transmitting - srsran::rf_buffer_t nr_tx_buffer; - bool nr_tx_buffer_ready = false; + bool tx_enabled = false; + srsran::rf_buffer_t tx_buffer = {}; }; } // namespace srsue diff --git a/srsue/hdr/stack/rrc/rrc.h b/srsue/hdr/stack/rrc/rrc.h index 62878f1ac..eac1412f9 100644 --- a/srsue/hdr/stack/rrc/rrc.h +++ b/srsue/hdr/stack/rrc/rrc.h @@ -64,7 +64,7 @@ typedef struct { #define SRSRAN_UE_CATEGORY_MAX 21 #define SRSRAN_RELEASE_MIN 8 #define SRSRAN_RELEASE_MAX 15 -#define SRSRAN_RELEASE_DEFAULT (SRSRAN_RELEASE_MAX) +#define SRSRAN_RELEASE_DEFAULT (SRSRAN_RELEASE_MIN) class phy_controller; class usim_interface_rrc; diff --git a/srsue/hdr/stack/rrc/rrc_nr.h b/srsue/hdr/stack/rrc/rrc_nr.h index 37a659e40..7a8a7414e 100644 --- a/srsue/hdr/stack/rrc/rrc_nr.h +++ b/srsue/hdr/stack/rrc/rrc_nr.h @@ -51,6 +51,7 @@ struct rrc_nr_args_t { core_less_args_t coreless; uint32_t sim_nr_meas_pci; bool pdcp_short_sn_support; + bool skip_rar; std::string supported_bands_nr_str; std::vector supported_bands_nr; std::vector supported_bands_eutra; diff --git a/srsue/hdr/stack/upper/nas_5gmm_state.h b/srsue/hdr/stack/upper/nas_5gmm_state.h index 8b106ce1f..fd383be0d 100644 --- a/srsue/hdr/stack/upper/nas_5gmm_state.h +++ b/srsue/hdr/stack/upper/nas_5gmm_state.h @@ -84,8 +84,8 @@ public: private: std::atomic state{state_t::null}; - deregistered_substate_t deregistered_substate = deregistered_substate_t::null; - registered_substate_t registered_substate = registered_substate_t::null; + std::atomic deregistered_substate{deregistered_substate_t::null}; + std::atomic registered_substate{registered_substate_t::null}; srslog::basic_logger& logger = srslog::fetch_basic_logger("NAS-5G"); }; @@ -94,4 +94,5 @@ const char* mm5g_deregistered_substate_text(mm5g_state_t::deregistered_substate_ const char* mm5g_registered_substate_text(mm5g_state_t::registered_substate_t type); } // namespace srsue + #endif diff --git a/srsue/hdr/stack/upper/nas_emm_state.h b/srsue/hdr/stack/upper/nas_emm_state.h index 2ae63f6d6..e73e6d0bd 100644 --- a/srsue/hdr/stack/upper/nas_emm_state.h +++ b/srsue/hdr/stack/upper/nas_emm_state.h @@ -86,10 +86,10 @@ public: const std::string get_full_state_text(); private: - std::atomic state{state_t::null}; // The GW might require to know the NAS state from another thread - deregistered_substate_t deregistered_substate = deregistered_substate_t::null; - registered_substate_t registered_substate = registered_substate_t::null; - srslog::basic_logger& logger = srslog::fetch_basic_logger("NAS"); + std::atomic state{state_t::null}; // The GW might require to know the NAS state from another thread + std::atomic deregistered_substate{deregistered_substate_t::null}; + std::atomic registered_substate{registered_substate_t::null}; + srslog::basic_logger& logger = srslog::fetch_basic_logger("NAS"); }; const char* emm_state_text(emm_state_t::state_t type); @@ -97,4 +97,5 @@ const char* emm_deregistered_substate_text(emm_state_t::deregistered_substate_t const char* emm_registered_substate_text(emm_state_t::registered_substate_t type); } // namespace srsue + #endif diff --git a/srsue/hdr/stack/upper/usim_base.h b/srsue/hdr/stack/upper/usim_base.h index 91560767f..f6e21428b 100644 --- a/srsue/hdr/stack/upper/usim_base.h +++ b/srsue/hdr/stack/upper/usim_base.h @@ -79,6 +79,9 @@ public: std::string get_imei_str() final; bool get_imsi_vec(uint8_t* imsi_, uint32_t n) final; + bool get_home_mcc_bytes(uint8_t* mcc_, uint32_t n) final; + bool get_home_mnc_bytes(uint8_t* mnc_, uint32_t n) final; + bool get_home_msin_bcd(uint8_t* msin_, uint32_t n) final; bool get_imei_vec(uint8_t* imei_, uint32_t n) final; bool get_home_plmn_id(srsran::plmn_id_t* home_plmn_id) final; diff --git a/srsue/src/main.cc b/srsue/src/main.cc index 66f0e1b53..e4380085c 100644 --- a/srsue/src/main.cc +++ b/srsue/src/main.cc @@ -141,6 +141,7 @@ static int parse_args(all_args_t* args, int argc, char* argv[]) ("rrc.mbms_service_port", bpo::value(&args->stack.rrc.mbms_service_port)->default_value(4321), "Port of the MBMS service") ("rrc.nr_measurement_pci", bpo::value(&args->stack.rrc_nr.sim_nr_meas_pci)->default_value(500), "NR PCI for the simulated NR measurement") ("rrc.nr_short_sn_support", bpo::value(&args->stack.rrc_nr.pdcp_short_sn_support)->default_value(true), "Announce PDCP short SN support") + ("rrc.skip_nr_rar", bpo::value(&args->stack.rrc_nr.skip_rar)->default_value(false), "Whether to skip RAR reception (temporary feature)") ("nas.apn", bpo::value(&args->stack.nas.apn_name)->default_value(""), "Set Access Point Name (APN) for data services") ("nas.apn_protocol", bpo::value(&args->stack.nas.apn_protocol)->default_value(""), "Set Access Point Name (APN) protocol for data services") diff --git a/srsue/src/phy/lte/sf_worker.cc b/srsue/src/phy/lte/sf_worker.cc index c6ad785ac..cecc7f135 100644 --- a/srsue/src/phy/lte/sf_worker.cc +++ b/srsue/src/phy/lte/sf_worker.cc @@ -112,20 +112,15 @@ uint32_t sf_worker::get_buffer_len() return cc_workers.at(0)->get_buffer_len(); } -void sf_worker::set_tti(uint32_t tti_) +void sf_worker::set_context(const srsran::phy_common_interface::worker_context_t& w_ctx) { - tti = tti_; + context.copy(w_ctx); for (auto& cc_worker : cc_workers) { - cc_worker->set_tti(tti); + cc_worker->set_tti(w_ctx.sf_idx); } - logger.set_context(tti); -} - -void sf_worker::set_tx_time(const srsran::rf_timestamp_t& tx_time_) -{ - tx_time.copy(tx_time_); + logger.set_context(w_ctx.sf_idx); } void sf_worker::set_prach(cf_t* prach_ptr_, float prach_power_) @@ -162,9 +157,10 @@ void sf_worker::set_config_unlocked(uint32_t cc_idx, const srsran::phy_cfg_t& ph void sf_worker::work_imp() { + uint32_t tti = context.sf_idx; srsran::rf_buffer_t tx_signal_ptr = {}; if (!cell_initiated) { - phy->worker_end(this, false, tx_signal_ptr, tx_time, false); + phy->worker_end(context, false, tx_signal_ptr); return; } @@ -235,7 +231,7 @@ void sf_worker::work_imp() } // Call worker_end to transmit the signal - phy->worker_end(this, tx_signal_ready, tx_signal_ptr, tx_time, false); + phy->worker_end(context, tx_signal_ready, tx_signal_ptr); if (rx_signal_ok) { update_measurements(); diff --git a/srsue/src/phy/nr/cc_worker.cc b/srsue/src/phy/nr/cc_worker.cc index 590492983..843043d1a 100644 --- a/srsue/src/phy/nr/cc_worker.cc +++ b/srsue/src/phy/nr/cc_worker.cc @@ -538,20 +538,29 @@ bool cc_worker::work_dl() bool cc_worker::work_ul() { + // Gather PDSCH ACK information independently if UL/DL + // If a HARQ ACK Feedback needs to be transmitted in this slot and it is NOT an UL slot, the accumulated HARQ feedback + // for this slot will be flushed + srsran_pdsch_ack_nr_t pdsch_ack = {}; + bool has_ul_ack = phy.get_pending_ack(ul_slot_cfg.idx, pdsch_ack); + // Check if it is a UL slot, if not skip if (!srsran_tdd_nr_is_ul(&phy.cfg.tdd, 0, ul_slot_cfg.idx)) { // No NR signal shall be transmitted srsran_vec_cf_zero(tx_buffer[0], ue_ul.ifft.sf_sz); + + // Check if there is any pending ACK for this DL slot... + if (pdsch_ack.nof_cc > 1) { + // ... in this case log a warning to inform about miss-configuration + logger.warning("Detected HARQ feedback on DL slot"); + } + return true; } srsran_uci_data_nr_t uci_data = {}; uint32_t pid = 0; - // Gather PDSCH ACK information - srsran_pdsch_ack_nr_t pdsch_ack = {}; - bool has_ul_ack = phy.get_pending_ack(ul_slot_cfg.idx, pdsch_ack); - // Request grant to PHY state for this transmit TTI srsran_sch_cfg_nr_t pusch_cfg = {}; bool has_pusch_grant = phy.get_ul_pending_grant(ul_slot_cfg.idx, pusch_cfg, pid); diff --git a/srsue/src/phy/nr/sf_worker.cc b/srsue/src/phy/nr/sf_worker.cc index bc24958ac..87361b936 100644 --- a/srsue/src/phy/nr/sf_worker.cc +++ b/srsue/src/phy/nr/sf_worker.cc @@ -68,18 +68,14 @@ uint32_t sf_worker::get_buffer_len() return cc_workers.at(0)->get_buffer_len(); } -void sf_worker::set_tti(uint32_t tti) +void sf_worker::set_context(const srsran::phy_common_interface::worker_context_t& w_ctx) { - tti_rx = tti; - logger.set_context(tti); + tti_rx = w_ctx.sf_idx; + logger.set_context(w_ctx.sf_idx); for (auto& w : cc_workers) { - w->set_tti(tti); + w->set_tti(w_ctx.sf_idx); } -} - -void sf_worker::set_tx_time(const srsran::rf_timestamp_t& tx_time_) -{ - tx_time.copy(tx_time_); + context.copy(w_ctx); } void sf_worker::work_imp() @@ -98,7 +94,7 @@ void sf_worker::work_imp() // Check if PRACH is available if (prach_ptr != nullptr) { // PRACH is available, set buffer, transmit and return - tx_buffer.set(0, prach_ptr); + tx_buffer.set(phy_state.args.rf_channel_offset, prach_ptr); tx_buffer.set_nof_samples(SRSRAN_SF_LEN_PRB_NR(phy_state.cfg.carrier.nof_prb)); // Notify MAC about PRACH transmission @@ -109,7 +105,7 @@ void sf_worker::work_imp() 0); // Transmit NR PRACH - common.worker_end(this, true, tx_buffer, tx_time, true); + common.worker_end(context, true, tx_buffer); // Reset PRACH pointer prach_ptr = nullptr; @@ -124,12 +120,12 @@ void sf_worker::work_imp() // Set Tx buffers for (uint32_t i = 0; i < (uint32_t)cc_workers.size(); i++) { - tx_buffer.set(i, cc_workers[i]->get_tx_buffer(0)); + tx_buffer.set(i + phy_state.args.rf_channel_offset, cc_workers[i]->get_tx_buffer(0)); } tx_buffer.set_nof_samples(SRSRAN_SF_LEN_PRB_NR(phy_state.cfg.carrier.nof_prb)); // Always call worker_end before returning - common.worker_end(this, true, tx_buffer, tx_time, true); + common.worker_end(context, true, tx_buffer); // Tell the plotting thread to draw the plots #ifdef ENABLE_GUI diff --git a/srsue/src/phy/phy_common.cc b/srsue/src/phy/phy_common.cc index 7e700df91..f7b48e104 100644 --- a/srsue/src/phy/phy_common.cc +++ b/srsue/src/phy/phy_common.cc @@ -54,10 +54,10 @@ void phy_common::init(phy_args_t* _args, stack_interface_phy_lte* _stack, rsrp_insync_itf* _chest_loop) { - radio_h = _radio; - stack = _stack; - args = _args; - insync_itf = _chest_loop; + radio_h = _radio; + stack = _stack; + args = _args; + insync_itf = _chest_loop; sr.reset(); // Instantiate UL channel emulator @@ -540,48 +540,49 @@ bool phy_common::get_dl_pending_ack(srsran_ul_sf_cfg_t* sf, uint32_t cc_idx, srs * Each worker uses this function to indicate that all processing is done and data is ready for transmission or * there is no transmission at all (tx_enable). In that case, the end of burst message will be sent to the radio */ -void phy_common::worker_end(void* tx_sem_id, - bool tx_enable, - srsran::rf_buffer_t& buffer, - srsran::rf_timestamp_t& tx_time, - bool is_nr) +void phy_common::worker_end(const worker_context_t& w_ctx, const bool& tx_enable, srsran::rf_buffer_t& buffer) { // Wait for the green light to transmit in the current TTI - semaphore.wait(tx_sem_id); + semaphore.wait(w_ctx.worker_ptr); - // If this is for NR, save Tx buffers... - if (is_nr) { - nr_tx_buffer = buffer; - nr_tx_buffer_ready = true; - semaphore.release(); - return; + // For each channel set or combine baseband + if (tx_enable) { + tx_buffer.set_combine(buffer); + + // Flag transmit enabled + tx_enabled = true; } - // ... otherwise, append NR base-band from saved buffer if available - if (nr_tx_buffer_ready) { - // Load NR carrier base-band - for (uint32_t i = 0; i < args->nof_nr_carriers * args->nof_rx_ant; i++) { - uint32_t channel_idx = args->nof_lte_carriers * args->nof_rx_ant + i; - buffer.set(channel_idx, nr_tx_buffer.get(i)); - } + // If the current worker is not the last one, skip transmission + if (not w_ctx.last) { + // Release semaphore and let next worker to get in + semaphore.release(); - // Remove NR buffer flag - nr_tx_buffer_ready = false; + // If this worker transmitted, hold the worker until last SF worker finishes + if (tx_enable) { + wait_last_worker(); + } - // Make sure it transmits in this TTI - tx_enable = true; + return; } - // Add Time Alignment + // Add current time alignment + srsran::rf_timestamp_t tx_time = w_ctx.tx_time; // get transmit time from the last worker tx_time.sub((double)ta.get_sec()); - // For each radio, transmit - if (tx_enable) { + // Check if any worker had a transmission + if (tx_enabled) { + // Set number of samples to the latest transmit buffer + tx_buffer.set_nof_samples(buffer.get_nof_samples()); + + // Run uplink channel emulator if (ul_channel) { - ul_channel->run(buffer.to_cf_t(), buffer.to_cf_t(), buffer.get_nof_samples(), tx_time.get(0)); + ul_channel->run(tx_buffer.to_cf_t(), tx_buffer.to_cf_t(), tx_buffer.get_nof_samples(), tx_time.get(0)); } - radio_h->tx(buffer, tx_time); + // Actual baseband transmission + radio_h->tx(tx_buffer, tx_time); + } else { if (radio_h->is_continuous_tx()) { if (is_pending_tx_end) { @@ -603,6 +604,15 @@ void phy_common::worker_end(void* tx_sem_id, } } + // Notify that last SF worker finished. Releases all the threads waiting. + last_worker(); + + // Reset tx buffer to prevent next SF uses previous data + tx_enabled = false; + for (uint32_t ch = 0; ch < SRSRAN_MAX_CHANNELS; ch++) { + tx_buffer.set(ch, nullptr); + } + // Allow next TTI to transmit semaphore.release(); } diff --git a/srsue/src/phy/sync.cc b/srsue/src/phy/sync.cc index 5af985300..0c21089e8 100644 --- a/srsue/src/phy/sync.cc +++ b/srsue/src/phy/sync.cc @@ -528,11 +528,8 @@ void sync::run_camping_in_sync_state(lte::sf_worker* lte_worker, worker_com->update_cfo_measurement(cc, cfo); } - lte_worker->set_tti(tti); - // Compute TX time: Any transmission happens in TTI+4 thus advance 4 ms the reception time last_rx_time.add(FDD_HARQ_DELAY_DL_MS * 1e-3); - lte_worker->set_tx_time(last_rx_time); // Advance/reset prach subframe pointer if (prach_ptr) { @@ -543,17 +540,35 @@ void sync::run_camping_in_sync_state(lte::sf_worker* lte_worker, } } - // Start NR worker only if present + // Set NR worker context and start if (nr_worker != nullptr) { + srsran::phy_common_interface::worker_context_t context; + context.sf_idx = tti; + context.worker_ptr = nr_worker; + context.last = (lte_worker == nullptr); // Set last if standalone + 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. - nr_worker->set_tti(tti); worker_com->semaphore.push(nr_worker); nr_worker_pool->start_worker(nr_worker); } - // Start LTE worker - worker_com->semaphore.push(lte_worker); - lte_worker_pool->start_worker(lte_worker); + // Set LTE worker context and start + if (lte_worker != nullptr) { + srsran::phy_common_interface::worker_context_t context; + context.sf_idx = tti; + context.worker_ptr = lte_worker; + context.last = true; + context.tx_time.copy(last_rx_time); + + lte_worker->set_context(context); + + // NR worker needs to be launched first, phy_common::worker_end expects first the NR worker and the LTE worker. + worker_com->semaphore.push(lte_worker); + lte_worker_pool->start_worker(lte_worker); + } } void sync::run_camping_state() { @@ -1051,7 +1066,7 @@ void sync::set_inter_frequency_measurement(uint32_t cc_idx, uint32_t earfcn_, sr void sync::set_cells_to_meas(uint32_t earfcn_, const std::set& pci) { std::lock_guard lock(intra_freq_cfg_mutex); - bool found = false; + bool found = false; for (size_t i = 0; i < intra_freq_meas.size() and not found; i++) { if (earfcn_ == intra_freq_meas[i]->get_earfcn()) { intra_freq_meas[i]->set_cells_to_meas(pci); diff --git a/srsue/src/stack/mac_nr/proc_ra_nr.cc b/srsue/src/stack/mac_nr/proc_ra_nr.cc index a493e3fce..f4bcec439 100644 --- a/srsue/src/stack/mac_nr/proc_ra_nr.cc +++ b/srsue/src/stack/mac_nr/proc_ra_nr.cc @@ -300,15 +300,20 @@ void proc_ra_nr::ra_error() reset(); } } else { - // if the Random Access procedure is not completed + // try again, if RA failed if (preamble_backoff) { backoff_wait = rand() % preamble_backoff; } else { backoff_wait = 0; } - logger.warning("Backoff wait interval %d", backoff_wait); - backoff_timer.set(backoff_wait, [this](uint32_t tid) { timer_expired(tid); }); - backoff_timer.run(); + logger.debug("Backoff wait interval %d", backoff_wait); + + if (backoff_wait > 0) { + backoff_timer.set(backoff_wait, [this](uint32_t tid) { timer_expired(tid); }); + backoff_timer.run(); + } else { + timer_expired(backoff_timer.id()); + } } } @@ -348,6 +353,12 @@ void proc_ra_nr::prach_sent(uint32_t tti, uint32_t s_id, uint32_t t_id, uint32_t ra_window_start = TTI_ADD(tti, 3); logger.debug("Calculated ra_window_start=%d, ra_window_length=%d", ra_window_start, ra_window_length); state = WAITING_FOR_RESPONSE_RECEPTION; + + if (rach_cfg.skip_rar) { + // temp hack for NSA eNB development + state = WAITING_FOR_COMPLETION; + ra_completion(); + } }); } diff --git a/srsue/src/stack/rrc/rrc_nr.cc b/srsue/src/stack/rrc/rrc_nr.cc index 6d4318eb3..eb61d4459 100644 --- a/srsue/src/stack/rrc/rrc_nr.cc +++ b/srsue/src/stack/rrc/rrc_nr.cc @@ -879,6 +879,7 @@ 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.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()); + rach_nr_cfg.skip_rar = args.skip_rar; mac->set_config(rach_nr_cfg); // Make the RACH configuration for PHY @@ -925,7 +926,7 @@ 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.pucch_cfg_common_present) { if (ul_cfg_common.init_ul_bwp.pucch_cfg_common.type() == setup_release_c::types_opts::setup) { - logger.info("PUCCH cfg commont setup not handled"); + logger.info("PUCCH cfg common setup not handled"); } else { logger.warning("Option pucch_cfg_common not of type setup"); return false; @@ -935,7 +936,7 @@ bool rrc_nr::apply_ul_common_cfg(const asn1::rrc_nr::ul_cfg_common_s& ul_cfg_com return false; } } else { - logger.warning("Option init_ul_bwp not present"); + logger.warning("Option init_ul_bwp in spCellConfigCommon not present"); return false; } return true; @@ -1216,7 +1217,7 @@ bool rrc_nr::apply_sp_cell_cfg(const sp_cell_cfg_s& sp_cell_cfg) return false; } } else { - logger.warning("Option pucch_cfg not present"); + logger.warning("Option pucch_cfg for initial UL BWP in spCellConfigDedicated not present"); return false; } if (sp_cell_cfg.sp_cell_cfg_ded.ul_cfg.init_ul_bwp.pusch_cfg_present) { @@ -1230,15 +1231,15 @@ bool rrc_nr::apply_sp_cell_cfg(const sp_cell_cfg_s& sp_cell_cfg) return false; } } else { - logger.warning("Option pusch_cfg not present"); + logger.warning("Option pusch_cfg in spCellConfigDedicated not present"); return false; } } else { - logger.warning("Option init_ul_bwp not present"); + logger.warning("Option init_ul_bwp in spCellConfigDedicated not present"); return false; } } else { - logger.warning("Option ul_cfg not present"); + logger.warning("Option ul_cfg in spCellConfigDedicated not present"); return false; } @@ -1268,7 +1269,7 @@ bool rrc_nr::apply_sp_cell_cfg(const sp_cell_cfg_s& sp_cell_cfg) return false; } } else { - logger.warning("Option csi_meas_cfg not present"); + logger.warning("Option csi_meas_cfg in spCellConfigDedicated not present"); return false; } diff --git a/srsue/src/stack/upper/nas_5g.cc b/srsue/src/stack/upper/nas_5g.cc index 6580bde70..942002792 100644 --- a/srsue/src/stack/upper/nas_5g.cc +++ b/srsue/src/stack/upper/nas_5g.cc @@ -156,6 +156,8 @@ int nas_5g::send_registration_request() logger.info("Generating registration request"); nas_5gs_msg nas_msg; + nas_msg.hdr.extended_protocol_discriminator = + nas_5gs_hdr::extended_protocol_discriminator_opts::extended_protocol_discriminator_5gmm; registration_request_t& reg_req = nas_msg.set_registration_request(); reg_req.registration_type_5gs.follow_on_request_bit = @@ -164,11 +166,11 @@ int nas_5g::send_registration_request() registration_type_5gs_t::registration_type_type_::options::initial_registration; mobile_identity_5gs_t::suci_s& suci = reg_req.mobile_identity_5gs.set_suci(); suci.supi_format = mobile_identity_5gs_t::suci_s::supi_format_type_::options::imsi; - mcc_to_bytes(0x0, suci.mcc.data()); - uint8_t mnc_len; - mnc_to_bytes(0x0, suci.mnc.data(), &mnc_len); - suci.scheme_output.resize(15); - usim->get_imsi_vec(suci.scheme_output.data(), 15); + usim->get_home_mcc_bytes(suci.mcc.data(), suci.mcc.size()); + usim->get_home_mcc_bytes(suci.mnc.data(), suci.mnc.size()); + + suci.scheme_output.resize(5); + usim->get_home_msin_bcd(suci.scheme_output.data(), 5); logger.info("Requesting IMSI attach (IMSI=%s)", usim->get_imsi_str().c_str()); if (nas_msg.pack(pdu) != SRSASN_SUCCESS) { diff --git a/srsue/src/stack/upper/test/usim_test.cc b/srsue/src/stack/upper/test/usim_test.cc index cd3fc4030..879db81e9 100644 --- a/srsue/src/stack/upper/test/usim_test.cc +++ b/srsue/src/stack/upper/test/usim_test.cc @@ -60,12 +60,8 @@ static uint8_t autn_enb[] = static constexpr uint16_t mcc = 208; static constexpr uint16_t mnc = 93; -int main(int argc, char** argv) +int gen_auth_response_test() { - auto& logger = srslog::fetch_basic_logger("USIM", false); - // Start the log backend. - srslog::init(); - uint8_t res[16]; int res_len; uint8_t k_asme[32]; @@ -78,8 +74,60 @@ int main(int argc, char** argv) args.using_op = true; args.op = "11111111111111111111111111111111"; + auto& logger = srslog::fetch_basic_logger("USIM", false); srsue::usim usim(logger); usim.init(&args); TESTASSERT(usim.generate_authentication_response(rand_enb, autn_enb, mcc, mnc, res, &res_len, k_asme) == AUTH_OK); + return SRSRAN_SUCCESS; +} + +int mcc_mnc_msin_test() +{ + usim_args_t args; + args.algo = "milenage"; + args.imei = "356092040793011"; + args.imsi = "208930000000001"; + args.k = "8BAF473F2F8FD09487CCCBD7097C6862"; + args.using_op = true; + args.op = "11111111111111111111111111111111"; + + auto& logger = srslog::fetch_basic_logger("USIM", false); + srsue::usim usim(logger); + usim.init(&args); + + uint8_t mcc_correct[] = {0x2, 0x0, 0x8}; + uint8_t mnc_correct[] = {0x9, 0x3, 0xf}; + uint8_t msin_correct_bcd[] = {0x00, 0x00, 0x00, 0x00, 0x10}; + + uint8_t mcc_test[3]; + usim.get_home_mcc_bytes(mcc_test, 3); + + TESTASSERT(mcc_correct[0] == mcc_test[0]); + TESTASSERT(mcc_correct[1] == mcc_test[1]); + TESTASSERT(mcc_correct[2] == mcc_test[2]); + + uint8_t mnc_test[3]; + usim.get_home_mnc_bytes(mnc_test, 3); + TESTASSERT(mnc_correct[0] == mnc_test[0]); + TESTASSERT(mnc_correct[1] == mnc_test[1]); + TESTASSERT(mnc_correct[2] == mnc_test[2]); + + uint8_t msin_test[5]; + usim.get_home_msin_bcd(msin_test, 5); + + TESTASSERT(msin_correct_bcd[0] == msin_test[0]); + TESTASSERT(msin_correct_bcd[1] == msin_test[1]); + TESTASSERT(msin_correct_bcd[2] == msin_test[2]); + TESTASSERT(msin_correct_bcd[3] == msin_test[3]); + TESTASSERT(msin_correct_bcd[4] == msin_test[4]); + return SRSRAN_SUCCESS; +} + +int main(int argc, char** argv) +{ + // Start the log backend. + srslog::init(); + TESTASSERT(gen_auth_response_test() == SRSRAN_SUCCESS); + TESTASSERT(mcc_mnc_msin_test() == SRSRAN_SUCCESS); } diff --git a/srsue/src/stack/upper/usim_base.cc b/srsue/src/stack/upper/usim_base.cc index 04a83273d..d79b88bfa 100644 --- a/srsue/src/stack/upper/usim_base.cc +++ b/srsue/src/stack/upper/usim_base.cc @@ -20,6 +20,7 @@ */ #include "srsue/hdr/stack/upper/usim_base.h" +#include "srsran/common/bcd_helpers.h" #include "srsue/hdr/stack/upper/usim.h" #ifdef HAVE_PCSC @@ -130,6 +131,88 @@ bool usim_base::get_home_plmn_id(srsran::plmn_id_t* home_plmn_id) return true; } +bool usim_base::get_home_mcc_bytes(uint8_t* mcc_, uint32_t n) +{ + if (!initiated) { + logger.error("USIM not initiated!"); + return false; + } + + if (NULL == mcc_ || n < 3) { + logger.error("Invalid parameters to get_home_mcc_bytes"); + return false; + } + + uint8_t imsi_vec[15]; + get_imsi_vec(imsi_vec, 15); + + std::string mcc_str = get_mcc_str(imsi_vec); + uint16_t mcc_bcd = 0; + + srsran::string_to_mcc(mcc_str, &mcc_bcd); + srsran::mcc_to_bytes(mcc_bcd, mcc_); + + return true; +} + +bool usim_base::get_home_mnc_bytes(uint8_t* mnc_, uint32_t n) +{ + if (!initiated) { + logger.error("USIM not initiated!"); + return false; + } + + if (NULL == mnc_ || n < 3) { + logger.error("Invalid parameters to get_home_mcc_bytes"); + return false; + } + + uint8_t imsi_vec[15]; + get_imsi_vec(imsi_vec, 15); + + std::string mcc_str = get_mcc_str(imsi_vec); + std::string mnc_str = get_mnc_str(imsi_vec, mcc_str); + + uint16_t mnc_bcd = 0; + uint8_t len = 0; + + srsran::string_to_mnc(mnc_str, &mnc_bcd); + srsran::mnc_to_bytes(mnc_bcd, mnc_, &len); + + if (len == 2) { + mnc_[2] = 0xf; + } + + return true; +} + +bool usim_base::get_home_msin_bcd(uint8_t* msin_, uint32_t n) +{ + if (!initiated) { + logger.error("USIM not initiated!"); + return false; + } + + if (NULL == msin_ || n < 5) { + logger.error("Invalid parameters to get_home_mcc_bytes"); + return false; + } + + srsran::plmn_id_t tmp_plmn; + get_home_plmn_id(&tmp_plmn); + + uint8_t imsi_vec[15]; + get_imsi_vec(imsi_vec, 15); + + int total_msin_len = (tmp_plmn.nof_mnc_digits + 3) / 2; + int j = 0; + for (int i = tmp_plmn.nof_mnc_digits + 3; i < 15; i += 2) { + msin_[j] = (imsi_vec[i]) | (imsi_vec[i + 1] << 4); + j++; + } + return true; +} + void usim_base::generate_nas_keys(uint8_t* k_asme, uint8_t* k_nas_enc, uint8_t* k_nas_int, diff --git a/srsue/src/ue.cc b/srsue/src/ue.cc index 4f97e4be4..a24bf08e1 100644 --- a/srsue/src/ue.cc +++ b/srsue/src/ue.cc @@ -102,6 +102,7 @@ int ue::init(const all_args_t& args_) 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; phy_args_nr.nof_carriers = args.phy.nof_nr_carriers; phy_args_nr.nof_phy_threads = args.phy.nof_phy_threads; phy_args_nr.worker_cpu_mask = args.phy.worker_cpu_mask; diff --git a/srsue/ue.conf.example b/srsue/ue.conf.example index 73620dfed..67ad68634 100644 --- a/srsue/ue.conf.example +++ b/srsue/ue.conf.example @@ -159,7 +159,7 @@ imei = 353490069873319 ##################################################################### [rrc] #ue_category = 4 -#release = 15 +#release = 8 #feature_group = 0xe6041000 #mbms_service_id = -1 #mbms_service_port = 4321 diff --git a/test/phy/CMakeLists.txt b/test/phy/CMakeLists.txt index 577e9b655..73bc9296a 100644 --- a/test/phy/CMakeLists.txt +++ b/test/phy/CMakeLists.txt @@ -36,70 +36,77 @@ if (RF_FOUND AND ENABLE_SRSUE AND ENABLE_SRSENB) ${Boost_LIBRARIES} ${ATOMIC_LIBS}) - add_nr_test(nr_phy_test_10MHz_dl_only nr_phy_test - --duration=100 - --gnb.stack.pdsch.slots=\"0,1,2,3,4,5\" - --gnb.stack.pdsch.start=0 # Start at RB 0 - --gnb.stack.pdsch.length=52 # Full 10 MHz BW - --gnb.stack.pdsch.mcs=28 # Maximum MCS - --gnb.stack.pusch.slots=none - --gnb.phy.nof_threads=${NR_PHY_TEST_GNB_NOF_THREADS} - --ue.phy.nof_threads=${NR_PHY_TEST_UE_NOF_THREADS} - ) + foreach (NR_PHY_TEST_BW "10MHz" "20MHz") + foreach (NR_PHY_TEST_TDD "6D+4U" "FR1.15-1") + set(NR_PHY_TEST_DURATION_MS 20) - add_nr_test(nr_phy_test_10MHz_ul_only nr_phy_test - --duration=100 # 100 slots - --gnb.stack.pdsch.slots=none - --gnb.stack.pusch.slots=6,7,8,9 # All possible UL slots - --gnb.stack.pusch.start=0 # Start at RB 0 - --gnb.stack.pusch.length=52 # Full 10 MHz BW - --gnb.stack.pusch.mcs=28 # Maximum MCS - --gnb.phy.nof_threads=${NR_PHY_TEST_GNB_NOF_THREADS} - --ue.phy.nof_threads=${NR_PHY_TEST_UE_NOF_THREADS} - ) + foreach (NR_PHY_TEST_PDSCH "default" "ts38101/5.2-1") + add_nr_test(nr_phy_test_${NR_PHY_TEST_BW}_${NR_PHY_TEST_TDD}_dl_${NR_PHY_TEST_PDSCH} nr_phy_test + --reference=carrier=${NR_PHY_TEST_BW},tdd=${NR_PHY_TEST_TDD},pdsch=${NR_PHY_TEST_PDSCH} + --duration=${NR_PHY_TEST_DURATION_MS} + --gnb.stack.pdsch.slots=0,1,2,3,4,5 # All possible DL slots + --gnb.stack.pdsch.start=0 # Start at RB 0 + --gnb.stack.pdsch.length=52 # Full 10 MHz BW + --gnb.stack.pdsch.mcs=27 # Maximum MCS + --gnb.stack.pusch.slots=none + --gnb.phy.nof_threads=${NR_PHY_TEST_GNB_NOF_THREADS} + --ue.phy.nof_threads=${NR_PHY_TEST_UE_NOF_THREADS} + ) + endforeach () - add_nr_test(nr_phy_test_10MHz_bidir nr_phy_test - --duration=100 # 100 slots - --gnb.stack.pdsch.slots=0,1,2,3,4,5 # All possible DL slots - --gnb.stack.pdsch.start=0 # Start at RB 0 - --gnb.stack.pdsch.length=52 # Full 10 MHz BW - --gnb.stack.pdsch.mcs=28 # Maximum MCS - --gnb.stack.pusch.slots=6,7,8,9 # All possible UL slots - --gnb.stack.pusch.start=0 # Start at RB 0 - --gnb.stack.pusch.length=52 # Full 10 MHz BW - --gnb.stack.pusch.mcs=28 # Maximum MCS - --gnb.phy.nof_threads=${NR_PHY_TEST_GNB_NOF_THREADS} - --ue.phy.nof_threads=${NR_PHY_TEST_UE_NOF_THREADS} - ) + add_nr_test(nr_phy_test_${NR_PHY_TEST_BW}_${NR_PHY_TEST_TDD}_ul_only nr_phy_test + --reference=carrier=${NR_PHY_TEST_BW},tdd=${NR_PHY_TEST_TDD} + --duration=${NR_PHY_TEST_DURATION_MS} + --gnb.stack.pdsch.slots=6 # No PDSCH + --gnb.stack.pusch.slots=6,7,8,9 # All possible UL slots + --gnb.stack.pusch.start=0 # Start at RB 0 + --gnb.stack.pusch.length=52 # Full 10 MHz BW + --gnb.stack.pusch.mcs=28 # Maximum MCS + --gnb.phy.nof_threads=${NR_PHY_TEST_GNB_NOF_THREADS} + --ue.phy.nof_threads=${NR_PHY_TEST_UE_NOF_THREADS} + ) - add_nr_test(nr_phy_test_10MHz_prach nr_phy_test - --duration=1000 # 100 slots - --gnb.stack.pdsch.slots=none # No PDSCH - --gnb.stack.pusch.slots=none # No PUSCH - --gnb.phy.nof_threads=${NR_PHY_TEST_GNB_NOF_THREADS} - --ue.stack.prach.period=30 # Transmit PRACH every 30 radio frames - --ue.stack.prach.preamble=10 # Use preamble 10 - --ue.phy.nof_threads=${NR_PHY_TEST_UE_NOF_THREADS} - ) + add_nr_test(nr_phy_test_${NR_PHY_TEST_BW}_${NR_PHY_TEST_TDD}_bidir nr_phy_test + --reference=carrier=${NR_PHY_TEST_BW},tdd=${NR_PHY_TEST_TDD} + --duration=${NR_PHY_TEST_DURATION_MS} + --gnb.stack.pdsch.slots=0,1,2,3,4,5 # All possible DL slots + --gnb.stack.pdsch.start=0 # Start at RB 0 + --gnb.stack.pdsch.length=52 # Full 10 MHz BW + --gnb.stack.pdsch.mcs=28 # Maximum MCS + --gnb.stack.pusch.slots=6,7,8,9 # All possible UL slots + --gnb.stack.pusch.start=0 # Start at RB 0 + --gnb.stack.pusch.length=52 # Full 10 MHz BW + --gnb.stack.pusch.mcs=28 # Maximum MCS + --gnb.phy.nof_threads=${NR_PHY_TEST_GNB_NOF_THREADS} + --ue.phy.nof_threads=${NR_PHY_TEST_UE_NOF_THREADS} + ) + endforeach () - add_nr_test(nr_phy_test_10MHz_sr nr_phy_test - --duration=1000 # 100 slots - --gnb.stack.pdsch.slots=none # No PDSCH - --gnb.stack.pusch.slots=none # No PUSCH - --gnb.phy.nof_threads=${NR_PHY_TEST_GNB_NOF_THREADS} - --ue.stack.sr.period=1 # Transmit SR every candidate - --ue.phy.nof_threads=${NR_PHY_TEST_UE_NOF_THREADS} - ) + add_nr_test(nr_phy_test_${NR_PHY_TEST_BW}_bidir_sched nr_phy_test + --reference=carrier=${NR_PHY_TEST_BW} + --duration=100 # 100 slots + --gnb.stack.pdsch.slots=0,1,2,3,4,5 # All possible DL slots + --gnb.stack.pdsch.start=0 # Start at RB 0 + --gnb.stack.pdsch.length=52 # Full 10 MHz BW + --gnb.stack.pdsch.mcs=28 # Maximum MCS + --gnb.stack.pusch.slots=6,7,8,9 # All possible UL slots + --gnb.stack.pusch.start=0 # Start at RB 0 + --gnb.stack.pusch.length=52 # Full 10 MHz BW + --gnb.stack.pusch.mcs=28 # Maximum MCS + --gnb.stack.use_dummy_sched=false # Use real NR scheduler + --gnb.phy.nof_threads=${NR_PHY_TEST_GNB_NOF_THREADS} + --ue.phy.nof_threads=${NR_PHY_TEST_UE_NOF_THREADS} + ) - add_nr_test(nr_phy_test_10MHz_dl_sr nr_phy_test - --duration=100 - --gnb.stack.pdsch.slots=\"0,1,2,3,4,5\" - --gnb.stack.pdsch.start=0 # Start at RB 0 - --gnb.stack.pdsch.length=2 # Full 10 MHz BW - --gnb.stack.pdsch.mcs=1 # Minimum MCS - --gnb.stack.pusch.slots=none - --gnb.phy.nof_threads=${NR_PHY_TEST_GNB_NOF_THREADS} - --ue.stack.sr.period=1 # Transmit SR every candidate - --ue.phy.nof_threads=${NR_PHY_TEST_UE_NOF_THREADS} - ) + add_nr_test(nr_phy_test_${NR_PHY_TEST_BW}_prach nr_phy_test + --reference=carrier=${NR_PHY_TEST_BW} + --duration=1000 # 100 slots + --gnb.stack.pdsch.slots=none # No PDSCH + --gnb.stack.pusch.slots=none # No PUSCH + --gnb.phy.nof_threads=${NR_PHY_TEST_GNB_NOF_THREADS} + --ue.stack.prach.period=30 # Transmit PRACH every 30 radio frames + --ue.stack.prach.preamble=10 # Use preamble 10 + --ue.phy.nof_threads=${NR_PHY_TEST_UE_NOF_THREADS} + ) + endforeach () endif () diff --git a/test/phy/dummy_gnb_stack.h b/test/phy/dummy_gnb_stack.h index 7f27c9cb0..a8fb2db07 100644 --- a/test/phy/dummy_gnb_stack.h +++ b/test/phy/dummy_gnb_stack.h @@ -37,6 +37,8 @@ class gnb_dummy_stack : public srsenb::stack_interface_phy_nr { + const static uint32_t NUMEROLOGY_IDX = 0; + public: struct prach_metrics_t { uint32_t count; @@ -80,9 +82,9 @@ private: srsran::phy_cfg_nr_t phy_cfg = {}; bool valid = false; - srsenb::sched_nr sched; - srsran::tti_point pdsch_tti, pusch_tti; - srslog::basic_logger& sched_logger; + std::unique_ptr sched; + srsran::slot_point pdsch_slot, pusch_slot; + srslog::basic_logger& sched_logger; std::mutex metrics_mutex; metrics_t metrics = {}; @@ -209,7 +211,7 @@ private: // Set TBS // Select grant and set data - pdsch.data[0] = tx_harq_proc[slot_cfg.idx].get_tb(pdsch.sch.grant.tb[0].tbs).data(); + pdsch.data[0] = tx_harq_proc[slot_cfg.idx].get_tb(pdsch.sch.grant.tb[0].tbs); // Set softbuffer pdsch.sch.grant.tb[0].softbuffer.tx = &tx_harq_proc[slot_cfg.idx].get_softbuffer(dci.ndi); @@ -271,7 +273,8 @@ private: } // Set softbuffer - pusch_cfg.grant.tb[0].softbuffer.rx = &rx_harq_proc[slot_cfg.idx].get_softbuffer(dci.ndi); + pusch_cfg.grant.tb[0].softbuffer.rx = + &rx_harq_proc[slot_cfg.idx].get_softbuffer(dci.ndi, pusch_cfg.grant.tb[0].tbs); // Push scheduling results dl_sched.pdcch_ul.push_back(pdcch); @@ -298,7 +301,7 @@ private: logger.debug("NACK received!"); } - sched.dl_ack_info(rnti, 0, ack_bit->pid, 0, is_ok); + sched->dl_ack_info(rnti, 0, ack_bit->pid, 0, is_ok); } // Process SR @@ -331,7 +334,6 @@ public: rnti(args.rnti), phy_cfg(args.phy_cfg), ss_id(args.ss_id), - sched(srsenb::sched_nr_interface::sched_cfg_t{}), use_dummy_sched(args.use_dummy_sched), sched_logger(srslog::fetch_basic_logger("MAC")) { @@ -339,14 +341,18 @@ public: sched_logger.set_level(srslog::basic_levels::debug); // create sched object + srsenb::sched_nr_interface::sched_cfg_t sched_cfg{}; + sched_cfg.pdsch_enabled = args.pdsch.slots != "" and args.pdsch.slots != "none"; + sched_cfg.pusch_enabled = args.pusch.slots != "" and args.pusch.slots != "none"; + sched.reset(new srsenb::sched_nr{sched_cfg}); std::vector cells_cfg = srsenb::get_default_cells_cfg(1, phy_cfg); - sched.cell_cfg(cells_cfg); + sched->cell_cfg(cells_cfg); // add UE to scheduler srsenb::sched_nr_interface::ue_cfg_t ue_cfg = srsenb::get_default_ue_cfg(1, phy_cfg); ue_cfg.fixed_dl_mcs = args.pdsch.mcs; ue_cfg.fixed_ul_mcs = args.pusch.mcs; - sched.ue_cfg(args.rnti, ue_cfg); + sched->ue_cfg(args.rnti, ue_cfg); dl.mcs = args.pdsch.mcs; ul.mcs = args.pusch.mcs; @@ -397,7 +403,7 @@ public: // Setup DL Data to ACK timing for (uint32_t i = 0; i < SRSRAN_NOF_SF_X_FRAME; i++) { - dl_data_to_ul_ack[i] = args.phy_cfg.harq_ack.dl_data_to_ul_ack[i % SRSRAN_MAX_NOF_DL_DATA_TO_UL]; + dl_data_to_ul_ack[i] = args.phy_cfg.harq_ack.dl_data_to_ul_ack[i % args.phy_cfg.tdd.pattern1.period_ms]; } // If reached this point the configuration is valid @@ -407,27 +413,25 @@ public: ~gnb_dummy_stack() {} bool is_valid() const { return valid; } - int rx_data_indication(rx_data_ind_t& grant) override { return 0; } - int slot_indication(const srsran_slot_cfg_t& slot_cfg) override { return 0; } int get_dl_sched(const srsran_slot_cfg_t& slot_cfg, dl_sched_t& dl_sched) override { logger.set_context(slot_cfg.idx); sched_logger.set_context(slot_cfg.idx); - if (not pdsch_tti.is_valid()) { - pdsch_tti = srsran::tti_point{slot_cfg.idx}; + if (not pdsch_slot.valid()) { + pdsch_slot = srsran::slot_point{NUMEROLOGY_IDX, slot_cfg.idx}; } else { - pdsch_tti++; + pdsch_slot++; } if (not use_dummy_sched) { - int ret = sched.get_dl_sched(pdsch_tti, 0, dl_sched); + int ret = sched->get_dl_sched(pdsch_slot, 0, dl_sched); for (pdsch_t& pdsch : dl_sched.pdsch) { // Set TBS // Select grant and set data - pdsch.data[0] = tx_harq_proc[slot_cfg.idx].get_tb(pdsch.sch.grant.tb[0].tbs).data(); + pdsch.data[0] = tx_harq_proc[slot_cfg.idx].get_tb(pdsch.sch.grant.tb[0].tbs); pdsch.data[1] = nullptr; } @@ -461,19 +465,14 @@ public: { logger.set_context(slot_cfg.idx); sched_logger.set_context(slot_cfg.idx); - if (not pusch_tti.is_valid()) { - pusch_tti = srsran::tti_point{slot_cfg.idx}; + if (not pusch_slot.valid()) { + pusch_slot = srsran::slot_point{NUMEROLOGY_IDX, slot_cfg.idx}; } else { - pusch_tti++; + pusch_slot++; } if (not use_dummy_sched) { - int ret = sched.get_ul_sched(pusch_tti, 0, ul_sched); - - for (pusch_t& pusch : ul_sched.pusch) { - pusch.data[0] = rx_harq_proc[pusch.pid].get_tb(pusch.sch.grant.tb[0].tbs).data(); - pusch.data[1] = nullptr; - } + int ret = sched->get_ul_sched(pusch_slot, 0, ul_sched); return ret; } @@ -501,10 +500,6 @@ public: // Schedule PUSCH if (has_pusch) { - // Generate data - pusch.data[0] = rx_harq_proc[pusch.pid].get_tb(pusch.sch.grant.tb[0].tbs).data(); - pusch.data[1] = nullptr; - // Put UCI configuration in PUSCH config if (not phy_cfg.get_pusch_uci_cfg(slot_cfg, uci_cfg, pusch.sch)) { logger.error("Error setting UCI configuration in PUSCH"); @@ -556,14 +551,14 @@ public: void dl_ack_info(uint16_t rnti_, uint32_t cc, uint32_t pid, uint32_t tb_idx, bool ack) { if (not use_dummy_sched) { - sched.dl_ack_info(rnti_, cc, pid, tb_idx, ack); + sched->dl_ack_info(rnti_, cc, pid, tb_idx, ack); } } void ul_crc_info(uint16_t rnti_, uint32_t cc, uint32_t pid, bool crc) { if (not use_dummy_sched) { - sched.ul_crc_info(rnti_, cc, pid, crc); + sched->ul_crc_info(rnti_, cc, pid, crc); } } @@ -575,6 +570,11 @@ public: return SRSRAN_ERROR; } + // Skip next steps if uci data is invalid + if (not pucch_info.uci_data.value.valid) { + return SRSRAN_SUCCESS; + } + // Handle PHY metrics metrics.pucch.epre_db_avg = SRSRAN_VEC_CMA(pucch_info.csi.epre_dB, metrics.pucch.epre_db_avg, metrics.pucch.count); metrics.pucch.epre_db_min = SRSRAN_MIN(metrics.pucch.epre_db_min, pucch_info.csi.epre_dB); @@ -593,7 +593,7 @@ public: return SRSRAN_SUCCESS; } - int pusch_info(const srsran_slot_cfg_t& slot_cfg, const pusch_info_t& pusch_info) override + int pusch_info(const srsran_slot_cfg_t& slot_cfg, pusch_info_t& pusch_info) override { // Handle UCI data if (not handle_uci_data(pusch_info.uci_cfg, pusch_info.pusch_data.uci)) { @@ -616,6 +616,18 @@ public: void rach_detected(const rach_info_t& rach_info) override { + if (not use_dummy_sched) { + srsenb::sched_nr_interface::dl_sched_rar_info_t ra_info; + ra_info.preamble_idx = rach_info.preamble; + ra_info.ta_cmd = rach_info.time_adv; + ra_info.ofdm_symbol_idx = 0; + ra_info.msg3_size = 7; + ra_info.freq_idx = 0; + ra_info.prach_slot = pdsch_slot - TX_ENB_DELAY; + ra_info.temp_crnti = rnti; + sched->dl_rach_info(0, ra_info); + } + std::unique_lock lock(metrics_mutex); prach_metrics_t& prach_metrics = metrics.prach[rach_info.preamble]; prach_metrics.avg_ta = SRSRAN_VEC_SAFE_CMA((float)rach_info.time_adv, prach_metrics.avg_ta, prach_metrics.count); diff --git a/test/phy/dummy_phy_common.h b/test/phy/dummy_phy_common.h index b402b3ed5..f14aff03a 100644 --- a/test/phy/dummy_phy_common.h +++ b/test/phy/dummy_phy_common.h @@ -155,7 +155,9 @@ public: uint32_t nof_channels = 1; args_t(double srate_hz_, uint32_t buffer_sz_ms_, uint32_t nof_channels_) : - srate_hz(srate_hz_), buffer_sz_ms(buffer_sz_ms_), nof_channels(nof_channels_) + srate_hz(srate_hz_), + buffer_sz_ms(buffer_sz_ms_), + nof_channels(nof_channels_) {} }; @@ -188,21 +190,20 @@ public: void push_semaphore(void* worker_ptr) { semaphore.push(worker_ptr); } - void - worker_end(void* h, bool tx_enable, srsran::rf_buffer_t& buffer, srsran::rf_timestamp_t& tx_time, bool is_nr) override + void worker_end(const worker_context_t& w_ctx, const bool& tx_enable, srsran::rf_buffer_t& buffer) override { // Synchronize worker - semaphore.wait(h); + semaphore.wait(w_ctx.worker_ptr); // Protect internal buffers and states std::unique_lock lock(ringbuffers_mutex); - uint64_t tx_ts = srsran_timestamp_uint64(&tx_time.get(0), srate_hz); + uint64_t tx_ts = srsran_timestamp_uint64(&w_ctx.tx_time.get(0), srate_hz); // Check transmit timestamp is not in the past if (tx_ts < write_ts) { logger.error("Tx time (%f) is %d samples in the past", - srsran_timestamp_real(tx_time.get_ptr(0)), + srsran_timestamp_real(&w_ctx.tx_time.get(0)), (uint32_t)(write_ts - tx_ts)); semaphore.release(); return; diff --git a/test/phy/dummy_rx_harq_proc.h b/test/phy/dummy_rx_harq_proc.h index 8c6083d17..9e855dfe6 100644 --- a/test/phy/dummy_rx_harq_proc.h +++ b/test/phy/dummy_rx_harq_proc.h @@ -50,17 +50,12 @@ public: ~dummy_rx_harq_proc() { srsran_softbuffer_rx_free(&softbuffer); } - srsran::byte_buffer_t& get_tb(uint32_t tbs_) - { - tbs = tbs_; - return data; - } - - srsran_softbuffer_rx_t& get_softbuffer(uint32_t ndi_) + srsran_softbuffer_rx_t& get_softbuffer(uint32_t ndi_, uint32_t tbs_) { if (ndi != ndi_ || first) { srsran_softbuffer_rx_reset(&softbuffer); ndi = ndi_; + tbs = tbs_; first = false; } diff --git a/test/phy/dummy_tx_harq_proc.h b/test/phy/dummy_tx_harq_proc.h index 85099dd7c..79b66e786 100644 --- a/test/phy/dummy_tx_harq_proc.h +++ b/test/phy/dummy_tx_harq_proc.h @@ -58,12 +58,12 @@ public: srsran_random_free(random_gen); } - srsran::byte_buffer_t& get_tb(uint32_t tbs_) + srsran::byte_buffer_t* get_tb(uint32_t tbs_) { std::unique_lock lock(mutex); tbs = tbs_; srsran_random_byte_vector(random_gen, data.msg, tbs / 8); - return data; + return &data; } srsran_softbuffer_tx_t& get_softbuffer(uint32_t ndi_) diff --git a/test/phy/dummy_ue_stack.h b/test/phy/dummy_ue_stack.h index 121feed98..186ecc413 100644 --- a/test/phy/dummy_ue_stack.h +++ b/test/phy/dummy_ue_stack.h @@ -86,7 +86,7 @@ public: void new_grant_dl(const uint32_t cc_idx, const mac_nr_grant_dl_t& grant, tb_action_dl_t* action) override { action->tb.enabled = true; - action->tb.softbuffer = &rx_harq_proc[grant.pid].get_softbuffer(grant.ndi); + action->tb.softbuffer = &rx_harq_proc[grant.pid].get_softbuffer(grant.ndi, grant.tbs); } void tb_decoded(const uint32_t cc_idx, const mac_nr_grant_dl_t& grant, tb_action_dl_result_t result) override {} void new_grant_ul(const uint32_t cc_idx, const mac_nr_grant_ul_t& grant, tb_action_ul_t* action) override @@ -95,7 +95,7 @@ public: return; } action->tb.enabled = true; - action->tb.payload = &tx_harq_proc[grant.pid].get_tb(grant.tbs); + action->tb.payload = tx_harq_proc[grant.pid].get_tb(grant.tbs); action->tb.softbuffer = &tx_harq_proc[grant.pid].get_softbuffer(grant.ndi); } void prach_sent(uint32_t tti, uint32_t s_id, uint32_t t_id, uint32_t f_id, uint32_t ul_carrier_id) override {} diff --git a/test/phy/nr_phy_test.cc b/test/phy/nr_phy_test.cc index bc0684205..dcdfa1fb9 100644 --- a/test/phy/nr_phy_test.cc +++ b/test/phy/nr_phy_test.cc @@ -32,6 +32,7 @@ namespace bpo = boost::program_options; test_bench::args_t::args_t(int argc, char** argv) { + std::string reference_cfg_str = ""; bpo::options_description options("Test bench options"); bpo::options_description options_gnb_stack("gNb stack and scheduling related options"); bpo::options_description options_gnb_phy("gNb PHY related options"); @@ -48,6 +49,7 @@ test_bench::args_t::args_t(int argc, char** argv) ("rnti", bpo::value(&rnti)->default_value(rnti), "UE RNTI") ("duration", bpo::value(&durations_slots)->default_value(durations_slots), "Test duration in slots") ("lib.log.level", bpo::value(&phy_lib_log_level)->default_value(phy_lib_log_level), "PHY librray log level") + ("reference", bpo::value(&reference_cfg_str)->default_value(reference_cfg_str), "Reference PHY configuration arguments") ; options_gnb_stack.add_options() @@ -109,8 +111,10 @@ test_bench::args_t::args_t(int argc, char** argv) } // Load default reference configuration - srsran::phy_cfg_nr_default_t::reference_cfg_t reference_cfg; - phy_cfg = srsran::phy_cfg_nr_default_t(reference_cfg); + phy_cfg = srsran::phy_cfg_nr_default_t(srsran::phy_cfg_nr_default_t::reference_cfg_t(reference_cfg_str)); + + // Calculate sampling rate in Hz + srate_hz = (double)(srsran_min_symbol_sz_rb(phy_cfg.carrier.nof_prb) * SRSRAN_SUBC_SPACING_NR(phy_cfg.carrier.scs)); cell_list.resize(1); cell_list[0].carrier = phy_cfg.carrier; @@ -263,9 +267,6 @@ int main(int argc, char** argv) metrics.gnb_stack.pucch.ta_us_min, metrics.gnb_stack.pucch.ta_us_max); srsran::console(" +------------+------------+------------+------------+\n"); - } else { - // In this case the gNb should not have detected any - TESTASSERT(metrics.gnb_stack.prach.empty()); } // Print SR diff --git a/test/phy/test_bench.h b/test/phy/test_bench.h index 486048796..446a4ee10 100644 --- a/test/phy/test_bench.h +++ b/test/phy/test_bench.h @@ -45,7 +45,7 @@ private: public: struct args_t { - double srate_hz = 11.52e6; + double srate_hz = (double)(768 * SRSRAN_SUBC_SPACING_NR(0)); uint32_t nof_channels = 1; uint32_t buffer_sz_ms = 10; bool valid = false; @@ -147,7 +147,14 @@ public: // Set gNb time gnb_time.add(TX_ENB_DELAY * 1e-3); - gnb_worker->set_time(slot_idx, gnb_time); + + // Set gnb context + srsran::phy_common_interface::worker_context_t gnb_context; + gnb_context.sf_idx = slot_idx; + gnb_context.worker_ptr = gnb_worker; + gnb_context.last = true; // Set last if standalone + gnb_context.tx_time.copy(gnb_time); + gnb_worker->set_context(gnb_context); // Start gNb work gnb_phy_com.push_semaphore(gnb_worker); @@ -167,8 +174,14 @@ public: // Set UE time ue_time.add(TX_ENB_DELAY * 1e-3); - ue_worker->set_tti(slot_idx); - ue_worker->set_tx_time(ue_time); + + // Set gnb context + srsran::phy_common_interface::worker_context_t ue_context; + ue_context.sf_idx = slot_idx; + ue_context.worker_ptr = ue_worker; + ue_context.last = true; // Set last if standalone + ue_context.tx_time.copy(gnb_time); + ue_worker->set_context(ue_context); // Run UE stack ue_stack.run_tti(slot_idx); diff --git a/test/run_lte.sh b/test/run_lte.sh index 47766ffa7..fa5f6e755 100755 --- a/test/run_lte.sh +++ b/test/run_lte.sh @@ -299,7 +299,7 @@ epc_args="$build_path/../srsepc/epc.conf.example \ --log.filename=./${nof_prb}prb_epc.log" enb_args="$build_path/../srsenb/enb.conf.example \ --enb_files.sib_config=$build_path/../srsenb/sib.conf.example \ - --enb_files.drb_config=$build_path/../srsenb/drb.conf.example \ + --enb_files.rb_config=$build_path/../srsenb/rb.conf.example \ --rf.device_name=zmq \ --log.all_level=info \ --enb.n_prb=$nof_prb \