4

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?

Maku
  • 199
  • 2
  • 15
  • @RawN what do you mean? – Maku Nov 23 '16 at 16:04
  • Ah I see, it's some sort of a linker script. You should consider adding the `atmel` or similar tag to your post. –  Nov 23 '16 at 16:10
  • Ok i added the tag, thought writing atmel in the beginning is enough. – Maku Nov 23 '16 at 16:14
  • Do you get a compiler/linker error? Have you looked for FWInfo in the map file? – kkrambo Nov 23 '16 at 19:47
  • I have only experience on 8bit AVR like your 168PB. But if `nFirmwareInfoBlock` is not used in FW, the linker optimizes away. I do not use makefile but atmel configuration, add .FWinfo to sections and there is an option I do not remember to disable optimization. Other solution is to put a dummy `pgm_read_word(nFirmwareInfoBlock)`. If this doesn't help there is a difference I don't know SAM VS AVR – Julien Nov 23 '16 at 21:02
  • @kkrambo I dont get any errors. I've added the section from the .map file to the post, was too long for comment. – Maku Nov 24 '16 at 08:24
  • @Julien Yes, thats exactly the way I did it on 168PB aswell. Tried it here but it didn't work that way.. – Maku Nov 24 '16 at 08:25

3 Answers3

1

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.

Community
  • 1
  • 1
Clifford
  • 88,407
  • 13
  • 85
  • 165
  • Thanks, didn't know the `used` attribute. I've used a dummy until now, and i am to print the content of the array, but it's stored at a random address in the flash rather than the one given.. – Maku Nov 24 '16 at 08:16
0

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

Julien
  • 1,810
  • 1
  • 16
  • 34
0

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.

SMFSW
  • 286
  • 1
  • 2
  • 10