diff --git a/srsenb/test/mac/scheduler_ca_test.cc b/srsenb/test/mac/scheduler_ca_test.cc index 0e17e6ca6..8adb19916 100644 --- a/srsenb/test/mac/scheduler_ca_test.cc +++ b/srsenb/test/mac/scheduler_ca_test.cc @@ -26,17 +26,7 @@ using namespace srsenb; -template -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; - } - } -} +uint32_t const seed = std::chrono::system_clock::now().time_since_epoch().count(); /******************* * Logging * @@ -61,100 +51,13 @@ srslte::scoped_log log_global{}; * Scheduler Tester for CA *****************************/ -class sched_ca_tester : public srsenb::sched +class sched_ca_tester : public common_sched_tester { public: - struct tti_info_t { - tti_params_t tti_params{10241}; - uint32_t nof_prachs = 0; - std::vector dl_sched_result; - std::vector ul_sched_result; - }; - - int cell_cfg(const std::vector& 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; - uint32_t tti_counter = 0; - - // testers - std::vector output_tester; - std::unique_ptr ue_tester; - std::unique_ptr sched_stats; - -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 to_ack; - std::multimap to_ul_ack; + int process_tti_events(const tti_ev& tti_events); + int run_tti(const tti_ev& tti_events) override; }; -int sched_ca_tester::cell_cfg(const std::vector& cell_params) -{ - sched::cell_cfg(cell_params); // call parent - ue_tester.reset(new user_state_sched_tester{cell_params}); - sched_stats.reset(new sched_result_stats{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) { @@ -192,9 +95,9 @@ int sched_ca_tester::process_tti_events(const tti_ev& tti_ev) 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 (user->msg3_tic.is_valid() and user->msg3_tic <= tic) { // If Msg4 not yet sent, allocate data in SRB0 buffer - uint32_t lcid = (user->msg4_tti >= 0) ? 2 : 0; + uint32_t lcid = (user->msg4_tic.is_valid()) ? 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 @@ -226,99 +129,10 @@ int sched_ca_tester::process_tti_events(const tti_ev& tti_ev) return SRSLTE_SUCCESS; } -int sched_ca_tester::process_ack_txs() +int sched_ca_tester::run_tti(const tti_ev& tti_events) { - /* check if user was removed. If so, clean respective acks */ - erase_if(to_ack, - [this](std::pair& elem) { return this->ue_db.count(elem.second.rnti) == 0; }); - erase_if(to_ul_ack, - [this](std::pair& 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()); + new_test_tti(); + log_global->info("[TESTER] ---- tti=%u | nof_ues=%zd ----\n", tic.tti_rx(), ue_db.size()); process_tti_events(tti_events); process_ack_txs(); @@ -335,76 +149,7 @@ void sched_ca_tester::run_tti(uint32_t tti_rx, const tti_ev& tti_events) } process_results(); - set_acks(); - - tti_counter++; -} - -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); - } - sched_stats->process_results(tti_info.tti_params, tti_info.dl_sched_result, tti_info.ul_sched_result); - - 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)); - } - } + schedule_acks(); return SRSLTE_SUCCESS; } @@ -416,8 +161,7 @@ sim_sched_args generate_default_sim_args(uint32_t nof_prb, uint32_t nof_ccs) { sim_sched_args sim_args; - sim_args.nof_ttis = 10240 + 10; - sim_args.P_retx = 0.1; + sim_args.P_retx = 0.1; sim_args.ue_cfg = generate_default_ue_cfg(); @@ -448,36 +192,40 @@ sim_sched_args generate_default_sim_args(uint32_t nof_prb, uint32_t nof_ccs) int run_sim1() { /* Simulation Configuration Arguments */ - uint32_t nof_prb = 25; - uint32_t nof_ccs = 2; + uint32_t nof_prb = 25; + uint32_t nof_ccs = 2; + uint32_t start_tti = 0; // rand_int(0, 10240); + + /* Setup simulation arguments struct */ + sim_sched_args sim_args = generate_default_sim_args(nof_prb, nof_ccs); + sim_args.sim_log = log_global.get(); + sim_args.start_tti = start_tti; /* Simulation Objects Setup */ sched_sim_event_generator generator; // Setup scheduler sched_ca_tester tester; - tester.sim_args = generate_default_sim_args(nof_prb, nof_ccs); tester.init(nullptr); - TESTASSERT(tester.cell_cfg(tester.sim_args.cell_cfg) == SRSLTE_SUCCESS); + tester.sim_cfg(sim_args); /* Internal configurations. Do not touch */ float ul_sr_exps[] = {1, 4}; // log rand float dl_data_exps[] = {1, 4}; // log rand float P_ul_sr = randf() * 0.5, P_dl = randf() * 0.5; - uint32_t tti_start = 0; // rand_int(0, 10240); const uint16_t rnti1 = 70; uint32_t pcell_idx = 0; /* Setup Simulation */ uint32_t prach_tti = 1, msg4_tot_delay = 10; // TODO: check correct value - uint32_t msg4_size = 20; // TODO: Check - uint32_t duration = 1000; - auto process_ttis = [&generator, &tti_start, &tester]() { - for (; tester.tti_counter <= generator.tti_counter;) { - uint32_t tti = (tti_start + tester.tti_counter) % 10240; - log_global->step(tti); - tester.run_tti(tti, generator.tti_events[tester.tti_counter]); - } - }; + uint32_t msg4_size = 20; // TODO: Check + uint32_t duration = 1000; + // auto process_ttis = [&generator, &tti_start, &tester]() { + // for (; tester.tti_counter <= generator.tti_counter;) { + // uint32_t tti = (tti_start + tester.tti_count) % 10240; + // log_global->step(tti); + // tester.run_tti(generator.tti_events[tester.tti_count]); + // } + // }; /* Simulation */ @@ -486,13 +234,13 @@ int run_sim1() tti_ev::user_cfg_ev* user = generator.add_new_default_user(duration); user->ue_cfg->supported_cc_list[0].enb_cc_idx = pcell_idx; user->rnti = rnti1; - process_ttis(); + tester.test_next_ttis(generator.tti_events); TESTASSERT(tester.ue_tester->user_exists(rnti1)); // 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(rnti1, msg4_size); - process_ttis(); + tester.test_next_ttis(generator.tti_events); // Event (20 TTIs): Data back and forth auto generate_data = [&](uint32_t nof_ttis, float prob_dl, float prob_ul) { @@ -510,7 +258,7 @@ int run_sim1() } }; generate_data(20, P_dl, P_ul_sr); - process_ttis(); + tester.test_next_ttis(generator.tti_events); // Event: Reconf Complete. Activate SCells. Check if CE correctly transmitted generator.step_tti(); @@ -521,7 +269,7 @@ int run_sim1() user->ue_cfg->supported_cc_list[i].active = true; user->ue_cfg->supported_cc_list[i].enb_cc_idx = i; } - process_ttis(); + tester.test_next_ttis(generator.tti_events); // When a new DL tx takes place, it should also encode the CE for (uint32_t i = 0; i < 100; ++i) { TESTASSERT(tester.tti_info.dl_sched_result[pcell_idx].nof_data_elems > 0); @@ -532,14 +280,14 @@ int run_sim1() break; } generator.step_tti(); - process_ttis(); + tester.test_next_ttis(generator.tti_events); // now we have two CCs } // now we have two CCs // Event: Generate a bit more data, now it should go through both cells generate_data(10, 1.0, 1.0); - process_ttis(); + tester.test_next_ttis(generator.tti_events); TESTASSERT(tester.sched_stats->users[rnti1].tot_dl_sched_data[0] > 0); TESTASSERT(tester.sched_stats->users[rnti1].tot_dl_sched_data[1] > 0); TESTASSERT(tester.sched_stats->users[rnti1].tot_ul_sched_data[0] > 0); @@ -551,6 +299,9 @@ int run_sim1() int main() { + // Setup rand seed + set_randseed(seed); + srslte::logmap::get_instance()->set_default_log_level(srslte::LOG_LEVEL_INFO); printf("[TESTER] This is the chosen seed: %u\n", seed); uint32_t N_runs = 1; diff --git a/srsenb/test/mac/scheduler_test_common.cc b/srsenb/test/mac/scheduler_test_common.cc index e96202aa9..ec80b2746 100644 --- a/srsenb/test/mac/scheduler_test_common.cc +++ b/srsenb/test/mac/scheduler_test_common.cc @@ -29,6 +29,32 @@ using namespace srsenb; +/*************************** + * Random Utils + **************************/ + +std::default_random_engine rand_gen; + +float ::srsenb::randf() +{ + static std::uniform_real_distribution unif_dist(0, 1.0); + return unif_dist(rand_gen); +} + +void ::srsenb::set_randseed(uint64_t seed) +{ + rand_gen = std::default_random_engine(seed); +} + +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 @@ -294,6 +320,11 @@ int srsenb::extract_dl_prbmask(const srslte_cell_t& cell, return SRSLTE_SUCCESS; } +void user_state_sched_tester::new_tti(uint32_t tti_rx) +{ + tic++; +} + int user_state_sched_tester::add_user(uint16_t rnti, uint32_t preamble_idx, const srsenb::sched_interface::ue_cfg_t& ue_cfg) @@ -301,7 +332,7 @@ int user_state_sched_tester::add_user(uint16_t r TESTASSERT(users.count(rnti) == 0); ue_state ue; ue.user_cfg = ue_cfg; - ue.prach_tti = tti_params.tti_rx; + ue.prach_tic = tic; ue.preamble_idx = preamble_idx; users.insert(std::make_pair(rnti, ue)); return SRSLTE_SUCCESS; @@ -361,89 +392,104 @@ int user_state_sched_tester::test_ra(uint32_t enb_ // 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, + CONDERROR(ul_result.pusch[i].needs_pdcch and not userinfo.msg3_tic.is_valid(), "[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"); + CONDERROR(not userinfo.rar_tic.is_valid(), "[TESTER] No UL allocs allowed before RAR\n"); + tti_counter msg3_tic = userinfo.rar_tic + FDD_HARQ_DELAY_MS + MSG3_DELAY_MS; + CONDERROR(msg3_tic > tic.tic_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"); + CONDERROR(not userinfo.msg3_tic.is_valid(), "[TESTER] No DL data alloc allowed before Msg3 alloc\n"); + CONDERROR(tic < userinfo.msg3_tic, "[TESTER] Msg4 cannot be tx without Msg3 being received\n"); } } - if (enb_cc_idx != userinfo.user_cfg.supported_cc_list[0].enb_cc_idx) { + uint32_t primary_cc_idx = userinfo.user_cfg.supported_cc_list[0].enb_cc_idx; + if (enb_cc_idx != primary_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 rar_window = {prach_tti + 3, prach_tti + 3 + cell_params[primary_cc_idx].prach_rar_window}; + std::array rar_window = { + userinfo.prach_tic + 3, userinfo.prach_tic + 3 + (int)cell_params[primary_cc_idx].prach_rar_window}; - CONDERROR(userinfo.rar_tti < 0 and tti_params.tti_tx_dl > rar_window[1], + tti_counter tic_tx_dl = tic.tic_tx_dl(); + CONDERROR(not userinfo.rar_tic.is_valid() and tic.tic_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]) { + if (tic_tx_dl <= rar_window[1] and tic_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; + if (data.prach_tti == (uint32_t)userinfo.prach_tic.tti_rx() and data.preamble_idx == userinfo.preamble_idx) { + CONDERROR(userinfo.rar_tic.is_valid(), "There was more than one RAR for the same user\n"); + userinfo.rar_tic = tic_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) { + if (userinfo.rar_tic.is_valid()) { + tti_counter expected_msg3_tti = userinfo.rar_tic + FDD_HARQ_DELAY_MS + MSG3_DELAY_MS; + if (expected_msg3_tti == tic.tic_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(userinfo.msg3_tic.is_valid(), "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; + userinfo.msg3_tic = tic.tic_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"); + } else if (expected_msg3_tti < tic.tic_tx_ul()) { + CONDERROR(not userinfo.msg3_tic.is_valid(), "[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; - } + 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_tic.is_valid(), "Duplicate ConRes CE for the same rnti\n"); + userinfo.msg4_tic = tic.tic_tx_dl(); } - CONDERROR(userinfo.msg4_tti < 0, "Data allocations are not allowed without first receiving ConRes\n"); } + CONDERROR(not userinfo.msg4_tic.is_valid(), "Data allocs are not allowed without first receiving ConRes\n"); } } } + for (uint32_t i = 0; i < ul_result.nof_dci_elems; ++i) { + auto& pusch_alloc = ul_result.pusch[i]; + if (not pusch_alloc.needs_pdcch) { + // can be adaptive retx or msg3 + auto& ue = users[pusch_alloc.dci.rnti]; + if (tic.tic_tx_ul() == ue.msg3_tic) { + msg3_count--; + } + } + } + CONDERROR(msg3_count > 0, "[TESTER] There are pending msg3 that do not belong to any active UE\n"); + return SRSLTE_SUCCESS; } +/** + * Individual tests: + * - All RARs belong to a user that just PRACHed + * - All DL/UL data allocs have a valid RNTI + */ 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) @@ -454,7 +500,7 @@ int user_state_sched_tester::test_ctrl_info(uint32_t 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& u) { - return u.second.preamble_idx == preamble_idx and ((uint32_t)u.second.prach_tti == prach_tti); + return u.second.preamble_idx == preamble_idx and ((uint32_t)u.second.prach_tic.tti_rx() == 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, @@ -550,3 +596,234 @@ sched_result_stats::user_stats* sched_result_stats::get_user(uint16_t rnti) users[rnti].tot_ul_sched_data.resize(cell_params.size(), 0); return &users[rnti]; } + +/*********************** + * Common Sched Tester + **********************/ + +int common_sched_tester::sim_cfg(sim_sched_args args) +{ + sim_args0 = std::move(args); + + sched::cell_cfg(sim_args0.cell_cfg); // call parent 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}); + + tester_log = sim_args0.sim_log; + + return SRSLTE_SUCCESS; +} + +int common_sched_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_); + + tester_log->info("[TESTER] Adding user rnti=0x%x\n", rnti); + return SRSLTE_SUCCESS; +} + +void common_sched_tester::rem_user(uint16_t rnti) +{ + ue_tester->rem_user(rnti); +} + +void common_sched_tester::new_test_tti() +{ + if (not tic.is_valid()) { + tic.set_start_tti(sim_args0.start_tti); + } else { + tic++; + } + + tti_info.tti_params = tti_params_t{tic.tti_rx()}; + tti_info.nof_prachs = 0; + tti_info.dl_sched_result.clear(); + tti_info.ul_sched_result.clear(); + + tester_log->step(tti_info.tti_params.tti_rx); + + ue_tester->new_tti(tti_info.tti_params.tti_rx); +} + +int common_sched_tester::process_ack_txs() +{ + /* check if user was removed. If so, clean respective acks */ + erase_if(to_ack, + [this](std::pair& elem) { return this->ue_db.count(elem.second.rnti) == 0; }); + erase_if(to_ul_ack, + [this](std::pair& 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"); + tester_log->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"); + tester_log->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; +} + +int common_sched_tester::schedule_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_args0.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_args0.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 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); + } + sched_stats->process_results(tti_info.tti_params, tti_info.dl_sched_result, tti_info.ul_sched_result); + + return SRSLTE_SUCCESS; +} + +int common_sched_tester::test_next_ttis(const std::vector& tti_events) +{ + uint32_t next_idx = tic.is_valid() ? tic.total_count() - sim_args0.start_tti + 1 : 0; + + for (; next_idx < tti_events.size(); ++next_idx) { + TESTASSERT(run_tti(tti_events[next_idx]) == SRSLTE_SUCCESS); + } + return SRSLTE_SUCCESS; +} diff --git a/srsenb/test/mac/scheduler_test_common.h b/srsenb/test/mac/scheduler_test_common.h index ffb6b3ebe..a1ae68881 100644 --- a/srsenb/test/mac/scheduler_test_common.h +++ b/srsenb/test/mac/scheduler_test_common.h @@ -22,15 +22,29 @@ #ifndef SRSLTE_SCHEDULER_TEST_COMMON_H #define SRSLTE_SCHEDULER_TEST_COMMON_H +#include "scheduler_test_utils.h" #include "srsenb/hdr/stack/mac/scheduler.h" +#include namespace srsenb { -// helpers +/*************************** + * Random Utils + **************************/ + +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: @@ -70,7 +84,8 @@ class user_state_sched_tester { public: struct ue_state { - int prach_tti = -1, rar_tti = -1, msg3_tti = -1, msg4_tti = -1; + tti_counter prach_tic, rar_tic, msg3_tic, msg4_tic; + // int prach_tic = -1, rar_tic = -1, msg3_tic = -1, msg4_tic = -1; bool drb_cfg_flag = false; srsenb::sched_interface::ue_cfg_t user_cfg; uint32_t dl_data = 0; @@ -83,18 +98,17 @@ public: { } - void new_tti(uint32_t tti_rx) { tti_params = tti_params_t{tti_rx}; } + void new_tti(uint32_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); - + /* Config 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 */ @@ -117,10 +131,10 @@ public: const sched_interface::ul_sched_res_t& ul_result); private: - const std::vector cell_params; + const std::vector& cell_params; std::map users; - tti_params_t tti_params{10241}; + tti_counter tic; }; class sched_result_stats @@ -149,6 +163,64 @@ private: const std::vector cell_params; }; +// Intrusive Scheduler Tester +class common_sched_tester : public sched +{ +public: + struct tti_info_t { + tti_params_t tti_params{10241}; + uint32_t nof_prachs = 0; + std::vector dl_sched_result; + std::vector ul_sched_result; + }; + + int sim_cfg(sim_sched_args args); + int add_user(uint16_t rnti, const ue_cfg_t& ue_cfg_); + void rem_user(uint16_t rnti); + int process_ack_txs(); + int schedule_acks(); + int process_results(); + + int test_next_ttis(const std::vector& tti_events); + virtual int run_tti(const tti_ev& tti_events) = 0; + + // args + sim_sched_args sim_args0; ///< arguments used to generate TTI events + srslte::log* tester_log = nullptr; + + // tti specific params + tti_info_t tti_info; + tti_counter tic; + + // testers + std::vector output_tester; + std::unique_ptr ue_tester; + std::unique_ptr sched_stats; + +protected: + 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; + }; + + void new_test_tti(); + + // control params + std::multimap to_ack; + std::multimap to_ul_ack; +}; + } // namespace srsenb #endif // SRSLTE_SCHEDULER_TEST_COMMON_H diff --git a/srsenb/test/mac/scheduler_test_rand.cc b/srsenb/test/mac/scheduler_test_rand.cc index f4c0d89a3..a37a65aa0 100644 --- a/srsenb/test/mac/scheduler_test_rand.cc +++ b/srsenb/test/mac/scheduler_test_rand.cc @@ -39,6 +39,7 @@ #include "srslte/radio/radio.h" #include "scheduler_test_common.h" +#include "scheduler_test_utils.h" #include "srslte/common/test_common.h" /******************************************************** @@ -68,20 +69,10 @@ * ... *******************************************************/ -/*************************** - * 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 unif_dist(0, 1.0); -bool check_old_pids = false; - -float randf() -{ - return unif_dist(rand_gen); -} +bool check_old_pids = false; struct ue_stats_t { uint64_t nof_dl_rbs = 0; @@ -89,18 +80,6 @@ struct ue_stats_t { }; std::map ue_stats; -template -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 * *******************/ @@ -144,15 +123,13 @@ struct sched_sim_args { uint32_t rem_rnti; }; - std::vector tti_events; - uint32_t nof_ttis; - float P_retx; - srsenb::sched_interface::ue_cfg_t ue_cfg; - srsenb::sched_interface::ue_bearer_cfg_t bearer_cfg; + std::vector tti_events2; + sim_sched_args sim_args; + // std::vector sim_events; }; // Designed for testing purposes -struct sched_tester : public srsenb::sched { +struct sched_tester : public srsenb::common_sched_tester { struct tester_user_results { uint32_t dl_pending_data = 0; uint32_t ul_pending_data = 0; ///< data pending for UL @@ -168,9 +145,6 @@ struct sched_tester : public srsenb::sched { srsenb::ul_harq_proc ul_harq; }; struct sched_tti_data { - uint32_t tti_rx; - uint32_t tti_tx_dl; - uint32_t tti_tx_ul; uint32_t current_cfi; uint32_t nof_prachs = 0; bool ul_pending_msg3_present = false; @@ -182,111 +156,61 @@ struct sched_tester : public srsenb::sched { srsenb::sched_interface::dl_sched_res_t sched_result_dl; }; struct ue_info { - int prach_tti = -1, rar_tti = -1, msg3_tti = -1, msg4_tti = -1; bool drb_cfg_flag = false; srsenb::sched_interface::ue_bearer_cfg_t bearer_cfg; - srsenb::sched_interface::ue_cfg_t user_cfg; - uint32_t dl_data = 0; - uint32_t ul_data = 0; uint32_t preamble_idx = 0; }; - struct ack_info_t { - uint16_t rnti; - uint32_t tti; - bool dl_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; - bool ack = false; - srsenb::ul_harq_proc ul_harq; - }; - sched_sim_args sim_args; + sched_sim_args sim_events; // tester control data - std::map tester_ues; - std::multimap to_ack; - std::multimap to_ul_ack; - typedef std::multimap::iterator ack_it_t; + std::map tester_ues; // sched results - sched_tti_data tti_data; - srsenb::tti_params_t tti_params{10241}; + sched_tti_data tti_data; - int cell_cfg(const std::vector& cell_cfg) final; int add_user(uint16_t rnti, srsenb::sched_interface::ue_bearer_cfg_t bearer_cfg, srsenb::sched_interface::ue_cfg_t ue_cfg_); void rem_user(uint16_t rnti); - int test_ra(); int test_tti_result(); int assert_no_empty_allocs(); int test_collisions(); int test_harqs(); - int test_sibs(); - void run_tti(uint32_t tti_rx); + int run_tti(const tti_ev& tti_events) final; private: - void new_test_tti(uint32_t tti_); + void new_test_tti(); int process_tti_args(); void before_sched(); int process_results(); - int ack_txs(); - - std::unique_ptr output_tester; }; -int sched_tester::cell_cfg(const std::vector& cell_cfg) -{ - sched::cell_cfg(cell_cfg); - output_tester.reset(new srsenb::output_sched_tester{sched_cell_params[CARRIER_IDX]}); - return SRSLTE_SUCCESS; -} - int sched_tester::add_user(uint16_t rnti, srsenb::sched_interface::ue_bearer_cfg_t bearer_cfg, srsenb::sched_interface::ue_cfg_t ue_cfg_) { + TESTASSERT(common_sched_tester::add_user(rnti, ue_cfg_) == SRSLTE_SUCCESS); ue_info info; - info.prach_tti = tti_data.tti_rx; info.bearer_cfg = bearer_cfg; - info.user_cfg = ue_cfg_; info.preamble_idx = tti_data.nof_prachs++; tester_ues.insert(std::make_pair(rnti, info)); - 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_data.tti_rx; - rar_info.temp_crnti = rnti; - rar_info.msg3_size = 7; - rar_info.preamble_idx = info.preamble_idx; - dl_rach_info(CARRIER_IDX, rar_info); - - // setup bearers - bearer_ue_cfg(rnti, 0, &bearer_cfg); - - log_global->info("[TESTER] Adding user rnti=0x%x\n", rnti); return SRSLTE_SUCCESS; } void sched_tester::rem_user(uint16_t rnti) { + common_sched_tester::rem_user(rnti); tester_ues.erase(rnti); tti_data.ue_data.erase(rnti); } -void sched_tester::new_test_tti(uint32_t tti_) +void sched_tester::new_test_tti() { + common_sched_tester::new_test_tti(); // NOTE: make a local copy, since some of these variables may be cleared during scheduling - tti_params = srsenb::tti_params_t{tti_}; - tti_data.tti_rx = tti_; - tti_data.tti_tx_dl = TTI_TX(tti_); - tti_data.tti_tx_ul = TTI_RX_ACK(tti_); - auto& pending_msg3s = carrier_schedulers[0]->get_sf_sched_ptr(tti_data.tti_rx)->get_pending_msg3(); + auto& pending_msg3s = carrier_schedulers[0]->get_sf_sched_ptr(tti_info.tti_params.tti_rx)->get_pending_msg3(); tti_data.ul_pending_msg3_present = false; if (not pending_msg3s.empty()) { tti_data.ul_pending_msg3_present = true; @@ -303,17 +227,17 @@ void sched_tester::new_test_tti(uint32_t tti_) int sched_tester::process_tti_args() { // may add a new user - if (sim_args.tti_events[tti_data.tti_rx].new_user) { - CONDERROR( - !srslte_prach_tti_opportunity_config_fdd(sched_cell_params[CARRIER_IDX].cfg.prach_config, tti_data.tti_rx, -1), - "[TESTER] New user added in a non-PRACH TTI\n"); - uint16_t rnti = sim_args.tti_events[tti_data.tti_rx].new_rnti; - add_user(rnti, sim_args.bearer_cfg, sim_args.ue_cfg); + if (sim_events.tti_events2[tti_info.tti_params.tti_rx].new_user) { + CONDERROR(!srslte_prach_tti_opportunity_config_fdd( + sched_cell_params[CARRIER_IDX].cfg.prach_config, tti_info.tti_params.tti_rx, -1), + "[TESTER] New user added in a non-PRACH TTI\n"); + uint16_t rnti = sim_events.tti_events2[tti_info.tti_params.tti_rx].new_rnti; + add_user(rnti, sim_events.sim_args.bearer_cfg, sim_events.sim_args.ue_cfg); } // 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; + if (sim_events.tti_events2[tti_info.tti_params.tti_rx].rem_user) { + uint16_t rnti = sim_events.tti_events2[tti_info.tti_params.tti_rx].rem_rnti; bearer_ue_rem(rnti, 0); ue_rem(rnti); rem_user(rnti); @@ -321,16 +245,16 @@ int sched_tester::process_tti_args() } // push UL SRs and DL packets - for (auto& e : sim_args.tti_events[tti_data.tti_rx].users) { + for (auto& e : sim_events.tti_events2[tti_info.tti_params.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 tot_ul_data = ue_db[e.first].get_pending_ul_new_data(tti_info.tti_params.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) { + auto* user = ue_tester->get_user_state(e.first); + if (e.second.dl_data > 0 and user->msg3_tic.is_valid() and user->msg3_tic.tti_rx() < tti_info.tti_params.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 lcid = (user->msg4_tic.is_valid()) ? 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 @@ -359,14 +283,15 @@ void sched_tester::before_sched() uint16_t rnti = it.first; srsenb::sched_ue* user = &it.second; tester_user_results d; - srsenb::ul_harq_proc* hul = user->get_ul_harq(tti_data.tti_tx_ul, CARRIER_IDX); + srsenb::ul_harq_proc* hul = user->get_ul_harq(tti_info.tti_params.tti_tx_ul, CARRIER_IDX); d.ul_pending_data = get_ul_buffer(rnti); - // user->get_pending_ul_new_data(tti_data.tti_tx_ul) or hul->has_pending_retx(); // get_ul_buffer(rnti); + // user->get_pending_ul_new_data(tti_info.tti_params.tti_tx_ul) or hul->has_pending_retx(); // + // get_ul_buffer(rnti); d.dl_pending_data = get_dl_buffer(rnti); d.has_ul_retx = hul->has_pending_retx(); d.has_ul_tx = d.has_ul_retx or d.ul_pending_data > 0; - srsenb::dl_harq_proc* hdl = user->get_pending_dl_harq(tti_data.tti_tx_dl, CARRIER_IDX); - d.has_dl_retx = (hdl != nullptr) and hdl->has_pending_retx(0, tti_data.tti_tx_dl); + srsenb::dl_harq_proc* hdl = user->get_pending_dl_harq(tti_info.tti_params.tti_tx_dl, CARRIER_IDX); + d.has_dl_retx = (hdl != nullptr) and hdl->has_pending_retx(0, tti_info.tti_params.tti_tx_dl); d.has_dl_tx = (hdl != nullptr) or (it.second.get_empty_dl_harq(CARRIER_IDX) != nullptr and d.dl_pending_data > 0); d.has_ul_newtx = not d.has_ul_retx and d.ul_pending_data > 0; tti_data.ue_data.insert(std::make_pair(rnti, d)); @@ -380,8 +305,8 @@ void sched_tester::before_sched() const srsenb::dl_harq_proc* h = user->get_dl_harq(i, CARRIER_IDX); tti_data.ue_data[rnti].dl_harqs[i] = *h; } - // NOTE: ACK might have just cleared the harq for tti_data.tti_tx_ul - tti_data.ue_data[rnti].ul_harq = *user->get_ul_harq(tti_data.tti_tx_ul, CARRIER_IDX); + // NOTE: ACK might have just cleared the harq for tti_info.tti_params.tti_tx_ul + tti_data.ue_data[rnti].ul_harq = *user->get_ul_harq(tti_info.tti_params.tti_tx_ul, CARRIER_IDX); } // TODO: Check whether pending pending_rar.rar_tti correspond to a prach_tti @@ -389,12 +314,16 @@ void sched_tester::before_sched() int sched_tester::process_results() { + tti_info.dl_sched_result.resize(1); + tti_info.dl_sched_result[0] = tti_data.sched_result_dl; + tti_info.ul_sched_result.resize(1); + tti_info.ul_sched_result[0] = tti_data.sched_result_ul; for (uint32_t i = 0; i < tti_data.sched_result_ul.nof_dci_elems; ++i) { uint16_t rnti = tti_data.sched_result_ul.pusch[i].dci.rnti; tti_data.ue_data[rnti].ul_sched = &tti_data.sched_result_ul.pusch[i]; CONDERROR(tester_ues.count(rnti) == 0, "[TESTER] [%d] The user rnti=0x%x that no longer exists got allocated.\n", - tti_data.tti_rx, + tti_info.tti_params.tti_rx, rnti); } for (uint32_t i = 0; i < tti_data.sched_result_dl.nof_data_elems; ++i) { @@ -402,116 +331,34 @@ int sched_tester::process_results() tti_data.ue_data[rnti].dl_sched = &tti_data.sched_result_dl.data[i]; CONDERROR(tester_ues.count(rnti) == 0, "[TESTER] [%d] The user rnti=0x%x that no longer exists got allocated.\n", - tti_data.tti_rx, + tti_info.tti_params.tti_rx, rnti); } test_tti_result(); - test_ra(); + ue_tester->test_ra(0, tti_info.dl_sched_result[CARRIER_IDX], tti_info.ul_sched_result[CARRIER_IDX]); test_collisions(); assert_no_empty_allocs(); test_harqs(); - test_sibs(); + output_tester[CARRIER_IDX].test_sib_scheduling(tti_info.tti_params, tti_data.sched_result_dl); return SRSLTE_SUCCESS; } -void sched_tester::run_tti(uint32_t tti_rx) +int sched_tester::run_tti(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()); + new_test_tti(); + log_global->info("[TESTER] ---- tti=%u | nof_ues=%zd ----\n", tic.tti_rx(), ue_db.size()); process_tti_args(); - ack_txs(); + 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); + dl_sched(tti_info.tti_params.tti_tx_dl, CARRIER_IDX, tti_data.sched_result_dl); + ul_sched(tti_info.tti_params.tti_tx_ul, CARRIER_IDX, tti_data.sched_result_ul); process_results(); -} - -/** - * Tests whether the RAR and Msg3 were scheduled within the expected windows - */ -int sched_tester::test_ra() -{ - uint32_t msg3_count = 0; - - // Test if allocations only take place for users with pending data or in RAR - for (auto& iter : tti_data.ue_data) { - uint16_t rnti = iter.first; - sched_tester::ue_info& userinfo = tester_ues[rnti]; - - // Check whether RA has completed correctly - int prach_tti = userinfo.prach_tti; - // if (userinfo.msg4_tti > prach_tti) { // Msg4 already scheduled - // continue; - // } - - uint32_t window[2] = {(uint32_t)prach_tti + 3, prach_tti + 3 + sched_cell_params[CARRIER_IDX].cfg.prach_rar_window}; - if (prach_tti >= userinfo.rar_tti) { // RAR not yet sent - CONDERROR(tti_data.tti_tx_dl > window[1], "[TESTER] There was no RAR scheduled within the RAR Window\n"); - if (tti_data.tti_tx_dl >= window[0]) { - for (uint32_t i = 0; i < tti_data.sched_result_dl.nof_rar_elems; ++i) { - for (uint32_t j = 0; j < tti_data.sched_result_dl.rar[i].nof_grants; ++j) { - auto& data = tti_data.sched_result_dl.rar[i].msg3_grant[j].data; - if (data.prach_tti == (uint32_t)userinfo.prach_tti and data.preamble_idx == userinfo.preamble_idx) { - userinfo.rar_tti = tti_data.tti_tx_dl; - } - } - } - } - } else if (userinfo.msg3_tti < prach_tti) { // Msg3 not yet sent - uint32_t msg3_tti = (uint32_t)(userinfo.rar_tti + FDD_HARQ_DELAY_MS + MSG3_DELAY_MS) % 10240; - if (msg3_tti == tti_data.tti_tx_ul) { - for (uint32_t i = 0; i < tti_data.sched_result_ul.nof_dci_elems; ++i) { - if (tti_data.sched_result_ul.pusch[i].dci.rnti == rnti) { - CONDERROR(tti_data.sched_result_ul.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_data.tti_tx_ul; - msg3_count++; - } - } - CONDERROR(msg3_count == 0, "[TESTER] No UL msg3 allocation was made\n"); - } else if (msg3_tti < tti_data.tti_tx_ul) { - TESTERROR("[TESTER] No UL msg3 allocation was made\n"); - } - } - - // Find any Msg4 allocation - for (uint32_t i = 0; i < tti_data.sched_result_dl.nof_data_elems; ++i) { - auto& ue_data = tti_data.sched_result_dl.data[i]; - if (ue_data.dci.rnti != rnti) { - continue; - } - for (uint32_t j = 0; j < tti_data.sched_result_dl.data[i].nof_pdu_elems[0]; ++j) { - if (ue_data.pdu[0][j].lcid == srslte::sch_subh::CON_RES_ID) { - // ConRes found - CONDERROR(ue_data.dci.format != SRSLTE_DCI_FORMAT1, "ConRes must be format1\n"); - CONDERROR(userinfo.msg3_tti > (int)tti_data.tti_rx, "Transmitting ConRes without receiving Msg3\n"); - CONDERROR(userinfo.msg4_tti >= 0, "ConRes CE cannot be retransmitted for the same rnti\n"); - userinfo.msg4_tti = tti_data.tti_tx_dl; - } - } - } - } - for (uint32_t i = 0; i < tti_data.sched_result_ul.nof_dci_elems; ++i) { - auto& pusch_alloc = tti_data.sched_result_ul.pusch[i]; - if (not pusch_alloc.needs_pdcch) { - // can be adaptive retx or msg3 - auto& ue = tester_ues[pusch_alloc.dci.rnti]; - if (tti_data.tti_tx_ul == (uint32_t)ue.msg3_tti) { - msg3_count--; - } - } - // msg3_count -= tti_data.sched_result_ul.pusch[i].needs_pdcch ? 0 : 1; - } - CONDERROR(msg3_count > 0, "[TESTER] There are pending msg3 that do not belong to any active UE\n"); - return SRSLTE_SUCCESS; } @@ -527,7 +374,7 @@ int sched_tester::assert_no_empty_allocs() // TODO: This test does not work for adaptive re-tx TESTERROR("[TESTER] There was a user without data that got allocated in UL\n"); } - // srsenb::ul_harq_proc* hul = user->get_ul_harq(tti_data.tti_tx_ul); + // srsenb::ul_harq_proc* hul = user->get_ul_harq(tti_info.tti_params.tti_tx_ul); iter.second.ul_retx_got_delayed = iter.second.has_ul_retx and iter.second.ul_harq.is_empty(0); tti_data.total_ues.ul_retx_got_delayed |= iter.second.ul_retx_got_delayed; // Retxs cannot give space to newtx allocations @@ -536,12 +383,12 @@ int sched_tester::assert_no_empty_allocs() } // There must be allocations if there is pending data/retxs. - bool no_dl_allocs = true; - for (auto& it : tti_data.ue_data) { - if (it.second.dl_sched != nullptr) { - no_dl_allocs = false; - } - } + // bool no_dl_allocs = true; + // for (auto& it : tti_data.ue_data) { + // if (it.second.dl_sched != nullptr) { + // no_dl_allocs = false; + // } + // } // CONDERROR(tti_data.total_ues.has_dl_tx and no_dl_allocs, "There was pending DL data but no user got allocated\n"); // TODO: You have to verify if there is space for the retx since it is non-adaptive return SRSLTE_SUCCESS; @@ -553,14 +400,14 @@ int sched_tester::assert_no_empty_allocs() int sched_tester::test_tti_result() { /* TEST: Check if there are collisions in the PDCCH */ - TESTASSERT(output_tester->test_pdcch_collisions( + TESTASSERT(output_tester[CARRIER_IDX].test_pdcch_collisions( tti_data.sched_result_dl, tti_data.sched_result_ul, &tti_data.used_cce) == SRSLTE_SUCCESS); /* TEST: Check whether dci values are correct */ - TESTASSERT(output_tester->test_dci_values_consistency(tti_data.sched_result_dl, tti_data.sched_result_ul) == - SRSLTE_SUCCESS); + TESTASSERT(output_tester[CARRIER_IDX].test_dci_values_consistency(tti_data.sched_result_dl, + tti_data.sched_result_ul) == SRSLTE_SUCCESS); - const srsenb::sf_sched* tti_sched = carrier_schedulers[0]->get_sf_sched_ptr(tti_data.tti_rx); + const srsenb::sf_sched* tti_sched = carrier_schedulers[0]->get_sf_sched_ptr(tti_info.tti_params.tti_rx); for (uint32_t i = 0; i < tti_data.sched_result_ul.nof_dci_elems; ++i) { const auto& pusch = tti_data.sched_result_ul.pusch[i]; @@ -584,7 +431,7 @@ int sched_tester::test_tti_result() } /* verify if sched_result "used_cce" coincide with sched "used_cce" */ - auto* tti_alloc = carrier_schedulers[0]->get_sf_sched_ptr(tti_data.tti_rx); + auto* tti_alloc = carrier_schedulers[0]->get_sf_sched_ptr(tti_info.tti_params.tti_rx); if (tti_data.used_cce != tti_alloc->get_pdcch_mask()) { std::string mask_str = tti_alloc->get_pdcch_mask().to_string(); TESTERROR( @@ -599,7 +446,8 @@ int sched_tester::test_tti_result() // 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_data.tti_rx); + // TESTERROR("[%d] There was pending UL data and free CCEs, but no user got allocated\n", + // tti_info.tti_params.tti_rx); // } // } // } @@ -616,19 +464,19 @@ int sched_tester::test_harqs() const srsenb::dl_harq_proc* h = ue_db[rnti].get_dl_harq(h_id, CARRIER_IDX); CONDERROR(h == nullptr, "[TESTER] scheduled DL harq pid=%d does not exist\n", h_id); CONDERROR(h->is_empty(), "[TESTER] Cannot schedule an empty harq proc\n"); - CONDERROR(h->get_tti() != tti_data.tti_tx_dl, + CONDERROR(h->get_tti() != tti_info.tti_params.tti_tx_dl, "[TESTER] The scheduled DL harq pid=%d does not a valid tti=%u\n", h_id, - tti_data.tti_tx_dl); + tti_info.tti_params.tti_tx_dl); CONDERROR(h->get_n_cce() != data.dci.location.ncce, "[TESTER] Harq DCI location does not match with result\n"); - if (tti_data.ue_data[rnti].dl_harqs[h_id].has_pending_retx(0, tti_data.tti_tx_dl)) { // retx + if (tti_data.ue_data[rnti].dl_harqs[h_id].has_pending_retx(0, tti_info.tti_params.tti_tx_dl)) { // retx CONDERROR(tti_data.ue_data[rnti].dl_harqs[h_id].nof_retx(0) + 1 != h->nof_retx(0), "[TESTER] A dl harq of user rnti=0x%x was likely overwritten.\n", rnti); - CONDERROR(h->nof_retx(0) >= sim_args.ue_cfg.maxharq_tx, + CONDERROR(h->nof_retx(0) >= sim_events.sim_args.ue_cfg.maxharq_tx, "[TESTER] The number of retx=%d exceeded its max=%d\n", h->nof_retx(0), - sim_args.ue_cfg.maxharq_tx); + sim_events.sim_args.ue_cfg.maxharq_tx); } else { // newtx CONDERROR(h->nof_retx(0) != 0, "[TESTER] A new harq was scheduled but with invalid number of retxs\n"); } @@ -638,11 +486,11 @@ int sched_tester::test_harqs() const auto& pusch = tti_data.sched_result_ul.pusch[i]; uint16_t rnti = pusch.dci.rnti; const auto& ue_data = tti_data.ue_data[rnti]; - const srsenb::ul_harq_proc* h = ue_db[rnti].get_ul_harq(tti_data.tti_tx_ul, CARRIER_IDX); + const srsenb::ul_harq_proc* h = ue_db[rnti].get_ul_harq(tti_info.tti_params.tti_tx_ul, CARRIER_IDX); CONDERROR(h == nullptr or h->is_empty(), "[TESTER] scheduled UL harq does not exist or is empty\n"); - CONDERROR(h->get_tti() != tti_data.tti_tx_ul, + CONDERROR(h->get_tti() != tti_info.tti_params.tti_tx_ul, "[TESTER] The scheduled UL harq does not a valid tti=%u\n", - tti_data.tti_tx_ul); + tti_info.tti_params.tti_tx_ul); CONDERROR(h->has_pending_ack(), "[TESTER] At the end of the TTI, there shouldnt be any pending ACKs\n"); if (h->has_pending_retx()) { @@ -662,7 +510,7 @@ int sched_tester::test_harqs() const auto& phich = tti_data.sched_result_ul.phich[i]; CONDERROR(tti_data.ue_data.count(phich.rnti) == 0, "[TESTER] Allocated PHICH rnti no longer exists\n"); const auto& hprev = tti_data.ue_data[phich.rnti].ul_harq; - const auto* h = ue_db[phich.rnti].get_ul_harq(tti_data.tti_tx_ul, CARRIER_IDX); + const auto* h = ue_db[phich.rnti].get_ul_harq(tti_info.tti_params.tti_tx_ul, CARRIER_IDX); CONDERROR(not hprev.has_pending_ack(), "[TESTER] Alloc PHICH did not have any pending ack\n"); bool maxretx_flag = hprev.nof_retx(0) + 1 >= hprev.max_nof_retx(); if (phich.phich == sched_interface::ul_sched_phich_t::ACK) { @@ -688,59 +536,15 @@ int sched_tester::test_harqs() } // schedule future acks - for (uint32_t i = 0; i < tti_data.sched_result_dl.nof_data_elems; ++i) { - ack_info_t ack_data; - ack_data.rnti = tti_data.sched_result_dl.data[i].dci.rnti; - ack_data.tti = FDD_HARQ_DELAY_MS + tti_data.tti_tx_dl; - const srsenb::dl_harq_proc* dl_h = - ue_db[ack_data.rnti].get_dl_harq(tti_data.sched_result_dl.data[i].dci.pid, CARRIER_IDX); - ack_data.dl_harq = *dl_h; - if (ack_data.dl_harq.nof_retx(0) == 0) { - ack_data.dl_ack = randf() > sim_args.P_retx; - } else { // always ack after three retxs - ack_data.dl_ack = ack_data.dl_harq.nof_retx(0) == 3; - } - - // Remove harq from the ack list if there was a harq rewrite - ack_it_t 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()) { - 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()); - ack_it_t 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_data.sched_result_ul.nof_dci_elems; ++i) { - const auto& pusch = tti_data.sched_result_ul.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_data.tti_tx_ul, CARRIER_IDX); - ack_data.tti_tx_ul = tti_data.tti_tx_ul; - ack_data.tti_ack = tti_data.tti_tx_ul + FDD_HARQ_DELAY_MS; - 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)); - } + TESTASSERT(schedule_acks() == SRSLTE_SUCCESS); // Check whether some pids got old if (check_old_pids) { for (auto& user : ue_db) { for (int i = 0; i < 2 * FDD_HARQ_DELAY_MS; i++) { if (not(user.second.get_dl_harq(i, CARRIER_IDX)->is_empty(0) and user.second.get_dl_harq(1, CARRIER_IDX))) { - if (srslte_tti_interval(tti_data.tti_tx_dl, user.second.get_dl_harq(i, CARRIER_IDX)->get_tti()) > 49) { + if (srslte_tti_interval(tti_info.tti_params.tti_tx_dl, user.second.get_dl_harq(i, CARRIER_IDX)->get_tti()) > + 49) { TESTERROR("[TESTER] The pid=%d for rnti=0x%x got old.\n", user.second.get_dl_harq(i, CARRIER_IDX)->get_id(), user.first); @@ -753,18 +557,14 @@ int sched_tester::test_harqs() return SRSLTE_SUCCESS; } -int sched_tester::test_sibs() -{ - return output_tester->test_sib_scheduling(tti_params, tti_data.sched_result_dl); -} - int sched_tester::test_collisions() { - const srsenb::sf_sched* tti_sched = carrier_schedulers[0]->get_sf_sched_ptr(tti_data.tti_rx); + const srsenb::sf_sched* tti_sched = carrier_schedulers[0]->get_sf_sched_ptr(tti_info.tti_params.tti_rx); srsenb::prbmask_t ul_allocs(sched_cell_params[CARRIER_IDX].cfg.cell.nof_prb); /* TEST: any collision in PUCCH and PUSCH */ - TESTASSERT(output_tester->test_pusch_collisions(tti_params, tti_data.sched_result_ul, ul_allocs) == SRSLTE_SUCCESS); + TESTASSERT(output_tester[CARRIER_IDX].test_pusch_collisions( + tti_info.tti_params, tti_data.sched_result_ul, ul_allocs) == SRSLTE_SUCCESS); /* TEST: check whether cumulative UL PRB masks coincide */ if (ul_allocs != tti_sched->get_ul_mask()) { @@ -806,7 +606,8 @@ int sched_tester::test_collisions() /* TEST: check any collision in PDSCH */ srsenb::rbgmask_t rbgmask(sched_cell_params[CARRIER_IDX].cfg.cell.nof_prb); - TESTASSERT(output_tester->test_pdsch_collisions(tti_params, tti_data.sched_result_dl, rbgmask) == SRSLTE_SUCCESS); + TESTASSERT(output_tester[CARRIER_IDX].test_pdsch_collisions(tti_info.tti_params, tti_data.sched_result_dl, 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); @@ -818,104 +619,25 @@ int sched_tester::test_collisions() } // TEST: check if resulting DL mask is equal to scheduler internal DL mask - if (rbgmask != carrier_schedulers[0]->get_sf_sched_ptr(tti_data.tti_rx)->get_dl_mask()) { + if (rbgmask != carrier_schedulers[0]->get_sf_sched_ptr(tti_info.tti_params.tti_rx)->get_dl_mask()) { TESTERROR("[TESTER] The UL PRB mask and the scheduler result UL mask are not consistent (%s!=%s)\n", rbgmask.to_string().c_str(), - carrier_schedulers[CARRIER_IDX]->get_sf_sched_ptr(tti_data.tti_rx)->get_dl_mask().to_string().c_str()); + carrier_schedulers[CARRIER_IDX] + ->get_sf_sched_ptr(tti_info.tti_params.tti_rx) + ->get_dl_mask() + .to_string() + .c_str()); } return SRSLTE_SUCCESS; } -int sched_tester::ack_txs() -{ - /* check if user was removed. If so, clean respective acks */ - erase_if(to_ack, - [this](std::pair& elem) { return this->ue_db.count(elem.second.rnti) == 0; }); - erase_if(to_ul_ack, - [this](std::pair& 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_data.tti_rx) { - continue; - } - srsenb::dl_harq_proc* h = ue_db[ack_it.second.rnti].get_dl_harq(ack_it.second.dl_harq.get_id(), CARRIER_IDX); - const srsenb::dl_harq_proc& hack = ack_it.second.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 (ack_it.second.dl_harq.is_empty(tb)) { - continue; - } - ret |= dl_ack_info(tti_data.tti_rx, ack_it.second.rnti, CARRIER_IDX, tb, ack_it.second.dl_ack) > 0; - } - CONDERROR(not ret, "[TESTER] The dl harq proc that was acked does not exist\n"); - - if (ack_it.second.dl_ack) { - CONDERROR(!h->is_empty(), "[TESTER] ACKed dl harq was not emptied\n"); - CONDERROR(h->has_pending_retx(0, tti_data.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_data.tti_rx, - ack_it.second.rnti, - ack_it.second.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_data.tti_rx) { - continue; - } - srsenb::ul_harq_proc* h = ue_db[ack_it.second.rnti].get_ul_harq(tti_data.tti_rx, CARRIER_IDX); - const srsenb::ul_harq_proc& hack = ack_it.second.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_data.tti_rx, ack_it.second.rnti, CARRIER_IDX, ack_it.second.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 (ack_it.second.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_data.tti_rx, ack_it.second.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_data.tti_rx); - to_ul_ack.erase(tti_data.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; -} - srsenb::sched_interface::cell_cfg_t generate_cell_cfg() { srsenb::sched_interface::cell_cfg_t cell_cfg = {}; srslte_cell_t& cell_cfg_phy = cell_cfg.cell; std::uniform_int_distribution dist_prb_idx(0, 5); - uint32_t prb_idx = dist_prb_idx(rand_gen); + uint32_t prb_idx = dist_prb_idx(srsenb::get_rand_gen()); /* Set PHY cell configuration */ cell_cfg_phy.id = 1; @@ -938,7 +660,7 @@ srsenb::sched_interface::cell_cfg_t generate_cell_cfg() return cell_cfg; } -void test_scheduler_rand(std::vector cell_cfg, const sched_sim_args& args) +void test_scheduler_rand(const sched_sim_args& args) { // Create classes sched_tester tester; @@ -946,101 +668,100 @@ void test_scheduler_rand(std::vector cell_c log_global->set_level(srslte::LOG_LEVEL_INFO); - tester.sim_args = args; - // srslte_cell_t& cell_cfg_phy = cell_cfg.cell; - // srsenb::sched_interface::dl_sched_res_t& sched_result_dl = tester.tti_data.sched_result_dl; - // srsenb::sched_interface::ul_sched_res_t& sched_result_ul = tester.tti_data.sched_result_ul; + tester.sim_events = args; + sim_sched_args sim_args = args.sim_args; + sim_args.ue_cfg = args.sim_args.ue_cfg; + sim_args.bearer_cfg = args.sim_args.bearer_cfg; + sim_args.start_tti = 0; + sim_args.sim_log = log_global.get(); tester.init(nullptr); - tester.cell_cfg(cell_cfg); + tester.sim_cfg(sim_args); - bool running = true; uint32_t tti = 0; uint32_t nof_ttis = 0; - while (running) { - if (nof_ttis > args.nof_ttis) { - running = false; - } + while (nof_ttis <= args.tti_events2.size()) { log_global->step(tti); - tester.run_tti(tti); + tester.run_tti({}); nof_ttis++; tti = (tti + 1) % 10240; } } -sched_sim_args rand_sim_params(const srsenb::sched_interface::cell_cfg_t& cell_cfg, uint32_t nof_ttis) +sched_sim_args rand_sim_params(uint32_t nof_ttis) { - sched_sim_args sim_args; + sched_sim_args sim_gen; std::vector > current_rntis; uint16_t rnti_start = 70; uint32_t max_conn_dur = 10000, min_conn_dur = 5000; - float P_ul_sr = randf() * 0.5, P_dl = randf() * 0.5; + float P_ul_sr = srsenb::randf() * 0.5, P_dl = srsenb::randf() * 0.5; float P_prach = 0.99f; // 0.1f + randf()*0.3f; float ul_sr_exps[] = {1, 4}; // log rand float dl_data_exps[] = {1, 4}; // log rand uint32_t max_nof_users = 5; std::uniform_int_distribution<> connection_dur_dist(min_conn_dur, max_conn_dur); - sim_args.ue_cfg = {}; - sim_args.ue_cfg.aperiodic_cqi_period = 40; - sim_args.ue_cfg.maxharq_tx = 5; - sim_args.ue_cfg.dl_cfg.tm = SRSLTE_TM1; - sim_args.ue_cfg.supported_cc_list.emplace_back(); - sim_args.ue_cfg.supported_cc_list.back().enb_cc_idx = 0; - sim_args.ue_cfg.ue_bearers[0].direction = srsenb::sched_interface::ue_bearer_cfg_t::BOTH; + sim_gen.sim_args.cell_cfg = {generate_cell_cfg()}; - bzero(&sim_args.bearer_cfg, sizeof(srsenb::sched_interface::ue_bearer_cfg_t)); - sim_args.bearer_cfg.direction = srsenb::sched_interface::ue_bearer_cfg_t::BOTH; + sim_gen.sim_args.ue_cfg = generate_default_ue_cfg(); - sim_args.nof_ttis = nof_ttis; - sim_args.P_retx = 0.1; - sim_args.tti_events.resize(sim_args.nof_ttis); + bzero(&sim_gen.sim_args.bearer_cfg, sizeof(srsenb::sched_interface::ue_bearer_cfg_t)); + sim_gen.sim_args.bearer_cfg.direction = srsenb::sched_interface::ue_bearer_cfg_t::BOTH; - for (uint32_t tti = 0; tti < sim_args.tti_events.size(); ++tti) { + sim_gen.sim_args.P_retx = 0.1; + sim_gen.tti_events2.resize(nof_ttis); + // sim_gen.sim_events.resize(nof_ttis); + + for (uint32_t tti = 0; tti < sim_gen.tti_events2.size(); ++tti) { if (not current_rntis.empty()) { // may rem user for (uint32_t i = 0; i < current_rntis.size(); ++i) { if (current_rntis[i][2] + current_rntis[i][1] <= tti) { - std::vector >::iterator it_to_rem = current_rntis.begin() + i; - sim_args.tti_events[tti].rem_user = true; - sim_args.tti_events[tti].rem_rnti = (*it_to_rem)[0]; + auto it_to_rem = current_rntis.begin() + i; + sim_gen.tti_events2[tti].rem_user = true; + sim_gen.tti_events2[tti].rem_rnti = (*it_to_rem)[0]; current_rntis.erase(it_to_rem); } } for (auto& current_rnti : current_rntis) { uint32_t rnti = current_rnti[0]; - if (randf() < P_ul_sr) { - float exp = ul_sr_exps[0] + randf() * (ul_sr_exps[1] - ul_sr_exps[0]); - sim_args.tti_events[tti].users[rnti].sr_data = (uint32_t)pow(10, exp); + if (srsenb::randf() < P_ul_sr) { + float exp = ul_sr_exps[0] + srsenb::randf() * (ul_sr_exps[1] - ul_sr_exps[0]); + sim_gen.tti_events2[tti].users[rnti].sr_data = (uint32_t)pow(10, exp); } - if (randf() < P_dl) { - float exp = dl_data_exps[0] + randf() * (dl_data_exps[1] - dl_data_exps[0]); - sim_args.tti_events[tti].users[rnti].dl_data = (uint32_t)pow(10, exp); + if (srsenb::randf() < P_dl) { + float exp = dl_data_exps[0] + srsenb::randf() * (dl_data_exps[1] - dl_data_exps[0]); + sim_gen.tti_events2[tti].users[rnti].dl_data = (uint32_t)pow(10, exp); } } } // may add new user (For now, we only support one UE per PRACH) - bool is_prach_tti = srslte_prach_tti_opportunity_config_fdd(cell_cfg.prach_config, tti, -1); - if (is_prach_tti and current_rntis.size() < max_nof_users and randf() < P_prach) { + bool is_prach_tti = + srslte_prach_tti_opportunity_config_fdd(sim_gen.sim_args.cell_cfg[CARRIER_IDX].prach_config, tti, -1); + if (is_prach_tti and current_rntis.size() < max_nof_users and srsenb::randf() < P_prach) { std::vector elem(3); elem[0] = rnti_start; elem[1] = tti; - elem[2] = connection_dur_dist(rand_gen); + elem[2] = connection_dur_dist(srsenb::get_rand_gen()); current_rntis.push_back(elem); - sim_args.tti_events[tti].new_user = true; - sim_args.tti_events[tti].new_rnti = rnti_start++; + sim_gen.tti_events2[tti].new_user = true; + sim_gen.tti_events2[tti].new_rnti = rnti_start; + rnti_start++; } } - return sim_args; + return sim_gen; } int main() { + // Setup seed + srsenb::set_randseed(seed); + srslte::logmap::set_default_log_level(srslte::LOG_LEVEL_INFO); printf("[TESTER] This is the chosen seed: %u\n", seed); /* initialize random seed: */ @@ -1048,10 +769,8 @@ int main() for (uint32_t n = 0; n < N_runs; ++n) { printf("Sim run number: %u\n", n + 1); - std::vector cell_cfg = {generate_cell_cfg()}; - sched_sim_args sim_args = rand_sim_params(cell_cfg[0], nof_ttis); - - test_scheduler_rand(cell_cfg, sim_args); + sched_sim_args sim_args = rand_sim_params(nof_ttis); + test_scheduler_rand(sim_args); } return 0; diff --git a/srsenb/test/mac/scheduler_test_utils.h b/srsenb/test/mac/scheduler_test_utils.h index 0d707d914..800ea4e43 100644 --- a/srsenb/test/mac/scheduler_test_utils.h +++ b/srsenb/test/mac/scheduler_test_utils.h @@ -27,36 +27,67 @@ #include "srslte/interfaces/sched_interface.h" #include #include -#include + +struct tti_counter { + tti_counter() = default; + void set_start_tti(uint32_t tti_) { counter = tti_; } + uint32_t tti_rx() const { return counter % 10240u; } + tti_counter tic_tx_dl() const { return tti_counter{counter + TX_DELAY}; } + tti_counter tic_tx_ul() const { return tti_counter{counter + TX_DELAY + FDD_HARQ_DELAY_MS}; } + bool operator==(const tti_counter& other) const { return counter == other.counter; } + bool operator!=(const tti_counter& other) const { return counter != other.counter; } + bool operator<(const tti_counter& other) const { return counter < other.counter; } + bool operator<=(const tti_counter& other) const { return counter <= other.counter; } + bool operator>=(const tti_counter& other) const { return counter >= other.counter; } + bool operator>(const tti_counter& other) const { return counter > other.counter; } + uint32_t operator-(const tti_counter& other) const { return counter - other.counter; } + tti_counter& operator-=(uint32_t jump) + { + counter -= jump; + return *this; + } + tti_counter& operator+=(uint32_t jump) + { + counter += jump; + return *this; + } + tti_counter& operator+=(int32_t jump) + { + counter += jump; + return *this; + } + tti_counter& operator++() { return this->operator+=(1); } + tti_counter operator+(int32_t jump) { return tti_counter{counter + jump}; } + tti_counter operator++(int) { return tti_counter{++counter}; } + bool is_valid() const { return counter != std::numeric_limits::max(); } + uint32_t total_count() const { return counter; } + +private: + explicit tti_counter(uint32_t c_) : counter(c_) {} + uint32_t counter = std::numeric_limits::max(); +}; /*************************** - * Setup Random generators + * Function helpers **************************/ -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 unif_dist(0, 1.0); -bool check_old_pids = false; - -float randf() +template +void erase_if(MapContainer& c, Predicate should_remove) { - return unif_dist(rand_gen); -} - -template -Integer rand_int(Integer lb, Integer ub) -{ - std::uniform_int_distribution dist(lb, ub); - return dist(rand_gen); + for (auto it = c.begin(); it != c.end();) { + if (should_remove(*it)) { + it = c.erase(it); + } else { + ++it; + } + } } /***************************** * Setup Sched Configuration ****************************/ -srsenb::sched_interface::cell_cfg_t generate_default_cell_cfg(uint32_t nof_prb) +inline srsenb::sched_interface::cell_cfg_t generate_default_cell_cfg(uint32_t nof_prb) { srsenb::sched_interface::cell_cfg_t cell_cfg = {}; srslte_cell_t& cell_cfg_phy = cell_cfg.cell; @@ -82,7 +113,7 @@ srsenb::sched_interface::cell_cfg_t generate_default_cell_cfg(uint32_t nof_prb) return cell_cfg; } -srsenb::sched_interface::ue_cfg_t generate_default_ue_cfg() +inline srsenb::sched_interface::ue_cfg_t generate_default_ue_cfg() { srsenb::sched_interface::ue_cfg_t ue_cfg = {}; @@ -98,7 +129,7 @@ srsenb::sched_interface::ue_cfg_t generate_default_ue_cfg() } /***************************** - * Event Setup Helpers + * Event Types ****************************/ // Struct that represents all the events that take place in a TTI @@ -119,11 +150,12 @@ struct tti_ev { }; struct sim_sched_args { - uint32_t nof_ttis; + uint32_t start_tti = 0; float P_retx; srsenb::sched_interface::ue_cfg_t ue_cfg; srsenb::sched_interface::ue_bearer_cfg_t bearer_cfg; std::vector cell_cfg; + srslte::log* sim_log = nullptr; }; // generate all events up front