diff --git a/srsgnb/hdr/stack/mac/sched_nr_helpers.h b/srsgnb/hdr/stack/mac/sched_nr_helpers.h index bcb7698fd..95f159014 100644 --- a/srsgnb/hdr/stack/mac/sched_nr_helpers.h +++ b/srsgnb/hdr/stack/mac/sched_nr_helpers.h @@ -24,6 +24,30 @@ class slot_ue; class ul_harq_proc; struct bwp_res_grid; +/// Helper function to verify if RNTI type can be placed in specified search space +/// Based on 38.213, Section 10.1 +inline bool is_rnti_type_valid_in_search_space(srsran_rnti_type_t rnti_type, srsran_search_space_type_t ss_type) +{ + switch (ss_type) { + case srsran_search_space_type_common_0: // fall-through + case srsran_search_space_type_common_0A: // Other SIBs + return rnti_type == srsran_rnti_type_si; + case srsran_search_space_type_common_1: + return rnti_type == srsran_rnti_type_ra or rnti_type == srsran_rnti_type_tc or + /* in case of Pcell -> */ rnti_type == srsran_rnti_type_c; + case srsran_search_space_type_common_2: + return rnti_type == srsran_rnti_type_p; + case srsran_search_space_type_common_3: + return rnti_type == srsran_rnti_type_c or rnti_type == srsran_rnti_type_ra; // TODO: Fix + case srsran_search_space_type_ue: + return rnti_type == srsran_rnti_type_c or rnti_type == srsran_rnti_type_cs or + rnti_type == srsran_rnti_type_sp_csi; + default: + break; + } + return false; +} + /// In case of Common SearchSpace, not all PRBs might be available void reduce_to_dl_coreset_bw(const bwp_params_t& bwp_cfg, uint32_t ss_id, diff --git a/srsgnb/hdr/stack/mac/sched_nr_interface_utils.h b/srsgnb/hdr/stack/mac/sched_nr_interface_utils.h index 7fd5d2377..f07135b8f 100644 --- a/srsgnb/hdr/stack/mac/sched_nr_interface_utils.h +++ b/srsgnb/hdr/stack/mac/sched_nr_interface_utils.h @@ -18,7 +18,7 @@ namespace srsenb { -// Helpers to handle PHY struct types +//////////////////////////////////// Search Space Helpers //////////////////////////////////////////// /// Get a range of active search spaces in a PDCCH configuration inline srsran::split_optional_span view_active_search_spaces(srsran_pdcch_cfg_nr_t& pdcch) @@ -31,6 +31,30 @@ view_active_search_spaces(const srsran_pdcch_cfg_nr_t& pdcch) return srsran::split_optional_span{pdcch.search_space, pdcch.search_space_present}; } +//////////////////////////////////// CORESET Helpers //////////////////////////////////////////// + +/// Get a range of active coresets in a PDCCH configuration +inline srsran::split_optional_span view_active_coresets(srsran_pdcch_cfg_nr_t& pdcch) +{ + return srsran::split_optional_span{pdcch.coreset, pdcch.coreset_present}; +} +inline srsran::split_optional_span view_active_coresets(const srsran_pdcch_cfg_nr_t& pdcch) +{ + return srsran::split_optional_span{pdcch.coreset, pdcch.coreset_present}; +} + +/// Get number of CCEs available in CORESET for PDCCH +uint32_t coreset_nof_cces(const srsran_coreset_t& coreset); + +//////////////////////////////////// Sched Output Helpers //////////////////////////////////////////// + +inline bool operator==(srsran_dci_location_t lhs, srsran_dci_location_t rhs) +{ + return lhs.ncce == rhs.ncce and lhs.L == rhs.L; +} + +//////////////////////////////////// UE configuration Helpers //////////////////////////////////////////// + srsran::phy_cfg_nr_t get_common_ue_phy_cfg(const sched_nr_interface::cell_cfg_t& cfg); } // namespace srsenb diff --git a/srsgnb/hdr/stack/mac/sched_nr_pdcch.h b/srsgnb/hdr/stack/mac/sched_nr_pdcch.h index 9089117ca..5c2c00611 100644 --- a/srsgnb/hdr/stack/mac/sched_nr_pdcch.h +++ b/srsgnb/hdr/stack/mac/sched_nr_pdcch.h @@ -28,19 +28,15 @@ using coreset_bitmap = srsran::bounded_bitset dci_list; - pdcch_dl_list_t& pdcch_dl_list; - pdcch_ul_list_t& pdcch_ul_list; // DFS decision tree of PDCCH grants struct tree_node { @@ -111,45 +100,83 @@ public: void reset(); /** - * Allocates RE space for DL DCI in PDCCH, avoiding in the process collisions with other PDCCH allocations - * @param rnti_type type of RNTI (e.g. SI, RA, C, TC) + * Allocates RE space for RAR DCI in PDCCH, avoiding in the process collisions with other PDCCH allocations + * Fills DCI context with RAR PDCCH allocation information + * @param ra_rnti RA-RNTI of RAR allocation + * @param aggr_idx Aggregation level index (0..4) + * @return PDCCH object with dci context filled if the allocation was successful. nullptr otherwise + */ + pdcch_dl_t* alloc_rar_pdcch(uint16_t ra_rnti, uint32_t aggr_idx); + + /** + * Allocates RE space for SI DCI in PDCCH, avoiding in the process collisions with other PDCCH allocations + * Fills DCI context with SI PDCCH allocation information + * @param ss_id Search space ID + * @param aggr_idx Aggregation level index (0..4) + * @return PDCCH object with dci context filled if the allocation was successful. nullptr otherwise + */ + pdcch_dl_t* alloc_si_pdcch(uint32_t ss_id, uint32_t aggr_idx); + + /** + * Allocates RE space for UE DL DCI in PDCCH, avoiding in the process collisions with other PDCCH allocations + * Fills DCI context with PDCCH allocation information + * @param rnti_type type of UE RNTI (e.g. C, TC) * @param ss_id Search space ID * @param aggr_idx Aggregation level index (0..4) - * @param user UE object or null in case of broadcast/RAR/paging allocation - * @return pdcch object if the allocation was successful + * @param user UE object parameters + * @return PDCCH object with dci context filled if the allocation was successful. nullptr otherwise */ - pdcch_dl_t* alloc_dl_pdcch(srsran_rnti_type_t rnti_type, - uint32_t ss_id, - uint32_t aggr_idx, - const ue_carrier_params_t* user = nullptr); + pdcch_dl_t* + alloc_dl_pdcch(srsran_rnti_type_t rnti_type, uint32_t ss_id, uint32_t aggr_idx, const ue_carrier_params_t& user); /** * Allocates RE space for UL DCI in PDCCH, avoiding in the process collisions with other PDCCH allocations + * Fills DCI context with PDCCH allocation information * @param ss_id Search space ID * @param aggr_idx Aggregation level index (0..4) * @param user UE object parameters - * @return pdcch object if the allocation was successful + * @return PDCCH object with dci context filled if the allocation was successful. nullptr otherwise */ - pdcch_ul_t* alloc_ul_pdcch(uint32_t ss_id, uint32_t aggr_idx, const ue_carrier_params_t* user); + pdcch_ul_t* alloc_ul_pdcch(uint32_t ss_id, uint32_t aggr_idx, const ue_carrier_params_t& user); /** * Cancel and remove last PDCCH allocation - * @param ss_id Search space ID */ - void rem_last_pdcch(uint32_t ss_id); + void rem_last_pdcch(srsran_dci_ctx_t& dci_ctx_to_rem); /// Returns the number of PDCCH allocations made in the slot uint32_t nof_allocations() const; + /// Number of CCEs in given coreset + uint32_t nof_cces(uint32_t coreset_id) const; + private: using slot_coreset_list = srsran::optional_array; + pdcch_dl_t* alloc_dl_pdcch_common(srsran_rnti_type_t rnti_type, + uint16_t rnti, + uint32_t ss_id, + uint32_t aggr_idx, + srsran_dci_format_nr_t dci_fmt, + const ue_carrier_params_t* user = nullptr); + + /// Helper function to verify valid inputs bool check_args_valid(srsran_rnti_type_t rnti_type, + uint16_t rnti, uint32_t ss_id, uint32_t aggr_idx, + srsran_dci_format_nr_t dci_fmt, const ue_carrier_params_t* user, bool is_dl) const; + /// Fill DCI context of allocated PDCCH + void fill_dci_ctx_common(srsran_dci_ctx_t& dci, + srsran_rnti_type_t rnti_type, + uint16_t rnti, + const srsran_search_space_t& ss, + srsran_dci_format_nr_t dci_fmt, + const ue_carrier_params_t* ue); + // args const bwp_params_t& bwp_cfg; srslog::basic_logger& logger; diff --git a/srsgnb/src/stack/mac/sched_nr_grant_allocator.cc b/srsgnb/src/stack/mac/sched_nr_grant_allocator.cc index f13d50b6c..7f47b12a4 100644 --- a/srsgnb/src/stack/mac/sched_nr_grant_allocator.cc +++ b/srsgnb/src/stack/mac/sched_nr_grant_allocator.cc @@ -131,7 +131,7 @@ alloc_result bwp_slot_allocator::alloc_si(uint32_t aggr_idx, } const uint32_t ss_id = 0; - pdcch_dl_t* pdcch = bwp_pdcch_slot.pdcchs.alloc_dl_pdcch(srsran_rnti_type_si, ss_id, aggr_idx); + pdcch_dl_t* pdcch = bwp_pdcch_slot.pdcchs.alloc_si_pdcch(ss_id, aggr_idx); if (pdcch == nullptr) { logger.warning("SCHED: Cannot allocate SIB1 due to lack of PDCCH space."); return alloc_result::no_cch_space; @@ -143,7 +143,7 @@ alloc_result bwp_slot_allocator::alloc_si(uint32_t aggr_idx, 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.rem_last_pdcch(ss_id); + bwp_pdcch_slot.pdcchs.rem_last_pdcch(pdcch->dci.ctx); return alloc_result::invalid_coderate; } @@ -156,7 +156,7 @@ alloc_result bwp_slot_allocator::alloc_si(uint32_t aggr_idx, &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.rem_last_pdcch(ss_id); + bwp_pdcch_slot.pdcchs.rem_last_pdcch(pdcch->dci.ctx); bwp_pdcch_slot.dl.phy.pdsch.pop_back(); return alloc_result::other_cause; } @@ -220,9 +220,7 @@ alloc_result bwp_slot_allocator::alloc_rar_and_msg3(uint16_t } // Find PDCCH position - const uint32_t coreset_id = cfg.cfg.pdcch.ra_search_space.coreset_id; - const uint32_t search_space_id = cfg.cfg.pdcch.ra_search_space.id; - pdcch_dl_t* pdcch = bwp_pdcch_slot.pdcchs.alloc_dl_pdcch(srsran_rnti_type_ra, search_space_id, aggr_idx); + pdcch_dl_t* pdcch = bwp_pdcch_slot.pdcchs.alloc_rar_pdcch(ra_rnti, aggr_idx); if (pdcch == nullptr) { // Could not find space in PDCCH logger.debug("SCHED: No space in PDCCH for DL tx."); @@ -234,7 +232,7 @@ alloc_result bwp_slot_allocator::alloc_rar_and_msg3(uint16_t // Generate DCI for RAR with given RA-RNTI if (not fill_dci_rar(interv, ra_rnti, *bwp_grid.cfg, pdcch->dci)) { // Cancel on-going PDCCH allocation - bwp_pdcch_slot.pdcchs.rem_last_pdcch(search_space_id); + bwp_pdcch_slot.pdcchs.rem_last_pdcch(pdcch->dci.ctx); return alloc_result::invalid_coderate; } auto& phy_cfg = slot_ues[pending_rachs[0].temp_crnti]->phy(); @@ -325,7 +323,7 @@ alloc_result bwp_slot_allocator::alloc_pdsch(slot_ue& ue, prb_grant dl_grant) const srsran_search_space_t& ss = *ss_candidates[0]; // Find space and allocate PDCCH - pdcch_dl_t* pdcch = bwp_pdcch_slot.pdcchs.alloc_dl_pdcch(rnti_type, ss.id, aggr_idx, &ue.cfg()); + pdcch_dl_t* pdcch = bwp_pdcch_slot.pdcchs.alloc_dl_pdcch(rnti_type, ss.id, aggr_idx, ue.cfg()); if (pdcch == nullptr) { // Could not find space in PDCCH logger.debug("Could not find PDCCH space for rnti=0x%x PDSCH allocation", ue->rnti); @@ -432,7 +430,7 @@ alloc_result bwp_slot_allocator::alloc_pusch(slot_ue& ue, prb_grant ul_grant) } const srsran_search_space_t& ss = *ss_candidates[0]; - pdcch_ul_t* pdcch = bwp_pdcch_slot.pdcchs.alloc_ul_pdcch(ss.id, aggr_idx, &ue.cfg()); + pdcch_ul_t* pdcch = bwp_pdcch_slot.pdcchs.alloc_ul_pdcch(ss.id, aggr_idx, ue.cfg()); if (pdcch == nullptr) { // Could not find space in PDCCH logger.debug("Could not find PDCCH space for rnti=0x%x PUSCH allocation", ue->rnti); diff --git a/srsgnb/src/stack/mac/sched_nr_interface_utils.cc b/srsgnb/src/stack/mac/sched_nr_interface_utils.cc index f197c1944..a25f82885 100644 --- a/srsgnb/src/stack/mac/sched_nr_interface_utils.cc +++ b/srsgnb/src/stack/mac/sched_nr_interface_utils.cc @@ -14,6 +14,13 @@ namespace srsenb { +uint32_t coreset_nof_cces(const srsran_coreset_t& coreset) +{ + const bool* res_active = &coreset.freq_resources[0]; + uint32_t nof_freq_res = std::count(res_active, res_active + SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE, true); + return nof_freq_res * coreset.duration; +} + srsran::phy_cfg_nr_t get_common_ue_phy_cfg(const sched_nr_interface::cell_cfg_t& cfg) { srsran::phy_cfg_nr_t ue_phy_cfg; diff --git a/srsgnb/src/stack/mac/sched_nr_pdcch.cc b/srsgnb/src/stack/mac/sched_nr_pdcch.cc index 3639b35d2..efbb9f73f 100644 --- a/srsgnb/src/stack/mac/sched_nr_pdcch.cc +++ b/srsgnb/src/stack/mac/sched_nr_pdcch.cc @@ -11,17 +11,18 @@ */ #include "srsgnb/hdr/stack/mac/sched_nr_pdcch.h" +#include "srsgnb/hdr/stack/mac/sched_nr_helpers.h" #include "srsran/common/string_helpers.h" namespace srsenb { namespace sched_nr_impl { template -void log_pdcch_alloc_failure(srslog::log_channel& log_ch, - srsran_rnti_type_t rnti_type, - uint32_t ss_id, - const ue_carrier_params_t* ue, - const char* cause_fmt, +void log_pdcch_alloc_failure(srslog::log_channel& log_ch, + srsran_rnti_type_t rnti_type, + uint32_t ss_id, + uint16_t rnti, + const char* cause_fmt, Args&&... args) { if (not log_ch.enabled()) { @@ -30,25 +31,19 @@ void log_pdcch_alloc_failure(srslog::log_channel& log_ch, // Log PDCCH allocation failure fmt::memory_buffer fmtbuf; - fmt::format_to(fmtbuf, "SCHED: Failure to allocate PDCCH for {}-rnti", srsran_rnti_type_str_short(rnti_type)); - if (rnti_type == srsran_rnti_type_c or rnti_type == srsran_rnti_type_tc) { - fmt::format_to(fmtbuf, "=0x{:x}", ue->rnti); - } - fmt::format_to(fmtbuf, ", ss_id={}. Cause:", ss_id); + fmt::format_to(fmtbuf, + "SCHED: Failure to allocate PDCCH for {}-rnti=0x{:x}, SS#{}. Cause: ", + srsran_rnti_type_str_short(rnti_type), + rnti, + ss_id); fmt::format_to(fmtbuf, cause_fmt, std::forward(args)...); log_ch("%s", srsran::to_c_str(fmtbuf)); } -coreset_region::coreset_region(const bwp_params_t& bwp_cfg_, - uint32_t coreset_id_, - uint32_t slot_idx_, - pdcch_dl_list_t& dl_list_, - pdcch_ul_list_t& ul_list_) : +coreset_region::coreset_region(const bwp_params_t& bwp_cfg_, uint32_t coreset_id_, uint32_t slot_idx_) : coreset_cfg(&bwp_cfg_.cfg.pdcch.coreset[coreset_id_]), coreset_id(coreset_id_), slot_idx(slot_idx_), - pdcch_dl_list(dl_list_), - pdcch_ul_list(ul_list_), rar_cce_list(bwp_cfg_.rar_cce_list), common_cce_list(bwp_cfg_.common_cce_list) { @@ -67,56 +62,24 @@ void coreset_region::reset() dfs_tree.clear(); saved_dfs_tree.clear(); dci_list.clear(); - pdcch_dl_list.clear(); - pdcch_ul_list.clear(); } -pdcch_dl_t* coreset_region::alloc_dl_pdcch(srsran_rnti_type_t rnti_type, - uint32_t aggr_idx, - uint32_t search_space_id, - const ue_carrier_params_t* user) -{ - // Add new DL PDCCH to sched result - pdcch_dl_list.emplace_back(); - - if (alloc_pdcch_common(rnti_type, true, aggr_idx, search_space_id, user)) { - return &pdcch_dl_list.back(); - } - - // Remove failed PDCCH allocation - pdcch_dl_list.pop_back(); - return nullptr; -} - -pdcch_ul_t* coreset_region::alloc_ul_pdcch(uint32_t aggr_idx, uint32_t search_space_id, const ue_carrier_params_t* user) -{ - // Add new UL PDCCH to sched result - pdcch_ul_list.emplace_back(); - - if (alloc_pdcch_common(srsran_rnti_type_c, false, aggr_idx, search_space_id, user)) { - return &pdcch_ul_list.back(); - } - - // Remove failed PDCCH allocation - pdcch_ul_list.pop_back(); - return nullptr; -} - -bool coreset_region::alloc_pdcch_common(srsran_rnti_type_t rnti_type, - bool is_dl, - uint32_t aggr_idx, - uint32_t search_space_id, - const ue_carrier_params_t* user) +bool coreset_region::alloc_pdcch(srsran_rnti_type_t rnti_type, + bool is_dl, + uint32_t aggr_idx, + uint32_t search_space_id, + const ue_carrier_params_t* user, + srsran_dci_ctx_t& dci) { saved_dfs_tree.clear(); alloc_record record; + record.dci = &dci; record.ue = user; record.aggr_idx = aggr_idx; record.ss_id = search_space_id; record.is_dl = is_dl; record.rnti_type = rnti_type; - record.idx = record.is_dl ? pdcch_dl_list.size() - 1 : pdcch_ul_list.size() - 1; // Try to allocate grant. If it fails, attempt the same grant, but using a different permutation of past grant DCI // positions @@ -143,11 +106,6 @@ void coreset_region::rem_last_pdcch() // Remove DCI record dfs_tree.pop_back(); - if (not dci_list.back().is_dl) { - pdcch_ul_list.pop_back(); - } else { - pdcch_dl_list.pop_back(); - } dci_list.pop_back(); } @@ -204,13 +162,7 @@ bool coreset_region::alloc_dfs_node(const alloc_record& record, uint32_t start_d // Allocation successful node.total_mask |= node.current_mask; alloc_dfs.push_back(node); - if (not record.is_dl) { - pdcch_ul_t& pdcch_ul = pdcch_ul_list[record.idx]; - pdcch_ul.dci.ctx.location = node.dci_pos; - } else { - pdcch_dl_t& pdcch_dl = pdcch_dl_list[record.idx]; - pdcch_dl.dci.ctx.location = node.dci_pos; - } + record.dci->location = node.dci_pos; return true; } @@ -245,111 +197,132 @@ bwp_pdcch_allocator::bwp_pdcch_allocator(const bwp_params_t& bwp_cfg_, for (uint32_t cs_idx = 0; cs_idx < SRSRAN_UE_DL_NR_MAX_NOF_CORESET; ++cs_idx) { if (bwp_cfg.cfg.pdcch.coreset_present[cs_idx]) { uint32_t cs_id = bwp_cfg.cfg.pdcch.coreset[cs_idx].id; - coresets.emplace(cs_id, bwp_cfg, cs_id, slot_idx, pdcch_dl_list, pdcch_ul_list); + coresets.emplace(cs_id, bwp_cfg, cs_id, slot_idx); } } } -/// Helper function to verify valid inputs -bool bwp_pdcch_allocator::check_args_valid(srsran_rnti_type_t rnti_type, - uint32_t ss_id, - uint32_t aggr_idx, - const ue_carrier_params_t* user, - bool is_dl) const +void bwp_pdcch_allocator::fill_dci_ctx_common(srsran_dci_ctx_t& dci, + srsran_rnti_type_t rnti_type, + uint16_t rnti, + const srsran_search_space_t& ss, + srsran_dci_format_nr_t dci_fmt, + const ue_carrier_params_t* ue) { - srsran_assert(ss_id < SRSRAN_UE_DL_NR_MAX_NOF_SEARCH_SPACE, "Invalid SearchSpace#%d", ss_id); - srsran_assert( - aggr_idx < SRSRAN_SEARCH_SPACE_NOF_AGGREGATION_LEVELS_NR, "Invalid aggregation level index=%d", aggr_idx); - - // Cell Configuration checks - if (not bwp_cfg.slots[slot_idx].is_dl) { - log_pdcch_alloc_failure(logger.error, rnti_type, ss_id, user, "DL is disabled for slot={}", slot_idx); - return false; - } - - // Coreset-specific checks - const srsran_search_space_t* ss = (user == nullptr) ? bwp_cfg.get_ss(ss_id) : user->get_ss(ss_id); - if (ss == nullptr) { - log_pdcch_alloc_failure(logger.error, rnti_type, ss_id, user, "SearchSpace has not been configured"); - return false; - } - if (ss->nof_candidates[aggr_idx] == 0) { - log_pdcch_alloc_failure(logger.warning, rnti_type, ss_id, user, "Chosen PDCCH doesn't have valid candidates"); - return false; - } + dci.ss_type = ss.type; + dci.coreset_id = ss.coreset_id; + const srsran_coreset_t* coreset = + ue == nullptr ? &bwp_cfg.cfg.pdcch.coreset[ss.coreset_id] : &ue->phy().pdcch.coreset[ss.coreset_id]; + dci.coreset_start_rb = srsran_coreset_start_rb(coreset); + dci.rnti_type = rnti_type; + dci.rnti = rnti; + dci.format = dci_fmt; +} - if (is_dl) { - if (pdcch_dl_list.full()) { - log_pdcch_alloc_failure( - logger.warning, rnti_type, ss_id, user, "Maximum number of allocations={} reached", pdcch_dl_list.size()); - return false; - } - if (rnti_type == srsran_rnti_type_si or rnti_type == srsran_rnti_type_ra or rnti_type == srsran_rnti_type_p) { - srsran_assert(user == nullptr, "No UE should be provided in case of non-UE PDCCH allocation"); - if (rnti_type == srsran_rnti_type_ra) { - srsran_assert(ss_id == bwp_cfg.cfg.pdcch.ra_search_space.id and bwp_cfg.cfg.pdcch.ra_search_space_present, - "PDCCH grant type does not match search space"); - } - } else { - srsran_assert(user != nullptr, "UE object must be provided for UE-specific PDCCH allocations"); - } - } else if (pdcch_ul_list.full()) { - log_pdcch_alloc_failure( - logger.warning, rnti_type, ss_id, user, "Maximum number of UL allocations={} reached", pdcch_ul_list.size()); - return false; - } +pdcch_dl_t* bwp_pdcch_allocator::alloc_rar_pdcch(uint16_t ra_rnti, uint32_t aggr_idx) +{ + srsran_assert(bwp_cfg.cfg.pdcch.ra_search_space_present, "Allocating RAR PDCCH in BWP without RA SearchSpace"); + return alloc_dl_pdcch_common( + srsran_rnti_type_ra, ra_rnti, bwp_cfg.cfg.pdcch.ra_search_space.id, aggr_idx, srsran_dci_format_nr_1_0, nullptr); +} - srsran_sanity_check(pdcch_dl_list.size() + pdcch_ul_list.size() == nof_allocations(), "Invalid PDCCH state"); - return true; +pdcch_dl_t* bwp_pdcch_allocator::alloc_si_pdcch(uint32_t ss_id, uint32_t aggr_idx) +{ + return alloc_dl_pdcch_common(srsran_rnti_type_si, SRSRAN_SIRNTI, ss_id, aggr_idx, srsran_dci_format_nr_1_0, nullptr); } pdcch_dl_t* bwp_pdcch_allocator::alloc_dl_pdcch(srsran_rnti_type_t rnti_type, uint32_t ss_id, uint32_t aggr_idx, - const ue_carrier_params_t* user) + const ue_carrier_params_t& user) +{ + static const srsran_dci_format_nr_t dci_fmt = srsran_dci_format_nr_1_0; // TODO: make it configurable + srsran_assert(rnti_type == srsran_rnti_type_c or rnti_type == srsran_rnti_type_tc, + "Invalid RNTI type=%s for UE-specific PDCCH", + srsran_rnti_type_str_short(rnti_type)); + return alloc_dl_pdcch_common(rnti_type, user.rnti, ss_id, aggr_idx, dci_fmt, &user); +} + +pdcch_dl_t* bwp_pdcch_allocator::alloc_dl_pdcch_common(srsran_rnti_type_t rnti_type, + uint16_t rnti, + uint32_t ss_id, + uint32_t aggr_idx, + srsran_dci_format_nr_t dci_fmt, + const ue_carrier_params_t* user) { - if (not check_args_valid(rnti_type, ss_id, aggr_idx, user, true)) { + if (not check_args_valid(rnti_type, rnti, ss_id, aggr_idx, dci_fmt, user, true)) { return nullptr; } const srsran_search_space_t& ss = (user == nullptr) ? *bwp_cfg.get_ss(ss_id) : *user->get_ss(ss_id); - pdcch_dl_t* pdcch = coresets[ss.coreset_id].alloc_dl_pdcch(rnti_type, aggr_idx, ss_id, user); + // Add new DL PDCCH to sched result + pdcch_dl_list.emplace_back(); + + bool success = + coresets[ss.coreset_id].alloc_pdcch(rnti_type, true, aggr_idx, ss_id, user, pdcch_dl_list.back().dci.ctx); + + if (not success) { + // Remove failed PDCCH allocation + pdcch_dl_list.pop_back(); - if (pdcch == nullptr) { // Log PDCCH allocation failure srslog::log_channel& ch = user == nullptr ? logger.warning : logger.debug; - log_pdcch_alloc_failure(ch, rnti_type, ss_id, user, "Failure to find available PDCCH position"); + log_pdcch_alloc_failure(ch, rnti_type, ss_id, rnti, "No available PDCCH position"); + + return nullptr; } - return pdcch; + + fill_dci_ctx_common(pdcch_dl_list.back().dci.ctx, rnti_type, rnti, ss, dci_fmt, user); + return &pdcch_dl_list.back(); } -pdcch_ul_t* bwp_pdcch_allocator::alloc_ul_pdcch(uint32_t ss_id, uint32_t aggr_idx, const ue_carrier_params_t* user) +pdcch_ul_t* bwp_pdcch_allocator::alloc_ul_pdcch(uint32_t ss_id, uint32_t aggr_idx, const ue_carrier_params_t& user) { - if (not check_args_valid(srsran_rnti_type_c, ss_id, aggr_idx, user, false)) { + static const srsran_dci_format_nr_t dci_fmt = srsran_dci_format_nr_0_0; // TODO: make it configurable + if (not check_args_valid(srsran_rnti_type_c, user.rnti, ss_id, aggr_idx, dci_fmt, &user, false)) { return nullptr; } - const srsran_search_space_t& ss = *user->get_ss(ss_id); + const srsran_search_space_t& ss = *user.get_ss(ss_id); + + // Add new UL PDCCH to sched result + pdcch_ul_list.emplace_back(); - pdcch_ul_t* pdcch = coresets[ss.coreset_id].alloc_ul_pdcch(aggr_idx, ss_id, user); + bool success = coresets[ss.coreset_id].alloc_pdcch( + srsran_rnti_type_c, false, aggr_idx, ss_id, &user, pdcch_ul_list.back().dci.ctx); + + if (not success) { + // Remove failed PDCCH allocation + pdcch_ul_list.pop_back(); - if (pdcch == nullptr) { // Log PDCCH allocation failure - log_pdcch_alloc_failure(logger.debug, srsran_rnti_type_c, ss_id, user, "Failure to find available PDCCH position"); + log_pdcch_alloc_failure(logger.debug, srsran_rnti_type_c, ss_id, user.rnti, "No available PDCCH position"); + + return nullptr; } - return pdcch; + fill_dci_ctx_common(pdcch_ul_list.back().dci.ctx, srsran_rnti_type_c, user.rnti, ss, dci_fmt, &user); + return &pdcch_ul_list.back(); } -void bwp_pdcch_allocator::rem_last_pdcch(uint32_t ss_id) +void bwp_pdcch_allocator::rem_last_pdcch(srsran_dci_ctx_t& dci_ctx_to_rem) { - const srsran_search_space_t& ss = bwp_cfg.cfg.pdcch.search_space[ss_id]; + uint32_t cs_id = dci_ctx_to_rem.coreset_id; - uint32_t coreset_id = ss.coreset_id; - coresets[coreset_id].rem_last_pdcch(); + if (&pdcch_dl_list.back().dci.ctx == &dci_ctx_to_rem) { + pdcch_dl_list.pop_back(); + } else if (&pdcch_ul_list.back().dci.ctx == &dci_ctx_to_rem) { + pdcch_ul_list.pop_back(); + } else { + logger.error("Invalid DCI context provided to be removed"); + return; + } + coresets[cs_id].rem_last_pdcch(); } void bwp_pdcch_allocator::reset() { + pdcch_dl_list.clear(); + pdcch_ul_list.clear(); for (coreset_region& coreset : coresets) { coreset.reset(); } @@ -364,5 +337,74 @@ uint32_t bwp_pdcch_allocator::nof_allocations() const return count; } +uint32_t bwp_pdcch_allocator::nof_cces(uint32_t coreset_id) const +{ + return coresets[coreset_id].nof_cces(); +} + +bool bwp_pdcch_allocator::check_args_valid(srsran_rnti_type_t rnti_type, + uint16_t rnti, + uint32_t ss_id, + uint32_t aggr_idx, + srsran_dci_format_nr_t dci_fmt, + const ue_carrier_params_t* user, + bool is_dl) const +{ + srsran_assert(ss_id < SRSRAN_UE_DL_NR_MAX_NOF_SEARCH_SPACE, "Invalid SearchSpace#%d", ss_id); + srsran_assert( + aggr_idx < SRSRAN_SEARCH_SPACE_NOF_AGGREGATION_LEVELS_NR, "Invalid aggregation level index=%d", aggr_idx); + + // DL must be active in given slot + if (not bwp_cfg.slots[slot_idx].is_dl) { + log_pdcch_alloc_failure(logger.error, rnti_type, ss_id, rnti, "DL is disabled for slot={}", slot_idx); + return false; + } + + // Verify SearchSpace validity + const srsran_search_space_t* ss = (user == nullptr) ? bwp_cfg.get_ss(ss_id) : user->get_ss(ss_id); + if (ss == nullptr) { + // Couldn't find SearchSpace + log_pdcch_alloc_failure(logger.error, rnti_type, ss_id, rnti, "SearchSpace has not been configured"); + return false; + } + if (ss->nof_candidates[aggr_idx] == 0) { + // No valid DCI position candidates given aggregation level + log_pdcch_alloc_failure( + logger.warning, rnti_type, ss_id, rnti, "Chosen SearchSpace doesn't have CCE candidates for L={}", aggr_idx); + return false; + } + if (not is_rnti_type_valid_in_search_space(rnti_type, ss->type)) { + // RNTI type doesnt match SearchSpace type + log_pdcch_alloc_failure( + logger.warning, rnti_type, ss_id, rnti, "Chosen SearchSpace type={} does not match rnti_type.", ss->type); + return false; + } + auto dci_fmt_equal = [dci_fmt](srsran_dci_format_nr_t f) { return f == dci_fmt; }; + if (std::none_of(&ss->formats[0], &ss->formats[ss->nof_formats], dci_fmt_equal)) { + log_pdcch_alloc_failure(logger.warning, + rnti_type, + ss_id, + rnti, + "Chosen SearchSpace does not support chosen dci format={}", + srsran_dci_format_nr_string(dci_fmt)); + return false; + } + + if (is_dl) { + if (pdcch_dl_list.full()) { + log_pdcch_alloc_failure( + logger.warning, rnti_type, ss_id, rnti, "Maximum number of allocations={} reached", pdcch_dl_list.size()); + return false; + } + } else if (pdcch_ul_list.full()) { + log_pdcch_alloc_failure( + logger.warning, rnti_type, ss_id, rnti, "Maximum number of UL allocations={} reached", pdcch_ul_list.size()); + return false; + } + + srsran_sanity_check(pdcch_dl_list.size() + pdcch_ul_list.size() == nof_allocations(), "Invalid PDCCH state"); + return true; +} + } // namespace sched_nr_impl } // namespace srsenb diff --git a/srsgnb/src/stack/mac/test/CMakeLists.txt b/srsgnb/src/stack/mac/test/CMakeLists.txt index 212ed4693..7b74e213d 100644 --- a/srsgnb/src/stack/mac/test/CMakeLists.txt +++ b/srsgnb/src/stack/mac/test/CMakeLists.txt @@ -26,6 +26,10 @@ target_link_libraries(sched_nr_prb_test ${Boost_LIBRARIES}) add_nr_test(sched_nr_prb_test sched_nr_prb_test) +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_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 11e8af877..b7816a350 100644 --- a/srsgnb/src/stack/mac/test/sched_nr_cfg_generators.h +++ b/srsgnb/src/stack/mac/test/sched_nr_cfg_generators.h @@ -30,6 +30,19 @@ inline srsran_coreset_t get_default_coreset0(uint32_t nof_prb) return coreset; } +inline srsran_search_space_t get_default_search_space0() +{ + srsran_search_space_t ss{}; + ss.coreset_id = 0; + ss.nof_formats = 1; + ss.formats[0] = srsran_dci_format_nr_1_0; + ss.type = srsran_search_space_type_common_0; + ss.id = 0; + ss.nof_candidates[2] = 1; + ss.duration = 1; + return ss; +} + inline sched_nr_interface::cell_cfg_t get_default_cell_cfg( const srsran::phy_cfg_nr_t& phy_cfg = srsran::phy_cfg_nr_default_t{srsran::phy_cfg_nr_default_t::reference_cfg_t{}}) { @@ -108,6 +121,61 @@ inline sched_nr_interface::ue_cfg_t get_default_ue_cfg( return uecfg; } +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.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; + cell_cfg.bwps[0].pdcch.search_space[1].coreset_id = 0; + cell_cfg.bwps[0].pdcch.search_space[1].type = srsran_search_space_type_common_1; + cell_cfg.bwps[0].pdcch.search_space[1].nof_candidates[2] = 1; + cell_cfg.bwps[0].pdcch.search_space[1].nof_formats = 2; + cell_cfg.bwps[0].pdcch.search_space[1].formats[0] = srsran_dci_format_nr_1_0; + cell_cfg.bwps[0].pdcch.search_space[1].formats[1] = srsran_dci_format_nr_0_0; + cell_cfg.bwps[0].pdcch.ra_search_space = cell_cfg.bwps[0].pdcch.search_space[1]; + return cell_cfg; +} + +// Generate default UE-dedicated CORESET config +inline srsran_coreset_t get_default_ue_specific_coreset(uint32_t id, uint32_t pci) +{ + srsran_coreset_t coreset = {}; + coreset.id = id; + coreset.mapping_type = srsran_coreset_mapping_type_non_interleaved; + coreset.duration = 1; + for (uint32_t i = 0; i < 8; ++i) { + coreset.freq_resources[i] = true; + } + coreset.dmrs_scrambling_id_present = false; + coreset.precoder_granularity = srsran_coreset_precoder_granularity_reg_bundle; + coreset.interleaver_size = srsran_coreset_bundle_size_n2; + coreset.reg_bundle_size = srsran_coreset_bundle_size_n6; + coreset.shift_index = pci; + coreset.offset_rb = 0; + return coreset; +} + +inline srsran_search_space_t get_default_ue_specific_search_space(uint32_t id, uint32_t coreset_id) +{ + srsran_search_space_t ss = {}; + ss.id = id; + ss.coreset_id = coreset_id; + ss.duration = 1; + ss.type = srsran_search_space_type_ue; + ss.nof_formats = 2; + ss.formats[0] = srsran_dci_format_nr_1_0; + ss.formats[1] = srsran_dci_format_nr_0_0; + ss.nof_candidates[0] = 2; + ss.nof_candidates[1] = 2; + ss.nof_candidates[2] = 2; + ss.nof_candidates[3] = 1; + ss.nof_candidates[4] = 0; + return ss; +} + } // namespace srsenb #endif // SRSRAN_SCHED_NR_CFG_GENERATORS_H diff --git a/srsgnb/src/stack/mac/test/sched_nr_common_test.cc b/srsgnb/src/stack/mac/test/sched_nr_common_test.cc index 5c7c007f5..b356b7845 100644 --- a/srsgnb/src/stack/mac/test/sched_nr_common_test.cc +++ b/srsgnb/src/stack/mac/test/sched_nr_common_test.cc @@ -12,29 +12,96 @@ #include "sched_nr_common_test.h" #include "srsgnb/hdr/stack/mac/sched_nr_cfg.h" +#include "srsgnb/hdr/stack/mac/sched_nr_helpers.h" #include "srsran/support/srsran_test.h" namespace srsenb { +using namespace sched_nr_impl; + +void test_dci_ctx_consistency(const srsran_pdcch_cfg_nr_t& pdcch_cfg, const srsran_dci_ctx_t& dci_ctx) +{ + TESTASSERT(dci_ctx.coreset_id < SRSRAN_UE_DL_NR_MAX_NOF_CORESET); + TESTASSERT(pdcch_cfg.coreset_present[dci_ctx.coreset_id]); + const srsran_coreset_t& coreset = pdcch_cfg.coreset[dci_ctx.coreset_id]; + + // DCI location + TESTASSERT(dci_ctx.location.L < SRSRAN_SEARCH_SPACE_NOF_AGGREGATION_LEVELS_NR); + TESTASSERT(dci_ctx.location.ncce + (1U << dci_ctx.location.L) <= coreset_nof_cces(coreset)); + // RNTI type, SearchSpace type, DCI format + TESTASSERT(sched_nr_impl::is_rnti_type_valid_in_search_space(dci_ctx.rnti_type, dci_ctx.ss_type)); + switch (dci_ctx.rnti_type) { + case srsran_rnti_type_si: + TESTASSERT_EQ(srsran_dci_format_nr_1_0, dci_ctx.format); + TESTASSERT_EQ(srsran_search_space_type_common_0, dci_ctx.ss_type); + TESTASSERT_EQ(SRSRAN_SIRNTI, dci_ctx.rnti); + break; + case srsran_rnti_type_ra: + TESTASSERT_EQ(dci_ctx.format, srsran_dci_format_nr_1_0); + TESTASSERT_EQ(dci_ctx.ss_type, srsran_search_space_type_common_1); + TESTASSERT(pdcch_cfg.ra_search_space_present); + TESTASSERT_EQ(pdcch_cfg.ra_search_space.coreset_id, dci_ctx.coreset_id); + TESTASSERT(pdcch_cfg.ra_search_space.nof_candidates[dci_ctx.location.L] > 0); + break; + case srsran_rnti_type_tc: + TESTASSERT_EQ(srsran_dci_format_nr_1_0, dci_ctx.format); + TESTASSERT_EQ(srsran_search_space_type_common_1, dci_ctx.ss_type); + break; + case srsran_rnti_type_c: + TESTASSERT(dci_ctx.format == srsran_dci_format_nr_1_0 or dci_ctx.format == srsran_dci_format_nr_1_1 or + dci_ctx.format == srsran_dci_format_nr_0_0 or dci_ctx.format == srsran_dci_format_nr_0_1); + break; + default: + srsran_terminate("rnti type=%d not supported", dci_ctx.rnti_type); + } + // CORESET position + TESTASSERT_EQ(srsran_coreset_start_rb(&coreset), dci_ctx.coreset_start_rb); +} + +void test_pdcch_collisions(const srsran_pdcch_cfg_nr_t& pdcch_cfg, + srsran::const_span dl_pdcchs, + srsran::const_span ul_pdcchs) +{ + srsran::optional_vector coreset_bitmaps; + for (const srsran_coreset_t& coreset : view_active_coresets(pdcch_cfg)) { + coreset_bitmaps.emplace(coreset.id, coreset_bitmap(coreset_nof_cces(coreset))); + } + + for (const pdcch_dl_t& pdcch : dl_pdcchs) { + coreset_bitmap& total_bitmap = coreset_bitmaps[pdcch.dci.ctx.coreset_id]; + coreset_bitmap alloc(total_bitmap.size()); + alloc.fill(pdcch.dci.ctx.location.ncce, pdcch.dci.ctx.location.ncce + (1U << pdcch.dci.ctx.location.L)); + TESTASSERT((alloc & total_bitmap).none()); + total_bitmap |= alloc; + } + for (const pdcch_ul_t& pdcch : ul_pdcchs) { + coreset_bitmap& total_bitmap = coreset_bitmaps[pdcch.dci.ctx.coreset_id]; + coreset_bitmap alloc(total_bitmap.size()); + alloc.fill(pdcch.dci.ctx.location.ncce, pdcch.dci.ctx.location.ncce + (1U << pdcch.dci.ctx.location.L)); + TESTASSERT((alloc & total_bitmap).none()); + total_bitmap |= alloc; + } +} + void test_dl_pdcch_consistency(const sched_nr_interface::cell_cfg_t& cell_cfg, srsran::const_span dl_pdcchs) { for (const auto& pdcch : dl_pdcchs) { TESTASSERT(pdcch.dci.bwp_id < cell_cfg.bwps.size()); const srsran_pdcch_cfg_nr_t& pdcch_cfg = cell_cfg.bwps[pdcch.dci.bwp_id].pdcch; - TESTASSERT(pdcch_cfg.coreset_present[pdcch.dci.ctx.coreset_id]); + test_dci_ctx_consistency(pdcch_cfg, pdcch.dci.ctx); - if (pdcch.dci.ctx.rnti_type == srsran_rnti_type_ra) { - TESTASSERT_EQ(pdcch.dci.ctx.format, srsran_dci_format_nr_1_0); - TESTASSERT_EQ(pdcch.dci.ctx.ss_type, srsran_search_space_type_common_1); - TESTASSERT(pdcch.dci.ctx.location.L < SRSRAN_SEARCH_SPACE_NOF_AGGREGATION_LEVELS_NR); + const srsran_coreset_t& coreset = pdcch_cfg.coreset[pdcch.dci.ctx.coreset_id]; + if (pdcch.dci.ctx.coreset_id == 0) { + TESTASSERT_EQ(srsran_coreset_get_bw(&pdcch_cfg.coreset[0]), pdcch.dci.coreset0_bw); + } - // check consistency with cell_cfg - TESTASSERT(pdcch_cfg.ra_search_space_present); - TESTASSERT_EQ(pdcch_cfg.ra_search_space.coreset_id, pdcch.dci.ctx.coreset_id); - TESTASSERT(pdcch_cfg.ra_search_space.nof_candidates[pdcch.dci.ctx.location.L] > 0); - } else if (pdcch.dci.ctx.rnti_type == srsran_rnti_type_c) { - TESTASSERT(pdcch.dci.ctx.format == srsran_dci_format_nr_1_0 or pdcch.dci.ctx.format == srsran_dci_format_nr_1_1); + switch (pdcch.dci.ctx.rnti_type) { + case srsran_rnti_type_si: + TESTASSERT(pdcch.dci.sii != 0 or pdcch.dci.ctx.coreset_id == 0); // sii=0 must go in CORESET#0 + break; + default: + break; } } } diff --git a/srsgnb/src/stack/mac/test/sched_nr_common_test.h b/srsgnb/src/stack/mac/test/sched_nr_common_test.h index ec1514d67..0405a8b98 100644 --- a/srsgnb/src/stack/mac/test/sched_nr_common_test.h +++ b/srsgnb/src/stack/mac/test/sched_nr_common_test.h @@ -18,6 +18,14 @@ namespace srsenb { +/// Test DCI context consistency +void test_dci_ctx_consistency(const srsran_pdcch_cfg_nr_t& pdcch_cfg, const srsran_dci_ctx_t& dci); + +/// Test PDCCH collisions +void test_pdcch_collisions(const srsran_pdcch_cfg_nr_t& pdcch_cfg, + srsran::const_span dl_pdcchs, + srsran::const_span ul_pddchs); + void test_dl_pdcch_consistency(const sched_nr_interface::cell_cfg_t& cell_cfg, srsran::const_span dl_pdcch); void test_pdsch_consistency(srsran::const_span dl_pdcch); diff --git a/srsgnb/src/stack/mac/test/sched_nr_pdcch_test.cc b/srsgnb/src/stack/mac/test/sched_nr_pdcch_test.cc new file mode 100644 index 000000000..dcf5233f5 --- /dev/null +++ b/srsgnb/src/stack/mac/test/sched_nr_pdcch_test.cc @@ -0,0 +1,228 @@ +/** + * + * \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 "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" + +namespace srsenb { + +using namespace sched_nr_impl; + +/** + * Test for the case CORESET#0 is active. + * Given only one PDCCH candidate position is supported, only one PDCCH allocation should take place per slot + * The test additionally verifies that the DCI context content is correct for each PDCCH allocation + */ +void test_coreset0_cfg() +{ + const uint32_t aggr_idx = 2; + + srsran::test_delimit_logger delimiter{"Test PDCCH Allocation in CORESET#0"}; + + sched_nr_impl::cell_cfg_t cell_cfg = get_default_sa_cell_cfg_common(); + sched_nr_interface::sched_args_t sched_args; + bwp_params_t bwp_params{cell_cfg, sched_args, 0, 0}; + + // UE config + ue_cfg_t uecfg = get_rach_ue_cfg(0); + uecfg.phy_cfg.pdcch = cell_cfg.bwps[0].pdcch; // Starts without UE-specific PDCCH + ue_carrier_params_t ue_cc{0x46, bwp_params, uecfg}; + + pdcch_dl_list_t dl_pdcchs; + pdcch_ul_list_t ul_pdcchs; + pdcch_dl_t* dl_pdcch = nullptr; + pdcch_ul_t* ul_pdcch = nullptr; + + bwp_pdcch_allocator pdcch_sched(bwp_params, 0, dl_pdcchs, ul_pdcchs); + for (const srsran_coreset_t& cs : view_active_coresets(cell_cfg.bwps[0].pdcch)) { + // Verify nof CCEs is correctly computed + TESTASSERT_EQ(coreset_nof_cces(cs), pdcch_sched.nof_cces(cs.id)); + } + + // Slot with SIB1 + TESTASSERT_EQ(0, pdcch_sched.nof_allocations()); + + // SIB1 allocation should be successful + dl_pdcch = pdcch_sched.alloc_si_pdcch(0, aggr_idx); + TESTASSERT(dl_pdcch != nullptr); + TESTASSERT_EQ(1, pdcch_sched.nof_allocations()); + TESTASSERT_EQ(srsran_rnti_type_si, dl_pdcch->dci.ctx.rnti_type); + TESTASSERT_EQ(0, dl_pdcch->dci.ctx.coreset_id); + test_dci_ctx_consistency(bwp_params.cfg.pdcch, dl_pdcch->dci.ctx); + + // No space for RAR, UE PDSCH/PUSCH + TESTASSERT(pdcch_sched.alloc_rar_pdcch(0x2, aggr_idx) == nullptr); + TESTASSERT(pdcch_sched.alloc_dl_pdcch(srsran_rnti_type_c, 1, aggr_idx, ue_cc) == nullptr); + TESTASSERT(pdcch_sched.alloc_ul_pdcch(1, aggr_idx, ue_cc) == nullptr); + + // Slot with RAR + pdcch_sched.reset(); + + // RAR allocation should be successful + dl_pdcch = pdcch_sched.alloc_rar_pdcch(0x2, aggr_idx); + TESTASSERT(dl_pdcch != nullptr); + TESTASSERT_EQ(1, pdcch_sched.nof_allocations()); + TESTASSERT_EQ(srsran_rnti_type_ra, dl_pdcch->dci.ctx.rnti_type); + TESTASSERT_EQ(0, dl_pdcch->dci.ctx.coreset_id); + TESTASSERT_EQ(srsran_search_space_type_common_1, dl_pdcch->dci.ctx.ss_type); + test_dci_ctx_consistency(bwp_params.cfg.pdcch, dl_pdcch->dci.ctx); + + // No space for RAR, UE PDSCH/PUSCH + TESTASSERT(pdcch_sched.alloc_dl_pdcch(srsran_rnti_type_c, 1, aggr_idx, ue_cc) == nullptr); + TESTASSERT(pdcch_sched.alloc_ul_pdcch(1, aggr_idx, ue_cc) == nullptr); + + // Slot with DL PDSCH + pdcch_sched.reset(); + + // 1st PDCCH allocation for DL should be successful + dl_pdcch = pdcch_sched.alloc_dl_pdcch(srsran_rnti_type_c, 1, aggr_idx, ue_cc); + TESTASSERT(dl_pdcch != nullptr); + TESTASSERT_EQ(1, pdcch_sched.nof_allocations()); + TESTASSERT_EQ(srsran_rnti_type_c, dl_pdcch->dci.ctx.rnti_type); + TESTASSERT_EQ(0u, dl_pdcch->dci.ctx.coreset_id); + TESTASSERT_EQ(srsran_search_space_type_common_1, dl_pdcch->dci.ctx.ss_type); + test_dci_ctx_consistency(bwp_params.cfg.pdcch, dl_pdcch->dci.ctx); + + // No space for 2nd PDCCH allocation + TESTASSERT(pdcch_sched.alloc_ul_pdcch(1, aggr_idx, ue_cc) == nullptr); + + // Slot with UL PDSCH + pdcch_sched.reset(); + + // 1st PDCCH allocation for UL should be successful + ul_pdcch = pdcch_sched.alloc_ul_pdcch(1, aggr_idx, ue_cc); + TESTASSERT(ul_pdcch != nullptr); + TESTASSERT_EQ(1, pdcch_sched.nof_allocations()); + TESTASSERT_EQ(srsran_rnti_type_c, ul_pdcch->dci.ctx.rnti_type); + TESTASSERT_EQ(0u, ul_pdcch->dci.ctx.coreset_id); + TESTASSERT_EQ(srsran_search_space_type_common_1, ul_pdcch->dci.ctx.ss_type); + test_dci_ctx_consistency(bwp_params.cfg.pdcch, ul_pdcch->dci.ctx); + + // No space for 2nd PDCCH allocation + TESTASSERT(pdcch_sched.alloc_dl_pdcch(srsran_rnti_type_c, 1, aggr_idx, ue_cc) == nullptr); +} + +/** + * Test for the case CORESET#2 is active. + * The PDCCH allocator should find enough space to fit SIB1/RAR (in CORESET#0) and UE-dedicated PDCCHs in (CORESET#2) + * The test additionally verifies that the DCI context content is correct for each PDCCH allocation and there are no + * collisions between PDCCH CCE allocations + */ +void test_coreset2_cfg() +{ + const uint32_t aggr_idx = 2; + + srsran::test_delimit_logger delimiter{"Test PDCCH Allocation in CORESET#0 and CORESET#2"}; + + 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); + sched_nr_interface::sched_args_t sched_args; + bwp_params_t bwp_params{cell_cfg, sched_args, 0, 0}; + + // UE config + 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 + ue_carrier_params_t ue_cc{0x46, bwp_params, uecfg}; + + pdcch_dl_list_t dl_pdcchs; + pdcch_ul_list_t ul_pdcchs; + pdcch_dl_t* dl_pdcch = nullptr; + pdcch_ul_t* ul_pdcch = nullptr; + + bwp_pdcch_allocator pdcch_sched(bwp_params, 0, dl_pdcchs, ul_pdcchs); + for (const srsran_coreset_t& cs : view_active_coresets(cell_cfg.bwps[0].pdcch)) { + // Verify nof CCEs is correctly computed + TESTASSERT_EQ(coreset_nof_cces(cs), pdcch_sched.nof_cces(cs.id)); + } + + // Slot with SIB1 + DL PDCCH and UL PDCCH + TESTASSERT_EQ(0, pdcch_sched.nof_allocations()); + + // SIB1 allocation should be successful + dl_pdcch = pdcch_sched.alloc_si_pdcch(0, aggr_idx); + TESTASSERT(dl_pdcch != nullptr); + TESTASSERT_EQ(1, pdcch_sched.nof_allocations()); + TESTASSERT_EQ(srsran_rnti_type_si, dl_pdcch->dci.ctx.rnti_type); + TESTASSERT_EQ(0, dl_pdcch->dci.ctx.coreset_id); + srsran_dci_location_t expected_loc{aggr_idx, 0}; + TESTASSERT(dl_pdcch->dci.ctx.location == expected_loc); + test_dci_ctx_consistency(bwp_params.cfg.pdcch, dl_pdcch->dci.ctx); + + // No space for RAR or PDSCH in SS#1 + TESTASSERT(pdcch_sched.alloc_rar_pdcch(0x2, aggr_idx) == nullptr); + TESTASSERT(pdcch_sched.alloc_dl_pdcch(srsran_rnti_type_c, 1, aggr_idx, ue_cc) == nullptr); + TESTASSERT(pdcch_sched.alloc_ul_pdcch(1, aggr_idx, ue_cc) == nullptr); + + // there is space for UE DL PDCCH in SS#2 + dl_pdcch = pdcch_sched.alloc_dl_pdcch(srsran_rnti_type_c, 2, aggr_idx, ue_cc); + TESTASSERT(dl_pdcch != nullptr); + TESTASSERT_EQ(2, pdcch_sched.nof_allocations()); + TESTASSERT_EQ(srsran_rnti_type_c, dl_pdcch->dci.ctx.rnti_type); + TESTASSERT_EQ(2u, dl_pdcch->dci.ctx.coreset_id); + TESTASSERT_EQ(srsran_search_space_type_ue, dl_pdcch->dci.ctx.ss_type); + expected_loc = srsran_dci_location_t{aggr_idx, 0}; + TESTASSERT(dl_pdcch->dci.ctx.location == expected_loc); + test_dci_ctx_consistency(bwp_params.cfg.pdcch, dl_pdcch->dci.ctx); + + // there is space for UE UL PDCCH in SS#2 + ul_pdcch = pdcch_sched.alloc_ul_pdcch(2, aggr_idx, ue_cc); + TESTASSERT(ul_pdcch != nullptr); + TESTASSERT_EQ(3, pdcch_sched.nof_allocations()); + TESTASSERT_EQ(srsran_rnti_type_c, ul_pdcch->dci.ctx.rnti_type); + TESTASSERT_EQ(srsran_dci_format_nr_0_0, ul_pdcch->dci.ctx.format); + TESTASSERT_EQ(2u, ul_pdcch->dci.ctx.coreset_id); + TESTASSERT_EQ(srsran_search_space_type_ue, ul_pdcch->dci.ctx.ss_type); + expected_loc = srsran_dci_location_t{aggr_idx, 4}; + TESTASSERT(ul_pdcch->dci.ctx.location == expected_loc); + test_dci_ctx_consistency(bwp_params.cfg.pdcch, ul_pdcch->dci.ctx); + + // No space for 3rd PDCCH allocation in SS#2 + TESTASSERT(pdcch_sched.alloc_dl_pdcch(srsran_rnti_type_c, 2, aggr_idx, ue_cc) == nullptr); + + // Verify there are no PDCCH collisions + TESTASSERT_EQ(3, pdcch_sched.nof_allocations()); + TESTASSERT_EQ(2, dl_pdcchs.size()); + TESTASSERT_EQ(1, ul_pdcchs.size()); + test_pdcch_collisions(bwp_params.cfg.pdcch, dl_pdcchs, ul_pdcchs); + + // Verify all coresets are correctly cleaned up + pdcch_sched.reset(); + TESTASSERT_EQ(0, pdcch_sched.nof_allocations()); + TESTASSERT_EQ(0, dl_pdcchs.size()); + TESTASSERT_EQ(0, ul_pdcchs.size()); +} + +} // 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_coreset0_cfg(); + srsenb::test_coreset2_cfg(); +} \ No newline at end of file