diff --git a/lib/include/srsran/phy/ue/ue_dl_nr.h b/lib/include/srsran/phy/ue/ue_dl_nr.h index 26bf5437e..fef512fad 100644 --- a/lib/include/srsran/phy/ue/ue_dl_nr.h +++ b/lib/include/srsran/phy/ue/ue_dl_nr.h @@ -167,4 +167,8 @@ SRSRAN_API int srsran_ue_dl_nr_gen_ack(const srsran_ue_dl_nr_harq_ack_cfg_t* cfg const srsran_pdsch_ack_nr_t* ack_info, srsran_uci_data_nr_t* uci_data); +SRSRAN_API int srsran_ue_dl_nr_ack_insert_m(srsran_pdsch_ack_nr_t* ack_info, srsran_pdsch_ack_m_nr_t* m); + +SRSRAN_API uint32_t srsran_ue_dl_nr_ack_info(const srsran_pdsch_ack_nr_t* ack_info, char* str, uint32_t str_len); + #endif // SRSRAN_UE_DL_NR_H diff --git a/lib/src/phy/ue/test/CMakeLists.txt b/lib/src/phy/ue/test/CMakeLists.txt index 67aa58bba..9bf31d112 100644 --- a/lib/src/phy/ue/test/CMakeLists.txt +++ b/lib/src/phy/ue/test/CMakeLists.txt @@ -14,6 +14,10 @@ add_executable(gen_ack_test gen_ack_test.c) target_link_libraries(gen_ack_test srsran_phy) add_test(gen_ack_test gen_ack_test) +add_executable(gen_ack_nr_test gen_ack_nr_test.c) +target_link_libraries(gen_ack_nr_test srsran_phy) +add_test(gen_ack_nr_test gen_ack_nr_test) + add_executable(pucch_resource_test pucch_resource_test.c) target_link_libraries(pucch_resource_test srsran_phy) add_test(pucch_resource_test pucch_resource_test) diff --git a/lib/src/phy/ue/test/gen_ack_nr_test.c b/lib/src/phy/ue/test/gen_ack_nr_test.c new file mode 100644 index 000000000..378df6b5b --- /dev/null +++ b/lib/src/phy/ue/test/gen_ack_nr_test.c @@ -0,0 +1,150 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#include "srsran/common/test_common.h" +#include "srsran/phy/ue/ue_dl_nr.h" +#include + +static int test_case_1() +{ + // Set configuration + srsran_ue_dl_nr_harq_ack_cfg_t cfg = {}; + cfg.harq_ack_codebook = srsran_pdsch_harq_ack_codebook_dynamic; + + // Generate ACK information + srsran_pdsch_ack_nr_t ack_info = {}; + ack_info.nof_cc = 1; + ack_info.use_pusch = true; + + srsran_pdsch_ack_m_nr_t m = {}; + m.value[0] = 1; + m.present = true; + + m.resource.k1 = 8; + m.resource.v_dai_dl = 0; + m.value[0] = 1; + m.present = true; + TESTASSERT(srsran_ue_dl_nr_ack_insert_m(&ack_info, &m) == SRSRAN_SUCCESS); + + m.resource.k1 = 5; + m.resource.v_dai_dl = 2; + TESTASSERT(srsran_ue_dl_nr_ack_insert_m(&ack_info, &m) == SRSRAN_SUCCESS); + + m.resource.k1 = 6; + m.resource.v_dai_dl = 1; + TESTASSERT(srsran_ue_dl_nr_ack_insert_m(&ack_info, &m) == SRSRAN_SUCCESS); + + m.resource.k1 = 4; + m.resource.v_dai_dl = 3; + TESTASSERT(srsran_ue_dl_nr_ack_insert_m(&ack_info, &m) == SRSRAN_SUCCESS); + + m.resource.k1 = 3; + m.resource.v_dai_dl = 0; + TESTASSERT(srsran_ue_dl_nr_ack_insert_m(&ack_info, &m) == SRSRAN_SUCCESS); + + // Print trace + char str[512] = {}; + TESTASSERT(srsran_ue_dl_nr_ack_info(&ack_info, str, (uint32_t)sizeof(str)) > SRSRAN_SUCCESS); + INFO("%s", str); + + // Generate UCI data + srsran_uci_data_nr_t uci_data = {}; + TESTASSERT(srsran_ue_dl_nr_gen_ack(&cfg, &ack_info, &uci_data) == SRSRAN_SUCCESS); + + // Assert UCI data + TESTASSERT(uci_data.cfg.o_ack == 5); + + return SRSRAN_SUCCESS; +} + +static int test_case_2() +{ + // Set configuration + srsran_ue_dl_nr_harq_ack_cfg_t cfg = {}; + cfg.harq_ack_codebook = srsran_pdsch_harq_ack_codebook_dynamic; + + // Generate ACK information + srsran_pdsch_ack_nr_t ack_info = {}; + ack_info.nof_cc = 1; + ack_info.use_pusch = true; + + srsran_pdsch_ack_m_nr_t m = {}; + m.value[0] = 1; + m.present = true; + + m.resource.k1 = 7; + m.resource.v_dai_dl = 1; + TESTASSERT(srsran_ue_dl_nr_ack_insert_m(&ack_info, &m) == SRSRAN_SUCCESS); + + m.resource.k1 = 6; + m.resource.v_dai_dl = 2; + TESTASSERT(srsran_ue_dl_nr_ack_insert_m(&ack_info, &m) == SRSRAN_SUCCESS); + + m.resource.k1 = 8; + m.resource.v_dai_dl = 0; + TESTASSERT(srsran_ue_dl_nr_ack_insert_m(&ack_info, &m) == SRSRAN_SUCCESS); + + m.resource.k1 = 5; + m.resource.v_dai_dl = 3; + TESTASSERT(srsran_ue_dl_nr_ack_insert_m(&ack_info, &m) == SRSRAN_SUCCESS); + + m.resource.k1 = 4; + m.resource.v_dai_dl = 0; + TESTASSERT(srsran_ue_dl_nr_ack_insert_m(&ack_info, &m) == SRSRAN_SUCCESS); + + // Print trace + char str[512] = {}; + TESTASSERT(srsran_ue_dl_nr_ack_info(&ack_info, str, (uint32_t)sizeof(str)) > SRSRAN_SUCCESS); + INFO("%s", str); + + // Generate UCI data + srsran_uci_data_nr_t uci_data = {}; + TESTASSERT(srsran_ue_dl_nr_gen_ack(&cfg, &ack_info, &uci_data) == SRSRAN_SUCCESS); + + // Assert UCI data + TESTASSERT(uci_data.cfg.o_ack == 5); + + return SRSRAN_SUCCESS; +} + +static void usage(char* prog) +{ + printf("Usage: %s [v]\n", prog); + printf("\t-v Increase srsran_verbose\n"); +} + +static void parse_args(int argc, char** argv) +{ + int opt; + while ((opt = getopt(argc, argv, "v")) != -1) { + switch (opt) { + case 'v': + srsran_verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + +int main(int argc, char** argv) +{ + parse_args(argc, argv); + + // Test only until Format1B - CS + TESTASSERT(test_case_1() == SRSRAN_SUCCESS); + TESTASSERT(test_case_2() == SRSRAN_SUCCESS); + + printf("Ok\n"); + return SRSRAN_SUCCESS; +} \ No newline at end of file diff --git a/lib/src/phy/ue/ue_dl_nr.c b/lib/src/phy/ue/ue_dl_nr.c index 2c90fccb5..2e8cc014b 100644 --- a/lib/src/phy/ue/ue_dl_nr.c +++ b/lib/src/phy/ue/ue_dl_nr.c @@ -760,3 +760,72 @@ int srsran_ue_dl_nr_gen_ack(const srsran_ue_dl_nr_harq_ack_cfg_t* cfg, ERROR("No HARQ-ACK codebook determination is NOT implemented"); return SRSRAN_ERROR; } + +int srsran_ue_dl_nr_ack_insert_m(srsran_pdsch_ack_nr_t* ack_info, srsran_pdsch_ack_m_nr_t* m) +{ + // Check inputs + if (ack_info == NULL || m == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // Protect SCell index and extract information + if (m->resource.scell_idx >= SRSRAN_MAX_CARRIERS) { + ERROR("Serving cell index (%d) exceeds maximum", m->resource.scell_idx); + return SRSRAN_ERROR; + } + srsran_pdsch_ack_cc_nr_t* cc = &ack_info->cc[m->resource.scell_idx]; + + // Find insertion index + uint32_t idx = cc->M; // Append at the end by default + for (uint32_t i = 0; i < cc->M; i++) { + if (cc->m[i].resource.k1 < m->resource.k1) { + idx = i; + break; + } + } + + // Increment count + cc->M += 1; + + // Make space for insertion + for (uint32_t i = cc->M - 1; i > idx; i--) { + cc->m[i] = cc->m[i - 1]; + } + + // Actual insertion + cc->m[idx] = *m; + + return SRSRAN_SUCCESS; +} + +uint32_t srsran_ue_dl_nr_ack_info(const srsran_pdsch_ack_nr_t* ack_info, char* str, uint32_t str_len) +{ + uint32_t len = 0; + + if (ack_info == NULL || str == NULL) { + return 0; + } + + // Print base info + len = srsran_print_check( + str, str_len, len, "use_pusch=%c nof_cc=%d\n", ack_info->use_pusch ? 'y' : 'n', ack_info->nof_cc); + + // Iterate all carriers + for (uint32_t cc = 0; cc < ack_info->nof_cc; cc++) { + len = srsran_print_check(str, str_len, len, " CC %d: M=%d\n", cc, ack_info->cc[cc].M); + for (uint32_t m = 0; m < ack_info->cc[cc].M; m++) { + if (ack_info->cc[cc].m[m].present) { + len = srsran_print_check(str, + str_len, + len, + " m %d: k1=%d dai=%d ack=%d\n", + m, + ack_info->cc[cc].m[m].resource.k1, + ack_info->cc[cc].m[m].resource.v_dai_dl, + ack_info->cc[cc].m[m].value[0]); + } + } + } + + return len; +} diff --git a/srsue/hdr/phy/nr/state.h b/srsue/hdr/phy/nr/state.h index f66509077..18a2bccd4 100644 --- a/srsue/hdr/phy/nr/state.h +++ b/srsue/hdr/phy/nr/state.h @@ -214,6 +214,12 @@ public: // Calculate Receive TTI uint32_t tti_tx = TTI_ADD(tti_rx, ack_resource.k1); + // Prepare ACK information + srsran_pdsch_ack_m_nr_t ack_m = {}; + ack_m.resource = ack_resource; + ack_m.value[0] = crc_ok ? 1 : 0; + ack_m.present = true; + // Scope mutex to protect read/write the list std::lock_guard lock(pending_ack_mutex); @@ -221,15 +227,10 @@ public: srsran_pdsch_ack_nr_t& ack = pending_ack[tti_tx]; ack.nof_cc = 1; - // Select serving cell - srsran_pdsch_ack_cc_nr_t& ack_cc = ack.cc[ack_resource.scell_idx]; - srsran_pdsch_ack_m_nr_t& ack_m = ack_cc.m[ack_cc.M]; - ack_cc.M++; - - // Set PDSCH transmission information - ack_m.resource = ack_resource; - ack_m.value[0] = crc_ok ? 1 : 0; - ack_m.present = true; + // Insert PDSCH transmission information + if (srsran_ue_dl_nr_ack_insert_m(&ack, &ack_m) < SRSRAN_SUCCESS) { + ERROR("Error inserting ACK m value"); + } } bool get_pending_ack(const uint32_t& tti_tx, srsran_pdsch_ack_nr_t& pdsch_ack) diff --git a/srsue/src/phy/nr/cc_worker.cc b/srsue/src/phy/nr/cc_worker.cc index bd7862b87..3a108b1d2 100644 --- a/srsue/src/phy/nr/cc_worker.cc +++ b/srsue/src/phy/nr/cc_worker.cc @@ -318,6 +318,14 @@ bool cc_worker::work_ul() // If PDSCH UL ACK is available, load into UCI if (has_ul_ack) { pdsch_ack.use_pusch = has_pusch_grant; + + if (logger.debug.enabled()) { + std::array str = {}; + if (srsran_ue_dl_nr_ack_info(&pdsch_ack, str.data(), (uint32_t)str.size()) > 0) { + logger.debug("%s", str.data()); + } + } + if (srsran_ue_dl_nr_gen_ack(&phy->cfg.harq_ack, &pdsch_ack, &uci_data) < SRSRAN_SUCCESS) { ERROR("Filling UCI ACK bits"); return false;