-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Add idle relocated TCM multicore test for nRF54H20 #25737
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 "") | ||
tomchy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| string(REGEX REPLACE "CONFIG_FLASH_LOAD_OFFSET=(.*)" "CONFIG_FLASH_LOAD_OFFSET=${code_partition_offset}" line ${line}) | ||
| endif() | ||
zycz marked this conversation as resolved.
Show resolved
Hide resolved
zycz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| endif() | ||
|
Comment on lines
+51
to
+57
|
||
|
|
||
| # 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 "") | ||
mkapala-nordic marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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}) | ||
zycz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| endif() | ||
| endif() | ||
|
|
||
| if("${line}" MATCHES "^#define CONFIG_FLASH_LOAD_SIZE .*$") | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,230 @@ | ||
| .. _idle_relocated_tcm_sample: | ||
|
|
||
divipillai marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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: | ||
divipillai marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| .. 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 [email protected]/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 [email protected]/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. | ||
zycz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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 | ||
zycz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| #. 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. | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is all of this done under variant image ?
All the adjustments in this
kconfig.cmakemodule was made so that one highlevel build can produce one image, and from there also create a variant image which uses the exact same configuration.e4be060
I don't see how
fw-to-relocatedfits into this ?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To my understanding (though I may be wrong), in the TCM-loader-enabled mode we have:
Since this logic is used when there are variant apps built, it means that we are in the Direct XIP update mode(s).
So now - as the zephyr,code-partition must be different for slot 0 and slot 1 in a regular Direct XIP, the fw-to-relocate must be different (to copy from either slot 0 or slot 1) once TCM-loader-enabled mode is used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but you're using devicetree to adjust Kconfig behind the scenes.
This file is kconfig.cmake, and kconfig.cmake is for kconfig handling and not intended for a devicetree handling / adjustments.
And before you start replying that devicetree is already done in this file, then I would like to direct to this PR #23562 .
Starting to manipulate n-kconfig settings depending on featureA, featureB, featureC, ... etc is not scalable, and definitely not making it easier for NCS customers to adjust the solution to their needs without patching NCS itself.
Please take a look at the original implemetation for variant image e4be060 and notice how clean that is as it doesn't need special treatment for xyz settings.
just because something was introduced as a quick fix for 3.1 doesn't mean it's a proper way to do things.
@ahasztag why wasn't #23562 followed up after 3.1 was done ?