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

DFU linker settings in gcc

Dear All

We implement a DFU over USB functionality to the custom board nrf52840 board by compiling via gcc. Somehow there is my understanding problem with the linker for bootloader and app. Here is the important part of the secure_bootloader.ld linker file for the bootloader:

MEMORY
{
  FLASH (rx) : ORIGIN = 0xf4000, LENGTH = 0xa000
  RAM (rwx) :  ORIGIN = 0x20000008, LENGTH = 0x3fff8
  uicr_bootloader_start_address (r) : ORIGIN = 0x00000FF8, LENGTH = 0x4
  bootloader_settings_page (r) : ORIGIN = 0x000FF000, LENGTH = 0x1000
  uicr_mbr_params_page (r) : ORIGIN = 0x00000FFC, LENGTH = 0x4
  mbr_params_page (r) : ORIGIN = 0x000FE000, LENGTH = 0x1000
}

and here is the generic_gcc_nrf52.ld linker file's part for the memory:

MEMORY
{
  FLASH (rx) : ORIGIN = 0x26000, LENGTH = 0xca000
  RAM (rwx) :  ORIGIN = 0x20002220, LENGTH = 0x3dde0
  uicr_bootloader_start_address (r) : ORIGIN = 0x00000FF8, LENGTH = 0x4
}

All compiles well and mergehex complains that I am overlapping some regions. Clearly I have a lack of understanding, how to create the linker file, as I am trying to sort it out by looking at the examples and reading the blogs. I am using SDK 15.0.0. Here is my confusion and following some questions:

1. According to the memory map, MBR starts at 0, is the statement correct: mbr_params_page (r) : ORIGIN = 0x000FE000, LENGTH = 0x1000

2. Is including in the app linker file: uicr_bootloader_start_address (r) : ORIGIN = 0x00000FF8, LENGTH = 0x4 immportant?

3. Bootloader and settings are as follows:

Bootloader DFU Settings:
* File:                     bootsettings.hex
* Family:                   NRF52840
* Start Address:            0x000FF000
* CRC:                      0x5593BD4D
* Settings Version:         0x00000001 (1)
* App Version:              0x00000002 (2)
* Bootloader Version:       0x00000001 (1)
* Bank Layout:              0x00000000
* Current Bank:             0x00000000
* Application Size:         0x0002785C (161884 bytes)
* Application CRC:          0x55A80F33
* Bank0 Bank Code:          0x00000001
* Softdevice Size:          0x00000000 (0 bytes)
* Boot Validation CRC:      0x00000000
* SD Boot Validation Type:  0x00000000 (0)
* App Boot Validation Type: 0x00000000 (0)

Any idea why the mergehex complains about possible region overlapping when app and bootloader (and settings) are merged?

4. What are the changes regarding DFU and bootloader from 15.0 to 15.3, would changing to 15.3 resolve these issues?

Best, Emek

Parents
  • Hi Bilen, 

    1. According to the memory map, MBR starts at 0, is the statement correct: mbr_params_page (r) : ORIGIN = 0x000FE000, LENGTH = 0x1000

     It is correct that the MBR starts at 0x0000, but it also needs a flash page to store some magic numbers and keep track of pending bootloader copy operations. This flash page is placed at the end of flash, above the bootloader it self at 0x000FE000.

     

    2. Is including in the app linker file: uicr_bootloader_start_address (r) : ORIGIN = 0x00000FF8, LENGTH = 0x4 immportant?

     Yes, this places the bootloader start address within the MBR, i.e. tells the MBR where to jump to on boot if a BL is present. 

     

    Bootloader DFU Settings:
    * File:                     bootsettings.hex
    * Family:                   NRF52840
    * Start Address:            0x000FF000
    * CRC:                      0x5593BD4D
    * Settings Version:         0x00000001 (1)
    * App Version:              0x00000002 (2)
    * Bootloader Version:       0x00000001 (1)
    * Bank Layout:              0x00000000
    * Current Bank:             0x00000000
    * Application Size:         0x0002785C (161884 bytes)
    * Application CRC:          0x55A80F33
    * Bank0 Bank Code:          0x00000001
    * Softdevice Size:          0x00000000 (0 bytes)
    * Boot Validation CRC:      0x00000000
    * SD Boot Validation Type:  0x00000000 (0)
    * App Boot Validation Type: 0x00000000 (0)

    Any idea why the mergehex complains about possible region overlapping when app and bootloader (and settings) are merged?

     So are you getting complaints about overlapping sections when you merge the bootloader and bootloader settings OR when you merge the app with the already merged bootloader + bootlaoder settings hex? If its the latter, then can you post the linker script for your application? I would like to check if you have adjusted the application flash region to allow enough space for the bootloader. 

    4. What are the changes regarding DFU and bootloader from 15.0 to 15.3, would changing to 15.3 resolve these issues?

     I think the biggest change we did is that we're placing the bootloader start address and MBR params page address inside the MBR in addition to writing them to the UICR registers, see this https://devzone.nordicsemi.com/f/nordic-q-a/47651/uicr_mbr_params_page-and-uicr_bootloader_start_address-overwrite-the-beginning-of-the-softdevice.

    Best regards

    Bjørn

  • Hi Bjørn

    Many thanks for your prompt reply.

    Merging bootloader with settings work fine. The problem is in merging bootloder_and_settings with the app. In this case I am using the generic_gcc...ld file (see above) with the flash

    FLASH (rx) : ORIGIN = 0x26000, LENGTH = 0xca000

    such that it ends at 155648+827392 = 983040 byte, if I understand it correctly. Then the bootloader shall start, from secure_bootloader.ld (see above)

    FLASH (rx) : ORIGIN = 0xf4000, LENGTH = 0xa000

    meaning that it starts at 0xf4000 meaning 999424 byte of length 40960 byte. Is this way of thinking correct by simply converting hex/decimal?

    Here is the full linker file for app, we use :

    SEARCH_DIR(.)
    GROUP(-lgcc -lc -lnosys)
    
    MEMORY
    {
      FLASH (rx) : ORIGIN = 0x26000, LENGTH = 0xca000
      RAM (rwx) :  ORIGIN = 0x20002220, LENGTH = 0x3dde0
      uicr_bootloader_start_address (r) : ORIGIN = 0x00000FF8, LENGTH = 0x4
    }
    
    SECTIONS
    {
      . = ALIGN(4);
      .uicr_bootloader_start_address :
      {
        PROVIDE(__start_uicr_bootloader_start_address = .);
        KEEP(*(SORT(.uicr_bootloader_start_address*)))
        PROVIDE(__stop_uicr_bootloader_start_address = .);
      } > uicr_bootloader_start_address
    }
    
    SECTIONS
    {
      . = ALIGN(4);
      .mem_section_dummy_ram :
      {
      }
      .cli_sorted_cmd_ptrs :
      {
        PROVIDE(__start_cli_sorted_cmd_ptrs = .);
        KEEP(*(.cli_sorted_cmd_ptrs))
        PROVIDE(__stop_cli_sorted_cmd_ptrs = .);
      } > RAM
      .fs_data :
      {
        PROVIDE(__start_fs_data = .);
        KEEP(*(.fs_data))
        PROVIDE(__stop_fs_data = .);
      } > RAM
      .log_dynamic_data :
      {
        PROVIDE(__start_log_dynamic_data = .);
        KEEP(*(SORT(.log_dynamic_data*)))
        PROVIDE(__stop_log_dynamic_data = .);
      } > RAM
      .log_filter_data :
      {
        PROVIDE(__start_log_filter_data = .);
        KEEP(*(SORT(.log_filter_data*)))
        PROVIDE(__stop_log_filter_data = .);
      } > RAM
    
    } INSERT AFTER .data;
    
    SECTIONS
    {
      .mem_section_dummy_rom :
      {
      }
      .sdh_ble_observers :
      {
        PROVIDE(__start_sdh_ble_observers = .);
        KEEP(*(SORT(.sdh_ble_observers*)))
        PROVIDE(__stop_sdh_ble_observers = .);
      } > FLASH
      .sdh_soc_observers :
      {
        PROVIDE(__start_sdh_soc_observers = .);
        KEEP(*(SORT(.sdh_soc_observers*)))
        PROVIDE(__stop_sdh_soc_observers = .);
      } > FLASH
      .sdh_stack_observers :
      {
        PROVIDE(__start_sdh_stack_observers = .);
        KEEP(*(SORT(.sdh_stack_observers*)))
        PROVIDE(__stop_sdh_stack_observers = .);
      } > FLASH
      .sdh_req_observers :
      {
        PROVIDE(__start_sdh_req_observers = .);
        KEEP(*(SORT(.sdh_req_observers*)))
        PROVIDE(__stop_sdh_req_observers = .);
      } > FLASH
      .sdh_state_observers :
      {
        PROVIDE(__start_sdh_state_observers = .);
        KEEP(*(SORT(.sdh_state_observers*)))
        PROVIDE(__stop_sdh_state_observers = .);
      } > FLASH
        .nrf_queue :
      {
        PROVIDE(__start_nrf_queue = .);
        KEEP(*(.nrf_queue))
        PROVIDE(__stop_nrf_queue = .);
      } > FLASH
        .nrf_balloc :
      {
        PROVIDE(__start_nrf_balloc = .);
        KEEP(*(.nrf_balloc))
        PROVIDE(__stop_nrf_balloc = .);
      } > FLASH
        .cli_command :
      {
        PROVIDE(__start_cli_command = .);
        KEEP(*(.cli_command))
        PROVIDE(__stop_cli_command = .);
      } > FLASH
      .crypto_data :
      {
        PROVIDE(__start_crypto_data = .);
        KEEP(*(SORT(.crypto_data*)))
        PROVIDE(__stop_crypto_data = .);
      } > FLASH
      .pwr_mgmt_data :
      {
        PROVIDE(__start_pwr_mgmt_data = .);
        KEEP(*(SORT(.pwr_mgmt_data*)))
        PROVIDE(__stop_pwr_mgmt_data = .);
      } > FLASH
      .log_const_data :
      {
        PROVIDE(__start_log_const_data = .);
        KEEP(*(SORT(.log_const_data*)))
        PROVIDE(__stop_log_const_data = .);
      } > FLASH
      .log_backends :
      {
        PROVIDE(__start_log_backends = .);
        KEEP(*(SORT(.log_backends*)))
        PROVIDE(__stop_log_backends = .);
      } > FLASH
    
    } INSERT AFTER .text
    
    INCLUDE "nrf_common.ld"
    

    For merging and then flashing, we use the following lines:

    nrfutil settings generate --family NRF52840 --application _build/nrf52840_xxaa.hex --application-version 2 --bootloader-version 1 --bl-settings-version 1 bootsettings.hex
    mergehex -m _bootloader/nrf52840_xxaa.hex bootsettings.hex -o bootloader_and_settings.hex
    mergehex -m _build/nrf52840_xxaa.hex bootloader_and_settings.hex -o app_firmware.hex
    
    nrfjprog --family nrf52 --recover
    nrfjprog --family nrf52 --program sdk/nRF5SDK_15.0.0/components/softdevice/s140/hex/s140_nrf52_6.0.0_softdevice.hex --sectorerase
    nrfjprog --family nrf52 --program app_firmware.hex --sectoranduicrerase --reset

  • Hi Bjørn

    I did solve the mergehex problem by using some help from:

    https://github.com/IOsonata/IOsonata/blob/master/ARM/Nordic/nRF52/nRF52840/ldscript/

    and now I am using the following in the linker file of the app:

    MEMORY
    {
    	FLASH (rx) : ORIGIN = 0x26000, LENGTH = 0xda000
    	RAM (rwx) :  ORIGIN = 0x20002220, LENGTH = 0x3DDE0
    	NOINIT (rwx) :  ORIGIN = 0x20007F80, LENGTH = 0x80
    	BOOTLOADER_SETTINGS (rw) : ORIGIN = 0x000FF000, LENGTH = 0x1000
    }
    
    
    SECTIONS
    {
    	.bootloaderSettings(NOLOAD) :
    	{
    
    	} > BOOTLOADER_SETTINGS
    	.noinit(NOLOAD) :
    	{
    
    	} > NOINIT
    }

    The mergehex is not complaining anymore and I can flash as expected.

    Now I have this issue. After merging and flashing app+bootloader, the chip resets by opening first the boatloader and then starting the app, exactly as it should be. Then I use reset, but the bootloader never starts and it jumps directly to the app. I know this simply by using LEDs.

    I use the following restart methods (all not working):

    1. Hard reset, simply cutting off the power and back.

    2. Soft device reset, sd_nvic_SystemReset();

    3. Setting gp register and soft reset

     

    ret_code_t status = sd_power_gpregret_clr(0, 0xFF);
    
        if (status == NRF_SUCCESS)
         {
              status = sd_power_gpregret_set(0, BOOTLOADER_DFU_START);
         }
    
        if (status == NRF_SUCCESS)
         {
            nrf_pwr_mgmt_shutdown(NRF_PWR_MGMT_SHUTDOWN_GOTO_DFU);
         }

    Here are my concerns:

    --Apparently bootloader is a bit different than in a PC, it is not always starting, but we need to trigger it. Which part is doing this, may be MBR, then is the procedure above (please see the first post) somehow wrong that I overwrite MBR or some other part?

    --App's linker file has only the info of bootloader settings page, is this really enough? I wonder how the app knows the bootloader start address, here is the part from the nrf_bootloader_info.h that I am using

    #define BOOTLOADER_DFU_GPREGRET_MASK            (0xF8)      /**< Mask for GPGPREGRET bits used for the magic pattern written to GPREGRET register to signal between main app and DFU. */
    #define BOOTLOADER_DFU_GPREGRET                 (0xB0)      /**< Magic pattern written to GPREGRET register to signal between main app and DFU. The 3 lower bits are assumed to be used for signalling purposes.*/
    #define BOOTLOADER_DFU_START_BIT_MASK           (0x01)      /**< Bit mask to signal from main application to enter DFU mode using a buttonless service. */
    
    #define BOOTLOADER_DFU_GPREGRET2_MASK           (0xF8)      /**< Mask for GPGPREGRET2 bits used for the magic pattern written to GPREGRET2 register to signal between main app and DFU. */
    #define BOOTLOADER_DFU_GPREGRET2                (0xA8)      /**< Magic pattern written to GPREGRET2 register to signal between main app and DFU. The 3 lower bits are assumed to be used for signalling purposes.*/
    #define BOOTLOADER_DFU_SKIP_CRC_BIT_MASK        (0x01)      /**< Bit mask to signal from main application that CRC-check is not needed for image verification. */
    
    
    #define BOOTLOADER_DFU_START    (BOOTLOADER_DFU_GPREGRET | BOOTLOADER_DFU_START_BIT_MASK)      /**< Magic number to signal that bootloader should enter DFU mode because of signal from Buttonless DFU in main app.*/
    #define BOOTLOADER_DFU_SKIP_CRC (BOOTLOADER_DFU_GPREGRET2 | BOOTLOADER_DFU_SKIP_CRC_BIT_MASK)  /**< Magic number to signal that CRC can be skipped due to low power modes.*/
    

    --Is it possible to check the restart method 3 above? There are several different posts suggesting

    nrf_power_gpregret_set() over the sd_power_gpregret_set() or setting gpregret2 instead of gpregret.

    Which one is relly true or does it matter? I am using sdk 15.0 (with the patch you mentioned for 15.3 in the above link) and soft device s140 version 6.0.0 on a 52840 chip.

    Best, Emek

  • bilenemek said:

    Now I have this issue. After merging and flashing app+bootloader, the chip resets by opening first the boatloader and then starting the app, exactly as it should be. Then I use reset, but the bootloader never starts and it jumps directly to the app. I know this simply by using LEDs.

    I use the following restart methods (all not working):

    1. Hard reset, simply cutting off the power and back.

    2. Soft device reset, sd_nvic_SystemReset();

    3. Setting gp register and soft reset

     The MBR will __always__  jump to the bootloader if the bootloader start address is written to UICR. So in all the above scenarios you should enter the bootloaders main(). Where in the bootloader code are you setting the LED? 

     

    bilenemek said:
    --Apparently bootloader is a bit different than in a PC, it is not always starting, but we need to trigger it. Which part is doing this, may be MBR, then is the procedure above (please see the first post) somehow wrong that I overwrite MBR or some other part?

     As written above, the MBR will always jump to the bootloader's vector table when the bootloader start address is written to UICR. 

     

    bilenemek said:
    --App's linker file has only the info of bootloader settings page, is this really enough? I wonder how the app knows the bootloader start address, here is the part from the nrf_bootloader_info.h that I am using

     The application does not need to know the bootloader start address as its the MBR that performs the jump on boot. The only thing the application needs to do is to signal the bootloader to stay in the bootloader and not jump to the application when  you nwat to update the firmware, e.g. by writting to the GPREGRET registers. 

     

    bilenemek said:

    --Is it possible to check the restart method 3 above? There are several different posts suggesting

    nrf_power_gpregret_set() over the sd_power_gpregret_set() or setting gpregret2 instead of gpregret.

    Which one is relly true or does it matter? I am using sdk 15.0 (with the patch you mentioned for 15.3 in the above link) and soft device s140 version 6.0.0 on a 52840 chip.

    Yes, you should be able to run the bootloader in a debug session when you have the applicaiton and bl settings file flashed. Pressing start execution should cause the BL to jump to the application. After you can see the nRF advertising as the applicaiton you can then set a breakpoint in the BL main and then trigger the jump from app to bootloader. You should then see that you enter the bootloader main and dfu_enter_check() should evaluate to true. 

    The bootloader only checks GPREGRET, not GPREGRET2. Make sure that the GPREGRET enter mode is enabled in the sdk_config.h file in the bootloader, e.g. NRF_BL_DFU_ENTER_METHOD_GPREGRET

    The patch for SDK v15.3.0 does not need to be applied in SDK v15.0.0 as the bootloader does not write the mbr params page address and the bootloader start address to the MBR region, only the UICR registers.

     Best regards

  • Thanks a lot for the clear explanation! Which part/command is writing the bootloader start address to the UICR? Is it the app (itself or its linker file) or flashing bootloader+settings? As the first time after flashing bootloader starts, I assume that the app is deleting this information or erase the whole uicr. Is there any way to erase uicr or bootloader address from it by using the app?

    I am using LEDs and not nrf logging (if I want to enable logging in the sdk, the linker complains that the size of the bootloader is bigger than the space reserved for it). LED triggering is very basic and directly in the very beginning of the main in the bootloader, please ignore the MBR part, now I know that I can clean it up, as follows:

    int main(void)
    {
    
        nrf_gpio_cfg_output(RGB_BLUE_PIN);
        nrf_gpio_pin_write(RGB_BLUE_PIN, HIGH);
    
        uint32_t ret_val;
    
        // Write bootloader start address to the MBR
        ret_val = nrf_bootloader_write_bl_addr_to_mbr();
        APP_ERROR_CHECK(ret_val);
        // Protect MBR and bootloader code from being overwritten.
        ret_val = nrf_bootloader_flash_protect(0, MBR_SIZE, false);
        APP_ERROR_CHECK(ret_val);
        ret_val = nrf_bootloader_flash_protect(BOOTLOADER_START_ADDR, BOOTLOADER_SIZE, false);
        APP_ERROR_CHECK(ret_val);
    
        (void) NRF_LOG_INIT(app_timer_cnt_get);
        NRF_LOG_DEFAULT_BACKENDS_INIT();
        
    
        ret_val = nrf_bootloader_init(dfu_observer);
        APP_ERROR_CHECK(ret_val);
    
        // Either there was no DFU functionality enabled in this project or the DFU module detected
        // no ongoing DFU operation and found a valid main application.
        // Boot the main application.
        nrf_bootloader_app_start();
    
        // Should never be reached.
        NRF_LOG_INFO("After main");
    }

  • bilenemek said:
    Which part/command is writing the bootloader start address to the UICR? Is it the app (itself or its linker file) or flashing bootloader+settings?

     The bootloader start address is placed in the UICR register at address 0x10001014 in the bootloader project. THis is linked in to the bootloader hex so the bootloader start address is written to the UICR register when you flash the bootloader hex. 

    bilenemek said:
    As the first time after flashing bootloader starts, I assume that the app is deleting this information or erase the whole uicr. Is there any way to erase uicr or bootloader address from it by using the app?

    No, the buttonless DFU example we have does not erase the UICR, otherwise the bootloader would not run on every boot. The application should at not really erase the UICR, but it is possible to erase the UICR at runtime by writting to the ERASEUICR register in the NVMC peripheral. 

     

    bilenemek said:
    I am using LEDs and not nrf logging (if I want to enable logging in the sdk, the linker complains that the size of the bootloader is bigger than the space reserved for it). LED triggering is very basic and directly in the very beginning of the main in the bootloader, please ignore the MBR part, now I know that I can clean it up, as follows:

     Ok, and you're not seeing the LED light up? Is the same GPIO that is connected to the LED used in the applicaiton? Could it be that the jump from bootloader to application happens so fast that you're not able to register it ? If so you should be able to check this with a logic analyzer or oscilloscope.

    Best regards

    Bjørn

  • Thanks, now I did understand, nrf_bootloader_info.h holds the info about the starting address. I was erasing UICR by correcting the voltage regulation, I do not know how to do this without NRF_NVMC->ERASEUICR but this is a subject of another topic.

    Everything is running as expected.

    To your question about LEDs. The bootloader is fast, but slower than 50 ms (eye can see 20 frames per second), so it is a good indicator, at least for testing.

Reply
  • Thanks, now I did understand, nrf_bootloader_info.h holds the info about the starting address. I was erasing UICR by correcting the voltage regulation, I do not know how to do this without NRF_NVMC->ERASEUICR but this is a subject of another topic.

    Everything is running as expected.

    To your question about LEDs. The bootloader is fast, but slower than 50 ms (eye can see 20 frames per second), so it is a good indicator, at least for testing.

Children
Related