# 片外 RAM

[English]

## 简介

ESP32 提供了好几百 KB 的片上 RAM，可以满足大部分需求。但有些场景可能需要更多 RAM，因此 ESP32 另外提供了高达 4 MB 的虚拟地址，供片外 PSRAM（伪静态随机存储器）存储器使用。片外 RAM 已经集成到内存映射中，在某些范围内与片上 RAM 使用方式相同。

## 硬件

ESP32 支持与 SPI flash 芯片并联的 PSRAM。虽然 ESP32 支持多种类型的 RAM 芯片，但 ESP-IDF 当前仅支持乐鑫品牌的 PSRAM 芯片，如 ESP-PSRAM32、ESP-PSRAM64 等。

备注

PSRAM 芯片的工作电压分为 1.8 V 和 3.3 V。其工作电压必须与 flash 的工作电压匹配。请查询相应 PSRAM 芯片以及 ESP32 的技术规格书获取准确的工作电压。对于 1.8 V 的 PSRAM 芯片，请确保在启动时将 MTDI 管脚设置为高电平，或者将 ESP32 中的 eFuses 设置为始终使用 1.8 V 的 VDD_SIO 电平，否则有可能会损坏 PSRAM 和/或 flash 芯片。

备注

乐鑫同时提供模组和系统级封装芯片，集成了兼容的 PSRAM 和 flash，可直接用于终端产品 PCB 中。如需了解更多信息，请前往乐鑫官网。注意，ESP-IDF SDK 可能与定制的 PSRAM 芯片不兼容。

有关将 SoC 或模组管脚连接到片外 PSRAM 芯片的具体细节，请查阅 SoC 或模组技术规格书。

## 配置片外 RAM

ESP-IDF 完全支持将片外 RAM 集成到你的应用程序中。在启动并完成片外 RAM 初始化后，可以将 ESP-IDF 配置为用多种方式处理片外 RAM：

- 集成片外 RAM 到 ESP32 内存映射

- 添加片外 RAM 到堆内存分配器

- 调用 malloc() 分配片外 RAM (default)

- 允许 .bss 段放入片外存储器

- 允许 .noinit 段放入片外存储器

### 集成片外 RAM 到 ESP32 内存映射

在 CONFIG_SPIRAM_USE 中选择 `Integrate RAM into memory map` 选项，以集成片外 RAM 到 ESP32 内存映射。

这是集成片外 RAM 最基础的设置选项，大多数用户需要用到其他更高级的选项。

ESP-IDF 启动过程中，片外 RAM 被映射到数据虚拟地址空间，该地址空间是动态分配的，其长度为 PSRAM 大小和可用数据虚拟地址空间大小之间的最小值。

应用程序可以创建指向该区域的指针，手动将数据放入片外存储器，并全权负责管理片外 RAM，包括协调缓存占用、防止发生损坏等。

建议通过 ESP-IDF 堆内存分配器访问 PSRAM（见下一小节）。

### 添加片外 RAM 到堆内存分配器

在 CONFIG_SPIRAM_USE 中选择 `Make RAM allocatable using heap_caps_malloc(..., MALLOC_CAP_SPIRAM)` 选项。

启用上述选项后，片外 RAM 被映射到数据虚拟地址空间，并将这个区域添加到携带 `MALLOC_CAP_SPIRAM` 标志的 堆内存分配器。

程序如果想从片外存储器分配存储空间，则需要调用 `heap_caps_malloc(size, MALLOC_CAP_SPIRAM)`，之后可以调用 `free()` 函数释放这部分存储空间。

### 调用 `malloc()` 分配片外 RAM

在 CONFIG_SPIRAM_USE 中选择 `Make RAM allocatable using malloc() as well` 选项，该选项为默认选项。

启用此选项后，片外存储器将被添加到内存分配程序（与上一选项相同），同时也将被添加到由标准 `malloc()` 函数返回的 RAM 中。

应用程序因此可以使用片外 RAM，无需重写代码就能使用 `heap_caps_malloc(..., MALLOC_CAP_SPIRAM)`。

如果某次内存分配偏向于片外存储器，也可以使用 CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL 设置分配空间的大小阈值，控制分配结果：

- 如果分配的空间小于或等于阈值，分配程序将首先选择内部存储器。

- 如果分配的空间大于阈值，分配程序将首先选择外部存储器。

如果优先考虑的内部或外部存储器中没有可用的存储块，分配程序则会选择其他类型存储。

由于有些内存缓冲器仅可在内部存储器中分配，因此需要使用第二个配置项 CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL 定义一个内部内存池，仅限显式的内部存储器分配使用（例如用于 DMA 的存储器）。常规 `malloc()` 将不会从该池中分配，但可以使用 MALLOC_CAP_DMA 和 `MALLOC_CAP_INTERNAL` 标志从该池中分配存储器。

### 允许 .bss 段放入片外存储器

通过勾选 CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY 启用该选项。

启用该选项后，PSRAM 被映射到的数据虚拟地址空间将用于存储来自 lwip、net80211、libpp、wpa_supplicant 和 bluedroid ESP-IDF 库中零初始化的数据（BSS 段）。

通过将宏 `EXT_RAM_BSS_ATTR` 应用于任何静态声明（未初始化为非零值），可以将附加数据从内部 BSS 段移到片外 RAM。

也可以使用链接器片段方案 `extram_bss` 将组件或库的 BSS 段放到片外 RAM 中。

启用此选项可以减少 BSS 段占用的内部静态存储。

剩余的片外 RAM 也可以通过上述方法添加到堆分配器中。

### 允许 .noinit 段放入片外存储器

通过勾选 CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY 启用该选项。启用该选项后，PSRAM 被映射到的数据虚拟地址空间将用于存储未初始化的数据。即使在启动或重新启动期间，放置在该段中的值也不会被初始化或修改。

通过应用 `EXT_RAM_NOINIT_ATTR` 宏，可以将数据从内部 NOINIT 段移到片外 RAM。剩余的片外 RAM 也可以通过上述方法添加到堆分配器中，具体请参考 添加片外 RAM 到堆内存分配器。

## 片外 RAM 使用限制

使用片外 RAM 有下面一些限制：

- flash cache 禁用时（比如，正在写入 flash），片外 RAM 将无法访问；同样，对片外 RAM 的读写操作也将导致 cache 访问异常。因此，ESP-IDF 不会在片外 RAM 中分配任务堆栈（详见下文）。

- 片外 RAM 与片外 flash 使用相同的 cache 区域，这意味着频繁在片外 RAM 访问的变量可以像在片上 RAM 中一样快速读取和修改。但访问大块数据时（大于 32 KB），cache 空间可能会不足，访问速度将降低到片外 RAM 的访问速度。此外，访问大块数据会挤出 flash cache，可能在之后降低代码的执行速度。

- 一般来说，片外 RAM 不会用作任务堆栈存储器。`xTaskCreate()` 及类似函数始终会为堆栈和任务 TCB 分配片上储存器。

可以使用 CONFIG_FREERTOS_TASK_CREATE_ALLOW_EXT_MEM 选项将任务堆栈放入片外存储器。这时，必须使用 `xTaskCreateStatic()` 指定从片外存储器分配的任务堆栈缓冲区，否则任务堆栈将仍从片上存储器分配。

## 初始化失败

默认情况下，片外 RAM 初始化失败将终止 ESP-IDF 启动。如果想禁用此功能，可启用 CONFIG_SPIRAM_IGNORE_NOTFOUND 配置选项。

如果启用 CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY，忽略失败的选项将无法使用，这是因为在链接时，链接器已经向片外存储器分配标志符。

- PSRAM 中的栈：对于不以任何直接或间接方式调用 ROM 中代码的任务，选项 CONFIG_FREERTOS_TASK_CREATE_ALLOW_EXT_MEM 会取消 `xTaskCreateStatic()` 中的检查，允许任务栈存放在外部 RAM 中。但 不建议 使用此选项。

- 在以 80 MHz 时钟速度运行时，外部 RAM 还必须占用 HSPI 或 VSPI 总线。设置 CONFIG_SPIRAM_OCCUPY_SPI_HOST 选项可以选择使用的 SPI 主机。

## 芯片版本

某些 ESP32 版本存在与外部 RAM 使用相关的问题，这些问题记录在 ESP32 系列芯⽚勘误表 文档中。ESP-IDF 会以下列特定方式处理上述错误：

### ESP32 rev v0.0

ESP-IDF 并未针对该版芯片的错误提供解决方案，也不能将外部 PSRAM 映射到 ESP32 的主存储器映射中。

### ESP32 rev v1.0

某些机器指令序列对外部内存进行操作时，该版芯片的错误会引发问题，详情请参阅 ESP32 系列芯⽚勘误表 第 3.2 节。为此，ESP32 GCC 编译器增加了标志 `-mfix-esp32-psram-cache-issue`，用于过滤这些序列，只输出可以安全执行的代码。请启用 CONFIG_SPIRAM_CACHE_WORKAROUND 选项以使用此方法。

启用此选项后，ESP-IDF 会链接到重新编译且带有额外标志的 Newlib，此外还会执行以下操作：

- 避免使用某些 ROM 函数

- 为 Wi-Fi 栈分配静态内存

### ESP32 rev v3.0

ESP32 rev v3.0 修复了 rev v1.0 中发现的 PSRAM 缓存问题。当 CONFIG_ESP32_REV_MIN 选项设置为 `rev v3.0` 时，会禁用与 PSRAM 相关的编译器解决方案。有关 ESP32 v3.0 的更多详情，请参阅 ESP32 芯⽚版本 v3.0 使⽤指南。

此文档对您有帮助吗？

- 反馈已收到，谢谢！
- 如果您有其他意见，欢迎填写乐鑫文档反馈表。

- 我们重视您的反馈。
- 您可以填写乐鑫文档反馈表告诉我们如何改进该文档。