nr,gnb,sched: implemented sched helper functions, wrote a unit test for PDCCH allocations

master
Francisco 3 years ago committed by Francisco Paisana
parent de13c68df7
commit 384004d8d1

@ -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,

@ -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<srsran_search_space_t> 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<const srsran_search_space_t>{pdcch.search_space, pdcch.search_space_present};
}
//////////////////////////////////// CORESET Helpers ////////////////////////////////////////////
/// Get a range of active coresets in a PDCCH configuration
inline srsran::split_optional_span<srsran_coreset_t> view_active_coresets(srsran_pdcch_cfg_nr_t& pdcch)
{
return srsran::split_optional_span<srsran_coreset_t>{pdcch.coreset, pdcch.coreset_present};
}
inline srsran::split_optional_span<const srsran_coreset_t> view_active_coresets(const srsran_pdcch_cfg_nr_t& pdcch)
{
return srsran::split_optional_span<const srsran_coreset_t>{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

@ -28,19 +28,15 @@ using coreset_bitmap = srsran::bounded_bitset<SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZ
class coreset_region
{
public:
coreset_region(const bwp_params_t& bwp_cfg_,
uint32_t coreset_id_,
uint32_t slot_idx,
pdcch_dl_list_t& pdcch_dl_list,
pdcch_ul_list_t& pdcch_ul_list);
coreset_region(const bwp_params_t& bwp_cfg_, uint32_t coreset_id_, uint32_t slot_idx);
void reset();
pdcch_dl_t* alloc_dl_pdcch(srsran_rnti_type_t rnti_type,
uint32_t aggr_idx,
uint32_t search_space_id,
const ue_carrier_params_t* user = nullptr);
pdcch_ul_t* alloc_ul_pdcch(uint32_t aggr_idx, uint32_t search_space_id, const ue_carrier_params_t* user);
bool 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);
void rem_last_pdcch();
@ -50,12 +46,6 @@ public:
size_t nof_allocs() const { return dfs_tree.size(); }
private:
bool 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 = nullptr);
const srsran_coreset_t* coreset_cfg;
uint32_t coreset_id;
uint32_t slot_idx;
@ -69,13 +59,12 @@ private:
uint32_t aggr_idx;
uint32_t ss_id;
uint32_t idx;
srsran_dci_ctx_t* dci;
srsran_rnti_type_t rnti_type;
bool is_dl;
const ue_carrier_params_t* ue;
};
srsran::bounded_vector<alloc_record, 2 * MAX_GRANTS> 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<coreset_region, SRSRAN_UE_DL_NR_MAX_NOF_CORESET>;
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;

@ -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);

@ -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;

@ -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 <typename... Args>
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>(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

@ -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)

@ -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

@ -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<pdcch_dl_t> dl_pdcchs,
srsran::const_span<pdcch_ul_t> ul_pdcchs)
{
srsran::optional_vector<coreset_bitmap> 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<sched_nr_impl::pdcch_dl_t> 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;
}
}
}

@ -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<sched_nr_impl::pdcch_dl_t> dl_pdcchs,
srsran::const_span<sched_nr_impl::pdcch_ul_t> ul_pddchs);
void test_dl_pdcch_consistency(const sched_nr_interface::cell_cfg_t& cell_cfg,
srsran::const_span<sched_nr_impl::pdcch_dl_t> dl_pdcch);
void test_pdsch_consistency(srsran::const_span<mac_interface_phy_nr::pdsch_t> dl_pdcch);

@ -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();
}
Loading…
Cancel
Save