From 90ec531554b95e8635140b09de6446c9572d8bb4 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Sun, 16 Nov 2025 16:25:05 +0100 Subject: [PATCH 01/29] Hashtable: remove unused code Signed-off-by: Robert Schmidt --- common/utils/ds/hashtable.c | 65 ------------------------------------- common/utils/ds/hashtable.h | 2 -- 2 files changed, 67 deletions(-) diff --git a/common/utils/ds/hashtable.c b/common/utils/ds/hashtable.c index c84d332973..78a62f570c 100644 --- a/common/utils/ds/hashtable.c +++ b/common/utils/ds/hashtable.c @@ -10,36 +10,6 @@ #include "assertions.h" -//------------------------------------------------------------------------------------------------------------------------------- -char *hashtable_rc_code2string(hashtable_rc_t rcP) -//------------------------------------------------------------------------------------------------------------------------------- -{ - switch (rcP) { - case HASH_TABLE_OK: - return "HASH_TABLE_OK"; - break; - - case HASH_TABLE_INSERT_OVERWRITTEN_DATA: - return "HASH_TABLE_INSERT_OVERWRITTEN_DATA"; - break; - - case HASH_TABLE_KEY_NOT_EXISTS: - return "HASH_TABLE_KEY_NOT_EXISTS"; - break; - - case HASH_TABLE_KEY_ALREADY_EXISTS: - return "HASH_TABLE_KEY_ALREADY_EXISTS"; - break; - - case HASH_TABLE_BAD_PARAMETER_HASHTABLE: - return "HASH_TABLE_BAD_PARAMETER_HASHTABLE"; - break; - - default: - return "UNKNOWN hashtable_rc_t"; - } -} -//------------------------------------------------------------------------------------------------------------------------------- /* * free int function * hash_free_int_func() is used when this hashtable is used to store int values as data (pointer = value). @@ -147,41 +117,6 @@ hashtable_rc_t hashtable_is_key_exists (const hash_table_t *const hashtblP, cons return HASH_TABLE_KEY_NOT_EXISTS; } -//------------------------------------------------------------------------------------------------------------------------------- -hashtable_rc_t hashtable_dump_content (const hash_table_t *const hashtblP, char *const buffer_pP, int *const remaining_bytes_in_buffer_pP ) -//------------------------------------------------------------------------------------------------------------------------------- -{ - hash_node_t *node = NULL; - unsigned int i = 0; - - if (hashtblP == NULL) { - *remaining_bytes_in_buffer_pP = snprintf( - buffer_pP, - *remaining_bytes_in_buffer_pP, - "HASH_TABLE_BAD_PARAMETER_HASHTABLE"); - return HASH_TABLE_BAD_PARAMETER_HASHTABLE; - } - - while ((i < hashtblP->size) && (*remaining_bytes_in_buffer_pP > 0)) { - if (hashtblP->nodes[i] != NULL) { - node=hashtblP->nodes[i]; - - while(node) { - *remaining_bytes_in_buffer_pP = snprintf( - buffer_pP, - *remaining_bytes_in_buffer_pP, - "Key 0x%"PRIx64" Element %p\n", - node->key, - node->data); - node=node->next; - } - } - - i += 1; - } - - return HASH_TABLE_OK; -} //------------------------------------------------------------------------------------------------------------------------------- /* diff --git a/common/utils/ds/hashtable.h b/common/utils/ds/hashtable.h index ba43b0230f..fd8fe013c3 100644 --- a/common/utils/ds/hashtable.h +++ b/common/utils/ds/hashtable.h @@ -44,12 +44,10 @@ typedef struct hash_table_iterator_s { const hash_table_t* const hashtbl; } hash_table_iterator_s; -char *hashtable_rc_code2string(hashtable_rc_t rcP); void hash_free_int_func(void *memoryP); hash_table_t *hashtable_create (const hash_size_t size, hash_size_t (*hashfunc)(const hash_key_t ), void (*freefunc)(void *)); hashtable_rc_t hashtable_destroy(hash_table_t **hashtbl); hashtable_rc_t hashtable_is_key_exists (const hash_table_t *const hashtbl, const uint64_t key); -hashtable_rc_t hashtable_dump_content (const hash_table_t *const hashtblP, char *const buffer_pP, int *const remaining_bytes_in_buffer_pP ); hashtable_rc_t hashtable_insert (hash_table_t *const hashtbl, const hash_key_t key, void *data); hashtable_rc_t hashtable_remove (hash_table_t *const hashtbl, const hash_key_t key); hashtable_rc_t hashtable_get (const hash_table_t *const hashtbl, const hash_key_t key, void **dataP); From 1be48251f0626b930e6485e07f205f3cefc582d2 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 13 Apr 2026 10:19:14 +0200 Subject: [PATCH 02/29] NFAPI_LIB needs z(lib) for crc32() Signed-off-by: Robert Schmidt --- CMakeLists.txt | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6e8758cea3..50ef0bfcba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -752,6 +752,7 @@ set(NFAPI_SRC add_library(NFAPI_LIB ${NFAPI_SRC}) target_link_libraries(NFAPI_LIB PUBLIC nfapi_common) target_link_libraries(NFAPI_LIB PUBLIC nr_fapi_p5 nr_fapi_p7) +target_link_libraries(NFAPI_LIB PRIVATE z) if(OAI_WLS) target_compile_definitions(NFAPI_LIB PRIVATE ENABLE_WLS) @@ -1742,7 +1743,7 @@ target_link_libraries(lte-softmodem PRIVATE lte_rrc nr_rrc s1ap m2ap x2ap m3ap GTPV1U SECURITY UTIL SCTP_CLIENT MME_APP SCHED_LIB SCHED_RU_LIB PHY_COMMON PHY PHY_RU L2 L2_LTE NFAPI_LIB NFAPI_VNF_LIB NFAPI_PNF_LIB NFAPI_USER_LIB MISC_NFAPI_LTE_LIB ${NAS_UE_LIB} ITTI SIMU radio_common softmodem_common - -Wl,--end-group z dl) + -Wl,--end-group dl) target_link_libraries(lte-softmodem PRIVATE pthread m CONFIG_LIB rt) target_link_libraries(lte-softmodem PRIVATE ${T_LIB}) @@ -1763,7 +1764,7 @@ add_executable(oairu target_link_libraries(oairu PRIVATE -Wl,--start-group SCHED_RU_LIB PHY_COMMON PHY_RU UTIL radio_common softmodem_common - -Wl,--end-group z dl) + -Wl,--end-group dl) target_link_libraries(oairu PRIVATE pthread m CONFIG_LIB rt ${T_LIB}) target_link_libraries(oairu PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs) @@ -1792,7 +1793,7 @@ target_link_libraries(lte-uesoftmodem PRIVATE SECURITY UTIL SCTP_CLIENT MME_APP SCHED_RU_LIB SCHED_UE_LIB PHY_COMMON PHY_UE PHY_RU L2_UE L2_LTE SIMU NFAPI_LIB NFAPI_PNF_LIB NFAPI_USER_LIB MISC_NFAPI_LTE_LIB ${NAS_UE_LIB} ITTI radio_common softmodem_common - -Wl,--end-group z dl) + -Wl,--end-group dl) target_link_libraries(lte-uesoftmodem PRIVATE pthread m CONFIG_LIB rt) target_link_libraries(lte-uesoftmodem PRIVATE ${T_LIB}) @@ -1812,7 +1813,7 @@ add_executable(nr-oru ${OPENAIR_DIR}/executables/main_nr_ru.c ) target_link_libraries(nr-oru PRIVATE - UTIL SCHED_RU_LIB PHY_COMMON PHY_RU PHY_NR shlib_loader z dl + UTIL SCHED_RU_LIB PHY_COMMON PHY_RU PHY_NR shlib_loader dl radio_common softmodem_common) target_link_libraries(nr-oru PRIVATE pthread m CONFIG_LIB rt ${T_LIB} utils barrier actor) @@ -1841,7 +1842,7 @@ target_link_libraries(nr-softmodem PRIVATE ngap s1ap L2_LTE_NR L2_NR MAC_NR_COMMON NFAPI_LIB NFAPI_VNF_LIB NFAPI_PNF_LIB NFAPI_USER_LIB SIMU x2ap f1ap m2ap m3ap e1ap radio_common time_management softmodem_common - -Wl,--end-group z dl) + -Wl,--end-group dl) target_link_libraries(nr-softmodem PRIVATE pthread m CONFIG_LIB rt) target_link_libraries(nr-softmodem PRIVATE ${T_LIB}) @@ -1881,7 +1882,7 @@ target_link_libraries(nr-cuup PRIVATE GTPV1U e1ap f1ap time_management softmodem_common alg - z dl pthread ${T_LIB}) + dl pthread ${T_LIB}) target_link_libraries(nr-cuup PRIVATE asn1_lte_rrc_hdrs asn1_nr_rrc_hdrs) if(E2_AGENT) target_link_libraries(nr-cuup PRIVATE e2_agent e2_agent_arg e2_ran_func_cuup) @@ -1909,7 +1910,7 @@ target_link_libraries(nr-uesoftmodem PRIVATE PHY_COMMON PHY_NR_COMMON PHY_NR_UE NR_L2_UE MAC_NR_COMMON NFAPI_LIB ITTI SIMU radio_common time_management softmodem_common - -Wl,--end-group z dl) + -Wl,--end-group dl) target_link_libraries(nr-uesoftmodem PRIVATE pthread m CONFIG_LIB rt nr_ue_phy_meas) target_link_libraries(nr-uesoftmodem PRIVATE ${T_LIB}) From 7e3406c3bd00466bd02cfab9d20cf4e58473fedb Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Sat, 11 Apr 2026 11:35:31 +0200 Subject: [PATCH 03/29] Delete dummy functions Signed-off-by: Robert Schmidt --- openair1/SCHED/nfapi_lte_dummy.c | 65 -------------------------------- 1 file changed, 65 deletions(-) diff --git a/openair1/SCHED/nfapi_lte_dummy.c b/openair1/SCHED/nfapi_lte_dummy.c index 1c22057064..55c339d7c8 100644 --- a/openair1/SCHED/nfapi_lte_dummy.c +++ b/openair1/SCHED/nfapi_lte_dummy.c @@ -16,18 +16,6 @@ int slot_ahead = 6; //uint8_t nfapi_mode=0; NR_IF_Module_t *NR_IF_Module_init(int Mod_id) {return NULL;} -void nr_fill_ulsch(PHY_VARS_gNB *gNB, - int frame, - int slot, - nfapi_nr_pusch_pdu_t *ulsch_pdu){} -void nr_fill_pucch(PHY_VARS_gNB *gNB, - int frame, - int slot, - nfapi_nr_pucch_pdu_t *pucch_pdu){} -void nr_schedule_rx_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu) -{ -} - void nr_phy_config_request(NR_PHY_Config_t *gNB){} void nr_dump_frame_parms(NR_DL_FRAME_PARMS *fp){} @@ -70,59 +58,6 @@ void *pnf_nr_p7_thread_start(void *ptr) { return 0; } - -bool wls_vnf_nr_send_p7_message(vnf_p7_t *vnf_p7, nfapi_nr_p7_message_header_t *msg) -{ - return false; -} - - -bool wls_vnf_nr_send_p5_message(vnf_t *vnf,uint16_t p5_idx, nfapi_nr_p4_p5_message_header_t* msg, uint32_t msg_len) -{ - return false; -} - -void *wls_fapi_vnf_nr_start_thread(void *ptr) -{ - return NULL; -} - -bool wls_pnf_nr_send_p7_message(pnf_p7_t* pnf_p7,nfapi_nr_p7_message_header_t *msg, uint32_t msg_len) -{ - return false; -} - -bool wls_pnf_nr_send_p5_message(pnf_t *pnf, nfapi_nr_p4_p5_message_header_t *msg, uint32_t msg_len) -{ - return false; -} - -void *wls_fapi_pnf_nr_start_thread(void *ptr) -{ - return NULL; -} - -void wls_pnf_set_p7_config(void *p7_config) -{ - -} - -void wls_fapi_nr_pnf_stop() -{ -} - -void wls_pnf_close(pthread_t p5_thread) -{ -} - -void wls_vnf_send_stop_request() -{ -} - -void wls_vnf_stop() -{ -} - void socket_nfapi_nr_pnf_stop() { } From b81442de913e175374a84edb5bacb42fa95111ec Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 13 Apr 2026 10:22:58 +0200 Subject: [PATCH 04/29] Link NFAPI_LIB into SCHED_NR_LIB phy_procedures_nr_gNB.c calls pack_srs() functions to pack individual SRS "subPDUs". Correspondingly, the library depends on NFAPI_LIB. In nr_ulsim, in a later commit, we will need to unpack the SRS.indication, so remove dummy functions and implement correctly. Signed-off-by: Robert Schmidt --- CMakeLists.txt | 1 + .../SIMULATION/NR_PHY/nr_unitary_common.c | 68 ------------------- 2 files changed, 1 insertion(+), 68 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 50ef0bfcba..665904c53f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -713,6 +713,7 @@ set(SCHED_NR_SRC ) add_library(SCHED_NR_LIB ${SCHED_NR_SRC}) target_link_libraries(SCHED_NR_LIB PRIVATE asn1_nr_rrc_hdrs UTIL) +target_link_libraries(SCHED_NR_LIB PRIVATE NFAPI_LIB) # some FAPI messages pack "subPDUs" set(SCHED_SRC_RU ${OPENAIR1_DIR}/SCHED/ru_procedures.c diff --git a/openair1/SIMULATION/NR_PHY/nr_unitary_common.c b/openair1/SIMULATION/NR_PHY/nr_unitary_common.c index 9a595a9295..87e668db28 100644 --- a/openair1/SIMULATION/NR_PHY/nr_unitary_common.c +++ b/openair1/SIMULATION/NR_PHY/nr_unitary_common.c @@ -56,40 +56,6 @@ void handle_nr_slot_ind(uint16_t sfn, uint16_t slot) UNUSED(slot); } -int pack_nr_srs_beamforming_report(void *pMessageBuf, void *pPackedBuf, uint32_t packedBufLen) -{ - UNUSED(pMessageBuf); - UNUSED(pPackedBuf); - UNUSED(packedBufLen); - return 0; -} -int unpack_nr_srs_beamforming_report(void *pMessageBuf, uint32_t messageBufLen, void *pUnpackedBuf, uint32_t unpackedBufLen) -{ - UNUSED(pMessageBuf); - UNUSED(messageBufLen); - UNUSED(pUnpackedBuf); - UNUSED(unpackedBufLen); - return 0; -} -int pack_nr_srs_normalized_channel_iq_matrix(void *pMessageBuf, void *pPackedBuf, uint32_t packedBufLen) -{ - UNUSED(pMessageBuf); - UNUSED(pPackedBuf); - UNUSED(packedBufLen); - return 0; -} -int unpack_nr_srs_normalized_channel_iq_matrix(void *pMessageBuf, - uint32_t messageBufLen, - void *pUnpackedBuf, - uint32_t unpackedBufLen) -{ - UNUSED(pMessageBuf); - UNUSED(messageBufLen); - UNUSED(pUnpackedBuf); - UNUSED(unpackedBufLen); - return 0; -} - void configure_nr_nfapi_pnf(char *vnf_ip_addr, int vnf_p5_port, char *pnf_ip_addr, int pnf_p7_port, int vnf_p7_port) { UNUSED(vnf_ip_addr); @@ -102,40 +68,6 @@ void configure_nr_nfapi_vnf(eth_params_t params) { UNUSED(params); } -int nfapi_nr_p7_message_pack(void *pMessageBuf, void *pPackedBuf, uint32_t packedBufLen, nfapi_p7_codec_config_t *config) -{ - UNUSED(pMessageBuf); - UNUSED(pPackedBuf); - UNUSED(packedBufLen); - UNUSED(config); - return 0; -} -bool nfapi_nr_p7_message_unpack(void *pMessageBuf, - uint32_t messageBufLen, - void *pUnpackedBuf, - uint32_t unpackedBufLen, - nfapi_p7_codec_config_t *config) -{ - UNUSED(pMessageBuf); - UNUSED(messageBufLen); - UNUSED(pUnpackedBuf); - UNUSED(unpackedBufLen); - UNUSED(config); - return false; -} -int nfapi_p7_message_header_unpack(void *pMessageBuf, - uint32_t messageBufLen, - void *pUnpackedBuf, - uint32_t unpackedBufLen, - nfapi_p7_codec_config_t *config) -{ - UNUSED(pMessageBuf); - UNUSED(messageBufLen); - UNUSED(pUnpackedBuf); - UNUSED(unpackedBufLen); - UNUSED(config); - return 0; -} void nr_mac_rrc_sync_ind(const module_id_t module_id, const frame_t frame, const bool in_sync) { From 449910dbc75bc8a2c9db66cbbdce05704551e2c7 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 17 Mar 2026 08:06:16 +0100 Subject: [PATCH 05/29] Add fsn lib to simplify frame/slot handling Refactor existing code, and harmonize interface. Add a couple of tests. Signed-off-by: Robert Schmidt --- common/utils/CMakeLists.txt | 2 +- common/utils/fsn.c | 55 ++++++++++++ common/utils/fsn.h | 36 ++++++++ common/utils/tests/CMakeLists.txt | 5 ++ common/utils/tests/test_fsn.cpp | 84 +++++++++++++++++++ .../LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 40 ++------- openair2/LAYER2/NR_MAC_gNB/main.c | 1 + openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h | 9 +- 8 files changed, 189 insertions(+), 43 deletions(-) create mode 100644 common/utils/fsn.c create mode 100644 common/utils/fsn.h create mode 100644 common/utils/tests/test_fsn.cpp diff --git a/common/utils/CMakeLists.txt b/common/utils/CMakeLists.txt index 3196b39868..fc113ffa46 100644 --- a/common/utils/CMakeLists.txt +++ b/common/utils/CMakeLists.txt @@ -16,7 +16,7 @@ add_subdirectory(nr) add_subdirectory(LOG) add_subdirectory(threadPool) add_subdirectory(time_manager) -add_library(utils utils.c system.c time_meas.c time_stat.c tuntap_if.c reverse_bits.c) +add_library(utils utils.c system.c time_meas.c time_stat.c tuntap_if.c reverse_bits.c fsn.c) target_include_directories(utils PUBLIC .) target_link_libraries(utils PRIVATE ${T_LIB}) add_subdirectory(barrier) diff --git a/common/utils/fsn.c b/common/utils/fsn.c new file mode 100644 index 0000000000..e8566a2da9 --- /dev/null +++ b/common/utils/fsn.c @@ -0,0 +1,55 @@ +/* + * SPDX-License-Identifier: LicenseRef-CSSL-1.0 + */ + +#include "fsn.h" + +#include + +static int num_slots[5] = {10, 20, 40, 80, 160}; + +fsn_t fsn_add_delta(fsn_t fsn, uint32_t delta) +{ + const int slots_frame = num_slots[fsn.mu]; + int next_f = fsn.f + delta / slots_frame; + int next_s = fsn.s + delta % slots_frame; + fsn_t res = { + .f = (next_f + next_s / slots_frame) % 1024, + .s = next_s % slots_frame, + .mu = fsn.mu, + }; + return res; +} + +int fsn_get_diff(fsn_t a, fsn_t b) +{ + assert(a.mu == b.mu); + const int slots_frame = num_slots[a.mu]; + int diff = (a.f * slots_frame + a.s) - (b.f * slots_frame + b.s); + if (diff < -512 * slots_frame) + diff += 1024 * slots_frame; + else if (diff > 512 * slots_frame) + diff -= 1024 * slots_frame; + return diff; +} + +fsn_t fsn_get_max(fsn_t a, fsn_t b) +{ + if (fsn_get_diff(a, b) <= 0) { + return b; + } else { + return a; + } +} + +bool fsn_equal(fsn_t a, fsn_t b) +{ + assert(a.mu == b.mu); + return a.f == b.f && a.s == b.s && a.mu == b.mu; +} + +bool fsn_in_the_past(fsn_t old, fsn_t new) +{ + int diff = fsn_get_diff(old, new); + return diff < 0; +} diff --git a/common/utils/fsn.h b/common/utils/fsn.h new file mode 100644 index 0000000000..dfa126bc85 --- /dev/null +++ b/common/utils/fsn.h @@ -0,0 +1,36 @@ +/* + * SPDX-License-Identifier: LicenseRef-CSSL-1.0 + */ + +#ifndef FSN_H_ +#define FSN_H_ + +#include +#include + +/// helper type to encapsulate a frame/slot combination in a single type. +typedef struct fsn { + uint16_t f; /// frame + uint16_t s; /// slot + uint8_t mu; /// numerology +} fsn_t; + +/* \brief Add delta slots to fsn. Note that this depends also on the stored + * numerology inside fsn. */ +fsn_t fsn_add_delta(fsn_t fsn, uint32_t delta); + +/* \brief Get difference of slots for a and b. + * \note asserts if numerology not the same. */ +int fsn_get_diff(fsn_t a, fsn_t b); + +/* \brief Gets the larger of two fsns */ +fsn_t fsn_get_max(fsn_t a, fsn_t b); + +/* \brief Compare if both fsn_t's are equal */ +bool fsn_equal(fsn_t a, fsn_t b); + +/* \brief Determine if told is in the past of tnew. The threshold is 512 + * frames. */ +bool fsn_in_the_past(fsn_t told, fsn_t tnew); + +#endif /* FSN_H_ */ diff --git a/common/utils/tests/CMakeLists.txt b/common/utils/tests/CMakeLists.txt index e3c310fed9..7401e2d1ea 100644 --- a/common/utils/tests/CMakeLists.txt +++ b/common/utils/tests/CMakeLists.txt @@ -2,3 +2,8 @@ add_executable(test_tpool_vs_actors test_tpool_vs_actors.c) target_link_libraries(test_tpool_vs_actors PRIVATE thread-pool pthread LOG minimal_lib actor) + +add_executable(test_fsn test_fsn.cpp) +add_dependencies(tests test_fsn) +target_link_libraries(test_fsn PRIVATE utils GTest::gtest) +add_test(NAME test_fsn COMMAND ./test_fsn) diff --git a/common/utils/tests/test_fsn.cpp b/common/utils/tests/test_fsn.cpp new file mode 100644 index 0000000000..3cbe34dfb2 --- /dev/null +++ b/common/utils/tests/test_fsn.cpp @@ -0,0 +1,84 @@ +/* + * SPDX-License-Identifier: LicenseRef-CSSL-1.0 + */ + +#include +extern "C" { +#include "fsn.h" +} + +TEST(fsn, test_mu0_fsn_add) { + fsn_t current = {123, 5, 0}; + fsn_t next = current; + + next = fsn_add_delta(next, 12); + EXPECT_EQ(next.f, 124); + EXPECT_EQ(next.s, 7); + EXPECT_EQ(next.mu, 0); + + next = fsn_add_delta(next, 10); + EXPECT_EQ(next.f, 125); + EXPECT_EQ(next.s, 7); + EXPECT_EQ(next.mu, 0); + + next = fsn_add_delta(next, 10 * 1024); + EXPECT_EQ(next.f, 125); + EXPECT_EQ(next.s, 7); + EXPECT_EQ(next.mu, 0); +} + +TEST(fsn, test_mu0_get_diff) { + fsn_t current = {123, 5, 0}; + fsn_t next = fsn_add_delta(current, 12); + int diff; + + diff = fsn_get_diff(current, next); + EXPECT_EQ(diff, -12); + + diff = fsn_get_diff(next, current); + EXPECT_EQ(diff, 12); +} + +TEST(fsn, test_mu0_get_max) { + fsn_t current = {123, 5, 0}; + fsn_t next = {125, 7, 0}; + + fsn_t max = fsn_get_max(current, next); + EXPECT_EQ(max.f, next.f); + EXPECT_EQ(max.s, next.s); + EXPECT_EQ(max.mu, next.mu); +} + +TEST(fsn, test_mu0_equal) { + fsn_t a = {123, 5, 0}; + fsn_t b = a; + fsn_t c = {123, 6, 0}; + + EXPECT_TRUE(fsn_equal(a, b)); + EXPECT_FALSE(fsn_equal(a, c)); +} + +TEST(fsn, test_mu0_in_the_past) { + fsn_t a = {0, 0, 0}; + fsn_t n = {100, 9, 0}; + fsn_t b_bef = {511, 9, 0}; + fsn_t b_ex = {512, 0, 0}; + fsn_t b_aft = {512, 1, 0}; + + // a is in the past of n + EXPECT_TRUE(fsn_in_the_past(a, n)); + // similarly, a is in the past of b_bef + EXPECT_TRUE(fsn_in_the_past(a, b_bef)); + // difference "to both sides" is 512 frames, so we consider a still in the + // past of b_ex + EXPECT_TRUE(fsn_in_the_past(a, b_ex)); + // (b_aft - a) < (a - b_aft) => b_aft is in the past of a because of + // wrap-around + EXPECT_TRUE(fsn_in_the_past(b_aft, a)); +} + +int main(int argc, char** argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index be869d9de9..db35776944 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -2528,34 +2528,6 @@ void post_process_ulsch(gNB_MAC_INST *nr_mac, post_process_pusch_t *pusch, NR_UE nr_mac->cset0_bwp_size); } -fsn_t fs_add_delta(const frame_structure_t *fs, uint32_t delta, fsn_t fsn) -{ - const int slots_frame = fs->numb_slots_frame; - fsn_t res = { - .f = (fsn.f + (fsn.s + delta) / slots_frame) % MAX_FRAME_NUMBER, - .s = (fsn.s + delta) % slots_frame, - }; - return res; -} -int fs_get_diff(const frame_structure_t *fs, fsn_t a, fsn_t b) -{ - const int slots_frame = fs->numb_slots_frame; - int diff = (a.f * slots_frame + a.s) - (b.f * slots_frame + b.s); - if (diff < -512 * slots_frame) - diff += 1024 * slots_frame; - else if (diff > 512 * slots_frame) - diff -= 1024 * slots_frame; - return diff; -} -fsn_t fs_get_max(const frame_structure_t *fs, fsn_t a, fsn_t b) -{ - if (fs_get_diff(fs, a, b) <= 0) { - return b; - } else { - return a; - } -} - static void nr_ulsch_preprocessor(gNB_MAC_INST *nr_mac, post_process_pusch_t *pp_pusch) { int frame = pp_pusch->frame; @@ -2579,8 +2551,8 @@ static void nr_ulsch_preprocessor(gNB_MAC_INST *nr_mac, post_process_pusch_t *pp // FAPI cannot handle more than MAX_DCI_CORESET DCIs max_dci = min(max_dci, MAX_DCI_CORESET); - fsn_t current = {frame, slot}; - fsn_t min_next = fs_add_delta(fs, min_rxtx, current); + fsn_t current = {frame, slot, *scc->ssbSubcarrierSpacing}; + fsn_t min_next = fsn_add_delta(current, min_rxtx); /* if it's the last DL slot, we should try all TDAs to make sure that the * scheduler can reach e.g. the next mixed slot. Otherwise, if we don't, we * might starve HARQ processes that need a retransmission in a specific slot @@ -2589,16 +2561,16 @@ static void nr_ulsch_preprocessor(gNB_MAC_INST *nr_mac, post_process_pusch_t *pp fsn_t *next = &nr_mac->ul_next; while (max_dci > 0) { /* go to the next UL slot, skipping DL if necessary */ - *next = fs_get_max(fs, *next, min_next); + *next = fsn_get_max(*next, min_next); while (!is_ul_slot(next->s, fs)) - *next = fs_add_delta(fs, 1, *next); + *next = fsn_add_delta(*next, 1); if (!is_dl_slot(current.s, fs)) // if current slot is not DL, nothing to do break; /* get a TDA so that next UL scheduling slot can be reached from current, * and exit if there is no such TDA. Remove koffset, as it is * "cell-specific", i.e., the UE will add it on the computation. */ - int k2 = fs_get_diff(fs, *next, current) - koffset; + int k2 = fsn_get_diff(*next, current) - koffset; DevAssert(k2 > 0); // we assume that all beams have the same symbol utilization in all RBs for // simplification (but this might not be true). Otherwise, we would need to @@ -2631,7 +2603,7 @@ static void nr_ulsch_preprocessor(gNB_MAC_INST *nr_mac, post_process_pusch_t *pp break; max_dci -= sched; - *next = fs_add_delta(fs, 1, *next); + *next = fsn_add_delta(*next, 1); } } diff --git a/openair2/LAYER2/NR_MAC_gNB/main.c b/openair2/LAYER2/NR_MAC_gNB/main.c index ca12c48c48..1f619308c2 100644 --- a/openair2/LAYER2/NR_MAC_gNB/main.c +++ b/openair2/LAYER2/NR_MAC_gNB/main.c @@ -293,6 +293,7 @@ void mac_top_init_gNB(ngran_node_t node_type, RC.nrmac[i]->cset0_bwp_start = 0; RC.nrmac[i]->cset0_bwp_size = 0; + RC.nrmac[i]->ul_next = (fsn_t) {.mu = *scc->ssbSubcarrierSpacing}; RC.nrmac[i]->print_ue_stats = true; pthread_mutex_init(&RC.nrmac[i]->sched_lock, NULL); diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index f7ea466a94..391bfeabe8 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -16,6 +16,7 @@ #include #include #include +#include "fsn.h" #include "common/utils/ds/seq_arr.h" #include "common/utils/nr/nr_common.h" #include "common/utils/ds/byte_array.h" @@ -878,14 +879,6 @@ typedef struct { NR_ControlResourceSet_t coreset; } NR_sched_ctrl_sib1_t; -/// helper type to encapsulate a frame/slot combination in a single type. -/// Currently only used in the UL preprocessor. Note: if you use this type -/// further, please refactor it into a common type first. -typedef struct fsn { - frame_t f; - slot_t s; -} fsn_t; - typedef struct NR_du_stats { /// cell-wide wide-band CQI distribution, see 28.552 5.1.1.11.1; /// 0-15 CQI, 1-8 RI, 1-3 CQI table From 51e17fd7c8bb36465b3b88382a371c97bfc3ed4f Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 17 Mar 2026 08:06:48 +0100 Subject: [PATCH 06/29] Add test_tpool_vs_actors to unit tests Signed-off-by: Robert Schmidt --- common/utils/tests/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/utils/tests/CMakeLists.txt b/common/utils/tests/CMakeLists.txt index 7401e2d1ea..14d2f83d7a 100644 --- a/common/utils/tests/CMakeLists.txt +++ b/common/utils/tests/CMakeLists.txt @@ -2,6 +2,8 @@ add_executable(test_tpool_vs_actors test_tpool_vs_actors.c) target_link_libraries(test_tpool_vs_actors PRIVATE thread-pool pthread LOG minimal_lib actor) +add_test(NAME test_tpool_vs_actors COMMAND ./test_tpool_vs_actors) +add_dependencies(tests test_tpool_vs_actors) add_executable(test_fsn test_fsn.cpp) add_dependencies(tests test_fsn) From 34aad5d2761bb50c0d054aa31da6f379f26b4d4f Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 11 Nov 2025 17:58:41 +0100 Subject: [PATCH 07/29] Add spsc_q data structure library This data structure is designed to support the use cases of the L1, i.e., provide a queue which can be quickly used to order L1 jobs in FIFO order with simple iteration of jobs to treat. It is a Single-Consumer Single-Producer queue library. Signed-off-by: Robert Schmidt --- common/utils/ds/CMakeLists.txt | 1 + common/utils/ds/spsc_q.c | 112 +++++++++++++++++ common/utils/ds/spsc_q.h | 32 +++++ common/utils/ds/tests/CMakeLists.txt | 10 +- common/utils/ds/tests/test_spsc_q.cpp | 135 +++++++++++++++++++++ common/utils/ds/tests/test_spsc_q_perf.cpp | 75 ++++++++++++ 6 files changed, 363 insertions(+), 2 deletions(-) create mode 100644 common/utils/ds/spsc_q.c create mode 100644 common/utils/ds/spsc_q.h create mode 100644 common/utils/ds/tests/test_spsc_q.cpp create mode 100644 common/utils/ds/tests/test_spsc_q_perf.cpp diff --git a/common/utils/ds/CMakeLists.txt b/common/utils/ds/CMakeLists.txt index 7007d77fac..6f7053a1be 100644 --- a/common/utils/ds/CMakeLists.txt +++ b/common/utils/ds/CMakeLists.txt @@ -6,6 +6,7 @@ add_library(ds OBJECT seq_arr.c hashtable.c obj_hashtable.c + spsc_q.c ) target_include_directories(ds PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/common/utils/ds/spsc_q.c b/common/utils/ds/spsc_q.c new file mode 100644 index 0000000000..fdc97f00b8 --- /dev/null +++ b/common/utils/ds/spsc_q.c @@ -0,0 +1,112 @@ +/* + * SPDX-License-Identifier: LicenseRef-CSSL-1.0 + */ + +#include +#include +#include + +#include "spsc_q.h" + +#define rb_lock(LOCK) if (pthread_mutex_lock(LOCK) != 0) abort() +#define rb_unlock(LOCK) if (pthread_mutex_unlock(LOCK) != 0) abort() + +spsc_q_t spsc_q_alloc(size_t cnt, size_t elsiz) +{ + /* internally, use one element more: the ringbuffer is full if + * (write_idx + 1) % cnt == read_idx, so it's full if one element is still + * free (because otherwise write_idx == read_idx, but this is the empty + * condition) */ + cnt += 1; + spsc_q_t rb = {.cnt = cnt, .elsiz = elsiz}; + rb.buf = calloc(cnt, elsiz); + pthread_mutex_init(&rb.mtx, NULL); + return rb; +} + +void spsc_q_free(spsc_q_t *rb) +{ + free(rb->buf); + pthread_mutex_destroy(&rb->mtx); + memset(rb, 0, sizeof(*rb)); +} + +bool spsc_q_put(spsc_q_t *rb, const void *src, size_t elsiz) +{ + assert(elsiz == rb->elsiz); + rb_lock(&rb->mtx); + if ((rb->write_idx + 1) % rb->cnt == rb->read_idx) { + rb_unlock(&rb->mtx); + return false; + } + + uint8_t *bufpos = &rb->buf[rb->write_idx * rb->elsiz]; + memcpy(bufpos, src, elsiz); + rb->write_idx = (rb->write_idx + 1) % rb->cnt; + rb_unlock(&rb->mtx); + return true; +} + +bool spsc_q_get_if(spsc_q_t *rb, pred p, void *user, void *dest, size_t elsiz) +{ + assert(elsiz == rb->elsiz); + rb_lock(&rb->mtx); + if (rb->write_idx == rb->read_idx) { + rb_unlock(&rb->mtx); + return false; + } + + uint8_t *bufpos = &rb->buf[rb->read_idx * rb->elsiz]; + if (!p(bufpos, user)) { + rb_unlock(&rb->mtx); + return false; + } + + memcpy(dest, bufpos, elsiz); + rb->read_idx = (rb->read_idx + 1) % rb->cnt; + rb_unlock(&rb->mtx); + return true; +} + +static bool always(const void *data, void *user) +{ + (void) data; + (void) user; + return true; +} +bool spsc_q_get(spsc_q_t *rb, void *dest, size_t elsiz) +{ + return spsc_q_get_if(rb, always, NULL, dest, elsiz); +} + +int spsc_q_get_while(spsc_q_t *rb, pred p, void *user, void *dest, size_t elsiz, size_t max_len) +{ + assert(elsiz == rb->elsiz); + rb_lock(&rb->mtx); + size_t count = 0; + while (rb->write_idx != rb->read_idx && count < max_len) { + uint8_t *bufpos = &rb->buf[rb->read_idx * rb->elsiz]; + if (!p(bufpos, user)) + break; + memcpy(dest + count * elsiz, bufpos, elsiz); + count++; + rb->read_idx = (rb->read_idx + 1) % rb->cnt; + } + rb_unlock(&rb->mtx); + return count; +} + +int spsc_q_drop_while(spsc_q_t *rb, pred p, void *user) +{ + rb_lock(&rb->mtx); + size_t dropped = 0; + while (rb->write_idx != rb->read_idx) { + uint8_t *bufpos = &rb->buf[rb->read_idx * rb->elsiz]; + if (!p(bufpos, user)) + break; + dropped++; + rb->read_idx = (rb->read_idx + 1) % rb->cnt; + } + rb_unlock(&rb->mtx); + return dropped; +} diff --git a/common/utils/ds/spsc_q.h b/common/utils/ds/spsc_q.h new file mode 100644 index 0000000000..d00cb4b77d --- /dev/null +++ b/common/utils/ds/spsc_q.h @@ -0,0 +1,32 @@ +/* + * SPDX-License-Identifier: LicenseRef-CSSL-1.0 + */ + +#ifndef SPSC_Q_H_ +#define SPSC_Q_H_ + +#include +#include +#include + +typedef struct spsc_q { + uint8_t *buf; + size_t elsiz; + size_t cnt; + size_t write_idx; + size_t read_idx; + pthread_mutex_t mtx; +} spsc_q_t; + +spsc_q_t spsc_q_alloc(size_t cnt, size_t elsiz); +void spsc_q_free(spsc_q_t *rb); + +bool spsc_q_put(spsc_q_t *rb, const void *src, size_t elsiz); +bool spsc_q_get(spsc_q_t *rb, void *dest, size_t elsiz); + +typedef bool (*pred)(const void *data, void *user); +bool spsc_q_get_if(spsc_q_t *rb, pred p, void *user, void *dest, size_t elsiz); +int spsc_q_drop_while(spsc_q_t *rb, pred p, void *user); +int spsc_q_get_while(spsc_q_t *rb, pred p, void *user, void *dest, size_t elsiz, size_t max_len); + +#endif /* SPSC_Q_H_ */ diff --git a/common/utils/ds/tests/CMakeLists.txt b/common/utils/ds/tests/CMakeLists.txt index a38cf159cb..1a2e5277a1 100644 --- a/common/utils/ds/tests/CMakeLists.txt +++ b/common/utils/ds/tests/CMakeLists.txt @@ -8,5 +8,11 @@ add_test(NAME test_seq_arr COMMAND test_seq_arr) # no options required add_executable(test_hashtable test_hashtable.cpp) add_dependencies(tests test_hashtable) target_link_libraries(test_hashtable PRIVATE ds GTest::gtest) -add_test(NAME test_hashtable - COMMAND ./test_hashtable) +add_test(NAME test_hashtable COMMAND ./test_hashtable) + +add_executable(test_spsc_q test_spsc_q.cpp) +target_link_libraries(test_spsc_q ds GTest::gtest) +add_dependencies(tests test_spsc_q) +add_test(NAME test_spsc_q COMMAND test_spsc_q) +add_executable(test_spsc_q_perf test_spsc_q_perf.cpp) +target_link_libraries(test_spsc_q_perf PRIVATE ds benchmark::benchmark) diff --git a/common/utils/ds/tests/test_spsc_q.cpp b/common/utils/ds/tests/test_spsc_q.cpp new file mode 100644 index 0000000000..0c1638d490 --- /dev/null +++ b/common/utils/ds/tests/test_spsc_q.cpp @@ -0,0 +1,135 @@ +/* + * SPDX-License-Identifier: LicenseRef-CSSL-1.0 + */ + +#include "gtest/gtest.h" +extern "C" { +#include "spsc_q.h" +} + +TEST(spsc_q, basic_test) { + spsc_q_t rb = spsc_q_alloc(2, sizeof(int)); + + int a = 1; + EXPECT_TRUE(spsc_q_put(&rb, &a, sizeof(a))); + EXPECT_TRUE(spsc_q_put(&rb, &a, sizeof(a))); + EXPECT_FALSE(spsc_q_put(&rb, &a, sizeof(a))); + + int b; + EXPECT_TRUE(spsc_q_get(&rb, &b, sizeof(b))); + EXPECT_EQ(a, b); + EXPECT_TRUE(spsc_q_get(&rb, &b, sizeof(b))); + EXPECT_EQ(a, b); + EXPECT_FALSE(spsc_q_get(&rb, &b, sizeof(b))); + + spsc_q_free(&rb); +} + +TEST(spsc_q, cont_write) { + spsc_q_t rb = spsc_q_alloc(10, sizeof(int)); + + int a = 1; + for (int i = 0; i < 1111; ++i) { + EXPECT_TRUE(spsc_q_put(&rb, &a, sizeof(a))); + a++; + EXPECT_TRUE(spsc_q_put(&rb, &a, sizeof(a))); + a++; + EXPECT_TRUE(spsc_q_put(&rb, &a, sizeof(a))); + a++; + + int b; + EXPECT_TRUE(spsc_q_get(&rb, &b, sizeof(b))); + EXPECT_EQ(b, a - 3); + EXPECT_TRUE(spsc_q_get(&rb, &b, sizeof(b))); + EXPECT_EQ(b, a - 2); + EXPECT_TRUE(spsc_q_get(&rb, &b, sizeof(b))); + EXPECT_EQ(b, a - 1); + } + + spsc_q_free(&rb); +} + +bool count(const void *data, void *user) +{ + EXPECT_TRUE(data != NULL); + int *c = (int *)user; + (*c)++; + return true; /* count all */ +} +TEST(spsc_q, iterator) { + spsc_q_t rb = spsc_q_alloc(10, sizeof(int)); + int n = 8; + for (int i = 0; i < n; ++i) + EXPECT_TRUE(spsc_q_put(&rb, &i, sizeof(i))); + + int c = 0; + int results[n]; + int ret = spsc_q_get_while(&rb, count, &c, results, sizeof(*results), n); + EXPECT_EQ(c, n); + EXPECT_EQ(ret, n); + for (int i = 0; i < n; ++i) + EXPECT_EQ(results[i], i); + + int b; + EXPECT_FALSE(spsc_q_get(&rb, &b, sizeof(b))); /* empty! */ + + spsc_q_free(&rb); +} + +bool drop(const void *data, void *user) +{ + const int *e = (const int *)data; + int *c = (int *)user; + EXPECT_EQ(*e, *c); + (*c)++; + return true; /* drop all */ +} +TEST(spsc_q, drop) { + spsc_q_t rb = spsc_q_alloc(10, sizeof(int)); + int n = 8; + for (int i = 0; i < n; ++i) + EXPECT_TRUE(spsc_q_put(&rb, &i, sizeof(i))); + + int c = 0; + int ret = spsc_q_drop_while(&rb, drop, &c); + EXPECT_EQ(c, n); + EXPECT_EQ(ret, n); + + int b; + EXPECT_FALSE(spsc_q_get(&rb, &b, sizeof(b))); /* empty! */ + + spsc_q_free(&rb); +} + + +TEST(spsc_q, full) { + int n = 3; + spsc_q_t rb = spsc_q_alloc(n, sizeof(int)); + int a = 1; + EXPECT_TRUE(spsc_q_put(&rb, &a, sizeof(a))); + a++; + EXPECT_TRUE(spsc_q_put(&rb, &a, sizeof(a))); + a++; + EXPECT_TRUE(spsc_q_put(&rb, &a, sizeof(a))); + a++; + EXPECT_FALSE(spsc_q_put(&rb, &a, sizeof(a))); /* full! */ + + int b; + EXPECT_TRUE(spsc_q_get(&rb, &b, sizeof(b))); + EXPECT_EQ(b, a - n); + + EXPECT_TRUE(spsc_q_put(&rb, &a, sizeof(a))); + a++; + EXPECT_FALSE(spsc_q_put(&rb, &a, sizeof(a))); /* full! */ + + EXPECT_TRUE(spsc_q_get(&rb, &b, sizeof(b))); + EXPECT_EQ(b, a - n); + + spsc_q_free(&rb); +} + +int main(int argc, char** argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/common/utils/ds/tests/test_spsc_q_perf.cpp b/common/utils/ds/tests/test_spsc_q_perf.cpp new file mode 100644 index 0000000000..6487e9e6c3 --- /dev/null +++ b/common/utils/ds/tests/test_spsc_q_perf.cpp @@ -0,0 +1,75 @@ +#include "benchmark/benchmark.h" +#include "stdio.h" +#include "assert.h" +#include "pthread.h" + +extern "C" { +#include "spsc_q.h" +} + +typedef struct q_args { + spsc_q_t q; + int range; +} q_args_t; + +static void *producer(void *arg) +{ + q_args_t *a = (q_args_t *) arg; + int i = 0; + while (i <= a->range) { + if (spsc_q_put(&a->q, &i, sizeof(i))) + i++; + } + return NULL; +} + +static void *consumer(void *arg) +{ + q_args_t *a = (q_args_t *) arg; + int i = 0; + while (i != a->range) { + int r; + if (spsc_q_get(&a->q, &r, sizeof(r))) { + assert(r == i); + i++; + } + } + return NULL; +} + +static void run_test(int range, int q_size) +{ + q_args_t args = { + .q = spsc_q_alloc(q_size, sizeof(int)), + .range = range, + }; + + pthread_t p, c; + int ret; + // start + ret = pthread_create(&p, NULL, producer, &args); + assert(ret == 0); + ret = pthread_create(&c, NULL, consumer, &args); + assert(ret == 0); + + ret = pthread_join(p, NULL); + assert(ret == 0); + ret = pthread_join(c, NULL); + assert(ret == 0); + // stop + // data/time => throughput + + spsc_q_free(&args.q); +} + +static void BM_spsc_q(benchmark::State &state) +{ + int range = 500000; + int q_size = state.range(0); + for (auto _ : state) + run_test(range, q_size); +} + +BENCHMARK(BM_spsc_q)->RangeMultiplier(2)->Range(10, 160); + +BENCHMARK_MAIN(); From b15d39421234e0562b68e043a5e8c55d3b22bfc8 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 15 Apr 2026 12:57:44 +0200 Subject: [PATCH 08/29] Improve spsc_q through atomics Because it is SPSC, atomic variables are enough to synchronize the two threads. Concretely put() is modified to ensure that read_idx is "acquired" so that the read index has been written, including memory, by the other thread. "Release" ensures that the write operation (including the memory to the buffer) is written before it is visible to the other thread in get() (which in turn "acquires" it). For more information, see also [1]. The rest of the library has been simplified to work only with put() and get(), reducing total code and the surface for possible bugs. This (and the previous) version has been tested with the threadSanitizer: TSAN_OPTIONS=halt_on_error=1 ./common/utils/ds/tests/test_spsc_q_perf On my machine, using Google Benchmark, I measure a considerable 5x speed improvement: $ /tmp/benchmark/tools/compare.py benchmarks pthread.json atomic.json Comparing pthread.json to atomic.json Benchmark Time CPU Time Old Time New CPU Old CPU New -------------------------------------------------------------------------------------------------------------- BM_spsc_q/10 -0.8201 -0.0740 266779912 47989020 52387 48512 BM_spsc_q/16 -0.8301 -0.0520 249656592 42428540 51462 48784 BM_spsc_q/32 -0.8003 -0.0841 230248798 45972155 53841 49311 BM_spsc_q/64 -0.7995 -0.0506 210429791 42199674 50690 48124 BM_spsc_q/128 -0.7930 -0.1101 205212273 42483155 52745 46936 BM_spsc_q/160 -0.7880 -0.1663 216644738 45938247 53400 44518 OVERALL_GEOMEAN -0.8057 -0.0904 0 0 0 0 [1] https://en.cppreference.com/w/c/atomic/memory_order.html Assisted-By: Claude:claude-sonnet-4-6 Signed-off-by: Robert Schmidt --- common/utils/ds/spsc_q.c | 48 +++++++++++----------------------------- common/utils/ds/spsc_q.h | 13 +++++++---- 2 files changed, 22 insertions(+), 39 deletions(-) diff --git a/common/utils/ds/spsc_q.c b/common/utils/ds/spsc_q.c index fdc97f00b8..6f05a19865 100644 --- a/common/utils/ds/spsc_q.c +++ b/common/utils/ds/spsc_q.c @@ -8,9 +8,6 @@ #include "spsc_q.h" -#define rb_lock(LOCK) if (pthread_mutex_lock(LOCK) != 0) abort() -#define rb_unlock(LOCK) if (pthread_mutex_unlock(LOCK) != 0) abort() - spsc_q_t spsc_q_alloc(size_t cnt, size_t elsiz) { /* internally, use one element more: the ringbuffer is full if @@ -20,51 +17,47 @@ spsc_q_t spsc_q_alloc(size_t cnt, size_t elsiz) cnt += 1; spsc_q_t rb = {.cnt = cnt, .elsiz = elsiz}; rb.buf = calloc(cnt, elsiz); - pthread_mutex_init(&rb.mtx, NULL); return rb; } void spsc_q_free(spsc_q_t *rb) { free(rb->buf); - pthread_mutex_destroy(&rb->mtx); memset(rb, 0, sizeof(*rb)); } bool spsc_q_put(spsc_q_t *rb, const void *src, size_t elsiz) { assert(elsiz == rb->elsiz); - rb_lock(&rb->mtx); - if ((rb->write_idx + 1) % rb->cnt == rb->read_idx) { - rb_unlock(&rb->mtx); + size_t w = atomic_load_explicit(&rb->write_idx, memory_order_relaxed); + size_t r = atomic_load_explicit(&rb->read_idx, memory_order_acquire); + if ((w + 1) % rb->cnt == r) { return false; } - uint8_t *bufpos = &rb->buf[rb->write_idx * rb->elsiz]; + uint8_t *bufpos = &rb->buf[w * rb->elsiz]; memcpy(bufpos, src, elsiz); - rb->write_idx = (rb->write_idx + 1) % rb->cnt; - rb_unlock(&rb->mtx); + atomic_store_explicit(&rb->write_idx, (w + 1) % rb->cnt, memory_order_release); return true; } bool spsc_q_get_if(spsc_q_t *rb, pred p, void *user, void *dest, size_t elsiz) { assert(elsiz == rb->elsiz); - rb_lock(&rb->mtx); - if (rb->write_idx == rb->read_idx) { - rb_unlock(&rb->mtx); + size_t w = atomic_load_explicit(&rb->write_idx, memory_order_acquire); + size_t r = atomic_load_explicit(&rb->read_idx, memory_order_relaxed); + if (w == r) { return false; } uint8_t *bufpos = &rb->buf[rb->read_idx * rb->elsiz]; if (!p(bufpos, user)) { - rb_unlock(&rb->mtx); return false; } - memcpy(dest, bufpos, elsiz); - rb->read_idx = (rb->read_idx + 1) % rb->cnt; - rb_unlock(&rb->mtx); + if (dest) + memcpy(dest, bufpos, elsiz); + atomic_store_explicit(&rb->read_idx, (r + 1) % rb->cnt, memory_order_release); return true; } @@ -82,31 +75,16 @@ bool spsc_q_get(spsc_q_t *rb, void *dest, size_t elsiz) int spsc_q_get_while(spsc_q_t *rb, pred p, void *user, void *dest, size_t elsiz, size_t max_len) { assert(elsiz == rb->elsiz); - rb_lock(&rb->mtx); size_t count = 0; - while (rb->write_idx != rb->read_idx && count < max_len) { - uint8_t *bufpos = &rb->buf[rb->read_idx * rb->elsiz]; - if (!p(bufpos, user)) - break; - memcpy(dest + count * elsiz, bufpos, elsiz); + while (count < max_len && spsc_q_get_if(rb, p, user, dest + elsiz * count, elsiz)) count++; - rb->read_idx = (rb->read_idx + 1) % rb->cnt; - } - rb_unlock(&rb->mtx); return count; } int spsc_q_drop_while(spsc_q_t *rb, pred p, void *user) { - rb_lock(&rb->mtx); size_t dropped = 0; - while (rb->write_idx != rb->read_idx) { - uint8_t *bufpos = &rb->buf[rb->read_idx * rb->elsiz]; - if (!p(bufpos, user)) - break; + while (spsc_q_get_if(rb, p, user, NULL, rb->elsiz)) dropped++; - rb->read_idx = (rb->read_idx + 1) % rb->cnt; - } - rb_unlock(&rb->mtx); return dropped; } diff --git a/common/utils/ds/spsc_q.h b/common/utils/ds/spsc_q.h index d00cb4b77d..7fd968b2f0 100644 --- a/common/utils/ds/spsc_q.h +++ b/common/utils/ds/spsc_q.h @@ -7,15 +7,20 @@ #include #include -#include +#include + +#if defined(__cplusplus) +// use atomic_size_t to help interworking with C++ +using std::atomic_size_t; +#endif typedef struct spsc_q { uint8_t *buf; size_t elsiz; size_t cnt; - size_t write_idx; - size_t read_idx; - pthread_mutex_t mtx; + + atomic_size_t write_idx; + atomic_size_t read_idx; } spsc_q_t; spsc_q_t spsc_q_alloc(size_t cnt, size_t elsiz); From 85b4057bde17f864deda55ea445fd48643f21a90 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 17 Mar 2026 08:42:59 +0100 Subject: [PATCH 09/29] Refactor PUCCH processing into its own function Signed-off-by: Robert Schmidt --- .../PHY/NR_TRANSPORT/nr_transport_proto.h | 4 +- openair1/PHY/NR_TRANSPORT/pucch_rx.c | 6 +-- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 52 +++++++++---------- 3 files changed, 29 insertions(+), 33 deletions(-) diff --git a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h index f7497a148a..eaf7fcbfb5 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h +++ b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h @@ -227,14 +227,14 @@ void nr_decode_pucch2(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_uci_pucch_pdu_format_2_3_4_t* uci_pdu, - nfapi_nr_pucch_pdu_t* pucch_pdu); + const nfapi_nr_pucch_pdu_t* pucch_pdu); void nr_decode_pucch0(PHY_VARS_gNB *gNB, c16_t **rxdataF, int frame, int slot, nfapi_nr_uci_pucch_pdu_format_0_1_t* uci_pdu, - nfapi_nr_pucch_pdu_t* pucch_pdu); + const nfapi_nr_pucch_pdu_t* pucch_pdu); #endif /*__NR_TRANSPORT__H__*/ diff --git a/openair1/PHY/NR_TRANSPORT/pucch_rx.c b/openair1/PHY/NR_TRANSPORT/pucch_rx.c index 8017fb0bc9..09af58141e 100644 --- a/openair1/PHY/NR_TRANSPORT/pucch_rx.c +++ b/openair1/PHY/NR_TRANSPORT/pucch_rx.c @@ -66,7 +66,7 @@ void nr_fill_pucch(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_pucch_pdu_t LOG_W(PHY, "PUCCH list is full\n"); } -int get_pucch0_cs_lut_index(PHY_VARS_gNB *gNB, nfapi_nr_pucch_pdu_t *pucch_pdu) +int get_pucch0_cs_lut_index(PHY_VARS_gNB *gNB, const nfapi_nr_pucch_pdu_t *pucch_pdu) { int i = 0; @@ -129,7 +129,7 @@ void nr_decode_pucch0(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_uci_pucch_pdu_format_0_1_t *uci_pdu, - nfapi_nr_pucch_pdu_t *pucch_pdu) + const nfapi_nr_pucch_pdu_t *pucch_pdu) { NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms; int soffset = (slot % RU_RX_SLOT_DEPTH) * frame_parms->symbols_per_slot * frame_parms->ofdm_symbol_size; @@ -1132,7 +1132,7 @@ void nr_decode_pucch2(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_uci_pucch_pdu_format_2_3_4_t *uci_pdu, - nfapi_nr_pucch_pdu_t *pucch_pdu) + const nfapi_nr_pucch_pdu_t *pucch_pdu) { NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms; // pucch_GroupHopping_t pucch_GroupHopping = pucch_pdu->group_hop_flag + (pucch_pdu->sequence_hop_flag<<1); diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index 7a5af5b070..11b8da6b73 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -989,6 +989,28 @@ void nr_srs_rx_procedures(PHY_VARS_gNB *gNB, } } +static void handle_pucch(PHY_VARS_gNB *gNB, c16_t **rxdataF, const NR_gNB_PUCCH_t *pucch, nfapi_nr_uci_t *uci) +{ + const nfapi_nr_pucch_pdu_t *pucch_pdu = &pucch->pucch_pdu; + + switch (pucch_pdu->format_type) { + case 0: + uci->pdu_type = NFAPI_NR_UCI_FORMAT_0_1_PDU_TYPE; + uci->pdu_size = sizeof(nfapi_nr_uci_pucch_pdu_format_0_1_t); + nfapi_nr_uci_pucch_pdu_format_0_1_t *uci_pdu_format0 = &uci->pucch_pdu_format_0_1; + nr_decode_pucch0(gNB, rxdataF, pucch->frame, pucch->slot, uci_pdu_format0, pucch_pdu); + break; + case 2: + uci->pdu_type = NFAPI_NR_UCI_FORMAT_2_3_4_PDU_TYPE; + uci->pdu_size = sizeof(nfapi_nr_uci_pucch_pdu_format_2_3_4_t); + nfapi_nr_uci_pucch_pdu_format_2_3_4_t *uci_pdu_format2 = &uci->pucch_pdu_format_2_3_4; + nr_decode_pucch2(gNB, rxdataF, pucch->frame, pucch->slot, uci_pdu_format2, pucch_pdu); + break; + default: + AssertFatal(1 == 0, "Only PUCCH formats 0 and 2 are currently supported\n"); + } +} + int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, NR_UL_IND_t *UL_INFO) { /* those variables to log T_GNB_PHY_PUCCH_PUSCH_IQ only when we try to decode */ @@ -1022,44 +1044,18 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N gNB_I0_measurements(gNB, slot_rx, first_symb, num_symb, rb_mask_ul); } - const int soffset = (slot_rx & 3) * frame_parms->symbols_per_slot * ofdm_symbol_size; start_meas(&gNB->phy_proc_rx); - for (int i = 0; i < gNB->max_nb_pucch; i++) { NR_gNB_PUCCH_t *pucch = &gNB->pucch[i]; if (!(pucch && pucch->active && pucch->frame == frame_rx && pucch->slot == slot_rx)) continue; c16_t **rxdataF = gNB->common_vars.rxdataF[pucch->beam_nb]; pucch_decode_done = 1; - nfapi_nr_pucch_pdu_t *pucch_pdu = &pucch->pucch_pdu; UL_INFO->uci_ind.uci_list = UL_INFO->uci_pdu_list; - nfapi_nr_uci_t *uci = UL_INFO->uci_ind.uci_list + UL_INFO->uci_ind.num_ucis; UL_INFO->uci_ind.sfn = frame_rx; UL_INFO->uci_ind.slot = slot_rx; - switch (pucch_pdu->format_type) { - case 0: - uci->pdu_type = NFAPI_NR_UCI_FORMAT_0_1_PDU_TYPE; - uci->pdu_size = sizeof(nfapi_nr_uci_pucch_pdu_format_0_1_t); - nfapi_nr_uci_pucch_pdu_format_0_1_t *uci_pdu_format0 = &uci->pucch_pdu_format_0_1; - int offset = - pucch_pdu->start_symbol_index * ofdm_symbol_size + (frame_parms->first_carrier_offset + pucch_pdu->prb_start * 12); - LOG_D(NR_PHY, - "frame %d, slot %d: PUCCH signal energy %d\n", - frame_rx, - slot_rx, - signal_energy_nodc(&rxdataF[0][soffset + offset], 12)); - nr_decode_pucch0(gNB, rxdataF, frame_rx, slot_rx, uci_pdu_format0, pucch_pdu); - break; - case 2: - uci->pdu_type = NFAPI_NR_UCI_FORMAT_2_3_4_PDU_TYPE; - uci->pdu_size = sizeof(nfapi_nr_uci_pucch_pdu_format_2_3_4_t); - nfapi_nr_uci_pucch_pdu_format_2_3_4_t *uci_pdu_format2 = &uci->pucch_pdu_format_2_3_4; - LOG_D(PHY, "%d.%d Calling nr_decode_pucch2\n", frame_rx, slot_rx); - nr_decode_pucch2(gNB, rxdataF, frame_rx, slot_rx, uci_pdu_format2, pucch_pdu); - break; - default: - AssertFatal(1 == 0, "Only PUCCH formats 0 and 2 are currently supported\n"); - } + nfapi_nr_uci_t *uci = UL_INFO->uci_ind.uci_list + UL_INFO->uci_ind.num_ucis; + handle_pucch(gNB, rxdataF, pucch, uci); UL_INFO->uci_ind.num_ucis += 1; pucch->active = false; } From e8e3f60e73b119888d250e5d6b3146d1122a41a8 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 8 Apr 2026 13:40:45 +0200 Subject: [PATCH 10/29] Use spsc_q for PUCCH processing Use the spsc queue to pass jobs from TX thread (scheduler) to RX thread (handling those jobs), reducing the amount spent searching for PUCCH jobs in an array, clarifying the design, and making it thread-safe. Signed-off-by: Robert Schmidt --- CMakeLists.txt | 2 +- openair1/PHY/INIT/nr_init.c | 9 +-- openair1/PHY/NR_TRANSPORT/pucch_rx.c | 52 +++++++-------- openair1/PHY/defs_gNB.h | 7 +- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 79 +++++++++++++---------- 5 files changed, 74 insertions(+), 75 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 665904c53f..6d15f78a29 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1070,7 +1070,7 @@ add_library(PHY_NR_COMMON ${PHY_NR_SRC_COMMON}) target_link_libraries(PHY_NR_COMMON PUBLIC UTIL) add_library(PHY_NR ${PHY_NR_SRC}) -target_link_libraries(PHY_NR nr_phy_common nr_common nr_fapi_p5 polar smallblock) +target_link_libraries(PHY_NR nr_phy_common nr_common nr_fapi_p5 polar smallblock ds) add_library(PHY_NR_NO_AVX_256 ${PHY_NR_SRC}) target_link_libraries(PHY_NR_NO_AVX_256 nr_phy_common nr_common) diff --git a/openair1/PHY/INIT/nr_init.c b/openair1/PHY/INIT/nr_init.c index 59bdd7012d..8a2a794fbd 100644 --- a/openair1/PHY/INIT/nr_init.c +++ b/openair1/PHY/INIT/nr_init.c @@ -437,14 +437,11 @@ void init_nr_transport(PHY_VARS_gNB *gNB) else buffer_ul_slots = (nb_ul_slots_period < slot_ahead) ? nb_ul_slots_period : slot_ahead; - gNB->max_nb_pucch = buffer_ul_slots ? MAX_MOBILES_PER_GNB * buffer_ul_slots : 1; gNB->max_nb_pusch = buffer_ul_slots ? MAX_MOBILES_PER_GNB * buffer_ul_slots : 1; gNB->max_nb_srs = buffer_ul_slots ? buffer_ul_slots << 1 : 1; // assuming at most 2 SRS per slot - gNB->pucch = (NR_gNB_PUCCH_t *)malloc16(gNB->max_nb_pucch * sizeof(NR_gNB_PUCCH_t)); - for (int i = 0; i < gNB->max_nb_pucch; i++) { - memset(&gNB->pucch[i], 0, sizeof(gNB->pucch[i])); - } + int max_nb_pucch = buffer_ul_slots ? MAX_MOBILES_PER_GNB * buffer_ul_slots : 1; + gNB->pucch_queue = spsc_q_alloc(max_nb_pucch, sizeof(NR_gNB_PUCCH_job_t)); gNB->srs = (NR_gNB_SRS_t *)malloc16(gNB->max_nb_srs * sizeof(NR_gNB_SRS_t)); for (int i = 0; i < gNB->max_nb_srs; i++) @@ -465,7 +462,7 @@ void reset_nr_transport(PHY_VARS_gNB *gNB) { const NR_DL_FRAME_PARMS *fp = &gNB->frame_parms; - free(gNB->pucch); + spsc_q_free(&gNB->pucch_queue); free(gNB->srs); for (int i = 0; i < gNB->max_nb_pusch; i++) diff --git a/openair1/PHY/NR_TRANSPORT/pucch_rx.c b/openair1/PHY/NR_TRANSPORT/pucch_rx.c index 09af58141e..fd6bd8ea42 100644 --- a/openair1/PHY/NR_TRANSPORT/pucch_rx.c +++ b/openair1/PHY/NR_TRANSPORT/pucch_rx.c @@ -33,37 +33,29 @@ //#define DEBUG_NR_PUCCH_RX 1 void nr_fill_pucch(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_pucch_pdu_t *pucch_pdu) { - for (int i = 0; i < gNB->max_nb_pucch; i++) { - NR_gNB_PUCCH_t *pucch = &gNB->pucch[i]; - if (pucch->active == false) { - pucch->frame = frame; - pucch->slot = slot; - pucch->active = true; - pucch->beam_nb = 0; - if (gNB->common_vars.beam_id) { - int fapi_beam_idx = pucch_pdu->beamforming.prgs_list[0].dig_bf_interface_list[0].beam_idx; - int bitmap = SL_to_bitmap(pucch_pdu->start_symbol_index, pucch_pdu->nr_of_symbols); - pucch->beam_nb = beam_index_allocation(gNB->enable_analog_das, - fapi_beam_idx, - &gNB->common_vars, - slot, - gNB->frame_parms.symbols_per_slot, - bitmap); - } - memcpy((void *)&pucch->pucch_pdu, (void *)pucch_pdu, sizeof(nfapi_nr_pucch_pdu_t)); - LOG_D(PHY, - "Programming PUCCH[%d] for %d.%d, format %d, nb_harq %d, nb_sr %d, nb_csi %d\n", - i, - pucch->frame, - pucch->slot, - pucch->pucch_pdu.format_type, - pucch->pucch_pdu.bit_len_harq, - pucch->pucch_pdu.sr_flag, - pucch->pucch_pdu.bit_len_csi_part1); - return; - } + LOG_D(PHY, + "%4d.%2d UE %04x Programming PUCCH format %d, nb_harq %d, nb_sr %d, nb_csi %d\n", + frame, + slot, + pucch_pdu->rnti, + pucch_pdu->format_type, + pucch_pdu->bit_len_harq, + pucch_pdu->sr_flag, + pucch_pdu->bit_len_csi_part1); + NR_gNB_PUCCH_job_t pucch = {.frame = frame, .slot = slot, .pucch_pdu = *pucch_pdu}; + if (gNB->common_vars.beam_id) { + int fapi_beam_idx = pucch_pdu->beamforming.prgs_list[0].dig_bf_interface_list[0].beam_idx; + int bitmap = SL_to_bitmap(pucch_pdu->start_symbol_index, pucch_pdu->nr_of_symbols); + pucch.beam_nb = beam_index_allocation(gNB->enable_analog_das, + fapi_beam_idx, + &gNB->common_vars, + slot, + gNB->frame_parms.symbols_per_slot, + bitmap); } - LOG_W(PHY, "PUCCH list is full\n"); + bool found = spsc_q_put(&gNB->pucch_queue, &pucch, sizeof(pucch)); + if (!found) + LOG_W(NR_PHY, "PUCCH list is full: dropping PUCCH UE %04x\n", pucch_pdu->rnti); } int get_pucch0_cs_lut_index(PHY_VARS_gNB *gNB, const nfapi_nr_pucch_pdu_t *pucch_pdu) diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index a6fb9d8890..9657cef20d 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -19,6 +19,7 @@ #include "nfapi_nr_interface_scf.h" #include "common/utils/threadPool/task_ans.h" #include "openair1/PHY/defs_RU.h" +#include "common/utils/ds/spsc_q.h" #define MAX_NUM_RU_PER_gNB 8 #define MAX_PUCCH0_NID 8 @@ -190,7 +191,6 @@ typedef struct { } NR_gNB_ULSCH_t; typedef struct { - bool active; // identifier for concurrent beams int beam_nb; /// Frame where current PUCCH pdu was sent @@ -199,7 +199,7 @@ typedef struct { uint32_t slot; /// ULSCH PDU nfapi_nr_pucch_pdu_t pucch_pdu; -} NR_gNB_PUCCH_t; +} NR_gNB_PUCCH_job_t; typedef struct { bool active; @@ -369,7 +369,6 @@ typedef struct PHY_VARS_gNB_s { nfapi_nr_ul_tti_request_t UL_tti_req; - int max_nb_pucch; int max_nb_srs; int max_nb_pdsch; int max_nb_pusch; @@ -380,7 +379,7 @@ typedef struct PHY_VARS_gNB_s { NR_gNB_DLSCH_t *dlsch; NR_gNB_PRS prs_vars; NR_gNB_PUSCH *pusch_vars; - NR_gNB_PUCCH_t *pucch; + spsc_q_t pucch_queue; NR_gNB_SRS_t *srs; NR_gNB_ULSCH_t *ulsch; NR_gNB_PHY_STATS_t phy_stats[MAX_MOBILES_PER_GNB]; diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index 11b8da6b73..1f28622231 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -2,6 +2,7 @@ * SPDX-License-Identifier: LicenseRef-CSSL-1.0 */ +#include "common/utils/fsn.h" #include "PHY/defs_gNB.h" #include "sched_nr.h" #include "PHY/NR_TRANSPORT/nr_transport_proto.h" @@ -613,10 +614,11 @@ static void nr_fill_indication(PHY_VARS_gNB *gNB, // Function to fill UL RB mask to be used for N0 measurements static void fill_ul_rb_mask(PHY_VARS_gNB *gNB, - int frame_rx, - int slot_rx, + fsn_t now, uint32_t rb_mask_ul[14][9], - nfapi_nr_max_num_of_symbol_per_slot_t *slot_conf) + nfapi_nr_max_num_of_symbol_per_slot_t *slot_conf, + const NR_gNB_PUCCH_job_t *pucch, + int n_pucch) { for (int symbol = 0; symbol < 14; symbol++) { for (int m = 0; m < 9; m++) { @@ -630,20 +632,9 @@ static void fill_ul_rb_mask(PHY_VARS_gNB *gNB, } } - for (int i = 0; i < gNB->max_nb_pucch; i++){ - const NR_gNB_PUCCH_t *pucch = gNB->pucch + i; - if (!(pucch && pucch->active && pucch->frame == frame_rx && pucch->slot == slot_rx)) - continue; - const nfapi_nr_pucch_pdu_t *pucch_pdu = &pucch->pucch_pdu; + for (int i = 0; i < n_pucch; ++i) { + const nfapi_nr_pucch_pdu_t *pucch_pdu = &pucch[i].pucch_pdu; const int start = pucch_pdu->start_symbol_index; - LOG_D(PHY, - "%d.%d pucch %d : start_symbol %d, nb_symbols %d, prb_size %d\n", - frame_rx, - slot_rx, - i, - start, - pucch_pdu->nr_of_symbols, - pucch_pdu->prb_size); for (int symbol = start; symbol < (start + pucch_pdu->nr_of_symbols); symbol++) { if (gNB->frame_parms.frame_type == FDD || (gNB->frame_parms.frame_type == TDD && slot_conf[symbol].slot_config.value == 1)) { for (int rb = 0; rb < pucch_pdu->prb_size; rb++) { @@ -661,7 +652,7 @@ static void fill_ul_rb_mask(PHY_VARS_gNB *gNB, NR_gNB_ULSCH_t *ulsch = &gNB->ulsch[ULSCH_id]; NR_UL_gNB_HARQ_t *ulsch_harq = ulsch->harq_process; AssertFatal(ulsch_harq != NULL, "harq_pid %d is not allocated\n", ulsch->harq_pid); - if (!(ulsch->active && ulsch->frame == frame_rx && ulsch->slot == slot_rx && !ulsch->handled)) + if (!(ulsch->active && ulsch->frame == now.f && ulsch->slot == now.s && !ulsch->handled)) continue; uint8_t symbol_start = ulsch_harq->ulsch_pdu.start_symbol_index; uint8_t symbol_end = symbol_start + ulsch_harq->ulsch_pdu.nr_of_symbols; @@ -678,7 +669,7 @@ static void fill_ul_rb_mask(PHY_VARS_gNB *gNB, for (int i = 0; i < gNB->max_nb_srs; i++) { NR_gNB_SRS_t *srs = &gNB->srs[i]; - if (!(srs && srs->active && srs->frame == frame_rx && srs->slot == slot_rx)) + if (!(srs && srs->active && srs->frame == now.f && srs->slot == now.s)) continue; nfapi_nr_srs_pdu_t *srs_pdu = &srs->srs_pdu; const uint8_t l0 = gNB->frame_parms.symbols_per_slot - 1 - srs_pdu->time_start_position; @@ -989,7 +980,7 @@ void nr_srs_rx_procedures(PHY_VARS_gNB *gNB, } } -static void handle_pucch(PHY_VARS_gNB *gNB, c16_t **rxdataF, const NR_gNB_PUCCH_t *pucch, nfapi_nr_uci_t *uci) +static void handle_pucch(PHY_VARS_gNB *gNB, c16_t **rxdataF, const NR_gNB_PUCCH_job_t *pucch, nfapi_nr_uci_t *uci) { const nfapi_nr_pucch_pdu_t *pucch_pdu = &pucch->pucch_pdu; @@ -1011,16 +1002,41 @@ static void handle_pucch(PHY_VARS_gNB *gNB, c16_t **rxdataF, const NR_gNB_PUCCH_ } } +static bool drop_old_pucch(const void *data, void *user) +{ + const NR_gNB_PUCCH_job_t *pucch = data; + const fsn_t *now = user; + const fsn_t t = {pucch->frame, pucch->slot, now->mu}; + bool drop = fsn_in_the_past(t, *now); + if (drop) + LOG_E(NR_PHY, "%4d.%2d PUCCH job for UE %04x is in the past (%4d.%2d)\n", now->f, now->s, pucch->pucch_pdu.rnti, t.f, t.s); + return drop; +} + +static bool get_current_pucch(const void *data, void *user) +{ + const NR_gNB_PUCCH_job_t *pucch = data; + const fsn_t *now = user; + const fsn_t t = {pucch->frame, pucch->slot, now->mu}; + return fsn_equal(t, *now); +} + int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, NR_UL_IND_t *UL_INFO) { /* those variables to log T_GNB_PHY_PUCCH_PUSCH_IQ only when we try to decode */ - int pucch_decode_done = 0; int pusch_decode_done = 0; int pusch_DTX = 0; const NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms; const uint16_t ofdm_symbol_size = frame_parms->ofdm_symbol_size; const uint8_t nb_antennas_rx = frame_parms->nb_antennas_rx; + + fsn_t now = {frame_rx, slot_rx, frame_parms->numerology_index}; + + spsc_q_drop_while(&gNB->pucch_queue, drop_old_pucch, &now); + NR_gNB_PUCCH_job_t pucch[MAX_NUM_NR_UCI_PDUS]; + int n_pucch = spsc_q_get_while(&gNB->pucch_queue, get_current_pucch, &now, pucch, sizeof(*pucch), MAX_NUM_NR_UCI_PDUS); + LOG_D(PHY,"phy_procedures_gNB_uespec_RX frame %d, slot %d\n",frame_rx,slot_rx); { // Mask of occupied RBs, per symbol and PRB @@ -1028,7 +1044,7 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N nfapi_nr_max_num_of_symbol_per_slot_t *slot_conf = NULL; if (frame_parms->frame_type == TDD) slot_conf = gNB->gNB_config.tdd_table.max_tdd_periodicity_list[slot_rx].max_num_of_symbol_per_slot_list; - fill_ul_rb_mask(gNB, frame_rx, slot_rx, rb_mask_ul, slot_conf); + fill_ul_rb_mask(gNB, now, rb_mask_ul, slot_conf, pucch, n_pucch); int first_symb = 0, num_symb = 0; if (frame_parms->frame_type == TDD) @@ -1045,19 +1061,14 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N } start_meas(&gNB->phy_proc_rx); - for (int i = 0; i < gNB->max_nb_pucch; i++) { - NR_gNB_PUCCH_t *pucch = &gNB->pucch[i]; - if (!(pucch && pucch->active && pucch->frame == frame_rx && pucch->slot == slot_rx)) - continue; + UL_INFO->uci_ind.uci_list = UL_INFO->uci_pdu_list; + UL_INFO->uci_ind.sfn = frame_rx; + UL_INFO->uci_ind.slot = slot_rx; + UL_INFO->uci_ind.num_ucis = n_pucch; + nfapi_nr_uci_t *uci = UL_INFO->uci_ind.uci_list; + for (int i = 0; i < n_pucch; ++i) { c16_t **rxdataF = gNB->common_vars.rxdataF[pucch->beam_nb]; - pucch_decode_done = 1; - UL_INFO->uci_ind.uci_list = UL_INFO->uci_pdu_list; - UL_INFO->uci_ind.sfn = frame_rx; - UL_INFO->uci_ind.slot = slot_rx; - nfapi_nr_uci_t *uci = UL_INFO->uci_ind.uci_list + UL_INFO->uci_ind.num_ucis; - handle_pucch(gNB, rxdataF, pucch, uci); - UL_INFO->uci_ind.num_ucis += 1; - pucch->active = false; + handle_pucch(gNB, rxdataF, &pucch[i], uci++); } UL_INFO->crc_ind.sfn = frame_rx; @@ -1377,7 +1388,7 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N stop_meas(&gNB->phy_proc_rx); - if (pucch_decode_done || pusch_decode_done) { + if (n_pucch > 0 || pusch_decode_done) { T(T_GNB_PHY_PUCCH_PUSCH_IQ, T_INT(frame_rx), T_INT(slot_rx), From 1aed7b5802a1e5d217ea22e60ec5db0274083129 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 24 Nov 2025 11:59:00 +0100 Subject: [PATCH 11/29] Cleanly pass pusch_vars to nr_pusch_channel_estimation() Pass pointer to data. This necessitates to move delay from NR_gNB_ULSCH_t (ULSCH data, including transport channels etc) to NR_gNB_PUSCH (lower-PHY PUSCH data): 1. This allows to pass a single pointer to nr_pusch_channel_estimation() 2. To me, delay estimation seems to be a lower-PHY calculation, so it fits better anyway. Signed-off-by: Robert Schmidt --- openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c | 6 +++--- openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h | 4 ++-- openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c | 2 +- openair1/PHY/defs_gNB.h | 3 ++- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 2 +- openair1/SIMULATION/NR_PHY/ulsim.c | 8 ++++---- 6 files changed, 13 insertions(+), 12 deletions(-) diff --git a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c index 4d70db6f02..68196e78c7 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c +++ b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c @@ -452,7 +452,7 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, int nl, unsigned short p, unsigned char symbol, - int ul_id, + NR_gNB_PUSCH *pusch_vars, int beam_nb, unsigned short bwp_start_subcarrier, nfapi_nr_pusch_pdu_t *pusch_pdu, @@ -534,7 +534,7 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, int nest_count = 0; uint64_t noise_amp2 = 0; - delay_t *delay = &gNB->ulsch[ul_id].delay; + delay_t *delay = &pusch_vars->delay; memset(delay, 0, sizeof(*delay)); int nb_antennas_rx = fp->nb_antennas_rx; @@ -579,7 +579,7 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, rdata->delay = &delay_arr[rdata->aarx]; rdata->beam_nb = beam_nb; rdata->frame_parms = fp; - rdata->pusch_vars = &gNB->pusch_vars[ul_id]; + rdata->pusch_vars = pusch_vars; rdata->chest_freq = gNB->chest_freq; rdata->rxdataF = gNB->common_vars.rxdataF; rdata->scope = gNB->scopeData; diff --git a/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h b/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h index 540a04ec52..57bac0dccc 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h +++ b/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h @@ -17,7 +17,7 @@ \param Ns slot number (0..19) \param p \param symbol symbol within slot -\param ul_id +\param pusch_vars lower-phy PUSCH info \param bwp_start_subcarrier first allocated subcarrier \param pusch_pdu \param max_ch maximum value of estimated channel @@ -29,7 +29,7 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, int nl, unsigned short p, unsigned char symbol, - int ul_id, + NR_gNB_PUSCH *pusch_vars, int beam_nb, unsigned short bwp_start_subcarrier, nfapi_nr_pusch_pdu_t *pusch_pdu, diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c index 82d6b0d99d..1984c4b03c 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c @@ -1159,7 +1159,7 @@ int nr_rx_pusch_tp(PHY_VARS_gNB *gNB, uint8_t ulsch_id, uint32_t frame, uint8_t nl, get_dmrs_port(nl, rel15_ul->dmrs_ports), symbol, - ulsch_id, + pusch_vars, beam_nb, bwp_start_subcarrier, rel15_ul, diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index 9657cef20d..bd611bbe8f 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -187,7 +187,6 @@ typedef struct { bool active; /// Flag to indicate that the UL configuration has been handled. Used to remove a stale ULSCH when frame wraps around uint8_t handled; - delay_t delay; } NR_gNB_ULSCH_t; typedef struct { @@ -275,6 +274,8 @@ typedef struct { int llr_offset[14]; /// flag to indicate DTX on reception int DTX; + /// delay estimation + delay_t delay; } NR_gNB_PUSCH; /// Context data structure for RX/TX portion of slot processing diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index 1f28622231..7f6ae103c5 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -542,7 +542,7 @@ static void nr_fill_indication(PHY_VARS_gNB *gNB, nfapi_nr_pusch_pdu_t *pusch_pdu = &harq_process->ulsch_pdu; // Get estimated timing advance for MAC - const int sync_pos = gNB->ulsch[ULSCH_id].delay.est_delay; + const int sync_pos = pusch->delay.est_delay; // scale the 16 factor in N_TA calculation in 38.213 section 4.2 according to the used FFT size uint16_t bw_scaling = 16 * gNB->frame_parms.ofdm_symbol_size / 2048; diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c index 0d432bfdd0..011b7ee680 100644 --- a/openair1/SIMULATION/NR_PHY/ulsim.c +++ b/openair1/SIMULATION/NR_PHY/ulsim.c @@ -1208,6 +1208,7 @@ int main(int argc, char *argv[]) uint8_t round = 0; crc_status = 1; errors_decoding = 0; + NR_gNB_PUSCH *pusch_vars = &gNB->pusch_vars[UE_id]; while (round < max_rounds && crc_status && !stop) { @@ -1594,7 +1595,6 @@ int main(int argc, char *argv[]) } } - NR_gNB_PUSCH *pusch_vars = &gNB->pusch_vars[UE_id]; if (n_trials == 1 && round == 0) { __attribute__((unused)) int off = ((nb_rb & 1) == 1) ? 4 : 0; @@ -1742,9 +1742,9 @@ int main(int argc, char *argv[]) if (!crc_status) effRate += ((double)TBS) / (double)round; - sum_pusch_delay += ulsch_gNB->delay.est_delay; - min_pusch_delay = min(ulsch_gNB->delay.est_delay, min_pusch_delay); - max_pusch_delay = max(ulsch_gNB->delay.est_delay, max_pusch_delay); + sum_pusch_delay += pusch_vars->delay.est_delay; + min_pusch_delay = min(pusch_vars->delay.est_delay, min_pusch_delay); + max_pusch_delay = max(pusch_vars->delay.est_delay, max_pusch_delay); delay_pusch_est_count++; if (do_SRS == 1) { From 0161568d69ff7394f3b6e46b750285ef468b2beb Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 24 Nov 2025 12:21:05 +0100 Subject: [PATCH 12/29] Apply const correctness and avoid pointers Amidst all these unnecessary pointers, the pointer on pusch_vars->ptrs_symbols is necessary to reset the PTRS number of symbols. For clarity, remove this pointer and use pusch_vars->ptrs_symbols directly. Signed-off-by: Robert Schmidt --- .../NR_ESTIMATION/nr_ul_channel_estimation.c | 54 +++++++++---------- openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h | 4 +- .../PHY/NR_TRANSPORT/nr_ulsch_demodulation.c | 12 ++--- 3 files changed, 32 insertions(+), 38 deletions(-) diff --git a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c index 68196e78c7..0119023882 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c +++ b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c @@ -34,7 +34,7 @@ typedef struct puschAntennaProc_s { int aarx; int beam_nb; int numAntennas; - nfapi_nr_pusch_pdu_t *pusch_pdu; + const nfapi_nr_pusch_pdu_t *pusch_pdu; int *max_ch; c16_t *pilot; int *nest_count; @@ -82,7 +82,7 @@ static void nr_pusch_antenna_processing(void *arg) int aarx = rdata->aarx; int numAntennas = rdata->numAntennas; unsigned short bwp_start_subcarrier = rdata->bwp_start_subcarrier; - nfapi_nr_pusch_pdu_t *pusch_pdu = rdata->pusch_pdu; + const nfapi_nr_pusch_pdu_t *pusch_pdu = rdata->pusch_pdu; int *max_ch = rdata->max_ch; c16_t *pilot = rdata->pilot; uint64_t noise_amp2 = *(rdata->noise_amp2); @@ -455,7 +455,7 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, NR_gNB_PUSCH *pusch_vars, int beam_nb, unsigned short bwp_start_subcarrier, - nfapi_nr_pusch_pdu_t *pusch_pdu, + const nfapi_nr_pusch_pdu_t *pusch_pdu, int *max_ch, uint32_t *nvar, c16_t *pusch_dmrs_slot_mem, @@ -646,7 +646,7 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, // #define DEBUG_UL_PTRS void nr_pusch_ptrs_processing(PHY_VARS_gNB *gNB, NR_DL_FRAME_PARMS *frame_parms, - nfapi_nr_pusch_pdu_t *rel15_ul, + const nfapi_nr_pusch_pdu_t *rel15_ul, uint8_t ulsch_id, uint8_t nr_tti_rx, unsigned char symbol, @@ -656,15 +656,13 @@ void nr_pusch_ptrs_processing(PHY_VARS_gNB *gNB, int32_t *ptrs_re_symbol = NULL; int8_t ret = 0; uint8_t symbInSlot = rel15_ul->start_symbol_index + rel15_ul->nr_of_symbols; - uint8_t *startSymbIndex = &rel15_ul->start_symbol_index; - uint8_t *nbSymb = &rel15_ul->nr_of_symbols; - uint8_t *L_ptrs = &rel15_ul->pusch_ptrs.ptrs_time_density; - uint8_t *K_ptrs = &rel15_ul->pusch_ptrs.ptrs_freq_density; - uint16_t *dmrsSymbPos = &rel15_ul->ul_dmrs_symb_pos; - uint16_t *ptrsSymbPos = &pusch_vars->ptrs_symbols; - uint8_t *ptrsSymbIdx = &pusch_vars->ptrs_symbol_index; - uint16_t *nb_rb = &rel15_ul->rb_size; - uint8_t *ptrsReOffset = &rel15_ul->pusch_ptrs.ptrs_ports_list[0].ptrs_re_offset; + uint8_t startSymbIndex = rel15_ul->start_symbol_index; + uint8_t nbSymb = rel15_ul->nr_of_symbols; + uint8_t L_ptrs = rel15_ul->pusch_ptrs.ptrs_time_density; + uint8_t K_ptrs = rel15_ul->pusch_ptrs.ptrs_freq_density; + uint16_t dmrsSymbPos = rel15_ul->ul_dmrs_symb_pos; + uint16_t nb_rb = rel15_ul->rb_size; + uint8_t ptrsReOffset = rel15_ul->pusch_ptrs.ptrs_ports_list[0].ptrs_re_offset; /* loop over antennas */ for (int aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) { @@ -673,7 +671,7 @@ void nr_pusch_ptrs_processing(PHY_VARS_gNB *gNB, *ptrs_re_symbol = 0; phase_per_symbol[symbol].i = 0; /* set DMRS estimates to 0 angle with magnitude 1 */ - if (is_dmrs_symbol(symbol, *dmrsSymbPos)) { + if (is_dmrs_symbol(symbol, dmrsSymbPos)) { /* set DMRS real estimation to 32767 */ phase_per_symbol[symbol].r = INT16_MAX; // 32767 #ifdef DEBUG_UL_PTRS @@ -683,17 +681,13 @@ void nr_pusch_ptrs_processing(PHY_VARS_gNB *gNB, phase_per_symbol[symbol].r = 0; } - if (symbol == *startSymbIndex) { - *ptrsSymbPos = 0; - set_ptrs_symb_idx(ptrsSymbPos, *nbSymb, *startSymbIndex, 1 << *L_ptrs, *dmrsSymbPos); + if (symbol == startSymbIndex) { + pusch_vars->ptrs_symbols = 0; + set_ptrs_symb_idx(&pusch_vars->ptrs_symbols, nbSymb, startSymbIndex, 1 << L_ptrs, dmrsSymbPos); } - /* if not PTRS symbol set current ptrs symbol index to zero*/ - *ptrsSymbIdx = 0; - /* Check if current symbol contains PTRS */ - if (is_ptrs_symbol(symbol, *ptrsSymbPos)) { - *ptrsSymbIdx = symbol; + if (is_ptrs_symbol(symbol, pusch_vars->ptrs_symbols)) { /*------------------------------------------------------------------------------------------------------- */ /* 1) Estimate common phase error per PTRS symbol */ /*------------------------------------------------------------------------------------------------------- */ @@ -703,9 +697,9 @@ void nr_pusch_ptrs_processing(PHY_VARS_gNB *gNB, rel15_ul->scid, nr_tti_rx, symbol); - nr_ptrs_cpe_estimation(*K_ptrs, - *ptrsReOffset, - *nb_rb, + nr_ptrs_cpe_estimation(K_ptrs, + ptrsReOffset, + nb_rb, rel15_ul->rnti, frame_parms->ofdm_symbol_size, &pusch_vars->rxdataF_comp[aarx][(symbol * nb_re_pusch)], @@ -720,8 +714,8 @@ void nr_pusch_ptrs_processing(PHY_VARS_gNB *gNB, /* 2) Interpolate PTRS estimated value in TD */ /*------------------------------------------------------------------------------------------------------- */ /* If L-PTRS is > 0 then we need interpolation */ - if (*L_ptrs > 0) { - ret = nr_ptrs_process_slot(*dmrsSymbPos, *ptrsSymbPos, (int16_t *)phase_per_symbol, *startSymbIndex, *nbSymb); + if (L_ptrs > 0) { + ret = nr_ptrs_process_slot(dmrsSymbPos, pusch_vars->ptrs_symbols, (int16_t *)phase_per_symbol, startSymbIndex, nbSymb); if (ret != 0) { LOG_W(PHY, "[PTRS] Compensation is skipped due to error in PTRS slot processing !!\n"); } @@ -730,17 +724,17 @@ void nr_pusch_ptrs_processing(PHY_VARS_gNB *gNB, /*------------------------------------------------------------------------------------------------------- */ /* 3) Compensated DMRS based estimated signal with PTRS estimation */ /*--------------------------------------------------------------------------------------------------------*/ - for (uint8_t i = *startSymbIndex; i < symbInSlot; i++) { + for (uint8_t i = startSymbIndex; i < symbInSlot; i++) { /* DMRS Symbol has 0 phase so no need to rotate the respective symbol */ /* Skip rotation if the slot processing is wrong */ - if ((!is_dmrs_symbol(i, *dmrsSymbPos)) && (ret == 0)) { + if ((!is_dmrs_symbol(i, dmrsSymbPos)) && (ret == 0)) { #ifdef DEBUG_UL_PTRS printf("[PHY][UL][PTRS]: Rotate Symbol %2d with %d + j* %d\n", i, phase_per_symbol[i].r, phase_per_symbol[i].i); #endif rotate_cpx_vector(&pusch_vars->rxdataF_comp[aarx][i * nb_re_pusch], &phase_per_symbol[i], &pusch_vars->rxdataF_comp[aarx][i * nb_re_pusch], - ((*nb_rb) * NR_NB_SC_PER_RB), + (nb_rb * NR_NB_SC_PER_RB), 15); } // if not DMRS Symbol } // symbol loop diff --git a/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h b/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h index 57bac0dccc..b5ec042419 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h +++ b/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h @@ -32,7 +32,7 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, NR_gNB_PUSCH *pusch_vars, int beam_nb, unsigned short bwp_start_subcarrier, - nfapi_nr_pusch_pdu_t *pusch_pdu, + const nfapi_nr_pusch_pdu_t *pusch_pdu, int *max_ch, uint32_t *nvar, c16_t *pusch_dmrs_slot_mem, @@ -52,7 +52,7 @@ void nr_est_srs_timing_advance_offset(uint16_t ofdm_symbol_size, void nr_pusch_ptrs_processing(PHY_VARS_gNB *gNB, NR_DL_FRAME_PARMS *frame_parms, - nfapi_nr_pusch_pdu_t *rel15_ul, + const nfapi_nr_pusch_pdu_t *rel15_ul, uint8_t ulsch_id, uint8_t nr_tti_rx, unsigned char symbol, diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c index 1984c4b03c..d9a79ac84d 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c @@ -80,7 +80,7 @@ static void nr_ulsch_extract_rbs(c16_t* const rxdataF, int rxoffset, int choffset, int is_dmrs_symbol, - nfapi_nr_pusch_pdu_t *pusch_pdu, + const nfapi_nr_pusch_pdu_t *pusch_pdu, NR_DL_FRAME_PARMS *frame_parms) { uint8_t delta = 0; @@ -160,7 +160,7 @@ static void nr_ulsch_extract_rbs(c16_t* const rxdataF, } } -static int get_nb_re_pusch (NR_DL_FRAME_PARMS *frame_parms, nfapi_nr_pusch_pdu_t *rel15_ul,int symbol) +static int get_nb_re_pusch (NR_DL_FRAME_PARMS *frame_parms, const nfapi_nr_pusch_pdu_t *rel15_ul, int symbol) { uint8_t dmrs_symbol_flag = (rel15_ul->ul_dmrs_symb_pos >> symbol) & 0x01; if (dmrs_symbol_flag == 1) { @@ -186,7 +186,7 @@ static void nr_ulsch_channel_compensation(uint32_t buffer_length, c16_t **rxComp, int nb_layers, c16_t rho[][nb_layers][buffer_length], - nfapi_nr_pusch_pdu_t *rel15_ul, + const nfapi_nr_pusch_pdu_t *rel15_ul, uint32_t symbol, uint32_t output_shift) { @@ -861,7 +861,7 @@ static void inner_rx(PHY_VARS_gNB *gNB, int slot, NR_DL_FRAME_PARMS *frame_parms, NR_gNB_PUSCH *pusch_vars, - nfapi_nr_pusch_pdu_t *rel15_ul, + const nfapi_nr_pusch_pdu_t *rel15_ul, c16_t **rxF, int16_t **llr, int soffset, @@ -1003,7 +1003,7 @@ static void inner_rx(PHY_VARS_gNB *gNB, typedef struct puschSymbolProc_s { PHY_VARS_gNB *gNB; NR_DL_FRAME_PARMS *frame_parms; - nfapi_nr_pusch_pdu_t *rel15_ul; + const nfapi_nr_pusch_pdu_t *rel15_ul; int ulsch_id; int slot; int startSymbol; @@ -1023,7 +1023,7 @@ static void nr_pusch_symbol_processing(void *arg) PHY_VARS_gNB *gNB = rdata->gNB; NR_DL_FRAME_PARMS *frame_parms = rdata->frame_parms; - nfapi_nr_pusch_pdu_t *rel15_ul = rdata->rel15_ul; + const nfapi_nr_pusch_pdu_t *rel15_ul = rdata->rel15_ul; int ulsch_id = rdata->ulsch_id; int slot = rdata->slot; NR_gNB_PUSCH *pusch_vars = &gNB->pusch_vars[ulsch_id]; From c99e5642b53fd02f5763e4d98b3fb3e8c3dc8d21 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 24 Nov 2025 12:23:15 +0100 Subject: [PATCH 13/29] Pass pointer directly Signed-off-by: Robert Schmidt --- .../NR_ESTIMATION/nr_ul_channel_estimation.c | 5 ++-- openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h | 2 +- .../PHY/NR_TRANSPORT/nr_transport_proto.h | 12 ++++++-- .../PHY/NR_TRANSPORT/nr_ulsch_demodulation.c | 29 ++++++++++--------- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 3 +- 5 files changed, 30 insertions(+), 21 deletions(-) diff --git a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c index 0119023882..a5190997fa 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c +++ b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c @@ -628,7 +628,7 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, * * PARAMETERS : gNB : gNB data structure * rel15_ul : UL parameters - * UE_id : UE ID + * pusch_vars : PUSCH data * nr_tti_rx : slot rx TTI * dmrs_symbol_flag: DMRS Symbol Flag * symbol : OFDM Symbol @@ -647,12 +647,11 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, void nr_pusch_ptrs_processing(PHY_VARS_gNB *gNB, NR_DL_FRAME_PARMS *frame_parms, const nfapi_nr_pusch_pdu_t *rel15_ul, - uint8_t ulsch_id, + NR_gNB_PUSCH *pusch_vars, uint8_t nr_tti_rx, unsigned char symbol, uint32_t nb_re_pusch) { - NR_gNB_PUSCH *pusch_vars = &gNB->pusch_vars[ulsch_id]; int32_t *ptrs_re_symbol = NULL; int8_t ret = 0; uint8_t symbInSlot = rel15_ul->start_symbol_index + rel15_ul->nr_of_symbols; diff --git a/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h b/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h index b5ec042419..25b9c27414 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h +++ b/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h @@ -53,7 +53,7 @@ void nr_est_srs_timing_advance_offset(uint16_t ofdm_symbol_size, void nr_pusch_ptrs_processing(PHY_VARS_gNB *gNB, NR_DL_FRAME_PARMS *frame_parms, const nfapi_nr_pusch_pdu_t *rel15_ul, - uint8_t ulsch_id, + NR_gNB_PUSCH *pusch_vars, uint8_t nr_tti_rx, unsigned char symbol, uint32_t nb_re_pusch); diff --git a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h index eaf7fcbfb5..43ecb0d006 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h +++ b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h @@ -85,11 +85,19 @@ void free_gNB_dlsch(NR_gNB_DLSCH_t *dlsch, uint16_t N_RB, const NR_DL_FRAME_PARM - LLR computation This function supports TM1, 2, 3, 5, and 6. @param ue Pointer to PHY variables - @param UE_id id of current UE + @param pusch_vars ULSCH PUSCH data + @param rel15_ul FAPI message to process + @param unav_res unav_res result @param frame Frame number @param slot Slot number */ -int nr_rx_pusch_tp(PHY_VARS_gNB *gNB, uint8_t ulsch_id, uint32_t frame, uint8_t slot, int beam_nb); +int nr_rx_pusch_tp(PHY_VARS_gNB *gNB, + NR_gNB_PUSCH *pusch_vars, + const nfapi_nr_pusch_pdu_t *rel15_ul, + uint32_t *ret_unav_res, + uint32_t frame, + uint8_t slot, + int beam_nb); /*! \brief This function implements the idft transform precoding in PUSCH diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c index d9a79ac84d..684e65699e 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c @@ -857,7 +857,6 @@ static uint8_t nr_ulsch_mmse_2layers(c16_t **rxdataF_comp, } static void inner_rx(PHY_VARS_gNB *gNB, - int ulsch_id, int slot, NR_DL_FRAME_PARMS *frame_parms, NR_gNB_PUSCH *pusch_vars, @@ -952,7 +951,7 @@ static void inner_rx(PHY_VARS_gNB *gNB, nr_pusch_ptrs_processing(gNB, frame_parms, rel15_ul, - ulsch_id, + pusch_vars, slot, symbol, buffer_length); @@ -1004,7 +1003,7 @@ typedef struct puschSymbolProc_s { PHY_VARS_gNB *gNB; NR_DL_FRAME_PARMS *frame_parms; const nfapi_nr_pusch_pdu_t *rel15_ul; - int ulsch_id; + NR_gNB_PUSCH *pusch_vars; int slot; int startSymbol; int numSymbols; @@ -1024,11 +1023,10 @@ static void nr_pusch_symbol_processing(void *arg) PHY_VARS_gNB *gNB = rdata->gNB; NR_DL_FRAME_PARMS *frame_parms = rdata->frame_parms; const nfapi_nr_pusch_pdu_t *rel15_ul = rdata->rel15_ul; - int ulsch_id = rdata->ulsch_id; int slot = rdata->slot; - NR_gNB_PUSCH *pusch_vars = &gNB->pusch_vars[ulsch_id]; + NR_gNB_PUSCH *pusch_vars = rdata->pusch_vars; for (int symbol = rdata->startSymbol; symbol < rdata->startSymbol + rdata->numSymbols; symbol++) { - if (gNB->pusch_vars[ulsch_id].ul_valid_re_per_slot[symbol] == 0) + if (pusch_vars->ul_valid_re_per_slot[symbol] == 0) continue; int soffset = (slot % RU_RX_SLOT_DEPTH) * frame_parms->symbols_per_slot * frame_parms->ofdm_symbol_size; int buffer_length = ceil_mod(pusch_vars->ul_valid_re_per_slot[symbol] * NR_NB_SC_PER_RB, 16); @@ -1038,7 +1036,6 @@ static void nr_pusch_symbol_processing(void *arg) llrss[l] = llrs[l]; inner_rx(gNB, - ulsch_id, slot, frame_parms, pusch_vars, @@ -1047,12 +1044,12 @@ static void nr_pusch_symbol_processing(void *arg) llrss, soffset, symbol, - gNB->pusch_vars[ulsch_id].log2_maxh, + pusch_vars->log2_maxh, rdata->nvar, rdata->rxFext_slot_mem, rdata->pusch_ch_est_dmrs_interpl_slot_mem); - int nb_re_pusch = gNB->pusch_vars[ulsch_id].ul_valid_re_per_slot[symbol]; + int nb_re_pusch = pusch_vars->ul_valid_re_per_slot[symbol]; // layer de-mapping int16_t *llr_ptr = llrs[0]; if (rel15_ul->nrOfLayers != 1) { @@ -1098,12 +1095,16 @@ static uint32_t average_u32(const uint32_t *x, uint16_t size) return (uint32_t)(sum_x / size); } -int nr_rx_pusch_tp(PHY_VARS_gNB *gNB, uint8_t ulsch_id, uint32_t frame, uint8_t slot, int beam_nb) +int nr_rx_pusch_tp(PHY_VARS_gNB *gNB, + NR_gNB_PUSCH *pusch_vars, + const nfapi_nr_pusch_pdu_t *rel15_ul, + uint32_t *ret_unav_res, + uint32_t frame, + uint8_t slot, + int beam_nb) { NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms; - nfapi_nr_pusch_pdu_t *rel15_ul = &gNB->ulsch[ulsch_id].harq_process->ulsch_pdu; - NR_gNB_PUSCH *pusch_vars = &gNB->pusch_vars[ulsch_id]; uint32_t bwp_start_subcarrier = ((rel15_ul->rb_start + rel15_ul->bwp_start) * NR_NB_SC_PER_RB + frame_parms->first_carrier_offset) % frame_parms->ofdm_symbol_size; LOG_D(PHY,"pusch %d.%d : bwp_start_subcarrier %d, rb_start %d, first_carrier_offset %d\n", frame,slot,bwp_start_subcarrier, rel15_ul->rb_start, frame_parms->first_carrier_offset); LOG_D(PHY,"pusch %d.%d : ul_dmrs_symb_pos %x\n",frame,slot,rel15_ul->ul_dmrs_symb_pos); @@ -1261,7 +1262,7 @@ int nr_rx_pusch_tp(PHY_VARS_gNB *gNB, uint8_t ulsch_id, uint32_t frame, uint8_t unav_res, rel15_ul->qam_mod_order, rel15_ul->nrOfLayers); - gNB->ulsch[ulsch_id].unav_res = unav_res; + *ret_unav_res = unav_res; // initialize scrambling sequence // int16_t scramblingSequence[G + 96] __attribute__((aligned(32))); @@ -1378,7 +1379,7 @@ int nr_rx_pusch_tp(PHY_VARS_gNB *gNB, uint8_t ulsch_id, uint32_t frame, uint8_t rdata->startSymbol = symbol; // Last task processes remainder symbols rdata->numSymbols = task_index == loop_iter - 1 ? rel15_ul->nr_of_symbols - (loop_iter - 1) * numSymbols : numSymbols; - rdata->ulsch_id = ulsch_id; + rdata->pusch_vars = pusch_vars; rdata->llr = pusch_vars->llr; rdata->scramblingSequence = scramblingSequence; rdata->nvar = nvar; diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index 7f6ae103c5..a82088e7ac 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -1086,6 +1086,7 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N if (!(ulsch->active && ulsch->frame == frame_rx && ulsch->slot == slot_rx && !ulsch->handled)) continue; LOG_D(PHY, "PUSCH ID %d with RNTI %x detection started in frame %d slot %d\n", ULSCH_id, ulsch->rnti, frame_rx, slot_rx); + const nfapi_nr_pusch_pdu_t *pdu = &ulsch_harq->ulsch_pdu; #ifdef DEBUG_RXDATA RU_t *ru = gNB->RU_list[0]; @@ -1119,8 +1120,8 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N pusch_decode_done = 1; start_meas(&gNB->rx_pusch_stats); - nr_rx_pusch_tp(gNB, ULSCH_id, frame_rx, slot_rx, ulsch->beam_nb); NR_gNB_PUSCH *pusch_vars = &gNB->pusch_vars[ULSCH_id]; + nr_rx_pusch_tp(gNB, pusch_vars, pdu, &ulsch->unav_res, frame_rx, slot_rx, ulsch->beam_nb); pusch_vars->ulsch_power_tot = 0; pusch_vars->ulsch_noise_power_tot = 0; for (int aarx = 0; aarx < nb_antennas_rx; aarx++) { From a2f8f2ead34c13175f6922d9b27300b4635a8c69 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 24 Nov 2025 13:25:20 +0100 Subject: [PATCH 14/29] Remove first useless iteration It is pointless to iterate ulsch_to_decode to find the number of jobs if we can simply pass it to the function. Use an additional {} block to limit the amount of indentation. Signed-off-by: Robert Schmidt --- openair1/PHY/NR_TRANSPORT/nr_ulsch.h | 2 +- openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c | 2 +- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 50 +++++++------------ openair1/SIMULATION/NR_PHY/ulschsim.c | 2 +- 4 files changed, 22 insertions(+), 34 deletions(-) diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch.h b/openair1/PHY/NR_TRANSPORT/nr_ulsch.h index b1e932f60d..514c8b60e5 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch.h +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch.h @@ -33,7 +33,7 @@ int nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB, uint32_t frame, uint8_t nr_tti_rx, uint32_t *G, - uint8_t *ULSCH_ids, + int *ULSCH_ids, int nb_pusch); /*! \brief Perform PUSCH unscrambling. TS 38.211 V15.4.0 subclause 6.3.1.1 diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c index 29a53ec5a0..e04f688a54 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c @@ -94,7 +94,7 @@ int nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB, uint32_t frame, uint8_t nr_tti_rx, uint32_t *G, - uint8_t *ULSCH_ids, + int *ULSCH_ids, int nb_pusch) { nrLDPC_TB_decoding_parameters_t TBs[nb_pusch]; diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index a82088e7ac..e52171de82 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -323,31 +323,18 @@ void phy_procedures_gNB_TX(PHY_VARS_gNB *gNB, stop_meas(&gNB->phase_comp_stats); } -static int nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, bool *ulsch_to_decode, NR_UL_IND_t *UL_INFO) +static int nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, int *ulsch_to_decode, int nb_pusch, NR_UL_IND_t *UL_INFO) { + DevAssert(nb_pusch > 0); NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms; - int nb_pusch = 0; - for (int ULSCH_id = 0; ULSCH_id < gNB->max_nb_pusch; ULSCH_id++) { - if (ulsch_to_decode[ULSCH_id]) { - nb_pusch++; - } - } - - if (nb_pusch == 0) { - return 0; - } - - uint8_t ULSCH_ids[nb_pusch]; uint32_t G[nb_pusch]; - int pusch_id = 0; - for (int ULSCH_id = 0; ULSCH_id < gNB->max_nb_pusch; ULSCH_id++) { + for (int pusch_id = 0; pusch_id < nb_pusch; pusch_id++) { - if (ulsch_to_decode[ULSCH_id]) { - - ULSCH_ids[pusch_id] = ULSCH_id; - - nfapi_nr_pusch_pdu_t *pusch_pdu = &gNB->ulsch[ULSCH_id].harq_process->ulsch_pdu; + int ULSCH_id = ulsch_to_decode[pusch_id]; + DevAssert(ULSCH_id >= 0); + { + const nfapi_nr_pusch_pdu_t *pusch_pdu = &gNB->ulsch[ULSCH_id].harq_process->ulsch_pdu; uint16_t nb_re_dmrs; uint16_t start_symbol = pusch_pdu->start_symbol_index; @@ -384,7 +371,6 @@ static int nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, boo number_dmrs_symbols, // number of dmrs symbols irrespective of single or double symbol dmrs pusch_pdu->qam_mod_order, pusch_pdu->nrOfLayers); - pusch_id++; } } @@ -392,15 +378,15 @@ static int nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, boo //--------------------- ULSCH decoding --------------------- //---------------------------------------------------------- - int ret_nr_ulsch_decoding = nr_ulsch_decoding(gNB, frame_parms, frame_rx, slot_rx, G, ULSCH_ids, nb_pusch); + int ret_nr_ulsch_decoding = nr_ulsch_decoding(gNB, frame_parms, frame_rx, slot_rx, G, ulsch_to_decode, nb_pusch); // CRC check per uplink shared channel - for (pusch_id = 0; pusch_id < nb_pusch; pusch_id++) { - uint8_t ULSCH_id = ULSCH_ids[pusch_id]; + for (int pusch_id = 0; pusch_id < nb_pusch; pusch_id++) { + uint8_t ULSCH_id = ulsch_to_decode[pusch_id]; NR_gNB_ULSCH_t *ulsch = &gNB->ulsch[ULSCH_id]; NR_gNB_PUSCH *pusch = &gNB->pusch_vars[ULSCH_id]; NR_UL_gNB_HARQ_t *ulsch_harq = ulsch->harq_process; - nfapi_nr_pusch_pdu_t *pusch_pdu = &ulsch_harq->ulsch_pdu; + const nfapi_nr_pusch_pdu_t *pusch_pdu = &ulsch_harq->ulsch_pdu; bool crc_valid = false; @@ -1077,9 +1063,10 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N UL_INFO->rx_ind.sfn = frame_rx; UL_INFO->rx_ind.slot = slot_rx; UL_INFO->rx_ind.pdu_list = UL_INFO->rx_pdu_list; - bool ulsch_to_decode[gNB->max_nb_pusch]; - bzero(ulsch_to_decode, sizeof(ulsch_to_decode)); + int num_pusch = 0; + int ulsch_idx_to_decode[gNB->max_nb_pusch]; for (int ULSCH_id = 0; ULSCH_id < gNB->max_nb_pusch; ULSCH_id++) { + ulsch_idx_to_decode[ULSCH_id] = -1; NR_gNB_ULSCH_t *ulsch = &gNB->ulsch[ULSCH_id]; NR_UL_gNB_HARQ_t *ulsch_harq = ulsch->harq_process; AssertFatal(ulsch_harq != NULL, "harq_pid %d is not allocated\n", ulsch->harq_pid); @@ -1163,7 +1150,7 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N pusch_vars->DTX = 0; } - ulsch_to_decode[ULSCH_id] = true; + ulsch_idx_to_decode[num_pusch++] = ULSCH_id; stop_meas(&gNB->rx_pusch_stats); // LOG_M("rxdataF_comp.m","rxF_comp",gNB->pusch_vars[0]->rxdataF_comp[0],6900,1,1); // LOG_M("rxdataF_ext.m","rxF_ext",gNB->pusch_vars[0]->rxdataF_ext[0],6900,1,1); @@ -1176,9 +1163,10 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N if (gNB->max_nb_pusch == 1) start_meas(&gNB->ulsch_decoding_stats); - int const ret_nr_ulsch_procedures = nr_ulsch_procedures(gNB, frame_rx, slot_rx, ulsch_to_decode, UL_INFO); - if (ret_nr_ulsch_procedures != 0) { - LOG_E(PHY,"Error in nr_ulsch_procedures, returned %d\n",ret_nr_ulsch_procedures); + if (num_pusch > 0) { + int ret_nr_ulsch_procedures = nr_ulsch_procedures(gNB, frame_rx, slot_rx, ulsch_idx_to_decode, num_pusch, UL_INFO); + if (ret_nr_ulsch_procedures != 0) + LOG_E(PHY,"Error in nr_ulsch_procedures, returned %d\n",ret_nr_ulsch_procedures); } /* Do ULSCH decoding time measurement only when number of PUSCH is limited to 1 diff --git a/openair1/SIMULATION/NR_PHY/ulschsim.c b/openair1/SIMULATION/NR_PHY/ulschsim.c index f07ad92ca9..64559757b1 100644 --- a/openair1/SIMULATION/NR_PHY/ulschsim.c +++ b/openair1/SIMULATION/NR_PHY/ulschsim.c @@ -419,7 +419,7 @@ int main(int argc, char **argv) uint16_t N_RE_prime,code_rate; unsigned char mod_order; uint8_t rvidx = 0; - uint8_t UE_id = 0; + int UE_id = 0; NR_gNB_ULSCH_t *ulsch_gNB = &gNB->ulsch[UE_id]; NR_UL_gNB_HARQ_t *harq_process_gNB = ulsch_gNB->harq_process; From c0a2dc9006492f09f53c0d49ccc4c1a7f58f8ce8 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 24 Nov 2025 15:25:59 +0100 Subject: [PATCH 15/29] Remove second useless loop iteration in nr_ulsch_procedures() Set the FAPI message in nr_ulschsim correctly in line with what nr_ulsch_procedures() expected. Add an AssertFatal() to verify that the assumed length of DMRS in the simulator and what is actually set in the FAPI message matches. Finally, the internal nr_get_G() code (looping over all PUSCH) does not double the DMRS because Nl == 3 || Nl == 4, so harmonize in the simulator. Signed-off-by: Robert Schmidt --- openair1/PHY/NR_TRANSPORT/nr_ulsch.h | 2 - openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c | 19 ++++++-- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 48 +------------------ openair1/SIMULATION/NR_PHY/ulschsim.c | 12 +++-- 4 files changed, 23 insertions(+), 58 deletions(-) diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch.h b/openair1/PHY/NR_TRANSPORT/nr_ulsch.h index 514c8b60e5..816ce9a16a 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch.h +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch.h @@ -23,7 +23,6 @@ NR_gNB_ULSCH_t new_gNB_ulsch(uint8_t max_ldpc_iterations, uint16_t N_RB_UL); @param frame_parms, Pointer to frame descriptor structure @param frame, current received frame @param nr_tti_rx, current received TTI - @param G @param ULSCH_ids, array of ULSCH ids @param nb_pusch, number of uplink shared channels */ @@ -32,7 +31,6 @@ int nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB, NR_DL_FRAME_PARMS *frame_parms, uint32_t frame, uint8_t nr_tti_rx, - uint32_t *G, int *ULSCH_ids, int nb_pusch); diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c index e04f688a54..e3b44e99ec 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c @@ -93,7 +93,6 @@ int nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB, NR_DL_FRAME_PARMS *frame_parms, uint32_t frame, uint8_t nr_tti_rx, - uint32_t *G, int *ULSCH_ids, int nb_pusch) { @@ -112,17 +111,29 @@ int nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB, NR_gNB_ULSCH_t *ulsch = &phy_vars_gNB->ulsch[ULSCH_id]; NR_gNB_PUSCH *pusch = &phy_vars_gNB->pusch_vars[ULSCH_id]; NR_UL_gNB_HARQ_t *harq_process = ulsch->harq_process; - nfapi_nr_pusch_pdu_t *pusch_pdu = &harq_process->ulsch_pdu; + const nfapi_nr_pusch_pdu_t *pusch_pdu = &harq_process->ulsch_pdu; nrLDPC_TB_decoding_parameters_t *TB_parameters = &TBs[pusch_id]; - TB_parameters->G = G[pusch_id]; - if (!harq_process) { LOG_E(PHY, "ulsch_decoding.c: NULL harq_process pointer\n"); return -1; } + uint8_t number_dmrs_symbols = count_bits64_with_mask(pusch_pdu->ul_dmrs_symb_pos, pusch_pdu->start_symbol_index, pusch_pdu->nr_of_symbols); + int factor = pusch_pdu->dmrs_config_type == pusch_dmrs_type1 ? 6 : 4; + int nb_re_dmrs = factor * pusch_pdu->num_dmrs_cdm_grps_no_data; + uint32_t G = nr_get_G(pusch_pdu->rb_size, + pusch_pdu->nr_of_symbols, + nb_re_dmrs, + number_dmrs_symbols, // number of dmrs symbols irrespective of single or double symbol dmrs + ulsch->unav_res, + pusch_pdu->qam_mod_order, + pusch_pdu->nrOfLayers); + DevAssert(G > 0); + TB_parameters->G = G; + + // The harq_pid is not unique among the active HARQ processes in the instance so we use ULSCH_id instead TB_parameters->harq_unique_pid = ULSCH_id; diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index e52171de82..70b9c15a44 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -327,58 +327,12 @@ static int nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, int { DevAssert(nb_pusch > 0); NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms; - - uint32_t G[nb_pusch]; - for (int pusch_id = 0; pusch_id < nb_pusch; pusch_id++) { - - int ULSCH_id = ulsch_to_decode[pusch_id]; - DevAssert(ULSCH_id >= 0); - { - const nfapi_nr_pusch_pdu_t *pusch_pdu = &gNB->ulsch[ULSCH_id].harq_process->ulsch_pdu; - - uint16_t nb_re_dmrs; - uint16_t start_symbol = pusch_pdu->start_symbol_index; - uint16_t number_symbols = pusch_pdu->nr_of_symbols; - - uint8_t number_dmrs_symbols = 0; - for (int l = start_symbol; l < start_symbol + number_symbols; l++) - number_dmrs_symbols += ((pusch_pdu->ul_dmrs_symb_pos)>>l)&0x01; - - if (pusch_pdu->dmrs_config_type==pusch_dmrs_type1) - nb_re_dmrs = 6*pusch_pdu->num_dmrs_cdm_grps_no_data; - else - nb_re_dmrs = 4*pusch_pdu->num_dmrs_cdm_grps_no_data; - - G[pusch_id] = nr_get_G(pusch_pdu->rb_size, - number_symbols, - nb_re_dmrs, - number_dmrs_symbols, // number of dmrs symbols irrespective of single or double symbol dmrs - gNB->ulsch[ULSCH_id].unav_res, - pusch_pdu->qam_mod_order, - pusch_pdu->nrOfLayers); - AssertFatal(G[pusch_id]>0,"G is 0 : rb_size %u, number_symbols %d, nb_re_dmrs %d, number_dmrs_symbols %d, qam_mod_order %u, nrOfLayer %u\n", - pusch_pdu->rb_size, - number_symbols, - nb_re_dmrs, - number_dmrs_symbols, // number of dmrs symbols irrespective of single or double symbol dmrs - pusch_pdu->qam_mod_order, - pusch_pdu->nrOfLayers); - LOG_D(PHY,"rb_size %d, number_symbols %d, nb_re_dmrs %d, dmrs symbol positions %d, number_dmrs_symbols %d, qam_mod_order %d, nrOfLayer %d\n", - pusch_pdu->rb_size, - number_symbols, - nb_re_dmrs, - pusch_pdu->ul_dmrs_symb_pos, - number_dmrs_symbols, // number of dmrs symbols irrespective of single or double symbol dmrs - pusch_pdu->qam_mod_order, - pusch_pdu->nrOfLayers); - } - } //---------------------------------------------------------- //--------------------- ULSCH decoding --------------------- //---------------------------------------------------------- - int ret_nr_ulsch_decoding = nr_ulsch_decoding(gNB, frame_parms, frame_rx, slot_rx, G, ulsch_to_decode, nb_pusch); + int ret_nr_ulsch_decoding = nr_ulsch_decoding(gNB, frame_parms, frame_rx, slot_rx, ulsch_to_decode, nb_pusch); // CRC check per uplink shared channel for (int pusch_id = 0; pusch_id < nb_pusch; pusch_id++) { diff --git a/openair1/SIMULATION/NR_PHY/ulschsim.c b/openair1/SIMULATION/NR_PHY/ulschsim.c index 64559757b1..7d193f5119 100644 --- a/openair1/SIMULATION/NR_PHY/ulschsim.c +++ b/openair1/SIMULATION/NR_PHY/ulschsim.c @@ -414,7 +414,7 @@ int main(int argc, char **argv) unsigned int TBS = 8424; unsigned int available_bits; uint8_t nb_re_dmrs = 6; - uint8_t length_dmrs = 1; + uint8_t length_dmrs = 2; uint8_t N_PRB_oh; uint16_t N_RE_prime,code_rate; unsigned char mod_order; @@ -429,9 +429,6 @@ int main(int argc, char **argv) nr_phy_data_tx_t phy_data = {0}; NR_UE_ULSCH_t *ulsch_ue = &phy_data.ulsch; - if ((Nl==4)||(Nl==3)) - nb_re_dmrs = nb_re_dmrs*2; - mod_order = nr_get_Qm_ul(Imcs, mcs_table); code_rate = nr_get_code_rate_ul(Imcs, mcs_table); available_bits = nr_get_G(nb_rb, nb_symb_sch, nb_re_dmrs, length_dmrs, 0, mod_order, Nl); @@ -445,10 +442,15 @@ int main(int argc, char **argv) rel15_ul->qam_mod_order = mod_order; rel15_ul->mcs_index = Imcs; rel15_ul->pusch_data.rv_index = rvidx; + rel15_ul->ul_dmrs_symb_pos = nb_re_dmrs; + rel15_ul->dmrs_config_type = pusch_dmrs_type1; + rel15_ul->num_dmrs_cdm_grps_no_data = 1; rel15_ul->nrOfLayers = Nl; rel15_ul->target_code_rate = code_rate; rel15_ul->pusch_data.tb_size = TBS>>3; rel15_ul->maintenance_parms_v3.ldpcBaseGraph = get_BG(TBS, code_rate); + int bits = count_bits64_with_mask(rel15_ul->ul_dmrs_symb_pos, rel15_ul->start_symbol_index, rel15_ul->nr_of_symbols); + AssertFatal(length_dmrs == bits, "length_dmrs %d bits %d\n", length_dmrs, bits); /////////////////////////////////////////////////// double modulated_input[16 * 68 * 384]; // [hna] 16 segments, 68*Zc @@ -558,7 +560,7 @@ int main(int argc, char **argv) printf("\n"); exit(-1); #endif - nr_ulsch_decoding(gNB, frame_parms, frame, subframe, &G, &UE_id, 1); + nr_ulsch_decoding(gNB, frame_parms, frame, subframe, &UE_id, 1); if (harq_process_gNB->processedSegments == harq_process_gNB->C) { bool crc_valid = check_crc(harq_process_gNB->b, lenWithCrc(1, (harq_process_gNB->TBS) << 3), crcType(1, (harq_process_gNB->TBS) << 3)); if (!crc_valid) { From 337abd5716748ce6a33cfd97950e3414b44ee5c6 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 24 Nov 2025 17:16:04 +0100 Subject: [PATCH 16/29] Remove superfluous tbs parameter Signed-off-by: Robert Schmidt --- openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c | 5 ++--- openair1/PHY/defs_gNB.h | 2 -- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 13 +++++++------ openair1/SIMULATION/NR_PHY/ulschsim.c | 2 +- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c index e3b44e99ec..9cb6e22c03 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c @@ -145,10 +145,9 @@ int nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB, // ------------------------------------------------------------------ TB_parameters->processedSegments = &harq_process->processedSegments; - harq_process->TBS = pusch_pdu->pusch_data.tb_size; TB_parameters->BG = pusch_pdu->maintenance_parms_v3.ldpcBaseGraph; - TB_parameters->A = (harq_process->TBS) << 3; + TB_parameters->A = pusch_pdu->pusch_data.tb_size << 3; NR_gNB_PHY_STATS_t *stats = get_phy_stats(phy_vars_gNB, ulsch->rnti); if (stats) { stats->frame = frame; @@ -160,7 +159,7 @@ int nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB, if (!harq_process->harq_to_be_cleared) { stats->ulsch_stats.current_Qm = TB_parameters->Qm; stats->ulsch_stats.current_RI = TB_parameters->nb_layers; - stats->ulsch_stats.total_bytes_tx += harq_process->TBS; + stats->ulsch_stats.total_bytes_tx += pusch_pdu->pusch_data.tb_size; } } diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index bd611bbe8f..8ab19981dd 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -136,8 +136,6 @@ typedef struct { /// flag used to clear d properly (together with d_to_be_cleared below) /// set to true in nr_fill_ulsch() when new_data_indicator is received bool harq_to_be_cleared; - /// Transport block size (This is A from 38.212 V15.4.0 section 5.1) - uint32_t TBS; /// Pointer to the payload (38.212 V15.4.0 section 5.1) uint8_t *b; /// Pointers to code blocks after code block segmentation and CRC attachment (38.212 V15.4.0 section 5.2.2) diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index 70b9c15a44..b2ed09a6ec 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -347,7 +347,8 @@ static int nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, int // if all segments are done if (ulsch_harq->processedSegments == ulsch_harq->C) { if (ulsch_harq->C > 1) { - crc_valid = check_crc(ulsch_harq->b, lenWithCrc(1, (ulsch_harq->TBS) << 3), crcType(1, (ulsch_harq->TBS) << 3)); + int tbs = pusch_pdu->pusch_data.tb_size; + crc_valid = check_crc(ulsch_harq->b, lenWithCrc(1, tbs << 3), crcType(1, tbs << 3)); } else { // When the number of code blocks is 1 (C = 1) and ulsch_harq->processedSegments = 1, we can assume a good TB because of the // CRC check made by the LDPC for early termination, so, no need to perform CRC check twice for a single code block @@ -412,8 +413,8 @@ static int nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, int T_INT((int)dmrs_port), // dmrs_antenna_port T_INT((int)pusch_pdu->scid), // dmrs_nscid T_INT((int)frame_parms->nb_antennas_rx), // rx antenna - T_INT(((ulsch_harq->TBS) << 3)), // number_of_bits - T_BUFFER((uint8_t *)((ulsch_harq->b)), ((ulsch_harq->TBS) << 3) / 8)); // data + T_INT((pusch_pdu->pusch_data.tb_size << 3)), // number_of_bits + T_BUFFER((uint8_t *)((ulsch_harq->b)), pusch_pdu->pusch_data.tb_size)); // data } #endif @@ -432,7 +433,7 @@ static int nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, int pusch_pdu->pusch_data.new_data_indicator, ulsch->active, ulsch_harq->round, - ulsch_harq->TBS, + pusch_pdu->pusch_data.tb_size, ulsch->max_ldpc_iterations); nr_fill_indication(gNB, ulsch->frame, ulsch->slot, ULSCH_id, ulsch->harq_pid, 0, 0, crc, pdu); LOG_D(PHY, "ULSCH received ok \n"); @@ -453,7 +454,7 @@ static int nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, int ulsch_harq->ulsch_pdu.pusch_data.rv_index, ulsch_harq->ulsch_pdu.rb_start, ulsch_harq->ulsch_pdu.rb_size, - ulsch_harq->TBS); + pusch_pdu->pusch_data.tb_size); nr_fill_indication(gNB, ulsch->frame, ulsch->slot, ULSCH_id, ulsch->harq_pid, 1, 0, crc, pdu); gNBdumpScopeData(gNB, ulsch->slot, ulsch->frame, "ULSCH_NACK"); ulsch->handled = 1; @@ -547,7 +548,7 @@ static void nr_fill_indication(PHY_VARS_gNB *gNB, if (crc_flag) pdu->pdu_length = 0; else { - pdu->pdu_length = harq_process->TBS; + pdu->pdu_length = pusch_pdu->pusch_data.tb_size; pdu->pdu = harq_process->b; } } diff --git a/openair1/SIMULATION/NR_PHY/ulschsim.c b/openair1/SIMULATION/NR_PHY/ulschsim.c index 7d193f5119..c5b8723908 100644 --- a/openair1/SIMULATION/NR_PHY/ulschsim.c +++ b/openair1/SIMULATION/NR_PHY/ulschsim.c @@ -562,7 +562,7 @@ int main(int argc, char **argv) #endif nr_ulsch_decoding(gNB, frame_parms, frame, subframe, &UE_id, 1); if (harq_process_gNB->processedSegments == harq_process_gNB->C) { - bool crc_valid = check_crc(harq_process_gNB->b, lenWithCrc(1, (harq_process_gNB->TBS) << 3), crcType(1, (harq_process_gNB->TBS) << 3)); + bool crc_valid = check_crc(harq_process_gNB->b, lenWithCrc(1, TBS << 3), crcType(1, TBS << 3)); if (!crc_valid) { n_false_positive++; } From cbe69c7dfaff6bbccb0aebf051e7a8ed714b3cab Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 24 Nov 2025 17:35:53 +0100 Subject: [PATCH 17/29] Refactor nr_fill_indication() Pass pointers directly instead of ULSCH_id. Put const where possible and applicable. Signed-off-by: Robert Schmidt --- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 64 +++++++---------------- 1 file changed, 20 insertions(+), 44 deletions(-) diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index b2ed09a6ec..c9f94ff5b6 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -27,22 +27,14 @@ //#define DEBUG_RXDATA //#define SRS_IND_DEBUG -static void nr_fill_indication(PHY_VARS_gNB *gNB, - int frame, - int slot_rx, - int UE_id, - uint8_t harq_pid, - uint8_t crc_flag, - int dtx_flag, - nfapi_nr_crc_t *crc, - nfapi_nr_rx_data_pdu_t *pdu); -static void nr_fill_indication(PHY_VARS_gNB *gNB, +static void nr_fill_indication(const PHY_VARS_gNB *gNB, int frame, int slot_rx, - int ULSCH_id, - uint8_t harq_pid, - uint8_t crc_flag, + const NR_gNB_PUSCH *pusch, + const nfapi_nr_pusch_pdu_t *pusch_pdu, + NR_gNB_PHY_STATS_t *stats, + uint8_t *payload, int dtx_flag, nfapi_nr_crc_t *crc, nfapi_nr_rx_data_pdu_t *pdu); @@ -340,6 +332,7 @@ static int nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, int NR_gNB_ULSCH_t *ulsch = &gNB->ulsch[ULSCH_id]; NR_gNB_PUSCH *pusch = &gNB->pusch_vars[ULSCH_id]; NR_UL_gNB_HARQ_t *ulsch_harq = ulsch->harq_process; + NR_gNB_PHY_STATS_t *stats = get_phy_stats(gNB, ulsch->rnti); const nfapi_nr_pusch_pdu_t *pusch_pdu = &ulsch_harq->ulsch_pdu; bool crc_valid = false; @@ -435,7 +428,7 @@ static int nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, int ulsch_harq->round, pusch_pdu->pusch_data.tb_size, ulsch->max_ldpc_iterations); - nr_fill_indication(gNB, ulsch->frame, ulsch->slot, ULSCH_id, ulsch->harq_pid, 0, 0, crc, pdu); + nr_fill_indication(gNB, ulsch->frame, ulsch->slot, pusch, pusch_pdu, stats, ulsch->harq_process->b, 0, crc, pdu); LOG_D(PHY, "ULSCH received ok \n"); ulsch->active = false; ulsch_harq->round = 0; @@ -455,7 +448,7 @@ static int nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, int ulsch_harq->ulsch_pdu.rb_start, ulsch_harq->ulsch_pdu.rb_size, pusch_pdu->pusch_data.tb_size); - nr_fill_indication(gNB, ulsch->frame, ulsch->slot, ULSCH_id, ulsch->harq_pid, 1, 0, crc, pdu); + nr_fill_indication(gNB, ulsch->frame, ulsch->slot, pusch, pusch_pdu, stats, NULL, 0, crc, pdu); gNBdumpScopeData(gNB, ulsch->slot, ulsch->frame, "ULSCH_NACK"); ulsch->handled = 1; LOG_D(PHY, "ULSCH %d in error\n",ULSCH_id); @@ -466,22 +459,17 @@ static int nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, int return ret_nr_ulsch_decoding; } -static void nr_fill_indication(PHY_VARS_gNB *gNB, +static void nr_fill_indication(const PHY_VARS_gNB *gNB, int frame, int slot_rx, - int ULSCH_id, - uint8_t harq_pid, - uint8_t crc_flag, + const NR_gNB_PUSCH *pusch, + const nfapi_nr_pusch_pdu_t *pusch_pdu, + NR_gNB_PHY_STATS_t *stats, + uint8_t *payload, int dtx_flag, nfapi_nr_crc_t *crc, nfapi_nr_rx_data_pdu_t *pdu) { - NR_UL_gNB_HARQ_t *harq_process = gNB->ulsch[ULSCH_id].harq_process; - NR_gNB_PHY_STATS_t *stats = get_phy_stats(gNB, gNB->ulsch[ULSCH_id].rnti); - NR_gNB_PUSCH *pusch = &gNB->pusch_vars[ULSCH_id]; - - nfapi_nr_pusch_pdu_t *pusch_pdu = &harq_process->ulsch_pdu; - // Get estimated timing advance for MAC const int sync_pos = pusch->delay.est_delay; @@ -504,14 +492,6 @@ static void nr_fill_indication(PHY_VARS_gNB *gNB, timing_advance_update = max(timing_advance_update, 0); timing_advance_update = min(timing_advance_update, 63); - if (crc_flag == 0) - LOG_D(PHY, - "%d.%d : Received PUSCH : Estimated timing advance PUSCH is = %d, timing_advance_update is %d \n", - frame, - slot_rx, - sync_pos, - timing_advance_update); - // estimate UL_CQI for MAC int SNRtimes10 = dB_fixed_x10(pusch->ulsch_power_tot) - dB_fixed_x10(pusch->ulsch_noise_power_tot); @@ -531,8 +511,8 @@ static void nr_fill_indication(PHY_VARS_gNB *gNB, crc->handle = pusch_pdu->handle; crc->rnti = pusch_pdu->rnti; - crc->harq_id = harq_pid; - crc->tb_crc_status = crc_flag; + crc->harq_id = pusch_pdu->pusch_data.harq_process_id; + crc->tb_crc_status = payload == NULL; // 222.10.02/04: pass 0, fail 1 crc->num_cb = pusch_pdu->pusch_data.num_cb; crc->ul_cqi = cqi; crc->timing_advance = timing_advance_update; @@ -541,16 +521,12 @@ static void nr_fill_indication(PHY_VARS_gNB *gNB, pdu->handle = pusch_pdu->handle; pdu->rnti = pusch_pdu->rnti; - pdu->harq_id = harq_pid; + pdu->harq_id = pusch_pdu->pusch_data.harq_process_id; pdu->ul_cqi = cqi; pdu->timing_advance = timing_advance_update; pdu->rssi = crc->rssi; - if (crc_flag) - pdu->pdu_length = 0; - else { - pdu->pdu_length = pusch_pdu->pusch_data.tb_size; - pdu->pdu = harq_process->b; - } + pdu->pdu_length = payload != NULL ? pusch_pdu->pusch_data.tb_size : 0; + pdu->pdu = payload; } // Function to fill UL RB mask to be used for N0 measurements @@ -1088,8 +1064,8 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N /* in case of phy_test mode, we still want to decode to measure execution time. Therefore, we don't yet call nr_fill_indication, it will be called later */ nfapi_nr_crc_t *crc = &UL_INFO->crc_ind.crc_list[UL_INFO->crc_ind.number_crcs++]; - nfapi_nr_rx_data_pdu_t *pdu = &UL_INFO->rx_ind.pdu_list[UL_INFO->rx_ind.number_of_pdus++]; - nr_fill_indication(gNB, frame_rx, slot_rx, ULSCH_id, ulsch->harq_pid, 1, 1, crc, pdu); + nfapi_nr_rx_data_pdu_t *rx = &UL_INFO->rx_ind.pdu_list[UL_INFO->rx_ind.number_of_pdus++]; + nr_fill_indication(gNB, frame_rx, slot_rx, pusch_vars, pdu, stats, NULL, 1, crc, rx); pusch_DTX++; gNBdumpScopeData(gNB, ulsch->slot, ulsch->frame, "ULSCH_DTX"); continue; From 25f77993caa23d134c5d127d8184278cc91a994e Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 24 Nov 2025 17:53:08 +0100 Subject: [PATCH 18/29] Refactor PUSCH processing into its own function Signed-off-by: Robert Schmidt --- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 168 +++++++++++----------- 1 file changed, 88 insertions(+), 80 deletions(-) diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index c9f94ff5b6..dc74669532 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -919,6 +919,92 @@ static void handle_pucch(PHY_VARS_gNB *gNB, c16_t **rxdataF, const NR_gNB_PUCCH_ } } +static bool handle_pusch_decode_trigger(PHY_VARS_gNB *gNB, NR_gNB_PUSCH *pusch_vars, NR_gNB_ULSCH_t *ulsch, NR_UL_IND_t *UL_INFO, int *pusch_DTX) +{ + NR_UL_gNB_HARQ_t *ulsch_harq = ulsch->harq_process; + AssertFatal(ulsch_harq != NULL, "harq_pid %d is not allocated\n", ulsch->harq_pid); + const nfapi_nr_pusch_pdu_t *pdu = &ulsch_harq->ulsch_pdu; + +#ifdef DEBUG_RXDATA + RU_t *ru = gNB->RU_list[0]; + int slot_offset = frame_parms->get_samples_slot_timestamp(ulsch->slot, frame_parms, 0); + slot_offset -= ru->N_TA_offset; + int32_t sample_offset = gNB->common_vars.debugBuff_sample_offset; + int16_t buf = (int16_t *)&gNB->common_vars.debugBuff[offset]; + buf[0] = (int16_t)ulsch->rnti; + buf[1] = (int16_t)pdu->rb_size; + buf[2] = (int16_t)pdu->rb_start; + buf[3] = (int16_t)pdu->nr_of_symbols; + buf[4] = (int16_t)pdu->start_symbol_index; + buf[5] = (int16_t)pdu->mcs_index; + buf[6] = (int16_t)pdu->pusch_data.rv_index; + buf[7] = (int16_t)ulsch->harq_pid; + memcpy(&gNB->common_vars.debugBuff[gNB->common_vars.debugBuff_sample_offset + 4], + &ru->common.rxdata[0][slot_offset], + frame_parms->get_samples_per_slot(ulsch->slot, frame_parms) * sizeof(int32_t)); + gNB->common_vars.debugBuff_sample_offset += (frame_parms->get_samples_per_slot(ulsch->slot, frame_parms) + 1000 + 4); + if (gNB->common_vars.debugBuff_sample_offset > ((frame_parms->get_samples_per_slot(ulsch->slot, frame_parms) + 1000 + 2) * 20)) { + FILE *f; + f = fopen("rxdata_buff.raw", "w"); + if (f == NULL) + exit(1); + fwrite((int16_t *)gNB->common_vars.debugBuff, + 2, + (frame_parms->get_samples_per_slot(ulsch->slot, frame_parms) + 1000 + 4) * 20 * 2, + f); + fclose(f); + exit(-1); + } +#endif + + start_meas(&gNB->rx_pusch_stats); + nr_rx_pusch_tp(gNB, pusch_vars, pdu, &ulsch->unav_res, ulsch->frame, ulsch->slot, ulsch->beam_nb); + pusch_vars->ulsch_power_tot = 0; + pusch_vars->ulsch_noise_power_tot = 0; + for (int aarx = 0; aarx < gNB->frame_parms.nb_antennas_rx; aarx++) { + pusch_vars->ulsch_power_tot += pusch_vars->ulsch_power[aarx]; + pusch_vars->ulsch_noise_power_tot += pusch_vars->ulsch_noise_power[aarx]; + } + if (dB_fixed_x10(pusch_vars->ulsch_power_tot) < dB_fixed_x10(pusch_vars->ulsch_noise_power_tot) + gNB->pusch_thres) { + NR_gNB_PHY_STATS_t *stats = get_phy_stats(gNB, ulsch->rnti); + + LOG_D(PHY, + "PUSCH not detected in %d.%d (%d,%d,%d)\n", + ulsch->frame, + ulsch->slot, + dB_fixed_x10(pusch_vars->ulsch_power_tot), + dB_fixed_x10(pusch_vars->ulsch_noise_power_tot), + gNB->pusch_thres); + pusch_vars->ulsch_power_tot = pusch_vars->ulsch_noise_power_tot; + pusch_vars->DTX = 1; + if (stats) + stats->ulsch_stats.DTX++; + if (!get_softmodem_params()->phy_test) { + /* in case of phy_test mode, we still want to decode to measure execution time. + Therefore, we don't yet call nr_fill_indication, it will be called later */ + nfapi_nr_crc_t *crc = &UL_INFO->crc_ind.crc_list[UL_INFO->crc_ind.number_crcs++]; + nfapi_nr_rx_data_pdu_t *rx = &UL_INFO->rx_ind.pdu_list[UL_INFO->rx_ind.number_of_pdus++]; + nr_fill_indication(gNB, ulsch->frame, ulsch->slot, pusch_vars, pdu, stats, NULL, 1, crc, rx); + (*pusch_DTX)++; + gNBdumpScopeData(gNB, ulsch->slot, ulsch->frame, "ULSCH_DTX"); + return false; + } + } else { + LOG_D(PHY, + "PUSCH detected in %d.%d (%d,%d,%d)\n", + ulsch->frame, + ulsch->slot, + dB_fixed_x10(pusch_vars->ulsch_power_tot), + dB_fixed_x10(pusch_vars->ulsch_noise_power_tot), + gNB->pusch_thres); + + pusch_vars->DTX = 0; + } + stop_meas(&gNB->rx_pusch_stats); + + return true; +} + static bool drop_old_pucch(const void *data, void *user) { const NR_gNB_PUCCH_job_t *pucch = data; @@ -999,92 +1085,14 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N for (int ULSCH_id = 0; ULSCH_id < gNB->max_nb_pusch; ULSCH_id++) { ulsch_idx_to_decode[ULSCH_id] = -1; NR_gNB_ULSCH_t *ulsch = &gNB->ulsch[ULSCH_id]; - NR_UL_gNB_HARQ_t *ulsch_harq = ulsch->harq_process; - AssertFatal(ulsch_harq != NULL, "harq_pid %d is not allocated\n", ulsch->harq_pid); if (!(ulsch->active && ulsch->frame == frame_rx && ulsch->slot == slot_rx && !ulsch->handled)) continue; - LOG_D(PHY, "PUSCH ID %d with RNTI %x detection started in frame %d slot %d\n", ULSCH_id, ulsch->rnti, frame_rx, slot_rx); - const nfapi_nr_pusch_pdu_t *pdu = &ulsch_harq->ulsch_pdu; - -#ifdef DEBUG_RXDATA - RU_t *ru = gNB->RU_list[0]; - int slot_offset = frame_parms->get_samples_slot_timestamp(slot_rx, frame_parms, 0); - slot_offset -= ru->N_TA_offset; - int32_t sample_offset = gNB->common_vars.debugBuff_sample_offset; - int16_t buf = (int16_t *)&gNB->common_vars.debugBuff[offset]; - buf[0] = (int16_t)ulsch->rnti; - buf[1] = (int16_t)pdu->rb_size; - buf[2] = (int16_t)pdu->rb_start; - buf[3] = (int16_t)pdu->nr_of_symbols; - buf[4] = (int16_t)pdu->start_symbol_index; - buf[5] = (int16_t)pdu->mcs_index; - buf[6] = (int16_t)pdu->pusch_data.rv_index; - buf[7] = (int16_t)ulsch->harq_pid; - memcpy(&gNB->common_vars.debugBuff[gNB->common_vars.debugBuff_sample_offset + 4], - &ru->common.rxdata[0][slot_offset], - get_samples_per_slot(slot_rx, frame_parms) * sizeof(int32_t)); - gNB->common_vars.debugBuff_sample_offset += (get_samples_per_slot(slot_rx, frame_parms) + 1000 + 4); - if (gNB->common_vars.debugBuff_sample_offset > ((get_samples_per_slot(slot_rx, frame_parms) + 1000 + 2) * 20)) { - FILE *f; - f = fopen("rxdata_buff.raw", "w"); - if (f == NULL) - exit(1); - fwrite((int16_t *)gNB->common_vars.debugBuff, 2, (get_samples_per_slot(slot_rx, frame_parms) + 1000 + 4) * 20 * 2, f); - fclose(f); - exit(-1); - } -#endif pusch_decode_done = 1; - - start_meas(&gNB->rx_pusch_stats); NR_gNB_PUSCH *pusch_vars = &gNB->pusch_vars[ULSCH_id]; - nr_rx_pusch_tp(gNB, pusch_vars, pdu, &ulsch->unav_res, frame_rx, slot_rx, ulsch->beam_nb); - pusch_vars->ulsch_power_tot = 0; - pusch_vars->ulsch_noise_power_tot = 0; - for (int aarx = 0; aarx < nb_antennas_rx; aarx++) { - pusch_vars->ulsch_power_tot += pusch_vars->ulsch_power[aarx]; - pusch_vars->ulsch_noise_power_tot += pusch_vars->ulsch_noise_power[aarx]; - } - if (dB_fixed_x10(pusch_vars->ulsch_power_tot) < dB_fixed_x10(pusch_vars->ulsch_noise_power_tot) + gNB->pusch_thres) { - NR_gNB_PHY_STATS_t *stats = get_phy_stats(gNB, ulsch->rnti); - LOG_D(PHY, - "PUSCH not detected in %d.%d (%d,%d,%d)\n", - frame_rx, - slot_rx, - dB_fixed_x10(pusch_vars->ulsch_power_tot), - dB_fixed_x10(pusch_vars->ulsch_noise_power_tot), - gNB->pusch_thres); - pusch_vars->ulsch_power_tot = pusch_vars->ulsch_noise_power_tot; - pusch_vars->DTX = 1; - if (stats) - stats->ulsch_stats.DTX++; - if (!get_softmodem_params()->phy_test) { - /* in case of phy_test mode, we still want to decode to measure execution time. - Therefore, we don't yet call nr_fill_indication, it will be called later */ - nfapi_nr_crc_t *crc = &UL_INFO->crc_ind.crc_list[UL_INFO->crc_ind.number_crcs++]; - nfapi_nr_rx_data_pdu_t *rx = &UL_INFO->rx_ind.pdu_list[UL_INFO->rx_ind.number_of_pdus++]; - nr_fill_indication(gNB, frame_rx, slot_rx, pusch_vars, pdu, stats, NULL, 1, crc, rx); - pusch_DTX++; - gNBdumpScopeData(gNB, ulsch->slot, ulsch->frame, "ULSCH_DTX"); - continue; - } - } else { - LOG_D(PHY, - "PUSCH detected in %d.%d (%d,%d,%d)\n", - frame_rx, - slot_rx, - dB_fixed_x10(pusch_vars->ulsch_power_tot), - dB_fixed_x10(pusch_vars->ulsch_noise_power_tot), - gNB->pusch_thres); - - pusch_vars->DTX = 0; - } - ulsch_idx_to_decode[num_pusch++] = ULSCH_id; - stop_meas(&gNB->rx_pusch_stats); - // LOG_M("rxdataF_comp.m","rxF_comp",gNB->pusch_vars[0]->rxdataF_comp[0],6900,1,1); - // LOG_M("rxdataF_ext.m","rxF_ext",gNB->pusch_vars[0]->rxdataF_ext[0],6900,1,1); + if (handle_pusch_decode_trigger(gNB, pusch_vars, ulsch, UL_INFO, &pusch_DTX)) + ulsch_idx_to_decode[num_pusch++] = ULSCH_id; } /* Do ULSCH decoding time measurement only when number of PUSCH is limited to 1 From 138e0f95472dd3558149e3c997f64aac1c1dbb3e Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 9 Apr 2026 09:39:11 +0200 Subject: [PATCH 19/29] Use spsc_q for PUSCH queue Use the spsc queue to pass jobs from TX thread (scheduler) to RX thread (handling those jobs), reducing the amount spent searching for PUSCH jobs, clarifying the design, and making it thread-safe. Since the PUSCH contexts have to be stored somewhere, there is still a single iteration over all contexts. Removing that is left for future work. Thus, this commit (together with previous commits) reduces the number of iterations over all PUSCH entries, only iterating over the actually requested PUSCH jobs. On the other hand, because only the RX thread iterates over PUSCH contexts, it should be thread-safe. Remove flag "handled", which is always 0. Signed-off-by: Robert Schmidt --- openair1/PHY/INIT/nr_init.c | 2 + openair1/PHY/NR_TRANSPORT/nr_ulsch.c | 69 +++----------- openair1/PHY/defs_gNB.h | 16 +++- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 110 +++++++++++++++++----- 4 files changed, 113 insertions(+), 84 deletions(-) diff --git a/openair1/PHY/INIT/nr_init.c b/openair1/PHY/INIT/nr_init.c index 8a2a794fbd..5845b14d51 100644 --- a/openair1/PHY/INIT/nr_init.c +++ b/openair1/PHY/INIT/nr_init.c @@ -442,6 +442,7 @@ void init_nr_transport(PHY_VARS_gNB *gNB) int max_nb_pucch = buffer_ul_slots ? MAX_MOBILES_PER_GNB * buffer_ul_slots : 1; gNB->pucch_queue = spsc_q_alloc(max_nb_pucch, sizeof(NR_gNB_PUCCH_job_t)); + gNB->pusch_queue = spsc_q_alloc(gNB->max_nb_pusch, sizeof(NR_gNB_PUSCH_job_t)); gNB->srs = (NR_gNB_SRS_t *)malloc16(gNB->max_nb_srs * sizeof(NR_gNB_SRS_t)); for (int i = 0; i < gNB->max_nb_srs; i++) @@ -463,6 +464,7 @@ void reset_nr_transport(PHY_VARS_gNB *gNB) const NR_DL_FRAME_PARMS *fp = &gNB->frame_parms; spsc_q_free(&gNB->pucch_queue); + spsc_q_free(&gNB->pusch_queue); free(gNB->srs); for (int i = 0; i < gNB->max_nb_pusch; i++) diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch.c index 2d7727b55a..51d885cc1e 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch.c @@ -11,30 +11,6 @@ #include "PHY/NR_TRANSPORT/nr_ulsch.h" #include "SCHED_NR/sched_nr.h" -static NR_gNB_ULSCH_t *find_nr_ulsch(PHY_VARS_gNB *gNB, uint16_t rnti, int pid) -{ - int16_t first_free_index = -1; - AssertFatal(gNB != NULL, "gNB is null\n"); - NR_gNB_ULSCH_t *ulsch = NULL; - - for (int i = 0; i < gNB->max_nb_pusch; i++) { - ulsch = &gNB->ulsch[i]; - AssertFatal(ulsch, "gNB->ulsch[%d] is null\n", i); - if (!ulsch->active) { - if (first_free_index == -1) - first_free_index = i; - } else { - // if there is already an active ULSCH for this RNTI and HARQ_PID - if ((ulsch->harq_pid == pid) && (ulsch->rnti == rnti)) - return ulsch; - } - } - if (first_free_index != -1) - ulsch = &gNB->ulsch[first_free_index]; - - return ulsch; -} - static void dump_pusch_pdu(int instance, int frame, int slot, nfapi_nr_pusch_pdu_t *pusch_pdu) { LOG_D(PHY, @@ -117,49 +93,28 @@ static void dump_pusch_pdu(int instance, int frame, int slot, nfapi_nr_pusch_pdu void nr_fill_ulsch(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_pusch_pdu_t *ulsch_pdu) { dump_pusch_pdu(gNB->Mod_id, frame, slot, ulsch_pdu); - int harq_pid = ulsch_pdu->pusch_data.harq_process_id; - NR_gNB_ULSCH_t *ulsch = find_nr_ulsch(gNB, ulsch_pdu->rnti, harq_pid); - if (ulsch == NULL) { - LOG_E(NR_PHY, "No ulsch_id found for rnti %04x\n", ulsch_pdu->rnti); - return; - } + LOG_D(NR_PHY, + "%4d.%2d Programming ULSCH RNTI %04x HARQ PID %d new data indicator %d\n", + frame, + slot, + ulsch_pdu->rnti, + ulsch_pdu->pusch_data.harq_process_id, + ulsch_pdu->pusch_data.new_data_indicator); - ulsch->rnti = ulsch_pdu->rnti; - ulsch->harq_pid = harq_pid; - ulsch->handled = 0; - ulsch->active = true; - ulsch->beam_nb = 0; + NR_gNB_PUSCH_job_t pusch = {.frame = frame, .slot = slot, .pusch_pdu = *ulsch_pdu}; if (gNB->common_vars.beam_id) { int fapi_beam_idx = ulsch_pdu->beamforming.prgs_list[0].dig_bf_interface_list[0].beam_idx; int bitmap = SL_to_bitmap(ulsch_pdu->start_symbol_index, ulsch_pdu->nr_of_symbols); - ulsch->beam_nb = beam_index_allocation(gNB->enable_analog_das, + pusch.beam_nb = beam_index_allocation(gNB->enable_analog_das, fapi_beam_idx, &gNB->common_vars, slot, gNB->frame_parms.symbols_per_slot, bitmap); } - ulsch->frame = frame; - ulsch->slot = slot; - - NR_UL_gNB_HARQ_t *harq = ulsch->harq_process; - if (ulsch_pdu->pusch_data.new_data_indicator) - harq->harq_to_be_cleared = true; - LOG_D(NR_PHY, - "NEW ULSCH %d.%d RNTI %x HARQ PID %d new data indicator %d\n", - frame, - slot, - ulsch_pdu->rnti, - harq_pid, - ulsch_pdu->pusch_data.new_data_indicator); - - if (ulsch_pdu->pusch_data.new_data_indicator) - harq->round = 0; - else - harq->round++; - - memcpy(&ulsch->harq_process->ulsch_pdu, ulsch_pdu, sizeof(ulsch->harq_process->ulsch_pdu)); - LOG_D(PHY, "Initializing nFAPI for ULSCH, harq_pid %d, layers %d\n", harq_pid, ulsch_pdu->nrOfLayers); + bool done = spsc_q_put(&gNB->pusch_queue, &pusch, sizeof(pusch)); + if (!done) + LOG_W(NR_PHY, "PUSCH queue is full: dropping PUSCH UE %04x\n", ulsch_pdu->rnti); } void reset_active_ulsch(PHY_VARS_gNB *gNB, int frame) diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index 8ab19981dd..6c805b5d16 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -128,7 +128,7 @@ typedef struct { typedef struct { /// Nfapi ULSCH PDU - nfapi_nr_pusch_pdu_t ulsch_pdu; + nfapi_nr_pusch_pdu_t ulsch_pdu; // !! /// Index of current HARQ round for this DLSCH uint8_t round; bool new_rx; @@ -183,10 +183,19 @@ typedef struct { int8_t last_iteration_cnt; /// Status Flag indicating for this ULSCH bool active; - /// Flag to indicate that the UL configuration has been handled. Used to remove a stale ULSCH when frame wraps around - uint8_t handled; } NR_gNB_ULSCH_t; +typedef struct { + // identifier for concurrent beams + int beam_nb; + /// Frame where current PUSCH pdu was sent + uint32_t frame; + /// Slot where current PUSCH pdu was sent + uint32_t slot; + /// ULSCH PDU + nfapi_nr_pusch_pdu_t pusch_pdu; +} NR_gNB_PUSCH_job_t; + typedef struct { // identifier for concurrent beams int beam_nb; @@ -379,6 +388,7 @@ typedef struct PHY_VARS_gNB_s { NR_gNB_PRS prs_vars; NR_gNB_PUSCH *pusch_vars; spsc_q_t pucch_queue; + spsc_q_t pusch_queue; NR_gNB_SRS_t *srs; NR_gNB_ULSCH_t *ulsch; NR_gNB_PHY_STATS_t phy_stats[MAX_MOBILES_PER_GNB]; diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index dc74669532..1f330ae58e 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -450,7 +450,6 @@ static int nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, int pusch_pdu->pusch_data.tb_size); nr_fill_indication(gNB, ulsch->frame, ulsch->slot, pusch, pusch_pdu, stats, NULL, 0, crc, pdu); gNBdumpScopeData(gNB, ulsch->slot, ulsch->frame, "ULSCH_NACK"); - ulsch->handled = 1; LOG_D(PHY, "ULSCH %d in error\n",ULSCH_id); ulsch->last_iteration_cnt = ulsch->max_ldpc_iterations; // Setting to max_ldpc_iterations is sufficient given that this variable is only used for checking for failure } @@ -535,7 +534,9 @@ static void fill_ul_rb_mask(PHY_VARS_gNB *gNB, uint32_t rb_mask_ul[14][9], nfapi_nr_max_num_of_symbol_per_slot_t *slot_conf, const NR_gNB_PUCCH_job_t *pucch, - int n_pucch) + int n_pucch, + const NR_gNB_PUSCH_job_t *pusch, + int n_pusch) { for (int symbol = 0; symbol < 14; symbol++) { for (int m = 0; m < 9; m++) { @@ -565,19 +566,15 @@ static void fill_ul_rb_mask(PHY_VARS_gNB *gNB, } } - for (int ULSCH_id = 0; ULSCH_id < gNB->max_nb_pusch; ULSCH_id++) { - NR_gNB_ULSCH_t *ulsch = &gNB->ulsch[ULSCH_id]; - NR_UL_gNB_HARQ_t *ulsch_harq = ulsch->harq_process; - AssertFatal(ulsch_harq != NULL, "harq_pid %d is not allocated\n", ulsch->harq_pid); - if (!(ulsch->active && ulsch->frame == now.f && ulsch->slot == now.s && !ulsch->handled)) - continue; - uint8_t symbol_start = ulsch_harq->ulsch_pdu.start_symbol_index; - uint8_t symbol_end = symbol_start + ulsch_harq->ulsch_pdu.nr_of_symbols; + for (int i = 0; i < n_pusch; i++) { + const nfapi_nr_pusch_pdu_t *pusch_pdu = &pusch[i].pusch_pdu; + uint8_t symbol_start = pusch_pdu->start_symbol_index; + uint8_t symbol_end = symbol_start + pusch_pdu->nr_of_symbols; for (int symbol = symbol_start; symbol < symbol_end; symbol++) { if (gNB->frame_parms.frame_type == FDD || (gNB->frame_parms.frame_type == TDD && slot_conf[symbol].slot_config.value == 1)) { - LOG_D(PHY, "symbol %d Filling rb_mask_ul rb_size %d\n", symbol, ulsch_harq->ulsch_pdu.rb_size); - for (int rb = 0; rb < ulsch_harq->ulsch_pdu.rb_size; rb++) { - int rb2 = rb + ulsch_harq->ulsch_pdu.rb_start + ulsch_harq->ulsch_pdu.bwp_start; + LOG_D(PHY, "symbol %d Filling rb_mask_ul rb_size %d\n", symbol, pusch_pdu->rb_size); + for (int rb = 0; rb < pusch_pdu->rb_size; rb++) { + int rb2 = rb + pusch_pdu->rb_start + pusch_pdu->bwp_start; rb_mask_ul[symbol][rb2 >> 5] |= 1U << (rb2 & 31); } } @@ -1024,10 +1021,74 @@ static bool get_current_pucch(const void *data, void *user) return fsn_equal(t, *now); } +static bool drop_old_pusch(const void *data, void *user) +{ + const NR_gNB_PUSCH_job_t *pusch = data; + const fsn_t *now = user; + const fsn_t t = {pusch->frame, pusch->slot, now->mu}; + bool drop = fsn_in_the_past(t, *now); + if (drop) + LOG_E(NR_PHY, "%4d.%2d PUSCH job for UE %04x is in the past (%4d.%2d)\n", now->f, now->s, pusch->pusch_pdu.rnti, t.f, t.s); + return drop; +} + +static bool get_current_pusch(const void *data, void *user) +{ + const NR_gNB_PUSCH_job_t *pusch = data; + const fsn_t *now = user; + const fsn_t t = {pusch->frame, pusch->slot, now->mu}; + return fsn_equal(t, *now); +} + +static int find_nr_ulsch_idx(PHY_VARS_gNB *gNB, uint16_t rnti, int pid) +{ + AssertFatal(gNB != NULL, "gNB is null\n"); + + int16_t first_free_index = -1; + for (int i = 0; i < gNB->max_nb_pusch; i++) { + const NR_gNB_ULSCH_t *ulsch = &gNB->ulsch[i]; + AssertFatal(ulsch, "gNB->ulsch[%d] is null\n", i); + if (!ulsch->active) { + if (first_free_index == -1) + first_free_index = i; + } else { + // if there is already an active ULSCH for this RNTI and HARQ_PID + if ((ulsch->harq_pid == pid) && (ulsch->rnti == rnti)) + return i; + } + } + return first_free_index; +} +static int handle_pusch_job_trigger(PHY_VARS_gNB *gNB, const NR_gNB_PUSCH_job_t *job) +{ + const nfapi_nr_pusch_pdu_t *pdu = &job->pusch_pdu; + int pid = pdu->pusch_data.harq_process_id; + int ULSCH_id = find_nr_ulsch_idx(gNB, pdu->rnti, pid); + if (ULSCH_id < 0) { + LOG_E(NR_PHY, "%4d.%2d cannot handle PUSCH job of RNTI %04x: no space\n", job->frame, job->slot, pdu->rnti); + return -1; + } + /* (re-)initialize this PUSCH context from job data */ + NR_gNB_ULSCH_t *ulsch = &gNB->ulsch[ULSCH_id]; + ulsch->active = true; + ulsch->frame = job->frame; + ulsch->slot = job->slot; + ulsch->rnti = pdu->rnti; + ulsch->harq_pid = pid; + ulsch->beam_nb = job->beam_nb; + ulsch->harq_process->ulsch_pdu = job->pusch_pdu; + if (pdu->pusch_data.new_data_indicator) { + ulsch->harq_process->harq_to_be_cleared = true; + ulsch->harq_process->round = 0; + } else { + ulsch->harq_process->round++; + } + return ULSCH_id; +} + int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, NR_UL_IND_t *UL_INFO) { /* those variables to log T_GNB_PHY_PUCCH_PUSCH_IQ only when we try to decode */ - int pusch_decode_done = 0; int pusch_DTX = 0; const NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms; @@ -1040,6 +1101,10 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N NR_gNB_PUCCH_job_t pucch[MAX_NUM_NR_UCI_PDUS]; int n_pucch = spsc_q_get_while(&gNB->pucch_queue, get_current_pucch, &now, pucch, sizeof(*pucch), MAX_NUM_NR_UCI_PDUS); + spsc_q_drop_while(&gNB->pusch_queue, drop_old_pusch, &now); + NR_gNB_PUSCH_job_t pusch[MAX_UL_PDUS_PER_SLOT]; + int n_pusch_jobs = spsc_q_get_while(&gNB->pusch_queue, get_current_pusch, &now, pusch, sizeof(*pusch), MAX_UL_PDUS_PER_SLOT); + LOG_D(PHY,"phy_procedures_gNB_uespec_RX frame %d, slot %d\n",frame_rx,slot_rx); { // Mask of occupied RBs, per symbol and PRB @@ -1047,7 +1112,7 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N nfapi_nr_max_num_of_symbol_per_slot_t *slot_conf = NULL; if (frame_parms->frame_type == TDD) slot_conf = gNB->gNB_config.tdd_table.max_tdd_periodicity_list[slot_rx].max_num_of_symbol_per_slot_list; - fill_ul_rb_mask(gNB, now, rb_mask_ul, slot_conf, pucch, n_pucch); + fill_ul_rb_mask(gNB, now, rb_mask_ul, slot_conf, pucch, n_pucch, pusch, n_pusch_jobs); int first_symb = 0, num_symb = 0; if (frame_parms->frame_type == TDD) @@ -1080,17 +1145,14 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N UL_INFO->rx_ind.sfn = frame_rx; UL_INFO->rx_ind.slot = slot_rx; UL_INFO->rx_ind.pdu_list = UL_INFO->rx_pdu_list; + int ulsch_idx_to_decode[MAX_UL_PDUS_PER_SLOT]; int num_pusch = 0; - int ulsch_idx_to_decode[gNB->max_nb_pusch]; - for (int ULSCH_id = 0; ULSCH_id < gNB->max_nb_pusch; ULSCH_id++) { - ulsch_idx_to_decode[ULSCH_id] = -1; - NR_gNB_ULSCH_t *ulsch = &gNB->ulsch[ULSCH_id]; - if (!(ulsch->active && ulsch->frame == frame_rx && ulsch->slot == slot_rx && !ulsch->handled)) + for (int i = 0; i < n_pusch_jobs; ++i) { + int ULSCH_id = handle_pusch_job_trigger(gNB, &pusch[i]); + if (ULSCH_id < 0) continue; - - pusch_decode_done = 1; NR_gNB_PUSCH *pusch_vars = &gNB->pusch_vars[ULSCH_id]; - + NR_gNB_ULSCH_t *ulsch = &gNB->ulsch[ULSCH_id]; if (handle_pusch_decode_trigger(gNB, pusch_vars, ulsch, UL_INFO, &pusch_DTX)) ulsch_idx_to_decode[num_pusch++] = ULSCH_id; } @@ -1316,7 +1378,7 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N stop_meas(&gNB->phy_proc_rx); - if (n_pucch > 0 || pusch_decode_done) { + if (n_pucch > 0 || num_pusch > 0) { T(T_GNB_PHY_PUCCH_PUSCH_IQ, T_INT(frame_rx), T_INT(slot_rx), From 2213235fc103ac95eda6fb880a98e79c7f2af6e6 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 9 Apr 2026 10:31:56 +0200 Subject: [PATCH 20/29] Simplify UE SRS: no global memory required Signed-off-by: Robert Schmidt --- openair1/PHY/INIT/nr_init_ue.c | 4 --- .../NR_UE_TRANSPORT/nr_transport_proto_ue.h | 10 ++++---- openair1/PHY/defs_nr_UE.h | 3 --- openair1/SCHED_NR_UE/phy_procedures_nr_ue.c | 25 ++++++++----------- openair1/SIMULATION/NR_PHY/srssim.c | 8 +----- 5 files changed, 16 insertions(+), 34 deletions(-) diff --git a/openair1/PHY/INIT/nr_init_ue.c b/openair1/PHY/INIT/nr_init_ue.c index d7d73e1c01..cccf4cf3a8 100644 --- a/openair1/PHY/INIT/nr_init_ue.c +++ b/openair1/PHY/INIT/nr_init_ue.c @@ -251,8 +251,6 @@ int init_nr_ue_signal(PHY_VARS_NR_UE *ue, int nb_connected_gNB) ue->nr_csi_info->csi_rs_generated_signal[i] = malloc16_clear(fp->samples_per_slot_wCP * sizeof(**ue->nr_csi_info->csi_rs_generated_signal)); } - - ue->nr_srs_info = malloc16_clear(sizeof(nr_srs_info_t)); } ue->init_averaging = 1; @@ -301,8 +299,6 @@ void term_nr_ue_signal(PHY_VARS_NR_UE *ue) free_and_zero(ue->nr_csi_info->csi_rs_generated_signal); free_and_zero(ue->nr_csi_info); - free_and_zero(ue->nr_srs_info); - free_and_zero(ue->prach_vars[gNB_id]); } diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h index 32d03c1010..7de87cc2ee 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h @@ -148,11 +148,11 @@ void nr_tx_rotation_and_ofdm_mod(const uint8_t slot, bool was_symbol_used[NR_SYMBOLS_PER_SLOT], bool no_phase_pre_comp); -bool ue_srs_procedures_nr(PHY_VARS_NR_UE *ue, - const UE_nr_rxtx_proc_t *proc, - c16_t **txdataF, - nr_phy_data_tx_t *phy_data, - bool was_symbol_used[NR_SYMBOLS_PER_SLOT]); +void ue_srs_procedures_nr(PHY_VARS_NR_UE *ue, + const UE_nr_rxtx_proc_t *proc, + c16_t **txdataF, + const fapi_nr_ul_config_srs_pdu *srs_config_pdu, + bool was_symbol_used[NR_SYMBOLS_PER_SLOT]); void clean_UE_harq(PHY_VARS_NR_UE *UE); diff --git a/openair1/PHY/defs_nr_UE.h b/openair1/PHY/defs_nr_UE.h index 63771f7125..37a4269ee2 100644 --- a/openair1/PHY/defs_nr_UE.h +++ b/openair1/PHY/defs_nr_UE.h @@ -402,9 +402,6 @@ typedef struct PHY_VARS_NR_UE_s { nrLDPC_coding_interface_t nrLDPC_coding_interface; uint8_t max_ldpc_iterations; - /// SRS variables - nr_srs_info_t *nr_srs_info; - /// CSI variables nr_csi_info_t *nr_csi_info; diff --git a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c index 225634937b..f9c8f38b56 100644 --- a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c +++ b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c @@ -243,7 +243,7 @@ void ue_ta_procedures(PHY_VARS_NR_UE *ue, int slot_tx, int frame_tx) } } -static void configure_srs_info(fapi_nr_ul_config_srs_pdu *srs_config_pdu, nr_srs_info_t *nr_srs_info) +static void configure_srs_info(const fapi_nr_ul_config_srs_pdu *srs_config_pdu, nr_srs_info_t *nr_srs_info) { nr_srs_info->B_SRS = srs_config_pdu->bandwidth_index; nr_srs_info->C_SRS = srs_config_pdu->config_index; @@ -272,25 +272,17 @@ static void configure_srs_info(fapi_nr_ul_config_srs_pdu *srs_config_pdu, nr_srs * PARAMETERS : pointer to ue context * pointer to rxtx context* * -* RETURN : 0 if it is a valid slot for transmitting srs -* -1 if srs should not be transmitted -* * DESCRIPTION : ue srs procedure * send srs according to current configuration * *********************************************************************/ -bool ue_srs_procedures_nr(PHY_VARS_NR_UE *ue, +void ue_srs_procedures_nr(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, c16_t **txdataF, - nr_phy_data_tx_t *phy_data, + const fapi_nr_ul_config_srs_pdu *srs_config_pdu, bool was_symbol_used[NR_SYMBOLS_PER_SLOT]) { - if(phy_data->srs_vars.active == false) { - return false; - } - NR_DL_FRAME_PARMS *frame_parms = &(ue->frame_parms); - fapi_nr_ul_config_srs_pdu *srs_config_pdu = &phy_data->srs_vars.srs_config_pdu; const uint8_t l0 = frame_parms->symbols_per_slot - 1 - srs_config_pdu->time_start_position; // Num consecutive SRS symbols according to 38.211 6.4.1.4.1 int num_srs_symbols[] = {1, 2, 4, 8, 12}; @@ -326,18 +318,20 @@ bool ue_srs_procedures_nr(PHY_VARS_NR_UE *ue, LOG_I(NR_PHY,"srs_config_pdu->t_offset = %u\n", srs_config_pdu->t_offset); #endif - configure_srs_info(srs_config_pdu, ue->nr_srs_info); + nr_srs_info_t nr_srs_info = {0}; + configure_srs_info(srs_config_pdu, &nr_srs_info); uint16_t symbol_offset = l0 * frame_parms->ofdm_symbol_size; bool generated = generate_srs_nr(frame_parms, txdataF, symbol_offset, srs_config_pdu->bwp_start, - ue->nr_srs_info, + &nr_srs_info, AMP, proc->frame_tx, proc->nr_slot_tx, frame_parms->nb_antennas_tx); - return generated; + DevAssert(generated); // if we can't generate despite the SRS config, there + // is a problem } void phy_procedures_nrUE_TX(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr_phy_data_tx_t *phy_data, c16_t **txp) @@ -365,7 +359,8 @@ void phy_procedures_nrUE_TX(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, n nr_ue_ulsch_procedures(ue, frame_tx, slot_tx, phy_data, (c16_t **)&txdataF, was_symbol_used); - ue_srs_procedures_nr(ue, proc, (c16_t **)&txdataF, phy_data, was_symbol_used); + if (phy_data->srs_vars.active) + ue_srs_procedures_nr(ue, proc, (c16_t **)&txdataF, &phy_data->srs_vars.srs_config_pdu, was_symbol_used); pucch_procedures_ue_nr(ue, proc, phy_data, (c16_t **)&txdataF, was_symbol_used); diff --git a/openair1/SIMULATION/NR_PHY/srssim.c b/openair1/SIMULATION/NR_PHY/srssim.c index afb4da5a80..6fcc763970 100644 --- a/openair1/SIMULATION/NR_PHY/srssim.c +++ b/openair1/SIMULATION/NR_PHY/srssim.c @@ -467,11 +467,6 @@ int main(int argc, char *argv[]) .beamforming.num_prgs = srs_pdu.beamforming.num_prgs, .beamforming.prg_size = srs_pdu.beamforming.prg_size}; - nr_phy_data_tx_t phy_data = {0}; - phy_data.srs_vars.active = true; - phy_data.srs_vars.srs_config_pdu = srs_config_pdu; - UE->nr_srs_info = malloc16_clear(sizeof(nr_srs_info_t)); - //----------- UE TX SRS procedures --------------------- UE_nr_rxtx_proc_t proc; @@ -492,7 +487,7 @@ int main(int argc, char *argv[]) for (i = 0; i < n_tx; i++) txd[i] = txdata[i] + slot_offset; - ue_srs_procedures_nr(UE, &proc, (c16_t **)txF, &phy_data, was_symbol_used); + ue_srs_procedures_nr(UE, &proc, (c16_t **)txF, &srs_config_pdu, was_symbol_used); //------------ TX rotation and OFDM Modulation -------------------------- nr_tx_rotation_and_ofdm_mod(slot, fp, n_tx, txF, txd, link_type_ul, was_symbol_used, false); @@ -666,7 +661,6 @@ int main(int argc, char *argv[]) phy_free_nr_gNB(gNB); free_channel_desc_scm(UE2gNB); - free_and_zero(UE->nr_srs_info); free(gNB->RU_list[0]); free(UE); From 4a47fbbef7639c922eb79d14b455c11bcebfc13a Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 9 Apr 2026 10:46:18 +0200 Subject: [PATCH 21/29] Remove global gNB SRS memory This means that as of this commit, the signal has to be permanently regenerated. In fact, with the SRS queue to be introduced in a follow-up commit, we don't have an easy way to cache the sequence generation, as there is no "index" to which we could refer to (hence, caching would be tricky). Instead, the next commits further refactor this to reduce the time for SRS sequence generation as well as the memory footprint. Signed-off-by: Robert Schmidt --- openair1/PHY/INIT/nr_init.c | 19 ------ .../PHY/NR_TRANSPORT/nr_transport_proto.h | 3 +- openair1/PHY/defs_gNB.h | 3 - openair1/PHY/defs_nr_common.h | 2 - openair1/SCHED_NR/phy_procedures_nr_gNB.c | 63 ++++++------------- openair1/SIMULATION/NR_PHY/srssim.c | 1 - 6 files changed, 21 insertions(+), 70 deletions(-) diff --git a/openair1/PHY/INIT/nr_init.c b/openair1/PHY/INIT/nr_init.c index 5845b14d51..7d56f8b410 100644 --- a/openair1/PHY/INIT/nr_init.c +++ b/openair1/PHY/INIT/nr_init.c @@ -141,16 +141,6 @@ void phy_init_nr_gNB(PHY_VARS_gNB *gNB) init_DLSCH_struct(gNB); - gNB->nr_srs_info = (nr_srs_info_t **)malloc16_clear(gNB->max_nb_srs * sizeof(nr_srs_info_t*)); - for (int id = 0; id < gNB->max_nb_srs; id++) { - gNB->nr_srs_info[id] = (nr_srs_info_t *)malloc16_clear(sizeof(nr_srs_info_t)); - gNB->nr_srs_info[id]->srs_generated_signal = malloc16_clear(MAX_NUM_NR_SRS_AP * sizeof(c16_t *)); - for(int ap=0; apnr_srs_info[id]->srs_generated_signal[ap] = - malloc16_clear(fp->ofdm_symbol_size * MAX_NUM_NR_SRS_SYMBOLS * sizeof(c16_t)); - } - } - /* Do NOT allocate per-antenna rxdataF: the gNB gets a pointer to the * RU to copy/recover freq-domain memory from there */ common_vars->rxdataF = (c16_t ***)malloc16(common_vars->num_beams_period * sizeof(c16_t**)); @@ -212,15 +202,6 @@ void phy_free_nr_gNB(PHY_VARS_gNB *gNB) PHY_MEASUREMENTS_gNB *meas = &gNB->measurements; free_and_zero(meas->n0_subband_power); - for (int id = 0; id < gNB->max_nb_srs; id++) { - for(int i=0; inr_srs_info[id]->srs_generated_signal[i]); - } - free_and_zero(gNB->nr_srs_info[id]->srs_generated_signal); - free_and_zero(gNB->nr_srs_info[id]); - } - free_and_zero(gNB->nr_srs_info); - free_ul_reference_signal_sequences(); free_gnb_lowpapr_sequences(); diff --git a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h index 43ecb0d006..d1431b6237 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h +++ b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h @@ -202,7 +202,7 @@ int nr_get_srs_signal(PHY_VARS_gNB *gNB, c16_t srs_received_signal[][gNB->frame_parms.ofdm_symbol_size * (1 << srs_pdu->num_symbols)], c16_t srs_received_noise[][gNB->frame_parms.ofdm_symbol_size * (1 << srs_pdu->num_symbols)]); -void nr_srs_rx_procedures(PHY_VARS_gNB *gNB, +nr_srs_info_t nr_srs_rx_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, uint8_t nb_antennas_rx, @@ -210,7 +210,6 @@ void nr_srs_rx_procedures(PHY_VARS_gNB *gNB, uint8_t N_symb_SRS, uint16_t ofdm_symbol_size, NR_gNB_SRS_t *srs, - nr_srs_info_t *nr_srs_info, int *srs_est, c16_t srs_estimated_channel_freq[][N_ap][ofdm_symbol_size * N_symb_SRS], c16_t srs_estimated_channel_time[][N_ap][NR_SRS_IDFT_OVERSAMP_FACTOR * ofdm_symbol_size], diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index 6c805b5d16..ef357508d7 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -394,9 +394,6 @@ typedef struct PHY_VARS_gNB_s { NR_gNB_PHY_STATS_t phy_stats[MAX_MOBILES_PER_GNB]; t_nrPolar_params **polarParams; - /// SRS variables - nr_srs_info_t **nr_srs_info; - // reference amplitude for TX int16_t TX_AMP; diff --git a/openair1/PHY/defs_nr_common.h b/openair1/PHY/defs_nr_common.h index 7cd439bcee..8b50f36290 100644 --- a/openair1/PHY/defs_nr_common.h +++ b/openair1/PHY/defs_nr_common.h @@ -77,8 +77,6 @@ typedef enum{ typedef struct { uint8_t k_0_p[MAX_NUM_NR_SRS_AP][MAX_NUM_NR_SRS_SYMBOLS]; uint8_t srs_generated_signal_bits; - c16_t **srs_generated_signal; - bool is_signal_generated; int B_SRS; int C_SRS; int b_hop; diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index 1f330ae58e..59285dd884 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -696,37 +696,7 @@ static void copy_srs_info(const nfapi_nr_srs_pdu_t *srs_config_pdu, nr_srs_info_ nr_srs_info->resource_type = srs_config_pdu->resource_type; } -static bool srs_has_changed(const nfapi_nr_srs_pdu_t *srs_config_pdu, nr_srs_info_t *nr_srs_info) -{ - return nr_srs_info->B_SRS != srs_config_pdu->bandwidth_index - || nr_srs_info->C_SRS != srs_config_pdu->config_index - || nr_srs_info->b_hop != srs_config_pdu->frequency_hopping - || nr_srs_info->comb_size != srs_config_pdu->comb_size - || nr_srs_info->K_TC_overbar != srs_config_pdu->comb_offset - || nr_srs_info->n_SRS_cs != srs_config_pdu->cyclic_shift - || nr_srs_info->n_ID_SRS != srs_config_pdu->sequence_id - || nr_srs_info->n_shift != srs_config_pdu->frequency_position - || nr_srs_info->n_RRC != srs_config_pdu->frequency_shift - || nr_srs_info->groupOrSequenceHopping != srs_config_pdu->group_or_sequence_hopping - || nr_srs_info->l_offset != srs_config_pdu->time_start_position - || nr_srs_info->T_SRS != srs_config_pdu->t_srs - || nr_srs_info->T_offset != srs_config_pdu->t_offset - || nr_srs_info->R != 1 << srs_config_pdu->num_repetitions - || nr_srs_info->N_symb_SRS != 1 << srs_config_pdu->num_symbols - || nr_srs_info->n_srs_ports != 1 << srs_config_pdu->num_ant_ports - || nr_srs_info->resource_type != srs_config_pdu->resource_type; -} - -static bool check_and_configure_srs_info(nfapi_nr_srs_pdu_t *srs_config_pdu, nr_srs_info_t *nr_srs_info) -{ - if (srs_has_changed(srs_config_pdu, nr_srs_info)) { - copy_srs_info(srs_config_pdu, nr_srs_info); - return true; - } - return false; -} - -void nr_srs_rx_procedures(PHY_VARS_gNB *gNB, +nr_srs_info_t nr_srs_rx_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, uint8_t nb_antennas_rx, @@ -734,7 +704,6 @@ void nr_srs_rx_procedures(PHY_VARS_gNB *gNB, uint8_t N_symb_SRS, uint16_t ofdm_symbol_size, NR_gNB_SRS_t *srs, - nr_srs_info_t *nr_srs_info, int *srs_est, c16_t srs_estimated_channel_freq[][N_ap][ofdm_symbol_size * N_symb_SRS], c16_t srs_estimated_channel_time[][N_ap][NR_SRS_IDFT_OVERSAMP_FACTOR * ofdm_symbol_size], @@ -750,12 +719,20 @@ void nr_srs_rx_procedures(PHY_VARS_gNB *gNB, start_meas(&gNB->generate_srs_stats); - if (check_and_configure_srs_info(srs_pdu, nr_srs_info)) { + nr_srs_info_t nr_srs_info = {0}; + copy_srs_info(srs_pdu, &nr_srs_info); + + // TODO permanently allocate? + c16_t srs_generated_signal_tmp[N_ap][ofdm_symbol_size * MAX_NUM_NR_SRS_SYMBOLS]; + c16_t *srs_generated_signal[N_ap]; + for (int i = 0; i < N_ap; ++i) + srs_generated_signal[i] = srs_generated_signal_tmp[i]; + if (true) { generate_srs_nr(frame_parms, - nr_srs_info->srs_generated_signal, + srs_generated_signal, 0, srs_pdu->bwp_start, - nr_srs_info, + &nr_srs_info, AMP, frame_rx, slot_rx, @@ -765,7 +742,7 @@ void nr_srs_rx_procedures(PHY_VARS_gNB *gNB, stop_meas(&gNB->generate_srs_stats); c16_t **rxdataF = gNB->common_vars.rxdataF[srs->beam_nb]; start_meas(&gNB->get_srs_signal_stats); - *srs_est = nr_get_srs_signal(gNB, rxdataF, slot_rx, srs_pdu, nr_srs_info, srs_received_signal, srs_received_noise); + *srs_est = nr_get_srs_signal(gNB, rxdataF, slot_rx, srs_pdu, &nr_srs_info, srs_received_signal, srs_received_noise); stop_meas(&gNB->get_srs_signal_stats); uint32_t signal_power_avg = 0; @@ -784,8 +761,8 @@ void nr_srs_rx_procedures(PHY_VARS_gNB *gNB, frame_parms->first_carrier_offset, N_symb_SRS, srs_pdu, - nr_srs_info, - nr_srs_info->srs_generated_signal[p_ind], + &nr_srs_info, + srs_generated_signal[p_ind], srs_received_signal[ant_rx_ind], srs_ls_estimated_channel[ant_rx_ind][p_ind], &delay_aux); @@ -803,7 +780,7 @@ void nr_srs_rx_procedures(PHY_VARS_gNB *gNB, frame_parms->first_carrier_offset, N_symb_SRS, srs_pdu, - nr_srs_info, + &nr_srs_info, srs_ls_estimated_channel[ant_rx_ind][p_ind], delay.est_delay, srs_received_noise[ant_rx_ind], @@ -848,7 +825,7 @@ void nr_srs_rx_procedures(PHY_VARS_gNB *gNB, frame_parms->first_carrier_offset, N_symb_SRS, srs_pdu, - nr_srs_info, + &nr_srs_info, signal_power_avg, srs_received_noise[ant_rx_ind], &noise_power_per_ant, @@ -892,6 +869,7 @@ void nr_srs_rx_procedures(PHY_VARS_gNB *gNB, T_INT(slot_rx), T_BUFFER(timing_advance_offset_nsec, nb_antennas_rx * sizeof(int16_t))); } + return nr_srs_info; } static void handle_pucch(PHY_VARS_gNB *gNB, c16_t **rxdataF, const NR_gNB_PUCCH_job_t *pucch, nfapi_nr_uci_t *uci) @@ -1197,7 +1175,7 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N c16_t srs_estimated_channel_time[nb_antennas_rx][N_ap][NR_SRS_IDFT_OVERSAMP_FACTOR * ofdm_symbol_size] __attribute__((aligned(32))); - nr_srs_rx_procedures(gNB, + nr_srs_info_t srs_info = nr_srs_rx_procedures(gNB, frame_rx, slot_rx, nb_antennas_rx, @@ -1205,7 +1183,6 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N N_symb_SRS, ofdm_symbol_size, srs, - gNB->nr_srs_info[i], &srs_est, srs_estimated_channel_freq, srs_estimated_channel_time, @@ -1308,7 +1285,7 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N nfapi_nr_srs_normalized_channel_iq_matrix_t nr_srs_channel_iq_matrix; fill_srs_channel_matrix(&nr_srs_channel_iq_matrix, srs_pdu, - gNB->nr_srs_info[i], + &srs_info, srs_pdu->srs_parameters_v4.iq_representation, nb_antennas_rx, srs_pdu->srs_parameters_v4.num_total_ue_antennas, diff --git a/openair1/SIMULATION/NR_PHY/srssim.c b/openair1/SIMULATION/NR_PHY/srssim.c index 6fcc763970..fc6186c0f1 100644 --- a/openair1/SIMULATION/NR_PHY/srssim.c +++ b/openair1/SIMULATION/NR_PHY/srssim.c @@ -577,7 +577,6 @@ int main(int argc, char *argv[]) N_symb_SRS, ofdm_symbol_size, srs, - gNB->nr_srs_info[0], &srs_est, srs_estimated_channel_freq, srs_estimated_channel_time, From cb4c3072216bc84f8e5e3230507b3437cb336c6f Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 10 Apr 2026 16:10:30 +0200 Subject: [PATCH 22/29] Speed up generate_srs_nr() - calculation in float: should be enough precision (it'll go into 16bit ints later...) - avoid round: the lower 15bits will be shifted away, the rounding will have no effect - group some multiplications to avoid re-computation of the same results. Signed-off-by: Robert Schmidt --- .../PHY/nr_phy_common/src/nr_phy_common_srs.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/openair1/PHY/nr_phy_common/src/nr_phy_common_srs.c b/openair1/PHY/nr_phy_common/src/nr_phy_common_srs.c index 76ad47a4e2..3bd4746c92 100644 --- a/openair1/PHY/nr_phy_common/src/nr_phy_common_srs.c +++ b/openair1/PHY/nr_phy_common/src/nr_phy_common_srs.c @@ -203,7 +203,7 @@ bool generate_srs_nr(const NR_DL_FRAME_PARMS *frame_parms, nr_srs_info->srs_generated_signal_bits = log2_approx(amp); } uint64_t subcarrier_offset = frame_parms->first_carrier_offset + bwp_start * NR_NB_SC_PER_RB; - double sqrt_N_ap = sqrt(N_ap); + float amp_sqrt_N_ap = amp / sqrt(N_ap); int n_b[nr_srs_info->B_SRS + 1]; // Find index of table which is for this SRS length @@ -298,14 +298,16 @@ bool generate_srs_nr(const NR_DL_FRAME_PARMS *frame_parms, uint16_t l_line_offset = l_line * frame_parms->ofdm_symbol_size; // For each port, and for each OFDM symbol, here it is computed and mapped an SRS sequence with M_sc_b_SRS symbols for (int k = 0; k < M_sc_b_SRS; k++) { - cd_t shift = {cos(alpha_i * k), sin(alpha_i * k)}; - const c16_t tmp = rv_ul_ref_sig[u][v][M_sc_b_SRS_index][k]; - cd_t r_overbar = {tmp.r, tmp.i}; + cf_t shift = {cosf(alpha_i * k), sinf(alpha_i * k)}; + const c16_t r_overbar = rv_ul_ref_sig[u][v][M_sc_b_SRS_index][k]; // cos(x+y) = cos(x)cos(y) - sin(x)sin(y) - cd_t r = cdMul(shift, r_overbar); - c16_t r_amp = {(((int32_t)round((double)amp * r.r / sqrt_N_ap)) >> 15), - (((int32_t)round((double)amp * r.i / sqrt_N_ap)) >> 15)}; + cf_t r = (cf_t) { + .r = shift.r * r_overbar.r - shift.i * r_overbar.i, + .i = shift.r * r_overbar.i + shift.i * r_overbar.r, + }; + c16_t r_amp = {(((int32_t)(amp_sqrt_N_ap * r.r)) >> 15), + (((int32_t)(amp_sqrt_N_ap * r.i)) >> 15)}; #ifdef SRS_DEBUG int subcarrier_log = subcarrier-subcarrier_offset; From 81b2fe634fc71ea48f6a1d458fd356be4b949a82 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Sun, 29 Mar 2026 13:59:11 +0200 Subject: [PATCH 23/29] Simplify SRS handling Remove useless logging defines. Move srs_estimated_channel_time to where it is used. Prepare for the next commit by moving UL_INFO to earlier inside phy_procedures_gNB_uespec_RX(). Signed-off-by: Robert Schmidt --- .../PHY/NR_TRANSPORT/nr_transport_proto.h | 1 - openair1/SCHED_NR/phy_procedures_nr_gNB.c | 95 +++---------------- openair1/SIMULATION/NR_PHY/srssim.c | 2 - 3 files changed, 12 insertions(+), 86 deletions(-) diff --git a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h index d1431b6237..c2ec658036 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h +++ b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h @@ -212,7 +212,6 @@ nr_srs_info_t nr_srs_rx_procedures(PHY_VARS_gNB *gNB, NR_gNB_SRS_t *srs, int *srs_est, c16_t srs_estimated_channel_freq[][N_ap][ofdm_symbol_size * N_symb_SRS], - c16_t srs_estimated_channel_time[][N_ap][NR_SRS_IDFT_OVERSAMP_FACTOR * ofdm_symbol_size], int16_t *snr_per_rb, uint16_t *timing_advance_offset, int16_t *timing_advance_offset_nsec); diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index 59285dd884..8b37b3b8e8 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -706,13 +706,15 @@ nr_srs_info_t nr_srs_rx_procedures(PHY_VARS_gNB *gNB, NR_gNB_SRS_t *srs, int *srs_est, c16_t srs_estimated_channel_freq[][N_ap][ofdm_symbol_size * N_symb_SRS], - c16_t srs_estimated_channel_time[][N_ap][NR_SRS_IDFT_OVERSAMP_FACTOR * ofdm_symbol_size], int16_t *snr_per_rb, uint16_t *timing_advance_offset, int16_t *timing_advance_offset_nsec) { NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms; nfapi_nr_srs_pdu_t *srs_pdu = &srs->srs_pdu; + + c16_t srs_estimated_channel_time[nb_antennas_rx][N_ap][NR_SRS_IDFT_OVERSAMP_FACTOR * ofdm_symbol_size] + __attribute__((aligned(32))); c16_t srs_received_signal[nb_antennas_rx][ofdm_symbol_size * N_symb_SRS]; c16_t srs_received_noise[nb_antennas_rx][ofdm_symbol_size * N_symb_SRS]; c16_t srs_estimated_channel_time_shifted[nb_antennas_rx][N_ap][NR_SRS_IDFT_OVERSAMP_FACTOR * ofdm_symbol_size]; @@ -1163,17 +1165,20 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N start_meas(&gNB->rx_srs_stats); - uint8_t N_symb_SRS = 1 << srs->srs_pdu.num_symbols; - uint8_t N_ap = 1 << srs->srs_pdu.num_ant_ports; - int16_t snr_per_rb[srs->srs_pdu.bwp_size]; - nfapi_nr_srs_pdu_t *srs_pdu = &srs->srs_pdu; + UL_INFO->srs_ind.sfn = frame_rx; + UL_INFO->srs_ind.slot = slot_rx; + UL_INFO->srs_ind.pdu_list = UL_INFO->srs_pdu_list; + nfapi_nr_srs_indication_pdu_t *srs_indication = UL_INFO->srs_pdu_list + UL_INFO->srs_ind.number_of_pdus++; + + const nfapi_nr_srs_pdu_t *srs_pdu = &srs->srs_pdu; + uint8_t N_symb_SRS = 1 << srs_pdu->num_symbols; + uint8_t N_ap = 1 << srs_pdu->num_ant_ports; + int16_t snr_per_rb[srs_pdu->bwp_size]; uint16_t timing_advance_offset; int16_t timing_advance_offset_nsec[nb_antennas_rx]; int srs_est; c16_t srs_estimated_channel_freq[nb_antennas_rx][N_ap][ofdm_symbol_size * N_symb_SRS] __attribute__((aligned(32))); - c16_t srs_estimated_channel_time[nb_antennas_rx][N_ap][NR_SRS_IDFT_OVERSAMP_FACTOR * ofdm_symbol_size] - __attribute__((aligned(32))); nr_srs_info_t srs_info = nr_srs_rx_procedures(gNB, frame_rx, @@ -1185,7 +1190,6 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N srs, &srs_est, srs_estimated_channel_freq, - srs_estimated_channel_time, snr_per_rb, &timing_advance_offset, timing_advance_offset_nsec); @@ -1194,12 +1198,6 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N srs_est = -1; } - UL_INFO->srs_ind.sfn = frame_rx; - UL_INFO->srs_ind.slot = slot_rx; - - // data model difficult to understand, nfapi do malloc for this pointer - UL_INFO->srs_ind.pdu_list = UL_INFO->srs_pdu_list; - nfapi_nr_srs_indication_pdu_t *srs_indication = UL_INFO->srs_pdu_list + UL_INFO->srs_ind.number_of_pdus++; srs_indication->handle = srs_pdu->handle; srs_indication->rnti = srs_pdu->rnti; srs_indication->timing_advance_offset = srs_est >= 0 ? timing_advance_offset : 0xFFFF; @@ -1226,16 +1224,6 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N } srs_indication->report_type = srs_pdu->srs_parameters_v4.report_type[0]; -#ifdef SRS_IND_DEBUG - LOG_I(NR_PHY, "UL_INFO->srs_ind.sfn = %i\n", UL_INFO->srs_ind.sfn); - LOG_I(NR_PHY, "UL_INFO->srs_ind.slot = %i\n", UL_INFO->srs_ind.slot); - LOG_I(NR_PHY, "srs_indication->rnti = %04x\n", srs_indication->rnti); - LOG_I(NR_PHY, "srs_indication->timing_advance = %i\n", srs_indication->timing_advance_offset); - LOG_I(NR_PHY, "srs_indication->timing_advance_offset_nsec = %i\n", srs_indication->timing_advance_offset_nsec); - LOG_I(NR_PHY, "srs_indication->srs_usage = %i\n", srs_indication->srs_usage); - LOG_I(NR_PHY, "srs_indication->report_type = %i\n", srs_indication->report_type); -#endif - nfapi_srs_report_tlv_t *report_tlv = &srs_indication->report_tlv; report_tlv->tag = 0; report_tlv->length = 0; @@ -1255,26 +1243,6 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N nr_srs_bf_report.num_reported_symbols); fill_srs_reported_symbol(&nr_srs_bf_report.reported_symbol_list[0], srs_pdu, snr_per_rb, srs_est); -#ifdef SRS_IND_DEBUG - LOG_I(NR_PHY, "nr_srs_bf_report.prg_size = %i\n", nr_srs_bf_report.prg_size); - LOG_I(NR_PHY, "nr_srs_bf_report.num_symbols = %i\n", nr_srs_bf_report.num_symbols); - LOG_I(NR_PHY, - "nr_srs_bf_report.wide_band_snr = %i (%i dB)\n", - nr_srs_bf_report.wide_band_snr, - (nr_srs_bf_report.wide_band_snr >> 1) - 64); - LOG_I(NR_PHY, "nr_srs_bf_report.num_reported_symbols = %i\n", nr_srs_bf_report.num_reported_symbols); - LOG_I(NR_PHY, - "nr_srs_bf_report.reported_symbol_list[0].num_prgs = %i\n", - nr_srs_bf_report.reported_symbol_list[0].num_prgs); - for (int prg_idx = 0; prg_idx < nr_srs_bf_report.reported_symbol_list[0].num_prgs; prg_idx++) { - LOG_I(NR_PHY, - "nr_srs_beamforming_report.reported_symbol_list[0].prg_list[%3i].rb_snr = %i (%i dB)\n", - prg_idx, - nr_srs_bf_report.reported_symbol_list[0].prg_list[prg_idx].rb_snr, - (nr_srs_bf_report.reported_symbol_list[0].prg_list[prg_idx].rb_snr >> 1) - 64); - } -#endif - report_tlv->length = pack_nr_srs_beamforming_report(&nr_srs_bf_report, report_tlv->value, sizeof(report_tlv->value)); stop_meas(&gNB->srs_beam_report_stats); break; @@ -1294,37 +1262,6 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N frame_parms, srs_estimated_channel_freq); -#ifdef SRS_IND_DEBUG - LOG_I(NR_PHY, - "nr_srs_channel_iq_matrix.normalized_iq_representation = %i\n", - nr_srs_channel_iq_matrix.normalized_iq_representation); - LOG_I(NR_PHY, - "nr_srs_channel_iq_matrix.num_gnb_antenna_elements = %i\n", - nr_srs_channel_iq_matrix.num_gnb_antenna_elements); - LOG_I(NR_PHY, "nr_srs_channel_iq_matrix.num_ue_srs_ports = %i\n", nr_srs_channel_iq_matrix.num_ue_srs_ports); - LOG_I(NR_PHY, "nr_srs_channel_iq_matrix.prg_size = %i\n", nr_srs_channel_iq_matrix.prg_size); - LOG_I(NR_PHY, "nr_srs_channel_iq_matrix.num_prgs = %i\n", nr_srs_channel_iq_matrix.num_prgs); - c16_t *channel_matrix16 = (c16_t *)nr_srs_channel_iq_matrix.channel_matrix; - c8_t *channel_matrix8 = (c8_t *)nr_srs_channel_iq_matrix.channel_matrix; - for (int uI = 0; uI < nr_srs_channel_iq_matrix.num_ue_srs_ports; uI++) { - for (int gI = 0; gI < nr_srs_channel_iq_matrix.num_gnb_antenna_elements; gI++) { - for (int pI = 0; pI < nr_srs_channel_iq_matrix.num_prgs; pI++) { - uint16_t index = uI * nr_srs_channel_iq_matrix.num_gnb_antenna_elements * nr_srs_channel_iq_matrix.num_prgs - + gI * nr_srs_channel_iq_matrix.num_prgs + pI; - LOG_I( - NR_PHY, - "(uI %i, gI %i, pI %i) channel_matrix --> real %i, imag %i\n", - uI, - gI, - pI, - nr_srs_channel_iq_matrix.normalized_iq_representation == 0 ? channel_matrix8[index].r : channel_matrix16[index].r, - nr_srs_channel_iq_matrix.normalized_iq_representation == 0 ? channel_matrix8[index].i - : channel_matrix16[index].i); - } - } - } -#endif - report_tlv->length = pack_nr_srs_normalized_channel_iq_matrix(&nr_srs_channel_iq_matrix, report_tlv->value, sizeof(report_tlv->value)); stop_meas(&gNB->srs_iq_matrix_stats); @@ -1341,14 +1278,6 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N } stop_meas(&gNB->srs_report_tlv_stats); -#ifdef SRS_IND_DEBUG - LOG_I(NR_PHY, "report_tlv->tag = %i\n", report_tlv->tag); - LOG_I(NR_PHY, "report_tlv->length = %i\n", report_tlv->length); - char *value = (char *)report_tlv->value; - for (int b = 0; b < report_tlv->length; b++) { - LOG_I(NR_PHY, "value[%i] = 0x%02x\n", b, value[b] & 0xFF); - } -#endif srs->active = false; stop_meas(&gNB->rx_srs_stats); } diff --git a/openair1/SIMULATION/NR_PHY/srssim.c b/openair1/SIMULATION/NR_PHY/srssim.c index fc6186c0f1..6aa3ca5e81 100644 --- a/openair1/SIMULATION/NR_PHY/srssim.c +++ b/openair1/SIMULATION/NR_PHY/srssim.c @@ -567,7 +567,6 @@ int main(int argc, char *argv[]) int16_t timing_advance_offset_nsec[n_rx]; int srs_est; c16_t srs_estimated_channel_freq[n_rx][N_ap][ofdm_symbol_size * N_symb_SRS] __attribute__((aligned(32))); - c16_t srs_estimated_channel_time[n_rx][N_ap][NR_SRS_IDFT_OVERSAMP_FACTOR * ofdm_symbol_size] __attribute__((aligned(32))); nr_srs_rx_procedures(gNB, frame, @@ -579,7 +578,6 @@ int main(int argc, char *argv[]) srs, &srs_est, srs_estimated_channel_freq, - srs_estimated_channel_time, snr_per_rb, &timing_advance_offset, timing_advance_offset_nsec); From 2bb1f36a2cff1c27d575ca20a121ddc453992cdc Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Sun, 29 Mar 2026 15:03:38 +0200 Subject: [PATCH 24/29] Remove SNR parameter The structure in which was snr will be a "SRS job" in a follow-up commit, so the snr parameter cannot be stored in there (and it is also not necessary to do so, as it can be local to the function handling nr_srs_rx_procedures()). Correctly read SRS SNR through FAPI message in nr_ulsim. Switch to SRS BEAMMANAGEMENT as only this message returns the actual SNR (unlike CODEBOOK). Add asserts to check what is returned is valid data in the sense of the test. Signed-off-by: Robert Schmidt --- openair1/PHY/NR_TRANSPORT/nr_transport_proto.h | 5 +++-- openair1/PHY/NR_TRANSPORT/srs_rx.c | 2 +- openair1/PHY/defs_gNB.h | 2 -- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 13 ++++++++----- openair1/SIMULATION/NR_PHY/srssim.c | 4 +++- openair1/SIMULATION/NR_PHY/ulsim.c | 11 +++++++++-- 6 files changed, 24 insertions(+), 13 deletions(-) diff --git a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h index c2ec658036..54acd23351 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h +++ b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h @@ -197,7 +197,7 @@ void nr_fill_srs(PHY_VARS_gNB *gNB, int nr_get_srs_signal(PHY_VARS_gNB *gNB, c16_t **rxdataF, slot_t slot, - nfapi_nr_srs_pdu_t *srs_pdu, + const nfapi_nr_srs_pdu_t *srs_pdu, nr_srs_info_t *nr_srs_info, c16_t srs_received_signal[][gNB->frame_parms.ofdm_symbol_size * (1 << srs_pdu->num_symbols)], c16_t srs_received_noise[][gNB->frame_parms.ofdm_symbol_size * (1 << srs_pdu->num_symbols)]); @@ -209,8 +209,9 @@ nr_srs_info_t nr_srs_rx_procedures(PHY_VARS_gNB *gNB, uint8_t N_ap, uint8_t N_symb_SRS, uint16_t ofdm_symbol_size, - NR_gNB_SRS_t *srs, + const NR_gNB_SRS_t *srs, int *srs_est, + int8_t *snr, c16_t srs_estimated_channel_freq[][N_ap][ofdm_symbol_size * N_symb_SRS], int16_t *snr_per_rb, uint16_t *timing_advance_offset, diff --git a/openair1/PHY/NR_TRANSPORT/srs_rx.c b/openair1/PHY/NR_TRANSPORT/srs_rx.c index fb6f5f648e..96695909a9 100644 --- a/openair1/PHY/NR_TRANSPORT/srs_rx.c +++ b/openair1/PHY/NR_TRANSPORT/srs_rx.c @@ -59,7 +59,7 @@ void nr_fill_srs(PHY_VARS_gNB *gNB, frame_t frame, slot_t slot, nfapi_nr_srs_pdu int nr_get_srs_signal(PHY_VARS_gNB *gNB, c16_t **rxdataF, slot_t slot, - nfapi_nr_srs_pdu_t *srs_pdu, + const nfapi_nr_srs_pdu_t *srs_pdu, nr_srs_info_t *nr_srs_info, c16_t srs_received_signal[][gNB->frame_parms.ofdm_symbol_size * (1 << srs_pdu->num_symbols)], c16_t srs_received_noise[][gNB->frame_parms.ofdm_symbol_size * (1 << srs_pdu->num_symbols)]) diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index ef357508d7..20e820a298 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -215,8 +215,6 @@ typedef struct { uint32_t frame; /// Slot where current SRS pdu was received uint32_t slot; - /// Measured SNR - int8_t snr; /// ULSCH PDU nfapi_nr_srs_pdu_t srs_pdu; } NR_gNB_SRS_t; diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index 8b37b3b8e8..17eed001cb 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -703,15 +703,16 @@ nr_srs_info_t nr_srs_rx_procedures(PHY_VARS_gNB *gNB, uint8_t N_ap, uint8_t N_symb_SRS, uint16_t ofdm_symbol_size, - NR_gNB_SRS_t *srs, + const NR_gNB_SRS_t *srs, int *srs_est, + int8_t *snr, c16_t srs_estimated_channel_freq[][N_ap][ofdm_symbol_size * N_symb_SRS], int16_t *snr_per_rb, uint16_t *timing_advance_offset, int16_t *timing_advance_offset_nsec) { NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms; - nfapi_nr_srs_pdu_t *srs_pdu = &srs->srs_pdu; + const nfapi_nr_srs_pdu_t *srs_pdu = &srs->srs_pdu; c16_t srs_estimated_channel_time[nb_antennas_rx][N_ap][NR_SRS_IDFT_OVERSAMP_FACTOR * ofdm_symbol_size] __attribute__((aligned(32))); @@ -836,7 +837,7 @@ nr_srs_info_t nr_srs_rx_procedures(PHY_VARS_gNB *gNB, } noise_power_avg /= nb_antennas_rx; - gNB->srs->snr = dB_fixed(signal_power_avg) - dB_fixed(max(noise_power_avg, 1)); + *snr = dB_fixed(signal_power_avg) - dB_fixed(max(noise_power_avg, 1)); const uint16_t m_SRS_b = get_m_srs(srs_pdu->config_index, srs_pdu->bandwidth_index); for (int rb = 0; rb < m_SRS_b; rb++) { @@ -1180,6 +1181,7 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N c16_t srs_estimated_channel_freq[nb_antennas_rx][N_ap][ofdm_symbol_size * N_symb_SRS] __attribute__((aligned(32))); + int8_t snr; nr_srs_info_t srs_info = nr_srs_rx_procedures(gNB, frame_rx, slot_rx, @@ -1189,12 +1191,13 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N ofdm_symbol_size, srs, &srs_est, + &snr, srs_estimated_channel_freq, snr_per_rb, &timing_advance_offset, timing_advance_offset_nsec); - if ((gNB->srs->snr * 10) < gNB->srs_thres) { + if ((snr * 10) < gNB->srs_thres) { srs_est = -1; } @@ -1236,7 +1239,7 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N nr_srs_bf_report.prg_size = srs_pdu->beamforming.prg_size; nr_srs_bf_report.num_symbols = N_symb_SRS; nr_srs_bf_report.wide_band_snr = - srs_est >= 0 ? (gNB->srs->snr + 64) << 1 : 0xFF; // 0xFF will be set if this field is invalid + srs_est >= 0 ? (snr + 64) << 1 : 0xFF; // 0xFF will be set if this field is invalid nr_srs_bf_report.num_reported_symbols = N_symb_SRS; AssertFatal(nr_srs_bf_report.num_reported_symbols == 1, "nr_srs_bf_report.num_reported_symbols %i not handled yet!\n", diff --git a/openair1/SIMULATION/NR_PHY/srssim.c b/openair1/SIMULATION/NR_PHY/srssim.c index 6aa3ca5e81..144bce1ab9 100644 --- a/openair1/SIMULATION/NR_PHY/srssim.c +++ b/openair1/SIMULATION/NR_PHY/srssim.c @@ -568,6 +568,7 @@ int main(int argc, char *argv[]) int srs_est; c16_t srs_estimated_channel_freq[n_rx][N_ap][ofdm_symbol_size * N_symb_SRS] __attribute__((aligned(32))); + int8_t snr; nr_srs_rx_procedures(gNB, frame, slot, @@ -577,12 +578,13 @@ int main(int argc, char *argv[]) ofdm_symbol_size, srs, &srs_est, + &snr, srs_estimated_channel_freq, snr_per_rb, &timing_advance_offset, timing_advance_offset_nsec); - sum_srs_snr += pow(10, (double)gNB->srs->snr / 10.0); + sum_srs_snr += pow(10, (double)snr / 10.0); int16_t delay_ns = delay * 1e9 / (fp->samples_per_frame * 100); for (int ant_idx = 0; ant_idx < n_rx; ant_idx++) { diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c index 011b7ee680..7e74f26f56 100644 --- a/openair1/SIMULATION/NR_PHY/ulsim.c +++ b/openair1/SIMULATION/NR_PHY/ulsim.c @@ -1308,7 +1308,7 @@ int main(int argc, char *argv[]) srs_pdu->resource_type = NR_SRS_Resource__resourceType_PR_periodic; srs_pdu->t_srs = 1; srs_pdu->srs_parameters_v4.srs_bandwidth_size = m_SRS[srs_pdu->config_index]; - srs_pdu->srs_parameters_v4.usage = 1 << NR_SRS_ResourceSet__usage_codebook; + srs_pdu->srs_parameters_v4.usage = 1 << NFAPI_NR_SRS_BEAMMANAGEMENT; // to get SNR srs_pdu->srs_parameters_v4.report_type[0] = 1; srs_pdu->srs_parameters_v4.iq_representation = 1; srs_pdu->srs_parameters_v4.prg_size = 1; @@ -1748,7 +1748,14 @@ int main(int argc, char *argv[]) delay_pusch_est_count++; if (do_SRS == 1) { - sum_srs_snr += gNB->srs->snr; + DevAssert(UL_INFO.srs_ind.number_of_pdus == 1); // there must be SRS indication + const nfapi_nr_srs_indication_pdu_t *srs_ind = &UL_INFO.srs_ind.pdu_list[0]; + DevAssert(srs_ind->srs_usage == NFAPI_NR_SRS_BEAMMANAGEMENT); + nfapi_nr_srs_beamforming_report_t bf_rep; + unpack_nr_srs_beamforming_report((void *)srs_ind->report_tlv.value, srs_ind->report_tlv.length, &bf_rep, sizeof(bf_rep)); + DevAssert(bf_rep.wide_band_snr != 0xff); + int8_t snr = (bf_rep.wide_band_snr >> 1) - 64; + sum_srs_snr += snr; srs_snr_count++; } } // trial loop From a5696562da9b616141ced971db05bf528270f0d6 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Sun, 29 Mar 2026 23:37:12 +0200 Subject: [PATCH 25/29] Refactor SRS processing into its own function Signed-off-by: Robert Schmidt --- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 228 +++++++++++----------- 1 file changed, 118 insertions(+), 110 deletions(-) diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index 17eed001cb..3c9f3fcf02 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -875,6 +875,122 @@ nr_srs_info_t nr_srs_rx_procedures(PHY_VARS_gNB *gNB, return nr_srs_info; } +static void handle_srs(fsn_t now, PHY_VARS_gNB *gNB, const NR_gNB_SRS_t *srs, nfapi_nr_srs_indication_pdu_t *srs_indication) +{ + const NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms; + const uint8_t nb_antennas_rx = frame_parms->nb_antennas_rx; + const uint16_t ofdm_symbol_size = frame_parms->ofdm_symbol_size; + const nfapi_nr_srs_pdu_t *srs_pdu = &srs->srs_pdu; + + uint8_t N_symb_SRS = 1 << srs_pdu->num_symbols; + uint8_t N_ap = 1 << srs_pdu->num_ant_ports; + int16_t snr_per_rb[srs_pdu->bwp_size]; + uint16_t timing_advance_offset; + int16_t timing_advance_offset_nsec[nb_antennas_rx]; + int srs_est; + + c16_t srs_estimated_channel_freq[nb_antennas_rx][N_ap][ofdm_symbol_size * N_symb_SRS] __attribute__((aligned(32))); + + int8_t snr; + nr_srs_info_t srs_info = nr_srs_rx_procedures(gNB, + now.f, + now.s, + nb_antennas_rx, + N_ap, + N_symb_SRS, + ofdm_symbol_size, + srs, + &srs_est, + &snr, + srs_estimated_channel_freq, + snr_per_rb, + &timing_advance_offset, + timing_advance_offset_nsec); + + if ((snr * 10) < gNB->srs_thres) { + srs_est = -1; + } + + srs_indication->handle = srs_pdu->handle; + srs_indication->rnti = srs_pdu->rnti; + srs_indication->timing_advance_offset = srs_est >= 0 ? timing_advance_offset : 0xFFFF; + // TODO: currently we fill timing_advance_offset_nsec for antenna 0. Need to extend it for other antennas + srs_indication->timing_advance_offset_nsec = srs_est >= 0 ? timing_advance_offset_nsec[0] : 0x8000; + switch (srs_pdu->srs_parameters_v4.usage) { + case 0: + LOG_W(NR_PHY, "SRS report was not requested by MAC\n"); + return; + case 1 << NFAPI_NR_SRS_BEAMMANAGEMENT: + srs_indication->srs_usage = NFAPI_NR_SRS_BEAMMANAGEMENT; + break; + case 1 << NFAPI_NR_SRS_CODEBOOK: + srs_indication->srs_usage = NFAPI_NR_SRS_CODEBOOK; + break; + case 1 << NFAPI_NR_SRS_NONCODEBOOK: + srs_indication->srs_usage = NFAPI_NR_SRS_NONCODEBOOK; + break; + case 1 << NFAPI_NR_SRS_ANTENNASWITCH: + srs_indication->srs_usage = NFAPI_NR_SRS_ANTENNASWITCH; + break; + default: + LOG_E(NR_PHY, "Invalid srs_pdu->srs_parameters_v4.usage %i\n", srs_pdu->srs_parameters_v4.usage); + } + srs_indication->report_type = srs_pdu->srs_parameters_v4.report_type[0]; + + nfapi_srs_report_tlv_t *report_tlv = &srs_indication->report_tlv; + report_tlv->tag = 0; + report_tlv->length = 0; + + start_meas(&gNB->srs_report_tlv_stats); + switch (srs_indication->srs_usage) { + case NFAPI_NR_SRS_BEAMMANAGEMENT: { + start_meas(&gNB->srs_beam_report_stats); + nfapi_nr_srs_beamforming_report_t nr_srs_bf_report; + nr_srs_bf_report.prg_size = srs_pdu->beamforming.prg_size; + nr_srs_bf_report.num_symbols = N_symb_SRS; + nr_srs_bf_report.wide_band_snr = srs_est >= 0 ? (snr + 64) << 1 : 0xFF; // 0xFF will be set if this field is invalid + nr_srs_bf_report.num_reported_symbols = N_symb_SRS; + AssertFatal(nr_srs_bf_report.num_reported_symbols == 1, + "nr_srs_bf_report.num_reported_symbols %i not handled yet!\n", + nr_srs_bf_report.num_reported_symbols); + fill_srs_reported_symbol(&nr_srs_bf_report.reported_symbol_list[0], srs_pdu, snr_per_rb, srs_est); + + report_tlv->length = pack_nr_srs_beamforming_report(&nr_srs_bf_report, report_tlv->value, sizeof(report_tlv->value)); + stop_meas(&gNB->srs_beam_report_stats); + break; + } + + case NFAPI_NR_SRS_CODEBOOK: { + start_meas(&gNB->srs_iq_matrix_stats); + nfapi_nr_srs_normalized_channel_iq_matrix_t nr_srs_channel_iq_matrix; + fill_srs_channel_matrix(&nr_srs_channel_iq_matrix, + srs_pdu, + &srs_info, + srs_pdu->srs_parameters_v4.iq_representation, + nb_antennas_rx, + srs_pdu->srs_parameters_v4.num_total_ue_antennas, + srs_pdu->srs_parameters_v4.prg_size, + srs_pdu->srs_parameters_v4.srs_bandwidth_size / srs_pdu->srs_parameters_v4.prg_size, + frame_parms, + srs_estimated_channel_freq); + + report_tlv->length = + pack_nr_srs_normalized_channel_iq_matrix(&nr_srs_channel_iq_matrix, report_tlv->value, sizeof(report_tlv->value)); + stop_meas(&gNB->srs_iq_matrix_stats); + break; + } + + case NFAPI_NR_SRS_NONCODEBOOK: + case NFAPI_NR_SRS_ANTENNASWITCH: + LOG_W(NR_PHY, "PHY procedures for this SRS usage are not implemented yet!\n"); + break; + + default: + AssertFatal(1 == 0, "Invalid SRS usage\n"); + } + stop_meas(&gNB->srs_report_tlv_stats); +} + static void handle_pucch(PHY_VARS_gNB *gNB, c16_t **rxdataF, const NR_gNB_PUCCH_job_t *pucch, nfapi_nr_uci_t *uci) { const nfapi_nr_pucch_pdu_t *pucch_pdu = &pucch->pucch_pdu; @@ -1074,7 +1190,6 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N const NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms; const uint16_t ofdm_symbol_size = frame_parms->ofdm_symbol_size; - const uint8_t nb_antennas_rx = frame_parms->nb_antennas_rx; fsn_t now = {frame_rx, slot_rx, frame_parms->numerology_index}; @@ -1171,115 +1286,7 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N UL_INFO->srs_ind.pdu_list = UL_INFO->srs_pdu_list; nfapi_nr_srs_indication_pdu_t *srs_indication = UL_INFO->srs_pdu_list + UL_INFO->srs_ind.number_of_pdus++; - const nfapi_nr_srs_pdu_t *srs_pdu = &srs->srs_pdu; - uint8_t N_symb_SRS = 1 << srs_pdu->num_symbols; - uint8_t N_ap = 1 << srs_pdu->num_ant_ports; - int16_t snr_per_rb[srs_pdu->bwp_size]; - uint16_t timing_advance_offset; - int16_t timing_advance_offset_nsec[nb_antennas_rx]; - int srs_est; - - c16_t srs_estimated_channel_freq[nb_antennas_rx][N_ap][ofdm_symbol_size * N_symb_SRS] __attribute__((aligned(32))); - - int8_t snr; - nr_srs_info_t srs_info = nr_srs_rx_procedures(gNB, - frame_rx, - slot_rx, - nb_antennas_rx, - N_ap, - N_symb_SRS, - ofdm_symbol_size, - srs, - &srs_est, - &snr, - srs_estimated_channel_freq, - snr_per_rb, - &timing_advance_offset, - timing_advance_offset_nsec); - - if ((snr * 10) < gNB->srs_thres) { - srs_est = -1; - } - - srs_indication->handle = srs_pdu->handle; - srs_indication->rnti = srs_pdu->rnti; - srs_indication->timing_advance_offset = srs_est >= 0 ? timing_advance_offset : 0xFFFF; - // TODO: currently we fill timing_advance_offset_nsec for antenna 0. Need to extend it for other antennas - srs_indication->timing_advance_offset_nsec = srs_est >= 0 ? timing_advance_offset_nsec[0] : 0x8000; - switch (srs_pdu->srs_parameters_v4.usage) { - case 0: - LOG_W(NR_PHY, "SRS report was not requested by MAC\n"); - return 0; - case 1 << NFAPI_NR_SRS_BEAMMANAGEMENT: - srs_indication->srs_usage = NFAPI_NR_SRS_BEAMMANAGEMENT; - break; - case 1 << NFAPI_NR_SRS_CODEBOOK: - srs_indication->srs_usage = NFAPI_NR_SRS_CODEBOOK; - break; - case 1 << NFAPI_NR_SRS_NONCODEBOOK: - srs_indication->srs_usage = NFAPI_NR_SRS_NONCODEBOOK; - break; - case 1 << NFAPI_NR_SRS_ANTENNASWITCH: - srs_indication->srs_usage = NFAPI_NR_SRS_ANTENNASWITCH; - break; - default: - LOG_E(NR_PHY, "Invalid srs_pdu->srs_parameters_v4.usage %i\n", srs_pdu->srs_parameters_v4.usage); - } - srs_indication->report_type = srs_pdu->srs_parameters_v4.report_type[0]; - - nfapi_srs_report_tlv_t *report_tlv = &srs_indication->report_tlv; - report_tlv->tag = 0; - report_tlv->length = 0; - - start_meas(&gNB->srs_report_tlv_stats); - switch (srs_indication->srs_usage) { - case NFAPI_NR_SRS_BEAMMANAGEMENT: { - start_meas(&gNB->srs_beam_report_stats); - nfapi_nr_srs_beamforming_report_t nr_srs_bf_report; - nr_srs_bf_report.prg_size = srs_pdu->beamforming.prg_size; - nr_srs_bf_report.num_symbols = N_symb_SRS; - nr_srs_bf_report.wide_band_snr = - srs_est >= 0 ? (snr + 64) << 1 : 0xFF; // 0xFF will be set if this field is invalid - nr_srs_bf_report.num_reported_symbols = N_symb_SRS; - AssertFatal(nr_srs_bf_report.num_reported_symbols == 1, - "nr_srs_bf_report.num_reported_symbols %i not handled yet!\n", - nr_srs_bf_report.num_reported_symbols); - fill_srs_reported_symbol(&nr_srs_bf_report.reported_symbol_list[0], srs_pdu, snr_per_rb, srs_est); - - report_tlv->length = pack_nr_srs_beamforming_report(&nr_srs_bf_report, report_tlv->value, sizeof(report_tlv->value)); - stop_meas(&gNB->srs_beam_report_stats); - break; - } - - case NFAPI_NR_SRS_CODEBOOK: { - start_meas(&gNB->srs_iq_matrix_stats); - nfapi_nr_srs_normalized_channel_iq_matrix_t nr_srs_channel_iq_matrix; - fill_srs_channel_matrix(&nr_srs_channel_iq_matrix, - srs_pdu, - &srs_info, - srs_pdu->srs_parameters_v4.iq_representation, - nb_antennas_rx, - srs_pdu->srs_parameters_v4.num_total_ue_antennas, - srs_pdu->srs_parameters_v4.prg_size, - srs_pdu->srs_parameters_v4.srs_bandwidth_size / srs_pdu->srs_parameters_v4.prg_size, - frame_parms, - srs_estimated_channel_freq); - - report_tlv->length = - pack_nr_srs_normalized_channel_iq_matrix(&nr_srs_channel_iq_matrix, report_tlv->value, sizeof(report_tlv->value)); - stop_meas(&gNB->srs_iq_matrix_stats); - break; - } - - case NFAPI_NR_SRS_NONCODEBOOK: - case NFAPI_NR_SRS_ANTENNASWITCH: - LOG_W(NR_PHY, "PHY procedures for this SRS usage are not implemented yet!\n"); - break; - - default: - AssertFatal(1 == 0, "Invalid SRS usage\n"); - } - stop_meas(&gNB->srs_report_tlv_stats); + handle_srs(now, gNB, srs, srs_indication); srs->active = false; stop_meas(&gNB->rx_srs_stats); @@ -1288,6 +1295,7 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N stop_meas(&gNB->phy_proc_rx); if (n_pucch > 0 || num_pusch > 0) { + UNUSED(ofdm_symbol_size); // only used if T activated T(T_GNB_PHY_PUCCH_PUSCH_IQ, T_INT(frame_rx), T_INT(slot_rx), From f5989abd0dd0f40b51b3c1af22592c6f522626d4 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 15 Apr 2026 11:12:49 +0200 Subject: [PATCH 26/29] Use spsc_q for SRS processing Use the spsc queue to pass jobs from TX thread (scheduler) to RX thread (handling those jobs), reducing the amount spent searching for SRS jobs, clarifying the design, and making it thread-safe. Signed-off-by: Robert Schmidt --- openair1/PHY/INIT/nr_init.c | 8 +-- .../PHY/NR_TRANSPORT/nr_transport_proto.h | 2 +- openair1/PHY/NR_TRANSPORT/srs_rx.c | 38 +++++------- openair1/PHY/defs_gNB.h | 6 +- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 61 +++++++++++-------- openair1/SIMULATION/NR_PHY/srssim.c | 15 ++--- 6 files changed, 62 insertions(+), 68 deletions(-) diff --git a/openair1/PHY/INIT/nr_init.c b/openair1/PHY/INIT/nr_init.c index 7d56f8b410..2246fcdde1 100644 --- a/openair1/PHY/INIT/nr_init.c +++ b/openair1/PHY/INIT/nr_init.c @@ -419,15 +419,13 @@ void init_nr_transport(PHY_VARS_gNB *gNB) buffer_ul_slots = (nb_ul_slots_period < slot_ahead) ? nb_ul_slots_period : slot_ahead; gNB->max_nb_pusch = buffer_ul_slots ? MAX_MOBILES_PER_GNB * buffer_ul_slots : 1; - gNB->max_nb_srs = buffer_ul_slots ? buffer_ul_slots << 1 : 1; // assuming at most 2 SRS per slot int max_nb_pucch = buffer_ul_slots ? MAX_MOBILES_PER_GNB * buffer_ul_slots : 1; gNB->pucch_queue = spsc_q_alloc(max_nb_pucch, sizeof(NR_gNB_PUCCH_job_t)); gNB->pusch_queue = spsc_q_alloc(gNB->max_nb_pusch, sizeof(NR_gNB_PUSCH_job_t)); - gNB->srs = (NR_gNB_SRS_t *)malloc16(gNB->max_nb_srs * sizeof(NR_gNB_SRS_t)); - for (int i = 0; i < gNB->max_nb_srs; i++) - gNB->srs[i].active = 0; + int max_nb_srs = buffer_ul_slots ? buffer_ul_slots << 1 : 1; // assuming at most 2 SRS per slot + gNB->srs_queue = spsc_q_alloc(max_nb_srs, sizeof(NR_gNB_SRS_job_t)); gNB->ulsch = (NR_gNB_ULSCH_t *)malloc16(gNB->max_nb_pusch * sizeof(NR_gNB_ULSCH_t)); for (int i = 0; i < gNB->max_nb_pusch; i++) { @@ -446,7 +444,7 @@ void reset_nr_transport(PHY_VARS_gNB *gNB) spsc_q_free(&gNB->pucch_queue); spsc_q_free(&gNB->pusch_queue); - free(gNB->srs); + spsc_q_free(&gNB->srs_queue); for (int i = 0; i < gNB->max_nb_pusch; i++) free_gNB_ulsch(&gNB->ulsch[i], fp->N_RB_UL); diff --git a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h index 54acd23351..32efa979cc 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h +++ b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h @@ -209,7 +209,7 @@ nr_srs_info_t nr_srs_rx_procedures(PHY_VARS_gNB *gNB, uint8_t N_ap, uint8_t N_symb_SRS, uint16_t ofdm_symbol_size, - const NR_gNB_SRS_t *srs, + const NR_gNB_SRS_job_t *srs, int *srs_est, int8_t *snr, c16_t srs_estimated_channel_freq[][N_ap][ofdm_symbol_size * N_symb_SRS], diff --git a/openair1/PHY/NR_TRANSPORT/srs_rx.c b/openair1/PHY/NR_TRANSPORT/srs_rx.c index 96695909a9..b0436cb2d7 100644 --- a/openair1/PHY/NR_TRANSPORT/srs_rx.c +++ b/openair1/PHY/NR_TRANSPORT/srs_rx.c @@ -29,31 +29,21 @@ void nr_fill_srs(PHY_VARS_gNB *gNB, frame_t frame, slot_t slot, nfapi_nr_srs_pdu_t *srs_pdu) { - bool found = false; - for (int i = 0; i < gNB->max_nb_srs; i++) { - NR_gNB_SRS_t *srs = &gNB->srs[i]; - if (srs->active == false) { - found = true; - srs->frame = frame; - srs->slot = slot; - srs->active = true; - srs->beam_nb = 0; - if (gNB->common_vars.beam_id) { - const uint8_t l0 = gNB->frame_parms.symbols_per_slot - 1 - srs_pdu->time_start_position; - int bitmap = SL_to_bitmap(l0, 1 << srs_pdu->num_symbols); - int fapi_beam_idx = srs_pdu->beamforming.prgs_list[0].dig_bf_interface_list[0].beam_idx; - srs->beam_nb = beam_index_allocation(gNB->enable_analog_das, - fapi_beam_idx, - &gNB->common_vars, - slot, - gNB->frame_parms.symbols_per_slot, - bitmap); - } - memcpy((void *)&srs->srs_pdu, (void *)srs_pdu, sizeof(nfapi_nr_srs_pdu_t)); - break; - } + NR_gNB_SRS_job_t srs = {.frame = frame, .slot = slot, .srs_pdu = *srs_pdu}; + if (gNB->common_vars.beam_id) { + const uint8_t l0 = gNB->frame_parms.symbols_per_slot - 1 - srs_pdu->time_start_position; + int bitmap = SL_to_bitmap(l0, 1 << srs_pdu->num_symbols); + int fapi_beam_idx = srs_pdu->beamforming.prgs_list[0].dig_bf_interface_list[0].beam_idx; + srs.beam_nb = beam_index_allocation(gNB->enable_analog_das, + fapi_beam_idx, + &gNB->common_vars, + slot, + gNB->frame_parms.symbols_per_slot, + bitmap); } - AssertFatal(found, "SRS list is full\n"); + bool found = spsc_q_put(&gNB->srs_queue, &srs, sizeof(srs)); + if (!found) + LOG_W(NR_PHY, "SRS list is full: dropping SRS UE %04x\n", srs_pdu->rnti); } int nr_get_srs_signal(PHY_VARS_gNB *gNB, diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index 20e820a298..479a214418 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -208,7 +208,6 @@ typedef struct { } NR_gNB_PUCCH_job_t; typedef struct { - bool active; // identifier for concurrent beams int beam_nb; /// Frame where current SRS pdu was received @@ -217,7 +216,7 @@ typedef struct { uint32_t slot; /// ULSCH PDU nfapi_nr_srs_pdu_t srs_pdu; -} NR_gNB_SRS_t; +} NR_gNB_SRS_job_t; typedef struct { /// \brief Pointers (dynamic) to the received data in the frequency domain. @@ -375,7 +374,6 @@ typedef struct PHY_VARS_gNB_s { nfapi_nr_ul_tti_request_t UL_tti_req; - int max_nb_srs; int max_nb_pdsch; int max_nb_pusch; @@ -387,7 +385,7 @@ typedef struct PHY_VARS_gNB_s { NR_gNB_PUSCH *pusch_vars; spsc_q_t pucch_queue; spsc_q_t pusch_queue; - NR_gNB_SRS_t *srs; + spsc_q_t srs_queue; NR_gNB_ULSCH_t *ulsch; NR_gNB_PHY_STATS_t phy_stats[MAX_MOBILES_PER_GNB]; t_nrPolar_params **polarParams; diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index 3c9f3fcf02..f9a1100724 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -536,7 +536,9 @@ static void fill_ul_rb_mask(PHY_VARS_gNB *gNB, const NR_gNB_PUCCH_job_t *pucch, int n_pucch, const NR_gNB_PUSCH_job_t *pusch, - int n_pusch) + int n_pusch, + const NR_gNB_SRS_job_t *srs, + int n_srs) { for (int symbol = 0; symbol < 14; symbol++) { for (int m = 0; m < 9; m++) { @@ -581,11 +583,8 @@ static void fill_ul_rb_mask(PHY_VARS_gNB *gNB, } } - for (int i = 0; i < gNB->max_nb_srs; i++) { - NR_gNB_SRS_t *srs = &gNB->srs[i]; - if (!(srs && srs->active && srs->frame == now.f && srs->slot == now.s)) - continue; - nfapi_nr_srs_pdu_t *srs_pdu = &srs->srs_pdu; + for (int i = 0; i < n_srs; i++) { + const nfapi_nr_srs_pdu_t *srs_pdu = &srs[i].srs_pdu; const uint8_t l0 = gNB->frame_parms.symbols_per_slot - 1 - srs_pdu->time_start_position; for (int symbol = 0; symbol < (1 << srs_pdu->num_symbols); symbol++) { for (int rb = srs_pdu->bwp_start; rb < (srs_pdu->bwp_start + srs_pdu->bwp_size); rb++) { @@ -703,7 +702,7 @@ nr_srs_info_t nr_srs_rx_procedures(PHY_VARS_gNB *gNB, uint8_t N_ap, uint8_t N_symb_SRS, uint16_t ofdm_symbol_size, - const NR_gNB_SRS_t *srs, + const NR_gNB_SRS_job_t *srs, int *srs_est, int8_t *snr, c16_t srs_estimated_channel_freq[][N_ap][ofdm_symbol_size * N_symb_SRS], @@ -875,7 +874,7 @@ nr_srs_info_t nr_srs_rx_procedures(PHY_VARS_gNB *gNB, return nr_srs_info; } -static void handle_srs(fsn_t now, PHY_VARS_gNB *gNB, const NR_gNB_SRS_t *srs, nfapi_nr_srs_indication_pdu_t *srs_indication) +static void handle_srs(fsn_t now, PHY_VARS_gNB *gNB, const NR_gNB_SRS_job_t *srs, nfapi_nr_srs_indication_pdu_t *srs_indication) { const NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms; const uint8_t nb_antennas_rx = frame_parms->nb_antennas_rx; @@ -1137,6 +1136,25 @@ static bool get_current_pusch(const void *data, void *user) return fsn_equal(t, *now); } +static bool drop_old_srs(const void *data, void *user) +{ + const NR_gNB_SRS_job_t *srs = data; + const fsn_t *now = user; + const fsn_t t = {srs->frame, srs->slot, now->mu}; + bool drop = fsn_in_the_past(t, *now); + if (drop) + LOG_E(NR_PHY, "%4d.%2d SRS job for UE %04x is in the past (%4d.%2d)\n", now->f, now->s, srs->srs_pdu.rnti, t.f, t.s); + return drop; +} + +static bool get_current_srs(const void *data, void *user) +{ + const NR_gNB_SRS_job_t *srs = data; + const fsn_t *now = user; + const fsn_t t = {srs->frame, srs->slot, now->mu}; + return fsn_equal(t, *now); +} + static int find_nr_ulsch_idx(PHY_VARS_gNB *gNB, uint16_t rnti, int pid) { AssertFatal(gNB != NULL, "gNB is null\n"); @@ -1201,6 +1219,10 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N NR_gNB_PUSCH_job_t pusch[MAX_UL_PDUS_PER_SLOT]; int n_pusch_jobs = spsc_q_get_while(&gNB->pusch_queue, get_current_pusch, &now, pusch, sizeof(*pusch), MAX_UL_PDUS_PER_SLOT); + spsc_q_drop_while(&gNB->srs_queue, drop_old_srs, &now); + NR_gNB_SRS_job_t srs[MAX_NUM_NR_SRS_PDUS]; + int n_srs = spsc_q_get_while(&gNB->srs_queue, get_current_srs, &now, srs, sizeof(*srs), MAX_NUM_NR_SRS_PDUS); + LOG_D(PHY,"phy_procedures_gNB_uespec_RX frame %d, slot %d\n",frame_rx,slot_rx); { // Mask of occupied RBs, per symbol and PRB @@ -1208,7 +1230,7 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N nfapi_nr_max_num_of_symbol_per_slot_t *slot_conf = NULL; if (frame_parms->frame_type == TDD) slot_conf = gNB->gNB_config.tdd_table.max_tdd_periodicity_list[slot_rx].max_num_of_symbol_per_slot_list; - fill_ul_rb_mask(gNB, now, rb_mask_ul, slot_conf, pucch, n_pucch, pusch, n_pusch_jobs); + fill_ul_rb_mask(gNB, now, rb_mask_ul, slot_conf, pucch, n_pucch, pusch, n_pusch_jobs, srs, n_srs); int first_symb = 0, num_symb = 0; if (frame_parms->frame_type == TDD) @@ -1273,22 +1295,13 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N if (gNB->max_nb_pusch == 1) stop_meas(&gNB->ulsch_decoding_stats); - for (int i = 0; i < gNB->max_nb_srs; i++) { - NR_gNB_SRS_t *srs = &gNB->srs[i]; - if (!(srs && srs->active && srs->frame == frame_rx && srs->slot == slot_rx)) - continue; - LOG_D(NR_PHY, "(%d.%d) gNB is waiting for SRS, id = %i\n", frame_rx, slot_rx, i); - + UL_INFO->srs_ind.sfn = frame_rx; + UL_INFO->srs_ind.slot = slot_rx; + UL_INFO->srs_ind.pdu_list = UL_INFO->srs_pdu_list; + UL_INFO->srs_ind.number_of_pdus = n_srs; + for (int i = 0; i < n_srs; ++i) { start_meas(&gNB->rx_srs_stats); - - UL_INFO->srs_ind.sfn = frame_rx; - UL_INFO->srs_ind.slot = slot_rx; - UL_INFO->srs_ind.pdu_list = UL_INFO->srs_pdu_list; - nfapi_nr_srs_indication_pdu_t *srs_indication = UL_INFO->srs_pdu_list + UL_INFO->srs_ind.number_of_pdus++; - - handle_srs(now, gNB, srs, srs_indication); - - srs->active = false; + handle_srs(now, gNB, &srs[i], &UL_INFO->srs_ind.pdu_list[i]); stop_meas(&gNB->rx_srs_stats); } diff --git a/openair1/SIMULATION/NR_PHY/srssim.c b/openair1/SIMULATION/NR_PHY/srssim.c index 144bce1ab9..0ab34dac47 100644 --- a/openair1/SIMULATION/NR_PHY/srssim.c +++ b/openair1/SIMULATION/NR_PHY/srssim.c @@ -435,11 +435,7 @@ int main(int argc, char *argv[]) .beamforming.num_prgs = m_SRS[srs_pdu.config_index], .beamforming.prg_size = 1}; - gNB->srs->srs_pdu = srs_pdu; - gNB->srs->active = true; - gNB->srs->beam_nb = 0; - gNB->srs->frame = frame; - gNB->srs->slot = slot; + NR_gNB_SRS_job_t srs_job = {.frame = frame, .slot = slot, .srs_pdu = srs_pdu}; // Configure SRS parameters at UE fapi_nr_ul_config_srs_pdu srs_config_pdu = {.rnti = srs_pdu.rnti, @@ -559,10 +555,9 @@ int main(int argc, char *argv[]) //----------- UE RX SRS procedures --------------------- start_meas(&gNB->rx_srs_stats); - NR_gNB_SRS_t *srs = &gNB->srs[0]; - uint8_t N_symb_SRS = 1 << srs->srs_pdu.num_symbols; - uint8_t N_ap = 1 << srs->srs_pdu.num_ant_ports; - int16_t snr_per_rb[srs->srs_pdu.bwp_size]; + uint8_t N_symb_SRS = 1 << srs_pdu.num_symbols; + uint8_t N_ap = 1 << srs_pdu.num_ant_ports; + int16_t snr_per_rb[srs_pdu.bwp_size]; uint16_t timing_advance_offset; int16_t timing_advance_offset_nsec[n_rx]; int srs_est; @@ -576,7 +571,7 @@ int main(int argc, char *argv[]) N_ap, N_symb_SRS, ofdm_symbol_size, - srs, + &srs_job, &srs_est, &snr, srs_estimated_channel_freq, From 39d3477d84c1b7feb1da69d1e7d17442fa70e39c Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 24 Nov 2025 17:56:08 +0100 Subject: [PATCH 27/29] TODO: reimplement NR_gNB_PHY_STATS_t lookup Signed-off-by: Robert Schmidt --- openair1/PHY/INIT/nr_init.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openair1/PHY/INIT/nr_init.c b/openair1/PHY/INIT/nr_init.c index 2246fcdde1..a007bbd6d7 100644 --- a/openair1/PHY/INIT/nr_init.c +++ b/openair1/PHY/INIT/nr_init.c @@ -50,6 +50,8 @@ int l1_north_init_gNB() NR_gNB_PHY_STATS_t *get_phy_stats(PHY_VARS_gNB *gNB, uint16_t rnti) { + // TODO reimplement with hashtable? also called from both UL/DL => not + // thread-safe NR_gNB_PHY_STATS_t *stats; int first_free = -1; for (int i = 0; i < MAX_MOBILES_PER_GNB; i++) { From 879e374d329ec350ce57f71fc793b810214b53ee Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 9 Apr 2026 17:47:26 +0200 Subject: [PATCH 28/29] Use bytes instead of bit packing for rb_mask_ul Clarify the code, the bitpacking might even potentially be slower. Signed-off-by: Robert Schmidt --- .../PHY/NR_ESTIMATION/nr_measurements_gNB.c | 6 +-- openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h | 2 +- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 52 +++++++------------ openair1/SIMULATION/NR_PHY/pucchsim.c | 11 ++-- 4 files changed, 27 insertions(+), 44 deletions(-) diff --git a/openair1/PHY/NR_ESTIMATION/nr_measurements_gNB.c b/openair1/PHY/NR_ESTIMATION/nr_measurements_gNB.c index a901f8dba1..f9b4991cfd 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_measurements_gNB.c +++ b/openair1/PHY/NR_ESTIMATION/nr_measurements_gNB.c @@ -123,12 +123,12 @@ void dump_nr_I0_stats(FILE *fd, PHY_VARS_gNB *gNB) fprintf(fd, "\nPRACH I0 = %d.%d dB\n", gNB->measurements.prach_I0 / 10, gNB->measurements.prach_I0 % 10); } -void gNB_I0_measurements(PHY_VARS_gNB *gNB, int slot, int first_symb, int num_symb, uint32_t rb_mask_ul[14][9]) +void gNB_I0_measurements(PHY_VARS_gNB *gNB, int slot, int first_symb, int num_symb, uint32_t rb_mask_ul[14][MAX_BWP_SIZE]) { NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms; NR_gNB_COMMON *common_vars = &gNB->common_vars; PHY_MEASUREMENTS_gNB *measurements = &gNB->measurements; - int nb_symb[275]={0}; + int nb_symb[MAX_BWP_SIZE] = {0}; unsigned int tmp_n0_subband[frame_parms->nb_antennas_rx][frame_parms->N_RB_UL]; memset(tmp_n0_subband, 0, sizeof(tmp_n0_subband)); @@ -137,7 +137,7 @@ void gNB_I0_measurements(PHY_VARS_gNB *gNB, int slot, int first_symb, int num_sy for (int s = first_symb; s < first_symb + num_symb; s++) { int offset0 = ((slot % RU_RX_SLOT_DEPTH) * frame_parms->symbols_per_slot + s) * frame_parms->ofdm_symbol_size; for (int rb = 0; rb < frame_parms->N_RB_UL; rb++) { - if ((rb_mask_ul[s][rb >> 5] & (1U << (rb & 31))) == 0 && // check that rb was not used in this subframe + if (rb_mask_ul[s][rb] == 0 && // check that rb was not used in this subframe !(I0_SKIP_DC && rb == frame_parms->N_RB_UL >> 1)) { // skip middle PRB because of artificial noise possibly created by FFT int offset = offset0 + (frame_parms->first_carrier_offset + (rb*12))%frame_parms->ofdm_symbol_size; nb_symb[rb]++; diff --git a/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h b/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h index 25b9c27414..34bf64cc51 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h +++ b/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h @@ -40,7 +40,7 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, void dump_nr_I0_stats(FILE *fd,PHY_VARS_gNB *gNB); -void gNB_I0_measurements(PHY_VARS_gNB *gNB, int slot, int first_symb, int num_symb, uint32_t rb_mask_ul[14][9]); +void gNB_I0_measurements(PHY_VARS_gNB *gNB, int slot, int first_symb, int num_symb, uint32_t rb_mask_ul[14][MAX_BWP_SIZE]); void nr_est_srs_timing_advance_offset(uint16_t ofdm_symbol_size, const c16_t srs_estimated_channel_time[][NR_SRS_IDFT_OVERSAMP_FACTOR * ofdm_symbol_size], diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index f9a1100724..686f88a5fa 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -531,8 +531,7 @@ static void nr_fill_indication(const PHY_VARS_gNB *gNB, // Function to fill UL RB mask to be used for N0 measurements static void fill_ul_rb_mask(PHY_VARS_gNB *gNB, fsn_t now, - uint32_t rb_mask_ul[14][9], - nfapi_nr_max_num_of_symbol_per_slot_t *slot_conf, + uint32_t rb_mask_ul[14][MAX_BWP_SIZE], const NR_gNB_PUCCH_job_t *pucch, int n_pucch, const NR_gNB_PUSCH_job_t *pusch, @@ -541,29 +540,20 @@ static void fill_ul_rb_mask(PHY_VARS_gNB *gNB, int n_srs) { for (int symbol = 0; symbol < 14; symbol++) { - for (int m = 0; m < 9; m++) { - rb_mask_ul[symbol][m] = 0; - for (int i = 0; i < 32; i++) { - int prbpos = (m * 32) + i; - if (prbpos > gNB->frame_parms.N_RB_UL) - break; - rb_mask_ul[symbol][m] |= (gNB->ulprbbl[prbpos] > 0 ? 1U : 0) << i; - } + for (int prb = 0; prb < MAX_BWP_SIZE; prb++) { + rb_mask_ul[symbol][prb] = gNB->ulprbbl[prb] > 0; } } for (int i = 0; i < n_pucch; ++i) { const nfapi_nr_pucch_pdu_t *pucch_pdu = &pucch[i].pucch_pdu; const int start = pucch_pdu->start_symbol_index; - for (int symbol = start; symbol < (start + pucch_pdu->nr_of_symbols); symbol++) { - if (gNB->frame_parms.frame_type == FDD || (gNB->frame_parms.frame_type == TDD && slot_conf[symbol].slot_config.value == 1)) { - for (int rb = 0; rb < pucch_pdu->prb_size; rb++) { - int rb2 = - rb + pucch_pdu->bwp_start - + ((symbol < start + (pucch_pdu->nr_of_symbols >> 1)) || (pucch_pdu->freq_hop_flag == 0) ? pucch_pdu->prb_start - : pucch_pdu->second_hop_prb); - rb_mask_ul[symbol][rb2 >> 5] |= 1U << (rb2 & 31); - } + for (int symbol = start; symbol < start + pucch_pdu->nr_of_symbols; symbol++) { + bool first_hop = symbol < start + (pucch_pdu->nr_of_symbols >> 1); + int prb_first_second = first_hop || pucch_pdu->freq_hop_flag == 0 ? pucch_pdu->prb_start : pucch_pdu->second_hop_prb; + for (int rb = 0; rb < pucch_pdu->prb_size; rb++) { + int prb = rb + pucch_pdu->bwp_start + prb_first_second; + rb_mask_ul[symbol][prb] = 1; } } } @@ -573,12 +563,9 @@ static void fill_ul_rb_mask(PHY_VARS_gNB *gNB, uint8_t symbol_start = pusch_pdu->start_symbol_index; uint8_t symbol_end = symbol_start + pusch_pdu->nr_of_symbols; for (int symbol = symbol_start; symbol < symbol_end; symbol++) { - if (gNB->frame_parms.frame_type == FDD || (gNB->frame_parms.frame_type == TDD && slot_conf[symbol].slot_config.value == 1)) { - LOG_D(PHY, "symbol %d Filling rb_mask_ul rb_size %d\n", symbol, pusch_pdu->rb_size); - for (int rb = 0; rb < pusch_pdu->rb_size; rb++) { - int rb2 = rb + pusch_pdu->rb_start + pusch_pdu->bwp_start; - rb_mask_ul[symbol][rb2 >> 5] |= 1U << (rb2 & 31); - } + for (int rb = 0; rb < pusch_pdu->rb_size; rb++) { + int prb = rb + pusch_pdu->rb_start + pusch_pdu->bwp_start; + rb_mask_ul[symbol][prb] = 1; } } } @@ -588,7 +575,7 @@ static void fill_ul_rb_mask(PHY_VARS_gNB *gNB, const uint8_t l0 = gNB->frame_parms.symbols_per_slot - 1 - srs_pdu->time_start_position; for (int symbol = 0; symbol < (1 << srs_pdu->num_symbols); symbol++) { for (int rb = srs_pdu->bwp_start; rb < (srs_pdu->bwp_start + srs_pdu->bwp_size); rb++) { - rb_mask_ul[l0 + symbol][rb >> 5] |= 1U << (rb & 31); + rb_mask_ul[l0 + symbol][rb] = 1; } } } @@ -1226,14 +1213,12 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N LOG_D(PHY,"phy_procedures_gNB_uespec_RX frame %d, slot %d\n",frame_rx,slot_rx); { // Mask of occupied RBs, per symbol and PRB - uint32_t rb_mask_ul[14][9]; - nfapi_nr_max_num_of_symbol_per_slot_t *slot_conf = NULL; - if (frame_parms->frame_type == TDD) - slot_conf = gNB->gNB_config.tdd_table.max_tdd_periodicity_list[slot_rx].max_num_of_symbol_per_slot_list; - fill_ul_rb_mask(gNB, now, rb_mask_ul, slot_conf, pucch, n_pucch, pusch, n_pusch_jobs, srs, n_srs); + uint32_t rb_mask_ul[14][MAX_BWP_SIZE] = {0}; + fill_ul_rb_mask(gNB, now, rb_mask_ul, pucch, n_pucch, pusch, n_pusch_jobs, srs, n_srs); int first_symb = 0, num_symb = 0; - if (frame_parms->frame_type == TDD) + if (frame_parms->frame_type == TDD) { + const nfapi_nr_max_num_of_symbol_per_slot_t *slot_conf = gNB->gNB_config.tdd_table.max_tdd_periodicity_list[slot_rx].max_num_of_symbol_per_slot_list; for (int symbol_count = 0; symbol_count < frame_parms->symbols_per_slot; symbol_count++) { if (slot_conf[symbol_count].slot_config.value == 1) { if (num_symb == 0) @@ -1241,8 +1226,9 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N num_symb++; } } - else + } else { num_symb = frame_parms->symbols_per_slot; + } gNB_I0_measurements(gNB, slot_rx, first_symb, num_symb, rb_mask_ul); } diff --git a/openair1/SIMULATION/NR_PHY/pucchsim.c b/openair1/SIMULATION/NR_PHY/pucchsim.c index 6674a94b14..40c6e454aa 100644 --- a/openair1/SIMULATION/NR_PHY/pucchsim.c +++ b/openair1/SIMULATION/NR_PHY/pucchsim.c @@ -630,27 +630,24 @@ int main(int argc, char **argv) rxlev_pucch += signal_energy((int32_t *)&rxdataF[aarx][startingSymbolIndex * frame_parms->ofdm_symbol_size], 12); // set UL mask for pucch allocation - uint32_t rb_mask_ul[14][9] = {0}; + uint32_t rb_mask_ul[14][MAX_BWP_SIZE] = {0}; if (format == 1) { for (int s = 0; s < frame_parms->symbols_per_slot; s++) { if (s >= startingSymbolIndex && s < (startingSymbolIndex + nrofSymbols / 2)) for (int rb = 0; rb < N_RB; rb++) { - int rb2 = rb + startingPRB; - rb_mask_ul[s][rb2 >> 5] |= (1 << (rb2 & 31)); + rb_mask_ul[s][rb + startingPRB] = 1; } else if (s >= (startingSymbolIndex + nrofSymbols / 2) && s < startingSymbolIndex + nrofSymbols) for (int rb = 0; rb < N_RB; rb++) { - int rb2 = rb + startingPRB_intraSlotHopping; - rb_mask_ul[s][rb2 >> 5] |= (1 << (rb2 & 31)); + rb_mask_ul[s][rb + startingPRB_intraSlotHopping] = 1; } } } else { for (int s = 0; s < frame_parms->symbols_per_slot; s++) { if (s >= startingSymbolIndex && s < (startingSymbolIndex + nrofSymbols)) for (int rb = 0; rb < N_RB; rb++) { - int rb2 = rb + startingPRB; - rb_mask_ul[s][rb2 >> 5] |= (1 << (rb2 & 31)); + rb_mask_ul[s][rb + startingPRB] = 1; } } } From 03096dd4957a8254400c74f8a2999a11bf5d7439 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Sat, 11 Apr 2026 11:32:04 +0200 Subject: [PATCH 29/29] Use spsc_q to process PRACH jobs Use two queues for PRACH processing: - prach_ru_queue: pass jobs from TX thread (scheduler) to RU thread. For split 8, the RU thread itself handles PRACH processing; for split 7.2, it is the library that is responsible for handling PRACH messages (see oran_fh_if4p5_south_in()) - prach_l1rx_queue: after jobs have been handled in RU thread, use this queue to pass jobs to the RX thread. There, preamble detection is performed, and RACH.indication FAPI messages are filled if a preamble has been detected. Together, these queues replace a linear search in a global array that has been modified by three threads at the same time. The design ensures that access now is thread-safe, and with less overhead. Signed-off-by: Robert Schmidt --- executables/nr-gnb.c | 7 +- executables/nr-ru.c | 14 +- openair1/PHY/INIT/nr_init.c | 3 +- openair1/PHY/NR_TRANSPORT/nr_prach.c | 130 ++++++++---------- .../PHY/NR_TRANSPORT/nr_transport_proto.h | 14 +- openair1/PHY/defs_gNB.h | 11 +- openair1/SCHED_NR/nr_prach_procedures.c | 17 +-- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 4 +- openair1/SCHED_NR/sched_nr.h | 2 +- openair1/SIMULATION/NR_PHY/prachsim.c | 19 ++- radio/fhi_72/oran_isolate.c | 22 ++- 11 files changed, 118 insertions(+), 125 deletions(-) diff --git a/executables/nr-gnb.c b/executables/nr-gnb.c index abbfa05df1..1b27b2e4e6 100644 --- a/executables/nr-gnb.c +++ b/executables/nr-gnb.c @@ -161,7 +161,12 @@ static void rx_func(processingData_L1_t *info) NR_UL_IND_t UL_INFO = {.frame = frame_rx, .slot = slot_rx, .module_id = gNB->Mod_id, .CC_id = gNB->CC_id}; // Do PRACH RU processing UL_INFO.rach_ind.pdu_list = UL_INFO.prach_pdu_indication_list; - L1_nr_prach_procedures(gNB, frame_rx, slot_rx, &UL_INFO.rach_ind); + UL_INFO.rach_ind.number_of_pdus = 0; + // even if processing is late, we might collect all PRACH + // the last PRACH's frame/slot is when all UE's appear to have accessed + prach_item_t p; + while (spsc_q_get(&gNB->prach_l1rx_queue, &p, sizeof(p))) + L1_nr_prach_procedures(gNB, &p, &UL_INFO.rach_ind); //WA: comment rotation in tx/rx if (gNB->phase_comp) { diff --git a/executables/nr-ru.c b/executables/nr-ru.c index 7554c8a9a6..a0bc7c2b5d 100644 --- a/executables/nr-ru.c +++ b/executables/nr-ru.c @@ -15,6 +15,7 @@ #include "common/utils/nr/nr_common.h" #include "common/utils/assertions.h" #include "common/utils/system.h" +#include "common/utils/fsn.h" #include "common/ran_context.h" #include "radio/COMMON/common_lib.h" @@ -980,11 +981,16 @@ void *ru_thread(void *param) proc->tti_rx * gNB->frame_parms.samples_per_slot_wCP); // Do PRACH RU processing - prach_item_t *p = - find_nr_prach(&gNB->prach_list, proc->frame_rx, proc->tti_rx, gNB->frame_parms.nb_antennas_rx, NR_SEARCH_EXIST); - if (p) { + fsn_t now = {.f = proc->frame_rx, .s = proc->tti_rx, .mu = fp->numerology_index}; + prach_item_t p; + while (get_next_nr_prach(&gNB->prach_ru_queue, &now, &p)) { // need to extract RACH data for later processing by rx_nr_prach() - rx_nr_prach_ru(p, ru->common.rxdata, ru->nr_frame_parms, ru->N_TA_offset); + rx_nr_prach_ru(&p, ru->common.rxdata, ru->nr_frame_parms, ru->N_TA_offset); + bool success = spsc_q_put(&gNB->prach_l1rx_queue, &p, sizeof(p)); + // assume prach_l1rx_queue never full: prach_ru_queue filled at + // constant pace, but prach_l1rx_queue emptied as fast as possible, + // see rx_func() + DevAssert(success); } // end if (prach_id >= 0) } // end if (ru->feprx) } // end if (slot_type == NR_UPLINK_SLOT || slot_type == NR_MIXED_SLOT) { diff --git a/openair1/PHY/INIT/nr_init.c b/openair1/PHY/INIT/nr_init.c index a007bbd6d7..35b27468f6 100644 --- a/openair1/PHY/INIT/nr_init.c +++ b/openair1/PHY/INIT/nr_init.c @@ -166,7 +166,7 @@ void phy_init_nr_gNB(PHY_VARS_gNB *gNB) common_vars->debugBuff_sample_offset = 0; // PRACH - init_prach_list(&gNB->prach_list); + init_nr_prach(gNB); int N_RB_UL = cfg->carrier_config.ul_grid_size[cfg->ssb_config.scs_common.value].value; int n_buf = Prx*max_ul_mimo_layers; @@ -208,6 +208,7 @@ void phy_free_nr_gNB(PHY_VARS_gNB *gNB) free_gnb_lowpapr_sequences(); reset_nr_transport(gNB); + reset_nr_prach(gNB); destroy_DLSCH_struct(gNB); diff --git a/openair1/PHY/NR_TRANSPORT/nr_prach.c b/openair1/PHY/NR_TRANSPORT/nr_prach.c index c54c9ccc29..dc3cb1950d 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_prach.c +++ b/openair1/PHY/NR_TRANSPORT/nr_prach.c @@ -12,72 +12,73 @@ #include "PHY/NR_TRANSPORT/nr_transport_common_proto.h" #include "openair1/PHY/NR_TRANSPORT/nr_prach.h" -void init_prach_list(prach_list_t *l) +void init_nr_prach(PHY_VARS_gNB *gNB) { - pthread_mutex_init(&l->prach_list_mutex, NULL); - memset(l->list, 0, sizeof(l->list)); + int num_prach = 8; + gNB->prach_ru_queue = spsc_q_alloc(num_prach, sizeof(prach_item_t)); + gNB->prach_l1rx_queue = spsc_q_alloc(num_prach, sizeof(prach_item_t)); } -void free_nr_prach_entry(prach_list_t *l, prach_item_t *p) +void reset_nr_prach(PHY_VARS_gNB *gNB) { - pthread_mutex_lock(&l->prach_list_mutex); - if (p->frame == -1) - LOG_E(NR_PHY_RACH, "Freeing a not allocated prach entry\n"); - *p = (prach_item_t){.frame = -1, .slot = -1, .num_slots = -1, .nb_rx = p->nb_rx, .prach_buf = (void *)(p + 1)}; - pthread_mutex_unlock(&l->prach_list_mutex); + spsc_q_free(&gNB->prach_ru_queue); + spsc_q_free(&gNB->prach_l1rx_queue); } -prach_item_t *find_nr_prach(prach_list_t *l, int frame, int slot, int nb_rx, nr_find_type_t type) +void free_nr_prach_entry(prach_item_t *p) { - pthread_mutex_lock(&l->prach_list_mutex); - AssertFatal(nb_rx, "Error! Number of antennas set to 0.\n"); - prach_item_t **p = l->list; - prach_item_t **end = p + NUMBER_OF_NR_PRACH_MAX; - for (; p < end; p++) - if ((*p) && (*p)->frame == frame && ((*p)->slot + (*p)->num_slots - 1) == slot) - break; - if (p == end) { - if (type == NR_SEARCH_EXIST) { - pthread_mutex_unlock(&l->prach_list_mutex); - return NULL; - } - for (p = l->list; p < end; p++) - if (!(*p) || ((*p)->frame == -1 && (*p)->slot == -1)) - break; - } - if (p == end) { - pthread_mutex_unlock(&l->prach_list_mutex); - return NULL; - } - // mark it used, it will be filled later - if (*p && (*p)->nb_rx != nb_rx) { - LOG_W(PHY, "rach procedure nb_rx ant changed from %d to %d\n", (*p)->nb_rx, nb_rx); - free(*p); - *p = NULL; - } - if (!(*p)) { - *p = calloc(1, sizeof(prach_item_t) + sizeof(c16_t) * nb_rx * NUMBER_OF_NR_RU_PRACH_OCCASIONS_MAX * NR_PRACH_SEQ_LEN_L); - (*p)->prach_buf = (void *)((*p) + 1); - } - (*p)->nb_rx = nb_rx; - (*p)->frame = frame; - pthread_mutex_unlock(&l->prach_list_mutex); - return (*p); + free(p->prach_buf); } -prach_item_t *nr_schedule_rx_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu) +static bool drop_old_prach(const void *data, void *user) +{ + const prach_item_t *p = data; + const fsn_t *now = user; + const fsn_t t = {p->frame, p->slot, now->mu}; + bool drop = fsn_in_the_past(t, *now); + if (drop) + LOG_E(NR_PHY, "%4d.%2d PRACH job is in the past (%4d.%2d)\n", now->f, now->s, t.f, t.s); + return drop; +} + +static bool get_current_prach(const void *data, void *user) +{ + const prach_item_t *p = data; + const fsn_t *now = user; + const fsn_t t = {p->frame, p->slot, now->mu}; + return fsn_equal(t, *now); +} + +bool get_next_nr_prach(spsc_q_t *q, const fsn_t *now, prach_item_t *p) +{ + spsc_q_drop_while(q, drop_old_prach, (void *)now); + return spsc_q_get_if(q, get_current_prach, (void *)now, p, sizeof(*p)); +} + +void nr_schedule_rx_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu) { - prach_item_t *prach = find_nr_prach(&gNB->prach_list, SFN, Slot, gNB->frame_parms.nb_antennas_rx, NR_SEARCH_EXIST_OR_FREE); - if (!prach) { - LOG_W(PHY, "no free space for a new detected rach, discarding\n"); - return NULL; - } const int fmt = prach_pdu->prach_format; - NR_DL_FRAME_PARMS *fp = &gNB->frame_parms; - // we set only elements as the whole structure has been cleaned when we release it - prach->frame = SFN; - prach->slot = Slot; - prach->num_slots = fmt < 4 ? get_long_prach_dur(fmt, fp->numerology_index) : 1; + const NR_DL_FRAME_PARMS *fp = &gNB->frame_parms; + const nfapi_nr_prach_config_t *cfg = &gNB->gNB_config.prach_config; + const nfapi_nr_num_prach_fd_occasions_t *occ = &cfg->num_prach_fd_occasions_list[prach_pdu->num_ra]; + prach_item_t prach = { + .frame = SFN, + .slot = Slot, + .num_slots = fmt < 4 ? get_long_prach_dur(fmt, fp->numerology_index) : 1, + .pdu = *prach_pdu, + .rootSequenceIndex = occ->prach_root_sequence_index.value, + .numrootSequenceIndex = occ->num_root_sequences.value, + .msg1_frequencystart = occ->k1.value, + .mu = cfg->prach_sub_c_spacing.value, + .prach_sequence_length = cfg->prach_sequence_length.value, + .restricted_set = cfg->restricted_set_config.value, + .numerology_index = fp->numerology_index, + .nb_rx = gNB->gNB_config.carrier_config.num_rx_ant.value, + .Xu = gNB->X_u, + .rx_prach = &gNB->rx_prach, + // TODO can be made permanently allocated? + .prach_buf = calloc_or_fail(1, sizeof(c16_t) * prach.nb_rx * NUMBER_OF_NR_RU_PRACH_OCCASIONS_MAX * NR_PRACH_SEQ_LEN_L), + }; if (gNB->common_vars.beam_id) { int n_symb = get_nr_prach_duration(prach_pdu->prach_format); AssertFatal(prach_pdu->beamforming.dig_bf_interface < NFAPI_MAX_NUM_BG_IF, @@ -87,7 +88,7 @@ prach_item_t *nr_schedule_rx_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_n int fapi_beam_idx = prach_pdu->beamforming.prgs_list[0].dig_bf_interface_list[i].beam_idx; int start_symb = prach_pdu->prach_start_symbol + i * n_symb; int bitmap = SL_to_bitmap(start_symb, n_symb); - prach->beams[i] = beam_index_allocation(gNB->enable_analog_das, + prach.beams[i] = beam_index_allocation(gNB->enable_analog_das, fapi_beam_idx, &gNB->common_vars, Slot, @@ -95,20 +96,9 @@ prach_item_t *nr_schedule_rx_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_n bitmap); } } - prach->pdu = *prach_pdu; - nfapi_nr_prach_config_t *cfg = &gNB->gNB_config.prach_config; - nfapi_nr_num_prach_fd_occasions_t *occ = &cfg->num_prach_fd_occasions_list[prach_pdu->num_ra]; - prach->rootSequenceIndex = occ->prach_root_sequence_index.value; - prach->numrootSequenceIndex = occ->num_root_sequences.value; - prach->msg1_frequencystart = occ->k1.value; - prach->mu = cfg->prach_sub_c_spacing.value; - prach->prach_sequence_length = cfg->prach_sequence_length.value; - prach->restricted_set = cfg->restricted_set_config.value; - prach->numerology_index = fp->numerology_index; - prach->nb_rx = gNB->gNB_config.carrier_config.num_rx_ant.value; - prach->Xu = gNB->X_u; - prach->rx_prach = &gNB->rx_prach; - return prach; + bool found = spsc_q_put(&gNB->prach_ru_queue, &prach, sizeof(prach)); + if (!found) + LOG_W(NR_PHY, "%4d.%2d PRACH occ queue is full: dropping PRACH request\n", SFN, Slot); } static void rx_nr_prach_ru_internal(prach_item_t *p, diff --git a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h index 32efa979cc..863903c242 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h +++ b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h @@ -11,6 +11,7 @@ #include "PHY/defs_nr_common.h" #include "PHY/defs_gNB.h" +#include "common/utils/fsn.h" #define NR_PBCH_PDU_BITS 24 @@ -167,7 +168,7 @@ void nr_fill_ulsch(PHY_VARS_gNB *gNB, int slot, nfapi_nr_pusch_pdu_t *ulsch_pdu); -prach_item_t *nr_schedule_rx_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu); +void nr_schedule_rx_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu); typedef struct rx_prach_out { uint16_t max_preamble; @@ -178,12 +179,6 @@ rx_prach_out_t rx_nr_prach(const prach_item_t *, int occasion); void rx_nr_prach_ru(prach_item_t *, int32_t **, NR_DL_FRAME_PARMS *frame_parms, int N_TA_offset); -typedef enum { - NR_SEARCH_EXIST = 0, - NR_SEARCH_EXIST_OR_FREE -} nr_find_type_t; - -prach_item_t *find_nr_prach(prach_list_t *, int frame, int slot, int nb_rx, nr_find_type_t type); void nr_fill_pucch(PHY_VARS_gNB *gNB, int frame, int slot, @@ -219,7 +214,10 @@ nr_srs_info_t nr_srs_rx_procedures(PHY_VARS_gNB *gNB, int get_nr_prach_duration(uint8_t prach_format); -void free_nr_prach_entry(prach_list_t *, prach_item_t *); +void init_nr_prach(PHY_VARS_gNB *gNB); +void reset_nr_prach(PHY_VARS_gNB *gNB); +void free_nr_prach_entry(prach_item_t *); +bool get_next_nr_prach(spsc_q_t *q, const fsn_t *now, prach_item_t *p); void nr_decode_pucch1(PHY_VARS_gNB *gNB, c16_t **rxdataF, diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index 479a214418..3e627c38cd 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -46,14 +46,6 @@ typedef struct { c16_t (*prach_buf)[NUMBER_OF_NR_RU_PRACH_OCCASIONS_MAX][NR_PRACH_SEQ_LEN_L]; } prach_item_t; -typedef struct { - /// prach commands - prach_item_t *list[NUMBER_OF_NR_PRACH_MAX]; - /// mutex for prach_list access - pthread_mutex_t prach_list_mutex; -} prach_list_t; -void init_prach_list(prach_list_t *); - typedef struct { int nb_id; int Nid[MAX_PUCCH0_NID]; @@ -378,7 +370,8 @@ typedef struct PHY_VARS_gNB_s { int max_nb_pusch; NR_gNB_COMMON common_vars; - prach_list_t prach_list; + spsc_q_t prach_ru_queue; + spsc_q_t prach_l1rx_queue; // TODO: can we remove c from NR_gNB_DLSCH_t and put it on the stack? NR_gNB_DLSCH_t *dlsch; NR_gNB_PRS prs_vars; diff --git a/openair1/SCHED_NR/nr_prach_procedures.c b/openair1/SCHED_NR/nr_prach_procedures.c index fcaf2f1c1b..929bf476f3 100644 --- a/openair1/SCHED_NR/nr_prach_procedures.c +++ b/openair1/SCHED_NR/nr_prach_procedures.c @@ -21,20 +21,14 @@ int get_nr_prach_duration(uint8_t prach_format) return val[prach_format]; } -void L1_nr_prach_procedures(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_rach_indication_t *rach_ind) +void L1_nr_prach_procedures(PHY_VARS_gNB *gNB, prach_item_t *prach_id, nfapi_nr_rach_indication_t *rach_ind) { + const frame_t frame = prach_id->frame; + const slot_t slot = prach_id->slot; rach_ind->sfn = frame; rach_ind->slot = slot; - rach_ind->number_of_pdus = 0; - - prach_item_t *prach_id = find_nr_prach(&gNB->prach_list, frame, slot, gNB->frame_parms.nb_antennas_rx, NR_SEARCH_EXIST); - if (!prach_id) { - return; - } - nfapi_nr_prach_pdu_t *prach_pdu = &prach_id->pdu; LOG_D(NR_PHY_RACH, "%d.%d, prachstart slot %d prach entry occas %d\n", frame, slot, prach_id->slot, prach_pdu->num_prach_ocas); - const int prach_start_slot = prach_id->slot; int N_dur = get_nr_prach_duration(prach_pdu->prach_format); for (int prach_oc = 0; prach_oc < prach_pdu->num_prach_ocas; prach_oc++) { @@ -63,7 +57,7 @@ void L1_nr_prach_procedures(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_rac "[RAPROC] %d.%d Initiating RA procedure with preamble %d, energy %d.%d dB (I0 %d, thres %d), delay %d start symbol " "%u freq index %u\n", frame, - prach_start_slot, + slot, res.max_preamble, res.max_preamble_energy / 10, res.max_preamble_energy % 10, @@ -100,7 +94,6 @@ void L1_nr_prach_procedures(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_rac if (gNB->prach_energy_counter < NUM_PRACH_RX_FOR_NOISE_ESTIMATE) gNB->prach_energy_counter++; } // if prach_id>0 - rach_ind->slot = prach_start_slot; LOG_D(NR_PHY_RACH, "Freeing PRACH entry\n"); - free_nr_prach_entry(&gNB->prach_list, prach_id); + free_nr_prach_entry(prach_id); } diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index 686f88a5fa..33ee17f374 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -1330,9 +1330,7 @@ void nr_save_ul_tti_req(PHY_VARS_gNB *gNB, nfapi_nr_ul_tti_request_t *UL_tti_req break; case NFAPI_NR_UL_CONFIG_PRACH_PDU_TYPE: { nfapi_nr_prach_pdu_t *prach_pdu = &UL_tti_req->pdus_list[i].prach_pdu; - prach_item_t *prach = nr_schedule_rx_prach(gNB, UL_tti_req->SFN, UL_tti_req->Slot, prach_pdu); - if (!prach) - LOG_W(NR_PHY_RACH, "Error in scheduling rach\n"); + nr_schedule_rx_prach(gNB, UL_tti_req->SFN, UL_tti_req->Slot, prach_pdu); } break; case NFAPI_NR_UL_CONFIG_SRS_PDU_TYPE: nr_fill_srs(gNB, UL_tti_req->SFN, UL_tti_req->Slot, &UL_tti_req->pdus_list[i].srs_pdu); diff --git a/openair1/SCHED_NR/sched_nr.h b/openair1/SCHED_NR/sched_nr.h index 7ff044b46c..ba1d00457b 100644 --- a/openair1/SCHED_NR/sched_nr.h +++ b/openair1/SCHED_NR/sched_nr.h @@ -18,7 +18,7 @@ void phy_procedures_gNB_TX(PHY_VARS_gNB *gNB, int slot); void nr_save_ul_tti_req(PHY_VARS_gNB *gNB, nfapi_nr_ul_tti_request_t *UL_tti_req); int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, NR_UL_IND_t *UL_INFO); -void L1_nr_prach_procedures(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_rach_indication_t *rach_ind); +void L1_nr_prach_procedures(PHY_VARS_gNB *gNB, prach_item_t *prach_id, nfapi_nr_rach_indication_t *rach_ind); void nr_common_signal_procedures (PHY_VARS_gNB *gNB,int frame,int slot, const nfapi_nr_dl_tti_ssb_pdu *ssb_pdu); void nr_feptx_ofdm(RU_t *ru,int frame_tx,int tti_tx); void nr_feptx0(RU_t *ru,int tti_tx,int first_symbol, int num_symbols, int aa); diff --git a/openair1/SIMULATION/NR_PHY/prachsim.c b/openair1/SIMULATION/NR_PHY/prachsim.c index 6e29abd9a5..b47b35b470 100644 --- a/openair1/SIMULATION/NR_PHY/prachsim.c +++ b/openair1/SIMULATION/NR_PHY/prachsim.c @@ -547,12 +547,10 @@ int main(int argc, char **argv){ phy_init_nr_gNB(gNB); nr_phy_init_RU(ru); - prach_item_t *prach_data = calloc(1,sizeof(prach_item_t)); - gNB->prach_list.list[0] = prach_data; - nfapi_nr_prach_pdu_t *prach_pdu = &prach_data->pdu; - prach_pdu->num_cs = get_NCS(NCS_config, format0, restrictedSetConfig); - prach_config->num_prach_fd_occasions_list[fd_occasion].num_root_sequences.value = 1+(64/(N_ZC/prach_pdu->num_cs)); - prach_pdu->prach_format = prach_format; + nfapi_nr_prach_pdu_t prach_pdu = {0}; + prach_pdu.num_cs = get_NCS(NCS_config, format0, restrictedSetConfig); + prach_config->num_prach_fd_occasions_list[fd_occasion].num_root_sequences.value = 1+(64/(N_ZC/prach_pdu.num_cs)); + prach_pdu.prach_format = prach_format; // Configure UE UE = malloc(sizeof(PHY_VARS_NR_UE)); @@ -749,14 +747,14 @@ int main(int argc, char **argv){ } nfapi_nr_prach_config_t *cfg = &gNB->gNB_config.prach_config; - nfapi_nr_num_prach_fd_occasions_t *occ = &cfg->num_prach_fd_occasions_list[prach_pdu->num_ra]; - prach_pdu->num_prach_ocas=1; + nfapi_nr_num_prach_fd_occasions_t *occ = &cfg->num_prach_fd_occasions_list[prach_pdu.num_ra]; + prach_pdu.num_prach_ocas=1; prach_item_t *in = malloc(sizeof(prach_item_t) + sizeof(c16_t) * gNB->gNB_config.carrier_config.num_rx_ant.value * NUMBER_OF_NR_RU_PRACH_OCCASIONS_MAX * NR_PRACH_SEQ_LEN_L); *in = (prach_item_t){.frame = frame, .slot = slot, - .pdu = *prach_pdu, + .pdu = prach_pdu, .rootSequenceIndex = occ->prach_root_sequence_index.value, .numrootSequenceIndex = occ->num_root_sequences.value, .msg1_frequencystart = occ->k1.value, @@ -772,7 +770,7 @@ int main(int argc, char **argv){ if (n_frames == 1) LOG_I(PHY, "ncs %d,num_seq %d\n", - prach_pdu->num_cs, + prach_pdu.num_cs, prach_config->num_prach_fd_occasions_list[fd_occasion].num_root_sequences.value); rx_prach_out_t out = rx_nr_prach(in, prachOccasion); @@ -823,7 +821,6 @@ int main(int argc, char **argv){ nr_phy_free_RU(ru); free(RC.ru[0]); free(RC.ru); - free(prach_data); phy_free_nr_gNB(gNB); // allocated in set_tdd_config_nr() int nb_slots_to_set = (1<gNB_list[0]->prach_list, *frame, *slot, ru->nr_frame_parms->nb_antennas_rx, SEARCH_EXIST); - if (prach_id) { + prach_item_t p; + PHY_VARS_gNB *gNB = ru->gNB_list[0]; + fsn_t now = {.f = *frame, .s = *slot, .mu = gNB->frame_parms.numerology_index}; + if (get_next_nr_prach(&gNB->prach_ru_queue, &now, &p)) { struct xran_fh_config *fh_cfg = get_xran_fh_config(0); int slots_per_subframe = 1 << fh_cfg->frame_conf.nNumerology; uint32_t subframe = *slot / slots_per_subframe; // `slot` = slot in which PRACH is received // PRACH occasion in a frame if and only if SFN % x == y, TS 38.211 Table 6.3.3.2-2/3/4 nr_prach_info_t prach_info = get_prach_info(0); bool is_prach_frame = (*frame % prach_info.x == prach_info.y); - bool is_prach_slot = is_prach_frame && xran_is_prach_slot(0, subframe, (prach_id->slot % slots_per_subframe)); // `prach_id->slot` = slot in which PRACH is scheduled + bool is_prach_slot = is_prach_frame && xran_is_prach_slot(0, subframe, (p.slot % slots_per_subframe)); // `p.slot` = slot in which PRACH is scheduled if (is_prach_slot) { - ru_info.prach_buf = prach_id->prach_buf; + ru_info.prach_buf = p.prach_buf; } else { - LOG_W(HW, "[%d.%d] Expected PRACH reception of scheduled slot %d\n", *frame, *slot, prach_id->slot); + LOG_W(HW, "[%d.%d] Expected PRACH reception of scheduled slot %d\n", *frame, *slot, p.slot); } } @@ -232,6 +235,15 @@ void oran_fh_if4p5_south_in(RU_t *ru, int *frame, int *slot) printf("ORAN: %d.%d ORAN_fh_if4p5_south_in ERROR in RX function \n", f, sl); } + if (ru_info.prach_buf) { + // xran_fh_rx_read_slot() should have read PRACH, write back to queue + bool success = spsc_q_put(&gNB->prach_l1rx_queue, &p, sizeof(p)); + // assume prach_l1rx_queue never full: prach_ru_queue filled at + // constant pace, but prach_l1rx_queue emptied as fast as possible, + // see rx_func() + DevAssert(success); + } + int slots_per_frame = 10 << (ru->openair0_cfg.nr_scs_for_raster); proc->tti_rx = sl; proc->frame_rx = f;