Zephyr下esp32s3外部psram配置细节
ESPS32S3本身有512KB的内部RAM,在内部RAM不够的情况下,可以通过配置外部PSRAM来扩展内容容量。Zephyr目前已经完整的支持ESP32S3的PSRAM。本文从使用入手,通过分析配置和实现来剖析esp32s3的PSRAM在Zephyr下的细节。
使用 链接到标题
设备树配置 链接到标题
要启用PSRAM,需要在设备树中按照PSRAM的大小配置PSRAM节点, 8M的PSRAM配置如下:
&psram0 {
size = <DT_SIZE_M(8)>;
};
Zephyr在zephyr/dts/xtensa/espressif/esp32s3下提供esp32s3相关模块的dtsi文件其中已经根据模块配置了PSRAM节点,文件名中r后面的数字表示PSRAM的大小:
esp32s3_mini_n4r2.dtsi esp32s3_r2.dtsi esp32s3_wroom_n16r2.dtsi esp32s3_wroom_n4r8.dtsi
esp32s3_mini_n8.dtsi esp32s3_r8.dtsi esp32s3_wroom_n16r8.dtsi esp32s3_wroom_n8.dtsi
esp32s3_pico_n8r2.dtsi esp32s3_r8v.dtsi esp32s3_wroom_n4.dtsi esp32s3_wroom_n8r2.dtsi
esp32s3_fn8.dtsi esp32s3_pico_n8r8.dtsi esp32s3_wroom_n16.dtsi esp32s3_wroom_n4r2.dtsi esp32s3_wroom_n8r8.dts
用户可以根据使用的模块在板级dts中包含dtsi文件来配置PSRAM
#include <espressif/esp32s3/esp32s3_wroom_n16r8.dtsi>
Kconfig配置 链接到标题
Kconfig配置决定是否要启用PSRAM,在prj.conf添加CONFIG_ESP_SPIRAM=y即可启用PSRAM。
默认情况下Zephyr以4线40M的PSRAM模式工作。通过下面的配置项可以修改PSRAM工作模式:
- 连线模式2选一
CONFIGSPIRAM_MODE_QUAD=y使用4线模式
- 连线模式2选一
CONFIG_SPIRAM_MODE_OCT=y使用8线模式 - 时钟2选一
CONFIG_SPIRAM_SPEED_40M=y使用40M的时钟CONFIG_SPIRAM_SPEED_80M=y使用80M的时钟
ESP32S3的PSRAM支持ECC,开启后将会占用总量的1/16来放ECC
CONFIG_SPIRAM_ECC_ENABLE=y开启ECC功能
使用PSRAM 链接到标题
配置PSRAM后Zephyr生成的代码和数据均不会直接使用PSRAM,有下面三种方式进行使用
1. XIP Flash上指令和数据搬到PSRAM 链接到标题
Zephyr支持将XIP Flash上的指令和数据搬到PSRAM上执行,以获得更高的执行速度
CONFIG_SPIRAM_FETCH_INSTRUCTIONS=y将指令从XIP Flash搬运到PSRAMCONFIG_SPIRAM_RODATA=y将只读数据从XIP Flash搬运到PSRAM
2. 在ld脚本中指定 链接到标题
在ld脚本中指定,例如lvgl和mbedtls的heap
KEEP(*(.lvgl_buf*))
. = ALIGN(16);
KEEP(*(.lvgl_heap*))
. = ALIGN(16);
KEEP(*(.mbedtls_heap*))
. = ALIGN(16);
3. 代码中使用PSRAM上的heap 链接到标题
启用PSRAM后,Zephyr会为esp32s3建立一个shared_multi_heap, 用户可以从该heap中分配到PSRAM的内存来使用
char *m_ext = shared_multi_heap_aligned_alloc(SMH_REG_ATTR_EXTERNAL, 32, BUF_SIZE);
memset(m_ext, 0, BUF_SIZE);
shared_multi_heap_free(SMH_REG_ATTR_EXTERNAL, m_ext);
PSRAM初始化过程 链接到标题
esp32s3在__esp_platform_app_start中执行PSRAM初始化过程,在z_prep_c进入到Zephyr世界前执行:
//初始化psram
esp_init_psram();
//在psram上建立共享堆
int err = esp_psram_smh_init();
if (err) {
printk("Failed to initialize PSRAM shared multi heap (%d)\n", err);
}
esp_init_psram实现在zephyr/soc/espressif/common/esp_psram.c中,
void esp_init_psram(void)
{
//初始化psram
if (esp_psram_init()) {
ets_printf("Failed to Initialize external RAM, aborting.\n");
return;
}
//检查psram大小是否符合配置
if (esp_psram_get_size() < CONFIG_ESP_SPIRAM_SIZE) {
ets_printf("External RAM size is less than configured.\n");
}
//检查psram是否通过内存测试
if (IS_ENABLED(CONFIG_ESP_SPIRAM_MEMTEST)) {
if (esp_psram_is_initialized()) {
if (!esp_psram_extram_test()) {
ets_printf("External RAM failed memory test!");
return;
}
}
}
//初始化psram的bss
memset(&_ext_ram_bss_start, 0,
(&_ext_ram_bss_end - &_ext_ram_bss_start) * sizeof(_ext_ram_bss_start));
}
esp_psram_init实现在modules/hal/espressif/components/esp_psram/esp_psram.c中,主要任务是psram通过mmu到ext_iram和ext_iram内存空间的映射,如果要在psram运行指令和放置rodata,就将flash上的指令和data搬运到psram。
实现说明 链接到标题
所有与PSRAM的配置项都放在zephyr/soc/espressif/common/Kconfig.spiram中进行配置,除了ESP_SPIRAM_SIZE从设备中获取外,其他配置项都有默认值。
config ESP_SPIRAM_SIZE
int "Size of SPIRAM part"
default $(dt_node_int_prop_int,$(ESP32_PSRAM0_NODE_PATH),size) if $(dt_nodelabel_enabled,psram0)
default 0
help
Specify size of SPIRAM part.
NOTE: In ESP32, if SPIRAM size is greater than 4MB,
only lower 4MB can be allocated using k_malloc().
Zephyr对esp32s3的PSRAM配置初始化主要是通过zephyr/soc/espressif/common/esp_psram.c调用modules/hal/espressif/components/esp_psram/esp_psram.c中的函数实现。