diff --git a/CMakeLists.txt b/CMakeLists.txt index 03cbc144c7..de7e300a67 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -463,7 +463,7 @@ add_library(f1ap ${F1AP_DIR}/f1ap_handlers.c ${F1AP_DIR}/f1ap_itti_messaging.c) target_include_directories(f1ap PUBLIC F1AP_DIR) -target_link_libraries(f1ap PUBLIC asn1_f1ap) +target_link_libraries(f1ap PUBLIC asn1_f1ap L2_NR) target_link_libraries(f1ap PRIVATE ngap nr_rrc HASHTABLE) # LPP @@ -1181,9 +1181,10 @@ target_include_directories(PHY_UE PRIVATE ${blas_INCLUDE_DIRS} ${lapacke_INCLUDE add_library(PHY_NR_COMMON ${PHY_NR_SRC_COMMON}) add_library(PHY_NR ${PHY_NR_SRC}) +target_link_libraries(PHY_NR nr_phy_common nr_common) add_library(PHY_NR_UE ${PHY_NR_UE_SRC}) -target_link_libraries(PHY_NR_UE PRIVATE asn1_nr_rrc_hdrs) +target_link_libraries(PHY_NR_UE PRIVATE asn1_nr_rrc_hdrs nr_phy_common nr_common) add_library(PHY_RU ${PHY_SRC_RU}) target_link_libraries(PHY_RU PRIVATE asn1_lte_rrc_hdrs) @@ -1413,7 +1414,6 @@ set (MAC_NR_SRC_UE ${NR_UE_MAC_DIR}/nr_ue_scheduler.c ${NR_UE_MAC_DIR}/nr_ue_dci_configuration.c ${NR_UE_MAC_DIR}/nr_ra_procedures.c - ${NR_UE_MAC_DIR}/nr_ue_power_procedures.c ) set (ENB_APP_SRC @@ -1462,7 +1462,7 @@ endif() add_library(MAC_UE_NR ${MAC_NR_SRC_UE}) -target_link_libraries(MAC_UE_NR PRIVATE asn1_lte_rrc_hdrs asn1_nr_rrc_hdrs) +target_link_libraries(MAC_UE_NR PRIVATE asn1_lte_rrc_hdrs asn1_nr_rrc_hdrs PUBLIC nr_ue_power_procedures) add_library(L2_LTE ${L2_LTE_SRC}) target_link_libraries(L2_LTE PRIVATE asn1_lte_rrc_hdrs asn1_nr_rrc_hdrs) @@ -1481,7 +1481,7 @@ add_library(e1_if target_link_libraries(e1_if PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs asn1_f1ap SECURITY ${OPENSSL_LIBRARIES} e1ap GTPV1U) -target_link_libraries(L2_NR PRIVATE f1ap x2ap s1ap ngap nr_rrc e1ap nr_rlc) +target_link_libraries(L2_NR PRIVATE f1ap x2ap s1ap ngap nr_rrc e1ap nr_rlc nr_common) if(E2_AGENT) target_link_libraries(L2_NR PUBLIC e2_agent e2_agent_arg e2_ran_func_du_cucp_cuup) target_compile_definitions(L2_NR PRIVATE ${E2AP_VERSION} ${KPM_VERSION} E2_AGENT) @@ -1516,7 +1516,7 @@ target_link_libraries(L2_UE PRIVATE asn1_lte_rrc_hdrs) add_library( NR_L2_UE ${NR_L2_SRC_UE} ${MAC_NR_SRC_UE} ) target_link_libraries(NR_L2_UE PRIVATE f1ap nr_rlc) -target_link_libraries(NR_L2_UE PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs) +target_link_libraries(NR_L2_UE PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs nr_common nr_ue_power_procedures) add_library(MAC_NR_COMMON ${OPENAIR2_DIR}/LAYER2/NR_MAC_COMMON/nr_mac_common.c @@ -2083,7 +2083,6 @@ add_executable(nr-softmodem ${OPENAIR2_DIR}/RRC/NAS/rb_config.c ${OPENAIR_DIR}/common/utils/lte/ue_power.c ${OPENAIR_DIR}/common/utils/lte/prach_utils.c - ${OPENAIR_DIR}/common/utils/nr/nr_common.c ${PHY_INTERFACE_DIR}/queue_t.c ${OPENAIR1_DIR}/PHY/TOOLS/phy_scope_interface.c ) @@ -2152,7 +2151,6 @@ add_executable(nr-uesoftmodem ${OPENAIR2_DIR}/LAYER2/NR_MAC_COMMON/nr_mac_common.c ${OPENAIR2_DIR}/RRC/NAS/rb_config.c ${OPENAIR3_DIR}/NAS/UE/nas_ue_task.c - ${OPENAIR_DIR}/common/utils/nr/nr_common.c ${OPENAIR1_DIR}/PHY/TOOLS/phy_scope_interface.c ) @@ -2207,7 +2205,6 @@ target_link_libraries(rftest PRIVATE asn1_lte_rrc_hdrs asn1_nr_rrc_hdrs) add_executable(polartest ${OPENAIR1_DIR}/PHY/CODING/TESTBENCH/polartest.c - ${OPENAIR_DIR}/common/utils/nr/nr_common.c ) target_link_libraries(polartest PRIVATE -Wl,--start-group UTIL SIMU PHY_COMMON PHY_NR PHY_NR_COMMON PHY_NR_UE CONFIG_LIB -Wl,--end-group @@ -2216,7 +2213,6 @@ target_link_libraries(polartest PRIVATE add_executable(smallblocktest ${OPENAIR1_DIR}/PHY/CODING/TESTBENCH/smallblocktest.c - ${OPENAIR_DIR}/common/utils/nr/nr_common.c ) target_link_libraries(smallblocktest PRIVATE @@ -2242,7 +2238,6 @@ target_link_libraries(ldpctest PRIVATE add_executable(nr_dlschsim ${OPENAIR1_DIR}/SIMULATION/NR_PHY/dlschsim.c ${OPENAIR1_DIR}/SIMULATION/NR_PHY/nr_dummy_functions.c - ${OPENAIR_DIR}/common/utils/nr/nr_common.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 @@ -2253,7 +2248,6 @@ target_link_libraries(nr_dlschsim PRIVATE asn1_nr_rrc_hdrs) add_executable(nr_pbchsim ${OPENAIR1_DIR}/SIMULATION/NR_PHY/pbchsim.c ${OPENAIR1_DIR}/SIMULATION/NR_PHY/nr_dummy_functions.c - ${OPENAIR_DIR}/common/utils/nr/nr_common.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 @@ -2264,7 +2258,6 @@ 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 ${OPENAIR1_DIR}/SIMULATION/NR_PHY/nr_dummy_functions.c - ${OPENAIR_DIR}/common/utils/nr/nr_common.c ${OPENAIR_DIR}/executables/softmodem-common.c ${OPENAIR2_DIR}/RRC/NAS/nas_config.c ${NR_UE_RRC_DIR}/rrc_nsa.c @@ -2282,7 +2275,6 @@ 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 ${OPENAIR1_DIR}/SIMULATION/NR_PHY/nr_dummy_functions.c - ${OPENAIR_DIR}/common/utils/nr/nr_common.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 @@ -2293,7 +2285,6 @@ 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 ${OPENAIR1_DIR}/SIMULATION/NR_PHY/nr_dummy_functions.c - ${OPENAIR_DIR}/common/utils/nr/nr_common.c ${OPENAIR_DIR}/executables/softmodem-common.c ${OPENAIR2_DIR}/RRC/NAS/nas_config.c ${NR_UE_RRC_DIR}/rrc_nsa.c @@ -2310,7 +2301,6 @@ 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 ${OPENAIR1_DIR}/SIMULATION/NR_PHY/nr_dummy_functions.c - ${OPENAIR_DIR}/common/utils/nr/nr_common.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 @@ -2320,7 +2310,6 @@ target_link_libraries(nr_prachsim PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs) add_executable(nr_ulschsim ${OPENAIR1_DIR}/SIMULATION/NR_PHY/ulschsim.c ${OPENAIR1_DIR}/SIMULATION/NR_PHY/nr_dummy_functions.c - ${OPENAIR_DIR}/common/utils/nr/nr_common.c ${PHY_INTERFACE_DIR}/queue_t.c ) target_link_libraries(nr_ulschsim PRIVATE @@ -2332,7 +2321,6 @@ 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 ${OPENAIR1_DIR}/SIMULATION/NR_PHY/nr_dummy_functions.c - ${OPENAIR_DIR}/common/utils/nr/nr_common.c ${OPENAIR_DIR}/executables/softmodem-common.c ${OPENAIR2_DIR}/RRC/NAS/nas_config.c ${NR_UE_RRC_DIR}/rrc_nsa.c @@ -2410,7 +2398,7 @@ if (${T_TRACER}) PHY_COMMON PHY PHY_UE PHY_NR PHY_NR_COMMON PHY_NR_UE PHY_RU PHY_MEX L2 L2_LTE L2_NR L2_LTE_NR L2_UE NR_L2_UE L2_UE_LTE_NR MAC_NR_COMMON MAC_UE_NR ngap CN_UTILS GTPV1U SCTP_CLIENT MME_APP LIB_NAS_UE NB_IoT SIMU SIMU_ETH OPENAIR0_LIB - ldpc_orig ldpc_optim ldpc_optim8seg ldpc_t2 ldpc_cl ldpc_cuda ldpc dfts config_internals) + ldpc_orig ldpc_optim ldpc_optim8seg ldpc_t2 ldpc_cl ldpc_cuda ldpc dfts config_internals nr_common) if (TARGET ${i}) add_dependencies(${i} generate_T) endif() @@ -2507,6 +2495,21 @@ if(ENABLE_TESTS) # meta-target: each test is supposed to add_dependencies(tests ${NEWTEST}) # then, it is possible to build and execute all tests using "ninja tests && ctest" add_custom_target(tests) + find_package(GTest) + if (NOT GTest_FOUND) + message(STATUS "GTest package not found, will download googletest automatically. To prevent that install google test on your system (libgtest-dev)") + include(FetchContent) + FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG 58d77fa8070e8cec2dc1ed015d66b454c8d78850 # 1.12.1 + ) + set(BUILD_GMOCK OFF) + set(INSTALL_GTEST OFF) + FetchContent_MakeAvailable(googletest) + add_library(GTest::gtest ALIAS gtest) + add_library(GTest::gtest_main ALIAS gtest_main) + endif() endif() add_subdirectory(common) diff --git a/ci-scripts/cls_containerize.py b/ci-scripts/cls_containerize.py index db34e38feb..5d353f2b77 100644 --- a/ci-scripts/cls_containerize.py +++ b/ci-scripts/cls_containerize.py @@ -680,6 +680,8 @@ class Containerize(): dockerfile = "ci-scripts/docker/Dockerfile.unittest.ubuntu20" ret = cmd.run(f'docker build --progress=plain --tag ran-unittests:{baseTag} --file {dockerfile} . &> {lSourcePath}/cmake_targets/log/unittest-build.log') if ret.returncode != 0: + build_log_name = f'build_log_{self.testCase_id}' + CopyLogsToExecutor(cmd, lSourcePath, build_log_name) logging.error(f'Cannot build unit tests') HTML.CreateHtmlTestRow("Unit test build failed", 'KO', [dockerfile]) HTML.CreateHtmlTabFooter(False) diff --git a/ci-scripts/cls_oaicitest.py b/ci-scripts/cls_oaicitest.py index db3cec5e35..a9d3652f9d 100644 --- a/ci-scripts/cls_oaicitest.py +++ b/ci-scripts/cls_oaicitest.py @@ -810,7 +810,7 @@ class OaiCiTest(): with cls_cmd.getConnection(ue.getHost()) as cmd_ue, cls_cmd.getConnection(EPC.IPAddress) as cmd_svr: port = 5002 + idx cmd_ue.run(f'{ue.getCmdPrefix()} iperf -B {ueIP} -s -u -i1 >> {server_filename} &', timeout=iperf_time*1.5) - cmd_svr.run(f'{svr.getCmdPrefix()} iperf -c {ueIP} -B {svrIP} {iperf_opt} -i1 2>&1 | tee {client_filename}', timeout=iperf_time*1.5) + cmd_svr.run(f'{svr.getCmdPrefix()} iperf -c {ueIP} -B {svrIP} {iperf_opt} -i1 2>&1 | tee {client_filename}', timeout=iperf_time*2.5) cmd_ue.run(f'cp {client_filename} {logPath}/{client_filename}') cmd_ue.run(f'cp {server_filename} {logPath}/{server_filename}') status, msg = Iperf_analyzeV2UDP(server_filename, self.iperf_bitrate_threshold, self.iperf_packetloss_threshold, target_bitrate) @@ -818,7 +818,7 @@ class OaiCiTest(): with cls_cmd.getConnection(ue.getHost()) as cmd_ue, cls_cmd.getConnection(EPC.IPAddress) as cmd_svr: port = 5002 + idx # note: some core setups start an iperf3 server automatically, indicated in ci_infra by runIperf3Server: False` - t = iperf_time * 1.5 + t = iperf_time * 2.5 if runIperf3Server: cmd_svr.run(f'{svr.getCmdPrefix()} nohup timeout -vk3 {t} iperf3 -s -B {svrIP} -p {port} -1 {jsonReport} &', timeout=t) cmd_ue.run(f'rm /tmp/{client_filename}', reportNonZero=False) diff --git a/ci-scripts/docker/Dockerfile.unittest.ubuntu20 b/ci-scripts/docker/Dockerfile.unittest.ubuntu20 index 7e47988719..57bc7f3bef 100644 --- a/ci-scripts/docker/Dockerfile.unittest.ubuntu20 +++ b/ci-scripts/docker/Dockerfile.unittest.ubuntu20 @@ -27,6 +27,11 @@ FROM ran-base:develop as ran-tests +RUN apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get upgrade --yes && \ + DEBIAN_FRONTEND=noninteractive apt-get install --yes \ + libgtest-dev + RUN rm -Rf /oai-ran WORKDIR /oai-ran COPY . . diff --git a/cmake_targets/tools/MODULES/Findxran.cmake b/cmake_targets/tools/MODULES/Findxran.cmake index 810206c32c..961efc4e38 100644 --- a/cmake_targets/tools/MODULES/Findxran.cmake +++ b/cmake_targets/tools/MODULES/Findxran.cmake @@ -109,6 +109,8 @@ elseif (xran_VERSION_STRING STREQUAL "oran_e_maintenance_release_v1.1") set(xran_VERSION 5.1.1) elseif (xran_VERSION_STRING STREQUAL "oran_e_maintenance_release_v1.2") set(xran_VERSION 5.1.2) +elseif (xran_VERSION_STRING STREQUAL "oran_e_maintenance_release_v1.3") + set(xran_VERSION 5.1.3) endif() unset(xran_VERSION_LINE) unset(xran_VERSION_STRING) diff --git a/cmake_targets/tools/oran_fhi_integration_patches/E/oaioran_E.patch b/cmake_targets/tools/oran_fhi_integration_patches/E/oaioran_E.patch index 6252623542..a40aefac2b 100644 --- a/cmake_targets/tools/oran_fhi_integration_patches/E/oaioran_E.patch +++ b/cmake_targets/tools/oran_fhi_integration_patches/E/oaioran_E.patch @@ -7,7 +7,7 @@ index 7508117..0dbe0bd 100644 #include -#define VERSIONX "oran_e_maintenance_release_v1.0" -+#define VERSIONX "oran_e_maintenance_release_v1.2" ++#define VERSIONX "oran_e_maintenance_release_v1.3" #define APP_O_DU 0 #define APP_O_RU 1 @@ -114,7 +114,7 @@ index 7419ae1..a85f973 100644 * @ingroup xran * diff --git a/fhi_lib/lib/api/xran_up_api.h b/fhi_lib/lib/api/xran_up_api.h -index 7d3afc5..3e00c5a 100644 +index 7d3afc5..e2f1f21 100644 --- a/fhi_lib/lib/api/xran_up_api.h +++ b/fhi_lib/lib/api/xran_up_api.h @@ -80,6 +80,7 @@ int32_t xran_extract_iq_samples(struct rte_mbuf *mbuf, @@ -125,6 +125,16 @@ index 7d3afc5..3e00c5a 100644 union ecpri_seq_id *seq_id, uint16_t *num_prbu, uint16_t *start_prbu, +@@ -89,7 +90,8 @@ int32_t xran_extract_iq_samples(struct rte_mbuf *mbuf, + int8_t expect_comp, + enum xran_comp_hdr_type staticComp, + uint8_t *compMeth, +- uint8_t *iqWidth); ++ uint8_t *iqWidth, ++ uint8_t *is_prach); + + inline int xran_prepare_iq_symbol_portion( + struct rte_mbuf *mbuf, diff --git a/fhi_lib/lib/ethernet/ethdi.c b/fhi_lib/lib/ethernet/ethdi.c index b6ba257..b53325f 100644 --- a/fhi_lib/lib/ethernet/ethdi.c @@ -173,9 +183,18 @@ index e6d3067..8e0abee 100644 static int16_t saturateAbs(int16_t inVal) { diff --git a/fhi_lib/lib/src/xran_bfp_uplane.cpp b/fhi_lib/lib/src/xran_bfp_uplane.cpp -index a345df4..7831fa0 100644 +index a345df4..109b05a 100644 --- a/fhi_lib/lib/src/xran_bfp_uplane.cpp +++ b/fhi_lib/lib/src/xran_bfp_uplane.cpp +@@ -90,7 +90,7 @@ namespace BFP_UPlane + { + const __m512i* rawData = reinterpret_cast(dataIn.dataExpanded); + /// Abs +- const auto rawDataAbs = _mm512_abs_epi16(rawData[0]); ++ const auto rawDataAbs = _mm512_abs_epi16(_mm512_loadu_epi16(rawData)); + /// No need to do a full horizontal max operation here, just do a max IQ step, + /// compute the exponents and then use a reduce max over all exponent values. This + /// is the fastest way to handle a single RB. @@ -116,7 +116,7 @@ namespace BFP_UPlane /// Get AVX512 pointer aligned to desired RB const __m512i* rawDataIn = reinterpret_cast(dataIn.dataExpanded + numREOffset); @@ -195,10 +214,18 @@ index a345df4..7831fa0 100644 dataOut->dataCompressed[thisRBExpAddr] = thisExp; /// Now have 1 RB worth of bytes separated into 3 chunks (1 per lane) diff --git a/fhi_lib/lib/src/xran_common.c b/fhi_lib/lib/src/xran_common.c -index baa673f..edd4ecb 100644 +index baa673f..00bfdf2 100644 --- a/fhi_lib/lib/src/xran_common.c +++ b/fhi_lib/lib/src/xran_common.c -@@ -656,6 +656,7 @@ process_mbuf(struct rte_mbuf *pkt, void* handle, struct xran_eaxc_info *p_cid) +@@ -650,12 +650,15 @@ process_mbuf(struct rte_mbuf *pkt, void* handle, struct xran_eaxc_info *p_cid) + uint8_t compMeth = 0; + uint8_t iqWidth = 0; + ++ uint8_t is_prach = 0; ++ + void *pHandle = NULL; + int ret = MBUF_FREE; + uint32_t mb_free = 0; int32_t valid_res = 0; int expect_comp = (p_dev_ctx->fh_cfg.ru_conf.compMeth != XRAN_COMPMETHOD_NONE); enum xran_comp_hdr_type staticComp = p_dev_ctx->fh_cfg.ru_conf.xranCompHdrType; @@ -206,7 +233,7 @@ index baa673f..edd4ecb 100644 if (staticComp == XRAN_COMP_HDR_TYPE_STATIC) { -@@ -674,6 +675,7 @@ process_mbuf(struct rte_mbuf *pkt, void* handle, struct xran_eaxc_info *p_cid) +@@ -674,6 +677,7 @@ process_mbuf(struct rte_mbuf *pkt, void* handle, struct xran_eaxc_info *p_cid) &subframe_id, &slot_id, &symb_id, @@ -214,7 +241,14 @@ index baa673f..edd4ecb 100644 &seq, &num_prbu, &start_prbu, -@@ -688,23 +690,9 @@ process_mbuf(struct rte_mbuf *pkt, void* handle, struct xran_eaxc_info *p_cid) +@@ -683,28 +687,15 @@ process_mbuf(struct rte_mbuf *pkt, void* handle, struct xran_eaxc_info *p_cid) + expect_comp, + staticComp, + &compMeth, +- &iqWidth); ++ &iqWidth, ++ &is_prach); + if (num_bytes <= 0){ print_err("num_bytes is wrong [%d]\n", num_bytes); return MBUF_FREE; } @@ -241,7 +275,16 @@ index baa673f..edd4ecb 100644 #ifndef FCN_ADAPT if(valid_res != 0) { print_dbg("valid_res is wrong [%d] ant %u (%u : %u : %u : %u) seq %u num_bytes %d\n", valid_res, Ant_ID, frame_id, subframe_id, slot_id, symb_id, seq.seq_id, num_bytes); -@@ -1189,7 +1177,7 @@ int generate_cpmsg_prach(void *pHandle, struct xran_cp_gen_params *params, struc +@@ -756,7 +747,7 @@ process_mbuf(struct rte_mbuf *pkt, void* handle, struct xran_eaxc_info *p_cid) + print_dbg("Transport layer fragmentation (eCPRI) is not supported\n"); + } + +- } else if (Ant_ID >= p_dev_ctx->PrachCPConfig.eAxC_offset && p_dev_ctx->fh_cfg.prachEnable) { ++ } else if (/*Ant_ID >= p_dev_ctx->PrachCPConfig.eAxC_offset && */p_dev_ctx->fh_cfg.prachEnable && is_prach) { + /* PRACH packet has ruportid = num_eAxc + ant_id */ + Ant_ID -= p_dev_ctx->PrachCPConfig.eAxC_offset; + symbol_total_bytes[p_dev_ctx->xran_port_id][CC_ID][Ant_ID] += num_bytes; +@@ -1189,7 +1180,7 @@ int generate_cpmsg_prach(void *pHandle, struct xran_cp_gen_params *params, struc timeOffset += startSymId * (2048 + 144); } timeOffset = timeOffset >> nNumerology; //original number is Tc, convert to Ts based on mu @@ -250,7 +293,7 @@ index baa673f..edd4ecb 100644 timeOffset += 16; params->dir = XRAN_DIR_UL; -@@ -1295,8 +1283,7 @@ int32_t ring_processing_func(void* args) +@@ -1295,8 +1286,7 @@ int32_t ring_processing_func(void* args) for (i = 0; i < ctx->io_cfg.num_vfs && i < XRAN_VF_MAX; i++){ for(qi = 0; qi < ctx->rxq_per_port[i]; qi++) { @@ -430,7 +473,7 @@ index 36bd72c..f7f5678 100644 int idx = 0; uint16_t *psrc = (uint16_t *)iq_data_start; diff --git a/fhi_lib/lib/src/xran_up_api.c b/fhi_lib/lib/src/xran_up_api.c -index 397853a..4a714b5 100644 +index 397853a..8977e3d 100644 --- a/fhi_lib/lib/src/xran_up_api.c +++ b/fhi_lib/lib/src/xran_up_api.c @@ -329,6 +329,7 @@ int32_t xran_extract_iq_samples(struct rte_mbuf *mbuf, @@ -441,7 +484,26 @@ index 397853a..4a714b5 100644 union ecpri_seq_id *seq_id, uint16_t *num_prbu, uint16_t *start_prbu, -@@ -387,6 +388,8 @@ int32_t xran_extract_iq_samples(struct rte_mbuf *mbuf, +@@ -338,7 +339,8 @@ int32_t xran_extract_iq_samples(struct rte_mbuf *mbuf, + int8_t expect_comp, + enum xran_comp_hdr_type staticComp, + uint8_t *compMeth, +- uint8_t *iqWidth) ++ uint8_t *iqWidth, ++ uint8_t *is_prach) + { + #if XRAN_MLOG_VAR + uint32_t mlogVar[10]; +@@ -374,6 +376,8 @@ int32_t xran_extract_iq_samples(struct rte_mbuf *mbuf, + return 0; /* packet too short */ + + radio_hdr->sf_slot_sym.value = rte_be_to_cpu_16(radio_hdr->sf_slot_sym.value); ++ if (radio_hdr->data_feature.filter_id > 0) *is_prach = 1; ++ else *is_prach = 0; + + if (frame_id) + *frame_id = radio_hdr->frame_id; +@@ -387,6 +391,8 @@ int32_t xran_extract_iq_samples(struct rte_mbuf *mbuf, if (symb_id) *symb_id = radio_hdr->sf_slot_sym.symb_id; @@ -450,7 +512,7 @@ index 397853a..4a714b5 100644 /* Process data section hdr */ struct data_section_hdr *data_hdr = (void *)rte_pktmbuf_adj(mbuf, sizeof(*radio_hdr)); -@@ -401,6 +404,7 @@ int32_t xran_extract_iq_samples(struct rte_mbuf *mbuf, +@@ -401,6 +407,7 @@ int32_t xran_extract_iq_samples(struct rte_mbuf *mbuf, *sym_inc = data_hdr->fields.sym_inc; *rb = data_hdr->fields.rb; *sect_id = data_hdr->fields.sect_id; diff --git a/common/utils/CMakeLists.txt b/common/utils/CMakeLists.txt index 094ca6b10d..c945f644bc 100644 --- a/common/utils/CMakeLists.txt +++ b/common/utils/CMakeLists.txt @@ -10,3 +10,4 @@ if(ENABLE_WEBSRV) endif() add_subdirectory(T) +add_subdirectory(nr) diff --git a/common/utils/nr/CMakeLists.txt b/common/utils/nr/CMakeLists.txt new file mode 100644 index 0000000000..6b58544a6d --- /dev/null +++ b/common/utils/nr/CMakeLists.txt @@ -0,0 +1,5 @@ +add_library(nr_common nr_common.c) +target_include_directories(nr_common PUBLIC .) +if (ENABLE_TESTS) + add_subdirectory(tests) +endif() diff --git a/common/utils/nr/nr_common.c b/common/utils/nr/nr_common.c index fb9d9103e6..dd24660c44 100644 --- a/common/utils/nr/nr_common.c +++ b/common/utils/nr/nr_common.c @@ -1126,21 +1126,23 @@ int get_scan_ssb_first_sc(const double fc, const int nbRB, const int nrBand, con const double startFreq = get_start_freq(fc, nbRB, mu); const double stopFreq = get_stop_freq(fc, nbRB, mu); - int scanGscnStart = -1; - int scanGscnStop = -1; - sync_raster_t tmpRaster = {0}; - for (const sync_raster_t *r = sync_raster; r < r + (sizeof(sync_raster) / sizeof(sync_raster_t)); r++) { - if (r->band == nrBand && r->scs_index == mu) { - tmpRaster = *r; - break; - } + int scanGscnStart = 0; + int scanGscnStop = 0; + const sync_raster_t *tmpRaster = sync_raster; + const sync_raster_t * end=sync_raster + sizeofArray(sync_raster); + while (tmpRaster < end && (tmpRaster->band != nrBand || tmpRaster->scs_index != mu)) + tmpRaster++; + if (tmpRaster >= end) { + LOG_E(PHY, "raster not found nrband=%d, mu=%d\n", nrBand, mu); + return 0; } - find_gscn_to_scan(startFreq, stopFreq, tmpRaster, &scanGscnStart, &scanGscnStop); + + find_gscn_to_scan(startFreq, stopFreq, *tmpRaster, &scanGscnStart, &scanGscnStop); const double scs = MU_SCS(mu) * 1e3; const double pointA = fc - (nbRB / 2 * scs * NR_NB_SC_PER_RB); int numGscn = 0; - for (int g = scanGscnStart; (g <= scanGscnStop) && (numGscn < MAX_GSCN_BAND); g += tmpRaster.step_gscn) { + for (int g = scanGscnStart; (g <= scanGscnStop) && (numGscn < MAX_GSCN_BAND); g += tmpRaster->step_gscn) { ssbInfo[numGscn].ssRef = get_ssref_from_gscn(g); ssbInfo[numGscn].ssbFirstSC = get_ssb_first_sc(pointA, ssbInfo[numGscn].ssRef, mu); ssbInfo[numGscn].gscn = g; @@ -1174,38 +1176,6 @@ void init_delay_table(uint16_t ofdm_symbol_size, } } -void freq2time(uint16_t ofdm_symbol_size, - int16_t *freq_signal, - int16_t *time_signal) -{ - const idft_size_idx_t idft_size = get_idft(ofdm_symbol_size); - idft(idft_size, freq_signal, time_signal, 1); -} - -void nr_est_delay(int ofdm_symbol_size, const c16_t *ls_est, c16_t *ch_estimates_time, delay_t *delay) -{ - freq2time(ofdm_symbol_size, (int16_t *)ls_est, (int16_t *)ch_estimates_time); - - int max_pos = delay->delay_max_pos; - int max_val = delay->delay_max_val; - const int sync_pos = 0; - - for (int i = 0; i < ofdm_symbol_size; i++) { - int temp = c16amp2(ch_estimates_time[i]) >> 1; - if (temp > max_val) { - max_pos = i; - max_val = temp; - } - } - - if (max_pos > ofdm_symbol_size / 2) - max_pos = max_pos - ofdm_symbol_size; - - delay->delay_max_pos = max_pos; - delay->delay_max_val = max_val; - delay->est_delay = max_pos - sync_pos; -} - void nr_timer_start(NR_timer_t *timer) { timer->active = true; diff --git a/common/utils/nr/nr_common.h b/common/utils/nr/nr_common.h index d044130412..6ed86cb7bd 100644 --- a/common/utils/nr/nr_common.h +++ b/common/utils/nr/nr_common.h @@ -241,12 +241,6 @@ uint32_t get_ssb_offset_to_pointA(uint32_t absoluteFrequencySSB, int get_ssb_subcarrier_offset(uint32_t absoluteFrequencySSB, uint32_t absoluteFrequencyPointA, int scs); int get_delay_idx(int delay, int max_delay_comp); -void freq2time(uint16_t ofdm_symbol_size, - int16_t *freq_signal, - int16_t *time_signal); - -void nr_est_delay(int ofdm_symbol_size, const c16_t *ls_est, c16_t *ch_estimates_time, delay_t *delay); - int get_scan_ssb_first_sc(const double fc, const int nbRB, const int nrBand, diff --git a/common/utils/nr/tests/CMakeLists.txt b/common/utils/nr/tests/CMakeLists.txt new file mode 100644 index 0000000000..fb995d2ab8 --- /dev/null +++ b/common/utils/nr/tests/CMakeLists.txt @@ -0,0 +1,5 @@ +add_executable(test_nr_common test_nr_common.cpp) +target_link_libraries(test_nr_common PRIVATE nr_common GTest::gtest minimal_lib) +add_dependencies(tests test_nr_common) +add_test(NAME test_nr_common + COMMAND ./test_nr_common) diff --git a/common/utils/nr/tests/test_nr_common.cpp b/common/utils/nr/tests/test_nr_common.cpp new file mode 100644 index 0000000000..fede91b8ee --- /dev/null +++ b/common/utils/nr/tests/test_nr_common.cpp @@ -0,0 +1,47 @@ +/* + * 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 +extern "C" { +#include "nr_common.h" +#include "common/utils/LOG/log.h" +} + +TEST(nr_common, nr_timer) { + NR_timer_t timer; + nr_timer_setup(&timer, 10, 1); + nr_timer_start(&timer); + EXPECT_TRUE(is_nr_timer_active(timer)); + EXPECT_FALSE(nr_timer_expired(timer)); + for (auto i = 0; i < 10; i++) { + nr_timer_tick(&timer); + } + EXPECT_FALSE(is_nr_timer_active(timer)); + EXPECT_TRUE(nr_timer_expired(timer)); +} + + +int main(int argc, char **argv) +{ + logInit(); + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/doc/ORAN_FHI7.2_Tutorial.md b/doc/ORAN_FHI7.2_Tutorial.md index 44b652cb7a..a000a6c3ad 100644 --- a/doc/ORAN_FHI7.2_Tutorial.md +++ b/doc/ORAN_FHI7.2_Tutorial.md @@ -391,6 +391,8 @@ Compile the fronthaul interface library by calling `make` and the option environment variables `RTE_SDK` for the path to the source tree of DPDK, and `XRAN_DIR` to set the path to the fronthaul library. +**Note**: you need at least gcc-11 and g++-11. + ```bash cd ~/phy/fhi_lib/lib make clean @@ -790,7 +792,6 @@ Edit the sample OAI gNB configuration file and check following parameters: compression * `iq_width_prach`: Width of PRACH IQ samples: if 16, no compression, if <16, applies compression - * `fft_size`: size of FFT performed by RU, set to 12 by default * `prach_config`: PRACH-specific configuration * `eAxC_offset`: PRACH antenna offset * `kbar`: the PRACH guard interval, provided in RU diff --git a/doc/UnitTests.md b/doc/UnitTests.md index 6ea18e2280..d1bbbed0ae 100644 --- a/doc/UnitTests.md +++ b/doc/UnitTests.md @@ -8,8 +8,9 @@ explaining how to test with cmake and ctest; it is a suggested read, and the following just lists the main points of how to compile the tests and how to add new ones. -At the time of writing, only the NR RLC tests have been integrated. The author -hopes that more tests follow suit. +GoogleTest is a C++ unit testing framework that has been added as an external dependency. While using GoogleTest is not a requirement it can simplify writing unit tests. +See [primer](http://google.github.io/googletest/primer.html) for a quick introduction. To add it to your test executable link against +`GTest::gtest` or `GTest::gtest_main`. # How to compile tests diff --git a/executables/nr-ru.c b/executables/nr-ru.c index a5a670916e..991c59e110 100644 --- a/executables/nr-ru.c +++ b/executables/nr-ru.c @@ -912,7 +912,7 @@ static void fill_rf_config(RU_t *ru, char *rf_config_file) } } -static void fill_split7_2_config(split7_config_t *split7, const nfapi_nr_config_request_scf_t *config, int slots_per_frame) +static void fill_split7_2_config(split7_config_t *split7, const nfapi_nr_config_request_scf_t *config, int slots_per_frame, uint16_t ofdm_symbol_size) { const nfapi_nr_prach_config_t *prach_config = &config->prach_config; const nfapi_nr_tdd_table_t *tdd_table = &config->tdd_table; @@ -934,6 +934,8 @@ static void fill_split7_2_config(split7_config_t *split7, const nfapi_nr_config_ } } } + + split7->fftSize = log2(ofdm_symbol_size); } /* this function maps the RU tx and rx buffers to the available rf chains. @@ -1157,7 +1159,7 @@ void *ru_thread( void *param ) { nr_dump_frame_parms(fp); nr_phy_init_RU(ru); fill_rf_config(ru, ru->rf_config_file); - fill_split7_2_config(&ru->openair0_cfg.split7, &ru->config, fp->slots_per_frame); + fill_split7_2_config(&ru->openair0_cfg.split7, &ru->config, fp->slots_per_frame, fp->ofdm_symbol_size); if(!emulate_rf) { // Start IF device if any @@ -1342,6 +1344,7 @@ void *ru_thread( void *param ) { rx_tti_busy[info->slot_rx % RU_RX_SLOT_DEPTH] = false; if ((info->slot_rx % RU_RX_SLOT_DEPTH) == (proc->tti_rx % RU_RX_SLOT_DEPTH)) not_done = false; + delNotifiedFIFO_elt(res); } if (!res) break; diff --git a/executables/nr-ue.c b/executables/nr-ue.c index 0a47de2a59..7627a6f5b9 100644 --- a/executables/nr-ue.c +++ b/executables/nr-ue.c @@ -735,6 +735,7 @@ void *UE_thread(void *arg) readFrame(UE, &sync_timestamp, false); notifiedFIFO_elt_t *Msg = newNotifiedFIFO_elt(sizeof(syncData_t), 0, &nf, UE_synch); syncData_t *syncMsg = (syncData_t *)NotifiedFifoData(Msg); + *syncMsg = (syncData_t){0}; NR_DL_FRAME_PARMS *fp = &UE->frame_parms; if (UE->UE_scan_carrier) { // Get list of GSCN in this band for UE's bandwidth and center frequency. @@ -743,10 +744,7 @@ void *UE_thread(void *arg) get_scan_ssb_first_sc(fp->dl_CarrierFreq, fp->N_RB_DL, fp->nr_band, fp->numerology_index, syncMsg->gscnInfo); } else { LOG_W(PHY, "SSB position provided\n"); - nr_gscn_info_t *g = syncMsg->gscnInfo; - g->ssbFirstSC = fp->ssb_start_subcarrier; - g->gscn = 0; - g->ssRef = 0; + syncMsg->gscnInfo[0] = (nr_gscn_info_t){.ssbFirstSC = fp->ssb_start_subcarrier}; syncMsg->numGscn = 1; } syncMsg->UE = UE; diff --git a/openair1/PHY/CMakeLists.txt b/openair1/PHY/CMakeLists.txt index 0a53c969a7..6f364e4daf 100644 --- a/openair1/PHY/CMakeLists.txt +++ b/openair1/PHY/CMakeLists.txt @@ -4,3 +4,4 @@ add_boolean_option(ENABLE_NRSCOPE OFF "Whether to build the 5G scope" OFF) if(ENABLE_UESCOPE OR ENABLE_ENBSCOPE OR ENABLE_NRSCOPE) add_subdirectory(TOOLS) endif() +add_subdirectory(nr_phy_common) diff --git a/openair1/PHY/NR_ESTIMATION/nr_measurements_gNB.c b/openair1/PHY/NR_ESTIMATION/nr_measurements_gNB.c index 47938f3b78..a45734e791 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_measurements_gNB.c +++ b/openair1/PHY/NR_ESTIMATION/nr_measurements_gNB.c @@ -160,7 +160,7 @@ void gNB_I0_measurements(PHY_VARS_gNB *gNB, int slot, int first_symb, int num_sy } //rb } // symb int nb_rb=0; - int32_t n0_subband_tot=0; + int64_t n0_subband_tot=0; int32_t n0_subband_tot_perANT[frame_parms->nb_antennas_rx]; memset(n0_subband_tot_perANT, 0, sizeof(n0_subband_tot_perANT)); @@ -200,7 +200,7 @@ void nr_gnb_measurements(PHY_VARS_gNB *gNB, unsigned char symbol, uint8_t nrOfLayers) { - int rx_power_tot = 0; + uint32_t rx_power_tot = 0; unsigned short rx_power_avg_dB; unsigned short rx_power_tot_dB; RU_t *ru = gNB->RU_list[0]; diff --git a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c index a7f0ef6682..5f49549d21 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c +++ b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c @@ -33,6 +33,7 @@ #include "PHY/NR_REFSIG/ul_ref_seq_nr.h" #include "executables/softmodem-common.h" +#include "nr_phy_common.h" //#define DEBUG_CH diff --git a/openair1/PHY/NR_TRANSPORT/nr_prach.c b/openair1/PHY/NR_TRANSPORT/nr_prach.c index 4bf70acaa8..c9f645333b 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_prach.c +++ b/openair1/PHY/NR_TRANSPORT/nr_prach.c @@ -316,6 +316,12 @@ void rx_nr_prach_ru(RU_t *ru, dftlen >>= 1; break; + case 23040: + // 20 MHz @ 23.04 Ms/s + Ncp = (Ncp * 3) / 4; + dftlen = (dftlen * 3) / 4; + break; + case 30720: // 20, 25, 30 MHz @ 30.72 Ms/s break; diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c index 723d13f65e..96266e279a 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c @@ -454,10 +454,9 @@ static void nr_ulsch_channel_level(int **ul_ch_estimates_ext, avg128U = simde_mm_add_epi32(avg128U, simde_mm_srai_epi32(simde_mm_madd_epi16(ul_ch128[i], ul_ch128[i]), x)); } - avg[aatx*frame_parms->nb_antennas_rx+aarx] = (((int32_t*)&avg128U)[0] + - ((int32_t*)&avg128U)[1] + - ((int32_t*)&avg128U)[2] + - ((int32_t*)&avg128U)[3]) / y; + int32_t *avg32i = (int32_t *)&avg128U; + int64_t avg64 = (int64_t)avg32i[0] + avg32i[1] + avg32i[2] + avg32i[3]; + avg[aatx * frame_parms->nb_antennas_rx + aarx] = avg64 / y; } } diff --git a/openair1/PHY/NR_TRANSPORT/pucch_rx.c b/openair1/PHY/NR_TRANSPORT/pucch_rx.c index 64ce300ae4..05d7f03832 100644 --- a/openair1/PHY/NR_TRANSPORT/pucch_rx.c +++ b/openair1/PHY/NR_TRANSPORT/pucch_rx.c @@ -237,7 +237,7 @@ void nr_decode_pucch0(PHY_VARS_gNB *gNB, uint8_t index=0; int nb_re_pucch = 12*pucch_pdu->prb_size; // prb size is 1 - int signal_energy = 0, signal_energy_ant0 = 0; + int64_t signal_energy = 0, signal_energy_ant0 = 0; for (int l=0; lnr_of_symbols; l++) { uint8_t l2 = l + pucch_pdu->start_symbol_index; @@ -1147,7 +1147,11 @@ void nr_decode_pucch2(PHY_VARS_gNB *gNB, uint32_t x1 = 0, x2 = 0, sGold = 0; uint8_t *sGold8 = (uint8_t *)&sGold; - x2 = (((1<<17)*((14*slot) + (pucch_pdu->start_symbol_index+symb) + 1)*((2*pucch_pdu->dmrs_scrambling_id) + 1)) + (2*pucch_pdu->dmrs_scrambling_id))%(1U<<31); // c_init calculation according to TS38.211 subclause + const int scramble = pucch_pdu->dmrs_scrambling_id * 2; + // fixme: when MR2754 will be merged, use the gold sequence cache instead of regenerate each time + x2 = ((1ULL << 17) * ((NR_NUMBER_OF_SYMBOLS_PER_SLOT * slot + pucch_pdu->start_symbol_index + symb + 1) * (scramble + 1)) + + scramble) + % (1U << 31); // c_init calculation according to TS38.211 subclause #ifdef DEBUG_NR_PUCCH_RX printf("slot %d, start_symbol_index %d, symbol %d, dmrs_scrambling_id %d\n", slot,pucch_pdu->start_symbol_index,symb,pucch_pdu->dmrs_scrambling_id); diff --git a/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c b/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c index 047f022d02..0c3847baaf 100644 --- a/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c +++ b/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c @@ -29,12 +29,13 @@ #include "PHY/NR_REFSIG/nr_mod_table.h" #include "PHY/NR_TRANSPORT/nr_sch_dmrs.h" #include "PHY/NR_TRANSPORT/nr_transport_proto.h" -#include "common/utils/nr/nr_common.h" +#include "nr_phy_common.h" #include "filt16a_32.h" #include "T.h" #include #include "nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface.h" + extern openair0_config_t openair0_cfg[]; // #define DEBUG_PDSCH @@ -651,12 +652,12 @@ int nr_pbch_channel_estimation(const NR_DL_FRAME_PARMS *fp, const UE_nr_rxtx_proc_t *proc, unsigned char symbol, int dmrss, - uint8_t ssb_index, - uint8_t n_hf, + uint ssb_index, + uint n_hf, int ssb_start_subcarrier, const c16_t rxdataF[][fp->samples_per_slot_wCP], bool sidelink, - uint16_t Nid) + uint Nid) { int Ns = proc->nr_slot_rx; c16_t pilot[200] __attribute__((aligned(16))); diff --git a/openair1/PHY/NR_UE_ESTIMATION/nr_estimation.h b/openair1/PHY/NR_UE_ESTIMATION/nr_estimation.h index 65bcd5c7d7..fc2e2bba62 100644 --- a/openair1/PHY/NR_UE_ESTIMATION/nr_estimation.h +++ b/openair1/PHY/NR_UE_ESTIMATION/nr_estimation.h @@ -75,12 +75,12 @@ int nr_pbch_channel_estimation(const NR_DL_FRAME_PARMS *fp, const UE_nr_rxtx_proc_t *proc, unsigned char symbol, int dmrss, - uint8_t ssb_index, - uint8_t n_hf, + uint ssb_index, + uint n_hf, int ssb_start_subcarrier, const c16_t rxdataF[][fp->samples_per_slot_wCP], bool sidelink, - uint16_t Nid); + uint Nid); int nr_pdsch_channel_estimation(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_initial_sync.c b/openair1/PHY/NR_UE_TRANSPORT/nr_initial_sync.c index 097739d142..4788c9ed9d 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_initial_sync.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_initial_sync.c @@ -47,9 +47,7 @@ #include "PHY/TOOLS/tools_defs.h" #include "nr-uesoftmodem.h" -//static nfapi_nr_config_request_t config_t; -//static nfapi_nr_config_request_t* config =&config_t; -// #define DEBUG_INITIAL_SYNCH +//#define DEBUG_INITIAL_SYNCH #define DUMP_PBCH_CH_ESTIMATES 0 // structure used for multiple SSB detection @@ -223,8 +221,7 @@ void nr_scan_ssb(void *arg) ssbInfo->ssbOffset = sync_pos - fp->nb_prefix_samples; #ifdef DEBUG_INITIAL_SYNCH - LOG_I(PHY, "[UE%d] Initial sync : Estimated PSS position %d, Nid2 %d\n", ue->Mod_id, sync_pos, ue->common_vars.nid2); - LOG_I(PHY, "sync_pos %d ssb_offset %d \n", sync_pos, ue->ssb_offset); + LOG_I(PHY, "Initial sync : Estimated PSS position %d, Nid2 %d, ssb offset %d\n", sync_pos, nid2, ssbInfo->ssbOffset); #endif /* check that SSS/PBCH block is continuous inside the received buffer */ if (ssbInfo->ssbOffset + NR_N_SYMBOLS_SSB * (fp->ofdm_symbol_size + fp->nb_prefix_samples) >= fp->samples_per_frame) { @@ -256,10 +253,6 @@ void nr_scan_ssb(void *arg) rxdataF, link_type_dl); -#ifdef DEBUG_INITIAL_SYNCH - LOG_I(PHY, "Calling sss detection (normal CP)\n"); -#endif - int freq_offset_sss = 0; int32_t metric_tdd_ncp = 0; uint8_t phase_tdd_ncp; @@ -273,7 +266,15 @@ void nr_scan_ssb(void *arg) &phase_tdd_ncp, &freq_offset_sss, rxdataF); - +#ifdef DEBUG_INITIAL_SYNCH + LOG_I(PHY, + "TDD Normal prefix: sss detection result; %d, CellId %d metric %d, phase %d, measured offset %d\n", + ssbInfo->syncRes.cell_detected, + ssbInfo->nidCell, + metric_tdd_ncp, + phase_tdd_ncp, + ssbInfo->syncRes.rx_offset); +#endif ssbInfo->freqOffset = freq_offset_pss + freq_offset_sss; uint32_t nr_gold_pbch_ref[2][64][NR_PBCH_DMRS_LENGTH_DWORD]; @@ -315,23 +316,24 @@ nr_initial_sync_t nr_initial_sync(UE_nr_rxtx_proc_t *proc, for (int s = 0; s < numGscn; s++) { notifiedFIFO_elt_t *req = newNotifiedFIFO_elt(sizeof(nr_ue_ssb_scan_t), gscnInfo[s].gscn, &nf, &nr_scan_ssb); nr_ue_ssb_scan_t *ssbInfo = (nr_ue_ssb_scan_t *)NotifiedFifoData(req); - ssbInfo->gscnInfo = gscnInfo[s]; + *ssbInfo = (nr_ue_ssb_scan_t){.gscnInfo = gscnInfo[s], + .fp = &ue->frame_parms, + .proc = proc, + .syncRes.cell_detected = false, + .nFrames = n_frames, + .foFlag = ue->UE_fo_compensation, + .targetNidCell = ue->target_Nid_cell}; ssbInfo->rxdata = malloc16_clear(fp->nb_antennas_rx * sizeof(c16_t *)); for (int ant = 0; ant < fp->nb_antennas_rx; ant++) { - ssbInfo->rxdata[ant] = malloc16_clear((fp->samples_per_frame * 2 + fp->ofdm_symbol_size) * sizeof(c16_t)); + ssbInfo->rxdata[ant] = malloc16(sizeof(c16_t) * (fp->samples_per_frame * 2 + fp->ofdm_symbol_size)); memcpy(ssbInfo->rxdata[ant], ue->common_vars.rxdata[ant], sizeof(c16_t) * fp->samples_per_frame * 2); + memset(ssbInfo->rxdata[ant] + fp->samples_per_frame * 2, 0, fp->ofdm_symbol_size * sizeof(c16_t)); } LOG_I(NR_PHY, "Scanning GSCN: %d, with SSB offset: %d, SSB Freq: %lf\n", ssbInfo->gscnInfo.gscn, ssbInfo->gscnInfo.ssbFirstSC, ssbInfo->gscnInfo.ssRef); - ssbInfo->fp = &ue->frame_parms; - ssbInfo->proc = proc; - ssbInfo->syncRes.cell_detected = false; - ssbInfo->nFrames = n_frames; - ssbInfo->foFlag = ue->UE_fo_compensation; - ssbInfo->targetNidCell = ue->target_Nid_cell; pushTpool(&get_nrUE_params()->Tpool, req); } @@ -437,16 +439,6 @@ nr_initial_sync_t nr_initial_sync(UE_nr_rxtx_proc_t *proc, res.syncRes.rx_offset = res.ssbOffset - sync_pos_frame; } -#ifdef DEBUG_INITIAL_SYNCH - LOG_I(PHY, - "TDD Normal prefix: CellId %d metric %d, phase %d, pbch detected %d, measured offset %d\n", - fp->Nid_cell, - metric_tdd_ncp, - phase_tdd_ncp, - ret.cell_detected, - ret.rx_offset); -#endif - if (res.syncRes.cell_detected) { LOG_I(PHY, "[UE%d] In synch, rx_offset %d samples\n", ue->Mod_id, res.syncRes.rx_offset); LOG_I(PHY, "[UE %d] Measured Carrier Frequency offset %d Hz\n", ue->Mod_id, res.freqOffset); @@ -455,7 +447,7 @@ nr_initial_sync_t nr_initial_sync(UE_nr_rxtx_proc_t *proc, LOG_I(PHY,"[UE%d] Initial sync : PBCH not ok\n",ue->Mod_id); LOG_I(PHY, "[UE%d] Initial sync : Estimated PSS position %d, Nid2 %d\n", ue->Mod_id, sync_pos, ue->common_vars.nid2); LOG_I(PHY,"[UE%d] Initial sync : Estimated Nid_cell %d, Frame_type %d\n",ue->Mod_id, - frame_parms->Nid_cell,frame_parms->frame_type); + fp->Nid_cell,fp->frame_type); #endif } @@ -481,10 +473,10 @@ nr_initial_sync_t nr_initial_sync(UE_nr_rxtx_proc_t *proc, ue->measurements.rx_power_avg_dB[0] = dB_fixed(ue->measurements.rx_power_avg[0]); #ifdef DEBUG_INITIAL_SYNCH - LOG_I(PHY, "[UE%d] Initial sync : Estimated power: %d dB\n", ue->Mod_id, ue->measurements.rx_power_avg_dB[0]); + LOG_I(PHY, "[UE%d] Initial sync failed : Estimated power: %d dB\n", ue->Mod_id, ue->measurements.rx_power_avg_dB[0]); #endif } else { - LOG_A(PHY, "Initial sync successful\n"); + 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); diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_pbch.c b/openair1/PHY/NR_UE_TRANSPORT/nr_pbch.c index e2f9e03cb3..a9b155a9bd 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_pbch.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_pbch.c @@ -55,11 +55,12 @@ static uint16_t nr_pbch_extract(uint32_t rxdataF_sz, uint32_t symbol, uint32_t s_offset, int ssb_start_subcarrier, - const NR_DL_FRAME_PARMS *frame_parms) + const NR_DL_FRAME_PARMS *frame_parms, + int nid) { uint16_t rb; uint8_t i, j, aarx; - int nushiftmod4 = frame_parms->Nid_cell % 4; + int nushiftmod4 = nid % 4; AssertFatal(symbol>=1 && symbol<5, "symbol %d illegal for PBCH extraction\n", symbol); @@ -418,7 +419,8 @@ int nr_rx_pbch(PHY_VARS_NR_UE *ue, symbol, symbol_offset, ssb_start_subcarrier, - frame_parms); + frame_parms, + Nid_cell); #ifdef DEBUG_PBCH LOG_I(PHY,"[PHY] PBCH Symbol %d ofdm size %d\n",symbol, frame_parms->ofdm_symbol_size); LOG_I(PHY,"[PHY] PBCH starting channel_level\n"); diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_prach.c b/openair1/PHY/NR_UE_TRANSPORT/nr_prach.c index ea24f0a45c..471b2aee38 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_prach.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_prach.c @@ -65,7 +65,7 @@ int32_t generate_nr_prach(PHY_VARS_NR_UE *ue, uint8_t gNB_id, int frame, uint8_t c16_t prach[(4688 + 4 * 24576) * 2] __attribute__((aligned(32))) = {0}; int16_t prachF_tmp[(4688+4*24576)*4*2] __attribute__((aligned(32))) = {0}; - int16_t Ncp = 0; + int Ncp = 0; int prach_start, prach_sequence_length, i, prach_len, dftlen, mu, kbar, K, n_ra_prb, k, prachStartSymbol, sample_offset_slot; fd_occasion = 0; @@ -332,6 +332,12 @@ int32_t generate_nr_prach(PHY_VARS_NR_UE *ue, uint8_t gNB_id, int frame, uint8_t dftlen >>= 1; break; + case 23040: + // 20 MHz @ 23.04 Ms/s + Ncp = (Ncp * 3) / 4; + dftlen = (dftlen * 3) / 4; + break; + case 30720: // 20, 25, 30 MHz @ 30.72 Ms/s break; diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index 579cb24e60..3cd3266a0a 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -343,13 +343,13 @@ typedef struct { /// \f$\log_2(\max|H_i|^2)\f$ int16_t log2_maxh; /// measured RX power based on DRS - int ulsch_power[8]; + uint32_t ulsch_power[8]; /// total signal over antennas - int ulsch_power_tot; + uint32_t ulsch_power_tot; /// measured RX noise power int ulsch_noise_power[8]; /// total noise over antennas - int ulsch_noise_power_tot; + uint32_t ulsch_noise_power_tot; /// \brief llr values. /// - first index: ? [0..1179743] (hard coded) int16_t *llr; diff --git a/openair1/PHY/nr_phy_common/CMakeLists.txt b/openair1/PHY/nr_phy_common/CMakeLists.txt new file mode 100644 index 0000000000..79dc8dd7f3 --- /dev/null +++ b/openair1/PHY/nr_phy_common/CMakeLists.txt @@ -0,0 +1,2 @@ +add_library(nr_phy_common src/nr_phy_common.c) +target_include_directories(nr_phy_common PUBLIC inc/) diff --git a/openair1/PHY/nr_phy_common/inc/nr_phy_common.h b/openair1/PHY/nr_phy_common/inc/nr_phy_common.h new file mode 100644 index 0000000000..8e8f7399f0 --- /dev/null +++ b/openair1/PHY/nr_phy_common/inc/nr_phy_common.h @@ -0,0 +1,30 @@ +/* + * 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 __NR_PHY_COMMON__H__ +#define __NR_PHY_COMMON__H__ +#include "PHY/impl_defs_top.h" +#include "PHY/TOOLS/tools_defs.h" + +void freq2time(uint16_t ofdm_symbol_size, int16_t *freq_signal, int16_t *time_signal); +void nr_est_delay(int ofdm_symbol_size, const c16_t *ls_est, c16_t *ch_estimates_time, delay_t *delay); +unsigned int nr_get_tx_amp(int power_dBm, int power_max_dBm, int total_nb_rb, int nb_rb); +#endif diff --git a/openair1/PHY/nr_phy_common/src/nr_phy_common.c b/openair1/PHY/nr_phy_common/src/nr_phy_common.c new file mode 100644 index 0000000000..b67a94d8f6 --- /dev/null +++ b/openair1/PHY/nr_phy_common/src/nr_phy_common.c @@ -0,0 +1,69 @@ +/* + * 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 "nr_phy_common.h" + +void freq2time(uint16_t ofdm_symbol_size, int16_t *freq_signal, int16_t *time_signal) +{ + const idft_size_idx_t idft_size = get_idft(ofdm_symbol_size); + idft(idft_size, freq_signal, time_signal, 1); +} + +void nr_est_delay(int ofdm_symbol_size, const c16_t *ls_est, c16_t *ch_estimates_time, delay_t *delay) +{ + freq2time(ofdm_symbol_size, (int16_t *)ls_est, (int16_t *)ch_estimates_time); + + int max_pos = delay->delay_max_pos; + int max_val = delay->delay_max_val; + const int sync_pos = 0; + + for (int i = 0; i < ofdm_symbol_size; i++) { + int temp = c16amp2(ch_estimates_time[i]) >> 1; + if (temp > max_val) { + max_pos = i; + max_val = temp; + } + } + + if (max_pos > ofdm_symbol_size / 2) + max_pos = max_pos - ofdm_symbol_size; + + delay->delay_max_pos = max_pos; + delay->delay_max_val = max_val; + delay->est_delay = max_pos - sync_pos; +} + +unsigned int nr_get_tx_amp(int power_dBm, int power_max_dBm, int total_nb_rb, int nb_rb) +{ + // assume power at AMP is 20dBm + // if gain = 20 (power == 40) + int gain_dB = power_dBm - power_max_dBm; + double gain_lin; + + gain_lin = pow(10, .1 * gain_dB); + if ((nb_rb > 0) && (nb_rb <= total_nb_rb)) { + return ((int)(AMP * sqrt(gain_lin * total_nb_rb / (double)nb_rb))); + } else { + LOG_E(PHY, "Illegal nb_rb/N_RB_UL combination (%d/%d)\n", nb_rb, total_nb_rb); + // mac_xface->macphy_exit(""); + } + return (0); +} diff --git a/openair1/SCHED_NR_UE/defs.h b/openair1/SCHED_NR_UE/defs.h index bdb3341387..1488a9433a 100644 --- a/openair1/SCHED_NR_UE/defs.h +++ b/openair1/SCHED_NR_UE/defs.h @@ -111,8 +111,6 @@ int8_t nr_find_ue(uint16_t rnti, PHY_VARS_eNB *phy_vars_eNB); */ void ue_ta_procedures(PHY_VARS_NR_UE *ue, int slot_tx, int frame_tx); -unsigned int nr_get_tx_amp(int power_dBm, int power_max_dBm, int N_RB_UL, int nb_rb); - void set_tx_harq_id(NR_UE_ULSCH_t *ulsch, int harq_pid, int slot_tx); int get_tx_harq_id(NR_UE_ULSCH_t *ulsch, int slot_tx); diff --git a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c index cbeda2e106..603120abdc 100644 --- a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c +++ b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c @@ -407,23 +407,6 @@ static int nr_ue_pbch_procedures(PHY_VARS_NR_UE *ue, return ret; } -unsigned int nr_get_tx_amp(int power_dBm, int power_max_dBm, int N_RB_UL, int nb_rb) -{ - - int gain_dB = power_dBm - power_max_dBm; - double gain_lin; - - gain_lin = pow(10,.1*gain_dB); - if ((nb_rb >0) && (nb_rb <= N_RB_UL)) { - return((int)(AMP*sqrt(gain_lin*N_RB_UL/(double)nb_rb))); - } - else { - LOG_E(PHY,"Illegal nb_rb/N_RB_UL combination (%d/%d)\n",nb_rb,N_RB_UL); - //mac_xface->macphy_exit(""); - } - return(0); -} - int nr_ue_pdcch_procedures(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, int32_t pdcch_est_size, diff --git a/openair2/LAYER2/CMakeLists.txt b/openair2/LAYER2/CMakeLists.txt index b8bc21e78c..9360ed7b2b 100644 --- a/openair2/LAYER2/CMakeLists.txt +++ b/openair2/LAYER2/CMakeLists.txt @@ -1 +1,2 @@ add_subdirectory(nr_rlc) +add_subdirectory(NR_MAC_UE) diff --git a/openair2/LAYER2/NR_MAC_UE/CMakeLists.txt b/openair2/LAYER2/NR_MAC_UE/CMakeLists.txt new file mode 100644 index 0000000000..6589a2fda5 --- /dev/null +++ b/openair2/LAYER2/NR_MAC_UE/CMakeLists.txt @@ -0,0 +1,5 @@ +add_library(nr_ue_power_procedures nr_ue_power_procedures.c) +target_link_libraries(nr_ue_power_procedures PUBLIC asn1_nr_rrc ${T_LIB} MAC_NR_COMMON nr_common) +if (ENABLE_TESTS) + add_subdirectory(tests) +endif() diff --git a/openair2/LAYER2/NR_MAC_UE/mac_proto.h b/openair2/LAYER2/NR_MAC_UE/mac_proto.h index a6a60fbd7f..01f49f8319 100644 --- a/openair2/LAYER2/NR_MAC_UE/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_UE/mac_proto.h @@ -227,7 +227,16 @@ int nr_ue_configure_pucch(NR_UE_MAC_INST_t *mac, PUCCH_sched_t *pucch, fapi_nr_ul_config_pucch_pdu *pucch_pdu); -int nr_get_Pcmax(NR_UE_MAC_INST_t *mac, int Qm, bool powerBoostPi2BPSK, int scs, int N_RB_UL, bool is_transform_precoding, int n_prbs, int start_prb); +int nr_get_Pcmax(int p_Max, + uint16_t nr_band, + frequency_range_t frequency_range, + int Qm, + bool powerBoostPi2BPSK, + int scs, + int N_RB_UL, + bool is_transform_precoding, + int n_prbs, + int start_prb); int get_sum_delta_pucch(NR_UE_MAC_INST_t *mac, int slot, frame_t frame); diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c index 5831bd2b11..1fc6f4b6e8 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c @@ -79,7 +79,16 @@ void init_RA(NR_UE_MAC_INST_t *mac, int n_prbs = get_N_RA_RB(prach_scs, mac->current_UL_BWP->scs); int start_prb = rach_ConfigGeneric->msg1_FrequencyStart + mac->current_UL_BWP->BWPStart; // PRACH shall be as specified for QPSK modulated DFT-s-OFDM of equivalent RB allocation (38.101-1) - prach_resources->RA_PCMAX = nr_get_Pcmax(mac, 2, false, prach_scs, cfg->carrier_config.dl_grid_size[prach_scs], true, n_prbs, start_prb); + prach_resources->RA_PCMAX = nr_get_Pcmax(mac->p_Max, + mac->nr_band, + mac->frequency_range, + 2, + false, + prach_scs, + cfg->carrier_config.dl_grid_size[prach_scs], + true, + n_prbs, + start_prb); prach_resources->RA_PREAMBLE_TRANSMISSION_COUNTER = 1; prach_resources->RA_PREAMBLE_POWER_RAMPING_COUNTER = 1; prach_resources->POWER_OFFSET_2STEP_RA = 0; diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_power_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_power_procedures.c index 3e9669775d..e826d95e4c 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_power_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_power_procedures.c @@ -85,16 +85,24 @@ static int get_deltatf(uint16_t nb_of_prbs, // -- Powerclass 3 capable UE (which is default power class unless otherwise stated) // -- Maximum power reduction (MPR_c) for power class 3 // -- no additional MPR (A_MPR_c) -int nr_get_Pcmax(NR_UE_MAC_INST_t *mac, int Qm, bool powerBoostPi2BPSK, int scs, int N_RB_UL, bool is_transform_precoding, int n_prbs, int start_prb) +int nr_get_Pcmax(int p_Max, + uint16_t nr_band, + frequency_range_t frequency_range, + int Qm, + bool powerBoostPi2BPSK, + int scs, + int N_RB_UL, + bool is_transform_precoding, + int n_prbs, + int start_prb) { - int nr_band = mac->nr_band; - if(mac->frequency_range == FR1) { - - //TODO configure P-MAX from the upper layers according to 38.331 + if (frequency_range == FR1) { + // TODO configure P-MAX from the upper layers according to 38.331 int p_powerclass = 23; // dBm assuming poweclass 3 UE - int p_emax = mac->p_Max != INT_MIN ? mac->p_Max : p_powerclass; + int p_emax = p_Max != INT_MIN ? p_Max : p_powerclass; int delta_P_powerclass = 0; // for powerclass 2 needs to be changed - if(mac->p_Max && Qm == 1 && powerBoostPi2BPSK && (nr_band == 40 || nr_band == 41 || nr_band == 77 || nr_band == 78 || nr_band == 79)) { + if (p_Max && Qm == 1 && powerBoostPi2BPSK + && (nr_band == 40 || nr_band == 41 || nr_band == 77 || nr_band == 78 || nr_band == 79)) { p_emax += 3; delta_P_powerclass -= 3; } @@ -103,54 +111,52 @@ int nr_get_Pcmax(NR_UE_MAC_INST_t *mac, int Qm, bool powerBoostPi2BPSK, int scs, int delta_T_IB = 0; // TODO in case of band 41 and PRB allocation within 4MHz of the upper or lower limit of the band -> delta_TC = 1.5 - if(nr_band == 41) + if (nr_band == 41) LOG_E(NR_MAC, "Need to implement delta_TC for band 41\n"); int delta_TC = 0; float MPR = 0; frame_type_t frame_type = get_frame_type(nr_band, scs); - if(compare_relative_ul_channel_bw(nr_band, scs, N_RB_UL, frame_type)) { + if (compare_relative_ul_channel_bw(nr_band, scs, N_RB_UL, frame_type)) { int rb_low = (n_prbs / 2) > 1 ? (n_prbs / 2) : 1; int rb_high = N_RB_UL - rb_low - n_prbs; bool is_inner_rb = start_prb >= rb_low && start_prb <= rb_high && n_prbs <= ((N_RB_UL / 2) + (N_RB_UL & 1)); // Table 6.2.2-1 in 38.101 switch (Qm) { - case 1 : + case 1: AssertFatal(false, "MPR for Pi/2 BPSK not implemented yet\n"); break; - case 2 : + case 2: if (is_transform_precoding) { - if(!is_inner_rb) + if (!is_inner_rb) MPR = 1; - } - else { - if(is_inner_rb) + } else { + if (is_inner_rb) MPR = 1.5; else MPR = 3; } break; - case 4 : + case 4: if (is_transform_precoding) { - if(is_inner_rb) + if (is_inner_rb) MPR = 1; else MPR = 2; - } - else { - if(is_inner_rb) + } else { + if (is_inner_rb) MPR = 2; else MPR = 3; } break; - case 6 : + case 6: if (is_transform_precoding) MPR = 2.5; else MPR = 3.5; break; - case 8 : + case 8: if (is_transform_precoding) MPR = 4.5; else @@ -170,12 +176,12 @@ int nr_get_Pcmax(NR_UE_MAC_INST_t *mac, int Qm, bool powerBoostPi2BPSK, int scs, if (P_MPR > total_reduction) total_reduction = P_MPR; int pcmax_high, pcmax_low; - if(mac->p_Max) { + if (p_Max) { pcmax_high = p_emax < (p_powerclass - delta_P_powerclass) ? p_emax : (p_powerclass - delta_P_powerclass); - pcmax_low = (p_emax - delta_TC) < (p_powerclass - delta_P_powerclass - total_reduction) ? - (p_emax - delta_TC) : (p_powerclass - delta_P_powerclass - total_reduction); - } - else { + pcmax_low = (p_emax - delta_TC) < (p_powerclass - delta_P_powerclass - total_reduction) + ? (p_emax - delta_TC) + : (p_powerclass - delta_P_powerclass - total_reduction); + } else { pcmax_high = p_powerclass - delta_P_powerclass; pcmax_low = p_powerclass - delta_P_powerclass - total_reduction; } @@ -183,8 +189,7 @@ int nr_get_Pcmax(NR_UE_MAC_INST_t *mac, int Qm, bool powerBoostPi2BPSK, int scs, int pcmax = (pcmax_low + pcmax_high) / 2; LOG_D(MAC, "Configured maximum output power: %d dBm <= PCMAX %d dBm <= %d dBm \n", pcmax_low, pcmax, pcmax_high); return pcmax; - } - else { + } else { // FR2 TODO it is even more complex because it is radiated power return 23; } @@ -294,7 +299,16 @@ int16_t get_pucch_tx_power_ue(NR_UE_MAC_INST_t *mac, // PUCCH shall be as specified for QPSK modulated DFT-s-OFDM of equivalent RB allocation (38.101-1) // TODO: P_CMAX for format 2 - int P_CMAX = nr_get_Pcmax(mac, 2, false, mac->current_UL_BWP->scs, mac->current_UL_BWP->BWPSize, true, 1, start_prb); + int P_CMAX = nr_get_Pcmax(mac->p_Max, + mac->nr_band, + mac->frequency_range, + 2, + false, + mac->current_UL_BWP->scs, + mac->current_UL_BWP->BWPSize, + true, + 1, + start_prb); int P_CMIN = -40; // TODO: minimum TX power, possibly 38.101-1 6.3.1 int16_t pathloss = compute_nr_SSB_PL(mac, mac->ssb_measurements.ssb_rsrp_dBm); @@ -358,3 +372,27 @@ static int get_deltatf(uint16_t nb_of_prbs, } return DELTA_TF; } + +// Returns the pathloss in dB for the active UL BWP on the selected carrier based on the DL RS associated with the PRACH transmission +// computation according to clause 7.4 (Physical random access channel) of 3GPP TS 38.213 version 16.3.0 Release 16 +// Assumptions: +// - PRACH transmission from a UE is not in response to a detection of a PDCCH order by the UE +// Measurement units: +// - referenceSignalPower: dBm/RE (average EPRE of the resources elements that carry secondary synchronization signals in dBm) +int16_t compute_nr_SSB_PL(NR_UE_MAC_INST_t *mac, short ssb_rsrp_dBm) +{ + fapi_nr_config_request_t *cfg = &mac->phy_config.config_req; + int referenceSignalPower = cfg->ssb_config.ss_pbch_power; + //TODO improve PL measurements. Probably not correct as it is. + + int16_t pathloss = (int16_t)(referenceSignalPower - ssb_rsrp_dBm); + + LOG_D(NR_MAC, "pathloss %d dB, referenceSignalPower %d dBm/RE (%f mW), RSRP %d dBm (%f mW)\n", + pathloss, + referenceSignalPower, + pow(10, referenceSignalPower/10), + ssb_rsrp_dBm, + pow(10, ssb_rsrp_dBm/10)); + + return pathloss; +} diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c index 2a7801afb8..68f969fcdc 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c @@ -1368,7 +1368,7 @@ void set_harq_status(NR_UE_MAC_INST_t *mac, // if temp_dai is less or equal than cumulative highest dai for given slot // it's an indication dai was reset due to modulo 4 operation if (temp_dai <= highest_dai) { - int mod4_count = (highest_dai / 4) + 1; // to take into account how many times dai wrapped up (modulo 4) + int mod4_count = (highest_dai + 1) / 4; // to take into account how many times dai wrapped up (modulo 4) current_harq->dai_cumul += (mod4_count * 4); } LOG_D(NR_MAC, @@ -3945,26 +3945,3 @@ static void nr_ue_process_rar(NR_UE_MAC_INST_t *mac, nr_downlink_indication_t *d return; } -// Returns the pathloss in dB for the active UL BWP on the selected carrier based on the DL RS associated with the PRACH transmission -// computation according to clause 7.4 (Physical random access channel) of 3GPP TS 38.213 version 16.3.0 Release 16 -// Assumptions: -// - PRACH transmission from a UE is not in response to a detection of a PDCCH order by the UE -// Measurement units: -// - referenceSignalPower: dBm/RE (average EPRE of the resources elements that carry secondary synchronization signals in dBm) -int16_t compute_nr_SSB_PL(NR_UE_MAC_INST_t *mac, short ssb_rsrp_dBm) -{ - fapi_nr_config_request_t *cfg = &mac->phy_config.config_req; - int referenceSignalPower = cfg->ssb_config.ss_pbch_power; - //TODO improve PL measurements. Probably not correct as it is. - - int16_t pathloss = (int16_t)(referenceSignalPower - ssb_rsrp_dBm); - - LOG_D(NR_MAC, "pathloss %d dB, referenceSignalPower %d dBm/RE (%f mW), RSRP %d dBm (%f mW)\n", - pathloss, - referenceSignalPower, - pow(10, referenceSignalPower/10), - ssb_rsrp_dBm, - pow(10, ssb_rsrp_dBm/10)); - - return pathloss; -} diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index 2b59755ef4..eca690e7cf 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -1768,13 +1768,25 @@ static void build_ssb_list(NR_UE_MAC_INST_t *mac) uint32_t curr_mask = cfg->ssb_table.ssb_mask_list[ssb_index / 32].ssb_mask; // check if if current SSB is transmitted if ((curr_mask >> (31 - (ssb_index % 32))) & 0x01) { - ssb_list->nb_ssb_per_index[ssb_index]=ssb_list->nb_tx_ssb; + ssb_list->nb_ssb_per_index[ssb_index] = ssb_list->nb_tx_ssb; ssb_list->nb_tx_ssb++; } + else + ssb_list->nb_ssb_per_index[ssb_index] = -1; } ssb_list->tx_ssb = calloc(ssb_list->nb_tx_ssb, sizeof(*ssb_list->tx_ssb)); } +static int get_ssb_idx_from_list(ssb_list_info_t *ssb_list, int idx) +{ + for (int ssb_index = 0; ssb_index < MAX_NB_SSB; ssb_index++) { + if (ssb_list->nb_ssb_per_index[ssb_index] == idx) + return ssb_index; + } + AssertFatal(false, "Couldn't find SSB index in SSB list\n"); + return 0; +} + // Map the transmitted SSBs to the ROs and create the association pattern according to the config static void map_ssb_to_ro(NR_UE_MAC_INST_t *mac) { @@ -1863,7 +1875,7 @@ static void map_ssb_to_ro(NR_UE_MAC_INST_t *mac) // WIP: only one possible association period so the starting PRACH configuration period is automatically 0 // WIP: For the moment, only map each SSB idx once per association period if configuration is multiple SSBs per RO // this is true if no PRACH occasions are conflicting with SSBs nor TDD_UL_DL_ConfigurationCommon schedule - int ssb_idx = 0; + int idx = 0; bool done = false; for (int i = 0; i < prach_period->nb_of_prach_conf_period && !done; i++, prach_configuration_period_idx++) { prach_period->prach_conf_period_list[i] = &prach_assoc_pattern->prach_conf_period_list[prach_configuration_period_idx]; @@ -1880,9 +1892,10 @@ static void map_ssb_to_ro(NR_UE_MAC_INST_t *mac) // Go through the list of transmitted SSBs and map the required amount of SSBs to this RO // WIP: For the moment, only map each SSB idx once per association period if configuration is multiple SSBs per RO // this is true if no PRACH occasions are conflicting with SSBs nor TDD_UL_DL_ConfigurationCommon schedule - for (; ssb_idx < ssb_list->nb_tx_ssb; ssb_idx++) { - ssb_info_t *tx_ssb = ssb_list->tx_ssb + ssb_idx; + for (; idx < ssb_list->nb_tx_ssb; idx++) { + ssb_info_t *tx_ssb = ssb_list->tx_ssb + idx; // Map only the transmitted ssb_idx + int ssb_idx = get_ssb_idx_from_list(ssb_list, idx); ro_p->mapped_ssb_idx[ro_p->nb_mapped_ssb] = ssb_idx; ro_p->nb_mapped_ssb++; AssertFatal(MAX_NB_RO_PER_SSB_IN_ASSOCIATION_PATTERN > tx_ssb->nb_mapped_ro + 1, @@ -1903,11 +1916,11 @@ static void map_ssb_to_ro(NR_UE_MAC_INST_t *mac) tx_ssb->nb_mapped_ro); // If all the required SSBs are mapped to this RO, exit the loop of SSBs if (ro_p->nb_mapped_ssb == ssb_rach_ratio) { - ssb_idx++; + idx++; break; } } - done = MAX_NB_SSB == ssb_idx; + done = MAX_NB_SSB == idx; } } } @@ -1919,8 +1932,8 @@ static void map_ssb_to_ro(NR_UE_MAC_INST_t *mac) for (prach_association_period_t *prach_period = prach_assoc_pattern->prach_association_period_list; prach_period < end; prach_period++) { // Go through the list of transmitted SSBs - for (int ssb_idx = 0; ssb_idx < ssb_list->nb_tx_ssb; ssb_idx++) { - ssb_info_t *tx_ssb = ssb_list->tx_ssb + ssb_idx; + for (int idx = 0; idx < ssb_list->nb_tx_ssb; idx++) { + ssb_info_t *tx_ssb = ssb_list->tx_ssb + idx; uint8_t nb_mapped_ro_in_association_period=0; // Reset the nb of mapped ROs for the new SSB index bool done = false; // Map all the required ROs to this SSB @@ -1936,6 +1949,7 @@ static void map_ssb_to_ro(NR_UE_MAC_INST_t *mac) for (int ro_in_freq = 0; ro_in_freq < slot_map->nb_of_prach_occasion_in_freq && !done; ro_in_freq++) { prach_occasion_info_t *ro_p = slot_map->prach_occasion + ro_in_time * slot_map->nb_of_prach_occasion_in_freq + ro_in_freq; + int ssb_idx = get_ssb_idx_from_list(ssb_list, idx); ro_p->mapped_ssb_idx[0] = ssb_idx; ro_p->nb_mapped_ssb = 1; AssertFatal(MAX_NB_RO_PER_SSB_IN_ASSOCIATION_PATTERN > tx_ssb->nb_mapped_ro + 1, diff --git a/openair2/LAYER2/NR_MAC_UE/tests/CMakeLists.txt b/openair2/LAYER2/NR_MAC_UE/tests/CMakeLists.txt new file mode 100644 index 0000000000..40c8c5372b --- /dev/null +++ b/openair2/LAYER2/NR_MAC_UE/tests/CMakeLists.txt @@ -0,0 +1,6 @@ +add_executable(test_nr_ue_power_procedures + test_nr_ue_power_procedures.cpp) +target_link_libraries(test_nr_ue_power_procedures PRIVATE nr_ue_power_procedures GTest::gtest minimal_lib) +add_dependencies(tests test_nr_ue_power_procedures) +add_test(NAME test_nr_ue_power_procedures + COMMAND ./test_nr_ue_power_procedures) diff --git a/openair2/LAYER2/NR_MAC_UE/tests/test_nr_ue_power_procedures.cpp b/openair2/LAYER2/NR_MAC_UE/tests/test_nr_ue_power_procedures.cpp new file mode 100644 index 0000000000..179d94bd1b --- /dev/null +++ b/openair2/LAYER2/NR_MAC_UE/tests/test_nr_ue_power_procedures.cpp @@ -0,0 +1,64 @@ +/* + * 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 "gtest/gtest.h" +extern "C" { +#include "openair2/LAYER2/NR_MAC_UE/mac_proto.h" +#include "executables/softmodem-common.h" +uint64_t get_softmodem_optmask(void) {return 0;} +static softmodem_params_t softmodem_params; +softmodem_params_t *get_softmodem_params(void) { + return &softmodem_params; + +} +} +#include +#include "common/utils/LOG/log.h" + +TEST(power_procedures_fr1, test_prach_max_tx_power_mpr) +{ + // Inner PRB, MPR = 1.5 + int prb_start = 4; + int N_RB_UL = 51; // 10Mhz + EXPECT_EQ(22, nr_get_Pcmax(23, 20, FR1, 2, false, 1, N_RB_UL, false, 6, prb_start)); + + // Outer PRB, MPR = 3 + prb_start = 0; + EXPECT_EQ(21, nr_get_Pcmax(23, 20, FR1, 2, false, 1, N_RB_UL, false, 6, prb_start)); + + // Channel bandwidth conditon not met, no MPR + N_RB_UL = 106; + EXPECT_EQ(23, nr_get_Pcmax(23, 20, FR1, 2, false, 1, N_RB_UL, false, 6, prb_start)); +} + +TEST(power_procedures_fr1, test_not_implemented) +{ + int N_RB_UL = 51; + EXPECT_DEATH(nr_get_Pcmax(23, 20, FR1, 1, false, 1, N_RB_UL, false, 6, 0), "MPR for Pi/2 BPSK not implemented yet"); +} + +int main(int argc, char** argv) +{ + logInit(); + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c index 6faa7e9d80..3d531315ea 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_bch.c @@ -65,6 +65,7 @@ static void schedule_ssb(frame_t frame, dl_config_pdu->PDUSize =2 + sizeof(nfapi_nr_dl_tti_ssb_pdu_rel15_t); AssertFatal(scc->physCellId!=NULL,"ServingCellConfigCommon->physCellId is null\n"); + AssertFatal(*scc->physCellId < 1008 && *scc->physCellId >=0, "5G physicall cell id out of range: %ld\n", *scc->physCellId); dl_config_pdu->ssb_pdu.ssb_pdu_rel15.PhysCellId = *scc->physCellId; dl_config_pdu->ssb_pdu.ssb_pdu_rel15.BetaPss = 0; dl_config_pdu->ssb_pdu.ssb_pdu_rel15.SsbBlockIndex = i_ssb; diff --git a/openair3/NAS/NR_UE/nr_nas_msg_sim.c b/openair3/NAS/NR_UE/nr_nas_msg_sim.c index e0712c3691..1d25258821 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg_sim.c +++ b/openair3/NAS/NR_UE/nr_nas_msg_sim.c @@ -58,6 +58,79 @@ extern uint16_t NB_UE_INST; static nr_ue_nas_t nr_ue_nas = {0}; static nr_nas_msg_snssai_t nas_allowed_nssai[8]; +typedef enum { + NAS_SECURITY_NO_SECURITY_CONTEXT, + NAS_SECURITY_UNPROTECTED, + NAS_SECURITY_INTEGRITY_FAILED, + NAS_SECURITY_INTEGRITY_PASSED, + NAS_SECURITY_BAD_INPUT +} security_state_t; + +security_state_t nas_security_rx_process(nr_ue_nas_t *nas, uint8_t *pdu_buffer, int pdu_length) +{ + if (nas->security_container == NULL) + return NAS_SECURITY_NO_SECURITY_CONTEXT; + if (pdu_buffer[1] == 0) + return NAS_SECURITY_UNPROTECTED; + /* header is 7 bytes, require at least one byte of payload */ + if (pdu_length < 8) + return NAS_SECURITY_BAD_INPUT; + /* only accept "integrity protected and ciphered" messages */ + if (pdu_buffer[1] != 2) { + LOG_E(NAS, "todo: unhandled security type %d\n", pdu_buffer[2]); + return NAS_SECURITY_BAD_INPUT; + } + + /* synchronize NAS SQN, based on 24.501 4.4.3.1 */ + int nas_sqn = pdu_buffer[6]; + int target_sqn = nas->security.nas_count_dl & 0xff; + if (nas_sqn != target_sqn) { + if (nas_sqn < target_sqn) + nas->security.nas_count_dl += 256; + nas->security.nas_count_dl &= ~255; + nas->security.nas_count_dl |= nas_sqn; + } + if (nas->security.nas_count_dl > 0x00ffffff) { + /* it's doubtful that this will happen, so let's simply exit for the time being */ + /* to be refined if needed */ + LOG_E(NAS, "max NAS COUNT DL reached\n"); + exit(1); + } + + /* check integrity */ + uint8_t computed_mac[4]; + nas_stream_cipher_t stream_cipher; + stream_cipher.context = nas->security_container->integrity_context; + stream_cipher.count = nas->security.nas_count_dl; + stream_cipher.bearer = 1; /* todo: don't hardcode */ + stream_cipher.direction = 1; + stream_cipher.message = pdu_buffer + 6; + /* length in bits */ + stream_cipher.blength = (pdu_length - 6) << 3; + stream_compute_integrity(nas->security_container->integrity_algorithm, &stream_cipher, computed_mac); + + uint8_t *received_mac = pdu_buffer + 2; + + if (memcmp(received_mac, computed_mac, 4) != 0) + return NAS_SECURITY_INTEGRITY_FAILED; + + /* decipher */ + uint8_t buf[pdu_length - 7]; + stream_cipher.context = nas->security_container->ciphering_context; + stream_cipher.count = nas->security.nas_count_dl; + stream_cipher.bearer = 1; /* todo: don't hardcode */ + stream_cipher.direction = 1; + stream_cipher.message = pdu_buffer + 7; + /* length in bits */ + stream_cipher.blength = (pdu_length - 7) << 3; + stream_compute_encrypt(nas->security_container->ciphering_algorithm, &stream_cipher, buf); + memcpy(pdu_buffer + 7, buf, pdu_length - 7); + + nas->security.nas_count_dl++; + + return NAS_SECURITY_INTEGRITY_PASSED; +} + static int nas_protected_security_header_encode( char *buffer, const fgs_nas_message_security_header_t *header, @@ -301,7 +374,8 @@ void derive_kamf(uint8_t *kseaf, uint8_t *kamf, uint16_t abba, uicc_t* uicc) { } //------------------------------------------------------------------------------ -void derive_knas(algorithm_type_dist_t nas_alg_type, uint8_t nas_alg_id, uint8_t kamf[32], uint8_t *knas_int) { +void derive_knas(algorithm_type_dist_t nas_alg_type, uint8_t nas_alg_id, uint8_t kamf[32], uint8_t *knas) +{ uint8_t S[20] = {0}; uint8_t out[32] = { 0 }; S[0] = 0x69; //FC @@ -315,7 +389,7 @@ void derive_knas(algorithm_type_dist_t nas_alg_type, uint8_t nas_alg_id, uint8_t byte_array_t data = {.buf = S, .len = 7}; kdf(kamf, data, 32, out); - memcpy(knas_int, out+16, 16); + memcpy(knas, out+16, 16); } void derive_kgnb(uint8_t kamf[32], uint32_t count, uint8_t *kgnb){ @@ -360,7 +434,6 @@ void derive_ue_keys(uint8_t *buf, nr_ue_nas_t *nas) { uint8_t *kausf = nas->security.kausf; uint8_t *kseaf = nas->security.kseaf; uint8_t *kamf = nas->security.kamf; - uint8_t *knas_int = nas->security.knas_int; uint8_t *output = nas->security.res; uint8_t *rand = nas->security.rand; uint8_t *kgnb = nas->security.kgnb; @@ -383,7 +456,6 @@ void derive_ue_keys(uint8_t *buf, nr_ue_nas_t *nas) { derive_kausf(ck, ik, sqn, kausf, nas->uicc); derive_kseaf(kausf, kseaf, nas->uicc); derive_kamf(kseaf, kamf, 0x0000, nas->uicc); - derive_knas(0x02, 2, kamf, knas_int); derive_kgnb(kamf,0,kgnb); printf("kausf:"); @@ -404,12 +476,6 @@ void derive_ue_keys(uint8_t *buf, nr_ue_nas_t *nas) { printf("%x ", kamf[i]); } printf("\n"); - - printf("knas_int:\n"); - for(int i = 0; i < 16; i++){ - printf("%x ", knas_int[i]); - } - printf("\n"); } nr_ue_nas_t *get_ue_nas_info(module_id_t module_id) @@ -464,8 +530,8 @@ void generateRegistrationRequest(as_nas_info_t *initialNasMsg, nr_ue_nas_t *nas) mm_msg->registration_request.presencemask |= REGISTRATION_REQUEST_UE_SECURITY_CAPABILITY_PRESENT; mm_msg->registration_request.nruesecuritycapability.iei = REGISTRATION_REQUEST_UE_SECURITY_CAPABILITY_IEI; mm_msg->registration_request.nruesecuritycapability.length = 8; - mm_msg->registration_request.nruesecuritycapability.fg_EA = 0xa0; - mm_msg->registration_request.nruesecuritycapability.fg_IA = 0x20; + mm_msg->registration_request.nruesecuritycapability.fg_EA = 0xe0; + mm_msg->registration_request.nruesecuritycapability.fg_IA = 0x60; mm_msg->registration_request.nruesecuritycapability.EEA = 0; mm_msg->registration_request.nruesecuritycapability.EIA = 0; size += 10; @@ -513,10 +579,6 @@ void generateIdentityResponse(as_nas_info_t *initialNasMsg, uint8_t identitytype static void generateAuthenticationResp(nr_ue_nas_t *nas, as_nas_info_t *initialNasMsg, uint8_t *buf) { derive_ue_keys(buf, nas); - /* todo: as of now, nia2 is hardcoded in derive_ue_keys(), remove this hardcoding, use NAS signalling for getting proper algorithm */ - /* todo: deal with ciphering for this stream_security_container_init() (handle ciphering in general) */ - /* todo: stream_security_container_delete() is not called anywhere, deal with that */ - nas->security_container = stream_security_container_init(0, 2 /* hardcoded: nia2 */, NULL, nas->security.knas_int); OctetString res; res.length = 16; res.value = calloc(1,16); @@ -570,6 +632,7 @@ static void generateSecurityModeComplete(nr_ue_nas_t *nas, as_nas_info_t *initia // set security protected header nas_msg.header.protocol_discriminator = FGS_MOBILITY_MANAGEMENT_MESSAGE; nas_msg.header.security_header_type = INTEGRITY_PROTECTED_AND_CIPHERED_WITH_NEW_SECU_CTX; + nas_msg.header.sequence_number = nas->security.nas_count_ul & 0xff; size += 7; mm_msg = &nas_msg.security_protected.plain.mm_msg; @@ -600,16 +663,29 @@ static void generateSecurityModeComplete(nr_ue_nas_t *nas, as_nas_info_t *initia initialNasMsg->length = security_header_len + mm_msg_encode(mm_msg, (uint8_t*)(initialNasMsg->data+security_header_len), size-security_header_len); + /* ciphering */ + uint8_t buf[initialNasMsg->length - 7]; + stream_cipher.context = nas->security_container->ciphering_context; + AssertFatal(nas->security.nas_count_ul <= 0xffffff, "fatal: NAS COUNT UL too big (todo: fix that)\n"); + stream_cipher.count = nas->security.nas_count_ul; + stream_cipher.bearer = 1; + stream_cipher.direction = 0; + stream_cipher.message = (unsigned char *)(initialNasMsg->data + 7); + /* length in bits */ + stream_cipher.blength = (initialNasMsg->length - 7) << 3; + stream_compute_encrypt(nas->security_container->ciphering_algorithm, &stream_cipher, buf); + memcpy(stream_cipher.message, buf, initialNasMsg->length - 7); + + /* integrity protection */ stream_cipher.context = nas->security_container->integrity_context; - stream_cipher.count = nas->security.mm_counter++; + stream_cipher.count = nas->security.nas_count_ul++; stream_cipher.bearer = 1; stream_cipher.direction = 0; stream_cipher.message = (unsigned char *)(initialNasMsg->data + 6); /* length in bits */ stream_cipher.blength = (initialNasMsg->length - 6) << 3; - // only for Type of integrity protection algorithm: 128-5G-IA2 (2) - stream_compute_integrity(EIA2_128_ALG_ID, &stream_cipher, mac); + stream_compute_integrity(nas->security_container->integrity_algorithm, &stream_cipher, mac); printf("mac %x %x %x %x \n", mac[0], mac[1], mac[2], mac[3]); for(int i = 0; i < 4; i++){ @@ -617,6 +693,40 @@ static void generateSecurityModeComplete(nr_ue_nas_t *nas, as_nas_info_t *initia } } +static void handle_security_mode_command(instance_t instance, nr_ue_nas_t *nas, as_nas_info_t *initialNasMsg, uint8_t *pdu, int pdu_length) +{ + /* retrieve integrity and ciphering algorithms */ + AssertFatal(pdu_length > 10, "nas: bad pdu\n"); + int ciphering_algorithm = (pdu[10] >> 4) & 0x0f; + int integrity_algorithm = pdu[10] & 0x0f; + + uint8_t *kamf = nas->security.kamf; + uint8_t *knas_enc = nas->security.knas_enc; + uint8_t *knas_int = nas->security.knas_int; + + /* derive keys */ + derive_knas(0x01, ciphering_algorithm, kamf, knas_enc); + derive_knas(0x02, integrity_algorithm, kamf, knas_int); + + printf("knas_int: "); + for(int i = 0; i < 16; i++){ + printf("%x ", knas_int[i]); + } + printf("\n"); + + printf("knas_enc: "); + for(int i = 0; i < 16; i++){ + printf("%x ", knas_enc[i]); + } + printf("\n"); + + /* todo: stream_security_container_delete() is not called anywhere, deal with that */ + nas->security_container = stream_security_container_init(ciphering_algorithm, integrity_algorithm, knas_enc, knas_int); + + nas_itti_kgnb_refresh_req(instance, nas->security.kgnb); + generateSecurityModeComplete(nas, initialNasMsg); +} + static void decodeRegistrationAccept(const uint8_t *buf, int len, nr_ue_nas_t *nas) { registration_accept_msg reg_acc = {0}; @@ -656,7 +766,7 @@ static void generateRegistrationComplete(nr_ue_nas_t *nas, as_nas_info_t *initia sp_msg->header.protocol_discriminator = FGS_MOBILITY_MANAGEMENT_MESSAGE; sp_msg->header.security_header_type = INTEGRITY_PROTECTED_AND_CIPHERED; sp_msg->header.message_authentication_code = 0; - sp_msg->header.sequence_number = 1; + sp_msg->header.sequence_number = nas->security.nas_count_ul & 0xff; length = 7; sp_msg->plain.mm_msg.registration_complete.protocoldiscriminator = FGS_MOBILITY_MANAGEMENT_MESSAGE; length += 1; @@ -672,6 +782,7 @@ static void generateRegistrationComplete(nr_ue_nas_t *nas, as_nas_info_t *initia // encode the message initialNasMsg->data = (Byte_t *)malloc(length * sizeof(Byte_t)); + initialNasMsg->length = length; /* Encode the first octet of the header (extended protocol discriminator) */ ENCODE_U8(initialNasMsg->data + size, sp_msg->header.protocol_discriminator, size); @@ -698,18 +809,29 @@ static void generateRegistrationComplete(nr_ue_nas_t *nas, as_nas_info_t *initia if(sortransparentcontainer) { encode_registration_complete(&sp_msg->plain.mm_msg.registration_complete, initialNasMsg->data + size, length - size); } - - initialNasMsg->length = length; + + /* ciphering */ + uint8_t buf[initialNasMsg->length - 7]; + stream_cipher.context = nas->security_container->ciphering_context; + AssertFatal(nas->security.nas_count_ul <= 0xffffff, "fatal: NAS COUNT UL too big (todo: fix that)\n"); + stream_cipher.count = nas->security.nas_count_ul; + stream_cipher.bearer = 1; + stream_cipher.direction = 0; + stream_cipher.message = (unsigned char *)(initialNasMsg->data + 7); + /* length in bits */ + stream_cipher.blength = (initialNasMsg->length - 7) << 3; + stream_compute_encrypt(nas->security_container->ciphering_algorithm, &stream_cipher, buf); + memcpy(stream_cipher.message, buf, initialNasMsg->length - 7); + + /* integrity protection */ stream_cipher.context = nas->security_container->integrity_context; - stream_cipher.count = nas->security.mm_counter++; + stream_cipher.count = nas->security.nas_count_ul++; stream_cipher.bearer = 1; stream_cipher.direction = 0; stream_cipher.message = (unsigned char *)(initialNasMsg->data + 6); /* length in bits */ stream_cipher.blength = (initialNasMsg->length - 6) << 3; - - // only for Type of integrity protection algorithm: 128-5G-IA2 (2) - stream_compute_integrity(EIA2_128_ALG_ID, &stream_cipher, mac); + stream_compute_integrity(nas->security_container->integrity_algorithm, &stream_cipher, mac); printf("mac %x %x %x %x \n", mac[0], mac[1], mac[2], mac[3]); for(int i = 0; i < 4; i++){ @@ -738,7 +860,7 @@ static void generateDeregistrationRequest(nr_ue_nas_t *nas, as_nas_info_t *initi sp_msg->header.protocol_discriminator = FGS_MOBILITY_MANAGEMENT_MESSAGE; sp_msg->header.security_header_type = INTEGRITY_PROTECTED_AND_CIPHERED; sp_msg->header.message_authentication_code = 0; - sp_msg->header.sequence_number = 3; + sp_msg->header.sequence_number = nas->security.nas_count_ul & 0xff; int size = sizeof(fgs_nas_message_security_header_t); fgs_deregistration_request_ue_originating_msg *dereg_req = &sp_msg->plain.mm_msg.fgs_deregistration_request_ue_originating; @@ -761,19 +883,36 @@ static void generateDeregistrationRequest(nr_ue_nas_t *nas, as_nas_info_t *initi initialNasMsg->length = security_header_len + mm_msg_encode(&sp_msg->plain.mm_msg, (uint8_t *)(initialNasMsg->data + security_header_len), size - security_header_len); - nas_stream_cipher_t stream_cipher = { - .context = nas->security_container->integrity_context, - .count = nas->security.mm_counter++, - .bearer = 1, - .direction = 0, - .message = (unsigned char *)(initialNasMsg->data + 6), - .blength = (initialNasMsg->length - 6) << 3, /* length in bits */ - }; - uint8_t mac[4]; - nas_stream_encrypt_eia2(&stream_cipher, mac); + nas_stream_cipher_t stream_cipher; - for(int i = 0; i < 4; i++) - initialNasMsg->data[2 + i] = mac[i]; + /* ciphering */ + uint8_t buf[initialNasMsg->length - 7]; + stream_cipher.context = nas->security_container->ciphering_context; + AssertFatal(nas->security.nas_count_ul <= 0xffffff, "fatal: NAS COUNT UL too big (todo: fix that)\n"); + stream_cipher.count = nas->security.nas_count_ul; + stream_cipher.bearer = 1; + stream_cipher.direction = 0; + stream_cipher.message = (unsigned char *)(initialNasMsg->data + 7); + /* length in bits */ + stream_cipher.blength = (initialNasMsg->length - 7) << 3; + stream_compute_encrypt(nas->security_container->ciphering_algorithm, &stream_cipher, buf); + memcpy(stream_cipher.message, buf, initialNasMsg->length - 7); + + /* integrity protection */ + stream_cipher.context = nas->security_container->integrity_context; + stream_cipher.count = nas->security.nas_count_ul++; + stream_cipher.bearer = 1; + stream_cipher.direction = 0; + stream_cipher.message = (unsigned char *)(initialNasMsg->data + 6); + /* length in bits */ + stream_cipher.blength = (initialNasMsg->length - 6) << 3; + uint8_t mac[4]; + stream_compute_integrity(nas->security_container->integrity_algorithm, &stream_cipher, mac); + + printf("mac %x %x %x %x \n", mac[0], mac[1], mac[2], mac[3]); + for(int i = 0; i < 4; i++){ + initialNasMsg->data[2+i] = mac[i]; + } } static void generatePduSessionEstablishRequest(nr_ue_nas_t *nas, as_nas_info_t *initialNasMsg, nas_pdu_session_req_t *pdu_req) @@ -800,7 +939,7 @@ static void generatePduSessionEstablishRequest(nr_ue_nas_t *nas, as_nas_info_t * uint8_t mac[4]; nas_msg.header.protocol_discriminator = FGS_MOBILITY_MANAGEMENT_MESSAGE; nas_msg.header.security_header_type = INTEGRITY_PROTECTED_AND_CIPHERED; - nas_msg.header.sequence_number = 2; + nas_msg.header.sequence_number = nas->security.nas_count_ul & 0xff; size += 7; @@ -852,16 +991,28 @@ static void generatePduSessionEstablishRequest(nr_ue_nas_t *nas, as_nas_info_t * initialNasMsg->length = security_header_len + mm_msg_encode(mm_msg, (uint8_t*)(initialNasMsg->data+security_header_len), size-security_header_len); + /* ciphering */ + uint8_t buf[initialNasMsg->length - 7]; + stream_cipher.context = nas->security_container->ciphering_context; + AssertFatal(nas->security.nas_count_ul <= 0xffffff, "fatal: NAS COUNT UL too big (todo: fix that)\n"); + stream_cipher.count = nas->security.nas_count_ul; + stream_cipher.bearer = 1; + stream_cipher.direction = 0; + stream_cipher.message = (unsigned char *)(initialNasMsg->data + 7); + /* length in bits */ + stream_cipher.blength = (initialNasMsg->length - 7) << 3; + stream_compute_encrypt(nas->security_container->ciphering_algorithm, &stream_cipher, buf); + memcpy(stream_cipher.message, buf, initialNasMsg->length - 7); + + /* integrity protection */ stream_cipher.context = nas->security_container->integrity_context; - stream_cipher.count = nas->security.mm_counter++; + stream_cipher.count = nas->security.nas_count_ul++; stream_cipher.bearer = 1; stream_cipher.direction = 0; stream_cipher.message = (unsigned char *)(initialNasMsg->data + 6); /* length in bits */ stream_cipher.blength = (initialNasMsg->length - 6) << 3; - - // only for Type of integrity protection algorithm: 128-5G-IA2 (2) - stream_compute_integrity(EIA2_128_ALG_ID, &stream_cipher, mac); + stream_compute_integrity(nas->security_container->integrity_algorithm, &stream_cipher, mac); printf("mac %x %x %x %x \n", mac[0], mac[1], mac[2], mac[3]); for(int i = 0; i < 4; i++){ @@ -870,30 +1021,41 @@ static void generatePduSessionEstablishRequest(nr_ue_nas_t *nas, as_nas_info_t * } -uint8_t get_msg_type(uint8_t *pdu_buffer, uint32_t length) { - uint8_t msg_type = 0; - uint8_t offset = 0; +static uint8_t get_msg_type(uint8_t *pdu_buffer, uint32_t length) +{ + if (pdu_buffer == NULL) + goto error; - if ((pdu_buffer != NULL) && (length > 0)) { - if (((nas_msg_header_t *)(pdu_buffer))->choice.security_protected_nas_msg_header_t.security_header_type > 0) { - offset += SECURITY_PROTECTED_5GS_NAS_MESSAGE_HEADER_LENGTH; - if (offset < length) { - msg_type = ((mm_msg_header_t *)(pdu_buffer + offset))->message_type; + /* get security header type */ + if (length < 2) + goto error; - if (msg_type == FGS_DOWNLINK_NAS_TRANSPORT) { - dl_nas_transport_t tmp; - memcpy(&tmp, pdu_buffer + offset, sizeof(tmp)); - msg_type = tmp.sm_nas_msg_header.message_type; - } - } - } else { // plain 5GS NAS message - msg_type = ((nas_msg_header_t *)(pdu_buffer))->choice.plain_nas_msg_header.message_type; - } - } else { - LOG_I(NAS, "[UE] Received invalid downlink message\n"); + int security_header_type = pdu_buffer[1]; + + if (security_header_type == 0) { + /* plain NAS message */ + if (length < 3) + goto error; + return pdu_buffer[2]; + } + + if (length < 10) + goto error; + + int msg_type = pdu_buffer[9]; + + if (msg_type == FGS_DOWNLINK_NAS_TRANSPORT) { + if (length < 17) + goto error; + + msg_type = pdu_buffer[16]; } return msg_type; + +error: + LOG_E(NAS, "[UE] Received invalid downlink message\n"); + return 0; } static void send_nas_uplink_data_req(instance_t instance, const as_nas_info_t *initial_nas_msg) @@ -998,11 +1160,13 @@ static void get_allowed_nssai(nr_nas_msg_snssai_t nssai[8], const uint8_t *pdu_b case 0x15: // allowed NSSAI length = *pdu_buffer++; parse_allowed_nssai(nssai, pdu_buffer, length); + pdu_buffer += length; break; default: - LOG_W(NAS, "This NAS IEI is not handled when extracting list of allowed NSSAI\n"); - pdu_buffer = end; + LOG_W(NAS, "This NAS IEI (0x%2.2x) is not handled when extracting list of allowed NSSAI\n", type); + length = *pdu_buffer++; + pdu_buffer += length; break; } } @@ -1135,13 +1299,23 @@ void *nas_nrue(void *args_p) NAS_CONN_ESTABLI_CNF(msg_p).nasMsg.length); uint8_t *pdu_buffer = NAS_CONN_ESTABLI_CNF(msg_p).nasMsg.data; - int msg_type = get_msg_type(pdu_buffer, NAS_CONN_ESTABLI_CNF(msg_p).nasMsg.length); + int pdu_length = NAS_CONN_ESTABLI_CNF(msg_p).nasMsg.length; + + nr_ue_nas_t *nas = get_ue_nas_info(0); + + security_state_t security_state = nas_security_rx_process(nas, pdu_buffer, pdu_length); + if (security_state != NAS_SECURITY_INTEGRITY_PASSED + && security_state != NAS_SECURITY_NO_SECURITY_CONTEXT) { + LOG_E(NAS, "NAS integrity failed, discard incoming message\n"); + break; + } + + int msg_type = get_msg_type(pdu_buffer, pdu_length); if (msg_type == REGISTRATION_ACCEPT) { - nr_ue_nas_t *nas = get_ue_nas_info(0); - handle_registration_accept(instance, nas, pdu_buffer, NAS_CONN_ESTABLI_CNF(msg_p).nasMsg.length); + handle_registration_accept(instance, nas, pdu_buffer, pdu_length); } else if (msg_type == FGS_PDU_SESSION_ESTABLISHMENT_ACC) { - capture_pdu_session_establishment_accept_msg(pdu_buffer, NAS_CONN_ESTABLI_CNF(msg_p).nasMsg.length); + capture_pdu_session_establishment_accept_msg(pdu_buffer, pdu_length); } break; @@ -1199,10 +1373,27 @@ void *nas_nrue(void *args_p) NAS_DOWNLINK_DATA_IND(msg_p).nasMsg.length, NAS_DOWNLINK_DATA_IND(msg_p).nasMsg.data); as_nas_info_t initialNasMsg = {0}; + nr_ue_nas_t *nas = get_ue_nas_info(0); uint8_t *pdu_buffer = NAS_DOWNLINK_DATA_IND(msg_p).nasMsg.data; - int msg_type = get_msg_type(pdu_buffer, NAS_DOWNLINK_DATA_IND(msg_p).nasMsg.length); - nr_ue_nas_t *nas = get_ue_nas_info(0); + int pdu_length = NAS_DOWNLINK_DATA_IND(msg_p).nasMsg.length; + + security_state_t security_state = nas_security_rx_process(nas, pdu_buffer, pdu_length); + /* special cases accepted without protection */ + if (security_state == NAS_SECURITY_UNPROTECTED) { + int msg_type = get_msg_type(pdu_buffer, pdu_length); + /* for the moment, only FGS_DEREGISTRATION_ACCEPT is accepted */ + if (msg_type == FGS_DEREGISTRATION_ACCEPT) + security_state = NAS_SECURITY_INTEGRITY_PASSED; + } + + if (security_state != NAS_SECURITY_INTEGRITY_PASSED + && security_state != NAS_SECURITY_NO_SECURITY_CONTEXT) { + LOG_E(NAS, "NAS integrity failed, discard incoming message\n"); + break; + } + + int msg_type = get_msg_type(pdu_buffer, pdu_length); switch (msg_type) { case FGS_IDENTITY_REQUEST: @@ -1212,14 +1403,13 @@ void *nas_nrue(void *args_p) generateAuthenticationResp(nas, &initialNasMsg, pdu_buffer); break; case FGS_SECURITY_MODE_COMMAND: - nas_itti_kgnb_refresh_req(instance, nas->security.kgnb); - generateSecurityModeComplete(nas, &initialNasMsg); + handle_security_mode_command(instance, nas, &initialNasMsg, pdu_buffer, pdu_length); break; case FGS_DOWNLINK_NAS_TRANSPORT: decodeDownlinkNASTransport(&initialNasMsg, pdu_buffer); break; case REGISTRATION_ACCEPT: - handle_registration_accept(instance, nas, pdu_buffer, NAS_DOWNLINK_DATA_IND(msg_p).nasMsg.length); + handle_registration_accept(instance, nas, pdu_buffer, pdu_length); break; case FGS_DEREGISTRATION_ACCEPT: LOG_I(NAS, "received deregistration accept\n"); diff --git a/openair3/NAS/NR_UE/nr_nas_msg_sim.h b/openair3/NAS/NR_UE/nr_nas_msg_sim.h index 9a260dfc8f..637004f95e 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg_sim.h +++ b/openair3/NAS/NR_UE/nr_nas_msg_sim.h @@ -89,11 +89,12 @@ typedef struct { uint8_t kseaf[32]; uint8_t kamf[32]; uint8_t knas_int[16]; + uint8_t knas_enc[16]; uint8_t res[16]; uint8_t rand[16]; uint8_t kgnb[32]; - uint32_t mm_counter; - uint32_t sm_counter; + uint32_t nas_count_ul; + uint32_t nas_count_dl; } ue_sa_security_key_t; typedef struct { diff --git a/openair3/SECU/nas_stream_eia1.c b/openair3/SECU/nas_stream_eia1.c index c648ee585f..dd514c777b 100644 --- a/openair3/SECU/nas_stream_eia1.c +++ b/openair3/SECU/nas_stream_eia1.c @@ -88,25 +88,36 @@ static uint64_t MUL64(uint64_t V, uint64_t P, uint64_t c) return result; } -/* mask32bit. -* Input n: an integer in 1-32. -* Output : a 32 bit mask. -* Prepares a 32 bit mask with required number of 1 bits on the MSB side. -*/ -static uint32_t mask32bit(int n) +/* read a big endian uint64_t at given address (potentially not 64-bits aligned) + * don't read more than 'available_bytes' + * (use 0 if no byte to read) + * (note: the compiler will optimize this, no need to do better) + */ +static inline uint64_t U64(uint8_t *p, int available_bytes) { - uint32_t mask=0x0; + uint64_t a = 0; + uint64_t b = 0; + uint64_t c = 0; + uint64_t d = 0; + uint64_t e = 0; + uint64_t f = 0; + uint64_t g = 0; + uint64_t h = 0; + switch (available_bytes) { + case 8: h = p[7]; /* falltrough */ + case 7: g = p[6]; /* falltrough */ + case 6: f = p[5]; /* falltrough */ + case 5: e = p[4]; /* falltrough */ + case 4: d = p[3]; /* falltrough */ + case 3: c = p[2]; /* falltrough */ + case 2: b = p[1]; /* falltrough */ + case 1: a = p[0]; + } - if ( n%32 == 0 ) - return 0xffffffff; - - while (n--) - mask = (mask>>1) ^ 0x80000000; - - return mask; + return (a << (32+24)) | (b << (32+16)) | (c << (32+8)) | (d << 32) + | (e << 24) | (f << 16) | (g << 8) | h; } - /*! * @brief Create integrity cmac t for a given message. * @param[in] stream_cipher Structure containing various variables to setup encoding @@ -125,11 +136,8 @@ void nas_stream_encrypt_eia1(nas_stream_cipher_t const *stream_cipher, uint8_t o uint64_t c; uint64_t M_D_2; int rem_bits; - uint32_t mask = 0; - uint32_t *message; uint8_t *key = (uint8_t *)stream_cipher->context; - message = (uint32_t*)stream_cipher->message; /* To operate 32 bit message internally. */ /* Load the Integrity Key for SNOW3G initialization as in section 4.4. */ memcpy(K+3,key+0,4); /*K[3] = key[0]; we assume K[3]=key[0]||key[1]||...||key[31] , with key[0] the @@ -181,11 +189,14 @@ void nas_stream_encrypt_eia1(nas_stream_cipher_t const *stream_cipher, uint8_t o EVAL = 0; c = 0x1b; + AssertFatal(stream_cipher->blength % 8 == 0, "unsupported buffer length\n"); + + uint8_t *message = stream_cipher->message; + /* for 0 <= i <= D-3 */ for (i=0; i 32) { - M_D_2 = ( (uint64_t) hton_int32(message[2*(D-2)]) << 32 ) | - (uint64_t) (hton_int32(message[2*(D-2)+1]) & mask); - } else { - M_D_2 = ( (uint64_t) hton_int32(message[2*(D-2)]) & mask) << 32 ; - } + M_D_2 = U64(&message[4 * (2*(D-2))], rem_bits/8); V = EVAL ^ M_D_2; EVAL = MUL64(V,P,c); diff --git a/radio/COMMON/common_lib.h b/radio/COMMON/common_lib.h index 0ff54a79ce..aa2d9e3189 100644 --- a/radio/COMMON/common_lib.h +++ b/radio/COMMON/common_lib.h @@ -195,6 +195,8 @@ typedef struct split7_config { struct { symbol_direction_t sym_dir[14]; } slot_dirs[160]; + /*! this is the exponent in 2^X for the FFT size */ + uint16_t fftSize; } split7_config_t; /*! \brief RF frontend parameters set by application */ diff --git a/radio/fhi_72/CMakeLists.txt b/radio/fhi_72/CMakeLists.txt index 88552eb46b..ba28d4c448 100644 --- a/radio/fhi_72/CMakeLists.txt +++ b/radio/fhi_72/CMakeLists.txt @@ -1,8 +1,15 @@ +# we need at least gcc-11 to build xran, so let's enforce it here +# (because xran might not check it, and we have control here) +if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 11.0 + OR CMAKE_C_COMPILER_VERSION VERSION_LESS 11.0) + message(FATAL_ERROR "you need to have at least gcc/g++-11 to use 7.2 FHI through xran") +endif() + # use env var PKG_CONFIG_PATH to override paths to libdpdk.pc pkg_check_modules(dpdk REQUIRED libdpdk) pkg_check_modules(numa REQUIRED numa) -find_package(xran 5.1.2 EXACT REQUIRED) # E release -> 5 +find_package(xran 5.1.3 EXACT REQUIRED) # E release -> 5 # Ignore xran-specific warning: we don't care/can't change the following warning, so suppress # alignment 1 of 'struct XYZ' is less than 2 diff --git a/radio/fhi_72/oran-config.c b/radio/fhi_72/oran-config.c index 853d2cefc4..ddee538dfd 100644 --- a/radio/fhi_72/oran-config.c +++ b/radio/fhi_72/oran-config.c @@ -729,7 +729,7 @@ static bool set_fh_frame_config(const openair0_config_t *oai0, struct xran_frame return true; } -static bool set_fh_ru_config(const paramdef_t *rup, int nru, struct xran_ru_config *ru_config) +static bool set_fh_ru_config(const paramdef_t *rup, uint16_t fftSize, int nru, struct xran_ru_config *ru_config) { ru_config->xranTech = XRAN_RAN_5GNR; ru_config->xranCat = XRAN_CATEGORY_A; @@ -740,7 +740,8 @@ static bool set_fh_ru_config(const paramdef_t *rup, int nru, struct xran_ru_conf ru_config->iqWidth_PRACH = *gpd(rup, nru, ORAN_RU_CONFIG_IQWIDTH_PRACH)->uptr; AssertFatal(ru_config->iqWidth_PRACH <= 16, "IQ Width for PRACH cannot be > 16!\n"); ru_config->compMeth_PRACH = ru_config->iqWidth_PRACH < 16 ? XRAN_COMPMETHOD_BLKFLOAT : XRAN_COMPMETHOD_NONE; - ru_config->fftSize = *gpd(rup, nru, ORAN_RU_CONFIG_FFT_SIZE)->uptr; + AssertFatal(fftSize > 0, "FFT size cannot be 0\n"); + ru_config->fftSize = fftSize; ru_config->byteOrder = XRAN_NE_BE_BYTE_ORDER; ru_config->iqOrder = XRAN_I_Q_ORDER; ru_config->xran_max_frame = 0; @@ -779,6 +780,7 @@ bool set_fh_config(int ru_idx, int num_rus, const openair0_config_t *oai0, struc DevAssert(oai0->rx_freq[0] == oai0->rx_freq[i]); DevAssert(oai0->nr_band > 0); DevAssert(oai0->nr_scs_for_raster > 0); + AssertFatal(oai0->threequarter_fs == 0, "cannot use three-quarter sampling with O-RAN 7.2 split\n"); // we simply assume that the loading process provides function to_nrarfcn() // to calculate the ARFCN numbers from frequency. That is not clean, but the @@ -869,7 +871,7 @@ bool set_fh_config(int ru_idx, int num_rus, const openair0_config_t *oai0, struc return false; if (!set_fh_frame_config(oai0, &fh_config->frame_conf)) return false; - if (!set_fh_ru_config(rup, nru, &fh_config->ru_conf)) + if (!set_fh_ru_config(rup, oai0->split7.fftSize, nru, &fh_config->ru_conf)) return false; fh_config->bbdev_enc = NULL; diff --git a/radio/fhi_72/oran-init.c b/radio/fhi_72/oran-init.c index 2fbca36d56..8673b5d2b4 100644 --- a/radio/fhi_72/oran-init.c +++ b/radio/fhi_72/oran-init.c @@ -123,6 +123,14 @@ static struct xran_prb_map get_xran_prb_map_ul(const struct xran_fh_config *f) return prbmap; } +static uint32_t next_power_2(uint32_t num) +{ + uint32_t power = 2; + while (power < num) + power <<= 1; + return power; +} + static uint32_t oran_allocate_uplane_buffers( void *instHandle, struct xran_buffer_list list[XRAN_MAX_ANTENNA_NR][XRAN_N_FE_BUF_LEN], @@ -132,7 +140,10 @@ static uint32_t oran_allocate_uplane_buffers( { xran_status_t status; uint32_t pool; - uint32_t numBufs = XRAN_N_FE_BUF_LEN * ant * XRAN_NUM_OF_SYMBOL_PER_SLOT; + // we need at least XRAN_N_FE_BUF_LEN * ant * XRAN_NUM_OF_SYMBOL_PER_SLOT + // buffers, but xran_bm_init() uses rte_pktmbuf_pool_create() which + // recommends to use a power of two for the buffers + uint32_t numBufs = next_power_2(XRAN_N_FE_BUF_LEN * ant * XRAN_NUM_OF_SYMBOL_PER_SLOT); status = xran_bm_init(instHandle, &pool, numBufs, bufSize); AssertFatal(XRAN_STATUS_SUCCESS == status, "Failed at xran_bm_init(), status %d\n", status); printf("xran_bm_init() hInstance %p poolIdx %u elements %u size %u\n", instHandle, pool, numBufs, bufSize); @@ -211,14 +222,14 @@ static void oran_allocate_cplane_buffers(void *instHandle, { xran_status_t status; uint32_t poolSec; - uint32_t numBufsSec = XRAN_N_FE_BUF_LEN * ant * XRAN_NUM_OF_SYMBOL_PER_SLOT * sect * XRAN_MAX_FRAGMENT; + uint32_t numBufsSec = next_power_2(XRAN_N_FE_BUF_LEN * ant * XRAN_NUM_OF_SYMBOL_PER_SLOT * sect * XRAN_MAX_FRAGMENT); uint32_t bufSizeSec = sizeof(struct xran_section_desc); status = xran_bm_init(instHandle, &poolSec, numBufsSec, bufSizeSec); AssertFatal(XRAN_STATUS_SUCCESS == status, "Failed at xran_bm_init(), status %d\n", status); printf("xran_bm_init() hInstance %p poolIdx %u elements %u size %u\n", instHandle, poolSec, numBufsSec, bufSizeSec); uint32_t poolPrb; - uint32_t numBufsPrb = XRAN_N_FE_BUF_LEN * ant * XRAN_NUM_OF_SYMBOL_PER_SLOT; + uint32_t numBufsPrb = next_power_2(XRAN_N_FE_BUF_LEN * ant * XRAN_NUM_OF_SYMBOL_PER_SLOT); uint32_t bufSizePrb = size_of_prb_map; status = xran_bm_init(instHandle, &poolPrb, numBufsPrb, bufSizePrb); AssertFatal(XRAN_STATUS_SUCCESS == status, "Failed at xran_bm_init(), status %d\n", status); diff --git a/radio/fhi_72/oran-params.h b/radio/fhi_72/oran-params.h index a3fb22267b..c7e17ea42a 100644 --- a/radio/fhi_72/oran-params.h +++ b/radio/fhi_72/oran-params.h @@ -91,13 +91,11 @@ #define ORAN_RU_CONFIG_IQWIDTH "iq_width" #define ORAN_RU_CONFIG_IQWIDTH_PRACH "iq_width_prach" -#define ORAN_RU_CONFIG_FFT_SIZE "fft_size" // clang-format off #define ORAN_RU_DESC {\ {ORAN_RU_CONFIG_IQWIDTH, "sample IQ width (16=uncompressed)\n", PARAMFLAG_MANDATORY, .u8ptr=NULL, .defuintval=16, TYPE_UINT8, 0}, \ {ORAN_RU_CONFIG_IQWIDTH_PRACH, "PRACH sample IQ width (16=uncompressed)\n", PARAMFLAG_MANDATORY, .u8ptr=NULL, .defuintval=16, TYPE_UINT8, 0}, \ - {ORAN_RU_CONFIG_FFT_SIZE, "Size of FFT at RU\n", PARAMFLAG_MANDATORY, .u8ptr=NULL, .defuintval=12, TYPE_UINT8, 0}, \ } // clang-format on diff --git a/radio/fhi_72/oran_isolate.c b/radio/fhi_72/oran_isolate.c index bdc786f482..32bdb7c771 100644 --- a/radio/fhi_72/oran_isolate.c +++ b/radio/fhi_72/oran_isolate.c @@ -34,6 +34,11 @@ #include "common/utils/threadPool/thread-pool.h" #include "oaioran.h" +// include the following file for VERSIONX, version of xran lib, to print it during +// startup. Only relevant for printing, if it ever makes problem, remove this +// line and the use of VERSIONX further below. It is relative to phy/fhi_lib/lib/api +#include "../../app/src/common.h" + typedef struct { eth_state_t e; rru_config_msg_type_t last_msg; @@ -309,7 +314,7 @@ __attribute__((__visibility__("default"))) int transport_init(openair0_device *d eth->last_msg = (rru_config_msg_type_t)-1; - printf("ORAN: %s\n", __FUNCTION__); + LOG_I(HW, "Initializing O-RAN 7.2 FH interface through xran library (compiled against headers of %s)\n", VERSIONX); initNotifiedFIFO(&oran_sync_fifo); eth->oran_priv = oai_oran_initialize(openair0_cfg); diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band77.273prb.fhi72.4x4-vvdn.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band77.273prb.fhi72.4x4-vvdn.conf index 5984d32b17..46bcb97982 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band77.273prb.fhi72.4x4-vvdn.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band77.273prb.fhi72.4x4-vvdn.conf @@ -295,7 +295,6 @@ fhi_72 = { ru_config = { iq_width = 9; iq_width_prach = 9; - fft_size = 12; }; prach_config = { eAxC_offset = 4; diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.273prb.fhi72.4x4-benetel550.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.273prb.fhi72.4x4-benetel550.conf index d5c22b7e5e..8cabc3e87e 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.273prb.fhi72.4x4-benetel550.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.273prb.fhi72.4x4-benetel550.conf @@ -288,7 +288,6 @@ fhi_72 = { ru_config = { iq_width = 9; iq_width_prach = 9; - fft_size = 12; }; prach_config = { eAxC_offset = 4; diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.273prb.fhi72.4x4-benetel650.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.273prb.fhi72.4x4-benetel650.conf index ddcb28a331..65d7a0e8c8 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.273prb.fhi72.4x4-benetel650.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.273prb.fhi72.4x4-benetel650.conf @@ -288,7 +288,6 @@ fhi_72 = { ru_config = { iq_width = 9; iq_width_prach = 9; - fft_size = 12; }; prach_config = { eAxC_offset = 4; diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.273prb.fhi72.4x4-liteon.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.273prb.fhi72.4x4-liteon.conf index 1ddba82cdf..88fa646936 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.273prb.fhi72.4x4-liteon.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.273prb.fhi72.4x4-liteon.conf @@ -286,7 +286,6 @@ fhi_72 = { ru_config = { iq_width = 9; iq_width_prach = 9; - fft_size = 12; }; prach_config = { eAxC_offset = 4;