Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions samples/dfu/ab_split/CMakeLists.txt
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)
14 changes: 14 additions & 0 deletions samples/dfu/ab_split/Kconfig
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"
194 changes: 194 additions & 0 deletions samples/dfu/ab_split/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
.. _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 slot-based dependency management, the :kconfig:option:`CONFIG_BOOT_VERSION_CMP_USE_SLOT_NUMBER` 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 slot-based dependency required to correctly verify the correctness of the images.
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.

In addition, set the :kconfig:option:`CONFIG_MCUBOOT_EXTRA_IMGTOOL_ARGS` library Kconfig option specific to the MCUboot library to specify the slot-specific image dependencies.
For example, to ensure that there is a valid radio image with version ``1.0.0`` or greater in the radio primary slot set:

.. code-block:: console

CONFIG_MCUBOOT_EXTRA_IMGTOOL_ARGS="--dependencies \"(0,primary,1.0.0)\""

The slot-specific dependency is automatically converted to point to the secondary slot when building the second variant of each image.

Building and running
********************

.. |sample path| replace:: :file:`samples/dfu/ab`

.. 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_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 samples/dfu/ab_split/boards/nrf54h20dk_nrf54h20_cpuapp.overlay
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;
};
};
106 changes: 106 additions & 0 deletions samples/dfu/ab_split/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# Enable MCUmgr and dependencies.
CONFIG_NET_BUF=y
CONFIG_ZCBOR=y
CONFIG_CRC=y
CONFIG_MCUMGR=y
CONFIG_STREAM_FLASH=y
CONFIG_FLASH_MAP=y

# Some command handlers require a large stack.
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2304
CONFIG_MAIN_STACK_SIZE=2176

# Ensure an MCUboot-compatible binary is generated.
CONFIG_BOOTLOADER_MCUBOOT=y

# Enable flash operations.
CONFIG_FLASH=y

# Required by the `taskstat` command.
CONFIG_THREAD_MONITOR=y

# Support for taskstat command
CONFIG_MCUMGR_GRP_OS_TASKSTAT=y

# Enable statistics and statistic names.
CONFIG_STATS=y
CONFIG_STATS_NAMES=y

# Enable most core commands.
CONFIG_FLASH=y
CONFIG_IMG_MANAGER=y
CONFIG_MCUMGR_GRP_IMG=y
CONFIG_MCUMGR_GRP_OS=y
CONFIG_MCUMGR_GRP_STAT=y

# Enable logging
CONFIG_LOG=y
CONFIG_MCUBOOT_UTIL_LOG_LEVEL_WRN=y

# Disable debug logging
CONFIG_LOG_MAX_LEVEL=3

# Enable boot requests through retained memory.
CONFIG_RETAINED_MEM=y
CONFIG_RETENTION=y
CONFIG_NRF_MCUBOOT_BOOT_REQUEST=y

CONFIG_RETENTION_BOOT_MODE=y
CONFIG_MCUMGR_GRP_OS_RESET_BOOT_MODE=y

# Enable DK LED/button library
CONFIG_DK_LIBRARY=y

# Configure bluetooth

CONFIG_BT=y
CONFIG_BT_PERIPHERAL=y

# Allow for large Bluetooth data packets.
CONFIG_BT_L2CAP_TX_MTU=498
CONFIG_BT_BUF_ACL_RX_SIZE=502
CONFIG_BT_BUF_ACL_TX_SIZE=502
CONFIG_BT_CTLR_DATA_LENGTH_MAX=251

# Enable the Bluetooth mcumgr transport (unauthenticated).
CONFIG_MCUMGR_TRANSPORT_BT=y
CONFIG_MCUMGR_TRANSPORT_BT_CONN_PARAM_CONTROL=y

# Enable the Shell mcumgr transport.
CONFIG_BASE64=y
CONFIG_CRC=y
CONFIG_SHELL=y
CONFIG_SHELL_BACKEND_SERIAL=y
CONFIG_MCUMGR_TRANSPORT_SHELL=y

# Enable the mcumgr Packet Reassembly feature over Bluetooth and its configuration dependencies.
# MCUmgr buffer size is optimized to fit one SMP packet divided into five Bluetooth Write Commands,
# transmitted with the maximum possible MTU value: 498 bytes.
CONFIG_MCUMGR_TRANSPORT_BT_REASSEMBLY=y
CONFIG_MCUMGR_TRANSPORT_NETBUF_SIZE=2475
CONFIG_MCUMGR_GRP_OS_MCUMGR_PARAMS=y
CONFIG_MCUMGR_TRANSPORT_WORKQUEUE_STACK_SIZE=4608

# Enable the LittleFS file system.
CONFIG_FILE_SYSTEM=y
CONFIG_FILE_SYSTEM_LITTLEFS=y

# Enable file system commands
CONFIG_MCUMGR_GRP_FS=y

# Enable the storage erase command.
CONFIG_MCUMGR_GRP_ZBASIC=y
CONFIG_MCUMGR_GRP_ZBASIC_STORAGE_ERASE=y

# Disable Bluetooth ping support
CONFIG_BT_CTLR_LE_PING=n

# Disable shell commands that are not needed
CONFIG_CLOCK_CONTROL_NRF_SHELL=n
CONFIG_DEVICE_SHELL=n
CONFIG_DEVMEM_SHELL=n
CONFIG_FLASH_SHELL=n

# Define dependencies
CONFIG_MCUBOOT_IMGTOOL_SIGN_VERSION="1.0.0"
CONFIG_MCUBOOT_EXTRA_IMGTOOL_ARGS="--dependencies \"(1,primary,1.0.0)\""
17 changes: 17 additions & 0 deletions samples/dfu/ab_split/sample.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
sample:
description: AB update sample with separated slots
name: ab_split
common:
sysbuild: true
build_only: true
tags:
- dfu_ab
- ci_samples_dfu

tests:
sample.dfu.ab_split:
sysbuild: true
platform_allow:
- nrf54h20dk/nrf54h20/cpuapp
integration_platforms:
- nrf54h20dk/nrf54h20/cpuapp
Loading
Loading