diff --git a/test/bench/bench_components_mlkem.c b/test/bench/bench_components_mlkem.c index e8e064fad..b50e3eaac 100644 --- a/test/bench/bench_components_mlkem.c +++ b/test/bench/bench_components_mlkem.c @@ -32,7 +32,10 @@ static int cmp_uint64_t(const void *a, const void *b) { - return (int)((*((const uint64_t *)a)) - (*((const uint64_t *)b))); + const uint64_t va = *((const uint64_t *)a); + const uint64_t vb = *((const uint64_t *)b); + + return (va > vb) - (va < vb); } #define CHECK(x) \ diff --git a/test/bench/bench_mlkem.c b/test/bench/bench_mlkem.c index a74509543..7fc54638d 100644 --- a/test/bench/bench_mlkem.c +++ b/test/bench/bench_mlkem.c @@ -37,7 +37,10 @@ static int cmp_uint64_t(const void *a, const void *b) { - return (int)((*((const uint64_t *)a)) - (*((const uint64_t *)b))); + const uint64_t va = *((const uint64_t *)a); + const uint64_t vb = *((const uint64_t *)b); + + return (va > vb) - (va < vb); } static void print_median(const char *txt, uint64_t cyc[MLK_BENCHMARK_NTESTS]) @@ -54,7 +57,7 @@ static void print_percentile_legend(void) printf("%21s", "percentile"); for (i = 0; i < sizeof(percentiles) / sizeof(percentiles[0]); i++) { - printf("%9d", percentiles[i]); + printf("%12d", percentiles[i]); } printf("\n"); } @@ -66,8 +69,8 @@ static void print_percentiles(const char *txt, printf("%10s percentiles:", txt); for (i = 0; i < sizeof(percentiles) / sizeof(percentiles[0]); i++) { - printf("%9" PRIu64, (cyc)[MLK_BENCHMARK_NTESTS * percentiles[i] / 100] / - MLK_BENCHMARK_NITERATIONS); + printf("%12" PRIu64, (cyc)[MLK_BENCHMARK_NTESTS * percentiles[i] / 100] / + MLK_BENCHMARK_NITERATIONS); } printf("\n"); } diff --git a/test/hal/hal.c b/test/hal/hal.c index 8b5e8c05f..4b3fbfb35 100644 --- a/test/hal/hal.c +++ b/test/hal/hal.c @@ -49,7 +49,7 @@ void enable_cyclecounter(void) {} void disable_cyclecounter(void) {} -uint64_t get_cyclecounter(void) { return k_cycle_get_32(); } +uint64_t get_cyclecounter(void) { return k_cycle_get_64(); } #elif defined(PMU_CYCLES) diff --git a/test/zephyr/app/CMakeLists.txt b/test/zephyr/app/CMakeLists.txt index 42f9cc860..0d014b81c 100644 --- a/test/zephyr/app/CMakeLists.txt +++ b/test/zephyr/app/CMakeLists.txt @@ -32,6 +32,14 @@ target_include_directories(app PRIVATE # emulator with the test's exit code; the NUCLEO-N657X0-Q hardware target needs # its own shim plus libc wrappers that route stdio over ITM/SWO. if(ZEPHYR_NUCLEO_N657X0_Q) + # The STM32N6 SystemInit() fallback derives VTOR from &g_pfnVectors. With + # Zephyr data in DTCM that symbol lives in RAM, so force VTOR to the ITCM + # vector table instead. + zephyr_compile_definitions( + USER_VECT_TAB_ADDRESS + VECT_TAB_BASE_ADDRESS=0x10000000 + VECT_TAB_OFFSET=0x0 + ) target_sources(app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/shim_nucleo_n657x0_q.c) target_compile_definitions(app PRIVATE printf=__wrap_printf diff --git a/test/zephyr/app/nucleo_n657x0_q.conf b/test/zephyr/app/nucleo_n657x0_q.conf index 16e60e76d..cadbec4de 100644 --- a/test/zephyr/app/nucleo_n657x0_q.conf +++ b/test/zephyr/app/nucleo_n657x0_q.conf @@ -1,12 +1,16 @@ # Copyright (c) The mlkem-native project authors # SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT -# The board default exposes a 2 MiB SRAM window at 0x34000000; its first -# 400 KiB is FLEXRAM. The NUCLEO runner repurposes FLEXMEM to expand the TCMs -# to 256 KiB ITCM + 256 KiB DTCM, so keep the Zephyr image above that low -# window and leave fixed host buffers after the linked image. -CONFIG_SRAM_BASE_ADDRESS=0x34080000 -CONFIG_SRAM_SIZE=192 +# The NUCLEO runner repurposes FLEXMEM before loading the ELF, expanding the +# Cortex-M55 TCMs to 256 KiB ITCM + 256 KiB DTCM. Link the RAM-loaded Zephyr +# image as XIP from ITCM and keep runtime data/stacks in DTCM. The fixed +# bootargs handoff block remains in AXISRAM at 0x340b0000. +CONFIG_XIP=y +CONFIG_FLASH_BASE_ADDRESS=0x10000000 +CONFIG_FLASH_SIZE=256 +CONFIG_SRAM_BASE_ADDRESS=0x30000000 +CONFIG_SRAM_SIZE=256 +CONFIG_SOC_EARLY_RESET_HOOK=y # The component benchmark app keeps five 8 KiB scratch buffers on the main # stack; measured max static stack depth is about 49.5 KiB, so 32 KiB is not diff --git a/test/zephyr/app/nucleo_n657x0_q.overlay b/test/zephyr/app/nucleo_n657x0_q.overlay index 15d78f7a9..8596beaf0 100644 --- a/test/zephyr/app/nucleo_n657x0_q.overlay +++ b/test/zephyr/app/nucleo_n657x0_q.overlay @@ -4,6 +4,18 @@ */ / { + mlk_itcm: memory@10000000 { + compatible = "zephyr,memory-region", "arm,itcm"; + reg = <0x10000000 0x00040000>; + zephyr,memory-region = "ITCM"; + }; + + mlk_dtcm: memory@30000000 { + compatible = "zephyr,memory-region", "arm,dtcm"; + reg = <0x30000000 0x00040000>; + zephyr,memory-region = "DTCM"; + }; + mlk_axisram: memory@34080000 { compatible = "zephyr,memory-region", "mmio-sram"; reg = <0x34080000 0x00030000>; @@ -11,6 +23,7 @@ }; chosen { - zephyr,sram = &mlk_axisram; + zephyr,flash = &mlk_itcm; + zephyr,sram = &mlk_dtcm; }; }; diff --git a/test/zephyr/app/shim_nucleo_n657x0_q.c b/test/zephyr/app/shim_nucleo_n657x0_q.c index 640c9c37d..426f1a339 100644 --- a/test/zephyr/app/shim_nucleo_n657x0_q.c +++ b/test/zephyr/app/shim_nucleo_n657x0_q.c @@ -50,6 +50,28 @@ static char nucleo_stdout_buf[8192]; static size_t nucleo_stdout_len; static bool nucleo_swo_enabled; +/* + * Zephyr calls this before stack setup or normal RAM initialization. The + * NUCLEO runner has already expanded DTCM to 256 KiB, and the image now keeps + * runtime data/stacks there, so scrub the whole DTCM window before Zephyr's + * early stack, BSS, and MPU setup touch it. + */ +__attribute__((naked, used)) void soc_early_reset_hook(void) +{ + __asm__ volatile( + "movs r2, #0\n" + "movs r3, #0\n" + "ldr r0, =0x30000000\n" + "ldr r1, =0x30040000\n" + "1:\n" + "strd r2, r3, [r0], #8\n" + "cmp r0, r1\n" + "blo 1b\n" + "dsb\n" + "isb\n" + "bx lr\n"); +} + static void nucleo_swo_pin_setup(void) { LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_GPIOB); diff --git a/test/zephyr/nucleo_n657x0_q/README.md b/test/zephyr/nucleo_n657x0_q/README.md index c9785cb16..51df6aea3 100644 --- a/test/zephyr/nucleo_n657x0_q/README.md +++ b/test/zephyr/nucleo_n657x0_q/README.md @@ -82,8 +82,9 @@ Zephyr's default NUCLEO-N657X0-Q RAM region is a 2 MiB SRAM window starting at `0x34000000`; its first 400 KiB is the STM32N657X0 FLEXRAM allocation. After reset, the Cortex-M55 TCM layout is 64 KiB ITCM and 128 KiB DTCM. The NUCLEO runner repurposes FLEXMEM to expand the TCMs to 256 KiB ITCM and 256 KiB DTCM -before each RAM-loaded Zephyr test, so the test image is linked above that low -AXISRAM/FLEXRAM window instead of using the board default: +before each RAM-loaded Zephyr test. The test image is then linked as XIP from +ITCM at `0x10000000`, with runtime data and stacks in DTCM at `0x30000000`; +the fixed bootargs handoff block remains in AXISRAM at `0x340b0000`. 1. OpenOCD attaches with `reset_config none`. 2. The script enables the SYSCFG clock by setting bit 0 in `RCC_APB4ENSR2` at @@ -95,16 +96,12 @@ AXISRAM/FLEXRAM window instead of using the board default: 6. It runs `reset run` so the expanded layout is applied before the Zephyr ELF is loaded. -The board is RAM-loaded, not flashed. Zephyr's default NUCLEO-N657X0-Q RAM -region is a 2 MiB SRAM window starting at `0x34000000`; its first 400 KiB is -the STM32N657X0 FLEXRAM allocation. The hardware benchmark setup is the reason -this target uses a board-specific memory layout: before each run, the hardware -wrapper repurposes FLEXMEM to expand the Cortex-M55 TCMs by setting the low -byte of `SYSCFG_CM55TCMCR` to `0x99`, selecting 256 KiB ITCM and 256 KiB DTCM, -then resets the target so that layout is active. Because this changes the low -AXISRAM/FLEXRAM window that the default Zephyr layout would use, the test image -is instead linked in a 192 KiB AXISRAM window at `0x34080000`; see -`nucleo_n657x0_q/README.md` for the exact register sequence. +The board is RAM-loaded, not flashed. The hardware benchmark setup is the +reason this target uses a board-specific memory layout: before each run, the +hardware wrapper repurposes FLEXMEM to expand the Cortex-M55 TCMs by setting +the low byte of `SYSCFG_CM55TCMCR` to `0x99`, selecting 256 KiB ITCM and +256 KiB DTCM, then resets the target so that layout is active. The Zephyr image +is linked as XIP from ITCM, while runtime data and stacks are placed in DTCM. Test stdout is captured over ITM stimulus port 0 through SWO. This avoids using semihosting for normal output: semihosting is blocking and each host operation diff --git a/test/zephyr/platform.mk b/test/zephyr/platform.mk index b045eccd8..3544f4458 100644 --- a/test/zephyr/platform.mk +++ b/test/zephyr/platform.mk @@ -61,6 +61,7 @@ ZEPHYR_APP := $(PLATFORM_PATH)/app ZEPHYR_BUILD_DIR := $(BUILD_DIR)/zephyr/$(ZEPHYR_TARGET) ZEPHYR_ACTIVE_TARGET := $(BUILD_DIR)/zephyr/.active-target ZEPHYR_APP_INPUTS := \ + $(PLATFORM_PATH)/platform.mk \ $(ZEPHYR_APP)/CMakeLists.txt \ $(ZEPHYR_APP)/Kconfig \ $(ZEPHYR_APP)/prj.conf \ @@ -111,12 +112,20 @@ CFLAGS += -DNTESTS_FUNC=3 -DNTESTS_KAT=100 \ # adds must not reach the Zephyr toolchain, which selects the target arch itself. ZEPHYR_TEST_CFLAGS = $(subst \",\\\",$(patsubst -Imlkem,-I$(abspath mlkem),$(CFLAGS))) # Keep make-exported project flags out of Zephyr's own CMake build; the app -# sources get those flags explicitly via ZEPHYR_TEST_CFLAGS. +# sources get those flags explicitly via ZEPHYR_TEST_CFLAGS. Also overwrite +# cached CMake global flags, so a prior polluted build directory cannot keep +# applying project warning flags to Zephyr's generated/helper targets. ZEPHYR_CMAKE_ENV := env -u CFLAGS -u CXXFLAGS -u CPPFLAGS -u LDFLAGS +ZEPHYR_CMAKE_CLEAR_FLAGS := \ + -DCMAKE_ASM_FLAGS:STRING= \ + -DCMAKE_C_FLAGS:STRING= \ + -DCMAKE_CXX_FLAGS:STRING= \ + -DCMAKE_EXE_LINKER_FLAGS:STRING= CUSTOM_BUILD = \ echo " ZEPHYR $(ZEPHYR_TARGET): $(notdir $@)" && \ $(ZEPHYR_CMAKE_ENV) cmake -GNinja -S $(ZEPHYR_APP) -B $(ZEPHYR_OUT) \ + $(ZEPHYR_CMAKE_CLEAR_FLAGS) \ -DBOARD=$(ZEPHYR_BOARD) \ -DZEPHYR_NATIVE_ROOT=$(CURDIR) \ -DZEPHYR_TEST_SRCS="$(strip $(TEST_SRCS))" \ @@ -126,7 +135,8 @@ CUSTOM_BUILD = \ $(ZEPHYR_TARGET_CMAKE_ARGS) \ -DUSER_CACHE_DIR=$(abspath $(ZEPHYR_OUT)/.cache) \ >/dev/null && \ - $(ZEPHYR_CMAKE_ENV) cmake --build $(ZEPHYR_OUT) >/dev/null && \ + $(ZEPHYR_CMAKE_ENV) cmake --build $(ZEPHYR_OUT) >$(ZEPHYR_OUT)/build.log 2>&1 || \ + { cat $(ZEPHYR_OUT)/build.log; exit 1; }; \ cp $(ZEPHYR_OUT)/zephyr/zephyr.elf $@ # A custom build links the test sources directly rather than from objects, so