From 24123313bfe6b2c80985554f09ca5cee84c43e11 Mon Sep 17 00:00:00 2001 From: David Rupprecht Date: Sun, 7 Feb 2021 16:47:09 +0100 Subject: [PATCH] Added MAC NR RA procedure with a minimal test case --- .../srslte/interfaces/mac_interface_types.h | 20 ++ .../srslte/interfaces/ue_nr_interfaces.h | 6 +- lib/src/mac/mac_rar_pdu_nr.cc | 2 +- srsue/hdr/phy/vnf_phy_nr.h | 4 +- srsue/hdr/stack/mac_nr/mac_nr.h | 27 +- srsue/hdr/stack/mac_nr/mac_nr_interfaces.h | 39 +++ srsue/hdr/stack/mac_nr/proc_ra_nr.h | 108 +++++++ srsue/src/stack/mac_nr/CMakeLists.txt | 5 +- srsue/src/stack/mac_nr/mac_nr.cc | 94 ++++-- srsue/src/stack/mac_nr/proc_ra_nr.cc | 306 ++++++++++++++++++ srsue/test/CMakeLists.txt | 4 + srsue/test/mac_nr/CMakeLists.txt | 11 + srsue/test/mac_nr/proc_ra_nr_test.cc | 130 ++++++++ 13 files changed, 715 insertions(+), 41 deletions(-) create mode 100644 srsue/hdr/stack/mac_nr/mac_nr_interfaces.h create mode 100644 srsue/hdr/stack/mac_nr/proc_ra_nr.h create mode 100644 srsue/src/stack/mac_nr/proc_ra_nr.cc create mode 100644 srsue/test/mac_nr/CMakeLists.txt create mode 100644 srsue/test/mac_nr/proc_ra_nr_test.cc diff --git a/lib/include/srslte/interfaces/mac_interface_types.h b/lib/include/srslte/interfaces/mac_interface_types.h index 3ed138c7d..bcd4ca546 100644 --- a/lib/include/srslte/interfaces/mac_interface_types.h +++ b/lib/include/srslte/interfaces/mac_interface_types.h @@ -116,6 +116,26 @@ struct rach_cfg_t { } }; +// 38.321 5.1.1 Not complete yet +struct rach_nr_cfg_t { + uint32_t prach_ConfigurationIndex; + int PreambleReceivedTargetPower; + uint32_t preambleTransMax; + uint32_t powerRampingStep; + uint32_t ra_responseWindow; + uint32_t ra_ContentionResolutionTimer; + + rach_nr_cfg_t() { reset(); } + void reset() + { + prach_ConfigurationIndex = 0; + PreambleReceivedTargetPower = 0; + powerRampingStep = 0; + preambleTransMax = 0; + ra_responseWindow = 0; + } +}; + struct mac_cfg_t { // Default constructor with default values as in 36.331 9.2.2 mac_cfg_t() { set_defaults(); } diff --git a/lib/include/srslte/interfaces/ue_nr_interfaces.h b/lib/include/srslte/interfaces/ue_nr_interfaces.h index cddd1698b..da22c45ee 100644 --- a/lib/include/srslte/interfaces/ue_nr_interfaces.h +++ b/lib/include/srslte/interfaces/ue_nr_interfaces.h @@ -95,11 +95,11 @@ public: // MAC informs PHY about UL grant included in RAR PDU virtual int set_ul_grant(std::array) = 0; - // MAC instructs PHY to send a PRACH at the next given occasion - virtual void send_prach(uint32_t prach_occasion, uint32_t preamble_index, int preamble_received_target_power) = 0; - // MAC instructs PHY to transmit MAC TB at the given TTI virtual int tx_request(const tx_request_t& request) = 0; + + /// Instruct PHY to send PRACH in the next occasion. + virtual void send_prach(const uint32_t prach_occasion, const int preamble_index, const float preamble_received_target_power, const float ta_base_sec = 0.0f) = 0; }; class phy_interface_rrc_nr diff --git a/lib/src/mac/mac_rar_pdu_nr.cc b/lib/src/mac/mac_rar_pdu_nr.cc index 558870d83..f456101e8 100644 --- a/lib/src/mac/mac_rar_pdu_nr.cc +++ b/lib/src/mac/mac_rar_pdu_nr.cc @@ -130,7 +130,7 @@ std::string mac_rar_subpdu_nr::to_string() { std::stringstream ss; if (has_rapid()) { - ss << "RAPID: " << rapid << ", Temp C-RNTI: " << temp_crnti << ", TA: " << ta << ", UL Grant: "; + ss << "RAPID: " << rapid << ", Temp C-RNTI: " << std::hex << temp_crnti << ", TA: " << ta << ", UL Grant: "; } else { ss << "Backoff Indicator: " << backoff_indicator << " "; } diff --git a/srsue/hdr/phy/vnf_phy_nr.h b/srsue/hdr/phy/vnf_phy_nr.h index d5b245d8c..6da63b849 100644 --- a/srsue/hdr/phy/vnf_phy_nr.h +++ b/srsue/hdr/phy/vnf_phy_nr.h @@ -47,9 +47,9 @@ public: void start_plot(); // MAC interface - int tx_request(const tx_request_t& request); + int tx_request(const tx_request_t& request); int set_ul_grant(std::array) { return SRSLTE_SUCCESS; }; - void send_prach(uint32_t prach_occasion, uint32_t preamble_index, int preamble_received_target_power){}; + void send_prach(const uint32_t preamble_idx, const int prach_occasion, const float target_power_dbm, const float ta_base_sec = 0.0f){}; private: std::unique_ptr vnf; diff --git a/srsue/hdr/stack/mac_nr/mac_nr.h b/srsue/hdr/stack/mac_nr/mac_nr.h index 01beae532..2ebc6ed26 100644 --- a/srsue/hdr/stack/mac_nr/mac_nr.h +++ b/srsue/hdr/stack/mac_nr/mac_nr.h @@ -13,6 +13,8 @@ #ifndef SRSUE_MAC_NR_H #define SRSUE_MAC_NR_H +#include "mac_nr_interfaces.h" +#include "proc_ra_nr.h" #include "srslte/common/block_queue.h" #include "srslte/common/mac_pcap.h" #include "srslte/interfaces/mac_interface_types.h" @@ -30,7 +32,7 @@ struct mac_nr_args_t { uint32_t drb_lcid; }; -class mac_nr final : public mac_interface_phy_nr, public mac_interface_rrc_nr +class mac_nr final : public mac_interface_phy_nr, public mac_interface_rrc_nr, public mac_interface_proc_ra_nr { public: mac_nr(srslte::ext_task_sched_handle task_sched_); @@ -65,12 +67,30 @@ public: void setup_lcid(const srslte::logical_channel_config_t& config); void set_config(const srslte::bsr_cfg_t& bsr_cfg); void set_config(const srslte::sr_cfg_t& sr_cfg); + void set_config(const srslte::rach_nr_cfg_t& rach_cfg); void set_contention_id(const uint64_t ue_identity); bool set_crnti(const uint16_t crnti); + void start_ra_procedure(); + + /// procedure ra nr interface + uint64_t get_contention_id(); + uint16_t get_c_rnti(); + void set_c_rnti(uint64_t c_rnti_); + + bool msg3_is_transmitted() { return true; } + void msg3_flush() {} + void msg3_prepare() {} + bool msg3_is_empty() { return true; } /// stack interface void process_pdus(); + static bool is_in_window(uint32_t tti, int* start, int* len); + + // PHY Interface + void prach_sent(const uint32_t tti); + void tb_decoded_ok(const uint8_t cc_idx, const uint32_t tti); + private: void write_pcap(const uint32_t cc_idx, mac_nr_grant_dl_t& grant); // If PCAPs are enabled for this MAC void handle_pdu(srslte::unique_byte_buffer_t pdu); @@ -97,7 +117,7 @@ private: bool started = false; - uint16_t crnti = 0xdead; + uint16_t c_rnti = SRSLTE_INVALID_RNTI; uint64_t contention_id = 0; static constexpr uint32_t MIN_RLC_PDU_LEN = @@ -117,6 +137,9 @@ private: srslte::unique_byte_buffer_t rlc_buffer = nullptr; srslte::task_multiqueue::queue_handle stack_task_dispatch_queue; + + // MAC Uplink-related procedures + proc_ra_nr proc_ra; }; } // namespace srsue diff --git a/srsue/hdr/stack/mac_nr/mac_nr_interfaces.h b/srsue/hdr/stack/mac_nr/mac_nr_interfaces.h new file mode 100644 index 000000000..94d073bb6 --- /dev/null +++ b/srsue/hdr/stack/mac_nr/mac_nr_interfaces.h @@ -0,0 +1,39 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#ifndef SRSUE_MAC_NR_INTERFACES_H +#define SRSUE_MAC_NR_INTERFACES_H + +#include "srslte/common/interfaces_common.h" + +namespace srsue { +/** + * @brief Interface from MAC NR parent class to subclass random access procedure + */ +class mac_interface_proc_ra_nr +{ +public: + // Functions for identity handling, e.g., contention id and c-rnti + virtual uint64_t get_contention_id() = 0; + virtual uint16_t get_c_rnti() = 0; + virtual void set_c_rnti(uint64_t c_rnti) = 0; + + // Functions for msg3 manipulation which shall be transparent to the procedure + virtual bool msg3_is_transmitted() = 0; + virtual void msg3_flush() = 0; + virtual void msg3_prepare() = 0; + virtual bool msg3_is_empty() = 0; +}; + +} // namespace srsue + +#endif // SRSUE_MAC_NR_INTERFACES_H \ No newline at end of file diff --git a/srsue/hdr/stack/mac_nr/proc_ra_nr.h b/srsue/hdr/stack/mac_nr/proc_ra_nr.h new file mode 100644 index 000000000..1afe8a1bd --- /dev/null +++ b/srsue/hdr/stack/mac_nr/proc_ra_nr.h @@ -0,0 +1,108 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#ifndef SRSUE_PROC_RA_NR_H +#define SRSUE_PROC_RA_NR_H + +#include +#include +#include + +#include "mac_nr_interfaces.h" +#include "srslte/common/common.h" +#include "srslte/common/task_scheduler.h" +#include "srslte/interfaces/ue_nr_interfaces.h" +#include "srslte/srslog/srslog.h" + +namespace srsue { + +class proc_ra_nr +{ +public: + proc_ra_nr(srslog::basic_logger& logger_); + ~proc_ra_nr(){}; + + void init(phy_interface_mac_nr* phy_h_, mac_interface_proc_ra_nr* mac_, srslte::ext_task_sched_handle* task_sched_); + void set_config(const srslte::rach_nr_cfg_t& rach_cfg); + bool is_contention_resolution(); + + bool is_rar_opportunity(uint32_t tti); + uint16_t get_rar_rnti(); + + // PHY interfaces + void prach_sent(uint32_t tti, uint32_t s_id, uint32_t t_id, uint32_t f_id, uint32_t ul_carrier_id); + void handle_rar_pdu(mac_interface_phy_nr::mac_nr_grant_dl_t& grant); + void pdcch_to_crnti(); + + void start_by_rrc(); + void reset(); + +private: + srslog::basic_logger& logger; + phy_interface_mac_nr* phy = nullptr; + mac_interface_proc_ra_nr* mac = nullptr; + srslte::ext_task_sched_handle* task_sched = nullptr; + srslte::task_multiqueue::queue_handle task_queue; + + int ra_window_length = -1, ra_window_start = -1; + uint16_t rar_rnti = SRSLTE_INVALID_RNTI; + uint16_t temp_rnti = SRSLTE_INVALID_RNTI; + + srslte::rach_nr_cfg_t rach_cfg = {}; + bool configured = false; + + enum ra_state_t { + IDLE = 0, + PDCCH_SETUP, + WAITING_FOR_PRACH_SENT, + WAITING_FOR_RESPONSE_RECEPTION, + WAITING_FOR_CONTENTION_RESOLUTION, + WAITING_FOR_COMPLETION, + MAX_RA_STATES, + }; + + std::atomic state = {IDLE}; + + enum initiators_t { MAC, RRC, initiators_t_NULLTYPE }; + std::atomic started_by = {initiators_t_NULLTYPE}; + + srslte::timer_handler::unique_timer prach_send_timer; + srslte::timer_handler::unique_timer rar_timeout_timer; + srslte::timer_handler::unique_timer contention_resolution_timer; + + // 38.321 5.1.1 Variables + uint32_t preamble_index = 0; + uint32_t preamble_transmission_counter = 0; + uint32_t preamble_power_ramping_step = 0; + int preamble_received_target_power = 0; + uint32_t scaling_factor_bi = 0; + // uint32_t temporary_c_rnti; + uint32_t power_offset_2step_ra = 0; + + // not explicty mentioned + uint32_t preambleTransMax = 0; + uint32_t prach_occasion = 0; + + uint32_t current_ta = 0; + void timer_expired(uint32_t timer_id); + // 38.321 Steps 5.1.1 - 5.1.6 + void ra_procedure_initialization(); + void ra_resource_selection(); + void ra_preamble_transmission(); + void ra_response_reception(const mac_interface_phy_nr::mac_nr_grant_dl_t& grant); + void ra_contention_resolution(); + void ra_contention_resolution(uint64_t rx_contention_id); + void ra_completion(); + void ra_error(); +}; +} // namespace srsue +#endif \ No newline at end of file diff --git a/srsue/src/stack/mac_nr/CMakeLists.txt b/srsue/src/stack/mac_nr/CMakeLists.txt index 7789c3edd..c4ced3176 100644 --- a/srsue/src/stack/mac_nr/CMakeLists.txt +++ b/srsue/src/stack/mac_nr/CMakeLists.txt @@ -6,5 +6,6 @@ # the distribution. # -set(SOURCES mac_nr.cc) -add_library(srsue_mac_nr STATIC ${SOURCES}) \ No newline at end of file +set(SOURCES mac_nr.cc proc_ra_nr.cc) +add_library(srsue_mac_nr STATIC ${SOURCES}) +target_link_libraries(srsue_mac_nr srslte_mac) \ No newline at end of file diff --git a/srsue/src/stack/mac_nr/mac_nr.cc b/srsue/src/stack/mac_nr/mac_nr.cc index 8160d50a3..4ae0a1e5a 100644 --- a/srsue/src/stack/mac_nr/mac_nr.cc +++ b/srsue/src/stack/mac_nr/mac_nr.cc @@ -12,11 +12,12 @@ #include "srsue/hdr/stack/mac_nr/mac_nr.h" #include "srslte/mac/mac_rar_pdu_nr.h" +#include "srsue/hdr/stack/mac_nr/proc_ra_nr.h" namespace srsue { mac_nr::mac_nr(srslte::ext_task_sched_handle task_sched_) : - task_sched(task_sched_), logger(srslog::fetch_basic_logger("MAC")) + task_sched(task_sched_), logger(srslog::fetch_basic_logger("MAC")), proc_ra(logger) { tx_buffer = srslte::make_byte_buffer(); rlc_buffer = srslte::make_byte_buffer(); @@ -72,7 +73,7 @@ void mac_nr::run_tti(const uint32_t tti) uint16_t mac_nr::get_ul_sched_rnti(const uint32_t tti) { - return crnti; + return c_rnti; } bool mac_nr::is_si_opportunity() @@ -118,12 +119,12 @@ uint16_t mac_nr::get_dl_sched_rnti(const uint32_t tti) bool mac_nr::has_crnti() { - return crnti != SRSLTE_INVALID_RNTI; + return c_rnti != SRSLTE_INVALID_RNTI; } uint16_t mac_nr::get_crnti() { - return crnti; + return c_rnti; } void mac_nr::bch_decoded_ok(uint32_t tti, srslte::unique_byte_buffer_t payload) @@ -184,7 +185,7 @@ void mac_nr::tb_decoded(const uint32_t cc_idx, mac_nr_grant_dl_t& grant) // handle PDU if (SRSLTE_RNTI_ISRAR(grant.rnti)) { // TODO: replace with proc_ra->get_rar_rnti() // TODO: pass to RA proc - handle_rar_pdu(grant); + // handle_rar_pdu(grant); } else { // Push DL PDUs to queue for back-ground processing for (uint32_t i = 0; i < SRSLTE_MAX_CODEWORDS; ++i) { @@ -198,27 +199,6 @@ void mac_nr::tb_decoded(const uint32_t cc_idx, mac_nr_grant_dl_t& grant) stack_task_dispatch_queue.push([this]() { process_pdus(); }); } -// Temporary helper until RA proc is complete -void mac_nr::handle_rar_pdu(mac_nr_grant_dl_t& grant) -{ - for (uint32_t i = 0; i < SRSLTE_MAX_CODEWORDS; ++i) { - if (grant.tb[i] != nullptr) { - srslte::mac_rar_pdu_nr pdu; - if (!pdu.unpack(grant.tb[i]->msg, grant.tb[i]->N_bytes)) { - logger.warning("Error unpacking RAR PDU"); - return; - } - logger.info(pdu.to_string()); - - for (auto& subpdu : pdu.get_subpdus()) { - if (subpdu.has_rapid() && subpdu.get_rapid() == 0 /* selected preamble */) { - phy->set_ul_grant(subpdu.get_ul_grant()); - } - } - } - } -} - void mac_nr::new_grant_ul(const uint32_t cc_idx, const mac_nr_grant_ul_t& grant) { phy_interface_stack_nr::tx_request_t tx_request = {}; @@ -303,23 +283,32 @@ void mac_nr::set_config(const srslte::sr_cfg_t& sr_cfg) logger.warning("Not Scheduling Request Config yet"); } +void mac_nr::set_config(const srslte::rach_nr_cfg_t& rach_cfg){ + proc_ra.set_config(rach_cfg); +} + void mac_nr::set_contention_id(uint64_t ue_identity) { contention_id = ue_identity; } -bool mac_nr::set_crnti(const uint16_t crnti_) +bool mac_nr::set_crnti(const uint16_t c_rnti_) { - if (is_valid_crnti(crnti_)) { - logger.info("Setting C-RNTI to 0x%X", crnti_); - crnti = crnti_; + if (is_valid_crnti(c_rnti_)) { + logger.info("Setting C-RNTI to 0x%X", c_rnti_); + c_rnti = c_rnti_; return true; } else { - logger.warning("Failed to set C-RNTI, 0x%X is not valid.", crnti_); + logger.warning("Failed to set C-RNTI, 0x%X is not valid.", c_rnti_); return false; } } +void mac_nr::start_ra_procedure() +{ + proc_ra.start_by_rrc(); +} + bool mac_nr::is_valid_crnti(const uint16_t crnti) { // TS 38.321 15.3.0 Table 7.1-1 @@ -361,4 +350,47 @@ void mac_nr::handle_pdu(srslte::unique_byte_buffer_t pdu) } } +uint64_t mac_nr::get_contention_id() +{ + return 0xdeadbeef; // TODO when rebased on PR +} + +uint16_t mac_nr::get_c_rnti() +{ + return c_rnti; +} + +void mac_nr::set_c_rnti(uint64_t c_rnti_) +{ + c_rnti = c_rnti_; +} + +// TODO same function as for mac_eutra +bool mac_nr::is_in_window(uint32_t tti, int* start, int* len) +{ + uint32_t st = (uint32_t)*start; + uint32_t l = (uint32_t)*len; + + if (srslte_tti_interval(tti, st) < l + 5) { + if (tti > st) { + if (tti <= st + l) { + return true; + } else { + *start = 0; + *len = 0; + return false; + } + } else { + if (tti <= (st + l) % 10240) { + return true; + } else { + *start = 0; + *len = 0; + return false; + } + } + } + return false; +} + } // namespace srsue diff --git a/srsue/src/stack/mac_nr/proc_ra_nr.cc b/srsue/src/stack/mac_nr/proc_ra_nr.cc new file mode 100644 index 000000000..43b55c50d --- /dev/null +++ b/srsue/src/stack/mac_nr/proc_ra_nr.cc @@ -0,0 +1,306 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#include "srsue/hdr/stack/mac_nr/proc_ra_nr.h" +#include "srsue/hdr/stack/mac_nr/mac_nr.h" +#include "srslte/mac/mac_rar_pdu_nr.h" + +namespace srsue { + +const char* state_str_nr[] = {"RA: IDLE: ", + "RA: PDCCH_SETUP: ", + "RA: WAITING_FOR_PRACH_SENT: ", + "RA: WAITING_FOR_RESPONSE_RECEPTION: ", + "RA: WAITING_FOR_COMPLETION: ", + "RA: MAX_RA_STATES: "}; + +// Table 7.2-1. Backoff Parameter values +uint32_t backoff_table_nr[16] = {0, 10, 20, 30, 40, 60, 80, 120, 160, 240, 320, 480, 960, 1920, 1920, 1920}; + +// Table 7.6-1: DELTA_PREAMBLE values long +int delta_preamble_db_table_nr[5] = {0, -3, -6, 0}; + +proc_ra_nr::proc_ra_nr(srslog::basic_logger& logger_) : logger(logger_) {} + +void proc_ra_nr::init(phy_interface_mac_nr* phy_, + mac_interface_proc_ra_nr* mac_, + srslte::ext_task_sched_handle* task_sched_) +{ + phy = phy_; + mac = mac_; + task_sched = task_sched_; + task_queue = task_sched->make_task_queue(); + prach_send_timer = task_sched->get_unique_timer(); + rar_timeout_timer = task_sched->get_unique_timer(); + contention_resolution_timer = task_sched->get_unique_timer(); +} + +/* Sets a new configuration. The configuration is applied by initialization() function */ +void proc_ra_nr::set_config(const srslte::rach_nr_cfg_t& rach_cfg_) +{ + if (state != IDLE) { + logger.warning("Wrong state for ra reponse reception %s (expected state %s)", + srslte::enum_to_text(state_str_nr, (uint32_t)ra_state_t::MAX_RA_STATES, state), + srslte::enum_to_text(state_str_nr, (uint32_t)ra_state_t::MAX_RA_STATES, IDLE)); + return; + } + rach_cfg = rach_cfg_; + configured = true; + logger.info("Set RACH common config (Config Index %d, preambleTransMax %d, Repsonse Window %d)", + rach_cfg.prach_ConfigurationIndex, + rach_cfg.preambleTransMax, + rach_cfg.ra_responseWindow); +} + +void proc_ra_nr::start_by_rrc() +{ + if (state != IDLE || configured == false) { + logger.warning("Trying to start PRACH by RRC order in invalid state (%s)", + srslte::enum_to_text(state_str_nr, (uint32_t)ra_state_t::MAX_RA_STATES, state)); + return; + } + started_by = initiators_t::RRC; + logger.info("Starting PRACH by RRC order"); + ra_procedure_initialization(); +} + +bool proc_ra_nr::is_rar_opportunity(uint32_t tti) +{ + // TODO replace second "&&"" by rar_timeout_timer.running if timer thread safe and delayed starting (tti+3) + if (state == WAITING_FOR_RESPONSE_RECEPTION && ra_window_start > 0 && ra_window_length > 0 && + mac_nr::is_in_window(tti, &ra_window_start, &ra_window_length)) { + logger.debug("SCHED: Searching RAR-RNTI=0x%x, tti=%d", rar_rnti, tti); + return true; + } + return false; +} + +uint16_t proc_ra_nr::get_rar_rnti() +{ + if (rar_rnti == SRSLTE_INVALID_RNTI || state != WAITING_FOR_RESPONSE_RECEPTION) { + logger.error("Requested ra rnti is invalid. Anyway we return an invalid ra rnti\n"); + return SRSLTE_INVALID_RNTI; + } + return rar_rnti; +} + +void proc_ra_nr::timer_expired(uint32_t timer_id) +{ + if (prach_send_timer.id() == timer_id) { + logger.error("PRACH Send timer expired. PRACH was not transmitted within %d ttis by phy. (TODO)", + prach_send_timer.duration()); + ra_error(); + } else if (rar_timeout_timer.id() == timer_id) { + logger.error("RAR Timer expired. RA response not received within the response window Response Error (TODO)"); + ra_error(); + } else if (contention_resolution_timer.id() == timer_id) { + logger.error("Contention Resolution Timer expired. Stopping PDCCH Search and going to Response Error (TODO)"); + ra_error(); + } else { + logger.error("Timer not implemented"); + } +} + +// 5.1.2 Random Access Resource selection +void proc_ra_nr::ra_procedure_initialization() +{ + mac->msg3_flush(); + preamble_power_ramping_step = rach_cfg.powerRampingStep; + scaling_factor_bi = 1; + preambleTransMax = rach_cfg.preambleTransMax; + ra_resource_selection(); +} + +// 5.1.2 Random Access Resource selection (TODO) +void proc_ra_nr::ra_resource_selection() +{ + ra_preamble_transmission(); +} + +// 5.1.3 Random Access Preamble transmission +void proc_ra_nr::ra_preamble_transmission() +{ + uint32_t delta_preamble = 0; // TODO calulate the delta preamble based on delta_preamble_db_table_nr + preamble_received_target_power = rach_cfg.PreambleReceivedTargetPower + delta_preamble + + (preamble_transmission_counter - 1) * rach_cfg.powerRampingStep + + power_offset_2step_ra; + preamble_index = 0; + prach_occasion = 0; + // instruct the physical layer to transmit the Random Access Preamble using the selected PRACH occasion, corresponding + // RA-RNTI (if available), PREAMBLE_INDEX, and PREAMBLE_RECEIVED_TARGET_POWER. + phy->send_prach(prach_occasion, preamble_index, preamble_received_target_power); + prach_send_timer.set(100, [this](uint32_t tid) { timer_expired(tid); }); // TODO find a suitable 100? + prach_send_timer.run(); + state = WAITING_FOR_PRACH_SENT; +} + +// 5.1.4 Random Access Preamble transmission +void proc_ra_nr::ra_response_reception(const mac_interface_phy_nr::mac_nr_grant_dl_t& grant) +{ + if (state != WAITING_FOR_RESPONSE_RECEPTION) { + logger.warning( + "Wrong state for ra reponse reception %s (expected state %s)", + srslte::enum_to_text(state_str_nr, (uint32_t)ra_state_t::MAX_RA_STATES, state), + srslte::enum_to_text(state_str_nr, (uint32_t)ra_state_t::MAX_RA_STATES, WAITING_FOR_RESPONSE_RECEPTION)); + return; + } + + // Stop rar timer + rar_timeout_timer.stop(); + for (uint32_t i = 0; i < SRSLTE_MAX_CODEWORDS; ++i) { + if (grant.tb[i] != nullptr) { + srslte::mac_rar_pdu_nr pdu; + if (!pdu.unpack(grant.tb[i]->msg, grant.tb[i]->N_bytes)) { + logger.warning("Error unpacking RAR PDU"); + return; + } + logger.info(pdu.to_string()); + + for (auto& subpdu : pdu.get_subpdus()) { + if (subpdu.has_rapid() && subpdu.get_rapid() == preamble_index) { + phy->set_ul_grant(subpdu.get_ul_grant()); + // reset all parameters that are used before rar + rar_rnti = SRSLTE_INVALID_RNTI; + mac->msg3_prepare(); + temp_rnti = subpdu.get_temp_crnti(); + current_ta = subpdu.get_ta(); + } + } + } + } + contention_resolution_timer.set(rach_cfg.ra_ContentionResolutionTimer, [this](uint32_t tid) { timer_expired(tid); }); + contention_resolution_timer.run(); + logger.debug("Waiting for Contention Resolution"); + state = WAITING_FOR_CONTENTION_RESOLUTION; +} + +// TS 38.321 Section 5.1.5 2 ways to resolve contention resolution +// if the C-RNTI MAC CE was included in Msg3: (only this one is implemented) +void proc_ra_nr::ra_contention_resolution() +{ + if (state != WAITING_FOR_CONTENTION_RESOLUTION) { + logger.warning( + "Wrong state for ra contention resolution by phy %s (expected state %s)", + srslte::enum_to_text(state_str_nr, (uint32_t)ra_state_t::MAX_RA_STATES, state), + srslte::enum_to_text(state_str_nr, (uint32_t)ra_state_t::MAX_RA_STATES, WAITING_FOR_CONTENTION_RESOLUTION)); + return; + } + if (started_by == initiators_t::RRC || started_by == initiators_t::MAC) { + logger.info("PDCCH to C-RNTI received with a new UL grant of transmission"); + contention_resolution_timer.stop(); + state = WAITING_FOR_COMPLETION; + ra_completion(); + } else { + logger.error("Not started by the correct initiator MAC or RRC"); + } +} + +// or else if the CCCH SDU was included in Msg3 and the PDCCH transmission is addressed to its TEMPORARY_C-RNTI: +void proc_ra_nr::ra_contention_resolution(uint64_t rx_contention_id) +{ + if (state != WAITING_FOR_CONTENTION_RESOLUTION) { + logger.warning( + "Wrong state for ra contention resolution by phy %s (expected state %s)", + srslte::enum_to_text(state_str_nr, (uint32_t)ra_state_t::MAX_RA_STATES, state), + srslte::enum_to_text(state_str_nr, (uint32_t)ra_state_t::MAX_RA_STATES, WAITING_FOR_CONTENTION_RESOLUTION)); + return; + } + // TODO +} + +void proc_ra_nr::ra_completion() +{ + if (state != WAITING_FOR_COMPLETION) { + logger.warning("Wrong state for ra completion by phy %s (expected state %s)", + srslte::enum_to_text(state_str_nr, (uint32_t)ra_state_t::MAX_RA_STATES, state), + srslte::enum_to_text(state_str_nr, (uint32_t)ra_state_t::MAX_RA_STATES, WAITING_FOR_COMPLETION)); + return; + } + mac->set_c_rnti(temp_rnti); + srslte::console("Random Access Complete. c-rnti=0x%x, ta=%d\n", mac->get_c_rnti(), current_ta); + logger.info("Random Access Complete. c-rnti=0x%x, ta=%d", mac->get_c_rnti(), current_ta); + temp_rnti = 0; + reset(); +} + +void proc_ra_nr::ra_error() +{ + logger.error("NR random access procedure error recovery not implemented yet\n"); +} + +// Is called by PHY once it has transmitted the prach transmitted, than configure RA-RNTI and wait for RAR reception +void proc_ra_nr::prach_sent(uint32_t tti, uint32_t s_id, uint32_t t_id, uint32_t f_id, uint32_t ul_carrier_id) +{ + task_queue.push([this, tti, s_id, t_id, f_id, ul_carrier_id]() { + if (state != WAITING_FOR_PRACH_SENT) { + logger.warning("Wrong state for prach sent notification by phy %s (expected state %s)", + srslte::enum_to_text(state_str_nr, (uint32_t)ra_state_t::MAX_RA_STATES, state), + srslte::enum_to_text(state_str_nr, (uint32_t)ra_state_t::MAX_RA_STATES, WAITING_FOR_PRACH_SENT)); + return; + } + prach_send_timer.stop(); + rar_rnti = 1 + s_id + 14 * t_id + 14 * 80 * f_id + 14 * 80 * 8 * ul_carrier_id; + logger.info( + "prach_occasion=%d, preamble_index=%d, ra-rnti=0x%x, ra-tti=%d, s_id=%d, t_id=%d, f_id=%d, ul_carrier_id=%d", + prach_occasion, + preamble_index, + rar_rnti, + tti, + s_id, + t_id, + f_id, + ul_carrier_id); + srslte::console("Random Access Transmission: prach_occasion=%d, preamble_index=%d, ra-rnti=0x%x, tti=%d\n", + prach_occasion, + preamble_index, + rar_rnti, + tti); + uint32_t rar_window_st = TTI_ADD(tti, 3); + // TODO check ra_response window (delayed start)? + rar_timeout_timer.set(rach_cfg.ra_responseWindow + 3, [this](uint32_t tid) { timer_expired(tid); }); + rar_timeout_timer.run(); + // Wait for RAR reception + ra_window_length = rach_cfg.ra_responseWindow; + 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; + }); +} + +// Called by PHY thread through MAC parent +void proc_ra_nr::handle_rar_pdu(mac_interface_phy_nr::mac_nr_grant_dl_t& grant) +{ + // Defer the handling of the grant to main stack thread in ra_response_reception + auto task_handler = [this](const mac_interface_phy_nr::mac_nr_grant_dl_t& t) { ra_response_reception(std::move(t)); }; + task_queue.push(std::bind(task_handler, std::move(grant))); +} + +// Called from PHY thread, defer actions therefore. +void proc_ra_nr::pdcch_to_crnti() +{ + task_queue.push([this]() { ra_contention_resolution(); }); +} + +bool proc_ra_nr::is_contention_resolution() +{ + return state == WAITING_FOR_CONTENTION_RESOLUTION; +} + +void proc_ra_nr::reset() +{ + state = IDLE; + started_by = initiators_t::initiators_t_NULLTYPE; + prach_send_timer.stop(); + rar_timeout_timer.stop(); + contention_resolution_timer.stop(); +} +} // namespace srsue \ No newline at end of file diff --git a/srsue/test/CMakeLists.txt b/srsue/test/CMakeLists.txt index cc124884b..c3f514a9f 100644 --- a/srsue/test/CMakeLists.txt +++ b/srsue/test/CMakeLists.txt @@ -9,6 +9,10 @@ add_subdirectory(phy) add_subdirectory(upper) +if (ENABLE_5GNR) + add_subdirectory(mac_nr) +endif (ENABLE_5GNR) + if (ENABLE_TTCN3) add_subdirectory(ttcn3) endif (ENABLE_TTCN3) diff --git a/srsue/test/mac_nr/CMakeLists.txt b/srsue/test/mac_nr/CMakeLists.txt new file mode 100644 index 000000000..cd87e0084 --- /dev/null +++ b/srsue/test/mac_nr/CMakeLists.txt @@ -0,0 +1,11 @@ +# +# 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. +# + +add_executable(proc_ra_nr_test proc_ra_nr_test.cc) +target_link_libraries(proc_ra_nr_test srsue_mac_nr srslte_common) +add_test(proc_ra_nr_test proc_ra_nr_test) \ No newline at end of file diff --git a/srsue/test/mac_nr/proc_ra_nr_test.cc b/srsue/test/mac_nr/proc_ra_nr_test.cc new file mode 100644 index 000000000..50501da88 --- /dev/null +++ b/srsue/test/mac_nr/proc_ra_nr_test.cc @@ -0,0 +1,130 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2020 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 "srslte/common/common.h" +#include "srslte/common/log_filter.h" +#include "srslte/common/test_common.h" +#include "srsue/hdr/stack/mac_nr/proc_ra_nr.h" + +using namespace srsue; + +class dummy_phy : public phy_interface_mac_nr +{ +public: + dummy_phy() {} + void send_prach(const uint32_t prach_occasion_, const int preamble_index_, const float preamble_received_target_power_, const float ta_base_sec_ = 0.0f) + { + prach_occasion = prach_occasion_; + preamble_index = preamble_index_; + preamble_received_target_power = preamble_received_target_power_; + } + int tx_request(const tx_request_t& request) {return 0;} + int set_ul_grant(std::array) { return 0; } + + void get_last_send_prach(uint32_t* prach_occasion_, uint32_t* preamble_index_, int* preamble_received_target_power_) + { + *prach_occasion_ = prach_occasion; + *preamble_index_ = preamble_index; + *preamble_received_target_power_ = preamble_received_target_power; + } + +private: + uint32_t prach_occasion; + uint32_t preamble_index; + int preamble_received_target_power; +}; + +class dummy_mac : public mac_interface_proc_ra_nr +{ +public: + uint64_t get_contention_id() { return 0xdeadbeaf; } + uint16_t get_c_rnti() { return crnti; } + void set_c_rnti(uint64_t c_rnti) { crnti = c_rnti; } + + bool msg3_is_transmitted() { return true; } + void msg3_flush() {} + void msg3_prepare() {} + bool msg3_is_empty() { return true; } + + void msga_flush(){}; + +private: + uint16_t crnti; +}; + +int main() +{ + srslog::init(); + auto& mac_logger = srslog::fetch_basic_logger("MAC"); + mac_logger.set_level(srslog::basic_levels::debug); + mac_logger.set_hex_dump_max_size(-1); + + dummy_phy dummy_phy; + dummy_mac dummy_mac; + srslte::task_scheduler task_sched{5, 2}; + srslte::ext_task_sched_handle ext_task_sched_h(&task_sched); + + proc_ra_nr proc_ra_nr(mac_logger); + + proc_ra_nr.init(&dummy_phy, &dummy_mac, &ext_task_sched_h); + TESTASSERT(proc_ra_nr.is_rar_opportunity(1) == false); + srslte::rach_nr_cfg_t rach_cfg; + rach_cfg.powerRampingStep = 4; + rach_cfg.prach_ConfigurationIndex = 16; + rach_cfg.PreambleReceivedTargetPower = -110; + rach_cfg.preambleTransMax = 7; + rach_cfg.ra_ContentionResolutionTimer = 64; + rach_cfg.ra_responseWindow = 10; + proc_ra_nr.set_config(rach_cfg); + proc_ra_nr.start_by_rrc(); + + // Test send prach parameters + uint32_t prach_occasion = 0; + uint32_t preamble_index = 0; + int preamble_received_target_power = 0; + dummy_phy.get_last_send_prach(&prach_occasion, &preamble_index, &preamble_received_target_power); + TESTASSERT(prach_occasion == 0); + TESTASSERT(preamble_index == 0); + TESTASSERT(preamble_received_target_power == -114); + // Simulate PHY and call prach_sent (random values) + uint32_t tti_start = 0; + proc_ra_nr.prach_sent(tti_start, 6, 0, 4, 1); + + for (uint32_t i = tti_start; i < rach_cfg.ra_responseWindow; i++) { + // update clock and run internal tasks + task_sched.tic(); + task_sched.run_pending_tasks(); + bool rar_opportunity = proc_ra_nr.is_rar_opportunity(i); + if (i < 3 + tti_start) { + TESTASSERT(rar_opportunity == false); + } else if (3 + tti_start > i && i < 3 + rach_cfg.ra_responseWindow) { + TESTASSERT(rar_opportunity == true); + TESTASSERT(proc_ra_nr.get_rar_rnti() == 0x3487); + } + } + mac_interface_phy_nr::mac_nr_grant_dl_t grant; + grant.rnti = 0x3487; + grant.tti = rach_cfg.ra_responseWindow + tti_start + 3; + grant.pid = 0x0123; + uint8_t mac_dl_rar_pdu[] = {0x40, 0x05, 0xa0, 0x00, 0x11, 0x46, 0x46, 0x16, 0x00, 0x00, 0x00}; + grant.tb[0] = srslte::make_byte_buffer(); + grant.tb[0].get()->append_bytes(mac_dl_rar_pdu, sizeof(mac_dl_rar_pdu)); + proc_ra_nr.handle_rar_pdu(grant); + + task_sched.tic(); + task_sched.run_pending_tasks(); + + proc_ra_nr.pdcch_to_crnti(); + + task_sched.tic(); + task_sched.run_pending_tasks(); + return SRSLTE_SUCCESS; +}