EmbeddedRelated.com
Blogs
The 2024 Embedded Online Conference

nRF5 to nRF Connect SDK migration via DFU over BLE

Mike VoytovichSeptember 7, 20232 comments

Background

Nordic Semiconductor is a leader in wireless SoC’s, especially in BLE MCU’s with their nRF product line. The nRF5 SDK was released in 2012, and up until recently was the primary SDK for development on their chipsets.

In Sept of 2021, Nordic announced that they would be moving all new development over to the nRF Connect SDK, and the nRF5 SDK would be entering maintenance mode. To take advantage of the features already provided by the nRF Connect SDK, as well as new development beyond BLE5, all of my new projects are based on NCS. In particular, the Device Tree architecture, West, Zephyr OS, Partition Manager, the large number of integrated device drivers, provided libraries such as LittleFS, Non-Volatile Settings (NVS), CBOR, TLS, etc make NCS the preferred SDK over nRF5 in my opinion. Furthermore nRF5 isn’t an option for newer chipsets such and the nRF91, and Zephyr is available for many other architectures such as ESP32, STM32, etc so a Zephyr-based OS has become my go-to for new product development.

Problem

Unfortunately, Nordic doesn’t provide a path to upgrade devices already manufactured with firmware based on the nRF5 SDK with a Device Firmware Update (DFU) path to firmware based on the nRF Connect SDK over BLE. This is probably because the SDK’s use different bootloaders (nRF5 uses a Nordic developed bootloader, NCS uses MCUBoot), the images are laid out in flash differently (e.g., nRF5 images have a Master Boot Record (MBR) at address 0 followed by a SoftDevice, while NCS images typically put MCUBoot at address 0), they use different formats for the DFU image, etc. which makes it hard to find a standard solution across many different hardware variations and firmware implementations.

This article is available in PDF format for easy printing

Note that the nRF5 bootloader can in fact boot applications built against either SDK; but, the nRF5 bootloader requires a SoftDevice to do a BLE DFU, so keeping the MBR plus SoftDevice plus nRF5 bootloader would leave very little room left over in the internal nRF52 flash for a Zephyr based application.

This means that legacy devices either (a) have to remain on the nRF5 SDK; or (b) have to be RMA’d and re-flashed via SWD. Nordic’s expectation is that legacy devices can remain on the nRF5 SDK; however, that can be problematic if there’s a desire to start developing with NCS for existing hardware. It means that hardware already shipped won’t get the new features only available in NCS; and 2 distinct codebases have to be maintained, making bug fixes, firmware updates, etc much more difficult.

Solution

This post describes one possible solution, implemented on the nRF52840. The hardware contains an external SPI flash, which is essential for this particular approach. The process works like this:

  • DFU an nRF5 SDK bootloader that removes protection on the MBR and bootloader regions
  • create a BLE service to transfer a merged (bootloader plus application) NCS image into external SPI flash
  • write some code that runs entirely in RAM with interrupts disabled, that can erase the nRF52 flash, and copy the NCS image in external SPI flash to the nRF52

While the concept is relatively simple, some of the implementation details and potential pitfalls may not be obvious. What follows contains some of the specifics of the solution outlined above.

DFU Protection

The nRF52 bootloader uses ACL (Access Control List) to protect the MBR and bootloader memory regions, setting them to read and execute only. This protection needs to be disabled in order to overwrite the MBR region with the merged NCS image, containing a bootloader starting where the MBR was previously located. The calls to this routine need to be removed:

nrf_bootloader_flash_protect()

Which sets the ACL registers (or BPROT registers on some nRF5 devices):

NRF_ACL->ACL[acl_instance].ADDR = address;
NRF_ACL->ACL[acl_instance].SIZE = size;
NRF_ACL->ACL[acl_instance].PERM = mask;

Keep in mind that signing and DFU’ing a bootloader that doesn’t protect these regions makes the bootloader insecure, but this change is temporary until the NCS update is complete.

BLE Service and Transferring Image

This aspect of the migration process is relatively straight forward. I created a custom BLE service in the application, that first accepts a header containing the image length and a CRC, and then accepts a streamed binary image and writes it into external SPI flash. Note that the image should be a binary of the merged .hex output by Zephyr, which contains both the bootloader and application — it can be created with something like this:

objcopy --input-target=ihex --output-target=binary build/zephyr/merged.hex merged.bin

Copying the Image

The final step is to copy the merged binary image from SPI flash into nRF52 flash starting at address 0. This assumes that the modified bootloader is already running, which disables ACL protection.

This code has to (a) run entirely from RAM, as we’re going to erase all of the code in the nRF52 flash; and (b) has to run with interrupts disabled, because we’re also removing the interrupt vectors. This means that any drivers will need to use polling instead of interrupts, and we’ll need to relocate all executable code into RAM.

First, interrupts need to be disabled, so we don’t try to jump to interrupt vectors after they’ve been erased:

__disable_irq();

GCC allows for code to be relocated to RAM with this attribute:

__attribute__((used, long_call, section(".data")))

Keep in mind that these RAM routines cannot reference *any* code in flash — so any calls to things like logging, drivers, etc are off limits! Fortunately GCC will produce a compilation error so it’ll be obvious if any flash-based code is being called.

Some of the routines that will likely need to be relocated into RAM are:

  • setting and clearing LEDs to provide indication of progress
  • petting the watchdog
  • erasing and writing to nRF52 internal flash
  • reading from SPI flash
  • rebooting the system

For example, a RAM based polling version of writing to nRF52 internal flash using the NVMC would look something like this:

__attribute__((used, long_call, section(".data"))) static void ram_nrf_nvmc_write_word(uint32_t address, uint32_t value)
{
  NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen;
  __ISB();
  __DSB();
  *(uint32_t *)address = value;
  while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {;}
  NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren;
  __ISB();
  __DSB();
}

One trick to take advantage of regarding reading from SPI flash is XIP — the nRF52 QSPI driver provides memory-mapped IO to QSPI flash, starting at address 0x12000000 . This allow for a simple pointer access read, vs porting a polled version of the QSPI version to run in RAM!

At this point, the entire image can be copied from SPI flash into nRF52 flash, and the CPU can be reset. On reboot, the nRF Connect SDK based firmware should be up and running!

Conclusion

I hope this post provides some insight into one approach for the nRF5 to NCS migration, the assurance that it is possible, and some inspiration for other ways to approach this migration. The time and effort to get this working can certainly pay off in the long run, especially if it means all future development can be consolidated into a single codebase that leverages the nRF Connect SDK moving forward.



The 2024 Embedded Online Conference
[ - ]
Comment by TimschoSeptember 14, 2023

Hello Mike, this seems to be a good way to move the old nRF devices to NCS and thank you for sharing it. I am glad to know that it works and will apply it  soon.  One more thing I would like to ask; does this method enable implementing the two stage bootlaoder or it  only refers to the first stage bootloader?

best regards

[ - ]
Comment by mikevoytSeptember 14, 2023

H Timscho, you could employ either bootloader option, because with this method, the entire nRF52 flash is erased and re-written with your new image.

So there are no dependencies whatsoever on what was previously written in flash with the nRF5 SDK; and the image you created with your new nRF Connect SDK project can be burned into flash in its entirety, just as you would using a JLINK for example.  Just keep in mind the new binary must contain a merged image with both the bootloader(s) as well as application image.

To post reply to a comment, click on the 'reply' button attached to each comment. To post a new comment (not a reply to a comment) check out the 'Write a Comment' tab at the top of the comments.

Please login (on the right) if you already have an account on this platform.

Otherwise, please use this form to register (free) an join one of the largest online community for Electrical/Embedded/DSP/FPGA/ML engineers: