将结构存储在ARM设备的ROM中

时间:2011-02-10 21:16:26

标签: c++ memory arm

我有一些我希望存储在ROM中的常量数据,因为它有相当多的数据,我正在使用内存受限的ARM7嵌入式设备。我试图使用看起来像这样的结构来做到这一点:

struct objdef
{
   int x;
   int y;
   bool (*function_ptr)(int);
   some_other_struct * const struct_array; // array of similar structures
   const void* vp; // previously ommittted to shorten code
}

然后我创建并初始化为globals:

const objdef def_instance = { 2, 3, function, array, NULL };

然而,尽管开始时const,但这会占用相当多的RAM。更具体地说,它显着增加了RW数据量,并最终导致设备在创建足够实例时锁定。

我正在使用uVision和ARM编译器,以及RTX实时内核。

有人知道为什么这不起作用或知道在ROM中存储结构化异构数据的更好方法吗?

更新

谢谢大家的回答,并且对于不早点回复你们的道歉。所以这是迄今为止的分数和我的一些额外观察。

可悲的是,__attribute__对RAM与ROM的影响为零,static const也是如此。我还没有时间尝试装配路线。

我的同事和我发现了一些更不寻常的行为。

首先,我必须注意,为了简单起见,我没有提到我的objdef结构包含const void*字段。有时会为字段分配一个定义为

的字符串表中的值
char const * const string_table [ROWS][COLS] =
{
  { "row1_1", "row1_2", "row1_3" },
  { "row2_1", "row2_2", "row2_3" },
 ...
}

const objdef def_instance = { 2, 3, function, array, NULL };//->ROM
const objdef def_instance = { 2, 3, function, array, string_table[0][0] };//->RAM

string_table按预期在ROM中。这里是踢球者:objdef的实例被放入ROM中,直到string_table中的一个值被分配给该const void*字段。之后,struct实例被移动到RAM。

但是当string_table更改为

char const string_table [ROWS][COLS][MAX_CHARS] =
{
  { "row1_1", "row1_2", "row1_3" },
  { "row2_1", "row2_2", "row2_3" },
 ...
}

const objdef def_instance = { 2, 3,function, array, NULL };//->ROM
const objdef def_instance = { 2, 3, function, array, string_table[0][0] };//->ROM

尽管objdef分配了const void*的{​​{1}}个实例。我不知道为什么这很重要。

我开始怀疑Dan是对的,我们的配置在某个地方搞砸了。

5 个答案:

答案 0 :(得分:2)

你的想法是正确和合理的。我使用过Keil / uVision(这可能是3年前的v3?)并且它总是按照你的预期工作,即它将const数据放入flash / ROM中。

我怀疑你的链接器配置/脚本。我会尝试回到我以前的工作&看看我如何配置它。我没有必要添加#pragma__attribute__指令,我只是将它放在.const&闪存/ ROM中的.text。我很久以前就设置了链接器配置/内存映射,所以不幸的是,我的回忆并不是很新鲜。

(对于那些谈论铸造和常量指针的人来说,我有点困惑......你没有问过任何关于它的事情,你似乎明白“const”是如何工作的。你想放置闪存/ ROM中的初始化数据用于保存RAM(启动时不是ROM-> RAM拷贝),更不用说启动时的轻微加速,对吧?你不是在问它是否可以改变它或者其他什么...... )

编辑/更新:

我刚注意到你的(const)结构中的最后一个字段是some_other_struct * const(指向some_other_struct的常量指针)。您可能希望尝试将其作为指向常量 some_other_struct [some_other_struct const * const]的(常量)指针(假设它指向的内容确实是常量)。在这种情况下,它可能只是工作。我不记得具体细节(请参阅这里的主题?),但这似乎开始变得熟悉了。即使您的指针目标不是const项目,并且最终无法执行此操作,请尝试更改结构定义&用一个指向const的指针初始化它,看看是否将它放入ROM中。即使你把它作为一个const指针并且一旦构建了它就不能改变,我似乎记得一些东西,如果目标不是const,链接器不认为它可以完全在链接时间初始化&将初始化推迟到执行C运行时启动代码时,包括初始化RW存储器的ROM到RAM副本。

答案 1 :(得分:1)

您可以随时尝试使用汇编语言。

使用DATA语句输入信息并发布(公开)数据的起始地址。

根据我的经验,大型只读数据在源文件中声明为static const。源文件中的简单全局函数将返回数据的地址。

答案 2 :(得分:1)

我假设您有一个分离RAM和ROM部分的scatterfile。你想要做的是用一个属性指定你的结构,放置它将放置在哪个部分,或者将它放在它自己的目标文件中,然后在你希望它在scatterfile中的部分中指定。

__attribute__((section("ROM"))) const objdef def_instance = { 2, 3, function, array };

C“const”关键字实际上并不会导致编译器在text或const部分中放置某些内容。它只允许编译器警告您尝试修改它。获取指向const对象的指针,将其转换为非const并写入它是完全有效的,编译器需要支持它。

答案 3 :(得分:1)

如果你在ARM上做的事情,你可能正在使用ELF二进制格式。 ELF文件包含许多部分,但是常量数据应该进入ELF二进制文件的.rodata或.text部分。您应该可以使用GNU实用程序readelf或RVCT实用程序fromelf进行检查。

现在假设您的符号发现自己位于elf文件的正确部分,您现在需要了解RTX加载程序如何完成其​​工作。实例也没有理由不能共享相同的只读内存,但这取决于加载器。如果可执行文件存储在rom中,它可以就地运行但仍可以加载到RAM中。这也取决于装载机。

答案 4 :(得分:0)

一个完整的例子本来是最好的。如果我采取这样的事情:

typedef struct
{
    char a;
    char b;
} some_other_struct;

struct objdef
{
   int x;
   int y;
   const some_other_struct * struct_array;
};

typedef struct
{
   int x;
   int y;
   const some_other_struct * struct_array;
} tobjdef;


const some_other_struct def_other = {4,5};
const struct objdef def_instance = { 2, 3, &def_other};
const tobjdef tdef_instance = { 2, 3, &def_other};

unsigned int read_write=7;

使用最新的codesourcery lite

编译它
arm-none-linux-gnueabi-gcc -S struct.c

我得到了

    .arch armv5te
    .fpu softvfp
    .eabi_attribute 20, 1
    .eabi_attribute 21, 1
    .eabi_attribute 23, 3
    .eabi_attribute 24, 1
    .eabi_attribute 25, 1
    .eabi_attribute 26, 2
    .eabi_attribute 30, 6
    .eabi_attribute 18, 4
    .file   "struct.c"
    .global def_other
    .section    .rodata
    .align  2
    .type   def_other, %object
    .size   def_other, 2
def_other:
    .byte   4
    .byte   5
    .global def_instance
    .align  2
    .type   def_instance, %object
    .size   def_instance, 12
def_instance:
    .word   2
    .word   3
    .word   def_other
    .global tdef_instance
    .align  2
    .type   tdef_instance, %object
    .size   tdef_instance, 12
tdef_instance:
    .word   2
    .word   3
    .word   def_other
    .global read_write
    .data
    .align  2
    .type   read_write, %object
    .size   read_write, 4
read_write:
    .word   7
    .ident  "GCC: (Sourcery G++ Lite 2010.09-50) 4.5.1"
    .section    .note.GNU-stack,"",%progbits

标记为.rodata的部分,我认为是理想的。然后由链接器脚本来确保将ro数据放入rom中。请注意,read_write变量是在从.rodata切换到.data之后读取/写入的。

所以要使它成为一个完整的二进制文件并查看它是否放在rom或ram(.text或.data)中,那么

的start.s


.globl _start
_start:
    b   reset
    b   hang
    b   hang
    b   hang

    b   hang
    b   hang
    b   hang
    b   hang

reset:
hang: b hang

然后

# arm-none-linux-gnueabi-gcc -c -o struct.o struct.c
# arm-none-linux-gnueabi-as -o start.o start.s
# arm-none-linux-gnueabi-ld -Ttext=0 -Tdata=0x1000 start.o struct.o -o struct.elf
# arm-none-linux-gnueabi-objdump -D struct.elf > struct.list

我们得到了


Disassembly of section .text:

00000000 <_start>:
   0:   ea000006    b   20 <reset>
   4:   ea000008    b   2c <hang>
   8:   ea000007    b   2c <hang>
   c:   ea000006    b   2c <hang>
  10:   ea000005    b   2c <hang>
  14:   ea000004    b   2c <hang>
  18:   ea000003    b   2c <hang>
  1c:   ea000002    b   2c <hang>

00000020 <reset>:
  20:   e59f0008    ldr r0, [pc, #8]    ; 30 <hang+0x4>
  24:   e5901000    ldr r1, [r0]
  28:   e5801000    str r1, [r0]

0000002c <hang>:
  2c:   eafffffe    b   2c <hang>
  30:   00001000    andeq   r1, r0, r0

Disassembly of section .data:

00001000 <read_write>:
    1000:   00000007    andeq   r0, r0, r7

Disassembly of section .rodata:

00000034 <def_other>:
  34:   00000504    andeq   r0, r0, r4, lsl #10

00000038 <def_instance>:
  38:   00000002    andeq   r0, r0, r2
  3c:   00000003    andeq   r0, r0, r3
  40:   00000034    andeq   r0, r0, r4, lsr r0

00000044 <tdef_instance>:
  44:   00000002    andeq   r0, r0, r2
  48:   00000003    andeq   r0, r0, r3
  4c:   00000034    andeq   r0, r0, r4, lsr r0

这达到了预期的效果。 read_write变量在ram中,结构在rom中。需要确保两个const声明都在正确的位置,编译器没有给出关于将const放在指向另一个结构的指针上的警告,它可能在编译时不能确定为const,甚至所有这些都得到了链接器脚本(如果使用一个)按需工作可能需要一些努力。例如,这个似乎有效:


MEMORY
{
    bob(RX)   : ORIGIN = 0x0000000, LENGTH = 0x8000
    ted(WAIL) : ORIGIN = 0x2000000, LENGTH = 0x8000
}

SECTIONS
{
    .text : { *(.text*) } > bob
    .data : { *(.data*) } > ted
}