Feedback
Feedback

If you are having issues with the exercises, please create a ticket on DevZone: devzone.nordicsemi.com
Click or drag files to this area to upload. You can upload up to 2 files.

Multi-image builds and the Partition Manager

v2.8.x – v2.7.0

This topic is not yet updated to nRF Connect SDK v2.7.0 or v2.8.0. This work is ongoing.

Apologies for any inconvenience.

v2.6.2 -v2.5.2

So far, we have covered general concepts related to bootloaders. In this section, we will dive into specific implementations used in the nRF Connect SDK. We will learn about the multi-image build system and the Partition Manager script, as we will use these to configure our bootloaders later. These have been briefly covered in the Fundamentals course, we will learn about both in more depth here.

Multi-image builds

To explain multi-image builds, let’s first have a look at how a program for an nRF chip would look without any framework for building multiple images. In this case, we first build the bootloader and then the application. Both are built separately, without any knowledge of each other. It is up to the developer to make sure that the bootloader and application agree on where in flash they are stored. We can call this Partition Information. After the images have been built, the output file (typically hex) from each build is written to the nRF chip separately. Here is a figure to illustrate the process:

Building individual images

It is possible to use the above method in the nRF Connect SDK, but it is not the default. Instead, we use our Multi-Image Build system to make the build system handle the building of multiple images.
In multi-image builds, an application is built as the main image, and all other images are added as child images to this main image.
Here is a simplified figure which shows how the multi-image build system can be illustrated:

Simplified illustration of Multi-image build

We will now cover the main features of multi-image builds in the following subsections.

Building images in order and choosing the default child image

When the main application is built, the multi-image build system will automatically build all child images in the correct order. As an example, if we add CONFIG_BOOTLOADER_MCUBOOT=y to the Zephyr Hello World sample, this will enable multi-image builds. (We will have a closer look at this in upcoming exercises) From the resulting build log, observe that a child image is built in the below figure, marked as (1):

While it is possible to add custom child images to applications, a few types of child images make up the majority of use-cases. These are Bootloaders and network core images. In the nRF Connect SDK, we have a set of default samples which can be used as child images for these. When enabling certain features, the multi-image build system will be enabled, and automatically choose a fitting image for that based on configurations in the main application, as follows:

Configuring child images

If Child images are often built from samples in the nRF Connect SDK, and we do not recommend making changes to the nRF Connect SDK source, then how can we configure the child images?
Multi-image builds gives us the possibility for configuring Image-specific variables, either by overlaying or changing existing child image configurations. There are two main ways to configure child images: Either using CMake arguments, or by using a predefined folder structure and configuration files.

To use CMake arguments, we can either overlay individual Kconfig options using -D<child_image>_CONFIG_WHATEVER=y, for example: -Dmcuboot_CONFIG_LOG_DEFAULT_LEVEL=3 . An illistration is shown below:

We can also overlay using files using CMake arguments. This is done by using -D<child_image>_EXTRA_CONF_FILE or -D<child_image>_DTC_OVERLAY_FILE. Or to overwrite Kconfig, we can use -D<child_image>_CONF_FILE.

The alternative is to use a folder structure to add files to the project. This is the method we recommend, and the method most of our examples use to configure child images. These files must be located inside a directory named child_image, and are named after the child image. This is typically child_image/mcuboot.conf or child_image/mcuboot/prj.conf, but a full list can be found at Multi-image builds: Permanent configuration changes to child images:

Here is an example of how a simple project with MCUboot overlays would look:

Generate output files

The multi-image build system will also generate output files, used for programming or DFU of the nRF chip. When we use the “Flash” function in the VS Code Extension for a single image build, build/zephyr/zephyr.hex is programmed to the nRF chip. For multi-image builds, build/zephyr/merged.hex is used instead. A list over output build files generated by multi-image builds can be found under Multi-image builds: What image files are. A more comprehensive list over generated output build in the nRF Connect SDK can be found in our Build and configuration system docs. The multi-image build system will also generate different output build files for different cores when relevant.

Keep track of partitioning

As previously mentioned: When building multiple images for a single core, we must make sure that the memory addresses (Partitions) are the same in configuration for the main application and the child image, so they do not overlap. This is also needed so that the first image can continue from the next application, for example when a bootloader boots the application.

For this, we have a standalone Python script called the Partition Manger. The multi-image build system uses the Partition Manager for partitioning. Since the Partitioning is a standalone script, it will be covered in its own section below.

Partitioning in nRF Connect SDK

In nRF Connect SDK there are three partitioning schemes, which one to utilize is something dependent on the SoC/SiP and the application as explained below:

  1. DTS Partitioning (relies on DTS binding fixed partitions defined in devicetree). This is used for basic samples with no multi-image build, such as the Blinky & Hello World sample
  2. Dynamic partitioning. A concept by the Partition Manager (nRF Connect SDK only-covered next). Default for nRF91 with TF-M, nRF53, nRF52+DFU/FOTA
  3. Static partitioning through pm_static.yml files. A concept by the Partition Manager (nRF Connect SDK only-covered next). Not default. Samples specific (Ex: Matter, Thingy:53). For DFU/FOTA, developers must enable static partitioning before the firmware production release.

Partition Manager

To quote from “DFU over UART from the application”:

Usually, the bootloader and the application must be configured with the same start address and size for where in non-volatile memory the new firmware is stored. These pre-decided memory areas are referred to as slots.

Zephyr uses the DTS binding fixed partitions to partition the memory into different slots. However, since the nRF Connect SDK has need for a more dynamical partition method, we have the Partition Manager. The Partition Manager uses yaml to define partitions, and generates a partition scheme to fit with features needed by an application.

When we have built a project, we can see how the partitioning was done by looking at build/partitions.yml or by using the VS Code Memory Report tool:

If you use a terminal, you can also use west build -t partition_manager_report. See Partition Manager report docs.

To see if the Partition Manager has been enabled for an application, check CONFIG_PARTITION_MANAGER_ENABLED. This is a read-only configuration that will automatically be set if the Partition Manager has been enabled.

In a multi-image build, Partition Manager partitioning for the main app is inherited by child images.

pm.yml files

Activation of some features in the nRF Connect SDK will automatically enable the Partition Manager. We can see a list of those at CONFIG_PARTITION_MANAGER_ENABLED.
When the Partition Manager is enabled, partitions will be created to fill all the memory. Which different partitions are created depends on which features are enabled in the application. For example, if CONFIG_NVS is enabled, a nvs partition is created, or if MCUboot is enabled, the partitions mcuboot, mcuboot_primary and mcuboot_secondary are created. These partitions are defined in pm.yml files. pm.yml files for child images can be found in child image sample folder, such as nrf/samples/bootloader/pm.yml. pm.yml files for other partitions can be found in the nrf/subsys/partition_manager/ folder.

Configuring the Partition Manager

To configure Partition Manager configuration, we can use Kconfig options related to each partition. For example, to configure the size of MCUboot, we can use CONFIG_PM_PARTITION_SIZE_MCUBOOT. I recommend looking at relevant pm.yml files to find configuration options. It is also possible to search for CONFIG_PM_ in our Kconfig search, but keep in mind that CONFIG_PM_ can refer to both Partition Manager and Power Management here.

Static Configuration

It is possible to “freeze” the partitioning scheme by creating doing static configuration, aka creating a pm_static.yml file in the main application. A good starting point for pm_static.yml is to copy build/partitions.yml. Static Partitioning is relevant for DFU, and we will explain why later in this course.

Register an account
Already have an account? Log in
(All fields are required unless specified optional)

  • 8 or more characters
  • Upper and lower case letters
  • At least one number or special character

Forgot your password?
Enter the email associated with your account, and we will send you a link to reset your password.