ESP32-C6
ESP32-C6 is Espressif's WiFi 6 + Bluetooth LE + 802.15.4 (Thread / Zigbee) microcontroller, emulated in the browser on a from-scratch RISC-V RV32IMAC interpreter (the RV32I base plus the M multiply/divide, A atomics and C compressed extensions, the machine-mode CSRs and the trap and interrupt machinery). The core is wired to an ESP32-C6 SoC model at the real hardware addresses: 512 KB unified HP SRAM, XIP flash, the GPIO block with the DevKitC-1 addressable WS2812 RGB LED on GPIO8, UART0, the systimer and a functional interrupt matrix. It runs REAL bare-metal firmware, including a WS2812 RGB cycle and an interactive UART REPL you can type into. Because the whole chip is ordinary JavaScript it plugs into the site's shared debugger: the real RISC-V disassembler, live x0-x31, pc and the machine CSRs, side-effect-free memory, single-instruction step, execution breakpoints and write-watchpoints. WiFi 6, BLE and 802.15.4 have no radio here and are HLE stubs, so the emulator is silent.
Runs on: Web browser
ESP32-C6 Online Emulator
Play ESP32-C6 using JavaScript directly in your browser.
Controls
Configurations
| Configuration | Emulator | Machine | OS | Legal | |
|---|---|---|---|---|---|
| MicroPython v1.24.1 | ESP32-C6 | ESP32-C6 | open | Open ⛶ | |
| RGB cycle (WS2812) | ESP32-C6 | ESP32-C6 | open | Open ⛶ | |
| Rainbow (WS2812 hue wheel) | ESP32-C6 | ESP32-C6 | open | Open ⛶ | |
| Mini-REPL (UART0) | ESP32-C6 | ESP32-C6 | open | Open ⛶ | |
| Hello UART (serial) | ESP32-C6 | ESP32-C6 | open | Open ⛶ |
Chips
Notes
Embedding
The CPU and SoC are written from scratch for this site (public domain / CC0) — nothing here wraps a third-party core. rv32-esp32c6.js is a plain RV32IMAC interpreter plus an ESP32-C6 SoC model at the real hardware addresses (from the ESP32-C6 TRM and the esp-idf soc.h / reg_base.h). It is all inspectable JavaScript state, which is exactly what lets the shared debugger drive it.
var sys = window.ESP32C6.createSystem(); // RV32IMAC core + ESP32-C6 SoC
sys.loadImage(bytes, 0x40800000); // flat app image into HP SRAM
sys.setEntry(0x40800000); // enter at the image start
sys.onUartTx = function(b){ /* UART0 TX -> console */ };
sys.feedRx(byte); // keyboard -> UART0 RX FIFO
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 RV32IMAC instruction each); breakpoints stop it by PC, write-watchpoints by bus address. Everything is plain JavaScript state:
| Member | Kind | What it does |
|---|---|---|
sys.step() | method | Fetch → decode width (16-bit compressed vs 32-bit) → execute one instruction, advancing pc; services a pending interrupt first. The single-step primitive. |
sys.regs (Int32Array 32) | field | x0-x31 (x0 pinned to 0). sys.getPc()/setPc() and sys.csrObj (mstatus/mtvec/mepc/mcause/mie/mip/…) complete the programmer's model — all readable and writable. |
sys.mem.sram / .flash / .lpram | field | The real memories as typed arrays — read side-effect-free for the hex + disassembly views. |
sys.read8/16/32, sys.write8/16/32 | method | The bus, decoding the ESP32-C6 memory map. The write path notes watched addresses. |
sys.rgb / sys.ledOn() | field / method | The live 24-bit colour the SoC decoded from the bit-banged WS2812 waveform on GPIO8 — polled each frame to draw the on-board RGB LED. |
sys.feedRx(b) | method | Serial RX in — the keyboard drives the mini-REPL through UART0. |
Debugger integration
The plug-in (esp32c6-debug.js) describes the ESP32-C6 to the shared debugger and nothing more — the core is not patched:
- Reused decoder. The core runs RISC-V, so the existing
/debugger/src/cpus/riscv.jsdisassembler (RV32I + M + the C compressed forms) is reused as-is under the display name "RISC-V RV32IMAC (ESP32-C6)". No new decoder was written. - Registers are read live each refresh: x0-x31 with their ABI names, the pc, and the machine CSRs (mstatus, mtvec, mepc, mcause, mie, mip, mscratch, mtval, misa). Each has a
set()that writes straight back intosys.regs/sys.csrObj. - Memory is exposed as three chips, all read side-effect-free directly off the typed arrays, each with its real base so the disassembly lines up with PC: HP SRAM at
0x40800000(unified I+D bus, disassembled), XIP flash at0x42000000(disassembled), and LP SRAM at0x50000000. The 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-C6 (2023) is a low-cost WiFi 6 + Bluetooth LE + 802.15.4 (Thread / Zigbee) microcontroller built on a single 32-bit RISC-V HP core (a low-power second RISC-V core is not modelled here):
- RISC-V RV32IMAC — one 32-bit HP core at up to 160 MHz implementing the I base, the M (multiply/divide), A (atomics) and C (compressed) extensions, with machine-mode CSRs, a trap unit and an interrupt matrix. This emulator implements all four extensions, the CSR/trap machinery and a functional interrupt matrix from scratch.
- Memory — 512 KB on-chip HP SRAM, reachable from both the instruction and data bus at the same address
0x40800000(unified, unlike the C3's split IRAM/DRAM aliases); code also executes in place (XIP) from external SPI flash mapped at0x42000000; a small LP (RTC) SRAM sits at0x50000000. All are at their real hardware addresses. - On-board RGB LED — the ESP32-C6-DevKitC-1 fits an addressable WS2812 RGB LED on GPIO8, driven through the GPIO block at
0x60091000. Firmware bit-bangs the 800 kHz one-wire waveform; this model decodes the colour straight from the real GPIO edge timing (the same NeoPixel decode used by the Circuit Playground Express / Feather boards). - Serial — UART0 at
0x60000000is the console the ROM bootloader and apps log to; its TX FIFO drives the serial panel and its RX FIFO is fed by the keyboard.
The default is stock, unmodified MicroPython v1.24.1 (MIT), taken byte-for-byte from micropython.org. The emulator HLE-s the ESP32-C6 mask ROM (from public linker-script addresses) and boots the real ESP-IDF + FreeRTOS: clock/PLL bring-up over the analog I2C master, the SPI-flash controller + the 64 KB-page flash-cache MMU, the ROM TLSF heap and soft-float library, the interrupt matrix (PLIC) + systimer, the FreeRTOS scheduler and the partition table (verified by a real MD5) — reaching a live Python >>> REPL. time.sleep runs off the FreeRTOS tick, machine.Timer off the timer groups, neopixel drives the on-board LED through the RMT transmitter, and littlefs formats and mounts on the modelled NOR flash. Also bundled is original CC0 machine code (RGB / rainbow / mini-REPL / hello), compiled freestanding (clang --target=riscv32 -march=rv32imac). Because it is real machine code on the real memory map, every instruction single-steps in the debugger.
Honest limits. This is a deliberate honest partial. The from-scratch core boots stock MicroPython to a real REPL, but the WiFi 6, BLE and 802.15.4 radios have no RF — their register windows are RAM-backed HLE stubs and no packets move: network.WLAN().active(True) does not return, and bluetooth.BLE().active(True) crashes the firmware, which restarts. Hardware I2C and SPI are not modelled and their transfers do not return (SoftI2C and SoftSPI work, with nothing attached); machine.lightsleep() and deepsleep() do not return because the power-management unit is not modelled. The Reboot button recovers from all of these. machine.PWM accepts its settings but reads back duty_u16=0, ADC reads are noise and the MAC is zeros. Timing is instruction-approximate, not cycle-exact: Python's clocks agree with each other, but waiting jumps to the next alarm, so idle time runs faster than real time. This stock firmware works out machine.Timer periods from a 40 MHz clock while selecting the timer's 80 MHz source, so a timer fires at half the requested period. The filesystem lives in memory: it survives resets, not a page reload. The low-power second RISC-V core is not modelled, and the board has no on-board audio, so this emulator is naturally silent.