a pass means a pinned firmware image keeps doing useful work, responds through modeled hardware or service boundaries, and produces matching results in the interpreter and jit. the scenario defines the claim.
current corpus · s3 workflows · run a check · target setup · hardware limits
these classic esp32 images pass their scripted scenarios in both engines. the table records coverage for those versions, rather than every feature each firmware offers.
| firmware / board | pinned version | what to keep in mind |
|---|---|---|
| marauder / cyd 2432s028 2usb | 1.12.1 | official esp32 rom elf required |
| marauder / cyd | 1.14.3 | no remaining gap in the scripted scenario |
| marauder / cyd 2432s028 | 1.15.1 | no remaining gap in the scripted scenario |
| marauder / 3.5-inch and guition variants | 1.14.3 | no remaining gap in the scripted scenario |
| nerdminer | 1.8.3 | no remaining gap in the scripted scenario |
| bruce / cyd 2432s028 | 1.16.1 | official rom elf required; expand radio interactions |
| meshtastic / t-beam | 2.7.26 | official rom elf required |
| openhasp / lanbon l8 | 0.7.0-rc13 | expand ft6336 touch and http/mqtt interactions |
| tasmota | 15.6.0 | expand mqtt and device drivers |
| wled | 16.0.1 | expand http, ddp, and e1.31 scenarios |
firmware and rom images are supplied separately. recheck when an upstream release changes.
Agon VDP 2.16.0 has a separate native end-to-end VDP gate:
cmake --build build --target flexe-agon-vdp-test -j
AGON_VDP_BIN=/path/to/firmware.bin ./scripts/check-agon-vdp.shThe gate pins the release application's SHA-256 to
b807beef35823b13a0a056f11b7464cd1b1c6356dce0e4098b78ebe059ded35f.
It runs native FreeRTOS without an ELF, application symbol hooks, or guest
memory patches. A UART2 peer sends the MOS handshake and VDU commands; the
firmware itself parses them and runs FabGL's drawing and interrupt code.
Both engines must return the exact handshake, mode information, character
query (F), and pixel queries (red/blue). The gate decodes raw I2S1 LCD DMA
scanout using its H/V sync bits and verifies:
- 640x480 mode 0: red background with white
FLEXEtext. - A real VDU mode switch to 320x240 mode 8: blue background with the same text.
- Stable complete frames, pinned pixel hashes, and matching UART output.
- Continued scanout through a configurable soak, with frame count consistent with the programmed pixel clock, including blanking and double scan.
- A native I2S0/DAC square wave requested through VDU audio commands: 1024 Hz for 500 ms, followed by silence, with the expected audio acknowledgment.
- Native UART0 serial-console input produces the correct UART2 keyboard packet.
- Zero unsupported MMIO, unregistered ROM calls, or unmapped accesses; the JIT must retire native instructions during the soak.
Set ARTIFACTS=/path/to/output to retain PPM captures, the captured DAC tone as
tone.wav, and a JSON results file.
The matching release map supplies only a diagnostic panic_abort breakpoint;
success depends on the observed UART and VGA behavior.
This catches an internal analog-I2C ROM-stub defect: writes were discarded and reads always returned zero, leaving FabGL in the APLL calibration loop. Byte and masked register accesses now retain state, and the APLL reset/start sequence completes calibration functionally. APLL SDM/divider registers now drive the shared I2S clock source, and LCD DMA uses its parallel word clock instead of PCM stereo timing. Analog lock timing is not modeled.
There is also a full-machine gate using the upstream eZ80 emulator as a separate process, with pinned Agon Platform MOS 3.0.2 and BBC BASIC images. The native VDP's UART and decoded VSync connect to the eZ80 side. MOS boots, runs shell commands, reads a file, loads BBC BASIC, and runs a program that changes video mode, waits for vertical blank, and prints arithmetic and loop results. Both engines must produce the same pinned final VGA pixels.
Keyboard input in these gates uses Agon's supported serial-console mode.
Physical PS/2 keyboard/mouse input is not supported: FabGL uses the classic
ULP FSM coprocessor, which Flexe does not yet execute. The eZ80 peer uses its
upstream host filesystem service for SD access; this does not validate ESP32
SD hardware. Other video modes, audio features beyond the tested square wave,
and arbitrary applications still need coverage. The release still logs a
core-1 watchdog-removal warning.
The approximately 40,000-cycle panic_abort reported in
issue #2 was not reproduced with
this release or the locally built current source; identifying that failure
still requires the reporter's exact image and command line.
scripts/check-stock-roms.sh checks completion, meaningful i/o, scenario
effects, and matching output digests. known official image hashes also have
pinned output digests.
| firmware | interactions covered |
|---|---|
| bruce | ili9341 rendering, gpio-bit-banged xpt2046 menu navigation, gpio36 penirq, sd initialization and fat |
| marauder | ili9341/xpt2046 navigation, uart0 commands, independent uart2 gps, sd/filesystem, wi-fi receive/raw transmit boundaries, ble scan/advertising |
| meshtastic | axp192 power rails, sx1276 discovery/init, serial configuration, encrypted lora tx/loopback rx through radiolib interrupts, nimble sync/advertising, u-blox ubx/nmea position and time |
| nerdminer | wi-fi association/event delivery, captive-portal http/dns, host-backed pool networking, mining task execution |
| openhasp | lanbon l8 st7789v on cs22/dc21/sclk19, host tcp/telnet, three jsonl commands through dispatcher/lvgl to a deterministic framebuffer |
| tasmota | access-point startup, production http Status 0, berry commands toggling gpio4 |
| wled | access-point startup, udp port 21324, host dnrgb input parsed by firmware, 30-pixel grb output through rmt refill interrupts to a 40 mhz ws2812 waveform |
wi-fi/bluetooth service interactions in this table retain their service boundaries. see the hardware matrix before treating them as silicon or rf support.
s3/lx7 is a supported functional target with interpreter and jit execution. its production and stock-driver gates have their own pinned inputs.
| gate | demonstrated behavior |
|---|---|
check-s3-nerdminer-portal.sh |
portal configuration, spiffs save, software restart, and reload; host-backed socket service |
check-s3-marauder.sh |
official 1.16.0 multiboard image, native bluetooth/wi-fi initialization, absent-gps probe, injected help, next prompt, nonzero jit retirement |
check-s3-wled-rmt.sh |
pinned 16.0.1 image; matching completed led frames and final cpu/time state, nonzero jit retirement |
check-s3-wled-http.sh |
matching source-build image/elf; native lwip/ethernet web ui, json state change, readback; libslirp 4.9+ and jq required |
| stock arduino and esp-idf gates | native drivers exercise buses, timers, gpio, dma, audio, camera, adc, touch, crypto, storage, and sleep/wake within their declared scope |
the current nerdminer full replay and wled unsupported-mmio audit require zero unsupported sites. older filesystem-only milestones had thousands of unhandled accesses; they do not describe the current full replay.
exact s3 artifacts and hardware coverage → driver gate commands →
BRUCE_BIN=/path/to/bruce.bin \
MARAUDER_BIN=/path/to/marauder.bin \
MESHTASTIC_BIN=/path/to/meshtastic.bin \
NERDMINER_BIN=/path/to/nerdminer.bin \
OPENHASP_BIN=/path/to/openhasp.bin \
TASMOTA_BIN=/path/to/tasmota32.bin \
WLED_BIN=/path/to/wled.bin \
FLEXE_ROM_ELF=/path/to/esp32_rev300_rom.elf \
./scripts/check-stock-roms.shadditional positional images use the marauder profile and still require both engines to agree.
FLEXE_ROMS=/path/to/corpus ./scripts/check-firmware.shscripts/check-firmware.sh accepts positional .bin files or files directly
under FLEXE_ROMS. each engine must:
- reach the virtual-time budget without trapping or stopping early;
- retire enough instructions to demonstrate useful execution;
- avoid unhandled mmio and unregistered rom calls;
- stay within the unmapped-memory limit and finish at a fetchable pc;
- produce the same normalized uart digest.
BATCH defaults to 10,000; MAX_UNMAPPED defaults to 1,000.
this generic progress gate does not establish all board interactions.
the image's chip id and revision bounds select the machine.
| option | use |
|---|---|
--target auto |
default: detect the target |
--target esp32 / --target esp32s3 |
assert a target, useful in ci |
-R ROM_ELF |
load the official matching mask-rom code and data |
--efuse BLOB |
load a 336-byte efuse blob overriding s3 mac and chip revision (default: revision-0 profile) |
--strap-mode HEX |
raw GPIO_STRAP_REG sample for rom boot selection (0x07 = s3 uart0 download; default: spi boot) |
--uart-tcp HOST:PORT |
bridge uart0 to a tcp server; while a client is connected, uart rx/tx move off stdin/stdout |
--control-tcp HOST:PORT |
host control channel: reset, erase-flash, erase-region, write-region, ping |
--ble-hci tcp:HOST:PORT |
s3-only: forward NimBLE HCI to an external H4 controller; requires -s ELF |
--wifi-ssid SSID + --wifi-password PASS |
model a station network in the air; association then enforces the match |
--net-hostfwd ap|sta:HOST_PORT:GUEST_IP:GUEST_PORT |
s3-only user-mode Ethernet forward off host loopback; repeatable within one interface and /24 |
-s firmware.elf |
load application symbols for debugging and symbol-based services |
-N |
run the firmware's native freertos; use for s3 |
--psram ap-8m-opi |
opt into an 8 mib aps6408l-3obmx octal psram on s3 cs1 |
--usb-console |
use native usb serial/jtag output instead of uart0 |
# classic images that need mask-rom data
./build/xtensa-emu -R /path/to/esp32_rev300_rom.elf firmware.bin
# s3 native freertos with the matching rom
./build/xtensa-emu --target esp32s3 -N \
-R /path/to/esp32s3_rev0_rom.elf firmware.binboth rom runners accept --rom-elf; FLEXE_ROM_ELF supplies the classic
runner default. s3 gate scripts use S3_ROM_ELF.
official rom binaries are not copied into the repository.
hardware-facing hooks retain priority; other mask-rom calls execute the loaded instructions. native translation uses target descriptors and exact hook membership, rather than firmware names or pc lists.
--uart-tcp bridges uart0 to a tcp server (one client at a time; recabling
drops the previous client) and --control-tcp serves reset, erase-flash,
erase-region OFF LEN, write-region OFF FILE, and ping with one reply
per line. a control reset rebuilds the machine in-process with flash
preserved, so both servers and the output stream survive it; to boot a newly
flashed image normally, restart without --strap-mode. control flash
operations share the guest write path, including translated-code
invalidation. scripts/check-s3-remote-channels.sh gates replies, reboot
survival, and post-reset uart output on the bridge socket.
--ble-hci is s3-only and forwards the guest's genuine NimBLE HCI to an
external H4-speaking controller (Bumble) over tcp, injecting its
events/ACL back into the host through the firmware's own transport
entries, so GAP/GATT run natively and a real phone can scan, connect, and
walk the GATT table. it needs application symbols (-s ELF) for the HCI
transport hooks and stays clear of classic targets, which keep their
observer/virtual-HCI behavior. scripts/check-s3-ble-hci.sh (with
tools/ble_hci_peer.py, pip3 install bumble) gates a full phone session
against the IDF bleprph example: three SIG services plus the custom
service, reads, writes, notification subscribes, and a clean disconnect.
--wifi-ssid with --wifi-password models one station network in the air
for classic targets (s3 keeps its native wi-fi stack). without it every
association succeeds, exactly as before; with it, esp_wifi_connect()
reports unknown ssids as NO_AP_FOUND, a first wrong-password attempt as
AUTH_FAIL and retries as CONNECTION_FAIL, matching esp-emulator's
station model, and the provisioned network appears in scan results with
the bssid the association events report. esp_wifi_get_config() echoes
the firmware's own configuration once set and otherwise answers with the
modeled network as though nvs had saved it, which is what lets
portal-provisioned firmware connect with no explicit set_config, like
the classic wifi_client fixture does.
--net-hostfwd is repeatable: each flag adds one loopback listener to the
same user-mode network, so one guest can serve several host ports. every
forward must name the network's own interface (ap or sta) and an
address in its /24; the network stays restricted to loopback either way.
esptool over socket:// is blocked, not working: the s3 rom enters uart0
download mode correctly under --strap-mode 0x07 (the banner reports
boot:0x7 (UART0_BOOT)), but its startup unpack table copies dram
initializers from mask-rom bytes the official rom elf does not ship
(PT_LOAD file ranges there are all zeros), so the unpack zeroes the
interface state — including ets_ops_table_ptr — and the download stub
traps before speaking slip. qemu-style loaders see the same absence; it is
a property of the distributed elf, verified word-for-word against the
unpack table and section headers. host-side slip flashing without the rom
stub is future work; the transports above are ready for it.
| area | supported boundary | outside that claim |
|---|---|---|
| cpu and jit | common windowed lx6/lx7 execution; unsupported translation falls back to the interpreter | cycle/cache accuracy |
| two cores | shared memory, interrupts, and deterministic batches on one timeline | simultaneous host execution and sub-instruction contention |
| flash | image-declared capacity, real merged partitions, nor read/program/erase, mmu mapping, session reset persistence | wear, interrupted-write power behavior, alternate ota-slot boot |
| optional s3 psram | profiled 8 mib array and mode registers, cache-mmu mapping, reset retention | calibrated dqs/refresh timing and other device profiles |
| peripheral drivers | register, dma, interrupt, and host-endpoint behavior demonstrated by each gate | all modes, physical wires, and calibrated latency |
| network/radio workflows | declared controller and host-service boundaries | rf/phy or general wi-fi/bluetooth equivalence |
| reset and sleep | target-specific retention, modeled wake sources, explicit reset behavior | all physical reset domains, brownout/ulp, or electrical transitions |
flash can grow up to 16 mib on classic esp32 and 128 mib on s3. guest writes
survive a session reset; they are not automatically saved to the input
.bin or retained across a new process.
for register details, timings, algorithms, exact hashes, and per-device gaps:
the firmware's own overflow/underflow vectors run by default. that keeps
setjmp/longjmp, dynamic stacks, callback frames, and wrapped register
files on the hardware abi save areas.
legacy diagnostic switches
FLEXE_WINDOW_VECTORS=0 selects the synthesized spill/fill path.
FLEXE_SHADOWFILL=1 enables the old shadow-record restore shortcut.
both shortcuts are off by default.
address-based wi-fi/bluetooth hooks require verified image fingerprints and
entry points checked against a symbol-bearing build from the same sdk/core.
use build/xt-dis -a ADDR -n LEN firmware.bin to inspect a stripped image.
the implementation notes also describe structural critical-section acceleration and its fallback rules.