Bootloader with Custom DFU

We have an existing DFU to deliver the pending firmware in flash.  I need to develop a custom bootloader that copies the pending firmware and activates it.  I am close but getting stuck. 

Issue:

- When app_activate() is called, it just skips over it.  Even when I add __root to it.  The function exists in the .map file, but I can't enter it in debugger.

Specs:

- nRF52840, SDK15.0.0, SD140, using IAR compiler

My memory layout is

  

My Steps to processing the hex files

1) To fake a pending firmware update, I offset the main application to place it in Bank-1:

- srec_cat.exe app.hex -Intel -offset 0x5F000 -o appOffset.hex -Intel

2) To create the bootloader settings, I use the nrfutil:

- `nrfutil.exe” settings generate --family NRF52840 --application appOffset.hex --application-version 1 --bootloader-version 1 --bl-settings-version 1 settings.hex

3) I use the NRF programmer to merge and program the MBR/SoftDevices, Pending FW, Bootloader, and Bootloader Settings

Debugging

I'm trying to reuse as much of the example bootloader.  

int main(void)
{		

	NOT_USED ret_code_t ret_val;
	
	/* Copy pending update then reboot
	1. Check if there is a pending fw update:
		- nrf_bootloader_fw_activate() checks 
		p_bank->bank_code == NRF_DFU_BANK_VALID_APP, then calls app_activate()
	2. Copies pending firmware to slot 0 via app_activate(), then checks the CRC
	3. If successful, bootloader_reset();
	*/
	
    ret_val = nrf_bootloader_init(NULL);
	
    // Should never be reached.
    NRF_LOG_INFO("After main");
    NRF_LOG_FLUSH();
}

When debugging, I have to manually write to the s_dfu_settings.  I assume this would typically be set by the DFU.

- When I created the bootloader settings, I used an offset copy of the app.  So here, I copied the info from Bank 0 to Bank 1 to tell the firmware that there is a pending update to handle.

This works in getting me to app_activate(), the function that copies the pending firmware to the target address (0x26000).  However, in the debugger, it just skips over this function.  I checked the map file and the function is linked.  What am I doing wrong?

I am manually setting s_dfu_settings.  Can I write to this in the application?  Is there a preferred way to do this?

  • I tried setting the optimization lower than "high" but the bootloader is too large.  That's probably why I can't follow it in the debugger. Is there an easy way to cut back on the bootloader, as I don't need the transport functions?

    Disassembly of the app_activate() function responsible for copying the pending FW update.  It gets to 0xf'a164 ADDS, then jumps out of the function.

  • I just realized I need to set the address of the pending fw update.  It did not help though.  What other settings are normally handled by the DFU?

    - s_dfu_settings.progress.update_start_address = 0x85000  // Bank-1

  • Hmm... That did copy the pending firmware update, but put it higher.  Need to look closer at the offset address.  Maybe I should create bootloader settings based on the app before offset.  Then load the pending update manually.

  • Hi,

    I do not have a full overview of your bootloader, but I will try to give some pointers and suggestions in hope that some may be useful.

    Debugging an application that is optimized is difficult, but the SDK bootloader (that you have based your on) use RTT loggign extensively if you build the debug variant, and you could add more logging relevant for what you are currently debugging. Enabling logging (which is done in the _debug sample bootloader projects) does increase the size though, so you need to be able to fit that while debugging. Another option could perhaps be to build the bootloader without optimization and accept that it is larger while debugging the bootloader, and use a simpler test application while doing this if you are able to make it fit? (This would be just while working on the bootloader).

    Regarding removing the transports, the transports are quite decoupled from the rest of the bootloader, and are registered build time via a section variable that holds the list of backends that is initialized one after another. You should not have to make many adjustments other than just remove all the files used for the transport (for instance serial UART if you based your bootloader on a project that used that).

  • Success, with some manual manipulation.

    1. Uploading pending FW update.  I place the pending FW update in Bank 2 and read the bootloader settings into a new structure (s_dfu_settings) by calling nrf_dfu_settings_init.  After updating the fields below, I call nrf_dfu_settings_write() to write the settings to Flash.

    s_dfu_settings

    - Set bank-1.image size and .image_crc:
    - Set bank_1.bank_code to 1 to indicate the bank had valid firmware
    - Set .progress.update_start_address to the start address of bank-1

    2. On Bootloader Startup, I call ret_nrf_bootloader_init(NULL) which handles the update, eventually calling  app_activate()to copy the firmware to bank-0, then reset the device.

    ~~~QUESTION~~~

    To step through the code, I have to set optimization to NONE and enlarge the space allocated to the bootloader by one page.  This includes changing the ROM_region in the .icf  file and the definition of the BOOTLOADER_START_ADDR:

    • From: #define BOOTLOADER_START_ADDR (CODE_START)
    • To: #define BOOTLOADER_START_ADDR 0x000F7000

    However, should I be modifying CODE_START instead?  How would one do that?

    • #define CODE_START ((uint32_t)&__vector_table)

    Also, I'd like to avoid modifying the SDK, as it is a shared resource for other projects.  But when I define BOOTLOADER_START_ADDR, I receive the error incompatible redefinition.  How can I define this without touching the SDK?

Related