在 ARM 设备上的 ROM 中存储结构
我想将一些常量数据存储在 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
}
然后我将其创建并初始化为全局变量:
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*
字段。之后,结构体实例被移至 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*
分配,它们仍被放置在 ROM 中。我不知道为什么这很重要。
我开始怀疑丹是对的,我们的配置在某个地方搞砸了。
I have some constant data that I want to store in ROM since there is a fair amount of it and I'm working with a memory-constrained ARM7 embedded device. I'm trying to do this using structures that look something like this:
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
}
which I then create and initialize as globals:
const objdef def_instance = { 2, 3, function, array, NULL };
However, this eats up quite a bit of RAM despite the const
at the beginning. More specifically, it significantly increases the amount of RW data and eventually causes the device to lock up if enough instances are created.
I'm using uVision and the ARM compiler, along with the RTX real-time kernel.
Does anybody know why this doesn't work or know a better way to store structured heterogenous data in ROM?
Update
Thank you all for your answers and my apologies for not getting back to you guys earlier. So here is the score so far and some additional observations on my part.
Sadly, __attribute__
has zero effect on RAM vs ROM and the same goes for static const
. I haven't had time to try the assembly route yet.
My coworkers and I have discovered some more unusual behavior, though.
First, I must note that for the sake of simplicity I did not mention that my objdef
structure contains a const void*
field. The field is sometimes assigned a value from a string table defined as
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
is in ROM as expected. And here's the kicker: instances of objdef
get put in ROM until one of the values in string_table
is assigned to that const void*
field. After that the struct instance is moved to RAM.
But when string_table
is changed to
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
those instances of objdef
are placed in ROM despite that const void*
assigment. I have no idea why this should matter.
I'm beginning to suspect that Dan is right and that our configuration is messed up somewhere.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
我假设您有一个分隔 RAM 和 ROM 部分的分散文件。您想要做的是使用属性来指定您的结构将放置在哪个部分,或者将其放置在它自己的目标文件中,然后在分散文件中指定您希望它位于的部分。
C“const”关键字并不会真正导致编译器在文本或 const 部分中放入某些内容。它只允许编译器警告您尝试修改它。获取指向 const 对象的指针,将其转换为非常量对象,然后写入它是完全有效的,并且编译器需要支持这一点。
I assume you have a scatterfile that separates your RAM and ROM sections. What you want to do is to specify your structure with an attribute for what section it will be placed, or to place this in its own object file and then specify that in the section you want it to be in the scatterfile.
The C "const" keyword doesn't really cause the compiler to put something in the text or const section. It only allows the compiler to warn you of attempts to modify it. It's perfectly valid to get a pointer to a const object, cast it to a non-const, and write to it, and the compiler needs to support that.
你的想法是正确的、合理的。我使用过 Keil / uVision(这是 v3,也许是 3 年前?)并且它总是按照您期望的方式工作,即将 const 数据放入闪存/ROM 中。
我怀疑你的链接器配置/脚本。我会尝试回到我以前的工作&看看我是如何配置的。我不必添加
#pragma
或__attribute__
指令,我只是将其放置在.const
& 中。.text
在闪存/ROM 中。我很久以前就设置了链接器配置/内存映射,所以不幸的是,我的回忆不是很新鲜。(我对那些谈论强制转换和 const 指针等的人有点困惑......你没有问任何关于这个的事情,你似乎理解“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,则链接器不会认为它是 const可以在链接时完全初始化将初始化推迟到执行 C 运行时启动代码时,包括。初始化的 RW 存储器的 ROM 到 RAM 的副本。Your thinking is correct and reasonable. I've used Keil / uVision (this was v3, maybe 3 years ago?) and it always worked how you expected it to, i.e. it put const data in flash/ROM.
I'd suspect your linker configuration / script. I'll try to go back to my old work & see how I had it configured. I didn't have to add
#pragma
or__attribute__
directives, I just had it place.const
&.text
in flash/ROM. I set up the linker configuration / memory map quite a while ago, so unfortunately, my recall isn't very fresh.(I'm a bit confused by people who are talking about casting & const pointers, etc... You didn't ask anything about that & you seem to understand how "const" works. You want to place the initialized data in flash/ROM to save RAM (not ROM->RAM copy at startup), not to mention a slight speedup at boot time, right? You're not asking if it's possible to change it or whatever...)
EDIT / UPDATE:
I just noticed the last field in your (const) struct is a
some_other_struct * const
(constant pointer to a some_other_struct). You might want to try making it a (constant) pointer to a constant some_other_struct [some_other_struct const * const
] (assuming what it points to is indeed constant). In that case it might just work. I don't remember the specifics (see a theme here?), but this is starting to seem familiar. Even if your pointer target isn't a const item, and you can't eventually do this, try changing the struct definition & initializing it w/ a pointer to const and just see if that drops it into ROM. Even though you have it as aconst
pointer and it can't change once the structure is built, I seem to remember something where if the target isn't also const, the linker doesn't think it can be fully initialized at link time & defers the initialization to when the C runtime startup code is executed, incl. the ROM to RAM copy of initialized RW memory.您始终可以尝试使用汇编语言。
使用
DATA
语句放入信息并发布(公开)数据的起始地址。根据我的经验,大型只读数据在源文件中声明为
static const
。源文件中的一个简单的全局函数将返回数据的地址。You could always try using assembly language.
Put in the information using
DATA
statements and publish (make public) the starting addresses of the data.In my experience, large Read-Only data was declared in a source file as
static const
. A simple global function inside the source file would return the address of the data.如果您在 ARM 上做一些事情,您可能会使用 ELF 二进制格式。 ELF 文件包含许多部分,但常量数据应该进入 ELF 二进制文件的 .rodata 或 .text 部分。您应该能够使用 GNU 实用程序 readelf 或 RVCT 实用程序 fromelf 来检查这一点。
现在假设您的符号发现自己位于 elf 文件的正确部分,您现在需要了解 RTX 加载程序如何完成其工作。实例也没有理由不能共享相同的只读内存,但这将取决于加载程序。如果可执行文件存储在 ROM 中,它可能会就地运行,但仍可能加载到 RAM 中。这也取决于装载机。
If you are doing stuff on ARM you are probably using the ELF binary format. ELF files contain an number of sections but constant data should find its way into .rodata or .text sections of the ELF binary. You should be able to check this with the GNU utility
readelf
or the RVCT utilityfromelf
.Now assuming you symbols find themselves in the correct part of the elf file, you now need to find out how the RTX loader does its job. There is also no reason why the instances cannot share the same read only memory but this will depend on the loader. If the executable is stored in the rom, it may be run in-place but may still be loaded into RAM. This also depends on the loader.
最好有一个完整的例子。如果我采用这样的方法:
并使用最新的 codesourcery lite 进行编译
,我会得到
标记为 .rodata 的部分,我认为这是需要的。然后由链接器脚本来确保 ro 数据放入 rom 中。请注意,read_write 变量是在从 .rodata 切换到 .data 后读取/写入的。
因此,要使其成为完整的二进制文件,并查看它是否被放置在 rom 或 ram(.text 或 .data)中,然后
start.s
然后
我们得到
并达到了预期的结果。 read_write变量在ram中,结构体在rom中。需要确保两个 const 声明都位于正确的位置,编译器不会发出任何警告,例如将 const 放在指向另一个结构的指针上,而该结构在编译时可能无法确定为 const,即使所有这些都得到了链接描述文件(如果您使用的话)要按需要工作可能需要一些努力。例如,这个似乎有效:
A complete example would have been best. If I take something like this:
And compile it with the latest codesourcery lite
I get
With the section marked as .rodata, which I would assume is desired. Then it is up to the linker script to make sure that ro data is put in rom. And note the read_write variable is after switching from .rodata to .data which is read/write.
So to make this a complete binary and see if it gets placed in rom or ram (.text or .data) then
start.s
Then
And we get
And that achieved the desired result. The read_write variable is in ram, the structs are in the rom. Need to make sure both the const declarations are in the right places, the compiler gives no warnings about say putting a const on some pointer to another structure that it may not determine at compile time as being a const, and even with all of that getting the linker script (if you use one) to work as desired can take some effort. for example this one seems to work: