SearchA-ZE › ESP32-S3

ESP32-S3

2020 Open source · CC0 On-board only Online

ESP32-S3 is Espressif's popular dual-core WiFi and Bluetooth microcontroller, emulated in the browser on a from-scratch Tensilica Xtensa LX7 interpreter, part of the Xtensa family. Xtensa is a RISC-like ISA with a windowed register file (a0-a15 rotate over a larger physical AR file via WINDOWBASE/WINDOWSTART, driven by CALL4/8/12 + ENTRY + RETW), variable-length 24-bit and 16-bit (density) instructions, a shift-amount register with zero-overhead LOOP registers, and PS/EPC1/EXCCAUSE for exceptions. The core is wired to an ESP32-S3 SoC model at the real hardware addresses: internal SRAM, XIP flash, the GPIO block with the on-board LED on GPIO48, and UART0. It runs REAL bare-metal Xtensa firmware, including an interactive UART REPL you can type into. Because the whole chip is ordinary JavaScript it plugs into the site's shared debugger: a from-scratch Xtensa disassembler, live a0-a15 + PC + the special registers, side-effect-free memory, single-instruction step, execution breakpoints and write-watchpoints. WiFi and BLE have no radio here and are HLE stubs, only core0 is run, and the devkit is silent.

ESP32-S3 at Espressif ↗

Runs on: Web browser

ESP32-S3 Online Emulator

Play ESP32-S3 using JavaScript directly in your browser.

Configurations

ConfigurationEmulatorMachineOSLegal
RGB LED colour cycleESP32-S3ESP32-S3openOpen ⛶
Mini-REPL (UART0)ESP32-S3ESP32-S3openOpen ⛶
RGB-LED blinkESP32-S3ESP32-S3openOpen ⛶
Hello UART (serial)ESP32-S3ESP32-S3openOpen ⛶

Chips

Notes

Embedding

The CPU and SoC are written from scratch for this site (public domain / CC0) — nothing here wraps a third-party core. xtensa-esp32s3.js is adapted from the site's ESP32 (Xtensa LX6) core: the LX7 the ESP32-S3 uses shares the same windowed register file, the 24/16-bit density encodings, L32R, the SAR shifts, the 32-bit multiply and the special registers, so the CPU interpreter is reused unchanged and what differs is the SoC — the ESP32-S3 memory map and peripheral layout (including the bank-1 GPIO registers, because the S3 has GPIO0-48). It is all inspectable JavaScript state, which is exactly what lets the shared debugger drive it.

var sys = window.ESP32S3.createSystem();        // Xtensa LX7 core + ESP32-S3 SoC
sys.loadImage(bytes, 0x40370000);            // flat app image into IRAM
sys.setEntry(0x40370000);                    // enter at the image start
sys.onUartTx = function(b){ /* UART0 TX -> console */ };
sys.feedRx(byte);                              // keyboard -> UART0 RX FIFO
sys.getPixel();                                // decoded WS2812 RGB LED (GPIO48)
sys.step();                                    // execute exactly one instruction

We own the run loop so the debugger can drive it. Each frame runs a time-boxed budget of sys.step() (one Xtensa instruction each); breakpoints stop it by PC, write-watchpoints by bus address. Everything is plain JavaScript state:

MemberKindWhat it does
sys.step()methodFetch → decode width (16-bit density vs 24-bit) → execute one instruction, advancing pc and wrapping the zero-overhead LOOP. The single-step primitive.
sys.ar(i) / sys.setAr(i,v)methodThe current window's a0-a15, resolved through WINDOWBASE over the 64-register physical AR file. sys.getPc()/setPc() and sys.sr (sar/ps/windowbase/windowstart/lbeg/lend/lcount/epc1/exccause) complete the programmer's model — all readable and writable.
sys.mem.sramI / .sramD / .flash / .rtcfieldThe real memories as typed arrays — read side-effect-free for the hex + disassembly views.
sys.read8/16/32, sys.write8/16/32methodThe bus, decoding the ESP32-S3 memory map. The write path notes watched addresses and decodes the WS2812 edge on GPIO48.
sys.getPixel()methodThe on-board RGB LED — the 24-bit GRB colour decoded from the bit-banged WS2812 waveform on GPIO48, polled each frame to draw the LED.
sys.feedRx(b)methodSerial RX in — the keyboard drives the mini-REPL through UART0.

Debugger integration

The plug-in (esp32-s3-debug.js) describes the ESP32-S3 to the shared debugger and nothing more — the core is not patched:

  • Reused Xtensa decoder. The ESP32-S3's LX7 uses the same instruction encodings as the LX6, so the shared from-scratch disassembler at /debugger/src/cpus/xtensa.js (registered as xtensa) is reused unmodified; the CPU is shown as "Xtensa LX7 (ESP32-S3)". It decodes the variable-length ISA (24-bit when the low nibble op0 is 0-7, 16-bit density forms when 8-13) — the ALU/shift/load-store/branch set, L32R, the windowed and CALL0 calls (CALL0/4/8/12, CALLX, ENTRY, RET, RETW), RSR/WSR/XSR and the narrow forms — and matches the interpreter's encodings exactly.
  • Registers are read live each refresh: the current window's a0-a15 (resolved through WINDOWBASE), the pc, and the special registers SAR, PS, WINDOWBASE, WINDOWSTART, the LBEG/LEND/LCOUNT loop registers and EPC1/EXCCAUSE. Each has a set() that writes straight back through the core. Single-stepping the Hello firmware's windowed CALL4/ENTRY/RETW visibly rotates WINDOWBASE.
  • Memory is exposed as four chips, all read side-effect-free directly off the typed arrays, each with its real base so the disassembly lines up with PC: IRAM at 0x40370000 (disassembled), DRAM at 0x3FC88000, XIP flash at 0x42000000 (disassembled), and RTC memory at 0x50000000. The UART/GPIO peripheral window is deliberately not a chip, because reading the UART FIFO pops a byte — memory reads stay side-effect-free.
  • Single step is one sys.step(). Breakpoints are host-side checks of the PC (in the running code chip's offset space, so a click in the disasm gutter halts) run instruction-by-instruction when any are set. Write-watchpoints are a set the SoC's write path checks, pausing the loop the moment a watched bus address is written.

Architecture

The Espressif ESP32-S3 (2020) is a low-cost WiFi + Bluetooth LE microcontroller built on a dual-core 32-bit Tensilica Xtensa LX7:

  • Xtensa LX7 — two 32-bit cores at up to 240 MHz. Like the ESP32's LX6, the LX7 is a configurable RISC-like ISA with a windowed register file (the visible a0-a15 rotate over a larger physical AR file via WINDOWBASE/WINDOWSTART, driven by CALL4/8/12 + ENTRY + RETW), variable-length 24-bit and 16-bit (density) instructions, a shift-amount register (SAR) with zero-overhead LOOP registers (LBEG/LEND/LCOUNT), and PS / EPC1 / EXCCAUSE for exceptions. Because the LX7 shares these with the LX6 for the code this firmware runs, the site's from-scratch Xtensa core is reused and only the SoC differs.
  • Memory — internal SRAM reachable from the instruction bus at 0x40370000 (IRAM) and the data bus at 0x3FC88000 (DRAM); code also executes in place (XIP) from external SPI flash mapped at 0x42000000 (and rodata at 0x3C000000); a small RTC memory sits at 0x50000000. All are at their real hardware addresses — note how they differ from the classic ESP32.
  • On-board RGB LED — an ESP32-S3-DevKitC carries a single addressable WS2812 RGB LED on GPIO48. GPIO48 lives in the GPIO block's bank-1 registers (GPIO_OUT1 / GPIO_ENABLE1 at 0x60004010/0x6000402C and their W1TS/W1TC set/clear pairs). The firmware bit-bangs the one-wire 800 kHz waveform and the SoC decodes the HIGH-pulse widths back into a 24-bit GRB colour.
  • SerialUART0 at 0x60000000 is the console the ROM bootloader and apps log to; its TX FIFO drives the serial panel and its RX FIFO (read through UART_STATUS' RXFIFO_CNT) is fed by the keyboard.

The bundled firmware is real Xtensa LX7 machine code authored for this site (CC0), assembled to flat ESP32-S3 images and run on the from-scratch interpreter. The default is an RGB-LED colour cycle that bit-bangs the on-board WS2812; the picker also has an interactive mini-REPL that evaluates integer expressions over UART0 — because it is real machine code on the real memory map, every instruction single-steps in the debugger and led on visibly lights the on-board RGB LED.

Honest limits. This is a deliberate honest partial. The from-scratch core runs real Xtensa LX7 and drives the real GPIO / UART, but the full ESP-IDF / FreeRTOS + WiFi/BLE stack is out of scope: only core0 is run (core1 is not modelled), the WiFi and BLE radios have no RF — their register windows are RAM-backed HLE stubs so probing firmware does not hang, but no packets move — and the watchdog is fed so nothing resets. The content is therefore bare-metal, not the full MicroPython/ESP-IDF image; timing is instruction-approximate, not cycle-exact; and the bare devkit has no on-board audio, so this emulator is naturally silent.