created first version of CA test. Now we need to create actual asserts

This commit is contained in:
Francisco Paisana 2020-02-20 19:24:21 +00:00
parent 507bc2d2a2
commit b319f8dfcd
6 changed files with 682 additions and 114 deletions

View File

@ -793,9 +793,10 @@ void sf_sched::set_dl_data_sched_result(const pdcch_grid_t::alloc_result_t& dci_
}
// Print Resulting DL Allocation
log_h->info("SCHED: DL %s rnti=0x%x, pid=%d, mask=0x%s, dci=(%d,%d), n_rtx=%d, tbs=%d, buffer=%d/%d\n",
log_h->info("SCHED: DL %s rnti=0x%x, cc=%d, pid=%d, mask=0x%s, dci=(%d,%d), n_rtx=%d, tbs=%d, buffer=%d/%d\n",
!is_newtx ? "retx" : "tx",
user->get_rnti(),
cc_cfg->enb_cc_idx,
h->get_id(),
data_alloc.user_mask.to_hex().c_str(),
data->dci.location.L,

View File

@ -101,7 +101,8 @@ void sched_ue::set_cfg(const sched_interface::ue_cfg_t& cfg_)
}
// update configuration
cfg = cfg_;
std::vector<sched::ue_cfg_t::cc_cfg_t> prev_supported_cc_list = std::move(cfg.supported_cc_list);
cfg = cfg_;
// update bearer cfgs
for (uint32_t i = 0; i < sched_interface::MAX_LC; ++i) {
@ -110,17 +111,24 @@ void sched_ue::set_cfg(const sched_interface::ue_cfg_t& cfg_)
// either add a new carrier, or reconfigure existing one
bool scell_activation_state_changed = false;
for (auto& cc_cfg : cfg.supported_cc_list) {
sched_ue_carrier* c = get_ue_carrier(cc_cfg.enb_cc_idx);
if (c == nullptr) {
// add new carrier to sched_ue
carriers.emplace_back(cfg, (*cell_params_list)[cc_cfg.enb_cc_idx], rnti, carriers.size());
scell_activation_state_changed |= carriers.size() > 1 and carriers.back().is_active();
for (uint32_t ue_idx = 0; ue_idx < cfg.supported_cc_list.size(); ++ue_idx) {
auto& cc_cfg = cfg.supported_cc_list[ue_idx];
if (ue_idx >= prev_supported_cc_list.size()) {
// New carrier needs to be added
carriers.emplace_back(cfg, (*cell_params_list)[cc_cfg.enb_cc_idx], rnti, ue_idx);
scell_activation_state_changed |= ue_idx > 0 and carriers.back().is_active();
} else if (cc_cfg.enb_cc_idx != prev_supported_cc_list[ue_idx].enb_cc_idx) {
// TODO: Check if this will ever happen.
// One carrier was added in the place of another
carriers[ue_idx] = sched_ue_carrier{cfg, (*cell_params_list)[cc_cfg.enb_cc_idx], rnti, ue_idx};
scell_activation_state_changed |= ue_idx > 0 and carriers[ue_idx].is_active();
} else {
// if SCell state changed
scell_activation_state_changed = c->is_active() != cc_cfg.active and c->get_ue_cc_idx() != 0;
// The enb_cc_idx, ue_cc_idx match previous configuration.
// The SCell state may have changed. In such case we will schedule a SCell Activation CE
scell_activation_state_changed = carriers[ue_idx].is_active() != cc_cfg.active and ue_idx > 0;
// reconfiguration of carrier might be needed.
c->set_cfg(cfg);
carriers[ue_idx].set_cfg(cfg);
}
}
if (scell_activation_state_changed) {
@ -921,7 +929,6 @@ std::pair<bool, uint32_t> sched_ue::get_cell_index(uint32_t enb_cc_idx) const
cfg.supported_cc_list.end(),
[enb_cc_idx](const sched_interface::ue_cfg_t::cc_cfg_t& u) { return u.enb_cc_idx == enb_cc_idx; });
if (it == cfg.supported_cc_list.end()) {
log_h->error("The carrier with eNB_cc_idx=%d does not exist\n", enb_cc_idx);
return std::make_pair(false, 0);
}
return std::make_pair(true, it->enb_cc_idx);

View File

@ -19,9 +19,24 @@
*
*/
#include "scheduler_test_common.h"
#include "scheduler_test_utils.h"
#include "srsenb/hdr/stack/mac/scheduler.h"
using namespace srsenb;
template <class MapContainer, class Predicate>
void erase_if(MapContainer& c, Predicate should_remove)
{
for (auto it = c.begin(); it != c.end();) {
if (should_remove(*it)) {
it = c.erase(it);
} else {
++it;
}
}
}
/*******************
* Logging *
*******************/
@ -104,7 +119,7 @@ sched_sim_events generate_sim1()
tti_ev::user_cfg_ev* user = generator.add_new_default_user(duration);
uint16_t rnti = user->rnti;
// Event (TTI=prach_tti+msg4_tot_delay): First Tx (Msg4)
// Event (TTI=prach_tti+msg4_tot_delay): First Tx (Msg4). Goes in SRB0 and contains ConRes
generator.step_tti(msg4_tot_delay);
generator.add_dl_data(rnti, msg4_size);
@ -151,38 +166,358 @@ sched_sim_events generate_sim1()
class sched_ca_tester : public srsenb::sched
{
public:
void run_tti(uint32_t tti);
struct tti_info_t {
tti_params_t tti_params{10241};
uint32_t nof_prachs = 0;
std::vector<sched_interface::dl_sched_res_t> dl_sched_result;
std::vector<sched_interface::ul_sched_res_t> ul_sched_result;
};
int cell_cfg(const std::vector<cell_cfg_t>& cell_params) final;
int add_user(uint16_t rnti, const ue_cfg_t& ue_cfg_);
void new_test_tti(uint32_t tti_rx);
int process_tti_events(const tti_ev& tti_events);
int process_ack_txs();
int set_acks();
void run_tti(uint32_t tti, const tti_ev& tti_events);
int process_results();
// args
sim_sched_args sim_args; ///< arguments used to generate TTI events
// tti specific params
tti_info_t tti_info;
// testers
std::vector<output_sched_tester> output_tester;
std::unique_ptr<user_state_sched_tester> ue_tester;
private:
struct ack_info_t {
uint16_t rnti;
uint32_t tti;
uint32_t ue_cc_idx;
bool ack = false;
uint32_t retx_delay = 0;
srsenb::dl_harq_proc dl_harq;
};
struct ul_ack_info_t {
uint16_t rnti;
uint32_t tti_ack, tti_tx_ul;
uint32_t ue_cc_idx;
bool ack = false;
srsenb::ul_harq_proc ul_harq;
};
std::multimap<uint32_t, ack_info_t> to_ack;
std::multimap<uint32_t, ul_ack_info_t> to_ul_ack;
};
void sched_ca_tester::run_tti(uint32_t tti_rx)
int sched_ca_tester::cell_cfg(const std::vector<cell_cfg_t>& cell_params)
{
// new_test_tti(tti_rx);
sched::cell_cfg(cell_params); // call parent
ue_tester.reset(new user_state_sched_tester{cell_params});
output_tester.clear();
output_tester.reserve(cell_params.size());
for (uint32_t i = 0; i < cell_params.size(); ++i) {
output_tester.emplace_back(sched_cell_params[i]);
}
return SRSLTE_SUCCESS;
}
int sched_ca_tester::add_user(uint16_t rnti, const ue_cfg_t& ue_cfg_)
{
CONDERROR(ue_cfg(rnti, ue_cfg_) != SRSLTE_SUCCESS, "[TESTER] Configuring new user rnti=0x%x to sched\n", rnti);
dl_sched_rar_info_t rar_info = {};
rar_info.prach_tti = tti_info.tti_params.tti_rx;
rar_info.temp_crnti = rnti;
rar_info.msg3_size = 7;
rar_info.preamble_idx = tti_info.nof_prachs++;
uint32_t pcell_idx = ue_cfg_.supported_cc_list[0].enb_cc_idx;
dl_rach_info(pcell_idx, rar_info);
ue_tester->add_user(rnti, rar_info.preamble_idx, ue_cfg_);
log_global->info("[TESTER] Adding user rnti=0x%x\n", rnti);
return SRSLTE_SUCCESS;
}
void sched_ca_tester::new_test_tti(uint32_t tti_rx)
{
tti_info.tti_params = tti_params_t{tti_rx};
tti_info.nof_prachs = 0;
tti_info.dl_sched_result.clear();
tti_info.ul_sched_result.clear();
ue_tester->new_tti(tti_rx);
}
int sched_ca_tester::process_tti_events(const tti_ev& tti_ev)
{
for (const tti_ev::user_cfg_ev& ue_ev : tti_ev.user_updates) {
// There is a new configuration
if (ue_ev.ue_cfg != nullptr) {
if (not ue_tester->user_exists(ue_ev.rnti)) {
// new user
TESTASSERT(add_user(ue_ev.rnti, *ue_ev.ue_cfg) == SRSLTE_SUCCESS);
} else {
// reconfiguration
TESTASSERT(ue_cfg(ue_ev.rnti, *ue_ev.ue_cfg) == SRSLTE_SUCCESS);
ue_tester->user_reconf(ue_ev.rnti, *ue_ev.ue_cfg);
}
}
// There is a user to remove
if (ue_ev.rem_user) {
// bearer_ue_rem(ue_ev.rnti, 0);
ue_rem(ue_ev.rnti);
ue_tester->rem_user(ue_ev.rnti);
log_global->info("[TESTER] Removing user rnti=0x%x\n", ue_ev.rnti);
}
// configure carriers
if (ue_ev.bearer_cfg != nullptr) {
CONDERROR(not ue_tester->user_exists(ue_ev.rnti), "User rnti=0x%x does not exist\n", ue_ev.rnti);
// TODO: Instantiate more bearers
bearer_ue_cfg(ue_ev.rnti, 0, ue_ev.bearer_cfg.get());
}
// push UL SRs and DL packets
if (ue_ev.buffer_ev != nullptr) {
auto* user = ue_tester->get_user_state(ue_ev.rnti);
CONDERROR(user == nullptr, "TESTER ERROR: Trying to schedule data for user that does not exist\n");
if (ue_ev.buffer_ev->dl_data > 0) {
// If Msg3 has already been received
if (user->msg3_tti >= 0 and (uint32_t) user->msg3_tti <= tti_info.tti_params.tti_rx) {
// If Msg4 not yet sent, allocate data in SRB0 buffer
uint32_t lcid = (user->msg4_tti >= 0) ? 2 : 0;
uint32_t pending_dl_new_data = ue_db[ue_ev.rnti].get_pending_dl_new_data();
if (lcid == 2 and not user->drb_cfg_flag) {
// If RRCSetup finished
if (pending_dl_new_data == 0) {
// setup lcid==2 bearer
sched::ue_bearer_cfg_t cfg = {};
cfg.direction = ue_bearer_cfg_t::BOTH;
ue_tester->bearer_cfg(ue_ev.rnti, 2, cfg);
bearer_ue_cfg(ue_ev.rnti, 2, &cfg);
} else {
// Let SRB0 get emptied
continue;
}
}
// Update DL buffer
uint32_t tot_dl_data = pending_dl_new_data + ue_ev.buffer_ev->dl_data; // TODO: derive pending based on rx
dl_rlc_buffer_state(ue_ev.rnti, lcid, tot_dl_data, 0); // TODO: Check retx_queue
}
}
if (ue_ev.buffer_ev->sr_data > 0 and user->drb_cfg_flag) {
uint32_t tot_ul_data =
ue_db[ue_ev.rnti].get_pending_ul_new_data(tti_info.tti_params.tti_tx_ul) + ue_ev.buffer_ev->sr_data;
uint32_t lcid = 2;
ul_bsr(ue_ev.rnti, lcid, tot_ul_data, true);
}
}
}
return SRSLTE_SUCCESS;
}
int sched_ca_tester::process_ack_txs()
{
/* check if user was removed. If so, clean respective acks */
erase_if(to_ack,
[this](std::pair<const uint32_t, ack_info_t>& elem) { return this->ue_db.count(elem.second.rnti) == 0; });
erase_if(to_ul_ack,
[this](std::pair<const uint32_t, ul_ack_info_t>& elem) { return this->ue_db.count(elem.second.rnti) == 0; });
/* Ack DL HARQs */
for (const auto& ack_it : to_ack) {
if (ack_it.second.tti != tti_info.tti_params.tti_rx) {
continue;
}
const ack_info_t& dl_ack = ack_it.second;
srsenb::dl_harq_proc* h = ue_db[dl_ack.rnti].get_dl_harq(ack_it.second.dl_harq.get_id(), dl_ack.ue_cc_idx);
const srsenb::dl_harq_proc& hack = dl_ack.dl_harq;
CONDERROR(hack.is_empty(), "[TESTER] The acked DL harq was not active\n");
bool ret = false;
for (uint32_t tb = 0; tb < SRSLTE_MAX_TB; ++tb) {
if (dl_ack.dl_harq.is_empty(tb)) {
continue;
}
ret |= dl_ack_info(tti_info.tti_params.tti_rx, dl_ack.rnti, dl_ack.ue_cc_idx, tb, dl_ack.ack) > 0;
}
CONDERROR(not ret, "[TESTER] The dl harq proc that was ACKed does not exist\n");
if (dl_ack.ack) {
CONDERROR(!h->is_empty(), "[TESTER] ACKed dl harq was not emptied\n");
CONDERROR(h->has_pending_retx(0, tti_info.tti_params.tti_tx_dl),
"[TESTER] ACKed dl harq still has pending retx\n");
log_global->info("[TESTER] DL ACK tti=%u rnti=0x%x pid=%d\n",
tti_info.tti_params.tti_rx,
dl_ack.rnti,
dl_ack.dl_harq.get_id());
} else {
CONDERROR(h->is_empty() and hack.nof_retx(0) + 1 < hack.max_nof_retx(), "[TESTER] NACKed DL harq got emptied\n");
}
}
/* Ack UL HARQs */
for (const auto& ack_it : to_ul_ack) {
if (ack_it.first != tti_info.tti_params.tti_rx) {
continue;
}
const ul_ack_info_t& ul_ack = ack_it.second;
srsenb::ul_harq_proc* h = ue_db[ul_ack.rnti].get_ul_harq(tti_info.tti_params.tti_rx, ul_ack.ue_cc_idx);
const srsenb::ul_harq_proc& hack = ul_ack.ul_harq;
CONDERROR(h == nullptr or h->get_tti() != hack.get_tti(), "[TESTER] UL Harq TTI does not match the ACK TTI\n");
CONDERROR(h->is_empty(0), "[TESTER] The acked UL harq is not active\n");
CONDERROR(hack.is_empty(0), "[TESTER] The acked UL harq was not active\n");
ul_crc_info(tti_info.tti_params.tti_rx, ul_ack.rnti, ul_ack.ue_cc_idx, ul_ack.ack);
CONDERROR(!h->get_pending_data(), "[TESTER] UL harq lost its pending data\n");
CONDERROR(!h->has_pending_ack(), "[TESTER] ACK/NACKed UL harq should have a pending ACK\n");
if (ul_ack.ack) {
CONDERROR(!h->is_empty(), "[TESTER] ACKed UL harq did not get emptied\n");
CONDERROR(h->has_pending_retx(), "[TESTER] ACKed UL harq still has pending retx\n");
log_global->info(
"[TESTER] UL ACK tti=%u rnti=0x%x pid=%d\n", tti_info.tti_params.tti_rx, ul_ack.rnti, hack.get_id());
} else {
// NACK
CONDERROR(!h->is_empty() and !h->has_pending_retx(), "[TESTER] If NACKed, UL harq has to have pending retx\n");
CONDERROR(h->is_empty() and hack.nof_retx(0) + 1 < hack.max_nof_retx(),
"[TESTER] Nacked UL harq did get emptied\n");
}
}
// erase processed acks
to_ack.erase(tti_info.tti_params.tti_rx);
to_ul_ack.erase(tti_info.tti_params.tti_rx);
// bool ack = true; //(tti_data.tti_rx % 3) == 0;
// if (tti_data.tti_rx >= FDD_HARQ_DELAY_MS) {
// for (auto it = ue_db.begin(); it != ue_db.end(); ++it) {
// uint16_t rnti = it->first;
// srsenb::ul_harq_proc* h = ue_db[rnti].get_ul_harq(tti_data.tti_rx);
// if (h != nullptr and not h->is_empty()) {
// ul_crc_info(tti_data.tti_rx, rnti, ack);
// }
// }
// }
return SRSLTE_SUCCESS;
}
void sched_ca_tester::run_tti(uint32_t tti_rx, const tti_ev& tti_events)
{
new_test_tti(tti_rx);
log_global->info("[TESTER] ---- tti=%u | nof_ues=%zd ----\n", tti_rx, ue_db.size());
// process_tti_args();
//
// ack_txs();
process_tti_events(tti_events);
process_ack_txs();
// before_sched();
//
// dl_sched(tti_data.tti_tx_dl, CARRIER_IDX, tti_data.sched_result_dl);
// ul_sched(tti_data.tti_tx_ul, CARRIER_IDX, tti_data.sched_result_ul);
//
// process_results();
// Call scheduler for all carriers
tti_info.dl_sched_result.resize(sched_cell_params.size());
for (uint32_t i = 0; i < sched_cell_params.size(); ++i) {
dl_sched(tti_info.tti_params.tti_tx_dl, i, tti_info.dl_sched_result[i]);
}
tti_info.ul_sched_result.resize(sched_cell_params.size());
for (uint32_t i = 0; i < sched_cell_params.size(); ++i) {
ul_sched(tti_info.tti_params.tti_tx_ul, i, tti_info.ul_sched_result[i]);
}
process_results();
set_acks();
}
int sched_ca_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);
}
return SRSLTE_SUCCESS;
}
int sched_ca_tester::set_acks()
{
for (uint32_t ccidx = 0; ccidx < sched_cell_params.size(); ++ccidx) {
// schedule future acks
for (uint32_t i = 0; i < tti_info.dl_sched_result[ccidx].nof_data_elems; ++i) {
ack_info_t ack_data;
ack_data.rnti = tti_info.dl_sched_result[ccidx].data[i].dci.rnti;
ack_data.tti = FDD_HARQ_DELAY_MS + tti_info.tti_params.tti_tx_dl;
ack_data.ue_cc_idx = ue_db[ack_data.rnti].get_cell_index(ccidx).second;
const srsenb::dl_harq_proc* dl_h =
ue_db[ack_data.rnti].get_dl_harq(tti_info.dl_sched_result[ccidx].data[i].dci.pid, ccidx);
ack_data.dl_harq = *dl_h;
if (ack_data.dl_harq.nof_retx(0) == 0) {
ack_data.ack = randf() > sim_args.P_retx;
} else { // always ack after three retxs
ack_data.ack = ack_data.dl_harq.nof_retx(0) == 3;
}
// Remove harq from the ack list if there was a harq rewrite
auto it = to_ack.begin();
while (it != to_ack.end() and it->first < ack_data.tti) {
if (it->second.rnti == ack_data.rnti and it->second.dl_harq.get_id() == ack_data.dl_harq.get_id() and
it->second.ue_cc_idx == ack_data.ue_cc_idx) {
CONDERROR(it->second.tti + 2 * FDD_HARQ_DELAY_MS > ack_data.tti,
"[TESTER] The retx dl harq id=%d was transmitted too soon\n",
ack_data.dl_harq.get_id());
auto toerase_it = it++;
to_ack.erase(toerase_it);
continue;
}
++it;
}
// add new ack to the list
to_ack.insert(std::make_pair(ack_data.tti, ack_data));
}
/* Schedule UL ACKs */
for (uint32_t i = 0; i < tti_info.ul_sched_result[ccidx].nof_dci_elems; ++i) {
const auto& pusch = tti_info.ul_sched_result[ccidx].pusch[i];
ul_ack_info_t ack_data;
ack_data.rnti = pusch.dci.rnti;
ack_data.ul_harq = *ue_db[ack_data.rnti].get_ul_harq(tti_info.tti_params.tti_tx_ul, ccidx);
ack_data.tti_tx_ul = tti_info.tti_params.tti_tx_ul;
ack_data.tti_ack = tti_info.tti_params.tti_tx_ul + FDD_HARQ_DELAY_MS;
ack_data.ue_cc_idx = ue_db[ack_data.rnti].get_cell_index(ccidx).second;
if (ack_data.ul_harq.nof_retx(0) == 0) {
ack_data.ack = randf() > sim_args.P_retx;
} else {
ack_data.ack = ack_data.ul_harq.nof_retx(0) == 3;
}
to_ul_ack.insert(std::make_pair(ack_data.tti_tx_ul, ack_data));
}
}
return SRSLTE_SUCCESS;
}
int test_scheduler_ca(const sched_sim_events& sim_events)
{
// Create classes
sched_ca_tester tester;
tester.sim_args = sim_events.sim_args;
// Setup scheduler
tester.init(nullptr);
tester.cell_cfg(sim_events.sim_args.cell_cfg);
TESTASSERT(tester.cell_cfg(sim_events.sim_args.cell_cfg) == SRSLTE_SUCCESS);
uint32_t tti_start = 0; // rand_int(0, 10240);
for (uint32_t nof_ttis = 0; nof_ttis < sim_events.sim_args.nof_ttis; ++nof_ttis) {
uint32_t tti = (tti_start + nof_ttis) % 10240;
log_global->step(tti);
tester.run_tti(tti);
tester.run_tti(tti, sim_events.tti_events[nof_ttis]);
}
return SRSLTE_SUCCESS;

View File

@ -20,10 +20,13 @@
*/
#include "scheduler_test_common.h"
#include "lib/include/srslte/common/pdu.h"
#include "srsenb/hdr/stack/mac/scheduler.h"
#include "srslte/common/test_common.h"
#include <set>
using namespace srsenb;
int output_sched_tester::test_pusch_collisions(const tti_params_t& tti_params,
@ -255,6 +258,20 @@ int output_sched_tester::test_dci_values_consistency(const sched_interface::dl_s
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;
}
int srsenb::extract_dl_prbmask(const srslte_cell_t& cell,
const srslte_dci_dl_t& dci,
srslte::bounded_bitset<100, true>* alloc_mask)
@ -276,3 +293,232 @@ int srsenb::extract_dl_prbmask(const srslte_cell_t& cell,
}
return SRSLTE_SUCCESS;
}
int user_state_sched_tester::add_user(uint16_t rnti,
uint32_t preamble_idx,
const srsenb::sched_interface::ue_cfg_t& ue_cfg)
{
TESTASSERT(users.count(rnti) == 0);
ue_state ue;
ue.user_cfg = ue_cfg;
ue.prach_tti = tti_params.tti_rx;
ue.preamble_idx = preamble_idx;
users.insert(std::make_pair(rnti, ue));
return SRSLTE_SUCCESS;
}
int user_state_sched_tester::user_reconf(uint16_t rnti, const srsenb::sched_interface::ue_cfg_t& ue_cfg)
{
TESTASSERT(users.count(rnti) > 0);
users[rnti].user_cfg = ue_cfg;
return SRSLTE_SUCCESS;
}
int user_state_sched_tester::bearer_cfg(uint16_t rnti,
uint32_t lcid,
const srsenb::sched_interface::ue_bearer_cfg_t& bearer_cfg)
{
auto it = users.find(rnti);
TESTASSERT(it != users.end());
it->second.user_cfg.ue_bearers[lcid] = bearer_cfg;
users[rnti].drb_cfg_flag = false;
for (uint32_t i = 2; i < it->second.user_cfg.ue_bearers.size(); ++i) {
if (it->second.user_cfg.ue_bearers[i].direction != sched_interface::ue_bearer_cfg_t::IDLE) {
users[rnti].drb_cfg_flag = true;
}
}
return SRSLTE_SUCCESS;
}
void user_state_sched_tester::rem_user(uint16_t rnti)
{
users.erase(rnti);
}
/**
* Tests whether the RAR and Msg3 were scheduled within the expected windows. Individual tests:
* - a user does not get UL allocs before Msg3
* - a user does not get DL data allocs before Msg3 is correctly received
* - a user RAR alloc falls within its RAR window
* - There is only one RAR in the RAR window for a given user
* - Msg3 is allocated in expected TTI, without PDCCH, and correct rnti
* - First Data allocation happens after Msg3, and contains a ConRes
* - No RARs are allocated with wrong enb_cc_idx, preamble_idx or wrong user
* TODO:
* - check Msg3 PRBs match the ones advertised in the RAR
* - space is enough for Msg3
*/
int user_state_sched_tester::test_ra(uint32_t enb_cc_idx,
const sched_interface::dl_sched_res_t& dl_result,
const sched_interface::ul_sched_res_t& ul_result)
{
uint32_t msg3_count = 0;
for (auto& iter : users) {
uint16_t rnti = iter.first;
ue_state& userinfo = iter.second;
// No UL allocations before Msg3
for (uint32_t i = 0; i < ul_result.nof_dci_elems; ++i) {
if (ul_result.pusch[i].dci.rnti == rnti) {
CONDERROR(ul_result.pusch[i].needs_pdcch and userinfo.msg3_tti < 0,
"[TESTER] No UL data allocation allowed before Msg3\n");
CONDERROR(userinfo.rar_tti < 0, "[TESTER] No UL allocation allowed before RAR\n");
uint32_t msg3_tti = (uint32_t)(userinfo.rar_tti + FDD_HARQ_DELAY_MS + MSG3_DELAY_MS) % 10240;
CONDERROR(msg3_tti > tti_params.tti_tx_ul, "No UL allocs allowed before Msg3 alloc\n");
}
}
// No DL data allocations before Msg3 is received
for (uint32_t i = 0; i < dl_result.nof_data_elems; ++i) {
if (dl_result.data[i].dci.rnti == rnti) {
CONDERROR(userinfo.msg3_tti < 0, "[TESTER] No DL data alloc allowed before Msg3 alloc\n");
CONDERROR(tti_params.tti_rx < (uint32_t)userinfo.msg3_tti,
"[TESTER] Msg4 cannot be tx without Msg3 being received\n");
}
}
if (enb_cc_idx != userinfo.user_cfg.supported_cc_list[0].enb_cc_idx) {
// only check for RAR/Msg3 presence for a UE's PCell
continue;
}
// No RAR allocations outside of rar_window
uint32_t prach_tti = (uint32_t)userinfo.prach_tti;
uint32_t primary_cc_idx = userinfo.user_cfg.supported_cc_list[0].enb_cc_idx;
std::array<uint32_t, 2> rar_window = {prach_tti + 3, prach_tti + 3 + cell_params[primary_cc_idx].prach_rar_window};
CONDERROR(userinfo.rar_tti < 0 and tti_params.tti_tx_dl > rar_window[1],
"[TESTER] RAR not scheduled within the RAR Window\n");
if (tti_params.tti_tx_dl <= rar_window[1] and tti_params.tti_tx_dl >= rar_window[0]) {
// Inside RAR window
for (uint32_t i = 0; i < dl_result.nof_rar_elems; ++i) {
for (uint32_t j = 0; j < dl_result.rar[i].nof_grants; ++j) {
auto& data = dl_result.rar[i].msg3_grant[j].data;
if (data.prach_tti == (uint32_t)userinfo.prach_tti and data.preamble_idx == userinfo.preamble_idx) {
CONDERROR(userinfo.rar_tti >= 0, "There was more than one RAR for the same user\n");
userinfo.rar_tti = tti_params.tti_tx_dl;
}
}
}
}
// Check whether Msg3 was allocated in expected TTI
if (userinfo.rar_tti >= 0) {
uint32_t expected_msg3_tti = (uint32_t)(userinfo.rar_tti + FDD_HARQ_DELAY_MS + MSG3_DELAY_MS) % 10240;
if (expected_msg3_tti == tti_params.tti_tx_ul) {
for (uint32_t i = 0; i < ul_result.nof_dci_elems; ++i) {
if (ul_result.pusch[i].dci.rnti == rnti) {
CONDERROR(userinfo.msg3_tti >= 0, "[TESTER] Only one Msg3 allowed per user\n");
CONDERROR(ul_result.pusch[i].needs_pdcch, "[TESTER] Msg3 allocations do not require PDCCH\n");
// CONDERROR(tti_data.ul_pending_msg3.rnti != rnti, "[TESTER] The UL pending msg3 RNTI did not
// match\n"); CONDERROR(not tti_data.ul_pending_msg3_present, "[TESTER] The UL pending msg3
// RNTI did not match\n");
userinfo.msg3_tti = tti_params.tti_tx_ul;
msg3_count++;
}
}
} else if (expected_msg3_tti < tti_params.tti_tx_ul) {
CONDERROR(userinfo.msg3_tti < 0, "[TESTER] No UL msg3 allocation was made\n");
}
}
// Find any Msg4 Allocation
if (userinfo.msg4_tti < 0) {
for (uint32_t i = 0; i < dl_result.nof_data_elems; ++i) {
if (dl_result.data[i].dci.rnti == rnti) {
for (uint32_t j = 0; j < dl_result.data[i].nof_pdu_elems[0]; ++j) {
if (dl_result.data[i].pdu[0][j].lcid == srslte::sch_subh::CON_RES_ID) {
// ConRes found
CONDERROR(dl_result.data[i].dci.format != SRSLTE_DCI_FORMAT1, "ConRes must be format1\n");
CONDERROR(userinfo.msg4_tti >= 0, "ConRes CE cannot be retransmitted for the same rnti\n");
userinfo.msg4_tti = tti_params.tti_tx_dl;
}
}
CONDERROR(userinfo.msg4_tti < 0, "Data allocations are not allowed without first receiving ConRes\n");
}
}
}
}
return SRSLTE_SUCCESS;
}
int user_state_sched_tester::test_ctrl_info(uint32_t enb_cc_idx,
const sched_interface::dl_sched_res_t& dl_result,
const sched_interface::ul_sched_res_t& ul_result)
{
/* TEST: Ensure there are no spurious RARs that do not belong to any user */
for (uint32_t i = 0; i < dl_result.nof_rar_elems; ++i) {
for (uint32_t j = 0; j < dl_result.rar[i].nof_grants; ++j) {
uint32_t prach_tti = dl_result.rar[i].msg3_grant[j].data.prach_tti;
uint32_t preamble_idx = dl_result.rar[i].msg3_grant[j].data.preamble_idx;
auto it = std::find_if(users.begin(), users.end(), [&](const std::pair<uint16_t, ue_state>& u) {
return u.second.preamble_idx == preamble_idx and ((uint32_t)u.second.prach_tti == prach_tti);
});
CONDERROR(it == users.end(), "There was a RAR allocation with no associated user");
CONDERROR(it->second.user_cfg.supported_cc_list[0].enb_cc_idx != enb_cc_idx,
"The allocated RAR is in the wrong cc\n");
}
}
/* TEST: All DL allocs have a correct rnti */
std::set<uint16_t> alloc_rntis;
for (uint32_t i = 0; i < dl_result.nof_data_elems; ++i) {
uint16_t rnti = dl_result.data[i].dci.rnti;
CONDERROR(alloc_rntis.count(rnti) > 0, "The user rnti=0x%x got allocated multiple times in DL\n", rnti);
CONDERROR(users.count(rnti) == 0, "The user rnti=0x%x allocated in DL does not exist\n", rnti);
alloc_rntis.insert(rnti);
}
/* TEST: All UL allocs have a correct rnti */
alloc_rntis.clear();
for (uint32_t i = 0; i < ul_result.nof_dci_elems; ++i) {
uint16_t rnti = ul_result.pusch[i].dci.rnti;
CONDERROR(alloc_rntis.count(rnti) > 0, "The user rnti=0x%x got allocated multiple times in UL\n", rnti);
CONDERROR(users.count(rnti) == 0, "The user rnti=0x%x allocated in UL does not exist\n", rnti);
alloc_rntis.insert(rnti);
}
return SRSLTE_SUCCESS;
}
/**
* Tests whether the SCells are correctly activated. Individual tests:
* - no DL and UL allocations in inactive carriers
*/
int user_state_sched_tester::test_scell_activation(uint32_t enb_cc_idx,
const sched_interface::dl_sched_res_t& dl_result,
const sched_interface::ul_sched_res_t& ul_result)
{
for (auto& iter : users) {
uint16_t rnti = iter.first;
ue_state& userinfo = iter.second;
auto it = std::find_if(userinfo.user_cfg.supported_cc_list.begin(),
userinfo.user_cfg.supported_cc_list.end(),
[enb_cc_idx](const sched::ue_cfg_t::cc_cfg_t& cc) { return cc.enb_cc_idx == enb_cc_idx; });
if (it == userinfo.user_cfg.supported_cc_list.end() or not it->active) {
// cell not active. Ensure data allocations are not made
for (uint32_t i = 0; i < dl_result.nof_data_elems; ++i) {
CONDERROR(dl_result.data[i].dci.rnti == rnti, "Allocated user in inactive carrier\n");
}
for (uint32_t i = 0; i < ul_result.nof_dci_elems; ++i) {
CONDERROR(ul_result.pusch[i].dci.rnti == rnti, "Allocated user in inactive carrier\n");
}
}
}
return SRSLTE_SUCCESS;
}
int user_state_sched_tester::test_all(uint32_t enb_cc_idx,
const sched_interface::dl_sched_res_t& dl_result,
const sched_interface::ul_sched_res_t& ul_result)
{
TESTASSERT(test_ra(enb_cc_idx, dl_result, ul_result) == SRSLTE_SUCCESS);
TESTASSERT(test_ctrl_info(enb_cc_idx, dl_result, ul_result) == SRSLTE_SUCCESS);
TESTASSERT(test_scell_activation(enb_cc_idx, dl_result, ul_result) == SRSLTE_SUCCESS);
return SRSLTE_SUCCESS;
}

View File

@ -58,10 +58,71 @@ public:
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;
};
class user_state_sched_tester
{
public:
struct ue_state {
int prach_tti = -1, rar_tti = -1, msg3_tti = -1, msg4_tti = -1;
bool drb_cfg_flag = false;
srsenb::sched_interface::ue_cfg_t user_cfg;
uint32_t dl_data = 0;
uint32_t ul_data = 0;
uint32_t preamble_idx = 0;
};
explicit user_state_sched_tester(const std::vector<srsenb::sched::cell_cfg_t>& cell_params_) :
cell_params(cell_params_)
{
}
void new_tti(uint32_t tti_rx) { tti_params = tti_params_t{tti_rx}; }
bool user_exists(uint16_t rnti) const { return users.find(rnti) != users.end(); }
const ue_state* get_user_state(uint16_t rnti) const
{
return users.count(rnti) > 0 ? &users.find(rnti)->second : nullptr;
}
/* Register new users */
int add_user(uint16_t rnti, uint32_t preamble_idx, const srsenb::sched_interface::ue_cfg_t& ue_cfg);
int user_reconf(uint16_t rnti, const srsenb::sched_interface::ue_cfg_t& ue_cfg);
int bearer_cfg(uint16_t rnti, uint32_t lcid, const srsenb::sched_interface::ue_bearer_cfg_t& bearer_cfg);
void rem_user(uint16_t rnti);
/* Test the timing of RAR, Msg3, Msg4 */
int test_ra(uint32_t enb_cc_idx,
const sched_interface::dl_sched_res_t& dl_result,
const sched_interface::ul_sched_res_t& ul_result);
/* Test allocs control content */
int test_ctrl_info(uint32_t enb_cc_idx,
const sched_interface::dl_sched_res_t& dl_result,
const sched_interface::ul_sched_res_t& ul_result);
/* Test correct activation of SCells */
int test_scell_activation(uint32_t enb_cc_idx,
const sched_interface::dl_sched_res_t& dl_result,
const sched_interface::ul_sched_res_t& ul_result);
int test_all(uint32_t enb_cc_idx,
const sched_interface::dl_sched_res_t& dl_result,
const sched_interface::ul_sched_res_t& ul_result);
private:
const std::vector<srsenb::sched::cell_cfg_t> cell_params;
std::map<uint16_t, ue_state> users;
tti_params_t tti_params{10241};
};
} // namespace srsenb
#endif // SRSLTE_SCHEDULER_TEST_COMMON_H

View File

@ -33,8 +33,8 @@
* Setup Random generators
**************************/
uint32_t const seed = std::chrono::system_clock::now().time_since_epoch().count();
// uint32_t const seed = 2452071795;
// uint32_t const seed = std::chrono::system_clock::now().time_since_epoch().count();
uint32_t const seed = 2452071795;
// uint32_t const seed = 1581009287; // prb==25
std::default_random_engine rand_gen(seed);
std::uniform_real_distribution<float> unif_dist(0, 1.0);
@ -209,6 +209,8 @@ struct sched_sim_event_generator {
}
tti_ev::user_cfg_ev* user = get_user_cfg(rnti);
user->ue_cfg.reset(new srsenb::sched_interface::ue_cfg_t{generate_default_ue_cfg()});
// it should by now have a DRB1. Add other DRBs manually
user->ue_cfg->ue_bearers[2].direction = srsenb::sched_interface::ue_bearer_cfg_t::BOTH;
return user;
}
@ -220,6 +222,7 @@ private:
user_updates.begin(), user_updates.end(), [&rnti](tti_ev::user_cfg_ev& user) { return user.rnti == rnti; });
if (it == user_updates.end()) {
user_updates.emplace_back();
user_updates.back().rnti = rnti;
return &user_updates.back();
}
return &(*it);
@ -256,89 +259,4 @@ private:
}
};
int add_user(srsenb::sched* sched_ptr,
uint16_t rnti,
uint32_t prach_tti,
uint32_t preamble_idx,
uint32_t enb_cc_idx,
srsenb::sched::ue_cfg_t& ue_cfg)
{
CONDERROR(sched_ptr->ue_cfg(rnti, ue_cfg) != SRSLTE_SUCCESS, "Configuring new user rnti=0x%x to sched\n", rnti);
srsenb::sched::dl_sched_rar_info_t rar_info = {};
rar_info.prach_tti = prach_tti;
rar_info.temp_crnti = rnti;
rar_info.msg3_size = 7;
rar_info.preamble_idx = preamble_idx;
sched_ptr->dl_rach_info(enb_cc_idx, rar_info);
// // setup bearers
// bearer_ue_cfg(rnti, 0, &bearer_cfg);
srslte::logmap::get("TEST")->info("Adding user rnti=0x%x\n", rnti);
return SRSLTE_SUCCESS;
}
int apply_tti_events(srsenb::sched* sched_ptr, uint32_t tti, const tti_ev& events)
{
uint32_t prach_preamble_idx = 0;
uint32_t enb_cc_idx = 0; // TODO: Users can connect from any carrier
for (const tti_ev::user_cfg_ev& user_ev : events.user_updates) {
// may add a new user
if (user_ev.ue_cfg != nullptr) {
add_user(sched_ptr, user_ev.rnti, tti, prach_preamble_idx++, enb_cc_idx, *user_ev.ue_cfg);
}
// may remove an existing user
if (user_ev.rem_user) {
sched_ptr->bearer_ue_rem(user_ev.rnti, 0);
sched_ptr->ue_rem(user_ev.rnti);
srslte::logmap::get("TEST")->info("Adding user rnti=0x%x\n", user_ev.rnti);
}
// push UL SRs and DL packets
}
// // may remove an existing user
// if (sim_args.tti_events[tti_data.tti_rx].rem_user) {
// uint16_t rnti = sim_args.tti_events[tti_data.tti_rx].rem_rnti;
// bearer_ue_rem(rnti, 0);
// ue_rem(rnti);
// rem_user(rnti);
// log_global->info("[TESTER] Removing user rnti=0x%x\n", rnti);
// }
//
// // push UL SRs and DL packets
// for (auto& e : sim_args.tti_events[tti_data.tti_rx].users) {
// if (e.second.sr_data > 0 and tester_ues[e.first].drb_cfg_flag) {
// uint32_t tot_ul_data = ue_db[e.first].get_pending_ul_new_data(tti_data.tti_tx_ul) + e.second.sr_data;
// uint32_t lcid = 0;
// ul_bsr(e.first, lcid, tot_ul_data, true);
// }
// if (e.second.dl_data > 0 and tester_ues[e.first].msg3_tti >= 0 and
// tester_ues[e.first].msg3_tti < (int)tti_data.tti_rx) {
// // If Msg4 not yet sent, allocate data in SRB0 buffer
// uint32_t lcid = (tester_ues[e.first].msg4_tti >= 0) ? 2 : 0;
// uint32_t pending_dl_new_data = ue_db[e.first].get_pending_dl_new_data();
// if (lcid == 2 and not tester_ues[e.first].drb_cfg_flag) {
// // If RRCSetup finished
// if (pending_dl_new_data == 0) {
// // setup lcid==2 bearer
// tester_ues[e.first].drb_cfg_flag = true;
// bearer_ue_cfg(e.first, 2, &tester_ues[e.first].bearer_cfg);
// } else {
// // Let SRB0 get emptied
// continue;
// }
// }
// // TODO: Does it need TTI for checking pending data?
// uint32_t tot_dl_data = pending_dl_new_data + e.second.dl_data;
// dl_rlc_buffer_state(e.first, lcid, tot_dl_data, 0);
// }
// }
return SRSLTE_SUCCESS;
}
#endif // SRSLTE_SCHEDULER_TEST_UTILS_H