Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs

Migrate from SES flash_placement.xml to ARM GCC linker (no init variables)

Hello,

I'm trying to transition from using the SES example projects to using the ARM GCC projects. While testing out code using the armgcc projects, I've noticed that placing variables in "non_init" section will not retain any values after pressing the reset button.

This is the snippet of code I'm using to test whether the variables are being initialized or not. It appears to work just fine when tested using the SES examples, but I cannot get it to work when I try switching to armgcc.

uint32_t testValue1 __attribute__((section(".retained_section")));
int main(void) {
	if (NRF_POWER->RESETREAS == 0) {
		testValue1 = 0x37;
	}
	else {
		NRF_LOG_INFO("Test values check %08x", testValue1);
	}
}

The flash_placements.xml file contains a field below which is what I'm assuming is allowing the no-initialization variables to work.

<ProgramSection alignment="4" load="No" name=".non_init" />	

However when I look within the armgcc linker script it doesn't appear to match with the flash_placements.xml file.

/* Linker script to configure memory regions. */

SEARCH_DIR(.)
GROUP(-lgcc -lc -lnosys)

MEMORY
{
  FLASH (rx) : ORIGIN = 0x27000, LENGTH = 0xd9000
  RAM (rwx) :  ORIGIN = 0x20002af8, LENGTH = 0x3d508
}

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_soc_observers :
  {
    PROVIDE(__start_sdh_soc_observers = .);
    KEEP(*(SORT(.sdh_soc_observers*)))
    PROVIDE(__stop_sdh_soc_observers = .);
  } > FLASH
  .sdh_ble_observers :
  {
    PROVIDE(__start_sdh_ble_observers = .);
    KEEP(*(SORT(.sdh_ble_observers*)))
    PROVIDE(__stop_sdh_ble_observers = .);
  } > FLASH
  .pwr_mgmt_data :
  {
    PROVIDE(__start_pwr_mgmt_data = .);
    KEEP(*(SORT(.pwr_mgmt_data*)))
    PROVIDE(__stop_pwr_mgmt_data = .);
  } > 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
  .sdh_stack_observers :
  {
    PROVIDE(__start_sdh_stack_observers = .);
    KEEP(*(SORT(.sdh_stack_observers*)))
    PROVIDE(__stop_sdh_stack_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
  .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"

I thought I could simply copy and paste the auto-generated ld file from SES, but it will not compile after making the change. 

Compiling file: main.cpp
Linking target: _build/nrf52840_xxaa.out
c:/program files (x86)/gnu arm embedded toolchain/9 2020-q2-update/bin/../lib/gcc/arm-none-eabi/9.3.1/../../../../arm-none-eabi/bin/ld.exe: _build/nrf52840_xxaa.out section `.fini' will not fit in region `UNPLACED_SECTIONS'
c:/program files (x86)/gnu arm embedded toolchain/9 2020-q2-update/bin/../lib/gcc/arm-none-eabi/9.3.1/../../../../arm-none-eabi/bin/ld.exe: region `UNPLACED_SECTIONS' overflowed by 5 bytes
c:/program files (x86)/gnu arm embedded toolchain/9 2020-q2-update/bin/../lib/gcc/arm-none-eabi/9.3.1/../../../../arm-none-eabi/bin/ld.exe: c:/program files (x86)/gnu arm embedded toolchain/9 2020-q2-update/bin/../lib/gcc/arm-none-eabi/9.3.1/../../../../arm-none-eabi/lib/thumb/v7e-m+fp/hard\libg_nano.a(lib_a-signalr.o): in function `_kill_r':
signalr.c:(.text._kill_r+0xe): undefined reference to `_kill'
c:/program files (x86)/gnu arm embedded toolchain/9 2020-q2-update/bin/../lib/gcc/arm-none-eabi/9.3.1/../../../../arm-none-eabi/bin/ld.exe: c:/program files (x86)/gnu arm embedded toolchain/9 2020-q2-update/bin/../lib/gcc/arm-none-eabi/9.3.1/../../../../arm-none-eabi/lib/thumb/v7e-m+fp/hard\libg_nano.a(lib_a-signalr.o): in function `_getpid_r':
signalr.c:(.text._getpid_r+0x0): undefined reference to `_getpid'
collect2.exe: error: ld returned 1 exit status

Why are the armgcc linker scripts not identical to what is generated for the SES projects? If I modify the linker script as shown in this thread the value will be retained. The thread isn't regarding nrf52840, and doesn't appear to be how the auto-generated ld file from SES does it so I'm not confident whether the thread is applicable for me.

 From NRF52 SES Flash Placement File (.xml) to GNU Linker script file (.ld) 

Parents
  • We have some other user needing noinit to behave deterministic, so they have something like below in their project linker file

    MEMORY
    {
      FLASH (rx) : ORIGIN = 0x26000, LENGTH = 0x5a000
      RAM (rwx) :  ORIGIN = 0x200018a8, LENGTH = 0xd758
      NO_INIT (rwx) :  ORIGIN = 0x2000f000, LENGTH = 0x1000
    }

    That worked for them. Can you also give this a try to add NO_INIT in your whole memory layout?

  • Yes I had modified it to include noinit, but the scope of my question has changed to include to the bigger picture for a project.

    My project is based on peripheral/ble_app_uart, with changes made to implement ble_app_buttonless_dfu and security from ble_app_gls.

    The only changes I've made to the memory layout so far have been these

    MEMORY
    {
      FLASH (rx) : ORIGIN = 0x27000, LENGTH = 0xD9000
      RAM (rwx) :  ORIGIN = 0x20002B58, LENGTH = 0x3D4A8
      NOINIT (rwx) : ORIGIN = 0x2000FF00, LENGTH = 0x100
      uicr_bootloader_start_address (r) : ORIGIN = 0x10001014, LENGTH = 0x4
    }
    
    SECTIONS
    {
      . = ALIGN(4);
      .noinit :
      {
        PROVIDE(__start_noinit = .);
        KEEP(*(SORT(.noinit*)))
        PROVIDE(__stop_noinit = .);
      } > NOINIT
    
      .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
    }

    While I have had noinit working, I'm still not convinced that the linker script is complete when compared to what is created for SES projects. For example, in SES, projects have values defined for Heap Size and Max Stack Size within project options, which are then embedded into the generated linker script. No such information is present within the armgcc projects which I've tried comparing with.

    I will keep trying to get the generated linker script to work with an example armgcc project since I think that's my best option

Reply
  • Yes I had modified it to include noinit, but the scope of my question has changed to include to the bigger picture for a project.

    My project is based on peripheral/ble_app_uart, with changes made to implement ble_app_buttonless_dfu and security from ble_app_gls.

    The only changes I've made to the memory layout so far have been these

    MEMORY
    {
      FLASH (rx) : ORIGIN = 0x27000, LENGTH = 0xD9000
      RAM (rwx) :  ORIGIN = 0x20002B58, LENGTH = 0x3D4A8
      NOINIT (rwx) : ORIGIN = 0x2000FF00, LENGTH = 0x100
      uicr_bootloader_start_address (r) : ORIGIN = 0x10001014, LENGTH = 0x4
    }
    
    SECTIONS
    {
      . = ALIGN(4);
      .noinit :
      {
        PROVIDE(__start_noinit = .);
        KEEP(*(SORT(.noinit*)))
        PROVIDE(__stop_noinit = .);
      } > NOINIT
    
      .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
    }

    While I have had noinit working, I'm still not convinced that the linker script is complete when compared to what is created for SES projects. For example, in SES, projects have values defined for Heap Size and Max Stack Size within project options, which are then embedded into the generated linker script. No such information is present within the armgcc projects which I've tried comparing with.

    I will keep trying to get the generated linker script to work with an example armgcc project since I think that's my best option

Children
  • Like I said, I do not think no effort has been put to map every default segger IDE settings into gcc linker/memory setting.

    But you can add these configs in your linker script (.ld) and also into your flash_placement.xml file like below

        <ProgramSection alignment="4" size="__HEAPSIZE__" load="No" name=".heap" />
        <ProgramSection alignment="8" size="__STACKSIZE__" load="No" place_from_segment_end="Yes" name=".stack"  address_symbol="__StackLimit" end_symbol="__StackTop"/>
        <ProgramSection alignment="8" size="__STACKSIZE_PROCESS__" load="No" name=".stack_process" />

    I see that you are trying to be cautious in trying to get every setting in SES you have ported to GCC which is very good, but from our end, I do think that no one did this comparison of checking that all IDE's have all default settings to be identical. Some of the default values are not used by some examples, like the heap. Examples does not necessarily use heap but the default values are there in some IDEs and not in some.

Related