From 2287cc7b2d9120467f86b24830aeb2b17c8230b8 Mon Sep 17 00:00:00 2001 From: Jan Zyczkowski Date: Sun, 23 Nov 2025 23:36:21 +0100 Subject: [PATCH 1/3] samples: radio_loader: Add radio core firmware loader Add radio_loader sample for nRF54H20 that implements a two-stage boot process for the radio core. The loader copies firmware from MRAM to TCM (Tightly Coupled Memory) at boot time and jumps to the loaded firmware. The loader uses devicetree chosen nodes to specify source and destination memory regions: - zephyr,loaded-fw-src: Source partition in MRAM - zephyr,loaded-fw-dst: Destination region in TCM JIRA: NCSDK-36461 Signed-off-by: Jan Zyczkowski --- samples/nrf54h20/radio_loader/CMakeLists.txt | 13 +++ samples/nrf54h20/radio_loader/app.overlay | 18 ++++ samples/nrf54h20/radio_loader/prj.conf | 94 ++++++++++++++++++++ samples/nrf54h20/radio_loader/src/main.c | 78 ++++++++++++++++ samples/nrf54h20/radio_loader/testcase.yaml | 12 +++ sysbuild/CMakeLists.txt | 1 + sysbuild/Kconfig.radioloader | 30 +++++++ sysbuild/Kconfig.sysbuild | 1 + sysbuild/radioloader.cmake | 28 ++++++ 9 files changed, 275 insertions(+) create mode 100644 samples/nrf54h20/radio_loader/CMakeLists.txt create mode 100644 samples/nrf54h20/radio_loader/app.overlay create mode 100644 samples/nrf54h20/radio_loader/prj.conf create mode 100644 samples/nrf54h20/radio_loader/src/main.c create mode 100644 samples/nrf54h20/radio_loader/testcase.yaml create mode 100644 sysbuild/Kconfig.radioloader create mode 100644 sysbuild/radioloader.cmake diff --git a/samples/nrf54h20/radio_loader/CMakeLists.txt b/samples/nrf54h20/radio_loader/CMakeLists.txt new file mode 100644 index 00000000000..24d2670bad9 --- /dev/null +++ b/samples/nrf54h20/radio_loader/CMakeLists.txt @@ -0,0 +1,13 @@ +# +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) + +project(loader) + +target_sources(app PRIVATE src/main.c) diff --git a/samples/nrf54h20/radio_loader/app.overlay b/samples/nrf54h20/radio_loader/app.overlay new file mode 100644 index 00000000000..6850b954e7f --- /dev/null +++ b/samples/nrf54h20/radio_loader/app.overlay @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + + +/* Dummy chosen node to satisfy the build */ +/{ + chosen { + zephyr,loaded-fw-src = &cpuapp_slot0_partition; + zephyr,loaded-fw-dst = &cpurad_ram0; + }; +}; + +&cpuapp_slot0_partition { + reg = <0x92000 DT_SIZE_K(128)>; +}; diff --git a/samples/nrf54h20/radio_loader/prj.conf b/samples/nrf54h20/radio_loader/prj.conf new file mode 100644 index 00000000000..6f57c25fea8 --- /dev/null +++ b/samples/nrf54h20/radio_loader/prj.conf @@ -0,0 +1,94 @@ +# +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +# ============================================================================== +# Optimized minimal configuration for radio_loader +# ============================================================================== +# This loader only runs SYS_INIT(load_and_jump_to_firmware, EARLY, 0) which: +# 1. Copies firmware from MRAM to TCM using memcpy() +# 2. Jumps to the loaded firmware's reset handler +# No Zephyr services, threading, or drivers are needed. +# +# Memory footprint target: < 4 KB + +# ============================================================================== +# Power Management +# ============================================================================== +CONFIG_PM=n +CONFIG_PM_DEVICE=n + +# ============================================================================== +# Kernel - Threading Disabled +# ============================================================================== +# We never reach main() or start the scheduler, so disable all threading +CONFIG_MULTITHREADING=n +CONFIG_MAIN_STACK_SIZE=512 +CONFIG_THREAD_STACK_INFO=n + +# Disable kernel features that require threading/scheduler +CONFIG_EVENTS=n +CONFIG_POLL=n +CONFIG_TIMESLICING=n + +# ============================================================================== +# Console, Debug, and Logging +# ============================================================================== +# No console output needed - loader jumps immediately to firmware +CONFIG_CONSOLE=n +CONFIG_UART_CONSOLE=n +CONFIG_SERIAL=n +CONFIG_PRINTK=n +CONFIG_EARLY_CONSOLE=n +CONFIG_LOG=n + +# Banners and debug features +CONFIG_NCS_BOOT_BANNER=n +CONFIG_BOOT_BANNER=n +CONFIG_ERRNO=n + +# ============================================================================== +# Device Drivers +# ============================================================================== +# No peripheral drivers needed - we only use memcpy and jump +CONFIG_GPIO=n +CONFIG_PINCTRL=n +CONFIG_I2C=n +CONFIG_SPI=n +CONFIG_WATCHDOG=n + +# ============================================================================== +# Interrupt Management +# ============================================================================== +CONFIG_DYNAMIC_INTERRUPTS=n +CONFIG_IRQ_OFFLOAD=n +CONFIG_GEN_IRQ_VECTOR_TABLE=n +CONFIG_GEN_ISR_TABLES=n +CONFIG_GEN_SW_ISR_TABLE=n + +# ============================================================================== +# Hardware Protection +# ============================================================================== +CONFIG_HW_STACK_PROTECTION=n +CONFIG_ARM_MPU=n + +# ============================================================================== +# Security and Crypto +# ============================================================================== +# No crypto needed for simple memory copy operation +CONFIG_NRF_SECURITY=n +CONFIG_MBEDTLS_PSA_CRYPTO_C=n +CONFIG_PSA_CRYPTO_DRIVER_OBERON=n +CONFIG_PSA_CRYPTO=n +CONFIG_PSA_SSF_CRYPTO_CLIENT=n + +# ============================================================================== +# Memory Optimization +# ============================================================================== +CONFIG_HEAP_MEM_POOL_SIZE=0 +CONFIG_SYS_HEAP_RUNTIME_STATS=n + +# Use nano printf for minimal footprint (only used if PRINTK somehow gets enabled) +CONFIG_CBPRINTF_NANO=y diff --git a/samples/nrf54h20/radio_loader/src/main.c b/samples/nrf54h20/radio_loader/src/main.c new file mode 100644 index 00000000000..e1754b638c1 --- /dev/null +++ b/samples/nrf54h20/radio_loader/src/main.c @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include + +/* The loader uses devicetree chosen nodes to specify source + * and destination memory regions: + * - zephyr,loaded-fw-src: Source partition in NVM + * - zephyr,loaded-fw-dst: Destination region in RAM + */ + +#define LOADED_FW_NVM_NODE DT_CHOSEN(zephyr_loaded_fw_src) +#define LOADED_FW_NVM_PARTITION_NODE DT_PARENT(DT_PARENT(LOADED_FW_NVM_NODE)) +#define LOADED_FW_NVM_ADDR (DT_REG_ADDR(LOADED_FW_NVM_NODE) + \ + DT_REG_ADDR(LOADED_FW_NVM_PARTITION_NODE)) +#define LOADED_FW_NVM_SIZE DT_REG_SIZE(LOADED_FW_NVM_NODE) + +#define LOADED_FW_RAM_NODE DT_CHOSEN(zephyr_loaded_fw_dst) +#define LOADED_FW_RAM_ADDR DT_REG_ADDR(LOADED_FW_RAM_NODE) +#define LOADED_FW_RAM_SIZE DT_REG_SIZE(LOADED_FW_RAM_NODE) + +/* Verify devicetree configuration at build time */ +BUILD_ASSERT(DT_NODE_EXISTS(DT_CHOSEN(zephyr_loaded_fw_src)), + "Missing chosen node: zephyr,loaded-fw-src"); +BUILD_ASSERT(DT_NODE_EXISTS(DT_CHOSEN(zephyr_loaded_fw_dst)), + "Missing chosen node: zephyr,loaded-fw-dst"); +BUILD_ASSERT(LOADED_FW_NVM_SIZE <= LOADED_FW_RAM_SIZE, + "Firmware size exceeds available TCM RAM"); + +/** + * @brief Copy firmware from MRAM to TCM and jump to it + * + * This function runs as SYS_INIT(EARLY, 0) before main() and the scheduler. + * It copies the firmware from MRAM to TCM for optimal performance, then + * transfers execution to the loaded firmware's reset handler. + * + * This function never returns - execution transfers to the loaded firmware. + * + * @return 0 on success (never reached), -1 on failure (never reached) + */ +static int load_and_jump_to_firmware(void) +{ + /* Copy firmware from MRAM to TCM */ + memcpy((void *)LOADED_FW_RAM_ADDR, (void *)LOADED_FW_NVM_ADDR, LOADED_FW_NVM_SIZE); + + /* Extract reset handler from ARM Cortex-M vector table (entry 1) */ + uint32_t *vector_table = (uint32_t *)LOADED_FW_RAM_ADDR; + typedef void reset_handler_t(void); + reset_handler_t *reset_handler = (reset_handler_t *)(vector_table[1]); + + /* Jump to loaded firmware - this never returns */ + reset_handler(); + + /* Should never reach here */ + return -1; +} + +SYS_INIT(load_and_jump_to_firmware, EARLY, 0); + +/** + * @brief Main function - should never be reached + * + * If we reach main(), the firmware load and jump failed. + * This indicates a critical error in the loader. + */ +int main(void) +{ +#ifdef CONFIG_PRINTK + printk("ERROR: Firmware jump failed!\n"); +#endif + while (1) { + /* Hang here if jump fails */ + } + return -1; +} diff --git a/samples/nrf54h20/radio_loader/testcase.yaml b/samples/nrf54h20/radio_loader/testcase.yaml new file mode 100644 index 00000000000..b3583e1fc48 --- /dev/null +++ b/samples/nrf54h20/radio_loader/testcase.yaml @@ -0,0 +1,12 @@ +common: + sysbuild: true + tags: + - ci_build + - ci_samples_nrf54h20 + +tests: + radio_loader.nrf54h20dk_cpurad: + platform_allow: + - nrf54h20dk/nrf54h20/cpurad + integration_platforms: + - nrf54h20dk/nrf54h20/cpurad diff --git a/sysbuild/CMakeLists.txt b/sysbuild/CMakeLists.txt index 4f1791f37a7..359972ca9f3 100644 --- a/sysbuild/CMakeLists.txt +++ b/sysbuild/CMakeLists.txt @@ -1040,6 +1040,7 @@ include(${CMAKE_CURRENT_LIST_DIR}/appcore.cmake) include(${CMAKE_CURRENT_LIST_DIR}/netcore.cmake) include(${CMAKE_CURRENT_LIST_DIR}/flprcore.cmake) include(${CMAKE_CURRENT_LIST_DIR}/pprcore.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/radioloader.cmake) include(${CMAKE_CURRENT_LIST_DIR}/secureboot.cmake) include(${CMAKE_CURRENT_LIST_DIR}/mcuboot.cmake) diff --git a/sysbuild/Kconfig.radioloader b/sysbuild/Kconfig.radioloader new file mode 100644 index 00000000000..4f459f877f7 --- /dev/null +++ b/sysbuild/Kconfig.radioloader @@ -0,0 +1,30 @@ +# +# Copyright (c) 2025 Nordic Semiconductor +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +config NRF_RADIO_LOADER + bool "Radio Core Firmware Loader [EXPERIMENTAL]" + depends on SOC_NRF54H20 + select EXPERIMENTAL + help + Enable the radio loader that copies firmware from MRAM to TCM + (Tightly Coupled Memory) at boot time. + + The loader runs from MRAM and: + - Copies firmware from MRAM partition to TCM + - Jumps to the loaded firmware in TCM for optimal performance + + Requires devicetree memory map configuration with partitions + and chosen nodes defined in the project's overlay files. + +if NRF_RADIO_LOADER + +config NRF_RADIO_LOADER_BOARD + string + default "nrf54h20dk/nrf54h20/cpurad" + help + Target board for the radio loader application. + +endif # NRF_RADIO_LOADER diff --git a/sysbuild/Kconfig.sysbuild b/sysbuild/Kconfig.sysbuild index 7c38969e12b..601c867fb78 100644 --- a/sysbuild/Kconfig.sysbuild +++ b/sysbuild/Kconfig.sysbuild @@ -81,6 +81,7 @@ rsource "Kconfig.appcore" rsource "Kconfig.flprcore" rsource "Kconfig.netcore" rsource "Kconfig.pprcore" +rsource "Kconfig.radioloader" rsource "Kconfig.secureboot" rsource "Kconfig.mcuboot" rsource "Kconfig.dfu" diff --git a/sysbuild/radioloader.cmake b/sysbuild/radioloader.cmake new file mode 100644 index 00000000000..47ea3a838c3 --- /dev/null +++ b/sysbuild/radioloader.cmake @@ -0,0 +1,28 @@ +# +# Copyright (c) 2025 Nordic Semiconductor +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +# Radio Loader sysbuild integration +# This file handles automatic integration of the radio_loader application + +if(SB_CONFIG_NRF_RADIO_LOADER) + message(STATUS "Adding radio_loader application") + message(STATUS "Board: ${SB_CONFIG_NRF_RADIO_LOADER_BOARD}") + + # Add radio_loader as an external Zephyr project + ExternalZephyrProject_Add( + APPLICATION radio_loader + SOURCE_DIR "${ZEPHYR_NRF_MODULE_DIR}/samples/nrf54h20/radio_loader" + BOARD ${SB_CONFIG_NRF_RADIO_LOADER_BOARD} + BOARD_REVISION ${BOARD_REVISION} + ) + + + UpdateableImage_Add(APPLICATION radio_loader) + # Note: Memory map configuration should be provided by the user project + # in: sysbuild/radio_loader/boards/.overlay + # This overlay should define partitions and chosen nodes for the loader + +endif() From 7e8bb54b44b6a930c78440b00bd8d3e879c89873 Mon Sep 17 00:00:00 2001 From: Jan Zyczkowski Date: Mon, 24 Nov 2025 08:28:09 +0100 Subject: [PATCH 2/3] sysbuild: Add support for relocation pattern in nRF54H20 Update sdk-zephyr revision and enhance Kconfig handling. If the 'fw-to-relocate' chosen node exists, the firmware will: - automatically calculate the Load Memory Address (LMA) adjustment for firmware relocation. - Modified Kconfig handling in cmake modules to accommodate a new property "fw-to-relocate". - Ensured backward compatibility by falling back to "zephyr,code-partition" if ther is not property "fw-to-relocate". JIRA: NCSDK-36461 Signed-off-by: Jan Zyczkowski --- cmake/modules/kconfig.cmake | 29 +++++++++++++++++-- cmake/sysbuild/sign_nrf54h20.cmake | 6 +++- soc/nordic/nrf54h/Kconfig.defconfig | 10 +++++++ .../nrf54h/Kconfig.defconfig.nrf54h20_cpurad | 20 +++++++++++++ 4 files changed, 61 insertions(+), 4 deletions(-) create mode 100644 soc/nordic/nrf54h/Kconfig.defconfig create mode 100644 soc/nordic/nrf54h/Kconfig.defconfig.nrf54h20_cpurad diff --git a/cmake/modules/kconfig.cmake b/cmake/modules/kconfig.cmake index b84cef0a9fc..73910cabc0b 100644 --- a/cmake/modules/kconfig.cmake +++ b/cmake/modules/kconfig.cmake @@ -29,7 +29,11 @@ if(CONFIG_NCS_IS_VARIANT_IMAGE) else() include(${ZEPHYR_NRF_MODULE_DIR}/cmake/sysbuild/bootloader_dts_utils.cmake) - dt_chosen(code_partition PROPERTY "zephyr,code-partition") + dt_chosen(code_partition PROPERTY "fw-to-relocate") + if("${code_partition}" STREQUAL "") + dt_chosen(code_partition PROPERTY "zephyr,code-partition") + endif() + dt_partition_addr(code_partition_offset PATH "${code_partition}" REQUIRED) dt_reg_size(code_partition_size PATH "${code_partition}" REQUIRED) @@ -44,8 +48,24 @@ if(CONFIG_NCS_IS_VARIANT_IMAGE) # Additionally, convert primary slot dependencies to secondary slot dependencies. set(dotconfig_variant_content) foreach(line IN LISTS dotconfig_content) + dt_chosen(fw_to_relocate_property PROPERTY "fw-to-relocate") if("${line}" MATCHES "^CONFIG_FLASH_LOAD_OFFSET=.*$") - string(REGEX REPLACE "CONFIG_FLASH_LOAD_OFFSET=(.*)" "CONFIG_FLASH_LOAD_OFFSET=${code_partition_offset}" line ${line}) + # Change the CONFIG_FLASH_LOAD_OFFSET value only if the fw_to_relocate_property is empty - meaning that the firmware is not being relocated. + if("${fw_to_relocate_property}" STREQUAL "") + string(REGEX REPLACE "CONFIG_FLASH_LOAD_OFFSET=(.*)" "CONFIG_FLASH_LOAD_OFFSET=${code_partition_offset}" line ${line}) + endif() + endif() + + # Change the CONFIG_BUILD_OUTPUT_ADJUST_LMA value only if the fw_to_relocate_property is not empty - meaning that the firmware is being relocated. + if(NOT "${fw_to_relocate_property}" STREQUAL "") + if("${line}" MATCHES "^CONFIG_BUILD_OUTPUT_ADJUST_LMA=.*$") + + dt_partition_addr(fw_to_relocate_offset ABSOLUTE PATH "${fw_to_relocate_property}" REQUIRED) + dt_chosen(tcm_code_property PROPERTY "zephyr,code-partition") + dt_reg_addr(tcm_code_addr PATH "${tcm_code_property}" REQUIRED) + + string(REGEX REPLACE "CONFIG_BUILD_OUTPUT_ADJUST_LMA=(.*)" "CONFIG_BUILD_OUTPUT_ADJUST_LMA=${flash_base_addr}+${fw_to_relocate_offset}-${tcm_code_addr}" line ${line}) + endif() endif() if("${line}" MATCHES "^CONFIG_FLASH_LOAD_SIZE=.*$") @@ -62,7 +82,10 @@ if(CONFIG_NCS_IS_VARIANT_IMAGE) set(autoconf_variant_content) foreach(line IN LISTS autoconf_content) if("${line}" MATCHES "^#define CONFIG_FLASH_LOAD_OFFSET .*$") - string(REGEX REPLACE "#define CONFIG_FLASH_LOAD_OFFSET (.*)" "#define CONFIG_FLASH_LOAD_OFFSET ${code_partition_offset}" line ${line}) + # Change the CONFIG_FLASH_LOAD_OFFSET value only if the fw_to_relocate_property is empty - meaning that the firmware is not being relocated. + if("${fw_to_relocate_property}" STREQUAL "") + string(REGEX REPLACE "#define CONFIG_FLASH_LOAD_OFFSET (.*)" "#define CONFIG_FLASH_LOAD_OFFSET ${code_partition_offset}" line ${line}) + endif() endif() if("${line}" MATCHES "^#define CONFIG_FLASH_LOAD_SIZE .*$") diff --git a/cmake/sysbuild/sign_nrf54h20.cmake b/cmake/sysbuild/sign_nrf54h20.cmake index 6946ed0c785..79732d52c37 100644 --- a/cmake/sysbuild/sign_nrf54h20.cmake +++ b/cmake/sysbuild/sign_nrf54h20.cmake @@ -25,7 +25,11 @@ function(check_merged_slot_boundaries merged_partition images) set(end_offset) sysbuild_get(start_offset IMAGE ${image} VAR CONFIG_ROM_START_OFFSET KCONFIG) sysbuild_get(end_offset IMAGE ${image} VAR CONFIG_ROM_END_OFFSET KCONFIG) - dt_chosen(code_flash TARGET ${image} PROPERTY "zephyr,code-partition") + dt_chosen(code_flash TARGET ${image} PROPERTY "fw-to-relocate") + if("${code_flash}" STREQUAL "") + dt_chosen(code_flash TARGET ${image} PROPERTY "zephyr,code-partition") + endif() + dt_partition_addr(code_addr PATH "${code_flash}" TARGET ${image} REQUIRED ABSOLUTE) dt_reg_size(code_size TARGET ${image} PATH ${code_flash}) diff --git a/soc/nordic/nrf54h/Kconfig.defconfig b/soc/nordic/nrf54h/Kconfig.defconfig new file mode 100644 index 00000000000..f116209dec6 --- /dev/null +++ b/soc/nordic/nrf54h/Kconfig.defconfig @@ -0,0 +1,10 @@ +# Nordic Semiconductor nRF54H MCU line + +# Copyright (c) 2025 Nordic Semiconductor ASA +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + +if SOC_NRF54H20_CPURAD + +rsource "Kconfig.defconfig.nrf54h20_cpurad" + +endif # SOC_NRF54H20_CPURAD diff --git a/soc/nordic/nrf54h/Kconfig.defconfig.nrf54h20_cpurad b/soc/nordic/nrf54h/Kconfig.defconfig.nrf54h20_cpurad new file mode 100644 index 00000000000..fae7a59ed32 --- /dev/null +++ b/soc/nordic/nrf54h/Kconfig.defconfig.nrf54h20_cpurad @@ -0,0 +1,20 @@ +# Nordic Semiconductor nRF54H20 Radio MCU + +# Copyright (c) 2025 Nordic Semiconductor ASA +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + +if SOC_NRF54H20_CPURAD + +# Support for firmware relocation pattern (load from MRAM, run from TCM) +# If 'fw-to-relocate' chosen node exists, use it to calculate LMA adjustment +# Otherwise, fall back to standard code-partition calculation +DT_CHOSEN_FW_TO_RELOCATE = fw-to-relocate +DT_CHOSEN_Z_CODE = zephyr,code-partition +DT_CHOSEN_Z_SRAM = zephyr,sram + +config BUILD_OUTPUT_ADJUST_LMA + default "$(dt_chosen_partition_addr_hex,$(DT_CHOSEN_FW_TO_RELOCATE)) - \ + $(dt_chosen_reg_addr_hex,$(DT_CHOSEN_Z_CODE))" \ + if "$(dt_chosen_enabled,$(DT_CHOSEN_FW_TO_RELOCATE))" = "y" + +endif # SOC_NRF54H20_CPURAD From 5d0da1b2cb47a78ff3fde291d95c77fcfffa0c70 Mon Sep 17 00:00:00 2001 From: Jan Zyczkowski Date: Mon, 24 Nov 2025 08:30:00 +0100 Subject: [PATCH 3/3] samples: Add idle relocated TCM multicore test for nRF54H20 Introduce a new sample demonstrating a multicore idle test with firmware relocated to the radio core's TCM. The test showcases the radio loader pattern, where firmware is loaded from MRAM to TCM at runtime, ensuring efficient execution. JIRA: NCSDK-36461 Signed-off-by: Jan Zyczkowski --- CODEOWNERS | 3 + .../releases/release-notes-changelog.rst | 5 + doc/nrf/samples/other.rst | 1 + .../idle_relocated_tcm/CMakeLists.txt | 19 ++ .../idle_relocated_tcm/Kconfig.sysbuild | 24 ++ .../nrf54h20/idle_relocated_tcm/README.rst | 230 ++++++++++++++++++ .../boards/memory_map.overlay | 133 ++++++++++ .../boards/nrf54h20dk_nrf54h20_cpuapp.overlay | 13 + samples/nrf54h20/idle_relocated_tcm/prj.conf | 12 + .../idle_relocated_tcm/remote/CMakeLists.txt | 13 + .../idle_relocated_tcm/remote/prj.conf | 8 + .../idle_relocated_tcm/remote/src/main.c | 32 +++ .../nrf54h20/idle_relocated_tcm/src/main.c | 28 +++ .../nrf54h20/idle_relocated_tcm/sysbuild.conf | 16 ++ .../sysbuild/mcuboot.overlay | 13 + .../boards/nrf54h20dk_nrf54h20_cpurad.overlay | 25 ++ .../sysbuild/radio_loader/prj.conf | 94 +++++++ .../radio_loader_secondary_app.overlay | 24 ++ .../boards/nrf54h20dk_nrf54h20_cpurad.overlay | 22 ++ .../sysbuild/remote_rad/prj.conf | 8 + .../sysbuild/remote_rad_secondary_app.overlay | 22 ++ .../nrf54h20/idle_relocated_tcm/testcase.yaml | 20 ++ 22 files changed, 765 insertions(+) create mode 100644 samples/nrf54h20/idle_relocated_tcm/CMakeLists.txt create mode 100644 samples/nrf54h20/idle_relocated_tcm/Kconfig.sysbuild create mode 100644 samples/nrf54h20/idle_relocated_tcm/README.rst create mode 100644 samples/nrf54h20/idle_relocated_tcm/boards/memory_map.overlay create mode 100644 samples/nrf54h20/idle_relocated_tcm/boards/nrf54h20dk_nrf54h20_cpuapp.overlay create mode 100644 samples/nrf54h20/idle_relocated_tcm/prj.conf create mode 100644 samples/nrf54h20/idle_relocated_tcm/remote/CMakeLists.txt create mode 100644 samples/nrf54h20/idle_relocated_tcm/remote/prj.conf create mode 100644 samples/nrf54h20/idle_relocated_tcm/remote/src/main.c create mode 100644 samples/nrf54h20/idle_relocated_tcm/src/main.c create mode 100644 samples/nrf54h20/idle_relocated_tcm/sysbuild.conf create mode 100644 samples/nrf54h20/idle_relocated_tcm/sysbuild/mcuboot.overlay create mode 100644 samples/nrf54h20/idle_relocated_tcm/sysbuild/radio_loader/boards/nrf54h20dk_nrf54h20_cpurad.overlay create mode 100644 samples/nrf54h20/idle_relocated_tcm/sysbuild/radio_loader/prj.conf create mode 100644 samples/nrf54h20/idle_relocated_tcm/sysbuild/radio_loader_secondary_app.overlay create mode 100644 samples/nrf54h20/idle_relocated_tcm/sysbuild/remote_rad/boards/nrf54h20dk_nrf54h20_cpurad.overlay create mode 100644 samples/nrf54h20/idle_relocated_tcm/sysbuild/remote_rad/prj.conf create mode 100644 samples/nrf54h20/idle_relocated_tcm/sysbuild/remote_rad_secondary_app.overlay create mode 100644 samples/nrf54h20/idle_relocated_tcm/testcase.yaml diff --git a/CODEOWNERS b/CODEOWNERS index e73c1f9b3a2..74bb11d19d7 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -523,6 +523,8 @@ /samples/nrf5340/empty_app_core/ @nrfconnect/ncs-si-muffin /samples/nrf5340/extxip_smp_svr/ @nrfconnect/ncs-eris /samples/nrf54h20/empty_app_core/ @nrfconnect/ncs-aurora +/samples/nrf54h20/idle_relocated_tcm/ @nrfconnect/ncs-si-muffin +/samples/nrf54h20/radio_loader/ @nrfconnect/ncs-si-muffin /samples/ironside_se/ @nrfconnect/ncs-aurora /samples/nrf_compress/ @nordicjm /samples/nrf_profiler/ @nrfconnect/ncs-si-bluebagel @@ -646,6 +648,7 @@ /samples/net/**/*.rst @nrfconnect/ncs-cia-doc /samples/net/coap_client/*.rst @nrfconnect/ncs-iot-oulu-tampere-doc /samples/nfc/**/*.rst @nrfconnect/ncs-si-muffin-doc +/samples/nrf54h20/idle_relocated_tcm/*.rst @nrfconnect/ncs-si-muffin-doc /samples/nrf5340/empty_app_core/*.rst @nrfconnect/ncs-si-muffin-doc /samples/nrf5340/extxip_smp_svr/*.rst @nrfconnect/ncs-eris-doc /samples/nrf5340/netboot/*.rst @nrfconnect/ncs-eris-doc diff --git a/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst b/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst index 5698aabd25f..182ab4072f9 100644 --- a/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst +++ b/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst @@ -855,6 +855,11 @@ Other samples * Added a new testing step demonstrating how to calculate event propagation statistics. Also added the related test preset for the :file:`calc_stats.py` script (:file:`nrf/scripts/nrf_profiler/stats_nordic_presets/app_event_manager_profiler_tracer.json`). +* Added: + + * The :ref:`idle_relocated_tcm_sample` sample to demonstrate how to relocate the firmware to the TCM memory at boot time. + The sample also uses the ``radio_loader`` sample image (located in :file:`nrf/samples/nrf54h20/radio_loader`), which cannot be tested as a standalone sample, to relocate the firmware from the MRAM to the TCM memory at boot time. + Drivers ======= diff --git a/doc/nrf/samples/other.rst b/doc/nrf/samples/other.rst index c624d9c2cf4..88d72a0a453 100644 --- a/doc/nrf/samples/other.rst +++ b/doc/nrf/samples/other.rst @@ -25,3 +25,4 @@ This section lists single |NCS| samples for various uses that are not part of ot ../../../tests/benchmarks/multicore/*/README ../../../samples/zephyr/smp_svr_mini_boot/README ../../../samples/basic/*/README + ../../../samples/nrf54h20/*/README diff --git a/samples/nrf54h20/idle_relocated_tcm/CMakeLists.txt b/samples/nrf54h20/idle_relocated_tcm/CMakeLists.txt new file mode 100644 index 00000000000..a6d82c7d387 --- /dev/null +++ b/samples/nrf54h20/idle_relocated_tcm/CMakeLists.txt @@ -0,0 +1,19 @@ +# +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +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(idle) + +target_sources(app PRIVATE src/main.c) diff --git a/samples/nrf54h20/idle_relocated_tcm/Kconfig.sysbuild b/samples/nrf54h20/idle_relocated_tcm/Kconfig.sysbuild new file mode 100644 index 00000000000..c4db24c3b94 --- /dev/null +++ b/samples/nrf54h20/idle_relocated_tcm/Kconfig.sysbuild @@ -0,0 +1,24 @@ +# +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +source "share/sysbuild/Kconfig" + +choice NETCORE + +default NETCORE_CUSTOM_RADIO + +config NETCORE_CUSTOM_RADIO + bool "Custom radio" + help + Use custom radio. + +endchoice + +config NETCORE_IMAGE_NAME + default "remote_rad" if NETCORE_CUSTOM_RADIO + +config NETCORE_IMAGE_PATH + default "$(ZEPHYR_NRF_MODULE_DIR)/samples/nrf54h20/idle_relocated_tcm/remote" if NETCORE_CUSTOM_RADIO diff --git a/samples/nrf54h20/idle_relocated_tcm/README.rst b/samples/nrf54h20/idle_relocated_tcm/README.rst new file mode 100644 index 00000000000..81defafa558 --- /dev/null +++ b/samples/nrf54h20/idle_relocated_tcm/README.rst @@ -0,0 +1,230 @@ +.. _idle_relocated_tcm_sample: + +Multicore idle test with firmware relocated to radio core TCM +############################################################# + +.. contents:: + :local: + :depth: 2 + +The test benchmarks the idle behavior of an application that runs on multiple cores. +It demonstrates a radio loader pattern where the radio core firmware is loaded from MRAM into TCM (Tightly Coupled Memory) at runtime. + +Requirements +************ + +The test supports the following development kit: + +.. table-from-rows:: /includes/sample_board_rows.txt + :header: heading + :rows: nrf54h20dk_nrf54h20_cpuapp + +Overview +******** + +This test demonstrates how to build a multicore idle application with :ref:`configuration_system_overview_sysbuild` using a two-stage boot process for the radio core: + +* Radio Loader - A small bootloader that runs on the radio core, copies firmware from MRAM to TCM, and jumps to it. +* Remote Firmware - The actual application that runs from TCM after being loaded. + +The test automatically relocates the remote firmware binary to the correct MRAM address during build time, ensuring it can be loaded by the radio loader. + +Architecture +============ + +The system uses the following memory layout: + +* **MRAM (Non-volatile):** + + * ``cpurad_loader_partition`` @ 0x92000 - Contains the radio loader (8 KB) + * ``cpurad_loaded_fw`` @ 0x94000 - Contains the remote firmware binary (128 KB) + +* **TCM (Volatile, fast execution):** + + * ``cpurad_ram0`` @ 0x23000000 - Code execution region (128 KB) + * ``cpurad_data_ram`` @ 0x23020000 - Data region (64 KB) + +Additional files +================ + +The test comes with the following additional files: + +* :file:`sysbuild.conf` - Enables the radio loader by setting ``CONFIG_NRF_RADIO_LOADER=y``. +* :file:`boards/memory_map.overlay` - Shared memory map configuration for both loader and remote firmware. +* :file:`sysbuild/radio_loader/` - Radio loader configuration overrides (:file:`prj.conf`, overlay). +* :file:`sysbuild/remote_rad/` - Radio core firmware configuration overrides (:file:`prj.conf`, overlay). + +Enabling the Radio Loader +************************* + +The radio loader is automatically added to the build when you enable it in sysbuild configuration. + +In :file:`sysbuild.conf`: + +.. code-block:: kconfig + + SB_CONFIG_NRF_RADIO_LOADER=y + +This single configuration option: + +#. Automatically adds the ``radio_loader`` application located in the :file:`nrf/samples/nrf54h20/radio_loader` folder. +#. Builds it for the CPURAD core. +#. No manual ``ExternalZephyrProject_Add()`` needed in sysbuild. + +Memory map configuration +======================== + +The memory map is defined in :file:`boards/memory_map.overlay` and is shared between the radio loader and remote firmware to ensure consistency. + +The overlay defines: + +#. TCM regions: + + .. code-block:: devicetree + + cpurad_ram0: sram@23000000 { + compatible = "mmio-sram"; + reg = <0x23000000 0x20000>; /* 128 KB for code */ + }; + cpurad_data_ram: sram@23020000 { + compatible = "mmio-sram"; + reg = <0x23020000 0x10000>; /* 64 KB for data */ + }; + +#. MRAM partitions: + + .. code-block:: devicetree + + &mram1x { + /delete-node/ partitions; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + cpurad_loader_partition: partition@92000 { + label = "cpurad_loader_partition"; + reg = <0x92000 DT_SIZE_K(8)>; /* 8 KB allocated (~4 KB actual) */ + }; + + cpurad_loaded_fw: partition@94000 { + label = "cpurad_loaded_fw"; + reg = <0x94000 DT_SIZE_K(128)>; /* 128 KB fixed */ + }; + }; + }; + +Automatic firmware relocation +***************************** + +The remote firmware must be relocated to match the MRAM partition address where it will be stored. +This is automatically done by Zephyr's ``CONFIG_BUILD_OUTPUT_ADJUST_LMA`` feature when the devicetree chosen nodes are configured correctly. + +How it works +============ + +Firmware relocation is handled automatically by Zephyr's build system using the ``CONFIG_BUILD_OUTPUT_ADJUST_LMA`` configuration option, which is configured in ``zephyr/soc/nordic/nrf54h/Kconfig.defconfig.nrf54h20_cpurad`` for all nRF54H20 CPURAD projects. + +The configuration automatically detects the ``fw-to-relocate`` chosen node in your devicetree. +When present, it calculates the LMA adjustment to relocate firmware from MRAM to TCM. +Without this chosen node, firmware runs directly from the ``zephyr,code-partition`` location (standard XIP behavior). + +Simply configure the devicetree chosen nodes correctly in your firmware's overlay: + +.. code-block:: devicetree + + /{ + chosen { + /* VMA: where code runs (TCM) */ + zephyr,code-partition = &cpurad_ram0; + zephyr,sram = &cpurad_data_ram; + + /* LMA: where to load from (MRAM partition) - enables relocation */ + fw-to-relocate = &cpurad_loaded_fw; + }; + }; + +Zephyr automatically calculates the Load Memory Address (LMA) adjustment based on your chosen nodes: + +**With fw-to-relocate chosen node** (for radio loader pattern): + +.. code-block:: text + + LMA_adjustment = fw-to-relocate address - zephyr,code-partition address + = cpurad_loaded_fw - cpurad_ram0 + = 0x94000 - 0x23000000 + +**Without fw-to-relocate** (standard behavior): + +.. code-block:: text + + LMA_adjustment = zephyr,code-partition address - zephyr,sram address + +The build system then adjusts the hex file so that the firmware is loaded from MRAM (``0x94000``), but runs from TCM (``0x23000000``). + +Building and running +******************** + +.. |test path| replace:: :file:`samples/nrf54h20/idle_relocated_tcm` + +.. include:: /includes/build_and_run_test.txt + +Testing +======= + +After programming the test to your development kit, complete the following steps to test it: + +1. |connect_terminal| +#. Reset the kit. +#. Observe the console output for both cores: + + * For the application core, the output should be as follows: + + .. code-block:: console + + *** Booting nRF Connect SDK zephyr-v3.5.0-3517-g9458a1aaf744 *** + build time: Nov 22 2025 17:00:59 + Multicore idle test on nrf54h20dk@0.9.0/nrf54h20/cpuapp + Multicore idle test iteration 0 + Multicore idle test iteration 1 + ... + + * For the radio core, the output should be as follows: + + .. code-block:: console + + *** Booting nRF Connect SDK zephyr-v3.5.0-3517-g9458a1aaf744 *** + build time: Nov 22 2025 17:00:29 + Multicore idle test on nrf54h20dk@0.9.0/nrf54h20/cpurad + Current PC (program counter) address: 0x23000ae0 + Multicore idle test iteration 0 + Multicore idle test iteration 1 + ... + + The radio loader first loads the firmware from MRAM (``0x0e094000``) to TCM (``0x23000000``) and then jumps to the loaded firmware. + This process is transparent and happens during the early boot stage. + +#. Verify the DFU process: + + #. Build the firmware for the secondary app slot, increase the version number in the :file:`prj.conf` file (uncomment the line): + + .. code-block:: kconfig + + CONFIG_MCUBOOT_IMGTOOL_SIGN_VERSION="0.0.1+0" + + #. Build the firmware: + + .. code-block:: console + + west build -p -b nrf54h20dk/nrf54h20/cpuapp + + #. Program the firmware to the secondary application slot: + + .. code-block:: console + + nrfutil device program --firmware build/zephyr_secondary_app.merged.hex --options chip_erase_mode=ERASE_NONE + + Reset the development kit. + The firmware must boot from the secondary application slot. + Observe the change in build time in the console output. diff --git a/samples/nrf54h20/idle_relocated_tcm/boards/memory_map.overlay b/samples/nrf54h20/idle_relocated_tcm/boards/memory_map.overlay new file mode 100644 index 00000000000..d2449066251 --- /dev/null +++ b/samples/nrf54h20/idle_relocated_tcm/boards/memory_map.overlay @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +/{ + soc{ + cpurad_data_ram: sram@23020000 { + compatible = "mmio-sram"; + reg = < 0x23020000 0x10000 >; + #address-cells = < 0x1 >; + #size-cells = < 0x1 >; + ranges = < 0x0 0x23020000 0x10000 >; + }; + }; +}; + +&mram1x { + /delete-node/ partitions; + + /* Redefine the "partitions" DTS node. */ + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + cpuapp_boot_partition: boot_partition: partition@30000 { + reg = < 0x30000 0x10000 >; + label = "mcuboot"; + }; + + slot0_partition: partition@40000 { + reg = <0x40000 DT_SIZE_K(656)>; + }; + + cpuapp_slot0_partition: partition@40800 { + reg = <0x40800 DT_SIZE_K(326)>; + label = "image-0"; + }; + + cpurad_slot0_partition: cpurad_loader_partition: partition@92000 { + label = "cpurad_loader_partition"; + reg = <0x92000 DT_SIZE_K(8)>; /* 8 KB allocated (~4 KB actual) */ + }; + + /* Remote firmware location in MRAM (fixed size for memcpy) */ + cpurad_loaded_fw: partition@94000 { + label = "cpurad_loaded_fw"; + reg = <0x94000 DT_SIZE_K(128)>; /* 128 KB fixed */ + }; + + cpurad_slot0_partition_container: partition@b2000 { + reg = <0xb2000 DT_SIZE_K(192)>; /* 238 - 128 - 8 = 192 KB allocated */ + }; + + cpuppr_code_partition: partition@e4000 { + reg = <0xe4000 DT_SIZE_K(64)>; + }; + + cpuflpr_code_partition: partition@f4000 { + reg = <0xf4000 DT_SIZE_K(48)>; + }; + + slot1_partition: partition@100000 { + reg = <0x100000 DT_SIZE_K(656)>; + }; + + cpuapp_slot1_partition: partition@100800 { + reg = <0x100800 DT_SIZE_K(326)>; + }; + + cpurad_loader_partition_slot1: cpurad_slot1_partition: partition@152000 { + label = "cpurad_loader_partition_slot1"; + reg = <0x152000 DT_SIZE_K(8)>; /* 8 KB allocated (~4 KB actual) */ + }; + + /* Remote firmware location in MRAM (fixed size for memcpy) */ + cpurad_loaded_fw_slot1: partition@154000 { + label = "cpurad_loaded_fw_slot1"; + reg = <0x154000 DT_SIZE_K(128)>; /* 128 KB fixed */ + }; + + cpurad_slot1_partition_container: partition@174000 { + reg = <0x174000 DT_SIZE_K(192)>; /* 238 - 128 - 8 = 192 KB allocated */ + }; + + storage_partition: partition@1a4000 { + reg = <0x1a4000 DT_SIZE_K(40)>; + }; + + periphconf_partition: partition@1ae000 { + reg = <0x1ae000 DT_SIZE_K(8)>; + }; + + secondary_partition: partition@1b0000 { + reg = <0x1b0000 DT_SIZE_K(64)>; + }; + + secondary_periphconf_partition: partition@1c0000 { + reg = <0x1c0000 DT_SIZE_K(8)>; + }; + + /* NB: A gap has been left here for future partitions */ + + /* 0x1fd000 was chosen for secure_storage_partition such that + * there is no more space after secure_storage_partition. + */ + secure_storage_partition: partition@1fd000 { + compatible = "fixed-subpartitions"; + reg = <0x1fd000 DT_SIZE_K(12)>; + ranges = <0x0 0x1fd000 0x3000>; + #address-cells = <1>; + #size-cells = <1>; + + cpuapp_crypto_partition: partition@0 { + reg = <0x0 DT_SIZE_K(4)>; + }; + + cpurad_crypto_partition: partition@1000 { + reg = <0x1000 DT_SIZE_K(4)>; + }; + + cpuapp_its_partition: partition@2000 { + reg = <0x2000 DT_SIZE_K(2)>; + }; + + cpurad_its_partition: partition@2800 { + reg = <0x2800 DT_SIZE_K(2)>; + }; + }; + }; +}; diff --git a/samples/nrf54h20/idle_relocated_tcm/boards/nrf54h20dk_nrf54h20_cpuapp.overlay b/samples/nrf54h20/idle_relocated_tcm/boards/nrf54h20dk_nrf54h20_cpuapp.overlay new file mode 100644 index 00000000000..5685cb6b65f --- /dev/null +++ b/samples/nrf54h20/idle_relocated_tcm/boards/nrf54h20dk_nrf54h20_cpuapp.overlay @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include "memory_map.overlay" + +&cpuapp_data { + reg = <0x2f000000 0x40000>; +}; + +secondary_app_partition: &cpuapp_slot1_partition {}; diff --git a/samples/nrf54h20/idle_relocated_tcm/prj.conf b/samples/nrf54h20/idle_relocated_tcm/prj.conf new file mode 100644 index 00000000000..d8e41511b6f --- /dev/null +++ b/samples/nrf54h20/idle_relocated_tcm/prj.conf @@ -0,0 +1,12 @@ +# +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +CONFIG_LOG=y + +CONFIG_SOC_NRF54H20_CPURAD_ENABLE=y + +# Set the version number for the firmware to verify dfu process +#CONFIG_MCUBOOT_IMGTOOL_SIGN_VERSION="0.0.1+0" diff --git a/samples/nrf54h20/idle_relocated_tcm/remote/CMakeLists.txt b/samples/nrf54h20/idle_relocated_tcm/remote/CMakeLists.txt new file mode 100644 index 00000000000..e4fbb0131ca --- /dev/null +++ b/samples/nrf54h20/idle_relocated_tcm/remote/CMakeLists.txt @@ -0,0 +1,13 @@ +# +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) + +project(idle) + +target_sources(app PRIVATE src/main.c) diff --git a/samples/nrf54h20/idle_relocated_tcm/remote/prj.conf b/samples/nrf54h20/idle_relocated_tcm/remote/prj.conf new file mode 100644 index 00000000000..4191b264366 --- /dev/null +++ b/samples/nrf54h20/idle_relocated_tcm/remote/prj.conf @@ -0,0 +1,8 @@ +# +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +CONFIG_LOG=y +CONFIG_FLASH_BASE_ADDRESS=0x0 diff --git a/samples/nrf54h20/idle_relocated_tcm/remote/src/main.c b/samples/nrf54h20/idle_relocated_tcm/remote/src/main.c new file mode 100644 index 00000000000..d8b4d982081 --- /dev/null +++ b/samples/nrf54h20/idle_relocated_tcm/remote/src/main.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include +#include + +LOG_MODULE_REGISTER(idle); + +int main(void) +{ + unsigned int cnt = 0; + uintptr_t pc; + + __asm__ volatile("adr %0, ." : "=r"(pc)); + LOG_INF("Multicore idle test on %s", CONFIG_BOARD_TARGET); + LOG_INF("Current PC (program counter) address: 0x%lx\n", (unsigned long)pc); + + /* using __TIME__ ensure that a new binary will be built on every + * compile which is convenient when testing firmware upgrade. + */ + LOG_INF("build time: " __DATE__ " " __TIME__); + + while (1) { + LOG_INF("Multicore idle test iteration %u", cnt++); + k_msleep(2000); + } + + return 0; +} diff --git a/samples/nrf54h20/idle_relocated_tcm/src/main.c b/samples/nrf54h20/idle_relocated_tcm/src/main.c new file mode 100644 index 00000000000..8bfa569648e --- /dev/null +++ b/samples/nrf54h20/idle_relocated_tcm/src/main.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include +#include + +LOG_MODULE_REGISTER(idle); + +int main(void) +{ + unsigned int cnt = 0; + + /* using __TIME__ ensure that a new binary will be built on every + * compile which is convenient when testing firmware upgrade. + */ + LOG_INF("build time: " __DATE__ " " __TIME__); + + LOG_INF("Multicore idle test on %s", CONFIG_BOARD_TARGET); + while (1) { + LOG_INF("Multicore idle test iteration %u", cnt++); + k_msleep(2000); + } + + return 0; +} diff --git a/samples/nrf54h20/idle_relocated_tcm/sysbuild.conf b/samples/nrf54h20/idle_relocated_tcm/sysbuild.conf new file mode 100644 index 00000000000..5f95387545a --- /dev/null +++ b/samples/nrf54h20/idle_relocated_tcm/sysbuild.conf @@ -0,0 +1,16 @@ +# +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +# This automatically adds the radio_loader application to the build +SB_CONFIG_NRF_RADIO_LOADER=y +SB_CONFIG_NETCORE_CUSTOM_RADIO=y + +SB_CONFIG_BOOTLOADER_MCUBOOT=y +SB_CONFIG_MCUBOOT_MODE_DIRECT_XIP=y +SB_CONFIG_BOOT_SIGNATURE_TYPE_ED25519=y +SB_CONFIG_BOOT_SIGNATURE_TYPE_PURE=y +# Reserve space for MCUboot trailer in CPURAD slots +SB_CONFIG_MCUBOOT_IMAGES_ROM_END_OFFSET_AUTO="remote_rad;remote_rad_secondary_app" diff --git a/samples/nrf54h20/idle_relocated_tcm/sysbuild/mcuboot.overlay b/samples/nrf54h20/idle_relocated_tcm/sysbuild/mcuboot.overlay new file mode 100644 index 00000000000..284946af590 --- /dev/null +++ b/samples/nrf54h20/idle_relocated_tcm/sysbuild/mcuboot.overlay @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include "../boards/memory_map.overlay" + +/ { + chosen { + zephyr,code-partition = &boot_partition; + }; +}; diff --git a/samples/nrf54h20/idle_relocated_tcm/sysbuild/radio_loader/boards/nrf54h20dk_nrf54h20_cpurad.overlay b/samples/nrf54h20/idle_relocated_tcm/sysbuild/radio_loader/boards/nrf54h20dk_nrf54h20_cpurad.overlay new file mode 100644 index 00000000000..e81c8ee3c8b --- /dev/null +++ b/samples/nrf54h20/idle_relocated_tcm/sysbuild/radio_loader/boards/nrf54h20dk_nrf54h20_cpurad.overlay @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include "../../../boards/memory_map.overlay" + +/{ + chosen { + zephyr,code-partition = &cpurad_loader_partition; + zephyr,sram = &cpurad_data_ram; + zephyr,loaded-fw-src = &cpurad_loaded_fw; + zephyr,loaded-fw-dst = &cpurad_ram0; + }; +}; + +&cpurad_ram0 { + compatible = "mmio-sram"; + /delete-property/ reg; + reg = < 0x23000000 0x20000 >; + ranges = < 0x0 0x23000000 0x20000 >; +}; + +secondary_app_partition: &cpurad_loader_partition_slot1 {}; diff --git a/samples/nrf54h20/idle_relocated_tcm/sysbuild/radio_loader/prj.conf b/samples/nrf54h20/idle_relocated_tcm/sysbuild/radio_loader/prj.conf new file mode 100644 index 00000000000..6f57c25fea8 --- /dev/null +++ b/samples/nrf54h20/idle_relocated_tcm/sysbuild/radio_loader/prj.conf @@ -0,0 +1,94 @@ +# +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +# ============================================================================== +# Optimized minimal configuration for radio_loader +# ============================================================================== +# This loader only runs SYS_INIT(load_and_jump_to_firmware, EARLY, 0) which: +# 1. Copies firmware from MRAM to TCM using memcpy() +# 2. Jumps to the loaded firmware's reset handler +# No Zephyr services, threading, or drivers are needed. +# +# Memory footprint target: < 4 KB + +# ============================================================================== +# Power Management +# ============================================================================== +CONFIG_PM=n +CONFIG_PM_DEVICE=n + +# ============================================================================== +# Kernel - Threading Disabled +# ============================================================================== +# We never reach main() or start the scheduler, so disable all threading +CONFIG_MULTITHREADING=n +CONFIG_MAIN_STACK_SIZE=512 +CONFIG_THREAD_STACK_INFO=n + +# Disable kernel features that require threading/scheduler +CONFIG_EVENTS=n +CONFIG_POLL=n +CONFIG_TIMESLICING=n + +# ============================================================================== +# Console, Debug, and Logging +# ============================================================================== +# No console output needed - loader jumps immediately to firmware +CONFIG_CONSOLE=n +CONFIG_UART_CONSOLE=n +CONFIG_SERIAL=n +CONFIG_PRINTK=n +CONFIG_EARLY_CONSOLE=n +CONFIG_LOG=n + +# Banners and debug features +CONFIG_NCS_BOOT_BANNER=n +CONFIG_BOOT_BANNER=n +CONFIG_ERRNO=n + +# ============================================================================== +# Device Drivers +# ============================================================================== +# No peripheral drivers needed - we only use memcpy and jump +CONFIG_GPIO=n +CONFIG_PINCTRL=n +CONFIG_I2C=n +CONFIG_SPI=n +CONFIG_WATCHDOG=n + +# ============================================================================== +# Interrupt Management +# ============================================================================== +CONFIG_DYNAMIC_INTERRUPTS=n +CONFIG_IRQ_OFFLOAD=n +CONFIG_GEN_IRQ_VECTOR_TABLE=n +CONFIG_GEN_ISR_TABLES=n +CONFIG_GEN_SW_ISR_TABLE=n + +# ============================================================================== +# Hardware Protection +# ============================================================================== +CONFIG_HW_STACK_PROTECTION=n +CONFIG_ARM_MPU=n + +# ============================================================================== +# Security and Crypto +# ============================================================================== +# No crypto needed for simple memory copy operation +CONFIG_NRF_SECURITY=n +CONFIG_MBEDTLS_PSA_CRYPTO_C=n +CONFIG_PSA_CRYPTO_DRIVER_OBERON=n +CONFIG_PSA_CRYPTO=n +CONFIG_PSA_SSF_CRYPTO_CLIENT=n + +# ============================================================================== +# Memory Optimization +# ============================================================================== +CONFIG_HEAP_MEM_POOL_SIZE=0 +CONFIG_SYS_HEAP_RUNTIME_STATS=n + +# Use nano printf for minimal footprint (only used if PRINTK somehow gets enabled) +CONFIG_CBPRINTF_NANO=y diff --git a/samples/nrf54h20/idle_relocated_tcm/sysbuild/radio_loader_secondary_app.overlay b/samples/nrf54h20/idle_relocated_tcm/sysbuild/radio_loader_secondary_app.overlay new file mode 100644 index 00000000000..6b7a55f3389 --- /dev/null +++ b/samples/nrf54h20/idle_relocated_tcm/sysbuild/radio_loader_secondary_app.overlay @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include "../boards/memory_map.overlay" + +/{ + chosen { + zephyr,loaded-fw-src = &cpurad_loaded_fw_slot1; + zephyr,loaded-fw-dst = &cpurad_ram0; + zephyr,sram = &cpurad_data_ram; + }; +}; + +&cpurad_ram0 { + compatible = "mmio-sram"; + /delete-property/ reg; + reg = < 0x23000000 0x20000 >; + ranges = < 0x0 0x23000000 0x20000 >; +}; + +secondary_app_partition: &cpurad_loader_partition_slot1 {}; diff --git a/samples/nrf54h20/idle_relocated_tcm/sysbuild/remote_rad/boards/nrf54h20dk_nrf54h20_cpurad.overlay b/samples/nrf54h20/idle_relocated_tcm/sysbuild/remote_rad/boards/nrf54h20dk_nrf54h20_cpurad.overlay new file mode 100644 index 00000000000..08960044832 --- /dev/null +++ b/samples/nrf54h20/idle_relocated_tcm/sysbuild/remote_rad/boards/nrf54h20dk_nrf54h20_cpurad.overlay @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include "../../../boards/memory_map.overlay" + +/{ + chosen { + zephyr,code-partition = &cpurad_ram0; + zephyr,sram = &cpurad_data_ram; + fw-to-relocate = &cpurad_loaded_fw; + }; +}; + +&cpurad_ram0 { + compatible = "mmio-sram"; + /delete-property/ reg; + reg = < 0x23000000 0x20000 >; + ranges = < 0x0 0x23000000 0x20000 >; +}; diff --git a/samples/nrf54h20/idle_relocated_tcm/sysbuild/remote_rad/prj.conf b/samples/nrf54h20/idle_relocated_tcm/sysbuild/remote_rad/prj.conf new file mode 100644 index 00000000000..4191b264366 --- /dev/null +++ b/samples/nrf54h20/idle_relocated_tcm/sysbuild/remote_rad/prj.conf @@ -0,0 +1,8 @@ +# +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +CONFIG_LOG=y +CONFIG_FLASH_BASE_ADDRESS=0x0 diff --git a/samples/nrf54h20/idle_relocated_tcm/sysbuild/remote_rad_secondary_app.overlay b/samples/nrf54h20/idle_relocated_tcm/sysbuild/remote_rad_secondary_app.overlay new file mode 100644 index 00000000000..2a57ad60ed0 --- /dev/null +++ b/samples/nrf54h20/idle_relocated_tcm/sysbuild/remote_rad_secondary_app.overlay @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include "../boards/memory_map.overlay" + +/{ + chosen { + fw-to-relocate = &cpurad_loaded_fw_slot1; + }; +}; + +&cpurad_ram0 { + compatible = "mmio-sram"; + /delete-property/ reg; + reg = < 0x23000000 0x20000 >; + ranges = < 0x0 0x23000000 0x20000 >; +}; + +secondary_app_partition: &cpurad_ram0 {}; diff --git a/samples/nrf54h20/idle_relocated_tcm/testcase.yaml b/samples/nrf54h20/idle_relocated_tcm/testcase.yaml new file mode 100644 index 00000000000..702e4dd2e27 --- /dev/null +++ b/samples/nrf54h20/idle_relocated_tcm/testcase.yaml @@ -0,0 +1,20 @@ +common: + sysbuild: true + tags: + - ci_build + - ci_samples_nrf54h20 + harness_config: + type: multi_line + ordered: true + regex: + - "Multicore idle test on" + - "Multicore idle test iteration 0" + - "Multicore idle test iteration 1" + +tests: + benchmarks.multicore.idle_relocated_tcm.nrf54h20dk_cpuapp_cpurad: + harness: console + platform_allow: + - nrf54h20dk/nrf54h20/cpuapp + integration_platforms: + - nrf54h20dk/nrf54h20/cpuapp