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

Custom bootloader w/o DFU transport

I am trying to write a custom bootloader to do FW updates on our device.  In our main application, we already have a way to transfer a new signed application image over BLE, and that's being saved in external flash that we communicate w/ over QSPI.  After reading through a few devzone Q&As, my understanding of what usually happens is the following:

0. System is reset

1. The MBR runs first and checks the UICR registers to see if bootloader exists. If it does it gives control to bootloader.

2. The bootloader will enter DFU mode if one of the DFU entering methods are used (button press, register set)

3. DFU mode enables a transport which allows for transferring a new image into Bank1

4. The image in Bank1 is checked for validity

5. If valid, Bank1 is transferred to Bank0

6. Then control is handed over to the application in Bank0

In our scheme, we've already taken care of the transferring of the image and the checking of validity in the main application, so I had the following simplified flow in mind:

1. From main app, after checking validity, copy new image from external flash to Bank1

2. Reset device to get into bootloader

3. Bank1 transferred to Bank0

4. Control is handed over to Bank0

My questions:

1. Is this flow even possible?

2. In Step1

a. Should the memory copied into Bank1 be the FW hex (binary) or is there some special format for this?

b. Should I be updating the bootloader settings page here?

c. How do tell the bootloader where Bank1 is?

d. How do I flag to the bootloader that there is a new validated application waiting in Bank1?

  • Hi Luis, 

    1. Is this flow even possible?

     Yes,  but I would recommend performing the validity check in the bootloader instead of in the application to avoid having code for verifying the ECC signature in both the application and the bootloader. 

    2. In Step1

     I would recommend using the request interface in the bootloader to perform the actual update, see Requests in the Bootloader and DFU modules documentation.  I assisted another customer that also wanted to perform DFU with the image stored in external flash. Please see the attached pdf.

     Bootloader_External_Flash_Modifications.pdf

    Please note that this just to give you an introduction /idea on how to use the request interface to perform the DFU from external flash. 

    Best regards

    Bjørn

  • I am actually trying a different approach because we already have to verify signatures etc in the main app.  So thus far I've been able to get the full image transferred over, stored in external flash, and validated in the main app.  I'm trying to avoid having the bootloader deal with the external flash at all.  The next steps in my procedure are:

    1. Transfer the image page-by-page from external flash into BANK1 (I have this set to 0x8a000)

    2. Once, this is done, update the following s_dfu_settings from the main app:

    bank_current = NRF_DFU_CURRENT_BANK_1

    bank_1.bank_code = NRF_DFU_BNAK_VALID_APP

    bank_1.image_size = <length of my FW image>

    progress.update_start_address = 0x8a000

    write_offset = 0

    3. Reset

    4. In bootloader, check if bank_1.bank_code = VALID_APP and if so transfer image from BANK1 to BANK0 and run application

    I haven't been able to get step1 to work with any FW image greater than 4kB (1 Flash page).  My code looks like this right now:

    bool external_flash_transfer_to_bank1(uint32_t src_addr, uint32_t len)
    {
        ret_code_t ret_val = NRF_ERROR_NOT_FOUND;
        uint32_t err_code;
        m_qspi_finished = false;
    
        uint8_t  num_full_pages = len/FLASH_PAGE_SIZE;
        uint16_t bytes_over_page = len%FLASH_PAGE_SIZE;
    
        //Transfer all the full pages first
        for (uint16_t i = 0; i < num_full_pages; i++)
        {
          external_flash_read(m_buffer_rx, FLASH_PAGE_SIZE, src_addr + (i*FLASH_PAGE_SIZE));
    
          //while(!is_bank1_flash_idle());
          ret_val = write_data_to_flash(BANK1_FLASH_ADDR + (i * FLASH_PAGE_SIZE), m_buffer_rx, FLASH_PAGE_SIZE);
          if(ret_val != NRF_SUCCESS)
          {
              NRF_LOG_INFO("Failed transferring full pages to bank1 on page %d.", i);
              return ret_val;
          }
        }
    
        //Transfer left over bytes
        memset(m_buffer_rx, 0xff, sizeof(m_buffer_rx));
        external_flash_read(m_buffer_rx, bytes_over_page, src_addr + (num_full_pages*FLASH_PAGE_SIZE));
    
        //while(!is_bank1_flash_idle());
        ret_val = write_data_to_flash(BANK1_FLASH_ADDR + (num_full_pages*FLASH_PAGE_SIZE), m_buffer_rx, FLASH_PAGE_SIZE);
        if(!ret_val) NRF_LOG_INFO("Failed transferring bank1 overflow bytes.");
        
        return ret_val;
    }


    It's been very hard to debug because I keep getting HardFaults when I try to step through the code.  I'm using SES, SDK 15.3, and the nrf52840. 

  • UPDATE:  I've been able to get the FW image transferred over to internal flash memory.  From the SDK there is a note that the fstorage calls using the SD backend can fail because they share resources with the SD NVIC.  So if, for example, the advertising interval is too aggressive it can mess with fstorage calls.  I got this to work by doing the following:


    nrf_sdh_disable_request();
    /* fstorage calls here */
    nrf_sdh_enable_request();

Related