mirror of https://github.com/PentHertz/srsLTE.git
clean common sched output tests
This commit is contained in:
parent
67690136be
commit
f24e5aadaf
|
@ -18,7 +18,7 @@
|
||||||
# and at http://www.gnu.org/licenses/.
|
# 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
|
# Scheduler subcomponent testing
|
||||||
add_executable(sched_grid_test sched_grid_test.cc)
|
add_executable(sched_grid_test sched_grid_test.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
|
|
@ -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
|
|
@ -24,6 +24,7 @@
|
||||||
#include "srsenb/hdr/stack/upper/common_enb.h"
|
#include "srsenb/hdr/stack/upper/common_enb.h"
|
||||||
#include "srslte/mac/pdu.h"
|
#include "srslte/mac/pdu.h"
|
||||||
|
|
||||||
|
#include "sched_common_test_suite.h"
|
||||||
#include "srslte/common/test_common.h"
|
#include "srslte/common/test_common.h"
|
||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
|
@ -52,250 +53,6 @@ std::default_random_engine& ::srsenb::get_rand_gen()
|
||||||
return 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
|
* User State Tester
|
||||||
***********************/
|
***********************/
|
||||||
|
@ -705,26 +462,6 @@ int ue_ctxt_test::schedule_acks(cc_result result)
|
||||||
return SRSLTE_SUCCESS;
|
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)
|
void user_state_sched_tester::new_tti(sched* sched_ptr, uint32_t tti_rx)
|
||||||
{
|
{
|
||||||
tic++;
|
tic++;
|
||||||
|
@ -878,11 +615,6 @@ int common_sched_tester::sim_cfg(sim_sched_args args)
|
||||||
sched::set_sched_cfg(&sim_args0.sched_args);
|
sched::set_sched_cfg(&sim_args0.sched_args);
|
||||||
|
|
||||||
ue_tester.reset(new user_state_sched_tester{sim_args0.cell_cfg});
|
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});
|
sched_stats.reset(new sched_result_stats{sim_args0.cell_cfg});
|
||||||
|
|
||||||
tester_log = sim_args0.sim_log;
|
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) {
|
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(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);
|
sched_stats->process_results(tti_info.tti_params, tti_info.dl_sched_result, tti_info.ul_sched_result);
|
||||||
|
|
||||||
|
|
|
@ -36,50 +36,10 @@ void set_randseed(uint64_t seed);
|
||||||
float randf();
|
float randf();
|
||||||
std::default_random_engine& get_rand_gen();
|
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
|
* 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<sched_interface::dl_sched_res_t>;
|
using dl_sched_res_list = std::vector<sched_interface::dl_sched_res_t>;
|
||||||
using ul_sched_res_list = std::vector<sched_interface::ul_sched_res_t>;
|
using ul_sched_res_list = std::vector<sched_interface::ul_sched_res_t>;
|
||||||
|
|
||||||
|
@ -261,7 +221,6 @@ public:
|
||||||
uint32_t tti_count = 0;
|
uint32_t tti_count = 0;
|
||||||
|
|
||||||
// testers
|
// testers
|
||||||
std::vector<output_sched_tester> output_tester;
|
|
||||||
std::unique_ptr<user_state_sched_tester> ue_tester;
|
std::unique_ptr<user_state_sched_tester> ue_tester;
|
||||||
std::unique_ptr<sched_result_stats> sched_stats;
|
std::unique_ptr<sched_result_stats> sched_stats;
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
#include "srslte/interfaces/sched_interface.h"
|
#include "srslte/interfaces/sched_interface.h"
|
||||||
#include "srslte/phy/utils/debug.h"
|
#include "srslte/phy/utils/debug.h"
|
||||||
|
|
||||||
|
#include "sched_common_test_suite.h"
|
||||||
#include "scheduler_test_common.h"
|
#include "scheduler_test_common.h"
|
||||||
#include "scheduler_test_utils.h"
|
#include "scheduler_test_utils.h"
|
||||||
#include "srslte/common/test_common.h"
|
#include "srslte/common/test_common.h"
|
||||||
|
@ -123,15 +124,14 @@ struct sched_tester : public srsenb::common_sched_tester {
|
||||||
sched_tti_data tti_data;
|
sched_tti_data tti_data;
|
||||||
|
|
||||||
void rem_user(uint16_t rnti) override;
|
void rem_user(uint16_t rnti) override;
|
||||||
int test_pdcch_collisions();
|
|
||||||
int assert_no_empty_allocs();
|
int assert_no_empty_allocs();
|
||||||
int test_sch_collisions();
|
|
||||||
int test_harqs();
|
int test_harqs();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void new_test_tti() override;
|
void new_test_tti() override;
|
||||||
void before_sched() override;
|
void before_sched() override;
|
||||||
int process_results() override;
|
int process_results() override;
|
||||||
|
int update_ue_stats();
|
||||||
};
|
};
|
||||||
|
|
||||||
void sched_tester::rem_user(uint16_t rnti)
|
void sched_tester::rem_user(uint16_t rnti)
|
||||||
|
@ -189,15 +189,25 @@ int sched_tester::process_results()
|
||||||
{
|
{
|
||||||
const srsenb::cc_sched_result* cc_result =
|
const srsenb::cc_sched_result* cc_result =
|
||||||
sched_results.get_cc(srslte::tti_point{tti_info.tti_params.tti_rx}, CARRIER_IDX);
|
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);
|
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]) ==
|
TESTASSERT(ue_tester->test_all(0, tti_info.dl_sched_result[CARRIER_IDX], tti_info.ul_sched_result[CARRIER_IDX]) ==
|
||||||
SRSLTE_SUCCESS);
|
SRSLTE_SUCCESS);
|
||||||
test_sch_collisions();
|
|
||||||
assert_no_empty_allocs();
|
assert_no_empty_allocs();
|
||||||
test_harqs();
|
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;
|
return SRSLTE_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -237,45 +247,6 @@ int sched_tester::assert_no_empty_allocs()
|
||||||
return SRSLTE_SUCCESS;
|
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<uint16_t, srsenb::sched_ue>::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()
|
int sched_tester::test_harqs()
|
||||||
{
|
{
|
||||||
/* check consistency of DL harq procedures and allocations */
|
/* check consistency of DL harq procedures and allocations */
|
||||||
|
@ -370,21 +341,8 @@ int sched_tester::test_harqs()
|
||||||
return SRSLTE_SUCCESS;
|
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
|
// 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) {
|
for (uint32_t i = 0; i < tti_info.ul_sched_result[CARRIER_IDX].nof_dci_elems; ++i) {
|
||||||
uint32_t L, RBstart;
|
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;
|
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
|
// 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);
|
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) {
|
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,
|
TESTASSERT(srsenb::extract_dl_prbmask(sched_cell_params[CARRIER_IDX].cfg.cell,
|
||||||
tti_info.dl_sched_result[CARRIER_IDX].data[i].dci,
|
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();
|
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;
|
return SRSLTE_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue