diff --git a/tests/subsys/nrfs/CMakeLists.txt b/tests/subsys/nrfs/CMakeLists.txt new file mode 100644 index 00000000000..af485e3dc2f --- /dev/null +++ b/tests/subsys/nrfs/CMakeLists.txt @@ -0,0 +1,19 @@ +# +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 +# + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) + +if(NOT SYSBUILD) + message(FATAL_ERROR + " This is a multi-image application that should be built using sysbuild.\n" + " Add --sysbuild argument to west build command to prepare all the images.") +endif() + +project(nrfs) + +target_sources(app PRIVATE src/main.c) diff --git a/tests/subsys/nrfs/Kconfig.sysbuild b/tests/subsys/nrfs/Kconfig.sysbuild new file mode 100644 index 00000000000..f281257725f --- /dev/null +++ b/tests/subsys/nrfs/Kconfig.sysbuild @@ -0,0 +1,9 @@ +# Copyright (c) 2025 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +source "${ZEPHYR_BASE}/share/sysbuild/Kconfig" + +config REMOTE_BOARD + string + default "$(BOARD)/nrf54h20/cpurad" if SOC_NRF54H20_CPUAPP + default "$(BOARD)/nrf54h20/cpuapp" if SOC_NRF54H20_CPURAD diff --git a/tests/subsys/nrfs/README.txt b/tests/subsys/nrfs/README.txt new file mode 100644 index 00000000000..45888e61c3c --- /dev/null +++ b/tests/subsys/nrfs/README.txt @@ -0,0 +1,7 @@ +The purpose of this test suite is to validate +NRFS services request handling performance. + +These reaquest are propagated over IPC. + +MRAM latency serivice and Temperature service +are used as benchmarks. diff --git a/tests/subsys/nrfs/prj.conf b/tests/subsys/nrfs/prj.conf new file mode 100644 index 00000000000..c1a01949d60 --- /dev/null +++ b/tests/subsys/nrfs/prj.conf @@ -0,0 +1,21 @@ +CONFIG_ZTEST=y +CONFIG_TEST_USERSPACE=y + +CONFIG_HEAP_MEM_POOL_SIZE=2048 +CONFIG_ISR_STACK_SIZE=1024 +CONFIG_MAIN_STACK_SIZE=4096 +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 + +CONFIG_NRFS=y +CONFIG_NRFS_MRAM_SERVICE_ENABLED=y +CONFIG_NRFS_TEMP_SERVICE_ENABLED=y +CONFIG_CLOCK_CONTROL=n + +CONFIG_ASSERT=y + +CONFIG_SERIAL=y +CONFIG_PRINTK=y +CONFIG_LOG=y +CONFIG_LOG_MODE_IMMEDIATE=n +CONFIG_CONSOLE=y +CONFIG_UART_CONSOLE=y diff --git a/tests/subsys/nrfs/remote/CMakeLists.txt b/tests/subsys/nrfs/remote/CMakeLists.txt new file mode 100644 index 00000000000..a0b3aab8b3d --- /dev/null +++ b/tests/subsys/nrfs/remote/CMakeLists.txt @@ -0,0 +1,12 @@ +# +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 +# + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(remote) + +target_sources(app PRIVATE ../src/main.c) diff --git a/tests/subsys/nrfs/remote/prj.conf b/tests/subsys/nrfs/remote/prj.conf new file mode 100644 index 00000000000..c1a01949d60 --- /dev/null +++ b/tests/subsys/nrfs/remote/prj.conf @@ -0,0 +1,21 @@ +CONFIG_ZTEST=y +CONFIG_TEST_USERSPACE=y + +CONFIG_HEAP_MEM_POOL_SIZE=2048 +CONFIG_ISR_STACK_SIZE=1024 +CONFIG_MAIN_STACK_SIZE=4096 +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 + +CONFIG_NRFS=y +CONFIG_NRFS_MRAM_SERVICE_ENABLED=y +CONFIG_NRFS_TEMP_SERVICE_ENABLED=y +CONFIG_CLOCK_CONTROL=n + +CONFIG_ASSERT=y + +CONFIG_SERIAL=y +CONFIG_PRINTK=y +CONFIG_LOG=y +CONFIG_LOG_MODE_IMMEDIATE=n +CONFIG_CONSOLE=y +CONFIG_UART_CONSOLE=y diff --git a/tests/subsys/nrfs/src/main.c b/tests/subsys/nrfs/src/main.c new file mode 100644 index 00000000000..affc38720b3 --- /dev/null +++ b/tests/subsys/nrfs/src/main.c @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include + +#define IPC_BACKEND_CONNECTION_TIMEOUT_MS 5000 +#define NUM_OF_MRAM_REQUESTS 10000 +#define MRAM_REQUESTS_DEAD_TIME_MS 1 +#define NUM_OF_TEMP_REQUESTS 100 +#define TEMP_REQUESTS_DEAD_TIME_MS 100 + +struct ipc_perf_result { + uint32_t sent_requests; + uint32_t handled_requests; + uint32_t failed_to_send; +}; + +static volatile uint32_t tst_perf_served_mram_requests; +static volatile uint32_t tst_perf_served_temp_meas_requests; + +/* + * Callback function for counting handled TEMP service requests + */ +static void temp_handler_for_performance_test(nrfs_temp_evt_t const *p_evt, void *context) +{ + switch (p_evt->type) { + case NRFS_TEMP_EVT_MEASURE_DONE: + tst_perf_served_temp_meas_requests++; + default: + break; + } +} + +/* + * Callback function for counting handled MRAM service requests + */ +static void mram_latency_handler_for_performance_test(nrfs_mram_latency_evt_t const *p_evt, + void *context) +{ + switch (p_evt->type) { + case NRFS_MRAM_LATENCY_REQ_APPLIED: + tst_perf_served_mram_requests++; + default: + break; + } +} + +/* + * Test NRFS MRAM latency service requests handling performance + */ +ZTEST(nrfs_stress_test, test_mram_nrfs_requests_performance) +{ + struct ipc_perf_result tst_ipc_perf_result; + uint32_t request_counter = 0; + volatile int32_t tst_ctx = 1; + + TC_PRINT("START test_mram_nrfs_requests_performance\n"); + zassert_equal(nrfs_mram_init(mram_latency_handler_for_performance_test), NRFS_SUCCESS, + "Failed to initialise NRFS MRAM latency service"); + memset(&tst_ipc_perf_result, 0, sizeof(tst_ipc_perf_result)); + while (request_counter < NUM_OF_MRAM_REQUESTS) { + if (nrfs_mram_set_latency(true, (void *)tst_ctx) == NRFS_SUCCESS) { + tst_ipc_perf_result.sent_requests++; + } else { + tst_ipc_perf_result.failed_to_send++; + } + k_msleep(MRAM_REQUESTS_DEAD_TIME_MS); + tst_ctx++; + request_counter++; + } + /* wait for any remaining requests responses */ + k_msleep(10 * MRAM_REQUESTS_DEAD_TIME_MS); + tst_ipc_perf_result.handled_requests = tst_perf_served_mram_requests; + TC_PRINT("STOP test_mram_nrfs_requests_performance\n"); + TC_PRINT("SENT: %d, HANDLED: %d, FAILED TO SEND: %d\n", tst_ipc_perf_result.sent_requests, + tst_ipc_perf_result.handled_requests, tst_ipc_perf_result.failed_to_send); + zassert_equal(tst_ipc_perf_result.sent_requests, tst_ipc_perf_result.handled_requests, + "NRFS MRAM requests sent != served"); +} + +/* + * Test temperature service requests handling performance + */ +ZTEST(nrfs_stress_test, test_temperature_nrfs_requests_performance) +{ + struct ipc_perf_result tst_ipc_perf_result; + uint32_t request_counter = 0; + volatile int32_t tst_ctx = 1; + + TC_PRINT("START test_temperature_nrfs_requests_performance\n"); + zassert_equal(nrfs_temp_init(temp_handler_for_performance_test), NRFS_SUCCESS, + "Failed to initialise NRFS temperature service"); + memset((void *)&tst_ipc_perf_result, 0, sizeof(tst_ipc_perf_result)); + while (request_counter < NUM_OF_TEMP_REQUESTS) { + if (nrfs_temp_measure_request((void *)tst_ctx) == NRFS_SUCCESS) { + tst_ipc_perf_result.sent_requests++; + } else { + tst_ipc_perf_result.failed_to_send++; + } + k_msleep(TEMP_REQUESTS_DEAD_TIME_MS); + tst_ctx++; + request_counter++; + } + /* wait for any remaining requests responses */ + k_msleep(10 * TEMP_REQUESTS_DEAD_TIME_MS); + tst_ipc_perf_result.handled_requests = tst_perf_served_temp_meas_requests; + TC_PRINT("STOP test_temperature_nrfs_requests_performance\n"); + TC_PRINT("SENT: %d, HANDLED: %d, FAILED TO SEND: %d\n", tst_ipc_perf_result.sent_requests, + tst_ipc_perf_result.handled_requests, tst_ipc_perf_result.failed_to_send); + zassert_equal(tst_ipc_perf_result.sent_requests, tst_ipc_perf_result.handled_requests, + "NRFS TEMP requests sent != served"); +} + +/* + * Test setup + */ +static void *test_setup(void) +{ + int ret; + + tst_perf_served_mram_requests = 0; + tst_perf_served_temp_meas_requests = 0; + + TC_PRINT("Hello World! %s\n", CONFIG_BOARD_TARGET); + TC_PRINT("Waiting for NRFS backend init\n"); + + /* Wait for IPC backend connection */ + ret = nrfs_backend_wait_for_connection(K_MSEC(IPC_BACKEND_CONNECTION_TIMEOUT_MS)); + zassert_equal(ret, 0, "Failed to establih NRFS backend connection. err: %d", ret); + return NULL; +} + +ZTEST_SUITE(nrfs_stress_test, NULL, test_setup, NULL, NULL, NULL); diff --git a/tests/subsys/nrfs/sysbuild.cmake b/tests/subsys/nrfs/sysbuild.cmake new file mode 100644 index 00000000000..41e281ff18d --- /dev/null +++ b/tests/subsys/nrfs/sysbuild.cmake @@ -0,0 +1,18 @@ +# Copyright (c) 2025 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +if("${SB_CONFIG_REMOTE_BOARD}" STREQUAL "") + message(FATAL_ERROR "REMOTE_BOARD must be set to a valid board name") +endif() + +# Add remote project +ExternalZephyrProject_Add( + APPLICATION remote + SOURCE_DIR ${APP_DIR}/remote + BOARD ${SB_CONFIG_REMOTE_BOARD} + BOARD_REVISION ${BOARD_REVISION} + ) + +# Add a dependency so that the remote image will be built and flashed first +add_dependencies(nrfs remote) +sysbuild_add_dependencies(FLASH nrfs remote) diff --git a/tests/subsys/nrfs/testcase.yaml b/tests/subsys/nrfs/testcase.yaml new file mode 100644 index 00000000000..527ff477cd0 --- /dev/null +++ b/tests/subsys/nrfs/testcase.yaml @@ -0,0 +1,14 @@ +common: + platform_allow: + - nrf54h20dk/nrf54h20/cpuapp + - nrf54h20dk/nrf54h20/cpurad + integration_platforms: + - nrf54h20dk/nrf54h20/cpuapp + - nrf54h20dk/nrf54h20/cpurad + tags: + - nrfs + harness: ztest + sysbuild: true + +tests: + subsys.nrfs.stress_test: {}