Repository navigation
ESP32 on the machine framework: LX6 cores, SoC models, DevKitC V4, ESP-IDF boots - #116
Merged
teo1747 merged 16 commits intoNov 8, 2026
Merged
Conversation
…ots ESP-IDF Two LX6 cores (core/mcu/xtensa/lx6.c, lx6fp.c) as core-isa.h configures the ESP32: windowed registers, loops, XEA2 exceptions, seven interrupt levels, CCOUNT/CCOMPARE timers, MAC16, booleans, single-precision FPU, region protection, the debug option; backend "xtensa" in build.c. The ESP32 description (catalog/mcus/espressif/esp32): the memory map, the ROM image built from Espressif's esp32_rev300_rom.elf (equal to QEMU's esp32-v3-rom.bin), the register file from esp32.svd, and models for DPORT (APP_CPU control, caches and flash MMU, interrupt matrix, the APP_CPU's own ROM view), RTC_CNTL, eFuse, APB_CTRL clocks, GPIO/IO_MUX with strapping, UART0-2, TIMG0/1, SPI0/1 and the GT25Q32A flash, SHA, the internal analog I2C, RNG and the AHB view. Espressif's ROM, ESP-IDF v5.4's second-stage bootloader and hello_world run unmodified to Hello world!
- emblink/esp32_devkitc: the DevKitC V4's power path (BAT760, AMS1117-3.3), power LED, Boot and EN buttons with EN's RC, both 19-pin headers, from Espressif's schematic; three fixtures - test_lx6: programs assembled by Espressif's assembler (gen_lx6_programs.py -> lx6_programs.h) check the core's instructions, windowed recursion through the ROM's window vectors, exceptions, the timer interrupt, FPU, MAC16, and the APP_CPU's ROM data - tests/vendor/esp-idf: esp32_hello_world built for the esp32 target - the description's fidelity and provenance, CHANGELOG, STATUS, coverage - a zero-overhead loop is not taken back when its last instruction branches to LEND (the ROM's strcmp); the DFP accelerator's user registers; no debug hooks in the core
The GPIO LED on GPIO2 (sdkconfig.defaults), an LED and 330 ohm on IO2: gpio_set_level through the matrix every second, 6/6.
run.py skips an example whose toolchain is missing rather than every example, and CI installs xtensa-esp32-elf with riscv32-esp-elf so the esp32_* examples run on the ESP32-DevKitC.
Eight high-speed and eight low-speed channels on four timers each (core/mcu/esp32/soc/ledc.c, the ESP32-C3's model on the ESP32's registers): REF_TICK, APB_CLK or SLOW_CLK over the 10.8 divider, up to 20 bits of duty, high-speed settings at the next overflow and low-speed ones after PARA_UP, fading, the overflow and fade interrupts, the matrix's ledc_hs/ls_sig_out. ledc_basic's 4 kHz on GPIO5: 2/2.
Datasheet v5.3 table 4-2: Modem-sleep of the dual-core chips at 80, 160 and 240 MHz (the range's low end waiting, high end running), 1 uA held; table 5-4's radio currents for when the radios are modelled.
The ESP32's I2C controllers from the C3's bit-level model on the ESP32's registers: 16 commands with its op codes (RSTART 0, WRITE 1, READ 2, STOP 3, END 4), the bus timing in APB_CLK cycles as i2c_ll counts it (SCL high + 7, or the filter's share), ACK_ERR, arbitration, the SCL timeout, the FIFOs through DATA and its AHB view, master and slave. The GPIO model gains the signal watch the controller follows the lines with, and DPORT's PERIP_RST_EN resets I2C0, I2C1 and LEDC. ESP-IDF v5.4's i2c_eeprom passes unmodified on the DevKitC with a 24LC256 (esp32_i2c_eeprom 3/3).
tools/validate.py rejected the ESP32's description: the schema had no xtensa backend and capped a port at 32 pins (the ESP32's GPIO bank has 40), Espressif's SVD lists fields that overlap (a whole-register STATUS beside its parts, two names for one bit), there was no gcc-xtensa toolchain profile, and the fidelity's validation entries were prose. svd2regs.py gains --resolve-overlaps (a field containing another taken out, of fields over the same bits the first kept, recorded in source); the ESP32's register file is regenerated with it, WIFI_CLK_EN kept as one field and the SVD's stray fields in CPUSDIO_INT1 and SAR_MEAS_WAIT2 reserved. The board and the MCU have their French text.
Three more of ESP-IDF's examples run unmodified on the DevKitC: gptimer (TIMG0, 17/17), uart_echo on UART2 (6/6) and real_time_stats across both cores (10/10, with IDLE0 and IDLE1). uart_echo echoed 128 bytes for each line: on the ESP32, ESP-IDF's uart_ll_get_rxfifo_len counts the bytes waiting from MEM_RX_STATUS's read and write addresses (the DPORT read's FIFO count is not to be trusted), and with both at 0 takes a nonzero count for a full FIFO. The UART model now moves the two addresses with each byte.
The ESP32's remote control peripheral from the C3's model on the ESP32's registers: eight channels each sending or receiving through the matrix (rmt_sig_out0-7, rmt_sig_in0-7), 64-word blocks of the 512-word RAM (added to the register file with svd2regs' --memory: the SVD leaves it out), the clock from APB_CLK or REF_TICK, end markers, continuous mode, wrap, TX_LIM, the carrier, the receiver's idle threshold and filter, CHnDATA, three interrupt bits a channel. ESP-IDF's get-started/blink with its LED-strip option runs unmodified on the DevKitC with a WS2812B on IO18 (esp32_blink_strip 7/7).
SENS's RTC controller's one-shot conversions on ADC1 (GPIO36-39, 32-35) and ADC2 (GPIO4, 0, 2, 15, 13, 12, 14, 27, 25, 26): MEAS1/2_START_SAR, the attenuation per channel, 9 to 12 bits, the converter's inverted output and DATA_INV, DONE after the conversion's SAR clocks. The transfer is the line ESP-IDF's Vref calibration fits for the chip, with the chip's eFuse Vref, so the calibrated voltage matches the pin's. ESP-IDF's adc/oneshot_read runs unmodified on the DevKitC with three sources (esp32_adc_oneshot_read 6/6: 1118 for 1.1 V, 2402 for 2.2 V, 433 for 0.5 V on ADC2).
Five sketches compiled for "ESP32 Dev Module" with Arduino-ESP32 3.2.0 run unmodified (tests/vendor/arduino-esp32, ESP32_*): AnalogReadSerial and ReadAnalogVoltage on VP, DigitalReadSerial, GetChipID (ESP32-D0WD-V3 Rev 301, two cores) and RepeatTimer stopped by the board's Boot button. Arduino turns on FreeRTOS's end-of-stack watchpoint, and its loopTask stopped at once with "Stack canary watchpoint triggered": the core took DBREAKC's MASK as the address bits ignored, where its set bits are the low bits compared (ESP-IDF's 32-byte watchpoint writes 0x20). test_lx6 gains a store watchpoint over 32 bytes, missed just past it and taken inside it.
(u32)PADS cut a 64-bit constant, which MSVC's /W4 /WX rejects; the low word is taken with a mask instead.
The branch register's bit is unsigned, the comparison's result an int: MSVC's /W4 /WX rejects the mixed ==.
RTC_IO's PAD_DAC1 and PAD_DAC2: with the pad the RTC IO MUX's and the DAC powered, GPIO25 and GPIO26 carry VDD3P3_RTC x code / 255 (the GPIO model hands the pad to the DAC as an analog output). ESP-IDF's dac/dac_oneshot runs unmodified on the DevKitC, its ADC2 reading both DACs back on their pads (esp32_dac_oneshot 4/4). For that the runtime's pin_volts gives a pin's own DAC voltage when its net has no analog part (it gave the digital level, so an ADC read 0).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Finishes the ESP32 work started in #115 by bringing the chip into the catalog and the machine framework, so the ESP32 runs like the other boards.
What it adds
core/mcu/xtensa/lx6.c,lx6fp.c, backendxtensa), configured as Espressif's core-isa.h describes:esp32-v3-rom-app.binbyte for byte.espressif/esp32:esp32_rev300_rom.elf(equal to QEMU'sesp32-v3-rom.bin)esp32.svd, with the SVD's overlapping fields resolved (svd2regs.py --resolve-overlaps, new) and the RMT RAM addedemblink/esp32_devkitc(ESP32-DevKitC V4, WROOM-32E), built from Espressif's schematic: power path, LED, Boot/EN buttons, headers, and three fixtures.xtensabackend, ports up to 64 pins (the ESP32's 40-GPIO bank); thegcc-xtensatoolchain profile.pin_voltsgave the digital level).Evidence
tests/vendor/esp-idf,esp32_*):esp32_hello_world9/9: ROM banner,chip revision: v3.1,Multicore app, 160 MHz,Hello world!,esp_restart→rst:0xc (SW_CPU_RESET)esp32_blink6/6,esp32_ledc_basic2/2 (4 kHz on GPIO5)esp32_i2c_eeprom3/3: a 24LC256 through I2C0 at 400 kHzesp32_gptimer17/17,esp32_uart_echo6/6 (UART2),esp32_real_time_stats10/10 (both cores)esp32_blink_strip7/7: a WS2812B through the RMTesp32_adc_oneshot_read6/6: ADC1 and ADC2 with the eFuse Vref calibration (1.1 V → 1118 → 1100 mV)esp32_dac_oneshot4/4: both DACs read back by ADC2tests/vendor/arduino-esp32,ESP32_*): AnalogReadSerial, ReadAnalogVoltage, DigitalReadSerial, GetChipID, RepeatTimer.test_lx6: assembled by Espressif's assembler (gen_lx6_programs.py), 0 failures; covers recursion through the ROM's own window vectors, APP_CPU's separate ROM data and a DBREAK watchpoint.-Wconversion -Wsign-conversion -Werror), ci-tcc and MSVC (/W4 /WX) build clean.check_docs,check_banned,validate.py --allandcatalog_text.py checkpass, and coverage is regenerated.docs/gallery/esp32-devkitc.Not yet
These are declared in the description's fidelity: