diff --git a/lib/include/srslte/interfaces/sched_interface.h b/lib/include/srslte/interfaces/sched_interface.h index b07a04819..b095f00f7 100644 --- a/lib/include/srslte/interfaces/sched_interface.h +++ b/lib/include/srslte/interfaces/sched_interface.h @@ -52,12 +52,13 @@ public: } cell_cfg_sib_t; struct sched_args_t { - int pdsch_mcs = -1; - int pdsch_max_mcs = 28; - int pusch_mcs = -1; - int pusch_max_mcs = 28; - int nof_ctrl_symbols = 3; - int max_aggr_level = 3; + int pdsch_mcs = -1; + int pdsch_max_mcs = 28; + int pusch_mcs = -1; + int pusch_max_mcs = 28; + uint32_t min_nof_ctrl_symbols = 1; + uint32_t max_nof_ctrl_symbols = 3; + int max_aggr_level = 3; }; struct cell_cfg_t { diff --git a/srsenb/hdr/stack/mac/scheduler_grid.h b/srsenb/hdr/stack/mac/scheduler_grid.h index 71a58d37d..28cdd0036 100644 --- a/srsenb/hdr/stack/mac/scheduler_grid.h +++ b/srsenb/hdr/stack/mac/scheduler_grid.h @@ -36,13 +36,13 @@ enum class alloc_type_t { DL_BC, DL_PCCH, DL_RAR, DL_DATA, UL_DATA }; //! Result of alloc attempt struct alloc_outcome_t { - enum result_enum { SUCCESS, DCI_COLLISION, RB_COLLISION, ERROR }; + enum result_enum { SUCCESS, DCI_COLLISION, RB_COLLISION, ERROR, NOF_RB_INVALID }; result_enum result = ERROR; alloc_outcome_t() = default; alloc_outcome_t(result_enum e) : result(e) {} operator result_enum() { return result; } operator bool() { return result == SUCCESS; } - const char* to_string() const; + const char* to_string() const; }; //! Result of a Subframe sched computation @@ -59,6 +59,7 @@ struct sf_sched_result { class pdcch_grid_t { public: + const static uint32_t MAX_CFI = 3; struct alloc_t { uint16_t rnti = 0; srslte_dci_location_t dci_pos = {0, 0}; @@ -68,39 +69,61 @@ public: using alloc_result_t = std::vector; void init(const sched_cell_params_t& cell_params_); - void new_tti(const tti_params_t& tti_params_, uint32_t start_cfi); + void new_tti(const tti_params_t& tti_params_); bool alloc_dci(alloc_type_t alloc_type, uint32_t aggr_idx, sched_ue* user = nullptr); bool set_cfi(uint32_t cfi); + void set_max_cfi(uint32_t cfi) { max_user_cfi = cfi; } // getters uint32_t get_cfi() const { return current_cfix + 1; } void get_allocs(alloc_result_t* vec = nullptr, pdcch_mask_t* tot_mask = nullptr, size_t idx = 0) const; - uint32_t nof_cces() const; - size_t nof_allocs() const { return nof_dci_allocs; } - size_t nof_alloc_combinations() const { return prev_end - prev_start; } + uint32_t nof_cces() const { return cc_cfg->nof_cce_table[current_cfix]; } + size_t nof_allocs() const { return dci_record_list.size(); } + size_t nof_alloc_combinations() const { return get_alloc_tree().nof_leaves(); } std::string result_to_string(bool verbose = false) const; private: - const static uint32_t nof_cfis = 3; - using tree_node_t = std::pair; ///< First represents the parent node idx, and second the alloc tree node + struct alloc_tree_t { + struct node_t { + int parent_idx; + alloc_t node; + node_t(int i, const alloc_t& a) : parent_idx(i), node(a) {} + }; + // state + size_t nof_cces; + std::vector dci_alloc_tree; + size_t prev_start = 0, prev_end = 0; - const sched_dci_cce_t* get_cce_loc_table(alloc_type_t alloc_type, sched_ue* user) const; - void update_alloc_tree(int node_idx, - uint32_t aggr_idx, - sched_ue* user, - alloc_type_t alloc_type, - const sched_dci_cce_t* dci_locs); + explicit alloc_tree_t(size_t nof_cces_) : nof_cces(nof_cces_) {} + size_t nof_leaves() const { return prev_end - prev_start; } + void reset(); + }; + struct alloc_record_t { + sched_ue* user; + uint32_t aggr_idx; + alloc_type_t alloc_type; + }; + + const alloc_tree_t& get_alloc_tree() const { return alloc_trees[current_cfix]; } + const sched_dci_cce_t* get_cce_loc_table(alloc_type_t alloc_type, sched_ue* user, uint32_t cfix) const; + + // PDCCH allocation algorithm + bool alloc_dci_record(const alloc_record_t& record, uint32_t cfix); + static bool add_tree_node_leaves(alloc_tree_t& tree, + int node_idx, + const alloc_record_t& dci_record, + const sched_dci_cce_t& dci_locs, + uint32_t tti_tx_dl); // consts const sched_cell_params_t* cc_cfg = nullptr; srslte::log_ref log_h; // tti vars - const tti_params_t* tti_params = nullptr; - uint32_t current_cfix = 0; - size_t prev_start = 0, prev_end = 0; - std::vector dci_alloc_tree; - size_t nof_dci_allocs = 0; + const tti_params_t* tti_params = nullptr; + uint32_t current_cfix = 0, max_user_cfi = MAX_CFI; + std::vector alloc_trees; ///< List of PDCCH alloc trees, where index is the cfi index + std::vector dci_record_list; ///< Keeps a record of all the PDCCH allocations done so far }; //! manages a subframe grid resources, namely CCE and DL/UL RB allocations @@ -113,7 +136,7 @@ public: }; void init(const sched_cell_params_t& cell_params_); - void new_tti(const tti_params_t& tti_params_, uint32_t start_cfi); + void new_tti(const tti_params_t& tti_params_); dl_ctrl_alloc_t alloc_dl_ctrl(uint32_t aggr_lvl, alloc_type_t alloc_type); alloc_outcome_t alloc_dl_data(sched_ue* user, const rbgmask_t& user_mask); bool reserve_dl_rbgs(uint32_t start_rbg, uint32_t end_rbg); @@ -228,7 +251,7 @@ public: // Control/Configuration Methods sf_sched(); void init(const sched_cell_params_t& cell_params_); - void new_tti(uint32_t tti_rx_, uint32_t start_cfi); + void new_tti(uint32_t tti_rx_); // DL alloc methods alloc_outcome_t alloc_bc(uint32_t aggr_lvl, uint32_t sib_idx, uint32_t sib_ntx); diff --git a/srsenb/hdr/stack/mac/scheduler_ue.h b/srsenb/hdr/stack/mac/scheduler_ue.h index 57c0b487e..96643b8e3 100644 --- a/srsenb/hdr/stack/mac/scheduler_ue.h +++ b/srsenb/hdr/stack/mac/scheduler_ue.h @@ -125,6 +125,7 @@ public: uint16_t get_rnti() const { return rnti; } std::pair get_cell_index(uint32_t enb_cc_idx) const; const sched_interface::ue_cfg_t& get_ue_cfg() const { return cfg; } + uint32_t get_aggr_level(uint32_t ue_cc_idx, uint32_t nof_bits); /******************************************************* * Functions used by scheduler metric objects diff --git a/srsenb/src/enb_cfg_parser.cc b/srsenb/src/enb_cfg_parser.cc index 28e48cd07..8880a66ed 100644 --- a/srsenb/src/enb_cfg_parser.cc +++ b/srsenb/src/enb_cfg_parser.cc @@ -937,10 +937,10 @@ int set_derived_args(all_args_t* args_, rrc_cfg_t* rrc_cfg_, phy_cfg_t* phy_cfg_ rrc_cfg_->enable_mbsfn = args_->stack.embms.enable; // Check number of control symbols - if (cell_cfg_.nof_prb < 50 && args_->stack.mac.sched.nof_ctrl_symbols != 3) { - args_->stack.mac.sched.nof_ctrl_symbols = 3; + if (cell_cfg_.nof_prb < 50 && args_->stack.mac.sched.max_nof_ctrl_symbols != 3) { + args_->stack.mac.sched.max_nof_ctrl_symbols = 3; INFO("Setting number of control symbols to %d for %d PRB cell.\n", - args_->stack.mac.sched.nof_ctrl_symbols, + args_->stack.mac.sched.max_nof_ctrl_symbols, cell_cfg_.nof_prb); } diff --git a/srsenb/src/main.cc b/srsenb/src/main.cc index 5b7f042d9..da1934b08 100644 --- a/srsenb/src/main.cc +++ b/srsenb/src/main.cc @@ -129,7 +129,7 @@ void parse_args(all_args_t* args, int argc, char* argv[]) ("scheduler.pusch_mcs", bpo::value(&args->stack.mac.sched.pusch_mcs)->default_value(-1), "Optional fixed PUSCH MCS (ignores reported CQIs if specified)") ("scheduler.pusch_max_mcs", bpo::value(&args->stack.mac.sched.pusch_max_mcs)->default_value(-1), "Optional PUSCH MCS limit") ("scheduler.max_aggr_level", bpo::value(&args->stack.mac.sched.max_aggr_level)->default_value(-1), "Optional maximum aggregation level index (l=log2(L)) ") - ("scheduler.nof_ctrl_symbols", bpo::value(&args->stack.mac.sched.nof_ctrl_symbols)->default_value(3), "Number of control symbols") + ("scheduler.nof_ctrl_symbols", bpo::value(&args->stack.mac.sched.max_nof_ctrl_symbols)->default_value(3), "Number of control symbols") /* Downlink Channel emulator section */ ("channel.dl.enable", bpo::value(&args->phy.dl_channel_args.enable)->default_value(false), "Enable/Disable internal Downlink channel emulator") @@ -259,11 +259,11 @@ void parse_args(all_args_t* args, int argc, char* argv[]) } if (args->stack.embms.enable) { - if (args->stack.mac.sched.nof_ctrl_symbols == 3) { + if (args->stack.mac.sched.max_nof_ctrl_symbols == 3) { fprintf(stderr, "nof_ctrl_symbols = %d, While using MBMS, please set number of control symbols to either 1 or 2, " "depending on the length of the non-mbsfn region\n", - args->stack.mac.sched.nof_ctrl_symbols); + args->stack.mac.sched.max_nof_ctrl_symbols); exit(1); } } diff --git a/srsenb/src/stack/mac/scheduler.cc b/srsenb/src/stack/mac/scheduler.cc index 104a48ad4..80199b1eb 100644 --- a/srsenb/src/stack/mac/scheduler.cc +++ b/srsenb/src/stack/mac/scheduler.cc @@ -88,14 +88,14 @@ bool sched_cell_params_t::set_cfg(uint32_t enb_cc_id } // Compute Common locations for DCI for each CFI - for (uint32_t cfi = 0; cfi < 3; cfi++) { - sched_utils::generate_cce_location(regs.get(), &common_locations[cfi], cfi + 1); + for (uint32_t cfix = 0; cfix < pdcch_grid_t::MAX_CFI; cfix++) { + sched_utils::generate_cce_location(regs.get(), &common_locations[cfix], cfix + 1); } - if (common_locations[sched_cfg->nof_ctrl_symbols - 1].nof_loc[2] == 0) { - Error("SCHED: Current cfi=%d is not valid for broadcast (check scheduler.nof_ctrl_symbols in conf file).\n", - sched_cfg->nof_ctrl_symbols); - Console("SCHED: Current cfi=%d is not valid for broadcast (check scheduler.nof_ctrl_symbols in conf file).\n", - sched_cfg->nof_ctrl_symbols); + if (common_locations[sched_cfg->max_nof_ctrl_symbols - 1].nof_loc[2] == 0) { + Error("SCHED: Current cfi=%d is not valid for broadcast (check scheduler.max_nof_ctrl_symbols in conf file).\n", + sched_cfg->max_nof_ctrl_symbols); + Console("SCHED: Current cfi=%d is not valid for broadcast (check scheduler.max_nof_ctrl_symbols in conf file).\n", + sched_cfg->max_nof_ctrl_symbols); return false; } diff --git a/srsenb/src/stack/mac/scheduler_carrier.cc b/srsenb/src/stack/mac/scheduler_carrier.cc index 058da406d..07c829eb5 100644 --- a/srsenb/src/stack/mac/scheduler_carrier.cc +++ b/srsenb/src/stack/mac/scheduler_carrier.cc @@ -409,7 +409,7 @@ sf_sched* sched::carrier_sched::get_sf_sched(uint32_t tti_rx) sf_sched* ret = &sf_scheds[tti_rx % sf_scheds.size()]; if (ret->get_tti_rx() != tti_rx) { // start new TTI. Bind the struct where the result is going to be stored - ret->new_tti(tti_rx, cc_cfg->sched_cfg->nof_ctrl_symbols); + ret->new_tti(tti_rx); } return ret; } diff --git a/srsenb/src/stack/mac/scheduler_grid.cc b/srsenb/src/stack/mac/scheduler_grid.cc index 67cf5e4d8..d417264e8 100644 --- a/srsenb/src/stack/mac/scheduler_grid.cc +++ b/srsenb/src/stack/mac/scheduler_grid.cc @@ -42,6 +42,8 @@ const char* alloc_outcome_t::to_string() const return "rb_collision"; case ERROR: return "error"; + case NOF_RB_INVALID: + return "invalid nof prbs"; } return "unknown error"; } @@ -59,37 +61,51 @@ tti_params_t::tti_params_t(uint32_t tti_rx_) : * PDCCH Allocation Methods *******************************************************/ -void pdcch_grid_t::init(const sched_cell_params_t& cell_params_) +void pdcch_grid_t::alloc_tree_t::reset() { - cc_cfg = &cell_params_; - log_h = srslte::logmap::get("MAC "); - current_cfix = cc_cfg->sched_cfg->nof_ctrl_symbols - 1; -} - -void pdcch_grid_t::new_tti(const tti_params_t& tti_params_, uint32_t start_cfi) -{ - tti_params = &tti_params_; - prev_start = 0; prev_end = 0; dci_alloc_tree.clear(); - nof_dci_allocs = 0; - set_cfi(start_cfi); } -const sched_dci_cce_t* pdcch_grid_t::get_cce_loc_table(alloc_type_t alloc_type, sched_ue* user) const +void pdcch_grid_t::init(const sched_cell_params_t& cell_params_) +{ + cc_cfg = &cell_params_; + log_h = srslte::logmap::get("MAC "); + + // init alloc trees + alloc_trees.reserve(cc_cfg->sched_cfg->max_nof_ctrl_symbols); + for (uint32_t i = 0; i < cc_cfg->sched_cfg->max_nof_ctrl_symbols; ++i) { + alloc_trees.emplace_back(cc_cfg->nof_cce_table[i]); + } +} + +void pdcch_grid_t::new_tti(const tti_params_t& tti_params_) +{ + tti_params = &tti_params_; + + // Reset back all CFIs + for (auto& t : alloc_trees) { + t.reset(); + } + dci_record_list.clear(); + current_cfix = cc_cfg->sched_cfg->min_nof_ctrl_symbols - 1; + max_user_cfi = cc_cfg->sched_cfg->max_nof_ctrl_symbols; +} + +const sched_dci_cce_t* pdcch_grid_t::get_cce_loc_table(alloc_type_t alloc_type, sched_ue* user, uint32_t cfix) const { switch (alloc_type) { case alloc_type_t::DL_BC: - return &cc_cfg->common_locations[current_cfix]; + return &cc_cfg->common_locations[cfix]; case alloc_type_t::DL_PCCH: - return &cc_cfg->common_locations[current_cfix]; + return &cc_cfg->common_locations[cfix]; case alloc_type_t::DL_RAR: - return &cc_cfg->rar_locations[current_cfix][tti_params->sf_idx_tx_dl]; + return &cc_cfg->rar_locations[cfix][tti_params->sf_idx_tx_dl]; case alloc_type_t::DL_DATA: - return user->get_locations(cc_cfg->enb_cc_idx, current_cfix + 1, tti_params->sf_idx_tx_dl); + return user->get_locations(cc_cfg->enb_cc_idx, cfix + 1, tti_params->sf_idx_tx_dl); case alloc_type_t::UL_DATA: - return user->get_locations(cc_cfg->enb_cc_idx, current_cfix + 1, tti_params->sf_idx_tx_dl); + return user->get_locations(cc_cfg->enb_cc_idx, cfix + 1, tti_params->sf_idx_tx_dl); default: break; } @@ -99,64 +115,85 @@ const sched_dci_cce_t* pdcch_grid_t::get_cce_loc_table(alloc_type_t alloc_type, bool pdcch_grid_t::alloc_dci(alloc_type_t alloc_type, uint32_t aggr_idx, sched_ue* user) { // TODO: Make the alloc tree update lazy + alloc_record_t record{.user = user, .aggr_idx = aggr_idx, .alloc_type = alloc_type}; - /* Get DCI Location Table */ - const sched_dci_cce_t* dci_locs = get_cce_loc_table(alloc_type, user); - if (dci_locs == nullptr) { + // Try to allocate user in PDCCH for given CFI. If it fails, increment CFI + uint32_t first_cfi = get_cfi(); + bool success = alloc_dci_record(record, get_cfi() - 1); + while (not success and get_cfi() < max_user_cfi) { + set_cfi(get_cfi() + 1); + success = alloc_dci_record(record, get_cfi() - 1); + } + if (not success) { + // DCI allocation failed. go back to original CFI + set_cfi(first_cfi); return false; } - /* Search for potential DCI positions */ - if (prev_end > 0) { - for (size_t j = prev_start; j < prev_end; ++j) { - update_alloc_tree((int)j, aggr_idx, user, alloc_type, dci_locs); - } - } else { - update_alloc_tree(-1, aggr_idx, user, alloc_type, dci_locs); - } - - // if no pdcch space was available - if (dci_alloc_tree.size() == prev_end) { - return false; - } - - prev_start = prev_end; - prev_end = dci_alloc_tree.size(); - - nof_dci_allocs++; - + // DCI record allocation successful + dci_record_list.push_back(record); return true; } -void pdcch_grid_t::update_alloc_tree(int parent_node_idx, - uint32_t aggr_idx, - sched_ue* user, - alloc_type_t alloc_type, - const sched_dci_cce_t* dci_locs) +bool pdcch_grid_t::alloc_dci_record(const alloc_record_t& record, uint32_t cfix) { + bool ret = false; + auto& tree = alloc_trees[cfix]; + + // Get DCI Location Table + const sched_dci_cce_t* dci_locs = get_cce_loc_table(record.alloc_type, record.user, cfix); + if (dci_locs == nullptr or dci_locs->nof_loc[record.aggr_idx] == 0) { + return ret; + } + + if (tree.prev_end > 0) { + for (size_t j = tree.prev_start; j < tree.prev_end; ++j) { + ret |= add_tree_node_leaves(tree, (int)j, record, *dci_locs, tti_params->tti_tx_dl); + } + } else { + ret = add_tree_node_leaves(tree, -1, record, *dci_locs, tti_params->tti_tx_dl); + } + + if (ret) { + tree.prev_start = tree.prev_end; + tree.prev_end = tree.dci_alloc_tree.size(); + } + + return ret; +} + +//! Algorithm to compute a valid PDCCH allocation +bool pdcch_grid_t::add_tree_node_leaves(alloc_tree_t& tree, + int parent_node_idx, + const alloc_record_t& dci_record, + const sched_dci_cce_t& dci_locs, + uint32_t tti_tx_dl) +{ + bool ret = false; + alloc_t alloc; - alloc.rnti = (user != nullptr) ? user->get_rnti() : (uint16_t)0u; - alloc.dci_pos.L = aggr_idx; + alloc.rnti = (dci_record.user != nullptr) ? dci_record.user->get_rnti() : (uint16_t)0u; + alloc.dci_pos.L = dci_record.aggr_idx; // get cumulative pdcch mask pdcch_mask_t cum_mask; if (parent_node_idx >= 0) { - cum_mask = dci_alloc_tree[parent_node_idx].second.total_mask; + cum_mask = tree.dci_alloc_tree[parent_node_idx].node.total_mask; } else { - cum_mask.resize(nof_cces()); + cum_mask.resize(tree.nof_cces); } - uint32_t nof_locs = dci_locs->nof_loc[aggr_idx]; + uint32_t nof_locs = dci_locs.nof_loc[dci_record.aggr_idx]; for (uint32_t i = 0; i < nof_locs; ++i) { - uint32_t startpos = dci_locs->cce_start[aggr_idx][i]; + uint32_t startpos = dci_locs.cce_start[dci_record.aggr_idx][i]; - if (alloc_type == alloc_type_t::DL_DATA and user->pucch_sr_collision(tti_params->tti_tx_dl, startpos)) { + if (dci_record.alloc_type == alloc_type_t::DL_DATA and dci_record.user->pucch_sr_collision(tti_tx_dl, startpos)) { // will cause a collision in the PUCCH continue; } - pdcch_mask_t alloc_mask(nof_cces()); - alloc_mask.fill(startpos, startpos + (1u << aggr_idx)); + pdcch_mask_t alloc_mask(tree.nof_cces); + alloc_mask.fill(startpos, startpos + (1u << dci_record.aggr_idx)); if ((cum_mask & alloc_mask).any()) { // there is collision. Try another mask continue; @@ -168,38 +205,65 @@ void pdcch_grid_t::update_alloc_tree(int parent_node_idx, alloc.dci_pos.ncce = startpos; // Prune if repetition - uint32_t j = prev_end; - for (; j < dci_alloc_tree.size(); ++j) { - if (dci_alloc_tree[j].second.total_mask == alloc.total_mask) { + uint32_t j = tree.prev_end; + for (; j < tree.dci_alloc_tree.size(); ++j) { + if (tree.dci_alloc_tree[j].node.total_mask == alloc.total_mask) { break; } } - if (j < dci_alloc_tree.size()) { + if (j < tree.dci_alloc_tree.size()) { continue; } // Register allocation - dci_alloc_tree.emplace_back(parent_node_idx, alloc); + tree.dci_alloc_tree.emplace_back(parent_node_idx, alloc); + ret = true; } + + return ret; } bool pdcch_grid_t::set_cfi(uint32_t cfi) { - current_cfix = cfi - 1; - // TODO: use this function for dynamic cfi + if (cfi < cc_cfg->sched_cfg->min_nof_ctrl_symbols or cfi > cc_cfg->sched_cfg->max_nof_ctrl_symbols) { + srslte::logmap::get("MAC")->error("Invalid CFI value. Defaulting to current CFI.\n"); + return true; + } + + uint32_t new_cfix = cfi - 1; + if (new_cfix == current_cfix) { + return true; + } + + // setup new PDCCH alloc tree + auto& new_tree = alloc_trees[new_cfix]; + new_tree.reset(); + + if (not dci_record_list.empty()) { + // there are already PDCCH allocations + + // Rebuild Allocation Tree + bool ret = true; + for (const auto& old_record : dci_record_list) { + ret &= alloc_dci_record(old_record, new_cfix); + } + + if (not ret) { + // Fail to rebuild allocation tree. Go back to previous CFI + return false; + } + } + + current_cfix = new_cfix; // TODO: The estimation of the number of required prbs in metric depends on CFI. Analyse the consequences return true; } -uint32_t pdcch_grid_t::nof_cces() const -{ - return cc_cfg->nof_cce_table[current_cfix]; -} - void pdcch_grid_t::get_allocs(alloc_result_t* vec, pdcch_mask_t* tot_mask, size_t idx) const { + auto& tree = alloc_trees[current_cfix]; // if alloc tree is empty - if (prev_start == prev_end) { + if (tree.prev_start == tree.prev_end) { if (vec != nullptr) { vec->clear(); } @@ -213,32 +277,33 @@ void pdcch_grid_t::get_allocs(alloc_result_t* vec, pdcch_mask_t* tot_mask, size_ // set vector of allocations if (vec != nullptr) { vec->clear(); - size_t i = prev_start + idx; - while (dci_alloc_tree[i].first >= 0) { - vec->push_back(&dci_alloc_tree[i].second); - i = (size_t)dci_alloc_tree[i].first; + size_t i = tree.prev_start + idx; + while (tree.dci_alloc_tree[i].parent_idx >= 0) { + vec->push_back(&tree.dci_alloc_tree[i].node); + i = (size_t)tree.dci_alloc_tree[i].parent_idx; } - vec->push_back(&dci_alloc_tree[i].second); + vec->push_back(&tree.dci_alloc_tree[i].node); std::reverse(vec->begin(), vec->end()); } // set final cce mask if (tot_mask != nullptr) { - *tot_mask = dci_alloc_tree[prev_start + idx].second.total_mask; + *tot_mask = tree.dci_alloc_tree[tree.prev_start + idx].node.total_mask; } } std::string pdcch_grid_t::result_to_string(bool verbose) const { + auto& tree = alloc_trees[current_cfix]; std::stringstream ss; - ss << "cfi=" << get_cfi() << ", mask_size=" << nof_cces() << ", " << prev_end - prev_start + ss << "cfi=" << get_cfi() << ", mask_size=" << nof_cces() << ", " << tree.prev_end - tree.prev_start << " DCI allocation combinations:\n"; // get all the possible combinations of DCI allocations uint32_t count = 0; - for (size_t i = prev_start; i < prev_end; ++i) { + for (size_t i = tree.prev_start; i < tree.prev_end; ++i) { alloc_result_t vec; pdcch_mask_t tot_mask; - get_allocs(&vec, &tot_mask, i - prev_start); + get_allocs(&vec, &tot_mask, i - tree.prev_start); ss << " combination " << count << ": mask=0x" << tot_mask.to_hex().c_str(); if (verbose) { @@ -276,7 +341,7 @@ void sf_grid_t::init(const sched_cell_params_t& cell_params_) pdcch_alloc.init(*cc_cfg); } -void sf_grid_t::new_tti(const tti_params_t& tti_params_, uint32_t start_cfi) +void sf_grid_t::new_tti(const tti_params_t& tti_params_) { tti_params = &tti_params_; @@ -285,11 +350,11 @@ void sf_grid_t::new_tti(const tti_params_t& tti_params_, uint32_t start_cfi) avail_rbg = nof_rbgs; // internal state - pdcch_alloc.new_tti(*tti_params, start_cfi); + pdcch_alloc.new_tti(*tti_params); } //! Allocates CCEs and RBs for the given mask and allocation type (e.g. data, BC, RAR, paging) -alloc_outcome_t sf_grid_t::alloc_dl(uint32_t aggr_lvl, alloc_type_t alloc_type, rbgmask_t alloc_mask, sched_ue* user) +alloc_outcome_t sf_grid_t::alloc_dl(uint32_t aggr_idx, alloc_type_t alloc_type, rbgmask_t alloc_mask, sched_ue* user) { // Check RBG collision if ((dl_mask & alloc_mask).any()) { @@ -297,7 +362,7 @@ alloc_outcome_t sf_grid_t::alloc_dl(uint32_t aggr_lvl, alloc_type_t alloc_type, } // Allocate DCI in PDCCH - if (not pdcch_alloc.alloc_dci(alloc_type, aggr_lvl, user)) { + if (not pdcch_alloc.alloc_dci(alloc_type, aggr_idx, user)) { if (user != nullptr) { if (log_h->get_level() == srslte::LOG_LEVEL_DEBUG) { log_h->debug("No space in PDCCH for rnti=0x%x DL tx. Current PDCCH allocation: %s\n", @@ -316,7 +381,7 @@ alloc_outcome_t sf_grid_t::alloc_dl(uint32_t aggr_lvl, alloc_type_t alloc_type, } //! Allocates CCEs and RBs for control allocs. It allocates RBs in a contiguous manner. -sf_grid_t::dl_ctrl_alloc_t sf_grid_t::alloc_dl_ctrl(uint32_t aggr_lvl, alloc_type_t alloc_type) +sf_grid_t::dl_ctrl_alloc_t sf_grid_t::alloc_dl_ctrl(uint32_t aggr_idx, alloc_type_t alloc_type) { rbg_range_t range; range.rbg_min = nof_rbgs - avail_rbg; @@ -335,16 +400,37 @@ sf_grid_t::dl_ctrl_alloc_t sf_grid_t::alloc_dl_ctrl(uint32_t aggr_lvl, alloc_typ // allocate DCI and RBGs rbgmask_t new_mask(dl_mask.size()); new_mask.fill(range.rbg_min, range.rbg_max); - return {alloc_dl(aggr_lvl, alloc_type, new_mask), range}; + return {alloc_dl(aggr_idx, alloc_type, new_mask), range}; } //! Allocates CCEs and RBs for a user DL data alloc. alloc_outcome_t sf_grid_t::alloc_dl_data(sched_ue* user, const rbgmask_t& user_mask) { + // Check if allocation would cause segmentation + uint32_t ue_cc_idx = user->get_cell_index(cc_cfg->enb_cc_idx).second; + rbg_range_t r = user->get_required_dl_rbgs(ue_cc_idx, pdcch_alloc.get_cfi()); + if (r.rbg_min > user_mask.count()) { + log_h->error("The number of RBGs allocated will force segmentation\n"); + return alloc_outcome_t::NOF_RB_INVALID; + } + // Place an upper bound in CFI if necessary, to avoid segmentation + if (pdcch_alloc.get_cfi() < cc_cfg->sched_cfg->max_nof_ctrl_symbols) { + for (uint32_t cfi = cc_cfg->sched_cfg->max_nof_ctrl_symbols; cfi >= pdcch_alloc.get_cfi() + 1; --cfi) { + r = user->get_required_dl_rbgs(ue_cc_idx, cfi); + if (r.rbg_min <= user_mask.count()) { + break; + } + // decrease max CFI + pdcch_alloc.set_max_cfi(cfi); + } + } + srslte_dci_format_t dci_format = user->get_dci_format(); uint32_t nof_bits = srslte_dci_format_sizeof(&cc_cfg->cfg.cell, nullptr, nullptr, dci_format); - uint32_t aggr_level = user->get_ue_carrier(cc_cfg->enb_cc_idx)->get_aggr_level(nof_bits); - return alloc_dl(aggr_level, alloc_type_t::DL_DATA, user_mask, user); + uint32_t aggr_idx = user->get_ue_carrier(cc_cfg->enb_cc_idx)->get_aggr_level(nof_bits); + alloc_outcome_t ret = alloc_dl(aggr_idx, alloc_type_t::DL_DATA, user_mask, user); + + return ret; } alloc_outcome_t sf_grid_t::alloc_ul_data(sched_ue* user, ul_harq_proc::ul_alloc_t alloc, bool needs_pdcch) @@ -410,7 +496,7 @@ void sf_sched::init(const sched_cell_params_t& cell_params_) max_msg3_prb = std::max(6u, cc_cfg->cfg.cell.nof_prb - (uint32_t)cc_cfg->cfg.nrb_pucch); } -void sf_sched::new_tti(uint32_t tti_rx_, uint32_t start_cfi) +void sf_sched::new_tti(uint32_t tti_rx_) { // reset internal state bc_allocs.clear(); @@ -419,7 +505,7 @@ void sf_sched::new_tti(uint32_t tti_rx_, uint32_t start_cfi) ul_data_allocs.clear(); tti_params = tti_params_t{tti_rx_}; - tti_alloc.new_tti(tti_params, start_cfi); + tti_alloc.new_tti(tti_params); // setup first prb to be used for msg3 alloc. Account for potential PRACH alloc last_msg3_prb = cc_cfg->cfg.nrb_pucch; diff --git a/srsenb/src/stack/mac/scheduler_ue.cc b/srsenb/src/stack/mac/scheduler_ue.cc index 0186d2dc9..950fe613b 100644 --- a/srsenb/src/stack/mac/scheduler_ue.cc +++ b/srsenb/src/stack/mac/scheduler_ue.cc @@ -1059,6 +1059,11 @@ std::pair sched_ue::get_cell_index(uint32_t enb_cc_idx) const return {false, std::numeric_limits::max()}; } +uint32_t sched_ue::get_aggr_level(uint32_t ue_cc_idx, uint32_t nof_bits) +{ + return carriers[ue_cc_idx].get_aggr_level(nof_bits); +} + void sched_ue::finish_tti(const tti_params_t& tti_params, uint32_t enb_cc_idx) { auto p = get_cell_index(enb_cc_idx); diff --git a/srsenb/test/mac/CMakeLists.txt b/srsenb/test/mac/CMakeLists.txt index 18f8f3d88..c69539b1d 100644 --- a/srsenb/test/mac/CMakeLists.txt +++ b/srsenb/test/mac/CMakeLists.txt @@ -18,15 +18,28 @@ # and at http://www.gnu.org/licenses/. # +add_library(scheduler_test_common STATIC scheduler_test_common.cc) + +# Scheduler subcomponent testing +add_executable(sched_grid_test sched_grid_test.cc) +target_link_libraries(sched_grid_test srsenb_mac + srsenb_phy + srslte_common + scheduler_test_common + ${CMAKE_THREAD_LIBS_INIT} + ${Boost_LIBRARIES}) +add_test(sched_grid_test sched_grid_test) + # Scheduler test random -add_executable(scheduler_test_rand scheduler_test_rand.cc scheduler_test_common.cc) -target_link_libraries(scheduler_test_rand srsenb_mac - srsenb_phy - srslte_common - srslte_phy - rrc_asn1 - ${CMAKE_THREAD_LIBS_INIT} - ${Boost_LIBRARIES}) +add_executable(scheduler_test_rand scheduler_test_rand.cc) +target_link_libraries(scheduler_test_rand srsenb_mac + srsenb_phy + srslte_common + scheduler_test_common + srslte_phy + rrc_asn1 + ${CMAKE_THREAD_LIBS_INIT} + ${Boost_LIBRARIES}) add_test(scheduler_test_rand scheduler_test_rand) # Scheduler test random for CA @@ -35,6 +48,7 @@ target_link_libraries(scheduler_ca_test srsenb_mac srsenb_phy srslte_common srslte_phy + scheduler_test_common rrc_asn1 ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES}) diff --git a/srsenb/test/mac/sched_grid_test.cc b/srsenb/test/mac/sched_grid_test.cc new file mode 100644 index 000000000..103b21010 --- /dev/null +++ b/srsenb/test/mac/sched_grid_test.cc @@ -0,0 +1,151 @@ +/* + * Copyright 2013-2020 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "scheduler_test_common.h" +#include "srsenb/hdr/stack/mac/scheduler_grid.h" +#include "srslte/common/test_common.h" + +using namespace srsenb; +// const uint32_t seed = std::chrono::system_clock::now().time_since_epoch().count(); +const uint32_t seed = 3930373626; + +const uint32_t PCell_IDX = 0; +const std::array prb_list = {6, 15, 25, 50, 75, 100}; + +uint32_t get_aggr_level(sched_ue& sched_ue, uint32_t ue_cc_idx, const std::vector& cell_params) +{ + srslte_dci_format_t dci_format = sched_ue.get_dci_format(); + uint32_t nof_dci_bits = srslte_dci_format_sizeof(&cell_params[ue_cc_idx].cfg.cell, nullptr, nullptr, dci_format); + uint32_t aggr_level = sched_ue.get_aggr_level(ue_cc_idx, nof_dci_bits); + return aggr_level; +} + +int test_pdcch_one_ue() +{ + using rand_uint = std::uniform_int_distribution; + const uint32_t ENB_CC_IDX = 0; + // Params + uint32_t nof_prb = prb_list[rand_uint{0, 5}(get_rand_gen())]; + uint16_t rnti = rand_uint{70, 120}(get_rand_gen()); + srslte::tti_point start_tti{rand_uint{0, 10240}(get_rand_gen())}; + uint32_t nof_ttis = 100; + + // Derived + std::vector cell_params(1); + sched_interface::ue_cfg_t ue_cfg = generate_default_ue_cfg(); + sched_interface::cell_cfg_t cell_cfg = generate_default_cell_cfg(nof_prb); + sched_interface::sched_args_t sched_args{}; + cell_params[ENB_CC_IDX].set_cfg(ENB_CC_IDX, cell_cfg, sched_args); + + pdcch_grid_t pdcch; + sched_ue sched_ue{}; + sched_ue.init(rnti, cell_params); + sched_ue.set_cfg(ue_cfg); + + pdcch.init(cell_params[PCell_IDX]); + TESTASSERT(pdcch.nof_alloc_combinations() == 0); + TESTASSERT(pdcch.nof_allocs() == 0); + + uint32_t tti_counter = 0; + for (; tti_counter < nof_ttis; ++tti_counter) { + tti_params_t tti_params{(start_tti + tti_counter).to_uint()}; + pdcch.new_tti(tti_params); + TESTASSERT(pdcch.nof_cces() == cell_params[ENB_CC_IDX].nof_cce_table[0]); + TESTASSERT(pdcch.get_cfi() == 1); // Start at CFI=1 + + // Set DL CQI - it should affect aggregation level + uint32_t dl_cqi = std::uniform_int_distribution{1, 25}(srsenb::get_rand_gen()); + sched_ue.set_dl_cqi(tti_params.tti_tx_dl, ENB_CC_IDX, dl_cqi); + uint32_t aggr_idx = get_aggr_level(sched_ue, PCell_IDX, cell_params); + uint32_t max_nof_cce_locs = + sched_ue.get_locations(ENB_CC_IDX, pdcch_grid_t::MAX_CFI, tti_params.sf_idx_tx_dl)->nof_loc[aggr_idx]; + + // allocate DL user + uint32_t prev_cfi = pdcch.get_cfi(); + srsenb::sched_dci_cce_t* dci_cce = sched_ue.get_locations(ENB_CC_IDX, prev_cfi, tti_params.sf_idx_tx_dl); + uint32_t prev_nof_cce_locs = dci_cce->nof_loc[aggr_idx]; + + TESTASSERT(pdcch.alloc_dci(alloc_type_t::DL_DATA, aggr_idx, &sched_ue)); + TESTASSERT(pdcch.nof_allocs() == 1); + if (prev_nof_cce_locs == pdcch.nof_allocs() - 1) { + // CFI must be increased + TESTASSERT(pdcch.get_cfi() > prev_cfi); + } else { + // Previous CFI should be fine + TESTASSERT(pdcch.get_cfi() == prev_cfi); + } + + dci_cce = sched_ue.get_locations(ENB_CC_IDX, pdcch.get_cfi(), tti_params.sf_idx_tx_dl); + uint32_t nof_dci_locs = dci_cce->nof_loc[aggr_idx]; + const uint32_t* dci_locs = dci_cce->cce_start[aggr_idx]; + + pdcch_grid_t::alloc_result_t pdcch_result; + pdcch_mask_t pdcch_mask; + pdcch.get_allocs(&pdcch_result, &pdcch_mask, 0); + TESTASSERT(pdcch_result.size() == 1); + TESTASSERT(pdcch_result[0]->rnti == sched_ue.get_rnti()); + TESTASSERT(pdcch_result[0]->total_mask.size() == cell_params[ENB_CC_IDX].nof_cce_table[pdcch.get_cfi() - 1]); + TESTASSERT(pdcch_result[0]->current_mask == pdcch_result[0]->total_mask); + TESTASSERT(pdcch_result[0]->current_mask.count() == 1u << aggr_idx); + TESTASSERT(std::count(dci_locs, dci_locs + nof_dci_locs, pdcch_result[0]->dci_pos.ncce) > 0); + + // allocate UL user + if (max_nof_cce_locs == pdcch.nof_allocs()) { + // no more space + continue; + } + prev_nof_cce_locs = nof_dci_locs; + prev_cfi = pdcch.get_cfi(); + TESTASSERT(pdcch.alloc_dci(alloc_type_t::UL_DATA, aggr_idx, &sched_ue)); + TESTASSERT(pdcch.nof_allocs() == 2); + if (prev_nof_cce_locs == pdcch.nof_allocs() - 1) { + // CFI must be increased + TESTASSERT(pdcch.get_cfi() > prev_cfi); + } else { + // Previous CFI should be fine + TESTASSERT(pdcch.get_cfi() == prev_cfi); + } + + dci_cce = sched_ue.get_locations(ENB_CC_IDX, pdcch.get_cfi(), tti_params.sf_idx_tx_dl); + nof_dci_locs = dci_cce->nof_loc[aggr_idx]; + dci_locs = dci_cce->cce_start[aggr_idx]; + + pdcch.get_allocs(&pdcch_result, &pdcch_mask, 0); + TESTASSERT(pdcch_result.size() == pdcch.nof_allocs()); + TESTASSERT(pdcch_result[1]->rnti == sched_ue.get_rnti()); + TESTASSERT(pdcch_result[1]->total_mask.size() == cell_params[ENB_CC_IDX].nof_cce_table[pdcch.get_cfi() - 1]); + TESTASSERT((pdcch_result[1]->current_mask & pdcch_result[0]->current_mask).none()); + TESTASSERT(pdcch_result[1]->current_mask.count() == 1u << aggr_idx); + TESTASSERT(pdcch_result[1]->total_mask == (pdcch_result[0]->current_mask | pdcch_result[1]->current_mask)); + TESTASSERT(std::count(dci_locs, dci_locs + nof_dci_locs, pdcch_result[0]->dci_pos.ncce) > 0); + } + TESTASSERT(tti_counter == nof_ttis); + + return SRSLTE_SUCCESS; +} + +int main() +{ + srsenb::set_randseed(seed); + printf("This is the chosen seed: %u\n", seed); + + TESTASSERT(test_pdcch_one_ue() == SRSLTE_SUCCESS); +} diff --git a/srsenb/test/mac/scheduler_test_rand.cc b/srsenb/test/mac/scheduler_test_rand.cc index 72512d5ea..161f4a2fc 100644 --- a/srsenb/test/mac/scheduler_test_rand.cc +++ b/srsenb/test/mac/scheduler_test_rand.cc @@ -244,7 +244,7 @@ int sched_tester::assert_no_empty_allocs() int sched_tester::test_pdcch_collisions() { srslte::bounded_bitset<128, true> used_cce; - used_cce.resize(srslte_regs_pdcch_ncce(sched_cell_params[CARRIER_IDX].regs.get(), sched_cfg.nof_ctrl_symbols)); + used_cce.resize(srslte_regs_pdcch_ncce(sched_cell_params[CARRIER_IDX].regs.get(), sched_cfg.max_nof_ctrl_symbols)); /* TEST: Check if there are collisions in the PDCCH */ TESTASSERT(output_tester[CARRIER_IDX].test_pdcch_collisions(tti_info.dl_sched_result[CARRIER_IDX],