SRSENB: enabled CA PUCCH decode in eNb

master
Xavier Arteaga 5 years ago committed by Xavier Arteaga
parent f10f64215e
commit 231431f569

@ -42,6 +42,9 @@ class mac_interface_phy_lte
public: public:
const static int MAX_GRANTS = 64; const static int MAX_GRANTS = 64;
/**
* DL grant structure per UE
*/
typedef struct { typedef struct {
srslte_dci_dl_t dci; srslte_dci_dl_t dci;
srslte_dci_cfg_t dci_cfg; srslte_dci_cfg_t dci_cfg;
@ -49,12 +52,18 @@ public:
srslte_softbuffer_tx_t* softbuffer_tx[SRSLTE_MAX_TB]; srslte_softbuffer_tx_t* softbuffer_tx[SRSLTE_MAX_TB];
} dl_sched_grant_t; } dl_sched_grant_t;
/**
* DL Scheduling result per cell/carrier
*/
typedef struct { typedef struct {
dl_sched_grant_t pdsch[MAX_GRANTS]; dl_sched_grant_t pdsch[MAX_GRANTS]; //< DL Grants
uint32_t nof_grants; uint32_t nof_grants; //< Number of DL grants
uint32_t cfi; uint32_t cfi; //< Current CFI of the cell, it can vary across cells
} dl_sched_t; // per carrier } dl_sched_t;
/**
* List of DL scheduling results, one entry per cell/carrier
*/
typedef std::vector<dl_sched_t> dl_sched_list_t; typedef std::vector<dl_sched_t> dl_sched_list_t;
typedef struct { typedef struct {
@ -62,6 +71,9 @@ public:
bool ack; bool ack;
} ul_sched_ack_t; } ul_sched_ack_t;
/**
* UL grant information per UE
*/
typedef struct { typedef struct {
srslte_dci_ul_t dci; srslte_dci_ul_t dci;
srslte_dci_cfg_t dci_cfg; srslte_dci_cfg_t dci_cfg;
@ -71,13 +83,19 @@ public:
srslte_softbuffer_rx_t* softbuffer_rx; srslte_softbuffer_rx_t* softbuffer_rx;
} ul_sched_grant_t; } ul_sched_grant_t;
/**
* UL Scheduling result per cell/carrier
*/
typedef struct { typedef struct {
ul_sched_grant_t pusch[MAX_GRANTS]; ul_sched_grant_t pusch[MAX_GRANTS];
ul_sched_ack_t phich[MAX_GRANTS]; ul_sched_ack_t phich[MAX_GRANTS];
uint32_t nof_grants; uint32_t nof_grants;
uint32_t nof_phich; 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_t> ul_sched_list_t; typedef std::vector<ul_sched_t> ul_sched_list_t;
virtual int sr_detected(uint32_t tti, uint16_t rnti) = 0; 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 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 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 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 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; 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 class phy_interface_mac_lte
{ {
public: 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 rem_rnti(uint16_t rnti) = 0;
virtual void set_mch_period_stop(uint32_t stop) = 0; virtual void set_mch_period_stop(uint32_t stop) = 0;
}; };
@ -126,8 +173,23 @@ public:
} phy_rrc_cfg_t; } phy_rrc_cfg_t;
virtual void virtual void
configure_mbsfn(asn1::rrc::sib_type2_s* sib2, asn1::rrc::sib_type13_r9_s* sib13, asn1::rrc::mcch_msg_s mcch) = 0; 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;
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_t> 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 class mac_interface_rrc

@ -33,8 +33,8 @@ typedef struct SRSLTE_API {
} srslte_uci_value_ack_t; } srslte_uci_value_ack_t;
typedef struct SRSLTE_API { typedef struct SRSLTE_API {
bool pending_tb[SRSLTE_MAX_CODEWORDS]; bool pending_tb[SRSLTE_MAX_CODEWORDS]; //< Indicates whether there was a grant that requires an ACK/NACK
uint32_t nof_acks; uint32_t nof_acks; //< Number of transport blocks, deduced from transmission mode
uint32_t ncce[SRSLTE_UCI_MAX_M]; uint32_t ncce[SRSLTE_UCI_MAX_M];
uint32_t N_bundle; uint32_t N_bundle;
uint32_t tdd_ack_M; uint32_t tdd_ack_M;

@ -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 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; srslte_pucch_format_t format = SRSLTE_PUCCH_FORMAT_ERROR;
uint32_t total_ack = srslte_uci_cfg_total_ack(uci_cfg);
// No CQI data // No CQI data
if (!uci_cfg->cqi.data_enable && uci_cfg->cqi.ri_len == 0) { 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 && 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; format = SRSLTE_PUCCH_FORMAT_3;
} }
// 1-bit ACK + optional SR // 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; format = SRSLTE_PUCCH_FORMAT_1A;
} }
// 2-bit ACK + optional SR // 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 format = SRSLTE_PUCCH_FORMAT_1B; // with channel selection if > 2
} }
// If UCI value is provided, use SR signal only, otherwise SR request opportunity // If UCI value is provided, use SR signal only, otherwise SR request opportunity
else if (uci_cfg->is_scheduling_request_tti) { else if (uci_cfg->is_scheduling_request_tti) {
format = SRSLTE_PUCCH_FORMAT_1; format = SRSLTE_PUCCH_FORMAT_1;
} else { } 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 // CQI data
else { else {
// CQI and no ack // CQI and no ack
if (srslte_uci_cfg_total_ack(uci_cfg) == 0) { if (total_ack == 0) {
format = SRSLTE_PUCCH_FORMAT_2; format = SRSLTE_PUCCH_FORMAT_2;
} }
// CQI + 1-bit ACK // 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; format = SRSLTE_PUCCH_FORMAT_2A;
} }
// CQI + 2-bit ACK // CQI + 2-bit ACK
else if (srslte_uci_cfg_total_ack(uci_cfg) == 2) { else if (total_ack == 2) {
format = SRSLTE_PUCCH_FORMAT_2B; format = SRSLTE_PUCCH_FORMAT_2B;
} }
// CQI + 2-bit ACK + cyclic prefix // 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; format = SRSLTE_PUCCH_FORMAT_2B;
} }
} }

@ -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 (ack_info->nof_cc == 1) {
// If only 1 configured cell, report 1 or 2 bits depending on number of detected TB // 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; 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) { } else if (uci_data->cfg.cqi.data_enable && !ack_info->is_pusch_available) {
// 3GPP 36.213 R.15 Section 10.1.1: // 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 // For FDD or for FDD-TDD and primary cell frame structure type 1 and for a UE that is configured with more than

@ -51,7 +51,7 @@ public:
std::string get_type() { return "lte"; }; std::string get_type() { return "lte"; };
/* MAC->PHY interface */ /* 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 rem_rnti(uint16_t rnti) final;
void set_mch_period_stop(uint32_t stop) final; void set_mch_period_stop(uint32_t stop) final;
@ -62,7 +62,7 @@ public:
static uint32_t tti_to_subf(uint32_t tti); static uint32_t tti_to_subf(uint32_t tti);
void start_plot(); 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]); void get_metrics(phy_metrics_t metrics[ENB_METRICS_MAX_USERS]);

@ -134,26 +134,104 @@ public:
// Map of pending ACKs for each user // Map of pending ACKs for each user
typedef struct { typedef struct {
bool is_pending[TTIMOD_SZ][SRSLTE_MAX_TB]; srslte_uci_cfg_ack_t ack[SRSLTE_MAX_CARRIERS];
uint16_t n_pdcch[TTIMOD_SZ];
} pending_ack_t; } pending_ack_t;
class common_ue class common_ue
{ {
public: public:
pending_ack_t pending_ack = {}; pending_ack_t pending_ack[TTIMOD_SZ] = {};
uint8_t ri = 0; uint8_t ri = 0;
uint32_t pcell_idx = 0;
srslte_ra_tb_t last_tb[SRSLTE_MAX_HARQ_PROC] = {}; srslte_ra_tb_t last_tb[SRSLTE_MAX_HARQ_PROC] = {};
std::map<uint32_t, uint32_t> scell_map;
}; };
std::map<uint16_t, common_ue> common_ue_db; std::map<uint16_t, common_ue> 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<uint32_t>& 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_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); * Removes all the pending ACKs of all the RNTIs for a given TTI
void ue_db_set_ri(uint16_t rnti, uint8_t ri); *
* @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); 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); void ue_db_set_last_ul_tb(uint16_t rnti, uint32_t pid, srslte_ra_tb_t tb);

@ -42,7 +42,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, 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();
@ -53,7 +53,7 @@ public:
int read_pucch_d(cf_t* pusch_d); int read_pucch_d(cf_t* pusch_d);
void start_plot(); 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]); uint32_t get_metrics(phy_metrics_t metrics[ENB_METRICS_MAX_USERS]);

@ -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 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 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 cqi_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t cqi_value) final
{
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 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 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, tb_idx, ack); 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 int crc_info(uint32_t tti, uint16_t rnti, uint32_t nof_bytes, bool crc_res) final
{ {

@ -58,9 +58,9 @@ public:
int ri_info(uint32_t tti, uint16_t rnti, uint32_t ri_value); 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 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 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 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); int get_dl_sched(uint32_t tti, dl_sched_list_t& dl_sched_res);

@ -186,7 +186,7 @@ void cc_worker::set_tti(uint32_t tti_)
int cc_worker::add_rnti(uint16_t rnti, bool is_temporal) 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)) { if (srslte_enb_dl_add_rnti(&enb_dl, rnti)) {
return -1; 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)) { if (ue_db.count(rnti)) {
ue_db[rnti]->ul_cfg = dedicated.ul_cfg; 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 = dedicated.dl_cfg;
ue_db[rnti]->dl_cfg.pdsch.rnti = rnti;
} else { } else {
Error("Setting config dedicated: rnti=0x%x does not exist\n", rnti); 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; uci_required |= uci_cfg->is_scheduling_request_tti;
// Get pending ACKs with an associated PUSCH transmission // Get pending ACKs from PDSCH
// TODO: Use ue_dl procedures to compute uci_ack_cfg for TDD and CA phy->ue_db_get_ack_pending(tti_rx, cc_idx, rnti, uci_cfg->ack);
for (uint32_t tb = 0; tb < SRSLTE_MAX_TB; tb++) { uint32_t nof_total_ack = srslte_uci_cfg_total_ack(uci_cfg);
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]); uci_required |= (nof_total_ack != 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]) { // if UCI is required and the PCell is not the only cell
uci_cfg->ack[cc_idx].nof_acks++; if (uci_required && nof_total_ack != uci_cfg->ack[0].nof_acks) {
uci_required = true; // 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 */ /* If only one ACK is required, it can be for TB0 or TB1 */
uint32_t ack_idx = 0; uint32_t ack_idx = 0;
for (uint32_t tb = 0; tb < SRSLTE_MAX_TB; tb++) { for (uint32_t ue_scell_idx = 0; ue_scell_idx < SRSLTE_MAX_CARRIERS; ue_scell_idx++) {
if (uci_cfg->ack[cc_idx].pending_tb[tb]) { uint32_t cc_ack_idx = phy->ue_db_get_cc_scell(rnti, ue_scell_idx);
bool ack = uci_value->ack.ack_value[ack_idx]; if (cc_ack_idx < phy->get_nof_carriers()) {
bool valid = uci_value->ack.valid;
phy->stack->ack_info(tti_rx, rnti, tb, ack && valid); // For each transport block...
ack_idx++; 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; cqi_value = uci_value->cqi.subband_ue.wideband_cqi;
break; 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) { if (uci_cfg->cqi.ri_len) {
phy->stack->ri_info(tti_rx, rnti, uci_value->ri); 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) */ /* Scales the Resources Elements affected by the power allocation (p_b) */
// srslte_enb_dl_prepare_power_allocation(&enb_dl); // 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++) { for (uint32_t i = 0; i < nof_grants; i++) {
uint16_t rnti = grants[i].dci.rnti; uint16_t rnti = grants[i].dci.rnti;
if (rnti) { if (rnti && ue_db.count(rnti)) {
// Compute DL grant // Compute DL grant
if (srslte_ra_dl_dci_to_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 // Save pending ACK
if (SRSLTE_RNTI_ISUSER(rnti)) { if (SRSLTE_RNTI_ISUSER(rnti)) {
/* For each TB */ // Push whole DCI
for (uint32_t tb_idx = 0; tb_idx < SRSLTE_MAX_TB; tb_idx++) { phy->ue_db_set_ack_pending(tti_tx_ul, cc_idx, grants[i].dci);
/* 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);
}
}
} }
if (LOG_THIS(rnti)) { 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 // Save metrics stats
ue_db[rnti]->metrics_dl(grants[i].dci.tb[0].mcs_idx); 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);
} }
} }

@ -192,17 +192,18 @@ uint32_t phy::tti_to_subf(uint32_t tti)
} }
/***** MAC->PHY interface **********/ /***** 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)) { 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++) { 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_ERROR;
} }
} }
return SRSLTE_SUCCESS; return SRSLTE_SUCCESS;
} }
@ -253,11 +254,29 @@ void phy::get_metrics(phy_metrics_t metrics[ENB_METRICS_MAX_USERS])
/***** RRC->PHY interface **********/ /***** 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++) { // Create list
workers[i].set_config_dedicated(rnti, dedicated); std::vector<uint32_t> 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) void phy::configure_mbsfn(sib_type2_s* sib2, sib_type13_r9_s* sib13, mcch_msg_s mcch)

@ -158,31 +158,44 @@ void phy_common::worker_end(uint32_t tti,
stack->tti_clock(); 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) { std::lock_guard<std::mutex> lock(user_mutex);
pending_ack_t* p = &((common_ue*)&iter->second)->pending_ack;
for (uint32_t tb_idx = 0; tb_idx < SRSLTE_MAX_TB; tb_idx++) { for (auto& iter : common_ue_db) {
p->is_pending[TTIMOD(tti)][tb_idx] = false; 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<uint32_t>& cell_index_list)
{ {
std::lock_guard<std::mutex> lock(user_mutex); std::lock_guard<std::mutex> lock(user_mutex);
// Create new user if did not exist
if (!common_ue_db.count(rnti)) { if (!common_ue_db.count(rnti)) {
add_rnti(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 // Private function not mutexed
void phy_common::add_rnti(uint16_t rnti) void phy_common::add_rnti(uint16_t rnti)
{ {
for (int i = 0; i < TTIMOD_SZ; i++) { for (auto& pending_ack : common_ue_db[rnti].pending_ack) {
for (uint32_t tb_idx = 0; tb_idx < SRSLTE_MAX_TB; tb_idx++) { pending_ack = {};
common_ue_db[rnti].pending_ack.is_pending[i][tb_idx] = false;
}
} }
} }
@ -194,28 +207,106 @@ 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<std::mutex> lock(user_mutex); std::lock_guard<std::mutex> lock(user_mutex);
// 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++;
}
}
}
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])
{
std::lock_guard<std::mutex> lock(user_mutex);
// 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<std::mutex> lock(user_mutex);
auto ret = 0;
if (common_ue_db.count(rnti)) { if (common_ue_db.count(rnti)) {
common_ue_db[rnti].pending_ack.is_pending[TTIMOD(tti)][tb_idx] = true; ret = common_ue_db[rnti].scell_map.size();
common_ue_db[rnti].pending_ack.n_pdcch[TTIMOD(tti)] = (uint16_t)last_n_pdcch;
} }
return ret;
} }
bool phy_common::ue_db_is_ack_pending(uint32_t tti, uint16_t rnti, uint32_t tb_idx, uint32_t* last_n_pdcch) uint32_t phy_common::ue_db_get_cc_pcell(uint16_t rnti)
{ {
bool ret = false;
std::lock_guard<std::mutex> lock(user_mutex); std::lock_guard<std::mutex> lock(user_mutex);
auto ret = static_cast<uint32_t>(cell_list.size());
if (common_ue_db.count(rnti)) { if (common_ue_db.count(rnti)) {
ret = common_ue_db[rnti].pending_ack.is_pending[TTIMOD(tti)][tb_idx]; ret = common_ue_db[rnti].pcell_idx;
common_ue_db[rnti].pending_ack.is_pending[TTIMOD(tti)][tb_idx] = false; }
return ret;
}
uint32_t phy_common::ue_db_get_cc_scell(uint16_t rnti, uint32_t scell_idx)
{
std::lock_guard<std::mutex> lock(user_mutex);
if (ret && last_n_pdcch) { if (common_ue_db.count(rnti)) {
*last_n_pdcch = common_ue_db[rnti].pending_ack.n_pdcch[TTIMOD(tti)]; auto& ue = common_ue_db[rnti];
for (auto& it : ue.scell_map) {
if (it.second == scell_idx) {
return it.first;
}
} }
} }
return ret;
return static_cast<uint32_t>(cell_list.size());
} }
void phy_common::ue_db_set_ri(uint16_t rnti, uint8_t ri) void phy_common::ue_db_set_ri(uint16_t rnti, uint8_t ri)

@ -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) { int ret = SRSLTE_ERROR;
w->add_rnti(rnti, is_temporal);
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) 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(); 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) { if (cc_idx < cc_workers.size()) {
w->set_config_dedicated(rnti, dedicated); 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.sf_type = sf_type;
dl_sf.non_mbsfn_region = mbsfn_cfg.non_mbsfn_region_length; 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 // Process DL
for (uint32_t cc = 0; cc < cc_workers.size(); cc++) { 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); cc_workers[cc]->work_dl(dl_sf, phy->dl_grants[t_tx_dl][cc], phy->ul_grants[t_tx_ul][cc], &mbsfn_cfg);

@ -207,7 +207,7 @@ int mac::ue_cfg(uint16_t rnti, sched_interface::ue_cfg_t* cfg)
ue_db[rnti]->is_phy_added = true; ue_db[rnti]->is_phy_added = true;
Info("Registering rnti=0x%x to PHY...\n", rnti); Info("Registering rnti=0x%x to PHY...\n", rnti);
// Register new user in PHY // 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); Error("Registering new ue rnti=0x%x to PHY\n", rnti);
} }
Info("Done registering 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); 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 // TODO: add cc_idx to interface
uint32_t cc_idx = 0;
pthread_rwlock_rdlock(&rwlock); pthread_rwlock_rdlock(&rwlock);
log_h->step(tti); log_h->step(tti);
uint32_t nof_bytes = scheduler.dl_ack_info(tti, rnti, cc_idx, tb_idx, ack); 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; 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); log_h->step(tti);
int ret = -1; 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); rrc_h->add_user(rnti, ue_cfg);
// Add temporal rnti to the PHY // 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); Error("Registering temporal-rnti=0x%x to PHY\n", rnti);
} }

@ -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 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 class gtpu_dummy : public gtpu_interface_rrc

@ -57,7 +57,7 @@ private:
{ \ { \
std::unique_lock<std::mutex> lock(mutex); \ std::unique_lock<std::mutex> lock(mutex); \
cvar.notify_all(); \ cvar.notify_all(); \
log_h.info(#NAME " received\n"); \ log_h.debug(#NAME " received\n"); \
received_##NAME = true; \ received_##NAME = true; \
} }
@ -69,7 +69,6 @@ private:
srslte::log_filter log_h; srslte::log_filter log_h;
std::vector<srslte_ringbuffer_t*> ringbuffers_tx; std::vector<srslte_ringbuffer_t*> ringbuffers_tx;
std::vector<srslte_ringbuffer_t*> ringbuffers_rx; std::vector<srslte_ringbuffer_t*> ringbuffers_rx;
srslte_timestamp_t ts_tx = {};
srslte_timestamp_t ts_rx = {}; srslte_timestamp_t ts_rx = {};
double rx_srate = 0.0; double rx_srate = 0.0;
bool running = true; bool running = true;
@ -98,13 +97,13 @@ private:
CALLBACK(get_info); CALLBACK(get_info);
public: 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"); log_h.set_level("info");
// Allocate receive ring buffer // Allocate receive ring buffer
for (uint32_t i = 0; i < nof_channels; i++) { 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) { if (!rb) {
ERROR("Allocating ring buffer\n"); ERROR("Allocating ring buffer\n");
} }
@ -118,7 +117,7 @@ public:
// Allocate transmit ring buffer // Allocate transmit ring buffer
for (uint32_t i = 0; i < nof_channels; i++) { 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) { if (!rb) {
ERROR("Allocating ring buffer\n"); ERROR("Allocating ring buffer\n");
} }
@ -191,9 +190,6 @@ public:
err = srslte_ringbuffer_write(ringbuffers_tx[i], buffer[i], nbytes); err = srslte_ringbuffer_write(ringbuffers_tx[i], buffer[i], nbytes);
} }
// Copy new timestamp
ts_tx = tx_time;
// Notify call // Notify call
notify_tx(); notify_tx();
@ -257,6 +253,8 @@ public:
class dummy_stack : public srsenb::stack_interface_phy_lte class dummy_stack : public srsenb::stack_interface_phy_lte
{ {
private: private:
static constexpr float prob_dl_grant = 0.50f;
std::mutex mutex; std::mutex mutex;
std::condition_variable cvar; std::condition_variable cvar;
srslte::log_filter log_h; srslte::log_filter log_h;
@ -265,6 +263,7 @@ private:
srslte_softbuffer_rx_t softbuffer_rx = {}; srslte_softbuffer_rx_t softbuffer_rx = {};
uint8_t* data = nullptr; uint8_t* data = nullptr;
uint16_t ue_rnti = 0; uint16_t ue_rnti = 0;
srslte_random_t random_gen = nullptr;
CALLBACK(sr_detected); CALLBACK(sr_detected);
CALLBACK(rach_detected); CALLBACK(rach_detected);
@ -282,8 +281,17 @@ private:
CALLBACK(rl_ok); CALLBACK(rl_ok);
CALLBACK(tti_clock); 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_t> tti_dl_info_sched_queue;
std::queue<tti_dl_info_t> tti_dl_info_ack_queue;
public: 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"); log_h.set_level("info");
srslte_softbuffer_tx_init(&softbuffer_tx, SRSLTE_MAX_PRB); srslte_softbuffer_tx_init(&softbuffer_tx, SRSLTE_MAX_PRB);
@ -321,7 +329,7 @@ public:
notify_pmi_info(); notify_pmi_info();
return 0; 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(); notify_cqi_info();
return 0; return 0;
@ -331,8 +339,16 @@ public:
notify_snr_info(); notify_snr_info();
return 0; 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(); notify_ack_info();
return 0; return 0;
} }
@ -343,31 +359,53 @@ public:
} }
int get_dl_sched(uint32_t tti, dl_sched_list_t& dl_sched_res) override 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 // Wait for UE
tti_sync.wait(); tti_sync.wait();
for (auto& dl_sched : dl_sched_res) { for (uint32_t cc_idx = 0; cc_idx < dl_sched_res.size(); cc_idx++) {
dl_sched.cfi = 1; auto& dl_sched = dl_sched_res[cc_idx];
dl_sched.nof_grants = 1;
dl_sched.pdsch[0].softbuffer_tx[0] = &softbuffer_tx; // Required
dl_sched.pdsch[0].softbuffer_tx[1] = &softbuffer_tx; dl_sched.cfi = 1;
dl_sched.pdsch[0].dci.location.ncce = 0;
dl_sched.pdsch[0].dci.location.L = 1; // Random decision on whether transmit or not
dl_sched.pdsch[0].dci.type0_alloc.rbg_bitmask = 0xffffffff; if (srslte_random_uniform_real_dist(random_gen, 0, 1) < prob_dl_grant) {
dl_sched.pdsch[0].dci.rnti = ue_rnti; dl_sched.nof_grants = 1;
dl_sched.pdsch[0].dci.alloc_type = SRSLTE_RA_ALLOC_TYPE0; dl_sched.pdsch[0].softbuffer_tx[0] = &softbuffer_tx;
dl_sched.pdsch[0].dci.tb[0].cw_idx = 0; dl_sched.pdsch[0].softbuffer_tx[1] = &softbuffer_tx;
dl_sched.pdsch[0].dci.tb[0].mcs_idx = 27; dl_sched.pdsch[0].dci.location.ncce = 0;
dl_sched.pdsch[0].dci.tb[0].rv = 0; dl_sched.pdsch[0].dci.location.L = 1;
dl_sched.pdsch[0].dci.tb[0].ndi = 0; dl_sched.pdsch[0].dci.type0_alloc.rbg_bitmask = 0xffffffff;
dl_sched.pdsch[0].data[0] = data; dl_sched.pdsch[0].dci.rnti = ue_rnti;
dl_sched.pdsch[0].data[1] = data; dl_sched.pdsch[0].dci.alloc_type = SRSLTE_RA_ALLOC_TYPE0;
dl_sched.pdsch[0].dci.format = SRSLTE_DCI_FORMAT1; 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;
}
} }
// Notify test engine
notify_get_dl_sched();
return 0; return 0;
} }
int get_mch_sched(uint32_t tti, bool is_mcch, dl_sched_list_t& dl_sched_res) override int get_mch_sched(uint32_t tti, bool is_mcch, dl_sched_list_t& dl_sched_res) override
@ -388,6 +426,28 @@ public:
notify_tti_clock(); notify_tti_clock();
tti_sync.increase(); 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 class dummy_ue
@ -412,7 +472,7 @@ public:
uint16_t rnti_, uint16_t rnti_,
const srslte::phy_cfg_t& dedicated_) : const srslte::phy_cfg_t& dedicated_) :
radio(&_radio), radio(&_radio),
log_h("UE PHY"), log_h("UE PHY", nullptr, true),
dedicated(dedicated_) dedicated(dedicated_)
{ {
// Calculate subframe length // Calculate subframe length
@ -487,10 +547,13 @@ public:
memset(tx_data, 0, 150000); memset(tx_data, 0, 150000);
// Push HARQ delay to radio // 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); radio->write_rx(buffers, sf_len);
sf_ul_cfg.tti = (sf_ul_cfg.tti + 1) % 10240; // Advance UL TTI too 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() ~dummy_ue()
@ -521,6 +584,9 @@ public:
int ret = SRSLTE_SUCCESS; int ret = SRSLTE_SUCCESS;
srslte_uci_data_t uci_data = {}; 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; uci_data.cfg = dedicated.ul_cfg.pucch.uci_cfg;
srslte_pdsch_ack_t pdsch_ack = {}; srslte_pdsch_ack_t pdsch_ack = {};
@ -634,21 +700,25 @@ private:
srslte::log_filter log_h; srslte::log_filter log_h;
srslte::logger_stdout logger_stdout; srslte::logger_stdout logger_stdout;
uint32_t nof_carriers = 0; uint32_t nof_carriers = 0;
srslte::phy_cfg_t common_dedicated = {};
uint16_t rnti = 0;
public: public:
phy_test_bench(srsenb::phy_args_t& phy_args, phy_test_bench(srsenb::phy_args_t& phy_args,
srsenb::phy_cfg_t& phy_cfg, srsenb::phy_cfg_t& phy_cfg,
uint16_t rnti, uint16_t rnti_,
const srslte::phy_cfg_t& dedicated) : uint32_t pcell_index,
const srslte::phy_cfg_t& dedicated_) :
log_h("TEST BENCH"), log_h("TEST BENCH"),
stack(rnti), stack(rnti_),
rnti(rnti_),
radio(phy_cfg.phy_cell_cfg.size()), radio(phy_cfg.phy_cell_cfg.size()),
enb_phy(&logger_stdout), 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<uint32_t>(phy_cfg.phy_cell_cfg.size())),
common_dedicated(dedicated_)
{ {
nof_carriers = phy_cfg.phy_cell_cfg.size();
// Always info // Always info
log_h.set_level("info"); log_h.set_level("info");
@ -656,10 +726,17 @@ public:
enb_phy.init(phy_args, phy_cfg, &radio, &stack); enb_phy.init(phy_args, phy_cfg, &radio, &stack);
// Add rnti to enb // Add rnti to enb
enb_phy.add_rnti(rnti, false); enb_phy.add_rnti(rnti, pcell_index, false);
// Configure UE PHY // 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() ~phy_test_bench()
@ -676,6 +753,7 @@ public:
TESTASSERT(!stack.get_received_rl_failure()); TESTASSERT(!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);
return ret; return ret;
} }
@ -723,7 +801,7 @@ int main(int argc, char** argv)
dedicated.ul_cfg.pucch.simul_cqi_ack = true; dedicated.ul_cfg.pucch.simul_cqi_ack = true;
std::unique_ptr<phy_test_bench> test_bench = std::unique_ptr<phy_test_bench> test_bench =
std::unique_ptr<phy_test_bench>(new phy_test_bench(phy_args, phy_cfg, 0x1234, dedicated)); std::unique_ptr<phy_test_bench>(new phy_test_bench(phy_args, phy_cfg, 0x1234, 0, dedicated));
for (uint32_t i = 0; i < 32; i++) { for (uint32_t i = 0; i < 32; i++) {
TESTASSERT(test_bench->run_tti() >= SRSLTE_SUCCESS); TESTASSERT(test_bench->run_tti() >= SRSLTE_SUCCESS);

Loading…
Cancel
Save