Maku
Maku

Reputation: 199

Writing array to specific flash location in the .hex file

For my embedded application on Atmel SAM4E16C i need to place an array with firmware information at the end of the .hex file. I'm using Atmel Studio 7 with GCC.

I've already done this for Atmega168PB but somehow it doesn't work for this project.

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
SEARCH_DIR(.)

/* Memory Spaces Definitions */
MEMORY
{
  rom (rx)  : ORIGIN = 0x00420000, LENGTH = 0x000E0000 /* changed to leave space for 128KB Bootloader -> was ORIGIN = 0x00400000, LENGTH = 0x00100000 */
  ram (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00020000
}

/* The stack size used by the application. NOTE: you need to adjust according to your application. */
__stack_size__ = DEFINED(__stack_size__) ? __stack_size__ : 0x3000;
__ram_end__ = ORIGIN(ram) + LENGTH(ram) - 4;
/* Firmware Info - 8 Bytes long at the end of ROM */
__FWInfo_start__ = ORIGIN(rom) + LENGTH(rom) - 8;
SECTIONS
{
.text :
{
    . = ALIGN(4);
    _sfixed = .;
    KEEP(*(.vectors .vectors.*))
    *(.text .text.* .gnu.linkonce.t.*)
    *(.glue_7t) *(.glue_7)
    *(.rodata .rodata* .gnu.linkonce.r.*)
    *(.ARM.extab* .gnu.linkonce.armextab.*)

    /* Support C constructors, and C destructors in both user code
       and the C library. This also provides support for C++ code. */
    . = ALIGN(4);
    KEEP(*(.init))
    . = ALIGN(4);
    __preinit_array_start = .;
    KEEP (*(.preinit_array))
    __preinit_array_end = .;

    . = ALIGN(4);
    __init_array_start = .;
    KEEP (*(SORT(.init_array.*)))
    KEEP (*(.init_array))
    __init_array_end = .;

    . = ALIGN(0x4);
    KEEP (*crtbegin.o(.ctors))
    KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
    KEEP (*(SORT(.ctors.*)))
    KEEP (*crtend.o(.ctors))

    . = ALIGN(4);
    KEEP(*(.fini))

    . = ALIGN(4);
    __fini_array_start = .;
    KEEP (*(.fini_array))
    KEEP (*(SORT(.fini_array.*)))
    __fini_array_end = .;

    KEEP (*crtbegin.o(.dtors))
    KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
    KEEP (*(SORT(.dtors.*)))
    KEEP (*crtend.o(.dtors))

    . = ALIGN(4);
    _efixed = .;            /* End of text section */
} > rom

/* .ARM.exidx is sorted, so has to go in its own output section.  */
PROVIDE_HIDDEN (__exidx_start = .);
.ARM.exidx :
{
  *(.ARM.exidx* .gnu.linkonce.armexidx.*)
} > rom
PROVIDE_HIDDEN (__exidx_end = .);

. = ALIGN(4);
_etext = .;

.relocate : AT (_etext)
{
    . = ALIGN(4);
    _srelocate = .;
    *(.ramfunc .ramfunc.*);
    *(.data .data.*);
    . = ALIGN(4);
    _erelocate = .;
} > ram

/* .bss section which is used for uninitialized data */
.bss (NOLOAD) :
{
    . = ALIGN(4);
    _sbss = . ;
    _szero = .;
    *(.bss .bss.*)
    *(COMMON)
    . = ALIGN(4);
    _ebss = . ;
    _ezero = .;
} > ram

/* stack section */
.stack (NOLOAD):
{
    . = ALIGN(8);
    _sstack = .;
    . = . + __stack_size__;
    . = ALIGN(8);
    _estack = .;
} > ram

. = ALIGN(4);
_end = . ;


/* 8 Byte Firmware Info Section */

.FWInfo : AT (__FWInfo_start__)
{
    *(.FWInfo)
} > rom
}

This is the linker script i'm using. I've added the __FWInfo_start__ and the .FWInfo section.

In my application i tried to define the firmware info block with attribute section .FWInfo but i'm unable to locate the data in my .hex file.

#define SIZE_OF_FWINFO  8
const uint8_t nFirmwareInfoBlock[SIZE_OF_FWINFO] __attribute__((section(".FWInfo"))) = {
    0xff,   // reserved for future
    0xff,   // reserved for future
    DEVICE_TYPE,    // DeviceType
    BUILD_NR,       // BuildNr of Firmware
    VERSION_MINOR,  // VersionMinor of Firmware
    VERSION_MAJOR,  // VersionMajor of Firmware
    0xFF,           // Checksum
    0xFF            // Checksum
};  

I hope someone can help me why this isn't working. Thanks in advance.

EDIT: Here are the entries in the .map file:

.data          0x00000000        0x0 src/main.o
.FWInfo        0x00000000        0x8 src/main.o
.debug_macro   0x00000000      0x8b0 src/main.o

and..

 *fill*         0x200133b0     0x3000 
                0x200163b0                . = ALIGN (0x8)
                0x200163b0                _estack = .
                0x200163b0                . = ALIGN (0x4)
                0x200163b0                _end = .

.FWInfo
 *(.FWInfo)
OUTPUT(Dali4Net.elf elf32-littlearm)

as far as i can read from the context in the second block there should be an address written after the .FWInfo or?

Upvotes: 4

Views: 1968

Answers (3)

SMFSW
SMFSW

Reputation: 286

I know this post is pretty old, but anyways...

As already stated, the use of KEEP directive to keep the section even if unused may be needed, or just ensuring it will always be kept no matter what.

More than that, I wanted to do something similar for a STM32. It seems that the order of sections is important too (even when an address is explicitly given). I had to place my extra section just before the start of RAM sections.

Here, you have sections that goes to ROM > rom and RAM > ram. You may also have some other section of initialized datas which are denoted >RAM AT> FLASH (for STM32 gcc). In this example, it seems that it's denoted as .relocate : AT (_etext).

Anyways, if it works the same way, you should place your own section just before the .bss which is the start of uninitialized datas (first section related to RAM only):

...
    _erelocate = .;
} > ram

/* 8 Byte Firmware Info Section */
.FWInfo : AT (__FWInfo_start__)
{
    KEEP(*(.FWInfo))
} > rom

/* .bss section which is used for uninitialized data */
.bss (NOLOAD) :
...

If it still doesn't work as expected, you may try to place this section before the .relocate : AT (_etext) section; which is located after the latest mentioned rom section, but as this section is relocated from ROM to RAM even if ambigous in the notation, I think the first guess should work.

Upvotes: 0

Clifford
Clifford

Reputation: 93476

From GCC variable attribute documentation:

used

This attribute, attached to a variable with static storage, means that the variable must be emitted even if it appears that the variable is not referenced.

So to prevent the unused data being removed by the linker, use:

const uint8_t nFirmwareInfoBlock[SIZE_OF_FWINFO]
              __attribute__((used,section(".FWInfo"))) = 
{ ... } ;

An alternative but perhaps less attractive solution is to declare the array volatile and then perform a dummy read i.e.:

const uint8_t nFirmwareInfoBlock[SIZE_OF_FWINFO]
              __attribute__((section(".FWInfo"))) = 
{ ... } ;

int main()
{
    uint8_t dummy = nFirmwareInfoBlock[0] ; 
    ...
}

The third method is to avoid toolchain dependencies altogether and to use the excellent if somewhat arcane SRecord utility to patch the data directly to the hex file as a post-build operation. This has the advantage of being tool-chain independent, but you will need a step perhaps to generate the data to be patched in, but writing a generator for that is probably trivial.

Upvotes: 1

Julien
Julien

Reputation: 1910

From my experience in Atmel studio 6 and 8bit AVR core:

define the section .FWInfo (I do it without makefile sorry) Flash config And doing

const uint8_t nFirmwareInfoBlock[SIZE_OF_FWINFO]
              __attribute__((used,section(".FWInfo"))) = 
{ ... } ;

is not enough, because only the compiler will keep the variable. Then the linker may discard it if it is not used. Of course a simple reference is enough to keep it but if it only used in the bootloader for example you can define a linker flag.

Again I did it graphically (sorry).

linker flag

Upvotes: 0

Related Questions