SRSENB: Fix PHY reconfiguration for HO

Minimal aesthetic change
This commit is contained in:
Xavier Arteaga 2020-05-15 12:54:53 +02:00 committed by Andre Puschmann
parent c892ae56be
commit 3634b351e4
13 changed files with 360 additions and 215 deletions

View File

@ -255,8 +255,14 @@ public:
typedef std::vector<phy_rrc_dedicated_t> phy_rrc_dedicated_list_t; typedef std::vector<phy_rrc_dedicated_t> phy_rrc_dedicated_list_t;
/** /**
* Sets the physical layer dedicated configuration for a given RNTI, a cell index and a secondary cell index. * Sets the physical layer dedicated configuration for a given RNTI. The dedicated configuration list shall provide
* The cc_idx indicates the eNb cell to configure and the scell_idx is the UE's cell index * 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 rnti the given RNTI
* @param dedicated_list Physical layer configuration for the indicated eNb cell * @param dedicated_list Physical layer configuration for the indicated eNb cell

View File

@ -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; 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]) { if (!q->users[rnti_idx]) {
q->users[rnti_idx] = calloc(1, sizeof(srslte_pdsch_user_t)); ERROR("Alocating PDSCH user\n");
if (!q->users[rnti_idx]) { return SRSLTE_ERROR;
ERROR("calloc");
return -1;
}
} }
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
for (int i = 0; i < SRSLTE_NOF_SF_X_FRAME; i++) { return SRSLTE_SUCCESS;
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;
}
}
}
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);
} }
// 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;
}
}
}
// 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; return SRSLTE_SUCCESS;
} }

View File

@ -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) int srslte_pucch_set_rnti(srslte_pucch_t* q, uint16_t rnti)
{ {
uint32_t rnti_idx = q->is_ue ? 0 : 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]) { if (!q->users[rnti_idx]) {
q->users[rnti_idx] = calloc(1, sizeof(srslte_pucch_user_t)); ERROR("Alocating PDSCH user\n");
if (!q->users[rnti_idx]) { return SRSLTE_ERROR;
perror("calloc");
return -1;
}
} }
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) {
for (uint32_t sf_idx = 0; sf_idx < SRSLTE_NOF_SF_X_FRAME; sf_idx++) { // The sequence was generated, cell has not changed and it is eNb, save any efforts
// Precompute scrambling sequence for pucch format 2 return SRSLTE_SUCCESS;
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;
}
}
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);
} }
// 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;
}
}
// 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; return SRSLTE_SUCCESS;
} }

View File

@ -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 */ * For the connection procedure, use srslte_pusch_encode() functions */
int srslte_pusch_set_rnti(srslte_pusch_t* q, uint16_t rnti) int srslte_pusch_set_rnti(srslte_pusch_t* q, uint16_t rnti)
{ {
uint32_t i;
uint32_t rnti_idx = q->is_ue ? 0 : 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]) { if (!q->users[rnti_idx]) {
q->users[rnti_idx] = calloc(1, sizeof(srslte_pusch_user_t)); ERROR("Alocating PDSCH user\n");
if (!q->users[rnti_idx]) { return SRSLTE_ERROR;
perror("calloc");
return -1;
}
} }
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) {
for (i = 0; i < SRSLTE_NOF_SF_X_FRAME; i++) { // The sequence was generated, cell has not changed and it is eNb, save any efforts
if (srslte_sequence_pusch(&q->users[rnti_idx]->seq[i], return SRSLTE_SUCCESS;
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;
}
}
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);
} }
// 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;
}
}
// 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; return SRSLTE_SUCCESS;
} }

View File

@ -42,7 +42,7 @@ public:
cf_t* get_buffer_tx(uint32_t antenna_idx); cf_t* get_buffer_tx(uint32_t antenna_idx);
void set_tti(uint32_t tti); 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); void rem_rnti(uint16_t rnti);
uint32_t get_nof_rnti(); uint32_t get_nof_rnti();
@ -94,7 +94,7 @@ private:
class ue class ue
{ {
public: public:
explicit ue(uint16_t rnti_, bool pcell_) : rnti(rnti_), pcell(pcell_) explicit ue(uint16_t rnti_) : rnti(rnti_)
{ {
// Do nothing // Do nothing
} }
@ -105,13 +105,11 @@ private:
void metrics_read(phy_metrics_t* metrics); void metrics_read(phy_metrics_t* metrics);
void metrics_dl(uint32_t mcs); void metrics_dl(uint32_t mcs);
void metrics_ul(uint32_t mcs, float rssi, float sinr, float turbo_iters); 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; } uint32_t get_rnti() const { return rnti; }
private: private:
uint32_t rnti = 0; uint32_t rnti = 0;
phy_metrics_t metrics = {}; phy_metrics_t metrics = {};
bool pcell;
}; };
// Component carrier index // Component carrier index

View File

@ -185,7 +185,7 @@ private:
* @param ue_cc_idx UE cell/carrier index that is asserted * @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 * @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 * 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 * @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 * @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 * 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); 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 * Get the current down-link physical layer configuration for an RNTI and an eNb cell/carrier
* *

View File

@ -41,7 +41,7 @@ public:
cf_t* get_buffer_rx(uint32_t cc_idx, uint32_t antenna_idx); 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); 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); void rem_rnti(uint16_t rnti);
uint32_t get_nof_rnti(); uint32_t get_nof_rnti();

View File

@ -127,14 +127,14 @@ void cc_worker::init(phy_common* phy_, srslte::log* log_h_, uint32_t cc_idx_)
} }
/* Setup SI-RNTI in PHY */ /* Setup SI-RNTI in PHY */
add_rnti(SRSLTE_SIRNTI, false, false); add_rnti(SRSLTE_SIRNTI, false);
/* Setup P-RNTI in PHY */ /* Setup P-RNTI in PHY */
add_rnti(SRSLTE_PRNTI, false, false); add_rnti(SRSLTE_PRNTI, false);
/* Setup RA-RNTI in PHY */ /* Setup RA-RNTI in PHY */
for (int i = SRSLTE_RARNTI_START; i <= SRSLTE_RARNTI_END; i++) { 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)) { 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); 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) { if (not is_temporal) {
@ -194,8 +194,8 @@ int cc_worker::add_rnti(uint16_t rnti, bool is_pcell, bool is_temporal)
mutex.lock(); mutex.lock();
// Create user unless already exists // Create user unless already exists
if (!ue_db.count(rnti)) { if (ue_db.count(rnti) == 0) {
ue_db[rnti] = new ue(rnti, is_pcell); ue_db[rnti] = new ue(rnti);
} }
mutex.unlock(); mutex.unlock();
@ -378,7 +378,7 @@ int cc_worker::decode_pucch()
uint16_t rnti = iter.first; uint16_t rnti = iter.first;
// If it's a User RNTI and doesn't have PUSCH grant in this TTI // 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); srslte_ul_cfg_t ul_cfg = phy->ue_db.get_ul_config(rnti, cc_idx);
// Check if user needs to receive PUCCH // Check if user needs to receive PUCCH

View File

@ -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++) { 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; 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); workers_common.ue_db.addmod_rnti(rnti, dedicated_list);
// Iterate over the list and add the RNTIs // Iterate over the list and add the RNTIs
for (uint32_t scell_idx = 0; scell_idx < dedicated_list.size(); scell_idx++) { for (const phy_rrc_dedicated_t& config : dedicated_list) {
auto& config = dedicated_list[scell_idx]; // Add RNTI to eNb cell/carrier.
// - Do not ignore PCell, it could have changed
// Configure only if active, ignore otherwise // - Do not remove RNTI from unused workers, it will be removed when the UE is released
if (scell_idx != 0 && config.configured) { if (config.configured) {
// Add RNTI to workers // Add RNTI to all SF workers
for (uint32_t w = 0; w < nof_workers; w++) { 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);
} }
} }
} }

View File

@ -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 // Check Component Carrier is part of UE SCell map
if (_get_ue_cc_idx(rnti, enb_cc_idx) == SRSLTE_MAX_CARRIERS) { 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; 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; 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) { if (_assert_rnti(rnti) != SRSLTE_SUCCESS) {
return SRSLTE_ERROR; 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; 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) { if (_assert_ue_cc(rnti, ue_cc_idx) != SRSLTE_SUCCESS) {
return SRSLTE_ERROR; return SRSLTE_ERROR;
} }
// Return error if not PCell or not Active SCell // 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) { 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); ERROR("Failed to assert active UE cell/carrier %d for RNTI 0x%X", ue_cc_idx, rnti);
return SRSLTE_ERROR; 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 // 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) { 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); ERROR("Failed to assert active eNb cell/carrier %d for RNTI 0x%X", enb_cc_idx, rnti);
return SRSLTE_ERROR; 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 inline int phy_ue_db::_assert_stack() const
{ {
if (not stack) { if (stack == nullptr) {
return SRSLTE_ERROR; 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 inline int phy_ue_db::_assert_cell_list_cfg() const
{ {
if (not cell_cfg_list) { if (cell_cfg_list == nullptr) {
return SRSLTE_ERROR; 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; return default_cfg;
} }
// Make sure the C-RNTI exists and the cell is active for the user // Make sure the C-RNTI exists and the cell/carrier is configured
if (_assert_active_enb_cc(rnti, enb_cc_idx) != SRSLTE_SUCCESS) { 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; 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 // 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) 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<std::mutex> lock(mutex); std::lock_guard<std::mutex> lock(mutex);
// Create new user if did not exist // Create new user if did not exist
if (!ue_db.count(rnti)) { if (ue_db.count(rnti) == 0) {
_add_rnti(rnti); _add_rnti(rnti);
} }
// Get UE by reference // Get UE by reference
common_ue& ue = ue_db[rnti]; common_ue& ue = ue_db[rnti];
// Number of configured serving cells // Number of configured secondary serving cells
uint32_t nof_configured_scell = 0; uint32_t nof_configured_scell = 0;
// Iterate PHY RRC configuration for each UE cell/carrier // 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; uint32_t nof_cc = SRSLTE_MIN(phy_rrc_dedicated_list.size(), SRSLTE_MAX_CARRIERS);
ue_cc_idx++) { for (uint32_t ue_cc_idx = 0; ue_cc_idx < nof_cc; ue_cc_idx++) {
auto& phy_rrc_dedicated = phy_rrc_dedicated_list[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 // Configured, add/modify entry in the cell_info map
cell_info_t& cell_info = ue.cell_info[ue_cc_idx]; cell_info_t& cell_info = ue.cell_info[ue_cc_idx];
if (phy_rrc_dedicated.configured or cell_info.state == cell_state_primary) { // Configure PHY
// Set cell information if (cell_info.state == cell_state_primary) {
cell_info.enb_cc_idx = phy_rrc_dedicated.enb_cc_idx; // 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) {
// Apply PCell configuration is stash cell_info.phy_cfg.set_defaults();
if (cell_info.state == cell_state_primary) { _set_common_config_rnti(rnti, cell_info.phy_cfg);
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);
} }
// Set Cell state, all inactive by default except PCell // Apply primmary serving cell configuration in stash
if (cell_info.state != cell_state_primary) { ue.pcell_cfg_stash = phy_rrc_dedicated.phy_cfg;
cell_info.state = cell_state_secondary_inactive; _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 // Count Serving cell
nof_configured_scell++; nof_configured_scell++;
@ -311,34 +313,38 @@ void phy_ue_db::addmod_rnti(uint16_t
// Cell without configuration (except PCell) // Cell without configuration (except PCell)
cell_info.state = cell_state_none; 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 // Disable the rest of potential serving cells
for (uint32_t cell_idx = phy_rrc_dedicated_list.size(); cell_idx < SRSLTE_MAX_CARRIERS; cell_idx++) { for (uint32_t i = nof_cc; i < SRSLTE_MAX_CARRIERS; i++) {
ue.cell_info[cell_idx].state = cell_state_none; 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 // 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++) { 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_secondary_inactive || if (ue.cell_info[ue_cc_idx].state == cell_state_primary) {
ue.cell_info[ue_cc_idx].state == cell_state_secondary_active) { // The primary cell applies changes in the stashed config
ue.cell_info[ue_cc_idx].phy_cfg.dl_cfg.dci.multiple_csi_request_enabled = (nof_configured_scell > 1); ue.pcell_cfg_stash.dl_cfg.dci.multiple_csi_request_enabled = (nof_configured_scell > 0);
} else if (ue.cell_info[ue_cc_idx].state == cell_state_primary) { } else {
ue.pcell_cfg_stash.dl_cfg.dci.multiple_csi_request_enabled = (nof_configured_scell > 1); // 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 // Copy necessary PCell configuration for receiving Configuration Completion from UE
srslte::phy_cfg_t& pcell_cfg = ue.cell_info[0].phy_cfg; 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; 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 tmp_pucch_cfg.N_pucch_1 = pcell_cfg.ul_cfg.pucch.N_pucch_1; ///< Used for ACK
// Load new UL configuration // Load new UL configuration
pcell_cfg.ul_cfg = ue.pcell_cfg_stash.ul_cfg; 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; pcell_cfg.ul_cfg.pucch = tmp_pucch_cfg;
} }
@ -346,7 +352,7 @@ void phy_ue_db::rem_rnti(uint16_t rnti)
{ {
std::lock_guard<std::mutex> lock(mutex); std::lock_guard<std::mutex> lock(mutex);
if (ue_db.count(rnti)) { if (ue_db.count(rnti) != 0) {
ue_db.erase(rnti); 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) void phy_ue_db::activate_deactivate_scell(uint16_t rnti, uint32_t ue_cc_idx, bool activate)
{ {
std::lock_guard<std::mutex> lock(mutex);
// Assert RNTI and SCell are valid // Assert RNTI and SCell are valid
if (_assert_ue_cc(rnti, ue_cc_idx) != SRSLTE_SUCCESS) { if (_assert_ue_cc(rnti, ue_cc_idx) != SRSLTE_SUCCESS) {
return; 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; 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<std::mutex> 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 srslte_dl_cfg_t phy_ue_db::get_dl_config(uint16_t rnti, uint32_t enb_cc_idx) const
{ {
std::lock_guard<std::mutex> lock(mutex); std::lock_guard<std::mutex> 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 cell_info_t& cell_info = ue.cell_info[cell_idx];
const srslte_dl_cfg_t& dl_cfg = cell_info.phy_cfg.dl_cfg; 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) { 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; 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); 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 // Iterate over the ACK information
for (uint32_t scell_idx = 0; scell_idx < SRSLTE_MAX_CARRIERS; 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[scell_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++) { for (uint32_t m = 0; m < pdsch_ack_cc.M; m++) {
if (pdsch_ack_cc.m[m].present) { if (pdsch_ack_cc.m[m].present) {
for (uint32_t tb = 0; tb < SRSLTE_MAX_CODEWORDS; tb++) { for (uint32_t tb = 0; tb < SRSLTE_MAX_CODEWORDS; tb++) {
if (pdsch_ack_cc.m[m].value[tb] != 2) { 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); _assert_active_ue_cc(rnti, uci_cfg.cqi.scell_index);
// Get CQI carrier index // Get CQI carrier index
auto& cqi_scell_info = ue_db.at(rnti).cell_info[uci_cfg.cqi.scell_index]; 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; uint32_t cqi_cc_idx = cqi_scell_info.enb_cc_idx;
// Notify CQI only if CRC is valid // Notify CQI only if CRC is valid
if (uci_value.cqi.data_crc) { if (uci_value.cqi.data_crc) {

View File

@ -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; int ret = SRSLTE_ERROR;
if (cc_idx < cc_workers.size()) { 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; ret = SRSLTE_SUCCESS;
} }

View File

@ -94,3 +94,11 @@ add_test(enb_phy_test_tm1_ca_cs enb_phy_test --duration=${ENB_PHY_TEST_DURATION}
# - 6 PRB # - 6 PRB
# - PUCCH format 1b with Channel selection ACK/NACK feedback mode # - 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) 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)

View File

@ -56,6 +56,7 @@ public:
} \ } \
\ \
bool get_received_##NAME() { return received_##NAME; } \ bool get_received_##NAME() { return received_##NAME; } \
void clear_##NAME() { received_##NAME = false; } \
\ \
private: \ private: \
void notify_##NAME() \ void notify_##NAME() \
@ -326,18 +327,16 @@ public:
explicit dummy_stack(const srsenb::phy_cfg_t& phy_cfg_, explicit dummy_stack(const srsenb::phy_cfg_t& phy_cfg_,
const srsenb::phy_interface_rrc_lte::phy_rrc_dedicated_list_t& phy_rrc_, const srsenb::phy_interface_rrc_lte::phy_rrc_dedicated_list_t& phy_rrc_,
const std::string& log_level, const std::string& log_level,
uint16_t rnti_, uint16_t rnti_) :
std::vector<uint32_t>& active_cell_list_) :
log_h("STACK"), log_h("STACK"),
ue_rnti(rnti_), ue_rnti(rnti_),
random_gen(srslte_random_init(rnti_)), random_gen(srslte_random_init(rnti_)),
phy_cell_cfg(phy_cfg_.phy_cell_cfg), phy_cell_cfg(phy_cfg_.phy_cell_cfg),
phy_rrc(phy_rrc_), phy_rrc(phy_rrc_)
active_cell_list(active_cell_list_)
{ {
log_h.set_level(log_level); log_h.set_level(log_level);
srslte_softbuffer_tx_init(&softbuffer_tx, SRSLTE_MAX_PRB); 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]) { for (auto& sb : softbuffer_rx[i]) {
srslte_softbuffer_rx_init(&sb, SRSLTE_MAX_PRB); srslte_softbuffer_rx_init(&sb, SRSLTE_MAX_PRB);
} }
@ -399,6 +398,8 @@ public:
srslte_random_free(random_gen); srslte_random_free(random_gen);
} }
void set_active_cell_list(std::vector<uint32_t>& active_cell_list_) { active_cell_list = active_cell_list_; }
int sr_detected(uint32_t tti, uint16_t rnti) override int sr_detected(uint32_t tti, uint16_t rnti) override
{ {
tti_sr_info_t tti_sr_info = {}; tti_sr_info_t tti_sr_info = {};
@ -674,7 +675,7 @@ public:
void rl_failure(uint16_t rnti) override { notify_rl_failure(); } void rl_failure(uint16_t rnti) override { notify_rl_failure(); }
void rl_ok(uint16_t rnti) override { notify_rl_ok(); } void rl_ok(uint16_t rnti) override { notify_rl_ok(); }
void tti_clock() override { notify_tti_clock(); } void tti_clock() override { notify_tti_clock(); }
int run_tti() int run_tti(bool enable_assert)
{ {
// Check DL ACKs match with grants // Check DL ACKs match with grants
while (not tti_dl_info_ack_queue.empty()) { 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); tti_dl_sched.tti = TTI_ADD(tti_dl_sched.tti, FDD_HARQ_DELAY_DL_MS);
// Assert that ACKs have been received // Assert that ACKs have been received
TESTASSERT(tti_dl_sched.tti == tti_dl_ack.tti); if (enable_assert) {
TESTASSERT(tti_dl_sched.cc_idx == tti_dl_ack.cc_idx); TESTASSERT(tti_dl_sched.tti == tti_dl_ack.tti);
TESTASSERT(tti_dl_sched.tb_idx == tti_dl_ack.tb_idx); TESTASSERT(tti_dl_sched.cc_idx == tti_dl_ack.cc_idx);
TESTASSERT(tti_dl_sched.ack == tti_dl_ack.ack); 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_sched_queue.pop();
tti_dl_info_ack_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(); tti_ul_info_t& tti_ul_ack = tti_ul_info_ack_queue.front();
// Assert that ACKs have been received // Assert that ACKs have been received
TESTASSERT(tti_ul_sched.tti == tti_ul_ack.tti); if (enable_assert) {
TESTASSERT(tti_ul_sched.cc_idx == tti_ul_ack.cc_idx); TESTASSERT(tti_ul_sched.tti == tti_ul_ack.tti);
TESTASSERT(tti_ul_sched.crc == tti_ul_ack.crc); 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_sched_queue.pop();
tti_ul_info_ack_queue.pop(); tti_ul_info_ack_queue.pop();
} }
// Check SR match with TTI // 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(); tti_sr_info_t tti_sr_info1 = tti_sr_info_queue.front();
// POP first from queue // POP first from queue
tti_sr_info_queue.pop(); tti_sr_info_queue.pop();
// Get second, do not pop if (enable_assert) {
tti_sr_info_t& tti_sr_info2 = tti_sr_info_queue.front(); // 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 SR info
log_h.info("SR: tti1=%d; tti2=%d; elapsed %d;\n", tti_sr_info1.tti, tti_sr_info2.tti, elapsed_tti); log_h.info("SR: tti1=%d; tti2=%d; elapsed %d;\n", tti_sr_info1.tti, tti_sr_info2.tti, elapsed_tti);
// Check first TTI // Check first TTI
TESTASSERT(tti_sr_info1.tti % 20 == 0); TESTASSERT(tti_sr_info1.tti % 20 == 0);
// Make sure the TTI difference is 20 // Make sure the TTI difference is 20
TESTASSERT(elapsed_tti == 20); TESTASSERT(elapsed_tti == 20);
}
} }
return SRSLTE_SUCCESS; return SRSLTE_SUCCESS;
@ -757,25 +764,15 @@ private:
std::map<uint32_t, uint32_t> last_ri = {}; std::map<uint32_t, uint32_t> last_ri = {};
public: public:
dummy_ue(dummy_radio* _radio, dummy_ue(dummy_radio* _radio, const srsenb::phy_cell_cfg_list_t& cell_list, std::string log_level, uint16_t rnti_) :
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_) :
radio(_radio), radio(_radio),
log_h("UE PHY", nullptr, true), log_h("UPHY", nullptr, true)
phy_rrc_cfg(phy_rrc_cfg_)
{ {
// Calculate subframe length // Calculate subframe length
nof_ports = cell_list[0].cell.nof_ports; nof_ports = cell_list[0].cell.nof_ports;
sf_len = static_cast<uint32_t>(SRSLTE_SF_LEN_PRB(cell_list[0].cell.nof_prb)); sf_len = static_cast<uint32_t>(SRSLTE_SF_LEN_PRB(cell_list[0].cell.nof_prb));
rnti = rnti_; 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)); log_h.set_level(std::move(log_level));
// Initialise one buffer per eNb // Initialise one buffer per eNb
@ -791,8 +788,9 @@ public:
srslte_vec_cf_zero(buffer, sf_len); srslte_vec_cf_zero(buffer, sf_len);
} }
for (auto& q : phy_rrc_cfg) { // Iterate over all cells
uint32_t cc_idx = q.enb_cc_idx; 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 // Allocate UE DL
auto* ue_dl = (srslte_ue_dl_t*)srslte_vec_malloc(sizeof(srslte_ue_dl_t)); 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); ue_dl_v.push_back(ue_dl);
// Initialise UE DL // Initialise UE DL
if (srslte_ue_dl_init( if (srslte_ue_dl_init(ue_dl, &buffers[cc_idx * nof_ports], cell.nof_prb, cell.nof_ports)) {
ue_dl, &buffers[cc_idx * nof_ports], cell_list[cc_idx].cell.nof_prb, cell_list[cc_idx].cell.nof_ports)) {
ERROR("Initiating UE DL\n"); ERROR("Initiating UE DL\n");
} }
// Set Cell // 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"); ERROR("Setting UE DL cell\n");
} }
@ -823,12 +820,12 @@ public:
ue_ul_v.push_back(ue_ul); ue_ul_v.push_back(ue_ul);
// Initialise 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"); ERROR("Setting UE UL cell\n");
} }
// Set cell // 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"); ERROR("Setting UE DL cell\n");
} }
@ -886,6 +883,17 @@ public:
srslte_softbuffer_tx_free(&softbuffer_tx); 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) int work_dl(srslte_pdsch_ack_t& pdsch_ack, srslte_uci_data_t& uci_data)
{ {
// Read DL // Read DL
@ -917,10 +925,10 @@ public:
ue_dl_cfg.last_ri = last_ri[i]; 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 // 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); TESTASSERT(nof_dl_grants >= SRSLTE_SUCCESS);
// Generate ACKs // Generate ACKs
@ -929,7 +937,8 @@ public:
srslte_dci_dl_info(dci_dl, str, sizeof(str)); srslte_dci_dl_info(dci_dl, str, sizeof(str));
log_h.info("[DL DCI] %s\n", 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"); log_h.error("Converting DCI message to DL dci\n");
return SRSLTE_ERROR; return SRSLTE_ERROR;
} }
@ -958,9 +967,9 @@ public:
} }
// Generate CQI periodic if required // 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.ri_len) {
uci_data.cfg.cqi.scell_index = i; uci_data.cfg.cqi.scell_index = i;
} }
@ -979,6 +988,7 @@ public:
for (uint32_t i = 0; i < phy_rrc_cfg.size(); i++) { for (uint32_t i = 0; i < phy_rrc_cfg.size(); i++) {
srslte_dci_ul_t dci_ul[SRSLTE_MAX_DCI_MSG] = {}; srslte_dci_ul_t dci_ul[SRSLTE_MAX_DCI_MSG] = {};
srslte::phy_cfg_t& dedicated = phy_rrc_cfg[i].phy_cfg; 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 = {}; srslte_ue_ul_cfg_t ue_ul_cfg = {};
ue_ul_cfg.ul_cfg = dedicated.ul_cfg; ue_ul_cfg.ul_cfg = dedicated.ul_cfg;
@ -993,7 +1003,7 @@ public:
ue_dl_cfg.cfg.pdsch.rnti = rnti; ue_dl_cfg.cfg.pdsch.rnti = rnti;
// Get UL grants // 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); TESTASSERT(nof_ul_grants >= SRSLTE_SUCCESS);
srslte_pusch_data_t pusch_data = {}; srslte_pusch_data_t pusch_data = {};
@ -1001,7 +1011,8 @@ public:
if (nof_ul_grants > SRSLTE_SUCCESS) { if (nof_ul_grants > SRSLTE_SUCCESS) {
TESTASSERT(srslte_ue_ul_dci_to_pusch_grant( 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); 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)); srslte_ue_ul_gen_sr(&ue_ul_cfg, &sf_ul_cfg, &uci_data, (bool)(sf_ul_cfg.tti % 20 == 0));
// Generate Acknowledgements // 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) { if (uci_data.cfg.cqi.ri_len) {
last_ri[uci_data.cfg.cqi.scell_index] = uci_data.value.ri; last_ri[uci_data.cfg.cqi.scell_index] = uci_data.value.ri;
@ -1031,7 +1042,7 @@ public:
} }
// Work UL // 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] = {}; char str[256] = {};
srslte_ue_ul_info(&ue_ul_cfg, &sf_ul_cfg, &pusch_data.uci, str, sizeof(str)); 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: public:
struct args_t { struct args_t {
uint16_t rnti = 0x1234; uint16_t rnti = 0x1234;
uint32_t duration = 10240; uint32_t duration = 10240;
uint32_t nof_enb_cells = 1; uint32_t nof_enb_cells = 1;
srslte_cell_t cell = {}; srslte_cell_t cell = {};
std::string ue_cell_list_str = "0"; ///< First indicates PCell std::string ue_cell_list_str = "0"; ///< First indicates PCell
std::vector<uint32_t> ue_cell_list = {0}; std::vector<uint32_t> ue_cell_list = {0};
std::string ack_mode = "normal"; std::string ack_mode = "normal";
std::string log_level = "none"; std::string log_level = "none";
uint32_t tm_u32 = 1; uint32_t tm_u32 = 1;
srslte_tm_t tm = SRSLTE_TM1; uint32_t period_pcell_rotate = 0;
srslte_tm_t tm = SRSLTE_TM1;
args_t() args_t()
{ {
cell.nof_prb = 6; cell.nof_prb = 6;
@ -1134,6 +1146,14 @@ private:
srsenb::phy_cfg_t phy_cfg; ///< eNb Cell/Carrier configuration 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 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: public:
explicit phy_test_bench(args_t& args_) : log_h("TEST BENCH") 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)); new dummy_radio(args.nof_enb_cells * args.cell.nof_ports, args.cell.nof_prb, args.log_level));
/// Create Dummy Stack isntance /// 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 initialisation instance
enb_phy = unique_srsenb_phy_t(new srsenb::phy(&logger_stdout)); enb_phy = unique_srsenb_phy_t(new srsenb::phy(&logger_stdout));
/// Initiate eNb PHY with the given RNTI /// Initiate eNb PHY with the given RNTI
enb_phy->init(phy_args, phy_cfg, radio.get(), stack.get()); 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->set_config_dedicated(args.rnti, phy_rrc_cfg);
enb_phy->complete_config_dedicated(args.rnti); enb_phy->complete_config_dedicated(args.rnti);
enb_phy->set_activation_deactivation_scell(args.rnti, activation); enb_phy->set_activation_deactivation_scell(args.rnti, activation);
/// Create dummy UE instance /// Create dummy UE instance
ue_phy = ue_phy = unique_dummy_ue_phy_t(new dummy_ue(radio.get(), phy_cfg.phy_cell_cfg, args.log_level, args.rnti));
unique_dummy_ue_phy_t(new dummy_ue(radio.get(), phy_cfg.phy_cell_cfg, args.log_level, args.rnti, phy_rrc_cfg));
/// Configure UE with initial configuration
ue_phy->reconfigure(phy_rrc_cfg);
} }
~phy_test_bench() ~phy_test_bench()
@ -1256,9 +1278,75 @@ public:
stack->tti_clock(); 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(not stack->get_received_rl_failure());
TESTASSERT(ue_phy->run_tti() >= SRSLTE_SUCCESS); 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<uint32_t> 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<bool, SRSLTE_MAX_CARRIERS> 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<uint32_t> 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; return ret;
} }
@ -1285,9 +1373,9 @@ int parse_args(int argc, char** argv, phy_test_bench::args_t& args)
("ack_mode", bpo::value<std::string>(&args.ack_mode), "HARQ ACK/NACK mode: normal, pucch3, cs") ("ack_mode", bpo::value<std::string>(&args.ack_mode), "HARQ ACK/NACK mode: normal, pucch3, cs")
("cell.nof_prb", bpo::value<uint32_t>(&args.cell.nof_prb)->default_value(args.cell.nof_prb), "eNb Cell/Carrier bandwidth") ("cell.nof_prb", bpo::value<uint32_t>(&args.cell.nof_prb)->default_value(args.cell.nof_prb), "eNb Cell/Carrier bandwidth")
("cell.nof_ports", bpo::value<uint32_t>(&args.cell.nof_ports)->default_value(args.cell.nof_ports), "eNb Cell/Carrier number of ports") ("cell.nof_ports", bpo::value<uint32_t>(&args.cell.nof_ports)->default_value(args.cell.nof_ports), "eNb Cell/Carrier number of ports")
("tm", bpo::value<uint32_t>(&args.tm_u32)->default_value(args.tm_u32), "Transmission mode") ("tm", bpo::value<uint32_t>(&args.tm_u32)->default_value(args.tm_u32), "Transmission mode")
("rotation", bpo::value<uint32_t>(&args.period_pcell_rotate), "Serving cells rotation period in ms, set to zero to disable")
; ;
options.add(common).add_options()("help", "Show this message"); options.add(common).add_options()("help", "Show this message");
// clang-format on // clang-format on