Common FAQs on DFU


image description

If you are looking for a place to get started with DFU, please have a look at this presentation here and the tutorial here.

In this post, we cover some in depth Q&A topics regarding Device Firmware Update.

Questions:

A. What happens if something fails when doing DFU, would my device ever get bricked ?

B. How the vector table be forwarded when doing DFU ?

C. How do we do buttonless DFU ?

D. Why bonding could be an issue with DFU ?

E. How do I program the original application via programmer (JLINK), not by DFU bootloader ?

F. I can't debug the bootloader, why and what I need to change to debug it ?

G. How do I generate the Init packet or the init .dat file? what about the .zip file, and then the .json file ?

H. I use watchdog timer in my application, it triggers a reset when I jump to bootloader. How do I avoid that ?

I. Why do I find the address of bootloader is different from the default address ?

J. My application's size is too big to fit in dual bank update limit size. Where can I find single bank OTA DFU ?

K. I want my image to be signed, only authorized image can be accepted. Do you have any example ?

L. I'm using the bootloader from a pretty old SDK. Which DFU master tool I should use for that ?




Answers:

A. What happens if something fails when doing DFU, would my device get bricked ?

There are several types of DFU:

  1. Application Single bank update: If DFU transfer fails, the old application is lost. The DFU will be restarted => nonfunctional until a successful DFU => but not bricked

  2. Application Dual bank update: If DFU transfer fails, the old application is untouched. The DFU update can be restarted or not. => not bricked. If glitch, or power failure happens during flash swapping, bootloader will compare CRC and jump back to DFU mode => not bricked. (consider CRC is calculated in bootloader). If a non-functional application is updated. DFU Bootloader won’t be able to detect that with CRC, but end user can always reset the device in bootloader mode and reupdate a functional application => Not bricked.

  3. Softdevice update: Same as application dual bank update. Old softdevice will not be damaged until correct image copied. The softdevice update can be restarted => not bricked. If glitch or or power failure happens during flash swaping, bootloader+MBR will try to swap again old softdevice with new softdevice image until CRC matched. => not bricked.

  4. Bootloader update: Same as softdevice update. => not bricked

All cases above assume that the device has a physical button to enter DFU bootloader (press and hold a button when reset/power on to enter DFU mode). If there is no physical button on the device and the only way to enter DFU mode is via application (buttonless), care must be taken to avoid uploading un-functional application which will brick the device.


B. How the vector table be forwarded when doing DFU ?

  • Master boot record (MBR), located at address 0x000000, has the chip's default interrupt vector table and is always executed first
  • If there is bootloader address in UICR.BOOT_ADR, MBR will forward interrupts to Bootloader vector table (because it doesn’t trust softdevice), and jump to bootloader reset handler.
  • If there is no bootloader address, MBR will forward interrupt to softdevice and jump to first address after softdevice (application).
  • When bootloader starts, and Softdevice update is not in progress, it will tell MBR to forward the interrupts to softdevice vector table (calling sd_mbr_command with SD_MBR_COMMAND_INIT_SD option). Then bootloader tells softdevice to forward interrupts back to bootloader's vector table with sd_softdevice_vector_table_base_set(BOOTLOADER_REGION_START);.
  • When the bootloader needs to start the application, it will tell the softdevice to start forwarding the interrupt to the application with sd_softdevice_vector_table_base_set(CODE_REGION_1_START).

C. How do we do buttonless DFU ?

Buttonless DFU means we start the DFU mode from the application without the need of a physical button. The main technique is to set a flag, could be the retention register GPREGRET, or write to flash, etc. And then jump to bootloader either by a reset or directly branching.

Note that the bootloader is always executed before the application. If there is a valid application and if there is no request for DFU, it will forward interrupt and start the main application. This is the reason why we need to set a retention flag to tell the bootloader to enter DFU mode instead of starting the application.

Simplified approach: we can simply set a flag in GPREGRET and do a softreset (NVIC_SystemReset()) to get to the bootloader. Cons: No sharing bond information between bootloader and application.

Current approach: we still set a flag in GPREGRET to tell the bootloader to enter DFU mode, however, we don't do a softreset, but branch directly to the reset handle of the bootloader. The reason we do that is to utilize the use of sharing RAM between application and bootloader. If the connection between the device and the central is paired and bonded, we will share this bond information between the bootloader and the application via m_peer_data that located in the noInit RAM area by a SVC function: dfu_ble_svc_set_peer_data().
After that the bootloader will use the shared bond information to re-encrypt the link when the central connect to it. Note that since we jump directly to bootloader's reset handle, we don't need to reinitialize the softdevice.

When we jump to bootloader, our attribute table (services, characteristics) is changed with only DFU service. To tell the central to re-do a service discovery, the bootloader will send a Service Changed indication. This is the reason why we need to make sure Service Changed indication characteristic is allowed (write "0200" to the Service Changed).

The commands to tell application jumping to bootloader, and how to integrate the buttonless DFU is available in the documentation here.

Side note: If you use SDK v7.x and want to test buttonless example with gcc compiler, it may not work. The assembly code to branch from application to bootloader was made for KEIL only. You would need to copy and replace the code in StartApplication() function in bootloader_util_gcc.c in SDK v7x with the code inside bootloader_util_reset() function (with macro for GNUC) in bootloader_util.c from SDK v8.0 or later. You may need to add "-fomit-frame-pointer " into your GCC compiler option if the linker complains about "error: r7 cannot be used in asm here".


D. Why bonding could be an issue with DFU ?

When you application is bonded with the central, every time the connection is established, both device will try to use the stored LTK to encrypt the connection. However, the bootloader may not have that information on where the application store bond information, format to parse it, etc. This results that connection between the central and the bootloader won't be able to be re-encrypted and could be terminated by the central device (iOS device won't terminate the connection but Android's does).

There are 2 solutions to cope with this:

  • Change the address of the bootloader so that it appears as a new device. The central device will not try to re-encrypt the connection. This is very simple, but keep in mind that the connection won't be secured.

  • If you want the DFU process to be encrypted you should send the bond information from the application to the bootloader. There are several way to do this, one of them is discussed in question C and here. Note that this RAM sharing solution only works if you start with you application first and then enter bootloader. You can use flash to share bond information between application, this way it's fine to start bootloader from reset.


E. How do I program the original application via programmer (JLINK), not by DFU ?

After you flash your bootloader, the next step is to program the application via the bootloader DFU mode.

However, doing DFU usually take more time (and hassle) than programming via Jlink programmer, both when developing the firmware and when in mass production. DFU is more for bugs fix and firmware version upgrade on end products.

The reason directly flash the application firmware via JLink programmer won't work when there is a bootloader is that there is a flag (bank_0 in p_bootloader_settings ) stored in flash ( BOOTLOADER_SETTINGS_ADDRESS = 0x0003FC00) to tell the bootloader if there is a valid application at bank 0 or not. By default it's 0xFF = BANK_INVALID_APP. We need to set it to BANK_VALID_APP = 0x01.

There are several ways to do it:

  • Use programmer to write directly 0x01 to that address of BOOTLOADER_SETTINGS_ADDRESS (0x0003FC00).

  • Modify the bootloader firmware to write 0x01 to that address using attribute . It's the same way as we did on writing to NRF_UICR_BOOT_START_ADDRESS the value of BOOTLOADER_REGION_START (see bootloader_setting_arm.c)

  • Combine the a hex that specifically set the byte to 0x01 at BOOTLOADER_SETTINGS_ADDRESS with the hex file of your application. You can also combine the bootloader hex and softdevice file to that combination, to create a single hex file for all of them. This solution is described here. The app_valid_setting_apply.hex is the hex that does the trick. This is very useful when you are doing gang programming.

Very similar to the BANK_VALID_APP flag is the CRC field in the bootloader setting. We also need to tell the bootloader either not to check the CRC or to prepare and write the correct CRC and size of the application image at bank 0. To set the bootloader not to check for CRC, we can simply write 0x00 to bank_0_crc and bank_0_size.

The .hex example on the above link sets the CRC flag to 0x00. You can also use programmer to write directly as how we do it with the BANK_VALID_APP flag. The hex file was created while we use the bootloader in SDK v6.x, in that version in the structure of bootloader_settings_t, each field is 32bit. On SDK v7.x and later, we uncheck the option "Enum Container always int" in C/C++ setting and that cause the enum only occupies 1 byte. It will mix with crc in one word as showed in the figure by testy in the first comment below.

Note that the current implementation of the bootloader on SDK v8.1 doesn't store CRC of the image it flash on bank 0 to flash, 0x00 is stored in the CRC field, meaning there is no CRC check when the device booting up. It's up to you to modify the code to store CRC to flash (look for m_image_crc in the code).

Note: from SDK v12 the bootloader setting is modified, please follow the guide here.

F. I can't debug the bootloader, why and what I need to change to debug it ?

Please have a look at this answer.


G. How do I generate the Init packet or the init .dat file? what about the .zip file, and then the .json file ?

From SDK v7.x sending init packet when doing DFU is obligated. The init packet format is described here.

To know how to generate the .dat and .zip file, look at this well maintained guide here

The reason we need a .zip file is that we don't want to send the binary and the init data file separately, especially when we may have 2-3 files need to be transferred. So we use a .zip file for it.

However, the naming could be a problem, how to tell the DFU master which file is bootloader, application, softdevice, etc ? Then the manifest.json comes in, to tell the DFU master what to look for. Fortunately we don't have to do it manually, nrf.exe tool handles that. Please see the above guide to know how to use nrf.exe tool.

Note that on MCP v3.9 we can select the hex file directly, the .zip will be automatically generated. And the auto generated init file is a generic one, which accept any softdevice, no application revision number, etc.


H. I use watchdog timer in my application, it triggers a reset when I jump to bootloader. How do I avoid that ?

When you jump to bootloader, either by issuing a softreset (NVIC_SystemReset) or branching, the WDT is not stopped or reset.

If you want to keep the WDT running when in bootloader mode, you would need to keep reloading the WDT with RR register inside bootloader.

Another solution is just to let the WDT run off to trigger a reset instead of doing a softreset when in application. However, this way needs a long delay in the application to wait for the WDT to run off. Also, retained register and RAM will be cleared when doing WDT reset, so flags, bonding information if any should be stored in flash.

There is a quicker way of doing it is to set the system in SYSTEM OFF and wake it up immediately. Waking up the system from SYSTEM OFF won't reset the WDT but entering SYSTEM OFF does. To wake the chip from SYSTEM OFF instantly we need to set DETECT signal to active. This can be done by simply configure a GPIO pin with SENSE level low and a pulldown for example.


I. Why do I find the address of bootloader is different from the default address ?

See question D.


J. My application's image is too big to fit in dual bank update size limit. Where can I find single bank BLE DFU ?

At the beginning when we was implementing DFU bootloader with 2 transportation options: OTA and Serial (UART), we assumed that Serial is more reliable than OTA and there might be a risk to do single bank update with OTA DFU.

However, BLE is a reliable protocol, there should be no packet lost otherwise re-transmission will cause the link to be timed out. And as discussed at #A, if there is a glitch when transferring the image, the device is not bricked and can do a retry on DFU.

I think it's relatively safe to do single bank update via BLE. The risk is the same risk as doing Softdevice/bootloader update.

In our current SDK single bank OTA is still not available. However, on SDK v8.1 you can simply replace the file dfu_dual_bank.c with file dfu_single_bank.c and you can have single bank BLE update.

In previous SDK (before SDK 8.1) simply replace the dfu_dual_bank.c with dfu_singIe_bank.c won't work out of the box. Some further modification needed. I ported one pretty long ago, based on SDK v6.0. The principle should be the same, you should be able to port the bootloader in the latest SDK example.

Download the single bank OTA DFU SDK v6.0 here.

Note that:

  • Only application update is supported, no softdevice, bootloader update.

  • Only MCP and nRFToolbox on the phone is compatible, the MCP on PC won't.


K. I want my image to be signed, only authorized image can be accepted. Do you have any example ?

We have an experimental DFU with signing provided from SDK v9.0 at \examples\dfu\experimental. The documentation and source code is provided in that folder.

Also there is another example on github.

Download the ble dfu symmetric signing here.


L. I'm using the bootloader from a pretty old SDK. Which DFU master tool I should use for that ?

Please have a look at this blog post to find the DFU master tool compatible with your DFU bootloader. Note that, latest nRFToolbox and Master Control Panel app is compatible with all DFU bootloader versions.


We will try to keep this blog updated with more Q&A, please leave your questions in the comment.

Anonymous
  • Is there a blog to upgrade the application/bootloader (ncs v1.4.0) firmware from another MCU connected via SPI bus?

  • Is there a naming convention for the dfu firmware zip packet we create? Can points be used? i.e., dfu1.0_package.zip or dfu1_0_package.zip. 

  • I have included usbd code to buttonless dfu. My device doesnt advertise after first OTA. I eant to figure out this issue.So i tried to debug.If I tried to debug the application when bootloader  is present, I am not able to debug it .So i erase the device,  then debug it.Then it get stuck at svci init function at main.And if I commented this and perform debug the application shows no issues or error at error handler function.How could I debug my application along with bootloader functions.

  • I am using nrf51 and sdk v10.0.0 I am getting "operation failed" message in nrf connect while updating via OTA, how to solve this?

  • Hello,

    Thanks alot for such a wonderful blog.

    C. How do we do buttonless DFU ? Buttonless DFU means we start the DFU mode from the application without the need of a physical button. The main technique is to set a flag, could be the retention register GPREGRET, or write to flash, etc. And then jump to bootloader either by a reset or directly branching.

    In this: or write to flash

    Can you explain this ? For triggering bootloader, if we write any data to bootloader location in the flash, it will switch to bootloader mode?

    I am trying to run the following code but device keep getting reset and not entering to bootloader.

    #define BOOTLOADER_DFU_START 0xB1
    
        void loop()
        {
    
    
     if (particular condition is true)
    {  
      write "1" to the customer[0] register 
    }
    .
    .
    .
    .
    .
    
    int x = read data from the UICR register customer[0];
    
          if (customer[0] == "1") {
            {
            sd_power_gpregret_set(BOOTLOADER_DFU_START);
    
            NVIC_SystemReset();   
    
           //OR set the register directly and then call NVIC_SystemReset(); 
    
            NRF_POWER->GPREGRET = BOOTLOADER_DFU_START;
    
            NVIC_SystemReset();
          } 
        }
    }