-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Add split-slot A/B sample #25036
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
Draft
tomchy
wants to merge
6
commits into
nrfconnect:main
Choose a base branch
from
tomchy:feature/mcuboot/NCSDK-NONE_Transaction_manifest
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+2,301
−6
Draft
Add split-slot A/B sample #25036
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
dcd7b51
mcumgr: Add a copy of image management lib
tomchy 58fdf74
mgmt: mcumgr: grp: img_mgmt: Add handling for QSPI XIP
tomchy dc5a11b
mcumgr: img_mgmt: Fix check for chosen code partition
tomchy eca54e8
sysbuild: Add a MCUboot manifest generator
tomchy 08cdb2d
mgmt: Handle manifest-based states
tomchy 886a5ae
samples: Add split slot A/B sample
tomchy File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| # | ||
| # Copyright (c) 2025 Nordic Semiconductor | ||
| # | ||
| # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause | ||
|
|
||
| include(${ZEPHYR_NRF_MODULE_DIR}/cmake/sysbuild/bootloader_dts_utils.cmake) | ||
|
|
||
| yaml_create(NAME mcuboot_manifest) | ||
| yaml_set(NAME mcuboot_manifest KEY format VALUE "1") | ||
| yaml_set(NAME mcuboot_manifest KEY images LIST) | ||
| set(manifest_path "manifest.yaml") | ||
| set(manifest_img_slot_0 "${DEFAULT_IMAGE}") | ||
|
|
||
| yaml_create(NAME mcuboot_secondary_manifest) | ||
| yaml_set(NAME mcuboot_secondary_manifest KEY format VALUE "1") | ||
| yaml_set(NAME mcuboot_secondary_manifest KEY images LIST) | ||
| set(manifest_secondary_path "manifest_secondary.yaml") | ||
| set(manifest_img_slot_1 "mcuboot_secondary_app") | ||
|
|
||
| # Since the default (merged) image is excluded from the manifest, because it contains the manifest | ||
| # itself, there is no need to construct the manifest when building a merged image. | ||
| if(NOT SB_CONFIG_MCUBOOT_SIGN_MERGED_BINARY) | ||
| sysbuild_get(manifest_img IMAGE mcuboot VAR CONFIG_MCUBOOT_MANIFEST_IMAGE_NUMBER KCONFIG) | ||
| math(EXPR manifest_slot_0 "${manifest_img} * 2") | ||
| math(EXPR manifest_slot_1 "${manifest_img} * 2 + 1") | ||
| dt_partition_addr(slot0_addr LABEL "slot${manifest_slot_0}_partition" TARGET mcuboot ABSOLUTE REQUIRED) | ||
| dt_partition_addr(slot1_addr LABEL "slot${manifest_slot_1}_partition" TARGET mcuboot ABSOLUTE REQUIRED) | ||
|
|
||
| UpdateableImage_Get(images GROUP "DEFAULT") | ||
| foreach(image ${images}) | ||
| sysbuild_get(BINARY_DIR IMAGE ${image} VAR APPLICATION_BINARY_DIR CACHE) | ||
| sysbuild_get(BINARY_BIN_FILE IMAGE ${image} VAR CONFIG_KERNEL_BIN_NAME KCONFIG) | ||
| dt_chosen(code_flash TARGET ${image} PROPERTY "zephyr,code-partition") | ||
| dt_partition_addr(code_addr PATH "${code_flash}" TARGET ${image} ABSOLUTE REQUIRED) | ||
|
|
||
| if("${code_addr}" STREQUAL "${slot0_addr}") | ||
| cmake_path(APPEND BINARY_DIR "zephyr" "manifest.yaml" OUTPUT_VARIABLE manifest_path) | ||
| set(manifest_img_slot_0 "${image}") | ||
| continue() | ||
| endif() | ||
|
|
||
| if(NOT "${SB_CONFIG_SIGNATURE_TYPE}" STREQUAL "NONE") | ||
| cmake_path(APPEND BINARY_DIR "zephyr" "${BINARY_BIN_FILE}.signed.bin" OUTPUT_VARIABLE image_path) | ||
| else() | ||
| cmake_path(APPEND BINARY_DIR "zephyr" "${BINARY_BIN_FILE}.bin" OUTPUT_VARIABLE image_path) | ||
| endif() | ||
|
|
||
| yaml_set(NAME mcuboot_manifest KEY images APPEND LIST MAP "path: ${image_path}, name: ${image}") | ||
| endforeach() | ||
|
|
||
| foreach(image ${images}) | ||
| if("${image}" STREQUAL "${manifest_img_slot_0}") | ||
| continue() | ||
| endif() | ||
| add_dependencies("${manifest_img_slot_0}" "${image}") | ||
| endforeach() | ||
|
|
||
| UpdateableImage_Get(variants GROUP "VARIANT") | ||
| foreach(image ${variants}) | ||
| sysbuild_get(BINARY_DIR IMAGE ${image} VAR APPLICATION_BINARY_DIR CACHE) | ||
| sysbuild_get(BINARY_BIN_FILE IMAGE ${image} VAR CONFIG_KERNEL_BIN_NAME KCONFIG) | ||
| dt_chosen(code_flash TARGET ${image} PROPERTY "zephyr,code-partition") | ||
| dt_partition_addr(code_addr PATH "${code_flash}" TARGET ${image} ABSOLUTE REQUIRED) | ||
|
|
||
| if("${code_addr}" STREQUAL "${slot1_addr}") | ||
| cmake_path(APPEND BINARY_DIR "zephyr" "manifest.yaml" OUTPUT_VARIABLE manifest_secondary_path) | ||
| set(manifest_img_slot_1 "${image}") | ||
| continue() | ||
| endif() | ||
|
|
||
| if(NOT "${SB_CONFIG_SIGNATURE_TYPE}" STREQUAL "NONE") | ||
| cmake_path(APPEND BINARY_DIR "zephyr" "${BINARY_BIN_FILE}.signed.bin" OUTPUT_VARIABLE image_path) | ||
| else() | ||
| cmake_path(APPEND BINARY_DIR "zephyr" "${BINARY_BIN_FILE}.bin" OUTPUT_VARIABLE image_path) | ||
| endif() | ||
|
|
||
| yaml_set(NAME mcuboot_secondary_manifest KEY images APPEND LIST MAP "path: ${image_path}, name: ${image}") | ||
| endforeach() | ||
|
|
||
| foreach(image ${variants}) | ||
| if("${image}" STREQUAL "${manifest_img_slot_1}") | ||
| continue() | ||
| endif() | ||
| add_dependencies("${manifest_img_slot_1}" "${image}") | ||
| endforeach() | ||
| endif() | ||
|
|
||
| yaml_save(NAME mcuboot_manifest FILE "${manifest_path}") | ||
| yaml_save(NAME mcuboot_secondary_manifest FILE "${manifest_secondary_path}") |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 | ||
| # | ||
|
|
||
|
|
||
| cmake_minimum_required(VERSION 3.20.0) | ||
|
|
||
| find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) | ||
| project(ab_split) | ||
|
|
||
| target_sources(app PRIVATE src/main.c) | ||
| target_sources(app PRIVATE src/ab_utils.c) | ||
|
|
||
| target_include_directories( | ||
| app PRIVATE | ||
| ${ZEPHYR_MCUBOOT_MODULE_DIR}/boot/bootutil/include | ||
| ${ZEPHYR_MCUBOOT_MODULE_DIR}/boot/zephyr/include | ||
| ${ZEPHYR_BASE}/samples/subsys/mgmt/mcumgr/smp_svr/src | ||
| ) | ||
|
|
||
| target_sources_ifdef(CONFIG_MCUMGR_TRANSPORT_BT app PRIVATE | ||
| ${ZEPHYR_BASE}/samples/subsys/mgmt/mcumgr/smp_svr/src/bluetooth.c) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| # | ||
| # Copyright (c) 2025 Nordic Semiconductor ASA | ||
| # | ||
| # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause | ||
| # | ||
|
|
||
| config N_BLINKS | ||
| int "Number of fast blinks" | ||
| default 1 | ||
|
|
||
| config EMULATE_APP_HEALTH_CHECK_FAILURE | ||
| bool "Blocks confirmation of being healthy after the update" | ||
|
|
||
| source "Kconfig.zephyr" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,185 @@ | ||
| .. _ab_split_sample: | ||
|
|
||
| A/B with MCUboot and separated slots | ||
| #################################### | ||
|
|
||
| .. contents:: | ||
| :local: | ||
| :depth: 2 | ||
|
|
||
| The A/B with MCUboot and separated slots sample demonstrates how to configure the application for updates using the A/B method using MCUboot. | ||
| This sample is a variant of the :ref:`A/B sample <ab_sample>`, where the application and radio images are not merged, but reside in separate MCUboot slots. | ||
| This split increases demand on the number of memory areas that should be individually locked from accidental writes as well as requires additional care when preparing updates, so only a compatible set of slots are booted. | ||
| The additional dependency check during the boot process increases the time to boot the system. | ||
|
|
||
| It also includes an example to perform a device health check before confirming the image after the update. | ||
| You can update the sample using the Simple Management Protocol (SMP) with UART or Bluetooth® Low Energy. | ||
|
|
||
| To prevent the build system from merging slots, the sysbuild :kconfig:option:`SB_CONFIG_MCUBOOT_SIGN_MERGED_BINARY` option is disabled. | ||
| To enable manifest-based dependency management, the :kconfig:option:`SB_CONFIG_MCUBOOT_MANIFEST_UPDATES=y` option is enabled in the :file:`sysbuild/mcuboot/prj.conf` file. | ||
|
|
||
| Requirements | ||
| ************ | ||
|
|
||
| The sample supports the following development kits: | ||
|
|
||
| .. table-from-sample-yaml:: | ||
|
|
||
| You need the nRF Device Manager app for update over Bluetooth Low Energy: | ||
|
|
||
| * `nRF Device Manager mobile app for Android`_ | ||
| * `nRF Device Manager mobile app for iOS`_ | ||
|
|
||
|
|
||
| Overview | ||
| ******** | ||
|
|
||
| This sample demonstrates firmware update using the A/B method. | ||
| This method allows two copies of the application in the NVM memory. | ||
| It is possible to switch between these copies without performing a swap, which significantly reduces time of device's unavailability during the update. | ||
| The switch between images can be triggered by the application or, for example, by a hardware button. | ||
|
|
||
| This sample implements an SMP server. | ||
| SMP is a basic transfer encoding used with the MCUmgr management protocol. | ||
| For more information about MCUmgr and SMP, see :ref:`device_mgmt`. | ||
|
|
||
| The sample supports the following MCUmgr transports by default: | ||
|
|
||
| * Bluetooth | ||
| * Serial (UART) | ||
|
|
||
| A/B functionality | ||
| ================= | ||
|
|
||
| When the A/B with separated slots functionality is used, the device has two slots for each application and radio firmwares: slot A and slot B. | ||
| The slots are equivalent, and the device can boot from either of them. | ||
| By design, the slot A of the application image boots the slot A of the radio image, so there is a manifest-based dependency required to correctly verify the correctness of the image pairs. | ||
| In the case of MCUboot, this is achieved by using the Direct XIP feature. | ||
| Thus, note that the terms slot 0, primary slot, slot A and slot 1, secondary slot, slot B are used interchangeably throughout the documentation. | ||
| This configuration allows a background update of the non-active slot while the application runs from the active slot. | ||
| After the update is complete, the device can quickly switch to the updated slot on the next reboot. | ||
|
|
||
| The following conditions decide which slot will be booted (active) on the next reboot: | ||
|
|
||
| 1. If one of the slots contains a valid image, it is marked as valid only if the same slot of the other image is also valid. | ||
| #. If one of the slots is not valid, the other slot is selected as active. | ||
| #. If both slots are valid, the slot marked as "preferred" is selected as active. | ||
| #. If both slots are valid and none is marked as "preferred," the slot with the higher version number is selected as active. | ||
| #. If none of the above conditions is met, slot A is selected as active. | ||
|
|
||
| You can set the preferred slot using the ``boot_request_set_preferred_slot`` function. | ||
| Currently, this only sets the boot preference for a single reboot. | ||
|
|
||
| Identifying the active slot | ||
| --------------------------- | ||
|
|
||
| If the project uses the Partition Manager, the currently running slot can be identified by checking if ``CONFIG_NCS_IS_VARIANT_IMAGE`` is defined. | ||
| If it is defined, the application is running from slot B. | ||
| Otherwise, it is running from slot A. | ||
|
|
||
| If the project does not use the Partition Manager (a configuration currently only supported on the nRF54H20), the currently running slot can be identified by comparing the address pointed `zephyr,code-partition` to specific node addresses defined in the device tree. | ||
| The following node partitions are used by default: | ||
|
|
||
| * ``cpuapp_slot0_partition`` - Application core, slot A | ||
| * ``cpuapp_slot1_partition`` - Application core, slot B | ||
| * ``cpurad_slot0_partition`` - Radio core, slot A | ||
| * ``cpurad_slot1_partition`` - Radio core, slot B | ||
|
|
||
| For example, verifying that the application is running from slot A can be done by using the following macro: | ||
|
|
||
| .. code-block:: c | ||
|
|
||
| #define IS_RUNNING_FROM_SLOT_A \ | ||
| (FIXED_PARTITION_NODE_OFFSET(DT_CHOSEN(zephyr_code_partition)) == \ | ||
| FIXED_PARTITION_OFFSET(cpuapp_slot0_partition)) | ||
|
|
||
| .. _ab_split_build_files: | ||
|
|
||
| Build files | ||
| ----------- | ||
|
|
||
| This sample overrides the default build strategy, so application and radio images are built separately. | ||
| In this case, the following files should be sent to the device when performing an update: | ||
|
|
||
| * :file:`build/mcuboot_secondary_app/zephyr/zephyr.signed.bin` - Contains the slot B of the application image. | ||
| This file should be uploaded to the secondary slot when the device is running from slot A. | ||
| * :file:`build/ipc_radio_secondary_app/zephyr/zephyr.signed.bin` - Contains the slot B of the radio image. | ||
| This file should be uploaded to the secondary slot when the device is running from slot A. | ||
| * :file:`build/ab/zephyr/zephyr.signed.bin` - Contains the slot A of the application image. | ||
| This file should be uploaded to the primary slot when the device is running from slot B. | ||
| * :file:`build/ipc_radio/zephyr/zephyr.signed.bin` - Contains the slot A of the radio image. | ||
| This file should be uploaded to the primary slot when the device is running from slot B. | ||
|
|
||
| User interface | ||
| ************** | ||
|
|
||
| LED 0: | ||
| This LED indicates that the application is running from slot A. | ||
| It is controlled as active low, meaning it will turn on once the application is booted and blinks (turns off) in short intervals. | ||
| The number of short blinks is configurable using the :kconfig:option:`CONFIG_N_BLINKS` Kconfig option. | ||
| It will remain off if the application is running from slot B. | ||
|
|
||
| LED 1: | ||
| This LED indicates that the application is running from slot B. | ||
| It is controlled as active low, meaning it will turn on once the application is booted and blinks (turns off) in short intervals. | ||
| The number of short blinks is configurable using the :kconfig:option:`CONFIG_N_BLINKS` Kconfig option. | ||
| It will remain off if the application is running from slot A. | ||
|
|
||
| Button 0: | ||
| By pressing this button, the non-active slot will be selected as the preferred slot on the next reboot. | ||
| This preference applies only to the next boot and is cleared after the subsequent reset. | ||
|
|
||
| Configuration | ||
| ************* | ||
|
|
||
| |config| | ||
|
|
||
| Configuration options | ||
| ===================== | ||
|
|
||
| Check and configure the following configuration option for the sample: | ||
|
|
||
| .. _CONFIG_N_BLINKS: | ||
|
|
||
| CONFIG_N_BLINKS - The number of blinks. | ||
| This configuration option sets the number of times the LED corresponding to the currently active slot blinks (LED0 for slot A, LED1 for slot B). | ||
| The default value of the option is set to ``1``, causing a single blink to indicate *Version 1*. | ||
| You can increment this value to represent an update, such as set it to ``2`` to indicate *Version 2*. | ||
|
|
||
| .. _CONFIG_EMULATE_APP_HEALTH_CHECK_FAILURE: | ||
|
|
||
| CONFIG_EMULATE_APP_HEALTH_CHECK_FAILURE - Enables emulation of a broken application that fails the self-test. | ||
| This configuration option emulates a broken application that does not pass the self-test. | ||
|
|
||
| Additional configuration | ||
| ======================== | ||
|
|
||
| Check and configure the :kconfig:option:`CONFIG_MCUBOOT_IMGTOOL_SIGN_VERSION` library Kconfig option specific to the MCUboot library. | ||
| This configuration option sets the version to pass to imgtool when signing. | ||
| To ensure the updated build is preferred after a DFU, set this option to a higher version than the version currently running on the device. | ||
|
|
||
| Building and running | ||
| ******************** | ||
|
|
||
| .. |sample path| replace:: :file:`samples/dfu/ab_split` | ||
|
|
||
| .. include:: /includes/build_and_run.txt | ||
|
|
||
| Testing | ||
| ======= | ||
|
|
||
| To perform DFU using the `nRF Connect Device Manager`_ mobile app, complete the following steps: | ||
|
|
||
| .. include:: /app_dev/device_guides/nrf52/fota_update.rst | ||
| :start-after: fota_upgrades_over_ble_nrfcdm_common_dfu_steps_start | ||
| :end-before: fota_upgrades_over_ble_nrfcdm_common_dfu_steps_end | ||
|
|
||
| Instead of using the :file:`dfu_application.zip` file, you can also send the appropriate binary file directly, as described in :ref:`ab_split_build_files`. | ||
| Make sure to select the correct file based on the currently running slot. | ||
|
|
||
| Dependencies | ||
| ************ | ||
|
|
||
| This sample uses the following |NCS| library: | ||
|
|
||
| * :ref:`MCUboot <mcuboot_index_ncs>` | ||
13 changes: 13 additions & 0 deletions
13
samples/dfu/ab_split/boards/nrf54h20dk_nrf54h20_cpuapp.overlay
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| /* | ||
| * Copyright (c) 2025 Nordic Semiconductor ASA | ||
| * | ||
| * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause | ||
| */ | ||
|
|
||
| #include "../sysbuild/nrf54h20dk_nrf54h20_memory_map.dtsi" | ||
|
|
||
| / { | ||
| chosen { | ||
| zephyr,boot-mode = &boot_request; | ||
| }; | ||
| }; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Corrected 'pointed' to 'pointed to by' for grammatical accuracy.