mirror of https://github.com/pvnis/srsRAN_4G.git
sched,nr: implement NR PDCCH allocation algorithm
parent
a70ad58440
commit
5b3c5ded63
@ -0,0 +1,103 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SRSRAN_SCHED_NR_PDCCH_H
|
||||||
|
#define SRSRAN_SCHED_NR_PDCCH_H
|
||||||
|
|
||||||
|
#include "srsenb/hdr/stack/mac/nr/sched_nr_common.h"
|
||||||
|
#include "srsran/adt/bounded_bitset.h"
|
||||||
|
#include "srsran/adt/bounded_vector.h"
|
||||||
|
#include "srsran/phy/common/phy_common_nr.h"
|
||||||
|
#include "srsran/phy/phch/dci.h"
|
||||||
|
|
||||||
|
namespace srsenb {
|
||||||
|
|
||||||
|
namespace sched_nr_impl {
|
||||||
|
|
||||||
|
using coreset_bitmap = srsran::bounded_bitset<SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE * SRSRAN_CORESET_DURATION_MAX, true>;
|
||||||
|
|
||||||
|
enum class pdcch_grant_type_t { sib, dl_data, ul_data };
|
||||||
|
|
||||||
|
class slot_ue;
|
||||||
|
|
||||||
|
struct pdcch_dl_t {
|
||||||
|
srsran_dci_cfg_nr_t dci_cfg = {};
|
||||||
|
srsran_dci_dl_nr_t dci = {};
|
||||||
|
};
|
||||||
|
static const size_t MAX_NOF_PDCCH_DL_GRANTS = 16;
|
||||||
|
using pdcch_dl_list_t = srsran::bounded_vector<pdcch_dl_t, MAX_NOF_PDCCH_DL_GRANTS>;
|
||||||
|
|
||||||
|
class coreset_region
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
coreset_region(uint32_t bwp_id_,
|
||||||
|
uint32_t slot_idx,
|
||||||
|
uint32_t nof_td_symbols,
|
||||||
|
uint32_t nof_freq_resources,
|
||||||
|
pdcch_dl_list_t& pdcch_list);
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocates DCI space in PDCCH, avoiding in the process collisions with other users
|
||||||
|
* @param pdcch_grant_type_t allocation type (e.g. DL data, UL data, SIB)
|
||||||
|
* @param aggr_idx Aggregation level index (0..4)
|
||||||
|
* @param user UE object or null in case of broadcast/RAR/paging allocation
|
||||||
|
* @return if the allocation was successful
|
||||||
|
*/
|
||||||
|
bool alloc_dci(pdcch_grant_type_t alloc_type, uint32_t aggr_idx, uint32_t coreset_id, slot_ue* user = nullptr);
|
||||||
|
|
||||||
|
void rem_last_dci();
|
||||||
|
|
||||||
|
uint32_t get_td_symbols() const { return nof_symbols; }
|
||||||
|
uint32_t get_freq_resources() const { return nof_freq_res; }
|
||||||
|
uint32_t nof_cces() const { return nof_freq_res * nof_symbols; }
|
||||||
|
size_t nof_allocs() const { return dfs_tree.size(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint32_t bwp_id;
|
||||||
|
uint32_t slot_idx;
|
||||||
|
uint32_t nof_symbols;
|
||||||
|
uint32_t nof_freq_res;
|
||||||
|
|
||||||
|
// List of PDCCH grants
|
||||||
|
struct alloc_record {
|
||||||
|
uint32_t coreset_id;
|
||||||
|
uint32_t aggr_idx;
|
||||||
|
uint32_t idx;
|
||||||
|
pdcch_grant_type_t alloc_type;
|
||||||
|
slot_ue* ue;
|
||||||
|
};
|
||||||
|
srsran::bounded_vector<alloc_record, MAX_NOF_PDCCH_DL_GRANTS> dci_list;
|
||||||
|
pdcch_dl_list_t& pdcch_dl_list;
|
||||||
|
|
||||||
|
// DFS decision tree of PDCCH grants
|
||||||
|
struct tree_node {
|
||||||
|
uint16_t rnti = SRSRAN_INVALID_RNTI;
|
||||||
|
uint32_t record_idx = 0;
|
||||||
|
uint32_t dci_pos_idx = 0;
|
||||||
|
srsran_dci_location_t dci_pos = {0, 0};
|
||||||
|
/// Accumulation of all PDCCH masks for the current solution (DFS path)
|
||||||
|
coreset_bitmap total_mask, current_mask;
|
||||||
|
};
|
||||||
|
using alloc_tree_dfs_t = srsran::bounded_vector<tree_node, MAX_NOF_PDCCH_DL_GRANTS>;
|
||||||
|
alloc_tree_dfs_t dfs_tree, saved_dfs_tree;
|
||||||
|
|
||||||
|
srsran::span<const uint32_t> get_cce_loc_table(const alloc_record& record) const;
|
||||||
|
bool alloc_dfs_node(const alloc_record& record, uint32_t dci_idx);
|
||||||
|
bool get_next_dfs();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace sched_nr_impl
|
||||||
|
|
||||||
|
} // namespace srsenb
|
||||||
|
|
||||||
|
#endif // SRSRAN_SCHED_NR_PDCCH_H
|
@ -0,0 +1,93 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SRSRAN_SCHED_LTE_COMMON_H
|
||||||
|
#define SRSRAN_SCHED_LTE_COMMON_H
|
||||||
|
|
||||||
|
#include "srsran/adt/bounded_bitset.h"
|
||||||
|
#include "srsran/common/tti_point.h"
|
||||||
|
#include "srsran/interfaces/sched_interface.h"
|
||||||
|
|
||||||
|
namespace srsenb {
|
||||||
|
|
||||||
|
/***********************
|
||||||
|
* Constants
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
constexpr float tti_duration_ms = 1;
|
||||||
|
constexpr uint32_t NOF_AGGR_LEVEL = 4;
|
||||||
|
|
||||||
|
/***********************
|
||||||
|
* Helper Types
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
/// List of CCE start positions in PDCCH
|
||||||
|
using cce_position_list = srsran::bounded_vector<uint32_t, 6>;
|
||||||
|
|
||||||
|
/// Map {L} -> list of CCE positions
|
||||||
|
using cce_cfi_position_table = std::array<cce_position_list, NOF_AGGR_LEVEL>;
|
||||||
|
|
||||||
|
/// Map {cfi, L} -> list of CCE positions
|
||||||
|
using cce_sf_position_table = std::array<std::array<cce_position_list, NOF_AGGR_LEVEL>, SRSRAN_NOF_CFI>;
|
||||||
|
|
||||||
|
/// Map {sf, cfi, L} -> list of CCE positions
|
||||||
|
using cce_frame_position_table = std::array<cce_sf_position_table, SRSRAN_NOF_SF_X_FRAME>;
|
||||||
|
|
||||||
|
/// structs to bundle together all the sched arguments, and share them with all the sched sub-components
|
||||||
|
class sched_cell_params_t
|
||||||
|
{
|
||||||
|
struct regs_deleter {
|
||||||
|
void operator()(srsran_regs_t* p);
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool set_cfg(uint32_t enb_cc_idx_,
|
||||||
|
const sched_interface::cell_cfg_t& cfg_,
|
||||||
|
const sched_interface::sched_args_t& sched_args);
|
||||||
|
// convenience getters
|
||||||
|
uint32_t nof_prbs_to_rbgs(uint32_t nof_prbs) const { return srsran::ceil_div(nof_prbs, P); }
|
||||||
|
uint32_t nof_prb() const { return cfg.cell.nof_prb; }
|
||||||
|
uint32_t get_dl_lb_nof_re(tti_point tti_tx_dl, uint32_t nof_prbs_alloc) const;
|
||||||
|
uint32_t get_dl_nof_res(srsran::tti_point tti_tx_dl, const srsran_dci_dl_t& dci, uint32_t cfi) const;
|
||||||
|
|
||||||
|
uint32_t enb_cc_idx = 0;
|
||||||
|
sched_interface::cell_cfg_t cfg = {};
|
||||||
|
srsran_pucch_cfg_t pucch_cfg_common = {};
|
||||||
|
const sched_interface::sched_args_t* sched_cfg = nullptr;
|
||||||
|
std::unique_ptr<srsran_regs_t, regs_deleter> regs;
|
||||||
|
cce_sf_position_table common_locations = {};
|
||||||
|
cce_frame_position_table rar_locations = {};
|
||||||
|
std::array<uint32_t, SRSRAN_NOF_CFI> nof_cce_table = {}; ///< map cfix -> nof cces in PDCCH
|
||||||
|
uint32_t P = 0;
|
||||||
|
uint32_t nof_rbgs = 0;
|
||||||
|
|
||||||
|
using dl_nof_re_table = srsran::bounded_vector<
|
||||||
|
std::array<std::array<std::array<uint32_t, SRSRAN_NOF_CFI>, SRSRAN_NOF_SLOTS_PER_SF>, SRSRAN_NOF_SF_X_FRAME>,
|
||||||
|
SRSRAN_MAX_PRB>;
|
||||||
|
using dl_lb_nof_re_table = std::array<srsran::bounded_vector<uint32_t, SRSRAN_MAX_PRB>, SRSRAN_NOF_SF_X_FRAME>;
|
||||||
|
|
||||||
|
/// Table of nof REs
|
||||||
|
dl_nof_re_table nof_re_table;
|
||||||
|
/// Cached computation of Lower bound of nof REs
|
||||||
|
dl_lb_nof_re_table nof_re_lb_table;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Type of Allocation stored in PDSCH/PUSCH
|
||||||
|
enum class alloc_type_t { DL_BC, DL_PCCH, DL_RAR, DL_DATA, UL_DATA };
|
||||||
|
inline bool is_dl_ctrl_alloc(alloc_type_t a)
|
||||||
|
{
|
||||||
|
return a == alloc_type_t::DL_BC or a == alloc_type_t::DL_PCCH or a == alloc_type_t::DL_RAR;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace srsenb
|
||||||
|
|
||||||
|
#endif // SRSRAN_SCHED_LTE_COMMON_H
|
@ -0,0 +1,43 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \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 "srsenb/hdr/stack/mac/nr/sched_nr_common.h"
|
||||||
|
|
||||||
|
namespace srsenb {
|
||||||
|
namespace sched_nr_impl {
|
||||||
|
|
||||||
|
sched_cell_params::sched_cell_params(uint32_t cc_, const cell_cfg_t& cell, const sched_cfg_t& sched_cfg_) :
|
||||||
|
cc(cc_), cell_cfg(cell), sched_cfg(sched_cfg_)
|
||||||
|
{}
|
||||||
|
|
||||||
|
sched_params::sched_params(const sched_cfg_t& sched_cfg_) : sched_cfg(sched_cfg_) {}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void get_dci_locs(const srsran_coreset_t& coreset,
|
||||||
|
const srsran_search_space_t& search_space,
|
||||||
|
uint16_t rnti,
|
||||||
|
bwp_cce_pos_list& cce_locs)
|
||||||
|
{
|
||||||
|
for (uint32_t sl = 0; sl < SRSRAN_NOF_SF_X_FRAME; ++sl) {
|
||||||
|
for (uint32_t agg_idx = 0; agg_idx < MAX_NOF_AGGR_LEVELS; ++agg_idx) {
|
||||||
|
pdcch_cce_pos_list pdcch_locs;
|
||||||
|
cce_locs[sl][agg_idx].resize(pdcch_locs.capacity());
|
||||||
|
uint32_t n =
|
||||||
|
srsran_pdcch_nr_locations_coreset(&coreset, &search_space, rnti, agg_idx, sl, cce_locs[sl][agg_idx].data());
|
||||||
|
cce_locs[sl][agg_idx].resize(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace sched_nr_impl
|
||||||
|
} // namespace srsenb
|
@ -0,0 +1,167 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \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 "srsenb/hdr/stack/mac/nr/sched_nr_pdcch.h"
|
||||||
|
#include "srsenb/hdr/stack/mac/nr/sched_nr_ue.h"
|
||||||
|
|
||||||
|
namespace srsenb {
|
||||||
|
namespace sched_nr_impl {
|
||||||
|
|
||||||
|
coreset_region::coreset_region(uint32_t bwp_id_,
|
||||||
|
uint32_t slot_idx_,
|
||||||
|
uint32_t nof_td_symbols,
|
||||||
|
uint32_t nof_freq_resources,
|
||||||
|
pdcch_dl_list_t& pdcch_list_) :
|
||||||
|
bwp_id(bwp_id_),
|
||||||
|
slot_idx(slot_idx_),
|
||||||
|
nof_symbols(nof_td_symbols),
|
||||||
|
nof_freq_res(nof_freq_resources),
|
||||||
|
pdcch_dl_list(pdcch_list_)
|
||||||
|
{
|
||||||
|
srsran_assert(nof_td_symbols <= SRSRAN_CORESET_DURATION_MAX,
|
||||||
|
"Possible number of time-domain OFDM symbols in CORESET must be within {1,2,3}");
|
||||||
|
srsran_assert(nof_freq_resources <= SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE,
|
||||||
|
"Provided number of CORESET freq domain resources=%d is too high",
|
||||||
|
nof_freq_resources);
|
||||||
|
}
|
||||||
|
|
||||||
|
void coreset_region::reset()
|
||||||
|
{
|
||||||
|
dfs_tree.clear();
|
||||||
|
saved_dfs_tree.clear();
|
||||||
|
dci_list.clear();
|
||||||
|
pdcch_dl_list.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool coreset_region::alloc_dci(pdcch_grant_type_t alloc_type, uint32_t aggr_idx, uint32_t coreset_id, slot_ue* user)
|
||||||
|
{
|
||||||
|
srsran_assert(aggr_idx <= 4, "Invalid DCI aggregation level=%d", 1U << aggr_idx);
|
||||||
|
srsran_assert((user == nullptr) xor
|
||||||
|
(alloc_type == pdcch_grant_type_t::dl_data or alloc_type == pdcch_grant_type_t::ul_data),
|
||||||
|
"UE should be only provided for DL or UL data allocations");
|
||||||
|
saved_dfs_tree.clear();
|
||||||
|
|
||||||
|
alloc_record record;
|
||||||
|
record.ue = user;
|
||||||
|
record.aggr_idx = aggr_idx;
|
||||||
|
record.alloc_type = alloc_type;
|
||||||
|
record.idx = pdcch_dl_list.size();
|
||||||
|
record.coreset_id = coreset_id;
|
||||||
|
pdcch_dl_list.emplace_back();
|
||||||
|
|
||||||
|
// Try to allocate grant. If it fails, attempt the same grant, but using a different permutation of past grant DCI
|
||||||
|
// positions
|
||||||
|
do {
|
||||||
|
bool success = alloc_dfs_node(record, 0);
|
||||||
|
if (success) {
|
||||||
|
// DCI record allocation successful
|
||||||
|
dci_list.push_back(record);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (dfs_tree.empty()) {
|
||||||
|
saved_dfs_tree = dfs_tree;
|
||||||
|
}
|
||||||
|
} while (get_next_dfs());
|
||||||
|
|
||||||
|
// Revert steps to initial state, before dci record allocation was attempted
|
||||||
|
dfs_tree = saved_dfs_tree;
|
||||||
|
pdcch_dl_list.pop_back();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void coreset_region::rem_last_dci()
|
||||||
|
{
|
||||||
|
srsran_assert(not dci_list.empty(), "%s called when no PDCCH have yet been allocated", __FUNCTION__);
|
||||||
|
|
||||||
|
// Remove DCI record
|
||||||
|
dfs_tree.pop_back();
|
||||||
|
dci_list.pop_back();
|
||||||
|
pdcch_dl_list.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool coreset_region::get_next_dfs()
|
||||||
|
{
|
||||||
|
do {
|
||||||
|
if (dfs_tree.empty()) {
|
||||||
|
// If we reach root, the allocation failed
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Attempt to re-add last tree node, but with a higher node child index
|
||||||
|
uint32_t start_child_idx = dfs_tree.back().dci_pos_idx + 1;
|
||||||
|
dfs_tree.pop_back();
|
||||||
|
while (dfs_tree.size() < dci_list.size() and alloc_dfs_node(dci_list[dfs_tree.size()], start_child_idx)) {
|
||||||
|
start_child_idx = 0;
|
||||||
|
}
|
||||||
|
} while (dfs_tree.size() < dci_list.size());
|
||||||
|
|
||||||
|
// Finished computation of next DFS node
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool coreset_region::alloc_dfs_node(const alloc_record& record, uint32_t start_dci_idx)
|
||||||
|
{
|
||||||
|
alloc_tree_dfs_t& alloc_dfs = dfs_tree;
|
||||||
|
// Get DCI Location Table
|
||||||
|
auto cce_locs = get_cce_loc_table(record);
|
||||||
|
if (start_dci_idx >= cce_locs.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
tree_node node;
|
||||||
|
node.dci_pos_idx = start_dci_idx;
|
||||||
|
node.dci_pos.L = record.aggr_idx;
|
||||||
|
node.rnti = record.ue != nullptr ? record.ue->rnti : SRSRAN_INVALID_RNTI;
|
||||||
|
node.current_mask.resize(nof_cces());
|
||||||
|
// get cumulative pdcch bitmap
|
||||||
|
if (not alloc_dfs.empty()) {
|
||||||
|
node.total_mask = alloc_dfs.back().total_mask;
|
||||||
|
} else {
|
||||||
|
node.total_mask.resize(nof_cces());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; node.dci_pos_idx < cce_locs.size(); ++node.dci_pos_idx) {
|
||||||
|
node.dci_pos.ncce = cce_locs[node.dci_pos_idx];
|
||||||
|
|
||||||
|
node.current_mask.reset();
|
||||||
|
node.current_mask.fill(node.dci_pos.ncce, node.dci_pos.ncce + (1U << record.aggr_idx));
|
||||||
|
if ((node.total_mask & node.current_mask).any()) {
|
||||||
|
// there is a PDCCH collision. Try another CCE position
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocation successful
|
||||||
|
node.total_mask |= node.current_mask;
|
||||||
|
alloc_dfs.push_back(node);
|
||||||
|
pdcch_dl_t& pdcch_dl = pdcch_dl_list[record.idx];
|
||||||
|
pdcch_dl.dci.ctx.location = node.dci_pos;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
srsran::span<const uint32_t> coreset_region::get_cce_loc_table(const alloc_record& record) const
|
||||||
|
{
|
||||||
|
switch (record.alloc_type) {
|
||||||
|
case pdcch_grant_type_t::dl_data:
|
||||||
|
return record.ue->cfg->cc_params[record.ue->cc]
|
||||||
|
.bwps[bwp_id]
|
||||||
|
.coresets[record.coreset_id]
|
||||||
|
.cce_positions[slot_idx][record.aggr_idx];
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace sched_nr_impl
|
||||||
|
} // namespace srsenb
|
Loading…
Reference in New Issue