GitHub - mikroverk/esp32sim: Open-source ESP32 emulator. Run real firmware locally or in your browser — no hardware, no cloud, no subscription.
Pangram verdict · v3.3
We believe that this text is a mix of AI and human-written content.
AI likelihood · overall
AIArticle text · 1,251 words · 6 segments analyzed
esp32sim — an ESP32 emulator in Rust: Xtensa (ESP32, S3) and RISC-V (C3, C6) Instruction-level emulation of four ESP32 SoCs, across both of Espressif's CPU architectures. Each boots the real mask ROM, the real 2nd-stage bootloader and an unmodified application image — no patched firmware, no stubs in the way — with enough of the SoC modelled to run real projects end to end. No cloud, no accounts. MIT. The classic ESP32 also boots its ECO3 ROM on dual LX6 cores, with GPIO, UART and timers; --board ttgo-tdisplay adds the LilyGO TTGO T-Display's ST7789 and buttons. ESP32-S3 ESP32-C3 ESP32-C6 CPU 2 × Xtensa LX7 @ 240 MHz 1 × RISC-V RV32IMC @ 160 MHz 1 × RISC-V RV32IMAC @ 160 MHz Command esp32sim esp32sim-c3 esp32sim-c6 Core crate xtensa-lx7 riscv-rv32 riscv-rv32 (+ the A extension) Reported full-listing decoder checks 977 k instructions, 0 mismatches 161 k instructions, 0 mismatches 126 k instructions, 0 mismatches Boots ROM → bootloader → FreeRTOS → app ROM → bootloader → FreeRTOS → app ROM → bootloader → FreeRTOS → app Boards / displays ST7735, ST7701S 480×480 + touch, WS2812, camera, audio none — console only ST7789 172×320 over SPI+DMA, WS2812, an 802.15.4 energy-detect stand-in WiFi virtual AP, WPA2, DHCP/DNS/NTP, NAT to the real network, HTTPS the same virtual AP and network: scan, WPA2, DHCP, NAT (one station) the same virtual AP and network: scan, WPA2, DHCP, NAT (one station, legacy rates) Speed block interpreter + AArch64 JIT, ~150–240 Minsn/s plain interpreter (no block cache or JIT yet) — still well above real time on hello_world same interpreter In the browser yes (WebAssembly) yes (WebAssembly) yes (WebAssembly) Reported hardware comparisons JTAG trace, 8000 steps after timing-loop resynchronization console diff, 205/208 lines identical console diff, 203/204 lines identical Status mature draft — see docs/esp32c3.md draft — see docs/esp32c6.md These full-listing and hardware counts describe earlier manual runs. CI runs the checked-in sampled decoder corpora and emulator golden outputs; it excludes the external full-listing tests. The Xtensa objdump test excludes PIE operand comparisons. Hardware trace comparison normalizes window exceptions and resynchronizes delay loops, so it is not uninterrupted cycle-by-cycle agreement. See the test scope, CI workflow, Xtensa decoder test and hardware comparator. Most of the SoC is shared: the C3 reuses the S3's UART, USB-Serial/JTAG, systimer, timer groups, GPIO, SPI flash controller, GDMA and SHA/AES/RSA models unchanged, and adds its own memory map, cache controller and interrupt matrix; the C6 reuses the same models again, with its own memory map, PLIC front-end, L1 cache, PCR and always-on LP blocks. Only the CPU crates are genuinely separate. Try it in a browser, no install: https://mikroverk.github.io/esp32sim/ — Linux on the S3, the Touch-LCD-4B panel with its SID player, the pocket-tank LLM aquarium, and the C3 and C6 demos. esp32sim/ emu-core/ the Bus and Core traits, Trap, ClockTree, AArch64 encoder — shared by both cores esp-soc/ Machine<S: Soc>: scheduler, device time, console, scripts, web UI, loaders, boards — one machine for every chip; a chip plugs in its cores, bus and interrupt routing; the virtual WiFi access point, the network behind it and the NAT, for every chip esp-periph/ the peripheral IP the chips share (UART, systimer, TIMG, GPIO, SPI_MEM, GDMA, crypto, I2S, RMT, I2C, …), the Device trait, and the device_set! table that mounts them ── ESP32-S3 (Xtensa LX7, dual core) ── xtensa-lx7/ decoder (sampled objdump checks plus optional full listings), interpreter (windowed regs, loops, XEA2 exceptions/interrupts, FPU, MAC16, booleans, PIE SIMD), basic-block interpreter, AArch64 JIT, objdump-compatible disassembler esp32s3/ SoC + boards: memory map, cache MMU, SPI flash/PSRAM, SHA/AES/RSA, RNG, systimer, timer groups, interrupt matrix (per core), GPIO, USB-CDC, UARTs, I2C, GDMA + I2S/LCD_CAM, RMT TX, regi2c, RTC WDT, WiFi MAC; board/: atech14 / waveshare-cam / waveshare-lcd4b / waveshare-amoled18-v2 / none cli/ the `esp32sim` command line, every chip (`--chip`); `esp32sim-c3` / `esp32sim-c6` are alias binaries ── ESP32-C3 and ESP32-C6 (RISC-V, single core) ── riscv-rv32/ RV32IMAC decoder (sampled objdump checks), interpreter, disassembler esp32c3/ the C3 SoC: memory map, interrupt matrix, cache controller, RNG; peripheral models shared with esp32s3 where the IP is identical esp32c6/ the C6 SoC: unified memory map and MMU, interrupt matrix + PLIC/INTPRI, L1 cache, PCR, the LP blocks (reset cause, software reset, RTC timer), ASSIST_DEBUG, the 802.15.4 and WiFi MACs tests/ golden-output regression tests and their fixtures (tests/README.md) ── shared ── wasm/ C-ABI crate wrapping all three Machines for the browser in one module web/ browser UI (board drawing, console, audio, camera) + emu.js/worker.js for wasm hw/ JTAG differential-test scripts against a real board, captured C3 and C6 consoles examples/ hello_world (IDF, S3, C3 and C6), waveshare-cam (autopling run script + photo), wifi-station (S3), c6-wifi-station (builds for the S3, C3 and C6; the WiFi goldens' firmware), c6-radio-dashboard (WiFi + 802.15.4 on the C6 board) tools/ PIE table generator (TRM-derived); bench.py (interleaved A/B benchmark); wasm-build.sh (the WebAssembly module) Boards (ESP32-S3) The SoC model emits pin-level events (GPIO edges, RMT symbol streams, I2S samples); a BoardModel (esp-soc/src/board.rs) interprets them. --board atech14 (default) is the Atech 14‑port board with its ST7735, WS2812 ring, encoder and buttons; --board none is a bare module — any ESP32‑S3 firmware, console only; --board waveshare-lcd4b is the Waveshare ESP32‑S3‑Touch‑LCD‑4B (ST7701S 480×480 over the LCD_CAM RGB bus, GT911 touch, TCA9554, codecs) running the esp32-screen LVGL panel with touch; --board waveshare-amoled18-v2 is the Waveshare ESP32-S3-Touch-AMOLED-1.8 V2 (CO5300 368x448 over QSPI, CST820 touch); --board waveshare-cam is the Waveshare ESP32‑S3‑CAM‑OV5640 (CH32V003 IO expander, OV5640 over SCCB, ES8311/ES7210 codecs on I2C0, speaker on I2S1, OV5640 on the LCD_CAM DVP port) — runs the waveshare-autopling firmware (IDF 5.5, 16 MB flash, 8 MB octal PSRAM: --flash-mb 16 --psram-mb 8) end to end: camera frames from --cam-image or the browser (picture upload / webcam) → esp‑dl pedestrian detector on the emulated PIE SIMD unit → pling on the ES8311. See examples/waveshare-cam/. Adding a board = implementing the trait (gpio_changes, rmt_frame, i2c_devices, camera_frame, …). The Pocket Synth firmware the atech14 demos run is its own project: atech-firmware — a PlatformIO build of the SID synth and a cRSID player for real .sid tunes. This repo carries the built images it produces, in web/wasm/fw/public/.
cargo build --release FW=web/wasm/fw/public # the demo images the goldens and the web page run ./target/release/esp32sim --boot rom \ --bootloader $FW/atech-bootloader.bin --ptable $FW/atech-ptable.bin --app $FW/atech-firmware.bin \ --script $FW/atech-script1.txt --wav out.wav --tft-png tft.png --max-seconds 5 The mask ROM ELF is picked up from ~/.espressif/tools/esp-rom-elfs/*/esp32s3_rev0_rom.elf (shipped with ESP‑IDF).
Without ESP-IDF, tools/fetch-demo-assets.sh --no-linux puts the S3, C3 and C6 ROMs in web/wasm/fw/: pass --rom web/wasm/fw/esp32s3_rev0_rom.elf, or set ESP32SIM_ROM_DIR=web/wasm/fw for the tests. --boot app skips ROM+bootloader and loads the app image directly. A plain ESP‑IDF project, e.g.
examples/hello_world (the IDF 5.5 get-started example built with idf.py set-target esp32s3 && idf.py build): H=examples/hello_world/build ./target/release/esp32sim --board none --boot rom --console uart0 \ --bootloader $H/bootloader/bootloader.bin --ptable $H/partition_table/partition-table.bin \ --app $H/hello_world.bin --elf $H/hello_world.elf --elf $H/bootloader/bootloader.elf --max-seconds 26 prints the ROM banner, the bootloader and app logs on UART0, Hello world!, the countdown, and then reboots through the RTC watchdog exactly as silicon does (rst:0xc (RTC_SW_CPU_RST)), ~30× faster than real time.
Chip resets (software, RTC watchdog) restart the machine from the ROM with the right reset cause; --no-reboot stops at the first reset instead. Run — ESP32-C3 esp32sim-c3 runs unmodified ESP-IDF firmware on the emulated RISC-V core, from the real mask ROM through the real bootloader into FreeRTOS. Same flags as esp32sim (--trace, --break, --watch, --peek, --disasm, --log-periph), printing RISC-V mnemonics with symbols.
H=examples/hello_world-c3/build ./target/release/esp32sim-c3 --boot rom --flash-mb 4 \ --bootloader $H/bootloader/bootloader.bin --ptable $H/partition_table/partition-table.bin \ --app $H/hello_world.bin --elf $H/hello_world.elf --max-seconds 26 prints the ROM banner, the bootloader log, Hello world!