? 384 KB 的ROM:用于程序启动和内核功能调用
? 400 KB 片上SRAM:用于数据和指令存储,时钟频率可配置,最大160 MHz。400 KB SRAM 中,有16 KB 配置为cache 专用
? RTC 快速存储器:为8 KB 的SRAM,可被主CPU 访问,在Deep-sleep 模式下可以保存数据
? 4 Kbit 的eFuse:其中1792 位保留给您使用,例如用于存储密钥和设备ID
? 支持最大16 MB 片外flash。(部分ESP32C3型号将flash封装内置)
系统结构和地址映射结构:
从上图可以看出,ESP32C3的存储介质,不管是类型,还是用途,都比较多的。ESP32C3是一个典型的哈弗结构处理器,拥有独立的数据总线和指令总线,数据地址空间和指令地址空间是分开的;即使是相同的存储位置,数据地址和指令地址是不一样的,即同序访问,如红色标注的ROM1、RAM1。图片中的颜色标注字体是为了更直观地理解内置ROM/SRAM的用途,我特意添加的。与常见的单片机不同,ROM/SRAM的用途不是单一的定义,程序不能任意使用所有内存,而是必须按硬性的地址映射来分配用途,见下表。
ROM0 :只允许指令总线访问,即固件程序存放于此
ROM1 :数据/指令总线都能访问,区别在于数据总线没有执行权限,只有读权限
SRAM0 :不允许数据总线访问,这块作为访问外部FLASH的缓存(CACHE)
SRAM1 :允许任意访问,且权限没有限制
RTC FastRAM :同SRAM1,区别在于它可用于深度休眠,保证数据不丢失
当然,从芯片开发者的角度来看,我们不必关心内置ROM,因为ROM已经固化了厂商的出厂固件,比如芯片的下载功能、启动模式等功能都基于此 ,出厂固件也称为第一级启动引导PBL(Primary Bootloader)。
接下来,分析下APP程序是如何使用片内RAM的。
以测试用例blink为例,编译完成后,可用命令直接查看blink.bin的内存占用情况、加载段的信息。
###查看blink.bin所需的RAM、ROM空间大小
PS G:\workspace\espressif\blink> idf.py size
Executing action: size
... ... ...
Total sizes:
Used stat D/IRAM: 54154 bytes ( 267142 remain, 16.9% used)
.data size: 4504 bytes #非常量静态数据
.bss size: 3688 bytes #未初始化的数据
.text size: 45962 bytes #程序代码
Used Flash size : 140940 bytes
.text: 101092 bytes #程序代码
.rodata: 39592 bytes #只读数据
Total image size: 191406 bytes (.bin may be padded larger)
###查看blink.bin文件内各个内存段的大小、加载地址,以及内存段的类型
PS G:\workspace\espressif\blink> esptool.py --chip esp32c3 image_info G:/workspace/espressif/blink/build/blink.bin
... ... ...
Segment 1: len 0x09ba8 load 0x3c020020 file_offs 0x00000018 [DROM]
Segment 2: len 0x01198 load 0x3fc8b400 file_offs 0x00009bc8 [DRAM,BYTE_ACCESSIBLE]
Segment 3: len 0x052a8 load 0x40380000 file_offs 0x0000ad68 [IRAM]
Segment 4: len 0x18ae4 load 0x42000020 file_offs 0x00010018 [IROM]
Segment 5: len 0x060e4 load 0x403852a8 file_offs 0x00028b04 [IRAM]
Checksum: 6a (valid)
Validation Hash: 9a9fb7ee6e46797dcbf956965a0f9b6022122636ebd1d85606e455d19d67d043 (valid)
###以下是ESP32C3板子启动后加载blink.bin的日志
I (83) boot: End of partition table
I (87) esp_image: segment 0: paddr=00010020 vaddr=3c020020 size=09ba8h ( 39848) map
I (102) esp_image: segment 1: paddr=00019bd0 vaddr=3fc8b400 size=01198h ( 4504) load
I (104) esp_image: segment 2: paddr=0001ad70 vaddr=40380000 size=052a8h ( 21160) load
I (116) esp_image: segment 3: paddr=00020020 vaddr=42000020 size=18ae4h (101092) map
I (137) esp_image: segment 4: paddr=00038b0c vaddr=403852a8 size=060e4h ( 24804) load
I (145) boot: Loaded app from partition at offset 0x10000
APP程序需要占用一定的ROM、RAM;ROM用于存放程序指令和只读的数据,RAM可存放数据,也可存放程序。
对于ESP32C3芯片,ROM位于片外的FLASH存储空间,程序不能直接在外存储器上运行,而是先通过Cache 访问将外存储器由MMU映射到虚地址空间(见地址映射结构图),APP内存段的加载地址就在该虚地址空间中;RAM则位于片内SRAM、RTC Fast RAM,按总线访问,可分为IRAM(指令RAM)和DRAM(数据RAM)。
IDF默认为APP程序指定的RAM是SRAM,但我们可以根据实际需求(例如只有在RTC Fast RAM的代码和数据才能在deep休眠后不丢失),去改变程序代码或数据的分配位置。IDF开发平台提供了一个链接器脚本生成机制,这应该是乐鑫官方独创的。
IDF提供了两种方式:
方式一:属性宏
宏定义文件位于\components\esp_common\include\esp_attr.h
// Forces code into IRAM instead of flash
#define IRAM_ATTR _SECTION_ATTR_IMPL(".iram1", __COUNTER__)
// Forces data into DRAM instead of flash
#define DRAM_ATTR _SECTION_ATTR_IMPL(".dram1", __COUNTER__)
在定义变量或声明函数时,设定属性宏,就可以告诉编译器在链接阶段分配相应的存储类型中。
#常量数据定义在DRAM,而不是FLASH,因为该常量被用于non-flash-safe中断函数
__NOINIT_ATTR uint32_t noinit_data;
#使用IRAM_ATTR 宏在源代码中指定需要放入IRAM 的代码
void IRAM_ATTR gpio_isr_handler(void* arg)
{
// ...
//= noinit_data
}
方式二:链接器脚本生成机制
方式一的使用比较简单方便,但局限也很明显,处理不了整个目标源文件或复杂的函数调用关系。
链接器脚本生成机制定义了一套规则,可以让用户指定代码和数据在ESP-IDF 组件中的存放区域。IDF平台中,扩展名(.lf)的文件,就是这套规则的脚本。
?编译APP时,使用的默认脚本如下: components\esp_system\app.lf
[scheme:default]
entries:
if APP_BUILD_USE_FLASH_SECTIONS = y:
text -> flash_text
rodata -> flash_rodata
else:
text -> iram0_text
rodata -> dram0_data
data -> dram0_data
bss -> dram0_bss
common -> dram0_bss
if ESP_ALLOW_BSS_SEG_EXTERNAL_MEMORY = y:
extram_bss -> extern_ram
else:
extram_bss -> dram0_bss
legacy_bss -> dram0_bss
iram -> iram0_text
iram_data -> iram0_data
iram_bss -> iram0_bss
dram -> dram0_data
rtc_text -> rtc_text
rtc_data -> rtc_data
rtc_rodata -> rtc_data
rtc_bss -> rtc_bss
[scheme:rtc]
entries:
text -> rtc_text
data -> rtc_data
rodata -> rtc_data
bss -> rtc_bss
common -> rtc_bss
[scheme:noflash]
entries:
text -> iram0_text
rodata -> dram0_data
[scheme:noflash_data]
entries:
rodata -> dram0_data
[scheme:noflash_text]
entries:
text -> iram0_text
[mapping:default]
archive: *
entries:
* (default)
?机制的具体细节,请直接看官网编程指南-链接器脚本生成机制。
与存储关联的内容,还有权限管理(PMS),本次就不在此展开。