This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

DFU OTA Bootloader update SDK10.0.0

Hi,

We have some custom boards with NRF51822 and the S110 (v8.0.0) softdevice, on which we have programmed the dfu dual bank bootloader, based on the example one from the SDK. The bootloader is at its default start address (0x3C000) and it seems to work ok. It can update the application firmware and it can update itself; I've created a zipped hex image for the same bootloader using the "nrfutil.exe dfu genpkg --bootloader" command and sent it OTA to the device, and that worked ok.

However, this bootloader does not preserve application data, and we need to preserve those in order to not lose bond information across dfu updates (our application firmware uses the buttonless dfu)

So, as per the documentation I have edited the dfu_types.h to allocate 3 pages using DFU_APP_DATA_RESERVED -- the goal being to preserve 2 (two) pages for pstorage in addition to the one (1) that is for swap. infocenter.nordicsemi.com/index.jsp

I'm using Keil Studio to compile the code, and I kept the mem Target settings of the original bootloader, that is: mem start at 0x3C000 and size: 0x3C00

If I send the corresponding zip image of this bootloader (eg bootloaderAppSave.zip) to the board (that has the previous bootloader) then after the dfu update the device is no longer discoverable and appears to not be working anymore.

If I use a programmer and nRF go Studio to flash the softdevice and then the new bootloader, then the bootloader works ok. It can also update itself if I send the new zip image (the bootloaderAppSave.zip) to it.

I tried dumping the flash data using the "nrfjprog -f nrf51 --readcode" to see if I notice anything strange. I compared this dump with the flash dump from a working version of the new bootloader (when using a programmer to flash it)

When the new bootloader is sent OTA to the device running the old (non app data preserving) bootloader then:

  1. At address 0x00000800 it has some data (0x0000000000980200AC0C000000000000), whereas normally (old bootloader and new bootloader flashed with programmer) the bytes there are all 0xFF (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)

  2. The full bootloader image is copied (as expected) in an intermediate flash location (from 0x0002A000 to 0x0002D2A0) (actual size: 0x32A0).

  3. At address 0x0003C000 is unwritten up to 0x0003C7FF), and the bootloader image starts at 0x0003C800 and goes up to 0x0003F2A0 at which point it is truncated. So the bootloader does not start at the expected address (0x3C000 as per the UICR that I read with nrfjprog) and only 0x2AA0 bytes of the 0x32A0 are copied there, shifted by 0x800.

Any ideas why this is happening?

I need to be able to update the OLD bootloader firmware over-the-air; flashing all of our devices with a programmer is not an option.

  • The size of the reserved app data affects the static memory layout of the swap pages (i.e., bank1 address). So the new bootloader will need to know the memory layout of the old bootloader in order to be able to finalize the update. Please refer to the following thread for more details: devzone.nordicsemi.com/.../

  • Thank you very much! That was what I was looking for, but somehow didn't come across those previous threads dealing with the issue

  • For anyone interested I'm adding our solution to this matter here, too.

    As noted we are working with SDK 10.0.0 extending the dual bank S110 ble bootloader sample code. For this particular issue (updating and old bootloader with no support for preserving application data, to a new that had to preserve 3 code pages of data (0xc00). Our app firmware is using the Device Manager module, and the pstorage is initialized within the Device Manager init method, so our memory had 1 Swap code page (size 0x400) right below the bootloader start address, and 2 reserved pages (in our app firmware code, PSTORAGE_NUM_OF_PAGES is set to 2).

    In our bootloader project, we eventually went for the bootloader code preserving 4 code pages (0x1000). There is no reason why the 0xc00 wouldn't also work for our case, but since we were faced with other issues and had to change and test / trial & error multiple combinations of stuff, I'm reporting here the current configuration that works.

    The basic solution, as Vidar mentions in the linked references is for the new bootloader to check on both the default DFU_BANK_1_REGION_START (where this bootloader will place uploaded firmware from now on) and an adjusted one (where the old bootloader actually put the new bootloader firmware when it was uploaded), if the first check fails. This check is done when the new bootloader needs to validate that its own image swap was completed successfully (when it takes over during the last steps of the dfu).

    So in dfu_types.h we have:

        #define DFU_APP_DATA_RESERVED           (0x1000) //(4*CODE_PAGE_SIZE)    /**< Size of Application Data that must be preserved between application updates. This value must be a multiple of page size. Page size is 0x400 (1024d) bytes, thus this value must be 0x0000, 0x0400, 0x0800, 0x0C00, 0x1000, etc. */
        #define DFU_BANK_PADDING                (DFU_APP_DATA_RESERVED % (2 * CODE_PAGE_SIZE)) /**< Padding to ensure that image size banked is always page sized. */
    // Page size we are padding to here is 0x800 multiple...
    // For 0xC00 (3 code_page_size) this evaluates to 0x400 for padding, to get 0x1000
    // For 0x1000 (4 code_page_size) there is no need for padding (0x00) 
        																										
        #define DFU_IMAGE_MAX_SIZE_FULL         (DFU_REGION_TOTAL_SIZE - DFU_APP_DATA_RESERVED)                 /**< Maximum size of an application, excluding save data from the application. */
        #define DFU_IMAGE_MAX_SIZE_BANKED       ((DFU_REGION_TOTAL_SIZE - \
                                                  DFU_APP_DATA_RESERVED - \
                                                  DFU_BANK_PADDING) / 2)                                        /**< Maximum size of an application, excluding save data from the application. */
        
        #define DFU_BL_IMAGE_MAX_SIZE           (BOOTLOADER_SETTINGS_ADDRESS - BOOTLOADER_REGION_START)         /**< Maximum size of a bootloader, excluding save data from the current bootloader. */
        
        #define DFU_BANK_0_REGION_START         CODE_REGION_1_START                                             /**< Bank 0 region start. */
        #define DFU_BANK_1_REGION_START         (DFU_BANK_0_REGION_START + DFU_IMAGE_MAX_SIZE_BANKED)  
        #define DFU_BANK_1_REGION_START_OLD_ALTER	(DFU_BANK_1_REGION_START + ((DFU_APP_DATA_RESERVED + DFU_BANK_PADDING) / 2))  /**< New: Bank 1 region start for bootloader with NO APP DATA PRESERVED. */ // new
    
        ...
    

    Then in dfu_dual_bank.c we have:

    uint32_t dfu_bl_image_validate(void)
    {
    	
        uint32_t errCode = NRF_SUCCESS;
    		
        bootloader_settings_t bootloader_settings;
        sd_mbr_command_t      sd_mbr_cmd;
    
        bootloader_settings_get(&bootloader_settings);
    
        if (bootloader_settings.bl_image_size != 0)
        {
    		// first check with the default DFU_BANK_1_REGION_START for bootloader-firmware update
            uint32_t bl_image_start = (bootloader_settings.sd_image_size == 0) ?
                                      DFU_BANK_1_REGION_START :
                                      bootloader_settings.sd_image_start +
                                      bootloader_settings.sd_image_size;
    
            sd_mbr_cmd.command             = SD_MBR_COMMAND_COMPARE;
            sd_mbr_cmd.params.compare.ptr1 = (uint32_t *)BOOTLOADER_REGION_START;
            sd_mbr_cmd.params.compare.ptr2 = (uint32_t *)(bl_image_start);
            sd_mbr_cmd.params.compare.len  = bootloader_settings.bl_image_size / sizeof(uint32_t);
    
    //      return sd_mbr_command(&sd_mbr_cmd); // old
    		errCode = sd_mbr_command(&sd_mbr_cmd); // new
    		if(errCode == NRF_SUCCESS || bootloader_settings.sd_image_size != 0) { // new check
    			// if success or case for Soft Device update, return the result of the compare
    			return errCode;        // new
    		} else { // new
    			// if the previous comparison failed FOR THE CASE OF BOOTLOADER-FIRMWARE UPDATE, then try again to compare with the alternate DFU_BANK_1_REGION_START_OLD_ALTER address.
    			bl_image_start = (bootloader_settings.sd_image_size == 0) ?
    						  (DFU_BANK_1_REGION_START_OLD_ALTER): 
    						  bootloader_settings.sd_image_start +
    						  bootloader_settings.sd_image_size;
    
    			sd_mbr_cmd.command             = SD_MBR_COMMAND_COMPARE;
    			sd_mbr_cmd.params.compare.ptr1 = (uint32_t *)BOOTLOADER_REGION_START;
    			sd_mbr_cmd.params.compare.ptr2 = (uint32_t *)(bl_image_start);
    			sd_mbr_cmd.params.compare.len  = bootloader_settings.bl_image_size / sizeof(uint32_t);
    			// this time we return the result of the compare command directly 
    			return sd_mbr_command(&sd_mbr_cmd); 
    		}
        }
        return NRF_SUCCESS;
    }
    
Related