我有一些我希望存储在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是对的,我们的配置在某个地方搞砸了。
答案 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 }