diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f07cfc2421..a458c217eb 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -122,6 +122,8 @@ helm: variables: CI_DESCRIPTION: Synchronization with srsGNB NOTIFY_SLACK: "true" + inherit: + variables: false trigger: project: softwareradiosystems/ci/srsgnb_k8s branch: main @@ -137,6 +139,8 @@ enterprise: CI_DESCRIPTION: Nightly SRSRAN_COMMIT: $CI_COMMIT_SHA NOTIFY_SLACK: "true" + inherit: + variables: false trigger: project: softwareradiosystems/srsran_5g_enterprise branch: main diff --git a/.gitlab/ci/e2e.yml b/.gitlab/ci/e2e.yml index aa887c2f26..333164d62a 100644 --- a/.gitlab/ci/e2e.yml +++ b/.gitlab/ci/e2e.yml @@ -277,6 +277,7 @@ smoke zmq: GROUP: uesim TESTBED: zmq_uesim E2E_LOG_LEVEL: "info" + RETINA_LAUNCHER_ARGS: "--retina-pod-timeout 900" needs: - job: "basic relwithdeb" artifacts: true @@ -564,6 +565,7 @@ android b200: - job: "basic relwithdeb" artifacts: true - *retina-needs + allow_failure: true android IMS: stage: rf @@ -580,6 +582,7 @@ android IMS: - job: "basic relwithdeb" artifacts: true - *retina-needs + allow_failure: true android x300: stage: rf @@ -595,6 +598,7 @@ android x300: - job: "basic relwithdeb" artifacts: true - *retina-needs + allow_failure: true ################################################################################ # VIAVI diff --git a/apps/cu/CMakeLists.txt b/apps/cu/CMakeLists.txt index dc499b09a2..87c5ffd73c 100644 --- a/apps/cu/CMakeLists.txt +++ b/apps/cu/CMakeLists.txt @@ -23,7 +23,6 @@ add_executable(srscu cu_appconfig_cli11_schema.cpp cu_appconfig_validator.cpp cu_appconfig_yaml_writer.cpp - cu_worker_manager.cpp ) install(TARGETS srscu diff --git a/apps/cu/cu.cpp b/apps/cu/cu.cpp index dd7df58801..aaf8e9d2a9 100644 --- a/apps/cu/cu.cpp +++ b/apps/cu/cu.cpp @@ -20,8 +20,6 @@ * */ -#include "srsran/cu_up/cu_up.h" -#include "srsran/cu_up/cu_up_factory.h" #include "srsran/f1ap/gateways/f1c_network_server_factory.h" #include "srsran/f1u/cu_up/split_connector/f1u_split_connector_factory.h" #include "srsran/gateways/udp_network_gateway.h" @@ -44,40 +42,32 @@ #include "srsran/support/versioning/version.h" #include "apps/cu/cu_appconfig_cli11_schema.h" -#include "apps/cu/cu_worker_manager.h" -#include "apps/units/cu_cp/cu_cp_builder.h" +#include "apps/units/cu_cp/cu_cp_application_unit.h" #include "apps/units/cu_cp/cu_cp_config_translators.h" -#include "apps/units/cu_cp/cu_cp_logger_registrator.h" #include "apps/units/cu_cp/cu_cp_unit_config.h" -#include "apps/units/cu_cp/cu_cp_unit_config_cli11_schema.h" -#include "apps/units/cu_cp/cu_cp_unit_config_validator.h" -#include "apps/units/cu_cp/cu_cp_unit_logger_config.h" #include "apps/units/cu_cp/pcap_factory.h" -#include "apps/units/cu_up/cu_up_builder.h" -#include "apps/units/cu_up/cu_up_logger_registrator.h" +#include "apps/units/cu_up/cu_up_application_unit.h" #include "apps/units/cu_up/cu_up_unit_config.h" -#include "apps/units/cu_up/cu_up_unit_config_cli11_schema.h" -#include "apps/units/cu_up/cu_up_unit_config_translators.h" -#include "apps/units/cu_up/cu_up_unit_config_validator.h" -#include "apps/units/cu_up/cu_up_wrapper.h" #include "apps/units/cu_up/pcap_factory.h" +#include "srsran/cu_up/cu_up.h" // TODO remove apps/gnb/*.h #include "apps/gnb/adapters/e2_gateway_remote_connector.h" #include "apps/gnb/gnb_appconfig_translators.h" -#include "srsran/e1ap/gateways/e1_local_connector_factory.h" -#include "srsran/ngap/gateways/n2_connection_client_factory.h" #include "apps/services/application_message_banners.h" #include "apps/services/application_tracer.h" #include "apps/services/buffer_pool/buffer_pool_manager.h" #include "apps/services/stdin_command_dispatcher.h" -#include "apps/units/cu_cp/cu_cp_unit_config_yaml_writer.h" -#include "apps/units/cu_up/cu_up_unit_config_yaml_writer.h" +#include "apps/services/worker_manager.h" +#include "apps/services/worker_manager_config.h" #include "cu_appconfig.h" #include "cu_appconfig_validator.h" #include "cu_appconfig_yaml_writer.h" +#include "srsran/e1ap/gateways/e1_local_connector_factory.h" +#include "srsran/ngap/gateways/n2_connection_client_factory.h" + #include #include @@ -135,9 +125,9 @@ static void initialize_log(const std::string& filename) srslog::init(); } -static void register_app_logs(const logger_appconfig& log_cfg, - const cu_cp_unit_logger_config& cu_cp_loggers, - const cu_up_unit_logger_config& cu_up_loggers) +static void register_app_logs(const logger_appconfig& log_cfg, + cu_cp_application_unit& cu_cp_app_unit, + cu_up_application_unit& cu_up_app_unit) { // Set log-level of app and all non-layer specific components to app level. for (const auto& id : {"ALL", "SCTP-GW", "IO-EPOLL", "UDP-GW", "PCAP"}) { @@ -161,20 +151,24 @@ static void register_app_logs(const logger_appconfig& log_cfg, metrics_logger.set_hex_dump_max_size(log_cfg.hex_max_size); // Register units logs. - register_cu_cp_loggers(cu_cp_loggers); - register_cu_up_loggers(cu_up_loggers); + cu_cp_app_unit.on_loggers_registration(); + cu_up_app_unit.on_loggers_registration(); } -// Temporary helper to create CU-UP. -// TODO remove -std::unique_ptr app_build_cu_up(const cu_up_unit_config& unit_cfg, - cu_worker_manager& workers, - const std::string& f1u_bind_addr, - srs_cu_up::e1_connection_client& e1_conn_client, - f1u_cu_up_gateway& f1u_gateway, - dlt_pcap& gtpu_pcap, - timer_manager& timers, - io_broker& io_brk); +static void fill_cu_worker_manager_config(worker_manager_config& config, const cu_appconfig& unit_cfg) +{ + config.nof_low_prio_threads = unit_cfg.expert_execution_cfg.threads.non_rt_threads.nof_non_rt_threads; + config.low_prio_sched_config = unit_cfg.expert_execution_cfg.affinities.low_priority_cpu_cfg; +} + +static void autoderive_cu_up_parameters_after_parsing(cu_up_unit_config& cu_up_cfg, const cu_cp_unit_config& cu_cp_cfg) +{ + // If no UPF is configured, we set the UPF configuration from the CU-CP AMF configuration. + if (cu_up_cfg.upf_cfg.bind_addr == "auto") { + cu_up_cfg.upf_cfg.bind_addr = cu_cp_cfg.amf_config.amf.bind_addr; + } + cu_up_cfg.upf_cfg.no_core = cu_cp_cfg.amf_config.no_core; +} int main(int argc, char** argv) { @@ -202,41 +196,41 @@ int main(int argc, char** argv) cu_appconfig cu_cfg; configure_cli11_with_cu_appconfig_schema(app, cu_cfg); - cu_cp_unit_config cu_cp_config; - cu_cp_config.pcap_cfg.set_default_filename("/tmp/cu"); - configure_cli11_with_cu_cp_unit_config_schema(app, cu_cp_config); + auto cu_cp_app_unit = create_cu_cp_application_unit("cu"); + cu_cp_app_unit->on_parsing_configuration_registration(app); - cu_up_unit_config cu_up_config; - cu_up_config.pcap_cfg.set_default_filename("/tmp/cu"); - configure_cli11_with_cu_up_unit_config_schema(app, cu_up_config); + auto cu_up_app_unit = create_cu_up_application_unit("cu"); + cu_up_app_unit->on_parsing_configuration_registration(app); // Set the callback for the app calling all the autoderivation functions. - app.callback([&app, &cu_cp_config, &cu_up_config]() { - autoderive_cu_cp_parameters_after_parsing(app, cu_cp_config); - autoderive_cu_up_parameters_after_parsing( - cu_cp_config.amf_config.amf.bind_addr, cu_cp_config.amf_config.no_core, cu_up_config); + app.callback([&app, &cu_cp_app_unit, &cu_up_app_unit]() { + cu_cp_app_unit->on_configuration_parameters_autoderivation(app); + + autoderive_cu_up_parameters_after_parsing(cu_up_app_unit->get_cu_up_unit_config(), + cu_cp_app_unit->get_cu_cp_unit_config()); }); // Parse arguments. CLI11_PARSE(app, argc, argv); // Check the modified configuration. - if (!validate_cu_appconfig(cu_cfg) || !validate_cu_cp_unit_config(cu_cp_config) || - !validate_cu_up_unit_config(cu_up_config)) { + if (!validate_cu_appconfig(cu_cfg) || + !cu_cp_app_unit->on_configuration_validation(os_sched_affinity_bitmask::available_cpus()) || + !cu_up_app_unit->on_configuration_validation(os_sched_affinity_bitmask::available_cpus())) { report_error("Invalid configuration detected.\n"); } // Set up logging. initialize_log(cu_cfg.log_cfg.filename); - register_app_logs(cu_cfg.log_cfg, cu_cp_config.loggers, cu_up_config.loggers); + register_app_logs(cu_cfg.log_cfg, *cu_cp_app_unit, *cu_up_app_unit); // Log input configuration. srslog::basic_logger& config_logger = srslog::fetch_basic_logger("CONFIG"); if (config_logger.debug.enabled()) { YAML::Node node; fill_cu_appconfig_in_yaml_schema(node, cu_cfg); - fill_cu_up_config_in_yaml_schema(node, cu_up_config); - fill_cu_cp_config_in_yaml_schema(node, cu_cp_config); + cu_cp_app_unit->dump_config(node); + cu_up_app_unit->dump_config(node); config_logger.debug("Input configuration (all values): \n{}", YAML::Dump(node)); } else { config_logger.info("Input configuration (only non-default values): \n{}", app.config_to_str(false, false)); @@ -273,11 +267,17 @@ int main(int argc, char** argv) check_drm_kms_polling(cu_logger); // Create worker manager. - cu_worker_manager workers{cu_cfg, cu_cp_config.pcap_cfg, cu_up_config.pcap_cfg, cu_up_config.gtpu_queue_size}; + worker_manager_config worker_manager_cfg; + fill_cu_worker_manager_config(worker_manager_cfg, cu_cfg); + cu_cp_app_unit->fill_worker_manager_config(worker_manager_cfg); + cu_up_app_unit->fill_worker_manager_config(worker_manager_cfg); + worker_manager workers{worker_manager_cfg}; // Create layer specific PCAPs. - cu_cp_dlt_pcaps cu_cp_dlt_pcaps = create_cu_cp_dlt_pcap(cu_cp_config.pcap_cfg, *workers.get_executor_getter()); - cu_up_dlt_pcaps cu_up_dlt_pcaps = create_cu_up_dlt_pcaps(cu_up_config.pcap_cfg, *workers.get_executor_getter()); + cu_cp_dlt_pcaps cu_cp_dlt_pcaps = + create_cu_cp_dlt_pcap(cu_cp_app_unit->get_cu_cp_unit_config().pcap_cfg, *workers.get_executor_getter()); + cu_up_dlt_pcaps cu_up_dlt_pcaps = + create_cu_up_dlt_pcaps(cu_up_app_unit->get_cu_up_unit_config().pcap_cfg, *workers.get_executor_getter()); // Create IO broker. const auto& low_prio_cpu_mask = cu_cfg.expert_execution_cfg.affinities.low_priority_cpu_cfg.mask; @@ -306,7 +306,7 @@ int main(int argc, char** argv) std::unique_ptr cu_f1u_gw = srs_cu_up::create_udp_ngu_gateway(cu_f1u_gw_config, *epoll_broker, *workers.cu_up_io_ul_exec); std::unique_ptr cu_f1u_conn = - srs_cu_up::create_split_f1u_gw({cu_f1u_gw.get(), cu_f1u_gtpu_demux.get(), *cu_up_dlt_pcaps.f1u, GTPU_PORT}); + srs_cu_up::create_split_f1u_gw({*cu_f1u_gw, *cu_f1u_gtpu_demux, *cu_up_dlt_pcaps.f1u, GTPU_PORT}); // Create E1AP local connector std::unique_ptr e1_gw = @@ -325,18 +325,11 @@ int main(int argc, char** argv) cu_cp_dependencies.cu_cp_executor = workers.cu_cp_exec; cu_cp_dependencies.cu_cp_e2_exec = workers.cu_cp_e2_exec; cu_cp_dependencies.timers = cu_timers; - - // Create N2 Client Gateways. - cu_cp_dependencies.n2_clients.push_back(srs_cu_cp::create_n2_connection_client(generate_n2_client_config( - cu_cp_config.amf_config.no_core, cu_cp_config.amf_config.amf, *cu_cp_dlt_pcaps.ngap, *epoll_broker))); - - for (const auto& amf : cu_cp_config.extra_amfs) { - cu_cp_dependencies.n2_clients.push_back(srs_cu_cp::create_n2_connection_client( - generate_n2_client_config(cu_cp_config.amf_config.no_core, amf, *cu_cp_dlt_pcaps.ngap, *epoll_broker))); - } + cu_cp_dependencies.ngap_pcap = cu_cp_dlt_pcaps.ngap.get(); + cu_cp_dependencies.broker = epoll_broker.get(); // create CU-CP. - auto cu_cp_obj_and_cmds = build_cu_cp(cu_cp_config, cu_cp_dependencies); + auto cu_cp_obj_and_cmds = cu_cp_app_unit->create_cu_cp(cu_cp_dependencies); srs_cu_cp::cu_cp& cu_cp_obj = *cu_cp_obj_and_cmds.unit; // Create console helper object for commands and metrics printing. @@ -359,14 +352,15 @@ int main(int argc, char** argv) cu_f1c_gw->attach_cu_cp(cu_cp_obj.get_f1c_handler()); // Create and start CU-UP - std::unique_ptr cu_up_obj = app_build_cu_up(cu_up_config, - workers, - cu_cfg.nru_cfg.bind_addr, - *e1_gw, - *cu_f1u_conn, - *cu_up_dlt_pcaps.n3, - *cu_timers, - *epoll_broker); + cu_up_unit_dependencies cu_up_unit_deps; + cu_up_unit_deps.workers = &workers; + cu_up_unit_deps.e1ap_conn_client = e1_gw.get(); + cu_up_unit_deps.f1u_gateway = cu_f1u_conn.get(); + cu_up_unit_deps.gtpu_pcap = cu_up_dlt_pcaps.n3.get(); + cu_up_unit_deps.timers = cu_timers; + cu_up_unit_deps.io_brk = epoll_broker.get(); + + std::unique_ptr cu_up_obj = cu_up_app_unit->create_cu_up_unit(cu_up_unit_deps); cu_up_obj->start(); { @@ -398,45 +392,3 @@ int main(int argc, char** argv) return 0; } - -// Temporary helper to create CU-UP. -// TODO remove -std::unique_ptr app_build_cu_up(const cu_up_unit_config& unit_cfg, - cu_worker_manager& workers, - const std::string& f1u_bind_address, - srs_cu_up::e1_connection_client& e1_conn_client, - f1u_cu_up_gateway& f1u_gateway, - dlt_pcap& gtpu_pcap, - timer_manager& timers, - io_broker& io_brk) -{ - srs_cu_up::cu_up_configuration config = generate_cu_up_config(unit_cfg); - config.ctrl_executor = workers.cu_up_ctrl_exec; - config.cu_up_e2_exec = workers.cu_up_e2_exec; - config.ue_exec_pool = workers.cu_up_exec_mapper.get(); - config.io_ul_executor = workers.cu_up_io_ul_exec; // Optionally select separate exec for UL IO - config.e1ap.e1_conn_client = &e1_conn_client; - config.f1u_gateway = &f1u_gateway; - config.gtpu_pcap = >pu_pcap; - config.timers = &timers; - config.qos = generate_cu_up_qos_config(unit_cfg); - - config.net_cfg.f1u_bind_addr = f1u_bind_address; // TODO remove this parameter and make sure that the CU-UP gets the - // bind address directly from the gateway. - - // Create NG-U gateway. - std::unique_ptr ngu_gw; - if (not unit_cfg.upf_cfg.no_core) { - udp_network_gateway_config ngu_gw_config = {}; - ngu_gw_config.bind_address = config.net_cfg.n3_bind_addr; - ngu_gw_config.bind_port = config.net_cfg.n3_bind_port; - ngu_gw_config.bind_interface = config.net_cfg.n3_bind_interface; - ngu_gw_config.rx_max_mmsg = config.net_cfg.n3_rx_max_mmsg; - ngu_gw = srs_cu_up::create_udp_ngu_gateway(ngu_gw_config, io_brk, *workers.cu_up_io_ul_exec); - } else { - ngu_gw = srs_cu_up::create_no_core_ngu_gateway(); - } - config.ngu_gw = ngu_gw.get(); - - return std::make_unique(std::move(ngu_gw), create_cu_up(config)); -} diff --git a/apps/cu/cu_worker_manager.cpp b/apps/cu/cu_worker_manager.cpp deleted file mode 100644 index 7623049762..0000000000 --- a/apps/cu/cu_worker_manager.cpp +++ /dev/null @@ -1,207 +0,0 @@ -/* - * - * Copyright 2021-2024 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN 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. - * - * srsRAN 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 "cu_worker_manager.h" -#include "srsran/support/event_tracing.h" - -using namespace srsran; - -static const uint32_t task_worker_queue_size = 2048; - -cu_worker_manager::cu_worker_manager(const cu_appconfig& appcfg, - cu_cp_unit_pcap_config cu_cp_pcap_cfg, - cu_up_unit_pcap_config cu_up_pcap_cfg, - unsigned gtpu_queue_size) : - low_prio_affinity_mng({appcfg.expert_execution_cfg.affinities.low_priority_cpu_cfg}) -{ - create_low_prio_executors(appcfg, cu_cp_pcap_cfg, cu_up_pcap_cfg, gtpu_queue_size); - associate_low_prio_executors(); -} - -void cu_worker_manager::stop() -{ - exec_mng.stop(); -} - -void cu_worker_manager::create_worker_pool(const std::string& name, - unsigned nof_workers, - unsigned queue_size, - const std::vector& execs, - os_thread_realtime_priority prio, - span cpu_masks) -{ - using namespace execution_config_helper; - - concurrent_queue_policy queue_policy = concurrent_queue_policy::locking_mpmc; - - const worker_pool pool{name, - nof_workers, - {{queue_policy, queue_size}}, - execs, - std::chrono::microseconds{queue_policy == concurrent_queue_policy::locking_mpmc ? 0 : 10}, - prio, - std::vector{cpu_masks.begin(), cpu_masks.end()}}; - if (not exec_mng.add_execution_context(create_execution_context(pool))) { - report_fatal_error("Failed to instantiate {} execution context", pool.name); - } -} - -void cu_worker_manager::create_prio_worker(const std::string& name, - unsigned queue_size, - const std::vector& execs, - const os_sched_affinity_bitmask& mask, - os_thread_realtime_priority prio) -{ - using namespace execution_config_helper; - - const single_worker worker_desc{ - name, {concurrent_queue_policy::locking_mpsc, queue_size}, execs, std::nullopt, prio, mask}; - if (not exec_mng.add_execution_context(create_execution_context(worker_desc))) { - report_fatal_error("Failed to instantiate {} execution context", worker_desc.name); - } -} - -void append_pcap_strands(std::vector& strand_list, - const cu_cp_unit_pcap_config& cp_pcaps, - const cu_up_unit_pcap_config& up_pcaps) -{ - using namespace execution_config_helper; - - // Default configuration for each pcap writer strand. - strand base_strand_cfg{{{"pcap_exec", concurrent_queue_policy::lockfree_mpmc, task_worker_queue_size}}}; - - // These layers have very low throughput, so no point in instantiating more than one strand. - // This means that there is no parallelization in pcap writing across these layers. - if (cp_pcaps.f1ap.enabled or cp_pcaps.ngap.enabled or cp_pcaps.e1ap.enabled) { - strand_list.emplace_back(base_strand_cfg); - } - - if (up_pcaps.n3.enabled) { - base_strand_cfg.queues[0].name = "n3_pcap_exec"; - strand_list.emplace_back(base_strand_cfg); - } - - if (up_pcaps.f1u.enabled) { - base_strand_cfg.queues[0].name = "f1u_pcap_exec"; - strand_list.emplace_back(base_strand_cfg); - } -} - -execution_config_helper::worker_pool cu_worker_manager::create_low_prio_workers(const cu_appconfig& appcfg) -{ - using namespace execution_config_helper; - - // Configure non-RT worker pool. - worker_pool non_rt_pool{ - "non_rt_pool", - appcfg.expert_execution_cfg.threads.non_rt_threads.nof_non_rt_threads, - {{concurrent_queue_policy::lockfree_mpmc, task_worker_queue_size}, // two task priority levels. - {concurrent_queue_policy::lockfree_mpmc, task_worker_queue_size}}, - // Left empty, is filled later. - {}, - std::chrono::microseconds{100}, - os_thread_realtime_priority::no_realtime(), - std::vector{appcfg.expert_execution_cfg.affinities.low_priority_cpu_cfg.mask}}; - - return non_rt_pool; -} - -void cu_worker_manager::create_low_prio_executors(const cu_appconfig& appcfg, - const cu_cp_unit_pcap_config& cu_cp_pcap_cfg, - const cu_up_unit_pcap_config& cu_up_pcap_cfg, - unsigned gtpu_queue_size) -{ - using namespace execution_config_helper; - // TODO: split executor creation and association to workers - worker_pool non_rt_pool = create_low_prio_workers(appcfg); - - // Associate executors to the worker pool. - // Used for PCAP writing. - non_rt_pool.executors.emplace_back("low_prio_exec", task_priority::max - 1); - // Used for control plane and timer management. - non_rt_pool.executors.emplace_back("high_prio_exec", task_priority::max); - // Used to serialize all CU-UP tasks, while CU-UP does not support multithreading. - non_rt_pool.executors.push_back({"cu_up_strand", - task_priority::max - 1, - {}, // define CU-UP strands below. - task_worker_queue_size}); - - std::vector& low_prio_strands = non_rt_pool.executors[0].strands; - std::vector& high_prio_strands = non_rt_pool.executors[1].strands; - std::vector& cu_up_strands = non_rt_pool.executors[2].strands; - - // Configuration of strands for PCAP writing. These strands will use the low priority executor. - append_pcap_strands(low_prio_strands, cu_cp_pcap_cfg, cu_up_pcap_cfg); - - // Configuration of strand for the control plane handling (CU-CP and DU-high control plane). This strand will - // support two priority levels, the highest being for timer management. - strand cp_strand{{{"timer_exec", concurrent_queue_policy::lockfree_spsc, task_worker_queue_size}, - {"ctrl_exec", concurrent_queue_policy::lockfree_mpmc, task_worker_queue_size}}}; - high_prio_strands.push_back(cp_strand); - - // Configuration of strands for user plane handling (CU-UP and DU-low user plane). Given that the CU-UP doesn't - // currently support multithreading, these strands will point to a strand that interfaces with the non-RT thread pool. - // Each UE strand will have three queues, one for timer management and configuration, one for DL data plane and one - // for UL data plane. - cu_up_strands.push_back( - strand{{{"cu_up_ctrl_exec", concurrent_queue_policy::lockfree_mpmc, task_worker_queue_size}, - {"cu_up_io_ul_exec", concurrent_queue_policy::lockfree_mpmc, task_worker_queue_size}}}); - for (unsigned i = 0; i != nof_cu_up_ue_strands; ++i) { - cu_up_strands.push_back( - strand{{{fmt::format("ue_up_ctrl_exec#{}", i), concurrent_queue_policy::lockfree_mpmc, task_worker_queue_size}, - {fmt::format("ue_up_ul_exec#{}", i), - concurrent_queue_policy::lockfree_mpmc, - gtpu_queue_size}, // TODO: Consider separate param for size of UL queue if needed. - {fmt::format("ue_up_dl_exec#{}", i), concurrent_queue_policy::lockfree_mpmc, gtpu_queue_size}}}); - } - - // Create non-RT worker pool. - if (not exec_mng.add_execution_context(create_execution_context(non_rt_pool))) { - report_fatal_error("Failed to instantiate {} execution context", non_rt_pool.name); - } -} - -void cu_worker_manager::associate_low_prio_executors() -{ - using namespace execution_config_helper; - const auto& exec_map = exec_mng.executors(); - - // Update executor pointer mapping - cu_cp_exec = exec_map.at("ctrl_exec"); - cu_cp_e2_exec = exec_map.at("ctrl_exec"); - metrics_hub_exec = exec_map.at("ctrl_exec"); - cu_up_ctrl_exec = exec_map.at("cu_up_ctrl_exec"); - cu_up_io_ul_exec = exec_map.at("cu_up_io_ul_exec"); - cu_up_e2_exec = exec_map.at("cu_up_ctrl_exec"); - - // Create CU-UP execution mapper object. - std::vector ue_up_dl_execs(nof_cu_up_ue_strands, nullptr); - std::vector ue_up_ul_execs(nof_cu_up_ue_strands, nullptr); - std::vector ue_up_ctrl_execs(nof_cu_up_ue_strands, nullptr); - for (unsigned i = 0; i != nof_cu_up_ue_strands; ++i) { - ue_up_dl_execs[i] = exec_map.at(fmt::format("ue_up_dl_exec#{}", i)); - ue_up_ul_execs[i] = exec_map.at(fmt::format("ue_up_ul_exec#{}", i)); - ue_up_ctrl_execs[i] = exec_map.at(fmt::format("ue_up_ctrl_exec#{}", i)); - } - cu_up_exec_mapper = srs_cu_up::make_cu_up_executor_pool( - *exec_map.at("cu_up_ctrl_exec"), ue_up_dl_execs, ue_up_ul_execs, ue_up_ctrl_execs, *exec_map.at("low_prio_exec")); -} diff --git a/apps/cu/cu_worker_manager.h b/apps/cu/cu_worker_manager.h deleted file mode 100644 index ae8c8fd8c7..0000000000 --- a/apps/cu/cu_worker_manager.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - * - * Copyright 2021-2024 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN 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. - * - * srsRAN 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/. - * - */ - -#pragma once - -#include "apps/services/os_sched_affinity_manager.h" -#include "apps/services/worker_manager_worker_getter.h" -#include "apps/units/cu_cp/cu_cp_unit_pcap_config.h" -#include "apps/units/cu_up/cu_up_unit_pcap_config.h" -#include "cu_appconfig.h" -#include "srsran/cu_up/cu_up_executor_pool.h" -#include "srsran/support/executors/task_execution_manager.h" -#include "srsran/support/executors/task_executor.h" - -namespace srsran { - -/// Manages the workers of the app. -struct cu_worker_manager : public worker_manager_executor_getter { - cu_worker_manager(const cu_appconfig& appcfg, - cu_cp_unit_pcap_config cu_cp_pcap_cfg, - cu_up_unit_pcap_config cu_up_pcap_cfg, - unsigned gtpu_queue_size); - - void stop(); - - /// cu-cp ctrl exec points to general ctrl_worker - /// cu-up ue exec points to the general ue_worker - /// - /// The handler side is responsible for executor dispatching: - /// - ngap::handle_message calls cu-cp ctrl exec - /// - f1ap_cu::handle_message calls cu-cp ctrl exec - /// - e1ap_cu_cp::handle_message calls cu-cp ctrl exec - /// - e1ap_cu_up::handle_message calls cu-up ue exec - - task_executor* cu_cp_exec = nullptr; - task_executor* cu_up_ctrl_exec = nullptr; ///< CU-UP executor for control - task_executor* cu_up_io_ul_exec = nullptr; ///< CU-UP executor for UL socket transmission - task_executor* cu_cp_e2_exec = nullptr; - task_executor* cu_up_e2_exec = nullptr; - task_executor* metrics_hub_exec = nullptr; - - std::unique_ptr cu_up_exec_mapper; - - // Gets the DU-low downlink executors. - void get_du_low_dl_executors(std::vector& executors, unsigned sector_id) const; - - /// Get executor based on the name. - task_executor* find_executor(const std::string& name) const - { - auto it = exec_mng.executors().find(name); - return it != exec_mng.executors().end() ? it->second : nullptr; - } - - task_executor& get_executor(const std::string& name) const override { return *exec_mng.executors().at(name); } - - worker_manager_executor_getter* get_executor_getter() { return this; } - -private: - static const unsigned nof_cu_up_ue_strands = 16; - - /// Manager of execution contexts and respective executors instantiated by the application. - task_execution_manager exec_mng; - - os_sched_affinity_manager low_prio_affinity_mng; - - /// CPU affinity bitmask manager per cell. - std::vector affinity_mng; - - /// Helper method to create workers with non zero priority. - void create_prio_worker(const std::string& name, - unsigned queue_size, - const std::vector& execs, - const os_sched_affinity_bitmask& mask, - os_thread_realtime_priority prio = os_thread_realtime_priority::no_realtime()); - - /// Helper method to create worker pool. - void create_worker_pool(const std::string& name, - unsigned nof_workers, - unsigned queue_size, - const std::vector& execs, - os_thread_realtime_priority prio = os_thread_realtime_priority::no_realtime(), - span cpu_masks = {}); - - execution_config_helper::worker_pool create_low_prio_workers(const cu_appconfig& appcfg); - void create_low_prio_executors(const cu_appconfig& appcfg, - const cu_cp_unit_pcap_config& cu_cp_pcap_cfg, - const cu_up_unit_pcap_config& cu_up_pcap_cfg, - unsigned gtpu_queue_size); - void associate_low_prio_executors(); -}; - -} // namespace srsran diff --git a/apps/du/du.cpp b/apps/du/du.cpp index 15831aa472..c05e0cfb2f 100644 --- a/apps/du/du.cpp +++ b/apps/du/du.cpp @@ -177,8 +177,7 @@ int main(int argc, char** argv) // Configure CLI11 with the DU application configuration schema. configure_cli11_with_du_appconfig_schema(app, du_cfg); - auto du_app_unit = create_flexible_du_application_unit(); - du_app_unit->get_du_high_unit_config().pcaps.set_default_filename("/tmp/du"); + auto du_app_unit = create_flexible_du_application_unit("du"); du_app_unit->on_parsing_configuration_registration(app); // Set the callback for the app calling all the autoderivation functions. diff --git a/apps/gnb/gnb.cpp b/apps/gnb/gnb.cpp index a42f61c3d6..18bf1a7d29 100644 --- a/apps/gnb/gnb.cpp +++ b/apps/gnb/gnb.cpp @@ -20,23 +20,22 @@ * */ +#include "srsran/support/backtrace.h" +#include "srsran/support/config_parsers.h" #include "srsran/support/cpu_features.h" #include "srsran/support/dynlink_manager.h" #include "srsran/support/event_tracing.h" +#include "srsran/support/io/io_broker_factory.h" #include "srsran/support/signal_handling.h" #include "srsran/support/versioning/build_info.h" #include "srsran/support/versioning/version.h" -#include "srsran/f1u/local_connector/f1u_local_connector.h" - -#include "srsran/ngap/gateways/n2_connection_client_factory.h" -#include "srsran/support/io/io_broker_factory.h" - +#include "srsran/du/du_power_controller.h" #include "srsran/e1ap/gateways/e1_local_connector_factory.h" #include "srsran/f1ap/gateways/f1c_local_connector_factory.h" +#include "srsran/f1u/local_connector/f1u_local_connector.h" #include "srsran/gtpu/ngu_gateway.h" -#include "srsran/support/backtrace.h" -#include "srsran/support/config_parsers.h" +#include "srsran/ngap/gateways/n2_connection_client_factory.h" #include "gnb_appconfig.h" #include "gnb_appconfig_cli11_schema.h" @@ -44,49 +43,34 @@ #include "gnb_appconfig_validators.h" #include "gnb_appconfig_yaml_writer.h" -#include "apps/services/worker_manager.h" - +#include "apps/services/application_message_banners.h" #include "apps/services/application_tracer.h" +#include "apps/services/buffer_pool/buffer_pool_manager.h" +#include "apps/services/core_isolation_manager.h" +#include "apps/services/e2_metric_connector_manager.h" +#include "apps/services/metrics/metrics_manager.h" +#include "apps/services/metrics/metrics_notifier_proxy.h" #include "apps/services/stdin_command_dispatcher.h" - -#include "apps/units/flexible_du/split_dynamic/dynamic_du_factory.h" +#include "apps/services/worker_manager.h" #include "apps/gnb/adapters/e2_gateway_remote_connector.h" -#include "apps/services/e2_metric_connector_manager.h" // Include ThreadSanitizer (TSAN) options if thread sanitization is enabled. // This include is not unused - it helps prevent false alarms from the thread sanitizer. #include "srsran/support/tsan_options.h" +#include "apps/units/cu_cp/cu_cp_application_unit.h" #include "apps/units/cu_cp/cu_cp_config_translators.h" -#include "apps/units/cu_cp/cu_cp_logger_registrator.h" -#include "apps/units/cu_cp/cu_cp_unit_config_cli11_schema.h" -#include "apps/units/cu_cp/cu_cp_unit_config_validator.h" -#include "apps/units/cu_up/cu_up_logger_registrator.h" -#include "apps/units/cu_up/cu_up_unit_config_cli11_schema.h" -#include "apps/units/cu_up/cu_up_unit_config_validator.h" - -#include - -#include "apps/services/application_message_banners.h" -#include "apps/services/buffer_pool/buffer_pool_manager.h" -#include "apps/services/core_isolation_manager.h" -#include "apps/services/metrics/metrics_manager.h" -#include "apps/services/metrics/metrics_notifier_proxy.h" -#include "apps/units/cu_cp/cu_cp_builder.h" -#include "apps/units/cu_cp/cu_cp_unit_config_yaml_writer.h" +#include "apps/units/cu_cp/cu_cp_unit_config.h" #include "apps/units/cu_cp/pcap_factory.h" -#include "apps/units/cu_up/cu_up_builder.h" -#include "apps/units/cu_up/cu_up_unit_config_translators.h" -#include "apps/units/cu_up/cu_up_unit_config_yaml_writer.h" +#include "apps/units/cu_up/cu_up_application_unit.h" +#include "apps/units/cu_up/cu_up_unit_config.h" #include "apps/units/cu_up/pcap_factory.h" #include "apps/units/flexible_du/du_high/du_high_config.h" #include "apps/units/flexible_du/du_high/pcap_factory.h" #include "apps/units/flexible_du/flexible_du_application_unit.h" -#include "srsran/du/du_power_controller.h" -#include "srsran/support/cli11_utils.h" - +#include #include #ifdef DPDK_FOUND @@ -146,10 +130,10 @@ static void initialize_log(const std::string& filename) srslog::init(); } -static void register_app_logs(const logger_appconfig& log_cfg, - const cu_cp_unit_logger_config& cu_cp_loggers, - const cu_up_unit_logger_config& cu_up_loggers, - flexible_du_application_unit& du_app_unit) +static void register_app_logs(const logger_appconfig& log_cfg, + cu_cp_application_unit& cu_cp_app_unit, + cu_up_application_unit& cu_up_app_unit, + flexible_du_application_unit& du_app_unit) { // Set log-level of app and all non-layer specific components to app level. for (const auto& id : {"ALL", "SCTP-GW", "IO-EPOLL", "UDP-GW", "PCAP"}) { @@ -177,8 +161,8 @@ static void register_app_logs(const logger_appconfig& log_cfg, e2ap_logger.set_hex_dump_max_size(log_cfg.hex_max_size); // Register units logs. - register_cu_cp_loggers(cu_cp_loggers); - register_cu_up_loggers(cu_up_loggers); + cu_cp_app_unit.on_loggers_registration(); + cu_up_app_unit.on_loggers_registration(); du_app_unit.on_loggers_registration(); } @@ -202,6 +186,15 @@ static void autoderive_slicing_args(du_high_unit_config& du_hi_cfg, cu_cp_unit_c } } +static void autoderive_cu_up_parameters_after_parsing(cu_up_unit_config& cu_up_cfg, const cu_cp_unit_config& cu_cp_cfg) +{ + // If no UPF is configured, we set the UPF configuration from the CU-CP AMF configuration. + if (cu_up_cfg.upf_cfg.bind_addr == "auto") { + cu_up_cfg.upf_cfg.bind_addr = cu_cp_cfg.amf_config.amf.bind_addr; + } + cu_up_cfg.upf_cfg.no_core = cu_cp_cfg.amf_config.no_core; +} + int main(int argc, char** argv) { // Set the application error handler. @@ -228,59 +221,57 @@ int main(int argc, char** argv) // Configure CLI11 with the gNB application configuration schema. configure_cli11_with_gnb_appconfig_schema(app, gnb_cfg); - cu_cp_unit_config cu_cp_config; - cu_cp_config.pcap_cfg.set_default_filename("/tmp/gnb"); - configure_cli11_with_cu_cp_unit_config_schema(app, cu_cp_config); + auto cu_cp_app_unit = create_cu_cp_application_unit("gnb"); + cu_cp_app_unit->on_parsing_configuration_registration(app); - cu_up_unit_config cu_up_config; - cu_up_config.pcap_cfg.set_default_filename("/tmp/gnb"); - configure_cli11_with_cu_up_unit_config_schema(app, cu_up_config); + auto cu_up_app_unit = create_cu_up_application_unit("gnb"); + cu_up_app_unit->on_parsing_configuration_registration(app); - auto du_app_unit = create_flexible_du_application_unit(); - du_app_unit->get_du_high_unit_config().pcaps.set_default_filename("/tmp/gnb"); + auto du_app_unit = create_flexible_du_application_unit("gnb"); du_app_unit->on_parsing_configuration_registration(app); // Set the callback for the app calling all the autoderivation functions. - app.callback([&app, &gnb_cfg, &du_app_unit, &cu_cp_config, &cu_up_config]() { + app.callback([&app, &gnb_cfg, &du_app_unit, &cu_cp_app_unit, &cu_up_app_unit]() { autoderive_gnb_parameters_after_parsing(app, gnb_cfg); - autoderive_slicing_args(du_app_unit->get_du_high_unit_config(), cu_cp_config); + autoderive_slicing_args(du_app_unit->get_du_high_unit_config(), cu_cp_app_unit->get_cu_cp_unit_config()); du_app_unit->on_configuration_parameters_autoderivation(app); // If test mode is enabled, we auto-enable "no_core" option and generate a amf config with no core. if (du_app_unit->get_du_high_unit_config().is_testmode_enabled()) { - cu_cp_config.amf_config.no_core = true; - cu_cp_config.amf_config.amf.supported_tas = {{7, {{"00101", {s_nssai_t{1}}}}}}; + cu_cp_app_unit->get_cu_cp_unit_config().amf_config.no_core = true; + cu_cp_app_unit->get_cu_cp_unit_config().amf_config.amf.supported_tas = {{7, {{"00101", {s_nssai_t{1}}}}}}; } - autoderive_cu_cp_parameters_after_parsing(app, cu_cp_config); - autoderive_cu_up_parameters_after_parsing( - cu_cp_config.amf_config.amf.bind_addr, cu_cp_config.amf_config.no_core, cu_up_config); + cu_cp_app_unit->on_configuration_parameters_autoderivation(app); + autoderive_cu_up_parameters_after_parsing(cu_up_app_unit->get_cu_up_unit_config(), + cu_cp_app_unit->get_cu_cp_unit_config()); }); // Parse arguments. CLI11_PARSE(app, argc, argv); + auto available_cpu_mask = (gnb_cfg.expert_execution_cfg.affinities.isolated_cpus) + ? gnb_cfg.expert_execution_cfg.affinities.isolated_cpus.value() + : os_sched_affinity_bitmask::available_cpus(); // Check the modified configuration. - if (!validate_appconfig(gnb_cfg) || !validate_cu_cp_unit_config(cu_cp_config) || - !validate_cu_up_unit_config(cu_up_config) || - !du_app_unit->on_configuration_validation((gnb_cfg.expert_execution_cfg.affinities.isolated_cpus) - ? gnb_cfg.expert_execution_cfg.affinities.isolated_cpus.value() - : os_sched_affinity_bitmask::available_cpus()) || - !validate_plmn_and_tacs(du_app_unit->get_du_high_unit_config(), cu_cp_config)) { + if (!validate_appconfig(gnb_cfg) || !cu_cp_app_unit->on_configuration_validation(available_cpu_mask) || + !cu_up_app_unit->on_configuration_validation(available_cpu_mask) || + !du_app_unit->on_configuration_validation(available_cpu_mask) || + !validate_plmn_and_tacs(du_app_unit->get_du_high_unit_config(), cu_cp_app_unit->get_cu_cp_unit_config())) { report_error("Invalid configuration detected.\n"); } // Set up logging. initialize_log(gnb_cfg.log_cfg.filename); - register_app_logs(gnb_cfg.log_cfg, cu_cp_config.loggers, cu_up_config.loggers, *du_app_unit); + register_app_logs(gnb_cfg.log_cfg, *cu_cp_app_unit, *cu_up_app_unit, *du_app_unit); // Log input configuration. srslog::basic_logger& config_logger = srslog::fetch_basic_logger("CONFIG"); if (config_logger.debug.enabled()) { YAML::Node node; fill_gnb_appconfig_in_yaml_schema(node, gnb_cfg); - fill_cu_up_config_in_yaml_schema(node, cu_up_config); - fill_cu_cp_config_in_yaml_schema(node, cu_cp_config); + cu_cp_app_unit->dump_config(node); + cu_up_app_unit->dump_config(node); du_app_unit->dump_config(node); config_logger.debug("Input configuration (all values): \n{}", YAML::Dump(node)); } else { @@ -333,8 +324,8 @@ int main(int argc, char** argv) // Instantiate worker manager. worker_manager_config worker_manager_cfg; fill_gnb_worker_manager_config(worker_manager_cfg, gnb_cfg); - fill_cu_cp_worker_manager_config(worker_manager_cfg, cu_cp_config); - fill_cu_up_worker_manager_config(worker_manager_cfg, cu_up_config); + cu_cp_app_unit->fill_worker_manager_config(worker_manager_cfg); + cu_up_app_unit->fill_worker_manager_config(worker_manager_cfg); du_app_unit->fill_worker_manager_config(worker_manager_cfg); worker_manager workers{worker_manager_cfg}; @@ -349,11 +340,13 @@ int main(int argc, char** argv) // Create layer specific PCAPs. // In the gNB app, there is no point in instantiating two pcaps for each node of E1 and F1. // We disable one accordingly. - cu_up_config.pcap_cfg.disable_e1_pcaps(); + cu_up_app_unit->get_cu_up_unit_config().pcap_cfg.disable_e1_pcaps(); du_app_unit->get_du_high_unit_config().pcaps.disable_f1_pcaps(); - cu_cp_dlt_pcaps cu_cp_dlt_pcaps = create_cu_cp_dlt_pcap(cu_cp_config.pcap_cfg, *workers.get_executor_getter()); - cu_up_dlt_pcaps cu_up_dlt_pcaps = create_cu_up_dlt_pcaps(cu_up_config.pcap_cfg, *workers.get_executor_getter()); - flexible_du_pcaps du_pcaps = create_du_pcaps(du_app_unit->get_du_high_unit_config().pcaps, workers); + cu_cp_dlt_pcaps cu_cp_dlt_pcaps = + create_cu_cp_dlt_pcap(cu_cp_app_unit->get_cu_cp_unit_config().pcap_cfg, *workers.get_executor_getter()); + cu_up_dlt_pcaps cu_up_dlt_pcaps = + create_cu_up_dlt_pcaps(cu_up_app_unit->get_cu_up_unit_config().pcap_cfg, *workers.get_executor_getter()); + flexible_du_pcaps du_pcaps = create_du_pcaps(du_app_unit->get_du_high_unit_config().pcaps, workers); std::unique_ptr f1c_gw = create_f1c_local_connector(f1c_local_connector_config{*cu_cp_dlt_pcaps.f1ap}); @@ -379,18 +372,15 @@ int main(int argc, char** argv) e2_metric_connector_manager e2_metric_connectors(du_app_unit->get_du_high_unit_config().cells_cfg.size()); - // Create CU-CP config. - cu_cp_build_dependencies cu_cp_dependencies; - cu_cp_dependencies.cu_cp_executor = workers.cu_cp_exec; - cu_cp_dependencies.cu_cp_e2_exec = workers.cu_cp_e2_exec; - cu_cp_dependencies.timers = cu_timers; - // Load CU-CP plugins if enabled std::optional ng_handover_plugin = - cu_cp_config.load_plugins ? dynlink_manager::create("libsrsran_plugin_ng_handover.so", gnb_logger) : std::nullopt; - std::optional mocn_plugin = - cu_cp_config.load_plugins ? dynlink_manager::create("libsrsran_plugin_mocn.so", gnb_logger) : std::nullopt; - if (cu_cp_config.load_plugins) { + cu_cp_app_unit->get_cu_cp_unit_config().load_plugins + ? dynlink_manager::create("libsrsran_plugin_ng_handover.so", gnb_logger) + : std::nullopt; + std::optional mocn_plugin = cu_cp_app_unit->get_cu_cp_unit_config().load_plugins + ? dynlink_manager::create("libsrsran_plugin_mocn.so", gnb_logger) + : std::nullopt; + if (cu_cp_app_unit->get_cu_cp_unit_config().load_plugins) { if (not ng_handover_plugin) { gnb_logger.error("Could not open NG Handover plugin"); return -1; @@ -400,7 +390,7 @@ int main(int argc, char** argv) gnb_logger.error("Could not open NG Handover function pointer"); return -1; } - cu_cp_config.start_ng_ho_func = ng_ho_func.value(); + cu_cp_app_unit->get_cu_cp_unit_config().start_ng_ho_func = ng_ho_func.value(); if (not mocn_plugin) { gnb_logger.error("Could not open MOCN plugin"); @@ -411,43 +401,44 @@ int main(int argc, char** argv) gnb_logger.error("Could not open MOCN function pointer"); return -1; } - cu_cp_config.connect_amfs_func_ptr = connect_amfs.value(); - expected disconnect_amfs = mocn_plugin->load_symbol("disconnect_amfs_func"); + cu_cp_app_unit->get_cu_cp_unit_config().connect_amfs_func_ptr = connect_amfs.value(); + expected disconnect_amfs = mocn_plugin->load_symbol("disconnect_amfs_func"); if (not disconnect_amfs) { gnb_logger.error("Could not open MOCN function pointer"); return -1; } - cu_cp_config.disconnect_amfs_func_ptr = disconnect_amfs.value(); - } - - // Create N2 Client Gateways. - cu_cp_dependencies.n2_clients.push_back(srs_cu_cp::create_n2_connection_client(generate_n2_client_config( - cu_cp_config.amf_config.no_core, cu_cp_config.amf_config.amf, *cu_cp_dlt_pcaps.ngap, *epoll_broker))); - - for (const auto& amf : cu_cp_config.extra_amfs) { - cu_cp_dependencies.n2_clients.push_back(srs_cu_cp::create_n2_connection_client( - generate_n2_client_config(cu_cp_config.amf_config.no_core, amf, *cu_cp_dlt_pcaps.ngap, *epoll_broker))); + cu_cp_app_unit->get_cu_cp_unit_config().disconnect_amfs_func_ptr = disconnect_amfs.value(); } - // E2AP configuration. - srsran::sctp_network_connector_config e2_du_nw_config = generate_e2ap_nw_config(gnb_cfg, E2_DU_PPID); - - // Create E2AP GW remote connector. - e2_gateway_remote_connector e2_gw{*epoll_broker, e2_du_nw_config, *du_pcaps.e2ap}; + // Create CU-CP dependencies. + cu_cp_build_dependencies cu_cp_dependencies; + cu_cp_dependencies.cu_cp_executor = workers.cu_cp_exec; + cu_cp_dependencies.cu_cp_e2_exec = workers.cu_cp_e2_exec; + cu_cp_dependencies.timers = cu_timers; + cu_cp_dependencies.ngap_pcap = cu_cp_dlt_pcaps.ngap.get(); + cu_cp_dependencies.broker = epoll_broker.get(); // create CU-CP. - auto cu_cp_obj_and_cmds = build_cu_cp(cu_cp_config, cu_cp_dependencies); + auto cu_cp_obj_and_cmds = cu_cp_app_unit->create_cu_cp(cu_cp_dependencies); srs_cu_cp::cu_cp& cu_cp_obj = *cu_cp_obj_and_cmds.unit; // Create and start CU-UP - std::unique_ptr cu_up_obj = build_cu_up(cu_up_config, - workers, - *e1_gw, - *f1u_conn->get_f1u_cu_up_gateway(), - *cu_up_dlt_pcaps.n3, - *cu_timers, - *epoll_broker); + cu_up_unit_dependencies cu_up_unit_deps; + cu_up_unit_deps.workers = &workers; + cu_up_unit_deps.e1ap_conn_client = e1_gw.get(); + cu_up_unit_deps.f1u_gateway = f1u_conn->get_f1u_cu_up_gateway(); + cu_up_unit_deps.gtpu_pcap = cu_up_dlt_pcaps.n3.get(); + cu_up_unit_deps.timers = cu_timers; + cu_up_unit_deps.io_brk = epoll_broker.get(); + + std::unique_ptr cu_up_obj = cu_up_app_unit->create_cu_up_unit(cu_up_unit_deps); + + // E2AP configuration. + sctp_network_connector_config e2_du_nw_config = generate_e2ap_nw_config(gnb_cfg, E2_DU_PPID); + + // Create E2AP GW remote connector. + e2_gateway_remote_connector e2_gw{*epoll_broker, e2_du_nw_config, *du_pcaps.e2ap}; // Instantiate one DU. app_services::metrics_notifier_proxy_impl metrics_notifier_forwarder; diff --git a/apps/services/worker_manager.cpp b/apps/services/worker_manager.cpp index 768cd014e2..f62a054cfa 100644 --- a/apps/services/worker_manager.cpp +++ b/apps/services/worker_manager.cpp @@ -48,10 +48,6 @@ worker_manager::worker_manager(const worker_manager_config& worker_cfg) : srsran_assert(ru_config_count <= 1, "Worker manager received configuration for more than one RU type"); } - if (worker_cfg.ru_ofh_cfg) { - ru_timing_mask = worker_cfg.ru_ofh_cfg.value().ru_timing_cpu; - } - for (const auto& cell_affinities : worker_cfg.config_affinities) { affinity_mng.emplace_back(cell_affinities); } @@ -59,9 +55,12 @@ worker_manager::worker_manager(const worker_manager_config& worker_cfg) : create_low_prio_executors(worker_cfg); associate_low_prio_executors(); - create_du_executors(worker_cfg.du_hi_cfg, worker_cfg.du_low_cfg, worker_cfg.fapi_cfg); + if (worker_cfg.du_hi_cfg) { + create_du_executors(worker_cfg.du_hi_cfg.value(), worker_cfg.du_low_cfg, worker_cfg.fapi_cfg); + } if (worker_cfg.ru_ofh_cfg) { + ru_timing_mask = worker_cfg.ru_ofh_cfg.value().ru_timing_cpu; ru_txrx_affinity_masks = worker_cfg.ru_ofh_cfg.value().txrx_affinities; create_ofh_executors(worker_cfg.ru_ofh_cfg.value()); } @@ -322,17 +321,19 @@ void worker_manager::create_low_prio_executors(const worker_manager_config& work strand cp_strand{{{"timer_exec", concurrent_queue_policy::lockfree_spsc, task_worker_queue_size, - not worker_cfg.du_hi_cfg.is_rt_mode_enabled}, + worker_cfg.du_hi_cfg ? !worker_cfg.du_hi_cfg->is_rt_mode_enabled : false}, {"ctrl_exec", concurrent_queue_policy::lockfree_mpmc, task_worker_queue_size}}}; high_prio_strands.push_back(cp_strand); // Setup strands for the data plane of all the instantiated DUs. // One strand per DU, each with multiple priority levels. - for (unsigned i = 0; i != worker_cfg.du_hi_cfg.nof_cells; ++i) { - low_prio_strands.push_back(strand{ - {{fmt::format("du_rb_prio_exec#{}", i), concurrent_queue_policy::lockfree_mpmc, task_worker_queue_size}, - {fmt::format("du_rb_ul_exec#{}", i), concurrent_queue_policy::lockfree_mpmc, worker_cfg.gtpu_queue_size}, - {fmt::format("du_rb_dl_exec#{}", i), concurrent_queue_policy::lockfree_mpmc, worker_cfg.gtpu_queue_size}}}); + if (worker_cfg.du_hi_cfg) { + for (unsigned i = 0; i != worker_cfg.du_hi_cfg->nof_cells; ++i) { + low_prio_strands.push_back(strand{ + {{fmt::format("du_rb_prio_exec#{}", i), concurrent_queue_policy::lockfree_mpmc, task_worker_queue_size}, + {fmt::format("du_rb_ul_exec#{}", i), concurrent_queue_policy::lockfree_mpmc, worker_cfg.gtpu_queue_size}, + {fmt::format("du_rb_dl_exec#{}", i), concurrent_queue_policy::lockfree_mpmc, worker_cfg.gtpu_queue_size}}}); + } } // Configuration of strands for user plane handling (CU-UP and DU-low user plane). Given that the CU-UP doesn't diff --git a/apps/services/worker_manager_config.h b/apps/services/worker_manager_config.h index 0f110ff22f..579a76a9cf 100644 --- a/apps/services/worker_manager_config.h +++ b/apps/services/worker_manager_config.h @@ -78,7 +78,7 @@ struct worker_manager_config { /// DU high worker configuration. struct du_high_config { unsigned nof_cells; - bool is_rt_mode_enabled = true; + bool is_rt_mode_enabled; }; /// PCAP worker configuration. @@ -97,14 +97,14 @@ struct worker_manager_config { unsigned nof_low_prio_threads; /// Low priority CPU bitmasks. os_sched_affinity_config low_prio_sched_config; - /// DU high configuration. - du_high_config du_hi_cfg; /// PCAP configuration. pcap_config pcap_cfg; /// GTPU queue size. unsigned gtpu_queue_size; /// Vector of affinities mask indexed by cell. std::vector> config_affinities; + /// DU high configuration. + std::optional du_hi_cfg; /// FAPI configuration. std::optional fapi_cfg; /// DU low configuration diff --git a/apps/units/cu_cp/CMakeLists.txt b/apps/units/cu_cp/CMakeLists.txt index 0201493181..ebf140dacf 100644 --- a/apps/units/cu_cp/CMakeLists.txt +++ b/apps/units/cu_cp/CMakeLists.txt @@ -24,7 +24,8 @@ set(SOURCES cu_cp_unit_config_cli11_schema.cpp cu_cp_config_translators.cpp cu_cp_unit_config_validator.cpp - cu_cp_unit_config_yaml_writer.cpp) + cu_cp_unit_config_yaml_writer.cpp + cu_cp_wrapper.cpp) add_library(srsran_cu_cp_app_unit STATIC ${SOURCES}) target_include_directories(srsran_cu_cp_app_unit PRIVATE ${CMAKE_SOURCE_DIR}) diff --git a/apps/units/cu_cp/cu_cp_application_unit.h b/apps/units/cu_cp/cu_cp_application_unit.h new file mode 100644 index 0000000000..7f0e8beaf0 --- /dev/null +++ b/apps/units/cu_cp/cu_cp_application_unit.h @@ -0,0 +1,55 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN 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. + * + * srsRAN 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/. + * + */ + +#pragma once + +#include "apps/units/application_unit.h" +#include "apps/units/cu_cp/cu_cp_builder.h" +#include + +namespace srsran { +struct worker_manager_config; + +/// CU-CP application unit interface. +class cu_cp_application_unit : public application_unit +{ +public: + virtual ~cu_cp_application_unit() = default; + + /// Creates a CU-CP unit with the given dependencies. + virtual cu_cp_unit create_cu_cp(cu_cp_build_dependencies& dependencies) = 0; + + /// Returns the CU-CP configuration of this CU-CP application unit. + virtual cu_cp_unit_config& get_cu_cp_unit_config() = 0; + virtual const cu_cp_unit_config& get_cu_cp_unit_config() const = 0; + + /// Dumps the CU-CP configuration into the given YAML node. + virtual void dump_config(YAML::Node& node) const = 0; + + /// Fills the given worker manager configuration with the CU-CP parameters. + virtual void fill_worker_manager_config(worker_manager_config& config) = 0; +}; + +/// Creates a CU-CP application unit. +std::unique_ptr create_cu_cp_application_unit(std::string_view app_name); + +} // namespace srsran diff --git a/apps/units/cu_cp/cu_cp_application_unit_impl.cpp b/apps/units/cu_cp/cu_cp_application_unit_impl.cpp index 3ee4825107..b49162a916 100644 --- a/apps/units/cu_cp/cu_cp_application_unit_impl.cpp +++ b/apps/units/cu_cp/cu_cp_application_unit_impl.cpp @@ -21,17 +21,32 @@ */ #include "cu_cp_application_unit_impl.h" +#include "cu_cp_builder.h" +#include "cu_cp_config_translators.h" #include "cu_cp_logger_registrator.h" #include "cu_cp_unit_config_cli11_schema.h" #include "cu_cp_unit_config_validator.h" +#include "cu_cp_unit_config_yaml_writer.h" using namespace srsran; +cu_cp_application_unit_impl::cu_cp_application_unit_impl(std::string_view app_name) +{ + unit_cfg.pcap_cfg.ngap.filename = fmt::format("/tmp/{}_ngap.pcap", app_name); + unit_cfg.pcap_cfg.e1ap.filename = fmt::format("/tmp/{}_e1ap.pcap", app_name); + unit_cfg.pcap_cfg.f1ap.filename = fmt::format("/tmp/{}_f1ap.pcap", app_name); +} + void cu_cp_application_unit_impl::on_parsing_configuration_registration(CLI::App& app) { configure_cli11_with_cu_cp_unit_config_schema(app, unit_cfg); } +void cu_cp_application_unit_impl::on_configuration_parameters_autoderivation(CLI::App& app) +{ + autoderive_cu_cp_parameters_after_parsing(app, unit_cfg); +} + bool cu_cp_application_unit_impl::on_configuration_validation(const os_sched_affinity_bitmask& available_cpus) const { return validate_cu_cp_unit_config(unit_cfg); @@ -41,3 +56,23 @@ void cu_cp_application_unit_impl::on_loggers_registration() { register_cu_cp_loggers(unit_cfg.loggers); } + +cu_cp_unit cu_cp_application_unit_impl::create_cu_cp(cu_cp_build_dependencies& dependencies) +{ + return build_cu_cp(unit_cfg, dependencies); +} + +void cu_cp_application_unit_impl::dump_config(YAML::Node& node) const +{ + fill_cu_cp_config_in_yaml_schema(node, unit_cfg); +} + +void cu_cp_application_unit_impl::fill_worker_manager_config(worker_manager_config& config) +{ + fill_cu_cp_worker_manager_config(config, unit_cfg); +} + +std::unique_ptr srsran::create_cu_cp_application_unit(std::string_view app_name) +{ + return std::make_unique(app_name); +} diff --git a/apps/units/cu_cp/cu_cp_application_unit_impl.h b/apps/units/cu_cp/cu_cp_application_unit_impl.h index 4c4d63ff90..182989db16 100644 --- a/apps/units/cu_cp/cu_cp_application_unit_impl.h +++ b/apps/units/cu_cp/cu_cp_application_unit_impl.h @@ -22,24 +22,42 @@ #pragma once -#include "../application_unit.h" -#include "cu_cp_unit_config.h" +#include "apps/units/cu_cp/cu_cp_application_unit.h" +#include "apps/units/cu_cp/cu_cp_unit_config.h" namespace srsran { /// CU-CP application unit implementation. -class cu_cp_application_unit_impl : public application_unit +class cu_cp_application_unit_impl : public cu_cp_application_unit { public: + explicit cu_cp_application_unit_impl(std::string_view app_name); + // See interface for documentation. void on_parsing_configuration_registration(CLI::App& app) override; + // See interface for documentation. + void on_configuration_parameters_autoderivation(CLI::App& app) override; + // See interface for documentation. bool on_configuration_validation(const os_sched_affinity_bitmask& available_cpus) const override; // See interface for documentation. void on_loggers_registration() override; + // See interface for documentation. + cu_cp_unit create_cu_cp(cu_cp_build_dependencies& dependencies) override; + + // See interface for documentation. + cu_cp_unit_config& get_cu_cp_unit_config() override { return unit_cfg; } + const cu_cp_unit_config& get_cu_cp_unit_config() const override { return unit_cfg; } + + // See interface for documentation. + void dump_config(YAML::Node& node) const override; + + // See interface for documentation. + void fill_worker_manager_config(worker_manager_config& config) override; + private: cu_cp_unit_config unit_cfg; }; diff --git a/apps/units/cu_cp/cu_cp_builder.cpp b/apps/units/cu_cp/cu_cp_builder.cpp index cd44033d2a..e92cb8a2a7 100644 --- a/apps/units/cu_cp/cu_cp_builder.cpp +++ b/apps/units/cu_cp/cu_cp_builder.cpp @@ -23,6 +23,8 @@ #include "cu_cp_builder.h" #include "cu_cp_commands.h" #include "cu_cp_config_translators.h" +#include "cu_cp_unit_config.h" +#include "cu_cp_wrapper.h" #include "srsran/cu_cp/cu_cp_factory.h" using namespace srsran; @@ -32,18 +34,33 @@ cu_cp_unit srsran::build_cu_cp(const cu_cp_unit_config& cu_cp_unit_cfg, cu_cp_bu srsran_assert(dependencies.cu_cp_executor, "Invalid CU-CP executor"); srsran_assert(dependencies.cu_cp_e2_exec, "Invalid E2 executor"); srsran_assert(dependencies.cu_cp_e2_exec, "Invalid E2 executor"); + srsran_assert(dependencies.ngap_pcap, "Invalid NGAP PCAP"); + srsran_assert(dependencies.broker, "Invalid IO broker"); srs_cu_cp::cu_cp_configuration cu_cp_cfg = generate_cu_cp_config(cu_cp_unit_cfg); cu_cp_cfg.services.cu_cp_executor = dependencies.cu_cp_executor; cu_cp_cfg.services.cu_cp_e2_exec = dependencies.cu_cp_e2_exec; cu_cp_cfg.services.timers = dependencies.timers; - for (unsigned pos = 0; pos < dependencies.n2_clients.size(); pos++) { - cu_cp_cfg.ngaps[pos].n2_gw = dependencies.n2_clients[pos].get(); + // Create N2 Client Gateways. + std::vector> n2_clients; + n2_clients.push_back( + srs_cu_cp::create_n2_connection_client(generate_n2_client_config(cu_cp_unit_cfg.amf_config.no_core, + cu_cp_unit_cfg.amf_config.amf, + *dependencies.ngap_pcap, + *dependencies.broker))); + + for (const auto& amf : cu_cp_unit_cfg.extra_amfs) { + n2_clients.push_back(srs_cu_cp::create_n2_connection_client(generate_n2_client_config( + cu_cp_unit_cfg.amf_config.no_core, amf, *dependencies.ngap_pcap, *dependencies.broker))); + } + + for (unsigned pos = 0; pos < n2_clients.size(); pos++) { + cu_cp_cfg.ngaps[pos].n2_gw = n2_clients[pos].get(); } cu_cp_unit cu_cmd_wrapper; - cu_cmd_wrapper.unit = create_cu_cp(cu_cp_cfg); + cu_cmd_wrapper.unit = std::make_unique(std::move(n2_clients), create_cu_cp(cu_cp_cfg)); // Add the commands; cu_cmd_wrapper.commands.push_back(std::make_unique(cu_cmd_wrapper.unit->get_command_handler())); diff --git a/apps/units/cu_cp/cu_cp_builder.h b/apps/units/cu_cp/cu_cp_builder.h index cbf8492318..7d5e70941a 100644 --- a/apps/units/cu_cp/cu_cp_builder.h +++ b/apps/units/cu_cp/cu_cp_builder.h @@ -27,6 +27,8 @@ namespace srsran { +class dlt_pcap; +class io_broker; struct cu_cp_unit_config; struct worker_manager; @@ -36,10 +38,11 @@ class n2_connection_client; /// CU-CP build dependencies. struct cu_cp_build_dependencies { - task_executor* cu_cp_executor = nullptr; - task_executor* cu_cp_e2_exec = nullptr; - std::vector> n2_clients; - timer_manager* timers = nullptr; + task_executor* cu_cp_executor = nullptr; + task_executor* cu_cp_e2_exec = nullptr; + timer_manager* timers = nullptr; + dlt_pcap* ngap_pcap = nullptr; + io_broker* broker = nullptr; }; /// Wraps the CU-CP and its supported application commands. diff --git a/apps/units/cu_cp/cu_cp_unit_pcap_config.h b/apps/units/cu_cp/cu_cp_unit_pcap_config.h index 9db457116a..93e5ee25db 100644 --- a/apps/units/cu_cp/cu_cp_unit_pcap_config.h +++ b/apps/units/cu_cp/cu_cp_unit_pcap_config.h @@ -22,7 +22,6 @@ #pragma once -#include "fmt/core.h" #include namespace srsran { @@ -41,15 +40,6 @@ struct cu_cp_unit_pcap_config { std::string filename = "/tmp/cu_f1ap.pcap"; bool enabled = false; } f1ap; - - /// helper method to set the filename prefix for different apps. - /// This is used to provide different defaults depending on the app, - /// e.g.: "/tmp/gnb_e1ap.pcap" or "/tmp/cu_e1ap.pcap" - void set_default_filename(std::string prefix) - { - ngap.filename = fmt::format("{}_ngap.pcap", prefix); - e1ap.filename = fmt::format("{}_e1ap.pcap", prefix); - f1ap.filename = fmt::format("{}_f1ap.pcap", prefix); - } }; + } // namespace srsran diff --git a/apps/units/cu_cp/cu_cp_wrapper.cpp b/apps/units/cu_cp/cu_cp_wrapper.cpp new file mode 100644 index 0000000000..848e480e60 --- /dev/null +++ b/apps/units/cu_cp/cu_cp_wrapper.cpp @@ -0,0 +1,68 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN 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. + * + * srsRAN 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 "cu_cp_wrapper.h" + +using namespace srsran; +using namespace srs_cu_cp; + +cu_cp_wrapper::cu_cp_wrapper(std::vector> n2_clients_, + std::unique_ptr cu_cp_) : + n2_clients(std::move(n2_clients_)), cu_cp(std::move(cu_cp_)) +{ + srsran_assert(cu_cp, "Invalid CU-CP object"); +} + +cu_cp_f1c_handler& cu_cp_wrapper::get_f1c_handler() +{ + return cu_cp->get_f1c_handler(); +} + +cu_cp_e1_handler& cu_cp_wrapper::get_e1_handler() +{ + return cu_cp->get_e1_handler(); +} + +cu_cp_ng_handler& cu_cp_wrapper::get_ng_handler() +{ + return cu_cp->get_ng_handler(); +} + +cu_cp_command_handler& cu_cp_wrapper::get_command_handler() +{ + return cu_cp->get_command_handler(); +} + +metrics_handler& cu_cp_wrapper::get_metrics_handler() +{ + return cu_cp->get_metrics_handler(); +} + +bool cu_cp_wrapper::start() +{ + return cu_cp->start(); +} + +void cu_cp_wrapper::stop() +{ + cu_cp->stop(); +} diff --git a/apps/units/cu_cp/cu_cp_wrapper.h b/apps/units/cu_cp/cu_cp_wrapper.h new file mode 100644 index 0000000000..88fedc796a --- /dev/null +++ b/apps/units/cu_cp/cu_cp_wrapper.h @@ -0,0 +1,59 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN 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. + * + * srsRAN 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/. + * + */ + +#pragma once + +#include "srsran/cu_cp/cu_cp.h" +#include "srsran/ngap/gateways/n2_connection_client.h" + +namespace srsran { + +/// \brief CU-CP wrapper implementation. +/// +/// The purpose of this wrapper is to keep the life cycle of the objects related only to the CU-CP. +class cu_cp_wrapper : public srs_cu_cp::cu_cp +{ +public: + cu_cp_wrapper(std::vector> n2_clients_, + std::unique_ptr cu_cp_); + + // See interface for documentation. + srs_cu_cp::cu_cp_f1c_handler& get_f1c_handler() override; + // See interface for documentation. + srs_cu_cp::cu_cp_e1_handler& get_e1_handler() override; + // See interface for documentation. + srs_cu_cp::cu_cp_ng_handler& get_ng_handler() override; + // See interface for documentation. + srs_cu_cp::cu_cp_command_handler& get_command_handler() override; + // See interface for documentation. + srs_cu_cp::metrics_handler& get_metrics_handler() override; + // See interface for documentation. + bool start() override; + // See interface for documentation. + void stop() override; + +private: + std::vector> n2_clients; + std::unique_ptr cu_cp; +}; + +} // namespace srsran diff --git a/apps/units/cu_up/cu_up_application_unit.h b/apps/units/cu_up/cu_up_application_unit.h new file mode 100644 index 0000000000..5d4688ac44 --- /dev/null +++ b/apps/units/cu_up/cu_up_application_unit.h @@ -0,0 +1,56 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN 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. + * + * srsRAN 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/. + * + */ + +#pragma once + +#include "apps/units/application_unit.h" +#include "apps/units/cu_up/cu_up_builder.h" +#include + +namespace srsran { +struct worker_manager_config; + +/// CU-UP application unit interface. +class cu_up_application_unit : public application_unit +{ +public: + virtual ~cu_up_application_unit() = default; + + /// Creates a CU-UP using the given dependencies. + virtual std::unique_ptr + create_cu_up_unit(const cu_up_unit_dependencies& dependencies) = 0; + + /// Returns the CU-UP unit configuration of this CU-UP application unit. + virtual cu_up_unit_config& get_cu_up_unit_config() = 0; + virtual const cu_up_unit_config& get_cu_up_unit_config() const = 0; + + /// Dumps the CU-UP configuration into the given YAML node. + virtual void dump_config(YAML::Node& node) const = 0; + + /// Fills the given worker manager configuration with the CU-UP parameters. + virtual void fill_worker_manager_config(worker_manager_config& config) = 0; +}; + +/// Creates a CU-UP application unit. +std::unique_ptr create_cu_up_application_unit(std::string_view app_name); + +} // namespace srsran diff --git a/apps/units/cu_up/cu_up_application_unit_impl.cpp b/apps/units/cu_up/cu_up_application_unit_impl.cpp index b5f8831568..daa3b09d51 100644 --- a/apps/units/cu_up/cu_up_application_unit_impl.cpp +++ b/apps/units/cu_up/cu_up_application_unit_impl.cpp @@ -21,12 +21,22 @@ */ #include "cu_up_application_unit_impl.h" +#include "cu_up_builder.h" #include "cu_up_logger_registrator.h" #include "cu_up_unit_config_cli11_schema.h" +#include "cu_up_unit_config_translators.h" #include "cu_up_unit_config_validator.h" +#include "cu_up_unit_config_yaml_writer.h" using namespace srsran; +cu_up_application_unit_impl::cu_up_application_unit_impl(std::string_view app_name) +{ + unit_cfg.pcap_cfg.n3.filename = fmt::format("/tmp/{}_n3.pcap", app_name); + unit_cfg.pcap_cfg.f1u.filename = fmt::format("/tmp/{}_f1u.pcap", app_name); + unit_cfg.pcap_cfg.e1ap.filename = fmt::format("/tmp/{}_e1ap.pcap", app_name); +} + void cu_up_application_unit_impl::on_parsing_configuration_registration(CLI::App& app) { configure_cli11_with_cu_up_unit_config_schema(app, unit_cfg); @@ -41,3 +51,24 @@ void cu_up_application_unit_impl::on_loggers_registration() { register_cu_up_loggers(unit_cfg.loggers); } +std::unique_ptr + +cu_up_application_unit_impl::create_cu_up_unit(const cu_up_unit_dependencies& dependencies) +{ + return build_cu_up(unit_cfg, dependencies); +} + +void cu_up_application_unit_impl::dump_config(YAML::Node& node) const +{ + fill_cu_up_config_in_yaml_schema(node, unit_cfg); +} + +void cu_up_application_unit_impl::fill_worker_manager_config(worker_manager_config& config) +{ + fill_cu_up_worker_manager_config(config, unit_cfg); +} + +std::unique_ptr srsran::create_cu_up_application_unit(std::string_view app_name) +{ + return std::make_unique(app_name); +} diff --git a/apps/units/cu_up/cu_up_application_unit_impl.h b/apps/units/cu_up/cu_up_application_unit_impl.h index 885a0886af..d2d57c3187 100644 --- a/apps/units/cu_up/cu_up_application_unit_impl.h +++ b/apps/units/cu_up/cu_up_application_unit_impl.h @@ -22,24 +22,42 @@ #pragma once -#include "../application_unit.h" +#include "apps/units/cu_up/cu_up_application_unit.h" #include "cu_up_unit_config.h" namespace srsran { /// CU-UP application unit implementation. -class cu_up_application_unit_impl : public application_unit +class cu_up_application_unit_impl : public cu_up_application_unit { public: + explicit cu_up_application_unit_impl(std::string_view app_name); + // See interface for documentation. void on_parsing_configuration_registration(CLI::App& app) override; + // See interface for documentation. + void on_configuration_parameters_autoderivation(CLI::App& app) override {} + // See interface for documentation. bool on_configuration_validation(const os_sched_affinity_bitmask& available_cpus) const override; // See interface for documentation. void on_loggers_registration() override; + // See interface for documentation. + std::unique_ptr create_cu_up_unit(const cu_up_unit_dependencies& dependencies) override; + + // See interface for documentation. + cu_up_unit_config& get_cu_up_unit_config() override { return unit_cfg; } + const cu_up_unit_config& get_cu_up_unit_config() const override { return unit_cfg; } + + // See interface for documentation. + void dump_config(YAML::Node& node) const override; + + // See interface for documentation. + void fill_worker_manager_config(worker_manager_config& config) override; + private: cu_up_unit_config unit_cfg; }; diff --git a/apps/units/cu_up/cu_up_builder.cpp b/apps/units/cu_up/cu_up_builder.cpp index 3102b01ac1..9c36555eb4 100644 --- a/apps/units/cu_up/cu_up_builder.cpp +++ b/apps/units/cu_up/cu_up_builder.cpp @@ -29,25 +29,24 @@ using namespace srsran; -std::unique_ptr srsran::build_cu_up(const cu_up_unit_config& unit_cfg, - worker_manager& workers, - srs_cu_up::e1_connection_client& e1_conn_client, - f1u_cu_up_gateway& f1u_gateway, - dlt_pcap& gtpu_pcap, - timer_manager& timers, - io_broker& io_brk) +std::unique_ptr srsran::build_cu_up(const cu_up_unit_config& unit_cfg, + const cu_up_unit_dependencies& dependencies) { srs_cu_up::cu_up_configuration config = generate_cu_up_config(unit_cfg); - config.ctrl_executor = workers.cu_up_ctrl_exec; - config.cu_up_e2_exec = workers.cu_up_e2_exec; - config.ue_exec_pool = workers.cu_up_exec_mapper.get(); - config.io_ul_executor = workers.cu_up_io_ul_exec; // Optionally select separate exec for UL IO - config.e1ap.e1_conn_client = &e1_conn_client; - config.f1u_gateway = &f1u_gateway; - config.gtpu_pcap = >pu_pcap; - config.timers = &timers; - config.qos = generate_cu_up_qos_config(unit_cfg); + config.ctrl_executor = dependencies.workers->cu_up_ctrl_exec; + config.cu_up_e2_exec = dependencies.workers->cu_up_e2_exec; + config.ue_exec_pool = dependencies.workers->cu_up_exec_mapper.get(); + config.io_ul_executor = dependencies.workers->cu_up_io_ul_exec; // Optionally select separate exec for UL IO + config.e1ap.e1_conn_client = dependencies.e1ap_conn_client; + config.f1u_gateway = dependencies.f1u_gateway; + config.gtpu_pcap = dependencies.gtpu_pcap; + config.timers = dependencies.timers; + config.qos = generate_cu_up_qos_config(unit_cfg); + auto address = config.f1u_gateway->get_cu_bind_address(); + srsran_assert(address.has_value(), "Invalid F1-U bind address"); + + config.net_cfg.f1u_bind_addr = address.value(); // Create NG-U gateway. std::unique_ptr ngu_gw; if (not unit_cfg.upf_cfg.no_core) { @@ -56,7 +55,8 @@ std::unique_ptr srsran::build_cu_up(const cu_up_unit ngu_gw_config.bind_port = config.net_cfg.n3_bind_port; ngu_gw_config.bind_interface = config.net_cfg.n3_bind_interface; ngu_gw_config.rx_max_mmsg = config.net_cfg.n3_rx_max_mmsg; - ngu_gw = srs_cu_up::create_udp_ngu_gateway(ngu_gw_config, io_brk, *workers.cu_up_io_ul_exec); + ngu_gw = + srs_cu_up::create_udp_ngu_gateway(ngu_gw_config, *dependencies.io_brk, *dependencies.workers->cu_up_io_ul_exec); } else { ngu_gw = srs_cu_up::create_no_core_ngu_gateway(); } diff --git a/apps/units/cu_up/cu_up_builder.h b/apps/units/cu_up/cu_up_builder.h index 40c8f1d097..9e9b7028d1 100644 --- a/apps/units/cu_up/cu_up_builder.h +++ b/apps/units/cu_up/cu_up_builder.h @@ -38,13 +38,18 @@ class f1u_cu_up_gateway; class io_broker; struct worker_manager; +/// CU-UP unit dependencies. +struct cu_up_unit_dependencies { + worker_manager* workers; + srs_cu_up::e1_connection_client* e1ap_conn_client; + f1u_cu_up_gateway* f1u_gateway; + dlt_pcap* gtpu_pcap; + timer_manager* timers; + io_broker* io_brk; +}; + /// Builds the CU UP using the given arguments. -std::unique_ptr build_cu_up(const cu_up_unit_config& unit_cfg, - worker_manager& workers, - srs_cu_up::e1_connection_client& e1ap_conn_client, - f1u_cu_up_gateway& f1u_gateway, - dlt_pcap& gtpu_pcap, - timer_manager& timers, - io_broker& io_brk); +std::unique_ptr build_cu_up(const cu_up_unit_config& unit_cfg, + const cu_up_unit_dependencies& dependencies); } // namespace srsran diff --git a/apps/units/cu_up/cu_up_unit_config_cli11_schema.cpp b/apps/units/cu_up/cu_up_unit_config_cli11_schema.cpp index 74d81c82f3..bf195dea5b 100644 --- a/apps/units/cu_up/cu_up_unit_config_cli11_schema.cpp +++ b/apps/units/cu_up/cu_up_unit_config_cli11_schema.cpp @@ -198,14 +198,3 @@ void srsran::configure_cli11_with_cu_up_unit_config_schema(CLI::App& app, cu_up_ CLI::App* test_mode_subcmd = add_subcommand(app, "test_mode", "CU-UP test mode parameters")->configurable(); configure_cli11_test_mode_args(*test_mode_subcmd, unit_cfg.test_mode_cfg); } - -void srsran::autoderive_cu_up_parameters_after_parsing(const std::string& n2_bind_addr, - bool no_core, - cu_up_unit_config& unit_cfg) -{ - // If no UPF is configured, we autoderive the UPF configuration from the CU-CP AMF configuration. - if (unit_cfg.upf_cfg.bind_addr == "auto") { - unit_cfg.upf_cfg.bind_addr = n2_bind_addr; - } - unit_cfg.upf_cfg.no_core = no_core; -} diff --git a/apps/units/cu_up/cu_up_unit_config_cli11_schema.h b/apps/units/cu_up/cu_up_unit_config_cli11_schema.h index 4df94e1faf..dfca1f8e35 100644 --- a/apps/units/cu_up/cu_up_unit_config_cli11_schema.h +++ b/apps/units/cu_up/cu_up_unit_config_cli11_schema.h @@ -31,7 +31,4 @@ struct cu_up_unit_config; /// Configures the given CLI11 application with the CU-UP application unit configuration schema. void configure_cli11_with_cu_up_unit_config_schema(CLI::App& app, cu_up_unit_config& unit_cfg); -/// Auto derive CU-UP parameters after the parsing. -void autoderive_cu_up_parameters_after_parsing(const std::string& bind_addr, bool no_core, cu_up_unit_config& unit_cfg); - } // namespace srsran diff --git a/apps/units/cu_up/cu_up_unit_pcap_config.h b/apps/units/cu_up/cu_up_unit_pcap_config.h index 77d0f399bf..e229a05ff9 100644 --- a/apps/units/cu_up/cu_up_unit_pcap_config.h +++ b/apps/units/cu_up/cu_up_unit_pcap_config.h @@ -22,7 +22,6 @@ #pragma once -#include "fmt/core.h" #include namespace srsran { @@ -42,15 +41,6 @@ struct cu_up_unit_pcap_config { bool enabled = false; } e1ap; - /// helper method to set the filename prefix for different apps. - /// This is used to provide different defaults depending on the app, - /// e.g.: "/tmp/gnb_e1ap.pcap" or "/tmp/cu_e1ap.pcap" - void set_default_filename(std::string prefix) - { - n3.filename = fmt::format("{}_n3.pcap", prefix); - f1u.filename = fmt::format("{}_f1u.pcap", prefix); - e1ap.filename = fmt::format("{}_e1ap.pcap", prefix); - } /// When using the gNB app, there is no point in instantiating /// E1 pcaps twice. This function force disables them in the CU-UP. /// TODO: revisit diff --git a/apps/units/flexible_du/du_high/du_high_config.h b/apps/units/flexible_du/du_high/du_high_config.h index 25560b87e6..d7ed9cbbb2 100644 --- a/apps/units/flexible_du/du_high/du_high_config.h +++ b/apps/units/flexible_du/du_high/du_high_config.h @@ -684,17 +684,7 @@ struct du_high_unit_pcap_config { std::string type = "udp"; bool enabled = false; } mac; - /// helper method to set the filename prefix for different apps. - /// This is used to provide different defaults depending on the app, - /// e.g.: "/tmp/gnb_f1ap.pcap", "/tmp/cu_f1ap.pcap" or "/tmp/du_f1ap.pcap" - void set_default_filename(std::string prefix) - { - e2ap.filename = fmt::format("{}_e2ap.pcap", prefix); - f1ap.filename = fmt::format("{}_f1ap.pcap", prefix); - f1u.filename = fmt::format("{}_f1u.pcap", prefix); - rlc.filename = fmt::format("{}_rlc.pcap", prefix); - mac.filename = fmt::format("{}_mac.pcap", prefix); - } + /// When using the gNB app, there is no point in instantiating /// F1 pcaps twice. This function force disables them. /// TODO: revisit diff --git a/apps/units/flexible_du/du_high/du_high_config_translators.cpp b/apps/units/flexible_du/du_high/du_high_config_translators.cpp index e89d6ab498..bc66f224c4 100644 --- a/apps/units/flexible_du/du_high/du_high_config_translators.cpp +++ b/apps/units/flexible_du/du_high/du_high_config_translators.cpp @@ -960,7 +960,7 @@ void srsran::fill_du_high_worker_manager_config(worker_manager_config& confi const du_high_unit_config& unit_cfg, bool is_blocking_mode_enabled) { - auto& du_hi_cfg = config.du_hi_cfg; + auto& du_hi_cfg = config.du_hi_cfg.emplace(); du_hi_cfg.is_rt_mode_enabled = !is_blocking_mode_enabled; du_hi_cfg.nof_cells = unit_cfg.cells_cfg.size(); diff --git a/apps/units/flexible_du/du_high/du_high_config_validator.cpp b/apps/units/flexible_du/du_high/du_high_config_validator.cpp index 150b12a828..531952f411 100644 --- a/apps/units/flexible_du/du_high/du_high_config_validator.cpp +++ b/apps/units/flexible_du/du_high/du_high_config_validator.cpp @@ -207,10 +207,9 @@ static bool validate_srb_unit_config(const std::map ret = band_helper::is_dl_arfcn_valid_given_band( *config.band, config.dl_f_ref_arfcn, config.common_scs, config.channel_bw_mhz); @@ -663,6 +661,14 @@ static bool validate_dl_arfcn_and_band(const du_high_unit_base_cell_config& conf fmt::print("Invalid DL ARFCN={} for band {}. Cause: {}.\n", config.dl_f_ref_arfcn, band, ret.error()); return false; } + // Check if also the corresponding UL ARFCN is valid. + const uint32_t ul_arfcn = band_helper::get_ul_arfcn_from_dl_arfcn(config.dl_f_ref_arfcn, config.band.value()); + ret = band_helper::is_ul_arfcn_valid_given_band(*config.band, ul_arfcn, config.channel_bw_mhz); + if (not ret.has_value()) { + // NOTE: The message must say that it's the DL ARFCN that is invalid, as that is the parameters set by the user. + fmt::print("Invalid DL ARFCN={} for band {}. Cause: {}.\n", config.dl_f_ref_arfcn, band, ret.error()); + return false; + } } else { if (band == nr_band::invalid) { fmt::print("Invalid DL ARFCN={}. Cause: Could not find a valid band.\n", config.dl_f_ref_arfcn); @@ -769,20 +775,19 @@ static bool validate_base_cell_unit_config(const du_high_unit_base_cell_config& fmt::print("Maximum Channel BW with SCS common 120kHz is 400MHz.\n"); return false; } - if (!validate_dl_arfcn_and_band(config)) { + if (!validate_dl_ul_arfcn_and_band(config)) { return false; } - const auto ssb_scs = band_helper::get_most_suitable_ssb_scs( - band_helper::get_band_from_dl_arfcn(config.dl_f_ref_arfcn), config.common_scs); + const nr_band band = config.band.value_or(band_helper::get_band_from_dl_arfcn(config.dl_f_ref_arfcn)); + const auto ssb_scs = band_helper::get_most_suitable_ssb_scs(band, config.common_scs); if (ssb_scs != config.common_scs) { fmt::print("Common SCS {}kHz is not equal to SSB SCS {}kHz. Different SCS for common and SSB is not supported.\n", scs_to_khz(config.common_scs), scs_to_khz(ssb_scs)); return false; } - const nr_band band = - config.band.has_value() ? config.band.value() : band_helper::get_band_from_dl_arfcn(config.dl_f_ref_arfcn); + const unsigned nof_crbs = band_helper::get_n_rbs_from_bw(config.channel_bw_mhz, config.common_scs, band_helper::get_freq_range(band)); @@ -845,8 +850,9 @@ static bool validate_cells_unit_config(span conf fmt::print("Invalid Sector ID {}, for a gNB Id of {} bits\n", cell.cell.sector_id.value(), gnb_id.bit_length); return false; } - const auto band = cell.cell.band.value_or(band_helper::get_band_from_dl_arfcn(cell.cell.dl_f_ref_arfcn)); - bool is_unlicensed = band_helper::is_unlicensed_band(band); + const auto band = cell.cell.band.value_or(band_helper::get_band_from_dl_arfcn(cell.cell.dl_f_ref_arfcn)); + bool is_unlicensed = band_helper::is_unlicensed_band(band); + // Check if the RA Response Window (in ms) is within the limits for licensed and unlicensed bands. unsigned int max_ra_resp_window = is_unlicensed ? 40 : 10; unsigned int ra_resp_window_ms = cell.cell.prach_cfg.ra_resp_window.value() >> to_numerology_value(cell.cell.common_scs); diff --git a/apps/units/flexible_du/du_low/CMakeLists.txt b/apps/units/flexible_du/du_low/CMakeLists.txt index b370236398..581277584f 100644 --- a/apps/units/flexible_du/du_low/CMakeLists.txt +++ b/apps/units/flexible_du/du_low/CMakeLists.txt @@ -36,4 +36,5 @@ if (DPDK_FOUND) hal_hwacc_pdsch hal_bbdev_factory) endif (DPDK_FOUND) + target_link_libraries(srsran_du_low_unit_helpers PUBLIC ${DU_LOW_UNIT_HELPERS_LIBRARIES}) diff --git a/apps/units/flexible_du/du_low/du_low_config_cli11_schema.cpp b/apps/units/flexible_du/du_low/du_low_config_cli11_schema.cpp index a7a81ab7b4..d93ff95ece 100644 --- a/apps/units/flexible_du/du_low/du_low_config_cli11_schema.cpp +++ b/apps/units/flexible_du/du_low/du_low_config_cli11_schema.cpp @@ -254,7 +254,7 @@ static void configure_cli11_bbdev_hwacc_args(CLI::App& app, std::optionalhwacc_type, "Type of BBDEV implementation")->capture_default_str(); + app.add_option("--bbdev_acc_type", config->bbdev_acc_type, "Type of BBDEV implementation")->capture_default_str(); app.add_option("--hwacc_type", config->hwacc_type, "Type of BBDEV hardware-accelerator")->capture_default_str(); app.add_option("--id", config->id, "ID of the BBDEV-based hardware-accelerator.") ->capture_default_str() diff --git a/apps/units/flexible_du/du_low/du_low_config_translator.cpp b/apps/units/flexible_du/du_low/du_low_config_translator.cpp index e132a239a5..7a38dddca8 100644 --- a/apps/units/flexible_du/du_low/du_low_config_translator.cpp +++ b/apps/units/flexible_du/du_low/du_low_config_translator.cpp @@ -32,7 +32,6 @@ using namespace srsran; static void generate_du_low_config(srs_du::du_low_config& out_config, const du_low_unit_config& du_low, - const hal_upper_phy_config& hal_config, span du_cells, span max_puschs_per_slot, unsigned du_id) @@ -43,9 +42,6 @@ static void generate_du_low_config(srs_du::du_low_config& out_config const srs_du::du_cell_config& cell = du_cells[i]; upper_phy_config& upper_phy_cell = out_config.cells.emplace_back().upper_phy_cfg; - // Initialize the HAL config of the upper PHY. - upper_phy_cell.hal_config = hal_config; - // Get band, frequency range and duplex mode from the band. nr_band band = cell.dl_carrier.band; const frequency_range freq_range = band_helper::get_freq_range(band); @@ -172,16 +168,13 @@ static void generate_du_low_config(srs_du::du_low_config& out_config } } -void srsran::generate_du_low_wrapper_config(srs_du::du_low_wrapper_config& out_config, - const du_low_unit_config& du_low_unit_cfg, - const hal_upper_phy_config& hal_config, - std::vector prach_ports, - span du_cells, - span max_puschs_per_slot, - unsigned du_id) +void srsran::generate_du_low_wrapper_config(srs_du::du_low_wrapper_config& out_config, + const du_low_unit_config& du_low_unit_cfg, + span du_cells, + span max_puschs_per_slot, + unsigned du_id) { - generate_du_low_config(out_config.du_low_cfg, du_low_unit_cfg, hal_config, du_cells, max_puschs_per_slot, du_id); - out_config.prach_ports = std::move(prach_ports); + generate_du_low_config(out_config.du_low_cfg, du_low_unit_cfg, du_cells, max_puschs_per_slot, du_id); } void srsran::fill_du_low_worker_manager_config(worker_manager_config& config, diff --git a/apps/units/flexible_du/du_low/du_low_config_translator.h b/apps/units/flexible_du/du_low/du_low_config_translator.h index 35e821a93b..60da5a1721 100644 --- a/apps/units/flexible_du/du_low/du_low_config_translator.h +++ b/apps/units/flexible_du/du_low/du_low_config_translator.h @@ -35,13 +35,11 @@ struct du_low_config; struct du_low_unit_config; struct worker_manager_config; -void generate_du_low_wrapper_config(srs_du::du_low_wrapper_config& out_config, - const du_low_unit_config& du_low_unit_cfg, - const hal_upper_phy_config& hal_config, - std::vector prach_ports, - span du_cells, - span max_puschs_per_slot, - unsigned du_id); +void generate_du_low_wrapper_config(srs_du::du_low_wrapper_config& out_config, + const du_low_unit_config& du_low_unit_cfg, + span du_cells, + span max_puschs_per_slot, + unsigned du_id); /// Fills the DU low worker manager parameters of the given worker manager configuration. void fill_du_low_worker_manager_config(worker_manager_config& config, diff --git a/apps/units/flexible_du/du_low/du_low_wrapper_config_helper.cpp b/apps/units/flexible_du/du_low/du_low_wrapper_config_helper.cpp index 686ee85b60..6867847d04 100644 --- a/apps/units/flexible_du/du_low/du_low_wrapper_config_helper.cpp +++ b/apps/units/flexible_du/du_low/du_low_wrapper_config_helper.cpp @@ -26,6 +26,13 @@ #include "du_low_config_translator.h" #include "srsran/du/du_low/du_low_wrapper_factory.h" #include "srsran/ran/slot_pdu_capacity_constants.h" +#ifdef DPDK_FOUND +#include "srsran/hal/dpdk/bbdev/bbdev_acc.h" +#include "srsran/hal/dpdk/bbdev/bbdev_acc_factory.h" +#include "srsran/hal/phy/upper/channel_processors/hw_accelerator_factories.h" +#include "srsran/hal/phy/upper/channel_processors/pusch/ext_harq_buffer_context_repository_factory.h" +#include "srsran/hal/phy/upper/channel_processors/pusch/hw_accelerator_factories.h" +#endif // DPDK_FOUND using namespace srsran; @@ -67,7 +74,6 @@ static void generate_dl_processor_config(downlink_processor_factory_sw_config& o void srsran::make_du_low_wrapper_config_and_dependencies( srs_du::du_low_wrapper_config& out_cfg, const du_low_unit_config& du_low_unit_cfg, - const hal_upper_phy_config& hal_config, std::vector prach_ports, span du_cells, span max_puschs_per_slot, @@ -78,8 +84,90 @@ void srsran::make_du_low_wrapper_config_and_dependencies( { out_cfg.du_low_cfg.logger = &srslog::fetch_basic_logger("DU"); - generate_du_low_wrapper_config( - out_cfg, du_low_unit_cfg, hal_config, std::move(prach_ports), du_cells, max_puschs_per_slot, du_id); + // Initialize hardware-accelerator (only if needed). + hal_upper_phy_config hal_config = {}; + hal_config.hwacc_pdsch_processor = false; + hal_config.hwacc_pusch_processor = false; +#ifdef DPDK_FOUND + if (du_low_unit_cfg.hal_config && du_low_unit_cfg.hal_config->bbdev_hwacc && + !du_low_unit_cfg.hal_config->bbdev_hwacc->hwacc_type.empty()) { + const bbdev_appconfig& bbdev_app_cfg = du_low_unit_cfg.hal_config->bbdev_hwacc.value(); + srslog::basic_logger& hwacc_logger = srslog::fetch_basic_logger("HWACC", false); + hwacc_logger.set_level(du_low_unit_cfg.loggers.hal_level); + + hal::bbdev_hwacc_pdsch_enc_factory_configuration hwacc_pdsch_enc_cfg = {}; + hal::bbdev_hwacc_pusch_dec_factory_configuration hwacc_pusch_dec_cfg = {}; + std::shared_ptr harq_buffer_context = nullptr; + unsigned nof_hwacc_dus = du_cells.size(); + + // Create a bbdev accelerator factory. + std::unique_ptr bbdev_acc_factory = + dpdk::create_bbdev_acc_factory(bbdev_app_cfg.bbdev_acc_type); + report_error_if_not(bbdev_acc_factory, + "Unable to create the {} bbdev hardware-accelerator interface factory.", + bbdev_app_cfg.bbdev_acc_type); + + // Intefacing to the bbdev-based hardware-accelerator. + dpdk::bbdev_acc_configuration bbdev_config; + bbdev_config.id = bbdev_app_cfg.id; + if (bbdev_app_cfg.pdsch_enc && bbdev_app_cfg.pdsch_enc->nof_hwacc > 0) { + bbdev_config.nof_ldpc_enc_lcores = nof_hwacc_dus * bbdev_app_cfg.pdsch_enc->nof_hwacc; + } + if (bbdev_app_cfg.pusch_dec && bbdev_app_cfg.pusch_dec->nof_hwacc > 0) { + bbdev_config.nof_ldpc_dec_lcores = nof_hwacc_dus * bbdev_app_cfg.pusch_dec->nof_hwacc; + } + // If no msg_mbuf size is defined, a worst-case value will be used. + bbdev_config.msg_mbuf_size = bbdev_app_cfg.msg_mbuf_size.value_or(RTE_BBDEV_LDPC_E_MAX_MBUF); + // If no rm_mbuf size is defined, a worst-case value will be used. + bbdev_config.rm_mbuf_size = bbdev_app_cfg.rm_mbuf_size.value_or(RTE_BBDEV_LDPC_E_MAX_MBUF); + // If no number of mbufs is defined, a worst-case value will be used. + bbdev_config.nof_mbuf = bbdev_app_cfg.nof_mbuf.value_or(static_cast(pow2(log2_ceil(MAX_NOF_SEGMENTS)))); + std::shared_ptr bbdev_accelerator = bbdev_acc_factory->create(bbdev_config, hwacc_logger); + report_error_if_not(bbdev_accelerator, "Unable to open the {} hardware-accelerator.", bbdev_app_cfg.hwacc_type); + + // Configure the hardware-accelerated PDSCH encoding factory (only if needed). + if (bbdev_app_cfg.pdsch_enc && bbdev_app_cfg.pdsch_enc->nof_hwacc > 0) { + hwacc_pdsch_enc_cfg.acc_type = bbdev_app_cfg.hwacc_type; + hwacc_pdsch_enc_cfg.bbdev_accelerator = bbdev_accelerator; + hwacc_pdsch_enc_cfg.cb_mode = bbdev_app_cfg.pdsch_enc->cb_mode; + // If no maximum buffer size is defined, a worst-case value will be used. + hwacc_pdsch_enc_cfg.max_tb_size = bbdev_app_cfg.pdsch_enc->max_buffer_size.value_or(RTE_BBDEV_LDPC_E_MAX_MBUF); + hwacc_pdsch_enc_cfg.dedicated_queue = bbdev_app_cfg.pdsch_enc->dedicated_queue; + hal_config.hwacc_pdsch_processor = true; + hal_config.hwacc_pdsch_enc_cfg = hwacc_pdsch_enc_cfg; + } + + // Configure the hardware-accelerated PUSCH decoding factory (only if needed). + if (bbdev_app_cfg.pusch_dec && bbdev_app_cfg.pusch_dec->nof_hwacc > 0) { + hwacc_pusch_dec_cfg.acc_type = bbdev_app_cfg.hwacc_type; + hwacc_pusch_dec_cfg.bbdev_accelerator = bbdev_accelerator; + hwacc_pusch_dec_cfg.ext_softbuffer = bbdev_app_cfg.pusch_dec->ext_softbuffer; + if (hwacc_pusch_dec_cfg.ext_softbuffer) { + // Set up an external HARQ buffer context repository. + unsigned nof_cbs = bbdev_app_cfg.pusch_dec->harq_context_size.value_or(MAX_NOF_SEGMENTS); + uint64_t ext_harq_buff_size = bbdev_accelerator->get_harq_buff_size_bytes(); + harq_buffer_context = hal::create_ext_harq_buffer_context_repository(nof_cbs, ext_harq_buff_size, false); + report_error_if_not(harq_buffer_context, + "Unable to create the external HARQ buffer context for the {} hardware-accelerator.", + bbdev_app_cfg.hwacc_type); + hwacc_pusch_dec_cfg.harq_buffer_context = harq_buffer_context; + } + hwacc_pusch_dec_cfg.dedicated_queue = bbdev_app_cfg.pusch_dec->dedicated_queue; + hal_config.hwacc_pusch_processor = true; + hal_config.hwacc_pusch_dec_cfg = hwacc_pusch_dec_cfg; + } + } +#endif // DPDK_FOUND + + generate_du_low_wrapper_config(out_cfg, du_low_unit_cfg, du_cells, max_puschs_per_slot, du_id); + + // Fill the hal config. + for (auto& cell : out_cfg.du_low_cfg.cells) { + cell.upper_phy_cfg.hal_config = hal_config; + } + + // Fill the PRACH ports. + out_cfg.prach_ports = std::move(prach_ports); // Fill the workers information. for (unsigned i = 0, e = out_cfg.du_low_cfg.cells.size(); i != e; ++i) { diff --git a/apps/units/flexible_du/du_low/du_low_wrapper_config_helper.h b/apps/units/flexible_du/du_low/du_low_wrapper_config_helper.h index d2e116a317..1410ecbfcc 100644 --- a/apps/units/flexible_du/du_low/du_low_wrapper_config_helper.h +++ b/apps/units/flexible_du/du_low/du_low_wrapper_config_helper.h @@ -36,7 +36,6 @@ struct worker_manager; void make_du_low_wrapper_config_and_dependencies(srs_du::du_low_wrapper_config& out_cfg, const du_low_unit_config& du_low_unit_cfg, - const hal_upper_phy_config& hal_config, std::vector prach_ports, span du_cells, span max_puschs_per_slot, diff --git a/apps/units/flexible_du/flexible_du_application_unit.h b/apps/units/flexible_du/flexible_du_application_unit.h index e4a6d31c59..8cab7440ef 100644 --- a/apps/units/flexible_du/flexible_du_application_unit.h +++ b/apps/units/flexible_du/flexible_du_application_unit.h @@ -55,6 +55,6 @@ class flexible_du_application_unit : public application_unit /// Different splits must provide an implementation of this free function so the applications can instantiate a flexible /// DU application unit. Only one of these implementations should be compiled, based on the DU_SPLIT_TYPE cmake /// definition. -std::unique_ptr create_flexible_du_application_unit(); +std::unique_ptr create_flexible_du_application_unit(std::string_view app_name); } // namespace srsran diff --git a/apps/units/flexible_du/split_6/split6_du_application_unit_impl.cpp b/apps/units/flexible_du/split_6/split6_du_application_unit_impl.cpp index 7a2854e294..1feef5faf7 100644 --- a/apps/units/flexible_du/split_6/split6_du_application_unit_impl.cpp +++ b/apps/units/flexible_du/split_6/split6_du_application_unit_impl.cpp @@ -32,6 +32,15 @@ using namespace srsran; +split6_du_application_unit_impl::split6_du_application_unit_impl(std::string_view app_name) +{ + unit_cfg.du_high_cfg.config.pcaps.e2ap.filename = fmt::format("/tmp/{}_e2ap.pcap", app_name); + unit_cfg.du_high_cfg.config.pcaps.f1ap.filename = fmt::format("/tmp/{}_f1ap.pcap", app_name); + unit_cfg.du_high_cfg.config.pcaps.f1u.filename = fmt::format("/tmp/{}_f1u.pcap", app_name); + unit_cfg.du_high_cfg.config.pcaps.rlc.filename = fmt::format("/tmp/{}_rlc.pcap", app_name); + unit_cfg.du_high_cfg.config.pcaps.mac.filename = fmt::format("/tmp/{}_mac.pcap", app_name); +} + void split6_du_application_unit_impl::on_loggers_registration() { register_split6_du_loggers(unit_cfg); diff --git a/apps/units/flexible_du/split_6/split6_du_application_unit_impl.h b/apps/units/flexible_du/split_6/split6_du_application_unit_impl.h index 253bd78f84..cef28e8f66 100644 --- a/apps/units/flexible_du/split_6/split6_du_application_unit_impl.h +++ b/apps/units/flexible_du/split_6/split6_du_application_unit_impl.h @@ -32,6 +32,8 @@ namespace srsran { class split6_du_application_unit_impl : public flexible_du_application_unit { public: + explicit split6_du_application_unit_impl(std::string_view app_name); + // See interface for documentation. void on_parsing_configuration_registration(CLI::App& app) override; diff --git a/apps/units/flexible_du/split_7_2/CMakeLists.txt b/apps/units/flexible_du/split_7_2/CMakeLists.txt index 279fd30d67..257327be29 100644 --- a/apps/units/flexible_du/split_7_2/CMakeLists.txt +++ b/apps/units/flexible_du/split_7_2/CMakeLists.txt @@ -33,7 +33,13 @@ if (DU_SPLIT_7_2) add_library(srsran_flexible_du STATIC ${SOURCES}) target_include_directories(srsran_flexible_du PRIVATE ${CMAKE_SOURCE_DIR}) - set(FLEXIBLE_DU_LIBRARIES + + # Hardware acceleration for both PUSCH and PDSCH is enabled by default when using DPDK. + if (DPDK_FOUND) + set_source_files_properties(split_7_2_du_factory.cpp PROPERTIES COMPILE_DEFINITIONS "DPDK_FOUND; HWACC_PDSCH_ENABLED; HWACC_PUSCH_ENABLED") + endif (DPDK_FOUND) + + target_link_libraries(srsran_flexible_du srsran_du_wrapper srsran_pcap srsran_app_services @@ -42,13 +48,4 @@ if (DU_SPLIT_7_2) srsran_split_7_2_app_unit_helpers srsran_du_high_unit_helpers) - # Hardware acceleration for both PUSCH and PDSCH is enabled by default when using DPDK. - if (DPDK_FOUND) - set_source_files_properties(split_7_2_du_factory.cpp PROPERTIES COMPILE_DEFINITIONS "DPDK_FOUND; HWACC_PDSCH_ENABLED; HWACC_PUSCH_ENABLED") - list(APPEND FLEXIBLE_DU_LIBRARIES hal_hwacc_pusch - hal_hwacc_pdsch - hal_bbdev_factory) - endif (DPDK_FOUND) - target_link_libraries(srsran_flexible_du ${FLEXIBLE_DU_LIBRARIES}) - endif () diff --git a/apps/units/flexible_du/split_7_2/split_7_2_du_application_unit_impl.cpp b/apps/units/flexible_du/split_7_2/split_7_2_du_application_unit_impl.cpp index 3827d5fa08..588f37f012 100644 --- a/apps/units/flexible_du/split_7_2/split_7_2_du_application_unit_impl.cpp +++ b/apps/units/flexible_du/split_7_2/split_7_2_du_application_unit_impl.cpp @@ -49,6 +49,15 @@ bool split_7_2_du_application_unit_impl::on_configuration_validation( return validate_split_7_2_du_unit_config(unit_cfg, available_cpus); } +split_7_2_du_application_unit_impl::split_7_2_du_application_unit_impl(std::string_view app_name) +{ + unit_cfg.du_high_cfg.config.pcaps.e2ap.filename = fmt::format("/tmp/{}_e2ap.pcap", app_name); + unit_cfg.du_high_cfg.config.pcaps.f1ap.filename = fmt::format("/tmp/{}_f1ap.pcap", app_name); + unit_cfg.du_high_cfg.config.pcaps.f1u.filename = fmt::format("/tmp/{}_f1u.pcap", app_name); + unit_cfg.du_high_cfg.config.pcaps.rlc.filename = fmt::format("/tmp/{}_rlc.pcap", app_name); + unit_cfg.du_high_cfg.config.pcaps.mac.filename = fmt::format("/tmp/{}_mac.pcap", app_name); +} + void split_7_2_du_application_unit_impl::on_parsing_configuration_registration(CLI::App& app) { configure_cli11_with_split_7_2_du_unit_config_schema(app, unit_cfg); @@ -59,9 +68,9 @@ du_unit split_7_2_du_application_unit_impl::create_flexible_du_unit(const du_uni return create_split_7_2_du(unit_cfg, dependencies); } -std::unique_ptr srsran::create_flexible_du_application_unit() +std::unique_ptr srsran::create_flexible_du_application_unit(std::string_view app_name) { - return std::make_unique(); + return std::make_unique(app_name); } void split_7_2_du_application_unit_impl::dump_config(YAML::Node& node) const diff --git a/apps/units/flexible_du/split_7_2/split_7_2_du_application_unit_impl.h b/apps/units/flexible_du/split_7_2/split_7_2_du_application_unit_impl.h index a8449ad847..d8c8e4715c 100644 --- a/apps/units/flexible_du/split_7_2/split_7_2_du_application_unit_impl.h +++ b/apps/units/flexible_du/split_7_2/split_7_2_du_application_unit_impl.h @@ -31,6 +31,8 @@ namespace srsran { class split_7_2_du_application_unit_impl : public flexible_du_application_unit { public: + explicit split_7_2_du_application_unit_impl(std::string_view app_name); + // See interface for documentation. void on_parsing_configuration_registration(CLI::App& app) override; diff --git a/apps/units/flexible_du/split_7_2/split_7_2_du_factory.cpp b/apps/units/flexible_du/split_7_2/split_7_2_du_factory.cpp index 019811e47e..ac1ec10641 100644 --- a/apps/units/flexible_du/split_7_2/split_7_2_du_factory.cpp +++ b/apps/units/flexible_du/split_7_2/split_7_2_du_factory.cpp @@ -35,13 +35,6 @@ #include "srsran/du/du_wrapper_factory.h" #include "srsran/pcap/rlc_pcap.h" #include "srsran/ru/ru_dummy_factory.h" -#ifdef DPDK_FOUND -#include "srsran/hal/dpdk/bbdev/bbdev_acc.h" -#include "srsran/hal/dpdk/bbdev/bbdev_acc_factory.h" -#include "srsran/hal/phy/upper/channel_processors/hw_accelerator_factories.h" -#include "srsran/hal/phy/upper/channel_processors/pusch/ext_harq_buffer_context_repository_factory.h" -#include "srsran/hal/phy/upper/channel_processors/pusch/hw_accelerator_factories.h" -#endif // DPDK_FOUND using namespace srsran; @@ -120,81 +113,6 @@ du_unit srsran::create_split_7_2_du(const split_7_2_du_unit_config& du_72_cfg, c max_pusch_per_slot.push_back(high.cell.pusch_cfg.max_puschs_per_slot); } - // Initialize hardware-accelerator (only if needed). - hal_upper_phy_config hal_config = {}; - hal_config.hwacc_pdsch_processor = false; - hal_config.hwacc_pusch_processor = false; -#ifdef DPDK_FOUND - hal::bbdev_hwacc_pdsch_enc_factory_configuration hwacc_pdsch_enc_cfg = {}; - hal::bbdev_hwacc_pusch_dec_factory_configuration hwacc_pusch_dec_cfg = {}; - std::shared_ptr harq_buffer_context = nullptr; - unsigned nof_hwacc_dus = du_cells.size(); - if (!du_lo.hal_config->bbdev_hwacc->hwacc_type.empty()) { - srslog::basic_logger& hwacc_logger = srslog::fetch_basic_logger("HWACC", false); - hwacc_logger.set_level(du_lo.loggers.hal_level); - - // Create a bbdev accelerator factory. - std::unique_ptr bbdev_acc_factory = - srsran::dpdk::create_bbdev_acc_factory(du_lo.hal_config->bbdev_hwacc->bbdev_acc_type); - report_error_if_not(bbdev_acc_factory, - "Unable to create the {} bbdev hardware-accelerator interface factory.", - du_lo.hal_config->bbdev_hwacc->bbdev_acc_type); - - // Intefacing to the bbdev-based hardware-accelerator. - dpdk::bbdev_acc_configuration bbdev_config; - bbdev_config.id = du_lo.hal_config->bbdev_hwacc->id; - if (du_lo.hal_config->bbdev_hwacc->pdsch_enc->nof_hwacc > 0) { - bbdev_config.nof_ldpc_enc_lcores = nof_hwacc_dus * du_lo.hal_config->bbdev_hwacc->pdsch_enc->nof_hwacc; - } - if (du_lo.hal_config->bbdev_hwacc->pusch_dec->nof_hwacc > 0) { - bbdev_config.nof_ldpc_dec_lcores = nof_hwacc_dus * du_lo.hal_config->bbdev_hwacc->pusch_dec->nof_hwacc; - } - // If no msg_mbuf size is defined, a worst-case value will be used. - bbdev_config.msg_mbuf_size = du_lo.hal_config->bbdev_hwacc->msg_mbuf_size.value_or(RTE_BBDEV_LDPC_E_MAX_MBUF); - // If no rm_mbuf size is defined, a worst-case value will be used. - bbdev_config.rm_mbuf_size = du_lo.hal_config->bbdev_hwacc->rm_mbuf_size.value_or(RTE_BBDEV_LDPC_E_MAX_MBUF); - // If no number of mbufs is defined, a worst-case value will be used. - bbdev_config.nof_mbuf = - du_lo.hal_config->bbdev_hwacc->nof_mbuf.value_or(static_cast(pow2(log2_ceil(MAX_NOF_SEGMENTS)))); - std::shared_ptr bbdev_accelerator = bbdev_acc_factory->create(bbdev_config, hwacc_logger); - report_error_if_not( - bbdev_accelerator, "Unable to open the {} hardware-accelerator.", du_lo.hal_config->bbdev_hwacc->hwacc_type); - - // Configure the hardware-accelerated PDSCH encoding factory (only if needed). - if (du_lo.hal_config->bbdev_hwacc->pdsch_enc->nof_hwacc > 0) { - hwacc_pdsch_enc_cfg.acc_type = du_lo.hal_config->bbdev_hwacc->hwacc_type; - hwacc_pdsch_enc_cfg.bbdev_accelerator = bbdev_accelerator; - hwacc_pdsch_enc_cfg.cb_mode = du_lo.hal_config->bbdev_hwacc->pdsch_enc->cb_mode; - // If no maximum buffer size is defined, a worst-case value will be used. - hwacc_pdsch_enc_cfg.max_tb_size = - du_lo.hal_config->bbdev_hwacc->pdsch_enc->max_buffer_size.value_or(RTE_BBDEV_LDPC_E_MAX_MBUF); - hwacc_pdsch_enc_cfg.dedicated_queue = du_lo.hal_config->bbdev_hwacc->pdsch_enc->dedicated_queue; - hal_config.hwacc_pdsch_processor = true; - hal_config.hwacc_pdsch_enc_cfg = hwacc_pdsch_enc_cfg; - } - - // Configure the hardware-accelerated PUSCH decoding factory (only if needed). - if (du_lo.hal_config->bbdev_hwacc->pusch_dec->nof_hwacc > 0) { - hwacc_pusch_dec_cfg.acc_type = du_lo.hal_config->bbdev_hwacc->hwacc_type; - hwacc_pusch_dec_cfg.bbdev_accelerator = bbdev_accelerator; - hwacc_pusch_dec_cfg.ext_softbuffer = du_lo.hal_config->bbdev_hwacc->pusch_dec->ext_softbuffer; - if (hwacc_pusch_dec_cfg.ext_softbuffer) { - // Set up an external HARQ buffer context repository. - unsigned nof_cbs = du_lo.hal_config->bbdev_hwacc->pusch_dec->harq_context_size.value_or(MAX_NOF_SEGMENTS); - uint64_t ext_harq_buff_size = bbdev_accelerator->get_harq_buff_size_bytes(); - harq_buffer_context = hal::create_ext_harq_buffer_context_repository(nof_cbs, ext_harq_buff_size, false); - report_error_if_not(harq_buffer_context, - "Unable to create the external HARQ buffer context for the {} hardware-accelerator.", - du_lo.hal_config->bbdev_hwacc->hwacc_type); - hwacc_pusch_dec_cfg.harq_buffer_context = harq_buffer_context; - } - hwacc_pusch_dec_cfg.dedicated_queue = du_lo.hal_config->bbdev_hwacc->pusch_dec->dedicated_queue; - hal_config.hwacc_pusch_processor = true; - hal_config.hwacc_pusch_dec_cfg = hwacc_pusch_dec_cfg; - } - } -#endif // DPDK_FOUND - for (unsigned i = 0, e = du_cells.size(); i != e; ++i) { // Create one DU per cell. srs_du::du_wrapper_config du_cfg = {}; @@ -204,7 +122,6 @@ du_unit srsran::create_split_7_2_du(const split_7_2_du_unit_config& du_72_cfg, c make_du_low_wrapper_config_and_dependencies(du_cfg.du_low_cfg, du_lo, - hal_config, {prach_ports[i]}, span(&du_cells[i], 1), span(&max_pusch_per_slot[i], 1), diff --git a/apps/units/flexible_du/split_8/CMakeLists.txt b/apps/units/flexible_du/split_8/CMakeLists.txt index f20e8ebe1c..0d9aed565d 100644 --- a/apps/units/flexible_du/split_8/CMakeLists.txt +++ b/apps/units/flexible_du/split_8/CMakeLists.txt @@ -33,7 +33,13 @@ if (DU_SPLIT_8) add_library(srsran_flexible_du STATIC ${SOURCES}) target_include_directories(srsran_flexible_du PRIVATE ${CMAKE_SOURCE_DIR}) - set(FLEXIBLE_DU_LIBRARIES + + # Hardware acceleration for both PUSCH and PDSCH is enabled by default when using DPDK. + if (DPDK_FOUND) + set_source_files_properties(split_8_du_factory.cpp PROPERTIES COMPILE_DEFINITIONS "DPDK_FOUND; HWACC_PDSCH_ENABLED; HWACC_PUSCH_ENABLED") + endif (DPDK_FOUND) + + target_link_libraries(srsran_flexible_du srsran_du_wrapper srsran_pcap srsran_app_services @@ -42,13 +48,4 @@ if (DU_SPLIT_8) srsran_split_8_app_unit_helpers srsran_du_high_unit_helpers) - # Hardware acceleration for both PUSCH and PDSCH is enabled by default when using DPDK. - if (DPDK_FOUND) - set_source_files_properties(split_8_du_factory.cpp PROPERTIES COMPILE_DEFINITIONS "DPDK_FOUND; HWACC_PDSCH_ENABLED; HWACC_PUSCH_ENABLED") - list(APPEND FLEXIBLE_DU_LIBRARIES hal_hwacc_pusch - hal_hwacc_pdsch - hal_bbdev_factory) - endif (DPDK_FOUND) - target_link_libraries(srsran_flexible_du ${FLEXIBLE_DU_LIBRARIES}) - endif () diff --git a/apps/units/flexible_du/split_8/split_8_du_application_unit_impl.cpp b/apps/units/flexible_du/split_8/split_8_du_application_unit_impl.cpp index 1612b625f1..a746c081b2 100644 --- a/apps/units/flexible_du/split_8/split_8_du_application_unit_impl.cpp +++ b/apps/units/flexible_du/split_8/split_8_du_application_unit_impl.cpp @@ -49,6 +49,15 @@ bool split_8_du_application_unit_impl::on_configuration_validation( return validate_split_8_du_unit_config(unit_cfg, available_cpus); } +split_8_du_application_unit_impl::split_8_du_application_unit_impl(std::string_view app_name) +{ + unit_cfg.du_high_cfg.config.pcaps.e2ap.filename = fmt::format("/tmp/{}_e2ap.pcap", app_name); + unit_cfg.du_high_cfg.config.pcaps.f1ap.filename = fmt::format("/tmp/{}_f1ap.pcap", app_name); + unit_cfg.du_high_cfg.config.pcaps.f1u.filename = fmt::format("/tmp/{}_f1u.pcap", app_name); + unit_cfg.du_high_cfg.config.pcaps.rlc.filename = fmt::format("/tmp/{}_rlc.pcap", app_name); + unit_cfg.du_high_cfg.config.pcaps.mac.filename = fmt::format("/tmp/{}_mac.pcap", app_name); +} + void split_8_du_application_unit_impl::on_parsing_configuration_registration(CLI::App& app) { configure_cli11_with_split_8_du_unit_config_schema(app, unit_cfg); @@ -59,9 +68,9 @@ du_unit split_8_du_application_unit_impl::create_flexible_du_unit(const du_unit_ return create_split_8_du(unit_cfg, dependencies); } -std::unique_ptr srsran::create_flexible_du_application_unit() +std::unique_ptr srsran::create_flexible_du_application_unit(std::string_view app_name) { - return std::make_unique(); + return std::make_unique(app_name); } void split_8_du_application_unit_impl::dump_config(YAML::Node& node) const diff --git a/apps/units/flexible_du/split_8/split_8_du_application_unit_impl.h b/apps/units/flexible_du/split_8/split_8_du_application_unit_impl.h index a578897009..b2c4ba002b 100644 --- a/apps/units/flexible_du/split_8/split_8_du_application_unit_impl.h +++ b/apps/units/flexible_du/split_8/split_8_du_application_unit_impl.h @@ -31,6 +31,8 @@ namespace srsran { class split_8_du_application_unit_impl : public flexible_du_application_unit { public: + explicit split_8_du_application_unit_impl(std::string_view app_name); + // See interface for documentation. void on_parsing_configuration_registration(CLI::App& app) override; diff --git a/apps/units/flexible_du/split_8/split_8_du_factory.cpp b/apps/units/flexible_du/split_8/split_8_du_factory.cpp index 00a2bde3d0..796952eaae 100644 --- a/apps/units/flexible_du/split_8/split_8_du_factory.cpp +++ b/apps/units/flexible_du/split_8/split_8_du_factory.cpp @@ -35,13 +35,6 @@ #include "srsran/du/du_wrapper_factory.h" #include "srsran/pcap/rlc_pcap.h" #include "srsran/ru/ru_dummy_factory.h" -#ifdef DPDK_FOUND -#include "srsran/hal/dpdk/bbdev/bbdev_acc.h" -#include "srsran/hal/dpdk/bbdev/bbdev_acc_factory.h" -#include "srsran/hal/phy/upper/channel_processors/hw_accelerator_factories.h" -#include "srsran/hal/phy/upper/channel_processors/pusch/ext_harq_buffer_context_repository_factory.h" -#include "srsran/hal/phy/upper/channel_processors/pusch/hw_accelerator_factories.h" -#endif // DPDK_FOUND using namespace srsran; @@ -120,81 +113,6 @@ du_unit srsran::create_split_8_du(const split_8_du_unit_config& du_8_cfg, const max_pusch_per_slot.push_back(high.cell.pusch_cfg.max_puschs_per_slot); } - // Initialize hardware-accelerator (only if needed). - hal_upper_phy_config hal_config = {}; - hal_config.hwacc_pdsch_processor = false; - hal_config.hwacc_pusch_processor = false; -#ifdef DPDK_FOUND - hal::bbdev_hwacc_pdsch_enc_factory_configuration hwacc_pdsch_enc_cfg = {}; - hal::bbdev_hwacc_pusch_dec_factory_configuration hwacc_pusch_dec_cfg = {}; - std::shared_ptr harq_buffer_context = nullptr; - unsigned nof_hwacc_dus = du_cells.size(); - if (!du_lo.hal_config->bbdev_hwacc->hwacc_type.empty()) { - srslog::basic_logger& hwacc_logger = srslog::fetch_basic_logger("HWACC", false); - hwacc_logger.set_level(du_lo.loggers.hal_level); - - // Create a bbdev accelerator factory. - std::unique_ptr bbdev_acc_factory = - srsran::dpdk::create_bbdev_acc_factory(du_lo.hal_config->bbdev_hwacc->bbdev_acc_type); - report_error_if_not(bbdev_acc_factory, - "Unable to create the {} bbdev hardware-accelerator interface factory.", - du_lo.hal_config->bbdev_hwacc->bbdev_acc_type); - - // Intefacing to the bbdev-based hardware-accelerator. - dpdk::bbdev_acc_configuration bbdev_config; - bbdev_config.id = du_lo.hal_config->bbdev_hwacc->id; - if (du_lo.hal_config->bbdev_hwacc->pdsch_enc->nof_hwacc > 0) { - bbdev_config.nof_ldpc_enc_lcores = nof_hwacc_dus * du_lo.hal_config->bbdev_hwacc->pdsch_enc->nof_hwacc; - } - if (du_lo.hal_config->bbdev_hwacc->pusch_dec->nof_hwacc > 0) { - bbdev_config.nof_ldpc_dec_lcores = nof_hwacc_dus * du_lo.hal_config->bbdev_hwacc->pusch_dec->nof_hwacc; - } - // If no msg_mbuf size is defined, a worst-case value will be used. - bbdev_config.msg_mbuf_size = du_lo.hal_config->bbdev_hwacc->msg_mbuf_size.value_or(RTE_BBDEV_LDPC_E_MAX_MBUF); - // If no rm_mbuf size is defined, a worst-case value will be used. - bbdev_config.rm_mbuf_size = du_lo.hal_config->bbdev_hwacc->rm_mbuf_size.value_or(RTE_BBDEV_LDPC_E_MAX_MBUF); - // If no number of mbufs is defined, a worst-case value will be used. - bbdev_config.nof_mbuf = - du_lo.hal_config->bbdev_hwacc->nof_mbuf.value_or(static_cast(pow2(log2_ceil(MAX_NOF_SEGMENTS)))); - std::shared_ptr bbdev_accelerator = bbdev_acc_factory->create(bbdev_config, hwacc_logger); - report_error_if_not( - bbdev_accelerator, "Unable to open the {} hardware-accelerator.", du_lo.hal_config->bbdev_hwacc->hwacc_type); - - // Configure the hardware-accelerated PDSCH encoding factory (only if needed). - if (du_lo.hal_config->bbdev_hwacc->pdsch_enc->nof_hwacc > 0) { - hwacc_pdsch_enc_cfg.acc_type = du_lo.hal_config->bbdev_hwacc->hwacc_type; - hwacc_pdsch_enc_cfg.bbdev_accelerator = bbdev_accelerator; - hwacc_pdsch_enc_cfg.cb_mode = du_lo.hal_config->bbdev_hwacc->pdsch_enc->cb_mode; - // If no maximum buffer size is defined, a worst-case value will be used. - hwacc_pdsch_enc_cfg.max_tb_size = - du_lo.hal_config->bbdev_hwacc->pdsch_enc->max_buffer_size.value_or(RTE_BBDEV_LDPC_E_MAX_MBUF); - hwacc_pdsch_enc_cfg.dedicated_queue = du_lo.hal_config->bbdev_hwacc->pdsch_enc->dedicated_queue; - hal_config.hwacc_pdsch_processor = true; - hal_config.hwacc_pdsch_enc_cfg = hwacc_pdsch_enc_cfg; - } - - // Configure the hardware-accelerated PUSCH decoding factory (only if needed). - if (du_lo.hal_config->bbdev_hwacc->pusch_dec->nof_hwacc > 0) { - hwacc_pusch_dec_cfg.acc_type = du_lo.hal_config->bbdev_hwacc->hwacc_type; - hwacc_pusch_dec_cfg.bbdev_accelerator = bbdev_accelerator; - hwacc_pusch_dec_cfg.ext_softbuffer = du_lo.hal_config->bbdev_hwacc->pusch_dec->ext_softbuffer; - if (hwacc_pusch_dec_cfg.ext_softbuffer) { - // Set up an external HARQ buffer context repository. - unsigned nof_cbs = du_lo.hal_config->bbdev_hwacc->pusch_dec->harq_context_size.value_or(MAX_NOF_SEGMENTS); - uint64_t ext_harq_buff_size = bbdev_accelerator->get_harq_buff_size_bytes(); - harq_buffer_context = hal::create_ext_harq_buffer_context_repository(nof_cbs, ext_harq_buff_size, false); - report_error_if_not(harq_buffer_context, - "Unable to create the external HARQ buffer context for the {} hardware-accelerator.", - du_lo.hal_config->bbdev_hwacc->hwacc_type); - hwacc_pusch_dec_cfg.harq_buffer_context = harq_buffer_context; - } - hwacc_pusch_dec_cfg.dedicated_queue = du_lo.hal_config->bbdev_hwacc->pusch_dec->dedicated_queue; - hal_config.hwacc_pusch_processor = true; - hal_config.hwacc_pusch_dec_cfg = hwacc_pusch_dec_cfg; - } - } -#endif // DPDK_FOUND - for (unsigned i = 0, e = du_cells.size(); i != e; ++i) { // Create one DU per cell. srs_du::du_wrapper_config du_cfg = {}; @@ -204,7 +122,6 @@ du_unit srsran::create_split_8_du(const split_8_du_unit_config& du_8_cfg, const make_du_low_wrapper_config_and_dependencies(du_cfg.du_low_cfg, du_lo, - hal_config, {prach_ports[i]}, span(&du_cells[i], 1), span(&max_pusch_per_slot[i], 1), diff --git a/apps/units/flexible_du/split_dynamic/CMakeLists.txt b/apps/units/flexible_du/split_dynamic/CMakeLists.txt index 4054cc4dfd..ec66acc97f 100644 --- a/apps/units/flexible_du/split_dynamic/CMakeLists.txt +++ b/apps/units/flexible_du/split_dynamic/CMakeLists.txt @@ -29,7 +29,13 @@ set(SOURCES add_library(srsran_flexible_du STATIC ${SOURCES}) target_include_directories(srsran_flexible_du PRIVATE ${CMAKE_SOURCE_DIR}) -set(FLEXIBLE_DU_LIBRARIES + +# Hardware acceleration for both PUSCH and PDSCH is enabled by default when using DPDK. +if (DPDK_FOUND) + set_source_files_properties(dynamic_du_factory.cpp PROPERTIES COMPILE_DEFINITIONS "DPDK_FOUND; HWACC_PDSCH_ENABLED; HWACC_PUSCH_ENABLED") +endif (DPDK_FOUND) + +target_link_libraries(srsran_flexible_du srsran_du_wrapper srsran_ru_dummy srsran_pcap @@ -39,12 +45,3 @@ set(FLEXIBLE_DU_LIBRARIES srsran_split_8_app_unit_helpers srsran_split_7_2_app_unit_helpers srsran_du_high_unit_helpers) - -# Hardware acceleration for both PUSCH and PDSCH is enabled by default when using DPDK. -if (DPDK_FOUND) - set_source_files_properties(dynamic_du_factory.cpp PROPERTIES COMPILE_DEFINITIONS "DPDK_FOUND; HWACC_PDSCH_ENABLED; HWACC_PUSCH_ENABLED") - list(APPEND FLEXIBLE_DU_LIBRARIES hal_hwacc_pusch - hal_hwacc_pdsch - hal_bbdev_factory) -endif (DPDK_FOUND) -target_link_libraries(srsran_flexible_du ${FLEXIBLE_DU_LIBRARIES}) diff --git a/apps/units/flexible_du/split_dynamic/dynamic_du_application_unit_impl.cpp b/apps/units/flexible_du/split_dynamic/dynamic_du_application_unit_impl.cpp index c0ee55b21f..f854c87156 100644 --- a/apps/units/flexible_du/split_dynamic/dynamic_du_application_unit_impl.cpp +++ b/apps/units/flexible_du/split_dynamic/dynamic_du_application_unit_impl.cpp @@ -46,6 +46,15 @@ bool dynamic_du_application_unit_impl::on_configuration_validation( return validate_dynamic_du_unit_config(unit_cfg, available_cpus); } +dynamic_du_application_unit_impl::dynamic_du_application_unit_impl(std::string_view app_name) +{ + unit_cfg.du_high_cfg.config.pcaps.e2ap.filename = fmt::format("/tmp/{}_e2ap.pcap", app_name); + unit_cfg.du_high_cfg.config.pcaps.f1ap.filename = fmt::format("/tmp/{}_f1ap.pcap", app_name); + unit_cfg.du_high_cfg.config.pcaps.f1u.filename = fmt::format("/tmp/{}_f1u.pcap", app_name); + unit_cfg.du_high_cfg.config.pcaps.rlc.filename = fmt::format("/tmp/{}_rlc.pcap", app_name); + unit_cfg.du_high_cfg.config.pcaps.mac.filename = fmt::format("/tmp/{}_mac.pcap", app_name); +} + void dynamic_du_application_unit_impl::on_parsing_configuration_registration(CLI::App& app) { configure_cli11_with_dynamic_du_unit_config_schema(app, unit_cfg); @@ -56,9 +65,9 @@ du_unit dynamic_du_application_unit_impl::create_flexible_du_unit(const du_unit_ return create_dynamic_du(unit_cfg, dependencies); } -std::unique_ptr srsran::create_flexible_du_application_unit() +std::unique_ptr srsran::create_flexible_du_application_unit(std::string_view app_name) { - return std::make_unique(); + return std::make_unique(app_name); } void dynamic_du_application_unit_impl::dump_config(YAML::Node& node) const diff --git a/apps/units/flexible_du/split_dynamic/dynamic_du_application_unit_impl.h b/apps/units/flexible_du/split_dynamic/dynamic_du_application_unit_impl.h index 274a194757..4d27eb9270 100644 --- a/apps/units/flexible_du/split_dynamic/dynamic_du_application_unit_impl.h +++ b/apps/units/flexible_du/split_dynamic/dynamic_du_application_unit_impl.h @@ -33,6 +33,8 @@ namespace srsran { class dynamic_du_application_unit_impl : public flexible_du_application_unit { public: + explicit dynamic_du_application_unit_impl(std::string_view app_name); + // See interface for documentation. void on_parsing_configuration_registration(CLI::App& app) override; diff --git a/apps/units/flexible_du/split_dynamic/dynamic_du_factory.cpp b/apps/units/flexible_du/split_dynamic/dynamic_du_factory.cpp index 0a1bb67438..455d62db06 100644 --- a/apps/units/flexible_du/split_dynamic/dynamic_du_factory.cpp +++ b/apps/units/flexible_du/split_dynamic/dynamic_du_factory.cpp @@ -39,13 +39,6 @@ #include "srsran/du/du_wrapper_factory.h" #include "srsran/pcap/rlc_pcap.h" #include "srsran/ru/ru_dummy_factory.h" -#ifdef DPDK_FOUND -#include "srsran/hal/dpdk/bbdev/bbdev_acc.h" -#include "srsran/hal/dpdk/bbdev/bbdev_acc_factory.h" -#include "srsran/hal/phy/upper/channel_processors/hw_accelerator_factories.h" -#include "srsran/hal/phy/upper/channel_processors/pusch/ext_harq_buffer_context_repository_factory.h" -#include "srsran/hal/phy/upper/channel_processors/pusch/hw_accelerator_factories.h" -#endif // DPDK_FOUND using namespace srsran; @@ -171,81 +164,6 @@ du_unit srsran::create_dynamic_du(const dynamic_du_unit_config& dyn_du_cfg, cons max_pusch_per_slot.push_back(high.cell.pusch_cfg.max_puschs_per_slot); } - // Initialize hardware-accelerator (only if needed). - hal_upper_phy_config hal_config = {}; - hal_config.hwacc_pdsch_processor = false; - hal_config.hwacc_pusch_processor = false; -#ifdef DPDK_FOUND - hal::bbdev_hwacc_pdsch_enc_factory_configuration hwacc_pdsch_enc_cfg = {}; - hal::bbdev_hwacc_pusch_dec_factory_configuration hwacc_pusch_dec_cfg = {}; - std::shared_ptr harq_buffer_context = nullptr; - unsigned nof_hwacc_dus = du_cells.size(); - if (!du_lo.hal_config->bbdev_hwacc->hwacc_type.empty()) { - srslog::basic_logger& hwacc_logger = srslog::fetch_basic_logger("HWACC", false); - hwacc_logger.set_level(du_lo.loggers.hal_level); - - // Create a bbdev accelerator factory. - std::unique_ptr bbdev_acc_factory = - srsran::dpdk::create_bbdev_acc_factory(du_lo.hal_config->bbdev_hwacc->bbdev_acc_type); - report_error_if_not(bbdev_acc_factory, - "Unable to create the {} bbdev hardware-accelerator interface factory.", - du_lo.hal_config->bbdev_hwacc->bbdev_acc_type); - - // Intefacing to the bbdev-based hardware-accelerator. - dpdk::bbdev_acc_configuration bbdev_config; - bbdev_config.id = du_lo.hal_config->bbdev_hwacc->id; - if (du_lo.hal_config->bbdev_hwacc->pdsch_enc->nof_hwacc > 0) { - bbdev_config.nof_ldpc_enc_lcores = nof_hwacc_dus * du_lo.hal_config->bbdev_hwacc->pdsch_enc->nof_hwacc; - } - if (du_lo.hal_config->bbdev_hwacc->pusch_dec->nof_hwacc > 0) { - bbdev_config.nof_ldpc_dec_lcores = nof_hwacc_dus * du_lo.hal_config->bbdev_hwacc->pusch_dec->nof_hwacc; - } - // If no msg_mbuf size is defined, a worst-case value will be used. - bbdev_config.msg_mbuf_size = du_lo.hal_config->bbdev_hwacc->msg_mbuf_size.value_or(RTE_BBDEV_LDPC_E_MAX_MBUF); - // If no rm_mbuf size is defined, a worst-case value will be used. - bbdev_config.rm_mbuf_size = du_lo.hal_config->bbdev_hwacc->rm_mbuf_size.value_or(RTE_BBDEV_LDPC_E_MAX_MBUF); - // If no number of mbufs is defined, a worst-case value will be used. - bbdev_config.nof_mbuf = - du_lo.hal_config->bbdev_hwacc->nof_mbuf.value_or(static_cast(pow2(log2_ceil(MAX_NOF_SEGMENTS)))); - std::shared_ptr bbdev_accelerator = bbdev_acc_factory->create(bbdev_config, hwacc_logger); - report_error_if_not( - bbdev_accelerator, "Unable to open the {} hardware-accelerator.", du_lo.hal_config->bbdev_hwacc->hwacc_type); - - // Configure the hardware-accelerated PDSCH encoding factory (only if needed). - if (du_lo.hal_config->bbdev_hwacc->pdsch_enc->nof_hwacc > 0) { - hwacc_pdsch_enc_cfg.acc_type = du_lo.hal_config->bbdev_hwacc->hwacc_type; - hwacc_pdsch_enc_cfg.bbdev_accelerator = bbdev_accelerator; - hwacc_pdsch_enc_cfg.cb_mode = du_lo.hal_config->bbdev_hwacc->pdsch_enc->cb_mode; - // If no maximum buffer size is defined, a worst-case value will be used. - hwacc_pdsch_enc_cfg.max_tb_size = - du_lo.hal_config->bbdev_hwacc->pdsch_enc->max_buffer_size.value_or(RTE_BBDEV_LDPC_E_MAX_MBUF); - hwacc_pdsch_enc_cfg.dedicated_queue = du_lo.hal_config->bbdev_hwacc->pdsch_enc->dedicated_queue; - hal_config.hwacc_pdsch_processor = true; - hal_config.hwacc_pdsch_enc_cfg = hwacc_pdsch_enc_cfg; - } - - // Configure the hardware-accelerated PUSCH decoding factory (only if needed). - if (du_lo.hal_config->bbdev_hwacc->pusch_dec->nof_hwacc > 0) { - hwacc_pusch_dec_cfg.acc_type = du_lo.hal_config->bbdev_hwacc->hwacc_type; - hwacc_pusch_dec_cfg.bbdev_accelerator = bbdev_accelerator; - hwacc_pusch_dec_cfg.ext_softbuffer = du_lo.hal_config->bbdev_hwacc->pusch_dec->ext_softbuffer; - if (hwacc_pusch_dec_cfg.ext_softbuffer) { - // Set up an external HARQ buffer context repository. - unsigned nof_cbs = du_lo.hal_config->bbdev_hwacc->pusch_dec->harq_context_size.value_or(MAX_NOF_SEGMENTS); - uint64_t ext_harq_buff_size = bbdev_accelerator->get_harq_buff_size_bytes(); - harq_buffer_context = hal::create_ext_harq_buffer_context_repository(nof_cbs, ext_harq_buff_size, false); - report_error_if_not(harq_buffer_context, - "Unable to create the external HARQ buffer context for the {} hardware-accelerator.", - du_lo.hal_config->bbdev_hwacc->hwacc_type); - hwacc_pusch_dec_cfg.harq_buffer_context = harq_buffer_context; - } - hwacc_pusch_dec_cfg.dedicated_queue = du_lo.hal_config->bbdev_hwacc->pusch_dec->dedicated_queue; - hal_config.hwacc_pusch_processor = true; - hal_config.hwacc_pusch_dec_cfg = hwacc_pusch_dec_cfg; - } - } -#endif // DPDK_FOUND - for (unsigned i = 0, e = du_cells.size(); i != e; ++i) { // Create one DU per cell. srs_du::du_wrapper_config du_cfg = {}; @@ -255,7 +173,6 @@ du_unit srsran::create_dynamic_du(const dynamic_du_unit_config& dyn_du_cfg, cons make_du_low_wrapper_config_and_dependencies(du_cfg.du_low_cfg, du_lo, - hal_config, {prach_ports[i]}, span(&du_cells[i], 1), span(&max_pusch_per_slot[i], 1), diff --git a/apps/units/flexible_du/split_dynamic/dynamic_du_unit_cli11_schema.cpp b/apps/units/flexible_du/split_dynamic/dynamic_du_unit_cli11_schema.cpp index d625a72508..349a78d665 100644 --- a/apps/units/flexible_du/split_dynamic/dynamic_du_unit_cli11_schema.cpp +++ b/apps/units/flexible_du/split_dynamic/dynamic_du_unit_cli11_schema.cpp @@ -167,9 +167,9 @@ void srsran::autoderive_dynamic_du_parameters_after_parsing(CLI::App& app, dynam } // Auto derive DU low parameters. - const auto& cell = parsed_cfg.du_high_cfg.config.cells_cfg.front().cell; - nr_band band = cell.band ? cell.band.value() : band_helper::get_band_from_dl_arfcn(cell.dl_f_ref_arfcn); - bool is_zmq_rf_driver = false; + const auto& cell = parsed_cfg.du_high_cfg.config.cells_cfg.front().cell; + const nr_band band = cell.band ? cell.band.value() : band_helper::get_band_from_dl_arfcn(cell.dl_f_ref_arfcn); + bool is_zmq_rf_driver = false; if (std::holds_alternative(parsed_cfg.ru_cfg)) { is_zmq_rf_driver = std::get(parsed_cfg.ru_cfg).device_driver == "zmq"; } diff --git a/include/srsran/f1u/cu_up/f1u_gateway.h b/include/srsran/f1u/cu_up/f1u_gateway.h index 9341d68a13..f79710a804 100644 --- a/include/srsran/f1u/cu_up/f1u_gateway.h +++ b/include/srsran/f1u/cu_up/f1u_gateway.h @@ -70,6 +70,8 @@ class f1u_cu_up_gateway : public srs_cu_up::f1u_bearer_disconnector virtual void attach_dl_teid(const up_transport_layer_info& ul_up_tnl_info, const up_transport_layer_info& dl_up_tnl_info) = 0; + + virtual expected get_cu_bind_address() const = 0; }; /// This class will be used to provide the interfaces to diff --git a/include/srsran/f1u/cu_up/split_connector/f1u_split_connector_factory.h b/include/srsran/f1u/cu_up/split_connector/f1u_split_connector_factory.h index 2a10490d9c..f36c8e18a3 100644 --- a/include/srsran/f1u/cu_up/split_connector/f1u_split_connector_factory.h +++ b/include/srsran/f1u/cu_up/split_connector/f1u_split_connector_factory.h @@ -31,8 +31,8 @@ namespace srsran::srs_cu_up { struct f1u_cu_up_split_gateway_creation_msg { - ngu_gateway* udp_gw; - gtpu_demux* demux; + ngu_gateway& udp_gw; + gtpu_demux& demux; dlt_pcap& gtpu_pcap; uint16_t peer_port; }; diff --git a/include/srsran/f1u/local_connector/f1u_local_connector.h b/include/srsran/f1u/local_connector/f1u_local_connector.h index e8aaa23aa2..59130e4727 100644 --- a/include/srsran/f1u/local_connector/f1u_local_connector.h +++ b/include/srsran/f1u/local_connector/f1u_local_connector.h @@ -213,6 +213,8 @@ class f1u_local_connector final : public srs_du::f1u_du_gateway, public f1u_cu_u return fmt::format("127.0.0.{}", 1 + static_cast(gnb_du_id)); } + expected get_cu_bind_address() const override { return {"127.0.2.1"}; } + private: srslog::basic_logger& logger_cu; srslog::basic_logger& logger_du; diff --git a/include/srsran/ran/band_helper.h b/include/srsran/ran/band_helper.h index d5c6842acc..2b4620fa9f 100644 --- a/include/srsran/ran/band_helper.h +++ b/include/srsran/ran/band_helper.h @@ -97,6 +97,14 @@ error_type is_dl_arfcn_valid_given_band(nr_band band, subcarrier_spacing scs, bs_channel_bandwidth bw = bs_channel_bandwidth::MHz10); +/// \brief Checks whether an Uplink ARFCN is valid for a given band. +/// \param[in] band Given NR band. +/// \param[in] arfcn_f_ref Given Uplink ARFCN of \c F_REF, as per TS 38.104, Section 5.4.2.1. +/// \param[in] bw Channel Bandwidth in MHz, which is required only to validate n28. +/// \return If the UL ARFCN is invalid for the band, a std::string value is returned with the reason. +error_type +is_ul_arfcn_valid_given_band(nr_band band, uint32_t arfcn_f_ref, bs_channel_bandwidth bw = bs_channel_bandwidth::MHz10); + /// @brief Get the respective UL ARFCN of a DL ARFCN. /// /// For paired spectrum (FDD) the function returns the respective ARFCN in the same band. diff --git a/include/srsran/ran/ssb_mapping.h b/include/srsran/ran/ssb_mapping.h index 4250d0391f..2bacff3b5a 100644 --- a/include/srsran/ran/ssb_mapping.h +++ b/include/srsran/ran/ssb_mapping.h @@ -170,9 +170,6 @@ inline unsigned ssb_get_k_first(frequency_range fr, return (k_first_15kHz * 15) / ssb_scs_kHz; } -/// Calculates SSB pattern from SSB subcarrier spacing and DL ARFCN. -ssb_pattern_case ssb_get_ssb_pattern(subcarrier_spacing ssb_scs, unsigned dl_arfcn); - /// \brief Calculates L_max, ie max number of SSB occasions per SSB period. Possible values are {4, 8, 64}. /// \remark See TS 38.213, Section 4.1. /// \param ssb_scs SSB Subcarrier Spacing. diff --git a/include/srsran/scheduler/config/serving_cell_config_validator.h b/include/srsran/scheduler/config/serving_cell_config_validator.h index 2aabaeba69..003907c2e8 100644 --- a/include/srsran/scheduler/config/serving_cell_config_validator.h +++ b/include/srsran/scheduler/config/serving_cell_config_validator.h @@ -44,7 +44,12 @@ validator_result validate_pdsch_cfg(const serving_cell_config& ue_cell_cfg); /// \param[in] ue_cell_cfg UE serving cell configuration to be validated. /// \param[in] nof_dl_antennas Number of antennas used for DL tx. /// \return In case an invalid parameter is detected, returns a string containing an error message. -error_type validate_pucch_cfg(const serving_cell_config& ue_cell_cfg, unsigned nof_dl_antennas); +validator_result validate_pucch_cfg(const serving_cell_config& ue_cell_cfg, unsigned nof_dl_antennas); + +/// \brief Validates SRS Config in \c sched_ue_creation_request_message used to create a UE. +/// \param[in] ue_cell_cfg UE serving cell configuration to be validated. +/// \return In case an invalid parameter is detected, returns a string containing an error message. +validator_result validate_srs_cfg(const serving_cell_config& ue_cell_cfg); /// \brief Validates the NZP-CSI-RS Resource list in \c serving_cell_config passed to a UE. validator_result validate_nzp_csi_rs_list(span nzp_csi_rs_list, diff --git a/include/srsran/scheduler/scheduler_paging_handler.h b/include/srsran/scheduler/scheduler_paging_handler.h index 2836751182..d85ccb912d 100644 --- a/include/srsran/scheduler/scheduler_paging_handler.h +++ b/include/srsran/scheduler/scheduler_paging_handler.h @@ -34,7 +34,7 @@ struct sched_paging_information { /// (Bit string of size 48). See TS 38.331. uint64_t paging_identity; /// Cells at which to perform Paging of UE. - std::vector paging_cells; + static_vector paging_cells; /// UE_ID: 5G-S-TMSI mod 1024. Used by the paging scheduler to calculate the Paging Frame. /// \remark See TS 38.304, clause 7.1. unsigned ue_identity_index_value; diff --git a/lib/du/du_low/du_low_wrapper_factory.cpp b/lib/du/du_low/du_low_wrapper_factory.cpp index 5143ae96fd..3101249ef1 100644 --- a/lib/du/du_low/du_low_wrapper_factory.cpp +++ b/lib/du/du_low/du_low_wrapper_factory.cpp @@ -64,11 +64,11 @@ static fapi::prach_config generate_prach_config_tlv(const du_cell_config& cell_c static fapi::carrier_config generate_carrier_config_tlv(const du_cell_config& du_cell) { // Deduce common numerology and grid size for DL and UL. - unsigned numerology = to_numerology_value(du_cell.scs_common); - unsigned grid_size_bw_prb = band_helper::get_n_rbs_from_bw( - MHz_to_bs_channel_bandwidth(du_cell.dl_carrier.carrier_bw_mhz), - du_cell.scs_common, - band_helper::get_freq_range(band_helper::get_band_from_dl_arfcn(du_cell.dl_carrier.arfcn_f_ref))); + unsigned numerology = to_numerology_value(du_cell.scs_common); + unsigned grid_size_bw_prb = + band_helper::get_n_rbs_from_bw(MHz_to_bs_channel_bandwidth(du_cell.dl_carrier.carrier_bw_mhz), + du_cell.scs_common, + band_helper::get_freq_range(du_cell.dl_carrier.band)); fapi::carrier_config fapi_config = {}; diff --git a/lib/f1u/cu_up/split_connector/f1u_split_connector.cpp b/lib/f1u/cu_up/split_connector/f1u_split_connector.cpp index 1bc5023bac..1bc6e8e380 100644 --- a/lib/f1u/cu_up/split_connector/f1u_split_connector.cpp +++ b/lib/f1u/cu_up/split_connector/f1u_split_connector.cpp @@ -121,8 +121,8 @@ void f1u_split_gateway_cu_bearer::stop() stopped = true; } -f1u_split_connector::f1u_split_connector(ngu_gateway* udp_gw_, - gtpu_demux* demux_, +f1u_split_connector::f1u_split_connector(ngu_gateway& udp_gw_, + gtpu_demux& demux_, dlt_pcap& gtpu_pcap_, uint16_t peer_port_) : logger_cu(srslog::fetch_basic_logger("CU-F1-U")), @@ -132,8 +132,8 @@ f1u_split_connector::f1u_split_connector(ngu_gateway* udp_gw_, gtpu_pcap(gtpu_pcap_) { gw_data_gtpu_demux_adapter = std::make_unique(); - udp_session = udp_gw->create(*gw_data_gtpu_demux_adapter); - gw_data_gtpu_demux_adapter->connect_gtpu_demux(*demux); + udp_session = udp_gw.create(*gw_data_gtpu_demux_adapter); + gw_data_gtpu_demux_adapter->connect_gtpu_demux(demux); } f1u_split_connector::~f1u_split_connector() = default; @@ -167,7 +167,7 @@ f1u_split_connector::create_cu_bearer(uint32_t ue_i cu_bearer->attach_tunnel_rx(std::move(tunnel_rx)); // attach tunnel rx to DEMUX - if (!demux->add_tunnel(ul_up_tnl_info.gtp_teid, cu_bearer->ul_exec, cu_bearer->get_tunnel_rx_interface())) { + if (!demux.add_tunnel(ul_up_tnl_info.gtp_teid, cu_bearer->ul_exec, cu_bearer->get_tunnel_rx_interface())) { logger_cu.error("Could not attach UL-TEID to demux RX. TEID {} already exists", ul_up_tnl_info.gtp_teid); // continue here; but the new tunnel won't be able to rx any data because the TEID was already registered at demux } @@ -226,7 +226,7 @@ void f1u_split_connector::disconnect_cu_bearer(const up_transport_layer_info& ul cu_bearer->gtpu_to_f1u_adapter->disconnect(); // Remove UL from GTP-U demux - demux->remove_tunnel(ul_up_tnl_info.gtp_teid); + demux.remove_tunnel(ul_up_tnl_info.gtp_teid); // Remove DL path { @@ -235,3 +235,14 @@ void f1u_split_connector::disconnect_cu_bearer(const up_transport_layer_info& ul } logger_cu.debug("Removed CU F1-U bearer with UL GTP Tunnel={}.", ul_up_tnl_info); } + +expected f1u_split_connector::get_cu_bind_address() const +{ + std::string ip_address; + + if (not udp_session->get_bind_address(ip_address)) { + return make_unexpected(default_error_t{}); + } + + return ip_address; +} diff --git a/lib/f1u/cu_up/split_connector/f1u_split_connector.h b/lib/f1u/cu_up/split_connector/f1u_split_connector.h index 20886660e5..9c73a5be1e 100644 --- a/lib/f1u/cu_up/split_connector/f1u_split_connector.h +++ b/lib/f1u/cu_up/split_connector/f1u_split_connector.h @@ -114,7 +114,7 @@ class f1u_split_gateway_cu_bearer final : public f1u_cu_up_gateway_bearer class f1u_split_connector final : public f1u_cu_up_udp_gateway { public: - f1u_split_connector(ngu_gateway* udp_gw_, gtpu_demux* demux_, dlt_pcap& gtpu_pcap_, uint16_t peer_port_ = GTPU_PORT); + f1u_split_connector(ngu_gateway& udp_gw_, gtpu_demux& demux_, dlt_pcap& gtpu_pcap_, uint16_t peer_port_ = GTPU_PORT); ~f1u_split_connector() override; f1u_cu_up_gateway* get_f1u_cu_up_gateway() { return this; } @@ -133,6 +133,8 @@ class f1u_split_connector final : public f1u_cu_up_udp_gateway void disconnect_cu_bearer(const up_transport_layer_info& ul_up_tnl_info) override; + expected get_cu_bind_address() const override; + private: srslog::basic_logger& logger_cu; // Key is the UL UP TNL Info (CU-CP address and UL TEID reserved by CU-CP) @@ -140,9 +142,9 @@ class f1u_split_connector final : public f1u_cu_up_udp_gateway std::mutex map_mutex; // shared mutex for access to cu_map uint16_t peer_port; - ngu_gateway* udp_gw; + ngu_gateway& udp_gw; std::unique_ptr udp_session; - gtpu_demux* demux; + gtpu_demux& demux; std::unique_ptr gw_data_gtpu_demux_adapter; dlt_pcap& gtpu_pcap; }; diff --git a/lib/gtpu/gtpu_demux_impl.cpp b/lib/gtpu/gtpu_demux_impl.cpp index ab3b63269c..0562f52cf6 100644 --- a/lib/gtpu/gtpu_demux_impl.cpp +++ b/lib/gtpu/gtpu_demux_impl.cpp @@ -37,48 +37,51 @@ bool gtpu_demux_impl::add_tunnel(gtpu_teid_t te gtpu_tunnel_common_rx_upper_layer_interface* tunnel) { std::lock_guard guard(map_mutex); - if (teid_to_tunnel.find(teid) != teid_to_tunnel.end()) { + auto it = teid_to_tunnel.try_emplace(teid, gtpu_demux_tunnel_ctx_t{&tunnel_exec, tunnel}); + if (not it.second) { logger.error("Tunnel already exists. teid={}", teid); return false; } + logger.info("Tunnel added. teid={}", teid); - teid_to_tunnel[teid] = {&tunnel_exec, tunnel}; return true; } bool gtpu_demux_impl::remove_tunnel(gtpu_teid_t teid) { std::lock_guard guard(map_mutex); - if (teid_to_tunnel.find(teid) == teid_to_tunnel.end()) { + auto it = teid_to_tunnel.find(teid); + if (it == teid_to_tunnel.end()) { logger.error("Tunnel not found. teid={}", teid); return false; } + logger.info("Tunnel removed. teid={}", teid); - teid_to_tunnel.erase(teid); + teid_to_tunnel.erase(it); return true; } void gtpu_demux_impl::handle_pdu(byte_buffer pdu, const sockaddr_storage& src_addr) { uint32_t read_teid = 0x01; // default to test DRB - if (!cfg.test_mode) { - if (!gtpu_read_teid(read_teid, pdu, logger)) { + if (not cfg.test_mode) { + if (not gtpu_read_teid(read_teid, pdu, logger)) { logger.error("Failed to read TEID from GTP-U PDU. pdu_len={}", pdu.length()); return; } } - gtpu_teid_t teid{read_teid}; std::lock_guard guard(map_mutex); - const auto& it = teid_to_tunnel.find(teid); + + gtpu_teid_t teid{read_teid}; + auto it = teid_to_tunnel.find(teid); if (it == teid_to_tunnel.end()) { logger.info("Dropped GTP-U PDU, tunnel not found. teid={}", teid); return; } - auto fn = [this, teid, p = std::move(pdu), src_addr]() mutable { handle_pdu_impl(teid, std::move(p), src_addr); }; - - if (not it->second.tunnel_exec->defer(std::move(fn))) { + if (not it->second.tunnel_exec->defer( + [this, teid, p = std::move(pdu), src_addr]() mutable { handle_pdu_impl(teid, std::move(p), src_addr); })) { if (not cfg.warn_on_drop) { logger.info("Dropped GTP-U PDU, queue is full. teid={}", teid); } else { @@ -102,19 +105,18 @@ void gtpu_demux_impl::handle_pdu_impl(gtpu_teid_t teid, byte_buffer pdu, const s gtpu_tunnel_common_rx_upper_layer_interface* tunnel = nullptr; { - /// Get GTP-U tunnel. We lookup the tunnel again, as the tunnel could have been - /// removed between the time PDU processing was enqueued and the time we actually - /// run the task. + // Get GTP-U tunnel. + // We lookup the tunnel again, as the tunnel could have been removed between the time PDU processing was enqueued + // and the time we actually run the task. std::lock_guard guard(map_mutex); - const auto& it = teid_to_tunnel.find(teid); + auto it = teid_to_tunnel.find(teid); if (it == teid_to_tunnel.end()) { logger.info("Dropped GTP-U PDU, tunnel not found. teid={}", teid); return; } tunnel = it->second.tunnel; } - // Forward entire PDU to the tunnel - // As removal happens in the same thread as handling the PDU, we no longer - // need the lock. + // Forward entire PDU to the tunnel. + // As removal happens in the same thread as handling the PDU, we no longer need the lock. tunnel->handle_pdu(std::move(pdu), src_addr); } diff --git a/lib/mac/mac_dl/mac_cell_processor.cpp b/lib/mac/mac_dl/mac_cell_processor.cpp index 9a918acd37..889e517a63 100644 --- a/lib/mac/mac_dl/mac_cell_processor.cpp +++ b/lib/mac/mac_dl/mac_cell_processor.cpp @@ -48,8 +48,7 @@ mac_cell_processor::mac_cell_processor(const mac_cell_creation_request& cell_cfg ue_mng(rnti_table), dl_harq_buffers(band_helper::get_n_rbs_from_bw(MHz_to_bs_channel_bandwidth(cell_cfg.dl_carrier.carrier_bw_mhz), cell_cfg.scs_common, - band_helper::get_freq_range(band_helper::get_band_from_dl_arfcn( - cell_cfg.dl_carrier.arfcn_f_ref))), + band_helper::get_freq_range(cell_cfg.dl_carrier.band)), cell_cfg.dl_carrier.nof_ant, ctrl_exec_), // The PDU pool has to be large enough to fit the maximum number of RARs and Paging PDUs per slot for all possible K0 diff --git a/lib/ran/band_helper.cpp b/lib/ran/band_helper.cpp index a6bf747af1..3eaf53cc91 100644 --- a/lib/ran/band_helper.cpp +++ b/lib/ran/band_helper.cpp @@ -63,7 +63,7 @@ struct nr_band_raster { // lower-bound and upper-bound. (FDD, TDD or SDL). // // NOTE: It only includes FDD, TDD, and SDL bands. -// NOTE: Band 2 is a subset of band 25 +// NOTE: Band 2 is a subset of band 25. // NOTE: Band 41 has two different Freq raster, we only consider raster 15kHz. // NOTE: FR2 bands have two different Freq raster, we only consider raster 120kHz. const uint32_t nof_nr_DL_bands = 83; @@ -93,6 +93,9 @@ static constexpr std::array nr_band_table = { {nr_band::n40, delta_freq_raster::kHz100, 460000, 20, 480000, 460000, 20, 480000}, {nr_band::n41, delta_freq_raster::kHz15, 499200, 3, 537999, 499200, 3, 537999}, {nr_band::n41, delta_freq_raster::kHz30, 499200, 6, 537996, 499200, 6, 537996}, + // Band n46 has a raster definded in Table 5.4.2.3-1, TS 38.104, but according to the table notes, only a few ARFCN + // values are valid. This range is reported below for completeness, but only the applicable ARFCN given in the + // notes will be used. {nr_band::n46, delta_freq_raster::kHz15, 743334, 1, 795000, 743334, 1, 795000}, {nr_band::n48, delta_freq_raster::kHz15, 636667, 1, 646666, 636667, 1, 646666}, {nr_band::n48, delta_freq_raster::kHz30, 636668, 2, 646666, 636668, 2, 646666}, @@ -129,12 +132,18 @@ static constexpr std::array nr_band_table = { {nr_band::n93, delta_freq_raster::kHz100, 176000, 20, 183000, 285400, 20, 286400}, {nr_band::n94, delta_freq_raster::kHz100, 176000, 20, 183000, 286400, 20, 303400}, {nr_band::n95, delta_freq_raster::kHz100, 402000, 20, 405000, 0, 0, 0}, + // Band n96 has a raster definded in Table 5.4.2.3-1, TS 38.104, but according to the table notes, only a few ARFCN + // values are valid. This range is reported below for completeness, but only the applicable ARFCN given in the + // notes will be used. {nr_band::n96, delta_freq_raster::kHz15, 795000, 1, 875000, 795000, 1, 875000}, {nr_band::n97, delta_freq_raster::kHz100, 460000, 20, 480000, 0, 0, 0}, {nr_band::n98, delta_freq_raster::kHz100, 376000, 20, 384000, 0, 0, 0}, {nr_band::n99, delta_freq_raster::kHz100, 325300, 20, 332100, 0, 0, 0}, {nr_band::n100, delta_freq_raster::kHz100, 174880, 20, 176000, 183880, 20, 185000}, {nr_band::n101, delta_freq_raster::kHz100, 380000, 20, 382000, 380000, 20, 382000}, + // Band n102 has a raster definded in Table 5.4.2.3-1, TS 38.104, but according to the table notes, only a few + // ARFCN values are valid. This range is reported below for completeness, but only the applicable ARFCN given in + // the notes will be used. {nr_band::n102, delta_freq_raster::kHz15, 796334, 1 , 828333, 796334, 1, 828333}, {nr_band::n104, delta_freq_raster::kHz15, 828334, 1 , 875000, 828334, 1, 875000}, {nr_band::n104, delta_freq_raster::kHz30, 828334, 2 , 875000, 828334, 2, 875000}, @@ -458,17 +467,13 @@ static nr_raster_params get_raster_params(double freq) static bool is_valid_raster_param(const nr_raster_params& raster) { - for (const nr_raster_params& fr : nr_fr_params) { - if (fr == raster) { - return true; - } - } - return false; + return std::any_of( + nr_fr_params.begin(), nr_fr_params.end(), [raster](const nr_raster_params& fr) { return fr == raster; }); } // Validates band n28, which has an additional ARFCN value to the given interval, as per Table 5.4.2.3-1, TS 38.104, // version 17.8.0. -static error_type validate_band_n28(uint32_t arfcn, bs_channel_bandwidth bw) +static error_type validate_band_n28(uint32_t arfcn, bs_channel_bandwidth bw, bool is_dl = true) { const nr_band_raster band_raster = fetch_band_raster(nr_band::n28, {}); if (band_raster.band == srsran::nr_band::invalid) { @@ -476,63 +481,60 @@ static error_type validate_band_n28(uint32_t arfcn, bs_channel_band } // Try first if the ARFCN matches any value of the interval for 100kHz channel raster. - if (arfcn >= band_raster.dl_nref_first and arfcn <= band_raster.dl_nref_last and - ((arfcn - band_raster.dl_nref_first) % band_raster.dl_nref_step) == 0) { + uint32_t nref_first = is_dl ? band_raster.dl_nref_first : band_raster.ul_nref_first; + uint32_t nref_last = is_dl ? band_raster.dl_nref_last : band_raster.ul_nref_last; + uint32_t nref_step = is_dl ? band_raster.dl_nref_step : band_raster.ul_nref_step; + if (arfcn >= nref_first and arfcn <= nref_last and ((arfcn - nref_first) % nref_step) == 0) { return error_type{}; } // Extra ARFCN value as per Table 5.4.2.3-1, TS 38.104, version 17.8.0 (see NOTE 4 in the table). - const uint32_t dl_arfnc_40MHz = 155608U; - if (bw == srsran::bs_channel_bandwidth::MHz40 and arfcn == dl_arfnc_40MHz) { + const uint32_t arfnc_40MHz = is_dl ? 155608U : 144608U; + if (bw == srsran::bs_channel_bandwidth::MHz40 and arfcn == arfnc_40MHz) { return error_type{}; } return make_unexpected( - fmt::format("DL ARFCN must be within the interval [{},{}], in steps of {}, for the chosen band", - band_raster.dl_nref_first, - band_raster.dl_nref_last, - band_raster.dl_nref_step)); + fmt::format("{} ARFCN must be within the interval [{},{}], in steps of {}, for the chosen band", + is_dl ? "DL" : "UL", + nref_first, + nref_last, + nref_step)); } // Validates band n46, whose valid ARFCN values depend on the channel BW, as per Table 5.4.2.3-1, TS 38.104, // version 17.8.0. static error_type validate_band_n46(uint32_t arfcn, bs_channel_bandwidth bw) { - const std::array n46_b_10_dlarfnc = {782000, 788668}; - const std::array n46_b_20_dlarfnc = { + constexpr std::array n46_b_10_dlarfnc = {782000, 788668}; + constexpr std::array n46_b_20_dlarfnc = { // clang-format off 744000, 745332, 746668, 748000, 749332, 750668, 752000, 753332, 754668, 756000, 765332, 766668, 768000, 769332, 770668, 772000, 773332, 774668, 776000, 777332, 778668, 780000, 781332, 783000, 784332, 785668, 787000, 788332, 789668, 791000, 792332, 793668 // clang-format on }; - const std::array n46_b_40_dlarfnc = { + constexpr std::array n46_b_40_dlarfnc = { // clang-format off 744668, 746000, 748668, 751332, 754000, 755332, 766000, 767332, 770000, 772668, 775332, 778000, 780668, 783668, 786332, 787668, 790332, 793000 // clang-format on }; - const std::array n46_b_60_dlarfnc = { + constexpr std::array n46_b_60_dlarfnc = { // clang-format off 745332, 746668, 748000, 752000, 753332, 754668, 766668, 768000, 769332, 773332, 774668, 778668, 780000, 784332, 785668, 791000, 792332 // clang-format on }; - const std::array n46_b_80_dlarfnc = { + constexpr std::array n46_b_80_dlarfnc = { 746000, 747332, 752668, 754000, 767332, 768668, 774000, 779332, 785000, 791668}; - const std::array n46_b_100_dlarfnc = {746668, 753332, 768000, 791000}; - - const nr_band_raster band_raster = fetch_band_raster(nr_band::n46, {}); - if (band_raster.band == srsran::nr_band::invalid or arfcn < band_raster.dl_nref_first or - arfcn > band_raster.dl_nref_last) { - return make_unexpected(fmt::format("Band n46 channel raster not found")); - } + constexpr std::array n46_b_100_dlarfnc = {746668, 753332, 768000, 791000}; + const char* error_msg = {"Only a restricted set of DL-ARFCN values are allowed in band n46"}; auto dl_arfcn_exist = [](span band_list, unsigned dl_arfcn) { return std::find(band_list.begin(), band_list.end(), dl_arfcn) != band_list.end(); }; - const char* error_msg = {"Only a restricted set of DL-ARFCN values are allowed in band n46"}; switch (bw) { case bs_channel_bandwidth::MHz10: { return dl_arfcn_exist(span(n46_b_10_dlarfnc), arfcn) ? error_type{} @@ -567,7 +569,7 @@ static error_type validate_band_n46(uint32_t arfcn, bs_channel_band // version 17.8.0. static error_type validate_band_n96(uint32_t arfcn, bs_channel_bandwidth bw) { - const std::array b_20_dlarfnc = { + constexpr std::array b_20_dlarfnc = { // clang-format off 797000, 798332, 799668, 801000, 802332, 803668, 805000, 806332, 807668, 809000, 810332, 811668, 813000, 814332, 815668, 817000, 818332, 819668, 821000, 822332, 823668, 825000, 826332, 827668, 829000, 830332, 831668, 833000, @@ -576,38 +578,32 @@ static error_type validate_band_n96(uint32_t arfcn, bs_channel_band 871668, 873000, 874332 // clang-format on }; - const std::array b_40_dlarfnc = { + constexpr std::array b_40_dlarfnc = { // clang-format off 797668, 800332, 803000, 805668, 808332, 811000, 813668, 816332, 819000, 821668, 824332, 827000, 829668, 832332, 835000, 837668, 840332, 843000, 845668, 848332, 851000, 853668, 856332, 859000, 861668, 864332, 867000, 869668, 872332 // clang-format on }; - const std::array b_60_dlarfnc = { + constexpr std::array b_60_dlarfnc = { // clang-format off 798332, 799668, 803668, 805000, 809000, 810332, 814332, 815668, 819668, 821000, 825000, 826332, 830332, 831668, 835668, 837000, 841000, 842332, 846332, 847668, 851668, 853000, 857000, 858332, 862332, 863668, 867668, 869000, 873000 // clang-format on }; - const std::array b_80_dlarfnc = { + constexpr std::array b_80_dlarfnc = { // clang-format off 799000, 804332, 809668, 815000, 820332, 825668, 831000, 836332, 841668, 847000, 852332, 857668, 863000, 868332 // clang-format on }; - const std::array b_100_dlarfnc = { + constexpr std::array b_100_dlarfnc = { // clang-format off 799668, 803668, 810332, 814332, 821000, 825000, 831668, 835668, 842332, 846332, 853000, 857000, 863668, 867668, 869000, 870332, 871668 // clang-format on }; - const nr_band_raster band_raster = fetch_band_raster(nr_band::n96, {}); - if (band_raster.band == srsran::nr_band::invalid or arfcn < band_raster.dl_nref_first or - arfcn > band_raster.dl_nref_last) { - return make_unexpected(fmt::format("Band n96 channel raster not found")); - } - auto dl_arfcn_exist = [](span band_list, unsigned dl_arfcn) { return std::find(band_list.begin(), band_list.end(), dl_arfcn) != band_list.end(); }; @@ -643,24 +639,24 @@ static error_type validate_band_n96(uint32_t arfcn, bs_channel_band // version 17.8.0. static error_type validate_band_n102(uint32_t arfcn, bs_channel_bandwidth bw) { - const std::array b_20_dlarfnc = { + constexpr std::array b_20_dlarfnc = { // clang-format off 797000, 798332, 799668, 801000, 802332, 803668, 805000, 806332, 807668, 809000, 810332, 811668, 813000, 814332, 815668, 817000, 818332, 819668, 821000, 822332, 823668, 825000, 826332, 827668 // clang-format on }; - const std::array b_40_dlarfnc = { + constexpr std::array b_40_dlarfnc = { // clang-format off 797668, 800332, 803000, 805668, 808332, 811000, 813668, 816332, 819000, 821668, 824332, 827000 // clang-format on }; - const std::array b_60_dlarfnc = { + constexpr std::array b_60_dlarfnc = { // clang-format off 798332, 799668, 803668, 805000, 809000, 810332, 814332, 815668, 819668, 821000, 825000, 826332 // clang-format on }; - const std::array b_80_dlarfnc = {799000, 804332, 809668, 815000, 820332, 825668}; - const std::array b_100_dlarfnc = {799668, 803668, 810332, 814332, 821000, 825000}; + constexpr std::array b_80_dlarfnc = {799000, 804332, 809668, 815000, 820332, 825668}; + constexpr std::array b_100_dlarfnc = {799668, 803668, 810332, 814332, 821000, 825000}; const nr_band_raster band_raster = fetch_band_raster(nr_band::n102, {}); if (band_raster.band == srsran::nr_band::invalid or arfcn < band_raster.dl_nref_first or @@ -755,7 +751,7 @@ error_type srsran::band_helper::is_dl_arfcn_valid_given_band(nr_ban { // Validates first the bands with non-standard ARFCN values. if (band == nr_band::n28) { - return validate_band_n28(arfcn_f_ref, bw); + return validate_band_n28(arfcn_f_ref, bw, true); } if (band == nr_band::n46) { @@ -808,10 +804,51 @@ error_type srsran::band_helper::is_dl_arfcn_valid_given_band(nr_ban return make_unexpected(fmt::format("Band {} is not valid", band)); } +error_type +srsran::band_helper::is_ul_arfcn_valid_given_band(nr_band band, uint32_t arfcn_f_ref, bs_channel_bandwidth bw) +{ + if (get_duplex_mode(band) != duplex_mode::FDD) { + return {}; + } + + // Validates first the bands with non-standard ARFCN values. + if (band == nr_band::n28) { + return validate_band_n28(arfcn_f_ref, bw, false); + } + + // Assume standard Delta freq raster of 100kHz. + const delta_freq_raster band_delta_freq_raster = delta_freq_raster::kHz100; + + for (const nr_band_raster& raster_band : nr_band_table) { + if (raster_band.band == band and raster_band.delta_f_rast == band_delta_freq_raster) { + // Check if the ARFCN doesn't exceed the band upper-bound for bands that support asymmetrical UL and DL channel + // BWs. + if ((band == nr_band::n66 or band == nr_band::n70 or band == nr_band::n92 or band == nr_band::n94) and + (arfcn_f_ref < raster_band.ul_nref_first or arfcn_f_ref > raster_band.ul_nref_last)) { + return make_unexpected( + fmt::format("Asymmetrical UL and DL channel BWs are not supported. The UL ARFCN resulting from the DL " + "ARFCN for band n{} must not exceed the band upper-bound={}", + band, + raster_band.ul_nref_last)); + } + if (arfcn_f_ref >= raster_band.ul_nref_first and arfcn_f_ref <= raster_band.ul_nref_last and + ((arfcn_f_ref - raster_band.ul_nref_first) % raster_band.ul_nref_step) == 0) { + return {}; + } + return make_unexpected( + fmt::format("UL ARFCN must be within the interval [{},{}], in steps of {}, for the chosen band", + raster_band.ul_nref_first, + raster_band.ul_nref_last, + raster_band.ul_nref_step)); + } + } + return make_unexpected(fmt::format("Band {} is not valid", band)); +} + uint32_t srsran::band_helper::get_ul_arfcn_from_dl_arfcn(uint32_t dl_arfcn, std::optional band) { // NOTE: The procedure implemented in this function is implementation-defined. - const nr_band operating_band = band.has_value() ? band.value() : get_band_from_dl_arfcn(dl_arfcn); + const nr_band operating_band = band.value_or(get_band_from_dl_arfcn(dl_arfcn)); // Return same ARFCN for TDD bands. if (get_duplex_mode(operating_band) == duplex_mode::TDD) { @@ -827,9 +864,14 @@ uint32_t srsran::band_helper::get_ul_arfcn_from_dl_arfcn(uint32_t dl_arfcn, std: // Derive UL ARFCN for FDD bands. for (const nr_band_raster& b_it : nr_band_table) { - if (b_it.band == get_band_from_dl_arfcn(dl_arfcn)) { - const uint32_t offset = (dl_arfcn - b_it.dl_nref_first) / b_it.dl_nref_step; - return (b_it.ul_nref_first + offset * b_it.ul_nref_step); + if (b_it.band == operating_band) { + const uint32_t offset = (dl_arfcn - b_it.dl_nref_first) / b_it.dl_nref_step; + const uint32_t candidate_ul_arfcn = b_it.ul_nref_first + offset * b_it.ul_nref_step; + // For band n66, n70, n92, n94, the UL spectrum is smaller than the corresponding DL spectrum, as these bands + // supports asymmetrical UL anc DL channel operations. However, the current GNB doesn't support this feature. + // If the resulting UL ARFCN is outside the valid range, return 0. + return (candidate_ul_arfcn >= b_it.ul_nref_first and candidate_ul_arfcn <= b_it.ul_nref_last) ? candidate_ul_arfcn + : 0U; } } diff --git a/lib/ran/ssb_mapping.cpp b/lib/ran/ssb_mapping.cpp index 32f5488c76..7ff8aafc8e 100644 --- a/lib/ran/ssb_mapping.cpp +++ b/lib/ran/ssb_mapping.cpp @@ -27,26 +27,17 @@ using namespace srsran; -ssb_pattern_case srsran::ssb_get_ssb_pattern(subcarrier_spacing ssb_scs, unsigned dl_arfcn) -{ - nr_band dl_idx_nr_band = band_helper::get_band_from_dl_arfcn(dl_arfcn); - srsran_assert(dl_idx_nr_band != nr_band::invalid, "Invalid NR band index"); - return band_helper::get_ssb_pattern(dl_idx_nr_band, ssb_scs); -} - uint8_t srsran::ssb_get_L_max(subcarrier_spacing ssb_scs, unsigned dl_arfcn, std::optional band) { uint8_t L_max = 0; // Derive the SSB-specific parameters (SSB pattern case, SSB L_max and SSB paired_spectrum flag) from those in the // MAC Cell config. - if (not band.has_value()) { - band.emplace(band_helper::get_band_from_dl_arfcn(dl_arfcn)); - srsran_assert(band.value() != nr_band::invalid, "Invalid NR band index"); - } - ssb_pattern_case ssb_case = band_helper::get_ssb_pattern(band.value(), ssb_scs); + const nr_band gnb_band = band.value_or(band_helper::get_band_from_dl_arfcn(dl_arfcn)); + srsran_assert(gnb_band != nr_band::invalid, "Invalid NR band index"); + ssb_pattern_case ssb_case = band_helper::get_ssb_pattern(gnb_band, ssb_scs); // Flag indicating whether cell is on paired spectrum (FDD) or unpaired (TDD, SDL, SUL). - bool paired_spectrum = band_helper::is_paired_spectrum(band.value()); + bool paired_spectrum = band_helper::is_paired_spectrum(gnb_band); // Get L_max from SSB pattern case and carrier frequency and paired spectrum flag. uint32_t f_arfcn = dl_arfcn; diff --git a/lib/scheduler/CMakeLists.txt b/lib/scheduler/CMakeLists.txt index ffec5e0894..3156b78612 100644 --- a/lib/scheduler/CMakeLists.txt +++ b/lib/scheduler/CMakeLists.txt @@ -35,6 +35,7 @@ set(SOURCES pucch_scheduling/pucch_resource_manager.cpp uci_scheduling/uci_scheduler_impl.cpp uci_scheduling/uci_allocator_impl.cpp + srs/srs_scheduler_impl.cpp support/pdcch/pdcch_type0_helpers.cpp support/pdsch/pdsch_default_time_allocation.cpp support/pdsch/pdsch_dmrs_symbol_mask.cpp diff --git a/lib/scheduler/cell/resource_grid.cpp b/lib/scheduler/cell/resource_grid.cpp index fe01bc7e3f..6155a7d173 100644 --- a/lib/scheduler/cell/resource_grid.cpp +++ b/lib/scheduler/cell/resource_grid.cpp @@ -286,6 +286,7 @@ void cell_slot_resource_allocator::slot_indication(slot_point new_slot) result.ul.puschs.clear(); result.ul.prachs.clear(); result.ul.pucchs.clear(); + result.ul.srss.clear(); dl_res_grid.clear(); ul_res_grid.clear(); diff --git a/lib/scheduler/cell_scheduler.cpp b/lib/scheduler/cell_scheduler.cpp index c10a7f3d3e..9e3386ba89 100644 --- a/lib/scheduler/cell_scheduler.cpp +++ b/lib/scheduler/cell_scheduler.cpp @@ -125,7 +125,7 @@ void cell_scheduler::run_slot(slot_point sl_tx) ra_sch.run_slot(res_grid); // > Schedule Paging. - pg_sch.schedule_paging(res_grid); + pg_sch.run_slot(res_grid); // > Schedule UE DL and UL data. ue_sched.run_slot(sl_tx); diff --git a/lib/scheduler/common_scheduling/paging_scheduler.cpp b/lib/scheduler/common_scheduling/paging_scheduler.cpp index 5293afb668..de79057033 100644 --- a/lib/scheduler/common_scheduling/paging_scheduler.cpp +++ b/lib/scheduler/common_scheduling/paging_scheduler.cpp @@ -33,6 +33,9 @@ using namespace srsran; +// (Implementation-defined) limit for maximum number of pending paging indications. +static constexpr size_t PAGING_INFO_QUEUE_SIZE = 128; + paging_scheduler::paging_scheduler(const scheduler_expert_config& expert_cfg_, const cell_configuration& cell_cfg_, pdcch_resource_allocator& pdcch_sch_, @@ -44,6 +47,7 @@ paging_scheduler::paging_scheduler(const scheduler_expert_config& nof_pf_per_drx_cycle(static_cast(cell_cfg.dl_cfg_common.pcch_cfg.nof_pf)), paging_frame_offset(cell_cfg.dl_cfg_common.pcch_cfg.paging_frame_offset), nof_po_per_pf(static_cast(cell_cfg.dl_cfg_common.pcch_cfg.ns)), + new_paging_notifications(PAGING_INFO_QUEUE_SIZE), logger(srslog::fetch_basic_logger("SCHED")) { if (cell_cfg.dl_cfg_common.init_dl_bwp.pdcch_common.paging_search_space_id.has_value()) { @@ -114,17 +118,14 @@ paging_scheduler::paging_scheduler(const scheduler_expert_config& } } -void paging_scheduler::schedule_paging(cell_resource_allocator& res_grid) +void paging_scheduler::run_slot(cell_resource_allocator& res_grid) { // Pop pending Paging notification and process them. - new_paging_notifications.slot_indication(); - span new_paging_infos = new_paging_notifications.get_events(); - for (const auto& pg_info : new_paging_infos) { + sched_paging_information new_pg_info; + while (new_paging_notifications.try_pop(new_pg_info)) { // Check whether Paging information is already present or not. i.e. tackle repeated Paging attempt from upper // layers. - if (paging_pending_ues.find(pg_info.paging_identity) == paging_pending_ues.cend()) { - paging_pending_ues[pg_info.paging_identity] = ue_paging_info{.info = pg_info, .retry_count = 0}; - } + paging_pending_ues.try_emplace(new_pg_info.paging_identity, ue_paging_info{.info = new_pg_info, .retry_count = 0}); } // NOTE: @@ -149,7 +150,7 @@ void paging_scheduler::schedule_paging(cell_resource_allocator& res_grid) // Check for maximum paging retries. auto it = paging_pending_ues.begin(); while (it != paging_pending_ues.end()) { - if (paging_pending_ues[it->first].retry_count >= expert_cfg.pg.max_paging_retries) { + if (it->second.retry_count >= expert_cfg.pg.max_paging_retries) { it = paging_pending_ues.erase(it); } else { ++it; @@ -267,7 +268,10 @@ unsigned paging_scheduler::get_accumulated_paging_msg_size(span @@ -52,7 +53,7 @@ class paging_scheduler /// \brief Performs paging (if any) scheduling for the current slot. /// /// \param[out,in] res_grid Resource grid with current allocations and scheduling results. - void schedule_paging(cell_resource_allocator& res_grid); + void run_slot(cell_resource_allocator& res_grid); /// Handles Paging information reported by upper layers. /// \param[in] paging_info Per UE paging information to be scheduled. @@ -64,6 +65,10 @@ class paging_scheduler paging_retries_count_type retry_count; }; + using paging_info_queue = concurrent_queue; + /// \brief Checks paging conditions for a UE in SearchSpace > 0 i.e pagingSearchSpace > 0 in its active BWP config. /// /// \param[in] pdcch_slot Slot at which the paging scheduler is called. @@ -163,7 +168,7 @@ class paging_scheduler /// List of notifications from upper layers containing Paging information. /// This is used only to avoid data race between threads. - slot_event_list new_paging_notifications; + paging_info_queue new_paging_notifications; /// Contains paging information of UEs yet to be scheduled. std::unordered_map paging_pending_ues; /// Lookup to keep track of scheduled paging UEs at a particular PDSCH time resource index. Index of \c diff --git a/lib/scheduler/common_scheduling/ra_scheduler.cpp b/lib/scheduler/common_scheduling/ra_scheduler.cpp index 2eab4b72a9..eb9ce518e6 100644 --- a/lib/scheduler/common_scheduling/ra_scheduler.cpp +++ b/lib/scheduler/common_scheduling/ra_scheduler.cpp @@ -97,6 +97,15 @@ class ra_scheduler::msg3_harq_timeout_notifier final : public harq_timeout_notif std::vector& pending_msg3s; }; +// (Implementation-defined) limit for maximum number of concurrent Msg3s. +static constexpr size_t MAX_NOF_MSG3 = 1024; + +// (Implementation-defined) limit for maximum number of pending RACH indications. +static constexpr size_t RACH_IND_QUEUE_SIZE = MAX_PRACH_OCCASIONS_PER_SLOT * 2; + +// (Implementation-defined) limit for maximum number of pending CRC indications. +static constexpr size_t CRC_IND_QUEUE_SIZE = MAX_PUCCH_PDUS_PER_SLOT * 2; + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ra_scheduler::ra_scheduler(const scheduler_ra_expert_config& sched_cfg_, @@ -117,6 +126,8 @@ ra_scheduler::ra_scheduler(const scheduler_ra_expert_config& sched_cfg_, cell_cfg.ul_cfg_common.init_ul_bwp.rach_cfg_common->rach_cfg_generic.prach_config_index) .format)), msg3_harqs(MAX_NOF_MSG3, 1, std::make_unique(pending_msg3s)), + pending_rachs(RACH_IND_QUEUE_SIZE), + pending_crcs(CRC_IND_QUEUE_SIZE), pending_msg3s(MAX_NOF_MSG3) { // Precompute RAR PDSCH and DCI PDUs. @@ -213,7 +224,10 @@ void ra_scheduler::precompute_msg3_pdus() void ra_scheduler::handle_rach_indication(const rach_indication_message& msg) { // Buffer detected RACHs to be handled in next slot. - pending_rachs.push(msg); + if (not pending_rachs.try_push(msg)) { + logger.warning( + "cell={}: Discarding RACH indication for slot={}. Cause: Event queue is full", msg.cell_index, msg.slot_rx); + } } void ra_scheduler::handle_rach_indication_impl(const rach_indication_message& msg) @@ -289,16 +303,17 @@ void ra_scheduler::handle_rach_indication_impl(const rach_indication_message& ms void ra_scheduler::handle_crc_indication(const ul_crc_indication& crc_ind) { - pending_crcs.push(crc_ind); + if (not pending_crcs.try_push(crc_ind)) { + logger.warning( + "cell={}: CRC indication for slot={} discarded. Cause: Event queue is full", crc_ind.cell_index, crc_ind.sl_rx); + } } void ra_scheduler::handle_pending_crc_indications_impl(cell_resource_allocator& res_alloc) { // Pop pending CRCs and process them. - pending_crcs.slot_indication(); - const span new_crc_inds = pending_crcs.get_events(); - - for (const ul_crc_indication& crc_ind : new_crc_inds) { + ul_crc_indication crc_ind; + while (pending_crcs.try_pop(crc_ind)) { for (const ul_crc_pdu_indication& crc : crc_ind.crcs) { srsran_assert(crc.ue_index == INVALID_DU_UE_INDEX, "Msg3 HARQ CRCs cannot have a ue index assigned yet"); auto& pending_msg3 = pending_msg3s[to_value(crc.rnti) % MAX_NOF_MSG3]; @@ -349,9 +364,8 @@ void ra_scheduler::run_slot(cell_resource_allocator& res_alloc) handle_pending_crc_indications_impl(res_alloc); // Pop pending RACHs and process them. - pending_rachs.slot_indication(); - const span new_rachs = pending_rachs.get_events(); - for (const rach_indication_message& rach : new_rachs) { + rach_indication_message rach; + while (pending_rachs.try_pop(rach)) { handle_rach_indication_impl(rach); } diff --git a/lib/scheduler/common_scheduling/ra_scheduler.h b/lib/scheduler/common_scheduling/ra_scheduler.h index 00210c8b2c..f3e8ea2b4b 100644 --- a/lib/scheduler/common_scheduling/ra_scheduler.h +++ b/lib/scheduler/common_scheduling/ra_scheduler.h @@ -26,7 +26,8 @@ #include "../cell/resource_grid.h" #include "../pdcch_scheduling/pdcch_resource_allocator.h" #include "../support/prbs_calculator.h" -#include "../support/slot_event_list.h" +#include "srsran/adt/concurrent_queue.h" +#include "srsran/adt/mpmc_queue.h" #include "srsran/ran/prach/prach_configuration.h" #include "srsran/scheduler/config/scheduler_expert_config.h" #include "srsran/srslog/srslog.h" @@ -53,9 +54,6 @@ uint16_t get_ra_rnti(unsigned slot_index, unsigned symbol_index, unsigned freque /// Scheduler for PRACH occasions, RAR PDSCHs and Msg3 PUSCH grants. class ra_scheduler { - /// Implementation-defined limit for maximum number of concurrent Msg3s. - static constexpr size_t MAX_NOF_MSG3 = 1024; - public: explicit ra_scheduler(const scheduler_ra_expert_config& sched_cfg_, const cell_configuration& cfg_, @@ -97,6 +95,13 @@ class ra_scheduler crb_interval crbs; }; + using rach_indication_queue = concurrent_queue; + using crc_indication_queue = concurrent_queue; + const bwp_configuration& get_dl_bwp_cfg() const { return cell_cfg.dl_cfg_common.init_dl_bwp.generic_params; } const pdsch_config_common& get_pdsch_cfg() const { return cell_cfg.dl_cfg_common.init_dl_bwp.pdsch_common; } const bwp_configuration& get_ul_bwp_cfg() const { return cell_cfg.ul_cfg_common.init_ul_bwp.generic_params; } @@ -185,11 +190,11 @@ class ra_scheduler sch_mcs_description msg3_mcs_config; // variables - cell_harq_manager msg3_harqs; - slot_event_list pending_rachs; - slot_event_list pending_crcs; - std::deque pending_rars; - std::vector pending_msg3s; + cell_harq_manager msg3_harqs; + rach_indication_queue pending_rachs; + crc_indication_queue pending_crcs; + std::deque pending_rars; + std::vector pending_msg3s; }; } // namespace srsran diff --git a/lib/scheduler/config/sched_config_manager.cpp b/lib/scheduler/config/sched_config_manager.cpp index 88ee6e7de2..2161021636 100644 --- a/lib/scheduler/config/sched_config_manager.cpp +++ b/lib/scheduler/config/sched_config_manager.cpp @@ -208,11 +208,7 @@ void sched_config_manager::handle_ue_config_complete(du_ue_index_t ue_index, std cell_metrics_handler& cell_metrics = metrics_handler.at(next_cfg->pcell_common_cfg().cell_index); if (ue_cfg_list[ue_index] == nullptr) { // UE creation case. - cell_metrics.handle_ue_creation(ue_index, - next_cfg->crnti, - next_cfg->pcell_common_cfg().pci, - next_cfg->pcell_common_cfg().nof_dl_prbs, - next_cfg->pcell_common_cfg().nof_slots_per_frame); + cell_metrics.handle_ue_creation(ue_index, next_cfg->crnti, next_cfg->pcell_common_cfg().pci); } else { // Reconfiguration case. cell_metrics.handle_ue_reconfiguration(ue_index); diff --git a/lib/scheduler/config/scheduler_ue_config_validator.cpp b/lib/scheduler/config/scheduler_ue_config_validator.cpp index f5229e049b..f5098f98f2 100644 --- a/lib/scheduler/config/scheduler_ue_config_validator.cpp +++ b/lib/scheduler/config/scheduler_ue_config_validator.cpp @@ -40,8 +40,10 @@ srsran::config_validators::validate_sched_ue_creation_request_message(const sche HANDLE_ERROR(validate_pdsch_cfg(cell.serv_cell_cfg)); - if (cell.serv_cell_cfg.ul_config.has_value() and cell.serv_cell_cfg.ul_config->init_ul_bwp.pucch_cfg.has_value()) { + if (cell.serv_cell_cfg.ul_config.has_value() and cell.serv_cell_cfg.ul_config->init_ul_bwp.pucch_cfg.has_value() and + cell.serv_cell_cfg.ul_config->init_ul_bwp.srs_cfg.has_value()) { HANDLE_ERROR(validate_pucch_cfg(cell.serv_cell_cfg, cell_cfg.dl_carrier.nof_ant)); + HANDLE_ERROR(validate_srs_cfg(cell.serv_cell_cfg)); } HANDLE_ERROR(validate_csi_meas_cfg(cell.serv_cell_cfg, cell_cfg.tdd_cfg_common)); diff --git a/lib/scheduler/config/serving_cell_config_validator.cpp b/lib/scheduler/config/serving_cell_config_validator.cpp index 2c68ac8f4d..31d54783e1 100644 --- a/lib/scheduler/config/serving_cell_config_validator.cpp +++ b/lib/scheduler/config/serving_cell_config_validator.cpp @@ -412,6 +412,61 @@ validator_result srsran::config_validators::validate_pucch_cfg(const serving_cel return {}; } +validator_result srsran::config_validators::validate_srs_cfg(const serving_cell_config& ue_cell_cfg) +{ + VERIFY(ue_cell_cfg.ul_config.has_value() and ue_cell_cfg.ul_config.value().init_ul_bwp.srs_cfg.has_value(), + "Missing configuration for uplinkConfig or srs-Config in spCellConfig"); + + const auto& srs_cfg = ue_cell_cfg.ul_config.value().init_ul_bwp.srs_cfg.value(); + + VERIFY(srs_cfg.srs_res_set_list.size() == 1 and srs_cfg.srs_res_set_list.front().id == srs_config::MIN_SRS_RES_SET_ID, + "The SRS resource set list is expected to have size 1 and its only set is expected to have ID 0"); + VERIFY(srs_cfg.srs_res_set_list.front().srs_res_id_list.size() == 1, + "The SRS resource list of the SRS resource set ID 0 is expected to have size 1"); + VERIFY(srs_cfg.srs_res_list.size() == 1 and srs_cfg.srs_res_list.front().id.ue_res_id == srs_config::MIN_SRS_RES_ID, + "The SRS resource list is expected to have size 1 and its only resource is expected to have ID 0"); + VERIFY(srs_cfg.srs_res_set_list.front().srs_res_id_list.front() == srs_cfg.srs_res_list.front().id.ue_res_id, + "The SRS resource set ID 0's resource should point to the SRS resource ID 0"); + const auto& srs_res_set = srs_cfg.srs_res_set_list.front(); + VERIFY(srs_res_set.srs_res_set_usage == srs_config::srs_resource_set::usage::codebook, + "Only SRS resource set usage \"codebook\" is supported"); + + const auto& srs_res = srs_cfg.srs_res_list.front(); + VERIFY( + (std::holds_alternative(srs_res_set.res_type) and + srs_res.res_type == srs_resource_type::aperiodic) or + (std::holds_alternative(srs_res_set.res_type) and + srs_res.res_type == srs_resource_type::periodic) or + (std::holds_alternative(srs_res_set.res_type) and + srs_res.res_type == srs_resource_type::semi_persistent), + "The SRS resource set and its resource should be of the same type"); + if (srs_res.res_type == srs_resource_type::periodic) { + VERIFY(srs_res.periodicity_and_offset.has_value(), + "The SRS resource should have a periodicity and offset when the resource type is periodic"); + VERIFY(srs_res.periodicity_and_offset.value().offset < + static_cast(srs_res.periodicity_and_offset.value().period), + "The SRS resource offset should be less than the periodicity"); + } + VERIFY(srs_res.tx_comb.tx_comb_offset < static_cast(srs_res.tx_comb.size), + "The SRS resource txCombOffset should be less than the TX comb size"); + const uint8_t max_tx_comb_cs = srs_res.tx_comb.size == tx_comb_size::n2 ? 7U : 11U; + VERIFY(srs_res.tx_comb.tx_comb_cyclic_shift <= max_tx_comb_cs, + "The SRS resource tx_comb_cyclic_shift should be less than or equal to 7 for TX comb size n2 and TX comb size " + "11 for n4"); + VERIFY(srs_res.res_mapping.rept_factor <= srs_res.res_mapping.nof_symb, + "The SRS resource repetition factor should be less than or equal to the number of symbols"); + // NOTE: The parameter \c start_pos indicates the distance from the last symbol of the slot. The actual starting + // OFDM symbol is NOF_OFDM_SYM_PER_SLOT_NORMAL_CP - (srs_res.res_mapping.start_pos + 1). + // The final symbol = + // NOF_OFDM_SYM_PER_SLOT_NORMAL_CP - (srs_res.res_mapping.start_pos + 1) + srs_res.res_mapping.nof_symb + // needs to be less than or equal to NOF_OFDM_SYM_PER_SLOT_NORMAL_CP. + VERIFY(static_cast(srs_res.res_mapping.nof_symb) <= srs_res.res_mapping.start_pos + 1, + "The SRS resource number of symbols and start position should be such that the SRS resource fits within the " + "slot symbols"); + + return {}; +} + validator_result srsran::config_validators::validate_nzp_csi_rs_list(span nzp_csi_rs_res_list, const std::optional& tdd_cfg_common) diff --git a/lib/scheduler/logging/scheduler_metric_handler.cpp b/lib/scheduler/logging/scheduler_metric_handler.cpp index de303bfd0c..f3d8648c1b 100644 --- a/lib/scheduler/logging/scheduler_metric_handler.cpp +++ b/lib/scheduler/logging/scheduler_metric_handler.cpp @@ -26,27 +26,22 @@ using namespace srsran; -cell_metrics_handler::cell_metrics_handler(msecs metrics_report_period, scheduler_metrics_notifier& notifier_) : - notifier(notifier_), report_period(metrics_report_period) +cell_metrics_handler::cell_metrics_handler(msecs metrics_report_period, + scheduler_metrics_notifier& notifier_, + const cell_configuration& cell_cfg_) : + notifier(notifier_), report_period(metrics_report_period), cell_cfg(cell_cfg_) { next_report.ue_metrics.reserve(MAX_NOF_DU_UES); next_report.events.reserve(MAX_NOF_DU_UES); } -void cell_metrics_handler::handle_ue_creation(du_ue_index_t ue_index, - rnti_t rnti, - pci_t pcell_pci, - unsigned num_prbs, - unsigned num_slots_per_frame) +void cell_metrics_handler::handle_ue_creation(du_ue_index_t ue_index, rnti_t rnti, pci_t pcell_pci) { ues.emplace(ue_index); - ues[ue_index].rnti = rnti; - ues[ue_index].ue_index = ue_index; - ues[ue_index].pci = pcell_pci; - ues[ue_index].nof_prbs = num_prbs; - ues[ue_index].num_slots_per_frame = num_slots_per_frame; + ues[ue_index].rnti = rnti; + ues[ue_index].ue_index = ue_index; + ues[ue_index].pci = pcell_pci; rnti_to_ue_index_lookup.emplace(rnti, ue_index); - nof_prbs = num_prbs; next_report.events.push_back(scheduler_cell_event{last_slot_tx, rnti, scheduler_cell_event::event_type::ue_add}); } @@ -226,7 +221,7 @@ void cell_metrics_handler::report_metrics() next_report.nof_error_indications = error_indication_counter; next_report.average_decision_latency = decision_latency_sum / report_period_slots; next_report.latency_histogram = decision_latency_hist; - next_report.nof_prbs = nof_prbs; + next_report.nof_prbs = cell_cfg.nof_dl_prbs; // TODO: to be removed from the report. next_report.nof_dl_slots = nof_dl_slots; next_report.nof_ul_slots = nof_ul_slots; @@ -265,8 +260,8 @@ void cell_metrics_handler::handle_slot_result(const sched_result& slot_res } if (dl_grant.pdsch_cfg.rbs.is_type0()) { u.data.tot_dl_prbs_used += full_dl_slot ? convert_rbgs_to_prbs(dl_grant.pdsch_cfg.rbs.type0(), - {0, u.nof_prbs}, - get_nominal_rbg_size(u.nof_prbs, true)) + {0, cell_cfg.nof_dl_prbs}, + get_nominal_rbg_size(cell_cfg.nof_dl_prbs, true)) .count() : 0; } else if (dl_grant.pdsch_cfg.rbs.is_type1()) { @@ -283,8 +278,8 @@ void cell_metrics_handler::handle_slot_result(const sched_result& slot_res if (ul_grant.pusch_cfg.rbs.is_type0()) { ues[it->second].data.tot_ul_prbs_used += full_ul_slot ? convert_rbgs_to_prbs(ul_grant.pusch_cfg.rbs.type0(), - {0, ues[it->second].nof_prbs}, - get_nominal_rbg_size(ues[it->second].nof_prbs, true)) + {0, cell_cfg.nof_dl_prbs}, + get_nominal_rbg_size(cell_cfg.nof_dl_prbs, true)) .count() : 0; } else if (ul_grant.pusch_cfg.rbs.is_type1()) { @@ -308,7 +303,7 @@ void cell_metrics_handler::handle_slot_result(const sched_result& slot_res void cell_metrics_handler::handle_ul_delay(du_ue_index_t ue_index, double delay) { if (ues.contains(ue_index)) { - ues[ue_index].data.sum_ul_delay_ms += delay * (10 / (ues[ue_index].num_slots_per_frame)); + ues[ue_index].data.sum_ul_delay_ms += delay; } } @@ -393,7 +388,7 @@ cell_metrics_handler* scheduler_metrics_handler::add_cell(const cell_configurati return nullptr; } - cells.emplace(cell_cfg.cell_index, report_period, notifier); + cells.emplace(cell_cfg.cell_index, report_period, notifier, cell_cfg); return &cells[cell_cfg.cell_index]; } diff --git a/lib/scheduler/logging/scheduler_metrics_handler.h b/lib/scheduler/logging/scheduler_metrics_handler.h index c7942e5949..6e33383e7b 100644 --- a/lib/scheduler/logging/scheduler_metrics_handler.h +++ b/lib/scheduler/logging/scheduler_metrics_handler.h @@ -73,8 +73,6 @@ class cell_metrics_handler final : public harq_timeout_handler, public sched_met ue_metric_context() {} pci_t pci; - unsigned nof_prbs; - unsigned num_slots_per_frame; du_ue_index_t ue_index; rnti_t rnti; unsigned last_bsr = 0; @@ -89,7 +87,8 @@ class cell_metrics_handler final : public harq_timeout_handler, public sched_met scheduler_metrics_notifier& notifier; const std::chrono::milliseconds report_period; - // Derived value. + const cell_configuration& cell_cfg; + /// Derived value. unsigned report_period_slots = 0; slot_point last_slot_tx; @@ -101,9 +100,6 @@ class cell_metrics_handler final : public harq_timeout_handler, public sched_met slotted_id_table ues; std::unordered_map rnti_to_ue_index_lookup; - /// Number of the cell PRBs. - unsigned nof_prbs = 0; - /// Number of full downlink slots. unsigned nof_dl_slots = 0; @@ -118,14 +114,12 @@ class cell_metrics_handler final : public harq_timeout_handler, public sched_met public: /// \brief Creates a scheduler UE metrics handler for a given cell. In case the metrics_report_period is zero, /// no metrics are reported. - explicit cell_metrics_handler(msecs metrics_report_period, scheduler_metrics_notifier& notifier); + explicit cell_metrics_handler(msecs metrics_report_period, + scheduler_metrics_notifier& notifier, + const cell_configuration& cell_cfg_); /// \brief Register creation of a UE. - void handle_ue_creation(du_ue_index_t ue_index, - rnti_t rnti, - pci_t pcell_pci, - unsigned num_prbs, - unsigned num_slots_per_frame) override; + void handle_ue_creation(du_ue_index_t ue_index, rnti_t rnti, pci_t pcell_pci) override; /// \brief Register UE reconfiguration. void handle_ue_reconfiguration(du_ue_index_t ue_index) override; diff --git a/lib/scheduler/logging/scheduler_metrics_ue_configurator.h b/lib/scheduler/logging/scheduler_metrics_ue_configurator.h index 7e19246c73..30336510c5 100644 --- a/lib/scheduler/logging/scheduler_metrics_ue_configurator.h +++ b/lib/scheduler/logging/scheduler_metrics_ue_configurator.h @@ -35,11 +35,7 @@ class sched_metrics_ue_configurator virtual ~sched_metrics_ue_configurator() = default; /// Adds a new UE to the reported metrics. - virtual void handle_ue_creation(du_ue_index_t ue_index, - rnti_t rnti, - pci_t pcell_pci, - unsigned num_prbs, - unsigned num_slots_per_frame) = 0; + virtual void handle_ue_creation(du_ue_index_t ue_index, rnti_t rnti, pci_t pcell_pci) = 0; /// Handle a reconfiguration of an existing UE. virtual void handle_ue_reconfiguration(du_ue_index_t ue_index) = 0; diff --git a/lib/scheduler/logging/scheduler_result_logger.cpp b/lib/scheduler/logging/scheduler_result_logger.cpp index 8eca0713aa..4f8ee419bd 100644 --- a/lib/scheduler/logging/scheduler_result_logger.cpp +++ b/lib/scheduler/logging/scheduler_result_logger.cpp @@ -300,6 +300,19 @@ void scheduler_result_logger::log_debug(const sched_result& result, std::chrono: } } + for (const auto& srs : result.ul.srss) { + fmt::format_to(fmtbuf, + "\n- SRS: c-rnti={} symb={} tx-comb=(n{} o={} cs={}) c_srs={} f_sh={} seq_id={}", + srs.crnti, + srs.symbols, + static_cast(srs.tx_comb), + srs.comb_offset, + srs.cyclic_shift, + srs.config_index, + srs.freq_shift, + srs.sequence_id); + } + if (fmtbuf.size() > 0) { const unsigned nof_pdschs = result.dl.paging_grants.size() + result.dl.rar_grants.size() + result.dl.ue_grants.size() + result.dl.bc.sibs.size(); diff --git a/lib/scheduler/policy/scheduler_time_rr.cpp b/lib/scheduler/policy/scheduler_time_rr.cpp index a81669c20c..5b6d6dee5c 100644 --- a/lib/scheduler/policy/scheduler_time_rr.cpp +++ b/lib/scheduler/policy/scheduler_time_rr.cpp @@ -66,6 +66,9 @@ static unsigned get_max_ues_to_be_sched(const slice_ue_repository& ues, bool is_ } } } + if (nof_ue_with_new_tx == 0) { + return 0; + } return scheduler_alloc_limits_lookup[lookup_idx].nof_ues_to_be_scheduled_per_slot; } @@ -82,12 +85,11 @@ static unsigned compute_max_nof_rbs_per_ue_per_slot(const slice_ue_repository& const scheduler_ue_expert_config& expert_cfg, unsigned slice_max_rbs) { - if (ues.empty()) { + unsigned nof_ues_to_be_scheduled_per_slot = get_max_ues_to_be_sched(ues, is_dl); + if (nof_ues_to_be_scheduled_per_slot == 0) { return 0; } - unsigned nof_ues_to_be_scheduled_per_slot = get_max_ues_to_be_sched(ues, is_dl); - // > Apply limits if passed to scheduler. if (is_dl) { nof_ues_to_be_scheduled_per_slot = std::min(expert_cfg.max_pdschs_per_slot, nof_ues_to_be_scheduled_per_slot); diff --git a/lib/scheduler/policy/scheduler_time_rr.h b/lib/scheduler/policy/scheduler_time_rr.h index 6f89ab6056..3c060b0cdd 100644 --- a/lib/scheduler/policy/scheduler_time_rr.h +++ b/lib/scheduler/policy/scheduler_time_rr.h @@ -45,7 +45,7 @@ class scheduler_time_rr : public scheduler_policy srslog::basic_logger& logger; du_ue_index_t next_dl_ue_index, next_ul_ue_index; - const scheduler_ue_expert_config& expert_cfg; + const scheduler_ue_expert_config expert_cfg; }; } // namespace srsran diff --git a/lib/scheduler/slicing/ran_slice_instance.cpp b/lib/scheduler/slicing/ran_slice_instance.cpp index 548b0aa75a..c3e438aa74 100644 --- a/lib/scheduler/slicing/ran_slice_instance.cpp +++ b/lib/scheduler/slicing/ran_slice_instance.cpp @@ -50,7 +50,7 @@ void ran_slice_instance::slot_indication(slot_point slot_tx) last_pdsch_alloc_slot.clear(); } if (last_pusch_alloc_slot.valid() and slot_tx > last_pusch_alloc_slot + MAX_SLOTS_SINCE_LAST_PXSCH) { - last_pdsch_alloc_slot.clear(); + last_pusch_alloc_slot.clear(); } } diff --git a/lib/scheduler/slicing/ran_slice_instance.h b/lib/scheduler/slicing/ran_slice_instance.h index 7876f94af3..c5dec7e17d 100644 --- a/lib/scheduler/slicing/ran_slice_instance.h +++ b/lib/scheduler/slicing/ran_slice_instance.h @@ -35,7 +35,8 @@ namespace srsran { class ran_slice_instance { // Const for the max difference in number of slots between the current slot and the last allocation slot. - static constexpr unsigned MAX_SLOTS_SINCE_LAST_PXSCH = 512; + // This limit ensures lack of ambiguity in the slot point comparison + static constexpr unsigned MAX_SLOTS_SINCE_LAST_PXSCH = 256; public: ran_slice_instance(ran_slice_id_t id_, const cell_configuration& cell_cfg_, const slice_rrm_policy_config& cfg_); diff --git a/lib/scheduler/slicing/slice_scheduler.cpp b/lib/scheduler/slicing/slice_scheduler.cpp index cb850cdb1a..c6bda26201 100644 --- a/lib/scheduler/slicing/slice_scheduler.cpp +++ b/lib/scheduler/slicing/slice_scheduler.cpp @@ -326,7 +326,8 @@ slice_scheduler::priority_type slice_scheduler::ran_slice_sched_context::get_pri // Priority when slice still needs to reach its minimum RB ratio agreement. static constexpr priority_type high_prio = 0x2U; static constexpr priority_type slice_prio_bitsize = 4U; - static constexpr priority_type delay_bitsize = 8U; + static constexpr priority_type delay_prio_bitsize = 8U; + static constexpr priority_type delay_prio_bitmask = (1U << delay_prio_bitsize) - 1U; static constexpr priority_type rr_bitsize = 8U; unsigned rb_count = is_dl ? inst.pdsch_rb_count : inst.nof_pusch_rbs_allocated(pxsch_slot); @@ -336,7 +337,7 @@ slice_scheduler::priority_type slice_scheduler::ran_slice_sched_context::get_pri } // Prioritize closer slots over slots further away into the future. This is relevant for UL-heavy cases. - unsigned slot_dist = 0xfU - ((pxsch_slot - pdcch_slot) & 0xfU); + unsigned slot_dist = 0xfU - std::min(static_cast(pxsch_slot - pdcch_slot), 0xfU); // In case minRB > 0 and minimum RB ratio agreement is not yet reached, we give it a higher priority. priority_type slice_prio = @@ -345,12 +346,11 @@ slice_scheduler::priority_type slice_scheduler::ran_slice_sched_context::get_pri // Increase priorities of slices that have not been scheduled for a long time. priority_type delay_prio = is_dl ? inst.nof_slots_since_last_pdsch(pxsch_slot) : inst.nof_slots_since_last_pusch(pxsch_slot); - delay_prio = delay_prio & ((1U << delay_bitsize) - 1U); + delay_prio = std::min(delay_prio, delay_prio_bitmask); // Round-robin across slices with the same slice and delay priorities. - priority_type rr_prio = (current_slot_count % nof_slices) == inst.id.value() ? 1 : 0; - rr_prio = rr_prio & ((1U << rr_bitsize) - 1U); + priority_type rr_prio = ((inst.id.value() + current_slot_count) % nof_slices) & ((1U << rr_bitsize) - 1U); - return (slot_dist << (delay_bitsize + rr_bitsize + slice_prio_bitsize)) + - (slice_prio << (delay_bitsize + rr_bitsize)) + (delay_prio << rr_bitsize) + rr_prio; + return (slot_dist << (delay_prio_bitsize + rr_bitsize + slice_prio_bitsize)) + + (slice_prio << (delay_prio_bitsize + rr_bitsize)) + (delay_prio << rr_bitsize) + rr_prio; } diff --git a/lib/scheduler/srs/srs_scheduler.h b/lib/scheduler/srs/srs_scheduler.h new file mode 100644 index 0000000000..55539a0092 --- /dev/null +++ b/lib/scheduler/srs/srs_scheduler.h @@ -0,0 +1,54 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN 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. + * + * srsRAN 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/. + * + */ + +#pragma once + +namespace srsran { + +struct cell_resource_allocator; +class ue_cell_configuration; + +/// SRS scheduling interface, which handles the scheduling of SRS opportunities. +class srs_scheduler +{ +public: + virtual ~srs_scheduler() = default; + + /// Schedules the SRS occasions. + /// \param[out,in] res_alloc struct with scheduling results. + virtual void run_slot(cell_resource_allocator& res_alloc) = 0; + + /// Adds a UE to the internal list of UEs to be scheduled. + /// \param[in] ue_cfg dedicated configuration of the UE to be added. + virtual void add_ue(const ue_cell_configuration& ue_cfg) = 0; + + /// Removes the UE from the internal list of UEs to be scheduled. + /// \param[in] ue_cfg UE dedicated configuration of the UE to be removed. + virtual void rem_ue(const ue_cell_configuration& ue_cfg) = 0; + + /// Updates the SRS configuration of this UE, if there are any changes w.r.t. the previous configuration. + /// \param[in] ue_cfg New UE dedicated configuration of the UE to be reconfigured. + /// \param[in] ue_cfg Old UE dedicated configuration of the UE to be reconfigured. + virtual void reconf_ue(const ue_cell_configuration& new_ue_cfg, const ue_cell_configuration& old_ue_cfg) = 0; +}; + +} // namespace srsran diff --git a/lib/scheduler/srs/srs_scheduler_impl.cpp b/lib/scheduler/srs/srs_scheduler_impl.cpp new file mode 100644 index 0000000000..43fcf0162b --- /dev/null +++ b/lib/scheduler/srs/srs_scheduler_impl.cpp @@ -0,0 +1,366 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN 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. + * + * srsRAN 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 "srs_scheduler_impl.h" +#include "../cell/resource_grid.h" +#include "srsran/srslog/srslog.h" + +using namespace srsran; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +srs_scheduler_impl::srs_scheduler_impl(const cell_configuration& cell_cfg_, ue_repository& ues_) : + cell_cfg(cell_cfg_), ues(ues_), logger(srslog::fetch_basic_logger("SCHED")) +{ + // Max size of the SRS resource slot wheel, dimensioned based on the maximum SRS periods. + periodic_srs_slot_wheel.resize(static_cast(srs_periodicity::sl2560)); + + // Pre-reserve space for the UEs that will be added. + updated_ues.reserve(MAX_NOF_DU_UES); +} + +srs_scheduler_impl::~srs_scheduler_impl() = default; + +///////////////////// Public functions //////////////////////////// + +void srs_scheduler_impl::run_slot(cell_resource_allocator& cell_alloc) +{ + // Initial allocation: we allocate opportunities all over the grid. + schedule_updated_ues_srs(cell_alloc); + + // Only allocate in the farthest slot in the grid. The allocation in the first slots of the grid has been completed by + // the previous function. + schedule_slot_srs(cell_alloc[cell_alloc.max_ul_slot_alloc_delay]); +} + +void srs_scheduler_impl::add_ue(const ue_cell_configuration& ue_cfg) +{ + if (not ue_cfg.cfg_dedicated().ul_config.has_value() or + not ue_cfg.cfg_dedicated().ul_config.value().init_ul_bwp.srs_cfg.has_value()) { + return; + } + const srs_config& srs_cfg = ue_cfg.cfg_dedicated().ul_config.value().init_ul_bwp.srs_cfg.value(); + + auto get_srs_res_with_id = [&srs_cfg](unsigned srs_res_id) { + return std::find_if( + srs_cfg.srs_res_list.begin(), + srs_cfg.srs_res_list.end(), + [srs_res_id](const srs_config::srs_resource& srs_res) { return srs_res.id.ue_res_id == srs_res_id; }); + }; + + for (const auto& srs_res_set : + ue_cfg.cfg_dedicated().ul_config.value().init_ul_bwp.srs_cfg.value().srs_res_set_list) { + // This scheduler is only for periodic SRS resources. + if (not std::holds_alternative(srs_res_set.res_type)) { + continue; + } + + for (const auto& srs_res_id : srs_res_set.srs_res_id_list) { + const auto* srs_res = get_srs_res_with_id(srs_res_id); + + if (srs_res == srs_cfg.srs_res_list.end()) { + logger.error("rnti={} SRS resource set id={} has an invalid SRS resource ID {}", + ue_cfg.crnti, + srs_res_set.id, + srs_res_id); + continue; + } + // We assume that a periodic SRS resource set only contains periodic SRS resources. This has been checked in the + // scheduler configuration validator. + srsran_sanity_check(srs_res->periodicity_and_offset.has_value(), + "rnti={}: Periodicity and offset not set for SRS resource ID={}", + ue_cfg.crnti, + srs_res->id.ue_res_id); + add_resource(ue_cfg.crnti, + srs_res->periodicity_and_offset.value().period, + srs_res->periodicity_and_offset.value().offset, + srs_res->id.ue_res_id); + } + } + + // Register the UE in the list of recently configured UEs. + updated_ues.push_back(ue_cfg.crnti); +} + +void srs_scheduler_impl::rem_ue(const ue_cell_configuration& ue_cfg) +{ + if (not ue_cfg.cfg_dedicated().ul_config.has_value() or + not ue_cfg.cfg_dedicated().ul_config.value().init_ul_bwp.srs_cfg.has_value()) { + return; + } + const srs_config& srs_cfg = ue_cfg.cfg_dedicated().ul_config.value().init_ul_bwp.srs_cfg.value(); + + auto get_srs_res_with_id = [&srs_cfg](unsigned srs_res_id) { + return std::find_if( + srs_cfg.srs_res_list.begin(), + srs_cfg.srs_res_list.end(), + [srs_res_id](const srs_config::srs_resource& srs_res) { return srs_res.id.ue_res_id == srs_res_id; }); + }; + + for (const auto& srs_res_set : + ue_cfg.cfg_dedicated().ul_config.value().init_ul_bwp.srs_cfg.value().srs_res_set_list) { + if (not std::holds_alternative(srs_res_set.res_type)) { + continue; + } + + for (const auto& srs_res_id : srs_res_set.srs_res_id_list) { + const auto* srs_res = get_srs_res_with_id(srs_res_id); + + if (srs_res == srs_cfg.srs_res_list.end()) { + logger.error("rnti={} SRS resource set id={} has an invalid SRS resource ID {}", + ue_cfg.crnti, + srs_res_set.id, + srs_res_id); + continue; + } + // We assume that a periodic SRS resource set only contains periodic SRS resources. This has been checked in the + // scheduler configuration validator. + srsran_sanity_check(srs_res->periodicity_and_offset.has_value(), + "rnti={}: Periodicity and offset not set for SRS resource ID={}", + ue_cfg.crnti, + srs_res->id.ue_res_id); + rem_resource(ue_cfg.crnti, + srs_res->periodicity_and_offset.value().period, + srs_res->periodicity_and_offset.value().offset, + srs_res->id.ue_res_id); + } + } +} + +void srs_scheduler_impl::reconf_ue(const ue_cell_configuration& new_ue_cfg, const ue_cell_configuration& old_ue_cfg) +{ + if (new_ue_cfg.cfg_dedicated().ul_config.has_value() and old_ue_cfg.cfg_dedicated().ul_config.has_value() and + new_ue_cfg.cfg_dedicated().ul_config.value().init_ul_bwp.srs_cfg.has_value() and + old_ue_cfg.cfg_dedicated().ul_config.value().init_ul_bwp.srs_cfg.has_value()) { + // Both old and new UE config have SRS config. + const auto& new_srs_cfg = new_ue_cfg.cfg_dedicated().ul_config.value().init_ul_bwp.srs_cfg.value(); + const auto& old_srs_cfg = old_ue_cfg.cfg_dedicated().ul_config.value().init_ul_bwp.srs_cfg.value(); + + if (new_srs_cfg.srs_res_set_list == old_srs_cfg.srs_res_set_list and + new_srs_cfg.srs_res_list == old_srs_cfg.srs_res_list) { + // Nothing changed. + return; + } + } + + rem_ue(old_ue_cfg); + add_ue(new_ue_cfg); +} + +///////////////////// Private functions //////////////////////////// + +void srs_scheduler_impl::schedule_slot_srs(srsran::cell_slot_resource_allocator& slot_alloc) +{ + // For the provided slot, check if there are any pending SRS resources to allocate, and allocate them. + auto& slot_srss = periodic_srs_slot_wheel[slot_alloc.slot.to_uint() % periodic_srs_slot_wheel.size()]; + for (auto srs_info_it : slot_srss) { + allocate_srs_opportunity(slot_alloc, srs_info_it); + } +} + +void srs_scheduler_impl::schedule_updated_ues_srs(cell_resource_allocator& cell_alloc) +{ + if (not updated_ues.empty()) { + // Schedule SRS up to the farthest slot. + for (unsigned n = 0; n != cell_alloc.max_ul_slot_alloc_delay; ++n) { + auto& slot_srss = periodic_srs_slot_wheel[(cell_alloc.slot_tx() + n).to_uint() % periodic_srs_slot_wheel.size()]; + + // For all the periodic SRS info element at this slot, allocate only those that belong to the UE updated_ues. + for (const periodic_srs_info& srs : slot_srss) { + const bool rnti_in_updated_ues = + std::find(updated_ues.begin(), updated_ues.end(), srs.rnti) != updated_ues.end(); + if (rnti_in_updated_ues) { + allocate_srs_opportunity(cell_alloc[n], srs); + } + } + } + + // Clear the list of updated UEs. + updated_ues.clear(); + } +} + +bool srs_scheduler_impl::allocate_srs_opportunity(cell_slot_resource_allocator& slot_alloc, + const periodic_srs_info& srs_opportunity) +{ + slot_point sl_srs = slot_alloc.slot; + + const ue_cell_configuration* ue_cfg = get_ue_cfg(srs_opportunity.rnti); + if (ue_cfg == nullptr) { + logger.error("cell={} c-rnti={}: UE for which SRS is being scheduled was not found", + cell_cfg.cell_index, + srs_opportunity.rnti); + return false; + } + + if (not ue_cfg->cell_cfg_common.is_ul_enabled(sl_srs)) { + logger.warning("cell={} c-rnti={}: slot={} for SRS resource id={} is being scheduled is not UL enabled", + cell_cfg.cell_index, + srs_opportunity.rnti, + sl_srs, + srs_opportunity.srs_res_id); + return false; + } + + if (slot_alloc.result.ul.srss.full()) { + logger.warning("cell={} c-rnti={}: SRS resource id={} cannot be allocated for slot={}. Cause: SRS list is full", + cell_cfg.cell_index, + srs_opportunity.rnti, + srs_opportunity.srs_res_id, + sl_srs); + return false; + } + + const auto srs_res_list = ue_cfg->cfg_dedicated().ul_config.value().init_ul_bwp.srs_cfg.value().srs_res_list; + + // Retrieve the SRS resource ID from the UE dedicated config. + const auto* res_it = std::find_if( + srs_res_list.begin(), srs_res_list.end(), [srs_opportunity](const srs_config::srs_resource& srs_res) { + return srs_res.id.ue_res_id == srs_opportunity.srs_res_id; + }); + + if (res_it == srs_res_list.end()) { + logger.warning("cell={} c-rnti={}: SRS resource id={} cannot be allocated for slot={}. Cause: SRS resource not " + "found in UE ded. config", + cell_cfg.cell_index, + srs_opportunity.rnti, + srs_opportunity.srs_res_id, + sl_srs); + return false; + } + + const unsigned nof_symbs_per_slot = get_nsymb_per_slot(cell_cfg.dl_cfg_common.init_dl_bwp.generic_params.cp); + const unsigned starting_symb = nof_symbs_per_slot - res_it->res_mapping.start_pos - 1; + slot_alloc.ul_res_grid.fill( + grant_info(cell_cfg.ul_cfg_common.init_ul_bwp.generic_params.scs, + ofdm_symbol_range{starting_symb, starting_symb + static_cast(res_it->res_mapping.nof_symb)}, + cell_cfg.ul_cfg_common.init_ul_bwp.generic_params.crbs)); + fill_srs_pdu(slot_alloc.result.ul.srss.emplace_back(), *res_it, *ue_cfg); + + return true; +} + +void srs_scheduler_impl::fill_srs_pdu(srs_info& pdu, + const srs_config::srs_resource& srs_res_cfg, + const ue_cell_configuration& ue_cfg) +{ + pdu.crnti = ue_cfg.crnti; + pdu.bwp_cfg = &cell_cfg.ul_cfg_common.init_ul_bwp.generic_params; + pdu.nof_antenna_ports = static_cast(srs_res_cfg.nof_ports); + + const unsigned nof_symbs_per_slot = get_nsymb_per_slot(cell_cfg.dl_cfg_common.init_dl_bwp.generic_params.cp); + const unsigned starting_symb = nof_symbs_per_slot - srs_res_cfg.res_mapping.start_pos - 1; + pdu.symbols.set(starting_symb, starting_symb + static_cast(srs_res_cfg.res_mapping.nof_symb)); + pdu.nof_repetitions = srs_res_cfg.res_mapping.rept_factor; + + pdu.config_index = srs_res_cfg.freq_hop.c_srs; + pdu.sequence_id = static_cast(srs_res_cfg.sequence_id); + pdu.bw_index = srs_res_cfg.freq_hop.b_srs; + pdu.tx_comb = srs_res_cfg.tx_comb.size; + pdu.comb_offset = srs_res_cfg.tx_comb.tx_comb_offset; + pdu.cyclic_shift = srs_res_cfg.tx_comb.tx_comb_cyclic_shift; + pdu.freq_position = srs_res_cfg.freq_domain_pos; + pdu.freq_shift = srs_res_cfg.freq_domain_shift; + pdu.freq_hopping = srs_res_cfg.freq_hop.b_hop; + pdu.group_or_seq_hopping = srs_res_cfg.grp_or_seq_hop; + pdu.resource_type = srs_res_cfg.res_type; + + pdu.t_srs_period = srs_res_cfg.periodicity_and_offset.value().period; + pdu.t_offset = srs_res_cfg.periodicity_and_offset.value().offset; +} + +void srs_scheduler_impl::add_resource(rnti_t crnti, + srs_periodicity res_period, + unsigned res_offset, + srs_config::srs_res_id res_id) +{ + // Add UE-SRS resource element for each offset in the periodic SRS slot wheel. + auto srs_period = static_cast(res_period); + for (unsigned wheel_offset = res_offset, wheel_size = periodic_srs_slot_wheel.size(); wheel_offset < wheel_size; + wheel_offset += srs_period) { + auto& slot_wheel = periodic_srs_slot_wheel[wheel_offset]; + + // Check if the UE is already in the slot wheel. + auto* it = std::find_if(slot_wheel.begin(), slot_wheel.end(), [crnti, res_id](const auto& r) { + return r.rnti == crnti and r.srs_res_id == res_id; + }); + + if (it == slot_wheel.end()) { + // New UE-SRS resource: create a new element in the list of SRS opportunities. + slot_wheel.push_back(periodic_srs_info{crnti, res_id}); + } else { + logger.error("rnti={}: SRS resource id={} with period={} and offset={} already exists in the SRS slot wheel", + crnti, + res_id, + res_period); + } + } +} + +void srs_scheduler_impl::rem_resource(rnti_t crnti, + srs_periodicity res_period, + unsigned res_offset, + srs_config::srs_res_id res_id) +{ + // For each offset in the periodic SRS slot wheel. + auto srs_period = static_cast(res_period); + for (unsigned wheel_offset = res_offset, wheel_size = periodic_srs_slot_wheel.size(); wheel_offset < wheel_size; + wheel_offset += srs_period) { + auto& slot_wheel = periodic_srs_slot_wheel[wheel_offset]; + + // Check if the UE-SRS resource element is still in the slot wheel. + auto* it = std::find_if(slot_wheel.begin(), slot_wheel.end(), [crnti, res_id](const auto& r) { + return r.rnti == crnti and r.srs_res_id == res_id; + }); + + if (it != slot_wheel.end()) { + // Move resource to last position and delete it to avoid O(N) removal. + if (it != slot_wheel.end() - 1) { + auto* last_it = slot_wheel.end() - 1; + std::swap(*it, *last_it); + } + slot_wheel.pop_back(); + + } else { + logger.error( + "rnti={}: no SRS resource id={} with period={} and offset={} found in the SRS slot wheel during UE removal", + crnti, + res_id, + res_period, + res_offset); + } + } +} + +///////////////////// Helper functions //////////////////////////// + +const ue_cell_configuration* srs_scheduler_impl::get_ue_cfg(rnti_t rnti) const +{ + auto* u = ues.find_by_rnti(rnti); + if (u != nullptr) { + auto* ue_cc = u->find_cell(cell_cfg.cell_index); + if (ue_cc != nullptr) { + return &ue_cc->cfg(); + } + } + return nullptr; +} diff --git a/lib/scheduler/srs/srs_scheduler_impl.h b/lib/scheduler/srs/srs_scheduler_impl.h new file mode 100644 index 0000000000..0ab9399dfb --- /dev/null +++ b/lib/scheduler/srs/srs_scheduler_impl.h @@ -0,0 +1,85 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN 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. + * + * srsRAN 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/. + * + */ + +#pragma once + +#include "../ue_scheduling/ue_repository.h" +#include "srs_scheduler.h" +#include "srsran/ran/srs/srs_configuration.h" + +namespace srsran { + +struct cell_resource_allocator; +struct cell_slot_resource_allocator; + +class srs_scheduler_impl : public srs_scheduler +{ +public: + explicit srs_scheduler_impl(const cell_configuration& cell_cfg_, ue_repository& ues_); + + ~srs_scheduler_impl() override; + + void run_slot(cell_resource_allocator& res_alloc) override; + + void add_ue(const ue_cell_configuration& ue_cfg) override; + + void rem_ue(const ue_cell_configuration& ue_cfg) override; + + void reconf_ue(const ue_cell_configuration& new_ue_cfg, const ue_cell_configuration& old_ue_cfg) override; + +private: + /// Information on currently configured SRS resources and corresponding UEs to be scheduled periodically. + struct periodic_srs_info { + rnti_t rnti = rnti_t::INVALID_RNTI; + srs_config::srs_res_id srs_res_id = srs_config::srs_res_id::MAX_NOF_SRS_RES; + }; + + // Helper to fetch a UE cell config. + const ue_cell_configuration* get_ue_cfg(rnti_t rnti) const; + // Helper that schedules the SRS for a given slot. + void schedule_slot_srs(cell_slot_resource_allocator& slot_alloc); + // Helper that schedules the SRS for UEs that were recently added or reconfigured. + void schedule_updated_ues_srs(cell_resource_allocator& res_alloc); + // Helper that allocates an SRS opportunity for a given UE. + bool allocate_srs_opportunity(cell_slot_resource_allocator& slot_alloc, const periodic_srs_info& srs_opportunity); + // Helper that fills the SRS PDU fields. + void fill_srs_pdu(srs_info& pdu, const srs_config::srs_resource& srs_res_cfg, const ue_cell_configuration& ue_cfg); + + void add_resource(rnti_t crnti, srs_periodicity period, unsigned offset, srs_config::srs_res_id res_id); + void rem_resource(rnti_t crnti, srs_periodicity period, unsigned offset, srs_config::srs_res_id res_id); + + // Cell configuration. + const cell_configuration& cell_cfg; + ue_repository& ues; + + srslog::basic_logger& logger; + + // Storage of the periodic SRSs to be scheduled in the resource grid. Each position of the vector represents a slot + // in a ring-like structure (ie slot % WHEEL_SIZE). Each of these vector indexes/slots contains a list of periodic + // SRS information to be scheduled in the respective slot. + std::vector> periodic_srs_slot_wheel; + + // UEs whose configuration has been updated in between the last and current slot indications. + std::vector updated_ues; +}; + +} // namespace srsran diff --git a/lib/scheduler/support/slot_event_list.h b/lib/scheduler/support/slot_event_list.h deleted file mode 100644 index cb709b20cc..0000000000 --- a/lib/scheduler/support/slot_event_list.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * - * Copyright 2021-2024 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN 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. - * - * srsRAN 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/. - * - */ - -#pragma once - -#include "srsran/adt/span.h" -#include -#include - -namespace srsran { - -/// This class stores incoming events in a thread-safe queue, and pops all enqueued events in a single batch, when -/// a new slot indication is notified. -/// The advantage of this class over a mutexed queue is the lower contention caused when popping elements from the -/// queue. This is particularly relevant because the events need to be processed during the scheduler slot handling. -/// \tparam Event type of event pushed/popped from the list. -template -class slot_event_list -{ -public: - void reserve(unsigned cap) - { - std::lock_guard lock(mutex); - pending_events.reserve(cap); - current_events.reserve(cap); - } - - void slot_indication() - { - current_events.clear(); - std::lock_guard lock(mutex); - pending_events.swap(current_events); - } - - template - void push(Ev&& ev) - { - static_assert(std::is_convertible::value, "Invalid type"); - std::lock_guard lock(mutex); - pending_events.push_back(std::forward(ev)); - } - - template - void emplace(Args&&... args) - { - std::lock_guard lock(mutex); - pending_events.template emplace_back(std::forward(args)...); - } - - span get_events() { return current_events; } - -private: - /// Stores all enqueued events that are going to be processed in the next slot_indication. - std::vector pending_events; - - /// Contains the events being processed in the current slot. - /// Note: the transfer of next_events to current_events is done via a std::swap, which for std::vector is very fast. - std::vector current_events; - - std::mutex mutex; -}; - -} // namespace srsran diff --git a/lib/scheduler/support/slot_sync_point.h b/lib/scheduler/support/slot_sync_point.h deleted file mode 100644 index 51c2802fa7..0000000000 --- a/lib/scheduler/support/slot_sync_point.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * - * Copyright 2021-2024 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN 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. - * - * srsRAN 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/. - * - */ - -#pragma once - -#include "srsran/ran/slot_point.h" -#include -#include - -namespace srsran { - -/// This object creates a barrier for multiple threads when wait() is called. Once the count reaches zero, -/// a callback is invoked. -class slot_sync_point -{ -public: - slot_sync_point() = default; - slot_sync_point(const slot_sync_point&) = delete; - slot_sync_point(slot_sync_point&&) = delete; - slot_sync_point& operator=(const slot_sync_point&) = delete; - slot_sync_point& operator=(slot_sync_point&&) = delete; - - /// \brief Called when thread enters synchronization point. Steps: - /// 1. If the current thread is the first to call wait(), the count is set to the number of workers. - /// 2. The count is decremented. - /// - If count > 0, the current thread blocks waiting for count to reach zero. - /// - If count==0, the current thread invokes completion callback, and notifies remaining threads to unlock. - /// \param sl current slot. - /// \param nof_workers number of workers currently passing through synchronization point. - /// \param func Completion callback invoked when count reaches zero. - template - void wait(slot_point sl, size_t nof_workers, const CompletionFunction& func) - { - std::unique_lock lock(mutex); - srsran_sanity_check(nof_workers > 0, "Invalid count"); - if (sl != last_sl) { - // Initialize barrier. - count = nof_workers; - last_sl = sl; - } - - if (--count == 0) { - // Phase completion step. - func(); - lock.unlock(); - cvar.notify_all(); - } else { - while (count > 0) { - cvar.wait(lock); - } - } - } - -private: - slot_point last_sl; - size_t count; - std::mutex mutex; - std::condition_variable cvar; -}; - -} // namespace srsran diff --git a/lib/scheduler/ue_scheduling/ue_event_manager.cpp b/lib/scheduler/ue_scheduling/ue_event_manager.cpp index f1d8771c5f..41f9d02660 100644 --- a/lib/scheduler/ue_scheduling/ue_event_manager.cpp +++ b/lib/scheduler/ue_scheduling/ue_event_manager.cpp @@ -23,6 +23,7 @@ #include "ue_event_manager.h" #include "../logging/scheduler_event_logger.h" #include "../logging/scheduler_metrics_handler.h" +#include "../srs/srs_scheduler.h" #include "../uci_scheduling/uci_scheduler_impl.h" using namespace srsran; @@ -161,6 +162,9 @@ void ue_event_manager::handle_ue_creation(ue_config_update_event ev) // Update UCI scheduler with new UE UCI resources. du_cells[pcell_index].uci_sched->add_ue(added_ue.get_cell(to_ue_cell_index(i)).cfg()); + // Update SRS scheduler with new UE SRS resources. + du_cells[pcell_index].srs_sched->add_ue(added_ue.get_cell(to_ue_cell_index(i)).cfg()); + // Add UE to slice scheduler. // Note: This action only has effect when UE is created in non-fallback mode. du_cells[pcell_index].slice_sched->add_ue(ueidx); @@ -199,11 +203,14 @@ void ue_event_manager::handle_ue_reconfiguration(ue_config_update_event ev) // UE carrier is being removed. // Update UE UCI resources in UCI scheduler. du_cells[ue_cc.cell_index].uci_sched->rem_ue(ue_cc.cfg()); + // Update UE SRS resources in SRS scheduler. + du_cells[ue_cc.cell_index].srs_sched->rem_ue(ue_cc.cfg()); // Schedule removal of UE in slice scheduler. du_cells[ue_cc.cell_index].slice_sched->rem_ue(ue_idx); } else { // UE carrier is being reconfigured. du_cells[ue_cc.cell_index].uci_sched->reconf_ue(ev.next_config().ue_cell_cfg(ue_cc.cell_index), ue_cc.cfg()); + du_cells[ue_cc.cell_index].srs_sched->reconf_ue(ev.next_config().ue_cell_cfg(ue_cc.cell_index), ue_cc.cfg()); } } for (unsigned i = 0, e = ev.next_config().nof_cells(); i != e; ++i) { @@ -212,6 +219,8 @@ void ue_event_manager::handle_ue_reconfiguration(ue_config_update_event ev) if (ue_cc == nullptr) { // New UE carrier is being added. du_cells[new_ue_cc_cfg.cell_cfg_common.cell_index].uci_sched->add_ue(new_ue_cc_cfg); + + du_cells[new_ue_cc_cfg.cell_cfg_common.cell_index].srs_sched->add_ue(new_ue_cc_cfg); } } @@ -254,6 +263,8 @@ void ue_event_manager::handle_ue_deletion(ue_config_delete_event ev) for (unsigned i = 0, e = u.nof_cells(); i != e; ++i) { // Update UCI scheduling by removing existing UE UCI resources. du_cells[u.get_cell(to_ue_cell_index(i)).cell_index].uci_sched->rem_ue(u.get_pcell().cfg()); + // Update SRS scheduling by removing existing UE SRS resources. + du_cells[u.get_cell(to_ue_cell_index(i)).cell_index].srs_sched->rem_ue(u.get_pcell().cfg()); // Schedule removal of UE from slice scheduler. du_cells[u.get_cell(to_ue_cell_index(i)).cell_index].slice_sched->rem_ue(ue_idx); } @@ -371,6 +382,9 @@ void ue_event_manager::handle_crc_indication(const ul_crc_indication& crc_ind) if (not cell_specific_events[crc_ind.cell_index].try_push(cell_event_t{ crc_ind.crcs[i].ue_index, [this, sl_rx = crc_ind.sl_rx, crc = crc_ind.crcs[i]](ue_cell& ue_cc) { + double delay_ms = + static_cast(last_sl - sl_rx) * (10 / du_cells[ue_cc.cell_index].cfg->nof_slots_per_frame); + const int tbs = ue_cc.handle_crc_pdu(sl_rx, crc); if (tbs < 0) { return; @@ -388,7 +402,7 @@ void ue_event_manager::handle_crc_indication(const ul_crc_indication& crc_ind) // Notify metrics handler. du_cells[ue_cc.cell_index].metrics->handle_crc_indication(crc, units::bytes{(unsigned)tbs}); - du_cells[ue_cc.cell_index].metrics->handle_ul_delay(crc.ue_index, last_sl - sl_rx); + du_cells[ue_cc.cell_index].metrics->handle_ul_delay(crc.ue_index, delay_ms); }, "CRC", true})) { @@ -748,6 +762,7 @@ void ue_event_manager::add_cell(const cell_creation_event& cell_ev) du_cells[cell_index].fallback_sched = &cell_ev.fallback_sched; du_cells[cell_index].uci_sched = &cell_ev.uci_sched; du_cells[cell_index].slice_sched = &cell_ev.slice_sched; + du_cells[cell_index].srs_sched = &cell_ev.srs_sched; du_cells[cell_index].metrics = &cell_ev.metrics; du_cells[cell_index].ev_logger = &cell_ev.ev_logger; diff --git a/lib/scheduler/ue_scheduling/ue_event_manager.h b/lib/scheduler/ue_scheduling/ue_event_manager.h index e567cae90e..426deffae2 100644 --- a/lib/scheduler/ue_scheduling/ue_event_manager.h +++ b/lib/scheduler/ue_scheduling/ue_event_manager.h @@ -37,6 +37,7 @@ class cell_metrics_handler; class scheduler_event_logger; class uci_scheduler_impl; class cell_harq_manager; +class srs_scheduler; struct cell_creation_event { cell_resource_allocator& cell_res_grid; @@ -44,6 +45,7 @@ struct cell_creation_event { ue_fallback_scheduler& fallback_sched; uci_scheduler_impl& uci_sched; slice_scheduler& slice_sched; + srs_scheduler& srs_sched; cell_metrics_handler& metrics; scheduler_event_logger& ev_logger; }; @@ -143,6 +145,7 @@ class ue_event_manager final : public sched_ue_configuration_handler, ue_fallback_scheduler* fallback_sched = nullptr; uci_scheduler_impl* uci_sched = nullptr; slice_scheduler* slice_sched = nullptr; + srs_scheduler* srs_sched = nullptr; cell_metrics_handler* metrics = nullptr; scheduler_event_logger* ev_logger = nullptr; }; diff --git a/lib/scheduler/ue_scheduling/ue_fallback_scheduler.h b/lib/scheduler/ue_scheduling/ue_fallback_scheduler.h index ad16cf4b43..76d5ee7766 100644 --- a/lib/scheduler/ue_scheduling/ue_fallback_scheduler.h +++ b/lib/scheduler/ue_scheduling/ue_fallback_scheduler.h @@ -24,7 +24,6 @@ #include "../policy/ue_allocator.h" #include "../support/sch_pdu_builder.h" -#include "../support/slot_event_list.h" #include "ue_repository.h" #include "srsran/scheduler/scheduler_configurator.h" #include diff --git a/lib/scheduler/ue_scheduling/ue_scheduler_impl.cpp b/lib/scheduler/ue_scheduling/ue_scheduler_impl.cpp index 58734f9bb3..3fd5fa985b 100644 --- a/lib/scheduler/ue_scheduling/ue_scheduler_impl.cpp +++ b/lib/scheduler/ue_scheduling/ue_scheduler_impl.cpp @@ -42,6 +42,7 @@ void ue_scheduler_impl::add_cell(const ue_scheduler_cell_params& params) cells[params.cell_index].fallback_sched, cells[params.cell_index].uci_sched, cells[params.cell_index].slice_sched, + cells[params.cell_index].srs_sched, *params.cell_metrics, *params.ev_logger}); ue_alloc.add_cell(params.cell_index, *params.pdcch_sched, *params.uci_alloc, *params.cell_res_alloc); @@ -213,6 +214,9 @@ void ue_scheduler_impl::run_slot(slot_point slot_tx) // Schedule periodic UCI (SR and CSI) before any UL grants. group_cell.uci_sched.run_slot(*group_cell.cell_res_alloc); + // Schedule periodic SRS before any UE grants. + cells[cell_index].srs_sched.run_slot(*group_cell.cell_res_alloc); + // Run cell-specific SRB0 scheduler. group_cell.fallback_sched.run_slot(*group_cell.cell_res_alloc); @@ -253,6 +257,7 @@ ue_scheduler_impl::cell::cell(const scheduler_ue_expert_config& expert_cfg, cell_harqs(MAX_NOF_DU_UES, MAX_NOF_HARQS, std::make_unique(metrics_handler)), uci_sched(params.cell_res_alloc->cfg, *params.uci_alloc, ues), fallback_sched(expert_cfg, params.cell_res_alloc->cfg, *params.pdcch_sched, *params.pucch_alloc, ues), - slice_sched(params.cell_res_alloc->cfg, ues) + slice_sched(params.cell_res_alloc->cfg, ues), + srs_sched(params.cell_res_alloc->cfg, ues) { } diff --git a/lib/scheduler/ue_scheduling/ue_scheduler_impl.h b/lib/scheduler/ue_scheduling/ue_scheduler_impl.h index b1f0f9faf8..f061a74a3e 100644 --- a/lib/scheduler/ue_scheduling/ue_scheduler_impl.h +++ b/lib/scheduler/ue_scheduling/ue_scheduler_impl.h @@ -27,6 +27,7 @@ #include "../policy/scheduler_policy.h" #include "../pucch_scheduling/pucch_guardbands_scheduler.h" #include "../slicing/slice_scheduler.h" +#include "../srs/srs_scheduler_impl.h" #include "../uci_scheduling/uci_scheduler_impl.h" #include "ue_cell_grid_allocator.h" #include "ue_event_manager.h" @@ -34,6 +35,7 @@ #include "ue_repository.h" #include "ue_scheduler.h" #include "srsran/scheduler/config/scheduler_expert_config.h" +#include namespace srsran { @@ -83,6 +85,9 @@ class ue_scheduler_impl final : public ue_scheduler /// Slice scheduler. slice_scheduler slice_sched; + /// SRS scheduler + srs_scheduler_impl srs_sched; + cell(const scheduler_ue_expert_config& expert_cfg, const ue_scheduler_cell_params& params, ue_repository& ues, diff --git a/tests/e2e/tests/ping.py b/tests/e2e/tests/ping.py index d3a420d8c3..fe8c186b7d 100644 --- a/tests/e2e/tests/ping.py +++ b/tests/e2e/tests/ping.py @@ -254,7 +254,7 @@ def test_zmq( sample_rate=None, # default from testbed global_timing_advance=0, time_alignment_calibration=0, - ue_stop_timeout=1, + ue_stop_timeout=3, enable_security_mode=ciphering, post_command=("cu_cp --inactivity_timer=600", ""), ) diff --git a/tests/e2e/tests/viavi/test_declaration.yml b/tests/e2e/tests/viavi/test_declaration.yml index b858f5bce1..f561d2a66b 100644 --- a/tests/e2e/tests/viavi/test_declaration.yml +++ b/tests/e2e/tests/viavi/test_declaration.yml @@ -159,8 +159,8 @@ tests: # test/fail criteria expected_dl_bitrate: 14.0e+3 expected_ul_bitrate: 1.0e+3 - expected_nof_kos: 3 - warning_as_errors: true + expected_nof_kos: 9999999999999 + warning_as_errors: false - campaign_filename: *campaign_filename test_name: "1UE birth-death UDP bidirectional" diff --git a/tests/unittests/cu_up/cu_up_test_helpers.h b/tests/unittests/cu_up/cu_up_test_helpers.h index 81b5a99b06..0b5d119226 100644 --- a/tests/unittests/cu_up/cu_up_test_helpers.h +++ b/tests/unittests/cu_up/cu_up_test_helpers.h @@ -253,6 +253,8 @@ class dummy_f1u_gateway final : public f1u_cu_up_gateway removed_ul_teid_list.push_back(ul_up_tnl_info.gtp_teid); } + expected get_cu_bind_address() const override { return "127.0.0.1"; } + std::list created_ul_teid_list = {}; std::list attached_ul_teid_list = {}; std::list removed_ul_teid_list = {}; diff --git a/tests/unittests/f1u/common/f1u_cu_split_connector_test.cpp b/tests/unittests/f1u/common/f1u_cu_split_connector_test.cpp index b542e7368e..d8b966ab9b 100644 --- a/tests/unittests/f1u/common/f1u_cu_split_connector_test.cpp +++ b/tests/unittests/f1u/common/f1u_cu_split_connector_test.cpp @@ -145,7 +145,7 @@ class f1u_cu_split_connector_test : public ::testing::Test nru_gw_config.reuse_addr = true; udp_gw = srs_cu_up::create_udp_ngu_gateway(nru_gw_config, *epoll_broker, io_tx_executor); - f1u_cu_up_split_gateway_creation_msg cu_create_msg{udp_gw.get(), demux.get(), dummy_pcap, tester_bind_port.value()}; + f1u_cu_up_split_gateway_creation_msg cu_create_msg{*udp_gw, *demux, dummy_pcap, tester_bind_port.value()}; cu_gw = create_split_f1u_gw(cu_create_msg); cu_gw_bind_port = cu_gw->get_bind_port(); ASSERT_TRUE(cu_gw_bind_port.has_value()); diff --git a/tests/unittests/ran/band_helper_test.cpp b/tests/unittests/ran/band_helper_test.cpp index 6a5a3751e2..4b091e9a35 100644 --- a/tests/unittests/ran/band_helper_test.cpp +++ b/tests/unittests/ran/band_helper_test.cpp @@ -20,108 +20,132 @@ * */ +#include "srsran/adt/span.h" #include "srsran/ran/band_helper.h" #include "srsran/ran/bs_channel_bandwidth.h" #include "srsran/ran/duplex_mode.h" #include "srsran/ran/subcarrier_spacing.h" +#include "srsran/support/test_utils.h" #include using namespace srsran; +using namespace band_helper; TEST(test_get_band_from_arfcn, mix_bands) { - ASSERT_NE(nr_band::invalid, band_helper::get_band_from_dl_arfcn(530000U)); - ASSERT_EQ(nr_band::invalid, band_helper::get_band_from_dl_arfcn(5300000U)); - ASSERT_EQ(nr_band::n1, band_helper::get_band_from_dl_arfcn(423000U)); - ASSERT_EQ(nr_band::n3, band_helper::get_band_from_dl_arfcn(365000U)); - ASSERT_EQ(nr_band::n5, band_helper::get_band_from_dl_arfcn(175000U)); - ASSERT_EQ(nr_band::n7, band_helper::get_band_from_dl_arfcn(530000U)); - ASSERT_EQ(nr_band::n25, band_helper::get_band_from_dl_arfcn(399000U)); - ASSERT_EQ(nr_band::n34, band_helper::get_band_from_dl_arfcn(404000U)); - ASSERT_EQ(nr_band::n38, band_helper::get_band_from_dl_arfcn(515000U)); - ASSERT_EQ(nr_band::n41, band_helper::get_band_from_dl_arfcn(499902U)); - ASSERT_EQ(nr_band::n41, band_helper::get_band_from_dl_arfcn(504000U)); - ASSERT_EQ(nr_band::n50, band_helper::get_band_from_dl_arfcn(286500U)); - ASSERT_EQ(nr_band::n51, band_helper::get_band_from_dl_arfcn(285500U)); - ASSERT_EQ(nr_band::n70, band_helper::get_band_from_dl_arfcn(400000U)); - ASSERT_EQ(nr_band::n77, band_helper::get_band_from_dl_arfcn(630500U)); + ASSERT_NE(nr_band::invalid, get_band_from_dl_arfcn(530000U)); + ASSERT_EQ(nr_band::invalid, get_band_from_dl_arfcn(5300000U)); + ASSERT_EQ(nr_band::n1, get_band_from_dl_arfcn(423000U)); + ASSERT_EQ(nr_band::n3, get_band_from_dl_arfcn(365000U)); + ASSERT_EQ(nr_band::n5, get_band_from_dl_arfcn(175000U)); + ASSERT_EQ(nr_band::n7, get_band_from_dl_arfcn(530000U)); + ASSERT_EQ(nr_band::n25, get_band_from_dl_arfcn(399000U)); + ASSERT_EQ(nr_band::n34, get_band_from_dl_arfcn(404000U)); + ASSERT_EQ(nr_band::n38, get_band_from_dl_arfcn(515000U)); + ASSERT_EQ(nr_band::n41, get_band_from_dl_arfcn(499902U)); + ASSERT_EQ(nr_band::n41, get_band_from_dl_arfcn(504000U)); + ASSERT_EQ(nr_band::n50, get_band_from_dl_arfcn(286500U)); + ASSERT_EQ(nr_band::n51, get_band_from_dl_arfcn(285500U)); + ASSERT_EQ(nr_band::n70, get_band_from_dl_arfcn(400000U)); + ASSERT_EQ(nr_band::n77, get_band_from_dl_arfcn(630500U)); } TEST(test_arfcn_freq_conversion, arfcn_to_freq) { - ASSERT_DOUBLE_EQ(3489.42e6, band_helper::nr_arfcn_to_freq(632628)); + ASSERT_DOUBLE_EQ(3489.42e6, nr_arfcn_to_freq(632628)); // default refPointA. - ASSERT_DOUBLE_EQ(3508.92e6, band_helper::nr_arfcn_to_freq(633928)); + ASSERT_DOUBLE_EQ(3508.92e6, nr_arfcn_to_freq(633928)); // default ARFCN with freq divisible by 11.52 MHz. - ASSERT_DOUBLE_EQ(3513.6e6, band_helper::nr_arfcn_to_freq(634240)); + ASSERT_DOUBLE_EQ(3513.6e6, nr_arfcn_to_freq(634240)); // n28 n67 - ASSERT_DOUBLE_EQ(703.0e6, band_helper::nr_arfcn_to_freq(140600)); - ASSERT_DOUBLE_EQ(729.0e6, band_helper::nr_arfcn_to_freq(145800)); - ASSERT_DOUBLE_EQ(768.0e6, band_helper::nr_arfcn_to_freq(153600)); - ASSERT_DOUBLE_EQ(788.0e6, band_helper::nr_arfcn_to_freq(157600)); + ASSERT_DOUBLE_EQ(703.0e6, nr_arfcn_to_freq(140600)); + ASSERT_DOUBLE_EQ(729.0e6, nr_arfcn_to_freq(145800)); + ASSERT_DOUBLE_EQ(768.0e6, nr_arfcn_to_freq(153600)); + ASSERT_DOUBLE_EQ(788.0e6, nr_arfcn_to_freq(157600)); // n20 - ASSERT_DOUBLE_EQ(791.0e6, band_helper::nr_arfcn_to_freq(158200)); - ASSERT_DOUBLE_EQ(801.0e6, band_helper::nr_arfcn_to_freq(160200)); - ASSERT_DOUBLE_EQ(811.0e6, band_helper::nr_arfcn_to_freq(162200)); - ASSERT_DOUBLE_EQ(842.0e6, band_helper::nr_arfcn_to_freq(168400)); + ASSERT_DOUBLE_EQ(791.0e6, nr_arfcn_to_freq(158200)); + ASSERT_DOUBLE_EQ(801.0e6, nr_arfcn_to_freq(160200)); + ASSERT_DOUBLE_EQ(811.0e6, nr_arfcn_to_freq(162200)); + ASSERT_DOUBLE_EQ(842.0e6, nr_arfcn_to_freq(168400)); // n32 n75 - ASSERT_DOUBLE_EQ(1452.0e6, band_helper::nr_arfcn_to_freq(290400)); - ASSERT_DOUBLE_EQ(1472.0e6, band_helper::nr_arfcn_to_freq(294400)); + ASSERT_DOUBLE_EQ(1452.0e6, nr_arfcn_to_freq(290400)); + ASSERT_DOUBLE_EQ(1472.0e6, nr_arfcn_to_freq(294400)); // n1 - ASSERT_DOUBLE_EQ(1920.0e6, band_helper::nr_arfcn_to_freq(384000)); - ASSERT_DOUBLE_EQ(1940.15e6, band_helper::nr_arfcn_to_freq(388030)); - ASSERT_DOUBLE_EQ(1959.15e6, band_helper::nr_arfcn_to_freq(391830)); - ASSERT_DOUBLE_EQ(2170.0e6, band_helper::nr_arfcn_to_freq(434000)); + ASSERT_DOUBLE_EQ(1920.0e6, nr_arfcn_to_freq(384000)); + ASSERT_DOUBLE_EQ(1940.15e6, nr_arfcn_to_freq(388030)); + ASSERT_DOUBLE_EQ(1959.15e6, nr_arfcn_to_freq(391830)); + ASSERT_DOUBLE_EQ(2170.0e6, nr_arfcn_to_freq(434000)); // n3 - ASSERT_DOUBLE_EQ(1710.0e6, band_helper::nr_arfcn_to_freq(342000)); - ASSERT_DOUBLE_EQ(1740.0e6, band_helper::nr_arfcn_to_freq(348000)); - ASSERT_DOUBLE_EQ(1805.0e6, band_helper::nr_arfcn_to_freq(361000)); - ASSERT_DOUBLE_EQ(1842.5e6, band_helper::nr_arfcn_to_freq(368500)); - ASSERT_DOUBLE_EQ(1880.0e6, band_helper::nr_arfcn_to_freq(376000)); + ASSERT_DOUBLE_EQ(1710.0e6, nr_arfcn_to_freq(342000)); + ASSERT_DOUBLE_EQ(1740.0e6, nr_arfcn_to_freq(348000)); + ASSERT_DOUBLE_EQ(1805.0e6, nr_arfcn_to_freq(361000)); + ASSERT_DOUBLE_EQ(1842.5e6, nr_arfcn_to_freq(368500)); + ASSERT_DOUBLE_EQ(1880.0e6, nr_arfcn_to_freq(376000)); // n5 - ASSERT_DOUBLE_EQ(881.5e6, band_helper::nr_arfcn_to_freq(176300)); - ASSERT_DOUBLE_EQ(836.5e6, band_helper::nr_arfcn_to_freq(167300)); + ASSERT_DOUBLE_EQ(881.5e6, nr_arfcn_to_freq(176300)); + ASSERT_DOUBLE_EQ(836.5e6, nr_arfcn_to_freq(167300)); // n7 n38 - ASSERT_DOUBLE_EQ(2500.0e6, band_helper::nr_arfcn_to_freq(500000)); - ASSERT_DOUBLE_EQ(2540.0e6, band_helper::nr_arfcn_to_freq(508000)); - ASSERT_DOUBLE_EQ(2610.0e6, band_helper::nr_arfcn_to_freq(522000)); - ASSERT_DOUBLE_EQ(2690.0e6, band_helper::nr_arfcn_to_freq(538000)); + ASSERT_DOUBLE_EQ(2500.0e6, nr_arfcn_to_freq(500000)); + ASSERT_DOUBLE_EQ(2540.0e6, nr_arfcn_to_freq(508000)); + ASSERT_DOUBLE_EQ(2610.0e6, nr_arfcn_to_freq(522000)); + ASSERT_DOUBLE_EQ(2690.0e6, nr_arfcn_to_freq(538000)); // n78 - ASSERT_DOUBLE_EQ(3513.6e6, band_helper::nr_arfcn_to_freq(634240)); + ASSERT_DOUBLE_EQ(3513.6e6, nr_arfcn_to_freq(634240)); } TEST(test_arfcn_freq_conversion, freq_to_arfcn) { - ASSERT_EQ(632628, band_helper::freq_to_nr_arfcn(3489.42e6)); - ASSERT_EQ(633928, band_helper::freq_to_nr_arfcn(3508.92e6)); - ASSERT_EQ(634240, band_helper::freq_to_nr_arfcn(3513.6e6)); + ASSERT_EQ(632628, freq_to_nr_arfcn(3489.42e6)); + ASSERT_EQ(633928, freq_to_nr_arfcn(3508.92e6)); + ASSERT_EQ(634240, freq_to_nr_arfcn(3513.6e6)); // n28 n67 - ASSERT_EQ(140600, band_helper::freq_to_nr_arfcn(703.0e6)); - ASSERT_EQ(145800, band_helper::freq_to_nr_arfcn(729.0e6)); - ASSERT_EQ(153600, band_helper::freq_to_nr_arfcn(768.0e6)); - ASSERT_EQ(157600, band_helper::freq_to_nr_arfcn(788.0e6)); + ASSERT_EQ(140600, freq_to_nr_arfcn(703.0e6)); + ASSERT_EQ(145800, freq_to_nr_arfcn(729.0e6)); + ASSERT_EQ(153600, freq_to_nr_arfcn(768.0e6)); + ASSERT_EQ(157600, freq_to_nr_arfcn(788.0e6)); // n5 - ASSERT_EQ(176300, band_helper::freq_to_nr_arfcn(881.5e6)); + ASSERT_EQ(176300, freq_to_nr_arfcn(881.5e6)); } TEST(get_ul_arfcn_from_dl_arfcn, mixed_frequencies) { // n5 - ASSERT_EQ(167300, band_helper::get_ul_arfcn_from_dl_arfcn(176300, {})); + ASSERT_EQ(167300, get_ul_arfcn_from_dl_arfcn(176300, {})); + + ASSERT_EQ(500000, get_ul_arfcn_from_dl_arfcn(524000, nr_band::n7)); + ASSERT_EQ(504060, get_ul_arfcn_from_dl_arfcn(528060, nr_band::n7)); + ASSERT_EQ(514000, get_ul_arfcn_from_dl_arfcn(538000, nr_band::n7)); + + ASSERT_EQ(383000, get_ul_arfcn_from_dl_arfcn(399000, nr_band::n25)); + + // n28. The spectrum of this band overlaps with that of n14. We need to check that the return UL ARFCN belongs to n28. + ASSERT_EQ(142600, get_ul_arfcn_from_dl_arfcn(153600, nr_band::n28)); + ASSERT_EQ(144608, get_ul_arfcn_from_dl_arfcn(155608, nr_band::n28)); + + // For n66, n70, n92, n94, the UL spectrum is smaller than the DL spectrum. When we convert the DL ARFCN + // to the corresponding UL ARFCN, if the UL ARFCN exceeds the band upper-bound, we return 0. + ASSERT_EQ(356000, get_ul_arfcn_from_dl_arfcn(436000, nr_band::n66)); + ASSERT_EQ(0, get_ul_arfcn_from_dl_arfcn(440000, nr_band::n66)); + ASSERT_EQ(342000, get_ul_arfcn_from_dl_arfcn(402000, nr_band::n70)); + ASSERT_EQ(0, get_ul_arfcn_from_dl_arfcn(404000, nr_band::n70)); + ASSERT_EQ(172400, get_ul_arfcn_from_dl_arfcn(292400, nr_band::n92)); + ASSERT_EQ(0, get_ul_arfcn_from_dl_arfcn(303400, nr_band::n92)); + ASSERT_EQ(183000, get_ul_arfcn_from_dl_arfcn(293400, nr_band::n94)); + ASSERT_EQ(0, get_ul_arfcn_from_dl_arfcn(303400, nr_band::n94)); } TEST(test_get_abs_freq_point_a_arfcn, mixed_frequencies) { // n3 - ASSERT_EQ(367564, band_helper::get_abs_freq_point_a_arfcn(52, 368500)); + ASSERT_EQ(367564, get_abs_freq_point_a_arfcn(52, 368500)); // n5 - ASSERT_EQ(175364, band_helper::get_abs_freq_point_a_arfcn(52, 176300)); + ASSERT_EQ(175364, get_abs_freq_point_a_arfcn(52, 176300)); // n7 n38 - ASSERT_EQ(528984, band_helper::get_abs_freq_point_a_arfcn(52, 529920)); + ASSERT_EQ(528984, get_abs_freq_point_a_arfcn(52, 529920)); // n78 - ASSERT_EQ(633928, band_helper::get_abs_freq_point_a_arfcn(52, 634240)); + ASSERT_EQ(633928, get_abs_freq_point_a_arfcn(52, 634240)); } TEST(test_arfcn_freq_conversion, arfcn_to_freq_corner_cases) @@ -129,16 +153,16 @@ TEST(test_arfcn_freq_conversion, arfcn_to_freq_corner_cases) const uint32_t max_valid_nr_arfcn = 3279165; // Max ARFCN is 3279165 at almost 10 GHz - ASSERT_DOUBLE_EQ(99.99996e9, band_helper::nr_arfcn_to_freq(max_valid_nr_arfcn)); + ASSERT_DOUBLE_EQ(99.99996e9, nr_arfcn_to_freq(max_valid_nr_arfcn)); // Invalid ARFCN - ASSERT_DOUBLE_EQ(0.0, band_helper::nr_arfcn_to_freq(max_valid_nr_arfcn + 1)); + ASSERT_DOUBLE_EQ(0.0, nr_arfcn_to_freq(max_valid_nr_arfcn + 1)); } TEST(test_center_freq_conversion, freq_center) { - ASSERT_DOUBLE_EQ(881.5e6, band_helper::get_center_freq_from_abs_freq_point_a(52, 175364)); - ASSERT_DOUBLE_EQ(836.5e6, band_helper::get_center_freq_from_abs_freq_point_a(52, 166364)); + ASSERT_DOUBLE_EQ(881.5e6, get_center_freq_from_abs_freq_point_a(52, 175364)); + ASSERT_DOUBLE_EQ(836.5e6, get_center_freq_from_abs_freq_point_a(52, 166364)); } TEST(test_band_duplexing, all_bands) @@ -147,18 +171,18 @@ TEST(test_band_duplexing, all_bands) if (b < nr_band::n29 or b == nr_band::n30 or b == nr_band::n65 or b == nr_band::n66 or (b >= srsran::nr_band::n70 and b <= srsran::nr_band::n74) or b == nr_band::n85 or (b >= srsran::nr_band::n91 and b <= srsran::nr_band::n94) or b == srsran::nr_band::n100) { - ASSERT_EQ(duplex_mode::FDD, band_helper::get_duplex_mode(b)); + ASSERT_EQ(duplex_mode::FDD, get_duplex_mode(b)); } else if ((b >= srsran::nr_band::n34 and b <= srsran::nr_band::n53) or (b >= srsran::nr_band::n77 and b <= srsran::nr_band::n79) or b == srsran::nr_band::n90 or b == srsran::nr_band::n96 or b > srsran::nr_band::n100) { - ASSERT_EQ(duplex_mode::TDD, band_helper::get_duplex_mode(b)); + ASSERT_EQ(duplex_mode::TDD, get_duplex_mode(b)); } else if (b == srsran::nr_band::n29 or b == srsran::nr_band::n67 or b == srsran::nr_band::n75 or b == srsran::nr_band::n76) { - ASSERT_EQ(duplex_mode::SDL, band_helper::get_duplex_mode(b)); + ASSERT_EQ(duplex_mode::SDL, get_duplex_mode(b)); } else if ((b >= srsran::nr_band::n80 and b <= srsran::nr_band::n84) or b == srsran::nr_band::n86 or b == srsran::nr_band::n89 or b == srsran::nr_band::n95 or (b >= srsran::nr_band::n97 and b <= srsran::nr_band::n99)) { - ASSERT_EQ(duplex_mode::SUL, band_helper::get_duplex_mode(b)); + ASSERT_EQ(duplex_mode::SUL, get_duplex_mode(b)); } else { ASSERT_FALSE(true); } @@ -179,7 +203,7 @@ TEST(test_is_paired_spectrum, paired) nr_band::n100}; for (const auto band : some_fdd_bands) { - ASSERT_TRUE(band_helper::is_paired_spectrum(band)); + ASSERT_TRUE(is_paired_spectrum(band)); } } @@ -200,7 +224,7 @@ TEST(test_is_paired_spectrum, unpaired) nr_band::n102}; for (const auto band : some_non_fdd_bands) { - ASSERT_FALSE(band_helper::is_paired_spectrum(band)); + ASSERT_FALSE(is_paired_spectrum(band)); } } @@ -224,10 +248,10 @@ TEST(test_ssb_pattern, ssb_pattern_case_A) // Skip SSB case-C-only bands and SUL bands. if (std::any_of( case_c_bands.begin(), case_c_bands.end(), [band](nr_band non_a_band) { return band == non_a_band; }) or - band_helper::get_duplex_mode(band) == srsran::duplex_mode::SUL) { + get_duplex_mode(band) == srsran::duplex_mode::SUL) { continue; } - ASSERT_EQ(ssb_pattern_case::A, band_helper::get_ssb_pattern(band, subcarrier_spacing::kHz15)); + ASSERT_EQ(ssb_pattern_case::A, get_ssb_pattern(band, subcarrier_spacing::kHz15)); } } @@ -236,7 +260,7 @@ TEST(test_ssb_pattern, ssb_pattern_case_B) const std::array case_b_bands{nr_band::n5, nr_band::n24, nr_band::n66}; for (auto band : case_b_bands) { - ASSERT_EQ(ssb_pattern_case::B, band_helper::get_ssb_pattern(band, subcarrier_spacing::kHz30)); + ASSERT_EQ(ssb_pattern_case::B, get_ssb_pattern(band, subcarrier_spacing::kHz30)); } } @@ -259,425 +283,666 @@ TEST(test_ssb_pattern, ssb_pattern_case_C) nr_band::n102, nr_band::n104}; for (auto band : case_c_bands) { - ASSERT_EQ(ssb_pattern_case::C, band_helper::get_ssb_pattern(band, subcarrier_spacing::kHz30)); + ASSERT_EQ(ssb_pattern_case::C, get_ssb_pattern(band, subcarrier_spacing::kHz30)); } } TEST(test_ssb_pattern, ssb_pattern_case_invalid) { - ASSERT_GT(ssb_pattern_case::invalid, band_helper::get_ssb_pattern(nr_band::n1, subcarrier_spacing::kHz15)); - ASSERT_EQ(ssb_pattern_case::invalid, band_helper::get_ssb_pattern(nr_band::n3, subcarrier_spacing::kHz30)); + ASSERT_GT(ssb_pattern_case::invalid, get_ssb_pattern(nr_band::n1, subcarrier_spacing::kHz15)); + ASSERT_EQ(ssb_pattern_case::invalid, get_ssb_pattern(nr_band::n3, subcarrier_spacing::kHz30)); } TEST(test_get_point_a_from_f_req, scs_kHz15) { // Band n1, BWs are {5MHz, 10MHz, 15MHz, 20MHz}. - ASSERT_DOUBLE_EQ(2122150000.0, - band_helper::get_abs_freq_point_a_from_f_ref(2124400000.0, 25, subcarrier_spacing::kHz15)); - ASSERT_DOUBLE_EQ(2105420000.0, - band_helper::get_abs_freq_point_a_from_f_ref(2110100000.0, 52, subcarrier_spacing::kHz15)); - ASSERT_DOUBLE_EQ(2108090000.0, - band_helper::get_abs_freq_point_a_from_f_ref(2115200000.0, 79, subcarrier_spacing::kHz15)); - ASSERT_DOUBLE_EQ(2150860000.0, - band_helper::get_abs_freq_point_a_from_f_ref(2160400000.0, 106, subcarrier_spacing::kHz15)); + ASSERT_DOUBLE_EQ(2122150000.0, get_abs_freq_point_a_from_f_ref(2124400000.0, 25, subcarrier_spacing::kHz15)); + ASSERT_DOUBLE_EQ(2105420000.0, get_abs_freq_point_a_from_f_ref(2110100000.0, 52, subcarrier_spacing::kHz15)); + ASSERT_DOUBLE_EQ(2108090000.0, get_abs_freq_point_a_from_f_ref(2115200000.0, 79, subcarrier_spacing::kHz15)); + ASSERT_DOUBLE_EQ(2150860000.0, get_abs_freq_point_a_from_f_ref(2160400000.0, 106, subcarrier_spacing::kHz15)); // Band n78, BWs are {10MHz, 15MHz, 20MHz, 30MHz, 40MHz, 50MHz}. - ASSERT_DOUBLE_EQ(3615675000.0, - band_helper::get_abs_freq_point_a_from_f_ref(3620355000.0, 52, subcarrier_spacing::kHz15)); - ASSERT_DOUBLE_EQ(3613245000.0, - band_helper::get_abs_freq_point_a_from_f_ref(3620355000.0, 79, subcarrier_spacing::kHz15)); - ASSERT_DOUBLE_EQ(3610815000.0, - band_helper::get_abs_freq_point_a_from_f_ref(3620355000.0, 106, subcarrier_spacing::kHz15)); - ASSERT_DOUBLE_EQ(3605955000.0, - band_helper::get_abs_freq_point_a_from_f_ref(3620355000.0, 160, subcarrier_spacing::kHz15)); - ASSERT_DOUBLE_EQ(3600915000.0, - band_helper::get_abs_freq_point_a_from_f_ref(3620355000.0, 216, subcarrier_spacing::kHz15)); - ASSERT_DOUBLE_EQ(3596055000.0, - band_helper::get_abs_freq_point_a_from_f_ref(3620355000.0, 270, subcarrier_spacing::kHz15)); + ASSERT_DOUBLE_EQ(3615675000.0, get_abs_freq_point_a_from_f_ref(3620355000.0, 52, subcarrier_spacing::kHz15)); + ASSERT_DOUBLE_EQ(3613245000.0, get_abs_freq_point_a_from_f_ref(3620355000.0, 79, subcarrier_spacing::kHz15)); + ASSERT_DOUBLE_EQ(3610815000.0, get_abs_freq_point_a_from_f_ref(3620355000.0, 106, subcarrier_spacing::kHz15)); + ASSERT_DOUBLE_EQ(3605955000.0, get_abs_freq_point_a_from_f_ref(3620355000.0, 160, subcarrier_spacing::kHz15)); + ASSERT_DOUBLE_EQ(3600915000.0, get_abs_freq_point_a_from_f_ref(3620355000.0, 216, subcarrier_spacing::kHz15)); + ASSERT_DOUBLE_EQ(3596055000.0, get_abs_freq_point_a_from_f_ref(3620355000.0, 270, subcarrier_spacing::kHz15)); } TEST(test_get_point_a_from_f_req, scs_kHz30) { // Band n3, BWs are {10MHz, 15MHz, 20MHz, 25MHz, 30MHz}. - ASSERT_DOUBLE_EQ(1821380000.0, - band_helper::get_abs_freq_point_a_from_f_ref(1825700000.0, 24, subcarrier_spacing::kHz30)); - ASSERT_DOUBLE_EQ(1844460000.0, - band_helper::get_abs_freq_point_a_from_f_ref(1851300000.0, 38, subcarrier_spacing::kHz30)); - ASSERT_DOUBLE_EQ(1860220000.0, - band_helper::get_abs_freq_point_a_from_f_ref(1869400000.0, 51, subcarrier_spacing::kHz30)); - ASSERT_DOUBLE_EQ(1837800000.0, - band_helper::get_abs_freq_point_a_from_f_ref(1849500000.0, 65, subcarrier_spacing::kHz30)); - ASSERT_DOUBLE_EQ(1837800000.0, - band_helper::get_abs_freq_point_a_from_f_ref(1849500000.0, 65, subcarrier_spacing::kHz30)); - ASSERT_DOUBLE_EQ(1842060000.0, - band_helper::get_abs_freq_point_a_from_f_ref(1856100000.0, 78, subcarrier_spacing::kHz30)); + ASSERT_DOUBLE_EQ(1821380000.0, get_abs_freq_point_a_from_f_ref(1825700000.0, 24, subcarrier_spacing::kHz30)); + ASSERT_DOUBLE_EQ(1844460000.0, get_abs_freq_point_a_from_f_ref(1851300000.0, 38, subcarrier_spacing::kHz30)); + ASSERT_DOUBLE_EQ(1860220000.0, get_abs_freq_point_a_from_f_ref(1869400000.0, 51, subcarrier_spacing::kHz30)); + ASSERT_DOUBLE_EQ(1837800000.0, get_abs_freq_point_a_from_f_ref(1849500000.0, 65, subcarrier_spacing::kHz30)); + ASSERT_DOUBLE_EQ(1837800000.0, get_abs_freq_point_a_from_f_ref(1849500000.0, 65, subcarrier_spacing::kHz30)); + ASSERT_DOUBLE_EQ(1842060000.0, get_abs_freq_point_a_from_f_ref(1856100000.0, 78, subcarrier_spacing::kHz30)); // Band n78, BWs that are tested are {15MHz, 20MHz, 30MHz, 50MHz, 80MHz, 100MHz}. - ASSERT_DOUBLE_EQ(3697320000.0, - band_helper::get_abs_freq_point_a_from_f_ref(3704160000.0, 38, subcarrier_spacing::kHz30)); - ASSERT_DOUBLE_EQ(3694980000.0, - band_helper::get_abs_freq_point_a_from_f_ref(3704160000.0, 51, subcarrier_spacing::kHz30)); - ASSERT_DOUBLE_EQ(3690120000.0, - band_helper::get_abs_freq_point_a_from_f_ref(3704160000.0, 78, subcarrier_spacing::kHz30)); - ASSERT_DOUBLE_EQ(3680220000.0, - band_helper::get_abs_freq_point_a_from_f_ref(3704160000.0, 133, subcarrier_spacing::kHz30)); - ASSERT_DOUBLE_EQ(3665100000.0, - band_helper::get_abs_freq_point_a_from_f_ref(3704160000.0, 217, subcarrier_spacing::kHz30)); - ASSERT_DOUBLE_EQ(3655020000.0, - band_helper::get_abs_freq_point_a_from_f_ref(3704160000.0, 273, subcarrier_spacing::kHz30)); + ASSERT_DOUBLE_EQ(3697320000.0, get_abs_freq_point_a_from_f_ref(3704160000.0, 38, subcarrier_spacing::kHz30)); + ASSERT_DOUBLE_EQ(3694980000.0, get_abs_freq_point_a_from_f_ref(3704160000.0, 51, subcarrier_spacing::kHz30)); + ASSERT_DOUBLE_EQ(3690120000.0, get_abs_freq_point_a_from_f_ref(3704160000.0, 78, subcarrier_spacing::kHz30)); + ASSERT_DOUBLE_EQ(3680220000.0, get_abs_freq_point_a_from_f_ref(3704160000.0, 133, subcarrier_spacing::kHz30)); + ASSERT_DOUBLE_EQ(3665100000.0, get_abs_freq_point_a_from_f_ref(3704160000.0, 217, subcarrier_spacing::kHz30)); + ASSERT_DOUBLE_EQ(3655020000.0, get_abs_freq_point_a_from_f_ref(3704160000.0, 273, subcarrier_spacing::kHz30)); } TEST(test_get_point_a_from_f_req, scs_kHz60) { // Band n41, BWs are {40MHz, 50MHz, 60MHz, 70MHz, 80MHz, 90MHz, 100MHz}. - ASSERT_DOUBLE_EQ(2671620000.0, - band_helper::get_abs_freq_point_a_from_f_ref(2689980000.0, 51, subcarrier_spacing::kHz60)); - ASSERT_DOUBLE_EQ(2666580000.0, - band_helper::get_abs_freq_point_a_from_f_ref(2689980000.0, 65, subcarrier_spacing::kHz60)); - ASSERT_DOUBLE_EQ(2661540000.0, - band_helper::get_abs_freq_point_a_from_f_ref(2689980000.0, 79, subcarrier_spacing::kHz60)); - ASSERT_DOUBLE_EQ(2656500000.0, - band_helper::get_abs_freq_point_a_from_f_ref(2689980000.0, 93, subcarrier_spacing::kHz60)); - ASSERT_DOUBLE_EQ(2651460000.0, - band_helper::get_abs_freq_point_a_from_f_ref(2689980000.0, 107, subcarrier_spacing::kHz60)); - ASSERT_DOUBLE_EQ(2646420000.0, - band_helper::get_abs_freq_point_a_from_f_ref(2689980000.0, 121, subcarrier_spacing::kHz60)); - ASSERT_DOUBLE_EQ(2641380000.0, - band_helper::get_abs_freq_point_a_from_f_ref(2689980000.0, 135, subcarrier_spacing::kHz60)); + ASSERT_DOUBLE_EQ(2671620000.0, get_abs_freq_point_a_from_f_ref(2689980000.0, 51, subcarrier_spacing::kHz60)); + ASSERT_DOUBLE_EQ(2666580000.0, get_abs_freq_point_a_from_f_ref(2689980000.0, 65, subcarrier_spacing::kHz60)); + ASSERT_DOUBLE_EQ(2661540000.0, get_abs_freq_point_a_from_f_ref(2689980000.0, 79, subcarrier_spacing::kHz60)); + ASSERT_DOUBLE_EQ(2656500000.0, get_abs_freq_point_a_from_f_ref(2689980000.0, 93, subcarrier_spacing::kHz60)); + ASSERT_DOUBLE_EQ(2651460000.0, get_abs_freq_point_a_from_f_ref(2689980000.0, 107, subcarrier_spacing::kHz60)); + ASSERT_DOUBLE_EQ(2646420000.0, get_abs_freq_point_a_from_f_ref(2689980000.0, 121, subcarrier_spacing::kHz60)); + ASSERT_DOUBLE_EQ(2641380000.0, get_abs_freq_point_a_from_f_ref(2689980000.0, 135, subcarrier_spacing::kHz60)); // Band n78, BWs that are tested are {10MHz, 40MHz, 60MHz, 70MHz, 90MHz}. - ASSERT_DOUBLE_EQ(3700200000.0, - band_helper::get_abs_freq_point_a_from_f_ref(3704160000.0, 11, subcarrier_spacing::kHz60)); - ASSERT_DOUBLE_EQ(3685800000.0, - band_helper::get_abs_freq_point_a_from_f_ref(3704160000.0, 51, subcarrier_spacing::kHz60)); - ASSERT_DOUBLE_EQ(3675720000.0, - band_helper::get_abs_freq_point_a_from_f_ref(3704160000.0, 79, subcarrier_spacing::kHz60)); - ASSERT_DOUBLE_EQ(3670680000.0, - band_helper::get_abs_freq_point_a_from_f_ref(3704160000.0, 93, subcarrier_spacing::kHz60)); - ASSERT_DOUBLE_EQ(3660600000.0, - band_helper::get_abs_freq_point_a_from_f_ref(3704160000.0, 121, subcarrier_spacing::kHz60)); + ASSERT_DOUBLE_EQ(3700200000.0, get_abs_freq_point_a_from_f_ref(3704160000.0, 11, subcarrier_spacing::kHz60)); + ASSERT_DOUBLE_EQ(3685800000.0, get_abs_freq_point_a_from_f_ref(3704160000.0, 51, subcarrier_spacing::kHz60)); + ASSERT_DOUBLE_EQ(3675720000.0, get_abs_freq_point_a_from_f_ref(3704160000.0, 79, subcarrier_spacing::kHz60)); + ASSERT_DOUBLE_EQ(3670680000.0, get_abs_freq_point_a_from_f_ref(3704160000.0, 93, subcarrier_spacing::kHz60)); + ASSERT_DOUBLE_EQ(3660600000.0, get_abs_freq_point_a_from_f_ref(3704160000.0, 121, subcarrier_spacing::kHz60)); } TEST(test_get_f_req_from_f_req_point_a, scs_kHz15) { // Band n1, BWs are {5MHz, 10MHz, 15MHz, 20MHz}. - ASSERT_DOUBLE_EQ(2124400000.0, - band_helper::get_f_ref_from_abs_freq_point_a(2122150000.0, 25, subcarrier_spacing::kHz15)); - ASSERT_DOUBLE_EQ(2110100000.0, - band_helper::get_f_ref_from_abs_freq_point_a(2105420000.0, 52, subcarrier_spacing::kHz15)); - ASSERT_DOUBLE_EQ(2115200000.0, - band_helper::get_f_ref_from_abs_freq_point_a(2108090000.0, 79, subcarrier_spacing::kHz15)); - ASSERT_DOUBLE_EQ(2160400000.0, - band_helper::get_f_ref_from_abs_freq_point_a(2150860000.0, 106, subcarrier_spacing::kHz15)); + ASSERT_DOUBLE_EQ(2124400000.0, get_f_ref_from_abs_freq_point_a(2122150000.0, 25, subcarrier_spacing::kHz15)); + ASSERT_DOUBLE_EQ(2110100000.0, get_f_ref_from_abs_freq_point_a(2105420000.0, 52, subcarrier_spacing::kHz15)); + ASSERT_DOUBLE_EQ(2115200000.0, get_f_ref_from_abs_freq_point_a(2108090000.0, 79, subcarrier_spacing::kHz15)); + ASSERT_DOUBLE_EQ(2160400000.0, get_f_ref_from_abs_freq_point_a(2150860000.0, 106, subcarrier_spacing::kHz15)); // Band n78, BWs are {10MHz, 15MHz, 20MHz, 30MHz, 40MHz, 50MHz}. - ASSERT_DOUBLE_EQ(3620355000.0, - band_helper::get_f_ref_from_abs_freq_point_a(3615675000.0, 52, subcarrier_spacing::kHz15)); - ASSERT_DOUBLE_EQ(3620355000.0, - band_helper::get_f_ref_from_abs_freq_point_a(3613245000.0, 79, subcarrier_spacing::kHz15)); - ASSERT_DOUBLE_EQ(3620355000.0, - band_helper::get_f_ref_from_abs_freq_point_a(3610815000.0, 106, subcarrier_spacing::kHz15)); - ASSERT_DOUBLE_EQ(3620355000.0, - band_helper::get_f_ref_from_abs_freq_point_a(3605955000.0, 160, subcarrier_spacing::kHz15)); - ASSERT_DOUBLE_EQ(3620355000.0, - band_helper::get_f_ref_from_abs_freq_point_a(3600915000.0, 216, subcarrier_spacing::kHz15)); - ASSERT_DOUBLE_EQ(3620355000.0, - band_helper::get_f_ref_from_abs_freq_point_a(3596055000.0, 270, subcarrier_spacing::kHz15)); + ASSERT_DOUBLE_EQ(3620355000.0, get_f_ref_from_abs_freq_point_a(3615675000.0, 52, subcarrier_spacing::kHz15)); + ASSERT_DOUBLE_EQ(3620355000.0, get_f_ref_from_abs_freq_point_a(3613245000.0, 79, subcarrier_spacing::kHz15)); + ASSERT_DOUBLE_EQ(3620355000.0, get_f_ref_from_abs_freq_point_a(3610815000.0, 106, subcarrier_spacing::kHz15)); + ASSERT_DOUBLE_EQ(3620355000.0, get_f_ref_from_abs_freq_point_a(3605955000.0, 160, subcarrier_spacing::kHz15)); + ASSERT_DOUBLE_EQ(3620355000.0, get_f_ref_from_abs_freq_point_a(3600915000.0, 216, subcarrier_spacing::kHz15)); + ASSERT_DOUBLE_EQ(3620355000.0, get_f_ref_from_abs_freq_point_a(3596055000.0, 270, subcarrier_spacing::kHz15)); } TEST(test_get_f_req_from_f_req_point_a, scs_kHz30) { // Band n3, BWs are {10MHz, 15MHz, 20MHz, 25MHz, 30MHz}. - ASSERT_DOUBLE_EQ(1825700000.0, - band_helper::get_f_ref_from_abs_freq_point_a(1821380000.0, 24, subcarrier_spacing::kHz30)); - ASSERT_DOUBLE_EQ(1851300000.0, - band_helper::get_f_ref_from_abs_freq_point_a(1844460000.0, 38, subcarrier_spacing::kHz30)); - ASSERT_DOUBLE_EQ(1869400000.0, - band_helper::get_f_ref_from_abs_freq_point_a(1860220000.0, 51, subcarrier_spacing::kHz30)); - ASSERT_DOUBLE_EQ(1849500000.0, - band_helper::get_f_ref_from_abs_freq_point_a(1837800000.0, 65, subcarrier_spacing::kHz30)); - ASSERT_DOUBLE_EQ(1849500000.0, - band_helper::get_f_ref_from_abs_freq_point_a(1837800000.0, 65, subcarrier_spacing::kHz30)); - ASSERT_DOUBLE_EQ(1856100000.0, - band_helper::get_f_ref_from_abs_freq_point_a(1842060000.0, 78, subcarrier_spacing::kHz30)); + ASSERT_DOUBLE_EQ(1825700000.0, get_f_ref_from_abs_freq_point_a(1821380000.0, 24, subcarrier_spacing::kHz30)); + ASSERT_DOUBLE_EQ(1851300000.0, get_f_ref_from_abs_freq_point_a(1844460000.0, 38, subcarrier_spacing::kHz30)); + ASSERT_DOUBLE_EQ(1869400000.0, get_f_ref_from_abs_freq_point_a(1860220000.0, 51, subcarrier_spacing::kHz30)); + ASSERT_DOUBLE_EQ(1849500000.0, get_f_ref_from_abs_freq_point_a(1837800000.0, 65, subcarrier_spacing::kHz30)); + ASSERT_DOUBLE_EQ(1849500000.0, get_f_ref_from_abs_freq_point_a(1837800000.0, 65, subcarrier_spacing::kHz30)); + ASSERT_DOUBLE_EQ(1856100000.0, get_f_ref_from_abs_freq_point_a(1842060000.0, 78, subcarrier_spacing::kHz30)); // Band n78, BWs that are tested are {15MHz, 20MHz, 30MHz, 50MHz, 80MHz, 100MHz}. - ASSERT_DOUBLE_EQ(3704160000.0, - band_helper::get_f_ref_from_abs_freq_point_a(3697320000.0, 38, subcarrier_spacing::kHz30)); - ASSERT_DOUBLE_EQ(3704160000.0, - band_helper::get_f_ref_from_abs_freq_point_a(3694980000.0, 51, subcarrier_spacing::kHz30)); - ASSERT_DOUBLE_EQ(3704160000.0, - band_helper::get_f_ref_from_abs_freq_point_a(3690120000.0, 78, subcarrier_spacing::kHz30)); - ASSERT_DOUBLE_EQ(3704160000.0, - band_helper::get_f_ref_from_abs_freq_point_a(3680220000.0, 133, subcarrier_spacing::kHz30)); - ASSERT_DOUBLE_EQ(3704160000.0, - band_helper::get_f_ref_from_abs_freq_point_a(3665100000.0, 217, subcarrier_spacing::kHz30)); - ASSERT_DOUBLE_EQ(3704160000.0, - band_helper::get_f_ref_from_abs_freq_point_a(3655020000.0, 273, subcarrier_spacing::kHz30)); + ASSERT_DOUBLE_EQ(3704160000.0, get_f_ref_from_abs_freq_point_a(3697320000.0, 38, subcarrier_spacing::kHz30)); + ASSERT_DOUBLE_EQ(3704160000.0, get_f_ref_from_abs_freq_point_a(3694980000.0, 51, subcarrier_spacing::kHz30)); + ASSERT_DOUBLE_EQ(3704160000.0, get_f_ref_from_abs_freq_point_a(3690120000.0, 78, subcarrier_spacing::kHz30)); + ASSERT_DOUBLE_EQ(3704160000.0, get_f_ref_from_abs_freq_point_a(3680220000.0, 133, subcarrier_spacing::kHz30)); + ASSERT_DOUBLE_EQ(3704160000.0, get_f_ref_from_abs_freq_point_a(3665100000.0, 217, subcarrier_spacing::kHz30)); + ASSERT_DOUBLE_EQ(3704160000.0, get_f_ref_from_abs_freq_point_a(3655020000.0, 273, subcarrier_spacing::kHz30)); } TEST(test_get_f_req_from_f_req_point_a, scs_kHz60) { // Band n41, BWs are {40MHz, 50MHz, 60MHz, 70MHz, 80MHz, 90MHz, 100MHz}. - ASSERT_DOUBLE_EQ(2689980000.0, - band_helper::get_f_ref_from_abs_freq_point_a(2671620000.0, 51, subcarrier_spacing::kHz60)); - ASSERT_DOUBLE_EQ(2689980000.0, - band_helper::get_f_ref_from_abs_freq_point_a(2666580000.0, 65, subcarrier_spacing::kHz60)); - ASSERT_DOUBLE_EQ(2689980000.0, - band_helper::get_f_ref_from_abs_freq_point_a(2661540000.0, 79, subcarrier_spacing::kHz60)); - ASSERT_DOUBLE_EQ(2689980000.0, - band_helper::get_f_ref_from_abs_freq_point_a(2656500000.0, 93, subcarrier_spacing::kHz60)); - ASSERT_DOUBLE_EQ(2689980000.0, - band_helper::get_f_ref_from_abs_freq_point_a(2651460000.0, 107, subcarrier_spacing::kHz60)); - ASSERT_DOUBLE_EQ(2689980000.0, - band_helper::get_f_ref_from_abs_freq_point_a(2646420000.0, 121, subcarrier_spacing::kHz60)); - ASSERT_DOUBLE_EQ(2689980000.0, - band_helper::get_f_ref_from_abs_freq_point_a(2641380000.0, 135, subcarrier_spacing::kHz60)); + ASSERT_DOUBLE_EQ(2689980000.0, get_f_ref_from_abs_freq_point_a(2671620000.0, 51, subcarrier_spacing::kHz60)); + ASSERT_DOUBLE_EQ(2689980000.0, get_f_ref_from_abs_freq_point_a(2666580000.0, 65, subcarrier_spacing::kHz60)); + ASSERT_DOUBLE_EQ(2689980000.0, get_f_ref_from_abs_freq_point_a(2661540000.0, 79, subcarrier_spacing::kHz60)); + ASSERT_DOUBLE_EQ(2689980000.0, get_f_ref_from_abs_freq_point_a(2656500000.0, 93, subcarrier_spacing::kHz60)); + ASSERT_DOUBLE_EQ(2689980000.0, get_f_ref_from_abs_freq_point_a(2651460000.0, 107, subcarrier_spacing::kHz60)); + ASSERT_DOUBLE_EQ(2689980000.0, get_f_ref_from_abs_freq_point_a(2646420000.0, 121, subcarrier_spacing::kHz60)); + ASSERT_DOUBLE_EQ(2689980000.0, get_f_ref_from_abs_freq_point_a(2641380000.0, 135, subcarrier_spacing::kHz60)); // Band n78, BWs that are tested are {10MHz, 40MHz, 60MHz, 70MHz, 90MHz}. - ASSERT_DOUBLE_EQ(3704160000.0, - band_helper::get_f_ref_from_abs_freq_point_a(3700200000.0, 11, subcarrier_spacing::kHz60)); - ASSERT_DOUBLE_EQ(3704160000.0, - band_helper::get_f_ref_from_abs_freq_point_a(3685800000.0, 51, subcarrier_spacing::kHz60)); - ASSERT_DOUBLE_EQ(3704160000.0, - band_helper::get_f_ref_from_abs_freq_point_a(3675720000.0, 79, subcarrier_spacing::kHz60)); - ASSERT_DOUBLE_EQ(3704160000.0, - band_helper::get_f_ref_from_abs_freq_point_a(3670680000.0, 93, subcarrier_spacing::kHz60)); - ASSERT_DOUBLE_EQ(3704160000.0, - band_helper::get_f_ref_from_abs_freq_point_a(3660600000.0, 121, subcarrier_spacing::kHz60)); + ASSERT_DOUBLE_EQ(3704160000.0, get_f_ref_from_abs_freq_point_a(3700200000.0, 11, subcarrier_spacing::kHz60)); + ASSERT_DOUBLE_EQ(3704160000.0, get_f_ref_from_abs_freq_point_a(3685800000.0, 51, subcarrier_spacing::kHz60)); + ASSERT_DOUBLE_EQ(3704160000.0, get_f_ref_from_abs_freq_point_a(3675720000.0, 79, subcarrier_spacing::kHz60)); + ASSERT_DOUBLE_EQ(3704160000.0, get_f_ref_from_abs_freq_point_a(3670680000.0, 93, subcarrier_spacing::kHz60)); + ASSERT_DOUBLE_EQ(3704160000.0, get_f_ref_from_abs_freq_point_a(3660600000.0, 121, subcarrier_spacing::kHz60)); } TEST(test_get_n_rbs_from_bw, scs_15kHz) { - ASSERT_EQ( - 25, band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz5, subcarrier_spacing::kHz15, frequency_range::FR1)); - ASSERT_EQ( - 52, band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz10, subcarrier_spacing::kHz15, frequency_range::FR1)); - ASSERT_EQ( - 79, band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz15, subcarrier_spacing::kHz15, frequency_range::FR1)); - ASSERT_EQ( - 106, - band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz20, subcarrier_spacing::kHz15, frequency_range::FR1)); - ASSERT_EQ( - 133, - band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz25, subcarrier_spacing::kHz15, frequency_range::FR1)); - ASSERT_EQ( - 160, - band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz30, subcarrier_spacing::kHz15, frequency_range::FR1)); - ASSERT_EQ( - 188, - band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz35, subcarrier_spacing::kHz15, frequency_range::FR1)); - ASSERT_EQ( - 216, - band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz40, subcarrier_spacing::kHz15, frequency_range::FR1)); - ASSERT_EQ( - 242, - band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz45, subcarrier_spacing::kHz15, frequency_range::FR1)); - ASSERT_EQ( - 270, - band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz50, subcarrier_spacing::kHz15, frequency_range::FR1)); - ASSERT_EQ( - 0, band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz60, subcarrier_spacing::kHz15, frequency_range::FR1)); + ASSERT_EQ(25, get_n_rbs_from_bw(bs_channel_bandwidth::MHz5, subcarrier_spacing::kHz15, frequency_range::FR1)); + ASSERT_EQ(52, get_n_rbs_from_bw(bs_channel_bandwidth::MHz10, subcarrier_spacing::kHz15, frequency_range::FR1)); + ASSERT_EQ(79, get_n_rbs_from_bw(bs_channel_bandwidth::MHz15, subcarrier_spacing::kHz15, frequency_range::FR1)); + ASSERT_EQ(106, get_n_rbs_from_bw(bs_channel_bandwidth::MHz20, subcarrier_spacing::kHz15, frequency_range::FR1)); + ASSERT_EQ(133, get_n_rbs_from_bw(bs_channel_bandwidth::MHz25, subcarrier_spacing::kHz15, frequency_range::FR1)); + ASSERT_EQ(160, get_n_rbs_from_bw(bs_channel_bandwidth::MHz30, subcarrier_spacing::kHz15, frequency_range::FR1)); + ASSERT_EQ(188, get_n_rbs_from_bw(bs_channel_bandwidth::MHz35, subcarrier_spacing::kHz15, frequency_range::FR1)); + ASSERT_EQ(216, get_n_rbs_from_bw(bs_channel_bandwidth::MHz40, subcarrier_spacing::kHz15, frequency_range::FR1)); + ASSERT_EQ(242, get_n_rbs_from_bw(bs_channel_bandwidth::MHz45, subcarrier_spacing::kHz15, frequency_range::FR1)); + ASSERT_EQ(270, get_n_rbs_from_bw(bs_channel_bandwidth::MHz50, subcarrier_spacing::kHz15, frequency_range::FR1)); + ASSERT_EQ(0, get_n_rbs_from_bw(bs_channel_bandwidth::MHz60, subcarrier_spacing::kHz15, frequency_range::FR1)); } TEST(test_get_n_rbs_from_bw, scs_30kHz) { - ASSERT_EQ( - 11, band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz5, subcarrier_spacing::kHz30, frequency_range::FR1)); - ASSERT_EQ( - 24, band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz10, subcarrier_spacing::kHz30, frequency_range::FR1)); - ASSERT_EQ( - 38, band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz15, subcarrier_spacing::kHz30, frequency_range::FR1)); - ASSERT_EQ( - 51, band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz20, subcarrier_spacing::kHz30, frequency_range::FR1)); - ASSERT_EQ( - 65, band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz25, subcarrier_spacing::kHz30, frequency_range::FR1)); - ASSERT_EQ( - 78, band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz30, subcarrier_spacing::kHz30, frequency_range::FR1)); - ASSERT_EQ( - 92, band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz35, subcarrier_spacing::kHz30, frequency_range::FR1)); - ASSERT_EQ( - 106, - band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz40, subcarrier_spacing::kHz30, frequency_range::FR1)); - ASSERT_EQ( - 119, - band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz45, subcarrier_spacing::kHz30, frequency_range::FR1)); - ASSERT_EQ( - 133, - band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz50, subcarrier_spacing::kHz30, frequency_range::FR1)); - ASSERT_EQ( - 162, - band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz60, subcarrier_spacing::kHz30, frequency_range::FR1)); - ASSERT_EQ( - 189, - band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz70, subcarrier_spacing::kHz30, frequency_range::FR1)); - ASSERT_EQ( - 217, - band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz80, subcarrier_spacing::kHz30, frequency_range::FR1)); - ASSERT_EQ( - 245, - band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz90, subcarrier_spacing::kHz30, frequency_range::FR1)); - ASSERT_EQ( - 273, - band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz100, subcarrier_spacing::kHz30, frequency_range::FR1)); + ASSERT_EQ(11, get_n_rbs_from_bw(bs_channel_bandwidth::MHz5, subcarrier_spacing::kHz30, frequency_range::FR1)); + ASSERT_EQ(24, get_n_rbs_from_bw(bs_channel_bandwidth::MHz10, subcarrier_spacing::kHz30, frequency_range::FR1)); + ASSERT_EQ(38, get_n_rbs_from_bw(bs_channel_bandwidth::MHz15, subcarrier_spacing::kHz30, frequency_range::FR1)); + ASSERT_EQ(51, get_n_rbs_from_bw(bs_channel_bandwidth::MHz20, subcarrier_spacing::kHz30, frequency_range::FR1)); + ASSERT_EQ(65, get_n_rbs_from_bw(bs_channel_bandwidth::MHz25, subcarrier_spacing::kHz30, frequency_range::FR1)); + ASSERT_EQ(78, get_n_rbs_from_bw(bs_channel_bandwidth::MHz30, subcarrier_spacing::kHz30, frequency_range::FR1)); + ASSERT_EQ(92, get_n_rbs_from_bw(bs_channel_bandwidth::MHz35, subcarrier_spacing::kHz30, frequency_range::FR1)); + ASSERT_EQ(106, get_n_rbs_from_bw(bs_channel_bandwidth::MHz40, subcarrier_spacing::kHz30, frequency_range::FR1)); + ASSERT_EQ(119, get_n_rbs_from_bw(bs_channel_bandwidth::MHz45, subcarrier_spacing::kHz30, frequency_range::FR1)); + ASSERT_EQ(133, get_n_rbs_from_bw(bs_channel_bandwidth::MHz50, subcarrier_spacing::kHz30, frequency_range::FR1)); + ASSERT_EQ(162, get_n_rbs_from_bw(bs_channel_bandwidth::MHz60, subcarrier_spacing::kHz30, frequency_range::FR1)); + ASSERT_EQ(189, get_n_rbs_from_bw(bs_channel_bandwidth::MHz70, subcarrier_spacing::kHz30, frequency_range::FR1)); + ASSERT_EQ(217, get_n_rbs_from_bw(bs_channel_bandwidth::MHz80, subcarrier_spacing::kHz30, frequency_range::FR1)); + ASSERT_EQ(245, get_n_rbs_from_bw(bs_channel_bandwidth::MHz90, subcarrier_spacing::kHz30, frequency_range::FR1)); + ASSERT_EQ(273, get_n_rbs_from_bw(bs_channel_bandwidth::MHz100, subcarrier_spacing::kHz30, frequency_range::FR1)); } TEST(test_get_n_rbs_from_bw, scs_60kHz) { - ASSERT_EQ( - 0, band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz5, subcarrier_spacing::kHz60, frequency_range::FR1)); - ASSERT_EQ( - 11, band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz10, subcarrier_spacing::kHz60, frequency_range::FR1)); - ASSERT_EQ( - 18, band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz15, subcarrier_spacing::kHz60, frequency_range::FR1)); - ASSERT_EQ( - 24, band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz20, subcarrier_spacing::kHz60, frequency_range::FR1)); - ASSERT_EQ( - 31, band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz25, subcarrier_spacing::kHz60, frequency_range::FR1)); - ASSERT_EQ( - 38, band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz30, subcarrier_spacing::kHz60, frequency_range::FR1)); - ASSERT_EQ( - 44, band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz35, subcarrier_spacing::kHz60, frequency_range::FR1)); - ASSERT_EQ( - 51, band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz40, subcarrier_spacing::kHz60, frequency_range::FR1)); - ASSERT_EQ( - 58, band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz45, subcarrier_spacing::kHz60, frequency_range::FR1)); - ASSERT_EQ( - 65, band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz50, subcarrier_spacing::kHz60, frequency_range::FR1)); - ASSERT_EQ( - 79, band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz60, subcarrier_spacing::kHz60, frequency_range::FR1)); - ASSERT_EQ( - 93, band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz70, subcarrier_spacing::kHz60, frequency_range::FR1)); - ASSERT_EQ( - 107, - band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz80, subcarrier_spacing::kHz60, frequency_range::FR1)); - ASSERT_EQ( - 121, - band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz90, subcarrier_spacing::kHz60, frequency_range::FR1)); - ASSERT_EQ( - 135, - band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz100, subcarrier_spacing::kHz60, frequency_range::FR1)); + ASSERT_EQ(0, get_n_rbs_from_bw(bs_channel_bandwidth::MHz5, subcarrier_spacing::kHz60, frequency_range::FR1)); + ASSERT_EQ(11, get_n_rbs_from_bw(bs_channel_bandwidth::MHz10, subcarrier_spacing::kHz60, frequency_range::FR1)); + ASSERT_EQ(18, get_n_rbs_from_bw(bs_channel_bandwidth::MHz15, subcarrier_spacing::kHz60, frequency_range::FR1)); + ASSERT_EQ(24, get_n_rbs_from_bw(bs_channel_bandwidth::MHz20, subcarrier_spacing::kHz60, frequency_range::FR1)); + ASSERT_EQ(31, get_n_rbs_from_bw(bs_channel_bandwidth::MHz25, subcarrier_spacing::kHz60, frequency_range::FR1)); + ASSERT_EQ(38, get_n_rbs_from_bw(bs_channel_bandwidth::MHz30, subcarrier_spacing::kHz60, frequency_range::FR1)); + ASSERT_EQ(44, get_n_rbs_from_bw(bs_channel_bandwidth::MHz35, subcarrier_spacing::kHz60, frequency_range::FR1)); + ASSERT_EQ(51, get_n_rbs_from_bw(bs_channel_bandwidth::MHz40, subcarrier_spacing::kHz60, frequency_range::FR1)); + ASSERT_EQ(58, get_n_rbs_from_bw(bs_channel_bandwidth::MHz45, subcarrier_spacing::kHz60, frequency_range::FR1)); + ASSERT_EQ(65, get_n_rbs_from_bw(bs_channel_bandwidth::MHz50, subcarrier_spacing::kHz60, frequency_range::FR1)); + ASSERT_EQ(79, get_n_rbs_from_bw(bs_channel_bandwidth::MHz60, subcarrier_spacing::kHz60, frequency_range::FR1)); + ASSERT_EQ(93, get_n_rbs_from_bw(bs_channel_bandwidth::MHz70, subcarrier_spacing::kHz60, frequency_range::FR1)); + ASSERT_EQ(107, get_n_rbs_from_bw(bs_channel_bandwidth::MHz80, subcarrier_spacing::kHz60, frequency_range::FR1)); + ASSERT_EQ(121, get_n_rbs_from_bw(bs_channel_bandwidth::MHz90, subcarrier_spacing::kHz60, frequency_range::FR1)); + ASSERT_EQ(135, get_n_rbs_from_bw(bs_channel_bandwidth::MHz100, subcarrier_spacing::kHz60, frequency_range::FR1)); } TEST(test_get_n_rbs_from_bw, scs_120kHz_fr2) { - ASSERT_EQ( - 66, band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz50, subcarrier_spacing::kHz60, frequency_range::FR2)); - ASSERT_EQ( - 132, - band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz100, subcarrier_spacing::kHz60, frequency_range::FR2)); - ASSERT_EQ( - 264, - band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz200, subcarrier_spacing::kHz60, frequency_range::FR2)); + ASSERT_EQ(66, get_n_rbs_from_bw(bs_channel_bandwidth::MHz50, subcarrier_spacing::kHz60, frequency_range::FR2)); + ASSERT_EQ(132, get_n_rbs_from_bw(bs_channel_bandwidth::MHz100, subcarrier_spacing::kHz60, frequency_range::FR2)); + ASSERT_EQ(264, get_n_rbs_from_bw(bs_channel_bandwidth::MHz200, subcarrier_spacing::kHz60, frequency_range::FR2)); } TEST(test_get_n_rbs_from_bw, scs_120kHz) { - ASSERT_EQ( - 32, - band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz50, subcarrier_spacing::kHz120, frequency_range::FR2)); - ASSERT_EQ( - 66, - band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz100, subcarrier_spacing::kHz120, frequency_range::FR2)); - ASSERT_EQ( - 132, - band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz200, subcarrier_spacing::kHz120, frequency_range::FR2)); - ASSERT_EQ( - 264, - band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz400, subcarrier_spacing::kHz120, frequency_range::FR2)); + ASSERT_EQ(32, get_n_rbs_from_bw(bs_channel_bandwidth::MHz50, subcarrier_spacing::kHz120, frequency_range::FR2)); + ASSERT_EQ(66, get_n_rbs_from_bw(bs_channel_bandwidth::MHz100, subcarrier_spacing::kHz120, frequency_range::FR2)); + ASSERT_EQ(132, get_n_rbs_from_bw(bs_channel_bandwidth::MHz200, subcarrier_spacing::kHz120, frequency_range::FR2)); + ASSERT_EQ(264, get_n_rbs_from_bw(bs_channel_bandwidth::MHz400, subcarrier_spacing::kHz120, frequency_range::FR2)); } TEST(test_get_n_rbs_from_bw, invalid_cases) { - ASSERT_EQ( - 0, band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz200, subcarrier_spacing::kHz60, frequency_range::FR1)); - ASSERT_EQ( - 0, band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz400, subcarrier_spacing::kHz60, frequency_range::FR2)); - ASSERT_EQ( - 0, band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz400, subcarrier_spacing::kHz15, frequency_range::FR2)); - ASSERT_EQ( - 0, - band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz400, subcarrier_spacing::kHz240, frequency_range::FR2)); - ASSERT_EQ( - 0, - band_helper::get_n_rbs_from_bw(bs_channel_bandwidth::MHz100, subcarrier_spacing::kHz120, frequency_range::FR1)); + ASSERT_EQ(0, get_n_rbs_from_bw(bs_channel_bandwidth::MHz200, subcarrier_spacing::kHz60, frequency_range::FR1)); + ASSERT_EQ(0, get_n_rbs_from_bw(bs_channel_bandwidth::MHz400, subcarrier_spacing::kHz60, frequency_range::FR2)); + ASSERT_EQ(0, get_n_rbs_from_bw(bs_channel_bandwidth::MHz400, subcarrier_spacing::kHz15, frequency_range::FR2)); + ASSERT_EQ(0, get_n_rbs_from_bw(bs_channel_bandwidth::MHz400, subcarrier_spacing::kHz240, frequency_range::FR2)); + ASSERT_EQ(0, get_n_rbs_from_bw(bs_channel_bandwidth::MHz100, subcarrier_spacing::kHz120, frequency_range::FR1)); } TEST(test_get_min_channel_bw, invalid_cases) { - ASSERT_EQ(min_channel_bandwidth::MHz5, band_helper::get_min_channel_bw(nr_band::n1, subcarrier_spacing::kHz15)); - ASSERT_EQ(min_channel_bandwidth::MHz10, band_helper::get_min_channel_bw(nr_band::n1, subcarrier_spacing::kHz30)); - ASSERT_EQ(min_channel_bandwidth::MHz10, band_helper::get_min_channel_bw(nr_band::n1, subcarrier_spacing::kHz60)); - ASSERT_EQ(min_channel_bandwidth::invalid, band_helper::get_min_channel_bw(nr_band::n1, subcarrier_spacing::kHz120)); - - ASSERT_EQ(min_channel_bandwidth::MHz5, band_helper::get_min_channel_bw(nr_band::n3, subcarrier_spacing::kHz15)); - ASSERT_EQ(min_channel_bandwidth::MHz10, band_helper::get_min_channel_bw(nr_band::n3, subcarrier_spacing::kHz30)); - ASSERT_EQ(min_channel_bandwidth::MHz10, band_helper::get_min_channel_bw(nr_band::n3, subcarrier_spacing::kHz60)); - ASSERT_EQ(min_channel_bandwidth::invalid, band_helper::get_min_channel_bw(nr_band::n3, subcarrier_spacing::kHz120)); - - ASSERT_EQ(min_channel_bandwidth::MHz5, band_helper::get_min_channel_bw(nr_band::n7, subcarrier_spacing::kHz15)); - ASSERT_EQ(min_channel_bandwidth::MHz10, band_helper::get_min_channel_bw(nr_band::n7, subcarrier_spacing::kHz30)); - ASSERT_EQ(min_channel_bandwidth::MHz10, band_helper::get_min_channel_bw(nr_band::n7, subcarrier_spacing::kHz60)); - ASSERT_EQ(min_channel_bandwidth::invalid, band_helper::get_min_channel_bw(nr_band::n7, subcarrier_spacing::kHz120)); - - ASSERT_EQ(min_channel_bandwidth::MHz5, band_helper::get_min_channel_bw(nr_band::n20, subcarrier_spacing::kHz15)); - ASSERT_EQ(min_channel_bandwidth::MHz10, band_helper::get_min_channel_bw(nr_band::n20, subcarrier_spacing::kHz30)); - ASSERT_EQ(min_channel_bandwidth::invalid, band_helper::get_min_channel_bw(nr_band::n20, subcarrier_spacing::kHz60)); - - ASSERT_EQ(min_channel_bandwidth::MHz5, band_helper::get_min_channel_bw(nr_band::n76, subcarrier_spacing::kHz15)); - ASSERT_EQ(min_channel_bandwidth::invalid, band_helper::get_min_channel_bw(nr_band::n76, subcarrier_spacing::kHz30)); - - ASSERT_EQ(min_channel_bandwidth::MHz10, band_helper::get_min_channel_bw(nr_band::n78, subcarrier_spacing::kHz15)); - ASSERT_EQ(min_channel_bandwidth::MHz10, band_helper::get_min_channel_bw(nr_band::n78, subcarrier_spacing::kHz30)); - ASSERT_EQ(min_channel_bandwidth::MHz10, band_helper::get_min_channel_bw(nr_band::n78, subcarrier_spacing::kHz60)); - ASSERT_EQ(min_channel_bandwidth::invalid, band_helper::get_min_channel_bw(nr_band::n78, subcarrier_spacing::kHz120)); - - ASSERT_EQ(min_channel_bandwidth::MHz10, band_helper::get_min_channel_bw(nr_band::n79, subcarrier_spacing::kHz15)); - ASSERT_EQ(min_channel_bandwidth::MHz10, band_helper::get_min_channel_bw(nr_band::n79, subcarrier_spacing::kHz30)); - ASSERT_EQ(min_channel_bandwidth::MHz10, band_helper::get_min_channel_bw(nr_band::n79, subcarrier_spacing::kHz60)); - ASSERT_EQ(min_channel_bandwidth::invalid, band_helper::get_min_channel_bw(nr_band::n79, subcarrier_spacing::kHz120)); - - ASSERT_EQ(min_channel_bandwidth::MHz20, band_helper::get_min_channel_bw(nr_band::n102, subcarrier_spacing::kHz15)); - ASSERT_EQ(min_channel_bandwidth::MHz20, band_helper::get_min_channel_bw(nr_band::n102, subcarrier_spacing::kHz30)); - ASSERT_EQ(min_channel_bandwidth::MHz20, band_helper::get_min_channel_bw(nr_band::n102, subcarrier_spacing::kHz60)); - ASSERT_EQ(min_channel_bandwidth::invalid, band_helper::get_min_channel_bw(nr_band::n102, subcarrier_spacing::kHz120)); - - ASSERT_EQ(min_channel_bandwidth::MHz50, band_helper::get_min_channel_bw(nr_band::n257, subcarrier_spacing::kHz60)); - ASSERT_EQ(min_channel_bandwidth::MHz50, band_helper::get_min_channel_bw(nr_band::n258, subcarrier_spacing::kHz60)); - ASSERT_EQ(min_channel_bandwidth::MHz50, band_helper::get_min_channel_bw(nr_band::n260, subcarrier_spacing::kHz60)); - ASSERT_EQ(min_channel_bandwidth::MHz50, band_helper::get_min_channel_bw(nr_band::n261, subcarrier_spacing::kHz60)); - ASSERT_EQ(min_channel_bandwidth::MHz50, band_helper::get_min_channel_bw(nr_band::n257, subcarrier_spacing::kHz120)); - ASSERT_EQ(min_channel_bandwidth::MHz50, band_helper::get_min_channel_bw(nr_band::n258, subcarrier_spacing::kHz120)); - ASSERT_EQ(min_channel_bandwidth::MHz50, band_helper::get_min_channel_bw(nr_band::n260, subcarrier_spacing::kHz120)); - ASSERT_EQ(min_channel_bandwidth::MHz50, band_helper::get_min_channel_bw(nr_band::n261, subcarrier_spacing::kHz120)); - ASSERT_EQ(min_channel_bandwidth::invalid, band_helper::get_min_channel_bw(nr_band::n257, subcarrier_spacing::kHz15)); - ASSERT_EQ(min_channel_bandwidth::invalid, band_helper::get_min_channel_bw(nr_band::n258, subcarrier_spacing::kHz15)); - ASSERT_EQ(min_channel_bandwidth::invalid, band_helper::get_min_channel_bw(nr_band::n260, subcarrier_spacing::kHz15)); - ASSERT_EQ(min_channel_bandwidth::invalid, band_helper::get_min_channel_bw(nr_band::n261, subcarrier_spacing::kHz15)); + ASSERT_EQ(min_channel_bandwidth::MHz5, get_min_channel_bw(nr_band::n1, subcarrier_spacing::kHz15)); + ASSERT_EQ(min_channel_bandwidth::MHz10, get_min_channel_bw(nr_band::n1, subcarrier_spacing::kHz30)); + ASSERT_EQ(min_channel_bandwidth::MHz10, get_min_channel_bw(nr_band::n1, subcarrier_spacing::kHz60)); + ASSERT_EQ(min_channel_bandwidth::invalid, get_min_channel_bw(nr_band::n1, subcarrier_spacing::kHz120)); + + ASSERT_EQ(min_channel_bandwidth::MHz5, get_min_channel_bw(nr_band::n3, subcarrier_spacing::kHz15)); + ASSERT_EQ(min_channel_bandwidth::MHz10, get_min_channel_bw(nr_band::n3, subcarrier_spacing::kHz30)); + ASSERT_EQ(min_channel_bandwidth::MHz10, get_min_channel_bw(nr_band::n3, subcarrier_spacing::kHz60)); + ASSERT_EQ(min_channel_bandwidth::invalid, get_min_channel_bw(nr_band::n3, subcarrier_spacing::kHz120)); + + ASSERT_EQ(min_channel_bandwidth::MHz5, get_min_channel_bw(nr_band::n7, subcarrier_spacing::kHz15)); + ASSERT_EQ(min_channel_bandwidth::MHz10, get_min_channel_bw(nr_band::n7, subcarrier_spacing::kHz30)); + ASSERT_EQ(min_channel_bandwidth::MHz10, get_min_channel_bw(nr_band::n7, subcarrier_spacing::kHz60)); + ASSERT_EQ(min_channel_bandwidth::invalid, get_min_channel_bw(nr_band::n7, subcarrier_spacing::kHz120)); + + ASSERT_EQ(min_channel_bandwidth::MHz5, get_min_channel_bw(nr_band::n20, subcarrier_spacing::kHz15)); + ASSERT_EQ(min_channel_bandwidth::MHz10, get_min_channel_bw(nr_band::n20, subcarrier_spacing::kHz30)); + ASSERT_EQ(min_channel_bandwidth::invalid, get_min_channel_bw(nr_band::n20, subcarrier_spacing::kHz60)); + + ASSERT_EQ(min_channel_bandwidth::MHz5, get_min_channel_bw(nr_band::n76, subcarrier_spacing::kHz15)); + ASSERT_EQ(min_channel_bandwidth::invalid, get_min_channel_bw(nr_band::n76, subcarrier_spacing::kHz30)); + + ASSERT_EQ(min_channel_bandwidth::MHz10, get_min_channel_bw(nr_band::n78, subcarrier_spacing::kHz15)); + ASSERT_EQ(min_channel_bandwidth::MHz10, get_min_channel_bw(nr_band::n78, subcarrier_spacing::kHz30)); + ASSERT_EQ(min_channel_bandwidth::MHz10, get_min_channel_bw(nr_band::n78, subcarrier_spacing::kHz60)); + ASSERT_EQ(min_channel_bandwidth::invalid, get_min_channel_bw(nr_band::n78, subcarrier_spacing::kHz120)); + + ASSERT_EQ(min_channel_bandwidth::MHz10, get_min_channel_bw(nr_band::n79, subcarrier_spacing::kHz15)); + ASSERT_EQ(min_channel_bandwidth::MHz10, get_min_channel_bw(nr_band::n79, subcarrier_spacing::kHz30)); + ASSERT_EQ(min_channel_bandwidth::MHz10, get_min_channel_bw(nr_band::n79, subcarrier_spacing::kHz60)); + ASSERT_EQ(min_channel_bandwidth::invalid, get_min_channel_bw(nr_band::n79, subcarrier_spacing::kHz120)); + + ASSERT_EQ(min_channel_bandwidth::MHz20, get_min_channel_bw(nr_band::n102, subcarrier_spacing::kHz15)); + ASSERT_EQ(min_channel_bandwidth::MHz20, get_min_channel_bw(nr_band::n102, subcarrier_spacing::kHz30)); + ASSERT_EQ(min_channel_bandwidth::MHz20, get_min_channel_bw(nr_band::n102, subcarrier_spacing::kHz60)); + ASSERT_EQ(min_channel_bandwidth::invalid, get_min_channel_bw(nr_band::n102, subcarrier_spacing::kHz120)); + + ASSERT_EQ(min_channel_bandwidth::MHz50, get_min_channel_bw(nr_band::n257, subcarrier_spacing::kHz60)); + ASSERT_EQ(min_channel_bandwidth::MHz50, get_min_channel_bw(nr_band::n258, subcarrier_spacing::kHz60)); + ASSERT_EQ(min_channel_bandwidth::MHz50, get_min_channel_bw(nr_band::n260, subcarrier_spacing::kHz60)); + ASSERT_EQ(min_channel_bandwidth::MHz50, get_min_channel_bw(nr_band::n261, subcarrier_spacing::kHz60)); + ASSERT_EQ(min_channel_bandwidth::MHz50, get_min_channel_bw(nr_band::n257, subcarrier_spacing::kHz120)); + ASSERT_EQ(min_channel_bandwidth::MHz50, get_min_channel_bw(nr_band::n258, subcarrier_spacing::kHz120)); + ASSERT_EQ(min_channel_bandwidth::MHz50, get_min_channel_bw(nr_band::n260, subcarrier_spacing::kHz120)); + ASSERT_EQ(min_channel_bandwidth::MHz50, get_min_channel_bw(nr_band::n261, subcarrier_spacing::kHz120)); + ASSERT_EQ(min_channel_bandwidth::invalid, get_min_channel_bw(nr_band::n257, subcarrier_spacing::kHz15)); + ASSERT_EQ(min_channel_bandwidth::invalid, get_min_channel_bw(nr_band::n258, subcarrier_spacing::kHz15)); + ASSERT_EQ(min_channel_bandwidth::invalid, get_min_channel_bw(nr_band::n260, subcarrier_spacing::kHz15)); + ASSERT_EQ(min_channel_bandwidth::invalid, get_min_channel_bw(nr_band::n261, subcarrier_spacing::kHz15)); } TEST(test_is_valid_ssb_arfcn, mixed_bands) { // ARFCN 427970 is a valid SSB ARFCN for n1, expect no error. - ASSERT_TRUE(band_helper::is_ssb_arfcn_valid_given_band(427970U, nr_band::n1, subcarrier_spacing::kHz15).has_value()); + ASSERT_TRUE(is_ssb_arfcn_valid_given_band(427970U, nr_band::n1, subcarrier_spacing::kHz15).has_value()); // ARFCN 433970 is NOT a valid SSB ARFCN for n1, expect an error. - ASSERT_FALSE(band_helper::is_ssb_arfcn_valid_given_band(433970U, nr_band::n1, subcarrier_spacing::kHz15).has_value()); + ASSERT_FALSE(is_ssb_arfcn_valid_given_band(433970U, nr_band::n1, subcarrier_spacing::kHz15).has_value()); // ARFCN 427970 is a valid SSB ARFCN for n1, expect no error. - ASSERT_TRUE(band_helper::is_ssb_arfcn_valid_given_band(755712U, nr_band::n46, subcarrier_spacing::kHz30).has_value()); + ASSERT_TRUE(is_ssb_arfcn_valid_given_band(755712U, nr_band::n46, subcarrier_spacing::kHz30).has_value()); // ARFCN 433970 is NOT a valid SSB ARFCN for n1, expect an error. - ASSERT_FALSE( - band_helper::is_ssb_arfcn_valid_given_band(785856U, nr_band::n46, subcarrier_spacing::kHz30).has_value()); + ASSERT_FALSE(is_ssb_arfcn_valid_given_band(785856U, nr_band::n46, subcarrier_spacing::kHz30).has_value()); } TEST(test_get_ssb_l_max, test_different_ssb_cases) { // Case A. - ASSERT_EQ(4, band_helper::get_ssb_l_max(nr_band::n3, subcarrier_spacing::kHz15, 363640U)); + ASSERT_EQ(4, get_ssb_l_max(nr_band::n3, subcarrier_spacing::kHz15, 363640U)); // Case B. - ASSERT_EQ(8, band_helper::get_ssb_l_max(nr_band::n5, subcarrier_spacing::kHz30, 176800U)); + ASSERT_EQ(8, get_ssb_l_max(nr_band::n5, subcarrier_spacing::kHz30, 176800U)); // Case C, f < f_cutoff. - ASSERT_EQ(4, band_helper::get_ssb_l_max(nr_band::n50, subcarrier_spacing::kHz30, 296400U)); + ASSERT_EQ(4, get_ssb_l_max(nr_band::n50, subcarrier_spacing::kHz30, 296400U)); // Case C, f > f_cutoff. - ASSERT_EQ(8, band_helper::get_ssb_l_max(nr_band::n104, subcarrier_spacing::kHz30, 860000U)); + ASSERT_EQ(8, get_ssb_l_max(nr_band::n104, subcarrier_spacing::kHz30, 860000U)); +} + +TEST(test_dl_arfcn_validator, test_different_fdd_bands) +{ + // Get a random raster point from the band raster. If outside_raster is true, the function returns a point outside the + // raster. + auto get_rnd_raster_point = [](span raster, bool outside_raster = false) -> uint32_t { + const uint32_t raster_step = raster[1]; + if (outside_raster) { + return test_rgen::bernoulli(0.5) ? raster.front() - raster_step : raster.back() + raster_step; + } + const unsigned nof_raster_points = (raster.back() - raster.front()) / raster_step + 1; + return test_rgen::uniform_int(0, nof_raster_points - 1) * raster_step + raster.front(); + }; + + // Definition of UL rasters for different bands. + constexpr std::array n1_raster = {422000, 20, 434000}; + constexpr std::array n2_raster = {386000, 20, 398000}; + constexpr std::array n3_raster = {361000, 20, 376000}; + constexpr std::array n5_raster = {173800, 20, 178800}; + constexpr std::array n7_raster = {524000, 20, 538000}; + constexpr std::array n14_raster = {151600, 20, 153600}; + constexpr std::array n28_raster = {151600, 20, 160600}; + + // FDD bands + ASSERT_TRUE(is_dl_arfcn_valid_given_band(nr_band::n1, get_rnd_raster_point(n1_raster), subcarrier_spacing::kHz15) + .has_value()); + ASSERT_FALSE( + is_dl_arfcn_valid_given_band(nr_band::n1, get_rnd_raster_point(n1_raster, true), subcarrier_spacing::kHz15) + .has_value()); + ASSERT_TRUE(is_dl_arfcn_valid_given_band(nr_band::n2, get_rnd_raster_point(n2_raster), subcarrier_spacing::kHz15) + .has_value()); + ASSERT_FALSE( + is_dl_arfcn_valid_given_band(nr_band::n2, get_rnd_raster_point(n2_raster, true), subcarrier_spacing::kHz15) + .has_value()); + ASSERT_TRUE(is_dl_arfcn_valid_given_band(nr_band::n3, get_rnd_raster_point(n3_raster), subcarrier_spacing::kHz15) + .has_value()); + ASSERT_FALSE( + is_dl_arfcn_valid_given_band(nr_band::n3, get_rnd_raster_point(n3_raster, true), subcarrier_spacing::kHz15) + .has_value()); + ASSERT_TRUE(is_dl_arfcn_valid_given_band(nr_band::n5, get_rnd_raster_point(n5_raster), subcarrier_spacing::kHz15) + .has_value()); + ASSERT_FALSE( + is_dl_arfcn_valid_given_band(nr_band::n5, get_rnd_raster_point(n5_raster, true), subcarrier_spacing::kHz15) + .has_value()); + ASSERT_TRUE(is_dl_arfcn_valid_given_band(nr_band::n7, get_rnd_raster_point(n7_raster), subcarrier_spacing::kHz15) + .has_value()); + ASSERT_FALSE( + is_dl_arfcn_valid_given_band(nr_band::n7, get_rnd_raster_point(n7_raster, true), subcarrier_spacing::kHz15) + .has_value()); + ASSERT_TRUE(is_dl_arfcn_valid_given_band(nr_band::n14, get_rnd_raster_point(n14_raster), subcarrier_spacing::kHz15) + .has_value()); + ASSERT_FALSE( + is_dl_arfcn_valid_given_band(nr_band::n14, get_rnd_raster_point(n14_raster, true), subcarrier_spacing::kHz15) + .has_value()); + ASSERT_TRUE(is_dl_arfcn_valid_given_band(nr_band::n28, get_rnd_raster_point(n28_raster), subcarrier_spacing::kHz15) + .has_value()); + ASSERT_FALSE( + is_dl_arfcn_valid_given_band(nr_band::n28, get_rnd_raster_point(n28_raster, true), subcarrier_spacing::kHz15) + .has_value()); + // Single UL ARFCN for n28, BW 40Mhz. + ASSERT_TRUE( + is_dl_arfcn_valid_given_band(nr_band::n28, 155608, subcarrier_spacing::kHz15, srsran::bs_channel_bandwidth::MHz40) + .has_value()); + ASSERT_FALSE( + is_dl_arfcn_valid_given_band(nr_band::n28, 155608, subcarrier_spacing::kHz15, srsran::bs_channel_bandwidth::MHz20) + .has_value()); +} + +TEST(test_dl_arfcn_validator, test_different_tdd_bands_with_regular_raster) +{ + // Get a random raster point from the band raster. If outside_raster is true, the function returns a point outside the + // raster. + auto get_rnd_raster_point = [](span raster, bool outside_raster = false) -> uint32_t { + const uint32_t raster_step = raster[1]; + if (outside_raster) { + return test_rgen::bernoulli(0.5) ? raster.front() - raster_step : raster.back() + raster_step; + } + const unsigned nof_raster_points = (raster.back() - raster.front()) / raster_step + 1; + return test_rgen::uniform_int(0, nof_raster_points - 1) * raster_step + raster.front(); + }; + + // Definition of UL rasters for different bands. + constexpr std::array n34_raster = {402000, 20, 405000}; + constexpr std::array n38_raster = {514000, 20, 524000}; + constexpr std::array n39_raster = {376000, 20, 384000}; + constexpr std::array n40_raster = {460000, 20, 480000}; + constexpr std::array n50_raster = {286400, 20, 303400}; + constexpr std::array n51_raster = {285400, 20, 286400}; + constexpr std::array n53_raster = {496700, 20, 499000}; + constexpr std::array n100_raster = {183880, 20, 185000}; + + // TDD bands. + ASSERT_TRUE(is_dl_arfcn_valid_given_band(nr_band::n34, get_rnd_raster_point(n34_raster), subcarrier_spacing::kHz30) + .has_value()); + ASSERT_FALSE( + is_dl_arfcn_valid_given_band(nr_band::n34, get_rnd_raster_point(n34_raster, true), subcarrier_spacing::kHz30) + .has_value()); + ASSERT_TRUE(is_dl_arfcn_valid_given_band(nr_band::n38, get_rnd_raster_point(n38_raster), subcarrier_spacing::kHz30) + .has_value()); + ASSERT_FALSE( + is_dl_arfcn_valid_given_band(nr_band::n38, get_rnd_raster_point(n38_raster, true), subcarrier_spacing::kHz30) + .has_value()); + ASSERT_TRUE(is_dl_arfcn_valid_given_band(nr_band::n39, get_rnd_raster_point(n39_raster), subcarrier_spacing::kHz30) + .has_value()); + ASSERT_FALSE( + is_dl_arfcn_valid_given_band(nr_band::n39, get_rnd_raster_point(n39_raster, true), subcarrier_spacing::kHz30) + .has_value()); + ASSERT_TRUE(is_dl_arfcn_valid_given_band(nr_band::n40, get_rnd_raster_point(n40_raster), subcarrier_spacing::kHz30) + .has_value()); + ASSERT_FALSE( + is_dl_arfcn_valid_given_band(nr_band::n40, get_rnd_raster_point(n40_raster, true), subcarrier_spacing::kHz30) + .has_value()); + ASSERT_TRUE(is_dl_arfcn_valid_given_band(nr_band::n50, get_rnd_raster_point(n50_raster), subcarrier_spacing::kHz30) + .has_value()); + ASSERT_FALSE( + is_dl_arfcn_valid_given_band(nr_band::n50, get_rnd_raster_point(n50_raster, true), subcarrier_spacing::kHz30) + .has_value()); + ASSERT_TRUE(is_dl_arfcn_valid_given_band(nr_band::n51, get_rnd_raster_point(n51_raster), subcarrier_spacing::kHz30) + .has_value()); + ASSERT_FALSE( + is_dl_arfcn_valid_given_band(nr_band::n51, get_rnd_raster_point(n51_raster, true), subcarrier_spacing::kHz30) + .has_value()); + ASSERT_TRUE(is_dl_arfcn_valid_given_band(nr_band::n53, get_rnd_raster_point(n53_raster), subcarrier_spacing::kHz30) + .has_value()); + ASSERT_FALSE( + is_dl_arfcn_valid_given_band(nr_band::n53, get_rnd_raster_point(n53_raster, true), subcarrier_spacing::kHz30) + .has_value()); + ASSERT_TRUE(is_dl_arfcn_valid_given_band(nr_band::n100, get_rnd_raster_point(n100_raster), subcarrier_spacing::kHz30) + .has_value()); + ASSERT_FALSE( + is_dl_arfcn_valid_given_band(nr_band::n100, get_rnd_raster_point(n100_raster, true), subcarrier_spacing::kHz30) + .has_value()); +} + +TEST(test_dl_arfcn_validator, test_tdd_bands_with_sparse_raster) +{ + // Band n46. + ASSERT_TRUE(is_dl_arfcn_valid_given_band( + nr_band::n46, 788668U, subcarrier_spacing::kHz15, srsran::bs_channel_bandwidth::MHz10) + .has_value()); + ASSERT_FALSE(is_dl_arfcn_valid_given_band( + nr_band::n46, 769332U, subcarrier_spacing::kHz15, srsran::bs_channel_bandwidth::MHz10) + .has_value()); + ASSERT_TRUE(is_dl_arfcn_valid_given_band( + nr_band::n46, 774668U, subcarrier_spacing::kHz15, srsran::bs_channel_bandwidth::MHz20) + .has_value()); + ASSERT_FALSE(is_dl_arfcn_valid_given_band( + nr_band::n46, 782000U, subcarrier_spacing::kHz15, srsran::bs_channel_bandwidth::MHz20) + .has_value()); + ASSERT_TRUE(is_dl_arfcn_valid_given_band( + nr_band::n46, 770000U, subcarrier_spacing::kHz15, srsran::bs_channel_bandwidth::MHz40) + .has_value()); + ASSERT_FALSE(is_dl_arfcn_valid_given_band( + nr_band::n46, 755334U, subcarrier_spacing::kHz15, srsran::bs_channel_bandwidth::MHz40) + .has_value()); + ASSERT_TRUE(is_dl_arfcn_valid_given_band( + nr_band::n46, 778668U, subcarrier_spacing::kHz15, srsran::bs_channel_bandwidth::MHz60) + .has_value()); + ASSERT_FALSE(is_dl_arfcn_valid_given_band( + nr_band::n46, 746000U, subcarrier_spacing::kHz15, srsran::bs_channel_bandwidth::MHz60) + .has_value()); + ASSERT_TRUE(is_dl_arfcn_valid_given_band( + nr_band::n46, 746000U, subcarrier_spacing::kHz15, srsran::bs_channel_bandwidth::MHz80) + .has_value()); + ASSERT_FALSE(is_dl_arfcn_valid_given_band( + nr_band::n46, 791000U, subcarrier_spacing::kHz15, srsran::bs_channel_bandwidth::MHz80) + .has_value()); + ASSERT_TRUE(is_dl_arfcn_valid_given_band( + nr_band::n46, 791000U, subcarrier_spacing::kHz15, srsran::bs_channel_bandwidth::MHz100) + .has_value()); + ASSERT_FALSE(is_dl_arfcn_valid_given_band( + nr_band::n46, 782000U, subcarrier_spacing::kHz15, srsran::bs_channel_bandwidth::MHz100) + .has_value()); + + // Band n96. + ASSERT_TRUE(is_dl_arfcn_valid_given_band( + nr_band::n96, 838332U, subcarrier_spacing::kHz30, srsran::bs_channel_bandwidth::MHz20) + .has_value()); + ASSERT_FALSE(is_dl_arfcn_valid_given_band( + nr_band::n96, 827000U, subcarrier_spacing::kHz30, srsran::bs_channel_bandwidth::MHz20) + .has_value()); + ASSERT_TRUE(is_dl_arfcn_valid_given_band( + nr_band::n96, 821668U, subcarrier_spacing::kHz30, srsran::bs_channel_bandwidth::MHz40) + .has_value()); + ASSERT_FALSE(is_dl_arfcn_valid_given_band( + nr_band::n96, 857000U, subcarrier_spacing::kHz30, srsran::bs_channel_bandwidth::MHz40) + .has_value()); + ASSERT_TRUE(is_dl_arfcn_valid_given_band( + nr_band::n96, 873000U, subcarrier_spacing::kHz30, srsran::bs_channel_bandwidth::MHz60) + .has_value()); + ASSERT_FALSE(is_dl_arfcn_valid_given_band( + nr_band::n96, 859000U, subcarrier_spacing::kHz30, srsran::bs_channel_bandwidth::MHz60) + .has_value()); + ASSERT_TRUE(is_dl_arfcn_valid_given_band( + nr_band::n96, 815000U, subcarrier_spacing::kHz30, srsran::bs_channel_bandwidth::MHz80) + .has_value()); + ASSERT_FALSE(is_dl_arfcn_valid_given_band( + nr_band::n96, 798332U, subcarrier_spacing::kHz30, srsran::bs_channel_bandwidth::MHz80) + .has_value()); + ASSERT_TRUE(is_dl_arfcn_valid_given_band( + nr_band::n96, 870332U, subcarrier_spacing::kHz30, srsran::bs_channel_bandwidth::MHz100) + .has_value()); + ASSERT_FALSE(is_dl_arfcn_valid_given_band( + nr_band::n96, 841668U, subcarrier_spacing::kHz30, srsran::bs_channel_bandwidth::MHz100) + .has_value()); + + // Band n102. + ASSERT_TRUE(is_dl_arfcn_valid_given_band( + nr_band::n102, 805000U, subcarrier_spacing::kHz30, srsran::bs_channel_bandwidth::MHz20) + .has_value()); + ASSERT_FALSE(is_dl_arfcn_valid_given_band( + nr_band::n102, 797668U, subcarrier_spacing::kHz30, srsran::bs_channel_bandwidth::MHz20) + .has_value()); + ASSERT_TRUE(is_dl_arfcn_valid_given_band( + nr_band::n102, 808332U, subcarrier_spacing::kHz30, srsran::bs_channel_bandwidth::MHz40) + .has_value()); + ASSERT_FALSE(is_dl_arfcn_valid_given_band( + nr_band::n102, 805000U, subcarrier_spacing::kHz30, srsran::bs_channel_bandwidth::MHz40) + .has_value()); + ASSERT_TRUE(is_dl_arfcn_valid_given_band( + nr_band::n102, 803668U, subcarrier_spacing::kHz30, srsran::bs_channel_bandwidth::MHz60) + .has_value()); + ASSERT_FALSE(is_dl_arfcn_valid_given_band( + nr_band::n102, 800332U, subcarrier_spacing::kHz30, srsran::bs_channel_bandwidth::MHz60) + .has_value()); + ASSERT_TRUE(is_dl_arfcn_valid_given_band( + nr_band::n102, 825668U, subcarrier_spacing::kHz30, srsran::bs_channel_bandwidth::MHz80) + .has_value()); + ASSERT_FALSE(is_dl_arfcn_valid_given_band( + nr_band::n102, 852000U, subcarrier_spacing::kHz30, srsran::bs_channel_bandwidth::MHz80) + .has_value()); + ASSERT_TRUE(is_dl_arfcn_valid_given_band( + nr_band::n102, 814332U, subcarrier_spacing::kHz30, srsran::bs_channel_bandwidth::MHz100) + .has_value()); + ASSERT_FALSE(is_dl_arfcn_valid_given_band( + nr_band::n102, 798332U, subcarrier_spacing::kHz30, srsran::bs_channel_bandwidth::MHz100) + .has_value()); +} + +TEST(test_dl_arfcn_validator, test_tdd_bands_with_scs_dependent_raster) +{ + // Get a random raster point from the band raster. If outside_raster is true, the function returns a point outside the + // raster. + auto get_rnd_raster_point = [](span raster, bool outside_raster = false) -> uint32_t { + const uint32_t raster_step = raster[1]; + if (outside_raster) { + return test_rgen::bernoulli(0.5) ? raster.front() - raster_step : raster.back() + raster_step; + } + const unsigned nof_raster_points = (raster.back() - raster.front()) / raster_step + 1; + return test_rgen::uniform_int(0, nof_raster_points - 1) * raster_step + raster.front(); + }; + + // Definition of UL rasters for different bands. + constexpr std::array n41_raster_scs_15 = {499200, 3, 537999}; + constexpr std::array n41_raster_scs_30 = {499200, 6, 537996}; + constexpr std::array n48_raster_scs_15 = {636667, 1, 646666}; + constexpr std::array n48_raster_scs_30 = {636668, 2, 646666}; + constexpr std::array n77_raster_scs_15 = {620000, 1, 680000}; + constexpr std::array n77_raster_scs_30 = {620000, 2, 680000}; + constexpr std::array n78_raster_scs_15 = {620000, 1, 653333}; + constexpr std::array n78_raster_scs_30 = {620000, 2, 653332}; + constexpr std::array n79_raster_scs_15 = {693334, 1, 733333}; + constexpr std::array n79_raster_scs_30 = {693334, 2, 733332}; + constexpr std::array n90_raster_scs_15 = {499200, 3, 537999}; + constexpr std::array n90_raster_scs_30 = {499200, 6, 537996}; + constexpr std::array n104_raster_scs_15 = {828334, 1, 875000}; + constexpr std::array n104_raster_scs_30 = {828334, 2, 875000}; + + ASSERT_TRUE( + is_dl_arfcn_valid_given_band(nr_band::n41, get_rnd_raster_point(n41_raster_scs_15), subcarrier_spacing::kHz15) + .has_value()); + ASSERT_FALSE(is_dl_arfcn_valid_given_band( + nr_band::n41, get_rnd_raster_point(n41_raster_scs_15, true), subcarrier_spacing::kHz15) + .has_value()); + ASSERT_TRUE( + is_dl_arfcn_valid_given_band(nr_band::n41, get_rnd_raster_point(n41_raster_scs_30), subcarrier_spacing::kHz30) + .has_value()); + ASSERT_FALSE(is_dl_arfcn_valid_given_band( + nr_band::n41, get_rnd_raster_point(n41_raster_scs_30, true), subcarrier_spacing::kHz30) + .has_value()); + ASSERT_TRUE( + is_dl_arfcn_valid_given_band(nr_band::n48, get_rnd_raster_point(n48_raster_scs_15), subcarrier_spacing::kHz15) + .has_value()); + ASSERT_FALSE(is_dl_arfcn_valid_given_band( + nr_band::n48, get_rnd_raster_point(n48_raster_scs_15, true), subcarrier_spacing::kHz15) + .has_value()); + ASSERT_TRUE( + is_dl_arfcn_valid_given_band(nr_band::n48, get_rnd_raster_point(n48_raster_scs_30), subcarrier_spacing::kHz30) + .has_value()); + ASSERT_FALSE(is_dl_arfcn_valid_given_band( + nr_band::n48, get_rnd_raster_point(n48_raster_scs_30, true), subcarrier_spacing::kHz30) + .has_value()); + ASSERT_TRUE( + is_dl_arfcn_valid_given_band(nr_band::n77, get_rnd_raster_point(n77_raster_scs_15), subcarrier_spacing::kHz15) + .has_value()); + ASSERT_FALSE(is_dl_arfcn_valid_given_band( + nr_band::n77, get_rnd_raster_point(n77_raster_scs_15, true), subcarrier_spacing::kHz15) + .has_value()); + ASSERT_TRUE( + is_dl_arfcn_valid_given_band(nr_band::n77, get_rnd_raster_point(n77_raster_scs_30), subcarrier_spacing::kHz30) + .has_value()); + ASSERT_FALSE(is_dl_arfcn_valid_given_band( + nr_band::n77, get_rnd_raster_point(n77_raster_scs_30, true), subcarrier_spacing::kHz30) + .has_value()); + ASSERT_TRUE( + is_dl_arfcn_valid_given_band(nr_band::n78, get_rnd_raster_point(n78_raster_scs_15), subcarrier_spacing::kHz15) + .has_value()); + ASSERT_FALSE(is_dl_arfcn_valid_given_band( + nr_band::n78, get_rnd_raster_point(n78_raster_scs_15, true), subcarrier_spacing::kHz15) + .has_value()); + ASSERT_TRUE( + is_dl_arfcn_valid_given_band(nr_band::n78, get_rnd_raster_point(n78_raster_scs_30), subcarrier_spacing::kHz30) + .has_value()); + ASSERT_FALSE(is_dl_arfcn_valid_given_band( + nr_band::n78, get_rnd_raster_point(n78_raster_scs_30, true), subcarrier_spacing::kHz30) + .has_value()); + ASSERT_TRUE( + is_dl_arfcn_valid_given_band(nr_band::n79, get_rnd_raster_point(n79_raster_scs_15), subcarrier_spacing::kHz15) + .has_value()); + ASSERT_FALSE(is_dl_arfcn_valid_given_band( + nr_band::n79, get_rnd_raster_point(n79_raster_scs_15, true), subcarrier_spacing::kHz15) + .has_value()); + ASSERT_TRUE( + is_dl_arfcn_valid_given_band(nr_band::n79, get_rnd_raster_point(n79_raster_scs_30), subcarrier_spacing::kHz30) + .has_value()); + ASSERT_FALSE(is_dl_arfcn_valid_given_band( + nr_band::n79, get_rnd_raster_point(n79_raster_scs_30, true), subcarrier_spacing::kHz30) + .has_value()); + ASSERT_TRUE( + is_dl_arfcn_valid_given_band(nr_band::n90, get_rnd_raster_point(n90_raster_scs_15), subcarrier_spacing::kHz15) + .has_value()); + ASSERT_FALSE(is_dl_arfcn_valid_given_band( + nr_band::n90, get_rnd_raster_point(n90_raster_scs_15, true), subcarrier_spacing::kHz15) + .has_value()); + ASSERT_TRUE( + is_dl_arfcn_valid_given_band(nr_band::n90, get_rnd_raster_point(n90_raster_scs_30), subcarrier_spacing::kHz30) + .has_value()); + ASSERT_FALSE(is_dl_arfcn_valid_given_band( + nr_band::n90, get_rnd_raster_point(n90_raster_scs_30, true), subcarrier_spacing::kHz30) + .has_value()); + ASSERT_TRUE( + is_dl_arfcn_valid_given_band(nr_band::n104, get_rnd_raster_point(n104_raster_scs_15), subcarrier_spacing::kHz15) + .has_value()); + ASSERT_FALSE(is_dl_arfcn_valid_given_band( + nr_band::n104, get_rnd_raster_point(n104_raster_scs_15, true), subcarrier_spacing::kHz15) + .has_value()); + ASSERT_TRUE( + is_dl_arfcn_valid_given_band(nr_band::n104, get_rnd_raster_point(n104_raster_scs_30), subcarrier_spacing::kHz30) + .has_value()); + ASSERT_FALSE(is_dl_arfcn_valid_given_band( + nr_band::n104, get_rnd_raster_point(n104_raster_scs_30, true), subcarrier_spacing::kHz30) + .has_value()); +} + +TEST(test_ul_arfcn_validator, test_different_bands) +{ + // Get a random raster point from the band raster. If outside_raster is true, the function returns a point outside the + // raster. + auto get_rnd_raster_point = [](span raster, bool outside_raster = false) -> uint32_t { + const uint32_t raster_step = raster[1]; + if (outside_raster) { + return test_rgen::bernoulli(0.5) ? raster.front() - raster_step : raster.back() + raster_step; + } + const unsigned nof_raster_points = (raster.back() - raster.front()) / raster_step + 1; + return test_rgen::uniform_int(0, nof_raster_points - 1) * raster_step + raster.front(); + }; + + // Definition of UL rasters for different bands. + constexpr std::array n2_raster = {370000, 20, 382000}; + constexpr std::array n3_raster = {342000, 20, 357000}; + constexpr std::array n7_raster = {500000, 20, 514000}; + constexpr std::array n14_raster = {157600, 20, 159600}; + constexpr std::array n28_raster = {140600, 20, 149600}; + + ASSERT_TRUE(is_ul_arfcn_valid_given_band(nr_band::n2, get_rnd_raster_point(n2_raster)).has_value()); + ASSERT_FALSE(is_ul_arfcn_valid_given_band(nr_band::n2, get_rnd_raster_point(n2_raster, true)).has_value()); + ASSERT_TRUE(is_ul_arfcn_valid_given_band(nr_band::n3, get_rnd_raster_point(n3_raster)).has_value()); + ASSERT_FALSE(is_ul_arfcn_valid_given_band(nr_band::n3, get_rnd_raster_point(n3_raster, true)).has_value()); + ASSERT_TRUE(is_ul_arfcn_valid_given_band(nr_band::n7, get_rnd_raster_point(n7_raster)).has_value()); + ASSERT_FALSE(is_ul_arfcn_valid_given_band(nr_band::n7, get_rnd_raster_point(n7_raster, true)).has_value()); + ASSERT_TRUE(is_ul_arfcn_valid_given_band(nr_band::n14, get_rnd_raster_point(n14_raster)).has_value()); + ASSERT_FALSE(is_ul_arfcn_valid_given_band(nr_band::n14, get_rnd_raster_point(n14_raster, true)).has_value()); + ASSERT_TRUE(is_ul_arfcn_valid_given_band(nr_band::n28, get_rnd_raster_point(n28_raster)).has_value()); + ASSERT_FALSE(is_ul_arfcn_valid_given_band(nr_band::n28, get_rnd_raster_point(n28_raster, true)).has_value()); + // Single UL ARFCN for n28, BW 40Mhz. + ASSERT_TRUE(is_ul_arfcn_valid_given_band(nr_band::n28, 144608, bs_channel_bandwidth::MHz40).has_value()); + // Let's provide a n14 frequency to n28. + ASSERT_FALSE(is_ul_arfcn_valid_given_band(nr_band::n28, 157600).has_value()); } diff --git a/tests/unittests/scheduler/CMakeLists.txt b/tests/unittests/scheduler/CMakeLists.txt index 7760b0262c..d4e6c5885e 100644 --- a/tests/unittests/scheduler/CMakeLists.txt +++ b/tests/unittests/scheduler/CMakeLists.txt @@ -31,6 +31,7 @@ add_subdirectory(uci_and_pucch) add_subdirectory(policy) add_subdirectory(config) add_subdirectory(slicing) +add_subdirectory(srs_scheduling) add_executable(sched_no_ue_test scheduler_no_ue_test.cpp) target_link_libraries(sched_no_ue_test srsran_sched diff --git a/tests/unittests/scheduler/common_scheduling/paging_scheduler_test.cpp b/tests/unittests/scheduler/common_scheduling/paging_scheduler_test.cpp index 61f4c709f1..63895e414d 100644 --- a/tests/unittests/scheduler/common_scheduling/paging_scheduler_test.cpp +++ b/tests/unittests/scheduler/common_scheduling/paging_scheduler_test.cpp @@ -128,7 +128,7 @@ class base_paging_sched_tester bench->res_grid.slot_indication(current_slot); bench->pdcch_sch.slot_indication(current_slot); - bench->pg_sch.schedule_paging(bench->res_grid); + bench->pg_sch.run_slot(bench->res_grid); // Log scheduling results. sched_res_logger.on_scheduler_result(bench->res_grid[0].result); diff --git a/tests/unittests/scheduler/scheduler_metrics_handler_test.cpp b/tests/unittests/scheduler/scheduler_metrics_handler_test.cpp index e5c4f67041..07f4ce2191 100644 --- a/tests/unittests/scheduler/scheduler_metrics_handler_test.cpp +++ b/tests/unittests/scheduler/scheduler_metrics_handler_test.cpp @@ -20,7 +20,9 @@ * */ +#include "lib/scheduler/config/cell_configuration.h" #include "lib/scheduler/logging/scheduler_metrics_handler.h" +#include "tests/unittests/scheduler/test_utils/config_generators.h" #include "srsran/support/test_utils.h" #include @@ -39,9 +41,12 @@ class scheduler_metrics_handler_tester : public ::testing::Test protected: scheduler_metrics_handler_tester( std::chrono::milliseconds period = std::chrono::milliseconds{test_rgen::uniform_int(2, 100)}) : - report_period(period), metrics(period, metrics_notif) + report_period(period), + cell_cfg(config_helpers::make_default_scheduler_expert_config(), + test_helpers::make_default_sched_cell_configuration_request()), + metrics(period, metrics_notif, cell_cfg) { - metrics.handle_ue_creation(test_ue_index, to_rnti(0x4601), pci_t{0}, nof_prbs, num_slots_per_frame); + metrics.handle_ue_creation(test_ue_index, to_rnti(0x4601), pci_t{0}); } void run_slot(const sched_result& sched_res, std::chrono::microseconds latency = std::chrono::microseconds{0}) @@ -64,13 +69,12 @@ class scheduler_metrics_handler_tester : public ::testing::Test std::chrono::milliseconds report_period; test_scheduler_ue_metrics_notifier metrics_notif; + cell_configuration cell_cfg; cell_metrics_handler metrics; du_ue_index_t test_ue_index = to_du_ue_index(test_rgen::uniform_int(0, MAX_NOF_DU_UES - 1)); slot_point next_sl_tx{0, test_rgen::uniform_int(0, 10239)}; - unsigned slot_count = 0; - unsigned nof_prbs = 100; - unsigned num_slots_per_frame = 10; + unsigned slot_count = 0; }; TEST_F(scheduler_metrics_handler_tester, metrics_sent_with_defined_periodicity) diff --git a/tests/unittests/scheduler/slicing/slice_scheduler_test.cpp b/tests/unittests/scheduler/slicing/slice_scheduler_test.cpp index 3178d188f2..a32a1535c6 100644 --- a/tests/unittests/scheduler/slicing/slice_scheduler_test.cpp +++ b/tests/unittests/scheduler/slicing/slice_scheduler_test.cpp @@ -312,9 +312,10 @@ TEST_F(rb_ratio_slice_scheduler_test, when_slice_with_min_rb_has_ues_then_it_is_ // Default SRB slice has very high priority. auto next_dl_slice = slice_sched.get_next_dl_candidate(); - ASSERT_EQ(next_dl_slice->id(), default_srb_slice_id); - - next_dl_slice = slice_sched.get_next_dl_candidate(); + // Default SRB slice has very high priority. We ignore it as candidate for this test. + if (next_dl_slice->id() == default_srb_slice_id) { + next_dl_slice = slice_sched.get_next_dl_candidate(); + } ASSERT_EQ(next_dl_slice->id(), drb1_slice_id); ASSERT_TRUE(next_dl_slice->is_candidate(to_du_ue_index(0), lcid_t::LCID_MIN_DRB)); } @@ -324,13 +325,12 @@ TEST_F(rb_ratio_slice_scheduler_test, when_slice_rb_ratios_are_min_bounded_then_ ASSERT_NE(this->add_ue(to_du_ue_index(0)), nullptr); run_slot(); - // Default SRB slice has very high priority. auto next_dl_slice = slice_sched.get_next_dl_candidate(); - ASSERT_EQ(next_dl_slice->id(), default_srb_slice_id); - - next_dl_slice = slice_sched.get_next_dl_candidate(); + // Default SRB slice has very high priority. We ignore it as candidate for this test. + if (next_dl_slice->id() == default_srb_slice_id) { + next_dl_slice = slice_sched.get_next_dl_candidate(); + } ASSERT_EQ(next_dl_slice->id(), drb1_slice_id); - ASSERT_EQ(next_dl_slice->remaining_rbs(), MIN_SLICE_RB); } @@ -340,17 +340,21 @@ TEST_F(rb_ratio_slice_scheduler_test, ASSERT_NE(this->add_ue(to_du_ue_index(0)), nullptr); run_slot(); - // Default SRB slice has very high priority. auto next_dl_slice = slice_sched.get_next_dl_candidate(); - ASSERT_EQ(next_dl_slice->id(), default_srb_slice_id); + // Default SRB slice has very high priority. We ignore it as candidate for this test. + if (next_dl_slice->id() == default_srb_slice_id) { + next_dl_slice = slice_sched.get_next_dl_candidate(); + } - next_dl_slice = slice_sched.get_next_dl_candidate(); ASSERT_EQ(next_dl_slice->id(), drb1_slice_id); next_dl_slice->store_grant(MIN_SLICE_RB - 1); // we leave one RB empty (MIN_SLICE_RB - 1). ASSERT_EQ(next_dl_slice->remaining_rbs(), 1); // No more slices to schedule. next_dl_slice = slice_sched.get_next_dl_candidate(); + if (next_dl_slice->id() == default_srb_slice_id) { + next_dl_slice = slice_sched.get_next_dl_candidate(); + } ASSERT_FALSE(next_dl_slice.has_value()); } @@ -362,14 +366,19 @@ TEST_F(rb_ratio_slice_scheduler_test, // Default SRB slice has very high priority. auto next_dl_slice = slice_sched.get_next_dl_candidate(); - ASSERT_EQ(next_dl_slice->id(), default_srb_slice_id); + // Default SRB slice has very high priority. We ignore it as candidate for this test. + if (next_dl_slice->id() == default_srb_slice_id) { + next_dl_slice = slice_sched.get_next_dl_candidate(); + } - next_dl_slice = slice_sched.get_next_dl_candidate(); ASSERT_EQ(next_dl_slice->id(), drb1_slice_id); next_dl_slice->store_grant(MIN_SLICE_RB); ASSERT_EQ(next_dl_slice->remaining_rbs(), 0); next_dl_slice = slice_sched.get_next_dl_candidate(); + if (next_dl_slice->id() == default_srb_slice_id) { + next_dl_slice = slice_sched.get_next_dl_candidate(); + } // Original slice is selected again, now using maxRB ratio as the remaining RBs. ASSERT_EQ(next_dl_slice->id(), drb1_slice_id); ASSERT_EQ(next_dl_slice->remaining_rbs(), MAX_SLICE_RB - MIN_SLICE_RB); @@ -385,26 +394,27 @@ TEST_F(rb_ratio_slice_scheduler_test, ASSERT_NE(this->add_ue(to_du_ue_index(0)), nullptr); run_slot(); - // Default SRB slice has very high priority. + // High priority slices auto next_dl_slice = slice_sched.get_next_dl_candidate(); - ASSERT_EQ(next_dl_slice->id(), default_srb_slice_id); - - next_dl_slice = slice_sched.get_next_dl_candidate(); ASSERT_EQ(next_dl_slice->id(), drb1_slice_id); next_dl_slice->store_grant(MIN_SLICE_RB); next_dl_slice = slice_sched.get_next_dl_candidate(); + ASSERT_EQ(next_dl_slice->id(), default_srb_slice_id); + + // Lower priority candidates. + next_dl_slice = slice_sched.get_next_dl_candidate(); next_dl_slice = slice_sched.get_next_dl_candidate(); ASSERT_FALSE(next_dl_slice.has_value()); // New slot and priorities are reestablished. run_slot(); next_dl_slice = slice_sched.get_next_dl_candidate(); + ASSERT_EQ(next_dl_slice->id(), default_srb_slice_id); + next_dl_slice = slice_sched.get_next_dl_candidate(); ASSERT_EQ(next_dl_slice->id(), drb1_slice_id); ASSERT_EQ(next_dl_slice->remaining_rbs(), MIN_SLICE_RB); next_dl_slice->store_grant(MIN_SLICE_RB); next_dl_slice = slice_sched.get_next_dl_candidate(); - ASSERT_EQ(next_dl_slice->id(), default_srb_slice_id); - next_dl_slice = slice_sched.get_next_dl_candidate(); ASSERT_EQ(next_dl_slice->id(), drb1_slice_id); ASSERT_EQ(next_dl_slice->remaining_rbs(), MAX_SLICE_RB - MIN_SLICE_RB); next_dl_slice = slice_sched.get_next_dl_candidate(); diff --git a/tests/unittests/scheduler/srs_scheduling/CMakeLists.txt b/tests/unittests/scheduler/srs_scheduling/CMakeLists.txt new file mode 100644 index 0000000000..5d01f11ce2 --- /dev/null +++ b/tests/unittests/scheduler/srs_scheduling/CMakeLists.txt @@ -0,0 +1,30 @@ +# +# Copyright 2021-2024 Software Radio Systems Limited +# +# This file is part of srsRAN +# +# srsRAN 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. +# +# srsRAN 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/. +# + +add_executable(srs_sched_test + srs_scheduler_test.cpp) +target_link_libraries(srs_sched_test + srsran_sched + scheduler_test_suite + srslog + sched_config + gtest + gtest_main) +gtest_discover_tests(srs_sched_test) diff --git a/tests/unittests/scheduler/srs_scheduling/srs_scheduler_test.cpp b/tests/unittests/scheduler/srs_scheduling/srs_scheduler_test.cpp new file mode 100644 index 0000000000..a003c71c71 --- /dev/null +++ b/tests/unittests/scheduler/srs_scheduling/srs_scheduler_test.cpp @@ -0,0 +1,352 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN 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. + * + * srsRAN 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 "lib/scheduler/srs/srs_scheduler_impl.h" +#include "tests/unittests/scheduler/test_utils/config_generators.h" +#include "tests/unittests/scheduler/test_utils/dummy_test_components.h" +#include "tests/unittests/scheduler/test_utils/scheduler_test_suite.h" +#include "srsran/du/du_cell_config_helpers.h" +#include "srsran/ran/srs/srs_bandwidth_configuration.h" +#include "srsran/support/test_utils.h" +#include +#include + +using namespace srsran; + +namespace { + +class dummy_harq_timeout_notifier : public harq_timeout_notifier +{ +public: + dummy_harq_timeout_notifier(harq_timeout_handler& handler_) : handler(handler_) {} + + void on_harq_timeout(du_ue_index_t ue_idx, bool is_dl, bool ack) override + { + handler.handle_harq_timeout(ue_idx, is_dl); + } + +private: + harq_timeout_handler& handler; +}; + +} // namespace + +static unsigned compute_c_srs(unsigned nof_ul_crbs) +{ + // In this test, we only consider the case where B_SRS is 0. + const uint8_t b_srs = 0U; + unsigned candidate_c_srs = 0U; + unsigned candidate_m_srs = 0U; + // Spans over Table 6.4.1.4.3-1 in TS 38.211 and find the smallest C_SRS that maximizes m_srs_0 under the + // constraint of m_SRS <= nof_BW_RBs. + for (unsigned c_srs_it = 0; c_srs_it != 64; ++c_srs_it) { + std::optional srs_cfg = srs_configuration_get(c_srs_it, b_srs); + srsran_assert(srs_cfg.has_value(), "C_SRS is required for this unittest"); + if (srs_cfg.value().m_srs <= nof_ul_crbs and srs_cfg.value().m_srs > candidate_m_srs) { + candidate_m_srs = srs_cfg->m_srs; + candidate_c_srs = c_srs_it; + } + } + return candidate_c_srs; +} + +static sched_cell_configuration_request_message make_custom_sched_cell_configuration_request(bool is_tdd = false) +{ + sched_cell_configuration_request_message req = test_helpers::make_default_sched_cell_configuration_request( + cell_config_builder_params{.scs_common = is_tdd ? subcarrier_spacing::kHz30 : subcarrier_spacing::kHz15, + .channel_bw_mhz = bs_channel_bandwidth::MHz20, + .dl_f_ref_arfcn = is_tdd ? 520000U : 365000U}); + return req; +} + +static bool is_ul_slot(unsigned offset, const tdd_ul_dl_config_common& tdd_cfg) +{ + const unsigned slot_index = offset % (NOF_SUBFRAMES_PER_FRAME * get_nof_slots_per_subframe(tdd_cfg.ref_scs)); + return srsran::get_active_tdd_ul_symbols(tdd_cfg, slot_index, cyclic_prefix::NORMAL).length() != 0; +} + +static sched_ue_creation_request_message +create_sched_ue_creation_request_for_srs_cfg(srs_periodicity srs_period, + unsigned nof_ul_crbs, + const std::optional tdd_cfg) +{ + sched_ue_creation_request_message ue_req = test_helpers::create_default_sched_ue_creation_request(); + auto& ue_srs_cfg = ue_req.cfg.cells.value().front().serv_cell_cfg.ul_config.value().init_ul_bwp.srs_cfg.value(); + + // Set SRS resource set periodic. + ue_srs_cfg.srs_res_set_list.front().res_type.emplace(); + + // Set SRS resource periodic. + srs_config::srs_resource& srs_res = ue_srs_cfg.srs_res_list.front(); + srs_res.res_type = srs_resource_type::periodic; + uint16_t srs_offset = 0U; + // The offset needs to be an UL slot. + if (tdd_cfg.has_value()) { + srsran_assert(static_cast(srs_period) >= tdd_cfg.value().pattern1.dl_ul_tx_period_nof_slots, + "SRS period cannot be smaller than the TDD period"); + + srs_offset = test_rgen::uniform_int(0, static_cast(srs_period) - 1); + while (not is_ul_slot(srs_offset, tdd_cfg.value())) { + srs_offset = test_rgen::uniform_int(0, static_cast(srs_period) - 1); + } + } else { + srs_offset = test_rgen::uniform_int(0, static_cast(srs_period) - 1); + } + srs_res.periodicity_and_offset.emplace(srs_config::srs_periodicity_and_offset{srs_period, srs_offset}); + + // This is for 1 UE only. If more UEs are added, this should be updated. + srs_res.tx_comb.size = srsran::tx_comb_size::n4; + srs_res.tx_comb.tx_comb_offset = 0U; + srs_res.tx_comb.tx_comb_cyclic_shift = 0U; + + // Set SRS in symbols [13, 14). + srs_res.res_mapping.start_pos = 0U; + srs_res.res_mapping.nof_symb = n1; + srs_res.res_mapping.rept_factor = n1; + + // Frequency hopping is disabled. + srs_res.freq_domain_pos = 0U; + srs_res.freq_hop.b_hop = 0U; + srs_res.freq_hop.b_srs = 0U; + srs_res.freq_hop.c_srs = compute_c_srs(nof_ul_crbs); + + // SRS placed in the middle of the BW. + std::optional srs_bw_cfg = srs_configuration_get(srs_res.freq_hop.c_srs, srs_res.freq_hop.b_srs); + srsran_assert(srs_bw_cfg.has_value(), "C_SRS is required for this unittest"); + srs_res.freq_domain_shift = (nof_ul_crbs - srs_bw_cfg->m_srs) / 2U; + + srs_res.sequence_id = 0U; + + return ue_req; +} + +namespace srs_periodicity_test { + +struct srs_test_params { + bool is_tdd; + srs_periodicity period; +}; + +std::ostream& operator<<(std::ostream& os, const srs_test_params& params) +{ + // Make sure there are no spaces nor colons in this string, otherwise Gtest complains about the test name being + // invalid. + os << "Is_tdd_" << params.is_tdd << "_srs_period_slots_" << static_cast(params.period); + return os; +} + +} // namespace srs_periodicity_test + +using namespace srs_periodicity_test; + +class test_bench +{ +public: + test_bench(srs_test_params params) : + expert_cfg{config_helpers::make_default_scheduler_expert_config()}, + cell_cfg{[this, ¶ms]() -> const cell_configuration& { + cell_cfg_list.emplace(to_du_cell_index(0), + std::make_unique( + expert_cfg, make_custom_sched_cell_configuration_request(params.is_tdd))); + return *cell_cfg_list[to_du_cell_index(0)]; + }()}, + cell_harqs{MAX_NOF_DU_UES, MAX_NOF_HARQS, std::make_unique(harq_timeout_handler)}, + srs_sched(cell_cfg, ues), + current_sl_tx{to_numerology_value(cell_cfg.dl_cfg_common.init_dl_bwp.generic_params.scs), 0} + { + slot_indication(current_sl_tx); + mac_logger.set_level(srslog::basic_levels::debug); + } + + // Class members. + scheduler_expert_config expert_cfg; + scheduler_harq_timeout_dummy_handler harq_timeout_handler; + cell_common_configuration_list cell_cfg_list{}; + const cell_configuration& cell_cfg; + cell_harq_manager cell_harqs; + ue_repository ues; + std::vector ue_ded_cfgs; + cell_resource_allocator res_grid{cell_cfg}; + srs_scheduler_impl srs_sched; + slot_point current_sl_tx; + + srslog::basic_logger& mac_logger = srslog::fetch_basic_logger("SCHED", true); + srslog::basic_logger& test_logger = srslog::fetch_basic_logger("TEST"); + + // Class methods. + void add_ue(srs_periodicity srs_period) + { + sched_ue_creation_request_message ue_req = create_sched_ue_creation_request_for_srs_cfg( + srs_period, + cell_cfg_list[to_du_cell_index(0)]->ul_cfg_common.init_ul_bwp.generic_params.crbs.length(), + cell_cfg.tdd_cfg_common); + ue_ded_cfgs.emplace_back(ue_req.ue_index, ue_req.crnti, cell_cfg_list, ue_req.cfg); + ues.add_ue(std::make_unique(ue_creation_command{ue_ded_cfgs.back(), ue_req.starts_in_fallback, cell_harqs})); + srs_sched.add_ue(ues[ue_req.ue_index].get_pcell().cfg()); + } + + void slot_indication(slot_point slot_tx) + { + mac_logger.set_context(slot_tx.sfn(), slot_tx.slot_index()); + test_logger.set_context(slot_tx.sfn(), slot_tx.slot_index()); + res_grid.slot_indication(slot_tx); + } + + expected test_srs_pdu(const srs_info& srs_pdu) const + { + const auto& srs_cfg = + ues[to_du_ue_index(0)].get_pcell().cfg().cfg_dedicated().ul_config.value().init_ul_bwp.srs_cfg.value(); + const auto& srs_res_cfg = srs_cfg.srs_res_list.front(); + + if (srs_pdu.crnti != ues[to_du_ue_index(0)].crnti) { + return make_unexpected(std::string("RNTI mismatch")); + } + if (srs_pdu.bwp_cfg != &cell_cfg.ul_cfg_common.init_ul_bwp.generic_params) { + return make_unexpected(std::string("BWP mismatch")); + } + if (srs_pdu.nof_antenna_ports != cell_cfg.ul_carrier.nof_ant) { + return make_unexpected(std::string("Nof antenna ports mismatch")); + } + if (srs_pdu.symbols != ofdm_symbol_range{NOF_OFDM_SYM_PER_SLOT_NORMAL_CP - 1 - srs_res_cfg.res_mapping.start_pos, + NOF_OFDM_SYM_PER_SLOT_NORMAL_CP - 1 - srs_res_cfg.res_mapping.start_pos + + static_cast(srs_res_cfg.res_mapping.nof_symb)}) { + return make_unexpected(std::string("Symbols mismatch")); + } + if (srs_pdu.nof_repetitions != srs_res_cfg.res_mapping.rept_factor) { + return make_unexpected(std::string("Repetition factor mismatch")); + } + if (srs_pdu.config_index != srs_res_cfg.freq_hop.c_srs) { + return make_unexpected(std::string("Frequency domain shift mismatch")); + } + if (srs_pdu.sequence_id != static_cast(srs_res_cfg.sequence_id)) { + return make_unexpected(std::string("Sequence ID mismatch")); + } + if (srs_pdu.bw_index != srs_res_cfg.freq_hop.b_srs) { + return make_unexpected(std::string("B_srs mismatch")); + } + if (srs_pdu.tx_comb != srs_res_cfg.tx_comb.size) { + return make_unexpected(std::string("TX comb size mismatch")); + } + if (srs_pdu.comb_offset != srs_res_cfg.tx_comb.tx_comb_offset) { + return make_unexpected(std::string("TX comb offset mismatch")); + } + if (srs_pdu.cyclic_shift != srs_res_cfg.tx_comb.tx_comb_cyclic_shift) { + return make_unexpected(std::string("TX comb cyclic shift mismatch")); + } + if (srs_pdu.freq_position != srs_res_cfg.freq_domain_pos) { + return make_unexpected(std::string("Freq. domain position mismatch")); + } + if (srs_pdu.freq_shift != static_cast(srs_res_cfg.freq_domain_shift)) { + return make_unexpected(std::string("Freq. domain position mismatch")); + } + if (srs_pdu.freq_hopping != srs_res_cfg.freq_hop.b_hop) { + return make_unexpected(std::string("b_hop mismatch")); + } + if (srs_pdu.group_or_seq_hopping != srs_res_cfg.grp_or_seq_hop) { + return make_unexpected(std::string("group_or_seq_hopping mismatch")); + } + if (srs_pdu.resource_type != srs_res_cfg.res_type) { + return make_unexpected(std::string("group_or_seq_hopping mismatch")); + } + if (srs_res_cfg.res_type != srsran::srs_resource_type::aperiodic) { + srsran_assert(srs_res_cfg.periodicity_and_offset.has_value(), "Periodicity and offset is required for this test"); + if (srs_pdu.t_srs_period != srs_res_cfg.periodicity_and_offset.value().period) { + return make_unexpected(std::string("Periodicity mismatch")); + } + if (srs_pdu.t_offset != static_cast(srs_res_cfg.periodicity_and_offset.value().offset)) { + return make_unexpected(std::string("Offset mismatch")); + } + } + + return true; + } + + uint16_t get_offset() const + { + if (ues.empty()) { + return 0; + } + return ues[to_du_ue_index(0U)] + .get_pcell() + .cfg() + .cfg_dedicated() + .ul_config.value() + .init_ul_bwp.srs_cfg.value() + .srs_res_list.front() + .periodicity_and_offset->offset; + } +}; + +class srs_scheduler_tester : public ::testing::TestWithParam, public test_bench +{ +protected: + srs_scheduler_tester() : test_bench(GetParam()) {} +}; + +TEST_P(srs_scheduler_tester, test_different_periods) +{ + auto srs_period_uint = static_cast(GetParam().period); + + const auto add_ue_slot = test_rgen::uniform_int(0, res_grid.RING_ALLOCATOR_SIZE); + // Check at the allocation for at least 4 the size of the resource grid. + const unsigned nof_slots_to_test = + add_ue_slot + std::max(srs_period_uint * 4, static_cast(res_grid.RING_ALLOCATOR_SIZE) * 4); + + for (unsigned sl_cnt = 0; sl_cnt < nof_slots_to_test + add_ue_slot; ++sl_cnt) { + // Add the UE at the specified slot. + if (sl_cnt == add_ue_slot) { + add_ue(GetParam().period); + } + + srs_sched.run_slot(res_grid); + // Only check the results once the UE has been added. + if (not ues.empty() and (current_sl_tx - get_offset()).to_uint() % srs_period_uint == 0) { + ASSERT_EQ(1, res_grid[0].result.ul.srss.size()); + expected pdu_test = test_srs_pdu(res_grid[0].result.ul.srss.front()); + ASSERT_TRUE(pdu_test.has_value()) << pdu_test.error(); + } else { + ASSERT_TRUE(res_grid[0].result.ul.srss.empty()); + } + + // Update the slot indicator. + slot_indication(++current_sl_tx); + } +} + +INSTANTIATE_TEST_SUITE_P(test_srs_scheduler_for_different_periods, + srs_scheduler_tester, + testing::Values(srs_test_params{.is_tdd = false, .period = srs_periodicity::sl1}, + srs_test_params{.is_tdd = false, .period = srs_periodicity::sl2}, + srs_test_params{.is_tdd = false, .period = srs_periodicity::sl4}, + srs_test_params{.is_tdd = false, .period = srs_periodicity::sl5}, + srs_test_params{.is_tdd = false, .period = srs_periodicity::sl8}, + srs_test_params{.is_tdd = false, .period = srs_periodicity::sl10}, + srs_test_params{.is_tdd = false, .period = srs_periodicity::sl20}, + srs_test_params{.is_tdd = false, .period = srs_periodicity::sl40}, + srs_test_params{.is_tdd = false, .period = srs_periodicity::sl80}, + srs_test_params{.is_tdd = true, .period = srs_periodicity::sl10}, + srs_test_params{.is_tdd = true, .period = srs_periodicity::sl20}, + srs_test_params{.is_tdd = true, .period = srs_periodicity::sl40}, + srs_test_params{.is_tdd = true, .period = srs_periodicity::sl80}, + srs_test_params{.is_tdd = true, .period = srs_periodicity::sl160}, + srs_test_params{.is_tdd = true, .period = srs_periodicity::sl320}, + srs_test_params{.is_tdd = true, .period = srs_periodicity::sl640})); diff --git a/tests/unittests/scheduler/test_utils/config_generators.cpp b/tests/unittests/scheduler/test_utils/config_generators.cpp index 660cf0c3fd..ed08caafd4 100644 --- a/tests/unittests/scheduler/test_utils/config_generators.cpp +++ b/tests/unittests/scheduler/test_utils/config_generators.cpp @@ -44,13 +44,7 @@ class dummy_scheduler_ue_metrics_notifier : public scheduler_metrics_notifier class dummy_sched_metrics_ue_configurator : public sched_metrics_ue_configurator { public: - void handle_ue_creation(du_ue_index_t ue_index, - rnti_t rnti, - pci_t pcell_pci, - unsigned num_prbs, - unsigned num_slots_per_frame) override - { - } + void handle_ue_creation(du_ue_index_t ue_index, rnti_t rnti, pci_t pcell_pci) override {} void handle_ue_reconfiguration(du_ue_index_t ue_index) override {} void handle_ue_deletion(du_ue_index_t ue_index) override {} }; diff --git a/tests/unittests/scheduler/test_utils/dummy_test_components.h b/tests/unittests/scheduler/test_utils/dummy_test_components.h index b16ef50f74..d5d4ef27b4 100644 --- a/tests/unittests/scheduler/test_utils/dummy_test_components.h +++ b/tests/unittests/scheduler/test_utils/dummy_test_components.h @@ -197,13 +197,7 @@ class scheduler_harq_timeout_dummy_notifier : public harq_timeout_notifier class scheduler_ue_metrics_dummy_configurator : public sched_metrics_ue_configurator { public: - void handle_ue_creation(du_ue_index_t ue_index, - rnti_t rnti, - pci_t pcell_pci, - unsigned num_prbs, - unsigned num_slots_per_frame) override - { - } + void handle_ue_creation(du_ue_index_t ue_index, rnti_t rnti, pci_t pcell_pci) override {} void handle_ue_reconfiguration(du_ue_index_t ue_index) override {} void handle_ue_deletion(du_ue_index_t ue_index) override {} };