nr,gnb,sched: changed pdsch_allocator interface and extended its respective unit tests

This commit is contained in:
Francisco 2021-12-10 18:24:28 +00:00 committed by Francisco Paisana
parent dd28f173b0
commit 632f2dbddd
8 changed files with 322 additions and 123 deletions

View File

@ -172,6 +172,9 @@ void phy_cfg_nr_default_t::make_pdsch_default(srsran_sch_hl_cfg_nr_t& pdsch)
pdsch.common_time_ra[0].sliv = srsran_ra_type2_to_riv(SRSRAN_NSYMB_PER_SLOT_NR - 1, 1, SRSRAN_NSYMB_PER_SLOT_NR);
pdsch.nof_common_time_ra = 1;
// Set contiguous PRBs as default
pdsch.alloc = srsran_resource_alloc_type1;
// Setup PDSCH DMRS type A position
pdsch.typeA_pos = srsran_dmrs_sch_typeA_pos_2;
}

View File

@ -94,15 +94,9 @@ struct bwp_params_t {
bwp_params_t(const cell_cfg_t& cell, const sched_args_t& sched_cfg_, uint32_t cc, uint32_t bwp_id);
uint32_t coreset_bw(uint32_t cs_id) const { return coresets[cs_id].bw; }
const bwp_rb_bitmap& coreset_prb_limits(uint32_t ss_id, srsran_dci_format_nr_t dci_fmt) const
{
if (used_common_prb_masks.contains(ss_id) and dci_fmt == srsran_dci_format_nr_1_0) {
return used_common_prb_masks[ss_id];
}
return cached_empty_prb_mask;
}
prb_interval coreset_prb_range(uint32_t cs_id) const { return coresets[cs_id].prb_limits; }
prb_interval dci_fmt_1_0_prb_lims(uint32_t cs_id) const { return coresets[cs_id].dci_1_0_prb_limits; }
bwp_rb_bitmap dci_fmt_1_0_excluded_prbs(uint32_t cs_id) const { return coresets[cs_id].usable_common_ss_prb_mask; }
const srsran_search_space_t* get_ss(uint32_t ss_id) const
{
@ -110,10 +104,11 @@ struct bwp_params_t {
}
private:
bwp_rb_bitmap cached_empty_prb_mask;
srsran::optional_vector<bwp_rb_bitmap> used_common_prb_masks;
bwp_rb_bitmap cached_empty_prb_mask;
struct coreset_cached_params {
uint32_t bw = 0;
prb_interval prb_limits;
prb_interval dci_1_0_prb_limits; /// See TS 38.214, section 5.1.2.2
bwp_rb_bitmap usable_common_ss_prb_mask;
};
srsran::optional_vector<coreset_cached_params> coresets;
};

View File

@ -28,47 +28,96 @@ public:
pdsch_allocator(const bwp_params_t& cfg_, uint32_t sl_index, pdsch_list_t& pdsch_lst);
/// Get available RBGs for allocation
rbg_bitmap occupied_rbgs(uint32_t ss_id, srsran_dci_format_nr_t dci_fmt) const
rbg_bitmap occupied_rbgs() const
{
return (dl_prbs | bwp_cfg.coreset_prb_limits(ss_id, dci_fmt)).rbgs();
// Note: in case, RBGs are used, dci format is not 1_0
return dl_prbs.rbgs();
}
/// Get available PRBs for allocation
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();
if (dci_fmt == srsran_dci_format_nr_1_0) {
const srsran_search_space_t* ss = bwp_cfg.get_ss(ss_id);
if (ss != nullptr and SRSRAN_SEARCH_SPACE_IS_COMMON(ss->type)) {
return (dl_prbs | bwp_cfg.dci_fmt_1_0_excluded_prbs(ss->coreset_id)).prbs();
}
}
return dl_prbs.prbs();
}
/// Checks if provided PDSCH arguments produce a valid PDSCH that fits into cell PRBs and does not collide with other
/// allocations
alloc_result is_grant_valid(uint32_t ss_id,
srsran_dci_format_nr_t dci_fmt,
const prb_grant& grant,
ue_carrier_params_t* ue = nullptr) const;
/// Verifies if the input arguments are valid for an SI allocation and grant doesnt collide with other grants
alloc_result is_si_grant_valid(uint32_t ss_id, const prb_grant& grant) const;
/// Verifies if the input arguments are valid for an RAR allocation and grant doesnt collide with other grants
alloc_result is_rar_grant_valid(const prb_grant& grant) const;
/// Verifies if the input arguments are valid for an UE allocation and grant doesnt collide with other grants
alloc_result is_ue_grant_valid(const ue_carrier_params_t& ue,
uint32_t ss_id,
srsran_dci_format_nr_t dci_fmt,
const prb_grant& grant) const;
/**
* @brief Tries to allocate PDSCH grant. Ensures that there are no collisions with other previous PDSCH allocations
* @param dci_ctx[in] PDCCH DL DCI context information
* @brief Tries to allocate UE PDSCH grant. Ensures that there are no collisions with other previous PDSCH allocations
* @param ss_id[in] SearchSpaceId used for allocation
* @param dci_fmt[in] Chosen DL DCI format
* @param grant[in] PRBs used for the grant
* @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
* @param ue[in] UE carrier parameters
* @param dci[out] DCI where frequency_assignment and time_assignment get stored.
* @return pdsch_t* of allocated PDSCH in case of success. alloc_result error code in case of failure
*/
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);
pdsch_alloc_result alloc_ue_pdsch(uint32_t ss_id,
srsran_dci_format_nr_t dci_fmt,
const prb_grant& grant,
const ue_carrier_params_t& ue,
srsran_dci_dl_nr_t& dci);
/// Similar to alloc_ue_pdsch, but it doesn't verify if input parameters are valid
pdsch_t& alloc_ue_pdsch_unchecked(uint32_t ss_id,
srsran_dci_format_nr_t dci_fmt,
const prb_grant& grant,
const ue_carrier_params_t& ue,
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] DCI where frequency and time assignment get stored.
* @brief Tries to allocate SI PDSCH grant. Ensures that there are no collisions with other previous PDSCH allocations
* @param ss_id[in] SearchSpaceId used for allocation
* @param grant[in] PRBs used for the grant
* @param dci[out] DCI where frequency_assignment and time_assignment get stored.
* @return pdsch_t* of allocated PDSCH in case of success. alloc_result error code in case of failure
*/
pdsch_t& alloc_pdsch_unchecked(const srsran_dci_ctx_t& dci_ctx, const prb_grant& grant, srsran_dci_dl_nr_t& dci);
pdsch_alloc_result alloc_si_pdsch(uint32_t ss_id, const prb_grant& grant, srsran_dci_dl_nr_t& dci);
/// Similar to alloc_si_pdsch, but it doesn't verify if input parameters are valid
pdsch_t& alloc_si_pdsch_unchecked(uint32_t ss_id, const prb_grant& grant, srsran_dci_dl_nr_t& dci);
/**
* @brief Tries to allocate RAR PDSCH grant. Ensures that there are no collisions with other previous PDSCH
* allocations
* @param grant[in] PRBs used for the grant
* @param dci[out] DCI where frequency_assignment and time_assignment get stored.
* @return pdsch_t* of allocated PDSCH in case of success. alloc_result error code in case of failure
*/
pdsch_alloc_result alloc_rar_pdsch(const prb_grant& grant, srsran_dci_dl_nr_t& dci);
/// Similar to alloc_rar_pdsch, but it doesn't verify if input parameters are valid
pdsch_t& alloc_rar_pdsch_unchecked(const prb_grant& grant, srsran_dci_dl_nr_t& dci);
/// Cancel last PDSCH allocation
void cancel_last_pdsch();
/// Clear all PDSCHs
void reset();
private:
alloc_result is_grant_valid_common(srsran_search_space_type_t ss_type,
srsran_dci_format_nr_t dci_fmt,
uint32_t coreset_id,
const prb_grant& grant) const;
pdsch_t& alloc_pdsch_unchecked(uint32_t coreset_id,
srsran_search_space_type_t ss_type,
srsran_dci_format_nr_t dci_fmt,
const prb_grant& grant,
srsran_dci_dl_nr_t& dci);
const bwp_params_t& bwp_cfg;
uint32_t slot_idx = 0;
@ -96,12 +145,13 @@ public:
/**
* @brief Tries to allocate PDSCH grant. Ensures that there are no collisions with other previous PDSCH allocations
* @param dci_ctx[in] PDCCH DL DCI context information
* @param ss_type[in] PDCCH chosen search space type
* @param grant[in] PRBs used for the grant
* @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
*/
pusch_alloc_result alloc_pusch(const srsran_dci_ctx_t& dci_ctx, const prb_grant& grant, srsran_dci_ul_nr_t& dci);
pusch_alloc_result
alloc_pusch(const srsran_search_space_type_t ss_type, const prb_grant& grant, srsran_dci_ul_nr_t& dci);
/**
* @brief Allocates PDSCH grant without verifying for collisions. Useful to avoid redundant is_grant_valid(...) calls

View File

@ -57,8 +57,20 @@ bwp_params_t::bwp_params_t(const cell_cfg_t& cell, const sched_args_t& sched_cfg
for (const srsran_coreset_t& cs : view_active_coresets(cfg.pdcch)) {
coresets.emplace(cs.id);
auto& cached_coreset = coresets[cs.id];
cached_coreset.bw = srsran_coreset_get_bw(&cs);
uint32_t rb_start = srsran_coreset_start_rb(&cs);
coresets[cs.id].prb_limits = prb_interval{rb_start, rb_start + srsran_coreset_get_bw(&cs)};
coresets[cs.id].usable_common_ss_prb_mask = cached_empty_prb_mask;
// TS 38.214, 5.1.2.2 - For DCI format 1_0 and common search space, lowest RB of the CORESET is the RB index = 0
coresets[cs.id].usable_common_ss_prb_mask |= prb_interval(0, rb_start);
coresets[cs.id].dci_1_0_prb_limits = prb_interval{rb_start, cfg.rb_width};
// TS 38.214, 5.1.2.2.2 - when DCI format 1_0, common search space and CORESET#0 is configured for the cell,
// RA type 1 allocs shall be within the CORESET#0 region
if (cfg.pdcch.coreset_present[0]) {
coresets[cs.id].dci_1_0_prb_limits = coresets[cs.id].prb_limits;
coresets[cs.id].usable_common_ss_prb_mask |= prb_interval(coresets[cs.id].prb_limits.stop(), cfg.rb_width);
}
}
// Derive params of individual slots
@ -116,16 +128,6 @@ bwp_params_t::bwp_params_t(const cell_cfg_t& cell, const sched_args_t& sched_cfg
ss_cce_list[sl][agg_idx].resize(n);
}
}
if (SRSRAN_SEARCH_SPACE_IS_COMMON(ss.type)) {
used_common_prb_masks.emplace(ss_id, cached_empty_prb_mask);
uint32_t coreset_start = srsran_coreset_start_rb(&cfg.pdcch.coreset[ss.coreset_id]);
used_common_prb_masks[ss_id] |= prb_interval(0, coreset_start);
if (ss.coreset_id == 0) {
uint32_t coreset0_bw = srsran_coreset_get_bw(&cfg.pdcch.coreset[0]);
used_common_prb_masks[ss_id] |= prb_interval(coreset_start + coreset0_bw, cfg.rb_width);
}
}
}
}

View File

@ -123,7 +123,7 @@ alloc_result bwp_slot_allocator::alloc_si(uint32_t aggr_idx,
bwp_slot_grid& bwp_pdcch_slot = bwp_grid[pdcch_slot];
// Verify there is space in PDSCH
alloc_result ret = bwp_pdcch_slot.pdschs.is_grant_valid(ss_id, dci_fmt, prbs);
alloc_result ret = bwp_pdcch_slot.pdschs.is_si_grant_valid(ss_id, prbs);
if (ret != alloc_result::success) {
return ret;
}
@ -137,7 +137,7 @@ alloc_result bwp_slot_allocator::alloc_si(uint32_t aggr_idx,
pdcch_dl_t& pdcch = *pdcch_result.value();
// Allocate PDSCH
pdsch_t& pdsch = bwp_pdcch_slot.pdschs.alloc_pdsch_unchecked(pdcch.dci.ctx, prbs, pdcch.dci);
pdsch_t& pdsch = bwp_pdcch_slot.pdschs.alloc_si_pdsch_unchecked(ss_id, prbs, pdcch.dci);
// Generate DCI for SIB
pdcch.dci_cfg.coreset0_bw = srsran_coreset_get_bw(&cfg.cfg.pdcch.coreset[0]);
@ -177,7 +177,7 @@ alloc_result bwp_slot_allocator::alloc_rar_and_msg3(uint16_t
bwp_slot_grid& bwp_msg3_slot = bwp_grid[msg3_slot];
// Verify there is space in PDSCH
alloc_result ret = bwp_pdcch_slot.pdschs.is_grant_valid(cfg.cfg.pdcch.ra_search_space.id, dci_fmt, interv);
alloc_result ret = bwp_pdcch_slot.pdschs.is_rar_grant_valid(interv);
if (ret != alloc_result::success) {
return ret;
}
@ -219,7 +219,7 @@ alloc_result bwp_slot_allocator::alloc_rar_and_msg3(uint16_t
pdcch_dl_t& pdcch = *pdcch_result.value();
// Allocate PDSCH
pdsch_t& pdsch = bwp_pdcch_slot.pdschs.alloc_pdsch_unchecked(pdcch.dci.ctx, interv, pdcch.dci);
pdsch_t& pdsch = bwp_pdcch_slot.pdschs.alloc_rar_pdsch_unchecked(interv, pdcch.dci);
// Generate DCI for RAR with given RA-RNTI
pdcch.dci_cfg = slot_ues[pending_rachs[0].temp_crnti]->get_dci_cfg();
@ -278,7 +278,7 @@ alloc_result bwp_slot_allocator::alloc_pdsch(slot_ue& ue, uint32_t ss_id, const
bwp_slot_grid& bwp_uci_slot = bwp_grid[ue.uci_slot]; // UCI : UL control info
// Verify there is space in PDSCH
alloc_result ret = bwp_pdcch_slot.pdschs.is_grant_valid(ss_id, dci_fmt, dl_grant);
alloc_result ret = bwp_pdcch_slot.pdschs.is_ue_grant_valid(ue.cfg(), ss_id, dci_fmt, dl_grant);
if (ret != alloc_result::success) {
return ret;
}
@ -309,7 +309,7 @@ alloc_result bwp_slot_allocator::alloc_pdsch(slot_ue& ue, uint32_t ss_id, const
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.dci);
pdsch_t& pdsch = bwp_pdcch_slot.pdschs.alloc_ue_pdsch_unchecked(ss_id, dci_fmt, dl_grant, ue.cfg(), pdcch.dci);
// Allocate HARQ
int mcs = ue->fixed_pdsch_mcs();
@ -470,9 +470,7 @@ prb_grant find_optimal_dl_grant(bwp_slot_allocator& slot_alloc, const slot_ue& u
{
static const srsran_dci_format_nr_t dci_fmt = srsran_dci_format_nr_1_0; // TODO: Support more DCI formats
const bwp_slot_grid& sl_grid = slot_alloc.res_grid()[ue.pdsch_slot];
prb_bitmap used_prb_mask = sl_grid.pdschs.occupied_prbs(ss_id, dci_fmt);
prb_bitmap used_prb_mask = slot_alloc.occupied_dl_prbs(ue.pdsch_slot, ss_id, dci_fmt);
prb_interval prb_interv = find_empty_interval_of_length(used_prb_mask, used_prb_mask.size(), 0);

View File

@ -45,7 +45,7 @@ void fill_dci_from_cfg(const bwp_params_t& bwp_cfg, srsran_dci_dl_nr_t& dci)
dci.bwp_id = bwp_cfg.bwp_id;
dci.cc_id = bwp_cfg.cc;
dci.tpc = 1;
dci.coreset0_bw = bwp_cfg.cfg.pdcch.coreset_present[0] ? bwp_cfg.coreset_bw(0) : 0;
dci.coreset0_bw = bwp_cfg.cfg.pdcch.coreset_present[0] ? bwp_cfg.coreset_prb_range(0).length() : 0;
}
void fill_dci_from_cfg(const bwp_params_t& bwp_cfg, srsran_dci_ul_nr_t& dci)

View File

@ -17,7 +17,7 @@ namespace srsenb {
namespace sched_nr_impl {
template <typename... Args>
void log_alloc_failure(srslog::log_channel& log_ch, uint32_t ss_id, const char* cause_fmt, Args&&... args)
void log_alloc_failure(srslog::log_channel& log_ch, const char* cause_fmt, Args&&... args)
{
if (not log_ch.enabled()) {
return;
@ -25,7 +25,7 @@ void log_alloc_failure(srslog::log_channel& log_ch, uint32_t ss_id, const char*
// Log allocation failure
fmt::memory_buffer fmtbuf;
fmt::format_to(fmtbuf, "SCHED: Failure to allocate PDSCH in SS#{}. Cause: ", ss_id);
fmt::format_to(fmtbuf, "SCHED: Failure to allocate PDSCH. Cause: ");
fmt::format_to(fmtbuf, cause_fmt, std::forward<Args>(args)...);
log_ch("%s", srsran::to_c_str(fmtbuf));
}
@ -43,70 +43,168 @@ void pdsch_allocator::reset()
dl_prbs.reset();
}
alloc_result pdsch_allocator::is_grant_valid(uint32_t ss_id,
srsran_dci_format_nr_t dci_fmt,
const prb_grant& grant,
ue_carrier_params_t* ue) const
alloc_result pdsch_allocator::is_grant_valid_common(srsran_search_space_type_t ss_type,
srsran_dci_format_nr_t dci_fmt,
uint32_t coreset_id,
const prb_grant& grant) const
{
// DL must be active in given slot
if (not bwp_cfg.slots[slot_idx].is_dl) {
log_alloc_failure(bwp_cfg.logger.error, ss_id, "DL is disabled for slot={}", slot_idx);
log_alloc_failure(bwp_cfg.logger.error, "DL is disabled for slot={}", slot_idx);
return alloc_result::no_sch_space;
}
// No space in Scheduler PDSCH output list
if (pdschs.full()) {
log_alloc_failure(bwp_cfg.logger.warning, ss_id, "Maximum number of PDSCHs={} reached.", pdschs.size());
log_alloc_failure(bwp_cfg.logger.warning, "Maximum number of PDSCHs={} reached.", pdschs.size());
return alloc_result::no_sch_space;
}
// Verify SearchSpace validity
const srsran_search_space_t* ss = (ue == nullptr) ? bwp_cfg.get_ss(ss_id) : ue->get_ss(ss_id);
if (ss == nullptr) {
// Couldn't find SearchSpace
log_alloc_failure(bwp_cfg.logger.error, ss_id, "SearchSpace has not been configured.");
// TS 38.214, 5.1.2.2 - "The UE shall assume that when the scheduling grant is received with DCI format 1_0, then
// downlink resource allocation type 1 is used."
if (dci_fmt == srsran_dci_format_nr_1_0 and not grant.is_alloc_type1()) {
log_alloc_failure(bwp_cfg.logger.warning, "DL Resource Allocation type 1 must be used in case of DCI format 1_0.");
return alloc_result::invalid_grant_params;
}
if (SRSRAN_SEARCH_SPACE_IS_COMMON(ss->type)) {
// In case of common SearchSpaces, the PRBs must be contiguous
if (grant.is_alloc_type0()) {
log_alloc_failure(bwp_cfg.logger.warning, ss_id, "AllocType0 not allowed in common SearchSpace.");
return alloc_result::invalid_grant_params;
}
// TS 38.214 - 5.1.2.2 - For DCI format 1_0 and Common Search Space, the list of available PRBs is limited by the
// rb_start and bandwidth of the coreset
if (dci_fmt == srsran_dci_format_nr_1_0 and SRSRAN_SEARCH_SPACE_IS_COMMON(ss_type)) {
// Grant PRBs do not collide with CORESET PRB limits (in case of common SearchSpace)
if (bwp_cfg.coreset_prb_limits(ss_id, dci_fmt).collides(grant)) {
bwp_cfg.logger.debug("SCHED: Provided RBG mask falls outside common CORESET PRB boundaries.");
if (bwp_cfg.dci_fmt_1_0_excluded_prbs(coreset_id).collides(grant)) {
log_alloc_failure(
bwp_cfg.logger.debug, "Provided PRB grant={:x} falls outside common CORESET PRB boundaries.", grant);
return alloc_result::sch_collision;
}
}
// Grant PRBs do not collide with previous PDSCH allocations
if (dl_prbs.collides(grant)) {
bwp_cfg.logger.debug("SCHED: Provided RBG mask collides with allocation previously made.");
log_alloc_failure(
bwp_cfg.logger.debug, "Provided PRB grant={:x} collides with allocations previously made.", grant);
return alloc_result::sch_collision;
}
return alloc_result::success;
}
srsran::expected<pdsch_t*, alloc_result> 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 pdsch_allocator::is_si_grant_valid(uint32_t ss_id, const prb_grant& grant) const
{
alloc_result code = is_grant_valid(ss_id, dci_ctx.format, grant);
// Verify SearchSpace validity
const srsran_search_space_t* ss = bwp_cfg.get_ss(ss_id);
if (ss == nullptr) {
// Couldn't find SearchSpace
log_alloc_failure(bwp_cfg.logger.error, "SearchSpace has not been configured.");
return alloc_result::invalid_grant_params;
}
return is_grant_valid_common(ss->type, srsran_dci_format_nr_1_0, ss->coreset_id, grant);
}
alloc_result pdsch_allocator::is_rar_grant_valid(const prb_grant& grant) const
{
srsran_sanity_check(bwp_cfg.cfg.pdcch.ra_search_space_present,
"Attempting RAR allocation in BWP with no raSearchSpace");
return is_grant_valid_common(bwp_cfg.cfg.pdcch.ra_search_space.type,
srsran_dci_format_nr_1_0,
bwp_cfg.cfg.pdcch.ra_search_space.coreset_id,
grant);
}
alloc_result pdsch_allocator::is_ue_grant_valid(const ue_carrier_params_t& ue,
uint32_t ss_id,
srsran_dci_format_nr_t dci_fmt,
const prb_grant& grant) const
{
const srsran_search_space_t* ss = ue.get_ss(ss_id);
if (ss == nullptr) {
// Couldn't find SearchSpace
log_alloc_failure(bwp_cfg.logger.error, "rnti=0x%x,SearchSpaceId={} has not been configured.", ue.rnti, ss_id);
return alloc_result::invalid_grant_params;
}
alloc_result alloc_result = is_grant_valid_common(ss->type, dci_fmt, ss->coreset_id, grant);
if (alloc_result != alloc_result::success) {
return alloc_result;
}
// TS 38.214, 5.1.2.2 - "the UE shall use the downlink frequency resource allocation type as defined by the higher
// layer parameter resourceAllocation"
if (ue.phy().pdsch.alloc != srsran_resource_alloc_dynamic) {
if ((ue.phy().pdsch.alloc == srsran_resource_alloc_type0) != grant.is_alloc_type0()) {
log_alloc_failure(bwp_cfg.logger.warning,
"UE rnti=0x{:x} PDSCH RA configuration type {} doesn't match grant type",
ue.rnti,
grant.is_alloc_type0() ? 0 : 1);
return alloc_result::invalid_grant_params;
}
}
return alloc_result::success;
}
pdsch_alloc_result pdsch_allocator::alloc_si_pdsch(uint32_t ss_id, const prb_grant& grant, srsran_dci_dl_nr_t& dci)
{
alloc_result code = is_si_grant_valid(ss_id, grant);
if (code != alloc_result::success) {
return code;
}
return {&alloc_pdsch_unchecked(dci_ctx, grant, dci)};
return {&alloc_si_pdsch_unchecked(ss_id, grant, dci)};
}
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)
pdsch_t& pdsch_allocator::alloc_si_pdsch_unchecked(uint32_t ss_id, const prb_grant& grant, srsran_dci_dl_nr_t& dci)
{
// Verify SearchSpace validity
const srsran_search_space_t* ss = bwp_cfg.get_ss(ss_id);
srsran_sanity_check(ss != nullptr, "SearchSpace has not been configured");
return alloc_pdsch_unchecked(ss->coreset_id, ss->type, srsran_dci_format_nr_1_0, grant, dci);
}
pdsch_alloc_result pdsch_allocator::alloc_rar_pdsch(const prb_grant& grant, srsran_dci_dl_nr_t& dci)
{
alloc_result code = is_rar_grant_valid(grant);
if (code != alloc_result::success) {
return code;
}
return {&alloc_rar_pdsch_unchecked(grant, dci)};
}
pdsch_t& pdsch_allocator::alloc_rar_pdsch_unchecked(const prb_grant& grant, srsran_dci_dl_nr_t& dci)
{
// TS 38.213, 8.2 - "In response to a PRACH transmission, a UE attempts to detect a DCI format 1_0"
const static srsran_dci_format_nr_t dci_fmt = srsran_dci_format_nr_1_0;
return alloc_pdsch_unchecked(
bwp_cfg.cfg.pdcch.ra_search_space.coreset_id, bwp_cfg.cfg.pdcch.ra_search_space.type, dci_fmt, grant, dci);
}
pdsch_alloc_result pdsch_allocator::alloc_ue_pdsch(uint32_t ss_id,
srsran_dci_format_nr_t dci_fmt,
const prb_grant& grant,
const ue_carrier_params_t& ue,
srsran_dci_dl_nr_t& dci)
{
alloc_result code = is_ue_grant_valid(ue, ss_id, dci_fmt, grant);
if (code != alloc_result::success) {
return code;
}
return {&alloc_ue_pdsch_unchecked(ss_id, dci_fmt, grant, ue, dci)};
}
pdsch_t& pdsch_allocator::alloc_ue_pdsch_unchecked(uint32_t ss_id,
srsran_dci_format_nr_t dci_fmt,
const prb_grant& grant,
const ue_carrier_params_t& ue,
srsran_dci_dl_nr_t& dci)
{
const srsran_search_space_t* ss = ue.get_ss(ss_id);
srsran_sanity_check(ss != nullptr, "SearchSpace has not been configured");
return alloc_pdsch_unchecked(ss->coreset_id, ss->type, dci_fmt, grant, dci);
}
pdsch_t& pdsch_allocator::alloc_pdsch_unchecked(uint32_t coreset_id,
srsran_search_space_type_t ss_type,
srsran_dci_format_nr_t dci_fmt,
const prb_grant& grant,
srsran_dci_dl_nr_t& out_dci)
{
// Create new PDSCH entry in output PDSCH list
pdschs.emplace_back();
@ -121,12 +219,14 @@ pdsch_t& pdsch_allocator::alloc_pdsch_unchecked(const srsran_dci_ctx_t& dci_ctx,
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)) {
if (dci_ctx.format == srsran_dci_format_nr_1_0) {
rb_start -= dci_ctx.coreset_start_rb;
if (SRSRAN_SEARCH_SPACE_IS_COMMON(ss_type)) {
prb_interval lims = bwp_cfg.coreset_prb_range(coreset_id);
if (dci_fmt == srsran_dci_format_nr_1_0) {
srsran_sanity_check(rb_start >= lims.start(), "Invalid PRB grant");
rb_start -= lims.start();
}
if (dci_ctx.coreset_id == 0) {
nof_prb = bwp_cfg.coreset_bw(0);
if (coreset_id == 0) {
nof_prb = lims.length();
}
}
srsran_sanity_check(rb_start + grant.prbs().length() <= nof_prb, "Invalid PRB grant");
@ -140,6 +240,7 @@ void pdsch_allocator::cancel_last_pdsch()
{
srsran_assert(not pdschs.empty(), "Trying to abort PDSCH allocation that does not exist");
pdschs.pop_back();
// TODO: clear bitmap allocated RBs
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -220,9 +321,9 @@ pusch_allocator::is_grant_valid(srsran_search_space_type_t ss_type, const prb_gr
}
pusch_alloc_result
pusch_allocator::alloc_pusch(const srsran_dci_ctx_t& dci_ctx, const prb_grant& grant, srsran_dci_ul_nr_t& dci)
pusch_allocator::alloc_pusch(const srsran_search_space_type_t ss_type, const prb_grant& grant, srsran_dci_ul_nr_t& dci)
{
alloc_result code = is_grant_valid(dci_ctx.ss_type, grant);
alloc_result code = is_grant_valid(ss_type, grant);
if (code != alloc_result::success) {
return code;
}

View File

@ -65,8 +65,7 @@ void test_dci_freq_assignment(const bwp_params_t& bwp_params, prb_interval grant
// 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()};
lims = bwp_params.dci_fmt_1_0_prb_lims(pdcch.dci.ctx.coreset_id);
}
// RB indexing should start from the first PRB of CORESET
@ -99,8 +98,8 @@ void test_si()
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);
uint32_t min_prb = bwp_params.dci_fmt_1_0_prb_lims(pdcch.dci.ctx.coreset_id).start();
uint32_t max_prb = bwp_params.dci_fmt_1_0_prb_lims(pdcch.dci.ctx.coreset_id).stop();
std::array<prb_interval, 3> grant_list = {
prb_interval{2, 4}, prb_interval{min_prb, max_prb}, prb_interval{0, bwp_params.nof_prb()}};
@ -113,13 +112,13 @@ void test_si()
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);
alloc_result check_ret = pdsch_sched.is_si_grant_valid(ss_id, 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);
alloc_res = pdsch_sched.alloc_si_pdsch(ss_id, grant, pdcch.dci);
if (success_expected) {
// SIB1 allocation doesnt go outside CORESET#0 BW
TESTASSERT(alloc_res.has_value());
@ -157,8 +156,8 @@ void test_rar()
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);
uint32_t min_prb = bwp_params.dci_fmt_1_0_prb_lims(pdcch.dci.ctx.coreset_id).start();
uint32_t max_prb = bwp_params.dci_fmt_1_0_prb_lims(pdcch.dci.ctx.coreset_id).stop();
std::array<prb_interval, 3> grant_list = {
prb_interval{2, 4}, prb_interval{min_prb, max_prb}, prb_interval{0, bwp_params.nof_prb()}};
@ -171,13 +170,13 @@ void test_rar()
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);
alloc_result check_ret = pdsch_sched.is_rar_grant_valid(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);
alloc_res = pdsch_sched.alloc_rar_pdsch(grant, pdcch.dci);
if (success_expected) {
// SIB1 allocation doesnt go outside CORESET#0 BW
TESTASSERT(alloc_res.has_value());
@ -217,8 +216,7 @@ void test_ue_pdsch()
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_common = bwp_params.dci_fmt_1_0_prb_lims(pdcch_common.dci.ctx.coreset_id);
prb_interval lims_ue{0, bwp_params.nof_prb()};
std::array<std::pair<uint32_t, prb_interval>, 4> grant_list = {std::make_pair(1, prb_interval{2, 4}),
@ -238,7 +236,7 @@ void test_ue_pdsch()
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);
alloc_result check_ret = pdsch_sched.is_ue_grant_valid(ue_cc, 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);
@ -246,10 +244,10 @@ void test_ue_pdsch()
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);
alloc_res = pdsch_sched.alloc_ue_pdsch(ss_id, srsran_dci_format_nr_1_0, grant, ue_cc, pdcch.dci);
TESTASSERT(success_expected == alloc_res.has_value());
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);
@ -258,7 +256,6 @@ void test_ue_pdsch()
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()));
@ -266,6 +263,55 @@ void test_ue_pdsch()
}
}
void test_pdsch_fail()
{
srsran::test_delimit_logger delimiter{"Test PDSCH Allocation Failure"};
// 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_rar, pdcch_si, pdcch;
pdcch_si.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, 0, srsran_rnti_type_si, SRSRAN_SIRNTI);
pdcch_rar.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, 1, srsran_rnti_type_ra, 0x2);
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);
// Allocations of type 0 are not compatible with DCI format 1_0
rbg_bitmap rbgs(bwp_params.N_rbg);
rbgs.set(1);
rbgs.set(3);
prb_grant grant_type0 = rbgs;
TESTASSERT_EQ(alloc_result::invalid_grant_params, pdsch_sched.alloc_si_pdsch(0, grant_type0, pdcch_si.dci).error());
TESTASSERT_EQ(alloc_result::invalid_grant_params, pdsch_sched.alloc_rar_pdsch(grant_type0, pdcch_rar.dci).error());
TESTASSERT_EQ(alloc_result::invalid_grant_params,
pdsch_sched.alloc_ue_pdsch(1, srsran_dci_format_nr_1_0, grant_type0, ue_cc, pdcch.dci).error());
TESTASSERT_EQ(alloc_result::invalid_grant_params,
pdsch_sched.alloc_ue_pdsch(2, srsran_dci_format_nr_1_0, grant_type0, ue_cc, pdcch.dci).error());
// Resource Allocation type must be compatible with UE PDSCH configuration
TESTASSERT_EQ(alloc_result::invalid_grant_params,
pdsch_sched.alloc_ue_pdsch(2, srsran_dci_format_nr_1_1, grant_type0, ue_cc, pdcch.dci).error());
// Allocations of DCI format 1_0 should start from CORESET first RB and their BW should be limited by CORESET#0 BW
prb_grant grant_type1 = prb_interval{0, bwp_params.coreset_prb_range(0).stop()};
TESTASSERT(pdsch_sched.alloc_ue_pdsch(1, srsran_dci_format_nr_1_0, grant_type1, ue_cc, pdcch.dci).is_error());
grant_type1 = prb_interval{bwp_params.coreset_prb_range(0).start(), bwp_params.nof_prb()};
TESTASSERT(pdsch_sched.alloc_ue_pdsch(1, srsran_dci_format_nr_1_0, grant_type1, ue_cc, pdcch.dci).is_error());
TESTASSERT(pdsch_sched.alloc_ue_pdsch(2, srsran_dci_format_nr_1_0, grant_type1, ue_cc, pdcch.dci).has_value());
// PRB collisions are detected
TESTASSERT(pdsch_sched.alloc_ue_pdsch(2, srsran_dci_format_nr_1_0, prb_interval{5, 6}, ue_cc, pdcch.dci).is_error());
}
void test_multi_pdsch()
{
srsran::test_delimit_logger delimiter{"Test Multiple PDSCH Allocations"};
@ -297,13 +343,13 @@ void test_multi_pdsch()
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_EQ(alloc_result::success, pdsch_sched.is_si_grant_valid(ss_id, sib_grant));
alloc_res = pdsch_sched.alloc_si_pdsch(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));
TESTASSERT_EQ(alloc_result::sch_collision, pdsch_sched.is_si_grant_valid(ss_id, sib_grant));
prb_bitmap last_prb_bitmap(used_prbs.size());
last_prb_bitmap.fill(sib_grant.start(), sib_grant.stop());
@ -316,13 +362,14 @@ void test_multi_pdsch()
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_EQ(alloc_result::success, pdsch_sched.is_ue_grant_valid(ue_cc, ss_id, srsran_dci_format_nr_1_0, ue_grant));
alloc_res = pdsch_sched.alloc_ue_pdsch(ss_id, srsran_dci_format_nr_1_0, ue_grant, ue_cc, 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));
TESTASSERT_EQ(alloc_result::sch_collision,
pdsch_sched.is_ue_grant_valid(ue_cc, ss_id, srsran_dci_format_nr_1_0, ue_grant));
last_prb_bitmap.reset();
last_prb_bitmap.fill(ue_grant.start(), ue_grant.stop());
@ -334,13 +381,15 @@ void test_multi_pdsch()
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_EQ(alloc_result::success,
pdsch_sched.is_ue_grant_valid(ue_cc, ss_id, srsran_dci_format_nr_1_0, ue_grant2));
alloc_res = pdsch_sched.alloc_ue_pdsch(ss_id, srsran_dci_format_nr_1_0, ue_grant2, ue_cc, 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));
TESTASSERT_EQ(alloc_result::sch_collision,
pdsch_sched.is_ue_grant_valid(ue_cc, ss_id, srsran_dci_format_nr_1_0, ue_grant2));
last_prb_bitmap.reset();
last_prb_bitmap.fill(ue_grant2.start(), ue_grant2.stop());
@ -382,7 +431,7 @@ void test_multi_pusch()
uint32_t ue_grant_size = 10;
prb_interval ue_grant = find_empty_interval_of_length(used_prbs, ue_grant_size);
TESTASSERT_EQ(alloc_result::success, pusch_sched.is_grant_valid(srsran_search_space_type_common_1, ue_grant));
alloc_res = pusch_sched.alloc_pusch(pdcch->dci.ctx, ue_grant, pdcch->dci);
alloc_res = pusch_sched.alloc_pusch(pdcch->dci.ctx.ss_type, ue_grant, pdcch->dci);
TESTASSERT(alloc_res.has_value());
prb_bitmap used_prbs_ue1 = pusch_sched.occupied_prbs();
TESTASSERT_EQ(used_prbs_ue1.count(), used_prbs.count() + ue_grant.length());
@ -399,7 +448,7 @@ void test_multi_pusch()
used_prbs = pusch_sched.occupied_prbs();
prb_interval ue2_grant = find_empty_interval_of_length(used_prbs, used_prbs.size());
TESTASSERT_EQ(alloc_result::success, pusch_sched.is_grant_valid(srsran_search_space_type_ue, ue2_grant));
alloc_res = pusch_sched.alloc_pusch(pdcch->dci.ctx, ue2_grant, pdcch->dci);
alloc_res = pusch_sched.alloc_pusch(pdcch->dci.ctx.ss_type, ue2_grant, pdcch->dci);
TESTASSERT(alloc_res.has_value());
prb_bitmap used_prbs_ue2 = pusch_sched.occupied_prbs();
TESTASSERT_EQ(used_prbs_ue2.count(), used_prbs.count() + ue2_grant.length());
@ -427,6 +476,7 @@ int main()
srsenb::test_si();
srsenb::test_rar();
srsenb::test_ue_pdsch();
srsenb::test_pdsch_fail();
srsenb::test_multi_pdsch();
srsenb::test_multi_pusch();
}