|
| 1 | +/* |
| 2 | + * |
| 3 | + * Copyright 2021-2024 Software Radio Systems Limited |
| 4 | + * |
| 5 | + * By using this file, you agree to the terms and conditions set |
| 6 | + * forth in the LICENSE file which can be found at the top level of |
| 7 | + * the distribution. |
| 8 | + * |
| 9 | + */ |
| 10 | + |
| 11 | +#include "du_srs_resource_manager.h" |
| 12 | +#include "du_ue_resource_config.h" |
| 13 | +#include "srsran/ran/srs/srs_bandwidth_configuration.h" |
| 14 | + |
| 15 | +using namespace srsran; |
| 16 | +using namespace srs_du; |
| 17 | + |
| 18 | +static std::optional<unsigned> compute_srs_bw_param(unsigned nof_ul_rbs) |
| 19 | +{ |
| 20 | + // Iterate over Table 6.4.1.4.3-1, TS 38.211, and find the first \f$C_{SRS}\f$ value that provides the |
| 21 | + // greatest \f$m_{SRS,0}\f$ such that it is less than or equal to number of UL RBs. |
| 22 | + |
| 23 | + // As per Table 6.4.1.4.3-1, TS 38.211, the maximum value of \f$C_{SRS}\f$ is 63. |
| 24 | + constexpr unsigned max_non_valid_c_srs = 64; |
| 25 | + constexpr uint8_t b_srs_0 = 0; |
| 26 | + for (uint8_t c_srs = 0; c_srs != max_non_valid_c_srs; c_srs++) { |
| 27 | + auto srs_params = srs_configuration_get(c_srs, b_srs_0); |
| 28 | + srsran_assert(srs_params.has_value(), "The SRS is not valid"); |
| 29 | + |
| 30 | + if (c_srs == 0 and srs_params.value().m_srs > nof_ul_rbs) { |
| 31 | + srsran_assertion_failure("C_SRS is not compatible with the number of UL RBs"); |
| 32 | + return std::nullopt; |
| 33 | + } |
| 34 | + if (srs_params.value().m_srs == nof_ul_rbs) { |
| 35 | + return c_srs; |
| 36 | + } |
| 37 | + if (srs_params.value().m_srs > nof_ul_rbs) { |
| 38 | + return c_srs - 1; |
| 39 | + } |
| 40 | + } |
| 41 | + return max_non_valid_c_srs - 1; |
| 42 | +} |
| 43 | + |
| 44 | +// Helper that returns the frequency shift value for the SRS; the value is computed in such a way that the SRS resources |
| 45 | +// are placed at the center of the band. |
| 46 | +static unsigned compute_freq_shift(unsigned c_srs, unsigned nof_ul_rbs) |
| 47 | +{ |
| 48 | + // As per Section 6.4.1.4.3, the parameter \f$C_{SRS}\f$ = 0 provides the bandwidth of the SRS resources. |
| 49 | + constexpr uint8_t b_srs_0 = 0; |
| 50 | + std::optional<srsran::srs_configuration> srs_params = srs_configuration_get(c_srs, b_srs_0); |
| 51 | + srsran_sanity_check(srs_params.has_value() and nof_ul_rbs >= srs_params.value().m_srs, |
| 52 | + "The SRS configuration is not valid"); |
| 53 | + |
| 54 | + return (nof_ul_rbs - srs_params.value().m_srs) / 2; |
| 55 | +} |
| 56 | + |
| 57 | +static bool is_ul_slot(unsigned offset, const tdd_ul_dl_config_common& tdd_cfg) |
| 58 | +{ |
| 59 | + const unsigned slot_index = offset % (NOF_SUBFRAMES_PER_FRAME * get_nof_slots_per_subframe(tdd_cfg.ref_scs)); |
| 60 | + return srsran::get_active_tdd_ul_symbols(tdd_cfg, slot_index, cyclic_prefix::NORMAL).length() != 0; |
| 61 | +} |
| 62 | + |
| 63 | +static bool is_partually_ul_slot(unsigned offset, const tdd_ul_dl_config_common& tdd_cfg) |
| 64 | +{ |
| 65 | + const unsigned slot_index = offset % (NOF_SUBFRAMES_PER_FRAME * get_nof_slots_per_subframe(tdd_cfg.ref_scs)); |
| 66 | + return srsran::get_active_tdd_ul_symbols(tdd_cfg, slot_index, cyclic_prefix::NORMAL).length() != 0 and |
| 67 | + srsran::get_active_tdd_dl_symbols(tdd_cfg, slot_index, cyclic_prefix::NORMAL).length() != |
| 68 | + NOF_OFDM_SYM_PER_SLOT_NORMAL_CP; |
| 69 | +} |
| 70 | + |
| 71 | +du_srs_policy_max_ul_th::du_srs_policy_max_ul_th(span<const du_cell_config> cell_cfg_list_) : |
| 72 | + cells(cell_cfg_list_.begin(), cell_cfg_list_.end()) |
| 73 | +{ |
| 74 | + for (auto& cell : cells) { |
| 75 | + std::optional<unsigned> c_srs = |
| 76 | + compute_srs_bw_param(cell.cell_cfg.ul_cfg_common.init_ul_bwp.generic_params.crbs.length()).value(); |
| 77 | + srsran_assert(c_srs.has_value(), "SRS parameters didn't provide a valid C_SRS value"); |
| 78 | + cell.srs_common_params.c_srs = c_srs.value(); |
| 79 | + std::optional<unsigned> freq_shift = |
| 80 | + compute_freq_shift(c_srs.value(), cell.cell_cfg.ul_cfg_common.init_ul_bwp.generic_params.crbs.length()); |
| 81 | + srsran_assert(freq_shift.has_value(), "SRS parameters didn't provide a valid freq_shift value"); |
| 82 | + cell.srs_common_params.freq_shift = freq_shift.value(); |
| 83 | + |
| 84 | + cell.cell_srs_res_list = generate_cell_srs_list(cell.cell_cfg); |
| 85 | + |
| 86 | + const unsigned srs_period_slots = static_cast<unsigned>(cell.cell_cfg.srs_cfg.srs_period.value()); |
| 87 | + cell.slot_resource_cnt.reserve(srs_period_slots); |
| 88 | + |
| 89 | + for (unsigned offset = 0; offset != srs_period_slots; ++offset) { |
| 90 | + unsigned offset_res_cnt = 0U; |
| 91 | + for (auto& res : cell.cell_srs_res_list) { |
| 92 | + // Handle TDD and FDD configurations separately, as we treat partially-UL slots differently from fully-UL slots. |
| 93 | + if (cell_cfg_list_[0].tdd_ul_dl_cfg_common.has_value()) { |
| 94 | + // Verify whether the offset maps to a partially- or fully-UL slot. |
| 95 | + if (not is_ul_slot(offset, cell_cfg_list_[0].tdd_ul_dl_cfg_common.value())) { |
| 96 | + continue; |
| 97 | + } |
| 98 | + // For partially-UL slots, we need to check if the SRS can be placed in the UL symbols of the slot. |
| 99 | + if (is_partually_ul_slot(offset, cell_cfg_list_[0].tdd_ul_dl_cfg_common.value())) { |
| 100 | + // TODO: Fix check for pattern 2. |
| 101 | + if (res.symbols.start() < NOF_OFDM_SYM_PER_SLOT_NORMAL_CP - |
| 102 | + cell_cfg_list_[0].tdd_ul_dl_cfg_common.value().pattern1.nof_ul_symbols) { |
| 103 | + continue; |
| 104 | + } |
| 105 | + } |
| 106 | + // This is the fully-UL slot case: check if the SRS can be placed in the UL symbols of the slot. |
| 107 | + else if (res.symbols.start() < |
| 108 | + NOF_OFDM_SYM_PER_SLOT_NORMAL_CP - cell.cell_cfg.srs_cfg.max_nof_symbols.to_uint()) { |
| 109 | + continue; |
| 110 | + } |
| 111 | + } |
| 112 | + // FDD case. |
| 113 | + else { |
| 114 | + if (res.symbols.start() < NOF_OFDM_SYM_PER_SLOT_NORMAL_CP - cell.cell_cfg.srs_cfg.max_nof_symbols.to_uint()) { |
| 115 | + continue; |
| 116 | + } |
| 117 | + } |
| 118 | + cell.srs_res_offset_free_list.push_back({res.cell_res_id, offset}); |
| 119 | + ++offset_res_cnt; |
| 120 | + } |
| 121 | + cell.slot_resource_cnt.push_back({0U, offset_res_cnt}); |
| 122 | + } |
| 123 | + } |
| 124 | +} |
| 125 | + |
| 126 | +bool du_srs_policy_max_ul_th::alloc_resources(cell_group_config& cell_grp_cfg) |
| 127 | +{ |
| 128 | + // Allocation of SR PUCCH offset. |
| 129 | + cell_grp_cfg.cells[0].serv_cell_cfg.ul_config->init_ul_bwp.srs_cfg.emplace( |
| 130 | + cells[cell_grp_cfg.cells[0].serv_cell_cfg.cell_index].default_srs_cfg); |
| 131 | + srs_config& ue_srs_cfg = cell_grp_cfg.cells[0].serv_cell_cfg.ul_config->init_ul_bwp.srs_cfg.value(); |
| 132 | + auto& free_srs_list = cells[cell_grp_cfg.cells[0].serv_cell_cfg.cell_index].srs_res_offset_free_list; |
| 133 | + |
| 134 | + // Verify where there are SRS resources to allocate a new UE. |
| 135 | + if (free_srs_list.empty()) { |
| 136 | + return false; |
| 137 | + } |
| 138 | + |
| 139 | + // Find the best resource ID and offset for this UE. |
| 140 | + auto srs_res_id_offset = cells[0].find_optimal_ue_srs_resource(); |
| 141 | + |
| 142 | + if (srs_res_id_offset == free_srs_list.end()) { |
| 143 | + return false; |
| 144 | + } |
| 145 | + |
| 146 | + const auto& du_res_it = cells[0].get_du_srs_res_cfg(srs_res_id_offset->first); |
| 147 | + |
| 148 | + if (du_res_it == cells[0].cell_srs_res_list.end()) { |
| 149 | + return false; |
| 150 | + } |
| 151 | + |
| 152 | + const auto& du_res = *du_res_it; |
| 153 | + |
| 154 | + // Update the SRS configuration with the parameters that are specific to this resource and for this UE. |
| 155 | + auto& only_ue_srs_res = ue_srs_cfg.srs_res_list.front(); |
| 156 | + const unsigned srs_offset = srs_res_id_offset->second; |
| 157 | + // NOTE: given that there is only 1 SRS resource per UE, we can assume that the SRS resource ID is 0. |
| 158 | + only_ue_srs_res.id.cell_res_id = du_res.cell_res_id; |
| 159 | + only_ue_srs_res.id.ue_res_id = static_cast<srs_config::srs_res_id>(0U); |
| 160 | + only_ue_srs_res.periodicity_and_offset.value().offset = srs_offset; |
| 161 | + only_ue_srs_res.tx_comb.tx_comb_offset = du_res.tx_comb_offset.to_uint(); |
| 162 | + only_ue_srs_res.freq_domain_pos = du_res.freq_dom_position; |
| 163 | + only_ue_srs_res.res_mapping.start_pos = NOF_OFDM_SYM_PER_SLOT_NORMAL_CP - du_res.symbols.start() - 1; |
| 164 | + only_ue_srs_res.res_mapping.nof_symb = static_cast<srs_nof_symbols>(du_res.symbols.length()); |
| 165 | + only_ue_srs_res.sequence_id = du_res.sequence_id; |
| 166 | + |
| 167 | + // Update the SRS configuration with the parameters that are common to the cell. |
| 168 | + only_ue_srs_res.freq_hop.c_srs = cells[cell_grp_cfg.cells[0].serv_cell_cfg.cell_index].srs_common_params.c_srs; |
| 169 | + only_ue_srs_res.freq_domain_shift = |
| 170 | + cells[cell_grp_cfg.cells[0].serv_cell_cfg.cell_index].srs_common_params.freq_shift; |
| 171 | + |
| 172 | + // Update the SRS resource set with the SRS id. |
| 173 | + ue_srs_cfg.srs_res_set_list.front().srs_res_id_list.front() = only_ue_srs_res.id.ue_res_id; |
| 174 | + |
| 175 | + // Remove the allocated SRS resource from the free list. |
| 176 | + free_srs_list.erase(srs_res_id_offset); |
| 177 | + |
| 178 | + // Update the used_not_full slot vector. |
| 179 | + ++cells[0].slot_resource_cnt[srs_offset].first; |
| 180 | + |
| 181 | + return true; |
| 182 | +} |
| 183 | + |
| 184 | +std::vector<std::pair<unsigned, unsigned>>::const_iterator |
| 185 | +du_srs_policy_max_ul_th::cell_context::find_optimal_ue_srs_resource() |
| 186 | +{ |
| 187 | + // The weights assigned here can be set to any value, as long as: |
| 188 | + // - symbol_weight_base is greater than 0; |
| 189 | + // - reuse_slot_discount less than symbol_weight_base; |
| 190 | + // - max_weight is > symbol_weight_base * (srs_builder_params::max_nof_symbols / srs_builder_params::nof_symbols). |
| 191 | + static constexpr unsigned max_weight = 100U; |
| 192 | + static constexpr unsigned symbol_weight_base = 10U; |
| 193 | + |
| 194 | + const auto weight_function = [&](const pair_res_id_offset& srs_res) { |
| 195 | + if (cell_cfg.tdd_ul_dl_cfg_common.has_value() and |
| 196 | + is_partually_ul_slot(srs_res.second, cell_cfg.tdd_ul_dl_cfg_common.value())) { |
| 197 | + return 0U; |
| 198 | + } |
| 199 | + |
| 200 | + // SRS res config. |
| 201 | + const auto srs_res_cfg_it = get_du_srs_res_cfg(srs_res.first); |
| 202 | + |
| 203 | + if (srs_res_cfg_it == cell_srs_res_list.end()) { |
| 204 | + return max_weight; |
| 205 | + } |
| 206 | + |
| 207 | + // Give priority to the last symbols within a slot. This reduces the space used for the SRS in a slot. |
| 208 | + const unsigned symb_weight = |
| 209 | + (NOF_OFDM_SYM_PER_SLOT_NORMAL_CP - srs_res_cfg_it->symbols.start()) * symbol_weight_base; |
| 210 | + |
| 211 | + // We consider a discount if the offset is already used but not full; this way, we give an incentive to the SRS |
| 212 | + // resources not to be allocated on a new slot, to avoid taking PUSCH symbols on a new slot. |
| 213 | + const unsigned reuse_slot_discount = offset_used_not_full(srs_res.second) ? symbol_weight_base / 2U : 0U; |
| 214 | + |
| 215 | + return symb_weight - reuse_slot_discount; |
| 216 | + }; |
| 217 | + |
| 218 | + auto optimal_res_it = std::min_element( |
| 219 | + srs_res_offset_free_list.begin(), |
| 220 | + srs_res_offset_free_list.end(), |
| 221 | + [&weight_function](const std::pair<unsigned, unsigned>& lhs, const std::pair<unsigned, unsigned>& rhs) { |
| 222 | + return weight_function(lhs) < weight_function(rhs); |
| 223 | + }); |
| 224 | + |
| 225 | + return optimal_res_it; |
| 226 | +} |
| 227 | + |
| 228 | +void du_srs_policy_max_ul_th::dealloc_resources(cell_group_config& cell_grp_cfg) |
| 229 | +{ |
| 230 | + if (not cell_grp_cfg.cells[0].serv_cell_cfg.ul_config->init_ul_bwp.srs_cfg.has_value()) { |
| 231 | + return; |
| 232 | + } |
| 233 | + |
| 234 | + const auto& ue_srs_cfg = cell_grp_cfg.cells[0].serv_cell_cfg.ul_config->init_ul_bwp.srs_cfg.value(); |
| 235 | + auto& free_srs_list = cells[cell_grp_cfg.cells[0].serv_cell_cfg.cell_index].srs_res_offset_free_list; |
| 236 | + |
| 237 | + for (const auto& srs_res : ue_srs_cfg.srs_res_list) { |
| 238 | + const unsigned offset_to_deallocate = srs_res.periodicity_and_offset.value().offset; |
| 239 | + free_srs_list.push_back({srs_res.id.cell_res_id, offset_to_deallocate}); |
| 240 | + |
| 241 | + // Update the used_not_full slot vector. |
| 242 | + srsran_assert(cells[0].slot_resource_cnt[offset_to_deallocate].first != 0, "The offset is expected to be non-zero"); |
| 243 | + --cells[0].slot_resource_cnt[offset_to_deallocate].first; |
| 244 | + } |
| 245 | +} |
0 commit comments