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