将数组写入 .hex 文件中的特定闪存位置

Posted

技术标签:

【中文标题】将数组写入 .hex 文件中的特定闪存位置【英文标题】:Writing array to specific flash location in the .hex file 【发布时间】:2016-11-23 15:56:53 【问题描述】:

对于我在 Atmel SAM4E16C 上的嵌入式应用程序,我需要在 .hex 文件的末尾放置一个带有固件信息的阵列。我正在使用带有 GCC 的 Atmel Studio 7。

我已经为 Atmega168PB 做过这个,但不知何故它不适用于这个项目。

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

这是我正在使用的链接器脚本。我添加了 __FWInfo_start__ 和 .FWInfo 部分。

在我的应用程序中,我尝试使用属性部分 .FWInfo 定义固件信息块,但我无法在我的 .hex 文件中找到数据。

#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
;  

我希望有人可以帮助我为什么这不起作用。提前致谢。

编辑:以下是 .map 文件中的条目:

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

和..

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

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

据我所知,第二个块中的上下文应该有一个写在 .FWInfo 或之后的地址?

【问题讨论】:

@RawN 你是什么意思? 啊,我明白了,它是某种链接器脚本。您应该考虑在帖子中添加atmel 或类似标签。 好吧,我加了标签,觉得开头写atmel就够了。 您是否收到编译器/链接器错误?您是否在地图文件中查找过 FWInfo? 我只有像你的 168PB 这样的 8bit AVR 的经验。但是如果nFirmwareInfoBlock 没有在固件中使用,链接器会优化掉。我不使用 makefile 而是使用 atmel 配置,将 .FWinfo 添加到部分,并且有一个我不记得禁用优化的选项。其他解决方案是放置一个假人pgm_read_word(nFirmwareInfoBlock)。如果这没有帮助,我不知道 SAM VS AVR 有什么区别 【参考方案1】:

来自 GCC variable attribute documentation:

used

此属性附加到具有静态存储的变量,意味着即使看起来未引用该变量,也必须发出该变量。

因此,为了防止链接器删除未使用的数据,请使用:

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

另一种可能不太吸引人的解决方案是声明数组volatile,然后执行虚拟读取,即:

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

int main()

    uint8_t dummy = nFirmwareInfoBlock[0] ; 
    ...

第三种方法是完全避免工具链依赖,并使用优秀的(虽然有点神秘)SRecord 实用程序将数据直接修补到 hex 文件作为 构建后 操作。这具有独立于工具链的优点,但您可能需要一个步骤来生成要修补的数据,但为此编写生成器可能很简单。

【讨论】:

谢谢,不知道used 属性。到目前为止,我一直使用一个虚拟对象,我要打印数组的内容,但它存储在闪存中的随机地址而不是给定的地址..【参考方案2】:

根据我在 Atmel studio 6 和 8bit AVR 内核的经验:

定义.FWInfo部分(对不起,我没有makefile) 并做

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

是不够的,因为只有编译器会保留变量。然后,如果不使用,链接器可能会丢弃它。当然,一个简单的引用就足以保留它,但如果它仅在引导加载程序中使用,您可以定义一个链接器标志。

我再次以图形方式完成(抱歉)。

【讨论】:

【参考方案3】:

我知道这篇文章很老了,但无论如何......

如前所述,即使可能需要使用 KEEP 指令来保留未使用的部分,或者只是确保无论如何都将始终保留它。

不仅如此,我还想为 STM32 做类似的事情。 似乎部分的顺序也很重要(即使明确给出了地址)。 我必须在 RAM 部分的开始之前放置我的额外部分。

在这里,您有进入 ROM > rom 和 RAM > ram 的部分。您可能还有一些其他部分的初始化数据,表示为>RAM AT> FLASH(对于 STM32 gcc)。在这个例子中,它似乎被表示为.relocate : AT (_etext)

无论如何,如果它的工作方式相同,您应该将自己的部分放在 .bss 之前,这是未初始化数据的开始(第一部分仅与 RAM 相关):

...
    _erelocate = .;
 > ram

/* 8 Byte Firmware Info Section */
.FWInfo : AT (__FWInfo_start__)

    KEEP(*(.FWInfo))
 > rom

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

如果仍然无法正常工作,您可以尝试将此部分放在.relocate : AT (_etext) 部分之前;它位于最新提到的 rom 部分之后,但由于该部分已从 ROM 重新定位到 RAM,即使符号不明确,我认为第一个猜测应该可行。

【讨论】:

以上是关于将数组写入 .hex 文件中的特定闪存位置的主要内容,如果未能解决你的问题,请参考以下文章

我是否可以使用bat脚本从一个闪存驱动器将文件夹安装到具有特定名称并更改驱动器的另一个闪存驱动器

stm32 flash半页写入

将小块数据写入闪存页面

arduino 闪存阵列

STM32L476 flash 页擦除没有效果

无法使用 Xamarin Android/Uwp 在 USB 闪存驱动器中写入文件