From f24e5aadaf0743c0545c32efd2308de92729d2ed Mon Sep 17 00:00:00 2001 From: Francisco Paisana Date: Tue, 10 Nov 2020 16:51:21 +0000 Subject: [PATCH] clean common sched output tests --- srsenb/test/mac/CMakeLists.txt | 2 +- srsenb/test/mac/sched_common_test_suite.cc | 301 +++++++++++++++++++++ srsenb/test/mac/sched_common_test_suite.h | 109 ++++++++ srsenb/test/mac/scheduler_test_common.cc | 278 +------------------ srsenb/test/mac/scheduler_test_common.h | 41 --- srsenb/test/mac/scheduler_test_rand.cc | 87 ++---- 6 files changed, 435 insertions(+), 383 deletions(-) create mode 100644 srsenb/test/mac/sched_common_test_suite.cc create mode 100644 srsenb/test/mac/sched_common_test_suite.h diff --git a/srsenb/test/mac/CMakeLists.txt b/srsenb/test/mac/CMakeLists.txt index 93cfd9e35..5d8ec5a9e 100644 --- a/srsenb/test/mac/CMakeLists.txt +++ b/srsenb/test/mac/CMakeLists.txt @@ -18,7 +18,7 @@ # and at http://www.gnu.org/licenses/. # -add_library(scheduler_test_common STATIC scheduler_test_common.cc) +add_library(scheduler_test_common STATIC scheduler_test_common.cc sched_common_test_suite.cc) # Scheduler subcomponent testing add_executable(sched_grid_test sched_grid_test.cc) diff --git a/srsenb/test/mac/sched_common_test_suite.cc b/srsenb/test/mac/sched_common_test_suite.cc new file mode 100644 index 000000000..bf2188f99 --- /dev/null +++ b/srsenb/test/mac/sched_common_test_suite.cc @@ -0,0 +1,301 @@ +/* + * Copyright 2013-2020 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "sched_common_test_suite.h" +#include "lib/include/srslte/phy/phch/prach.h" +#include "srslte/common/test_common.h" + +using srslte::tti_point; + +namespace srsenb { + +int test_pusch_collisions(const sf_output_res_t& sf_out, const prbmask_t* expected_ul_mask) +{ + auto& cell_params = sf_out.cell_params; + uint32_t nof_prb = cell_params.nof_prb(); + prbmask_t ul_allocs(nof_prb); + + auto try_ul_fill = [&](prb_interval alloc, const char* ch_str, bool strict = true) { + CONDERROR(alloc.stop() > nof_prb, "Allocated RBs %s out-of-bounds\n", alloc.to_string().c_str()); + CONDERROR(alloc.empty(), "Allocations must have at least one PRB\n"); + if (strict and ul_allocs.any(alloc.start(), alloc.stop())) { + TESTERROR("Collision Detected of %s alloc=%s and cumulative_mask=0x%s\n", + ch_str, + alloc.to_string().c_str(), + ul_allocs.to_hex().c_str()); + } + ul_allocs.fill(alloc.start(), alloc.stop(), true); + return SRSLTE_SUCCESS; + }; + + /* TEST: Check if there is space for PRACH */ + bool is_prach_tti_tx_ul = + srslte_prach_tti_opportunity_config_fdd(sf_out.cell_params.cfg.prach_config, sf_out.tti_tx_ul().to_uint(), -1); + if (is_prach_tti_tx_ul) { + try_ul_fill({cell_params.cfg.prach_freq_offset, cell_params.cfg.prach_freq_offset + 6}, "PRACH"); + } + + /* TEST: check collisions in PUCCH */ + bool strict = nof_prb != 6 or (not is_prach_tti_tx_ul); // and not tti_data.ul_pending_msg3_present); + try_ul_fill({0, (uint32_t)cell_params.cfg.nrb_pucch}, "PUCCH", strict); + try_ul_fill({cell_params.cfg.cell.nof_prb - cell_params.cfg.nrb_pucch, (uint32_t)cell_params.cfg.cell.nof_prb}, + "PUCCH", + strict); + + /* TEST: check collisions in the UL PUSCH */ + for (uint32_t i = 0; i < sf_out.ul_result.nof_dci_elems; ++i) { + uint32_t L, RBstart; + srslte_ra_type2_from_riv(sf_out.ul_result.pusch[i].dci.type2_alloc.riv, &L, &RBstart, nof_prb, nof_prb); + strict = sf_out.ul_result.pusch[i].needs_pdcch or nof_prb != 6; // Msg3 may collide with PUCCH at PRB==6 + try_ul_fill({RBstart, RBstart + L}, "PUSCH", strict); + } + + CONDERROR(expected_ul_mask != nullptr and *expected_ul_mask != ul_allocs, + "The derived UL PRB mask %s does not match the expected one %s\n", + ul_allocs.to_string().c_str(), + expected_ul_mask->to_string().c_str()); + + return SRSLTE_SUCCESS; +} + +int extract_dl_prbmask(const srslte_cell_t& cell, + const srslte_dci_dl_t& dci, + srslte::bounded_bitset<100, true>& alloc_mask) +{ + srslte_pdsch_grant_t grant; + srslte_dl_sf_cfg_t dl_sf = {}; + + alloc_mask.resize(cell.nof_prb); + alloc_mask.reset(); + + CONDERROR(srslte_ra_dl_dci_to_grant(&cell, &dl_sf, SRSLTE_TM1, false, &dci, &grant) == SRSLTE_ERROR, + "Failed to decode PDSCH grant\n"); + for (uint32_t j = 0; j < alloc_mask.size(); ++j) { + if (grant.prb_idx[0][j]) { + alloc_mask.set(j); + } + } + return SRSLTE_SUCCESS; +} + +int test_pdsch_collisions(const sf_output_res_t& sf_out, const rbgmask_t* expected_rbgmask) +{ + srslte::bounded_bitset<100, true> dl_allocs(sf_out.cell_params.nof_prb()), alloc_mask(sf_out.cell_params.nof_prb()); + rbgmask_t rbgmask{sf_out.cell_params.nof_rbgs}; + + auto try_dl_mask_fill = [&](const srslte_dci_dl_t& dci, const char* channel) { + if (extract_dl_prbmask(sf_out.cell_params.cfg.cell, dci, alloc_mask) != SRSLTE_SUCCESS) { + return SRSLTE_ERROR; + } + CONDERROR(alloc_mask.none(), "DL allocation must occupy at least one RBG.\n"); + if ((dl_allocs & alloc_mask).any()) { + TESTERROR("Detected collision in the DL %s allocation (%s intersects %s)\n", + channel, + dl_allocs.to_string().c_str(), + alloc_mask.to_string().c_str()); + } + dl_allocs |= alloc_mask; + return SRSLTE_SUCCESS; + }; + + // Decode BC allocations, check collisions, and fill cumulative mask + for (uint32_t i = 0; i < sf_out.dl_result.nof_bc_elems; ++i) { + TESTASSERT(try_dl_mask_fill(sf_out.dl_result.bc[i].dci, "BC") == SRSLTE_SUCCESS); + } + + // Decode RAR allocations, check collisions, and fill cumulative mask + for (uint32_t i = 0; i < sf_out.dl_result.nof_rar_elems; ++i) { + TESTASSERT(try_dl_mask_fill(sf_out.dl_result.rar[i].dci, "RAR") == SRSLTE_SUCCESS); + } + + // forbid Data in DL if its ACKs conflict with PRACH for PRB==6 + if (sf_out.cell_params.nof_prb() == 6) { + if (srslte_prach_tti_opportunity_config_fdd( + sf_out.cell_params.cfg.prach_config, sf_out.tti_rx_ack_dl().to_uint(), -1)) { + dl_allocs.fill(0, dl_allocs.size()); + } + } + + // Decode Data allocations, check collisions and fill cumulative mask + for (uint32_t i = 0; i < sf_out.dl_result.nof_data_elems; ++i) { + TESTASSERT(try_dl_mask_fill(sf_out.dl_result.data[i].dci, "data") == SRSLTE_SUCCESS); + } + + // TEST: check for holes in the PRB mask (RBGs not fully filled) + rbgmask.resize(sf_out.cell_params.nof_rbgs); + rbgmask.reset(); + srslte::bounded_bitset<100, true> rev_alloc = ~dl_allocs; + for (uint32_t i = 0; i < sf_out.cell_params.nof_rbgs; ++i) { + uint32_t lim = SRSLTE_MIN((i + 1) * sf_out.cell_params.P, dl_allocs.size()); + bool val = dl_allocs.any(i * sf_out.cell_params.P, lim); + CONDERROR(rev_alloc.any(i * sf_out.cell_params.P, lim) and val, "No holes can be left in an RBG\n"); + if (val) { + rbgmask.set(i); + } + } + + CONDERROR(expected_rbgmask != nullptr and *expected_rbgmask != rbgmask, + "The derived DL RBG mask %s does not match the expected one %s\n", + rbgmask.to_string().c_str(), + expected_rbgmask->to_string().c_str()); + + return SRSLTE_SUCCESS; +} + +/** + * - SIB1 is allocated in correct TTIs + * - TB size is adequate for SIB allocation + * - The SIBs with index>1 are allocated in expected TTI windows + */ +int test_sib_scheduling(const sf_output_res_t& sf_out) +{ + uint32_t sfn = sf_out.tti_tx_dl().to_uint() / 10; + uint32_t sf_idx = sf_out.tti_tx_dl().to_uint() % 10; + bool sib1_expected = ((sfn % 2) == 0) and sf_idx == 5; + + using bc_elem = const sched_interface::dl_sched_bc_t; + bc_elem* bc_begin = &sf_out.dl_result.bc[0]; + bc_elem* bc_end = &sf_out.dl_result.bc[sf_out.dl_result.nof_bc_elems]; + + /* Test if SIB1 was correctly scheduled */ + auto it = std::find_if(bc_begin, bc_end, [](bc_elem& elem) { return elem.index == 0; }); + CONDERROR(sib1_expected and it == bc_end, "Failed to allocate SIB1 in even sfn, sf_idx==5\n"); + CONDERROR(not sib1_expected and it != bc_end, "SIB1 allocated in wrong TTI.\n"); + + /* Test if any SIB was scheduled with wrong index, tbs, or outside of its window */ + for (bc_elem* bc = bc_begin; bc != bc_end; ++bc) { + if (bc->index == 0) { + continue; + } + CONDERROR(bc->index >= sched_interface::MAX_SIBS, "Invalid SIB idx=%d\n", bc->index + 1); + CONDERROR(bc->tbs < sf_out.cell_params.cfg.sibs[bc->index].len, + "Allocated BC process with TBS=%d < sib_len=%d\n", + bc->tbs, + sf_out.cell_params.cfg.sibs[bc->index].len); + uint32_t x = (bc->index - 1) * sf_out.cell_params.cfg.si_window_ms; + uint32_t sf = x % 10; + uint32_t sfn_start = sfn; + while ((sfn_start % sf_out.cell_params.cfg.sibs[bc->index].period_rf) != x / 10) { + sfn_start--; + } + srslte::tti_point win_start{sfn_start * 10 + sf}; + srslte::tti_interval window{win_start, win_start + sf_out.cell_params.cfg.si_window_ms}; + CONDERROR(not window.contains(sf_out.tti_tx_dl()), "Scheduled SIB is outside of its SIB window\n"); + } + return SRSLTE_SUCCESS; +} + +int test_pdcch_collisions(const sf_output_res_t& sf_out, const srslte::bounded_bitset<128, true>* expected_cce_mask) +{ + int ret = srslte_regs_pdcch_ncce(sf_out.cell_params.regs.get(), sf_out.dl_result.cfi); + TESTASSERT(ret > 0); + uint32_t ncce = ret; + srslte::bounded_bitset<128, true> used_cce{ncce}; + + // Helper Function: checks if there is any collision. If not, fills the PDCCH mask + auto try_cce_fill = [&](const srslte_dci_location_t& dci_loc, const char* ch) { + uint32_t cce_start = dci_loc.ncce, cce_stop = dci_loc.ncce + (1u << dci_loc.L); + CONDERROR(dci_loc.L == 0, "The aggregation level %d is not valid\n", dci_loc.L); + CONDERROR( + cce_start >= ncce or cce_stop > ncce, "The CCE positions (%u, %u) do not fit in PDCCH\n", cce_start, cce_stop); + CONDERROR(used_cce.any(cce_start, cce_stop), + "%s DCI collision between CCE positions (%u, %u)\n", + ch, + cce_start, + cce_stop); + used_cce.fill(cce_start, cce_stop); + return SRSLTE_SUCCESS; + }; + + /* TEST: verify there are no dci collisions for UL, DL data, BC, RAR */ + for (uint32_t i = 0; i < sf_out.ul_result.nof_dci_elems; ++i) { + const auto& pusch = sf_out.ul_result.pusch[i]; + if (not pusch.needs_pdcch) { + // In case of non-adaptive retx or Msg3 + continue; + } + try_cce_fill(pusch.dci.location, "UL"); + } + for (uint32_t i = 0; i < sf_out.dl_result.nof_data_elems; ++i) { + try_cce_fill(sf_out.dl_result.data[i].dci.location, "DL data"); + } + for (uint32_t i = 0; i < sf_out.dl_result.nof_bc_elems; ++i) { + try_cce_fill(sf_out.dl_result.bc[i].dci.location, "DL BC"); + } + for (uint32_t i = 0; i < sf_out.dl_result.nof_rar_elems; ++i) { + try_cce_fill(sf_out.dl_result.rar[i].dci.location, "DL RAR"); + } + + CONDERROR(expected_cce_mask != nullptr and *expected_cce_mask != used_cce, + "The derived PDCCH mask %s does not match the expected one %s\n", + used_cce.to_string().c_str(), + expected_cce_mask->to_string().c_str()); + + return SRSLTE_SUCCESS; +} + +int test_dci_content_common(const sf_output_res_t& sf_out) +{ + for (uint32_t i = 0; i < sf_out.ul_result.nof_dci_elems; ++i) { + const auto& pusch = sf_out.ul_result.pusch[i]; + CONDERROR(pusch.tbs == 0, "Allocated PUSCH with invalid TBS=%d\n", pusch.tbs); + if (not pusch.needs_pdcch) { + // In case of non-adaptive retx or Msg3 + continue; + } + // TODO: extend this test + } + for (uint32_t i = 0; i < sf_out.dl_result.nof_data_elems; ++i) { + auto& data = sf_out.dl_result.data[i]; + CONDERROR(data.tbs[0] == 0, "Allocated DL data has empty TBS\n"); + } + for (uint32_t i = 0; i < sf_out.dl_result.nof_bc_elems; ++i) { + auto& bc = sf_out.dl_result.bc[i]; + if (bc.type == sched_interface::dl_sched_bc_t::BCCH) { + CONDERROR(bc.tbs < sf_out.cell_params.cfg.sibs[bc.index].len, + "Allocated BC process with TBS=%d < sib_len=%d\n", + bc.tbs, + sf_out.cell_params.cfg.sibs[bc.index].len); + } else if (bc.type == sched_interface::dl_sched_bc_t::PCCH) { + CONDERROR(bc.tbs == 0, "Allocated paging process with invalid TBS=%d\n", bc.tbs); + } else { + TESTERROR("Invalid broadcast process id=%d\n", (int)bc.type); + } + } + for (uint32_t i = 0; i < sf_out.dl_result.nof_rar_elems; ++i) { + const auto& rar = sf_out.dl_result.rar[i]; + CONDERROR(rar.tbs == 0, "Allocated RAR process with invalid TBS=%d\n", rar.tbs); + } + + return SRSLTE_SUCCESS; +} + +int test_all_common(const sf_output_res_t& sf_out) +{ + TESTASSERT(test_pusch_collisions(sf_out, nullptr) == SRSLTE_SUCCESS); + TESTASSERT(test_pdsch_collisions(sf_out, nullptr) == SRSLTE_SUCCESS); + TESTASSERT(test_sib_scheduling(sf_out) == SRSLTE_SUCCESS); + TESTASSERT(test_pdcch_collisions(sf_out, nullptr) == SRSLTE_SUCCESS); + TESTASSERT(test_dci_content_common(sf_out) == SRSLTE_SUCCESS); + return SRSLTE_SUCCESS; +} + +} // namespace srsenb diff --git a/srsenb/test/mac/sched_common_test_suite.h b/srsenb/test/mac/sched_common_test_suite.h new file mode 100644 index 000000000..c7b6a73a0 --- /dev/null +++ b/srsenb/test/mac/sched_common_test_suite.h @@ -0,0 +1,109 @@ +/* + * Copyright 2013-2020 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSLTE_SCHED_COMMON_TEST_SUITE_H +#define SRSLTE_SCHED_COMMON_TEST_SUITE_H + +#include "srsenb/hdr/stack/mac/scheduler_common.h" +#include "srslte/common/bounded_bitset.h" +#include "srslte/common/tti_point.h" +#include "srslte/interfaces/sched_interface.h" + +namespace srsenb { + +struct sf_output_res_t { + const sched_cell_params_t& cell_params; + srslte::tti_point tti_rx; + const sched_interface::ul_sched_res_t& ul_result; + const sched_interface::dl_sched_res_t& dl_result; + srslte::tti_point tti_tx_ul() const { return srslte::to_tx_ul(tti_rx); } + srslte::tti_point tti_rx_ack_dl() const { return tti_tx_ul(); } + srslte::tti_point tti_tx_dl() const { return srslte::to_tx_dl(tti_rx); } +}; + +/** + * verifies correctness of the output of PUSCH allocations for a subframe. Current checks: + * - individual ul allocs are within (0, n_prb) bounds + * - individual ul allocs occupy at least one prb + * - no collisions between individual ul allocs + * - space is left for PRACH and PUCCH + * - if expected_ul_mask arg is not null, assert it matches the cumulative mask of all PUSCH allocations + * @param sf_out result of subframe allocation and associated metadata + * @param expected_ul_mask optional arg for expected cumulative UL PRB mask + * @return error code + */ +int test_pusch_collisions(const sf_output_res_t& sf_out, const prbmask_t* expected_ul_mask); + +/** + * verifies correctness of the output of PDSCH allocations for a subframe. Current checks: + * - individual DL allocs are within (0, n_prb) bounds + * - individual DL allocs occupy at least one RBG + * - no collisions between individual DL allocs + * - DL data alloc ACKs do not collide with PRACH in UL + * - if expected_rbgmask arg is not null, assert it matches the cumulative mask of all PDSCH allocations + * @param sf_out result of subframe allocation and associated metadata + * @param expected_rbgmask optional arg for expected cumulative DL RBG mask + * @return error code + */ +int test_pdsch_collisions(const sf_output_res_t& sf_out, const rbgmask_t* expected_rbgmask); + +/** + * verifies correctness of SIB allocations. Current checks: + * - SIB1 is allocated in correct TTIs + * - TB size is adequate for SIB allocation + * - The SIBs with index>1 are allocated in expected TTI windows + * @param sf_out result of subframe allocation and associated metadata + * @return error code + */ +int test_sib_scheduling(const sf_output_res_t& sf_out); + +/** + * verifies correctness of PDCCH allocations for DL, RAR, UL, Broadcast. Current checks: + * - DCI CCE positions fit in available PDCCH resources + * - no collisions between individual DCI CCE positions + * @param sf_out + * @param used_cce + * @return error code + */ +int test_pdcch_collisions(const sf_output_res_t& sf_out, const srslte::bounded_bitset<128, true>* expected_cce_mask); + +/** + * verifies correctness of DCI content for params that are independent of the UE configuration. + * - TB size is large enough + * @param sf_out + * @return error code + */ +int test_dci_content_common(const sf_output_res_t& sf_out); + +/** + * Call all available eNB common tests + * @return error code + */ +int test_all_common(const sf_output_res_t& sf_out); + +/// Helper function to get DL PRBs from DCI +int extract_dl_prbmask(const srslte_cell_t& cell, + const srslte_dci_dl_t& dci, + srslte::bounded_bitset<100, true>& alloc_mask); + +} // namespace srsenb + +#endif // SRSLTE_SCHED_COMMON_TEST_SUITE_H diff --git a/srsenb/test/mac/scheduler_test_common.cc b/srsenb/test/mac/scheduler_test_common.cc index c7ca3ca36..66a55a67e 100644 --- a/srsenb/test/mac/scheduler_test_common.cc +++ b/srsenb/test/mac/scheduler_test_common.cc @@ -24,6 +24,7 @@ #include "srsenb/hdr/stack/upper/common_enb.h" #include "srslte/mac/pdu.h" +#include "sched_common_test_suite.h" #include "srslte/common/test_common.h" #include @@ -52,250 +53,6 @@ std::default_random_engine& ::srsenb::get_rand_gen() return rand_gen; } -/*************************** - * Sched Testers - **************************/ - -int output_sched_tester::test_pusch_collisions(const tti_params_t& tti_params, - const sched_interface::ul_sched_res_t& ul_result, - prbmask_t& ul_allocs) const -{ - uint32_t nof_prb = cell_params.nof_prb(); - ul_allocs.resize(nof_prb); - ul_allocs.reset(); - - auto try_ul_fill = [&](prb_interval alloc, const char* ch_str, bool strict = true) { - CONDERROR(alloc.stop() > nof_prb, "Allocated RBs %s out-of-bounds\n", alloc.to_string().c_str()); - CONDERROR(alloc.empty(), "Allocations must have at least one PRB\n"); - if (strict and ul_allocs.any(alloc.start(), alloc.stop())) { - TESTERROR("Collision Detected of %s alloc=%s and cumulative_mask=0x%s\n", - ch_str, - alloc.to_string().c_str(), - ul_allocs.to_hex().c_str()); - } - ul_allocs.fill(alloc.start(), alloc.stop(), true); - return SRSLTE_SUCCESS; - }; - - /* TEST: Check if there is space for PRACH */ - bool is_prach_tti_tx_ul = - srslte_prach_tti_opportunity_config_fdd(cell_params.cfg.prach_config, tti_params.tti_tx_ul, -1); - if (is_prach_tti_tx_ul) { - try_ul_fill({cell_params.cfg.prach_freq_offset, cell_params.cfg.prach_freq_offset + 6}, "PRACH"); - } - - /* TEST: check collisions in PUCCH */ - bool strict = nof_prb != 6 or (not is_prach_tti_tx_ul); // and not tti_data.ul_pending_msg3_present); - try_ul_fill({0, (uint32_t)cell_params.cfg.nrb_pucch}, "PUCCH", strict); - try_ul_fill({cell_params.cfg.cell.nof_prb - cell_params.cfg.nrb_pucch, (uint32_t)cell_params.cfg.cell.nof_prb}, - "PUCCH", - strict); - - /* TEST: check collisions in the UL PUSCH */ - for (uint32_t i = 0; i < ul_result.nof_dci_elems; ++i) { - uint32_t L, RBstart; - srslte_ra_type2_from_riv(ul_result.pusch[i].dci.type2_alloc.riv, &L, &RBstart, nof_prb, nof_prb); - strict = ul_result.pusch[i].needs_pdcch or nof_prb != 6; // Msg3 may collide with PUCCH at PRB==6 - try_ul_fill({RBstart, RBstart + L}, "PUSCH", strict); - // ue_stats[ul_result.pusch[i].dci.rnti].nof_ul_rbs += L; - } - - return SRSLTE_SUCCESS; -} - -int output_sched_tester::test_pdsch_collisions(const tti_params_t& tti_params, - const sched_interface::dl_sched_res_t& dl_result, - rbgmask_t& rbgmask) const -{ - srslte::bounded_bitset<100, true> dl_allocs(cell_params.cfg.cell.nof_prb), alloc_mask(cell_params.cfg.cell.nof_prb); - - auto try_dl_mask_fill = [&](const srslte_dci_dl_t& dci, const char* channel) { - if (extract_dl_prbmask(cell_params.cfg.cell, dci, &alloc_mask) != SRSLTE_SUCCESS) { - return SRSLTE_ERROR; - } - if ((dl_allocs & alloc_mask).any()) { - TESTERROR("Detected collision in the DL %s allocation (%s intersects %s)\n", - channel, - dl_allocs.to_string().c_str(), - alloc_mask.to_string().c_str()); - } - dl_allocs |= alloc_mask; - return SRSLTE_SUCCESS; - }; - - // Decode BC allocations, check collisions, and fill cumulative mask - for (uint32_t i = 0; i < dl_result.nof_bc_elems; ++i) { - TESTASSERT(try_dl_mask_fill(dl_result.bc[i].dci, "BC") == SRSLTE_SUCCESS); - } - - // Decode RAR allocations, check collisions, and fill cumulative mask - for (uint32_t i = 0; i < dl_result.nof_rar_elems; ++i) { - TESTASSERT(try_dl_mask_fill(dl_result.rar[i].dci, "RAR") == SRSLTE_SUCCESS); - } - - // forbid Data in DL if it conflicts with PRACH for PRB==6 - if (cell_params.cfg.cell.nof_prb == 6) { - uint32_t tti_rx_ack = tti_params.tti_rx_ack_dl(); - if (srslte_prach_tti_opportunity_config_fdd(cell_params.cfg.prach_config, tti_rx_ack, -1)) { - dl_allocs.fill(0, dl_allocs.size()); - } - } - - // Decode Data allocations, check collisions and fill cumulative mask - for (uint32_t i = 0; i < dl_result.nof_data_elems; ++i) { - TESTASSERT(try_dl_mask_fill(dl_result.data[i].dci, "data") == SRSLTE_SUCCESS); - } - - // TEST: check for holes in the PRB mask (RBGs not fully filled) - rbgmask.resize(cell_params.nof_rbgs); - rbgmask.reset(); - srslte::bounded_bitset<100, true> rev_alloc = ~dl_allocs; - for (uint32_t i = 0; i < cell_params.nof_rbgs; ++i) { - uint32_t lim = SRSLTE_MIN((i + 1) * cell_params.P, dl_allocs.size()); - bool val = dl_allocs.any(i * cell_params.P, lim); - CONDERROR(rev_alloc.any(i * cell_params.P, lim) and val, "No holes can be left in an RBG\n"); - if (val) { - rbgmask.set(i); - } - } - - return SRSLTE_SUCCESS; -} - -int output_sched_tester::test_sib_scheduling(const tti_params_t& tti_params, - const sched_interface::dl_sched_res_t& dl_result) const -{ - uint32_t sfn = tti_params.sfn_tx_dl; - uint32_t sf_idx = tti_params.sf_idx_tx_dl; - bool sib1_present = ((sfn % 2) == 0) and sf_idx == 5; - - using bc_elem = const sched_interface::dl_sched_bc_t; - bc_elem* bc_begin = &dl_result.bc[0]; - bc_elem* bc_end = &dl_result.bc[dl_result.nof_bc_elems]; - - /* Test if SIB1 was correctly scheduled */ - if (sib1_present) { - auto it = std::find_if(bc_begin, bc_end, [](bc_elem& elem) { return elem.index == 0; }); - CONDERROR(it == bc_end, "Failed to allocate SIB1 in even sfn, sf_idx==5\n"); - } - - /* Test if any SIB was scheduled with wrong index, tbs, or outside of its window */ - for (bc_elem* bc = bc_begin; bc != bc_end; ++bc) { - if (bc->index == 0) { - continue; - } - CONDERROR(bc->index >= sched_interface::MAX_SIBS, "Invalid SIB idx=%d\n", bc->index + 1); - CONDERROR(bc->tbs < cell_params.cfg.sibs[bc->index].len, - "Allocated BC process with TBS=%d < sib_len=%d\n", - bc->tbs, - cell_params.cfg.sibs[bc->index].len); - uint32_t x = (bc->index - 1) * cell_params.cfg.si_window_ms; - uint32_t sf = x % 10; - uint32_t sfn_start = sfn; - while ((sfn_start % cell_params.cfg.sibs[bc->index].period_rf) != x / 10) { - sfn_start--; - } - uint32_t win_start = sfn_start * 10 + sf; - uint32_t win_end = win_start + cell_params.cfg.si_window_ms; - CONDERROR(tti_params.tti_tx_dl < win_start or tti_params.tti_tx_dl > win_end, - "Scheduled SIB is outside of its SIB window\n"); - } - return SRSLTE_SUCCESS; -} - -int output_sched_tester::test_pdcch_collisions(const sched_interface::dl_sched_res_t& dl_result, - const sched_interface::ul_sched_res_t& ul_result, - srslte::bounded_bitset<128, true>* used_cce) const -{ - used_cce->resize(srslte_regs_pdcch_ncce(cell_params.regs.get(), dl_result.cfi)); - used_cce->reset(); - - // Helper Function: checks if there is any collision. If not, fills the PDCCH mask - auto try_cce_fill = [&](const srslte_dci_location_t& dci_loc, const char* ch) { - uint32_t cce_start = dci_loc.ncce, cce_stop = dci_loc.ncce + (1u << dci_loc.L); - if (used_cce->any(cce_start, cce_stop)) { - TESTERROR("%s DCI collision between CCE positions (%u, %u)\n", ch, cce_start, cce_stop); - } - used_cce->fill(cce_start, cce_stop); - return SRSLTE_SUCCESS; - }; - - /* TEST: verify there are no dci collisions for UL, DL data, BC, RAR */ - for (uint32_t i = 0; i < ul_result.nof_dci_elems; ++i) { - const auto& pusch = ul_result.pusch[i]; - if (not pusch.needs_pdcch) { - // In case of non-adaptive retx or Msg3 - continue; - } - try_cce_fill(pusch.dci.location, "UL"); - } - for (uint32_t i = 0; i < dl_result.nof_data_elems; ++i) { - try_cce_fill(dl_result.data[i].dci.location, "DL data"); - } - for (uint32_t i = 0; i < dl_result.nof_bc_elems; ++i) { - try_cce_fill(dl_result.bc[i].dci.location, "DL BC"); - } - for (uint32_t i = 0; i < dl_result.nof_rar_elems; ++i) { - try_cce_fill(dl_result.rar[i].dci.location, "DL RAR"); - } - - return SRSLTE_SUCCESS; -} - -int output_sched_tester::test_dci_values_consistency(const sched_interface::dl_sched_res_t& dl_result, - const sched_interface::ul_sched_res_t& ul_result) const -{ - for (uint32_t i = 0; i < ul_result.nof_dci_elems; ++i) { - const auto& pusch = ul_result.pusch[i]; - CONDERROR(pusch.tbs == 0, "Allocated PUSCH with invalid TBS=%d\n", pusch.tbs); - // CONDERROR(ue_db.count(pusch.dci.rnti) == 0, "The allocated rnti=0x%x does not exist\n", pusch.dci.rnti); - if (not pusch.needs_pdcch) { - // In case of non-adaptive retx or Msg3 - continue; - } - CONDERROR(pusch.dci.location.L == 0, - "Invalid aggregation level %d\n", - pusch.dci.location.L); // TODO: Extend this test - } - for (uint32_t i = 0; i < dl_result.nof_data_elems; ++i) { - auto& data = dl_result.data[i]; - CONDERROR(data.tbs[0] == 0, "Allocated DL data has empty TBS\n"); - } - for (uint32_t i = 0; i < dl_result.nof_bc_elems; ++i) { - auto& bc = dl_result.bc[i]; - if (bc.type == sched_interface::dl_sched_bc_t::BCCH) { - CONDERROR(bc.tbs < cell_params.cfg.sibs[bc.index].len, - "Allocated BC process with TBS=%d < sib_len=%d\n", - bc.tbs, - cell_params.cfg.sibs[bc.index].len); - } else if (bc.type == sched_interface::dl_sched_bc_t::PCCH) { - CONDERROR(bc.tbs == 0, "Allocated paging process with invalid TBS=%d\n", bc.tbs); - } else { - TESTERROR("Invalid broadcast process id=%d\n", (int)bc.type); - } - } - for (uint32_t i = 0; i < dl_result.nof_rar_elems; ++i) { - const auto& rar = dl_result.rar[i]; - CONDERROR(rar.tbs == 0, "Allocated RAR process with invalid TBS=%d\n", rar.tbs); - } - - return SRSLTE_SUCCESS; -} - -int output_sched_tester::test_all(const tti_params_t& tti_params, - const sched_interface::dl_sched_res_t& dl_result, - const sched_interface::ul_sched_res_t& ul_result) const -{ - prbmask_t ul_allocs; - TESTASSERT(test_pusch_collisions(tti_params, ul_result, ul_allocs) == SRSLTE_SUCCESS); - rbgmask_t dl_mask; - TESTASSERT(test_pdsch_collisions(tti_params, dl_result, dl_mask) == SRSLTE_SUCCESS); - TESTASSERT(test_sib_scheduling(tti_params, dl_result) == SRSLTE_SUCCESS); - srslte::bounded_bitset<128, true> used_cce; - TESTASSERT(test_pdcch_collisions(dl_result, ul_result, &used_cce) == SRSLTE_SUCCESS); - return SRSLTE_SUCCESS; -} - /*********************** * User State Tester ***********************/ @@ -705,26 +462,6 @@ int ue_ctxt_test::schedule_acks(cc_result result) return SRSLTE_SUCCESS; } -int srsenb::extract_dl_prbmask(const srslte_cell_t& cell, - const srslte_dci_dl_t& dci, - srslte::bounded_bitset<100, true>* alloc_mask) -{ - srslte_pdsch_grant_t grant; - srslte_dl_sf_cfg_t dl_sf = {}; - - alloc_mask->resize(cell.nof_prb); - alloc_mask->reset(); - - CONDERROR(srslte_ra_dl_dci_to_grant(&cell, &dl_sf, SRSLTE_TM1, false, &dci, &grant) == SRSLTE_ERROR, - "Failed to decode PDSCH grant\n"); - for (uint32_t j = 0; j < alloc_mask->size(); ++j) { - if (grant.prb_idx[0][j]) { - alloc_mask->set(j); - } - } - return SRSLTE_SUCCESS; -} - void user_state_sched_tester::new_tti(sched* sched_ptr, uint32_t tti_rx) { tic++; @@ -878,11 +615,6 @@ int common_sched_tester::sim_cfg(sim_sched_args args) sched::set_sched_cfg(&sim_args0.sched_args); ue_tester.reset(new user_state_sched_tester{sim_args0.cell_cfg}); - output_tester.clear(); - output_tester.reserve(sim_args0.cell_cfg.size()); - for (uint32_t i = 0; i < sim_args0.cell_cfg.size(); ++i) { - output_tester.emplace_back(sched_cell_params[i]); - } sched_stats.reset(new sched_result_stats{sim_args0.cell_cfg}); tester_log = sim_args0.sim_log; @@ -950,8 +682,12 @@ int common_sched_tester::process_results() { for (uint32_t i = 0; i < sched_cell_params.size(); ++i) { TESTASSERT(ue_tester->test_all(i, tti_info.dl_sched_result[i], tti_info.ul_sched_result[i]) == SRSLTE_SUCCESS); - TESTASSERT(output_tester[i].test_all( - tti_info.tti_params, tti_info.dl_sched_result[i], tti_info.ul_sched_result[i]) == SRSLTE_SUCCESS); + + sf_output_res_t sf_out{sched_cell_params[i], + srslte::tti_point{tti_info.tti_params.tti_rx}, + tti_info.ul_sched_result[i], + tti_info.dl_sched_result[i]}; + TESTASSERT(test_all_common(sf_out) == SRSLTE_SUCCESS); } sched_stats->process_results(tti_info.tti_params, tti_info.dl_sched_result, tti_info.ul_sched_result); diff --git a/srsenb/test/mac/scheduler_test_common.h b/srsenb/test/mac/scheduler_test_common.h index 10aac52d1..518447372 100644 --- a/srsenb/test/mac/scheduler_test_common.h +++ b/srsenb/test/mac/scheduler_test_common.h @@ -36,50 +36,10 @@ void set_randseed(uint64_t seed); float randf(); std::default_random_engine& get_rand_gen(); -// other helpers -int extract_dl_prbmask(const srslte_cell_t& cell, - const srslte_dci_dl_t& dci, - srslte::bounded_bitset<100, true>* alloc_mask); - /************************** * Testers *************************/ -class output_sched_tester -{ -public: - explicit output_sched_tester(const sched_cell_params_t& cell_params_) : cell_params(cell_params_) {} - - /* Check for collisions between RB allocations in the PUSCH and PUCCH */ - int test_pusch_collisions(const tti_params_t& tti_params, - const sched_interface::ul_sched_res_t& ul_result, - prbmask_t& ul_allocs) const; - - /* Check for collision between RB allocations in the PDSCH */ - int test_pdsch_collisions(const tti_params_t& tti_params, - const sched_interface::dl_sched_res_t& dl_result, - rbgmask_t& dl_mask) const; - - /* Check if SIBs are scheduled within their window */ - int test_sib_scheduling(const tti_params_t& tti_params, const sched_interface::dl_sched_res_t& dl_result) const; - - /* Check for collisions in the PDCCH */ - int test_pdcch_collisions(const sched_interface::dl_sched_res_t& dl_result, - const sched_interface::ul_sched_res_t& ul_result, - srslte::bounded_bitset<128, true>* used_cce) const; - - /* Check DCI params correctness */ - int test_dci_values_consistency(const sched_interface::dl_sched_res_t& dl_result, - const sched_interface::ul_sched_res_t& ul_result) const; - - int test_all(const tti_params_t& tti_params, - const sched_interface::dl_sched_res_t& dl_result, - const sched_interface::ul_sched_res_t& ul_result) const; - -private: - const sched_cell_params_t& cell_params; -}; - using dl_sched_res_list = std::vector; using ul_sched_res_list = std::vector; @@ -261,7 +221,6 @@ public: uint32_t tti_count = 0; // testers - std::vector output_tester; std::unique_ptr ue_tester; std::unique_ptr sched_stats; diff --git a/srsenb/test/mac/scheduler_test_rand.cc b/srsenb/test/mac/scheduler_test_rand.cc index e13347529..57db71018 100644 --- a/srsenb/test/mac/scheduler_test_rand.cc +++ b/srsenb/test/mac/scheduler_test_rand.cc @@ -37,6 +37,7 @@ #include "srslte/interfaces/sched_interface.h" #include "srslte/phy/utils/debug.h" +#include "sched_common_test_suite.h" #include "scheduler_test_common.h" #include "scheduler_test_utils.h" #include "srslte/common/test_common.h" @@ -123,15 +124,14 @@ struct sched_tester : public srsenb::common_sched_tester { sched_tti_data tti_data; void rem_user(uint16_t rnti) override; - int test_pdcch_collisions(); int assert_no_empty_allocs(); - int test_sch_collisions(); int test_harqs(); private: void new_test_tti() override; void before_sched() override; int process_results() override; + int update_ue_stats(); }; void sched_tester::rem_user(uint16_t rnti) @@ -189,15 +189,25 @@ int sched_tester::process_results() { const srsenb::cc_sched_result* cc_result = sched_results.get_cc(srslte::tti_point{tti_info.tti_params.tti_rx}, CARRIER_IDX); + srsenb::sf_output_res_t sf_out{sched_cell_params[CARRIER_IDX], + tti_point{tti_info.tti_params.tti_rx}, + tti_info.ul_sched_result[CARRIER_IDX], + tti_info.dl_sched_result[CARRIER_IDX]}; TESTASSERT(tti_info.tti_params.tti_rx == cc_result->tti_params.tti_rx); - test_pdcch_collisions(); + // Common tests + TESTASSERT(test_pdcch_collisions(sf_out, &cc_result->pdcch_mask) == SRSLTE_SUCCESS); + TESTASSERT(test_dci_content_common(sf_out) == SRSLTE_SUCCESS); + TESTASSERT(test_sib_scheduling(sf_out) == SRSLTE_SUCCESS); + TESTASSERT(test_pusch_collisions(sf_out, &cc_result->ul_mask) == SRSLTE_SUCCESS); + TESTASSERT(test_pdsch_collisions(sf_out, &cc_result->dl_mask) == SRSLTE_SUCCESS); + + // UE dedicated tests TESTASSERT(ue_tester->test_all(0, tti_info.dl_sched_result[CARRIER_IDX], tti_info.ul_sched_result[CARRIER_IDX]) == SRSLTE_SUCCESS); - test_sch_collisions(); assert_no_empty_allocs(); test_harqs(); - output_tester[CARRIER_IDX].test_sib_scheduling(tti_info.tti_params, tti_info.dl_sched_result[CARRIER_IDX]); + update_ue_stats(); return SRSLTE_SUCCESS; } @@ -237,45 +247,6 @@ int sched_tester::assert_no_empty_allocs() return SRSLTE_SUCCESS; } -/** - * Tests whether there were collisions in the DCI allocations - */ -int sched_tester::test_pdcch_collisions() -{ - srslte::bounded_bitset<128, true> used_cce; - used_cce.resize(srslte_regs_pdcch_ncce(sched_cell_params[CARRIER_IDX].regs.get(), sched_cfg.max_nof_ctrl_symbols)); - - /* TEST: Check if there are collisions in the PDCCH */ - TESTASSERT(output_tester[CARRIER_IDX].test_pdcch_collisions(tti_info.dl_sched_result[CARRIER_IDX], - tti_info.ul_sched_result[CARRIER_IDX], - &used_cce) == SRSLTE_SUCCESS); - - /* TEST: Check whether dci values are correct */ - TESTASSERT(output_tester[CARRIER_IDX].test_dci_values_consistency( - tti_info.dl_sched_result[CARRIER_IDX], tti_info.ul_sched_result[CARRIER_IDX]) == SRSLTE_SUCCESS); - - /* verify if sched_result "used_cce" coincide with sched "used_cce" */ - const srsenb::cc_sched_result* cc_result = sched_results.get_cc(tti_point{tti_info.tti_params.tti_rx}, CARRIER_IDX); - if (used_cce != cc_result->pdcch_mask) { - std::string mask_str = cc_result->pdcch_mask.to_string(); - TESTERROR("The used_cce do not match: (%s!=%s)\n", mask_str.c_str(), used_cce.to_string().c_str()); - } - // TODO: Check postponed retxs - - // typedef std::map::iterator it_t; - // // There must be allocations if there is pending data/retxs. - // if(total_ues.has_ul_tx and ul_sched_result.empty()) { - // for (it_t it = ue_db.begin(); it != ue_db.end(); ++it) { - // uint32_t aggr_level = it->second.get_aggr_level(srslte_dci_format_sizeof(SRSLTE_DCI_FORMAT0, cfg.cell.nof_prb, - // cfg.cell.nof_ports)); if (find_empty_dci(it->second.get_locations(current_cfi, sf_idx), aggr_level) > 0) { - // TESTERROR("[%d] There was pending UL data and free CCEs, but no user got allocated\n", - // tti_info.tti_params.tti_rx); - // } - // } - // } - return SRSLTE_SUCCESS; -} - int sched_tester::test_harqs() { /* check consistency of DL harq procedures and allocations */ @@ -370,21 +341,8 @@ int sched_tester::test_harqs() return SRSLTE_SUCCESS; } -int sched_tester::test_sch_collisions() +int sched_tester::update_ue_stats() { - const srsenb::cc_sched_result* cc_result = sched_results.get_cc(tti_point{tti_info.tti_params.tti_rx}, CARRIER_IDX); - - srsenb::prbmask_t ul_allocs(sched_cell_params[CARRIER_IDX].cfg.cell.nof_prb); - - /* TEST: any collision in PUCCH and PUSCH */ - TESTASSERT(output_tester[CARRIER_IDX].test_pusch_collisions( - tti_info.tti_params, tti_info.ul_sched_result[CARRIER_IDX], ul_allocs) == SRSLTE_SUCCESS); - - /* TEST: check whether cumulative UL PRB masks coincide */ - if (ul_allocs != cc_result->ul_mask) { - TESTERROR("The UL PRB mask and the scheduler result UL mask are not consistent\n"); - } - // update ue stats with number of allocated UL PRBs for (uint32_t i = 0; i < tti_info.ul_sched_result[CARRIER_IDX].nof_dci_elems; ++i) { uint32_t L, RBstart; @@ -396,26 +354,15 @@ int sched_tester::test_sch_collisions() ue_stats[tti_info.ul_sched_result[CARRIER_IDX].pusch[i].dci.rnti].nof_ul_rbs += L; } - /* TEST: check any collision in PDSCH */ - srsenb::rbgmask_t rbgmask(sched_cell_params[CARRIER_IDX].cfg.cell.nof_prb); - TESTASSERT(output_tester[CARRIER_IDX].test_pdsch_collisions( - tti_info.tti_params, tti_info.dl_sched_result[CARRIER_IDX], rbgmask) == SRSLTE_SUCCESS); - // update ue stats with number of DL RB allocations srslte::bounded_bitset<100, true> alloc_mask(sched_cell_params[CARRIER_IDX].cfg.cell.nof_prb); for (uint32_t i = 0; i < tti_info.dl_sched_result[CARRIER_IDX].nof_data_elems; ++i) { TESTASSERT(srsenb::extract_dl_prbmask(sched_cell_params[CARRIER_IDX].cfg.cell, tti_info.dl_sched_result[CARRIER_IDX].data[i].dci, - &alloc_mask) == SRSLTE_SUCCESS); + alloc_mask) == SRSLTE_SUCCESS); ue_stats[tti_info.dl_sched_result[CARRIER_IDX].data[i].dci.rnti].nof_dl_rbs += alloc_mask.count(); } - // TEST: check if resulting DL mask is equal to scheduler internal DL mask - if (rbgmask != cc_result->dl_mask) { - TESTERROR("The DL PRB mask and the scheduler result DL mask are not consistent (%s!=%s)\n", - rbgmask.to_string().c_str(), - cc_result->dl_mask.to_string().c_str()); - } return SRSLTE_SUCCESS; }