ESP32-S3
The Espressif ESP32-S3 (dual-core Xtensa LX7, 512 KB SRAM, external QSPI
flash, Wi-Fi + BLE 5, USB) is LabWired's reference Xtensa target. All
declared peripherals are declarative — the chip yaml references
external YAML descriptors under configs/peripherals/esp32s3/.
There are two chip yamls for ESP32-S3 today:
configs/chips/esp32s3.yaml— minimal declarative model (UART0 + GPIO + TIMG0 + INTERRUPT_CORE0) used by older blinky / hello-world examples.configs/chips/esp32s3-zero.yaml— fuller "Plan 2/4" model used by theesp32s3-i2c-tmp102demo; adds I²C0, SYSTIMER, USB-Serial-JTAG, RTC_CNTL stub, eFuse stub, IRAM + ROM-thunks + flash-XIP mapping. Use this variant for new ESP32-S3 work — the simulator'sconfigure_xtensa_esp32s3is the authoritative wiring code that this yaml documents.
Status at a glance
Live status: the table below is a hand-maintained snapshot. For the authoritative, auto-generated view see the chip conformance scoreboard (level · modelled peripherals · register-match vs silicon) and the tier-1 matrix (per-peripheral pass/fail).
| Aspect | Status |
|---|---|
| Chip yamls | esp32s3.yaml (minimal) · esp32s3-zero.yaml (Plan 2/4) |
| System yaml | configs/systems/esp32s3-zero.yaml |
| Reference firmware | examples/esp32s3-i2c-tmp102/ (Plan 4, canonical) · examples/esp32s3-blinky/ · examples/esp32s3-hello-world/ |
| Validation | Plan 4 end-to-end test: crates/core/tests/e2e_i2c_tmp102.rs |
| Tier | structural — declarative + Plan 2/4 peripheral models |
Peripherals (esp32s3-zero, Plan 2/4 model)
| Peripheral | Base | Status | Notes |
|---|---|---|---|
| Xtensa LX7 | — | ✅ modeled (dual) | PRO_CPU + APP_CPU round-robin (Machine::with_secondary_cpu); FPU coprocessor not modeled |
| IRAM | 0x40370000 | ✅ ram region | 512 KiB |
| ROM thunks | 0x40000000 | ✅ thunk bank | 384 KiB internal ROM call sites |
| Flash (I-cache) | 0x42000000 | ✅ XIP mapping | 4 MiB |
| Flash (D-cache) | 0x3C000000 | ✅ XIP mapping | 4 MiB |
| USB Serial/JTAG | 0x60038000 | ✅ modeled | 4 KiB — used as console in Plan 4 demo |
| SYSTIMER | 0x60023000 | ✅ modeled | 4 KiB — periodic alarm in Plan 4 demo |
| SYSTEM | 0x600C0000 | ⚙ stub | 4 KiB |
| RTC_CNTL | 0x60008000 | ⚙ stub | 4 KiB |
| eFuse | 0x60007000 | ⚙ stub | 4 KiB |
| I²C0 | 0x60013000 | ✅ modeled | 4 KiB — round-trip exercised in esp32s3-i2c-tmp102 |
| GDMA | 0x6003F000 | ✅ modeled | 5 channels; M2M descriptor walks and peripheral-coupled byte movement (see below) |
| SPI2 / SPI3 | 0x60024000 / 0x60025000 | ✅ modeled | GP-SPI master: CPU W-buffer path + GDMA-coupled DMA transactions; device attach surface (e-paper e2e) |
| I²S0 / I²S1 | 0x6000F000 / 0x6002D000 | ✅ modeled | Config/control twin + GDMA-coupled sample streaming (RXEOF_NUM byte semantics) |
| LCD_CAM | 0x60041000 | ✅ modeled (LCD TX + CAM RX) | Config/control twin + i80 pixel streaming: LCD_CMD_VAL command phase and the GDMA outlink payload drive an attached parallel panel. CAM slave: an attached DVP sensor (ov3660) streams frames into the GDMA inlink with the VSYNC / IN_SUC_EOF sequence esp32-camera counts (see Camera). RGB-mode sync generation is not modelled |
| UART0/1/2 | 0x60000000 / 0x60010000 / 0x6002E000 | ✅ modeled | FIFO + STATUS + baud-paced drain; UART0 doubles as the UHCI0 DMA endpoint |
The authoritative wiring (configure_xtensa_esp32s3) additionally registers
LEDC, RMT, PCNT, TIMG0/1, SAR-ADC, TWAI, AES, SHA, RSA, HMAC, DS, MCPWM0/1,
SD/MMC, LCD_CAM and USB-OTG models — see
crates/core/src/system/xtensa.rs for the full list and fidelity notes.
The plain esp32s3.yaml (used by older examples) is a strict subset:
UART0 @ 0x60000000 + the declarative GPIO/TIMG0/INTERRUPT_CORE0 peripherals
under configs/peripherals/esp32s3/.
See docs/declarative_registers.md for
how declarative peripheral descriptors work.
DMA (GDMA)
The GDMA model (crates/core/src/peripherals/esp32s3/gdma.rs) moves real
bytes:
- Memory-to-memory (
MEM_TRANS_EN): full descriptor-chain walks with owner-bit writeback. - Peripheral-coupled mode (routed by
IN_PERI_SEL/OUT_PERI_SEL): UART (UHCI0), SPI2, SPI3, I²S0 and I²S1 transfer real bytes between descriptor chains and the peripheral models — incremental 64-byte-per-tick pumps, per-descriptor owner/length writeback, per-direction EOF interrupts (sources 66–75). - LCD_CAM OUT (
OUT_PERI_SEL = 5): pixel streaming for theesp_lcdi80 driver. The outlink chain feeds the LCD_CAM i80 master, which drives an attached 8080 parallel panel (ili9341-16bit) with the command word fromLCD_CMD_VALand the DMA payload as data. The walk honourssuc_eof, andLCD_TRANS_DONEis withheld until the chain drains so the driver cannot remount descriptors mid-transfer. - LCD_CAM IN (
IN_PERI_SEL = 5): camera receive. The CAM slave hands the attached sensor's bytes to the inlink as they arrive at the PCLK rate;IN_SUC_EOFlatches everyCAM_REC_DATA_BYTELEN + 1bytes (or at VSYNC withCAM_VS_EOF_EN), closing the current descriptor with its length andsuc_eof.IN_CHECK_OWNERis honoured, so the driver's circular ring keeps filling on its second lap, andIN_LINK.STOPends the receive. - Other peripheral ids (AES, SHA, ADC, RMT, unknown/unbound) keep an auto-complete fallback: EOF latches without byte movement so polling firmware progresses.
Camera (DVP)
An ov3660 part (configs type ov3660, kit in
crates/core/src/peripherals/components/ov3660.rs) puts a camera on the
board: its SCCB port answers at 0x3C on the I²C controller named by
connection: (esp32-camera uses i2c1), and its DVP output is bound to
LCD_CAM's CAM slave.
A camera named on i2c0 with a pad route: (what the Studio canvas emits)
works too. Any S3 I²C controller can drive any pad through the GPIO matrix,
so the camera also answers the other controller, exactly while the firmware
routes that controller's SCL/SDA output signals (FUNCn_OUT_SEL_CFG, I2CEXT1
= 91/92) to the camera's pads. esp32-camera opens I2C1 and routes it to
SIOC/SIOD, so it finds the camera however the lab names the bus:
Unmodified esp32-camera firmware runs against it: probe and chip ID
(0x3660), the sensor init table, set_framesize / set_pixformat, then
esp_camera_fb_get() returning frames in internal RAM or PSRAM. Frames
follow the window (0x3808–0x380B) and format (0x4300: RGB565, YUV422, Y8)
the driver programs, or are baseline JPEG (below). Checked with Espressif's
take_picture example (RGB565 and JPEG, QVGA, one PSRAM frame buffer) and a
two-buffer CAMERA_GRAB_LATEST preview to an ST7789.
The picture is synthetic and driven by stimulus: pattern (0 colour bars,
1 checkerboard, 2 gradient, 3 flat), brightness (0–100 %) and motion
(pixels the scene scrolls per frame). The sensor's own colour-bar bit
(0x503D[7]) overrides the stimulus, as on silicon.
JPEG (0x3821[5]): each frame is a real baseline JPEG of the scene, YCbCr
4:2:2 at the programmed window, quantised by COMPRESSION_CTRL07 (0x4407,
the QS set_quality writes). It is sized for the driver: esp32-camera
reports a PSRAM JPEG frame as (EOFs + 1) × 1 KiB and looks for FF D9 only in
that last block, so the stream is zero-padded to one byte short of the next
1 KiB boundary, and the one length that could never be found (an exact
multiple of 1 KiB) gets a 4-byte COM segment. esp_camera_fb_get() returns
the trimmed JPEG; decoders accept it. Not the OV3660's own encoder, so sizes
are representative, not bit-exact.
The camera pins in the example are Espressif's CAMERA_MODEL_ESP32S3_EYE
map (arduino-esp32 camera_pins.h), the one ESP32-S3 N16R8 camera boards
are documented against.
Timing: one byte per 20 MHz PCLK, a 200 µs back porch and 1 ms of
blanking. Two guards keep the driver's event count intact when the
simulator fast-forwards idle time: VSYNC waits until a captured frame has
been handed to GDMA, and GDMA moves no bytes past an IN_SUC_EOF the CPU
has not acknowledged.
RTC IO MUX
rtc_io (0x6000_8400) models the RTC IO MUX and RTC GPIO: GPIO0..21 =
RTC_GPIO0..21 at bit 10 + n. A pad whose MUX_SEL is set is driven by
RTC_GPIO_OUT / RTC_GPIO_ENABLE (open drain with RTC_GPIO_PINn.PAD_DRIVER,
RUE / RDE pulls) instead of GPIO_OUT / GPIO_ENABLE and the GPIO
matrix, and RTC_GPIO_IN / GPIO_IN read it through its FUN_IE;
RTC_GPIO_PINn.INT_TYPE latches RTC_GPIO_STATUS, which reaches no CPU
interrupt (none on silicon either). The pad holds (RTC_CNTL_PAD_HOLD_REG)
are not modelled. See GPIO nets.
Not yet modeled (commonly expected on ESP32-S3)
Still absent from the model: WIFI / BT controller, XTS_AES, TEMP_SENSOR, APB_CTRL, PSRAM cache controller, FPU coprocessor.
Firmware that touches unmapped registers will hit
MemoryAccessViolation or stall in a polling loop. See
docs/getting_started_firmware.md.