diff --git a/CTestConfig.cmake b/CTestConfig.cmake index a868d7957..fe80d73cf 100644 --- a/CTestConfig.cmake +++ b/CTestConfig.cmake @@ -24,4 +24,4 @@ set(CTEST_DROP_METHOD "http") set(CTEST_DROP_SITE "my.cdash.org") set(CTEST_DROP_LOCATION "/submit.php?project=srsRAN") set(CTEST_DROP_SITE_CDASH TRUE) -set(VALGRIND_COMMAND_OPTIONS "--error-exitcode=1 --trace-children=yes --leak-check=full --show-reachable=yes --vex-guest-max-insns=25") +set(VALGRIND_COMMAND_OPTIONS "--error-exitcode=1 --trace-children=yes --leak-check=full --show-leak-kinds=all --track-origins=yes --show-reachable=yes --vex-guest-max-insns=25") diff --git a/cmake/modules/SRSRAN_install_configs.sh.in b/cmake/modules/SRSRAN_install_configs.sh.in index aaa642cad..314b25f3b 100755 --- a/cmake/modules/SRSRAN_install_configs.sh.in +++ b/cmake/modules/SRSRAN_install_configs.sh.in @@ -105,7 +105,7 @@ install_file "ue.conf.example" install_file "enb.conf.example" install_file "sib.conf.example" install_file "rr.conf.example" -install_file "drb.conf.example" +install_file "rb.conf.example" install_file "epc.conf.example" install_file "mbms.conf.example" install_file "user_db.csv.example" diff --git a/lib/include/srsran/asn1/nas_5g_msg.h b/lib/include/srsran/asn1/nas_5g_msg.h index 25ad34f87..65a299d78 100644 --- a/lib/include/srsran/asn1/nas_5g_msg.h +++ b/lib/include/srsran/asn1/nas_5g_msg.h @@ -2127,265 +2127,353 @@ public: registration_request_t& set_registration_request() { set(msg_types::options::registration_request); - msg_container = srslog::detail::any{registration_request_t()}; + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{registration_request_t()}; return *srslog::detail::any_cast(&msg_container); } registration_accept_t& set_registration_accept() { set(msg_types::options::registration_accept); - msg_container = srslog::detail::any{registration_accept_t()}; + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{registration_accept_t()}; return *srslog::detail::any_cast(&msg_container); } registration_complete_t& set_registration_complete() { set(msg_types::options::registration_complete); - msg_container = srslog::detail::any{registration_complete_t()}; + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{registration_complete_t()}; return *srslog::detail::any_cast(&msg_container); } registration_reject_t& set_registration_reject() { set(msg_types::options::registration_reject); - msg_container = srslog::detail::any{registration_reject_t()}; + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{registration_reject_t()}; return *srslog::detail::any_cast(&msg_container); } deregistration_request_ue_originating_t& set_deregistration_request_ue_originating() { set(msg_types::options::deregistration_request_ue_originating); - msg_container = srslog::detail::any{deregistration_request_ue_originating_t()}; + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{deregistration_request_ue_originating_t()}; return *srslog::detail::any_cast(&msg_container); } deregistration_accept_ue_originating_t& set_deregistration_accept_ue_originating() { set(msg_types::options::deregistration_accept_ue_originating); - msg_container = srslog::detail::any{deregistration_accept_ue_originating_t()}; + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{deregistration_accept_ue_originating_t()}; return *srslog::detail::any_cast(&msg_container); } deregistration_request_ue_terminated_t& set_deregistration_request_ue_terminated() { set(msg_types::options::deregistration_request_ue_terminated); - msg_container = srslog::detail::any{deregistration_request_ue_terminated_t()}; + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{deregistration_request_ue_terminated_t()}; return *srslog::detail::any_cast(&msg_container); } deregistration_accept_ue_terminated_t& set_deregistration_accept_ue_terminated() { set(msg_types::options::deregistration_accept_ue_terminated); - msg_container = srslog::detail::any{deregistration_accept_ue_terminated_t()}; + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{deregistration_accept_ue_terminated_t()}; return *srslog::detail::any_cast(&msg_container); } service_request_t& set_service_request() { set(msg_types::options::service_request); - msg_container = srslog::detail::any{service_request_t()}; + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{service_request_t()}; return *srslog::detail::any_cast(&msg_container); } service_reject_t& set_service_reject() { set(msg_types::options::service_reject); - msg_container = srslog::detail::any{service_reject_t()}; + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{service_reject_t()}; return *srslog::detail::any_cast(&msg_container); } service_accept_t& set_service_accept() { set(msg_types::options::service_accept); - msg_container = srslog::detail::any{service_accept_t()}; + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{service_accept_t()}; return *srslog::detail::any_cast(&msg_container); } configuration_update_command_t& set_configuration_update_command() { set(msg_types::options::configuration_update_command); - msg_container = srslog::detail::any{configuration_update_command_t()}; + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{configuration_update_command_t()}; return *srslog::detail::any_cast(&msg_container); } configuration_update_complete_t& set_configuration_update_complete() { set(msg_types::options::configuration_update_complete); - msg_container = srslog::detail::any{configuration_update_complete_t()}; + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{configuration_update_complete_t()}; return *srslog::detail::any_cast(&msg_container); } authentication_request_t& set_authentication_request() { set(msg_types::options::authentication_request); - msg_container = srslog::detail::any{authentication_request_t()}; + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{authentication_request_t()}; return *srslog::detail::any_cast(&msg_container); } authentication_response_t& set_authentication_response() { set(msg_types::options::authentication_response); - msg_container = srslog::detail::any{authentication_response_t()}; + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{authentication_response_t()}; return *srslog::detail::any_cast(&msg_container); } authentication_reject_t& set_authentication_reject() { set(msg_types::options::authentication_reject); - msg_container = srslog::detail::any{authentication_reject_t()}; + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{authentication_reject_t()}; return *srslog::detail::any_cast(&msg_container); } authentication_failure_t& set_authentication_failure() { set(msg_types::options::authentication_failure); - msg_container = srslog::detail::any{authentication_failure_t()}; + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{authentication_failure_t()}; return *srslog::detail::any_cast(&msg_container); } authentication_result_t& set_authentication_result() { set(msg_types::options::authentication_result); - msg_container = srslog::detail::any{authentication_result_t()}; + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{authentication_result_t()}; return *srslog::detail::any_cast(&msg_container); } identity_request_t& set_identity_request() { set(msg_types::options::identity_request); - msg_container = srslog::detail::any{identity_request_t()}; + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{identity_request_t()}; return *srslog::detail::any_cast(&msg_container); } identity_response_t& set_identity_response() { set(msg_types::options::identity_response); - msg_container = srslog::detail::any{identity_response_t()}; + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{identity_response_t()}; return *srslog::detail::any_cast(&msg_container); } security_mode_command_t& set_security_mode_command() { set(msg_types::options::security_mode_command); - msg_container = srslog::detail::any{security_mode_command_t()}; + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{security_mode_command_t()}; return *srslog::detail::any_cast(&msg_container); } security_mode_complete_t& set_security_mode_complete() { set(msg_types::options::security_mode_complete); - msg_container = srslog::detail::any{security_mode_complete_t()}; + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{security_mode_complete_t()}; return *srslog::detail::any_cast(&msg_container); } security_mode_reject_t& set_security_mode_reject() { set(msg_types::options::security_mode_reject); - msg_container = srslog::detail::any{security_mode_reject_t()}; + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{security_mode_reject_t()}; return *srslog::detail::any_cast(&msg_container); } status_5gmm_t& set_status_5gmm() { set(msg_types::options::status_5gmm); - msg_container = srslog::detail::any{status_5gmm_t()}; + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{status_5gmm_t()}; return *srslog::detail::any_cast(&msg_container); } notification_t& set_notification() { set(msg_types::options::notification); - msg_container = srslog::detail::any{notification_t()}; + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{notification_t()}; return *srslog::detail::any_cast(&msg_container); } notification_response_t& set_notification_response() { set(msg_types::options::notification_response); - msg_container = srslog::detail::any{notification_response_t()}; + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{notification_response_t()}; return *srslog::detail::any_cast(&msg_container); } ul_nas_transport_t& set_ul_nas_transport() { set(msg_types::options::ul_nas_transport); - msg_container = srslog::detail::any{ul_nas_transport_t()}; + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{ul_nas_transport_t()}; return *srslog::detail::any_cast(&msg_container); } dl_nas_transport_t& set_dl_nas_transport() { set(msg_types::options::dl_nas_transport); - msg_container = srslog::detail::any{dl_nas_transport_t()}; + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{dl_nas_transport_t()}; return *srslog::detail::any_cast(&msg_container); } pdu_session_establishment_request_t& set_pdu_session_establishment_request() { set(msg_types::options::pdu_session_establishment_request); - msg_container = srslog::detail::any{pdu_session_establishment_request_t()}; + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + msg_container = srslog::detail::any{pdu_session_establishment_request_t()}; return *srslog::detail::any_cast(&msg_container); } pdu_session_establishment_accept_t& set_pdu_session_establishment_accept() { set(msg_types::options::pdu_session_establishment_accept); - msg_container = srslog::detail::any{pdu_session_establishment_accept_t()}; + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + msg_container = srslog::detail::any{pdu_session_establishment_accept_t()}; return *srslog::detail::any_cast(&msg_container); } pdu_session_establishment_reject_t& set_pdu_session_establishment_reject() { set(msg_types::options::pdu_session_establishment_reject); - msg_container = srslog::detail::any{pdu_session_establishment_reject_t()}; + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + msg_container = srslog::detail::any{pdu_session_establishment_reject_t()}; return *srslog::detail::any_cast(&msg_container); } pdu_session_authentication_command_t& set_pdu_session_authentication_command() { set(msg_types::options::pdu_session_authentication_command); - msg_container = srslog::detail::any{pdu_session_authentication_command_t()}; + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + msg_container = srslog::detail::any{pdu_session_authentication_command_t()}; return *srslog::detail::any_cast(&msg_container); } pdu_session_authentication_complete_t& set_pdu_session_authentication_complete() { set(msg_types::options::pdu_session_authentication_complete); - msg_container = srslog::detail::any{pdu_session_authentication_complete_t()}; + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + msg_container = srslog::detail::any{pdu_session_authentication_complete_t()}; return *srslog::detail::any_cast(&msg_container); } pdu_session_authentication_result_t& set_pdu_session_authentication_result() { set(msg_types::options::pdu_session_authentication_result); - msg_container = srslog::detail::any{pdu_session_authentication_result_t()}; + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + msg_container = srslog::detail::any{pdu_session_authentication_result_t()}; return *srslog::detail::any_cast(&msg_container); } pdu_session_modification_request_t& set_pdu_session_modification_request() { set(msg_types::options::pdu_session_modification_request); - msg_container = srslog::detail::any{pdu_session_modification_request_t()}; + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + msg_container = srslog::detail::any{pdu_session_modification_request_t()}; return *srslog::detail::any_cast(&msg_container); } pdu_session_modification_reject_t& set_pdu_session_modification_reject() { set(msg_types::options::pdu_session_modification_reject); - msg_container = srslog::detail::any{pdu_session_modification_reject_t()}; + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + msg_container = srslog::detail::any{pdu_session_modification_reject_t()}; return *srslog::detail::any_cast(&msg_container); } pdu_session_modification_command_t& set_pdu_session_modification_command() { set(msg_types::options::pdu_session_modification_command); - msg_container = srslog::detail::any{pdu_session_modification_command_t()}; + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + msg_container = srslog::detail::any{pdu_session_modification_command_t()}; return *srslog::detail::any_cast(&msg_container); } pdu_session_modification_complete_t& set_pdu_session_modification_complete() { set(msg_types::options::pdu_session_modification_complete); - msg_container = srslog::detail::any{pdu_session_modification_complete_t()}; + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + msg_container = srslog::detail::any{pdu_session_modification_complete_t()}; return *srslog::detail::any_cast(&msg_container); } pdu_session_modification_command_reject_t& set_pdu_session_modification_command_reject() { set(msg_types::options::pdu_session_modification_command_reject); - msg_container = srslog::detail::any{pdu_session_modification_command_reject_t()}; + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + msg_container = srslog::detail::any{pdu_session_modification_command_reject_t()}; return *srslog::detail::any_cast(&msg_container); } pdu_session_release_request_t& set_pdu_session_release_request() { set(msg_types::options::pdu_session_release_request); - msg_container = srslog::detail::any{pdu_session_release_request_t()}; + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + msg_container = srslog::detail::any{pdu_session_release_request_t()}; return *srslog::detail::any_cast(&msg_container); } pdu_session_release_reject_t& set_pdu_session_release_reject() { set(msg_types::options::pdu_session_release_reject); - msg_container = srslog::detail::any{pdu_session_release_reject_t()}; + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + msg_container = srslog::detail::any{pdu_session_release_reject_t()}; return *srslog::detail::any_cast(&msg_container); } pdu_session_release_command_t& set_pdu_session_release_command() { set(msg_types::options::pdu_session_release_command); - msg_container = srslog::detail::any{pdu_session_release_command_t()}; + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + msg_container = srslog::detail::any{pdu_session_release_command_t()}; return *srslog::detail::any_cast(&msg_container); } pdu_session_release_complete_t& set_pdu_session_release_complete() { set(msg_types::options::pdu_session_release_complete); - msg_container = srslog::detail::any{pdu_session_release_complete_t()}; + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + msg_container = srslog::detail::any{pdu_session_release_complete_t()}; return *srslog::detail::any_cast(&msg_container); } status_5gsm_t& set_status_5gsm() { set(msg_types::options::status_5gsm); - msg_container = srslog::detail::any{status_5gsm_t()}; + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + msg_container = srslog::detail::any{status_5gsm_t()}; return *srslog::detail::any_cast(&msg_container); } diff --git a/lib/include/srsran/common/basic_pnf.h b/lib/include/srsran/common/basic_pnf.h deleted file mode 100644 index 4e9550857..000000000 --- a/lib/include/srsran/common/basic_pnf.h +++ /dev/null @@ -1,558 +0,0 @@ -/** - * Copyright 2013-2021 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/. - * - */ - -#ifndef SRSRAN_BASIC_PNF_H -#define SRSRAN_BASIC_PNF_H - -#include "basic_vnf_api.h" -#include "common.h" -#include "srsran/adt/choice_type.h" -#include "srsran/common/block_queue.h" -#include "srsran/common/buffer_pool.h" -#include "srsran/srslog/srslog.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define RAND_SEED (12384) -#define RX_TIMEOUT_MS (500) - -#define MIN_TB_LEN (100) // MAX_TB_LEN defined in api.h - -#define PING_REQUEST_PDU 1 - -namespace srsran { - -struct pnf_metrics_t { - uint32_t avg_rtt_us; - uint32_t num_timing_errors; - uint32_t num_pdus; - uint32_t tb_size; -}; - -class srsran_basic_pnf -{ - using msg_header_t = basic_vnf_api::msg_header_t; - const static size_t buffer_size = - srsran::static_max::value; - using msg_buffer_t = std::array; - -public: - srsran_basic_pnf(const std::string& type_, - const std::string& vnf_p5_addr, - const uint16_t& vnf_p5_port, - const uint32_t& sf_interval, - const int32_t& num_sf_, - const uint32_t& tb_len_) : - running(false), - type(type_), - tti(100), ///< Random start TTI - vnf_addr(vnf_p5_addr), - vnf_port(vnf_p5_port), - sf_interval_us(sf_interval), - num_sf(num_sf_), - tb_len(tb_len_), - rand_gen(RAND_SEED), - rand_dist(MIN_TB_LEN, MAX_TB_LEN) - { - logger.set_level(srslog::basic_levels::warning); - logger.set_hex_dump_max_size(-1); - } - - ~srsran_basic_pnf() { stop(); }; - - bool start() - { - // create socket - sockfd = socket(AF_INET, SOCK_DGRAM, 0); - if (sockfd < 0) { - perror("socket"); - return false; - } - - int enable = 1; -#if defined(SO_REUSEADDR) - if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) { - perror("setsockopt(SO_REUSEADDR) failed"); - } -#endif -#if defined(SO_REUSEPORT) - if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(int)) < 0) { - perror("setsockopt(SO_REUSEPORT) failed"); - } -#endif - - bzero(&servaddr, sizeof(servaddr)); - servaddr.sin_family = AF_INET; - servaddr.sin_addr.s_addr = inet_addr(vnf_addr.c_str()); - servaddr.sin_port = htons(vnf_port); - - // start main thread - running = true; - - if (type == "gnb") { - rx_thread = std::unique_ptr(new std::thread(&srsran_basic_pnf::dl_handler_thread, this)); - tx_thread = std::unique_ptr(new std::thread(&srsran_basic_pnf::ul_handler_thread, this)); - } else { - tx_thread = std::unique_ptr(new std::thread(&srsran_basic_pnf::ue_dl_handler_thread, this)); - } - - return true; - }; - - bool stop() - { - running = false; - - if (rx_thread) { - if (rx_thread->joinable()) { - rx_thread->join(); - } - } - - if (tx_thread) { - if (tx_thread->joinable()) { - tx_thread->join(); - } - } - - return true; - }; - - pnf_metrics_t get_metrics() - { - pnf_metrics_t tmp = metrics; - metrics = {}; - return tmp; - } - - void connect_out_rf_queue(srsran::block_queue* rf_queue_) - { - rf_out_queue = rf_queue_; - policy = bridge; - } - srsran::block_queue* get_in_rf_queue() - { - policy = bridge; - return &rf_in_queue; - } - -private: - //! Waits for DL Config or Tx Request Msg from VNF and forwards to RF - void dl_handler_thread() - { - pthread_setname_np(pthread_self(), rx_thread_name.c_str()); - - // set_rt_prio(); - - struct pollfd fd; - fd.fd = sockfd; - fd.events = POLLIN; - - std::unique_ptr rx_buffer{new msg_buffer_t{}}; - - while (running) { - // receive response - int ret = poll(&fd, 1, RX_TIMEOUT_MS); - switch (ret) { - case -1: - printf("Error occurred.\n"); - running = false; - break; - case 0: - // Timeout - printf("Error: Didn't receive response after %dms\n", RX_TIMEOUT_MS); - break; - default: - int recv_ret = recv(sockfd, rx_buffer->data(), sizeof(*rx_buffer), 0); - handle_msg(rx_buffer->data(), recv_ret); - break; - } - - std::lock_guard lock(mutex); - auto rtt = - std::chrono::duration_cast(std::chrono::steady_clock::now() - tti_start_time) - .count(); - - // TODO: add averaging - metrics.avg_rtt_us = rtt; - } - }; - - void ul_handler_thread() - { - pthread_setname_np(pthread_self(), tx_thread_name.c_str()); - - // set_rt_prio(); - - struct pollfd fd; - fd.fd = sockfd; - fd.events = POLLIN; - - const uint32_t max_basic_api_pdu = sizeof(basic_vnf_api::dl_conf_msg_t) + 32; // larger than biggest message - std::unique_ptr > rx_buffer = - std::unique_ptr >(new std::array); - - int32_t sf_counter = 0; - while (running && (num_sf > 0 ? sf_counter < num_sf : true)) { - { - std::lock_guard lock(mutex); - - // Increase TTI - tti = (tti + 1) % 10240; - - // Take time before sending the SF indication - tti_start_time = std::chrono::steady_clock::now(); - - // Send request - send_sf_ind(tti); - - if (policy == bridge) { - // send_rx_data_ind(tti); - } else { - // provide UL data every 2nd TTI - if (tti % 2 == 0) { - send_rx_data_ind(tti); - } - } - - sf_counter++; - } - - std::this_thread::sleep_for(std::chrono::microseconds(sf_interval_us)); - } - - printf("Leaving Tx thread after %d subframes\n", sf_counter); - }; - - void ue_dl_handler_thread() - { - pthread_setname_np(pthread_self(), tx_thread_name.c_str()); - - // set_rt_prio(); - - struct pollfd fd; - fd.fd = sockfd; - fd.events = POLLIN; - - const uint32_t max_basic_api_pdu = sizeof(basic_vnf_api::dl_conf_msg_t) + 32; // larger than biggest message - std::unique_ptr > rx_buffer = - std::unique_ptr >(new std::array); - - int32_t sf_counter = 0; - while (running && (num_sf > 0 ? sf_counter < num_sf : true)) { - { - std::lock_guard lock(mutex); - - // Increase TTI - tti = (tti + 1) % 10240; - - // Take time before sending the SF indication - tti_start_time = std::chrono::steady_clock::now(); - - // Send SF indication - send_sf_ind(tti); - - if (policy == bridge) { - srsran::unique_byte_buffer_t tb; - if (rf_in_queue.try_pop(&tb)) { - send_dl_ind(tti, std::move(tb)); - } - } else { - // provide DL grant every even TTI, and UL grant every odd - if (tti % 2 == 0) { - send_dl_ind(tti); - } else { - send_ul_ind(tti); - } - } - - sf_counter++; - } - - std::this_thread::sleep_for(std::chrono::microseconds(sf_interval_us)); - } - - printf("Leaving Tx thread after %d subframes\n", sf_counter); - }; - - void send_sf_ind(uint32_t tti_) - { - basic_vnf_api::sf_ind_msg_t sf_ind; - bzero(&sf_ind, sizeof(sf_ind)); - sf_ind.header.type = basic_vnf_api::SF_IND; - sf_ind.header.msg_len = sizeof(sf_ind) - sizeof(basic_vnf_api::msg_header_t); - sf_ind.tti = tti_; - sf_ind.t1 = 0; - sf_ind.tb_len = tb_len > 0 ? tb_len : rand_dist(rand_gen); - - int n = 0; - if ((n = sendto(sockfd, &sf_ind, sizeof(sf_ind), 0, (struct sockaddr*)&servaddr, sizeof(servaddr))) < 0) { - printf("sendto failed, ret=%d\n", n); - } - } - - int handle_msg(const uint8_t* buffer, const uint32_t len) - { - basic_vnf_api::msg_header_t* header = (basic_vnf_api::msg_header_t*)buffer; - - logger.debug("Received %s (%d B) in TTI", basic_vnf_api::msg_type_text[header->type], len); - - switch (header->type) { - case basic_vnf_api::SF_IND: - printf("Error: %s not handled by VNF\n", basic_vnf_api::msg_type_text[header->type]); - break; - case basic_vnf_api::DL_CONFIG: - handle_dl_config((basic_vnf_api::dl_conf_msg_t*)header); - break; - case basic_vnf_api::TX_REQUEST: - handle_tx_request((basic_vnf_api::tx_request_msg_t*)header); - break; - default: - printf("Unknown msg type.\n"); - break; - } - return 0; - } - - int handle_dl_config(basic_vnf_api::dl_conf_msg_t* msg) - { - // printf("Received DL config for TTI=%d\n", msg->tti); - - if (msg->tti != tti) { - metrics.num_timing_errors++; - // printf("Received DL config for TTI=%d but current TTI is %d\n", msg->tti, tti.load()); - return -1; - } - - return 0; - } - - int handle_tx_request(basic_vnf_api::tx_request_msg_t* msg) - { - if (msg->tti != tti) { - metrics.num_timing_errors++; - } - - for (uint32_t i = 0; i < msg->nof_pdus; ++i) { - metrics.tb_size += msg->pdus[i].length; - } - - metrics.num_pdus += msg->nof_pdus; - - if (rf_out_queue != nullptr) { - uint32_t len = sizeof(*msg) - sizeof(msg->pdus->data) + msg->pdus->length; - - srsran::unique_byte_buffer_t tx = srsran::make_byte_buffer(); - if (tx == nullptr) { - return -1; - } - memcpy(tx->msg, msg, len); - rf_out_queue->push(std::move(tx)); - } - - return 0; - } - - void send_rx_data_ind(const uint32_t tti_) - { - // MAC PDU for UL-SCH with IPv6 router solicitation - static uint8_t tv[] = {0x04, 0x38, 0x00, 0x80, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x08, 0x3a, 0xff, 0xfe, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x44, 0x4b, 0x0f, 0x2c, 0x33, 0x98, 0xf2, - 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x85, 0x00, 0x4b, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x7f, 0x00, 0x00, 0x3f, 0x00}; - - basic_vnf_api::rx_data_ind_msg_t rx_ind = {}; - - rx_ind.header.type = basic_vnf_api::RX_DATA_IND; - rx_ind.header.msg_len = sizeof(rx_ind) - sizeof(basic_vnf_api::msg_header_t); - rx_ind.sfn = tti_; - rx_ind.t1 = 0; - - rx_ind.nof_pdus = 1; - rx_ind.pdus[0].type = basic_vnf_api::PUSCH; - rx_ind.pdus[0].length = tb_len > 0 ? tb_len : rand_dist(rand_gen); - - if (rx_ind.pdus[0].length >= sizeof(tv)) { - // copy TV - memcpy(rx_ind.pdus[0].data, tv, sizeof(tv)); - // set remaining bytes to zero - memset(rx_ind.pdus[0].data + sizeof(tv), 0xaa, rx_ind.pdus[0].length - sizeof(tv)); - } else { - // just fill with dummy bytes - memset(rx_ind.pdus[0].data, 0xab, rx_ind.pdus[0].length); - } - - int n = 0; - if ((n = sendto(sockfd, &rx_ind, sizeof(rx_ind), 0, (struct sockaddr*)&servaddr, sizeof(servaddr))) < 0) { - printf("sendto failed, ret=%d\n", n); - } - } - - void send_dl_ind(uint32_t tti_, srsran::unique_byte_buffer_t tb = {}) - { -#if PING_REQUEST_PDU - static uint8_t tv[] = { - 0x04, 0x5c, 0x00, 0x80, 0x00, 0x00, 0x45, 0x00, 0x00, 0x54, 0x15, 0x02, 0x40, 0x00, 0x40, 0x01, 0xa2, 0x52, - 0xc0, 0xa8, 0x01, 0x01, 0xc0, 0xa8, 0x01, 0x03, 0x08, 0x00, 0x26, 0x40, 0x5e, 0x8f, 0x00, 0xb3, 0x04, 0x55, - 0xc4, 0x5d, 0x00, 0x00, 0x00, 0x00, 0xe4, 0xf7, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x12, 0x13, - 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, - 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, - 0x4f, 0x7f, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; -#else - // MAC PDU with a single LCID with padding only - static uint8_t tv[] = { - 0x01, - 0x08, - 0x11, - 0x22, - 0x33, - 0x44, - 0x55, - 0x66, - 0x77, - 0x88, - 0x3f, - }; -#endif // PING_REQUEST_PDU - basic_vnf_api::dl_ind_msg_t dl_ind = {}; - - dl_ind.header.type = basic_vnf_api::DL_IND; - dl_ind.header.msg_len = sizeof(dl_ind) - sizeof(basic_vnf_api::msg_header_t); - dl_ind.tti = tti_; - dl_ind.t1 = 0; - - uint32_t tot_bytes = 0; - uint32_t tb_size = tb_len > 0 ? tb_len : rand_dist(rand_gen); - - if (tb != nullptr) { - auto* header = (basic_vnf_api::msg_header_t*)tb->msg; - - if (header->type == basic_vnf_api::TX_REQUEST) { - auto* tx_req = (basic_vnf_api::tx_request_msg_t*)tb->msg; - dl_ind.nof_pdus = tx_req->nof_pdus; - // dl_ind.tti = tx_req->tti; - for (uint32_t i = 0; i < dl_ind.nof_pdus; ++i) { - dl_ind.pdus[i].length = tx_req->pdus[i].length; - dl_ind.pdus[i].type = tx_req->pdus[i].type; - memcpy(dl_ind.pdus[i].data, tx_req->pdus[i].data, dl_ind.pdus[i].length); - tot_bytes += dl_ind.pdus[i].length; - logger.info( - dl_ind.pdus[i].data, dl_ind.pdus[i].length, "Sending to UE a PDU (%d bytes)", dl_ind.pdus[i].length); - } - } - } else { - uint32_t N_bytes = sizeof(tv); - - // Create default - dl_ind.nof_pdus = 1; - dl_ind.pdus[0].type = basic_vnf_api::PDSCH; - dl_ind.pdus[0].length = tb_size; - - if (dl_ind.pdus[0].length >= N_bytes) { - // copy TV - memcpy(dl_ind.pdus[0].data, tv, N_bytes); - tot_bytes = N_bytes; - } - - logger.info(dl_ind.pdus[0].data, N_bytes, "Sending to UE a TB (%d bytes)", N_bytes); - } - - if (tot_bytes > 0 and tot_bytes < tb_size) { - uint8_t* offset = &dl_ind.pdus[dl_ind.nof_pdus - 1].data[dl_ind.pdus[dl_ind.nof_pdus - 1].length]; - memset(offset, 0xaa, tb_size - tot_bytes); - } else if (tot_bytes == 0) { - // just fill with dummy bytes - dl_ind.nof_pdus = 1; - memset(dl_ind.pdus[0].data, 0xab, tb_size); - } - - int n = 0; - if ((n = sendto(sockfd, &dl_ind, sizeof(dl_ind), 0, (struct sockaddr*)&servaddr, sizeof(servaddr))) < 0) { - printf("sendto failed, ret=%d\n", n); - } - } - - void send_ul_ind(uint32_t tti_) - { - basic_vnf_api::ul_ind_msg_t ul_ind = {}; - - ul_ind.header.type = basic_vnf_api::UL_IND; - ul_ind.header.msg_len = sizeof(ul_ind) - sizeof(basic_vnf_api::msg_header_t); - ul_ind.tti = tti_; - ul_ind.t1 = 0; - - ul_ind.pdus.type = basic_vnf_api::PUSCH; - ul_ind.pdus.length = tb_len > 0 ? tb_len : rand_dist(rand_gen); - - int n = 0; - if ((n = sendto(sockfd, &ul_ind, sizeof(ul_ind), 0, (struct sockaddr*)&servaddr, sizeof(servaddr))) < 0) { - printf("sendto failed, ret=%d\n", n); - } - } - - std::unique_ptr tx_thread, rx_thread; - std::string tx_thread_name = "TX_PNF", rx_thread_name = "RX_PNF"; - bool running = false; - srslog::basic_logger& logger = srslog::fetch_basic_logger("PNF", false); - - std::mutex mutex; - std::atomic tti; - std::chrono::steady_clock::time_point tti_start_time; - - std::string type; - - std::string vnf_addr; - uint16_t vnf_port = 3333; - - uint32_t sf_interval_us = 1000; - int32_t num_sf = -1; - uint32_t tb_len = 100; - - pnf_metrics_t metrics = {}; - - int sockfd = 0; - struct sockaddr_in servaddr = {}; - - // For random number generation - std::mt19937 rand_gen; - std::uniform_int_distribution rand_dist; - - // two policies possible: dummy packets generated by the PNF class, or bridge between UE and gNB PNFs with TBs - // entering/exiting each PNF via the rf_in_queue/rf_out_queue. - srsran::block_queue* rf_out_queue = nullptr; - srsran::block_queue rf_in_queue; - enum data_policy_t { self_gen, bridge } policy = self_gen; -}; - -} // namespace srsran - -#endif // SRSRAN_BASIC_PNF_H diff --git a/lib/include/srsran/common/basic_vnf.h b/lib/include/srsran/common/basic_vnf.h deleted file mode 100644 index d68952950..000000000 --- a/lib/include/srsran/common/basic_vnf.h +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Copyright 2013-2021 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/. - * - */ - -#ifndef SRSRAN_BASIC_VNF_H -#define SRSRAN_BASIC_VNF_H - -#include "basic_vnf_api.h" -#include "common.h" -#include "srsran/common/threads.h" -#include "srsran/interfaces/gnb_interfaces.h" -#include "srsran/interfaces/ue_nr_interfaces.h" -#include "srsran/srslog/srslog.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace srsran { - -class srsran_basic_vnf : public thread -{ -public: - srsran_basic_vnf(const vnf_args_t& args_, stack_interface_phy_nr* stack_); - ~srsran_basic_vnf(); - - bool stop(); - - int dl_config_request(const srsenb::phy_interface_stack_nr::dl_config_request_t& request); - int tx_request(const srsenb::phy_interface_stack_nr::tx_request_t& request); - int tx_request(const srsue::phy_interface_stack_nr::tx_request_t& request); - -private: - void run_thread(); - - // handlers - int handle_msg(const uint8_t* buffer, const uint32_t len); - int handle_sf_ind(basic_vnf_api::sf_ind_msg_t* msg); - int handle_dl_ind(basic_vnf_api::dl_ind_msg_t* msg); - int handle_ul_ind(basic_vnf_api::ul_ind_msg_t* msg); - int handle_rx_data_ind(basic_vnf_api::rx_data_ind_msg_t* msg); - - // senders - int send_dl_config_request(); - - // helpers - uint32_t calc_full_msg_len(const basic_vnf_api::tx_request_msg_t& msg); - - srslog::basic_logger& logger = srslog::fetch_basic_logger("VNF", false); - srsenb::stack_interface_phy_nr* m_gnb_stack = nullptr; - srsue::stack_interface_phy_nr* m_ue_stack = nullptr; - - std::unique_ptr m_tx_req_msg; - - bool running = false; - - vnf_args_t m_args = {}; - - int sockfd = 0; - struct sockaddr_in servaddr = {}, client_addr = {}; - - uint32_t last_sf_indication_time = 0; -}; - -} // namespace srsran - -#endif // SRSRAN_BASIC_VNF_H diff --git a/lib/include/srsran/common/basic_vnf_api.h b/lib/include/srsran/common/basic_vnf_api.h deleted file mode 100644 index 52dc7aedc..000000000 --- a/lib/include/srsran/common/basic_vnf_api.h +++ /dev/null @@ -1,168 +0,0 @@ -/** - * Copyright 2013-2021 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/. - * - */ - -#ifndef SRSRAN_BASIC_VNF_API_H -#define SRSRAN_BASIC_VNF_API_H - -#include - -namespace basic_vnf_api { - -// PNF (the PHY) VNF (the L2/L3) -// | | -// | | -// | - | -// | \ sf_ind_msg_t -// | \ | -// | \ | -// | \ | -// | \ | -// | \ | -// | \| -// | | -// | | DL_CONFIG.request -// | /| -// | / | -// | / | -// | / | -// | / | -// | / dl_conf_msg_t -// | / | -// |/ | TX.request -// | /| -// | / | -// | / | -// | / | -// | / | -// | / tx_request_msg_t -// | / | -// |/ | -// | | - -// Primitive API messages for basic testing basic VNF/PNF interaction -enum msg_type_t { - SF_IND, ///< To signal start of new subframe (later slot) for both UE and gNB - DL_CONFIG, ///< To configure the DL for gNB - TX_REQUEST, ///< For DL data for gNB - RX_DATA_IND, ///< For UL Data for gNB - DL_IND, ///< For the UE for DL data - UL_IND, ///< For the UE for UL Data - MSG_TYPE_NITEMS -}; -static const char* msg_type_text[MSG_TYPE_NITEMS] = - {"SF Indication", "DL_CONFIG.Request", "TX.Request", "RX_Data.indication", "DL_Indication", "UL_Indication"}; -enum pdu_type_t { MAC_PBCH, PHY_PBCH, PDCCH, PDSCH, PUSCH }; - -struct msg_header_t { - msg_type_t type; - uint32_t msg_len; -}; - -struct sf_ind_msg_t { - msg_header_t header; - uint32_t t1; // Timestamp taken at PNF - uint32_t tti; // TTI of requested subframe - uint32_t tb_len; // Length of the TB -}; - -#define MAX_TB_LEN (16 * 1024) -#define MAX_PDU_SIZE (16 * 1024) -#define MAX_NUM_PDUS (1) - -struct phy_pbch_pdu_t { - uint16_t phy_cell_id; // 0 - 1007 - uint8_t ss_block_index; // 0-63 - uint8_t ssb_sc_offset; // 0-15 - uint8_t dmrs_pos; // 0-1 - uint8_t pdcch_config; // 0-255 -}; - -struct dl_conf_msg_t { - msg_header_t header; - uint32_t t1; // Replayed timestamp - uint32_t t2; // Timestamp taken at VNF - uint32_t tti; // TTI - uint16_t beam_id; // tx beam id for the slot -}; - -struct tx_request_pdu_t { - uint16_t length; - uint16_t index; // index indicated in dl_config - pdu_type_t type; // physical chan of pdu/tb - uint8_t data[MAX_PDU_SIZE]; -}; - -struct tx_request_msg_t { - msg_header_t header; - uint32_t tti; // TTI - uint32_t tb_len; // actual TB len - uint32_t nof_pdus; - tx_request_pdu_t pdus[MAX_NUM_PDUS]; -}; - -struct rx_data_ind_pdu_t { - uint16_t length; - pdu_type_t type; // physical chan of pdu/tb - uint8_t data[MAX_PDU_SIZE]; -}; - -struct rx_data_ind_msg_t { - msg_header_t header; - uint32_t t1; // Timestamp taken at PNF - uint32_t sfn; ///< SFN (0-1023) - uint32_t slot; ///< Slot (0-319) - uint32_t tb_len; ///< actual TB len - uint32_t nof_pdus; // - rx_data_ind_pdu_t pdus[MAX_NUM_PDUS]; -}; - -// UE specific messages -struct dl_ind_pdu_t { - pdu_type_t type; // physical chan of pdu/tb - uint16_t length; - uint8_t data[MAX_PDU_SIZE]; -}; - -struct dl_ind_msg_t { - msg_header_t header; - uint32_t t1; // Timestamp taken at PNF - uint32_t tti; // tti or slot? - uint32_t nof_pdus; - dl_ind_pdu_t pdus[MAX_NUM_PDUS]; -}; - -///< Messages for UL (only one PDU) -struct ul_ind_pdu_t { - pdu_type_t type; // physical chan of pdu/tb - uint16_t length; -}; - -struct ul_ind_msg_t { - msg_header_t header; - uint32_t t1; // Timestamp taken at PNF - uint32_t tti; // tti or slot? - uint32_t rnti; ///< RNTI of this grant - ul_ind_pdu_t pdus; -}; - -} // namespace basic_vnf_api - -#endif // SRSRAN_BASIC_VNF_API_H diff --git a/lib/include/srsran/common/signal_handler.h b/lib/include/srsran/common/signal_handler.h index 03b9a356d..4b17067a2 100644 --- a/lib/include/srsran/common/signal_handler.h +++ b/lib/include/srsran/common/signal_handler.h @@ -40,7 +40,7 @@ extern "C" { // static vars required by signal handling static srslog::sink* log_sink = nullptr; -static bool running = true; +static std::atomic running = {true}; static void srsran_signal_handler(int signal) { diff --git a/lib/include/srsran/interfaces/enb_mac_interfaces.h b/lib/include/srsran/interfaces/enb_mac_interfaces.h index 165642dbf..2da352d51 100644 --- a/lib/include/srsran/interfaces/enb_mac_interfaces.h +++ b/lib/include/srsran/interfaces/enb_mac_interfaces.h @@ -34,6 +34,7 @@ struct mac_args_t { int nr_tb_size = -1; uint32_t nof_prealloc_ues; ///< Number of UE resources to pre-allocate at eNB startup uint32_t max_nof_kos; + int rlf_min_ul_snr_estim; }; /* Interface PHY -> MAC */ diff --git a/lib/include/srsran/interfaces/gnb_interfaces.h b/lib/include/srsran/interfaces/gnb_interfaces.h index fda78af8e..1031b318b 100644 --- a/lib/include/srsran/interfaces/gnb_interfaces.h +++ b/lib/include/srsran/interfaces/gnb_interfaces.h @@ -171,42 +171,31 @@ public: virtual void notify_pdcp_integrity_error(uint16_t rnti, uint32_t lcid) = 0; }; -class phy_interface_stack_nr +class phy_interface_rrc_nr { public: - const static int MAX_DL_GRANTS = 4; + /** + * @brief Describes physical layer configuration common among all the UEs for a given cell + */ + struct common_cfg_t { + srsran_carrier_nr_t carrier; + srsran_pdcch_cfg_nr_t pdcch; + srsran_prach_cfg_t prach; + }; - typedef struct { - // TODO: include NR related fields - } dl_sched_grant_t; + virtual int set_common_cfg(const common_cfg_t& common_cfg) = 0; +}; - typedef struct { - bool mib_present; - } bch_sched_t; +class phy_interface_mac_nr +{ +public: + // TBD +}; - typedef struct { - uint32_t tti; - uint32_t nof_grants; - dl_sched_grant_t pdsch[MAX_DL_GRANTS]; - int beam_id; - } dl_config_request_t; - - typedef struct { - bch_sched_t pbch; - uint16_t length; - uint16_t index; // index indicated in dl_config - uint8_t* data[SRSRAN_MAX_TB]; // always a pointer in our case - } tx_request_pdu_t; - - typedef struct { - uint32_t tti; - uint32_t tb_len; - uint32_t nof_pdus; - tx_request_pdu_t pdus[MAX_DL_GRANTS]; - } tx_request_t; - - virtual int dl_config_request(const dl_config_request_t& request) = 0; - virtual int tx_request(const tx_request_t& request) = 0; +class phy_interface_stack_nr : public phy_interface_rrc_nr, public phy_interface_mac_nr +{ +public: + // TBD }; class stack_interface_mac @@ -280,11 +269,17 @@ public: // ... add signal measurements here }; - virtual int slot_indication(const srsran_slot_cfg_t& slot_cfg) = 0; - virtual int get_dl_sched(const srsran_slot_cfg_t& slot_cfg, dl_sched_t& dl_sched) = 0; - virtual int get_ul_sched(const srsran_slot_cfg_t& slot_cfg, ul_sched_t& ul_sched) = 0; - virtual int pucch_info(const srsran_slot_cfg_t& slot_cfg, const pucch_info_t& pucch_info) = 0; - virtual int pusch_info(const srsran_slot_cfg_t& slot_cfg, const pusch_info_t& pusch_info) = 0; + struct rach_info_t { + uint32_t preamble; + uint32_t time_adv; + }; + + virtual int slot_indication(const srsran_slot_cfg_t& slot_cfg) = 0; + virtual int get_dl_sched(const srsran_slot_cfg_t& slot_cfg, dl_sched_t& dl_sched) = 0; + virtual int get_ul_sched(const srsran_slot_cfg_t& slot_cfg, ul_sched_t& ul_sched) = 0; + virtual int pucch_info(const srsran_slot_cfg_t& slot_cfg, const pucch_info_t& pucch_info) = 0; + virtual int pusch_info(const srsran_slot_cfg_t& slot_cfg, const pusch_info_t& pusch_info) = 0; + virtual void rach_detected(const rach_info_t& rach_info) = 0; }; class stack_interface_phy_nr : public mac_interface_phy_nr, public srsran::stack_interface_phy_nr diff --git a/lib/include/srsran/interfaces/sched_interface.h b/lib/include/srsran/interfaces/sched_interface.h index dbcdca8a7..6f31c8750 100644 --- a/lib/include/srsran/interfaces/sched_interface.h +++ b/lib/include/srsran/interfaces/sched_interface.h @@ -75,6 +75,7 @@ public: int init_ul_snr_value = 5; int init_dl_cqi = 5; float max_sib_coderate = 0.8; + int pdcch_cqi_offset = 0; }; struct cell_cfg_t { diff --git a/lib/include/srsran/interfaces/ue_nas_interfaces.h b/lib/include/srsran/interfaces/ue_nas_interfaces.h index 15f02819d..a1850064e 100644 --- a/lib/include/srsran/interfaces/ue_nas_interfaces.h +++ b/lib/include/srsran/interfaces/ue_nas_interfaces.h @@ -48,6 +48,17 @@ public: virtual bool connection_request_completed(bool outcome) = 0; }; +class nas_5g_interface_rrc_nr +{ +public: +}; + +class nas_5g_interface_procedures +{ +public: + virtual int send_registration_request() = 0; +}; + } // namespace srsue #endif // SRSRAN_UE_NAS_INTERFACES_H diff --git a/lib/include/srsran/interfaces/ue_nr_interfaces.h b/lib/include/srsran/interfaces/ue_nr_interfaces.h index 4ce83ec01..250f07c35 100644 --- a/lib/include/srsran/interfaces/ue_nr_interfaces.h +++ b/lib/include/srsran/interfaces/ue_nr_interfaces.h @@ -184,6 +184,7 @@ struct phy_args_nr_t { srsran_ue_ul_nr_args_t ul = {}; std::set fixed_sr = {1}; uint32_t fix_wideband_cqi = 15; // Set to a non-zero value for fixing the wide-band CQI report + bool store_pdsch_ko = false; phy_args_nr_t() { diff --git a/lib/include/srsran/interfaces/ue_phy_interfaces.h b/lib/include/srsran/interfaces/ue_phy_interfaces.h index 4a5fcdba3..e84196515 100644 --- a/lib/include/srsran/interfaces/ue_phy_interfaces.h +++ b/lib/include/srsran/interfaces/ue_phy_interfaces.h @@ -95,6 +95,8 @@ struct phy_args_t { float force_ul_amplitude = 0.0f; bool detect_cp = false; + bool nr_store_pdsch_ko = false; + float in_sync_rsrp_dbm_th = -130.0f; float in_sync_snr_db_th = 1.0f; uint32_t nof_in_sync_events = 10; @@ -138,8 +140,8 @@ public: } prach_info_t; virtual void - prach_send(uint32_t preamble_idx, int allowed_subframe, float target_power_dbm, float ta_base_sec = 0.0f) = 0; - virtual prach_info_t prach_get_info() = 0; + prach_send(uint32_t preamble_idx, int allowed_subframe, float target_power_dbm, float ta_base_sec = 0.0f) = 0; + virtual prach_info_t prach_get_info() = 0; /* Indicates the transmission of a SR signal in the next opportunity */ virtual void sr_send() = 0; diff --git a/lib/include/srsran/interfaces/ue_rrc_interfaces.h b/lib/include/srsran/interfaces/ue_rrc_interfaces.h index 24e8a8697..b9d216704 100644 --- a/lib/include/srsran/interfaces/ue_rrc_interfaces.h +++ b/lib/include/srsran/interfaces/ue_rrc_interfaces.h @@ -122,6 +122,10 @@ public: virtual bool is_config_pending() = 0; }; +class rrc_nr_interface_nas_5g +{ +public: +}; } // namespace srsue #endif // SRSRAN_UE_RRC_INTERFACES_H diff --git a/lib/include/srsran/phy/phch/dci_nr.h b/lib/include/srsran/phy/phch/dci_nr.h index 0418722ca..de3396a31 100644 --- a/lib/include/srsran/phy/phch/dci_nr.h +++ b/lib/include/srsran/phy/phch/dci_nr.h @@ -156,8 +156,14 @@ typedef struct SRSRAN_API { uint32_t pid; ///< HARQ process number uint32_t dai; ///< Downlink assignment index uint32_t tpc; ///< TPC command for scheduled PUCCH - uint32_t pucch_resource; ///< PUCCH resource indicator + uint32_t pucch_resource; ///< PUCCH resource indicator for HARQ feedback + ///< @note PUCCH resource is selected from PUCCH-ResourceSet if available, otherwise the UE + ///< shall pick a pucch-ResourceCommon from Table 9.2.1-1. uint32_t harq_feedback; ///< PDSCH-to-HARQ_feedback timing indicator + ///< @note harq_feedback for format 1_0 indicates the delay between the PDSCH reception and + ///< the UL transmission timing + ///< @note harq_feedback for format 1_1 is index of the delay indicated in DL data to UL ACK + ///< dedicated configuration table // P-RNTI specific fields uint32_t smi; ///< Short Messages Indicator diff --git a/lib/include/srsran/phy/phch/uci_cfg_nr.h b/lib/include/srsran/phy/phch/uci_cfg_nr.h index ccd485dc8..5e062e8b9 100644 --- a/lib/include/srsran/phy/phch/uci_cfg_nr.h +++ b/lib/include/srsran/phy/phch/uci_cfg_nr.h @@ -44,12 +44,11 @@ * @brief Uplink Control Information bits configuration for PUCCH transmission */ typedef struct { - uint16_t rnti; ///< RNTI - uint32_t resource_id; ///< PUCCH resource indicator field in the DCI format 1_0 or DCI format 1_1 - uint32_t n_cce_0; ///< index of a first CCE for the PDCCH reception - uint32_t N_cce; ///< number of CCEs in a CORESET of a PDCCH reception with DCI format 1_0 or 1_1 - uint32_t sr_resource_id; ///< Scheduling request resource identifier, only valid if positive SR - bool sr_positive_present; ///< Set to true if there is at least one positive SR + uint16_t rnti; ///< RNTI + uint32_t resource_id; ///< PUCCH resource indicator field in the DCI format 1_0 or DCI format 1_1 + uint32_t n_cce_0; ///< index of a first CCE for the PDCCH reception + uint32_t N_cce; ///< number of CCEs in a CORESET of a PDCCH reception with DCI format 1_0 or 1_1 + uint32_t sr_resource_id; ///< Scheduling request resource identifier, only valid if positive SR } srsran_uci_nr_pucch_cfg_t; /** @@ -79,6 +78,7 @@ typedef struct SRSRAN_API { /// Common Parameters srsran_harq_ack_cfg_t ack; ///< HARQ-ACK configuration uint32_t o_sr; ///< Number of SR bits + bool sr_positive_present; ///< Set to true if there is at least one positive SR srsran_csi_report_cfg_t csi[SRSRAN_CSI_MAX_NOF_REPORT]; ///< CSI report configuration uint32_t nof_csi; ///< Number of CSI reports union { diff --git a/lib/include/srsran/radio/radio.h b/lib/include/srsran/radio/radio.h index d4a3859ab..befb33c1d 100644 --- a/lib/include/srsran/radio/radio.h +++ b/lib/include/srsran/radio/radio.h @@ -99,6 +99,7 @@ private: std::vector rf_info = {}; std::vector rx_offset_n = {}; rf_metrics_t rf_metrics = {}; + std::mutex metrics_mutex; srslog::basic_logger& logger = srslog::fetch_basic_logger("RF", false); phy_interface_radio* phy = nullptr; cf_t* zeros = nullptr; diff --git a/lib/include/srsran/srslog/shared_types.h b/lib/include/srsran/srslog/shared_types.h index 7d6e7b5a3..2693d3f55 100644 --- a/lib/include/srsran/srslog/shared_types.h +++ b/lib/include/srsran/srslog/shared_types.h @@ -23,6 +23,7 @@ #define SRSLOG_SHARED_TYPES_H #include +#include namespace srslog { @@ -30,8 +31,7 @@ namespace srslog { using error_handler = std::function; /// Backend priority levels. -enum class backend_priority -{ +enum class backend_priority { /// Default priority of the operating system. normal, /// Thread will be given a high priority. @@ -40,6 +40,18 @@ enum class backend_priority very_high }; +/// syslog log local types +enum class syslog_local_type { + local0, + local1, + local2, + local3, + local4, + local5, + local6, + local7, +}; + } // namespace srslog #endif // SRSLOG_SHARED_TYPES_H diff --git a/lib/include/srsran/srslog/srslog.h b/lib/include/srsran/srslog/srslog.h index 68c901f2f..2269790f5 100644 --- a/lib/include/srsran/srslog/srslog.h +++ b/lib/include/srsran/srslog/srslog.h @@ -193,6 +193,14 @@ sink& fetch_file_sink(const std::string& path, size_t max_size = 0, std::unique_ptr f = get_default_log_formatter()); +/// Returns an instance of a sink that writes into syslog +/// preamble: The string prepended to every message, If ident is "", the program name is used. +/// log_local: custom unused facilities that syslog provides which can be used by the user +/// NOTE: Any '#' characters in the path will get removed. +sink& fetch_syslog_sink(const std::string& preamble_ = "", + syslog_local_type log_local_ = syslog_local_type::local0, + std::unique_ptr f = get_default_log_formatter()); + /// Installs a custom user defined sink in the framework getting associated to /// the specified id. Returns true on success, otherwise false. /// WARNING: This function is an advanced feature and users should really know diff --git a/lib/src/asn1/nas_5g_ies.cc b/lib/src/asn1/nas_5g_ies.cc index e93d79f06..a86a47c11 100644 --- a/lib/src/asn1/nas_5g_ies.cc +++ b/lib/src/asn1/nas_5g_ies.cc @@ -441,7 +441,7 @@ SRSASN_CODE capability_5gmm_t::pack(asn1::bit_ref& bref) HANDLE_CODE(bref.pack(cag, 1)); bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length < 1 || length > 13) { asn1::log_error("Encoding Failed (5GMM capability): Packed length (%d) is not in range of min: 1 and max 13 bytes", @@ -541,7 +541,7 @@ SRSASN_CODE ue_security_capability_t::pack(asn1::bit_ref& bref) HANDLE_CODE(bref.pack(eia7_supported, 1)); } bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length < 2 || length > 8) { asn1::log_error( @@ -617,7 +617,7 @@ SRSASN_CODE nssai_t::pack(asn1::bit_ref& bref) } bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length < 2 || length > 144) { asn1::log_error("Encoding Failed (NSSAI): Packed length (%d) is not in range of min: 2 and max 144 bytes", length); @@ -742,7 +742,7 @@ SRSASN_CODE s1_ue_network_capability_t::pack(asn1::bit_ref& bref) HANDLE_CODE(bref.pack(racs_supported, 1)); bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length < 2 || length > 13) { asn1::log_error( @@ -887,7 +887,7 @@ SRSASN_CODE uplink_data_status_t::pack(asn1::bit_ref& bref) HANDLE_CODE(bref.pack(psi_8, 1)); bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length < 2 || length > 32) { asn1::log_error( @@ -957,7 +957,7 @@ SRSASN_CODE pdu_session_status_t::pack(asn1::bit_ref& bref) HANDLE_CODE(bref.pack(psi_8, 1)); bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length < 2 || length > 32) { asn1::log_error( @@ -1037,7 +1037,7 @@ SRSASN_CODE ue_status_t::pack(asn1::bit_ref& bref) HANDLE_CODE(bref.pack(s1_mode_reg, 1)); bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length != 1) { asn1::log_error("Encoding Failed (UE status): Packed length (%d) does not equal expected length 1", length); @@ -1089,7 +1089,7 @@ SRSASN_CODE allowed_pdu_session_status_t::pack(asn1::bit_ref& bref) HANDLE_CODE(bref.pack(psi_8, 1)); bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length < 2 || length > 32) { asn1::log_error( @@ -1147,7 +1147,7 @@ SRSASN_CODE ue_usage_setting_t::pack(asn1::bit_ref& bref) HANDLE_CODE(ue_usage_setting.pack(bref)); bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length != 1) { asn1::log_error("Encoding Failed (UE usage setting): Packed length (%d) does not equal expected length 1", length); @@ -1185,7 +1185,7 @@ SRSASN_CODE drx_parameters_5gs_t::pack(asn1::bit_ref& bref) HANDLE_CODE(drx_value.pack(bref)); bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length != 1) { asn1::log_error("Encoding Failed (5GS DRX parameters): Packed length (%d) does not equal expected length 1", @@ -1221,7 +1221,7 @@ SRSASN_CODE eps_nas_message_container_t::pack(asn1::bit_ref& bref) HANDLE_CODE(bref.pack_bytes(eps_nas_message_container.data(), eps_nas_message_container.size())); bref.align_bytes_zero(); - uint16_t length = (uint16_t)(ceilf(bref.distance(bref_length) / 8) - 2); + uint16_t length = (uint16_t)(ceilf((float)bref.distance(bref_length) / 8) - 2); HANDLE_CODE(bref_length.pack(length, 16)); return SRSASN_SUCCESS; @@ -1250,7 +1250,7 @@ SRSASN_CODE ladn_indication_t::pack(asn1::bit_ref& bref) } bref.align_bytes_zero(); - uint16_t length = (uint16_t)(ceilf(bref.distance(bref_length) / 8) - 2); + uint16_t length = (uint16_t)(ceilf((float)bref.distance(bref_length) / 8) - 2); // MIN 0 not check because auf uint underflow if (length > 808) { @@ -1306,7 +1306,7 @@ SRSASN_CODE payload_container_t::pack(asn1::bit_ref& bref) HANDLE_CODE(bref.pack_bytes(payload_container_contents.data(), payload_container_contents.size())); bref.align_bytes_zero(); - uint16_t length = (uint16_t)(ceilf(bref.distance(bref_length) / 8) - 2); + uint16_t length = (uint16_t)(ceilf((float)bref.distance(bref_length) / 8) - 2); // MAX 65535 not check because auf uint overflow if (length < 1) { @@ -1370,7 +1370,7 @@ SRSASN_CODE update_type_5gs_t::pack(asn1::bit_ref& bref) HANDLE_CODE(sms_requested.pack(bref)); bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); HANDLE_CODE(bref_length.pack(length, 8)); return SRSASN_SUCCESS; @@ -1400,7 +1400,7 @@ SRSASN_CODE mobile_station_classmark_2_t::pack(asn1::bit_ref& bref) // TODO proper packing bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length != 3) { asn1::log_error("Encoding Failed (Mobile station classmark 2): Packed length (%d) does not equal expected length 3", @@ -1438,7 +1438,7 @@ SRSASN_CODE supported_codec_list_t::pack(asn1::bit_ref& bref) // TODO proper packing bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length < 3) { asn1::log_error("Encoding Failed (Supported codec list): Packed length (%d) is not in range of min: 3 bytes", @@ -1474,7 +1474,7 @@ SRSASN_CODE message_container_t::pack(asn1::bit_ref& bref) HANDLE_CODE(bref.pack_bytes(nas_message_container.data(), nas_message_container.size())); bref.align_bytes_zero(); - uint16_t length = (uint16_t)(ceilf(bref.distance(bref_length) / 8) - 2); + uint16_t length = (uint16_t)(ceilf((float)bref.distance(bref_length) / 8) - 2); if (length < 1 || length > 65532) { asn1::log_error( @@ -1527,7 +1527,7 @@ SRSASN_CODE eps_bearer_context_status_t::pack(asn1::bit_ref& bref) HANDLE_CODE(bref.pack(ebi_8, 1)); bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length != 2) { asn1::log_error("Encoding Failed (EPS bearer context status): Packed length (%d) does not equal expected length 2", @@ -1579,7 +1579,7 @@ SRSASN_CODE extended_drx_parameters_t::pack(asn1::bit_ref& bref) HANDLE_CODE(e_drx_value.pack(bref)); bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length != 1) { asn1::log_error("Encoding Failed (Extended DRX parameters): Packed length (%d) does not equal expected length 1", @@ -1616,7 +1616,7 @@ SRSASN_CODE gprs_timer_3_t::pack(asn1::bit_ref& bref) HANDLE_CODE(bref.pack(timer_value, 5)); bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length != 1) { asn1::log_error("Encoding Failed (GPRS timer 3): Packed length (%d) does not equal expected length 1", length); @@ -1650,7 +1650,7 @@ SRSASN_CODE ue_radio_capability_id_t::pack(asn1::bit_ref& bref) HANDLE_CODE(bref.pack_bytes(ue_radio_capability_id.data(), ue_radio_capability_id.size())); bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); HANDLE_CODE(bref_length.pack(length, 8)); return SRSASN_SUCCESS; @@ -1677,7 +1677,7 @@ SRSASN_CODE mapped_nssai_t::pack(asn1::bit_ref& bref) // TODO proper packing bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length < 2 || length > 40) { asn1::log_error("Encoding Failed (Mapped NSSAI): Packed length (%d) is not in range of min: 2 and max 40 bytes", @@ -1716,7 +1716,7 @@ SRSASN_CODE additional_information_requested_t::pack(asn1::bit_ref& bref) HANDLE_CODE(bref.pack(cipher_key, 1)); bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); HANDLE_CODE(bref_length.pack(length, 8)); return SRSASN_SUCCESS; @@ -1743,7 +1743,7 @@ SRSASN_CODE wus_assistance_information_t::pack(asn1::bit_ref& bref) // TODO proper packing bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length < 1) { asn1::log_error("Encoding Failed (WUS assistance information): Packed length (%d) is not in range of min: 1 bytes", @@ -1803,7 +1803,7 @@ SRSASN_CODE nb_n1_mode_drx_parameters_t::pack(asn1::bit_ref& bref) HANDLE_CODE(nb_n1_mode_drx_value.pack(bref)); bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length != 1) { asn1::log_error("Encoding Failed (NB-N1 mode DRX parameters): Packed length (%d) does not equal expected length 1", @@ -1846,7 +1846,7 @@ SRSASN_CODE registration_result_5gs_t::pack(asn1::bit_ref& bref) HANDLE_CODE(registration_result.pack(bref)); bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length != 1) { asn1::log_error("Encoding Failed (5GS registration result): Packed length (%d) does not equal expected length 1", @@ -1886,7 +1886,7 @@ SRSASN_CODE plmn_list_t::pack(asn1::bit_ref& bref) // TODO proper packing bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); HANDLE_CODE(bref_length.pack(length, 8)); return SRSASN_SUCCESS; @@ -1913,7 +1913,7 @@ SRSASN_CODE tracking_area_identity_list_5gs_t::pack(asn1::bit_ref& bref) // TODO proper packing bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length != 7) { asn1::log_error( @@ -1952,7 +1952,7 @@ SRSASN_CODE rejected_nssai_t::pack(asn1::bit_ref& bref) // TODO proper packing bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); HANDLE_CODE(bref_length.pack(length, 8)); return SRSASN_SUCCESS; @@ -1979,7 +1979,7 @@ SRSASN_CODE network_feature_support_5gs_t::pack(asn1::bit_ref& bref) // TODO proper packing bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length < 1 || length > 3) { asn1::log_error( @@ -2033,7 +2033,7 @@ SRSASN_CODE pdu_session_reactivation_result_t::pack(asn1::bit_ref& bref) HANDLE_CODE(bref.pack(psi_8, 1)); bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length < 2 || length > 32) { asn1::log_error("Encoding Failed (PDU session reactivation result): Packed length (%d) is not in range of min: 2 " @@ -2092,7 +2092,7 @@ SRSASN_CODE pdu_session_reactivation_result_error_cause_t::pack(asn1::bit_ref& b // TODO proper packing bref.align_bytes_zero(); - uint16_t length = (uint16_t)(ceilf(bref.distance(bref_length) / 8) - 2); + uint16_t length = (uint16_t)(ceilf((float)bref.distance(bref_length) / 8) - 2); if (length < 2 || length > 512) { asn1::log_error("Encoding Failed (PDU session reactivation result error cause): Packed length (%d) is not in range " @@ -2132,7 +2132,7 @@ SRSASN_CODE ladn_information_t::pack(asn1::bit_ref& bref) // TODO proper packing bref.align_bytes_zero(); - uint16_t length = (uint16_t)(ceilf(bref.distance(bref_length) / 8) - 2); + uint16_t length = (uint16_t)(ceilf((float)bref.distance(bref_length) / 8) - 2); // MIN 0 not check because auf uint underflow if (length > 1712) { @@ -2170,7 +2170,7 @@ SRSASN_CODE service_area_list_t::pack(asn1::bit_ref& bref) // TODO proper packing bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); HANDLE_CODE(bref_length.pack(length, 8)); return SRSASN_SUCCESS; @@ -2197,7 +2197,7 @@ SRSASN_CODE gprs_timer_2_t::pack(asn1::bit_ref& bref) HANDLE_CODE(bref.pack(timer_value, 8)); bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length != 1) { asn1::log_error("Encoding Failed (GPRS timer 2): Packed length (%d) does not equal expected length 1", length); @@ -2231,7 +2231,7 @@ SRSASN_CODE emergency_number_list_t::pack(asn1::bit_ref& bref) // TODO proper packing bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length < 3 || length > 48) { asn1::log_error( @@ -2270,7 +2270,7 @@ SRSASN_CODE extended_emergency_number_list_t::pack(asn1::bit_ref& bref) // TODO proper packing bref.align_bytes_zero(); - uint16_t length = (uint16_t)(ceilf(bref.distance(bref_length) / 8) - 2); + uint16_t length = (uint16_t)(ceilf((float)bref.distance(bref_length) / 8) - 2); // MAX 65535 not check because auf uint overflow if (length < 4) { @@ -2310,7 +2310,7 @@ SRSASN_CODE sor_transparent_container_t::pack(asn1::bit_ref& bref) // TODO proper packing bref.align_bytes_zero(); - uint16_t length = (uint16_t)(ceilf(bref.distance(bref_length) / 8) - 2); + uint16_t length = (uint16_t)(ceilf((float)bref.distance(bref_length) / 8) - 2); if (length < 17) { asn1::log_error("Encoding Failed (SOR transparent container): Packed length (%d) is not in range of min: 17 bytes", @@ -2347,7 +2347,7 @@ SRSASN_CODE eap_message_t::pack(asn1::bit_ref& bref) HANDLE_CODE(bref.pack_bytes(eap_message.data(), eap_message.size())); bref.align_bytes_zero(); - uint16_t length = (uint16_t)(ceilf(bref.distance(bref_length) / 8) - 2); + uint16_t length = (uint16_t)(ceilf((float)bref.distance(bref_length) / 8) - 2); if (length < 4 || length > 1500) { asn1::log_error("Encoding Failed (EAP message): Packed length (%d) is not in range of min: 4 and max 1500 bytes", @@ -2403,7 +2403,7 @@ SRSASN_CODE operator_defined_access_category_definitions_t::pack(asn1::bit_ref& // TODO proper packing bref.align_bytes_zero(); - uint16_t length = (uint16_t)(ceilf(bref.distance(bref_length) / 8) - 2); + uint16_t length = (uint16_t)(ceilf((float)bref.distance(bref_length) / 8) - 2); HANDLE_CODE(bref_length.pack(length, 16)); return SRSASN_SUCCESS; @@ -2470,7 +2470,7 @@ SRSASN_CODE ciphering_key_data_t::pack(asn1::bit_ref& bref) // TODO proper packing bref.align_bytes_zero(); - uint16_t length = (uint16_t)(ceilf(bref.distance(bref_length) / 8) - 2); + uint16_t length = (uint16_t)(ceilf((float)bref.distance(bref_length) / 8) - 2); if (length < 31 || length > 2672) { asn1::log_error( @@ -2509,7 +2509,7 @@ SRSASN_CODE cag_information_list_t::pack(asn1::bit_ref& bref) // TODO proper packing bref.align_bytes_zero(); - uint16_t length = (uint16_t)(ceilf(bref.distance(bref_length) / 8) - 2); + uint16_t length = (uint16_t)(ceilf((float)bref.distance(bref_length) / 8) - 2); HANDLE_CODE(bref_length.pack(length, 16)); return SRSASN_SUCCESS; @@ -2537,7 +2537,7 @@ SRSASN_CODE truncated_5g_s_tmsi_configuration_t::pack(asn1::bit_ref& bref) HANDLE_CODE(bref.pack(truncated_amf__pointer_value, 4)); bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length != 1) { asn1::log_error( @@ -2665,7 +2665,7 @@ SRSASN_CODE network_name_t::pack(asn1::bit_ref& bref) // TODO proper packing bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); HANDLE_CODE(bref_length.pack(length, 8)); return SRSASN_SUCCESS; @@ -2734,7 +2734,7 @@ SRSASN_CODE daylight_saving_time_t::pack(asn1::bit_ref& bref) HANDLE_CODE(value.pack(bref)); bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length != 1) { asn1::log_error("Encoding Failed (Daylight saving time): Packed length (%d) does not equal expected length 1", @@ -2808,7 +2808,7 @@ SRSASN_CODE abba_t::pack(asn1::bit_ref& bref) HANDLE_CODE(bref.pack_bytes(abba_contents.data(), abba_contents.size())); bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length < 2) { asn1::log_error("Encoding Failed (ABBA): Packed length (%d) is not in range of min: 2 bytes", length); @@ -2858,7 +2858,7 @@ SRSASN_CODE authentication_parameter_autn_t::pack(asn1::bit_ref& bref) HANDLE_CODE(bref.pack_bytes(autn.data(), autn.size())); bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length != 16) { asn1::log_error( @@ -2895,7 +2895,7 @@ SRSASN_CODE authentication_response_parameter_t::pack(asn1::bit_ref& bref) HANDLE_CODE(bref.pack_bytes(res.data(), res.size())); bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length != 16) { asn1::log_error( @@ -2932,7 +2932,7 @@ SRSASN_CODE authentication_failure_parameter_t::pack(asn1::bit_ref& bref) HANDLE_CODE(bref.pack_bytes(auth_failure.data(), auth_failure.size())); bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length != 14) { asn1::log_error( @@ -3058,7 +3058,7 @@ SRSASN_CODE additional_5g_security_information_t::pack(asn1::bit_ref& bref) HANDLE_CODE(bref.pack(hdp, 1)); bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length != 1) { asn1::log_error( @@ -3141,7 +3141,7 @@ SRSASN_CODE s1_ue_security_capability_t::pack(asn1::bit_ref& bref) HANDLE_CODE(bref.pack(gea7, 1)); bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length < 2 || length > 5) { asn1::log_error( @@ -3305,7 +3305,7 @@ SRSASN_CODE s_nssai_t::pack(asn1::bit_ref& bref) } bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length < 1 || length > 8) { asn1::log_error("Encoding Failed (S-NSSAI): Packed length (%d) is not in range of min: 1 and max 8 bytes", length); @@ -3354,7 +3354,7 @@ SRSASN_CODE dnn_t::pack(asn1::bit_ref& bref) HANDLE_CODE(bref.pack_bytes(dnn_value.data(), dnn_value.size())); bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length < 1 || length > 100) { asn1::log_error("Encoding Failed (DNN): Packed length (%d) is not in range of min: 1 and max 100 bytes", length); @@ -3388,7 +3388,7 @@ SRSASN_CODE additional_information_t::pack(asn1::bit_ref& bref) HANDLE_CODE(bref.pack_bytes(additional_information_value.data(), additional_information_value.size())); bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length < 1) { asn1::log_error("Encoding Failed (Additional information): Packed length (%d) is not in range of min: 1 bytes", @@ -3518,7 +3518,7 @@ SRSASN_CODE capability_5gsm_t::pack(asn1::bit_ref& bref) // TODO proper packing bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length < 1 || length > 13) { asn1::log_error("Encoding Failed (5GSM capability): Packed length (%d) is not in range of min: 1 and max 13 bytes", @@ -3596,7 +3596,7 @@ SRSASN_CODE sm_pdu_dn_request_container_t::pack(asn1::bit_ref& bref) HANDLE_CODE(bref.pack_bytes(dn_specific_identity.data(), dn_specific_identity.size())); bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length < 1 || length > 253) { asn1::log_error( @@ -3635,7 +3635,7 @@ SRSASN_CODE extended_protocol_configuration_options_t::pack(asn1::bit_ref& bref) // TODO proper packing bref.align_bytes_zero(); - uint16_t length = (uint16_t)(ceilf(bref.distance(bref_length) / 8) - 2); + uint16_t length = (uint16_t)(ceilf((float)bref.distance(bref_length) / 8) - 2); // MAX 65535 not check because auf uint overflow if (length < 1) { @@ -3677,7 +3677,7 @@ SRSASN_CODE ip_header_compression_configuration_t::pack(asn1::bit_ref& bref) // TODO proper packing bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); // MAX 255 not check because auf uint overflow if (length < 3) { @@ -3718,7 +3718,7 @@ SRSASN_CODE ds_tt__ethernet_port_mac_address_t::pack(asn1::bit_ref& bref) HANDLE_CODE(bref.pack_bytes(ds_tt__ethernet_port_mac_address_contents.data(), 6)); bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length != 6) { asn1::log_error( @@ -3755,7 +3755,7 @@ SRSASN_CODE ue_ds_tt_residence_time_t::pack(asn1::bit_ref& bref) HANDLE_CODE(bref.pack_bytes(ue_ds_tt_residence_time_contents.data(), 8)); bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length != 8) { asn1::log_error("Encoding Failed (UE-DS-TT residence time): Packed length (%d) does not equal expected length 8", @@ -3790,7 +3790,7 @@ SRSASN_CODE port_management_information_container_t::pack(asn1::bit_ref& bref) HANDLE_CODE( bref.pack_bytes(port_management_information_container.data(), port_management_information_container.size())); bref.align_bytes_zero(); - uint16_t length = (uint16_t)(ceilf(bref.distance(bref_length) / 8) - 2); + uint16_t length = (uint16_t)(ceilf((float)bref.distance(bref_length) / 8) - 2); // MAX 65535 not check because auf uint overflow if (length < 1) { @@ -3832,7 +3832,7 @@ SRSASN_CODE ethernet_header_compression_configuration_t::pack(asn1::bit_ref& bre HANDLE_CODE(cid__length.pack(bref)); bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length != 1) { asn1::log_error("Encoding Failed (Ethernet header compression configuration): Packed length (%d) does not equal " @@ -3888,7 +3888,7 @@ SRSASN_CODE pdu_address_t::pack(asn1::bit_ref& bref) } bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length < 5 || length > 29) { asn1::log_error("Encoding Failed (PDU address): Packed length (%d) is not in range of min: 5 and max 29 bytes", @@ -3947,7 +3947,7 @@ SRSASN_CODE qo_s_rules_t::pack(asn1::bit_ref& bref) HANDLE_CODE(bref.advance_bits(16)); bref.align_bytes_zero(); - uint16_t length = (uint16_t)(ceilf(bref.distance(bref_length) / 8) - 2); + uint16_t length = (uint16_t)(ceilf((float)bref.distance(bref_length) / 8) - 2); // MAX 65535 not check because auf uint overflow if (length < 4) { @@ -3986,7 +3986,7 @@ SRSASN_CODE session_ambr_t::pack(asn1::bit_ref& bref) HANDLE_CODE(bref.pack(session_ambr_for_uplink, 16)); bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length != 6) { asn1::log_error("Encoding Failed (Session-AMBR): Packed length (%d) does not equal expected length 6", length); @@ -4077,7 +4077,7 @@ SRSASN_CODE mapped_eps_bearer_contexts_t::pack(asn1::bit_ref& bref) // TODO proper packing bref.align_bytes_zero(); - uint16_t length = (uint16_t)(ceilf(bref.distance(bref_length) / 8) - 2); + uint16_t length = (uint16_t)(ceilf((float)bref.distance(bref_length) / 8) - 2); // MAX 65535 not check because auf uint overflow if (length < 4) { @@ -4117,7 +4117,7 @@ SRSASN_CODE qo_s_flow_descriptions_t::pack(asn1::bit_ref& bref) // TODO proper packing bref.align_bytes_zero(); - uint16_t length = (uint16_t)(ceilf(bref.distance(bref_length) / 8) - 2); + uint16_t length = (uint16_t)(ceilf((float)bref.distance(bref_length) / 8) - 2); // MAX 65535 not check because auf uint overflow if (length < 3) { @@ -4158,7 +4158,7 @@ SRSASN_CODE network_feature_support_5gsm_t::pack(asn1::bit_ref& bref) HANDLE_CODE(ept_s1.pack(bref)); bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length < 1 || length > 13) { asn1::log_error( @@ -4201,7 +4201,7 @@ SRSASN_CODE serving_plmn_rate_control_t::pack(asn1::bit_ref& bref) HANDLE_CODE(bref.pack(serving_plmn_rate_control_value, 16)); bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length != 2) { asn1::log_error("Encoding Failed (Serving PLMN rate control): Packed length (%d) does not equal expected length 2", @@ -4236,7 +4236,7 @@ SRSASN_CODE atsss_container_t::pack(asn1::bit_ref& bref) HANDLE_CODE(bref.pack_bytes(nas_message_container.data(), nas_message_container.size())); bref.align_bytes_zero(); - uint16_t length = (uint16_t)(ceilf(bref.distance(bref_length) / 8) - 2); + uint16_t length = (uint16_t)(ceilf((float)bref.distance(bref_length) / 8) - 2); // MIN 0 not check because auf uint underflow // MAX 65535 not check because auf uint overflow @@ -4337,7 +4337,7 @@ SRSASN_CODE re_attempt_indicator_t::pack(asn1::bit_ref& bref) HANDLE_CODE(bref.pack(ratc, 1)); bref.align_bytes_zero(); - uint8_t length = (uint8_t)(ceilf(bref.distance(bref_length) / 8) - 1); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); if (length != 1) { asn1::log_error("Encoding Failed (Re-attempt indicator): Packed length (%d) does not equal expected length 1", diff --git a/lib/src/asn1/rrc_utils.cc b/lib/src/asn1/rrc_utils.cc index 53df1f35b..af79bcac9 100644 --- a/lib/src/asn1/rrc_utils.cc +++ b/lib/src/asn1/rrc_utils.cc @@ -141,7 +141,9 @@ srsran::rlc_config_t make_rlc_config_t(const asn1::rrc::rlc_cfg_c& asn1_type) rlc_cfg.rlc_mode = rlc_mode_t::am; rlc_cfg.am.t_poll_retx = asn1_type.am().ul_am_rlc.t_poll_retx.to_number(); rlc_cfg.am.poll_pdu = asn1_type.am().ul_am_rlc.poll_pdu.to_number(); - rlc_cfg.am.poll_byte = asn1_type.am().ul_am_rlc.poll_byte.to_number() * 1000; // KB + rlc_cfg.am.poll_byte = asn1_type.am().ul_am_rlc.poll_byte.to_number() < 0 + ? -1 + : asn1_type.am().ul_am_rlc.poll_byte.to_number() * 1000; // KB rlc_cfg.am.max_retx_thresh = asn1_type.am().ul_am_rlc.max_retx_thres.to_number(); rlc_cfg.am.t_reordering = asn1_type.am().dl_am_rlc.t_reordering.to_number(); rlc_cfg.am.t_status_prohibit = asn1_type.am().dl_am_rlc.t_status_prohibit.to_number(); diff --git a/lib/src/common/CMakeLists.txt b/lib/src/common/CMakeLists.txt index 430d54c28..867b05e49 100644 --- a/lib/src/common/CMakeLists.txt +++ b/lib/src/common/CMakeLists.txt @@ -46,8 +46,7 @@ set(SOURCES arch_select.cc time_prof.cc version.c zuc.cc - s3g.cc - basic_vnf.cc) + s3g.cc) # Avoid warnings caused by libmbedtls about deprecated functions set_source_files_properties(security.cc PROPERTIES COMPILE_FLAGS -Wno-deprecated-declarations) diff --git a/lib/src/common/basic_vnf.cc b/lib/src/common/basic_vnf.cc deleted file mode 100644 index 7a4371e59..000000000 --- a/lib/src/common/basic_vnf.cc +++ /dev/null @@ -1,408 +0,0 @@ -/** - * Copyright 2013-2021 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 "srsran/common/basic_vnf.h" -#include "srsran/common/buffer_pool.h" -#include "srsran/interfaces/ue_nr_interfaces.h" -#include -#include -#include - -#define RAND_SEED (12314) -#define RX_TIMEOUT_MS (1000) - -namespace srsran { - -struct srsran_pnf_info_t { - // TODO: fill when needed -}; - -struct srsran_vnf_info_t {}; - -srsran_basic_vnf::srsran_basic_vnf(const vnf_args_t& args_, stack_interface_phy_nr* stack_) : - m_args(args_), thread("BASIC_VNF_P7"), m_tx_req_msg(new basic_vnf_api::tx_request_msg_t) -{ - logger.set_level(srslog::str_to_basic_level(m_args.log_level)); - logger.set_hex_dump_max_size(m_args.log_hex_limit); - - if (m_args.type == "gnb" || m_args.type == "ue") { - if (m_args.type == "gnb") { - m_gnb_stack = (srsenb::stack_interface_phy_nr*)stack_; - } else { - m_ue_stack = (srsue::stack_interface_phy_nr*)stack_; - } - - logger.info("Initializing VNF for gNB"); - start(); - } else { - logger.error("Unknown VNF type. Exiting."); - } -} - -srsran_basic_vnf::~srsran_basic_vnf() -{ - stop(); -} - -void srsran_basic_vnf::run_thread() -{ - // Bind to UDP socket - sockfd = socket(AF_INET, SOCK_DGRAM, 0); - if (sockfd < 0) { - perror("socket"); - return; - } - - // Make sockets reusable - int enable = 1; -#if defined(SO_REUSEADDR) - if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) { - perror("setsockopt(SO_REUSEADDR) failed"); - } -#endif -#if defined(SO_REUSEPORT) - if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(int)) < 0) { - perror("setsockopt(SO_REUSEPORT) failed"); - } -#endif - - servaddr.sin_family = AF_INET; - servaddr.sin_addr.s_addr = htonl(INADDR_ANY); - servaddr.sin_port = htons(m_args.bind_port); - - if (bind(sockfd, (struct sockaddr*)&servaddr, sizeof(struct sockaddr_in))) { - perror("bind"); - return; - } - - struct pollfd fd; - fd.fd = sockfd; - fd.events = POLLIN; - - const uint32_t max_basic_api_pdu = sizeof(basic_vnf_api::dl_ind_msg_t) + 32; // larger than biggest message - std::unique_ptr > rx_buffer = - std::unique_ptr >(new std::array); - - running = true; - - logger.info("Started VNF handler listening on %s:%d", m_args.bind_addr.c_str(), m_args.bind_port); - - while (running) { - int ret = poll(&fd, 1, RX_TIMEOUT_MS); - switch (ret) { - case -1: - printf("Error occured.\n"); - break; - case 0: - // Timeout - break; - default: - - socklen_t len = sizeof(client_addr); - ret = recvfrom(sockfd, rx_buffer->data(), rx_buffer->size(), MSG_WAITALL, (struct sockaddr*)&client_addr, &len); - - handle_msg(rx_buffer->data(), ret); - break; - } - } - logger.info("VNF thread stopped"); -} - -int srsran_basic_vnf::handle_msg(const uint8_t* buffer, const uint32_t len) -{ - basic_vnf_api::msg_header_t* header = (basic_vnf_api::msg_header_t*)buffer; - - logger.info("Received %s (%d B)", basic_vnf_api::msg_type_text[header->type], len); - - switch (header->type) { - case basic_vnf_api::SF_IND: - handle_sf_ind((basic_vnf_api::sf_ind_msg_t*)header); - break; - case basic_vnf_api::DL_CONFIG: - printf("Error: %s not handled by VNF\n", basic_vnf_api::msg_type_text[header->type]); - break; - case basic_vnf_api::DL_IND: - handle_dl_ind((basic_vnf_api::dl_ind_msg_t*)header); - break; - case basic_vnf_api::UL_IND: - handle_ul_ind((basic_vnf_api::ul_ind_msg_t*)header); - break; - case basic_vnf_api::RX_DATA_IND: - handle_rx_data_ind((basic_vnf_api::rx_data_ind_msg_t*)header); - break; - default: - printf("Unknown msg type.\n"); - break; - } - return 0; -} - -int srsran_basic_vnf::handle_sf_ind(basic_vnf_api::sf_ind_msg_t* msg) -{ - int ret = SRSRAN_SUCCESS; - logger.info("Received %s for TTI=%d", basic_vnf_api::msg_type_text[msg->header.type], msg->tti); - - // store Rx timestamp - last_sf_indication_time = msg->t1; - - if (m_gnb_stack != nullptr) { - srsran_slot_cfg_t slot_cfg = {}; - slot_cfg.idx = msg->tti; - m_gnb_stack->slot_indication(slot_cfg); - } else if (m_ue_stack != nullptr) { - m_ue_stack->sf_indication(msg->tti); - } else { - ret = SRSRAN_ERROR; - } - - return ret; -} - -int srsran_basic_vnf::handle_dl_ind(basic_vnf_api::dl_ind_msg_t* msg) -{ - int ret = SRSRAN_ERROR; - logger.info("Received %s for TTI=%d", basic_vnf_api::msg_type_text[msg->header.type], msg->tti); - - uint32_t cc_idx = 0; - - // fill DL struct - srsue::stack_interface_phy_nr::mac_nr_grant_dl_t dl_grant = {}; - dl_grant.tti = msg->tti; - - if (msg->nof_pdus > SRSRAN_MAX_TB) { - logger.error("Too many TBs (%d > %d)", msg->nof_pdus, SRSRAN_MAX_TB); - goto exit; - } - - for (uint32_t i = 0; i < msg->nof_pdus; ++i) { - srsue::stack_interface_phy_nr::tb_action_dl_result_t result = {}; - result.payload = srsran::make_byte_buffer(); - if (result.payload != nullptr && result.payload->get_tailroom() >= msg->pdus[i].length) { - result.ack = true; - memcpy(result.payload->msg, msg->pdus[i].data, msg->pdus[i].length); - result.payload->N_bytes = msg->pdus[i].length; - if (msg->pdus[i].type == basic_vnf_api::PDSCH) { - m_ue_stack->tb_decoded(cc_idx, dl_grant, std::move(result)); - } - } else { - logger.error("TB too big to fit into buffer (%d)", msg->pdus[i].length); - return SRSRAN_ERROR; - } - } - - ret = SRSRAN_SUCCESS; - -exit: - - return ret; -} - -int srsran_basic_vnf::handle_ul_ind(basic_vnf_api::ul_ind_msg_t* msg) -{ - logger.info("Received %s for TTI=%d", basic_vnf_api::msg_type_text[msg->header.type], msg->tti); - - if (msg->pdus.type != basic_vnf_api::PUSCH) { - logger.error("Received UL indication for wrong PDU type"); - return SRSRAN_ERROR; - } - - uint32_t cc_idx = 0; - - // fill DL struct - srsue::stack_interface_phy_nr::mac_nr_grant_ul_t ul_grant = {}; - ul_grant.tti = msg->tti; - ul_grant.tbs = msg->pdus.length; - ul_grant.rnti = msg->rnti; - - srsue::stack_interface_phy_nr::tb_action_ul_t ul_action = {}; - m_ue_stack->new_grant_ul(cc_idx, ul_grant, &ul_action); - - return SRSRAN_SUCCESS; -} - -int srsran_basic_vnf::handle_rx_data_ind(basic_vnf_api::rx_data_ind_msg_t* msg) -{ - logger.info("Received %s for TTI=%d", basic_vnf_api::msg_type_text[msg->header.type], msg->sfn); - - if (msg->nof_pdus != 1 || msg->pdus[0].type != basic_vnf_api::PUSCH) { - logger.error("Received UL indication for wrong PDU type"); - return SRSRAN_ERROR; - } - - // fill struct - srsenb::stack_interface_phy_nr::rx_data_ind_t rx_data = {}; - rx_data.tti = msg->sfn; - rx_data.tb = srsran::make_byte_buffer(); - if (rx_data.tb->get_tailroom() >= msg->pdus[0].length) { - // copy actual data - memcpy(rx_data.tb->msg, msg->pdus[0].data, msg->pdus[0].length); - rx_data.tb->N_bytes = msg->pdus[0].length; - if (msg->pdus[0].type == basic_vnf_api::PUSCH) { - m_gnb_stack->rx_data_indication(rx_data); - } - } - - return SRSRAN_SUCCESS; -} - -int srsran_basic_vnf::dl_config_request(const srsenb::phy_interface_stack_nr::dl_config_request_t& request) -{ - // Generate DL Config - basic_vnf_api::dl_conf_msg_t dl_conf = {}; - dl_conf.header.type = basic_vnf_api::DL_CONFIG; - dl_conf.header.msg_len = sizeof(dl_conf) - sizeof(basic_vnf_api::msg_header_t); - - dl_conf.t1 = last_sf_indication_time; // play back the time - dl_conf.t2 = 0xaa; // TODO: add timestamp - dl_conf.tti = request.tti; - dl_conf.beam_id = request.beam_id; - - // Send entire struct - uint32_t len = sizeof(dl_conf); - - // Send it to PNF - logger.info("Sending %s (%d B)", basic_vnf_api::msg_type_text[dl_conf.header.type], len); - int n = 0; - if ((n = sendto(sockfd, &dl_conf, len, MSG_CONFIRM, (struct sockaddr*)&client_addr, sizeof(client_addr))) < 0) { - logger.error("sendto failed, ret=%d", n); - } - - return 0; -} - -/// Tx request from UE, i.e. UL transmission -int srsran_basic_vnf::tx_request(const srsue::phy_interface_stack_nr::tx_request_t& request) -{ - // Generate Tx request - m_tx_req_msg->header.type = basic_vnf_api::TX_REQUEST; - m_tx_req_msg->header.msg_len = 0; // set further down - - m_tx_req_msg->tti = request.tti; - - m_tx_req_msg->nof_pdus = 1; - m_tx_req_msg->pdus[0].index = 0; - m_tx_req_msg->pdus[0].type = basic_vnf_api::PUSCH; - m_tx_req_msg->pdus[0].length = request.tb_len; - - if (request.tb_len <= MAX_PDU_SIZE) { - // copy data from TB0 - memcpy(m_tx_req_msg->pdus[0].data, request.data, request.tb_len); - } else { - logger.error("Trying to send %d B PDU. Maximum size is %d B", request.tb_len, MAX_PDU_SIZE); - } - - // calculate actual length of - uint32_t len = calc_full_msg_len(*m_tx_req_msg.get()); - - // update msg header length field - m_tx_req_msg->header.msg_len = len - sizeof(basic_vnf_api::msg_header_t); - - // Send it to PNF - logger.info("Sending %s (%d B)", basic_vnf_api::msg_type_text[m_tx_req_msg->header.type], len); - int n = 0; - if ((n = sendto(sockfd, m_tx_req_msg.get(), len, MSG_CONFIRM, (struct sockaddr*)&client_addr, sizeof(client_addr))) < - 0) { - logger.error("sendto failed, ret=%d", n); - } - - return 0; -} - -int srsran_basic_vnf::tx_request(const srsenb::phy_interface_stack_nr::tx_request_t& request) -{ - if (request.nof_pdus > MAX_NUM_PDUS) { - logger.error("Trying to send %d PDUs but only %d supported", request.nof_pdus, MAX_NUM_PDUS); - return SRSRAN_ERROR; - } - if (request.nof_pdus == 0) { - return SRSRAN_SUCCESS; - } - - // Generate Tx request - m_tx_req_msg->header.type = basic_vnf_api::TX_REQUEST; - m_tx_req_msg->header.msg_len = 0; // set further down - - m_tx_req_msg->nof_pdus = request.nof_pdus; - m_tx_req_msg->tti = request.tti; - - for (uint32_t i = 0; i < m_tx_req_msg->nof_pdus; ++i) { - if (request.pdus[i].length <= MAX_PDU_SIZE) { - m_tx_req_msg->pdus[i].index = i; - m_tx_req_msg->pdus[i].type = request.pdus[i].pbch.mib_present ? basic_vnf_api::MAC_PBCH : basic_vnf_api::PDSCH; - m_tx_req_msg->pdus[i].length = request.pdus[i].length; - // copy data from TB0 - memcpy(m_tx_req_msg->pdus[i].data, request.pdus[i].data[0], m_tx_req_msg->pdus[i].length); - } else { - logger.error("Trying to send %d B PDU. Maximum size is %d B", request.pdus[i].length, MAX_PDU_SIZE); - } - } - - // calculate actual length of message - uint32_t len = calc_full_msg_len(*m_tx_req_msg.get()); - - // update msg header length field - m_tx_req_msg->header.msg_len = len - sizeof(basic_vnf_api::msg_header_t); - - // Send it to PNF - logger.info("Sending %s (%d B)", basic_vnf_api::msg_type_text[m_tx_req_msg->header.type], len); - if (logger.debug.enabled()) { - for (uint32_t i = 0; i < m_tx_req_msg->nof_pdus; ++i) { - logger.debug(m_tx_req_msg->pdus[i].data, - m_tx_req_msg->pdus[i].length, - "Sending PDU %s:%d (%d bytes)", - basic_vnf_api::msg_type_text[m_tx_req_msg->header.type], - m_tx_req_msg->pdus[i].index, - m_tx_req_msg->pdus[i].length); - } - } - int n = 0; - if ((n = sendto(sockfd, m_tx_req_msg.get(), len, MSG_CONFIRM, (struct sockaddr*)&client_addr, sizeof(client_addr))) < - 0) { - logger.error("sendto failed, ret=%d", n); - } - - return 0; -} - -uint32_t srsran_basic_vnf::calc_full_msg_len(const basic_vnf_api::tx_request_msg_t& msg) -{ - // start with mandatory part - uint32_t len = sizeof(basic_vnf_api::msg_header_t) + 3 * sizeof(uint32_t); - - // add all PDUs - for (uint32_t i = 0; i < msg.nof_pdus; ++i) { - len += 2 * sizeof(uint16_t) + sizeof(basic_vnf_api::pdu_type_t) + msg.pdus[i].length; - } - - return len; -} - -bool srsran_basic_vnf::stop() -{ - if (running) { - running = false; - wait_thread_finish(); - } - - return true; -} - -} // namespace srsran diff --git a/lib/src/common/phy_cfg_nr_default.cc b/lib/src/common/phy_cfg_nr_default.cc index 906764f9c..e3998eeec 100644 --- a/lib/src/common/phy_cfg_nr_default.cc +++ b/lib/src/common/phy_cfg_nr_default.cc @@ -172,7 +172,7 @@ void phy_cfg_nr_default_t::make_harq_auto(srsran_harq_ack_cfg_hl_t& harq, const srsran_tdd_config_nr_t& tdd_cfg) { // Generate as many entries as DL slots - harq.nof_dl_data_to_ul_ack = SRSRAN_MAX(tdd_cfg.pattern1.nof_dl_slots, SRSRAN_MAX_NOF_DL_DATA_TO_UL); + harq.nof_dl_data_to_ul_ack = SRSRAN_MIN(tdd_cfg.pattern1.nof_dl_slots, SRSRAN_MAX_NOF_DL_DATA_TO_UL); // Set PDSCH to ACK timing delay to 4 or more for (uint32_t n = 0; n < harq.nof_dl_data_to_ul_ack; n++) { @@ -206,6 +206,7 @@ void phy_cfg_nr_default_t::make_prach_default_lte(srsran_prach_cfg_t& prach) prach.config_idx = 0; prach.freq_offset = 2; prach.root_seq_idx = 0; + prach.is_nr = true; } phy_cfg_nr_default_t::phy_cfg_nr_default_t(const reference_cfg_t& reference_cfg) @@ -259,4 +260,4 @@ phy_cfg_nr_default_t::phy_cfg_nr_default_t(const reference_cfg_t& reference_cfg) } } -} // namespace srsran \ No newline at end of file +} // namespace srsran diff --git a/lib/src/phy/phch/pusch_nr.c b/lib/src/phy/phch/pusch_nr.c index 4d5d764fa..dd59731bc 100644 --- a/lib/src/phy/phch/pusch_nr.c +++ b/lib/src/phy/phch/pusch_nr.c @@ -848,6 +848,13 @@ static inline int pusch_nr_decode_codeword(srsran_pusch_nr_t* q, // Demultiplex UCI only if necessary if (q->uci_mux) { + // As it can be HARQ-ACK takes LLRs from ULSCH, demultiplex HARQ-ACK first + int8_t* g_ack = (int8_t*)q->g_ack; + for (uint32_t i = 0; i < q->G_ack; i++) { + g_ack[i] = llr[q->pos_ack[i]]; + llr[q->pos_ack[i]] = 0; + } + // Demultiplex UL-SCH, change sign int8_t* g_ulsch = (int8_t*)q->g_ulsch; for (uint32_t i = 0; i < q->G_ulsch; i++) { @@ -857,12 +864,6 @@ static inline int pusch_nr_decode_codeword(srsran_pusch_nr_t* q, g_ulsch[i] = 0; } - // Demultiplex HARQ-ACK - int8_t* g_ack = (int8_t*)q->g_ack; - for (uint32_t i = 0; i < q->G_ack; i++) { - g_ack[i] = llr[q->pos_ack[i]]; - } - // Demultiplex CSI part 1 int8_t* g_csi1 = (int8_t*)q->g_csi1; for (uint32_t i = 0; i < q->G_csi1; i++) { diff --git a/lib/src/phy/phch/ra_nr.c b/lib/src/phy/phch/ra_nr.c index 23cc3e222..6f620e6b7 100644 --- a/lib/src/phy/phch/ra_nr.c +++ b/lib/src/phy/phch/ra_nr.c @@ -1049,6 +1049,12 @@ int srsran_ra_ul_set_grant_uci_nr(const srsran_carrier_nr_t* carrier, for (uint32_t i = 0; i < SRSRAN_MAX_TB; i++) { pusch_cfg->grant.tb[i].nof_bits = pusch_cfg->grant.tb[i].nof_re * srsran_mod_bits_x_symbol(pusch_cfg->grant.tb[i].mod) - Gack - Gcsi1 - Gcsi2; + + if (pusch_cfg->grant.tb[i].nof_bits > 0) { + pusch_cfg->grant.tb[i].R_prime = (double)pusch_cfg->grant.tb[i].tbs / (double)pusch_cfg->grant.tb[i].nof_bits; + } else { + pusch_cfg->grant.tb[i].R_prime = NAN; + } } return SRSRAN_SUCCESS; diff --git a/lib/src/phy/phch/ra_ul_nr.c b/lib/src/phy/phch/ra_ul_nr.c index 670567254..28250b171 100644 --- a/lib/src/phy/phch/ra_ul_nr.c +++ b/lib/src/phy/phch/ra_ul_nr.c @@ -539,7 +539,7 @@ int srsran_ra_ul_nr_pucch_resource(const srsran_pucch_nr_hl_cfg_t* pucch_cfg, // - At least one positive SR // - up to 2 HARQ-ACK // - No CSI report - if (uci_cfg->pucch.sr_positive_present > 0 && uci_cfg->ack.count <= SRSRAN_PUCCH_NR_FORMAT1_MAX_NOF_BITS && + if (uci_cfg->sr_positive_present > 0 && uci_cfg->ack.count <= SRSRAN_PUCCH_NR_FORMAT1_MAX_NOF_BITS && uci_cfg->nof_csi == 0) { uint32_t sr_resource_id = uci_cfg->pucch.sr_resource_id; if (sr_resource_id >= SRSRAN_PUCCH_MAX_NOF_SR_RESOURCES) { diff --git a/lib/src/phy/phch/sch_nr.c b/lib/src/phy/phch/sch_nr.c index 364b0e07f..3c36e9921 100644 --- a/lib/src/phy/phch/sch_nr.c +++ b/lib/src/phy/phch/sch_nr.c @@ -655,19 +655,10 @@ static int sch_nr_decode(srsran_sch_nr_t* q, nof_iter_sum += n_iter_cb; // Check if CB is all zeros - uint32_t cb_len = cfg.Kp - cfg.L_cb; - bool all_zeros = true; - for (uint32_t i = 0; i < cb_len && all_zeros; i++) { - all_zeros = (q->temp_cb[i] == 0); - } + uint32_t cb_len = cfg.Kp - cfg.L_cb; - tb->softbuffer.rx->cb_crc[r] = (ret != 0) && (!all_zeros); - SCH_INFO_RX("CB %d/%d iter=%d CRC=%s all_zeros=%s", - r, - cfg.C, - n_iter_cb, - tb->softbuffer.rx->cb_crc[r] ? "OK" : "KO", - all_zeros ? "yes" : "no"); + tb->softbuffer.rx->cb_crc[r] = (ret != 0); + SCH_INFO_RX("CB %d/%d iter=%d CRC=%s", r, cfg.C, n_iter_cb, tb->softbuffer.rx->cb_crc[r] ? "OK" : "KO"); // CB Debug trace if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) { @@ -789,7 +780,7 @@ int srsran_sch_nr_tb_info(const srsran_sch_tb_t* tb, const srsran_sch_tb_res_nr_ tb->cw_idx, srsran_mod_string(tb->mod), tb->tbs / 8, - tb->R, + tb->R_prime, tb->rv); if (res != NULL) { diff --git a/lib/src/phy/phch/test/pusch_nr_test.c b/lib/src/phy/phch/test/pusch_nr_test.c index 5d582d223..db05cb12d 100644 --- a/lib/src/phy/phch/test/pusch_nr_test.c +++ b/lib/src/phy/phch/test/pusch_nr_test.c @@ -286,25 +286,47 @@ int main(int argc, char** argv) goto clean_exit; } - float mse = 0.0f; + // Check symbols Mean Square Error (MSE) uint32_t nof_re = srsran_ra_dl_nr_slot_nof_re(&pusch_cfg, &pusch_cfg.grant); - for (uint32_t i = 0; i < pusch_cfg.grant.nof_layers; i++) { - for (uint32_t j = 0; j < nof_re; j++) { - mse += cabsf(pusch_tx.d[i][j] - pusch_rx.d[i][j]); - } - } if (nof_re * pusch_cfg.grant.nof_layers > 0) { - mse = mse / (nof_re * pusch_cfg.grant.nof_layers); - } - if (mse > 0.001) { - ERROR("MSE error (%f) is too high", mse); + float mse = 0.0f; for (uint32_t i = 0; i < pusch_cfg.grant.nof_layers; i++) { - printf("d_tx[%d]=", i); - srsran_vec_fprint_c(stdout, pusch_tx.d[i], nof_re); - printf("d_rx[%d]=", i); - srsran_vec_fprint_c(stdout, pusch_rx.d[i], nof_re); + for (uint32_t j = 0; j < nof_re; j++) { + mse += cabsf(pusch_tx.d[i][j] - pusch_rx.d[i][j]); + } + } + mse = mse / (nof_re * pusch_cfg.grant.nof_layers); + if (mse > 0.001) { + ERROR("MSE error (%f) is too high", mse); + for (uint32_t i = 0; i < pusch_cfg.grant.nof_layers; i++) { + printf("d_tx[%d]=", i); + srsran_vec_fprint_c(stdout, pusch_tx.d[i], nof_re); + printf("d_rx[%d]=", i); + srsran_vec_fprint_c(stdout, pusch_rx.d[i], nof_re); + } + goto clean_exit; + } + } + + // Check Received SCH LLR match + if (pusch_rx.G_ulsch > 0) { + for (uint32_t i = 0; i < pusch_rx.G_ulsch; i++) { + uint8_t rx_bit = (((int8_t*)pusch_rx.g_ulsch)[i]) < 0 ? 1 : 0; + if (rx_bit == 0) { + pusch_rx.g_ulsch[i] = pusch_tx.g_ulsch[i]; + } else { + pusch_rx.g_ulsch[i] = rx_bit; + } + } + if (memcmp(pusch_tx.g_ulsch, pusch_rx.g_ulsch, pusch_tx.G_ulsch) != 0) { + printf("g_ulsch_tx="); + srsran_vec_fprint_byte(stdout, pusch_tx.g_ulsch, pusch_tx.G_ulsch); + printf("g_ulsch_rx="); + srsran_vec_fprint_byte(stdout, pusch_rx.g_ulsch, pusch_tx.G_ulsch); + // srsran_vec_fprint_bs(stdout, (int8_t*)pusch_rx.g_ulsch, pusch_rx.G_ulsch); + + goto clean_exit; } - goto clean_exit; } // Validate UL-SCH CRC check diff --git a/lib/src/phy/phch/uci_nr.c b/lib/src/phy/phch/uci_nr.c index 27eeacfc0..94d8d94d5 100644 --- a/lib/src/phy/phch/uci_nr.c +++ b/lib/src/phy/phch/uci_nr.c @@ -989,7 +989,16 @@ uint32_t srsran_uci_nr_total_bits(const srsran_uci_cfg_nr_t* uci_cfg) return 0; } - return uci_cfg->ack.count + uci_cfg->o_sr + srsran_csi_part1_nof_bits(uci_cfg->csi, uci_cfg->nof_csi); + uint32_t o_csi = srsran_csi_part1_nof_bits(uci_cfg->csi, uci_cfg->nof_csi); + + // According to 38.213 9.2.4 UE procedure for reporting SR + // The UE transmits a PUCCH in the PUCCH resource for the corresponding SR configuration only when the UE transmits a + // positive SR + if (uci_cfg->ack.count == 0 && o_csi == 0 && !uci_cfg->sr_positive_present) { + return 0; + } + + return uci_cfg->ack.count + uci_cfg->o_sr + o_csi; } uint32_t srsran_uci_nr_info(const srsran_uci_data_nr_t* uci_data, char* str, uint32_t str_len) diff --git a/lib/src/phy/rf/rf_imp.c b/lib/src/phy/rf/rf_imp.c index e9eaa110f..1742434a1 100644 --- a/lib/src/phy/rf/rf_imp.c +++ b/lib/src/phy/rf/rf_imp.c @@ -183,13 +183,13 @@ int srsran_rf_open_multi(srsran_rf_t* h, char* args, uint32_t nof_channels) int srsran_rf_close(srsran_rf_t* rf) { // Stop gain thread + pthread_mutex_lock(&rf->mutex); if (rf->thread_gain_run) { - pthread_mutex_lock(&rf->mutex); rf->thread_gain_run = false; - pthread_mutex_unlock(&rf->mutex); - pthread_cond_signal(&rf->cond); - pthread_join(rf->thread_gain, NULL); } + pthread_mutex_unlock(&rf->mutex); + pthread_cond_signal(&rf->cond); + pthread_join(rf->thread_gain, NULL); return ((rf_dev_t*)rf->dev)->srsran_rf_close(rf->handler); } diff --git a/lib/src/phy/rf/rf_uhd_imp.cc b/lib/src/phy/rf/rf_uhd_imp.cc index c4f769566..0abd6192f 100644 --- a/lib/src/phy/rf/rf_uhd_imp.cc +++ b/lib/src/phy/rf/rf_uhd_imp.cc @@ -29,6 +29,7 @@ #include "rf_helper.h" #include "srsran/phy/utils/debug.h" +#include "srsran/phy/utils/vector.h" #include "rf_uhd_generic.h" #include "rf_uhd_imp.h" @@ -197,10 +198,12 @@ void suppress_handler(const char* x) // do nothing } -static cf_t zero_mem[64 * 1024] = {}; +static std::array zero_mem = {}; // For transmitting zeros +static std::array dummy_mem = {}; // For receiving static void log_overflow(rf_uhd_handler_t* h) { + std::unique_lock lock(h->tx_mutex); if (h->tx_state == RF_UHD_IMP_TX_STATE_BURST) { h->tx_state = RF_UHD_IMP_TX_STATE_END_OF_BURST; } @@ -215,6 +218,7 @@ static void log_overflow(rf_uhd_handler_t* h) static void log_late(rf_uhd_handler_t* h, bool is_rx) { + std::unique_lock lock(h->tx_mutex); if (h->tx_state == RF_UHD_IMP_TX_STATE_BURST) { h->tx_state = RF_UHD_IMP_TX_STATE_END_OF_BURST; } @@ -563,13 +567,14 @@ void rf_uhd_flush_buffer(void* h) // Set all pointers to zero buffer for (auto& i : data) { - i = zero_mem; + i = dummy_mem.data(); } // Receive until time out uhd::rx_metadata_t md; do { - if (handler->uhd->receive(data, handler->rx_nof_samples, md, 0.0, false, rxd_samples) != UHD_ERROR_NONE) { + uint32_t nsamples = SRSRAN_MIN(handler->rx_nof_samples, (uint32_t)dummy_mem.size()); + if (handler->uhd->receive(data, nsamples, md, 0.0, false, rxd_samples) != UHD_ERROR_NONE) { log_rx_error(handler); return; } @@ -958,7 +963,7 @@ static inline int rf_uhd_imp_end_burst(rf_uhd_handler_t* handler) // Set buffer pointers for (int i = 0; i < SRSRAN_MAX_CHANNELS; i++) { - buffs_ptr[i] = zero_mem; + buffs_ptr[i] = zero_mem.data(); } // Set metadata @@ -1270,13 +1275,19 @@ int rf_uhd_recv_with_time_multi(void* h, // Receive stream in multiple blocks while (rxd_samples_total < nsamples and trials < RF_UHD_IMP_MAX_RX_TRIALS) { void* buffs_ptr[SRSRAN_MAX_CHANNELS] = {}; - for (uint32_t i = 0; i < handler->nof_rx_channels; i++) { - cf_t* data_c = (cf_t*)data[i]; - buffs_ptr[i] = &data_c[rxd_samples_total]; - } size_t num_samps_left = nsamples - rxd_samples_total; - size_t num_rx_samples = (num_samps_left > handler->rx_nof_samples) ? handler->rx_nof_samples : num_samps_left; + size_t num_rx_samples = SRSRAN_MIN(handler->rx_nof_samples, num_samps_left); + + for (uint32_t i = 0; i < handler->nof_rx_channels; i++) { + if (data[i] != nullptr) { + cf_t* data_c = (cf_t*)data[i]; + buffs_ptr[i] = &data_c[rxd_samples_total]; + } else { + buffs_ptr[i] = dummy_mem.data(); + num_rx_samples = SRSRAN_MIN(num_rx_samples, (uint32_t)dummy_mem.size()); + } + } if (handler->uhd->receive(buffs_ptr, num_rx_samples, md, 1.0, false, rxd_samples) != UHD_ERROR_NONE) { log_rx_error(handler); @@ -1388,9 +1399,9 @@ int rf_uhd_send_timed_multi(void* h, cf_t* data_c[SRSRAN_MAX_CHANNELS] = {}; for (uint32_t i = 0; i < SRSRAN_MAX_CHANNELS; i++) { if (i < handler->nof_tx_channels) { - data_c[i] = (data[i] != nullptr) ? (cf_t*)(data[i]) : zero_mem; + data_c[i] = (data[i] != nullptr) ? (cf_t*)(data[i]) : zero_mem.data(); } else { - data_c[i] = zero_mem; + data_c[i] = zero_mem.data(); } } diff --git a/lib/src/radio/radio.cc b/lib/src/radio/radio.cc index 852b79538..ee2aad6e5 100644 --- a/lib/src/radio/radio.cc +++ b/lib/src/radio/radio.cc @@ -988,6 +988,7 @@ srsran_rf_info_t* radio::get_info() bool radio::get_metrics(rf_metrics_t* metrics) { + std::lock_guard lock(metrics_mutex); *metrics = rf_metrics; rf_metrics = {}; return true; @@ -999,8 +1000,11 @@ void radio::handle_rf_msg(srsran_rf_error_t error) return; } if (error.type == srsran_rf_error_t::SRSRAN_RF_ERROR_OVERFLOW) { - rf_metrics.rf_o++; - rf_metrics.rf_error = true; + { + std::lock_guard lock(metrics_mutex); + rf_metrics.rf_o++; + rf_metrics.rf_error = true; + } logger.info("Overflow"); // inform PHY about overflow @@ -1008,13 +1012,15 @@ void radio::handle_rf_msg(srsran_rf_error_t error) phy->radio_overflow(); } } else if (error.type == srsran_rf_error_t::SRSRAN_RF_ERROR_UNDERFLOW) { + logger.info("Underflow"); + std::lock_guard lock(metrics_mutex); rf_metrics.rf_u++; rf_metrics.rf_error = true; - logger.info("Underflow"); } else if (error.type == srsran_rf_error_t::SRSRAN_RF_ERROR_LATE) { + logger.info("Late (detected in %s)", error.opt ? "rx call" : "asynchronous thread"); + std::lock_guard lock(metrics_mutex); rf_metrics.rf_l++; rf_metrics.rf_error = true; - logger.info("Late (detected in %s)", error.opt ? "rx call" : "asynchronous thread"); } else if (error.type == srsran_rf_error_t::SRSRAN_RF_ERROR_RX) { logger.error("Fatal radio error occured."); phy->radio_failure(); diff --git a/lib/src/srslog/formatters/text_formatter.cpp b/lib/src/srslog/formatters/text_formatter.cpp index e6ad59889..6d0f6165a 100644 --- a/lib/src/srslog/formatters/text_formatter.cpp +++ b/lib/src/srslog/formatters/text_formatter.cpp @@ -52,7 +52,7 @@ static void format_metadata(const detail::log_entry_metadata& metadata, fmt::mem std::tm current_time = fmt::gmtime(std::chrono::high_resolution_clock::to_time_t(metadata.tp)); auto us_fraction = std::chrono::duration_cast(metadata.tp.time_since_epoch()).count() % 1000000u; - fmt::format_to(buffer, "{:%H:%M:%S}.{:06} ", current_time, us_fraction); + fmt::format_to(buffer, "{:%F}T{:%H:%M:%S}.{:06} ", current_time, current_time, us_fraction); // Format optional fields if present. if (!metadata.log_name.empty()) { diff --git a/lib/src/srslog/sinks/syslog_sink.h b/lib/src/srslog/sinks/syslog_sink.h new file mode 100644 index 000000000..2c0b8b02a --- /dev/null +++ b/lib/src/srslog/sinks/syslog_sink.h @@ -0,0 +1,97 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#ifndef SRSLOG_SYSLOG_SINK_H +#define SRSLOG_SYSLOG_SINK_H + +#include "srsran/srslog/shared_types.h" +#include "srsran/srslog/sink.h" +#include + +namespace srslog { + +/// This sink implementation writes to syslog. +class syslog_sink : public sink +{ +public: + syslog_sink(std::unique_ptr f, + std::string preamble_ = "", + syslog_local_type log_local_ = syslog_local_type::local0) : + sink(std::move(f)) + { + create_syslog(preamble_, syslog_translate(log_local_)); + } + + syslog_sink(const syslog_sink& other) = delete; + syslog_sink& operator=(const syslog_sink& other) = delete; + + detail::error_string write(detail::memory_buffer buffer) override + { + std::string entry(buffer.data(), buffer.size()); + if (entry.find("[E]") != std::string::npos) { + syslog(LOG_ERR, "%s", buffer.data()); + } else if (entry.find("[W]") != std::string::npos) { + syslog(LOG_WARNING, "%s", buffer.data()); + } else if (entry.find("[I]") != std::string::npos) { + syslog(LOG_INFO, "%s", buffer.data()); + } else if (entry.find("[D]") != std::string::npos) { + syslog(LOG_DEBUG, "%s", buffer.data()); + } else { + syslog(LOG_ERR, "%s", buffer.data()); + } + // openlog syslog does not return any value. + return {}; + } + + detail::error_string flush() override { return {}; } + +private: + /// Creates a new syslog + detail::error_string create_syslog(std::string preamble, int log_local) + { + if (preamble == "") { + openlog(NULL, LOG_CONS | LOG_PID | LOG_NDELAY, log_local); + } else { + openlog(preamble.c_str(), LOG_CONS | LOG_PID | LOG_NDELAY, log_local); + } + return {}; + } + + static int syslog_translate(syslog_local_type log_local) + { + switch (log_local) { + case syslog_local_type::local0: + return LOG_LOCAL0; + case syslog_local_type::local1: + return LOG_LOCAL1; + case syslog_local_type::local2: + return LOG_LOCAL2; + case syslog_local_type::local3: + return LOG_LOCAL3; + case syslog_local_type::local4: + return LOG_LOCAL4; + case syslog_local_type::local5: + return LOG_LOCAL5; + case syslog_local_type::local6: + return LOG_LOCAL6; + case syslog_local_type::local7: + return LOG_LOCAL7; + default: + return LOG_LOCAL0; + break; + } + }; +}; + +} // namespace srslog + +#endif // SRSLOG_SYSLOG_SINK_H diff --git a/lib/src/srslog/srslog.cpp b/lib/src/srslog/srslog.cpp index fcf443a8b..0d15bdacc 100644 --- a/lib/src/srslog/srslog.cpp +++ b/lib/src/srslog/srslog.cpp @@ -22,6 +22,7 @@ #include "srsran/srslog/srslog.h" #include "formatters/json_formatter.h" #include "sinks/file_sink.h" +#include "sinks/syslog_sink.h" #include "srslog_instance.h" using namespace srslog; @@ -174,6 +175,25 @@ sink& srslog::fetch_file_sink(const std::string& path, size_t max_size, std::uni return *s; } +sink& srslog::fetch_syslog_sink(const std::string& preamble_, + syslog_local_type log_local_, + std::unique_ptr f) +{ + std::string sink_id = preamble_ + std::to_string(static_cast(log_local_)); + if (auto* s = find_sink(sink_id)) { + return *s; + } + + //: TODO: GCC5 or lower versions emits an error if we use the new() expression + // directly, use redundant piecewise_construct instead. + auto& s = srslog_instance::get().get_sink_repo().emplace( + std::piecewise_construct, + std::forward_as_tuple(sink_id), + std::forward_as_tuple(new syslog_sink(std::move(f), preamble_, log_local_))); + + return *s; +} + bool srslog::install_custom_sink(const std::string& id, std::unique_ptr s) { assert(!id.empty() && "Empty path string"); diff --git a/lib/test/common/CMakeLists.txt b/lib/test/common/CMakeLists.txt index c03f458f8..db44853ca 100644 --- a/lib/test/common/CMakeLists.txt +++ b/lib/test/common/CMakeLists.txt @@ -85,11 +85,5 @@ add_executable(task_scheduler_test task_scheduler_test.cc) target_link_libraries(task_scheduler_test srsran_common ${ATOMIC_LIBS}) add_test(task_scheduler_test task_scheduler_test) -add_executable(pnf_dummy pnf_dummy.cc) -target_link_libraries(pnf_dummy srsran_common ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES}) - -add_executable(pnf_bridge pnf_bridge.cc) -target_link_libraries(pnf_bridge srsran_common ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES}) - add_executable(mac_pcap_net_test mac_pcap_net_test.cc) target_link_libraries(mac_pcap_net_test srsran_common ${SCTP_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) diff --git a/lib/test/common/pnf_bridge.cc b/lib/test/common/pnf_bridge.cc deleted file mode 100644 index 3f93142b6..000000000 --- a/lib/test/common/pnf_bridge.cc +++ /dev/null @@ -1,127 +0,0 @@ -/** - * Copyright 2013-2021 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 -#include -#include -#include -#include -#include -#include - -#include "srsran/common/basic_pnf.h" - -using namespace std; -namespace bpo = boost::program_options; - -struct pnf_args_t { - std::string gnb_vnf_addr; - std::string ue_vnf_addr; - uint16_t gnb_vnf_port; - uint16_t ue_vnf_port; - uint32_t sf_interval; - int32_t num_sf; - uint32_t tb_len; -}; - -void parse_args(pnf_args_t* args, int argc, char* argv[]) -{ - // Command line only options - bpo::options_description general("General options"); - - general.add_options()("help,h", "Produce help message")("version,v", "Print version information and exit"); - - // Command line or config file options - bpo::options_description common("Configuration options"); - - // clang-format off - common.add_options() - ("vnf.gnb_addr", bpo::value(&args->gnb_vnf_addr)->default_value("127.0.0.1"), "VNF address") - ("vnf.ue_addr", bpo::value(&args->ue_vnf_addr)->default_value("127.0.0.1"), "VNF address") - ("vnf.gnb_port", bpo::value(&args->gnb_vnf_port)->default_value(3333), "gNB VNF port") - ("vnf.ue_port", bpo::value(&args->ue_vnf_port)->default_value(3334), "UE VNF port") - ("sf_interval", bpo::value(&args->sf_interval)->default_value(1000), "Interval between subframes in us") - ("num_sf", bpo::value(&args->num_sf)->default_value(-1), "Number of subframes to signal (-1 infinity)") - ("tb_len", bpo::value(&args->tb_len)->default_value(1600), "TB lenth (0 for random size)"); - // clang-format on - - // these options are allowed on the command line - bpo::options_description cmdline_options; - cmdline_options.add(common).add(general); - - // parse the command line and store result in vm - bpo::variables_map vm; - bpo::store(bpo::command_line_parser(argc, argv).options(cmdline_options).run(), vm); - bpo::notify(vm); - - // help option was given - print usage and exit - if (vm.count("help")) { - cout << "Usage: " << argv[0] << " [OPTIONS] config_file" << endl << endl; - cout << common << endl << general << endl; - exit(0); - } -} - -bool running = true; -void sig_int_handler(int signo) -{ - printf("SIGINT received. Exiting...\n"); - if (signo == SIGINT) { - running = false; - } -} - -int main(int argc, char** argv) -{ - signal(SIGINT, sig_int_handler); - - pnf_args_t args; - parse_args(&args, argc, argv); - - srsran::srsran_basic_pnf ue_pnf("ue", args.ue_vnf_addr, args.ue_vnf_port, args.sf_interval, args.num_sf, args.tb_len); - srsran::srsran_basic_pnf gnb_pnf( - "gnb", args.gnb_vnf_addr, args.gnb_vnf_port, args.sf_interval, args.num_sf, args.tb_len); - - gnb_pnf.connect_out_rf_queue(ue_pnf.get_in_rf_queue()); - - ue_pnf.start(); - gnb_pnf.start(); - - while (running) { - for (uint32_t i = 0; i < 2; ++i) { - srsran::pnf_metrics_t metrics = (i == 0) ? ue_pnf.get_metrics() : gnb_pnf.get_metrics(); - printf("%s: RTT=%d, #Error=%d, #PDUs=%d, Total TB size=%d, Rate=%.2f Mbit/s\n", - i == 0 ? "UE" : "gNB", - metrics.avg_rtt_us, - metrics.num_timing_errors, - metrics.num_pdus, - metrics.tb_size, - metrics.tb_size * 8 / 1.0e6); - } - - std::this_thread::sleep_for(std::chrono::seconds(1)); - } - - ue_pnf.stop(); - gnb_pnf.stop(); - - return 0; -} \ No newline at end of file diff --git a/lib/test/common/pnf_dummy.cc b/lib/test/common/pnf_dummy.cc deleted file mode 100644 index 6bc2ede55..000000000 --- a/lib/test/common/pnf_dummy.cc +++ /dev/null @@ -1,117 +0,0 @@ -/** - * Copyright 2013-2021 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 -#include -#include -#include -#include -#include -#include - -#include "srsran/common/basic_pnf.h" - -using namespace std; -namespace bpo = boost::program_options; - -struct pnf_args_t { - std::string type; - std::string vnf_addr; - uint16_t vnf_port; - uint32_t sf_interval; - int32_t num_sf; - uint32_t tb_len; -}; - -void parse_args(pnf_args_t* args, int argc, char* argv[]) -{ - // Command line only options - bpo::options_description general("General options"); - - general.add_options()("help,h", "Produce help message")("version,v", "Print version information and exit"); - - // Command line or config file options - bpo::options_description common("Configuration options"); - - // clang-format off - common.add_options() - ("vnf.type", bpo::value(&args->type)->default_value("gnb"), "VNF instance type [gnb,ue]") - ("vnf.addr", bpo::value(&args->vnf_addr)->default_value("127.0.0.1"), "VNF address") - ("vnf.port", bpo::value(&args->vnf_port)->default_value(3333), "VNF port") - ("sf_interval", bpo::value(&args->sf_interval)->default_value(1000), "Interval between subframes in us") - ("num_sf", bpo::value(&args->num_sf)->default_value(-1), "Number of subframes to signal (-1 infinity)") - ("tb_len", bpo::value(&args->tb_len)->default_value(0), "TB lenth (0 for random size)"); - - // clang-format on - - // these options are allowed on the command line - bpo::options_description cmdline_options; - cmdline_options.add(common).add(general); - - // parse the command line and store result in vm - bpo::variables_map vm; - bpo::store(bpo::command_line_parser(argc, argv).options(cmdline_options).run(), vm); - bpo::notify(vm); - - // help option was given - print usage and exit - if (vm.count("help")) { - cout << "Usage: " << argv[0] << " [OPTIONS] config_file" << endl << endl; - cout << common << endl << general << endl; - exit(0); - } -} - -bool running = true; -void sig_int_handler(int signo) -{ - printf("SIGINT received. Exiting...\n"); - if (signo == SIGINT) { - running = false; - } -} - -int main(int argc, char** argv) -{ - signal(SIGINT, sig_int_handler); - - pnf_args_t args; - parse_args(&args, argc, argv); - - srsran::srsran_basic_pnf pnf(args.type, args.vnf_addr, args.vnf_port, args.sf_interval, args.num_sf, args.tb_len); - - pnf.start(); - - while (running) { - srsran::pnf_metrics_t metrics = pnf.get_metrics(); - printf("RTT=%d, #Error=%d, #PDUs=%d, Total TB size=%d, Rate=%.2f Mbit/s\n", - metrics.avg_rtt_us, - metrics.num_timing_errors, - metrics.num_pdus, - metrics.tb_size, - metrics.tb_size * 8 / 1e6); - - std::this_thread::sleep_for(std::chrono::seconds(1)); - } - - pnf.stop(); - - return 0; -} \ No newline at end of file diff --git a/lib/test/srslog/CMakeLists.txt b/lib/test/srslog/CMakeLists.txt index acf3df319..26bb4b4ea 100644 --- a/lib/test/srslog/CMakeLists.txt +++ b/lib/test/srslog/CMakeLists.txt @@ -47,6 +47,10 @@ target_include_directories(file_sink_test PUBLIC ../../) target_link_libraries(file_sink_test srslog) add_test(file_sink_test file_sink_test) +add_executable(syslog_sink_test syslog_sink_test.cpp) +target_include_directories(syslog_sink_test PUBLIC ../../) +target_link_libraries(syslog_sink_test srslog) + add_executable(file_utils_test file_utils_test.cpp) target_include_directories(file_utils_test PUBLIC ../../) target_link_libraries(file_utils_test srslog) diff --git a/lib/test/srslog/syslog_sink_test.cpp b/lib/test/srslog/syslog_sink_test.cpp new file mode 100644 index 000000000..51f958fb4 --- /dev/null +++ b/lib/test/srslog/syslog_sink_test.cpp @@ -0,0 +1,63 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#include "src/srslog/sinks/syslog_sink.h" +#include "srsran/srslog/srslog.h" +#include "test_dummies.h" +#include "testing_helpers.h" + +#include +#include + +using namespace srslog; + +/// Syslog sink name. +static constexpr char sink_name[] = "srslog_syslog_sink"; + +static bool find_string_infile(std::string filename, std::string pattern) +{ + std::ifstream file(filename); + std::string line; + bool found = false; + + if (file.is_open()) { + while (std::getline(file, line)) { + if (line.find(pattern) != std::string::npos) { // WILL SEARCH 2015-1113 in file + found = true; + } + } + } else { + printf("WARNING: Could not open file %s", filename.c_str()); + } + return found; +} + +static bool syslog_basic_test() +{ + syslog_sink syslog_sink(get_default_log_formatter()); + + // Build a 1000 byte entry. + std::string entry(1000, 'a'); + + syslog_sink.write(detail::memory_buffer(entry)); + + syslog_sink.flush(); + + ASSERT_EQ(find_string_infile("/var/log/syslog", entry), true); + return true; +} + +int main() +{ + TEST_FUNCTION(syslog_basic_test); + return 0; +} \ No newline at end of file diff --git a/lib/test/srslog/text_formatter_test.cpp b/lib/test/srslog/text_formatter_test.cpp index d8d21aaa6..f68db4ef1 100644 --- a/lib/test/srslog/text_formatter_test.cpp +++ b/lib/test/srslog/text_formatter_test.cpp @@ -46,7 +46,7 @@ static bool when_fully_filled_log_entry_then_everything_is_formatted() fmt::dynamic_format_arg_store store; text_formatter{}.format(build_log_entry_metadata(&store), buffer); std::string result = fmt::to_string(buffer); - std::string expected = "00:00:00.050000 [ABC ] [Z] [ 10] Text 88\n"; + std::string expected = "1970-01-01T00:00:00.050000 [ABC ] [Z] [ 10] Text 88\n"; ASSERT_EQ(result, expected); @@ -62,7 +62,7 @@ static bool when_log_entry_without_name_is_passed_then_name_is_not_formatted() fmt::memory_buffer buffer; text_formatter{}.format(std::move(entry), buffer); std::string result = fmt::to_string(buffer); - std::string expected = "00:00:00.050000 [Z] [ 10] Text 88\n"; + std::string expected = "1970-01-01T00:00:00.050000 [Z] [ 10] Text 88\n"; ASSERT_EQ(result, expected); @@ -78,7 +78,7 @@ static bool when_log_entry_without_tag_is_passed_then_tag_is_not_formatted() fmt::memory_buffer buffer; text_formatter{}.format(std::move(entry), buffer); std::string result = fmt::to_string(buffer); - std::string expected = "00:00:00.050000 [ABC ] [ 10] Text 88\n"; + std::string expected = "1970-01-01T00:00:00.050000 [ABC ] [ 10] Text 88\n"; ASSERT_EQ(result, expected); @@ -94,7 +94,7 @@ static bool when_log_entry_without_context_is_passed_then_context_is_not_formatt fmt::memory_buffer buffer; text_formatter{}.format(std::move(entry), buffer); std::string result = fmt::to_string(buffer); - std::string expected = "00:00:00.050000 [ABC ] [Z] Text 88\n"; + std::string expected = "1970-01-01T00:00:00.050000 [ABC ] [Z] Text 88\n"; ASSERT_EQ(result, expected); @@ -111,7 +111,7 @@ static bool when_log_entry_with_hex_dump_is_passed_then_hex_dump_is_formatted() fmt::memory_buffer buffer; text_formatter{}.format(std::move(entry), buffer); std::string result = fmt::to_string(buffer); - std::string expected = "00:00:00.050000 [ABC ] [Z] [ 10] Text 88\n" + std::string expected = "1970-01-01T00:00:00.050000 [ABC ] [Z] [ 10] Text 88\n" " 0000: 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n" " 0010: 10 11 12 13\n"; @@ -185,7 +185,7 @@ static bool when_log_entry_with_only_context_is_passed_then_context_is_formatted fmt::memory_buffer buffer; text_formatter{}.format_ctx(ctx, std::move(entry), buffer); std::string result = fmt::to_string(buffer); - std::string expected = "00:00:00.050000 [ABC ] [Z] [ 10] Context dump for " + std::string expected = "1970-01-01T00:00:00.050000 [ABC ] [Z] [ 10] Context dump for " "\"Complex Context\"\n" " > List: sector_list\n" " > Set: sector_metrics\n" @@ -227,7 +227,7 @@ static bool when_log_entry_with_context_and_message_is_passed_then_context_is_fo fmt::memory_buffer buffer; text_formatter{}.format_ctx(ctx, std::move(entry), buffer); std::string result = fmt::to_string(buffer); - std::string expected = "00:00:00.050000 [ABC ] [Z] [ 10] [[sector_metrics_type: event, " + std::string expected = "1970-01-01T00:00:00.050000 [ABC ] [Z] [ 10] [[sector_metrics_type: event, " "sector_metrics_sector_id: 1, [ue_container_Throughput: 1.2 MB/s, " "ue_container_Address: 10.20.30.40, [RF_SNR: 5.1 dB, RF_PWR: -11 " "dBm][RF_SNR: 10.1 dB, RF_PWR: -20 dBm]][ue_container_Throughput: 10.2 " diff --git a/srsenb/CMakeLists.txt b/srsenb/CMakeLists.txt index 979dc7295..059269762 100644 --- a/srsenb/CMakeLists.txt +++ b/srsenb/CMakeLists.txt @@ -53,6 +53,6 @@ add_subdirectory(test) # Default configuration files ######################################################################## install(FILES enb.conf.example DESTINATION ${DATA_DIR}) -install(FILES drb.conf.example DESTINATION ${DATA_DIR}) -install(FILES rr.conf.example DESTINATION ${DATA_DIR}) -install(FILES sib.conf.example DESTINATION ${DATA_DIR}) \ No newline at end of file +install(FILES rb.conf.example DESTINATION ${DATA_DIR}) +install(FILES rr.conf.example DESTINATION ${DATA_DIR}) +install(FILES sib.conf.example DESTINATION ${DATA_DIR}) diff --git a/srsenb/enb.conf.example b/srsenb/enb.conf.example index f7760089d..d3c36dc18 100644 --- a/srsenb/enb.conf.example +++ b/srsenb/enb.conf.example @@ -36,12 +36,12 @@ n_prb = 50 # sib_config: SIB1, SIB2 and SIB3 configuration file # note: when enabling mbms, use the sib.conf.mbsfn configuration file which includes SIB13 # rr_config: Radio Resources configuration file -# drb_config: DRB configuration file +# rb_config: SRB/DRB configuration file ##################################################################### [enb_files] sib_config = sib.conf rr_config = rr.conf -drb_config = drb.conf +rb_config = rb.conf ##################################################################### # RF configuration @@ -180,6 +180,7 @@ enable = false # init_ul_snr_value: Initial UL SNR value used for computing MCS in the first UL grant # init_dl_cqi: DL CQI value used before any CQI report is available to the eNB # max_sib_coderate: Upper bound on SIB and RAR grants coderate +# pdcch_cqi_offset: CQI offset in derivation of PDCCH aggregation level # ##################################################################### [scheduler] @@ -204,6 +205,7 @@ enable = false #init_ul_snr_value=5 #init_dl_cqi=5 #max_sib_coderate=0.3 +#pdcch_cqi_offset=0 ##################################################################### # eMBMS configuration options @@ -324,6 +326,7 @@ enable = false # tracing_enable: Write source code tracing information to a file. # tracing_filename: File path to use for tracing information. # tracing_buffcapacity: Maximum capacity in bytes the tracing framework can store. +# stdout_ts_enable: Prints once per second the timestamp into stdout. # pregenerate_signals: Pregenerate uplink signals after attach. Improves CPU performance. # tx_amplitude: Transmit amplitude factor (set 0-1 to reduce PAPR) # rrc_inactivity_timer Inactivity timeout used to remove UE context from RRC (in milliseconds). @@ -335,6 +338,8 @@ enable = false # gtpu_tunnel_timeout: Time that GTPU takes to release indirect forwarding tunnel since the last received GTPU PDU (0 for no timer). #ts1_reloc_prep_timeout: S1AP TS 36.413 TS1RelocPrep Expiry Timeout value in milliseconds #ts1_reloc_overall_timeout: S1AP TS 36.413 TS1RelocOverall Expiry Timeout value in milliseconds +# rlf_release_timer_ms: Time taken by eNB to release UE context after it detects an RLF +# rlf_min_ul_snr_estim: SNR threshold in dB below which the enb is notified with rlf ko # ##################################################################### [expert] @@ -351,6 +356,7 @@ enable = false #tracing_enable = true #tracing_filename = /tmp/enb_tracing.log #tracing_buffcapacity = 1000000 +#stdout_ts_enable = false #pregenerate_signals = false #tx_amplitude = 0.6 #rrc_inactivity_timer = 30000 @@ -365,3 +371,5 @@ enable = false #extended_cp = false #ts1_reloc_prep_timeout = 10000 #ts1_reloc_overall_timeout = 10000 +#rlf_release_timer_ms = 4000 +#rlf_min_ul_snr_estim = -2 diff --git a/srsenb/hdr/enb.h b/srsenb/hdr/enb.h index eb64a7f2a..74df35734 100644 --- a/srsenb/hdr/enb.h +++ b/srsenb/hdr/enb.h @@ -71,7 +71,7 @@ struct enb_args_t { struct enb_files_t { std::string sib_config; std::string rr_config; - std::string drb_config; + std::string rb_config; }; struct log_args_t { diff --git a/srsenb/hdr/phy/nr/slot_worker.h b/srsenb/hdr/phy/nr/slot_worker.h index dd84a34c7..f416fd76c 100644 --- a/srsenb/hdr/phy/nr/slot_worker.h +++ b/srsenb/hdr/phy/nr/slot_worker.h @@ -41,12 +41,11 @@ class slot_worker final : public srsran::thread_pool::worker { public: struct args_t { - uint32_t cell_index = 0; - srsran_carrier_nr_t carrier = {}; - uint32_t nof_tx_ports = 1; - uint32_t nof_rx_ports = 1; - uint32_t pusch_max_nof_iter = 10; - srsran_pdcch_cfg_nr_t pdcch_cfg = {}; ///< PDCCH configuration + uint32_t cell_index = 0; + uint32_t nof_max_prb = SRSRAN_MAX_PRB_NR; + uint32_t nof_tx_ports = 1; + uint32_t nof_rx_ports = 1; + uint32_t pusch_max_nof_iter = 10; }; slot_worker(srsran::phy_common_interface& common_, stack_interface_phy_nr& stack_, srslog::basic_logger& logger); @@ -54,6 +53,8 @@ public: bool init(const args_t& args); + bool set_common_cfg(const srsran_carrier_nr_t& carrier, const srsran_pdcch_cfg_nr_t& pdcch_cfg_); + /* Functions used by main PHY thread */ cf_t* get_buffer_rx(uint32_t antenna_idx); cf_t* get_buffer_tx(uint32_t antenna_idx); diff --git a/srsenb/hdr/phy/nr/worker_pool.h b/srsenb/hdr/phy/nr/worker_pool.h index cf3fe4264..f40776efc 100644 --- a/srsenb/hdr/phy/nr/worker_pool.h +++ b/srsenb/hdr/phy/nr/worker_pool.h @@ -24,7 +24,9 @@ #include "slot_worker.h" #include "srsenb/hdr/phy/phy_interfaces.h" +#include "srsenb/hdr/phy/prach_worker.h" #include "srsran/common/thread_pool.h" +#include "srsran/interfaces/enb_mac_interfaces.h" #include "srsran/interfaces/gnb_interfaces.h" namespace srsenb { @@ -32,20 +34,71 @@ namespace nr { class worker_pool { +private: + class prach_stack_adaptor_t : public stack_interface_phy_lte + { + private: + stack_interface_phy_nr& stack; + + public: + prach_stack_adaptor_t(stack_interface_phy_nr& stack_) : stack(stack_) + { + // Do nothing + } + + int sr_detected(uint32_t tti, uint16_t rnti) override { return 0; } + void rach_detected(uint32_t tti, uint32_t primary_cc_idx, uint32_t preamble_idx, uint32_t time_adv) override + { + stack_interface_phy_nr::rach_info_t rach_info = {}; + rach_info.preamble = preamble_idx; + rach_info.time_adv = time_adv; + + stack.rach_detected(rach_info); + } + int ri_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t ri_value) override { return 0; } + int pmi_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t pmi_value) override { return 0; } + int cqi_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t cqi_value) override { return 0; } + int snr_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, float snr_db, ul_channel_t ch) override { return 0; } + int ta_info(uint32_t tti, uint16_t rnti, float ta_us) override { return 0; } + int ack_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t tb_idx, bool ack) override { return 0; } + int crc_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t nof_bytes, bool crc_res) override { return 0; } + int push_pdu(uint32_t tti_rx, + uint16_t rnti, + uint32_t enb_cc_idx, + uint32_t nof_bytes, + bool crc_res, + uint32_t ul_nof_prbs) override + { + return 0; + } + int get_dl_sched(uint32_t tti, dl_sched_list_t& dl_sched_res) override { return 0; } + int get_mch_sched(uint32_t tti, bool is_mcch, dl_sched_list_t& dl_sched_res) override { return 0; } + int get_ul_sched(uint32_t tti, ul_sched_list_t& ul_sched_res) override { return 0; } + void set_sched_dl_tti_mask(uint8_t* tti_mask, uint32_t nof_sfs) override {} + void tti_clock() override {} + }; + srsran::phy_common_interface& common; stack_interface_phy_nr& stack; srslog::sink& log_sink; srsran::thread_pool pool; std::vector > workers; + prach_worker_pool prach; + uint32_t current_tti = 0; ///< Current TTI, read and write from same thread + srslog::basic_logger& logger; + prach_stack_adaptor_t prach_stack_adaptor; + + // Current configuration + std::mutex common_cfg_mutex; + srsran_carrier_nr_t carrier = {}; + srsran_pdcch_cfg_nr_t pdcch_cfg = {}; public: struct args_t { - uint32_t nof_phy_threads = 3; - uint32_t prio = 52; - std::string log_level = "info"; - uint32_t log_hex_limit = 64; - std::string log_id_preamble = ""; - uint32_t pusch_max_nof_iter = 10; + uint32_t nof_phy_threads = 3; + uint32_t prio = 52; + uint32_t pusch_max_nof_iter = 10; + srsran::phy_log_args_t log = {}; }; slot_worker* operator[](std::size_t pos) { return workers.at(pos).get(); } @@ -58,6 +111,7 @@ public: slot_worker* wait_worker_id(uint32_t id); void start_worker(slot_worker* w); void stop(); + int set_common_cfg(const phy_interface_rrc_nr::common_cfg_t& common_cfg); }; } // namespace nr diff --git a/srsenb/hdr/phy/vnf_phy_nr.h b/srsenb/hdr/phy/vnf_phy_nr.h deleted file mode 100644 index a4895fe36..000000000 --- a/srsenb/hdr/phy/vnf_phy_nr.h +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright 2013-2021 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/. - * - */ - -#ifndef SRSGNB_NR_VNF_PHY_H -#define SRSGNB_NR_VNF_PHY_H - -#include "srsenb/hdr/phy/enb_phy_base.h" -#include "srsenb/hdr/phy/phy_common.h" -#include "srsran/common/basic_vnf.h" -#include "srsran/interfaces/enb_metrics_interface.h" -#include "srsran/interfaces/gnb_interfaces.h" -#include "srsran/interfaces/radio_interfaces.h" - -namespace srsenb { - -struct nr_phy_cfg_t { - // TODO: add cell and RRC configs -}; - -class vnf_phy_nr : public srsenb::enb_phy_base, public srsenb::phy_interface_stack_nr -{ -public: - vnf_phy_nr() = default; - ~vnf_phy_nr(); - - int init(const srsenb::phy_args_t& args, const nr_phy_cfg_t& cfg, srsenb::stack_interface_phy_nr* stack_); - void stop() override; - - std::string get_type() override { return "vnf"; }; - - void start_plot() override; - - void get_metrics(std::vector& metrics) override; - - // MAC interface - int dl_config_request(const dl_config_request_t& request) override; - int tx_request(const tx_request_t& request) override; - - void cmd_cell_gain(uint32_t cell_idx, float gain_db) override - { - // Do nothing - } - -private: - std::unique_ptr vnf = nullptr; - - bool initialized = false; - - void parse_config(const nr_phy_cfg_t& cfg); -}; - -} // namespace srsenb - -#endif // SRSGNB_NR_VNF_PHY_H diff --git a/srsenb/hdr/stack/gnb_stack_nr.h b/srsenb/hdr/stack/gnb_stack_nr.h index 438a82aaa..8fd97d47c 100644 --- a/srsenb/hdr/stack/gnb_stack_nr.h +++ b/srsenb/hdr/stack/gnb_stack_nr.h @@ -86,6 +86,7 @@ public: int get_ul_sched(const srsran_slot_cfg_t& slot_cfg, ul_sched_t& ul_sched) override; int pucch_info(const srsran_slot_cfg_t& slot_cfg, const pucch_info_t& pucch_info) override; int pusch_info(const srsran_slot_cfg_t& slot_cfg, const pusch_info_t& pusch_info) override; + void rach_detected(const rach_info_t& rach_info) override; private: void run_thread() final; diff --git a/srsenb/hdr/stack/mac/mac_nr.h b/srsenb/hdr/stack/mac/mac_nr.h index f6f5c57a9..14fcc7fb5 100644 --- a/srsenb/hdr/stack/mac/mac_nr.h +++ b/srsenb/hdr/stack/mac/mac_nr.h @@ -80,12 +80,9 @@ public: int get_ul_sched(const srsran_slot_cfg_t& slot_cfg, ul_sched_t& ul_sched) override; int pucch_info(const srsran_slot_cfg_t& slot_cfg, const pucch_info_t& pucch_info) override; int pusch_info(const srsran_slot_cfg_t& slot_cfg, const pusch_info_t& pusch_info) override; + void rach_detected(const rach_info_t& rach_info) override; private: - void get_dl_config(const uint32_t tti, - phy_interface_stack_nr::dl_config_request_t& config_request, - phy_interface_stack_nr::tx_request_t& tx_request); - // PDU processing int handle_pdu(srsran::unique_byte_buffer_t pdu); diff --git a/srsenb/hdr/stack/mac/nr/harq_softbuffer.h b/srsenb/hdr/stack/mac/nr/harq_softbuffer.h new file mode 100644 index 000000000..ec33718b6 --- /dev/null +++ b/srsenb/hdr/stack/mac/nr/harq_softbuffer.h @@ -0,0 +1,132 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#ifndef SRSRAN_HARQ_SOFTBUFFER_H +#define SRSRAN_HARQ_SOFTBUFFER_H + +#include "srsran/adt/pool/pool_interface.h" +#include "srsran/adt/span.h" +extern "C" { +#include "srsran/phy/common/phy_common_nr.h" +#include "srsran/phy/fec/softbuffer.h" +} + +namespace srsenb { + +class tx_harq_softbuffer +{ +public: + tx_harq_softbuffer() { bzero(&buffer, sizeof(buffer)); } + explicit tx_harq_softbuffer(uint32_t nof_prb_) { srsran_softbuffer_tx_init(&buffer, nof_prb_); } + tx_harq_softbuffer(const tx_harq_softbuffer&) = delete; + tx_harq_softbuffer(tx_harq_softbuffer&& other) noexcept + { + memcpy(&buffer, &other.buffer, sizeof(other.buffer)); + bzero(&other.buffer, sizeof(other.buffer)); + } + tx_harq_softbuffer& operator=(const tx_harq_softbuffer&) = delete; + tx_harq_softbuffer& operator =(tx_harq_softbuffer&& other) noexcept + { + if (this != &other) { + destroy(); + memcpy(&buffer, &other.buffer, sizeof(other.buffer)); + bzero(&other.buffer, sizeof(other.buffer)); + } + return *this; + } + ~tx_harq_softbuffer() { destroy(); } + + void reset() { srsran_softbuffer_tx_reset(&buffer); } + + srsran_softbuffer_tx_t& operator*() { return buffer; } + const srsran_softbuffer_tx_t& operator*() const { return buffer; } + srsran_softbuffer_tx_t* operator->() { return &buffer; } + const srsran_softbuffer_tx_t* operator->() const { return &buffer; } + srsran_softbuffer_tx_t* get() { return &buffer; } + const srsran_softbuffer_tx_t* get() const { return &buffer; } + +private: + void destroy() { srsran_softbuffer_tx_free(&buffer); } + + srsran_softbuffer_tx_t buffer; +}; + +class rx_harq_softbuffer +{ +public: + rx_harq_softbuffer() { bzero(&buffer, sizeof(buffer)); } + explicit rx_harq_softbuffer(uint32_t nof_prb_) { srsran_softbuffer_rx_init(&buffer, nof_prb_); } + rx_harq_softbuffer(const rx_harq_softbuffer&) = delete; + rx_harq_softbuffer(rx_harq_softbuffer&& other) noexcept + { + memcpy(&buffer, &other.buffer, sizeof(other.buffer)); + bzero(&other.buffer, sizeof(other.buffer)); + } + rx_harq_softbuffer& operator=(const rx_harq_softbuffer&) = delete; + rx_harq_softbuffer& operator =(rx_harq_softbuffer&& other) noexcept + { + if (this != &other) { + destroy(); + memcpy(&buffer, &other.buffer, sizeof(other.buffer)); + bzero(&other.buffer, sizeof(other.buffer)); + } + return *this; + } + ~rx_harq_softbuffer() { destroy(); } + + void reset() { srsran_softbuffer_rx_reset(&buffer); } + void reset(uint32_t tbs_bits) { srsran_softbuffer_rx_reset_tbs(&buffer, tbs_bits); } + + srsran_softbuffer_rx_t& operator*() { return buffer; } + const srsran_softbuffer_rx_t& operator*() const { return buffer; } + srsran_softbuffer_rx_t* operator->() { return &buffer; } + const srsran_softbuffer_rx_t* operator->() const { return &buffer; } + srsran_softbuffer_rx_t* get() { return &buffer; } + const srsran_softbuffer_rx_t* get() const { return &buffer; } + +private: + void destroy() { srsran_softbuffer_rx_free(&buffer); } + + srsran_softbuffer_rx_t buffer; +}; + +class harq_softbuffer_pool +{ +public: + harq_softbuffer_pool(const harq_softbuffer_pool&) = delete; + harq_softbuffer_pool(harq_softbuffer_pool&&) = delete; + harq_softbuffer_pool& operator=(const harq_softbuffer_pool&) = delete; + harq_softbuffer_pool& operator=(harq_softbuffer_pool&&) = delete; + + void init_pool(uint32_t nof_prb, uint32_t batch_size = MAX_HARQ * 4, uint32_t thres = 0, uint32_t init_size = 0); + + srsran::unique_pool_ptr get_tx(uint32_t nof_prb); + srsran::unique_pool_ptr get_rx(uint32_t nof_prb); + + static harq_softbuffer_pool& get_instance() + { + static harq_softbuffer_pool pool; + return pool; + } + +private: + const static uint32_t MAX_HARQ = 16; + + harq_softbuffer_pool() = default; + + std::array >, SRSRAN_MAX_PRB_NR> tx_pool; + std::array >, SRSRAN_MAX_PRB_NR> rx_pool; +}; + +} // namespace srsenb + +#endif // SRSRAN_HARQ_SOFTBUFFER_H diff --git a/srsenb/hdr/stack/mac/nr/sched_nr.h b/srsenb/hdr/stack/mac/nr/sched_nr.h index 0161bc93d..d4519e472 100644 --- a/srsenb/hdr/stack/mac/nr/sched_nr.h +++ b/srsenb/hdr/stack/mac/nr/sched_nr.h @@ -22,7 +22,7 @@ #ifndef SRSRAN_SCHED_NR_H #define SRSRAN_SCHED_NR_H -#include "sched_nr_common.h" +#include "sched_nr_cfg.h" #include "sched_nr_interface.h" #include "sched_nr_ue.h" #include "srsran/adt/pool/cached_alloc.h" @@ -36,9 +36,11 @@ namespace srsenb { namespace sched_nr_impl { class sched_worker_manager; -} +class serv_cell_ctxt; +} // namespace sched_nr_impl class ue_event_manager; +class sched_result_manager; class sched_nr final : public sched_nr_interface { @@ -48,17 +50,19 @@ public: int cell_cfg(srsran::const_span cell_list) override; void ue_cfg(uint16_t rnti, const ue_cfg_t& cfg) override; - void slot_indication(tti_point tti_rx) override; - int generate_sched_result(tti_point tti_rx, uint32_t cc, tti_request_t& tti_req) override; - void dl_ack_info(uint16_t rnti, uint32_t cc, uint32_t pid, uint32_t tb_idx, bool ack) override; void ul_sr_info(tti_point tti_rx, uint16_t rnti) override; + int get_dl_sched(tti_point pdsch_tti, uint32_t cc, dl_sched_t& result) override; + int get_ul_sched(tti_point pdcch_tti, uint32_t cc, ul_sched_t& result) override; + private: + int generate_slot_result(tti_point pdcch_tti, uint32_t cc); void ue_cfg_impl(uint16_t rnti, const ue_cfg_t& cfg); // args sched_nr_impl::sched_params cfg; + srslog::basic_logger& logger; using sched_worker_manager = sched_nr_impl::sched_worker_manager; std::unique_ptr sched_workers; @@ -67,8 +71,14 @@ private: std::mutex ue_db_mutex; ue_map_t ue_db; - // management of PHY UE feedback + // management of UE feedback std::unique_ptr pending_events; + + // management of Sched Result buffering + std::unique_ptr pending_results; + + // management of cell resources + std::vector > cells; }; } // namespace srsenb diff --git a/srsenb/hdr/stack/mac/nr/sched_nr_bwp.h b/srsenb/hdr/stack/mac/nr/sched_nr_bwp.h new file mode 100644 index 000000000..252ee6f26 --- /dev/null +++ b/srsenb/hdr/stack/mac/nr/sched_nr_bwp.h @@ -0,0 +1,78 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#ifndef SRSRAN_SCHED_NR_BWP_H +#define SRSRAN_SCHED_NR_BWP_H + +#include "sched_nr_cfg.h" +#include "sched_nr_rb_grid.h" +#include "srsran/adt/pool/cached_alloc.h" + +namespace srsenb { +namespace sched_nr_impl { + +using dl_sched_rar_info_t = sched_nr_interface::dl_sched_rar_info_t; + +struct pending_rar_t { + uint16_t ra_rnti = 0; + tti_point prach_tti; + srsran::bounded_vector msg3_grant; +}; + +/// RAR/Msg3 scheduler +class ra_sched +{ +public: + explicit ra_sched(const bwp_params& bwp_cfg_); + + int dl_rach_info(const dl_sched_rar_info_t& rar_info); + void run_slot(bwp_slot_allocator& slot_grid); + size_t empty() const { return pending_rars.empty(); } + +private: + alloc_result + allocate_pending_rar(bwp_slot_allocator& slot_grid, const pending_rar_t& rar, uint32_t& nof_grants_alloc); + + const bwp_params* bwp_cfg = nullptr; + srslog::basic_logger& logger; + + srsran::deque pending_rars; +}; + +class bwp_ctxt +{ +public: + explicit bwp_ctxt(const bwp_params& bwp_cfg); + + const bwp_params* cfg; + + // channel-specific schedulers + ra_sched ra; + + // Stores pending allocations and PRB bitmaps + bwp_res_grid grid; +}; + +class serv_cell_ctxt +{ +public: + srsran::bounded_vector bwps; + + explicit serv_cell_ctxt(const sched_cell_params& cell_cfg_); + + const sched_cell_params* cfg; +}; + +} // namespace sched_nr_impl +} // namespace srsenb + +#endif // SRSRAN_SCHED_NR_BWP_H diff --git a/srsenb/hdr/stack/mac/nr/sched_nr_cfg.h b/srsenb/hdr/stack/mac/nr/sched_nr_cfg.h new file mode 100644 index 000000000..8ec8bfe37 --- /dev/null +++ b/srsenb/hdr/stack/mac/nr/sched_nr_cfg.h @@ -0,0 +1,190 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#ifndef SRSRAN_SCHED_NR_CFG_H +#define SRSRAN_SCHED_NR_CFG_H + +#include "sched_nr_interface.h" +#include "sched_nr_rb.h" + +namespace srsenb { + +const static size_t SCHED_NR_MAX_USERS = 4; +const static size_t SCHED_NR_NOF_SUBFRAMES = 10; +const static size_t SCHED_NR_NOF_HARQS = 16; +static const size_t MAX_NOF_AGGR_LEVELS = 5; + +namespace sched_nr_impl { + +const static size_t MAX_GRANTS = sched_nr_interface::MAX_GRANTS; + +using pucch_t = mac_interface_phy_nr::pucch_t; +using pucch_list_t = srsran::bounded_vector; +using pusch_t = mac_interface_phy_nr::pusch_t; +using pusch_list_t = srsran::bounded_vector; + +using sched_cfg_t = sched_nr_interface::sched_cfg_t; +using cell_cfg_t = sched_nr_interface::cell_cfg_t; +using bwp_cfg_t = sched_nr_interface::bwp_cfg_t; + +struct bwp_params { + const uint32_t bwp_id; + const uint32_t cc; + const bwp_cfg_t& cfg; + const cell_cfg_t& cell_cfg; + const sched_cfg_t& sched_cfg; + + // derived params + uint32_t P; + uint32_t N_rbg; + + bwp_params(const cell_cfg_t& cell, const sched_cfg_t& sched_cfg_, uint32_t cc, uint32_t bwp_id); +}; + +struct sched_cell_params { + const uint32_t cc; + const cell_cfg_t cell_cfg; + const sched_cfg_t& sched_cfg; + std::vector bwps; + + sched_cell_params(uint32_t cc_, const cell_cfg_t& cell, const sched_cfg_t& sched_cfg_); + + uint32_t nof_prb() const { return cell_cfg.carrier.nof_prb; } +}; + +struct sched_params { + const sched_cfg_t sched_cfg; + std::vector cells; + + explicit sched_params(const sched_cfg_t& sched_cfg_); +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +using prb_bitmap = srsran::bounded_bitset; +using rbgmask_t = srsran::bounded_bitset; + +using pdcchmask_t = srsran::bounded_bitset; + +using pdcch_cce_pos_list = srsran::bounded_vector; +using bwp_cce_pos_list = std::array, SRSRAN_NOF_SF_X_FRAME>; +void get_dci_locs(const srsran_coreset_t& coreset, + const srsran_search_space_t& search_space, + uint16_t rnti, + bwp_cce_pos_list& cce_locs); + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +using ue_cfg_t = sched_nr_interface::ue_cfg_t; +using ue_cc_cfg_t = sched_nr_interface::ue_cc_cfg_t; + +class bwp_ue_cfg +{ +public: + bwp_ue_cfg() = default; + explicit bwp_ue_cfg(uint16_t rnti, const bwp_params& bwp_cfg, const ue_cfg_t& uecfg_); + + const ue_cfg_t* ue_cfg() const { return cfg_; } + const srsran::phy_cfg_nr_t& phy() const { return cfg_->phy_cfg; } + const bwp_params& active_bwp() const { return *bwp_cfg; } + const bwp_cce_pos_list& cce_pos_list(uint32_t search_id) const + { + return cce_positions_list[ss_id_to_cce_idx[search_id]]; + } + +private: + uint16_t rnti = SRSRAN_INVALID_RNTI; + const ue_cfg_t* cfg_ = nullptr; + const bwp_params* bwp_cfg = nullptr; + + std::vector cce_positions_list; + std::array ss_id_to_cce_idx; +}; + +class ue_cfg_extended : public ue_cfg_t +{ +public: + struct search_space_params { + const srsran_search_space_t* cfg; + bwp_cce_pos_list cce_positions; + }; + struct coreset_params { + srsran_coreset_t* cfg = nullptr; + std::vector ss_list; + }; + struct bwp_params { + std::array, SRSRAN_UE_DL_NR_MAX_NOF_SEARCH_SPACE> ss_list; + std::vector coresets; + }; + struct cc_params { + srsran::bounded_vector bwps; + }; + + ue_cfg_extended() = default; + explicit ue_cfg_extended(uint16_t rnti, const ue_cfg_t& uecfg); + + const bwp_cce_pos_list& get_dci_pos_list(uint32_t cc, uint32_t bwp_id, uint32_t search_space_id) const + { + return cc_params[cc].bwps[bwp_id].ss_list[search_space_id]->cce_positions; + } + + uint16_t rnti; + std::vector cc_params; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +struct resource_guard { +public: + resource_guard() = default; + resource_guard(const resource_guard& other) = delete; + resource_guard(resource_guard&& other) = delete; + resource_guard& operator=(const resource_guard& other) = delete; + resource_guard& operator=(resource_guard&& other) = delete; + bool busy() const { return flag; } + + struct token { + token() = default; + token(resource_guard& parent) : flag(parent.busy() ? nullptr : &parent.flag) + { + if (flag != nullptr) { + *flag = true; + } + } + token(token&&) noexcept = default; + token& operator=(token&&) noexcept = default; + void release() { flag.reset(); } + bool owns_token() const { return flag != nullptr; } + bool empty() const { return flag == nullptr; } + + private: + struct release_deleter { + void operator()(bool* ptr) + { + if (ptr != nullptr) { + srsran_assert(*ptr == true, "resource token: detected inconsistency token state"); + *ptr = false; + } + } + }; + std::unique_ptr flag; + }; + +private: + bool flag = false; +}; + +} // namespace sched_nr_impl + +} // namespace srsenb + +#endif // SRSRAN_SCHED_NR_CFG_H diff --git a/srsenb/hdr/stack/mac/nr/sched_nr_common.h b/srsenb/hdr/stack/mac/nr/sched_nr_common.h deleted file mode 100644 index 5268c0d2b..000000000 --- a/srsenb/hdr/stack/mac/nr/sched_nr_common.h +++ /dev/null @@ -1,111 +0,0 @@ -/** - * Copyright 2013-2021 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/. - * - */ - -#ifndef SRSRAN_SCHED_NR_COMMON_H -#define SRSRAN_SCHED_NR_COMMON_H - -#include "sched_nr_interface.h" -#include "srsran/adt/bounded_bitset.h" - -namespace srsenb { - -const static size_t SCHED_NR_MAX_USERS = 4; -const static size_t SCHED_NR_NOF_SUBFRAMES = 10; -const static size_t SCHED_NR_NOF_HARQS = 16; -static const size_t MAX_NOF_AGGR_LEVELS = 5; - -namespace sched_nr_impl { - -const static size_t MAX_GRANTS = sched_nr_interface::MAX_GRANTS; - -using sched_cfg_t = sched_nr_interface::sched_cfg_t; -using cell_cfg_t = sched_nr_interface::cell_cfg_t; - -struct sched_cell_params { - const uint32_t cc; - const cell_cfg_t cell_cfg; - const sched_cfg_t& sched_cfg; - - sched_cell_params(uint32_t cc_, const cell_cfg_t& cell, const sched_cfg_t& sched_cfg_); -}; - -struct sched_params { - const sched_cfg_t sched_cfg; - std::vector cells; - - explicit sched_params(const sched_cfg_t& sched_cfg_); -}; - -using pdcchmask_t = srsran::bounded_bitset; -using rbgmask_t = srsran::bounded_bitset; - -struct resource_guard { -public: - resource_guard() = default; - resource_guard(const resource_guard& other) = delete; - resource_guard(resource_guard&& other) = delete; - resource_guard& operator=(const resource_guard& other) = delete; - resource_guard& operator=(resource_guard&& other) = delete; - bool busy() const { return flag; } - - struct token { - token() = default; - token(resource_guard& parent) : flag(parent.busy() ? nullptr : &parent.flag) - { - if (flag != nullptr) { - *flag = true; - } - } - token(token&&) noexcept = default; - token& operator=(token&&) noexcept = default; - void release() { flag.reset(); } - bool owns_token() const { return flag != nullptr; } - bool empty() const { return flag == nullptr; } - - private: - struct release_deleter { - void operator()(bool* ptr) - { - if (ptr != nullptr) { - srsran_assert(*ptr == true, "resource token: detected inconsistency token state"); - *ptr = false; - } - } - }; - std::unique_ptr flag; - }; - -private: - bool flag = false; -}; - -using pdcch_cce_pos_list = srsran::bounded_vector; -using bwp_cce_pos_list = std::array, SRSRAN_NOF_SF_X_FRAME>; -void get_dci_locs(const srsran_coreset_t& coreset, - const srsran_search_space_t& search_space, - uint16_t rnti, - bwp_cce_pos_list& cce_locs); - -} // namespace sched_nr_impl - -} // namespace srsenb - -#endif // SRSRAN_SCHED_NR_COMMON_H diff --git a/srsenb/hdr/stack/mac/nr/sched_nr_harq.h b/srsenb/hdr/stack/mac/nr/sched_nr_harq.h index 6fe328c38..27051e16d 100644 --- a/srsenb/hdr/stack/mac/nr/sched_nr_harq.h +++ b/srsenb/hdr/stack/mac/nr/sched_nr_harq.h @@ -22,7 +22,8 @@ #ifndef SRSRAN_SCHED_NR_HARQ_H #define SRSRAN_SCHED_NR_HARQ_H -#include "sched_nr_common.h" +#include "sched_nr_cfg.h" +#include "srsenb/hdr/stack/mac/nr/harq_softbuffer.h" #include "srsran/common/tti_point.h" #include @@ -40,19 +41,25 @@ public: } bool empty(uint32_t tb_idx) const { return not tb[tb_idx].active; } bool has_pending_retx(tti_point tti_rx) const { return not empty() and not tb[0].ack_state and tti_ack <= tti_rx; } - uint32_t nof_retx() const { return tb[0].n_rtx; } - uint32_t max_nof_retx() const { return max_retx; } - uint32_t tbs() const { return tb[0].tbs; } - uint32_t ndi() const { return tb[0].ndi; } - uint32_t mcs() const { return tb[0].mcs; } + uint32_t nof_retx() const { return tb[0].n_rtx; } + uint32_t max_nof_retx() const { return max_retx; } + uint32_t tbs() const { return tb[0].tbs; } + uint32_t ndi() const { return tb[0].ndi; } + uint32_t mcs() const { return tb[0].mcs; } + const prb_grant& prbs() const { return prbs_; } + tti_point harq_tti_ack() const { return tti_ack; } bool ack_info(uint32_t tb_idx, bool ack); void new_tti(tti_point tti_rx); void reset(); bool - new_tx(tti_point tti_tx, tti_point tti_ack, const rbgmask_t& rbgmask, uint32_t mcs, uint32_t tbs, uint32_t max_retx); - bool new_retx(tti_point tti_tx, tti_point tti_ack, const rbgmask_t& rbgmask, int* mcs, int* tbs); + new_tx(tti_point tti_tx, tti_point tti_ack, const prb_grant& grant, uint32_t mcs, uint32_t tbs, uint32_t max_retx); + bool new_retx(tti_point tti_tx, tti_point tti_ack, const prb_grant& grant); + bool new_retx(tti_point tti_tx, tti_point tti_ack); + + // NOTE: Has to be used before first tx is dispatched + bool set_tbs(uint32_t tbs); const uint32_t pid; @@ -69,27 +76,59 @@ private: uint32_t max_retx = 1; tti_point tti_tx; tti_point tti_ack; - rbgmask_t rbgmask; + prb_grant prbs_; std::array tb; }; +class dl_harq_proc : public harq_proc +{ +public: + dl_harq_proc(uint32_t id_, uint32_t nprb) : + harq_proc(id_), softbuffer(harq_softbuffer_pool::get_instance().get_tx(nprb)) + {} + + tx_harq_softbuffer& get_softbuffer() { return *softbuffer; } + +private: + srsran::unique_pool_ptr softbuffer; +}; + +class ul_harq_proc : public harq_proc +{ +public: + ul_harq_proc(uint32_t id_, uint32_t nprb) : + harq_proc(id_), softbuffer(harq_softbuffer_pool::get_instance().get_rx(nprb)) + {} + + rx_harq_softbuffer& get_softbuffer() { return *softbuffer; } + + bool set_tbs(uint32_t tbs) + { + softbuffer->reset(tbs * 8u); + return harq_proc::set_tbs(tbs); + } + +private: + srsran::unique_pool_ptr softbuffer; +}; + class harq_entity { public: - explicit harq_entity(uint32_t nof_harq_procs = SCHED_NR_MAX_HARQ); + explicit harq_entity(uint32_t nprb, uint32_t nof_harq_procs = SCHED_NR_MAX_HARQ); void new_tti(tti_point tti_rx_); void dl_ack_info(uint32_t pid, uint32_t tb_idx, bool ack) { dl_harqs[pid].ack_info(tb_idx, ack); } - harq_proc* find_pending_dl_retx() + dl_harq_proc* find_pending_dl_retx() { - return find_dl([this](const harq_proc& h) { return h.has_pending_retx(tti_rx); }); + return find_dl([this](const dl_harq_proc& h) { return h.has_pending_retx(tti_rx); }); } harq_proc* find_pending_ul_retx() { return find_ul([this](const harq_proc& h) { return h.has_pending_retx(tti_rx); }); } - harq_proc* find_empty_dl_harq() + dl_harq_proc* find_empty_dl_harq() { return find_dl([](const harq_proc& h) { return h.empty(); }); } @@ -100,7 +139,7 @@ public: private: template - harq_proc* find_dl(Predicate p) + dl_harq_proc* find_dl(Predicate p) { auto it = std::find_if(dl_harqs.begin(), dl_harqs.end(), p); return (it == dl_harqs.end()) ? nullptr : &(*it); @@ -112,9 +151,9 @@ private: return (it == ul_harqs.end()) ? nullptr : &(*it); } - tti_point tti_rx; - std::vector dl_harqs; - std::vector ul_harqs; + tti_point tti_rx; + std::vector dl_harqs; + std::vector ul_harqs; }; } // namespace sched_nr_impl diff --git a/srsenb/hdr/stack/mac/nr/sched_nr_helpers.h b/srsenb/hdr/stack/mac/nr/sched_nr_helpers.h new file mode 100644 index 000000000..019628cd1 --- /dev/null +++ b/srsenb/hdr/stack/mac/nr/sched_nr_helpers.h @@ -0,0 +1,44 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#ifndef SRSRAN_SCHED_NR_HELPERS_H +#define SRSRAN_SCHED_NR_HELPERS_H + +#include "sched_nr_cfg.h" + +namespace srsenb { +namespace sched_nr_impl { + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool fill_dci_rar(prb_interval interv, const bwp_params& bwp_cfg, srsran_dci_dl_nr_t& dci); + +class slot_ue; + +/// Generate PDCCH DL DCI fields +void fill_dl_dci_ue_fields(const slot_ue& ue, + const bwp_params& bwp_cfg, + uint32_t ss_id, + srsran_dci_location_t dci_pos, + srsran_dci_dl_nr_t& dci); + +/// Generate PDCCH UL DCI fields +void fill_ul_dci_ue_fields(const slot_ue& ue, + const bwp_params& bwp_cfg, + uint32_t ss_id, + srsran_dci_location_t dci_pos, + srsran_dci_ul_nr_t& dci); + +} // namespace sched_nr_impl +} // namespace srsenb + +#endif // SRSRAN_SCHED_NR_HELPERS_H diff --git a/srsenb/hdr/stack/mac/nr/sched_nr_interface.h b/srsenb/hdr/stack/mac/nr/sched_nr_interface.h index e40eab90b..5e83ebc69 100644 --- a/srsenb/hdr/stack/mac/nr/sched_nr_interface.h +++ b/srsenb/hdr/stack/mac/nr/sched_nr_interface.h @@ -24,6 +24,7 @@ #include "srsran/adt/bounded_bitset.h" #include "srsran/adt/bounded_vector.h" +#include "srsran/adt/optional.h" #include "srsran/adt/span.h" #include "srsran/common/phy_cfg_nr.h" #include "srsran/common/tti_point.h" @@ -34,18 +35,15 @@ namespace srsenb { const static size_t SCHED_NR_MAX_CARRIERS = 4; const static uint16_t SCHED_NR_INVALID_RNTI = 0; -const static size_t SCHED_NR_MAX_PDSCH_DATA = 16; -const static size_t SCHED_NR_MAX_NOF_RBGS = 25; -const static size_t SCHED_NR_MAX_UL_ALLOCS = 16; +const static size_t SCHED_NR_MAX_NOF_RBGS = 18; const static size_t SCHED_NR_MAX_TB = 1; const static size_t SCHED_NR_MAX_HARQ = 16; -const static size_t SCHED_NR_MAX_BWP_PER_CELL = 1; +const static size_t SCHED_NR_MAX_BWP_PER_CELL = 2; class sched_nr_interface { public: - using pdcch_bitmap = srsran::bounded_bitset; - using rbg_bitmap = srsran::bounded_bitset; + static const size_t MAX_GRANTS = mac_interface_phy_nr::MAX_GRANTS; ///// Configuration ///// @@ -53,21 +51,25 @@ public: uint8_t k0 = 0; // 0..32 uint8_t k1 = 4; // 0..32 }; - using pdsch_td_res_alloc_list = srsran::bounded_vector; + using pdsch_td_res_alloc_list = srsran::bounded_vector; struct pusch_td_res_alloc { uint8_t k2 = 4; // 0..32 }; - using pusch_td_res_alloc_list = srsran::bounded_vector; + using pusch_td_res_alloc_list = srsran::bounded_vector; struct bwp_cfg_t { - uint32_t start_rb = 0; - uint32_t rb_width = 100; + uint32_t start_rb = 0; + uint32_t rb_width = 100; + srsran_pdcch_cfg_nr_t pdcch = {}; + srsran_sch_hl_cfg_nr_t pdsch = {}; + srsran_sch_hl_cfg_nr_t pusch = {}; + uint32_t rar_window_size = 3; }; struct cell_cfg_t { - uint32_t nof_prb = 100; - uint32_t nof_rbg = 25; - srsran::bounded_vector bwps{1}; + srsran_carrier_nr_t carrier = {}; + srsran_tdd_config_nr_t tdd = {}; + srsran::bounded_vector bwps{1}; // idx0 for BWP-common }; struct sched_cfg_t { @@ -75,9 +77,8 @@ public: }; struct ue_cc_cfg_t { - bool active = false; - pdsch_td_res_alloc_list pdsch_res_list{1}; - pusch_td_res_alloc_list pusch_res_list{1}; + bool active = false; + uint32_t cc = 0; }; struct ue_cfg_t { @@ -86,47 +87,26 @@ public: srsran::phy_cfg_nr_t phy_cfg = {}; }; + ////// RACH ////// + + struct dl_sched_rar_info_t { + uint32_t preamble_idx; + uint32_t ta_cmd; + uint16_t temp_crnti; + uint32_t msg3_size; + uint32_t prach_tti; + }; + ///// Sched Result ///// - const static int MAX_GRANTS = 64; + using dl_sched_t = mac_interface_phy_nr::dl_sched_t; + using ul_sched_t = mac_interface_phy_nr::ul_sched_t; - using pdcch_dl_t = mac_interface_phy_nr::pdcch_dl_t; - using pdcch_ul_t = mac_interface_phy_nr::pdcch_ul_t; - using pdcch_dl_list_t = srsran::bounded_vector; - using pdcch_ul_list_t = srsran::bounded_vector; - - struct pdsch_t { - srsran_sch_cfg_nr_t sch = {}; ///< PDSCH configuration - }; - using pdsch_list_t = srsran::bounded_vector; - - struct dl_tti_request_t { - tti_point pdsch_tti; - pdcch_dl_list_t pdcchs; - pdsch_list_t pdschs; - }; - - struct pusch_grant { - srsran_dci_ul_nr_t dci; - rbg_bitmap bitmap; - }; - using pusch_list = srsran::bounded_vector; - - struct ul_tti_request_t { - tti_point pusch_tti; - srsran::bounded_vector pusch; - }; - - struct tti_request_t { - dl_tti_request_t dl_res; - ul_tti_request_t ul_res; - }; - - virtual ~sched_nr_interface() = default; - virtual int cell_cfg(srsran::const_span ue_cfg) = 0; - virtual void ue_cfg(uint16_t rnti, const ue_cfg_t& ue_cfg) = 0; - virtual void slot_indication(tti_point tti_rx) = 0; - virtual int generate_sched_result(tti_point tti_rx, uint32_t cc, tti_request_t& result) = 0; + virtual ~sched_nr_interface() = default; + virtual int cell_cfg(srsran::const_span ue_cfg) = 0; + virtual void ue_cfg(uint16_t rnti, const ue_cfg_t& ue_cfg) = 0; + virtual int get_dl_sched(tti_point tti_rx, uint32_t cc, dl_sched_t& result) = 0; + virtual int get_ul_sched(tti_point tti_rx, uint32_t cc, ul_sched_t& result) = 0; virtual void dl_ack_info(uint16_t rnti, uint32_t cc, uint32_t pid, uint32_t tb_idx, bool ack) = 0; virtual void ul_sr_info(tti_point, uint16_t rnti) = 0; diff --git a/srsenb/hdr/stack/mac/nr/sched_nr_pdcch.h b/srsenb/hdr/stack/mac/nr/sched_nr_pdcch.h index a035a760b..d9871a50a 100644 --- a/srsenb/hdr/stack/mac/nr/sched_nr_pdcch.h +++ b/srsenb/hdr/stack/mac/nr/sched_nr_pdcch.h @@ -22,7 +22,7 @@ #ifndef SRSRAN_SCHED_NR_PDCCH_H #define SRSRAN_SCHED_NR_PDCCH_H -#include "srsenb/hdr/stack/mac/nr/sched_nr_common.h" +#include "srsenb/hdr/stack/mac/nr/sched_nr_cfg.h" #include "srsran/adt/bounded_bitset.h" #include "srsran/adt/bounded_vector.h" #include "srsran/phy/common/phy_common_nr.h" @@ -34,23 +34,24 @@ namespace sched_nr_impl { using coreset_bitmap = srsran::bounded_bitset; -enum class pdcch_grant_type_t { sib, dl_data, ul_data }; +enum class pdcch_grant_type_t { sib, rar, dl_data, ul_data }; class slot_ue; -using pdcch_dl_t = sched_nr_interface::pdcch_dl_t; -using pdcch_dl_list_t = sched_nr_interface::pdcch_dl_list_t; -using pdcch_ul_t = sched_nr_interface::pdcch_ul_t; -using pdcch_ul_list_t = sched_nr_interface::pdcch_ul_list_t; +using bwp_cfg_t = sched_nr_interface::bwp_cfg_t; +using pdcch_dl_t = mac_interface_phy_nr::pdcch_dl_t; +using pdcch_ul_t = mac_interface_phy_nr::pdcch_ul_t; +using pdcch_dl_list_t = srsran::bounded_vector; +using pdcch_ul_list_t = srsran::bounded_vector; class coreset_region { public: - coreset_region(uint32_t bwp_id_, - uint32_t slot_idx, - uint32_t nof_td_symbols, - uint32_t nof_freq_resources, - pdcch_dl_list_t& pdcch_list); + coreset_region(const bwp_params& bwp_cfg_, + uint32_t coreset_id_, + uint32_t slot_idx, + pdcch_dl_list_t& pdcch_dl_list, + pdcch_ul_list_t& pdcch_ul_list); void reset(); /** @@ -60,31 +61,32 @@ public: * @param user UE object or null in case of broadcast/RAR/paging allocation * @return if the allocation was successful */ - bool alloc_dci(pdcch_grant_type_t alloc_type, uint32_t aggr_idx, uint32_t coreset_id, slot_ue* user = nullptr); + bool alloc_dci(pdcch_grant_type_t alloc_type, uint32_t aggr_idx, uint32_t search_space_id, slot_ue* user = nullptr); void rem_last_dci(); - uint32_t get_td_symbols() const { return nof_symbols; } + uint32_t get_td_symbols() const { return coreset_cfg->duration; } uint32_t get_freq_resources() const { return nof_freq_res; } - uint32_t nof_cces() const { return nof_freq_res * nof_symbols; } + uint32_t nof_cces() const { return nof_freq_res * get_td_symbols(); } size_t nof_allocs() const { return dfs_tree.size(); } private: - uint32_t bwp_id; - uint32_t slot_idx; - uint32_t nof_symbols; - uint32_t nof_freq_res; + const srsran_coreset_t* coreset_cfg; + uint32_t coreset_id; + uint32_t slot_idx; + uint32_t nof_freq_res = 0; // List of PDCCH grants struct alloc_record { - uint32_t coreset_id; uint32_t aggr_idx; + uint32_t ss_id; uint32_t idx; pdcch_grant_type_t alloc_type; slot_ue* ue; }; srsran::bounded_vector dci_list; pdcch_dl_list_t& pdcch_dl_list; + pdcch_ul_list_t& pdcch_ul_list; // DFS decision tree of PDCCH grants struct tree_node { diff --git a/srsenb/hdr/stack/mac/nr/sched_nr_phy_helpers.h b/srsenb/hdr/stack/mac/nr/sched_nr_phy_helpers.h deleted file mode 100644 index 21528710e..000000000 --- a/srsenb/hdr/stack/mac/nr/sched_nr_phy_helpers.h +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright 2013-2021 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/. - * - */ - -#ifndef SRSRAN_SCHED_NR_PHY_HELPERS_H -#define SRSRAN_SCHED_NR_PHY_HELPERS_H - -#include "sched_nr_common.h" - -namespace srsenb { -namespace sched_nr_impl { - -uint32_t get_P(uint32_t bwp_nof_prb, bool config_1_or_2); -uint32_t get_nof_rbgs(uint32_t bwp_nof_prb, uint32_t bwp_start, bool config1_or_2); - -void bitmap_to_prb_array(const rbgmask_t& bitmap, uint32_t bwp_nof_prb, srsran_sch_grant_nr_t& grant); - -class slot_ue; -void fill_dci_ue_cfg(const slot_ue& ue, srsran_dci_dl_nr_t& dci); -void fill_dci_ue_cfg(const slot_ue& ue, srsran_dci_ul_nr_t& dci); - -} // namespace sched_nr_impl -} // namespace srsenb - -#endif // SRSRAN_SCHED_NR_PHY_HELPERS_H diff --git a/srsenb/hdr/stack/mac/nr/sched_nr_rb.h b/srsenb/hdr/stack/mac/nr/sched_nr_rb.h new file mode 100644 index 000000000..a8e88e2b0 --- /dev/null +++ b/srsenb/hdr/stack/mac/nr/sched_nr_rb.h @@ -0,0 +1,237 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#ifndef SRSRAN_SCHED_NR_RB_H +#define SRSRAN_SCHED_NR_RB_H + +#include "srsenb/hdr/stack/mac/nr/sched_nr_interface.h" +#include "srsran/adt/bounded_bitset.h" +#include "srsran/phy/common/phy_common_nr.h" + +namespace srsenb { +namespace sched_nr_impl { + +using prb_bitmap = srsran::bounded_bitset; +using rbg_bitmap = srsran::bounded_bitset; + +/// TS 38.214, Table 6.1.2.2.1-1 - Nominal RBG size P +uint32_t get_P(uint32_t bwp_nof_prb, bool config_1_or_2); + +/// TS 38.214 - total number of RBGs for a uplink bandwidth part of size "bwp_nof_prb" PRBs +uint32_t get_nof_rbgs(uint32_t bwp_nof_prb, uint32_t bwp_start, bool config1_or_2); + +/// Struct to express a {min,...,max} range of PRBs +struct prb_interval : public srsran::interval { + using interval::interval; +}; + +struct prb_grant { + prb_grant() = default; + prb_grant(const prb_interval& other) noexcept : alloc_type_0(false), alloc(other) {} + prb_grant(const rbg_bitmap& other) noexcept : alloc_type_0(true), alloc(other) {} + prb_grant(const prb_grant& other) noexcept : alloc_type_0(other.alloc_type_0), alloc(other.alloc_type_0, other.alloc) + {} + prb_grant& operator=(const prb_grant& other) noexcept + { + if (this == &other) { + return *this; + } + if (other.alloc_type_0) { + *this = other.rbgs(); + } else { + *this = other.prbs(); + } + return *this; + } + prb_grant& operator=(const prb_interval& prbs) + { + if (alloc_type_0) { + alloc_type_0 = false; + alloc.rbgs.~rbg_bitmap(); + new (&alloc.interv) prb_interval(prbs); + } else { + alloc.interv = alloc.interv; + } + return *this; + } + prb_grant& operator=(const rbg_bitmap& rbgs) + { + if (alloc_type_0) { + alloc.rbgs = rbgs; + } else { + alloc_type_0 = true; + alloc.interv.~prb_interval(); + new (&alloc.rbgs) rbg_bitmap(rbgs); + } + return *this; + } + ~prb_grant() + { + if (is_alloc_type0()) { + alloc.rbgs.~rbg_bitmap(); + } else { + alloc.interv.~prb_interval(); + } + } + + bool is_alloc_type0() const { return alloc_type_0; } + bool is_alloc_type1() const { return not is_alloc_type0(); } + const rbg_bitmap& rbgs() const + { + srsran_assert(is_alloc_type0(), "Invalid access to rbgs() field of grant with alloc type 1"); + return alloc.rbgs; + } + const prb_interval& prbs() const + { + srsran_assert(is_alloc_type1(), "Invalid access to prbs() field of grant with alloc type 0"); + return alloc.interv; + } + rbg_bitmap& rbgs() + { + srsran_assert(is_alloc_type0(), "Invalid access to rbgs() field of grant with alloc type 1"); + return alloc.rbgs; + } + prb_interval& prbs() + { + srsran_assert(is_alloc_type1(), "Invalid access to prbs() field of grant with alloc type 0"); + return alloc.interv; + } + +private: + bool alloc_type_0 = false; + union alloc_t { + rbg_bitmap rbgs; + prb_interval interv; + + alloc_t() : interv(0, 0) {} + explicit alloc_t(const prb_interval& prbs) : interv(prbs) {} + explicit alloc_t(const rbg_bitmap& rbgs_) : rbgs(rbgs_) {} + alloc_t(bool type0, const alloc_t& other) + { + if (type0) { + new (&rbgs) rbg_bitmap(other.rbgs); + } else { + new (&interv) prb_interval(other.interv); + } + } + } alloc; +}; + +struct bwp_rb_bitmap { +public: + bwp_rb_bitmap() = default; + bwp_rb_bitmap(uint32_t bwp_nof_prbs, uint32_t bwp_prb_start_, bool config1_or_2); + + void reset() + { + prbs_.reset(); + rbgs_.reset(); + } + + template + void operator|=(const T& grant) + { + add(grant); + } + + void add(const prb_interval& prbs) + { + prbs_.fill(prbs.start(), prbs.stop()); + add_prbs_to_rbgs(prbs); + } + void add(const prb_bitmap& grant) + { + prbs_ |= grant; + add_prbs_to_rbgs(grant); + } + void add(const rbg_bitmap& grant) + { + rbgs_ |= grant; + add_rbgs_to_prbs(grant); + } + void add(const prb_grant& grant) + { + if (grant.is_alloc_type0()) { + add(grant.rbgs()); + } else { + add(grant.prbs()); + } + } + bool collides(const prb_grant& grant) const + { + if (grant.is_alloc_type0()) { + return (rbgs() & grant.rbgs()).any(); + } + return prbs().any(grant.prbs().start(), grant.prbs().stop()); + } + bool test(uint32_t prb_idx) { return prbs().test(prb_idx); } + void set(uint32_t prb_idx) + { + prbs_.set(prb_idx); + rbgs_.set(prb_to_rbg_idx(prb_idx)); + } + + const prb_bitmap& prbs() const { return prbs_; } + const rbg_bitmap& rbgs() const { return rbgs_; } + uint32_t P() const { return P_; } + uint32_t nof_prbs() const { return prbs_.size(); } + uint32_t nof_rbgs() const { return rbgs_.size(); } + + uint32_t prb_to_rbg_idx(uint32_t prb_idx) const; + +private: + prb_bitmap prbs_; + rbg_bitmap rbgs_; + uint32_t P_ = 0; + uint32_t Pnofbits = 0; + uint32_t first_rbg_size = 0; + + void add_prbs_to_rbgs(const prb_bitmap& grant); + void add_prbs_to_rbgs(const prb_interval& grant); + void add_rbgs_to_prbs(const rbg_bitmap& grant); +}; + +inline prb_interval +find_next_empty_interval(const prb_bitmap& mask, size_t start_prb_idx = 0, size_t last_prb_idx = SRSRAN_MAX_PRB_NR) +{ + int rb_start = mask.find_lowest(start_prb_idx, std::min(mask.size(), last_prb_idx), false); + if (rb_start != -1) { + int rb_end = mask.find_lowest(rb_start + 1, std::min(mask.size(), last_prb_idx), true); + return {(uint32_t)rb_start, (uint32_t)(rb_end < 0 ? mask.size() : rb_end)}; + } + return {}; +} + +inline prb_interval find_empty_interval_of_length(const prb_bitmap& mask, size_t nof_prbs, uint32_t start_prb_idx = 0) +{ + prb_interval max_interv; + do { + prb_interval interv = find_next_empty_interval(mask, start_prb_idx, mask.size()); + if (interv.empty()) { + break; + } + if (interv.length() >= nof_prbs) { + max_interv.set(interv.start(), interv.start() + nof_prbs); + break; + } + if (interv.length() > max_interv.length()) { + max_interv = interv; + } + start_prb_idx = interv.stop() + 1; + } while (start_prb_idx < mask.size()); + return max_interv; +} + +} // namespace sched_nr_impl +} // namespace srsenb + +#endif // SRSRAN_SCHED_NR_RB_H diff --git a/srsenb/hdr/stack/mac/nr/sched_nr_rb_grid.h b/srsenb/hdr/stack/mac/nr/sched_nr_rb_grid.h index 3b07c3c6e..3c3077b95 100644 --- a/srsenb/hdr/stack/mac/nr/sched_nr_rb_grid.h +++ b/srsenb/hdr/stack/mac/nr/sched_nr_rb_grid.h @@ -24,6 +24,7 @@ #include "../sched_common.h" #include "lib/include/srsran/adt/circular_array.h" +#include "sched_nr_helpers.h" #include "sched_nr_interface.h" #include "sched_nr_pdcch.h" #include "sched_nr_ue.h" @@ -31,71 +32,76 @@ namespace srsenb { namespace sched_nr_impl { -using pdsch_bitmap = srsran::bounded_bitset<25, true>; -using pusch_bitmap = srsran::bounded_bitset<25, true>; +struct pending_rar_t; -using pdsch_t = sched_nr_interface::pdsch_t; -using pdsch_list_t = sched_nr_interface::pdsch_list_t; - -using pusch_list = sched_nr_interface::pusch_list; - -struct pucch_t {}; +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// const static size_t MAX_CORESET_PER_BWP = 3; -using slot_coreset_list = srsran::bounded_vector; +using slot_coreset_list = std::array, MAX_CORESET_PER_BWP>; + +using pdsch_t = mac_interface_phy_nr::pdsch_t; +using pdsch_list_t = srsran::bounded_vector; + +struct harq_ack_t { + const srsran::phy_cfg_nr_t* phy_cfg; + srsran_harq_ack_resource_t res; +}; +using harq_ack_list_t = srsran::bounded_vector; struct bwp_slot_grid { - pdcch_dl_list_t pdcch_dl_list; - pdcch_ul_list_t pdcch_ul_list; - slot_coreset_list coresets; - pdsch_bitmap dl_rbgs; - pdsch_list_t pdsch_grants; - pusch_bitmap ul_rbgs; - pusch_list pusch_grants; - srsran::bounded_vector pucch_grants; + uint32_t slot_idx; + const bwp_params* cfg; + + bool is_dl, is_ul; + bwp_rb_bitmap dl_prbs; + bwp_rb_bitmap ul_prbs; + pdcch_dl_list_t dl_pdcchs; + pdcch_ul_list_t ul_pdcchs; + pdsch_list_t pdschs; + slot_coreset_list coresets; + pusch_list_t puschs; + harq_ack_list_t pending_acks; bwp_slot_grid() = default; - explicit bwp_slot_grid(const sched_cell_params& cell_params, uint32_t bwp_id_, uint32_t slot_idx_); + explicit bwp_slot_grid(const bwp_params& bwp_params, uint32_t slot_idx_); void reset(); }; struct bwp_res_grid { - bwp_res_grid(const sched_cell_params& cell_cfg_, uint32_t bwp_id_); + bwp_res_grid(const bwp_params& bwp_cfg_); - bwp_slot_grid& operator[](tti_point tti) { return slots[tti.sf_idx()]; }; - const bwp_slot_grid& operator[](tti_point tti) const { return slots[tti.sf_idx()]; }; - uint32_t id() const { return bwp_id; } - uint32_t nof_prbs() const { return cell_cfg->cell_cfg.nof_prb; } + bwp_slot_grid& operator[](tti_point tti) { return slots[tti.to_uint() % slots.capacity()]; }; + const bwp_slot_grid& operator[](tti_point tti) const { return slots[tti.to_uint() % slots.capacity()]; }; + uint32_t id() const { return cfg->bwp_id; } + uint32_t nof_prbs() const { return cfg->cfg.rb_width; } + + const bwp_params* cfg = nullptr; private: - uint32_t bwp_id; - const sched_cell_params* cell_cfg = nullptr; - srsran::bounded_vector slots; }; -struct cell_res_grid { - const sched_cell_params* cell_cfg = nullptr; - srsran::bounded_vector bwps; - - explicit cell_res_grid(const sched_cell_params& cell_cfg); -}; - -class slot_bwp_sched +class bwp_slot_allocator { public: - explicit slot_bwp_sched(uint32_t bwp_id, cell_res_grid& phy_grid_); + explicit bwp_slot_allocator(bwp_res_grid& bwp_grid_); - alloc_result alloc_pdsch(slot_ue& ue, const rbgmask_t& dl_mask); + void new_slot(tti_point pdcch_tti_) { pdcch_tti = pdcch_tti_; } + + alloc_result alloc_rar(uint32_t aggr_idx, const pending_rar_t& rar, prb_interval interv, uint32_t max_nof_grants); + alloc_result alloc_pdsch(slot_ue& ue, const prb_grant& dl_grant); alloc_result alloc_pusch(slot_ue& ue, const rbgmask_t& dl_mask); - const sched_cell_params& cfg; + tti_point get_pdcch_tti() const { return pdcch_tti; } + const bwp_res_grid& res_grid() const { return bwp_grid; } + + const bwp_params& cfg; private: srslog::basic_logger& logger; bwp_res_grid& bwp_grid; - tti_point tti_rx; + tti_point pdcch_tti; }; } // namespace sched_nr_impl diff --git a/srsenb/hdr/stack/mac/nr/sched_nr_ue.h b/srsenb/hdr/stack/mac/nr/sched_nr_ue.h index 797416b19..3a12a0f49 100644 --- a/srsenb/hdr/stack/mac/nr/sched_nr_ue.h +++ b/srsenb/hdr/stack/mac/nr/sched_nr_ue.h @@ -22,7 +22,7 @@ #ifndef SRSRAN_SCHED_NR_UE_H #define SRSRAN_SCHED_NR_UE_H -#include "sched_nr_common.h" +#include "sched_nr_cfg.h" #include "sched_nr_harq.h" #include "sched_nr_interface.h" #include "srsran/adt/circular_map.h" @@ -33,35 +33,6 @@ namespace srsenb { namespace sched_nr_impl { -using ue_cfg_t = sched_nr_interface::ue_cfg_t; -using ue_cc_cfg_t = sched_nr_interface::ue_cc_cfg_t; - -class ue_cfg_extended : public ue_cfg_t -{ -public: - struct search_space_params { - srsran_search_space_t* cfg = nullptr; - }; - struct coreset_params { - srsran_coreset_t* cfg = nullptr; - std::vector ss_list; - bwp_cce_pos_list cce_positions; - }; - struct bwp_params { - std::vector search_spaces; - std::vector coresets; - }; - struct cc_params { - srsran::bounded_vector bwps; - }; - - uint16_t rnti; - std::vector cc_params; - - ue_cfg_extended() = default; - explicit ue_cfg_extended(uint16_t rnti, const ue_cfg_t& uecfg); -}; - class ue_carrier; class slot_ue @@ -79,19 +50,18 @@ public: uint32_t cc = SCHED_NR_MAX_CARRIERS; // UE parameters common to all sectors - const ue_cfg_extended* cfg = nullptr; - bool pending_sr; + const bwp_ue_cfg* cfg = nullptr; + bool pending_sr; // UE parameters that are sector specific const ue_cc_cfg_t* cc_cfg = nullptr; - uint32_t bwp_id; tti_point pdcch_tti; tti_point pdsch_tti; tti_point pusch_tti; tti_point uci_tti; uint32_t dl_cqi; uint32_t ul_cqi; - harq_proc* h_dl = nullptr; + dl_harq_proc* h_dl = nullptr; harq_proc* h_ul = nullptr; private: @@ -101,8 +71,8 @@ private: class ue_carrier { public: - ue_carrier(uint16_t rnti, uint32_t cc, const ue_cfg_t& cfg); - slot_ue try_reserve(tti_point pdcch_tti, const ue_cfg_extended& cfg); + ue_carrier(uint16_t rnti, const ue_cfg_t& cfg, const sched_cell_params& cell_params_); + slot_ue try_reserve(tti_point pdcch_tti, const ue_cfg_t& cfg); void push_feedback(srsran::move_callback callback); const uint16_t rnti; @@ -115,7 +85,8 @@ public: harq_entity harq_ent; private: - const ue_cfg_t* cfg = nullptr; + bwp_ue_cfg bwp_cfg; + const sched_cell_params& cell_params; resource_guard busy; tti_point last_tti_rx; @@ -126,7 +97,7 @@ private: class ue { public: - ue(uint16_t rnti, const ue_cfg_t& cfg); + ue(uint16_t rnti, const ue_cfg_t& cfg, const sched_params& sched_cfg_); slot_ue try_reserve(tti_point tti_rx, uint32_t cc); @@ -137,12 +108,13 @@ public: std::array, SCHED_NR_MAX_CARRIERS> carriers; private: - const uint16_t rnti; + const uint16_t rnti; + const sched_params& sched_cfg; bool pending_sr = false; - int current_idx = 0; - std::array ue_cfgs; + int current_idx = 0; + std::array ue_cfgs; }; using ue_map_t = srsran::static_circular_map, SCHED_NR_MAX_USERS>; diff --git a/srsenb/hdr/stack/mac/nr/sched_nr_worker.h b/srsenb/hdr/stack/mac/nr/sched_nr_worker.h index 3f4d520ae..40a3f7e35 100644 --- a/srsenb/hdr/stack/mac/nr/sched_nr_worker.h +++ b/srsenb/hdr/stack/mac/nr/sched_nr_worker.h @@ -22,7 +22,8 @@ #ifndef SRSRAN_SCHED_NR_WORKER_H #define SRSRAN_SCHED_NR_WORKER_H -#include "sched_nr_common.h" +#include "sched_nr_bwp.h" +#include "sched_nr_cfg.h" #include "sched_nr_rb_grid.h" #include "sched_nr_ue.h" #include "srsran/adt/circular_array.h" @@ -31,19 +32,17 @@ #include "srsran/adt/span.h" #include #include -#include namespace srsenb { namespace sched_nr_impl { -using slot_res_t = sched_nr_interface::tti_request_t; +using dl_sched_t = sched_nr_interface::dl_sched_t; +using ul_sched_t = sched_nr_interface::ul_sched_t; class slot_cc_worker { public: - explicit slot_cc_worker(const sched_cell_params& cell_params, cell_res_grid& phy_grid) : - cfg(cell_params), res_grid(0, phy_grid) - {} + explicit slot_cc_worker(serv_cell_ctxt& sched); void start(tti_point tti_rx_, ue_map_t& ue_db_); void run(); @@ -53,41 +52,50 @@ public: private: void alloc_dl_ues(); void alloc_ul_ues(); + void log_result() const; const sched_cell_params& cfg; + serv_cell_ctxt& cell; + srslog::basic_logger& logger; - tti_point tti_rx; - slot_bwp_sched res_grid; + tti_point tti_rx; + bwp_slot_allocator bwp_alloc; srsran::static_circular_map slot_ues; }; class sched_worker_manager { + struct slot_worker_ctxt { + std::mutex slot_mutex; // lock of all workers of the same slot. + std::condition_variable cvar; + tti_point tti_rx; + int nof_workers_waiting = 0; + std::atomic worker_count{0}; // variable shared across slot_cc_workers + std::vector workers; + }; + public: explicit sched_worker_manager(ue_map_t& ue_db_, const sched_params& cfg_); sched_worker_manager(const sched_worker_manager&) = delete; sched_worker_manager(sched_worker_manager&&) = delete; ~sched_worker_manager(); - void reserve_workers(tti_point tti_rx); - void start_tti(tti_point tti_rx); - bool run_tti(tti_point tti_rx, uint32_t cc, sched_nr_interface::tti_request_t& req); - void end_tti(tti_point tti_rx); + void start_slot(tti_point tti_rx, srsran::move_callback process_feedback); + bool run_slot(tti_point tti_rx, uint32_t cc); + void release_slot(tti_point tti_rx); + bool save_sched_result(tti_point pdcch_tti, uint32_t cc, dl_sched_t& dl_res, ul_sched_t& ul_res); private: - const sched_params& cfg; - ue_map_t& ue_db; + const sched_params& cfg; + ue_map_t& ue_db; + srslog::basic_logger& logger; - struct slot_worker_ctxt { - sem_t sf_sem; // lock of all workers of the same slot. unlocked by last slot_cc_worker - tti_point tti_rx; - std::atomic worker_count{0}; // variable shared across slot_cc_workers - std::vector workers; - }; - std::vector > slot_ctxts; + std::mutex ue_db_mutex; - srsran::bounded_vector cell_grid_list; + std::vector > slot_worker_ctxts; + + srsran::bounded_vector cell_grid_list; slot_worker_ctxt& get_sf(tti_point tti_rx); }; diff --git a/srsenb/hdr/stack/mac/ue.h b/srsenb/hdr/stack/mac/ue.h index ba272b252..e5b206d81 100644 --- a/srsenb/hdr/stack/mac/ue.h +++ b/srsenb/hdr/stack/mac/ue.h @@ -90,6 +90,7 @@ public: private: srslog::basic_logger* logger; + std::mutex mutex; srsran::static_circular_map pdu_map; }; @@ -224,7 +225,6 @@ private: // Mutexes std::mutex mutex; - std::mutex rx_buffers_mutex; }; } // namespace srsenb diff --git a/srsenb/hdr/stack/rrc/rrc_config.h b/srsenb/hdr/stack/rrc/rrc_config.h index 8920987bc..050f73606 100644 --- a/srsenb/hdr/stack/rrc/rrc_config.h +++ b/srsenb/hdr/stack/rrc/rrc_config.h @@ -40,12 +40,18 @@ struct rrc_cfg_sr_t { }; struct rrc_cfg_qci_t { - bool configured = false; + bool configured = false; + int enb_dl_max_retx_thres = -1; asn1::rrc::lc_ch_cfg_s::ul_specific_params_s_ lc_cfg; asn1::rrc::pdcp_cfg_s pdcp_cfg; asn1::rrc::rlc_cfg_c rlc_cfg; }; +struct srb_cfg_t { + int enb_dl_max_retx_thres = -1; + asn1::rrc::srb_to_add_mod_s::rlc_cfg_c_ rlc_cfg; +}; + struct rrc_cfg_t { uint32_t enb_id; ///< Required to pack SIB1 // Per eNB SIBs @@ -71,6 +77,8 @@ struct rrc_cfg_t { uint32_t max_mac_dl_kos; uint32_t max_mac_ul_kos; uint32_t rlf_release_timer_ms; + srb_cfg_t srb1_cfg; + srb_cfg_t srb2_cfg; }; constexpr uint32_t UE_PCELL_CC_IDX = 0; diff --git a/srsenb/hdr/stack/rrc/rrc_config_common.h b/srsenb/hdr/stack/rrc/rrc_config_common.h index 78e9bab3b..539fc15bd 100644 --- a/srsenb/hdr/stack/rrc/rrc_config_common.h +++ b/srsenb/hdr/stack/rrc/rrc_config_common.h @@ -40,6 +40,8 @@ struct rrc_cfg_cqi_t { uint32_t nof_prb; uint32_t period; uint32_t m_ri; + bool is_subband_enabled; + uint32_t subband_k; bool simultaneousAckCQI; rrc_cfg_cqi_mode_t mode; }; diff --git a/srsenb/drb.conf.example b/srsenb/rb.conf.example similarity index 55% rename from srsenb/drb.conf.example rename to srsenb/rb.conf.example index 55a1a1491..27d997fc3 100644 --- a/srsenb/drb.conf.example +++ b/srsenb/rb.conf.example @@ -1,6 +1,41 @@ - // All times are in ms. Use -1 for infinity, where available +// srb1_config = { +// rlc_config = { +// ul_am = { +// t_poll_retx = 45; +// poll_pdu = -1; +// poll_byte = -1; +// max_retx_thresh = 4; +// }; +// dl_am = { +// t_reordering = 35; +// t_status_prohibit = 0; +// }; +// }; +// enb_specific = { +// dl_max_retx_thresh = 32; +// }; +// } + +// srb2_config = { +// rlc_config = { +// ul_am = { +// t_poll_retx = 45; +// poll_pdu = -1; +// poll_byte = -1; +// max_retx_thresh = 4; +// }; +// dl_am = { +// t_reordering = 35; +// t_status_prohibit = 0; +// }; +// }; +// enb_specific = { +// dl_max_retx_thresh = 32; +// }; +// } + qci_config = ( { @@ -24,6 +59,9 @@ qci_config = ( bucket_size_duration = 100; log_chan_group = 2; }; + enb_specific = { + dl_max_retx_thresh = 32; + }; }, { qci=9; @@ -49,6 +87,9 @@ qci_config = ( bucket_size_duration = 100; log_chan_group = 3; }; + enb_specific = { + dl_max_retx_thresh = 32; + }; } ); diff --git a/srsenb/rr.conf.example b/srsenb/rr.conf.example index ee1b285a9..6c3bef5a9 100644 --- a/srsenb/rr.conf.example +++ b/srsenb/rr.conf.example @@ -47,6 +47,7 @@ phy_cnfg = //subframe = [0, 10, 20, 30]; // Optional vector of subframe indices every period where CQI resources will be allocated (default uses all) nof_prb = 1; m_ri = 8; // RI period in CQI period + //subband_k = 1; // If enabled and > 0, configures sub-band CQI reporting and defines K (see 36.213 7.2.2). If disabled, configures wideband CQI }; }; diff --git a/srsenb/src/CMakeLists.txt b/srsenb/src/CMakeLists.txt index e9dd9112a..e041ace70 100644 --- a/srsenb/src/CMakeLists.txt +++ b/srsenb/src/CMakeLists.txt @@ -41,7 +41,7 @@ add_executable(srsenb main.cc enb.cc metrics_stdout.cc metrics_csv.cc metrics_js set(SRSENB_SOURCES srsenb_phy srsenb_stack srsenb_common srsenb_s1ap srsenb_upper srsenb_mac srsenb_rrc srslog system) set(SRSRAN_SOURCES srsran_common srsran_mac srsran_phy srsran_gtpu srsran_rlc srsran_pdcp srsran_radio rrc_asn1 s1ap_asn1 enb_cfg_parser srslog system) -set(SRSENB_SOURCES ${SRSENB_SOURCES} srsgnb_phy srsgnb_stack srsgnb_ngap srsgnb_upper srsgnb_mac srsgnb_rrc) +set(SRSENB_SOURCES ${SRSENB_SOURCES} srsgnb_stack srsgnb_ngap srsgnb_upper srsgnb_mac srsgnb_rrc) set(SRSRAN_SOURCES ${SRSRAN_SOURCES} rrc_nr_asn1 ngap_nr_asn1) target_link_libraries(srsenb ${SRSENB_SOURCES} diff --git a/srsenb/src/enb.cc b/srsenb/src/enb.cc index b145f9896..4d75bc7d5 100644 --- a/srsenb/src/enb.cc +++ b/srsenb/src/enb.cc @@ -20,7 +20,6 @@ */ #include "srsenb/hdr/enb.h" -#include "srsenb/hdr/phy/vnf_phy_nr.h" #include "srsenb/hdr/stack/enb_stack_lte.h" #include "srsenb/hdr/stack/gnb_stack_nr.h" #include "srsenb/src/enb_cfg_parser.h" @@ -104,40 +103,9 @@ int enb::init(const all_args_t& args_) phy = std::move(lte_phy); radio = std::move(lte_radio); - } else if (args.stack.type == "nr") { - std::unique_ptr nr_stack(new srsenb::gnb_stack_nr); - std::unique_ptr nr_radio(new srsran::radio_null); - std::unique_ptr nr_phy(new srsenb::vnf_phy_nr); - - // Init layers - if (nr_radio->init(args.rf, nullptr)) { - srsran::console("Error initializing radio.\n"); - return SRSRAN_ERROR; - } - - // TODO: where do we put this? - srsenb::nr_phy_cfg_t nr_phy_cfg = {}; - - args.phy.vnf_args.type = "gnb"; - args.phy.vnf_args.log_level = args.phy.log.phy_level; - args.phy.vnf_args.log_hex_limit = args.phy.log.phy_hex_limit; - if (nr_phy->init(args.phy, nr_phy_cfg, nr_stack.get())) { - srsran::console("Error initializing PHY.\n"); - return SRSRAN_ERROR; - } - - // Same here, where do we put this? - srsenb::rrc_nr_cfg_t rrc_nr_cfg = {}; - rrc_nr_cfg.coreless = args.stack.coreless; - - if (nr_stack->init(args.stack, rrc_nr_cfg, nr_phy.get())) { - srsran::console("Error initializing stack.\n"); - return SRSRAN_ERROR; - } - - stack = std::move(nr_stack); - phy = std::move(nr_phy); - radio = std::move(nr_radio); + } else { + srsran::console("Stack type %s not supported.\n", args.stack.type.c_str()); + return SRSRAN_ERROR; } started = true; // set to true in any case to allow stopping the eNB if an error happened diff --git a/srsenb/src/enb_cfg_parser.cc b/srsenb/src/enb_cfg_parser.cc index 42fde5e61..8893f5216 100644 --- a/srsenb/src/enb_cfg_parser.cc +++ b/srsenb/src/enb_cfg_parser.cc @@ -82,7 +82,7 @@ int field_sched_info::parse(libconfig::Setting& root) for (uint32_t i = 0; i < data->sched_info_list.size(); i++) { if (not parse_enum_by_number(data->sched_info_list[i].si_periodicity, "si_periodicity", root[i])) { fprintf(stderr, "Missing field si_periodicity in sched_info=%d\n", i); - return -1; + return SRSRAN_ERROR; } if (root[i].exists("si_mapping_info")) { data->sched_info_list[i].sib_map_info.resize((uint32_t)root[i]["si_mapping_info"].getLength()); @@ -93,12 +93,12 @@ int field_sched_info::parse(libconfig::Setting& root) data->sched_info_list[i].sib_map_info[j].value = (sib_type_e::options)(sib_index - 3); } else { fprintf(stderr, "Invalid SIB index %d for si_mapping_info=%d in sched_info=%d\n", sib_index, j, i); - return -1; + return SRSRAN_ERROR; } } } else { fprintf(stderr, "Number of si_mapping_info values exceeds maximum (%d)\n", ASN1_RRC_MAX_SIB); - return -1; + return SRSRAN_ERROR; } } else { data->sched_info_list[i].sib_map_info.resize(0); @@ -114,13 +114,13 @@ int field_intra_neigh_cell_list::parse(libconfig::Setting& root) for (uint32_t i = 0; i < data->intra_freq_neigh_cell_list.size() && i < ASN1_RRC_MAX_CELL_INTRA; i++) { if (not parse_enum_by_number(data->intra_freq_neigh_cell_list[i].q_offset_cell, "q_offset_range", root[i])) { fprintf(stderr, "Missing field q_offset_range in neigh_cell=%d\n", i); - return -1; + return SRSRAN_ERROR; } int phys_cell_id = 0; if (!root[i].lookupValue("phys_cell_id", phys_cell_id)) { fprintf(stderr, "Missing field phys_cell_id in neigh_cell=%d\n", i); - return -1; + return SRSRAN_ERROR; } data->intra_freq_neigh_cell_list[i].pci = (uint16)phys_cell_id; } @@ -134,14 +134,14 @@ int field_intra_black_cell_list::parse(libconfig::Setting& root) for (uint32_t i = 0; i < data->intra_freq_black_cell_list.size() && i < ASN1_RRC_MAX_CELL_BLACK; i++) { if (not parse_enum_by_number(data->intra_freq_black_cell_list[i].range, "range", root[i])) { fprintf(stderr, "Missing field range in black_cell=%d\n", i); - return -1; + return SRSRAN_ERROR; } data->intra_freq_black_cell_list[i].range_present = true; int start = 0; if (!root[i].lookupValue("start", start)) { fprintf(stderr, "Missing field start in black_cell=%d\n", i); - return -1; + return SRSRAN_ERROR; } data->intra_freq_black_cell_list[i].start = (uint16)start; } @@ -154,7 +154,7 @@ int field_carrier_freqs_info_list::parse(libconfig::Setting& root) data->carrier_freqs_info_list_present = data->carrier_freqs_info_list.size() > 0; if (data->carrier_freqs_info_list.size() > ASN1_RRC_MAX_GNFG) { ERROR("CarrierFreqsInfoGERAN cannot have more than %d entries", ASN1_RRC_MAX_GNFG); - return -1; + return SRSRAN_ERROR; } for (uint32_t i = 0; i < data->carrier_freqs_info_list.size(); i++) { int cell_resel_prio; @@ -173,27 +173,27 @@ int field_carrier_freqs_info_list::parse(libconfig::Setting& root) "ncc_permitted", &data->carrier_freqs_info_list[i].common_info.ncc_permitted); if (ncc_permitted.parse(root[i])) { ERROR("Error parsing `ncc_permitted` in carrier_freqs_info_lsit=%d", i); - return -1; + return SRSRAN_ERROR; } int q_rx_lev_min = 0; if (!root[i].lookupValue("q_rx_lev_min", q_rx_lev_min)) { ERROR("Missing field `q_rx_lev_min` in carrier_freqs_info_list=%d", i); - return -1; + return SRSRAN_ERROR; } data->carrier_freqs_info_list[i].common_info.q_rx_lev_min = q_rx_lev_min; int thresh_x_high = 0; if (!root[i].lookupValue("thresh_x_high", thresh_x_high)) { ERROR("Missing field `thresh_x_high` in carrier_freqs_info_list=%d", i); - return -1; + return SRSRAN_ERROR; } data->carrier_freqs_info_list[i].common_info.thresh_x_high = thresh_x_high; int thresh_x_low = 0; if (!root[i].lookupValue("thresh_x_low", thresh_x_low)) { ERROR("Missing field `thresh_x_low` in carrier_freqs_info_list=%d", i); - return -1; + return SRSRAN_ERROR; } data->carrier_freqs_info_list[i].common_info.thresh_x_low = thresh_x_low; @@ -206,7 +206,7 @@ int field_carrier_freqs_info_list::parse(libconfig::Setting& root) &data->carrier_freqs_info_list[i].carrier_freqs.band_ind); if (band_ind.parse(root[i])) { ERROR("Error parsing `band_ind` in carrier_freqs_info_list=%d", i); - return -1; + return SRSRAN_ERROR; } data->carrier_freqs_info_list[i].carrier_freqs.following_arfcns.set_explicit_list_of_arfcns(); @@ -222,12 +222,12 @@ int field_carrier_freqs_info_list::parse(libconfig::Setting& root) exp_l[j] = (short unsigned int)arfcn; } else { fprintf(stderr, "Invalid ARFCN %d in for carrier_freqs_info_list=%d explicit_list_of_arfcns\n", i, j); - return -1; + return SRSRAN_ERROR; } } } else { fprintf(stderr, "Number of ARFCN in explicit_list_of_arfcns exceeds maximum (%d)\n", 31); - return -1; + return SRSRAN_ERROR; } } else { exp_l.resize(0); @@ -273,7 +273,7 @@ int mbsfn_sf_cfg_list_parser::parse(Setting& root) } if (len > 1) { fprintf(stderr, "Only mbsfnSubframeConfigListLengths of size 1 are supported\n"); - return -1; + return SRSRAN_ERROR; } *enabled = true; mbsfn_list->resize(len); @@ -315,53 +315,53 @@ int mbsfn_area_info_list_parser::parse(Setting& root) &mbsfn_item->non_mbsfn_region_len); if (fieldlen.parse(root["mbsfn_area_info_list"])) { fprintf(stderr, "Error parsing non_mbsfn_region_length\n"); - return -1; + return SRSRAN_ERROR; } field_asn1_enum_str repeat( "mcch_repetition_period", &mbsfn_item->mcch_cfg_r9.mcch_repeat_period_r9); if (repeat.parse(root["mbsfn_area_info_list"])) { fprintf(stderr, "Error parsing mcch_repetition_period\n"); - return -1; + return SRSRAN_ERROR; } field_asn1_enum_str mod( "mcch_modification_period", &mbsfn_item->mcch_cfg_r9.mcch_mod_period_r9); if (mod.parse(root["mbsfn_area_info_list"])) { fprintf(stderr, "Error parsing mcch_modification_period\n"); - return -1; + return SRSRAN_ERROR; } field_asn1_enum_str sig("signalling_mcs", &mbsfn_item->mcch_cfg_r9.sig_mcs_r9); if (sig.parse(root["mbsfn_area_info_list"])) { fprintf(stderr, "Error parsing signalling_mcs\n"); - return -1; + return SRSRAN_ERROR; } parser::field areaid("mbsfn_area_id", &mbsfn_item->mbsfn_area_id_r9); if (areaid.parse(root["mbsfn_area_info_list"])) { fprintf(stderr, "Error parsing mbsfn_area_id\n"); - return -1; + return SRSRAN_ERROR; } parser::field notif_ind("notification_indicator", &mbsfn_item->notif_ind_r9); if (notif_ind.parse(root["mbsfn_area_info_list"])) { fprintf(stderr, "Error parsing notification_indicator\n"); - return -1; + return SRSRAN_ERROR; } parser::field offset("mcch_offset", &mbsfn_item->mcch_cfg_r9.mcch_offset_r9); if (offset.parse(root["mbsfn_area_info_list"])) { fprintf(stderr, "Error parsing mcch_offset\n"); - return -1; + return SRSRAN_ERROR; } field_asn1_bitstring_number, uint8_t> alloc_info("sf_alloc_info", &mbsfn_item->mcch_cfg_r9.sf_alloc_info_r9); if (alloc_info.parse(root["mbsfn_area_info_list"])) { fprintf(stderr, "Error parsing mbsfn_area_info_list\n"); - return -1; + return SRSRAN_ERROR; } return 0; @@ -395,17 +395,83 @@ int phr_cnfg_parser::parse(libconfig::Setting& root) mac_main_cfg_s::phr_cfg_c_::setup_s_& s = phr_cfg->setup(); if (not parse_enum_by_str(s.dl_pathloss_change, "dl_pathloss_change", root["phr_cnfg"])) { - return -1; + return SRSRAN_ERROR; } if (not parse_enum_by_number(s.periodic_phr_timer, "periodic_phr_timer", root["phr_cnfg"])) { - return -1; + return SRSRAN_ERROR; } if (not parse_enum_by_number(s.prohibit_phr_timer, "prohibit_phr_timer", root["phr_cnfg"])) { - return -1; + return SRSRAN_ERROR; } return 0; } +int field_srb::parse(libconfig::Setting& root) +{ + // Parse RLC AM section + rlc_cfg_c* rlc_cfg = &cfg.rlc_cfg.set_explicit_value(); + if (root.exists("ul_am") && root.exists("dl_am")) { + rlc_cfg->set_am(); + } + + // RLC-UM Should not exist section + if (root.exists("ul_um") || root.exists("dl_um")) { + ERROR("Error SRBs must be AM."); + return SRSRAN_ERROR; + } + + // Parse RLC-AM section + if (root.exists("ul_am")) { + ul_am_rlc_s* am_rlc = &rlc_cfg->am().ul_am_rlc; + + field_asn1_enum_number t_poll_retx("t_poll_retx", &am_rlc->t_poll_retx); + if (t_poll_retx.parse(root["ul_am"])) { + ERROR("Error can't find t_poll_retx in section ul_am"); + return SRSRAN_ERROR; + } + + field_asn1_enum_number poll_pdu("poll_pdu", &am_rlc->poll_pdu); + if (poll_pdu.parse(root["ul_am"])) { + ERROR("Error can't find poll_pdu in section ul_am"); + return SRSRAN_ERROR; + } + + field_asn1_enum_number poll_byte("poll_byte", &am_rlc->poll_byte); + if (poll_byte.parse(root["ul_am"])) { + ERROR("Error can't find poll_byte in section ul_am"); + return SRSRAN_ERROR; + } + + field_asn1_enum_number max_retx_thresh("max_retx_thresh", &am_rlc->max_retx_thres); + if (max_retx_thresh.parse(root["ul_am"])) { + ERROR("Error can't find max_retx_thresh in section ul_am"); + return SRSRAN_ERROR; + } + } + + if (root.exists("dl_am")) { + dl_am_rlc_s* am_rlc = &rlc_cfg->am().dl_am_rlc; + + field_asn1_enum_number t_reordering("t_reordering", &am_rlc->t_reordering); + if (t_reordering.parse(root["dl_am"])) { + ERROR("Error can't find t_reordering in section dl_am"); + return SRSRAN_ERROR; + } + + field_asn1_enum_number t_status_prohibit("t_status_prohibit", &am_rlc->t_status_prohibit); + if (t_status_prohibit.parse(root["dl_am"])) { + ERROR("Error can't find t_status_prohibit in section dl_am"); + return SRSRAN_ERROR; + } + } + + if (root.exists("enb_specific")) { + cfg.enb_dl_max_retx_thres = (int)root["enb_specific"]["dl_max_retx_thresh"]; + } + + return 0; +} + int field_qci::parse(libconfig::Setting& root) { auto nof_qci = (uint32_t)root.getLength(); @@ -418,7 +484,7 @@ int field_qci::parse(libconfig::Setting& root) // Parse PDCP section if (!q.exists("pdcp_config")) { fprintf(stderr, "Error section pdcp_config not found for qci=%d\n", qci); - return -1; + return SRSRAN_ERROR; } rrc_cfg_qci_t qcicfg; @@ -447,7 +513,7 @@ int field_qci::parse(libconfig::Setting& root) rlc_cfg->set_um_uni_dir_dl(); } else { fprintf(stderr, "Invalid combination of UL/DL UM/AM for qci=%d\n", qci); - return -1; + return SRSRAN_ERROR; } // Parse RLC-UM section @@ -462,7 +528,7 @@ int field_qci::parse(libconfig::Setting& root) field_asn1_enum_number sn_field_len("sn_field_length", &um_rlc->sn_field_len); if (sn_field_len.parse(q["rlc_config"]["ul_um"])) { ERROR("Error can't find sn_field_length in section ul_um"); - return -1; + return SRSRAN_ERROR; } } @@ -477,13 +543,13 @@ int field_qci::parse(libconfig::Setting& root) field_asn1_enum_number sn_field_len("sn_field_length", &um_rlc->sn_field_len); if (sn_field_len.parse(q["rlc_config"]["dl_um"])) { ERROR("Error can't find sn_field_length in section dl_um"); - return -1; + return SRSRAN_ERROR; } field_asn1_enum_number t_reordering("t_reordering", &um_rlc->t_reordering); if (t_reordering.parse(q["rlc_config"]["dl_um"])) { ERROR("Error can't find t_reordering in section dl_um"); - return -1; + return SRSRAN_ERROR; } } @@ -494,26 +560,26 @@ int field_qci::parse(libconfig::Setting& root) field_asn1_enum_number t_poll_retx("t_poll_retx", &am_rlc->t_poll_retx); if (t_poll_retx.parse(q["rlc_config"]["ul_am"])) { ERROR("Error can't find t_poll_retx in section ul_am"); - return -1; + return SRSRAN_ERROR; } field_asn1_enum_number poll_pdu("poll_pdu", &am_rlc->poll_pdu); if (poll_pdu.parse(q["rlc_config"]["ul_am"])) { ERROR("Error can't find poll_pdu in section ul_am"); - return -1; + return SRSRAN_ERROR; } field_asn1_enum_number poll_byte("poll_byte", &am_rlc->poll_byte); if (poll_byte.parse(q["rlc_config"]["ul_am"])) { ERROR("Error can't find poll_byte in section ul_am"); - return -1; + return SRSRAN_ERROR; } field_asn1_enum_number max_retx_thresh("max_retx_thresh", &am_rlc->max_retx_thres); if (max_retx_thresh.parse(q["rlc_config"]["ul_am"])) { ERROR("Error can't find max_retx_thresh in section ul_am"); - return -1; + return SRSRAN_ERROR; } } @@ -523,20 +589,20 @@ int field_qci::parse(libconfig::Setting& root) field_asn1_enum_number t_reordering("t_reordering", &am_rlc->t_reordering); if (t_reordering.parse(q["rlc_config"]["dl_am"])) { ERROR("Error can't find t_reordering in section dl_am"); - return -1; + return SRSRAN_ERROR; } field_asn1_enum_number t_status_prohibit("t_status_prohibit", &am_rlc->t_status_prohibit); if (t_status_prohibit.parse(q["rlc_config"]["dl_am"])) { ERROR("Error can't find t_status_prohibit in section dl_am"); - return -1; + return SRSRAN_ERROR; } } // Parse logical channel configuration section if (!q.exists("logical_channel_config")) { fprintf(stderr, "Error section logical_channel_config not found for qci=%d\n", qci); - return -1; + return SRSRAN_ERROR; } lc_ch_cfg_s::ul_specific_params_s_* lc_cfg = &qcicfg.lc_cfg; @@ -544,26 +610,31 @@ int field_qci::parse(libconfig::Setting& root) parser::field priority("priority", &lc_cfg->prio); if (priority.parse(q["logical_channel_config"])) { ERROR("Error can't find logical_channel_config in section priority"); - return -1; + return SRSRAN_ERROR; } field_asn1_enum_number prioritised_bit_rate( "prioritized_bit_rate", &lc_cfg->prioritised_bit_rate); if (prioritised_bit_rate.parse(q["logical_channel_config"])) { fprintf(stderr, "Error can't find prioritized_bit_rate in section logical_channel_config\n"); - return -1; + return SRSRAN_ERROR; } field_asn1_enum_number bucket_size_duration( "bucket_size_duration", &lc_cfg->bucket_size_dur); if (bucket_size_duration.parse(q["logical_channel_config"])) { ERROR("Error can't find bucket_size_duration in section logical_channel_config"); - return -1; + return SRSRAN_ERROR; } parser::field log_chan_group("log_chan_group", &lc_cfg->lc_ch_group); lc_cfg->lc_ch_group_present = not log_chan_group.parse(q["logical_channel_config"]); qcicfg.configured = true; + + if (q.exists("enb_specific")) { + qcicfg.enb_dl_max_retx_thres = (int)q["enb_specific"]["dl_max_retx_thresh"]; + } + cfg.insert(std::make_pair(qci, qcicfg)); } @@ -674,6 +745,8 @@ int parse_rr(all_args_t* args_, rrc_cfg_t* rrc_cfg_) cqi_report_cnfg.add_field(new parser::field("period", &rrc_cfg_->cqi_cfg.period)); cqi_report_cnfg.add_field(new parser::field("m_ri", &rrc_cfg_->cqi_cfg.m_ri)); cqi_report_cnfg.add_field(new parser::field("nof_prb", &rrc_cfg_->cqi_cfg.nof_prb)); + cqi_report_cnfg.add_field( + new parser::field("subband_k", &rrc_cfg_->cqi_cfg.subband_k, &rrc_cfg_->cqi_cfg.is_subband_enabled)); cqi_report_cnfg.add_field(new parser::field("simultaneousAckCQI", &rrc_cfg_->cqi_cfg.simultaneousAckCQI)); cqi_report_cnfg.add_field(new field_sf_mapping(rrc_cfg_->cqi_cfg.sf_mapping, &rrc_cfg_->cqi_cfg.nof_subframes, 1)); @@ -789,7 +862,7 @@ static int parse_cell_list(all_args_t* args, rrc_cfg_t* rrc_cfg, Setting& root) HANDLEPARSERCODE(parse_meas_cell_list(&cell_cfg.meas_cfg, cellroot["meas_cell_list"])); if (not cellroot.exists("meas_report_desc")) { ERROR("PARSER ERROR: \"ho_active\" is set to true, but field \"meas_report_desc\" doesn't exist.\n"); - return -1; + return SRSRAN_ERROR; } HANDLEPARSERCODE(parse_meas_report_desc(&cell_cfg.meas_cfg, cellroot["meas_report_desc"])); } @@ -815,13 +888,13 @@ static int parse_cell_list(all_args_t* args, rrc_cfg_t* rrc_cfg, Setting& root) // Check RF port is not repeated if (it->rf_port == it2->rf_port) { ERROR("Repeated RF port for multiple cells"); - return -1; + return SRSRAN_ERROR; } // Check cell ID is not repeated if (it->cell_id == it2->cell_id) { ERROR("Repeated Cell identifier"); - return -1; + return SRSRAN_ERROR; } } } @@ -1535,7 +1608,7 @@ int parse_sib9(std::string filename, sib_type9_s* data) } return 0; } else { - return -1; + return SRSRAN_ERROR; } } @@ -1581,19 +1654,19 @@ int parse_sibs(all_args_t* args_, rrc_cfg_t* rrc_cfg_, srsenb::phy_cfg_t* phy_co std::string mnc_str; if (not srsran::mnc_to_string(args_->stack.s1ap.mnc, &mnc_str)) { ERROR("The provided mnc=%d is not valid", args_->stack.s1ap.mnc); - return -1; + return SRSRAN_ERROR; } std::string mcc_str; if (not srsran::mcc_to_string(args_->stack.s1ap.mcc, &mcc_str)) { ERROR("The provided mnc=%d is not valid", args_->stack.s1ap.mcc); - return -1; + return SRSRAN_ERROR; } sib_type1_s::cell_access_related_info_s_* cell_access = &sib1->cell_access_related_info; cell_access->plmn_id_list.resize(1); srsran::plmn_id_t plmn; if (plmn.from_string(mcc_str + mnc_str) == SRSRAN_ERROR) { ERROR("Could not convert %s to a plmn_id", (mcc_str + mnc_str).c_str()); - return -1; + return SRSRAN_ERROR; } srsran::to_asn1(&cell_access->plmn_id_list[0].plmn_id, plmn); cell_access->plmn_id_list[0].cell_reserved_for_oper = plmn_id_info_s::cell_reserved_for_oper_e_::not_reserved; @@ -1619,7 +1692,7 @@ int parse_sibs(all_args_t* args_, rrc_cfg_t* rrc_cfg_, srsenb::phy_cfg_t* phy_co // verify SIB13 is available if (not sib_is_present(sib1->sched_info_list, sib_type_e::sib_type13_v920)) { fprintf(stderr, "SIB13 not present in sched_info.\n"); - return -1; + return SRSRAN_ERROR; } } @@ -1673,9 +1746,40 @@ namespace drb_sections { int parse_drb(all_args_t* args_, rrc_cfg_t* rrc_cfg_) { + parser::section srb1("srb1_config"); + bool srb1_present = false; + srb1.set_optional(&srb1_present); + + parser::section srb1_rlc_cfg("rlc_config"); + srb1.add_subsection(&srb1_rlc_cfg); + srb1_rlc_cfg.add_field(new field_srb(rrc_cfg_->srb1_cfg)); + + parser::section srb2("srb2_config"); + bool srb2_present = false; + srb2.set_optional(&srb2_present); + + parser::section srb2_rlc_cfg("rlc_config"); + srb2.add_subsection(&srb2_rlc_cfg); + srb2_rlc_cfg.add_field(new field_srb(rrc_cfg_->srb2_cfg)); + parser::section qci("qci_config"); qci.add_field(new field_qci(rrc_cfg_->qci_cfg)); - return parser::parse_section(args_->enb_files.drb_config, &qci); + + // Run parser with two sections + parser p(args_->enb_files.rb_config); + p.add_section(&srb1); + p.add_section(&srb2); + p.add_section(&qci); + + int ret = p.parse(); + if (not srb1_present) { + rrc_cfg_->srb1_cfg.rlc_cfg.set_default_value(); + } + if (not srb2_present) { + rrc_cfg_->srb2_cfg.rlc_cfg.set_default_value(); + } + + return ret; } } // namespace drb_sections diff --git a/srsenb/src/enb_cfg_parser.h b/srsenb/src/enb_cfg_parser.h index 963690d7b..3f410a134 100644 --- a/srsenb/src/enb_cfg_parser.h +++ b/srsenb/src/enb_cfg_parser.h @@ -155,6 +155,18 @@ private: uint32_t default_offset; }; +class field_srb final : public parser::field_itf +{ +public: + explicit field_srb(srb_cfg_t& cfg_) : cfg(cfg_) {} + const char* get_name() override { return "field_srb"; } + + int parse(Setting& root) override; + +private: + srb_cfg_t& cfg; +}; + class field_qci final : public parser::field_itf { public: diff --git a/srsenb/src/main.cc b/srsenb/src/main.cc index 20b6c1179..097490d14 100644 --- a/srsenb/src/main.cc +++ b/srsenb/src/main.cc @@ -54,7 +54,8 @@ namespace bpo = boost::program_options; /********************************************************************** * Program arguments processing ***********************************************************************/ -string config_file; +string config_file; +static bool stdout_ts_enable = false; void parse_args(all_args_t* args, int argc, char* argv[]) { @@ -92,7 +93,7 @@ void parse_args(all_args_t* args, int argc, char* argv[]) ("enb_files.sib_config", bpo::value(&args->enb_files.sib_config)->default_value("sib.conf"), "SIB configuration files") ("enb_files.rr_config", bpo::value(&args->enb_files.rr_config)->default_value("rr.conf"), "RR configuration files") - ("enb_files.drb_config", bpo::value(&args->enb_files.drb_config)->default_value("drb.conf"), "DRB configuration files") + ("enb_files.rb_config", bpo::value(&args->enb_files.rb_config)->default_value("rb.conf"), "SRB/DRB configuration files") ("rf.dl_earfcn", bpo::value(&args->enb.dl_earfcn)->default_value(0), "Force Downlink EARFCN for single cell") ("rf.srate", bpo::value(&args->rf.srate_hz)->default_value(0.0), "Force Tx and Rx sampling rate in Hz") @@ -171,6 +172,8 @@ void parse_args(all_args_t* args, int argc, char* argv[]) ("scheduler.init_ul_snr_value", bpo::value(&args->stack.mac.sched.init_ul_snr_value)->default_value(5), "Initial UL SNR value used for computing MCS in the first UL grant") ("scheduler.init_dl_cqi", bpo::value(&args->stack.mac.sched.init_dl_cqi)->default_value(5), "DL CQI value used before any CQI report is available to the eNB") ("scheduler.max_sib_coderate", bpo::value(&args->stack.mac.sched.max_sib_coderate)->default_value(0.8), "Upper bound on SIB and RAR grants coderate") + ("scheduler.pdcch_cqi_offset", bpo::value(&args->stack.mac.sched.pdcch_cqi_offset)->default_value(0), "CQI offset in derivation of PDCCH aggregation level") + /* Downlink Channel emulator section */ @@ -233,6 +236,7 @@ void parse_args(all_args_t* args, int argc, char* argv[]) ("expert.tracing_enable", bpo::value(&args->general.tracing_enable)->default_value(false), "Events tracing") ("expert.tracing_filename", bpo::value(&args->general.tracing_filename)->default_value("/tmp/enb_tracing.log"), "Tracing events filename") ("expert.tracing_buffcapacity", bpo::value(&args->general.tracing_buffcapacity)->default_value(1000000), "Tracing buffer capcity") + ("expert.stdout_ts_enable", bpo::value(&stdout_ts_enable)->default_value(false), "Prints once per second the timestamp into stdout") ("expert.rrc_inactivity_timer", bpo::value(&args->general.rrc_inactivity_timer)->default_value(30000), "Inactivity timer in ms.") ("expert.print_buffer_state", bpo::value(&args->general.print_buffer_state)->default_value(false), "Prints on the console the buffer state every 10 seconds") ("expert.eea_pref_list", bpo::value(&args->general.eea_pref_list)->default_value("EEA0, EEA2, EEA1"), "Ordered preference list for the selection of encryption algorithm (EEA) (default: EEA0, EEA2, EEA1).") @@ -246,6 +250,7 @@ void parse_args(all_args_t* args, int argc, char* argv[]) ("expert.extended_cp", bpo::value(&args->phy.extended_cp)->default_value(false), "Use extended cyclic prefix") ("expert.ts1_reloc_prep_timeout", bpo::value(&args->stack.s1ap.ts1_reloc_prep_timeout)->default_value(10000), "S1AP TS 36.413 TS1RelocPrep Expiry Timeout value in milliseconds") ("expert.ts1_reloc_overall_timeout", bpo::value(&args->stack.s1ap.ts1_reloc_overall_timeout)->default_value(10000), "S1AP TS 36.413 TS1RelocOverall Expiry Timeout value in milliseconds") + ("expert.rlf_min_ul_snr_estim", bpo::value(&args->stack.mac.rlf_min_ul_snr_estim)->default_value(-2), "SNR threshold in dB below which the eNB is notified with rlf ko.") // eMBMS section @@ -445,8 +450,8 @@ void parse_args(all_args_t* args, int argc, char* argv[]) exit(1); } - if (!config_exists(args->enb_files.drb_config, "drb.conf")) { - cout << "Failed to read DRB configuration file " << args->enb_files.drb_config << " - exiting" << endl; + if (!config_exists(args->enb_files.rb_config, "rb.conf")) { + cout << "Failed to read DRB configuration file " << args->enb_files.rb_config << " - exiting" << endl; exit(1); } @@ -629,7 +634,8 @@ int main(int argc, char* argv[]) enb->start_plot(); } } - int cnt = 0; + int cnt = 0; + int ts_cnt = 0; while (running) { if (args.general.print_buffer_state) { cnt++; @@ -638,6 +644,16 @@ int main(int argc, char* argv[]) enb->print_pool(); } } + if (stdout_ts_enable) { + if (++ts_cnt == 100) { + ts_cnt = 0; + char buff[64]; + std::time_t t = std::time(nullptr); + if (std::strftime(buff, sizeof(buff), "%FT%T", std::gmtime(&t))) { + std::cout << buff << '\n'; + } + } + } std::this_thread::sleep_for(std::chrono::milliseconds(10)); } input.join(); diff --git a/srsenb/src/phy/CMakeLists.txt b/srsenb/src/phy/CMakeLists.txt index 3e5f9fa0e..a0fa5d026 100644 --- a/srsenb/src/phy/CMakeLists.txt +++ b/srsenb/src/phy/CMakeLists.txt @@ -31,8 +31,6 @@ set(SOURCES txrx.cc) add_library(srsenb_phy STATIC ${SOURCES}) -add_library(srsgnb_phy STATIC vnf_phy_nr.cc) - if (ENABLE_GUI AND SRSGUI_FOUND) target_link_libraries(srsenb_phy ${SRSGUI_LIBRARIES}) endif () diff --git a/srsenb/src/phy/nr/slot_worker.cc b/srsenb/src/phy/nr/slot_worker.cc index e35464f62..026cd00a7 100644 --- a/srsenb/src/phy/nr/slot_worker.cc +++ b/srsenb/src/phy/nr/slot_worker.cc @@ -35,11 +35,12 @@ slot_worker::slot_worker(srsran::phy_common_interface& common_, bool slot_worker::init(const args_t& args) { // Calculate subframe length - sf_len = SRSRAN_SF_LEN_PRB_NR(args.carrier.nof_prb); + sf_len = SRSRAN_SF_LEN_PRB_NR(args.nof_max_prb); // Copy common configurations cell_index = args.cell_index; - pdcch_cfg = args.pdcch_cfg; + // FIXME: + // pdcch_cfg = args.pdcch_cfg; // Allocate Tx buffers tx_buffer.resize(args.nof_tx_ports); @@ -64,10 +65,10 @@ bool slot_worker::init(const args_t& args) // Prepare DL arguments srsran_gnb_dl_args_t dl_args = {}; dl_args.pdsch.measure_time = true; - dl_args.pdsch.max_layers = args.carrier.max_mimo_layers; - dl_args.pdsch.max_prb = args.carrier.nof_prb; + dl_args.pdsch.max_layers = args.nof_tx_ports; + dl_args.pdsch.max_prb = args.nof_max_prb; dl_args.nof_tx_antennas = args.nof_tx_ports; - dl_args.nof_max_prb = args.carrier.nof_prb; + dl_args.nof_max_prb = args.nof_max_prb; // Initialise DL if (srsran_gnb_dl_init(&gnb_dl, tx_buffer.data(), &dl_args) < SRSRAN_SUCCESS) { @@ -75,18 +76,13 @@ bool slot_worker::init(const args_t& args) return false; } - // Set gNb DL carrier - if (srsran_gnb_dl_set_carrier(&gnb_dl, &args.carrier) < SRSRAN_SUCCESS) { - logger.error("Error setting DL carrier"); - return false; - } // Prepare UL arguments srsran_gnb_ul_args_t ul_args = {}; ul_args.pusch.measure_time = true; - ul_args.pusch.max_layers = args.carrier.max_mimo_layers; - ul_args.pusch.max_prb = args.carrier.nof_prb; - ul_args.nof_max_prb = args.carrier.nof_prb; + ul_args.pusch.max_layers = args.nof_rx_ports; + ul_args.pusch.max_prb = args.nof_max_prb; + ul_args.nof_max_prb = args.nof_max_prb; // Initialise UL if (srsran_gnb_ul_init(&gnb_ul, rx_buffer[0], &ul_args) < SRSRAN_SUCCESS) { @@ -94,12 +90,6 @@ bool slot_worker::init(const args_t& args) return false; } - // Set gNb UL carrier - if (srsran_gnb_ul_set_carrier(&gnb_ul, &args.carrier) < SRSRAN_SUCCESS) { - logger.error("Error setting UL carrier"); - return false; - } - return true; } @@ -166,7 +156,7 @@ bool slot_worker::work_ul() return false; } - // Decode PUCCH + // For each PUCCH... for (stack_interface_phy_nr::pucch_t& pucch : ul_sched.pucch) { stack_interface_phy_nr::pucch_info_t pucch_info = {}; pucch_info.uci_data.cfg = pucch.uci_cfg; @@ -197,7 +187,7 @@ bool slot_worker::work_ul() } } - // Decode PUSCH + // For each PUSCH... for (stack_interface_phy_nr::pusch_t& pusch : ul_sched.pusch) { // Get payload PDU stack_interface_phy_nr::pusch_info_t pusch_info = {}; @@ -206,7 +196,7 @@ bool slot_worker::work_ul() pusch_info.pusch_data.tb[0].payload = pusch.data[0]; pusch_info.pusch_data.tb[1].payload = pusch.data[1]; - // Decode PUCCH + // Decode PUSCH if (srsran_gnb_ul_get_pusch(&gnb_ul, &ul_slot_cfg, &pusch.sch, &pusch.sch.grant, &pusch_info.pusch_data) < SRSRAN_SUCCESS) { logger.error("Error getting PUSCH"); @@ -349,6 +339,27 @@ void slot_worker::work_imp() common.worker_end(this, true, tx_rf_buffer, tx_time, true); } +bool slot_worker::set_common_cfg(const srsran_carrier_nr_t& carrier, const srsran_pdcch_cfg_nr_t& pdcch_cfg_) +{ + // Set gNb DL carrier + if (srsran_gnb_dl_set_carrier(&gnb_dl, &carrier) < SRSRAN_SUCCESS) { + logger.error("Error setting DL carrier"); + return false; + } + + // Set gNb UL carrier + if (srsran_gnb_ul_set_carrier(&gnb_ul, &carrier) < SRSRAN_SUCCESS) { + logger.error("Error setting UL carrier"); + return false; + } + + pdcch_cfg = pdcch_cfg_; + + // Update subframe length + sf_len = SRSRAN_SF_LEN_PRB_NR(carrier.nof_prb); + + return true; +} } // namespace nr -} // namespace srsenb \ No newline at end of file +} // namespace srsenb diff --git a/srsenb/src/phy/nr/worker_pool.cc b/srsenb/src/phy/nr/worker_pool.cc index c8c891d6d..afdac4e72 100644 --- a/srsenb/src/phy/nr/worker_pool.cc +++ b/srsenb/src/phy/nr/worker_pool.cc @@ -27,19 +27,27 @@ worker_pool::worker_pool(srsran::phy_common_interface& common_, stack_interface_phy_nr& stack_, srslog::sink& log_sink_, uint32_t max_workers) : - pool(max_workers), common(common_), stack(stack_), log_sink(log_sink_) + pool(max_workers), + common(common_), + stack(stack_), + log_sink(log_sink_), + logger(srslog::fetch_basic_logger("PHY-NR", log_sink)), + prach_stack_adaptor(stack_) { // Do nothing } bool worker_pool::init(const args_t& args, const phy_cell_cfg_list_nr_t& cell_list) { + // Configure logger + srslog::basic_levels log_level = srslog::str_to_basic_level(args.log.phy_level); + logger.set_level(log_level); + // Add workers to workers pool and start threads - srslog::basic_levels log_level = srslog::str_to_basic_level(args.log_level); for (uint32_t i = 0; i < args.nof_phy_threads; i++) { - auto& log = srslog::fetch_basic_logger(fmt::format("{}PHY{}-NR", args.log_id_preamble, i), log_sink); + auto& log = srslog::fetch_basic_logger(fmt::format("{}PHY{}-NR", args.log.id_preamble, i), log_sink); log.set_level(log_level); - log.set_hex_dump_max_size(args.log_hex_limit); + log.set_hex_dump_max_size(args.log.phy_hex_limit); auto w = new slot_worker(common, stack, log); pool.init_worker(i, w, args.prio); @@ -48,11 +56,10 @@ bool worker_pool::init(const args_t& args, const phy_cell_cfg_list_nr_t& cell_li slot_worker::args_t w_args = {}; uint32_t cell_index = 0; w_args.cell_index = cell_index; - w_args.carrier = cell_list[cell_index].carrier; + w_args.nof_max_prb = cell_list[cell_index].carrier.nof_prb; w_args.nof_tx_ports = cell_list[cell_index].carrier.max_mimo_layers; w_args.nof_rx_ports = cell_list[cell_index].carrier.max_mimo_layers; w_args.pusch_max_nof_iter = args.pusch_max_nof_iter; - w_args.pdcch_cfg = cell_list[cell_index].pdcch; if (not w->init(w_args)) { return false; @@ -64,12 +71,41 @@ bool worker_pool::init(const args_t& args, const phy_cell_cfg_list_nr_t& cell_li void worker_pool::start_worker(slot_worker* w) { + // Feed PRACH detection before start processing + prach.new_tti(0, current_tti, w->get_buffer_rx(0)); + + // Start actual worker pool.start_worker(w); } slot_worker* worker_pool::wait_worker(uint32_t tti) { - return (slot_worker*)pool.wait_worker(tti); + slot_worker* w = (slot_worker*)pool.wait_worker(tti); + + // Only if a worker was available + if (w != nullptr) { + srsran_carrier_nr_t carrier_; + srsran_pdcch_cfg_nr_t pdcch_cfg_; + + // Copy configuration + { + std::unique_lock lock(common_cfg_mutex); + carrier_ = carrier; + pdcch_cfg_ = pdcch_cfg; + } + + // Set worker configuration + if (not w->set_common_cfg(carrier_, pdcch_cfg_)) { + logger.error("Error setting common config"); + return nullptr; + } + } + + // Save current TTI + current_tti = tti; + + // Return worker + return w; } slot_worker* worker_pool::wait_worker_id(uint32_t id) @@ -80,7 +116,42 @@ slot_worker* worker_pool::wait_worker_id(uint32_t id) void worker_pool::stop() { pool.stop(); + prach.stop(); +} + +int worker_pool::set_common_cfg(const phy_interface_rrc_nr::common_cfg_t& common_cfg) +{ + // Best effort to convert NR carrier into LTE cell + srsran_cell_t cell = {}; + int ret = srsran_carrier_to_cell(&common_cfg.carrier, &cell); + if (ret < SRSRAN_SUCCESS) { + logger.error("Converting carrier to cell for PRACH (%d)", ret); + return SRSRAN_ERROR; + } + + // Best effort to set up NR-PRACH config reused for NR + srsran_prach_cfg_t prach_cfg = common_cfg.prach; + uint32_t lte_nr_prach_offset = (common_cfg.carrier.nof_prb - cell.nof_prb) / 2; + if (prach_cfg.freq_offset < lte_nr_prach_offset) { + logger.error("prach_cfg.freq_offset=%d is not compatible with LTE", prach_cfg.freq_offset); + return SRSRAN_ERROR; + } + prach_cfg.freq_offset -= lte_nr_prach_offset; + prach_cfg.is_nr = true; + + // Set the PRACH configuration + prach.init(0, cell, prach_cfg, &prach_stack_adaptor, logger, 0, 1); + prach.set_max_prach_offset_us(1000); + + // Save current configuration + { + std::unique_lock lock(common_cfg_mutex); + carrier = common_cfg.carrier; + pdcch_cfg = common_cfg.pdcch; + } + + return SRSRAN_SUCCESS; } } // namespace nr -} // namespace srsenb \ No newline at end of file +} // namespace srsenb diff --git a/srsenb/src/phy/vnf_phy_nr.cc b/srsenb/src/phy/vnf_phy_nr.cc deleted file mode 100644 index 42ac6dc37..000000000 --- a/srsenb/src/phy/vnf_phy_nr.cc +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Copyright 2013-2021 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 -#include - -#include "srsenb/hdr/phy/vnf_phy_nr.h" -#include "srsran/common/basic_vnf_api.h" - -using namespace std; - -namespace srsenb { - -vnf_phy_nr::~vnf_phy_nr() -{ - stop(); -} - -void vnf_phy_nr::parse_config(const nr_phy_cfg_t& cfg) {} - -int vnf_phy_nr::init(const srsenb::phy_args_t& args, const nr_phy_cfg_t& cfg, srsenb::stack_interface_phy_nr* stack_) -{ - mlockall(MCL_CURRENT | MCL_FUTURE); - - // create VNF - vnf = std::unique_ptr(new srsran::srsran_basic_vnf(args.vnf_args, stack_)); - - initialized = true; - - return SRSRAN_SUCCESS; -} - -void vnf_phy_nr::stop() -{ - if (initialized) { - vnf->stop(); - initialized = false; - } -} - -// Start GUI -void vnf_phy_nr::start_plot() {} - -void vnf_phy_nr::get_metrics(std::vector& metrics) {} - -int vnf_phy_nr::dl_config_request(const dl_config_request_t& request) -{ - // prepare DL config request over basic API and send - return vnf->dl_config_request(request); -} - -int vnf_phy_nr::tx_request(const tx_request_t& request) -{ - // send Tx request over basic API - return vnf->tx_request(request); -} - -} // namespace srsenb diff --git a/srsenb/src/stack/gnb_stack_nr.cc b/srsenb/src/stack/gnb_stack_nr.cc index 7306135d7..b348bbc42 100644 --- a/srsenb/src/stack/gnb_stack_nr.cc +++ b/srsenb/src/stack/gnb_stack_nr.cc @@ -207,4 +207,6 @@ int gnb_stack_nr::pusch_info(const srsran_slot_cfg_t& slot_cfg, const mac_interf return m_mac->pusch_info(slot_cfg, pusch_info); } +void gnb_stack_nr::rach_detected(const rach_info_t& rach_info) {} + } // namespace srsenb diff --git a/srsenb/src/stack/mac/mac.cc b/srsenb/src/stack/mac/mac.cc index 77badc48f..00974df04 100644 --- a/srsenb/src/stack/mac/mac.cc +++ b/srsenb/src/stack/mac/mac.cc @@ -410,7 +410,7 @@ int mac::snr_info(uint32_t tti_rx, uint16_t rnti, uint32_t enb_cc_idx, float snr return SRSRAN_ERROR; } - rrc_h->set_radiolink_ul_state(rnti, snr > 0); + rrc_h->set_radiolink_ul_state(rnti, snr >= args.rlf_min_ul_snr_estim); return scheduler.ul_snr_info(tti_rx, rnti, enb_cc_idx, snr, (uint32_t)ch); } diff --git a/srsenb/src/stack/mac/nr/CMakeLists.txt b/srsenb/src/stack/mac/nr/CMakeLists.txt index 45d05a6b2..310691697 100644 --- a/srsenb/src/stack/mac/nr/CMakeLists.txt +++ b/srsenb/src/stack/mac/nr/CMakeLists.txt @@ -18,6 +18,7 @@ # and at http://www.gnu.org/licenses/. # -set(SOURCES mac_nr.cc sched_nr.cc sched_nr_ue.cc sched_nr_worker.cc sched_nr_rb_grid.cc sched_nr_harq.cc sched_nr_pdcch.cc sched_nr_common.cc sched_nr_phy_helpers.cc) +set(SOURCES mac_nr.cc sched_nr.cc sched_nr_ue.cc sched_nr_worker.cc sched_nr_rb_grid.cc sched_nr_harq.cc + sched_nr_pdcch.cc sched_nr_cfg.cc sched_nr_helpers.cc sched_nr_bwp.cc sched_nr_rb.cc harq_softbuffer.cc) add_library(srsgnb_mac STATIC ${SOURCES}) diff --git a/srsenb/src/stack/mac/nr/harq_softbuffer.cc b/srsenb/src/stack/mac/nr/harq_softbuffer.cc new file mode 100644 index 000000000..b60465d8f --- /dev/null +++ b/srsenb/src/stack/mac/nr/harq_softbuffer.cc @@ -0,0 +1,62 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#include "srsenb/hdr/stack/mac/nr/harq_softbuffer.h" +#include "srsran/adt/pool/obj_pool.h" + +namespace srsenb { + +void harq_softbuffer_pool::init_pool(uint32_t nof_prb, uint32_t batch_size, uint32_t thres, uint32_t init_size) +{ + srsran_assert(nof_prb <= SRSRAN_MAX_PRB_NR, "Invalid nof prb=%d", nof_prb); + size_t idx = nof_prb - 1; + if (tx_pool[idx] != nullptr) { + return; + } + if (thres == 0) { + thres = batch_size; + } + if (init_size == 0) { + init_size = batch_size; + } + auto init_tx_softbuffers = [nof_prb](void* ptr) { new (ptr) tx_harq_softbuffer(nof_prb); }; + auto recycle_tx_softbuffers = [](tx_harq_softbuffer& softbuffer) { softbuffer.reset(); }; + tx_pool[idx].reset(new srsran::background_obj_pool( + batch_size, thres, init_size, init_tx_softbuffers, recycle_tx_softbuffers)); + + auto init_rx_softbuffers = [nof_prb](void* ptr) { new (ptr) rx_harq_softbuffer(nof_prb); }; + auto recycle_rx_softbuffers = [](rx_harq_softbuffer& softbuffer) { softbuffer.reset(); }; + rx_pool[idx].reset(new srsran::background_obj_pool( + batch_size, thres, init_size, init_rx_softbuffers, recycle_rx_softbuffers)); +} + +srsran::unique_pool_ptr harq_softbuffer_pool::get_tx(uint32_t nof_prb) +{ + srsran_assert(nof_prb <= SRSRAN_MAX_PRB_NR, "Invalid Nprb=%d", nof_prb); + size_t idx = nof_prb - 1; + if (tx_pool[idx] == nullptr) { + init_pool(nof_prb); + } + return tx_pool[idx]->make(); +} + +srsran::unique_pool_ptr harq_softbuffer_pool::get_rx(uint32_t nof_prb) +{ + srsran_assert(nof_prb <= SRSRAN_MAX_PRB_NR, "Invalid Nprb=%d", nof_prb); + size_t idx = nof_prb - 1; + if (rx_pool[idx] == nullptr) { + init_pool(nof_prb); + } + return rx_pool[idx]->make(); +} + +} // namespace srsenb diff --git a/srsenb/src/stack/mac/nr/mac_nr.cc b/srsenb/src/stack/mac/nr/mac_nr.cc index 3b862f7fb..9fe88fafc 100644 --- a/srsenb/src/stack/mac/nr/mac_nr.cc +++ b/srsenb/src/stack/mac/nr/mac_nr.cc @@ -98,114 +98,6 @@ void mac_nr::stop() void mac_nr::get_metrics(srsenb::mac_metrics_t& metrics) {} -// Fills both, DL_CONFIG.request and TX.request structs -void mac_nr::get_dl_config(const uint32_t tti, - phy_interface_stack_nr::dl_config_request_t& config_request, - phy_interface_stack_nr::tx_request_t& tx_request) -{ - // send MIB over BCH every 80ms - if (tti % 80 == 0) { - // try to read BCH PDU from RRC - if (rrc_h->read_pdu_bcch_bch(tti, bcch_bch_payload) == SRSRAN_SUCCESS) { - logger.info("Adding BCH in TTI=%d", tti); - tx_request.pdus[tx_request.nof_pdus].pbch.mib_present = true; - tx_request.pdus[tx_request.nof_pdus].data[0] = bcch_bch_payload->msg; - tx_request.pdus[tx_request.nof_pdus].length = bcch_bch_payload->N_bytes; - tx_request.pdus[tx_request.nof_pdus].index = tx_request.nof_pdus; - tx_request.nof_pdus++; - - if (pcap) { - pcap->write_dl_bch(bcch_bch_payload->msg, bcch_bch_payload->N_bytes, 0xffff, 0, tti); - } - } else { - logger.error("Couldn't read BCH payload from RRC"); - } - } - - // Schedule SIBs - for (auto& sib : bcch_dlsch_payload) { - if (sib.payload->N_bytes > 0) { - if (tti % (sib.periodicity * 10) == 0) { - logger.info("Adding SIB %d in TTI=%d", sib.index, tti); - - tx_request.pdus[tx_request.nof_pdus].data[0] = sib.payload->msg; - tx_request.pdus[tx_request.nof_pdus].length = sib.payload->N_bytes; - tx_request.pdus[tx_request.nof_pdus].index = tx_request.nof_pdus; - - if (pcap) { - pcap->write_dl_si_rnti_nr(sib.payload->msg, sib.payload->N_bytes, 0xffff, 0, tti); - } - - tx_request.nof_pdus++; - } - } - } - - // Add MAC padding if TTI is empty - if (tx_request.nof_pdus == 0) { - uint32_t buffer_index = tti % SRSRAN_FDD_NOF_HARQ; - - ue_tx_buffer.at(buffer_index)->clear(); - ue_tx_pdu.init_tx(ue_tx_buffer.at(buffer_index).get(), args.tb_size); - - // read RLC PDU - ue_rlc_buffer->clear(); - int pdu_len = rlc_h->read_pdu(args.rnti, 4, ue_rlc_buffer->msg, args.tb_size - 2); - - // Only create PDU if RLC has something to tx - if (pdu_len > 0) { - logger.info("Adding MAC PDU for RNTI=%d", args.rnti); - ue_rlc_buffer->N_bytes = pdu_len; - logger.info(ue_rlc_buffer->msg, ue_rlc_buffer->N_bytes, "Read %d B from RLC", ue_rlc_buffer->N_bytes); - - // add to MAC PDU and pack - ue_tx_pdu.add_sdu(4, ue_rlc_buffer->msg, ue_rlc_buffer->N_bytes); - ue_tx_pdu.pack(); - - logger.debug(ue_tx_buffer.at(buffer_index)->msg, - ue_tx_buffer.at(buffer_index)->N_bytes, - "Generated MAC PDU (%d B)", - ue_tx_buffer.at(buffer_index)->N_bytes); - - tx_request.pdus[tx_request.nof_pdus].data[0] = ue_tx_buffer.at(buffer_index)->msg; - tx_request.pdus[tx_request.nof_pdus].length = ue_tx_buffer.at(buffer_index)->N_bytes; - tx_request.pdus[tx_request.nof_pdus].index = tx_request.nof_pdus; - - if (pcap) { - pcap->write_dl_crnti_nr(tx_request.pdus[tx_request.nof_pdus].data[0], - tx_request.pdus[tx_request.nof_pdus].length, - args.rnti, - buffer_index, - tti); - } - - tx_request.nof_pdus++; - } - } - - config_request.tti = tti; - tx_request.tti = tti; -} - -int mac_nr::slot_indication(const srsran_slot_cfg_t& slot_cfg) -{ - phy_interface_stack_nr::dl_config_request_t config_request = {}; - phy_interface_stack_nr::tx_request_t tx_request = {}; - - // step MAC TTI - logger.set_context(slot_cfg.idx); - - get_dl_config(slot_cfg.idx, config_request, tx_request); - - // send DL_CONFIG.request - phy_h->dl_config_request(config_request); - - // send TX.request - phy_h->tx_request(tx_request); - - return SRSRAN_SUCCESS; -} - int mac_nr::rx_data_indication(stack_interface_phy_nr::rx_data_ind_t& rx_data) { // push received PDU on queue @@ -279,6 +171,11 @@ int mac_nr::cell_cfg(srsenb::sched_interface::cell_cfg_t* cell_cfg) return SRSRAN_SUCCESS; } +int mac_nr::slot_indication(const srsran_slot_cfg_t& slot_cfg) +{ + return 0; +} + int mac_nr::get_dl_sched(const srsran_slot_cfg_t& slot_cfg, dl_sched_t& dl_sched) { return 0; @@ -295,5 +192,6 @@ int mac_nr::pusch_info(const srsran_slot_cfg_t& slot_cfg, const mac_interface_ph { return 0; } +void mac_nr::rach_detected(const mac_interface_phy_nr::rach_info_t& rach_info) {} } // namespace srsenb diff --git a/srsenb/src/stack/mac/nr/sched_nr.cc b/srsenb/src/stack/mac/nr/sched_nr.cc index 5ad2de8ac..1a61f00a7 100644 --- a/srsenb/src/stack/mac/nr/sched_nr.cc +++ b/srsenb/src/stack/mac/nr/sched_nr.cc @@ -20,15 +20,14 @@ */ #include "srsenb/hdr/stack/mac/nr/sched_nr.h" +#include "srsenb/hdr/stack/mac/nr/harq_softbuffer.h" +#include "srsenb/hdr/stack/mac/nr/sched_nr_bwp.h" #include "srsenb/hdr/stack/mac/nr/sched_nr_worker.h" #include "srsran/common/thread_pool.h" namespace srsenb { -using sched_nr_impl::sched_worker_manager; -using sched_nr_impl::ue; -using sched_nr_impl::ue_carrier; -using sched_nr_impl::ue_map_t; +using namespace sched_nr_impl; static int assert_ue_cfg_valid(uint16_t rnti, const sched_nr_interface::ue_cfg_t& uecfg); @@ -53,7 +52,7 @@ public: feedback_list.back().cc = cc; feedback_list.back().callback = std::move(event); } - void new_tti() + void new_slot() { { std::lock_guard lock(common_mutex); @@ -90,7 +89,71 @@ private: /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -sched_nr::sched_nr(const sched_cfg_t& sched_cfg) : cfg(sched_cfg), pending_events(new ue_event_manager(ue_db)) {} +class sched_result_manager +{ +public: + explicit sched_result_manager(uint32_t nof_cc_) + { + for (auto& v : results) { + v.resize(nof_cc_); + } + } + + dl_sched_t& add_dl_result(tti_point tti, uint32_t cc) + { + if (not has_dl_result(tti, cc)) { + results[tti.to_uint()][cc].tti_dl = tti; + results[tti.to_uint()][cc].dl_res = {}; + } + return results[tti.to_uint()][cc].dl_res; + } + ul_sched_t& add_ul_result(tti_point tti, uint32_t cc) + { + if (not has_ul_result(tti, cc)) { + results[tti.to_uint()][cc].tti_ul = tti; + results[tti.to_uint()][cc].ul_res = {}; + } + return results[tti.to_uint()][cc].ul_res; + } + + bool has_dl_result(tti_point tti, uint32_t cc) const { return results[tti.to_uint()][cc].tti_dl == tti; } + + bool has_ul_result(tti_point tti, uint32_t cc) const { return results[tti.to_uint()][cc].tti_ul == tti; } + + dl_sched_t pop_dl_result(tti_point tti, uint32_t cc) + { + if (has_dl_result(tti, cc)) { + results[tti.to_uint()][cc].tti_dl.reset(); + return results[tti.to_uint()][cc].dl_res; + } + return {}; + } + + ul_sched_t pop_ul_result(tti_point tti, uint32_t cc) + { + if (has_ul_result(tti, cc)) { + results[tti.to_uint()][cc].tti_ul.reset(); + return results[tti.to_uint()][cc].ul_res; + } + return {}; + } + +private: + struct slot_result_t { + tti_point tti_dl; + tti_point tti_ul; + dl_sched_t dl_res; + ul_sched_t ul_res; + }; + + srsran::circular_array, TTIMOD_SZ> results; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +sched_nr::sched_nr(const sched_cfg_t& sched_cfg) : + cfg(sched_cfg), pending_events(new ue_event_manager(ue_db)), logger(srslog::fetch_basic_logger("MAC")) +{} sched_nr::~sched_nr() {} @@ -101,7 +164,9 @@ int sched_nr::cell_cfg(srsran::const_span cell_list) cfg.cells.emplace_back(cc, cell_list[cc], cfg.sched_cfg); } + pending_results.reset(new sched_result_manager(cell_list.size())); sched_workers.reset(new sched_nr_impl::sched_worker_manager(ue_db, cfg)); + return SRSRAN_SUCCESS; } @@ -114,39 +179,56 @@ void sched_nr::ue_cfg(uint16_t rnti, const ue_cfg_t& uecfg) void sched_nr::ue_cfg_impl(uint16_t rnti, const ue_cfg_t& uecfg) { if (not ue_db.contains(rnti)) { - ue_db.insert(rnti, std::unique_ptr(new ue{rnti, uecfg})); + ue_db.insert(rnti, std::unique_ptr(new ue{rnti, uecfg, cfg})); } else { ue_db[rnti]->set_cfg(uecfg); } } -void sched_nr::slot_indication(tti_point tti_rx) -{ - // Lock slot workers for provided tti_rx - sched_workers->reserve_workers(tti_rx); - - { - // synchronize {tti,cc} state. e.g. reserve UE resources for {tti,cc} decision, process feedback - std::lock_guard lock(ue_db_mutex); - // Process pending events - pending_events->new_tti(); - - sched_workers->start_tti(tti_rx); - } -} - /// Generate {tti,cc} scheduling decision -int sched_nr::generate_sched_result(tti_point tti_rx, uint32_t cc, tti_request_t& req) +int sched_nr::generate_slot_result(tti_point pdcch_tti, uint32_t cc) { + tti_point tti_rx = pdcch_tti - TX_ENB_DELAY; + + // Lock carrier workers for provided tti_rx + sched_workers->start_slot(tti_rx, [this]() { + // In case it is first worker for the given slot + // synchronize {tti,cc} state. e.g. reserve UE resources for {tti,cc} decision, process feedback + pending_events->new_slot(); + }); + // unlocked, parallel region - bool all_workers_finished = sched_workers->run_tti(tti_rx, cc, req); + bool all_workers_finished = sched_workers->run_slot(tti_rx, cc); if (all_workers_finished) { // once all workers of the same subframe finished, synchronize sched outcome with ue_db - std::lock_guard lock(ue_db_mutex); - sched_workers->end_tti(tti_rx); + sched_workers->release_slot(tti_rx); } + // Copy results to intermediate buffer + dl_sched_t& dl_res = pending_results->add_dl_result(pdcch_tti, cc); + ul_sched_t& ul_res = pending_results->add_ul_result(pdcch_tti, cc); + sched_workers->save_sched_result(pdcch_tti, cc, dl_res, ul_res); + + return SRSRAN_SUCCESS; +} + +int sched_nr::get_dl_sched(tti_point tti_tx, uint32_t cc, dl_sched_t& result) +{ + if (not pending_results->has_dl_result(tti_tx, cc)) { + generate_slot_result(tti_tx, cc); + } + + result = pending_results->pop_dl_result(tti_tx, cc); + return SRSRAN_SUCCESS; +} +int sched_nr::get_ul_sched(tti_point tti_rx, uint32_t cc, ul_sched_t& result) +{ + if (not pending_results->has_ul_result(tti_rx, cc)) { + return SRSRAN_ERROR; + } + + result = pending_results->pop_ul_result(tti_rx, cc); return SRSRAN_SUCCESS; } @@ -165,15 +247,21 @@ void sched_nr::ul_sr_info(tti_point tti_rx, uint16_t rnti) }); } +#define VERIFY_INPUT(cond, msg, ...) \ + do { \ + if (not(cond)) { \ + srslog::fetch_basic_logger("MAC").warning(msg, ##__VA_ARGS__); \ + return SRSRAN_ERROR; \ + } \ + } while (0) + int assert_ue_cfg_valid(uint16_t rnti, const sched_nr_interface::ue_cfg_t& uecfg) { - const srslog::basic_logger& logger = srslog::fetch_basic_logger("MAC"); - if (std::count(&uecfg.phy_cfg.pdcch.coreset_present[0], - &uecfg.phy_cfg.pdcch.coreset_present[SRSRAN_UE_DL_NR_MAX_NOF_CORESET], - true) == 0) { - logger.warning("Provided rnti=0x%x configuration does not contain any coreset", rnti); - return SRSRAN_ERROR; - } + VERIFY_INPUT(std::count(&uecfg.phy_cfg.pdcch.coreset_present[0], + &uecfg.phy_cfg.pdcch.coreset_present[SRSRAN_UE_DL_NR_MAX_NOF_CORESET], + true) > 0, + "Provided rnti=0x%x configuration does not contain any coreset", + rnti); return SRSRAN_SUCCESS; } diff --git a/srsenb/src/stack/mac/nr/sched_nr_bwp.cc b/srsenb/src/stack/mac/nr/sched_nr_bwp.cc new file mode 100644 index 000000000..03f7c314b --- /dev/null +++ b/srsenb/src/stack/mac/nr/sched_nr_bwp.cc @@ -0,0 +1,161 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#include "srsenb/hdr/stack/mac/nr/sched_nr_bwp.h" +#include "srsran/common/standard_streams.h" +#include "srsran/common/string_helpers.h" + +namespace srsenb { +namespace sched_nr_impl { + +ra_sched::ra_sched(const bwp_params& bwp_cfg_) : bwp_cfg(&bwp_cfg_), logger(srslog::fetch_basic_logger("MAC")) {} + +alloc_result +ra_sched::allocate_pending_rar(bwp_slot_allocator& slot_grid, const pending_rar_t& rar, uint32_t& nof_grants_alloc) +{ + const uint32_t rar_aggr_level = 2; + const prb_bitmap& prbs = slot_grid.res_grid()[slot_grid.get_pdcch_tti()].dl_prbs.prbs(); + + alloc_result ret = alloc_result::other_cause; + for (nof_grants_alloc = rar.msg3_grant.size(); nof_grants_alloc > 0; nof_grants_alloc--) { + ret = alloc_result::invalid_coderate; + uint32_t start_prb_idx = 0; + for (uint32_t nprb = 1; nprb < bwp_cfg->cfg.rb_width and ret == alloc_result::invalid_coderate; ++nprb) { + prb_interval interv = find_empty_interval_of_length(prbs, nprb, start_prb_idx); + start_prb_idx = interv.stop(); + if (interv.length() == nprb) { + ret = slot_grid.alloc_rar(rar_aggr_level, rar, interv, nof_grants_alloc); + } else { + ret = alloc_result::no_sch_space; + } + } + + // If allocation was not successful because there were not enough RBGs, try allocating fewer Msg3 grants + if (ret != alloc_result::invalid_coderate and ret != alloc_result::no_sch_space) { + break; + } + } + if (ret != alloc_result::success) { + logger.info("SCHED: RAR allocation for L=%d was postponed. Cause=%s", rar_aggr_level, to_string(ret)); + } + return ret; +} + +void ra_sched::run_slot(bwp_slot_allocator& slot_grid) +{ + static const uint32_t PRACH_RAR_OFFSET = 3; + tti_point pdcch_tti = slot_grid.get_pdcch_tti(); + + for (auto it = pending_rars.begin(); it != pending_rars.end();) { + pending_rar_t& rar = *it; + + // In case of RAR outside RAR window: + // - if window has passed, discard RAR + // - if window hasn't started, stop loop, as RARs are ordered by TTI + tti_interval rar_window{rar.prach_tti + PRACH_RAR_OFFSET, + rar.prach_tti + PRACH_RAR_OFFSET + bwp_cfg->cfg.rar_window_size}; + if (not rar_window.contains(pdcch_tti)) { + if (pdcch_tti >= rar_window.stop()) { + fmt::memory_buffer str_buffer; + fmt::format_to(str_buffer, + "SCHED: Could not transmit RAR within the window (RA={}, Window={}, RAR={}", + rar.prach_tti, + rar_window, + pdcch_tti); + srsran::console("%s\n", srsran::to_c_str(str_buffer)); + logger.warning("%s", srsran::to_c_str(str_buffer)); + it = pending_rars.erase(it); + continue; + } + return; + } + + // Try to schedule DCI + RBGs for RAR Grant + uint32_t nof_rar_allocs = 0; + alloc_result ret = allocate_pending_rar(slot_grid, rar, nof_rar_allocs); + + if (ret == alloc_result::success) { + // If RAR allocation was successful: + // - in case all Msg3 grants were allocated, remove pending RAR, and continue with following RAR + // - otherwise, erase only Msg3 grants that were allocated, and stop iteration + + if (nof_rar_allocs == rar.msg3_grant.size()) { + it = pending_rars.erase(it); + } else { + std::copy(rar.msg3_grant.begin() + nof_rar_allocs, rar.msg3_grant.end(), rar.msg3_grant.begin()); + rar.msg3_grant.resize(rar.msg3_grant.size() - nof_rar_allocs); + break; + } + } else { + // If RAR allocation was not successful: + // - in case of unavailable PDCCH space, try next pending RAR allocation + // - otherwise, stop iteration + if (ret != alloc_result::no_cch_space) { + break; + } + ++it; + } + } +} + +int ra_sched::dl_rach_info(const dl_sched_rar_info_t& rar_info) +{ + logger.info("SCHED: New PRACH tti=%d, preamble=%d, temp_crnti=0x%x, ta_cmd=%d, msg3_size=%d", + rar_info.prach_tti, + rar_info.preamble_idx, + rar_info.temp_crnti, + rar_info.ta_cmd, + rar_info.msg3_size); + + // RA-RNTI = 1 + t_id + f_id + // t_id = index of first subframe specified by PRACH (0<=t_id<10) + // f_id = index of the PRACH within subframe, in ascending order of freq domain (0<=f_id<6) (for FDD, f_id=0) + uint16_t ra_rnti = 1 + (uint16_t)(rar_info.prach_tti % 10u); + + // find pending rar with same RA-RNTI + for (pending_rar_t& r : pending_rars) { + if (r.prach_tti.to_uint() == rar_info.prach_tti and ra_rnti == r.ra_rnti) { + if (r.msg3_grant.size() >= sched_interface::MAX_RAR_LIST) { + logger.warning("PRACH ignored, as the the maximum number of RAR grants per tti has been reached"); + return SRSRAN_ERROR; + } + r.msg3_grant.push_back(rar_info); + return SRSRAN_SUCCESS; + } + } + + // create new RAR + pending_rar_t p; + p.ra_rnti = ra_rnti; + p.prach_tti = tti_point{rar_info.prach_tti}; + p.msg3_grant.push_back(rar_info); + pending_rars.push_back(p); + + return SRSRAN_SUCCESS; +} + +bwp_ctxt::bwp_ctxt(const bwp_params& bwp_cfg) : cfg(&bwp_cfg), ra(bwp_cfg), grid(bwp_cfg) {} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +serv_cell_ctxt::serv_cell_ctxt(const sched_cell_params& cell_cfg_) : cfg(&cell_cfg_) +{ + for (uint32_t bwp_id = 0; bwp_id < cfg->cell_cfg.bwps.size(); ++bwp_id) { + bwps.emplace_back(cell_cfg_.bwps[bwp_id]); + } + + // Pre-allocate HARQs in common pool of softbuffers + harq_softbuffer_pool::get_instance().init_pool(cfg->nof_prb()); +} + +} // namespace sched_nr_impl +} // namespace srsenb diff --git a/srsenb/src/stack/mac/nr/sched_nr_cfg.cc b/srsenb/src/stack/mac/nr/sched_nr_cfg.cc new file mode 100644 index 000000000..89ad86a49 --- /dev/null +++ b/srsenb/src/stack/mac/nr/sched_nr_cfg.cc @@ -0,0 +1,110 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#include "srsenb/hdr/stack/mac/nr/sched_nr_cfg.h" +#include "srsenb/hdr/stack/mac/nr/sched_nr_helpers.h" + +namespace srsenb { +namespace sched_nr_impl { + +bwp_params::bwp_params(const cell_cfg_t& cell, const sched_cfg_t& sched_cfg_, uint32_t cc_, uint32_t bwp_id_) : + cell_cfg(cell), sched_cfg(sched_cfg_), cc(cc_), bwp_id(bwp_id_), cfg(cell.bwps[bwp_id_]) +{ + P = get_P(cfg.rb_width, cfg.pdsch.rbg_size_cfg_1); + N_rbg = get_nof_rbgs(cfg.rb_width, cfg.start_rb, cfg.pdsch.rbg_size_cfg_1); + + srsran_assert(bwp_id != 0 or cfg.pdcch.coreset_present[0], "CORESET#0 has to be active for initial BWP"); +} + +sched_cell_params::sched_cell_params(uint32_t cc_, const cell_cfg_t& cell, const sched_cfg_t& sched_cfg_) : + cc(cc_), cell_cfg(cell), sched_cfg(sched_cfg_) +{ + bwps.reserve(cell.bwps.size()); + for (uint32_t i = 0; i < cell.bwps.size(); ++i) { + bwps.emplace_back(cell, sched_cfg_, cc, i); + } + srsran_assert(not bwps.empty(), "No BWPs were configured"); +} + +sched_params::sched_params(const sched_cfg_t& sched_cfg_) : sched_cfg(sched_cfg_) {} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void get_dci_locs(const srsran_coreset_t& coreset, + const srsran_search_space_t& search_space, + uint16_t rnti, + bwp_cce_pos_list& cce_locs) +{ + for (uint32_t sl = 0; sl < SRSRAN_NOF_SF_X_FRAME; ++sl) { + for (uint32_t agg_idx = 0; agg_idx < MAX_NOF_AGGR_LEVELS; ++agg_idx) { + pdcch_cce_pos_list pdcch_locs; + cce_locs[sl][agg_idx].resize(pdcch_locs.capacity()); + uint32_t n = + srsran_pdcch_nr_locations_coreset(&coreset, &search_space, rnti, agg_idx, sl, cce_locs[sl][agg_idx].data()); + cce_locs[sl][agg_idx].resize(n); + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bwp_ue_cfg::bwp_ue_cfg(uint16_t rnti_, const bwp_params& bwp_cfg_, const ue_cfg_t& uecfg_) : + rnti(rnti_), cfg_(&uecfg_), bwp_cfg(&bwp_cfg_) +{ + std::fill(ss_id_to_cce_idx.begin(), ss_id_to_cce_idx.end(), SRSRAN_UE_DL_NR_MAX_NOF_SEARCH_SPACE); + const auto& pdcch = phy().pdcch; + for (uint32_t i = 0; i < SRSRAN_UE_DL_NR_MAX_NOF_SEARCH_SPACE; ++i) { + if (pdcch.search_space_present[i]) { + const auto& ss = pdcch.search_space[i]; + srsran_assert(pdcch.coreset_present[ss.coreset_id], + "Invalid mapping search space id=%d to coreset id=%d", + ss.id, + ss.coreset_id); + const auto& coreset = pdcch.coreset[ss.coreset_id]; + cce_positions_list.emplace_back(); + get_dci_locs(coreset, ss, rnti, cce_positions_list.back()); + ss_id_to_cce_idx[ss.id] = cce_positions_list.size() - 1; + } + } +} + +ue_cfg_extended::ue_cfg_extended(uint16_t rnti_, const ue_cfg_t& uecfg) : ue_cfg_t(uecfg), rnti(rnti_) +{ + cc_params.resize(carriers.size()); + for (uint32_t cc = 0; cc < cc_params.size(); ++cc) { + cc_params[cc].bwps.resize(1); + auto& bwp = cc_params[cc].bwps[0]; + for (uint32_t ssid = 0; ssid < SRSRAN_UE_DL_NR_MAX_NOF_SEARCH_SPACE; ++ssid) { + if (phy_cfg.pdcch.search_space_present[ssid]) { + auto& ss = phy_cfg.pdcch.search_space[ssid]; + bwp.ss_list[ss.id].emplace(); + bwp.ss_list[ss.id]->cfg = &ss; + get_dci_locs(phy_cfg.pdcch.coreset[ss.coreset_id], ss, rnti, bwp.ss_list[ss.id]->cce_positions); + } + } + for (uint32_t idx = 0; idx < SRSRAN_UE_DL_NR_MAX_NOF_CORESET; ++idx) { + if (phy_cfg.pdcch.coreset_present[idx]) { + bwp.coresets.emplace_back(); + auto& coreset = bwp.coresets.back(); + coreset.cfg = &phy_cfg.pdcch.coreset[idx]; + for (auto& ss : bwp.ss_list) { + if (ss.has_value() and ss->cfg->coreset_id == coreset.cfg->id) { + coreset.ss_list.push_back(ss->cfg->id); + } + } + } + } + } +} + +} // namespace sched_nr_impl +} // namespace srsenb \ No newline at end of file diff --git a/srsenb/src/stack/mac/nr/sched_nr_common.cc b/srsenb/src/stack/mac/nr/sched_nr_common.cc deleted file mode 100644 index 8577de130..000000000 --- a/srsenb/src/stack/mac/nr/sched_nr_common.cc +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright 2013-2021 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 "srsenb/hdr/stack/mac/nr/sched_nr_common.h" - -namespace srsenb { -namespace sched_nr_impl { - -sched_cell_params::sched_cell_params(uint32_t cc_, const cell_cfg_t& cell, const sched_cfg_t& sched_cfg_) : - cc(cc_), cell_cfg(cell), sched_cfg(sched_cfg_) -{} - -sched_params::sched_params(const sched_cfg_t& sched_cfg_) : sched_cfg(sched_cfg_) {} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void get_dci_locs(const srsran_coreset_t& coreset, - const srsran_search_space_t& search_space, - uint16_t rnti, - bwp_cce_pos_list& cce_locs) -{ - for (uint32_t sl = 0; sl < SRSRAN_NOF_SF_X_FRAME; ++sl) { - for (uint32_t agg_idx = 0; agg_idx < MAX_NOF_AGGR_LEVELS; ++agg_idx) { - pdcch_cce_pos_list pdcch_locs; - cce_locs[sl][agg_idx].resize(pdcch_locs.capacity()); - uint32_t n = - srsran_pdcch_nr_locations_coreset(&coreset, &search_space, rnti, agg_idx, sl, cce_locs[sl][agg_idx].data()); - cce_locs[sl][agg_idx].resize(n); - } - } -} - -} // namespace sched_nr_impl -} // namespace srsenb \ No newline at end of file diff --git a/srsenb/src/stack/mac/nr/sched_nr_harq.cc b/srsenb/src/stack/mac/nr/sched_nr_harq.cc index 4cdb9f4b4..0dae2e23b 100644 --- a/srsenb/src/stack/mac/nr/sched_nr_harq.cc +++ b/srsenb/src/stack/mac/nr/sched_nr_harq.cc @@ -54,7 +54,7 @@ void harq_proc::reset() bool harq_proc::new_tx(tti_point tti_tx_, tti_point tti_ack_, - const rbgmask_t& rbgmask_, + const prb_grant& grant, uint32_t mcs, uint32_t tbs, uint32_t max_retx_) @@ -66,7 +66,7 @@ bool harq_proc::new_tx(tti_point tti_tx_, max_retx = max_retx_; tti_tx = tti_tx_; tti_ack = tti_ack_; - rbgmask = rbgmask_; + prbs_ = grant; tb[0].ndi = !tb[0].ndi; tb[0].mcs = mcs; tb[0].tbs = tbs; @@ -74,32 +74,49 @@ bool harq_proc::new_tx(tti_point tti_tx_, return true; } -bool harq_proc::new_retx(tti_point tti_tx_, tti_point tti_ack_, const rbgmask_t& rbgmask_, int* mcs, int* tbs) +bool harq_proc::set_tbs(uint32_t tbs) { - if (empty() or rbgmask.count() != rbgmask.count()) { + if (empty() or nof_retx() > 0) { + return false; + } + tb[0].tbs = tbs; + return true; +} + +bool harq_proc::new_retx(tti_point tti_tx_, tti_point tti_ack_, const prb_grant& grant) +{ + if (grant.is_alloc_type0() != prbs_.is_alloc_type0() or + (grant.is_alloc_type0() and grant.rbgs().count() != prbs_.rbgs().count()) or + (grant.is_alloc_type1() and grant.prbs().length() == prbs_.prbs().length())) { + return false; + } + if (new_retx(tti_tx_, tti_ack_)) { + prbs_ = grant; + return true; + } + return false; +} + +bool harq_proc::new_retx(tti_point tti_tx_, tti_point tti_ack_) +{ + if (empty()) { return false; } tti_tx = tti_tx_; tti_ack = tti_ack_; - rbgmask = rbgmask_; tb[0].ack_state = false; tb[0].n_rtx++; - if (mcs != nullptr) { - *mcs = tb[0].mcs; - } - if (tbs != nullptr) { - *tbs = tb[0].tbs; - } return true; } -harq_entity::harq_entity(uint32_t nof_harq_procs) +harq_entity::harq_entity(uint32_t nprb, uint32_t nof_harq_procs) { + // Create HARQs dl_harqs.reserve(nof_harq_procs); ul_harqs.reserve(nof_harq_procs); for (uint32_t pid = 0; pid < nof_harq_procs; ++pid) { - dl_harqs.emplace_back(pid); - ul_harqs.emplace_back(pid); + dl_harqs.emplace_back(pid, nprb); + ul_harqs.emplace_back(pid, nprb); } } diff --git a/srsenb/src/stack/mac/nr/sched_nr_helpers.cc b/srsenb/src/stack/mac/nr/sched_nr_helpers.cc new file mode 100644 index 000000000..cc9469fa7 --- /dev/null +++ b/srsenb/src/stack/mac/nr/sched_nr_helpers.cc @@ -0,0 +1,85 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#include "srsenb/hdr/stack/mac/nr/sched_nr_helpers.h" +#include "srsenb/hdr/stack/mac/nr/sched_nr_harq.h" +#include "srsenb/hdr/stack/mac/nr/sched_nr_ue.h" + +namespace srsenb { +namespace sched_nr_impl { + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool fill_dci_rar(prb_interval interv, const bwp_params& cell, srsran_dci_dl_nr_t& dci) +{ + dci.mcs = 5; + return true; +} + +template +void fill_dci_common(const slot_ue& ue, const bwp_params& bwp_cfg, DciDlOrUl& dci) +{ + const static uint32_t rv_idx[4] = {0, 2, 3, 1}; + + dci.bwp_id = ue.cfg->active_bwp().bwp_id; + dci.cc_id = ue.cc; + dci.tpc = 1; + // harq + harq_proc* h = std::is_same::value ? ue.h_dl : ue.h_ul; + dci.pid = h->pid; + dci.ndi = h->ndi(); + dci.mcs = h->mcs(); + dci.rv = rv_idx[h->nof_retx() % 4]; + // PRB assignment + const prb_grant& grant = h->prbs(); + if (grant.is_alloc_type0()) { + dci.freq_domain_assigment = grant.rbgs().to_uint64(); + } else { + dci.freq_domain_assigment = + srsran_ra_nr_type1_riv(bwp_cfg.cfg.rb_width, grant.prbs().start(), grant.prbs().length()); + } + dci.time_domain_assigment = 0; +} + +void fill_dl_dci_ue_fields(const slot_ue& ue, + const bwp_params& bwp_cfg, + uint32_t ss_id, + srsran_dci_location_t dci_pos, + srsran_dci_dl_nr_t& dci) +{ + // Note: DCI location may not be the final one, as scheduler may rellocate the UE PDCCH. However, the remaining DCI + // params are independent of the exact DCI location + bool ret = ue.cfg->phy().get_dci_ctx_pdsch_rnti_c(ss_id, dci_pos, ue.rnti, dci.ctx); + srsran_assert(ret, "Invalid DL DCI format"); + + fill_dci_common(ue, bwp_cfg, dci); + if (dci.ctx.format == srsran_dci_format_nr_1_0) { + dci.harq_feedback = ue.cfg->phy().harq_ack.dl_data_to_ul_ack[ue.pdsch_tti.sf_idx()] - 1; + } else { + dci.harq_feedback = ue.pdsch_tti.sf_idx(); + } +} + +void fill_ul_dci_ue_fields(const slot_ue& ue, + const bwp_params& bwp_cfg, + uint32_t ss_id, + srsran_dci_location_t dci_pos, + srsran_dci_ul_nr_t& dci) +{ + bool ret = ue.cfg->phy().get_dci_ctx_pdsch_rnti_c(ss_id, dci_pos, ue.rnti, dci.ctx); + srsran_assert(ret, "Invalid DL DCI format"); + + fill_dci_common(ue, bwp_cfg, dci); +} + +} // namespace sched_nr_impl +} // namespace srsenb \ No newline at end of file diff --git a/srsenb/src/stack/mac/nr/sched_nr_pdcch.cc b/srsenb/src/stack/mac/nr/sched_nr_pdcch.cc index 8f4a3fa2c..8f4368222 100644 --- a/srsenb/src/stack/mac/nr/sched_nr_pdcch.cc +++ b/srsenb/src/stack/mac/nr/sched_nr_pdcch.cc @@ -25,22 +25,25 @@ namespace srsenb { namespace sched_nr_impl { -coreset_region::coreset_region(uint32_t bwp_id_, - uint32_t slot_idx_, - uint32_t nof_td_symbols, - uint32_t nof_freq_resources, - pdcch_dl_list_t& pdcch_list_) : - bwp_id(bwp_id_), +coreset_region::coreset_region(const bwp_params& bwp_cfg_, + uint32_t coreset_id_, + uint32_t slot_idx_, + pdcch_dl_list_t& dl_list_, + pdcch_ul_list_t& ul_list_) : + coreset_cfg(&bwp_cfg_.cfg.pdcch.coreset[coreset_id_]), + coreset_id(coreset_id_), slot_idx(slot_idx_), - nof_symbols(nof_td_symbols), - nof_freq_res(nof_freq_resources), - pdcch_dl_list(pdcch_list_) + pdcch_dl_list(dl_list_), + pdcch_ul_list(ul_list_) { - srsran_assert(nof_td_symbols <= SRSRAN_CORESET_DURATION_MAX, + const bool* res_active = &coreset_cfg->freq_resources[0]; + nof_freq_res = std::count(res_active, res_active + SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE, true); + srsran_assert(get_td_symbols() <= SRSRAN_CORESET_DURATION_MAX, "Possible number of time-domain OFDM symbols in CORESET must be within {1,2,3}"); - srsran_assert(nof_freq_resources <= SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE, - "Provided number of CORESET freq domain resources=%d is too high", - nof_freq_resources); + srsran_assert(nof_freq_res <= bwp_cfg_.cell_cfg.carrier.nof_prb, + "The number of frequency resources=%d of coreset_id=%d exceeds BWP bandwidth", + nof_freq_res, + coreset_id); } void coreset_region::reset() @@ -49,9 +52,13 @@ void coreset_region::reset() saved_dfs_tree.clear(); dci_list.clear(); pdcch_dl_list.clear(); + pdcch_ul_list.clear(); } -bool coreset_region::alloc_dci(pdcch_grant_type_t alloc_type, uint32_t aggr_idx, uint32_t coreset_id, slot_ue* user) +bool coreset_region::alloc_dci(pdcch_grant_type_t alloc_type, + uint32_t aggr_idx, + uint32_t search_space_id, + slot_ue* user) { srsran_assert(aggr_idx <= 4, "Invalid DCI aggregation level=%d", 1U << aggr_idx); srsran_assert((user == nullptr) xor @@ -62,10 +69,15 @@ bool coreset_region::alloc_dci(pdcch_grant_type_t alloc_type, uint32_t aggr_idx, alloc_record record; record.ue = user; record.aggr_idx = aggr_idx; + record.ss_id = search_space_id; record.alloc_type = alloc_type; - record.idx = pdcch_dl_list.size(); - record.coreset_id = coreset_id; - pdcch_dl_list.emplace_back(); + if (record.alloc_type == pdcch_grant_type_t::ul_data) { + record.idx = pdcch_ul_list.size(); + pdcch_ul_list.emplace_back(); + } else { + record.idx = pdcch_dl_list.size(); + pdcch_dl_list.emplace_back(); + } // Try to allocate grant. If it fails, attempt the same grant, but using a different permutation of past grant DCI // positions @@ -83,7 +95,11 @@ bool coreset_region::alloc_dci(pdcch_grant_type_t alloc_type, uint32_t aggr_idx, // Revert steps to initial state, before dci record allocation was attempted dfs_tree = saved_dfs_tree; - pdcch_dl_list.pop_back(); + if (record.alloc_type == pdcch_grant_type_t::ul_data) { + pdcch_ul_list.pop_back(); + } else { + pdcch_dl_list.pop_back(); + } return false; } @@ -93,8 +109,12 @@ void coreset_region::rem_last_dci() // Remove DCI record dfs_tree.pop_back(); + if (dci_list.back().alloc_type == pdcch_grant_type_t::ul_data) { + pdcch_ul_list.pop_back(); + } else { + pdcch_dl_list.pop_back(); + } dci_list.pop_back(); - pdcch_dl_list.pop_back(); } bool coreset_region::get_next_dfs() @@ -150,8 +170,13 @@ bool coreset_region::alloc_dfs_node(const alloc_record& record, uint32_t start_d // Allocation successful node.total_mask |= node.current_mask; alloc_dfs.push_back(node); - pdcch_dl_t& pdcch_dl = pdcch_dl_list[record.idx]; - pdcch_dl.dci.ctx.location = node.dci_pos; + if (record.alloc_type == pdcch_grant_type_t::ul_data) { + pdcch_ul_t& pdcch_ul = pdcch_ul_list[record.idx]; + pdcch_ul.dci.ctx.location = node.dci_pos; + } else { + pdcch_dl_t& pdcch_dl = pdcch_dl_list[record.idx]; + pdcch_dl.dci.ctx.location = node.dci_pos; + } return true; } @@ -162,10 +187,9 @@ srsran::span coreset_region::get_cce_loc_table(const alloc_recor { switch (record.alloc_type) { case pdcch_grant_type_t::dl_data: - return record.ue->cfg->cc_params[record.ue->cc] - .bwps[bwp_id] - .coresets[record.coreset_id] - .cce_positions[slot_idx][record.aggr_idx]; + return record.ue->cfg->cce_pos_list(record.ss_id)[slot_idx][record.aggr_idx]; + case pdcch_grant_type_t::ul_data: + return record.ue->cfg->cce_pos_list(record.ss_id)[slot_idx][record.aggr_idx]; default: break; } diff --git a/srsenb/src/stack/mac/nr/sched_nr_phy_helpers.cc b/srsenb/src/stack/mac/nr/sched_nr_phy_helpers.cc deleted file mode 100644 index 60a71d221..000000000 --- a/srsenb/src/stack/mac/nr/sched_nr_phy_helpers.cc +++ /dev/null @@ -1,111 +0,0 @@ -/** - * Copyright 2013-2021 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 "srsenb/hdr/stack/mac/nr/sched_nr_phy_helpers.h" -#include "srsenb/hdr/stack/mac/nr/sched_nr_harq.h" -#include "srsenb/hdr/stack/mac/nr/sched_nr_ue.h" - -namespace srsenb { -namespace sched_nr_impl { - -/// Table 6.1.2.2.1-1 - Nominal RBG size P -uint32_t get_P(uint32_t bwp_nof_prb, bool config_1_or_2) -{ - srsran_assert(bwp_nof_prb > 0 and bwp_nof_prb <= 275, "Invalid BWP size"); - if (bwp_nof_prb <= 36) { - return config_1_or_2 ? 2 : 4; - } - if (bwp_nof_prb <= 72) { - return config_1_or_2 ? 4 : 8; - } - if (bwp_nof_prb <= 144) { - return config_1_or_2 ? 8 : 16; - } - return 16; -} - -uint32_t get_nof_rbgs(uint32_t bwp_nof_prb, uint32_t bwp_start, bool config1_or_2) -{ - uint32_t P = get_P(bwp_nof_prb, config1_or_2); - return srsran::ceil_div(bwp_nof_prb + (bwp_start % P), P); -} - -uint32_t get_rbg_size(uint32_t bwp_nof_prb, uint32_t bwp_start, bool config1_or_2, uint32_t rbg_idx) -{ - uint32_t P = get_P(bwp_nof_prb, config1_or_2); - uint32_t nof_rbgs = get_nof_rbgs(bwp_nof_prb, bwp_start, config1_or_2); - if (rbg_idx == 0) { - return P - (bwp_start % P); - } - if (rbg_idx == nof_rbgs - 1) { - uint32_t ret = (bwp_start + bwp_nof_prb) % P; - return ret > 0 ? ret : P; - } - return P; -} - -void bitmap_to_prb_array(const rbgmask_t& bitmap, uint32_t bwp_nof_prb, srsran_sch_grant_nr_t& grant) -{ - uint32_t count = 0; - grant.nof_prb = bwp_nof_prb; - for (uint32_t rbg = 0; rbg < bitmap.size(); ++rbg) { - bool val = bitmap.test(rbg); - uint32_t rbg_size = get_rbg_size(bwp_nof_prb, 0, true, rbg); - for (uint32_t prb = count; prb < count + rbg_size; ++prb) { - grant.prb_idx[prb] = val; - } - } -} - -void fill_dci_harq(const harq_proc& h, srsran_dci_dl_nr_t& dci) -{ - dci.pid = h.pid; - dci.ndi = h.ndi(); - dci.mcs = h.mcs(); -} - -void fill_dci_ue_cfg(const slot_ue& ue, srsran_dci_dl_nr_t& dci) -{ - dci.bwp_id = ue.bwp_id; - dci.cc_id = ue.cc; - dci.ctx.rnti = ue.rnti; - dci.tpc = 1; - fill_dci_harq(*ue.h_dl, dci); -} - -void fill_dci_harq(const harq_proc& h, srsran_dci_ul_nr_t& dci) -{ - dci.pid = h.pid; - dci.ndi = h.ndi(); - dci.mcs = h.mcs(); -} - -void fill_dci_ue_cfg(const slot_ue& ue, srsran_dci_ul_nr_t& dci) -{ - dci.bwp_id = ue.bwp_id; - dci.cc_id = ue.cc; - dci.ctx.rnti = ue.rnti; - dci.tpc = 1; - fill_dci_harq(*ue.h_ul, dci); -} - -} // namespace sched_nr_impl -} // namespace srsenb \ No newline at end of file diff --git a/srsenb/src/stack/mac/nr/sched_nr_rb.cc b/srsenb/src/stack/mac/nr/sched_nr_rb.cc new file mode 100644 index 000000000..e24844b7f --- /dev/null +++ b/srsenb/src/stack/mac/nr/sched_nr_rb.cc @@ -0,0 +1,105 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#include "srsenb/hdr/stack/mac/nr/sched_nr_rb.h" + +namespace srsenb { +namespace sched_nr_impl { + +/// TS 38.214, Table 6.1.2.2.1-1 - Nominal RBG size P +uint32_t get_P(uint32_t bwp_nof_prb, bool config_1_or_2) +{ + srsran_assert(bwp_nof_prb > 0 and bwp_nof_prb <= 275, "Invalid BWP size"); + if (bwp_nof_prb <= 36) { + return config_1_or_2 ? 2 : 4; + } + if (bwp_nof_prb <= 72) { + return config_1_or_2 ? 4 : 8; + } + if (bwp_nof_prb <= 144) { + return config_1_or_2 ? 8 : 16; + } + return 16; +} + +/// TS 38.214 - total number of RBGs for a uplink bandwidth part of size "bwp_nof_prb" PRBs +uint32_t get_nof_rbgs(uint32_t bwp_nof_prb, uint32_t bwp_start, bool config1_or_2) +{ + uint32_t P = get_P(bwp_nof_prb, config1_or_2); + return srsran::ceil_div(bwp_nof_prb + (bwp_start % P), P); +} + +uint32_t get_rbg_size(uint32_t bwp_nof_prb, uint32_t bwp_start, bool config1_or_2, uint32_t rbg_idx) +{ + uint32_t P = get_P(bwp_nof_prb, config1_or_2); + uint32_t nof_rbgs = get_nof_rbgs(bwp_nof_prb, bwp_start, config1_or_2); + if (rbg_idx == 0) { + return P - (bwp_start % P); + } + if (rbg_idx == nof_rbgs - 1) { + uint32_t ret = (bwp_start + bwp_nof_prb) % P; + return ret > 0 ? ret : P; + } + return P; +} + +bwp_rb_bitmap::bwp_rb_bitmap(uint32_t bwp_nof_prbs, uint32_t bwp_prb_start_, bool config1_or_2) : + prbs_(bwp_nof_prbs), + rbgs_(get_nof_rbgs(bwp_nof_prbs, bwp_prb_start_, config1_or_2)), + P_(get_P(bwp_nof_prbs, config1_or_2)), + Pnofbits(log2(P_)), + first_rbg_size(get_rbg_size(bwp_nof_prbs, bwp_prb_start_, config1_or_2, 0)) +{} + +uint32_t bwp_rb_bitmap::prb_to_rbg_idx(uint32_t prb_idx) const +{ + return ((prb_idx + P_ - first_rbg_size) >> Pnofbits); +} + +void bwp_rb_bitmap::add_prbs_to_rbgs(const prb_bitmap& grant) +{ + int idx = 0; + do { + idx = grant.find_lowest(idx, grant.size(), true); + if (idx < 0) { + return; + } + uint32_t rbg_idx = prb_to_rbg_idx(idx); + rbgs_.set(rbg_idx, true); + idx++; + } while (idx != (int)prbs_.size()); +} + +void bwp_rb_bitmap::add_prbs_to_rbgs(const prb_interval& grant) +{ + uint32_t rbg_start = prb_to_rbg_idx(grant.start()); + uint32_t rbg_stop = std::min(prb_to_rbg_idx(grant.stop() - 1) + 1u, (uint32_t)rbgs_.size()); + rbgs_.fill(rbg_start, rbg_stop); +} + +void bwp_rb_bitmap::add_rbgs_to_prbs(const rbg_bitmap& grant) +{ + int idx = 0; + do { + idx = grant.find_lowest(idx, grant.size(), true); + if (idx < 0) { + return; + } + uint32_t prb_idx = (idx - 1) * P_ + first_rbg_size; + uint32_t prb_end = std::min(prb_idx + ((idx == 0) ? first_rbg_size : P_), (uint32_t)prbs_.size()); + prbs_.fill(prb_idx, prb_end); + idx++; + } while (idx != (int)prbs_.size()); +} + +} // namespace sched_nr_impl +} // namespace srsenb diff --git a/srsenb/src/stack/mac/nr/sched_nr_rb_grid.cc b/srsenb/src/stack/mac/nr/sched_nr_rb_grid.cc index cbdcdbb99..c20afcb8e 100644 --- a/srsenb/src/stack/mac/nr/sched_nr_rb_grid.cc +++ b/srsenb/src/stack/mac/nr/sched_nr_rb_grid.cc @@ -20,138 +20,239 @@ */ #include "srsenb/hdr/stack/mac/nr/sched_nr_rb_grid.h" -#include "srsenb/hdr/stack/mac/nr/sched_nr_phy_helpers.h" +#include "srsenb/hdr/stack/mac/nr/sched_nr_helpers.h" namespace srsenb { namespace sched_nr_impl { -using pusch_grant = sched_nr_interface::pusch_grant; +#define NUMEROLOGY_IDX 0 -bwp_slot_grid::bwp_slot_grid(const sched_cell_params& cell_params, uint32_t bwp_id_, uint32_t slot_idx_) : - dl_rbgs(cell_params.cell_cfg.nof_rbg), ul_rbgs(cell_params.cell_cfg.nof_rbg) +bwp_slot_grid::bwp_slot_grid(const bwp_params& bwp_cfg_, uint32_t slot_idx_) : + dl_prbs(bwp_cfg_.cfg.rb_width, bwp_cfg_.cfg.start_rb, bwp_cfg_.cfg.pdsch.rbg_size_cfg_1), + ul_prbs(bwp_cfg_.cfg.rb_width, bwp_cfg_.cfg.start_rb, bwp_cfg_.cfg.pdsch.rbg_size_cfg_1), + slot_idx(slot_idx_), + cfg(&bwp_cfg_), + is_dl(srsran_tdd_nr_is_dl(&bwp_cfg_.cell_cfg.tdd, NUMEROLOGY_IDX, slot_idx_)), + is_ul(srsran_tdd_nr_is_ul(&bwp_cfg_.cell_cfg.tdd, NUMEROLOGY_IDX, slot_idx_)) { - coresets.emplace_back(bwp_id_, slot_idx_, 1, cell_params.cell_cfg.bwps[bwp_id_].rb_width / 6, pdcch_dl_list); + for (uint32_t cs_idx = 0; cs_idx < SRSRAN_UE_DL_NR_MAX_NOF_CORESET; ++cs_idx) { + if (cfg->cfg.pdcch.coreset_present[cs_idx]) { + uint32_t cs_id = cfg->cfg.pdcch.coreset[cs_idx].id; + coresets[cs_id].emplace(*cfg, cs_id, slot_idx_, dl_pdcchs, ul_pdcchs); + } + } } void bwp_slot_grid::reset() { for (auto& coreset : coresets) { - coreset.reset(); + if (coreset.has_value()) { + coreset->reset(); + } } - dl_rbgs.reset(); - ul_rbgs.reset(); - pdsch_grants.clear(); - pdcch_dl_list.clear(); - pusch_grants.clear(); - pucch_grants.clear(); + dl_prbs.reset(); + ul_prbs.reset(); + dl_pdcchs.clear(); + ul_pdcchs.clear(); + pending_acks.clear(); } -bwp_res_grid::bwp_res_grid(const sched_cell_params& cell_cfg_, uint32_t bwp_id_) : bwp_id(bwp_id_), cell_cfg(&cell_cfg_) +bwp_res_grid::bwp_res_grid(const bwp_params& bwp_cfg_) : cfg(&bwp_cfg_) { - for (uint32_t sl = 0; sl < SCHED_NR_NOF_SUBFRAMES; ++sl) { - slots.emplace_back(cell_cfg_, bwp_id, sl); - } -} - -cell_res_grid::cell_res_grid(const sched_cell_params& cell_cfg_) : cell_cfg(&cell_cfg_) -{ - for (uint32_t bwp_id = 0; bwp_id < cell_cfg->cell_cfg.bwps.size(); ++bwp_id) { - bwps.emplace_back(cell_cfg_, bwp_id); + for (uint32_t sl = 0; sl < slots.capacity(); ++sl) { + slots.emplace_back(*cfg, sl % static_cast(SRSRAN_NSLOTS_PER_FRAME_NR(0u))); } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -slot_bwp_sched::slot_bwp_sched(uint32_t bwp_id, cell_res_grid& phy_grid_) : - logger(srslog::fetch_basic_logger("MAC")), cfg(*phy_grid_.cell_cfg), bwp_grid(phy_grid_.bwps[bwp_id]) +bwp_slot_allocator::bwp_slot_allocator(bwp_res_grid& bwp_grid_) : + logger(srslog::fetch_basic_logger("MAC")), cfg(*bwp_grid_.cfg), bwp_grid(bwp_grid_) {} -alloc_result slot_bwp_sched::alloc_pdsch(slot_ue& ue, const rbgmask_t& dl_mask) +alloc_result bwp_slot_allocator::alloc_rar(uint32_t aggr_idx, + const srsenb::sched_nr_impl::pending_rar_t& rar, + prb_interval interv, + uint32_t nof_grants) { - if (ue.h_dl == nullptr) { - logger.warning("SCHED: Trying to allocate PDSCH for rnti=0x%x with no available HARQs", ue.rnti); - return alloc_result::no_rnti_opportunity; - } - pdsch_list_t& pdsch_grants = bwp_grid[ue.pdsch_tti].pdsch_grants; - if (pdsch_grants.full()) { + static const uint32_t msg3_nof_prbs = 3; + + bwp_slot_grid& bwp_pdcch_slot = bwp_grid[pdcch_tti]; + bwp_slot_grid& bwp_msg3_slot = bwp_grid[pdcch_tti + 4]; + + if (bwp_pdcch_slot.dl_pdcchs.full()) { logger.warning("SCHED: Maximum number of DL allocations reached"); return alloc_result::no_grant_space; } - rbgmask_t& pdsch_mask = bwp_grid[ue.pdsch_tti].dl_rbgs; + + // Check DL RB collision + const prb_bitmap& pdsch_mask = bwp_pdcch_slot.dl_prbs.prbs(); + prb_bitmap dl_mask(pdsch_mask.size()); + dl_mask.fill(interv.start(), interv.stop()); if ((pdsch_mask & dl_mask).any()) { + logger.debug("SCHED: Provided RBG mask collides with allocation previously made."); return alloc_result::sch_collision; } - const uint32_t aggr_idx = 3, coreset_id = 0; - if (not bwp_grid[ue.pdcch_tti].coresets[coreset_id].alloc_dci( - pdcch_grant_type_t::dl_data, aggr_idx, coreset_id, &ue)) { + + // Check Msg3 RB collision + uint32_t total_ul_nof_prbs = msg3_nof_prbs * nof_grants; + uint32_t total_ul_nof_rbgs = srsran::ceil_div(total_ul_nof_prbs, get_P(bwp_grid.nof_prbs(), false)); + prb_interval msg3_rbgs = find_empty_interval_of_length(bwp_msg3_slot.ul_prbs.prbs(), total_ul_nof_rbgs); + if (msg3_rbgs.length() < total_ul_nof_rbgs) { + logger.debug("SCHED: No space in PUSCH for Msg3."); + return alloc_result::sch_collision; + } + + // Find PDCCH position + const uint32_t coreset_id = cfg.cfg.pdcch.ra_search_space.coreset_id; + const uint32_t search_space_id = cfg.cfg.pdcch.ra_search_space.id; + if (not bwp_pdcch_slot.coresets[coreset_id]->alloc_dci(pdcch_grant_type_t::rar, aggr_idx, search_space_id, nullptr)) { // Could not find space in PDCCH + logger.debug("SCHED: No space in PDCCH for DL tx."); return alloc_result::no_cch_space; } - int mcs = -1, tbs = -1; - if (ue.h_dl->empty()) { - mcs = 20; - tbs = 100; - bool ret = ue.h_dl->new_tx(ue.pdsch_tti, ue.uci_tti, dl_mask, mcs, tbs, 4); - srsran_assert(ret, "Failed to allocate DL HARQ"); - } else { - bool ret = ue.h_dl->new_retx(ue.pdsch_tti, ue.uci_tti, dl_mask, &mcs, &tbs); - srsran_assert(ret, "Failed to allocate DL HARQ retx"); + // Generate DCI for RAR + pdcch_dl_t& pdcch = bwp_pdcch_slot.dl_pdcchs.back(); + if (not fill_dci_rar(interv, *bwp_grid.cfg, pdcch.dci)) { + // Cancel on-going PDCCH allocation + bwp_pdcch_slot.coresets[coreset_id]->rem_last_dci(); + return alloc_result::invalid_coderate; } - // Allocation Successful - pdcch_dl_t& pdcch = bwp_grid[ue.pdcch_tti].pdcch_dl_list.back(); - fill_dci_ue_cfg(ue, pdcch.dci); - pdsch_grants.emplace_back(); - pdsch_t& grant = pdsch_grants.back(); - grant.sch.grant.rnti = ue.rnti; - bitmap_to_prb_array(dl_mask, bwp_grid.nof_prbs(), grant.sch.grant); - pdsch_mask |= dl_mask; + // RAR allocation successful. + bwp_pdcch_slot.dl_prbs.add(interv); return alloc_result::success; } -alloc_result slot_bwp_sched::alloc_pusch(slot_ue& ue, const rbgmask_t& ul_mask) +alloc_result bwp_slot_allocator::alloc_pdsch(slot_ue& ue, const prb_grant& dl_grant) +{ + if (ue.cfg->active_bwp().bwp_id != bwp_grid.cfg->bwp_id) { + logger.warning( + "SCHED: Trying to allocate PDSCH for rnti=0x%x in inactive BWP id=%d", ue.rnti, ue.cfg->active_bwp().bwp_id); + return alloc_result::no_rnti_opportunity; + } + if (ue.h_dl == nullptr) { + logger.warning("SCHED: Trying to allocate PDSCH for rnti=0x%x with no available HARQs", ue.rnti); + return alloc_result::no_rnti_opportunity; + } + bwp_slot_grid& bwp_pdcch_slot = bwp_grid[ue.pdcch_tti]; + bwp_slot_grid& bwp_pdsch_slot = bwp_grid[ue.pdsch_tti]; + bwp_slot_grid& bwp_uci_slot = bwp_grid[ue.uci_tti]; + if (not bwp_pdsch_slot.is_dl) { + logger.warning("SCHED: Trying to allocate PDSCH in TDD non-DL slot index=%d", bwp_pdsch_slot.slot_idx); + return alloc_result::no_sch_space; + } + pdcch_dl_list_t& pdsch_grants = bwp_pdsch_slot.dl_pdcchs; + if (pdsch_grants.full()) { + logger.warning("SCHED: Maximum number of DL allocations reached"); + return alloc_result::no_grant_space; + } + if (bwp_pdcch_slot.dl_prbs.collides(dl_grant)) { + return alloc_result::sch_collision; + } + + // Find space in PUCCH + // TODO + + // Find space and allocate PDCCH + const uint32_t aggr_idx = 2, ss_id = 1; + uint32_t coreset_id = ue.cfg->phy().pdcch.search_space[ss_id].coreset_id; + if (not bwp_pdcch_slot.coresets[coreset_id]->alloc_dci(pdcch_grant_type_t::dl_data, aggr_idx, ss_id, &ue)) { + // Could not find space in PDCCH + return alloc_result::no_cch_space; + } + + // Allocate HARQ + if (ue.h_dl->empty()) { + int mcs = 20; + int tbs = 100; + bool ret = ue.h_dl->new_tx(ue.pdsch_tti, ue.uci_tti, dl_grant, mcs, tbs, 4); + srsran_assert(ret, "Failed to allocate DL HARQ"); + } else { + bool ret = ue.h_dl->new_retx(ue.pdsch_tti, ue.uci_tti, dl_grant); + srsran_assert(ret, "Failed to allocate DL HARQ retx"); + } + + // Allocation Successful + + // Generate PDCCH + pdcch_dl_t& pdcch = bwp_pdcch_slot.dl_pdcchs.back(); + fill_dl_dci_ue_fields(ue, *bwp_grid.cfg, ss_id, pdcch.dci.ctx.location, pdcch.dci); + pdcch.dci.pucch_resource = 0; + pdcch.dci.dai = std::count_if(bwp_uci_slot.pending_acks.begin(), + bwp_uci_slot.pending_acks.end(), + [&ue](const harq_ack_t& p) { return p.res.rnti == ue.rnti; }); + pdcch.dci.dai %= 4; + + // Generate PUCCH + bwp_uci_slot.pending_acks.emplace_back(); + bwp_uci_slot.pending_acks.back().phy_cfg = &ue.cfg->phy(); + srsran_assert(ue.cfg->phy().get_pdsch_ack_resource(pdcch.dci, bwp_uci_slot.pending_acks.back().res), + "Error getting ack resource"); + + // Generate PDSCH + bwp_pdsch_slot.dl_prbs |= dl_grant; + bwp_pdsch_slot.pdschs.emplace_back(); + pdsch_t& pdsch = bwp_pdsch_slot.pdschs.back(); + srsran_slot_cfg_t slot_cfg; + slot_cfg.idx = ue.pdsch_tti.sf_idx(); + bool ret = ue.cfg->phy().get_pdsch_cfg(slot_cfg, pdcch.dci, pdsch.sch); + srsran_assert(ret, "Error converting DCI to grant"); + if (ue.h_dl->nof_retx() == 0) { + ue.h_dl->set_tbs(pdsch.sch.grant.tb[0].tbs); // update HARQ with correct TBS + } else { + srsran_assert(pdsch.sch.grant.tb[0].tbs == (int)ue.h_dl->tbs(), "The TBS did not remain constant in retx"); + } + pdsch.sch.grant.tb[0].softbuffer.tx = ue.h_dl->get_softbuffer().get(); + + return alloc_result::success; +} + +alloc_result bwp_slot_allocator::alloc_pusch(slot_ue& ue, const rbg_bitmap& ul_mask) { if (ue.h_ul == nullptr) { logger.warning("SCHED: Trying to allocate PUSCH for rnti=0x%x with no available HARQs", ue.rnti); return alloc_result::no_rnti_opportunity; } - pusch_list& pusch_grants = bwp_grid[ue.pusch_tti].pusch_grants; - if (pusch_grants.full()) { + auto& bwp_pdcch_slot = bwp_grid[ue.pdcch_tti]; + auto& bwp_pusch_slot = bwp_grid[ue.pusch_tti]; + if (not bwp_pusch_slot.is_ul) { + logger.warning("SCHED: Trying to allocate PUSCH in TDD non-UL slot index=%d", bwp_pusch_slot.slot_idx); + return alloc_result::no_sch_space; + } + pdcch_ul_list_t& pdcchs = bwp_pdcch_slot.ul_pdcchs; + if (pdcchs.full()) { logger.warning("SCHED: Maximum number of UL allocations reached"); return alloc_result::no_grant_space; } - rbgmask_t& pusch_mask = bwp_grid[ue.pusch_tti].ul_rbgs; + const rbg_bitmap& pusch_mask = bwp_pusch_slot.ul_prbs.rbgs(); if ((pusch_mask & ul_mask).any()) { return alloc_result::sch_collision; } - const uint32_t aggr_idx = 3, coreset_id = 0; - if (not bwp_grid[ue.pdcch_tti].coresets[coreset_id].alloc_dci( - pdcch_grant_type_t::ul_data, aggr_idx, coreset_id, &ue)) { + const uint32_t aggr_idx = 2, ss_id = 1; + uint32_t coreset_id = ue.cfg->phy().pdcch.search_space[ss_id].coreset_id; + if (not bwp_pdcch_slot.coresets[coreset_id].value().alloc_dci(pdcch_grant_type_t::ul_data, aggr_idx, ss_id, &ue)) { // Could not find space in PDCCH return alloc_result::no_cch_space; } - int mcs = -1, tbs = -1; if (ue.h_ul->empty()) { - mcs = 20; - tbs = 100; - bool ret = ue.h_ul->new_tx(ue.pusch_tti, ue.pusch_tti, ul_mask, mcs, tbs, ue.cfg->maxharq_tx); + int mcs = 20; + int tbs = 100; + bool ret = ue.h_ul->new_tx(ue.pusch_tti, ue.pusch_tti, ul_mask, mcs, tbs, ue.cfg->ue_cfg()->maxharq_tx); srsran_assert(ret, "Failed to allocate UL HARQ"); } else { - srsran_assert(ue.h_ul->new_retx(ue.pusch_tti, ue.pusch_tti, ul_mask, &mcs, &tbs), - "Failed to allocate UL HARQ retx"); + srsran_assert(ue.h_ul->new_retx(ue.pusch_tti, ue.pusch_tti, ul_mask), "Failed to allocate UL HARQ retx"); } // Allocation Successful - pdcch_ul_t& pdcch = bwp_grid[ue.pdcch_tti].pdcch_ul_list.back(); - fill_dci_ue_cfg(ue, pdcch.dci); - pusch_grants.emplace_back(); - pusch_grant& grant = pusch_grants.back(); - grant.dci.ctx.rnti = ue.rnti; - grant.bitmap = ul_mask; - pusch_mask |= ul_mask; + // Generate PDCCH + pdcch_ul_t& pdcch = pdcchs.back(); + fill_ul_dci_ue_fields(ue, *bwp_grid.cfg, ss_id, pdcch.dci.ctx.location, pdcch.dci); + // Generate PUSCH + bwp_pusch_slot.ul_prbs.add(ul_mask); return alloc_result::success; } diff --git a/srsenb/src/stack/mac/nr/sched_nr_ue.cc b/srsenb/src/stack/mac/nr/sched_nr_ue.cc index 27e9250f8..dac82de04 100644 --- a/srsenb/src/stack/mac/nr/sched_nr_ue.cc +++ b/srsenb/src/stack/mac/nr/sched_nr_ue.cc @@ -25,57 +25,35 @@ namespace srsenb { namespace sched_nr_impl { -ue_cfg_extended::ue_cfg_extended(uint16_t rnti_, const ue_cfg_t& uecfg) : ue_cfg_t(uecfg), rnti(rnti_) -{ - cc_params.resize(carriers.size()); - for (uint32_t cc = 0; cc < cc_params.size(); ++cc) { - cc_params[cc].bwps.resize(1); - auto& bwp = cc_params[cc].bwps[0]; - for (uint32_t ssid = 0; ssid < SRSRAN_UE_DL_NR_MAX_NOF_SEARCH_SPACE; ++ssid) { - if (phy_cfg.pdcch.search_space_present[ssid]) { - bwp.search_spaces.emplace_back(); - bwp.search_spaces.back().cfg = &phy_cfg.pdcch.search_space[ssid]; - } - } - for (uint32_t csid = 0; csid < SRSRAN_UE_DL_NR_MAX_NOF_CORESET; ++csid) { - if (phy_cfg.pdcch.coreset_present[csid]) { - bwp.coresets.emplace_back(); - auto& coreset = bwp.coresets.back(); - coreset.cfg = &phy_cfg.pdcch.coreset[csid]; - for (auto& ss : bwp.search_spaces) { - if (ss.cfg->coreset_id == csid) { - coreset.ss_list.push_back(&ss); - get_dci_locs(*coreset.cfg, *coreset.ss_list.back()->cfg, rnti, coreset.cce_positions); - } - } - } - } - } -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - slot_ue::slot_ue(resource_guard::token ue_token_, uint16_t rnti_, tti_point tti_rx_, uint32_t cc_) : ue_token(std::move(ue_token_)), rnti(rnti_), tti_rx(tti_rx_), cc(cc_) {} /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -ue_carrier::ue_carrier(uint16_t rnti_, uint32_t cc_, const ue_cfg_t& uecfg_) : rnti(rnti_), cc(cc_), cfg(&uecfg_) {} +ue_carrier::ue_carrier(uint16_t rnti_, const ue_cfg_t& uecfg_, const sched_cell_params& cell_params_) : + rnti(rnti_), + cc(cell_params_.cc), + bwp_cfg(rnti_, cell_params_.bwps[0], uecfg_), + cell_params(cell_params_), + harq_ent(cell_params_.nof_prb()) +{} void ue_carrier::push_feedback(srsran::move_callback callback) { pending_feedback.push_back(std::move(callback)); } -slot_ue ue_carrier::try_reserve(tti_point tti_rx, const ue_cfg_extended& uecfg_) +slot_ue ue_carrier::try_reserve(tti_point tti_rx, const ue_cfg_t& uecfg_) { slot_ue sfu(busy, rnti, tti_rx, cc); if (sfu.empty()) { return sfu; } // successfully acquired. Process any CC-specific pending feedback - cfg = &uecfg_; + if (bwp_cfg.ue_cfg() != &uecfg_) { + bwp_cfg = bwp_ue_cfg(rnti, cell_params.bwps[0], uecfg_); + } while (not pending_feedback.empty()) { pending_feedback.front()(*this); pending_feedback.pop_front(); @@ -90,23 +68,35 @@ slot_ue ue_carrier::try_reserve(tti_point tti_rx, const ue_cfg_extended& uecfg_) } // set UE parameters common to all carriers - sfu.cfg = &uecfg_; + sfu.cfg = &bwp_cfg; // copy cc-specific parameters and find available HARQs - sfu.cc_cfg = &uecfg_.carriers[cc]; - sfu.pdcch_tti = tti_rx + TX_ENB_DELAY; - sfu.pdsch_tti = sfu.pdcch_tti + sfu.cc_cfg->pdsch_res_list[0].k0; - sfu.pusch_tti = sfu.pdcch_tti + sfu.cc_cfg->pusch_res_list[0].k2; - sfu.uci_tti = sfu.pdsch_tti + sfu.cc_cfg->pdsch_res_list[0].k1; + sfu.cc_cfg = &uecfg_.carriers[cc]; + sfu.pdcch_tti = tti_rx + TX_ENB_DELAY; + const uint32_t k0 = 0; + sfu.pdsch_tti = sfu.pdcch_tti + k0; + uint32_t k1 = + sfu.cfg->phy().harq_ack.dl_data_to_ul_ack[sfu.pdsch_tti.sf_idx() % sfu.cfg->phy().harq_ack.nof_dl_data_to_ul_ack]; + sfu.uci_tti = sfu.pdsch_tti + k1; + uint32_t k2 = k1; + sfu.pusch_tti = sfu.pdcch_tti + k2; sfu.dl_cqi = dl_cqi; sfu.ul_cqi = ul_cqi; - sfu.h_dl = harq_ent.find_pending_dl_retx(); - if (sfu.h_dl == nullptr) { - sfu.h_dl = harq_ent.find_empty_dl_harq(); + + const srsran_tdd_config_nr_t& tdd_cfg = cell_params.cell_cfg.tdd; + if (srsran_tdd_nr_is_dl(&tdd_cfg, 0, sfu.pdsch_tti.sf_idx())) { + // If DL enabled + sfu.h_dl = harq_ent.find_pending_dl_retx(); + if (sfu.h_dl == nullptr) { + sfu.h_dl = harq_ent.find_empty_dl_harq(); + } } - sfu.h_ul = harq_ent.find_pending_ul_retx(); - if (sfu.h_ul == nullptr) { - sfu.h_ul = harq_ent.find_empty_ul_harq(); + if (srsran_tdd_nr_is_ul(&tdd_cfg, 0, sfu.pusch_tti.sf_idx())) { + // If UL enabled + sfu.h_ul = harq_ent.find_pending_ul_retx(); + if (sfu.h_ul == nullptr) { + sfu.h_ul = harq_ent.find_empty_ul_harq(); + } } if (sfu.h_dl == nullptr and sfu.h_ul == nullptr) { @@ -119,12 +109,12 @@ slot_ue ue_carrier::try_reserve(tti_point tti_rx, const ue_cfg_extended& uecfg_) /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -ue::ue(uint16_t rnti_, const ue_cfg_t& cfg) : rnti(rnti_) +ue::ue(uint16_t rnti_, const ue_cfg_t& cfg, const sched_params& sched_cfg_) : rnti(rnti_), sched_cfg(sched_cfg_) { - ue_cfgs[0] = ue_cfg_extended(rnti, cfg); + ue_cfgs[0] = cfg; for (uint32_t cc = 0; cc < cfg.carriers.size(); ++cc) { if (cfg.carriers[cc].active) { - carriers[cc].reset(new ue_carrier(rnti, cc, cfg)); + carriers[cc].reset(new ue_carrier(rnti, cfg, sched_cfg.cells[cc])); } } } diff --git a/srsenb/src/stack/mac/nr/sched_nr_worker.cc b/srsenb/src/stack/mac/nr/sched_nr_worker.cc index 3b20c2466..8c2445268 100644 --- a/srsenb/src/stack/mac/nr/sched_nr_worker.cc +++ b/srsenb/src/stack/mac/nr/sched_nr_worker.cc @@ -20,10 +20,15 @@ */ #include "srsenb/hdr/stack/mac/nr/sched_nr_worker.h" +#include "srsran/common/string_helpers.h" namespace srsenb { namespace sched_nr_impl { +slot_cc_worker::slot_cc_worker(serv_cell_ctxt& cc_sched) : + cell(cc_sched), cfg(*cc_sched.cfg), bwp_alloc(cc_sched.bwps[0].grid), logger(srslog::fetch_basic_logger("MAC")) +{} + /// Called at the beginning of TTI in a locked context, to reserve available UE resources void slot_cc_worker::start(tti_point tti_rx_, ue_map_t& ue_db) { @@ -43,22 +48,23 @@ void slot_cc_worker::start(tti_point tti_rx_, ue_map_t& ue_db) } // UE acquired successfully for scheduling in this {tti, cc} } - - tti_rx = tti_rx_; } void slot_cc_worker::run() { srsran_assert(running(), "scheduler worker::run() called for non-active worker"); - // Prioritize PDCCH scheduling for DL and UL data in a RoundRobin fashion - if ((tti_rx.to_uint() & 0x1u) == 0) { - alloc_dl_ues(); - alloc_ul_ues(); - } else { - alloc_ul_ues(); - alloc_dl_ues(); - } + bwp_alloc.new_slot(tti_rx + TX_ENB_DELAY); + + // Allocate pending RARs + cell.bwps[0].ra.run_slot(bwp_alloc); + + // TODO: Prioritize PDCCH scheduling for DL and UL data in a Round-Robin fashion + alloc_dl_ues(); + alloc_ul_ues(); + + // Log CC scheduler result + log_result(); } void slot_cc_worker::end_tti() @@ -81,10 +87,11 @@ void slot_cc_worker::alloc_dl_ues() return; } - rbgmask_t dlmask(cfg.cell_cfg.nof_rbg); + rbgmask_t dlmask(cfg.bwps[0].N_rbg); dlmask.fill(0, dlmask.size(), true); - res_grid.alloc_pdsch(ue, dlmask); + bwp_alloc.alloc_pdsch(ue, dlmask); } + void slot_cc_worker::alloc_ul_ues() { if (slot_ues.empty()) { @@ -95,69 +102,99 @@ void slot_cc_worker::alloc_ul_ues() return; } - rbgmask_t ulmask(cfg.cell_cfg.nof_rbg); + rbgmask_t ulmask(cfg.bwps[0].N_rbg); ulmask.fill(0, ulmask.size(), true); - res_grid.alloc_pusch(ue, ulmask); + bwp_alloc.alloc_pusch(ue, ulmask); +} + +void slot_cc_worker::log_result() const +{ + const bwp_slot_grid& bwp_slot = cell.bwps[0].grid[tti_rx + TX_ENB_DELAY]; + for (const pdcch_dl_t& pdcch : bwp_slot.dl_pdcchs) { + fmt::memory_buffer fmtbuf; + if (pdcch.dci.ctx.rnti_type == srsran_rnti_type_c) { + const slot_ue& ue = slot_ues[pdcch.dci.ctx.rnti]; + fmt::format_to(fmtbuf, + "SCHED: DL {}, cc={}, rnti=0x{:x}, pid={}, nrtx={}, dai={}, tti_pdsch={}, tti_ack={}", + ue.h_dl->nof_retx() == 0 ? "tx" : "retx", + cell.cfg->cc, + ue.rnti, + ue.h_dl->pid, + ue.h_dl->nof_retx(), + pdcch.dci.dai, + ue.pdsch_tti, + ue.uci_tti); + } else if (pdcch.dci.ctx.rnti_type == srsran_rnti_type_ra) { + fmt::format_to(fmtbuf, "SCHED: DL RAR, cc={}", cell.cfg->cc); + } else { + fmt::format_to(fmtbuf, "SCHED: unknown format"); + } + + logger.info("%s", srsran::to_c_str(fmtbuf)); + } } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -sched_worker_manager::sched_worker_manager(ue_map_t& ue_db_, const sched_params& cfg_) : cfg(cfg_), ue_db(ue_db_) +sched_worker_manager::sched_worker_manager(ue_map_t& ue_db_, const sched_params& cfg_) : + cfg(cfg_), ue_db(ue_db_), logger(srslog::fetch_basic_logger("MAC")) { for (uint32_t cc = 0; cc < cfg.cells.size(); ++cc) { cell_grid_list.emplace_back(cfg.cells[cc]); } // Note: For now, we only allow parallelism at the sector level - slot_ctxts.resize(cfg.sched_cfg.nof_concurrent_subframes); + slot_worker_ctxts.resize(cfg.sched_cfg.nof_concurrent_subframes); for (size_t i = 0; i < cfg.sched_cfg.nof_concurrent_subframes; ++i) { - slot_ctxts[i].reset(new slot_worker_ctxt()); - sem_init(&slot_ctxts[i]->sf_sem, 0, 1); - slot_ctxts[i]->workers.reserve(cfg.cells.size()); + slot_worker_ctxts[i].reset(new slot_worker_ctxt()); + slot_worker_ctxts[i]->workers.reserve(cfg.cells.size()); for (uint32_t cc = 0; cc < cfg.cells.size(); ++cc) { - slot_ctxts[i]->workers.emplace_back(cfg.cells[cc], cell_grid_list[cc]); + slot_worker_ctxts[i]->workers.emplace_back(cell_grid_list[cc]); } } } -sched_worker_manager::~sched_worker_manager() -{ - // acquire all slot worker contexts - for (auto& slot_ctxt : slot_ctxts) { - sem_wait(&slot_ctxt->sf_sem); - } - // destroy all slot worker contexts - for (auto& slot_ctxt : slot_ctxts) { - sem_destroy(&slot_ctxt->sf_sem); - } -} +sched_worker_manager::~sched_worker_manager() = default; sched_worker_manager::slot_worker_ctxt& sched_worker_manager::get_sf(tti_point tti_rx) { - return *slot_ctxts[tti_rx.to_uint() % slot_ctxts.size()]; + return *slot_worker_ctxts[tti_rx.to_uint() % slot_worker_ctxts.size()]; } -void sched_worker_manager::reserve_workers(tti_point tti_rx_) +void sched_worker_manager::start_slot(tti_point tti_rx, srsran::move_callback process_feedback) { - // lock if slot worker is already being used - auto& sf_worker_ctxt = get_sf(tti_rx_); - sem_wait(&sf_worker_ctxt.sf_sem); + auto& sf_worker_ctxt = get_sf(tti_rx); - sf_worker_ctxt.tti_rx = tti_rx_; + std::unique_lock lock(sf_worker_ctxt.slot_mutex); + while ((sf_worker_ctxt.tti_rx.is_valid() and sf_worker_ctxt.tti_rx != tti_rx)) { + // wait for previous slot to finish + sf_worker_ctxt.nof_workers_waiting++; + sf_worker_ctxt.cvar.wait(lock); + sf_worker_ctxt.nof_workers_waiting--; + } + if (sf_worker_ctxt.tti_rx == tti_rx) { + // another worker with the same slot idx already started + return; + } + + { + std::lock_guard db_lock(ue_db_mutex); + + process_feedback(); + + for (uint32_t cc = 0; cc < sf_worker_ctxt.workers.size(); ++cc) { + sf_worker_ctxt.workers[cc].start(tti_rx, ue_db); + } + } + + sf_worker_ctxt.tti_rx = tti_rx; sf_worker_ctxt.worker_count.store(static_cast(sf_worker_ctxt.workers.size()), std::memory_order_relaxed); -} - -void sched_worker_manager::start_tti(tti_point tti_rx_) -{ - auto& sf_worker_ctxt = get_sf(tti_rx_); - srsran_assert(sf_worker_ctxt.tti_rx == tti_rx_, "invalid run_tti(tti, cc) arguments"); - - for (uint32_t cc = 0; cc < sf_worker_ctxt.workers.size(); ++cc) { - sf_worker_ctxt.workers[cc].start(sf_worker_ctxt.tti_rx, ue_db); + if (sf_worker_ctxt.nof_workers_waiting > 0) { + sf_worker_ctxt.cvar.notify_all(); } } -bool sched_worker_manager::run_tti(tti_point tti_rx_, uint32_t cc, slot_res_t& tti_req) +bool sched_worker_manager::run_slot(tti_point tti_rx_, uint32_t cc) { auto& sf_worker_ctxt = get_sf(tti_rx_); srsran_assert(sf_worker_ctxt.tti_rx == tti_rx_, "invalid run_tti(tti, cc) arguments"); @@ -165,15 +202,6 @@ bool sched_worker_manager::run_tti(tti_point tti_rx_, uint32_t cc, slot_res_t& t // Get {tti, cc} scheduling decision sf_worker_ctxt.workers[cc].run(); - // Copy requested TTI DL and UL sched result - tti_req.dl_res.pdsch_tti = tti_rx_ + TX_ENB_DELAY; - tti_req.dl_res.pdcchs = cell_grid_list[cc].bwps[0][tti_req.dl_res.pdsch_tti].pdcch_dl_list; - tti_req.dl_res.pdschs = cell_grid_list[cc].bwps[0][tti_req.dl_res.pdsch_tti].pdsch_grants; - cell_grid_list[cc].bwps[0][tti_req.dl_res.pdsch_tti].reset(); - tti_req.ul_res.pusch_tti = tti_rx_ + TX_ENB_DELAY; - tti_req.ul_res.pusch = cell_grid_list[cc].bwps[0][tti_req.ul_res.pusch_tti].pusch_grants; - cell_grid_list[cc].bwps[0][tti_req.ul_res.pusch_tti].reset(); - // decrement the number of active workers int rem_workers = sf_worker_ctxt.worker_count.fetch_sub(1, std::memory_order_release) - 1; srsran_assert(rem_workers >= 0, "invalid number of calls to run_tti(tti, cc)"); @@ -181,18 +209,76 @@ bool sched_worker_manager::run_tti(tti_point tti_rx_, uint32_t cc, slot_res_t& t return rem_workers == 0; } -void sched_worker_manager::end_tti(tti_point tti_rx_) +void sched_worker_manager::release_slot(tti_point tti_rx_) { auto& sf_worker_ctxt = get_sf(tti_rx_); srsran_assert(sf_worker_ctxt.tti_rx == tti_rx_, "invalid run_tti(tti, cc) arguments"); srsran_assert(sf_worker_ctxt.worker_count == 0, "invalid number of calls to run_tti(tti, cc)"); - // All the workers of the same TTI have finished. Synchronize scheduling decisions with UEs state - for (slot_cc_worker& worker : sf_worker_ctxt.workers) { - worker.end_tti(); + { + std::lock_guard lock(ue_db_mutex); + + // All the workers of the same slot have finished. Synchronize scheduling decisions with UEs state + for (slot_cc_worker& worker : sf_worker_ctxt.workers) { + worker.end_tti(); + } } - sem_post(&sf_worker_ctxt.sf_sem); + std::unique_lock lock(sf_worker_ctxt.slot_mutex); + sf_worker_ctxt.tti_rx = {}; + if (sf_worker_ctxt.nof_workers_waiting > 0) { + lock.unlock(); + sf_worker_ctxt.cvar.notify_one(); + } +} + +bool sched_worker_manager::save_sched_result(tti_point pdcch_tti, uint32_t cc, dl_sched_t& dl_res, ul_sched_t& ul_res) +{ + auto& bwp_slot = cell_grid_list[cc].bwps[0].grid[pdcch_tti]; + + dl_res.pdcch_dl = bwp_slot.dl_pdcchs; + dl_res.pdcch_ul = bwp_slot.ul_pdcchs; + dl_res.pdsch = bwp_slot.pdschs; + ul_res.pusch = bwp_slot.puschs; + + // Group pending HARQ ACKs + srsran_pdsch_ack_nr_t ack = {}; + ack.nof_cc = not bwp_slot.pending_acks.empty(); + const srsran::phy_cfg_nr_t* phy_cfg = nullptr; + for (const harq_ack_t& pending_ack : bwp_slot.pending_acks) { + srsran_harq_ack_m_t ack_m = {}; + ack_m.resource = pending_ack.res; + ack_m.present = true; + srsran_harq_ack_insert_m(&ack, &ack_m); + phy_cfg = pending_ack.phy_cfg; + } + + if (phy_cfg != nullptr) { + srsran_slot_cfg_t slot_cfg{}; + slot_cfg.idx = pdcch_tti.sf_idx(); + srsran_uci_cfg_nr_t uci_cfg = {}; + srsran_assert(phy_cfg->get_uci_cfg(slot_cfg, ack, uci_cfg), "Error getting UCI CFG"); + + if (uci_cfg.ack.count > 0 || uci_cfg.nof_csi > 0 || uci_cfg.o_sr > 0) { + if (not ul_res.pusch.empty()) { + // Put UCI configuration in PUSCH config + srsran_assert(phy_cfg->get_pusch_uci_cfg(slot_cfg, uci_cfg, ul_res.pusch[0].sch), + "Error setting UCI configuration in PUSCH"); + } else { + // Put UCI configuration in PUCCH config + ul_res.pucch.emplace_back(); + pucch_t& pucch = ul_res.pucch.back(); + pucch.uci_cfg = uci_cfg; + srsran_assert(phy_cfg->get_pucch_uci_cfg(slot_cfg, pucch.uci_cfg, pucch.pucch_cfg, pucch.resource), + "Error getting PUCCH UCI cfg"); + } + } + } + + // clear up BWP slot + bwp_slot.reset(); + + return true; } } // namespace sched_nr_impl diff --git a/srsenb/src/stack/mac/sched_ue_ctrl/sched_ue_cell.cc b/srsenb/src/stack/mac/sched_ue_ctrl/sched_ue_cell.cc index fbfa86527..5c259e4b6 100644 --- a/srsenb/src/stack/mac/sched_ue_ctrl/sched_ue_cell.cc +++ b/srsenb/src/stack/mac/sched_ue_ctrl/sched_ue_cell.cc @@ -288,6 +288,7 @@ uint32_t sched_ue_cell::get_aggr_level(uint32_t nof_bits) const } else { dl_cqi = dl_cqi_ctxt.get_avg_cqi(); } + dl_cqi = std::max(cell_cfg->sched_cfg->pdcch_cqi_offset + (int)dl_cqi, 0); return srsenb::get_aggr_level(nof_bits, dl_cqi, cell_cfg->sched_cfg->min_aggr_level, diff --git a/srsenb/src/stack/mac/ue.cc b/srsenb/src/stack/mac/ue.cc index 6179235a9..a731bec30 100644 --- a/srsenb/src/stack/mac/ue.cc +++ b/srsenb/src/stack/mac/ue.cc @@ -80,6 +80,7 @@ cc_used_buffers_map::~cc_used_buffers_map() srsran::unique_byte_buffer_t cc_used_buffers_map::release_pdu(tti_point tti) { + std::unique_lock lock(mutex); if (not has_tti(tti)) { return nullptr; } @@ -94,6 +95,7 @@ srsran::unique_byte_buffer_t cc_used_buffers_map::release_pdu(tti_point tti) uint8_t* cc_used_buffers_map::request_pdu(tti_point tti, uint32_t len) { + std::unique_lock lock(mutex); if (not pdu_map.has_space(tti.to_uint())) { logger->error("UE buffers: could not allocate buffer for tti=%d", tti.to_uint()); return nullptr; @@ -114,6 +116,7 @@ uint8_t* cc_used_buffers_map::request_pdu(tti_point tti, uint32_t len) void cc_used_buffers_map::clear_old_pdus(tti_point current_tti) { + std::unique_lock lock(mutex); static const uint32_t old_tti_threshold = SRSRAN_FDD_NOF_HARQ + 4; tti_point max_tti{current_tti - old_tti_threshold}; @@ -262,13 +265,11 @@ srsran_softbuffer_tx_t* ue::get_tx_softbuffer(uint32_t enb_cc_idx, uint32_t harq uint8_t* ue::request_buffer(uint32_t tti, uint32_t enb_cc_idx, uint32_t len) { srsran_assert(len > 0, "UE buffers: Requesting buffer for zero bytes"); - std::unique_lock lock(rx_buffers_mutex); return cc_buffers[enb_cc_idx].get_rx_used_buffers().request_pdu(tti_point(tti), len); } void ue::clear_old_buffers(uint32_t tti) { - std::unique_lock lock(rx_buffers_mutex); // remove old buffers for (auto& cc : cc_buffers) { cc.get_rx_used_buffers().clear_old_pdus(tti_point{tti}); @@ -398,7 +399,6 @@ void ue::process_pdu(srsran::unique_byte_buffer_t pdu, uint32_t ue_cc_idx, uint3 srsran::unique_byte_buffer_t ue::release_pdu(uint32_t tti, uint32_t enb_cc_idx) { - std::lock_guard lock(rx_buffers_mutex); return cc_buffers[enb_cc_idx].get_rx_used_buffers().release_pdu(tti_point(tti)); } diff --git a/srsenb/src/stack/rrc/rrc.cc b/srsenb/src/stack/rrc/rrc.cc index da5c5208d..b14c0280e 100644 --- a/srsenb/src/stack/rrc/rrc.cc +++ b/srsenb/src/stack/rrc/rrc.cc @@ -112,6 +112,14 @@ int32_t rrc::init(const rrc_cfg_t& cfg_, running = true; + if (logger.debug.enabled()) { + asn1::json_writer js{}; + cfg.srb1_cfg.rlc_cfg.to_json(js); + logger.debug("SRB1 configuration: %s", js.to_string().c_str()); + js = {}; + cfg.srb2_cfg.rlc_cfg.to_json(js); + logger.debug("SRB2 configuration: %s", js.to_string().c_str()); + } return SRSRAN_SUCCESS; } diff --git a/srsenb/src/stack/rrc/rrc_endc.cc b/srsenb/src/stack/rrc/rrc_endc.cc index c61f6b849..985ea28ce 100644 --- a/srsenb/src/stack/rrc/rrc_endc.cc +++ b/srsenb/src/stack/rrc/rrc_endc.cc @@ -56,18 +56,18 @@ bool rrc::ue::rrc_endc::fill_conn_recfg(asn1::rrc::rrc_conn_recfg_r8_ies_s* conn meas_cfg_s& meas_cfg = conn_recfg->meas_cfg; meas_cfg.meas_obj_to_add_mod_list_present = true; - meas_cfg.meas_obj_to_add_mod_list.resize(2); - auto& meas_obj = meas_cfg.meas_obj_to_add_mod_list[0]; - meas_obj.meas_obj_id = 1; + meas_obj_to_add_mod_s meas_obj = {}; + meas_obj.meas_obj_id = meas_cfg.meas_obj_to_add_mod_list.size() + 1; meas_obj.meas_obj.set_meas_obj_eutra(); meas_obj.meas_obj.meas_obj_eutra().carrier_freq = 300; meas_obj.meas_obj.meas_obj_eutra().allowed_meas_bw = allowed_meas_bw_opts::mbw50; meas_obj.meas_obj.meas_obj_eutra().presence_ant_port1 = false; meas_obj.meas_obj.meas_obj_eutra().neigh_cell_cfg.from_number(0b01); + meas_cfg.meas_obj_to_add_mod_list.push_back(meas_obj); - auto& meas_obj2 = meas_cfg.meas_obj_to_add_mod_list[1]; - meas_obj2.meas_obj_id = 2; + meas_obj_to_add_mod_s meas_obj2 = {}; + meas_obj2.meas_obj_id = meas_cfg.meas_obj_to_add_mod_list.size() + 1; meas_obj2.meas_obj.set_meas_obj_nr_r15(); meas_obj2.meas_obj.meas_obj_nr_r15().carrier_freq_r15 = 634176; meas_obj2.meas_obj.meas_obj_nr_r15().rs_cfg_ssb_r15.meas_timing_cfg_r15.periodicity_and_offset_r15.set_sf20_r15(); @@ -78,13 +78,13 @@ bool rrc::ue::rrc_endc::fill_conn_recfg(asn1::rrc::rrc_conn_recfg_r8_ies_s* conn meas_obj2.meas_obj.meas_obj_nr_r15().ext = true; meas_obj2.meas_obj.meas_obj_nr_r15().band_nr_r15.set_present(true); meas_obj2.meas_obj.meas_obj_nr_r15().band_nr_r15.get()->set_setup() = 78; + meas_cfg.meas_obj_to_add_mod_list.push_back(meas_obj2); // report config meas_cfg.report_cfg_to_add_mod_list_present = true; - meas_cfg.report_cfg_to_add_mod_list.resize(1); - auto& report_cfg = meas_cfg.report_cfg_to_add_mod_list[0]; + report_cfg_to_add_mod_s report_cfg = {}; - report_cfg.report_cfg_id = 1; + report_cfg.report_cfg_id = meas_cfg.report_cfg_to_add_mod_list.size() + 1; report_cfg.report_cfg.set_report_cfg_inter_rat(); report_cfg.report_cfg.report_cfg_inter_rat().trigger_type.set_event(); report_cfg.report_cfg.report_cfg_inter_rat().trigger_type.event().event_id.set_event_b1_nr_r15(); @@ -109,14 +109,15 @@ bool rrc::ue::rrc_endc::fill_conn_recfg(asn1::rrc::rrc_conn_recfg_r8_ies_s* conn report_cfg.report_cfg.report_cfg_inter_rat().report_quant_cell_nr_r15.get()->ss_rsrp = true; report_cfg.report_cfg.report_cfg_inter_rat().report_quant_cell_nr_r15.get()->ss_rsrq = true; report_cfg.report_cfg.report_cfg_inter_rat().report_quant_cell_nr_r15.get()->ss_sinr = true; + meas_cfg.report_cfg_to_add_mod_list.push_back(report_cfg); // measIdToAddModList meas_cfg.meas_id_to_add_mod_list_present = true; - meas_cfg.meas_id_to_add_mod_list.resize(1); - auto& meas_id = meas_cfg.meas_id_to_add_mod_list[0]; - meas_id.meas_id = 1; - meas_id.meas_obj_id = 2; - meas_id.report_cfg_id = 1; + meas_id_to_add_mod_s meas_id = {}; + meas_id.meas_id = meas_obj.meas_obj_id; + meas_id.meas_obj_id = meas_obj2.meas_obj_id; + meas_id.report_cfg_id = report_cfg.report_cfg_id; + meas_cfg.meas_id_to_add_mod_list.push_back(meas_id); // quantityConfig meas_cfg.quant_cfg_present = true; diff --git a/srsenb/src/stack/rrc/rrc_ue.cc b/srsenb/src/stack/rrc/rrc_ue.cc index 387694913..3292ce8be 100644 --- a/srsenb/src/stack/rrc/rrc_ue.cc +++ b/srsenb/src/stack/rrc/rrc_ue.cc @@ -27,6 +27,7 @@ #include "srsenb/hdr/stack/rrc/ue_rr_cfg.h" #include "srsran/asn1/rrc_utils.h" #include "srsran/common/enb_events.h" +#include "srsran/common/srsran_assert.h" #include "srsran/common/standard_streams.h" #include "srsran/interfaces/enb_pdcp_interfaces.h" #include "srsran/interfaces/enb_rlc_interfaces.h" @@ -1420,8 +1421,30 @@ void rrc::ue::apply_pdcp_drb_updates(const rr_cfg_ded_s& pending_rr_cfg) void rrc::ue::apply_rlc_rb_updates(const rr_cfg_ded_s& pending_rr_cfg) { for (const srb_to_add_mod_s& srb : pending_rr_cfg.srb_to_add_mod_list) { - parent->rlc->add_bearer(rnti, srb.srb_id, srsran::rlc_config_t::srb_config(srb.srb_id)); + srb_cfg_t* srb_cfg; + if (srb.srb_id == 1) { + srb_cfg = &parent->cfg.srb1_cfg; + } else if (srb.srb_id == 2) { + srb_cfg = &parent->cfg.srb2_cfg; + } else { + srsran_terminate("Invalid LTE SRB id=%d", srb.srb_id); + } + + if (srb_cfg->rlc_cfg.type() == srb_to_add_mod_s::rlc_cfg_c_::types_opts::explicit_value) { + srsran::rlc_config_t rlc_cfg = srsran::make_rlc_config_t(srb_cfg->rlc_cfg.explicit_value()); + if (rlc_cfg.rlc_mode == srsran::rlc_mode_t::am and srb_cfg->enb_dl_max_retx_thres > 0) { + rlc_cfg.am.max_retx_thresh = srb_cfg->enb_dl_max_retx_thres; + } + parent->rlc->add_bearer(rnti, srb.srb_id, rlc_cfg); + } else { + srsran::rlc_config_t rlc_cfg = srsran::rlc_config_t::srb_config(srb.srb_id); + if (rlc_cfg.rlc_mode == srsran::rlc_mode_t::am and srb_cfg->enb_dl_max_retx_thres > 0) { + rlc_cfg.am.max_retx_thresh = srb_cfg->enb_dl_max_retx_thres; + } + parent->rlc->add_bearer(rnti, srb.srb_id, rlc_cfg); + } } + if (pending_rr_cfg.drb_to_release_list.size() > 0) { for (uint8_t drb_id : pending_rr_cfg.drb_to_release_list) { parent->rlc->del_bearer(rnti, drb_to_lcid((lte_drb)drb_id)); @@ -1431,7 +1454,13 @@ void rrc::ue::apply_rlc_rb_updates(const rr_cfg_ded_s& pending_rr_cfg) if (not drb.rlc_cfg_present) { parent->logger.warning("Default RLC DRB config not supported"); } - parent->rlc->add_bearer(rnti, drb.lc_ch_id, srsran::make_rlc_config_t(drb.rlc_cfg)); + srsran::rlc_config_t rlc_cfg = srsran::make_rlc_config_t(drb.rlc_cfg); + const bearer_cfg_handler::erab_t& erab = bearer_list.get_erabs().at(drb.eps_bearer_id); + if (rlc_cfg.rlc_mode == srsran::rlc_mode_t::am and + parent->cfg.qci_cfg.at(erab.qos_params.qci).enb_dl_max_retx_thres > 0) { + rlc_cfg.am.max_retx_thresh = parent->cfg.qci_cfg.at(erab.qos_params.qci).enb_dl_max_retx_thres; + } + parent->rlc->add_bearer(rnti, drb.lc_ch_id, rlc_cfg); } } diff --git a/srsenb/src/stack/rrc/ue_rr_cfg.cc b/srsenb/src/stack/rrc/ue_rr_cfg.cc index 41b51e474..6df72d286 100644 --- a/srsenb/src/stack/rrc/ue_rr_cfg.cc +++ b/srsenb/src/stack/rrc/ue_rr_cfg.cc @@ -42,7 +42,8 @@ namespace srsenb { * SRBs / DRBs *****************************/ -srb_to_add_mod_s* add_srb(srb_to_add_mod_list_l& srbs, uint8_t srb_id) +srb_to_add_mod_s* +add_srb(srb_to_add_mod_list_l& srbs, uint8_t srb_id, const asn1::rrc::srb_to_add_mod_s::rlc_cfg_c_& srb_cfg) { if (srb_id > 2 or srb_id == 0) { srslog::fetch_basic_logger("RRC").error("Invalid SRB id=%d", srb_id); @@ -54,20 +55,19 @@ srb_to_add_mod_s* add_srb(srb_to_add_mod_list_l& srbs, uint8_t srb_id) srb_it->lc_ch_cfg_present = true; srb_it->lc_ch_cfg.set(srb_to_add_mod_s::lc_ch_cfg_c_::types_opts::default_value); srb_it->rlc_cfg_present = true; - srb_it->rlc_cfg.set(srb_to_add_mod_s::rlc_cfg_c_::types_opts::default_value); - + srb_it->rlc_cfg = srb_cfg; return srb_it; } -void fill_srbs_reconf(srb_to_add_mod_list_l& srbs, const srb_to_add_mod_list_l& current_srbs) +void fill_srbs_reconf(srb_to_add_mod_list_l& srbs, const srb_to_add_mod_list_l& current_srbs, const rrc_cfg_t& enb_cfg) { // NOTE: In case of Handover, the Reconf includes SRB1 if (srsran::find_rrc_obj_id(current_srbs, 1) == current_srbs.end()) { - add_srb(srbs, 1); + add_srb(srbs, 1, enb_cfg.srb1_cfg.rlc_cfg); } if (srsran::find_rrc_obj_id(current_srbs, 2) == current_srbs.end()) { - add_srb(srbs, 2); + add_srb(srbs, 2, enb_cfg.srb2_cfg.rlc_cfg); } } @@ -129,8 +129,14 @@ void fill_cqi_report_enb_cfg(cqi_report_cfg_s& cqi_report_cfg, const rrc_cfg_t& } else { cqi_report_cfg.cqi_report_periodic_present = true; auto& cqi_setup = cqi_report_cfg.cqi_report_periodic.set_setup(); - cqi_setup.cqi_format_ind_periodic.set( - cqi_report_periodic_c::setup_s_::cqi_format_ind_periodic_c_::types::wideband_cqi); + if (enb_cfg.cqi_cfg.subband_k == 0) { + cqi_setup.cqi_format_ind_periodic.set( + cqi_report_periodic_c::setup_s_::cqi_format_ind_periodic_c_::types::wideband_cqi); + } else { + cqi_setup.cqi_format_ind_periodic.set( + cqi_report_periodic_c::setup_s_::cqi_format_ind_periodic_c_::types::subband_cqi); + cqi_setup.cqi_format_ind_periodic.subband_cqi().k = enb_cfg.cqi_cfg.subband_k; + } cqi_setup.simul_ack_nack_and_cqi = enb_cfg.cqi_cfg.simultaneousAckCQI; } cqi_report_cfg.nom_pdsch_rs_epre_offset = 0; @@ -308,7 +314,7 @@ void fill_rr_cfg_ded_setup(asn1::rrc::rr_cfg_ded_s& rr_cfg, // (Re)establish SRB1 rr_cfg.srb_to_add_mod_list_present = true; - add_srb(rr_cfg.srb_to_add_mod_list, 1); + add_srb(rr_cfg.srb_to_add_mod_list, 1, enb_cfg.srb1_cfg.rlc_cfg); // Setup SR/CQI configs rr_cfg.phys_cfg_ded_present = true; @@ -324,7 +330,7 @@ void fill_rr_cfg_ded_reconf(asn1::rrc::rr_cfg_ded_s& rr_cfg, bool phy_cfg_updated) { // (Re)establish SRBs - fill_srbs_reconf(rr_cfg.srb_to_add_mod_list, current_rr_cfg.srb_to_add_mod_list); + fill_srbs_reconf(rr_cfg.srb_to_add_mod_list, current_rr_cfg.srb_to_add_mod_list, enb_cfg); rr_cfg.srb_to_add_mod_list_present = rr_cfg.srb_to_add_mod_list.size() > 0; // Update DRBs if required diff --git a/srsenb/src/stack/upper/CMakeLists.txt b/srsenb/src/stack/upper/CMakeLists.txt index 43ab73e8c..70c4e1b5c 100644 --- a/srsenb/src/stack/upper/CMakeLists.txt +++ b/srsenb/src/stack/upper/CMakeLists.txt @@ -20,6 +20,7 @@ set(SOURCES gtpu.cc pdcp.cc rlc.cc) add_library(srsenb_upper STATIC ${SOURCES}) +target_link_libraries(srsenb_upper srsran_asn1 srsran_gtpu) set(SOURCES pdcp_nr.cc rlc_nr.cc sdap.cc) add_library(srsgnb_upper STATIC ${SOURCES}) diff --git a/srsenb/test/mac/nr/CMakeLists.txt b/srsenb/test/mac/nr/CMakeLists.txt index a231a8721..a97e0fcb7 100644 --- a/srsenb/test/mac/nr/CMakeLists.txt +++ b/srsenb/test/mac/nr/CMakeLists.txt @@ -18,10 +18,18 @@ # and at http://www.gnu.org/licenses/. # -add_executable(sched_nr_test sched_nr_test.cc sched_nr_sim_ue.cc) +add_executable(sched_nr_test sched_nr_test.cc sched_nr_sim_ue.cc sched_nr_ue_ded_test_suite.cc) target_link_libraries(sched_nr_test srsgnb_mac srsran_common ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES}) add_test(sched_nr_test sched_nr_test) + +add_executable(sched_nr_prb_test sched_nr_prb_test.cc) +target_link_libraries(sched_nr_prb_test + srsgnb_mac + srsran_common + ${CMAKE_THREAD_LIBS_INIT} + ${Boost_LIBRARIES}) +add_test(sched_nr_prb_test sched_nr_prb_test) diff --git a/srsenb/test/mac/nr/sched_nr_prb_test.cc b/srsenb/test/mac/nr/sched_nr_prb_test.cc new file mode 100644 index 000000000..71bc78776 --- /dev/null +++ b/srsenb/test/mac/nr/sched_nr_prb_test.cc @@ -0,0 +1,130 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#include "srsenb/hdr/stack/mac/nr/sched_nr_rb.h" +#include "srsran/common/test_common.h" + +using namespace srsenb; +using namespace srsenb::sched_nr_impl; + +void test_bwp_prb_grant() +{ + // TEST: default ctor + prb_grant grant; + TESTASSERT(grant.is_alloc_type1()); + TESTASSERT(grant.prbs().length() == 0); + + // TEST: assignment of RBGs + rbg_bitmap rbgs(18); + rbgs.set(1); + grant = rbgs; + TESTASSERT(grant.is_alloc_type0() and grant.rbgs().count() == 1); + + // TEST: assignment of PRBs + prb_interval prb_interv(2, 5); + grant = prb_interv; + TESTASSERT(grant.is_alloc_type1() and grant.prbs().length() == 3); + + // TEST: non-default ctor + prb_grant grant2(prb_interv), grant3(rbgs); + TESTASSERT(grant2.is_alloc_type1() and grant2.prbs().length() == 3); + TESTASSERT(grant3.is_alloc_type0() and grant3.rbgs().count() == 1); + + // TEST: copy ctor + prb_grant grant4(grant2), grant5(grant3); + TESTASSERT(grant4.is_alloc_type1() and grant4.prbs().length() == 3); + TESTASSERT(grant5.is_alloc_type0() and grant5.rbgs().count() == 1); + + // TEST: copy assignment + grant = grant3; + TESTASSERT(grant.is_alloc_type0() and grant.rbgs().count() == 1); + grant = grant2; + TESTASSERT(grant.is_alloc_type1() and grant.prbs().length() == 3); +} + +void test_bwp_rb_bitmap() +{ + bwp_rb_bitmap rb_bitmap(275, 0, true); + + TESTASSERT(rb_bitmap.P() == 16); + TESTASSERT(rb_bitmap.rbgs().none()); + TESTASSERT(rb_bitmap.prbs().none()); + TESTASSERT(rb_bitmap.prbs().size() == 275 and rb_bitmap.nof_prbs() == 275); + TESTASSERT(rb_bitmap.rbgs().size() == 18 and rb_bitmap.nof_rbgs() == 18); + + rb_bitmap.add(prb_interval{0, 1}); + TESTASSERT(rb_bitmap.prbs().count() == 1 and rb_bitmap.prbs().test(0)); + TESTASSERT(rb_bitmap.rbgs().count() == 1 and rb_bitmap.rbgs().test(0)); + rb_bitmap.add(prb_interval{2, 4}); + TESTASSERT(rb_bitmap.prbs().count() == 3 and rb_bitmap.prbs().test(2) and not rb_bitmap.prbs().test(1)); + TESTASSERT(rb_bitmap.rbgs().count() == 1 and rb_bitmap.rbgs().test(0)); + + prb_bitmap prbs(rb_bitmap.nof_prbs()); + prbs.set(1); + prbs.set(2); + prbs.set(15); + rb_bitmap.add(prbs); + TESTASSERT(rb_bitmap.prbs().count() == 5 and rb_bitmap.prbs().test(1) and rb_bitmap.prbs().test(15)); + TESTASSERT(rb_bitmap.rbgs().count() == 1 and rb_bitmap.rbgs().test(0)); + prbs.set(16); + rb_bitmap |= prbs; + TESTASSERT(rb_bitmap.prbs().count() == 6 and rb_bitmap.prbs().test(16)); + TESTASSERT(rb_bitmap.rbgs().count() == 2 and rb_bitmap.rbgs().test(1)); + + rbg_bitmap rbgs(rb_bitmap.nof_rbgs()); + rbgs.set(3); + rbgs.set(17); + rb_bitmap |= rbgs; + TESTASSERT(rb_bitmap.prbs().count() == (6 + 16 + 3) and rb_bitmap.prbs().test(rb_bitmap.nof_prbs() - 1)); + TESTASSERT(rb_bitmap.rbgs().count() == 4 and rb_bitmap.rbgs().test(3) and rb_bitmap.rbgs().test(17)); + rbgs.set(0); + rb_bitmap |= rbgs; + TESTASSERT(rb_bitmap.prbs().count() == (16 + 1 + 16 + 3) and rb_bitmap.prbs().test(rb_bitmap.nof_prbs() - 1)); + TESTASSERT(rb_bitmap.rbgs().count() == 4 and rb_bitmap.rbgs().test(3) and rb_bitmap.rbgs().test(17)); + + // TEST: collides operator + TESTASSERT(rb_bitmap.collides(rbgs)); + TESTASSERT(rb_bitmap.collides(prb_interval{0, 2})); +} + +void test_bwp_rb_bitmap_search() +{ + bwp_rb_bitmap rb_bitmap(275, 0, true); + + prb_interval prbs = find_empty_interval_of_length(rb_bitmap.prbs(), 5); + TESTASSERT(prbs == prb_interval(0, 5)); + prbs = find_empty_interval_of_length(rb_bitmap.prbs(), rb_bitmap.prbs().size()); + TESTASSERT(prbs == prb_interval(0, rb_bitmap.prbs().size())); + + rb_bitmap |= prb_interval{1, 5}; + prbs = find_empty_interval_of_length(rb_bitmap.prbs(), rb_bitmap.prbs().size()); + TESTASSERT(prbs == prb_interval(5, rb_bitmap.prbs().size())); + + rb_bitmap |= prb_interval{16, 32}; + prbs = find_empty_interval_of_length(rb_bitmap.prbs(), rb_bitmap.prbs().size()); + TESTASSERT(prbs == prb_interval(32, rb_bitmap.prbs().size())); + + rb_bitmap |= prb_interval{270, 275}; + prbs = find_empty_interval_of_length(rb_bitmap.prbs(), rb_bitmap.prbs().size()); + TESTASSERT(prbs == prb_interval(32, 270)); + prbs = find_empty_interval_of_length(rb_bitmap.prbs(), 1); + TESTASSERT(prbs == prb_interval(0, 1)); + prbs = find_empty_interval_of_length(rb_bitmap.prbs(), 5); + TESTASSERT(prbs == prb_interval(5, 10)); +} + +int main() +{ + test_bwp_prb_grant(); + test_bwp_rb_bitmap(); + test_bwp_rb_bitmap_search(); +} diff --git a/srsenb/test/mac/nr/sched_nr_sim_ue.cc b/srsenb/test/mac/nr/sched_nr_sim_ue.cc index c0a805232..0177147a3 100644 --- a/srsenb/test/mac/nr/sched_nr_sim_ue.cc +++ b/srsenb/test/mac/nr/sched_nr_sim_ue.cc @@ -20,6 +20,7 @@ */ #include "sched_nr_sim_ue.h" +#include "sched_nr_ue_ded_test_suite.h" #include "srsran/common/test_common.h" namespace srsenb { @@ -47,14 +48,31 @@ sched_nr_ue_sim::sched_nr_ue_sim(uint16_t rnti_, int sched_nr_ue_sim::update(const sched_nr_cc_output_res_t& cc_out) { update_dl_harqs(cc_out); + + for (uint32_t i = 0; i < cc_out.dl_cc_result->pdcch_dl.size(); ++i) { + const auto& data = cc_out.dl_cc_result->pdcch_dl[i]; + if (data.dci.ctx.rnti != ctxt.rnti) { + continue; + } + tti_point pdcch_tti = cc_out.tti; + uint32_t k1 = ctxt.ue_cfg.phy_cfg.harq_ack + .dl_data_to_ul_ack[pdcch_tti.sf_idx() % ctxt.ue_cfg.phy_cfg.harq_ack.nof_dl_data_to_ul_ack]; + tti_point uci_tti = pdcch_tti + k1; + + ctxt.cc_list[cc_out.cc].pending_acks[uci_tti.to_uint()]++; + } + + // clear up old slots + ctxt.cc_list[cc_out.cc].pending_acks[(cc_out.tti - 1).to_uint()] = 0; + return SRSRAN_SUCCESS; } void sched_nr_ue_sim::update_dl_harqs(const sched_nr_cc_output_res_t& cc_out) { uint32_t cc = cc_out.cc; - for (uint32_t i = 0; i < cc_out.dl_cc_result->pdschs.size(); ++i) { - const auto& data = cc_out.dl_cc_result->pdcchs[i]; + for (uint32_t i = 0; i < cc_out.dl_cc_result->pdcch_dl.size(); ++i) { + const auto& data = cc_out.dl_cc_result->pdcch_dl[i]; if (data.dci.ctx.rnti != ctxt.rnti) { continue; } @@ -63,7 +81,7 @@ void sched_nr_ue_sim::update_dl_harqs(const sched_nr_cc_output_res_t& cc_out) // It is newtx h.nof_retxs = 0; h.ndi = data.dci.ndi; - h.first_tti_rx = cc_out.tti_rx; + h.first_tti_tx = cc_out.tti; h.dci_loc = data.dci.ctx.location; h.tbs = 100; // TODO } else { @@ -71,7 +89,11 @@ void sched_nr_ue_sim::update_dl_harqs(const sched_nr_cc_output_res_t& cc_out) h.nof_retxs++; } h.active = true; - h.last_tti_rx = cc_out.tti_rx; + h.last_tti_tx = cc_out.tti; + h.last_tti_ack = + h.last_tti_tx + + ctxt.ue_cfg.phy_cfg.harq_ack + .dl_data_to_ul_ack[h.last_tti_tx.sf_idx() % ctxt.ue_cfg.phy_cfg.harq_ack.nof_dl_data_to_ul_ack]; h.nof_txs++; } } @@ -90,6 +112,7 @@ sched_nr_sim_base::sched_nr_sim_base(const sched_nr_interface::sched_cfg_t& cell_params.emplace_back(cc, cell_cfg_list[cc], sched_args); } sched_ptr->cell_cfg(cell_cfg_list); // call parent cfg + TESTASSERT(cell_params.size() > 0); } @@ -103,61 +126,68 @@ int sched_nr_sim_base::add_user(uint16_t rnti, const sched_nr_interface::ue_cfg_ TESTASSERT(ue_db.count(rnti) == 0); sched_ptr->ue_cfg(rnti, ue_cfg_); - ue_db.insert(std::make_pair(rnti, sched_nr_ue_sim(rnti, ue_cfg_, current_tti_rx, preamble_idx))); + ue_db.insert(std::make_pair(rnti, sched_nr_ue_sim(rnti, ue_cfg_, current_tti_tx, preamble_idx))); return SRSRAN_SUCCESS; } -void sched_nr_sim_base::slot_indication(srsran::tti_point tti_rx) +void sched_nr_sim_base::new_slot(srsran::tti_point tti_tx) { - { - std::unique_lock lock(mutex); - logger.set_context(tti_rx.to_uint()); - mac_logger.set_context(tti_rx.to_uint()); - current_tti_rx = tti_rx; - logger.info("---------------- TTI=%d ---------------", tti_rx.to_uint()); - for (auto& ue : ue_db) { - ue_tti_events events; - set_default_tti_events(ue.second.get_ctxt(), events); - set_external_tti_events(ue.second.get_ctxt(), events); - apply_tti_events(ue.second.get_ctxt(), events); - } + std::unique_lock lock(mutex); + while (cc_finished > 0) { + cvar.wait(lock); + } + logger.set_context(tti_tx.to_uint()); + mac_logger.set_context(tti_tx.to_uint()); + logger.info("---------------- TTI=%d ---------------", tti_tx.to_uint()); + current_tti_tx = tti_tx; + cc_finished = cell_params.size(); + for (auto& ue : ue_db) { + ue_nr_tti_events events; + set_default_tti_events(ue.second.get_ctxt(), events); + set_external_tti_events(ue.second.get_ctxt(), events); + apply_tti_events(ue.second.get_ctxt(), events); } - sched_ptr->slot_indication(tti_rx); } void sched_nr_sim_base::update(sched_nr_cc_output_res_t& cc_out) { std::unique_lock lock(mutex); - for (auto& ue_pair : ue_db) { - ue_pair.second.update(cc_out); + + sim_nr_enb_ctxt_t ctxt; + ctxt = get_enb_ctxt(); + test_dl_sched_result(ctxt, cc_out); + + for (auto& u : ue_db) { + u.second.update(cc_out); + } + + if (--cc_finished <= 0) { + cvar.notify_one(); } } -int sched_nr_sim_base::set_default_tti_events(const sim_nr_ue_ctxt_t& ue_ctxt, ue_tti_events& pending_events) +int sched_nr_sim_base::set_default_tti_events(const sim_nr_ue_ctxt_t& ue_ctxt, ue_nr_tti_events& pending_events) { pending_events.cc_list.clear(); pending_events.cc_list.resize(cell_params.size()); - pending_events.tti_rx = current_tti_rx; + pending_events.tti_rx = current_tti_tx; for (uint32_t enb_cc_idx = 0; enb_cc_idx < pending_events.cc_list.size(); ++enb_cc_idx) { auto& cc_feedback = pending_events.cc_list[enb_cc_idx]; cc_feedback.configured = true; - cc_feedback.ue_cc_idx = enb_cc_idx; for (uint32_t pid = 0; pid < SCHED_NR_MAX_HARQ; ++pid) { - auto& dl_h = ue_ctxt.cc_list[cc_feedback.ue_cc_idx].dl_harqs[pid]; - auto& ul_h = ue_ctxt.cc_list[cc_feedback.ue_cc_idx].ul_harqs[pid]; + auto& dl_h = ue_ctxt.cc_list[enb_cc_idx].dl_harqs[pid]; + auto& ul_h = ue_ctxt.cc_list[enb_cc_idx].ul_harqs[pid]; // Set default DL ACK - if (dl_h.active and (dl_h.last_tti_rx + 8) == current_tti_rx) { - cc_feedback.dl_pid = pid; - cc_feedback.dl_ack = true; // default is ACK + if (dl_h.active and (dl_h.last_tti_ack) == current_tti_tx) { + cc_feedback.dl_acks.push_back(ue_nr_tti_events::ack_t{pid, true}); } // Set default UL ACK - if (ul_h.active and (ul_h.last_tti_rx + 8) == current_tti_rx) { - cc_feedback.ul_pid = pid; - cc_feedback.ul_ack = true; + if (ul_h.active and (ul_h.last_tti_tx + 8) == current_tti_tx) { + cc_feedback.ul_acks.emplace_back(ue_nr_tti_events::ack_t{pid, true}); } // TODO: other CSI @@ -167,7 +197,7 @@ int sched_nr_sim_base::set_default_tti_events(const sim_nr_ue_ctxt_t& ue_ctxt, u return SRSRAN_SUCCESS; } -int sched_nr_sim_base::apply_tti_events(sim_nr_ue_ctxt_t& ue_ctxt, const ue_tti_events& events) +int sched_nr_sim_base::apply_tti_events(sim_nr_ue_ctxt_t& ue_ctxt, const ue_nr_tti_events& events) { for (uint32_t enb_cc_idx = 0; enb_cc_idx < events.cc_list.size(); ++enb_cc_idx) { const auto& cc_feedback = events.cc_list[enb_cc_idx]; @@ -175,35 +205,29 @@ int sched_nr_sim_base::apply_tti_events(sim_nr_ue_ctxt_t& ue_ctxt, const ue_tti_ continue; } - if (cc_feedback.dl_pid >= 0) { - auto& h = ue_ctxt.cc_list[cc_feedback.ue_cc_idx].dl_harqs[cc_feedback.dl_pid]; + for (auto& ack : cc_feedback.dl_acks) { + auto& h = ue_ctxt.cc_list[enb_cc_idx].dl_harqs[ack.pid]; - if (cc_feedback.dl_ack) { - logger.info("DL ACK rnti=0x%x tti_dl_tx=%u cc=%d pid=%d", - ue_ctxt.rnti, - to_tx_dl(h.last_tti_rx).to_uint(), - enb_cc_idx, - cc_feedback.dl_pid); + if (ack.ack) { + logger.info( + "DL ACK rnti=0x%x tti_dl_tx=%u cc=%d pid=%d", ue_ctxt.rnti, h.last_tti_tx.to_uint(), enb_cc_idx, ack.pid); } // update scheduler - sched_ptr->dl_ack_info(ue_ctxt.rnti, enb_cc_idx, cc_feedback.dl_pid, cc_feedback.tb, cc_feedback.dl_ack); + sched_ptr->dl_ack_info(ue_ctxt.rnti, enb_cc_idx, h.pid, 0, ack.ack); // update UE sim context - if (cc_feedback.dl_ack or ue_ctxt.is_last_dl_retx(cc_feedback.ue_cc_idx, cc_feedback.dl_pid)) { + if (ack.ack or ue_ctxt.is_last_dl_retx(enb_cc_idx, h.pid)) { h.active = false; } } - if (cc_feedback.ul_pid >= 0) { - auto& h = ue_ctxt.cc_list[cc_feedback.ue_cc_idx].ul_harqs[cc_feedback.ul_pid]; + for (auto& ack : cc_feedback.ul_acks) { + auto& h = ue_ctxt.cc_list[enb_cc_idx].ul_harqs[ack.pid]; - if (cc_feedback.ul_ack) { - logger.info("UL ACK rnti=0x%x, tti_ul_tx=%u, cc=%d pid=%d", - ue_ctxt.rnti, - to_tx_ul(h.last_tti_rx).to_uint(), - enb_cc_idx, - cc_feedback.ul_pid); + if (ack.ack) { + logger.info( + "UL ACK rnti=0x%x, tti_ul_tx=%u, cc=%d pid=%d", ue_ctxt.rnti, h.last_tti_tx.to_uint(), enb_cc_idx, h.pid); } // // update scheduler @@ -217,4 +241,16 @@ int sched_nr_sim_base::apply_tti_events(sim_nr_ue_ctxt_t& ue_ctxt, const ue_tti_ return SRSRAN_SUCCESS; } +sim_nr_enb_ctxt_t sched_nr_sim_base::get_enb_ctxt() const +{ + sim_nr_enb_ctxt_t ctxt; + ctxt.cell_params = cell_params; + + for (auto& ue_pair : ue_db) { + ctxt.ue_db.insert(std::make_pair(ue_pair.first, &ue_pair.second.get_ctxt())); + } + + return ctxt; +} + } // namespace srsenb \ No newline at end of file diff --git a/srsenb/test/mac/nr/sched_nr_sim_ue.h b/srsenb/test/mac/nr/sched_nr_sim_ue.h index a81a22e5c..6e0b8b6ef 100644 --- a/srsenb/test/mac/nr/sched_nr_sim_ue.h +++ b/srsenb/test/mac/nr/sched_nr_sim_ue.h @@ -24,20 +24,49 @@ #include "../sched_sim_ue.h" #include "srsenb/hdr/stack/mac/nr/sched_nr.h" +#include "srsran/adt/circular_array.h" #include namespace srsenb { +const static uint32_t MAX_GRANTS = mac_interface_phy_nr::MAX_GRANTS; + +struct ue_nr_harq_ctxt_t { + bool active = false; + bool ndi = false; + uint32_t pid = 0; + uint32_t nof_txs = 0; + uint32_t nof_retxs = std::numeric_limits::max(); + uint32_t riv = 0; + srsran_dci_location_t dci_loc = {}; + uint32_t tbs = 0; + tti_point last_tti_tx, first_tti_tx, last_tti_ack; +}; struct sched_nr_cc_output_res_t { - tti_point tti_rx; + tti_point tti; uint32_t cc; - sched_nr_interface::dl_tti_request_t* dl_cc_result; - sched_nr_interface::ul_tti_request_t* ul_cc_result; + const sched_nr_interface::dl_sched_t* dl_cc_result; + const sched_nr_interface::ul_sched_t* ul_cc_result; }; struct ue_nr_cc_ctxt_t { - std::array dl_harqs; - std::array ul_harqs; + std::array dl_harqs; + std::array ul_harqs; + srsran::circular_array pending_acks; +}; + +struct ue_nr_tti_events { + struct ack_t { + uint32_t pid; + bool ack; + }; + struct cc_data { + bool configured = false; + srsran::bounded_vector dl_acks; + srsran::bounded_vector ul_acks; + }; + srsran::tti_point tti_rx; + std::vector cc_list; }; struct sim_nr_ue_ctxt_t { @@ -53,6 +82,10 @@ struct sim_nr_ue_ctxt_t { return h.nof_retxs + 1 >= ue_cfg.maxharq_tx; } }; +struct sim_nr_enb_ctxt_t { + srsran::span cell_params; + std::map ue_db; +}; class sched_nr_ue_sim { @@ -84,7 +117,7 @@ public: int add_user(uint16_t rnti, const sched_nr_interface::ue_cfg_t& ue_cfg_, uint32_t preamble_idx); - void slot_indication(srsran::tti_point tti_rx); + void new_slot(srsran::tti_point tti_tx); void update(sched_nr_cc_output_res_t& cc_out); sched_nr_ue_sim& at(uint16_t rnti) { return ue_db.at(rnti); } @@ -110,18 +143,20 @@ public: tti_point get_tti_rx() const { std::lock_guard lock(mutex); - return current_tti_rx; + return current_tti_tx; } + sim_nr_enb_ctxt_t get_enb_ctxt() const; + std::map::iterator begin() { return ue_db.begin(); } std::map::iterator end() { return ue_db.end(); } // configurable by simulator concrete implementation - virtual void set_external_tti_events(const sim_nr_ue_ctxt_t& ue_ctxt, ue_tti_events& pending_events) {} + virtual void set_external_tti_events(const sim_nr_ue_ctxt_t& ue_ctxt, ue_nr_tti_events& pending_events) {} private: - int set_default_tti_events(const sim_nr_ue_ctxt_t& ue_ctxt, ue_tti_events& pending_events); - int apply_tti_events(sim_nr_ue_ctxt_t& ue_ctxt, const ue_tti_events& events); + int set_default_tti_events(const sim_nr_ue_ctxt_t& ue_ctxt, ue_nr_tti_events& pending_events); + int apply_tti_events(sim_nr_ue_ctxt_t& ue_ctxt, const ue_nr_tti_events& events); std::string test_name; srslog::basic_logger& logger; @@ -129,11 +164,13 @@ private: std::unique_ptr sched_ptr; std::vector cell_params; - srsran::tti_point current_tti_rx; + srsran::tti_point current_tti_tx; + int cc_finished = 0; + std::map ue_db; mutable std::mutex mutex; - std::condition_variable cond_var; + std::condition_variable cvar; }; } // namespace srsenb diff --git a/srsenb/test/mac/nr/sched_nr_test.cc b/srsenb/test/mac/nr/sched_nr_test.cc index a218c33f1..d4fd4d1d4 100644 --- a/srsenb/test/mac/nr/sched_nr_test.cc +++ b/srsenb/test/mac/nr/sched_nr_test.cc @@ -21,11 +21,72 @@ #include "sched_nr_sim_ue.h" #include "srsenb/hdr/stack/mac/nr/sched_nr.h" +#include "srsran/common/phy_cfg_nr_default.h" #include "srsran/common/test_common.h" #include "srsran/common/thread_pool.h" +#include namespace srsenb { +using dl_sched_t = sched_nr_interface::dl_sched_t; + +static const srsran::phy_cfg_nr_t default_phy_cfg = + srsran::phy_cfg_nr_default_t{srsran::phy_cfg_nr_default_t::reference_cfg_t{}}; + +srsran_coreset_t get_default_coreset0() +{ + srsran_coreset_t coreset{}; + coreset.id = 0; + coreset.duration = 1; + coreset.precoder_granularity = srsran_coreset_precoder_granularity_reg_bundle; + for (uint32_t i = 0; i < SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE; ++i) { + coreset.freq_resources[i] = i < 8; + } + return coreset; +} + +sched_nr_interface::cell_cfg_t get_default_cell_cfg() +{ + sched_nr_interface::cell_cfg_t cell_cfg{}; + + cell_cfg.carrier = default_phy_cfg.carrier; + cell_cfg.tdd = default_phy_cfg.tdd; + + cell_cfg.bwps.resize(1); + cell_cfg.bwps[0].pdcch = default_phy_cfg.pdcch; + cell_cfg.bwps[0].pdsch = default_phy_cfg.pdsch; + cell_cfg.bwps[0].rb_width = default_phy_cfg.carrier.nof_prb; + + cell_cfg.bwps[0].pdcch.coreset_present[0] = true; + cell_cfg.bwps[0].pdcch.coreset[0] = get_default_coreset0(); + cell_cfg.bwps[0].pdcch.search_space_present[0] = true; + auto& ss = cell_cfg.bwps[0].pdcch.search_space[0]; + ss.id = 0; + ss.coreset_id = 0; + ss.duration = 1; + ss.type = srsran_search_space_type_common_0; + ss.nof_candidates[0] = 1; + ss.nof_candidates[1] = 1; + ss.nof_candidates[2] = 1; + ss.nof_candidates[3] = 0; + ss.nof_candidates[4] = 0; + ss.nof_formats = 1; + ss.formats[0] = srsran_dci_format_nr_1_0; + cell_cfg.bwps[0].pdcch.ra_search_space_present = true; + cell_cfg.bwps[0].pdcch.ra_search_space = cell_cfg.bwps[0].pdcch.search_space[1]; + + return cell_cfg; +} +std::vector get_default_cells_cfg(uint32_t nof_sectors) +{ + std::vector cells; + cells.reserve(nof_sectors); + for (uint32_t i = 0; i < nof_sectors; ++i) { + cells.push_back(get_default_cell_cfg()); + } + return cells; +} + sched_nr_interface::ue_cfg_t get_default_ue_cfg(uint32_t nof_cc) { sched_nr_interface::ue_cfg_t uecfg{}; @@ -33,58 +94,52 @@ sched_nr_interface::ue_cfg_t get_default_ue_cfg(uint32_t nof_cc) for (uint32_t cc = 0; cc < nof_cc; ++cc) { uecfg.carriers[cc].active = true; } - uecfg.phy_cfg.pdcch.coreset_present[0] = true; - uecfg.phy_cfg.pdcch.coreset[0].id = 0; - for (uint32_t i = 0; i < 100 / 6; ++i) { - uecfg.phy_cfg.pdcch.coreset[0].freq_resources[i] = true; - } - uecfg.phy_cfg.pdcch.coreset[0].duration = 1; - uecfg.phy_cfg.pdcch.search_space_present[0] = true; - uecfg.phy_cfg.pdcch.search_space[0].id = 0; - uecfg.phy_cfg.pdcch.search_space[0].coreset_id = 0; - uecfg.phy_cfg.pdcch.search_space[0].duration = 1; - uecfg.phy_cfg.pdcch.search_space[0].type = srsran_search_space_type_common_0; - uecfg.phy_cfg.pdcch.search_space[0].nof_candidates[0] = 1; - uecfg.phy_cfg.pdcch.search_space[0].nof_candidates[1] = 1; - uecfg.phy_cfg.pdcch.search_space[0].nof_candidates[2] = 1; - uecfg.phy_cfg.pdcch.search_space[0].nof_candidates[3] = 1; - uecfg.phy_cfg.pdcch.search_space[0].nof_formats = 1; - uecfg.phy_cfg.pdcch.search_space[0].formats[0] = srsran_dci_format_nr_0_0; + uecfg.phy_cfg = default_phy_cfg; + return uecfg; } struct task_job_manager { - std::mutex mutex; - std::condition_variable cond_var; - int tasks = 0; - int res_count = 0; - int pdsch_count = 0; - int max_tasks = std::numeric_limits::max() / 2; - srslog::basic_logger& test_logger = srslog::fetch_basic_logger("TEST"); + std::mutex mutex; + int res_count = 0; + int pdsch_count = 0; + srslog::basic_logger& test_logger = srslog::fetch_basic_logger("TEST"); + struct slot_guard { + int count = 0; + std::condition_variable cvar; + }; + srsran::bounded_vector slot_counter{}; - void start_task() + explicit task_job_manager(int max_concurrent_slots = 4) : slot_counter(max_concurrent_slots) {} + + void start_slot(tti_point tti, int nof_sectors) { std::unique_lock lock(mutex); - while (tasks >= max_tasks) { - cond_var.wait(lock); + auto& sl = slot_counter[tti.to_uint() % slot_counter.size()]; + while (sl.count > 0) { + sl.cvar.wait(lock); } - tasks++; + sl.count = nof_sectors; } - void finish_task(const sched_nr_interface::tti_request_t& res) + void finish_cc(tti_point tti, const dl_sched_t& dl_res, const sched_nr_interface::ul_sched_t& ul_res) { std::unique_lock lock(mutex); - TESTASSERT(res.dl_res.pdschs.size() <= 1); + TESTASSERT(dl_res.pdcch_dl.size() <= 1); res_count++; - pdsch_count += res.dl_res.pdschs.size(); - if (tasks-- >= max_tasks or tasks == 0) { - cond_var.notify_one(); + pdsch_count += dl_res.pdcch_dl.size(); + auto& sl = slot_counter[tti.to_uint() % slot_counter.size()]; + if (--sl.count == 0) { + sl.cvar.notify_one(); } } void wait_task_finish() { std::unique_lock lock(mutex); - while (tasks > 0) { - cond_var.wait(lock); + for (auto& sl : slot_counter) { + while (sl.count > 0) { + sl.cvar.wait(lock); + } + sl.count = 1; } } void print_results() const @@ -96,13 +151,11 @@ struct task_job_manager { void sched_nr_cfg_serialized_test() { - auto& mac_logger = srslog::fetch_basic_logger("MAC"); - uint32_t max_nof_ttis = 1000, nof_sectors = 2; task_job_manager tasks; sched_nr_interface::sched_cfg_t cfg; - std::vector cells_cfg(nof_sectors); + std::vector cells_cfg = get_default_cells_cfg(nof_sectors); sched_nr_sim_base sched_tester(cfg, cells_cfg, "Serialized Test"); @@ -110,50 +163,71 @@ void sched_nr_cfg_serialized_test() sched_tester.add_user(0x46, uecfg, 0); + std::vector count_per_cc(nof_sectors, 0); for (uint32_t nof_ttis = 0; nof_ttis < max_nof_ttis; ++nof_ttis) { - tti_point tti(nof_ttis % 10240); - sched_tester.slot_indication(tti); + tti_point tti_rx(nof_ttis % 10240); + tti_point tti_tx = tti_rx + TX_ENB_DELAY; + tasks.start_slot(tti_rx, nof_sectors); + sched_tester.new_slot(tti_tx); for (uint32_t cc = 0; cc < cells_cfg.size(); ++cc) { - tasks.start_task(); - sched_nr_interface::tti_request_t res; - TESTASSERT(sched_tester.get_sched()->generate_sched_result(tti, cc, res) == SRSRAN_SUCCESS); - sched_nr_cc_output_res_t out{tti, cc, &res.dl_res, &res.ul_res}; + sched_nr_interface::dl_sched_t dl_res; + sched_nr_interface::ul_sched_t ul_res; + auto tp1 = std::chrono::steady_clock::now(); + TESTASSERT(sched_tester.get_sched()->get_dl_sched(tti_tx, cc, dl_res) == SRSRAN_SUCCESS); + TESTASSERT(sched_tester.get_sched()->get_ul_sched(tti_tx, cc, ul_res) == SRSRAN_SUCCESS); + auto tp2 = std::chrono::steady_clock::now(); + count_per_cc[cc] += std::chrono::duration_cast(tp2 - tp1).count(); + sched_nr_cc_output_res_t out{tti_tx, cc, &dl_res, &ul_res}; sched_tester.update(out); - tasks.finish_task(res); - TESTASSERT(res.dl_res.pdschs.size() == 1); + tasks.finish_cc(tti_rx, dl_res, ul_res); + TESTASSERT(not srsran_tdd_nr_is_dl(&cells_cfg[cc].tdd, 0, (tti_tx).sf_idx()) or dl_res.pdcch_dl.size() == 1); } } tasks.print_results(); - TESTASSERT(tasks.pdsch_count == (int)(max_nof_ttis * nof_sectors)); + TESTASSERT(tasks.pdsch_count == (int)(max_nof_ttis * nof_sectors * 0.6)); + + double final_avg_usec = 0; + for (uint32_t cc = 0; cc < cells_cfg.size(); ++cc) { + final_avg_usec += count_per_cc[cc]; + } + final_avg_usec = final_avg_usec / 1000.0 / max_nof_ttis; + printf("Total time taken per slot: %f usec\n", final_avg_usec); } void sched_nr_cfg_parallel_cc_test() { - auto& mac_logger = srslog::fetch_basic_logger("MAC"); - + uint32_t nof_sectors = 2; uint32_t max_nof_ttis = 1000; task_job_manager tasks; sched_nr_interface::sched_cfg_t cfg; - std::vector cells_cfg(4); + std::vector cells_cfg = get_default_cells_cfg(nof_sectors); sched_nr_sim_base sched_tester(cfg, cells_cfg, "Parallel CC Test"); sched_nr_interface::ue_cfg_t uecfg = get_default_ue_cfg(cells_cfg.size()); sched_tester.add_user(0x46, uecfg, 0); + std::array, SRSRAN_MAX_CARRIERS> nano_count{}; for (uint32_t nof_ttis = 0; nof_ttis < max_nof_ttis; ++nof_ttis) { - tti_point tti(nof_ttis % 10240); - sched_tester.slot_indication(tti); + tti_point tti_rx(nof_ttis % 10240); + tti_point tti_tx = tti_rx + TX_ENB_DELAY; + tasks.start_slot(tti_tx, nof_sectors); + sched_tester.new_slot(tti_tx); for (uint32_t cc = 0; cc < cells_cfg.size(); ++cc) { - tasks.start_task(); - srsran::get_background_workers().push_task([cc, tti, &tasks, &sched_tester]() { - sched_nr_interface::tti_request_t res; - TESTASSERT(sched_tester.get_sched()->generate_sched_result(tti, cc, res) == SRSRAN_SUCCESS); - sched_nr_cc_output_res_t out{tti, cc, &res.dl_res, &res.ul_res}; + srsran::get_background_workers().push_task([cc, tti_tx, &tasks, &sched_tester, &nano_count]() { + sched_nr_interface::dl_sched_t dl_res; + sched_nr_interface::ul_sched_t ul_res; + auto tp1 = std::chrono::steady_clock::now(); + TESTASSERT(sched_tester.get_sched()->get_dl_sched(tti_tx, cc, dl_res) == SRSRAN_SUCCESS); + TESTASSERT(sched_tester.get_sched()->get_ul_sched(tti_tx, cc, ul_res) == SRSRAN_SUCCESS); + auto tp2 = std::chrono::steady_clock::now(); + nano_count[cc].fetch_add(std::chrono::duration_cast(tp2 - tp1).count(), + std::memory_order_relaxed); + sched_nr_cc_output_res_t out{tti_tx, cc, &dl_res, &ul_res}; sched_tester.update(out); - tasks.finish_task(res); + tasks.finish_cc(tti_tx, dl_res, ul_res); }); } } @@ -161,6 +235,14 @@ void sched_nr_cfg_parallel_cc_test() tasks.wait_task_finish(); tasks.print_results(); + TESTASSERT(tasks.pdsch_count == (int)(max_nof_ttis * nof_sectors * 0.6)); + + double final_avg_usec = 0; + for (uint32_t i = 0; i < nof_sectors; ++i) { + final_avg_usec += nano_count[i]; + } + final_avg_usec = final_avg_usec / 1000.0 / max_nof_ttis / nof_sectors; + printf("Total time taken per slot [usec]: %f\n", final_avg_usec); } void sched_nr_cfg_parallel_sf_test() @@ -170,25 +252,33 @@ void sched_nr_cfg_parallel_sf_test() task_job_manager tasks; sched_nr_interface::sched_cfg_t cfg; - cfg.nof_concurrent_subframes = 2; - std::vector cells_cfg; - cells_cfg.resize(nof_sectors); + cfg.nof_concurrent_subframes = 2; + std::vector cells_cfg = get_default_cells_cfg(nof_sectors); - sched_nr sched(cfg); - sched.cell_cfg(cells_cfg); + sched_nr_sim_base sched_tester(cfg, cells_cfg, "Parallel SF Test"); sched_nr_interface::ue_cfg_t uecfg = get_default_ue_cfg(cells_cfg.size()); - sched.ue_cfg(0x46, uecfg); + sched_tester.add_user(0x46, uecfg, 0); + std::array, SRSRAN_MAX_CARRIERS> nano_count{}; for (uint32_t nof_ttis = 0; nof_ttis < max_nof_ttis; ++nof_ttis) { - tti_point tti(nof_ttis % 10240); - sched.slot_indication(tti); + tti_point tti_rx(nof_ttis % 10240); + tti_point tti_tx = tti_rx + TX_ENB_DELAY; + tasks.start_slot(tti_tx, nof_sectors); + sched_tester.new_slot(tti_tx); for (uint32_t cc = 0; cc < cells_cfg.size(); ++cc) { - tasks.start_task(); - srsran::get_background_workers().push_task([cc, &sched, tti, &tasks]() { - sched_nr_interface::tti_request_t res; - TESTASSERT(sched.generate_sched_result(tti, cc, res) == SRSRAN_SUCCESS); - tasks.finish_task(res); + srsran::get_background_workers().push_task([cc, tti_tx, &sched_tester, &tasks, &nano_count]() { + sched_nr_interface::dl_sched_t dl_res; + sched_nr_interface::ul_sched_t ul_res; + auto tp1 = std::chrono::steady_clock::now(); + TESTASSERT(sched_tester.get_sched()->get_dl_sched(tti_tx, cc, dl_res) == SRSRAN_SUCCESS); + TESTASSERT(sched_tester.get_sched()->get_ul_sched(tti_tx, cc, ul_res) == SRSRAN_SUCCESS); + auto tp2 = std::chrono::steady_clock::now(); + nano_count[cc].fetch_add(std::chrono::duration_cast(tp2 - tp1).count(), + std::memory_order_relaxed); + sched_nr_cc_output_res_t out{tti_tx, cc, &dl_res, &ul_res}; + sched_tester.update(out); + tasks.finish_cc(tti_tx, dl_res, ul_res); }); } } @@ -196,6 +286,13 @@ void sched_nr_cfg_parallel_sf_test() tasks.wait_task_finish(); tasks.print_results(); + + double final_avg_usec = 0; + for (uint32_t i = 0; i < nof_sectors; ++i) { + final_avg_usec += nano_count[i]; + } + final_avg_usec = final_avg_usec / 1000.0 / max_nof_ttis / nof_sectors; + printf("Total time taken per slot [usec]: %f\n", final_avg_usec); } } // namespace srsenb @@ -203,16 +300,16 @@ void sched_nr_cfg_parallel_sf_test() int main() { auto& test_logger = srslog::fetch_basic_logger("TEST"); - test_logger.set_level(srslog::basic_levels::debug); + test_logger.set_level(srslog::basic_levels::info); auto& mac_logger = srslog::fetch_basic_logger("MAC"); - mac_logger.set_level(srslog::basic_levels::debug); + mac_logger.set_level(srslog::basic_levels::info); auto& pool_logger = srslog::fetch_basic_logger("POOL"); - pool_logger.set_level(srslog::basic_levels::debug); + pool_logger.set_level(srslog::basic_levels::info); // Start the log backend. srslog::init(); - srsran::get_background_workers().set_nof_workers(8); + srsran::get_background_workers().set_nof_workers(6); srsenb::sched_nr_cfg_serialized_test(); srsenb::sched_nr_cfg_parallel_cc_test(); diff --git a/srsenb/test/mac/nr/sched_nr_ue_ded_test_suite.cc b/srsenb/test/mac/nr/sched_nr_ue_ded_test_suite.cc new file mode 100644 index 000000000..13eba2fb7 --- /dev/null +++ b/srsenb/test/mac/nr/sched_nr_ue_ded_test_suite.cc @@ -0,0 +1,61 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#include "sched_nr_ue_ded_test_suite.h" +#include "srsenb/hdr/stack/mac/nr/sched_nr_rb_grid.h" +#include "srsran/common/test_common.h" + +namespace srsenb { + +using namespace srsenb::sched_nr_impl; + +void test_dl_sched_result(const sim_nr_enb_ctxt_t& enb_ctxt, const sched_nr_cc_output_res_t& cc_out) +{ + tti_point pdcch_tti = cc_out.tti; + const pdcch_dl_list_t& pdcchs = cc_out.dl_cc_result->pdcch_dl; + const pdsch_list_t& pdschs = cc_out.dl_cc_result->pdsch; + + // Iterate over UE PDCCH allocations + for (const pdcch_dl_t& pdcch : pdcchs) { + if (pdcch.dci.ctx.rnti_type != srsran_rnti_type_c) { + continue; + } + const sim_nr_ue_ctxt_t& ue = *enb_ctxt.ue_db.at(pdcch.dci.ctx.rnti); + uint32_t k1 = ue.ue_cfg.phy_cfg.harq_ack + .dl_data_to_ul_ack[pdcch_tti.sf_idx() % ue.ue_cfg.phy_cfg.harq_ack.nof_dl_data_to_ul_ack]; + + // CHECK: Carrier activation + TESTASSERT(ue.ue_cfg.carriers[cc_out.cc].active); + + // CHECK: Coreset chosen/DCI content + TESTASSERT(ue.ue_cfg.phy_cfg.pdcch.coreset_present[pdcch.dci.ctx.coreset_id]); + const auto& coreset = ue.ue_cfg.phy_cfg.pdcch.coreset[pdcch.dci.ctx.coreset_id]; + TESTASSERT(coreset.id == pdcch.dci.ctx.coreset_id); + TESTASSERT(pdcch.dci.ctx.format == srsran_dci_format_nr_1_0 or pdcch.dci.ctx.format == srsran_dci_format_nr_1_1); + + // CHECK: UCI + if (pdcch.dci.ctx.format == srsran_dci_format_nr_1_0) { + TESTASSERT(pdcch.dci.harq_feedback == k1 - 1); + } else { + TESTASSERT(pdcch.dci.harq_feedback == pdcch_tti.sf_idx()); + } + TESTASSERT(ue.cc_list[cc_out.cc].pending_acks[(pdcch_tti + k1).to_uint()] % 4 == pdcch.dci.dai); + } + + for (const pdsch_t& pdsch : pdschs) { + TESTASSERT(pdsch.sch.grant.tb[0].softbuffer.tx != nullptr); + TESTASSERT(pdsch.sch.grant.tb[0].softbuffer.tx->buffer_b != nullptr); + TESTASSERT(pdsch.sch.grant.tb[0].softbuffer.tx->max_cb > 0); + } +} + +} // namespace srsenb diff --git a/srsenb/test/mac/nr/sched_nr_ue_ded_test_suite.h b/srsenb/test/mac/nr/sched_nr_ue_ded_test_suite.h new file mode 100644 index 000000000..3bc19f74f --- /dev/null +++ b/srsenb/test/mac/nr/sched_nr_ue_ded_test_suite.h @@ -0,0 +1,24 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#ifndef SRSRAN_SCHED_NR_UE_DED_TEST_SUITE_H +#define SRSRAN_SCHED_NR_UE_DED_TEST_SUITE_H + +#include "sched_nr_sim_ue.h" + +namespace srsenb { + +void test_dl_sched_result(const sim_nr_enb_ctxt_t& enb_ctxt, const sched_nr_cc_output_res_t& cc_out); + +} + +#endif // SRSRAN_SCHED_NR_UE_DED_TEST_SUITE_H diff --git a/srsenb/test/rrc/test_helpers.cc b/srsenb/test/rrc/test_helpers.cc index 1aa8e2d8a..55049e05f 100644 --- a/srsenb/test/rrc/test_helpers.cc +++ b/srsenb/test/rrc/test_helpers.cc @@ -37,7 +37,7 @@ int parse_default_cfg_phy(rrc_cfg_t* rrc_cfg, phy_cfg_t* phy_cfg, srsenb::all_ar *rrc_cfg = {}; args.enb_files.sib_config = argparse::repository_dir + "/sib.conf.example"; args.enb_files.rr_config = argparse::repository_dir + "/rr.conf.example"; - args.enb_files.drb_config = argparse::repository_dir + "/drb.conf.example"; + args.enb_files.rb_config = argparse::repository_dir + "/rb.conf.example"; srslog::fetch_basic_logger("TEST").debug("sib file path=%s", args.enb_files.sib_config.c_str()); args.enb.enb_id = 0x19B; @@ -57,7 +57,7 @@ int parse_default_cfg(rrc_cfg_t* rrc_cfg, srsenb::all_args_t& args) *rrc_cfg = {}; args.enb_files.sib_config = argparse::repository_dir + "/sib.conf.example"; args.enb_files.rr_config = argparse::repository_dir + "/rr.conf.example"; - args.enb_files.drb_config = argparse::repository_dir + "/drb.conf.example"; + args.enb_files.rb_config = argparse::repository_dir + "/rb.conf.example"; srslog::fetch_basic_logger("TEST").debug("sib file path=%s", args.enb_files.sib_config.c_str()); args.enb.enb_id = 0x19B; @@ -193,4 +193,4 @@ bool is_cell_cfg_equal(const meas_cell_cfg_t& cfg, const cells_to_add_mod_s& cel return cfg.pci == cell.pci and cell.cell_individual_offset.to_number() == (int8_t)round(cfg.q_offset); } -} // namespace srsenb \ No newline at end of file +} // namespace srsenb diff --git a/srsue/hdr/phy/nr/state.h b/srsue/hdr/phy/nr/state.h index b16858c56..b6058f44a 100644 --- a/srsue/hdr/phy/nr/state.h +++ b/srsue/hdr/phy/nr/state.h @@ -325,7 +325,7 @@ public: } // Initialise counters - uint32_t sr_count_all = (uint32_t)n; + uint32_t sr_count_all = (uint32_t)n; // Number of opportunities in this TTI uint32_t sr_count_positive = 0; // Iterate all opportunities and check if there is a pending SR @@ -342,10 +342,10 @@ public: } // Configure SR fields in UCI data - uci_data.cfg.pucch.sr_resource_id = sr_resource_id[0]; - uci_data.cfg.o_sr = srsran_ra_ul_nr_nof_sr_bits(sr_count_all); - uci_data.cfg.pucch.sr_positive_present = sr_count_positive > 0; - uci_data.value.sr = sr_count_positive; + uci_data.cfg.pucch.sr_resource_id = sr_resource_id[0]; + uci_data.cfg.o_sr = srsran_ra_ul_nr_nof_sr_bits(sr_count_all); + uci_data.cfg.sr_positive_present = sr_count_positive > 0; + uci_data.value.sr = sr_count_positive; } void get_periodic_csi(const uint32_t& tti, srsran_uci_data_nr_t& uci_data) diff --git a/srsue/hdr/phy/nr/worker_pool.h b/srsue/hdr/phy/nr/worker_pool.h index 384f95b61..fe855abad 100644 --- a/srsue/hdr/phy/nr/worker_pool.h +++ b/srsue/hdr/phy/nr/worker_pool.h @@ -29,7 +29,7 @@ namespace srsue { namespace nr { -class worker_pool +class worker_pool : public srsue::phy_interface_stack_nr { private: srslog::basic_logger& logger; @@ -46,12 +46,18 @@ public: sf_worker* wait_worker(uint32_t tti); void start_worker(sf_worker* w); void stop(); - void send_prach(uint32_t prach_occasion, uint32_t preamble_index, int preamble_received_target_power); - int set_ul_grant(std::array array, uint16_t rnti, srsran_rnti_type_t rnti_type); - bool set_config(const srsran::phy_cfg_nr_t& cfg); - bool has_valid_sr_resource(uint32_t sr_id); - void clear_pending_grants(); + void send_prach(const uint32_t prach_occasion, + const int preamble_index, + const float preamble_received_target_power, + const float ta_base_sec = 0.0f) override; + int set_ul_grant(std::array array, + uint16_t rnti, + srsran_rnti_type_t rnti_type) override; + bool set_config(const srsran::phy_cfg_nr_t& cfg) override; + bool has_valid_sr_resource(uint32_t sr_id) override; + void clear_pending_grants() override; void get_metrics(phy_metrics_t& m); + int tx_request(const tx_request_t& request) override; }; } // namespace nr diff --git a/srsue/hdr/phy/vnf_phy_nr.h b/srsue/hdr/phy/vnf_phy_nr.h deleted file mode 100644 index 8f4512ef4..000000000 --- a/srsue/hdr/phy/vnf_phy_nr.h +++ /dev/null @@ -1,82 +0,0 @@ -/** - * Copyright 2013-2021 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/. - * - */ - -#ifndef SRSUE_VNF_PHY_NR_H -#define SRSUE_VNF_PHY_NR_H - -#include "srsenb/hdr/phy/phy_common.h" -#include "srsran/common/basic_vnf.h" -#include "srsran/interfaces/enb_metrics_interface.h" -#include "srsran/interfaces/radio_interfaces.h" -#include "srsran/interfaces/ue_interfaces.h" -#include "srsran/interfaces/ue_nr_interfaces.h" -#include "srsue/hdr/phy/ue_nr_phy_base.h" - -namespace srsue { - -class vnf_phy_nr : public srsue::ue_phy_base, public srsue::phy_interface_stack_nr -{ -public: - vnf_phy_nr() = default; - ~vnf_phy_nr(); - - int init(const srsue::phy_args_t& args, srsue::stack_interface_phy_nr* stack_); - - int init(const srsue::phy_args_t& args_) override; - - void set_earfcn(std::vector earfcns); - - void stop() override; - - void wait_initialize() override; - void get_metrics(const srsran::srsran_rat_t& rat, phy_metrics_t* m) override; - - std::string get_type() override { return "vnf_nr"; }; - - void start_plot() override; - - // RRC interface - bool set_config(const srsran::phy_cfg_nr_t& cfg) override; - - // MAC interface - int tx_request(const tx_request_t& request) override; - int set_ul_grant(std::array, uint16_t rnti, srsran_rnti_type_t rnti_type) override - { - return SRSRAN_SUCCESS; - }; - void send_prach(const uint32_t preamble_idx, - const int prach_occasion, - const float target_power_dbm, - const float ta_base_sec = 0.0f) override{}; - bool has_valid_sr_resource(uint32_t sr_id) override; - void clear_pending_grants() override; - -private: - std::unique_ptr vnf; - - srsue::stack_interface_phy_nr* stack = nullptr; - - bool initialized = false; -}; - -} // namespace srsue - -#endif // SRSUE_VNF_PHY_NR_H diff --git a/srsue/hdr/stack/mac/proc_ra.h b/srsue/hdr/stack/mac/proc_ra.h index 157d0764e..2c484f513 100644 --- a/srsue/hdr/stack/mac/proc_ra.h +++ b/srsue/hdr/stack/mac/proc_ra.h @@ -155,9 +155,9 @@ private: std::atomic transmitted_contention_id = {0}; std::atomic transmitted_crnti = {0}; - bool started_by_pdcch = false; - uint32_t rar_grant_nbytes = 0; - bool rar_received = false; + bool started_by_pdcch = false; + uint32_t rar_grant_nbytes = 0; + std::atomic rar_received = {false}; }; } // namespace srsue diff --git a/srsue/hdr/stack/upper/nas.h b/srsue/hdr/stack/upper/nas.h index 9cbc30d66..1b0acfbfb 100644 --- a/srsue/hdr/stack/upper/nas.h +++ b/srsue/hdr/stack/upper/nas.h @@ -22,6 +22,7 @@ #ifndef SRSUE_NAS_H #define SRSUE_NAS_H +#include "nas_base.h" #include "srsran/asn1/liblte_mme.h" #include "srsran/common/buffer_pool.h" #include "srsran/common/common.h" @@ -43,7 +44,7 @@ class usim_interface_nas; class gw_interface_nas; class rrc_interface_nas; -class nas : public nas_interface_rrc, public srsran::timer_callback +class nas : public nas_interface_rrc, public srsran::timer_callback, public nas_base { public: explicit nas(srsran::task_sched_handle task_sched_); @@ -80,11 +81,7 @@ public: // timer callback void timer_expired(uint32_t timeout_id) override; - // PCAP - void start_pcap(srsran::nas_pcap* pcap_) { pcap = pcap_; } - private: - srslog::basic_logger& logger; rrc_interface_nas* rrc = nullptr; usim_interface_nas* usim = nullptr; gw_interface_nas* gw = nullptr; @@ -101,18 +98,6 @@ private: std::vector known_plmns; - // Security context - struct nas_sec_ctxt { - uint8_t ksi; - uint8_t k_asme[32]; - uint32_t tx_count; - uint32_t rx_count; - uint32_t k_enb_count; - srsran::CIPHERING_ALGORITHM_ID_ENUM cipher_algo; - srsran::INTEGRITY_ALGORITHM_ID_ENUM integ_algo; - LIBLTE_MME_EPS_MOBILE_ID_GUTI_STRUCT guti; - }; - typedef enum { DEFAULT_EPS_BEARER = 0, DEDICATED_EPS_BEARER } eps_bearer_type_t; typedef struct { @@ -127,7 +112,6 @@ private: bool have_guti = false; bool have_ctxt = false; - nas_sec_ctxt ctxt = {}; bool auth_request = false; uint8_t current_sec_hdr = LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS; @@ -164,23 +148,13 @@ private: // Security bool eia_caps[8] = {}; bool eea_caps[8] = {}; - uint8_t k_nas_enc[32] = {}; - uint8_t k_nas_int[32] = {}; // Airplane mode simulation typedef enum { DISABLED = 0, ENABLED } airplane_mode_state_t; airplane_mode_state_t airplane_mode_state = {}; srsran::timer_handler::unique_timer airplane_mode_sim_timer; - // PCAP - srsran::nas_pcap* pcap = nullptr; - // Security - void - integrity_generate(uint8_t* key_128, uint32_t count, uint8_t direction, uint8_t* msg, uint32_t msg_len, uint8_t* mac); - bool integrity_check(srsran::byte_buffer_t* pdu); - void cipher_encrypt(srsran::byte_buffer_t* pdu); - void cipher_decrypt(srsran::byte_buffer_t* pdu); int apply_security_config(srsran::unique_byte_buffer_t& pdu, uint8_t sec_hdr_type); void reset_security_context(); void set_k_enb_count(uint32_t count); diff --git a/srsue/hdr/stack/upper/nas_5g.h b/srsue/hdr/stack/upper/nas_5g.h new file mode 100644 index 000000000..34171d605 --- /dev/null +++ b/srsue/hdr/stack/upper/nas_5g.h @@ -0,0 +1,98 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#ifndef SRSUE_NAS_5G_H +#define SRSUE_NAS_5G_H + +#include "nas_base.h" +#include "srsran/common/buffer_pool.h" +#include "srsran/common/common.h" +#include "srsran/common/nas_pcap.h" +#include "srsran/common/security.h" +#include "srsran/common/stack_procedure.h" +#include "srsran/common/task_scheduler.h" +#include "srsran/interfaces/ue_gw_interfaces.h" +#include "srsran/interfaces/ue_nas_interfaces.h" +#include "srsran/interfaces/ue_rrc_interfaces.h" +#include "srsran/interfaces/ue_usim_interfaces.h" +#include "srsran/srslog/srslog.h" +#include "srsue/hdr/stack/upper/nas_5gmm_state.h" +#include "srsue/hdr/stack/upper/nas_config.h" + +using srsran::byte_buffer_t; + +namespace srsue { + +class nas_5g : public nas_base, public nas_5g_interface_rrc_nr, public nas_5g_interface_procedures +{ +public: + explicit nas_5g(srsran::task_sched_handle task_sched_); + virtual ~nas_5g(); + int init(usim_interface_nas* usim_, rrc_nr_interface_nas_5g* rrc_nr_, gw_interface_nas* gw_, const nas_args_t& cfg_); + void stop(); + void run_tti(); + + // Stack+RRC interface + bool is_registered(); + + // timer callback + void timer_expired(uint32_t timeout_id); + + // Stack interface + int switch_on(); + int switch_off(); + int enable_data(); + int disable_data(); + int start_service_request(); + +private: + rrc_nr_interface_nas_5g* rrc_nr = nullptr; + usim_interface_nas* usim = nullptr; + gw_interface_nas* gw = nullptr; + + bool running = false; + + nas_args_t cfg = {}; + mm5g_state_t state = {}; + + // Security + bool ia5g_caps[8] = {}; + bool ea5g_caps[8] = {}; + + // timers + srsran::task_sched_handle task_sched; + srsran::timer_handler::unique_timer t3502; // started when registration failure and the attempt counter is equal to 5 + srsran::timer_handler::unique_timer t3510; // started when transmission of REGISTRATION REQUEST message. ON EXPIRY: + // start T3511 or T3502 as specified in subclause 5.5.1.2.7 + srsran::timer_handler::unique_timer t3511; // started when registration failure due to lower layer failure + srsran::timer_handler::unique_timer t3521; // started when detach request is sent + srsran::timer_handler::unique_timer reregistration_timer; // started to trigger delayed re-attach + + // Values according to TS 24.501 Sec 10.2 + const uint32_t t3502_duration_ms = 12 * 60 * 1000; // 12m + const uint32_t t3510_duration_ms = 15 * 1000; // 15s + const uint32_t t3511_duration_ms = 10 * 1000; // 10s + const uint32_t t3521_duration_ms = 15 * 1000; // 15s + const uint32_t reregistration_timer_duration_ms = 2 * 1000; // 2s (arbitrarily chosen to delay re-attach) + + srsran::proc_manager_list_t callbacks; + + // Procedures + // Forward declartion + class registration_procedure; + + srsran::proc_t registration_proc; + + int send_registration_request(); +}; +} // namespace srsue +#endif \ No newline at end of file diff --git a/srsue/hdr/stack/upper/nas_5g_procedures.h b/srsue/hdr/stack/upper/nas_5g_procedures.h new file mode 100644 index 000000000..85ef15bcd --- /dev/null +++ b/srsue/hdr/stack/upper/nas_5g_procedures.h @@ -0,0 +1,40 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#ifndef SRSUE_NAS_5G_PROCEDURES_H_ +#define SRSUE_NAS_5G_PROCEDURES_H_ + +#include "srsue/hdr/stack/upper/nas_5g.h" + +namespace srsue { + +/** + * @brief 5G NAS registration procedure + * + * Specified in 24 501 V16.7.0 + * 5GMM specific procedures + * 5.5.1 Registration procedure + */ +class nas_5g::registration_procedure +{ +public: + explicit registration_procedure(nas_5g_interface_procedures* parent_nas_); + srsran::proc_outcome_t init(); + srsran::proc_outcome_t step(); + static const char* name() { return "Registration Procedure"; } + +private: + nas_5g_interface_procedures* parent_nas; +}; +} // namespace srsue + +#endif // SRSUE_NAS_5G_PROCEDURES_H_ \ No newline at end of file diff --git a/srsue/hdr/stack/upper/nas_5gmm_state.h b/srsue/hdr/stack/upper/nas_5gmm_state.h new file mode 100644 index 000000000..29fb2a983 --- /dev/null +++ b/srsue/hdr/stack/upper/nas_5gmm_state.h @@ -0,0 +1,88 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#ifndef SRSUE_NR_NAS_MM5G_STATE_H +#define SRSUE_NR_NAS_MM5G_STATE_H + +#include "srsran/srslog/srslog.h" +#include +#include + +namespace srsue { + +// 5GMM states (3GPP 24.501 v16.07.0) +class mm5g_state_t +{ +public: + enum class state_t { + null = 0, + deregistered, + registered_initiated, + registered, + deregistered_initiated, + service_request_initiated, + }; + + // 5GMM-DEREGISTERED sub-states (3GPP 24.501 v16.07.0) + enum class deregistered_substate_t { + null = 0, // This should be used when not in mm5g-DEREGISTERED + normal_service, + limited_service, + attempting_to_registration, + plmn_search, + no_supi, + no_cell_available, + e_call_inactive, + initial_registration_needed, + }; + + // 5GMM-DEREGISTERED sub-states (3GPP 24.501 v16.07.0) + enum class registered_substate_t { + null = 0, // This should be used when not in mm5g-REGISTERED + normal_service, + non_allowed_service, + attempting_registration_update, + limited_service, + plmn_search, + no_cell_available, + update_needed, + }; + + // FSM setters + void set_null(); + void set_deregistered(deregistered_substate_t substate); + void set_deregistered_initiated(); + void set_registered(registered_substate_t substate); + void set_registered_initiated(); + void set_service_request_initiated(); + + // FSM getters + state_t get_state() { return state; } + deregistered_substate_t get_deregistered_substate() { return deregistered_substate; } + registered_substate_t get_registered_substate() { return registered_substate; } + + // Text Helpers + const std::string get_full_state_text(); + +private: + std::atomic state{state_t::null}; + deregistered_substate_t deregistered_substate = deregistered_substate_t::null; + registered_substate_t registered_substate = registered_substate_t::null; + srslog::basic_logger& logger = srslog::fetch_basic_logger("NAS-5G"); +}; + +const char* mm5g_state_text(mm5g_state_t::state_t type); +const char* mm5g_deregistered_substate_text(mm5g_state_t::deregistered_substate_t type); +const char* mm5g_registered_substate_text(mm5g_state_t::registered_substate_t type); + +} // namespace srsue +#endif diff --git a/srsue/hdr/stack/upper/nas_base.h b/srsue/hdr/stack/upper/nas_base.h new file mode 100644 index 000000000..225278146 --- /dev/null +++ b/srsue/hdr/stack/upper/nas_base.h @@ -0,0 +1,67 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#ifndef SRSUE_NAS_BASE_H +#define SRSUE_NAS_BASE_H + +#include "srsran/asn1/liblte_mme.h" +#include "srsran/common/buffer_pool.h" +#include "srsran/common/common.h" +#include "srsran/common/nas_pcap.h" +#include "srsran/common/security.h" +#include "srsran/common/string_helpers.h" +#include "srsran/config.h" + +using srsran::byte_buffer_t; + +namespace srsue { + +class nas_base +{ +public: + nas_base(const std::string& type_); + // PCAP + void start_pcap(srsran::nas_pcap* pcap_) { pcap = pcap_; } + +protected: + srslog::basic_logger& logger; + // PCAP + srsran::nas_pcap* pcap = nullptr; + + // Security context + struct nas_sec_ctxt { + uint8_t ksi; + uint8_t k_asme[32]; + uint32_t tx_count; + uint32_t rx_count; + uint32_t k_enb_count; + srsran::CIPHERING_ALGORITHM_ID_ENUM cipher_algo; + srsran::INTEGRITY_ALGORITHM_ID_ENUM integ_algo; + LIBLTE_MME_EPS_MOBILE_ID_GUTI_STRUCT guti; + }; + + nas_sec_ctxt ctxt = {}; + uint8_t k_nas_enc[32] = {}; + uint8_t k_nas_int[32] = {}; + + int parse_security_algorithm_list(std::string algorithm_string, bool* algorithm_caps); + + // Security + void + integrity_generate(uint8_t* key_128, uint32_t count, uint8_t direction, uint8_t* msg, uint32_t msg_len, uint8_t* mac); + bool integrity_check(srsran::byte_buffer_t* pdu); + void cipher_encrypt(srsran::byte_buffer_t* pdu); + void cipher_decrypt(srsran::byte_buffer_t* pdu); +}; + +} // namespace srsue +#endif diff --git a/srsue/hdr/stack/upper/nas_config.h b/srsue/hdr/stack/upper/nas_config.h index a3b560de3..104de01d1 100644 --- a/srsue/hdr/stack/upper/nas_config.h +++ b/srsue/hdr/stack/upper/nas_config.h @@ -43,6 +43,8 @@ public: bool force_imsi_attach; std::string eia; std::string eea; + std::string ia5g; + std::string ea5g; nas_sim_args_t sim; }; diff --git a/srsue/hdr/stack/upper/test/nas_test_common.h b/srsue/hdr/stack/upper/test/nas_test_common.h new file mode 100644 index 000000000..faa40bf51 --- /dev/null +++ b/srsue/hdr/stack/upper/test/nas_test_common.h @@ -0,0 +1,208 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#ifndef SRSUE_NAS_TEST_COMMON +#define SRSUE_NAS_TEST_COMMON + +#include "srsran/common/bcd_helpers.h" +#include "srsran/common/test_common.h" +#include "srsran/common/tsan_options.h" +#include "srsran/interfaces/ue_pdcp_interfaces.h" +#include "srsran/interfaces/ue_rrc_interfaces.h" +#include "srsran/srslog/srslog.h" +#include "srsran/test/ue_test_interfaces.h" +#include "srsue/hdr/stack/upper/gw.h" +#include "srsue/hdr/stack/upper/nas.h" +#include "srsue/hdr/stack/upper/nas_5g.h" +#include "srsue/hdr/stack/upper/usim.h" +#include "srsue/hdr/stack/upper/usim_base.h" + +#define LCID 1 + +uint8_t auth_request_pdu[] = {0x07, 0x52, 0x01, 0x0c, 0x63, 0xa8, 0x54, 0x13, 0xe6, 0xa4, 0xce, 0xd9, + 0x86, 0xfb, 0xe5, 0xce, 0x9b, 0x62, 0x5e, 0x10, 0x67, 0x57, 0xb3, 0xc2, + 0xb9, 0x70, 0x90, 0x01, 0x0c, 0x72, 0x8a, 0x67, 0x57, 0x92, 0x52, 0xb8}; + +uint8_t sec_mode_command_pdu[] = {0x37, 0x4e, 0xfd, 0x57, 0x11, 0x00, 0x07, 0x5d, 0x02, 0x01, 0x02, 0xf0, 0x70, 0xc1}; + +uint8_t attach_accept_pdu[] = {0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x42, 0x01, 0x3e, 0x06, 0x00, 0x00, + 0xf1, 0x10, 0x00, 0x01, 0x00, 0x2a, 0x52, 0x01, 0xc1, 0x01, 0x04, 0x1b, 0x07, + 0x74, 0x65, 0x73, 0x74, 0x31, 0x32, 0x33, 0x06, 0x6d, 0x6e, 0x63, 0x30, 0x30, + 0x31, 0x06, 0x6d, 0x63, 0x63, 0x30, 0x30, 0x31, 0x04, 0x67, 0x70, 0x72, 0x73, + 0x05, 0x01, 0xc0, 0xa8, 0x05, 0x02, 0x27, 0x01, 0x80, 0x50, 0x0b, 0xf6, 0x00, + 0xf1, 0x10, 0x80, 0x01, 0x01, 0x35, 0x16, 0x6d, 0xbc, 0x64, 0x01, 0x00}; + +uint8_t esm_info_req_pdu[] = {0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x5a, 0xd9}; + +uint8_t activate_dedicated_eps_bearer_pdu[] = {0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x00, 0xc5, 0x05, + 0x01, 0x01, 0x07, 0x21, 0x31, 0x00, 0x03, 0x40, 0x08, 0xae, + 0x5d, 0x02, 0x00, 0xc2, 0x81, 0x34, 0x01, 0x4d}; + +uint8_t deactivate_eps_bearer_pdu[] = {0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x00, 0xcd, 0x24}; + +uint16 mcc = 61441; +uint16 mnc = 65281; + +using namespace srsue; + +namespace srsran { + +// fake classes +class pdcp_dummy : public rrc_interface_pdcp, public pdcp_interface_stack +{ +public: + void write_pdu(uint32_t lcid, unique_byte_buffer_t pdu) override {} + void write_pdu_bcch_bch(unique_byte_buffer_t pdu) override {} + void write_pdu_bcch_dlsch(unique_byte_buffer_t pdu) override {} + void write_pdu_pcch(unique_byte_buffer_t pdu) override {} + void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t sdu) override {} + const char* get_rb_name(uint32_t lcid) override { return "lcid"; } + void write_sdu(uint32_t lcid, unique_byte_buffer_t sdu, int sn = -1) override {} + bool is_eps_bearer_id_enabled(uint32_t eps_bearer_id) { return false; } + void notify_pdcp_integrity_error(uint32_t lcid) override {} + bool is_lcid_enabled(uint32_t lcid) override { return false; } +}; + +class rrc_dummy : public rrc_interface_nas +{ +public: + rrc_dummy() : last_sdu_len(0) + { + plmns[0].plmn_id.from_number(mcc, mnc); + plmns[0].tac = 0xffff; + } + void init(nas* nas_) { nas_ptr = nas_; } + void write_sdu(unique_byte_buffer_t sdu) + { + last_sdu_len = sdu->N_bytes; + // printf("NAS generated SDU (len=%d):\n", sdu->N_bytes); + // srsran_vec_fprint_byte(stdout, sdu->msg, sdu->N_bytes); + } + const char* get_rb_name(uint32_t lcid) { return "lcid"; } + uint32_t get_last_sdu_len() { return last_sdu_len; } + void reset() { last_sdu_len = 0; } + + bool plmn_search() + { + nas_ptr->plmn_search_completed(plmns, 1); + return true; + } + void plmn_select(srsran::plmn_id_t plmn_id){}; + void set_ue_identity(srsran::s_tmsi_t s_tmsi) {} + bool connection_request(srsran::establishment_cause_t cause, srsran::unique_byte_buffer_t sdu) + { + printf("NAS generated SDU (len=%d):\n", sdu->N_bytes); + last_sdu_len = sdu->N_bytes; + srsran_vec_fprint_byte(stdout, sdu->msg, sdu->N_bytes); + is_connected_flag = true; + nas_ptr->connection_request_completed(true); + return true; + } + bool is_connected() { return is_connected_flag; } + + uint16_t get_mcc() { return mcc; } + uint16_t get_mnc() { return mnc; } + void enable_capabilities() {} + uint32_t get_lcid_for_eps_bearer(const uint32_t& eps_bearer_id) { return 0; } + void paging_completed(bool outcome) {} + bool has_nr_dc() { return false; } + +private: + nas* nas_ptr; + uint32_t last_sdu_len; + nas_interface_rrc::found_plmn_t plmns[nas_interface_rrc::MAX_FOUND_PLMNS]; + bool is_connected_flag = false; +}; + +class rrc_nr_dummy : public rrc_nr_interface_nas_5g +{ +public: + rrc_nr_dummy() : last_sdu_len(0) + { + plmns[0].plmn_id.from_number(mcc, mnc); + plmns[0].tac = 0xffff; + } + void init(nas_5g* nas_5g_) { nas_5g_ptr = nas_5g_; } + void write_sdu(unique_byte_buffer_t sdu) + { + last_sdu_len = sdu->N_bytes; + // printf("NAS generated SDU (len=%d):\n", sdu->N_bytes); + } + +private: + nas_5g* nas_5g_ptr; + uint32_t last_sdu_len; + nas_interface_rrc::found_plmn_t plmns[nas_interface_rrc::MAX_FOUND_PLMNS]; +}; +template +class test_stack_dummy : public srsue::stack_test_dummy, public stack_interface_gw, public thread +{ +public: + test_stack_dummy(pdcp_interface_stack* pdcp_) : pdcp(pdcp_), thread("DUMMY STACK") {} + void init(T* nas_) + { + nas = nas_; + start(-1); + } + bool switch_on() + { + nas->switch_on(); + return true; + } + void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu) { pdcp->write_sdu(lcid, std::move(sdu)); } + bool has_active_radio_bearer(uint32_t eps_bearer_id) { return true; } + + bool is_registered() { return true; } + + bool start_service_request() { return true; } + + void run_thread() + { + running = true; + while (running) { + task_sched.tic(); + task_sched.run_pending_tasks(); + nas->run_tti(); + } + } + void stop() + { + while (not running) { + usleep(1000); + } + running = false; + wait_thread_finish(); + } + pdcp_interface_stack* pdcp = nullptr; + T* nas = nullptr; + std::atomic running = {false}; +}; + +class gw_dummy : public gw_interface_nas, public gw_interface_pdcp +{ + int setup_if_addr(uint32_t eps_bearer_id, uint8_t pdn_type, uint32_t ip_addr, uint8_t* ipv6_if_id, char* err_str) + { + return SRSRAN_SUCCESS; + } + int deactivate_eps_bearer(const uint32_t eps_bearer_id) { return SRSRAN_SUCCESS; } + int apply_traffic_flow_template(const uint8_t& eps_bearer_id, const LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_STRUCT* tft) + { + return SRSRAN_SUCCESS; + } + void write_pdu(uint32_t lcid, unique_byte_buffer_t pdu) {} + void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t sdu) {} + void set_test_loop_mode(const test_loop_mode_state_t mode, const uint32_t ip_pdu_delay_ms = 0) {} +}; + +} // namespace srsran + +#endif // SRSUE_NAS_TEST_COMMON \ No newline at end of file diff --git a/srsue/src/main.cc b/srsue/src/main.cc index a1d9f27e8..66f0e1b53 100644 --- a/srsue/src/main.cc +++ b/srsue/src/main.cc @@ -414,6 +414,11 @@ static int parse_args(all_args_t* args, int argc, char* argv[]) bpo::value(&args->phy.force_N_id_2)->default_value(-1), "Force using a specific PSS (set to -1 to allow all PSSs).") + // PHY NR args + ("phy.nr.store_pdsch_ko", + bpo::value(&args->phy.nr_store_pdsch_ko)->default_value(false), + "Dumps the PDSCH baseband samples into a file on KO reception.") + // UE simulation args ("sim.airplane_t_on_ms", bpo::value(&args->stack.nas.sim.airplane_t_on_ms)->default_value(-1), diff --git a/srsue/src/phy/CMakeLists.txt b/srsue/src/phy/CMakeLists.txt index ae15bacc8..de3201632 100644 --- a/srsue/src/phy/CMakeLists.txt +++ b/srsue/src/phy/CMakeLists.txt @@ -25,7 +25,4 @@ add_library(srsue_phy STATIC ${SOURCES}) if(ENABLE_GUI AND SRSGUI_FOUND) target_link_libraries(srsue_phy ${SRSGUI_LIBRARIES}) -endif() - -set(SOURCES_NR "../phy/vnf_phy_nr.cc") -add_library(srsue_phy_nr STATIC ${SOURCES_NR}) +endif() \ No newline at end of file diff --git a/srsue/src/phy/nr/cc_worker.cc b/srsue/src/phy/nr/cc_worker.cc index 86fde1d66..590492983 100644 --- a/srsue/src/phy/nr/cc_worker.cc +++ b/srsue/src/phy/nr/cc_worker.cc @@ -22,6 +22,7 @@ #include "srsue/hdr/phy/nr/cc_worker.h" #include "srsran/common/band_helper.h" #include "srsran/common/buffer_pool.h" +#include "srsran/common/string_helpers.h" #include "srsran/srsran.h" namespace srsue { @@ -309,6 +310,32 @@ bool cc_worker::decode_pdsch_dl() } } + if (not pdsch_res.tb[0].crc and phy.args.store_pdsch_ko) { + static unsigned unique_filename_id = 0; + unsigned id = ++unique_filename_id; + + fmt::memory_buffer filename; + fmt::format_to(filename, "pdsch_ko_bb_samples_{}.bin", id); + + srsran_filesink_t filesink = {}; + if (srsran_filesink_init(&filesink, (char*)srsran::to_c_str(filename), SRSRAN_COMPLEX_FLOAT_BIN) == 0) { + srsran_filesink_write(&filesink, (void*)rx_buffer[0], ue_dl.fft[0].sf_sz); + srsran_filesink_free(&filesink); + + str_extra_t str_extra; + srsran_sch_cfg_nr_info(&pdsch_cfg, str_extra.data(), (uint32_t)str_extra.size()); + logger.info("PDSCH: KO detected, dumping PDSCH baseband samples into file '%s'" + "\n" + "Information: cc_idx=%d pid=%d slot_idx=%d sf_len=%d\n%s", + srsran::to_c_str(filename), + cc_idx, + pid, + dl_slot_cfg.idx, + ue_dl.fft[0].sf_sz, + str_extra.data()); + } + } + // Enqueue PDSCH ACK information only if the RNTI is type C if (pdsch_cfg.grant.rnti_type == srsran_rnti_type_c) { phy.set_pending_ack(dl_slot_cfg.idx, ack_resource, pdsch_res.tb[0].crc); @@ -547,7 +574,7 @@ bool cc_worker::work_ul() } // Add SR to UCI data only if there is no UL grant! - if (!has_ul_ack) { + if (not has_pusch_grant) { phy.get_pending_sr(ul_slot_cfg.idx, uci_data); } diff --git a/srsue/src/phy/nr/sf_worker.cc b/srsue/src/phy/nr/sf_worker.cc index e5ede2394..e40e0ea70 100644 --- a/srsue/src/phy/nr/sf_worker.cc +++ b/srsue/src/phy/nr/sf_worker.cc @@ -99,6 +99,7 @@ void sf_worker::work_imp() if (prach_ptr != nullptr) { // PRACH is available, set buffer, transmit and return tx_buffer.set(0, prach_ptr); + tx_buffer.set_nof_samples(SRSRAN_SF_LEN_PRB_NR(phy_state.cfg.carrier.nof_prb)); // Notify MAC about PRACH transmission phy_state.stack->prach_sent(TTI_TX(tti_rx), diff --git a/srsue/src/phy/nr/worker_pool.cc b/srsue/src/phy/nr/worker_pool.cc index ef580d243..0cc6f9165 100644 --- a/srsue/src/phy/nr/worker_pool.cc +++ b/srsue/src/phy/nr/worker_pool.cc @@ -100,7 +100,10 @@ void worker_pool::stop() pool.stop(); } -void worker_pool::send_prach(uint32_t prach_occasion, uint32_t preamble_index, int preamble_received_target_power) +void worker_pool::send_prach(const uint32_t prach_occasion, + const int preamble_index, + const float preamble_received_target_power, + const float ta_base_sec) { prach_buffer->prepare_to_send(preamble_index); } @@ -195,6 +198,10 @@ void worker_pool::get_metrics(phy_metrics_t& m) { phy_state.get_metrics(m); } +int worker_pool::tx_request(const phy_interface_mac_nr::tx_request_t& request) +{ + return 0; +} } // namespace nr } // namespace srsue diff --git a/srsue/src/phy/vnf_phy_nr.cc b/srsue/src/phy/vnf_phy_nr.cc deleted file mode 100644 index 591e70ad3..000000000 --- a/srsue/src/phy/vnf_phy_nr.cc +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Copyright 2013-2021 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 -#include -#include -#include -#include -#include -#include - -#include "srsran/common/basic_vnf_api.h" -#include "srsran/common/test_common.h" -#include "srsran/common/threads.h" -#include "srsue/hdr/phy/vnf_phy_nr.h" - -using namespace std; - -namespace srsue { - -vnf_phy_nr::~vnf_phy_nr() -{ - stop(); -} - -int vnf_phy_nr::init(const srsue::phy_args_t& args_, srsue::stack_interface_phy_nr* stack_) -{ - stack = stack_; - return init(args_); -} - -int vnf_phy_nr::init(const srsue::phy_args_t& args_) -{ - // create VNF - vnf = std::unique_ptr(new srsran::srsran_basic_vnf(args_.vnf_args, stack)); - initialized = true; - return SRSRAN_SUCCESS; -} - -void vnf_phy_nr::set_earfcn(std::vector earfcns) {} - -void vnf_phy_nr::stop() -{ - if (initialized) { - vnf->stop(); - initialized = false; - } -} - -// Start GUI -void vnf_phy_nr::start_plot() {} - -void vnf_phy_nr::wait_initialize() {} - -void vnf_phy_nr::get_metrics(const srsran::srsran_rat_t& rat, phy_metrics_t* m) {} - -int vnf_phy_nr::tx_request(const tx_request_t& request) -{ - // send Tx request over basic API - return vnf->tx_request(request); -} -bool vnf_phy_nr::set_config(const srsran::phy_cfg_nr_t& cfg) -{ - return false; -} -bool vnf_phy_nr::has_valid_sr_resource(uint32_t sr_id) -{ - return false; -} - -void vnf_phy_nr::clear_pending_grants() {} - -} // namespace srsue \ No newline at end of file diff --git a/srsue/src/stack/mac_nr/mac_nr.cc b/srsue/src/stack/mac_nr/mac_nr.cc index ad7d28d85..4e6d271f7 100644 --- a/srsue/src/stack/mac_nr/mac_nr.cc +++ b/srsue/src/stack/mac_nr/mac_nr.cc @@ -306,7 +306,9 @@ void mac_nr::tb_decoded(const uint32_t cc_idx, const mac_nr_grant_dl_t& grant, t write_pcap(cc_idx, grant, result); if (proc_ra.has_rar_rnti() && grant.rnti == proc_ra.get_rar_rnti()) { - proc_ra.handle_rar_pdu(result); + if (result.ack && result.payload != nullptr) { + proc_ra.handle_rar_pdu(result); + } } else { // Assert HARQ entity if (dl_harq.at(cc_idx) == nullptr) { diff --git a/srsue/src/stack/upper/CMakeLists.txt b/srsue/src/stack/upper/CMakeLists.txt index 303c47aa3..701dbbdde 100644 --- a/srsue/src/stack/upper/CMakeLists.txt +++ b/srsue/src/stack/upper/CMakeLists.txt @@ -20,14 +20,16 @@ add_subdirectory(test) -set(SOURCES nas.cc nas_emm_state.cc nas_idle_procedures.cc gw.cc usim_base.cc usim.cc tft_packet_filter.cc) +set(SOURCES nas.cc nas_emm_state.cc nas_idle_procedures.cc gw.cc usim_base.cc usim.cc tft_packet_filter.cc nas_base.cc nas_5g_procedures.cc nas_5g.cc nas_5gmm_state.cc) if(HAVE_PCSC) list(APPEND SOURCES "pcsc_usim.cc") endif(HAVE_PCSC) add_library(srsue_upper STATIC ${SOURCES}) -target_link_libraries(srsue_upper ${ATOMIC_LIBS}) +target_link_libraries(srsue_upper ${ATOMIC_LIBS} srsran_asn1) + +target_link_libraries(srsue_upper nas_5g_msg) if(HAVE_PCSC) target_link_libraries(srsue_upper ${PCSCLITE_LIBRARY}) diff --git a/srsue/src/stack/upper/nas.cc b/srsue/src/stack/upper/nas.cc index 830a6d424..85a6cae6b 100644 --- a/srsue/src/stack/upper/nas.cc +++ b/srsue/src/stack/upper/nas.cc @@ -44,6 +44,7 @@ namespace srsue { ********************************************************************/ nas::nas(srsran::task_sched_handle task_sched_) : + nas_base("NAS"), plmn_searcher(this), task_sched(task_sched_), t3402(task_sched_.get_unique_timer()), @@ -51,8 +52,7 @@ nas::nas(srsran::task_sched_handle task_sched_) : t3411(task_sched_.get_unique_timer()), t3421(task_sched_.get_unique_timer()), reattach_timer(task_sched_.get_unique_timer()), - airplane_mode_sim_timer(task_sched_.get_unique_timer()), - logger(srslog::fetch_basic_logger("NAS")) + airplane_mode_sim_timer(task_sched_.get_unique_timer()) {} int nas::init(usim_interface_nas* usim_, rrc_interface_nas* rrc_, gw_interface_nas* gw_, const nas_args_t& cfg_) @@ -67,30 +67,21 @@ int nas::init(usim_interface_nas* usim_, rrc_interface_nas* rrc_, gw_interface_n } // parse and sanity check EIA list - std::vector cap_list; - srsran::string_parse_list(cfg_.eia, ',', cap_list); - if (cap_list.empty()) { - logger.error("Empty EIA list. Select at least one EIA algorithm."); - } - for (std::vector::const_iterator it = cap_list.begin(); it != cap_list.end(); ++it) { - if (*it != 0 && *it < 4) { - eia_caps[*it] = true; - } else { - logger.error("EIA%d is not a valid EIA algorithm.", *it); - } + if (parse_security_algorithm_list(cfg_.eia, eia_caps) != SRSRAN_SUCCESS) { + logger.warning("Failed to parse integrity protection algorithm list: Defaulting to EIA1-128, EIA2-128, EIA3-128"); + eia_caps[0] = false; + eia_caps[1] = true; + eia_caps[2] = true; + eia_caps[3] = true; } // parse and sanity check EEA list - srsran::string_parse_list(cfg_.eea, ',', cap_list); - if (cap_list.empty()) { - logger.error("Empty EEA list. Select at least one EEA algorithm."); - } - for (std::vector::const_iterator it = cap_list.begin(); it != cap_list.end(); ++it) { - if (*it < 4) { - eea_caps[*it] = true; - } else { - logger.error("EEA%d is not a valid EEA algorithm.", *it); - } + if (parse_security_algorithm_list(cfg_.eea, eea_caps) != SRSRAN_SUCCESS) { + logger.warning("Failed to parse encryption algorithm list: Defaulting to EEA0, EEA1-128, EEA2-128, EEA3-128"); + eea_caps[0] = true; + eea_caps[1] = true; + eea_caps[2] = true; + eea_caps[3] = true; } cfg = cfg_; @@ -698,204 +689,6 @@ void nas::select_plmn() } } -/******************************************************************************* - * Security - ******************************************************************************/ - -void nas::integrity_generate(uint8_t* key_128, - uint32_t count, - uint8_t direction, - uint8_t* msg, - uint32_t msg_len, - uint8_t* mac) -{ - switch (ctxt.integ_algo) { - case INTEGRITY_ALGORITHM_ID_EIA0: - break; - case INTEGRITY_ALGORITHM_ID_128_EIA1: - security_128_eia1(key_128, - count, - 0, // Bearer always 0 for NAS - direction, - msg, - msg_len, - mac); - break; - case INTEGRITY_ALGORITHM_ID_128_EIA2: - security_128_eia2(key_128, - count, - 0, // Bearer always 0 for NAS - direction, - msg, - msg_len, - mac); - break; - case INTEGRITY_ALGORITHM_ID_128_EIA3: - security_128_eia3(key_128, - count, - 0, // Bearer always 0 for NAS - direction, - msg, - msg_len, - mac); - break; - default: - break; - } -} - -// This function depends to a valid k_nas_int. -// This key is generated in the security mode command. -bool nas::integrity_check(byte_buffer_t* pdu) -{ - if (pdu == nullptr) { - logger.error("Invalid PDU"); - return false; - } - - if (pdu->N_bytes > 5) { - uint8_t exp_mac[4] = {0}; - uint8_t* mac = &pdu->msg[1]; - - // generate expected MAC - uint32_t count_est = (ctxt.rx_count & 0x00FFFF00u) | pdu->msg[5]; - integrity_generate( - &k_nas_int[16], count_est, SECURITY_DIRECTION_DOWNLINK, &pdu->msg[5], pdu->N_bytes - 5, &exp_mac[0]); - - // Check if expected mac equals the sent mac - for (int i = 0; i < 4; i++) { - if (exp_mac[i] != mac[i]) { - logger.warning("Integrity check failure. Local: count=%d, [%02x %02x %02x %02x], " - "Received: count=%d, [%02x %02x %02x %02x]", - count_est, - exp_mac[0], - exp_mac[1], - exp_mac[2], - exp_mac[3], - pdu->msg[5], - mac[0], - mac[1], - mac[2], - mac[3]); - return false; - } - } - logger.info("Integrity check ok. Local: count=%d, Received: count=%d [%02x %02x %02x %02x]", - count_est, - pdu->msg[5], - mac[0], - mac[1], - mac[2], - mac[3]); - - // Updated local count (according to TS 24.301 Sec. 4.4.3.3) - if (count_est != ctxt.rx_count) { - logger.info("Update local count to estimated count %d", count_est); - ctxt.rx_count = count_est; - } - return true; - } else { - logger.error("Invalid integrity check PDU size (%d)", pdu->N_bytes); - return false; - } -} - -void nas::cipher_encrypt(byte_buffer_t* pdu) -{ - byte_buffer_t pdu_tmp; - - if (ctxt.cipher_algo != CIPHERING_ALGORITHM_ID_EEA0) { - logger.debug("Encrypting PDU. count=%d", ctxt.tx_count); - } - - switch (ctxt.cipher_algo) { - case CIPHERING_ALGORITHM_ID_EEA0: - break; - case CIPHERING_ALGORITHM_ID_128_EEA1: - security_128_eea1(&k_nas_enc[16], - ctxt.tx_count, - 0, // Bearer always 0 for NAS - SECURITY_DIRECTION_UPLINK, - &pdu->msg[6], - pdu->N_bytes - 6, - &pdu_tmp.msg[6]); - memcpy(&pdu->msg[6], &pdu_tmp.msg[6], pdu->N_bytes - 6); - break; - case CIPHERING_ALGORITHM_ID_128_EEA2: - security_128_eea2(&k_nas_enc[16], - ctxt.tx_count, - 0, // Bearer always 0 for NAS - SECURITY_DIRECTION_UPLINK, - &pdu->msg[6], - pdu->N_bytes - 6, - &pdu_tmp.msg[6]); - memcpy(&pdu->msg[6], &pdu_tmp.msg[6], pdu->N_bytes - 6); - break; - case CIPHERING_ALGORITHM_ID_128_EEA3: - security_128_eea3(&k_nas_enc[16], - ctxt.tx_count, - 0, // Bearer always 0 for NAS - SECURITY_DIRECTION_UPLINK, - &pdu->msg[6], - pdu->N_bytes - 6, - &pdu_tmp.msg[6]); - memcpy(&pdu->msg[6], &pdu_tmp.msg[6], pdu->N_bytes - 6); - break; - default: - logger.error("Ciphering algorithm not known"); - break; - } -} - -void nas::cipher_decrypt(byte_buffer_t* pdu) -{ - byte_buffer_t tmp_pdu; - - uint32_t count_est = (ctxt.rx_count & 0x00FFFF00u) | pdu->msg[5]; - if (ctxt.cipher_algo != CIPHERING_ALGORITHM_ID_EEA0) { - logger.debug("Decrypting PDU. Local: count=%d, Received: count=%d", ctxt.rx_count, count_est); - } - - switch (ctxt.cipher_algo) { - case CIPHERING_ALGORITHM_ID_EEA0: - break; - case CIPHERING_ALGORITHM_ID_128_EEA1: - security_128_eea1(&k_nas_enc[16], - count_est, - 0, // Bearer always 0 for NAS - SECURITY_DIRECTION_DOWNLINK, - &pdu->msg[6], - pdu->N_bytes - 6, - &tmp_pdu.msg[6]); - memcpy(&pdu->msg[6], &tmp_pdu.msg[6], pdu->N_bytes - 6); - break; - case CIPHERING_ALGORITHM_ID_128_EEA2: - security_128_eea2(&k_nas_enc[16], - count_est, - 0, // Bearer always 0 for NAS - SECURITY_DIRECTION_DOWNLINK, - &pdu->msg[6], - pdu->N_bytes - 6, - &tmp_pdu.msg[6]); - logger.debug(tmp_pdu.msg, pdu->N_bytes, "Decrypted"); - memcpy(&pdu->msg[6], &tmp_pdu.msg[6], pdu->N_bytes - 6); - break; - case CIPHERING_ALGORITHM_ID_128_EEA3: - security_128_eea3(&k_nas_enc[16], - count_est, - 0, // Bearer always 0 for NAS - SECURITY_DIRECTION_DOWNLINK, - &pdu->msg[6], - pdu->N_bytes - 6, - &tmp_pdu.msg[6]); - logger.debug(tmp_pdu.msg, pdu->N_bytes, "Decrypted"); - memcpy(&pdu->msg[6], &tmp_pdu.msg[6], pdu->N_bytes - 6); - break; - default: - logger.error("Ciphering algorithms not known"); - break; - } -} bool nas::check_cap_replay(LIBLTE_MME_UE_SECURITY_CAPABILITIES_STRUCT* caps) { diff --git a/srsue/src/stack/upper/nas_5g.cc b/srsue/src/stack/upper/nas_5g.cc new file mode 100644 index 000000000..97718fc75 --- /dev/null +++ b/srsue/src/stack/upper/nas_5g.cc @@ -0,0 +1,236 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#include "srsue/hdr/stack/upper/nas_5g.h" +#include "srsran/asn1/nas_5g_ies.h" +#include "srsran/asn1/nas_5g_msg.h" +#include "srsran/common/bcd_helpers.h" +#include "srsran/common/security.h" +#include "srsran/common/standard_streams.h" +#include "srsran/common/string_helpers.h" +#include "srsran/interfaces/ue_gw_interfaces.h" +#include "srsran/interfaces/ue_rrc_interfaces.h" +#include "srsran/interfaces/ue_usim_interfaces.h" +#include "srsue/hdr/stack/upper/nas_5g_procedures.h" + +#include +#include +#include +#include + +using namespace srsran; +using namespace srsran::nas_5g; + +namespace srsue { + +/********************************************************************* + * NAS 5G (NR) + ********************************************************************/ + +nas_5g::nas_5g(srsran::task_sched_handle task_sched_) : + nas_base("NAS-5G"), + task_sched(task_sched_), + t3502(task_sched_.get_unique_timer()), + t3510(task_sched_.get_unique_timer()), + t3511(task_sched_.get_unique_timer()), + t3521(task_sched_.get_unique_timer()), + reregistration_timer(task_sched_.get_unique_timer()), + registration_proc(this) +{ + // Configure timers + t3502.set(t3502_duration_ms, [this](uint32_t tid) { timer_expired(tid); }); + t3510.set(t3510_duration_ms, [this](uint32_t tid) { timer_expired(tid); }); + t3511.set(t3511_duration_ms, [this](uint32_t tid) { timer_expired(tid); }); + t3521.set(t3521_duration_ms, [this](uint32_t tid) { timer_expired(tid); }); + reregistration_timer.set(reregistration_timer_duration_ms, [this](uint32_t tid) { timer_expired(tid); }); +} + +nas_5g::~nas_5g() {} + +void nas_5g::stop() +{ + running = false; +} + +int nas_5g::init(usim_interface_nas* usim_, + rrc_nr_interface_nas_5g* rrc_nr_, + gw_interface_nas* gw_, + const nas_args_t& cfg_) +{ + usim = usim_; + rrc_nr = rrc_nr_; + gw = gw_; + cfg = cfg_; + + // parse and sanity check EIA list + if (parse_security_algorithm_list(cfg_.ia5g, ia5g_caps) != SRSRAN_SUCCESS) { + logger.warning("Failed to parse integrity algorithm list: Defaulting to 5G-EI1-128, 5G-EI2-128, 5G-EI3-128"); + ia5g_caps[0] = false; + ia5g_caps[1] = true; + ia5g_caps[2] = true; + ia5g_caps[3] = true; + } + + // parse and sanity check EEA list + if (parse_security_algorithm_list(cfg_.ea5g, ea5g_caps) != SRSRAN_SUCCESS) { + logger.warning( + "Failed to parse encryption algorithm list: Defaulting to 5G-EA0, 5G-EA1-128, 5G-EA2-128, 5G-EA3-128"); + ea5g_caps[0] = true; + ea5g_caps[1] = true; + ea5g_caps[2] = true; + ea5g_caps[3] = true; + } + + running = true; + return SRSRAN_SUCCESS; +} + +void nas_5g::run_tti() +{ + // Process PLMN selection ongoing procedures + callbacks.run(); + + // Transmit intiating messages if necessary + switch (state.get_state()) { + case mm5g_state_t::state_t::deregistered: + // TODO Make sure cell selection is finished after transitioning from another state (if required) + // Make sure the RRC is finished transitioning to RRC Idle + if (reregistration_timer.is_running()) { + logger.debug("Waiting for re-attach timer to expire to attach again."); + return; + } + switch (state.get_deregistered_substate()) { + case mm5g_state_t::deregistered_substate_t::plmn_search: + case mm5g_state_t::deregistered_substate_t::normal_service: + case mm5g_state_t::deregistered_substate_t::initial_registration_needed: + registration_proc.launch(); + break; + case mm5g_state_t::deregistered_substate_t::attempting_to_registration: + case mm5g_state_t::deregistered_substate_t::no_supi: + case mm5g_state_t::deregistered_substate_t::no_cell_available: + case mm5g_state_t::deregistered_substate_t::e_call_inactive: + logger.debug("Attempting to registration (not implemented) %s", state.get_full_state_text().c_str()); + default: + break; + } + case mm5g_state_t::state_t::registered: + break; + case mm5g_state_t::state_t::deregistered_initiated: + logger.debug("UE detaching..."); + break; + default: + break; + } +} + +/******************************************************************************* + * Senders + ******************************************************************************/ + +int nas_5g::send_registration_request() +{ + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + if (!pdu) { + logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return SRSRAN_ERROR; + } + + logger.info("Generating registration request"); + + nas_5gs_msg nas_msg; + registration_request_t& reg_req = nas_msg.set_registration_request(); + + reg_req.registration_type_5gs.follow_on_request_bit = + registration_type_5gs_t::follow_on_request_bit_type_::options::no_follow_on_request_pending; + reg_req.registration_type_5gs.registration_type = + registration_type_5gs_t::registration_type_type_::options::initial_registration; + mobile_identity_5gs_t::suci_s& suci = reg_req.mobile_identity_5gs.set_suci(); + suci.supi_format = mobile_identity_5gs_t::suci_s::supi_format_type_::options::imsi; + mcc_to_bytes(0x0, suci.mcc.data()); + uint8_t mnc_len; + mnc_to_bytes(0x0, suci.mnc.data(), &mnc_len); + suci.scheme_output.resize(15); + usim->get_imsi_vec(suci.scheme_output.data(), 15); + logger.info("Requesting IMSI attach (IMSI=%s)", usim->get_imsi_str().c_str()); + + if (nas_msg.pack(pdu) != SRSASN_SUCCESS) { + logger.error("Failed to pack registration request"); + return SRSRAN_ERROR; + } + + if (pcap != nullptr) { + pcap->write_nas(pdu.get()->msg, pdu.get()->N_bytes); + } + + // start T3510 + logger.debug("Starting T3410. Timeout in %d ms.", t3510.duration()); + t3510.run(); + + state.set_registered_initiated(); + + return SRSRAN_SUCCESS; +} + +/******************************************************************************* + * UE Stack and RRC common Interface + ******************************************************************************/ +bool nas_5g::is_registered() +{ + return state.get_state() == mm5g_state_t::state_t::registered; +} + +/******************************************************************************* + * NAS Timers + ******************************************************************************/ +void nas_5g::timer_expired(uint32_t timeout_id) +{ + // TODO +} + +/******************************************************************************* + * UE Stack Interface + ******************************************************************************/ +int nas_5g::switch_on() +{ + logger.info("Switching on"); + state.set_deregistered(mm5g_state_t::deregistered_substate_t::plmn_search); + return SRSRAN_SUCCESS; +} + +int nas_5g::switch_off() +{ + logger.info("Switching off"); + // TODO + return SRSRAN_SUCCESS; +} + +int nas_5g::enable_data() +{ + logger.info("Enabling data services"); + return switch_on(); +} + +int nas_5g::disable_data() +{ + logger.info("Disabling data services"); + // TODO + return SRSRAN_SUCCESS; +} + +int nas_5g::start_service_request() +{ + logger.info("Service Request"); + // TODO + return SRSRAN_SUCCESS; +} + +} // namespace srsue \ No newline at end of file diff --git a/srsue/src/stack/upper/nas_5g_procedures.cc b/srsue/src/stack/upper/nas_5g_procedures.cc new file mode 100644 index 000000000..bf2b80e2d --- /dev/null +++ b/srsue/src/stack/upper/nas_5g_procedures.cc @@ -0,0 +1,38 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#include "srsue/hdr/stack/upper/nas_5g_procedures.h" + +#include +#include +#include +#include + +using namespace srsran; + +namespace srsue { + +nas_5g::registration_procedure::registration_procedure(nas_5g_interface_procedures* parent_nas_) : + parent_nas(parent_nas_) +{} + +srsran::proc_outcome_t nas_5g::registration_procedure::init() +{ + parent_nas->send_registration_request(); + return srsran::proc_outcome_t::yield; +} +srsran::proc_outcome_t nas_5g::registration_procedure::step() +{ + return srsran::proc_outcome_t::success; +} + +} // namespace srsue \ No newline at end of file diff --git a/srsue/src/stack/upper/nas_5gmm_state.cc b/srsue/src/stack/upper/nas_5gmm_state.cc new file mode 100644 index 000000000..cc8ba316e --- /dev/null +++ b/srsue/src/stack/upper/nas_5gmm_state.cc @@ -0,0 +1,149 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ +#include "srsue/hdr/stack/upper/nas_5gmm_state.h" + +namespace srsue { + +// FSM setters +void mm5g_state_t::set_null() +{ + state = state_t::null; + deregistered_substate = deregistered_substate_t::null; + registered_substate = registered_substate_t::null; + logger.debug("Changed to mm5g state: %s", get_full_state_text().c_str()); +} + +void mm5g_state_t::set_deregistered(deregistered_substate_t substate) +{ + state = state_t::deregistered; + deregistered_substate = substate; + registered_substate = registered_substate_t::null; + logger.debug("Changed to mm5g state: %s", get_full_state_text().c_str()); +} + +void mm5g_state_t::set_deregistered_initiated() +{ + state = state_t::deregistered_initiated; + deregistered_substate = deregistered_substate_t::null; + registered_substate = registered_substate_t::null; + logger.debug("Changed to mm5g state: %s", get_full_state_text().c_str()); +} + +void mm5g_state_t::set_registered(registered_substate_t substate) +{ + state = state_t::registered; + deregistered_substate = deregistered_substate_t::null; + registered_substate = substate; + logger.debug("Changed to mm5g state: %s", get_full_state_text().c_str()); +} + +void mm5g_state_t::set_registered_initiated() +{ + state = state_t::registered_initiated; + deregistered_substate = deregistered_substate_t::null; + registered_substate = registered_substate_t::null; + logger.debug("Changed to mm5g state: %s", get_full_state_text().c_str()); +} + +void mm5g_state_t::set_service_request_initiated() +{ + state = state_t::service_request_initiated; + deregistered_substate = deregistered_substate_t::null; + registered_substate = registered_substate_t::null; + logger.debug("Changed to mm5g state: %s", get_full_state_text().c_str()); +} + +const std::string mm5g_state_t::get_full_state_text() +{ + if (state == state_t::deregistered) { + return mm5g_state_text(state) + std::string(", with substate ") + + mm5g_deregistered_substate_text(deregistered_substate); + } else if (state == state_t::registered) { + return mm5g_state_text(state) + std::string(", with substate ") + + mm5g_registered_substate_text(registered_substate); + } else { + return mm5g_state_text(state); + } + return std::string("Invalid State"); +} + +/* + * Logging helper functions + */ +const char* mm5g_state_text(mm5g_state_t::state_t type) +{ + switch (type) { + case mm5g_state_t::state_t::null: + return "NULL"; + case mm5g_state_t::state_t::deregistered: + return "DEREGISTERED"; + case mm5g_state_t::state_t::registered_initiated: + return "REGISTERED-INITIATED"; + case mm5g_state_t::state_t::registered: + return "REGISTERED"; + case mm5g_state_t::state_t::deregistered_initiated: + return "DEREGISTERED-INITIATED"; + case mm5g_state_t::state_t::service_request_initiated: + return "SERVICE-REQUEST-INITIATED"; + } + return "INVALID"; +} + +const char* mm5g_deregistered_substate_text(mm5g_state_t::deregistered_substate_t type) +{ + switch (type) { + case mm5g_state_t::deregistered_substate_t::null: + return "NULL"; + case mm5g_state_t::deregistered_substate_t::normal_service: + return "NORMAL-SERVICE"; + case mm5g_state_t::deregistered_substate_t::limited_service: + return "LIMITED-SERVICE"; + case mm5g_state_t::deregistered_substate_t::attempting_to_registration: + return "ATTEMPTING-TO-REGISTRATION"; + case mm5g_state_t::deregistered_substate_t::plmn_search: + return "PLMN-SEARCH"; + case mm5g_state_t::deregistered_substate_t::no_supi: + return "NO-SUPI"; + case mm5g_state_t::deregistered_substate_t::no_cell_available: + return "NO-CELL-AVAILABLE"; + case mm5g_state_t::deregistered_substate_t::e_call_inactive: + return "eCALL-INACTIVE"; + case mm5g_state_t::deregistered_substate_t::initial_registration_needed: + return "ATTACH-NEEDED"; + } + return "INVALID"; +} + +const char* mm5g_registered_substate_text(mm5g_state_t::registered_substate_t type) +{ + switch (type) { + case mm5g_state_t::registered_substate_t::null: + return "NULL"; + case mm5g_state_t::registered_substate_t::normal_service: + return "NORMAL-SERVICE"; + case mm5g_state_t::registered_substate_t::non_allowed_service: + return "NON-ALLOWED-SERVICE"; + case mm5g_state_t::registered_substate_t::attempting_registration_update: + return "ATTEMPTING-REGISTRATION-UPDATE"; + case mm5g_state_t::registered_substate_t::limited_service: + return "LIMITED-SERVICE"; + case mm5g_state_t::registered_substate_t::plmn_search: + return "PLMN-SEARCH"; + case mm5g_state_t::registered_substate_t::no_cell_available: + return "NO-CELL-AVAILABLE"; + case mm5g_state_t::registered_substate_t::update_needed: + return "UPDATE-NEEDED"; + } + return "INVALID"; +} + +} // namespace srsue diff --git a/srsue/src/stack/upper/nas_base.cc b/srsue/src/stack/upper/nas_base.cc new file mode 100644 index 000000000..b4a01f76a --- /dev/null +++ b/srsue/src/stack/upper/nas_base.cc @@ -0,0 +1,238 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#include "srsue/hdr/stack/upper/nas_base.h" + +using namespace srsran; +namespace srsue { +nas_base::nas_base(const std::string& type_) : logger(srslog::fetch_basic_logger(type_)) {} + +int nas_base::parse_security_algorithm_list(std::string algorithm_string, bool* algorithm_caps) +{ + // parse and sanity check security algorithm list + std::vector cap_list; + srsran::string_parse_list(algorithm_string, ',', cap_list); + if (cap_list.empty()) { + logger.error("Empty security list. Select at least one security algorithm."); + return SRSRAN_ERROR; + } + for (std::vector::const_iterator it = cap_list.begin(); it != cap_list.end(); ++it) { + if (*it < 4) { + algorithm_caps[*it] = true; + } else { + logger.error("EEA/EIA/5G-EA/5G-IA %d is not a valid algorithm.", *it); + return SRSRAN_ERROR; + } + } + return SRSRAN_SUCCESS; +} + +/******************************************************************************* + * Security + ******************************************************************************/ + +void nas_base::integrity_generate(uint8_t* key_128, + uint32_t count, + uint8_t direction, + uint8_t* msg, + uint32_t msg_len, + uint8_t* mac) +{ + switch (ctxt.integ_algo) { + case INTEGRITY_ALGORITHM_ID_EIA0: + break; + case INTEGRITY_ALGORITHM_ID_128_EIA1: + security_128_eia1(key_128, + count, + 0, // Bearer always 0 for NAS + direction, + msg, + msg_len, + mac); + break; + case INTEGRITY_ALGORITHM_ID_128_EIA2: + security_128_eia2(key_128, + count, + 0, // Bearer always 0 for NAS + direction, + msg, + msg_len, + mac); + break; + case INTEGRITY_ALGORITHM_ID_128_EIA3: + security_128_eia3(key_128, + count, + 0, // Bearer always 0 for NAS + direction, + msg, + msg_len, + mac); + break; + default: + break; + } +} + +// This function depends to a valid k_nas_int. +// This key is generated in the security mode command. +bool nas_base::integrity_check(byte_buffer_t* pdu) +{ + if (pdu == nullptr) { + logger.error("Invalid PDU"); + return false; + } + + if (pdu->N_bytes > 5) { + uint8_t exp_mac[4] = {0}; + uint8_t* mac = &pdu->msg[1]; + + // generate expected MAC + uint32_t count_est = (ctxt.rx_count & 0x00FFFF00u) | pdu->msg[5]; + integrity_generate( + &k_nas_int[16], count_est, SECURITY_DIRECTION_DOWNLINK, &pdu->msg[5], pdu->N_bytes - 5, &exp_mac[0]); + + // Check if expected mac equals the sent mac + for (int i = 0; i < 4; i++) { + if (exp_mac[i] != mac[i]) { + logger.warning("Integrity check failure. Local: count=%d, [%02x %02x %02x %02x], " + "Received: count=%d, [%02x %02x %02x %02x]", + count_est, + exp_mac[0], + exp_mac[1], + exp_mac[2], + exp_mac[3], + pdu->msg[5], + mac[0], + mac[1], + mac[2], + mac[3]); + return false; + } + } + logger.info("Integrity check ok. Local: count=%d, Received: count=%d [%02x %02x %02x %02x]", + count_est, + pdu->msg[5], + mac[0], + mac[1], + mac[2], + mac[3]); + + // Updated local count (according to TS 24.301 Sec. 4.4.3.3) + if (count_est != ctxt.rx_count) { + logger.info("Update local count to estimated count %d", count_est); + ctxt.rx_count = count_est; + } + return true; + } else { + logger.error("Invalid integrity check PDU size (%d)", pdu->N_bytes); + return false; + } +} + +void nas_base::cipher_encrypt(byte_buffer_t* pdu) +{ + byte_buffer_t pdu_tmp; + + if (ctxt.cipher_algo != CIPHERING_ALGORITHM_ID_EEA0) { + logger.debug("Encrypting PDU. count=%d", ctxt.tx_count); + } + + switch (ctxt.cipher_algo) { + case CIPHERING_ALGORITHM_ID_EEA0: + break; + case CIPHERING_ALGORITHM_ID_128_EEA1: + security_128_eea1(&k_nas_enc[16], + ctxt.tx_count, + 0, // Bearer always 0 for NAS + SECURITY_DIRECTION_UPLINK, + &pdu->msg[6], + pdu->N_bytes - 6, + &pdu_tmp.msg[6]); + memcpy(&pdu->msg[6], &pdu_tmp.msg[6], pdu->N_bytes - 6); + break; + case CIPHERING_ALGORITHM_ID_128_EEA2: + security_128_eea2(&k_nas_enc[16], + ctxt.tx_count, + 0, // Bearer always 0 for NAS + SECURITY_DIRECTION_UPLINK, + &pdu->msg[6], + pdu->N_bytes - 6, + &pdu_tmp.msg[6]); + memcpy(&pdu->msg[6], &pdu_tmp.msg[6], pdu->N_bytes - 6); + break; + case CIPHERING_ALGORITHM_ID_128_EEA3: + security_128_eea3(&k_nas_enc[16], + ctxt.tx_count, + 0, // Bearer always 0 for NAS + SECURITY_DIRECTION_UPLINK, + &pdu->msg[6], + pdu->N_bytes - 6, + &pdu_tmp.msg[6]); + memcpy(&pdu->msg[6], &pdu_tmp.msg[6], pdu->N_bytes - 6); + break; + default: + logger.error("Ciphering algorithm not known"); + break; + } +} + +void nas_base::cipher_decrypt(byte_buffer_t* pdu) +{ + byte_buffer_t tmp_pdu; + + uint32_t count_est = (ctxt.rx_count & 0x00FFFF00u) | pdu->msg[5]; + if (ctxt.cipher_algo != CIPHERING_ALGORITHM_ID_EEA0) { + logger.debug("Decrypting PDU. Local: count=%d, Received: count=%d", ctxt.rx_count, count_est); + } + + switch (ctxt.cipher_algo) { + case CIPHERING_ALGORITHM_ID_EEA0: + break; + case CIPHERING_ALGORITHM_ID_128_EEA1: + security_128_eea1(&k_nas_enc[16], + count_est, + 0, // Bearer always 0 for NAS + SECURITY_DIRECTION_DOWNLINK, + &pdu->msg[6], + pdu->N_bytes - 6, + &tmp_pdu.msg[6]); + memcpy(&pdu->msg[6], &tmp_pdu.msg[6], pdu->N_bytes - 6); + break; + case CIPHERING_ALGORITHM_ID_128_EEA2: + security_128_eea2(&k_nas_enc[16], + count_est, + 0, // Bearer always 0 for NAS + SECURITY_DIRECTION_DOWNLINK, + &pdu->msg[6], + pdu->N_bytes - 6, + &tmp_pdu.msg[6]); + logger.debug(tmp_pdu.msg, pdu->N_bytes, "Decrypted"); + memcpy(&pdu->msg[6], &tmp_pdu.msg[6], pdu->N_bytes - 6); + break; + case CIPHERING_ALGORITHM_ID_128_EEA3: + security_128_eea3(&k_nas_enc[16], + count_est, + 0, // Bearer always 0 for NAS + SECURITY_DIRECTION_DOWNLINK, + &pdu->msg[6], + pdu->N_bytes - 6, + &tmp_pdu.msg[6]); + logger.debug(tmp_pdu.msg, pdu->N_bytes, "Decrypted"); + memcpy(&pdu->msg[6], &tmp_pdu.msg[6], pdu->N_bytes - 6); + break; + default: + logger.error("Ciphering algorithms not known"); + break; + } +} + +} // namespace srsue \ No newline at end of file diff --git a/srsue/src/stack/upper/test/CMakeLists.txt b/srsue/src/stack/upper/test/CMakeLists.txt index 782d4daca..aacda4d6f 100644 --- a/srsue/src/stack/upper/test/CMakeLists.txt +++ b/srsue/src/stack/upper/test/CMakeLists.txt @@ -31,6 +31,10 @@ add_executable(nas_test nas_test.cc) target_link_libraries(nas_test srsue_upper srsran_common srsran_phy rrc_asn1 srsran_asn1) add_test(nas_test nas_test) +add_executable(nas_5g_test nas_5g_test.cc) +target_link_libraries(nas_5g_test srsue_upper srsran_phy rrc_asn1) +add_test(nas_5g_test nas_5g_test) + add_executable(gw_test gw_test.cc) target_link_libraries(gw_test srsue_upper srsran_common srsran_phy) add_test(gw_test gw_test) diff --git a/srsue/src/stack/upper/test/nas_5g_test.cc b/srsue/src/stack/upper/test/nas_5g_test.cc new file mode 100644 index 000000000..8556e326a --- /dev/null +++ b/srsue/src/stack/upper/test/nas_5g_test.cc @@ -0,0 +1,108 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#include "srsran/common/bcd_helpers.h" +#include "srsran/common/test_common.h" +#include "srsran/common/tsan_options.h" +#include "srsran/interfaces/ue_pdcp_interfaces.h" +#include "srsran/srslog/srslog.h" +#include "srsran/test/ue_test_interfaces.h" +#include "srsue/hdr/stack/upper/gw.h" +#include "srsue/hdr/stack/upper/nas_5g.h" +#include "srsue/hdr/stack/upper/test/nas_test_common.h" + +using namespace srsue; +using namespace srsran; + +#define HAVE_PCAP 0 + +int amf_attach_request_test(srsran::nas_pcap* pcap) +{ + int ret = SRSRAN_ERROR; + + rrc_nr_dummy rrc_nr_dummy; + pdcp_dummy pdcp_dummy; + + srsue::usim usim(srslog::fetch_basic_logger("USIM")); + usim_args_t args; + args.mode = "soft"; + args.algo = "xor"; + args.imei = "353490069873319"; + args.imsi = "001010123456789"; + args.k = "00112233445566778899aabbccddeeff"; + args.op = "63BFA50EE6523365FF14C1F45F88737D"; + usim.init(&args); + + nas_args_t nas_cfg; + nas_cfg.force_imsi_attach = true; + nas_cfg.apn_name = "test123"; + nas_cfg.ia5g = "0,1,2,3"; + nas_cfg.ea5g = "0,1,2,3"; + + test_stack_dummy stack(&pdcp_dummy); + srsue::nas_5g nas_5g(&stack.task_sched); + srsue::gw gw; + + if (pcap != nullptr) { + nas_5g.start_pcap(pcap); + } + + gw_args_t gw_args; + gw_args.tun_dev_name = "tun0"; + gw_args.log.gw_level = "debug"; + gw_args.log.gw_hex_limit = 100000; + + gw.init(gw_args, &stack); + stack.init(&nas_5g); + + nas_5g.init(&usim, &rrc_nr_dummy, &gw, nas_cfg); + rrc_nr_dummy.init(&nas_5g); + + // trigger test + stack.switch_on(); + stack.stop(); + + gw.stop(); + ret = SRSRAN_SUCCESS; + + return ret; +} + +int main(int argc, char** argv) +{ + // Setup logging. + auto& rrc_logger = srslog::fetch_basic_logger("RRC", false); + rrc_logger.set_level(srslog::basic_levels::debug); + rrc_logger.set_hex_dump_max_size(100000); + auto& nas_logger = srslog::fetch_basic_logger("NAS", false); + nas_logger.set_level(srslog::basic_levels::debug); + nas_logger.set_hex_dump_max_size(100000); + auto& usim_logger = srslog::fetch_basic_logger("USIM", false); + usim_logger.set_level(srslog::basic_levels::debug); + usim_logger.set_hex_dump_max_size(100000); + auto& gw_logger = srslog::fetch_basic_logger("GW", false); + gw_logger.set_level(srslog::basic_levels::debug); + gw_logger.set_hex_dump_max_size(100000); + + // Start the log backend. + srslog::init(); +#if HAVE_PCAP + srsran::nas_pcap pcap; + pcap.open("nas_5g_test.pcap", 0, srsran::srsran_rat_t::nr); + TESTASSERT(amf_attach_request_test(&pcap) == SRSRAN_SUCCESS); + pcap.close(); +#else + TESTASSERT(amf_attach_request_test(nullptr) == SRSRAN_SUCCESS); +#endif // HAVE_PCAP + + return SRSRAN_SUCCESS; +} diff --git a/srsue/src/stack/upper/test/nas_test.cc b/srsue/src/stack/upper/test/nas_test.cc index a235ba780..48838f3d4 100644 --- a/srsue/src/stack/upper/test/nas_test.cc +++ b/srsue/src/stack/upper/test/nas_test.cc @@ -27,10 +27,12 @@ #include "srsran/test/ue_test_interfaces.h" #include "srsue/hdr/stack/upper/gw.h" #include "srsue/hdr/stack/upper/nas.h" +#include "srsue/hdr/stack/upper/test/nas_test_common.h" #include "srsue/hdr/stack/upper/usim.h" #include "srsue/hdr/stack/upper/usim_base.h" using namespace srsue; +using namespace srsran; static_assert(alignof(LIBLTE_BYTE_MSG_STRUCT) == alignof(byte_buffer_t), "liblte buffer and byte buffer members misaligned"); @@ -41,164 +43,74 @@ static_assert(offsetof(LIBLTE_BYTE_MSG_STRUCT, header) == offsetof(byte_buffer_t static_assert(sizeof(LIBLTE_BYTE_MSG_STRUCT) <= offsetof(byte_buffer_t, msg), "liblte buffer and byte buffer members misaligned"); -#define LCID 1 - -uint8_t auth_request_pdu[] = {0x07, 0x52, 0x01, 0x0c, 0x63, 0xa8, 0x54, 0x13, 0xe6, 0xa4, 0xce, 0xd9, - 0x86, 0xfb, 0xe5, 0xce, 0x9b, 0x62, 0x5e, 0x10, 0x67, 0x57, 0xb3, 0xc2, - 0xb9, 0x70, 0x90, 0x01, 0x0c, 0x72, 0x8a, 0x67, 0x57, 0x92, 0x52, 0xb8}; - -uint8_t sec_mode_command_pdu[] = {0x37, 0x4e, 0xfd, 0x57, 0x11, 0x00, 0x07, 0x5d, 0x02, 0x01, 0x02, 0xf0, 0x70, 0xc1}; - -uint8_t attach_accept_pdu[] = {0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x42, 0x01, 0x3e, 0x06, 0x00, 0x00, - 0xf1, 0x10, 0x00, 0x01, 0x00, 0x2a, 0x52, 0x01, 0xc1, 0x01, 0x04, 0x1b, 0x07, - 0x74, 0x65, 0x73, 0x74, 0x31, 0x32, 0x33, 0x06, 0x6d, 0x6e, 0x63, 0x30, 0x30, - 0x31, 0x06, 0x6d, 0x63, 0x63, 0x30, 0x30, 0x31, 0x04, 0x67, 0x70, 0x72, 0x73, - 0x05, 0x01, 0xc0, 0xa8, 0x05, 0x02, 0x27, 0x01, 0x80, 0x50, 0x0b, 0xf6, 0x00, - 0xf1, 0x10, 0x80, 0x01, 0x01, 0x35, 0x16, 0x6d, 0xbc, 0x64, 0x01, 0x00}; - -uint8_t esm_info_req_pdu[] = {0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x5a, 0xd9}; - -uint8_t activate_dedicated_eps_bearer_pdu[] = {0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x00, 0xc5, 0x05, - 0x01, 0x01, 0x07, 0x21, 0x31, 0x00, 0x03, 0x40, 0x08, 0xae, - 0x5d, 0x02, 0x00, 0xc2, 0x81, 0x34, 0x01, 0x4d}; - -uint8_t deactivate_eps_bearer_pdu[] = {0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x00, 0xcd, 0x24}; - -uint16 mcc = 61441; -uint16 mnc = 65281; - -using namespace srsran; - -namespace srsran { - -// fake classes -class pdcp_dummy : public rrc_interface_pdcp, public pdcp_interface_stack +int mme_attach_request_test() { -public: - void write_pdu(uint32_t lcid, unique_byte_buffer_t pdu) override {} - void write_pdu_bcch_bch(unique_byte_buffer_t pdu) override {} - void write_pdu_bcch_dlsch(unique_byte_buffer_t pdu) override {} - void write_pdu_pcch(unique_byte_buffer_t pdu) override {} - void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t sdu) override {} - const char* get_rb_name(uint32_t lcid) override { return "lcid"; } - void write_sdu(uint32_t lcid, unique_byte_buffer_t sdu, int sn = -1) override {} - bool is_eps_bearer_id_enabled(uint32_t eps_bearer_id) { return false; } - void notify_pdcp_integrity_error(uint32_t lcid) override {} - bool is_lcid_enabled(uint32_t lcid) override { return false; } -}; + int ret = SRSRAN_ERROR; + + rrc_dummy rrc_dummy; + pdcp_dummy pdcp_dummy; + + srsue::usim usim(srslog::fetch_basic_logger("USIM")); + usim_args_t args; + args.mode = "soft"; + args.algo = "xor"; + args.imei = "353490069873319"; + args.imsi = "001010123456789"; + args.k = "00112233445566778899aabbccddeeff"; + args.op = "63BFA50EE6523365FF14C1F45F88737D"; + usim.init(&args); -class rrc_dummy : public rrc_interface_nas -{ -public: - rrc_dummy() : last_sdu_len(0) { - plmns[0].plmn_id.from_number(mcc, mnc); - plmns[0].tac = 0xffff; - } - void init(nas* nas_) { nas_ptr = nas_; } - void write_sdu(unique_byte_buffer_t sdu) - { - last_sdu_len = sdu->N_bytes; - // printf("NAS generated SDU (len=%d):\n", sdu->N_bytes); - // srsran_vec_fprint_byte(stdout, sdu->msg, sdu->N_bytes); - } - const char* get_rb_name(uint32_t lcid) { return "lcid"; } - uint32_t get_last_sdu_len() { return last_sdu_len; } - void reset() { last_sdu_len = 0; } + nas_args_t nas_cfg; + nas_cfg.force_imsi_attach = true; + nas_cfg.eia = "1,2,3"; + nas_cfg.eea = "0,1,2,3"; + nas_cfg.apn_name = "test123"; - bool plmn_search() - { - nas_ptr->plmn_search_completed(plmns, 1); - return true; - } - void plmn_select(srsran::plmn_id_t plmn_id){}; - void set_ue_identity(srsran::s_tmsi_t s_tmsi) {} - bool connection_request(srsran::establishment_cause_t cause, srsran::unique_byte_buffer_t sdu) - { - printf("NAS generated SDU (len=%d):\n", sdu->N_bytes); - last_sdu_len = sdu->N_bytes; - srsran_vec_fprint_byte(stdout, sdu->msg, sdu->N_bytes); - is_connected_flag = true; - nas_ptr->connection_request_completed(true); - return true; - } - bool is_connected() { return is_connected_flag; } + test_stack_dummy stack(&pdcp_dummy); + srsue::nas nas(&stack.task_sched); + srsue::gw gw; - uint16_t get_mcc() { return mcc; } - uint16_t get_mnc() { return mnc; } - void enable_capabilities() {} - uint32_t get_lcid_for_eps_bearer(const uint32_t& eps_bearer_id) { return 0; } - void paging_completed(bool outcome) {} - bool has_nr_dc() { return false; } + nas.init(&usim, &rrc_dummy, &gw, nas_cfg); + rrc_dummy.init(&nas); -private: - nas* nas_ptr; - uint32_t last_sdu_len; - nas_interface_rrc::found_plmn_t plmns[nas_interface_rrc::MAX_FOUND_PLMNS]; - bool is_connected_flag = false; -}; + gw_args_t gw_args; + gw_args.tun_dev_name = "tun0"; + gw_args.log.gw_level = "debug"; + gw_args.log.gw_hex_limit = 100000; -class test_stack_dummy : public srsue::stack_test_dummy, public stack_interface_gw, public thread -{ -public: - test_stack_dummy(pdcp_interface_stack* pdcp_) : pdcp(pdcp_), thread("DUMMY STACK") {} - void init(srsue::nas* nas_) - { - nas = nas_; - start(-1); - } - bool switch_on() { return true; } - void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu) { pdcp->write_sdu(lcid, std::move(sdu)); } - bool has_active_radio_bearer(uint32_t eps_bearer_id) { return true; } + gw.init(gw_args, &stack); + stack.init(&nas); + // trigger test + stack.switch_on(); + stack.stop(); - bool is_registered() { return true; } + // this will time out in the first place - bool start_service_request() { return true; } + // reset length of last received NAS PDU + rrc_dummy.reset(); - void run_thread() - { - running = true; - while (running) { - task_sched.tic(); - task_sched.run_pending_tasks(); - nas->run_tti(); + // finally push attach accept + byte_buffer_pool* pool = byte_buffer_pool::get_instance(); + unique_byte_buffer_t tmp = srsran::make_byte_buffer(); + TESTASSERT(tmp != nullptr); + memcpy(tmp->msg, attach_accept_pdu, sizeof(attach_accept_pdu)); + tmp->N_bytes = sizeof(attach_accept_pdu); + nas.write_pdu(LCID, std::move(tmp)); + nas_metrics_t metrics; + nas.get_metrics(&metrics); + TESTASSERT(metrics.nof_active_eps_bearer == 1); + + // check length of generated NAS SDU (attach complete) + if (rrc_dummy.get_last_sdu_len() > 3) { + ret = SRSRAN_SUCCESS; } + // ensure buffers are deleted before pool cleanup + gw.stop(); } - void stop() - { - while (not running) { - usleep(1000); - } - running = false; - wait_thread_finish(); - } - pdcp_interface_stack* pdcp = nullptr; - srsue::nas* nas = nullptr; - std::atomic running = {false}; -}; -class gw_dummy : public gw_interface_nas, public gw_interface_pdcp -{ - int setup_if_addr(uint32_t eps_bearer_id, - uint8_t pdn_type, - uint32_t ip_addr, - uint8_t* ipv6_if_id, - char* err_str) - { - return SRSRAN_SUCCESS; - } - int deactivate_eps_bearer(const uint32_t eps_bearer_id) { return SRSRAN_SUCCESS; } - int apply_traffic_flow_template(const uint8_t& eps_bearer_id, - const LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_STRUCT* tft) - { - return SRSRAN_SUCCESS; - } - void write_pdu(uint32_t lcid, unique_byte_buffer_t pdu) {} - void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t sdu) {} - void set_test_loop_mode(const test_loop_mode_state_t mode, const uint32_t ip_pdu_delay_ms = 0) {} -}; - -} // namespace srsran + return ret; +} int security_command_test() { @@ -255,73 +167,6 @@ int security_command_test() return ret; } -int mme_attach_request_test() -{ - int ret = SRSRAN_ERROR; - - rrc_dummy rrc_dummy; - pdcp_dummy pdcp_dummy; - - srsue::usim usim(srslog::fetch_basic_logger("USIM")); - usim_args_t args; - args.mode = "soft"; - args.algo = "xor"; - args.imei = "353490069873319"; - args.imsi = "001010123456789"; - args.k = "00112233445566778899aabbccddeeff"; - args.op = "63BFA50EE6523365FF14C1F45F88737D"; - usim.init(&args); - - { - nas_args_t nas_cfg; - nas_cfg.force_imsi_attach = true; - nas_cfg.apn_name = "test123"; - - test_stack_dummy stack(&pdcp_dummy); - srsue::nas nas(&stack.task_sched); - srsue::gw gw; - - nas.init(&usim, &rrc_dummy, &gw, nas_cfg); - rrc_dummy.init(&nas); - - gw_args_t gw_args; - gw_args.tun_dev_name = "tun0"; - gw_args.log.gw_level = "debug"; - gw_args.log.gw_hex_limit = 100000; - - gw.init(gw_args, &stack); - stack.init(&nas); - // trigger test - stack.switch_on(); - stack.stop(); - - // this will time out in the first place - - // reset length of last received NAS PDU - rrc_dummy.reset(); - - // finally push attach accept - byte_buffer_pool* pool = byte_buffer_pool::get_instance(); - unique_byte_buffer_t tmp = srsran::make_byte_buffer(); - TESTASSERT(tmp != nullptr); - memcpy(tmp->msg, attach_accept_pdu, sizeof(attach_accept_pdu)); - tmp->N_bytes = sizeof(attach_accept_pdu); - nas.write_pdu(LCID, std::move(tmp)); - nas_metrics_t metrics; - nas.get_metrics(&metrics); - TESTASSERT(metrics.nof_active_eps_bearer == 1); - - // check length of generated NAS SDU (attach complete) - if (rrc_dummy.get_last_sdu_len() > 3) { - ret = SRSRAN_SUCCESS; - } - // ensure buffers are deleted before pool cleanup - gw.stop(); - } - - return ret; -} - int esm_info_request_test() { int ret = SRSRAN_ERROR; @@ -348,6 +193,8 @@ int esm_info_request_test() cfg.apn_name = "srsran"; cfg.apn_user = "srsuser"; cfg.apn_pass = "srspass"; + cfg.eia = "1,2,3"; + cfg.eea = "0,1,2,3"; cfg.force_imsi_attach = true; nas.init(&usim, &rrc_dummy, &gw, cfg); @@ -388,6 +235,8 @@ int dedicated_eps_bearer_test() srsue::nas nas(&stack.task_sched); nas_args_t cfg = {}; cfg.force_imsi_attach = true; // make sure we get a fresh security context + cfg.eia = "1,2,3"; + cfg.eea = "0,1,2,3"; nas.init(&usim, &rrc_dummy, &gw, cfg); // push dedicated EPS bearer PDU to NAS @@ -462,25 +311,10 @@ int main(int argc, char** argv) // Start the log backend. srslog::init(); - if (security_command_test()) { - printf("Security command test failed.\n"); - return -1; - } + TESTASSERT(mme_attach_request_test() == SRSRAN_SUCCESS); + TESTASSERT(security_command_test() == SRSRAN_SUCCESS); + TESTASSERT(esm_info_request_test() == SRSRAN_SUCCESS); + TESTASSERT(dedicated_eps_bearer_test() == SRSRAN_SUCCESS); - if (mme_attach_request_test()) { - printf("Attach request test failed.\n"); - return -1; - } - - if (esm_info_request_test()) { - printf("ESM info request test failed.\n"); - return -1; - } - - if (dedicated_eps_bearer_test()) { - printf("Dedicated EPS bearer test failed.\n"); - return -1; - } - - return 0; + return SRSRAN_SUCCESS; } diff --git a/srsue/src/test/ttcn3/hdr/lte_ttcn3_phy.h b/srsue/src/test/ttcn3/hdr/lte_ttcn3_phy.h index 6f8dc1e26..0d248103f 100644 --- a/srsue/src/test/ttcn3/hdr/lte_ttcn3_phy.h +++ b/srsue/src/test/ttcn3/hdr/lte_ttcn3_phy.h @@ -128,8 +128,8 @@ private: int prach_tti_tx = -1; - int sr_tx_tti = -1; - bool sr_pending = false; + std::atomic sr_tx_tti = {-1}; + std::atomic sr_pending = {false}; std::mutex phy_mutex; diff --git a/srsue/src/test/ttcn3/hdr/ttcn3_syssim.h b/srsue/src/test/ttcn3/hdr/ttcn3_syssim.h index 274cea4db..8dd6fa6ec 100644 --- a/srsue/src/test/ttcn3/hdr/ttcn3_syssim.h +++ b/srsue/src/test/ttcn3/hdr/ttcn3_syssim.h @@ -285,7 +285,7 @@ private: std::vector cells; int32_t pcell_idx = -1; - // Main mutex to protect access from syssim's main thread (epoll handlers) and calls from UE's stack thread + // Main mutex to protect access from syssim's main thread (epoll handlers) and calls from UE's stack thread std::mutex syssim_mutex; // Internal function diff --git a/srsue/src/test/ttcn3/src/ttcn3_dut.cc b/srsue/src/test/ttcn3/src/ttcn3_dut.cc index 15d66f832..6484865fc 100644 --- a/srsue/src/test/ttcn3/src/ttcn3_dut.cc +++ b/srsue/src/test/ttcn3/src/ttcn3_dut.cc @@ -114,7 +114,7 @@ all_args_t parse_args(ttcn3_dut_args_t* args, int argc, char* argv[]) all_args.stack.log.gw_hex_limit = args->log_hex_level; all_args.stack.log.usim_hex_limit = args->log_hex_level; - all_args.stack.sync_queue_size = 1; + all_args.stack.sync_queue_size = MULTIQUEUE_DEFAULT_CAPACITY; return all_args; } diff --git a/srsue/src/test/ttcn3/src/ttcn3_syssim.cc b/srsue/src/test/ttcn3/src/ttcn3_syssim.cc index 40f6a4877..e38670ed6 100644 --- a/srsue/src/test/ttcn3/src/ttcn3_syssim.cc +++ b/srsue/src/test/ttcn3/src/ttcn3_syssim.cc @@ -257,8 +257,8 @@ void ttcn3_syssim::new_tti_indication(uint64_t res) // DL/UL processing if UE has selected cell dl_rnti = ue->get_dl_sched_rnti(tti); - if (SRSRAN_RNTI_ISSI(dl_rnti)) { - // deliver SIBs one after another + if (SRSRAN_RNTI_ISSI(dl_rnti) && (tti % 2 == 0)) { + // deliver SIBs one after another in every other TTI mac_interface_phy_lte::mac_grant_dl_t dl_grant = {}; dl_grant.tti = tti; dl_grant.pid = get_pid(tti); @@ -266,7 +266,7 @@ void ttcn3_syssim::new_tti_indication(uint64_t res) dl_grant.tb[0].tbs = cells[pcell_idx]->sibs[cells[pcell_idx]->sib_idx]->N_bytes; dl_grant.tb[0].ndi = get_ndi_for_new_dl_tx(tti); ue->new_tb(dl_grant, cells[pcell_idx]->sibs[cells[pcell_idx]->sib_idx]->msg); - logger.info("Delivered SIB%d for pcell_idx=%d", cells[pcell_idx]->sib_idx, pcell_idx); + logger.info("Delivered SIB%d for pcell_idx=%d", cells[pcell_idx]->sib_idx + 1, pcell_idx); cells[pcell_idx]->sib_idx = (cells[pcell_idx]->sib_idx + 1) % cells[pcell_idx]->sibs.size(); } else if (SRSRAN_RNTI_ISRAR(dl_rnti)) { if (prach_tti != -1) { diff --git a/srsue/src/ue.cc b/srsue/src/ue.cc index 63dbdfa47..f5e4e0a07 100644 --- a/srsue/src/ue.cc +++ b/srsue/src/ue.cc @@ -21,12 +21,12 @@ #include "srsue/hdr/ue.h" #include "srsran/build_info.h" +#include "srsran/common/standard_streams.h" #include "srsran/common/string_helpers.h" #include "srsran/radio/radio.h" #include "srsran/radio/radio_null.h" #include "srsran/srsran.h" #include "srsue/hdr/phy/phy.h" -#include "srsue/hdr/phy/vnf_phy_nr.h" #include "srsue/hdr/stack/ue_stack_lte.h" #include "srsue/hdr/stack/ue_stack_nr.h" #include @@ -106,6 +106,7 @@ int ue::init(const all_args_t& args_) phy_args_nr.nof_phy_threads = args.phy.nof_phy_threads; phy_args_nr.worker_cpu_mask = args.phy.worker_cpu_mask; phy_args_nr.log = args.phy.log; + phy_args_nr.store_pdsch_ko = args.phy.nr_store_pdsch_ko; if (lte_phy->init(phy_args_nr, lte_stack.get(), lte_radio.get())) { srsran::console("Error initializing NR PHY.\n"); ret = SRSRAN_ERROR; @@ -130,7 +131,7 @@ int ue::init(const all_args_t& args_) logger.info("Initializing NR stack"); std::unique_ptr nr_stack(new srsue::ue_stack_nr()); std::unique_ptr nr_radio(new srsran::radio_null); - std::unique_ptr nr_phy(new srsue::vnf_phy_nr); + std::unique_ptr nr_phy; std::unique_ptr gw_ptr(new gw()); // Init layers @@ -139,12 +140,12 @@ int ue::init(const all_args_t& args_) return SRSRAN_ERROR; } - if (nr_phy->init(args.phy, nr_stack.get())) { + if (nr_phy->init(args.phy)) { srsran::console("Error initializing PHY.\n"); return SRSRAN_ERROR; } - if (nr_stack->init(args.stack, nr_phy.get(), gw_ptr.get())) { + if (nr_stack->init(args.stack)) { srsran::console("Error initializing stack.\n"); return SRSRAN_ERROR; } diff --git a/srsue/ue.conf.example b/srsue/ue.conf.example index 0b868aaf5..73620dfed 100644 --- a/srsue/ue.conf.example +++ b/srsue/ue.conf.example @@ -374,6 +374,15 @@ enable = false #nof_in_sync_events = 10 #nof_out_of_sync_events = 20 +##################################################################### +# PHY NR specific configuration options +# +# store_pdsch_ko: Dumps the PDSCH baseband samples into a file on KO reception +# +##################################################################### +[phy.nr] +#store_pdsch_ko = false + ##################################################################### # Simulation configuration options # diff --git a/test/phy/CMakeLists.txt b/test/phy/CMakeLists.txt index 61c5d09f3..42e48f13e 100644 --- a/test/phy/CMakeLists.txt +++ b/test/phy/CMakeLists.txt @@ -21,7 +21,6 @@ if (RF_FOUND AND ENABLE_SRSUE AND ENABLE_SRSENB) add_executable(nr_phy_test nr_phy_test.cc) target_link_libraries(nr_phy_test - srsue_phy_nr srsue_phy srsran_common srsran_phy @@ -33,5 +32,37 @@ if (RF_FOUND AND ENABLE_SRSUE AND ENABLE_SRSENB) ${Boost_LIBRARIES} ${ATOMIC_LIBS}) - add_nr_test(nr_phy_test nr_phy_test) + add_nr_test(nr_phy_test_10MHz_dl_only nr_phy_test + --duration=100 + --gnb.stack.pdsch.slots=\"0,1,2,3,4,5\" + --gnb.stack.pusch.slots=\"\") + + add_nr_test(nr_phy_test_10MHz_ul_only nr_phy_test + --duration=100 # 100 slots + --gnb.stack.pdsch.slots=6 # No PDSCH + --gnb.stack.pusch.slots=6,7,8,9 # All possible UL slots + --gnb.stack.pusch.start=0 # Start at RB 0 + --gnb.stack.pusch.length=52 # Full 10 MHz BW + --gnb.stack.pusch.mcs=28 # Maximum MCS + ) + + add_nr_test(nr_phy_test_10MHz_bidir nr_phy_test + --duration=100 # 100 slots + --gnb.stack.pdsch.slots=0,1,2,3,4,5 # All possible DL slots + --gnb.stack.pdsch.start=0 # Start at RB 0 + --gnb.stack.pdsch.length=52 # Full 10 MHz BW + --gnb.stack.pdsch.mcs=28 # Maximum MCS + --gnb.stack.pusch.slots=6,7,8,9 # All possible UL slots + --gnb.stack.pusch.start=0 # Start at RB 0 + --gnb.stack.pusch.length=52 # Full 10 MHz BW + --gnb.stack.pusch.mcs=28 # Maximum MCS + ) + + add_nr_test(nr_phy_test_10MHz_prach nr_phy_test + --duration=1000 # 100 slots + --gnb.stack.pdsch.slots=6 # No PDSCH + --gnb.stack.pusch.slots=0 # No PUSCH + --ue.stack.prach.period=30 # Transmit PRACH every 30 radio frames + --ue.stack.prach.preamble=10 # Use preamble 10 + ) endif () diff --git a/test/phy/dummy_gnb_stack.h b/test/phy/dummy_gnb_stack.h index f38601811..4dcb36790 100644 --- a/test/phy/dummy_gnb_stack.h +++ b/test/phy/dummy_gnb_stack.h @@ -25,32 +25,44 @@ #include "dummy_rx_harq_proc.h" #include "dummy_tx_harq_proc.h" #include +#include #include #include #include #include +#include #include class gnb_dummy_stack : public srsenb::stack_interface_phy_nr { -private: - srslog::basic_logger& logger = srslog::fetch_basic_logger("GNB STK"); - const uint16_t rnti = 0x1234; - const uint32_t mcs = 1; - srsran::circular_array dci_dl_location; - srsran::circular_array dci_ul_location; - srsran::circular_array dl_data_to_ul_ack; - uint32_t ss_id = 0; - uint32_t dl_freq_res = 0; - uint32_t dl_time_res = 0; - uint32_t ul_freq_res = 0; - uint32_t ul_time_res = 0; - srsran_random_t random_gen = nullptr; - srsran::phy_cfg_nr_t phy_cfg = {}; - bool valid = false; +public: + struct prach_metrics_t { + uint32_t count; + float avg_ta; + }; - std::mutex mac_metrics_mutex; - srsenb::mac_ue_metrics_t mac_metrics = {}; + struct metrics_t { + std::map prach = {}; ///< PRACH metrics indexed with premable index + srsenb::mac_ue_metrics_t mac = {}; ///< MAC metrics + }; + +private: + srslog::basic_logger& logger = srslog::fetch_basic_logger("GNB STK"); + const uint16_t rnti = 0x1234; + struct { + srsran::circular_array dci_location; + uint32_t mcs; + uint32_t freq_res = 0; + std::set slots; + } dl, ul; + srsran::circular_array dl_data_to_ul_ack; + uint32_t ss_id = 0; + srsran_random_t random_gen = nullptr; + srsran::phy_cfg_nr_t phy_cfg = {}; + bool valid = false; + + std::mutex metrics_mutex; + metrics_t metrics = {}; // HARQ feedback class pending_ack_t @@ -127,6 +139,10 @@ private: bool schedule_pdsch(const srsran_slot_cfg_t& slot_cfg, dl_sched_t& dl_sched) { + if (dl.slots.count(SRSRAN_SLOT_NR_MOD(srsran_subcarrier_spacing_15kHz, slot_cfg.idx)) == 0) { + return true; + } + // Instantiate PDCCH and PDSCH pdcch_dl_t pdcch = {}; pdsch_t pdsch = {}; @@ -138,7 +154,7 @@ private: pdcch.dci_cfg = phy_cfg.get_dci_cfg(); // Fill DCI context - if (not phy_cfg.get_dci_ctx_pdsch_rnti_c(ss_id, dci_dl_location[slot_cfg.idx], rnti, pdcch.dci.ctx)) { + if (not phy_cfg.get_dci_ctx_pdsch_rnti_c(ss_id, dl.dci_location[slot_cfg.idx], rnti, pdcch.dci.ctx)) { logger.error("Error filling PDSCH DCI context"); return false; } @@ -148,9 +164,9 @@ private: // Fill DCI fields srsran_dci_dl_nr_t& dci = pdcch.dci; - dci.freq_domain_assigment = dl_freq_res; - dci.time_domain_assigment = dl_time_res; - dci.mcs = mcs; + dci.freq_domain_assigment = dl.freq_res; + dci.time_domain_assigment = 0; + dci.mcs = dl.mcs; dci.rv = 0; dci.ndi = (slot_cfg.idx / SRSRAN_NOF_SF_X_FRAME) % 2; dci.pid = slot_cfg.idx % SRSRAN_NOF_SF_X_FRAME; @@ -201,6 +217,10 @@ private: bool schedule_pusch(const srsran_slot_cfg_t& slot_cfg, dl_sched_t& dl_sched) { + if (ul.slots.count(SRSRAN_SLOT_NR_MOD(srsran_subcarrier_spacing_15kHz, slot_cfg.idx + 4)) == 0) { + return true; + } + // Instantiate PDCCH pdcch_ul_t pdcch = {}; @@ -208,17 +228,17 @@ private: pdcch.dci_cfg = phy_cfg.get_dci_cfg(); // Fill DCI context - if (not phy_cfg.get_dci_ctx_pusch_rnti_c(ss_id, dci_ul_location[slot_cfg.idx], rnti, pdcch.dci.ctx)) { + if (not phy_cfg.get_dci_ctx_pusch_rnti_c(ss_id, ul.dci_location[slot_cfg.idx], rnti, pdcch.dci.ctx)) { logger.error("Error filling PDSCH DCI context"); return false; } // Fill DCI fields srsran_dci_ul_nr_t& dci = pdcch.dci; - dci.freq_domain_assigment = ul_freq_res; - dci.time_domain_assigment = ul_time_res; + dci.freq_domain_assigment = ul.freq_res; + dci.time_domain_assigment = 0; dci.freq_hopping_flag = 0; - dci.mcs = mcs; + dci.mcs = ul.mcs; dci.rv = 0; dci.ndi = (slot_cfg.idx / SRSRAN_NOF_SF_X_FRAME) % 2; dci.pid = slot_cfg.idx % SRSRAN_NOF_SF_X_FRAME; @@ -245,16 +265,16 @@ private: bool handle_uci_data(const srsran_uci_cfg_nr_t& cfg, const srsran_uci_value_nr_t& value) { - std::unique_lock lock(mac_metrics_mutex); + std::unique_lock lock(metrics_mutex); for (uint32_t i = 0; i < cfg.ack.count; i++) { const srsran_harq_ack_bit_t* ack_bit = &cfg.ack.bits[i]; bool is_ok = (value.ack[i] == 1) and value.valid; uint32_t tb_count = (ack_bit->tb0 ? 1 : 0) + (ack_bit->tb1 ? 1 : 0); - mac_metrics.tx_brate += tx_harq_proc[ack_bit->pid].get_tbs(); - mac_metrics.tx_pkts += tb_count; + metrics.mac.tx_brate += tx_harq_proc[ack_bit->pid].get_tbs(); + metrics.mac.tx_pkts += tb_count; if (not is_ok) { - mac_metrics.tx_errors += tb_count; + metrics.mac.tx_errors += tb_count; logger.debug("NACK received!"); } } @@ -264,27 +284,32 @@ private: public: struct args_t { - srsran::phy_cfg_nr_t phy_cfg; ///< Physical layer configuration - uint16_t rnti = 0x1234; ///< C-RNTI - uint32_t mcs = 10; ///< Modulation code scheme - uint32_t ss_id = 1; ///< Search Space identifier - uint32_t pdcch_aggregation_level = 0; ///< PDCCH aggregation level - uint32_t pdcch_dl_candidate_index = 0; ///< PDCCH DL DCI candidate index - uint32_t pdcch_ul_candidate_index = 1; ///< PDCCH UL DCI candidate index - uint32_t dl_start_rb = 0; ///< Start resource block - uint32_t dl_length_rb = 0; ///< Number of resource blocks - uint32_t ul_start_rb = 0; ///< Start resource block - uint32_t ul_length_rb = 0; ///< Number of resource blocks - uint32_t dl_time_res = 0; ///< PDSCH time resource - std::string log_level = "debug"; + srsran::phy_cfg_nr_t phy_cfg; ///< Physical layer configuration + uint16_t rnti = 0x1234; ///< C-RNTI + uint32_t ss_id = 1; ///< Search Space identifier + uint32_t pdcch_aggregation_level = 0; ///< PDCCH aggregation level + uint32_t pdcch_dl_candidate = 0; ///< PDCCH DL DCI candidate index + uint32_t pdcch_ul_candidate = 1; ///< PDCCH UL DCI candidate index + struct { + uint32_t rb_start = 0; ///< Start frequency domain resource block + uint32_t rb_length = 10; ///< Number of frequency domain resource blocks + uint32_t mcs = 10; ///< Modulation code scheme + std::string slots = ""; ///< Slot list, empty string means no scheduling + } pdsch, pusch; + std::string log_level = "warning"; }; - gnb_dummy_stack(args_t args) : - mcs(args.mcs), rnti(args.rnti), dl_time_res(args.dl_time_res), phy_cfg(args.phy_cfg), ss_id(args.ss_id) + gnb_dummy_stack(const args_t& args) : rnti(args.rnti), phy_cfg(args.phy_cfg), ss_id(args.ss_id) { random_gen = srsran_random_init(0x1234); logger.set_level(srslog::str_to_basic_level(args.log_level)); + dl.mcs = args.pdsch.mcs; + ul.mcs = args.pusch.mcs; + + srsran::string_parse_list(args.pdsch.slots, ',', dl.slots); + srsran::string_parse_list(args.pusch.slots, ',', ul.slots); + // Select DCI locations for (uint32_t slot = 0; slot < SRSRAN_NOF_SF_X_FRAME; slot++) { srsran::bounded_vector locations; @@ -296,31 +321,31 @@ public: } // DCI DL - if (args.pdcch_dl_candidate_index >= locations.size()) { + if (args.pdcch_dl_candidate >= locations.size()) { logger.error("Candidate index %d exceeds the number of candidates %d for aggregation level %d", - args.pdcch_dl_candidate_index, + args.pdcch_dl_candidate, (uint32_t)locations.size(), args.pdcch_aggregation_level); return; } - dci_dl_location[slot] = locations[args.pdcch_dl_candidate_index]; + dl.dci_location[slot] = locations[args.pdcch_dl_candidate]; // DCI UL - if (args.pdcch_ul_candidate_index >= locations.size()) { + if (args.pdcch_ul_candidate >= locations.size()) { logger.error("Candidate index %d exceeds the number of candidates %d for aggregation level %d", - args.pdcch_ul_candidate_index, + args.pdcch_ul_candidate, (uint32_t)locations.size(), args.pdcch_aggregation_level); return; } - dci_ul_location[slot] = locations[args.pdcch_ul_candidate_index]; + ul.dci_location[slot] = locations[args.pdcch_ul_candidate]; } // Select DL frequency domain resources - dl_freq_res = srsran_ra_nr_type1_riv(args.phy_cfg.carrier.nof_prb, args.dl_start_rb, args.dl_length_rb); + dl.freq_res = srsran_ra_nr_type1_riv(args.phy_cfg.carrier.nof_prb, args.pdsch.rb_start, args.pdsch.rb_length); // Select DL frequency domain resources - ul_freq_res = srsran_ra_nr_type1_riv(args.phy_cfg.carrier.nof_prb, args.ul_start_rb, args.ul_length_rb); + ul.freq_res = srsran_ra_nr_type1_riv(args.phy_cfg.carrier.nof_prb, args.pusch.rb_start, args.pusch.rb_length); // Setup DL Data to ACK timing for (uint32_t i = 0; i < SRSRAN_NOF_SF_X_FRAME; i++) { @@ -447,19 +472,27 @@ public: } if (not pusch_info.pusch_data.tb[0].crc) { - mac_metrics.rx_errors++; + metrics.mac.rx_errors++; } - mac_metrics.rx_brate += rx_harq_proc[pusch_info.pid].get_tbs(); - mac_metrics.rx_pkts++; + metrics.mac.rx_brate += rx_harq_proc[pusch_info.pid].get_tbs(); + metrics.mac.rx_pkts++; return SRSRAN_SUCCESS; } - srsenb::mac_ue_metrics_t get_metrics() + void rach_detected(const rach_info_t& rach_info) override { - std::unique_lock lock(mac_metrics_mutex); + std::unique_lock lock(metrics_mutex); + prach_metrics_t& prach_metrics = metrics.prach[rach_info.preamble]; + prach_metrics.avg_ta = SRSRAN_VEC_SAFE_CMA((float)rach_info.time_adv, prach_metrics.avg_ta, prach_metrics.count); + prach_metrics.count++; + } - return mac_metrics; + metrics_t get_metrics() + { + std::unique_lock lock(metrics_mutex); + + return metrics; } }; diff --git a/test/phy/dummy_ue_stack.h b/test/phy/dummy_ue_stack.h index bc759680a..ab54a4f16 100644 --- a/test/phy/dummy_ue_stack.h +++ b/test/phy/dummy_ue_stack.h @@ -26,23 +26,60 @@ class ue_dummy_stack : public srsue::stack_interface_phy_nr { +public: + struct prach_metrics_t { + uint32_t count; + }; + + struct metrics_t { + std::map prach = {}; ///< PRACH metrics indexed with premable index + }; + private: - srsran_random_t random_gen = srsran_random_init(0x4567); - uint16_t rnti = 0; - bool valid = false; + srsran_random_t random_gen = srsran_random_init(0x4567); + uint16_t rnti = 0; + bool valid = false; + uint32_t sr_period = 0; + uint32_t sr_count = 0; + uint32_t prach_period = 0; + uint32_t prach_preamble = 0; + metrics_t metrics = {}; + srsue::phy_interface_stack_nr& phy; srsran::circular_array tx_harq_proc; srsran::circular_array rx_harq_proc; public: struct args_t { - uint16_t rnti = 0x1234; + uint16_t rnti = 0x1234; ///< C-RNTI for PUSCH and PDSCH transmissions + uint32_t sr_period = 0; ///< Indicates positive SR period in number of opportunities. Set to 0 to disable. + uint32_t prach_period = 0; ///< Requests PHY to transmit PRACH periodically in frames. Set to 0 to disable. + uint32_t prach_preamble = 0; ///< Requests PHY to transmit PRACH periodically in frames. Set to 0 to disable. }; - ue_dummy_stack(const args_t& args) : rnti(args.rnti) { valid = true; } + ue_dummy_stack(const args_t& args, srsue::phy_interface_stack_nr& phy_) : + rnti(args.rnti), + sr_period(args.sr_period), + prach_period(args.prach_period), + prach_preamble(args.prach_preamble), + phy(phy_) + { + valid = true; + } ~ue_dummy_stack() { srsran_random_free(random_gen); } - void in_sync() override {} - void out_of_sync() override {} - void run_tti(const uint32_t tti) override {} + void in_sync() override {} + void out_of_sync() override {} + void run_tti(const uint32_t tti) override + { + // Run PRACH + if (prach_period != 0) { + uint32_t slot_idx = tti % SRSRAN_NSLOTS_PER_FRAME_NR(srsran_subcarrier_spacing_15kHz); + uint32_t sfn = tti / SRSRAN_NSLOTS_PER_FRAME_NR(srsran_subcarrier_spacing_15kHz); + if (slot_idx == 0 and sfn % prach_period == 0) { + phy.send_prach(0, prach_preamble, 0.0f, 0.0f); + metrics.prach[prach_preamble].count++; + } + } + } int sf_indication(const uint32_t tti) override { return 0; } sched_rnti_t get_dl_sched_rnti_nr(const uint32_t tti) override { return {rnti, srsran_rnti_type_c}; } sched_rnti_t get_ul_sched_rnti_nr(const uint32_t tti) override { return {rnti, srsran_rnti_type_c}; } @@ -60,11 +97,24 @@ public: action->tb.enabled = true; action->tb.payload = &tx_harq_proc[grant.pid].get_tb(grant.tbs); action->tb.softbuffer = &tx_harq_proc[grant.pid].get_softbuffer(grant.ndi); - srsran_random_byte_vector(random_gen, action->tb.payload->msg, grant.tbs / 8); + srsran_random_byte_vector(random_gen, action->tb.payload->msg, grant.tbs); } void prach_sent(uint32_t tti, uint32_t s_id, uint32_t t_id, uint32_t f_id, uint32_t ul_carrier_id) override {} - bool sr_opportunity(uint32_t tti, uint32_t sr_id, bool meas_gap, bool ul_sch_tx) override { return false; } + bool sr_opportunity(uint32_t tti, uint32_t sr_id, bool meas_gap, bool ul_sch_tx) override + { + if (sr_period == 0) { + return false; + } + + bool ret = (sr_count % sr_period == 0); + + sr_count++; + + return ret; + } bool is_valid() const { return valid; } + + metrics_t get_metrics() { return metrics; } }; #endif // SRSRAN_DUMMY_UE_STACK_H diff --git a/test/phy/nr_phy_test.cc b/test/phy/nr_phy_test.cc index 610b1d8d9..c0ea0eec2 100644 --- a/test/phy/nr_phy_test.cc +++ b/test/phy/nr_phy_test.cc @@ -24,11 +24,88 @@ #include "srsran/common/phy_cfg_nr_default.h" #include "srsran/common/test_common.h" #include "test_bench.h" +#include +#include + +// shorten boost program options namespace +namespace bpo = boost::program_options; test_bench::args_t::args_t(int argc, char** argv) { - // Flag configuration as valid - valid = true; + bpo::options_description options("Test bench options"); + bpo::options_description options_gnb_stack("gNb stack and scheduling related options"); + bpo::options_description options_gnb_phy("gNb PHY related options"); + bpo::options_description options_ue_stack("UE stack options"); + bpo::options_description options_ue_phy("UE stack options"); + + uint16_t rnti = 0x1234; + + gnb_stack.pdsch.slots = "0,1,2,3,4,5"; + gnb_stack.pusch.slots = "6,7,8,9"; + + // clang-format off + options.add_options() + ("rnti", bpo::value(&rnti)->default_value(rnti), "UE RNTI") + ("duration", bpo::value(&durations_slots)->default_value(durations_slots), "Test duration in slots") + ("lib.log.level", bpo::value(&phy_lib_log_level)->default_value(phy_lib_log_level), "PHY librray log level") + ; + + options_gnb_stack.add_options() + ("gnb.stack.pdcch.aggregation_level", bpo::value(&gnb_stack.pdcch_aggregation_level)->default_value(gnb_stack.pdcch_aggregation_level), "PDCCH aggregation level") + ("gnb.stack.pdsch.candidate", bpo::value(&gnb_stack.pdcch_dl_candidate)->default_value(gnb_stack.pdcch_dl_candidate), "PDCCH candidate index for PDSCH") + ("gnb.stack.pdsch.start", bpo::value(&gnb_stack.pdsch.rb_start)->default_value(0), "PDSCH scheduling frequency allocation start") + ("gnb.stack.pdsch.length", bpo::value(&gnb_stack.pdsch.rb_length)->default_value(gnb_stack.pdsch.rb_length), "PDSCH scheduling frequency allocation length") + ("gnb.stack.pdsch.slots", bpo::value(&gnb_stack.pdsch.slots)->default_value(gnb_stack.pdsch.slots), "Slots enabled for PDSCH") + ("gnb.stack.pdsch.mcs", bpo::value(&gnb_stack.pdsch.mcs)->default_value(gnb_stack.pdsch.mcs), "PDSCH scheduling modulation code scheme") + ("gnb.stack.pusch.candidate", bpo::value(&gnb_stack.pdcch_ul_candidate)->default_value(gnb_stack.pdcch_ul_candidate), "PDCCH candidate index for PUSCH") + ("gnb.stack.pusch.start", bpo::value(&gnb_stack.pusch.rb_start)->default_value(0), "PUSCH scheduling frequency allocation start") + ("gnb.stack.pusch.length", bpo::value(&gnb_stack.pusch.rb_length)->default_value(gnb_stack.pusch.rb_length), "PUSCH scheduling frequency allocation length") + ("gnb.stack.pusch.slots", bpo::value(&gnb_stack.pusch.slots)->default_value(gnb_stack.pusch.slots), "Slots enabled for PUSCH") + ("gnb.stack.pusch.mcs", bpo::value(&gnb_stack.pusch.mcs)->default_value(gnb_stack.pusch.mcs), "PUSCH scheduling modulation code scheme") + ("gnb.stack.log.level", bpo::value(&gnb_stack.log_level)->default_value(gnb_stack.log_level), "Stack log level") + ; + + options_gnb_phy.add_options() + ("gnb.phy.nof_threads", bpo::value(&gnb_phy.nof_phy_threads)->default_value(1), "Number of threads") + ("gnb.phy.log.level", bpo::value(&gnb_phy.log.phy_level)->default_value("warning"), "gNb PHY log level") + ("gnb.phy.log.hex_limit", bpo::value(&gnb_phy.log.phy_hex_limit)->default_value(0), "gNb PHY log hex limit") + ("gnb.phy.log.id_preamble", bpo::value(&gnb_phy.log.id_preamble)->default_value("GNB/"), "gNb PHY log ID preamble") + ("gnb.phy.pusch.max_iter", bpo::value(&gnb_phy.pusch_max_nof_iter)->default_value(10), "PUSCH LDPC max number of iterations") + ; + + options_ue_phy.add_options() + ("ue.phy.nof_threads", bpo::value(&ue_phy.nof_phy_threads)->default_value(1), "Number of threads") + ("ue.phy.log.level", bpo::value(&ue_phy.log.phy_level)->default_value("warning"), "UE PHY log level") + ("ue.phy.log.hex_limit", bpo::value(&ue_phy.log.phy_hex_limit)->default_value(0), "UE PHY log hex limit") + ("ue.phy.log.id_preamble", bpo::value(&ue_phy.log.id_preamble)->default_value(" UE/"), "UE PHY log ID preamble") + ; + + options_ue_stack.add_options() + ("ue.stack.sr.period", bpo::value(&ue_stack.sr_period)->default_value(ue_stack.sr_period), "SR period in number of opportunities. Set 0 to disable and 1 for all.") + ("ue.stack.prach.period", bpo::value(&ue_stack.prach_period)->default_value(ue_stack.prach_period), "PRACH period in SFN. Set 0 to disable and 1 for all.") + ("ue.stack.prach.preamble", bpo::value(&ue_stack.prach_preamble)->default_value(ue_stack.prach_preamble), "PRACH preamble. Set 0 to disable and 1 for all.") + ; + + options.add(options_gnb_stack).add(options_gnb_phy).add(options_ue_stack).add(options_ue_phy).add_options() + ("help", "Show this message") + ; + // clang-format on + + bpo::variables_map vm; + try { + bpo::store(bpo::command_line_parser(argc, argv).options(options).run(), vm); + bpo::notify(vm); + } catch (bpo::error& e) { + std::cerr << e.what() << std::endl; + return; + } + + // help option was given or error - print usage and exit + if (vm.count("help")) { + std::cout << "Usage: " << argv[0] << " [OPTIONS] config_file" << std::endl << std::endl; + std::cout << options << std::endl << std::endl; + return; + } // Load default reference configuration srsran::phy_cfg_nr_default_t::reference_cfg_t reference_cfg; @@ -39,6 +116,24 @@ test_bench::args_t::args_t(int argc, char** argv) cell_list[0].rf_port = 0; cell_list[0].cell_id = 0; cell_list[0].pdcch = phy_cfg.pdcch; + + ue_stack.rnti = rnti; + + gnb_stack.rnti = rnti; + gnb_stack.phy_cfg = phy_cfg; + + if (gnb_stack.pdsch.rb_length == 0) { + gnb_stack.pdsch.rb_length = phy_cfg.carrier.nof_prb; + gnb_stack.pdsch.rb_start = 0; + } + + if (gnb_stack.pusch.rb_length == 0) { + gnb_stack.pusch.rb_length = phy_cfg.carrier.nof_prb; + gnb_stack.pdsch.rb_start = 0; + } + + // Flag configuration as valid + valid = true; } int main(int argc, char** argv) @@ -47,48 +142,19 @@ int main(int argc, char** argv) // Parse test bench arguments test_bench::args_t args(argc, argv); - args.gnb_args.log_id_preamble = "GNB/"; - args.gnb_args.log_level = "info"; - args.gnb_args.nof_phy_threads = 1; - args.ue_args.log.id_preamble = " UE/"; - args.ue_args.log.phy_level = "info"; - args.ue_args.log.phy_hex_limit = 1; - args.ue_args.nof_phy_threads = 1; // Parse arguments TESTASSERT(args.valid); - // Create UE stack arguments - ue_dummy_stack::args_t ue_stack_args = {}; - ue_stack_args.rnti = 0x1234; - - // Create UE stack - ue_dummy_stack ue_stack(ue_stack_args); - TESTASSERT(ue_stack.is_valid()); - - // Create GNB stack arguments - gnb_dummy_stack::args_t gnb_stack_args = {}; - gnb_stack_args.rnti = 0x1234; - gnb_stack_args.mcs = 10; - gnb_stack_args.phy_cfg = args.phy_cfg; - gnb_stack_args.dl_start_rb = 0; - gnb_stack_args.dl_length_rb = args.phy_cfg.carrier.nof_prb; - gnb_stack_args.ul_start_rb = 0; - gnb_stack_args.ul_length_rb = args.phy_cfg.carrier.nof_prb; - - // Create GNB stack - gnb_dummy_stack gnb_stack(gnb_stack_args); - TESTASSERT(gnb_stack.is_valid()); - // Create test bench - test_bench tb(args, gnb_stack, ue_stack); + test_bench tb(args); // Assert bench is initialised correctly TESTASSERT(tb.is_initialised()); // Run per TTI basis - for (uint32_t i = 0; i < 1000; i++) { - TESTASSERT(tb.run_tti()); + while (tb.run_tti()) { + ; // Do nothing } // Stop test bench @@ -98,44 +164,83 @@ int main(int argc, char** argv) srslog::flush(); // Retrieve MAC metrics - srsenb::mac_ue_metrics_t mac_metrics = gnb_stack.get_metrics(); + test_bench::metrics_t metrics = tb.get_gnb_metrics(); - // Print metrics - float pdsch_bler = 0.0f; - if (mac_metrics.tx_pkts != 0) { - pdsch_bler = (float)mac_metrics.tx_errors / (float)mac_metrics.tx_pkts; - } - float pusch_bler = 0.0f; - if (mac_metrics.rx_pkts != 0) { - pusch_bler = (float)mac_metrics.rx_errors / (float)mac_metrics.rx_pkts; - } - float pdsch_shed_rate = 0.0f; - if (mac_metrics.tx_pkts != 0) { - pdsch_shed_rate = (float)mac_metrics.tx_brate / (float)mac_metrics.tx_pkts / 1000.0f; - } - float pusch_shed_rate = 0.0f; - if (mac_metrics.rx_pkts != 0) { - pusch_shed_rate = (float)mac_metrics.rx_brate / (float)mac_metrics.rx_pkts / 1000.0f; + // Print PDSCH metrics if scheduled + if (metrics.gnb_stack.mac.tx_pkts > 0) { + float pdsch_bler = 0.0f; + pdsch_bler = (float)metrics.gnb_stack.mac.tx_errors / (float)metrics.gnb_stack.mac.tx_pkts; + + float pdsch_shed_rate = 0.0f; + pdsch_shed_rate = (float)metrics.gnb_stack.mac.tx_brate / (float)metrics.gnb_stack.mac.tx_pkts / 1000.0f; + + srsran::console("PDSCH:\n"); + srsran::console(" Count: %d\n", metrics.gnb_stack.mac.tx_pkts); + srsran::console(" BLER: %f\n", pdsch_bler); + srsran::console(" Sched Rate: %f Mbps\n", pdsch_shed_rate); + srsran::console(" Net Rate: %f Mbps\n", (1.0f - pdsch_bler) * pdsch_shed_rate); + srsran::console(" Retx Rate: %f Mbps\n", pdsch_bler * pdsch_shed_rate); + srsran::console("\n"); } - srsran::console("PDSCH:\n"); - srsran::console(" Count: %d\n", mac_metrics.tx_pkts); - srsran::console(" BLER: %f\n", pdsch_bler); - srsran::console(" Sched Rate: %f Mbps\n", pdsch_shed_rate); - srsran::console(" Net Rate: %f Mbps\n", (1.0f - pdsch_bler) * pdsch_shed_rate); - srsran::console(" Retx Rate: %f Mbps\n", pdsch_bler * pdsch_shed_rate); + // Print PUSCH metrics if scheduled + if (metrics.gnb_stack.mac.rx_pkts > 0) { + float pusch_bler = 0.0f; + if (metrics.gnb_stack.mac.rx_pkts != 0) { + pusch_bler = (float)metrics.gnb_stack.mac.rx_errors / (float)metrics.gnb_stack.mac.rx_pkts; + } - srsran::console("\n"); - srsran::console("PUSCH:\n"); - srsran::console(" Count: %d\n", mac_metrics.rx_pkts); - srsran::console(" BLER: %f\n", pusch_bler); - srsran::console(" Sched Rate: %f Mbps\n", pusch_shed_rate); - srsran::console(" Net Rate: %f Mbps\n", (1.0f - pusch_bler) * pusch_shed_rate); - srsran::console(" Retx Rate: %f Mbps\n", pusch_bler * pusch_shed_rate); + float pusch_shed_rate = 0.0f; + if (metrics.gnb_stack.mac.rx_pkts != 0) { + pusch_shed_rate = (float)metrics.gnb_stack.mac.rx_brate / (float)metrics.gnb_stack.mac.rx_pkts / 1000.0f; + } + + srsran::console("PUSCH:\n"); + srsran::console(" Count: %d\n", metrics.gnb_stack.mac.rx_pkts); + srsran::console(" BLER: %f\n", pusch_bler); + srsran::console(" Sched Rate: %f Mbps\n", pusch_shed_rate); + srsran::console(" Net Rate: %f Mbps\n", (1.0f - pusch_bler) * pusch_shed_rate); + srsran::console(" Retx Rate: %f Mbps\n", pusch_bler * pusch_shed_rate); + srsran::console("\n"); + } + + // Print PRACH + if (metrics.ue_stack.prach.size() > 0) { + srsran::console("PRACH:\n"); + srsran::console(" UE transmitted:\n"); + srsran::console(" +------------+------------+\n"); + srsran::console(" | %10s | %10s |\n", "preamble", "count"); + srsran::console(" +------------+------------+\n"); + + for (const auto& p : metrics.ue_stack.prach) { + srsran::console(" | %10d | %10d |\n", p.first, p.second.count); + + // Ensure the detected count matches with transmission + TESTASSERT(metrics.gnb_stack.prach.count(p.first)); + TESTASSERT(metrics.gnb_stack.prach[p.first].count == p.second.count); + } + srsran::console(" +------------+------------+\n\n"); + + srsran::console(" GNB detected:\n"); + srsran::console(" +------------+------------+------------+\n"); + srsran::console(" | %10s | %10s | %10s |\n", "preamble", "count", "avg TA"); + srsran::console(" +------------+------------+------------+\n"); + + for (const auto& p : metrics.gnb_stack.prach) { + srsran::console(" | %10d | %10d | %10.1f |\n", p.first, p.second.count, p.second.avg_ta); + + // Ensure all detected preambles were transmitted + TESTASSERT(metrics.ue_stack.prach.count(p.first) > 0); + } + srsran::console(" +------------+------------+------------+\n\n"); + } else { + // In this case no PRACH should + TESTASSERT(metrics.gnb_stack.prach.empty()); + } // Assert metrics - TESTASSERT(mac_metrics.tx_errors == 0); - TESTASSERT(mac_metrics.rx_errors == 0); + TESTASSERT(metrics.gnb_stack.mac.tx_errors == 0); + TESTASSERT(metrics.gnb_stack.mac.rx_errors == 0); // If reached here, the test is successful return SRSRAN_SUCCESS; diff --git a/test/phy/test_bench.h b/test/phy/test_bench.h index 4855738e3..486048796 100644 --- a/test/phy/test_bench.h +++ b/test/phy/test_bench.h @@ -31,9 +31,13 @@ class test_bench private: const std::string UE_PHY_COM_LOG_NAME = "UE /PHY/COM"; const std::string GNB_PHY_COM_LOG_NAME = "GNB/PHY/COM"; - uint32_t tti = 0; + uint32_t slot_idx = 0; + uint64_t slot_count = 0; + uint64_t duration_slots = 0; + gnb_dummy_stack gnb_stack; srsenb::nr::worker_pool gnb_phy; phy_common gnb_phy_com; + ue_dummy_stack ue_stack; srsue::nr::worker_pool ue_phy; phy_common ue_phy_com; bool initialised = false; @@ -47,32 +51,53 @@ public: bool valid = false; srsran::phy_cfg_nr_t phy_cfg = {}; srsenb::phy_cell_cfg_list_nr_t cell_list = {}; - srsenb::nr::worker_pool::args_t gnb_args; - srsue::phy_args_nr_t ue_args; - uint16_t rnti = 0x1234; + srsenb::nr::worker_pool::args_t gnb_phy; + gnb_dummy_stack::args_t gnb_stack; + srsue::phy_args_nr_t ue_phy; + ue_dummy_stack::args_t ue_stack; std::string phy_com_log_level = "info"; + std::string phy_lib_log_level = "none"; + uint64_t durations_slots = 100; args_t(int argc, char** argv); }; - test_bench(const args_t& args, srsenb::stack_interface_phy_nr& gnb_stack, srsue::stack_interface_phy_nr& ue_stack) : - ue_phy(args.ue_args.nof_phy_threads), - gnb_phy(gnb_phy_com, gnb_stack, srslog::get_default_sink(), args.gnb_args.nof_phy_threads), + struct metrics_t { + gnb_dummy_stack::metrics_t gnb_stack = {}; + ue_dummy_stack::metrics_t ue_stack = {}; + }; + + test_bench(const args_t& args) : + gnb_stack(args.gnb_stack), + gnb_phy(gnb_phy_com, gnb_stack, srslog::get_default_sink(), args.gnb_phy.nof_phy_threads), + ue_stack(args.ue_stack, ue_phy), + ue_phy(args.ue_phy.nof_phy_threads), + ue_phy_com(phy_common::args_t(args.srate_hz, args.buffer_sz_ms, args.nof_channels), srslog::fetch_basic_logger(UE_PHY_COM_LOG_NAME, srslog::get_default_sink(), false)), gnb_phy_com(phy_common::args_t(args.srate_hz, args.buffer_sz_ms, args.nof_channels), srslog::fetch_basic_logger(GNB_PHY_COM_LOG_NAME, srslog::get_default_sink(), false)), - sf_sz((uint32_t)std::round(args.srate_hz * 1e-3)) + sf_sz((uint32_t)std::round(args.srate_hz * 1e-3)), + duration_slots(args.durations_slots) { srslog::fetch_basic_logger(UE_PHY_COM_LOG_NAME).set_level(srslog::str_to_basic_level(args.phy_com_log_level)); srslog::fetch_basic_logger(GNB_PHY_COM_LOG_NAME).set_level(srslog::str_to_basic_level(args.phy_com_log_level)); - if (not gnb_phy.init(args.gnb_args, args.cell_list)) { + if (not gnb_phy.init(args.gnb_phy, args.cell_list)) { + return; + } + + srsenb::phy_interface_rrc_nr::common_cfg_t common_cfg = {}; + common_cfg.carrier = args.phy_cfg.carrier; + common_cfg.pdcch = args.phy_cfg.pdcch; + common_cfg.prach = args.phy_cfg.prach; + + if (gnb_phy.set_common_cfg(common_cfg) < SRSRAN_SUCCESS) { return; } // Initialise UE PHY - if (not ue_phy.init(args.ue_args, ue_phy_com, &ue_stack, 31)) { + if (not ue_phy.init(args.ue_phy, ue_phy_com, &ue_stack, 31)) { return; } @@ -81,6 +106,16 @@ public: return; } + // Make sure PHY log is not set by UE or gNb PHY + handler_registered = 0; + if (args.phy_lib_log_level == "info") { + srsran_verbose = SRSRAN_VERBOSE_INFO; + } else if (args.phy_lib_log_level == "debug") { + srsran_verbose = SRSRAN_VERBOSE_DEBUG; + } else { + srsran_verbose = SRSRAN_VERBOSE_NONE; + } + initialised = true; } @@ -94,12 +129,12 @@ public: ~test_bench() = default; - bool is_initialised() const { return initialised; } + bool is_initialised() const { return ue_stack.is_valid() and gnb_stack.is_valid() and initialised; } bool run_tti() { // Get gNb worker - srsenb::nr::slot_worker* gnb_worker = gnb_phy.wait_worker(tti); + srsenb::nr::slot_worker* gnb_worker = gnb_phy.wait_worker(slot_idx); if (gnb_worker == nullptr) { return false; } @@ -112,14 +147,14 @@ public: // Set gNb time gnb_time.add(TX_ENB_DELAY * 1e-3); - gnb_worker->set_time(tti, gnb_time); + gnb_worker->set_time(slot_idx, gnb_time); // Start gNb work gnb_phy_com.push_semaphore(gnb_worker); gnb_phy.start_worker(gnb_worker); // Get UE worker - srsue::nr::sf_worker* ue_worker = ue_phy.wait_worker(tti); + srsue::nr::sf_worker* ue_worker = ue_phy.wait_worker(slot_idx); if (ue_worker == nullptr) { return false; } @@ -132,15 +167,27 @@ public: // Set UE time ue_time.add(TX_ENB_DELAY * 1e-3); - ue_worker->set_tti(tti); + ue_worker->set_tti(slot_idx); ue_worker->set_tx_time(ue_time); - // Start gNb work + // Run UE stack + ue_stack.run_tti(slot_idx); + + // Start UE work ue_phy_com.push_semaphore(ue_worker); ue_phy.start_worker(ue_worker); - tti++; - return true; + slot_count++; + slot_idx = slot_count % (1024 * SRSRAN_NSLOTS_PER_FRAME_NR(srsran_subcarrier_spacing_15kHz)); + return slot_count <= duration_slots; + } + + metrics_t get_gnb_metrics() + { + metrics_t metrics = {}; + metrics.gnb_stack = gnb_stack.get_metrics(); + metrics.ue_stack = ue_stack.get_metrics(); + return metrics; } };