【问题标题】:Locating the ROM address of a RAM variable定位 RAM 变量的 ROM 地址
【发布时间】:2018-07-12 14:06:26
【问题描述】:

我想知道是否有办法获取用于为 RAM 变量的初始值播种的 ROM 地址?给定一个声明static uint32_t foo = 0x12345678;,初始值0x12345678作为ROM存在于某处,作为&foo的初始值。在某个时间点,我希望能够将 foo 的值重置为其初始状态。

我可以创建第二个变量 const static uint32_t initial_foo = 0x12345678; 来使用,但这会使存储此用例变量所需的 ROM 数据空间增加一倍。

从 RAM 开头的偏移量 &foo(或更具体地讲 &_srelocate,请参阅下面的链接描述文件)能否与 ROM 空间中的符号之一可靠相关?

ARM/GNU C 链接器 v 6.3.1 脚本的选定部分如下。

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

/* Memory Spaces Definitions */
MEMORY
{
  rom (rx)  : 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;

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 and stack sections removed */

    . = ALIGN(4);
    _end = . ;
}

【问题讨论】:

  • 如果只写赋值foo = 0x12345678;,编译器不会以最高效的方式重新初始化变量吗?
  • 您可以通过链接描述文件从 ROM 中获取变量的地址,并在运行时对 RAM 执行memcpy。毫无疑问,Lundin 的答案更简单、更快捷。

标签: c linker arm embedded


【解决方案1】:

不要有一个 RAM 变量type foo = value;,只需创建一个变量const type foo = value; 并确保它分配在 ROM 中(如果变量具有静态存储持续时间,则应该是这种情况)。然后根据需要手动将其复制到 RAM。这样该值仅在 ROM 中存储一次; ROM 变量的初始化器不单独存储。

这样您就不会浪费任何内存,也不必担心以某种方式在 .rodata 中搜索初始化值,这将是相当有问题的做法。

【讨论】:

  • 是的,好点。基于 RAM 的变量的默认值可以不指定,并在模块初始化或重置期间从 ROM 显式复制。
  • 复制该值的代码将需要更多的 ROM 空间,而不是将该值存储两次(初始化器加上 const var)。更好地使用宏作为值,一个初始化程序(如果您有超过 2 个或 3 个变量,这非常有效),如果您必须“重置”该值,只需分配它。这避免了值的冗余,并让编译器决定如何进行赋值。
  • 上面的uint32_t 示例只是作为示例。更复杂的struct 类型会消耗更多内存,从而避免不必要的副本。我认为 ARM/GNU C(或任何)编译器不会将宏文字的重复使用解析为相同的内存地址,因此每次使用都会产生 ROM 副本。例如,使用 50 个 "foo" 文字,我上次检查时在 ROM 中产生了 50 个 "foo" 实例。
  • @mje:是的,这有点复杂。然而,如果没有更多信息,这可能是一个 XY 问题。拥有大量全局(非const)变量通常不是一个好习惯。
  • @Olaf 不,一个额外的初始化程序自然存储在 ROM 中,因为那毫无意义。这不是我见过的任何基于 ROM 的系统上生成代码的方式。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-14
  • 1970-01-01
  • 2018-04-08
相关资源
最近更新 更多