diff --git a/srsue/hdr/phy/phy_common.h b/srsue/hdr/phy/phy_common.h index fea225678..a162daa0f 100644 --- a/srsue/hdr/phy/phy_common.h +++ b/srsue/hdr/phy/phy_common.h @@ -31,6 +31,7 @@ #include "srslte/interfaces/ue_interfaces.h" #include "srslte/radio/radio.h" #include "srslte/srslte.h" +#include "srsue/hdr/phy/scell/scell_state.h" #include "ta_control.h" #include #include @@ -59,14 +60,8 @@ public: srslte::phy_cfg_mbsfn_t mbsfn_config = {}; - // SCell EARFCN, PCI, configured and enabled list - typedef struct { - uint32_t earfcn = 0; - uint32_t pci = 0; - bool configured = false; - bool enabled = false; - } scell_cfg_t; - scell_cfg_t scell_cfg[SRSLTE_MAX_CARRIERS]; + // Secondary serving cell states + scell::state scell_state; // Save last TBS for uplink (mcs >= 28) srslte_ra_tb_t last_ul_tb[SRSLTE_MAX_HARQ_PROC][SRSLTE_MAX_CARRIERS] = {}; @@ -162,9 +157,6 @@ public: void reset(); void reset_radio(); - /* SCell Management */ - void enable_scell(uint32_t cc_idx, bool enable); - void build_mch_table(); void build_mcch_table(); void set_mcch(); @@ -240,22 +232,22 @@ public: private: std::mutex meas_mutex; - float pathloss[SRSLTE_MAX_CARRIERS] = {}; - float cur_pathloss = 0.0f; - float cur_pusch_power = 0.0f; - float avg_rsrp[SRSLTE_MAX_CARRIERS] = {}; - float avg_rsrp_dbm[SRSLTE_MAX_CARRIERS] = {}; - float avg_rsrq_db[SRSLTE_MAX_CARRIERS] = {}; - float avg_rssi_dbm[SRSLTE_MAX_CARRIERS] = {}; - float avg_cfo_hz[SRSLTE_MAX_CARRIERS] = {}; - float rx_gain_offset = 0.0f; - float avg_sinr_db[SRSLTE_MAX_CARRIERS] = {}; - float avg_snr_db[SRSLTE_MAX_CARRIERS] = {}; - float avg_noise[SRSLTE_MAX_CARRIERS] = {}; - float avg_rsrp_neigh[SRSLTE_MAX_CARRIERS] = {}; - - uint32_t pcell_report_period = 0; - uint32_t rssi_read_cnt = 0; + float pathloss[SRSLTE_MAX_CARRIERS] = {}; + float cur_pathloss = 0.0f; + float cur_pusch_power = 0.0f; + float avg_rsrp[SRSLTE_MAX_CARRIERS] = {}; + float avg_rsrp_dbm[SRSLTE_MAX_CARRIERS] = {}; + float avg_rsrq_db[SRSLTE_MAX_CARRIERS] = {}; + float avg_rssi_dbm[SRSLTE_MAX_CARRIERS] = {}; + float avg_cfo_hz[SRSLTE_MAX_CARRIERS] = {}; + float rx_gain_offset = 0.0f; + float avg_sinr_db[SRSLTE_MAX_CARRIERS] = {}; + float avg_snr_db[SRSLTE_MAX_CARRIERS] = {}; + float avg_noise[SRSLTE_MAX_CARRIERS] = {}; + float avg_rsrp_neigh[SRSLTE_MAX_CARRIERS] = {}; + + uint32_t pcell_report_period = 0; + uint32_t rssi_read_cnt = 0; rsrp_insync_itf* insync_itf = nullptr; diff --git a/srsue/hdr/phy/scell/scell_state.h b/srsue/hdr/phy/scell/scell_state.h new file mode 100644 index 000000000..90624fa62 --- /dev/null +++ b/srsue/hdr/phy/scell/scell_state.h @@ -0,0 +1,234 @@ +/* + * Copyright 2013-2020 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSLTE_SCELL_STATE_H +#define SRSLTE_SCELL_STATE_H + +#include +#include +#include + +namespace srsue { +namespace scell { + +/** + * References + * + * According to 3GPP 36.321 R10 (MAC procedures) section 5.13 Activation/Deactivation of SCells + * An activated cell operation shall include (Summarised): + * - SRS transmissions on the SCell; + * - CQI/PMI/RI/PTI reporting for the SCell; + * - PDCCH monitoring on the SCell; + * - PDCCH monitoring for the SCell + * + * According to 3GPP 36.213 R10 (PHY procedures) section 4.3 Timing for Secondary Cell Activation / Deactivation + * When a UE receives an activation command for a secondary cell in subframe n, the corresponding actions shall be + * applied at subframe n+8. (Summarised) + */ +class state +{ +private: + static constexpr uint32_t activation_delay_tti = FDD_HARQ_DELAY_DL_MS + FDD_HARQ_DELAY_UL_MS; + static constexpr uint32_t activation_margin_tti = FDD_HARQ_DELAY_DL_MS; + + // SCell EARFCN, PCI, configured and enabled list + struct cfg { + uint32_t earfcn = 0; + uint32_t pci = 0; + enum { none = 0, inactive, active } status = none; + }; + + std::array scell_cfg; + + enum { idle = 0, waiting, transition } activation_state = idle; + uint32_t activation_cmd = 0; + uint32_t activation_tti = 0; + mutable std::mutex mutex; + + bool _get_cmd_activation(uint32_t cc_idx) const { return ((activation_cmd >> cc_idx) & 0x1) == 0x1; } + + bool _tti_greater_or_equal_than(uint32_t a, uint32_t b) const { return TTI_SUB(a, b) < 10240 / 2; } + +public: + /** + * A SCell Activation/Deactivation command is received. Stores the new command and the TTI. Also, the internal state + * goes to waiting. + * + * If a previous command was received and not applied, it will discard it. + * + * @param cmd SCell Activation/Deactivation command + * @param tti TTI in which the command was received + */ + void set_activation_deactivation(uint32_t cmd, uint32_t tti) + { + std::unique_lock lock(mutex); + + // Store command + activation_cmd = cmd; + + // Command is waiting + activation_state = waiting; + + activation_tti = TTI_ADD(tti, activation_delay_tti); + } + + /** + * @brief Deactivates all the active SCells + */ + void deactivate_all() + { + for (cfg& e : scell_cfg) { + if (e.status == cfg::active) { + e.status = cfg::inactive; + } + } + } + + void run_tti(uint32_t tti) + { + std::unique_lock lock(mutex); + + switch (activation_state) { + + case idle: + // waiting for receiving a command, do nothing + break; + case waiting: + // Detect that TTI when the CMD needs to be applied, the activation cannot be done instantly because some + // workers might be currently ongoing, so only update state + if (_tti_greater_or_equal_than(tti, activation_tti)) { + activation_state = transition; + } + break; + case transition: + // Detect when the TTI has increased enough to make sure there arent workers, set the configuration + if (TTI_SUB(tti, activation_tti) >= activation_margin_tti) { + + // Reload cell states + for (uint32_t i = 1; i < SRSLTE_MAX_CARRIERS; i++) { + // Get Activation command value + bool activate = _get_cmd_activation(i); + + // Apply activation only if the cell was configured + if (scell_cfg[i].status != cfg::none) { + scell_cfg[i].status = activate ? cfg::active : cfg::inactive; + } + } + + // Go back to initial state + activation_state = idle; + } + break; + } + } + + void configure(uint32_t cc_idx, uint32_t earfcn, uint32_t pci) + { + std::unique_lock lock(mutex); + + if (cc_idx == 0 or cc_idx >= SRSLTE_MAX_CARRIERS) { + ERROR("CC IDX %d out-of-range\n", cc_idx); + return; + } + + scell_cfg[cc_idx].status = cfg::inactive; + scell_cfg[cc_idx].earfcn = earfcn; + scell_cfg[cc_idx].pci = pci; + } + + bool is_active(uint32_t cc_idx, uint32_t tti) const + { + + if (cc_idx == 0) { + return true; + } + + if (cc_idx >= SRSLTE_MAX_CARRIERS) { + return false; + } + + std::unique_lock lock(mutex); + + // Use stashed activation if the activation is transitioning and the current TTI requires new value + if (activation_state == transition and scell_cfg[cc_idx].status != cfg::none and + _tti_greater_or_equal_than(tti, activation_tti)) { + return _get_cmd_activation(cc_idx); + } + + return scell_cfg[cc_idx].status == cfg::active; + } + + bool is_configured(uint32_t cc_idx) const + { + + if (cc_idx == 0) { + return true; + } + + if (cc_idx >= SRSLTE_MAX_CARRIERS) { + return false; + } + + std::unique_lock lock(mutex); + + return scell_cfg[cc_idx].status != cfg::none; + } + + void reset() + { + std::unique_lock lock(mutex); + + activation_state = idle; + + for (cfg& e : scell_cfg) { + e.status = cfg::none; + e.earfcn = 0; + e.pci = UINT32_MAX; + } + } + + uint32_t get_pci(uint32_t cc_idx) + { + std::unique_lock lock(mutex); + + if (cc_idx == 0 or cc_idx >= SRSLTE_MAX_CARRIERS) { + ERROR("CC IDX %d out-of-range\n", cc_idx); + return 0; + } + + return scell_cfg[cc_idx].pci; + } + + uint32_t get_earfcn(uint32_t cc_idx) + { + std::unique_lock lock(mutex); + + if (cc_idx == 0 or cc_idx >= SRSLTE_MAX_CARRIERS) { + ERROR("CC IDX %d out-of-range\n", cc_idx); + return 0; + } + + return scell_cfg[cc_idx].earfcn; + } +}; +} // namespace scell +} // namespace srsue +#endif // SRSLTE_SCELL_STATE_H \ No newline at end of file diff --git a/srsue/src/phy/cc_worker.cc b/srsue/src/phy/cc_worker.cc index 7e3b5ea0d..ce30ca311 100644 --- a/srsue/src/phy/cc_worker.cc +++ b/srsue/src/phy/cc_worker.cc @@ -847,7 +847,7 @@ void cc_worker::set_uci_ack(srslte_uci_data_t* uci_data, // Only PCell generates ACK for all SCell for (uint32_t i = 0; i < phy->args->nof_carriers; i++) { - if (i == 0 || phy->scell_cfg[i].configured) { + if (phy->scell_state.is_configured(i)) { phy->get_dl_pending_ack(&sf_cfg_ul, i, &ack_info.cc[i]); nof_configured_carriers++; } diff --git a/srsue/src/phy/phy.cc b/srsue/src/phy/phy.cc index 1d903b67b..419f0d220 100644 --- a/srsue/src/phy/phy.cc +++ b/srsue/src/phy/phy.cc @@ -246,8 +246,8 @@ void phy::get_metrics(phy_metrics_t* m) m->info[0].dl_earfcn = dl_earfcn; for (uint32_t i = 1; i < args.nof_carriers; i++) { - m->info[i].dl_earfcn = common.scell_cfg[i].earfcn; - m->info[i].pci = common.scell_cfg[i].pci; + m->info[i].dl_earfcn = common.scell_state.get_earfcn(i); + m->info[i].pci = common.scell_state.get_pci(i); } common.get_ch_metrics(m->ch); @@ -269,20 +269,12 @@ void phy::set_timeadv(uint32_t ta_cmd) void phy::deactivate_scells() { - for (uint32_t i = 1; i < SRSLTE_MAX_CARRIERS; i++) { - common.enable_scell(i, false); - } + common.scell_state.deactivate_all(); } void phy::set_activation_deactivation_scell(uint32_t cmd, uint32_t tti) { - /* Implements 3GPP 36.321 section 6.1.3.8. Activation/Deactivation MAC Control Element*/ - for (uint32_t i = 1; i < SRSLTE_MAX_CARRIERS; i++) { - bool activated = ((cmd >> i) & 0x1u) == 0x1u; - - /* Enable actual cell */ - common.enable_scell(i, activated); - } + common.scell_state.set_activation_deactivation(cmd, tti); } void phy::configure_prach_params() @@ -527,16 +519,13 @@ bool phy::set_scell(srslte_cell_t cell_info, uint32_t cc_idx, uint32_t earfcn) return false; } - bool earfcn_is_different = common.scell_cfg[cc_idx].earfcn != earfcn; + bool earfcn_is_different = common.scell_state.get_earfcn(cc_idx) != earfcn; // Set inter-frequency measurement sfsync.set_inter_frequency_measurement(cc_idx, earfcn, cell_info); - // Store SCell earfcn and pci - common.scell_cfg[cc_idx].earfcn = earfcn; - common.scell_cfg[cc_idx].pci = cell_info.id; - common.scell_cfg[cc_idx].configured = true; - common.scell_cfg[cc_idx].enabled = false; + // Store secondary serving cell EARFCN and PCI + common.scell_state.configure(cc_idx, earfcn, cell_info.id); // Reset cell configuration for (uint32_t i = 0; i < nof_workers; i++) { diff --git a/srsue/src/phy/phy_common.cc b/srsue/src/phy/phy_common.cc index 9befbdfbf..a18417eb1 100644 --- a/srsue/src/phy/phy_common.cc +++ b/srsue/src/phy/phy_common.cc @@ -742,7 +742,7 @@ void phy_common::update_measurements(uint32_t set_ch_metrics(cc_idx, ch); // Prepare measurements for serving cells - bool active = (cc_idx == 0 || scell_cfg[cc_idx].configured); + bool active = scell_state.is_configured(cc_idx); if (active && ((sf_cfg_dl.tti % pcell_report_period) == pcell_report_period - 1)) { rrc_interface_phy_lte::phy_meas_t meas = {}; meas.rsrp = avg_rsrp_dbm[cc_idx]; @@ -753,8 +753,8 @@ void phy_common::update_measurements(uint32_t if (cc_idx == 0) { meas.pci = cell.id; } else { - meas.earfcn = scell_cfg[cc_idx].earfcn; - meas.pci = scell_cfg[cc_idx].pci; + meas.earfcn = scell_state.get_earfcn(cc_idx); + meas.pci = scell_state.get_pci(cc_idx); } serving_cells.push_back(meas); } @@ -903,7 +903,7 @@ void phy_common::reset() ZERO_OBJECT(avg_rsrp); ZERO_OBJECT(avg_rsrp_dbm); ZERO_OBJECT(avg_rsrq_db); - ZERO_OBJECT(scell_cfg); + scell_state.reset(); reset_neighbour_cells(); @@ -1125,19 +1125,4 @@ bool phy_common::is_mbsfn_sf(srslte_mbsfn_cfg_t* cfg, uint32_t phy_tti) return is_mch_subframe(cfg, phy_tti); } -void phy_common::enable_scell(uint32_t cc_idx, bool enable) -{ - if (cc_idx < SRSLTE_MAX_CARRIERS) { - if (scell_cfg[cc_idx].configured) { - scell_cfg[cc_idx].enabled = enable; - } else { - if (enable) { - Error("Leaving SCell %s. cc_idx=%d has not been configured.\n", - scell_cfg[cc_idx].enabled ? "enabled" : "disabled", - cc_idx); - } - } - } -} - } // namespace srsue diff --git a/srsue/src/phy/sf_worker.cc b/srsue/src/phy/sf_worker.cc index ec1d08c00..a1c3c164c 100644 --- a/srsue/src/phy/sf_worker.cc +++ b/srsue/src/phy/sf_worker.cc @@ -205,7 +205,7 @@ void sf_worker::work_imp() if (carrier_idx == 0 && phy->is_mbsfn_sf(&mbsfn_cfg, tti)) { cc_workers[0]->work_dl_mbsfn(mbsfn_cfg); // Don't do chest_ok in mbsfn since it trigger measurements } else { - if ((carrier_idx == 0) || (phy->scell_cfg[carrier_idx].enabled && phy->scell_cfg[carrier_idx].configured)) { + if (phy->scell_state.is_active(carrier_idx, tti)) { rx_signal_ok = cc_workers[carrier_idx]->work_dl_regular(); } } @@ -230,14 +230,14 @@ void sf_worker::work_imp() for (uint32_t carrier_idx = 0; carrier_idx < phy->args->nof_carriers and not uci_data.cfg.cqi.data_enable and uci_data.cfg.cqi.ri_len == 0; carrier_idx++) { - if (carrier_idx == 0 or phy->scell_cfg[carrier_idx].configured) { + if (phy->scell_state.is_active(carrier_idx, TTI_TX(tti))) { cc_workers[carrier_idx]->set_uci_periodic_cqi(&uci_data); } } // Loop through all carriers for (uint32_t carrier_idx = 0; carrier_idx < phy->args->nof_carriers; carrier_idx++) { - if ((carrier_idx == 0) || (phy->scell_cfg[carrier_idx].enabled && phy->scell_cfg[carrier_idx].configured)) { + if (phy->scell_state.is_active(carrier_idx, tti)) { tx_signal_ready |= cc_workers[carrier_idx]->work_ul(uci_cc_idx == carrier_idx ? &uci_data : nullptr); // Set signal pointer based on offset diff --git a/srsue/src/phy/sync.cc b/srsue/src/phy/sync.cc index 0b5888367..a02bf7bf0 100644 --- a/srsue/src/phy/sync.cc +++ b/srsue/src/phy/sync.cc @@ -480,6 +480,9 @@ void sync::run_camping_in_sync_state(sf_worker* worker, srslte::rf_buffer_t& syn worker->set_prach(prach_ptr ? &prach_ptr[prach_sf_cnt * SRSLTE_SF_LEN_PRB(cell.nof_prb)] : nullptr, prach_power); + // Execute Serving Cell state FSM + worker_com->scell_state.run_tti(tti); + // Set CFO for all Carriers for (uint32_t cc = 0; cc < worker_com->args->nof_carriers; cc++) { worker->set_cfo_unlocked(cc, get_tx_cfo());