From 231431f569ff2fe93760431f29b3f601091b7b78 Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Mon, 10 Feb 2020 12:59:23 +0100 Subject: [PATCH] SRSENB: enabled CA PUCCH decode in eNb --- .../srslte/interfaces/enb_interfaces.h | 84 +++++++-- lib/include/srslte/phy/phch/uci_cfg.h | 4 +- lib/src/phy/phch/pucch.c | 20 +-- lib/src/phy/ue/ue_dl.c | 15 ++ srsenb/hdr/phy/phy.h | 4 +- srsenb/hdr/phy/phy_common.h | 94 +++++++++- srsenb/hdr/phy/sf_worker.h | 4 +- srsenb/hdr/stack/enb_stack_lte.h | 11 +- srsenb/hdr/stack/mac/mac.h | 4 +- srsenb/src/phy/cc_worker.cc | 64 +++---- srsenb/src/phy/phy.cc | 31 +++- srsenb/src/phy/phy_common.cc | 135 ++++++++++++--- srsenb/src/phy/sf_worker.cc | 23 ++- srsenb/src/stack/mac/mac.cc | 11 +- srsenb/test/common/dummy_classes.h | 2 +- srsenb/test/phy/enb_phy_test.cc | 162 +++++++++++++----- 16 files changed, 513 insertions(+), 155 deletions(-) diff --git a/lib/include/srslte/interfaces/enb_interfaces.h b/lib/include/srslte/interfaces/enb_interfaces.h index 7e7ee1465..05d4bad53 100644 --- a/lib/include/srslte/interfaces/enb_interfaces.h +++ b/lib/include/srslte/interfaces/enb_interfaces.h @@ -42,6 +42,9 @@ class mac_interface_phy_lte public: const static int MAX_GRANTS = 64; + /** + * DL grant structure per UE + */ typedef struct { srslte_dci_dl_t dci; srslte_dci_cfg_t dci_cfg; @@ -49,12 +52,18 @@ public: srslte_softbuffer_tx_t* softbuffer_tx[SRSLTE_MAX_TB]; } dl_sched_grant_t; + /** + * DL Scheduling result per cell/carrier + */ typedef struct { - dl_sched_grant_t pdsch[MAX_GRANTS]; - uint32_t nof_grants; - uint32_t cfi; - } dl_sched_t; // per carrier + dl_sched_grant_t pdsch[MAX_GRANTS]; //< DL Grants + uint32_t nof_grants; //< Number of DL grants + uint32_t cfi; //< Current CFI of the cell, it can vary across cells + } dl_sched_t; + /** + * List of DL scheduling results, one entry per cell/carrier + */ typedef std::vector dl_sched_list_t; typedef struct { @@ -62,6 +71,9 @@ public: bool ack; } ul_sched_ack_t; + /** + * UL grant information per UE + */ typedef struct { srslte_dci_ul_t dci; srslte_dci_cfg_t dci_cfg; @@ -71,13 +83,19 @@ public: srslte_softbuffer_rx_t* softbuffer_rx; } ul_sched_grant_t; + /** + * UL Scheduling result per cell/carrier + */ typedef struct { ul_sched_grant_t pusch[MAX_GRANTS]; ul_sched_ack_t phich[MAX_GRANTS]; uint32_t nof_grants; uint32_t nof_phich; - } ul_sched_t; // per carrier + } ul_sched_t; + /** + * List of UL scheduling results, one entry per cell/carrier + */ typedef std::vector ul_sched_list_t; virtual int sr_detected(uint32_t tti, uint16_t rnti) = 0; @@ -85,9 +103,30 @@ public: virtual int ri_info(uint32_t tti, uint16_t rnti, uint32_t ri_value) = 0; virtual int pmi_info(uint32_t tti, uint16_t rnti, uint32_t pmi_value) = 0; - virtual int cqi_info(uint32_t tti, uint16_t rnti, uint32_t cqi_value) = 0; + + /** + * PHY callback for for giving MAC the Channel Quality information of a given RNTI, TTI and eNb cell/carrier + * @param tti the given TTI + * @param rnti the UE identifier in the eNb + * @param cqi_value the corresponding Channel Quality Information + * @return SRSLTE_SUCCESS if no error occurs, SRSLTE_ERROR* if an error occurs + */ + virtual int cqi_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t cqi_value) = 0; + virtual int snr_info(uint32_t tti, uint16_t rnti, float snr_db) = 0; - virtual int ack_info(uint32_t tti, uint16_t rnti, uint32_t tb_idx, bool ack) = 0; + + /** + * PHY callback for giving MAC the HARQ DL ACK/NACK feedback information for a given RNTI, TTI, eNb cell/carrier and + * Transport block. + * + * @param tti the given TTI + * @param rnti the UE identifier in the eNb + * @param cc_idx the eNb Cell/Carrier identifier + * @param tb_idx the transport block index + * @param ack true for ACK, false for NACK, do not call for DTX + * @return SRSLTE_SUCCESS if no error occurs, SRSLTE_ERROR* if an error occurs + */ + virtual int ack_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t tb_idx, bool ack) = 0; virtual int crc_info(uint32_t tti, uint16_t rnti, uint32_t nof_bytes, bool crc_res) = 0; virtual int get_dl_sched(uint32_t tti, dl_sched_list_t& dl_sched_res) = 0; @@ -104,8 +143,16 @@ public: class phy_interface_mac_lte { public: - /* MAC adds/removes an RNTI to the list of active RNTIs */ - virtual int add_rnti(uint16_t rnti, bool is_temporal = false) = 0; + /** + * Interface for MAC to add or modify user in the active UE database setting. This function requires a primary cell + * (PCell) index and a list of secondary cells (SCell) for the UE. The elements in the list SCell list must follow the + * UE's SCell indexes order. + * + * @param rnti identifier of the user + * @param pcell_index Primary cell (PCell) index + * @param is_temporal Indicates whether the UE is temporal + */ + virtual int add_rnti(uint16_t rnti, uint32_t pcell_index, bool is_temporal) = 0; virtual void rem_rnti(uint16_t rnti) = 0; virtual void set_mch_period_stop(uint32_t stop) = 0; }; @@ -126,8 +173,23 @@ public: } phy_rrc_cfg_t; virtual void - configure_mbsfn(asn1::rrc::sib_type2_s* sib2, asn1::rrc::sib_type13_r9_s* sib13, asn1::rrc::mcch_msg_s mcch) = 0; - virtual void set_config_dedicated(uint16_t rnti, const srslte::phy_cfg_t& dedicated) = 0; + configure_mbsfn(asn1::rrc::sib_type2_s* sib2, asn1::rrc::sib_type13_r9_s* sib13, asn1::rrc::mcch_msg_s mcch) = 0; + + typedef struct { + uint32_t cc_idx = 0; ///< eNb Cell index + srslte::phy_cfg_t phy_cfg = {}; ///< Dedicated physical layer configuration + } phy_rrc_dedicated_t; + + 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 + * + * @param rnti the given RNTI + * @param dedicated_list Physical layer configuration for the indicated eNb cell + */ + virtual void set_config_dedicated(uint16_t rnti, const phy_rrc_dedicated_list_t& dedicated_list) = 0; }; class mac_interface_rrc diff --git a/lib/include/srslte/phy/phch/uci_cfg.h b/lib/include/srslte/phy/phch/uci_cfg.h index bdb1aeeb5..8baa9ab77 100644 --- a/lib/include/srslte/phy/phch/uci_cfg.h +++ b/lib/include/srslte/phy/phch/uci_cfg.h @@ -33,8 +33,8 @@ typedef struct SRSLTE_API { } srslte_uci_value_ack_t; typedef struct SRSLTE_API { - bool pending_tb[SRSLTE_MAX_CODEWORDS]; - uint32_t nof_acks; + bool pending_tb[SRSLTE_MAX_CODEWORDS]; //< Indicates whether there was a grant that requires an ACK/NACK + uint32_t nof_acks; //< Number of transport blocks, deduced from transmission mode uint32_t ncce[SRSLTE_UCI_MAX_M]; uint32_t N_bundle; uint32_t tdd_ack_M; diff --git a/lib/src/phy/phch/pucch.c b/lib/src/phy/phch/pucch.c index d722754d2..7558bc8d7 100644 --- a/lib/src/phy/phch/pucch.c +++ b/lib/src/phy/phch/pucch.c @@ -1255,45 +1255,45 @@ void srslte_pucch_rx_info(srslte_pucch_cfg_t* cfg, srslte_uci_value_t* uci_data, srslte_pucch_format_t srslte_pucch_select_format(srslte_pucch_cfg_t* cfg, srslte_uci_cfg_t* uci_cfg, srslte_cp_t cp) { srslte_pucch_format_t format = SRSLTE_PUCCH_FORMAT_ERROR; + uint32_t total_ack = srslte_uci_cfg_total_ack(uci_cfg); + // No CQI data if (!uci_cfg->cqi.data_enable && uci_cfg->cqi.ri_len == 0) { - // PUCCH Format 3 condition specified in: - // 3GPP 36.213 10.1.2.2.2 PUCCH format 3 HARQ-ACK procedure if (cfg->ack_nack_feedback_mode == SRSLTE_PUCCH_ACK_NACK_FEEDBACK_MODE_PUCCH3 && - srslte_uci_cfg_total_ack(uci_cfg) > 1) { + total_ack > uci_cfg->ack[0].nof_acks) { format = SRSLTE_PUCCH_FORMAT_3; } // 1-bit ACK + optional SR - else if (srslte_uci_cfg_total_ack(uci_cfg) == 1) { + else if (total_ack == 1) { format = SRSLTE_PUCCH_FORMAT_1A; } // 2-bit ACK + optional SR - else if (srslte_uci_cfg_total_ack(uci_cfg) >= 2 && srslte_uci_cfg_total_ack(uci_cfg) <= 4) { + else if (total_ack >= 2 && total_ack <= 4) { format = SRSLTE_PUCCH_FORMAT_1B; // with channel selection if > 2 } // If UCI value is provided, use SR signal only, otherwise SR request opportunity else if (uci_cfg->is_scheduling_request_tti) { format = SRSLTE_PUCCH_FORMAT_1; } else { - ERROR("Error selecting PUCCH format: Unsupported number of ACK bits %d\n", srslte_uci_cfg_total_ack(uci_cfg)); + ERROR("Error selecting PUCCH format: Unsupported number of ACK bits %d\n", total_ack); } } // CQI data else { // CQI and no ack - if (srslte_uci_cfg_total_ack(uci_cfg) == 0) { + if (total_ack == 0) { format = SRSLTE_PUCCH_FORMAT_2; } // CQI + 1-bit ACK - else if (srslte_uci_cfg_total_ack(uci_cfg) == 1 && SRSLTE_CP_ISNORM(cp)) { + else if (total_ack == 1 && SRSLTE_CP_ISNORM(cp)) { format = SRSLTE_PUCCH_FORMAT_2A; } // CQI + 2-bit ACK - else if (srslte_uci_cfg_total_ack(uci_cfg) == 2) { + else if (total_ack == 2) { format = SRSLTE_PUCCH_FORMAT_2B; } // CQI + 2-bit ACK + cyclic prefix - else if (srslte_uci_cfg_total_ack(uci_cfg) == 1 && SRSLTE_CP_ISEXT(cp)) { + else if (total_ack == 1 && SRSLTE_CP_ISEXT(cp)) { format = SRSLTE_PUCCH_FORMAT_2B; } } diff --git a/lib/src/phy/ue/ue_dl.c b/lib/src/phy/ue/ue_dl.c index 121b2ee63..d5bdb5f17 100644 --- a/lib/src/phy/ue/ue_dl.c +++ b/lib/src/phy/ue/ue_dl.c @@ -978,6 +978,21 @@ static void gen_ack_fdd(srslte_pdsch_ack_t* ack_info, srslte_uci_data_t* uci_dat if (ack_info->nof_cc == 1) { // If only 1 configured cell, report 1 or 2 bits depending on number of detected TB uci_data->cfg.ack[0].nof_acks = tb_count; + } else if (ack_info->ack_nack_feedback_mode == SRSLTE_PUCCH_ACK_NACK_FEEDBACK_MODE_PUCCH3 && + tb_count_cc0 == tb_count) { + // According to 3GPP 36.213 Section 10.1.2.2.2 PUCCH format 3 HARQ-ACK procedure + // For FDD with PUCCH format 3, the UE shall use PUCCH resource n_pucch_3 or n_pucch_1 for transmission of + // HARQ-ACK in subframe n where + // - for a PDSCH transmission only on the primary cell indicated by the detection of a corresponding PDCCH in + // subframe n − 4 , or for a PDCCH indicating downlink SPS release (defined in subclause 9.2) in subframe n − 4 + // on the primary cell, the UE shall use PUCCH format 1a/1b and PUCCH resource n_pucch_1. + // - for a PDSCH transmission only on the primary cell where there is not a corresponding PDCCH detected on + // subframe n - 4, the UE shall use PUCCH format 1a/1b and PUCCH resource n_pucch_1 where the value of n_pucch_1 + // is determined according to higher layer configuration and Table 9.2-2. + // - for a PDSCH transmission on the secondary cell indicated by the detection of a corresponding PDCCH in + // subframe n − 4 , the UE shall use PUCCH format 3 and PUCCH resource n_pucch_3 where the value of n PUCCH + // is determined according to higher layer configuration and Table 10.1.2.2.2-1. + uci_data->cfg.ack[0].nof_acks = tb_count_cc0; // So, set only PCell } else if (uci_data->cfg.cqi.data_enable && !ack_info->is_pusch_available) { // 3GPP 36.213 R.15 Section 10.1.1: // For FDD or for FDD-TDD and primary cell frame structure type 1 and for a UE that is configured with more than diff --git a/srsenb/hdr/phy/phy.h b/srsenb/hdr/phy/phy.h index b50235dfc..f691fc013 100644 --- a/srsenb/hdr/phy/phy.h +++ b/srsenb/hdr/phy/phy.h @@ -51,7 +51,7 @@ public: std::string get_type() { return "lte"; }; /* MAC->PHY interface */ - int add_rnti(uint16_t rnti, bool is_temporal = false) final; + int add_rnti(uint16_t rnti, uint32_t pcell_index, bool is_temporal) final; void rem_rnti(uint16_t rnti) final; void set_mch_period_stop(uint32_t stop) final; @@ -62,7 +62,7 @@ public: static uint32_t tti_to_subf(uint32_t tti); void start_plot(); - void set_config_dedicated(uint16_t rnti, const srslte::phy_cfg_t& dedicated); + void set_config_dedicated(uint16_t rnti, const phy_rrc_dedicated_list_t& dedicated_list) override; void get_metrics(phy_metrics_t metrics[ENB_METRICS_MAX_USERS]); diff --git a/srsenb/hdr/phy/phy_common.h b/srsenb/hdr/phy/phy_common.h index d7fe99636..01fbf0056 100644 --- a/srsenb/hdr/phy/phy_common.h +++ b/srsenb/hdr/phy/phy_common.h @@ -134,26 +134,104 @@ public: // Map of pending ACKs for each user typedef struct { - bool is_pending[TTIMOD_SZ][SRSLTE_MAX_TB]; - uint16_t n_pdcch[TTIMOD_SZ]; + srslte_uci_cfg_ack_t ack[SRSLTE_MAX_CARRIERS]; } pending_ack_t; class common_ue { public: - pending_ack_t pending_ack = {}; + pending_ack_t pending_ack[TTIMOD_SZ] = {}; uint8_t ri = 0; + uint32_t pcell_idx = 0; srslte_ra_tb_t last_tb[SRSLTE_MAX_HARQ_PROC] = {}; + std::map scell_map; }; std::map common_ue_db; - void ue_db_add_rnti(uint16_t rnti); + /** + * Adds or modifies a user in the UE database setting. This function requires a list of cells indexes for the UE. The + * first element of the list must be the PCell and the rest will be SCell in the order + * + * @param rnti identifier of the user + * @param cell_index_list List of the eNb cell indexes for carrier aggregation + */ + void ue_db_addmod_rnti(uint16_t rnti, const std::vector& cell_index_list); + + /** + * Removes a whole UE entry from the UE database + * + * @param rnti identifier of the UE + */ void ue_db_rem_rnti(uint16_t rnti); - void ue_db_clear(uint32_t tti); - void ue_db_set_ack_pending(uint32_t tti, uint16_t rnti, uint32_t tb_idx, uint32_t n_pdcch); - bool ue_db_is_ack_pending(uint32_t tti, uint16_t rnti, uint32_t tb_idx, uint32_t* last_n_pdcch = nullptr); - void ue_db_set_ri(uint16_t rnti, uint8_t ri); + + /** + * Removes all the pending ACKs of all the RNTIs for a given TTI + * + * @param tti is the given TTI to clear + */ + void ue_db_clear_tti_pending_ack(uint32_t tti); + + /** + * Sets the pending ACK for a given TTI in a given Component Carrier and user (RNTI is a member of the DCI) + * + * @param tti is the given TTI to fill + * @param cc_idx the carrier where the DCI is scheduled + * @param dci carries the Transport Block and required scheduling information + * + */ + void ue_db_set_ack_pending(uint32_t tti, uint32_t cc_idx, const srslte_dci_dl_t& dci); + + /** + * Requests ACK information for a given RNTI that needs to acknowledge PDSCH transmissions in the cc_idx cell/carrier. + * + * @param tti is the given TTI to fill + * @param cc_idx the carrier where the DCI is scheduled + * @param rnti is the UE identifier + * @param uci_cfg_ack vector pointing at the UCI configuration + * + */ + void ue_db_get_ack_pending(uint32_t tti, + uint32_t cc_idx, + uint16_t rnti, + srslte_uci_cfg_ack_t uci_cfg_ack[SRSLTE_MAX_CARRIERS]); + + /** + * Provides the number of aggregated cells for a given RNTI + * @param rnti UE's RNTI + * @return the number of aggregated cells if the RNTI exists, otherwise it returns 0 + */ + uint32_t ue_db_get_nof_ca_cells(uint16_t rnti); + + /** + * Provides the PCell index of a given UE from its RNTI + * @param rnti UE's RNTI + * @return the index of the PCell if it exists, the number of cells otherwise + */ + uint32_t ue_db_get_cc_pcell(uint16_t rnti); + + /** + * Requests the eNb cell index of given RNTI from its scell_idx + * + * @param rnti the UE temporal ID + * @param scell_idx the UE SCell index, use 0 for PCell index + * @return the eNb cell index if found, the number of eNb cells otherwise + * + */ + uint32_t ue_db_get_cc_scell(uint16_t rnti, uint32_t scell_idx); + + /** + * + * @param rnti + * @param ri + */ + void ue_db_set_ri(uint16_t rnti, uint8_t ri); + + /** + * + * @param rnti + * @return + */ uint8_t ue_db_get_ri(uint16_t rnti); void ue_db_set_last_ul_tb(uint16_t rnti, uint32_t pid, srslte_ra_tb_t tb); diff --git a/srsenb/hdr/phy/sf_worker.h b/srsenb/hdr/phy/sf_worker.h index 14515ad4a..cfd82e552 100644 --- a/srsenb/hdr/phy/sf_worker.h +++ b/srsenb/hdr/phy/sf_worker.h @@ -42,7 +42,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, 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(); @@ -53,7 +53,7 @@ public: int read_pucch_d(cf_t* pusch_d); void start_plot(); - void set_config_dedicated(uint16_t rnti, const srslte::phy_cfg_t& dedicated); + void set_config_dedicated(uint16_t rnti, uint32_t cc_idx, const srslte::phy_cfg_t& dedicated); uint32_t get_metrics(phy_metrics_t metrics[ENB_METRICS_MAX_USERS]); diff --git a/srsenb/hdr/stack/enb_stack_lte.h b/srsenb/hdr/stack/enb_stack_lte.h index e0cf97112..65e6d72e4 100644 --- a/srsenb/hdr/stack/enb_stack_lte.h +++ b/srsenb/hdr/stack/enb_stack_lte.h @@ -69,11 +69,14 @@ public: } int ri_info(uint32_t tti, uint16_t rnti, uint32_t ri_value) final { return mac.ri_info(tti, rnti, ri_value); } int pmi_info(uint32_t tti, uint16_t rnti, uint32_t pmi_value) final { return mac.pmi_info(tti, rnti, pmi_value); } - int cqi_info(uint32_t tti, uint16_t rnti, uint32_t cqi_value) final { return mac.cqi_info(tti, rnti, cqi_value); } - int snr_info(uint32_t tti, uint16_t rnti, float snr_db) final { return mac.snr_info(tti, rnti, snr_db); } - int ack_info(uint32_t tti, uint16_t rnti, uint32_t tb_idx, bool ack) final + int cqi_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t cqi_value) final { - return mac.ack_info(tti, rnti, tb_idx, ack); + return mac.cqi_info(tti, rnti, cc_idx, cqi_value); + } + int snr_info(uint32_t tti, uint16_t rnti, float snr_db) final { return mac.snr_info(tti, rnti, snr_db); } + int ack_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t tb_idx, bool ack) final + { + return mac.ack_info(tti, rnti, cc_idx, tb_idx, ack); } int crc_info(uint32_t tti, uint16_t rnti, uint32_t nof_bytes, bool crc_res) final { diff --git a/srsenb/hdr/stack/mac/mac.h b/srsenb/hdr/stack/mac/mac.h index 0c6857ae2..26c3ef9d4 100644 --- a/srsenb/hdr/stack/mac/mac.h +++ b/srsenb/hdr/stack/mac/mac.h @@ -58,9 +58,9 @@ public: int ri_info(uint32_t tti, uint16_t rnti, uint32_t ri_value); int pmi_info(uint32_t tti, uint16_t rnti, uint32_t pmi_value); - int cqi_info(uint32_t tti, uint16_t rnti, uint32_t cqi_value); + int cqi_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t cqi_value) override; int snr_info(uint32_t tti, uint16_t rnti, float snr); - int ack_info(uint32_t tti, uint16_t rnti, uint32_t tb_idx, bool ack); + int ack_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t tb_idx, bool ack) override; int crc_info(uint32_t tti, uint16_t rnti, uint32_t nof_bytes, bool crc_res); int get_dl_sched(uint32_t tti, dl_sched_list_t& dl_sched_res); diff --git a/srsenb/src/phy/cc_worker.cc b/srsenb/src/phy/cc_worker.cc index bd974a22e..c3b8097b4 100644 --- a/srsenb/src/phy/cc_worker.cc +++ b/srsenb/src/phy/cc_worker.cc @@ -186,7 +186,7 @@ void cc_worker::set_tti(uint32_t tti_) int cc_worker::add_rnti(uint16_t rnti, bool is_temporal) { - if (!is_temporal) { + if (!is_temporal && !ue_db.count(rnti)) { if (srslte_enb_dl_add_rnti(&enb_dl, rnti)) { return -1; } @@ -252,7 +252,10 @@ void cc_worker::set_config_dedicated(uint16_t rnti, const srslte::phy_cfg_t& ded if (ue_db.count(rnti)) { ue_db[rnti]->ul_cfg = dedicated.ul_cfg; + ue_db[rnti]->ul_cfg.pucch.rnti = rnti; + ue_db[rnti]->ul_cfg.pusch.rnti = rnti; ue_db[rnti]->dl_cfg = dedicated.dl_cfg; + ue_db[rnti]->dl_cfg.pdsch.rnti = rnti; } else { Error("Setting config dedicated: rnti=0x%x does not exist\n", rnti); } @@ -320,14 +323,17 @@ bool cc_worker::fill_uci_cfg(uint16_t rnti, bool aperiodic_cqi_request, srslte_u uci_required |= uci_cfg->is_scheduling_request_tti; - // Get pending ACKs with an associated PUSCH transmission - // TODO: Use ue_dl procedures to compute uci_ack_cfg for TDD and CA - for (uint32_t tb = 0; tb < SRSLTE_MAX_TB; tb++) { - uci_cfg->ack[cc_idx].pending_tb[tb] = phy->ue_db_is_ack_pending(tti_rx, rnti, tb, &uci_cfg->ack[cc_idx].ncce[0]); - Debug("ACK: is pending tti=%d, mod=%d, value=%d\n", tti_rx, TTIMOD(tti_rx), uci_cfg->ack[cc_idx].pending_tb[tb]); - if (uci_cfg->ack[cc_idx].pending_tb[tb]) { - uci_cfg->ack[cc_idx].nof_acks++; - uci_required = true; + // Get pending ACKs from PDSCH + phy->ue_db_get_ack_pending(tti_rx, cc_idx, rnti, uci_cfg->ack); + uint32_t nof_total_ack = srslte_uci_cfg_total_ack(uci_cfg); + uci_required |= (nof_total_ack != 0); + + // if UCI is required and the PCell is not the only cell + if (uci_required && nof_total_ack != uci_cfg->ack[0].nof_acks) { + // More than one carrier requires ACKs + for (uint32_t cc = 0; cc < phy->ue_db_get_nof_ca_cells(rnti); cc++) { + // Assume all aggregated carriers are on the same transmission mode + uci_cfg->ack[cc].nof_acks = (ue_db[rnti]->dl_cfg.tm < SRSLTE_TM3) ? 1 : 2; } } @@ -352,12 +358,20 @@ void cc_worker::send_uci_data(uint16_t rnti, srslte_uci_cfg_t* uci_cfg, srslte_u /* If only one ACK is required, it can be for TB0 or TB1 */ uint32_t ack_idx = 0; - for (uint32_t tb = 0; tb < SRSLTE_MAX_TB; tb++) { - if (uci_cfg->ack[cc_idx].pending_tb[tb]) { - bool ack = uci_value->ack.ack_value[ack_idx]; - bool valid = uci_value->ack.valid; - phy->stack->ack_info(tti_rx, rnti, tb, ack && valid); - ack_idx++; + for (uint32_t ue_scell_idx = 0; ue_scell_idx < SRSLTE_MAX_CARRIERS; ue_scell_idx++) { + uint32_t cc_ack_idx = phy->ue_db_get_cc_scell(rnti, ue_scell_idx); + if (cc_ack_idx < phy->get_nof_carriers()) { + + // For each transport block... + for (uint32_t tb = 0; tb < uci_cfg->ack[ue_scell_idx].nof_acks; tb++) { + // Check if the SCell ACK was pending + if (uci_cfg->ack[ue_scell_idx].pending_tb[tb]) { + bool ack = uci_value->ack.ack_value[ack_idx]; + bool valid = uci_value->ack.valid; + phy->stack->ack_info(tti_rx, rnti, cc_ack_idx, tb, ack && valid); + } + ack_idx++; + } } } @@ -379,7 +393,7 @@ void cc_worker::send_uci_data(uint16_t rnti, srslte_uci_cfg_t* uci_cfg, srslte_u cqi_value = uci_value->cqi.subband_ue.wideband_cqi; break; } - phy->stack->cqi_info(tti_rx, rnti, cqi_value); + phy->stack->cqi_info(tti_rx, rnti, 0, cqi_value); } if (uci_cfg->cqi.ri_len) { phy->stack->ri_info(tti_rx, rnti, uci_value->ri); @@ -611,13 +625,9 @@ int cc_worker::encode_pdsch(stack_interface_phy_lte::dl_sched_grant_t* grants, u /* Scales the Resources Elements affected by the power allocation (p_b) */ // srslte_enb_dl_prepare_power_allocation(&enb_dl); - - // Prepare for receive ACK for DL grants in t_tx_dl+4 - phy->ue_db_clear(tti_tx_ul); - for (uint32_t i = 0; i < nof_grants; i++) { uint16_t rnti = grants[i].dci.rnti; - if (rnti) { + if (rnti && ue_db.count(rnti)) { // Compute DL grant if (srslte_ra_dl_dci_to_grant( @@ -638,14 +648,8 @@ int cc_worker::encode_pdsch(stack_interface_phy_lte::dl_sched_grant_t* grants, u // Save pending ACK if (SRSLTE_RNTI_ISUSER(rnti)) { - /* For each TB */ - for (uint32_t tb_idx = 0; tb_idx < SRSLTE_MAX_TB; tb_idx++) { - /* If TB enabled, set pending ACK */ - if (ue_db[rnti]->dl_cfg.pdsch.grant.tb[tb_idx].enabled) { - Debug("ACK: set pending tti=%d, mod=%d\n", tti_tx_ul, TTIMOD(tti_tx_ul)); - phy->ue_db_set_ack_pending(tti_tx_ul, rnti, tb_idx, grants[i].dci.location.ncce); - } - } + // Push whole DCI + phy->ue_db_set_ack_pending(tti_tx_ul, cc_idx, grants[i].dci); } if (LOG_THIS(rnti)) { @@ -657,6 +661,8 @@ int cc_worker::encode_pdsch(stack_interface_phy_lte::dl_sched_grant_t* grants, u // Save metrics stats ue_db[rnti]->metrics_dl(grants[i].dci.tb[0].mcs_idx); + } else { + ERROR("RNTI (x%x) not found in Component Carrier worker %d\n", rnti, cc_idx); } } diff --git a/srsenb/src/phy/phy.cc b/srsenb/src/phy/phy.cc index 6501e93c3..75f24693a 100644 --- a/srsenb/src/phy/phy.cc +++ b/srsenb/src/phy/phy.cc @@ -192,17 +192,18 @@ uint32_t phy::tti_to_subf(uint32_t tti) } /***** MAC->PHY interface **********/ -int phy::add_rnti(uint16_t rnti, bool is_temporal) +int phy::add_rnti(uint16_t rnti, uint32_t pcell_index, bool is_temporal) { if (SRSLTE_RNTI_ISUSER(rnti)) { - workers_common.ue_db_add_rnti(rnti); + workers_common.ue_db_addmod_rnti(rnti, {pcell_index}); } for (uint32_t i = 0; i < nof_workers; i++) { - if (workers[i].add_rnti(rnti, is_temporal)) { + if (workers[i].add_rnti(rnti, pcell_index, is_temporal)) { return SRSLTE_ERROR; } } + return SRSLTE_SUCCESS; } @@ -253,11 +254,29 @@ void phy::get_metrics(phy_metrics_t metrics[ENB_METRICS_MAX_USERS]) /***** RRC->PHY interface **********/ -void phy::set_config_dedicated(uint16_t rnti, const srslte::phy_cfg_t& dedicated) +void phy::set_config_dedicated(uint16_t rnti, const phy_rrc_dedicated_list_t& dedicated_list) { - for (uint32_t i = 0; i < nof_workers; i++) { - workers[i].set_config_dedicated(rnti, dedicated); + // Create list + std::vector scell_idx_list(dedicated_list.size()); + + for (uint32_t i = 0; i < dedicated_list.size(); i++) { + auto& config = dedicated_list[i]; + + // Set SCell index in list + scell_idx_list[i] = config.cc_idx; + + // Configure workers + for (uint32_t w = 0; w < nof_workers; w++) { + // Add RNTI to worker + workers[w].add_rnti(rnti, config.cc_idx, false); + + // Configure RNTI + workers[w].set_config_dedicated(rnti, config.cc_idx, config.phy_cfg); + } } + + // Finally, set UE database + workers_common.ue_db_addmod_rnti(rnti, scell_idx_list); } void phy::configure_mbsfn(sib_type2_s* sib2, sib_type13_r9_s* sib13, mcch_msg_s mcch) diff --git a/srsenb/src/phy/phy_common.cc b/srsenb/src/phy/phy_common.cc index ad2e3a39f..71e8273d4 100644 --- a/srsenb/src/phy/phy_common.cc +++ b/srsenb/src/phy/phy_common.cc @@ -158,31 +158,44 @@ void phy_common::worker_end(uint32_t tti, stack->tti_clock(); } -void phy_common::ue_db_clear(uint32_t tti) +void phy_common::ue_db_clear_tti_pending_ack(uint32_t tti) { - for (auto iter = common_ue_db.begin(); iter != common_ue_db.end(); ++iter) { - pending_ack_t* p = &((common_ue*)&iter->second)->pending_ack; - for (uint32_t tb_idx = 0; tb_idx < SRSLTE_MAX_TB; tb_idx++) { - p->is_pending[TTIMOD(tti)][tb_idx] = false; - } + std::lock_guard lock(user_mutex); + + for (auto& iter : common_ue_db) { + common_ue& ue = iter.second; + + // Reset all pending ACKs in all carriers + ue.pending_ack[TTIMOD(tti)] = {}; } } -void phy_common::ue_db_add_rnti(uint16_t rnti) +void phy_common::ue_db_addmod_rnti(uint16_t rnti, const std::vector& cell_index_list) { std::lock_guard lock(user_mutex); + + // Create new user if did not exist if (!common_ue_db.count(rnti)) { add_rnti(rnti); } + + // Get UE by reference + common_ue& ue = common_ue_db[rnti]; + + // Clear SCell map to avoid overlap from previous calls + ue.scell_map.clear(); + + // Add SCells to map + for (uint32_t i = 0; i < cell_index_list.size(); i++) { + common_ue_db[rnti].scell_map[cell_index_list[i]] = i; + } } // Private function not mutexed void phy_common::add_rnti(uint16_t rnti) { - for (int i = 0; i < TTIMOD_SZ; i++) { - for (uint32_t tb_idx = 0; tb_idx < SRSLTE_MAX_TB; tb_idx++) { - common_ue_db[rnti].pending_ack.is_pending[i][tb_idx] = false; - } + for (auto& pending_ack : common_ue_db[rnti].pending_ack) { + pending_ack = {}; } } @@ -194,30 +207,108 @@ void phy_common::ue_db_rem_rnti(uint16_t rnti) } } -void phy_common::ue_db_set_ack_pending(uint32_t tti, uint16_t rnti, uint32_t tb_idx, uint32_t last_n_pdcch) +void phy_common::ue_db_set_ack_pending(uint32_t tti, uint32_t cc_idx, const srslte_dci_dl_t& dci) { std::lock_guard lock(user_mutex); - if (common_ue_db.count(rnti)) { - common_ue_db[rnti].pending_ack.is_pending[TTIMOD(tti)][tb_idx] = true; - common_ue_db[rnti].pending_ack.n_pdcch[TTIMOD(tti)] = (uint16_t)last_n_pdcch; + + // Check if the UE exists + if (!common_ue_db.count(dci.rnti)) { + return; + } + + // Check Component Carrier is part of UE SCell map + common_ue& ue = common_ue_db[dci.rnti]; + if (!ue.scell_map.count(cc_idx)) { + return; + } + + uint32_t scell_idx = ue.scell_map[cc_idx]; + uint32_t tti_idx = TTIMOD(tti); + pending_ack_t& pending_ack = ue.pending_ack[tti_idx]; + + // Set DCI info + pending_ack.ack[scell_idx].grant_cc_idx = scell_idx; // No cross carrier scheduling supported + pending_ack.ack[scell_idx].ncce[0] = dci.location.ncce; + + // Set TB info + for (uint32_t i = 0; i < SRSLTE_MAX_TB; i++) { + if (SRSLTE_DCI_IS_TB_EN(dci.tb[i])) { + pending_ack.ack[scell_idx].pending_tb[i] = true; + pending_ack.ack[scell_idx].nof_acks++; + } } } -bool phy_common::ue_db_is_ack_pending(uint32_t tti, uint16_t rnti, uint32_t tb_idx, uint32_t* last_n_pdcch) +void phy_common::ue_db_get_ack_pending(uint32_t tti, + uint32_t cc_idx, + uint16_t rnti, + srslte_uci_cfg_ack_t uci_cfg_ack[SRSLTE_MAX_CARRIERS]) { - bool ret = false; std::lock_guard lock(user_mutex); - if (common_ue_db.count(rnti)) { - ret = common_ue_db[rnti].pending_ack.is_pending[TTIMOD(tti)][tb_idx]; - common_ue_db[rnti].pending_ack.is_pending[TTIMOD(tti)][tb_idx] = false; - if (ret && last_n_pdcch) { - *last_n_pdcch = common_ue_db[rnti].pending_ack.n_pdcch[TTIMOD(tti)]; + // Check if the UE exists + if (!common_ue_db.count(rnti)) { + return; + } + + common_ue& ue = common_ue_db[rnti]; + uint32_t tti_idx = TTIMOD(tti); + + // Check Component Carrier is the UE PCell + if (common_ue_db[rnti].pcell_idx == cc_idx) { + srslte_uci_cfg_ack_t* pending_acks = ue.pending_ack[tti_idx].ack; + + for (uint32_t i = 0; i < SRSLTE_MAX_CARRIERS; i++) { + // Copy pending acks + uci_cfg_ack[i] = pending_acks[i]; + + // Reset stored pending acks + pending_acks[i] = {}; + } + } else { + for (uint32_t i = 0; i < SRSLTE_MAX_CARRIERS; i++) { + // Set all to zeros, equivalent to no UCI data + uci_cfg_ack[i] = {}; } } +} + +uint32_t phy_common::ue_db_get_nof_ca_cells(uint16_t rnti) +{ + std::lock_guard lock(user_mutex); + auto ret = 0; + if (common_ue_db.count(rnti)) { + ret = common_ue_db[rnti].scell_map.size(); + } return ret; } +uint32_t phy_common::ue_db_get_cc_pcell(uint16_t rnti) +{ + std::lock_guard lock(user_mutex); + auto ret = static_cast(cell_list.size()); + if (common_ue_db.count(rnti)) { + ret = common_ue_db[rnti].pcell_idx; + } + return ret; +} + +uint32_t phy_common::ue_db_get_cc_scell(uint16_t rnti, uint32_t scell_idx) +{ + std::lock_guard lock(user_mutex); + + if (common_ue_db.count(rnti)) { + auto& ue = common_ue_db[rnti]; + for (auto& it : ue.scell_map) { + if (it.second == scell_idx) { + return it.first; + } + } + } + + return static_cast(cell_list.size()); +} + void phy_common::ue_db_set_ri(uint16_t rnti, uint8_t ri) { std::lock_guard lock(user_mutex); diff --git a/srsenb/src/phy/sf_worker.cc b/srsenb/src/phy/sf_worker.cc index edf20953b..4ac762816 100644 --- a/srsenb/src/phy/sf_worker.cc +++ b/srsenb/src/phy/sf_worker.cc @@ -136,12 +136,16 @@ void sf_worker::set_time(uint32_t tti_, uint32_t tx_worker_cnt_, srslte_timestam } } -int sf_worker::add_rnti(uint16_t rnti, bool is_temporal) +int sf_worker::add_rnti(uint16_t rnti, uint32_t cc_idx, bool is_temporal) { - for (auto& w : cc_workers) { - w->add_rnti(rnti, is_temporal); + int ret = SRSLTE_ERROR; + + if (cc_idx < cc_workers.size()) { + cc_workers[cc_idx]->add_rnti(rnti, is_temporal); + ret = SRSLTE_SUCCESS; } - return SRSLTE_SUCCESS; + + return ret; } void sf_worker::rem_rnti(uint16_t rnti) @@ -156,10 +160,12 @@ uint32_t sf_worker::get_nof_rnti() return cc_workers[0]->get_nof_rnti(); } -void sf_worker::set_config_dedicated(uint16_t rnti, const srslte::phy_cfg_t& dedicated) +void sf_worker::set_config_dedicated(uint16_t rnti, uint32_t cc_idx, const srslte::phy_cfg_t& dedicated) { - for (auto& w : cc_workers) { - w->set_config_dedicated(rnti, dedicated); + if (cc_idx < cc_workers.size()) { + cc_workers[cc_idx]->set_config_dedicated(rnti, dedicated); + } else { + log_h->error("cc_idx %d exceeds the number of carriers (%ld)\n", cc_idx, cc_workers.size()); } } @@ -226,6 +232,9 @@ void sf_worker::work_imp() dl_sf.sf_type = sf_type; dl_sf.non_mbsfn_region = mbsfn_cfg.non_mbsfn_region_length; + // Prepare for receive ACK for DL grants in t_tx_dl+4 + phy->ue_db_clear_tti_pending_ack(tti_tx_ul); + // Process DL for (uint32_t cc = 0; cc < cc_workers.size(); cc++) { cc_workers[cc]->work_dl(dl_sf, phy->dl_grants[t_tx_dl][cc], phy->ul_grants[t_tx_ul][cc], &mbsfn_cfg); diff --git a/srsenb/src/stack/mac/mac.cc b/srsenb/src/stack/mac/mac.cc index 21850d699..950feb170 100644 --- a/srsenb/src/stack/mac/mac.cc +++ b/srsenb/src/stack/mac/mac.cc @@ -207,7 +207,7 @@ int mac::ue_cfg(uint16_t rnti, sched_interface::ue_cfg_t* cfg) ue_db[rnti]->is_phy_added = true; Info("Registering rnti=0x%x to PHY...\n", rnti); // Register new user in PHY - if (phy_h->add_rnti(rnti)) { + if (phy_h->add_rnti(rnti, 0, false)) { Error("Registering new ue rnti=0x%x to PHY\n", rnti); } Info("Done registering rnti=0x%x to PHY...\n", rnti); @@ -304,10 +304,9 @@ void mac::rl_ok(uint16_t rnti) pthread_rwlock_unlock(&rwlock); } -int mac::ack_info(uint32_t tti, uint16_t rnti, uint32_t tb_idx, bool ack) +int mac::ack_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t tb_idx, bool ack) { // TODO: add cc_idx to interface - uint32_t cc_idx = 0; pthread_rwlock_rdlock(&rwlock); log_h->step(tti); uint32_t nof_bytes = scheduler.dl_ack_info(tti, rnti, cc_idx, tb_idx, ack); @@ -388,10 +387,8 @@ int mac::pmi_info(uint32_t tti, uint16_t rnti, uint32_t pmi_value) return ret; } -int mac::cqi_info(uint32_t tti, uint16_t rnti, uint32_t cqi_value) +int mac::cqi_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t cqi_value) { - // TODO: add cc_idx to interface - uint32_t cc_idx = 0; log_h->step(tti); int ret = -1; @@ -484,7 +481,7 @@ int mac::rach_detected(uint32_t tti, uint32_t enb_cc_idx, uint32_t preamble_idx, rrc_h->add_user(rnti, ue_cfg); // Add temporal rnti to the PHY - if (phy_h->add_rnti(rnti, true)) { + if (phy_h->add_rnti(rnti, enb_cc_idx, true)) { Error("Registering temporal-rnti=0x%x to PHY\n", rnti); } diff --git a/srsenb/test/common/dummy_classes.h b/srsenb/test/common/dummy_classes.h index 54be340a8..5104bf6d5 100644 --- a/srsenb/test/common/dummy_classes.h +++ b/srsenb/test/common/dummy_classes.h @@ -121,7 +121,7 @@ public: configure_mbsfn(asn1::rrc::sib_type2_s* sib2, asn1::rrc::sib_type13_r9_s* sib13, asn1::rrc::mcch_msg_s mcch) override { } - void set_config_dedicated(uint16_t rnti, const srslte::phy_cfg_t& dedicated) override {} + void set_config_dedicated(uint16_t rnti, const phy_rrc_dedicated_list_t& dedicated_list) override {} }; class gtpu_dummy : public gtpu_interface_rrc diff --git a/srsenb/test/phy/enb_phy_test.cc b/srsenb/test/phy/enb_phy_test.cc index 1e79784fd..fb97ef25f 100644 --- a/srsenb/test/phy/enb_phy_test.cc +++ b/srsenb/test/phy/enb_phy_test.cc @@ -57,7 +57,7 @@ private: { \ std::unique_lock lock(mutex); \ cvar.notify_all(); \ - log_h.info(#NAME " received\n"); \ + log_h.debug(#NAME " received\n"); \ received_##NAME = true; \ } @@ -69,7 +69,6 @@ private: srslte::log_filter log_h; std::vector ringbuffers_tx; std::vector ringbuffers_rx; - srslte_timestamp_t ts_tx = {}; srslte_timestamp_t ts_rx = {}; double rx_srate = 0.0; bool running = true; @@ -98,13 +97,13 @@ private: CALLBACK(get_info); public: - dummy_radio(uint32_t nof_channels) : log_h("RADIO") + explicit dummy_radio(uint32_t nof_channels) : log_h("RADIO") { log_h.set_level("info"); // Allocate receive ring buffer for (uint32_t i = 0; i < nof_channels; i++) { - srslte_ringbuffer_t* rb = (srslte_ringbuffer_t*)srslte_vec_malloc(sizeof(srslte_ringbuffer_t)); + auto* rb = (srslte_ringbuffer_t*)srslte_vec_malloc(sizeof(srslte_ringbuffer_t)); if (!rb) { ERROR("Allocating ring buffer\n"); } @@ -118,7 +117,7 @@ public: // Allocate transmit ring buffer for (uint32_t i = 0; i < nof_channels; i++) { - srslte_ringbuffer_t* rb = (srslte_ringbuffer_t*)srslte_vec_malloc(sizeof(srslte_ringbuffer_t)); + auto* rb = (srslte_ringbuffer_t*)srslte_vec_malloc(sizeof(srslte_ringbuffer_t)); if (!rb) { ERROR("Allocating ring buffer\n"); } @@ -191,9 +190,6 @@ public: err = srslte_ringbuffer_write(ringbuffers_tx[i], buffer[i], nbytes); } - // Copy new timestamp - ts_tx = tx_time; - // Notify call notify_tx(); @@ -257,6 +253,8 @@ public: class dummy_stack : public srsenb::stack_interface_phy_lte { private: + static constexpr float prob_dl_grant = 0.50f; + std::mutex mutex; std::condition_variable cvar; srslte::log_filter log_h; @@ -265,6 +263,7 @@ private: srslte_softbuffer_rx_t softbuffer_rx = {}; uint8_t* data = nullptr; uint16_t ue_rnti = 0; + srslte_random_t random_gen = nullptr; CALLBACK(sr_detected); CALLBACK(rach_detected); @@ -282,8 +281,17 @@ private: CALLBACK(rl_ok); CALLBACK(tti_clock); + typedef struct { + uint32_t tti; + uint32_t cc_idx; + uint32_t tb_idx; + } tti_dl_info_t; + + std::queue tti_dl_info_sched_queue; + std::queue tti_dl_info_ack_queue; + public: - dummy_stack(uint16_t rnti_) : log_h("STACK"), ue_rnti(rnti_) + explicit dummy_stack(uint16_t rnti_) : log_h("STACK"), ue_rnti(rnti_), random_gen(srslte_random_init(0)) { log_h.set_level("info"); srslte_softbuffer_tx_init(&softbuffer_tx, SRSLTE_MAX_PRB); @@ -321,7 +329,7 @@ public: notify_pmi_info(); return 0; } - int cqi_info(uint32_t tti, uint16_t rnti, uint32_t cqi_value) override + int cqi_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t cqi_value) override { notify_cqi_info(); return 0; @@ -331,8 +339,16 @@ public: notify_snr_info(); return 0; } - int ack_info(uint32_t tti, uint16_t rnti, uint32_t tb_idx, bool ack) override + int ack_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t tb_idx, bool ack) override { + // Push grant info in queue + tti_dl_info_t tti_dl_info = {}; + tti_dl_info.tti = tti; + tti_dl_info.cc_idx = cc_idx; + tti_dl_info.tb_idx = 0; + tti_dl_info_ack_queue.push(tti_dl_info); + + log_h.info("Received ACK tti=%d; rnti=x%x; cc=%d; tb=%d; ack=%d;\n", tti, rnti, cc_idx, tb_idx, ack); notify_ack_info(); return 0; } @@ -343,30 +359,52 @@ public: } int get_dl_sched(uint32_t tti, dl_sched_list_t& dl_sched_res) override { + // Notify test engine + notify_get_dl_sched(); + // Wait for UE tti_sync.wait(); - for (auto& dl_sched : dl_sched_res) { - dl_sched.cfi = 1; - dl_sched.nof_grants = 1; - dl_sched.pdsch[0].softbuffer_tx[0] = &softbuffer_tx; - dl_sched.pdsch[0].softbuffer_tx[1] = &softbuffer_tx; - dl_sched.pdsch[0].dci.location.ncce = 0; - dl_sched.pdsch[0].dci.location.L = 1; - dl_sched.pdsch[0].dci.type0_alloc.rbg_bitmask = 0xffffffff; - dl_sched.pdsch[0].dci.rnti = ue_rnti; - dl_sched.pdsch[0].dci.alloc_type = SRSLTE_RA_ALLOC_TYPE0; - dl_sched.pdsch[0].dci.tb[0].cw_idx = 0; - dl_sched.pdsch[0].dci.tb[0].mcs_idx = 27; - dl_sched.pdsch[0].dci.tb[0].rv = 0; - dl_sched.pdsch[0].dci.tb[0].ndi = 0; - dl_sched.pdsch[0].data[0] = data; - dl_sched.pdsch[0].data[1] = data; - dl_sched.pdsch[0].dci.format = SRSLTE_DCI_FORMAT1; - } + for (uint32_t cc_idx = 0; cc_idx < dl_sched_res.size(); cc_idx++) { + auto& dl_sched = dl_sched_res[cc_idx]; - // Notify test engine - notify_get_dl_sched(); + // Required + dl_sched.cfi = 1; + + // Random decision on whether transmit or not + if (srslte_random_uniform_real_dist(random_gen, 0, 1) < prob_dl_grant) { + dl_sched.nof_grants = 1; + dl_sched.pdsch[0].softbuffer_tx[0] = &softbuffer_tx; + dl_sched.pdsch[0].softbuffer_tx[1] = &softbuffer_tx; + dl_sched.pdsch[0].dci.location.ncce = 0; + dl_sched.pdsch[0].dci.location.L = 1; + dl_sched.pdsch[0].dci.type0_alloc.rbg_bitmask = 0xffffffff; + dl_sched.pdsch[0].dci.rnti = ue_rnti; + dl_sched.pdsch[0].dci.alloc_type = SRSLTE_RA_ALLOC_TYPE0; + dl_sched.pdsch[0].dci.tb[0].cw_idx = 0; + dl_sched.pdsch[0].dci.tb[0].mcs_idx = 27; + dl_sched.pdsch[0].dci.tb[0].rv = 0; + dl_sched.pdsch[0].dci.tb[0].ndi = false; + dl_sched.pdsch[0].dci.tb[1].cw_idx = 1; + dl_sched.pdsch[0].dci.tb[1].mcs_idx = 0; + dl_sched.pdsch[0].dci.tb[1].rv = 1; + dl_sched.pdsch[0].dci.tb[1].ndi = false; + dl_sched.pdsch[0].data[0] = data; + dl_sched.pdsch[0].data[1] = data; + dl_sched.pdsch[0].dci.format = SRSLTE_DCI_FORMAT1; + + // Push grant info in queue + tti_dl_info_t tti_dl_info = {}; + tti_dl_info.tti = tti; + tti_dl_info.cc_idx = cc_idx; + tti_dl_info.tb_idx = 0; + + // Push to queue + tti_dl_info_sched_queue.push(tti_dl_info); + } else { + dl_sched.nof_grants = 0; + } + } return 0; } @@ -388,6 +426,28 @@ public: notify_tti_clock(); tti_sync.increase(); } + int run_tti() + { + // Check ACKs match with grants + while (!tti_dl_info_ack_queue.empty()) { + // Get both Info + tti_dl_info_t& tti_dl_sched = tti_dl_info_sched_queue.front(); + tti_dl_info_t& tti_dl_ack = tti_dl_info_ack_queue.front(); + + // Calculate ACK TTI + tti_dl_sched.tti = (tti_dl_sched.tti + FDD_HARQ_DELAY_MS) % 10240; + + // 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); + + tti_dl_info_sched_queue.pop(); + tti_dl_info_ack_queue.pop(); + } + + return SRSLTE_SUCCESS; + } }; class dummy_ue @@ -412,7 +472,7 @@ public: uint16_t rnti_, const srslte::phy_cfg_t& dedicated_) : radio(&_radio), - log_h("UE PHY"), + log_h("UE PHY", nullptr, true), dedicated(dedicated_) { // Calculate subframe length @@ -487,10 +547,13 @@ public: memset(tx_data, 0, 150000); // Push HARQ delay to radio - for (uint32_t i = 0; i < FDD_HARQ_DELAY_MS; i++) { + for (uint32_t i = 0; i < TX_DELAY; i++) { radio->write_rx(buffers, sf_len); sf_ul_cfg.tti = (sf_ul_cfg.tti + 1) % 10240; // Advance UL TTI too } + for (uint32_t i = 0; i < FDD_HARQ_DELAY_MS; i++) { + radio->write_rx(buffers, sf_len); + } } ~dummy_ue() @@ -521,6 +584,9 @@ public: int ret = SRSLTE_SUCCESS; srslte_uci_data_t uci_data = {}; + // Set logging TTI + log_h.step(sf_dl_cfg.tti); + uci_data.cfg = dedicated.ul_cfg.pucch.uci_cfg; srslte_pdsch_ack_t pdsch_ack = {}; @@ -634,21 +700,25 @@ private: srslte::log_filter log_h; srslte::logger_stdout logger_stdout; uint32_t nof_carriers = 0; + srslte::phy_cfg_t common_dedicated = {}; + uint16_t rnti = 0; public: phy_test_bench(srsenb::phy_args_t& phy_args, srsenb::phy_cfg_t& phy_cfg, - uint16_t rnti, - const srslte::phy_cfg_t& dedicated) : + uint16_t rnti_, + uint32_t pcell_index, + const srslte::phy_cfg_t& dedicated_) : log_h("TEST BENCH"), - stack(rnti), + stack(rnti_), + rnti(rnti_), radio(phy_cfg.phy_cell_cfg.size()), enb_phy(&logger_stdout), - ue_phy(radio, phy_cfg.phy_cell_cfg, rnti, dedicated) + ue_phy(radio, phy_cfg.phy_cell_cfg, rnti_, dedicated_), + nof_carriers(static_cast(phy_cfg.phy_cell_cfg.size())), + common_dedicated(dedicated_) { - nof_carriers = phy_cfg.phy_cell_cfg.size(); - // Always info log_h.set_level("info"); @@ -656,10 +726,17 @@ public: enb_phy.init(phy_args, phy_cfg, &radio, &stack); // Add rnti to enb - enb_phy.add_rnti(rnti, false); + enb_phy.add_rnti(rnti, pcell_index, false); // Configure UE PHY - enb_phy.set_config_dedicated(rnti, dedicated); + uint32_t pcell_idx = 0; + srsenb::phy_interface_rrc_lte::phy_rrc_dedicated_list_t dedicated_list(4); + for (uint32_t i = 0; i < 4; i++) { + common_dedicated.dl_cfg.cqi_report.pmi_idx = 16 + i; + dedicated_list[i].cc_idx = (i + pcell_idx) % phy_cfg.phy_cell_cfg.size(); + dedicated_list[i].phy_cfg = common_dedicated; + } + enb_phy.set_config_dedicated(rnti, dedicated_list); } ~phy_test_bench() @@ -676,6 +753,7 @@ public: TESTASSERT(!stack.get_received_rl_failure()); TESTASSERT(ue_phy.run_tti() >= SRSLTE_SUCCESS); + TESTASSERT(stack.run_tti() >= SRSLTE_SUCCESS); return ret; } @@ -723,7 +801,7 @@ int main(int argc, char** argv) dedicated.ul_cfg.pucch.simul_cqi_ack = true; std::unique_ptr test_bench = - std::unique_ptr(new phy_test_bench(phy_args, phy_cfg, 0x1234, dedicated)); + std::unique_ptr(new phy_test_bench(phy_args, phy_cfg, 0x1234, 0, dedicated)); for (uint32_t i = 0; i < 32; i++) { TESTASSERT(test_bench->run_tti() >= SRSLTE_SUCCESS);