Skip to content

Commit 5b677e6

Browse files
committed
samples: Add split slot A/B sample
Add a variant of the A/B sample that presents how to perform A/B updates in a system, where application and radio images uses separate images and slots. Ref: NCSDK-35733 Signed-off-by: Tomasz Chyrowicz <[email protected]>
1 parent 9facdc1 commit 5b677e6

File tree

14 files changed

+790
-0
lines changed

14 files changed

+790
-0
lines changed

CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,7 @@
498498
/samples/dect/dect_phy/dect_shell/ @nrfconnect/ncs-modem-tre
499499
/samples/dect/dect_phy/hello_dect/ @nrfconnect/ncs-modem
500500
/samples/dfu/ab/ @nrfconnect/ncs-eris
501+
/samples/dfu/ab_split/ @nrfconnect/ncs-eris
501502
/samples/dfu/dfu_target/ @nrfconnect/ncs-eris
502503
/samples/dfu/dfu_multi_image/ @nrfconnect/ncs-eris
503504
/samples/dfu/fw_loader/ @nrfconnect/ncs-eris
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#
2+
# Copyright (c) 2025 Nordic Semiconductor ASA
3+
#
4+
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
#
6+
7+
cmake_minimum_required(VERSION 3.20.0)
8+
9+
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
10+
project(ab_split)
11+
12+
target_sources(app PRIVATE
13+
src/main.c
14+
src/ab_utils.c
15+
)
16+
17+
target_include_directories(app PRIVATE
18+
${ZEPHYR_MCUBOOT_MODULE_DIR}/boot/bootutil/include
19+
${ZEPHYR_MCUBOOT_MODULE_DIR}/boot/zephyr/include
20+
${ZEPHYR_BASE}/samples/subsys/mgmt/mcumgr/smp_svr/src
21+
)
22+
23+
target_sources_ifdef(CONFIG_MCUMGR_TRANSPORT_BT app PRIVATE
24+
${ZEPHYR_BASE}/samples/subsys/mgmt/mcumgr/smp_svr/src/bluetooth.c
25+
)

samples/dfu/ab_split/Kconfig

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#
2+
# Copyright (c) 2025 Nordic Semiconductor ASA
3+
#
4+
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
#
6+
7+
config N_BLINKS
8+
int "Number of fast blinks"
9+
default 1
10+
11+
config EMULATE_APP_HEALTH_CHECK_FAILURE
12+
bool "Blocks confirmation of being healthy after the update"
13+
14+
source "Kconfig.zephyr"

samples/dfu/ab_split/README.rst

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
.. _ab_split_sample:
2+
3+
A/B with MCUboot and separated slots
4+
####################################
5+
6+
.. contents::
7+
:local:
8+
:depth: 2
9+
10+
The A/B with MCUboot and separated slots sample demonstrates how to configure the application for updates using the A/B method using MCUboot.
11+
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.
12+
This split increases the number of memory areas that must be individually protected from accidental writes.
13+
It also requires additional care when preparing updates to ensure that only a compatible set of slots is booted.
14+
The additional dependency check during the boot process increases the time to boot the system.
15+
16+
It also includes an example to perform a device health check before confirming the image after the update.
17+
You can update the sample using the Simple Management Protocol (SMP) with UART or Bluetooth® Low Energy.
18+
19+
To prevent the build system from merging slots, the sysbuild :kconfig:option:`SB_CONFIG_MCUBOOT_SIGN_MERGED_BINARY` option is disabled.
20+
To enable manifest-based dependency management, the :kconfig:option:`SB_CONFIG_MCUBOOT_MANIFEST_UPDATES=y` option is enabled in the :file:`sysbuild.conf` file.
21+
22+
Requirements
23+
************
24+
25+
The sample supports the following development kits:
26+
27+
.. table-from-sample-yaml::
28+
29+
You need the nRF Device Manager app for update over Bluetooth® Low Energy:
30+
31+
* `nRF Device Manager mobile app for Android`_
32+
* `nRF Device Manager mobile app for iOS`_
33+
34+
35+
Overview
36+
********
37+
38+
This sample demonstrates a firmware update using the A/B method.
39+
This method allows you to store two copies of the application in non-volatile memory (NVM).
40+
You can switch between these copies without performing a swap.
41+
This solution significantly reduces the time during the update in which the device is unavailable.
42+
The switch between images can be triggered by the application or, for example, by a hardware button.
43+
44+
This sample implements an SMP server.
45+
SMP is a basic transfer encoding used with the MCUmgr management protocol.
46+
For more information about MCUmgr and SMP, see :ref:`device_mgmt`.
47+
48+
The sample supports the following MCUmgr transports by default:
49+
50+
* Bluetooth
51+
* Serial (UART)
52+
53+
A/B functionality
54+
=================
55+
56+
When you use the A/B configuration with separated slots, the device provides two slots for each set of application and radio firmware: slot A and slot B.
57+
The slots are equivalent, and the device can boot from either of them.
58+
With MCUboot, this is achieved by using the Direct XIP feature.
59+
By design, slot A of the application image boots slot A of the radio image.
60+
This design implies that verifying the image pairs correctly requires a manifest-based dependency.
61+
There can be only one image that includes the manifest TLV.
62+
Its index is configured using :kconfig:option:`SB_CONFIG_MCUBOOT_MANIFEST_IMAGE_INDEX`.
63+
By default, the application image index (``0``) is selected.
64+
65+
In this document, the following conventions are followed:
66+
67+
* The application image index (``0``) is referred to as the *manifest image*.
68+
* The following names refer to the same images and are used interchangeably throughout the documentation:
69+
70+
* *Slot 0*, *primary slot*, and *slot A*
71+
* *Slot 1*, *secondary slot*, and *slot B*
72+
73+
This configuration allows a background update of the non-active slot while the application runs from the active slot.
74+
After the update is complete, the device can quickly switch to the updated slot on the next reboot.
75+
76+
The following conditions decide which slot is considered *active* and is booted on the next reboot:
77+
78+
1. If one of the slots of the manifest image contains a valid image, it is marked as valid only if all other images, described by the manifest are present and placed in the same slot as the manifest.
79+
#. If one of the slots of the manifest image is not valid, the other slot is selected as active.
80+
#. If both slots of the manifest image are valid, the slot marked as "preferred" is selected as active.
81+
#. If both slots of the manifest image are valid and none is marked as *preferred*, the slot with the higher version number is selected as active.
82+
#. If none of the above conditions is met, slot A is selected as active.
83+
#. For all other images, the same slot is selected.
84+
85+
You can set the preferred slot using the ``boot_request_set_preferred_slot`` function.
86+
Currently, this only sets the boot preference for a single reboot.
87+
88+
Identifying the active slot
89+
---------------------------
90+
91+
If the project uses the Partition Manager, the currently running slot can be identified by checking if ``CONFIG_NCS_IS_VARIANT_IMAGE`` is defined.
92+
If it is defined, the application is running from slot B.
93+
Otherwise, it is running from slot A.
94+
95+
If the project does not use the Partition Manager (a configuration currently supported only on the nRF54H20 SoC), you can identify the currently running slot by comparing the address referenced by ``zephyr,code-partition`` with the specific node addresses defined in the devicetree.
96+
The following node partitions are used by default:
97+
98+
* ``cpuapp_slot0_partition`` - Application core, slot A
99+
* ``cpuapp_slot1_partition`` - Application core, slot B
100+
* ``cpurad_slot0_partition`` - Radio core, slot A
101+
* ``cpurad_slot1_partition`` - Radio core, slot B
102+
103+
For example, verifying that the application is running from slot A can be done by using the following macro:
104+
105+
.. code-block:: c
106+
107+
#define IS_RUNNING_FROM_SLOT_A \
108+
(FIXED_PARTITION_NODE_OFFSET(DT_CHOSEN(zephyr_code_partition)) == \
109+
FIXED_PARTITION_OFFSET(cpuapp_slot0_partition))
110+
111+
.. _ab_split_build_files:
112+
113+
Build files
114+
-----------
115+
116+
This sample overrides the default build strategy, so application and radio images are built separately.
117+
In this case, you must send the following files to the device when performing an update:
118+
119+
120+
* :file:`build/mcuboot_secondary_app/zephyr/zephyr.signed.bin` - Contains the slot B of the application image.
121+
Upload this file to the secondary slot when the device is running from slot A.
122+
* :file:`build/ipc_radio_secondary_app/zephyr/zephyr.signed.bin` - Contains the slot B of the radio image.
123+
Upload this file to the secondary slot when the device is running from slot A.
124+
* :file:`build/ab/zephyr/zephyr.signed.bin` - Contains the slot A of the application image.
125+
Upload this file to the primary slot when the device is running from slot B.
126+
* :file:`build/ipc_radio/zephyr/zephyr.signed.bin` - Contains the slot A of the radio image.
127+
Upload this file to the primary slot when the device is running from slot B.
128+
129+
User interface
130+
**************
131+
132+
LED 0:
133+
This LED indicates that the application is running from slot A.
134+
It is controlled as active low.
135+
This means that it turns on once the application is booted and turns off in short intervals to blinks.
136+
The number of short blinks is configurable using the :kconfig:option:`CONFIG_N_BLINKS` Kconfig option.
137+
It remains off when the application is running from slot B.
138+
139+
LED 1:
140+
This LED indicates that the application is running from slot B.
141+
It is controlled as active low.
142+
This means that it turns on once the application is booted and turns off at short intervals to blinks.
143+
The number of short blinks is configurable using the :kconfig:option:`CONFIG_N_BLINKS` Kconfig option.
144+
It remains off when the application is running from slot A.
145+
146+
Button 0:
147+
By pressing this button, you select the non-active slot as the preferred slot for the next reboot.
148+
This preference applies only to the next boot and is cleared after the subsequent reset.
149+
150+
Configuration
151+
*************
152+
153+
|config|
154+
155+
Configuration options
156+
=====================
157+
158+
Check and configure the following configuration options for the sample:
159+
160+
.. _CONFIG_N_BLINKS_ABSPLIT:
161+
162+
CONFIG_N_BLINKS - The number of blinks.
163+
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).
164+
The default value of the option is set to ``1``, causing a single blink to indicate *Version 1*.
165+
You can increment this value to represent an update, such as set it to ``2`` to indicate *Version 2*.
166+
167+
.. _CONFIG_EMULATE_APP_HEALTH_CHECK_FAILURE_AB_SPLIT:
168+
169+
CONFIG_EMULATE_APP_HEALTH_CHECK_FAILURE - Enables emulation of a broken application that fails the self-test.
170+
This configuration option emulates a broken application that does not pass the self-test.
171+
172+
Additional configuration
173+
========================
174+
175+
Check and configure the :kconfig:option:`CONFIG_MCUBOOT_IMGTOOL_SIGN_VERSION` Kconfig option for the MCUboot library.
176+
This configuration option sets the version to pass to imgtool when signing.
177+
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.
178+
179+
Building and running
180+
********************
181+
182+
.. |sample path| replace:: :file:`samples/dfu/ab_split`
183+
184+
.. include:: /includes/build_and_run.txt
185+
186+
Testing
187+
=======
188+
189+
To perform DFU using the `nRF Connect Device Manager`_ mobile app, complete the following steps:
190+
191+
.. include:: /app_dev/device_guides/nrf52/fota_update.rst
192+
:start-after: fota_upgrades_over_ble_nrfcdm_common_dfu_steps_start
193+
:end-before: fota_upgrades_over_ble_nrfcdm_common_dfu_steps_end
194+
195+
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`.
196+
Make sure to select the correct file based on the currently running slot.
197+
198+
Dependencies
199+
************
200+
201+
This sample uses the following |NCS| library:
202+
203+
* :ref:`MCUboot <mcuboot_index_ncs>`
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/*
2+
* Copyright (c) 2025 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
*/
6+
7+
#include "../sysbuild/nrf54h20dk_nrf54h20_memory_map.dtsi"
8+
9+
/ {
10+
chosen {
11+
zephyr,boot-mode = &boot_request;
12+
};
13+
};

samples/dfu/ab_split/prj.conf

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# Enable MCUmgr and dependencies.
2+
CONFIG_NET_BUF=y
3+
CONFIG_ZCBOR=y
4+
CONFIG_CRC=y
5+
CONFIG_MCUMGR=y
6+
CONFIG_STREAM_FLASH=y
7+
CONFIG_FLASH_MAP=y
8+
9+
# Some command handlers require a large stack.
10+
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2304
11+
CONFIG_MAIN_STACK_SIZE=2176
12+
13+
# Ensure an MCUboot-compatible binary is generated.
14+
CONFIG_BOOTLOADER_MCUBOOT=y
15+
16+
# Enable flash operations.
17+
CONFIG_FLASH=y
18+
19+
# Required by the `taskstat` command.
20+
CONFIG_THREAD_MONITOR=y
21+
22+
# Support for taskstat command
23+
CONFIG_MCUMGR_GRP_OS_TASKSTAT=y
24+
25+
# Enable statistics and statistic names.
26+
CONFIG_STATS=y
27+
CONFIG_STATS_NAMES=y
28+
29+
# Enable most core commands.
30+
CONFIG_FLASH=y
31+
CONFIG_IMG_MANAGER=y
32+
CONFIG_MCUMGR_GRP_IMG=y
33+
CONFIG_MCUMGR_GRP_IMG_NRF=y
34+
CONFIG_MCUMGR_GRP_OS=y
35+
CONFIG_MCUMGR_GRP_STAT=y
36+
37+
# Enable logging
38+
CONFIG_LOG=y
39+
CONFIG_MCUBOOT_UTIL_LOG_LEVEL_WRN=y
40+
41+
# Disable debug logging
42+
CONFIG_LOG_MAX_LEVEL=3
43+
44+
# Enable boot requests through retained memory.
45+
CONFIG_RETAINED_MEM=y
46+
CONFIG_RETENTION=y
47+
CONFIG_NRF_MCUBOOT_BOOT_REQUEST=y
48+
49+
CONFIG_RETENTION_BOOT_MODE=y
50+
CONFIG_MCUMGR_GRP_OS_RESET_BOOT_MODE=y
51+
52+
# Enable DK LED/button library
53+
CONFIG_DK_LIBRARY=y
54+
55+
# Configure bluetooth
56+
57+
CONFIG_BT=y
58+
CONFIG_BT_PERIPHERAL=y
59+
60+
# Allow for large Bluetooth data packets.
61+
CONFIG_BT_L2CAP_TX_MTU=498
62+
CONFIG_BT_BUF_ACL_RX_SIZE=502
63+
CONFIG_BT_BUF_ACL_TX_SIZE=502
64+
65+
# Enable the Bluetooth mcumgr transport (unauthenticated).
66+
CONFIG_MCUMGR_TRANSPORT_BT=y
67+
CONFIG_MCUMGR_TRANSPORT_BT_CONN_PARAM_CONTROL=y
68+
69+
# Enable the Shell mcumgr transport.
70+
CONFIG_BASE64=y
71+
CONFIG_CRC=y
72+
CONFIG_SHELL=y
73+
CONFIG_SHELL_BACKEND_SERIAL=y
74+
CONFIG_MCUMGR_TRANSPORT_SHELL=y
75+
76+
# Enable the mcumgr Packet Reassembly feature over Bluetooth and its configuration dependencies.
77+
# MCUmgr buffer size is optimized to fit one SMP packet divided into five Bluetooth Write Commands,
78+
# transmitted with the maximum possible MTU value: 498 bytes.
79+
CONFIG_MCUMGR_TRANSPORT_BT_REASSEMBLY=y
80+
CONFIG_MCUMGR_TRANSPORT_NETBUF_SIZE=2475
81+
CONFIG_MCUMGR_GRP_OS_MCUMGR_PARAMS=y
82+
CONFIG_MCUMGR_TRANSPORT_WORKQUEUE_STACK_SIZE=4608
83+
84+
# Enable the LittleFS file system.
85+
CONFIG_FILE_SYSTEM=y
86+
CONFIG_FILE_SYSTEM_LITTLEFS=y
87+
88+
# Enable file system commands
89+
CONFIG_MCUMGR_GRP_FS=y
90+
91+
# Enable the storage erase command.
92+
CONFIG_MCUMGR_GRP_ZBASIC=y
93+
CONFIG_MCUMGR_GRP_ZBASIC_STORAGE_ERASE=y
94+
95+
# Disable Bluetooth ping support
96+
CONFIG_BT_CTLR_LE_PING=n
97+
98+
# Disable shell commands that are not needed
99+
CONFIG_CLOCK_CONTROL_NRF_SHELL=n
100+
CONFIG_DEVICE_SHELL=n
101+
CONFIG_DEVMEM_SHELL=n
102+
CONFIG_FLASH_SHELL=n

samples/dfu/ab_split/sample.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
sample:
2+
description: AB update sample with separated slots
3+
name: ab_split
4+
common:
5+
sysbuild: true
6+
build_only: true
7+
tags:
8+
- dfu_ab
9+
- ci_samples_dfu
10+
11+
tests:
12+
sample.dfu.ab_split:
13+
extra_configs:
14+
- CONFIG_MCUMGR_GRP_IMG_ALLOW_CONFIRM_NON_ACTIVE_IMAGE_ANY=y
15+
platform_allow:
16+
- nrf54h20dk/nrf54h20/cpuapp
17+
integration_platforms:
18+
- nrf54h20dk/nrf54h20/cpuapp

0 commit comments

Comments
 (0)