ESP32-S3 QEMU 无法稳定运行 PSRAM 配置:诊断与 QEMU 专用降级方案

ESP32-S3 QEMU 无法稳定运行 PSRAM 配置:诊断与 QEMU 专用降级方案

本文记录在 Windows + ESP-IDF v6.0.1 + Espressif QEMU 环境下,ESP32-S3 工程启用 Octal PSRAM 后无法在 QEMU 中稳定启动的问题,以及用于模拟器基础验证的处理方案。

本文方案的目标是让固件在 QEMU 中启动和运行基础功能,并不等价于验证真实硬件上的 PSRAM 功能。


1. 问题背景

许多 ESP32-S3 工程面向带 PSRAM 的真实开发板配置,例如:

CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_OCT=y
CONFIG_SPIRAM_TYPE_AUTO=y

在真实硬件上,这类配置通常用于:

  • 承载大块动态内存;
  • Wi-Fi/LwIP 缓冲区;
  • 图形或多媒体资源;
  • WebAssembly runtime / 应用堆;
  • 大型文件系统缓存。

在 ESP-WASMachine 中,WAMR runtime 的分配器也会根据 PSRAM 配置选择 heap capability:

#ifdef CONFIG_SPIRAM
    uint32_t caps = MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT;
#else
    uint32_t caps = MALLOC_CAP_8BIT;
#endif

因此在真实设备上,PSRAM 对较大的 WASM 运行负载非常重要。

但在 QEMU 中直接使用这套真实硬件配置,可能无法稳定启动。


2. 典型现象

执行:

idf.py qemu monitor

可能出现以下现象之一:

  • QEMU 刚启动便退出;

  • IDF monitor 显示:

    [WinError 10054] 远程主机强迫关闭了一个现有的连接。
    
  • monitor 能连接,但没有任何 UART 启动日志;

  • 前台启动 QEMU 时,bootloader 加载应用镜像后 QEMU 进程异常退出;

  • 在启用 Octal PSRAM 时,QEMU 启动参数包含:

    -global driver=ssi_psram,property=is_octal,value=true
    

这通常意味着 QEMU 对当前 ESP32-S3、IDF 和 Octal PSRAM 配置组合的模拟存在限制,而不是应用层业务代码必然有问题。


3. 不能简单靠 -m 增大内存解决

ESP32-S3 QEMU 常见参数:

-M esp32s3 -m 32M

这里的 -m 32M 用于模拟外部内存容量。

但如果固件配置为:

CONFIG_SPIRAM=n

ESP-IDF 不会初始化外部 RAM,也不会将它纳入可用 heap。因此即使把 QEMU 参数改为:

-m 64M

或:

-m 128M

应用也不会自动得到更多可用内存。

结论:

  • -m 仅表示 QEMU 可模拟外部内存;
  • CONFIG_SPIRAM=y 才表示 IDF 将其当作 PSRAM 初始化和使用;
  • 如果 QEMU 无法正确模拟该 PSRAM 路径,单纯增大 -m 没有帮助。

4. 推荐方案:建立 QEMU 专用 defaults

不要修改真实硬件默认配置。新增一份只供 QEMU 使用的配置,例如:

sdkconfig.defaults.qemu

建议内容:

# ESP32-S3 QEMU does not reliably emulate the Octal PSRAM used by the
# hardware configuration. Keep PSRAM disabled in the simulator-only build.
CONFIG_SPIRAM=n

# With PSRAM disabled, WAMR allocates executable AOT memory from internal RAM.
# ESP-IDF memory protection removes MALLOC_CAP_EXEC, so disable it for the
# simulator-only build.
CONFIG_ESP_SYSTEM_MEMPROT=n

其中两项分别解决不同问题。

4.1 禁用 PSRAM

CONFIG_SPIRAM=n

作用:

  • 避开 QEMU 的 Octal PSRAM 模拟路径;
  • 让 runtime 使用内部 DRAM;
  • 适合执行小型测试程序和基本交互验证。

代价:

  • 可用 heap 显著减少;
  • 不适合大 WASM 应用;
  • 不等价于真实板子的内存容量与内存分配行为。

4.2 禁用 IDF 内存保护

CONFIG_ESP_SYSTEM_MEMPROT=n

关闭 PSRAM 后,部分 WAMR AOT 内存映射代码会使用:

MALLOC_CAP_EXEC

但 IDF 6 启用内存保护时会移除这一 capability,典型编译错误:

error: 'MALLOC_CAP_EXEC' undeclared
HINT: MALLOC_CAP_EXEC capability cannot be used if
CONFIG_ESP_SYSTEM_PMP_IDRAM_SPLIT or CONFIG_ESP_SYSTEM_MEMPROT_FEATURE is enabled.

因此,在 PSRAM 关闭的 QEMU 版本中,应关闭:

CONFIG_ESP_SYSTEM_MEMPROT=n

这是一项模拟器专用安全降级。真实硬件构建不应因为 QEMU 验证方便而关闭内存保护。


5. ESP-IDF 6 与 WAMR 的内存分配关系

WAMR 的 ESP-IDF 内存映射代码在不同配置下使用不同 capability。

启用外部双总线镜像/PSRAM 路径时,通常使用:

MALLOC_CAP_SPIRAM

关闭 PSRAM 后,则会使用:

MALLOC_CAP_EXEC

而 ESP-IDF 的内存布局在 CONFIG_ESP_SYSTEM_MEMPROT=y 时,不提供可执行 heap capability。

因此以下组合不可用:

CONFIG_SPIRAM=n
CONFIG_ESP_SYSTEM_MEMPROT=y

适用于 QEMU 基础验证的组合是:

CONFIG_SPIRAM=n
CONFIG_ESP_SYSTEM_MEMPROT=n

6. 推荐构建方式:使用独立构建目录

建议 QEMU 与真实硬件各自使用独立 build 目录:

build        # 真实硬件配置
build-qemu   # QEMU 专用配置

例如:

cd D:\code\your-project

idf.py -B build-qemu `
  -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.defaults.esp32s3;sdkconfig.defaults.qemu" `
  set-target esp32s3

idf.py -B build-qemu build

验证最终配置:

Select-String -Path sdkconfig -Pattern "CONFIG_SPIRAM|CONFIG_ESP_SYSTEM_MEMPROT"

预期:

# CONFIG_SPIRAM is not set
# CONFIG_ESP_SYSTEM_MEMPROT is not set

sdkconfig 位于项目根目录或由当前 SDKCONFIG 路径决定;如项目将其放在其他位置,请按实际路径检查。


7. 如何启动和观察 QEMU 日志

推荐以前台方式启动:

idf.py -B build-qemu qemu

它会将模拟 ESP32-S3 的 UART 直接连接到当前终端。

相比之下:

idf.py -B build-qemu qemu monitor

会使用 TCP 串口端口连接 IDF monitor。在某些 Windows QEMU 环境中,monitor 可以成功连接,但不一定稳定显示启动期 UART 日志。

因此排查 PSRAM 或启动问题时,优先用:

idf.py -B build-qemu qemu

可以直接观察:

  • ROM 启动;
  • bootloader;
  • Flash 初始化;
  • 内存初始化;
  • app_main()
  • panic/assert/backtrace。

8. 验证成功的标志

在关闭 PSRAM 的 QEMU 构建中,至少应能看到:

heap_init: Initializing. RAM available for dynamic allocation:
...
main_task: Calling app_main()

并且不应出现:

assert failed
Rebooting...

如果工程提供 shell,可进一步运行最小功能验证,例如:

WASMachine> iwasm wasm/hello_world.wasm
Hello World!

这说明固件、runtime 和基本应用流程可以在 QEMU 中工作。


9. 适用范围与限制

此 workaround 适合:

  • CI 中的启动冒烟测试;
  • QEMU 下的基础功能验证;
  • Shell、文件系统、网络基础逻辑调试;
  • 小型 WebAssembly 程序验证;
  • 不依赖大块外部 RAM 的功能开发。

不适合将以下结论直接推导到真实硬件:

  • PSRAM 容量是否足够;
  • PSRAM 分配是否正确;
  • PSRAM 性能;
  • 大 WASM 模块加载能力;
  • 多应用或高并发运行;
  • 图形、音频、大缓存等高内存场景;
  • 内存保护相关安全行为。

真实硬件仍应保留:

CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_OCT=y
CONFIG_ESP_SYSTEM_MEMPROT=y

以及其对应的硬件 Flash 和 PSRAM 配置。


10. 总结

当 ESP32-S3 项目在 QEMU 中无法使用真实硬件的 Octal PSRAM 配置时,可采用专用模拟器配置:

CONFIG_SPIRAM=n
CONFIG_ESP_SYSTEM_MEMPROT=n

核心原则:

  1. 不修改真实硬件的默认配置;
  2. 使用 sdkconfig.defaults.qemu 管理模拟器差异;
  3. 使用独立的 build-qemu 目录;
  4. 前台运行 idf.py -B build-qemu qemu 观察真实启动日志;
  5. 将其定位为基础模拟验证方案,而非 PSRAM 功能验证方案。
posted @ 2026-07-29 14:23  口嗨养生博  阅读(7)  评论(0)    收藏  举报