From 784bf81a1a2186a9dcc62d5ab9eac21d3b00a6b3 Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Tue, 7 Apr 2020 17:26:32 +0200 Subject: [PATCH] Multiple fixes HARQ ACK/NACK feedback and CSI reporting for MIMO and CA --- lib/include/srslte/phy/enb/enb_dl.h | 7 +- lib/include/srslte/phy/phch/cqi.h | 1 - lib/src/phy/enb/enb_dl.c | 144 +++++++++++--- lib/src/phy/phch/cqi.c | 2 +- lib/src/phy/phch/dci.c | 6 +- lib/src/phy/phch/sch.c | 4 +- lib/src/phy/ue/ue_dl.c | 280 ++++++++++++++++++---------- srsenb/hdr/phy/phy_ue_db.h | 2 +- srsenb/src/phy/phy_ue_db.cc | 57 +++--- srsenb/test/phy/CMakeLists.txt | 61 +++++- srsenb/test/phy/enb_phy_test.cc | 258 +++++++++++++++++-------- 11 files changed, 572 insertions(+), 250 deletions(-) diff --git a/lib/include/srslte/phy/enb/enb_dl.h b/lib/include/srslte/phy/enb/enb_dl.h index 3c16df148..4ceee5b45 100644 --- a/lib/include/srslte/phy/enb/enb_dl.h +++ b/lib/include/srslte/phy/enb/enb_dl.h @@ -151,10 +151,13 @@ SRSLTE_API void srslte_enb_dl_gen_ack(const srslte_cell_t* cell, * info itself. Note that it expects that the HARQ-ACK info has been set prior the UCI Data decoding. * * @param cell points to the physical layer cell parameters + * @param uci_cfg points to the UCI configration * @param uci_value points to the received UCI values * @param ack_info is the HARQ-ACK information */ -SRSLTE_API void -srslte_enb_dl_get_ack(const srslte_cell_t* cell, const srslte_uci_value_t* uci_value, srslte_pdsch_ack_t* pdsch_ack); +SRSLTE_API void srslte_enb_dl_get_ack(const srslte_cell_t* cell, + const srslte_uci_cfg_t* uci_cfg, + const srslte_uci_value_t* uci_value, + srslte_pdsch_ack_t* pdsch_ack); #endif // SRSLTE_ENB_DL_H diff --git a/lib/include/srslte/phy/phch/cqi.h b/lib/include/srslte/phy/phch/cqi.h index 78008cc1d..3fc970cb1 100644 --- a/lib/include/srslte/phy/phch/cqi.h +++ b/lib/include/srslte/phy/phch/cqi.h @@ -120,7 +120,6 @@ typedef enum { typedef struct SRSLTE_API { bool data_enable; - bool ri_present; bool pmi_present; bool four_antenna_ports; ///< If cell has 4 antenna ports then true otherwise false bool rank_is_not_one; ///< If rank > 1 then true otherwise false diff --git a/lib/src/phy/enb/enb_dl.c b/lib/src/phy/enb/enb_dl.c index 519851c3f..f16873853 100644 --- a/lib/src/phy/enb/enb_dl.c +++ b/lib/src/phy/enb/enb_dl.c @@ -421,7 +421,7 @@ bool srslte_enb_dl_gen_cqi_periodic(const srslte_cell_t* cell, { bool cqi_enabled = false; if (srslte_cqi_periodic_ri_send(&dl_cfg->cqi_report, tti, cell->frame_type)) { - cqi_cfg->ri_len = 1; /* Asumes only 1 bit for RI */ + cqi_cfg->ri_len = srslte_ri_nof_bits(cell); cqi_enabled = true; } else if (srslte_cqi_periodic_send(&dl_cfg->cqi_report, tti, cell->frame_type)) { cqi_cfg->type = SRSLTE_CQI_TYPE_WIDEBAND; @@ -445,7 +445,7 @@ bool srslte_enb_dl_gen_cqi_aperiodic(const srslte_cell_t* cell, cqi_cfg->type = SRSLTE_CQI_TYPE_SUBBAND_HL; if (dl_cfg->tm == SRSLTE_TM3 || dl_cfg->tm == SRSLTE_TM4) { - cqi_cfg->ri_present = true; + cqi_cfg->ri_len = srslte_ri_nof_bits(cell); } cqi_cfg->N = (cell->nof_prb > 7) ? srslte_cqi_hl_get_no_subbands(cell->nof_prb) : 0; cqi_cfg->four_antenna_ports = (cell->nof_ports == 4); @@ -493,45 +493,135 @@ void srslte_enb_dl_gen_ack(const srslte_cell_t* cell, *uci_cfg = uci_data.cfg; } -static void get_ack_fdd(const srslte_uci_value_t* uci_value, srslte_pdsch_ack_t* pdsch_ack) +static void enb_dl_get_ack_fdd_all_spatial_bundling(const srslte_uci_value_t* uci_value, + srslte_pdsch_ack_t* pdsch_ack, + uint32_t nof_tb) { - uint32_t nof_tb = 1; - if (pdsch_ack->transmission_mode > SRSLTE_TM2) { - nof_tb = SRSLTE_MAX_CODEWORDS; - } - - // Second clause: When 2 CC are configured with PUCCH CS mode and SR is also requested, bundle spatial codewords - if (pdsch_ack->nof_cc == SRSLTE_PUCCH_CS_MAX_CARRIERS && uci_value->scheduling_request == true && - pdsch_ack->ack_nack_feedback_mode == SRSLTE_PUCCH_ACK_NACK_FEEDBACK_MODE_CS) { - for (uint32_t cc_idx = 0; cc_idx < pdsch_ack->nof_cc; cc_idx++) { - if (pdsch_ack->cc[cc_idx].m[0].present) { - if (uci_value->ack.ack_value[cc_idx] == 1) { - for (uint32_t tb = 0; tb < nof_tb; tb++) { + for (uint32_t cc_idx = 0; cc_idx < pdsch_ack->nof_cc; cc_idx++) { + if (pdsch_ack->cc[cc_idx].m[0].present) { + if (uci_value->ack.ack_value[cc_idx] == 1) { + for (uint32_t tb = 0; tb < nof_tb; tb++) { + // Check that TB was transmitted + if (pdsch_ack->cc[cc_idx].m[0].value[tb] != 2) { pdsch_ack->cc[cc_idx].m[0].value[tb] = uci_value->ack.ack_value[cc_idx]; } } } } - } else { - // By default, in FDD we just pass through all HARQ-ACK bits - uint32_t n = 0; - for (uint32_t cc_idx = 0; cc_idx < pdsch_ack->nof_cc; cc_idx++) { - for (uint32_t tb = 0; tb < nof_tb; tb++, n++) { - if (pdsch_ack->cc[cc_idx].m[0].present) { - pdsch_ack->cc[cc_idx].m[0].value[tb] = uci_value->ack.ack_value[n]; - } - } - } + } +} + +static void +enb_dl_get_ack_fdd_pcell_skip_drx(const srslte_uci_value_t* uci_value, srslte_pdsch_ack_t* pdsch_ack, uint32_t nof_tb) +{ + if (pdsch_ack->cc[0].m[0].present) { + if (uci_value->ack.ack_value[0] == 1) { + for (uint32_t tb = 0; tb < nof_tb; tb++) { + // Check that TB was transmitted + if (pdsch_ack->cc[0].m[0].value[tb] != 2) { + pdsch_ack->cc[0].m[0].value[tb] = uci_value->ack.ack_value[0]; + } + } + } + } +} + +static void +enb_dl_get_ack_fdd_all_keep_drx(const srslte_uci_value_t* uci_value, srslte_pdsch_ack_t* pdsch_ack, uint32_t nof_tb) +{ + for (uint32_t cc_idx = 0; cc_idx < pdsch_ack->nof_cc; cc_idx++) { + if (pdsch_ack->cc[cc_idx].m[0].present) { + if (uci_value->ack.ack_value[cc_idx] == 1) { + for (uint32_t tb = 0; tb < nof_tb; tb++) { + // Check that TB was transmitted + if (pdsch_ack->cc[cc_idx].m[0].value[tb] != 2) { + pdsch_ack->cc[cc_idx].m[0].value[tb] = uci_value->ack.ack_value[cc_idx * nof_tb + tb]; + } + } + } + } + } +} + +static void +get_ack_fdd(const srslte_uci_cfg_t* uci_cfg, const srslte_uci_value_t* uci_value, srslte_pdsch_ack_t* pdsch_ack) +{ + // Number of transport blocks for the current Transmission Mode + uint32_t nof_tb = 1; + if (pdsch_ack->transmission_mode > SRSLTE_TM2) { + nof_tb = SRSLTE_MAX_CODEWORDS; + } + + // Count number of transmissions + uint32_t tb_count = 0; // All transmissions + uint32_t tb_count_cc0 = 0; // Transmissions on PCell + for (uint32_t cc_idx = 0; cc_idx < pdsch_ack->nof_cc; cc_idx++) { + for (uint32_t tb = 0; tb < nof_tb; tb++) { + if (pdsch_ack->cc[cc_idx].m[0].present && pdsch_ack->cc[cc_idx].m[0].value[tb] != 2) { + tb_count++; + } + + // Save primary cell number of TB + if (cc_idx == 0) { + tb_count_cc0 = tb_count; + } + } + } + + // Does CSI report need to be transmitted? + bool csi_report = uci_cfg->cqi.data_enable || uci_cfg->cqi.ri_len; + + switch (pdsch_ack->ack_nack_feedback_mode) { + + case SRSLTE_PUCCH_ACK_NACK_FEEDBACK_MODE_NORMAL: + // Get ACK from PCell only, skipping DRX + enb_dl_get_ack_fdd_pcell_skip_drx(uci_value, pdsch_ack, nof_tb); + break; + case SRSLTE_PUCCH_ACK_NACK_FEEDBACK_MODE_CS: + if (pdsch_ack->nof_cc == 1) { + enb_dl_get_ack_fdd_pcell_skip_drx(uci_value, pdsch_ack, nof_tb); + } else if (pdsch_ack->is_pusch_available) { + enb_dl_get_ack_fdd_all_keep_drx(uci_value, pdsch_ack, nof_tb); + } else if (uci_value->scheduling_request) { + // For FDD with PUCCH format 1b with channel selection, when both HARQ-ACK and SR are transmitted in the same + // sub-frame a UE shall transmit the HARQ-ACK on its assigned HARQ-ACK PUCCH resource with channel selection as + // defined in subclause 10.1.2.2.1 for a negative SR transmission and transmit one HARQ-ACK bit per serving cell + // on its assigned SR PUCCH resource for a positive SR transmission according to the following: + // − if only one transport block or a PDCCH indicating downlink SPS release is detected on a serving cell, the + // HARQ-ACK bit for the serving cell is the HARQ-ACK bit corresponding to the transport block or the PDCCH + // indicating downlink SPS release; + // − if two transport blocks are received on a serving cell, the HARQ-ACK bit for the serving cell is generated + // by spatially bundling the HARQ-ACK bits corresponding to the transport blocks; + // − if neither PDSCH transmission for which HARQ-ACK response shall be provided nor PDCCH indicating + // downlink SPS release is detected for a serving cell, the HARQ-ACK bit for the serving cell is set to NACK; + enb_dl_get_ack_fdd_all_spatial_bundling(uci_value, pdsch_ack, nof_tb); + } else if (csi_report) { + enb_dl_get_ack_fdd_pcell_skip_drx(uci_value, pdsch_ack, nof_tb); + } else { + enb_dl_get_ack_fdd_all_keep_drx(uci_value, pdsch_ack, nof_tb); + } + break; + case SRSLTE_PUCCH_ACK_NACK_FEEDBACK_MODE_PUCCH3: + if (tb_count == tb_count_cc0) { + enb_dl_get_ack_fdd_pcell_skip_drx(uci_value, pdsch_ack, nof_tb); + } else { + enb_dl_get_ack_fdd_all_keep_drx(uci_value, pdsch_ack, nof_tb); + } + break; + case SRSLTE_PUCCH_ACK_NACK_FEEDBACK_MODE_ERROR: + default:; // Do nothing + break; } } void srslte_enb_dl_get_ack(const srslte_cell_t* cell, + const srslte_uci_cfg_t* uci_cfg, const srslte_uci_value_t* uci_value, srslte_pdsch_ack_t* pdsch_ack) { if (cell->frame_type == SRSLTE_FDD) { - get_ack_fdd(uci_value, pdsch_ack); + get_ack_fdd(uci_cfg, uci_value, pdsch_ack); } else { ERROR("Not implemented for TDD\n"); } -} \ No newline at end of file +} diff --git a/lib/src/phy/phch/cqi.c b/lib/src/phy/phch/cqi.c index 2cb837bd9..29ae53d79 100644 --- a/lib/src/phy/phch/cqi.c +++ b/lib/src/phy/phch/cqi.c @@ -322,7 +322,7 @@ int srslte_cqi_size(srslte_cqi_cfg_t* cfg) int size = 0; if (!cfg->data_enable) { - return 0; + return cfg->ri_len; } switch (cfg->type) { diff --git a/lib/src/phy/phch/dci.c b/lib/src/phy/phch/dci.c index 01ccedcb2..c39df14a8 100644 --- a/lib/src/phy/phch/dci.c +++ b/lib/src/phy/phch/dci.c @@ -1205,7 +1205,7 @@ static int dci_format2AB_unpack(srslte_cell_t* cell, dci->pinfo = srslte_bit_pack(&y, precoding_bits_f2a(cell->nof_ports)); } - // Apply TB swap table + // Apply TB swap table according to 3GPP 36.212 R8, section 5.3.3.1.5 if (nof_tb == 2) { // Table 5.3.3.1.5-1 for (uint32_t i = 0; i < SRSLTE_MAX_CODEWORDS; i++) { @@ -1213,10 +1213,6 @@ static int dci_format2AB_unpack(srslte_cell_t* cell, } } else { // Table 5.3.3.1.5-2 - if (!SRSLTE_DCI_IS_TB_EN(dci->tb[0])) { - dci->tb[0] = dci->tb[1]; - } - SRSLTE_DCI_TB_DISABLE(dci->tb[1]); for (uint32_t i = 0; i < SRSLTE_MAX_CODEWORDS; i++) { dci->tb[i].cw_idx = 0; } diff --git a/lib/src/phy/phch/sch.c b/lib/src/phy/phch/sch.c index e0d1e84a4..dffad4d88 100644 --- a/lib/src/phy/phch/sch.c +++ b/lib/src/phy/phch/sch.c @@ -1025,7 +1025,7 @@ static int uci_decode_ri_ack(srslte_sch_t* q, // If there is RI and CQI, assume RI = 1 for the purpose of RI/ACK decoding (3GPP 36.212 Clause 5.2.4.1. ) if (cfg->uci_cfg.cqi.data_enable) { - if (cfg->uci_cfg.cqi.type == SRSLTE_CQI_TYPE_SUBBAND_HL && cfg->uci_cfg.cqi.ri_present) { + if (cfg->uci_cfg.cqi.type == SRSLTE_CQI_TYPE_SUBBAND_HL && cfg->uci_cfg.cqi.ri_len) { cfg->uci_cfg.cqi.rank_is_not_one = false; } } @@ -1075,7 +1075,7 @@ static int uci_decode_ri_ack(srslte_sch_t* q, // Now set correct RI if (cfg->uci_cfg.cqi.data_enable) { - if (cfg->uci_cfg.cqi.type == SRSLTE_CQI_TYPE_SUBBAND_HL && cfg->uci_cfg.cqi.ri_present) { + if (cfg->uci_cfg.cqi.type == SRSLTE_CQI_TYPE_SUBBAND_HL && cfg->uci_cfg.cqi.ri_len) { cfg->uci_cfg.cqi.rank_is_not_one = uci_data->ri > 0; } } diff --git a/lib/src/phy/ue/ue_dl.c b/lib/src/phy/ue/ue_dl.c index c94531346..a1ba9402d 100644 --- a/lib/src/phy/ue/ue_dl.c +++ b/lib/src/phy/ue/ue_dl.c @@ -945,70 +945,186 @@ void srslte_ue_dl_gen_cqi_aperiodic(srslte_ue_dl_t* q, } } +static void ue_dl_gen_ack_fdd_none(const srslte_pdsch_ack_t* ack_info, srslte_uci_data_t* uci_data) +{ + // Set all carriers number of ACKs to 0 + for (uint32_t i = 0; i < ack_info->nof_cc; i++) { + uci_data->cfg.ack[i].nof_acks = 0; + } +} + +static void +ue_dl_gen_ack_fdd_pcell_skip_drx(const srslte_pdsch_ack_t* ack_info, srslte_uci_data_t* uci_data, uint32_t nof_tb) +{ + uint32_t ack_idx = 0; + + // Find ACK/NACK + if (ack_info->cc[0].m[0].present) { + for (uint32_t tb = 0; tb < nof_tb; tb++) { + if (ack_info->cc[0].m[0].value[tb] != 2) { + uci_data->value.ack.ack_value[ack_idx] = ack_info->cc[0].m[0].value[tb]; + ack_idx++; + } + } + } + + // Set number of ACKs for PCell + uci_data->cfg.ack[0].nof_acks = ack_idx; + + // Set rest of carriers to 0 ACKs + for (uint32_t i = 1; i < ack_info->nof_cc; i++) { + uci_data->cfg.ack[i].nof_acks = 0; + } +} + +static void +ue_dl_gen_ack_fdd_all_keep_drx(const srslte_pdsch_ack_t* ack_info, srslte_uci_data_t* uci_data, uint32_t nof_tb) +{ + + for (uint32_t cc_idx = 0; cc_idx < ack_info->nof_cc; cc_idx++) { + // Find ACK/NACK + if (ack_info->cc[cc_idx].m[0].present) { + for (uint32_t tb = 0; tb < nof_tb; tb++) { + if (ack_info->cc[cc_idx].m[0].value[tb] != 2) { + uci_data->value.ack.ack_value[cc_idx * nof_tb + tb] = ack_info->cc[cc_idx].m[0].value[tb]; + } + } + } + + // Set all carriers to maximum number of TBs + uci_data->cfg.ack[cc_idx].nof_acks = nof_tb; + } +} + +static void +ue_dl_gen_ack_fdd_all_spatial_bundling(const srslte_pdsch_ack_t* ack_info, srslte_uci_data_t* uci_data, uint32_t nof_tb) +{ + uint32_t nof_ack = 0; + + for (uint32_t cc_idx = 0; cc_idx < ack_info->nof_cc; cc_idx++) { + if (ack_info->cc[cc_idx].m[0].present) { + uci_data->value.ack.ack_value[cc_idx] = 1; + for (uint32_t tb = 0; tb < nof_tb; tb++) { + if (ack_info->cc[cc_idx].m[0].value[tb] != 2) { + uci_data->value.ack.ack_value[cc_idx] &= ack_info->cc[cc_idx].m[0].value[tb]; + nof_ack++; + } + } + } else { + uci_data->value.ack.ack_value[cc_idx] = 2; + } + } + + // If no ACK is counted, set all zero, bundle otherwise + for (uint32_t i = 0; i < SRSLTE_PUCCH_CS_MAX_CARRIERS; i++) { + uci_data->cfg.ack[i].nof_acks = (nof_ack == 0) ? 0 : 1; + } +} + /* UE downlink procedure for reporting HARQ-ACK bits in FDD, Section 7.3 36.213 */ static void gen_ack_fdd(const srslte_pdsch_ack_t* ack_info, srslte_uci_data_t* uci_data) { + // Number of transport blocks for the current Transmission Mode uint32_t nof_tb = 1; if (ack_info->transmission_mode > SRSLTE_TM2) { nof_tb = SRSLTE_MAX_CODEWORDS; } - // Second clause: When 2 CC are configured with PUCCH CS mode and SR is also requested, bundle spatial codewords - if (!ack_info->is_pusch_available && ack_info->nof_cc == SRSLTE_PUCCH_CS_MAX_CARRIERS && - uci_data->value.scheduling_request == true && - ack_info->ack_nack_feedback_mode == SRSLTE_PUCCH_ACK_NACK_FEEDBACK_MODE_CS) { - uint32_t nof_ack = 0; - for (uint32_t cc_idx = 0; cc_idx < ack_info->nof_cc; cc_idx++) { - if (ack_info->cc[cc_idx].m[0].present) { - uci_data->value.ack.ack_value[cc_idx] = 1; - for (uint32_t tb = 0; tb < nof_tb; tb++) { - if (ack_info->cc[cc_idx].m[0].value[tb] != 2) { - uci_data->value.ack.ack_value[cc_idx] &= ack_info->cc[cc_idx].m[0].value[tb]; - nof_ack++; - } - } + // Count number of transmissions + uint32_t tb_count = 0; // All transmissions + uint32_t tb_count_cc0 = 0; // Transmissions on PCell + for (uint32_t cc_idx = 0; cc_idx < ack_info->nof_cc; cc_idx++) { + for (uint32_t tb = 0; tb < nof_tb; tb++) { + if (ack_info->cc[cc_idx].m[0].present && ack_info->cc[cc_idx].m[0].value[tb] != 2) { + tb_count++; + } + + // Save primary cell number of TB + if (cc_idx == 0) { + tb_count_cc0 = tb_count; + } + } + } + + // if no transmission counted return without reporting any ACK/NACK + if (tb_count == 0) { + ue_dl_gen_ack_fdd_none(ack_info, uci_data); + return; + } + + // Count total of Uplink Control Bits + uint32_t total_uci_bits = + tb_count + srslte_cqi_size(&uci_data->cfg.cqi) + (uci_data->value.scheduling_request ? 1 : 0); + + // Does CSI report need to be transmitted? + bool csi_report = uci_data->cfg.cqi.data_enable || uci_data->cfg.cqi.ri_len; + + // Logic for dropping CSI report if required + if (csi_report && !ack_info->is_pusch_available) { + bool drop_csi_report = true; ///< CSI report shall be dropped by default + + // 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 + // one serving cell, in case of collision between a periodic CSI report and an HARQ-ACK in a same subframe without + // PUSCH, + + // - if the parameter simultaneousAckNackAndCQI provided by higher layers is set TRUE and if the HARQ-ACK + // corresponds to a PDSCH transmission or PDCCH/EPDCCH indicating downlink SPS release only on the + // primary cell, then the periodic CSI report is multiplexed with HARQ-ACK on PUCCH using PUCCH format 2/2a/2b + drop_csi_report &= !(tb_count_cc0 == tb_count && ack_info->simul_cqi_ack); + + // - else if the UE is configured with PUCCH format 3 and if the parameter simultaneousAckNackAndCQI-Format3- + // r11 provided by higher layers is set TRUE, and if PUCCH resource is determined according to subclause + // 10.1.2.2.2, and + // - if the total number of bits in the subframe corresponding to HARQ-ACKs, SR (if any), and the CSI is not + // larger than 22 or + // - if the total number of bits in the subframe corresponding to spatially bundled HARQ-ACKs, SR (if any), + // and the CSI is not larger than 22 then the periodic CSI report is multiplexed with HARQ-ACK on PUCCH + // using the determined PUCCH format 3 resource according to [4] + drop_csi_report &= !(ack_info->simul_cqi_ack_pucch3 && total_uci_bits <= 22); + + // - otherwise, CSI is dropped + if (drop_csi_report) { + uci_data->cfg.cqi.data_enable = false; + uci_data->cfg.cqi.ri_len = 0; + csi_report = false; + } + } + + // For each HARQ ACK/NACK feedback mode + switch (ack_info->ack_nack_feedback_mode) { + + case SRSLTE_PUCCH_ACK_NACK_FEEDBACK_MODE_NORMAL: + // Get ACK from PCell only, skipping DRX + ue_dl_gen_ack_fdd_pcell_skip_drx(ack_info, uci_data, nof_tb); + break; + case SRSLTE_PUCCH_ACK_NACK_FEEDBACK_MODE_CS: + // Configured with more than serving cell and PUCCH Format 1b with channel selection + if (ack_info->nof_cc == 1) { + ue_dl_gen_ack_fdd_pcell_skip_drx(ack_info, uci_data, nof_tb); + } else if (ack_info->is_pusch_available) { + ue_dl_gen_ack_fdd_all_keep_drx(ack_info, uci_data, nof_tb); + } else if (uci_data->value.scheduling_request) { + // For FDD with PUCCH format 1b with channel selection, when both HARQ-ACK and SR are transmitted in the same + // sub-frame a UE shall transmit the HARQ-ACK on its assigned HARQ-ACK PUCCH resource with channel selection as + // defined in subclause 10.1.2.2.1 for a negative SR transmission and transmit one HARQ-ACK bit per serving cell + // on its assigned SR PUCCH resource for a positive SR transmission according to the following: + // − if only one transport block or a PDCCH indicating downlink SPS release is detected on a serving cell, the + // HARQ-ACK bit for the serving cell is the HARQ-ACK bit corresponding to the transport block or the PDCCH + // indicating downlink SPS release; + // − if two transport blocks are received on a serving cell, the HARQ-ACK bit for the serving cell is generated + // by spatially bundling the HARQ-ACK bits corresponding to the transport blocks; + // − if neither PDSCH transmission for which HARQ-ACK response shall be provided nor PDCCH indicating + // downlink SPS release is detected for a serving cell, the HARQ-ACK bit for the serving cell is set to NACK; + ue_dl_gen_ack_fdd_all_spatial_bundling(ack_info, uci_data, nof_tb); + } else if (csi_report) { + ue_dl_gen_ack_fdd_pcell_skip_drx(ack_info, uci_data, nof_tb); } else { - uci_data->value.ack.ack_value[cc_idx] = 2; + ue_dl_gen_ack_fdd_all_keep_drx(ack_info, uci_data, nof_tb); } - } - - // If no ACK is counted, set all zero, bundle otherwise - for (uint32_t i = 0; i < SRSLTE_PUCCH_CS_MAX_CARRIERS; i++) { - uci_data->cfg.ack[i].nof_acks = (nof_ack == 0) ? 0 : 1; - } - } else { - // By default, in FDD we just pass through all HARQ-ACK bits - uint32_t tb_count = 0; - uint32_t tb_count_cc0 = 0; - uint32_t n = 0; - for (uint32_t cc_idx = 0; cc_idx < ack_info->nof_cc; cc_idx++) { - for (uint32_t tb = 0; tb < nof_tb; tb++, n++) { - uci_data->value.ack.ack_value[n] = ack_info->cc[cc_idx].m[0].value[tb]; - if (ack_info->cc[cc_idx].m[0].present && ack_info->cc[cc_idx].m[0].value[tb] != 2) { - tb_count++; - } - - // Save primary cell number of TB - if (cc_idx == 0) { - tb_count_cc0 = tb_count; - } - } - } - - uint32_t total_uci_bits = - tb_count + srslte_cqi_size(&uci_data->cfg.cqi) + (uci_data->value.scheduling_request ? 1 : 0); - - if (tb_count == 0) { - // If no PDSCH TB detected, set all to zeros and do not modify the UCI configuration - for (int i = 0; i < ack_info->nof_cc; i++) { - uci_data->cfg.ack[i].nof_acks = 0; - } - } else 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) { + break; + case SRSLTE_PUCCH_ACK_NACK_FEEDBACK_MODE_PUCCH3: // 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 @@ -1021,56 +1137,15 @@ static void gen_ack_fdd(const srslte_pdsch_ack_t* ack_info, srslte_uci_data_t* u // - 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 - - for (int i = 1; i < ack_info->nof_cc; i++) { - uci_data->cfg.ack[i].nof_acks = 0; + if (tb_count == tb_count_cc0) { + ue_dl_gen_ack_fdd_pcell_skip_drx(ack_info, uci_data, nof_tb); + } else { + ue_dl_gen_ack_fdd_all_keep_drx(ack_info, uci_data, nof_tb); } - } else { - // For 2 or more configured cells, report nof_tb per carrier except if there are no HARQ-ACK bits to report, in - // which case we set to 0 - for (int i = 0; i < ack_info->nof_cc; i++) { - uci_data->cfg.ack[i].nof_acks = nof_tb; - } - } - - if (tb_count && uci_data->cfg.cqi.data_enable && !ack_info->is_pusch_available) { - bool drop_csi_report = true; ///< CSI report shall be dropped by default - - // 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 - // one serving cell, in case of collision between a periodic CSI report and an HARQ-ACK in a same subframe without - // PUSCH, - - // - if the parameter simultaneousAckNackAndCQI provided by higher layers is set TRUE and if the HARQ-ACK - // corresponds to a PDSCH transmission or PDCCH/EPDCCH indicating downlink SPS release only on the - // primary cell, then the periodic CSI report is multiplexed with HARQ-ACK on PUCCH using PUCCH format 2/2a/2b - if ((tb_count_cc0 == tb_count && ack_info->simul_cqi_ack)) { - // Do not drop CSI report - drop_csi_report = false; - - // Set number of active only ACKs - uci_data->cfg.ack[0].nof_acks = tb_count_cc0; - - // Set all SCell number of ACKs to 0 - for (int i = 1; i < ack_info->nof_cc; i++) { - uci_data->cfg.ack[i].nof_acks = 0; - } - } - - // - else if the UE is configured with PUCCH format 3 and if the parameter simultaneousAckNackAndCQI-Format3- - // r11 provided by higher layers is set TRUE, and if PUCCH resource is determined according to subclause - // 10.1.2.2.2, and - // - if the total number of bits in the subframe corresponding to HARQ-ACKs, SR (if any), and the CSI is not - // larger than 22 or - // - if the total number of bits in the subframe corresponding to spatially bundled HARQ-ACKs, SR (if any), - // and the CSI is not larger than 22 then the periodic CSI report is multiplexed with HARQ-ACK on PUCCH - // using the determined PUCCH format 3 resource according to [4] - drop_csi_report &= !(ack_info->simul_cqi_ack_pucch3 && total_uci_bits <= 22); - - // - otherwise, CSI is dropped - uci_data->cfg.cqi.data_enable = !drop_csi_report; - } + break; + case SRSLTE_PUCCH_ACK_NACK_FEEDBACK_MODE_ERROR: + default:; // Do nothing + break; } // n_cce values are just copied @@ -1297,7 +1372,6 @@ void srslte_ue_dl_gen_ack(const srslte_cell_t* cell, const srslte_pdsch_ack_t* ack_info, srslte_uci_data_t* uci_data) { - if (cell->frame_type == SRSLTE_FDD) { gen_ack_fdd(ack_info, uci_data); } else { diff --git a/srsenb/hdr/phy/phy_ue_db.h b/srsenb/hdr/phy/phy_ue_db.h index dea6cf870..a8b51055e 100644 --- a/srsenb/hdr/phy/phy_ue_db.h +++ b/srsenb/hdr/phy/phy_ue_db.h @@ -266,7 +266,7 @@ public: uint16_t rnti, bool aperiodic_cqi_request, bool is_pusch_available, - srslte_uci_cfg_t& uci_cfg) const; + srslte_uci_cfg_t& uci_cfg); /** * Sends the decoded Uplink Control Information by PUCCH or PUSCH to MAC diff --git a/srsenb/src/phy/phy_ue_db.cc b/srsenb/src/phy/phy_ue_db.cc index 322404a50..d5e0d8658 100644 --- a/srsenb/src/phy/phy_ue_db.cc +++ b/srsenb/src/phy/phy_ue_db.cc @@ -361,10 +361,13 @@ void phy_ue_db::set_ack_pending(uint32_t tti, uint32_t enb_cc_idx, const srslte_ pdsch_ack_m.resource.tpc_for_pucch = dci.tpc_pucch; // Set TB info - for (uint32_t i = 0; i < srslte_dci_format_max_tb(dci.format); i++) { - if (SRSLTE_DCI_IS_TB_EN(dci.tb[i])) { - pdsch_ack_m.value[i] = 1; + for (uint32_t tb_idx = 0; tb_idx < SRSLTE_MAX_CODEWORDS; tb_idx++) { + // Count only if the TB is enabled and the TB index is valid for the DCI format + if (SRSLTE_DCI_IS_TB_EN(dci.tb[tb_idx]) and tb_idx < srslte_dci_format_max_tb(dci.format)) { + pdsch_ack_m.value[tb_idx] = 1; pdsch_ack_m.k++; + } else { + pdsch_ack_m.value[tb_idx] = 2; } } } @@ -374,7 +377,7 @@ bool phy_ue_db::fill_uci_cfg(uint32_t tti, uint16_t rnti, bool aperiodic_cqi_request, bool is_pusch_available, - srslte_uci_cfg_t& uci_cfg) const + srslte_uci_cfg_t& uci_cfg) { std::lock_guard lock(mutex); @@ -391,9 +394,12 @@ bool phy_ue_db::fill_uci_cfg(uint32_t tti, return false; } - const auto& ue = ue_db.at(rnti); - const auto& pcell_cfg = ue.cell_info[0].phy_cfg; - bool uci_required = false; + common_ue& ue = ue_db.at(rnti); + const srslte::phy_cfg_t& pcell_cfg = ue.cell_info[0].phy_cfg; + bool uci_required = false; + + const cell_info_t& pcell_info = ue.cell_info[0]; + const srslte_cell_t& pcell = cell_cfg_list->at(pcell_info.enb_cc_idx).cell; // Check if SR opportunity (will only be used in PUCCH) uci_cfg.is_scheduling_request_tti = (srslte_ue_ul_sr_send_tti(&pcell_cfg.ul_cfg.pucch, tti) == 1); @@ -420,20 +426,17 @@ bool phy_ue_db::fill_uci_cfg(uint32_t tti, // If no periodic CQI report required, check aperiodic reporting if ((not periodic_cqi_required) and aperiodic_cqi_request) { // Aperiodic only supported for PCell - const cell_info_t& pcell_info = ue.cell_info[0]; - const srslte_cell_t& cell = cell_cfg_list->at(pcell_info.enb_cc_idx).cell; - const srslte_dl_cfg_t& dl_cfg = pcell_info.phy_cfg.dl_cfg; + const srslte_dl_cfg_t& dl_cfg = pcell_info.phy_cfg.dl_cfg; - uci_required = srslte_enb_dl_gen_cqi_aperiodic(&cell, &dl_cfg, pcell_info.last_ri, &uci_cfg.cqi); + uci_required = srslte_enb_dl_gen_cqi_aperiodic(&pcell, &dl_cfg, pcell_info.last_ri, &uci_cfg.cqi); } // Get pending ACKs from PDSCH - srslte_dl_sf_cfg_t dl_sf_cfg = {}; - dl_sf_cfg.tti = tti; - const srslte_cell_t& cell = cell_cfg_list->at(ue.cell_info[0].enb_cc_idx).cell; - srslte_pdsch_ack_t ack_info = ue.pdsch_ack[TTIMOD(tti)]; - ack_info.is_pusch_available = is_pusch_available; - srslte_enb_dl_gen_ack(&cell, &dl_sf_cfg, &ack_info, &uci_cfg); + srslte_dl_sf_cfg_t dl_sf_cfg = {}; + dl_sf_cfg.tti = tti; + srslte_pdsch_ack_t& pdsch_ack = ue.pdsch_ack[TTIMOD(tti)]; + pdsch_ack.is_pusch_available = is_pusch_available; + srslte_enb_dl_gen_ack(&pcell, &dl_sf_cfg, &pdsch_ack, &uci_cfg); uci_required |= (srslte_uci_cfg_total_ack(&uci_cfg) > 0); // Return whether UCI needs to be decoded @@ -468,15 +471,17 @@ void phy_ue_db::send_uci_data(uint32_t tti, // Get ACK info srslte_pdsch_ack_t& pdsch_ack = ue.pdsch_ack[TTIMOD(tti)]; - srslte_enb_dl_get_ack(&cell_cfg_list->at(ue.cell_info[0].enb_cc_idx).cell, &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 for (uint32_t scell_idx = 0; scell_idx < SRSLTE_MAX_CARRIERS; scell_idx++) { const srslte_pdsch_ack_cc_t& pdsch_ack_cc = pdsch_ack.cc[scell_idx]; for (uint32_t m = 0; m < pdsch_ack_cc.M; m++) { if (pdsch_ack_cc.m[m].present) { - for (uint32_t tb = 0; tb < pdsch_ack_cc.m[m].k; tb++) { - stack->ack_info(tti, rnti, ue.cell_info[scell_idx].enb_cc_idx, tb, pdsch_ack_cc.m[m].value[tb] == 1); + for (uint32_t tb = 0; tb < SRSLTE_MAX_CODEWORDS; tb++) { + if (pdsch_ack_cc.m[m].value[tb] != 2) { + stack->ack_info(tti, rnti, ue.cell_info[scell_idx].enb_cc_idx, tb, pdsch_ack_cc.m[m].value[tb] == 1); + } } } } @@ -511,12 +516,6 @@ void phy_ue_db::send_uci_data(uint32_t tti, stack->cqi_info(tti, rnti, cqi_cc_idx, cqi_value); } - // Rank indicator (TM3 and TM4) - if (uci_cfg.cqi.ri_len) { - stack->ri_info(tti, rnti, cqi_cc_idx, uci_value.ri); - cqi_scell_info.last_ri = uci_value.ri; - } - // Precoding Matrix indicator (TM4) if (uci_cfg.cqi.pmi_present) { uint8_t pmi_value = 0; @@ -534,6 +533,12 @@ void phy_ue_db::send_uci_data(uint32_t tti, stack->pmi_info(tti, rnti, cqi_cc_idx, pmi_value); } } + + // Rank indicator (TM3 and TM4) + if (uci_cfg.cqi.ri_len) { + stack->ri_info(tti, rnti, cqi_cc_idx, uci_value.ri); + cqi_scell_info.last_ri = uci_value.ri; + } } void phy_ue_db::set_last_ul_tb(uint16_t rnti, uint32_t enb_cc_idx, uint32_t pid, srslte_ra_tb_t tb) diff --git a/srsenb/test/phy/CMakeLists.txt b/srsenb/test/phy/CMakeLists.txt index 01d626547..0a42d4631 100644 --- a/srsenb/test/phy/CMakeLists.txt +++ b/srsenb/test/phy/CMakeLists.txt @@ -35,19 +35,62 @@ target_link_libraries(enb_phy_test set(ENB_PHY_TEST_DURATION 128) -# Basic eNb PHY test: +# eNb PHY test: +# - Single carrier +# - Transmission Mode 1 # - 1 eNb cell/carrier (no carrier aggregation) -# - maximum bandwidth 100 -add_test(enb_phy_test_base enb_phy_test --duration=${ENB_PHY_TEST_DURATION} --cell.nof_prb=100 ) +# - 100 PRB +add_test(enb_phy_test_tm1 enb_phy_test --duration=${ENB_PHY_TEST_DURATION} --cell.nof_prb=100 --tm=1) -# Five carrier aggregation using PUCCH3 -# - 6 eNb cell/carrier (only using 5 aggregated) +# Single carrier TM2 eNb PHY test: +# - Single carrier +# - Transmission Mode 2 +# - 1 eNb cell/carrier (no carrier aggregation) +# - 6 PRB +add_test(enb_phy_test_tm2 enb_phy_test --duration=${ENB_PHY_TEST_DURATION} --cell.nof_prb=100 --tm=2) + +# Single carrier TM3 eNb PHY test: +# - Single carrier +# - Transmission Mode 3 +# - 1 eNb cell/carrier (no carrier aggregation) +# - 6 PRB +add_test(enb_phy_test_tm3 enb_phy_test --duration=${ENB_PHY_TEST_DURATION} --cell.nof_prb=100 --tm=3) + +# Single carrier eNb PHY test: +# - Single carrier +# - Transmission Mode 4 +# - 1 eNb cell/carrier (no carrier aggregation) +# - 6 PRB +add_test(enb_phy_test_tm4 enb_phy_test --duration=${ENB_PHY_TEST_DURATION} --cell.nof_prb=100 --tm=4) + +# Five carrier aggregation using PUCCH3: +# - 6 eNb cell/carrier +# - Transmission Mode 1 +# - 5 Aggregated carriers # - 6 PRB # - PUCCH format 3 ACK/NACK feedback mode and more than 2 ACK/NACK bits in PUSCH -add_test(enb_phy_test_ca_pucch3 enb_phy_test --duration=${ENB_PHY_TEST_DURATION} --nof_enb_cells=6 --ue_cell_list=3,4,0,1,2 --ack_mode=pucch3 --cell.nof_prb=6 ) +add_test(enb_phy_test_tm1_ca_pucch3 enb_phy_test --duration=${ENB_PHY_TEST_DURATION} --nof_enb_cells=6 --ue_cell_list=3,4,0,1,2 --ack_mode=pucch3 --cell.nof_prb=6 --tm=1) -# Two carrier aggregation using Channel Selection -# - 6 eNb cell/carrier (only using 2 aggregated) +# Five carrier aggregation using PUCCH3: +# - 6 eNb cell/carrier +# - Transmission Mode 4 +# - 5 Aggregated carriers +# - 6 PRB +# - PUCCH format 3 ACK/NACK feedback mode and more than 2 ACK/NACK bits in PUSCH +add_test(enb_phy_test_tm4_ca_pucch3 enb_phy_test --duration=${ENB_PHY_TEST_DURATION} --nof_enb_cells=6 --ue_cell_list=0,4,3,1,2 --ack_mode=pucch3 --cell.nof_prb=6 --tm=4) + +# Two carrier aggregation using Channel Selection: +# - 6 eNb cell/carrier +# - Transmission Mode 1 +# - 2 Aggregated carriers # - 6 PRB # - PUCCH format 1b with Channel selection ACK/NACK feedback mode -add_test(enb_phy_test_cs enb_phy_test --duration=${ENB_PHY_TEST_DURATION} --nof_enb_cells=6 --ue_cell_list=5,4 --ack_mode=cs --cell.nof_prb=6 ) +add_test(enb_phy_test_tm1_ca_cs enb_phy_test --duration=${ENB_PHY_TEST_DURATION} --nof_enb_cells=6 --ue_cell_list=5,4 --ack_mode=cs --cell.nof_prb=6 --tm=1) + +# Two carrier aggregation using Channel Selection: +# - 6 eNb cell/carrier +# - Transmission Mode 4 +# - 2 Aggregated carriers +# - 6 PRB +# - PUCCH format 1b with Channel selection ACK/NACK feedback mode +add_test(enb_phy_test_tm4_ca_cs enb_phy_test --duration=${ENB_PHY_TEST_DURATION} --nof_enb_cells=6 --ue_cell_list=1,5 --ack_mode=cs --cell.nof_prb=6 --tm=4) diff --git a/srsenb/test/phy/enb_phy_test.cc b/srsenb/test/phy/enb_phy_test.cc index fefec084e..c1482527d 100644 --- a/srsenb/test/phy/enb_phy_test.cc +++ b/srsenb/test/phy/enb_phy_test.cc @@ -255,19 +255,20 @@ typedef std::unique_ptr unique_dummy_radio_t; class dummy_stack : public srsenb::stack_interface_phy_lte { private: - static constexpr float prob_dl_grant = 0.50f; - static constexpr float prob_ul_grant = 0.10f; - static constexpr uint32_t cfi = 2; + static constexpr float prob_dl_grant = 0.50f; + static constexpr float prob_ul_grant = 0.10f; + static constexpr uint32_t cfi = 2; - srsenb::phy_cell_cfg_list_t phy_cell_cfg; - std::mutex mutex; - std::condition_variable cvar; - srslte::log_filter log_h; - srslte_softbuffer_tx_t softbuffer_tx = {}; - srslte_softbuffer_rx_t softbuffer_rx[SRSLTE_MAX_CARRIERS][SRSLTE_FDD_NOF_HARQ] = {}; - uint8_t* data = nullptr; - uint16_t ue_rnti = 0; - srslte_random_t random_gen = nullptr; + srsenb::phy_cell_cfg_list_t phy_cell_cfg; + srsenb::phy_interface_rrc_lte::phy_rrc_dedicated_list_t phy_rrc; + std::mutex mutex; + std::condition_variable cvar; + srslte::log_filter log_h; + srslte_softbuffer_tx_t softbuffer_tx = {}; + srslte_softbuffer_rx_t softbuffer_rx[SRSLTE_MAX_CARRIERS][SRSLTE_FDD_NOF_HARQ] = {}; + uint8_t* data = nullptr; + uint16_t ue_rnti = 0; + srslte_random_t random_gen = nullptr; CALLBACK(sr_detected); CALLBACK(rach_detected); @@ -290,7 +291,7 @@ private: uint32_t tti; uint32_t cc_idx; uint32_t tb_idx; - uint32_t ack; + bool ack; } tti_dl_info_t; typedef struct { @@ -322,14 +323,16 @@ private: uint32_t ul_riv = 0; public: - explicit dummy_stack(srsenb::phy_cfg_t& phy_cfg_, - const std::string& log_level, - uint16_t rnti_, - std::vector& active_cell_list_) : + explicit dummy_stack(const srsenb::phy_cfg_t& phy_cfg_, + const srsenb::phy_interface_rrc_lte::phy_rrc_dedicated_list_t& phy_rrc_, + const std::string& log_level, + uint16_t rnti_, + std::vector& active_cell_list_) : log_h("STACK"), ue_rnti(rnti_), random_gen(srslte_random_init(rnti_)), phy_cell_cfg(phy_cfg_.phy_cell_cfg), + phy_rrc(phy_rrc_), active_cell_list(active_cell_list_) { log_h.set_level(log_level); @@ -415,11 +418,17 @@ public: int ri_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t ri_value) override { notify_ri_info(); + + log_h.info("Received RI tti=%d; rnti=0x%x; cc_idx=%d; ri=%d;\n", tti, rnti, cc_idx, ri_value); + return 0; } int pmi_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t pmi_value) override { notify_pmi_info(); + + log_h.info("Received PMI tti=%d; rnti=0x%x; cc_idx=%d; pmi=%d;\n", tti, rnti, cc_idx, pmi_value); + return 0; } int cqi_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t cqi_value) override @@ -453,7 +462,7 @@ public: 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.tb_idx = tb_idx; tti_dl_info.ack = ack; tti_dl_info_ack_queue.push(tti_dl_info); @@ -484,14 +493,25 @@ public: dl_sched_res[0].cfi = cfi; // Iterate for each carrier + uint32_t scell_idx = 0; for (uint32_t& cc_idx : active_cell_list) { auto& dl_sched = dl_sched_res[cc_idx]; // Required dl_sched.cfi = cfi; + // Default TB scheduling + bool sched_tb[SRSLTE_MAX_TB] = {}; + + sched_tb[0] = srslte_random_bool(random_gen, prob_dl_grant); + + // Schedule second TB for TM3 or TM4 + if (phy_rrc[scell_idx].phy_cfg.dl_cfg.tm == SRSLTE_TM3 or phy_rrc[scell_idx].phy_cfg.dl_cfg.tm == SRSLTE_TM4) { + sched_tb[1] = srslte_random_bool(random_gen, prob_dl_grant); + } + // Random decision on whether transmit or not - bool sched = srslte_random_bool(random_gen, prob_dl_grant); + bool sched = sched_tb[0] | sched_tb[1]; // RNTI needs to be valid sched &= (ue_rnti != 0); @@ -511,18 +531,24 @@ public: 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; - dl_sched.pdsch[0].dci.tpc_pucch = (location.ncce + 1) % SRSLTE_PUCCH_SIZE_AN_CS; + dl_sched.pdsch[0].dci.tpc_pucch = (location.ncce) % SRSLTE_PUCCH_SIZE_AN_CS; + + // Set DCI format depending on the transmission mode + switch (phy_rrc[0].phy_cfg.dl_cfg.tm) { + default: + case SRSLTE_TM1: + case SRSLTE_TM2: + dl_sched.pdsch[0].dci.format = SRSLTE_DCI_FORMAT1; + break; + case SRSLTE_TM3: + dl_sched.pdsch[0].dci.format = SRSLTE_DCI_FORMAT2A; + break; + case SRSLTE_TM4: + dl_sched.pdsch[0].dci.format = SRSLTE_DCI_FORMAT2; + break; + } // Push grant info in queue tti_dl_info_t tti_dl_info = {}; @@ -531,11 +557,34 @@ public: tti_dl_info.tb_idx = 0; tti_dl_info.ack = true; - // Push to queue - tti_dl_info_sched_queue.push(tti_dl_info); + // Schedule TB + uint32_t cw_count = 0; + for (uint32_t tb = 0; tb < SRSLTE_MAX_TB; tb++) { + if (sched_tb[tb]) { + log_h.debug("Transmitted DL grant tti=%d; rnti=0x%x; cc=%d; tb=%d;\n", tti, ue_rnti, cc_idx, tb); + + // Create Grant with maximum safe MCS + dl_sched.pdsch[0].dci.tb[tb].cw_idx = cw_count++; + dl_sched.pdsch[0].dci.tb[tb].mcs_idx = 27; + dl_sched.pdsch[0].dci.tb[tb].rv = 0; + dl_sched.pdsch[0].dci.tb[tb].ndi = false; + + // Push to queue + tti_dl_info.tb_idx = tb; + tti_dl_info_sched_queue.push(tti_dl_info); + } else { + // Create Grant with no TB + dl_sched.pdsch[0].dci.tb[tb].cw_idx = 0; + dl_sched.pdsch[0].dci.tb[tb].mcs_idx = 0; + dl_sched.pdsch[0].dci.tb[tb].rv = 1; + dl_sched.pdsch[0].dci.tb[tb].ndi = false; + } + } } else { dl_sched.nof_grants = 0; } + + scell_idx++; } return 0; @@ -624,11 +673,8 @@ public: void set_sched_dl_tti_mask(uint8_t* tti_mask, uint32_t nof_sfs) override { notify_set_sched_dl_tti_mask(); } void rl_failure(uint16_t rnti) override { notify_rl_failure(); } void rl_ok(uint16_t rnti) override { notify_rl_ok(); } - void tti_clock() override - { - notify_tti_clock(); - } - int run_tti() + void tti_clock() override { notify_tti_clock(); } + int run_tti() { // Check DL ACKs match with grants while (not tti_dl_info_ack_queue.empty()) { @@ -700,6 +746,7 @@ private: std::vector buffers = {}; dummy_radio* radio = nullptr; uint32_t sf_len = 0; + uint32_t nof_ports = 0; uint16_t rnti = 0; srslte_dl_sf_cfg_t sf_dl_cfg = {}; srslte_ul_sf_cfg_t sf_ul_cfg = {}; @@ -707,6 +754,7 @@ private: uint8_t* tx_data = nullptr; srsenb::phy_interface_rrc_lte::phy_rrc_dedicated_list_t phy_rrc_cfg = {}; srslte::log_filter log_h; + std::map last_ri = {}; public: dummy_ue(dummy_radio* _radio, @@ -714,16 +762,19 @@ public: std::string log_level, uint16_t rnti_, const srsenb::phy_interface_rrc_lte::phy_rrc_dedicated_list_t& phy_rrc_cfg_) : - radio(_radio), log_h("UE PHY", nullptr, true), phy_rrc_cfg(phy_rrc_cfg_) + radio(_radio), + log_h("UE PHY", nullptr, true), + phy_rrc_cfg(phy_rrc_cfg_) { // Calculate subframe length - sf_len = static_cast(SRSLTE_SF_LEN_PRB(cell_list[0].cell.nof_prb)); - rnti = rnti_; + nof_ports = cell_list[0].cell.nof_ports; + sf_len = static_cast(SRSLTE_SF_LEN_PRB(cell_list[0].cell.nof_prb)); + rnti = rnti_; log_h.set_level(std::move(log_level)); // Initialise one buffer per eNb - for (uint32_t i = 0; i < cell_list.size(); i++) { + for (uint32_t i = 0; i < cell_list.size() * nof_ports; i++) { // Allocate buffers cf_t* buffer = srslte_vec_cf_malloc(sf_len); if (not buffer) { @@ -747,7 +798,7 @@ public: // Initialise UE DL if (srslte_ue_dl_init( - ue_dl, &buffers[cc_idx], cell_list[cc_idx].cell.nof_prb, cell_list[cc_idx].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"); } @@ -767,7 +818,7 @@ public: ue_ul_v.push_back(ue_ul); // Initialise UE UL - if (srslte_ue_ul_init(ue_ul, buffers[cc_idx], cell_list[cc_idx].cell.nof_prb)) { + if (srslte_ue_ul_init(ue_ul, buffers[cc_idx * nof_ports], cell_list[cc_idx].cell.nof_prb)) { ERROR("Setting UE UL cell\n"); } @@ -856,6 +907,11 @@ public: ue_dl_cfg.cfg.cqi_report.periodic_mode = SRSLTE_CQI_MODE_12; ue_dl_cfg.cfg.pdsch.rnti = rnti; + int report_ri_cc_idx = -1; + if (last_ri.count(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); // Get DL Grants @@ -883,8 +939,14 @@ public: pdsch_ack.cc[i].m[0].resource.n_cce = dci_dl->location.ncce; pdsch_ack.cc[i].m[0].resource.grant_cc_idx = i; pdsch_ack.cc[i].m[0].resource.tpc_for_pucch = dci_dl->tpc_pucch; - pdsch_ack.cc[i].m[0].value[0] = 1; - pdsch_ack.cc[i].m[0].value[1] = 1; + + for (uint32_t tb_idx = 0; tb_idx < SRSLTE_MAX_TB; tb_idx++) { + if (ue_dl_cfg.cfg.pdsch.grant.tb[tb_idx].enabled) { + pdsch_ack.cc[i].m[0].value[tb_idx] = 1; + } else { + pdsch_ack.cc[i].m[0].value[tb_idx] = 2; + } + } } else { pdsch_ack.cc[i].M = 1; pdsch_ack.cc[i].m[0].present = false; @@ -892,6 +954,11 @@ public: // Generate CQI periodic if required srslte_ue_dl_gen_cqi_periodic(ue_dl_v[i], &ue_dl_cfg, 0x0f, sf_ul_cfg.tti, &uci_data); + + if (srslte_cqi_periodic_ri_send(&ue_dl_cfg.cfg.cqi_report, sf_ul_cfg.tti, ue_dl_v[i]->cell.frame_type) && + uci_data.cfg.cqi.ri_len) { + uci_data.cfg.cqi.scell_index = i; + } } return SRSLTE_SUCCESS; @@ -945,6 +1012,10 @@ public: // Generate Acknowledgements srslte_ue_dl_gen_ack(&ue_dl_v[i]->cell, &sf_dl_cfg, &pdsch_ack, &uci_data); + + if (uci_data.cfg.cqi.ri_len) { + last_ri[uci_data.cfg.cqi.scell_index] = uci_data.value.ri; + } } // Set UCI only for PCel @@ -1000,20 +1071,44 @@ class phy_test_bench { public: struct args_t { - uint16_t rnti = 0x1234; - uint32_t duration = 10240; - uint32_t nof_enb_cells = 1; - srslte_cell_t cell = {}; + uint16_t rnti = 0x1234; + uint32_t duration = 10240; + uint32_t nof_enb_cells = 1; + srslte_cell_t cell = {}; std::string ue_cell_list_str = "0"; ///< First indicates PCell std::vector ue_cell_list = {0}; - std::string ack_mode = "normal"; - std::string log_level = "none"; - + std::string ack_mode = "normal"; + std::string log_level = "none"; + uint32_t tm_u32 = 1; + srslte_tm_t tm = SRSLTE_TM1; args_t() { cell.nof_prb = 6; cell.nof_ports = 1; } + + // Initialises secondary parameters + void init() + { + switch (tm_u32) { + case 2: + cell.nof_ports = 2; + tm = SRSLTE_TM2; + break; + case 3: + cell.nof_ports = 2; + tm = SRSLTE_TM3; + break; + case 4: + cell.nof_ports = 2; + tm = SRSLTE_TM4; + break; + case 1: + default: + cell.nof_ports = 1; + tm = SRSLTE_TM1; + } + } }; private: @@ -1068,29 +1163,41 @@ public: phy_cfg.prach_cnfg.prach_cfg_info.zero_correlation_zone_cfg = 5; // Create base UE dedicated configuration - srslte::phy_cfg_t dedicated = {}; - dedicated.ul_cfg.pucch.ack_nack_feedback_mode = srslte_string_ack_nack_feedback_mode(args.ack_mode.c_str()); - dedicated.ul_cfg.pucch.delta_pucch_shift = delta_pucch; - dedicated.ul_cfg.pucch.n_rb_2 = 2; - dedicated.ul_cfg.pucch.N_cs = 0; - dedicated.ul_cfg.pucch.n_pucch_sr = 0; - dedicated.ul_cfg.pucch.N_pucch_1 = N_pucch_1; - dedicated.ul_cfg.pucch.n_pucch_2 = 5; - dedicated.ul_cfg.pucch.simul_cqi_ack = true; - dedicated.ul_cfg.pucch.sr_configured = true; - dedicated.ul_cfg.pucch.I_sr = 5; - dedicated.ul_cfg.pucch.n1_pucch_an_cs[0][0] = N_pucch_1 + delta_pucch * 1; - dedicated.ul_cfg.pucch.n1_pucch_an_cs[1][0] = N_pucch_1 + delta_pucch * 2; - dedicated.ul_cfg.pucch.n1_pucch_an_cs[2][0] = N_pucch_1 + delta_pucch * 3; - dedicated.ul_cfg.pucch.n1_pucch_an_cs[3][0] = N_pucch_1 + delta_pucch * 4; - dedicated.ul_cfg.pucch.n1_pucch_an_cs[0][1] = N_pucch_1 + delta_pucch * 2; - dedicated.ul_cfg.pucch.n1_pucch_an_cs[1][1] = N_pucch_1 + delta_pucch * 3; - dedicated.ul_cfg.pucch.n1_pucch_an_cs[2][1] = N_pucch_1 + delta_pucch * 4; - dedicated.ul_cfg.pucch.n1_pucch_an_cs[3][1] = N_pucch_1 + delta_pucch * 1; + srslte::phy_cfg_t dedicated = {}; + + // Configure DL + dedicated.dl_cfg.tm = args.tm; + + // Configure reporting dedicated.dl_cfg.cqi_report.periodic_configured = true; dedicated.dl_cfg.cqi_report.pmi_idx = 25; dedicated.dl_cfg.cqi_report.periodic_mode = SRSLTE_CQI_MODE_20; - dedicated.ul_cfg.pusch.uci_offset.I_offset_ack = 7; + + if (args.tm == SRSLTE_TM3 or args.tm == SRSLTE_TM4) { + dedicated.dl_cfg.cqi_report.ri_idx_present = true; + dedicated.dl_cfg.cqi_report.ri_idx = 483; // Every 8 CQI/PMI report, schedule an RI report + } + + // Configure UL Resources + dedicated.ul_cfg.pucch.ack_nack_feedback_mode = srslte_string_ack_nack_feedback_mode(args.ack_mode.c_str()); + dedicated.ul_cfg.pucch.delta_pucch_shift = delta_pucch; + dedicated.ul_cfg.pucch.n_rb_2 = 2; + dedicated.ul_cfg.pucch.N_cs = 0; + dedicated.ul_cfg.pucch.n_pucch_sr = 0; + dedicated.ul_cfg.pucch.N_pucch_1 = N_pucch_1; + dedicated.ul_cfg.pucch.n_pucch_2 = 5; + dedicated.ul_cfg.pucch.simul_cqi_ack = true; + dedicated.ul_cfg.pucch.sr_configured = true; + dedicated.ul_cfg.pucch.I_sr = 5; + dedicated.ul_cfg.pucch.n1_pucch_an_cs[0][0] = N_pucch_1 + 2; + dedicated.ul_cfg.pucch.n1_pucch_an_cs[1][0] = N_pucch_1 + 3; + dedicated.ul_cfg.pucch.n1_pucch_an_cs[2][0] = N_pucch_1 + 4; + dedicated.ul_cfg.pucch.n1_pucch_an_cs[3][0] = N_pucch_1 + 5; + dedicated.ul_cfg.pucch.n1_pucch_an_cs[0][1] = N_pucch_1 + 3; + dedicated.ul_cfg.pucch.n1_pucch_an_cs[1][1] = N_pucch_1 + 4; + dedicated.ul_cfg.pucch.n1_pucch_an_cs[2][1] = N_pucch_1 + 5; + dedicated.ul_cfg.pucch.n1_pucch_an_cs[3][1] = N_pucch_1 + 6; + dedicated.ul_cfg.pusch.uci_offset.I_offset_ack = 7; // Configure UE PHY std::array activation = {}; ///< Activation/Deactivation vector @@ -1111,10 +1218,11 @@ public: } /// Create Radio instance - radio = unique_dummy_radio_t(new dummy_radio(args.nof_enb_cells, args.cell.nof_prb, args.log_level)); + radio = unique_dummy_radio_t( + new dummy_radio(args.nof_enb_cells * args.cell.nof_ports, args.cell.nof_prb, args.log_level)); /// Create Dummy Stack isntance - stack = unique_dummy_stack_t(new dummy_stack(phy_cfg, 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, args.ue_cell_list)); /// eNb PHY initialisation instance enb_phy = unique_srsenb_phy_t(new srsenb::phy(&logger_stdout)); @@ -1171,6 +1279,7 @@ int parse_args(int argc, char** argv, phy_test_bench::args_t& args) ("ack_mode", bpo::value(&args.ack_mode), "HARQ ACK/NACK mode: normal, pucch3, cs") ("cell.nof_prb", bpo::value(&args.cell.nof_prb)->default_value(args.cell.nof_prb), "eNb Cell/Carrier bandwidth") ("cell.nof_ports", bpo::value(&args.cell.nof_ports)->default_value(args.cell.nof_ports), "eNb Cell/Carrier number of ports") + ("tm", bpo::value(&args.tm_u32)->default_value(args.tm_u32), "Transmission mode") ; options.add(common).add_options()("help", "Show this message"); @@ -1216,6 +1325,9 @@ int main(int argc, char** argv) // Parse arguments TESTASSERT(parse_args(argc, argv, test_args) == SRSLTE_SUCCESS); + // Initialize secondary parameters + test_args.init(); + // Create Test Bench unique_phy_test_bench test_bench = unique_phy_test_bench(new phy_test_bench(test_args));