diff --git a/lib/include/srslte/interfaces/enb_interfaces.h b/lib/include/srslte/interfaces/enb_interfaces.h index 5b9749cc8..e11bc5db2 100644 --- a/lib/include/srslte/interfaces/enb_interfaces.h +++ b/lib/include/srslte/interfaces/enb_interfaces.h @@ -255,8 +255,14 @@ public: typedef std::vector phy_rrc_dedicated_list_t; /** - * Sets the physical layer dedicated configuration for a given RNTI, a cell index and a secondary cell index. - * The cc_idx indicates the eNb cell to configure and the scell_idx is the UE's cell index + * Sets the physical layer dedicated configuration for a given RNTI. The dedicated configuration list shall provide + * all the required information configuration for the following cases: + * - Add an RNTI straight from RRC + * - Moving primary to another serving cell + * - Add/Remove secondary serving cells + * + * Remind this call will partially reconfigure the primary serving cell, `complete_config_dedicated``shall be called + * in order to complete the configuration. * * @param rnti the given RNTI * @param dedicated_list Physical layer configuration for the indicated eNb cell diff --git a/lib/src/phy/phch/pdsch.c b/lib/src/phy/phch/pdsch.c index 684a915f8..c3eb2d764 100644 --- a/lib/src/phy/phch/pdsch.c +++ b/lib/src/phy/phch/pdsch.c @@ -517,36 +517,44 @@ int srslte_pdsch_set_rnti(srslte_pdsch_t* q, uint16_t rnti) { uint32_t rnti_idx = q->is_ue ? 0 : rnti; - if (!q->users[rnti_idx] || q->is_ue) { + // Decide whether re-generating the sequence + if (!q->users[rnti_idx]) { + // If the sequence is not allocated generate + q->users[rnti_idx] = calloc(1, sizeof(srslte_pdsch_user_t)); if (!q->users[rnti_idx]) { - q->users[rnti_idx] = calloc(1, sizeof(srslte_pdsch_user_t)); - if (!q->users[rnti_idx]) { - ERROR("calloc"); - return -1; - } + ERROR("Alocating PDSCH user\n"); + return SRSLTE_ERROR; } - q->users[rnti_idx]->sequence_generated = false; + } else if (q->users[rnti_idx]->sequence_generated && q->users[rnti_idx]->cell_id == q->cell.id && !q->is_ue) { + // The sequence was generated, cell has not changed and it is eNb, save any efforts + return SRSLTE_SUCCESS; + } - for (int i = 0; i < SRSLTE_NOF_SF_X_FRAME; i++) { - for (int j = 0; j < SRSLTE_MAX_CODEWORDS; j++) { - if (srslte_sequence_pdsch(&q->users[rnti_idx]->seq[j][i], - rnti, - j, - 2 * i, - q->cell.id, - q->max_re * srslte_mod_bits_x_symbol(SRSLTE_MOD_256QAM))) { - ERROR("Error initializing PDSCH scrambling sequence\n"); - srslte_pdsch_free_rnti(q, rnti); - return SRSLTE_ERROR; - } + // Set sequence as not generated + q->users[rnti_idx]->sequence_generated = false; + + // For each subframe + for (int sf_idx = 0; sf_idx < SRSLTE_NOF_SF_X_FRAME; sf_idx++) { + // For each codeword + for (int j = 0; j < SRSLTE_MAX_CODEWORDS; j++) { + if (srslte_sequence_pdsch(&q->users[rnti_idx]->seq[j][sf_idx], + rnti, + j, + SRSLTE_NOF_SLOTS_PER_SF * sf_idx, + q->cell.id, + q->max_re * srslte_mod_bits_x_symbol(SRSLTE_MOD_256QAM))) { + ERROR("Error initializing PDSCH scrambling sequence\n"); + srslte_pdsch_free_rnti(q, rnti); + return SRSLTE_ERROR; } } - q->ue_rnti = rnti; - q->users[rnti_idx]->cell_id = q->cell.id; - q->users[rnti_idx]->sequence_generated = true; - } else { - ERROR("Error generating PDSCH sequence: rnti=0x%x already generated\n", rnti); } + + // Save generation states + q->ue_rnti = rnti; + q->users[rnti_idx]->cell_id = q->cell.id; + q->users[rnti_idx]->sequence_generated = true; + return SRSLTE_SUCCESS; } diff --git a/lib/src/phy/phch/pucch.c b/lib/src/phy/phch/pucch.c index 1d3a5c573..f321ebd3d 100644 --- a/lib/src/phy/phch/pucch.c +++ b/lib/src/phy/phch/pucch.c @@ -163,31 +163,39 @@ void srslte_pucch_free_rnti(srslte_pucch_t* q, uint16_t rnti) int srslte_pucch_set_rnti(srslte_pucch_t* q, uint16_t rnti) { - uint32_t rnti_idx = q->is_ue ? 0 : rnti; - if (!q->users[rnti_idx] || q->is_ue) { + + // Decide whether re-generating the sequence + if (!q->users[rnti_idx]) { + // If the sequence is not allocated generate + q->users[rnti_idx] = calloc(1, sizeof(srslte_pdsch_user_t)); if (!q->users[rnti_idx]) { - q->users[rnti_idx] = calloc(1, sizeof(srslte_pucch_user_t)); - if (!q->users[rnti_idx]) { - perror("calloc"); - return -1; - } + ERROR("Alocating PDSCH user\n"); + return SRSLTE_ERROR; } - q->users[rnti_idx]->sequence_generated = false; - for (uint32_t sf_idx = 0; sf_idx < SRSLTE_NOF_SF_X_FRAME; sf_idx++) { - // Precompute scrambling sequence for pucch format 2 - if (srslte_sequence_pucch(&q->users[rnti_idx]->seq_f2[sf_idx], rnti, 2 * sf_idx, q->cell.id)) { - ERROR("Error computing PUCCH Format 2 scrambling sequence\n"); - srslte_pucch_free_rnti(q, rnti); - return SRSLTE_ERROR; - } + } else if (q->users[rnti_idx]->sequence_generated && q->users[rnti_idx]->cell_id == q->cell.id && !q->is_ue) { + // The sequence was generated, cell has not changed and it is eNb, save any efforts + return SRSLTE_SUCCESS; + } + + // Set sequence as not generated + q->users[rnti_idx]->sequence_generated = false; + + // For each subframe + for (int sf_idx = 0; sf_idx < SRSLTE_NOF_SF_X_FRAME; sf_idx++) { + if (srslte_sequence_pucch( + &q->users[rnti_idx]->seq_f2[sf_idx], rnti, SRSLTE_NOF_SLOTS_PER_SF * sf_idx, q->cell.id)) { + ERROR("Error initializing PUCCH scrambling sequence\n"); + srslte_pucch_free_rnti(q, rnti); + return SRSLTE_ERROR; } - q->ue_rnti = rnti; - q->users[rnti_idx]->cell_id = q->cell.id; - q->users[rnti_idx]->sequence_generated = true; - } else { - ERROR("Error generating PUSCH sequence: rnti=0x%x already generated\n", rnti); } + + // Save generation states + q->ue_rnti = rnti; + q->users[rnti_idx]->cell_id = q->cell.id; + q->users[rnti_idx]->sequence_generated = true; + return SRSLTE_SUCCESS; } diff --git a/lib/src/phy/phch/pusch.c b/lib/src/phy/phch/pusch.c index 6b0c1dba0..540c85b76 100644 --- a/lib/src/phy/phch/pusch.c +++ b/lib/src/phy/phch/pusch.c @@ -264,36 +264,42 @@ int srslte_pusch_set_cell(srslte_pusch_t* q, srslte_cell_t cell) * For the connection procedure, use srslte_pusch_encode() functions */ int srslte_pusch_set_rnti(srslte_pusch_t* q, uint16_t rnti) { - uint32_t i; - uint32_t rnti_idx = q->is_ue ? 0 : rnti; - if (!q->users[rnti_idx] || q->is_ue) { + // Decide whether re-generating the sequence + if (!q->users[rnti_idx]) { + // If the sequence is not allocated generate + q->users[rnti_idx] = calloc(1, sizeof(srslte_pdsch_user_t)); if (!q->users[rnti_idx]) { - q->users[rnti_idx] = calloc(1, sizeof(srslte_pusch_user_t)); - if (!q->users[rnti_idx]) { - perror("calloc"); - return -1; - } + ERROR("Alocating PDSCH user\n"); + return SRSLTE_ERROR; } - q->users[rnti_idx]->sequence_generated = false; - for (i = 0; i < SRSLTE_NOF_SF_X_FRAME; i++) { - if (srslte_sequence_pusch(&q->users[rnti_idx]->seq[i], - rnti, - 2 * i, - q->cell.id, - q->max_re * srslte_mod_bits_x_symbol(SRSLTE_MOD_64QAM))) { - ERROR("Error initializing PUSCH scrambling sequence\n"); - srslte_pusch_free_rnti(q, rnti); - return SRSLTE_ERROR; - } + } else if (q->users[rnti_idx]->sequence_generated && q->users[rnti_idx]->cell_id == q->cell.id && !q->is_ue) { + // The sequence was generated, cell has not changed and it is eNb, save any efforts + return SRSLTE_SUCCESS; + } + + // Set sequence as not generated + q->users[rnti_idx]->sequence_generated = false; + + // For each subframe + for (int sf_idx = 0; sf_idx < SRSLTE_NOF_SF_X_FRAME; sf_idx++) { + if (srslte_sequence_pusch(&q->users[rnti_idx]->seq[sf_idx], + rnti, + SRSLTE_NOF_SLOTS_PER_SF * sf_idx, + q->cell.id, + q->max_re * srslte_mod_bits_x_symbol(SRSLTE_MOD_64QAM))) { + ERROR("Error initializing PUSCH scrambling sequence\n"); + srslte_pusch_free_rnti(q, rnti); + return SRSLTE_ERROR; } - q->ue_rnti = rnti; - q->users[rnti_idx]->cell_id = q->cell.id; - q->users[rnti_idx]->sequence_generated = true; - } else { - ERROR("Error generating PUSCH sequence: rnti=0x%x already generated\n", rnti); } + + // Save generation states + q->ue_rnti = rnti; + q->users[rnti_idx]->cell_id = q->cell.id; + q->users[rnti_idx]->sequence_generated = true; + return SRSLTE_SUCCESS; } diff --git a/srsenb/hdr/phy/cc_worker.h b/srsenb/hdr/phy/cc_worker.h index 4958eb99e..4d3d60e05 100644 --- a/srsenb/hdr/phy/cc_worker.h +++ b/srsenb/hdr/phy/cc_worker.h @@ -42,7 +42,7 @@ public: cf_t* get_buffer_tx(uint32_t antenna_idx); void set_tti(uint32_t tti); - int add_rnti(uint16_t rnti, bool is_pcell, bool is_temporal); + int add_rnti(uint16_t rnti, bool is_temporal); void rem_rnti(uint16_t rnti); uint32_t get_nof_rnti(); @@ -94,7 +94,7 @@ private: class ue { public: - explicit ue(uint16_t rnti_, bool pcell_) : rnti(rnti_), pcell(pcell_) + explicit ue(uint16_t rnti_) : rnti(rnti_) { // Do nothing } @@ -105,13 +105,11 @@ private: void metrics_read(phy_metrics_t* metrics); void metrics_dl(uint32_t mcs); void metrics_ul(uint32_t mcs, float rssi, float sinr, float turbo_iters); - bool is_pcell() { return pcell; } uint32_t get_rnti() const { return rnti; } private: uint32_t rnti = 0; phy_metrics_t metrics = {}; - bool pcell; }; // Component carrier index diff --git a/srsenb/hdr/phy/phy_ue_db.h b/srsenb/hdr/phy/phy_ue_db.h index 82901555f..3a0f46098 100644 --- a/srsenb/hdr/phy/phy_ue_db.h +++ b/srsenb/hdr/phy/phy_ue_db.h @@ -185,7 +185,7 @@ private: * @param ue_cc_idx UE cell/carrier index that is asserted * @return SRSLTE_SUCCESS if the indicated cell/carrier index is valid, otherwise it returns SRSLTE_ERROR */ - inline int _assert_ue_cc(uint16_t rnti, uint32_t ue_cc_idx); + inline int _assert_ue_cc(uint16_t rnti, uint32_t ue_cc_idx) const; /** * Checks if an RNTI is configured to use an specified UE cell/carrier as PCell or SCell and it is active @@ -193,7 +193,7 @@ private: * @param ue_cc_idx UE cell/carrier index that is asserted * @return SRSLTE_SUCCESS if the indicated cell/carrier is active, otherwise it returns SRSLTE_ERROR */ - inline int _assert_active_ue_cc(uint16_t rnti, uint32_t ue_cc_idx); + inline int _assert_active_ue_cc(uint16_t rnti, uint32_t ue_cc_idx) const; /** * Checks if an RNTI is configured to use an specified eNb cell/carrier as PCell or SCell and it is active @@ -267,6 +267,14 @@ public: */ void activate_deactivate_scell(uint16_t rnti, uint32_t ue_cc_idx, bool activate); + /** + * Asserts a given eNb cell is PCell of the given RNTI + * @param rnti identifier of the UE + * @param enb_cc_idx eNb cell/carrier index + * @return It returns true if it is the primmary cell, othwerwise it returns false + */ + bool is_pcell(uint16_t rnti, uint32_t enb_cc_idx) const; + /** * Get the current down-link physical layer configuration for an RNTI and an eNb cell/carrier * diff --git a/srsenb/hdr/phy/sf_worker.h b/srsenb/hdr/phy/sf_worker.h index 11a6ff37c..8ca54130e 100644 --- a/srsenb/hdr/phy/sf_worker.h +++ b/srsenb/hdr/phy/sf_worker.h @@ -41,7 +41,7 @@ public: cf_t* get_buffer_rx(uint32_t cc_idx, uint32_t antenna_idx); void set_time(uint32_t tti, uint32_t tx_worker_cnt, srslte_timestamp_t tx_time); - int add_rnti(uint16_t rnti, uint32_t cc_idx, bool is_pcell, bool is_temporal); + int add_rnti(uint16_t rnti, uint32_t cc_idx, bool is_temporal); void rem_rnti(uint16_t rnti); uint32_t get_nof_rnti(); diff --git a/srsenb/src/phy/cc_worker.cc b/srsenb/src/phy/cc_worker.cc index a6b9d51d9..599935e9c 100644 --- a/srsenb/src/phy/cc_worker.cc +++ b/srsenb/src/phy/cc_worker.cc @@ -127,14 +127,14 @@ void cc_worker::init(phy_common* phy_, srslte::log* log_h_, uint32_t cc_idx_) } /* Setup SI-RNTI in PHY */ - add_rnti(SRSLTE_SIRNTI, false, false); + add_rnti(SRSLTE_SIRNTI, false); /* Setup P-RNTI in PHY */ - add_rnti(SRSLTE_PRNTI, false, false); + add_rnti(SRSLTE_PRNTI, false); /* Setup RA-RNTI in PHY */ for (int i = SRSLTE_RARNTI_START; i <= SRSLTE_RARNTI_END; i++) { - add_rnti(i, false, false); + add_rnti(i, false); } if (srslte_softbuffer_tx_init(&temp_mbsfn_softbuffer, nof_prb)) { @@ -180,7 +180,7 @@ void cc_worker::set_tti(uint32_t tti_) tti_tx_ul = TTI_RX_ACK(tti_rx); } -int cc_worker::add_rnti(uint16_t rnti, bool is_pcell, bool is_temporal) +int cc_worker::add_rnti(uint16_t rnti, bool is_temporal) { if (not is_temporal) { @@ -194,8 +194,8 @@ int cc_worker::add_rnti(uint16_t rnti, bool is_pcell, bool is_temporal) mutex.lock(); // Create user unless already exists - if (!ue_db.count(rnti)) { - ue_db[rnti] = new ue(rnti, is_pcell); + if (ue_db.count(rnti) == 0) { + ue_db[rnti] = new ue(rnti); } mutex.unlock(); @@ -377,7 +377,7 @@ int cc_worker::decode_pucch() uint16_t rnti = iter.first; // If it's a User RNTI and doesn't have PUSCH grant in this TTI - if (SRSLTE_RNTI_ISUSER(rnti) && !ue_db[rnti]->is_grant_available && ue_db[rnti]->is_pcell()) { + if (SRSLTE_RNTI_ISUSER(rnti) and not ue_db[rnti]->is_grant_available and phy->ue_db.is_pcell(rnti, cc_idx)) { srslte_ul_cfg_t ul_cfg = phy->ue_db.get_ul_config(rnti, cc_idx); // Check if user needs to receive PUCCH diff --git a/srsenb/src/phy/phy.cc b/srsenb/src/phy/phy.cc index 06148550d..30165dc8a 100644 --- a/srsenb/src/phy/phy.cc +++ b/srsenb/src/phy/phy.cc @@ -161,7 +161,7 @@ int phy::add_rnti(uint16_t rnti, uint32_t pcell_index, bool is_temporal) } for (uint32_t i = 0; i < nof_workers; i++) { - if (workers[i].add_rnti(rnti, pcell_index, true, is_temporal)) { + if (workers[i].add_rnti(rnti, pcell_index, is_temporal) != SRSLTE_SUCCESS) { return SRSLTE_ERROR; } } @@ -234,14 +234,14 @@ void phy::set_config_dedicated(uint16_t rnti, const phy_rrc_dedicated_list_t& de workers_common.ue_db.addmod_rnti(rnti, dedicated_list); // Iterate over the list and add the RNTIs - for (uint32_t scell_idx = 0; scell_idx < dedicated_list.size(); scell_idx++) { - auto& config = dedicated_list[scell_idx]; - - // Configure only if active, ignore otherwise - if (scell_idx != 0 && config.configured) { - // Add RNTI to workers + for (const phy_rrc_dedicated_t& config : dedicated_list) { + // Add RNTI to eNb cell/carrier. + // - Do not ignore PCell, it could have changed + // - Do not remove RNTI from unused workers, it will be removed when the UE is released + if (config.configured) { + // Add RNTI to all SF workers for (uint32_t w = 0; w < nof_workers; w++) { - workers[w].add_rnti(rnti, config.enb_cc_idx, false, false); + workers[w].add_rnti(rnti, config.enb_cc_idx, false); } } } diff --git a/srsenb/src/phy/phy_ue_db.cc b/srsenb/src/phy/phy_ue_db.cc index cfaaae7c4..8ccfa639c 100644 --- a/srsenb/src/phy/phy_ue_db.cc +++ b/srsenb/src/phy/phy_ue_db.cc @@ -138,7 +138,6 @@ inline int phy_ue_db::_assert_enb_cc(uint16_t rnti, uint32_t enb_cc_idx) const // Check Component Carrier is part of UE SCell map if (_get_ue_cc_idx(rnti, enb_cc_idx) == SRSLTE_MAX_CARRIERS) { - ERROR("Trying to access cell/carrier index %d in RNTI 0x%X. It does not exist.\n", enb_cc_idx, rnti); return SRSLTE_ERROR; } @@ -160,7 +159,7 @@ inline int phy_ue_db::_assert_enb_pcell(uint16_t rnti, uint32_t enb_cc_idx) cons return SRSLTE_SUCCESS; } -inline int phy_ue_db::_assert_ue_cc(uint16_t rnti, uint32_t ue_cc_idx) +inline int phy_ue_db::_assert_ue_cc(uint16_t rnti, uint32_t ue_cc_idx) const { if (_assert_rnti(rnti) != SRSLTE_SUCCESS) { return SRSLTE_ERROR; @@ -175,14 +174,14 @@ inline int phy_ue_db::_assert_ue_cc(uint16_t rnti, uint32_t ue_cc_idx) return SRSLTE_SUCCESS; } -inline int phy_ue_db::_assert_active_ue_cc(uint16_t rnti, uint32_t ue_cc_idx) +inline int phy_ue_db::_assert_active_ue_cc(uint16_t rnti, uint32_t ue_cc_idx) const { if (_assert_ue_cc(rnti, ue_cc_idx) != SRSLTE_SUCCESS) { return SRSLTE_ERROR; } // Return error if not PCell or not Active SCell - auto& cell_info = ue_db.at(rnti).cell_info[ue_cc_idx]; + const cell_info_t& cell_info = ue_db.at(rnti).cell_info[ue_cc_idx]; if (cell_info.state != cell_state_primary and cell_info.state != cell_state_secondary_active) { ERROR("Failed to assert active UE cell/carrier %d for RNTI 0x%X", ue_cc_idx, rnti); return SRSLTE_ERROR; @@ -198,7 +197,7 @@ inline int phy_ue_db::_assert_active_enb_cc(uint16_t rnti, uint32_t enb_cc_idx) } // Check SCell is active, ignore PCell state - auto& cell_info = ue_db.at(rnti).cell_info[_get_ue_cc_idx(rnti, enb_cc_idx)]; + const cell_info_t& cell_info = ue_db.at(rnti).cell_info[_get_ue_cc_idx(rnti, enb_cc_idx)]; if (cell_info.state != cell_state_primary and cell_info.state != cell_state_secondary_active) { ERROR("Failed to assert active eNb cell/carrier %d for RNTI 0x%X", enb_cc_idx, rnti); return SRSLTE_ERROR; @@ -209,7 +208,7 @@ inline int phy_ue_db::_assert_active_enb_cc(uint16_t rnti, uint32_t enb_cc_idx) inline int phy_ue_db::_assert_stack() const { - if (not stack) { + if (stack == nullptr) { return SRSLTE_ERROR; } @@ -218,7 +217,7 @@ inline int phy_ue_db::_assert_stack() const inline int phy_ue_db::_assert_cell_list_cfg() const { - if (not cell_cfg_list) { + if (cell_cfg_list == nullptr) { return SRSLTE_ERROR; } @@ -238,8 +237,9 @@ inline srslte::phy_cfg_t phy_ue_db::_get_rnti_config(uint16_t rnti, uint32_t enb return default_cfg; } - // Make sure the C-RNTI exists and the cell is active for the user - if (_assert_active_enb_cc(rnti, enb_cc_idx) != SRSLTE_SUCCESS) { + // Make sure the C-RNTI exists and the cell/carrier is configured + if (_assert_enb_cc(rnti, enb_cc_idx) != SRSLTE_SUCCESS) { + ERROR("Trying to access cell/carrier %d in RNTI 0x%X. It is not active.\n", enb_cc_idx, rnti); return default_cfg; } @@ -251,7 +251,7 @@ inline srslte::phy_cfg_t phy_ue_db::_get_rnti_config(uint16_t rnti, uint32_t enb } // Otherwise return current configuration - return ue_db.at(rnti).cell_info[ue_cc_idx].phy_cfg; + return ue_db.at(rnti).cell_info.at(ue_cc_idx).phy_cfg; } void phy_ue_db::clear_tti_pending_ack(uint32_t tti) @@ -270,40 +270,42 @@ void phy_ue_db::addmod_rnti(uint16_t std::lock_guard lock(mutex); // Create new user if did not exist - if (!ue_db.count(rnti)) { + if (ue_db.count(rnti) == 0) { _add_rnti(rnti); } // Get UE by reference common_ue& ue = ue_db[rnti]; - // Number of configured serving cells + // Number of configured secondary serving cells uint32_t nof_configured_scell = 0; // Iterate PHY RRC configuration for each UE cell/carrier - for (uint32_t ue_cc_idx = 0; ue_cc_idx < phy_rrc_dedicated_list.size() && ue_cc_idx < SRSLTE_MAX_CARRIERS; - ue_cc_idx++) { - auto& phy_rrc_dedicated = phy_rrc_dedicated_list[ue_cc_idx]; + uint32_t nof_cc = SRSLTE_MIN(phy_rrc_dedicated_list.size(), SRSLTE_MAX_CARRIERS); + for (uint32_t ue_cc_idx = 0; ue_cc_idx < nof_cc; ue_cc_idx++) { + const phy_interface_rrc_lte::phy_rrc_dedicated_t& phy_rrc_dedicated = phy_rrc_dedicated_list[ue_cc_idx]; + // Configured, add/modify entry in the cell_info map cell_info_t& cell_info = ue.cell_info[ue_cc_idx]; - if (phy_rrc_dedicated.configured or cell_info.state == cell_state_primary) { - // Set cell information - cell_info.enb_cc_idx = phy_rrc_dedicated.enb_cc_idx; - - // Apply PCell configuration is stash - if (cell_info.state == cell_state_primary) { - ue.pcell_cfg_stash = phy_rrc_dedicated.phy_cfg; - _set_common_config_rnti(rnti, ue.pcell_cfg_stash); - } else { - ue.cell_info[ue_cc_idx].phy_cfg = phy_rrc_dedicated.phy_cfg; - _set_common_config_rnti(rnti, ue.cell_info[ue_cc_idx].phy_cfg); + // Configure PHY + if (cell_info.state == cell_state_primary) { + // If primary serving cell's eNb cell/carrier index changed, it applies default current config + if (cell_info.enb_cc_idx != phy_rrc_dedicated.enb_cc_idx) { + cell_info.phy_cfg.set_defaults(); + _set_common_config_rnti(rnti, cell_info.phy_cfg); } - // Set Cell state, all inactive by default except PCell - if (cell_info.state != cell_state_primary) { - cell_info.state = cell_state_secondary_inactive; - } + // Apply primmary serving cell configuration in stash + ue.pcell_cfg_stash = phy_rrc_dedicated.phy_cfg; + _set_common_config_rnti(rnti, ue.pcell_cfg_stash); + } else if (phy_rrc_dedicated.configured) { + // + cell_info.phy_cfg = phy_rrc_dedicated.phy_cfg; + _set_common_config_rnti(rnti, cell_info.phy_cfg); + + // Set Cell state, all inactive by default + cell_info.state = cell_state_secondary_inactive; // Count Serving cell nof_configured_scell++; @@ -311,34 +313,38 @@ void phy_ue_db::addmod_rnti(uint16_t // Cell without configuration (except PCell) cell_info.state = cell_state_none; } + + // Set serving cell index + cell_info.enb_cc_idx = phy_rrc_dedicated.enb_cc_idx; } - // Make sure remaining cells are set to none - for (uint32_t cell_idx = phy_rrc_dedicated_list.size(); cell_idx < SRSLTE_MAX_CARRIERS; cell_idx++) { - ue.cell_info[cell_idx].state = cell_state_none; + // Disable the rest of potential serving cells + for (uint32_t i = nof_cc; i < SRSLTE_MAX_CARRIERS; i++) { + ue.cell_info[i].state = cell_state_none; } // Enable/Disable extended CSI field in DCI according to 3GPP 36.212 R10 5.3.3.1.1 Format 0 - for (uint32_t ue_cc_idx = 0; ue_cc_idx < SRSLTE_MAX_CARRIERS; ue_cc_idx++) { - if (ue.cell_info[ue_cc_idx].state == cell_state_secondary_inactive || - ue.cell_info[ue_cc_idx].state == cell_state_secondary_active) { - ue.cell_info[ue_cc_idx].phy_cfg.dl_cfg.dci.multiple_csi_request_enabled = (nof_configured_scell > 1); - } else if (ue.cell_info[ue_cc_idx].state == cell_state_primary) { - ue.pcell_cfg_stash.dl_cfg.dci.multiple_csi_request_enabled = (nof_configured_scell > 1); + for (uint32_t ue_cc_idx = 0; ue_cc_idx < nof_cc; ue_cc_idx++) { + if (ue.cell_info[ue_cc_idx].state == cell_state_primary) { + // The primary cell applies changes in the stashed config + ue.pcell_cfg_stash.dl_cfg.dci.multiple_csi_request_enabled = (nof_configured_scell > 0); + } else { + // The rest apply changes directly + ue.cell_info[ue_cc_idx].phy_cfg.dl_cfg.dci.multiple_csi_request_enabled = (nof_configured_scell > 0); } } // Copy necessary PCell configuration for receiving Configuration Completion from UE srslte::phy_cfg_t& pcell_cfg = ue.cell_info[0].phy_cfg; - // Setup Temporal PUCCH configuration + // Setup temporal PUCCH configuration srslte_pucch_cfg_t tmp_pucch_cfg = ue.pcell_cfg_stash.ul_cfg.pucch; tmp_pucch_cfg.N_pucch_1 = pcell_cfg.ul_cfg.pucch.N_pucch_1; ///< Used for ACK // Load new UL configuration pcell_cfg.ul_cfg = ue.pcell_cfg_stash.ul_cfg; - // Overwrite PUCCH with tenporal PUCCH + // Overwrite PUCCH with temporal PUCCH pcell_cfg.ul_cfg.pucch = tmp_pucch_cfg; } @@ -346,7 +352,7 @@ void phy_ue_db::rem_rnti(uint16_t rnti) { std::lock_guard lock(mutex); - if (ue_db.count(rnti)) { + if (ue_db.count(rnti) != 0) { ue_db.erase(rnti); } } @@ -366,6 +372,8 @@ void phy_ue_db::complete_config(uint16_t rnti) void phy_ue_db::activate_deactivate_scell(uint16_t rnti, uint32_t ue_cc_idx, bool activate) { + std::lock_guard lock(mutex); + // Assert RNTI and SCell are valid if (_assert_ue_cc(rnti, ue_cc_idx) != SRSLTE_SUCCESS) { return; @@ -383,6 +391,12 @@ void phy_ue_db::activate_deactivate_scell(uint16_t rnti, uint32_t ue_cc_idx, boo cell_info.state = (activate) ? cell_state_secondary_active : cell_state_secondary_inactive; } +bool phy_ue_db::is_pcell(uint16_t rnti, uint32_t enb_cc_idx) const +{ + std::lock_guard lock(mutex); + return _assert_enb_pcell(rnti, enb_cc_idx) == SRSLTE_SUCCESS; +} + srslte_dl_cfg_t phy_ue_db::get_dl_config(uint16_t rnti, uint32_t enb_cc_idx) const { std::lock_guard lock(mutex); @@ -481,6 +495,7 @@ bool phy_ue_db::fill_uci_cfg(uint32_t tti, const cell_info_t& cell_info = ue.cell_info[cell_idx]; const srslte_dl_cfg_t& dl_cfg = cell_info.phy_cfg.dl_cfg; + // Check report for primary and active cells if (cell_info.state == cell_state_primary or cell_info.state == cell_state_secondary_active) { const srslte_cell_t& cell = cell_cfg_list->at(cell_info.enb_cc_idx).cell; @@ -544,13 +559,13 @@ void phy_ue_db::send_uci_data(uint32_t tti, srslte_enb_dl_get_ack(&cell_cfg_list->at(ue.cell_info[0].enb_cc_idx).cell, &uci_cfg, &uci_value, &pdsch_ack); // Iterate over the ACK information - for (uint32_t scell_idx = 0; scell_idx < SRSLTE_MAX_CARRIERS; scell_idx++) { - const srslte_pdsch_ack_cc_t& pdsch_ack_cc = pdsch_ack.cc[scell_idx]; + for (uint32_t ue_cc_idx = 0; ue_cc_idx < SRSLTE_MAX_CARRIERS; ue_cc_idx++) { + const srslte_pdsch_ack_cc_t& pdsch_ack_cc = pdsch_ack.cc[ue_cc_idx]; for (uint32_t m = 0; m < pdsch_ack_cc.M; m++) { if (pdsch_ack_cc.m[m].present) { for (uint32_t tb = 0; tb < SRSLTE_MAX_CODEWORDS; tb++) { if (pdsch_ack_cc.m[m].value[tb] != 2) { - stack->ack_info(tti, rnti, ue.cell_info[scell_idx].enb_cc_idx, tb, pdsch_ack_cc.m[m].value[tb] == 1); + stack->ack_info(tti, rnti, ue.cell_info[ue_cc_idx].enb_cc_idx, tb, pdsch_ack_cc.m[m].value[tb] == 1); } } } @@ -561,8 +576,8 @@ void phy_ue_db::send_uci_data(uint32_t tti, _assert_active_ue_cc(rnti, uci_cfg.cqi.scell_index); // Get CQI carrier index - auto& cqi_scell_info = ue_db.at(rnti).cell_info[uci_cfg.cqi.scell_index]; - uint32_t cqi_cc_idx = cqi_scell_info.enb_cc_idx; + cell_info_t& cqi_scell_info = ue_db.at(rnti).cell_info[uci_cfg.cqi.scell_index]; + uint32_t cqi_cc_idx = cqi_scell_info.enb_cc_idx; // Notify CQI only if CRC is valid if (uci_value.cqi.data_crc) { diff --git a/srsenb/src/phy/sf_worker.cc b/srsenb/src/phy/sf_worker.cc index 7378e0c3a..98ba0769c 100644 --- a/srsenb/src/phy/sf_worker.cc +++ b/srsenb/src/phy/sf_worker.cc @@ -129,12 +129,12 @@ void sf_worker::set_time(uint32_t tti_, uint32_t tx_worker_cnt_, srslte_timestam } } -int sf_worker::add_rnti(uint16_t rnti, uint32_t cc_idx, bool is_pcell, bool is_temporal) +int sf_worker::add_rnti(uint16_t rnti, uint32_t cc_idx, bool is_temporal) { int ret = SRSLTE_ERROR; if (cc_idx < cc_workers.size()) { - cc_workers[cc_idx]->add_rnti(rnti, is_pcell, is_temporal); + cc_workers[cc_idx]->add_rnti(rnti, is_temporal); ret = SRSLTE_SUCCESS; } diff --git a/srsenb/test/phy/CMakeLists.txt b/srsenb/test/phy/CMakeLists.txt index 0a42d4631..0d755cdcc 100644 --- a/srsenb/test/phy/CMakeLists.txt +++ b/srsenb/test/phy/CMakeLists.txt @@ -94,3 +94,11 @@ add_test(enb_phy_test_tm1_ca_cs enb_phy_test --duration=${ENB_PHY_TEST_DURATION} # - 6 PRB # - PUCCH format 1b with Channel selection ACK/NACK feedback mode add_test(enb_phy_test_tm4_ca_cs enb_phy_test --duration=${ENB_PHY_TEST_DURATION} --nof_enb_cells=6 --ue_cell_list=1,5 --ack_mode=cs --cell.nof_prb=6 --tm=4) + +# Two carrier aggregation using Channel Selection and HO: +# - 3 eNb cell/carrier +# - Transmission Mode 1 +# - 2 Aggregated carriers +# - 100 PRB +# - PUCCH format 1b with Channel selection ACK/NACK feedback mode +add_test(enb_phy_test_tm1_ca_cs_ho enb_phy_test --duration=1000 --nof_enb_cells=3 --ue_cell_list=2,0 --ack_mode=cs --cell.nof_prb=100 --tm=1 --rotation=100) diff --git a/srsenb/test/phy/enb_phy_test.cc b/srsenb/test/phy/enb_phy_test.cc index 132e20fd6..60af5898c 100644 --- a/srsenb/test/phy/enb_phy_test.cc +++ b/srsenb/test/phy/enb_phy_test.cc @@ -56,6 +56,7 @@ public: } \ \ bool get_received_##NAME() { return received_##NAME; } \ + void clear_##NAME() { received_##NAME = false; } \ \ private: \ void notify_##NAME() \ @@ -326,18 +327,16 @@ public: explicit dummy_stack(const srsenb::phy_cfg_t& phy_cfg_, const srsenb::phy_interface_rrc_lte::phy_rrc_dedicated_list_t& phy_rrc_, const std::string& log_level, - uint16_t rnti_, - std::vector& active_cell_list_) : + uint16_t rnti_) : log_h("STACK"), ue_rnti(rnti_), random_gen(srslte_random_init(rnti_)), phy_cell_cfg(phy_cfg_.phy_cell_cfg), - phy_rrc(phy_rrc_), - active_cell_list(active_cell_list_) + phy_rrc(phy_rrc_) { log_h.set_level(log_level); srslte_softbuffer_tx_init(&softbuffer_tx, SRSLTE_MAX_PRB); - for (uint32_t i = 0; i < active_cell_list.size(); i++) { + for (uint32_t i = 0; i < phy_rrc.size(); i++) { for (auto& sb : softbuffer_rx[i]) { srslte_softbuffer_rx_init(&sb, SRSLTE_MAX_PRB); } @@ -399,6 +398,8 @@ public: srslte_random_free(random_gen); } + void set_active_cell_list(std::vector& active_cell_list_) { active_cell_list = active_cell_list_; } + int sr_detected(uint32_t tti, uint16_t rnti) override { tti_sr_info_t tti_sr_info = {}; @@ -674,7 +675,7 @@ public: void rl_failure(uint16_t rnti) override { notify_rl_failure(); } void rl_ok(uint16_t rnti) override { notify_rl_ok(); } void tti_clock() override { notify_tti_clock(); } - int run_tti() + int run_tti(bool enable_assert) { // Check DL ACKs match with grants while (not tti_dl_info_ack_queue.empty()) { @@ -686,11 +687,12 @@ public: tti_dl_sched.tti = TTI_ADD(tti_dl_sched.tti, FDD_HARQ_DELAY_DL_MS); // Assert that ACKs have been received - TESTASSERT(tti_dl_sched.tti == tti_dl_ack.tti); - TESTASSERT(tti_dl_sched.cc_idx == tti_dl_ack.cc_idx); - TESTASSERT(tti_dl_sched.tb_idx == tti_dl_ack.tb_idx); - TESTASSERT(tti_dl_sched.ack == tti_dl_ack.ack); - + if (enable_assert) { + TESTASSERT(tti_dl_sched.tti == tti_dl_ack.tti); + TESTASSERT(tti_dl_sched.cc_idx == tti_dl_ack.cc_idx); + TESTASSERT(tti_dl_sched.tb_idx == tti_dl_ack.tb_idx); + TESTASSERT(tti_dl_sched.ack == tti_dl_ack.ack); + } tti_dl_info_sched_queue.pop(); tti_dl_info_ack_queue.pop(); } @@ -702,34 +704,39 @@ public: tti_ul_info_t& tti_ul_ack = tti_ul_info_ack_queue.front(); // Assert that ACKs have been received - TESTASSERT(tti_ul_sched.tti == tti_ul_ack.tti); - TESTASSERT(tti_ul_sched.cc_idx == tti_ul_ack.cc_idx); - TESTASSERT(tti_ul_sched.crc == tti_ul_ack.crc); + if (enable_assert) { + TESTASSERT(tti_ul_sched.tti == tti_ul_ack.tti); + TESTASSERT(tti_ul_sched.cc_idx == tti_ul_ack.cc_idx); + TESTASSERT(tti_ul_sched.crc == tti_ul_ack.crc); + } tti_ul_info_sched_queue.pop(); tti_ul_info_ack_queue.pop(); } // Check SR match with TTI - while (tti_sr_info_queue.size() > 1) { + size_t req_queue_size = (enable_assert) ? 1 : 0; + while (tti_sr_info_queue.size() > req_queue_size) { tti_sr_info_t tti_sr_info1 = tti_sr_info_queue.front(); // POP first from queue tti_sr_info_queue.pop(); - // Get second, do not pop - tti_sr_info_t& tti_sr_info2 = tti_sr_info_queue.front(); + if (enable_assert) { + // Get second, do not pop + tti_sr_info_t& tti_sr_info2 = tti_sr_info_queue.front(); - uint32_t elapsed_tti = TTI_SUB(tti_sr_info2.tti, tti_sr_info1.tti); + uint32_t elapsed_tti = TTI_SUB(tti_sr_info2.tti, tti_sr_info1.tti); - // Log SR info - log_h.info("SR: tti1=%d; tti2=%d; elapsed %d;\n", tti_sr_info1.tti, tti_sr_info2.tti, elapsed_tti); + // Log SR info + log_h.info("SR: tti1=%d; tti2=%d; elapsed %d;\n", tti_sr_info1.tti, tti_sr_info2.tti, elapsed_tti); - // Check first TTI - TESTASSERT(tti_sr_info1.tti % 20 == 0); + // Check first TTI + TESTASSERT(tti_sr_info1.tti % 20 == 0); - // Make sure the TTI difference is 20 - TESTASSERT(elapsed_tti == 20); + // Make sure the TTI difference is 20 + TESTASSERT(elapsed_tti == 20); + } } return SRSLTE_SUCCESS; @@ -757,25 +764,15 @@ private: std::map last_ri = {}; public: - dummy_ue(dummy_radio* _radio, - const srsenb::phy_cell_cfg_list_t& cell_list, - std::string log_level, - uint16_t rnti_, - const srsenb::phy_interface_rrc_lte::phy_rrc_dedicated_list_t& phy_rrc_cfg_) : + dummy_ue(dummy_radio* _radio, const srsenb::phy_cell_cfg_list_t& cell_list, std::string log_level, uint16_t rnti_) : radio(_radio), - log_h("UE PHY", nullptr, true), - phy_rrc_cfg(phy_rrc_cfg_) + log_h("UPHY", nullptr, true) { // Calculate subframe length nof_ports = cell_list[0].cell.nof_ports; sf_len = static_cast(SRSLTE_SF_LEN_PRB(cell_list[0].cell.nof_prb)); rnti = rnti_; - // Enable Extended CSI request bits in DCI format 0 according to 3GPP 36.212 R10 5.3.3.1.1 - for (auto& e : phy_rrc_cfg) { - e.phy_cfg.dl_cfg.dci.multiple_csi_request_enabled = (phy_rrc_cfg.size() > 1); - } - log_h.set_level(std::move(log_level)); // Initialise one buffer per eNb @@ -791,8 +788,9 @@ public: srslte_vec_cf_zero(buffer, sf_len); } - for (auto& q : phy_rrc_cfg) { - uint32_t cc_idx = q.enb_cc_idx; + // Iterate over all cells + for (uint32_t cc_idx = 0; cc_idx < (uint32_t)cell_list.size(); cc_idx++) { + const srslte_cell_t& cell = cell_list[cc_idx].cell; // Allocate UE DL auto* ue_dl = (srslte_ue_dl_t*)srslte_vec_malloc(sizeof(srslte_ue_dl_t)); @@ -802,13 +800,12 @@ public: ue_dl_v.push_back(ue_dl); // Initialise UE DL - if (srslte_ue_dl_init( - ue_dl, &buffers[cc_idx * nof_ports], cell_list[cc_idx].cell.nof_prb, cell_list[cc_idx].cell.nof_ports)) { + if (srslte_ue_dl_init(ue_dl, &buffers[cc_idx * nof_ports], cell.nof_prb, cell.nof_ports)) { ERROR("Initiating UE DL\n"); } // Set Cell - if (srslte_ue_dl_set_cell(ue_dl, cell_list[cc_idx].cell)) { + if (srslte_ue_dl_set_cell(ue_dl, cell)) { ERROR("Setting UE DL cell\n"); } @@ -823,12 +820,12 @@ public: ue_ul_v.push_back(ue_ul); // Initialise UE UL - if (srslte_ue_ul_init(ue_ul, buffers[cc_idx * nof_ports], cell_list[cc_idx].cell.nof_prb)) { + if (srslte_ue_ul_init(ue_ul, buffers[cc_idx * nof_ports], cell.nof_prb)) { ERROR("Setting UE UL cell\n"); } // Set cell - if (srslte_ue_ul_set_cell(ue_ul, cell_list[cc_idx].cell)) { + if (srslte_ue_ul_set_cell(ue_ul, cell)) { ERROR("Setting UE DL cell\n"); } @@ -886,6 +883,17 @@ public: srslte_softbuffer_tx_free(&softbuffer_tx); } + void reconfigure(const srsenb::phy_interface_rrc_lte::phy_rrc_dedicated_list_t& phy_rrc_cfg_) + { + // Copy new configuration + phy_rrc_cfg = phy_rrc_cfg_; + + // Enable Extended CSI request bits in DCI format 0 according to 3GPP 36.212 R10 5.3.3.1.1 + for (auto& e : phy_rrc_cfg) { + e.phy_cfg.dl_cfg.dci.multiple_csi_request_enabled = (phy_rrc_cfg.size() > 1); + } + } + int work_dl(srslte_pdsch_ack_t& pdsch_ack, srslte_uci_data_t& uci_data) { // Read DL @@ -917,10 +925,10 @@ public: ue_dl_cfg.last_ri = last_ri[i]; } - srslte_ue_dl_decode_fft_estimate(ue_dl_v[i], &sf_dl_cfg, &ue_dl_cfg); + srslte_ue_dl_decode_fft_estimate(ue_dl_v[cc_idx], &sf_dl_cfg, &ue_dl_cfg); // Get DL Grants - int nof_dl_grants = srslte_ue_dl_find_dl_dci(ue_dl_v[i], &sf_dl_cfg, &ue_dl_cfg, rnti, dci_dl); + int nof_dl_grants = srslte_ue_dl_find_dl_dci(ue_dl_v[cc_idx], &sf_dl_cfg, &ue_dl_cfg, rnti, dci_dl); TESTASSERT(nof_dl_grants >= SRSLTE_SUCCESS); // Generate ACKs @@ -929,7 +937,8 @@ public: srslte_dci_dl_info(dci_dl, str, sizeof(str)); log_h.info("[DL DCI] %s\n", str); - if (srslte_ue_dl_dci_to_pdsch_grant(ue_dl_v[i], &sf_dl_cfg, &ue_dl_cfg, dci_dl, &ue_dl_cfg.cfg.pdsch.grant)) { + if (srslte_ue_dl_dci_to_pdsch_grant( + ue_dl_v[cc_idx], &sf_dl_cfg, &ue_dl_cfg, dci_dl, &ue_dl_cfg.cfg.pdsch.grant)) { log_h.error("Converting DCI message to DL dci\n"); return SRSLTE_ERROR; } @@ -958,9 +967,9 @@ public: } // Generate CQI periodic if required - srslte_ue_dl_gen_cqi_periodic(ue_dl_v[i], &ue_dl_cfg, 0x0f, sf_ul_cfg.tti, &uci_data); + srslte_ue_dl_gen_cqi_periodic(ue_dl_v[cc_idx], &ue_dl_cfg, 0x0f, sf_ul_cfg.tti, &uci_data); - if (srslte_cqi_periodic_ri_send(&ue_dl_cfg.cfg.cqi_report, sf_ul_cfg.tti, ue_dl_v[i]->cell.frame_type) && + if (srslte_cqi_periodic_ri_send(&ue_dl_cfg.cfg.cqi_report, sf_ul_cfg.tti, ue_dl_v[cc_idx]->cell.frame_type) && uci_data.cfg.cqi.ri_len) { uci_data.cfg.cqi.scell_index = i; } @@ -979,6 +988,7 @@ public: for (uint32_t i = 0; i < phy_rrc_cfg.size(); i++) { srslte_dci_ul_t dci_ul[SRSLTE_MAX_DCI_MSG] = {}; srslte::phy_cfg_t& dedicated = phy_rrc_cfg[i].phy_cfg; + uint32_t cc_idx = phy_rrc_cfg[i].enb_cc_idx; srslte_ue_ul_cfg_t ue_ul_cfg = {}; ue_ul_cfg.ul_cfg = dedicated.ul_cfg; @@ -993,7 +1003,7 @@ public: ue_dl_cfg.cfg.pdsch.rnti = rnti; // Get UL grants - int nof_ul_grants = srslte_ue_dl_find_ul_dci(ue_dl_v[i], &sf_dl_cfg, &ue_dl_cfg, rnti, dci_ul); + int nof_ul_grants = srslte_ue_dl_find_ul_dci(ue_dl_v[cc_idx], &sf_dl_cfg, &ue_dl_cfg, rnti, dci_ul); TESTASSERT(nof_ul_grants >= SRSLTE_SUCCESS); srslte_pusch_data_t pusch_data = {}; @@ -1001,7 +1011,8 @@ public: if (nof_ul_grants > SRSLTE_SUCCESS) { TESTASSERT(srslte_ue_ul_dci_to_pusch_grant( - ue_ul_v[i], &sf_ul_cfg, &ue_ul_cfg, dci_ul, &ue_ul_cfg.ul_cfg.pusch.grant) >= SRSLTE_SUCCESS); + ue_ul_v[cc_idx], &sf_ul_cfg, &ue_ul_cfg, dci_ul, &ue_ul_cfg.ul_cfg.pusch.grant) >= + SRSLTE_SUCCESS); srslte_softbuffer_tx_reset(&softbuffer_tx); @@ -1016,7 +1027,7 @@ public: srslte_ue_ul_gen_sr(&ue_ul_cfg, &sf_ul_cfg, &uci_data, (bool)(sf_ul_cfg.tti % 20 == 0)); // Generate Acknowledgements - srslte_ue_dl_gen_ack(&ue_dl_v[i]->cell, &sf_dl_cfg, &pdsch_ack, &uci_data); + srslte_ue_dl_gen_ack(&ue_dl_v[cc_idx]->cell, &sf_dl_cfg, &pdsch_ack, &uci_data); if (uci_data.cfg.cqi.ri_len) { last_ri[uci_data.cfg.cqi.scell_index] = uci_data.value.ri; @@ -1031,7 +1042,7 @@ public: } // Work UL - TESTASSERT(srslte_ue_ul_encode(ue_ul_v[i], &sf_ul_cfg, &ue_ul_cfg, &pusch_data) >= SRSLTE_SUCCESS); + TESTASSERT(srslte_ue_ul_encode(ue_ul_v[cc_idx], &sf_ul_cfg, &ue_ul_cfg, &pusch_data) >= SRSLTE_SUCCESS); char str[256] = {}; srslte_ue_ul_info(&ue_ul_cfg, &sf_ul_cfg, &pusch_data.uci, str, sizeof(str)); @@ -1076,16 +1087,17 @@ class phy_test_bench { public: struct args_t { - uint16_t rnti = 0x1234; - uint32_t duration = 10240; - uint32_t nof_enb_cells = 1; - srslte_cell_t cell = {}; - std::string ue_cell_list_str = "0"; ///< First indicates PCell - std::vector ue_cell_list = {0}; - std::string ack_mode = "normal"; - std::string log_level = "none"; - uint32_t tm_u32 = 1; - srslte_tm_t tm = SRSLTE_TM1; + uint16_t rnti = 0x1234; + uint32_t duration = 10240; + uint32_t nof_enb_cells = 1; + srslte_cell_t cell = {}; + std::string ue_cell_list_str = "0"; ///< First indicates PCell + std::vector ue_cell_list = {0}; + std::string ack_mode = "normal"; + std::string log_level = "none"; + uint32_t tm_u32 = 1; + uint32_t period_pcell_rotate = 0; + srslte_tm_t tm = SRSLTE_TM1; args_t() { cell.nof_prb = 6; @@ -1134,6 +1146,14 @@ private: srsenb::phy_cfg_t phy_cfg; ///< eNb Cell/Carrier configuration srsenb::phy_interface_rrc_lte::phy_rrc_dedicated_list_t phy_rrc_cfg; ///< UE PHY configuration + uint64_t tti_counter = 0; + typedef enum { + change_state_assert = 0, + change_state_flush, + change_state_wait_steady, + } change_state_t; + change_state_t change_state = change_state_assert; + public: explicit phy_test_bench(args_t& args_) : log_h("TEST BENCH") { @@ -1227,21 +1247,23 @@ public: new dummy_radio(args.nof_enb_cells * args.cell.nof_ports, args.cell.nof_prb, args.log_level)); /// Create Dummy Stack isntance - stack = unique_dummy_stack_t(new dummy_stack(phy_cfg, phy_rrc_cfg, args.log_level, args.rnti, args.ue_cell_list)); + stack = unique_dummy_stack_t(new dummy_stack(phy_cfg, phy_rrc_cfg, args.log_level, args.rnti)); + stack->set_active_cell_list(args.ue_cell_list); /// eNb PHY initialisation instance enb_phy = unique_srsenb_phy_t(new srsenb::phy(&logger_stdout)); /// Initiate eNb PHY with the given RNTI enb_phy->init(phy_args, phy_cfg, radio.get(), stack.get()); - enb_phy->add_rnti(args.rnti, args.ue_cell_list[0], false); enb_phy->set_config_dedicated(args.rnti, phy_rrc_cfg); enb_phy->complete_config_dedicated(args.rnti); enb_phy->set_activation_deactivation_scell(args.rnti, activation); /// Create dummy UE instance - ue_phy = - unique_dummy_ue_phy_t(new dummy_ue(radio.get(), phy_cfg.phy_cell_cfg, args.log_level, args.rnti, phy_rrc_cfg)); + ue_phy = unique_dummy_ue_phy_t(new dummy_ue(radio.get(), phy_cfg.phy_cell_cfg, args.log_level, args.rnti)); + + /// Configure UE with initial configuration + ue_phy->reconfigure(phy_rrc_cfg); } ~phy_test_bench() @@ -1256,9 +1278,75 @@ public: stack->tti_clock(); + // If no assertion enabled, clear radio link failure to avoid errors in cell transitions + if (change_state != change_state_assert) { + stack->clear_rl_failure(); + } + TESTASSERT(not stack->get_received_rl_failure()); TESTASSERT(ue_phy->run_tti() >= SRSLTE_SUCCESS); - TESTASSERT(stack->run_tti() >= SRSLTE_SUCCESS); + TESTASSERT(stack->run_tti(change_state == change_state_assert) >= SRSLTE_SUCCESS); + + // Change state FSM + switch (change_state) { + case change_state_assert: + if (args.period_pcell_rotate > 0 and tti_counter >= args.period_pcell_rotate) { + log_h.warning("******* Cell rotation: Disable scheduling *******\n"); + // Disable all cells + std::vector active_cells; + stack->set_active_cell_list(active_cells); + + change_state = change_state_flush; + tti_counter = 0; + } + break; + case change_state_flush: + if (tti_counter >= 2 * FDD_HARQ_DELAY_DL_MS + FDD_HARQ_DELAY_UL_MS) { + log_h.warning("******* Cell rotation: Reconfigure *******\n"); + + std::array activation = {}; ///< Activation/Deactivation vector + + // Rotate primary cells + for (auto& q : phy_rrc_cfg) { + q.enb_cc_idx = (q.enb_cc_idx + 1) % args.nof_enb_cells; + } + + for (uint32_t i = 0; i < args.ue_cell_list.size(); i++) { + activation[i] = true; + } + + // Reconfigure eNb PHY + enb_phy->set_config_dedicated(args.rnti, phy_rrc_cfg); + enb_phy->complete_config_dedicated(args.rnti); + enb_phy->set_activation_deactivation_scell(args.rnti, activation); + + // Reconfigure UE PHY + ue_phy->reconfigure(phy_rrc_cfg); + + change_state = change_state_wait_steady; + tti_counter = 0; + } + break; + case change_state_wait_steady: + if (tti_counter >= FDD_HARQ_DELAY_DL_MS + FDD_HARQ_DELAY_UL_MS) { + log_h.warning("******* Cell rotation: Enable scheduling *******\n"); + + std::vector active_cell_list; + + // Rotate primary cells + for (auto& q : phy_rrc_cfg) { + active_cell_list.push_back(q.enb_cc_idx); + } + + stack->set_active_cell_list(active_cell_list); + + change_state = change_state_assert; + tti_counter = 0; + } + break; + } + // Increment counter + tti_counter++; return ret; } @@ -1285,9 +1373,9 @@ int parse_args(int argc, char** argv, phy_test_bench::args_t& args) ("ack_mode", bpo::value(&args.ack_mode), "HARQ ACK/NACK mode: normal, pucch3, cs") ("cell.nof_prb", bpo::value(&args.cell.nof_prb)->default_value(args.cell.nof_prb), "eNb Cell/Carrier bandwidth") ("cell.nof_ports", bpo::value(&args.cell.nof_ports)->default_value(args.cell.nof_ports), "eNb Cell/Carrier number of ports") - ("tm", bpo::value(&args.tm_u32)->default_value(args.tm_u32), "Transmission mode") + ("tm", bpo::value(&args.tm_u32)->default_value(args.tm_u32), "Transmission mode") + ("rotation", bpo::value(&args.period_pcell_rotate), "Serving cells rotation period in ms, set to zero to disable") ; - options.add(common).add_options()("help", "Show this message"); // clang-format on