Support for external RAM

[中文]

Introduction

ESP32-S2 has a few hundred kilobytes of internal RAM, residing on the same die as the rest of the chip components. It can be insufficient for some purposes, so ESP32-S2 has the ability to also use up to 4 MB of external SPI RAM memory. The external memory is incorporated in the memory map and, with certain restrictions, is usable in the same way as internal data RAM.

Hardware

ESP32-S2 supports SPI PSRAM connected in parallel with the SPI flash chip. While ESP32-S2 is capable of supporting several types of RAM chips, ESP-IDF only supports the ESP-PSRAM32 chip at the moment.

The ESP-PSRAM32 chip is a 1.8 V device which can only be used in parallel with a 1.8 V flash component. Make sure to either set the MTDI pin to a high signal level on bootup, or program ESP32-S2 eFuses to always use the VDD_SIO level of 1.8 V. Not doing this can damage the PSRAM and/or flash chip.

Configuring External RAM

ESP-IDF fully supports the use of external memory in applications. Once the external RAM is initialized at startup, ESP-IDF can be configured to handle it in several ways:

Integrate RAM into the ESP32-S2 memory map

Select this option by choosing “Integrate RAM into memory map” from CONFIG_SPIRAM_USE.

This is the most basic option for external SPI RAM integration. Most likely, you will need another, more advanced option.

During the ESP-IDF startup, external RAM is mapped into the data address space, starting at address 0x3F800000 (byte-accessible). The length of this region is the same as the SPI RAM size (up to the limit of 4 MB).

Applications can manually place data in external memory by creating pointers to this region. So if an application uses external memory, it is responsible for all management of the external SPI RAM: coordinating buffer usage, preventing corruption, etc.

Add external RAM to the capability allocator

Select this option by choosing “Make RAM allocatable using heap_caps_malloc(…, MALLOC_CAP_SPIRAM)” from CONFIG_SPIRAM_USE.

When enabled, memory is mapped to address 0x3F800000 and also added to the capabilities-based heap memory allocator using MALLOC_CAP_SPIRAM.

To allocate memory from external RAM, a program should call heap_caps_malloc(size, MALLOC_CAP_SPIRAM). After use, this memory can be freed by calling the normal free() function.

Provide external RAM via malloc()

Select this option by choosing “Make RAM allocatable using malloc() as well” from CONFIG_SPIRAM_USE. This is the default option.

In this case, memory is added to the capability allocator as described for the previous option. However, it is also added to the pool of RAM that can be returned by the standard malloc() function.

This allows any application to use the external RAM without having to rewrite the code to use heap_caps_malloc(..., MALLOC_CAP_SPIRAM).

An additional configuration item, CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL, can be used to set the size threshold when a single allocation should prefer external memory:

  • When allocating a size less than the threshold, the allocator will try internal memory first.

  • When allocating a size equal to or larger than the threshold, the allocator will try external memory first.

If a suitable block of preferred internal/external memory is not available, the allocator will try the other type of memory.

Because some buffers can only be allocated in internal memory, a second configuration item CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL defines a pool of internal memory which is reserved for only explicitly internal allocations (such as memory for DMA use). Regular malloc() will not allocate from this pool. The MALLOC_CAP_DMA and MALLOC_CAP_INTERNAL flags can be used to allocate memory from this pool.

Restrictions

External RAM use has the following restrictions:

  • When flash cache is disabled (for example, if the flash is being written to), the external RAM also becomes inaccessible; any reads from or writes to it will lead to an illegal cache access exception. This is also the reason why ESP-IDF does not by default allocate any task stacks in external RAM (see below).

  • External RAM cannot be used as a place to store DMA transaction descriptors or as a buffer for a DMA transfer to read from or write into. Any buffers that will be used in combination with DMA must be allocated using heap_caps_malloc(size, MALLOC_CAP_DMA) and can be freed using a standard free() call.

  • External RAM uses the same cache region as the external flash. This means that frequently accessed variables in external RAM can be read and modified almost as quickly as in internal ram. However, when accessing large chunks of data (>32 KB), the cache can be insufficient, and speeds will fall back to the access speed of the external RAM. Moreover, accessing large chunks of data can “push out” cached flash, possibly making the execution of code slower afterwards.

  • External RAM cannot be used as task stack memory. Due to this, xTaskCreate() and similar functions will always allocate internal memory for stack and task TCBs, and functions such as xTaskCreateStatic() will check if the buffers passed are internal.

  • By default, failure to initialize external RAM will cause the ESP-IDF startup to abort. This can be disabled by enabling the config item CONFIG_SPIRAM_IGNORE_NOTFOUND.