diff --git a/srsgnb/hdr/stack/mac/sched_nr_pdsch.h b/srsgnb/hdr/stack/mac/sched_nr_pdsch.h index 2e6b9ec0a..60cee7130 100644 --- a/srsgnb/hdr/stack/mac/sched_nr_pdsch.h +++ b/srsgnb/hdr/stack/mac/sched_nr_pdsch.h @@ -20,18 +20,20 @@ namespace srsenb { namespace sched_nr_impl { +using pdsch_alloc_result = srsran::expected; + class pdsch_allocator { public: pdsch_allocator(const bwp_params_t& cfg_, uint32_t sl_index, pdsch_list_t& pdsch_lst); /// Get available RBGs for allocation - rbg_bitmap available_rbgs(uint32_t ss_id, srsran_dci_format_nr_t dci_fmt) const + rbg_bitmap occupied_rbgs(uint32_t ss_id, srsran_dci_format_nr_t dci_fmt) const { return (dl_prbs | bwp_cfg.coreset_prb_limits(ss_id, dci_fmt)).rbgs(); } /// Get available PRBs for allocation - prb_bitmap available_prbs(uint32_t ss_id, srsran_dci_format_nr_t dci_fmt) const + prb_bitmap occupied_prbs(uint32_t ss_id, srsran_dci_format_nr_t dci_fmt) const { return (dl_prbs | bwp_cfg.coreset_prb_limits(ss_id, dci_fmt)).prbs(); } @@ -48,19 +50,19 @@ public: * @param dci_ctx[in] PDCCH DL DCI context information * @param ss_id[in] SearchSpaceId used for allocation * @param grant[in] PRBs used for the grant - * @param pdcch[out] PDCCH where frequency_assignment and time_assignment get stored. + * @param pdcch[out] DCI where frequency_assignment and time_assignment get stored. * @return pdsch_t object pointer in case of success. alloc_result error code in case of failure */ - srsran::expected - alloc_pdsch(const srsran_dci_ctx_t& dci_ctx, uint32_t ss_id, const prb_grant& grant, pdcch_dl_t& pdcch); + pdsch_alloc_result + alloc_pdsch(const srsran_dci_ctx_t& dci_ctx, uint32_t ss_id, const prb_grant& grant, srsran_dci_dl_nr_t& dci); /** * @brief Allocates PDSCH grant without verifying for collisions. Useful to avoid redundant is_grant_valid(...) calls * @param dci_ctx[in] PDCCH DL DCI context information * @param grant[in] PRBs used for the grant - * @param pdcch[out] PDCCH where frequency and time assignment get stored. + * @param pdcch[out] DCI where frequency and time assignment get stored. */ - pdsch_t& alloc_pdsch_unchecked(const srsran_dci_ctx_t& dci_ctx, const prb_grant& grant, pdcch_dl_t& pdcch); + pdsch_t& alloc_pdsch_unchecked(const srsran_dci_ctx_t& dci_ctx, const prb_grant& grant, srsran_dci_dl_nr_t& dci); void cancel_last_pdsch(); diff --git a/srsgnb/src/stack/mac/sched_nr_bwp.cc b/srsgnb/src/stack/mac/sched_nr_bwp.cc index c04b2fa57..89736096c 100644 --- a/srsgnb/src/stack/mac/sched_nr_bwp.cc +++ b/srsgnb/src/stack/mac/sched_nr_bwp.cc @@ -25,7 +25,7 @@ alloc_result ra_sched::allocate_pending_rar(bwp_slot_allocator& slot_grid, const pending_rar_t& rar, uint32_t& nof_grants_alloc) { const uint32_t rar_aggr_level = 2; - prb_bitmap prbs = slot_grid.res_grid()[slot_grid.get_pdcch_tti()].pdschs.available_prbs( + prb_bitmap prbs = slot_grid.res_grid()[slot_grid.get_pdcch_tti()].pdschs.occupied_prbs( bwp_cfg->cfg.pdcch.ra_search_space.id, srsran_dci_format_nr_1_0); alloc_result ret = alloc_result::other_cause; diff --git a/srsgnb/src/stack/mac/sched_nr_grant_allocator.cc b/srsgnb/src/stack/mac/sched_nr_grant_allocator.cc index fddd55eb2..2722c2a93 100644 --- a/srsgnb/src/stack/mac/sched_nr_grant_allocator.cc +++ b/srsgnb/src/stack/mac/sched_nr_grant_allocator.cc @@ -138,16 +138,16 @@ alloc_result bwp_slot_allocator::alloc_si(uint32_t aggr_idx, logger.warning("SCHED: Cannot allocate SIB1 due to lack of PDCCH space."); return pdcch_result.error(); } - pdcch_dl_t* pdcch = pdcch_result.value(); + pdcch_dl_t pdcch = *pdcch_result.value(); // SI allocation successful. // Allocate PDSCH - pdsch_t& pdsch = bwp_pdcch_slot.pdschs.alloc_pdsch_unchecked(pdcch->dci.ctx, prbs, *pdcch); + pdsch_t& pdsch = bwp_pdcch_slot.pdschs.alloc_pdsch_unchecked(pdcch.dci.ctx, prbs, pdcch.dci); // Generate DCI for SIB - pdcch->dci_cfg.coreset0_bw = srsran_coreset_get_bw(&cfg.cfg.pdcch.coreset[0]); - if (not fill_dci_sib(prbs, si_idx, si_ntx, *bwp_grid.cfg, pdcch->dci)) { + pdcch.dci_cfg.coreset0_bw = srsran_coreset_get_bw(&cfg.cfg.pdcch.coreset[0]); + if (not fill_dci_sib(prbs, si_idx, si_ntx, *bwp_grid.cfg, pdcch.dci)) { // Cancel on-going PDCCH allocation bwp_pdcch_slot.pdcchs.cancel_last_pdcch(); return alloc_result::invalid_coderate; @@ -157,7 +157,7 @@ alloc_result bwp_slot_allocator::alloc_si(uint32_t aggr_idx, srsran_slot_cfg_t slot_cfg; slot_cfg.idx = pdcch_slot.to_uint(); int code = srsran_ra_dl_dci_to_grant_nr( - &cfg.cell_cfg.carrier, &slot_cfg, &cfg.cfg.pdsch, &pdcch->dci, &pdsch.sch, &pdsch.sch.grant); + &cfg.cell_cfg.carrier, &slot_cfg, &cfg.cfg.pdsch, &pdcch.dci, &pdsch.sch, &pdsch.sch.grant); if (code != SRSRAN_SUCCESS) { logger.warning("Error generating SIB PDSCH grant."); bwp_pdcch_slot.pdcchs.cancel_last_pdcch(); @@ -239,7 +239,7 @@ alloc_result bwp_slot_allocator::alloc_rar_and_msg3(uint16_t pdcch->dci_cfg = phy_cfg.get_dci_cfg(); // Allocate PDSCH - pdsch_t& pdsch = bwp_pdcch_slot.pdschs.alloc_pdsch_unchecked(pdcch->dci.ctx, interv, *pdcch); + pdsch_t& pdsch = bwp_pdcch_slot.pdschs.alloc_pdsch_unchecked(pdcch->dci.ctx, interv, pdcch->dci); // Generate DCI for RAR with given RA-RNTI if (not fill_dci_rar(interv, ra_rnti, *bwp_grid.cfg, pdcch->dci)) { @@ -345,7 +345,7 @@ alloc_result bwp_slot_allocator::alloc_pdsch(slot_ue& ue, prb_grant dl_grant) pdcch_dl_t& pdcch = *pdcch_result.value(); // Allocate PDSCH - pdsch_t& pdsch = bwp_pdcch_slot.pdschs.alloc_pdsch_unchecked(pdcch.dci.ctx, dl_grant, pdcch); + pdsch_t& pdsch = bwp_pdcch_slot.pdschs.alloc_pdsch_unchecked(pdcch.dci.ctx, dl_grant, pdcch.dci); // Allocate HARQ int mcs = ue->fixed_pdsch_mcs(); diff --git a/srsgnb/src/stack/mac/sched_nr_pdsch.cc b/srsgnb/src/stack/mac/sched_nr_pdsch.cc index 3b374be0c..b5e0300e3 100644 --- a/srsgnb/src/stack/mac/sched_nr_pdsch.cc +++ b/srsgnb/src/stack/mac/sched_nr_pdsch.cc @@ -91,19 +91,22 @@ alloc_result pdsch_allocator::is_grant_valid(uint32_t ss_id, return alloc_result::success; } -srsran::expected -pdsch_allocator::alloc_pdsch(const srsran_dci_ctx_t& dci_ctx, uint32_t ss_id, const prb_grant& grant, pdcch_dl_t& pdcch) +srsran::expected pdsch_allocator::alloc_pdsch(const srsran_dci_ctx_t& dci_ctx, + uint32_t ss_id, + const prb_grant& grant, + srsran_dci_dl_nr_t& dci) { alloc_result code = is_grant_valid(ss_id, dci_ctx.format, grant); if (code != alloc_result::success) { return code; } - return {&alloc_pdsch_unchecked(dci_ctx, grant, pdcch)}; + return {&alloc_pdsch_unchecked(dci_ctx, grant, dci)}; } -pdsch_t& -pdsch_allocator::alloc_pdsch_unchecked(const srsran_dci_ctx_t& dci_ctx, const prb_grant& grant, pdcch_dl_t& pdcch) +pdsch_t& pdsch_allocator::alloc_pdsch_unchecked(const srsran_dci_ctx_t& dci_ctx, + const prb_grant& grant, + srsran_dci_dl_nr_t& out_dci) { // Create new PDSCH entry in output PDSCH list pdschs.emplace_back(); @@ -113,9 +116,9 @@ pdsch_allocator::alloc_pdsch_unchecked(const srsran_dci_ctx_t& dci_ctx, const pr dl_prbs |= grant; // Fill DCI with PDSCH freq/time allocation information - pdcch.dci.time_domain_assigment = 0; + out_dci.time_domain_assigment = 0; if (grant.is_alloc_type0()) { - pdcch.dci.freq_domain_assigment = grant.rbgs().to_uint64(); + out_dci.freq_domain_assigment = grant.rbgs().to_uint64(); } else { uint32_t rb_start = grant.prbs().start(), nof_prb = bwp_cfg.nof_prb(); if (SRSRAN_SEARCH_SPACE_IS_COMMON(dci_ctx.ss_type)) { @@ -127,7 +130,7 @@ pdsch_allocator::alloc_pdsch_unchecked(const srsran_dci_ctx_t& dci_ctx, const pr } } srsran_sanity_check(rb_start + grant.prbs().length() <= nof_prb, "Invalid PRB grant"); - pdcch.dci.freq_domain_assigment = srsran_ra_nr_type1_riv(nof_prb, rb_start, grant.prbs().length()); + out_dci.freq_domain_assigment = srsran_ra_nr_type1_riv(nof_prb, rb_start, grant.prbs().length()); } return pdsch; diff --git a/srsgnb/src/stack/mac/sched_nr_signalling.cc b/srsgnb/src/stack/mac/sched_nr_signalling.cc index 9fc545ca1..c0e9f27f3 100644 --- a/srsgnb/src/stack/mac/sched_nr_signalling.cc +++ b/srsgnb/src/stack/mac/sched_nr_signalling.cc @@ -152,7 +152,7 @@ void si_sched::run_slot(bwp_slot_allocator& bwp_alloc) const uint32_t si_aggr_level = 2; const uint32_t ss_id = 0; slot_point sl_pdcch = bwp_alloc.get_pdcch_tti(); - prb_bitmap prbs = bwp_alloc.res_grid()[sl_pdcch].pdschs.available_prbs(ss_id, srsran_dci_format_nr_1_0); + prb_bitmap prbs = bwp_alloc.res_grid()[sl_pdcch].pdschs.occupied_prbs(ss_id, srsran_dci_format_nr_1_0); // Update SI windows uint32_t N = bwp_cfg->slots.size(); diff --git a/srsgnb/src/stack/mac/test/CMakeLists.txt b/srsgnb/src/stack/mac/test/CMakeLists.txt index 7b74e213d..ebe92e9de 100644 --- a/srsgnb/src/stack/mac/test/CMakeLists.txt +++ b/srsgnb/src/stack/mac/test/CMakeLists.txt @@ -30,6 +30,10 @@ add_executable(sched_nr_pdcch_test sched_nr_pdcch_test.cc) target_link_libraries(sched_nr_pdcch_test srsgnb_mac sched_nr_test_suite srsran_common) add_nr_test(sched_nr_pdcch_test sched_nr_pdcch_test) +add_executable(sched_nr_pdsch_test sched_nr_pdsch_test.cc) +target_link_libraries(sched_nr_pdsch_test srsgnb_mac sched_nr_test_suite srsran_common) +add_nr_test(sched_nr_pdsch_test sched_nr_pdsch_test) + add_executable(sched_nr_rar_test sched_nr_rar_test.cc) target_link_libraries(sched_nr_rar_test srsgnb_mac sched_nr_test_suite srsran_common) add_nr_test(sched_nr_rar_test sched_nr_rar_test) diff --git a/srsgnb/src/stack/mac/test/sched_nr_cfg_generators.h b/srsgnb/src/stack/mac/test/sched_nr_cfg_generators.h index b7816a350..216e99040 100644 --- a/srsgnb/src/stack/mac/test/sched_nr_cfg_generators.h +++ b/srsgnb/src/stack/mac/test/sched_nr_cfg_generators.h @@ -126,6 +126,7 @@ inline sched_nr_interface::cell_cfg_t get_default_sa_cell_cfg_common() sched_nr_interface::cell_cfg_t cell_cfg = get_default_cell_cfg(); cell_cfg.bwps[0].pdcch.coreset_present[0] = true; cell_cfg.bwps[0].pdcch.coreset[0] = get_default_coreset0(52); + cell_cfg.bwps[0].pdcch.coreset[0].offset_rb = 1; cell_cfg.bwps[0].pdcch.search_space_present[0] = true; cell_cfg.bwps[0].pdcch.search_space[0] = get_default_search_space0(); cell_cfg.bwps[0].pdcch.coreset_present[1] = false; diff --git a/srsgnb/src/stack/mac/test/sched_nr_pdcch_test.cc b/srsgnb/src/stack/mac/test/sched_nr_pdcch_test.cc index 3c5cbe563..bb38005d1 100644 --- a/srsgnb/src/stack/mac/test/sched_nr_pdcch_test.cc +++ b/srsgnb/src/stack/mac/test/sched_nr_pdcch_test.cc @@ -12,7 +12,6 @@ #include "sched_nr_cfg_generators.h" #include "sched_nr_common_test.h" -#include "srsgnb/hdr/stack/mac/sched_nr_helpers.h" #include "srsgnb/hdr/stack/mac/sched_nr_interface_utils.h" #include "srsgnb/hdr/stack/mac/sched_nr_pdcch.h" #include "srsran/common/test_common.h" diff --git a/srsgnb/src/stack/mac/test/sched_nr_pdsch_test.cc b/srsgnb/src/stack/mac/test/sched_nr_pdsch_test.cc new file mode 100644 index 000000000..8f8f263a6 --- /dev/null +++ b/srsgnb/src/stack/mac/test/sched_nr_pdsch_test.cc @@ -0,0 +1,375 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#include "sched_nr_cfg_generators.h" +#include "srsgnb/hdr/stack/mac/sched_nr_interface_utils.h" +#include "srsgnb/hdr/stack/mac/sched_nr_pdsch.h" +#include "srsran/common/test_common.h" +extern "C" { +#include "srsran/phy/common/sliv.h" +} + +namespace srsenb { + +using namespace sched_nr_impl; + +sched_nr_interface::cell_cfg_t get_cell_cfg() +{ + sched_nr_impl::cell_cfg_t cell_cfg = get_default_sa_cell_cfg_common(); + cell_cfg.bwps[0].pdcch.search_space_present[2] = true; + cell_cfg.bwps[0].pdcch.search_space[2] = get_default_ue_specific_search_space(2, 2); + cell_cfg.bwps[0].pdcch.coreset_present[2] = true; + cell_cfg.bwps[0].pdcch.coreset[2] = get_default_ue_specific_coreset(2, cell_cfg.carrier.pci); + return cell_cfg; +} + +sched_nr_interface::ue_cfg_t get_ue_cfg(const sched_nr_interface::cell_cfg_t& cell_cfg) +{ + ue_cfg_t uecfg = get_rach_ue_cfg(0); + uecfg.phy_cfg = get_common_ue_phy_cfg(cell_cfg); + uecfg.phy_cfg.pdcch = cell_cfg.bwps[0].pdcch; // Starts with UE-specific PDCCH + return uecfg; +} + +srsran_dci_ctx_t generate_dci_ctx(const srsran_pdcch_cfg_nr_t& pdcch, + uint32_t ss_id, + srsran_rnti_type_t rnti_type, + uint16_t rnti, + srsran_dci_format_nr_t dci_fmt = srsran_dci_format_nr_1_0) +{ + const srsran_search_space_t& ss = pdcch.search_space[ss_id]; + const srsran_coreset_t& cs = pdcch.coreset[ss.coreset_id]; + + srsran_dci_ctx_t ctx; + ctx.location = {2, 4}; + ctx.ss_type = ss.type; + ctx.coreset_id = ss.coreset_id; + ctx.coreset_start_rb = srsran_coreset_start_rb(&cs); + ctx.rnti_type = rnti_type; + ctx.format = dci_fmt; + ctx.rnti = rnti; + return ctx; +} + +void test_dci_freq_assignment(const bwp_params_t& bwp_params, prb_interval grant, const pdcch_dl_t& pdcch) +{ + // Compute BWP PRB limits + prb_interval lims{0, bwp_params.nof_prb()}; + if (SRSRAN_SEARCH_SPACE_IS_COMMON(pdcch.dci.ctx.ss_type) and pdcch.dci.ctx.format == srsran_dci_format_nr_1_0) { + uint32_t s = pdcch.dci.ctx.coreset_start_rb; + lims = prb_interval{s, pdcch.dci.ctx.coreset_id == 0 ? s + bwp_params.coreset_bw(0) : bwp_params.nof_prb()}; + } + + // RB indexing should start from the first PRB of CORESET + uint32_t expected_freq_assignment = + srsran_ra_nr_type1_riv(lims.length(), grant.start() - lims.start(), grant.length()); + TESTASSERT_EQ(expected_freq_assignment, pdcch.dci.freq_domain_assigment); + + uint32_t st, len; + srsran_sliv_to_s_and_l(lims.length(), pdcch.dci.freq_domain_assigment, &st, &len); + prb_interval allocated_prbs{st + lims.start(), st + lims.start() + len}; + TESTASSERT(allocated_prbs == grant); +} + +void test_si_sched() +{ + srsran::test_delimit_logger delimiter{"Test PDSCH SI Allocation"}; + + static const uint32_t ss_id = 0; + + // Create Cell and UE configs + sched_nr_impl::cell_cfg_t cell_cfg = get_cell_cfg(); + sched_nr_interface::sched_args_t sched_args; + bwp_params_t bwp_params{cell_cfg, sched_args, 0, 0}; + + pdsch_list_t pdschs; + pdsch_alloc_result alloc_res; + + pdsch_allocator pdsch_sched(bwp_params, 0, pdschs); + + pdcch_dl_t pdcch; + pdcch.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, ss_id, srsran_rnti_type_si, SRSRAN_SIRNTI); + + uint32_t min_prb = pdcch.dci.ctx.coreset_start_rb; + uint32_t max_prb = min_prb + bwp_params.coreset_bw(0); + + std::array grant_list = { + prb_interval{2, 4}, prb_interval{min_prb, max_prb}, prb_interval{0, bwp_params.nof_prb()}}; + + for (uint32_t i = 0; i < grant_list.size(); ++i) { + pdsch_sched.reset(); + TESTASSERT_EQ(0, pdschs.size()); + + prb_interval grant = grant_list[i]; + + bool success_expected = grant.start() >= min_prb and grant.stop() <= max_prb; + + alloc_result check_ret = pdsch_sched.is_grant_valid(ss_id, srsran_dci_format_nr_1_0, grant); + prb_bitmap avail_prbs = pdsch_sched.occupied_prbs(ss_id, srsran_dci_format_nr_1_0); + TESTASSERT_EQ((int)min_prb, avail_prbs.find_lowest(0, avail_prbs.size(), false)); + TESTASSERT_EQ((int)max_prb, avail_prbs.find_lowest(min_prb, avail_prbs.size(), true)); + + printf("Attempt %d should be %ssuccessful\n", i, success_expected ? "" : "un"); + alloc_res = pdsch_sched.alloc_pdsch(pdcch.dci.ctx, ss_id, grant, pdcch.dci); + if (success_expected) { + // SIB1 allocation doesnt go outside CORESET#0 BW + TESTASSERT(alloc_res.has_value()); + TESTASSERT_EQ(1, pdschs.size()); + TESTASSERT(&pdschs.back() == alloc_res.value()); + TESTASSERT_EQ(0, pdcch.dci.time_domain_assigment); + + TESTASSERT(not avail_prbs.any(grant.start(), grant.stop())); + + test_dci_freq_assignment(bwp_params, grant, pdcch); + } else { + TESTASSERT(alloc_res.is_error()); + TESTASSERT(check_ret == alloc_res.error()); + TESTASSERT_EQ(0, pdschs.size()); + TESTASSERT(avail_prbs.any(grant.start(), grant.stop())); + } + } +} + +void test_rar_sched() +{ + srsran::test_delimit_logger delimiter{"Test PDSCH RAR Allocation"}; + static const uint32_t ss_id = 1; + + // Create Cell and UE configs + sched_nr_impl::cell_cfg_t cell_cfg = get_cell_cfg(); + sched_nr_interface::sched_args_t sched_args; + bwp_params_t bwp_params{cell_cfg, sched_args, 0, 0}; + + pdsch_list_t pdschs; + pdsch_alloc_result alloc_res; + + pdsch_allocator pdsch_sched(bwp_params, 0, pdschs); + + pdcch_dl_t pdcch; + pdcch.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, ss_id, srsran_rnti_type_ra, 0x2); + + uint32_t min_prb = pdcch.dci.ctx.coreset_start_rb; + uint32_t max_prb = min_prb + bwp_params.coreset_bw(0); + + std::array grant_list = { + prb_interval{2, 4}, prb_interval{min_prb, max_prb}, prb_interval{0, bwp_params.nof_prb()}}; + + for (uint32_t i = 0; i < grant_list.size(); ++i) { + pdsch_sched.reset(); + TESTASSERT_EQ(0, pdschs.size()); + + prb_interval grant = grant_list[i]; + + bool success_expected = grant.start() >= min_prb and grant.stop() <= max_prb; + + alloc_result check_ret = pdsch_sched.is_grant_valid(ss_id, srsran_dci_format_nr_1_0, grant); + prb_bitmap avail_prbs = pdsch_sched.occupied_prbs(ss_id, srsran_dci_format_nr_1_0); + TESTASSERT_EQ((int)min_prb, avail_prbs.find_lowest(0, avail_prbs.size(), false)); + TESTASSERT_EQ((int)max_prb, avail_prbs.find_lowest(min_prb, avail_prbs.size(), true)); + + printf("Attempt %d should be %ssuccessful\n", i, success_expected ? "" : "un"); + alloc_res = pdsch_sched.alloc_pdsch(pdcch.dci.ctx, ss_id, grant, pdcch.dci); + if (success_expected) { + // SIB1 allocation doesnt go outside CORESET#0 BW + TESTASSERT(alloc_res.has_value()); + TESTASSERT_EQ(1, pdschs.size()); + TESTASSERT(&pdschs.back() == alloc_res.value()); + TESTASSERT_EQ(0, pdcch.dci.time_domain_assigment); + + TESTASSERT(not avail_prbs.any(grant.start(), grant.stop())); + + test_dci_freq_assignment(bwp_params, grant, pdcch); + } else { + TESTASSERT(alloc_res.is_error()); + TESTASSERT(check_ret == alloc_res.error()); + TESTASSERT_EQ(0, pdschs.size()); + TESTASSERT(avail_prbs.any(grant.start(), grant.stop())); + } + } +} + +void test_ue_sched() +{ + srsran::test_delimit_logger delimiter{"Test PDSCH UE Allocation"}; + + // Create Cell and UE configs + sched_nr_impl::cell_cfg_t cell_cfg = get_cell_cfg(); + sched_nr_impl::ue_cfg_t uecfg = get_ue_cfg(cell_cfg); + sched_nr_interface::sched_args_t sched_args; + bwp_params_t bwp_params{cell_cfg, sched_args, 0, 0}; + ue_carrier_params_t ue_cc{0x4601, bwp_params, uecfg}; + + pdsch_list_t pdschs; + pdsch_alloc_result alloc_res; + + pdsch_allocator pdsch_sched(bwp_params, 0, pdschs); + + pdcch_dl_t pdcch_common, pdcch_ue; + pdcch_common.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, 1, srsran_rnti_type_c, 0x4601); + pdcch_ue.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, 2, srsran_rnti_type_c, 0x4601); + + prb_interval lims_common{pdcch_common.dci.ctx.coreset_start_rb, + pdcch_common.dci.ctx.coreset_start_rb + bwp_params.coreset_bw(0)}; + prb_interval lims_ue{0, bwp_params.nof_prb()}; + + std::array, 4> grant_list = {std::make_pair(1, prb_interval{2, 4}), + std::make_pair(1, lims_common), + std::make_pair(1, lims_ue), + std::make_pair(2, lims_common)}; + + for (uint32_t i = 0; i < grant_list.size(); ++i) { + pdsch_sched.reset(); + TESTASSERT_EQ(0, pdschs.size()); + + auto g = grant_list[i]; + uint32_t ss_id = g.first; + prb_interval grant = g.second; + prb_interval lims = ss_id == 1 ? lims_common : lims_ue; + pdcch_dl_t& pdcch = ss_id == 1 ? pdcch_common : pdcch_ue; + + bool success_expected = grant.start() >= lims.start() and grant.stop() <= lims.stop(); + + alloc_result check_ret = pdsch_sched.is_grant_valid(ss_id, srsran_dci_format_nr_1_0, grant); + prb_bitmap avail_prbs = pdsch_sched.occupied_prbs(ss_id, srsran_dci_format_nr_1_0); + int pos = avail_prbs.find_lowest(0, avail_prbs.size(), false); + TESTASSERT_EQ((int)lims.start(), pos); + pos = avail_prbs.find_lowest(lims.start(), avail_prbs.size(), true); + TESTASSERT_EQ((int)lims.stop(), (pos < 0 ? (int)avail_prbs.size() : pos)); + + printf("Attempt %d should be %ssuccessful\n", i, success_expected ? "" : "un"); + alloc_res = pdsch_sched.alloc_pdsch(pdcch.dci.ctx, ss_id, grant, pdcch.dci); + if (success_expected) { + // SIB1 allocation doesnt go outside CORESET#0 BW + TESTASSERT(alloc_res.has_value()); + TESTASSERT_EQ(1, pdschs.size()); + TESTASSERT(&pdschs.back() == alloc_res.value()); + TESTASSERT_EQ(0, pdcch.dci.time_domain_assigment); + + TESTASSERT(not avail_prbs.any(grant.start(), grant.stop())); + + test_dci_freq_assignment(bwp_params, grant, pdcch); + } else { + TESTASSERT(alloc_res.is_error()); + TESTASSERT(check_ret == alloc_res.error()); + TESTASSERT_EQ(0, pdschs.size()); + TESTASSERT(avail_prbs.any(grant.start(), grant.stop())); + } + } +} + +void test_multi_sched() +{ + srsran::test_delimit_logger delimiter{"Test Multiple PDSCH Allocations"}; + + // Create Cell and UE configs + sched_nr_impl::cell_cfg_t cell_cfg = get_cell_cfg(); + sched_nr_impl::ue_cfg_t uecfg = get_ue_cfg(cell_cfg); + sched_nr_interface::sched_args_t sched_args; + bwp_params_t bwp_params{cell_cfg, sched_args, 0, 0}; + ue_carrier_params_t ue_cc{0x4601, bwp_params, uecfg}; + ue_carrier_params_t ue_cc2{0x4602, bwp_params, uecfg}; + + pdsch_list_t pdschs; + pdsch_alloc_result alloc_res; + + pdsch_allocator pdsch_sched(bwp_params, 0, pdschs); + + pdcch_dl_t pdcch_sib, pdcch_common, pdcch_ue; + pdcch_sib.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, 0, srsran_rnti_type_si, SRSRAN_SIRNTI); + pdcch_common.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, 1, srsran_rnti_type_c, 0x4601); + pdcch_ue.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, 2, srsran_rnti_type_c, 0x4602); + + // Allocate SIB1 + uint32_t ss_id = 0; + pdcch_dl_t* pdcch = &pdcch_sib; + prb_bitmap used_prbs = pdsch_sched.occupied_prbs(ss_id, srsran_dci_format_nr_1_0); + fmt::print("No allocations yet. Occupied PRBs for common SearchSpace: {:b}\n", used_prbs); + uint32_t sib1_grant_size = 4; + prb_bitmap sib_prbs = ~used_prbs; + int first_prb = sib_prbs.find_lowest(0, sib_prbs.size(), true); + prb_interval sib_grant{(uint32_t)first_prb, sib1_grant_size}; + TESTASSERT_EQ(alloc_result::success, pdsch_sched.is_grant_valid(ss_id, srsran_dci_format_nr_1_0, sib_grant)); + alloc_res = pdsch_sched.alloc_pdsch(pdcch->dci.ctx, ss_id, sib_grant, pdcch->dci); + TESTASSERT(alloc_res.has_value()); + test_dci_freq_assignment(bwp_params, sib_grant, *pdcch); + prb_bitmap used_prbs_sib1 = pdsch_sched.occupied_prbs(ss_id, srsran_dci_format_nr_1_0); + TESTASSERT_EQ(used_prbs_sib1.count(), used_prbs.count() + sib_grant.length()); + TESTASSERT_EQ(alloc_result::sch_collision, pdsch_sched.is_grant_valid(ss_id, srsran_dci_format_nr_1_0, sib_grant)); + + prb_bitmap last_prb_bitmap(used_prbs.size()); + last_prb_bitmap.fill(sib_grant.start(), sib_grant.stop()); + fmt::print("SIB1 allocated. Occupied PRBs:\n{:b} -> {:b}\n", last_prb_bitmap, used_prbs_sib1); + + // Allocate UE in common SearchSpace + ss_id = 1; + pdcch = &pdcch_common; + prb_bitmap ue_prbs = ~used_prbs_sib1; + first_prb = ue_prbs.find_lowest(0, ue_prbs.size(), true); + uint32_t ue_grant_size = 10; + prb_interval ue_grant{(uint32_t)first_prb, ue_grant_size}; + TESTASSERT_EQ(alloc_result::success, pdsch_sched.is_grant_valid(ss_id, srsran_dci_format_nr_1_0, ue_grant)); + alloc_res = pdsch_sched.alloc_pdsch(pdcch->dci.ctx, ss_id, ue_grant, pdcch->dci); + TESTASSERT(alloc_res.has_value()); + test_dci_freq_assignment(bwp_params, ue_grant, *pdcch); + prb_bitmap used_prbs_ue = pdsch_sched.occupied_prbs(ss_id, srsran_dci_format_nr_1_0); + TESTASSERT_EQ(used_prbs_ue.count(), used_prbs_sib1.count() + ue_grant.length()); + TESTASSERT_EQ(alloc_result::sch_collision, pdsch_sched.is_grant_valid(ss_id, srsran_dci_format_nr_1_0, ue_grant)); + + last_prb_bitmap.reset(); + last_prb_bitmap.fill(ue_grant.start(), ue_grant.stop()); + fmt::print("C-RNTI allocated in Common SearchSpace. Occupied PRBs:\n{:b} -> {:b}\n", last_prb_bitmap, used_prbs_ue); + + // Allocate UE in UE SearchSpace + ss_id = 2; + pdcch = &pdcch_ue; + used_prbs = pdsch_sched.occupied_prbs(ss_id, srsran_dci_format_nr_1_0); + prb_interval ue_grant2 = find_empty_interval_of_length(used_prbs, used_prbs_ue.size(), 0); + TESTASSERT_EQ(bwp_params.nof_prb(), ue_grant2.stop()); + TESTASSERT_EQ(alloc_result::success, pdsch_sched.is_grant_valid(ss_id, srsran_dci_format_nr_1_0, ue_grant2)); + alloc_res = pdsch_sched.alloc_pdsch(pdcch->dci.ctx, ss_id, ue_grant2, pdcch->dci); + TESTASSERT(alloc_res.has_value()); + test_dci_freq_assignment(bwp_params, ue_grant2, *pdcch); + prb_bitmap used_prbs_ue2 = pdsch_sched.occupied_prbs(ss_id, srsran_dci_format_nr_1_0); + TESTASSERT_EQ(used_prbs_ue2.count(), used_prbs.count() + ue_grant2.length()); + TESTASSERT_EQ(alloc_result::sch_collision, pdsch_sched.is_grant_valid(ss_id, srsran_dci_format_nr_1_0, ue_grant2)); + + last_prb_bitmap.reset(); + last_prb_bitmap.fill(ue_grant2.start(), ue_grant2.stop()); + fmt::print("C-RNTI allocated in UE-dedicated common SearchSpace. Occupied PRBs:\n{:b} -> {:b}\n", + last_prb_bitmap, + used_prbs_ue2); + + TESTASSERT_EQ(3, pdschs.size()); + pdsch_sched.reset(); + TESTASSERT_EQ(0, pdschs.size()); + TESTASSERT_EQ(0, pdsch_sched.occupied_prbs(2, srsran_dci_format_nr_1_0).count()); +} + +} // namespace srsenb + +int main() +{ + auto& test_logger = srslog::fetch_basic_logger("TEST"); + test_logger.set_level(srslog::basic_levels::info); + auto& mac_nr_logger = srslog::fetch_basic_logger("MAC-NR"); + mac_nr_logger.set_level(srslog::basic_levels::debug); + auto& pool_logger = srslog::fetch_basic_logger("POOL"); + pool_logger.set_level(srslog::basic_levels::debug); + + // Start the log backend. + srslog::init(); + + srsenb::test_si_sched(); + srsenb::test_rar_sched(); + srsenb::test_ue_sched(); + srsenb::test_multi_sched(); +} \ No newline at end of file