Espressif publicó el 15 de septiembre la guía para correr Zephyr sobre sus SoC emulados, sin placa de por medio. El camino pasa por el fork de QEMU de Espressif, que emula ESP32, ESP32-S3, ESP32-C3 y ESP32-C6.

Con ese fork se puede compilar el firmware, arrancarlo bajo emulación y engancharle un depurador sin grabar nada en hardware. El QEMU que viene con el SDK de Zephyr, y el que traen la mayoría de las distribuciones de Linux, no modela estos chips, así que el fork no es opcional.

La guía instala Espressif QEMU, lo habilita dentro de una compilación de Zephyr y recorre samples/hello_world sobre esos cuatro destinos, con Simple Boot y con MCUboot más sysbuild. El mismo flujo sirve para otros samples y tests de Zephyr que solo necesiten los bloques del núcleo emulado, o sea UART, flash, temporizadores, cripto y periféricos relacionados. En el camino también se engancha GDB a través de debugserver y se ubica el toolchain del SDK de Zephyr.

Quien ya haya usado QEMU con ESP-IDF, por ejemplo en Trying out ESP32-C3 security features using QEMU, va a reconocer el lado del emulador. La parte nueva es el montaje del lado de Zephyr.

¿Qué funciona hoy sobre Espressif QEMU?

El emulador modela lo suficiente de cada SoC para el arranque habitual, o sea el traspaso desde la ROM y el bootloader, la flash SPI, la consola UART, la lectura de eFuse y de revisión, los temporizadores y varios periféricos internos. Los stacks inalámbricos, Wi-Fi y Bluetooth, quedan fuera de alcance, igual que otros periféricos. La matriz de funcionalidades de Espressif QEMU es la referencia válida sobre qué está modelado y qué no.

En la práctica, hello_world es el punto de partida y no el techo. Sobre ESP32, ESP32-S3, ESP32-C3 y ESP32-C6, los mismos destinos de placa /qemu ya se usaron para correr, entre otros:

  • samples/hello_world
  • samples/drivers/crypto y los tests de AES y SHA por hardware bajo tests/crypto/
  • samples/drivers/watchdog, el watchdog del Timer Group
  • tests/boards/espressif/memory_layout, tanto con Simple Boot como con MCUboot y sysbuild
  • tests/drivers/clock_control/clock_control_api, en ESP32, ESP32-S3 y ESP32-C3
  • tests/drivers/retained_mem/api
  • tests/boards/espressif/interrupt, en los destinos Xtensa
  • samples/boards/espressif/spiram_test, para PSRAM QPI y octal en ESP32 y ESP32-S3, donde QEMU necesita un tamaño de memoria que calce

Siguen necesitando hardware real las cargas de Wi-Fi y Bluetooth, además de los periféricos y las rutas de reinicio que el emulador todavía no implementa, como disparar el callback del watchdog externo de 32 kHz.

Para las opciones avanzadas de QEMU, entre ellas los straps de GPIO, el scripting de eFuse, el tamaño de PSRAM con -m y los ayudantes de red, están las páginas por SoC dentro de esp-toolchain-docs, con material específico para ESP32, ESP32-S3 y ESP32-C3.

Requisitos antes de empezar

Hace falta una instalación funcionando de Zephyr, con workspace de west y el SDK, y poder compilar samples/hello_world para una placa Espressif. Para quien parte de cero está Zephyr RTOS on ESP32, First Steps.

El tamaño de flash es una restricción dura. Espressif QEMU solo acepta imágenes de flash SPI rellenadas a 2, 4, 8 o 16 MB. La imagen combinada sigue el tamaño de zephyr,flash que declara el device tree de la placa, así que hay que usar una placa o un overlay cuyo tamaño de flash sea uno de esos valores. Los ejemplos con DevKitC ya cumplen esa condición.

Otro detalle que conviene tener claro antes de instalar nada. Xtensa y RISC-V usan paquetes de QEMU distintos y no son intercambiables.

SoCPaquete de QEMU
ESP32 y ESP32-S3qemu-system-xtensa
ESP32-C3 y ESP32-C6qemu-system-riscv32

QEMU upstream contra el fork de Espressif

El QEMU upstream, que es el del hosttools del SDK de Zephyr y el de los paquetes típicos de Linux, no implementa las máquinas de los SoC de Espressif. Con esos binarios, qemu-system-xtensa -machine help no lista esp32 ni esp32s3, y qemu-system-riscv32 no lista esp32c3 ni esp32c6. Tampoco traen los modelos de flash, eFuse y UART que Zephyr necesita para arrancar esos chips.

Por eso Zephyr no confía en el primer qemu-system-* que encuentre en el PATH. Al configurar, prueba los candidatos de ESPRESSIF_QEMU_PATH, QEMU_BIN_PATH y PATH con -machine help, y se queda con el primer binario que efectivamente liste la máquina del SoC. Durante la compilación debería aparecer algo así:

Código
-- Espressif QEMU: /home/user/opt/qemu-xtensa-softmmu/qemu/bin/qemu-system-xtensa (-machine esp32)

Como la búsqueda ocurre al configurar, hay que instalar Espressif QEMU antes de configurar, o volver a correr con --pristine después de cambiar las rutas.

Instalar Espressif QEMU

La vía más corta es bajar los binarios precompilados para Linux x86_64, con una salvedad. Al momento de escribir la guía, esos binarios no cubren el ESP32-C6.

Hay que bajar los archivos esp-develop-* más recientes desde las releases de espressif/qemu. El ejemplo usa esp-develop-9.2.2-20260417:

Código
mkdir -p ~/Downloads ~/opt
cd ~/Downloads

wget https://github.com/espressif/qemu/releases/download/esp-develop-9.2.2-20260417/qemu-xtensa-softmmu-esp_develop_9.2.2_20260417-x86_64-linux-gnu.tar.xz
wget https://github.com/espressif/qemu/releases/download/esp-develop-9.2.2-20260417/qemu-riscv32-softmmu-esp_develop_9.2.2_20260417-x86_64-linux-gnu.tar.xz

tar -xf qemu-xtensa-softmmu-esp_develop_9.2.2_20260417-x86_64-linux-gnu.tar.xz -C ~/opt --one-top-level=qemu-xtensa-softmmu
tar -xf qemu-riscv32-softmmu-esp_develop_9.2.2_20260417-x86_64-linux-gnu.tar.xz -C ~/opt --one-top-level=qemu-riscv32-softmmu

Después van los dos directorios bin al PATH, o se apunta ESPRESSIF_QEMU_PATH o QEMU_BIN_PATH al directorio que contenga el binario que se necesita.

Código
export PATH="$HOME/opt/qemu-xtensa-softmmu/qemu/bin:$HOME/opt/qemu-riscv32-softmmu/qemu/bin:$PATH"

# opcional
# export ESPRESSIF_QEMU_PATH=$HOME/opt/qemu-xtensa-softmmu/qemu/bin

Para comprobar que quedó bien:

Código
qemu-system-xtensa -machine help | grep esp32
qemu-system-riscv32 -machine help | grep esp32

Compilar desde el código para el ESP32-C6

Los tarballs publicados hasta esp-develop-9.2.2-* incluyen esp32, esp32s3 y esp32c3. El ESP32-C6, o sea -machine esp32c6, está en el árbol esp-develop pero todavía no en esos binarios, así que para el C6 hay que compilar QEMU por cuenta propia.

Los prerrequisitos, entre ellos libgcrypt, están en esp-toolchain-docs. Xtensa y RISC-V usan valores distintos de --target-list. Para RISC-V, o sea ESP32-C3 y ESP32-C6, la configuración de ejemplo que funciona en un host Linux típico es esta:

Código
git clone https://github.com/espressif/qemu.git
cd qemu

CFLAGS="-Wno-unused-but-set-variable -Wno-discarded-qualifiers -Wno-format-truncation" ./configure --target-list=riscv32-softmmu     --enable-gcrypt     --enable-slirp     --enable-sdl     --disable-strip --disable-user     --disable-capstone --disable-vnc     --disable-gtk

ninja -C build

Después se agrega build/, donde queda qemu-system-riscv32, al PATH o se apunta ahí ESPRESSIF_QEMU_PATH, y se confirma:

Código
./build/qemu-system-riscv32 -machine help | grep esp32c6

Para Xtensa, o sea ESP32 y ESP32-S3, se usa --target-list=xtensa-softmmu. Las opciones que corresponden están en el README de QEMU para ESP32.

Compilar y correr hello_world

Los ejemplos asumen que la shell está dentro del workspace de west, donde existe zephyr/samples/hello_world, y con Espressif QEMU en el PATH.

Hay dos maneras equivalentes de activar QEMU en las placas de referencia. Una es el modo opt-in, con el destino de placa de hardware más -DCONFIG_ESPRESSIF_QEMU=y. La otra es la variante de placa, agregando /qemu y omitiendo el -D. Las cuatro variantes son esp32_devkitc/esp32/procpu/qemu, esp32s3_devkitc/esp32s3/procpu/qemu, esp32c3_devkitc/esp32c3/qemu y esp32c6_devkitc/esp32c6/hpcore/qemu.

Para salir de QEMU hay que apretar CTRL + A y después X.

ESP32

Código
west build -b esp32_devkitc/esp32/procpu zephyr/samples/hello_world   -d build-qemu-esp32 --no-sysbuild --pristine --   -DCONFIG_ESPRESSIF_QEMU=y

west build -d build-qemu-esp32 -t run

La imagen de flash queda en build-qemu-esp32/zephyr/flash_image.bin.

Con sysbuild y MCUboot el comando cambia poco:

Código
west build -b esp32_devkitc/esp32/procpu zephyr/samples/hello_world   -d build-qemu-esp32-sb --sysbuild --pristine --   -DCONFIG_ESPRESSIF_QEMU=y

west build -d build-qemu-esp32-sb --domain hello_world -t run

Ahí la imagen queda en build-qemu-esp32-sb/hello_world/zephyr/flash_image.bin, y hay que esperar los mensajes de MCUboot antes del Hello World!.

La guía incluye además una sesión grabada de Simple Boot sobre ESP32, que muestra el armado del entorno, la compilación de la aplicación y la corrida sobre Espressif QEMU.

ESP32-S3

Código
west build -b esp32s3_devkitc/esp32s3/procpu zephyr/samples/hello_world   -d build-qemu-esp32s3 --no-sysbuild --pristine --   -DCONFIG_ESPRESSIF_QEMU=y

west build -d build-qemu-esp32s3 -t run

Con sysbuild y MCUboot:

Código
west build -b esp32s3_devkitc/esp32s3/procpu zephyr/samples/hello_world   -d build-qemu-esp32s3-sb --sysbuild --pristine --   -DCONFIG_ESPRESSIF_QEMU=y

west build -d build-qemu-esp32s3-sb --domain hello_world -t run

Acá corresponde el paquete Xtensa, qemu-system-xtensa con -machine esp32s3.

ESP32-C3

Código
west build -b esp32c3_devkitc/esp32c3 zephyr/samples/hello_world   -d build-qemu-esp32c3 --no-sysbuild --pristine --   -DCONFIG_ESPRESSIF_QEMU=y

west build -d build-qemu-esp32c3 -t run

Con sysbuild y MCUboot:

Código
west build -b esp32c3_devkitc/esp32c3 zephyr/samples/hello_world   -d build-qemu-esp32c3-sb --sysbuild --pristine --   -DCONFIG_ESPRESSIF_QEMU=y

west build -d build-qemu-esp32c3-sb --domain hello_world -t run

Acá va el paquete RISC-V, qemu-system-riscv32, y el -icount 3 se agrega solo.

ESP32-C6

Necesita una compilación de QEMU para RISC-V que liste -machine esp32c6, o sea la que se arma desde el código.