From ebab12403f86760898c463a6fc1d91f9cbd35518 Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Mon, 25 Oct 2021 10:51:02 +0200 Subject: [PATCH] Asynchronous NR PHY configuration (#3527) * Fix a race condition when accessing the NR PHY cfg by the RRC and phy workers. Rework how the phy cfg is handled, now workers have their own copy that gets updated after a reconfig moving it out of the state class. * Default initialize sf_len member in sf_worker for consistency. * Asynchronous NR PHY configuration * Fix compilation * Corrected method override and fix unitialised value * Added carrier equal comparison to avoid aligment byte padding comparison Co-authored-by: faluco --- lib/include/srsran/common/phy_cfg_nr.h | 5 + .../srsran/interfaces/ue_nr_interfaces.h | 7 +- lib/include/srsran/phy/common/phy_common_nr.h | 8 ++ lib/src/phy/common/phy_common_nr.c | 19 +++ srsue/hdr/phy/nr/cc_worker.h | 5 +- srsue/hdr/phy/nr/sf_worker.h | 18 ++- srsue/hdr/phy/nr/state.h | 28 ++-- srsue/hdr/phy/nr/worker_pool.h | 3 + srsue/hdr/phy/phy.h | 8 +- srsue/hdr/stack/rrc/rrc_nr.h | 9 ++ srsue/hdr/stack/ue_stack_lte.h | 1 + srsue/hdr/stack/ue_stack_nr.h | 17 +-- srsue/src/phy/nr/cc_worker.cc | 77 +++++------ srsue/src/phy/nr/sf_worker.cc | 20 +-- srsue/src/phy/nr/worker_pool.cc | 120 +++++++++++++----- srsue/src/phy/phy.cc | 38 ++++-- srsue/src/stack/rrc/rrc_nr.cc | 23 +++- srsue/src/stack/ue_stack_lte.cc | 4 + srsue/src/stack/ue_stack_nr.cc | 5 + test/phy/dummy_ue_stack.h | 5 + 20 files changed, 305 insertions(+), 115 deletions(-) diff --git a/lib/include/srsran/common/phy_cfg_nr.h b/lib/include/srsran/common/phy_cfg_nr.h index 327d28443..d555b2e69 100644 --- a/lib/include/srsran/common/phy_cfg_nr.h +++ b/lib/include/srsran/common/phy_cfg_nr.h @@ -49,6 +49,11 @@ struct phy_cfg_nr_t { phy_cfg_nr_t() {} + bool carrier_is_equal(const srsran::phy_cfg_nr_t& other) const + { + return srsran_carrier_nr_equal(&carrier, &other.carrier); + } + /** * @brief Computes the DCI configuration for the current UE configuration */ diff --git a/lib/include/srsran/interfaces/ue_nr_interfaces.h b/lib/include/srsran/interfaces/ue_nr_interfaces.h index f43a6f354..22fae72ca 100644 --- a/lib/include/srsran/interfaces/ue_nr_interfaces.h +++ b/lib/include/srsran/interfaces/ue_nr_interfaces.h @@ -25,9 +25,10 @@ namespace srsue { class rrc_interface_phy_nr { public: - virtual void in_sync() = 0; - virtual void out_of_sync() = 0; - virtual void run_tti(const uint32_t tti) = 0; + virtual void in_sync() = 0; + virtual void out_of_sync() = 0; + virtual void run_tti(const uint32_t tti) = 0; + virtual void set_phy_config_complete(bool status) = 0; }; class mac_interface_phy_nr diff --git a/lib/include/srsran/phy/common/phy_common_nr.h b/lib/include/srsran/phy/common/phy_common_nr.h index 059674a89..c8612c317 100644 --- a/lib/include/srsran/phy/common/phy_common_nr.h +++ b/lib/include/srsran/phy/common/phy_common_nr.h @@ -718,6 +718,14 @@ SRSRAN_API const char* srsran_ssb_pattern_to_str(srsran_ssb_patern_t pattern); */ SRSRAN_API srsran_ssb_patern_t srsran_ssb_pattern_fom_str(const char* str); +/** + * @brief Compares if two NR carrier structures are equal + * @param a First carrier to compare + * @param b Second carrier to compare + * @return True if all the carrier structure fields are equal, otherwise false + */ +SRSRAN_API bool srsran_carrier_nr_equal(const srsran_carrier_nr_t* a, const srsran_carrier_nr_t* b); + #ifdef __cplusplus } #endif diff --git a/lib/src/phy/common/phy_common_nr.c b/lib/src/phy/common/phy_common_nr.c index af1703f06..b3b2e3360 100644 --- a/lib/src/phy/common/phy_common_nr.c +++ b/lib/src/phy/common/phy_common_nr.c @@ -730,3 +730,22 @@ srsran_ssb_patern_t srsran_ssb_pattern_fom_str(const char* str) return SRSRAN_SSB_PATTERN_INVALID; } + +bool srsran_carrier_nr_equal(const srsran_carrier_nr_t* a, const srsran_carrier_nr_t* b) +{ + if (a == NULL || b == NULL) { + return false; + } + + bool ret = (a->pci == b->pci); + ret = ret && (a->dl_center_frequency_hz == b->dl_center_frequency_hz); + ret = ret && (a->ul_center_frequency_hz == b->ul_center_frequency_hz); + ret = ret && (a->ssb_center_freq_hz == b->ssb_center_freq_hz); + ret = ret && (a->offset_to_carrier == b->offset_to_carrier); + ret = ret && (a->scs == b->scs); + ret = ret && (a->nof_prb == b->nof_prb); + ret = ret && (a->start == b->start); + ret = ret && (a->max_mimo_layers == b->max_mimo_layers); + + return ret; +} \ No newline at end of file diff --git a/srsue/hdr/phy/nr/cc_worker.h b/srsue/hdr/phy/nr/cc_worker.h index 90e1f5d7f..12d92e394 100644 --- a/srsue/hdr/phy/nr/cc_worker.h +++ b/srsue/hdr/phy/nr/cc_worker.h @@ -22,10 +22,10 @@ namespace nr { class cc_worker { public: - cc_worker(uint32_t cc_idx, srslog::basic_logger& log, state& phy_state_); + cc_worker(uint32_t cc_idx, srslog::basic_logger& log, state& phy_state_, const srsran::phy_cfg_nr_t& cfg); ~cc_worker(); - bool update_cfg(); + void update_cfg(const srsran::phy_cfg_nr_t& new_config); void set_tti(uint32_t tti); cf_t* get_rx_buffer(uint32_t antenna_idx); @@ -50,6 +50,7 @@ private: std::array tx_buffer = {}; uint32_t buffer_sz = 0; state& phy; + srsran::phy_cfg_nr_t cfg; srsran_ssb_t ssb = {}; srsran_ue_dl_nr_t ue_dl = {}; srsran_ue_ul_nr_t ue_ul = {}; diff --git a/srsue/hdr/phy/nr/sf_worker.h b/srsue/hdr/phy/nr/sf_worker.h index 7b69c079e..b26e4fede 100644 --- a/srsue/hdr/phy/nr/sf_worker.h +++ b/srsue/hdr/phy/nr/sf_worker.h @@ -31,17 +31,25 @@ namespace nr { class sf_worker final : public srsran::thread_pool::worker { public: - sf_worker(srsran::phy_common_interface& common_, state& phy_state_, srslog::basic_logger& logger); + sf_worker(srsran::phy_common_interface& common_, + state& phy_state_, + const srsran::phy_cfg_nr_t& cfg, + srslog::basic_logger& logger); ~sf_worker() = default; - bool update_cfg(uint32_t cc_idx); - /* 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_context(const srsran::phy_common_interface::worker_context_t& w_ctx); int read_pdsch_d(cf_t* pdsch_d); void start_plot(); + void set_cfg(const srsran::phy_cfg_nr_t& new_cfg) + { + for (unsigned i = 0, e = cc_workers.size(); i != e; ++i) { + update_cfg(i, new_cfg); + } + sf_len = SRSRAN_SF_LEN_PRB_NR(new_cfg.carrier.nof_prb); + } void set_prach(cf_t* prach_ptr, float prach_power); @@ -49,6 +57,9 @@ private: /* Inherited from thread_pool::worker. Function called every subframe to run the DL/UL processing */ void work_imp() override; + void update_cfg(uint32_t cc_idx, const srsran::phy_cfg_nr_t& new_cfg); + +private: std::vector > cc_workers; srsran::phy_common_interface& common; @@ -59,6 +70,7 @@ private: cf_t* prach_ptr = nullptr; float prach_power = 0; srsran::phy_common_interface::worker_context_t context = {}; + uint32_t sf_len = 0; }; } // namespace nr diff --git a/srsue/hdr/phy/nr/state.h b/srsue/hdr/phy/nr/state.h index 238d5e646..83d4d7505 100644 --- a/srsue/hdr/phy/nr/state.h +++ b/srsue/hdr/phy/nr/state.h @@ -84,9 +84,6 @@ public: /// Physical layer user configuration phy_args_nr_t args = {}; - /// Physical layer higher layer configuration, provided by higher layers through configuration messages - srsran::phy_cfg_nr_t cfg = {}; - /// Semaphore for aligning UL work srsran::tti_semaphore dl_ul_semaphore; @@ -101,10 +98,13 @@ public: /** * @brief Stores a received UL DCI into the pending UL grant list + * @param cfg Physical layer configuration object * @param slot_rx The TTI in which the grant was received * @param dci_ul The UL DCI message to store */ - void set_ul_pending_grant(const srsran_slot_cfg_t& slot_rx, const srsran_dci_ul_nr_t& dci_ul) + void set_ul_pending_grant(const srsran::phy_cfg_nr_t& cfg, + const srsran_slot_cfg_t& slot_rx, + const srsran_dci_ul_nr_t& dci_ul) { // Convert UL DCI to grant srsran_sch_cfg_nr_t pusch_cfg = {}; @@ -160,10 +160,12 @@ public: /** * @brief Stores a received DL DCI into the pending DL grant list + * @param cfg Physical layer configuration object * @param tti_rx The TTI in which the grant was received * @param dci_dl The DL DCI message to store */ - void set_dl_pending_grant(const srsran_slot_cfg_t& slot, const srsran_dci_dl_nr_t& dci_dl) + void + set_dl_pending_grant(const srsran::phy_cfg_nr_t& cfg, const srsran_slot_cfg_t& slot, const srsran_dci_dl_nr_t& dci_dl) { // Convert DL DCI to grant srsran_sch_cfg_nr_t pdsch_cfg = {}; @@ -289,7 +291,7 @@ public: reset_measurements(); } - bool has_valid_sr_resource(uint32_t sr_id) + bool has_valid_sr_resource(const srsran::phy_cfg_nr_t& cfg, uint32_t sr_id) { for (const srsran_pucch_nr_sr_resource_t& r : cfg.pucch.sr_resources) { if (r.configured && r.sr_id == sr_id) { @@ -310,7 +312,7 @@ public: pending_ack = {}; } - void get_pending_sr(const uint32_t& tti, srsran_uci_data_nr_t& uci_data) + void get_pending_sr(const srsran::phy_cfg_nr_t& cfg, const uint32_t& tti, srsran_uci_data_nr_t& uci_data) { // Calculate all SR opportunities in the given TTI uint32_t sr_resource_id[SRSRAN_PUCCH_MAX_NOF_SR_RESOURCES] = {}; @@ -344,7 +346,8 @@ public: uci_data.value.sr = sr_count_positive; } - void get_periodic_csi(const srsran_slot_cfg_t& slot_cfg, srsran_uci_data_nr_t& uci_data) + void + get_periodic_csi(const srsran::phy_cfg_nr_t& cfg, const srsran_slot_cfg_t& slot_cfg, srsran_uci_data_nr_t& uci_data) { // Generate report configurations int n = srsran_csi_reports_generate(&cfg.csi, &slot_cfg, uci_data.cfg.csi); @@ -451,10 +454,12 @@ public: /** * @brief Processes a new NZP-CSI-RS channel measurement + * @param cfg Physical layer configuration object * @param new_measure New measurement * @param resource_set_id NZP-CSI-RS resource set identifier used for the channel measurement */ - void new_nzp_csi_rs_channel_measurement(const srsran_csi_channel_measurements_t& new_measure, + void new_nzp_csi_rs_channel_measurement(const srsran::phy_cfg_nr_t& cfg, + const srsran_csi_channel_measurements_t& new_measure, uint32_t resource_set_id) { std::lock_guard lock(csi_measurements_mutex); @@ -469,12 +474,14 @@ public: /** * @brief Processes a new Tracking Reference Signal (TRS) measurement * @param new_measure New measurement + * @param cfg Physical layer configuration object * @param resource_set_id NZP-CSI-RS resource set identifier used for the channel measurement if it is configured from * a NZP-CSI-RS * @param K_csi_rs Number of NZP-CSI-RS resources used for the measurement, set to 0 if another type of signal is * measured (i.e. SSB) */ void new_csi_trs_measurement(const srsran_csi_trs_measurements_t& new_meas, + const srsran::phy_cfg_nr_t& cfg, uint32_t resource_set_id = 0, uint32_t K_csi_rs = 0) { @@ -500,7 +507,7 @@ public: measurements.wideband_snr_db = new_meas.snr_dB; measurements.nof_ports = 1; // Other values are not supported measurements.K_csi_rs = K_csi_rs; - new_nzp_csi_rs_channel_measurement(measurements, resource_set_id); + new_nzp_csi_rs_channel_measurement(cfg, measurements, resource_set_id); // Update tracking information trs_measurements_mutex.lock(); @@ -529,6 +536,7 @@ public: void set_ul_ext_cfo(float ext_cfo_hz) { ul_ext_cfo_hz = ext_cfo_hz; } }; + } // namespace nr } // namespace srsue diff --git a/srsue/hdr/phy/nr/worker_pool.h b/srsue/hdr/phy/nr/worker_pool.h index 18d2cb98d..5495cb815 100644 --- a/srsue/hdr/phy/nr/worker_pool.h +++ b/srsue/hdr/phy/nr/worker_pool.h @@ -33,6 +33,9 @@ private: cf_t* prach_ptr = nullptr; float prach_target_power = 0.0f; uint32_t sf_sz = 0; + srsran::phy_cfg_nr_t cfg{}; + std::vector pending_cfgs; + std::mutex cfg_mutex; public: sf_worker* operator[](std::size_t pos) { return workers.at(pos).get(); } diff --git a/srsue/hdr/phy/phy.h b/srsue/hdr/phy/phy.h index 2a4ec37c6..1e4fac9fb 100644 --- a/srsue/hdr/phy/phy.h +++ b/srsue/hdr/phy/phy.h @@ -194,7 +194,8 @@ private: srslog::basic_logger& logger_phy; srslog::basic_logger& logger_phy_lib; - srsue::stack_interface_phy_lte* stack = nullptr; + srsue::stack_interface_phy_lte* stack = nullptr; + srsue::stack_interface_phy_nr* stack_nr = nullptr; lte::worker_pool lte_workers; nr::worker_pool nr_workers; @@ -205,8 +206,9 @@ private: srsran_prach_cfg_t prach_cfg = {}; srsran_tdd_config_t tdd_config = {}; - srsran::phy_cfg_t config = {}; - phy_args_t args = {}; + srsran::phy_cfg_t config = {}; + srsran::phy_cfg_nr_t config_nr = {}; + phy_args_t args = {}; // Since cell_search/cell_select operations take a lot of time, we use another queue to process the other commands // in parallel and avoid accumulating in the queue diff --git a/srsue/hdr/stack/rrc/rrc_nr.h b/srsue/hdr/stack/rrc/rrc_nr.h index 177cf8fc0..823a479e3 100644 --- a/srsue/hdr/stack/rrc/rrc_nr.h +++ b/srsue/hdr/stack/rrc/rrc_nr.h @@ -119,6 +119,8 @@ public: // STACK interface void cell_search_completed(const rrc_interface_phy_lte::cell_search_ret_t& cs_ret, const phy_cell_t& found_cell); + void set_phy_config_complete(bool status) final; + private: srsran::task_sched_handle task_sched; struct cmd_msg_t { @@ -158,6 +160,13 @@ private: // rrc_nr_state_t state = RRC_NR_STATE_IDLE; + // Stores the state of the PHy configuration setting + enum { + PHY_CFG_STATE_NONE = 0, + PHY_CFG_STATE_APPLY_SP_CELL, + PHY_CFG_STATE_RA_COMPLETED, + } phy_cfg_state; + rrc_nr_args_t args = {}; const char* get_rb_name(uint32_t lcid) final; diff --git a/srsue/hdr/stack/ue_stack_lte.h b/srsue/hdr/stack/ue_stack_lte.h index 89b7742e8..a6b6b4dd5 100644 --- a/srsue/hdr/stack/ue_stack_lte.h +++ b/srsue/hdr/stack/ue_stack_lte.h @@ -83,6 +83,7 @@ public: void cell_select_complete(bool status) final; void set_config_complete(bool status) final; void set_scell_complete(bool status) final; + void set_phy_config_complete(bool status) final; // MAC Interface for EUTRA PHY uint16_t get_dl_sched_rnti(uint32_t tti) final { return mac.get_dl_sched_rnti(tti); } diff --git a/srsue/hdr/stack/ue_stack_nr.h b/srsue/hdr/stack/ue_stack_nr.h index 1174c9dad..bd018935b 100644 --- a/srsue/hdr/stack/ue_stack_nr.h +++ b/srsue/hdr/stack/ue_stack_nr.h @@ -60,24 +60,25 @@ public: int init(const stack_args_t& args_, phy_interface_stack_nr* phy_, gw_interface_stack* gw_); bool switch_on() final; bool switch_off() final; - void stop(); + void stop() final; // GW srsue stack_interface_gw dummy interface - bool is_registered() { return true; }; - bool start_service_request() { return true; }; + bool is_registered() final { return true; }; + bool start_service_request() final { return true; }; - bool get_metrics(stack_metrics_t* metrics); + bool get_metrics(stack_metrics_t* metrics) final; bool is_rrc_connected(); // RRC interface for PHY void in_sync() final; void out_of_sync() final; void run_tti(const uint32_t tti) final; + void set_phy_config_complete(bool status) override; // MAC interface for PHY sched_rnti_t get_dl_sched_rnti_nr(const uint32_t tti) final { return mac->get_dl_sched_rnti_nr(tti); } sched_rnti_t get_ul_sched_rnti_nr(const uint32_t tti) final { return mac->get_ul_sched_rnti_nr(tti); } - int sf_indication(const uint32_t tti) + int sf_indication(const uint32_t tti) final { run_tti(tti); return SRSRAN_SUCCESS; @@ -94,11 +95,11 @@ public: { mac->new_grant_ul(cc_idx, grant, action); } - void prach_sent(uint32_t tti, uint32_t s_id, uint32_t t_id, uint32_t f_id, uint32_t ul_carrier_id) + void prach_sent(uint32_t tti, uint32_t s_id, uint32_t t_id, uint32_t f_id, uint32_t ul_carrier_id) final { mac->prach_sent(tti, s_id, t_id, f_id, ul_carrier_id); } - bool sr_opportunity(uint32_t tti, uint32_t sr_id, bool meas_gap, bool ul_sch_tx) + bool sr_opportunity(uint32_t tti, uint32_t sr_id, bool meas_gap, bool ul_sch_tx) final { return mac->sr_opportunity(tti, sr_id, meas_gap, ul_sch_tx); } @@ -108,7 +109,7 @@ public: bool has_active_radio_bearer(uint32_t eps_bearer_id) final { return true; /* TODO: add EPS to LCID mapping */ } // Interface for RRC - srsran::tti_point get_current_tti() { return srsran::tti_point{0}; } + srsran::tti_point get_current_tti() final { return srsran::tti_point{0}; } void add_eps_bearer(uint8_t eps_bearer_id, srsran::srsran_rat_t rat, uint32_t lcid) final {} void remove_eps_bearer(uint8_t eps_bearer_id) final {} void reset_eps_bearers() final {} diff --git a/srsue/src/phy/nr/cc_worker.cc b/srsue/src/phy/nr/cc_worker.cc index c169d21ba..8fbcaeb9a 100644 --- a/srsue/src/phy/nr/cc_worker.cc +++ b/srsue/src/phy/nr/cc_worker.cc @@ -18,8 +18,9 @@ namespace srsue { namespace nr { -cc_worker::cc_worker(uint32_t cc_idx_, srslog::basic_logger& log, state& phy_state_) : - cc_idx(cc_idx_), phy(phy_state_), logger(log) + +cc_worker::cc_worker(uint32_t cc_idx_, srslog::basic_logger& log, state& phy_state_, const srsran::phy_cfg_nr_t& cfg) : + cc_idx(cc_idx_), phy(phy_state_), cfg(cfg), logger(log) { cf_t* rx_buffer_c[SRSRAN_MAX_PORTS] = {}; @@ -67,34 +68,35 @@ cc_worker::~cc_worker() } } -bool cc_worker::update_cfg() +void cc_worker::update_cfg(const srsran::phy_cfg_nr_t& new_config) { - if (srsran_ue_dl_nr_set_carrier(&ue_dl, &phy.cfg.carrier) < SRSRAN_SUCCESS) { + cfg = new_config; + configured = false; + + if (srsran_ue_dl_nr_set_carrier(&ue_dl, &cfg.carrier) < SRSRAN_SUCCESS) { ERROR("Error setting carrier"); - return false; + return; } - if (srsran_ue_ul_nr_set_carrier(&ue_ul, &phy.cfg.carrier) < SRSRAN_SUCCESS) { + if (srsran_ue_ul_nr_set_carrier(&ue_ul, &cfg.carrier) < SRSRAN_SUCCESS) { ERROR("Error setting carrier"); - return false; + return; } - srsran_dci_cfg_nr_t dci_cfg = phy.cfg.get_dci_cfg(); - if (srsran_ue_dl_nr_set_pdcch_config(&ue_dl, &phy.cfg.pdcch, &dci_cfg) < SRSRAN_SUCCESS) { + srsran_dci_cfg_nr_t dci_cfg = cfg.get_dci_cfg(); + if (srsran_ue_dl_nr_set_pdcch_config(&ue_dl, &cfg.pdcch, &dci_cfg) < SRSRAN_SUCCESS) { logger.error("Error setting NR PDCCH configuration"); - return false; + return; } - srsran_ssb_cfg_t ssb_cfg = phy.cfg.get_ssb_cfg(); - ssb_cfg.srate_hz = srsran_min_symbol_sz_rb(phy.cfg.carrier.nof_prb) * SRSRAN_SUBC_SPACING_NR(phy.cfg.carrier.scs); + srsran_ssb_cfg_t ssb_cfg = cfg.get_ssb_cfg(); + ssb_cfg.srate_hz = srsran_min_symbol_sz_rb(cfg.carrier.nof_prb) * SRSRAN_SUBC_SPACING_NR(cfg.carrier.scs); if (srsran_ssb_set_cfg(&ssb, &ssb_cfg) < SRSRAN_SUCCESS) { logger.error("Error setting SSB configuration"); - return false; + return; } configured = true; - - return true; } void cc_worker::set_tti(uint32_t tti) @@ -155,7 +157,7 @@ void cc_worker::decode_pdcch_dl() } // Enqueue UL grants - phy.set_dl_pending_grant(dl_slot_cfg, dci_rx[i]); + phy.set_dl_pending_grant(cfg, dl_slot_cfg, dci_rx[i]); } if (logger.debug.enabled()) { @@ -202,7 +204,7 @@ void cc_worker::decode_pdcch_ul() } // Enqueue UL grants - phy.set_ul_pending_grant(dl_slot_cfg, dci_rx[i]); + phy.set_ul_pending_grant(cfg, dl_slot_cfg, dci_rx[i]); } } @@ -344,7 +346,7 @@ bool cc_worker::measure_csi() srsran_csi_trs_measurements_t meas = {}; // Iterate all possible candidates - const std::array position_in_burst = phy.cfg.ssb.position_in_burst; + const std::array& position_in_burst = cfg.ssb.position_in_burst; for (uint32_t ssb_idx = 0; ssb_idx < SRSRAN_SSB_NOF_CANDIDATES; ssb_idx++) { // Skip SSB candidate if not enabled if (not position_in_burst[ssb_idx]) { @@ -352,7 +354,7 @@ bool cc_worker::measure_csi() } // Measure SSB candidate - if (srsran_ssb_csi_measure(&ssb, phy.cfg.carrier.pci, ssb_idx, rx_buffer[0], &meas) < SRSRAN_SUCCESS) { + if (srsran_ssb_csi_measure(&ssb, cfg.carrier.pci, ssb_idx, rx_buffer[0], &meas) < SRSRAN_SUCCESS) { logger.error("Error measuring SSB"); return false; } @@ -363,10 +365,10 @@ bool cc_worker::measure_csi() logger.debug("SSB-CSI: %s", str.data()); } - bool hrf = (dl_slot_cfg.idx % SRSRAN_NSLOTS_PER_FRAME_NR(phy.cfg.carrier.scs) > - SRSRAN_NSLOTS_PER_FRAME_NR(phy.cfg.carrier.scs) / 2); + bool hrf = (dl_slot_cfg.idx % SRSRAN_NSLOTS_PER_FRAME_NR(cfg.carrier.scs) > + SRSRAN_NSLOTS_PER_FRAME_NR(cfg.carrier.scs) / 2); srsran_pbch_msg_nr_t pbch_msg = {}; - if (srsran_ssb_decode_pbch(&ssb, phy.cfg.carrier.pci, hrf, ssb_idx, rx_buffer[0], &pbch_msg) < SRSRAN_SUCCESS) { + if (srsran_ssb_decode_pbch(&ssb, cfg.carrier.pci, hrf, ssb_idx, rx_buffer[0], &pbch_msg) < SRSRAN_SUCCESS) { logger.error("Error decoding PBCH"); return false; } @@ -381,10 +383,10 @@ bool cc_worker::measure_csi() } // Check if the SFN matches - if (mib.sfn != dl_slot_cfg.idx / SRSRAN_NSLOTS_PER_FRAME_NR(phy.cfg.carrier.scs)) { + if (mib.sfn != dl_slot_cfg.idx / SRSRAN_NSLOTS_PER_FRAME_NR(cfg.carrier.scs)) { logger.error("PBCH-MIB: NR SFN (%d) does not match current SFN (%d)", mib.sfn, - dl_slot_cfg.idx / SRSRAN_NSLOTS_PER_FRAME_NR(phy.cfg.carrier.scs)); + dl_slot_cfg.idx / SRSRAN_NSLOTS_PER_FRAME_NR(cfg.carrier.scs)); } // Log MIB information @@ -399,7 +401,7 @@ bool cc_worker::measure_csi() } // Report SSB candidate channel measurement to the PHY state - phy.new_csi_trs_measurement(meas); + phy.new_csi_trs_measurement(meas, cfg); } } @@ -407,7 +409,7 @@ bool cc_worker::measure_csi() bool estimate_fft = false; for (uint32_t resource_set_id = 0; resource_set_id < SRSRAN_PHCH_CFG_MAX_NOF_CSI_RS_SETS; resource_set_id++) { // Select NZP-CSI-RS set - const srsran_csi_rs_nzp_set_t& nzp_set = phy.cfg.pdsch.nzp_csi_rs_sets[resource_set_id]; + const srsran_csi_rs_nzp_set_t& nzp_set = cfg.pdsch.nzp_csi_rs_sets[resource_set_id]; // Skip set if not set as TRS (it will be processed later) if (not nzp_set.trs_info) { @@ -439,13 +441,13 @@ bool cc_worker::measure_csi() logger.debug("NZP-CSI-RS (TRS): id=%d %s", resource_set_id, str.data()); } - phy.new_csi_trs_measurement(trs_measurements, resource_set_id, (uint32_t)n); + phy.new_csi_trs_measurement(trs_measurements, cfg, resource_set_id, (uint32_t)n); } // Iterate all NZP-CSI-RS not marked as TRS and perform channel measurements for (uint32_t resource_set_id = 0; resource_set_id < SRSRAN_PHCH_CFG_MAX_NOF_CSI_RS_SETS; resource_set_id++) { // Select NZP-CSI-RS set - const srsran_csi_rs_nzp_set_t& nzp_set = phy.cfg.pdsch.nzp_csi_rs_sets[resource_set_id]; + const srsran_csi_rs_nzp_set_t& nzp_set = cfg.pdsch.nzp_csi_rs_sets[resource_set_id]; // Skip set if set as TRS (it was processed previously) if (nzp_set.trs_info) { @@ -478,7 +480,7 @@ bool cc_worker::measure_csi() measurements.wideband_snr_db); // Report new measurement to the PHY state - phy.new_nzp_csi_rs_channel_measurement(measurements, resource_set_id); + phy.new_nzp_csi_rs_channel_measurement(cfg, measurements, resource_set_id); } return true; @@ -492,7 +494,7 @@ bool cc_worker::work_dl() } // Check if it is a DL slot, if not skip - if (!srsran_duplex_nr_is_dl(&phy.cfg.duplex, 0, dl_slot_cfg.idx)) { + if (!srsran_duplex_nr_is_dl(&cfg.duplex, 0, dl_slot_cfg.idx)) { return true; } @@ -540,7 +542,7 @@ bool cc_worker::work_ul() 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_duplex_nr_is_ul(&phy.cfg.duplex, 0, ul_slot_cfg.idx)) { + if (!srsran_duplex_nr_is_ul(&cfg.duplex, 0, ul_slot_cfg.idx)) { // No NR signal shall be transmitted srsran_vec_cf_zero(tx_buffer[0], ue_ul.ifft.sf_sz); @@ -571,7 +573,7 @@ bool cc_worker::work_ul() } } - if (srsran_harq_ack_pack(&phy.cfg.harq_ack, &pdsch_ack, &uci_data) < SRSRAN_SUCCESS) { + if (srsran_harq_ack_pack(&cfg.harq_ack, &pdsch_ack, &uci_data) < SRSRAN_SUCCESS) { ERROR("Filling UCI ACK bits"); return false; } @@ -579,11 +581,11 @@ bool cc_worker::work_ul() // Add SR to UCI data only if there is no UL grant! if (not has_pusch_grant) { - phy.get_pending_sr(ul_slot_cfg.idx, uci_data); + phy.get_pending_sr(cfg, ul_slot_cfg.idx, uci_data); } // Add CSI reports to UCI data if available - phy.get_periodic_csi(ul_slot_cfg, uci_data); + phy.get_periodic_csi(cfg, ul_slot_cfg, uci_data); // Setup frequency offset srsran_ue_ul_nr_set_freq_offset(&ue_ul, phy.get_ul_cfo()); @@ -608,7 +610,7 @@ bool cc_worker::work_ul() } // Set UCI configuration following procedures - srsran_ra_ul_set_grant_uci_nr(&phy.cfg.carrier, &phy.cfg.pusch, &uci_data.cfg, &pusch_cfg); + srsran_ra_ul_set_grant_uci_nr(&cfg.carrier, &cfg.pusch, &uci_data.cfg, &pusch_cfg); // Assigning MAC provided values to PUSCH config structs pusch_cfg.grant.tb[0].softbuffer.tx = ul_action.tb.softbuffer; @@ -660,14 +662,13 @@ bool cc_worker::work_ul() } else if (srsran_uci_nr_total_bits(&uci_data.cfg) > 0) { // Get PUCCH resource srsran_pucch_nr_resource_t resource = {}; - if (srsran_ra_ul_nr_pucch_resource(&phy.cfg.pucch, &uci_data.cfg, &resource) < SRSRAN_SUCCESS) { + if (srsran_ra_ul_nr_pucch_resource(&cfg.pucch, &uci_data.cfg, &resource) < SRSRAN_SUCCESS) { ERROR("Selecting PUCCH resource"); return false; } // Encode PUCCH message - if (srsran_ue_ul_nr_encode_pucch(&ue_ul, &ul_slot_cfg, &phy.cfg.pucch.common, &resource, &uci_data) < - SRSRAN_SUCCESS) { + if (srsran_ue_ul_nr_encode_pucch(&ue_ul, &ul_slot_cfg, &cfg.pucch.common, &resource, &uci_data) < SRSRAN_SUCCESS) { ERROR("Encoding PUCCH"); return false; } diff --git a/srsue/src/phy/nr/sf_worker.cc b/srsue/src/phy/nr/sf_worker.cc index 703084d82..d1887e81c 100644 --- a/srsue/src/phy/nr/sf_worker.cc +++ b/srsue/src/phy/nr/sf_worker.cc @@ -27,22 +27,26 @@ static int plot_worker_id = -1; namespace srsue { namespace nr { -sf_worker::sf_worker(srsran::phy_common_interface& common_, state& phy_state_, srslog::basic_logger& log) : - phy_state(phy_state_), common(common_), logger(log) + +sf_worker::sf_worker(srsran::phy_common_interface& common_, + state& phy_state_, + const srsran::phy_cfg_nr_t& cfg, + srslog::basic_logger& log) : + phy_state(phy_state_), common(common_), logger(log), sf_len(SRSRAN_SF_LEN_PRB_NR(cfg.carrier.nof_prb)) { for (uint32_t i = 0; i < phy_state.args.nof_carriers; i++) { - cc_worker* w = new cc_worker(i, log, phy_state); + cc_worker* w = new cc_worker(i, log, phy_state, cfg); cc_workers.push_back(std::unique_ptr(w)); } } -bool sf_worker::update_cfg(uint32_t cc_idx) +void sf_worker::update_cfg(uint32_t cc_idx, const srsran::phy_cfg_nr_t& new_cfg) { if (cc_idx >= cc_workers.size()) { - return false; + return; } - return cc_workers[cc_idx]->update_cfg(); + cc_workers[cc_idx]->update_cfg(new_cfg); } cf_t* sf_worker::get_buffer(uint32_t cc_idx, uint32_t antenna_idx) @@ -86,7 +90,7 @@ void sf_worker::work_imp() if (prach_ptr != nullptr) { // PRACH is available, set buffer, transmit and return 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)); + tx_buffer.set_nof_samples(sf_len); // Transmit NR PRACH common.worker_end(context, true, tx_buffer); @@ -106,7 +110,7 @@ void sf_worker::work_imp() for (uint32_t i = 0; i < (uint32_t)cc_workers.size(); i++) { 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)); + tx_buffer.set_nof_samples(sf_len); // Always call worker_end before returning common.worker_end(context, true, tx_buffer); diff --git a/srsue/src/phy/nr/worker_pool.cc b/srsue/src/phy/nr/worker_pool.cc index dfa333166..9a5ea50e2 100644 --- a/srsue/src/phy/nr/worker_pool.cc +++ b/srsue/src/phy/nr/worker_pool.cc @@ -25,9 +25,16 @@ bool worker_pool::init(const phy_args_nr_t& args, phy_state.stack = stack_; phy_state.args = args; - // Set carrier attributes - phy_state.cfg.carrier.pci = 500; - phy_state.cfg.carrier.nof_prb = args.max_nof_prb; + { + std::lock_guard lock(cfg_mutex); + pending_cfgs.resize(args.nof_phy_threads); + for (auto&& b : pending_cfgs) { + b = false; + } + // Set carrier attributes + cfg.carrier.pci = 500; + cfg.carrier.nof_prb = args.max_nof_prb; + } // Set NR arguments phy_state.args.nof_carriers = args.nof_carriers; @@ -47,7 +54,11 @@ bool worker_pool::init(const phy_args_nr_t& args, log.set_level(srslog::str_to_basic_level(args.log.phy_level)); log.set_hex_dump_max_size(args.log.phy_hex_limit); - auto w = new sf_worker(common, phy_state, log); + sf_worker* w = nullptr; + { + std::lock_guard lock(cfg_mutex); + w = new sf_worker(common, phy_state, cfg, log); + } pool.init_worker(i, w, prio, args.worker_cpu_mask); workers.push_back(std::unique_ptr(w)); } @@ -76,8 +87,18 @@ sf_worker* worker_pool::wait_worker(uint32_t tti) logger.set_context(tti); sf_worker* worker = (sf_worker*)pool.wait_worker(tti); + uint32_t pci = 0; + { + std::lock_guard lock(cfg_mutex); + pci = cfg.carrier.pci; + if (pending_cfgs[worker->get_id()]) { + pending_cfgs[worker->get_id()] = false; + worker->set_cfg(cfg); + } + } + // Generate PRACH if ready - if (prach_buffer->is_ready_to_send(tti, phy_state.cfg.carrier.pci)) { + if (prach_buffer->is_ready_to_send(tti, pci)) { prach_ptr = prach_buffer->generate(phy_state.get_ul_cfo() / 15000, &prach_nof_sf, &prach_target_power); // Scale signal to maximum @@ -90,12 +111,19 @@ sf_worker* worker_pool::wait_worker(uint32_t tti) } } + uint32_t config_idx = 0; + srsran_duplex_mode_t mode = SRSRAN_DUPLEX_MODE_FDD; + srsran_subcarrier_spacing_t scs = srsran_subcarrier_spacing_15kHz; + { + std::lock_guard lock(cfg_mutex); + config_idx = cfg.prach.config_idx; + mode = cfg.duplex.mode; + scs = cfg.carrier.scs; + } + // Notify MAC about PRACH transmission - phy_state.stack->prach_sent(TTI_TX(tti), - srsran_prach_nr_start_symbol(phy_state.cfg.prach.config_idx, phy_state.cfg.duplex.mode), - SRSRAN_SLOT_NR_MOD(phy_state.cfg.carrier.scs, TTI_TX(tti)), - 0, - 0); + phy_state.stack->prach_sent( + TTI_TX(tti), srsran_prach_nr_start_symbol(config_idx, mode), SRSRAN_SLOT_NR_MOD(scs, TTI_TX(tti)), 0, 0); } // Set PRACH transmission buffer in workers if it is pending @@ -162,48 +190,81 @@ int worker_pool::set_ul_grant(uint32_t rar logger.info("Setting RAR Grant: %s", str.data()); } - phy_state.set_ul_pending_grant(msg3_slot_cfg, dci_ul); + std::lock_guard lock(cfg_mutex); + phy_state.set_ul_pending_grant(cfg, msg3_slot_cfg, dci_ul); return SRSRAN_SUCCESS; } -bool worker_pool::set_config(const srsran::phy_cfg_nr_t& cfg) + +bool worker_pool::set_config(const srsran::phy_cfg_nr_t& new_cfg) { - uint32_t dl_arfcn = srsran::srsran_band_helper().freq_to_nr_arfcn(cfg.carrier.dl_center_frequency_hz); - phy_state.cfg = cfg; - sf_sz = SRSRAN_SF_LEN_PRB_NR(cfg.carrier.nof_prb); + uint32_t dl_arfcn = srsran::srsran_band_helper().freq_to_nr_arfcn(new_cfg.carrier.dl_center_frequency_hz); + sf_sz = SRSRAN_SF_LEN_PRB_NR(new_cfg.carrier.nof_prb); - logger.info("Setting new PHY configuration ARFCN=%d, PCI=%d", dl_arfcn, cfg.carrier.pci); + bool carrier_equal; + { + std::lock_guard lock(cfg_mutex); + + // Check if the carrier has changed + carrier_equal = cfg.carrier_is_equal(new_cfg); + + // If the carrier has not changed, reset pending flags. Configuration will be copied when the worker is reserved + // from the real-time thread + for (auto&& b : pending_cfgs) { + b = carrier_equal; + } + + // Update configuration + cfg = new_cfg; + } + + // If the carrier has changed, the configuration cannot be set from the real-time thread + if (not carrier_equal) { + // Configure each worker with the new configuration + for (uint32_t i = 0; i < (uint32_t)workers.size(); i++) { + // Wait for each worker to avoid concurrency issues + sf_worker* w = (sf_worker*)pool.wait_worker_id(i); + if (w == nullptr) { + // Unlikely to happen + continue; + } + + // Configure worker + w->set_cfg(new_cfg); + + // Release worker + w->release(); + + // As the worker has been configured, there is no need to load new configuration from the real-time thread + pending_cfgs[i] = false; + } + } + + logger.info("Setting new PHY configuration ARFCN=%d, PCI=%d", dl_arfcn, new_cfg.carrier.pci); // Set carrier information info_metrics_t info = {}; - info.pci = cfg.carrier.pci; + info.pci = new_cfg.carrier.pci; info.dl_earfcn = dl_arfcn; phy_state.set_info_metrics(info); // Best effort to convert NR carrier into LTE cell srsran_cell_t cell = {}; - int ret = srsran_carrier_to_cell(&phy_state.cfg.carrier, &cell); + int ret = srsran_carrier_to_cell(&new_cfg.carrier, &cell); if (ret < SRSRAN_SUCCESS) { logger.error("Converting carrier to cell for PRACH (%d)", ret); return false; } - // Request workers to run any procedure related to configuration update - for (auto& w : workers) { - if (not w->update_cfg(0)) { - return false; - } - } - // Best effort to set up NR-PRACH config reused for NR - srsran_prach_cfg_t prach_cfg = cfg.prach; - uint32_t lte_nr_prach_offset = (phy_state.cfg.carrier.nof_prb - cell.nof_prb) / 2; + srsran_prach_cfg_t prach_cfg = new_cfg.prach; + uint32_t lte_nr_prach_offset = (new_cfg.carrier.nof_prb - cell.nof_prb) / 2; if (prach_cfg.freq_offset < lte_nr_prach_offset) { logger.error("prach_cfg.freq_offset=%d is not compatible with LTE", prach_cfg.freq_offset); return false; } prach_cfg.freq_offset -= lte_nr_prach_offset; - prach_cfg.tdd_config.configured = (cfg.duplex.mode == SRSRAN_DUPLEX_MODE_TDD); + prach_cfg.tdd_config.configured = (new_cfg.duplex.mode == SRSRAN_DUPLEX_MODE_TDD); // Set the PRACH configuration if (not prach_buffer->set_cell(cell, prach_cfg)) { @@ -216,7 +277,8 @@ bool worker_pool::set_config(const srsran::phy_cfg_nr_t& cfg) bool worker_pool::has_valid_sr_resource(uint32_t sr_id) { - return phy_state.has_valid_sr_resource(sr_id); + std::lock_guard lock(cfg_mutex); + return phy_state.has_valid_sr_resource(cfg, sr_id); } void worker_pool::clear_pending_grants() diff --git a/srsue/src/phy/phy.cc b/srsue/src/phy/phy.cc index 51573cca9..39ab0fdfc 100644 --- a/srsue/src/phy/phy.cc +++ b/srsue/src/phy/phy.cc @@ -623,6 +623,7 @@ void phy::set_mch_period_stop(uint32_t stop) int phy::init(const phy_args_nr_t& args_, stack_interface_phy_nr* stack_, srsran::radio_interface_phy* radio_) { + stack_nr = stack_; if (!nr_workers.init(args_, common, stack_, WORKERS_THREAD_PRIO)) { return SRSRAN_ERROR; } @@ -658,17 +659,36 @@ void phy::set_earfcn(std::vector earfcns) bool phy::set_config(const srsran::phy_cfg_nr_t& cfg) { - srsran::srsran_band_helper band_helper; + // Stash NR configuration + config_nr = cfg; - // tune radio - for (uint32_t i = 0; i < common.args->nof_nr_carriers; i++) { - logger_phy.info("Tuning Rx channel %d to %.2f MHz", i + common.args->nof_lte_carriers, cfg.carrier.dl_center_frequency_hz / 1e6); - radio->set_rx_freq(i + common.args->nof_lte_carriers, cfg.carrier.dl_center_frequency_hz); - logger_phy.info("Tuning Tx channel %d to %.2f MHz", i + common.args->nof_lte_carriers, cfg.carrier.ul_center_frequency_hz / 1e6); - radio->set_tx_freq(i + common.args->nof_lte_carriers, cfg.carrier.ul_center_frequency_hz); - } + // Setup carrier configuration asynchronously + cmd_worker.add_cmd([this]() { + srsran::srsran_band_helper band_helper; + + // tune radio + for (uint32_t i = 0; i < common.args->nof_nr_carriers; i++) { + logger_phy.info("Tuning Rx channel %d to %.2f MHz", + i + common.args->nof_lte_carriers, + config_nr.carrier.dl_center_frequency_hz / 1e6); + radio->set_rx_freq(i + common.args->nof_lte_carriers, config_nr.carrier.dl_center_frequency_hz); + logger_phy.info("Tuning Tx channel %d to %.2f MHz", + i + common.args->nof_lte_carriers, + config_nr.carrier.ul_center_frequency_hz / 1e6); + radio->set_tx_freq(i + common.args->nof_lte_carriers, config_nr.carrier.ul_center_frequency_hz); + } + + // Set UE configuration + bool ret = nr_workers.set_config(config_nr); - return nr_workers.set_config(cfg); + // Notify PHY config completion + if (stack_nr != nullptr) { + stack_nr->set_phy_config_complete(ret); + } + + return ret; + }); + return true; } bool phy::has_valid_sr_resource(uint32_t sr_id) diff --git a/srsue/src/stack/rrc/rrc_nr.cc b/srsue/src/stack/rrc/rrc_nr.cc index 3f668883e..0aed24a96 100644 --- a/srsue/src/stack/rrc/rrc_nr.cc +++ b/srsue/src/stack/rrc/rrc_nr.cc @@ -1326,8 +1326,8 @@ bool rrc_nr::apply_sp_cell_cfg(const sp_cell_cfg_s& sp_cell_cfg) current_phycfg.csi = prev_csi; phy->set_config(current_phycfg); - // Start RA procedure - mac->start_ra_procedure(); + phy_cfg_state = PHY_CFG_STATE_APPLY_SP_CELL; + return true; } @@ -1526,6 +1526,7 @@ void rrc_nr::ra_completed() { logger.info("RA completed. Applying remaining CSI configuration."); phy->set_config(phy_cfg); + phy_cfg_state = PHY_CFG_STATE_RA_COMPLETED; } void rrc_nr::ra_problem() { @@ -1538,6 +1539,24 @@ void rrc_nr::release_pucch_srs() {} void rrc_nr::cell_search_completed(const rrc_interface_phy_lte::cell_search_ret_t& cs_ret, const phy_cell_t& found_cell) {} +void rrc_nr::set_phy_config_complete(bool status) +{ + switch (phy_cfg_state) { + case PHY_CFG_STATE_NONE: + logger.warning("PHY configuration completed without a clear state."); + break; + case PHY_CFG_STATE_APPLY_SP_CELL: + // Start RA procedure + logger.info("PHY configuration completed. Starting RA procedure."); + mac->start_ra_procedure(); + break; + case PHY_CFG_STATE_RA_COMPLETED: + logger.info("Remaining CSI configuration completed."); + break; + } + phy_cfg_state = PHY_CFG_STATE_NONE; +} + /* Procedures */ rrc_nr::connection_reconf_no_ho_proc::connection_reconf_no_ho_proc(rrc_nr* parent_) : rrc_ptr(parent_), initiator(nr) {} diff --git a/srsue/src/stack/ue_stack_lte.cc b/srsue/src/stack/ue_stack_lte.cc index e65144b96..339a835a8 100644 --- a/srsue/src/stack/ue_stack_lte.cc +++ b/srsue/src/stack/ue_stack_lte.cc @@ -487,5 +487,9 @@ void ue_stack_lte::run_tti_impl(uint32_t tti, uint32_t tti_jump) stack_logger.warning("Detected slow task processing (sync_queue_len=%zd).", sync_task_queue.size()); } } +void ue_stack_lte::set_phy_config_complete(bool status) +{ + cfg_task_queue.push([this, status]() { rrc_nr.set_phy_config_complete(status); }); +} } // namespace srsue diff --git a/srsue/src/stack/ue_stack_nr.cc b/srsue/src/stack/ue_stack_nr.cc index bd8b95a61..b5c2a38fc 100644 --- a/srsue/src/stack/ue_stack_nr.cc +++ b/srsue/src/stack/ue_stack_nr.cc @@ -197,4 +197,9 @@ void ue_stack_nr::run_tti_impl(uint32_t tti) task_sched.tic(); } +void ue_stack_nr::set_phy_config_complete(bool status) +{ + sync_task_queue.push([this, status]() { rrc->set_phy_config_complete(status); }); +} + } // namespace srsue diff --git a/test/phy/dummy_ue_stack.h b/test/phy/dummy_ue_stack.h index 598099fec..e90f38cc2 100644 --- a/test/phy/dummy_ue_stack.h +++ b/test/phy/dummy_ue_stack.h @@ -123,6 +123,11 @@ public: bool is_valid() const { return valid; } metrics_t get_metrics() { return metrics; } + + void set_phy_config_complete(bool status) override + { + + } }; #endif // SRSRAN_DUMMY_UE_STACK_H