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

Modifying BL Settings Page from Main App

I'm using an SES with NRF52840, w/ SDK 15.3, and SoftDevice S140.  Im working on DFU and have two components:

- a main app that already takes care of moving a FW image into BANK1 (which I've defined as 0x8a000) in internal flash

- a modified bootloader that skips over DFU transport initialization and moves straight to FW activation

TLDR; what is the proper way of flagging a new FW image is ready for activation to the bootloader?

What I've been able to do with an NRF52840 DK is:

- Manually flash a new FW image in Bank1 using nrfjprog

- Add the following code in nrf_bootloader_init. Whenever the button is pressed, dfu_settings are manually changed to flag that there is a FW image waiting to be activated in Bank1

if(nrf_gpio_pin_read(NRF_BL_DFU_ENTER_METHOD_BUTTON_PIN) == 0)
{
  // THESE ARE BEING HARD-CODED TO FORCE FW ACTIVATION
  s_dfu_settings.bank_current                   = NRF_DFU_CURRENT_BANK_1;
  s_dfu_settings.bank_1.bank_code               = NRF_DFU_BANK_VALID_APP;
  s_dfu_settings.bank_1.image_size              = 1708;
  s_dfu_settings.progress.update_start_address  = 0x8a000;
  s_dfu_settings.write_offset                   = 0;
  //APP_ERROR_CHECK(nrf_dfu_settings_write(NULL));
}

This successfully moves the image from Bank1 to Bank0 and starts the app.  I've been able to change the pulsing duration in the blinky code example this way.

Then I tried moving this to my main app. What I did:

- import nrf_dfu_settings.c and it's dependencies

- added the memory sections for the bootloader_settings_page in the flash_placement.xml and Memory Segments in SES
- enable NRF_DFU_IN_APP and NRF_DFU_SETTINGS_COMPATIBILITY_MODE

- called nrf_dfu_settings_init(true) from my main()

- then, in my code, once the FW was validated and copied into Bank1 I wrote the same settings as above except this time calling nrf_dfu_settings_write(NULL)

//Update BL settings to signal a new valid app is ready
s_dfu_settings.bank_current                   = NRF_DFU_CURRENT_BANK_1;
s_dfu_settings.bank_1.bank_code               = NRF_DFU_BANK_VALID_APP;
s_dfu_settings.bank_1.image_size              = scratchpad_data.app_len;
s_dfu_settings.progress.update_start_address  = BANK1_FLASH_ADDR;
s_dfu_settings.write_offset                   = 0;
APP_ERROR_CHECK(nrf_dfu_settings_write(NULL));

However, doing this results in the following error:

<warning> nrf_dfu_settings: Settings write aborted since it tries writing to forbidden settings.
<error> app: Fatal error: 16385
<warning> app: System reset

So... what is the proper way to mimic the result I saw on the DK but doing it in the main app?

Parents
  • Hi,

    The default bootloader settings implementation does not allow the app to change the fields shown inside the red squares below. This is to help ensure that only the bootloader can do the final validation and activation of new FW images. 

    But it doesn't mean that has to be done this way. I see that you are not storing any "init_command" for the bootloader, so I guess you have already done the validation and just want the bootloader to activate the FW by moving the image from Bank 1 to Bank 0? If that's the case I think it may make sense to just the change nrf_dfu_settings.c implementation to allow these fields to be controlled by the application as well. Relevant functions to modify would be settings_forbidden_parts_equal_to_backup() and settings_forbidden_parts_copy_from_backup(). 

    Best regards,

    Vidar

  • Thanks for the response, Vidar.  Unfortunately I'm still not having luck.  I found this post and so I did the following:

    IN THE MAIN APP

    -------------------------

    1. Updated my settings-writing code to look like this:

    //Update BL settings to signal a new valid app is ready
    s_dfu_settings.bank_1.bank_code                 = NRF_DFU_BANK_VALID_APP;
    s_dfu_settings.bank_0.bank_code                 = NRF_DFU_BANK_INVALID;
    s_dfu_settings.app_version                      = 0;
    s_dfu_settings.bank_1.image_crc                 = crc32_compute((uint8_t *)BANK1_FLASH_ADDR, scratchpad_data.app_len, NULL);
    s_dfu_settings.bank_1.image_size                = scratchpad_data.app_len;
    s_dfu_settings.bank_current                     = NRF_DFU_CURRENT_BANK_1;
    s_dfu_settings.bank_layout                      = NRF_DFU_BANK_LAYOUT_DUAL;
    
    /* Set the progress to zero and remove the last command */
    memset(&s_dfu_settings.progress, 0, sizeof(dfu_progress_t));
    memset(s_dfu_settings.init_command, 0xFF, DFU_SIGNED_COMMAND_SIZE);
    s_dfu_settings.write_offset                     = 0;
    s_dfu_settings.progress.update_start_address    = BANK1_FLASH_ADDR;
    
    s_dfu_settings.boot_validation_softdevice.type  = NO_VALIDATION;
    s_dfu_settings.boot_validation_app.type         = VALIDATE_CRC;
    s_dfu_settings.boot_validation_bootloader.type  = NO_VALIDATION;
    memcpy(s_dfu_settings.boot_validation_app.bytes, &s_dfu_settings.bank_1.image_crc, sizeof(uint32_t));
    
    s_dfu_settings.settings_version                 = NRF_DFU_SETTINGS_VERSION;
    
    APP_ERROR_CHECK(nrf_dfu_settings_write(NULL));
    
    sd_nvic_SystemReset();

    2. Disabled NRF_DFU_IN_APP in the main application to allow writing of forbidden sections

    3. Call nrf_dfu_settings_init(true) in the main app bc I have the SD initialized.

    IN THE BOOTLOADER

    --------------------------------

    1. call nrf_dfu_settings_init(false) and commented out the settings_forbidden_copy_from_backup() to avoid overwriting the settings I've written from the main app

    if (settings_valid)
    {
        NRF_LOG_DEBUG("Using settings page.");
        memcpy(&s_dfu_settings, m_dfu_settings_buffer, sizeof(nrf_dfu_settings_t));
        if (settings_backup_valid)
        {
            NRF_LOG_DEBUG("Would have copied forbidden parts from backup page. Skipping.");
            //NRF_LOG_DEBUG("Copying forbidden parts from backup page.");
            //settings_forbidden_parts_copy_from_backup((uint8_t *)&s_dfu_settings);
        }
    }

    2. Commented out the post_validate() function 

    #if NRF_BL_DFU_ALLOW_UPDATE_FROM_APP
    // Postvalidate if DFU has signaled that update is ready.
    if (s_dfu_settings.bank_current == NRF_DFU_CURRENT_BANK_1)
    {
        NRF_LOG_INFO("Current bank is set to Bank1");
        //postvalidate();
    }
    #endif

    3. Enabled NRF_BL_DFU_ALLOW_UPDATE_FROM_APP

    RESULTS

    --------------

    1. If I put a breakpoint right before the reset in the main app I can see that the s_dfu_settings structure has changed and I get the following RTT logs, so to me that looks like the writing is happening successfully...

     nrf_dfu_settings: Writing settings...
     nrf_dfu_settings: Erasing old settings at: 0x000FF000
     nrf_dfu_flash: nrf_fstorage_erase(addr=0x0x000FF000, len=1 pages), queue usage: 0
     nrf_dfu_flash: nrf_fstorage_write(addr=0x000FF000, src=0x20024A14, len=896 bytes), queue usage: 1

    2. If I use RTT logging on the bootloader however, I see the reset happen, but I get the following print out:

    <info> app: Inside main
    <debug> app: In nrf_bootloader_init
    <debug> nrf_dfu_settings: Calling nrf_dfu_settings_init()...
    <debug> nrf_dfu_flash: Initializing nrf_fstorage_nvmc backend.
    <debug> nrf_dfu_settings: Using settings page.
    <debug> nrf_dfu_settings: Would have copied forbidden parts from backup page. Skipping.
    <debug> nrf_dfu_settings: Destination settings are identical to source, write not needed. Skipping.
    <info> nrf_dfu_settings: Backing up settings page to address 0xFE000.
    <debug> nrf_dfu_settings: Destination settings are identical to source, write not needed. Skipping.
    <debug> app: Enter nrf_bootloader_fw_activate
    <info> app: No firmware to activate.
    <info> nrf_dfu_settings: Backing up settings page to address 0xFE000.
    <debug> nrf_dfu_settings: Destination settings are identical to source, write not needed. Skipping.
    <debug> app: Running nrf_bootloader_app_start with address: 0x00001000
    <debug> app: Disabling interrupts. NVIC->ICER[0]: 0x0

    So even though it seems like a successful write is happening on the main application side, the settings don't seem to take and the bootloader doesn't recognize the new FW image.

  • UDPATE: I was able to get the update to work if I commented out the line right before the s_dfu_settings block.  This line was the piece of code that was actually doing the transferring of the code from external flash to Bank1, but since the FW image had already been transferred to Bank1 in previous calls, I could get away with commenting it out.  Obviously this won't work in production, so I'm trying to understand how this line might affect the settings writing.  The function l commented out was the external_flash_transfer_to_bank1():

    external_flash_transfer_to_bank1(test_fw_addr + CAM_UPDATE_HEADER_SIZE, scratchpad_data.app_len);
    
    //Update BL settings to signal a new valid app is ready
    s_dfu_settings.bank_1.bank_code                 = NRF_DFU_BANK_VALID_APP;
    s_dfu_settings.bank_0.bank_code                 = ......


    and the actual function looks like this:

    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;
    
        NRF_LOG_INFO("Transferring from external flash to BANK1");
    
        // fstorage shares resources with SD, temporarily disable SD to avoid HardFaults
        nrf_sdh_disable_request();
    
        //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));
    
          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);
              break;
          }
        }
        
        if(ret_val == NRF_SUCCESS)
        {
            //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));
    
            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_SUCCESS) NRF_LOG_INFO("Failed transferring bank1 overflow bytes.");
        }
        
        NRF_LOG_INFO("External flash transfer to BANK1 success.");
        // re-enable the SD when done
        nrf_sdh_enable_request();
        return ret_val;
    }

    Any ideas on what in this function could be messing with the settings writing?

Reply
  • UDPATE: I was able to get the update to work if I commented out the line right before the s_dfu_settings block.  This line was the piece of code that was actually doing the transferring of the code from external flash to Bank1, but since the FW image had already been transferred to Bank1 in previous calls, I could get away with commenting it out.  Obviously this won't work in production, so I'm trying to understand how this line might affect the settings writing.  The function l commented out was the external_flash_transfer_to_bank1():

    external_flash_transfer_to_bank1(test_fw_addr + CAM_UPDATE_HEADER_SIZE, scratchpad_data.app_len);
    
    //Update BL settings to signal a new valid app is ready
    s_dfu_settings.bank_1.bank_code                 = NRF_DFU_BANK_VALID_APP;
    s_dfu_settings.bank_0.bank_code                 = ......


    and the actual function looks like this:

    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;
    
        NRF_LOG_INFO("Transferring from external flash to BANK1");
    
        // fstorage shares resources with SD, temporarily disable SD to avoid HardFaults
        nrf_sdh_disable_request();
    
        //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));
    
          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);
              break;
          }
        }
        
        if(ret_val == NRF_SUCCESS)
        {
            //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));
    
            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_SUCCESS) NRF_LOG_INFO("Failed transferring bank1 overflow bytes.");
        }
        
        NRF_LOG_INFO("External flash transfer to BANK1 success.");
        // re-enable the SD when done
        nrf_sdh_enable_request();
        return ret_val;
    }

    Any ideas on what in this function could be messing with the settings writing?

Children
  • UPDATE:  I was able to get the settings written successfully. 

    I am using the SD backend for fstorage because I am using S140. From the SDK:

    [The SD] backend interfaces with the SoftDevice flash API to perform 
    operations in flash. It can be used any time the SoftDevice is present, 
    regardless of whether the SoftDevice is enabled or not. However, when 
    running a protocol stack, the success of flash operations is tied to the 
    timing constraints of the protocol and the characteristics of the hardware. 
    Using too aggressive scan or advertising intervals, or running multiple 
    connections, can influence the success of a flash operation.

    BUT I was only protecting the flash read/write/erases for my own internal flash transfers (by temporarily disabling the SD with nrf_sdh_disable_request()) and was re-enabling right after.  Because the sf_dfu_settings write was placed after this re-enabling, the SD was now interfering with the settings writing.  I changed my code such that I don't re-enable the SD until after all the flash functions were complete and this fixed everything.  Hope this might help someone else!

Related