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

发布于 2024-10-17 09:11:42 字数 1860 浏览 0 评论 0原文

我想将一些常量数据存储在 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

雨的味道风的声音 2024-10-24 09:11:42

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

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

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.

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

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.

饮惑 2024-10-24 09:11:42

你的想法是正确的、合理的。我使用过 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 a const 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.

一袭白衣梦中忆 2024-10-24 09:11:42

您始终可以尝试使用汇编语言。

使用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.

纵山崖 2024-10-24 09:11:42

如果您在 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 utility fromelf.

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.

尛丟丟 2024-10-24 09:11:42

最好有一个完整的例子。如果我采用这样的方法:

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
}

A complete example would have been best. If I take something like this:

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;

And compile it with the latest codesourcery lite

arm-none-linux-gnueabi-gcc -S struct.c

I get

    .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

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

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

    b   hang
    b   hang
    b   hang
    b   hang

reset:
hang: b hang

Then

# 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

And we get

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

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:

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

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文