diff --git a/CMakeLists.txt b/CMakeLists.txt index bae1c590bc..015e66e46d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,38 @@ project (OpenAirInterface LANGUAGES C CXX) set(CMAKE_CXX_STANDARD 17) set(OAI_VERSION 2.4.0) +option(CUDA_ENABLE "Enable CUDA accelerated channel simulation" OFF) +if(CUDA_ENABLE) + find_package(CUDA REQUIRED) + find_package(CUDAToolkit REQUIRED) + message(STATUS "CUDA explicitly enabled, building with GPU acceleration support.") + + enable_language(CUDA) + + if(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64") + set(CMAKE_CUDA_ARCHITECTURES 90) + endif() + + option(USE_UNIFIED_MEMORY "Enable CUDA Unified Memory path" OFF) + option(USE_ATS_MEMORY "Enable ATS Hybrid Memory path" ON) + + if(USE_UNIFIED_MEMORY AND USE_ATS_MEMORY) + message(FATAL_ERROR "Cannot enable both USE_UNIFIED_MEMORY and USE_ATS_MEMORY at the same time.") + endif() + + if(USE_UNIFIED_MEMORY) + add_compile_definitions(USE_UNIFIED_MEMORY) + message(STATUS "CUDA Unified Memory path enabled.") + elseif(USE_ATS_MEMORY) + add_compile_definitions(USE_ATS_MEMORY) + message(STATUS "CUDA ATS Hybrid Memory path enabled.") + else() + message(STATUS "CUDA Explicit Copy path enabled (default ATS was overridden).") + endif() + + add_compile_definitions(ENABLE_CUDA) +endif() + ######################################################### # Base directories, compatible with legacy OAI building # ######################################################### @@ -166,6 +198,8 @@ elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64") set(C_FLAGS_PROCESSOR "${C_FLAGS_PROCESSOR} -gdwarf-2 -mcpu=neoverse-n1 -lgcc -lrt") elseif (CPUPART MATCHES "0xd49") # Neoverse-N2 set(C_FLAGS_PROCESSOR "${C_FLAGS_PROCESSOR} -gdwarf-2 -mcpu=neoverse-n2 -ftree-vectorize -lgcc -lrt") + elseif (CPUPART MATCHES "0xd03") # Cortex-A53 + set(C_FLAGS_PROCESSOR "${C_FLAGS_PROCESSOR} -gdwarf-2 -mcpu=cortex-a53 -lgcc -lrt") else () set(C_FLAGS_PROCESSOR "${C_FLAGS_PROCESSOR} -gdwarf-2 -mcpu=native -lgcc -lrt") endif() @@ -174,7 +208,12 @@ else() endif() if (NOT CROSS_COMPILE) - set(C_FLAGS_PROCESSOR "${C_FLAGS_PROCESSOR} -march=native") + if (CPUPART MATCHES "0xd03") + # Cortex-A53 does not support ARMv8.2-A extensions + set(C_FLAGS_PROCESSOR "${C_FLAGS_PROCESSOR} -march=armv8-a+simd") + else() + set(C_FLAGS_PROCESSOR "${C_FLAGS_PROCESSOR} -march=native") + endif() else () set(C_FLAGS_PROCESSOR "${C_FLAGS_PROCESSOR} -march=armv8.2-a") endif() @@ -282,6 +321,22 @@ if(GIT_FOUND) ) endif() +option(USE_UNIFIED_MEMORY "Enable CUDA Unified Memory path instead of explicit copies" OFF) + +option(USE_ATS_MEMORY "Enable Hybrid ATS (CPU->GPU) and Explicit Copy (GPU->CPU) path" OFF) + +if(USE_UNIFIED_MEMORY AND USE_ATS_MEMORY) + message(FATAL_ERROR "Cannot enable both USE_UNIFIED_MEMORY and USE_ATS_MEMORY at the same time.") +endif() + +if(USE_UNIFIED_MEMORY) + add_compile_definitions(USE_UNIFIED_MEMORY) + message(STATUS "CUDA Unified Memory path enabled.") +elseif(USE_ATS_MEMORY) + add_compile_definitions(USE_ATS_MEMORY) + message(STATUS "CUDA ATS Hybrid Memory path enabled.") +endif() + # Debug related options ######################################### # asn1c skeletons have hardcoded this flag to make customized debug logs @@ -297,7 +352,7 @@ add_boolean_option(TRACY_ENABLE OFF "Enable tracy instrumentation" ON) if (TRACY_ENABLE) # the tracy version here should match the tracy server version # below is latest release as of this commit - CPMAddPackage("gh:wolfpld/tracy#0.12.2") + CPMAddPackage("gh:wolfpld/tracy#v0.13.1") endif() set (OCP_ITTI ${OPENAIR_DIR}/common/utils/ocp_itti) @@ -342,7 +397,7 @@ target_link_libraries(lte_rrc PRIVATE nr_rrc) #NR RRC ####### add_library(nr_rrc ${OPENAIR2_DIR}/RRC/NR/MESSAGES/asn1_msg.c) -target_link_libraries(nr_rrc PUBLIC asn1_nr_rrc asn1_lte_rrc) +target_link_libraries(nr_rrc PUBLIC asn1_nr_rrc asn1_lte_rrc rrc_cell_management) # S1AP ############## @@ -477,7 +532,7 @@ add_library(f1ap ${F1AP_DIR}/f1ap_itti_messaging.c) target_include_directories(f1ap PUBLIC F1AP_DIR) target_link_libraries(f1ap PUBLIC asn1_f1ap GTPV1U) -target_link_libraries(f1ap PRIVATE ngap nr_rrc HASHTABLE f1ap_lib) +target_link_libraries(f1ap PRIVATE ngap nr_rrc ds f1ap_lib) target_include_directories(f1ap PRIVATE ${F1AP_DIR}/lib) if(E2_AGENT) target_compile_definitions(f1ap PRIVATE E2_AGENT) @@ -515,8 +570,6 @@ target_link_libraries(params_libconfig PRIVATE config_internals ${libconfig_LIBR add_library(shlib_loader OBJECT common/utils/load_module_shlib.c) target_link_libraries(shlib_loader PRIVATE CONFIG_LIB) -include_directories ("${OPENAIR_DIR}/radio/COMMON") - ############################################################## # ???!!! TO BE DOCUMENTED OPTIONS !!!??? ############################################################## @@ -617,12 +670,6 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/common/utils/T # set the T_LIB name to T if the target exists, otherwise leave empty set(T_LIB $) -add_library(HASHTABLE - ${OPENAIR_DIR}/common/utils/hashtable/hashtable.c - ${OPENAIR_DIR}/common/utils/hashtable/obj_hashtable.c -) -include_directories(${OPENAIR_DIR}/common/utils/hashtable) - add_library(UTIL ${OPENAIR_DIR}/common/utils/LOG/vcd_signal_dumper.c ${OPENAIR2_DIR}/UTIL/OPT/probe.c @@ -780,24 +827,6 @@ include_directories(${NFAPI_USER_DIR}) # Layer 1 ############################# -set(PHY_POLARSRC - ${OPENAIR1_DIR}/PHY/CODING/nrPolar_tools/nr_polar_init.c - ${OPENAIR1_DIR}/PHY/CODING/nrPolar_tools/nr_bitwise_operations.c - ${OPENAIR1_DIR}/PHY/CODING/nrPolar_tools/nr_crc_byte.c - ${OPENAIR1_DIR}/PHY/CODING/nrPolar_tools/nr_polar_decoder.c - ${OPENAIR1_DIR}/PHY/CODING/nrPolar_tools/nr_polar_decoding_tools.c - ${OPENAIR1_DIR}/PHY/CODING/nrPolar_tools/nr_polar_encoder.c - ${OPENAIR1_DIR}/PHY/CODING/nrPolar_tools/nr_polar_interleaving_pattern.c - ${OPENAIR1_DIR}/PHY/CODING/nrPolar_tools/nr_polar_kernal_operation.c - ${OPENAIR1_DIR}/PHY/CODING/nrPolar_tools/nr_polar_kronecker_power_matrices.c - ${OPENAIR1_DIR}/PHY/CODING/nrPolar_tools/nr_polar_matrix_and_array.c - ${OPENAIR1_DIR}/PHY/CODING/nrPolar_tools/nr_polar_procedures.c - ${OPENAIR1_DIR}/PHY/CODING/nrPolar_tools/nr_polar_sequence_pattern.c -) -set(PHY_SMALLBLOCKSRC - ${OPENAIR1_DIR}/PHY/CODING/nrSmallBlock/encodeSmallBlock.c - ${OPENAIR1_DIR}/PHY/CODING/nrSmallBlock/decodeSmallBlock.c -) set(PHY_TURBOIF ${OPENAIR1_DIR}/PHY/CODING/coding_load.c ) @@ -808,8 +837,6 @@ set(PHY_NRLDPC_CODINGIF add_library(dfts MODULE ${OPENAIR1_DIR}/PHY/TOOLS/oai_dfts.c ${OPENAIR1_DIR}/PHY/TOOLS/oai_dfts_neon.c) -add_library(crc_byte OBJECT ${OPENAIR1_DIR}/PHY/CODING/crc_byte.c) - set(PHY_SRC_COMMON ${OPENAIR1_DIR}/PHY/LTE_TRANSPORT/dci_tools_common.c ${OPENAIR1_DIR}/PHY/LTE_TRANSPORT/lte_mcs.c @@ -978,8 +1005,6 @@ set(PHY_SRC_UE ${OPENAIR1_DIR}/PHY/TOOLS/dB_routines.c ${OPENAIR1_DIR}/PHY/TOOLS/sqrt.c ${OPENAIR1_DIR}/PHY/TOOLS/oai_arith_operations.c - ${PHY_POLARSRC} - ${PHY_SMALLBLOCKSRC} ${PHY_NRLDPC_CODINGIF} ${OPENAIR1_DIR}/PHY/NR_TRANSPORT/pucch_rx.c ${OPENAIR1_DIR}/PHY/NR_TRANSPORT/srs_rx.c @@ -1029,8 +1054,6 @@ set(PHY_SRC_UE ${OPENAIR1_DIR}/PHY/TOOLS/sqrt.c ${OPENAIR1_DIR}/PHY/TOOLS/oai_arith_operations.c ${OPENAIR1_DIR}/PHY/INIT/nr_init_ue.c - ${PHY_POLARSRC} - ${PHY_SMALLBLOCKSRC} ${PHY_NRLDPC_CODINGIF} ) @@ -1064,14 +1087,14 @@ add_library(PHY_NR_COMMON ${PHY_NR_SRC_COMMON}) target_link_libraries(PHY_NR_COMMON PUBLIC UTIL) add_library(PHY_NR ${PHY_NR_SRC}) -target_link_libraries(PHY_NR nr_phy_common nr_common nr_fapi_p5) +target_link_libraries(PHY_NR nr_phy_common nr_common nr_fapi_p5 polar smallblock) add_library(PHY_NR_NO_AVX_256 ${PHY_NR_SRC}) target_link_libraries(PHY_NR_NO_AVX_256 nr_phy_common nr_common) target_compile_definitions(PHY_NR_NO_AVX_256 PUBLIC USE_128BIT) add_library(PHY_NR_UE ${PHY_NR_UE_SRC}) -target_link_libraries(PHY_NR_UE PRIVATE asn1_nr_rrc_hdrs nr_phy_common nr_common UTIL) +target_link_libraries(PHY_NR_UE PRIVATE asn1_nr_rrc_hdrs nr_phy_common nr_common UTIL polar smallblock) add_library(PHY_RU ${PHY_SRC_RU}) target_link_libraries(PHY_RU PRIVATE asn1_lte_rrc_hdrs UTIL) @@ -1201,6 +1224,7 @@ set(NR_L2_SRC_UE ${NR_UE_RRC_DIR}/L2_interface_ue.c ${NR_UE_RRC_DIR}/main_ue.c ${NR_UE_RRC_DIR}/rrc_UE.c + ${NR_UE_RRC_DIR}/verify_RRC.c ${NR_UE_RRC_DIR}/rrc_timers_and_constants.c ${NR_UE_RRC_DIR}/rrc_sl_preconfig.c ) @@ -1687,6 +1711,17 @@ set (SIMUSRC add_library(SIMU STATIC ${SIMUSRC} ) target_include_directories(SIMU PUBLIC ${OPENAIR1_DIR}/SIMULATION/TOOLS ${OPENAIR1_DIR}/SIMULATION/RF) +option(CHANNEL_SSE "Enable SSE optimizations for channel simulation" OFF) +if(CHANNEL_SSE) + message(STATUS "SSE-optimized channel simulation enabled.") + target_compile_definitions(SIMU PRIVATE CHANNEL_SSE) +endif() + +if(CUDA_FOUND) + set_property(TARGET SIMU PROPERTY CUDA_ARCHITECTURES ${CMAKE_CUDA_ARCHITECTURES}) + set_property(TARGET SIMU PROPERTY LINKER_LANGUAGE CXX) +endif() + include_directories("${NFAPI_DIR}/nfapi/public_inc") include_directories("${NFAPI_DIR}/common/public_inc") include_directories("${NFAPI_DIR}/pnf/public_inc") @@ -1717,12 +1752,9 @@ add_executable(lte-softmodem ${OPENAIR_DIR}/executables/lte-ru.c ${OPENAIR_DIR}/executables/ru_control.c ${OPENAIR_DIR}/executables/lte-softmodem.c - ${OPENAIR_DIR}/executables/softmodem-common.c ${OPENAIR2_DIR}/ENB_APP/NB_IoT_interface.c ${OPENAIR_DIR}/executables/create_tasks.c ${OPENAIR_DIR}/executables/create_tasks_mbms.c - ${OPENAIR_DIR}/radio/COMMON/common_lib.c - ${OPENAIR_DIR}/radio/COMMON/record_player.c ${OPENAIR3_DIR}/NAS/UE/nas_ue_task.c ${PHY_INTERFACE_DIR}/queue_t.c ${OPENAIR_DIR}/common/utils/lte/ue_power.c @@ -1733,9 +1765,9 @@ add_dependencies(lte-softmodem oai_iqplayer) target_link_libraries(lte-softmodem PRIVATE -Wl,--start-group - lte_rrc nr_rrc s1ap m2ap x2ap m3ap GTPV1U SECURITY UTIL HASHTABLE SCTP_CLIENT MME_APP SCHED_LIB SCHED_RU_LIB + lte_rrc nr_rrc s1ap m2ap x2ap m3ap GTPV1U SECURITY UTIL SCTP_CLIENT MME_APP SCHED_LIB SCHED_RU_LIB PHY_COMMON PHY PHY_RU L2 L2_LTE NFAPI_LIB NFAPI_VNF_LIB NFAPI_PNF_LIB NFAPI_USER_LIB MISC_NFAPI_LTE_LIB - ${NAS_UE_LIB} ITTI SIMU shlib_loader + ${NAS_UE_LIB} ITTI SIMU radio_common softmodem_common -Wl,--end-group z dl) target_link_libraries(lte-softmodem PRIVATE pthread m CONFIG_LIB rt) @@ -1751,16 +1783,13 @@ endif() add_executable(oairu ${OPENAIR_DIR}/executables/lte-ru.c ${OPENAIR_DIR}/executables/ru_control.c - ${OPENAIR_DIR}/radio/COMMON/common_lib.c - ${OPENAIR_DIR}/radio/COMMON/record_player.c - ${OPENAIR_DIR}/executables/softmodem-common.c ${OPENAIR_DIR}/openair1/SCHED/phy_procedures_lte_common.c ${OPENAIR_DIR}/executables/main_ru.c ${OPENAIR_DIR}/common/utils/lte/prach_utils.c ) target_link_libraries(oairu PRIVATE -Wl,--start-group - SCHED_RU_LIB PHY_COMMON PHY_RU UTIL shlib_loader + SCHED_RU_LIB PHY_COMMON PHY_RU UTIL radio_common softmodem_common -Wl,--end-group z dl) target_link_libraries(oairu PRIVATE pthread m CONFIG_LIB rt ${T_LIB}) @@ -1777,10 +1806,7 @@ target_link_libraries(lte-softmodem PRIVATE add_executable(lte-uesoftmodem ${OPENAIR_DIR}/executables/lte-ue.c ${OPENAIR_DIR}/executables/lte-uesoftmodem.c - ${OPENAIR_DIR}/executables/softmodem-common.c ${OPENAIR_DIR}/executables/create_tasks_ue.c - ${OPENAIR_DIR}/radio/COMMON/common_lib.c - ${OPENAIR_DIR}/radio/COMMON/record_player.c ${OPENAIR3_DIR}/NAS/UE/nas_ue_task.c ${OPENAIR_DIR}/common/utils/lte/ue_power.c ${OPENAIR_DIR}/common/utils/lte/prach_utils.c @@ -1790,9 +1816,9 @@ add_executable(lte-uesoftmodem target_link_libraries(lte-uesoftmodem PRIVATE -Wl,--start-group lte_rrc nr_rrc s1ap x2ap m2ap m3ap - SECURITY UTIL HASHTABLE SCTP_CLIENT MME_APP SCHED_RU_LIB SCHED_UE_LIB PHY_COMMON + SECURITY UTIL SCTP_CLIENT MME_APP SCHED_RU_LIB SCHED_UE_LIB PHY_COMMON PHY_UE PHY_RU L2_UE L2_LTE SIMU NFAPI_LIB NFAPI_PNF_LIB NFAPI_USER_LIB MISC_NFAPI_LTE_LIB - ${NAS_UE_LIB} ITTI shlib_loader + ${NAS_UE_LIB} ITTI radio_common softmodem_common -Wl,--end-group z dl) target_link_libraries(lte-uesoftmodem PRIVATE pthread m CONFIG_LIB rt) @@ -1811,13 +1837,11 @@ add_executable(nr-oru ${OPENAIR_DIR}/openair1/SCHED_NR/phy_frame_config_nr.c ${OPENAIR_DIR}/openair1/SCHED_NR/nr_prach_procedures.c ${OPENAIR_DIR}/openair1/SCHED/phy_procedures_lte_common.c - ${OPENAIR_DIR}/radio/COMMON/common_lib.c - ${OPENAIR_DIR}/radio/COMMON/record_player.c - ${OPENAIR_DIR}/executables/softmodem-common.c ${OPENAIR_DIR}/executables/main_nr_ru.c ) target_link_libraries(nr-oru PRIVATE - UTIL SCHED_RU_LIB PHY_COMMON PHY_RU PHY_NR shlib_loader z dl) + UTIL SCHED_RU_LIB PHY_COMMON PHY_RU PHY_NR shlib_loader z dl + radio_common softmodem_common) target_link_libraries(nr-oru PRIVATE pthread m CONFIG_LIB rt ${T_LIB} utils barrier actor) target_link_libraries(nr-oru PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs nr_phy_common time_management) @@ -1832,9 +1856,6 @@ add_executable(nr-softmodem ${OPENAIR_DIR}/executables/nr-gnb.c ${OPENAIR_DIR}/executables/nr-ru.c ${OPENAIR_DIR}/executables/nr-softmodem.c - ${OPENAIR_DIR}/executables/softmodem-common.c - ${OPENAIR_DIR}/radio/COMMON/common_lib.c - ${OPENAIR_DIR}/radio/COMMON/record_player.c ${OPENAIR_DIR}/common/utils/lte/ue_power.c ${OPENAIR_DIR}/common/utils/lte/prach_utils.c ${PHY_INTERFACE_DIR}/queue_t.c @@ -1843,11 +1864,11 @@ add_executable(nr-softmodem target_link_libraries(nr-softmodem PRIVATE -Wl,--start-group - UTIL HASHTABLE SCTP_CLIENT SCHED_LIB SCHED_RU_LIB SCHED_NR_LIB PHY_NR PHY PHY_COMMON PHY_NR_COMMON PHY_RU GTPV1U SECURITY + UTIL SCTP_CLIENT SCHED_LIB SCHED_RU_LIB SCHED_NR_LIB PHY_NR PHY PHY_COMMON PHY_NR_COMMON PHY_RU GTPV1U SECURITY ITTI ${NAS_UE_LIB} lte_rrc nr_rrc ngap s1ap L2_LTE_NR L2_NR MAC_NR_COMMON NFAPI_LIB NFAPI_VNF_LIB NFAPI_PNF_LIB NFAPI_USER_LIB SIMU - x2ap f1ap m2ap m3ap e1ap shlib_loader - time_management + x2ap f1ap m2ap m3ap e1ap radio_common + time_management softmodem_common -Wl,--end-group z dl) target_link_libraries(nr-softmodem PRIVATE pthread m CONFIG_LIB rt) @@ -1877,7 +1898,6 @@ target_link_libraries(nr-softmodem PRIVATE add_executable(nr-cuup executables/nr-cuup.c - executables/softmodem-common.c ${NR_RRC_DIR}/rrc_gNB_UE_context.c ${OPENAIR2_DIR}/E1AP/e1ap_setup.c ${NR_PDCP_SRC} @@ -1887,9 +1907,9 @@ add_executable(nr-cuup target_link_libraries(nr-cuup PRIVATE CONFIG_LIB ITTI SCTP_CLIENT GTPV1U e1ap f1ap - time_management + time_management softmodem_common alg - z dl pthread shlib_loader ${T_LIB}) + z dl pthread ${T_LIB}) target_link_libraries(nr-cuup PRIVATE asn1_lte_rrc_hdrs asn1_nr_rrc_hdrs) if(E2_AGENT) target_link_libraries(nr-cuup PRIVATE e2_agent e2_agent_arg e2_ran_func_cuup) @@ -1906,11 +1926,6 @@ add_executable(nr-uesoftmodem ${OPENAIR_DIR}/executables/position_interface.c ${OPENAIR_DIR}/executables/nr-ue.c ${OPENAIR_DIR}/executables/nr-ue-ru.c - ${OPENAIR_DIR}/executables/softmodem-common.c - ${OPENAIR_DIR}/radio/COMMON/common_lib.c - ${OPENAIR_DIR}/radio/COMMON/record_player.c - ${OPENAIR2_DIR}/LAYER2/NR_MAC_COMMON/nr_mac_common.c - ${OPENAIR2_DIR}/LAYER2/NR_MAC_COMMON/nr_mac_common_tdd.c ${OPENAIR1_DIR}/PHY/TOOLS/phy_scope_interface.c ${NFAPI_USER_DIR}/nfapi.c ${PHY_INTERFACE_DIR}/queue_t.c @@ -1918,10 +1933,10 @@ add_executable(nr-uesoftmodem target_link_libraries(nr-uesoftmodem PRIVATE -Wl,--start-group - nr_rrc SECURITY UTIL HASHTABLE SCHED_RU_LIB SCHED_NR_UE_LIB + nr_rrc SECURITY UTIL SCHED_RU_LIB SCHED_NR_UE_LIB PHY_COMMON PHY_NR_COMMON PHY_NR_UE NR_L2_UE MAC_NR_COMMON NFAPI_LIB - ITTI SIMU shlib_loader - time_management + ITTI SIMU radio_common + time_management softmodem_common -Wl,--end-group z dl) target_link_libraries(nr-uesoftmodem PRIVATE pthread m CONFIG_LIB rt nr_ue_phy_meas) @@ -1954,10 +1969,10 @@ target_link_libraries(nr-uesoftmodem PRIVATE add_executable(rftest ${OPENAIR_DIR}/openair1/PHY/TOOLS/calibration_test.c ${OPENAIR_DIR}/openair1/PHY/TOOLS/calibration_scope.c - ${OPENAIR_DIR}/radio/COMMON/common_lib.c - ${OPENAIR_DIR}/executables/softmodem-common.c ) -target_link_libraries(rftest PRIVATE minimal_lib PHY_NR_COMMON UTIL forms pthread dl m ${T_LIB} shlib_loader) +target_link_libraries(rftest PRIVATE + minimal_lib PHY_NR_COMMON UTIL forms pthread dl m ${T_LIB} + radio_common softmodem_common) target_link_libraries(rftest PRIVATE asn1_lte_rrc_hdrs asn1_nr_rrc_hdrs) add_executable(polartest @@ -1974,7 +1989,7 @@ add_executable(smallblocktest target_link_libraries(smallblocktest PRIVATE -Wl,--start-group UTIL SIMU PHY_NR PHY_COMMON PHY_NR_COMMON -Wl,--end-group - m pthread dl shlib_loader ${T_LIB} + m pthread dl ${T_LIB} ) @@ -1984,7 +1999,7 @@ add_executable(ldpctest ) target_link_libraries(ldpctest PRIVATE -Wl,--start-group UTIL SIMU PHY_COMMON PHY_NR_COMMON -Wl,--end-group - m pthread dl shlib_loader ${T_LIB} + m pthread dl ${T_LIB} # link 'check_crc' to make it resolved in the LDPC coding libraries # 'check_crc' is not used in ldpctest so it is not linked in the executable by default # --whole-archive links 'check_crc' in the executable even though it is note used, see 'man ld' @@ -1997,54 +2012,53 @@ target_link_libraries(physim_common PRIVATE UTIL) add_executable(nr_dlschsim ${OPENAIR1_DIR}/SIMULATION/NR_PHY/dlschsim.c) target_link_libraries(nr_dlschsim PRIVATE -Wl,--start-group UTIL SIMU PHY_COMMON PHY_NR_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB CONFIG_LIB MAC_NR_COMMON -Wl,--end-group - m pthread ${T_LIB} ITTI dl shlib_loader physim_common + m pthread ${T_LIB} ITTI dl physim_common ) target_link_libraries(nr_dlschsim PRIVATE asn1_nr_rrc_hdrs) add_executable(nr_pbchsim ${OPENAIR1_DIR}/SIMULATION/NR_PHY/pbchsim.c) target_link_libraries(nr_pbchsim PRIVATE -Wl,--start-group UTIL SIMU PHY_COMMON PHY_NR_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB CONFIG_LIB MAC_NR_COMMON -Wl,--end-group - m pthread ${T_LIB} ITTI dl shlib_loader physim_common + m pthread ${T_LIB} ITTI dl physim_common ) target_link_libraries(nr_pbchsim PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs) add_executable(nr_psbchsim ${OPENAIR1_DIR}/SIMULATION/NR_PHY/psbchsim.c - ${OPENAIR_DIR}/executables/softmodem-common.c ${NFAPI_USER_DIR}/nfapi.c ${NFAPI_USER_DIR}/gnb_ind_vars.c ${PHY_INTERFACE_DIR}/queue_t.c ) target_link_libraries(nr_psbchsim PRIVATE - -Wl,--start-group UTIL SIMU PHY_COMMON PHY_NR_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB SCHED_NR_UE_LIB MAC_UE_NR MAC_NR_COMMON CONFIG_LIB L2_NR -lz -Wl,--end-group - m pthread ${T_LIB} ITTI dl shlib_loader physim_common + -Wl,--start-group UTIL SIMU PHY_COMMON PHY_NR_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB SCHED_NR_UE_LIB MAC_UE_NR MAC_NR_COMMON CONFIG_LIB L2_NR -Wl,--end-group + m pthread ${T_LIB} ITTI dl physim_common softmodem_common ) target_link_libraries(nr_psbchsim PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs) add_executable(nr_pucchsim ${OPENAIR1_DIR}/SIMULATION/NR_PHY/pucchsim.c) target_link_libraries(nr_pucchsim PRIVATE -Wl,--start-group UTIL SIMU PHY_COMMON PHY_NR_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB CONFIG_LIB MAC_NR_COMMON -Wl,--end-group - m pthread ${T_LIB} ITTI dl shlib_loader physim_common + m pthread ${T_LIB} ITTI dl physim_common ) target_link_libraries(nr_pucchsim PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs) add_executable(nr_dlsim ${OPENAIR1_DIR}/SIMULATION/NR_PHY/dlsim.c - ${OPENAIR_DIR}/executables/softmodem-common.c ${NFAPI_USER_DIR}/nfapi.c ${NFAPI_USER_DIR}/gnb_ind_vars.c ${PHY_INTERFACE_DIR}/queue_t.c ) target_link_libraries(nr_dlsim PRIVATE - -Wl,--start-group UTIL SIMU PHY_COMMON PHY_NR_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB SCHED_NR_UE_LIB MAC_UE_NR MAC_NR_COMMON nr_rrc CONFIG_LIB L2_NR HASHTABLE x2ap SECURITY ngap -lz -Wl,--end-group - m pthread ${T_LIB} ITTI dl shlib_loader nr_ue_phy_meas physim_common + -Wl,--start-group UTIL SIMU PHY_COMMON PHY_NR_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB SCHED_NR_UE_LIB MAC_UE_NR MAC_NR_COMMON nr_rrc CONFIG_LIB L2_NR x2ap SECURITY ngap -Wl,--end-group + m pthread ${T_LIB} ITTI dl nr_ue_phy_meas physim_common + softmodem_common ) target_link_libraries(nr_dlsim PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs) add_executable(nr_prachsim ${OPENAIR1_DIR}/SIMULATION/NR_PHY/prachsim.c) target_link_libraries(nr_prachsim PRIVATE - -Wl,--start-group UTIL SIMU PHY_COMMON PHY_NR_COMMON PHY_NR PHY_RU PHY_NR_UE MAC_NR_COMMON SCHED_NR_LIB CONFIG_LIB -lz -Wl,--end-group - m pthread ${T_LIB} ITTI dl shlib_loader physim_common) + -Wl,--start-group UTIL SIMU PHY_COMMON PHY_NR_COMMON PHY_NR PHY_RU PHY_NR_UE MAC_NR_COMMON SCHED_NR_LIB CONFIG_LIB -Wl,--end-group + m pthread ${T_LIB} ITTI dl physim_common) target_link_libraries(nr_prachsim PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs) add_executable(nr_ulschsim @@ -2053,41 +2067,48 @@ add_executable(nr_ulschsim ) target_link_libraries(nr_ulschsim PRIVATE -Wl,--start-group UTIL SIMU PHY_COMMON PHY_NR_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB CONFIG_LIB MAC_NR_COMMON -Wl,--end-group - m pthread ${T_LIB} ITTI dl shlib_loader physim_common + m pthread ${T_LIB} ITTI dl physim_common ) target_link_libraries(nr_ulschsim PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs) add_executable(nr_ulsim ${OPENAIR1_DIR}/SIMULATION/NR_PHY/ulsim.c - ${OPENAIR_DIR}/executables/softmodem-common.c ${NFAPI_USER_DIR}/nfapi.c ${NFAPI_USER_DIR}/gnb_ind_vars.c ${PHY_INTERFACE_DIR}/queue_t.c ) target_link_libraries(nr_ulsim PRIVATE - -Wl,--start-group UTIL SIMU PHY_COMMON PHY_NR_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB SCHED_NR_UE_LIB MAC_UE_NR MAC_NR_COMMON nr_rrc CONFIG_LIB L2_NR HASHTABLE x2ap SECURITY ngap -lz -Wl,--end-group - m pthread ${T_LIB} ITTI dl shlib_loader nr_ue_phy_meas physim_common + -Wl,--start-group UTIL SIMU PHY_COMMON PHY_NR_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB SCHED_NR_UE_LIB MAC_UE_NR MAC_NR_COMMON nr_rrc CONFIG_LIB L2_NR x2ap SECURITY ngap -Wl,--end-group + m pthread ${T_LIB} ITTI dl nr_ue_phy_meas physim_common softmodem_common ) target_link_libraries(nr_ulsim PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs) add_executable(nr_srssim ${OPENAIR1_DIR}/SIMULATION/NR_PHY/srssim.c - ${OPENAIR_DIR}/executables/softmodem-common.c ${NFAPI_USER_DIR}/nfapi.c ${NFAPI_USER_DIR}/gnb_ind_vars.c ${PHY_INTERFACE_DIR}/queue_t.c ) target_link_libraries(nr_srssim PRIVATE - -Wl,--start-group UTIL SIMU PHY_COMMON PHY_NR_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB SCHED_NR_UE_LIB MAC_UE_NR MAC_NR_COMMON nr_rrc L2_NR -lz -Wl,--end-group - m pthread ITTI dl nr_ue_phy_meas physim_common + -Wl,--start-group UTIL SIMU PHY_COMMON PHY_NR_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB SCHED_NR_UE_LIB MAC_UE_NR MAC_NR_COMMON -Wl,--end-group + m pthread ITTI dl nr_ue_phy_meas physim_common softmodem_common ) +if(CUDA_ENABLE) + if (TARGET oai_cuda_lib) + target_link_libraries(nr_dlsim PRIVATE oai_cuda_lib) + target_link_libraries(nr_ulsim PRIVATE oai_cuda_lib) + + target_compile_definitions(nr_dlsim PRIVATE ENABLE_CUDA) + target_compile_definitions(nr_ulsim PRIVATE ENABLE_CUDA) + endif() +endif() + # these simulators do not compile: # dlsim_tm7 pbchsim scansim mbmssim pdcchsim pucchsim prachsim syncsim foreach(myExe dlsim ulsim) - add_executable(${myExe} ${OPENAIR1_DIR}/SIMULATION/LTE_PHY/${myExe}.c ${OPENAIR_DIR}/common/utils/lte/ue_power.c @@ -2097,7 +2118,7 @@ foreach(myExe dlsim ulsim) ) target_link_libraries (${myExe} PRIVATE -Wl,--start-group SIMU UTIL SCHED_LIB SCHED_RU_LIB SCHED_UE_LIB PHY_COMMON PHY_NR_COMMON PHY PHY_UE PHY_RU ITTI -Wl,--end-group - pthread m rt CONFIG_LIB ${T_LIB} dl shlib_loader + pthread m rt CONFIG_LIB ${T_LIB} dl ) target_link_libraries(${myExe} PRIVATE asn1_lte_rrc_hdrs) add_dependencies(${myExe} coding) @@ -2136,7 +2157,7 @@ if (${T_TRACER}) #all "add_library" definitions ITTI lte_rrc nr_rrc s1ap x2ap m2ap m3ap f1ap params_libconfig - oai_eth_transpro HASHTABLE UTIL + oai_eth_transpro UTIL SECURITY SCHED_LIB SCHED_NR_LIB SCHED_RU_LIB SCHED_UE_LIB SCHED_NR_UE_LIB NFAPI_LIB NFAPI_PNF_LIB NFAPI_VNF_LIB NFAPI_USER_LIB MISC_NFAPI_LTE_LIB @@ -2189,6 +2210,7 @@ if(ENABLE_TESTS) endif() add_subdirectory(common) +add_subdirectory(executables) add_subdirectory(doc) add_subdirectory(nfapi) add_subdirectory(openair1) @@ -2197,6 +2219,11 @@ add_subdirectory(openair3) add_subdirectory(radio) add_subdirectory(tests) +if(TARGET oai_cuda_lib) + message(STATUS "CUDA library 'oai_cuda_lib' found, linking to targets...") + target_link_libraries(nr_dlsim PRIVATE oai_cuda_lib) + target_link_libraries(nr_ulsim PRIVATE oai_cuda_lib) +endif() if(PACKAGING_LTE OR PACKAGING_NR OR PACKAGING_COMMON OR PACKAGING_USRP OR PACKAGING_PHYSIM) include("tools/packages/packages.cmake") diff --git a/ci-scripts/Jenkinsfile b/ci-scripts/Jenkinsfile index 3112b8f2cf..abb2e3942c 100644 --- a/ci-scripts/Jenkinsfile +++ b/ci-scripts/Jenkinsfile @@ -115,7 +115,6 @@ pipeline { } else { sh "git fetch" sh "git checkout -f ${commitID}" - sh "git reset --hard origin/${commitID}" } } else { echo "\u274C Some parameters are missing" diff --git a/ci-scripts/Jenkinsfile-GitLab-Container b/ci-scripts/Jenkinsfile-GitLab-Container index e395973b25..e94f386e2e 100644 --- a/ci-scripts/Jenkinsfile-GitLab-Container +++ b/ci-scripts/Jenkinsfile-GitLab-Container @@ -334,6 +334,29 @@ pipeline { } } } + stage ("Channel-Simulation") { + when { expression {do5Gtest || do5GUeTest} } + steps { + script { + triggerSlaveJob ('RAN-Channel-Simulation', 'Channel-Simulation') + } + } + post { + always { + script { + // Using a unique variable name for each test stage to avoid overwriting on a global variable + // due to parallel-time concurrency + channelSimStatus = finalizeSlaveJob('RAN-Channel-Simulation') + } + } + failure { + script { + currentBuild.result = 'FAILURE' + failingStages += channelSimStatus + } + } + } + } stage ("PhySim-Cluster-4G") { when { expression {do4Gtest} } steps { @@ -703,11 +726,11 @@ pipeline { } } } - stage ("SA-2x2-Module-CN5G") { + stage ("SA-Multi-Antenna-CN5G") { when { expression {do5Gtest} } steps { script { - triggerSlaveJob ('RAN-SA-2x2-Module-CN5G', 'SA-2x2-Module-CN5G') + triggerSlaveJob ('RAN-SA-Multi-Antenna-CN5G', 'SA-Multi-Antenna-CN5G') } } post { @@ -715,13 +738,13 @@ pipeline { script { // Using a unique variable name for each test stage to avoid overwriting on a global variable // due to parallel-time concurrency - saTDD2x2Status = finalizeSlaveJob('RAN-SA-2x2-Module-CN5G') + saMultiAntennaStatus = finalizeSlaveJob('RAN-SA-Multi-Antenna-CN5G') } } failure { script { currentBuild.result = 'FAILURE' - failingStages += saTDD2x2Status + failingStages += saMultiAntennaStatus } } } diff --git a/ci-scripts/cls_oaicitest.py b/ci-scripts/cls_oaicitest.py index cf37e591c6..237e795937 100644 --- a/ci-scripts/cls_oaicitest.py +++ b/ci-scripts/cls_oaicitest.py @@ -255,7 +255,7 @@ def Deploy_Physim(ctx, HTML, node, workdir, script, options): logging.debug(f'Running physims on server {node} workdir {workdir}') with cls_cmd.getConnection(node) as c: sys_info = c.exec_script("scripts/sys-info.sh", 5) - ret = c.exec_script(script, 1000, options) + ret = c.exec_script(script, 1500, options) logging.debug(f'"{script}" finished with code {ret.returncode}, output:\n{ret.stdout}') HTML.CreateHtmlTestRowQueue('Query system info', 'OK', [sys_info.stdout]) with cls_cmd.getConnection(node) as ssh: diff --git a/ci-scripts/conf_files/gnb.sa.band257.u3.66prb.rfsim.conf b/ci-scripts/conf_files/gnb.sa.band257.u3.66prb.rfsim.conf index 0c1ca120d5..569d887cbb 100644 --- a/ci-scripts/conf_files/gnb.sa.band257.u3.66prb.rfsim.conf +++ b/ci-scripts/conf_files/gnb.sa.band257.u3.66prb.rfsim.conf @@ -175,7 +175,7 @@ MACRLCs = ({ tr_n_preference = "local_RRC"; pusch_TargetSNRx10 = 200; pucch_TargetSNRx10 = 200; - set_analog_beamforming = 2; + set_analog_beamforming = "lophy"; beam_duration = 1; beams_per_period = 1; beam_weights = [0]; // single SSB -> one analog beam diff --git a/ci-scripts/conf_files/gnb.sa.band66.106prb.rfsim.conf b/ci-scripts/conf_files/gnb.sa.band66.106prb.rfsim.conf index bae2bc15ff..fc957d9eef 100644 --- a/ci-scripts/conf_files/gnb.sa.band66.106prb.rfsim.conf +++ b/ci-scripts/conf_files/gnb.sa.band66.106prb.rfsim.conf @@ -18,7 +18,7 @@ gNBs = ////////// Physical parameters: min_rxtxtime = 6; - enable_sdap = 1; + enable_sdap = 0; servingCellConfigCommon = ( { diff --git a/ci-scripts/conf_files/untested/gnb.sa.band66.fr1.106PRB.usrpn300.conf b/ci-scripts/conf_files/untested/gnb.sa.band66.fr1.106PRB.usrpn300.conf index 6715202229..22a65995d5 100644 --- a/ci-scripts/conf_files/untested/gnb.sa.band66.fr1.106PRB.usrpn300.conf +++ b/ci-scripts/conf_files/untested/gnb.sa.band66.fr1.106PRB.usrpn300.conf @@ -18,7 +18,7 @@ gNBs = ////////// Physical parameters: min_rxtxtime = 6; - enable_sdap = 1; + enable_sdap = 0; servingCellConfigCommon = ( { diff --git a/ci-scripts/docker/Dockerfile.channelsim.ubuntu b/ci-scripts/docker/Dockerfile.channelsim.ubuntu new file mode 100644 index 0000000000..9aab73f652 --- /dev/null +++ b/ci-scripts/docker/Dockerfile.channelsim.ubuntu @@ -0,0 +1,52 @@ +#/* +# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more +# * contributor license agreements. See the NOTICE file distributed with +# * this work for additional information regarding copyright ownership. +# * The OpenAirInterface Software Alliance licenses this file to You under +# * the OAI Public License, Version 1.1 (the "License"); you may not use this file +# * except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.openairinterface.org/?page_id=698 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +# *------------------------------------------------------------------------------- +# * For more information about the OpenAirInterface (OAI) Software Alliance: +# * contact@openairinterface.org +# */ +#--------------------------------------------------------------------- +# +# Dockerfile for the Open-Air-Interface BUILD service +# Valid for Ubuntu 24.04 +# +#--------------------------------------------------------------------- +FROM nvidia/cuda:12.9.1-devel-ubuntu22.04 AS cuda-image +FROM ran-base:develop AS ran-tests + +RUN rm -Rf /oai-ran +COPY --from=cuda-image /usr/local/cuda/ /usr/local/cuda/ + +# Set the LD_LIBRARY_PATH to ensure the system can find the copied libraries. +# This is crucial for applications that use CUDA. +ENV LD_LIBRARY_PATH=/usr/local/cuda/lib64:/usr/local/cuda/targets/sbsa-linux/lib +ENV PATH=/usr/local/cuda/bin:$PATH +ENV C_INCLUDE_PATH=/usr/local/cuda/include + +WORKDIR /oai-ran +COPY . . + +WORKDIR /oai-ran/build +RUN cmake -GNinja -DENABLE_PHYSIM_TESTS=ON -DENABLE_TESTS=ON \ + -DSANITIZE_UNDEFINED=OFF -DSANITIZE_ADDRESS=OFF \ + -DCMAKE_C_FLAGS=-Werror -DCMAKE_CXX_FLAGS=-Werror \ + -DPHYSIM_CHECK_FILES="ThresholdsCuda.cmake" \ + -DCUDA_ENABLE=ON \ + -DUSE_UNIFIED_MEMORY=ON \ + -DUSE_ATS_MEMORY=OFF \ + -DCMAKE_RUNTIME_OUTPUT_DIRECTORY=/oai-ran/cmake_targets/ran_build/build \ + .. && \ + ninja test_channel_scalability test_multipath test_noise diff --git a/ci-scripts/main.py b/ci-scripts/main.py index 1995ff545b..d86af05871 100644 --- a/ci-scripts/main.py +++ b/ci-scripts/main.py @@ -202,9 +202,9 @@ def ExecuteActionWithParam(action, ctx, node): workdir = CLUSTER.eNBSourceCodePath success = cls_oaicitest.Deploy_Physim(ctx, HTML, node, workdir, script, options) - elif action == 'Build_Deploy_Docker_PhySim' or action == 'Build_Deploy_Source_PhySim': + elif action == 'Build_Deploy_PhySim': ctest_opt = test.findtext('ctest-opt') or '' - script = "scripts/docker-build-and-deploy-physims.sh" if action == 'Build_Deploy_Docker_PhySim' else 'scripts/source-deploy-physims.sh' + script = test.findtext('script') options = f"{CONTAINERS.eNBSourceCodePath} {ctest_opt}" workdir = CONTAINERS.eNBSourceCodePath success = cls_oaicitest.Deploy_Physim(ctx, HTML, node, workdir, script, options) diff --git a/ci-scripts/scripts/docker-build-and-deploy-chansim.sh b/ci-scripts/scripts/docker-build-and-deploy-chansim.sh new file mode 100755 index 0000000000..991517330c --- /dev/null +++ b/ci-scripts/scripts/docker-build-and-deploy-chansim.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +function die() { echo $@; exit 1; } +[ $# -ge 1 ] || die "usage: $0 [ctest-options]" + +IMAGE=ran-channelsim:ci-temp +CONTAINER=ran-channelsim +function cleanup-docker() { + docker stop ${CONTAINER} + docker rm ${CONTAINER} + docker rmi ${IMAGE} + docker volume prune --force +} +trap cleanup-docker EXIT + +set -x +DIR=$1 +RESULT_DIR=${DIR}/ +shift +CTEST_OPT=$@ + +# build channel simulator +docker build --progress=plain --tag ${IMAGE} --file ${DIR}/ci-scripts/docker/Dockerfile.channelsim.ubuntu ${DIR} &>> ${RESULT_DIR}/channelsim_build_log.txt +if [ $? -ne 0 ]; then + echo "build of channel simulator failed" + exit 1 +fi + +# get a JSON description of all tests to run +docker run -a STDOUT --workdir /oai-ran/build/ --env LD_LIBRARY_PATH=/oai-ran/build/ --rm --name ${CONTAINER} ${IMAGE} ctest ${CTEST_OPT} --show-only=json-v1 &> ${RESULT_DIR}/desc-tests.json +JSON_RES=$? + +# run the actual tests: we don't suppy --rm as we have to copy the files +# similar to unit tests, we can't mount the file where we write physims-5g-run.xml to +# as it would write a file as root, but this script is run as a normal user +docker run -a STDOUT --gpus all --workdir /oai-ran/build/ --env LD_LIBRARY_PATH=/oai-ran/build/ --name ${CONTAINER} ${IMAGE} ctest ${CTEST_OPT} --output-junit results-run.xml --test-output-size-passed 100000 --test-output-size-failed 100000 &>> ${RESULT_DIR}/channelsim_log.txt +RUN_RES=$? +docker cp ${CONTAINER}:/oai-ran/build/results-run.xml ${RESULT_DIR}/ +docker cp ${CONTAINER}:/oai-ran/build/Testing/Temporary/LastTestsFailed.log ${RESULT_DIR}/ +docker cp ${CONTAINER}:/oai-ran/build/Testing/Temporary/LastTest.log ${RESULT_DIR}/ + +# if both were successful, return 0 +# TODO not sure +#[[ $JSON_RES -eq 0 && $RUN_RES -eq 0 ]] && exit 0 + +exit 0 diff --git a/ci-scripts/xml_class_list.yml b/ci-scripts/xml_class_list.yml index a20b78fc99..e8fa42dcd3 100755 --- a/ci-scripts/xml_class_list.yml +++ b/ci-scripts/xml_class_list.yml @@ -24,8 +24,7 @@ - Undeploy_Object - Cppcheck_Analysis - Deploy_Run_OC_PhySim - - Build_Deploy_Docker_PhySim - - Build_Deploy_Source_PhySim + - Build_Deploy_PhySim - LicenceAndFormattingCheck - Push_Local_Registry - Pull_Local_Registry diff --git a/ci-scripts/xml_files/container_5g_vrtsim_cirdb.xml b/ci-scripts/xml_files/container_5g_vrtsim_cirdb.xml new file mode 100644 index 0000000000..84a6a527f7 --- /dev/null +++ b/ci-scripts/xml_files/container_5g_vrtsim_cirdb.xml @@ -0,0 +1,130 @@ + + + vrtsim-5gnr-cirdb + Monolithic gNB vrtsim + CIRDB + wrench + + + Pull_Local_Registry + Pull Images from Local Registry + localhost + oai-gnb oai-nr-ue + arm_ + + + + Create_Workspace + Create new Workspace + localhost + + + + Deploy_Object + Deploy OAI 5G CoreNetwork + localhost + ci-scripts/yaml_files/5g_vrtsim_cirdb + mysql oai-amf oai-smf oai-upf oai-ext-dn + + + + Deploy_Object + Deploy OAI 5G gNB+UE RF sim SA + localhost + ci-scripts/yaml_files/5g_vrtsim_cirdb + oai-gnb oai-nr-ue + + + + Attach_UE + Attach OAI UE (Wait for IP) + rfsim5g_ue + localhost + + + + Ping + Ping ext-dn from NR-UE + rfsim5g_ue + localhost + rfsim5g_ext_dn + localhost + -c 20 -i 0.25 + 5 + + + + Ping + Ping NR-UE from ext-dn + rfsim5g_ext_dn + localhost + rfsim5g_ue + localhost + -c 20 -i 0.25 + 5 + + + + Iperf + Iperf (DL/3Mbps/UDP)(20 sec) + -u -b 3M -t 20 -R + rfsim5g_ue + localhost + rfsim5g_ext_dn + localhost + 5 + 90 + + + + Iperf + Iperf (UL/1Mbps/UDP)(20 sec) + -u -b 1M -t 20 + rfsim5g_ue + localhost + rfsim5g_ext_dn + localhost + 5 + 90 + + + + Undeploy_Object + true + Undeploy all OAI 5G stack + localhost + ci-scripts/yaml_files/5g_vrtsim_cirdb + 10,0,0,0 + 1,0,0,0 + + + + Clean_Test_Server_Images + true + Clean Test Images on Test Server + localhost + oai-gnb oai-nr-ue + + + + diff --git a/ci-scripts/xml_files/physim_gracehopper.xml b/ci-scripts/xml_files/physim_gracehopper.xml index 533696d28c..bebdb3a9c2 100644 --- a/ci-scripts/xml_files/physim_gracehopper.xml +++ b/ci-scripts/xml_files/physim_gracehopper.xml @@ -32,8 +32,9 @@ - Build_Deploy_Docker_PhySim - Build and Deploy PhySim + Build_Deploy_PhySim + Build and Deploy PhySim in Docker + localhost -R 5g -E "nr_dlsim|nr_ulsim|ldpctest" -j32 diff --git a/ci-scripts/xml_files/physim_timed_gracehopper.xml b/ci-scripts/xml_files/physim_timed_gracehopper.xml index 9844f6ebb3..6813c02775 100644 --- a/ci-scripts/xml_files/physim_timed_gracehopper.xml +++ b/ci-scripts/xml_files/physim_timed_gracehopper.xml @@ -32,8 +32,9 @@ - Build_Deploy_Docker_PhySim + Build_Deploy_PhySim Build and Deploy PhySim with timing check + localhost -R 5g -R "nr_dlsim|nr_ulsim|ldpctest" -E physim.5g.nr_dlsim.mcs.mimo.test9 -j1 diff --git a/ci-scripts/xml_files/t2_offload_physim_enc_dec.xml b/ci-scripts/xml_files/t2_offload_physim_enc_dec.xml index afac50ba16..80b933aa4a 100644 --- a/ci-scripts/xml_files/t2_offload_physim_enc_dec.xml +++ b/ci-scripts/xml_files/t2_offload_physim_enc_dec.xml @@ -32,8 +32,9 @@ - Build_Deploy_Source_PhySim + Build_Deploy_PhySim Build and Deploy PhySim T2 Offload tests + caracal -R 5g-offload -j1 diff --git a/ci-scripts/xml_files/test_channel_sim_gracehopper.xml b/ci-scripts/xml_files/test_channel_sim_gracehopper.xml new file mode 100644 index 0000000000..cd6419acec --- /dev/null +++ b/ci-scripts/xml_files/test_channel_sim_gracehopper.xml @@ -0,0 +1,43 @@ + + + channelsim-tab + ChannelSim + wrench + + + Create_Workspace + Create new Workspace + localhost + + + + Build_Deploy_PhySim + Build and Deploy Test Channel Simulation + + localhost + -R cuda.5g.channelsim + + + + diff --git a/ci-scripts/yaml_files/5g_vrtsim_cirdb/docker-compose.yaml b/ci-scripts/yaml_files/5g_vrtsim_cirdb/docker-compose.yaml new file mode 100644 index 0000000000..0d7b509450 --- /dev/null +++ b/ci-scripts/yaml_files/5g_vrtsim_cirdb/docker-compose.yaml @@ -0,0 +1,183 @@ +services: + # CIR Generator service - generates CIRDB files on every CI run + cir-generator: + image: oaisoftwarealliance/cir-generator:test + environment: + GENERATOR_ARGS: "--demo" + OUTPUT: "--out /opt/output/cir_db.bin" + volumes: + - cirdb_data:/opt/output + + mysql: + container_name: "rfsim5g-mysql" + image: mysql:8.0 + init: true + volumes: + - ../5g_rfsimulator/oai_db.sql:/docker-entrypoint-initdb.d/oai_db.sql + - ../5g_rfsimulator/mysql-healthcheck.sh:/tmp/mysql-healthcheck.sh + environment: + - TZ=Europe/Paris + - MYSQL_DATABASE=oai_db + - MYSQL_USER=test + - MYSQL_PASSWORD=test + - MYSQL_ROOT_PASSWORD=linux + healthcheck: + test: /bin/bash -c "/tmp/mysql-healthcheck.sh" + interval: 10s + timeout: 5s + start_period: 10s + start_interval: 500ms + retries: 30 + networks: + public_net: + ipv4_address: 192.168.71.131 + + oai-amf: + container_name: "rfsim5g-oai-amf" + image: oaisoftwarealliance/oai-amf:v2.1.10 + environment: + - TZ=Europe/paris + volumes: + - ../5g_rfsimulator/mini_nonrf_config.yaml:/openair-amf/etc/config.yaml + depends_on: + - mysql + networks: + public_net: + ipv4_address: 192.168.71.132 + + oai-smf: + container_name: "rfsim5g-oai-smf" + image: oaisoftwarealliance/oai-smf:v2.1.10 + environment: + - TZ=Europe/Paris + volumes: + - ../5g_rfsimulator/mini_nonrf_config.yaml:/openair-smf/etc/config.yaml + depends_on: + - oai-amf + networks: + public_net: + ipv4_address: 192.168.71.133 + + oai-upf: + container_name: "rfsim5g-oai-upf" + image: oaisoftwarealliance/oai-upf:v2.1.10 + init: true + environment: + - TZ=Europe/Paris + volumes: + - ../5g_rfsimulator/mini_nonrf_config.yaml:/openair-upf/etc/config.yaml + depends_on: + - oai-smf + cap_add: + - NET_ADMIN + - SYS_ADMIN + cap_drop: + - ALL + privileged: true + networks: + public_net: + ipv4_address: 192.168.71.134 + interface_name: eth0 + traffic_net: + ipv4_address: 192.168.72.134 + interface_name: eth1 + + oai-ext-dn: + privileged: true + container_name: rfsim5g-oai-ext-dn + image: oaisoftwarealliance/trf-gen-cn5g:latest + init: true + entrypoint: /bin/bash -c \ + "iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE;"\ + "ip route add 12.1.1.0/24 via 192.168.72.134 dev eth0; sleep infinity" + depends_on: + - oai-upf + networks: + traffic_net: + ipv4_address: 192.168.72.135 + healthcheck: + test: /bin/bash -c "ping -c 2 192.168.72.134" + interval: 10s + timeout: 5s + retries: 5 + start_period: 10s + start_interval: 500ms + + oai-gnb: + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} + container_name: rfsim5g-oai-gnb + cap_drop: + - ALL + environment: + USE_ADDITIONAL_OPTIONS: -E --log_config.global_log_options level,nocolor,time --device.name vrtsim --vrtsim.role server --vrtsim.cirdb 1 --vrtsim.cirdb_file /cirdb/cir_db.bin --vrtsim.cirdb_yaml /cirdb/cir_db.yaml --vrtsim.cirdb_model_id 0 --vrtsim.cirdb_ds_ns 1 --vrtsim.cirdb_speed_mps 1.5 + depends_on: + cir-generator: + condition: service_completed_successfully + oai-ext-dn: + condition: service_started + networks: + public_net: + ipv4_address: 192.168.71.140 + volumes: + - ../../conf_files/gnb.sa.band78.106prb.vrtsim.2x2.yaml:/opt/oai-gnb/etc/gnb.yaml + - cirdb_data:/cirdb:ro + - tmp_data:/tmp/ + healthcheck: + test: /bin/bash -c "pgrep nr-softmodem" + interval: 10s + timeout: 5s + start_period: 10s + start_interval: 500ms + retries: 5 + ipc: host + + oai-nr-ue: + image: ${REGISTRY-oaisoftwarealliance/}${NRUE_IMG:-oai-nr-ue}:${TAG:-develop} + container_name: rfsim5g-oai-nr-ue + cap_drop: + - ALL + cap_add: + - NET_ADMIN + - NET_RAW + environment: + USE_ADDITIONAL_OPTIONS: -E -r 106 --numerology 1 --band 78 -C 3619200000 --ssb 516 --uicc0.imsi 208990100001100 --log_config.global_log_options level,nocolor,time --device.name vrtsim --vrtsim.role client + depends_on: + - oai-gnb + networks: + public_net: + ipv4_address: 192.168.71.150 + devices: + - /dev/net/tun:/dev/net/tun + volumes: + - ../../conf_files/nrue.vrtsim.chanmod.yaml:/opt/oai-nr-ue/etc/nr-ue.yaml + - tmp_data:/tmp/ + healthcheck: + test: /bin/bash -c "pgrep nr-uesoftmodem" + interval: 10s + timeout: 5s + retries: 5 + start_period: 10s + start_interval: 500ms + ipc: host + +networks: + public_net: + driver: bridge + name: rfsim5g-oai-public-net + ipam: + config: + - subnet: 192.168.71.128/26 + driver_opts: + com.docker.network.bridge.name: "rfsim5g-public" + traffic_net: + driver: bridge + name: rfsim5g-oai-traffic-net + ipam: + config: + - subnet: 192.168.72.128/26 + driver_opts: + com.docker.network.bridge.name: "rfsim5g-traffic" + +volumes: + tmp_data: + cirdb_data: diff --git a/cmake_targets/build_oai b/cmake_targets/build_oai index c6d1276e88..c9e268407d 100755 --- a/cmake_targets/build_oai +++ b/cmake_targets/build_oai @@ -36,9 +36,8 @@ set_openair_env # Variables for UE data generation conf_nvram_path=$OPENAIR_DIR/openair3/NAS/TOOLS/ue_eurecom_test_sfr.conf -HW="" +HWs="" BUILD_DIR=ran_build -DISABLE_HARDWARE_DEPENDENCY="False" CMAKE_BUILD_TYPE="RelWithDebInfo" CMAKE_CMD="$CMAKE" OPTIONAL_LIBRARIES="telnetsrv enbscope uescope nrscope ldpc_cuda ldpc_aal ldpc_xdma websrv oai_iqplayer imscope imscope_record" @@ -254,22 +253,22 @@ function main() { -w | --hardware) case "$2" in "USRP" | "BLADERF" | "LMSSDR" | "IRIS") - HW="OAI_"$2 + HWs+=" OAI_"$2 TARGET_LIST="$TARGET_LIST oai_${2,,}devif" # ,, makes lowercase CMAKE_CMD="$CMAKE_CMD -DOAI_$2=ON" ;; "SIMU") - HW="OAI_"$2 + HWs+=" OAI_"$2 TARGET_LIST="$TARGET_LIST rfsimulator" CMAKE_CMD="$CMAKE_CMD -DOAI_$2=ON" ;; "AW2SORI") - HW="OAI_"$2 + HWs=" OAI_"$2 TARGET_LIST="$TARGET_LIST aw2sori_transpro" CMAKE_CMD="$CMAKE_CMD -DOAI_$2=ON" ;; "AERIAL"|"Aerial"|"aerial") - HW="OAI_AERIAL" + HWs+=" OAI_AERIAL" CMAKE_CMD="$CMAKE_CMD -DOAI_AERIAL=ON" ;; "None") @@ -328,10 +327,6 @@ function main() { CMAKE_CMD="$CMAKE_CMD -DT_TRACER=False" echo_info "Disabling the T tracer" shift 1;; - --disable-hardware-dependency) - echo_info "Disabling hardware dependency for compiling software" - DISABLE_HARDWARE_DEPENDENCY="True" - shift 1;; --ue-trace) CMAKE_CMD="$CMAKE_CMD -DUE_DEBUG_TRACE=False" echo_info "Enabling UE trace for debug" @@ -424,27 +419,23 @@ function main() { echo_info "installing LTTng" install_lttng $1 fi - if [ "$HW" == "OAI_USRP" ] ; then - echo_info "installing packages for USRP support" - check_install_usrp_uhd_driver - if [ ! -v BUILD_UHD_FROM_SOURCE ] && [ ! "$DISABLE_HARDWARE_DEPENDENCY" == "True" ]; then - install_usrp_uhd_driver $UHD_IMAGES_DIR + for HW in $HWs; do + if [ "$HW" == "OAI_USRP" ] ; then + echo_info "installing packages for USRP support" + check_install_usrp_uhd_driver + if [ ! -v BUILD_UHD_FROM_SOURCE ]; then + install_usrp_uhd_driver $UHD_IMAGES_DIR + fi + elif [ "$HW" == "OAI_BLADERF" ] ; then + echo_info "installing packages for BLADERF support" + check_install_bladerf_driver + elif [ "$HW" == "OAI_IRIS" ] ; then + echo_info "installing packages for IRIS support" + check_install_soapy + else + echo_warning "no installation scripts for hardware $HW" fi - fi - if [ "$HW" == "OAI_BLADERF" ] ; then - echo_info "installing packages for BLADERF support" - check_install_bladerf_driver - if [ ! "$DISABLE_HARDWARE_DEPENDENCY" == "True" ]; then - flash_firmware_bladerf - fi - fi - if [ "$HW" == "OAI_IRIS" ] ; then - echo_info "installing packages for IRIS support" - check_install_soapy - #if [ ! "$DISABLE_HARDWARE_DEPENDENCY" == "True" ]; then - # flash_firmware_iris - #fi - fi + done fi if [ "$INSTALL_OPTIONAL" = "1" ] ; then diff --git a/cmake_targets/tools/build_helper b/cmake_targets/tools/build_helper index 3443d06c43..74d45cda9b 100755 --- a/cmake_targets/tools/build_helper +++ b/cmake_targets/tools/build_helper @@ -107,6 +107,7 @@ check_supported_distribution() { "ubuntu22.04") return 0 ;; "ubuntu20.04") return 0 ;; "debian11") return 0 ;; + "debian12") return 0 ;; "fedora41") return 0 ;; "fedora40") return 0 ;; "rhel8.2") return 0 ;; @@ -345,19 +346,10 @@ install_bladerf_driver_from_source(){ make $SUDO make install $SUDO ldconfig - echo "Downloading FPGA and firmware images" - cd /tmp/bladeRF - #wget has been removed from default dependencies, so we must be sure that's installed - $SUDO $INSTALLER -y install wget - wget --tries=3 --retry-connrefused https://www.nuand.com/fx3/bladeRF_fw_latest.img - wget --tries=3 --retry-connrefused https://www.nuand.com/fpga/hostedx40-latest.rbf - sudo mkdir -p /usr/share/Nuand/bladeRF - sudo mv bladeRF_fw_latest.img /usr/share/Nuand/bladeRF/bladeRF_fw.img - sudo mv hostedx40-latest.rbf /usr/share/Nuand/bladeRF/hostedx40.rbf } check_install_bladerf_driver(){ - if [[ "$OS_DISTRO" == "ubuntu" ]]; then + if [[ "$OS_BASEDISTRO" == "debian" ]]; then $SUDO apt-get install -y bladerf libbladerf-dev $SUDO apt-get install -y bladerf-firmware-fx3 $SUDO apt-get install -y bladerf-fpga-hostedx40 @@ -368,10 +360,6 @@ check_install_bladerf_driver(){ fi } -flash_firmware_bladerf() { - $SUDO bladeRF-cli --flash-firmware /usr/share/Nuand/bladeRF/bladeRF_fw.img -} - check_install_lmssdr_driver(){ if ( [ -d "/usr/local/include/lime" ] && [ -f "/usr/local/include/lime/LimeSuite.h" ] ) @@ -453,7 +441,7 @@ check_install_additional_tools (){ local optional_packages="" if [[ "$OS_BASEDISTRO" == "debian" ]]; then case "$(get_distribution_release)" in - "ubuntu20.04" | "ubuntu22.04" | "debian11" ) + "ubuntu20.04" | "ubuntu22.04" | "debian11" | "debian12" ) optional_packages="python3 python3-pip python3-dev python3-scipy python3-matplotlib python3-pyroute2 universal-ctags" ;; esac @@ -496,7 +484,7 @@ check_install_oai_software() { if [[ "$OS_BASEDISTRO" == "debian" ]]; then $SUDO apt install -y software-properties-common case "$(get_distribution_release)" in - "debian11") + "debian11" | "debian12" ) specific_packages="libz-dev" ;; esac diff --git a/common/utils/CMakeLists.txt b/common/utils/CMakeLists.txt index 395139132d..f4611914d4 100644 --- a/common/utils/CMakeLists.txt +++ b/common/utils/CMakeLists.txt @@ -14,12 +14,9 @@ add_subdirectory(nr) add_subdirectory(LOG) add_subdirectory(threadPool) add_subdirectory(time_manager) -add_library(utils utils.c system.c time_meas.c time_stat.c tuntap_if.c) +add_library(utils utils.c system.c time_meas.c time_stat.c tuntap_if.c reverse_bits.c) target_include_directories(utils PUBLIC .) target_link_libraries(utils PRIVATE ${T_LIB}) -if (ENABLE_TESTS) - add_subdirectory(hashtable/tests) -endif() add_subdirectory(barrier) add_subdirectory(actor) add_subdirectory(shm_iq_channel) diff --git a/common/utils/ds/CMakeLists.txt b/common/utils/ds/CMakeLists.txt index b5f5efb47a..22dc7a3c63 100644 --- a/common/utils/ds/CMakeLists.txt +++ b/common/utils/ds/CMakeLists.txt @@ -2,6 +2,8 @@ add_library(ds OBJECT byte_array.c byte_array_producer.c seq_arr.c + hashtable.c + obj_hashtable.c ) target_include_directories(ds PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/common/utils/hashtable/hashtable.c b/common/utils/ds/hashtable.c similarity index 100% rename from common/utils/hashtable/hashtable.c rename to common/utils/ds/hashtable.c diff --git a/common/utils/hashtable/hashtable.h b/common/utils/ds/hashtable.h similarity index 100% rename from common/utils/hashtable/hashtable.h rename to common/utils/ds/hashtable.h diff --git a/common/utils/hashtable/obj_hashtable.c b/common/utils/ds/obj_hashtable.c similarity index 100% rename from common/utils/hashtable/obj_hashtable.c rename to common/utils/ds/obj_hashtable.c diff --git a/common/utils/hashtable/obj_hashtable.h b/common/utils/ds/obj_hashtable.h similarity index 100% rename from common/utils/hashtable/obj_hashtable.h rename to common/utils/ds/obj_hashtable.h diff --git a/common/utils/ds/tests/CMakeLists.txt b/common/utils/ds/tests/CMakeLists.txt index a3ef512f09..a38fba2dab 100644 --- a/common/utils/ds/tests/CMakeLists.txt +++ b/common/utils/ds/tests/CMakeLists.txt @@ -2,3 +2,9 @@ add_executable(test_seq_arr test_seq_array.c) target_link_libraries(test_seq_arr ds alg) add_dependencies(tests test_seq_arr) add_test(NAME test_seq_arr COMMAND test_seq_arr) # no options required + +add_executable(test_hashtable test_hashtable.cpp) +add_dependencies(tests test_hashtable) +target_link_libraries(test_hashtable PRIVATE ds GTest::gtest) +add_test(NAME test_hashtable + COMMAND ./test_hashtable) diff --git a/common/utils/hashtable/tests/test_hashtable.cpp b/common/utils/ds/tests/test_hashtable.cpp similarity index 100% rename from common/utils/hashtable/tests/test_hashtable.cpp rename to common/utils/ds/tests/test_hashtable.cpp diff --git a/common/utils/hashtable/tests/CMakeLists.txt b/common/utils/hashtable/tests/CMakeLists.txt deleted file mode 100644 index baf2461262..0000000000 --- a/common/utils/hashtable/tests/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -add_executable(test_hashtable test_hashtable.cpp) -add_dependencies(tests test_hashtable) -target_link_libraries(test_hashtable PRIVATE HASHTABLE GTest::gtest) -add_test(NAME test_hashtable - COMMAND ./test_hashtable) diff --git a/common/utils/nr/nr_common.c b/common/utils/nr/nr_common.c index 96b8681cff..b370b8da66 100644 --- a/common/utils/nr/nr_common.c +++ b/common/utils/nr/nr_common.c @@ -37,7 +37,6 @@ #include "nr_common.h" #include #include -#include #define C_SRS_NUMBER (64) #define B_SRS_NUMBER (4) @@ -112,46 +111,6 @@ static const unsigned short srs_bandwidth_config[C_SRS_NUMBER][B_SRS_NUMBER][2] /* 63 */ {{272, 1}, {16, 17}, {8, 2}, {4, 2}}, }; -static const uint8_t bit_reverse_table_256[] = { - 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, 0x08, 0x88, 0x48, 0xC8, - 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, - 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, - 0x3C, 0xBC, 0x7C, 0xFC, 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, - 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, 0x06, 0x86, 0x46, 0xC6, - 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, - 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, - 0x31, 0xB1, 0x71, 0xF1, 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, - 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, 0x0D, 0x8D, 0x4D, 0xCD, - 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, - 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, - 0x3B, 0xBB, 0x7B, 0xFB, 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, - 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF}; - -void reverse_bits_u8(uint8_t const* in, size_t sz, uint8_t* out) -{ - DevAssert(in != NULL); - DevAssert(out != NULL); - -// Bit reversal implementation based on https://wunkolo.github.io/post/2020/11/gf2p8affineqb-bit-reversal/ -#if defined(__GFNI__) && defined(__AVX512F__) - int simde_sz = 64; - int i = 0; - int simde_bound = sz - simde_sz; - for (; i <= simde_bound; i += simde_sz) { - __m512i input = _mm512_loadu_epi8(&in[i]); - __m512i reversed = _mm512_gf2p8affine_epi64_epi8(input, _mm512_set1_epi64(0x8040201008040201), 0); - _mm512_storeu_epi8(&out[i], reversed); - } - - for (; i < sz; ++i) { - out[i] = bit_reverse_table_256[in[i]]; - } -#else - for(size_t i = 0; i < sz; ++i) - out[i] = bit_reverse_table_256[in[i]]; -#endif -} - /** @brief 3GPP TS 38.133 Table 10.1.6.1-1 mapping from dBm to RSRP index */ uint8_t get_rsrp_index(int rsrp_dBm) { @@ -163,31 +122,6 @@ uint8_t get_rsrp_index(int rsrp_dBm) return (uint8_t)index; } -// Reverse bits implementation based on http://graphics.stanford.edu/~seander/bithacks.html -uint64_t reverse_bits(uint64_t in, int n_bits) -{ - // Reverse n_bits in uint64_t variable, example: - // n_bits: 10 - // in: 10 0000 1111 - // return: 11 1100 0001 - - AssertFatal(n_bits <= 64, "Maximum bits to reverse is 64, impossible to reverse %d bits!\n", n_bits); - uint64_t rev_bits = 0; - uint8_t *p = (uint8_t *)∈ - uint8_t *q = (uint8_t *)&rev_bits; - int n_bytes = n_bits >> 3; - for (int n = 0; n < n_bytes; n++) { - q[n_bytes - 1 - n] = bit_reverse_table_256[p[n]]; - } - - // Reverse remaining bits (not aligned with 8-bit) - rev_bits = rev_bits << (n_bits % 8); - for (int i = n_bytes * 8; i < n_bits; i++) { - rev_bits |= ((in >> i) & 0x1) << (n_bits - i - 1); - } - return rev_bits; -} - #define NUM_BW_ENTRIES 15 static const int tables_5_3_2[5][NUM_BW_ENTRIES] = { @@ -1447,6 +1381,31 @@ unsigned short get_N_b_srs(int c_srs, int b_srs) { return srs_bandwidth_config[c_srs][b_srs][1]; } +// TODO: Implement to b_SRS = 1 and b_SRS = 2 +long rrc_get_max_nr_csrs(const int max_rbs, const long b_SRS) +{ + if(b_SRS>0) { + LOG_E(NR_RRC,"rrc_get_max_nr_csrs(): Not implemented yet for b_SRS>0\n"); + return 0; // This c_srs is always valid + } + + const uint16_t m_SRS[64] = { 4, 8, 12, 16, 16, 20, 24, 24, 28, 32, 36, 40, 48, 48, 52, 56, 60, 64, 72, 72, 76, 80, 88, + 96, 96, 104, 112, 120, 120, 120, 128, 128, 128, 132, 136, 144, 144, 144, 144, 152, 160, + 160, 160, 168, 176, 184, 192, 192, 192, 192, 208, 216, 224, 240, 240, 240, 240, 256, 256, + 256, 264, 272, 272, 272 }; + + long c_srs = 0; + uint16_t m = 4; + for(int c = 1; c<64; c++) { + if(m_SRS[c]>m && m_SRS[c]<5G-TMSI (32 bits)> + return ((uint64_t)amf_set_id << 38) | ((uint64_t)amf_pointer << 32) | m_tmsi; +} + +/** @brief Construct 5G-S-TMSI-Part1 from 5G-S-TMSI components + * @param amf_set_id AMF Set ID (10 bits) + * @param amf_pointer AMF Pointer (6 bits) + * @param m_tmsi 5G-TMSI (32 bits) + * @return 5G-S-TMSI-Part1 (rightmost 39 bits of the full 5G-S-TMSI) + * @note 5G-S-TMSI-Part1 is the rightmost 39 bits of the full 5G-S-TMSI: + * - Bits 32-37: AMF Pointer (6 bits) + * - Bits 0-31: 5G-TMSI (32 bits) + * @ref 3GPP TS 23.003 */ +uint64_t nr_construct_5g_s_tmsi_part1(uint16_t amf_set_id, uint8_t amf_pointer, uint32_t m_tmsi) +{ + // Construct full 5G-S-TMSI and extract Part1: rightmost 39 bits + uint64_t full_s_tmsi = nr_construct_5g_s_tmsi(amf_set_id, amf_pointer, m_tmsi); + return full_s_tmsi & ((1ULL << 39) - 1); +} diff --git a/common/utils/nr/nr_common.h b/common/utils/nr/nr_common.h index e62d66658c..ada9813373 100644 --- a/common/utils/nr/nr_common.h +++ b/common/utils/nr/nr_common.h @@ -286,9 +286,6 @@ static inline void warn_higher_threequarter_fs(const int n_rb, const int mu) mu); } -uint64_t reverse_bits(uint64_t in, int n_bits); -void reverse_bits_u8(uint8_t const* in, size_t sz, uint8_t* out); - uint64_t from_nrarfcn(int nr_bandP, uint8_t scs_index, uint32_t dl_nrarfcn); uint32_t to_nrarfcn(int nr_bandP, uint64_t dl_CarrierFreq, uint8_t scs_index, uint32_t bw); @@ -357,6 +354,12 @@ frequency_range_t get_freq_range_from_band(uint16_t band); */ float get_beta_dmrs(int num_cdm_groups_no_data, bool is_type2); +/** @brief Construct full 5G-S-TMSI from 5G-S-TMSI components */ +uint64_t nr_construct_5g_s_tmsi(uint16_t amf_set_id, uint8_t amf_pointer, uint32_t m_tmsi); + +/** @brief Construct 5G-S-TMSI-Part1 from 5G-S-TMSI components */ +uint64_t nr_construct_5g_s_tmsi_part1(uint16_t amf_set_id, uint8_t amf_pointer, uint32_t m_tmsi); + #define CEILIDIV(a,b) ((a+b-1)/b) #define ROUNDIDIV(a,b) (((a<<1)+b)/(b<<1)) #define BOUNDED_EVAL(a, b, c) (min(c, max(a, b))) diff --git a/common/utils/reverse_bits.c b/common/utils/reverse_bits.c new file mode 100644 index 0000000000..525929c557 --- /dev/null +++ b/common/utils/reverse_bits.c @@ -0,0 +1,92 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#include "reverse_bits.h" +// we avoid assertions.h as it necessitates othe dependencies (e.g., exit_function) +#include +#include + +static const uint8_t bit_reverse_table_256[] = { + 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, 0x08, 0x88, 0x48, 0xC8, + 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, + 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, + 0x3C, 0xBC, 0x7C, 0xFC, 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, + 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, 0x06, 0x86, 0x46, 0xC6, + 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, + 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, + 0x31, 0xB1, 0x71, 0xF1, 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, + 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, 0x0D, 0x8D, 0x4D, 0xCD, + 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, + 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, + 0x3B, 0xBB, 0x7B, 0xFB, 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, + 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF}; + +void reverse_bits_u8(uint8_t const* in, size_t sz, uint8_t* out) +{ + assert(in != NULL); + assert(out != NULL); + +// Bit reversal implementation based on https://wunkolo.github.io/post/2020/11/gf2p8affineqb-bit-reversal/ +#if defined(__GFNI__) && defined(__AVX512F__) + int simde_sz = 64; + int i = 0; + int simde_bound = sz - simde_sz; + for (; i <= simde_bound; i += simde_sz) { + __m512i input = _mm512_loadu_epi8(&in[i]); + __m512i reversed = _mm512_gf2p8affine_epi64_epi8(input, _mm512_set1_epi64(0x8040201008040201), 0); + _mm512_storeu_epi8(&out[i], reversed); + } + + for (; i < sz; ++i) { + out[i] = bit_reverse_table_256[in[i]]; + } +#else + for(size_t i = 0; i < sz; ++i) + out[i] = bit_reverse_table_256[in[i]]; +#endif +} + + +// Reverse bits implementation based on http://graphics.stanford.edu/~seander/bithacks.html +uint64_t reverse_bits(uint64_t in, int n_bits) +{ + // Reverse n_bits in uint64_t variable, example: + // n_bits: 10 + // in: 10 0000 1111 + // return: 11 1100 0001 + + assert(n_bits <= 64); // Maximum bits to reverse is 64 + uint64_t rev_bits = 0; + uint8_t *p = (uint8_t *)∈ + uint8_t *q = (uint8_t *)&rev_bits; + int n_bytes = n_bits >> 3; + for (int n = 0; n < n_bytes; n++) { + q[n_bytes - 1 - n] = bit_reverse_table_256[p[n]]; + } + + // Reverse remaining bits (not aligned with 8-bit) + rev_bits = rev_bits << (n_bits % 8); + for (int i = n_bytes * 8; i < n_bits; i++) { + rev_bits |= ((in >> i) & 0x1) << (n_bits - i - 1); + } + return rev_bits; +} + diff --git a/common/utils/reverse_bits.h b/common/utils/reverse_bits.h new file mode 100644 index 0000000000..5627ba4b6d --- /dev/null +++ b/common/utils/reverse_bits.h @@ -0,0 +1,31 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#ifndef REVERSE_BITS_H_ +#define REVERSE_BITS_H_ + +#include +#include + +uint64_t reverse_bits(uint64_t in, int n_bits); +void reverse_bits_u8(uint8_t const* in, size_t sz, uint8_t* out); + +#endif /* REVERSE_BITS_H_ */ diff --git a/common/utils/telnetsrv/telnetsrv_ci.c b/common/utils/telnetsrv/telnetsrv_ci.c index a2bcccd150..084e3b93f3 100644 --- a/common/utils/telnetsrv/telnetsrv_ci.c +++ b/common/utils/telnetsrv/telnetsrv_ci.c @@ -168,7 +168,7 @@ int fetch_du_by_ue_id(char *buf, int debug, telnet_printfunc_t prnt) nr_rrc_du_container_t *du = get_du_for_ue(RC.nrrrc[0], ue_id); if (du) { - prnt("gNB_DU_id %d is connected to ue_id %ld\n", du->setup_req->gNB_DU_id, ue_id); + prnt("gNB_DU_id %ld is connected to ue_id %ld\n", du->gNB_DU_id, ue_id); return 0; } else { ERROR_MSG_RET("No DU connected\n"); diff --git a/doc/MAC/mac-usage.md b/doc/MAC/mac-usage.md index 8dd15f845a..94c813d7ac 100644 --- a/doc/MAC/mac-usage.md +++ b/doc/MAC/mac-usage.md @@ -229,7 +229,7 @@ In the `MACRLCs` section of the gNB/DU configuration file: (see `ulsch_max_frame_inactivity`) or after scheduling request (SR) * `identity_precoding_matrix` (default 0=false): flag to enable to use only the identity precoding matrix in DL precoding -* `set_analog_beamforming` (default 0=false): flag to enable analog +* `set_analog_beamforming` (default "none"): parameter to enable analog beamforming (for more information [`analog_beamforming.md`](../analog_beamforming.md)) * `beam_duration` (default 1): duration/number of consecutive slots for a given set of beams, depending on hardware switching performance diff --git a/doc/ORAN_FHI7.2_Tutorial.md b/doc/ORAN_FHI7.2_Tutorial.md index a241fbb3f5..b02f2973a6 100644 --- a/doc/ORAN_FHI7.2_Tutorial.md +++ b/doc/ORAN_FHI7.2_Tutorial.md @@ -1031,6 +1031,7 @@ Edit the sample OAI gNB configuration file and check following parameters: * `prach_config`: PRACH-specific configuration * `eAxC_offset`: PRACH antenna offset; if not set, default value of `N = max(Nrx,Ntx)` is used * `kbar`: the PRACH guard interval, provided in RU + * `app_id`: `DU` or `RU`. Sets the application `id` value in xRAN. Use the default value: `DU`. Layer mapping (eAxC offsets) happens as follows: - For PUSCH/PDSCH, the layers are mapped to `[0,1,...,Nrx-1]/[0,1,...,Ntx-1]` where `Nrx/Ntx` is the @@ -1491,6 +1492,7 @@ fhi_72 = { * `dpdk_iova_mode`: [*] * `owdm_enable`: [*] * `fh_config`: only DU delay profile (`T1a` and `Ta4`) + * `app_id`: [*] [*] see [Configure OAI gNB](#configure-oai-gnb) for more details diff --git a/doc/README.md b/doc/README.md index 022174a4e7..8fc53eea24 100644 --- a/doc/README.md +++ b/doc/README.md @@ -44,6 +44,7 @@ There is some general information in the [OpenAirInterface Gitlab Wiki](https:// - [How to run a 4G setup using L1 simulator](./L1SIM.md) _Note: we recommend the RFsimulator_ - [How to use the L2 simulator](./L2NFAPI.md) - [How to use the OAI channel simulator](../openair1/SIMULATION/TOOLS/DOC/channel_simulation.md) +- [How to use GPU-accelerated channel simulation](../openair1/SIMULATION/TOOLS/DOC/gpu_acceleration.md) - [How to use multiple BWPs](./RUN_NR_multiple_BWPs.md) - [How to run OAI-VNF and OAI-PNF](./nfapi.md): how to run the FAPI/nFAPI split, including some general remarks on FAPI/nFAPI. @@ -103,7 +104,7 @@ Some directories under `radio` contain READMEs: - [RFsimulator](../radio/rfsimulator/README.md) - [USRP](../radio/USRP/README.md) -- [BladeRF](../radio/BLADERF/README) +- [BladeRF](../radio/BLADERF/README.md) - [IQPlayer](../radio/iqplayer/DOC/iqrecordplayer_usage.md), and [general documentation](./iqrecordplayer_usage.md) - [fhi_72](../radio/fhi_72/README.md) - [vrtsim](../radio/vrtsim/README.md) diff --git a/doc/RRC/rrc-dev.md b/doc/RRC/rrc-dev.md index f5c4956c9d..2299394e0c 100644 --- a/doc/RRC/rrc-dev.md +++ b/doc/RRC/rrc-dev.md @@ -390,13 +390,255 @@ sequenceDiagram ## Structures -### Cells +### DUs and Cells -OAI 5G RRC does not actually handle multiple cells as of now, but multiple DUs, -each being limited to one cell. +OAI 5G RRC is enabling support for multiple DUs, with each DU potentially +serving multiple cells (though currently each DU is limited to one cell in practice). +The architecture separates DU management from cell management to enable future +multi-cell per DU capability. -Cell-related data is stored in `nr_rrc_du_container_t`, and kept in a tree -indexed by the SCTP association ID. +#### DU Management + +DU-related data is stored in `nr_rrc_du_container_t`, and kept in a red-black +tree indexed by the unique SCTP association ID (`assoc_id`). Each DU container stores: +- DU identity and name +- SCTP association ID for F1 interface communication +- RRC version information +- Sequential array of cell pointers (`cells`) - stores pointers to cells belonging to this DU (seq_arr_t) + +Key Functions: +- `get_du_by_assoc_id()` - Lookup DU by SCTP association ID (O(log d) tree lookup) +- `get_du_for_ue()` - Get DU associated with a specific UE +- `find_target_du()` - Find a target DU for handover operations + +#### Cell Management + +Cell-related data is stored in `nr_rrc_cell_container_t`. Cells are stored in two data structures: + +1. Global cell tree (`rrc->cells`): Red-black tree indexed by `cell_id`, containing all cells from all DUs. Used for efficient O(log N_CELL) lookups across all DUs. PCI reuse across the network is allowed and this is reflected in the tree. +2. DU cell array (`du->cells`): Sequential array (seq_arr_t) of cell pointers, storing only cells belonging to that specific DU. Used for DU-specific operations. PCI must be unique within a DU. + +Each cell container stores: +- Cell identity (NR Cell ID) and PCI (Physical Cell ID) +- Link to serving DU via the unique `assoc_id` +- Cell-specific information (PLMN, TAC, frequency, mode TDD/FDD) +- MIB, SIB1, and MeasurementTimingConfiguration messages + +Key Functions: +- `get_cell_by_cell_id()` - Lookup cell by NR Cell ID using global cell tree +- `rrc_get_cell_for_du()` - Lookup cell by cell_id within a specific DU's cell array +- `rrc_get_cell_by_pci_for_du()` - Lookup cell by PCI within DU's cells array +- `rrc_add_cell_to_du()` - Add cell to DU's sequential array +- `rrc_free_cell_container()` - Free cell container and associated ASN.1 structures + +Architecture Notes: +- The `assoc_id` field (in cell and DUs containers) links cells to their serving DUs +- When a DU connects via F1 Setup, cells are added to both the global tree and the DU's array +- When a DU disconnects, cells are removed from both structures +- Global tree enables efficient cross-DU cell lookups (O(log N_CELL)) +- DU array enables efficient per-DU cell iteration (O(k) where k=cells per DU) +- Each DU maintains a sequential array of cell pointers + +##### DU and Cell Lifecycle + +The following diagram shows the lifecycle of DUs and their associated cells, including the main F1AP messages and internal operations: + +```mermaid +sequenceDiagram + participant CellTree as Cell Tree + participant DUTree as DU Tree + participant CU as CU-CP + participant DU as gNB-DU + participant UE as UE + + Note over DU,CellTree: DU Connection & Cell Registration + DU->>CU: F1AP F1 Setup Request(DU ID, Cell Info, MIB/SIB1) + Note over CU: Validate: PLMN match with CU configuration + alt PLMN mismatch + CU->>DU: F1AP F1 Setup Failure (PLMN not served) + end + Note over CU: Validate: DU ID uniqueness (RB_FOREACH gNB_DU_id in DU tree) + alt DU ID already exists + CU->>DU: F1AP F1 Setup Failure (Unspecified) + end + Note over CU: Validate: Neighbour cell configuration (if configured) + alt Neighbour config invalid + CU->>DU: F1AP F1 Setup Failure (Unspecified) + end + Note over CU: Extract MIB/SIB1 from system info (if present) + alt System info extraction fails + CU->>DU: F1AP F1 Setup Failure (Semantic error) + end + alt All Validations Success + CU->>CU: Create cell container (nr_rrc_cell_container_t), set assoc_id, copy cell info, set MIB/SIB1 + CU->>CellTree: rrc_add_cell(rrc, new) - RB_INSERT into global tree, increment rrc->num_cells + alt Duplicate cell_id (collision) + CU->>DU: F1AP F1 Setup Failure (Cell not available) + else Cell added to tree + CU->>CU: Create DU container (nr_rrc_du_container_t) + CU->>CU: seq_arr_init(&du->cells) - Initialize DU's cell array + CU->>DUTree: rrc_add_du(rrc, du) - RB_INSERT(du), increment rrc->num_dus + CU->>CU: rrc_add_cell_to_du(&du->cells, new) - Add cell to DU's array + Note over CU,UE: Cell available for UE association + CU->>CU: Encode CU SIBs (if configured) + CU->>DU: F1AP F1 Setup Response(Cells to Activate, CU SIBs) + Note over CU,CellTree: DU and cell now active + end + end + + UE->>CU: RRC Setup Request + Note over CU: rrc_handle_RRCSetupRequest() + CU->>CU: get_cell_by_cell_id(&rrc->cells) - Use global tree + alt Cell not found + CU->>UE: RRC Reject + end + Note over CU: UE Cell Association + CU->>CU: rrc_add_ue_serving_cell(UE, cell, RRC_PCELL_INDEX) + + Note over DU,CellTree: Optional: Cell Configuration Update + opt DU sends configuration update + DU->>CU: F1AP DU Configuration Update(Add/Modify/Delete cells) + Note over CU: get_du_by_assoc_id(assoc_id) + loop For each cell to add + CU->>CU: get_cell_by_cell_id(&rrc->cells) - Check cell_id uniqueness globally + alt Duplicate cell_id found + Note over CU: Reject and return + end + CU->>CU: rrc_get_cell_by_pci_for_du(&du->cells) - Check PCI unique within DU + alt Duplicate PCI in DU + Note over CU: Reject and return + end + end + loop Cell Modification + CU->>CU: get_cell_by_cell_id(cells, old_nr_cellid) - Find cell by old cell_id + CU->>CU: update_cell_info(rrc, old_nci, new_ci) - Update in place + Note over CU: If cell_id changes: RB_REMOVE then re-insert after update + Note over CU: Free old MTC if new measurement timing config provided + Note over CU: If sys_info present: extract MIB/SIB1 and set on cell + end + CU->>DU: F1AP DU Configuration Update Acknowledge + end + + Note over DU,CellTree: DU Disconnection & Cell Cleanup + DU-->>CU: F1AP Lost Connection(SCTP connection lost) + Note over CU: rrc_CU_process_f1_lost_connection() + CU->>DUTree: RB_FIND(du) - Find DU by assoc_id using temporary struct + alt DU not found + Note over CU: Log warning and return + end + Note over CU: rrc_cleanup_du() then invalidate_du_connections() + CU->>CU: Iterate cells in DU's array (last to first) + loop For each cell in DU's array + CU->>CU: seq_arr_erase(&du->cells, cell_ptr) - Remove from DU's array + CU->>CellTree: rrc_rm_cell(): RB_REMOVE(cell), decrement num_cells, rrc_free_cell_container() + end + CU->>DUTree: rrc_rm_du(): RB_REMOVE(du) - Remove DU from tree + CU->>CU: Decrement rrc->num_dus counter + CU->>CU: seq_arr_free(&du->cells) - Free DU's cell array + CU->>CU: rrc_free_du_container() - Free DU container + Note over CU: invalidate_du_connections() + loop For each UE: + CU->>CU: rrc_remove_ue_scells_from_du() - Remove SCells from disconnected DU + alt UE belongs to disconnected DU + CU->>CU: Set du_assoc_id = 0 (mark DU offline) + CU->>CU: Trigger NGAP UE Context Release Request + end + end + Note over CU,CellTree: DU and all cells removed +``` + +Key Functions: +- `rrc_gNB_process_f1_setup_req()` - Handles F1 Setup Request, creates DU and cell containers. + Validates PLMN match, DU ID uniqueness, and cell_id/PCI uniqueness before creating containers. +- `rrc_gNB_process_f1_du_configuration_update()` - Handles cell configuration updates. + Currently supports cell modification (MIB/SIB1 updates) and validates cell additions, but cell addition + and deletion are not yet fully implemented. +- `rrc_CU_process_f1_lost_connection()` - Handles DU disconnection. Calls `rrc_cleanup_du()` to + remove all cells and the DU from their trees and free resources, then + `invalidate_du_connections()` to clean up UE associations (e.g. trigger NGAP UE Context Release Request for UEs on that DU). + +#### UE Cell Association Management + +The RRC maintains a per-UE association with serving cells, tracking which cells +a UE is currently using. This replaces the previous single-cell assumption and +enables proper multi-cell support where each UE can have multiple serving cells +(one PCell and up to 31 SCells). The servCellIndex (TS 38.331) is tracked per-UE +in the `ue_serving_cell_t` structure in the UE context (the same cell can have +different servCellIndex values for different UEs). + +Data Structures: +- `ue_serving_cell_t`: Stores serving cell information (nci, servCellIndex, assoc_id) +- `gNB_RRC_UE_t.serving_cells`: Dynamic array (seq_arr_t) of serving cell entries. PCell is always at index 0. + +Key Functions: +- `rrc_add_ue_serving_cell()` - Adds a new serving cell to UE's serving_cells array. +- `rrc_get_ue_serving_cell_by_id()` - Retrieves serving cell entry by servCellIndex. +- `ue_get_pcell_entry()` - Returns the PCell serving cell entry (first element in serving_cells). +- `rrc_remove_ue_scells_from_du()` - Removes all serving cells belonging to a specific DU via assoc_id (e.g. during handover or DU disconnection). + +##### Handover and Cell Association Updates + +During handover, the UE's serving cell list is updated so the PCell reflects the target cell and source-DU cells are removed. + +###### F1 handover (inter-DU, same CU-CP) + +Cell association is updated when the **source** DU sends F1AP UE Context Modification Response (after it has sent the RRC Reconfiguration to the UE). The CU-CP is a single RRC instance; both source DU and target DU are under the same CU. + +```mermaid +sequenceDiagram + participant SourceDU as Source DU + participant CU as CU-CP + + Note over CU,CU: F1 handover: cell association update on Context Modification Response + SourceDU->>CU: F1AP UE Context Modification Response + Note over CU: rrc_CU_process_ue_context_modification_response() + Note over CU: Check: ho_context && source && target (F1 HO) + + alt F1 handover in progress + CU->>CU: nr_rrc_apply_target_context(UE) + Note over CU: F1 UE data: du_assoc_id = target DU, secondary_ue = target DU UE ID, RNTI = target RNTI + CU->>CU: nr_rrc_update_cell_assoc_after_ho(rrc, UE) + Note over CU: F1 branch (ho_context->source present): + CU->>CU: rrc_remove_ue_scells_from_du(UE, source_ctx->cell->assoc_id) + Note over CU: Remove all serving cells (incl. PCell) belonging to source DU + CU->>CU: rrc_add_ue_serving_cell(UE, target_ctx->cell, RRC_PCELL_INDEX) + Note over CU: Handover complete: PCell = target cell, source DU cells removed + end +``` + +###### N2 handover (inter-gNB: source CU vs target CU) + +Cell association is updated only on the target CU-CP, when the target DU sends F1AP UE Context Setup Response. The UE context was created for handover, so there is no existing serving cell to remove. The flow is triggered inside the handover request acknowledge callback. + +- Target CU-CP: Receives HANDOVER REQUEST, sets up bearer and F1 UE context on target DU; when the target DU sends F1AP UE Context Setup Response, the target CU runs the cell-association update and then sends HANDOVER REQUEST ACKNOWLEDGE. + +```mermaid +sequenceDiagram + participant TargetDU as Target DU + participant TargetCU as Target CU-CP + + Note over TargetCU: N2 handover: cell association update + Note over TargetCU: F1AP UE Context Setup Resp from target DU (in nr_rrc_n2_ho_acknowledge) + TargetDU->>TargetCU: F1AP UE Context Setup Response + Note over TargetCU: rrc_CU_process_ue_context_setup_response() then callback ho_req_ack() + Note over TargetCU: nr_rrc_n2_ho_acknowledge(rrc, UE) + + TargetCU->>TargetCU: nr_rrc_apply_target_context(UE) + Note over TargetCU: F1 UE data: du_assoc_id = target DU, secondary_ue = target DU UE ID, RNTI = target RNTI + TargetCU->>TargetCU: nr_rrc_update_cell_assoc_after_ho(rrc, UE) + Note over TargetCU: N2 branch (ho_context->source NULL): no SCells to remove + TargetCU->>TargetCU: rrc_add_ue_serving_cell(UE, target_ctx->cell, RRC_PCELL_INDEX) + Note over TargetCU: Then: encode Handover Command, send NGAP HANDOVER REQUEST ACKNOWLEDGE +``` + +## UE Context + +UE context information is stored in `gNB_RRC_UE_t`, which includes: +- Serving cells tracking: Dynamic array of serving cells (PCell + SCells) +- Security context: Keys, algorithms, and security state +- Radio bearers: SRB and DRB configurations +- PDU sessions: Active PDU session information +- Handover context: Temporary data during handover procedures ### CU-UPs diff --git a/doc/RRC/rrc-usage.md b/doc/RRC/rrc-usage.md index bae31e864a..0794d8b804 100644 --- a/doc/RRC/rrc-usage.md +++ b/doc/RRC/rrc-usage.md @@ -41,15 +41,19 @@ For each DU, it prints: ``` 1 connected DUs -[1] DU ID 3584 (gNB-OAI-DU) assoc_id 8: nrCellID 12345678, PCI 0, SSB ARFCN 641280 - TDD: band 78 ARFCN 640008 SCS 30 (kHz) PRB 106 +[1] DU ID 3584 (gNB-OAI) integrated DU-CU: 1 cell + [1] nrCellID 12345678, PCI 0, Mode TDD, SSB ARFCN 641280 + TDD: band 78 ARFCN 640008 SCS 30 (kHz) PRB 106 ``` -i.e., an index (`[1]`), the DU ID and it's name, the SCTP association ID -(`assoc_id 8`, cf. UE information), and DU specific information for the cell -(cell ID, physical cell identity/PCI, the SSB frequency in ARFCN notation, the -band and Point A ARFCN, subcarrier spacing/SCS, and the number of resource -blocks/PRB). Only one cell per DU is supported. +The output shows: +- Total number of connected DUs +- For each DU: an index (`[1]`), the DU ID and name, connection type (either `integrated DU-CU` for monolithic deployments or `assoc_id X` for split deployments), and the number of cells served by this DU +- For each cell served by the DU: a cell index (`[1]`), the NR Cell ID, Physical Cell ID (PCI), operating mode (TDD or FDD), and SSB ARFCN +- Cell-specific frequency information: for TDD mode, the band, Point A ARFCN, subcarrier spacing (SCS), and number of resource blocks (PRB); for FDD mode, separate DL and UL frequency information + +The RRC enables the support of multiple cells per DU (though currently each DU +typically serves one cell in practice). As of now, it does not print information about connected CU-UPs or AMFs. @@ -84,7 +88,7 @@ the [MAC configuration](../MAC/mac-usage.md) as well for SIB configuration. `0xffffff` is a reserved value and means "no SD" Note that: SST=1, no SD is "eMBB"; SST=2, no SD is "URLLC"; SST=3, no SD is "mMTC" -- `enable_sdap` (default: false): enable the use of the SDAP layer. If +- `enable_sdap` (default: true): enable the use of the SDAP layer. If deactivated, a transparent SDAP header is prepended to packets, but no further processing is being done. - `cu_sibs` (default: `[]`) list of SIBs to give to the DU for transmission. @@ -94,8 +98,6 @@ the [MAC configuration](../MAC/mac-usage.md) as well for SIB configuration. - `um_on_default_drb` (default: false): use RLC UM instead of RLC AM on default bearers -- `drbs` (default: 0): the number of DRBs to allocate for a UE, only useful for - do-ra or phy-test testing #### Neighbor-gNB configuration diff --git a/doc/RUNMODEM.md b/doc/RUNMODEM.md index 1515172b24..b747775860 100644 --- a/doc/RUNMODEM.md +++ b/doc/RUNMODEM.md @@ -544,7 +544,7 @@ sudo ./nr-uesoftmodem --do-ra #### Run OAI with SDAP & Custom DRBs -To run OAI gNB with SDAP, simply include `--gNBs.[0].enable_sdap 1` to the binary's arguments. +SDAP is enabled by default. To disable SDAP, include `--gNBs.[0].enable_sdap 0` to the binary's arguments. The DRB creation is dependent on the 5QI. If the 5QI corresponds to a GBR Flow it assigns a dedicated data radio bearer. diff --git a/doc/TESTBenches.md b/doc/TESTBenches.md index 42558449b3..f91321012c 100644 --- a/doc/TESTBenches.md +++ b/doc/TESTBenches.md @@ -220,7 +220,7 @@ information on how the images are built. ~5G-NR - 5G-NR SA test setup: OAI VNF + PNF/NVIDIA CUBB on gracehopper1-oai + Foxconn RU, up2 + COTS UE (Quectel RM520N), OAI CN5G - container images for gNB deployment -- [RAN-SA-2x2-Module-CN5G](https://jenkins-oai.eurecom.fr/view/RAN/job/RAN-SA-2x2-Module-CN5G/) +- [RAN-SA-Multi-Antenna-CN5G](https://jenkins-oai.eurecom.fr/view/RAN/job/RAN-SA-Multi-Antenna-CN5G/) ~5G-NR - matix + N310 (gNB), up2 + COTS UE (Quectel RM520N), OAI 5GC deployed in docker on matix - NR performance tests: 2x2 configuration, 60 MHz and 100 MHz bandwidth @@ -234,6 +234,10 @@ information on how the images are built. - groot (CU+DU0) + B210, rocket (DU1) + B210, raspix (Quectel RM520N UE), OAI CN5G - OpenShift cluster for CN deployment - Attenuator (mini circuits RC4DAT-6G-60) - controlled from rocket +- [RAN-Channel-Simulation](https://jenkins-oai.eurecom.fr/job/RAN-Channel-Simulation/) + ~5G-NR + - gracehopper1-oai + - run channel simulation on CPU and GPU using test_channel_scalability ### RAN-CI-NSA-Trigger diff --git a/doc/analog_beamforming.md b/doc/analog_beamforming.md index b71b6376c3..bb0e7a1009 100644 --- a/doc/analog_beamforming.md +++ b/doc/analog_beamforming.md @@ -19,12 +19,12 @@ Analog beamforming implementation also allows to enable distributed antenna syst A set of parameters in configuration files controls the implementation of analog beamforming and instructs the scheduler on how to behave in such scenarios. Since most notably this technique in 5G is employed in FR2, the configuration file example currently available is a RFsim one for band 261. [Config file example](../ci-scripts/conf_files/gnb.sa.band257.u3.66prb.rfsim.conf) In the `MACRLC` section of configuration files, there are three new parameters: `set_analog_beamforming`, `beam_duration` and `beams_per_period`. The explanation of these parameters is here provided: -- `set_analog_beamforming` can be set a value equal to 1 or 2 to activate or 0 to desactivate analog beamforming (default value is 0) +- `set_analog_beamforming` can be set to "none", "preconfigured" or "lophy" (default value is "none") - `beam_duration` is the number of slots (currently minimum duration of a beam) the scheduler is tied to a beam (default value is 1) - `beams_per_period` is the number of concurrent beams the RU can handle in the beam duration (default value is 1) - `beam_weights` is a vector field containing the set of beam indices to be provided by the OAI L1 to the RU is also required. In current implementation, the number of beam indices should be equal to the number of SSBs transmitted -Setting analog beamforming to 1 or 2 changes the way FAPI beam index is treated. By setting 1, we instruct L1 to look up in Hi-PHY preconfigured DBM beam index. By setting 2, we instruct L2 to directly signal to Lo-PHY the beam index (e.g. over 7.2x fronthaul). +Setting analog beamforming to "preconfigured" or "lophy" changes the way FAPI beam index is treated. By setting "preconfigured", we instruct L1 to look up in Hi-PHY preconfigured DBM beam index. By setting "lophy", we instruct L2 to directly signal to Lo-PHY the beam index (e.g. over 7.2x fronthaul). DAS is enabled by setting to 1 the parameter `enable_das` in the L1 section of the configuration file. In case of DAS enabled, the field `beam_weights` in `MACRLC` section can be omitted and the number of beams per period equals the total number of beams. diff --git a/docker/Dockerfile.base.ubuntu b/docker/Dockerfile.base.ubuntu index 3d7e162544..b0eb5c4d32 100644 --- a/docker/Dockerfile.base.ubuntu +++ b/docker/Dockerfile.base.ubuntu @@ -86,4 +86,4 @@ COPY oaienv . RUN /bin/sh oaienv && \ cd cmake_targets && \ mkdir -p log && \ - ./build_oai -I -w USRP --install-optional-packages + ./build_oai -I -w USRP -w BLADERF --install-optional-packages diff --git a/docker/Dockerfile.build.ubuntu b/docker/Dockerfile.build.ubuntu index 9ccd921043..bc2ed577a6 100644 --- a/docker/Dockerfile.build.ubuntu +++ b/docker/Dockerfile.build.ubuntu @@ -42,22 +42,15 @@ RUN \ # Mount CPM package cache --mount=type=cache,target=/root/.cache/cpm/ \ /bin/sh oaienv && \ - cd cmake_targets && \ - mkdir -p log && \ - ./build_oai -c --ninja \ - --eNB --gNB --RU --UE --nrUE \ - --build-lib "telnetsrv enbscope uescope nrscope" \ - -w USRP -t Ethernet \ - --build-e2 --cmake-opt -DKPM_VERSION=$KPM_VERSION --cmake-opt -DE2AP_VERSION=$E2AP_VERSION \ - --cmake-opt -DOAI_VRTSIM_TAPS_CLIENT=ON \ - --noavx512 \ - --build-tool-opt -k10 \ - --cmake-opt -DCMAKE_C_FLAGS="-Werror" --cmake-opt -DCMAKE_CXX_FLAGS="-Werror" $BUILD_OPTION && \ - # Mainly to see if the sanitize option was perfectly executed - echo "---- ldd on executables ----" && \ - ldd ran_build/build/*softmodem* ran_build/build/nr-cuup ran_build/build/oairu && \ - echo "---- ldd on shared libraries ----" && \ - ldd ran_build/build/*.so + mkdir -p cmake_targets/ran_build/build && \ + cd cmake_targets/ran_build/build && \ + cmake ../../../ -GNinja \ + -DENABLE_TELNETSRV=ON -DENABLE_ENBSCOPE=ON -DENABLE_UESCOPE=ON -DENABLE_NRSCOPE=ON \ + -DOAI_USRP=ON -DOAI_BLADERF=ON -DOAI_ETHERNET=ON -DOAI_VRTSIM_TAPS_CLIENT=ON \ + -DE2_AGENT=ON -DKPM_VERSION=KPM_V3_00 -DE2AP_VERSION=E2AP_V3 \ + -DAVX512=OFF && \ + # -DCMAKE_C_FLAGS=-Werror -DCMAKE_CXX_FLAGS=-Werror && \ + ninja ## Build FlexRIC for SM Models RUN \ diff --git a/executables/CMakeLists.txt b/executables/CMakeLists.txt new file mode 100644 index 0000000000..d3b925bb02 --- /dev/null +++ b/executables/CMakeLists.txt @@ -0,0 +1,2 @@ +add_library(softmodem_common STATIC softmodem-common.c) +target_link_libraries(softmodem_common PUBLIC shlib_loader) diff --git a/executables/main_nr_ru.c b/executables/main_nr_ru.c index d8f2a02235..1aab4e95f6 100644 --- a/executables/main_nr_ru.c +++ b/executables/main_nr_ru.c @@ -33,8 +33,6 @@ #include "PHY/phy_extern.h" #include "PHY/TOOLS/phy_scope_interface.h" #include "common/utils/LOG/log.h" -#include "common/utils/LOG/vcd_signal_dumper.h" -// #include "PHY/INIT/phy_init.h" #include "openair2/ENB_APP/enb_paramdef.h" #include "system.h" #include "nfapi/oai_integration/vendor_ext.h" diff --git a/executables/nr-ru.c b/executables/nr-ru.c index f8d44ff4c0..397f940745 100644 --- a/executables/nr-ru.c +++ b/executables/nr-ru.c @@ -48,7 +48,6 @@ #include "common/utils/LOG/log.h" #include "common/utils/time_manager/time_manager.h" -#include "common/utils/LOG/vcd_signal_dumper.h" #include /* these variables have to be defined before including ENB_APP/enb_paramdef.h and GNB_APP/gnb_paramdef.h */ @@ -244,9 +243,8 @@ int connect_rau(RU_t *ru) { /* Southbound Fronthaul functions, RCC/RAU */ // southbound IF5 fronthaul for 16-bit OAI format -void fh_if5_south_out(RU_t *ru, int frame, int slot, uint64_t timestamp) { - if (ru == RC.ru[0]) - VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_TRX_TST, ru->proc.timestamp_tx & 0xffffffff); +void fh_if5_south_out(RU_t *ru, int frame, int slot, uint64_t timestamp) +{ int offset = get_samples_slot_timestamp(ru->nr_frame_parms, slot); void *buffs[ru->nb_tx]; for (int aid = 0; aid < ru->nb_tx; aid++) @@ -266,10 +264,8 @@ void fh_if5_south_out(RU_t *ru, int frame, int slot, uint64_t timestamp) { } // southbound IF4p5 fronthaul -void fh_if4p5_south_out(RU_t *ru, int frame, int slot, uint64_t timestamp) { - if (ru == RC.ru[0]) - VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_TRX_TST, ru->proc.timestamp_tx & 0xffffffff); - +void fh_if4p5_south_out(RU_t *ru, int frame, int slot, uint64_t timestamp) +{ LOG_D(PHY,"Sending IF4p5 for frame %d subframe %d\n",ru->proc.frame_tx,ru->proc.tti_tx); if ((nr_slot_select(&ru->config, ru->proc.frame_tx, ru->proc.tti_tx) & NR_DOWNLINK_SLOT) > 0) @@ -281,12 +277,10 @@ void fh_if4p5_south_out(RU_t *ru, int frame, int slot, uint64_t timestamp) { // Synchronous if5 from south -void fh_if5_south_in(RU_t *ru, - int *frame, - int *tti) { +void fh_if5_south_in(RU_t *ru, int *frame, int *tti) +{ NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms; RU_proc_t *proc = &ru->proc; - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_RECV_IF5, 1 ); start_meas(&ru->rx_fhaul); ru->ifdevice.trx_read_func2(&ru->ifdevice, &proc->timestamp_rx, NULL, get_samples_per_slot(*tti, fp)); @@ -339,9 +333,6 @@ void fh_if5_south_in(RU_t *ru, proc->first_rx, ru->rx_fhaul.p_time / (cpu_freq_GHz * 1000.0), rxmeas.tv_nsec); - VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TRX_TS, proc->timestamp_rx&0xffffffff ); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_RECV_IF5, 0 ); - } // Synchronous if4p5 from south @@ -398,15 +389,7 @@ void fh_if4p5_south_in(RU_t *ru, *slot = proc->tti_rx; } - if (ru == RC.ru[0]) { - VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_RX0_RU, f ); - VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TTI_NUMBER_RX0_RU, sl); - VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_TX0_RU, proc->frame_tx ); - VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TTI_NUMBER_TX0_RU, proc->tti_tx ); - } - proc->symbol_mask[proc->tti_rx] = 0; - VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TRX_TS, proc->timestamp_rx&0xffffffff ); LOG_D(PHY,"RU %d: fh_if4p5_south_in sleeping ...\n",ru->idx); } @@ -448,7 +431,8 @@ void fh_if4p5_south_asynch_in(RU_t *ru,int *frame,int *slot) { // RRU IF4p5 TX fronthaul receiver. Assumes an if_device on input and if or rf device on output // receives one subframe's worth of IF4p5 OFDM symbols and OFDM modulates -void fh_if4p5_north_in(RU_t *ru,int *frame,int *slot) { +void fh_if4p5_north_in(RU_t *ru,int *frame,int *slot) +{ uint32_t symbol_number=0; uint32_t symbol_mask, symbol_mask_full; uint16_t packet_type; @@ -461,12 +445,6 @@ void fh_if4p5_north_in(RU_t *ru,int *frame,int *slot) { recv_IF4p5(ru, frame, slot, &packet_type, &symbol_number); symbol_mask = symbol_mask | (1<frame_tx_unwrap) * fp->samples_per_subframe * 10 + get_samples_slot_timestamp(fp, slot_tx); LOG_D(PHY, "RU %d/%d TST %lu, frame %d, subframe %d\n", ru->idx, 0, proc->timestamp_tx, frame_tx, slot_tx); - // dump VCD output for first RU in list - if (ru == RC.ru[0]) { - VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_TX0_RU, frame_tx ); - VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TTI_NUMBER_TX0_RU, slot_tx ); - } - if (ru->feptx_ofdm) ru->feptx_ofdm(ru, frame_tx, slot_tx); @@ -538,18 +510,16 @@ void fh_if4p5_north_asynch_in(RU_t *ru,int *frame,int *slot) { ru->fh_south_out(ru, frame_tx, slot_tx, proc->timestamp_tx); } -void fh_if5_north_out(RU_t *ru) { +void fh_if5_north_out(RU_t *ru) +{ /// **** send_IF5 of rxdata to BBU **** /// - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_SEND_IF5, 1 ); AssertFatal(1 == 0, "Shouldn't get here\n"); } // RRU IF4p5 northbound interface (RX) -void fh_if4p5_north_out(RU_t *ru) { +void fh_if4p5_north_out(RU_t *ru) +{ RU_proc_t *proc=&ru->proc; - if (ru->idx == 0) - VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_TTI_NUMBER_RX0_RU, proc->tti_rx); - start_meas(&ru->tx_fhaul); send_IF4p5(ru, proc->frame_rx, proc->tti_rx, IF4p5_PULFFT); stop_meas(&ru->tx_fhaul); @@ -569,15 +539,12 @@ static void rx_rf(RU_t *ru, int *frame, int *slot) for (int i = 0; i < nb; i++) rxp[i] = (void *)&ru->common.rxdata[i][get_samples_slot_timestamp(fp, *slot)]; - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_READ, 1); openair0_timestamp_t old_ts = proc->timestamp_rx; LOG_D(PHY,"Reading %d samples for slot %d (%p)\n", samples_per_slot, *slot, rxp[0]); openair0_timestamp_t ts; unsigned int rxs; rxs = ru->rfdevice.trx_read_func(&ru->rfdevice, &ts, rxp, samples_per_slot, nb); - - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_READ, 0 ); proc->timestamp_rx = ts-ru->ts_offset; if (rxs != samples_per_slot) @@ -617,12 +584,6 @@ static void rx_rf(RU_t *ru, int *frame, int *slot) proc->tti_tx, fp->slots_per_frame); - // dump VCD output for first RU in list - if (ru == RC.ru[0]) { - VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_RX0_RU, proc->frame_rx ); - VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TTI_NUMBER_RX0_RU, proc->tti_rx ); - } - if (proc->first_rx == 0) { if (proc->tti_rx != *slot) { LOG_E(PHY, @@ -653,8 +614,6 @@ static void rx_rf(RU_t *ru, int *frame, int *slot) metadata mt = {.slot = *slot, .frame = *frame}; gNBscopeCopyWithMetadata(ru, gNbTimeDomainSamples, rxp[0], sizeof(c16_t), 1, samples_per_slot, 0, &mt); - VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TRX_TS, (proc->timestamp_rx+ru->ts_offset)&0xffffffff ); - if (rxs != samples_per_slot) { //exit_fun( "problem receiving samples" ); LOG_E(PHY, "problem receiving samples\n"); @@ -773,17 +732,11 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp) const int flags = flags_burst | (flags_gpio << 4); proc->first_tx = 0; - VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_TRX_WRITE_FLAGS, flags); - VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_TX0_RU, frame); - VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_TTI_NUMBER_TX0_RU, slot); - int nt = ru->nb_tx * ru->num_beams_period; void *txp[nt]; for (int i = 0; i < nt; i++) txp[i] = (void *)&ru->common.txdata[i][get_samples_slot_timestamp(fp, slot)] - sf_extension * sizeof(int32_t); - VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_TRX_TST, (timestamp + ru->ts_offset) & 0xffffffff); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_WRITE, 1); // prepare tx buffer pointers uint32_t txs = ru->rfdevice.trx_write_func(&ru->rfdevice, timestamp + ru->ts_offset - sf_extension, @@ -804,7 +757,6 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp) siglen + sf_extension, txs, 10 * log10((double)signal_energy(txp[0], siglen + sf_extension))); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_WRITE, 0); } static void fill_rf_config(RU_t *ru, char *rf_config_file) @@ -1208,10 +1160,8 @@ void *ru_thread(void *param) // Do PRACH RU processing prach_item_t *p = find_nr_prach(&gNB->prach_list, proc->frame_rx, proc->tti_rx, SEARCH_EXIST); if (p) { - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_RU_PRACH_RX, 1 ); // need to extract RACH data for lqter processing by rx_nr_prach() rx_nr_prach_ru(p, ru->common.rxdata, ru->nr_frame_parms, ru->N_TA_offset); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_RU_PRACH_RX, 0); } // end if (prach_id >= 0) } // end if (ru->feprx) } // end if (slot_type == NR_UPLINK_SLOT || slot_type == NR_MIXED_SLOT) { diff --git a/executables/nr-uesoftmodem.c b/executables/nr-uesoftmodem.c index a1dcaed52c..a0e06850b7 100644 --- a/executables/nr-uesoftmodem.c +++ b/executables/nr-uesoftmodem.c @@ -60,7 +60,6 @@ unsigned short config_frames[4] = {2,9,11,13}; #endif #include "common/utils/LOG/log.h" #include "common/utils/time_manager/time_manager.h" -#include "common/utils/LOG/vcd_signal_dumper.h" #include "UTIL/OPT/opt.h" #include "LAYER2/nr_pdcp/nr_pdcp_oai_api.h" @@ -166,8 +165,6 @@ static void get_options(configmodule_interface_t *cfg) paramdef_t cmdline_params[] = CMDLINE_NRUEPARAMS_DESC; int numparams = sizeofArray(cmdline_params); config_get(cfg, cmdline_params, numparams, NULL); - if (nrUE_params.vcdflag > 0) - ouput_vcd = 1; AssertFatal(nrUE_params.extra_pdu_id == -1, "Add additional PDU sessions in uicc.pdu_sessions array instead\n"); } @@ -285,9 +282,6 @@ int main(int argc, char **argv) int ret_loader = load_nrLDPC_coding_interface(NULL, &nrLDPC_coding_interface); AssertFatal(ret_loader == 0, "error loading LDPC library\n"); - if (ouput_vcd) { - vcd_signal_dumper_init("/tmp/openair_dump_nrUE.vcd"); - } // strdup to put the sring in the core file for post mortem identification char *pckg = strdup(OAI_PACKAGE_VERSION); LOG_I(HW, "Version: %s\n", pckg); @@ -479,9 +473,6 @@ int main(int argc, char **argv) LOG_W(NR_PHY, "Returned from ITTI signal handler\n"); oai_exit = 1; - if (ouput_vcd) - vcd_signal_dumper_close(); - if (PHY_vars_UE_g && PHY_vars_UE_g[0]) { for (int CC_id = 0; CC_id < MAX_NUM_CCs; CC_id++) { PHY_VARS_NR_UE *phy_vars = PHY_vars_UE_g[0][CC_id]; diff --git a/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h b/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h index 10e6b7c14a..fcd1968c25 100644 --- a/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h +++ b/nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h @@ -716,7 +716,13 @@ typedef enum { X(NFAPI_NR_PHY_API_MSG_TX_ERR, 0X8) #ifdef ENABLE_AERIAL +// Error codes obtained from Aerial L1 file +// cuPHY-CP/scfl2adapter/lib/scf_5g_fapi/scf_5g_fapi.h #define AERIAL_ERROR_LIST \ + X(SCF_ERROR_CODE_MSG_INVALID_PHY_ID, 0x9) \ + X(SCF_ERROR_CODE_MSG_UNINSTANTIATED_PHY, 0xA) \ + X(SCF_ERROR_CODE_MSG_INVALID_DFE_Profile, 0xB) \ + X(SCF_ERROR_CODE_MSG_PHY_PROFILE_SELECTION, 0xC) \ X(AERIAL_ERROR_CODE_FAPI_END, 0x32) \ X(AERIAL_ERROR_CODE_L1_PROC_OBJ_UNAVAILABLE_ERR, 0x33) \ X(AERIAL_ERROR_CODE_MSG_LATE_SLOT_ERR, 0x34) \ @@ -729,7 +735,26 @@ typedef enum { X(AERIAL_ERROR_CODE_L1_P1_EXIT_ERROR, 0x3B) \ X(AERIAL_ERROR_CODE_L1_P2_EXIT_ERROR, 0x3C) \ X(AERIAL_ERROR_CODE_L1_DL_CH_ERROR, 0x3D) \ - X(AERIAL_ERROR_CODE_L1_UL_CH_ERROR, 0x3E) + X(AERIAL_ERROR_CODE_L1_UL_CH_ERROR, 0x3E) \ + X(SCF_ERROR_CODE_EARLY_HARQ_TIMING_ERROR, 0x3F) \ + X(SCF_ERROR_CODE_SRS_CHEST_BUFF_BAD_STATE, 0x40) \ + X(SCF_ERROR_CODE_BEAM_ID_OUT_OF_RANGE, 0x41) \ + X(SCF_ERROR_CODE_PTP_SVC_ERROR, 0x42) \ + X(SCF_ERROR_CODE_PTP_SYNCED, 0x43) \ + X(SCF_ERROR_CODE_L1_MISSING_UL_IQ, 0x44) \ + X(SCF_ERROR_CODE_MSG_CAPACITY_EXCEEDED, 0x45) \ + X(SCF_ERROR_CODE_RHOCP_PTP_EVENTS_ERROR, 0x46) \ + X(SCF_ERROR_CODE_RHOCP_PTP_EVENTS_SYNCED, 0x47) \ + X(SCF_FAPI_SSB_PBCH_L1_LIMIT_EXCEEDED, 0x81) \ + X(SCF_FAPI_PDCCH_L1_LIMIT_EXCEEDED, 0x82) \ + X(SCF_FAPI_PDSCH_L1_LIMIT_EXCEEDED, 0x84) \ + X(SCF_FAPI_CSIRS_L1_LIMIT_EXCEEDED, 0x88) \ + X(SCF_FAPI_PUSCH_L1_LIMIT_EXCEEDED, 0xC1) \ + X(SCF_FAPI_PUCCH_L1_LIMIT_EXCEEDED, 0xC2) \ + X(SCF_FAPI_SRS_L1_LIMIT_EXCEEDED, 0xC4) \ + X(SCF_FAPI_PRACH_L1_LIMIT_EXCEEDED, 0xC8) \ + X(SCF_ERROR_CODE_RELEASED_HARQ_BUFFER_INFO, 0xD0) + #else #define AERIAL_ERROR_LIST #endif diff --git a/openair1/PHY/CODING/CMakeLists.txt b/openair1/PHY/CODING/CMakeLists.txt index f696c71213..cdfbbf6f6f 100644 --- a/openair1/PHY/CODING/CMakeLists.txt +++ b/openair1/PHY/CODING/CMakeLists.txt @@ -66,5 +66,9 @@ if (ENABLE_LDPC_CUDA) add_dependencies(nr_dlschsim ldpc_cuda) endif() +add_library(crc_byte OBJECT crc_byte.c) + add_subdirectory(nrLDPC_coding) add_subdirectory(nrLDPC_decoder) +add_subdirectory(nrPolar_tools) +add_subdirectory(nrSmallBlock) diff --git a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/nrLDPC_coding_aal.c b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/nrLDPC_coding_aal.c index 31ca0b387b..89f16bb706 100644 --- a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/nrLDPC_coding_aal.c +++ b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/nrLDPC_coding_aal.c @@ -5,6 +5,7 @@ #include "../nrLDPC_coding_interface.h" #include "nrLDPC_coding_aal.h" #include "PHY/sse_intrin.h" +#include "reverse_bits.h" #include #define NR_LDPC_ENABLE_PARITY_CHECK diff --git a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_segment/nrLDPC_coding_segment_decoder.c b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_segment/nrLDPC_coding_segment_decoder.c index 374ecb4b19..00f1787859 100644 --- a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_segment/nrLDPC_coding_segment_decoder.c +++ b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_segment/nrLDPC_coding_segment_decoder.c @@ -37,7 +37,6 @@ #include "PHY/NR_TRANSPORT/nr_dlsch.h" #include "SCHED_NR/sched_nr.h" #include "defs.h" -#include "common/utils/LOG/vcd_signal_dumper.h" #include "common/utils/LOG/log.h" #include diff --git a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_segment/nrLDPC_coding_segment_encoder.c b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_segment/nrLDPC_coding_segment_encoder.c index 5d0e90e46b..c0cb6af49f 100644 --- a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_segment/nrLDPC_coding_segment_encoder.c +++ b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_segment/nrLDPC_coding_segment_encoder.c @@ -34,7 +34,6 @@ #include "PHY/NR_TRANSPORT/nr_transport_common_proto.h" #include "PHY/NR_TRANSPORT/nr_dlsch.h" #include "SCHED_NR/sched_nr.h" -#include "common/utils/LOG/vcd_signal_dumper.h" #include "common/utils/LOG/log.h" #include "common/utils/nr/nr_common.h" #include diff --git a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_xdma/nrLDPC_coding_xdma.c b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_xdma/nrLDPC_coding_xdma.c index 8aa1a95cbe..7a13087610 100644 --- a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_xdma/nrLDPC_coding_xdma.c +++ b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_xdma/nrLDPC_coding_xdma.c @@ -40,7 +40,6 @@ #include "PHY/defs_gNB.h" #include "SCHED_NR/sched_nr.h" #include "common/utils/LOG/log.h" -#include "common/utils/LOG/vcd_signal_dumper.h" #include "defs.h" // #define DEBUG_ULSCH_DECODING // #define gNB_DEBUG_TRACE diff --git a/openair1/PHY/CODING/nrPolar_tools/CMakeLists.txt b/openair1/PHY/CODING/nrPolar_tools/CMakeLists.txt new file mode 100644 index 0000000000..14e3e816e1 --- /dev/null +++ b/openair1/PHY/CODING/nrPolar_tools/CMakeLists.txt @@ -0,0 +1,15 @@ +add_library(polar STATIC + nr_polar_init.c + nr_bitwise_operations.c + nr_crc_byte.c + nr_polar_decoder.c + nr_polar_decoding_tools.c + nr_polar_encoder.c + nr_polar_interleaving_pattern.c + nr_polar_kernal_operation.c + nr_polar_kronecker_power_matrices.c + nr_polar_matrix_and_array.c + nr_polar_procedures.c + nr_polar_sequence_pattern.c +) +target_link_libraries(polar PRIVATE utils) diff --git a/openair1/PHY/CODING/nrPolar_tools/nr_polar_procedures.c b/openair1/PHY/CODING/nrPolar_tools/nr_polar_procedures.c index cf1df52ac0..506d185e89 100644 --- a/openair1/PHY/CODING/nrPolar_tools/nr_polar_procedures.c +++ b/openair1/PHY/CODING/nrPolar_tools/nr_polar_procedures.c @@ -30,8 +30,8 @@ * \warning */ -#include "common/utils/nr/nr_common.h" #include "PHY/CODING/nrPolar_tools/nr_polar_defs.h" +#include "reverse_bits.h" #include #include #include diff --git a/openair1/PHY/CODING/nrSmallBlock/CMakeLists.txt b/openair1/PHY/CODING/nrSmallBlock/CMakeLists.txt new file mode 100644 index 0000000000..63f0361c74 --- /dev/null +++ b/openair1/PHY/CODING/nrSmallBlock/CMakeLists.txt @@ -0,0 +1 @@ +add_library(smallblock STATIC encodeSmallBlock.c decodeSmallBlock.c) diff --git a/openair1/PHY/INIT/nr_init_ru.c b/openair1/PHY/INIT/nr_init_ru.c index d82ff3c919..787d05697c 100644 --- a/openair1/PHY/INIT/nr_init_ru.c +++ b/openair1/PHY/INIT/nr_init_ru.c @@ -22,7 +22,6 @@ #include "phy_init.h" #include "PHY/phy_extern.h" #include "SCHED/sched_common.h" -#include "common/utils/LOG/vcd_signal_dumper.h" #include "assertions.h" #include #include "openair1/PHY/defs_RU.h" diff --git a/openair1/PHY/INIT/nr_init_ue.c b/openair1/PHY/INIT/nr_init_ue.c index e5c8e975c9..7b21102bbf 100644 --- a/openair1/PHY/INIT/nr_init_ue.c +++ b/openair1/PHY/INIT/nr_init_ue.c @@ -22,7 +22,6 @@ #include "nr_phy_init.h" #include "openair1/PHY/defs_RU.h" #include "openair1/PHY/impl_defs_nr.h" -#include "common/utils/LOG/vcd_signal_dumper.h" #include "assertions.h" #include "PHY/MODULATION/nr_modulation.h" #include "PHY/NR_UE_TRANSPORT/nr_transport_ue.h" diff --git a/openair1/PHY/MODULATION/nr_modulation.c b/openair1/PHY/MODULATION/nr_modulation.c index eb4dc50bc9..562c43895b 100644 --- a/openair1/PHY/MODULATION/nr_modulation.c +++ b/openair1/PHY/MODULATION/nr_modulation.c @@ -760,6 +760,8 @@ static inline __attribute__((always_inline)) int16x8_t cmac0_prec128(int16x8_t x // int16x8_t xr = vuzp1q_s16(x, x); // even lanes int16x8_t xi = vuzp2q_s16(x, x); // odd lanes +#ifdef __ARM_FEATURE_QRDMX + // ARMv8.1-A: Use RDM instructions // real = ar*br - ai*bi (Q15 scaling via high-half doubling muls) int16x8_t real = vqdmulhq_s16(xr, wr); // ≈ round((2*xr*wr)/2^16) real = vqrdmlshq_s16(real, xi, wi); // real -= round((2*xi*wi)/2^16) @@ -767,7 +769,21 @@ static inline __attribute__((always_inline)) int16x8_t cmac0_prec128(int16x8_t x // imag = ar*bi + ai*br int16x8_t imag = vqdmulhq_s16(xr, wi); imag = vqrdmlahq_s16(imag, xi, wr); // imag += round((2*xi*wr)/2^16) - // +#else + // ARMv8.0-A fallback: Use standard 32-bit multiply + int32x4_t real_lo = vmull_s16(vget_low_s16(xr), vget_low_s16(wr)); + int32x4_t real_hi = vmull_s16(vget_high_s16(xr), vget_high_s16(wr)); + real_lo = vmlsl_s16(real_lo, vget_low_s16(xi), vget_low_s16(wi)); + real_hi = vmlsl_s16(real_hi, vget_high_s16(xi), vget_high_s16(wi)); + + int32x4_t imag_lo = vmull_s16(vget_low_s16(xr), vget_low_s16(wi)); + int32x4_t imag_hi = vmull_s16(vget_high_s16(xr), vget_high_s16(wi)); + imag_lo = vmlal_s16(imag_lo, vget_low_s16(xi), vget_low_s16(wr)); + imag_hi = vmlal_s16(imag_hi, vget_high_s16(xi), vget_high_s16(wr)); + + int16x8_t real = vcombine_s16(vqrshrn_n_s32(real_lo, 15), vqrshrn_n_s32(real_hi, 15)); + int16x8_t imag = vcombine_s16(vqrshrn_n_s32(imag_lo, 15), vqrshrn_n_s32(imag_hi, 15)); +#endif // Re-interleave [real, imag] int16x8x2_t produ = vzipq_s16(real, imag); return produ.val[0]; diff --git a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c index f9203f488f..d4432ef308 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c +++ b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c @@ -992,17 +992,8 @@ int nr_srs_channel_estimation(int ant, // Compute wideband SNR on the symbol 0 int tot_subcarriers = m_SRS_b * NR_NB_SC_PER_RB; - uint16_t subcarrier_abs = first_subcarrier + nr_srs_info->k_0_p[p_index][0]; - if (subcarrier_abs + tot_subcarriers < ofdm_symbol_size_half) { - *signal_power = signal_energy_nodc(&srs_estimated_channel_freq[subcarrier_abs], tot_subcarriers) / tot_subcarriers; - } else { - int size1 = ofdm_symbol_size_half - subcarrier_abs; - int size2 = tot_subcarriers - size1; - uint64_t signal_power_p1 = signal_energy_nodc(&srs_estimated_channel_freq[subcarrier_abs], size1) * size1; - uint64_t signal_power_p2 = signal_energy_nodc(&srs_estimated_channel_freq[ofdm_symbol_size_half], size2) * size2; - *signal_power = (signal_power_p1 + signal_power_p2) / tot_subcarriers; - } + *signal_power = signal_energy_nodc(&srs_estimated_channel_freq[subcarrier_abs], tot_subcarriers); if (*signal_power == 0) { LOG_W(NR_PHY, "Received SRS signal power is 0\n"); diff --git a/openair1/PHY/NR_TRANSPORT/nr_dlsch.c b/openair1/PHY/NR_TRANSPORT/nr_dlsch.c index b8663aa435..9018aabfde 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_dlsch.c +++ b/openair1/PHY/NR_TRANSPORT/nr_dlsch.c @@ -36,7 +36,6 @@ #include "PHY/MODULATION/nr_modulation.h" #include "PHY/NR_REFSIG/dmrs_nr.h" #include "PHY/NR_REFSIG/ptrs_nr.h" -#include "common/utils/LOG/vcd_signal_dumper.h" #include "common/utils/nr/nr_common.h" #include "executables/softmodem-common.h" #include "SCHED_NR/sched_nr.h" @@ -628,7 +627,6 @@ static int do_one_dlsch(unsigned char *input_ptr, PHY_VARS_gNB *gNB, NR_gNB_DLSC /// Modulation start_meas(dlsch_modulation_stats); nr_modulation(scrambled_output, encoded_length, Qm, (int16_t *)mod_symbs[codeWord]); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_gNB_PDSCH_MODULATION, 0); stop_meas(dlsch_modulation_stats); #ifdef DEBUG_DLSCH printf("PDSCH Modulation: Qm %d(%d)\n", Qm, nb_re); diff --git a/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c b/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c index f74685ed71..71f5759396 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c +++ b/openair1/PHY/NR_TRANSPORT/nr_dlsch_coding.c @@ -33,7 +33,6 @@ #include "PHY/NR_TRANSPORT/nr_transport_common_proto.h" #include "PHY/NR_TRANSPORT/nr_dlsch.h" #include "SCHED_NR/sched_nr.h" -#include "common/utils/LOG/vcd_signal_dumper.h" #include "common/utils/LOG/log.h" #include "common/utils/nr/nr_common.h" #include @@ -118,8 +117,6 @@ int nr_dlsch_encoding(PHY_VARS_gNB *gNB, time_stats_t *dlsch_interleaving_stats, time_stats_t *dlsch_segmentation_stats) { - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_gNB_DLSCH_ENCODING, VCD_FUNCTION_IN); - nrLDPC_TB_encoding_parameters_t TBs[n_dlsch]; memset(TBs, 0, sizeof(TBs)); @@ -291,7 +288,5 @@ int nr_dlsch_encoding(PHY_VARS_gNB *gNB, // merge_meas(, &segment_parameters->ts_ldpc_encode); } } - - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_gNB_DLSCH_ENCODING, VCD_FUNCTION_OUT); return 0; } diff --git a/openair1/PHY/NR_TRANSPORT/nr_pbch.c b/openair1/PHY/NR_TRANSPORT/nr_pbch.c index c5edd4cfde..2aa2a113ad 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_pbch.c +++ b/openair1/PHY/NR_TRANSPORT/nr_pbch.c @@ -35,6 +35,7 @@ #include "PHY/NR_TRANSPORT/nr_transport_proto.h" #include "PHY/sse_intrin.h" #include "executables/softmodem-common.h" +#include "reverse_bits.h" #include "openair1/PHY/NR_REFSIG/nr_refsig_common.h" #include "openair1/PHY/NR_REFSIG/nr_mod_table.h" #include "openair1/PHY/TOOLS/tools_defs.h" diff --git a/openair1/PHY/NR_TRANSPORT/nr_prach_common.c b/openair1/PHY/NR_TRANSPORT/nr_prach_common.c index b680d826fa..7a7c64a048 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_prach_common.c +++ b/openair1/PHY/NR_TRANSPORT/nr_prach_common.c @@ -30,13 +30,11 @@ * \warning */ #include "PHY/sse_intrin.h" -#include "common/utils/LOG/vcd_signal_dumper.h" #include "PHY/impl_defs_nr.h" #include "PHY/defs_nr_UE.h" #include "PHY/NR_TRANSPORT/nr_prach.h" #include "PHY/NR_TRANSPORT/nr_transport_common_proto.h" #include "common/utils/LOG/log.h" -#include "common/utils/LOG/vcd_signal_dumper.h" #include "T.h" /* Extended Euclidean Algorithm to compute modulo inverse */ @@ -89,9 +87,6 @@ void compute_nr_prach_seq(uint8_t short_sequence, uint8_t num_sequences, uint8_t int N_ZC; const uint16_t* prach_root_sequence_map; - - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_UE_COMPUTE_PRACH, VCD_FUNCTION_IN); - LOG_D(PHY,"compute_prach_seq: prach short sequence %x, num_sequences %d, rootSequenceIndex %d\n", short_sequence, num_sequences, rootSequenceIndex); N_ZC = (short_sequence) ? NR_PRACH_SEQ_LEN_S : NR_PRACH_SEQ_LEN_L; @@ -132,6 +127,4 @@ void compute_nr_prach_seq(uint8_t short_sequence, uint8_t num_sequences, uint8_t X_u[i][k] = ru; } } - - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_UE_COMPUTE_PRACH, VCD_FUNCTION_OUT); } diff --git a/openair1/PHY/NR_TRANSPORT/nr_scrambling.c b/openair1/PHY/NR_TRANSPORT/nr_scrambling.c index 9577f943e4..5197242633 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_scrambling.c +++ b/openair1/PHY/NR_TRANSPORT/nr_scrambling.c @@ -21,7 +21,6 @@ #include "nr_transport_common_proto.h" #include "PHY/NR_REFSIG/nr_refsig.h" -#include "common/utils/LOG/vcd_signal_dumper.h" #define DEBUG_SCRAMBLING(a) //#define DEBUG_SCRAMBLING(a) a void nr_codeword_scrambling(uint8_t *in, diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c index a7d5206dd0..403847035c 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c @@ -36,7 +36,6 @@ #include "PHY/NR_TRANSPORT/nr_dlsch.h" #include "SCHED_NR/sched_nr.h" #include "defs.h" -#include "common/utils/LOG/vcd_signal_dumper.h" #include "common/utils/LOG/log.h" #include // #define DEBUG_ULSCH_DECODING @@ -115,8 +114,6 @@ int nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB, uint8_t *ULSCH_ids, int nb_pusch) { - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_gNB_ULSCH_DECODING, 1); - nrLDPC_TB_decoding_parameters_t TBs[nb_pusch]; memset(TBs, 0, sizeof(TBs)); nrLDPC_slot_decoding_parameters_t slot_parameters = {.frame = frame, diff --git a/openair1/PHY/NR_TRANSPORT/pucch_rx.c b/openair1/PHY/NR_TRANSPORT/pucch_rx.c index 8e2076df2f..61bdce06b2 100644 --- a/openair1/PHY/NR_TRANSPORT/pucch_rx.c +++ b/openair1/PHY/NR_TRANSPORT/pucch_rx.c @@ -46,10 +46,10 @@ #include "PHY/NR_TRANSPORT/nr_transport_proto.h" #include "PHY/NR_REFSIG/nr_refsig.h" #include "common/utils/LOG/log.h" -#include "common/utils/LOG/vcd_signal_dumper.h" #include "nfapi/oai_integration/vendor_ext.h" #include "nfapi/oai_integration/vendor_ext.h" #include "SCHED_NR/sched_nr.h" +#include "reverse_bits.h" #include "T.h" #include "nr_phy_common.h" diff --git a/openair1/PHY/NR_UE_ESTIMATION/nr_adjust_synch_ue.c b/openair1/PHY/NR_UE_ESTIMATION/nr_adjust_synch_ue.c index 7bcd8264f8..a407282c1b 100644 --- a/openair1/PHY/NR_UE_ESTIMATION/nr_adjust_synch_ue.c +++ b/openair1/PHY/NR_UE_ESTIMATION/nr_adjust_synch_ue.c @@ -25,7 +25,6 @@ #include "PHY/impl_defs_top.h" #include "executables/nr-uesoftmodem.h" -#include "common/utils/LOG/vcd_signal_dumper.h" //#define DEBUG_PHY @@ -44,8 +43,6 @@ int nr_adjust_synch_ue(NR_DL_FRAME_PARMS *frame_parms, { int max_val = 0, max_pos = 0; - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_ADJUST_SYNCH, VCD_FUNCTION_IN); - // search for maximum position within the cyclic prefix for (int i = -frame_parms->nb_prefix_samples; i < frame_parms->nb_prefix_samples; i++) { int temp = 0; @@ -95,6 +92,5 @@ int nr_adjust_synch_ue(NR_DL_FRAME_PARMS *frame_parms, ue->max_pos_iir += -round(sampleShift * PID_P) * 32768; ue->max_pos_acc += max_pos; - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_ADJUST_SYNCH, VCD_FUNCTION_OUT); return sample_shift; } diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_decoding.c b/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_decoding.c index be71e435a0..56b86dc4f5 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_decoding.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_decoding.c @@ -22,7 +22,6 @@ /*! \file PHY/NR_UE_TRANSPORT/nr_dlsch_decoding_slot.c */ -#include "common/utils/LOG/vcd_signal_dumper.h" #include "PHY/defs_nr_UE.h" #include "SCHED_NR_UE/harq_nr.h" #include "PHY/CODING/coding_extern.h" @@ -144,8 +143,6 @@ void nr_dlsch_decoding(PHY_VARS_NR_UE *phy_vars_ue, TB_parameters->Qm, Coderate); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_DLSCH_SEGMENTATION, VCD_FUNCTION_IN); - if (harq_process->first_rx == 1) { // This is a new packet, so compute quantities regarding segmentation nr_segmentation(NULL, @@ -180,8 +177,6 @@ void nr_dlsch_decoding(PHY_VARS_NR_UE *phy_vars_ue, } max_num_segments = max(max_num_segments, TB_parameters->C); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_DLSCH_SEGMENTATION, VCD_FUNCTION_OUT); - if (LOG_DEBUGFLAG(DEBUG_DLSCH_DECOD)) LOG_I(PHY, "Segmentation: C %d, K %d\n", harq_process->C, harq_process->K); diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_initial_sync.c b/openair1/PHY/NR_UE_TRANSPORT/nr_initial_sync.c index e747b86528..4b4b4b7be8 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_initial_sync.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_initial_sync.c @@ -35,7 +35,6 @@ #include "nr_transport_proto_ue.h" #include "PHY/NR_UE_ESTIMATION/nr_estimation.h" #include "SCHED_NR_UE/defs.h" -#include "common/utils/LOG/vcd_signal_dumper.h" #include "common/utils/nr/nr_common.h" #include "common_lib.h" @@ -483,7 +482,6 @@ nr_initial_sync_t nr_initial_sync(UE_nr_rxtx_proc_t *proc, if (ue->if_inst && ue->if_inst->dl_indication) ue->if_inst->dl_indication(&dl_indication); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_NR_INITIAL_UE_SYNC, VCD_FUNCTION_IN); LOG_D(PHY, "nr_initial sync ue RB_DL %d\n", fp->N_RB_DL); @@ -554,7 +552,6 @@ nr_initial_sync_t nr_initial_sync(UE_nr_rxtx_proc_t *proc, LOG_A(PHY, "Initial sync successful, PCI: %d\n", fp->Nid_cell); } // exit_fun("debug exit"); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_NR_INITIAL_UE_SYNC, VCD_FUNCTION_OUT); if (res) return res->syncRes; else diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_pbch.c b/openair1/PHY/NR_UE_TRANSPORT/nr_pbch.c index 8d3ac6db21..613e70be0d 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_pbch.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_pbch.c @@ -38,6 +38,7 @@ #include #include "openair1/PHY/NR_REFSIG/nr_refsig_common.h" #include "PHY/nr_phy_common/inc/nr_phy_common.h" +#include "reverse_bits.h" #include "instrumentation.h" //#define DEBUG_PBCH //#define DEBUG_PBCH_ENCODING diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_prach.c b/openair1/PHY/NR_UE_TRANSPORT/nr_prach.c index 05a251d999..d55b81e0c1 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_prach.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_prach.c @@ -30,15 +30,12 @@ * \warning */ #include "PHY/sse_intrin.h" -#include "common/utils/LOG/vcd_signal_dumper.h" - #include "PHY/impl_defs_nr.h" #include "PHY/defs_nr_UE.h" #include "PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h" #include "PHY/NR_TRANSPORT/nr_transport_common_proto.h" #include "common/utils/LOG/log.h" -#include "common/utils/LOG/vcd_signal_dumper.h" #include "T.h" diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c index d68ae6e9ae..029c1c5d38 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c @@ -31,7 +31,6 @@ #include "PHY/CODING/nrLDPC_coding/nrLDPC_coding_interface.h" #include "PHY/NR_UE_TRANSPORT/nr_transport_ue.h" #include "executables/nr-uesoftmodem.h" -#include "common/utils/LOG/vcd_signal_dumper.h" #include "PHY/log_tools.h" int nr_ulsch_pre_encoding(PHY_VARS_NR_UE *ue, @@ -137,7 +136,6 @@ int nr_ulsch_pre_encoding(PHY_VARS_NR_UE *ue, harq_process->BG = pusch_pdu->ldpcBaseGraph; - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_NR_SEGMENTATION, VCD_FUNCTION_IN); start_meas_nr_ue_phy(ue, ULSCH_SEGMENTATION_STATS); harq_process->Kb = nr_segmentation(harq_process->payload_AB, harq_process->c, @@ -152,7 +150,6 @@ int nr_ulsch_pre_encoding(PHY_VARS_NR_UE *ue, return (-1); } stop_meas_nr_ue_phy(ue, ULSCH_SEGMENTATION_STATS); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_NR_SEGMENTATION, VCD_FUNCTION_OUT); } // pusch_id return 0; } @@ -167,7 +164,6 @@ int nr_ulsch_encoding(PHY_VARS_NR_UE *ue, uint16_t number_dmrs_symbols) { start_meas_nr_ue_phy(ue, ULSCH_ENCODING_STATS); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_NR_UE_ULSCH_ENCODING, VCD_FUNCTION_IN); nrLDPC_TB_encoding_parameters_t TBs[nb_ulsch]; memset(TBs, 0, sizeof(TBs)); @@ -237,9 +233,6 @@ int nr_ulsch_encoding(PHY_VARS_NR_UE *ue, } // pusch_id ///////////////////////// | LDCP coding | //////////////////////////////////// - - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_LDPC_ENCODER_OPTIM, VCD_FUNCTION_IN); - ue->nrLDPC_coding_interface.nrLDPC_coding_encoder(&slot_parameters); for (uint8_t pusch_id = 0; pusch_id < nb_ulsch; pusch_id++) { @@ -252,9 +245,6 @@ int nr_ulsch_encoding(PHY_VARS_NR_UE *ue, } } - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_LDPC_ENCODER_OPTIM, VCD_FUNCTION_OUT); - - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_NR_UE_ULSCH_ENCODING, VCD_FUNCTION_OUT); stop_meas_nr_ue_phy(ue, ULSCH_ENCODING_STATS); return 0; } diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c index 6708cef338..804529ffbf 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c @@ -39,7 +39,6 @@ #include "PHY/MODULATION/modulation_common.h" #include "common/utils/assertions.h" #include "common/utils/nr/nr_common.h" -#include "common/utils/LOG/vcd_signal_dumper.h" #include "PHY/NR_TRANSPORT/nr_transport_common_proto.h" #include "PHY/NR_TRANSPORT/nr_sch_dmrs.h" #include "PHY/defs_nr_common.h" diff --git a/openair1/PHY/NR_UE_TRANSPORT/pss_nr.c b/openair1/PHY/NR_UE_TRANSPORT/pss_nr.c index 08ed65945c..e4b68a4d66 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/pss_nr.c +++ b/openair1/PHY/NR_UE_TRANSPORT/pss_nr.c @@ -39,7 +39,6 @@ #include "PHY/defs_nr_UE.h" #include "PHY/NR_REFSIG/ss_pbch_nr.h" -#include "common/utils/LOG/vcd_signal_dumper.h" #define DEFINE_VARIABLES_PSS_NR_H #include "PHY/NR_REFSIG/pss_nr.h" @@ -391,7 +390,6 @@ int pss_synchro_nr(const c16_t **rxdata, int *pssPeak, int *pssAvg) { - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PSS_SYNCHRO_NR, VCD_FUNCTION_IN); #ifdef DBG_PSS_NR LOG_M("rxdata0_rand.m","rxd0_rand", &PHY_vars_UE->common_vars.rxdata[0][0], frame_parms->samples_per_frame, 1, 1); @@ -406,13 +404,9 @@ int pss_synchro_nr(const c16_t **rxdata, start_meas(&generic_time[TIME_PSS]); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PSS_SEARCH_TIME_NR, VCD_FUNCTION_IN); - int synchro_position = pss_search_time_nr(rxdata, frame_parms, pssTime, fo_flag, is, target_Nid_cell, nid2, f_off, pssPeak, pssAvg, -1, -1); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PSS_SEARCH_TIME_NR, VCD_FUNCTION_OUT); - #if TEST_SYNCHRO_TIMING_PSS stop_meas(&generic_time[TIME_PSS]); @@ -444,7 +438,6 @@ int pss_synchro_nr(const c16_t **rxdata, } #endif - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PSS_SYNCHRO_NR, VCD_FUNCTION_OUT); return synchro_position; } diff --git a/openair1/PHY/NR_UE_TRANSPORT/pucch_nr.c b/openair1/PHY/NR_UE_TRANSPORT/pucch_nr.c index b6b20cefaf..51285edd0b 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/pucch_nr.c +++ b/openair1/PHY/NR_UE_TRANSPORT/pucch_nr.c @@ -39,7 +39,7 @@ #include "PHY/NR_TRANSPORT/nr_transport_common_proto.h" #include #include "common/utils/LOG/log.h" -#include "common/utils/LOG/vcd_signal_dumper.h" +#include "reverse_bits.h" #include "openair1/PHY/NR_REFSIG/nr_refsig.h" #include "T.h" diff --git a/openair1/PHY/NR_UE_TRANSPORT/pucch_nr.h b/openair1/PHY/NR_UE_TRANSPORT/pucch_nr.h index 1f24b589f4..7f769872de 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/pucch_nr.h +++ b/openair1/PHY/NR_UE_TRANSPORT/pucch_nr.h @@ -39,8 +39,6 @@ //#include "PHY/extern.h" #include "common/utils/LOG/log.h" -#include "common/utils/LOG/vcd_signal_dumper.h" - #include "T.h" #define ONE_OVER_SQRT2 23170 // 32767/sqrt(2) = 23170 (ONE_OVER_SQRT2) diff --git a/openair1/PHY/TOOLS/tests/CMakeLists.txt b/openair1/PHY/TOOLS/tests/CMakeLists.txt index 9a73f1ae8e..3750cacaa3 100644 --- a/openair1/PHY/TOOLS/tests/CMakeLists.txt +++ b/openair1/PHY/TOOLS/tests/CMakeLists.txt @@ -38,4 +38,37 @@ add_executable(test_sse_intrinsics test_sse_intrinsics.cpp) target_link_libraries(test_sse_intrinsics PRIVATE GTest::gtest LOG minimal_lib) add_dependencies(tests test_sse_intrinsics) add_test(NAME test_sse_intrinsics - COMMAND ./test_sse_intrinsics) \ No newline at end of file + COMMAND ./test_sse_intrinsics) + +if(CUDA_ENABLE) +add_executable(test_multipath test_multipath.c) +target_link_libraries(test_multipath PRIVATE UTIL SIMU LOG CONFIG_LIB shlib_loader m oai_cuda_lib) + +add_dependencies(tests test_multipath) +add_test(NAME test_multipath COMMAND test_multipath) + +add_executable(test_noise test_noise.c) +target_link_libraries(test_noise PRIVATE UTIL SIMU PHY_COMMON LOG CONFIG_LIB shlib_loader m oai_cuda_lib) + +add_dependencies(tests test_noise) +add_test(NAME test_noise COMMAND test_noise) + +add_executable(test_channel_simulation test_channel_simulation.c) +target_link_libraries(test_channel_simulation PRIVATE UTIL SIMU PHY_COMMON LOG CONFIG_LIB shlib_loader m oai_cuda_lib) + +add_dependencies(tests test_channel_simulation) +add_test(NAME test_channel_simulation COMMAND test_channel_simulation) + +add_executable(test_channel_scalability test_channel_scalability.c) +target_link_libraries(test_channel_scalability PRIVATE UTIL SIMU PHY_COMMON LOG CONFIG_LIB shlib_loader m oai_cuda_lib) + +add_dependencies(tests test_channel_scalability) +add_test(NAME test_channel_scalability COMMAND test_channel_scalability) + +if (TARGET oai_cuda_lib) + target_compile_definitions(test_multipath PRIVATE ENABLE_CUDA) + target_compile_definitions(test_noise PRIVATE ENABLE_CUDA) + target_compile_definitions(test_channel_simulation PRIVATE ENABLE_CUDA) + target_compile_definitions(test_channel_scalability PRIVATE ENABLE_CUDA) +endif() +endif() diff --git a/openair1/PHY/TOOLS/tests/test_channel_scalability.c b/openair1/PHY/TOOLS/tests/test_channel_scalability.c new file mode 100644 index 0000000000..ca95e8c02e --- /dev/null +++ b/openair1/PHY/TOOLS/tests/test_channel_scalability.c @@ -0,0 +1,645 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#include +#include +#include +#include +#include +#include + +#include "PHY/TOOLS/tools_defs.h" +#include "SIMULATION/TOOLS/sim.h" +#include "SIMULATION/TOOLS/oai_cuda.h" +#include "common/utils/LOG/log.h" +#include "common/utils/utils.h" +#include + +configmodule_interface_t *uniqCfg = NULL; + +void exit_function(const char *file, const char *function, const int line, const char *s, const int assert_not_exit) +{ + fprintf(stderr, "Exit function called from %s:%d in %s(). Message: %s\n", file, line, function, s); + exit(1); +} + +void generate_random_signal_interleaved(float **sig_interleaved, int nb_ant, int num_samples) +{ + for (int i = 0; i < nb_ant; i++) { + for (int j = 0; j < num_samples; j++) { + sig_interleaved[i][2 * j] = (float)((rand() % 2000) - 1000); // Real part (I) + sig_interleaved[i][2 * j + 1] = (float)((rand() % 2000) - 1000); // Imaginary part (Q) + } + } +} + +channel_desc_t *create_manual_channel_desc(int nb_tx, int nb_rx, int channel_length) +{ + channel_desc_t *desc = (channel_desc_t *)calloc(1, sizeof(channel_desc_t)); + desc->nb_tx = nb_tx; + desc->nb_rx = nb_rx; + desc->channel_length = channel_length; + desc->path_loss_dB = 0.0; + desc->channel_offset = 0; + int num_links = nb_tx * nb_rx; + float path_loss = (float)pow(10, desc->path_loss_dB / 20.0); + desc->ch = (struct complexd **)malloc(num_links * sizeof(struct complexd *)); + for (int i = 0; i < num_links; i++) { + desc->ch[i] = (struct complexd *)malloc(channel_length * sizeof(struct complexd)); + for (int l = 0; l < channel_length; l++) { + desc->ch[i][l].r = ((double)rand() / (double)RAND_MAX * 0.1) * path_loss; + desc->ch[i][l].i = ((double)rand() / (double)RAND_MAX * 0.1) * path_loss; + } + } + return desc; +} + +void free_manual_channel_desc(channel_desc_t *desc) +{ + if (!desc) + return; + int num_links = desc->nb_tx * desc->nb_rx; + for (int i = 0; i < num_links; i++) { + if (desc->ch[i]) + free(desc->ch[i]); + } + if (desc->ch) + free(desc->ch); + free(desc); +} + +int main(int argc, char **argv) +{ + logInit(); + randominit(0); + + int num_channels = 1; + int nb_tx = 4; + int nb_rx = 4; + int num_samples = 122880; + int channel_length = 32; + int num_trials = 50; + int sum_outputs = 0; + char mode_str[10] = "batch"; + + struct option long_options[] = {{"num-channels", required_argument, 0, 'c'}, + {"nb-tx", required_argument, 0, 't'}, + {"nb-rx", required_argument, 0, 'r'}, + {"num-samples", required_argument, 0, 's'}, + {"ch-len", required_argument, 0, 'l'}, + {"trials", required_argument, 0, 'n'}, + {"sum-outputs", no_argument, 0, 'S'}, + {"mode", required_argument, 0, 'm'}, + {"help", no_argument, 0, 'h'}, + {0, 0, 0, 0}}; + + int opt; + while ((opt = getopt_long(argc, argv, "c:t:r:s:l:n:Sm:h", long_options, NULL)) != -1) { + switch (opt) { + case 'c': + num_channels = atoi(optarg); + break; + case 't': + nb_tx = atoi(optarg); + break; + case 'r': + nb_rx = atoi(optarg); + break; + case 's': + num_samples = atoi(optarg); + break; + case 'l': + channel_length = atoi(optarg); + break; + case 'n': + num_trials = atoi(optarg); + break; + case 'S': + sum_outputs = 1; + break; + case 'm': + strncpy(mode_str, optarg, sizeof(mode_str) - 1); + break; + case 'h': + printf("Usage: %s [options]\n", argv[0]); + printf(" -c, --num-channels Number of parallel channels to simulate (Default: 1)\n"); + printf(" -t, --nb-tx Number of transmit antennas (Default: 4)\n"); + printf(" -r, --nb-rx Number of receive antennas (Default: 4)\n"); + printf(" -s, --num-samples Number of samples (Default: 30720)\n"); + printf(" -l, --ch-len Channel length (Default: 32)\n"); + printf(" -n, --trials Number of trials for averaging (Default: 50)\n"); + printf(" -S, --sum-outputs Enable summation of outputs for interference simulation.\n"); + printf(" -m, --mode GPU execution mode (Default: batch)\n"); + printf(" -h, --help Show this help message\n"); + return 0; + default: + exit(1); + } + } + + // --- MEMORY ALLOCATION --- + // HOST MEMORY + int num_tx_signals = sum_outputs ? num_channels : 1; + float *tx_sig_data = malloc(num_tx_signals * nb_tx * num_samples * 2 * sizeof(float)); + float ***tx_sig_interleaved = malloc(num_tx_signals * sizeof(float **)); + for (int i = 0; i < num_tx_signals; i++) { + tx_sig_interleaved[i] = malloc(nb_tx * sizeof(float *)); + for (int j = 0; j < nb_tx; j++) { + tx_sig_interleaved[i][j] = tx_sig_data + (i * nb_tx + j) * num_samples * 2; + } + } + + float *rx_multipath_data = malloc(nb_rx * num_samples * 2 * sizeof(float)); + float **rx_multipath_re_cpu = malloc(nb_rx * sizeof(float *)); + float **rx_multipath_im_cpu = malloc(nb_rx * sizeof(float *)); + for (int i = 0; i < nb_rx; i++) { + rx_multipath_re_cpu[i] = rx_multipath_data + i * num_samples; + rx_multipath_im_cpu[i] = rx_multipath_data + (nb_rx + i) * num_samples; + } + + c16_t *output_cpu_data = malloc(num_channels * nb_rx * num_samples * sizeof(c16_t)); + c16_t ***output_cpu = malloc(num_channels * sizeof(c16_t **)); + for (int c = 0; c < num_channels; c++) { + output_cpu[c] = malloc(nb_rx * sizeof(c16_t *)); + for (int i = 0; i < nb_rx; i++) { + output_cpu[c][i] = output_cpu_data + (c * nb_rx + i) * num_samples; + } + } + + channel_desc_t **channels = malloc(num_channels * sizeof(channel_desc_t *)); + + // Define some realistic default values for the channel model + // double sampling_rate = 122.88e6; + // double channel_bandwidth = 100e6; + // uint64_t center_freq = 3.5e9; + // double ue_speed_kmh = 3.0; + // double max_doppler = (ue_speed_kmh * 1000.0 / 3600.0) * center_freq / 3e8; + + for (int c = 0; c < num_channels; c++) { + // Use the full channel descriptor initialization with all 13 arguments + // channels[c] = new_channel_desc_scm(nb_tx, + // nb_rx, + // TDL_A, // channel_model + // sampling_rate, + // center_freq, + // channel_bandwidth, + // 1e-7, // DS_TDL (Delay Spread) + // max_doppler, // maxDoppler + // CORR_LEVEL_LOW, // corr_level + // 0.0, // forgetting_factor + // 0, // channel_offset + // 0.0, // path_loss_dB + // -100.0); // noise_power_dB + + channels[c] = create_manual_channel_desc(nb_tx, nb_rx, channel_length); + if (!channels[c]) { + fprintf(stderr, "Error creating channel descriptor %d\n", c); + exit(1); + } + } + + // DEVICE MEMORY + void *d_tx_sig = NULL, *d_rx_sig = NULL, *d_curand_states = NULL, *h_tx_sig_pinned = NULL, *h_output_sig_pinned = NULL, + *d_channel_coeffs_gpu = NULL, **d_individual_gpu_outputs = NULL, *d_summed_gpu_output = NULL; + c16_t **output_gpu = NULL; + + void *d_tx_sig_batch = NULL, *d_intermediate_sig_batch = NULL, *d_final_output_batch = NULL, *d_channel_coeffs_batch = NULL; + float2 *h_channel_coeffs_batch = NULL; + float *h_channel_coeffs = NULL; + float2 *h_tx_sig_batch_interleaved = NULL; + + const int max_taps = 256; + const int padding_len = max_taps - 1; + const int padded_num_samples = num_samples + padding_len; + + // Sizes for batch mode + size_t tx_batch_bytes = num_channels * nb_tx * padded_num_samples * sizeof(float2); + size_t intermediate_batch_bytes = num_channels * nb_rx * num_samples * sizeof(float2); + size_t final_batch_bytes = num_channels * nb_rx * num_samples * sizeof(short2); + size_t channel_batch_bytes = num_channels * nb_tx * nb_rx * max_taps * sizeof(float2); + + // Sizes for serial/stream mode + size_t tx_bytes = nb_tx * padded_num_samples * 2 * sizeof(float); + size_t rx_bytes = nb_rx * num_samples * sizeof(float2); + size_t output_bytes = nb_rx * num_samples * sizeof(short2); + size_t channel_bytes = nb_tx * nb_rx * max_taps * sizeof(float2); + + if (strcmp(mode_str, "batch") == 0) { + h_channel_coeffs_batch = malloc(channel_batch_bytes); +#if defined(USE_UNIFIED_MEMORY) + printf("Memory Mode: Unified Memory\n"); + cudaMallocManaged(&d_tx_sig_batch, tx_batch_bytes, cudaMemAttachGlobal); + cudaMallocManaged(&d_intermediate_sig_batch, intermediate_batch_bytes, cudaMemAttachGlobal); + cudaMallocManaged(&d_final_output_batch, final_batch_bytes, cudaMemAttachGlobal); + cudaMallocManaged(&d_channel_coeffs_batch, channel_batch_bytes, cudaMemAttachGlobal); +#elif defined(USE_ATS_MEMORY) + printf("Memory Mode: ATS\n"); + d_tx_sig_batch = malloc(tx_batch_bytes); + cudaMalloc(&d_intermediate_sig_batch, intermediate_batch_bytes); + cudaMalloc(&d_final_output_batch, final_batch_bytes); + cudaMalloc(&d_channel_coeffs_batch, channel_batch_bytes); +#else + printf("Memory Mode: Explicit Copy\n"); + cudaMalloc(&d_tx_sig_batch, tx_batch_bytes); + cudaMalloc(&d_intermediate_sig_batch, intermediate_batch_bytes); + cudaMalloc(&d_final_output_batch, final_batch_bytes); + cudaMalloc(&d_channel_coeffs_batch, channel_batch_bytes); + h_tx_sig_batch_interleaved = (float2 *)malloc(tx_batch_bytes); +#endif + + if (sum_outputs) { + cudaMalloc(&d_summed_gpu_output, final_batch_bytes); + } + + } else { + // Serial & Stream + h_channel_coeffs = malloc(channel_bytes); + d_individual_gpu_outputs = malloc(num_channels * sizeof(void *)); + +#if defined(USE_UNIFIED_MEMORY) + printf("Memory Mode: Unified Memory\n"); + cudaMallocManaged(&d_channel_coeffs_gpu, channel_bytes, cudaMemAttachGlobal); + cudaMallocManaged(&d_tx_sig, tx_bytes, cudaMemAttachGlobal); + cudaMallocManaged(&d_rx_sig, rx_bytes, cudaMemAttachGlobal); + for (int c = 0; c < num_channels; c++) { + cudaMallocManaged(&d_individual_gpu_outputs[c], output_bytes, cudaMemAttachGlobal); + } + if (sum_outputs) { + cudaMallocManaged(&d_summed_gpu_output, output_bytes, cudaMemAttachGlobal); + } + h_tx_sig_pinned = d_tx_sig; +#elif defined(USE_ATS_MEMORY) + printf("Memory Mode: ATS\n"); + cudaMalloc(&d_channel_coeffs_gpu, channel_bytes); + cudaMalloc(&d_rx_sig, rx_bytes); + h_tx_sig_pinned = malloc(tx_bytes); + d_tx_sig = NULL; + for (int c = 0; c < num_channels; c++) { + cudaMalloc(&d_individual_gpu_outputs[c], output_bytes); + } + if (sum_outputs) { + cudaMalloc(&d_summed_gpu_output, output_bytes); + } + if (strcmp(mode_str, "serial") == 0) { + h_output_sig_pinned = malloc(output_bytes); + } +#else + printf("Memory Mode: Explicit Copy\n"); + cudaMalloc(&d_channel_coeffs_gpu, channel_bytes); + cudaMalloc(&d_tx_sig, tx_bytes); + cudaMalloc(&d_rx_sig, rx_bytes); + cudaMallocHost(&h_tx_sig_pinned, tx_bytes); + for (int c = 0; c < num_channels; c++) { + cudaMalloc(&d_individual_gpu_outputs[c], output_bytes); + } + if (sum_outputs) { + cudaMalloc(&d_summed_gpu_output, output_bytes); + } + if (strcmp(mode_str, "serial") == 0) { + cudaMallocHost(&h_output_sig_pinned, output_bytes); + } +#endif + + if (strcmp(mode_str, "serial") == 0) { + output_gpu = malloc(nb_rx * sizeof(c16_t *)); + output_gpu[0] = malloc(nb_rx * num_samples * sizeof(c16_t)); + for (int i = 1; i < nb_rx; i++) + output_gpu[i] = output_gpu[0] + i * num_samples; + } + } + + d_curand_states = create_and_init_curand_states_cuda(nb_rx * num_samples, time(NULL)); + + double total_cpu_ns = 0; + double total_gpu_ns = 0; + + for (int t = 0; t < num_trials; t++) { + if (sum_outputs) { + d_individual_gpu_outputs = malloc(num_channels * sizeof(void *)); + } + for (int i = 0; i < num_tx_signals; i++) { + generate_random_signal_interleaved(tx_sig_interleaved[i], nb_tx, num_samples); + } + for (int c = 0; c < num_channels; c++) + random_channel(channels[c], 0); + + struct timespec start, end; + + // --- CPU RUN --- + clock_gettime(CLOCK_MONOTONIC, &start); + for (int c = 0; c < num_channels; c++) { + float **current_tx = sum_outputs ? tx_sig_interleaved[c] : tx_sig_interleaved[0]; + multipath_channel_float(channels[c], current_tx, rx_multipath_re_cpu, rx_multipath_im_cpu, num_samples, 1, 0); + add_noise_float(output_cpu[c], + (const float **)rx_multipath_re_cpu, + (const float **)rx_multipath_im_cpu, + 0.1, + num_samples, + 0, + 0, + 0, + false, + nb_rx); + } + if (sum_outputs) { + c16_t *final_sum_cpu = calloc(nb_rx * num_samples, sizeof(c16_t)); + for (int c = 0; c < num_channels; c++) { + for (int i = 0; i < nb_rx * num_samples; i++) { + final_sum_cpu[i].r += output_cpu[c][0][i].r; + final_sum_cpu[i].i += output_cpu[c][0][i].i; + } + } + free(final_sum_cpu); + } + clock_gettime(CLOCK_MONOTONIC, &end); + total_cpu_ns += (end.tv_sec - start.tv_sec) * 1e9 + (end.tv_nsec - start.tv_nsec); + + // --- GPU RUN --- + clock_gettime(CLOCK_MONOTONIC, &start); + if (strcmp(mode_str, "batch") == 0) { + for (int c = 0; c < num_channels; c++) { + for (int link = 0; link < nb_tx * nb_rx; link++) { + for (int l = 0; l < channel_length; l++) { + int batch_idx = (c * nb_tx * nb_rx * max_taps) + (link * max_taps) + l; + h_channel_coeffs_batch[batch_idx].x = (float)channels[c]->ch[link][l].r; + h_channel_coeffs_batch[batch_idx].y = (float)channels[c]->ch[link][l].i; + } + } + } + } + + if (strcmp(mode_str, "batch") == 0) { +#if defined(USE_UNIFIED_MEMORY) || defined(USE_ATS_MEMORY) + float2 *tx_batch_ptr = (float2 *)d_tx_sig_batch; + memset(tx_batch_ptr, 0, tx_batch_bytes); + for (int c = 0; c < num_channels; c++) { + float **current_tx = sum_outputs ? tx_sig_interleaved[c] : tx_sig_interleaved[0]; + for (int j = 0; j < nb_tx; j++) { + float2 *data_start_ptr = tx_batch_ptr + (c * nb_tx + j) * padded_num_samples + padding_len; + for (int i = 0; i < num_samples; i++) { + data_start_ptr[i] = make_float2(current_tx[j][2 * i], current_tx[j][2 * i + 1]); + } + } + } +#else // EXPLICIT COPY + memset(h_tx_sig_batch_interleaved, 0, tx_batch_bytes); + for (int c = 0; c < num_channels; c++) { + float **current_tx = sum_outputs ? tx_sig_interleaved[c] : tx_sig_interleaved[0]; + for (int j = 0; j < nb_tx; j++) { + float2 *data_start_ptr = h_tx_sig_batch_interleaved + (c * nb_tx + j) * padded_num_samples + padding_len; + for (int i = 0; i < num_samples; i++) { + data_start_ptr[i] = make_float2(current_tx[j][2 * i], current_tx[j][2 * i + 1]); + } + } + } + cudaMemcpy(d_tx_sig_batch, h_tx_sig_batch_interleaved, tx_batch_bytes, cudaMemcpyHostToDevice); +#endif + + cudaMemcpy(d_channel_coeffs_batch, h_channel_coeffs_batch, channel_batch_bytes, cudaMemcpyHostToDevice); + + run_channel_pipeline_cuda_batched(num_channels, + nb_tx, + nb_rx, + channel_length, + num_samples, + d_channel_coeffs_batch, + 0.1, + 0, + 0xFFFF, + 0xFFFF, + d_tx_sig_batch, + d_intermediate_sig_batch, + d_final_output_batch, + d_curand_states); + cudaDeviceSynchronize(); + + if (sum_outputs) { + for (int c = 0; c < num_channels; c++) { + d_individual_gpu_outputs[c] = d_final_output_batch + c * nb_rx * num_samples * sizeof(short2); + } + sum_channel_outputs_cuda(d_individual_gpu_outputs, d_summed_gpu_output, num_channels, nb_rx, num_samples); + } + + } else { + if (strcmp(mode_str, "stream") == 0) { + cudaStream_t streams[num_channels]; + for (int c = 0; c < num_channels; c++) + cudaStreamCreateWithFlags(&streams[c], cudaStreamNonBlocking); + + for (int c = 0; c < num_channels; c++) { + for (int link = 0; link < nb_tx * nb_rx; link++) { + for (int l = 0; l < channels[c]->channel_length; l++) { + int idx = link * max_taps + l; + ((float2 *)h_channel_coeffs)[idx].x = (float)channels[c]->ch[link][l].r; + ((float2 *)h_channel_coeffs)[idx].y = (float)channels[c]->ch[link][l].i; + } + } + + float *h_tx_ptr = (float *)h_tx_sig_pinned; + float **current_tx = sum_outputs ? tx_sig_interleaved[c] : tx_sig_interleaved[0]; + memset(h_tx_ptr, 0, tx_bytes); + for (int j = 0; j < nb_tx; j++) { + float *data_start_ptr = h_tx_ptr + (j * padded_num_samples + padding_len) * 2; + memcpy(data_start_ptr, current_tx[j], num_samples * 2 * sizeof(float)); + } + + run_channel_pipeline_cuda_streamed(nb_tx, + nb_rx, + channels[c]->channel_length, + num_samples, + h_channel_coeffs, + 0.1, + 0, + 0xFFFF, + 0xFFFF, + d_tx_sig, + d_rx_sig, + d_individual_gpu_outputs[c], + d_curand_states, + h_tx_sig_pinned, + d_channel_coeffs_gpu, + (void *)streams[c]); + } + if (sum_outputs) { + sum_channel_outputs_cuda(d_individual_gpu_outputs, d_summed_gpu_output, num_channels, nb_rx, num_samples); + } + cudaDeviceSynchronize(); + for (int c = 0; c < num_channels; c++) + cudaStreamDestroy(streams[c]); + + } else if (strcmp(mode_str, "serial") == 0) { + for (int c = 0; c < num_channels; c++) { + float *h_tx_ptr = (float *)h_tx_sig_pinned; + float **current_tx = sum_outputs ? tx_sig_interleaved[c] : tx_sig_interleaved[0]; + + memset(h_tx_ptr, 0, tx_bytes); + for (int j = 0; j < nb_tx; j++) { + float *data_start_ptr = h_tx_ptr + (j * padded_num_samples + padding_len) * 2; + memcpy(data_start_ptr, current_tx[j], num_samples * 2 * sizeof(float)); + } + + for (int link = 0; link < nb_tx * nb_rx; link++) { + for (int l = 0; l < channels[c]->channel_length; l++) { + int idx = link * max_taps + l; + ((float2 *)h_channel_coeffs)[idx].x = (float)channels[c]->ch[link][l].r; + ((float2 *)h_channel_coeffs)[idx].y = (float)channels[c]->ch[link][l].i; + } + } + + void *host_output_ptr_for_pipeline = h_output_sig_pinned; +#if defined(USE_UNIFIED_MEMORY) + host_output_ptr_for_pipeline = d_individual_gpu_outputs[c]; +#endif + + run_channel_pipeline_cuda(output_gpu, + nb_tx, + nb_rx, + channels[c]->channel_length, + num_samples, + h_channel_coeffs, + 0.1, + 0, + 0xFFFF, + 0xFFFF, + 0, + 0, + d_tx_sig, + d_rx_sig, + d_individual_gpu_outputs[c], + d_curand_states, + h_tx_sig_pinned, + host_output_ptr_for_pipeline, + d_channel_coeffs_gpu); + } + } + } + cudaDeviceSynchronize(); + clock_gettime(CLOCK_MONOTONIC, &end); + total_gpu_ns += (end.tv_sec - start.tv_sec) * 1e9 + (end.tv_nsec - start.tv_nsec); + } + + double avg_cpu_us = (total_cpu_ns / num_trials) / 1000.0; + double avg_gpu_us = (total_gpu_ns / num_trials) / 1000.0; + double speedup = (avg_cpu_us > 0 && avg_gpu_us > 0) ? (avg_cpu_us / avg_gpu_us) : 0; + double avg_cpu_per_channel_us = avg_cpu_us / num_channels; + double avg_gpu_per_channel_us = avg_gpu_us / num_channels; + double total_samples_processed = (double)num_channels * nb_rx * num_samples; + double gpu_throughput_gsps = total_samples_processed / (avg_gpu_us * 1000.0); + char val_str[30]; + + printf("\n--- Final Benchmark Results ---\n\n"); + printf("+----------------------------------+--------------------------+\n"); + printf("| %-32s | %-24s |\n", "Configuration", "Value"); + printf("+----------------------------------+--------------------------+\n"); + printf("| %-32s | %-24d |\n", "Parallel Channels", num_channels); + snprintf(val_str, sizeof(val_str), "%d x %d", nb_tx, nb_rx); + printf("| %-32s | %-24s |\n", "MIMO Configuration (Tx x Rx)", val_str); + printf("| %-32s | %-24d |\n", "Signal Length (Samples)", num_samples); + printf("| %-32s | %-24d |\n", "Trials per configuration", num_trials); + printf("+----------------------------------+--------------------------+\n"); + printf("| %-32s | %-24s |\n", "Performance Metric", "Value"); + printf("+----------------------------------+--------------------------+\n"); + printf("| %-32s %-19.2f %-4s |\n", "Total CPU Time", avg_cpu_us, "[us]"); + printf("| %-32s %-19.2f %-4s |\n", "Total GPU Time", avg_gpu_us, "[us]"); + printf("| %-32s %-19.2f %-4s |\n", "Avg Time per Channel - CPU", avg_cpu_per_channel_us, "[us]"); + printf("| %-32s %-19.2f %-4s |\n", "Avg Time per Channel - GPU", avg_gpu_per_channel_us, "[us]"); + snprintf(val_str, sizeof(val_str), "%.2fx", speedup); + printf("| %-32s %-24s |\n", "Speedup (CPU/GPU)", val_str); + printf("| %-32s %-24.3f |\n", "GPU Throughput (GSPS)", gpu_throughput_gsps); + printf("+----------------------------------+--------------------------+\n"); + + free(tx_sig_data); + for (int i = 0; i < num_tx_signals; i++) { + free(tx_sig_interleaved[i]); + } + free(tx_sig_interleaved); + + free(rx_multipath_data); + free(rx_multipath_re_cpu); + free(rx_multipath_im_cpu); + + free(output_cpu_data); + for (int c = 0; c < num_channels; c++) { + free(output_cpu[c]); + free_manual_channel_desc(channels[c]); + } + free(output_cpu); + free(channels); + + if (strcmp(mode_str, "batch") == 0) { + free(h_channel_coeffs_batch); + if (sum_outputs) { + cudaFree(d_summed_gpu_output); + } +#if defined(USE_ATS_MEMORY) + free(d_tx_sig_batch); +#else + cudaFree(d_tx_sig_batch); + if (h_tx_sig_batch_interleaved) + free(h_tx_sig_batch_interleaved); +#endif + cudaFree(d_intermediate_sig_batch); + cudaFree(d_final_output_batch); + cudaFree(d_channel_coeffs_batch); + } else { // Serial & Stream Cleanup + free(h_channel_coeffs); + cudaFree(d_channel_coeffs_gpu); + + for (int c = 0; c < num_channels; c++) { + cudaFree(d_individual_gpu_outputs[c]); + } + free(d_individual_gpu_outputs); + + if (sum_outputs) { + cudaFree(d_summed_gpu_output); + } + +#if defined(USE_UNIFIED_MEMORY) + cudaFree(d_tx_sig); + cudaFree(d_rx_sig); +#elif defined(USE_ATS_MEMORY) + free(h_tx_sig_pinned); + cudaFree(d_rx_sig); +#else // EXPLICIT COPY + cudaFreeHost(h_tx_sig_pinned); + cudaFree(d_tx_sig); + cudaFree(d_rx_sig); +#endif + + if (strcmp(mode_str, "serial") == 0) { + if (h_output_sig_pinned) { +#if defined(USE_ATS_MEMORY) + free(h_output_sig_pinned); +#elif defined(USE_EXPLICIT_COPY) + cudaFreeHost(h_output_sig_pinned); +#endif + } + if (output_gpu) { + free(output_gpu[0]); + free(output_gpu); + } + } + } + destroy_curand_states_cuda(d_curand_states); + + printf("Benchmark finished.\n"); + return 0; +} diff --git a/openair1/PHY/TOOLS/tests/test_channel_simulation.c b/openair1/PHY/TOOLS/tests/test_channel_simulation.c new file mode 100644 index 0000000000..8e66350ce7 --- /dev/null +++ b/openair1/PHY/TOOLS/tests/test_channel_simulation.c @@ -0,0 +1,309 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#include +#include +#include +#include +#include + +#include "PHY/TOOLS/tools_defs.h" +#include "SIMULATION/TOOLS/sim.h" +#include "SIMULATION/TOOLS/oai_cuda.h" +#include "common/utils/LOG/log.h" +#include "common/utils/utils.h" +#include + +configmodule_interface_t *uniqCfg = NULL; + +void exit_function(const char *file, const char *function, const int line, const char *s, const int assert_not_exit) +{ + fprintf(stderr, "Exit function called from %s:%d in %s(). Message: %s\n", file, line, function, s); + exit(1); +} + +void generate_random_signal_interleaved(float **sig_interleaved, int nb_ant, int num_samples) +{ + for (int i = 0; i < nb_ant; i++) { + for (int j = 0; j < num_samples; j++) { + sig_interleaved[i][2 * j] = (float)((rand() % 2000) - 1000); + sig_interleaved[i][2 * j + 1] = (float)((rand() % 2000) - 1000); + } + } +} + +channel_desc_t *create_manual_channel_desc(int nb_tx, int nb_rx, int channel_length) +{ + channel_desc_t *desc = (channel_desc_t *)calloc(1, sizeof(channel_desc_t)); + desc->nb_tx = nb_tx; + desc->nb_rx = nb_rx; + desc->channel_length = channel_length; + desc->path_loss_dB = 0.0; + desc->channel_offset = 0; + int num_links = nb_tx * nb_rx; + float path_loss = (float)pow(10, desc->path_loss_dB / 20.0); + desc->ch = (struct complexd **)malloc(num_links * sizeof(struct complexd *)); + for (int i = 0; i < num_links; i++) { + desc->ch[i] = (struct complexd *)malloc(channel_length * sizeof(struct complexd)); + for (int l = 0; l < channel_length; l++) { + desc->ch[i][l].r = ((double)rand() / (double)RAND_MAX * 0.1) * path_loss; + desc->ch[i][l].i = ((double)rand() / (double)RAND_MAX * 0.1) * path_loss; + } + } + return desc; +} + +void free_manual_channel_desc(channel_desc_t *desc) +{ + if (!desc) + return; + int num_links = desc->nb_tx * desc->nb_rx; + for (int i = 0; i < num_links; i++) + free(desc->ch[i]); + free(desc->ch); + free(desc); +} + +int main(int argc, char **argv) +{ + logInit(); + randominit(0); + + int nb_tx_configs[] = {1, 2, 4, 8}; + int nb_rx_configs[] = {1, 2, 4, 8}; + int num_samples_configs[] = {30720, 61440, 122880}; + int channel_length_configs[] = {16, 32}; + char *channel_type_names[] = {"Short Channel", "Long Channel"}; + int num_trials = 50; + float snr_db = 15.0f; + + printf("Starting Full Channel Pipeline Benchmark (Multipath + Noise)\n"); + printf("Averaging each test case over %d trials.\n", num_trials); + printf( + "----------------------------------------------------------------------------------------------------------------------\n"); + printf("%-15s | %-15s | %-15s | %-20s | %-20s | %-15s\n", + "Channel Type", + "MIMO Config", + "Signal Length", + "CPU Pipeline (us)", + "GPU Pipeline (us)", + "Overall Speedup"); + printf( + "----------------------------------------------------------------------------------------------------------------------\n"); + + for (int c = 0; c < sizeof(channel_length_configs) / sizeof(int); c++) { + for (int s = 0; s < sizeof(num_samples_configs) / sizeof(int); s++) { + for (int m = 0; m < sizeof(nb_tx_configs) / sizeof(int); m++) { + int nb_tx = nb_tx_configs[m]; + int nb_rx = nb_rx_configs[m]; + int num_samples = num_samples_configs[s]; + int channel_length = channel_length_configs[c]; + + char mimo_str[16]; + sprintf(mimo_str, "%dx%d", nb_tx, nb_rx); + + channel_desc_t *chan_desc = create_manual_channel_desc(nb_tx, nb_rx, channel_length); + + float **tx_sig_interleaved = malloc(nb_tx * sizeof(float *)); + float **rx_multipath_re_cpu = malloc(nb_rx * sizeof(float *)); + float **rx_multipath_im_cpu = malloc(nb_rx * sizeof(float *)); + c16_t **output_cpu = malloc(nb_rx * sizeof(c16_t *)); + c16_t **output_gpu = malloc(nb_rx * sizeof(c16_t *)); + + for (int i = 0; i < nb_tx; i++) { + tx_sig_interleaved[i] = malloc(num_samples * 2 * sizeof(float)); + } + for (int i = 0; i < nb_rx; i++) { + rx_multipath_re_cpu[i] = malloc(num_samples * sizeof(float)); + rx_multipath_im_cpu[i] = malloc(num_samples * sizeof(float)); + } + output_cpu[0] = malloc(nb_rx * num_samples * sizeof(c16_t)); + output_gpu[0] = malloc(nb_rx * num_samples * sizeof(c16_t)); + for (int i = 1; i < nb_rx; i++) { + output_cpu[i] = output_cpu[0] + i * num_samples; + output_gpu[i] = output_gpu[0] + i * num_samples; + } + + void *d_tx_sig = NULL, *d_rx_sig = NULL, *d_output_noise = NULL, *d_curand_states = NULL; + void *h_tx_sig_pinned = NULL, *h_output_sig_pinned = NULL, *d_channel_coeffs_gpu = NULL; + + const int padding_len = channel_length - 1; + const size_t padded_tx_buffer_bytes = nb_tx * (num_samples + padding_len) * 2 * sizeof(float); + const size_t rx_buffer_bytes = nb_rx * num_samples * sizeof(float2); // Intermediate buffer + const size_t output_buffer_bytes = nb_rx * num_samples * sizeof(short2); + const size_t channel_buffer_size = nb_tx * nb_rx * channel_length * sizeof(float2); + +#if defined(USE_UNIFIED_MEMORY) + cudaMallocManaged(&d_tx_sig, padded_tx_buffer_bytes, cudaMemAttachGlobal); + cudaMallocManaged(&d_rx_sig, rx_buffer_bytes, cudaMemAttachGlobal); + cudaMallocManaged(&d_output_noise, output_buffer_bytes, cudaMemAttachGlobal); + cudaMallocManaged(&d_channel_coeffs_gpu, channel_buffer_size, cudaMemAttachGlobal); + h_tx_sig_pinned = d_tx_sig; + h_output_sig_pinned = d_output_noise; +#elif defined(USE_ATS_MEMORY) + h_tx_sig_pinned = malloc(padded_tx_buffer_bytes); + d_tx_sig = NULL; + cudaMalloc(&d_rx_sig, rx_buffer_bytes); + cudaMalloc(&d_output_noise, output_buffer_bytes); + cudaMalloc(&d_channel_coeffs_gpu, channel_buffer_size); + h_output_sig_pinned = malloc(output_buffer_bytes); +#else + cudaMalloc(&d_tx_sig, padded_tx_buffer_bytes); + cudaMalloc(&d_rx_sig, rx_buffer_bytes); + cudaMalloc(&d_output_noise, output_buffer_bytes); + cudaMalloc(&d_channel_coeffs_gpu, channel_buffer_size); + cudaMallocHost(&h_tx_sig_pinned, padded_tx_buffer_bytes); + cudaMallocHost(&h_output_sig_pinned, output_buffer_bytes); +#endif + + d_curand_states = create_and_init_curand_states_cuda(nb_rx * num_samples, time(NULL)); + + double ts = 1.0 / 30.72e6; + float sigma2 = 1.0f / powf(10.0f, snr_db / 10.0f); + int num_links = nb_tx * nb_rx; + float *h_channel_coeffs = (float *)malloc(num_links * channel_length * sizeof(float2)); + for (int link = 0; link < num_links; link++) { + for (int l = 0; l < channel_length; l++) { + int idx = link * channel_length + l; + ((float2 *)h_channel_coeffs)[idx].x = (float)chan_desc->ch[link][l].r; + ((float2 *)h_channel_coeffs)[idx].y = (float)chan_desc->ch[link][l].i; + } + } + + double total_cpu_ns = 0; + double total_gpu_ns = 0; + + for (int t = 0; t < num_trials; t++) { + generate_random_signal_interleaved(tx_sig_interleaved, nb_tx, num_samples); + struct timespec start, end; + + const int padding_len = channel_length - 1; + const int padded_num_samples = num_samples + padding_len; + float *h_tx_ptr = (float *)h_tx_sig_pinned; + memset(h_tx_ptr, 0, padded_tx_buffer_bytes); + for (int j = 0; j < nb_tx; j++) { + float *data_start_ptr = h_tx_ptr + (j * padded_num_samples + padding_len) * 2; + memcpy(data_start_ptr, tx_sig_interleaved[j], num_samples * 2 * sizeof(float)); + } + + clock_gettime(CLOCK_MONOTONIC, &start); + float **tx_sig_for_cpu = malloc(nb_tx * sizeof(float *)); + for (int j = 0; j < nb_tx; j++) { + tx_sig_for_cpu[j] = h_tx_ptr + (j * padded_num_samples + padding_len) * 2; + } + multipath_channel_float(chan_desc, tx_sig_for_cpu, rx_multipath_re_cpu, rx_multipath_im_cpu, num_samples, 1, 0); + free(tx_sig_for_cpu); + add_noise_float(output_cpu, + (const float **)rx_multipath_re_cpu, + (const float **)rx_multipath_im_cpu, + sigma2, + num_samples, + 0, + ts, + 0, + false, + nb_rx); + clock_gettime(CLOCK_MONOTONIC, &end); + total_cpu_ns += (end.tv_sec - start.tv_sec) * 1e9 + (end.tv_nsec - start.tv_nsec); + + clock_gettime(CLOCK_MONOTONIC, &start); + run_channel_pipeline_cuda(output_gpu, + nb_tx, + nb_rx, + channel_length, + num_samples, + h_channel_coeffs, + sigma2, + ts, + 0, + 0, + 0, + 0, + d_tx_sig, + d_rx_sig, + d_output_noise, + d_curand_states, + h_tx_sig_pinned, + h_output_sig_pinned, + d_channel_coeffs_gpu); + cudaDeviceSynchronize(); + clock_gettime(CLOCK_MONOTONIC, &end); + total_gpu_ns += (end.tv_sec - start.tv_sec) * 1e9 + (end.tv_nsec - start.tv_nsec); + } + + double avg_cpu_us = (total_cpu_ns / num_trials) / 1000.0; + double avg_gpu_us = (total_gpu_ns / num_trials) / 1000.0; + double speedup = (avg_gpu_us > 0) ? (avg_cpu_us / avg_gpu_us) : 0; + + printf("%-15s | %-15s | %-15d | %-20.2f | %-20.2f | %-15.2fx\n", + channel_type_names[c], + mimo_str, + num_samples, + avg_cpu_us, + avg_gpu_us, + speedup); + + free(h_channel_coeffs); + for (int i = 0; i < nb_tx; i++) { + free(tx_sig_interleaved[i]); + } + for (int i = 0; i < nb_rx; i++) { + free(rx_multipath_re_cpu[i]); + free(rx_multipath_im_cpu[i]); + } + free(tx_sig_interleaved); + free(rx_multipath_re_cpu); + free(rx_multipath_im_cpu); + free(output_cpu[0]); + free(output_gpu[0]); + free(output_cpu); + free(output_gpu); + +#if defined(USE_UNIFIED_MEMORY) + cudaFree(d_tx_sig); // In UM, h_tx_sig_pinned is an alias for d_tx_sig + cudaFree(d_rx_sig); + cudaFree(d_output_noise); +#elif defined(USE_ATS_MEMORY) + free(h_tx_sig_pinned); + cudaFree(d_rx_sig); + cudaFree(d_output_noise); + free(h_output_sig_pinned); +#else // EXPLICIT COPY + cudaFree(d_tx_sig); + cudaFree(d_rx_sig); + cudaFree(d_output_noise); + cudaFreeHost(h_tx_sig_pinned); + cudaFreeHost(h_output_sig_pinned); +#endif + + cudaFree(d_channel_coeffs_gpu); + destroy_curand_states_cuda(d_curand_states); + free_manual_channel_desc(chan_desc); + } + } + } + + printf( + "----------------------------------------------------------------------------------------------------------------------\n"); + printf("Pipeline benchmark finished.\n"); + + return 0; +} diff --git a/openair1/PHY/TOOLS/tests/test_multipath.c b/openair1/PHY/TOOLS/tests/test_multipath.c new file mode 100644 index 0000000000..6b1258146b --- /dev/null +++ b/openair1/PHY/TOOLS/tests/test_multipath.c @@ -0,0 +1,289 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#include +#include +#include +#include +#include +#include "PHY/TOOLS/tools_defs.h" +#include "SIMULATION/TOOLS/sim.h" +#include +#include "SIMULATION/TOOLS/oai_cuda.h" +#include "common/utils/LOG/log.h" +#include "common/utils/utils.h" + +configmodule_interface_t *uniqCfg = NULL; + +void exit_function(const char *file, const char *function, const int line, const char *s, const int assert_not_exit) +{ + fprintf(stderr, "Exit function called from %s:%d in %s(). Message: %s\n", file, line, function, s); + exit(1); +} + +void generate_random_signal_interleaved(float **sig_interleaved, int nb_ant, int num_samples) +{ + for (int i = 0; i < nb_ant; i++) { + for (int j = 0; j < num_samples; j++) { + sig_interleaved[i][2 * j] = (float)rand() / (float)RAND_MAX * 2.0f - 1.0f; + sig_interleaved[i][2 * j + 1] = (float)rand() / (float)RAND_MAX * 2.0f - 1.0f; + } + } +} + +channel_desc_t *create_manual_channel_desc(int nb_tx, int nb_rx, int channel_length) +{ + channel_desc_t *desc = (channel_desc_t *)calloc(1, sizeof(channel_desc_t)); + desc->nb_tx = nb_tx; + desc->nb_rx = nb_rx; + desc->channel_length = channel_length; + desc->path_loss_dB = 10.0; + desc->channel_offset = 0; + int num_links = nb_tx * nb_rx; + desc->ch = (struct complexd **)malloc(num_links * sizeof(struct complexd *)); + float path_loss = (float)pow(10, desc->path_loss_dB / 20.0); + for (int i = 0; i < num_links; i++) { + desc->ch[i] = (struct complexd *)malloc(channel_length * sizeof(struct complexd)); + for (int l = 0; l < channel_length; l++) { + desc->ch[i][l].r = (double)rand() / (double)RAND_MAX * path_loss; + desc->ch[i][l].i = (double)rand() / (double)RAND_MAX * path_loss; + } + } + return desc; +} + +void free_manual_channel_desc(channel_desc_t *desc) +{ + if (!desc) + return; + int num_links = desc->nb_tx * desc->nb_rx; + for (int i = 0; i < num_links; i++) + free(desc->ch[i]); + free(desc->ch); + free(desc); +} + +int verify_results(float **re_cpu, float **im_cpu, float **re_gpu, float **im_gpu, int nb_rx, int num_samples) +{ + double total_error = 0.0; + for (int i = 0; i < nb_rx; i++) { + for (int j = 0; j < num_samples; j++) { + double err_re = re_cpu[i][j] - re_gpu[i][j]; + double err_im = im_cpu[i][j] - im_gpu[i][j]; + total_error += (err_re * err_re) + (err_im * err_im); + } + } + double mse = total_error / (nb_rx * num_samples); + return (mse < 1e-9) ? 0 : 1; +} + +int main(int argc, char **argv) +{ + logInit(); + randominit(0); + + int nb_tx_configs[] = {1, 2, 4, 8}; + int nb_rx_configs[] = {1, 2, 4, 8}; + int num_samples_configs[] = {30720, 61440, 122880}; + int channel_length_configs[] = {16, 32}; + char *channel_type_names[] = {"Short Channel", "Long Channel"}; + int num_trials = 100; + + printf("Starting Multipath Channel Benchmark (CPU vs. Interleaved CUDA)\n"); + printf("Averaging each test case over %d trials.\n", num_trials); + printf( + "----------------------------------------------------------------------------------------------------------------------------" + "--------------\n"); + printf("%-15s | %-15s | %-15s | %-15s | %-15s | %-15s | %-10s\n", + "Channel Type", + "MIMO Config", + "Signal Length", + "CPU Time (us)", + "CUDA Time (us)", + "Speedup", + "Verification"); + printf( + "----------------------------------------------------------------------------------------------------------------------------" + "--------------\n"); + + for (int c = 0; c < sizeof(channel_length_configs) / sizeof(int); c++) { + for (int s = 0; s < sizeof(num_samples_configs) / sizeof(int); s++) { + for (int m = 0; m < sizeof(nb_tx_configs) / sizeof(int); m++) { + int nb_tx = nb_tx_configs[m]; + int nb_rx = nb_rx_configs[m]; + int num_samples = num_samples_configs[s]; + int channel_length = channel_length_configs[c]; + + char mimo_str[16]; + sprintf(mimo_str, "%dx%d", nb_tx, nb_rx); + + channel_desc_t *chan_desc = create_manual_channel_desc(nb_tx, nb_rx, channel_length); + + float **s_interleaved = malloc(nb_tx * sizeof(float *)); + float **r_re_cpu = malloc(nb_rx * sizeof(float *)); + float **r_im_cpu = malloc(nb_rx * sizeof(float *)); + float **r_re_gpu = malloc(nb_rx * sizeof(float *)); + float **r_im_gpu = malloc(nb_rx * sizeof(float *)); + + for (int i = 0; i < nb_tx; i++) { + s_interleaved[i] = malloc(num_samples * 2 * sizeof(float)); + } + for (int i = 0; i < nb_rx; i++) { + r_re_cpu[i] = malloc(num_samples * sizeof(float)); + r_im_cpu[i] = malloc(num_samples * sizeof(float)); + r_re_gpu[i] = malloc(num_samples * sizeof(float)); + r_im_gpu[i] = malloc(num_samples * sizeof(float)); + } + + void *d_tx_sig = NULL, *d_rx_sig = NULL, *d_channel_coeffs_gpu = NULL, *h_tx_sig_pinned = NULL; + const int num_samples_to_process = num_samples - chan_desc->channel_offset; + const int padding_len = channel_length - 1; + const size_t channel_buffer_size = nb_tx * nb_rx * channel_length * sizeof(float2); + const size_t tx_buffer_bytes = nb_tx * (num_samples_to_process + padding_len) * 2 * sizeof(float); + const size_t rx_buffer_bytes = nb_rx * num_samples_to_process * sizeof(float2); + +#if defined(USE_UNIFIED_MEMORY) + cudaMallocManaged(&d_tx_sig, tx_buffer_bytes, cudaMemAttachGlobal); + cudaMallocManaged(&d_rx_sig, rx_buffer_bytes, cudaMemAttachGlobal); + cudaMallocManaged(&d_channel_coeffs_gpu, channel_buffer_size, cudaMemAttachGlobal); + h_tx_sig_pinned = d_tx_sig; +#elif defined(USE_ATS_MEMORY) + h_tx_sig_pinned = malloc(tx_buffer_bytes); + d_tx_sig = NULL; + cudaMalloc(&d_rx_sig, rx_buffer_bytes); + cudaMalloc(&d_channel_coeffs_gpu, channel_buffer_size); +#else // EXPLICIT COPY + cudaMalloc(&d_tx_sig, tx_buffer_bytes); + cudaMalloc(&d_rx_sig, rx_buffer_bytes); + cudaMalloc(&d_channel_coeffs_gpu, channel_buffer_size); + cudaMallocHost(&h_tx_sig_pinned, tx_buffer_bytes); +#endif + + double total_cpu_ns = 0; + double total_gpu_ns = 0; + + int num_links = chan_desc->nb_tx * chan_desc->nb_rx; + float *h_channel_coeffs = (float *)malloc(num_links * channel_length * sizeof(float2)); + for (int link = 0; link < num_links; link++) { + for (int l = 0; l < channel_length; l++) { + int idx = link * channel_length + l; + ((float2 *)h_channel_coeffs)[idx].x = (float)chan_desc->ch[link][l].r; + ((float2 *)h_channel_coeffs)[idx].y = (float)chan_desc->ch[link][l].i; + } + } + + struct timespec start, end; + + generate_random_signal_interleaved(s_interleaved, nb_tx, num_samples); + + const int padded_num_samples = num_samples + padding_len; + float *h_tx_ptr = (float *)h_tx_sig_pinned; + memset(h_tx_ptr, 0, tx_buffer_bytes); + for (int j = 0; j < nb_tx; j++) { + float *data_start_ptr = h_tx_ptr + (j * padded_num_samples + padding_len) * 2; + memcpy(data_start_ptr, s_interleaved[j], num_samples * 2 * sizeof(float)); + } + + float **tx_sig_for_cpu = malloc(nb_tx * sizeof(float *)); + for (int j = 0; j < nb_tx; j++) { + tx_sig_for_cpu[j] = h_tx_ptr + (j * padded_num_samples + padding_len) * 2; + } + + multipath_channel_float(chan_desc, tx_sig_for_cpu, r_re_cpu, r_im_cpu, num_samples, 1, 0); + + clock_gettime(CLOCK_MONOTONIC, &start); + for (int t = 0; t < num_trials; t++) { + multipath_channel_float(chan_desc, tx_sig_for_cpu, r_re_cpu, r_im_cpu, num_samples, 1, 0); + } + clock_gettime(CLOCK_MONOTONIC, &end); + total_cpu_ns = (end.tv_sec - start.tv_sec) * 1e9 + (end.tv_nsec - start.tv_nsec); + free(tx_sig_for_cpu); + + clock_gettime(CLOCK_MONOTONIC, &start); + for (int t = 0; t < num_trials; t++) { + multipath_channel_cuda(r_re_gpu, + r_im_gpu, + nb_tx, + nb_rx, + channel_length, + num_samples, + chan_desc->channel_offset, + h_channel_coeffs, + d_tx_sig, + d_rx_sig, + d_channel_coeffs_gpu, + h_tx_sig_pinned); + } + cudaDeviceSynchronize(); + clock_gettime(CLOCK_MONOTONIC, &end); + total_gpu_ns = (end.tv_sec - start.tv_sec) * 1e9 + (end.tv_nsec - start.tv_nsec); + + int verification_passed = (verify_results(r_re_cpu, r_im_cpu, r_re_gpu, r_im_gpu, nb_rx, num_samples) == 0); + double avg_cpu_us = (total_cpu_ns / num_trials) / 1000.0; + double avg_gpu_us = (total_gpu_ns / num_trials) / 1000.0; + double speedup = (avg_gpu_us > 0) ? (avg_cpu_us / avg_gpu_us) : 0; + + printf("%-15s | %-15s | %-15d | %-15.2f | %-15.2f | %-15.2fx | %-10s\n", + channel_type_names[c], + mimo_str, + num_samples, + avg_cpu_us, + avg_gpu_us, + speedup, + (verification_passed ? "PASSED" : "FAILED")); + + free(h_channel_coeffs); + for (int i = 0; i < nb_tx; i++) { + free(s_interleaved[i]); + } + for (int i = 0; i < nb_rx; i++) { + free(r_re_cpu[i]); + free(r_im_cpu[i]); + free(r_re_gpu[i]); + free(r_im_gpu[i]); + } + free(s_interleaved); + free(r_re_cpu); + free(r_im_cpu); + free(r_re_gpu); + free(r_im_gpu); +#if defined(USE_UNIFIED_MEMORY) + cudaFree(d_tx_sig); +#elif defined(USE_ATS_MEMORY) + free(h_tx_sig_pinned); +#else // EXPLICIT COPY + cudaFree(d_tx_sig); + cudaFreeHost(h_tx_sig_pinned); +#endif + cudaFree(d_rx_sig); + cudaFree(d_channel_coeffs_gpu); + free_manual_channel_desc(chan_desc); + } + } + } + + printf( + "----------------------------------------------------------------------------------------------------------------------------" + "--------------\n"); + printf("Benchmark finished.\n"); + + return 0; +} diff --git a/openair1/PHY/TOOLS/tests/test_noise.c b/openair1/PHY/TOOLS/tests/test_noise.c new file mode 100644 index 0000000000..70d4a15ada --- /dev/null +++ b/openair1/PHY/TOOLS/tests/test_noise.c @@ -0,0 +1,186 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#include +#include +#include +#include +#include +#include "PHY/TOOLS/tools_defs.h" +#include "SIMULATION/TOOLS/sim.h" +#include "SIMULATION/TOOLS/oai_cuda.h" +#include "common/utils/LOG/log.h" +#include "common/utils/utils.h" +#include + +configmodule_interface_t *uniqCfg = NULL; + +void exit_function(const char *file, const char *function, const int line, const char *s, const int assert_not_exit) +{ + fprintf(stderr, "Exit function called from %s:%d in %s(). Message: %s\n", file, line, function, s); + exit(1); +} + +void generate_random_signal(float **sig_re, float **sig_im, int nb_ant, int num_samples) +{ + for (int i = 0; i < nb_ant; i++) { + for (int j = 0; j < num_samples; j++) { + sig_re[i][j] = (float)((rand() % 20000) - 10000); + sig_im[i][j] = (float)((rand() % 20000) - 10000); + } + } +} + +int main(int argc, char **argv) +{ + logInit(); + randominit(0); + + int nb_rx_configs[] = {1, 2, 4, 8}; + int num_samples_configs[] = {30720, 61440, 122880}; + int num_trials = 100; + float snr_db = 10.0f; + + printf("Starting Noise Generation Benchmark (CPU vs. CUDA)\n"); + printf("Averaging each test case over %d trials.\n", num_trials); + printf("---------------------------------------------------------------------------------------------\n"); + printf("%-15s | %-15s | %-15s | %-15s | %-15s\n", "Antennas", "Signal Length", "CPU Time (us)", "CUDA Time (us)", "Speedup"); + printf("---------------------------------------------------------------------------------------------\n"); + + for (int r = 0; r < sizeof(nb_rx_configs) / sizeof(int); r++) { + for (int s = 0; s < sizeof(num_samples_configs) / sizeof(int); s++) { + int nb_rx = nb_rx_configs[r]; + int num_samples = num_samples_configs[s]; + + float **r_re = malloc(nb_rx * sizeof(float *)); + float **r_im = malloc(nb_rx * sizeof(float *)); + for (int i = 0; i < nb_rx; i++) { + r_re[i] = malloc(num_samples * sizeof(float)); + r_im[i] = malloc(num_samples * sizeof(float)); + } + + c16_t **output_cpu = malloc(nb_rx * sizeof(c16_t *)); + c16_t **output_gpu = malloc(nb_rx * sizeof(c16_t *)); + output_cpu[0] = malloc(nb_rx * num_samples * sizeof(c16_t)); + output_gpu[0] = malloc(nb_rx * num_samples * sizeof(c16_t)); + for (int i = 1; i < nb_rx; i++) { + output_cpu[i] = output_cpu[0] + i * num_samples; + output_gpu[i] = output_gpu[0] + i * num_samples; + } + + void *d_r_sig, *d_output_sig, *d_curand_states; + void *h_r_sig_pinned, *h_output_sig_pinned; + +#if defined(USE_UNIFIED_MEMORY) + cudaMallocManaged(&d_r_sig, nb_rx * num_samples * sizeof(float) * 2, cudaMemAttachGlobal); + cudaMallocManaged(&d_output_sig, nb_rx * num_samples * sizeof(short) * 2, cudaMemAttachGlobal); + + int deviceId; + cudaGetDevice(&deviceId); + cudaMemAdvise(d_r_sig, nb_rx * num_samples * sizeof(float) * 2, cudaMemAdviseSetReadMostly, deviceId); + cudaMemAdvise(d_output_sig, nb_rx * num_samples * sizeof(short) * 2, cudaMemAdviseSetPreferredLocation, deviceId); + cudaMemAdvise(d_output_sig, nb_rx * num_samples * sizeof(short) * 2, cudaMemAdviseSetAccessedBy, cudaCpuDeviceId); + + // Pinned memory is not used in the UM path for this wrapper + h_r_sig_pinned = NULL; + h_output_sig_pinned = NULL; +#else + cudaMalloc(&d_r_sig, nb_rx * num_samples * sizeof(float) * 2); + cudaMalloc(&d_output_sig, nb_rx * num_samples * sizeof(short) * 2); + cudaMallocHost(&h_r_sig_pinned, nb_rx * num_samples * sizeof(float) * 2); + cudaMallocHost(&h_output_sig_pinned, nb_rx * num_samples * sizeof(short) * 2); +#endif + d_curand_states = create_and_init_curand_states_cuda(nb_rx * num_samples, time(NULL)); + + double total_cpu_ns = 0; + double total_gpu_ns = 0; + + double ts = 1.0 / 30.72e6; + float signal_power = 1.0f; + float sigma2 = signal_power / powf(10.0f, snr_db / 10.0f); + + for (int t = 0; t < num_trials; t++) { + generate_random_signal(r_re, r_im, nb_rx, num_samples); + + struct timespec start, end; + + clock_gettime(CLOCK_MONOTONIC, &start); + add_noise_float(output_cpu, (const float **)r_re, (const float **)r_im, sigma2, num_samples, 0, ts, 0, false, nb_rx); + clock_gettime(CLOCK_MONOTONIC, &end); + total_cpu_ns += (end.tv_sec - start.tv_sec) * 1e9 + (end.tv_nsec - start.tv_nsec); + + clock_gettime(CLOCK_MONOTONIC, &start); + add_noise_cuda((const float **)r_re, + (const float **)r_im, + output_gpu, + num_samples, + nb_rx, + sigma2, + ts, + 0, + 0, + 0, + 0, + d_r_sig, + d_output_sig, + d_curand_states, + h_r_sig_pinned, + h_output_sig_pinned); + cudaDeviceSynchronize(); + clock_gettime(CLOCK_MONOTONIC, &end); + total_gpu_ns += (end.tv_sec - start.tv_sec) * 1e9 + (end.tv_nsec - start.tv_nsec); + } + + double avg_cpu_us = (total_cpu_ns / num_trials) / 1000.0; + double avg_gpu_us = (total_gpu_ns / num_trials) / 1000.0; + double speedup = (avg_gpu_us > 0) ? (avg_cpu_us / avg_gpu_us) : 0; + + printf("%-15d | %-15d | %-15.2f | %-15.2f | %-15.2fx\n", nb_rx, num_samples, avg_cpu_us, avg_gpu_us, speedup); + + for (int i = 0; i < nb_rx; i++) { + free(r_re[i]); + free(r_im[i]); + } + free(r_re); + free(r_im); + free(output_cpu[0]); + free(output_gpu[0]); + free(output_cpu); + free(output_gpu); + +#if defined(USE_UNIFIED_MEMORY) + cudaFree(d_r_sig); + cudaFree(d_output_sig); +#else + cudaFree(d_r_sig); + cudaFree(d_output_sig); + cudaFreeHost(h_r_sig_pinned); + cudaFreeHost(h_output_sig_pinned); +#endif + destroy_curand_states_cuda(d_curand_states); + } + } + + printf("---------------------------------------------------------------------------------------------\n"); + printf("Benchmark finished.\n"); + + return 0; +} diff --git a/openair1/PHY/TOOLS/tools_defs.h b/openair1/PHY/TOOLS/tools_defs.h index 36fc496545..40f6ca46f6 100644 --- a/openair1/PHY/TOOLS/tools_defs.h +++ b/openair1/PHY/TOOLS/tools_defs.h @@ -816,7 +816,8 @@ static inline void rotate_cpx_vector(const c16_t *const x, const c16_t *const al // Split interleaved -> separate real/imag int16x8_t br = vuzp1q_s16(x_128[i], x_128[i]); int16x8_t bi = vuzp2q_s16(x_128[i], x_128[i]); - +#ifdef __ARM_FEATURE_QRDMX + // ARMv8.1-A: Use RDM instructions (rounding doubling multiply) // Start with the two “diagonal” products using high-half, doubling, sat: // x = round( (2*ar*br) / 2^16 ), y = round( (2*ar*bi) / 2^16 ) int16x8_t real = vqdmulhq_s16(ar, br); @@ -827,7 +828,21 @@ static inline void rotate_cpx_vector(const c16_t *const x, const c16_t *const al // imag += round( (2*ai*br) / 2^16 ) imag = vqrdmlahq_s16(imag, ai, br); +#else + // ARMv8.0-A fallback: Use standard 32-bit multiply + int32x4_t real_lo = vmull_s16(vget_low_s16(ar), vget_low_s16(br)); + int32x4_t real_hi = vmull_s16(vget_high_s16(ar), vget_high_s16(br)); + real_lo = vmlsl_s16(real_lo, vget_low_s16(ai), vget_low_s16(bi)); + real_hi = vmlsl_s16(real_hi, vget_high_s16(ai), vget_high_s16(bi)); + int32x4_t imag_lo = vmull_s16(vget_low_s16(ar), vget_low_s16(bi)); + int32x4_t imag_hi = vmull_s16(vget_high_s16(ar), vget_high_s16(bi)); + imag_lo = vmlal_s16(imag_lo, vget_low_s16(ai), vget_low_s16(br)); + imag_hi = vmlal_s16(imag_hi, vget_high_s16(ai), vget_high_s16(br)); + + int16x8_t real = vcombine_s16(vqrshrn_n_s32(real_lo, 15), vqrshrn_n_s32(real_hi, 15)); + int16x8_t imag = vcombine_s16(vqrshrn_n_s32(imag_lo, 15), vqrshrn_n_s32(imag_hi, 15)); +#endif // Re-interleave [real, imag] int16x8x2_t z = vzipq_s16(real, imag); diff --git a/openair1/SCHED_NR/nr_prach_procedures.c b/openair1/SCHED_NR/nr_prach_procedures.c index d5d6a8860e..bc0c7b9461 100644 --- a/openair1/SCHED_NR/nr_prach_procedures.c +++ b/openair1/SCHED_NR/nr_prach_procedures.c @@ -35,7 +35,6 @@ #include "nfapi_nr_interface_scf.h" #include "nfapi_pnf.h" #include "common/utils/LOG/log.h" -#include "common/utils/LOG/vcd_signal_dumper.h" #include "assertions.h" #include @@ -48,7 +47,6 @@ int get_nr_prach_duration(uint8_t prach_format) void L1_nr_prach_procedures(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_rach_indication_t *rach_ind) { - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_ENB_PRACH_RX,1); rach_ind->sfn = frame; rach_ind->slot = slot; rach_ind->number_of_pdus = 0; @@ -129,5 +127,4 @@ void L1_nr_prach_procedures(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_rac rach_ind->slot = prach_start_slot; LOG_D(NR_PHY_RACH, "Freeing PRACH entry\n"); free_nr_prach_entry(&gNB->prach_list, prach_id); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_ENB_PRACH_RX,0); } diff --git a/openair1/SCHED_NR/nr_ru_procedures.c b/openair1/SCHED_NR/nr_ru_procedures.c index 4cf05d5772..eb2c83c10a 100644 --- a/openair1/SCHED_NR/nr_ru_procedures.c +++ b/openair1/SCHED_NR/nr_ru_procedures.c @@ -38,7 +38,6 @@ #include "common/utils/LOG/log.h" #include "common/utils/system.h" -#include "common/utils/LOG/vcd_signal_dumper.h" #include "T.h" @@ -55,8 +54,6 @@ void nr_feptx0(RU_t *ru, int tti_tx, int first_symbol, int num_symbols, int aa) unsigned int slot_offset,slot_offsetF; int slot = tti_tx; - //VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_RU_FEPTX_OFDM+(first_symbol!=0?1:0) , 1 ); - if (aa == 0 && first_symbol == 0) start_meas(&ru->ofdm_mod_stats); slot_offset = get_samples_slot_timestamp(fp, slot); @@ -142,13 +139,11 @@ void nr_feptx0(RU_t *ru, int tti_tx, int first_symbol, int num_symbols, int aa) if (aa == 0 && first_symbol == 0) stop_meas(&ru->ofdm_mod_stats); - - //VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_RU_FEPTX_OFDM+(first_symbol!=0?1:0), 0); } // RU FEP TX OFDM modulation, single-thread -void nr_feptx_ofdm(RU_t *ru,int frame_tx,int tti_tx) { - +void nr_feptx_ofdm(RU_t *ru,int frame_tx,int tti_tx) +{ nfapi_nr_config_request_scf_t *cfg = &ru->gNB_list[0]->gNB_config; NR_DL_FRAME_PARMS *fp=ru->nr_frame_parms; int cyclic_prefix_type = NFAPI_CP_NORMAL; @@ -159,17 +154,11 @@ void nr_feptx_ofdm(RU_t *ru,int frame_tx,int tti_tx) { int slot = tti_tx; int *txdata = &ru->common.txdata[aa][get_samples_slot_timestamp(fp, slot)]; - if (nr_slot_select(cfg,frame_tx,slot) == NR_UPLINK_SLOT) return; - - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_RU_FEPTX_OFDM , 1 ); - - - // LOG_D(HW,"Frame %d: Generating slot %d\n",frame,next_slot); + if (nr_slot_select(cfg,frame_tx,slot) == NR_UPLINK_SLOT) + return; nr_feptx0(ru,slot,0,NR_NUMBER_OF_SYMBOLS_PER_SLOT,aa); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_RU_FEPTX_OFDM , 0 ); - LOG_D(PHY, "feptx_ofdm (TXPATH): frame %d, slot %d: txp (time %p) %d dB, txp (freq) %d dB\n", frame_tx, @@ -233,7 +222,6 @@ void nr_feptx(void *arg) int txdataF_offset = (slot * fp->samples_per_slot_wCP) + startSymbol * fp->ofdm_symbol_size; int txdataF_BF_offset = startSymbol * fp->ofdm_symbol_size; - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_RU_FEPTX_PREC+feptx->aid , 1); int tx_idx = aa + bb * ru->nb_tx; if (tx_idx == 0) @@ -256,9 +244,8 @@ void nr_feptx(void *arg) if (tx_idx == 0) stop_meas(&ru->precoding_stats); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_RU_FEPTX_PREC+feptx->aid , 0); - ////////////FEPTX//////////// + ////////////FEPTX//////////// nr_feptx0(ru, slot, startSymbol, numSymbols, tx_idx); // Task completed in // @@ -271,8 +258,6 @@ void nr_feptx_tp(RU_t *ru, int frame_tx, int slot) nfapi_nr_config_request_scf_t *cfg = &ru->gNB_list[0]->gNB_config; if (nr_slot_select(cfg, frame_tx, slot) == NR_UPLINK_SLOT) return; - if (ru->idx == 0) - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_RU_FEPTX_OFDM, 1); start_meas(&ru->ofdm_total_stats); int nt = ru->nb_tx * ru->num_beams_period; @@ -316,8 +301,6 @@ void nr_feptx_tp(RU_t *ru, int frame_tx, int slot) join_task_ans(&ans); stop_meas(&ru->ofdm_total_stats); - if (ru->idx == 0) - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_RU_FEPTX_OFDM, 0); } // core RX FEP routine, called by threads in RU thread-pool @@ -340,10 +323,9 @@ void nr_fep(void *arg) } // RU RX FEP using thread-pool -void nr_fep_tp(RU_t *ru, int slot) { - - int nbfeprx=0; - if (ru->idx == 0) VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_RU_FEPRX, 1 ); +void nr_fep_tp(RU_t *ru, int slot) +{ + int nbfeprx = 0; start_meas(&ru->ofdm_demod_stats); int nt = ru->nb_rx * ru->num_beams_period; @@ -384,6 +366,4 @@ void nr_fep_tp(RU_t *ru, int slot) { join_task_ans(&ans); stop_meas(&ru->ofdm_demod_stats); - if (ru->idx == 0) VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_RU_FEPRX, 0 ); } - diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index 5f75a04b05..ead0b624f7 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -28,7 +28,6 @@ #include "PHY/NR_ESTIMATION/nr_ul_estimation.h" #include "nfapi/open-nFAPI/nfapi/public_inc/nfapi_interface.h" #include "common/utils/LOG/log.h" -#include "common/utils/LOG/vcd_signal_dumper.h" #include "PHY/INIT/nr_phy_init.h" #include "PHY/MODULATION/nr_modulation.h" #include "T.h" @@ -264,8 +263,6 @@ void phy_procedures_gNB_TX(PHY_VARS_gNB *gNB, if ((cfg->cell_config.frame_duplex_type.value == TDD) && (nr_slot_select(cfg,frame,slot) == NR_UPLINK_SLOT)) return; - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_gNB_TX + gNB->CC_id, 1); - // clear the transmit data array and beam index for the current slot for (int i = 0; i < gNB->common_vars.num_beams_period; i++) { for (int aa = 0; aa < cfg->carrier_config.num_tx_ant.value; aa++) { @@ -326,10 +323,8 @@ void phy_procedures_gNB_TX(PHY_VARS_gNB *gNB, } if (num_pdsch > 0) { - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_GENERATE_DLSCH,1); LOG_D(PHY, "PDSCH generation started (%d) in frame %d.%d\n", num_pdsch, frame, slot); nr_generate_pdsch(gNB, num_pdsch, gNB->dlsch, frame, slot); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_GENERATE_DLSCH,0); } //apply the OFDM symbol rotation here @@ -354,8 +349,6 @@ void phy_procedures_gNB_TX(PHY_VARS_gNB *gNB, } } stop_meas(&gNB->phase_comp_stats); - - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_gNB_TX + gNB->CC_id, 0); } static int nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, bool *ulsch_to_decode, NR_UL_IND_t *UL_INFO) @@ -1002,7 +995,6 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N const uint16_t ofdm_symbol_size = frame_parms->ofdm_symbol_size; const int nb_symb = frame_parms->symbols_per_slot; const uint8_t nb_antennas_rx = frame_parms->nb_antennas_rx; - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_gNB_UESPEC_RX,1); LOG_D(PHY,"phy_procedures_gNB_uespec_RX frame %d, slot %d\n",frame_rx,slot_rx); { // Mask of occupied RBs, per symbol and PRB @@ -1115,7 +1107,6 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N pusch_decode_done = 1; - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_NR_RX_PUSCH, 1); start_meas(&gNB->rx_pusch_stats); nr_rx_pusch_tp(gNB, ULSCH_id, frame_rx, slot_rx, ulsch->harq_pid, ulsch->beam_nb); NR_gNB_PUSCH *pusch_vars = &gNB->pusch_vars[ULSCH_id]; @@ -1162,7 +1153,6 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N } ulsch_to_decode[ULSCH_id] = true; stop_meas(&gNB->rx_pusch_stats); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_NR_RX_PUSCH, 0); // LOG_M("rxdataF_comp.m","rxF_comp",gNB->pusch_vars[0]->rxdataF_comp[0],6900,1,1); // LOG_M("rxdataF_ext.m","rxF_ext",gNB->pusch_vars[0]->rxdataF_ext[0],6900,1,1); } @@ -1394,7 +1384,6 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N T_BUFFER(&gNB->common_vars.rxdataF[0][0][0], nb_symb * ofdm_symbol_size * 4)); } - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_gNB_UESPEC_RX,0); return pusch_DTX; } diff --git a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c index c21dafc1b3..c080076b4a 100644 --- a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c +++ b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c @@ -70,7 +70,6 @@ fifo_dump_emos_UE emos_dump_UE; #endif -#include "common/utils/LOG/vcd_signal_dumper.h" #include "UTIL/OPT/opt.h" #include "intertask_interface.h" #include "T.h" @@ -372,7 +371,6 @@ void phy_procedures_nrUE_TX(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, n AssertFatal(ue->CC_id == 0, "Transmission on secondary CCs is not supported yet\n"); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_UE_TX,VCD_FUNCTION_IN); #if T_TRACER T(T_UE_PHY_UL_TICK, T_INT(ue->Mod_id), T_INT(frame_tx % 1024), T_INT(slot_tx)); #endif @@ -416,7 +414,6 @@ void phy_procedures_nrUE_TX(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, n LOG_D(PHY, "****** end TX-Chain for AbsSubframe %d.%d ******\n", proc->frame_tx, proc->nr_slot_tx); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_UE_TX, VCD_FUNCTION_OUT); stop_meas_nr_ue_phy(ue, PHY_PROC_TX); } @@ -430,7 +427,6 @@ static void nr_ue_measurement_procedures(uint16_t l, NR_DL_FRAME_PARMS *frame_parms=&ue->frame_parms; int nr_slot_rx = proc->nr_slot_rx; int gNB_id = proc->gNB_id; - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_MEASUREMENT_PROCEDURES, VCD_FUNCTION_IN); if (l == 2) { LOG_D(PHY,"Doing UE measurement procedures in symbol l %u Ncp %d nr_slot_rx %d, rxdata %p\n", @@ -459,18 +455,10 @@ static void nr_ue_measurement_procedures(uint16_t l, if (( nr_slot_rx == 2) && (l==(2-frame_parms->Ncp))) { // AGC - - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_GAIN_CONTROL, VCD_FUNCTION_IN); - - //printf("start adjust gain power avg db %d\n", ue->measurements.rx_power_avg_dB[gNB_id]); phy_adjust_gain_nr (ue,ue->measurements.rx_power_avg_dB[gNB_id],gNB_id); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_GAIN_CONTROL, VCD_FUNCTION_OUT); - -} - - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_MEASUREMENT_PROCEDURES, VCD_FUNCTION_OUT); + } } static int nr_ue_pbch_procedures(PHY_VARS_NR_UE *ue, @@ -487,8 +475,6 @@ static int nr_ue_pbch_procedures(PHY_VARS_NR_UE *ue, int nr_slot_rx = proc->nr_slot_rx; int gNB_id = proc->gNB_id; - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_PBCH_PROCEDURES, VCD_FUNCTION_IN); - LOG_D(PHY,"[UE %d] Frame %d Slot %d, Trying PBCH (NidCell %d, gNB_id %d)\n",ue->Mod_id,frame_rx,nr_slot_rx,ue->frame_parms.Nid_cell,gNB_id); fapiPbch_t result; int hf_frame_bit, ssb_index, symb_offset; @@ -525,7 +511,6 @@ static int nr_ue_pbch_procedures(PHY_VARS_NR_UE *ue, } else { LOG_E(PHY, "[UE %d] frame %d, nr_slot_rx %d, Error decoding PBCH!\n", ue->Mod_id, frame_rx, nr_slot_rx); } - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_PBCH_PROCEDURES, VCD_FUNCTION_OUT); TracyCZoneEnd(ctx); return ret; } @@ -1086,7 +1071,6 @@ int pbch_processing(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr_phy_da int sampleShift = INT_MAX; nr_ue_dlsch_init(phy_data->dlsch, NR_MAX_NB_LAYERS>4 ? 2:1, ue->max_ldpc_iterations); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_UE_RX, VCD_FUNCTION_IN); #if T_TRACER T(T_UE_PHY_DL_TICK, T_INT(ue->Mod_id), T_INT(frame_rx % 1024), T_INT(nr_slot_rx)); #endif @@ -1111,7 +1095,6 @@ int pbch_processing(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr_phy_da int ssb_slot_2 = (ssb_period == 0) ? ssb_slot + (fp->slots_per_frame >> 1) : -1; if (ssb_slot == nr_slot_rx || ssb_slot_2 == nr_slot_rx) { - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_SLOT_FEP_PBCH, VCD_FUNCTION_IN); LOG_D(PHY," ------ PBCH ChannelComp/LLR: frame.slot %d.%d ------ \n", frame_rx%1024, nr_slot_rx); __attribute__ ((aligned(32))) struct complex16 dl_ch_estimates[fp->nb_antennas_rx][estimateSz]; @@ -1189,7 +1172,6 @@ int pbch_processing(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr_phy_da LOG_D(PHY, "Doing N0 measurements in %s\n", __FUNCTION__); nr_ue_rrc_measurements(ue, proc, rxdataF); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_SLOT_FEP_PBCH, VCD_FUNCTION_OUT); } } } @@ -1279,7 +1261,6 @@ void pdsch_processing(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr_phy_ } if (dlsch[0].active) { - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_SLOT_FEP_PDSCH, VCD_FUNCTION_IN); fapi_nr_dl_config_dlsch_pdu_rel15_t *dlsch_config = &dlsch[0].dlsch_config; uint16_t nb_symb_sch = dlsch_config->number_symbols; uint16_t start_symb_sch = dlsch_config->start_symbol; @@ -1292,7 +1273,6 @@ void pdsch_processing(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr_phy_ slot_fep_map[m] = true; } } - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_SLOT_FEP_PDSCH, VCD_FUNCTION_OUT); const uint8_t nb_re_dmrs = get_num_dmrs_re_per_rb(dlsch_config->dmrsConfigType, dlsch_config->n_dmrs_cdm_groups); uint16_t dmrs_len = get_num_dmrs(dlsch_config->dlDmrsSymbPos); @@ -1322,18 +1302,13 @@ void pdsch_processing(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr_phy_ for (int i = 0; i < nb_codewords; i++) llr[i] = (int16_t *)malloc16_clear(rx_llr_buf_sz * sizeof(int16_t)); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PDSCH_PROC_C, VCD_FUNCTION_IN); // it returns -1 in case of internal failure, or 0 in case of normal result int ret_pdsch = nr_ue_pdsch_procedures(ue, proc, dlsch, llr, rxdataF, G); TracyCPlot("pdsch mcs", dlsch->dlsch_config.mcs); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PDSCH_PROC_C, VCD_FUNCTION_OUT); - UEscopeCopy(ue, pdschLlr, llr[0], sizeof(int16_t), 1, G, 0); LOG_D(PHY, "DLSCH data reception at nr_slot_rx: %d\n", nr_slot_rx); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PDSCH_PROC, VCD_FUNCTION_IN); - start_meas_nr_ue_phy(ue, DLSCH_PROCEDURES_STATS); if (ret_pdsch >= 0) { @@ -1361,7 +1336,6 @@ void pdsch_processing(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr_phy_ if (ue->phy_sim_pdsch_llr) memcpy(ue->phy_sim_pdsch_llr, llr[0], sizeof(int16_t) * rx_llr_buf_sz); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PDSCH_PROC, VCD_FUNCTION_OUT); for (int i=0; iframe_parms.nb_antennas_rx, rxdataF_sz, 0); } @@ -1403,8 +1374,6 @@ static void nr_ue_prach_procedures(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t * int frame_tx = proc->frame_tx, nr_slot_tx = proc->nr_slot_tx, prach_power; // tx_amp uint8_t mod_id = ue->Mod_id; - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_UE_TX_PRACH, VCD_FUNCTION_IN); - NR_UE_PRACH *prach_var = ue->prach_vars[gNB_id]; if (prach_var->active) { fapi_nr_ul_config_prach_pdu *prach_pdu = &prach_var->prach_pdu; @@ -1423,8 +1392,6 @@ static void nr_ue_prach_procedures(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t * prach_var->amp = AMP; - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_GENERATE_PRACH, VCD_FUNCTION_IN); - start_meas_nr_ue_phy(ue, PRACH_GEN_STATS); prach_power = generate_nr_prach(ue, gNB_id, frame_tx, nr_slot_tx, txData); stop_meas_nr_ue_phy(ue, PRACH_GEN_STATS); @@ -1436,8 +1403,6 @@ static void nr_ue_prach_procedures(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t * ue->phy_cpu_stats.cpu_time_stats[PRACH_GEN_STATS].p_time / (cpuf * 1000.0)); } - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_GENERATE_PRACH, VCD_FUNCTION_OUT); - LOG_D(PHY, "In %s: [UE %d][RAPROC][%d.%d]: Generated PRACH Msg1 (TX power PRACH %d dBm, digital power %d dBW (amp %d)\n", __FUNCTION__, @@ -1457,7 +1422,4 @@ static void nr_ue_prach_procedures(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t * // set as inactive in the last slot prach_var->active = !(nr_slot_tx == (prach_pdu->prach_slot + prach_var->num_prach_slots - 1)); } - - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_UE_TX_PRACH, VCD_FUNCTION_OUT); - } diff --git a/openair1/SCHED_NR_UE/phy_procedures_nr_ue_sl.c b/openair1/SCHED_NR_UE/phy_procedures_nr_ue_sl.c index dbdaaa4689..1e2151d1d3 100644 --- a/openair1/SCHED_NR_UE/phy_procedures_nr_ue_sl.c +++ b/openair1/SCHED_NR_UE/phy_procedures_nr_ue_sl.c @@ -24,7 +24,6 @@ #include "PHY/defs_nr_UE.h" #include #include "common/utils/LOG/log.h" -#include "common/utils/LOG/vcd_signal_dumper.h" #include "UTIL/OPT/opt.h" #include "intertask_interface.h" #include "T.h" @@ -121,8 +120,6 @@ static int nr_ue_psbch_procedures(PHY_VARS_NR_UE *ue, sl_nr_ue_phy_params_t *sl_phy_params = &ue->SL_UE_PHY_PARAMS; uint16_t rx_slss_id = sl_phy_params->sl_config.sl_sync_source.rx_slss_id; - // VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_PSBCH_PROCEDURES, VCD_FUNCTION_IN); - LOG_D(NR_PHY, "[UE %d] Frame %d Slot %d, Trying PSBCH (SLSS ID %d)\n", ue->Mod_id, @@ -165,7 +162,6 @@ static int nr_ue_psbch_procedures(PHY_VARS_NR_UE *ue, if (ue->if_inst && ue->if_inst->sl_indication) ue->if_inst->sl_indication(&sl_indication); - // VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_PSBCH_PROCEDURES, VCD_FUNCTION_OUT); return ret; } @@ -177,7 +173,6 @@ int psbch_pscch_processing(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr NR_DL_FRAME_PARMS *fp = &sl_phy_params->sl_frame_params; int sampleShift = INT_MAX; - // VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_UE_RX_SL, VCD_FUNCTION_IN); start_meas(&sl_phy_params->phy_proc_sl_rx); LOG_D(NR_PHY, " ****** Sidelink RX-Chain for Frame.Slot %d.%d ****** \n", frame_rx % 1024, nr_slot_rx); @@ -188,7 +183,6 @@ int psbch_pscch_processing(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr if (phy_data->sl_rx_action == SL_NR_CONFIG_TYPE_RX_PSBCH) { const int estimateSz = fp->symbols_per_slot * fp->ofdm_symbol_size; - // VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_SLOT_FEP_PSBCH, VCD_FUNCTION_IN); LOG_D(NR_PHY, " ----- PSBCH RX TTI: frame.slot %d.%d ------ \n", frame_rx % 1024, nr_slot_rx); __attribute__((aligned(32))) struct complex16 dl_ch_estimates[fp->nb_antennas_rx][estimateSz]; @@ -243,7 +237,6 @@ int psbch_pscch_processing(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr LOG_D(NR_PHY, "Doing N0 measurements in %s\n", __FUNCTION__); // nr_ue_rrc_measurements(ue, proc, rxdataF); - // VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_SLOT_FEP_PSBCH, VCD_FUNCTION_OUT); if (frame_rx % 64 == 0) { LOG_I(NR_PHY, "============================================\n"); @@ -275,8 +268,6 @@ void phy_procedures_nrUE_SL_TX(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc sl_nr_ue_phy_params_t *sl_phy_params = &ue->SL_UE_PHY_PARAMS; NR_DL_FRAME_PARMS *fp = &sl_phy_params->sl_frame_params; - // VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_UE_TX_SL,VCD_FUNCTION_IN); - const int samplesF_per_slot = NR_SYMBOLS_PER_SLOT * fp->ofdm_symbol_size; c16_t txdataF_buf[fp->nb_antennas_tx * samplesF_per_slot] __attribute__((aligned(32))); memset(txdataF_buf, 0, sizeof(txdataF_buf)); @@ -326,8 +317,5 @@ void phy_procedures_nrUE_SL_TX(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc } LOG_D(NR_PHY, "****** end Sidelink TX-Chain for AbsSubframe %d.%d ******\n", frame_tx, slot_tx); - - // VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_UE_TX_SL, VCD_FUNCTION_OUT); stop_meas(&sl_phy_params->phy_proc_sl_tx); - } diff --git a/openair1/SIMULATION/CMakeLists.txt b/openair1/SIMULATION/CMakeLists.txt index b93118ccb7..a9497b0658 100644 --- a/openair1/SIMULATION/CMakeLists.txt +++ b/openair1/SIMULATION/CMakeLists.txt @@ -1,3 +1,4 @@ if(ENABLE_PHYSIM_TESTS) add_subdirectory(tests) + add_subdirectory(TOOLS) endif() diff --git a/openair1/SIMULATION/NR_PHY/dlschsim.c b/openair1/SIMULATION/NR_PHY/dlschsim.c index cd952a2ecb..fa1ee27db1 100644 --- a/openair1/SIMULATION/NR_PHY/dlschsim.c +++ b/openair1/SIMULATION/NR_PHY/dlschsim.c @@ -28,7 +28,6 @@ #include "common/ran_context.h" #include "common/config/config_userapi.h" #include "common/utils/LOG/log.h" -#include "common/utils/LOG/vcd_signal_dumper.h" #include "common/utils/load_module_shlib.h" #include "T.h" #include "PHY/defs_gNB.h" @@ -143,7 +142,7 @@ int main(int argc, char **argv) randominit(); int c; - while ((c = getopt(argc, argv, "--:O:df:hpVg:i:j:n:l:m:r:s:S:y:z:M:N:F:R:P:L:X:")) != -1) { + while ((c = getopt(argc, argv, "--:O:df:hpg:i:j:n:l:m:r:s:S:y:z:M:N:F:R:P:L:X:")) != -1) { /* ignore long options starting with '--' and their arguments that are handled by configmodule */ /* with this opstring getopt returns 1 for non-option arguments, refer to 'man 3 getopt' */ @@ -215,10 +214,6 @@ int main(int argc, char **argv) #endif break; - case 'V': - ouput_vcd = 1; - break; - case 'S': snr1 = atof(optarg); snr1set = 1; @@ -319,7 +314,6 @@ int main(int argc, char **argv) printf("%s -h(elp) -p(extended_prefix) -N cell_id -f output_filename -F input_filename -g channel_model -n n_frames -t Delayspread -s snr0 -S snr1 -y TXant -z RXant -i Intefrence0 -j Interference1 -A interpolation_file -C(alibration offset dB) -N CellId\n", argv[0]); printf("-h This message\n"); printf("-p Use extended prefix mode\n"); - printf("-V Enable VCD dumb functions\n"); //printf("-d Use TDD\n"); printf("-n Number of frames to simulate\n"); printf("-s Starting SNR, runs from SNR0 to SNR0 + 5 dB. If n_frames is 1 then just SNR is simulated\n"); @@ -351,9 +345,6 @@ int main(int argc, char **argv) if (snr1set == 0) snr1 = snr0 + 10; - if (ouput_vcd) - vcd_signal_dumper_init("/tmp/openair_dump_nr_dlschsim.vcd"); - gNB2UE = new_channel_desc_scm(n_tx, n_rx, channel_model, @@ -562,8 +553,6 @@ int main(int argc, char **argv) exit(-1); #endif - vcd_signal_dumper_dump_function_by_name(VCD_SIGNAL_DUMPER_FUNCTIONS_DLSCH_DECODING0, VCD_FUNCTION_IN); - int a_segments = MAX_NUM_NR_DLSCH_SEGMENTS_PER_LAYER*NR_MAX_NB_LAYERS; //number of segments to be allocated int num_rb = dlsch0_ue->dlsch_config.number_rbs; if (num_rb != 273) { @@ -585,8 +574,6 @@ int main(int argc, char **argv) 1, DLSCH_ids); - vcd_signal_dumper_dump_function_by_name(VCD_SIGNAL_DUMPER_FUNCTIONS_DLSCH_DECODING0, VCD_FUNCTION_OUT); - if (dlsch0_ue->last_iteration_cnt > dlsch0_ue->max_ldpc_iterations) n_errors++; @@ -657,8 +644,6 @@ int main(int argc, char **argv) if (input_fd) fclose(input_fd); - if (ouput_vcd) - vcd_signal_dumper_close(); end_configmodule(uniqCfg); loader_reset(); logTerm(); diff --git a/openair1/SIMULATION/NR_PHY/srssim.c b/openair1/SIMULATION/NR_PHY/srssim.c index 0a284a2f6d..f791173e39 100644 --- a/openair1/SIMULATION/NR_PHY/srssim.c +++ b/openair1/SIMULATION/NR_PHY/srssim.c @@ -68,6 +68,8 @@ nrUE_params_t *get_nrUE_params(void) channel_desc_t *UE2gNB[MAX_MOBILES_PER_GNB][NUMBER_OF_gNB_MAX]; configmodule_interface_t *uniqCfg = NULL; +NR_IF_Module_t *NR_IF_Module_init(int Mod_id) { return (NULL); } + void e1_bearer_context_setup(const e1ap_bearer_setup_req_t *req) { abort(); diff --git a/openair1/SIMULATION/TOOLS/CMakeLists.txt b/openair1/SIMULATION/TOOLS/CMakeLists.txt new file mode 100644 index 0000000000..d104c0ccdc --- /dev/null +++ b/openair1/SIMULATION/TOOLS/CMakeLists.txt @@ -0,0 +1,34 @@ +#/* +# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more +# * contributor license agreements. See the NOTICE file distributed with +# * this work for additional information regarding copyright ownership. +# * The OpenAirInterface Software Alliance licenses this file to You under +# * the OAI Public License, Version 1.1 (the "License"); you may not use this file +# * except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.openairinterface.org/?page_id=698 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +# *------------------------------------------------------------------------------- +# * For more information about the OpenAirInterface (OAI) Software Alliance: +# * contact@openairinterface.org +# */ + +if(CUDA_ENABLE) + add_library(oai_cuda_lib STATIC + multipath_channel.cu + phase_noise.cu + channel_pipeline.cu + ) + + target_link_libraries(oai_cuda_lib PUBLIC CUDA::toolkit) + + target_include_directories(oai_cuda_lib PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} + ) +endif() diff --git a/openair1/SIMULATION/TOOLS/DOC/gpu_acceleration.md b/openair1/SIMULATION/TOOLS/DOC/gpu_acceleration.md new file mode 100644 index 0000000000..a34d289bb0 --- /dev/null +++ b/openair1/SIMULATION/TOOLS/DOC/gpu_acceleration.md @@ -0,0 +1,212 @@ +# GPU-Accelerated Channel Simulation + +[[_TOC_]] + +## Overview + +This document describes the CUDA-based GPU acceleration pipeline for the OAI channel simulation. The primary goal of this feature is to offload the computationally intensive `multipath_channel` convolution and `add_noise` functions from the CPU to the GPU. This overcomes existing performance bottlenecks and enables large-scale, real-time physical layer simulation that is not feasible with the CPU-based models. + +The result is a complete, high-performance pipeline integrated into the OAI simulators. The feature is supported by a new benchmark suite created to validate correctness and analyze performance across a wide range of channel models and hardware configurations. + +## Implementation + +This feature is implemented across a set of new CUDA source files and integrated into the existing OAI simulation framework. + +### Multipath Channel Convolution (`multipath_channel.cu`) + +The core of the channel simulation is the multipath convolution. On the CPU, this is a sequential process where each output sample is calculated by iterating through all channel taps and accumulating the results. To parallelize this on the GPU, we assign one CUDA thread to calculate one output sample. + +The key to making this parallel approach efficient is the use of a tiled convolution pattern with `__shared__` memory. Instead of having every thread read from slow global memory for each step of the convolution, threads in a block cooperate to pre-fetch all the data they will need into the on-chip shared memory. + +To further optimize this, the responsibility for handling boundary conditions (the "halo" of past samples) has been moved from the GPU kernel to the host. The host code now prepares a larger input buffer that is **pre-padded** with `channel_length - 1` zeros before the actual signal data. This allows the kernel to be significantly simplified by removing the previous boundary-checking logic. + +```cpp +// Snippet from the multipath_channel_kernel in multipath_channel.cu + +// Step 1: Each thread cooperates to load a tile of the pre-padded signal +// into fast shared memory. +const int padding_len = channel_length - 1; +const int padded_num_samples = num_samples + padding_len; + +for (int k = tid; k < shared_mem_size; k += blockDim.x) { + int load_idx = block_start_idx + k; + // Unconditional load from the padded buffer + int interleaved_idx = 2 * (j * padded_num_samples + load_idx); + tx_shared[k] = make_float2(tx_sig[interleaved_idx], tx_sig[interleaved_idx + 1]); +} +__syncthreads(); +``` + +Once the data is staged in fast shared memory, each thread can perform the full convolution for its assigned output sample. The inner loop iterates through the `channel_length` taps, and all reads of the input signal (`tx_sample`) come from the `tx_shared` buffer. + +```cpp +// Snippet from multipath_channel_kernel in multipath_channel.cu +// Step 2: Each thread performs convolution using the data in shared memory + for (int l = 0; l < channel_length; l++) { + // Read the historical transmit sample from the shared memory + float2 tx_sample = tx_shared[tid + (channel_length - 1) - l]; + + // Get the corresponding channel weight from global memory + int chan_link_idx = ii + (j * nb_rx); + float2 chan_weight = d_channel_coeffs[chan_link_idx * channel_length + l]; + + // Perform the complex multiply-accumulate + rx_tmp = complex_add(rx_tmp, complex_mul(tx_sample, chan_weight)); + } + __syncthreads(); +} + +// Write the final result from the thread's private register to global memory... +``` + +### Noise Generation (`phase_noise.cu`) + +After the multipath convolution, the next stage of the pipeline is to add realistic noise to the signal. This includes both Additive White Gaussian Noise (AWGN) and phase noise. + +The CPU implementation performs this in a simple loop. For each sample of the signal, it calls a standard C library function to generate pseudo-random numbers, scales them appropriately, and adds them to the sample. This process is sequential and computationally inexpensive for a single signal but becomes a bottleneck when simulating many channels that all require high-quality random numbers simultaneously. + +The GPU port follows the same parallelization strategy as the multipath kernel: one CUDA thread is assigned to process one single sample. The main challenge is generating millions of high-quality random numbers in parallel. To achieve this, the implementation relies on NVIDIA's **`cuRAND`** library. + +The `cuRAND` library is stateful, meaning each thread needs its own unique generator "state" to produce an independent sequence of random numbers. In our implementation, a pool of `curandState_t` objects is initialized once in global memory. When the noise kernel is launched, each thread is assigned a unique state from this pool. The thread uses its state to generate random numbers, and then writes the updated state back to global memory for the next use. + +A simplified view of this logic within the `add_noise_and_phase_noise_kernel` is shown below: + +```cpp +// Simplified snippet from add_noise_and_phase_noise_kernel in phase_noise.cu +__global__ void add_noise_and_phase_noise_kernel(...) +{ + // Each thread calculates its unique sample index 'i' and antenna index 'ii'... + + // Load this thread's unique random number generator state + curandState_t local_state = states[ii * num_samples + i]; + + // Generate random numbers using the local state + // Generates two normal-distributed numbers for AWGN (real and imaginary) + float2 awgn = curand_normal2(&local_state); + // Generates one normal-distributed number for phase noise + float phase_error = curand_normal(&local_state) * pn_std_dev; + + // Apply noise to the input signal from the previous kernel stage, and then aplu phase_error + + // Write the updated state back to global memory for the next simulation run + states[ii * num_samples + i]= local_state; + + // Clamp the final floating-point value and store it as a short integer + output_sig[ii * num_samples + i]= make_short2(...); +} +``` + +### Pipeline Orchestration (`channel_pipeline.cu`) + +The `channel_pipeline.cu` file serves two primary functions: providing an interoperable API and orchestrating kernel execution. + +Its first purpose is to act as an abstraction layer between the host C/C++ code of the OAI simulator and the CUDA C++ device code. It exposes a C-style Application Programming Interface (API) using `extern "C"`, which allows the standard C-based components to call the GPU pipeline without needing to handle CUDA-specific syntax or complexities. + +Its second purpose is to orchestrate the execution of the individual CUDA kernels. It defines the logical flow of the channel simulation by launching the `multipath_channel_kernel` and `add_noise_and_phase_noise_kernel` in sequence and managing the data dependencies between them. + +The functions within this file, such as `run_channel_pipeline_cuda`, implement a consistent workflow. Upon being called, they first cast the generic `void*` pointers received from the C host into their specific CUDA data types. The implementation then contains the conditional logic, via preprocessor directives, to support the various memory models. Following data setup, the functions define the CUDA execution grid and block dimensions and launch the two kernels sequentially. The output of the multipath kernel is directed to an intermediate buffer, which serves as the input for the subsequent noise kernel. Finally, the functions manage synchronization with the host and handle the transfer of the final results from the GPU back to host memory. + +*The external API of these pipeline functions has been streamlined. The function signatures no longer require a separate `tx_sig_interleaved` parameter, instead relying on a single, pre-padded host buffer (`h_tx_sig_pinned`) as the authoritative source for the input signal, which simplifies the data flow from the simulators.* + +--- + +## Memory Management Models + +To provide flexibility and enable performance testing on different hardware architectures, the project now supports three distinct memory management models. The desired model is selected at build time by passing a flag to the CMake build command. + +### ATS Hybrid Model (`-DUSE_ATS_MEMORY=ON`) + +This is a hybrid approach that leverages Address Translation Services (ATS), a hardware feature allowing the GPU to directly access host-allocated memory. In our implementation, the large input signal buffer is allocated in host memory using `malloc`. The GPU kernel is then able to read this data directly from the host, eliminating the need for an initial Host-to-Device `cudaMemcpy` for that buffer. Intermediate and output buffers are still allocated on the device, and the final result is copied back to the host explicitly. Due to its better performance compared to the other methods, it's also the default behaviour when no flag is specified. + +### Unified Memory Model (`-DUSE_UNIFIED_MEMORY=ON`) + +This model simplifies memory management by creating a single, managed memory space that is accessible to both the CPU and the GPU. Memory is allocated once using `cudaMallocManaged`. The CUDA runtime and driver then automatically handle the migration of data to whichever processor is accessing it (e.g., moving data to the GPU's memory when a kernel is launched). This removes the need for most explicit `cudaMemcpy` calls. While the CUDA driver automatically migrates data on-demand, the implementation includes further optimizations to guide this process for better performance. `cudaMemAdvise` is used to provide performance hints to the driver, such as setting a buffer's preferred location to the GPU or marking it as mostly read-only. Additionally, `cudaMemPrefetchAsync` is also used to explicitly move data to the GPU's memory ahead of time. This ensures the data is already local when a kernel begins execution, which helps to hide the latency of the data migration. + +### Explicit Copy Model + +This is the traditional CUDA programming model. The host and device have separate and distinct memory spaces. The programmer is responsible for all data management. Memory must be allocated on the device using `cudaMalloc`, and data must be manually transferred between the host and device using explicit `cudaMemcpy` calls before and after a kernel launch. This configuration can be run with other flags turned off by using `-DUSE_ATS_MEMORY=OFF` and `-DUSE_UNIFIED_MEMORY=OFF`. + +----- + +## Project Integration + +To ensure the feature is modular, the integration into the OAI project was handled at both the build system and source code levels. All of the new CUDA source files (`channel_pipeline.cu`, `multipath_channel.cu`, `phase_noise.cu`) are compiled into a single static library named `oai_cuda_lib`. This is defined in the main `CMakeLists.txt` file and is controlled by the `CUDA_ENABLE` CMake option, which is disabled by default. When this option is activated (`-DCUDA_ENABLE=ON`), the build system compiles the CUDA library and also defines a global `ENABLE_CUDA` preprocessor macro that is visible to the rest of the project. + +--- + +## Benchmark and Analysis Suite + +To validate the correctness, measure performance, and analyze the scalability of the GPU pipeline, a dedicated suite of tests was developed. The source code for these benchmarks is located in the `openair1/PHY/TOOLS/tests/` directory. This suite includes both focused unitary tests for individual components and a comprehensive benchmark for the end-to-end pipeline, which is the primary tool for performance evaluation. + +### `test_channel_scalability.c` + +This program is the main tool for evaluating the end-to-end GPU channel simulation pipeline. It's designed to be highly configurable, allowing for the simulation of a wide variety of workloads and execution strategies. + +#### Configuration and Usage + +The benchmark is configured at runtime using a set of command-line flags. + +| Flag | Long Version | Argument | Description | +| :--- | :--- | :--- | :--- | +| `-c` | `--num-channels` | `` | Sets the number of independent channels to simulate. | +| `-t` | `--nb-tx` | `` | Sets the number of transmit antennas. | +| `-r` | `--nb-rx` | `` | Sets the number of receive antennas. | +| `-s` | `--num-samples` | `` | Sets the number of samples per signal. | +| `-l` | `--ch-len` | `` | Sets the length of the channel impulse response. | +| `-n` | `--trials` | `` | Sets the number of times each test is run for averaging. | +| `-S` | `--sum-outputs` | (none) | Enables interference simulation by summing channel outputs. (Using unique input signals) | +| `-m` | `--mode` | `` | Sets the GPU execution mode: `serial`, `stream`, or `batch`. | +| `-h` | `--help` | (none) | Displays the help message with all options. | + +#### Execution Modes + +The `-m` or `--mode` flag controls the strategy used to execute the pipeline for multiple channels: + + * **`serial`**: This mode processes each channel sequentially. It loops through all channels and makes a synchronous call to `run_channel_pipeline_cuda` for each one, waiting for it to complete before starting the next. + * **`stream`**: This mode is designed to test concurrency. It launches all channel simulations asynchronously into separate CUDA streams by calling `run_channel_pipeline_cuda_streamed` in a loop. This allows the GPU hardware to overlap the execution of many kernels. + * **`batch`**: It aggregates the data for all channels on the host and makes a single call to `run_channel_pipeline_cuda_batched`. This launches one massive, unified kernel to process all channels at once, minimizing CPU overhead and maximizing GPU throughput. + +### `test_channel_simulation.c` + +The `test_channel_simulation.c` program was one of the initial benchmarks created for this project. Its primary function is to perform a direct performance comparison between the full, sequential CPU-based channel pipeline and the original synchronous GPU-based pipeline implemented in `run_channel_pipeline_cuda`. It served as an early-stage tool to validate the end-to-end functionality of the basic GPU port and to obtain initial speedup measurements before more advanced optimizations were developed. + +### `test_multipath.c` + +The `test_multipath.c` program is a unitary test designed specifically to validate the `multipath_channel_cuda` function in isolation. The key feature of this test is the `verify_results` function, which performs a sample-by-sample comparison of the output arrays from both versions and calculates the Mean Squared Error (MSE). If the error is below a predefined tolerance, the test is marked as *"PASSED"* in the final report, confirming that the GPU kernel is producing numerically correct results. + +### `test_noise.c` + +Similar to the multipath test, `test_noise.c` is a unitary test that focuses on a single component: the `add_noise_cuda` function. Its purpose is to validate the correctness of the GPU-based noise generation and measure its performance against the equivalent CPU version. + +----- + +### Direct CPU vs. GPU Speedup + +To provide a clear, baseline performance comparison, the `test_channel_simulation` benchmark was run. This tool directly compares the execution time of the sequential, `float`-based CPU pipeline against the baseline synchronous GPU pipeline (`run_channel_pipeline_cuda`). The following tests were executed on a GH200 server using the ATS memory model. The results demonstrate the performance gains achieved by offloading the channel simulation to the GPU. + +| Channel Type | MIMO Config | Signal Length | CPU Pipeline (µs) | GPU Pipeline (µs) | Overall Speedup | +| :--- | :--- | :--- | :--- | :--- | :--- | +Short Channel | 1x1 | 30720 | 654.12 | 45.60 | 14.34 x +Short Channel | 2x2 | 30720 | 2145.07 | 57.48 | 37.32 x +Short Channel | 4x4 | 30720 | 7611.24 | 84.20 | 90.40 x +Short Channel | 8x8 | 30720 | 28190.40 | 148.07 | 190.39 x +Short Channel | 1x1 | 61440 | 1305.85 | 54.16 | 24.11 x +Short Channel | 2x2 | 61440 | 4290.31 | 80.34 | 53.40 x +Short Channel | 4x4 | 61440 | 15264.63 | 136.62 | 111.73 x +Short Channel | 8x8 | 61440 | 56561.05 | 255.82 | 221.10 x +Short Channel | 1x1 | 122880 | 2617.06 | 79.33 | 32.99 x +Short Channel | 2x2 | 122880 | 8585.16 | 129.69 | 66.20 x +Short Channel | 4x4 | 122880 | 30543.07 | 233.36 | 130.88 x +Short Channel | 8x8 | 122880 | 113185.64 | 468.10 | 241.80 x +Long Channel | 1x1 | 30720 | 1068.25 | 44.37 | 24.08 x +Long Channel | 2x2 | 30720 | 3795.86 | 58.86 | 64.49 x +Long Channel | 4x4 | 30720 | 14209.40 | 89.08 | 159.51 x +Long Channel | 8x8 | 30720 | 54579.90 | 164.72 | 331.36 x +Long Channel | 1x1 | 61440 | 2139.73 | 56.02 | 38.20 x +Long Channel | 2x2 | 61440 | 7608.75 | 83.18 | 91.47 x +Long Channel | 4x4 | 61440 | 28463.66 | 145.80 | 195.23 x +Long Channel | 8x8 | 61440 | 109785.85 | 289.96 | 378.62 x +Long Channel | 1x1 | 122880 | 4267.25 | 80.65 | 52.91 x +Long Channel | 2x2 | 122880 | 15209.56 | 135.12 | 112.56 x +Long Channel | 4x4 | 122880 | 56951.08 | 249.47 | 228.29 x +Long Channel | 8x8 | 122880 | 222368.44 | 524.97 | 423.59 x diff --git a/openair1/SIMULATION/TOOLS/channel_pipeline.cu b/openair1/SIMULATION/TOOLS/channel_pipeline.cu new file mode 100644 index 0000000000..81c9ba0734 --- /dev/null +++ b/openair1/SIMULATION/TOOLS/channel_pipeline.cu @@ -0,0 +1,296 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#include +#include +#include "oai_cuda.h" + +__global__ void multipath_channel_kernel_batched(const float2 *__restrict__ d_channel_coeffs, + const float2 *__restrict__ tx_sig, + float2 *__restrict__ rx_sig, + int num_samples, + int channel_length, + int nb_tx, + int nb_rx); + +__global__ void add_noise_and_phase_noise_kernel_batched(const float2 *__restrict__ r_sig, + short2 *__restrict__ output_sig, + curandState_t *states, + int num_samples, + int nb_rx, + float sigma, + float pn_std_dev, + bool apply_phase_noise); + +#define CHECK_CUDA(val) checkCuda((val), #val, __FILE__, __LINE__) +static void checkCuda(cudaError_t result, const char *const func, const char *const file, const int line) +{ + if (result != cudaSuccess) { + fprintf(stderr, + "CUDA Error at %s:%d code=%d(%s) \"%s\" \n", + file, + line, + static_cast(result), + cudaGetErrorString(result), + func); + cudaDeviceReset(); + exit(EXIT_FAILURE); + } +} + +__global__ void sum_outputs_kernel(const short2 *__restrict__ *__restrict__ individual_outputs, + short2 *__restrict__ final_summed_output, + int num_channels, + int num_samples_per_antenna) +{ + const int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i >= num_samples_per_antenna) + return; + + float2 sum = make_float2(0.0f, 0.0f); + + for (int c = 0; c < num_channels; c++) { + sum.x += individual_outputs[c][i].x; + sum.y += individual_outputs[c][i].y; + } + + final_summed_output[i].x = (short)fmaxf(-32768.0f, fminf(32767.0f, sum.x)); + final_summed_output[i].y = (short)fmaxf(-32768.0f, fminf(32767.0f, sum.y)); +} + +extern "C" { +void run_channel_pipeline_cuda(c16_t **output_signal, + int nb_tx, + int nb_rx, + int channel_length, + uint32_t num_samples, + float *h_channel_coeffs, + float sigma2, + double ts, + uint16_t pdu_bit_map, + uint16_t ptrs_bit_map, + int slot_offset, + int delay, + void *d_tx_sig_void, + void *d_intermediate_sig_void, + void *d_final_output_void, + void *d_curand_states_void, + void *h_tx_sig_pinned_void, + void *h_final_output_pinned_void, + void *d_channel_coeffs_void) +{ + // --- Cast void pointers --- + float2 *d_intermediate_sig = (float2 *)d_intermediate_sig_void; + short2 *d_final_output = (short2 *)d_final_output_void; + curandState_t *d_curand_states = (curandState_t *)d_curand_states_void; + float2 *d_channel_coeffs = (float2 *)d_channel_coeffs_void; + + const int padding_len = channel_length - 1; + const size_t padded_stride_bytes = (num_samples + padding_len) * 2 * sizeof(float); + const size_t total_padded_tx_bytes = nb_tx * padded_stride_bytes; + + float *kernel_input_ptr; +#if defined(USE_UNIFIED_MEMORY) || defined(USE_ATS_MEMORY) + kernel_input_ptr = (float *)h_tx_sig_pinned_void; +#else + float *d_tx_sig = (float *)d_tx_sig_void; + float *h_tx_sig_pinned = (float *)h_tx_sig_pinned_void; + CHECK_CUDA(cudaMemcpy(d_tx_sig, h_tx_sig_pinned, total_padded_tx_bytes, cudaMemcpyHostToDevice)); + + kernel_input_ptr = d_tx_sig; +#endif + + size_t channel_size_bytes = nb_tx * nb_rx * channel_length * sizeof(float2); + CHECK_CUDA(cudaMemcpy(d_channel_coeffs, h_channel_coeffs, channel_size_bytes, cudaMemcpyHostToDevice)); + + dim3 threads_multipath(512, 1); + dim3 blocks_multipath((num_samples + threads_multipath.x - 1) / threads_multipath.x, nb_rx); + size_t sharedMemSize = (threads_multipath.x + channel_length - 1) * sizeof(float2); + multipath_channel_kernel<<>>(d_channel_coeffs, + kernel_input_ptr, + d_intermediate_sig, + num_samples, + channel_length, + nb_tx, + nb_rx); + + dim3 threads_noise(256, 1); + dim3 blocks_noise((num_samples + threads_noise.x - 1) / threads_noise.x, nb_rx); + float pn_variance = 1e-5f * 2.0f * 3.1415926535f * 300.0f * (float)ts; + bool apply_phase_noise = (pdu_bit_map & ptrs_bit_map); + add_noise_and_phase_noise_kernel<<>>(d_intermediate_sig, + d_final_output, + d_curand_states, + num_samples, + sqrtf(sigma2 / 2.0f), + sqrtf(pn_variance), + apply_phase_noise); + + cudaDeviceSynchronize(); + + // If output_signal is NULL, the caller intends to keep the data on the GPU + // for further processing (e.g., summing outputs). Otherwise, copy back to host. + if (output_signal != NULL) { +#if defined(USE_UNIFIED_MEMORY) + short2 *h_final_output_pinned = (short2 *)h_final_output_pinned_void; + for (int ii = 0; ii < nb_rx; ii++) { + memcpy(output_signal[ii] + slot_offset + delay, h_final_output_pinned + ii * num_samples, num_samples * sizeof(short2)); + } +#else + short2 *h_final_output_pinned = (short2 *)h_final_output_pinned_void; + CHECK_CUDA(cudaMemcpy(h_final_output_pinned, d_final_output, nb_rx * num_samples * sizeof(short2), cudaMemcpyDeviceToHost)); + for (int ii = 0; ii < nb_rx; ii++) { + memcpy(output_signal[ii] + slot_offset + delay, h_final_output_pinned + ii * num_samples, num_samples * sizeof(short2)); + } +#endif + } +} + +void sum_channel_outputs_cuda(void **d_individual_outputs, void *d_final_output, int num_channels, int nb_rx, int num_samples) +{ + void **d_ptr_array; + size_t ptr_array_size = num_channels * sizeof(void *); + CHECK_CUDA(cudaMalloc(&d_ptr_array, ptr_array_size)); + + CHECK_CUDA(cudaMemcpy(d_ptr_array, d_individual_outputs, ptr_array_size, cudaMemcpyHostToDevice)); + + int num_total_samples = nb_rx * num_samples; + dim3 threads(256, 1); + dim3 blocks((num_total_samples + threads.x - 1) / threads.x, 1); + + sum_outputs_kernel<<>>((const short2 **)d_ptr_array, (short2 *)d_final_output, num_channels, num_total_samples); + + CHECK_CUDA(cudaFree(d_ptr_array)); +} + +void run_channel_pipeline_cuda_streamed(int nb_tx, + int nb_rx, + int channel_length, + uint32_t num_samples, + float *h_channel_coeffs, + float sigma2, + double ts, + uint16_t pdu_bit_map, + uint16_t ptrs_bit_map, + void *d_tx_sig_void, + void *d_intermediate_sig_void, + void *d_final_output_void, + void *d_curand_states_void, + void *h_tx_sig_pinned_void, + void *d_channel_coeffs_void, + void *stream_void) +{ + cudaStream_t stream = (cudaStream_t)stream_void; + float2 *d_intermediate_sig = (float2 *)d_intermediate_sig_void; + short2 *d_final_output = (short2 *)d_final_output_void; + curandState_t *d_curand_states = (curandState_t *)d_curand_states_void; + float2 *d_channel_coeffs = (float2 *)d_channel_coeffs_void; + float *kernel_input_ptr; + +#if defined(USE_UNIFIED_MEMORY) || defined(USE_ATS_MEMORY) + kernel_input_ptr = (float *)h_tx_sig_pinned_void; +#else + float *d_tx_sig = (float *)d_tx_sig_void; + float *h_tx_sig_pinned = (float *)h_tx_sig_pinned_void; + + const int padding_len = channel_length - 1; + const size_t total_padded_tx_bytes = nb_tx * (num_samples + padding_len) * 2 * sizeof(float); + + CHECK_CUDA(cudaMemcpyAsync(d_tx_sig, h_tx_sig_pinned, total_padded_tx_bytes, cudaMemcpyHostToDevice, stream)); + kernel_input_ptr = d_tx_sig; +#endif + + size_t channel_size_bytes = nb_tx * nb_rx * channel_length * sizeof(float2); + CHECK_CUDA(cudaMemcpyAsync(d_channel_coeffs, h_channel_coeffs, channel_size_bytes, cudaMemcpyHostToDevice, stream)); + + dim3 threads_multipath(512, 1); + dim3 blocks_multipath((num_samples + threads_multipath.x - 1) / threads_multipath.x, nb_rx); + size_t sharedMemSize = (threads_multipath.x + channel_length - 1) * sizeof(float2); + multipath_channel_kernel<<>>(d_channel_coeffs, + kernel_input_ptr, + d_intermediate_sig, + num_samples, + channel_length, + nb_tx, + nb_rx); + + dim3 threads_noise(256, 1); + dim3 blocks_noise((num_samples + threads_noise.x - 1) / threads_noise.x, nb_rx); + float pn_variance = 1e-5f * 2.0f * 3.1415926535f * 300.0f * (float)ts; + bool apply_phase_noise = (pdu_bit_map & ptrs_bit_map); + add_noise_and_phase_noise_kernel<<>>(d_intermediate_sig, + d_final_output, + d_curand_states, + num_samples, + sqrtf(sigma2 / 2.0f), + sqrtf(pn_variance), + apply_phase_noise); +} + +void run_channel_pipeline_cuda_batched(int num_channels, + int nb_tx, + int nb_rx, + int channel_length, + uint32_t num_samples, + void *d_channel_coeffs_batch, + float sigma2, + double ts, + uint16_t pdu_bit_map, + uint16_t ptrs_bit_map, + void *d_tx_sig_batch, + void *d_intermediate_sig_batch, + void *d_final_output_batch, + void *d_curand_states) +{ + float2 *d_tx = (float2 *)d_tx_sig_batch; + float2 *d_intermediate = (float2 *)d_intermediate_sig_batch; + short2 *d_final = (short2 *)d_final_output_batch; + float2 *d_coeffs = (float2 *)d_channel_coeffs_batch; + curandState_t *d_states = (curandState_t *)d_curand_states; + + dim3 threads_multipath(512, 1, 1); + dim3 blocks_multipath((num_samples + threads_multipath.x - 1) / threads_multipath.x, nb_rx, num_channels); + size_t sharedMemSize = (threads_multipath.x + channel_length - 1) * sizeof(float2); + + multipath_channel_kernel_batched<<>>(d_coeffs, + d_tx, + d_intermediate, + num_samples, + channel_length, + nb_tx, + nb_rx); + + dim3 threads_noise(256, 1, 1); + dim3 blocks_noise((num_samples + threads_noise.x - 1) / threads_noise.x, nb_rx, num_channels); + float pn_variance = 1e-5f * 2.0f * 3.1415926535f * 300.0f * (float)ts; + bool apply_phase_noise = (pdu_bit_map & ptrs_bit_map); + add_noise_and_phase_noise_kernel_batched<<>>(d_intermediate, + d_final, + d_states, + num_samples, + nb_rx, + sqrtf(sigma2 / 2.0f), + sqrtf(pn_variance), + apply_phase_noise); + + // Synchronization happens in the benchmark (test_channel_scalability) after this call returns. +} +} // extern "C" diff --git a/openair1/SIMULATION/TOOLS/multipath_channel.c b/openair1/SIMULATION/TOOLS/multipath_channel.c index aeaa4f811f..de12663f1f 100644 --- a/openair1/SIMULATION/TOOLS/multipath_channel.c +++ b/openair1/SIMULATION/TOOLS/multipath_channel.c @@ -28,147 +28,144 @@ uint8_t multipath_channel_nosigconv(channel_desc_t *desc) { - random_channel(desc,0); - return(1); + random_channel(desc, 0); + return (1); +} + +void interleave_channel_output(float **rx_sig_re, float **rx_sig_im, float **output_interleaved, int nb_rx, int num_samples) +{ + for (int i = 0; i < nb_rx; i++) { + for (int j = 0; j < num_samples; j++) { + output_interleaved[i][2 * j] = rx_sig_re[i][j]; + output_interleaved[i][2 * j + 1] = rx_sig_im[i][j]; + } + } } //#define CHANNEL_SSE + #ifdef CHANNEL_SSE -void __attribute__ ((no_sanitize_address)) multipath_channel(channel_desc_t *desc, - double tx_sig_re[NB_ANTENNAS_TX][30720*2], - double tx_sig_im[NB_ANTENANS_TX][30720*2], - double rx_sig_re[NB_ANTENNAS_RX][30720*2], - double rx_sig_im[NB_ANTENNAS_RX][30720*2], - uint32_t length, - uint8_t keep_channel, - int log_channel) +void __attribute__((no_sanitize_address)) multipath_channel(channel_desc_t *desc, + double **tx_sig_re, + double **tx_sig_im, + double **rx_sig_re, + double **rx_sig_im, + uint32_t length, + uint8_t keep_channel, + int log_channel) { + int i, ii, j, l; + // int simd_length; + // int simd_length, tail_start; + simde__m128d rx_tmp128_re, rx_tmp128_im, tx128_re, tx128_im, ch128_r, ch128_i, pathloss128; - int i,ii,j,l; - int length1, length2, tail; - simde__m128d rx_tmp128_re_f, rx_tmp128_im_f, rx_tmp128_re, rx_tmp128_im, rx_tmp128_1, rx_tmp128_2, rx_tmp128_3, rx_tmp128_4, - tx128_re, tx128_im, ch128_x, ch128_y, pathloss128; - - double path_loss = pow(10,desc->path_loss_dB/20); + double path_loss = pow(10, desc->path_loss_dB / 20); uint64_t dd = desc->channel_offset; pathloss128 = simde_mm_set1_pd(path_loss); -#ifdef DEBUG_CH - printf("[CHANNEL] keep = %d : path_loss = %g (%f), nb_rx %d, nb_tx %d, dd %lu, len %d \n",keep_channel,path_loss,desc->path_loss_dB,desc->nb_rx,desc->nb_tx,dd,desc->channel_length); -#endif - - if (keep_channel) { - // do nothing - keep channel - } else { - random_channel(desc,0); + if (keep_channel == 0) { + random_channel(desc, 0); } - start_meas(&desc->convolution); + // simd_length = (length - dd) / 2; + // tail_start = simd_length * 2; -#ifdef DEBUG_CH + for (i = 0; i < (int)(length - dd); i += 2) { + for (ii = 0; ii < desc->nb_rx; ii++) { + rx_tmp128_re = simde_mm_setzero_pd(); + rx_tmp128_im = simde_mm_setzero_pd(); - for (int l = 0; l<(int)desc->channel_length; l++) { - printf("%p (%f,%f) ",desc->ch[0],desc->ch[0][l].x,desc->ch[0][l].y); - } - - printf("\n"); -#endif - - tail = ((int)length-dd)%2; - - if(tail) - length1 = ((int)length-dd)-1; - else - length1 = ((int)length-dd); - - length2 = length1/2; - - for (i=0; inb_rx; ii++) { - // rx_tmp.x = 0; - // rx_tmp.y = 0; - rx_tmp128_re_f = simde_mm_setzero_pd(); - rx_tmp128_im_f = simde_mm_setzero_pd(); - - for (j=0; jnb_tx; j++) { - for (l = 0; l<(int)desc->channel_length; l++) { - if ((i>=0) && (i-l)>=0) { //SIMD correct only if length1 > 2*channel_length...which is almost always satisfied - // tx.x = tx_sig_re[j][i-l]; - // tx.y = tx_sig_im[j][i-l]; - tx128_re = simde_mm_loadu_pd(&tx_sig_re[j][2 * i - l]); // tx_sig_re[j][i-l+1], tx_sig_re[j][i-l] - tx128_im = simde_mm_loadu_pd(&tx_sig_im[j][2 * i - l]); - } else { - //tx.x =0; - //tx.y =0; - tx128_re = simde_mm_setzero_pd(); - tx128_im = simde_mm_setzero_pd(); + for (i = 0; i < (int)(length - dd); i += 2) { + for (ii = 0; ii < desc->nb_rx; ii++) { + rx_tmp128_re = simde_mm_setzero_pd(); + rx_tmp128_im = simde_mm_setzero_pd(); + for (j = 0; j < desc->nb_tx; j++) { + for (l = 0; l < (int)desc->channel_length; l++) { + double tx_re[2] = {0.0, 0.0}, tx_im[2] = {0.0, 0.0}; + if ((i - l) >= 0) + tx_re[0] = tx_sig_re[j][i - l]; + if ((i + 1 - l) >= 0) + tx_re[1] = tx_sig_re[j][i + 1 - l]; + if ((i - l) >= 0) + tx_im[0] = tx_sig_im[j][i - l]; + if ((i + 1 - l) >= 0) + tx_im[1] = tx_sig_im[j][i + 1 - l]; + tx128_re = simde_mm_loadu_pd(tx_re); + tx128_im = simde_mm_loadu_pd(tx_im); + ch128_r = simde_mm_set1_pd(desc->ch[ii + (j * desc->nb_rx)][l].r); + ch128_i = simde_mm_set1_pd(desc->ch[ii + (j * desc->nb_rx)][l].i); + rx_tmp128_re = + simde_mm_add_pd(rx_tmp128_re, + simde_mm_sub_pd(simde_mm_mul_pd(tx128_re, ch128_r), simde_mm_mul_pd(tx128_im, ch128_i))); + rx_tmp128_im = + simde_mm_add_pd(rx_tmp128_im, + simde_mm_add_pd(simde_mm_mul_pd(tx128_re, ch128_i), simde_mm_mul_pd(tx128_im, ch128_r))); + } } - - ch128_x = simde_mm_set1_pd(desc->ch[ii + (j * desc->nb_rx)][l].x); - ch128_y = simde_mm_set1_pd(desc->ch[ii + (j * desc->nb_rx)][l].y); - // rx_tmp.x += (tx.x * desc->ch[ii+(j*desc->nb_rx)][l].x) - (tx.y * desc->ch[ii+(j*desc->nb_rx)][l].y); - // rx_tmp.y += (tx.y * desc->ch[ii+(j*desc->nb_rx)][l].x) + (tx.x * desc->ch[ii+(j*desc->nb_rx)][l].y); - rx_tmp128_1 = simde_mm_mul_pd(tx128_re, ch128_x); - rx_tmp128_2 = simde_mm_mul_pd(tx128_re, ch128_y); - rx_tmp128_3 = simde_mm_mul_pd(tx128_im, ch128_x); - rx_tmp128_4 = simde_mm_mul_pd(tx128_im, ch128_y); - rx_tmp128_re = simde_mm_sub_pd(rx_tmp128_1, rx_tmp128_4); - rx_tmp128_im = simde_mm_add_pd(rx_tmp128_2, rx_tmp128_3); - rx_tmp128_re_f = simde_mm_add_pd(rx_tmp128_re_f, rx_tmp128_re); - rx_tmp128_im_f = simde_mm_add_pd(rx_tmp128_im_f, rx_tmp128_im); - } //l - } // j - - //rx_sig_re[ii][i+dd] = rx_tmp.x*path_loss; - //rx_sig_im[ii][i+dd] = rx_tmp.y*path_loss; - rx_tmp128_re_f = simde_mm_mul_pd(rx_tmp128_re_f, pathloss128); - rx_tmp128_im_f = simde_mm_mul_pd(rx_tmp128_im_f, pathloss128); - simde_mm_storeu_pd(&rx_sig_re[ii][2 * i + dd], rx_tmp128_re_f); // max index: length-dd -1 + dd = length -1 - simde_mm_storeu_pd(&rx_sig_im[ii][2 * i + dd], rx_tmp128_im_f); - /* - if ((ii==0)&&((i%32)==0)) { - printf("%p %p %f,%f => %e,%e\n",rx_sig_re[ii],rx_sig_im[ii],rx_tmp.x,rx_tmp.y,rx_sig_re[ii][i-dd],rx_sig_im[ii][i-dd]); + simde_mm_storeu_pd(&rx_sig_re[ii][i + dd], simde_mm_mul_pd(rx_tmp128_re, pathloss128)); + simde_mm_storeu_pd(&rx_sig_im[ii][i + dd], simde_mm_mul_pd(rx_tmp128_im, pathloss128)); + } } - */ - //rx_sig_re[ii][i] = sqrt(.5)*(tx_sig_re[0][i] + tx_sig_re[1][i]); - //rx_sig_im[ii][i] = sqrt(.5)*(tx_sig_im[0][i] + tx_sig_im[1][i]); - } // ii } // i - stop_meas(&desc->convolution); - + // Handle the final sample if the length is odd + if ((length - dd) % 2) { + int i_tail = length - dd - 1; + for (ii = 0; ii < desc->nb_rx; ii++) { + struct complexd rx_tmp = {0}; + for (j = 0; j < desc->nb_tx; j++) { + struct complexd *chan = desc->ch[ii + (j * desc->nb_rx)]; + for (l = 0; l < (int)desc->channel_length; l++) { + if ((i_tail - l) >= 0) { + struct complexd tx; + tx.r = tx_sig_re[j][i_tail - l]; + tx.i = tx_sig_im[j][i_tail - l]; + rx_tmp.r += (tx.r * chan[l].r) - (tx.i * chan[l].i); + rx_tmp.i += (tx.i * chan[l].r) + (tx.r * chan[l].i); + } + } + } + rx_sig_re[ii][i_tail + dd] = rx_tmp.r * path_loss; + rx_sig_im[ii][i_tail + dd] = rx_tmp.i * path_loss; + } + } } #else -void __attribute__ ((no_sanitize_address)) multipath_channel(channel_desc_t *desc, - double *tx_sig_re[NB_ANTENNAS_TX], - double *tx_sig_im[NB_ANTENNAS_TX], - double *rx_sig_re[NB_ANTENNAS_RX], - double *rx_sig_im[NB_ANTENNAS_RX], - uint32_t length, - uint8_t keep_channel, - int log_channel) +void __attribute__((no_sanitize_address)) multipath_channel(channel_desc_t *desc, + double **tx_sig_re, + double **tx_sig_im, + double **rx_sig_re, + double **rx_sig_im, + uint32_t length, + uint8_t keep_channel, + int log_channel) { - - double path_loss = pow(10,desc->path_loss_dB/20); + double path_loss = pow(10, desc->path_loss_dB / 20); uint64_t dd = desc->channel_offset; #ifdef DEBUG_CH printf("[CHANNEL] keep = %d : path_loss = %g (%f), nb_rx %d, nb_tx %d, dd %lu, len %d \n", - keep_channel, path_loss, desc->path_loss_dB, desc->nb_rx, desc->nb_tx, dd, desc->channel_length); + keep_channel, + path_loss, + desc->path_loss_dB, + desc->nb_rx, + desc->nb_tx, + dd, + desc->channel_length); #endif if (keep_channel) { // do nothing - keep channel } else { - random_channel(desc,0); + random_channel(desc, 0); } #ifdef DEBUG_CH - for (int l = 0; l<(int)desc->channel_length; l++) { + for (int l = 0; l < (int)desc->channel_length; l++) { printf("ch[%i] = (%f, %f)\n", l, desc->ch[0][l].r, desc->ch[0][l].i); } #endif @@ -178,27 +175,27 @@ void __attribute__ ((no_sanitize_address)) multipath_channel(channel_desc_t *des get_cexp_doppler(cexp_doppler, desc, length); } - for (int i=0; i<((int)length-dd); i++) { - for (int ii=0; iinb_rx; ii++) { - struct complexd rx_tmp={0}; - for (int j=0; jnb_tx; j++) { - struct complexd *chan=desc->ch[ii+(j*desc->nb_rx)]; - for (int l = 0; l<(int)desc->channel_length; l++) { - if ((i>=0) && (i-l)>=0) { + for (int i = 0; i < ((int)length - dd); i++) { + for (int ii = 0; ii < desc->nb_rx; ii++) { + struct complexd rx_tmp = {0}; + for (int j = 0; j < desc->nb_tx; j++) { + struct complexd *chan = desc->ch[ii + (j * desc->nb_rx)]; + for (int l = 0; l < (int)desc->channel_length; l++) { + if ((i >= 0) && (i - l) >= 0) { struct complexd tx; - tx.r = tx_sig_re[j][i-l]; - tx.i = tx_sig_im[j][i-l]; + tx.r = tx_sig_re[j][i - l]; + tx.i = tx_sig_im[j][i - l]; rx_tmp.r += (tx.r * chan[l].r) - (tx.i * chan[l].i); rx_tmp.i += (tx.i * chan[l].r) + (tx.r * chan[l].i); } - #if 0 +#if 0 if (i==0 && log_channel == 1) { printf("channel[%d][%d][%d] = %f dB \t(%e, %e)\n", ii, j, l, 10 * log10(pow(chan[l].r, 2.0) + pow(chan[l].i, 2.0)), chan[l].r, chan[l].i); } - #endif - } //l - } // j +#endif + } // l + } // j #if 0 if (desc->max_Doppler != 0.0) rx_tmp = cdMul(rx_tmp, cexp_doppler[i]); @@ -206,26 +203,190 @@ void __attribute__ ((no_sanitize_address)) multipath_channel(channel_desc_t *des #ifdef DOPPLER_DEBUG printf("[k %2i] cexp_doppler = (%7.4f, %7.4f), abs(cexp_doppler) = %.4f\n", - i, - cexp_doppler[i].r, - cexp_doppler[i].i, - sqrt(cexp_doppler[i].r * cexp_doppler[i].r + cexp_doppler[i].i * cexp_doppler[i].i)); + i, + cexp_doppler[i].r, + cexp_doppler[i].i, + sqrt(cexp_doppler[i].r * cexp_doppler[i].r + cexp_doppler[i].i * cexp_doppler[i].i)); #endif - rx_sig_re[ii][i+dd] = rx_tmp.r*path_loss; - rx_sig_im[ii][i+dd] = rx_tmp.i*path_loss; + rx_sig_re[ii][i + dd] = rx_tmp.r * path_loss; + rx_sig_im[ii][i + dd] = rx_tmp.i * path_loss; #ifdef DEBUG_CH - if ((i%32)==0) { - printf("rx aa %d: %f, %f => %e, %e\n", - ii, rx_tmp.r, rx_tmp.i, rx_sig_re[ii][i-dd], rx_sig_im[ii][i-dd]); - } -#endif - //rx_sig_re[ii][i] = sqrt(.5)*(tx_sig_re[0][i] + tx_sig_re[1][i]); - //rx_sig_im[ii][i] = sqrt(.5)*(tx_sig_im[0][i] + tx_sig_im[1][i]); + if ((i % 32) == 0) { + printf("rx aa %d: %f, %f => %e, %e\n", ii, rx_tmp.r, rx_tmp.i, rx_sig_re[ii][i - dd], rx_sig_im[ii][i - dd]); + } +#endif + // rx_sig_re[ii][i] = sqrt(.5)*(tx_sig_re[0][i] + tx_sig_re[1][i]); + // rx_sig_im[ii][i] = sqrt(.5)*(tx_sig_im[0][i] + tx_sig_im[1][i]); } // ii } // i } #endif +#ifdef CHANNEL_SSE +void __attribute__((no_sanitize_address)) multipath_channel_float(channel_desc_t *desc, + float **tx_sig_interleaved, + float **rx_sig_re, + float **rx_sig_im, + uint32_t length, + uint8_t keep_channel, + int log_channel) +{ + int i, ii, j, l; + simde__m128 rx_tmp128_re, rx_tmp128_im, tx128_re, tx128_im, ch128_r, ch128_i, pathloss128; + uint64_t dd = desc->channel_offset; + if (keep_channel == 0) { + random_channel(desc, 0); + } + + struct complexd cexp_doppler[length]; + if (desc->max_Doppler != 0.0) { + get_cexp_doppler(cexp_doppler, desc, length); + } + + if (dd >= length) { + return; // No samples to process + } + + for (i = 0; i <= (int)(length - dd) - 4; i += 4) { + for (ii = 0; ii < desc->nb_rx; ii++) { + rx_tmp128_re = simde_mm_setzero_ps(); + rx_tmp128_im = simde_mm_setzero_ps(); + + for (j = 0; j < desc->nb_tx; j++) { + for (l = 0; l < (int)desc->channel_length; l++) { + int idx = i - l; + if (idx >= 0) { + // Load 2 complex numbers (4 floats) => {I0, Q0, I1, Q1} + simde__m128 vec0 = simde_mm_loadu_ps(&tx_sig_interleaved[j][2 * idx]); + simde__m128 vec1 = simde_mm_loadu_ps(&tx_sig_interleaved[j][2 * (idx + 2)]); + tx128_re = simde_mm_shuffle_ps(vec0, vec1, SIMDE_MM_SHUFFLE(2, 0, 2, 0)); // {I0,I1,I2,I3} + tx128_im = simde_mm_shuffle_ps(vec0, vec1, SIMDE_MM_SHUFFLE(3, 1, 3, 1)); // {Q0,Q1,Q2,Q3} + } else { + tx128_re = simde_mm_setzero_ps(); + tx128_im = simde_mm_setzero_ps(); + } + + ch128_r = simde_mm_set1_ps((float)desc->ch[ii + (j * desc->nb_rx)][l].r); + ch128_i = simde_mm_set1_ps((float)desc->ch[ii + (j * desc->nb_rx)][l].i); + + // re = (tx_re * ch_r) - (tx_im * ch_i) + rx_tmp128_re = simde_mm_add_ps(rx_tmp128_re, + simde_mm_sub_ps(simde_mm_mul_ps(tx128_re, ch128_r), simde_mm_mul_ps(tx128_im, ch128_i))); + // im = (tx_re * ch_i) + (tx_im * ch_r) + rx_tmp128_im = simde_mm_add_ps(rx_tmp128_im, + simde_mm_add_ps(simde_mm_mul_ps(tx128_re, ch128_i), simde_mm_mul_ps(tx128_im, ch128_r))); + } + } + +#if 0 + if (desc->max_Doppler != 0.0) { + float doppler_re[4] = {(float)cexp_doppler[i].r, (float)cexp_doppler[i+1].r, (float)cexp_doppler[i+2].r, (float)cexp_doppler[i+3].r}; + float doppler_im[4] = {(float)cexp_doppler[i].i, (float)cexp_doppler[i+1].i, (float)cexp_doppler[i+2].i, (float)cexp_doppler[i+3].i}; + + simde__m128 doppler128_r = simde_mm_loadu_ps(doppler_re); + simde__m128 doppler128_i = simde_mm_loadu_ps(doppler_im); + + simde__m128 temp_re = rx_tmp128_re; + simde__m128 temp_im = rx_tmp128_im; + + rx_tmp128_re = simde_mm_sub_ps(simde_mm_mul_ps(temp_re, doppler128_r), simde_mm_mul_ps(temp_im, doppler128_i)); + rx_tmp128_im = simde_mm_add_ps(simde_mm_mul_ps(temp_im, doppler128_r), simde_mm_mul_ps(temp_re, doppler128_i)); + } +#endif + simde_mm_storeu_ps(&rx_sig_re[ii][i + dd], rx_tmp128_re); + simde_mm_storeu_ps(&rx_sig_im[ii][i + dd], rx_tmp128_im); + } + } + + // Handle remaining samples (tail loop) that are not a multiple of 4 + for (; i < (int)(length - dd); i++) { + for (ii = 0; ii < desc->nb_rx; ii++) { + struct complexf rx_tmp = {0.0f, 0.0f}; + for (j = 0; j < desc->nb_tx; j++) { + struct complexd *chan = desc->ch[ii + (j * desc->nb_rx)]; + for (l = 0; l < (int)desc->channel_length; l++) { + if ((i - l) >= 0) { + struct complexf tx; + tx.r = tx_sig_interleaved[j][2 * (i - l)]; + tx.i = tx_sig_interleaved[j][2 * (i - l) + 1]; + rx_tmp.r += (tx.r * (float)chan[l].r) - (tx.i * (float)chan[l].i); + rx_tmp.i += (tx.i * (float)chan[l].r) + (tx.r * (float)chan[l].i); + } + } + } +#if 0 + if (desc->max_Doppler != 0.0) { + struct complexf doppler_factor = {(float)cexp_doppler[i].r, (float)cexp_doppler[i].i}; + struct complexf temp = rx_tmp; + rx_tmp.r = (temp.r * doppler_factor.r) - (temp.i * doppler_factor.i); + rx_tmp.i = (temp.i * doppler_factor.r) + (temp.r * doppler_factor.i); + } +#endif + rx_sig_re[ii][i + dd] = rx_tmp.r; + rx_sig_im[ii][i + dd] = rx_tmp.i; + } + } +} + +#else +void multipath_channel_float(channel_desc_t *desc, + float **tx_sig_interleaved, + float **rx_sig_re, + float **rx_sig_im, + uint32_t length, + uint8_t keep_channel, + int log_channel) +{ + uint64_t dd = desc->channel_offset; + + if (keep_channel == 0) { + random_channel(desc, 0); + } + + struct complexd cexp_doppler[length]; + if (desc->max_Doppler != 0.0) { + get_cexp_doppler(cexp_doppler, desc, length); + } + + if (dd >= length) { + return; + } + + for (int i = 0; i < ((int)length - dd); i++) { + for (int ii = 0; ii < desc->nb_rx; ii++) { + struct complexf rx_tmp = {0.0f, 0.0f}; + + for (int j = 0; j < desc->nb_tx; j++) { + struct complexd *chan = desc->ch[ii + (j * desc->nb_rx)]; + + for (int l = 0; l < (int)desc->channel_length; l++) { + if ((i - l) >= 0) { + struct complexf tx; + tx.r = tx_sig_interleaved[j][2 * (i - l)]; + tx.i = tx_sig_interleaved[j][2 * (i - l) + 1]; + rx_tmp.r += (tx.r * (float)chan[l].r) - (tx.i * (float)chan[l].i); + rx_tmp.i += (tx.i * (float)chan[l].r) + (tx.r * (float)chan[l].i); + } + } // l (channel_length) + } // j (nb_tx) + +#if 0 + if (desc->max_Doppler != 0.0) { + // Perform complex multiplication: rx_tmp = rx_tmp * cexp_doppler[i] + struct complexf doppler_factor = {(float)cexp_doppler[i].r, (float)cexp_doppler[i].i}; + struct complexf temp = rx_tmp; + rx_tmp.r = (temp.r * doppler_factor.r) - (temp.i * doppler_factor.i); + rx_tmp.i = (temp.i * doppler_factor.r) + (temp.r * doppler_factor.i); + } +#endif + rx_sig_re[ii][i + dd] = rx_tmp.r; + rx_sig_im[ii][i + dd] = rx_tmp.i; + + } // ii (nb_rx) + } // i (length) +} + +#endif diff --git a/openair1/SIMULATION/TOOLS/multipath_channel.cu b/openair1/SIMULATION/TOOLS/multipath_channel.cu new file mode 100644 index 0000000000..7294ccc694 --- /dev/null +++ b/openair1/SIMULATION/TOOLS/multipath_channel.cu @@ -0,0 +1,272 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#include +#include +#include "oai_cuda.h" + +#define CHECK_CUDA(val) checkCuda((val), #val, __FILE__, __LINE__) +static void checkCuda(cudaError_t result, const char *const func, const char *const file, const int line) +{ + if (result != cudaSuccess) { + fprintf(stderr, + "CUDA Error at %s:%d code=%d(%s) \"%s\" \n", + file, + line, + static_cast(result), + cudaGetErrorString(result), + func); + cudaDeviceReset(); + exit(EXIT_FAILURE); + } +} + +__device__ __forceinline__ float2 complex_mul(float2 a, float2 b) +{ + return make_float2(a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x); +} + +__device__ __forceinline__ float2 complex_add(float2 a, float2 b) +{ + return make_float2(a.x + b.x, a.y + b.y); +} + +__global__ void interleave_output_kernel(const float *__restrict__ rx_re, + const float *__restrict__ rx_im, + float2 *__restrict__ output_interleaved, + int num_total_samples) +{ + const int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < num_total_samples) { + output_interleaved[i].x = rx_re[i]; + output_interleaved[i].y = rx_im[i]; + } +} + +__global__ void multipath_channel_kernel(const float2 *__restrict__ d_channel_coeffs, + const float *__restrict__ tx_sig, + float2 *__restrict__ rx_sig, + int num_samples, + int channel_length, + int nb_tx, + int nb_rx) +{ + extern __shared__ float2 tx_shared[]; + const int i = blockIdx.x * blockDim.x + threadIdx.x; + const int rx_ant_idx = blockIdx.y; + const int padding_len = channel_length - 1; + const int padded_num_samples = num_samples + padding_len; + + if (i >= num_samples) + return; + + float2 rx_tmp = make_float2(0.0f, 0.0f); + + for (int j = 0; j < nb_tx; j++) { + const int tid = threadIdx.x; + const int block_start_idx = blockIdx.x * blockDim.x; + const int shared_mem_size = blockDim.x + channel_length - 1; + + for (int k = tid; k < shared_mem_size; k += blockDim.x) { + int load_idx = block_start_idx + k; + int interleaved_idx = 2 * (j * padded_num_samples + load_idx); + tx_shared[k] = make_float2(tx_sig[interleaved_idx], tx_sig[interleaved_idx + 1]); + } + __syncthreads(); + + for (int l = 0; l < channel_length; l++) { + float2 tx_sample = tx_shared[tid + (channel_length - 1) - l]; + int chan_link_idx = rx_ant_idx + (j * nb_rx); + float2 chan_weight = d_channel_coeffs[chan_link_idx * channel_length + l]; + rx_tmp = complex_add(rx_tmp, complex_mul(tx_sample, chan_weight)); + } + __syncthreads(); + } + + rx_sig[rx_ant_idx * num_samples + i].x = rx_tmp.x; + rx_sig[rx_ant_idx * num_samples + i].y = rx_tmp.y; +} + +__global__ void multipath_channel_kernel_batched(const float2 *__restrict__ d_channel_coeffs, + const float2 *__restrict__ tx_sig, + float2 *__restrict__ rx_sig, + int num_samples, + int channel_length, + int nb_tx, + int nb_rx) +{ + extern __shared__ float2 tx_shared[]; + + const int i = blockIdx.x * blockDim.x + threadIdx.x; + const int rx_ant_idx = blockIdx.y; + const int c = blockIdx.z; + + if (i >= num_samples) + return; + + float2 rx_tmp = make_float2(0.0f, 0.0f); + + const int padding_len = channel_length - 1; + const int padded_num_samples = num_samples + padding_len; + + const int channel_tx_offset = c * nb_tx * padded_num_samples; + const int channel_rx_offset = c * nb_rx * num_samples; + + for (int j = 0; j < nb_tx; j++) { + const int tid = threadIdx.x; + const int block_start_idx = blockIdx.x * blockDim.x; + const int shared_mem_size = blockDim.x + channel_length - 1; + + for (int k = tid; k < shared_mem_size; k += blockDim.x) { + int load_idx = block_start_idx + k; + tx_shared[k] = tx_sig[channel_tx_offset + j * padded_num_samples + load_idx]; + } + __syncthreads(); + + for (int l = 0; l < channel_length; l++) { + float2 tx_sample = tx_shared[tid + (channel_length - 1) - l]; + int chan_link_idx = (c * nb_tx * nb_rx) + (rx_ant_idx + j * nb_rx); + float2 chan_weight = d_channel_coeffs[chan_link_idx * channel_length + l]; + rx_tmp = complex_add(rx_tmp, complex_mul(tx_sample, chan_weight)); + } + __syncthreads(); + } + + rx_sig[channel_rx_offset + rx_ant_idx * num_samples + i].x = rx_tmp.x; + rx_sig[channel_rx_offset + rx_ant_idx * num_samples + i].y = rx_tmp.y; +} + +extern "C" { + +void multipath_channel_cuda(float **rx_sig_re, + float **rx_sig_im, + int nb_tx, + int nb_rx, + int channel_length, + uint32_t length, + uint64_t channel_offset, + float *h_channel_coeffs, + void *d_tx_sig_void, + void *d_rx_sig_void, + void *d_channel_coeffs_void, + void *h_tx_sig_pinned_void) +{ + float *d_tx_sig = (float *)d_tx_sig_void; + float2 *d_rx_sig = (float2 *)d_rx_sig_void; + float2 *d_channel_coeffs = (float2 *)d_channel_coeffs_void; + int num_samples = length - (int)channel_offset; + float *kernel_input_ptr; + + const int padding_len = channel_length - 1; + const size_t total_padded_tx_bytes = nb_tx * (num_samples + padding_len) * 2 * sizeof(float); + +#if defined(USE_UNIFIED_MEMORY) || defined(USE_ATS_MEMORY) + kernel_input_ptr = (float *)h_tx_sig_pinned_void; +#else // EXPLICIT COPY + float *h_tx_sig_pinned = (float *)h_tx_sig_pinned_void; + CHECK_CUDA(cudaMemcpy(d_tx_sig, h_tx_sig_pinned, total_padded_tx_bytes, cudaMemcpyHostToDevice)); + kernel_input_ptr = d_tx_sig; +#endif + + size_t channel_size_bytes = nb_tx * nb_rx * channel_length * sizeof(float2); + CHECK_CUDA(cudaMemcpy(d_channel_coeffs, h_channel_coeffs, channel_size_bytes, cudaMemcpyHostToDevice)); + + dim3 threadsPerBlock(512, 1); + dim3 numBlocks((num_samples + threadsPerBlock.x - 1) / threadsPerBlock.x, nb_rx); + size_t sharedMemSize = (threadsPerBlock.x + channel_length - 1) * sizeof(float2); + multipath_channel_kernel<<>>(d_channel_coeffs, + kernel_input_ptr, + d_rx_sig, + num_samples, + channel_length, + nb_tx, + nb_rx); + +#if defined(USE_UNIFIED_MEMORY) + CHECK_CUDA(cudaDeviceSynchronize()); + for (int ii = 0; ii < nb_rx; ii++) { + for (int i = 0; i < num_samples; i++) { + float2 result = d_rx_sig[ii * num_samples + i]; + rx_sig_re[ii][i + channel_offset] = result.x; + rx_sig_im[ii][i + channel_offset] = result.y; + } + } +#else + CHECK_CUDA(cudaDeviceSynchronize()); + float2 *h_rx_sig = (float2 *)malloc(nb_rx * num_samples * sizeof(float2)); + CHECK_CUDA(cudaMemcpy(h_rx_sig, d_rx_sig, nb_rx * num_samples * sizeof(float2), cudaMemcpyDeviceToHost)); + for (int ii = 0; ii < nb_rx; ii++) { + for (int i = 0; i < num_samples; i++) { + float2 result = h_rx_sig[ii * num_samples + i]; + rx_sig_re[ii][i + channel_offset] = result.x; + rx_sig_im[ii][i + channel_offset] = result.y; + } + } + free(h_rx_sig); +#endif +} + +void interleave_channel_output_cuda(float **rx_sig_re, + float **rx_sig_im, + void **output_interleaved_void, + int nb_rx, + int num_samples) +{ + float2 **output_interleaved = (float2 **)output_interleaved_void; + + int num_total_samples = nb_rx * num_samples; + size_t total_bytes_per_plane = num_total_samples * sizeof(float); + size_t total_bytes_interleaved = num_total_samples * sizeof(float2); + + float *h_re_flat = (float *)malloc(total_bytes_per_plane); + float *h_im_flat = (float *)malloc(total_bytes_per_plane); + float2 *h_out_flat = (float2 *)malloc(total_bytes_interleaved); + + for (int i = 0; i < nb_rx; i++) { + memcpy(h_re_flat + i * num_samples, rx_sig_re[i], num_samples * sizeof(float)); + memcpy(h_im_flat + i * num_samples, rx_sig_im[i], num_samples * sizeof(float)); + } + float *d_re, *d_im; + float2 *d_out; + CHECK_CUDA(cudaMalloc((void **)&d_re, total_bytes_per_plane)); + CHECK_CUDA(cudaMalloc((void **)&d_im, total_bytes_per_plane)); + CHECK_CUDA(cudaMalloc((void **)&d_out, total_bytes_interleaved)); + CHECK_CUDA(cudaMemcpy(d_re, h_re_flat, total_bytes_per_plane, cudaMemcpyHostToDevice)); + CHECK_CUDA(cudaMemcpy(d_im, h_im_flat, total_bytes_per_plane, cudaMemcpyHostToDevice)); + + int threadsPerBlock = 512; + int blocksPerGrid = (num_total_samples + threadsPerBlock - 1) / threadsPerBlock; + interleave_output_kernel<<>>(d_re, d_im, d_out, num_total_samples); + + CHECK_CUDA(cudaMemcpy(h_out_flat, d_out, total_bytes_interleaved, cudaMemcpyDeviceToHost)); + + for (int i = 0; i < nb_rx; i++) { + memcpy(output_interleaved[i], h_out_flat + i * num_samples, num_samples * sizeof(float2)); + } + free(h_re_flat); + free(h_im_flat); + free(h_out_flat); + CHECK_CUDA(cudaFree(d_re)); + CHECK_CUDA(cudaFree(d_im)); + CHECK_CUDA(cudaFree(d_out)); +} + +} // extern "C" diff --git a/openair1/SIMULATION/TOOLS/oai_cuda.h b/openair1/SIMULATION/TOOLS/oai_cuda.h new file mode 100644 index 0000000000..1b3f3d0ae1 --- /dev/null +++ b/openair1/SIMULATION/TOOLS/oai_cuda.h @@ -0,0 +1,172 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#ifndef __OAI_CUDA_H__ +#define __OAI_CUDA_H__ + +#include + +#ifdef __NVCC__ +typedef struct complex16 { + int16_t r; + int16_t i; +} c16_t; +#else +#include "PHY/TOOLS/tools_defs.h" +#endif + +#ifdef __NVCC__ +#include +__device__ float2 complex_mul(float2 a, float2 b); + +__global__ void multipath_channel_kernel(const float2 *__restrict__ d_channel_coeffs, + const float *__restrict__ tx_sig, + float2 *__restrict__ rx_sig, + int num_samples, + int channel_length, + int nb_tx, + int nb_rx); + +__global__ void add_noise_and_phase_noise_kernel(const float2 *__restrict__ r_sig, + short2 *__restrict__ output_sig, + curandState_t *states, + int num_samples, + float sigma, + float pn_std_dev, + bool apply_phase_noise); + +#endif // __NVCC__ + +#ifdef __cplusplus +extern "C" { +#endif + +void multipath_channel_cuda(float **rx_sig_re, + float **rx_sig_im, + int nb_tx, + int nb_rx, + int channel_length, + uint32_t length, + uint64_t channel_offset, + float *h_channel_coeffs, + void *d_tx_sig, + void *d_rx_sig, + void *d_channel_coeffs, + void *h_tx_sig_pinned); + +void add_noise_cuda(const float **r_re, + const float **r_im, + c16_t **output_signal, + int num_samples, + int nb_rx, + float sigma2, + double ts, + int slot_offset, + int delay, + uint16_t pdu_bit_map, + uint16_t ptrs_bit_map, + void *d_r_sig, + void *d_output_sig, + void *d_curand_states, + void *h_r_sig_pinned, + void *h_output_sig_pinned); + +void run_channel_pipeline_cuda(c16_t **output_signal, + int nb_tx, + int nb_rx, + int channel_length, + uint32_t num_samples, + float *h_channel_coeffs, + float sigma2, + double ts, + uint16_t pdu_bit_map, + uint16_t ptrs_bit_map, + int slot_offset, + int delay, + void *d_tx_sig, + void *d_intermediate_sig, + void *d_final_output, + void *d_curand_states, + void *h_tx_sig_pinned, + void *h_final_output_pinned, + void *d_channel_coeffs); + +void run_channel_pipeline_cuda_batched(int num_channels, + int nb_tx, + int nb_rx, + int channel_length, + uint32_t num_samples, + void *d_channel_coeffs_batch, + float sigma2, + double ts, + uint16_t pdu_bit_map, + uint16_t ptrs_bit_map, + void *d_tx_sig_batch, + void *d_intermediate_sig_batch, + void *d_final_output_batch, + void *d_curand_states); + +void run_channel_pipeline_cuda_streamed(int nb_tx, + int nb_rx, + int channel_length, + uint32_t num_samples, + float *h_channel_coeffs, + float sigma2, + double ts, + uint16_t pdu_bit_map, + uint16_t ptrs_bit_map, + void *d_tx_sig_void, + void *d_intermediate_sig_void, + void *d_final_output_void, + void *d_curand_states_void, + void *h_tx_sig_pinned_void, + void *d_channel_coeffs_void, + void *stream_void); + +void sum_channel_outputs_cuda(void **d_individual_outputs, void *d_final_output, int num_channels, int nb_rx, int num_samples); + +/** + * \brief Interleaves separate real and imaginary signal buffers into a complex format. + * + * This utility function takes separate planar buffers for the real (I) and imaginary (Q) + * components of a signal and combines them into a single interleaved buffer of float2 + * structs, which is a common format for CUDA processing. + * + * \param[in] rx_sig_re Array of pointers to the real parts of the input signals. + * \param[in] rx_sig_im Array of pointers to the imaginary parts of the input signals. + * \param[out] output_interleaved Pointer to an array of pointers where the interleaved output will be stored. + * \param[in] nb_rx The number of receive antennas (i.e., the number of signals). + * \param[in] num_samples The number of samples per signal. + * + * \note The caller is responsible for allocating memory for the `output_interleaved` buffer. It should + * point to an array of `nb_rx` pointers, where each of those pointers is allocated with a size of + * `num_samples * sizeof(float2)`. + */ +void interleave_channel_output_cuda(float **rx_sig_re, float **rx_sig_im, void **output_interleaved, int nb_rx, int num_samples); + +void *create_and_init_curand_states_cuda(int num_elements, unsigned long long seed); +void destroy_curand_states_cuda(void *d_curand_states); + +#ifdef __cplusplus +} +#endif + +#endif // __OAI_CUDA_H__ diff --git a/openair1/SIMULATION/TOOLS/phase_noise.c b/openair1/SIMULATION/TOOLS/phase_noise.c index 5bb6df3b5e..e443629ee1 100644 --- a/openair1/SIMULATION/TOOLS/phase_noise.c +++ b/openair1/SIMULATION/TOOLS/phase_noise.c @@ -22,12 +22,11 @@ #include #include "sim.h" - /* linear phase noise model */ void phase_noise(double ts, int16_t *InRe, int16_t *InIm) { - static uint64_t i=0; - double fd = 300;//0.01*30000 + static uint64_t i = 0; + double fd = 300; // 0.01*30000 double phase = (double)(i * fd * ts); c16_t rot = get_sin_cos(phase); c16_t val = {.r = *InRe, .i = *InIm}; @@ -61,3 +60,29 @@ void add_noise(c16_t **rxdata, } } } + +void add_noise_float(c16_t **rxdata, + const float **r_re, + const float **r_im, + const float sigma, + const int length, + const int slot_offset, + const double ts, + const int delay, + bool apply_phase_noise, + const uint8_t nb_antennas_rx) +{ + float sqrt_sigma_div2 = sqrtf(sigma / 2.0f); + + for (int i = 0; i < length; i++) { + for (int ap = 0; ap < nb_antennas_rx; ap++) { + c16_t *rxd = &rxdata[ap][slot_offset + i + delay]; + rxd->r = (int16_t)(r_re[ap][i] + (gaussZiggurat(0.0, 1.0) * sqrt_sigma_div2)); + rxd->i = (int16_t)(r_im[ap][i] + (gaussZiggurat(0.0, 1.0) * sqrt_sigma_div2)); + + if (apply_phase_noise) { + phase_noise(ts, &rxd->r, &rxd->i); + } + } + } +} diff --git a/openair1/SIMULATION/TOOLS/phase_noise.cu b/openair1/SIMULATION/TOOLS/phase_noise.cu new file mode 100644 index 0000000000..587c16ad69 --- /dev/null +++ b/openair1/SIMULATION/TOOLS/phase_noise.cu @@ -0,0 +1,234 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#include +#include +#include +#include +#include +#include "oai_cuda.h" + +#define CHECK_CUDA(val) checkCuda((val), __FILE__, __LINE__) +static void checkCuda(cudaError_t result, const char *file, int line) +{ + if (result != cudaSuccess) { + fprintf(stderr, "CUDA Error at %s:%d: %s\n", file, line, cudaGetErrorString(result)); + exit(1); + } +} + +__device__ __forceinline__ float2 complex_mul(float2 a, float2 b) +{ + return make_float2(a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x); +} + +__global__ void init_curand_states_kernel(curandState_t *states, unsigned long long seed, int num_elements) +{ + int idx = blockIdx.x * blockDim.x + threadIdx.x; + if (idx < num_elements) { + curand_init(seed, idx, 0, &states[idx]); + } +} + +__global__ void add_noise_and_phase_noise_kernel(const float2 *__restrict__ r_sig, + short2 *__restrict__ output_sig, + curandState_t *states, + int num_samples, + float sigma, + float pn_std_dev, + bool apply_phase_noise) +{ + const int i = blockIdx.x * blockDim.x + threadIdx.x; + const int ii = blockIdx.y; + + if (i >= num_samples) + return; + + // Each thread handles one sample and its corresponding cuRAND state + curandState_t local_state = states[ii * num_samples + i]; + + float2 noisy_signal = r_sig[ii * num_samples + i]; + + float2 awgn = curand_normal2(&local_state); + noisy_signal.x += awgn.x * sigma; + noisy_signal.y += awgn.y * sigma; + + float2 final_signal = noisy_signal; + + if (apply_phase_noise) { + float phase_error = curand_normal(&local_state) * pn_std_dev; + float cos_phi, sin_phi; + __sincosf(phase_error, &sin_phi, &cos_phi); + float2 phase_rot = make_float2(cos_phi, sin_phi); + final_signal = complex_mul(noisy_signal, phase_rot); + } + + states[ii * num_samples + i] = local_state; + output_sig[ii * num_samples + i] = make_short2((short)fmaxf(-32768.0f, fminf(32767.0f, final_signal.x)), + (short)fmaxf(-32768.0f, fminf(32767.0f, final_signal.y))); +} + +__global__ void add_noise_and_phase_noise_kernel_batched(const float2 *__restrict__ r_sig, + short2 *__restrict__ output_sig, + curandState_t *states, + int num_samples, + int nb_rx, + float sigma, + float pn_std_dev, + bool apply_phase_noise) +{ + const int i = blockIdx.x * blockDim.x + threadIdx.x; // Sample index + const int ii = blockIdx.y; // RX antenna index + const int c = blockIdx.z; // Channel index + + if (i >= num_samples) + return; + + const int flat_output_idx = (c * nb_rx * num_samples) + (ii * num_samples) + i; + // Index for the smaller, reused pool of cuRAND states (does NOT include channel) + const int state_pool_idx = (ii * num_samples) + i; + + curandState_t local_state = states[state_pool_idx]; + + float2 noisy_signal = r_sig[flat_output_idx]; + + float2 awgn = curand_normal2(&local_state); + noisy_signal.x += awgn.x * sigma; + noisy_signal.y += awgn.y * sigma; + + float2 final_signal = noisy_signal; + + if (apply_phase_noise) { + float phase_error = curand_normal(&local_state) * pn_std_dev; + float cos_phi, sin_phi; + __sincosf(phase_error, &sin_phi, &cos_phi); + float2 phase_rot = make_float2(cos_phi, sin_phi); + final_signal = complex_mul(noisy_signal, phase_rot); + } + + states[state_pool_idx] = local_state; + + output_sig[flat_output_idx] = make_short2((short)fmaxf(-32768.0f, fminf(32767.0f, final_signal.x)), + (short)fmaxf(-32768.0f, fminf(32767.0f, final_signal.y))); +} + +extern "C" { + +void add_noise_cuda(const float **r_re, + const float **r_im, + c16_t **output_signal, + int num_samples, + int nb_rx, + float sigma2, + double ts, + int slot_offset, + int delay, + uint16_t pdu_bit_map, + uint16_t ptrs_bit_map, + void *d_r_sig_void, + void *d_output_sig_void, + void *d_curand_states_void, + void *h_r_sig_void, + void *h_output_temp_void) +{ + float2 *d_r_sig = (float2 *)d_r_sig_void; + short2 *d_output_sig = (short2 *)d_output_sig_void; + curandState_t *d_curand_states = (curandState_t *)d_curand_states_void; + + float2 *kernel_input_ptr; + +#if defined(USE_UNIFIED_MEMORY) + for (int ii = 0; ii < nb_rx; ii++) { + for (int i = 0; i < num_samples; i++) { + d_r_sig[ii * num_samples + i] = make_float2(r_re[ii][i], r_im[ii][i]); + } + } + kernel_input_ptr = d_r_sig; +#elif defined(USE_ATS_MEMORY) + float2 *h_r_sig = (float2 *)h_r_sig_void; + for (int ii = 0; ii < nb_rx; ii++) { + for (int i = 0; i < num_samples; i++) { + h_r_sig[ii * num_samples + i] = make_float2(r_re[ii][i], r_im[ii][i]); + } + } + kernel_input_ptr = h_r_sig; +#else // EXPLICIT COPY + float2 *h_r_sig = (float2 *)h_r_sig_void; + for (int ii = 0; ii < nb_rx; ii++) { + for (int i = 0; i < num_samples; i++) { + h_r_sig[ii * num_samples + i] = make_float2(r_re[ii][i], r_im[ii][i]); + } + } + CHECK_CUDA(cudaMemcpy(d_r_sig, h_r_sig, nb_rx * num_samples * sizeof(float2), cudaMemcpyHostToDevice)); + kernel_input_ptr = d_r_sig; +#endif + + dim3 threadsPerBlock(256, 1); + dim3 numBlocks((num_samples + threadsPerBlock.x - 1) / threadsPerBlock.x, nb_rx); + float pn_variance = 1e-5f * 2.0f * 3.1415926535f * 300.0f * (float)ts; + bool apply_phase_noise = (pdu_bit_map & ptrs_bit_map); + add_noise_and_phase_noise_kernel<<>>(kernel_input_ptr, + d_output_sig, + d_curand_states, + num_samples, + sqrtf(sigma2 / 2.0f), + sqrtf(pn_variance), + apply_phase_noise); + +#if defined(USE_UNIFIED_MEMORY) + CHECK_CUDA(cudaDeviceSynchronize()); + for (int ii = 0; ii < nb_rx; ii++) { + for (int i = 0; i < num_samples; i++) { + short2 result = d_output_sig[ii * num_samples + i]; + output_signal[ii][i + slot_offset + delay].r = result.x; + output_signal[ii][i + slot_offset + delay].i = result.y; + } + } +#else + short2 *h_output_temp = (short2 *)h_output_temp_void; + CHECK_CUDA(cudaMemcpy(h_output_temp, d_output_sig, nb_rx * num_samples * sizeof(short2), cudaMemcpyDeviceToHost)); + CHECK_CUDA(cudaDeviceSynchronize()); + for (int ii = 0; ii < nb_rx; ii++) { + memcpy(output_signal[ii] + slot_offset + delay, h_output_temp + ii * num_samples, num_samples * sizeof(short2)); + } +#endif +} + +void *create_and_init_curand_states_cuda(int num_elements, unsigned long long seed) +{ + void *d_states_void; + CHECK_CUDA(cudaMalloc(&d_states_void, num_elements * sizeof(curandState_t))); + + int threads = 256; + int blocks = (num_elements + threads - 1) / threads; + init_curand_states_kernel<<>>((curandState_t *)d_states_void, seed, num_elements); + CHECK_CUDA(cudaDeviceSynchronize()); + + return d_states_void; +} + +void destroy_curand_states_cuda(void *d_curand_states) +{ + if (d_curand_states) { + CHECK_CUDA(cudaFree(d_curand_states)); + } +} +} // extern "C" diff --git a/openair1/SIMULATION/TOOLS/sim.h b/openair1/SIMULATION/TOOLS/sim.h index d31109a836..24f1c053f9 100644 --- a/openair1/SIMULATION/TOOLS/sim.h +++ b/openair1/SIMULATION/TOOLS/sim.h @@ -412,13 +412,39 @@ int random_channel(channel_desc_t *desc, uint8_t abstraction_flag); */ void multipath_channel(channel_desc_t *desc, - double *tx_sig_re[NB_ANTENNAS_TX], - double *tx_sig_im[NB_ANTENNAS_TX], - double *rx_sig_re[NB_ANTENNAS_RX], - double *rx_sig_im[NB_ANTENNAS_RX], + double **tx_sig_re, + double **tx_sig_im, + double **rx_sig_re, + double **rx_sig_im, uint32_t length, uint8_t keep_channel, int log_channel); + +void multipath_channel_float(channel_desc_t *desc, + float **tx_sig_interleaved, + float **rx_sig_re, + float **rx_sig_im, + uint32_t length, + uint8_t keep_channel, + int log_channel); + +void interleave_channel_output(float **rx_sig_re, + float **rx_sig_im, + float **output_interleaved, + int nb_rx, + int num_samples); + +void add_noise_float(c16_t **rxdata, + const float **r_re, + const float **r_im, + const float sigma2, + const int length, + const int slot_offset, + const double ts, + const int delay, + bool apply_phase_noise, + const uint8_t nb_antennas_rx); + /* \fn double compute_pbch_sinr(channel_desc_t *desc, channel_desc_t *desc_i1, diff --git a/openair1/SIMULATION/tests/CMakeLists.txt b/openair1/SIMULATION/tests/CMakeLists.txt index 0c2ab8dd39..afd7dcffd5 100644 --- a/openair1/SIMULATION/tests/CMakeLists.txt +++ b/openair1/SIMULATION/tests/CMakeLists.txt @@ -414,6 +414,79 @@ add_physim_test(physim.5g-offload.nr_ulsim.testT2.9 "T2 LDPC offload: SNR = 30, endif() +#################################################################################### +###### cuda-specific tests ###### +#################################################################################### + +if (CUDA_ENABLE) +add_physim_test(cuda.5g.channelsim.test_multipath "unitary test designed specifically to validate the multipath_channel_cuda" test_multipath) +add_physim_test(cuda.5g.channelsim.test_noise "unitary test that focuses on a single component: the add_noise_cuda function" test_noise) + +# --- Test Suite 1: 61440 samples, 2/4/8/32 TX - 2/4/8/32 RX --- +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test1 "16 channels 61440 samples 2T2R Batch" test_channel_scalability -c 16 -t 2 -r 2 -s 61440 -l 32 -m batch -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test2 "16 channels 61440 samples 2T2R Stream" test_channel_scalability -c 16 -t 2 -r 2 -s 61440 -l 32 -m stream -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test3 "16 channels 61440 samples 2T2R Serial" test_channel_scalability -c 16 -t 2 -r 2 -s 61440 -l 32 -m serial -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test4 "16 channels 61440 samples 4T2R Batch" test_channel_scalability -c 16 -t 4 -r 2 -s 61440 -l 32 -m batch -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test5 "16 channels 61440 samples 4T2R Stream" test_channel_scalability -c 16 -t 4 -r 2 -s 61440 -l 32 -m stream -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test6 "16 channels 61440 samples 4T2R Serial" test_channel_scalability -c 16 -t 4 -r 2 -s 61440 -l 32 -m serial -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test7 "16 channels 61440 samples 8T2R Batch" test_channel_scalability -c 16 -t 8 -r 2 -s 61440 -l 32 -m batch -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test8 "16 channels 61440 samples 8T2R Stream" test_channel_scalability -c 16 -t 8 -r 2 -s 61440 -l 32 -m stream -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test9 "16 channels 61440 samples 8T2R Serial" test_channel_scalability -c 16 -t 8 -r 2 -s 61440 -l 32 -m serial -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test10 "16 channels 61440 samples 32T2R Batch" test_channel_scalability -c 16 -t 32 -r 2 -s 61440 -l 32 -m batch -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test11 "16 channels 61440 samples 32T2R Stream" test_channel_scalability -c 16 -t 32 -r 2 -s 61440 -l 32 -m stream -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test12 "16 channels 61440 samples 32T2R Serial" test_channel_scalability -c 16 -t 32 -r 2 -s 61440 -l 32 -m serial -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test13 "16 channels 61440 samples 2T4R Batch" test_channel_scalability -c 16 -t 2 -r 4 -s 61440 -l 32 -m batch -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test14 "16 channels 61440 samples 2T4R Stream" test_channel_scalability -c 16 -t 2 -r 4 -s 61440 -l 32 -m stream -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test15 "16 channels 61440 samples 2T4R Serial" test_channel_scalability -c 16 -t 2 -r 4 -s 61440 -l 32 -m serial -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test16 "16 channels 61440 samples 2T8R Batch" test_channel_scalability -c 16 -t 2 -r 8 -s 61440 -l 32 -m batch -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test17 "16 channels 61440 samples 2T8R Stream" test_channel_scalability -c 16 -t 2 -r 8 -s 61440 -l 32 -m stream -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test18 "16 channels 61440 samples 2T8R Serial" test_channel_scalability -c 16 -t 2 -r 8 -s 61440 -l 32 -m serial -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test19 "16 channels 61440 samples 2T32R Batch" test_channel_scalability -c 16 -t 2 -r 32 -s 61440 -l 32 -m batch -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test20 "16 channels 61440 samples 2T32R Stream" test_channel_scalability -c 16 -t 2 -r 32 -s 61440 -l 32 -m stream -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test21 "16 channels 61440 samples 2T32R Serial" test_channel_scalability -c 16 -t 2 -r 32 -s 61440 -l 32 -m serial -n 12) + +# --- Test Suite 2: 122880 samples, 2/4/8/32 TX - 2/4/8/32 RX --- +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test22 "16 channels 122880 samples 2T2R Batch" test_channel_scalability -c 16 -t 2 -r 2 -s 122880 -l 32 -m batch -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test23 "16 channels 122880 samples 2T2R Stream" test_channel_scalability -c 16 -t 2 -r 2 -s 122880 -l 32 -m stream -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test24 "16 channels 122880 samples 2T2R Serial" test_channel_scalability -c 16 -t 2 -r 2 -s 122880 -l 32 -m serial -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test25 "16 channels 122880 samples 4T2R Batch" test_channel_scalability -c 16 -t 4 -r 2 -s 122880 -l 32 -m batch -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test26 "16 channels 122880 samples 4T2R Stream" test_channel_scalability -c 16 -t 4 -r 2 -s 122880 -l 32 -m stream -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test27 "16 channels 122880 samples 4T2R Serial" test_channel_scalability -c 16 -t 4 -r 2 -s 122880 -l 32 -m serial -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test28 "16 channels 122880 samples 8T2R Batch" test_channel_scalability -c 16 -t 8 -r 2 -s 122880 -l 32 -m batch -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test29 "16 channels 122880 samples 8T2R Stream" test_channel_scalability -c 16 -t 8 -r 2 -s 122880 -l 32 -m stream -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test30 "16 channels 122880 samples 8T2R Serial" test_channel_scalability -c 16 -t 8 -r 2 -s 122880 -l 32 -m serial -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test31 "16 channels 122880 samples 32T2R Batch" test_channel_scalability -c 16 -t 32 -r 2 -s 122880 -l 32 -m batch -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test32 "16 channels 122880 samples 32T2R Stream" test_channel_scalability -c 16 -t 32 -r 2 -s 122880 -l 32 -m stream -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test33 "16 channels 122880 samples 32T2R Serial" test_channel_scalability -c 16 -t 32 -r 2 -s 122880 -l 32 -m serial -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test34 "16 channels 122880 samples 2T4R Batch" test_channel_scalability -c 16 -t 2 -r 4 -s 122880 -l 32 -m batch -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test35 "16 channels 122880 samples 2T4R Stream" test_channel_scalability -c 16 -t 2 -r 4 -s 122880 -l 32 -m stream -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test36 "16 channels 122880 samples 2T4R Serial" test_channel_scalability -c 16 -t 2 -r 4 -s 122880 -l 32 -m serial -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test37 "16 channels 122880 samples 2T8R Batch" test_channel_scalability -c 16 -t 2 -r 8 -s 122880 -l 32 -m batch -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test38 "16 channels 122880 samples 2T8R Stream" test_channel_scalability -c 16 -t 2 -r 8 -s 122880 -l 32 -m stream -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test39 "16 channels 122880 samples 2T8R Serial" test_channel_scalability -c 16 -t 2 -r 8 -s 122880 -l 32 -m serial -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test40 "16 channels 122880 samples 2T32R Batch" test_channel_scalability -c 16 -t 2 -r 32 -s 122880 -l 32 -m batch -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test41 "16 channels 122880 samples 2T32R Stream" test_channel_scalability -c 16 -t 2 -r 32 -s 122880 -l 32 -m stream -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test42 "16 channels 122880 samples 2T32R Serial" test_channel_scalability -c 16 -t 2 -r 32 -s 122880 -l 32 -m serial -n 12) + +# --- Test Suite 3: 61440 samples, 128 channel length, 2/4/8/32 TX - 2/4/8/32 RX -- +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test43 "16 channels 61440 samples long channel 2T2R Batch" test_channel_scalability -c 16 -t 2 -r 2 -s 61440 -l 128 -m batch -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test44 "16 channels 61440 samples long channel 2T2R Stream" test_channel_scalability -c 16 -t 2 -r 2 -s 61440 -l 128 -m stream -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test45 "16 channels 61440 samples long channel 2T2R Serial" test_channel_scalability -c 16 -t 2 -r 2 -s 61440 -l 128 -m serial -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test46 "16 channels 61440 samples long channel 4T2R Batch" test_channel_scalability -c 16 -t 4 -r 2 -s 61440 -l 128 -m batch -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test47 "16 channels 61440 samples long channel 4T2R Stream" test_channel_scalability -c 16 -t 4 -r 2 -s 61440 -l 128 -m stream -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test48 "16 channels 61440 samples long channel 4T2R Serial" test_channel_scalability -c 16 -t 4 -r 2 -s 61440 -l 128 -m serial -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test49 "16 channels 61440 samples long channel 8T2R Batch" test_channel_scalability -c 16 -t 8 -r 2 -s 61440 -l 128 -m batch -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test50 "16 channels 61440 samples long channel 8T2R Stream" test_channel_scalability -c 16 -t 8 -r 2 -s 61440 -l 128 -m stream -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test51 "16 channels 61440 samples long channel 8T2R Serial" test_channel_scalability -c 16 -t 8 -r 2 -s 61440 -l 128 -m serial -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test52 "16 channels 61440 samples long channel 2T4R Batch" test_channel_scalability -c 16 -t 2 -r 4 -s 61440 -l 128 -m batch -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test53 "16 channels 61440 samples long channel 2T4R Stream" test_channel_scalability -c 16 -t 2 -r 4 -s 61440 -l 128 -m stream -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test54 "16 channels 61440 samples long channel 2T4R Serial" test_channel_scalability -c 16 -t 2 -r 4 -s 61440 -l 128 -m serial -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test55 "16 channels 61440 samples long channel 2T8R Batch" test_channel_scalability -c 16 -t 2 -r 8 -s 61440 -l 128 -m batch -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test56 "16 channels 61440 samples long channel 2T8R Stream" test_channel_scalability -c 16 -t 2 -r 8 -s 61440 -l 128 -m stream -n 12) +add_physim_test(cuda.5g.channelsim.test_channel_scalability.test57 "16 channels 61440 samples long channel 2T8R Serial" test_channel_scalability -c 16 -t 2 -r 8 -s 61440 -l 128 -m serial -n 12) + +endif() + if(PHYSIM_CHECK_FILES) foreach(PHYSIM_CHECK_FILE IN LISTS PHYSIM_CHECK_FILES) include("${CMAKE_CURRENT_SOURCE_DIR}/${PHYSIM_CHECK_FILE}") diff --git a/openair1/SIMULATION/tests/ThresholdsCuda.cmake b/openair1/SIMULATION/tests/ThresholdsCuda.cmake new file mode 100644 index 0000000000..9f4aab802f --- /dev/null +++ b/openair1/SIMULATION/tests/ThresholdsCuda.cmake @@ -0,0 +1,296 @@ +# ============================================================ +# Test Suite 1: 61440 samples, short channel (l=32) +# ============================================================ + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test1 "Total CPU Time" "< 142200") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test1 "Total GPU Time" "< 5700") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test1 "Avg Time per Channel - CPU" LOWER 100 UPPER 8900) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test1 "Avg Time per Channel - GPU" LOWER 100 UPPER 400) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test2 "Total CPU Time" "< 142200") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test2 "Total GPU Time" "< 2700") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test2 "Avg Time per Channel - CPU" LOWER 100 UPPER 8900) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test2 "Avg Time per Channel - GPU" LOWER 100 UPPER 200) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test3 "Total CPU Time" "< 142200") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test3 "Total GPU Time" "< 5300") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test3 "Avg Time per Channel - CPU" LOWER 100 UPPER 8900) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test3 "Avg Time per Channel - GPU" LOWER 100 UPPER 300) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test4 "Total CPU Time" "< 268000") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test4 "Total GPU Time" "< 12000") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test4 "Avg Time per Channel - CPU" LOWER 100 UPPER 16700) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test4 "Avg Time per Channel - GPU" LOWER 100 UPPER 800) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test5 "Total CPU Time" "< 268500") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test5 "Total GPU Time" "< 6400") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test5 "Avg Time per Channel - CPU" LOWER 100 UPPER 16700) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test5 "Avg Time per Channel - GPU" LOWER 100 UPPER 400) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test6 "Total CPU Time" "< 268200") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test6 "Total GPU Time" "< 9200") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test6 "Avg Time per Channel - CPU" LOWER 100 UPPER 16700) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test6 "Avg Time per Channel - GPU" LOWER 100 UPPER 600) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test7 "Total CPU Time" "< 522200") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test7 "Total GPU Time" "< 21800") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test7 "Avg Time per Channel - CPU" LOWER 100 UPPER 32600) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test7 "Avg Time per Channel - GPU" LOWER 100 UPPER 1400) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test8 "Total CPU Time" "< 522200") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test8 "Total GPU Time" "< 17400") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test8 "Avg Time per Channel - CPU" LOWER 100 UPPER 32600) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test8 "Avg Time per Channel - GPU" LOWER 100 UPPER 1100) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test9 "Total CPU Time" "< 522200") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test9 "Total GPU Time" "< 25300") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test9 "Avg Time per Channel - CPU" LOWER 100 UPPER 32600) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test9 "Avg Time per Channel - GPU" LOWER 100 UPPER 1600) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test10 "Total CPU Time" "< 2065800") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test10 "Total GPU Time" "< 79400") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test10 "Avg Time per Channel - CPU" LOWER 100 UPPER 129100) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test10 "Avg Time per Channel - GPU" LOWER 100 UPPER 5000) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test11 "Total CPU Time" "< 2069700") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test11 "Total GPU Time" "< 66800") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test11 "Avg Time per Channel - CPU" LOWER 100 UPPER 129400) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test11 "Avg Time per Channel - GPU" LOWER 100 UPPER 4200) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test12 "Total CPU Time" "< 2066500") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test12 "Total GPU Time" "< 86800") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test12 "Avg Time per Channel - CPU" LOWER 100 UPPER 129200) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test12 "Avg Time per Channel - GPU" LOWER 100 UPPER 5400) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test13 "Total CPU Time" "< 283700") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test13 "Total GPU Time" "< 6600") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test13 "Avg Time per Channel - CPU" LOWER 100 UPPER 17700) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test13 "Avg Time per Channel - GPU" LOWER 100 UPPER 400) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test14 "Total CPU Time" "< 283800") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test14 "Total GPU Time" "< 3000") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test14 "Avg Time per Channel - CPU" LOWER 100 UPPER 17700) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test14 "Avg Time per Channel - GPU" LOWER 100 UPPER 200) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test15 "Total CPU Time" "< 283800") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test15 "Total GPU Time" "< 8700") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test15 "Avg Time per Channel - CPU" LOWER 100 UPPER 17700) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test15 "Avg Time per Channel - GPU" LOWER 100 UPPER 500) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test16 "Total CPU Time" "< 566900") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test16 "Total GPU Time" "< 7080") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test16 "Avg Time per Channel - CPU" LOWER 100 UPPER 35400) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test16 "Avg Time per Channel - GPU" LOWER 100 UPPER 400) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test17 "Total CPU Time" "< 566800") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test17 "Total GPU Time" "< 4200") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test17 "Avg Time per Channel - CPU" LOWER 100 UPPER 35400) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test17 "Avg Time per Channel - GPU" LOWER 100 UPPER 300) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test18 "Total CPU Time" "< 566500") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test18 "Total GPU Time" "< 16740") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test18 "Avg Time per Channel - CPU" LOWER 100 UPPER 35400) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test18 "Avg Time per Channel - GPU" LOWER 100 UPPER 1040) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test19 "Total CPU Time" "< 2280200") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test19 "Total GPU Time" "< 10390") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test19 "Avg Time per Channel - CPU" LOWER 100 UPPER 142500) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test19 "Avg Time per Channel - GPU" LOWER 100 UPPER 650) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test20 "Total CPU Time" "< 2279300") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test20 "Total GPU Time" "< 5760") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test20 "Avg Time per Channel - CPU" LOWER 100 UPPER 142500) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test20 "Avg Time per Channel - GPU" LOWER 100 UPPER 400) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test21 "Total CPU Time" "< 2282200") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test21 "Total GPU Time" "< 38900") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test21 "Avg Time per Channel - CPU" LOWER 100 UPPER 142600) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test21 "Avg Time per Channel - GPU" LOWER 100 UPPER 2400) + +# ============================================================ +# Test Suite 2: 122880 samples, short channel (l=32) +# ============================================================ + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test22 "Total CPU Time" "< 283400") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test22 "Total GPU Time" "< 10130") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test22 "Avg Time per Channel - CPU" LOWER 100 UPPER 17700) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test22 "Avg Time per Channel - GPU" LOWER 100 UPPER 600) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test23 "Total CPU Time" "< 283700") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test23 "Total GPU Time" "< 6300") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test23 "Avg Time per Channel - CPU" LOWER 100 UPPER 17700) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test23 "Avg Time per Channel - GPU" LOWER 100 UPPER 400) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test24 "Total CPU Time" "< 284000") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test24 "Total GPU Time" "< 12490") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test24 "Avg Time per Channel - CPU" LOWER 100 UPPER 17700) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test24 "Avg Time per Channel - GPU" LOWER 100 UPPER 800) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test25 "Total CPU Time" "< 535500") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test25 "Total GPU Time" "< 24940") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test25 "Avg Time per Channel - CPU" LOWER 100 UPPER 33400) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test25 "Avg Time per Channel - GPU" LOWER 100 UPPER 1560) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test26 "Total CPU Time" "< 547200") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test26 "Total GPU Time" "< 15600") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test26 "Avg Time per Channel - CPU" LOWER 100 UPPER 34200) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test26 "Avg Time per Channel - GPU" LOWER 100 UPPER 1000) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test27 "Total CPU Time" "< 536000") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test27 "Total GPU Time" "< 29030") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test27 "Avg Time per Channel - CPU" LOWER 100 UPPER 33500) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test27 "Avg Time per Channel - GPU" LOWER 100 UPPER 1800) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test28 "Total CPU Time" "< 1044020") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test28 "Total GPU Time" "< 52100") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test28 "Avg Time per Channel - CPU" LOWER 100 UPPER 65200) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test28 "Avg Time per Channel - GPU" LOWER 100 UPPER 3260) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test29 "Total CPU Time" "< 1043540") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test29 "Total GPU Time" "< 38890") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test29 "Avg Time per Channel - CPU" LOWER 100 UPPER 65200) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test29 "Avg Time per Channel - GPU" LOWER 100 UPPER 2430) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test30 "Total CPU Time" "< 1043740") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test30 "Total GPU Time" "< 43320") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test30 "Avg Time per Channel - CPU" LOWER 100 UPPER 65200) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test30 "Avg Time per Channel - GPU" LOWER 100 UPPER 2710) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test31 "Total CPU Time" "< 4131800") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test31 "Total GPU Time" "< 211840") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test31 "Avg Time per Channel - CPU" LOWER 100 UPPER 258200) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test31 "Avg Time per Channel - GPU" LOWER 100 UPPER 13240) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test32 "Total CPU Time" "< 4136320") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test32 "Total GPU Time" "< 110880") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test32 "Avg Time per Channel - CPU" LOWER 100 UPPER 258520) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test32 "Avg Time per Channel - GPU" LOWER 100 UPPER 6930) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test33 "Total CPU Time" "< 4134310") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test33 "Total GPU Time" "< 150710") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test33 "Avg Time per Channel - CPU" LOWER 100 UPPER 258400) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test33 "Avg Time per Channel - GPU" LOWER 100 UPPER 9420) + +# ============================================================ +# Test Suite 2 & 3 continued +# ============================================================ + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test34 "Total CPU Time" "< 569000") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test34 "Total GPU Time" "< 10460") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test34 "Avg Time per Channel - CPU" LOWER 100 UPPER 35600) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test34 "Avg Time per Channel - GPU" LOWER 100 UPPER 654) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test35 "Total CPU Time" "< 568900") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test35 "Total GPU Time" "< 6890") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test35 "Avg Time per Channel - CPU" LOWER 100 UPPER 35600) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test35 "Avg Time per Channel - GPU" LOWER 100 UPPER 400) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test36 "Total CPU Time" "< 569100") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test36 "Total GPU Time" "< 20770") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test36 "Avg Time per Channel - CPU" LOWER 100 UPPER 35600) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test36 "Avg Time per Channel - GPU" LOWER 100 UPPER 1300) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test37 "Total CPU Time" "< 1137170") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test37 "Total GPU Time" "< 11250") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test37 "Avg Time per Channel - CPU" LOWER 100 UPPER 71100) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test37 "Avg Time per Channel - GPU" LOWER 100 UPPER 700) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test38 "Total CPU Time" "< 1136670") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test38 "Total GPU Time" "< 7050") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test38 "Avg Time per Channel - CPU" LOWER 100 UPPER 71000) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test38 "Avg Time per Channel - GPU" LOWER 100 UPPER 400) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test39 "Total CPU Time" "< 1139410") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test39 "Total GPU Time" "< 30100") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test39 "Avg Time per Channel - CPU" LOWER 100 UPPER 71200) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test39 "Avg Time per Channel - GPU" LOWER 100 UPPER 1900) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test40 "Total CPU Time" "< 4632760") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test40 "Total GPU Time" "< 18070") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test40 "Avg Time per Channel - CPU" LOWER 100 UPPER 289500) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test40 "Avg Time per Channel - GPU" LOWER 100 UPPER 1130) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test41 "Total CPU Time" "< 4656730") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test41 "Total GPU Time" "< 10540") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test41 "Avg Time per Channel - CPU" LOWER 100 UPPER 291000) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test41 "Avg Time per Channel - GPU" LOWER 100 UPPER 700) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test42 "Total CPU Time" "< 4658160") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test42 "Total GPU Time" "< 72580") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test42 "Avg Time per Channel - CPU" LOWER 100 UPPER 291100) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test42 "Avg Time per Channel - GPU" LOWER 100 UPPER 4540) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test43 "Total CPU Time" "< 527200") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test43 "Total GPU Time" "< 5280") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test43 "Avg Time per Channel - CPU" LOWER 100 UPPER 33000) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test43 "Avg Time per Channel - GPU" LOWER 100 UPPER 330) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test44 "Total CPU Time" "< 526800") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test44 "Total GPU Time" "< 3020") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test44 "Avg Time per Channel - CPU" LOWER 100 UPPER 32900) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test44 "Avg Time per Channel - GPU" LOWER 100 UPPER 200) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test45 "Total CPU Time" "< 527100") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test45 "Total GPU Time" "< 5660") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test45 "Avg Time per Channel - CPU" LOWER 100 UPPER 32900) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test45 "Avg Time per Channel - GPU" LOWER 100 UPPER 400) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test46 "Total CPU Time" "< 1039350") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test46 "Total GPU Time" "< 10230") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test46 "Avg Time per Channel - CPU" LOWER 100 UPPER 65000) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test46 "Avg Time per Channel - GPU" LOWER 100 UPPER 640) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test47 "Total CPU Time" "< 1039770") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test47 "Total GPU Time" "< 6780") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test47 "Avg Time per Channel - CPU" LOWER 100 UPPER 65000) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test47 "Avg Time per Channel - GPU" LOWER 100 UPPER 400) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test48 "Total CPU Time" "< 1039390") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test48 "Total GPU Time" "< 12420") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test48 "Avg Time per Channel - CPU" LOWER 100 UPPER 65000) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test48 "Avg Time per Channel - GPU" LOWER 100 UPPER 800) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test49 "Total CPU Time" "< 2042770") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test49 "Total GPU Time" "< 19430") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test49 "Avg Time per Channel - CPU" LOWER 100 UPPER 127700) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test49 "Avg Time per Channel - GPU" LOWER 100 UPPER 1200) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test50 "Total CPU Time" "< 2040890") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test50 "Total GPU Time" "< 17950") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test50 "Avg Time per Channel - CPU" LOWER 100 UPPER 127600) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test50 "Avg Time per Channel - GPU" LOWER 100 UPPER 1100) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test51 "Total CPU Time" "< 2045820") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test51 "Total GPU Time" "< 26450") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test51 "Avg Time per Channel - CPU" LOWER 100 UPPER 127900) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test51 "Avg Time per Channel - GPU" LOWER 100 UPPER 1700) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test52 "Total CPU Time" "< 1052280") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test52 "Total GPU Time" "< 6120") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test52 "Avg Time per Channel - CPU" LOWER 100 UPPER 65800) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test52 "Avg Time per Channel - GPU" LOWER 100 UPPER 400) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test53 "Total CPU Time" "< 1052410") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test53 "Total GPU Time" "< 3170") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test53 "Avg Time per Channel - CPU" LOWER 100 UPPER 65800) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test53 "Avg Time per Channel - GPU" LOWER 100 UPPER 200) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test54 "Total CPU Time" "< 1052480") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test54 "Total GPU Time" "< 9340") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test54 "Avg Time per Channel - CPU" LOWER 100 UPPER 65800) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test54 "Avg Time per Channel - GPU" LOWER 100 UPPER 600) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test55 "Total CPU Time" "< 2058590") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test55 "Total GPU Time" "< 6960") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test55 "Avg Time per Channel - CPU" LOWER 100 UPPER 128700) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test55 "Avg Time per Channel - GPU" LOWER 100 UPPER 400) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test56 "Total CPU Time" "< 2058740") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test56 "Total GPU Time" "< 4450") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test56 "Avg Time per Channel - CPU" LOWER 100 UPPER 128700) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test56 "Avg Time per Channel - GPU" LOWER 100 UPPER 300) + +check_threshold(cuda.5g.channelsim.test_channel_scalability.test57 "Total CPU Time" "< 2059020") +check_threshold(cuda.5g.channelsim.test_channel_scalability.test57 "Total GPU Time" "< 18250") +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test57 "Avg Time per Channel - CPU" LOWER 100 UPPER 128700) +check_threshold_range(cuda.5g.channelsim.test_channel_scalability.test57 "Avg Time per Channel - GPU" LOWER 100 UPPER 1100) diff --git a/openair1/SIMULATION/tests/analyze-timing.sh b/openair1/SIMULATION/tests/analyze-timing.sh index f88b595a97..c1fb9cd48b 100755 --- a/openair1/SIMULATION/tests/analyze-timing.sh +++ b/openair1/SIMULATION/tests/analyze-timing.sh @@ -47,7 +47,7 @@ while [ $# -gt 0 ]; do } else { r = "FAIL"; } - RESULTS['${NUM}']=sprintf("CHECK %-35s %7.2f %-8s %s", "'${PATTERN}'", meas, " '${COND}'", r); + RESULTS['${NUM}']=sprintf("CHECK %-40s %10.2f %-10s %s", "'${PATTERN}'", meas, "'${COND}'", r); } ' diff --git a/openair2/COMMON/e1ap_messages_types.h b/openair2/COMMON/e1ap_messages_types.h index 711c126dab..48cfef98f3 100644 --- a/openair2/COMMON/e1ap_messages_types.h +++ b/openair2/COMMON/e1ap_messages_types.h @@ -268,8 +268,8 @@ typedef struct up_params_s { /* IE SDAP Configuration (clause 9.3.1.39 of 3GPP TS 38.463) */ typedef struct bearer_context_sdap_config_s { long defaultDRB; - long sDAP_Header_UL; - long sDAP_Header_DL; + bool sDAP_Header_UL; + bool sDAP_Header_DL; } bearer_context_sdap_config_t; /* IE PDCP Configuration (clause 9.3.1.38 of 3GPP TS 38.463) */ @@ -404,11 +404,17 @@ typedef struct DRB_nGRAN_to_modify_s { // DL UP Transport Layer Information (O) (clause 9.3.1.13, 9.3.2.1) int numDlUpParam; up_params_t DlUpParamList[E1AP_MAX_NUM_UP_PARAM]; - // QoS Flows Information To Modify (O) (clause 9.3.1.25, 9.3.1.26) - int numQosFlow2Setup; + // Flow Mapping Information (O) (clause 9.3.1.25, 9.3.1.26) + int numQosFlowsMod; qos_flow_to_setup_t qosFlows[E1AP_MAX_NUM_QOS_FLOWS]; } DRB_nGRAN_to_mod_t; +/* DRB To Remove Item (NG-RAN) clause 9.3.1.11 */ +typedef struct { + // DRB ID (M) + long id; +} drb_to_remove_t; + typedef enum e1ap_indication_e { SECURITY_REQUIRED = 0, SECURITY_PREFERRED, @@ -472,6 +478,9 @@ typedef struct pdu_session_to_mod_s { long numDRB2Modify; // DRB To Modify Item (1..) DRB_nGRAN_to_mod_t DRBnGRanModList[E1AP_MAX_NUM_DRBS]; + // DRB To Remove List (0..maxnoofDRBs) + int n_drb_to_remove; + drb_to_remove_t drbs_to_remove[E1AP_MAX_NUM_DRBS]; } pdu_session_to_mod_t; /** PDU Session Resource To Remove List (3GPP TS 38.463 clause 9.3.3.12) */ diff --git a/openair2/COMMON/f1ap_messages_types.h b/openair2/COMMON/f1ap_messages_types.h index 0cd090ebc7..c891a44adc 100644 --- a/openair2/COMMON/f1ap_messages_types.h +++ b/openair2/COMMON/f1ap_messages_types.h @@ -74,12 +74,11 @@ */ #define F1AP_TRANSPORT_LAYER_ADDRESS_SIZE (160 / 8) - -// Note this should be 512 from maxval in 38.473 -#define F1AP_MAX_NB_CELLS 2 +/* F1 Setup Request (9.3.1.4 of 3GPP TS 38.473) */ +#define F1AP_MAX_NB_CELLS 512 #define F1AP_MAX_NO_OF_TNL_ASSOCIATIONS 32 -#define F1AP_MAX_NO_UE_ID 1024 +#define F1AP_MAX_NO_UE_ID 65536 /* 9.3.1.42 of 3GPP TS 38.473 - gNB-CU System Information */ #define F1AP_MAX_NO_SIB_TYPES 32 @@ -122,54 +121,53 @@ typedef struct f1ap_tdd_info_t { } f1ap_tdd_info_t; typedef struct f1ap_served_cell_info_t { - // NR CGI + // NR CGI (Mandatory) plmn_id_t plmn; uint64_t nr_cellid; // NR Global Cell Id - - // NR Physical Cell Ids + // NR PCI (Mandatory) uint16_t nr_pci; - - /* Tracking area code */ + // 5GS TAC (Optional) uint32_t *tac; - - // Number of slice support items (max 16, could be increased to as much as 1024) + // TAI Slice Support List (Optional) uint16_t num_ssi; nssai_t nssai[MAX_NUM_SLICES]; - + // NR-Mode-Info (Mandatory) f1ap_mode_t mode; union { f1ap_fdd_info_t fdd; f1ap_tdd_info_t tdd; }; - + // Measurement Timing Configuration (Mandatory) uint8_t *measurement_timing_config; int measurement_timing_config_len; } f1ap_served_cell_info_t; typedef struct f1ap_gnb_du_system_info_t { + // MIB message (Mandatory) uint8_t *mib; int mib_length; + // SIB1 message (Mandatory) uint8_t *sib1; int sib1_length; } f1ap_gnb_du_system_info_t; typedef struct f1ap_setup_req_s { - /// ulong transaction id + // Transaction ID (Mandatory) uint64_t transaction_id; - - // F1_Setup_Req payload + // gNB-DU ID (Mandatory) uint64_t gNB_DU_id; + // gNB-DU Name (Optional) char *gNB_DU_name; - - /// rrc version + // gNB-DU RRC version (Mandatory) uint8_t rrc_ver[3]; - - /// number of DU cells available - uint16_t num_cells_available; //0< num_cells_available <= 512; + // gNB-DU Served Cells List (0..1) + uint16_t num_cells_available; // (1..512) struct { + // Served Cell Information (Mandatory) f1ap_served_cell_info_t info; + // gNB-DU System Information (Optional) f1ap_gnb_du_system_info_t *sys_info; - } cell[F1AP_MAX_NB_CELLS]; + } *cell; } f1ap_setup_req_t; typedef struct f1ap_du_register_req_t { @@ -203,8 +201,8 @@ typedef struct f1ap_setup_resp_s { /// string holding gNB_CU_name char *gNB_CU_name; /// number of DU cells to activate - uint16_t num_cells_to_activate; //0< num_cells_to_activate <= 512; - served_cells_to_activate_t cells_to_activate[F1AP_MAX_NB_CELLS]; + uint16_t num_cells_to_activate; + served_cells_to_activate_t *cells_to_activate; /// rrc version uint8_t rrc_ver[3]; @@ -215,8 +213,8 @@ typedef struct f1ap_gnb_cu_configuration_update_s { /// Transaction ID uint64_t transaction_id; /// number of DU cells to activate - uint16_t num_cells_to_activate; //0< num_cells_to_activate/mod <= 512; - served_cells_to_activate_t cells_to_activate[F1AP_MAX_NB_CELLS]; + uint16_t num_cells_to_activate; + served_cells_to_activate_t *cells_to_activate; } f1ap_gnb_cu_configuration_update_t; typedef struct f1ap_setup_failure_s { @@ -236,7 +234,7 @@ typedef struct f1ap_gnb_cu_configuration_update_acknowledge_s { plmn_id_t plmn; uint64_t nr_cellid; uint16_t cause; - } cells_failed_to_be_activated[F1AP_MAX_NB_CELLS]; + } * cells_failed_to_be_activated; int have_criticality; uint16_t criticality_diagnostics; // gNB-CU TNL Association Setup List @@ -245,14 +243,14 @@ typedef struct f1ap_gnb_cu_configuration_update_acknowledge_s { // gNB-CU TNL Association Failed to Setup List uint16_t noofTNLAssociations_failed; f1ap_cp_tnl_t tnlAssociations_failed[F1AP_MAX_NO_OF_TNL_ASSOCIATIONS]; - // Dedicated SI Delivery Needed UE List + // Dedicated SI Delivery Needed UE List (max 65536 UE IDs) uint16_t noofDedicatedSIDeliveryNeededUEs; struct { uint32_t gNB_CU_ue_id; // NR CGI plmn_id_t ue_plmn; uint64_t ue_nr_cellid; - } dedicatedSIDeliveryNeededUEs[F1AP_MAX_NO_UE_ID]; + } * dedicatedSIDeliveryNeededUEs; } f1ap_gnb_cu_configuration_update_acknowledge_t; typedef struct f1ap_gnb_cu_configuration_update_failure_s { @@ -279,7 +277,7 @@ typedef struct f1ap_gnb_du_configuration_update_s { struct { f1ap_served_cell_info_t info; f1ap_gnb_du_system_info_t *sys_info; - } cell_to_add[F1AP_MAX_NB_CELLS]; + } * cell_to_add; /// int cells_to_modify uint16_t num_cells_to_modify; @@ -288,7 +286,7 @@ typedef struct f1ap_gnb_du_configuration_update_s { uint64_t old_nr_cellid; // NR Global Cell Id f1ap_served_cell_info_t info; f1ap_gnb_du_system_info_t *sys_info; - } cell_to_modify[F1AP_MAX_NB_CELLS]; + } * cell_to_modify; /// int cells_to_delete uint16_t num_cells_to_delete; @@ -296,9 +294,9 @@ typedef struct f1ap_gnb_du_configuration_update_s { // NR CGI plmn_id_t plmn; uint64_t nr_cellid; // NR Global Cell Id - } cell_to_delete[F1AP_MAX_NB_CELLS]; + } * cell_to_delete; - f1ap_cell_status_t status[F1AP_MAX_NB_CELLS]; + f1ap_cell_status_t *status; int num_status; /// gNB-DU unique ID, at least within a gNB-CU (0 .. 2^36 - 1) @@ -310,7 +308,7 @@ typedef struct f1ap_gnb_du_configuration_update_acknowledge_s { uint64_t transaction_id; /// number of DU cells to activate uint16_t num_cells_to_activate; // 0< num_cells_to_activate <= 512; - served_cells_to_activate_t cells_to_activate[F1AP_MAX_NB_CELLS]; + served_cells_to_activate_t *cells_to_activate; } f1ap_gnb_du_configuration_update_acknowledge_t; typedef struct f1ap_gnb_du_configuration_update_failure_s { diff --git a/openair2/COMMON/ngap_messages_types.h b/openair2/COMMON/ngap_messages_types.h index b7c6c281ec..111a131f39 100644 --- a/openair2/COMMON/ngap_messages_types.h +++ b/openair2/COMMON/ngap_messages_types.h @@ -226,18 +226,21 @@ typedef struct pdusession_setup_s { pdusession_associate_qosflow_t associated_qos_flows[MAX_QOS_FLOWS]; } pdusession_setup_t; +/* QoS Flow Add or Modify Response Item (3GPP TS 38.413 9.2.1.6) */ typedef struct qos_flow_tobe_modified_s { - uint8_t qfi; // 0~63 -} qos_flow_tobe_modified_t; + // QoS Flow Identifier + uint8_t qfi; +} qos_flow_addmod_response_item_t; +/* PDU Session Resource Modify Response Item (3GPP TS 38.413 9.2.1.6) */ typedef struct pdusession_modify_s { - /* Unique pdusession_id for the UE. */ + // PDU Session ID uint8_t pdusession_id; - + /* PDU Session Resource Modify Response Transfer */ + // QoS Flow Add or Modify Response List uint8_t nb_of_qos_flow; - // qos_flow_add_or_modify - qos_flow_tobe_modified_t qos[MAX_QOS_FLOWS]; + qos_flow_addmod_response_item_t qos[MAX_QOS_FLOWS]; } pdusession_modify_t; /* Cause (9.3.1.2 of 3GPP TS 38.413) */ @@ -889,17 +892,17 @@ typedef struct ngap_pdusession_modify_req_s { pdusession_resource_item_t pdusession[NGAP_MAX_PDU_SESSION]; } ngap_pdusession_modify_req_t; +/* 9.2.1.6 of 3GPP TS 38.413 */ typedef struct ngap_pdusession_modify_resp_s { + // RAN UE NGAP ID uint32_t gNB_ue_ngap_id; - - /* Number of pdusession modify-ed in the list */ + // AMF UE NGAP ID + uint64_t amf_ue_ngap_id; + // PDU Session Resource Modify Response List (0..256) uint16_t nb_of_pdusessions; - /* list of pdusession modify-ed by RRC layers */ pdusession_modify_t pdusessions[NGAP_MAX_PDU_SESSION]; - - /* Number of pdusession failed to be modify in list */ + // PDU Session Resource Failed to Modify List (0..256) uint16_t nb_of_pdusessions_failed; - /* list of pdusessions that failed to be modify */ pdusession_failed_t pdusessions_failed[NGAP_MAX_PDU_SESSION]; } ngap_pdusession_modify_resp_t; diff --git a/openair2/COMMON/rrc_messages_types.h b/openair2/COMMON/rrc_messages_types.h index cfc6914f4b..d11e6573c7 100644 --- a/openair2/COMMON/rrc_messages_types.h +++ b/openair2/COMMON/rrc_messages_types.h @@ -414,23 +414,6 @@ typedef struct NbIoTRrcConfigurationReq_s { long ue_TimersAndConstants_n311_NB; } NbIoTRrcConfigurationReq; -// gNB: GNB_APP -> RRC messages -typedef struct NRRrcConfigurationReq_s { - uint32_t tac; - plmn_id_t plmn[PLMN_LIST_MAX_SIZE]; - uint8_t num_plmn; - - bool um_on_default_drb; - bool enable_sdap; - int drbs; -} gNB_RrcConfigurationReq; - -typedef struct NRDuDlReq_s { - rnti_t rnti; - uint8_t *buf; - uint64_t srb_id; -} NRDuDlReq_t; - // eNB: realtime -> RRC messages typedef struct rrc_subframe_process_s { protocol_ctxt_t ctxt; diff --git a/openair2/E1AP/lib/e1ap_bearer_context_management.c b/openair2/E1AP/lib/e1ap_bearer_context_management.c index f2593f103c..15c472b10d 100644 --- a/openair2/E1AP/lib/e1ap_bearer_context_management.c +++ b/openair2/E1AP/lib/e1ap_bearer_context_management.c @@ -192,8 +192,8 @@ static E1AP_SDAP_Configuration_t e1_encode_sdap_config(const bearer_context_sdap { E1AP_SDAP_Configuration_t out = {0}; out.defaultDRB = in->defaultDRB ? E1AP_DefaultDRB_true : E1AP_DefaultDRB_false; - out.sDAP_Header_UL = in->sDAP_Header_UL; - out.sDAP_Header_DL = in->sDAP_Header_DL; + out.sDAP_Header_UL = in->sDAP_Header_UL ? E1AP_SDAP_Header_UL_present : E1AP_SDAP_Header_UL_absent; + out.sDAP_Header_DL = in->sDAP_Header_DL ? E1AP_SDAP_Header_DL_present : E1AP_SDAP_Header_DL_absent; return out; } @@ -201,8 +201,8 @@ static E1AP_SDAP_Configuration_t e1_encode_sdap_config(const bearer_context_sdap static bool e1_decode_sdap_config(bearer_context_sdap_config_t *out, const E1AP_SDAP_Configuration_t *in) { out->defaultDRB = in->defaultDRB == E1AP_DefaultDRB_true; - out->sDAP_Header_UL = in->sDAP_Header_UL; - out->sDAP_Header_DL = in->sDAP_Header_DL; + out->sDAP_Header_UL = in->sDAP_Header_UL == E1AP_SDAP_Header_UL_present; + out->sDAP_Header_DL = in->sDAP_Header_DL == E1AP_SDAP_Header_DL_present; return true; } @@ -212,8 +212,8 @@ static bool e1_decode_sdap_config(bearer_context_sdap_config_t *out, const E1AP_ static bool eq_sdap_config(const bearer_context_sdap_config_t *a, const bearer_context_sdap_config_t *b) { _E1_EQ_CHECK_LONG(a->defaultDRB, b->defaultDRB); - _E1_EQ_CHECK_LONG(a->sDAP_Header_UL, b->sDAP_Header_UL); - _E1_EQ_CHECK_LONG(a->sDAP_Header_DL, b->sDAP_Header_DL); + _E1_EQ_CHECK_INT(a->sDAP_Header_UL, b->sDAP_Header_UL); + _E1_EQ_CHECK_INT(a->sDAP_Header_DL, b->sDAP_Header_DL); return true; } @@ -1459,12 +1459,20 @@ static E1AP_PDU_Session_Resource_To_Modify_Item_t e1_encode_pdu_session_to_mod_i } } // QoS Flows to setup (O) - for (const qos_flow_to_setup_t *k = j->qosFlows; k < j->qosFlows + j->numQosFlow2Setup; k++) { + for (const qos_flow_to_setup_t *k = j->qosFlows; k < j->qosFlows + j->numQosFlowsMod; k++) { asn1cCalloc(drb2Mod->flow_Mapping_Information, flow_Mapping_Information); asn1cSequenceAdd(flow_Mapping_Information->list, E1AP_QoS_Flow_QoS_Parameter_Item_t, ie1_2); *ie1_2 = e1_encode_qos_flow_to_setup(k); } } + // DRB To Remove List (O) + if (in->n_drb_to_remove > 0) { + asn1cCalloc(out.dRB_To_Remove_List_NG_RAN, drb2Remove_List); + for (int r = 0; r < in->n_drb_to_remove; r++) { + asn1cSequenceAdd(drb2Remove_List->list, E1AP_DRB_To_Remove_Item_NG_RAN_t, drb2Rem); + drb2Rem->dRB_ID = in->drbs_to_remove[r].id; + } + } return out; } @@ -1740,12 +1748,21 @@ static bool e1_decode_pdu_session_to_mod_item(pdu_session_to_mod_t *out, const E // QoS Flows Information To Be Setup (O) if (drb2Mod->flow_Mapping_Information) { E1AP_QoS_Flow_QoS_Parameter_List_t *qos2SetupList = drb2Mod->flow_Mapping_Information; - drb->numQosFlow2Setup = qos2SetupList->list.count; + drb->numQosFlowsMod = qos2SetupList->list.count; for (int k = 0; k < qos2SetupList->list.count; k++) { CHECK_E1AP_DEC(e1_decode_qos_flow_to_setup(drb->qosFlows + k, qos2SetupList->list.array[k])); } } } + // DRB To Remove List (O) + if (in->dRB_To_Remove_List_NG_RAN) { + E1AP_DRB_To_Remove_List_NG_RAN_t *rm = in->dRB_To_Remove_List_NG_RAN; + out->n_drb_to_remove = rm->list.count; + for (int r = 0; r < rm->list.count; r++) { + E1AP_DRB_To_Remove_Item_NG_RAN_t *item = rm->list.array[r]; + out->drbs_to_remove[r].id = item->dRB_ID; + } + } return true; } @@ -1931,6 +1948,9 @@ static pdu_session_to_mod_t cp_pdu_session_to_mod_item(const pdu_session_to_mod_ // DRB to modify list for (int j = 0; j < msg->numDRB2Modify; j++) cp.DRBnGRanModList[j] = cp_drb_to_mod_item(&msg->DRBnGRanModList[j]); + // DRB to remove list + for (int r = 0; r < msg->n_drb_to_remove; r++) + cp.drbs_to_remove[r] = msg->drbs_to_remove[r]; _E1_CP_OPTIONAL_IE(&cp, msg, securityIndication); _E1_CP_OPTIONAL_IE(&cp, msg, UP_TL_information); return cp; @@ -1972,8 +1992,8 @@ static bool eq_security_ind(security_indication_t *a, security_indication_t *b) static bool eq_drb_to_mod(const DRB_nGRAN_to_mod_t *a, const DRB_nGRAN_to_mod_t *b) { _E1_EQ_CHECK_LONG(a->id, b->id); - _E1_EQ_CHECK_INT(a->numQosFlow2Setup, b->numQosFlow2Setup); - for (int i = 0; i < a->numQosFlow2Setup; i++) { + _E1_EQ_CHECK_INT(a->numQosFlowsMod, b->numQosFlowsMod); + for (int i = 0; i < a->numQosFlowsMod; i++) { if (!eq_qos_flow(&a->qosFlows[i], &b->qosFlows[i])) return false; } @@ -2021,6 +2041,10 @@ static bool eq_pdu_session_to_mod_item(const pdu_session_to_mod_t *a, const pdu_ return false; } _E1_EQ_CHECK_OPTIONAL_PTR(a, b, securityIndication); + _E1_EQ_CHECK_INT(a->n_drb_to_remove, b->n_drb_to_remove); + for (int r = 0; r < a->n_drb_to_remove; r++) { + _E1_EQ_CHECK_LONG(a->drbs_to_remove[r].id, b->drbs_to_remove[r].id); + } if (a->securityIndication && b->securityIndication) { if (!eq_security_ind(a->securityIndication, b->securityIndication)) return false; diff --git a/openair2/E1AP/lib/e1ap_lib_includes.h b/openair2/E1AP/lib/e1ap_lib_includes.h index adafbe0ae5..f58875a306 100644 --- a/openair2/E1AP/lib/e1ap_lib_includes.h +++ b/openair2/E1AP/lib/e1ap_lib_includes.h @@ -76,6 +76,8 @@ #include "E1AP_PDU-Session-Resource-To-Modify-Item.h" #include "E1AP_DRB-To-Modify-List-NG-RAN.h" #include "E1AP_DRB-To-Modify-Item-NG-RAN.h" +#include "E1AP_DRB-To-Remove-List-NG-RAN.h" +#include "E1AP_DRB-To-Remove-Item-NG-RAN.h" #include "E1AP_PDU-Session-Resource-To-Setup-Mod-Item.h" #include "E1AP_DRB-To-Setup-Mod-Item-NG-RAN.h" #include "E1AP_PDU-Session-Resource-To-Remove-Item.h" diff --git a/openair2/E1AP/tests/e1ap_lib_test.c b/openair2/E1AP/tests/e1ap_lib_test.c index 50116514fb..6a94e0df23 100644 --- a/openair2/E1AP/tests/e1ap_lib_test.c +++ b/openair2/E1AP/tests/e1ap_lib_test.c @@ -505,7 +505,7 @@ static void test_bearer_context_modification_request(void) .pdcp_config = malloc_or_fail(sizeof(*drb_to_mod.pdcp_config)), .pdcp_sn_status_requested = true, .pdcp_status = malloc_or_fail(sizeof(*drb_to_mod.pdcp_status)), - .numQosFlow2Setup = 1, + .numQosFlowsMod = 1, .qosFlows[0] = dummy_qos_flows, }; *drb_to_mod.pdcp_config = dummy_pdcp_config; @@ -515,6 +515,8 @@ static void test_bearer_context_modification_request(void) .sessionId = 1, .numDRB2Modify = 1, .DRBnGRanModList[0] = drb_to_mod, + .n_drb_to_remove = 1, + .drbs_to_remove[0].id = 2, }; DRB_nGRAN_to_setup_t drb_to_setup = { diff --git a/openair2/F1AP/CMakeLists.txt b/openair2/F1AP/CMakeLists.txt index 53ab2823ed..052964b3e1 100644 --- a/openair2/F1AP/CMakeLists.txt +++ b/openair2/F1AP/CMakeLists.txt @@ -3,7 +3,7 @@ add_subdirectory(MESSAGES) if(ENABLE_TESTS) add_executable(f1ap_ids_test f1ap_ids_test.c f1ap_ids.c) - target_link_libraries(f1ap_ids_test HASHTABLE minimal_lib) + target_link_libraries(f1ap_ids_test ds minimal_lib) add_dependencies(tests f1ap_ids_test) add_test(NAME F1AP_ID_test COMMAND f1ap_ids_test) diff --git a/openair2/F1AP/f1ap_ids.c b/openair2/F1AP/f1ap_ids.c index 5569d60c48..d222d9e35a 100644 --- a/openair2/F1AP/f1ap_ids.c +++ b/openair2/F1AP/f1ap_ids.c @@ -25,7 +25,7 @@ #include "f1ap_ids.h" #include -#include "common/utils/hashtable/hashtable.h" +#include "ds/hashtable.h" #include "common/utils/assertions.h" diff --git a/openair2/F1AP/lib/f1ap_interface_management.c b/openair2/F1AP/lib/f1ap_interface_management.c index 2253493832..55b4972d4b 100644 --- a/openair2/F1AP/lib/f1ap_interface_management.c +++ b/openair2/F1AP/lib/f1ap_interface_management.c @@ -423,7 +423,7 @@ static F1AP_ProtocolExtensionContainer_11023P34_t *write_slice_info(int num_ssi, /** * @brief F1AP Setup Request memory management */ -void free_f1ap_cell(const f1ap_served_cell_info_t *info, const f1ap_gnb_du_system_info_t *sys_info) +static void free_f1ap_cell(const f1ap_served_cell_info_t *info, const f1ap_gnb_du_system_info_t *sys_info) { if (sys_info) { free(sys_info->mib); @@ -842,8 +842,9 @@ bool decode_f1ap_setup_request(const F1AP_F1AP_PDU_t *pdu, f1ap_setup_req_t *out /* GNB_DU_Served_Cells_List */ out->num_cells_available = ie->value.choice.GNB_DU_Served_Cells_List.list.count; AssertError(out->num_cells_available > 0, return false, "at least 1 cell must be present"); + out->cell = calloc_or_fail(out->num_cells_available, sizeof(*out->cell)); /* Loop over gNB-DU Served Cells Items */ - for (int i = 0; i < out->num_cells_available; i++) { + for (int i = 0; i < out->num_cells_available && i < F1AP_MAX_NB_CELLS; i++) { F1AP_GNB_DU_Served_Cells_Item_t *served_cells_item = &(((F1AP_GNB_DU_Served_Cells_ItemIEs_t *)ie->value.choice.GNB_DU_Served_Cells_List.list.array[i]) ->value.choice.GNB_DU_Served_Cells_Item); @@ -885,7 +886,7 @@ bool decode_f1ap_setup_request(const F1AP_F1AP_PDU_t *pdu, f1ap_setup_req_t *out return true; } -f1ap_served_cell_info_t copy_f1ap_served_cell_info(const f1ap_served_cell_info_t *src) +static f1ap_served_cell_info_t copy_f1ap_served_cell_info(const f1ap_served_cell_info_t *src) { f1ap_served_cell_info_t dst = { .plmn = src->plmn, @@ -952,7 +953,9 @@ f1ap_setup_req_t cp_f1ap_setup_request(const f1ap_setup_req_t *msg) cp.transaction_id = msg->transaction_id; /* num_cells_available */ cp.num_cells_available = msg->num_cells_available; - for (int n = 0; n < msg->num_cells_available; n++) { + if (cp.num_cells_available > 0) + cp.cell = calloc_or_fail(cp.num_cells_available, sizeof(*cp.cell)); + for (int n = 0; n < msg->num_cells_available && n < F1AP_MAX_NB_CELLS; n++) { /* cell.info */ cp.cell[n].info = copy_f1ap_served_cell_info(&msg->cell[n].info); /* cell.sys_info */ @@ -972,7 +975,7 @@ bool eq_f1ap_setup_request(const f1ap_setup_req_t *a, const f1ap_setup_req_t *b) _F1_EQ_CHECK_STR(a->gNB_DU_name, b->gNB_DU_name); _F1_EQ_CHECK_LONG(a->transaction_id, b->transaction_id); _F1_EQ_CHECK_INT(a->num_cells_available, b->num_cells_available); - for (int i = 0; i < a->num_cells_available; i++) { + for (int i = 0; i < a->num_cells_available && i < F1AP_MAX_NB_CELLS; i++) { if (!eq_f1ap_cell_info(&a->cell[i].info, &b->cell[i].info)) return false; if (!eq_f1ap_sys_info(a->cell[i].sys_info, b->cell[i].sys_info)) @@ -992,8 +995,11 @@ void free_f1ap_setup_request(const f1ap_setup_req_t *msg) { DevAssert(msg != NULL); free(msg->gNB_DU_name); - for (int i = 0; i < msg->num_cells_available; i++) { - free_f1ap_cell(&msg->cell[i].info, msg->cell[i].sys_info); + if (msg->cell != NULL) { + for (int i = 0; i < msg->num_cells_available; i++) { + free_f1ap_cell(&msg->cell[i].info, msg->cell[i].sys_info); + } + free(msg->cell); } } @@ -1032,15 +1038,16 @@ F1AP_F1AP_PDU_t *encode_f1ap_setup_response(const f1ap_setup_resp_t *msg) OCTET_STRING_fromBuf(&ie2->value.choice.GNB_CU_Name, msg->gNB_CU_name, strlen(msg->gNB_CU_name)); } // Cells to be Activated List (O) - for (int i = 0; i < msg->num_cells_to_activate; i++) { + if (msg->num_cells_to_activate > 0) { + DevAssert(msg->num_cells_to_activate <= F1AP_MAX_NB_CELLS); asn1cSequenceAdd(out->protocolIEs.list, F1AP_F1SetupResponseIEs_t, ie3); ie3->id = F1AP_ProtocolIE_ID_id_Cells_to_be_Activated_List; ie3->criticality = F1AP_Criticality_reject; ie3->value.present = F1AP_F1SetupResponseIEs__value_PR_Cells_to_be_Activated_List; - asn1cSequenceAdd(ie3->value.choice.Cells_to_be_Activated_List.list, - F1AP_Cells_to_be_Activated_List_ItemIEs_t, - cells_to_be_activated_ies); - encode_cells_to_activate(&msg->cells_to_activate[i], cells_to_be_activated_ies); + for (int i = 0; i < msg->num_cells_to_activate; i++) { + asn1cSequenceAdd(ie3->value.choice.Cells_to_be_Activated_List.list, F1AP_Cells_to_be_Activated_List_ItemIEs_t, cells); + encode_cells_to_activate(&msg->cells_to_activate[i], cells); + } } // gNB-CU RRC version (M) asn1cSequenceAdd(out->protocolIEs.list, F1AP_F1SetupResponseIEs_t, ie4); @@ -1131,7 +1138,8 @@ bool decode_f1ap_setup_response(const F1AP_F1AP_PDU_t *pdu, f1ap_setup_resp_t *o out->num_cells_to_activate = Cells_to_be_Activated_List->list.count; // Loop Cells to be Activated List Items (count >= 1) AssertError(out->num_cells_to_activate > 0, return false, "At least 1 cell must be present"); - for (int i = 0; i < out->num_cells_to_activate; i++) { + out->cells_to_activate = calloc_or_fail(out->num_cells_to_activate, sizeof(*out->cells_to_activate)); + for (int i = 0; i < out->num_cells_to_activate && i < F1AP_MAX_NB_CELLS; i++) { const F1AP_Cells_to_be_Activated_List_ItemIEs_t *itemIEs = (F1AP_Cells_to_be_Activated_List_ItemIEs_t *)Cells_to_be_Activated_List->list.array[i]; if (!decode_cells_to_activate(&out->cells_to_activate[i], itemIEs)) @@ -1157,7 +1165,7 @@ bool eq_f1ap_setup_response(const f1ap_setup_resp_t *a, const f1ap_setup_resp_t _F1_EQ_CHECK_INT(a->num_cells_to_activate, b->num_cells_to_activate); _F1_EQ_CHECK_LONG(a->transaction_id, b->transaction_id); if (a->num_cells_to_activate) { - for (int i = 0; i < a->num_cells_to_activate; i++) { + for (int i = 0; i < a->num_cells_to_activate && i < F1AP_MAX_NB_CELLS; i++) { const served_cells_to_activate_t *a_cell = &a->cells_to_activate[i]; const served_cells_to_activate_t *b_cell = &b->cells_to_activate[i]; _F1_EQ_CHECK_LONG(a_cell->nr_cellid, b_cell->nr_cellid); @@ -1197,7 +1205,8 @@ f1ap_setup_resp_t cp_f1ap_setup_response(const f1ap_setup_resp_t *msg) cp.transaction_id = msg->transaction_id; /* num_cells_available */ cp.num_cells_to_activate = msg->num_cells_to_activate; - for (int n = 0; n < msg->num_cells_to_activate; n++) { + cp.cells_to_activate = calloc_or_fail(cp.num_cells_to_activate, sizeof(*cp.cells_to_activate)); + for (int n = 0; n < msg->num_cells_to_activate && n < F1AP_MAX_NB_CELLS; n++) { /* cell.info */ served_cells_to_activate_t *cp_cell = &cp.cells_to_activate[n]; const served_cells_to_activate_t *msg_cell = &msg->cells_to_activate[n]; @@ -1230,6 +1239,7 @@ void free_f1ap_setup_response(const f1ap_setup_resp_t *msg) for (int j = 0; j < msg->cells_to_activate[i].num_SI; j++) if (msg->cells_to_activate[i].SI_msg[j].SI_container_length > 0) free(msg->cells_to_activate[i].SI_msg[j].SI_container); + free(msg->cells_to_activate); } /* ==================================== @@ -1373,6 +1383,7 @@ F1AP_F1AP_PDU_t *encode_f1ap_du_configuration_update(const f1ap_gnb_du_configura /* mandatory */ /* c2. Served_Cells_To_Add */ if (msg->num_cells_to_add > 0) { + DevAssert(msg->num_cells_to_add <= F1AP_MAX_NB_CELLS); asn1cSequenceAdd(out->protocolIEs.list, F1AP_GNBDUConfigurationUpdateIEs_t, ie2); ie2->id = F1AP_ProtocolIE_ID_id_Served_Cells_To_Add_List; ie2->criticality = F1AP_Criticality_reject; @@ -1397,6 +1408,7 @@ F1AP_F1AP_PDU_t *encode_f1ap_du_configuration_update(const f1ap_gnb_du_configura /* mandatory */ /* c3. Served_Cells_To_Modify */ if (msg->num_cells_to_modify > 0) { + DevAssert(msg->num_cells_to_modify <= F1AP_MAX_NB_CELLS); asn1cSequenceAdd(out->protocolIEs.list, F1AP_GNBDUConfigurationUpdateIEs_t, ie3); ie3->id = F1AP_ProtocolIE_ID_id_Served_Cells_To_Modify_List; ie3->criticality = F1AP_Criticality_reject; @@ -1426,6 +1438,7 @@ F1AP_F1AP_PDU_t *encode_f1ap_du_configuration_update(const f1ap_gnb_du_configura /* mandatory */ /* c4. Served_Cells_To_Delete */ if (msg->num_cells_to_delete > 0) { + DevAssert(msg->num_cells_to_delete <= F1AP_MAX_NB_CELLS); asn1cSequenceAdd(out->protocolIEs.list, F1AP_GNBDUConfigurationUpdateIEs_t, ie4); ie4->id = F1AP_ProtocolIE_ID_id_Served_Cells_To_Delete_List; ie4->criticality = F1AP_Criticality_reject; @@ -1447,6 +1460,7 @@ F1AP_F1AP_PDU_t *encode_f1ap_du_configuration_update(const f1ap_gnb_du_configura } if (msg->num_status > 0) { + DevAssert(msg->num_status <= F1AP_MAX_NB_CELLS); asn1cSequenceAdd(out->protocolIEs.list, F1AP_GNBDUConfigurationUpdateIEs_t, ie4); ie4->id = F1AP_ProtocolIE_ID_id_Cells_Status_List; ie4->criticality = F1AP_Criticality_reject; @@ -1507,7 +1521,8 @@ bool decode_f1ap_du_configuration_update(const F1AP_F1AP_PDU_t *pdu, f1ap_gnb_du /* Served Cells To Add List */ out->num_cells_to_add = ie->value.choice.Served_Cells_To_Add_List.list.count; AssertError(out->num_cells_to_add > 0, return false, "at least 1 cell to add shall to be present"); - for (int i = 0; i < out->num_cells_to_add; i++) { + out->cell_to_add = calloc_or_fail(out->num_cells_to_add, sizeof(*out->cell_to_add)); + for (int i = 0; i < out->num_cells_to_add && i < F1AP_MAX_NB_CELLS; i++) { F1AP_Served_Cells_To_Add_Item_t *served_cells_item = &((F1AP_Served_Cells_To_Add_ItemIEs_t *)ie->value.choice.Served_Cells_To_Add_List.list.array[i]) ->value.choice.Served_Cells_To_Add_Item; @@ -1525,7 +1540,8 @@ bool decode_f1ap_du_configuration_update(const F1AP_F1AP_PDU_t *pdu, f1ap_gnb_du /* Served Cells To Modify List (O) */ out->num_cells_to_modify = ie->value.choice.Served_Cells_To_Modify_List.list.count; AssertError(out->num_cells_to_modify > 0, return false, "at least 1 cell to modify shall to be present"); - for (int i = 0; i < out->num_cells_to_modify; i++) { + out->cell_to_modify = calloc_or_fail(out->num_cells_to_modify, sizeof(*out->cell_to_modify)); + for (int i = 0; i < out->num_cells_to_modify && i < F1AP_MAX_NB_CELLS; i++) { /* Served Cells To Modify List item (count >= 1) */ F1AP_Served_Cells_To_Modify_Item_t *served_cells_item = &((F1AP_Served_Cells_To_Modify_ItemIEs_t *)ie->value.choice.Served_Cells_To_Modify_List.list.array[i]) @@ -1550,7 +1566,8 @@ bool decode_f1ap_du_configuration_update(const F1AP_F1AP_PDU_t *pdu, f1ap_gnb_du /* Served Cells To Delete List */ out->num_cells_to_delete = ie->value.choice.Served_Cells_To_Delete_List.list.count; AssertError(out->num_cells_to_delete > 0, return false, "at least 1 cell to delete shall to be present"); - for (int i = 0; i < out->num_cells_to_delete; i++) { + out->cell_to_delete = calloc_or_fail(out->num_cells_to_delete, sizeof(*out->cell_to_delete)); + for (int i = 0; i < out->num_cells_to_delete && i < F1AP_MAX_NB_CELLS; i++) { F1AP_Served_Cells_To_Delete_Item_t *served_cells_item = &((F1AP_Served_Cells_To_Delete_ItemIEs_t *)ie->value.choice.Served_Cells_To_Delete_List.list.array[i]) ->value.choice.Served_Cells_To_Delete_Item; @@ -1565,7 +1582,8 @@ bool decode_f1ap_du_configuration_update(const F1AP_F1AP_PDU_t *pdu, f1ap_gnb_du case F1AP_ProtocolIE_ID_id_Cells_Status_List: /* Cells Status List (O) */ out->num_status = ie->value.choice.Cells_Status_List.list.count; - for (int i = 0; i < out->num_status; ++i) { + out->status = calloc_or_fail(out->num_status, sizeof(*out->status)); + for (int i = 0; i < out->num_status && i < F1AP_MAX_NB_CELLS; ++i) { const F1AP_Cells_Status_ItemIEs_t *csi_ie = (F1AP_Cells_Status_ItemIEs_t *)ie->value.choice.Cells_Status_List.list.array[i]; AssertError(csi_ie->value.present == F1AP_Cells_Status_ItemIEs__value_PR_Cells_Status_Item, @@ -1604,11 +1622,14 @@ void free_f1ap_du_configuration_update(const f1ap_gnb_du_configuration_update_t for (int i = 0; i < msg->num_cells_to_add; i++) { free_f1ap_cell(&msg->cell_to_add[i].info, msg->cell_to_add[i].sys_info); } + free(msg->cell_to_add); for (int i = 0; i < msg->num_cells_to_modify; i++) { free_f1ap_cell(&msg->cell_to_modify[i].info, msg->cell_to_modify[i].sys_info); } + free(msg->cell_to_modify); + free(msg->cell_to_delete); + free(msg->status); } - /** * @brief F1 gNB-DU Configuration Update check */ @@ -1621,7 +1642,7 @@ bool eq_f1ap_du_configuration_update(const f1ap_gnb_du_configuration_update_t *a _F1_EQ_CHECK_LONG(a->transaction_id, b->transaction_id); /* to add */ _F1_EQ_CHECK_INT(a->num_cells_to_add, b->num_cells_to_add); - for (int i = 0; i < a->num_cells_to_add; i++) { + for (int i = 0; i < a->num_cells_to_add && i < F1AP_MAX_NB_CELLS; i++) { if (!eq_f1ap_cell_info(&a->cell_to_add[i].info, &b->cell_to_add[i].info)) return false; if (!eq_f1ap_sys_info(a->cell_to_add[i].sys_info, b->cell_to_add[i].sys_info)) @@ -1629,14 +1650,14 @@ bool eq_f1ap_du_configuration_update(const f1ap_gnb_du_configuration_update_t *a } /* to delete */ _F1_EQ_CHECK_INT(a->num_cells_to_delete, b->num_cells_to_delete); - for (int i = 0; i < a->num_cells_to_delete; i++) { + for (int i = 0; i < a->num_cells_to_delete && i < F1AP_MAX_NB_CELLS; i++) { _F1_EQ_CHECK_LONG(a->cell_to_delete[i].nr_cellid, b->cell_to_delete[i].nr_cellid); if (!eq_f1ap_plmn(&a->cell_to_delete[i].plmn, &b->cell_to_delete[i].plmn)) return false; } /* to modify */ _F1_EQ_CHECK_INT(a->num_cells_to_modify, b->num_cells_to_modify); - for (int i = 0; i < a->num_cells_to_modify; i++) { + for (int i = 0; i < a->num_cells_to_modify && i < F1AP_MAX_NB_CELLS; i++) { if (!eq_f1ap_plmn(&a->cell_to_modify[i].old_plmn, &b->cell_to_modify[i].old_plmn)) return false; _F1_EQ_CHECK_LONG(a->cell_to_modify[i].old_nr_cellid, b->cell_to_modify[i].old_nr_cellid); @@ -1647,7 +1668,7 @@ bool eq_f1ap_du_configuration_update(const f1ap_gnb_du_configuration_update_t *a } /* cell status */ _F1_EQ_CHECK_INT(a->num_status, b->num_status); - for (int i = 0; i < a->num_status; ++i) { + for (int i = 0; i < a->num_status && i < F1AP_MAX_NB_CELLS; ++i) { const f1ap_cell_status_t *astatus = &a->status[i]; const f1ap_cell_status_t *bstatus = &b->status[i]; if (!eq_f1ap_plmn(&astatus->plmn, &bstatus->plmn)) @@ -1673,19 +1694,22 @@ f1ap_gnb_du_configuration_update_t cp_f1ap_du_configuration_update(const f1ap_gn cp.transaction_id = msg->transaction_id; /* to add */ cp.num_cells_to_add = msg->num_cells_to_add; - for (int i = 0; i < cp.num_cells_to_add; ++i) { + cp.cell_to_add = calloc_or_fail(cp.num_cells_to_add, sizeof(*cp.cell_to_add)); + for (int i = 0; i < cp.num_cells_to_add && i < F1AP_MAX_NB_CELLS; ++i) { cp.cell_to_add[i].info = copy_f1ap_served_cell_info(&msg->cell_to_add[i].info); cp.cell_to_add[i].sys_info = copy_f1ap_gnb_du_system_info(msg->cell_to_add[i].sys_info); } /* to delete */ cp.num_cells_to_delete = msg->num_cells_to_delete; - for (int i = 0; i < cp.num_cells_to_delete; i++) { + cp.cell_to_delete = calloc_or_fail(cp.num_cells_to_delete, sizeof(*cp.cell_to_delete)); + for (int i = 0; i < cp.num_cells_to_delete && i < F1AP_MAX_NB_CELLS; i++) { cp.cell_to_delete[i].nr_cellid = msg->cell_to_delete[i].nr_cellid; cp.cell_to_delete[i].plmn = msg->cell_to_delete[i].plmn; } /* to modify */ cp.num_cells_to_modify = msg->num_cells_to_modify; - for (int i = 0; i < cp.num_cells_to_modify; i++) { + cp.cell_to_modify = calloc_or_fail(cp.num_cells_to_modify, sizeof(*cp.cell_to_modify)); + for (int i = 0; i < cp.num_cells_to_modify && i < F1AP_MAX_NB_CELLS; i++) { cp.cell_to_modify[i].old_plmn = msg->cell_to_modify[i].old_plmn; cp.cell_to_modify[i].old_nr_cellid = msg->cell_to_modify[i].old_nr_cellid; cp.cell_to_modify[i].info = copy_f1ap_served_cell_info(&msg->cell_to_modify[i].info); @@ -1693,7 +1717,8 @@ f1ap_gnb_du_configuration_update_t cp_f1ap_du_configuration_update(const f1ap_gn } /* cell status */ cp.num_status = msg->num_status; - for (int i = 0; i < cp.num_status; ++i) + cp.status = calloc_or_fail(cp.num_status, sizeof(*cp.status)); + for (int i = 0; i < cp.num_status && i < F1AP_MAX_NB_CELLS; ++i) cp.status[i] = msg->status[i]; return cp; } @@ -1725,15 +1750,16 @@ F1AP_F1AP_PDU_t *encode_f1ap_cu_configuration_update(const f1ap_gnb_cu_configura ieC1->value.choice.TransactionID = msg->transaction_id; /* optional c2. Cells_to_be_Activated_List (O) */ - for (int i = 0; i < msg->num_cells_to_activate; i++) { + if (msg->num_cells_to_activate > 0) { asn1cSequenceAdd(cfgUpdate->protocolIEs.list, F1AP_GNBCUConfigurationUpdateIEs_t, ieC3); ieC3->id = F1AP_ProtocolIE_ID_id_Cells_to_be_Activated_List; ieC3->criticality = F1AP_Criticality_reject; ieC3->value.present = F1AP_GNBCUConfigurationUpdateIEs__value_PR_Cells_to_be_Activated_List; - asn1cSequenceAdd(ieC3->value.choice.Cells_to_be_Activated_List.list, - F1AP_Cells_to_be_Activated_List_ItemIEs_t, - cells_to_be_activated_ies); - encode_cells_to_activate(&msg->cells_to_activate[i], cells_to_be_activated_ies); + DevAssert(msg->num_cells_to_activate <= F1AP_MAX_NB_CELLS); + for (int i = 0; i < msg->num_cells_to_activate; i++) { + asn1cSequenceAdd(ieC3->value.choice.Cells_to_be_Activated_List.list, F1AP_Cells_to_be_Activated_List_ItemIEs_t, cells); + encode_cells_to_activate(&msg->cells_to_activate[i], cells); + } } return pdu; } @@ -1768,7 +1794,8 @@ bool decode_f1ap_cu_configuration_update(const F1AP_F1AP_PDU_t *pdu, f1ap_gnb_cu F1AP_Cells_to_be_Activated_List_t *Cells_to_be_Activated_List = &ie->value.choice.Cells_to_be_Activated_List; out->num_cells_to_activate = Cells_to_be_Activated_List->list.count; AssertError(out->num_cells_to_activate > 0, return false, "At least 1 cell to activate must be present"); - for (int i = 0; i < out->num_cells_to_activate; i++) { + out->cells_to_activate = calloc_or_fail(out->num_cells_to_activate, sizeof(*out->cells_to_activate)); + for (int i = 0; i < out->num_cells_to_activate && i < F1AP_MAX_NB_CELLS; i++) { if (!decode_cells_to_activate(&out->cells_to_activate[i], (F1AP_Cells_to_be_Activated_List_ItemIEs_t *)Cells_to_be_Activated_List->list.array[i])) return false; @@ -1786,8 +1813,9 @@ bool decode_f1ap_cu_configuration_update(const F1AP_F1AP_PDU_t *pdu, f1ap_gnb_cu void free_f1ap_cu_configuration_update(const f1ap_gnb_cu_configuration_update_t *msg) { for (int i = 0; i < msg->num_cells_to_activate; i++) - for (int j = 0; j < msg->cells_to_activate[j].num_SI; j++) + for (int j = 0; j < msg->cells_to_activate[i].num_SI; j++) free(msg->cells_to_activate[i].SI_msg[j].SI_container); + free(msg->cells_to_activate); } /** @@ -1798,16 +1826,18 @@ bool eq_f1ap_cu_configuration_update(const f1ap_gnb_cu_configuration_update_t *a _F1_EQ_CHECK_LONG(a->transaction_id, b->transaction_id); /* to activate */ _F1_EQ_CHECK_INT(a->num_cells_to_activate, b->num_cells_to_activate); - for (int i = 0; i < a->num_cells_to_activate; i++) { + for (int i = 0; i < a->num_cells_to_activate && i < F1AP_MAX_NB_CELLS; i++) { _F1_EQ_CHECK_LONG(a->cells_to_activate[i].nr_cellid, b->cells_to_activate[i].nr_cellid); _F1_EQ_CHECK_INT(a->cells_to_activate[i].nrpci, b->cells_to_activate[i].nrpci); if (!eq_f1ap_plmn(&a->cells_to_activate[i].plmn, &b->cells_to_activate[i].plmn)) return false; _F1_EQ_CHECK_INT(a->cells_to_activate[i].num_SI, b->cells_to_activate[i].num_SI); for (int s = 0; s < a->cells_to_activate[i].num_SI; s++) { - _F1_EQ_CHECK_INT(*a->cells_to_activate[i].SI_msg[s].SI_container, *a->cells_to_activate[i].SI_msg[s].SI_container); - _F1_EQ_CHECK_INT(a->cells_to_activate[i].SI_msg[s].SI_container_length, a->cells_to_activate[i].SI_msg[s].SI_container_length); - _F1_EQ_CHECK_INT(a->cells_to_activate[i].SI_msg[s].SI_type, a->cells_to_activate[i].SI_msg[s].SI_type); + f1ap_sib_msg_t *a_sib_msg = &a->cells_to_activate[i].SI_msg[s]; + f1ap_sib_msg_t *b_sib_msg = &b->cells_to_activate[i].SI_msg[s]; + _F1_EQ_CHECK_INT(*a_sib_msg->SI_container, *b_sib_msg->SI_container); + _F1_EQ_CHECK_INT(a_sib_msg->SI_container_length, b_sib_msg->SI_container_length); + _F1_EQ_CHECK_INT(a_sib_msg->SI_type, b_sib_msg->SI_type); } } return true; @@ -1822,7 +1852,8 @@ f1ap_gnb_cu_configuration_update_t cp_f1ap_cu_configuration_update(const f1ap_gn /* transaction_id */ cp.transaction_id = msg->transaction_id; cp.num_cells_to_activate = msg->num_cells_to_activate; - for (int i = 0; i < cp.num_cells_to_activate; i++) { + cp.cells_to_activate = calloc_or_fail(cp.num_cells_to_activate, sizeof(*cp.cells_to_activate)); + for (int i = 0; i < cp.num_cells_to_activate && i < F1AP_MAX_NB_CELLS; i++) { cp.cells_to_activate[i] = msg->cells_to_activate[i]; for (int s = 0; s < cp.cells_to_activate[i].num_SI; s++) { cp.cells_to_activate[i].SI_msg[s] = msg->cells_to_activate[i].SI_msg[s]; @@ -1862,6 +1893,7 @@ F1AP_F1AP_PDU_t *encode_f1ap_cu_configuration_update_acknowledge(const f1ap_gnb_ ie1->value.choice.TransactionID = msg->transaction_id; // Cells Failed to be Activated List (0..1) if (msg->num_cells_failed_to_be_activated > 0) { + DevAssert(msg->num_cells_failed_to_be_activated <= F1AP_MAX_NB_CELLS); asn1cSequenceAdd(out->protocolIEs.list, F1AP_GNBCUConfigurationUpdateAcknowledgeIEs_t, ie2); ie2->id = F1AP_ProtocolIE_ID_id_Cells_Failed_to_be_Activated_List; ie2->criticality = F1AP_Criticality_reject; @@ -1878,9 +1910,8 @@ F1AP_F1AP_PDU_t *encode_f1ap_cu_configuration_update_acknowledge(const f1ap_gnb_ p1->cause.choice.radioNetwork = msg->cells_failed_to_be_activated[i].cause; // NR CGI (M) const plmn_id_t *plmn = &msg->cells_failed_to_be_activated[i].plmn; - MCC_MNC_TO_PLMNID(plmn->mcc, plmn->mnc, plmn->mnc_digit_length, &(p1->nRCGI.pLMN_Identity)); - printf("plmn->mcc %d %d %d %ld \n", - p1->nRCGI.pLMN_Identity.buf[0], p1->nRCGI.pLMN_Identity.buf[1], p1->nRCGI.pLMN_Identity.buf[2], p1->nRCGI.pLMN_Identity.size); + F1AP_PLMN_Identity_t *p2 = &p1->nRCGI.pLMN_Identity; + MCC_MNC_TO_PLMNID(plmn->mcc, plmn->mnc, plmn->mnc_digit_length, p2); NR_CELL_ID_TO_BIT_STRING(msg->cells_failed_to_be_activated[i].nr_cellid, &(p1->nRCGI.nRCellIdentity)); } } @@ -1919,7 +1950,9 @@ bool decode_f1ap_cu_configuration_update_acknowledge(const F1AP_F1AP_PDU_t *pdu, F1AP_GNBCUConfigurationUpdateAcknowledgeIEs__value_PR_Cells_Failed_to_be_Activated_List); F1AP_Cells_Failed_to_be_Activated_List_t *cell_fail_list = &ie->value.choice.Cells_Failed_to_be_Activated_List; out->num_cells_failed_to_be_activated = cell_fail_list->list.count; - for (int j = 0; j < out->num_cells_failed_to_be_activated; j++) { + out->cells_failed_to_be_activated = + calloc_or_fail(out->num_cells_failed_to_be_activated, sizeof(*out->cells_failed_to_be_activated)); + for (int j = 0; j < out->num_cells_failed_to_be_activated && j < F1AP_MAX_NB_CELLS; j++) { const F1AP_Cells_Failed_to_be_Activated_List_ItemIEs_t *itemIE = (F1AP_Cells_Failed_to_be_Activated_List_ItemIEs_t *)cell_fail_list->list.array[j]; const F1AP_Cells_Failed_to_be_Activated_List_Item_t *item = &itemIE->value.choice.Cells_Failed_to_be_Activated_List_Item; @@ -1966,7 +1999,7 @@ bool eq_f1ap_cu_configuration_update_acknowledge(const f1ap_gnb_cu_configuration _F1_EQ_CHECK_LONG(a->transaction_id, b->transaction_id); // number of cells failed to be activated _F1_EQ_CHECK_INT(a->num_cells_failed_to_be_activated, b->num_cells_failed_to_be_activated); - for (int i = 0; i < a->num_cells_failed_to_be_activated; i++) { + for (int i = 0; i < a->num_cells_failed_to_be_activated && i < F1AP_MAX_NB_CELLS; i++) { if (!eq_f1ap_plmn(&a->cells_failed_to_be_activated[i].plmn, &b->cells_failed_to_be_activated[i].plmn)) return false; _F1_EQ_CHECK_LONG(a->cells_failed_to_be_activated[i].nr_cellid, b->cells_failed_to_be_activated[i].nr_cellid); @@ -1974,21 +2007,21 @@ bool eq_f1ap_cu_configuration_update_acknowledge(const f1ap_gnb_cu_configuration } // TNL Associations to setup _F1_EQ_CHECK_INT(a->noofTNLAssociations_to_setup, b->noofTNLAssociations_to_setup); - for (int i = 0; i < a->noofTNLAssociations_to_setup; i++) { + for (int i = 0; i < a->noofTNLAssociations_to_setup && i < F1AP_MAX_NO_OF_TNL_ASSOCIATIONS; i++) { // Explicit comparison of TNL Association fields _F1_EQ_CHECK_INT(a->tnlAssociations_to_setup[i].tl_address, b->tnlAssociations_to_setup[i].tl_address); _F1_EQ_CHECK_INT(a->tnlAssociations_to_setup[i].port, b->tnlAssociations_to_setup[i].port); } // TNL Associations failed to setup _F1_EQ_CHECK_INT(a->noofTNLAssociations_failed, b->noofTNLAssociations_failed); - for (int i = 0; i < a->noofTNLAssociations_failed; i++) { + for (int i = 0; i < a->noofTNLAssociations_failed && i < F1AP_MAX_NO_OF_TNL_ASSOCIATIONS; i++) { // Explicit comparison of TNL Association fields _F1_EQ_CHECK_INT(a->tnlAssociations_failed[i].tl_address, b->tnlAssociations_failed[i].tl_address); _F1_EQ_CHECK_INT(a->tnlAssociations_failed[i].port, b->tnlAssociations_failed[i].port); } // Dedicated SI Delivery Needed UE List _F1_EQ_CHECK_INT(a->noofDedicatedSIDeliveryNeededUEs, b->noofDedicatedSIDeliveryNeededUEs); - for (int i = 0; i < a->noofDedicatedSIDeliveryNeededUEs; i++) { + for (int i = 0; i < a->noofDedicatedSIDeliveryNeededUEs && i < F1AP_MAX_NO_UE_ID; i++) { _F1_EQ_CHECK_INT(a->dedicatedSIDeliveryNeededUEs[i].gNB_CU_ue_id, b->dedicatedSIDeliveryNeededUEs[i].gNB_CU_ue_id); if (!eq_f1ap_plmn(&a->dedicatedSIDeliveryNeededUEs[i].ue_plmn, &b->dedicatedSIDeliveryNeededUEs[i].ue_plmn)) return false; @@ -2008,23 +2041,34 @@ f1ap_gnb_cu_configuration_update_acknowledge_t cp_f1ap_cu_configuration_update_a cp.transaction_id = msg->transaction_id; // number of cells failed to be activated cp.num_cells_failed_to_be_activated = msg->num_cells_failed_to_be_activated; - for (int i = 0; i < cp.num_cells_failed_to_be_activated; i++) + cp.cells_failed_to_be_activated = calloc_or_fail(cp.num_cells_failed_to_be_activated, sizeof(*cp.cells_failed_to_be_activated)); + for (int i = 0; i < cp.num_cells_failed_to_be_activated && i < F1AP_MAX_NB_CELLS; i++) cp.cells_failed_to_be_activated[i] = msg->cells_failed_to_be_activated[i]; // TNL Associations to setup cp.noofTNLAssociations_to_setup = msg->noofTNLAssociations_to_setup; - for (int i = 0; i < cp.noofTNLAssociations_to_setup; i++) + for (int i = 0; i < cp.noofTNLAssociations_to_setup && i < F1AP_MAX_NO_OF_TNL_ASSOCIATIONS; i++) cp.tnlAssociations_to_setup[i] = msg->tnlAssociations_to_setup[i]; // TNL Associations failed to setup cp.noofTNLAssociations_failed = msg->noofTNLAssociations_failed; - for (int i = 0; i < cp.noofTNLAssociations_failed; i++) + for (int i = 0; i < cp.noofTNLAssociations_failed && i < F1AP_MAX_NO_OF_TNL_ASSOCIATIONS; i++) cp.tnlAssociations_failed[i] = msg->tnlAssociations_failed[i]; // Dedicated SI Delivery Needed UE List cp.noofDedicatedSIDeliveryNeededUEs = msg->noofDedicatedSIDeliveryNeededUEs; - for (int i = 0; i < cp.noofDedicatedSIDeliveryNeededUEs; i++) + cp.dedicatedSIDeliveryNeededUEs = calloc_or_fail(cp.noofDedicatedSIDeliveryNeededUEs, sizeof(*cp.dedicatedSIDeliveryNeededUEs)); + for (int i = 0; i < cp.noofDedicatedSIDeliveryNeededUEs && i < F1AP_MAX_NO_UE_ID; i++) cp.dedicatedSIDeliveryNeededUEs[i] = msg->dedicatedSIDeliveryNeededUEs[i]; return cp; } +/** + * @brief F1 gNB-CU Configuration Update Acknowledge memory management + */ +void free_f1ap_cu_configuration_update_acknowledge(const f1ap_gnb_cu_configuration_update_acknowledge_t *msg) +{ + free(msg->cells_failed_to_be_activated); + free(msg->dedicatedSIDeliveryNeededUEs); +} + /* ================================== * F1AP gNB-DU Configuration Ack * ================================== */ @@ -2094,10 +2138,11 @@ bool decode_f1ap_du_configuration_update_acknowledge(const F1AP_F1AP_PDU_t *pdu, _F1_EQ_CHECK_INT(ie->value.present, F1AP_GNBDUConfigurationUpdateAcknowledgeIEs__value_PR_Cells_to_be_Activated_List); F1AP_Cells_to_be_Activated_List_t *list = &ie->value.choice.Cells_to_be_Activated_List; out->num_cells_to_activate = list->list.count; - for (int j = 0; j < out->num_cells_to_activate; j++) { + out->cells_to_activate = calloc_or_fail(out->num_cells_to_activate, sizeof(*out->cells_to_activate)); + for (int j = 0; j < out->num_cells_to_activate && j < F1AP_MAX_NB_CELLS; j++) { const F1AP_Cells_to_be_Activated_List_ItemIEs_t *itemIE = (F1AP_Cells_to_be_Activated_List_ItemIEs_t *)list->list.array[j]; - decode_cells_to_activate(&out->cells_to_activate[0], itemIE); + decode_cells_to_activate(&out->cells_to_activate[j], itemIE); } break; } @@ -2120,7 +2165,7 @@ bool eq_f1ap_du_configuration_update_acknowledge(const f1ap_gnb_du_configuration // number of cells to activate _F1_EQ_CHECK_INT(a->num_cells_to_activate, b->num_cells_to_activate); // loop over cells to activate - for (int i = 0; i < a->num_cells_to_activate; i++) { + for (int i = 0; i < a->num_cells_to_activate && i < F1AP_MAX_NB_CELLS; i++) { _F1_EQ_CHECK_LONG(a->cells_to_activate[i].nr_cellid, b->cells_to_activate[i].nr_cellid); _F1_EQ_CHECK_INT(a->cells_to_activate[i].nrpci, b->cells_to_activate[i].nrpci); if (!eq_f1ap_plmn(&a->cells_to_activate[i].plmn, &b->cells_to_activate[i].plmn)) @@ -2148,7 +2193,8 @@ f1ap_gnb_du_configuration_update_acknowledge_t cp_f1ap_du_configuration_update_a // number of cells to activate cp.num_cells_to_activate = msg->num_cells_to_activate; // Loop through cells to activate - for (int i = 0; i < cp.num_cells_to_activate; i++) { + cp.cells_to_activate = calloc_or_fail(cp.num_cells_to_activate, sizeof(*cp.cells_to_activate)); + for (int i = 0; i < cp.num_cells_to_activate && i < F1AP_MAX_NB_CELLS; i++) { cp.cells_to_activate[i] = msg->cells_to_activate[i]; for (int s = 0; s < cp.cells_to_activate[i].num_SI; s++) { f1ap_sib_msg_t *cp_sib = &cp.cells_to_activate[i].SI_msg[s]; @@ -2175,4 +2221,5 @@ void free_f1ap_du_configuration_update_acknowledge(const f1ap_gnb_du_configurati } } } + free(msg->cells_to_activate); } diff --git a/openair2/F1AP/lib/f1ap_interface_management.h b/openair2/F1AP/lib/f1ap_interface_management.h index 1b484f3476..942a9932ad 100644 --- a/openair2/F1AP/lib/f1ap_interface_management.h +++ b/openair2/F1AP/lib/f1ap_interface_management.h @@ -46,8 +46,6 @@ bool decode_f1ap_setup_request(const struct F1AP_F1AP_PDU *pdu, f1ap_setup_req_t f1ap_setup_req_t cp_f1ap_setup_request(const f1ap_setup_req_t *msg); bool eq_f1ap_setup_request(const f1ap_setup_req_t *a, const f1ap_setup_req_t *b); void free_f1ap_setup_request(const f1ap_setup_req_t *msg); -f1ap_served_cell_info_t copy_f1ap_served_cell_info(const f1ap_served_cell_info_t *src); -void free_f1ap_cell(const f1ap_served_cell_info_t *info, const f1ap_gnb_du_system_info_t *sys_info); /* F1 Setup Response */ struct F1AP_F1AP_PDU *encode_f1ap_setup_response(const f1ap_setup_resp_t *msg); @@ -84,6 +82,7 @@ bool eq_f1ap_cu_configuration_update_acknowledge(const f1ap_gnb_cu_configuration const f1ap_gnb_cu_configuration_update_acknowledge_t *b); f1ap_gnb_cu_configuration_update_acknowledge_t cp_f1ap_cu_configuration_update_acknowledge( const f1ap_gnb_cu_configuration_update_acknowledge_t *msg); +void free_f1ap_cu_configuration_update_acknowledge(const f1ap_gnb_cu_configuration_update_acknowledge_t *msg); /* F1 gNB-DU Configuration Update Acknowledge */ struct F1AP_F1AP_PDU *encode_f1ap_du_configuration_update_acknowledge(const f1ap_gnb_du_configuration_update_acknowledge_t *msg); diff --git a/openair2/F1AP/tests/f1ap_lib_test.c b/openair2/F1AP/tests/f1ap_lib_test.c index cba787f4dc..e60d768b94 100644 --- a/openair2/F1AP/tests/f1ap_lib_test.c +++ b/openair2/F1AP/tests/f1ap_lib_test.c @@ -244,8 +244,9 @@ static void test_f1ap_setup_request(void) .rrc_ver[0] = 12, .rrc_ver[1] = 34, .rrc_ver[2] = 56, - .cell[0].info = info, }; + orig.cell = calloc_or_fail(orig.num_cells_available, sizeof(*orig.cell)); + orig.cell[0].info = info; orig.cell[0].sys_info = calloc_or_fail(1, sizeof(*orig.cell[0].sys_info)); *orig.cell[0].sys_info = sys_info; // encode @@ -322,12 +323,14 @@ static void test_f1ap_setup_response(void) .rrc_ver[1] = 34, .rrc_ver[2] = 56, .num_cells_to_activate = 1, - .cells_to_activate[0].nr_cellid = 123456, - .cells_to_activate[0].nrpci = 1, - .cells_to_activate[0].plmn.mcc = 12, - .cells_to_activate[0].plmn.mnc = 123, - .cells_to_activate[0].plmn.mnc_digit_length = 3, }; + /* Allocate memory for cells_to_activate array */ + orig.cells_to_activate = calloc_or_fail(orig.num_cells_to_activate, sizeof(*orig.cells_to_activate)); + orig.cells_to_activate[0].nr_cellid = 123456; + orig.cells_to_activate[0].nrpci = 1; + orig.cells_to_activate[0].plmn.mcc = 12; + orig.cells_to_activate[0].plmn.mnc = 123; + orig.cells_to_activate[0].plmn.mnc_digit_length = 3; /* Cells to activate */ if (orig.num_cells_to_activate) { /* SI_container */ @@ -556,30 +559,37 @@ static void test_f1ap_du_configuration_update(void) f1ap_gnb_du_configuration_update_t orig = { .transaction_id = 2, .num_cells_to_add = 1, - .cell_to_add[0].info = info2, .num_cells_to_modify = 1, - .cell_to_modify[0].info = info, - .cell_to_modify[0].old_nr_cellid = 1235UL, - .cell_to_modify[0].old_plmn.mcc = 208, - .cell_to_modify[0].old_plmn.mnc = 88, - .cell_to_modify[0].old_plmn.mnc_digit_length = 2, .num_cells_to_delete = 1, - .cell_to_delete[0].nr_cellid = 1234UL, - .cell_to_delete[0].plmn.mcc = 1, - .cell_to_delete[0].plmn.mnc = 1, - .cell_to_delete[0].plmn.mnc_digit_length = 3, .num_status = 2, - .status[0].nr_cellid = 542UL, - .status[0].plmn.mcc = 1, - .status[0].plmn.mnc = 2, - .status[0].plmn.mnc_digit_length = 3, - .status[0].service_state = F1AP_STATE_IN_SERVICE, - .status[1].nr_cellid = 33UL, - .status[1].plmn.mcc = 5, - .status[1].plmn.mnc = 13, - .status[1].plmn.mnc_digit_length = 2, - .status[1].service_state = F1AP_STATE_OUT_OF_SERVICE, }; + /* Allocate memory for arrays */ + orig.cell_to_add = calloc_or_fail(orig.num_cells_to_add, sizeof(*orig.cell_to_add)); + orig.cell_to_modify = calloc_or_fail(orig.num_cells_to_modify, sizeof(*orig.cell_to_modify)); + orig.cell_to_delete = calloc_or_fail(orig.num_cells_to_delete, sizeof(*orig.cell_to_delete)); + orig.status = calloc_or_fail(orig.num_status, sizeof(*orig.status)); + + /* Fill in the data */ + orig.cell_to_add[0].info = info2; + orig.cell_to_modify[0].info = info; + orig.cell_to_modify[0].old_nr_cellid = 1235UL; + orig.cell_to_modify[0].old_plmn.mcc = 208; + orig.cell_to_modify[0].old_plmn.mnc = 88; + orig.cell_to_modify[0].old_plmn.mnc_digit_length = 2; + orig.cell_to_delete[0].nr_cellid = 1234UL; + orig.cell_to_delete[0].plmn.mcc = 1; + orig.cell_to_delete[0].plmn.mnc = 1; + orig.cell_to_delete[0].plmn.mnc_digit_length = 3; + orig.status[0].nr_cellid = 542UL; + orig.status[0].plmn.mcc = 1; + orig.status[0].plmn.mnc = 2; + orig.status[0].plmn.mnc_digit_length = 3; + orig.status[0].service_state = F1AP_STATE_IN_SERVICE; + orig.status[1].nr_cellid = 33UL; + orig.status[1].plmn.mcc = 5; + orig.status[1].plmn.mnc = 13; + orig.status[1].plmn.mnc_digit_length = 2; + orig.status[1].service_state = F1AP_STATE_OUT_OF_SERVICE; orig.cell_to_modify[0].sys_info = calloc_or_fail(1, sizeof(*orig.cell_to_modify[0].sys_info)); *orig.cell_to_modify[0].sys_info = sys_info; orig.gNB_DU_ID = malloc_or_fail(sizeof(*orig.gNB_DU_ID)); @@ -614,18 +624,18 @@ static void test_f1ap_du_configuration_update_acknowledge(void) f1ap_gnb_du_configuration_update_acknowledge_t orig = { .transaction_id = 5, .num_cells_to_activate = 1, - .cells_to_activate = {{ - .nr_cellid = 987654321, - .nrpci = 50, - .plmn = {.mcc = 001, .mnc = 01, .mnc_digit_length = 2}, - .num_SI = 1, - .SI_msg = {{ - .SI_type = 7, - .SI_container_length = 10, - .SI_container = malloc(sizeof(uint8_t) * 10), - }}, - }}, }; + // Allocate memory for cells_to_activate array + orig.cells_to_activate = calloc_or_fail(orig.num_cells_to_activate, sizeof(*orig.cells_to_activate)); + orig.cells_to_activate[0].nr_cellid = 987654321; + orig.cells_to_activate[0].nrpci = 50; + orig.cells_to_activate[0].plmn.mcc = 001; + orig.cells_to_activate[0].plmn.mnc = 01; + orig.cells_to_activate[0].plmn.mnc_digit_length = 2; + orig.cells_to_activate[0].num_SI = 1; + orig.cells_to_activate[0].SI_msg[0].SI_type = 7; + orig.cells_to_activate[0].SI_msg[0].SI_container_length = 10; + orig.cells_to_activate[0].SI_msg[0].SI_container = malloc(sizeof(uint8_t) * 10); for (int i = 0; i < orig.cells_to_activate[0].SI_msg[0].SI_container_length; i++) { orig.cells_to_activate[0].SI_msg[0].SI_container[i] = i; } @@ -657,17 +667,21 @@ static void test_f1ap_du_configuration_update_acknowledge(void) static void test_f1ap_cu_configuration_update(void) { /* create message */ - f1ap_gnb_cu_configuration_update_t orig = {.transaction_id = 2, - .num_cells_to_activate = 1, - .cells_to_activate = {{.nr_cellid = 123456789, - .nrpci = 100, - .plmn = {.mcc = 001, .mnc = 01, .mnc_digit_length = 2}, - .num_SI = 1, - .SI_msg = {{ - .SI_type = 7, - .SI_container_length = 10, - .SI_container = malloc(sizeof(uint8_t) * 10), - }}}}}; + f1ap_gnb_cu_configuration_update_t orig = { + .transaction_id = 2, + .num_cells_to_activate = 1, + }; + // Allocate memory for cells_to_activate array + orig.cells_to_activate = calloc_or_fail(orig.num_cells_to_activate, sizeof(*orig.cells_to_activate)); + orig.cells_to_activate[0].nr_cellid = 123456789; + orig.cells_to_activate[0].nrpci = 100; + orig.cells_to_activate[0].plmn.mcc = 001; + orig.cells_to_activate[0].plmn.mnc = 01; + orig.cells_to_activate[0].plmn.mnc_digit_length = 2; + orig.cells_to_activate[0].num_SI = 1; + orig.cells_to_activate[0].SI_msg[0].SI_type = 7; + orig.cells_to_activate[0].SI_msg[0].SI_container_length = 10; + orig.cells_to_activate[0].SI_msg[0].SI_container = malloc(sizeof(uint8_t) * 10); F1AP_F1AP_PDU_t *f1enc = encode_f1ap_cu_configuration_update(&orig); F1AP_F1AP_PDU_t *f1dec = f1ap_encode_decode(f1enc); f1ap_msg_free(f1enc); @@ -697,10 +711,14 @@ static void test_f1ap_cu_configuration_update_acknowledge(void) f1ap_gnb_cu_configuration_update_acknowledge_t orig = { .transaction_id = 2, .num_cells_failed_to_be_activated = 1, - .cells_failed_to_be_activated = {{.nr_cellid = 123456789, - .plmn = {.mcc = 001, .mnc = 01, .mnc_digit_length = 2}, - .cause = F1AP_CAUSE_RADIO_NETWORK}} - }; + }; + // Allocate memory for cells_failed_to_be_activated array + orig.cells_failed_to_be_activated = calloc_or_fail(orig.num_cells_failed_to_be_activated, sizeof(*orig.cells_failed_to_be_activated)); + orig.cells_failed_to_be_activated[0].nr_cellid = 123456789; + orig.cells_failed_to_be_activated[0].plmn.mcc = 001; + orig.cells_failed_to_be_activated[0].plmn.mnc = 01; + orig.cells_failed_to_be_activated[0].plmn.mnc_digit_length = 2; + orig.cells_failed_to_be_activated[0].cause = F1AP_CAUSE_RADIO_NETWORK; // F1AP Enc/dec F1AP_F1AP_PDU_t *f1enc = encode_f1ap_cu_configuration_update_acknowledge(&orig); F1AP_F1AP_PDU_t *f1dec = f1ap_encode_decode(f1enc); @@ -713,13 +731,14 @@ static void test_f1ap_cu_configuration_update_acknowledge(void) // Equality check ret = eq_f1ap_cu_configuration_update_acknowledge(&orig, &decoded); AssertFatal(ret, "eq_f1ap_cu_configuration_update_acknowledge(): decoded message doesn't match\n"); - // No free needed + free_f1ap_cu_configuration_update_acknowledge(&decoded); // Deep copy f1ap_gnb_cu_configuration_update_acknowledge_t cp = cp_f1ap_cu_configuration_update_acknowledge(&orig); // Equality check ret = eq_f1ap_cu_configuration_update_acknowledge(&orig, &cp); AssertFatal(ret, "eq_f1ap_cu_configuration_update_acknowledge(): copied message doesn't match\n"); - // No free needed + free_f1ap_cu_configuration_update_acknowledge(&cp); + free_f1ap_cu_configuration_update_acknowledge(&orig); } static byte_array_t get_test_ba(const char *s) diff --git a/openair2/GNB_APP/MACRLC_nr_paramdef.h b/openair2/GNB_APP/MACRLC_nr_paramdef.h index 6bd02cf74a..cb46836cb4 100644 --- a/openair2/GNB_APP/MACRLC_nr_paramdef.h +++ b/openair2/GNB_APP/MACRLC_nr_paramdef.h @@ -143,7 +143,7 @@ {CONFIG_STRING_MACRLC_LOCAL_N_ADDRESS_F1U, NULL, 0, .strptr=NULL, .defstrval=NULL, TYPE_STRING, 0}, \ {CONFIG_STRING_MACRLC_TRANSPORT_S_SHM_PREFIX, NULL, 0, .strptr=NULL, .defstrval="nvipc", TYPE_STRING, 0}, \ {CONFIG_STRING_MACRLC_TRANSPORT_S_POLL_CORE, NULL, 0, .i8ptr=NULL, .defintval=-1, TYPE_INT8, 0}, \ - {CONFIG_STRING_MACRLC_ANALOG_BEAMFORMING, HLP_MACRLC_AB, 0, .u8ptr=NULL, .defintval=0, TYPE_UINT8, 0}, \ + {CONFIG_STRING_MACRLC_ANALOG_BEAMFORMING, HLP_MACRLC_AB, 0, .strptr=NULL, .defstrval="none", TYPE_STRING, 0}, \ {CONFIG_STRING_MACRLC_BEAM_DURATION, HLP_MACRLC_BEAM_DURATION, 0, .u8ptr=NULL, .defintval=1, TYPE_UINT8, 0}, \ {CONFIG_STRING_MACRLC_BEAMS_PERIOD, HLP_MACRLC_BEAMS_PERIOD, 0, .u8ptr=NULL, .defintval=1, TYPE_UINT8, 0}, \ {CONFIG_STRING_MACRLC_BEAM_WEIGHTS_LIST, NULL, 0, .iptr=NULL, .defintarrayval=0, TYPE_INTARRAY,0}, \ @@ -236,7 +236,10 @@ { .s5 = { NULL } }, \ { .s5 = { NULL } }, \ { .s2 = { NULL } }, \ - { .s5 = { NULL } }, \ + { .s3a = { config_checkstr_assign_integer, \ + {"none", "preconfigured", "lophy"}, \ + {NO_BEAM_MODE, PRECONFIGURED_BEAM_IDX, LOPHY_BEAM_IDX}, \ + 3 } }, \ { .s5 = { NULL } }, \ { .s5 = { NULL } }, \ { .s5 = { NULL } }, \ diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index 0b3845f5b9..f9e8261c0f 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -75,6 +75,7 @@ #include "s1ap_messages_types.h" #include "sctp_default_values.h" #include "seq_arr.h" +#include "common/utils/alg/find.h" #include "uper_encoder.h" #include "utils.h" #include "x2ap_messages_types.h" @@ -717,7 +718,6 @@ void RCconfig_verify(configmodule_interface_t *cfg, ngran_node_t node_type) } else if (NODE_IS_DU(node_type)) { // verify that there is no bearer config verify_gnb_param_notset(gnbp, GNB_ENABLE_SDAP_IDX, GNB_CONFIG_STRING_ENABLE_SDAP); - verify_gnb_param_notset(gnbp, GNB_DRBS, GNB_CONFIG_STRING_DRBS); verify_section_notset(cfg, GNB_CONFIG_STRING_GNB_LIST ".[0]", GNB_CONFIG_STRING_AMF_IP_ADDRESS); verify_section_notset(cfg, NULL, CONFIG_STRING_SECURITY); @@ -1161,6 +1161,7 @@ static f1ap_setup_req_t *RC_read_F1Setup(uint64_t id, req->gNB_DU_id = id; req->gNB_DU_name = strdup(name); req->num_cells_available = 1; + req->cell = calloc_or_fail(req->num_cells_available, sizeof(*req->cell)); req->cell[0].info = *info; LOG_I(GNB_APP, "F1AP: gNB idx %d gNB_DU_id %ld, gNB_DU_name %s, TAC %d MCC/MNC/length %d/%d/%d cellID %ld\n", @@ -1623,9 +1624,9 @@ void RCconfig_nr_macrlc(configmodule_interface_t *cfg) LOG_I(NR_PHY, "Copying %d blacklisted PRB to L1 context\n", num_ulprbbl); memcpy(RC.nrmac[j]->ulprbbl, prbbl, MAX_BWP_SIZE * sizeof(prbbl[0])); } - int ab = *MacRLC_ParamList.paramarray[j][MACRLC_ANALOG_BEAMFORMING_IDX].u8ptr; - if (ab > 0) { - if (ab == 1) + RC.nrmac[j]->beam_info.beam_mode = config_get_processedint(cfg, &MacRLC_ParamList.paramarray[j][MACRLC_ANALOG_BEAMFORMING_IDX]); + if (RC.nrmac[j]->beam_info.beam_mode != NO_BEAM_MODE) { + if (RC.nrmac[j]->beam_info.beam_mode == PRECONFIGURED_BEAM_IDX) AssertFatal(NFAPI_MODE == NFAPI_MONOLITHIC, "Analog beamforming only supported for monolithic scenario\n"); NR_beam_info_t *beam_info = &RC.nrmac[j]->beam_info; int beams_per_period = *MacRLC_ParamList.paramarray[j][MACRLC_ANALOG_BEAMS_PERIOD_IDX].u8ptr; @@ -1633,10 +1634,6 @@ void RCconfig_nr_macrlc(configmodule_interface_t *cfg) beam_info->beam_duration = *MacRLC_ParamList.paramarray[j][MACRLC_ANALOG_BEAM_DURATION_IDX].u8ptr; beam_info->beams_per_period = beams_per_period; beam_info->beam_allocation_size = -1; // to be initialized once we have information on frame configuration - // TODO: Indicate this to MAC via FAPI TLV 0x0164. - beam_info->beam_mode = ab == 1 ? PRECONFIGURED_BEAM_IDX : LOPHY_BEAM_IDX; - } else { - RC.nrmac[j]->beam_info.beam_mode = NO_BEAM_MODE; } // TODO config_isparamset doesn't seem to work for array types, checking numelt instead int n = MacRLC_ParamList.paramarray[j][MACRLC_BEAMWEIGHTS_IDX].numelt; @@ -1646,7 +1643,7 @@ void RCconfig_nr_macrlc(configmodule_interface_t *cfg) AssertFatal(*(L1_ParamList.paramarray[j][L1_ANALOG_DAS].uptr) == 0, "No need to set beam weights in case of DAS\n"); } int num_beam = n; - if (!ab) { + if (RC.nrmac[j]->beam_info.beam_mode == PRECONFIGURED_BEAM_IDX) { AssertFatal(n % num_tx == 0, "Error! Number of beam input needs to be multiple of TX antennas\n"); num_beam = n / num_tx; } @@ -1682,6 +1679,7 @@ void RCconfig_nr_macrlc(configmodule_interface_t *cfg) seq_arr_t *du_SIBs = RC.nrmac[0]->common_channels[0].du_SIBs; f1ap_setup_req_t *req = RC_read_F1Setup(gnb_du_id, name, &info, scc, mib, sib1, du_SIBs); AssertFatal(req != NULL, "could not read F1 Setup information\n"); + LOG_I(GNB_APP, "Configured DU: cell ID %lu, PCI %d\n", info.nr_cellid, info.nr_pci); RC.nrmac[0]->f1_config.setup_req = req; RC.nrmac[0]->f1_config.gnb_id = gnb_id; @@ -1850,6 +1848,22 @@ static seq_arr_t *fill_cu_sibs(paramdef_t *GNBparamarray) return SIBs; } +static bool eq_neighbour_pci(const void *vval, const void *vit) +{ + const int *pci = (const int *)vval; + const nr_neighbour_cell_t *neighbour = (const nr_neighbour_cell_t *)vit; + return neighbour->physicalCellId == *pci; +} + +/** @brief Find neighbour by PCI within a cell's neighbour list (first match) */ +static nr_neighbour_cell_t *get_neighbour_by_pci(seq_arr_t *neighbour_cells, int pci) +{ + elm_arr_t elm = find_if(neighbour_cells, &pci, eq_neighbour_pci); + if (elm.found) + return (nr_neighbour_cell_t *)elm.it; + return NULL; +} + static void fill_neighbour_cell_configuration(uint8_t gnb_idx, gNB_RRC_INST *rrc) { char gnbpath[MAX_OPTNAME_SIZE + 8]; @@ -1907,6 +1921,14 @@ static void fill_neighbour_cell_configuration(uint8_t gnb_idx, gNB_RRC_INST *rrc n.plmn.mcc = *NeighbourPlmn[GNB_MOBILE_COUNTRY_CODE_IDX].uptr; n.plmn.mnc = *NeighbourPlmn[GNB_MOBILE_NETWORK_CODE_IDX].uptr; n.plmn.mnc_digit_length = *NeighbourPlmn[GNB_MNC_DIGIT_LENGTH].uptr; + if (get_neighbour_by_pci(cell.neighbour_cells, n.physicalCellId) != NULL) { + LOG_E(GNB_APP, + "Cell %ld: duplicate PCI %d in neighbour list (nrcell_id %ld)\n", + cell.nr_cell_id, + n.physicalCellId, + n.nrcell_id); + AssertFatal(false, "PCI must be unique within a cell's neighbour list\n"); + } seq_arr_push_back(cell.neighbour_cells, &n, sizeof(n)); LOG_I(GNB_APP, " [%d] neighbor ID %d cellId %ld PCI %d SCS %d SSB ARFCN %u TAC %u PLMN %03u.%0*u\n", @@ -2051,8 +2073,6 @@ gNB_RRC_INST *RCconfig_NRRRC() rrc->eth_params_s.transp_preference = ETH_UDP_MODE; } - rrc->nr_cellid = (uint64_t)*(GNBParamList.paramarray[i][GNB_NRCELLID_IDX].u64ptr); - if (strcmp(*(GNBParamList.paramarray[i][GNB_TRANSPORT_S_PREFERENCE_IDX].strptr), "local_mac") == 0) { } else if (strcmp(*(GNBParamList.paramarray[i][GNB_TRANSPORT_S_PREFERENCE_IDX].strptr), "cudu") == 0) { @@ -2068,7 +2088,7 @@ gNB_RRC_INST *RCconfig_NRRRC() // search if in active list - gNB_RrcConfigurationReq nrrrc_config = {0}; + nr_rrc_config_t nrrrc_config = {0}; for (k=0; k number_symbols = tda_info.nrOfSymbols; dlsch_pdu->start_symbol = tda_info.startSymbolIndex; - struct NR_DMRS_DownlinkConfig *dl_dmrs_config = NULL; - if (pdsch_config) - dl_dmrs_config = (tda_info.mapping_type == typeA) ? pdsch_config->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup - : pdsch_config->dmrs_DownlinkForPDSCH_MappingTypeB->choice.setup; + mappingType_t type = tda_info.mapping_type; + NR_DMRS_DownlinkConfig_t *dl_dmrs_config = NULL; + if (pdsch_config) { + if (type == typeA) { + if (!pdsch_config->dmrs_DownlinkForPDSCH_MappingTypeA) { + LOG_E(MAC, "Invalid PDSCH DMRS configuration, expected typeA but not configured\n"); + return -1; + } else + dl_dmrs_config = pdsch_config->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup; + } else { // typeB + if (!pdsch_config->dmrs_DownlinkForPDSCH_MappingTypeB) { + LOG_E(MAC, "Invalid PDSCH DMRS configuration, expected typeB but not configured\n"); + return -1; + } else + dl_dmrs_config = pdsch_config->dmrs_DownlinkForPDSCH_MappingTypeB->choice.setup; + } + } dlsch_pdu->nscid = 0; if (dl_dmrs_config && dl_dmrs_config->scramblingID0) @@ -1184,9 +1197,23 @@ static int nr_ue_process_dci_dl_11(NR_UE_MAC_INST_t *mac, dlsch_pdu->number_symbols = tda_info.nrOfSymbols; dlsch_pdu->start_symbol = tda_info.startSymbolIndex; - struct NR_DMRS_DownlinkConfig *dl_dmrs_config = (tda_info.mapping_type == typeA) - ? pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup - : pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeB->choice.setup; + mappingType_t type = tda_info.mapping_type; + NR_DMRS_DownlinkConfig_t *dl_dmrs_config = NULL; + if (pdsch_Config) { + if (type == typeA) { + if (!pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeA) { + LOG_E(MAC, "Invalid PDSCH DMRS configuration, expected typeA but not configured\n"); + return -1; + } else + dl_dmrs_config = pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup; + } else { // typeB + if (!pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeB) { + LOG_E(MAC, "Invalid PDSCH DMRS configuration, expected typeB but not configured\n"); + return -1; + } else + dl_dmrs_config = pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeB->choice.setup; + } + } switch (dci->dmrs_sequence_initialization.val) { case 0: @@ -1417,9 +1444,9 @@ static int nr_ue_process_dci_dl_11(NR_UE_MAC_INST_t *mac, dlsch_pdu->tbslbrm = nr_compute_tbslbrm(dlsch_pdu->mcs_table, sc_info->dl_bw_tbslbrm, nl_tbslbrm); /*PTRS configuration */ dlsch_pdu->pduBitmap = 0; - if (pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS != NULL) { + if (dl_dmrs_config->phaseTrackingRS != NULL) { bool valid_ptrs_setup = - set_dl_ptrs_values(pdsch_Config->dmrs_DownlinkForPDSCH_MappingTypeA->choice.setup->phaseTrackingRS->choice.setup, + set_dl_ptrs_values(dl_dmrs_config->phaseTrackingRS->choice.setup, dlsch_pdu->number_rbs, dlsch_pdu->mcs, dlsch_pdu->mcs_table, @@ -4208,8 +4235,6 @@ int nr_write_ce_ulsch_pdu(uint8_t *mac_ce, void nr_ue_send_sdu(NR_UE_MAC_INST_t *mac, nr_downlink_indication_t *dl_info, int pdu_id) { - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_SEND_SDU, VCD_FUNCTION_IN); - LOG_D(NR_MAC, "In [%d.%d] Handling DLSCH PDU type %d\n", dl_info->frame, @@ -4237,5 +4262,4 @@ void nr_ue_send_sdu(NR_UE_MAC_INST_t *mac, nr_downlink_indication_t *dl_info, in default : AssertFatal(false, "Invalid DLSCH PDU type\n"); } - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_SEND_SDU, VCD_FUNCTION_OUT); } diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index fcc977d9e5..d06c8302a6 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -598,12 +598,21 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, } int mappingtype = tda_info->mapping_type; - NR_DMRS_UplinkConfig_t *NR_DMRS_ulconfig = NULL; - if(pusch_Config) { - NR_DMRS_ulconfig = (mappingtype == NR_PUSCH_TimeDomainResourceAllocation__mappingType_typeA) - ? pusch_Config->dmrs_UplinkForPUSCH_MappingTypeA->choice.setup - : pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup; + if (pusch_Config) { + if (mappingtype == NR_PUSCH_TimeDomainResourceAllocation__mappingType_typeA) { + if (!pusch_Config->dmrs_UplinkForPUSCH_MappingTypeA) { + LOG_E(MAC, "Invalid PUSCH DMRS configuration, expected typeA but not configured\n"); + return -1; + } else + NR_DMRS_ulconfig = pusch_Config->dmrs_UplinkForPUSCH_MappingTypeA->choice.setup; + } else { // typeB + if (!pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB) { + LOG_E(MAC, "Invalid PDSCH DMRS configuration, expected typeB but not configured\n"); + return -1; + } else + NR_DMRS_ulconfig = pusch_Config->dmrs_UplinkForPUSCH_MappingTypeB->choice.setup; + } } pusch_config_pdu->scid = 0; diff --git a/openair2/LAYER2/NR_MAC_gNB/config.c b/openair2/LAYER2/NR_MAC_gNB/config.c index dfa68773a4..005ad05720 100644 --- a/openair2/LAYER2/NR_MAC_gNB/config.c +++ b/openair2/LAYER2/NR_MAC_gNB/config.c @@ -708,7 +708,7 @@ static void config_common(gNB_MAC_INST *nrmac, const nr_mac_config_t *config, NR // precoding matrix configuration (to be improved) cfg->pmi_list = init_DL_MIMO_codebook(nrmac, pdsch_AntennaPorts); - if (nrmac->beam_info.beam_mode == PRECONFIGURED_BEAM_IDX) { + if (nrmac->beam_info.beam_mode != NO_BEAM_MODE) { LOG_I(NR_MAC, "Configuring analog beamforming in config_request message\n"); cfg->analog_beamforming_ve.num_beams_period_vendor_ext.tl.tag = NFAPI_NR_FAPI_NUM_BEAMS_PERIOD_VENDOR_EXTENSION_TAG; cfg->analog_beamforming_ve.num_beams_period_vendor_ext.value = nrmac->beam_info.beams_per_period; @@ -1093,14 +1093,17 @@ void prepare_du_configuration_update(gNB_MAC_INST *mac, { /* send gNB-DU configuration update to RRC */ f1ap_gnb_du_configuration_update_t update = { - .transaction_id = 1, - .num_status = 1, - .status[0].plmn = info->plmn, - .status[0].nr_cellid = info->nr_cellid, - .status[0].service_state = F1AP_STATE_IN_SERVICE, + .transaction_id = 1, + .num_status = 1, + .status = calloc_or_fail(1, sizeof(f1ap_cell_status_t)), }; + update.status[0].plmn = info->plmn; + update.status[0].nr_cellid = info->nr_cellid; + update.status[0].service_state = F1AP_STATE_IN_SERVICE; + if (mib && sib1) { - update.num_cells_to_modify = 1, + update.num_cells_to_modify = 1; + update.cell_to_modify = calloc_or_fail(1, sizeof(*update.cell_to_modify)); update.cell_to_modify[0].old_nr_cellid = info->nr_cellid; update.cell_to_modify[0].info = *info; update.cell_to_modify[0].sys_info = get_sys_info(mib, sib1, NULL); diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c index 6a9cce656a..4df7f690a1 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c @@ -36,7 +36,6 @@ #include "common/utils/LOG/log.h" #include "common/utils/nr/nr_common.h" -#include "common/utils/LOG/vcd_signal_dumper.h" #include "UTIL/OPT/opt.h" #include "openair2/X2AP/x2ap_eNB.h" @@ -159,7 +158,6 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, frame_t frame, slot_t slo gNB->frame = frame; start_meas(&gNB->gNB_scheduler); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_gNB_DLSCH_ULSCH_SCHEDULER,VCD_FUNCTION_IN); for (int CC_id = 0; CC_id < MAX_NUM_CCs; CC_id++) { int num_beams = 1; @@ -264,5 +262,4 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, frame_t frame, slot_t slo stop_meas(&gNB->gNB_scheduler); NR_SCHED_UNLOCK(&gNB->sched_lock); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_gNB_DLSCH_ULSCH_SCHEDULER,VCD_FUNCTION_OUT); } diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c index 1657a492fe..8d1eda288b 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c @@ -37,7 +37,6 @@ /* Utils */ #include "common/utils/LOG/log.h" -#include "common/utils/LOG/vcd_signal_dumper.h" #include "common/utils/nr/nr_common.h" #include "UTIL/OPT/opt.h" @@ -696,8 +695,6 @@ void nr_initiate_ra_proc(module_id_t module_idP, int16_t timing_offset, uint32_t preamble_power) { - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_INITIATE_RA_PROC, 1); - gNB_MAC_INST *nr_mac = RC.nrmac[module_idP]; NR_SCHED_LOCK(&nr_mac->sched_lock); @@ -766,7 +763,6 @@ void nr_initiate_ra_proc(module_id_t module_idP, UE->UE_beam_index = get_beam_from_ssbidx(nr_mac, cc->ssb_index[n_ssb]); LOG_I(NR_MAC, "UE %04x: Sync beam index %d\n", UE->rnti, UE->UE_beam_index); NR_SCHED_UNLOCK(&nr_mac->sched_lock); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_INITIATE_RA_PROC, 0); } static void start_ra_contention_resolution_timer(NR_RA_t *ra, const long ra_ContentionResolutionTimer, const int K2, const int scs) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c index b3e3236161..501543e4be 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c @@ -34,7 +34,6 @@ #include "NR_MAC_gNB/nr_mac_gNB.h" #include "NR_MAC_gNB/mac_proto.h" #include "common/utils/LOG/log.h" -#include "common/utils/LOG/vcd_signal_dumper.h" #include "UTIL/OPT/opt.h" #include "common/utils/nr/nr_common.h" diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 2dea54ea4a..7742b06294 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -35,7 +35,6 @@ #include "NR_MAC_gNB/nr_mac_gNB.h" #include "NR_MAC_gNB/mac_proto.h" #include "common/utils/LOG/log.h" -#include "common/utils/LOG/vcd_signal_dumper.h" #include "common/utils/nr/nr_common.h" #include "UTIL/OPT/opt.h" diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c b/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c index 71547f85e7..fe4a64da2b 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c +++ b/openair2/LAYER2/NR_MAC_gNB/nr_radio_config.c @@ -41,6 +41,7 @@ #include "openair2/LAYER2/NR_MAC_gNB/mac_proto.h" #include "openair3/UTILS/conversions.h" #include "LAYER2/nr_rlc/nr_rlc_asn1_utils.h" +#include "reverse_bits.h" #include "NR_MeasurementTimingConfiguration.h" #include "uper_decoder.h" @@ -652,31 +653,6 @@ static void set_dl_maxmimolayers(NR_PDSCH_ServingCellConfig_t *pdsch_servingcell *pdsch_servingcellconfig->ext1->maxMIMO_Layers = min(maxMIMO_layers, ue_supported_layers); } -// TODO: Implement to b_SRS = 1 and b_SRS = 2 -long rrc_get_max_nr_csrs(const int max_rbs, const long b_SRS) { - - if(b_SRS>0) { - LOG_E(NR_RRC,"rrc_get_max_nr_csrs(): Not implemented yet for b_SRS>0\n"); - return 0; // This c_srs is always valid - } - - const uint16_t m_SRS[64] = { 4, 8, 12, 16, 16, 20, 24, 24, 28, 32, 36, 40, 48, 48, 52, 56, 60, 64, 72, 72, 76, 80, 88, - 96, 96, 104, 112, 120, 120, 120, 128, 128, 128, 132, 136, 144, 144, 144, 144, 152, 160, - 160, 160, 168, 176, 184, 192, 192, 192, 192, 208, 216, 224, 240, 240, 240, 240, 256, 256, - 256, 264, 272, 272, 272 }; - - long c_srs = 0; - uint16_t m = 4; - for(int c = 1; c<64; c++) { - if(m_SRS[c]>m && m_SRS[c]cnAssociation->choice.sdap_Config, sdap_config); sdap_config->pdu_Session = pdu->sessionId; /* SDAP */ - sdap_config->sdap_HeaderDL = drb->sdap_config.sDAP_Header_DL; - sdap_config->sdap_HeaderUL = drb->sdap_config.sDAP_Header_UL; + /* Convert from internal representation to ASN.1 enum: + * Internal: false=absent, true=present (or uninitialized=false means absent) + * ASN.1: 0=present, 1=absent */ + sdap_config->sdap_HeaderDL = drb->sdap_config.sDAP_Header_DL ? NR_SDAP_Config__sdap_HeaderDL_present : NR_SDAP_Config__sdap_HeaderDL_absent; + sdap_config->sdap_HeaderUL = drb->sdap_config.sDAP_Header_UL ? NR_SDAP_Config__sdap_HeaderUL_present : NR_SDAP_Config__sdap_HeaderUL_absent; sdap_config->defaultDRB = drb->sdap_config.defaultDRB; asn1cCalloc(sdap_config->mappedQoS_FlowsToAdd, FlowsToAdd); for (int j = 0; j < drb->numQosFlow2Setup; j++) { diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c b/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c index e588c2665b..fdc0c57fe4 100644 --- a/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c @@ -31,11 +31,6 @@ #include #include #include -#include "LAYER2/MAC/mac_extern.h" -#include "LTE_DRB-ToAddModList.h" -#include "LTE_DRB-ToReleaseList.h" -#include "LTE_PMCH-InfoList-r9.h" -#include "LTE_SRB-ToAddModList.h" #include "NR_DRB-ToAddMod.h" #include "NR_QFI.h" #include "NR_SDAP-Config.h" @@ -53,7 +48,7 @@ #include "f1ap_messages_types.h" #include "gnb_config.h" #include "gtpv1_u_messages_types.h" -#include "hashtable.h" +#include "ds/hashtable.h" #include "intertask_interface.h" #include "common/utils/LOG/log.h" #include "nfapi/oai_integration/vendor_ext.h" diff --git a/openair2/NR_UE_PHY_INTERFACE/NR_IF_Module.c b/openair2/NR_UE_PHY_INTERFACE/NR_IF_Module.c index 42030133af..73845551af 100644 --- a/openair2/NR_UE_PHY_INTERFACE/NR_IF_Module.c +++ b/openair2/NR_UE_PHY_INTERFACE/NR_IF_Module.c @@ -35,6 +35,7 @@ #include "NR_MAC_UE/mac_proto.h" #include "assertions.h" #include "SCHED_NR_UE/fapi_nr_ue_l1.h" +#include "reverse_bits.h" #include "openair2/RRC/NR_UE/L2_interface_ue.h" #define MAX_IF_MODULES 100 diff --git a/openair2/RRC/LTE/rrc_UE.c b/openair2/RRC/LTE/rrc_UE.c index c8ea0fa878..6b327d92bc 100644 --- a/openair2/RRC/LTE/rrc_UE.c +++ b/openair2/RRC/LTE/rrc_UE.c @@ -35,7 +35,7 @@ #include #include "assertions.h" -#include "hashtable.h" +#include "ds/hashtable.h" #include "oai_asn1.h" #include "rrc_defs.h" #include "rrc_extern.h" diff --git a/openair2/RRC/NR/CMakeLists.txt b/openair2/RRC/NR/CMakeLists.txt index 658bfeee5a..9ca17a42bd 100644 --- a/openair2/RRC/NR/CMakeLists.txt +++ b/openair2/RRC/NR/CMakeLists.txt @@ -1,5 +1,10 @@ add_subdirectory(MESSAGES) +# Library for RRC cell management functions +add_library(rrc_cell_management STATIC rrc_cell_management.c) +target_link_libraries(rrc_cell_management PUBLIC asn1_nr_rrc asn1_lte_rrc) +target_link_libraries(rrc_cell_management PRIVATE utils LOG asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs) + if(ENABLE_TESTS) add_subdirectory(tests) endif() diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c index 2c3cf2717a..57fb3daf12 100644 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.c +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c @@ -325,7 +325,6 @@ int do_RRCSetup(uint8_t *const buffer, const uint8_t transaction_id, const uint8_t *masterCellGroup, int masterCellGroup_len, - const gNB_RrcConfigurationReq *configuration, NR_SRB_ToAddModList_t *SRBs) //------------------------------------------------------------------------------ { diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.h b/openair2/RRC/NR/MESSAGES/asn1_msg.h index 2e955f8cff..d6783211a6 100644 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.h +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.h @@ -32,6 +32,7 @@ #define __RRC_NR_MESSAGES_ASN1_MSG__H__ #include +#include "common/platform_constants.h" #include #include #include "NR_ARFCN-ValueNR.h" @@ -52,10 +53,10 @@ #include "NR_SecurityConfig.h" #include "NR_MeasurementReport.h" #include "NR_MeasurementTimingConfiguration.h" +#include "NR_UE-NR-Capability.h" #include "NR_UE-CapabilityRAT-ContainerList.h" #include "ds/seq_arr.h" #include "ds/byte_array.h" -#include "rrc_messages_types.h" #include "openair2/LAYER2/nr_pdcp/nr_pdcp_configuration.h" #include "common/utils/nr/nr_common.h" struct asn_TYPE_descriptor_s; @@ -95,7 +96,6 @@ int do_RRCSetup(uint8_t *const buffer, const uint8_t transaction_id, const uint8_t *masterCellGroup, int masterCellGroup_len, - const gNB_RrcConfigurationReq *configuration, NR_SRB_ToAddModList_t *SRBs); int do_NR_SecurityModeCommand(uint8_t *const buffer, diff --git a/openair2/RRC/NR/nr_rrc_defs.h b/openair2/RRC/NR/nr_rrc_defs.h index 46e1ef3384..b992af3e07 100644 --- a/openair2/RRC/NR/nr_rrc_defs.h +++ b/openair2/RRC/NR/nr_rrc_defs.h @@ -148,6 +148,16 @@ typedef enum { RRC_UECAPABILITY_ENQUIRY, } rrc_action_t; +typedef struct nr_rrc_config { + uint32_t tac; + plmn_id_t plmn[PLMN_LIST_MAX_SIZE]; + uint8_t num_plmn; + + bool um_on_default_drb; + bool enable_sdap; + int drbs; +} nr_rrc_config_t; + /* Small state for delaying NG-triggered actions (setup/release) */ typedef struct { int max_delays; @@ -166,6 +176,26 @@ typedef struct { pdusession_level_qos_parameter_t qos; } nr_rrc_qos_t; +typedef struct { + uint64_t dl_br; + uint64_t ul_br; +} nr_rrc_ambr_t; + +/** @brief UE serving cell information + * @note ServCellIndex is a short identity used to uniquely identify a serving cell + * (PCell, PSCell, or SCell) across cell groups (TS 38.331). + * Value 0 applies for the PCell, while the SCellIndex that has previously + * been assigned applies for SCells. + * @note Range: 0..maxNrofServingCells-1 where maxNrofServingCells = 32 */ +typedef struct { + /* NR Cell Identity (cell_id) */ + uint64_t nci; + /* ServCellIndex (TS 38.331): 0 = PCell, 1-31 = SCell */ + uint8_t serving_cell_id; + /* SCTP association ID of the DU that owns this cell (for fast lookup) */ + sctp_assoc_t assoc_id; +} ue_serving_cell_t; + /* forward declaration */ typedef struct nr_handover_context_s nr_handover_context_t; @@ -207,7 +237,9 @@ typedef struct gNB_RRC_UE_s { uint64_t ng_5G_S_TMSI_Part1; NR_EstablishmentCause_t establishment_cause; - uint64_t nr_cellid; + /* Dynamic array of UE serving cells */ + seq_arr_t serving_cells; /* ue_serving_cell_t */ + uint32_t rrc_ue_id; uint64_t amf_ue_ngap_id; // Globally Unique AMF Identifier @@ -244,6 +276,9 @@ typedef struct gNB_RRC_UE_s { int n_initial_pdu; pdusession_t *initial_pdus; + // Aggregate Maximum Bit Rate + nr_rrc_ambr_t ambr; + /* Nas Pdu */ byte_array_t nas_pdu; @@ -335,15 +370,64 @@ typedef struct cucp_cuup_if_s { cucp_cuup_bearer_context_release_func_t bearer_context_release; } cucp_cuup_if_t; +typedef struct { + int band; + uint32_t arfcn; + uint8_t scs; + uint16_t nrb; +} nr_rrc_freq_info_t; + +typedef struct { + nr_rrc_freq_info_t dlul; +} nr_rrc_tdd_info_t; + +typedef struct { + nr_rrc_freq_info_t dl; + nr_rrc_freq_info_t ul; +} nr_rrc_fdd_info_t; + +typedef struct { + /* operating mode: TDD or FDD */ + enum { NR_MODE_TDD = 0, NR_MODE_FDD = 1 } mode; + uint64_t cell_id; + uint16_t pci; + union { + nr_rrc_tdd_info_t tdd; + nr_rrc_fdd_info_t fdd; + }; + plmn_id_t plmn; + uint16_t tac; +} nr_rrc_cell_info_t; + +typedef struct nr_rrc_cell_container_t { + /* Tree-related data */ + RB_ENTRY(nr_rrc_cell_container_t) entries; + /* transport association */ + sctp_assoc_t assoc_id; + /* Cell-only RRC-local info */ + nr_rrc_cell_info_t info; + /* MIB message (6.2.2 TS 38.331) */ + NR_MIB_t *mib; + /* SIB1 message (6.2.2 TS 38.331) */ + NR_SIB1_t *sib1; + /* MeasurementTimingConfiguration inter-node (TS 38.331) */ + NR_MeasurementTimingConfiguration_t *mtc; +} nr_rrc_cell_container_t; + typedef struct nr_rrc_du_container_t { /* Tree-related data */ RB_ENTRY(nr_rrc_du_container_t) entries; - + /* DU-only information */ + /* Transport association identifier for this DU */ sctp_assoc_t assoc_id; - f1ap_setup_req_t *setup_req; - NR_MIB_t *mib; - NR_SIB1_t *sib1; - NR_MeasurementTimingConfiguration_t *mtc; + /* DU identity */ + uint64_t gNB_DU_id; + /* DU name */ + char *gNB_DU_name; + /* RRC version */ + uint8_t rrc_ver[3]; + /* Cells, indexed by cell_id */ + seq_arr_t cells; /* nr_rrc_cell_container_t* */ } nr_rrc_du_container_t; typedef struct nr_rrc_cuup_container_t { @@ -364,11 +448,9 @@ typedef struct gNB_RRC_INST_s { eth_params_t eth_params_s; uid_allocator_t uid_allocator; RB_HEAD(rrc_nr_ue_tree_s, rrc_gNB_ue_context_s) rrc_ue_head; // ue_context tree key search by rnti - /// NR cell id - uint64_t nr_cellid; // RRC configuration - gNB_RrcConfigurationReq configuration; + nr_rrc_config_t configuration; seq_arr_t *SIBs; // gNB N3 GTPU instance @@ -387,6 +469,10 @@ typedef struct gNB_RRC_INST_s { RB_HEAD(rrc_du_tree, nr_rrc_du_container_t) dus; // DUs, indexed by assoc_id size_t num_dus; + /* Global cell tree, indexed by cell_id */ + RB_HEAD(rrc_cell_tree, nr_rrc_cell_container_t) cells; + size_t num_cells; + RB_HEAD(rrc_cuup_tree, nr_rrc_cuup_container_t) cuups; // CU-UPs, indexed by assoc_id size_t num_cuups; @@ -395,8 +481,12 @@ typedef struct gNB_RRC_INST_s { nr_rlc_configuration_t rlc_config; } gNB_RRC_INST; +/** Forward declaration for UE log macros */ +const ue_serving_cell_t *ue_get_pcell_entry(const gNB_RRC_UE_t *ue); + #define UE_LOG_FMT "(cellID %lx, UE ID %d RNTI %04x)" -#define UE_LOG_ARGS(ue_context) (ue_context)->nr_cellid, (ue_context)->rrc_ue_id, (ue_context)->rnti +#define UE_LOG_ARGS(ue_context) \ + (ue_get_pcell_entry(ue_context) ? ue_get_pcell_entry(ue_context)->nci : 0), (ue_context)->rrc_ue_id, (ue_context)->rnti #define LOG_UE_DL_EVENT(ue_context, fmt, ...) LOG_A(NR_RRC, "[DL] " UE_LOG_FMT " " fmt, UE_LOG_ARGS(ue_context) __VA_OPT__(,) __VA_ARGS__) #define LOG_UE_EVENT(ue_context, fmt, ...) LOG_A(NR_RRC, "[--] " UE_LOG_FMT " " fmt, UE_LOG_ARGS(ue_context) __VA_OPT__(,) __VA_ARGS__) diff --git a/openair2/RRC/NR/nr_rrc_proto.h b/openair2/RRC/NR/nr_rrc_proto.h index 1db6a4d236..2e6b0cac51 100644 --- a/openair2/RRC/NR/nr_rrc_proto.h +++ b/openair2/RRC/NR/nr_rrc_proto.h @@ -39,6 +39,7 @@ #include "NR_CG-ConfigInfo.h" #include "NR_RRCReconfiguration.h" #include "RRC/NR/MESSAGES/asn1_msg.h" +#include "f1ap_messages_types.h" #define SRB1 1 #define SRB2 2 @@ -117,7 +118,7 @@ byte_array_t rrc_gNB_encode_RRCReconfiguration(gNB_RRC_INST *rrc, gNB_RRC_UE_t * nr_rrc_reconfig_param_t get_RRCReconfiguration_params(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, uint8_t srb_reest_bitmap, bool drb_reestablish); f1ap_qos_flow_param_t get_qos_char_from_qos_flow_param(const pdusession_level_qos_parameter_t *qos_param); -void openair_rrc_gNB_configuration(gNB_RRC_INST *rrc, gNB_RrcConfigurationReq *configuration); +void openair_rrc_gNB_configuration(gNB_RRC_INST *rrc, nr_rrc_config_t *configuration); NR_SRB_ToAddModList_t *createSRBlist(gNB_RRC_UE_t *ue, uint8_t reestablish); NR_DRB_ToAddModList_t *createDRBlist(gNB_RRC_UE_t *ue, bool reestablish, bool do_integrity, bool do_ciphering); void activate_srb(gNB_RRC_UE_t *UE, int srb_id); @@ -127,11 +128,11 @@ void init_delayed_action(delayed_action_state_t *delayed_action); /** @brief Prepare and send F1AP UE Context Setup Request for a target DU * @param rrc RRC instance * @param ue UE context - * @param du Target DU container containing cell and setup information + * @param cell Target cell container containing cell information * @param ho_prep_info Optional handover preparation information (NULL if not provided) */ void rrc_f1_ue_context_setup_for_target_du(const gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, - const nr_rrc_du_container_t *du, + const nr_rrc_cell_container_t *cell, const byte_array_t *ho_prep_info); #endif diff --git a/openair2/RRC/NR/rrc_cell_management.c b/openair2/RRC/NR/rrc_cell_management.c new file mode 100644 index 0000000000..0df2665c04 --- /dev/null +++ b/openair2/RRC/NR/rrc_cell_management.c @@ -0,0 +1,411 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#include +#include +#include "rrc_cell_management.h" +#include "common/utils/assertions.h" // for DevAssert +#include "common/utils/collection/tree.h" // for RB_FOREACH +#include "common/utils/alg/find.h" // for find_if +#include "common/utils/ds/seq_arr.h" // for seq_arr_t, seq_arr_free +#include "common/utils/LOG/log.h" // for LOG_I, LOG_W, LOG_E +#include "openair2/F1AP/f1ap_ids.h" // for cu_exists_f1_ue_data, cu_get_f1_ue_data +#include "asn_application.h" // for ASN_STRUCT_FREE +#include "NR_MIB.h" // for asn_DEF_NR_MIB +#include "NR_SIB1.h" // for asn_DEF_NR_SIB1 +#include "NR_MeasurementTimingConfiguration.h" // for asn_DEF_NR_MeasurementTimingConfiguration + +/** @brief Comparison function for DU tree + * @param[in] a Pointer to first DU container + * @param[in] b Pointer to second DU container + * @return Comparison result */ +int du_compare(const nr_rrc_du_container_t *a, const nr_rrc_du_container_t *b) +{ + if (a->assoc_id > b->assoc_id) + return 1; + if (a->assoc_id == b->assoc_id) + return 0; + return -1; /* a->assoc_id < b->assoc_id */ +} + +// Generate RB tree functions for DU tree +RB_GENERATE(rrc_du_tree, nr_rrc_du_container_t, entries, du_compare); + +/** @brief Find DU by SCTP association ID (uses DU RB-tree lookup) + * @param[in] rrc Pointer to RRC instance + * @param[in] assoc_id SCTP association ID + * @return Pointer to the DU container, or NULL if not found + * @note O(log N_DU) complexity using DU RB-tree lookup */ +nr_rrc_du_container_t *get_du_by_assoc_id(gNB_RRC_INST *rrc, const sctp_assoc_t assoc_id) +{ + DevAssert(rrc != NULL); + nr_rrc_du_container_t e = {.assoc_id = assoc_id}; + return RB_FIND(rrc_du_tree, &rrc->dus, &e); +} + +/** @brief Add DU to RRC DU tree and increment rrc->num_dus on success + * @param[in] rrc Pointer to RRC instance + * @param[in] du Pointer to DU container to add + * @return NULL if inserted successfully; pointer to existing DU (collision) if duplicate assoc_id */ +nr_rrc_du_container_t *rrc_add_du(gNB_RRC_INST *rrc, nr_rrc_du_container_t *du) +{ + DevAssert(rrc != NULL); + DevAssert(du != NULL); + nr_rrc_du_container_t *collision = RB_INSERT(rrc_du_tree, &rrc->dus, du); + if (collision == NULL) + rrc->num_dus++; + return collision; +} + +/** @brief Free a DU container and its allocated fields (gNB_DU_name); free the DU itself + * @param[in] du Pointer to DU container to free + * @note Does NOT remove the DU from the tree or free du->cells; caller must do RB_REMOVE and seq_arr_free first */ +void rrc_free_du_container(nr_rrc_du_container_t *du) +{ + DevAssert(du != NULL); + free(du->gNB_DU_name); + free(du); +} + +/** @brief Remove DU from RRC DU tree, decrement rrc->num_dus, and free the DU and its resources + * @param[in] rrc Pointer to RRC instance + * @param[in] du Pointer to DU container to remove (caller must have removed and freed all cells first) */ +void rrc_rm_du(gNB_RRC_INST *rrc, nr_rrc_du_container_t *du) +{ + DevAssert(rrc != NULL); + DevAssert(du != NULL); + nr_rrc_du_container_t *removed = RB_REMOVE(rrc_du_tree, &rrc->dus, du); + AssertFatal(removed == du, "Failed to remove DU (assoc_id %d) from tree\n", du->assoc_id); + rrc->num_dus--; + seq_arr_free(&du->cells, NULL); + rrc_free_du_container(du); +} + +/** @brief Remove all cells from the DU (and from global cell tree), remove the DU from the tree, and free the DU + * @param[in] rrc Pointer to RRC instance + * @param[in] du Pointer to DU container to cleanup (removed from tree and freed) */ +void rrc_cleanup_du(gNB_RRC_INST *rrc, nr_rrc_du_container_t *du) +{ + DevAssert(rrc != NULL); + DevAssert(du != NULL); + for (size_t i = seq_arr_size(&du->cells); i > 0; i--) { + nr_rrc_cell_container_t **cell_ptr = seq_arr_at(&du->cells, i - 1); + nr_rrc_cell_container_t *cell = *cell_ptr; + LOG_I(NR_RRC, "Removing cell %lu from DU cells array\n", cell->info.cell_id); + seq_arr_erase(&du->cells, cell_ptr); + rrc_rm_cell(rrc, cell); + } + rrc_rm_du(rrc, du); +} + +/** @brief Comparison function for cell tree + * @param[in] a Pointer to first cell container + * @param[in] b Pointer to second cell container + * @return Comparison result */ +int rrc_cell_cmp(const struct nr_rrc_cell_container_t *a, const struct nr_rrc_cell_container_t *b) +{ + if (a->info.cell_id < b->info.cell_id) + return -1; + if (a->info.cell_id > b->info.cell_id) + return 1; + return 0; +} + +/** @brief Generate RB tree functions for cell tree */ +RB_GENERATE(rrc_cell_tree, nr_rrc_cell_container_t, entries, rrc_cell_cmp); + +/** @brief Find cell container by cell ID (lookup in global cell tree) + * @param[in] cells Pointer to global cell tree + * @param[in] cell_id Cell ID + * @return Pointer to the cell container, or NULL if not found + * @note O(log N_CELL) using RB-tree lookup */ +nr_rrc_cell_container_t *get_cell_by_cell_id(struct rrc_cell_tree *cells, const uint64_t cell_id) +{ + DevAssert(cells != NULL); + nr_rrc_cell_container_t search_key = {.info = {.cell_id = cell_id}}; + return RB_FIND(rrc_cell_tree, cells, &search_key); +} + +/** @brief Add cell to global cell tree and increment rrc->num_cells on success + * @param[in] rrc Pointer to RRC instance + * @param[in] cell Pointer to cell container to add + * @return NULL if inserted successfully; pointer to existing cell (collision) if duplicate cell_id */ +nr_rrc_cell_container_t *rrc_add_cell(gNB_RRC_INST *rrc, nr_rrc_cell_container_t *cell) +{ + DevAssert(rrc != NULL); + DevAssert(cell != NULL); + nr_rrc_cell_container_t *collision = RB_INSERT(rrc_cell_tree, &rrc->cells, cell); + if (collision == NULL) + rrc->num_cells++; + return collision; +} + +/** @brief Remove cell from global cell tree, decrement rrc->num_cells, and free the cell + * @param[in] rrc Pointer to RRC instance + * @param[in] cell Pointer to cell container to remove */ +void rrc_rm_cell(gNB_RRC_INST *rrc, nr_rrc_cell_container_t *cell) +{ + DevAssert(rrc != NULL); + DevAssert(cell != NULL); + nr_rrc_cell_container_t *removed = RB_REMOVE(rrc_cell_tree, &rrc->cells, cell); + AssertFatal(removed == cell, "Failed to remove cell %ld from tree\n", cell->info.cell_id); + rrc->num_cells--; + rrc_free_cell_container(cell); +} + +static bool eq_cell_id(const void *vval, const void *vit) +{ + const uint64_t *id = (const uint64_t *)vval; + const nr_rrc_cell_container_t **elem_ptr = (const nr_rrc_cell_container_t **)vit; + return (*elem_ptr) != NULL && (*elem_ptr)->info.cell_id == *id; +} + +/** @brief Get cell by cell_id from DU's cells array + * @param[in] cells Pointer to DU's cells sequence array + * @param[in] cell_id Cell ID to search for + * @return Pointer to the cell container, or NULL if not found */ +nr_rrc_cell_container_t *rrc_get_cell_for_du(seq_arr_t *cells, uint64_t cell_id) +{ + DevAssert(cells); + elm_arr_t elm = find_if(cells, &cell_id, eq_cell_id); + if (elm.found) { + // elm.it points to an element in seq_arr, which stores nr_rrc_cell_container_t* + nr_rrc_cell_container_t **cell_ptr = (nr_rrc_cell_container_t **)elm.it; + return *cell_ptr; + } + return NULL; +} + +static bool eq_pci_in_du(const void *vval, const void *vit) +{ + const uint16_t *pci = (const uint16_t *)vval; + const nr_rrc_cell_container_t **elem_ptr = (const nr_rrc_cell_container_t **)vit; + return (*elem_ptr) != NULL && (*elem_ptr)->info.pci == *pci; +} + +/** @brief Get cell by PCI from DU's cells array + * @param[in] cells Pointer to DU's cells sequence array + * @param[in] pci PCI to search for + * @return Pointer to the cell container, or NULL if not found + * @note PCI must be unique within a DU; returns the first match */ +nr_rrc_cell_container_t *rrc_get_cell_by_pci_for_du(const seq_arr_t *cells, uint16_t pci) +{ + DevAssert(cells); + elm_arr_t elm = find_if((seq_arr_t *)cells, &pci, eq_pci_in_du); + if (elm.found) { + // elm.it points to an element in seq_arr, which stores nr_rrc_cell_container_t* + nr_rrc_cell_container_t **cell_ptr = (nr_rrc_cell_container_t **)elm.it; + return *cell_ptr; + } + return NULL; +} + +/** @brief Add cell to DU's cells array + * @param[in] cells Pointer to DU's cells sequence array + * @param[in] cell Pointer to cell container to add + * @return Pointer to the added cell, or NULL on duplicate cell_id/PCI or error + * @note Rejects duplicate cell_id or PCI within the DU */ +nr_rrc_cell_container_t *rrc_add_cell_to_du(seq_arr_t *cells, nr_rrc_cell_container_t *cell) +{ + DevAssert(cells); + DevAssert(cell); + + // Check for duplicate cell_id within this DU + nr_rrc_cell_container_t *exists = rrc_get_cell_for_du(cells, cell->info.cell_id); + if (exists) { + LOG_E(NR_RRC, "Trying to add an already existing cell with ID=%lu\n", cell->info.cell_id); + return NULL; + } + + // Check for duplicate PCI within this DU (PCI must be unique within a DU) + nr_rrc_cell_container_t *existing_pci = rrc_get_cell_by_pci_for_du(cells, cell->info.pci); + if (existing_pci != NULL) { + LOG_E(NR_RRC, + "Trying to add cell with ID=%lu, but PCI %d already exists in this DU (cell ID %lu)\n", + cell->info.cell_id, + cell->info.pci, + existing_pci->info.cell_id); + return NULL; + } + + seq_arr_push_back(cells, &cell, sizeof(nr_rrc_cell_container_t *)); + LOG_I(NR_RRC, "Added cell %lu to DU cells array (total cells = %ld)\n", cell->info.cell_id, seq_arr_size(cells)); + + return cell; +} + +/** @brief Free a cell container and all its associated resources + * @param[in] cell Pointer to the cell container to free + * @note Frees ASN.1 structures (mib, sib1, mtc) and the cell container itself + * @note Does NOT remove the cell from the tree - caller must do RB_REMOVE first */ +void rrc_free_cell_container(nr_rrc_cell_container_t *cell) +{ + DevAssert(cell); + ASN_STRUCT_FREE(asn_DEF_NR_MIB, cell->mib); + ASN_STRUCT_FREE(asn_DEF_NR_SIB1, cell->sib1); + ASN_STRUCT_FREE(asn_DEF_NR_MeasurementTimingConfiguration, cell->mtc); + free(cell); +} + +static bool eq_serving_cell_id(const void *vval, const void *vit) +{ + const uint8_t *id = (const uint8_t *)vval; + const ue_serving_cell_t *elem = (const ue_serving_cell_t *)vit; + return elem->serving_cell_id == *id; +} + +/** @brief Get serving cell from UE's serving_cells array by serving_cell_id + * @param[in] ue UE context + * @param[in] serving_cell_id ServCellIndex (0 = PCell, 1-31 = SCell) + * @return Pointer to serving cell entry, or NULL if not found */ +ue_serving_cell_t *rrc_get_ue_serving_cell_by_id(const gNB_RRC_UE_t *ue, uint8_t serving_cell_id) +{ + DevAssert(ue); + elm_arr_t elm = find_if((seq_arr_t *)&ue->serving_cells, &serving_cell_id, eq_serving_cell_id); + if (elm.found) + return (ue_serving_cell_t *)elm.it; + return NULL; +} + +/** @brief Add a serving cell to UE's serving_cells array + * @param[in] ue UE context + * @param[in] cell Cell container (nci and assoc_id taken from here) + * @param[in] serving_cell_id ServCellIndex (0 = PCell, 1-31 = SCell) + * @return Pointer to the added or existing entry, or NULL on error + * @note Uniqueness: (serving_cell_id, nci) must be unique. Same pair returns existing; + * same serving_cell_id, different nci returns NULL. */ +ue_serving_cell_t *rrc_add_ue_serving_cell(gNB_RRC_UE_t *ue, const nr_rrc_cell_container_t *cell, uint8_t serving_cell_id) +{ + DevAssert(ue); + DevAssert(serving_cell_id < RRC_MAX_NUM_SERVING_CELLS); + + // Check maximum number of items in list + size_t current_count = seq_arr_size(&ue->serving_cells); + if (current_count >= RRC_MAX_NUM_SERVING_CELLS) { + LOG_E(NR_RRC, "Maximum number of serving cells (%d) reached\n", RRC_MAX_NUM_SERVING_CELLS); + return NULL; + } + + // Check if serving cell with this serving_cell_id already exists + ue_serving_cell_t *existing = rrc_get_ue_serving_cell_by_id(ue, serving_cell_id); + if (existing != NULL) { + // If it's the same nci, return existing + if (existing->nci == cell->info.cell_id) { + LOG_D(NR_RRC, "Serving cell %d already has cell %ld\n", serving_cell_id, cell->info.cell_id); + return existing; + } + // Reject if already exists with different nci + LOG_E(NR_RRC, "Serving cell with serving_cell_id %d already exists with different nci\n", serving_cell_id); + return NULL; + } + + LOG_I(NR_RRC, + "Adding new %s (cell %ld, assoc_id %d)\n", + serving_cell_id == RRC_PCELL_INDEX ? "PCell" : "SCell", + cell->info.cell_id, + cell->assoc_id); + ue_serving_cell_t new_cell = {.nci = cell->info.cell_id, .serving_cell_id = serving_cell_id, .assoc_id = cell->assoc_id}; + + size_t elt_size = sizeof(ue_serving_cell_t); + if (serving_cell_id == RRC_PCELL_INDEX) { + /* PCell must match F1 UE data (serving_cells and F1 UE data stay in sync). */ + DevAssert(cu_exists_f1_ue_data(ue->rrc_ue_id)); + DevAssert(cu_get_f1_ue_data(ue->rrc_ue_id).du_assoc_id == cell->assoc_id); + /* Ensure PCell is always at index 0 in serving_cells */ + size_t n = seq_arr_size(&ue->serving_cells); + seq_arr_push_back(&ue->serving_cells, &new_cell, elt_size); + if (n > 0) { + /* If there are existing cells, shift them right to make room for the new PCell */ + void *first = seq_arr_front(&ue->serving_cells); + memmove((uint8_t *)first + elt_size, first, n * elt_size); + memcpy(first, &new_cell, elt_size); + } + } else { + /* SCells are appended after PCell (if any) */ + seq_arr_push_back(&ue->serving_cells, &new_cell, elt_size); + } + return rrc_get_ue_serving_cell_by_id(ue, serving_cell_id); +} + +/** @brief Remove serving cells that belong to a specific DU + * @param[in] ue UE context + * @param[in] assoc_id Association ID of the DU + * @note Removes all serving cells (including PCell) that belong to the specified DU. + * Uses stored assoc_id for O(k) comparison without cell lookup. */ +void rrc_remove_ue_scells_from_du(gNB_RRC_UE_t *ue, sctp_assoc_t assoc_id) +{ + DevAssert(ue); + LOG_I(NR_RRC, "Removing SCells from DU (assoc_id=%d) for UE %d\n", assoc_id, ue->rrc_ue_id); + + // Collect indices of SCells to delete (don't modify array while iterating) + uint32_t indices_to_delete[RRC_MAX_NUM_SERVING_CELLS]; + size_t num_to_delete = 0; + uint32_t index = 0; + + FOR_EACH_SEQ_ARR (ue_serving_cell_t *, scell, &ue->serving_cells) { + // Use stored assoc_id for direct comparison + if (scell->assoc_id == assoc_id) { + indices_to_delete[num_to_delete++] = index; + } + index++; + } + + // Now delete the collected serving cells from end to beginning for safety + for (size_t i = num_to_delete; i > 0; i--) { + uint32_t idx = indices_to_delete[i - 1]; + void *it = seq_arr_at(&ue->serving_cells, idx); + seq_arr_erase(&ue->serving_cells, it); + } + +} + +/** @brief Get PCell serving cell entry for a UE based on serving_cells + * @param[in] ue UE context + * @return Pointer to serving cell entry for PCell, or NULL if not found + * @note PCell is always at index 0. */ +const ue_serving_cell_t *ue_get_pcell_entry(const gNB_RRC_UE_t *ue) +{ + DevAssert(ue); + size_t count = seq_arr_size(&ue->serving_cells); + if (count == 0) + return NULL; + + /* PCell is always the first serving cell */ + const ue_serving_cell_t *first = seq_arr_front(&ue->serving_cells); + DevAssert(first != NULL); + DevAssert(first->serving_cell_id == RRC_PCELL_INDEX); + return first; +} + +/** @brief Get UE's PCell container from the global cell tree + * @param[in] rrc RRC instance (global cell tree) + * @param[in] ue UE context + * @return Pointer to PCell container, or NULL if UE has no PCell or cell not in tree */ +nr_rrc_cell_container_t *rrc_get_pcell_for_ue(gNB_RRC_INST *rrc, const gNB_RRC_UE_t *ue) +{ + DevAssert(rrc != NULL); + DevAssert(ue != NULL); + const ue_serving_cell_t *pcell_entry = ue_get_pcell_entry(ue); + if (pcell_entry == NULL) + return NULL; + return get_cell_by_cell_id(&rrc->cells, pcell_entry->nci); +} + diff --git a/openair2/RRC/NR/rrc_cell_management.h b/openair2/RRC/NR/rrc_cell_management.h new file mode 100644 index 0000000000..99071cceb2 --- /dev/null +++ b/openair2/RRC/NR/rrc_cell_management.h @@ -0,0 +1,73 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#ifndef RRC_CELL_MANAGEMENT_H_ +#define RRC_CELL_MANAGEMENT_H_ + +#include "nr_rrc_defs.h" // for nr_rrc_du_container_t, nr_rrc_cell_container_t, RB_HEAD +#include "common/utils/collection/tree.h" +#include +#include + +/** @brief ServCellIndex value for Primary Cell (TS 38.331) + * @note ServCellIndex is a short identity used to uniquely identify a serving cell + * (PCell, PSCell, or SCell) across cell groups. Value 0 applies for the PCell. */ +#define RRC_PCELL_INDEX 0 + +/** @brief Maximum number of serving cells (TS 38.331) + * @note maxNrofServingCells = 32 (1 PCell + up to 31 SCells) + * @note ServCellIndex range: INTEGER (0..maxNrofServingCells-1) = 0..31 */ +#define RRC_MAX_NUM_SERVING_CELLS 32 + +// Generate RB tree functions (prototypes) +int rrc_cell_cmp(const struct nr_rrc_cell_container_t *a, const struct nr_rrc_cell_container_t *b); +RB_PROTOTYPE(rrc_cell_tree, nr_rrc_cell_container_t, entries, rrc_cell_cmp); +int du_compare(const struct nr_rrc_du_container_t *a, const struct nr_rrc_du_container_t *b); +RB_PROTOTYPE(rrc_du_tree, nr_rrc_du_container_t, entries, du_compare); + +// DU management (global DU tree) +nr_rrc_du_container_t *rrc_add_du(gNB_RRC_INST *rrc, nr_rrc_du_container_t *du); +void rrc_rm_du(gNB_RRC_INST *rrc, nr_rrc_du_container_t *du); +void rrc_cleanup_du(gNB_RRC_INST *rrc, nr_rrc_du_container_t *du); +void rrc_free_du_container(nr_rrc_du_container_t *du); +nr_rrc_du_container_t *get_du_by_assoc_id(gNB_RRC_INST *rrc, const sctp_assoc_t assoc_id); + +// Cell management (global cell tree) +nr_rrc_cell_container_t *get_cell_by_cell_id(struct rrc_cell_tree *cells, const uint64_t cell_id); +nr_rrc_cell_container_t *rrc_add_cell(gNB_RRC_INST *rrc, nr_rrc_cell_container_t *cell); +void rrc_rm_cell(gNB_RRC_INST *rrc, nr_rrc_cell_container_t *cell); + +// Cell management (DU-specific cell tree) +nr_rrc_cell_container_t *rrc_get_cell_for_du(seq_arr_t *cells, uint64_t cell_id); +nr_rrc_cell_container_t *rrc_get_cell_by_pci_for_du(const seq_arr_t *cells, uint16_t pci); +nr_rrc_cell_container_t *rrc_add_cell_to_du(seq_arr_t *cells, nr_rrc_cell_container_t *cell); + +// Cell cleanup +void rrc_free_cell_container(nr_rrc_cell_container_t *cell); + +// UE serving cell functions +ue_serving_cell_t *rrc_add_ue_serving_cell(gNB_RRC_UE_t *ue, const nr_rrc_cell_container_t *cell, uint8_t serving_cell_id); +void rrc_remove_ue_scells_from_du(gNB_RRC_UE_t *ue, sctp_assoc_t assoc_id); +ue_serving_cell_t *rrc_get_ue_serving_cell_by_id(const gNB_RRC_UE_t *ue, uint8_t serving_cell_id); +const ue_serving_cell_t *ue_get_pcell_entry(const gNB_RRC_UE_t *ue); +nr_rrc_cell_container_t *rrc_get_pcell_for_ue(gNB_RRC_INST *rrc, const gNB_RRC_UE_t *ue); + +#endif /* RRC_CELL_MANAGEMENT_H_ */ diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 6b914c27e9..a68ac6691e 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -82,8 +82,10 @@ #include "openair2/F1AP/lib/f1ap_ue_context.h" #include "rrc_gNB_NGAP.h" #include "rrc_gNB_du.h" +#include "rrc_cell_management.h" #include "rrc_gNB_mobility.h" #include "rrc_gNB_radio_bearers.h" +#include "rrc_cell_management.h" #include "rrc_messages_types.h" #include "rrc_gNB_asn1.h" #include "seq_arr.h" @@ -358,7 +360,7 @@ static void rrc_gNB_CU_DU_init(gNB_RRC_INST *rrc) cu_init_f1_ue_data(); } -void openair_rrc_gNB_configuration(gNB_RRC_INST *rrc, gNB_RrcConfigurationReq *configuration) +void openair_rrc_gNB_configuration(gNB_RRC_INST *rrc, nr_rrc_config_t *configuration) { AssertFatal(rrc != NULL, "RC.nrrrc not initialized!"); AssertFatal(MAX_MOBILES_PER_GNB < (module_id_t)0xFFFFFFFFFFFFFFFF, " variable overflow"); @@ -369,6 +371,7 @@ void openair_rrc_gNB_configuration(gNB_RRC_INST *rrc, gNB_RrcConfigurationReq *c RB_INIT(&rrc->rrc_ue_head); RB_INIT(&rrc->cuups); RB_INIT(&rrc->dus); + RB_INIT(&rrc->cells); rrc->configuration = *configuration; } @@ -531,16 +534,18 @@ void activate_srb(gNB_RRC_UE_t *UE, int srb_id) * This helper function initializes common fields that are shared between * different UE context setup scenarios (serving DU, target DU). * @param ue UE context - * @param du DU container for measurement timing configuration + * @param cell (primary serving) cell container * @param gNB_DU_ue_id Optional gNB-DU UE ID to include in the request (NULL if not provided) * @return F1AP UE Context Setup Request */ static f1ap_ue_context_setup_req_t rrc_fill_f1_ue_context_setup(gNB_RRC_UE_t *ue, - const nr_rrc_du_container_t *du, + const nr_rrc_cell_container_t *cell, const uint32_t *gNB_DU_ue_id) { f1ap_ue_context_setup_req_t req = { .gNB_CU_ue_id = ue->rrc_ue_id, - .servCellIndex = 0, + .servCellIndex = RRC_PCELL_INDEX, + .plmn = cell->info.plmn, + .nr_cellid = cell->info.cell_id, }; /* UE Aggregate Maximum Bit Rate Uplink is C-ifDRBSetup: 1 Gbps */ @@ -548,7 +553,7 @@ static f1ap_ue_context_setup_req_t rrc_fill_f1_ue_context_setup(gNB_RRC_UE_t *ue *req.gnb_du_ue_agg_mbr_ul = 1000000000; /* bps */ /* Measurement timing configuration */ - req.cu_to_du_rrc_info.meas_timing_config = get_meas_timing_config(du->mtc, ue->measConfig); + req.cu_to_du_rrc_info.meas_timing_config = get_meas_timing_config(cell->mtc, ue->measConfig); /* UE capabilities */ if (ue->ue_cap_buffer.len > 0) { @@ -562,13 +567,6 @@ static f1ap_ue_context_setup_req_t rrc_fill_f1_ue_context_setup(gNB_RRC_UE_t *ue *req.gNB_DU_ue_id = *gNB_DU_ue_id; } - /* PLMN and cell ID from DU setup request */ - const f1ap_served_cell_info_t *cell_info = &du->setup_req->cell[0].info; - req.plmn.mcc = cell_info->plmn.mcc; - req.plmn.mnc = cell_info->plmn.mnc; - req.plmn.mnc_digit_length = cell_info->plmn.mnc_digit_length; - req.nr_cellid = cell_info->nr_cellid; - return req; } @@ -589,7 +587,7 @@ static void rrc_gNB_generate_RRCSetup(instance_t instance, ue_p->xids[xid] = RRC_SETUP; NR_SRB_ToAddModList_t *SRBs = createSRBlist(ue_p, false); - int size = do_RRCSetup(buf, sizeof(buf), xid, masterCellGroup, masterCellGroup_len, &rrc->configuration, SRBs); + int size = do_RRCSetup(buf, sizeof(buf), xid, masterCellGroup, masterCellGroup_len, SRBs); AssertFatal(size > 0, "do_RRCSetup failed\n"); AssertFatal(size <= 1024, "memory corruption\n"); @@ -770,22 +768,24 @@ void free_RRCReconfiguration_params(nr_rrc_reconfig_param_t params) NR_MeasConfig_t *nr_rrc_get_measconfig(const gNB_RRC_INST *rrc, uint64_t nr_cellid) { - nr_rrc_du_container_t *du = get_du_by_cell_id((gNB_RRC_INST *)rrc, nr_cellid); - DevAssert(du != NULL); - f1ap_served_cell_info_t *cell_info = &du->setup_req->cell[0].info; + nr_rrc_cell_container_t *cell = get_cell_by_cell_id(&((gNB_RRC_INST *)rrc)->cells, nr_cellid); + DevAssert(cell != NULL); - if (du->mtc != NULL) { + if (cell->mtc != NULL) { NR_ReportConfigToAddMod_t *rc_PER = NULL; NR_ReportConfigToAddMod_t *rc_A2 = NULL; seq_arr_t rc_A3_seq = {0}; seq_arr_t neigh_seq = {0}; seq_arr_init(&rc_A3_seq, sizeof(NR_ReportConfigToAddMod_t)); seq_arr_init(&neigh_seq, sizeof(nr_neighbour_cell_t)); - int scs = get_ssb_scs(cell_info); - int band = get_dl_band(cell_info); - const NR_MeasTimingList_t *mtlist = du->mtc->criticalExtensions.choice.c1->choice.measTimingConf->measTiming; + + // Get cell info from the cell container + int scs = cell->info.mode == NR_MODE_TDD ? cell->info.tdd.dlul.scs : cell->info.fdd.dl.scs; + int band = cell->info.mode == NR_MODE_TDD ? cell->info.tdd.dlul.band : cell->info.fdd.dl.band; + + const NR_MeasTimingList_t *mtlist = cell->mtc->criticalExtensions.choice.c1->choice.measTimingConf->measTiming; const NR_MeasTiming_t *mt = mtlist->list.array[0]; - const neighbour_cell_configuration_t *neighbour_config = get_neighbour_cell_config(rrc, cell_info->nr_cellid); + const neighbour_cell_configuration_t *neighbour_config = get_neighbour_cell_config(rrc, cell->info.cell_id); seq_arr_t *neighbour_cells = NULL; if (neighbour_config) neighbour_cells = neighbour_config->neighbour_cells; @@ -835,7 +835,7 @@ NR_MeasConfig_t *nr_rrc_get_measconfig(const gNB_RRC_INST *rrc, uint64_t nr_cell if (rrc->measurementConfiguration.a2_event) rc_A2 = prepare_a2_event_report(rrc->measurementConfiguration.a2_event); - NR_MeasConfig_t *result = get_MeasConfig(mt, band, scs, cell_info->nr_pci, rc_PER, rc_A2, &rc_A3_seq, &neigh_seq, neigh_a3_id); + NR_MeasConfig_t *result = get_MeasConfig(mt, band, scs, cell->info.pci, rc_PER, rc_A2, &rc_A3_seq, &neigh_seq, neigh_a3_id); // Clean up sequence arrays seq_arr_free(&rc_A3_seq, NULL); @@ -1058,12 +1058,14 @@ static void rrc_gNB_send_f1_drb_release_request(gNB_RRC_INST *rrc, gNB_RRC_UE_t f1ap_ue_context_mod_req_t req = { .gNB_CU_ue_id = ue_p->rrc_ue_id, .gNB_DU_ue_id = ue_data.secondary_ue, - .servCellIndex = 0, + .servCellIndex = RRC_PCELL_INDEX, }; req.plmn = malloc_or_fail(sizeof(*req.plmn)); *req.plmn = rrc->configuration.plmn[0]; + nr_rrc_cell_container_t *cell = rrc_get_pcell_for_ue(rrc, ue_p); + DevAssert(cell != NULL); req.nr_cellid = malloc_or_fail(sizeof(*req.nr_cellid)); - *req.nr_cellid = rrc->nr_cellid; + *req.nr_cellid = cell->info.cell_id; req.drbs_rel = malloc_or_fail(sizeof(*req.drbs_rel)); req.drbs_rel_len = n_drb_to_release; memcpy(req.drbs_rel, drb_to_release, n_drb_to_release * sizeof(int)); @@ -1190,7 +1192,7 @@ static void cuup_notify_reestablishment(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue_p) * @param du DU container containing cell information for key derivation */ static void rrc_gNB_generate_RRCReestablishment(rrc_gNB_ue_context_t *ue_context_pP, const rnti_t old_rnti, - const nr_rrc_du_container_t *du) + const nr_rrc_cell_container_t *cell) { module_id_t module_id = 0; gNB_RRC_INST *rrc = RC.nrrrc[module_id]; @@ -1198,16 +1200,19 @@ static void rrc_gNB_generate_RRCReestablishment(rrc_gNB_ue_context_t *ue_context uint8_t buffer[NR_RRC_BUF_SIZE] = {0}; uint8_t xid = rrc_gNB_get_next_transaction_identifier(module_id); ue_p->xids[xid] = RRC_REESTABLISH; - const f1ap_served_cell_info_t *cell_info = &du->setup_req->cell[0].info; - uint32_t ssb_arfcn = get_ssb_arfcn(du); - LOG_I(NR_RRC, "Reestablishment update key pci=%d, earfcn_dl=%u\n", cell_info->nr_pci, ssb_arfcn); + + nr_rrc_cell_container_t *pcell = rrc_get_pcell_for_ue(rrc, ue_p); + DevAssert(pcell != NULL); + DevAssert(pcell->info.cell_id == cell->info.cell_id); + uint32_t ssb_arfcn = get_ssb_arfcn(pcell); + LOG_I(NR_RRC, "Reestablishment update key pci=%d, earfcn_dl=%u\n", pcell->info.pci, ssb_arfcn); /* Per TS 33.501 6.9.2.3.4: When sending RRCReestablishment with nextHopChainingCount, * we're staying at the same NCC level (not advancing), so use horizontal derivation * from the currently active KgNB. Vertical derivation (from NH) is only used when * advancing to a new NCC level, which happens during handover or masterKeyUpdate. */ const uint8_t *base_key = ue_p->kgnb; - nr_derive_key_ng_ran_star(cell_info->nr_pci, ssb_arfcn, base_key, ue_p->kgnb); + nr_derive_key_ng_ran_star(pcell->info.pci, ssb_arfcn, base_key, ue_p->kgnb); int size = do_RRCReestablishment(ue_context_pP->ue_context.nh_ncc, buffer, NR_RRC_BUF_SIZE, xid); LOG_A(NR_RRC, "Send RRCReestablishment [%d bytes] to RNTI %04x\n", size, ue_p->rnti); @@ -1282,13 +1287,13 @@ static void rrc_gNB_process_RRCReestablishmentComplete(gNB_RRC_INST *rrc, /** Handle UE access on a different DU than the original one * Per 38.401 8.7: "If the UE accessed from a gNB-DU other than the original * one, the gNB-CU should trigger the UE Context Setup procedure". */ - /* UE is accessing a different DU - trigger UE Context Setup per TS 38.401 §8.7 */ - nr_rrc_du_container_t *du = get_du_by_assoc_id(rrc, assoc_id); - if (du == NULL) { - LOG_E(NR_RRC, "UE %d: cannot find DU for assoc_id %d\n", ue_p->rrc_ue_id, assoc_id); + nr_rrc_cell_container_t *cell = rrc_get_pcell_for_ue(rrc, ue_p); + // UE should have a PCell set at this point (set when RRCReestablishmentRequest was handled) + if (cell == NULL) { + LOG_E(NR_RRC, "UE %d: no PCell for reestablishment on different DU\n", ue_p->rrc_ue_id); return; } - rrc_f1_ue_context_setup_for_target_du(rrc, ue_p, du, NULL); + rrc_f1_ue_context_setup_for_target_du(rrc, ue_p, cell, NULL); } else { // Request DU to provide CellGroupConfig with reestablishRLC flags for re-establishment // According to TS 38.473 transparency requirements, CU should not construct or re-encode CellGroupConfig @@ -1404,9 +1409,17 @@ static void rrc_handle_RRCSetupRequest(gNB_RRC_INST *rrc, byte_array_t cgc = {.buf = msg->du2cu_rrc_container, .len = msg->du2cu_rrc_container_length}; store_cgc(UE, &cgc); UE->establishment_cause = rrcSetupRequest->establishmentCause; - UE->nr_cellid = msg->nr_cellid; + // Add PCell to serving_cells array + nr_rrc_cell_container_t *cell = get_cell_by_cell_id(&rrc->cells, msg->nr_cellid); + if (!cell) { + LOG_E(NR_RRC, "Cell %ld not found, rejecting UE\n", msg->nr_cellid); + rrc_gNB_generate_RRCReject(rrc, ue_context_p); + return; + } + /* Add PCell to serving_cells array */ + rrc_add_ue_serving_cell(UE, cell, RRC_PCELL_INDEX); UE->ongoing_reconfiguration = false; - UE->measConfig = nr_rrc_get_measconfig(rrc, UE->nr_cellid); + UE->measConfig = nr_rrc_get_measconfig(rrc, msg->nr_cellid); activate_srb(UE, 1); rrc_gNB_generate_RRCSetup(0, msg->crnti, ue_context_p, msg->du2cu_rrc_container, msg->du2cu_rrc_container_length); } @@ -1434,7 +1447,8 @@ static rrc_gNB_ue_context_t *rrc_gNB_get_ue_context_source_cell(gNB_RRC_INST *rr if (!ue->ho_context || !ue->ho_context->source) continue; nr_ho_source_cu_t *source_ctx = ue->ho_context->source; - if (source_ctx->old_rnti == rntiP && source_ctx->du->setup_req->cell[0].info.nr_pci == pci) + const nr_rrc_cell_container_t *source_cell = source_ctx->cell; + if (source_cell && source_ctx->old_rnti == rntiP && source_cell->info.pci == pci) return ue_context_p; } return NULL; @@ -1454,6 +1468,28 @@ static void f1u_dl_gtp_rollback(gNB_RRC_UE_t *UE) } } +/** @brief Resolve previous/old cell (UE identity physCellId): same cell, else neighbor + * of current cell, else any cell with that PCI in this DU. */ +static const nr_rrc_cell_container_t *get_previous_cell_by_pci_in_du(gNB_RRC_INST *rrc, + const nr_rrc_cell_container_t *current_cell, + const nr_rrc_du_container_t *du, + uint16_t pci) +{ + if (current_cell->info.pci == pci) + return current_cell; + + const neighbour_cell_configuration_t *neigh_config = get_neighbour_cell_config(rrc, current_cell->info.cell_id); + if (neigh_config != NULL) { + const nr_neighbour_cell_t *neighbour = get_neighbour_cell_by_pci(neigh_config, pci); + if (neighbour != NULL) { + nr_rrc_cell_container_t *c = get_cell_by_cell_id(&rrc->cells, neighbour->nrcell_id); + if (c != NULL && c->assoc_id == du->assoc_id) + return c; + } + } + return rrc_get_cell_by_pci_for_du(&du->cells, pci); +} + static void rrc_handle_RRCReestablishmentRequest(gNB_RRC_INST *rrc, sctp_assoc_t assoc_id, const NR_RRCReestablishmentRequest_IEs_t *req, @@ -1484,15 +1520,17 @@ static void rrc_handle_RRCReestablishmentRequest(gNB_RRC_INST *rrc, goto fallback_rrc_setup; } - if (du->mib == NULL || du->sib1 == NULL) { - /* we don't have MIB/SIB1 of the DU, and therefore cannot generate the - * Reestablishment (as we would need the SSB's ARFCN, which we cannot - * compute). So generate RRC Setup instead */ - LOG_E(NR_RRC, "Reestablishment request: no MIB/SIB1 of DU present, cannot do reestablishment, force setup request\n"); - goto fallback_rrc_setup; + // Fetch current cell: where the reestablishment request was received + nr_rrc_cell_container_t *current_cell = get_cell_by_cell_id(&rrc->cells, msg->nr_cellid); + if (current_cell == NULL || current_cell->assoc_id != du->assoc_id) { + LOG_E(NR_RRC, + "received CCCH message, but no corresponding cell found for nr_cellid %ld in DU (assoc_id %d)\n", + msg->nr_cellid, + assoc_id); + return; } - if (du->mtc == NULL) { + if (current_cell->mtc == NULL) { // some UEs don't send MeasurementTimingConfiguration, so we don't know the // SSB ARFCN and can't do reestablishment. handle it gracefully by doing // RRC setup procedure instead @@ -1501,7 +1539,12 @@ static void rrc_handle_RRCReestablishmentRequest(gNB_RRC_INST *rrc, goto fallback_rrc_setup; } - // Try to find UE context by RNTI and assoc_id (normal case: same DU) + const nr_rrc_cell_container_t *cell = get_previous_cell_by_pci_in_du(rrc, current_cell, du, (uint16_t)physCellId); + if (cell == NULL) { + LOG_E(NR_RRC, "received CCCH message, but no corresponding cell found for PCI %ld in DU (assoc_id %d)\n", physCellId, assoc_id); + return; + } + ue_context_p = rrc_gNB_get_ue_context_by_rnti(rrc, assoc_id, old_rnti); if (ue_context_p == NULL) { // Fallback 1: Try to find UE by RNTI only (re-establishment on different DU scenario) @@ -1524,14 +1567,12 @@ static void rrc_handle_RRCReestablishmentRequest(gNB_RRC_INST *rrc, goto fallback_rrc_setup; } - /* should check phys cell ID to identify the correct cell */ - const f1ap_served_cell_info_t *cell_info = &du->setup_req->cell[0].info; - const f1ap_served_cell_info_t *previous_cell_info = get_cell_information_by_phycellId(physCellId); f1_ue_data_t ue_data = cu_get_f1_ue_data(UE->rrc_ue_id); nr_ho_source_cu_t *source_ctx = UE->ho_context ? UE->ho_context->source : NULL; - DevAssert(!source_ctx || source_ctx->du->setup_req->num_cells_available == 1); - bool ho_reestab_on_source = source_ctx && previous_cell_info->nr_cellid == source_ctx->du->setup_req->cell[0].info.nr_cellid; + DevAssert(!source_ctx || source_ctx->cell != NULL); + const nr_rrc_cell_container_t *source_cell = source_ctx ? source_ctx->cell : NULL; + bool ho_reestab_on_source = source_cell ? cell->info.cell_id == source_cell->info.cell_id : false; if (ho_reestab_on_source) { /* the UE came back on the source DU while doing handover, release at @@ -1551,11 +1592,11 @@ static void rrc_handle_RRCReestablishmentRequest(gNB_RRC_INST *rrc, store_cgc(UE, &source_ctx->old_cgc); /* update to old DU assoc id -- RNTI + secondary DU UE ID further below */ - ue_data.du_assoc_id = source_ctx->du->assoc_id; + ue_data.du_assoc_id = source_cell->assoc_id; bool success = cu_update_f1_ue_data(UE->rrc_ue_id, &ue_data); DevAssert(success); nr_rrc_finalize_ho(UE); - } else if (physCellId != cell_info->nr_pci) { + } else if (physCellId != cell->info.pci) { /* Check if this is a different DU scenario or "too fast movement" scenario */ if (assoc_id != ue_data.du_assoc_id) { /* Different DU scenario - physCellId differs because UE is re-establishing on a different DU. @@ -1567,7 +1608,7 @@ static void rrc_handle_RRCReestablishmentRequest(gNB_RRC_INST *rrc, "UE %d: Re-establishment on different DU (physCellId %ld from old cell != %d from new DU, du_assoc_id %d -> %d)\n", UE->rrc_ue_id, physCellId, - cell_info->nr_pci, + cell->info.pci, ue_data.du_assoc_id, assoc_id); @@ -1606,7 +1647,7 @@ static void rrc_handle_RRCReestablishmentRequest(gNB_RRC_INST *rrc, LOG_I(NR_RRC, "RRC Reestablishment Request from different physCellId (%ld) than current physCellId (%d), fallback to RRC setup\n", physCellId, - cell_info->nr_pci); + cell->info.pci); ngap_cause = NGAP_CAUSE_RADIO_NETWORK_RELEASE_DUE_TO_NGRAN_GENERATED_REASON; goto fallback_rrc_setup; } @@ -1616,12 +1657,24 @@ static void rrc_handle_RRCReestablishmentRequest(gNB_RRC_INST *rrc, // update with new RNTI, and update secondary UE association UE->rnti = msg->crnti; - UE->nr_cellid = msg->nr_cellid; + + /* Update PCell in serving_cells array */ + DevAssert(cell->info.cell_id == msg->nr_cellid); + // Reset the serving set to the new PCell, remove all serving cells from the current PCell's DU. + const ue_serving_cell_t *existing_pcell = ue_get_pcell_entry(UE); + DevAssert(existing_pcell != NULL); + rrc_remove_ue_scells_from_du(UE, existing_pcell->assoc_id); + ue_serving_cell_t *added = rrc_add_ue_serving_cell(UE, cell, RRC_PCELL_INDEX); + if (added == NULL) { + LOG_E(NR_RRC, "Reestablishment: failed to add PCell (cell %ld), rejecting reestablishment\n", cell->info.cell_id); + goto fallback_rrc_setup; + } + ue_data.secondary_ue = msg->gNB_DU_ue_id; bool success = cu_update_f1_ue_data(UE->rrc_ue_id, &ue_data); DevAssert(success); - rrc_gNB_generate_RRCReestablishment(ue_context_p, old_rnti, du); + rrc_gNB_generate_RRCReestablishment(ue_context_p, old_rnti, cell); return; fallback_rrc_setup: @@ -1713,13 +1766,34 @@ static void process_Event_Based_Measurement_Report(gNB_RRC_INST *rrc, neighbour_pci, neighbourCellRSRP); - const f1ap_served_cell_info_t *neigh_cell = get_cell_information_by_phycellId(neighbour_pci); - const f1ap_served_cell_info_t *serving_cell = get_cell_information_by_phycellId(scell_pci); - const neighbour_cell_configuration_t *cell = get_neighbour_cell_config(rrc, serving_cell->nr_cellid); + // Resolve source cell by PCI from UE's serving cells + const nr_rrc_cell_container_t *source_cell = NULL; + FOR_EACH_SEQ_ARR (ue_serving_cell_t *, scell, &ue->serving_cells) { + nr_rrc_cell_container_t *cell_by_id = get_cell_by_cell_id(&rrc->cells, scell->nci); + if (cell_by_id != NULL && cell_by_id->info.pci == scell_pci) { + source_cell = cell_by_id; + break; + } + } + if (source_cell == NULL) { + LOG_W(NR_RRC, + "UE %d: received measurement report but source cell with PCI %d not found in UE's serving cells, skipping\n", + ue->rrc_ue_id, + scell_pci); + continue; + } + + // Resolve target cell via neighbour configuration + const neighbour_cell_configuration_t *cell = get_neighbour_cell_config(rrc, source_cell->info.cell_id); const nr_neighbour_cell_t *neighbour = get_neighbour_cell_by_pci(cell, neighbour_pci); + const nr_rrc_cell_container_t *target_cell = NULL; + if (neighbour != NULL) { + // Resolve target cell by cell_id from neighbour config (may be NULL if no F1 connection) + target_cell = get_cell_by_cell_id(&rrc->cells, neighbour->nrcell_id); + } // CU does not have f1 connection with neighbour cell context. So check does serving cell has this phyCellId as a // neighbour. - if (!neigh_cell && neighbour) { + if (!target_cell && neighbour) { // No F1 connection but static neighbour configuration is available const nr_a3_event_t *a3_event_configuration = get_a3_configuration(rrc, neighbour->physicalCellId); /* if no A3 event configured for this physical cell ID, try to get the default one */ @@ -1738,12 +1812,9 @@ static void process_Event_Based_Measurement_Report(gNB_RRC_INST *rrc, } LOG_D(NR_RRC, "HO LOG: Trigger N2 HO for the neighbour gnb: %u cell: %lu\n", neighbour->gNB_ID, neighbour->nrcell_id); } - } else if (neigh_cell && neighbour) { + } else if (target_cell && neighbour) { /* we know the cell and are connected to the DU! */ - nr_rrc_du_container_t *source_du = get_du_by_cell_id(rrc, serving_cell->nr_cellid); - DevAssert(source_du); - nr_rrc_du_container_t *target_du = get_du_by_cell_id(rrc, neigh_cell->nr_cellid); - nr_rrc_trigger_f1_ho(rrc, ue, source_du, target_du); + nr_rrc_trigger_f1_ho(rrc, ue, source_cell, target_cell); } else { LOG_W(NR_RRC, "UE %d: received A3 event for stronger neighbor PCI %d, but no such neighbour in configuration\n", ue->rrc_ue_id, neighbour_pci); } @@ -1963,7 +2034,7 @@ static void handle_ueCapabilityInformation(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, * up security and request capabilities, so trigger PDU sessions now. The * UE NAS message will be forwarded in the corresponding reconfiguration, * the Initial context setup response after reconfiguration complete. */ - if (!trigger_bearer_setup(rrc, UE, UE->n_initial_pdu, UE->initial_pdus, 0)) { + if (!trigger_bearer_setup(rrc, UE, UE->n_initial_pdu, UE->initial_pdus, UE->ambr.dl_br)) { LOG_W(NR_RRC, "Failed to setup bearers for UE %d: send Initial Context Setup Response\n", UE->rrc_ue_id); rrc_gNB_send_NGAP_INITIAL_CONTEXT_SETUP_RESP(rrc, UE); rrc_forward_ue_nas_message(rrc, UE); @@ -2213,7 +2284,7 @@ static int rrc_gNB_decode_dcch(gNB_RRC_INST *rrc, const f1ap_ul_rrc_message_t *m * to set up security, so trigger PDU sessions now. The UE NAS * message will be forwarded in the corresponding reconfiguration, * the Initial context setup response after reconfiguration complete. */ - if (!trigger_bearer_setup(rrc, UE, UE->n_initial_pdu, UE->initial_pdus, 0)) { + if (!trigger_bearer_setup(rrc, UE, UE->n_initial_pdu, UE->initial_pdus, UE->ambr.dl_br)) { LOG_W(NR_RRC, "Failed to setup bearers for UE %d: send Initial Context Setup Response\n", UE->rrc_ue_id); rrc_gNB_send_NGAP_INITIAL_CONTEXT_SETUP_RESP(rrc, UE); rrc_forward_ue_nas_message(rrc, UE); @@ -2612,12 +2683,15 @@ static void rrc_CU_process_ue_context_release_request(MessageDef *msg_p, sctp_as gNB_RRC_UE_t *UE = &ue_context_p->ue_context; if (UE->ho_context != NULL) { nr_ho_source_cu_t *source_ctx = UE->ho_context->source; - bool from_source_du = source_ctx && source_ctx->du->assoc_id == assoc_id; + bool from_source_du = source_ctx && source_ctx->cell->assoc_id == assoc_id; if (from_source_du) { // we received release request from the source DU, but HO is still // ongoing; free the UE, and remove the HO context. - LOG_W(NR_RRC, "UE %d: release request from source DU ID %ld during HO, marking HO as complete\n", UE->rrc_ue_id, source_ctx->du->setup_req->gNB_DU_id); - RETURN_IF_INVALID_ASSOC_ID(source_ctx->du->assoc_id); + LOG_W(NR_RRC, + "UE %d: release request from source cell PCI %d during HO, marking HO as complete\n", + UE->rrc_ue_id, + source_ctx->cell->info.pci); + RETURN_IF_INVALID_ASSOC_ID(source_ctx->cell->assoc_id); f1ap_ue_context_rel_cmd_t cmd = { .gNB_CU_ue_id = UE->rrc_ue_id, .gNB_DU_ue_id = source_ctx->du_ue_id, @@ -2663,6 +2737,7 @@ static void rrc_delete_ue_data(gNB_RRC_UE_t *UE) UE->redcap_cap = NULL; seq_arr_free(&UE->pduSessions, free_pdusession); seq_arr_free(&UE->drbs, free_drb); + seq_arr_free(&UE->serving_cells, NULL); } void rrc_remove_ue(gNB_RRC_INST *rrc, rrc_gNB_ue_context_t *ue_context_p) @@ -2744,6 +2819,10 @@ static void rrc_CU_process_ue_context_modification_response(MessageDef *msg_p, i nr_ho_source_cu_t *source_ctx = UE->ho_context->source; DevAssert(source_ctx->old_rnti == UE->rnti); nr_rrc_apply_target_context(UE); + /* Update cell association after handover */ + if (!nr_rrc_update_cell_assoc_after_ho(rrc, UE)) { + return; + } } } @@ -2765,7 +2844,9 @@ static void rrc_CU_process_ue_modification_required(MessageDef *msg_p, instance_ } gNB_RRC_UE_t *UE = &ue_context_p->ue_context; - if (UE->ho_context && UE->ho_context->source && UE->ho_context->source->du && UE->ho_context->source->du->assoc_id == assoc_id) { + bool on_source_du = UE->ho_context && UE->ho_context->source && UE->ho_context->source->cell + && UE->ho_context->source->cell->assoc_id == assoc_id; + if (on_source_du) { LOG_W(NR_RRC, "UE %d: UE Context Modification Required during handover, ignoring message\n", UE->rrc_ue_id); return; } @@ -2852,6 +2933,104 @@ f1ap_qos_flow_param_t get_qos_char_from_qos_flow_param(const pdusession_level_qo return qos_char; } +static f1ap_drb_info_nr_t fill_f1_drb_info_nr(pdusession_t *pdu, const uint8_t *qfis, const int num_qfis) +{ + f1ap_drb_info_nr_t drb_info = {0}; + drb_info.nssai = pdu->nssai; + drb_info.flows_len = num_qfis; + drb_info.flows = calloc_or_fail(num_qfis, sizeof(*drb_info.flows)); + for (int i = 0; i < num_qfis; i++) { + int qfi = qfis[i]; + /* find the stored QoS flow parameters for this QFI */ + nr_rrc_qos_t *qos_param = find_qos(&pdu->qos, qfi); + DevAssert(qos_param); + /* QoS flows Flows Mapped to DRB Item */ + f1ap_drb_flows_mapped_t *flow = &drb_info.flows[i]; + flow->qfi = qfi; + flow->param = get_qos_char_from_qos_flow_param(&qos_param->qos); + } + /* DRB QoS IE: we just reuse the ones from the first flow */ + drb_info.drb_qos = drb_info.flows[0].param; + return drb_info; +} + +/** @brief Fill F1 DRB to Be Setup List (UE Context Setup Request) + * @param rrc RRC instance + * @param rrc_drb RRC DRB structure + * @param pdu PDU session parameters + * @param qfis List of QFIs + * @param num_qfis Number of QFIs in list + * @return Filled F1 DRB to setup structure */ +static f1ap_drb_to_setup_t fill_f1_drb_to_be_setup(const gNB_RRC_INST *rrc, + const drb_t *rrc_drb, + rrc_pdu_session_param_t *pdu, + const uint8_t *qfis, + int num_qfis) +{ + DevAssert(rrc); + DevAssert(rrc_drb); + DevAssert(pdu); + DevAssert(qfis); + DevAssert(num_qfis > 0); + DevAssert(num_qfis <= MAX_QOS_FLOWS); + + f1ap_drb_to_setup_t drb = {0}; + + drb.id = rrc_drb->drb_id; + drb.qos_choice = F1AP_QOS_CHOICE_NR; + /* DRB Info (including QoS flows) */ + drb.nr = fill_f1_drb_info_nr(&pdu->param, qfis, num_qfis); + + /* tunnel info */ + if (rrc_drb->cuup_tunnel_config.addr.length != sizeof(in_addr_t)) { + LOG_W(RRC, + "IPv4 address expected (length == %zu), got length %d\n", + sizeof(in_addr_t), + rrc_drb->cuup_tunnel_config.addr.length); + } + memcpy(&drb.up_ul_tnl[0].tl_address, rrc_drb->cuup_tunnel_config.addr.buffer, sizeof(in_addr_t)); + drb.up_ul_tnl[0].teid = rrc_drb->cuup_tunnel_config.teid; + drb.up_ul_tnl_len = 1; + + /* RLC mode */ + drb.rlc_mode = rrc->configuration.um_on_default_drb ? F1AP_RLC_MODE_UM_BIDIR : F1AP_RLC_MODE_AM; + + /* PDCP SN length */ + const nr_pdcp_configuration_t *pdcp = &rrc_drb->pdcp_config; + DevAssert(pdcp->drb.sn_size == 18 || pdcp->drb.sn_size == 12); + drb.dl_pdcp_sn_len = malloc_or_fail(sizeof(*drb.dl_pdcp_sn_len)); + *drb.dl_pdcp_sn_len = pdcp->drb.sn_size == 18 ? F1AP_PDCP_SN_18B : F1AP_PDCP_SN_12B; + drb.ul_pdcp_sn_len = malloc_or_fail(sizeof(*drb.ul_pdcp_sn_len)); + *drb.ul_pdcp_sn_len = pdcp->drb.sn_size == 18 ? F1AP_PDCP_SN_18B : F1AP_PDCP_SN_12B; + + return drb; +} + +/** @brief Helper function to process DRBs from a PDU session's DRB setup list */ +static int fill_f1_drbs_from_e1(const gNB_RRC_INST *rrc, + gNB_RRC_UE_t *UE, + rrc_pdu_session_param_t *pdu, + const DRB_nGRAN_setup_t *drb_list, + const int drb_list_len, + f1ap_drb_to_setup_t drbs[32]) +{ + int nb_drb = 0; + for (int i = 0; i < drb_list_len; i++) { + const DRB_nGRAN_setup_t *drb_config = &drb_list[i]; + drb_t *rrc_drb = get_drb(&UE->drbs, drb_config->id); + DevAssert(rrc_drb); + int nb_qos_flows = drb_config->numQosFlowSetup; + DevAssert(nb_qos_flows <= MAX_QOS_FLOWS); + AssertFatal(nb_qos_flows > 0, "must map at least one flow to a DRB\n"); + uint8_t qfis[MAX_QOS_FLOWS] = {0}; + for (int j = 0; j < nb_qos_flows; j++) + qfis[j] = drb_config->qosFlows[j].qfi; + drbs[nb_drb++] = fill_f1_drb_to_be_setup(rrc, rrc_drb, pdu, qfis, nb_qos_flows); + } + DevAssert(nb_drb < MAX_DRBS_PER_UE); + return nb_drb; +} + /* \brief fills a list of DRBs to be setup from a number of PDU sessions in E1 * bearer setup response */ static int fill_drb_to_be_setup_from_e1_resp(const gNB_RRC_INST *rrc, @@ -2862,55 +3041,12 @@ static int fill_drb_to_be_setup_from_e1_resp(const gNB_RRC_INST *rrc, { int nb_drb = 0; for (int p = 0; p < numPduSession; ++p) { - rrc_pdu_session_param_t *pdu = find_pduSession(&UE->pduSessions, pduSession[p].id); + const pdu_session_setup_t *session = &pduSession[p]; + rrc_pdu_session_param_t *pdu = find_pduSession(&UE->pduSessions, session->id); DevAssert(pdu); - for (int i = 0; i < pduSession[p].numDRBSetup; i++) { - const DRB_nGRAN_setup_t *drb_config = &pduSession[p].DRBnGRanList[i]; - drb_t *rrc_drb = get_drb(&UE->drbs, pduSession[p].DRBnGRanList[i].id); - nr_pdcp_configuration_t pdcp = rrc_drb->pdcp_config; - DevAssert(rrc_drb); - - DevAssert(nb_drb < MAX_DRBS_PER_UE); - f1ap_drb_to_setup_t *drb = &drbs[nb_drb]; - drb->id = rrc_drb->drb_id; - - drb->qos_choice = F1AP_QOS_CHOICE_NR; - - /* pass QoS info to MAC */ - int nb_qos_flows = drb_config->numQosFlowSetup; - AssertFatal(nb_qos_flows > 0, "must map at least one flow to a DRB\n"); - drb->nr.flows_len = nb_qos_flows; - drb->nr.flows = calloc_or_fail(nb_qos_flows, sizeof(*drb->nr.flows)); - for (int j = 0; j < nb_qos_flows; j++) { - int qfi = drb_config->qosFlows[j].qfi; - /* find the QoS characteristics stored at RRC based on QFI returned - * from E1. We expect this to correspond to a QFI we passed to E1 - * previously, so we expect it to exist. */ - nr_rrc_qos_t *qos_param = find_qos(&pdu->param.qos, qfi); - DevAssert(qos_param); - f1ap_drb_flows_mapped_t *flow = &drb->nr.flows[j]; - flow->qfi = qfi; - flow->param = get_qos_char_from_qos_flow_param(&qos_param->qos); - } - /* the DRB QoS parameters: we just reuse the ones from the first flow */ - drb->nr.drb_qos = drb->nr.flows[0].param; - - /* pass NSSAI info to MAC */ - drb->nr.nssai = pdu->param.nssai; - - drb->up_ul_tnl[0].tl_address = drb_config->UpParamList[0].tl_info.tlAddress; - drb->up_ul_tnl[0].teid = drb_config->UpParamList[0].tl_info.teId; - drb->up_ul_tnl_len = 1; - drb->rlc_mode = rrc->configuration.um_on_default_drb ? F1AP_RLC_MODE_UM_BIDIR : F1AP_RLC_MODE_AM; - DevAssert(pdcp.drb.sn_size == 18 || pdcp.drb.sn_size == 12); - drb->dl_pdcp_sn_len = malloc_or_fail(sizeof(*drb->dl_pdcp_sn_len)); - *drb->dl_pdcp_sn_len = pdcp.drb.sn_size == 18 ? F1AP_PDCP_SN_18B : F1AP_PDCP_SN_12B; - drb->ul_pdcp_sn_len = malloc_or_fail(sizeof(*drb->ul_pdcp_sn_len)); - *drb->ul_pdcp_sn_len = pdcp.drb.sn_size == 18 ? F1AP_PDCP_SN_18B : F1AP_PDCP_SN_12B; - - nb_drb++; - } + nb_drb += fill_f1_drbs_from_e1(rrc, UE, pdu, session->DRBnGRanList, session->numDRBSetup, drbs); } + DevAssert(nb_drb < MAX_DRBS_PER_UE); return nb_drb; } @@ -3261,7 +3397,7 @@ void *rrc_gnb_task(void *args_p) { break; case NGAP_PDUSESSION_MODIFY_REQ: - rrc_gNB_process_NGAP_PDUSESSION_MODIFY_REQ(msg_p, instance); + rrc_gNB_process_NGAP_PDUSESSION_MODIFY_REQ(&NGAP_PDUSESSION_MODIFY_REQ(msg_p), instance); break; case NGAP_PDUSESSION_RELEASE_COMMAND: @@ -3554,39 +3690,23 @@ static int rrc_fill_f1_drb_to_setup(const gNB_RRC_INST *rrc, const gNB_RRC_UE_t FOR_EACH_SEQ_ARR(drb_t *, rrc_drb, &ue->drbs) { DevAssert(nb_drb < MAX_DRBS_PER_UE); - f1ap_drb_to_setup_t *drb = &drbs[nb_drb]; - nr_pdcp_configuration_t *pdcp = &rrc_drb->pdcp_config; - nb_drb++; /* fetch an existing PDU session for this DRB */ rrc_pdu_session_param_t *pdu = find_pduSession_from_drbId((gNB_RRC_UE_t *)ue, rrc_drb->drb_id); AssertFatal(pdu != NULL, "no PDU session for DRB ID %d\n", rrc_drb->drb_id); - drb->id = rrc_drb->drb_id; + /* Collect all QFIs mapped to this DRB */ + uint8_t qfis[MAX_QOS_FLOWS] = {0}; + int num_qfis = 0; + FOR_EACH_SEQ_ARR (nr_rrc_qos_t *, qos, &pdu->param.qos) { + if (qos->drb_id == rrc_drb->drb_id) { + DevAssert(num_qfis < MAX_QOS_FLOWS); + qfis[num_qfis++] = qos->qos.qfi; + } + } + AssertFatal(num_qfis > 0, "no QoS flows mapped to DRB ID %d\n", rrc_drb->drb_id); - drb->qos_choice = F1AP_QOS_CHOICE_NR; - drb->nr.nssai = pdu->param.nssai; - drb->nr.flows_len = 1; - drb->nr.flows = calloc_or_fail(1, sizeof(*drb->nr.flows)); - - /* QoS flow associated with this DRB: use first QoS flow */ - AssertFatal(seq_arr_size(&pdu->param.qos) == 1, "only 1 Qos flow supported\n"); - nr_rrc_qos_t *qos_param = (nr_rrc_qos_t *)seq_arr_at(&pdu->param.qos, 0); - DevAssert(qos_param->qos.qfi > 0); - drb->nr.flows[0].qfi = qos_param->qos.qfi; - drb->nr.flows[0].param = get_qos_char_from_qos_flow_param(&qos_param->qos); - /* the DRB QoS parameters: reuse the ones from the first flow */ - drb->nr.drb_qos = drb->nr.flows[0].param; - - memcpy(&drb->up_ul_tnl[0].tl_address, &rrc_drb->cuup_tunnel_config.addr.buffer, sizeof(uint8_t) * 4); - drb->up_ul_tnl[0].teid = rrc_drb->cuup_tunnel_config.teid; - drb->up_ul_tnl_len = 1; - - drb->rlc_mode = rrc->configuration.um_on_default_drb ? F1AP_RLC_MODE_UM_BIDIR : F1AP_RLC_MODE_AM; - DevAssert(pdcp->drb.sn_size == 18 || pdcp->drb.sn_size == 12); - drb->dl_pdcp_sn_len = malloc_or_fail(sizeof(*drb->dl_pdcp_sn_len)); - *drb->dl_pdcp_sn_len = pdcp->drb.sn_size == 18 ? F1AP_PDCP_SN_18B : F1AP_PDCP_SN_12B; - drb->ul_pdcp_sn_len = malloc_or_fail(sizeof(*drb->ul_pdcp_sn_len)); - *drb->ul_pdcp_sn_len = pdcp->drb.sn_size == 18 ? F1AP_PDCP_SN_18B : F1AP_PDCP_SN_12B; + /* Use the common function to fill DRB setup */ + drbs[nb_drb++] = fill_f1_drb_to_be_setup(rrc, rrc_drb, pdu, qfis, num_qfis); } return nb_drb; } @@ -3603,14 +3723,14 @@ static int rrc_fill_f1_drb_to_setup(const gNB_RRC_INST *rrc, const gNB_RRC_UE_t * - Handover preparation information (if provided) * @param rrc RRC instance * @param ue UE context - * @param du Target DU container containing cell and setup information + * @param cell Target cell container containing cell information * @param ho_prep_info Optional handover preparation information */ void rrc_f1_ue_context_setup_for_target_du(const gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, - const nr_rrc_du_container_t *du, + const nr_rrc_cell_container_t *cell, const byte_array_t *ho_prep_info) { - DevAssert(du != NULL); + DevAssert(cell != NULL); /* Handle two scenarios: * 1. Handover (ho_prep_info provided): Prepare and copy handover preparation information * for the target DU. The target DU will assign a new gNB-DU UE ID in the response. @@ -3632,7 +3752,7 @@ void rrc_f1_ue_context_setup_for_target_du(const gNB_RRC_INST *rrc, du_ue_id = ue_data.secondary_ue; opt_du_ue_id = &du_ue_id; } - LOG_I(NR_RRC, "Triggering UE Context Setup for UE %d on DU %d\n", ue->rrc_ue_id, du->assoc_id); + LOG_I(NR_RRC, "Triggering UE Context Setup for UE %d on DU %d\n", ue->rrc_ue_id, cell->assoc_id); int nb_srb = 2; f1ap_srb_to_setup_t *srbs = calloc_or_fail(nb_srb, sizeof(*srbs)); f1ap_drb_to_setup_t *drbs = calloc_or_fail(MAX_DRBS_PER_UE, sizeof(*drbs)); @@ -3646,12 +3766,12 @@ void rrc_f1_ue_context_setup_for_target_du(const gNB_RRC_INST *rrc, /* Update measurement config for target DU */ free_MeasConfig(ue->measConfig); - ue->measConfig = nr_rrc_get_measconfig(rrc, du->setup_req->cell[0].info.nr_cellid); + ue->measConfig = nr_rrc_get_measconfig(rrc, cell->info.cell_id); byte_array_t *meas_config = calloc_or_fail(1, sizeof(*meas_config)); meas_config->buf = calloc_or_fail(1, NR_RRC_BUF_SIZE); meas_config->len = do_NR_MeasConfig(ue->measConfig, meas_config->buf, NR_RRC_BUF_SIZE); /* Fill common fields */ - f1ap_ue_context_setup_req_t req = rrc_fill_f1_ue_context_setup(ue, du, opt_du_ue_id); + f1ap_ue_context_setup_req_t req = rrc_fill_f1_ue_context_setup(ue, cell, opt_du_ue_id); /* Fill target DU specific fields */ req.srbs_len = 2; req.srbs = srbs; @@ -3660,8 +3780,8 @@ void rrc_f1_ue_context_setup_for_target_du(const gNB_RRC_INST *rrc, req.cu_to_du_rrc_info.meas_config = meas_config; req.cu_to_du_rrc_info.ho_prep_info = hpi; - RETURN_IF_INVALID_ASSOC_ID(du->assoc_id); - rrc->mac_rrc.ue_context_setup_request(du->assoc_id, &req); + RETURN_IF_INVALID_ASSOC_ID(cell->assoc_id); + rrc->mac_rrc.ue_context_setup_request(cell->assoc_id, &req); free_ue_context_setup_req(&req); } @@ -3686,7 +3806,8 @@ void rrc_f1_ue_context_setup_from_e1_response(const gNB_RRC_INST *rrc, AssertFatal(!NODE_IS_DU(rrc->node_type), "illegal node type DU!\n"); - nr_rrc_du_container_t *du = get_du_for_ue((gNB_RRC_INST *)rrc, ue_p->rrc_ue_id); + nr_rrc_cell_container_t *cell = rrc_get_pcell_for_ue((gNB_RRC_INST *)rrc, ue_p); + DevAssert(cell != NULL); int nb_srb = 1; f1ap_srb_to_setup_t *srbs = calloc_or_fail(nb_srb, sizeof(*srbs)); @@ -3703,7 +3824,7 @@ void rrc_f1_ue_context_setup_from_e1_response(const gNB_RRC_INST *rrc, const uint32_t du_ue_id = ue_data.secondary_ue; /* Fill common fields */ - f1ap_ue_context_setup_req_t ue_context_setup_req = rrc_fill_f1_ue_context_setup(ue_p, du, &du_ue_id); + f1ap_ue_context_setup_req_t ue_context_setup_req = rrc_fill_f1_ue_context_setup(ue_p, cell, &du_ue_id); /* Fill bearers information for serving DU */ ue_context_setup_req.srbs_len = nb_srb; ue_context_setup_req.srbs = srbs; diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.c b/openair2/RRC/NR/rrc_gNB_NGAP.c index bf8a797a49..8ebd0b0a37 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.c +++ b/openair2/RRC/NR/rrc_gNB_NGAP.c @@ -90,7 +90,9 @@ #include "uper_encoder.h" #include "rrc_gNB_mobility.h" #include "rrc_gNB_du.h" +#include "rrc_cell_management.h" #include "common/utils/alg/find.h" +#include "common/utils/nr/nr_common.h" #ifdef E2_AGENT #include "openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc_extern.h" @@ -254,8 +256,9 @@ void rrc_gNB_send_NGAP_NAS_FIRST_REQ(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, NR_RRC /* UE is connected to only one PLMN at a time: store this as the serving PLMN */ UE->serving_plmn = req->plmn; - // Cell ID (NR CGI) - req->nr_cell_id = UE->nr_cellid; + nr_rrc_cell_container_t *cell = rrc_get_pcell_for_ue(rrc, UE); + DevAssert(cell); + req->nr_cell_id = cell->info.cell_id; // PLMN (NR CGI and TAI) plmn_id_t *p = &req->plmn; LOG_I(NGAP, "Selected PLMN in the NG Initial UE Message: MCC=%03d MNC=%0*d\n", p->mcc, p->mnc_digit_length, p->mnc); @@ -289,6 +292,31 @@ void rrc_gNB_send_NGAP_NAS_FIRST_REQ(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, NR_RRC itti_send_msg_to_task(TASK_NGAP, rrc->module_id, message_p); } +/// @brief set QoS Flows to Setup in E1 DRB To Setup List +static qos_flow_to_setup_t fill_e1_qos_flow_to_setup(const pdusession_level_qos_parameter_t *qos) +{ + qos_flow_to_setup_t qos_flow = { .qfi = qos->qfi }; + // ARP + ngran_allocation_retention_priority_t *arp_out = &qos_flow.qos_params.alloc_reten_priority; + const qos_arp_t *arp_in = &qos->arp; + arp_out->priority_level = arp_in->priority_level; + arp_out->preemption_capability = arp_in->pre_emp_capability; + arp_out->preemption_vulnerability = arp_in->pre_emp_vulnerability; + // QoS Characteristics + qos_characteristics_t *qos_characteristics = &qos_flow.qos_params.qos_characteristics; + if (qos->fiveQI_type == NON_DYNAMIC) { + qos_characteristics->non_dynamic.fiveqi = qos->fiveQI; + qos_characteristics->non_dynamic.qos_priority_level = qos->qos_priority; + } else { + qos_characteristics->dynamic.fiveqi = qos->fiveQI; + qos_characteristics->dynamic.qos_priority_level = qos->qos_priority; + // NOTE: missing packet error rate and delay budget + } + qos_characteristics->qos_type = qos->fiveQI_type; + + return qos_flow; +} + /** @brief Returns an instance of E1AP DRB To Setup List */ static DRB_nGRAN_to_setup_t fill_e1_drb_to_setup(const drb_t *rrc_drb, const pdusession_t *session, @@ -299,8 +327,8 @@ static DRB_nGRAN_to_setup_t fill_e1_drb_to_setup(const drb_t *rrc_drb, drb_ngran.id = rrc_drb->drb_id; drb_ngran.sdap_config.defaultDRB = true; - drb_ngran.sdap_config.sDAP_Header_UL = session->sdap_config.header_ul_absent ? 1 : 0; - drb_ngran.sdap_config.sDAP_Header_DL = session->sdap_config.header_dl_absent ? 1 : 0; + drb_ngran.sdap_config.sDAP_Header_UL = session->sdap_config.header_ul_absent ? false : true; + drb_ngran.sdap_config.sDAP_Header_DL = session->sdap_config.header_dl_absent ? false : true; drb_ngran.pdcp_config = set_bearer_context_pdcp_config(rrc_drb->pdcp_config, um_on_default_drb, redcap_cap); @@ -310,26 +338,13 @@ static DRB_nGRAN_to_setup_t fill_e1_drb_to_setup(const drb_t *rrc_drb, } FOR_EACH_SEQ_ARR(nr_rrc_qos_t *, qos, &session->qos) { - pdusession_level_qos_parameter_t *qos_session = &qos->qos; + if (qos->drb_id != drb_ngran.id) + continue; + const pdusession_level_qos_parameter_t *qos_session = &qos->qos; DevAssert(drb_ngran.numQosFlow2Setup < MAX_QOS_FLOWS); - qos_flow_to_setup_t *qos_flow = &drb_ngran.qosFlows[drb_ngran.numQosFlow2Setup++]; - qos_characteristics_t *qos_char = &qos_flow->qos_params.qos_characteristics; - qos_flow->qfi = qos_session->qfi; - qos_char->qos_type = qos_session->fiveQI_type; - if (qos_char->qos_type == DYNAMIC) { - qos_char->dynamic.fiveqi = qos_session->fiveQI; - qos_char->dynamic.qos_priority_level = qos_session->qos_priority; - } else { - qos_char->non_dynamic.fiveqi = qos_session->fiveQI; - qos_char->non_dynamic.qos_priority_level = qos_session->qos_priority; - } - - ngran_allocation_retention_priority_t *arp_out = &qos_flow->qos_params.alloc_reten_priority; - qos_arp_t *arp_in = &qos_session->arp; - arp_out->priority_level = arp_in->priority_level; - arp_out->preemption_capability = arp_in->pre_emp_capability; - arp_out->preemption_vulnerability = arp_in->pre_emp_vulnerability; + drb_ngran.qosFlows[drb_ngran.numQosFlow2Setup++] = fill_e1_qos_flow_to_setup(qos_session); } + DevAssert(drb_ngran.numQosFlow2Setup > 0); return drb_ngran; } @@ -355,6 +370,10 @@ static nr_sdap_configuration_t get_sdap_config(const bool enable_sdap) */ bool trigger_bearer_setup(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, pdusession_t *sessions, uint64_t ueAggMaxBitRateDownlink) { + if (ueAggMaxBitRateDownlink == UINT64_MAX) { + LOG_E(NR_RRC, "UE %d: UE aggregate maximum bitrate must be known by the NG-RAN node\n", UE->rrc_ue_id); + return false; + } AssertFatal(UE->as_security_active, "logic bug: security should be active when activating DRBs\n"); e1ap_bearer_setup_req_t bearer_req = {0}; @@ -485,12 +504,15 @@ int rrc_gNB_process_NGAP_INITIAL_CONTEXT_SETUP_REQ(MessageDef *msg_p, instance_t UE->nas_pdu = req->nas_pdu; if (req->nb_of_pdusessions > 0) { + AssertFatal(req->has_ue_ambr, "UE aggregate maximum bitrate is required when there are PDU sessions to setup"); /* if there are PDU sessions to setup, store them to be created once * security (and UE capabilities) are received */ UE->n_initial_pdu = req->nb_of_pdusessions; UE->initial_pdus = calloc_or_fail(UE->n_initial_pdu, sizeof(*UE->initial_pdus)); for (int i = 0; i < UE->n_initial_pdu; ++i) cp_pdusession_resource_item_to_pdusession(&UE->initial_pdus[i], &req->pdusession[i]); + UE->ambr.dl_br = req->ue_ambr.br_dl; + UE->ambr.ul_br = req->ue_ambr.br_ul; } /* security */ @@ -513,7 +535,7 @@ int rrc_gNB_process_NGAP_INITIAL_CONTEXT_SETUP_REQ(MessageDef *msg_p, instance_t // do not remove the above allocation which is reused here: this is used // in handle_rrcReconfigurationComplete() to know that we need to send a // Initial context setup response message - if (!trigger_bearer_setup(rrc, UE, UE->n_initial_pdu, UE->initial_pdus, 0)) { + if (!trigger_bearer_setup(rrc, UE, UE->n_initial_pdu, UE->initial_pdus, UE->ambr.dl_br)) { LOG_W(NR_RRC, "UE %d: reject PDU Session Setup in Initial Context Setup Response\n", UE->rrc_ue_id); ngap_cause_t cause = {.type = NGAP_CAUSE_RADIO_NETWORK, .value = NGAP_CAUSE_RADIO_NETWORK_RESOURCES_NOT_AVAILABLE_FOR_THE_SLICE}; send_ngap_initial_context_setup_resp_fail(rrc->module_id, req, cause); @@ -747,8 +769,10 @@ void rrc_gNB_send_NGAP_UPLINK_NAS(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, const NR_ NGAP_UPLINK_NAS(msg_p).nas_pdu.buf = buf; /* Fill PLMN and location info: use serving PLMN of the UE */ NGAP_UPLINK_NAS(msg_p).plmn = UE->serving_plmn; - NGAP_UPLINK_NAS(msg_p).nr_cell_id = rrc->nr_cellid; - NGAP_UPLINK_NAS(msg_p).tac = rrc->configuration.tac; + nr_rrc_cell_container_t *cell = rrc_get_pcell_for_ue(rrc, UE); + DevAssert(cell); + NGAP_UPLINK_NAS(msg_p).nr_cell_id = cell->info.cell_id; + NGAP_UPLINK_NAS(msg_p).tac = cell->info.tac != 0 ? cell->info.tac : rrc->configuration.tac; itti_send_msg_to_task(TASK_NGAP, rrc->module_id, msg_p); } @@ -866,9 +890,13 @@ void rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ(MessageDef *msg_p, instance_t ins for (int i = 0; i < msg->nb_pdusessions_tosetup; ++i) cp_pdusession_resource_item_to_pdusession(&to_setup[i], &msg->pdusession[i]); - uint64_t dl_ambr = msg->has_ue_ambr ? msg->ueAggMaxBitRate.br_dl : 0; + /** Set AMBR if provided (optional) */ + if (msg->has_ue_ambr) { + UE->ambr.dl_br = msg->ueAggMaxBitRate.br_dl; + UE->ambr.ul_br = msg->ueAggMaxBitRate.br_ul; + } - if (!trigger_bearer_setup(rrc, UE, msg->nb_pdusessions_tosetup, to_setup, dl_ambr)) { + if (!trigger_bearer_setup(rrc, UE, msg->nb_pdusessions_tosetup, to_setup, UE->ambr.dl_br)) { // Reject PDU Session Resource setup if there's no CU-UP associated LOG_W(NR_RRC, "UE %d: reject PDU Session Setup in PDU Session Resource Setup Response\n", UE->rrc_ue_id); ngap_cause_t cause = {.type = NGAP_CAUSE_RADIO_NETWORK, .value = NGAP_CAUSE_RADIO_NETWORK_RESOURCES_NOT_AVAILABLE_FOR_THE_SLICE}; @@ -911,33 +939,64 @@ static void nr_rrc_update_pdusession(pdusession_t *dst, const pdusession_resourc } //------------------------------------------------------------------------------ -int rrc_gNB_process_NGAP_PDUSESSION_MODIFY_REQ(MessageDef *msg_p, instance_t instance) -//------------------------------------------------------------------------------ +/** @brief Handle abnormal conditions in PDU Session Modify procedure. + * Send a response with failed of PDU sessions only, for all PDU Sessions in the request */ +static int rrc_gNB_ngap_pdusession_mod_failure(int module_id, const ngap_pdusession_modify_req_t *req, const ngap_cause_t cause) { - rrc_gNB_ue_context_t *ue_context_p = NULL; - - ngap_pdusession_modify_req_t *req = &NGAP_PDUSESSION_MODIFY_REQ(msg_p); + MessageDef *msg_p = itti_alloc_new_message (TASK_RRC_GNB, module_id, NGAP_PDUSESSION_MODIFY_RESP); + if (msg_p == NULL) { + LOG_E(NR_RRC, "itti_alloc_new_message failed, msg_p is NULL \n"); + return -1; + } + ngap_pdusession_modify_resp_t *resp = &NGAP_PDUSESSION_MODIFY_RESP(msg_p); + resp->amf_ue_ngap_id = req->amf_ue_ngap_id; + resp->gNB_ue_ngap_id = req->gNB_ue_ngap_id; + resp->nb_of_pdusessions_failed = req->nb_pdusessions_tomodify; + resp->pdusessions_failed->cause = cause; + for (int i = 0; i < req->nb_pdusessions_tomodify; i++) + resp->pdusessions_failed[i].pdusession_id = req->pdusession[i].pdusession_id; + itti_send_msg_to_task(TASK_NGAP, module_id, msg_p); + return 0; +} +int rrc_gNB_process_NGAP_PDUSESSION_MODIFY_REQ(const ngap_pdusession_modify_req_t *req, instance_t instance) +{ gNB_RRC_INST *rrc = RC.nrrrc[instance]; - ue_context_p = rrc_gNB_get_ue_context(rrc, req->gNB_ue_ngap_id); + + rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, req->gNB_ue_ngap_id); if (ue_context_p == NULL) { LOG_W(NR_RRC, "[gNB %ld] In NGAP_PDUSESSION_MODIFY_REQ: unknown UE from NGAP ids (%u)\n", instance, req->gNB_ue_ngap_id); - // TO implement return setup failed + ngap_cause_t cause = {.type = NGAP_CAUSE_RADIO_NETWORK, .value = NGAP_CAUSE_RADIO_NETWORK_INCONSISTENT_REMOTE_UE_NGAP_ID}; + rrc_gNB_ngap_pdusession_mod_failure(rrc->module_id, req, cause); return (-1); } gNB_RRC_UE_t *UE = &ue_context_p->ue_context; + if (UE->amf_ue_ngap_id != req->amf_ue_ngap_id) { + LOG_W(NR_RRC, + "Stored amf_ue_ngap_id %ld for UE %x does not match the requested one %ld\n", + UE->amf_ue_ngap_id, + UE->rrc_ue_id, + req->amf_ue_ngap_id); + ngap_cause_t cause = {.type = NGAP_CAUSE_RADIO_NETWORK, .value = NGAP_CAUSE_RADIO_NETWORK_INCONSISTENT_REMOTE_UE_NGAP_ID}; + rrc_gNB_ngap_pdusession_mod_failure(rrc->module_id, req, cause); + return -1; + } + + uint8_t xid = rrc_gNB_get_next_transaction_identifier(rrc->module_id); bool all_failed = true; + DevAssert(req->nb_pdusessions_tomodify <= NGAP_MAX_PDU_SESSION); for (int i = 0; i < req->nb_pdusessions_tomodify; i++) { const pdusession_resource_item_t *sessMod = &req->pdusession[i]; rrc_pdu_session_param_t *session = find_pduSession(&UE->pduSessions, sessMod->pdusession_id); if (!session) { + /* 8.2.3.4 3GPP TS 38.413: If the NG-RAN node receives unrecognized PDU Session ID IEs, + the NG-RAN node shall report the corresponding invalid PDU sessions as failed. So we add it to the list. */ LOG_W(NR_RRC, "Requested modification of non-existing PDU session, refusing modification\n"); - rrc_pdu_session_param_t to_add = {0}; - to_add.status = PDU_SESSION_STATUS_FAILED; - to_add.param.pdusession_id = sessMod->pdusession_id; - to_add.cause.type = NGAP_CAUSE_RADIO_NETWORK; - to_add.cause.value = NGAP_CAUSE_RADIO_NETWORK_UNKNOWN_PDU_SESSION_ID; - seq_arr_push_back(&UE->pduSessions, &to_add, sizeof(to_add)); + ngap_cause_t cause = {.type = NGAP_CAUSE_RADIO_NETWORK, .value = NGAP_CAUSE_RADIO_NETWORK_UNKNOWN_PDU_SESSION_ID}; + pdusession_t session = {.pdusession_id = sessMod->pdusession_id}; + rrc_pdu_session_param_t *added = add_pduSession(&UE->pduSessions, &session); + added->status = PDU_SESSION_STATUS_FAILED; + added->cause = cause; } else { all_failed = false; session->status = PDU_SESSION_STATUS_NEW; @@ -949,50 +1008,34 @@ int rrc_gNB_process_NGAP_PDUSESSION_MODIFY_REQ(MessageDef *msg_p, instance_t ins if (!all_failed) { rrc_gNB_modify_dedicatedRRCReconfiguration(rrc, UE); } else { - LOG_I(NR_RRC, - "pdu session modify failed, fill NGAP_PDUSESSION_MODIFY_RESP with the pdu session information that failed to modify \n"); MessageDef *msg_fail_p = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_PDUSESSION_MODIFY_RESP); if (msg_fail_p == NULL) { LOG_E(NR_RRC, "itti_alloc_new_message failed, msg_fail_p is NULL \n"); - return (-1); + return -1; } - ngap_pdusession_modify_resp_t *msg = &NGAP_PDUSESSION_MODIFY_RESP(msg_fail_p); - msg->gNB_ue_ngap_id = req->gNB_ue_ngap_id; - msg->nb_of_pdusessions = 0; - - FOR_EACH_SEQ_ARR(rrc_pdu_session_param_t *, session, &UE->pduSessions) { - if (session->status == PDU_SESSION_STATUS_FAILED) { - msg->pdusessions_failed[msg->nb_of_pdusessions_failed].pdusession_id = session->param.pdusession_id; - msg->pdusessions_failed[msg->nb_of_pdusessions_failed].cause.type = session->cause.type; - msg->pdusessions_failed[msg->nb_of_pdusessions_failed].cause.value = session->cause.value; - msg->nb_of_pdusessions_failed++; - } - } - itti_send_msg_to_task(TASK_NGAP, instance, msg_fail_p); + rrc_gNB_send_NGAP_PDUSESSION_MODIFY_RESP(rrc, UE, xid); } return (0); } +/** @brief Send PDU Session Resource Setup Response (9.2.1.6 3GPP TS 38.413) + * Direction: Direction: NG-RAN node → AMF */ int rrc_gNB_send_NGAP_PDUSESSION_MODIFY_RESP(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, uint8_t xid) { - MessageDef *msg_p = NULL; - uint16_t pdu_sessions_failed = 0; - uint16_t pdu_sessions_done = 0; - if (!seq_arr_size(&UE->pduSessions)) { LOG_W(NR_RRC, "UE %d: No PDU sessions in the list, don't send NG PDU Session Modify Response\n", UE->rrc_ue_id); return -1; } - msg_p = itti_alloc_new_message (TASK_RRC_GNB, rrc->module_id, NGAP_PDUSESSION_MODIFY_RESP); + MessageDef *msg_p = itti_alloc_new_message(TASK_RRC_GNB, rrc->module_id, NGAP_PDUSESSION_MODIFY_RESP); if (msg_p == NULL) { LOG_E(NR_RRC, "itti_alloc_new_message failed, msg_p is NULL \n"); return (-1); } - ngap_pdusession_modify_resp_t *resp = &NGAP_PDUSESSION_MODIFY_RESP(msg_p); - LOG_I(NR_RRC, "send message NGAP_PDUSESSION_MODIFY_RESP \n"); + ngap_pdusession_modify_resp_t *resp = &NGAP_PDUSESSION_MODIFY_RESP(msg_p); resp->gNB_ue_ngap_id = UE->rrc_ue_id; + resp->amf_ue_ngap_id = UE->amf_ue_ngap_id; FOR_EACH_SEQ_ARR(rrc_pdu_session_param_t *, session, &UE->pduSessions) { if (xid != session->xid) { @@ -1005,47 +1048,35 @@ int rrc_gNB_send_NGAP_PDUSESSION_MODIFY_RESP(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE continue; } if (session->status == PDU_SESSION_STATUS_DONE) { - LOG_I(NR_RRC, "Successfully modified PDU Session %d \n", session->param.pdusession_id); + LOG_I(NR_RRC, "PDU Session Modify successful (pdusession_id=%d) \n", session->param.pdusession_id); // Update status session->status = PDU_SESSION_STATUS_ESTABLISHED; session->cause.type = NGAP_CAUSE_NOTHING; // Fill response - DevAssert(pdu_sessions_done < NGAP_MAX_PDU_SESSION); - pdusession_modify_t *p = &resp->pdusessions[pdu_sessions_done++]; + DevAssert(resp->nb_of_pdusessions <= NGAP_MAX_PDU_SESSION); + pdusession_modify_t *p = &resp->pdusessions[resp->nb_of_pdusessions++]; p->pdusession_id = session->param.pdusession_id; FOR_EACH_SEQ_ARR(nr_rrc_qos_t *, qos_session, &session->param.qos) { DevAssert(p->nb_of_qos_flow < MAX_QOS_FLOWS); - qos_flow_tobe_modified_t *q = &p->qos[p->nb_of_qos_flow++]; + qos_flow_addmod_response_item_t *q = &p->qos[p->nb_of_qos_flow++]; q->qfi = qos_session->qos.qfi; } - LOG_I(NR_RRC, - "Modify Resp (msg index %d, status %d, xid %d): nb_of_pduSessions %ld, pdusession_id %d \n ", - pdu_sessions_done, - session->status, - xid, - seq_arr_size(&UE->pduSessions), - p->pdusession_id); + p->pdusession_id = session->param.pdusession_id; } else if ((session->status == PDU_SESSION_STATUS_NEW) || (session->status == PDU_SESSION_STATUS_ESTABLISHED)) { LOG_D(NR_RRC, "PDU SESSION is NEW or already ESTABLISHED\n"); } else if (session->status == PDU_SESSION_STATUS_FAILED) { - DevAssert(pdu_sessions_failed < NGAP_MAX_PDU_SESSION); - resp->pdusessions_failed[pdu_sessions_failed].pdusession_id = session->param.pdusession_id; - resp->pdusessions_failed[pdu_sessions_failed].cause.type = session->cause.type; - resp->pdusessions_failed[pdu_sessions_failed].cause.value = session->cause.value; - pdu_sessions_failed++; + DevAssert(resp->nb_of_pdusessions_failed <= NGAP_MAX_PDU_SESSION); + pdusession_failed_t *failed = &resp->pdusessions_failed[resp->nb_of_pdusessions_failed++]; + failed->pdusession_id = session->param.pdusession_id; + failed->cause = session->cause; + rm_pduSession(&UE->pduSessions, &UE->drbs, session->param.pdusession_id); } else LOG_W(NR_RRC, "Modify pdu session %d, unknown state %d \n ", session->param.pdusession_id, session->status); } - resp->nb_of_pdusessions = pdu_sessions_done; - resp->nb_of_pdusessions_failed = pdu_sessions_failed; - - if (pdu_sessions_done > 0 || pdu_sessions_failed > 0) { - LOG_D(NR_RRC, "NGAP_PDUSESSION_MODIFY_RESP: sending the message (total pdu session %ld)\n", seq_arr_size(&UE->pduSessions)); - itti_send_msg_to_task (TASK_NGAP, rrc->module_id, msg_p); - } else { - itti_free (ITTI_MSG_ORIGIN_ID(msg_p), msg_p); - } + // Send message to NGAP (always send: if no PDU sessions, only mandatory IEs) + LOG_D(NR_RRC, "Send NG PDU Session Modify Response (nb_of_pdusessions %d, xid %d)\n", resp->nb_of_pdusessions, xid); + itti_send_msg_to_task(TASK_NGAP, rrc->module_id, msg_p); return 0; } @@ -1102,10 +1133,11 @@ int rrc_gNB_process_Handover_Request(gNB_RRC_INST *rrc, instance_t instance, nga return -1; } - struct nr_rrc_du_container_t *du = get_du_by_cell_id(rrc, msg->nr_cell_id); - if (du == NULL) { + // Get cell by cell_id + nr_rrc_cell_container_t *cell = get_cell_by_cell_id(&rrc->cells, msg->nr_cell_id); + if (cell == NULL) { /* Cell Not Found! Return HO Request Failure*/ - LOG_E(RRC, "Failed to process Handover Request: no DU found with NR Cell ID=%lu \n", msg->nr_cell_id); + LOG_E(RRC, "Failed to process Handover Request: no cell found with NR Cell ID=%lu \n", msg->nr_cell_id); ngap_handover_failure_t fail = { .amf_ue_ngap_id = msg->amf_ue_ngap_id, .cause.type = NGAP_CAUSE_RADIO_NETWORK, @@ -1115,6 +1147,18 @@ int rrc_gNB_process_Handover_Request(gNB_RRC_INST *rrc, instance_t instance, nga return -1; } + struct nr_rrc_du_container_t *du = get_du_by_assoc_id(rrc, cell->assoc_id); + if(du == NULL) { + LOG_E(NR_RRC, "Failed to process Handover Request: no DU found with assoc_id=%d\n", cell->assoc_id); + ngap_handover_failure_t fail = { + .amf_ue_ngap_id = msg->amf_ue_ngap_id, + .cause.type = NGAP_CAUSE_RADIO_NETWORK, + .cause.value = NGAP_CAUSE_RADIO_NETWORK_HO_FAILURE_IN_TARGET_5GC_NGRAN_NODE_OR_TARGET_SYSTEM, + }; + rrc_gNB_send_NGAP_HANDOVER_FAILURE(rrc, &fail); + return -1; + } + // Validate PLMN from GUAMI against allowed PLMN list const plmn_id_t *serving_plmn = get_serving_plmn(rrc, &msg->guami.plmn); if (!serving_plmn) { @@ -1128,16 +1172,16 @@ int rrc_gNB_process_Handover_Request(gNB_RRC_INST *rrc, instance_t instance, nga return -1; } - uint16_t pci = du->setup_req->cell[0].info.nr_pci; + uint16_t pci = cell->info.pci; LOG_I(NR_RRC, "Received Handover Request (on NR Cell ID=%lu, PCI=%u) \n", msg->nr_cell_id, pci); // Create UE context - sctp_assoc_t curr_assoc_id = du->assoc_id; - rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_create_ue_context(curr_assoc_id, UINT16_MAX, rrc, UINT64_MAX, UINT32_MAX); + rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_create_ue_context(du->assoc_id, UINT16_MAX, rrc, UINT64_MAX, UINT32_MAX); gNB_RRC_UE_t *UE = &ue_context_p->ue_context; // allocate context for target UE->ho_context = alloc_ho_ctx(HO_CTX_TARGET); + UE->ho_context->target->cell = cell; UE->ho_context->target->ho_trigger = nr_rrc_trigger_n2_ho_target; // Store IDs in UE context @@ -1159,9 +1203,9 @@ int rrc_gNB_process_Handover_Request(gNB_RRC_INST *rrc, instance_t instance, nga // Reset KgNB memset(UE->kgnb, 0, SECURITY_KEY_LENGTH); // Derive KgNB* - const f1ap_served_cell_info_t *cell_info = &du->setup_req->cell[0].info; - uint32_t ssb_arfcn = get_ssb_arfcn(du); - nr_derive_key_ng_ran_star(cell_info->nr_pci, ssb_arfcn, UE->nh, UE->kgnb); + const nr_rrc_cell_info_t *cell_info = &cell->info; + uint32_t ssb_arfcn = get_ssb_arfcn(cell); + nr_derive_key_ng_ran_star(cell_info->pci, ssb_arfcn, UE->nh, UE->kgnb); UE->as_security_active = true; // Activate SRBs activate_srb(UE, SRB1); @@ -1184,7 +1228,11 @@ int rrc_gNB_process_Handover_Request(gNB_RRC_INST *rrc, instance_t instance, nga cp_pdusession_transfer_to_pdusession(pdu, &ho_pdu->pdusessionTransfer); } - if (!trigger_bearer_setup(rrc, UE, msg->nb_of_pdusessions, to_setup, msg->ue_ambr.br_dl)) { + // Store UE aggregate maximum bitrate + UE->ambr.dl_br = msg->ue_ambr.br_dl; + UE->ambr.ul_br = msg->ue_ambr.br_ul; + + if (!trigger_bearer_setup(rrc, UE, msg->nb_of_pdusessions, to_setup, UE->ambr.dl_br)) { LOG_E(NR_RRC, "Failed to establish PDU session: handover failed\n"); ngap_handover_failure_t fail = { @@ -1196,7 +1244,6 @@ int rrc_gNB_process_Handover_Request(gNB_RRC_INST *rrc, instance_t instance, nga rrc_remove_ue(rrc, ue_context_p); return -1; } - return 0; } @@ -1445,24 +1492,28 @@ void rrc_gNB_send_NGAP_HANDOVER_REQUEST_ACKNOWLEDGE(gNB_RRC_INST *rrc, gNB_RRC_U void rrc_gNB_send_NGAP_HANDOVER_NOTIFY(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE) { LOG_I(NR_RRC, "Triggering NGAP Handover Notify\n"); - nr_rrc_du_container_t *du = get_du_by_cell_id(rrc, rrc->nr_cellid); + nr_rrc_cell_container_t *cell = rrc_get_pcell_for_ue(rrc, UE); + if (cell == NULL) { + LOG_E(NR_RRC, "Failed to send Handover Notify: no PCell found for UE %d\n", UE->rrc_ue_id); + return; + } + nr_rrc_du_container_t *du = get_du_for_ue(rrc, UE->rrc_ue_id); if (du == NULL) { - LOG_E(NR_RRC, "Failed to send Handover Notify: no DU found with NR Cell ID=%lu\n", rrc->nr_cellid); + LOG_E(NR_RRC, "Failed to send Handover Notify: no DU found for UE %d\n", UE->rrc_ue_id); return; } - MessageDef *msg_p = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_HANDOVER_NOTIFY); ngap_handover_notify_t *ho_notify = &NGAP_HANDOVER_NOTIFY(msg_p); memset(ho_notify, 0, sizeof(*ho_notify)); ho_notify->gNB_ue_ngap_id = UE->rrc_ue_id; ho_notify->amf_ue_ngap_id = UE->amf_ue_ngap_id; - ho_notify->user_info.nrCellIdentity = rrc->nr_cellid; + ho_notify->user_info.nrCellIdentity = cell->info.cell_id; target_ran_node_id_t *target_ng_ran = &ho_notify->user_info.target_ng_ran; - target_ng_ran->tac = *du->setup_req->cell->info.tac; + target_ng_ran->tac = cell->info.tac; target_ng_ran->targetgNBId = rrc->node_id; - target_ng_ran->plmn_identity = du->setup_req->cell->info.plmn; + target_ng_ran->plmn_identity = cell->info.plmn; itti_send_msg_to_task(TASK_NGAP, rrc->module_id, msg_p); } @@ -1590,7 +1641,7 @@ int rrc_gNB_process_PAGING_IND(MessageDef *msg_p, instance_t instance) p->mnc_digit_length, p->mnc, msg->tac[tai_size]); - gNB_RrcConfigurationReq *req = &RC.nrrrc[instance]->configuration; + nr_rrc_config_t *req = &RC.nrrrc[instance]->configuration; for (uint8_t j = 0; j < req->num_plmn; j++) { plmn_id_t *req_plmn = &req->plmn[j]; if (req_plmn->mcc == p->mcc && req_plmn->mnc == p->mnc && req->tac == msg->tac[tai_size]) { @@ -1599,7 +1650,23 @@ int rrc_gNB_process_PAGING_IND(MessageDef *msg_p, instance_t instance) if (NODE_IS_CU(RC.nrrrc[instance]->node_type)) { MessageDef *m = itti_alloc_new_message(TASK_RRC_GNB, 0, F1AP_PAGING_IND); F1AP_PAGING_IND(m).plmn = *req_plmn; - F1AP_PAGING_IND (m).nr_cellid = RC.nrrrc[j]->nr_cellid; + // Construct 5G-S-TMSI-Part1 from paging identity to find UE + const fiveg_s_tmsi_t *s_tmsi = &msg->ue_paging_identity.s_tmsi; + uint64_t s_tmsi_part1 = nr_construct_5g_s_tmsi_part1(s_tmsi->amf_set_id, s_tmsi->amf_pointer, s_tmsi->m_tmsi); + // Try to find UE by 5G-S-TMSI-Part1 + rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_ue_context_5g_s_tmsi_exist(RC.nrrrc[instance], s_tmsi_part1); + if (ue_context_p == NULL) { + LOG_W(NR_RRC, "UE not found by paging identity\n"); + return -1; + } + // UE found: use its associated cell + gNB_RRC_UE_t *UE = &ue_context_p->ue_context; + nr_rrc_cell_container_t *cell = rrc_get_pcell_for_ue(RC.nrrrc[instance], UE); + if (cell == NULL) { + LOG_W(NR_RRC, "UE %d (rnti %04x) found by paging identity but has no valid cell\n", UE->rrc_ue_id, UE->rnti); + return -1; + } + F1AP_PAGING_IND(m).nr_cellid = cell->info.cell_id; F1AP_PAGING_IND(m).ueidentityindexvalue = (uint16_t)(msg->ue_paging_identity.s_tmsi.m_tmsi % 1024); F1AP_PAGING_IND(m).fiveg_s_tmsi = msg->ue_paging_identity.s_tmsi.m_tmsi; F1AP_PAGING_IND(m).paging_drx = msg->paging_drx; diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.h b/openair2/RRC/NR/rrc_gNB_NGAP.h index c3f692ff5f..2530b3a803 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.h +++ b/openair2/RRC/NR/rrc_gNB_NGAP.h @@ -60,7 +60,7 @@ void rrc_gNB_send_NGAP_PDUSESSION_SETUP_RESP(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE void rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ(MessageDef *msg_p, instance_t instance); -int rrc_gNB_process_NGAP_PDUSESSION_MODIFY_REQ(MessageDef *msg_p, instance_t instance); +int rrc_gNB_process_NGAP_PDUSESSION_MODIFY_REQ(const ngap_pdusession_modify_req_t *req, instance_t instance); int rrc_gNB_send_NGAP_PDUSESSION_MODIFY_RESP(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, uint8_t xid); diff --git a/openair2/RRC/NR/rrc_gNB_UE_context.c b/openair2/RRC/NR/rrc_gNB_UE_context.c index 86838927ab..d9a5a727c9 100644 --- a/openair2/RRC/NR/rrc_gNB_UE_context.c +++ b/openair2/RRC/NR/rrc_gNB_UE_context.c @@ -41,6 +41,7 @@ #include "openair2/F1AP/f1ap_ids.h" #include "tree.h" #include "rrc_gNB_radio_bearers.h" +#include "rrc_cell_management.h" static void rrc_gNB_ue_context_update_time(rrc_gNB_ue_context_t *ctxt) { @@ -155,10 +156,11 @@ void rrc_gNB_remove_ue_context(gNB_RRC_INST *rrc_instance_pP, rrc_gNB_ue_context return; } - LOG_UE_EVENT(&ue_context_pP->ue_context, "Remove UE context\n"); + gNB_RRC_UE_t *ue = &ue_context_pP->ue_context; + LOG_I(NR_RRC, "[--] (UE ID %d RNTI %04x) Remove UE context\n", ue->rrc_ue_id, ue->rnti); RB_REMOVE(rrc_nr_ue_tree_s, &rrc_instance_pP->rrc_ue_head, ue_context_pP); - uid_linear_allocator_free(&rrc_instance_pP->uid_allocator, ue_context_pP->ue_context.rrc_ue_id - 1); - cu_remove_f1_ue_data(ue_context_pP->ue_context.rrc_ue_id); + uid_linear_allocator_free(&rrc_instance_pP->uid_allocator, ue->rrc_ue_id - 1); + cu_remove_f1_ue_data(ue->rrc_ue_id); rrc_gNB_free_mem_ue_context(ue_context_pP); } @@ -218,11 +220,15 @@ rrc_gNB_ue_context_t *rrc_gNB_create_ue_context(sctp_assoc_t assoc_id, ue->rrc_ue_id); bool success = cu_add_f1_ue_data(ue->rrc_ue_id, &ue_data); DevAssert(success); + ue->ambr.dl_br = UINT64_MAX; + ue->ambr.ul_br = UINT64_MAX; // Initialise setup PDU Sessions list seq_arr_init(&ue->pduSessions, sizeof(rrc_pdu_session_param_t)); // Initialise setup DRBs list seq_arr_init(&ue->drbs, sizeof(drb_t)); + // Initialise serving cells list + seq_arr_init(&ue->serving_cells, sizeof(ue_serving_cell_t)); RB_INSERT(rrc_nr_ue_tree_s, &rrc_instance_pP->rrc_ue_head, ue_context_p); LOG_UE_EVENT(ue, diff --git a/openair2/RRC/NR/rrc_gNB_cuup.c b/openair2/RRC/NR/rrc_gNB_cuup.c index 1e3df71822..8292699a5c 100644 --- a/openair2/RRC/NR/rrc_gNB_cuup.c +++ b/openair2/RRC/NR/rrc_gNB_cuup.c @@ -43,6 +43,7 @@ #include "tree.h" #include "e1ap_interface_management.h" #include "rrc_gNB_NGAP.h" +#include "rrc_cell_management.h" static int cuup_compare(const nr_rrc_cuup_container_t *a, const nr_rrc_cuup_container_t *b) { diff --git a/openair2/RRC/NR/rrc_gNB_du.c b/openair2/RRC/NR/rrc_gNB_du.c index 246df60626..3381d60ce5 100644 --- a/openair2/RRC/NR/rrc_gNB_du.c +++ b/openair2/RRC/NR/rrc_gNB_du.c @@ -20,6 +20,7 @@ */ #include "rrc_gNB_du.h" +#include "rrc_cell_management.h" #include #include #include @@ -52,12 +53,12 @@ #include "utils.h" #include "xer_encoder.h" -int get_dl_band(const struct f1ap_served_cell_info_t *cell_info) +static int get_dl_band(const struct f1ap_served_cell_info_t *cell_info) { return cell_info->mode == F1AP_MODE_TDD ? cell_info->tdd.freqinfo.band : cell_info->fdd.dl_freqinfo.band; } -int get_ssb_scs(const struct f1ap_served_cell_info_t *cell_info) +static int get_ssb_scs(const struct f1ap_served_cell_info_t *cell_info) { return cell_info->mode == F1AP_MODE_TDD ? cell_info->tdd.tbw.scs : cell_info->fdd.dl_tbw.scs; } @@ -71,6 +72,9 @@ static NR_SSB_MTC_t *get_ssb_mtc(const NR_MeasurementTimingConfiguration_t *mtc) return ssb_mtc; } +/** @brief Return the frequency of the SS block of the cell for which this message is included, + * or of other SS blocks within the same carrier. MeasurementTimingConfiguration message is + * used to convey assistance information for measurement timing between nodes (e.g F1 setup) */ static int ssb_arfcn_mtc(const NR_MeasurementTimingConfiguration_t *mtc) { /* format has been verified when accepting MeasurementTimingConfiguration */ @@ -78,27 +82,20 @@ static int ssb_arfcn_mtc(const NR_MeasurementTimingConfiguration_t *mtc) return mtlist->list.array[0]->frequencyAndTiming->carrierFreq; } -int get_ssb_arfcn(const struct nr_rrc_du_container_t *du) +/** @brief Find first cell owned by this DU (assoc_id) and return its SSB ARFCN + * @note Current assumption is that each DU serves only one cell*/ +int get_ssb_arfcn(const struct nr_rrc_cell_container_t *cell) { - DevAssert(du != NULL && du->mtc != NULL); - return ssb_arfcn_mtc(du->mtc); + DevAssert(cell != NULL); + if (cell->mtc != NULL) + return ssb_arfcn_mtc(cell->mtc); + AssertFatal(false, "no mtc found for cell %ld\n", cell->info.cell_id); + return 0; } -static int du_compare(const nr_rrc_du_container_t *a, const nr_rrc_du_container_t *b) -{ - if (a->assoc_id > b->assoc_id) - return 1; - if (a->assoc_id == b->assoc_id) - return 0; - return -1; /* a->assoc_id < b->assoc_id */ -} - -/* Tree management functions */ -RB_GENERATE/*_STATIC*/(rrc_du_tree, nr_rrc_du_container_t, entries, du_compare); - static bool rrc_gNB_plmn_matches(const gNB_RRC_INST *rrc, const f1ap_served_cell_info_t *info) { - const gNB_RrcConfigurationReq *conf = &rrc->configuration; + const nr_rrc_config_t *conf = &rrc->configuration; return conf->num_plmn == 1 // F1 supports only one && conf->plmn[0].mcc == info->plmn.mcc && conf->plmn[0].mnc == info->plmn.mnc; } @@ -172,33 +169,17 @@ static int nr_cell_id_match(const void *key, const void *element) return 1; } -static neighbour_cell_configuration_t *get_cell_neighbour_list(const gNB_RRC_INST *rrc, const f1ap_served_cell_info_t *cell_info) +static neighbour_cell_configuration_t *get_cell_neighbour_list(const gNB_RRC_INST *rrc, const nr_rrc_cell_container_t *cell) { void *base = seq_arr_front(rrc->neighbour_cell_configuration); size_t nmemb = seq_arr_size(rrc->neighbour_cell_configuration); size_t size = sizeof(neighbour_cell_configuration_t); - void *it = bsearch((void *)&cell_info->nr_cellid, base, nmemb, size, nr_cell_id_match); + void *it = bsearch((void *)&cell->info.cell_id, base, nmemb, size, nr_cell_id_match); return (neighbour_cell_configuration_t *)it; } -const struct f1ap_served_cell_info_t *get_cell_information_by_phycellId(int phyCellId) -{ - gNB_RRC_INST *rrc = RC.nrrrc[0]; - nr_rrc_du_container_t *it = NULL; - RB_FOREACH (it, rrc_du_tree, &rrc->dus) { - for (int cellIdx = 0; cellIdx < it->setup_req->num_cells_available; cellIdx++) { - const f1ap_served_cell_info_t *cell_info = &(it->setup_req->cell[cellIdx].info); - if (cell_info->nr_pci == phyCellId) { - LOG_D(NR_RRC, "HO LOG: Found cell with phyCellId %d\n", phyCellId); - return cell_info; - } - } - } - return NULL; -} - static void is_intra_frequency_neighbour(void *ssb_arfcn, void *neighbour_cell) { uint32_t *ssb_arfcn_ptr = (uint32_t *)ssb_arfcn; @@ -212,28 +193,36 @@ static void is_intra_frequency_neighbour(void *ssb_arfcn, void *neighbour_cell) /** * @brief Labels neighbour cells if they are intra frequency to prepare meas config only for intra frequency ho * @param[in] rrc Pointer to RRC instance - * @param[in] cell_info Pointer to cell information + * @param[in] cell Pointer to cell container */ -static void label_intra_frequency_neighbours(gNB_RRC_INST *rrc, - const nr_rrc_du_container_t *du, - const f1ap_served_cell_info_t *cell_info) +static void label_intra_frequency_neighbours(gNB_RRC_INST *rrc, const nr_rrc_cell_container_t *cell) { if (!rrc->neighbour_cell_configuration) return; - neighbour_cell_configuration_t *neighbour_cell_config = get_cell_neighbour_list(rrc, cell_info); + neighbour_cell_configuration_t *neighbour_cell_config = get_cell_neighbour_list(rrc, cell); if (!neighbour_cell_config) return; - LOG_D(NR_RRC, "HO LOG: Cell: %lu has neighbour cell configuration!\n", cell_info->nr_cellid); - uint32_t ssb_arfcn = get_ssb_arfcn(du); + uint32_t ssb_arfcn = get_ssb_arfcn(cell); + LOG_D(NR_RRC, + "Cell %lu (PCI %d, SSB ARFCN %u) has neighbour cell configuration, labeling intra-frequency neighbours\n", + cell->info.cell_id, + cell->info.pci, + ssb_arfcn); seq_arr_t *cell_neighbour_list = neighbour_cell_config->neighbour_cells; for_each(cell_neighbour_list, (void *)&ssb_arfcn, is_intra_frequency_neighbour); } -static bool valid_du_in_neighbour_configs(const seq_arr_t *neighbour_cell_configuration, const f1ap_served_cell_info_t *cell, int ssb_arfcn) +static bool valid_du_in_neighbour_configs(const seq_arr_t *neighbour_cell_configuration, const f1ap_served_cell_info_t *cell) { + // MTC is mandatory, but some DUs don't send it in the F1 Setup Request, so + // "tolerate" this behavior, despite it being mandatory + NR_MeasurementTimingConfiguration_t *mtc = extract_mtc(cell->measurement_timing_config, cell->measurement_timing_config_len); + int ssb_arfcn = ssb_arfcn_mtc(mtc); + ASN_STRUCT_FREE(asn_DEF_NR_MeasurementTimingConfiguration, mtc); + for (int c = 0; c < neighbour_cell_configuration->size; c++) { const neighbour_cell_configuration_t *neighbour_config = seq_arr_at(neighbour_cell_configuration, c); for (int ni = 0; ni < neighbour_config->neighbour_cells->size; ni++) { @@ -289,6 +278,44 @@ static bool valid_du_in_neighbour_configs(const seq_arr_t *neighbour_cell_config return true; } +static void cp_f1_served_cell_info_to_cell(nr_rrc_cell_container_t *dst, const f1ap_served_cell_info_t *src) +{ + DevAssert(dst != NULL); + DevAssert(src != NULL); + + // Copy basic cell information + dst->info.cell_id = src->nr_cellid; + dst->info.pci = src->nr_pci; + dst->info.plmn = src->plmn; + dst->info.tac = src->tac ? *src->tac : 0; + + // Copy frequency information based on mode + if (src->mode == F1AP_MODE_TDD) { + dst->info.mode = NR_MODE_TDD; + dst->info.tdd.dlul.band = src->tdd.freqinfo.band; + dst->info.tdd.dlul.arfcn = src->tdd.freqinfo.arfcn; + dst->info.tdd.dlul.scs = src->tdd.tbw.scs; + dst->info.tdd.dlul.nrb = src->tdd.tbw.nrb; + } else { + dst->info.mode = NR_MODE_FDD; + /* DL */ + dst->info.fdd.dl.band = src->fdd.dl_freqinfo.band; + dst->info.fdd.dl.arfcn = src->fdd.dl_freqinfo.arfcn; + dst->info.fdd.dl.scs = src->fdd.dl_tbw.scs; + dst->info.fdd.dl.nrb = src->fdd.dl_tbw.nrb; + /* UL */ + dst->info.fdd.ul.band = src->fdd.ul_freqinfo.band; + dst->info.fdd.ul.arfcn = src->fdd.ul_freqinfo.arfcn; + dst->info.fdd.ul.scs = src->fdd.ul_tbw.scs; + dst->info.fdd.ul.nrb = src->fdd.ul_tbw.nrb; + } + + // Extract and store MTC + if (src->measurement_timing_config_len > 0) { + dst->mtc = extract_mtc(src->measurement_timing_config, src->measurement_timing_config_len); + } +} + void rrc_gNB_process_f1_setup_req(f1ap_setup_req_t *req, sctp_assoc_t assoc_id) { AssertFatal(assoc_id != 0, "illegal assoc_id == 0: should be -1 (monolithic) or >0 (split)\n"); @@ -300,147 +327,147 @@ void rrc_gNB_process_f1_setup_req(f1ap_setup_req_t *req, sctp_assoc_t assoc_id) f1ap_setup_failure_t fail = {.transaction_id = F1AP_get_next_transaction_identifier(0, 0)}; // check: - // - it is one cell // - PLMN and Cell ID matches // - no previous DU with the same ID // else reject - if (req->num_cells_available != 1) { - LOG_E(NR_RRC, "can only handle on DU cell, but gNB_DU %ld has %d\n", req->gNB_DU_id, req->num_cells_available); - fail.cause = F1AP_CauseRadioNetwork_gNB_CU_Cell_Capacity_Exceeded; - rrc->mac_rrc.f1_setup_failure(assoc_id, &fail); - return; - } - f1ap_served_cell_info_t *cell_info = &req->cell[0].info; - if (!rrc_gNB_plmn_matches(rrc, cell_info)) { - LOG_E(NR_RRC, - "PLMN mismatch: CU %03d.%0*d, DU %03d%0*d\n", - rrc->configuration.plmn[0].mcc, - rrc->configuration.plmn[0].mnc_digit_length, - rrc->configuration.plmn[0].mnc, - cell_info->plmn.mcc, - cell_info->plmn.mnc_digit_length, - cell_info->plmn.mnc); - fail.cause = F1AP_CauseRadioNetwork_plmn_not_served_by_the_gNB_CU; - rrc->mac_rrc.f1_setup_failure(assoc_id, &fail); - return; - } nr_rrc_du_container_t *it = NULL; RB_FOREACH(it, rrc_du_tree, &rrc->dus) { - if (it->setup_req->gNB_DU_id == req->gNB_DU_id) { + if (it->gNB_DU_id == req->gNB_DU_id) { LOG_E(NR_RRC, "gNB-DU ID: existing DU %s on assoc_id %d already has ID %ld, rejecting requesting gNB-DU\n", - it->setup_req->gNB_DU_name, + it->gNB_DU_name, it->assoc_id, - it->setup_req->gNB_DU_id); - fail.cause = F1AP_CauseMisc_unspecified; - rrc->mac_rrc.f1_setup_failure(assoc_id, &fail); - return; - } - // note: we assume that each DU contains only one cell; otherwise, we would - // need to check every cell in the requesting DU to any existing cell. - const f1ap_served_cell_info_t *exist_info = &it->setup_req->cell[0].info; - const f1ap_served_cell_info_t *new_info = &req->cell[0].info; - if (exist_info->nr_cellid == new_info->nr_cellid || exist_info->nr_pci == new_info->nr_pci) { - LOG_E(NR_RRC, - "existing DU %s on assoc_id %d already has cellID %ld/physCellId %d, rejecting requesting gNB-DU with cellID %ld/physCellId %d\n", - it->setup_req->gNB_DU_name, - it->assoc_id, - exist_info->nr_cellid, - exist_info->nr_pci, - new_info->nr_cellid, - new_info->nr_pci); + it->gNB_DU_id); fail.cause = F1AP_CauseMisc_unspecified; rrc->mac_rrc.f1_setup_failure(assoc_id, &fail); return; } } + LOG_I(NR_RRC, "Accepting DU %ld (%s) (RRC version %u.%u.%u)\n", req->gNB_DU_id, req->gNB_DU_name, req->rrc_ver[0], req->rrc_ver[1], req->rrc_ver[2]); - // MTC is mandatory, but some DUs don't send it in the F1 Setup Request, so - // "tolerate" this behavior, despite it being mandatory - NR_MeasurementTimingConfiguration_t *mtc = - extract_mtc(cell_info->measurement_timing_config, cell_info->measurement_timing_config_len); - - if (rrc->neighbour_cell_configuration - && !valid_du_in_neighbour_configs(rrc->neighbour_cell_configuration, cell_info, ssb_arfcn_mtc(mtc))) { - LOG_E(NR_RRC, "problem with DU %ld in neighbor configuration, rejecting DU\n", req->gNB_DU_id); - f1ap_setup_failure_t fail = {.cause = F1AP_CauseMisc_unspecified}; - rrc->mac_rrc.f1_setup_failure(assoc_id, &fail); - ASN_STRUCT_FREE(asn_DEF_NR_MeasurementTimingConfiguration, mtc); - return; - } - - const f1ap_gnb_du_system_info_t *sys_info = req->cell[0].sys_info; - NR_MIB_t *mib = NULL; - NR_SIB1_t *sib1 = NULL; - - if (sys_info != NULL && sys_info->mib != NULL && !(sys_info->sib1 == NULL && IS_SA_MODE(get_softmodem_params()))) { - if (!extract_sys_info(sys_info, &mib, &sib1)) { - LOG_W(NR_RRC, "rejecting DU ID %ld\n", req->gNB_DU_id); - fail.cause = F1AP_CauseProtocol_semantic_error; - rrc->mac_rrc.f1_setup_failure(assoc_id, &fail); - ASN_STRUCT_FREE(asn_DEF_NR_MeasurementTimingConfiguration, mtc); - return; - } - } - LOG_I(NR_RRC, "Accepting DU %ld (%s), sending F1 Setup Response\n", req->gNB_DU_id, req->gNB_DU_name); - LOG_I(NR_RRC, "DU uses RRC version %u.%u.%u\n", req->rrc_ver[0], req->rrc_ver[1], req->rrc_ver[2]); - - // we accept the DU - nr_rrc_du_container_t *du = calloc(1, sizeof(*du)); - AssertFatal(du, "out of memory\n"); + // DU is accepted, add it to tree and add cell to DU's cell array + nr_rrc_du_container_t *du = calloc_or_fail(1, sizeof(*du)); du->assoc_id = assoc_id; + du->gNB_DU_id = req->gNB_DU_id; + du->gNB_DU_name = strdup(req->gNB_DU_name ? req->gNB_DU_name : "N/A"); + memcpy(du->rrc_ver, req->rrc_ver, sizeof(du->rrc_ver)); - /* ITTI will free the setup request message via free(). So the memory - * "inside" of the message will remain, but the "outside" container no, so - * allocate memory and copy it in */ - du->setup_req = calloc(1,sizeof(*du->setup_req)); - AssertFatal(du->setup_req, "out of memory\n"); - // Copy F1AP message - *du->setup_req = cp_f1ap_setup_request(req); - // MIB can be null and configured later via DU Configuration Update - du->mib = mib; - du->sib1 = sib1; - du->mtc = mtc; - RB_INSERT(rrc_du_tree, &rrc->dus, du); - rrc->num_dus++; - - served_cells_to_activate_t cell = { - .plmn = cell_info->plmn, - .nr_cellid = cell_info->nr_cellid, - .nrpci = cell_info->nr_pci, - .num_SI = 0, - }; - - // Encode CU SIBs and configure setup response with sysinfo - seq_arr_t *sibs = rrc->SIBs; - if (sibs) { - for (int i = 0; i < sibs->size; i++) { - nr_SIBs_t *sib = (nr_SIBs_t *)seq_arr_at(sibs, i); - switch (sib->SIB_type) { - case 2: { - NR_SSB_MTC_t *ssbmtc = get_ssb_mtc(mtc); - sib->SIB_size = do_SIB2_NR(&sib->SIB_buffer, ssbmtc); - cell.SI_msg[cell.num_SI].SI_container = sib->SIB_buffer; - cell.SI_msg[cell.num_SI].SI_container_length = sib->SIB_size; - cell.SI_msg[cell.num_SI].SI_type = sib->SIB_type; - cell.num_SI++; - } break; - default: - AssertFatal(false, "SIB%d not handled yet\n", sib->SIB_type); - } - } - } - - if (du->mib != NULL && du->sib1 != NULL) - label_intra_frequency_neighbours(rrc, du, cell_info); + // Initialize cell array for this DU + seq_arr_init(&du->cells, sizeof(nr_rrc_cell_container_t *)); + nr_rrc_du_container_t *du_collision = rrc_add_du(rrc, du); + AssertFatal(du_collision == NULL, "rrc_add_du should succeed for new DU (assoc_id %d)", assoc_id); + // Build F1 Setup Response f1ap_setup_resp_t resp = {.transaction_id = req->transaction_id, - .num_cells_to_activate = 1, - .cells_to_activate[0] = cell}; + .num_cells_to_activate = req->num_cells_available, + .cells_to_activate = calloc_or_fail(req->num_cells_available, sizeof(*resp.cells_to_activate))}; int num = read_version(TO_STRING(NR_RRC_VERSION), &resp.rrc_ver[0], &resp.rrc_ver[1], &resp.rrc_ver[2]); AssertFatal(num == 3, "could not read RRC version string %s\n", TO_STRING(NR_RRC_VERSION)); if (rrc->node_name != NULL) resp.gNB_CU_name = strdup(rrc->node_name); + + if (req->num_cells_available > 1) { + LOG_W(NR_RRC, "Received F1 Setup Request with %u cells, only one cell is supported\n", req->num_cells_available); + } + for (int i = 0; i < req->num_cells_available; i++) { + f1ap_served_cell_info_t *cell_info = &req->cell[i].info; + if (!rrc_gNB_plmn_matches(rrc, cell_info)) { + LOG_E(NR_RRC, + "PLMN mismatch: CU %03d.%0*d, DU %03d%0*d\n", + rrc->configuration.plmn[0].mcc, + rrc->configuration.plmn[0].mnc_digit_length, + rrc->configuration.plmn[0].mnc, + cell_info->plmn.mcc, + cell_info->plmn.mnc_digit_length, + cell_info->plmn.mnc); + fail.cause = F1AP_CauseRadioNetwork_plmn_not_served_by_the_gNB_CU; + rrc->mac_rrc.f1_setup_failure(assoc_id, &fail); + free_f1ap_setup_response(&resp); + return; + } + + if (rrc->neighbour_cell_configuration && !valid_du_in_neighbour_configs(rrc->neighbour_cell_configuration, cell_info)) { + LOG_E(NR_RRC, "problem with DU %ld in neighbor configuration, rejecting DU\n", req->gNB_DU_id); + f1ap_setup_failure_t fail = {.cause = F1AP_CauseMisc_unspecified}; + rrc->mac_rrc.f1_setup_failure(assoc_id, &fail); + free_f1ap_setup_response(&resp); + return; + } + + const f1ap_gnb_du_system_info_t *sys_info = req->cell[i].sys_info; + NR_MIB_t *mib = NULL; + NR_SIB1_t *sib1 = NULL; + if (sys_info != NULL && sys_info->mib != NULL && !(sys_info->sib1 == NULL && IS_SA_MODE(get_softmodem_params()))) { + if (!extract_sys_info(sys_info, &mib, &sib1)) { + LOG_W(NR_RRC, "rejecting DU ID %ld\n", req->gNB_DU_id); + fail.cause = F1AP_CauseProtocol_semantic_error; + rrc->mac_rrc.f1_setup_failure(assoc_id, &fail); + free_f1ap_setup_response(&resp); + return; + } + } + + /* Create cell and add to global tree */ + nr_rrc_cell_container_t *new = calloc_or_fail(1, sizeof(*new)); + new->assoc_id = assoc_id; + cp_f1_served_cell_info_to_cell(new, cell_info); + new->mib = mib; + new->sib1 = sib1; + // Add cell to DU's cell array + nr_rrc_cell_container_t *collision = rrc_add_cell(rrc, new); + if (collision != NULL) { + nr_rrc_du_container_t *existing_du = get_du_by_assoc_id(rrc, collision->assoc_id); + const char *du_name = existing_du ? existing_du->gNB_DU_name : "unknown"; + LOG_E(NR_RRC, + "Cell ID %lu already exists in DU %s (assoc_id %d), rejecting requesting gNB-DU\n", + new->info.cell_id, + du_name, + collision->assoc_id); + rrc_free_cell_container(new); + fail.cause = F1AP_CauseRadioNetwork_cell_not_available; + rrc->mac_rrc.f1_setup_failure(assoc_id, &fail); + free_f1ap_setup_response(&resp); + return; + } + + nr_rrc_cell_container_t *added = rrc_add_cell_to_du(&du->cells, new); + AssertFatal(added != NULL, "Failed to add cell %ld to DU %ld\n", new->info.cell_id, du->gNB_DU_id); + LOG_I(NR_RRC, "DU %ld: Added cell %ld\n", du->gNB_DU_id, new->info.cell_id); + + served_cells_to_activate_t cell = { + .plmn = cell_info->plmn, + .nr_cellid = cell_info->nr_cellid, + .nrpci = cell_info->nr_pci, + .num_SI = 0, + }; + + // Encode CU SIBs and configure setup response with sysinfo + seq_arr_t *sibs = rrc->SIBs; + if (sibs) { + for (int i = 0; i < sibs->size; i++) { + nr_SIBs_t *sib = (nr_SIBs_t *)seq_arr_at(sibs, i); + switch (sib->SIB_type) { + case 2: { + NR_SSB_MTC_t *ssbmtc = get_ssb_mtc(new->mtc); + sib->SIB_size = do_SIB2_NR(&sib->SIB_buffer, ssbmtc); + cell.SI_msg[cell.num_SI].SI_container = sib->SIB_buffer; + cell.SI_msg[cell.num_SI].SI_container_length = sib->SIB_size; + cell.SI_msg[cell.num_SI].SI_type = sib->SIB_type; + cell.num_SI++; + } break; + default: + AssertFatal(false, "SIB%d not handled yet\n", sib->SIB_type); + } + } + } + + if (new->mib != NULL &&new->sib1 != NULL) + label_intra_frequency_neighbours(rrc, new); + + resp.cells_to_activate[i] = cell; + } + + LOG_I(NR_RRC, "DU %ld (%s): sending F1 Setup Response\n", req->gNB_DU_id, req->gNB_DU_name); rrc->mac_rrc.f1_setup_response(assoc_id, &resp); free_f1ap_setup_response(&resp); @@ -460,6 +487,10 @@ static int invalidate_du_connections(gNB_RRC_INST *rrc, sctp_assoc_t assoc_id) uint32_t ue_id = UE->rrc_ue_id; if (UE->ho_context != NULL) LOG_W(NR_RRC, "DU disconnected while handover for UE %d active\n", ue_id); + + // Remove SCells from this DU for this UE + rrc_remove_ue_scells_from_du(UE, assoc_id); + f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_id); if (ue_data.du_assoc_id != assoc_id) continue; /* this UE is on another DU */ @@ -486,26 +517,77 @@ static int invalidate_du_connections(gNB_RRC_INST *rrc, sctp_assoc_t assoc_id) return count; } -static void update_cell_info(nr_rrc_du_container_t *du, const f1ap_served_cell_info_t *new_ci) +/** @brief Update cell information from F1AP DU configuration update + * @param[in] rrc RRC instance (needed to get DU and check PCI duplicates) + * @param[in] old_nci The cell_id used to find the cell in the tree + * @param[in] new_ci Pointer to the new cell information from F1AP message + * @return Pointer to the updated cell container, or NULL on failure + * @note RB tree manipulation: The cell tree is keyed by cell_id. If the cell_id changes + * during update, the node must be removed from the tree before updating (using the + * old cell_id) and re-inserted after updating (using the new cell_id). This is + * necessary because RB trees maintain ordering based on the key value. If cell_id + * doesn't change, no tree manipulation is needed. */ +static nr_rrc_cell_container_t *update_cell_info(gNB_RRC_INST *rrc, const uint64_t old_nci, const f1ap_served_cell_info_t *new_ci) { - DevAssert(du != NULL); + DevAssert(rrc != NULL); DevAssert(new_ci != NULL); - AssertFatal(du->setup_req->num_cells_available == 1, "expected 1 cell for DU, but has %d\n", du->setup_req->num_cells_available); - f1ap_served_cell_info_t *ci = &du->setup_req->cell[0].info; - // make sure no memory is allocated - free_f1ap_cell(ci, NULL); - *ci = copy_f1ap_served_cell_info(new_ci); - - NR_MeasurementTimingConfiguration_t *new_mtc = - extract_mtc(new_ci->measurement_timing_config, new_ci->measurement_timing_config_len); - if (new_mtc != NULL) { - ASN_STRUCT_FREE(asn_DEF_NR_MeasurementTimingConfiguration, du->mtc); - du->mtc = new_mtc; - } else { - LOG_E(NR_RRC, "error decoding MeasurementTimingConfiguration during cell update, ignoring new config\n"); - ASN_STRUCT_FREE(asn_DEF_NR_MeasurementTimingConfiguration, new_mtc); + struct rrc_cell_tree *cells = &rrc->cells; + // Find the cell by old cell_id from the update message + nr_rrc_cell_container_t *cell = get_cell_by_cell_id(cells, old_nci); + if (cell == NULL) { + LOG_W(NR_RRC, "no cell with ID %ld found, ignoring gNB-DU configuration update\n", old_nci); + return NULL; } + + // Check if PCI will change - if so, check for duplicate PCI within the same DU + bool pci_changed = (cell->info.pci != new_ci->nr_pci); + if (pci_changed) { + nr_rrc_du_container_t *du = get_du_by_assoc_id(rrc, cell->assoc_id); + if (du != NULL) { + // Check if new PCI would conflict with another cell in this DU (excluding current cell) + nr_rrc_cell_container_t *existing_pci = rrc_get_cell_by_pci_for_du(&du->cells, new_ci->nr_pci); + if (existing_pci != NULL && existing_pci != cell) { + LOG_E(NR_RRC, + "Cannot change PCI from %d to %d: PCI %d already exists in this DU (cell ID %lu)\n", + cell->info.pci, + new_ci->nr_pci, + new_ci->nr_pci, + existing_pci->info.cell_id); + return NULL; + } + } + } + + // Check if cell_id will change - if so, need to update tree structure + bool cell_id_changed = (old_nci != new_ci->nr_cellid); + if (cell_id_changed) { + // Check if new cell_id would create a duplicate (excluding current cell) + nr_rrc_cell_container_t *existing = get_cell_by_cell_id(cells, new_ci->nr_cellid); + if (existing != NULL && existing != cell) { + LOG_E(NR_RRC, "Cannot change cell_id from %ld to %ld: new cell_id already exists\n", old_nci, new_ci->nr_cellid); + return NULL; + } + // Remove from tree before updating cell_id + nr_rrc_cell_container_t *removed = RB_REMOVE(rrc_cell_tree, cells, cell); + AssertFatal(removed == cell, "Failed to remove cell %ld from tree\n", old_nci); + } + + // Free old MTC before updating + if (new_ci->measurement_timing_config_len > 0) + ASN_STRUCT_FREE(asn_DEF_NR_MeasurementTimingConfiguration, cell->mtc); + + // Update cell information from the new F1AP served cell info + cp_f1_served_cell_info_to_cell(cell, new_ci); + + // If cell_id changed, re-insert into tree with new cell_id + if (cell_id_changed) { + nr_rrc_cell_container_t *existing = RB_INSERT(rrc_cell_tree, cells, cell); + AssertFatal(existing == NULL, "Failed to insert cell %ld into tree\n", cell->info.cell_id); + LOG_I(NR_RRC, "Updated cell_id from %ld to %ld\n", old_nci, cell->info.cell_id); + } + + return cell; } void rrc_gNB_process_f1_du_configuration_update(f1ap_gnb_du_configuration_update_t *conf_up, sctp_assoc_t assoc_id) @@ -522,53 +604,84 @@ void rrc_gNB_process_f1_du_configuration_update(f1ap_gnb_du_configuration_update nr_rrc_du_container_t *du = get_du_by_assoc_id(rrc, assoc_id); AssertError(du != NULL, return, "no DU found for assoc_id %d\n", assoc_id); - - const f1ap_served_cell_info_t *info = &du->setup_req->cell[0].info; - if (conf_up->num_cells_to_add > 0) { - // Here we check if the number of cell limit is respectet, otherwise send failure - LOG_W(NR_RRC, "du_configuration_update->cells_to_add_list is not supported yet"); + // Validate each cell to add for uniqueness (cell_id globally; PCI within this DU) + for (uint16_t i = 0; i < conf_up->num_cells_to_add; i++) { + f1ap_served_cell_info_t *cell_info = &conf_up->cell_to_add[i].info; + // Check for duplicate cell_id globally + nr_rrc_cell_container_t *existing = get_cell_by_cell_id(&rrc->cells, cell_info->nr_cellid); + if (existing != NULL) { + nr_rrc_du_container_t *existing_du = get_du_by_assoc_id(rrc, existing->assoc_id); + const char *du_name = existing_du ? existing_du->gNB_DU_name : "unknown"; + LOG_E(NR_RRC, + "Cell ID %lu already exists in DU %s (assoc_id %d), rejecting cell addition\n", + cell_info->nr_cellid, + du_name, + existing->assoc_id); + /** @todo send failure message */ + return; + } + // Check for duplicate PCI within this DU + nr_rrc_cell_container_t *existing_pci = rrc_get_cell_by_pci_for_du(&du->cells, cell_info->nr_pci); + if (existing_pci != NULL) { + LOG_E(NR_RRC, + "PCI %d already exists in this DU (cell ID %lu), rejecting cell addition (cell ID %lu)\n", + cell_info->nr_pci, + existing_pci->info.cell_id, + cell_info->nr_cellid); + /** @todo send failure message */ + return; + } } if (conf_up->num_cells_to_modify > 0) { // here the old nrcgi is used to find the cell information, if it exist then we modify consequently otherwise we fail AssertFatal(conf_up->num_cells_to_modify == 1, "cannot handle more than one cell!\n"); - - if (info->nr_cellid != conf_up->cell_to_modify[0].old_nr_cellid) { - LOG_W(NR_RRC, "no cell with ID %ld found, ignoring gNB-DU configuration update\n", conf_up->cell_to_modify[0].old_nr_cellid); - return; - } + const uint64_t old_nci = conf_up->cell_to_modify[0].old_nr_cellid; + const f1ap_served_cell_info_t *new_ci = &conf_up->cell_to_modify[0].info; // verify the new plmn of the cell - if (!rrc_gNB_plmn_matches(rrc, &conf_up->cell_to_modify[0].info)) { + if (!rrc_gNB_plmn_matches(rrc, new_ci)) { LOG_W(NR_RRC, "PLMN does not match, ignoring gNB-DU configuration update\n"); return; } - update_cell_info(du, &conf_up->cell_to_modify[0].info); + nr_rrc_cell_container_t *cell = update_cell_info(rrc, old_nci, new_ci); + if (cell == NULL) { + LOG_W(NR_RRC, "Failed to update cell %ld, ignoring gNB-DU configuration update\n", old_nci); + return; + } + if (cell->assoc_id != du->assoc_id) { + LOG_W(NR_RRC, + "cell %ld belongs to different DU (assoc_id %d vs %d), ignoring gNB-DU configuration update\n", + old_nci, + cell->assoc_id, + du->assoc_id); + return; + } const f1ap_gnb_du_system_info_t *sys_info = conf_up->cell_to_modify[0].sys_info; if (sys_info != NULL && sys_info->mib != NULL && !(sys_info->sib1 == NULL && IS_SA_MODE(get_softmodem_params()))) { // MIB is mandatory, so will be overwritten. SIB1 is optional, so will // only be overwritten if present in sys_info - ASN_STRUCT_FREE(asn_DEF_NR_MIB, du->mib); + ASN_STRUCT_FREE(asn_DEF_NR_MIB, cell->mib); if (sys_info->sib1 != NULL) { - ASN_STRUCT_FREE(asn_DEF_NR_SIB1, du->sib1); - du->sib1 = NULL; + ASN_STRUCT_FREE(asn_DEF_NR_SIB1, cell->sib1); + cell->sib1 = NULL; } NR_MIB_t *mib = NULL; - if (!extract_sys_info(sys_info, &mib, &du->sib1)) { - LOG_W(NR_RRC, "cannot update sys_info for DU %ld\n", du->setup_req->gNB_DU_id); + if (!extract_sys_info(sys_info, &mib, &cell->sib1)) { + LOG_W(NR_RRC, "cannot update sys_info for DU %ld\n", du->gNB_DU_id); } else { DevAssert(mib != NULL); - du->mib = mib; - LOG_I(NR_RRC, "update system information of DU %ld\n", du->setup_req->gNB_DU_id); + cell->mib = mib; + LOG_I(NR_RRC, "update system information of DU %ld\n", du->gNB_DU_id); } } - if (du->mib != NULL && du->sib1 != NULL) { - const f1ap_served_cell_info_t *cell_info = &du->setup_req->cell[0].info; - label_intra_frequency_neighbours(rrc, du, cell_info); + + if (cell && cell->mib != NULL && cell->sib1 != NULL && cell->assoc_id == du->assoc_id) { + label_intra_frequency_neighbours(rrc, cell); } } @@ -601,18 +714,8 @@ void rrc_CU_process_f1_lost_connection(gNB_RRC_INST *rrc, f1ap_lost_connection_t return; } - f1ap_setup_req_t *req = du->setup_req; - LOG_I(NR_RRC, "releasing DU ID %ld (%s) on assoc_id %d\n", req->gNB_DU_id, req->gNB_DU_name, assoc_id); - ASN_STRUCT_FREE(asn_DEF_NR_MIB, du->mib); - ASN_STRUCT_FREE(asn_DEF_NR_SIB1, du->sib1); - ASN_STRUCT_FREE(asn_DEF_NR_MeasurementTimingConfiguration, du->mtc); - if (du->setup_req) - free_f1ap_setup_request(du->setup_req); - free(du->setup_req); - nr_rrc_du_container_t *removed = RB_REMOVE(rrc_du_tree, &rrc->dus, du); - DevAssert(removed != NULL); - free(du); - rrc->num_dus--; + // Clean up DU and associated cell resources + rrc_cleanup_du(rrc, du); int num = invalidate_du_connections(rrc, assoc_id); if (num > 0) { @@ -628,52 +731,53 @@ nr_rrc_du_container_t *get_du_for_ue(gNB_RRC_INST *rrc, uint32_t ue_id) return get_du_by_assoc_id(rrc, ue_data.du_assoc_id); } -nr_rrc_du_container_t *get_du_by_assoc_id(gNB_RRC_INST *rrc, sctp_assoc_t assoc_id) -{ - nr_rrc_du_container_t e = {.assoc_id = assoc_id}; - return RB_FIND(rrc_du_tree, &rrc->dus, &e); -} - -/* \brief find DU by cell ID. Note: currently the CU is limited to one cell per - * DU, hence here, DU == cell. Modify this to look up a specific cell. */ -nr_rrc_du_container_t *get_du_by_cell_id(gNB_RRC_INST *rrc, uint64_t cell_id) -{ - nr_rrc_du_container_t *du = NULL; - RB_FOREACH(du, rrc_du_tree, &rrc->dus) { - if (cell_id == du->setup_req->cell[0].info.nr_cellid) - return du; - } - return NULL; -} - +/** @brief Dump DU and cell information to file + * @param[in] rrc RRC instance + * @param[in] f File handle to write to + * @note Complexity: O(d + N_CELL) = O(N_CELL) where d = number of DUs, N_CELL = total number of cells across all DUs + * @note Called periodically (every 1 second) for stats logging */ void dump_du_info(const gNB_RRC_INST *rrc, FILE *f) { fprintf(f, "%ld connected DUs \n", rrc->num_dus); - int i = 1; + int du_idx = 1; nr_rrc_du_container_t *du = NULL; - /* cast is necessary to eliminate warning "discards ‘const’ qualifier" */ + /* cast is necessary to eliminate warning "discards 'const' qualifier" */ RB_FOREACH(du, rrc_du_tree, &((gNB_RRC_INST *)rrc)->dus) { - const f1ap_setup_req_t *sr = du->setup_req; - fprintf(f, "[%d] DU ID %ld (%s) ", i++, sr->gNB_DU_id, sr->gNB_DU_name); + fprintf(f, "[%d] DU ID %ld (%s) ", du_idx++, du->gNB_DU_id, du->gNB_DU_name); if (du->assoc_id == -1) { fprintf(f, "integrated DU-CU"); } else { fprintf(f, "assoc_id %d", du->assoc_id); } - const f1ap_served_cell_info_t *info = &sr->cell[0].info; - fprintf(f, ": nrCellID %ld, PCI %d, SSB ARFCN %d\n", info->nr_cellid, info->nr_pci, get_ssb_arfcn(du)); - if (info->mode == F1AP_MODE_TDD) { - const f1ap_nr_frequency_info_t *fi = &info->tdd.freqinfo; - const f1ap_transmission_bandwidth_t *tb = &info->tdd.tbw; - fprintf(f, " TDD: band %d ARFCN %d SCS %d (kHz) PRB %d\n", fi->band, fi->arfcn, 15 * (1 << tb->scs), tb->nrb); + /* Print all cells for this DU */ + int cell_count = seq_arr_size(&du->cells); + + if (cell_count == 0) { + fprintf(f, ": no cells\n"); } else { - const f1ap_nr_frequency_info_t *dfi = &info->fdd.dl_freqinfo; - const f1ap_transmission_bandwidth_t *dtb = &info->fdd.dl_tbw; - fprintf(f, " FDD: DL band %d ARFCN %d SCS %d (kHz) PRB %d\n", dfi->band, dfi->arfcn, 15 * (1 << dtb->scs), dtb->nrb); - const f1ap_nr_frequency_info_t *ufi = &info->fdd.ul_freqinfo; - const f1ap_transmission_bandwidth_t *utb = &info->fdd.ul_tbw; - fprintf(f, " UL band %d ARFCN %d SCS %d (kHz) PRB %d\n", ufi->band, ufi->arfcn, 15 * (1 << utb->scs), utb->nrb); + fprintf(f, ": %d cell%s\n", cell_count, cell_count > 1 ? "s" : ""); + int cell_idx = 1; + FOR_EACH_SEQ_ARR (nr_rrc_cell_container_t **, cell_ptr, &du->cells) { + nr_rrc_cell_container_t *cell = *cell_ptr; + const char *mode = (cell->info.mode == NR_MODE_TDD) ? "TDD" : "FDD"; + fprintf(f, + " [%d] nrCellID %lu, PCI %d, Mode %s, SSB ARFCN %d\n", + cell_idx++, + cell->info.cell_id, + cell->info.pci, + mode, + ssb_arfcn_mtc(cell->mtc)); + if (cell->info.mode == NR_MODE_TDD) { + const nr_rrc_freq_info_t *tdd = &cell->info.tdd.dlul; + fprintf(f, " TDD: band %d ARFCN %d SCS %d (kHz) PRB %d\n", tdd->band, tdd->arfcn, 15 * (1 << tdd->scs), tdd->nrb); + } else { + const nr_rrc_freq_info_t *dl = &cell->info.fdd.dl; + const nr_rrc_freq_info_t *ul = &cell->info.fdd.ul; + fprintf(f, " DL: band %d ARFCN %d SCS %d (kHz) PRB %d\n", dl->band, dl->arfcn, 15 * (1 << dl->scs), dl->nrb); + fprintf(f, " UL: band %d ARFCN %d SCS %d (kHz) PRB %d\n", ul->band, ul->arfcn, 15 * (1 << ul->scs), ul->nrb); + } + } } } } diff --git a/openair2/RRC/NR/rrc_gNB_du.h b/openair2/RRC/NR/rrc_gNB_du.h index 514416211f..8942af9af5 100644 --- a/openair2/RRC/NR/rrc_gNB_du.h +++ b/openair2/RRC/NR/rrc_gNB_du.h @@ -30,6 +30,7 @@ struct f1ap_setup_req_s; struct f1ap_lost_connection_t; struct gNB_RRC_INST_s; struct nr_rrc_du_container_t; +struct nr_rrc_cell_container_t; struct f1ap_gnb_du_configuration_update_s; struct f1ap_served_cell_info_t; @@ -38,15 +39,10 @@ void rrc_CU_process_f1_lost_connection(struct gNB_RRC_INST_s *rrc, struct f1ap_l void rrc_gNB_process_f1_du_configuration_update(struct f1ap_gnb_du_configuration_update_s *conf_up, sctp_assoc_t assoc_id); struct nr_rrc_du_container_t *get_du_for_ue(struct gNB_RRC_INST_s *rrc, uint32_t ue_id); -struct nr_rrc_du_container_t *get_du_by_assoc_id(struct gNB_RRC_INST_s *rrc, sctp_assoc_t assoc_id); -const struct f1ap_served_cell_info_t *get_cell_information_by_phycellId(int phyCellId); -struct nr_rrc_du_container_t *get_du_by_cell_id(struct gNB_RRC_INST_s *rrc, uint64_t cell_id); void dump_du_info(const struct gNB_RRC_INST_s *rrc, FILE *f); -int get_dl_band(const struct f1ap_served_cell_info_t *cell_info); -int get_ssb_scs(const struct f1ap_served_cell_info_t *cell_info); -int get_ssb_arfcn(const struct nr_rrc_du_container_t *du); +int get_ssb_arfcn(const struct nr_rrc_cell_container_t *cell); struct nr_rrc_du_container_t *find_target_du(struct gNB_RRC_INST_s *rrc, sctp_assoc_t source_assoc_id); diff --git a/openair2/RRC/NR/rrc_gNB_mobility.c b/openair2/RRC/NR/rrc_gNB_mobility.c index a2230c1146..d18937fbc5 100644 --- a/openair2/RRC/NR/rrc_gNB_mobility.c +++ b/openair2/RRC/NR/rrc_gNB_mobility.c @@ -27,6 +27,7 @@ #include "nr_rrc_proto.h" #include "rrc_gNB_du.h" +#include "rrc_cell_management.h" #include "rrc_gNB_radio_bearers.h" #include "rrc_gNB_UE_context.h" #include "openair2/LAYER2/NR_MAC_COMMON/nr_mac.h" @@ -37,6 +38,7 @@ #include "openair3/SECU/key_nas_deriver.h" #include "openair2/RRC/NR/rrc_gNB_NGAP.h" #include "NR_DL-DCCH-MessageType.h" +#include "rrc_cell_management.h" #ifdef E2_AGENT #include "openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc_extern.h" @@ -80,15 +82,12 @@ void nr_rrc_apply_target_context(gNB_RRC_UE_t *UE) /* update F1 data: secondary UE association and DU association */ ue_data.secondary_ue = target_ctx->du_ue_id; - ue_data.du_assoc_id = target_ctx->du->assoc_id; + ue_data.du_assoc_id = target_ctx->cell->assoc_id; bool success = cu_update_f1_ue_data(UE->rrc_ue_id, &ue_data); DevAssert(success); /* update UE RNTI */ UE->rnti = target_ctx->new_rnti; - - /* update UE NR cell ID */ - UE->nr_cellid = target_ctx->du->setup_req->cell[0].info.nr_cellid; } /* \brief Initiate a handover of UE to a specific target cell handled by this @@ -101,8 +100,7 @@ void nr_rrc_apply_target_context(gNB_RRC_UE_t *UE) * \param ho_ctxt contextual data for the type of handover (F1, N2, Xn) */ static void nr_initiate_handover(const gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, - const nr_rrc_du_container_t *source_du, - const nr_rrc_du_container_t *target_du, + const nr_rrc_cell_container_t *source_cell, byte_array_t *ho_prep_info, ho_req_ack_t ack, ho_success_t success, @@ -111,10 +109,10 @@ static void nr_initiate_handover(const gNB_RRC_INST *rrc, { DevAssert(rrc != NULL); DevAssert(ue != NULL); - DevAssert(target_du != NULL); - // source_du might be NULL -> inter-CU handover + // source_cell might be NULL -> inter-CU handover DevAssert(ho_prep_info->buf != NULL && ho_prep_info->len > 0); DevAssert(ue->ho_context); + DevAssert(ue->ho_context->target != NULL); // if any reconfiguration is ongoing, abort handover request for (int i = 0; i < NR_RRC_TRANSACTION_IDENTIFIER_NUMBER; ++i) { @@ -125,7 +123,9 @@ static void nr_initiate_handover(const gNB_RRC_INST *rrc, } nr_handover_context_t *ho_ctx = ue->ho_context; - ho_ctx->target->du = target_du; + const nr_rrc_cell_container_t *target_cell = ho_ctx->target->cell; + DevAssert(target_cell != NULL); + // we will know target->{du_ue_id,new_rnti} once we have UE ctxt setup // response ho_ctx->target->ho_req_ack = ack; @@ -133,14 +133,14 @@ static void nr_initiate_handover(const gNB_RRC_INST *rrc, ho_ctx->target->ho_failure = failure; const f1_ue_data_t ue_data = cu_get_f1_ue_data(ue->rrc_ue_id); - if (source_du != NULL) { - DevAssert(source_du->assoc_id == ue_data.du_assoc_id); - // we also have the source DU (F1 handover), store meta info + if (source_cell != NULL) { + DevAssert(source_cell->assoc_id == ue_data.du_assoc_id); ho_ctx->source->ho_cancel = cancel; - ho_ctx->source->du = source_du; + ho_ctx->source->cell = source_cell; ho_ctx->source->du_ue_id = ue_data.secondary_ue; ho_ctx->source->old_rnti = ue->rnti; + DevAssert(source_cell->assoc_id == ue_data.du_assoc_id); // Save the GTP-U tunnel info for source DU before process UE context setup request/response // since tunnel info will be updated by calling store_du_f1u_tunnel() in rrc_CU_process_ue_context_setup_response() @@ -153,14 +153,14 @@ static void nr_initiate_handover(const gNB_RRC_INST *rrc, } LOG_A(NR_RRC, - "Handover triggered for UE %u/RNTI %04x towards DU %ld/assoc_id %d/PCI %d\n", + "Handover triggered for UE %u/RNTI %04x towards cell %ld/assoc_id %d/PCI %d\n", ue->rrc_ue_id, ue->rnti, - target_du->setup_req->gNB_DU_id, - target_du->assoc_id, - target_du->setup_req->cell[0].info.nr_pci); + target_cell->info.cell_id, + target_cell->assoc_id, + target_cell->info.pci); - rrc_f1_ue_context_setup_for_target_du(rrc, ue, target_du, ho_prep_info); + rrc_f1_ue_context_setup_for_target_du(rrc, ue, target_cell, ho_prep_info); } typedef struct deliver_ue_ctxt_modification_data_t { @@ -249,15 +249,19 @@ static void nr_rrc_f1_ho_complete(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE) DevAssert(UE->ho_context != NULL); nr_ho_source_cu_t *source_ctx = UE->ho_context->source; DevAssert(source_ctx != NULL); - RETURN_IF_INVALID_ASSOC_ID(source_ctx->du->assoc_id); + RETURN_IF_INVALID_ASSOC_ID(source_ctx->cell->assoc_id); f1ap_ue_context_rel_cmd_t cmd = { .gNB_CU_ue_id = UE->rrc_ue_id, .gNB_DU_ue_id = source_ctx->du_ue_id, .cause = F1AP_CAUSE_RADIO_NETWORK, .cause_value = 5, // 5 = F1AP_CauseRadioNetwork_interaction_with_other_procedure }; - rrc->mac_rrc.ue_context_release_command(source_ctx->du->assoc_id, &cmd); - LOG_I(NR_RRC, "UE %d Handover: trigger release on DU assoc_id %d\n", UE->rrc_ue_id, source_ctx->du->assoc_id); + rrc->mac_rrc.ue_context_release_command(source_ctx->cell->assoc_id, &cmd); + LOG_I(NR_RRC, + "UE %d Handover: trigger release on cell PCI %d/assoc_id %d\n", + UE->rrc_ue_id, + source_ctx->cell->info.pci, + source_ctx->cell->assoc_id); } static void nr_rrc_cancel_f1_ho(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE) @@ -271,10 +275,13 @@ static void nr_rrc_cancel_f1_ho(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE) .cause = F1AP_CAUSE_RADIO_NETWORK, // better .cause_value = 5, // 5 = F1AP_CauseRadioNetwork_interaction_with_other_procedure }; - rrc->mac_rrc.ue_context_release_command(target_ctx->du->assoc_id, &cmd); + rrc->mac_rrc.ue_context_release_command(target_ctx->cell->assoc_id, &cmd); } -void nr_rrc_trigger_f1_ho(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, nr_rrc_du_container_t *source_du, nr_rrc_du_container_t *target_du) +void nr_rrc_trigger_f1_ho(gNB_RRC_INST *rrc, + gNB_RRC_UE_t *ue, + const nr_rrc_cell_container_t *source_cell, + const nr_rrc_cell_container_t *target_cell) { DevAssert(rrc != NULL); DevAssert(ue != NULL); @@ -288,6 +295,7 @@ void nr_rrc_trigger_f1_ho(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, nr_rrc_du_contain return; } ue->ho_context = alloc_ho_ctx(HO_CTX_BOTH); + ue->ho_context->target->cell = target_cell; // corresponds to a "handover request", 38.300 Sec 9.3.2.3 // see also 38.413 Sec 9.3.1.29 for information on source-CU to target-CU @@ -299,7 +307,7 @@ void nr_rrc_trigger_f1_ho(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, nr_rrc_du_contain ho_success_t success = nr_rrc_f1_ho_complete; ho_cancel_t cancel = nr_rrc_cancel_f1_ho; byte_array_t hpi = {.buf = buf, .len = size}; - nr_initiate_handover(rrc, ue, source_du, target_du, &hpi, ack, success, cancel, NULL); + nr_initiate_handover(rrc, ue, source_cell, &hpi, ack, success, cancel, NULL); } void nr_rrc_finalize_ho(gNB_RRC_UE_t *ue) @@ -324,6 +332,11 @@ void nr_HO_F1_trigger_telnet(gNB_RRC_INST *rrc, uint32_t rrc_ue_id) LOG_E(NR_RRC, "cannot get source gNB-DU with assoc_id %d for UE %u\n", ue_data.du_assoc_id, ue->rrc_ue_id); return; } + nr_rrc_cell_container_t *source_cell = rrc_get_pcell_for_ue(rrc, ue); + if (source_cell == NULL) { + LOG_E(NR_RRC, "cannot get source cell for UE %u\n", ue->rrc_ue_id); + return; + } nr_rrc_du_container_t *target_du = find_target_du(rrc, source_du->assoc_id); if (target_du == NULL) { @@ -331,7 +344,19 @@ void nr_HO_F1_trigger_telnet(gNB_RRC_INST *rrc, uint32_t rrc_ue_id) return; } - nr_rrc_trigger_f1_ho(rrc, ue, source_du, target_du); + // For target cell, get the first cell from target DU + // (in future, this could be selected based on measurement) + nr_rrc_cell_container_t *target_cell = NULL; + FOR_EACH_SEQ_ARR (nr_rrc_cell_container_t **, cell_ptr, &target_du->cells) { + target_cell = *cell_ptr; + break; // Get first cell + } + if (target_cell == NULL) { + LOG_E(NR_RRC, "cannot get target cell for UE %u\n", ue->rrc_ue_id); + return; + } + + nr_rrc_trigger_f1_ho(rrc, ue, source_cell, target_cell); } /** @brief Generate the HandoverPreparationInformation to be carried @@ -395,6 +420,74 @@ static byte_array_t rrc_gNB_encode_HandoverCommand(gNB_RRC_UE_t *UE, gNB_RRC_INS return out; } +/** @brief Update cell association after handover: set PCell on target DU + * @param[in] rrc RRC instance + * @param[in] UE UE context + * @return true on success, false on failure + * + * Generic for all handover types (F1, N2, Xn). Behaviour depends on whether + * ho_context->source is set on this CU: + * - Source present (intra-CU): F1 HO, remove all serving cells belonging to + * the source DU, then add target cell as PCell. + * - Source absent (inter-CU): N2 or Xn handover (target CU created new UE + * on Handover Request). This UE context has no serving cells yet; add + * target cell as first PCell. */ +bool nr_rrc_update_cell_assoc_after_ho(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE) +{ + DevAssert(UE->ho_context); + DevAssert(UE->ho_context->target); + nr_ho_target_cu_t *target_ctx = UE->ho_context->target; + + /* Pre-check: ensure F1 UE data matches target cell before mutating state. */ + if (!cu_exists_f1_ue_data(UE->rrc_ue_id)) { + LOG_E(NR_RRC, "UE %d handover: no F1 UE data, cannot add PCell\n", UE->rrc_ue_id); + return false; + } + f1_ue_data_t ue_data = cu_get_f1_ue_data(UE->rrc_ue_id); + if (ue_data.du_assoc_id != target_ctx->cell->assoc_id) { + LOG_E(NR_RRC, + "UE %d handover: target cell assoc_id %d does not match F1 du_assoc_id %d\n", + UE->rrc_ue_id, + target_ctx->cell->assoc_id, + ue_data.du_assoc_id); + return false; + } + + if (UE->ho_context->source) { + /* F1 HO (intra-CU): migrate serving cells from source DU to target cell */ + nr_ho_source_cu_t *source_ctx = UE->ho_context->source; + + // Remove SCells that belong to the source DU during handover (cell group changes) + // This also removes PCell if it was on the source DU + rrc_remove_ue_scells_from_du(UE, source_ctx->cell->assoc_id); + + // Update PCell in serving_cells + ue_serving_cell_t *added = rrc_add_ue_serving_cell(UE, target_ctx->cell, RRC_PCELL_INDEX); + if (added == NULL) { + LOG_E(NR_RRC, + "UE %d handover: failed to add PCell (cell %ld) to serving cells\n", + UE->rrc_ue_id, + target_ctx->cell->info.cell_id); + return false; + } + } else { + /* Inter-CU (N2/Xn target): this UE context has no serving cells yet */ + const ue_serving_cell_t *existing_pcell = ue_get_pcell_entry(UE); + AssertFatal(existing_pcell == NULL, "UE context should not have PCell yet (inter-CU target)\n"); + + ue_serving_cell_t *added = rrc_add_ue_serving_cell(UE, target_ctx->cell, RRC_PCELL_INDEX); + if (added == NULL) { + LOG_E(NR_RRC, + "UE %d handover: failed to add PCell (cell %ld) to serving cells\n", + UE->rrc_ue_id, + target_ctx->cell->info.cell_id); + return false; + } + } + + return true; +} + /** @brief This callback is used by the target gNB * to trigger the Handover Request Acknowledge towards the AMF */ static void nr_rrc_n2_ho_acknowledge(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE) @@ -407,13 +500,23 @@ static void nr_rrc_n2_ho_acknowledge(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE) AssertFatal(previous_data.secondary_ue == -1, "there was already a DU present\n"); nr_rrc_apply_target_context(UE); + /* Update cell association after handover */ + nr_ho_target_cu_t *target = UE->ho_context->target; + if (!nr_rrc_update_cell_assoc_after_ho(rrc, UE)) { + ngap_handover_failure_t fail = {.amf_ue_ngap_id = UE->amf_ue_ngap_id, + .cause.type = NGAP_CAUSE_RADIO_NETWORK, + .cause.value = NGAP_CAUSE_RADIO_NETWORK_HO_FAILURE_IN_TARGET_5GC_NGRAN_NODE_OR_TARGET_SYSTEM}; + target->ho_failure(rrc, UE->rrc_ue_id, &fail); + return; + } + byte_array_t hoCommand = rrc_gNB_encode_HandoverCommand(UE, rrc); if (hoCommand.len < 0) { LOG_E(NR_RRC, "ASN1 message encoding failed: failed to generate Handover Command Message\n"); ngap_handover_failure_t fail = {.amf_ue_ngap_id = UE->amf_ue_ngap_id, .cause.type = NGAP_CAUSE_RADIO_NETWORK, .cause.value = NGAP_CAUSE_RADIO_NETWORK_HO_FAILURE_IN_TARGET_5GC_NGRAN_NODE_OR_TARGET_SYSTEM}; - UE->ho_context->target->ho_failure(rrc, UE->rrc_ue_id, &fail); + target->ho_failure(rrc, UE->rrc_ue_id, &fail); return; } else { LOG_D(NR_RRC, "HO LOG: Handover Command for UE %u Encoded (%ld bytes)\n", UE->rrc_ue_id, hoCommand.len); @@ -463,8 +566,7 @@ void nr_rrc_trigger_n2_ho_target(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue) ho_success_t success = nr_rrc_n2_ho_complete; ho_failure_t failure = nr_rrc_n2_ho_failure; - const nr_rrc_du_container_t *target_du = get_du_for_ue(rrc, ue->rrc_ue_id); - nr_initiate_handover(rrc, ue, NULL, target_du, &ue->ho_context->target->ue_ho_prep_info, ack, success, NULL, failure); + nr_initiate_handover(rrc, ue, NULL, &ue->ho_context->target->ue_ho_prep_info, ack, success, NULL, failure); FREE_AND_ZERO_BYTE_ARRAY(ue->ho_context->target->ue_ho_prep_info); NR_UE_NR_Capability_t *ue_cap = get_ue_nr_capability(ue->rnti, ue->ue_cap_buffer.buf, ue->ue_cap_buffer.len); @@ -495,7 +597,13 @@ void nr_rrc_trigger_n2_ho(gNB_RRC_INST *rrc, } ue->ho_context = alloc_ho_ctx(HO_CTX_SOURCE); - ue->ho_context->source->du = get_du_for_ue(rrc, ue->rrc_ue_id); + nr_rrc_cell_container_t *source_cell = rrc_get_pcell_for_ue(rrc, ue); + if (source_cell == NULL) { + LOG_E(NR_RRC, "No source cell found for UE %d\n", ue->rrc_ue_id); + free_byte_array(hoPrepInfo); + return; + } + ue->ho_context->source->cell = source_cell; ue->ho_context->source->ho_status_transfer = rrc_gNB_send_NGAP_ul_ran_status_transfer; ue->ho_context->source->ho_cancel = nr_rrc_n2_ho_cancel; @@ -514,42 +622,51 @@ void nr_HO_N2_trigger_telnet(gNB_RRC_INST *rrc, uint32_t neighbour_pci, uint32_t return; } gNB_RRC_UE_t *UE = &ue_context_p->ue_context; - - struct nr_rrc_du_container_t *du = get_du_for_ue(rrc, rrc_ue_id); + nr_rrc_du_container_t *du = get_du_for_ue(rrc, UE->rrc_ue_id); if (du == NULL) { LOG_E(NR_RRC, "N2 HO trigger failed for UE %d: Unknown DU\n", rrc_ue_id); return; } - uint16_t scell_pci = du->setup_req->cell[0].info.nr_pci; + nr_rrc_cell_container_t *pcell = rrc_get_pcell_for_ue(rrc, UE); + if (pcell == NULL) { + LOG_E(NR_RRC, "N2 HO trigger failed for UE %d: Unknown cell\n", rrc_ue_id); + return; + } + uint16_t scell_pci = pcell->info.pci; // Simulate handover on the same cell (testing purposes) if (neighbour_pci == scell_pci) { LOG_I(NR_RRC, "UE %d: trigger handover on the same cell PCI=%d\n", rrc_ue_id, neighbour_pci); nr_neighbour_cell_t neighbourConfig = { .isIntraFrequencyNeighbour = true, - .gNB_ID = du->setup_req->gNB_DU_id, - .nrcell_id = du->setup_req->cell[0].info.nr_cellid, - .physicalCellId = du->setup_req->cell[0].info.nr_pci, - .plmn = du->setup_req->cell[0].info.plmn, - .subcarrierSpacing = du->setup_req->cell[0].info.tdd.tbw.scs, + .gNB_ID = du->gNB_DU_id, + .nrcell_id = pcell->info.cell_id, + .physicalCellId = pcell->info.pci, + .plmn = pcell->info.plmn, + .subcarrierSpacing = pcell->info.mode == NR_MODE_TDD ? pcell->info.tdd.dlul.scs : pcell->info.fdd.dl.scs, }; nr_rrc_trigger_n2_ho(rrc, UE, neighbour_pci, &neighbourConfig); return; } - const f1ap_served_cell_info_t *scell_du = get_cell_information_by_phycellId(scell_pci); - DevAssert(scell_du); LOG_I(NR_RRC, "UE %d: triggered N2 HO, source PCI=%d to target PCI=%d\n", rrc_ue_id, scell_pci, neighbour_pci); - const neighbour_cell_configuration_t *cell = get_neighbour_cell_config(rrc, scell_du->nr_cellid); - const nr_neighbour_cell_t *neighbour = get_neighbour_cell_by_pci(cell, neighbour_pci); + const neighbour_cell_configuration_t *cell = get_neighbour_cell_config(rrc, pcell->info.cell_id); + if (cell == NULL) { + LOG_E(NR_RRC, + "N2 HO trigger failed for UE %d: could not find neighbour cell with Cell ID=%ld\n", + rrc_ue_id, + pcell->info.cell_id); + return; + } + const nr_neighbour_cell_t *neighbour = get_neighbour_cell_by_pci(cell, neighbour_pci); if (neighbour == NULL) { LOG_E(NR_RRC, "N2 HO trigger failed for UE %d: could not find neighbour cell with PCI=%d\n", rrc_ue_id, neighbour_pci); return; } - nr_rrc_trigger_n2_ho(rrc, UE, scell_du->nr_pci, neighbour); + nr_rrc_trigger_n2_ho(rrc, UE, pcell->info.pci, neighbour); } // This function detects if there are at least two different ssbFrequency values, and if so, returns meas_timing_config; diff --git a/openair2/RRC/NR/rrc_gNB_mobility.h b/openair2/RRC/NR/rrc_gNB_mobility.h index 456730060e..d4a51efeb9 100644 --- a/openair2/RRC/NR/rrc_gNB_mobility.h +++ b/openair2/RRC/NR/rrc_gNB_mobility.h @@ -28,7 +28,7 @@ /* forward declarations */ typedef struct gNB_RRC_INST_s gNB_RRC_INST; typedef struct gNB_RRC_UE_s gNB_RRC_UE_t; -typedef struct nr_rrc_du_container_t nr_rrc_du_container_t; +typedef struct nr_rrc_cell_container_t nr_rrc_cell_container_t; typedef struct NR_CellGroupConfig NR_CellGroupConfig_t; @@ -40,8 +40,8 @@ typedef int (*ho_status_transfer_t)(gNB_RRC_INST *rrc, const e1_pdcp_status_info_t *pdcp_status); typedef struct nr_ho_source_cu { - /// pointer to the (source) DU structure - const nr_rrc_du_container_t *du; + /// pointer to the (source) cell container + const nr_rrc_cell_container_t *cell; /// (source) DU UE ID; in F1, the DU UE ID will change to the new (target) DU /// UE ID during handover, and the CU needs to keep track of this in case of /// reestablishment @@ -67,8 +67,8 @@ typedef void (*ho_failure_t)(gNB_RRC_INST *rrc, uint32_t gnb_ue_id, ngap_handove typedef void (*ho_trigger_t)(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue); typedef struct nr_ho_target_cu { - /// pointer to the (target) DU structure - const nr_rrc_du_container_t *du; + /// pointer to the (target) cell container + const nr_rrc_cell_container_t *cell; /// (target) DU UE ID; the (source) DU UE ID will change to the new (target) /// DU UE ID after sending a reconfiguration, which is later than when /// receiving this ID @@ -95,7 +95,10 @@ typedef struct nr_handover_context_s { typedef enum { HO_CTX_BOTH, HO_CTX_SOURCE, HO_CTX_TARGET } ho_ctx_type_t; nr_handover_context_t *alloc_ho_ctx(ho_ctx_type_t type); -void nr_rrc_trigger_f1_ho(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, nr_rrc_du_container_t *source_du, nr_rrc_du_container_t *target_du); +void nr_rrc_trigger_f1_ho(gNB_RRC_INST *rrc, + gNB_RRC_UE_t *ue, + const nr_rrc_cell_container_t *source_cell, + const nr_rrc_cell_container_t *target_cell); void nr_rrc_finalize_ho(gNB_RRC_UE_t *ue); void nr_rrc_n2_ho_failure(gNB_RRC_INST *rrc, uint32_t gnb_ue_id, ngap_handover_failure_t *msg); @@ -112,4 +115,6 @@ byte_array_t *get_meas_timing_config(const NR_MeasurementTimingConfiguration_t * void nr_rrc_apply_target_context(gNB_RRC_UE_t *UE); +bool nr_rrc_update_cell_assoc_after_ho(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE); + #endif /* RRC_GNB_MOBILITY_H_ */ diff --git a/openair2/RRC/NR/rrc_gNB_nsa.c b/openair2/RRC/NR/rrc_gNB_nsa.c index 88f06338d7..18513d14c5 100644 --- a/openair2/RRC/NR/rrc_gNB_nsa.c +++ b/openair2/RRC/NR/rrc_gNB_nsa.c @@ -62,6 +62,7 @@ #include "openair3/SECU/key_nas_deriver.h" #include "openair2/SDAP/nr_sdap/nr_sdap_entity.h" #include "rrc_gNB_du.h" +#include "rrc_cell_management.h" #include "rlc.h" #include "s1ap_messages_types.h" #include "tree.h" @@ -368,20 +369,37 @@ void rrc_add_nsa_user(gNB_RRC_INST *rrc, x2ap_ENDC_sgnb_addition_req_t *m, sctp_ *ue_agg_mbr_ul = 1000000000; byte_array_t *cg_configinfo = malloc_or_fail(sizeof(*cg_configinfo)); *cg_configinfo = cgci; + + f1_ue_data_t ue_data = cu_get_f1_ue_data(UE->rrc_ue_id); + RETURN_IF_INVALID_ASSOC_ID(ue_data.du_assoc_id); + nr_rrc_du_container_t *du = get_du_by_assoc_id(rrc, ue_data.du_assoc_id); + if (!du) { + LOG_E(NR_RRC, "UE %d: no valid cell for UE context setup (DU assoc_id %d not found)\n", UE->rrc_ue_id, ue_data.du_assoc_id); + return; + } + // Get the first cell from the DU (NSA assumption: 1 cell per DU) + if (seq_arr_size(&du->cells) == 0) { + LOG_E(NR_RRC, "UE %d: no cells available in DU (assoc_id %d)\n", UE->rrc_ue_id, ue_data.du_assoc_id); + return; + } + nr_rrc_cell_container_t *ue_cell = *(nr_rrc_cell_container_t **)seq_arr_at(&du->cells, 0); + // Add the cell as PCell + if (rrc_add_ue_serving_cell(UE, ue_cell, RRC_PCELL_INDEX) == NULL) { + LOG_E(NR_RRC, "UE %d: failed to add PCell (cell %ld) for UE context setup\n", UE->rrc_ue_id, ue_cell->info.cell_id); + return; + } f1ap_ue_context_setup_req_t req = { .gNB_CU_ue_id = UE->rrc_ue_id, .plmn.mcc = rrc->configuration.plmn[0].mcc, .plmn.mnc = rrc->configuration.plmn[0].mnc, .plmn.mnc_digit_length = rrc->configuration.plmn[0].mnc_digit_length, - .nr_cellid = rrc->nr_cellid, - .servCellIndex = 0, + .nr_cellid = ue_cell->info.cell_id, + .servCellIndex = RRC_PCELL_INDEX, .drbs_len = 1, .drbs = drb, .cu_to_du_rrc_info.cg_configinfo = cg_configinfo, .gnb_du_ue_agg_mbr_ul = ue_agg_mbr_ul, }; - f1_ue_data_t ue_data = cu_get_f1_ue_data(UE->rrc_ue_id); - RETURN_IF_INVALID_ASSOC_ID(ue_data.du_assoc_id); rrc->mac_rrc.ue_context_setup_request(ue_data.du_assoc_id, &req); free_ue_context_setup_req(&req); } diff --git a/openair2/RRC/NR/tests/CMakeLists.txt b/openair2/RRC/NR/tests/CMakeLists.txt index f7a4a95b1b..f68501c99e 100644 --- a/openair2/RRC/NR/tests/CMakeLists.txt +++ b/openair2/RRC/NR/tests/CMakeLists.txt @@ -5,3 +5,19 @@ add_test(NAME rrc_bearers_test COMMAND rrc_bearers_test) target_include_directories(rrc_bearers_test PRIVATE ./common/utils/ds common/utils/LOG) target_link_libraries(rrc_bearers_test PRIVATE ds alg asn1_e1ap asn1_lte_rrc_hdrs asn1_nr_rrc_hdrs LOG T) target_compile_definitions(rrc_bearers_test PRIVATE ENABLE_TESTS) + +add_executable(rrc_cell_management_test rrc_cell_management_test.c) +add_dependencies(tests rrc_cell_management_test) +add_test(NAME rrc_cell_management_test COMMAND rrc_cell_management_test) +target_include_directories(rrc_cell_management_test PRIVATE ./common/utils/ds common/utils/LOG) +target_link_libraries(rrc_cell_management_test PRIVATE + rrc_cell_management + f1ap + ds + alg + LOG + utils + asn1_nr_rrc_hdrs + asn1_lte_rrc_hdrs +) +target_compile_definitions(rrc_cell_management_test PRIVATE ENABLE_TESTS) diff --git a/openair2/RRC/NR/tests/rrc_cell_management_test.c b/openair2/RRC/NR/tests/rrc_cell_management_test.c new file mode 100644 index 0000000000..b3aa5e6881 --- /dev/null +++ b/openair2/RRC/NR/tests/rrc_cell_management_test.c @@ -0,0 +1,460 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#include +#include +#include +#include "common/utils/utils.h" // for calloc_or_fail +#include "common/utils/assertions.h" // for AssertFatal, DevAssert +#include "common/utils/collection/tree.h" // for RB tree macros +#include "openair2/RRC/NR/rrc_cell_management.h" // for cell management functions +#include "openair2/RRC/NR/rrc_gNB_du.h" // for get_du_by_assoc_id +#include "common/utils/LOG/log.h" // for LOG_I, LOG_A, etc. +#include "common/utils/ds/seq_arr.h" // for seq_arr_free +#include "openair2/F1AP/f1ap_ids.h" // for cu_init_f1_ue_data, cu_add_f1_ue_data + +// Test constants +static const sctp_assoc_t ASSOC_ID_DU1 = 8; +static const sctp_assoc_t ASSOC_ID_DU2 = 9; +static const sctp_assoc_t ASSOC_ID_DU3 = 10; +static const uint64_t DU_ID_1 = 1001; +static const uint64_t DU_ID_2 = 1002; +static const uint64_t DU_ID_3 = 1003; +static const uint64_t CELL_ID_1 = 12345678; +static const uint64_t CELL_ID_2 = 87654321; +static const uint64_t CELL_ID_3 = 11111111; +static const uint64_t CELL_ID_4 = 22222222; +static const uint64_t CELL_ID_5 = 33333333; +static const uint64_t CELL_ID_6 = 44444444; +static const uint64_t CELL_ID_7 = 55555555; +static const uint64_t CELL_ID_NONEXISTENT = 99999999; +static const uint16_t PCI_1 = 1; +static const uint16_t PCI_2 = 4; +static const uint16_t PCI_3 = 5; +static const uint16_t PCI_10 = 10; +static const uint16_t PCI_20 = 20; +static const uint16_t PCI_30 = 30; +static const uint16_t PCI_40 = 40; +static const uint16_t PCI_NONEXISTENT = 99; +static const uint8_t SERVING_CELL_ID_SCELL1 = 1; +static const uint8_t SERVING_CELL_ID_SCELL2 = 2; +static const uint32_t UE_ID = 1; + +// Mock functions and variables +configmodule_interface_t *uniqCfg = NULL; +softmodem_params_t *get_softmodem_params(void) +{ + return NULL; +} + +void exit_function(const char *file, const char *function, const int line, const char *s, const int assert) +{ + printf("detected error at %s:%d:%s: %s\n", file, line, function, s); + abort(); +} + +// Helper function to create a test cell container +static nr_rrc_cell_container_t *create_test_cell(uint64_t cell_id, uint16_t pci, sctp_assoc_t assoc_id) +{ + nr_rrc_cell_container_t *cell = calloc_or_fail(1, sizeof(*cell)); + cell->info.cell_id = cell_id; + cell->info.pci = pci; + cell->assoc_id = assoc_id; + cell->info.mode = NR_MODE_TDD; + cell->info.plmn.mcc = 222; + cell->info.plmn.mnc = 1; + cell->info.tac = 1; + return cell; +} + +// Helper function to create a test DU container +static nr_rrc_du_container_t *create_test_du(sctp_assoc_t assoc_id, uint64_t du_id, const char *name) +{ + nr_rrc_du_container_t *du = calloc_or_fail(1, sizeof(*du)); + du->assoc_id = assoc_id; + du->gNB_DU_id = du_id; + du->gNB_DU_name = strdup(name); + AssertFatal(du->gNB_DU_name != NULL, "strdup failed for DU name"); + seq_arr_init(&du->cells, sizeof(nr_rrc_cell_container_t *)); + return du; +} + +// Helper function to add DU to RRC instance +static void add_du_to_rrc(gNB_RRC_INST *rrc, nr_rrc_du_container_t *du) +{ + nr_rrc_du_container_t *collision = rrc_add_du(rrc, du); + AssertFatal(collision == NULL, "rrc_add_du should succeed for new DU (assoc_id %d)", du->assoc_id); +} + +// Helper function to find cell by PCI in global tree (for testing PCI reuse across network) +// Returns first match; PCI is not globally unique +static nr_rrc_cell_container_t *get_cell_by_pci(struct rrc_cell_tree *cells, const uint16_t pci) +{ + nr_rrc_cell_container_t *cell = NULL; + RB_FOREACH (cell, rrc_cell_tree, cells) { + if (cell->info.pci == pci) + return cell; + } + return NULL; +} + +/** @brief Test DU lookup by assoc_id + * @note Tests the functions: + * - get_du_by_assoc_id() */ +static void test_du_lookup(void) +{ + LOG_I(NR_RRC, "Testing DU lookup by assoc_id\n"); + + gNB_RRC_INST rrc = {0}; + RB_INIT(&rrc.dus); + RB_INIT(&rrc.cells); + + /* Sub-test 1: Create multiple DUs and insert into RRC DU tree */ + struct { + nr_rrc_du_container_t *du; + sctp_assoc_t assoc_id; + uint64_t du_id; + } dus[] = { + {create_test_du(ASSOC_ID_DU1, DU_ID_1, "DU-1"), ASSOC_ID_DU1, DU_ID_1}, + {create_test_du(ASSOC_ID_DU2, DU_ID_2, "DU-2"), ASSOC_ID_DU2, DU_ID_2}, + {create_test_du(ASSOC_ID_DU3, DU_ID_3, "DU-3"), ASSOC_ID_DU3, DU_ID_3}, + }; + + for (size_t i = 0; i < sizeof(dus) / sizeof(dus[0]); i++) { + add_du_to_rrc(&rrc, dus[i].du); + } + + /* Sub-test 2: Test lookup existing DUs */ + for (size_t i = 0; i < sizeof(dus) / sizeof(dus[0]); i++) { + nr_rrc_du_container_t *found = get_du_by_assoc_id(&rrc, dus[i].assoc_id); + AssertFatal(found == dus[i].du, "DU with assoc_id %d should be found", dus[i].assoc_id); + AssertFatal(found->gNB_DU_id == dus[i].du_id, "DU ID should match (expected %lu, got %lu)", dus[i].du_id, found->gNB_DU_id); + } + + /* Sub-test 3: Test lookup non-existing assoc_id */ + static const sctp_assoc_t ASSOC_ID_NONEXISTENT = 99; + nr_rrc_du_container_t *found = get_du_by_assoc_id(&rrc, ASSOC_ID_NONEXISTENT); + AssertFatal(found == NULL, "Non-existing assoc_id %d should return NULL", ASSOC_ID_NONEXISTENT); + + /* Sub-test 4: Test invalid assoc_id (0 is considered invalid) */ + static const sctp_assoc_t ASSOC_ID_INVALID = 0; + found = get_du_by_assoc_id(&rrc, ASSOC_ID_INVALID); + AssertFatal(found == NULL, "Invalid assoc_id %d should return NULL", ASSOC_ID_INVALID); + + LOG_A(NR_RRC, "DU lookup test passed\n"); + + /* Sub-test 5: Cleanup with free of DUs and their cell containers */ + for (size_t i = 0; i < sizeof(dus) / sizeof(dus[0]); i++) { + rrc_cleanup_du(&rrc, dus[i].du); + } +} + +/** @brief Test cell lookup, cell-DU association, and UE cell association + * @note Tests the functions: + * - get_cell_by_cell_id() + * - rrc_get_cell_for_du() + * - rrc_get_cell_by_pci_for_du() + * - rrc_add_cell_to_du() + * - rrc_add_ue_serving_cell() + * - rrc_get_ue_serving_cell_by_id() + * - rrc_get_pcell_for_ue() + * - rrc_remove_ue_scells_from_du() + * - rrc_free_cell_container() */ +static void test_cell_lookup(void) +{ + LOG_I(NR_RRC, "Testing cell lookup functions\n"); + + cu_init_f1_ue_data(); + + gNB_RRC_INST rrc = {0}; + RB_INIT(&rrc.dus); + RB_INIT(&rrc.cells); + + nr_rrc_du_container_t *du1 = create_test_du(ASSOC_ID_DU1, DU_ID_1, "DU-1"); + nr_rrc_du_container_t *du2 = create_test_du(ASSOC_ID_DU2, DU_ID_2, "DU-2"); + add_du_to_rrc(&rrc, du1); + add_du_to_rrc(&rrc, du2); + + /* Sub-test 1: Create multiple cells per DU and insert into global cell tree and DU's seq_arr */ + struct { + nr_rrc_cell_container_t *cell; + uint64_t cell_id; + uint16_t pci; + nr_rrc_du_container_t *du; + } cells[] = { + {create_test_cell(CELL_ID_1, PCI_10, ASSOC_ID_DU1), CELL_ID_1, PCI_10, du1}, + {create_test_cell(CELL_ID_2, PCI_20, ASSOC_ID_DU1), CELL_ID_2, PCI_20, du1}, + {create_test_cell(CELL_ID_3, PCI_1, ASSOC_ID_DU1), CELL_ID_3, PCI_1, du1}, + {create_test_cell(CELL_ID_4, PCI_2, ASSOC_ID_DU2), CELL_ID_4, PCI_2, du2}, + {create_test_cell(CELL_ID_5, PCI_3, ASSOC_ID_DU2), CELL_ID_5, PCI_3, du2}, + }; + + // Insert cells: check for duplicate cell_id first, then insert into global tree and DU's seq_arr + for (size_t i = 0; i < sizeofArray(cells); i++) { + nr_rrc_cell_container_t *existing = get_cell_by_cell_id(&rrc.cells, cells[i].cell_id); + AssertFatal(existing == NULL, "Cell ID %lu should not exist before insertion", cells[i].cell_id); + // Insert into global cell tree + nr_rrc_cell_container_t *collision = rrc_add_cell(&rrc, cells[i].cell); + AssertFatal(collision == NULL, "rrc_add_cell should succeed for new cell (ID %lu)", cells[i].cell_id); + // Insert into DU's seq_arr + nr_rrc_cell_container_t *added = rrc_add_cell_to_du(&cells[i].du->cells, cells[i].cell); + AssertFatal(added != NULL, "rrc_add_cell_to_du should succeed for new cell (ID %lu)", cells[i].cell_id); + } + + nr_rrc_cell_container_t *found; + /* Sub-test 2: Test lookup by cell_id */ + found = get_cell_by_cell_id(&rrc.cells, CELL_ID_1); + AssertFatal(found != NULL && found == cells[0].cell, "Cell ID %lu should be found", CELL_ID_1); + found = get_cell_by_cell_id(&rrc.cells, CELL_ID_4); + AssertFatal(found != NULL && found == cells[3].cell, "Cell ID %lu should be found", CELL_ID_4); + + /* Sub-test 3: Test lookup by PCI (returns first match) */ + found = get_cell_by_pci(&rrc.cells, PCI_10); + AssertFatal(found != NULL && found->info.cell_id == CELL_ID_1, + "Cell with PCI %d should be found (expected cell_id %lu)", + PCI_10, + CELL_ID_1); + + /* Sub-test 4: Test lookup non-existing cell */ + found = get_cell_by_cell_id(&rrc.cells, CELL_ID_NONEXISTENT); + AssertFatal(found == NULL, "Non-existing cell_id should return NULL"); + found = get_cell_by_pci(&rrc.cells, PCI_NONEXISTENT); + AssertFatal(found == NULL, "Non-existing PCI should return NULL"); + + /* Sub-test 5: Test rrc_get_cell_for_du() - DU-specific cell lookup */ + found = rrc_get_cell_for_du(&du1->cells, CELL_ID_1); + AssertFatal(found != NULL && found->info.cell_id == CELL_ID_1, "rrc_get_cell_for_du should find cell in DU1"); + found = rrc_get_cell_for_du(&du2->cells, CELL_ID_4); + AssertFatal(found != NULL && found->info.cell_id == CELL_ID_4, "rrc_get_cell_for_du should find cell in DU2"); + // Test non-existing cell in DU + nr_rrc_cell_container_t *not_found = rrc_get_cell_for_du(&du1->cells, CELL_ID_NONEXISTENT); + AssertFatal(not_found == NULL, "rrc_get_cell_for_du should return NULL for non-existing cell"); + // Test cell from different DU + not_found = rrc_get_cell_for_du(&du1->cells, CELL_ID_4); + AssertFatal(not_found == NULL, "rrc_get_cell_for_du should return NULL for cell from different DU"); + + /* Sub-test 6: Test rrc_get_cell_by_pci_for_du() - DU-specific PCI lookup */ + nr_rrc_cell_container_t *cell_in_du = rrc_get_cell_by_pci_for_du(&du1->cells, PCI_10); + AssertFatal(cell_in_du != NULL, "rrc_get_cell_by_pci_for_du should find cell with PCI %d in DU1", PCI_10); + AssertFatal(cell_in_du->info.cell_id == CELL_ID_1, + "Found cell should have correct cell_id (expected %lu, got %lu)", + CELL_ID_1, + cell_in_du->info.cell_id); + cell_in_du = rrc_get_cell_by_pci_for_du(&du2->cells, PCI_2); + AssertFatal(cell_in_du != NULL, "rrc_get_cell_by_pci_for_du should find cell with PCI %d in DU2", PCI_2); + AssertFatal(cell_in_du->info.cell_id == CELL_ID_4, + "Found cell should have correct cell_id (expected %lu, got %lu)", + CELL_ID_4, + cell_in_du->info.cell_id); + // Test non-existing PCI in DU + cell_in_du = rrc_get_cell_by_pci_for_du(&du1->cells, PCI_NONEXISTENT); + AssertFatal(cell_in_du == NULL, "rrc_get_cell_by_pci_for_du should return NULL for non-existing PCI"); + // Test PCI from different DU (should not find it in this DU) + cell_in_du = rrc_get_cell_by_pci_for_du(&du1->cells, PCI_2); + AssertFatal(cell_in_du == NULL, "rrc_get_cell_by_pci_for_du should return NULL for PCI from different DU"); + + /* Sub-test 8: Test PCI reuse across different DUs (allowed globally) */ + // Add a cell to DU2 with the same PCI as a cell in DU1 (PCI reuse is allowed globally) + nr_rrc_cell_container_t *cell_on_du2_same_pci = create_test_cell(CELL_ID_6, PCI_10, ASSOC_ID_DU2); + nr_rrc_cell_container_t *already_in_tree = get_cell_by_cell_id(&rrc.cells, CELL_ID_6); + AssertFatal(already_in_tree == NULL, "Cell ID %lu should not exist before insertion", CELL_ID_6); + // Insert into global cell tree (PCI reuse is allowed) + nr_rrc_cell_container_t *tree_collision = rrc_add_cell(&rrc, cell_on_du2_same_pci); + AssertFatal(tree_collision == NULL, "rrc_add_cell should succeed for new cell (ID %lu)", CELL_ID_6); + // Add to DU2 (should succeed - PCI reuse across DUs is allowed) + nr_rrc_cell_container_t *added_to_du2 = rrc_add_cell_to_du(&du2->cells, cell_on_du2_same_pci); + AssertFatal(added_to_du2 != NULL, "rrc_add_cell_to_du should succeed for cell with PCI reused from different DU"); + // Verify both cells with same PCI exist in global tree (fetch by cell_id) + nr_rrc_cell_container_t *cell1 = get_cell_by_cell_id(&rrc.cells, CELL_ID_1); + nr_rrc_cell_container_t *cell6 = get_cell_by_cell_id(&rrc.cells, CELL_ID_6); + AssertFatal(cell1 != NULL && cell6 != NULL, "Both cells with same PCI should exist in global tree"); + AssertFatal(cell1->info.pci == PCI_10 && cell6->info.pci == PCI_10, "Both cells should have PCI %d", PCI_10); + // get_cell_by_pci returns first match only; verify it returns one of them + nr_rrc_cell_container_t *first_match_global = get_cell_by_pci(&rrc.cells, PCI_10); + AssertFatal(first_match_global != NULL, "get_cell_by_pci should find a cell with PCI %d", PCI_10); + AssertFatal(first_match_global == cell1 || first_match_global == cell6, + "get_cell_by_pci should return one of the two cells with PCI %d", + PCI_10); + // Verify each DU can find its own cell by PCI + nr_rrc_cell_container_t *cell_in_du1 = rrc_get_cell_by_pci_for_du(&du1->cells, PCI_10); + AssertFatal(cell_in_du1 != NULL && cell_in_du1->info.cell_id == CELL_ID_1, "DU1 should find its own cell with PCI %d", PCI_10); + nr_rrc_cell_container_t *cell_in_du2 = rrc_get_cell_by_pci_for_du(&du2->cells, PCI_10); + AssertFatal(cell_in_du2 != NULL && cell_in_du2->info.cell_id == CELL_ID_6, "DU2 should find its own cell with PCI %d", PCI_10); + + /* Sub-test 9: Test PCI reuse within same DU (should be rejected) */ + nr_rrc_cell_container_t *cell_with_duplicate_pci = create_test_cell(CELL_ID_NONEXISTENT, PCI_10, ASSOC_ID_DU1); + // Try to add to DU1 - should fail because PCI_10 already exists in DU1 + nr_rrc_cell_container_t *add_result = rrc_add_cell_to_du(&du1->cells, cell_with_duplicate_pci); + AssertFatal(add_result == NULL, "rrc_add_cell_to_du should reject cell with duplicate PCI within same DU"); + rrc_free_cell_container(cell_with_duplicate_pci); + + /* Sub-test 10: Verify cell counts per DU */ + static const int expected_du1_cells = 3; + static const int expected_du2_cells = 3; // includes cell_on_du2_same_pci + size_t count_du1 = seq_arr_size(&du1->cells); + size_t count_du2 = seq_arr_size(&du2->cells); + AssertFatal(count_du1 == expected_du1_cells, "DU 1 should have %d cells (got %zu)", expected_du1_cells, count_du1); + AssertFatal(count_du2 == expected_du2_cells, "DU 2 should have %d cells (got %zu)", expected_du2_cells, count_du2); + + /* Sub-test 11: Test UE cell association - create test UE context */ + gNB_RRC_UE_t ue = {0}; + ue.rrc_ue_id = UE_ID; + seq_arr_init(&ue.serving_cells, sizeof(ue_serving_cell_t)); + + /* Register F1 UE data for this UE at the CU */ + f1_ue_data_t f1_data = { + .secondary_ue = 0, + .e1_assoc_id = 0, + .du_assoc_id = ASSOC_ID_DU1, + }; + bool f1_added = cu_add_f1_ue_data(UE_ID, &f1_data); + AssertFatal(f1_added, "cu_add_f1_ue_data should succeed for test UE"); + + /* Sub-test 12: Test rrc_add_ue_serving_cell() - Add PCell, ue_get_pcell_entry(), rrc_get_pcell_for_ue() */ + ue_serving_cell_t *added_pcell = rrc_add_ue_serving_cell(&ue, cells[0].cell, RRC_PCELL_INDEX); + AssertFatal(added_pcell != NULL && added_pcell->nci == CELL_ID_1 && added_pcell->serving_cell_id == RRC_PCELL_INDEX, + "rrc_add_ue_serving_cell should succeed"); + const ue_serving_cell_t *pcell_entry = ue_get_pcell_entry(&ue); + AssertFatal(pcell_entry != NULL && pcell_entry->nci == CELL_ID_1 && pcell_entry->serving_cell_id == RRC_PCELL_INDEX, + "ue_get_pcell_entry should return correct PCell entry"); + nr_rrc_cell_container_t *pcell = rrc_get_pcell_for_ue(&rrc, &ue); + AssertFatal(pcell == cells[0].cell, "rrc_get_pcell_for_ue should return same cell as PCell"); + + /* Sub-test 13: Test adding SCells and rrc_get_ue_serving_cell_by_id() */ + ue_serving_cell_t *added_scell = rrc_add_ue_serving_cell(&ue, cells[1].cell, SERVING_CELL_ID_SCELL1); + AssertFatal(added_scell != NULL && added_scell->serving_cell_id == SERVING_CELL_ID_SCELL1, + "rrc_add_ue_serving_cell should succeed for SCell"); + AssertFatal(added_scell->assoc_id == ASSOC_ID_DU1, + "SCell should have correct assoc_id (expected %d, got %d)", + ASSOC_ID_DU1, + added_scell->assoc_id); + // Test rrc_get_ue_serving_cell_by_id() for PCell and SCell + ue_serving_cell_t *found_serving_cell = rrc_get_ue_serving_cell_by_id(&ue, RRC_PCELL_INDEX); + AssertFatal(found_serving_cell != NULL && found_serving_cell == added_pcell, "rrc_get_ue_serving_cell_by_id should find PCell"); + found_serving_cell = rrc_get_ue_serving_cell_by_id(&ue, SERVING_CELL_ID_SCELL1); + AssertFatal(found_serving_cell != NULL && found_serving_cell == added_scell, "rrc_get_ue_serving_cell_by_id should find SCell"); + // Test non-existing serving cell ID + ue_serving_cell_t *not_found_serving = rrc_get_ue_serving_cell_by_id(&ue, SERVING_CELL_ID_SCELL2); + AssertFatal(not_found_serving == NULL, "rrc_get_ue_serving_cell_by_id should return NULL for non-existing serving cell ID"); + static const size_t expected_serving_cells_2 = 2; + AssertFatal(seq_arr_size(&ue.serving_cells) == expected_serving_cells_2, + "UE should have %zu serving cells (got %zu)", + expected_serving_cells_2, + seq_arr_size(&ue.serving_cells)); + + // Add second SCell + nr_rrc_cell_container_t *scell2 = create_test_cell(CELL_ID_7, PCI_30, ASSOC_ID_DU1); + nr_rrc_cell_container_t *already_present = get_cell_by_cell_id(&rrc.cells, CELL_ID_7); + AssertFatal(already_present == NULL, "Cell ID %lu should not exist before insertion", CELL_ID_7); + // Insert into global cell tree + nr_rrc_cell_container_t *collision = rrc_add_cell(&rrc, scell2); + AssertFatal(collision == NULL, "rrc_add_cell should succeed for new cell (ID %lu)", CELL_ID_7); + // Insert into DU's seq_arr + nr_rrc_cell_container_t *added_scell2_du = rrc_add_cell_to_du(&du1->cells, scell2); + AssertFatal(added_scell2_du != NULL, "rrc_add_cell_to_du should succeed for new cell (ID %lu)", CELL_ID_7); + ue_serving_cell_t *added_scell2 = rrc_add_ue_serving_cell(&ue, scell2, SERVING_CELL_ID_SCELL2); + AssertFatal(added_scell2 != NULL, "rrc_add_ue_serving_cell should add new SCell"); + AssertFatal(added_scell2->serving_cell_id == SERVING_CELL_ID_SCELL2, "New SCell should have correct serving_cell_id (expected %d, got %d)", SERVING_CELL_ID_SCELL2, added_scell2->serving_cell_id); + AssertFatal(added_scell2->assoc_id == ASSOC_ID_DU1, + "SCell should have correct assoc_id (expected %d, got %d)", + ASSOC_ID_DU1, + added_scell2->assoc_id); + + /* Sub-test 14: Test multiple PCell enforcement - adding another PCell should fail if one already exists */ + nr_rrc_cell_container_t *test_cell = create_test_cell(CELL_ID_NONEXISTENT, PCI_NONEXISTENT, ASSOC_ID_DU1); + AssertFatal(rrc_add_ue_serving_cell(&ue, test_cell, RRC_PCELL_INDEX) == NULL, + "Adding another PCell when one already exists should fail"); + AssertFatal(rrc_get_pcell_for_ue(&rrc, &ue) == cells[0].cell, + "PCell should still be the original one when adding duplicate PCell fails"); + rrc_free_cell_container(test_cell); + + /* Sub-test 15: Test rrc_add_ue_serving_cell() with existing PCell (same nci, should return existing) */ + ue_serving_cell_t *existing_pcell = rrc_add_ue_serving_cell(&ue, cells[0].cell, RRC_PCELL_INDEX); + AssertFatal(existing_pcell != NULL && existing_pcell->nci == CELL_ID_1, + "rrc_add_ue_serving_cell should succeed for existing PCell with same nci"); + AssertFatal(existing_pcell == added_pcell, "Should return existing PCell entry, not create new one"); + + /* Sub-test 16: Test maximum serving cells limit */ + static const uint64_t test_cell_id_base = 10000000; + static const uint8_t starting_serving_cell_id = 3; + nr_rrc_cell_container_t *test_cells[RRC_MAX_NUM_SERVING_CELLS] = {0}; + size_t test_cell_count = 0; + for (uint8_t i = starting_serving_cell_id; i < RRC_MAX_NUM_SERVING_CELLS; i++) { + uint64_t test_cell_id = test_cell_id_base + i; + uint16_t test_pci = PCI_40 + i; + test_cells[test_cell_count] = create_test_cell(test_cell_id, test_pci, ASSOC_ID_DU1); + // Insert into global cell tree + nr_rrc_cell_container_t *existing = rrc_add_cell(&rrc, test_cells[test_cell_count]); + if (existing != NULL) { + LOG_E(NR_RRC, "Cell %lu already exists in tree, skipping insertion\n", test_cell_id); + rrc_free_cell_container(test_cells[test_cell_count]); + test_cells[test_cell_count] = NULL; + break; + } + // Insert into DU's seq_arr + rrc_add_cell_to_du(&du1->cells, test_cells[test_cell_count]); + if (rrc_add_ue_serving_cell(&ue, test_cells[test_cell_count], i) == NULL) + break; + test_cell_count++; + } + size_t final_count = seq_arr_size(&ue.serving_cells); + AssertFatal(final_count <= RRC_MAX_NUM_SERVING_CELLS, + "Should not exceed max serving cells (got %zu, max %d)", + final_count, + RRC_MAX_NUM_SERVING_CELLS); + + /* Sub-test 17: Test rrc_remove_ue_scells_from_du() */ + size_t before_remove = seq_arr_size(&ue.serving_cells); + rrc_remove_ue_scells_from_du(&ue, ASSOC_ID_DU1); + AssertFatal(seq_arr_size(&ue.serving_cells) < before_remove, "Should remove some serving cells"); + AssertFatal(ue_get_pcell_entry(&ue) == NULL, "PCell should be removed when it has matching assoc_id"); + + /* Sub-test 18: Test rrc_get_pcell_for_ue() edge case (UE with no serving cells) */ + gNB_RRC_UE_t ue_empty = {0}; + seq_arr_init(&ue_empty.serving_cells, sizeof(ue_serving_cell_t)); + AssertFatal(rrc_get_pcell_for_ue(&rrc, &ue_empty) == NULL, + "rrc_get_pcell_for_ue should return NULL for UE with no serving cells"); + seq_arr_free(&ue_empty.serving_cells, NULL); + + LOG_A(NR_RRC, "Cell lookup test passed\n"); + + /* Sub-test 19: Cleanup */ + seq_arr_free(&ue.serving_cells, NULL); + rrc_cleanup_du(&rrc, du1); + rrc_cleanup_du(&rrc, du2); +} + +int main() +{ + // Initialize logging system + logInit(); + set_log(NR_RRC, OAILOG_INFO); + + LOG_I(NR_RRC, "Starting RRC Cell Management Tests\n"); + + // DU lookup tests + test_du_lookup(); + // Cell lookup, cell-DU association, and UE cell association tests + test_cell_lookup(); + + LOG_A(NR_RRC, "All RRC Cell Management tests passed successfully!\n"); + return 0; +} diff --git a/openair2/RRC/NR_UE/rrc_UE.c b/openair2/RRC/NR_UE/rrc_UE.c index 4860e0b63e..65a8bf9432 100644 --- a/openair2/RRC/NR_UE/rrc_UE.c +++ b/openair2/RRC/NR_UE/rrc_UE.c @@ -61,7 +61,6 @@ #include "openair3/SECU/key_nas_deriver.h" #include "common/utils/LOG/log.h" -#include "common/utils/LOG/vcd_signal_dumper.h" #ifndef CELLULAR #include "RRC/NR/MESSAGES/asn1_msg.h" @@ -294,8 +293,6 @@ static void nr_rrc_process_ntnconfig(NR_UE_RRC_INST_t *rrc, NR_UE_RRC_SI_INFO *S static void nr_decode_SI(NR_UE_RRC_SI_INFO *SI_info, NR_SystemInformation_t *si, NR_UE_RRC_INST_t *rrc, int hfn, int frame) { - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_RRC_UE_DECODE_SI, VCD_FUNCTION_IN); - // Dump contents if (si->criticalExtensions.present == NR_SystemInformation__criticalExtensions_PR_systemInformation || si->criticalExtensions.present == NR_SystemInformation__criticalExtensions_PR_criticalExtensionsFuture_r16) { @@ -386,7 +383,6 @@ static void nr_decode_SI(NR_UE_RRC_SI_INFO *SI_info, NR_SystemInformation_t *si, sib19_msg->can_start_ra = rrc->is_NTN_UE; nr_rrc_send_msg_to_mac(rrc, &rrc_msg); } - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_RRC_UE_DECODE_SI, VCD_FUNCTION_OUT); } static void nr_rrc_ue_prepare_RRCSetupRequest(NR_UE_RRC_INST_t *rrc) @@ -1006,8 +1002,14 @@ static void nr_rrc_process_reconfigurationWithSync(NR_UE_RRC_INST_t *rrc, } } -static void nr_rrc_cellgroup_configuration(NR_UE_RRC_INST_t *rrc, NR_CellGroupConfig_t *cgConfig, int gNB_index, bool dedicatedsib1) +static bool nr_rrc_cellgroup_configuration(NR_UE_RRC_INST_t *rrc, NR_CellGroupConfig_t *cgConfig, int gNB_index, bool dedicatedsib1) { + if (!check_cellgroup_config(cgConfig)) { + // if we received a configuration not supported or with some wrong combination + // we call the function for RLF (re-establishment if security is activated, going to IDLE otherwise) + handle_rlf_detection(rrc); + return false; + } NR_SpCellConfig_t *spCellConfig = cgConfig->spCellConfig; if(spCellConfig) { NR_ServingCellConfig_t *spCellConfigDedicated = spCellConfig->spCellConfigDedicated; @@ -1059,14 +1061,10 @@ static void nr_rrc_cellgroup_configuration(NR_UE_RRC_INST_t *rrc, NR_CellGroupCo } nr_rrc_manage_rlc_bearers(rrc, cgConfig); - - if (cgConfig->ext1) - AssertFatal(cgConfig->ext1->reportUplinkTxDirectCurrent == NULL, "Reporting of UplinkTxDirectCurrent not implemented\n"); - AssertFatal(cgConfig->sCellToReleaseList == NULL, "Secondary serving cell release not implemented\n"); - AssertFatal(cgConfig->sCellToAddModList == NULL, "Secondary serving cell addition not implemented\n"); + return true; } -static void nr_rrc_ue_process_masterCellGroup(NR_UE_RRC_INST_t *rrc, +static bool nr_rrc_ue_process_masterCellGroup(NR_UE_RRC_INST_t *rrc, OCTET_STRING_t *masterCellGroup, long *fullConfig, int gNB_index) @@ -1080,13 +1078,18 @@ static void nr_rrc_ue_process_masterCellGroup(NR_UE_RRC_INST_t *rrc, masterCellGroup->size, 0, 0); if ((dec_rval.code != RC_OK) && (dec_rval.consumed == 0)) { LOG_E(NR_RRC, "CellGroupConfig decode error\n"); - return; + // if the ASN1 decoding fails for the received CellGroup configuration + // we call the function for RLF (re-establishment if security is activated, going to IDLE otherwise) + handle_rlf_detection(rrc); + return false; } if (LOG_DEBUGFLAG(DEBUG_ASN1)) { xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, (const void *) cellGroupConfig); } - nr_rrc_cellgroup_configuration(rrc, cellGroupConfig, gNB_index, false); + bool ret = nr_rrc_cellgroup_configuration(rrc, cellGroupConfig, gNB_index, false); + if (!ret) + return false; LOG_D(RRC, "Sending CellGroupConfig to MAC the pointer will be managed by mac\n"); nr_mac_rrc_message_t rrc_msg = {0}; @@ -1097,6 +1100,7 @@ static void nr_rrc_ue_process_masterCellGroup(NR_UE_RRC_INST_t *rrc, mac_msg->hfn = rrc->current_hfn; mac_msg->frame = rrc->current_frame; nr_rrc_send_msg_to_mac(rrc, &rrc_msg); + return true; } static bool nr_rrc_process_reconfiguration_v1530(NR_UE_RRC_INST_t *rrc, NR_RRCReconfiguration_v1530_IEs_t *rec_1530, int gNB_index) @@ -1105,8 +1109,11 @@ static bool nr_rrc_process_reconfiguration_v1530(NR_UE_RRC_INST_t *rrc, NR_RRCRe // TODO perform the full configuration procedure as specified in 5.3.5.11 of 331 LOG_E(NR_RRC, "RRCReconfiguration includes fullConfig but this is not implemented yet\n"); } - if (rec_1530->masterCellGroup) - nr_rrc_ue_process_masterCellGroup(rrc, rec_1530->masterCellGroup, rec_1530->fullConfig, gNB_index); + if (rec_1530->masterCellGroup) { + bool ret = nr_rrc_ue_process_masterCellGroup(rrc, rec_1530->masterCellGroup, rec_1530->fullConfig, gNB_index); + if (!ret) + return false; + } if (rec_1530->masterKeyUpdate) { as_security_key_update(rrc, rec_1530->masterKeyUpdate); nr_pdcp_entity_security_keys_and_algos_t sp = get_security_rrc_parameters(rrc, true); @@ -1544,11 +1551,15 @@ static void nr_rrc_ue_process_rrcReconfiguration(NR_UE_RRC_INST_t *rrc, int gNB_ LOG_E(NR_RRC, "\n"); // free the memory SEQUENCE_free(&asn_DEF_NR_CellGroupConfig, (void *)cellGroupConfig, 1); + // if the ASN1 decoding fails for the received CellGroup configuration + // we call the function for RLF (re-establishment if security is activated, going to IDLE otherwise) + handle_rlf_detection(rrc); } else { if (LOG_DEBUGFLAG(DEBUG_ASN1)) xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, (const void *) cellGroupConfig); - nr_rrc_cellgroup_configuration(rrc, cellGroupConfig, gNB_index, dedicatedsib1); + bool ret = nr_rrc_cellgroup_configuration(rrc, cellGroupConfig, gNB_index, dedicatedsib1); + AssertFatal(ret, "CellGroup has wrong configuration for the UE. Unexpected\n"); AssertFatal(!IS_SA_MODE(get_softmodem_params()), "secondaryCellGroup only used in NSA for now\n"); nr_mac_rrc_message_t rrc_msg = {0}; rrc_msg.payload_type = NR_MAC_RRC_CONFIG_CG; @@ -2014,7 +2025,6 @@ static void nr_rrc_ue_decode_NR_BCCH_DL_SCH_Message(NR_UE_RRC_INST_t *rrc, if (Sdu_len == 0) // decoding failed in L2 return; - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_DECODE_BCCH, VCD_FUNCTION_IN); NR_BCCH_DL_SCH_Message_t *bcch_message = NULL; asn_dec_rval_t dec_rval = uper_decode_complete(NULL, &asn_DEF_NR_BCCH_DL_SCH_Message, @@ -2027,7 +2037,6 @@ static void nr_rrc_ue_decode_NR_BCCH_DL_SCH_Message(NR_UE_RRC_INST_t *rrc, log_dump(NR_RRC, Sdu, Sdu_len, LOG_DUMP_CHAR," Received bytes:\n"); // free the memory SEQUENCE_free(&asn_DEF_NR_BCCH_DL_SCH_Message, (void *)bcch_message, 1); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_UE_DECODE_BCCH, VCD_FUNCTION_OUT ); return; } @@ -2053,7 +2062,6 @@ static void nr_rrc_ue_decode_NR_BCCH_DL_SCH_Message(NR_UE_RRC_INST_t *rrc, } } SEQUENCE_free(&asn_DEF_NR_BCCH_DL_SCH_Message, bcch_message, ASFM_FREE_EVERYTHING); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_UE_DECODE_BCCH, VCD_FUNCTION_OUT ); } static void rrc_ue_generate_RRCSetupComplete(const NR_UE_RRC_INST_t *rrc, const uint8_t Transaction_id) @@ -2152,7 +2160,9 @@ static void nr_rrc_process_rrcsetup(NR_UE_RRC_INST_t *rrc, const NR_RRCSetup_t * nr_rrc_rrcsetup_fallback(rrc); // perform the cell group configuration procedure in accordance with the received masterCellGroup - nr_rrc_ue_process_masterCellGroup(rrc, &rrcSetup->criticalExtensions.choice.rrcSetup->masterCellGroup, NULL, 0); + bool ret = nr_rrc_ue_process_masterCellGroup(rrc, &rrcSetup->criticalExtensions.choice.rrcSetup->masterCellGroup, NULL, 0); + if (!ret) + return; // perform the radio bearer configuration procedure in accordance with the received radioBearerConfig nr_rrc_ue_process_RadioBearerConfig(rrc, &rrcSetup->criticalExtensions.choice.rrcSetup->radioBearerConfig); @@ -2232,7 +2242,6 @@ static int8_t nr_rrc_ue_decode_ccch(NR_UE_RRC_INST_t *rrc, const NRRrcMacCcchDat NR_DL_CCCH_Message_t *dl_ccch_msg = NULL; asn_dec_rval_t dec_rval; int rval=0; - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_DECODE_CCCH, VCD_FUNCTION_IN); LOG_D(RRC, "[NR UE%ld] Decoding DL-CCCH message (%d bytes), State %d\n", rrc->ue_id, ind->sdu_size, rrc->nrRrcState); dec_rval = uper_decode(NULL, &asn_DEF_NR_DL_CCCH_Message, (void **)&dl_ccch_msg, ind->sdu, ind->sdu_size, 0, 0); @@ -2242,7 +2251,6 @@ static int8_t nr_rrc_ue_decode_ccch(NR_UE_RRC_INST_t *rrc, const NRRrcMacCcchDat if ((dec_rval.code != RC_OK) && (dec_rval.consumed == 0)) { LOG_E(RRC, "[UE %ld] Failed to decode DL-CCCH-Message (%zu bytes)\n", rrc->ue_id, dec_rval.consumed); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_DECODE_CCCH, VCD_FUNCTION_OUT); return -1; } @@ -2273,7 +2281,6 @@ static int8_t nr_rrc_ue_decode_ccch(NR_UE_RRC_INST_t *rrc, const NRRrcMacCcchDat } ASN_STRUCT_FREE(asn_DEF_NR_DL_CCCH_Message, dl_ccch_msg); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_DECODE_CCCH, VCD_FUNCTION_OUT); return rval; } diff --git a/openair2/RRC/NR_UE/rrc_proto.h b/openair2/RRC/NR_UE/rrc_proto.h index ab14f704b9..34b3d7e2c7 100644 --- a/openair2/RRC/NR_UE/rrc_proto.h +++ b/openair2/RRC/NR_UE/rrc_proto.h @@ -99,7 +99,7 @@ void nr_rrc_ue_decode_NR_SBCCH_SL_BCH_Message(NR_UE_RRC_INST_t *rrc, const uint16_t rx_slss_id); void nr_rrc_set_mac_queue(instance_t instance, notifiedFIFO_t *mac_input_nf); - +bool check_cellgroup_config(const NR_CellGroupConfig_t *cgConfig); /** @}*/ #endif diff --git a/openair2/RRC/NR_UE/verify_RRC.c b/openair2/RRC/NR_UE/verify_RRC.c new file mode 100644 index 0000000000..b33da53dae --- /dev/null +++ b/openair2/RRC/NR_UE/verify_RRC.c @@ -0,0 +1,314 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + + #include "rrc_proto.h" + +static bool check_resourcesForInterference(const NR_CSI_MeasConfig_t *meas, NR_CSI_ResourceConfigId_t res_id, bool is_CSIIM) +{ + NR_CSI_ResourceConfig_t *res = NULL; + if (meas->csi_ResourceConfigToAddModList) { + for (int i = 0; i < meas->csi_ResourceConfigToAddModList->list.count; i++) { + if (meas->csi_ResourceConfigToAddModList->list.array[i]->csi_ResourceConfigId == res_id) + res = meas->csi_ResourceConfigToAddModList->list.array[i]; + } + } + if (!res) { + LOG_E(NR_RRC, "No CSI-Resource matching with resourcesForInterference set in report config\n"); + return false; + } else { + if (is_CSIIM) { + if (res->csi_RS_ResourceSetList.present != NR_CSI_ResourceConfig__csi_RS_ResourceSetList_PR_csi_IM_ResourceSetList) { + LOG_E(NR_RRC, "resourcesForInterference doesn't point to CSI-IM resource\n"); + return false; + } + } else { + if (res->csi_RS_ResourceSetList.present != NR_CSI_ResourceConfig__csi_RS_ResourceSetList_PR_nzp_CSI_RS_SSB + || !res->csi_RS_ResourceSetList.choice.nzp_CSI_RS_SSB->nzp_CSI_RS_ResourceSetList) { + LOG_E(NR_RRC, "NZP CSI-RS resourcesForInterference doesn't point to NZP CSI-RS resource\n"); + return false; + } + } + } + return true; +} + +static bool check_resourcesForChannelMeasurement(const NR_CSI_MeasConfig_t *meas, + NR_CSI_ResourceConfigId_t res_id, + struct NR_CSI_ReportConfig__reportQuantity quantity) +{ + NR_CSI_ResourceConfig_t *res = NULL; + if (meas->csi_ResourceConfigToAddModList) { + for (int i = 0; i < meas->csi_ResourceConfigToAddModList->list.count; i++) { + if (meas->csi_ResourceConfigToAddModList->list.array[i]->csi_ResourceConfigId == res_id) + res = meas->csi_ResourceConfigToAddModList->list.array[i]; + } + } + if (!res) { + LOG_E(NR_RRC, "No CSI-Resource matching with resourcesForChannelMeasurement set in report config\n"); + return false; + } else { + if (res->csi_RS_ResourceSetList.present != NR_CSI_ResourceConfig__csi_RS_ResourceSetList_PR_nzp_CSI_RS_SSB) { + LOG_E(NR_RRC, "resourcesForChannelMeasurement doesn't point to CSI-RS-SSB resource\n"); + return false; + } else { + if (!res->csi_RS_ResourceSetList.choice.nzp_CSI_RS_SSB->nzp_CSI_RS_ResourceSetList + && (quantity.present != NR_CSI_ReportConfig__reportQuantity_PR_ssb_Index_RSRP + && quantity.present != NR_CSI_ReportConfig__reportQuantity_PR_NOTHING + && quantity.present != NR_CSI_ReportConfig__reportQuantity_PR_none)) { + LOG_E(NR_RRC, "resourcesForChannelMeasurement is for CRI measurement but doen't point to an CRI-RS resource\n"); + return false; + } + } + } + return true; +} + +static bool check_csi_resourceMapping_consistency(NR_CSI_RS_ResourceMapping_t resourceMapping) +{ + bool valid = true; + struct NR_CSI_RS_ResourceMapping__density *density = &resourceMapping.density; + switch(resourceMapping.frequencyDomainAllocation.present) { + case NR_CSI_RS_ResourceMapping__frequencyDomainAllocation_PR_row1: + if (resourceMapping.nrofPorts != NR_CSI_RS_ResourceMapping__nrofPorts_p1) + valid = false; + else if (resourceMapping.cdm_Type != NR_CSI_RS_ResourceMapping__cdm_Type_noCDM) + valid = false; + else if (density->present != NR_CSI_RS_ResourceMapping__density_PR_three + && density->present != NR_CSI_RS_ResourceMapping__density_PR_NOTHING) + valid = false; + break; + case NR_CSI_RS_ResourceMapping__frequencyDomainAllocation_PR_row2: + if (resourceMapping.nrofPorts != NR_CSI_RS_ResourceMapping__nrofPorts_p1) + valid = false; + else if (resourceMapping.cdm_Type != NR_CSI_RS_ResourceMapping__cdm_Type_noCDM) + valid = false; + else if (density->present != NR_CSI_RS_ResourceMapping__density_PR_dot5 + && density->present != NR_CSI_RS_ResourceMapping__density_PR_one) + valid = false; + break; + case NR_CSI_RS_ResourceMapping__frequencyDomainAllocation_PR_row4: + if (resourceMapping.nrofPorts != NR_CSI_RS_ResourceMapping__nrofPorts_p4) + valid = false; + else if (resourceMapping.cdm_Type != NR_CSI_RS_ResourceMapping__cdm_Type_fd_CDM2) + valid = false; + else if (density->present != NR_CSI_RS_ResourceMapping__density_PR_one + && density->present != NR_CSI_RS_ResourceMapping__density_PR_NOTHING) + valid = false; + break; + case NR_CSI_RS_ResourceMapping__frequencyDomainAllocation_PR_other: + switch(resourceMapping.nrofPorts) { + case NR_CSI_RS_ResourceMapping__nrofPorts_p1: + valid = false; // 1 port is row 1 or 2 not other + break; + case NR_CSI_RS_ResourceMapping__nrofPorts_p2: + if (resourceMapping.cdm_Type != NR_CSI_RS_ResourceMapping__cdm_Type_fd_CDM2) + valid = false; + else if (density->present != NR_CSI_RS_ResourceMapping__density_PR_dot5 + && density->present != NR_CSI_RS_ResourceMapping__density_PR_one) + valid = false; + break; + case NR_CSI_RS_ResourceMapping__nrofPorts_p4: + // row 5 -> l0+1 + if (resourceMapping.firstOFDMSymbolInTimeDomain == 13) + valid = false; + else if (resourceMapping.cdm_Type != NR_CSI_RS_ResourceMapping__cdm_Type_fd_CDM2) + valid = false; + else if (density->present != NR_CSI_RS_ResourceMapping__density_PR_one + && density->present != NR_CSI_RS_ResourceMapping__density_PR_NOTHING) + valid = false; + break; + case NR_CSI_RS_ResourceMapping__nrofPorts_p8: { + uint8_t freq = BIT_STRING_to_uint8(&resourceMapping.frequencyDomainAllocation.choice.other); + if (resourceMapping.cdm_Type == NR_CSI_RS_ResourceMapping__cdm_Type_cdm4_FD2_TD2 || count_bits(&freq, 1) != 4) { + // row 7 and 8 -> l0+1 + if (resourceMapping.firstOFDMSymbolInTimeDomain == 13) + valid = false; + } + if (resourceMapping.cdm_Type != NR_CSI_RS_ResourceMapping__cdm_Type_fd_CDM2 + && resourceMapping.cdm_Type != NR_CSI_RS_ResourceMapping__cdm_Type_cdm4_FD2_TD2) + valid = false; + else if (density->present != NR_CSI_RS_ResourceMapping__density_PR_one + && density->present != NR_CSI_RS_ResourceMapping__density_PR_NOTHING) + valid = false; + break; + } + case NR_CSI_RS_ResourceMapping__nrofPorts_p12: + if (resourceMapping.cdm_Type == NR_CSI_RS_ResourceMapping__cdm_Type_cdm4_FD2_TD2) { + // row 10 -> l0+1 + if (resourceMapping.firstOFDMSymbolInTimeDomain == 13) + valid = false; + } else if (resourceMapping.cdm_Type != NR_CSI_RS_ResourceMapping__cdm_Type_fd_CDM2) + valid = false; + else if (density->present != NR_CSI_RS_ResourceMapping__density_PR_one + && density->present != NR_CSI_RS_ResourceMapping__density_PR_NOTHING) + valid = false; + break; + case NR_CSI_RS_ResourceMapping__nrofPorts_p16: + // l0 + 1 in all cases + if (resourceMapping.firstOFDMSymbolInTimeDomain == 13) + valid = false; + else if (resourceMapping.cdm_Type != NR_CSI_RS_ResourceMapping__cdm_Type_fd_CDM2 + && resourceMapping.cdm_Type != NR_CSI_RS_ResourceMapping__cdm_Type_cdm4_FD2_TD2) + valid = false; + else if (density->present != NR_CSI_RS_ResourceMapping__density_PR_dot5 + && density->present != NR_CSI_RS_ResourceMapping__density_PR_one) + valid = false; + case NR_CSI_RS_ResourceMapping__nrofPorts_p24: + case NR_CSI_RS_ResourceMapping__nrofPorts_p32: + if (resourceMapping.cdm_Type == NR_CSI_RS_ResourceMapping__cdm_Type_noCDM) + valid = false; + else if (resourceMapping.cdm_Type == NR_CSI_RS_ResourceMapping__cdm_Type_cdm4_FD2_TD2 + || resourceMapping.cdm_Type == NR_CSI_RS_ResourceMapping__cdm_Type_fd_CDM2) { + // row 13, 14 and 16, 17 -> l0+1 and l1 (which should be larger than l0+1 + if (resourceMapping.firstOFDMSymbolInTimeDomain == 13) + valid = false; + if (!resourceMapping.firstOFDMSymbolInTimeDomain2 + || *resourceMapping.firstOFDMSymbolInTimeDomain2 <= resourceMapping.firstOFDMSymbolInTimeDomain + 1) + valid = false; + } else { + // row 15 and 18 -> l0+3 + if (resourceMapping.firstOFDMSymbolInTimeDomain > 10) + valid = false; + } + if (valid + && (density->present != NR_CSI_RS_ResourceMapping__density_PR_dot5 + && density->present != NR_CSI_RS_ResourceMapping__density_PR_one)) + valid = false; + break; + default: + valid = false; + } + break; + default: + valid = false; + } + if (!valid) + return false; + return true; +} + +static bool check_csi_report_consistency(const NR_CSI_MeasConfig_t *meas) +{ + if (!meas || !meas->csi_ReportConfigToAddModList) + return true; + for (int i = 0; i < meas->csi_ReportConfigToAddModList->list.count; i++) { + NR_CSI_ReportConfig_t *csirep = meas->csi_ReportConfigToAddModList->list.array[i]; + if (!check_resourcesForChannelMeasurement(meas, csirep->resourcesForChannelMeasurement, csirep->reportQuantity)) + return false; + if (csirep->csi_IM_ResourcesForInterference) { + if (!check_resourcesForInterference(meas, *csirep->csi_IM_ResourcesForInterference, true)) + return false; + } + if (csirep->nzp_CSI_RS_ResourcesForInterference) { + if (!check_resourcesForInterference(meas, *csirep->nzp_CSI_RS_ResourcesForInterference, false)) + return false; + } + } + return true; +} + +static bool check_csi_resource_consistency(const NR_CSI_MeasConfig_t *meas) +{ + if (!meas || !meas->nzp_CSI_RS_ResourceToAddModList) + return true; + for (int i = 0; i < meas->nzp_CSI_RS_ResourceToAddModList->list.count; i++) { + NR_NZP_CSI_RS_Resource_t *res = meas->nzp_CSI_RS_ResourceToAddModList->list.array[i]; + if (!check_csi_resourceMapping_consistency(res->resourceMapping)) + return false; + } + return true; +} + +static bool check_csi_MeasConfig(struct NR_SetupRelease_CSI_MeasConfig *csi_MeasConfig) +{ + const NR_CSI_MeasConfig_t *meas = csi_MeasConfig->choice.setup; + if (!check_csi_report_consistency(meas)) + return false; + if (!check_csi_resource_consistency(meas)) + return false; + return true; +} + +static bool check_srs_config(NR_SRS_Config_t *srs_Config) +{ + if (srs_Config->srs_ResourceToAddModList) { + for (int i = 0; i < srs_Config->srs_ResourceToAddModList->list.count; i++) { + NR_SRS_Resource_t *res = srs_Config->srs_ResourceToAddModList->list.array[i]; + int start = NR_NUMBER_OF_SYMBOLS_PER_SLOT - res->resourceMapping.startPosition - 1; + int num = 1 << res->resourceMapping.nrofSymbols; + if (start + num > NR_NUMBER_OF_SYMBOLS_PER_SLOT) { + LOG_E(NR_RRC, "The configured SRS resource exceeds the slot boundary\n"); + return false; + } + } + } + return true; +} + +static bool check_ul_bwp_config(NR_BWP_UplinkDedicated_t *bwp_Config) +{ + if (bwp_Config->srs_Config && bwp_Config->srs_Config->choice.setup) { + if (!check_srs_config(bwp_Config->srs_Config->choice.setup)) + return false; + } + return true; +} + +bool check_cellgroup_config(const NR_CellGroupConfig_t *cgConfig) +{ + if (cgConfig->spCellConfig && cgConfig->spCellConfig->spCellConfigDedicated) { + NR_ServingCellConfig_t *spCellConfigDedicated = cgConfig->spCellConfig->spCellConfigDedicated; + if (spCellConfigDedicated->csi_MeasConfig) { + if (!check_csi_MeasConfig(spCellConfigDedicated->csi_MeasConfig)) + return false; + } + if (spCellConfigDedicated->uplinkConfig) { + NR_UplinkConfig_t *ul_Config = spCellConfigDedicated->uplinkConfig; + if (ul_Config->initialUplinkBWP) { + if (!check_ul_bwp_config(ul_Config->initialUplinkBWP)) + return false; + } + if (ul_Config->uplinkBWP_ToAddModList) { + for (int i = 0; i < ul_Config->uplinkBWP_ToAddModList->list.count; i++) { + NR_BWP_Uplink_t *ul_bwp = ul_Config->uplinkBWP_ToAddModList->list.array[i]; + if (ul_bwp->bwp_Dedicated) { + if (!check_ul_bwp_config(ul_bwp->bwp_Dedicated)) + return false; + } + } + } + } + } + + if (cgConfig->ext1 && cgConfig->ext1->reportUplinkTxDirectCurrent) { + LOG_E(NR_RRC, "Reporting of UplinkTxDirectCurrent not implemented\n"); + return false; + } + if (cgConfig->sCellToReleaseList) { + LOG_E(NR_RRC, "Secondary serving cell release not implemented\n"); + return false; + } + if (cgConfig->sCellToAddModList) { + LOG_E(NR_RRC, "Secondary serving cell addition not implemented\n"); + return false; + } + return true; +} diff --git a/openair3/NAS/NR_UE/nr_nas_msg.c b/openair3/NAS/NR_UE/nr_nas_msg.c index 8f4f9bd280..64f9feef54 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg.c +++ b/openair3/NAS/NR_UE/nr_nas_msg.c @@ -38,6 +38,7 @@ #include #include #include "common/utils/ds/byte_array.h" +#include "common/utils/nr/nr_common.h" #include "AuthenticationResponseParameter.h" #include "FGCNasMessageContainer.h" #include "FGSDeregistrationRequestUEOriginating.h" @@ -1787,7 +1788,7 @@ static void send_nas_5gmm_ind(instance_t instance, const Guti5GSMobileIdentity_t MessageDef *msg = itti_alloc_new_message(TASK_NAS_NRUE, 0, NAS_5GMM_IND); nas_5gmm_ind_t *ind = &NAS_5GMM_IND(msg); LOG_I(NR_RRC, "5G-GUTI: AMF pointer %u, AMF Set ID %u, 5G-TMSI %u \n", guti->amfpointer, guti->amfsetid, guti->tmsi); - ind->fiveG_STMSI = ((uint64_t)guti->amfsetid << 38) | ((uint64_t)guti->amfpointer << 32) | guti->tmsi; + ind->fiveG_STMSI = nr_construct_5g_s_tmsi(guti->amfsetid, guti->amfpointer, guti->tmsi); itti_send_msg_to_task(TASK_RRC_NRUE, instance, msg); } diff --git a/openair3/NGAP/ngap_gNB_nas_procedures.c b/openair3/NGAP/ngap_gNB_nas_procedures.c index 7bf7753668..8a2cfeead5 100644 --- a/openair3/NGAP/ngap_gNB_nas_procedures.c +++ b/openair3/NGAP/ngap_gNB_nas_procedures.c @@ -1084,7 +1084,7 @@ int ngap_gNB_pdusession_modify_resp(instance_t instance, ngap_pdusession_modify_ ie->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID; ie->criticality = NGAP_Criticality_ignore; ie->value.present = NGAP_PDUSessionResourceModifyResponseIEs__value_PR_AMF_UE_NGAP_ID; - asn_uint642INTEGER(&ie->value.choice.AMF_UE_NGAP_ID, ue_context_p->amf_ue_ngap_id); + asn_uint642INTEGER(&ie->value.choice.AMF_UE_NGAP_ID, pdusession_modify_resp_p->amf_ue_ngap_id); asn1cSeqAdd(&out->protocolIEs.list, ie); } /* mandatory */ @@ -1115,12 +1115,11 @@ int ngap_gNB_pdusession_modify_resp(instance_t instance, ngap_pdusession_modify_ qos->qosFlowIdentifier = pdusession_modify_resp_p->pdusessions[i].qos[qos_flow_index].qfi; } asn_encode_to_new_buffer_result_t res = {0}; - NGAP_PDUSessionResourceModifyResponseTransfer_t *transfer_p = NULL; - res = asn_encode_to_new_buffer(NULL, ATS_ALIGNED_CANONICAL_PER, &asn_DEF_NGAP_PDUSessionResourceModifyResponseTransfer, transfer_p); + res = asn_encode_to_new_buffer(NULL, ATS_ALIGNED_CANONICAL_PER, &asn_DEF_NGAP_PDUSessionResourceModifyResponseTransfer, &transfer); item->pDUSessionResourceModifyResponseTransfer.buf = res.buffer; item->pDUSessionResourceModifyResponseTransfer.size = res.result.encoded; - ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NGAP_PDUSessionResourceModifyResponseTransfer, transfer_p); + ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NGAP_PDUSessionResourceModifyResponseTransfer, &transfer); NGAP_DEBUG("pdusession_modify_resp: modified pdusession ID %ld\n", item->pDUSessionID); } @@ -1143,12 +1142,11 @@ int ngap_gNB_pdusession_modify_resp(instance_t instance, ngap_pdusession_modify_ encode_ngap_cause(&pdusessionTransfer.cause, &pdusession_modify_resp_p->pdusessions_failed[i].cause); asn_encode_to_new_buffer_result_t res = {0}; - NGAP_PDUSessionResourceModifyUnsuccessfulTransfer_t *pdusessionTransfer_p = NULL; res = asn_encode_to_new_buffer(NULL, ATS_ALIGNED_CANONICAL_PER, &asn_DEF_NGAP_PDUSessionResourceModifyUnsuccessfulTransfer, &pdusessionTransfer); item->pDUSessionResourceModifyUnsuccessfulTransfer.buf = res.buffer; item->pDUSessionResourceModifyUnsuccessfulTransfer.size = res.result.encoded; - ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NGAP_PDUSessionResourceModifyUnsuccessfulTransfer, pdusessionTransfer_p); + ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NGAP_PDUSessionResourceModifyUnsuccessfulTransfer, &pdusessionTransfer); NGAP_INFO("pdusession_modify_resp: failed pdusession ID %ld\n", item->pDUSessionID); } diff --git a/radio/BLADERF/README b/radio/BLADERF/README deleted file mode 100644 index c93c938baf..0000000000 --- a/radio/BLADERF/README +++ /dev/null @@ -1,138 +0,0 @@ -BladeRF documentation -===================== - -As of 2018-11-06, the bladeRF support is not fully automatic and requires -some manual settings before use. This documentation is the ultimate source -of information. If something described in this file does not work or does -not correspond to the reality, then contact us so we can fix the problems -and update this documentation. - -1. Install bladeRF 2.0 libraries. - - As of now, it's better to install from source. - So, do not run: ./build_oai -I -w BLADERF - (That is: do not include '-w BLADERF'.) - - Instead, follow the instructions at: https://github.com/Nuand/bladeRF - - If you already had some bladeRF software installed using automatic - methods, first remove it by hand ('apt-get purge bladeRF' or something - similar, you can get the list of installed bladeRF packages by running - 'dpkg -l|grep -i blade', remove them all). - -2. Update the device. - - Download the latest FX3 firmware and FPGA images from Nuand's website. - As of writing, this is: - - https://github.com/Nuand/bladeRF/wiki - - That points to the following pages. - For FX3: - - http://www.nuand.com/fx3_images/ - - For FPGA: - - http://www.nuand.com/fpga_images/ - - Install FX3 firmware: - - sudo bladeRF-cli -f bladeRF_fw_latest.img - - Install FPGA image (this is for BladeRF x40): - - sudo bladeRF-cli -L hostedx40-latest.rbf - - Retrieve calibration information: - - sudo bladeRF-cli -i - info - - That outputs the serial number of your device. - - Go to: - - https://www.nuand.com/calibration - - And enter your serial number. - The website tells you to run something like: - - sudo bladeRF-cli -i - flash_init_cal 40 0x9271 - - Actual values depend on your device and serial number. - -3. Calibrate the bladeRF device. - - We will work with band 7 on 2.68GHz with a bandwidth of 5 MHz (25 RBs). - - Plug the bladeRF device, then run: - - sudo bladeRF-cli -i - set frequency tx 2680000000 - set frequency rx 2560000000 - set gain rx 60 - set gain tx 60 - set bandwidth 5000000 - set samplerate 7680000 - cal lms - cal lms - cal lms - cal dc rxtx - cal dc rxtx - cal dc rxtx - -4. Tune the RX gain using the enb tracer. - - Run the softmodem and the 'enb' tracer. For instructions, see: - - https://gitlab.eurecom.fr/oai/openairinterface5g/wikis/T/basic - - In the enb window, check the 'input signal'. You should see some blue - signal as seen at: - - https://gitlab.eurecom.fr/oai/openairinterface5g/wikis/T/enb - - (the 'Time signal power' plot). - - The level should be around 30. - - If it's not around 30 then edit your configuration file and modify - the value 'max_rxgain' in the section 'RUs'. - - The configuration file to use is: - - configuration/bladeRF/enb-band7-5mhz.conf - - In the configuration file, you also need to set the correct values for: - - - tracking_area_code - - plmn_list: mcc, mnc, mnc_length - - mme_ip_address: this is the IP address used by the computer running - the softmodem to connect to the EPC - - NETWORK_INTERFACES: all the ENB*ADDRESS* variables have to point - to the IP address of the EPC machine - -5. You're good to go. - - You can now connect a UE and pass some traffic. If everything is well - configured you can expect more than 16 Mb/s of throughput in the downlink - using iperf and more than 8 Mb/s in the uplink. Looking at the logs, you - should find lines containing 'PHR 40' and 'CQI 15'. If your values are - lower then your setup may need some adjustments. - -6. In case of problems. - - If the performance of the softmodem is very bad, you can stop it and - run the calibration again, without setting the parameters (frequencies, - gains, etc.). Just run: - - sudo bladeRF-cli -i - cal lms - cal dc rxtx - - That may help. - - Be sure to use proper radio equipment (duplexer, antennas, clean - environment without interferences). diff --git a/radio/BLADERF/README.md b/radio/BLADERF/README.md new file mode 100644 index 0000000000..d2ac6e1547 --- /dev/null +++ b/radio/BLADERF/README.md @@ -0,0 +1,148 @@ +# BladeRF 2.0 Micro Documentation + +[TOC] + +## Install BladeRF 2.0 Micro Libraries + +You can install the BladeRF library by using `build_oai -I -w BLADERF`, which +will install it from the package manager if the distribution supports it, or +falls back to a build from source otherwise. + +If you intentionally want to install BladeRF from source, you can follow +[Nuand's Getting Started +guide](https://github.com/Nuand/bladeRF/wiki/Getting-Started%3A-Linux#building-bladerf-libraries-and-tools-from-source) +to do so. + +## Configure the device + +### Image Flash + +Download the latest FX3 firmware and FPGA images from Nuand's website. There +are [fx3 images](http://www.nuand.com/fx3_images/) and [FPGA +images](http://www.nuand.com/fpga_images/). Download and save the corresponding +files to your computer, then install the FX3 firmware + + sudo bladeRF-cli -f bladeRF_fw_latest.img + +and install the FPGA image (this is x40): + + sudo bladeRF-cli -L hostedx40-latest.rbf + +### Use of the configuration file + +This configuration will help the bladeRF board to load the bitstream images +automatically, and can be used to set some options automatically that are not +configured by OAI. + +Create the bladeRF config file: + +```bash +$ cat ~/.config/Nuand/bladeRF/bladerf.conf +# Load the hostedx40.rbf FPGA image, set the trim DAC, and +# configure the frequency +fpga ~/.config/Nuand/bladeRF/image.rbf +# trimdac 592 +# frequency 2.4G +``` + +More information is available at [Nuand's +website](https://nuand.com/libbladeRF-doc/v2.5.0/configfile.html). + +### Useful commands for `bladerf-cli` + +`bladeRF-cli` can be used to query various device details. + +Show the version of bladeRF and device details + + bladeRF-cli --exec version + bladeRF-cli --exec info + +Probe for a specific device + + bladeRF-cli --probe + +Print some information on device capabilities and current configuration +(bandwidth, frequency, clocking and sample rate, various features) + + bladeRF-cli --exec print + +### Calibration + +Retrieve calibration serial number: + + bladerf-cli --exec info + +Go to the [Nuand calibration page](https://www.nuand.com/calibration) and enter +your serial number. The website tells you to run something like: + + sudo bladeRF-cli -i + flash_init_cal 40 0x9271 + +Actual values depend on your device and serial number. + +You can also rerun a calibration like so + + sudo bladeRF-cli -i + cal lms + cal dc rxtx + +## OAI + +### Build BladeRF driver + +You can use `build_oai` to build with BladeRF support, e.g., + + cd openairinterface5g/cmake_targets/ + ./build_oai --ninja --nrUE --gNB -w BLADERF + +or you can use cmake directly + + cd openairinterface5g/ + cmake -B build -G Ninja -DOAI_BLADERF=ON + cmake --build build -t oai_bladerfdevif # to build only the driver + cmake --build build # to build it all + +For more information about the build, please refer to [the build +documentation](../../doc/BUILD.md). + +### Adapt the OAI-gNB configuration file to your system + +You can start from a [sample BladeRF configuration +fiel](../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.51PRB.bladerf20xa0.conf). + +The only BladeRF-specific configuration resides in `RUs.[0].sdr_addrs`. This +string is passed to +[`bladerf_open()`](https://www.nuand.com/libbladeRF-doc/v2.6.0/group___f_n___i_n_i_t.html#gab341ac98615f393da9158ea59cdb6a24) +and can be used to select a specific BladeRF device. For instance, to open a +specific device with serial 730f5a36545849d9aa71ca6cf50e8ca0 on USB, you would +add `libusb: serial=730f5a36545849d9aa71ca6cf50e8ca0`. + +The TX gain is automatically chosen to be the maximum, and can be reduced with +`att_tx`. The RX gain is taken from `RUs.[0].max_rxgain`, and can be attenuated +with `att_rx`. + +The rest of the paramaters (PLMN, frequencies, IP addresses) is the same as in +any other configuration. Refer to the [general +tutorials](../..//doc/NR_SA_Tutorial_OAI_CN5G.md) for more information. + +### Run the setup + +Assuming you built from source above: + + cd ~/openairinterface5g/build/ + sudo ./nr-softmodem -O ../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.51PRB.bladerf20xa0.conf -E + +### In case of problems + +Refer back to the calibration section above. If not done yet, consider +calibrating your device. + +You can get more logs from BladeRF. To do so, you have to edit the code and +modify this line inside [`bladerf_lib.c`](./bladerf_lib.c). + +```patch +- bladerf_log_set_verbosity(BLADERF_LOG_LEVEL_INFO); ++ bladerf_log_set_verbosity(BLADERF_LOG_LEVEL_VERBOSE); +``` + +in function `device_init()`. diff --git a/radio/BLADERF/bladerf_lib.c b/radio/BLADERF/bladerf_lib.c index 2298287b5a..437aef4c23 100644 --- a/radio/BLADERF/bladerf_lib.c +++ b/radio/BLADERF/bladerf_lib.c @@ -19,107 +19,144 @@ * contact@openairinterface.org */ -/** bladerf_lib.c - * - * Author: navid nikaein - */ - - #include #include -#include -#include "bladerf_lib.h" -#include "math.h" -#include "PHY/sse_intrin.h" +#include + +#include +#include "common_lib.h" + +/*! \brief BladeRF specific data structure */ +typedef struct { + //! opaque BladeRF device struct. An empty ("") or NULL device identifier will result in the first encountered device being opened + //! (using the first discovered backend) + struct bladerf *dev; + + //! Number of buffers + unsigned int num_buffers; + //! Buffer size + unsigned int buffer_size; + //! Number of transfers + unsigned int num_transfers; + //! RX timeout + unsigned int rx_timeout_ms; + //! TX timeout + unsigned int tx_timeout_ms; + //! Metadata for RX + struct bladerf_metadata meta_rx; + //! Metadata for TX + struct bladerf_metadata meta_tx; + //! Sample rate + unsigned int sample_rate; + + // -------------------------------- + // Debug and output control + // -------------------------------- + //! Number of underflows + int num_underflows; + //! Number of overflows + int num_overflows; + //! number of RX errors + int num_rx_errors; + //! Number of TX errors + int num_tx_errors; + + //! timestamp of current TX + uint64_t tx_current_ts; + //! timestamp of current RX + uint64_t rx_current_ts; + //! number of actual samples transmitted + uint64_t tx_actual_nsamps; + //! number of actual samples received + uint64_t rx_actual_nsamps; + //! number of TX samples + uint64_t tx_nsamps; + //! number of RX samples + uint64_t rx_nsamps; + //! number of TX count + uint64_t tx_count; + //! number of RX count + uint64_t rx_count; +} brf_state_t; /** @addtogroup _BLADERF_PHY_RF_INTERFACE_ * @{ */ -//! Number of BladeRF devices -int num_devices=0; +#define BLADERF_CHECK(COND, FMT, ...) \ + if (COND) { \ + LOG_I(HW, FMT "\n" __VA_OPT__(, ) __VA_ARGS__); \ + } else { \ + LOG_E(HW, "Failed: " FMT ": %s\n" __VA_OPT__(, ) __VA_ARGS__, bladerf_strerror(status)); \ + return -1; \ + } -/*These items configure the underlying asynch stream used by the the sync interface. - */ +const bladerf_format format = BLADERF_FORMAT_SC16_Q11_META; -/*! \brief BladeRF Init function (not used at the moment) - * \param device RF frontend parameters set by application - * \returns 0 on success - */ -int trx_brf_init(openair0_device_t *device) +static const char *ch_dir(bladerf_channel ch) { - return 0; + switch (ch) { + case BLADERF_CHANNEL_RX(0): + return "RX0"; + case BLADERF_CHANNEL_RX(1): + return "RX1"; + case BLADERF_CHANNEL_TX(0): + return "TX0"; + case BLADERF_CHANNEL_TX(1): + return "TX1"; + default: + return "unknown"; + } + return "unknown"; } - -/*! \brief get current timestamp - *\param device the hardware to use - *\param module the bladeRf module - *\returns timestamp of BladeRF - */ -openair0_timestamp_t trx_get_timestamp(openair0_device_t *device, bladerf_module module) -{ - int status; - struct bladerf_metadata meta; - brf_state_t *brf = (brf_state_t*)device->priv; - memset(&meta, 0, sizeof(meta)); - - if ((status=bladerf_get_timestamp(brf->dev, module, &meta.timestamp)) != 0) { - fprintf(stderr,"Failed to get current %s timestamp: %s\n",(module == BLADERF_MODULE_RX ) ? "RX" : "TX", bladerf_strerror(status)); - return -1; - } // else {printf("Current RX timestampe 0x%016"PRIx64"\n", meta.timestamp); } - - return meta.timestamp; -} - - /*! \brief Start BladeRF * \param device the hardware to use * \returns 0 on success */ -int trx_brf_start(openair0_device_t *device) +static int trx_brf_start(openair0_device_t *device) { - brf_state_t *brf = (brf_state_t*)device->priv; - int status; + brf_state_t *brf = device->priv; + int status; - brf->meta_tx.flags = 0; + brf->meta_tx.flags = 0; - if ((status = bladerf_sync_config(brf->dev, - BLADERF_MODULE_TX, - BLADERF_FORMAT_SC16_Q11_META, - brf->num_buffers, - brf->buffer_size, - brf->num_transfers, - 100/*brf->tx_timeout_ms*/)) != 0 ) { - fprintf(stderr,"Failed to configure TX sync interface: %s\n", bladerf_strerror(status)); - abort(); - } - if ((status = bladerf_sync_config(brf->dev, - BLADERF_MODULE_RX, - BLADERF_FORMAT_SC16_Q11_META, - brf->num_buffers, - brf->buffer_size, - brf->num_transfers, - 100/*brf->rx_timeout_ms*/)) != 0 ) { - fprintf(stderr,"Failed to configure RX sync interface: %s\n", bladerf_strerror(status)); - abort(); - } - if ((status=bladerf_enable_module(brf->dev, BLADERF_MODULE_TX, true)) != 0) { - fprintf(stderr,"Failed to enable TX module: %s\n", bladerf_strerror(status)); - abort(); - } - if ((status=bladerf_enable_module(brf->dev, BLADERF_MODULE_RX, true)) != 0) { - fprintf(stderr,"Failed to enable RX module: %s\n", bladerf_strerror(status)); - abort(); - } + /* Configure the device's TX module for use with the sync interface. + * SC16 Q11 samples *with* metadata are used. */ + status = bladerf_sync_config(brf->dev, BLADERF_CHANNEL_TX(0), format, + brf->num_buffers, brf->buffer_size, brf->num_transfers, brf->tx_timeout_ms); + BLADERF_CHECK(status == 0, + "Set TX sync interface num_buffers %u, buffer_size %u, num_transfer %u, tx_timeout_ms %u", + brf->num_buffers, brf->buffer_size, brf->num_transfers, brf->tx_timeout_ms); - return 0; + /* Configure the device's RX module for use with the sync interface. + * SC16 Q11 samples *with* metadata are used. */ + status = bladerf_sync_config(brf->dev, BLADERF_CHANNEL_RX(0), format, + brf->num_buffers, brf->buffer_size, brf->num_transfers, brf->rx_timeout_ms); + BLADERF_CHECK(status == 0, + "Set RX sync interface num_buffers %u, buffer_size %u, num_transfer %u, rx_timeout_ms %u", + brf->num_buffers, brf->buffer_size, brf->num_transfers, brf->rx_timeout_ms); + + /* We must always enable the TX module after calling bladerf_sync_config(), and + * before attempting to TX samples via bladerf_sync_tx(). */ + status = bladerf_enable_module(brf->dev, BLADERF_CHANNEL_TX(0), true); + BLADERF_CHECK(status == 0, "Enable TX0 module"); + + /* We must always enable the RX module after calling bladerf_sync_config(), and + * before attempting to RX samples via bladerf_sync_rx(). */ + status = bladerf_enable_module(brf->dev, BLADERF_CHANNEL_RX(0), true); + BLADERF_CHECK(status == 0, "Enable RX module"); + + // in case it was set to verbosity before, disable now because otherwise the + // terminal will be flooded + bladerf_log_set_verbosity(BLADERF_LOG_LEVEL_INFO); + return 0; } /*! \brief Called to send samples to the BladeRF RF target \param device pointer to the device structure specific to the RF hardware target - \param timestamp The timestamp at whicch the first sample MUST be sent + \param timestamp The timestamp at which the first sample MUST be sent \param buff Buffer which holds the samples \param nsamps number of samples to be sent \param cc index of the component carrier @@ -128,46 +165,37 @@ int trx_brf_start(openair0_device_t *device) */ static int trx_brf_write(openair0_device_t *device, openair0_timestamp_t ptimestamp, void **buff, int nsamps, int cc, int flags) { - int status; - brf_state_t *brf = (brf_state_t*)device->priv; - /* BRF has only 1 rx/tx chaine : is it correct? */ - int16_t *samples = (int16_t*)buff[0]; - ptimestamp -= device->openair0_cfg->command_line_sample_advance - device->openair0_cfg->tx_sample_advance; - //memset(&brf->meta_tx, 0, sizeof(brf->meta_tx)); - // When BLADERF_META_FLAG_TX_NOW is used the timestamp is not used, so one can't schedule a tx - if (brf->meta_tx.flags == 0 ) - brf->meta_tx.flags = (BLADERF_META_FLAG_TX_BURST_START);// | BLADERF_META_FLAG_TX_BURST_END);// | BLADERF_META_FLAG_TX_NOW); + brf_state_t *brf = device->priv; + /* BRF has only 1 rx/tx chaine : is it correct? TODO: now, handle also two! */ + int16_t *samples = (int16_t *)buff[0]; + ptimestamp -= device->openair0_cfg->command_line_sample_advance - device->openair0_cfg->tx_sample_advance; - brf->meta_tx.timestamp= (uint64_t) (ptimestamp); - status = bladerf_sync_tx(brf->dev, samples, (unsigned int) nsamps, &brf->meta_tx, 2*brf->tx_timeout_ms); + // When BLADERF_META_FLAG_TX_NOW is used the timestamp is not used, so one can't schedule a tx + if (brf->meta_tx.flags == 0) + brf->meta_tx.flags = BLADERF_META_FLAG_TX_BURST_START; + brf->meta_tx.timestamp = (uint64_t)(ptimestamp); - if (brf->meta_tx.flags == BLADERF_META_FLAG_TX_BURST_START) - brf->meta_tx.flags = BLADERF_META_FLAG_TX_UPDATE_TIMESTAMP; + int status = bladerf_sync_tx(brf->dev, samples, nsamps, &brf->meta_tx, 2 * brf->tx_timeout_ms); + if (status == BLADERF_ERR_TIME_PAST) { + brf->num_tx_errors++; + LOG_W(HW, "Timestamp error: Requested timestamp %ld is in the past.\n", ptimestamp); + } else if (status != 0) { + brf->num_tx_errors++; + LOG_W(HW, "Error during TX: %s\n", bladerf_strerror(status)); + } + if (brf->meta_tx.flags == BLADERF_META_FLAG_TX_BURST_START) + brf->meta_tx.flags = BLADERF_META_FLAG_TX_UPDATE_TIMESTAMP; - if (status != 0) { - //fprintf(stderr,"Failed to TX sample: %s\n", bladerf_strerror(status)); - brf->num_tx_errors++; - brf_error(status); - } else if (brf->meta_tx.status & BLADERF_META_STATUS_UNDERRUN) { - /* libbladeRF does not report this status. It is here for future use. */ - fprintf(stderr, "TX Underrun detected. %u valid samples were read.\n", brf->meta_tx.actual_count); - brf->num_underflows++; - } - //printf("Provided TX timestampe %u, meta timestame %u\n", ptimestamp,brf->meta_tx.timestamp); + brf->tx_current_ts = brf->meta_tx.timestamp; + brf->tx_actual_nsamps += brf->meta_tx.actual_count; + brf->tx_nsamps += nsamps; + brf->tx_count++; - // printf("tx status %d \n",brf->meta_tx.status); - brf->tx_current_ts=brf->meta_tx.timestamp; - brf->tx_actual_nsamps+=brf->meta_tx.actual_count; - brf->tx_nsamps+=nsamps; - brf->tx_count++; - - - return nsamps; //brf->meta_tx.actual_count; + return nsamps; // brf->meta_tx.actual_count; } - /*! \brief Receive samples from hardware. * Read \ref nsamps samples from each channel to buffers. buff[0] is the array for * the first channel. *ptimestamp is the time at which the first sample @@ -181,63 +209,56 @@ static int trx_brf_write(openair0_device_t *device, openair0_timestamp_t ptimest */ static int trx_brf_read(openair0_device_t *device, openair0_timestamp_t *ptimestamp, void **buff, int nsamps, int cc) { - int status=0; - brf_state_t *brf = (brf_state_t*)device->priv; + brf_state_t *brf = device->priv; - // BRF has only one rx/tx chain - int16_t *samples = (int16_t*)buff[0]; + // BRF has only one rx/tx chain + int16_t *samples = (int16_t *)buff[0]; - brf->meta_rx.actual_count = 0; - brf->meta_rx.flags = BLADERF_META_FLAG_RX_NOW; - status = bladerf_sync_rx(brf->dev, samples, (unsigned int) nsamps, &brf->meta_rx, 2*brf->rx_timeout_ms); + brf->meta_rx.actual_count = 0; + brf->meta_rx.flags = BLADERF_META_FLAG_RX_NOW; + int status = bladerf_sync_rx(brf->dev, samples, (unsigned int)nsamps, &brf->meta_rx, 2 * brf->rx_timeout_ms); - // printf("Current RX timestampe %u, nsamps %u, actual %u, cc %d\n", brf->meta_rx.timestamp, nsamps, brf->meta_rx.actual_count, cc); + if (status != 0) { + LOG_E(HW, "RX failed: %s\n", bladerf_strerror(status)); + brf->num_rx_errors++; + } else if (brf->meta_rx.status & BLADERF_META_STATUS_OVERRUN) { + brf->num_overflows++; + LOG_W(HW, + "%d RX overrun is detected. timestamp %ld. Requested %u but got %u samples\n", + brf->num_overflows, + brf->meta_rx.timestamp, + brf->meta_rx.actual_count, + nsamps); + } - if (status != 0) { - fprintf(stderr, "RX failed: %s\n", bladerf_strerror(status)); - // printf("RX failed: %s\n", bladerf_strerror(status)); - brf->num_rx_errors++; - } else if ( brf->meta_rx.status & BLADERF_META_STATUS_OVERRUN) { - brf->num_overflows++; - printf("RX overrun (%d) is detected. t=" "%" PRIu64 "Got %u samples. nsymps %d\n", - brf->num_overflows,brf->meta_rx.timestamp, brf->meta_rx.actual_count, nsamps); - } + if (brf->meta_rx.actual_count != nsamps) { + LOG_E(HW, "RX bad samples count, wanted %d, got %d\n", nsamps, brf->meta_rx.actual_count); + } - if (brf->meta_rx.actual_count != nsamps) { - printf("RX bad samples count, wanted %d, got %d\n", nsamps, brf->meta_rx.actual_count); - } + brf->rx_current_ts = brf->meta_rx.timestamp; + brf->rx_actual_nsamps += brf->meta_rx.actual_count; + brf->rx_nsamps += nsamps; + brf->rx_count++; - //printf("Current RX timestampe %u\n", brf->meta_rx.timestamp); - //printf("[BRF] (buff %p) ts=0x%"PRIu64" %s\n",samples, brf->meta_rx.timestamp,bladerf_strerror(status)); - brf->rx_current_ts=brf->meta_rx.timestamp; - brf->rx_actual_nsamps+=brf->meta_rx.actual_count; - brf->rx_nsamps+=nsamps; - brf->rx_count++; - - - *ptimestamp = brf->meta_rx.timestamp; - - return nsamps; //brf->meta_rx.actual_count; + *ptimestamp = brf->meta_rx.timestamp; + return nsamps; // brf->meta_rx.actual_count; } - /*! \brief Terminate operation of the BladeRF transceiver -- free all associated resources * \param device the hardware to use */ -void trx_brf_end(openair0_device_t *device) +static void trx_brf_end(openair0_device_t *device) { - int status; - brf_state_t *brf = (brf_state_t*)device->priv; - // Disable RX module, shutting down our underlying RX stream - if ((status=bladerf_enable_module(brf->dev, BLADERF_MODULE_RX, false)) != 0) { - fprintf(stderr, "Failed to disable RX module: %s\n", bladerf_strerror(status)); - } - if ((status=bladerf_enable_module(brf->dev, BLADERF_MODULE_TX, false)) != 0) { - fprintf(stderr, "Failed to disable TX module: %s\n", bladerf_strerror(status)); - } - bladerf_close(brf->dev); - exit(1); + brf_state_t *brf = device->priv; + // Disable RX module, shutting down our underlying RX stream + + int status; + if ((status = bladerf_enable_module(brf->dev, BLADERF_CHANNEL_RX(0), false)) != 0) + LOG_E(HW, "Failed: Disable RX module: %s\n", bladerf_strerror(status)); + if ((status = bladerf_enable_module(brf->dev, BLADERF_CHANNEL_TX(0), false)) != 0) + LOG_E(HW, "Failed: Disable TX module: %s\n", bladerf_strerror(status)); + bladerf_close(brf->dev); } @@ -245,58 +266,49 @@ void trx_brf_end(openair0_device_t *device) * \param device the hardware to use * \returns 0 on success */ -int trx_brf_get_stats(openair0_device_t *device) +static int trx_brf_get_stats(openair0_device_t *device) { - return(0); + return 0; } - /*! \brief Reset the BladeRF statistics -* \param device the hardware to use -* \returns 0 on success -*/ -int trx_brf_reset_stats(openair0_device_t *device) + * \param device the hardware to use + * \returns 0 on success + */ +static int trx_brf_reset_stats(openair0_device_t *device) { - return(0); + return 0; } - /*! \brief Stop BladeRF * \param card the hardware to use * \returns 0 in success */ -int trx_brf_stop(openair0_device_t *device) +static int trx_brf_stop(openair0_device_t *device) { - return(0); + return 0; } - /*! \brief Set frequencies (TX/RX) * \param device the hardware to use * \param openair0_cfg1 openair0 Config structure (ignored. It is there to comply with RF common API) * \returns 0 in success */ -int trx_brf_set_freq(openair0_device_t *device, openair0_config_t *openair0_cfg1) +static int trx_brf_set_freq(openair0_device_t *device, openair0_config_t *openair0_cfg1) { - int status; - brf_state_t *brf = (brf_state_t *)device->priv; - openair0_config_t *openair0_cfg = (openair0_config_t *)device->openair0_cfg; + int status; + brf_state_t *brf = device->priv; + openair0_config_t *openair0_cfg = (openair0_config_t *)device->openair0_cfg; + unsigned int tx_freq = (unsigned int)openair0_cfg->tx_freq[0]; + status = bladerf_set_frequency(brf->dev, BLADERF_CHANNEL_TX(0), tx_freq); + BLADERF_CHECK(status == 0, "Set TX frequency %u", tx_freq); - if ((status=bladerf_set_frequency(brf->dev, BLADERF_MODULE_TX, (unsigned int) openair0_cfg->tx_freq[0])) != 0) { - fprintf(stderr,"Failed to set TX frequency: %s\n",bladerf_strerror(status)); - brf_error(status); - } else - printf("[BRF] set TX Frequency to %u\n", (unsigned int) openair0_cfg->tx_freq[0]); - - if ((status=bladerf_set_frequency(brf->dev, BLADERF_MODULE_RX, (unsigned int) openair0_cfg->rx_freq[0])) != 0) { - fprintf(stderr,"Failed to set RX frequency: %s\n",bladerf_strerror(status)); - brf_error(status); - } else - printf("[BRF] set RX frequency to %u\n",(unsigned int)openair0_cfg->rx_freq[0]); - - return(0); + unsigned int rx_freq = (unsigned int)openair0_cfg->rx_freq[0]; + status = bladerf_set_frequency(brf->dev, BLADERF_CHANNEL_RX(0), rx_freq); + BLADERF_CHECK(status == 0, "Set RX frequency %u", rx_freq); + return 0; } @@ -305,633 +317,50 @@ int trx_brf_set_freq(openair0_device_t *device, openair0_config_t *openair0_cfg1 * \param openair0_cfg openair0 Config structure * \returns 0 in success */ -int trx_brf_set_gains(openair0_device_t *device, openair0_config_t *openair0_cfg) +static int trx_brf_set_gains(openair0_device_t *device, openair0_config_t *openair0_cfg) { - return(0); -} -int trx_brf_write_init(openair0_device_t *device) -{ - return 0; + return 0; } -#define RXDCLENGTH 16384 -int16_t cos_fsover8[8] = {2047, 1447, 0, -1448, -2047, -1448, 0, 1447}; -int16_t cos_3fsover8[8] = {2047, -1448, 0, 1447, -2047, 1447, 0, -1448}; - -/*! \brief calibration table for BladeRF */ -rx_gain_calib_table_t calib_table_fx4[] = { - {2300000000.0,53.5}, - {1880000000.0,57.0}, - {816000000.0,73.0}, - {-1,0} -}; - - -/*! \brief set RX gain offset from calibration table - * \param openair0_cfg RF frontend parameters set by application - * \param chain_index RF chain ID - */ -void set_rx_gain_offset(openair0_config_t *openair0_cfg, - int chain_index) +static int trx_brf_write_init(openair0_device_t *device) { - int i=0; - // loop through calibration table to find best adjustment factor for RX frequency - double min_diff = 6e9,diff; - - while (openair0_cfg->rx_gain_calib_table[i].freq>0) { - diff = fabs(openair0_cfg->rx_freq[chain_index] - openair0_cfg->rx_gain_calib_table[i].freq); - printf("cal %d: freq %f, offset %f, diff %f\n", - i, - openair0_cfg->rx_gain_calib_table[i].freq, - openair0_cfg->rx_gain_calib_table[i].offset,diff); - if (min_diff > diff) { - min_diff = diff; - openair0_cfg->rx_gain_offset[chain_index] = openair0_cfg->rx_gain_calib_table[i].offset; - } - i++; - } - + return 0; } +typedef struct channel_config { + bladerf_channel channel; + unsigned int frequency; + unsigned int sample_rate; + unsigned int bandwidth; + int gain; +} channel_config_t; -/*! \brief Calibrate LMSSDR RF - * \param device the hardware to use - */ -void calibrate_rf(openair0_device_t *device) + +static int configure_channel(struct bladerf *dev, channel_config_t *c) { - /* TODO: this function does not seem to work. Disabled until fixed. */ - return; + int status; + unsigned int actual_value = 0; + const char *dir = ch_dir(c->channel); - brf_state_t *brf = (brf_state_t *)device->priv; - openair0_timestamp_t ptimestamp; - int16_t *calib_buffp,*calib_tx_buffp; - int16_t calib_buff[2*RXDCLENGTH]; - int16_t calib_tx_buff[2*RXDCLENGTH]; - int i,j,offI,offQ,offIold,offQold,offInew,offQnew,offphase,offphaseold,offphasenew,offgain,offgainold,offgainnew; - int32_t meanI,meanQ,meanIold,meanQold; - int cnt=0,loop; + status = bladerf_set_frequency(dev, c->channel, c->frequency); + BLADERF_CHECK(status == 0, "Set %s frequency %u", dir, c->frequency); - // put TX on a far-away frequency to avoid interference in RX band - bladerf_set_frequency(brf->dev,BLADERF_MODULE_TX, (unsigned int) device->openair0_cfg->rx_freq[0] + 200e6); - // Set gains to close to max - bladerf_set_gain(brf->dev, BLADERF_MODULE_RX, 60); - bladerf_set_gain(brf->dev, BLADERF_MODULE_TX, 60); + status = bladerf_set_sample_rate(dev, c->channel, c->sample_rate, &actual_value); + BLADERF_CHECK(status == 0, "Set %s sample rate %u (is %u)", dir, c->sample_rate, actual_value); - // fill TX buffer with fs/8 complex sinusoid - j=0; - for (i=0; ichannel, c->bandwidth, &actual_value); + BLADERF_CHECK(status == 0, "Set %s bandwidth %u (is %u)", dir, c->bandwidth, actual_value); - offIold=offQold=2048; - bladerf_set_correction(brf->dev,BLADERF_MODULE_RX,BLADERF_CORR_LMS_DCOFF_I,offIold); - bladerf_set_correction(brf->dev,BLADERF_MODULE_RX,BLADERF_CORR_LMS_DCOFF_Q,offQold); - for (i=0; i<10; i++) - trx_brf_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0); + status = bladerf_set_gain(dev, c->channel, c->gain); + BLADERF_CHECK(status == 0, "Set %s gain %u", dir, c->gain); - for (meanIold=meanQold=i=j=0; i (%d,%d)\n",offIold,offQold,meanIold,meanQold); + bladerf_gain g; + status = bladerf_get_gain(dev, c->channel, &g); + BLADERF_CHECK(status == 0, "Get %s gain: %u", dir, g); - offI=offQ=-2048; - bladerf_set_correction(brf->dev,BLADERF_MODULE_RX,BLADERF_CORR_LMS_DCOFF_I,offI); - bladerf_set_correction(brf->dev,BLADERF_MODULE_RX,BLADERF_CORR_LMS_DCOFF_Q,offQ); - for (i=0; i<10; i++) - trx_brf_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0); - - for (meanI=meanQ=i=j=0; i (%d,%d)\n",offI,offQ,meanI,meanQ); - - while (cnt++ < 12) { - - offInew=(offIold+offI)>>1; - offQnew=(offQold+offQ)>>1; - - if (meanI*meanI < meanIold*meanIold) { - meanIold = meanI; - offIold = offI; - printf("[BRF] *** RX DC: offI %d => %d\n",offIold,meanI); - } - if (meanQ*meanQ < meanQold*meanQold) { - meanQold = meanQ; - offQold = offQ; - printf("[BRF] *** RX DC: offQ %d => %d\n",offQold,meanQ); - } - offI = offInew; - offQ = offQnew; - bladerf_set_correction(brf->dev,BLADERF_MODULE_RX,BLADERF_CORR_LMS_DCOFF_I,offI); - bladerf_set_correction(brf->dev,BLADERF_MODULE_RX,BLADERF_CORR_LMS_DCOFF_Q,offQ); - - for (i=0; i<10; i++) - trx_brf_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0); - - for (meanI=meanQ=i=j=0; i (%d,%d)\n",offI,offQ,meanI,meanQ); - } - - printf("[BRF] RX DC: (%d,%d) => (%d,%d)\n",offIold,offQold,meanIold,meanQold); - bladerf_set_correction(brf->dev,BLADERF_MODULE_RX,BLADERF_CORR_LMS_DCOFF_I,offIold); - bladerf_set_correction(brf->dev,BLADERF_MODULE_RX,BLADERF_CORR_LMS_DCOFF_Q,offQold); - - // TX DC offset - // PUT TX as f_RX + fs/4 - // loop back BLADERF_LB_RF_LNA1 - bladerf_set_frequency(brf->dev,BLADERF_MODULE_TX, (unsigned int) device->openair0_cfg->rx_freq[0] + (unsigned int) device->openair0_cfg->sample_rate/4); - bladerf_set_loopback (brf->dev,BLADERF_LB_RF_LNA1); - - offIold=2048; - bladerf_set_correction(brf->dev,BLADERF_MODULE_TX,BLADERF_CORR_LMS_DCOFF_I,offIold); - for (i=0; i<10; i++) { - trx_brf_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0); - trx_brf_write(device,ptimestamp+5*RXDCLENGTH, (void **)&calib_tx_buffp, RXDCLENGTH, 0, 0); - } - for (meanIold=meanQold=i=j=0; i (%d,%d)\n",offIold,meanIold,meanQold); - - offI=-2048; - bladerf_set_correction(brf->dev,BLADERF_MODULE_TX,BLADERF_CORR_LMS_DCOFF_I,offI); - for (i=0; i<10; i++) { - trx_brf_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0); - trx_brf_write(device,ptimestamp+5*RXDCLENGTH, (void **)&calib_tx_buffp, RXDCLENGTH, 0, 0); - } - for (meanI=meanQ=i=j=0; i (%d,%d)\n",offI,meanI,meanQ); - cnt = 0; - while (cnt++ < 12) { - - offInew=(offIold+offI)>>1; - if (meanI*meanI+meanQ*meanQ < meanIold*meanIold +meanQold*meanQold) { - printf("[BRF] TX DC (offI): ([%d,%d]) => %d : %d\n",offIold,offI,offInew,meanI*meanI+meanQ*meanQ); - meanIold = meanI; - meanQold = meanQ; - offIold = offI; - } - offI = offInew; - bladerf_set_correction(brf->dev,BLADERF_MODULE_TX,BLADERF_CORR_LMS_DCOFF_I,offI); - - for (i=0; i<10; i++) { - trx_brf_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0); - trx_brf_write(device,ptimestamp+5*RXDCLENGTH, (void **)&calib_tx_buffp, RXDCLENGTH, 0, 0); - } - for (meanI=meanQ=i=j=0; i (%d,%d)\n",offI,meanI,meanQ); - } - - bladerf_set_correction(brf->dev,BLADERF_MODULE_TX,BLADERF_CORR_LMS_DCOFF_I,offIold); - - offQold=2048; - bladerf_set_correction(brf->dev,BLADERF_MODULE_TX,BLADERF_CORR_LMS_DCOFF_Q,offQold); - for (i=0; i<10; i++) { - trx_brf_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0); - trx_brf_write(device,ptimestamp+5*RXDCLENGTH, (void **)&calib_tx_buffp, RXDCLENGTH, 0, 0); - } - // project on fs/4 - for (meanIold=meanQold=i=j=0; i (%d,%d)\n",offQold,meanIold,meanQold); - - offQ=-2048; - bladerf_set_correction(brf->dev,BLADERF_MODULE_TX,BLADERF_CORR_LMS_DCOFF_Q,offQ); - for (i=0; i<10; i++) { - trx_brf_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0); - trx_brf_write(device,ptimestamp+5*RXDCLENGTH, (void **)&calib_tx_buffp, RXDCLENGTH, 0, 0); - } - for (meanI=meanQ=i=j=0; i (%d,%d)\n",offQ,meanI,meanQ); - - cnt=0; - while (cnt++ < 12) { - - offQnew=(offQold+offQ)>>1; - if (meanI*meanI+meanQ*meanQ < meanIold*meanIold +meanQold*meanQold) { - printf("[BRF] TX DC (offQ): ([%d,%d]) => %d : %d\n",offQold,offQ,offQnew,meanI*meanI+meanQ*meanQ); - - meanIold = meanI; - meanQold = meanQ; - offQold = offQ; - } - offQ = offQnew; - bladerf_set_correction(brf->dev,BLADERF_MODULE_TX,BLADERF_CORR_LMS_DCOFF_Q,offQ); - - for (i=0; i<10; i++) { - trx_brf_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0); - trx_brf_write(device,ptimestamp+5*RXDCLENGTH, (void **)&calib_tx_buffp, RXDCLENGTH, 0, 0); - } - for (meanI=meanQ=i=j=0; i (%d,%d)\n",offQ,meanI,meanQ); - } - - printf("[BRF] TX DC: (%d,%d) => (%d,%d)\n",offIold,offQold,meanIold,meanQold); - - bladerf_set_correction(brf->dev,BLADERF_MODULE_TX,BLADERF_CORR_LMS_DCOFF_Q,offQold); - - // TX IQ imbalance - for (loop=0; loop<2; loop++) { - offphaseold=4096; - bladerf_set_correction(brf->dev,BLADERF_MODULE_TX,BLADERF_CORR_FPGA_PHASE,offphaseold); - for (i=0; i<10; i++) { - trx_brf_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0); - trx_brf_write(device,ptimestamp+5*RXDCLENGTH, (void **)&calib_tx_buffp, RXDCLENGTH, 0, 0); - } - // project on fs/8 (Image of TX signal in +ve frequencies) - for (meanIold=meanQold=i=j=0; i>11; - meanQold+= (calib_buff[j]*cos_fsover8[(i+2)&7] + calib_buff[j+1]*cos_fsover8[i&7])>>11; - j+=2; - } - - meanIold/=RXDCLENGTH; - meanQold/=RXDCLENGTH; - printf("[BRF] TX IQ (offphase): %d => (%d,%d)\n",offphaseold,meanIold,meanQold); - - offphase=-4096; - bladerf_set_correction(brf->dev,BLADERF_MODULE_TX,BLADERF_CORR_FPGA_PHASE,offphase); - for (i=0; i<10; i++) { - trx_brf_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0); - trx_brf_write(device,ptimestamp+5*RXDCLENGTH, (void **)&calib_tx_buffp, RXDCLENGTH, 0, 0); - } - // project on fs/8 (Image of TX signal in +ve frequencies) - for (meanI=meanQ=i=j=0; i>11; - meanQ+= (calib_buff[j]*cos_fsover8[(i+2)&7] + calib_buff[j+1]*cos_fsover8[i&7])>>11; - j+=2; - } - - meanI/=RXDCLENGTH; - meanQ/=RXDCLENGTH; - printf("[BRF] TX IQ (offphase): %d => (%d,%d)\n",offphase,meanI,meanQ); - - cnt=0; - while (cnt++ < 13) { - - offphasenew=(offphaseold+offphase)>>1; - printf("[BRF] TX IQ (offphase): ([%d,%d]) => %d : %d\n",offphaseold,offphase,offphasenew,meanI*meanI+meanQ*meanQ); - if (meanI*meanI+meanQ*meanQ < meanIold*meanIold +meanQold*meanQold) { - - - meanIold = meanI; - meanQold = meanQ; - offphaseold = offphase; - } - offphase = offphasenew; - bladerf_set_correction(brf->dev,BLADERF_MODULE_TX,BLADERF_CORR_FPGA_PHASE,offphase); - - for (i=0; i<10; i++) { - trx_brf_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0); - trx_brf_write(device,ptimestamp+5*RXDCLENGTH, (void **)&calib_tx_buffp, RXDCLENGTH, 0, 0); - } - // project on fs/8 (Image of TX signal in +ve frequencies) - for (meanI=meanQ=i=j=0; i>11; - meanQ+= (calib_buff[j]*cos_fsover8[(i+2)&7] + calib_buff[j+1]*cos_fsover8[i&7])>>11; - j+=2; - } - meanI/=RXDCLENGTH; - meanQ/=RXDCLENGTH; - - // printf("[BRF] TX DC (offQ): %d => (%d,%d)\n",offQ,meanI,meanQ); - } - - printf("[BRF] TX IQ offphase: %d => (%d,%d)\n",offphaseold,meanIold,meanQold); - - bladerf_set_correction(brf->dev,BLADERF_MODULE_TX,BLADERF_CORR_FPGA_PHASE,offphaseold); - - offgainold=4096; - bladerf_set_correction(brf->dev,BLADERF_MODULE_TX,BLADERF_CORR_FPGA_GAIN,offgainold); - for (i=0; i<10; i++) { - trx_brf_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0); - trx_brf_write(device,ptimestamp+5*RXDCLENGTH, (void **)&calib_tx_buffp, RXDCLENGTH, 0, 0); - } - // project on fs/8 (Image of TX signal in +ve frequencies) - for (meanIold=meanQold=i=j=0; i>11; - meanQold+= (calib_buff[j]*cos_fsover8[(i+2)&7] + calib_buff[j+1]*cos_fsover8[i&7])>>11; - j+=2; - } - - meanIold/=RXDCLENGTH; - meanQold/=RXDCLENGTH; - printf("[BRF] TX IQ (offgain): %d => (%d,%d)\n",offgainold,meanIold,meanQold); - - offgain=-4096; - bladerf_set_correction(brf->dev,BLADERF_MODULE_TX,BLADERF_CORR_FPGA_GAIN,offgain); - for (i=0; i<10; i++) { - trx_brf_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0); - trx_brf_write(device,ptimestamp+5*RXDCLENGTH, (void **)&calib_tx_buffp, RXDCLENGTH, 0, 0); - } - // project on fs/8 (Image of TX signal in +ve frequencies) - for (meanI=meanQ=i=j=0; i>11; - meanQ+= (calib_buff[j]*cos_fsover8[(i+2)&7] + calib_buff[j+1]*cos_fsover8[i&7])>>11; - j+=2; - } - - meanI/=RXDCLENGTH; - meanQ/=RXDCLENGTH; - printf("[BRF] TX IQ (offgain): %d => (%d,%d)\n",offgain,meanI,meanQ); - - cnt=0; - while (cnt++ < 13) { - - offgainnew=(offgainold+offgain)>>1; - if (meanI*meanI+meanQ*meanQ < meanIold*meanIold +meanQold*meanQold) { - printf("[BRF] TX IQ (offgain): ([%d,%d]) => %d : %d\n",offgainold,offgain,offgainnew,meanI*meanI+meanQ*meanQ); - - meanIold = meanI; - meanQold = meanQ; - offgainold = offgain; - } - offgain = offgainnew; - bladerf_set_correction(brf->dev,BLADERF_MODULE_TX,BLADERF_CORR_FPGA_GAIN,offgain); - - for (i=0; i<10; i++) { - trx_brf_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0); - trx_brf_write(device,ptimestamp+5*RXDCLENGTH, (void **)&calib_tx_buffp, RXDCLENGTH, 0, 0); - } - // project on fs/8 (Image of TX signal in +ve frequencies) - for (meanI=meanQ=i=j=0; i>11; - meanQ+= (calib_buff[j]*cos_fsover8[(i+2)&7] + calib_buff[j+1]*cos_fsover8[i&7])>>11; - j+=2; - } - meanI/=RXDCLENGTH; - meanQ/=RXDCLENGTH; - - // printf("[BRF] TX DC (offQ): %d => (%d,%d)\n",offQ,meanI,meanQ); - } - - printf("[BRF] TX IQ offgain: %d => (%d,%d)\n",offgainold,meanIold,meanQold); - - bladerf_set_correction(brf->dev,BLADERF_MODULE_TX,BLADERF_CORR_FPGA_GAIN,offgainold); - } - - // RX IQ imbalance - for (loop=0; loop<2; loop++) { - offphaseold=4096; - bladerf_set_correction(brf->dev,BLADERF_MODULE_RX,BLADERF_CORR_FPGA_PHASE,offphaseold); - for (i=0; i<10; i++) { - trx_brf_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0); - trx_brf_write(device,ptimestamp+5*RXDCLENGTH, (void **)&calib_tx_buffp, RXDCLENGTH, 0, 0); - } - // project on -3fs/8 (Image of TX signal in -ve frequencies) - for (meanIold=meanQold=i=j=0; i>11; - meanQold+= (calib_buff[j]*cos_3fsover8[(i+2)&7] + calib_buff[j+1]*cos_3fsover8[i&7])>>11; - j+=2; - } - - meanIold/=RXDCLENGTH; - meanQold/=RXDCLENGTH; - printf("[BRF] RX IQ (offphase): %d => (%d,%d)\n",offphaseold,meanIold,meanQold); - - offphase=-4096; - bladerf_set_correction(brf->dev,BLADERF_MODULE_RX,BLADERF_CORR_FPGA_PHASE,offphase); - for (i=0; i<10; i++) { - trx_brf_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0); - trx_brf_write(device,ptimestamp+5*RXDCLENGTH, (void **)&calib_tx_buffp, RXDCLENGTH, 0, 0); - } - // project on -3fs/8 (Image of TX signal in -ve frequencies) - for (meanI=meanQ=i=j=0; i>11; - meanQ+= (calib_buff[j]*cos_3fsover8[(i+2)&7] + calib_buff[j+1]*cos_3fsover8[i&7])>>11; - j+=2; - } - - meanI/=RXDCLENGTH; - meanQ/=RXDCLENGTH; - printf("[BRF] RX IQ (offphase): %d => (%d,%d)\n",offphase,meanI,meanQ); - - cnt=0; - while (cnt++ < 13) { - - offphasenew=(offphaseold+offphase)>>1; - printf("[BRF] RX IQ (offphase): ([%d,%d]) => %d : %d\n",offphaseold,offphase,offphasenew,meanI*meanI+meanQ*meanQ); - if (meanI*meanI+meanQ*meanQ < meanIold*meanIold +meanQold*meanQold) { - - - meanIold = meanI; - meanQold = meanQ; - offphaseold = offphase; - } - offphase = offphasenew; - bladerf_set_correction(brf->dev,BLADERF_MODULE_RX,BLADERF_CORR_FPGA_PHASE,offphase); - - for (i=0; i<10; i++) { - trx_brf_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0); - trx_brf_write(device,ptimestamp+5*RXDCLENGTH, (void **)&calib_tx_buffp, RXDCLENGTH, 0, 0); - } - // project on -3fs/8 (Image of TX signal in -ve frequencies) - for (meanI=meanQ=i=j=0; i>11; - meanQ+= (calib_buff[j]*cos_3fsover8[(i+2)&7] + calib_buff[j+1]*cos_3fsover8[i&7])>>11; - j+=2; - } - meanI/=RXDCLENGTH; - meanQ/=RXDCLENGTH; - - // printf("[BRF] TX DC (offQ): %d => (%d,%d)\n",offQ,meanI,meanQ); - } - - printf("[BRF] RX IQ offphase: %d => (%d,%d)\n",offphaseold,meanIold,meanQold); - - bladerf_set_correction(brf->dev,BLADERF_MODULE_RX,BLADERF_CORR_FPGA_PHASE,offphaseold); - - offgainold=4096; - bladerf_set_correction(brf->dev,BLADERF_MODULE_RX,BLADERF_CORR_FPGA_GAIN,offgainold); - for (i=0; i<10; i++) { - trx_brf_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0); - trx_brf_write(device,ptimestamp+5*RXDCLENGTH, (void **)&calib_tx_buffp, RXDCLENGTH, 0,0); - } - // project on -3fs/8 (Image of TX signal in +ve frequencies) - for (meanIold=meanQold=i=j=0; i>11; - meanQold+= (calib_buff[j]*cos_3fsover8[(i+2)&7] + calib_buff[j+1]*cos_3fsover8[i&7])>>11; - j+=2; - } - - meanIold/=RXDCLENGTH; - meanQold/=RXDCLENGTH; - printf("[BRF] RX IQ (offgain): %d => (%d,%d)\n",offgainold,meanIold,meanQold); - - offgain=-4096; - bladerf_set_correction(brf->dev,BLADERF_MODULE_RX,BLADERF_CORR_FPGA_GAIN,offgain); - for (i=0; i<10; i++) { - trx_brf_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0); - trx_brf_write(device,ptimestamp+5*RXDCLENGTH, (void **)&calib_tx_buffp, RXDCLENGTH, 0, 0); - } - // project on 3fs/8 (Image of TX signal in -ve frequencies) - for (meanI=meanQ=i=j=0; i>11; - meanQ+= (calib_buff[j]*cos_3fsover8[(i+2)&7] + calib_buff[j+1]*cos_3fsover8[i&7])>>11; - j+=2; - } - - meanI/=RXDCLENGTH; - meanQ/=RXDCLENGTH; - printf("[BRF] RX IQ (offgain): %d => (%d,%d)\n",offgain,meanI,meanQ); - - cnt=0; - while (cnt++ < 13) { - - offgainnew=(offgainold+offgain)>>1; - if (meanI*meanI+meanQ*meanQ < meanIold*meanIold +meanQold*meanQold) { - printf("[BRF] RX IQ (offgain): ([%d,%d]) => %d : %d\n",offgainold,offgain,offgainnew,meanI*meanI+meanQ*meanQ); - - meanIold = meanI; - meanQold = meanQ; - offgainold = offgain; - } - offgain = offgainnew; - bladerf_set_correction(brf->dev,BLADERF_MODULE_RX,BLADERF_CORR_FPGA_GAIN,offgain); - - for (i=0; i<10; i++) { - trx_brf_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0); - trx_brf_write(device,ptimestamp+5*RXDCLENGTH, (void **)&calib_tx_buffp, RXDCLENGTH, 0, 0); - } - // project on -3fs/8 (Image of TX signal in -ve frequencies) - for (meanI=meanQ=i=j=0; i>11; - meanQ+= (calib_buff[j]*cos_3fsover8[(i+2)&7] + calib_buff[j+1]*cos_3fsover8[i&7])>>11; - j+=2; - } - meanI/=RXDCLENGTH; - meanQ/=RXDCLENGTH; - - // printf("[BRF] TX DC (offQ): %d => (%d,%d)\n",offQ,meanI,meanQ); - } - - printf("[BRF] RX IQ offgain: %d => (%d,%d)\n",offgainold,meanIold,meanQold); - - bladerf_set_correction(brf->dev,BLADERF_MODULE_RX,BLADERF_CORR_FPGA_GAIN,offgainold); - } - - bladerf_set_frequency(brf->dev,BLADERF_MODULE_TX, (unsigned int) device->openair0_cfg->tx_freq[0]); - bladerf_set_loopback(brf->dev,BLADERF_LB_NONE); - bladerf_set_gain(brf->dev, BLADERF_MODULE_RX, (unsigned int) device->openair0_cfg->rx_gain[0]-device->openair0_cfg[0].rx_gain_offset[0]); - bladerf_set_gain(brf->dev, BLADERF_MODULE_TX, (unsigned int) device->openair0_cfg->tx_gain[0]); - // LOG_M("blade_rf_test.m","rxs",calib_buff,RXDCLENGTH,1,1); + return 0; } - /*! \brief Initialize Openair BLADERF target. It returns 0 if OK * \param device the hardware to use * \param openair0_cfg RF frontend parameters set by application @@ -939,266 +368,155 @@ void calibrate_rf(openair0_device_t *device) */ int device_init(openair0_device_t *device, openair0_config_t *openair0_cfg) { - int status; - brf_state_t *brf = (brf_state_t*)malloc(sizeof(brf_state_t)); - memset(brf, 0, sizeof(brf_state_t)); - /* device specific */ - //openair0_cfg->txlaunch_wait = 1;//manage when TX processing is triggered - //openair0_cfg->txlaunch_wait_slotcount = 1; //manage when TX processing is triggered + int status; + brf_state_t brf = {0}; + + switch ((long) openair0_cfg->sample_rate) { + case 46080000: + openair0_cfg->samples_per_packet = 8192; + openair0_cfg->tx_sample_advance = 0; + brf.num_buffers = 128; + brf.num_transfers = 32; + brf.rx_timeout_ms = 2000; + break; - // init required params - switch ((int)openair0_cfg->sample_rate) { case 30720000: - openair0_cfg->samples_per_packet = 2048; - openair0_cfg->tx_sample_advance = 0; - break; + openair0_cfg->samples_per_packet = 4096; + openair0_cfg->tx_sample_advance = 0; + brf.num_buffers = 128; + brf.num_transfers = 32; + brf.rx_timeout_ms = 2000; + break; + + case 23040000: + openair0_cfg->samples_per_packet = 2048; + openair0_cfg->tx_sample_advance = 0; + brf.num_buffers = 64; + brf.num_transfers = 16; + brf.rx_timeout_ms = 2000; + break; + case 15360000: - openair0_cfg->samples_per_packet = 2048; - openair0_cfg->tx_sample_advance = 0; - break; + openair0_cfg->samples_per_packet = 2048; + openair0_cfg->tx_sample_advance = 0; + + brf.num_buffers = 64; + brf.num_transfers = 16; + brf.rx_timeout_ms = 2000; + break; + case 11520000: + openair0_cfg->samples_per_packet = 2048; + openair0_cfg->tx_sample_advance = 0; + brf.num_buffers = 64; + brf.num_transfers = 16; + brf.rx_timeout_ms = 2000; + break; + case 7680000: - openair0_cfg->samples_per_packet = 1024; - openair0_cfg->tx_sample_advance = 0; - break; + openair0_cfg->samples_per_packet = 1024; + openair0_cfg->tx_sample_advance = 0; + brf.num_buffers = 32; + brf.num_transfers = 16; + brf.rx_timeout_ms = 2000; + break; + case 1920000: - openair0_cfg->samples_per_packet = 256; - openair0_cfg->tx_sample_advance = 50; - break; + openair0_cfg->samples_per_packet = 256; + openair0_cfg->tx_sample_advance = 50; + brf.num_buffers = 128; + brf.num_transfers = 16; + brf.rx_timeout_ms = 2000; + break; + default: - printf("Error: unknown sampling rate %f\n",openair0_cfg->sample_rate); - exit(-1); - break; - } - openair0_cfg->rx_gain_calib_table = calib_table_fx4; + LOG_E(HW, "Error: unknown sampling rate %f\n", openair0_cfg->sample_rate); + return -1; + break; + } - // The number of buffers to use in the underlying data stream - brf->num_buffers = 128; - // the size of the underlying stream buffers, in samples - brf->buffer_size = (unsigned int) openair0_cfg->samples_per_packet;//*sizeof(int32_t); // buffer size = 4096 for sample_len of 1024 - brf->num_transfers = 16; - brf->rx_timeout_ms = 0; - brf->tx_timeout_ms = 0; - brf->sample_rate=(unsigned int)openair0_cfg->sample_rate; + brf.tx_timeout_ms = 2000; + brf.buffer_size = (unsigned int)openair0_cfg->samples_per_packet; + brf.sample_rate = (unsigned int)openair0_cfg->sample_rate; - memset(&brf->meta_rx, 0, sizeof(brf->meta_rx)); - memset(&brf->meta_tx, 0, sizeof(brf->meta_tx)); + float rxterm = brf.num_transfers * brf.buffer_size * 1000.0 / brf.rx_timeout_ms; + float txterm = brf.num_transfers * brf.buffer_size * 1000.0 / brf.tx_timeout_ms; + LOG_I(HW, + "sample rate %d trnsfers %d timeout rx %d tx %d buffer size %d (rx %f, tx %f)\n", + brf.sample_rate, + brf.num_transfers, + brf.rx_timeout_ms, + brf.tx_timeout_ms, + brf.buffer_size, + rxterm, + txterm); + DevAssert(brf.sample_rate > txterm * 1.25); // see doc on async interface + DevAssert(brf.sample_rate > rxterm * 1.25); - printf("\n[BRF] sampling_rate %u, num_buffers %u, buffer_size %u, num transfer %u, timeout_ms (rx %u, tx %u)\n", - brf->sample_rate, brf->num_buffers, brf->buffer_size,brf->num_transfers, brf->rx_timeout_ms, brf->tx_timeout_ms); + memset(&brf.meta_rx, 0, sizeof(brf.meta_rx)); + memset(&brf.meta_tx, 0, sizeof(brf.meta_tx)); - if ((status=bladerf_open(&brf->dev, "")) != 0 ) { - fprintf(stderr,"Failed to open brf device: %s\n",bladerf_strerror(status)); - brf_error(status); - } - printf("[BRF] init dev %p\n", brf->dev); - switch(bladerf_device_speed(brf->dev)) { - case BLADERF_DEVICE_SPEED_SUPER: - printf("[BRF] Device operates at max speed\n"); - break; - default: - printf("[BRF] Device does not operates at max speed, change the USB port\n"); - brf_error(BLADERF_ERR_UNSUPPORTED); - } - // RX - // Example of CLI output: RX Frequency: 2539999999Hz + // set this to BLADERF_LOG_LEVEL_VERBOSE for more detailed BladeRF info + bladerf_log_set_verbosity(BLADERF_LOG_LEVEL_INFO); + status = bladerf_open(&brf.dev, openair0_cfg->sdr_addrs); + BLADERF_CHECK(status == 0, "Init BladeRF device '%s'", openair0_cfg->sdr_addrs); - if ((status=bladerf_set_gain_mode(brf->dev, BLADERF_MODULE_RX, BLADERF_GAIN_MGC))) { - fprintf(stderr, "[BRF] Failed to disable AGC\n"); - brf_error(status); + if (bladerf_device_speed(brf.dev) != BLADERF_DEVICE_SPEED_SUPER) { + LOG_E(HW, "Device does not operate at max speed, change the USB port\n"); + return -1; } - if ((status=bladerf_set_frequency(brf->dev, BLADERF_MODULE_RX, (unsigned int) openair0_cfg->rx_freq[0])) != 0) { - fprintf(stderr,"Failed to set RX frequency: %s\n",bladerf_strerror(status)); - brf_error(status); - } else - printf("[BRF] set RX frequency to %u\n",(unsigned int)openair0_cfg->rx_freq[0]); + /** [Forcing Tuning Mode to FPGA] */ + const bladerf_tuning_mode tMode = BLADERF_TUNING_MODE_FPGA; + status = bladerf_set_tuning_mode(brf.dev, tMode); + BLADERF_CHECK(status == 0, "Set tuning mode FPGA"); + status = bladerf_set_gain_mode(brf.dev, BLADERF_CHANNEL_RX(0), BLADERF_GAIN_MGC); + BLADERF_CHECK(status == 0, "Disable AGC"); + channel_config_t rx_chan = { + .channel = BLADERF_CHANNEL_RX(0), + .frequency = openair0_cfg->rx_freq[0], + .sample_rate = openair0_cfg->sample_rate, + .bandwidth = openair0_cfg->rx_bw, + .gain = openair0_cfg->rx_gain[0], + }; + configure_channel(brf.dev, &rx_chan); - unsigned int actual_value=0; - if ((status=bladerf_set_sample_rate(brf->dev, BLADERF_MODULE_RX, (unsigned int) openair0_cfg->sample_rate, &actual_value)) != 0) { - fprintf(stderr,"Failed to set RX sample rate: %s\n", bladerf_strerror(status)); - brf_error(status); - } else - printf("[BRF] set RX sample rate to %u, %u\n", (unsigned int) openair0_cfg->sample_rate, actual_value); + // This is a hack. The USRP library currently does max_gain-tx_gain (tx_gain + // is the attenuation, so completely illogical), but let's keep what users + // would expect by doing the same. Existing configs can then be reused. + // Note also that the doc says that get_gain_range() should be called after + // setting the frequency, but the gain should normally come from outside, so + // be set by user and independent of the exact frequency. + const struct bladerf_range *g = NULL; + status = bladerf_get_gain_range(brf.dev, BLADERF_CHANNEL_TX(0), &g); + BLADERF_CHECK(status == 0 && g != NULL, "Get TX0 gain range"); + channel_config_t tx_chan = { + .channel = BLADERF_CHANNEL_TX(0), + .frequency = openair0_cfg->tx_freq[0], + .sample_rate = openair0_cfg->sample_rate, + .bandwidth = openair0_cfg->tx_bw, + .gain = g->max - openair0_cfg->tx_gain[0], // see comment above + }; + configure_channel(brf.dev, &tx_chan); + LOG_I(HW, "BLADERF: Initializing device done\n"); + device->type = BLADERF_DEV; + device->trx_start_func = trx_brf_start; + device->trx_end_func = trx_brf_end; + device->trx_read_func = trx_brf_read; + device->trx_write_func = trx_brf_write; + device->trx_get_stats_func = trx_brf_get_stats; + device->trx_reset_stats_func = trx_brf_reset_stats; + device->trx_stop_func = trx_brf_stop; + device->trx_set_freq_func = trx_brf_set_freq; + device->trx_set_gains_func = trx_brf_set_gains; + device->trx_write_init = trx_brf_write_init; + device->openair0_cfg = openair0_cfg; + device->priv = malloc_or_fail(sizeof(brf_state_t)); + *(brf_state_t *)device->priv = brf; - if ((status=bladerf_set_bandwidth(brf->dev, BLADERF_MODULE_RX, (unsigned int) openair0_cfg->rx_bw*2, &actual_value)) != 0) { - fprintf(stderr,"Failed to set RX bandwidth: %s\n", bladerf_strerror(status)); - brf_error(status); - } else - printf("[BRF] set RX bandwidth to %u, %u\n",(unsigned int)openair0_cfg->rx_bw*2, actual_value); - - set_rx_gain_offset(&openair0_cfg[0],0); - if ((status=bladerf_set_gain(brf->dev, BLADERF_MODULE_RX, (int) openair0_cfg->rx_gain[0]-openair0_cfg[0].rx_gain_offset[0])) != 0) { - fprintf(stderr,"Failed to set RX gain: %s\n",bladerf_strerror(status)); - brf_error(status); - } else - printf("[BRF] set RX gain to %d (%d)\n",(int)(openair0_cfg->rx_gain[0]-openair0_cfg[0].rx_gain_offset[0]),(int)openair0_cfg[0].rx_gain_offset[0]); - - // TX - - if ((status=bladerf_set_frequency(brf->dev, BLADERF_MODULE_TX, (unsigned int) openair0_cfg->tx_freq[0])) != 0) { - fprintf(stderr,"Failed to set TX frequency: %s\n",bladerf_strerror(status)); - brf_error(status); - } else - printf("[BRF] set TX Frequency to %u\n", (unsigned int) openair0_cfg->tx_freq[0]); - - if ((status=bladerf_set_sample_rate(brf->dev, BLADERF_MODULE_TX, (unsigned int) openair0_cfg->sample_rate, NULL)) != 0) { - fprintf(stderr,"Failed to set TX sample rate: %s\n", bladerf_strerror(status)); - brf_error(status); - } else - printf("[BRF] set TX sampling rate to %u \n", (unsigned int) openair0_cfg->sample_rate); - - if ((status=bladerf_set_bandwidth(brf->dev, BLADERF_MODULE_TX,(unsigned int)openair0_cfg->tx_bw*2, NULL)) != 0) { - fprintf(stderr, "Failed to set TX bandwidth: %s\n", bladerf_strerror(status)); - brf_error(status); - } else - printf("[BRF] set TX bandwidth to %u \n", (unsigned int) openair0_cfg->tx_bw*2); - - if ((status=bladerf_set_gain(brf->dev, BLADERF_MODULE_TX, (int) openair0_cfg->tx_gain[0])) != 0) { - fprintf(stderr,"Failed to set TX gain: %s\n",bladerf_strerror(status)); - brf_error(status); - } else - printf("[BRF] set the TX gain to %d\n", (int)openair0_cfg->tx_gain[0]); - - - /* Configure the device's TX module for use with the sync interface. - * SC16 Q11 samples *with* metadata are used. */ - if ((status=bladerf_sync_config(brf->dev, BLADERF_MODULE_TX,BLADERF_FORMAT_SC16_Q11_META,brf->num_buffers,brf->buffer_size,brf->num_transfers,brf->tx_timeout_ms)) != 0 ) { - fprintf(stderr,"Failed to configure TX sync interface: %s\n", bladerf_strerror(status)); - brf_error(status); - } else - printf("[BRF] configured TX sync interface \n"); - - /* Configure the device's RX module for use with the sync interface. - * SC16 Q11 samples *with* metadata are used. */ - if ((status=bladerf_sync_config(brf->dev, BLADERF_MODULE_RX, BLADERF_FORMAT_SC16_Q11_META,brf->num_buffers,brf->buffer_size,brf->num_transfers,brf->rx_timeout_ms)) != 0 ) { - fprintf(stderr,"Failed to configure RX sync interface: %s\n", bladerf_strerror(status)); - brf_error(status); - } else - printf("[BRF] configured Rx sync interface \n"); - - - /* We must always enable the TX module after calling bladerf_sync_config(), and - * before attempting to TX samples via bladerf_sync_tx(). */ - if ((status=bladerf_enable_module(brf->dev, BLADERF_MODULE_TX, true)) != 0) { - fprintf(stderr,"Failed to enable TX module: %s\n", bladerf_strerror(status)); - brf_error(status); - } else - printf("[BRF] TX module enabled \n"); - - /* We must always enable the RX module after calling bladerf_sync_config(), and - * before attempting to RX samples via bladerf_sync_rx(). */ - if ((status=bladerf_enable_module(brf->dev, BLADERF_MODULE_RX, true)) != 0) { - fprintf(stderr,"Failed to enable RX module: %s\n", bladerf_strerror(status)); - brf_error(status); - } else - printf("[BRF] RX module enabled \n"); - - // calibrate - -#if 0 - /* those calls fail for "Nuand bladeRF 2.0 (bladerf2)", so commented, - * but let's keep the code in case it's needed (for other boards, - * for anything, unfortunately we don't have other boards to test - * right now) - */ - if ((status=bladerf_calibrate_dc(brf->dev, BLADERF_DC_CAL_LPF_TUNING)) != 0 || - (status=bladerf_calibrate_dc(brf->dev, BLADERF_DC_CAL_TX_LPF)) != 0 || - (status=bladerf_calibrate_dc(brf->dev, BLADERF_DC_CAL_RX_LPF)) != 0 || - (status=bladerf_calibrate_dc(brf->dev, BLADERF_DC_CAL_RXVGA2)) != 0) { - fprintf(stderr, "[BRF] error calibrating\n"); - brf_error(status); - } else - printf("[BRF] calibration OK\n"); -#endif - - /* set log to info, available log levels are: - * - BLADERF_LOG_LEVEL_VERBOSE - * - BLADERF_LOG_LEVEL_DEBUG - * - BLADERF_LOG_LEVEL_INFO - * - BLADERF_LOG_LEVEL_WARNING - * - BLADERF_LOG_LEVEL_ERROR - * - BLADERF_LOG_LEVEL_CRITICAL - * - BLADERF_LOG_LEVEL_SILENT - */ - bladerf_log_set_verbosity(BLADERF_LOG_LEVEL_INFO); - - printf("BLADERF: Initializing openair0_device_t\n"); - device->Mod_id = num_devices++; - device->type = BLADERF_DEV; - device->trx_start_func = trx_brf_start; - device->trx_end_func = trx_brf_end; - device->trx_read_func = trx_brf_read; - device->trx_write_func = trx_brf_write; - device->trx_get_stats_func = trx_brf_get_stats; - device->trx_reset_stats_func = trx_brf_reset_stats; - device->trx_stop_func = trx_brf_stop; - device->trx_set_freq_func = trx_brf_set_freq; - device->trx_set_gains_func = trx_brf_set_gains; - device->trx_write_init = trx_brf_write_init; - device->openair0_cfg = openair0_cfg; - device->priv = (void *)brf; - - calibrate_rf(device); - - // memcpy((void*)&device->openair0_cfg,(void*)&openair0_cfg[0],sizeof(openair0_config_t)); - - if ((status=bladerf_enable_module(brf->dev, BLADERF_MODULE_TX, false)) != 0) { - fprintf(stderr,"Failed to enable TX module: %s\n", bladerf_strerror(status)); - abort(); - } - if ((status=bladerf_enable_module(brf->dev, BLADERF_MODULE_RX, false)) != 0) { - fprintf(stderr,"Failed to enable RX module: %s\n", bladerf_strerror(status)); - abort(); - } - - return 0; -} - - -/*! \brief bladeRF error report - * \param status - * \returns 0 on success - */ -int brf_error(int status) -{ - fprintf(stderr, "[BRF] brf_error: %s\n", bladerf_strerror(status)); - exit(-1); - return status; // or status error code -} - - -/*! \brief Open BladeRF from serial port - * \param serial name of serial port on which to open BladeRF device - * \returns bladerf device structure - */ -struct bladerf * open_bladerf_from_serial(const char *serial) -{ - int status; - struct bladerf *dev; - struct bladerf_devinfo info; - /* Initialize all fields to "don't care" wildcard values. - * - * Immediately passing this to bladerf_open_with_devinfo() would cause - * libbladeRF to open any device on any available backend. */ - bladerf_init_devinfo(&info); - /* Specify the desired device's serial number, while leaving all other - * fields in the info structure wildcard values */ - strncpy(info.serial, serial, BLADERF_SERIAL_LENGTH - 1); - info.serial[BLADERF_SERIAL_LENGTH - 1] = '\0'; - status = bladerf_open_with_devinfo(&dev, &info); - - if (status == BLADERF_ERR_NODEV) { - printf("No devices available with serial=%s\n", serial); - return NULL; - } else if (status != 0) { - fprintf(stderr, "Failed to open device with serial=%s (%s)\n", serial, bladerf_strerror(status)); - return NULL; - } else { - return dev; - } + return 0; } /*@}*/ diff --git a/radio/BLADERF/bladerf_lib.h b/radio/BLADERF/bladerf_lib.h deleted file mode 100644 index a3ba44ee6c..0000000000 --- a/radio/BLADERF/bladerf_lib.h +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The OpenAirInterface Software Alliance licenses this file to You under - * the OAI Public License, Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.openairinterface.org/?page_id=698 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *------------------------------------------------------------------------------- - * For more information about the OpenAirInterface (OAI) Software Alliance: - * contact@openairinterface.org - */ - -/** bladerf_lib.h - * - * Author: navid nikaein - */ - -#ifndef BLADERF_LIB_H -#define BLADERF_LIB_H - -#include - -#include "common_lib.h" - -/** @addtogroup _BLADERF_PHY_RF_INTERFACE_ - * @{ - */ - -/*! \brief BladeRF specific data structure */ -typedef struct { - - //! opaque BladeRF device struct. An empty ("") or NULL device identifier will result in the first encountered device being opened (using the first discovered backend) - struct bladerf *dev; - - //! Number of buffers - unsigned int num_buffers; - //! Buffer size - unsigned int buffer_size; - //! Number of transfers - unsigned int num_transfers; - //! RX timeout - unsigned int rx_timeout_ms; - //! TX timeout - unsigned int tx_timeout_ms; - //! Metadata for RX - struct bladerf_metadata meta_rx; - //!Metadata for TX - struct bladerf_metadata meta_tx; - //! Sample rate - unsigned int sample_rate; - //! time offset between transmiter timestamp and receiver timestamp; - double tdiff; - //! TX number of forward samples use brf_time_offset to get this value - int tx_forward_nsamps; //166 for 20Mhz - - - // -------------------------------- - // Debug and output control - // -------------------------------- - //! Number of underflows - int num_underflows; - //! Number of overflows - int num_overflows; - //! number of sequential errors - int num_seq_errors; - //! number of RX errors - int num_rx_errors; - //! Number of TX errors - int num_tx_errors; - - //! timestamp of current TX - uint64_t tx_current_ts; - //! timestamp of current RX - uint64_t rx_current_ts; - //! number of actual samples transmitted - uint64_t tx_actual_nsamps; - //! number of actual samples received - uint64_t rx_actual_nsamps; - //! number of TX samples - uint64_t tx_nsamps; - //! number of RX samples - uint64_t rx_nsamps; - //! number of TX count - uint64_t tx_count; - //! number of RX count - uint64_t rx_count; - //! timestamp of RX packet - openair0_timestamp_t rx_timestamp; - -} brf_state_t; -/* - * func prototypes - */ -/*! \brief BladeRF Init function (not used at the moment) - * \param device RF frontend parameters set by application - */ -int trx_brf_init(openair0_device_t *device); - -/*! \brief get current timestamp - *\param device the hardware to use - *\param module the bladeRf module - */ -openair0_timestamp_t trx_get_timestamp(openair0_device_t *device, bladerf_module module); - -/*! \brief bladeRF error report - * \param status - * \returns 0 on success - */ -int brf_error(int status); - -/*@}*/ - -#endif /* BLADERF_LIB_H */ diff --git a/radio/CMakeLists.txt b/radio/CMakeLists.txt index 1159416afa..c5da06ffde 100644 --- a/radio/CMakeLists.txt +++ b/radio/CMakeLists.txt @@ -1,3 +1,5 @@ +add_subdirectory(COMMON) + add_boolean_option(OAI_AW2SORI OFF "Activate OAI's AW2S driver" OFF) if(OAI_AW2SORI) add_subdirectory(AW2SORI) diff --git a/radio/COMMON/CMakeLists.txt b/radio/COMMON/CMakeLists.txt new file mode 100644 index 0000000000..af70ac4047 --- /dev/null +++ b/radio/COMMON/CMakeLists.txt @@ -0,0 +1,3 @@ +add_library(radio_common STATIC common_lib.c record_player.c) +target_include_directories(radio_common PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +target_link_libraries(radio_common PUBLIC dl log_headers shlib_loader) diff --git a/radio/COMMON/common_lib.c b/radio/COMMON/common_lib.c index 7b70dc20fa..4720347f20 100644 --- a/radio/COMMON/common_lib.c +++ b/radio/COMMON/common_lib.c @@ -40,7 +40,6 @@ #include "assertions.h" #include "common/utils/load_module_shlib.h" #include "common/utils/LOG/log.h" -//#include "targets/RT/USER/lte-softmodem.h" #include "executables/softmodem-common.h" #define MAX_GAP 100ULL diff --git a/radio/fhi_72/oran-config.c b/radio/fhi_72/oran-config.c index 8283b63a7d..89fd2e56db 100644 --- a/radio/fhi_72/oran-config.c +++ b/radio/fhi_72/oran-config.c @@ -461,14 +461,18 @@ char bbdev_dev[32] = ""; char bbdev_vfio_vf_token[64] = ""; #endif -static bool set_fh_io_cfg(struct xran_io_cfg *io_cfg, const paramdef_t *fhip, int nump, const int num_rus, const int is_du) +static bool set_fh_io_cfg(struct xran_io_cfg *io_cfg, const paramdef_t *fhip, int nump, const int num_rus) { DevAssert(fhip != NULL); int num_dev = gpd(fhip, nump, ORAN_CONFIG_DPDK_DEVICES)->numelt; AssertFatal(num_dev > 0, "need to provide DPDK devices for O-RAN 7.2 Fronthaul\n"); AssertFatal(num_dev < 17, "too many DPDK devices for O-RAN 7.2 Fronthaul\n"); - io_cfg->id = 1 - is_du; // 0 -> xran as O-DU; 1 -> xran as O-RU + int app_id_index = config_paramidx_fromname((paramdef_t *)fhip, nump, ORAN_CONFIG_APP_ID); + AssertFatal(app_id_index >= 0,"Index for %s config option not found!\n", ORAN_CONFIG_APP_ID); + io_cfg->id = config_get_processedint(config_get_if(), (paramdef_t *)&fhip[app_id_index]); + LOG_A(PHY, "Initializing XRAN layer as %s\n", io_cfg->id == XRAN_APP_ID_O_DU ? "O-DU" : "O-RU"); + io_cfg->num_vfs = num_dev; // number of VFs for C-plane and U-plane (should be even); max = XRAN_VF_MAX io_cfg->num_rxq = 1; // number of RX queues per VF for (int i = 0; i < num_dev; ++i) { @@ -541,16 +545,16 @@ static bool set_fh_io_cfg(struct xran_io_cfg *io_cfg, const paramdef_t *fhip, in /* if RU does support, io_cfg->eowd_cmn[0] should only be filled as id = O_DU; io_cfg->eowd_cmn[1] only used if id = O_RU */ const uint16_t owdm_enable = *gpd(fhip, nump, ORAN_CONFIG_ECPRI_OWDM)->uptr; if (owdm_enable) { - io_cfg->eowd_cmn[0].initiator_en = is_du ? 1 : 0; // 1 -> initiator (always O-DU), 0 -> recipient (always O-RU) - io_cfg->eowd_cmn[0].numberOfSamples = 8; // total number of samples to be collected and averaged per port - io_cfg->eowd_cmn[0].filterType = 0; // 0 -> simple average based on number of measurements; not used in xran in both E and F releases - io_cfg->eowd_cmn[0].responseTo = 10000000; // response timeout in [ns] - io_cfg->eowd_cmn[0].measVf = 0; // VF using the OWD transmitter; within xran, the measurements are calculated per each supported VF, but starts from measVf - io_cfg->eowd_cmn[0].measState = 0; // the state of the OWD transmitter; 0 -> OWDMTX_INIT (enum xran_owdm_tx_state) - io_cfg->eowd_cmn[0].measId = 0; // measurement ID to be used by the transmitter - io_cfg->eowd_cmn[0].measMethod = 0; // measurement method; 0 -> XRAN_REQUEST (enum xran_owd_meas_method) - io_cfg->eowd_cmn[0].owdm_enable = 1; // 1 -> enabled; 0 -> disabled - io_cfg->eowd_cmn[0].owdm_PlLength = 40; // payload in the measurement packet; 40 <= PlLength <= 1400 + io_cfg->eowd_cmn[XRAN_APP_ID_O_DU].initiator_en = io_cfg->id == XRAN_APP_ID_O_DU ? 1 : 0; // 1 -> initiator (always O-DU), 0 -> recipient (always O-RU) + io_cfg->eowd_cmn[XRAN_APP_ID_O_DU].numberOfSamples = 8; // total number of samples to be collected and averaged per port + io_cfg->eowd_cmn[XRAN_APP_ID_O_DU].filterType = 0; // 0 -> simple average based on number of measurements; not used in xran in both E and F releases + io_cfg->eowd_cmn[XRAN_APP_ID_O_DU].responseTo = 10000000; // response timeout in [ns] + io_cfg->eowd_cmn[XRAN_APP_ID_O_DU].measVf = 0; // VF using the OWD transmitter; within xran, the measurements are calculated per each supported VF, but starts from measVf + io_cfg->eowd_cmn[XRAN_APP_ID_O_DU].measState = 0; // the state of the OWD transmitter; 0 -> OWDMTX_INIT (enum xran_owdm_tx_state) + io_cfg->eowd_cmn[XRAN_APP_ID_O_DU].measId = 0; // measurement ID to be used by the transmitter + io_cfg->eowd_cmn[XRAN_APP_ID_O_DU].measMethod = 0; // measurement method; 0 -> XRAN_REQUEST (enum xran_owd_meas_method) + io_cfg->eowd_cmn[XRAN_APP_ID_O_DU].owdm_enable = 1; // 1 -> enabled; 0 -> disabled + io_cfg->eowd_cmn[XRAN_APP_ID_O_DU].owdm_PlLength = 40; // payload in the measurement packet; 40 <= PlLength <= 1400 } /* eCPRI OWDM per port variables for O-DU; this parameter is filled within xran library */ // eowd_port[0][XRAN_VF_MAX] @@ -679,19 +683,6 @@ static bool set_fh_init(void *mplane_api, struct xran_fh_init *fh_init, enum xra const int nfh = sizeofArray(FHconfigs); config_getlist(config_get_if(), &FH_ConfigList, FHconfigs, nfh, aprefix); - int num_rus = FH_ConfigList.numelt; // based on the number of fh_config sections -> number of RUs - int is_du = 0; - - int num_ru_addr = gpd(fhip, nump, ORAN_CONFIG_RU_ADDR)->numelt; - int num_du_addr = gpd(fhip, nump, ORAN_CONFIG_DU_ADDR)->numelt; - if (num_ru_addr > 0 && num_du_addr == 0) - is_du = 1; - else if (num_du_addr > 0 && num_ru_addr == 0) - is_du = 0; - else - AssertFatal(false, "Illegal node configuration, num_du_addr %d, num_ru_addr %d\n", num_du_addr, num_ru_addr); - fh_init->xran_ports = is_du == 1 ? num_rus : num_du_addr; - #ifdef OAI_MPLANE ru_session_list_t *ru_session_list = (ru_session_list_t *)mplane_api; int num_rus = ru_session_list->num_rus; @@ -718,8 +709,10 @@ static bool set_fh_init(void *mplane_api, struct xran_fh_init *fh_init, enum xra } } #else + int num_rus = FH_ConfigList.numelt; // based on the number of fh_config sections -> number of RUs + fh_init->xran_ports = num_rus; - if (!set_fh_io_cfg(&fh_init->io_cfg, fhip, nump, num_rus, is_du)) + if (!set_fh_io_cfg(&fh_init->io_cfg, fhip, nump, num_rus)) return false; if (!set_fh_eaxcid_conf(&fh_init->eAxCId_conf, xran_cat)) return false; @@ -728,13 +721,10 @@ static bool set_fh_init(void *mplane_api, struct xran_fh_init *fh_init, enum xra of DL fragments (nPrbElm) needed for transmission of one symbol. */ fh_init->mtu = *gpd(fhip, nump, ORAN_CONFIG_MTU)->uptr; - fh_init->p_o_du_addr = NULL; // DPDK retreives DU MAC address within the xran library with rte_eth_macaddr_get() function - - char **ru_addrs,**du_addrs; - - if (is_du > 0) { + if (fh_init->io_cfg.id == XRAN_APP_ID_O_DU) { + int num_ru_addr = gpd(fhip, nump, ORAN_CONFIG_RU_ADDR)->numelt; fh_init->p_o_ru_addr = calloc(num_ru_addr, sizeof(struct rte_ether_addr)); - ru_addrs = gpd(fhip, nump, ORAN_CONFIG_RU_ADDR)->strlistptr; + char **ru_addrs = gpd(fhip, nump, ORAN_CONFIG_RU_ADDR)->strlistptr; AssertFatal(fh_init->p_o_ru_addr != NULL, "out of memory\n"); for (int i = 0; i < num_ru_addr; ++i) { struct rte_ether_addr *ea = (struct rte_ether_addr *)fh_init->p_o_ru_addr; @@ -743,10 +733,12 @@ static bool set_fh_init(void *mplane_api, struct xran_fh_init *fh_init, enum xra return false; } } + // DPDK retreives DU MAC address within the xran library with rte_eth_macaddr_get() function fh_init->p_o_du_addr = NULL; } else { + int num_du_addr = gpd(fhip, nump, ORAN_CONFIG_DU_ADDR)->numelt; fh_init->p_o_du_addr = calloc(num_du_addr, sizeof(struct rte_ether_addr)); - du_addrs = gpd(fhip, nump, ORAN_CONFIG_DU_ADDR)->strlistptr; + char **du_addrs = gpd(fhip, nump, ORAN_CONFIG_DU_ADDR)->strlistptr; AssertFatal(fh_init->p_o_du_addr != NULL, "out of memory\n"); for (int i = 0; i < num_du_addr; ++i) { struct rte_ether_addr *ea = (struct rte_ether_addr *)fh_init->p_o_du_addr; @@ -755,6 +747,7 @@ static bool set_fh_init(void *mplane_api, struct xran_fh_init *fh_init, enum xra return false; } } + // DPDK retreives RU MAC address within the xran library with rte_eth_macaddr_get() function fh_init->p_o_ru_addr = NULL; } #endif diff --git a/radio/fhi_72/oran-params.h b/radio/fhi_72/oran-params.h index 9f12418909..caa56ccca4 100644 --- a/radio/fhi_72/oran-params.h +++ b/radio/fhi_72/oran-params.h @@ -32,7 +32,7 @@ #define ORAN_CONFIG_IO_CORE "io_core" #define ORAN_CONFIG_WORKER_CORES "worker_cores" #define ORAN_CONFIG_DU_KEYPAIR "du_key_pair" // only needed for M-plane -#define ORAN_CONFIG_DU_ADDR "du_addr" // only needed for M-plane +#define ORAN_CONFIG_DU_ADDR "du_addr" // needed for M-plane or O-RU #define ORAN_CONFIG_VLAN_TAG "vlan_tag" // only needed for M-plane #define ORAN_CONFIG_RU_ADDR "ru_addr" // not needed if M-plane used #define ORAN_CONFIG_RU_USERNAME "ru_username" // only needed for M-plane @@ -44,6 +44,14 @@ #define ORAN_CONFIG_DPDK_MEM_SIZE "dpdk_mem_size" #define ORAN_CONFIG_DPDK_IOVA_MODE "dpdk_iova_mode" #define ORAN_CONFIG_ECPRI_OWDM "owdm_enable" +#define ORAN_CONFIG_APP_ID "app_id" + +#define APP_ID_O_DU "DU" +#define APP_ID_O_RU "RU" + +// Provide a definition since xran is missing one +#define XRAN_APP_ID_O_DU 0 +#define XRAN_APP_ID_O_RU 1 // clang-format off // TODO: PCI addr check @@ -66,6 +74,7 @@ {ORAN_CONFIG_DPDK_MEM_SIZE, "DPDK huge page pre-allocation in MiB\n", 0, .uptr=NULL, .defuintval=8192, TYPE_UINT, 0}, \ {ORAN_CONFIG_DPDK_IOVA_MODE, "DPDK IOVA mode\n", 0, .strptr=NULL, .defstrval="PA", TYPE_STRING, 0}, \ {ORAN_CONFIG_ECPRI_OWDM, "eCPRI One-Way Delay Measurements\n", PARAMFLAG_BOOL, .uptr=NULL, .defuintval=0, TYPE_UINT, 0}, \ + {ORAN_CONFIG_APP_ID, "Either DU or RU", 0, .strptr=NULL, .defstrval=APP_ID_O_DU, TYPE_STRING, 0} \ } // clang-format off @@ -88,6 +97,8 @@ { .s3a = { config_checkstr_assign_integer, \ {"PA", "VA"}, {0, 1}, 2} }, \ { .s5 = { NULL } }, \ + { .s3a = { config_checkstr_assign_integer, \ + {APP_ID_O_DU, APP_ID_O_RU}, {XRAN_APP_ID_O_DU, XRAN_APP_ID_O_RU}, 2} }, \ } diff --git a/radio/fhi_72/oran_isolate.c b/radio/fhi_72/oran_isolate.c index 89b16d7198..c9991549b1 100644 --- a/radio/fhi_72/oran_isolate.c +++ b/radio/fhi_72/oran_isolate.c @@ -29,7 +29,6 @@ #include "xran_sync_api.h" #include "common/utils/LOG/log.h" -#include "common/utils/LOG/vcd_signal_dumper.h" #include "openair1/PHY/defs_gNB.h" #include "openair1/PHY/NR_TRANSPORT/nr_transport_proto.h" #include "oaioran.h" diff --git a/radio/vrtsim/CMakeLists.txt b/radio/vrtsim/CMakeLists.txt index 71d18cc5bc..9726743361 100644 --- a/radio/vrtsim/CMakeLists.txt +++ b/radio/vrtsim/CMakeLists.txt @@ -1,13 +1,36 @@ +# Noise device static lib add_library(noise_device STATIC noise_device.c) target_link_libraries(noise_device PRIVATE log_headers) -add_library(vrtsim MODULE vrtsim.c) -set_target_properties(vrtsim PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) -target_link_libraries(vrtsim PRIVATE SIMU shm_td_iq_channel actor noise_device log_headers) +# vrtsim module +add_library(vrtsim MODULE + vrtsim.c + cirdb_provider.c + cirdb_yaml.cpp +) +set_target_properties(vrtsim PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) + +find_package(yaml-cpp REQUIRED) + +# Link libraries - always link yaml-cpp +target_link_libraries(vrtsim + PRIVATE + SIMU + shm_td_iq_channel + actor + noise_device + log_headers + pthread + yaml-cpp +) + +# Optional taps client add_boolean_option(OAI_VRTSIM_TAPS_CLIENT OFF "Enable taps client" ON) if (OAI_VRTSIM_TAPS_CLIENT) + target_compile_definitions(vrtsim PRIVATE ENABLE_TAPS_CLIENT=1) target_link_libraries(vrtsim PRIVATE taps_client) + file(DOWNLOAD "https://gitlab.eurecom.fr/oai/raytracing-channel-emulator/-/raw/59589ad2a534881810b2aa7dd81e63066245bb8d/server/api/taps.fbs?inline=false" ${CMAKE_SOURCE_DIR}/taps.fbs @@ -21,6 +44,7 @@ if (OAI_VRTSIM_TAPS_CLIENT) DEPENDS ${TAPS_IF_PATH} COMMENT "Generating flatbuffers API from ${TAPS_IF_PATH}" ) + add_library(taps_api INTERFACE) target_include_directories(taps_api INTERFACE ${CMAKE_CURRENT_BINARY_DIR}) diff --git a/radio/vrtsim/cirdb_provider.c b/radio/vrtsim/cirdb_provider.c new file mode 100644 index 0000000000..39a7bff29c --- /dev/null +++ b/radio/vrtsim/cirdb_provider.c @@ -0,0 +1,348 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common/utils/LOG/log.h" +#include "common/utils/assertions.h" +#include "SIMULATION/TOOLS/sim.h" + +#include "cirdb_provider.h" +#include "cirdb_yaml.h" + +#define NUM_TAPS_BUFFERS 4 +#define MAX_L_PUBLISH 8 + +static inline double dabs(double x) +{ + return x < 0.0 ? -x : x; +} + +typedef struct { + void *taps_blob; /* contiguous complexf for all links */ + channel_desc_t *ch; /* ch_ps points into taps_blob */ +} cirdb_buffer_t; + +typedef struct { + /* I/O */ + FILE *fh; + + /* Selected entry meta */ + int model_id; + int n_tx; + int n_rx; + int L_full; + int S; + double fs_hz; + double snapshot_dt_s; + float ds_ns; + float speed_mps; + uint64_t sel_offset; + uint64_t sel_nbytes; + + /* Publication control */ + uint32_t L_out; + uint32_t snap_idx; + + /* Shape in publication */ + int num_tx; + int num_rx; + + /* Double buffering for readers */ + cirdb_buffer_t bufs[NUM_TAPS_BUFFERS]; + int cur; + + /* Temporary read buffer for one snapshot of full L_full taps */ + void *snapshot_tmp; + size_t snapshot_tmp_bytes; + + /* Published pointer location owned by caller */ + channel_desc_t **channel_desc_out; + + /* Bookkeeping of last step computed from elapsed time */ + int64_t last_step_applied; /* -1 until first update */ +} cirdb_g; + +static cirdb_g G; + +static inline void fread_exact(void *dst, size_t sz, FILE *fh) +{ + size_t r = fread(dst, 1, sz, fh); + if (r != sz) { + LOG_E(HW, "CIRDB short read: expected %zu got %zu errno=%d\n", sz, r, errno); + abort(); + } +} + +static inline size_t cf_bytes(size_t n) +{ + return n * sizeof(struct complexf); +} + +/* Point channel_desc ch_ps into a compacted taps buffer */ +static void point_channel_desc(channel_desc_t *ch, struct complexf *base, int num_tx, int num_rx, size_t L_out) +{ + for (int aarx = 0; aarx < num_rx; aarx++) { + for (int aatx = 0; aatx < num_tx; aatx++) { + int link = aarx + num_rx * aatx; + ch->ch_ps[link] = &base[link * L_out]; + } + } + ch->channel_length = L_out; + ch->path_loss_dB = 0; + ch->nb_tx = num_tx; + ch->nb_rx = num_rx; +} + +/* Copy first L_out taps per link from a full L_full snapshot */ +static void compact_L_out(struct complexf *dst, + const struct complexf *src_full, + int num_tx, + int num_rx, + size_t L_full, + size_t L_out) +{ + for (int aatx = 0; aatx < num_tx; aatx++) { + for (int aarx = 0; aarx < num_rx; aarx++) { + size_t link = aarx + num_rx * aatx; + const struct complexf *src = &src_full[link * L_full]; + struct complexf *dst_link = &dst[link * L_out]; + memcpy(dst_link, src, cf_bytes(L_out)); + } + } +} + +/* Load snapshot index s into the next publication buffer and flip */ +static void load_snapshot_and_publish(uint32_t s) +{ + uint64_t stride = (uint64_t)G.n_tx * G.n_rx * G.L_full * sizeof(struct complexf); + uint64_t off = G.sel_offset + (uint64_t)s * stride; + + if (fseeko(G.fh, (off_t)off, SEEK_SET) != 0) { + LOG_E(HW, "CIRDB fseoko failed errno=%d\n", errno); + abort(); + } + fread_exact(G.snapshot_tmp, (size_t)stride, G.fh); + + int next = (G.cur + 1) % NUM_TAPS_BUFFERS; + cirdb_buffer_t *buf = &G.bufs[next]; + + compact_L_out((struct complexf *)buf->taps_blob, (const struct complexf *)G.snapshot_tmp, G.n_tx, G.n_rx, G.L_full, G.L_out); + + point_channel_desc(buf->ch, (struct complexf *)buf->taps_blob, G.n_tx, G.n_rx, G.L_out); + + *G.channel_desc_out = buf->ch; + G.cur = next; +} + +void cirdb_connect(int id, + int num_tx_antennas, + int num_rx_antennas, + const cirdb_select_opts_t *sel, + channel_desc_t **channel_desc_out) +{ + (void)id; + memset(&G, 0, sizeof(G)); + G.channel_desc_out = channel_desc_out; + G.num_tx = num_tx_antennas; + G.num_rx = num_rx_antennas; + G.cur = 0; + G.last_step_applied = -1; + + /* Require explicit paths in cirdb_select_opts_t */ + AssertFatal(sel != NULL, "cirdb_select_opts_t must not be NULL\n"); + AssertFatal(sel->bin_path && sel->bin_path[0], "CIRDB requires explicit bin_path in cirdb_select_opts_t\n"); + AssertFatal(sel->yaml_path && sel->yaml_path[0], "CIRDB requires explicit yaml_path in cirdb_select_opts_t\n"); + + const char *use_bin = sel->bin_path; + const char *sidecar = sel->yaml_path; + + LOG_I(HW, "CIRDB: using database %s with metadata %s\n", use_bin, sidecar); + + G.fh = fopen(use_bin, "rb"); + AssertFatal(G.fh != NULL, "Cannot open CIRDB database file '%s': %s\n", use_bin, strerror(errno)); + + /* Selection request */ + int want_model_id = sel->want_model_id; + AssertFatal(want_model_id >= 0 && want_model_id <= 4, + "Invalid model_id=%d (valid: 0..4)\n", + want_model_id); + float want_ds = (sel && sel->want_ds_ns > 0) ? sel->want_ds_ns : -1.0f; + float want_speed = (sel && sel->want_speed_mps > 0) ? sel->want_speed_mps : -1.0f; + + LOG_I(HW, + "CIRDB: Searching for entry with model_id=%d, antennas=%dx%d, DS=%.1fns, speed=%.1fm/s\n", + want_model_id, + G.num_tx, + G.num_rx, + want_ds, + want_speed); + + cirdb_entry_meta_t m = (cirdb_entry_meta_t){0}; + cirdb_select_req_t req = {.want_model_id = want_model_id, + .want_tx = G.num_tx, + .want_rx = G.num_rx, + .want_ds_ns = want_ds, + .want_speed_mps = want_speed, + .allow_shape_swap = 0, + .w_ds = 1.0f, + .w_speed = 0.2f, + .yaml_path = sidecar}; + + int ok = cirdb_yaml_select(&req, &m); + + // STRICT validation: fail if no match found + AssertFatal(ok > 0, + "No suitable CIR entry found in YAML %s\n" + " Requested: model_id=%d, antennas=%dx%d, DS=%.1fns, speed=%.1fm/s\n" + " Possible causes:\n" + " - No entry with model_id=%d in database\n" + " - No entry with antenna configuration %dx%d in database\n" + " Check the YAML file for available entries\n", + sidecar, + want_model_id, + G.num_tx, + G.num_rx, + want_ds, + want_speed, + want_model_id, + G.num_tx, + G.num_rx); + + // STRICT validation: antenna dimensions must match exactly + AssertFatal(m.n_tx == G.num_tx && m.n_rx == G.num_rx, + "CIRDB antenna mismatch: requested %dx%d but selected entry has %dx%d\n" + " This should never happen - indicates a bug in selection logic\n", + G.num_tx, + G.num_rx, + m.n_tx, + m.n_rx); + + // STRICT validation: model_id must match if specified + if (want_model_id >= 0) { + AssertFatal(m.model_id == want_model_id, + "CIRDB model_id mismatch: requested %d but selected entry has %d\n" + " This should never happen - indicates a bug in selection logic\n", + want_model_id, + m.model_id); + } + + // FLEXIBLE: warn if delay spread doesn't match exactly (using closest available) + if (want_ds > 0 && dabs(m.ds_ns - want_ds) > 0.1) { + LOG_W(HW, "CIRDB: Requested delay spread %.1f ns not available in database, using closest match: %.1f ns\n", want_ds, m.ds_ns); + } + + // FLEXIBLE: warn if speed doesn't match exactly (using closest available) + if (want_speed > 0 && dabs(m.speed_mps - want_speed) > 0.1) { + LOG_W(HW, + "CIRDB: Requested speed %.1f m/s not available in database, using closest match: %.1f m/s\n", + want_speed, + m.speed_mps); + } + + G.model_id = m.model_id; + G.n_tx = m.n_tx; + G.n_rx = m.n_rx; + G.L_full = m.L; + G.S = m.S; + G.fs_hz = m.fs_hz; + G.snapshot_dt_s = m.snapshot_dt_s; + G.ds_ns = m.ds_ns; + G.speed_mps = m.speed_mps; + G.sel_offset = m.offset_bytes; + G.sel_nbytes = m.nbytes; + G.L_out = (m.L <= MAX_L_PUBLISH ? (uint32_t)m.L : MAX_L_PUBLISH); + G.snap_idx = 0; + + G.snapshot_tmp_bytes = (size_t)G.n_tx * G.n_rx * G.L_full * sizeof(struct complexf); + G.snapshot_tmp = calloc_or_fail(1, G.snapshot_tmp_bytes); + + for (int i = 0; i < NUM_TAPS_BUFFERS; i++) { + size_t blob_cf = (size_t)G.n_tx * G.n_rx * G.L_out; + G.bufs[i].taps_blob = calloc_or_fail(blob_cf, sizeof(struct complexf)); + G.bufs[i].ch = calloc_or_fail(1, sizeof(channel_desc_t)); + G.bufs[i].ch->ch_ps = calloc_or_fail(G.n_rx * G.n_tx, sizeof(struct complexf *)); + G.bufs[i].ch->nb_tx = G.n_tx; + G.bufs[i].ch->nb_rx = G.n_rx; + } + + if (G.S > 0) { + load_snapshot_and_publish(0); + } + + LOG_I(HW, + "CIRDB: Selected entry - model=%d DS=%.3fns shape=%ux%u L=%u/%u S=%u fs=%.0f dt=%.6fs speed=%.3fm/s\n", + G.model_id, + G.ds_ns, + G.n_tx, + G.n_rx, + G.L_out, + G.L_full, + G.S, + G.fs_hz, + G.snapshot_dt_s, + G.speed_mps); +} + +void cirdb_update(uint64_t ns_since_start) +{ + if (!G.channel_desc_out || !*G.channel_desc_out) + return; + if (G.S <= 0) + return; + + double dt_s = (G.snapshot_dt_s > 0.0 ? G.snapshot_dt_s : 0.5); + if (dt_s <= 0.0) + dt_s = 0.5; + + double steps_f = (ns_since_start * 1e-9) / dt_s; + int64_t step = (int64_t)(steps_f >= 0.0 ? steps_f : 0.0); + + if (step != G.last_step_applied) { + uint32_t s = (uint32_t)(step % G.S); + load_snapshot_and_publish(s); + G.snap_idx = s; + G.last_step_applied = step; + } +} + +void cirdb_stop(void) +{ + for (int i = 0; i < NUM_TAPS_BUFFERS; i++) { + free(G.bufs[i].taps_blob); + if (G.bufs[i].ch) { + free(G.bufs[i].ch->ch_ps); + free(G.bufs[i].ch); + } + } + free(G.snapshot_tmp); + if (G.fh) + fclose(G.fh); + memset(&G, 0, sizeof(G)); + G.last_step_applied = -1; +} diff --git a/radio/vrtsim/cirdb_provider.h b/radio/vrtsim/cirdb_provider.h new file mode 100644 index 0000000000..42e49f3c5e --- /dev/null +++ b/radio/vrtsim/cirdb_provider.h @@ -0,0 +1,60 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#ifndef OAI_VRTSIM_CIRDB_PROVIDER_H +#define OAI_VRTSIM_CIRDB_PROVIDER_H + +#include +#include "SIMULATION/TOOLS/sim.h" + +/* + Selection and timing options: + + want_model_id: TDL family index. Allowed: 0 (TDL-A), 1 (TDL-B), 2 (TDL-C), 3 (TDL-D), 4 (TDL-E). + Set < 0 to express no preference. + want_ds_ns: RMS delay spread in ns. Typical values: 30, 100, 300. Nonnegative float. + Set < 0 to express no preference. + want_speed_mps: UE speed in m/s. Typical: walking ≈ 1.5, urban 3..10, vehicular 10..30. + Set < 0 to express no preference. +*/ +typedef struct { + const char *yaml_path; /* optional absolute path to vrtsim.yaml */ + const char *bin_path; /* optional absolute path to cir_db.bin */ + + int want_model_id; /* 0..4, or <0 for no preference */ + float want_ds_ns; /* nonnegative, or <0 for no preference */ + float want_speed_mps; /* nonnegative, or <0 for no preference */ +} cirdb_select_opts_t; + +/* Initialize provider and publish snapshot 0 through channel_desc_out. */ +void cirdb_connect(int id, + int num_tx_antennas, + int num_rx_antennas, + const cirdb_select_opts_t *sel, + channel_desc_t **channel_desc_out); + +/* Advance snapshot selection based on elapsed nanoseconds since the start. */ +void cirdb_update(uint64_t ns_since_start); + +/* Tear down and free resources. */ +void cirdb_stop(void); + +#endif /* OAI_VRTSIM_CIRDB_PROVIDER_H */ diff --git a/radio/vrtsim/cirdb_yaml.cpp b/radio/vrtsim/cirdb_yaml.cpp new file mode 100644 index 0000000000..aa95b09505 --- /dev/null +++ b/radio/vrtsim/cirdb_yaml.cpp @@ -0,0 +1,152 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#include "cirdb_yaml.h" +#include +#include +#include +extern "C" { +#include "common/utils/LOG/log.h" +} + +static inline double dabs(double x) +{ + return x < 0 ? -x : x; +} + +int cirdb_yaml_scan(const char *yaml_path) +{ + YAML::Node root; + try { + root = YAML::LoadFile(yaml_path); + } catch (const YAML::Exception &e) { + LOG_E(HW, "CIRDB YAML parse error in %s: %s\n", yaml_path, e.what()); + return -1; + } + auto ents = root["entries"]; + if (!ents || !ents.IsSequence()) { + LOG_E(HW, "CIRDB YAML: missing or invalid 'entries' in %s\n", yaml_path); + return -1; + } + + int n = static_cast(ents.size()); + LOG_I(HW, "CIRDB YAML: %d entries in %s\n", n, yaml_path); + return n; +} + +int cirdb_yaml_select(const cirdb_select_req_t *req, cirdb_entry_meta_t *out) +{ + if (!req || !out || !req->yaml_path) { + LOG_E(HW, "CIRDB YAML: invalid arguments to cirdb_yaml_select\n"); + return -1; + } + + YAML::Node root; + try { + root = YAML::LoadFile(req->yaml_path); + } catch (const YAML::Exception &e) { + LOG_E(HW, "CIRDB YAML parse error in %s: %s\n", req->yaml_path, e.what()); + return -1; + } + + auto ents = root["entries"]; + if (!ents || !ents.IsSequence()) { + LOG_E(HW, "CIRDB YAML: missing or invalid 'entries' in %s\n", req->yaml_path); + return -1; + } + + bool found = false; + double best_cost = 1e300; + cirdb_entry_meta_t best{}; + + // Determine if specific parameters are required + bool want_specific_model = (req->want_model_id >= 0); + bool want_specific_antennas = (req->want_tx > 0 && req->want_rx > 0); + + for (std::size_t i = 0; i < ents.size(); ++i) { + const YAML::Node e = ents[i]; + int pair_order = e["pair_order"].as(0); + + int model_id = e["model_id"].as(-1); + + // STRICT: model_id must match if specified + if (want_specific_model && model_id != req->want_model_id) + continue; + + int n_tx = e["n_tx"].as(0); + int n_rx = e["n_rx"].as(0); + + // STRICT: antenna dimensions must match exactly if specified + bool shape_ok = true; + if (want_specific_antennas) { + shape_ok = (n_tx == req->want_tx && n_rx == req->want_rx); + bool shape_swap_ok = req->allow_shape_swap ? (n_tx == req->want_rx && n_rx == req->want_tx) : false; + shape_ok = shape_ok || shape_swap_ok; + + if (!shape_ok) + continue; // Skip entries with wrong antenna configuration + } + + // STRICT: antenna dimensions must match exactly if specified + if (want_specific_antennas) { + if (n_tx != req->want_tx || n_rx != req->want_rx) + continue; // Skip entries with wrong antenna configuration + } + + float ds_ns = e["ds_ns"].as(0.0f); + float sp = e["speed_mps"].as(0.0f); + + // FLEXIBLE: delay spread and speed - find closest match using cost function + double cds = 0.0; + double csp = 0.0; + + if (req->want_ds_ns > 0) + cds = dabs((double)ds_ns - (double)req->want_ds_ns); + + if (req->want_speed_mps > 0) + csp = dabs((double)sp - (double)req->want_speed_mps); + + double cost = (req->w_ds > 0 ? req->w_ds : 1.0) * cds + (req->w_speed > 0 ? req->w_speed : 0.2) * csp; + + if (!found || cost < best_cost) { + best_cost = cost; + found = true; + + best.model_id = model_id; + best.n_tx = n_tx; + best.n_rx = n_rx; + best.L = e["L"].as(0); + best.S = e["S"].as(0); + best.fs_hz = e["fs_hz"].as(0.0); + best.snapshot_dt_s = e["snapshot_dt_s"].as(0.0); + best.ds_ns = ds_ns; + best.speed_mps = sp; + best.pair_order = pair_order; + best.offset_bytes = e["offset_bytes"].as(0); + best.nbytes = e["nbytes"].as(0); + } + } + + if (!found) + return 0; + *out = best; + return 1; +} diff --git a/radio/vrtsim/cirdb_yaml.h b/radio/vrtsim/cirdb_yaml.h new file mode 100644 index 0000000000..e3cf61f8cf --- /dev/null +++ b/radio/vrtsim/cirdb_yaml.h @@ -0,0 +1,61 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + int want_model_id; // 0..4 for TDL-A..E, or -1 for any + int want_tx; // exact match + int want_rx; // exact match + float want_ds_ns; // nearest + float want_speed_mps; // nearest + int allow_shape_swap; // allow TX/RX swap when matching + float w_ds; // DS distance weight + float w_speed; // speed distance weight + const char *yaml_path; // sidecar path +} cirdb_select_req_t; + +typedef struct { + int model_id; + int n_tx; + int n_rx; + int L; // taps per link in file + int S; // snapshots + double fs_hz; + double snapshot_dt_s; + float ds_ns; + float speed_mps; + int pair_order; + uint64_t offset_bytes; + uint64_t nbytes; +} cirdb_entry_meta_t; + +int cirdb_yaml_select(const cirdb_select_req_t *req, cirdb_entry_meta_t *out); +int cirdb_yaml_scan(const char *yaml_path); + +#ifdef __cplusplus +} +#endif diff --git a/radio/vrtsim/vrtsim.c b/radio/vrtsim/vrtsim.c index f2f9cfef1d..9a0cd779dc 100644 --- a/radio/vrtsim/vrtsim.c +++ b/radio/vrtsim/vrtsim.c @@ -51,6 +51,8 @@ #include "noise_device.h" #include "simde/x86/avx512.h" #include "taps_client.h" +#include "cirdb_provider.h" +#include "cirdb_yaml.h" // Simulator role typedef enum { ROLE_SERVER = 1, ROLE_CLIENT } role; @@ -79,6 +81,15 @@ typedef enum { ROLE_SERVER = 1, ROLE_CLIENT } role; {"chanmod", "Enable channel modelling", 0, .iptr = &vrtsim_state->chanmod, .defintval = 0, TYPE_INT, 0}, \ {"taps-socket", TAPS_SOCKET_HLP, 0, .strptr = &vrtsim_state->taps_socket, .defstrval = NULL, TYPE_STRING, 0}, \ {"client-num-rx-antennas", CLIENT_NUM_RX_HLP, 0, .iptr = &vrtsim_state->client_num_rx_antennas, .defintval = 1, TYPE_INT, 0}, \ + /* CIR DB enable and paths */ \ + {"cirdb", "Use CIR database for channel taps (1 yes, 0 no)", 0, .iptr = &vrtsim_state->use_cirdb, .defintval = 0, TYPE_INT, 0}, \ + {"cirdb-path", "Directory that holds vrtsim.yaml and cir_db.bin", 0, .strptr = &vrtsim_state->cirdb_path, .defstrval = NULL, TYPE_STRING, 0}, \ + {"cirdb_yaml", "Absolute path to CIR DB YAML file (optional, overrides cirdb-path)", 0, .strptr = &vrtsim_state->cirdb_yaml, .defstrval = NULL, TYPE_STRING, 0}, \ + {"cirdb_file", "Absolute path to CIR DB binary file (optional, overrides cirdb-path)", 0, .strptr = &vrtsim_state->cirdb_file, .defstrval = NULL, TYPE_STRING, 0}, \ + /* CIR DB selection knobs */ \ + {"cirdb_model_id", "Preferred TDL model id 0..4", 0, .iptr = &vrtsim_state->cirdb_model_id, .defintval = 0, TYPE_INT, 0}, \ + {"cirdb_ds_ns", "Desired RMS delay spread in ns", 0, .dblptr = &vrtsim_state->cirdb_ds_ns, .defdblval = 10.0, TYPE_DOUBLE, 0}, \ + {"cirdb_speed_mps", "Desired speed in m/s", 0, .dblptr = &vrtsim_state->cirdb_speed_mps, .defdblval = 1.5, TYPE_DOUBLE, 0}, \ }; // clang-format on @@ -126,6 +137,14 @@ typedef struct { char *taps_socket; int client_num_rx_antennas; struct timespec start_ts; + /* CIR DB state */ + int use_cirdb; + char *cirdb_path; + char *cirdb_yaml; + char *cirdb_file; + int cirdb_model_id; + double cirdb_ds_ns; + double cirdb_speed_mps; } vrtsim_state_t; static void histogram_add(histogram_t *histogram, double diff) @@ -166,7 +185,7 @@ static void load_channel_model(vrtsim_state_t *vrtsim_state) char *model_name = vrtsim_state->role == ROLE_CLIENT ? "client_tx_channel_model" : "server_tx_channel_model"; vrtsim_state->channel_desc = find_channel_desc_fromname(model_name); AssertFatal(vrtsim_state->channel_desc != NULL, - "Could not find model name %s. Make sure it is present in the config file", + "Could not find model name %s. Make sure it is present in the config file\n", model_name); LOG_I(HW, "Channel model %s parameters: path_loss_dB=%.2f, nb_tx=%d, nb_rx=%d, channel_length=%d\n", @@ -201,6 +220,9 @@ static void vrtsim_readconfig(vrtsim_state_t *vrtsim_state) AssertFatal(false, "Invalid configuration: Build with OAI_VRTSIM_TAPS_CLIENT to use taps socket\n"); } #endif + if (vrtsim_state->use_cirdb) { + LOG_A(HW, "VRTSIM: CIR DB is enabled at runtime\n"); + } } static void *vrtsim_timing_job(void *arg) @@ -305,7 +327,7 @@ static int vrtsim_connect(openair0_device_t *device) // Handle channel modelling after number of RX antennas are known int num_tx_stats = 1; - if (vrtsim_state->chanmod || vrtsim_state->taps_socket) { + if (vrtsim_state->chanmod || vrtsim_state->taps_socket || vrtsim_state->use_cirdb) { vrtsim_state->channel_modelling_actors = calloc_or_fail(vrtsim_state->peer_info.num_rx_antennas, sizeof(Actor_t)); for (int i = 0; i < vrtsim_state->peer_info.num_rx_antennas; i++) { init_actor(&vrtsim_state->channel_modelling_actors[i], "chanmod", -1); @@ -316,6 +338,53 @@ static int vrtsim_connect(openair0_device_t *device) device->openair0_cfg[0].tx_num_channels, vrtsim_state->peer_info.num_rx_antennas, &vrtsim_state->channel_desc); + } else if (vrtsim_state->use_cirdb) { + const char *yaml_path = NULL; + const char *bin_path = NULL; + + if (vrtsim_state->cirdb_yaml && vrtsim_state->cirdb_yaml[0]) + yaml_path = vrtsim_state->cirdb_yaml; + if (vrtsim_state->cirdb_file && vrtsim_state->cirdb_file[0]) + bin_path = vrtsim_state->cirdb_file; + + char yaml_buf[PATH_MAX]; + char bin_buf[PATH_MAX]; + + if (!yaml_path || !bin_path) { + const char *base = vrtsim_state->cirdb_path; + if (base && base[0]) { + if (!yaml_path) { + snprintf(yaml_buf, sizeof(yaml_buf), "%s/%s", base, "vrtsim.yaml"); + yaml_path = yaml_buf; + } + if (!bin_path) { + snprintf(bin_buf, sizeof(bin_buf), "%s/%s", base, "cir_db.bin"); + bin_path = bin_buf; + } + } + } + + cirdb_select_opts_t sel = (cirdb_select_opts_t){0}; + sel.yaml_path = yaml_path; + sel.bin_path = bin_path; + AssertFatal(vrtsim_state->cirdb_model_id >= 0 && vrtsim_state->cirdb_model_id <= 4, + "Invalid cirdb_model_id=%d (valid: 0..4)\n", + vrtsim_state->cirdb_model_id); + sel.want_model_id = vrtsim_state->cirdb_model_id; + sel.want_ds_ns = (float)(vrtsim_state->cirdb_ds_ns > 0.0 ? vrtsim_state->cirdb_ds_ns : -1.0); + sel.want_speed_mps = (float)(vrtsim_state->cirdb_speed_mps > 0.0 ? vrtsim_state->cirdb_speed_mps : -1.0); + + LOG_A(HW, + "VRTSIM: CIR DB select yaml='%s' bin='%s'\n", + sel.yaml_path ? sel.yaml_path : "(auto)", + sel.bin_path ? sel.bin_path : "(auto)"); + + cirdb_connect(0, + device->openair0_cfg[0].tx_num_channels, + vrtsim_state->peer_info.num_rx_antennas, + &sel, + &vrtsim_state->channel_desc); + LOG_A(HW, "VRTSIM: channel taps via CIR DB\n"); } else { load_channel_model(vrtsim_state); } @@ -381,20 +450,21 @@ static void perform_channel_modelling(void *arg) int nb_tx_ant = channel_modelling_args->nbAnt; c16_t **input_samples = (c16_t **)channel_modelling_args->samples; - int aligned_nsamps = ceil_mod(nsamps, (512 / 8) / sizeof(cf_t)); - cf_t samples[aligned_nsamps] __attribute__((aligned(64))); - // Apply noise from global settings - get_noise_vector((float *)samples, nsamps * 2); - channel_desc_t *channel_desc = vrtsim_state->channel_desc; if (channel_desc == NULL) { return; } + if (vrtsim_state->use_cirdb) { + double seconds = (double)channel_modelling_args->timestamp / vrtsim_state->sample_rate; + uint64_t elapsed_ns = (uint64_t)(seconds * 1e9 + 0.5); + cirdb_update(elapsed_ns); + } + cf_t channel_impulse_response[nb_tx_ant][channel_desc->channel_length]; cf_t *channel_impulse_response_p[nb_tx_ant]; - if (!vrtsim_state->taps_socket) { + if (!vrtsim_state->taps_socket && !vrtsim_state->use_cirdb) { const float pathloss_linear = powf(10, channel_desc->path_loss_dB / 20.0); // Convert channel impulse response to float + apply pathloss for (int aatx = 0; aatx < nb_tx_ant; aatx++) { @@ -413,16 +483,25 @@ static void perform_channel_modelling(void *arg) } for (int batch_index = 0; batch_index < channel_modelling_args->num_batches; batch_index++) { - int start_sample = batch_index * channel_modelling_args->batch_size; - int num_samples = min(channel_modelling_args->batch_size, nsamps - start_sample); + const int start_sample = batch_index * channel_modelling_args->batch_size; + const int num_samples = min(channel_modelling_args->batch_size, nsamps - start_sample); if (start_sample >= nsamps) { break; } + + const int aligned_batch = ceil_mod(num_samples, (512 / 8) / sizeof(cf_t)); + cf_t samples[aligned_batch] __attribute__((aligned(64))); + memset(samples, 0, sizeof(samples)); + + // Apply noise from global settings (only valid samples) + get_noise_vector((float *)samples, num_samples * 2); + for (int aatx = 0; aatx < nb_tx_ant; aatx++) { - for (int i = start_sample; i < start_sample + num_samples; i++) { - cf_t *impulse_response = channel_impulse_response_p[aatx]; + cf_t *impulse_response = channel_impulse_response_p[aatx]; + for (int i = 0; i < num_samples; i++) { + const int gi = start_sample + i; for (int l = 0; l < channel_desc->channel_length; l++) { - int idx = i - l; + const int idx = gi - l; // TODO: Use AVX2 for this c16_t tx_input = idx >= 0 ? input_samples[aatx][idx] : channel_modelling_args->saved_samples[aatx][SAVED_SAMPLES_LEN + idx]; @@ -432,31 +511,32 @@ static void perform_channel_modelling(void *arg) } } - // Convert to c16_t - c16_t samples_out[aligned_nsamps] __attribute__((aligned(64))); - #if defined(__AVX512F__) - for (int i = 0; i < aligned_nsamps / 8; i++) { + // Convert to c16_t (only once per batch) + c16_t samples_out[aligned_batch] __attribute__((aligned(64))); +#if defined(__AVX512F__) + for (int i = 0; i < aligned_batch / 8; i++) { simde__m512 *in = (simde__m512 *)&samples[i * 8]; simde__m256i *out = (simde__m256i *)&samples_out[i * 8]; *out = simde_mm512_cvtsepi32_epi16(simde_mm512_cvtps_epi32(*in)); } - #elif defined(__AVX2__) - for (int i = 0; i < aligned_nsamps / 4; i++) { +#elif defined(__AVX2__) + for (int i = 0; i < aligned_batch / 4; i++) { simde__m256 *in = (simde__m256 *)&samples[i * 4]; simde__m128i *out = (simde__m128i *)&samples_out[i * 4]; *out = simde_mm256_cvtsepi32_epi16(simde_mm256_cvtps_epi32(*in)); } - #else - for (int i = 0; i < nsamps; i++) { +#else + for (int i = 0; i < num_samples; i++) { samples_out[i].r = lroundf(samples[i].r); samples_out[i].i = lroundf(samples[i].i); } - #endif +#endif + // Write the batch as a single contiguous transfer vrtsim_write_internal(channel_modelling_args->vrtsim_state, - channel_modelling_args->timestamp, + channel_modelling_args->timestamp + start_sample, samples_out, - channel_modelling_args->nsamps, + num_samples, aarx, channel_modelling_args->flags, aarx); @@ -507,7 +587,6 @@ static int vrtsim_write_with_chanmod(vrtsim_state_t *vrtsim_state, for (int aatx = 0; aatx < nbAnt; aatx++) memcpy(&args->saved_samples[aatx][0], saved_samples[aatx], sizeof(c16_t) * SAVED_SAMPLES_LEN); } - memcpy(args->saved_samples, saved_samples, sizeof(saved_samples)); pushNotifiedFIFO(&vrtsim_state->channel_modelling_actors[aarx].fifo, task); } @@ -543,7 +622,7 @@ static int vrtsim_write(openair0_device_t *device, AssertFatal(timestamp >= 0, "Timestamp must be non-negative, got %ld\n", timestamp); timestamp -= device->openair0_cfg->command_line_sample_advance; vrtsim_state_t *vrtsim_state = (vrtsim_state_t *)device->priv; - bool channel_modelling = vrtsim_state->chanmod || vrtsim_state->taps_socket; + bool channel_modelling = vrtsim_state->chanmod || vrtsim_state->taps_socket || vrtsim_state->use_cirdb; return channel_modelling ? vrtsim_write_with_chanmod(vrtsim_state, timestamp, samplesVoid, nsamps, nbAnt, flags) : vrtsim_write_internal(vrtsim_state, timestamp, (c16_t *)samplesVoid[0], nsamps, 0, flags, 0); } @@ -609,7 +688,7 @@ static void vrtsim_end(openair0_device_t *device) } tx_timing_t *tx_timing = vrtsim_state->tx_timing; - if (vrtsim_state->chanmod || vrtsim_state->taps_socket) { + if (vrtsim_state->chanmod || vrtsim_state->taps_socket || vrtsim_state->use_cirdb) { for (int i = 0; i < vrtsim_state->peer_info.num_rx_antennas; i++) { shutdown_actor(&vrtsim_state->channel_modelling_actors[i]); } @@ -623,7 +702,9 @@ static void vrtsim_end(openair0_device_t *device) } tx_timing->average_tx_budget /= vrtsim_state->peer_info.num_rx_antennas; free_noise_device(); - if (vrtsim_state->taps_socket) { + if (vrtsim_state->use_cirdb) { + cirdb_stop(); + } else if (vrtsim_state->taps_socket) { taps_client_stop(); } } @@ -711,8 +792,9 @@ __attribute__((__visibility__("default"))) int device_init(openair0_device_t *de vrtsim_state->tx_num_channels = openair0_cfg->tx_num_channels; vrtsim_state->rx_num_channels = openair0_cfg->rx_num_channels; - if (vrtsim_state->chanmod || vrtsim_state->taps_socket) { + if (vrtsim_state->chanmod || vrtsim_state->taps_socket || vrtsim_state->use_cirdb) { init_channelmod(); + randominit(); int noise_power_dBFS = get_noise_power_dBFS(); int16_t noise_power = noise_power_dBFS == INVALID_DBFS_VALUE ? 0 : (int16_t)(32767.0 / powf(10.0, .05 * -noise_power_dBFS)); LOG_A(HW, "VRTSIM: Noise power %d sample value\n", noise_power); diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band77.273prb.fhi72.4x4-das-benetel650_650.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band77.273prb.fhi72.4x4-das-benetel650_650.conf index 4a5553006a..a16d392ac4 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band77.273prb.fhi72.4x4-das-benetel650_650.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band77.273prb.fhi72.4x4-das-benetel650_650.conf @@ -181,7 +181,7 @@ MACRLCs = ( pusch_FailureThres = 100; ul_max_mcs = 28; min_grant_prb = 1; - set_analog_beamforming = 1; + set_analog_beamforming = "lophy"; beam_duration = 1; beams_per_period = 2; } diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.106prb.bladerf2.0xa4.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.106prb.bladerf2.0xa4.conf new file mode 100644 index 0000000000..d53e3137fe --- /dev/null +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.106prb.bladerf2.0xa4.conf @@ -0,0 +1,229 @@ +Active_gNBs = ( "gNB-OAI"); +# Asn1_verbosity, choice in: none, info, annoying +Asn1_verbosity = "none"; + +gNBs = +( + { + ////////// Identification parameters: + gNB_ID = 0xe00; + gNB_name = "gNB-OAI"; + + // Tracking area code, 0x0000 and 0xfffe are reserved values + tracking_area_code = 1; + plmn_list = ({ mcc = 001; mnc = 01; mnc_length = 2; snssaiList = ({ sst = 1; }) }); + + nr_cellid = 12345678L; + + ////////// Physical parameters: + + min_rxtxtime = 2; + + servingCellConfigCommon = ( + { + #spCellConfigCommon + + physCellId = 0; + +# downlinkConfigCommon + #frequencyInfoDL + # this is 3300.60 MHz + 53*12*30e-3 MHz = 3319.68 + absoluteFrequencySSB = 621312; + dl_frequencyBand = 78; + # this is 3300.60 MHz + dl_absoluteFrequencyPointA = 620040; + #scs-SpecificCarrierList + dl_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_subcarrierSpacing = 1; + dl_carrierBandwidth = 106; + #initialDownlinkBWP + #genericParameters + # this is RBstart=0,L=106 (275*(L-1))+RBstart + initialDLBWPlocationAndBandwidth = 28875; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialDLBWPsubcarrierSpacing = 1; + #pdcch-ConfigCommon + initialDLBWPcontrolResourceSetZero = 11; + initialDLBWPsearchSpaceZero = 0; + + + + #uplinkConfigCommon + #frequencyInfoUL + ul_frequencyBand = 78; + #scs-SpecificCarrierList + ul_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_subcarrierSpacing = 1; + ul_carrierBandwidth = 106; + pMax = 20; + #initialUplinkBWP + #genericParameters + initialULBWPlocationAndBandwidth = 28875; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialULBWPsubcarrierSpacing = 1; + #rach-ConfigCommon + #rach-ConfigGeneric + prach_ConfigurationIndex = 98; +#prach_msg1_FDM +#0 = one, 1=two, 2=four, 3=eight + prach_msg1_FDM = 0; + prach_msg1_FrequencyStart = 0; + zeroCorrelationZoneConfig = 12; + preambleReceivedTargetPower = -96; +#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200) + preambleTransMax = 6; +#powerRampingStep +# 0=dB0,1=dB2,2=dB4,3=dB6 + powerRampingStep = 1; +#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR +#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen + ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4; +#one (0..15) 4,8,12,16,...60,64 + ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 15; +#ra_ContentionResolutionTimer +#(0..7) 8,16,24,32,40,48,56,64 + ra_ContentionResolutionTimer = 7; + rsrp_ThresholdSSB = 19; +#prach-RootSequenceIndex_PR +#1 = 839, 2 = 139 + prach_RootSequenceIndex_PR = 2; + prach_RootSequenceIndex = 1; + # SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex + # + msg1_SubcarrierSpacing = 1, +# restrictedSetConfig +# 0=unrestricted, 1=restricted type A, 2=restricted type B + restrictedSetConfig = 0, + + msg3_DeltaPreamble = 1; + p0_NominalWithGrant =-90; + +# pucch-ConfigCommon setup : +# pucchGroupHopping +# 0 = neither, 1= group hopping, 2=sequence hopping + pucchGroupHopping = 0; + hoppingId = 40; + p0_nominal = -90; + + ssb_PositionsInBurst_Bitmap = 1; + +# ssb_periodicityServingCell +# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1 + ssb_periodicityServingCell = 2; + +# dmrs_TypeA_position +# 0 = pos2, 1 = pos3 + dmrs_TypeA_Position = 0; + +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + subcarrierSpacing = 1; + + + #tdd-UL-DL-ConfigurationCommon +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + referenceSubcarrierSpacing = 1; + # pattern1 + # dl_UL_TransmissionPeriodicity + # 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10 + dl_UL_TransmissionPeriodicity = 6; + nrofDownlinkSlots = 7; + nrofDownlinkSymbols = 6; + nrofUplinkSlots = 2; + nrofUplinkSymbols = 4; + + ssPBCH_BlockPower = -25; + } + + ); + + + # ------- SCTP definitions + SCTP : + { + # Number of streams to use in input/output + SCTP_INSTREAMS = 2; + SCTP_OUTSTREAMS = 2; + }; + + ////////// AMF parameters: + amf_ip_address = ({ ipv4 = "192.168.70.132"; }); + + + NETWORK_INTERFACES : + { + GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.70.129/24"; + GNB_IPV4_ADDRESS_FOR_NGU = "192.168.70.129/24"; + GNB_PORT_FOR_S1U = 2152; # Spec 2152 + }; + + } +); + +MACRLCs = ( +{ + num_cc = 1; + tr_s_preference = "local_L1"; + tr_n_preference = "local_RRC"; + pusch_TargetSNRx10 = 200; + pucch_TargetSNRx10 = 200; +} +); + +L1s = ( +{ + num_cc = 1; + tr_n_preference = "local_mac"; + prach_dtx_threshold = 200; +} +); + +RUs = ( +{ + local_rf = "yes" + nb_tx = 1 + nb_rx = 1 + att_tx = 0; + att_rx = 0; + bands = [78]; + max_pdschReferenceSignalPower = -27; + max_rxgain = 75; + eNB_instances = [0]; + sdr_addrs = "libusb: serial=730f5a36545849d9aa71ca6cf50e8ca0" +} +); + +security = { + # preferred ciphering algorithms + # the first one of the list that an UE supports in chosen + # valid values: nea0, nea1, nea2, nea3 + ciphering_algorithms = ( "nea0" ); + + # preferred integrity algorithms + # the first one of the list that an UE supports in chosen + # valid values: nia0, nia1, nia2, nia3 + integrity_algorithms = ( "nia2", "nia0" ); + + # setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter + # what 'ciphering_algorithms' configures; same thing for 'drb_integrity' + drb_ciphering = "yes"; + drb_integrity = "no"; +}; + +log_config : +{ + global_log_level = "info"; + hw_log_level = "info"; + phy_log_level = "info"; + mac_log_level = "info"; + rlc_log_level = "info"; + pdcp_log_level = "info"; + rrc_log_level = "info"; +}; diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.51PRB.bladerf2.0xa4.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.51PRB.bladerf2.0xa4.conf new file mode 100644 index 0000000000..b48977847b --- /dev/null +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.51PRB.bladerf2.0xa4.conf @@ -0,0 +1,229 @@ +Active_gNBs = ( "gNB-OAI"); +# Asn1_verbosity, choice in: none, info, annoying +Asn1_verbosity = "none"; + +gNBs = +( + { + ////////// Identification parameters: + gNB_ID = 0xe00; + gNB_name = "gNB-OAI"; + + // Tracking area code, 0x0000 and 0xfffe are reserved values + tracking_area_code = 1; + plmn_list = ({ mcc = 001; mnc = 01; mnc_length = 2; snssaiList = ({ sst = 1; }) }); + + nr_cellid = 12345678L; + + ////////// Physical parameters: + + min_rxtxtime = 2; + + servingCellConfigCommon = ( + { + #spCellConfigCommon + + physCellId = 0; + +# downlinkConfigCommon + #frequencyInfoDL + # this is 3600 MHz + 43 PRBs@30kHz SCS (same as initial BWP) + absoluteFrequencySSB = 636672; + dl_frequencyBand = 78; + # this is 3600 MHz + dl_absoluteFrequencyPointA = 636060; + #scs-SpecificCarrierList + dl_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_subcarrierSpacing = 1; + dl_carrierBandwidth = 51; + #initialDownlinkBWP + #genericParameters + # this is RBstart=27,L=48 (275*(L-1))+RBstart + initialDLBWPlocationAndBandwidth = 13750; # 6366 12925 12956 28875 12952 +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialDLBWPsubcarrierSpacing = 1; + #pdcch-ConfigCommon + initialDLBWPcontrolResourceSetZero = 11; + initialDLBWPsearchSpaceZero = 0; + + #uplinkConfigCommon + #frequencyInfoUL + ul_frequencyBand = 78; + #scs-SpecificCarrierList + ul_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_subcarrierSpacing = 1; + ul_carrierBandwidth = 51; + pMax = 20; + #initialUplinkBWP + #genericParameters + initialULBWPlocationAndBandwidth = 13750; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialULBWPsubcarrierSpacing = 1; + #rach-ConfigCommon + #rach-ConfigGeneric + prach_ConfigurationIndex = 98; +#prach_msg1_FDM +#0 = one, 1=two, 2=four, 3=eight + prach_msg1_FDM = 0; + prach_msg1_FrequencyStart = 0; + zeroCorrelationZoneConfig = 13; + preambleReceivedTargetPower = -96; +#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200) + preambleTransMax = 6; +#powerRampingStep +# 0=dB0,1=dB2,2=dB4,3=dB6 + powerRampingStep = 1; +#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR +#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen + ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4; +#oneHalf (0..15) 4,8,12,16,...60,64 + ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 14; +#ra_ContentionResolutionTimer +#(0..7) 8,16,24,32,40,48,56,64 + ra_ContentionResolutionTimer = 7; + rsrp_ThresholdSSB = 19; +#prach-RootSequenceIndex_PR +#1 = 839, 2 = 139 + prach_RootSequenceIndex_PR = 2; + prach_RootSequenceIndex = 1; + # SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex + # + msg1_SubcarrierSpacing = 1, +# restrictedSetConfig +# 0=unrestricted, 1=restricted type A, 2=restricted type B + restrictedSetConfig = 0, + + msg3_DeltaPreamble = 1; + p0_NominalWithGrant =-90; + +# pucch-ConfigCommon setup : +# pucchGroupHopping +# 0 = neither, 1= group hopping, 2=sequence hopping + pucchGroupHopping = 0; + hoppingId = 40; + p0_nominal = -90; + ssb_PositionsInBurst_Bitmap = 1; + +# ssb_periodicityServingCell +# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1 + ssb_periodicityServingCell = 2; + +# dmrs_TypeA_position +# 0 = pos2, 1 = pos3 + dmrs_TypeA_Position = 0; + +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + subcarrierSpacing = 1; + + + #tdd-UL-DL-ConfigurationCommon +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + referenceSubcarrierSpacing = 1; + # pattern1 + # dl_UL_TransmissionPeriodicity + # 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10 + dl_UL_TransmissionPeriodicity = 6; + nrofDownlinkSlots = 7; + nrofDownlinkSymbols = 6; + nrofUplinkSlots = 2; + nrofUplinkSymbols = 4; + + ssPBCH_BlockPower = -25; + } + + ); + + + # ------- SCTP definitions + SCTP : + { + # Number of streams to use in input/output + SCTP_INSTREAMS = 2; + SCTP_OUTSTREAMS = 2; + }; + + + ////////// AMF parameters: + amf_ip_address = ({ ipv4 = "192.168.70.132"; }); + + + NETWORK_INTERFACES : + { + GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.70.129/24"; + GNB_IPV4_ADDRESS_FOR_NGU = "192.168.70.129/24"; + GNB_PORT_FOR_S1U = 2152; # Spec 2152 + }; + + } +); + +MACRLCs = ( +{ + num_cc = 1; + tr_s_preference = "local_L1"; + tr_n_preference = "local_RRC"; + pusch_TargetSNRx10 = 200; + pucch_TargetSNRx10 = 200; +} +); + +L1s = ( +{ + num_cc = 1; + tr_n_preference = "local_mac"; + prach_dtx_threshold = 150; + pucch0_dtx_threshold = 100; + ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0 +} +); + +RUs = ( +{ + local_rf = "yes" + nb_tx = 1 + nb_rx = 1 + att_tx = 0; + att_rx = 0; + bands = [78]; + max_pdschReferenceSignalPower = -27; + max_rxgain = 75; + eNB_instances = [0]; + sdr_addrs = "libusb: serial=730f5a36545849d9aa71ca6cf50e8ca0" +} +); + +security = { + # preferred ciphering algorithms + # the first one of the list that an UE supports in chosen + # valid values: nea0, nea1, nea2, nea3 + ciphering_algorithms = ( "nea0" ); + + # preferred integrity algorithms + # the first one of the list that an UE supports in chosen + # valid values: nia0, nia1, nia2, nia3 + integrity_algorithms = ( "nia2", "nia0" ); + + # setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter + # what 'ciphering_algorithms' configures; same thing for 'drb_integrity' + drb_ciphering = "yes"; + drb_integrity = "no"; +}; + +log_config : +{ + global_log_level = "info"; + hw_log_level = "info"; + phy_log_level = "info"; + mac_log_level = "info"; + rlc_log_level = "info"; + pdcp_log_level = "info"; + rrc_log_level = "info"; +}; diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf index 7f750d7259..ec036d30ca 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf @@ -248,3 +248,15 @@ e2_agent = { #sm_dir = "/path/where/the/SMs/are/located/" sm_dir = "/usr/local/lib/flexric/" }; + +vrtsim : +{ + #role = "server"; # "server" or "client" + #client-num-rx-antennas = 1; # publish expected client RX count + #cirdb = 1; # Enable CIR DB taps + # cirdb_yaml = "/path/to/vrtsim.yaml"; + # cirdb_file = "/path/to/cir_db.bin"; + cirdb_model_id = 0; # 0..4 for 3GPP TDL-A..E + cirdb_ds_ns = 10.0; # RMS delay spread in ns (10, 30) + cirdb_speed_mps = 1.5; # UE speed in m/s (1.5, 30) +}; diff --git a/tests/nr-cuup/nr-cuup-load-test.c b/tests/nr-cuup/nr-cuup-load-test.c index 0774b26b1b..047d316afc 100644 --- a/tests/nr-cuup/nr-cuup-load-test.c +++ b/tests/nr-cuup/nr-cuup-load-test.c @@ -418,7 +418,9 @@ static void *sender_thread(void *v) { struct thr_data *d = v; - size_t oh = d->is_ul ? 3 : 0; + // Overhead: PDCP header (2 bytes) for UL packets + // SDAP headers are disabled, so only PDCP header is added + size_t oh = d->is_ul ? 2 : 0; size_t packet_len = d->data_len + oh; uint8_t buf[packet_len]; memset(buf, 0, packet_len);