avr-gcc Arduino atmega2560 使用太多 RAM

avr-gcc Arduino atmega2560 using far too much RAM

我目前正在尝试编译一些代码以在 AVR (ATMEGA2560) 上运行,看起来我 运行 内存不足。

我查看了列表(由 avr-objdump -x -S project.elf 生成),我发现 .data 太大而无法放入 8kb RAM(大约 12k)——事实上 'real' RAM 内容从 0x802FEE 开始,地址为 space for 'external RAM'.

我明白了:

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .data         00003088  00800200  0002297a  00022a0e  2**0
                  CONTENTS, ALLOC, LOAD, DATA
  1 .text         0002297a  00000000  00000000  00000094  2**1
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  2 .bss          00000755  00803288  00803288  00025a98  2**2
                  ALLOC
  3 .stab         00001a7c  00000000  00000000  00025a98  2**2
                  CONTENTS, READONLY, DEBUGGING
  4 .stabstr      00000d4d  00000000  00000000  00027514  2**0
                  CONTENTS, READONLY, DEBUGGING

然后我搜索 .data 个符号并根据地址排序:

grep "\.data" project.lst | sort
00800200 g       .data  00000000 __data_start
00800200 l    d  .data  00000000 .data
00802fee g     O .data  00000008 __thenan_sf
00802ff6 g     O .data  00000100 __clz_tab
008030f6 l     O .data  00000004 next
... lots of stuff in here ....
00803267 l     O .data  00000010 CSWTCH.18
00803277  w    O .data  00000010 _ZTV14HardwareSerial
00803288 g       .data  00000000 __data_end
00803288 g       .data  00000000 _edata

所以 .data 应该从 0x800200 开始,但由于某种原因,第一个符号位于 00802fee - 这完全超出了地址范围。

我尝试了 -Wl,--section-start,.data=0x800000,--defsym=__heap_end=0x8021FF 但这只会像预期的那样将事情向后移动 0x200 - .data 开始时仍然有一些东西将所有东西推出。

有谁知道这是什么,或者为什么会这样?这很烦人,因为如果不是这样,一切都应该适合。

好吧,OP 已经发现(在 Olaf 关于从哪里开始寻找的评论的帮助下)实际占用 space 的是什么,但确实应该有一个答案,所以在这里尝试总结一下:

  1. 数据段中的space也被匿名数据占用(代码中的literals),尤其是string literals .

  2. 这里棘手的是:avr是一个哈佛架构,这意味着有different/independent地址space s 用于代码和数据,与广泛使用的 冯·诺依曼架构 相反,只有一个统一的地址 space。 是为后者设计的,所以只能处理一个地址space。在avr芯片上,数据地址space由RAM支持,代码地址space由闪存[=50支持] =].

    现在,如果 运行 RAM 不足,将 只读 数据也放入闪存中是明智的,但有一个问题:给定一个函数a char *,编译器将翻译此函数,假设指针指向数据地址 space 并从那里发出程序集提取。因此,需要另一个类似的函数来查找代码地址 space。

  3. avr-gccavr-libc 中对这个问题的解决方案是提供 PROGMEM 限定符,因此编译器知道以这种方式限定的数据应该存在于程序中内存(代码地址space)。还有一个方便的宏 PSTR() 可以使字符串文字驻留在程序内存中,而无需引入另一个 PROGMEM 限定的标识符。为了使用这些,avr-libc 有一些标准函数后缀 _P(例如,puts_P()),它们的作用完全相同,但期望它们的参数在程序内存中。那样使用它有点麻烦,但是如果 不知道不同的地址 spaces.

    就不可能透明地处理这个问题

由于 avr 芯片上的 RAM 通常很小,因此您可以采取一些其他措施来节省它,例如根本不使用堆(在许多嵌入式程序中,你真的不需要动态分配,只是想一想),在适当的地方使用位域,让编译器“打包”所有结构(-fpack-struct 选项),总是对枚举使用尽可能小的大小(-fshort-enums 选项),始终对小整数使用 uint8_t,在适当的地方使用位域(或使用位 masking/shifting 编码状态),等等。