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 的是什么,但确实应该有一个答案,所以在这里尝试总结一下:
数据段中的space也被匿名数据占用(代码中的literals),尤其是string literals .
这里棘手的是:avr
是一个哈佛架构,这意味着有different/independent地址space s 用于代码和数据,与广泛使用的 冯·诺依曼架构 相反,只有一个统一的地址 space。 c是为后者设计的,所以只能处理一个地址space。在avr
芯片上,数据地址space由RAM支持,代码地址space由闪存[=50支持] =].
现在,如果 运行 RAM 不足,将 只读 数据也放入闪存中是明智的,但有一个问题:给定一个函数a char *
,编译器将翻译此函数,假设指针指向数据地址 space 并从那里发出程序集提取。因此,需要另一个类似的函数来查找代码地址 space。
avr-gcc
和 avr-libc
中对这个问题的解决方案是提供 PROGMEM
限定符,因此编译器知道以这种方式限定的数据应该存在于程序中内存(代码地址space)。还有一个方便的宏 PSTR()
可以使字符串文字驻留在程序内存中,而无需引入另一个 PROGMEM
限定的标识符。为了使用这些,avr-libc
有一些标准函数后缀 _P
(例如,puts_P()
),它们的作用完全相同,但期望它们的参数在程序内存中。那样使用它有点麻烦,但是如果 c 不知道不同的地址 spaces.
就不可能透明地处理这个问题
由于 avr
芯片上的 RAM 通常很小,因此您可以采取一些其他措施来节省它,例如根本不使用堆(在许多嵌入式程序中,你真的不需要动态分配,只是想一想),在适当的地方使用位域,让编译器“打包”所有结构(-fpack-struct
选项),总是对枚举使用尽可能小的大小(-fshort-enums
选项),始终对小整数使用 uint8_t
,在适当的地方使用位域(或使用位 masking/shifting 编码状态),等等。
我目前正在尝试编译一些代码以在 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 的是什么,但确实应该有一个答案,所以在这里尝试总结一下:
数据段中的space也被匿名数据占用(代码中的literals),尤其是string literals .
这里棘手的是:
avr
是一个哈佛架构,这意味着有different/independent地址space s 用于代码和数据,与广泛使用的 冯·诺依曼架构 相反,只有一个统一的地址 space。 c是为后者设计的,所以只能处理一个地址space。在avr
芯片上,数据地址space由RAM支持,代码地址space由闪存[=50支持] =].现在,如果 运行 RAM 不足,将 只读 数据也放入闪存中是明智的,但有一个问题:给定一个函数a
char *
,编译器将翻译此函数,假设指针指向数据地址 space 并从那里发出程序集提取。因此,需要另一个类似的函数来查找代码地址 space。
就不可能透明地处理这个问题avr-gcc
和avr-libc
中对这个问题的解决方案是提供PROGMEM
限定符,因此编译器知道以这种方式限定的数据应该存在于程序中内存(代码地址space)。还有一个方便的宏PSTR()
可以使字符串文字驻留在程序内存中,而无需引入另一个PROGMEM
限定的标识符。为了使用这些,avr-libc
有一些标准函数后缀_P
(例如,puts_P()
),它们的作用完全相同,但期望它们的参数在程序内存中。那样使用它有点麻烦,但是如果 c 不知道不同的地址 spaces.
由于 avr
芯片上的 RAM 通常很小,因此您可以采取一些其他措施来节省它,例如根本不使用堆(在许多嵌入式程序中,你真的不需要动态分配,只是想一想),在适当的地方使用位域,让编译器“打包”所有结构(-fpack-struct
选项),总是对枚举使用尽可能小的大小(-fshort-enums
选项),始终对小整数使用 uint8_t
,在适当的地方使用位域(或使用位 masking/shifting 编码状态),等等。