ESP32Sharp 0.1.0-beta.1

This is a prerelease version of ESP32Sharp.
dotnet add package ESP32Sharp --version 0.1.0-beta.1
                    
NuGet\Install-Package ESP32Sharp -Version 0.1.0-beta.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ESP32Sharp" Version="0.1.0-beta.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ESP32Sharp" Version="0.1.0-beta.1" />
                    
Directory.Packages.props
<PackageReference Include="ESP32Sharp" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add ESP32Sharp --version 0.1.0-beta.1
                    
#r "nuget: ESP32Sharp, 0.1.0-beta.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package ESP32Sharp@0.1.0-beta.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=ESP32Sharp&version=0.1.0-beta.1&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=ESP32Sharp&version=0.1.0-beta.1&prerelease
                    
Install as a Cake Tool

ESP32Sharp

A high-performance ESP32 emulator written in C# with NativeAOT optimization for iOS, Android, macOS, and Windows.

Features

  • Multi-ISA Support: Xtensa LX6/LX7 (ESP32, S2, S3) + RISC-V RV32IMC/IMAC (ESP32-C3, C6, H2)
  • NativeAOT Optimized: Zero JIT overhead, runs natively on iOS
  • Hardware Accurate: All register addresses and behavior sourced from Espressif TRMs
  • AOT-Safe Design: No reflection, no dynamic code generation, sealed classes for devirtualization
  • Windowed Register Support: Full Xtensa window overflow/underflow exception handling

Current Status (Phase 16 Complete)

  • 644 tests passing (0 failures; 7 firmware integration tests pass, 3 skip — blockers tracked below)
  • 179+ Xtensa opcodes implemented: full RRR/RRI8/BRI8/BRI12/CALL/RI16/CALLX format coverage + FPU + boolean coprocessor + debug exceptions + atomics
  • 19 peripherals: GPIO, UART, Timer (TIMG0/TIMG1), MWDT/RWDT watchdogs, Interrupts, SPI, I2C, DMA, LEDC, DPORT, eFuse, RTC_CNTL, SHA, ADC (12-bit/18ch), DAC (8-bit/2ch), RMT (8ch), I2S, PCNT, TouchPad, TempSensor
  • Complete ESP32 memory map: DRAM0/1, IRAM0/1, ROM, Flash, RTC FAST/SLOW, eFuse, RTC_CNTL, SHA, TIMG0, TIMG1, IROM, DROM (17 regions)
  • Flash MMU: 64-entry MMU with 64KB pages, IROM/DROM mapping; Esp32FirmwareLoader maps segments via mmu.MapIrom/MapDrom
  • ROM stubs: dispatch table at 0x4000_xxxx; ets_printf, SPIRead, heap_caps_malloc/free, ets_delay_us, Cache_Read_Enable
  • Firmware infra: partition table parser, firmware segment loader, SPIFFS reader, NVS key-value store, UartReplHost (bidirectional REPL I/O), Esp32HeapTracker
  • FPU/Coprocessor: FR0–FR15, BR (16-bit boolean), FCR/FSR; all FP arithmetic, conversions, comparisons, conditional moves

Boot blockers (firmware integration tests skipped until resolved)

Blocker Description Affected tests
B1 eFuse peripheral incomplete — chip-ID/MAC reads from 0x6001A000 not yet wired into boot sequence All IDF firmware
B2 RTC_CNTL reset reason register not initialised on reset All IDF firmware
B3 SHA accelerator computation stub — HMAC/eFuse uses SHA Secure boot
B4 Xtensa instruction coverage gap for IDF startup — audit needed All IDF firmware

Project Structure

ESP32Sharp/
├── src/ESP32Sharp/                     # Main library (single NuGet package)
│   ├── Core/                           # ISA-agnostic abstractions
│   │   ├── Memory/                     # IMemoryBus, Ram, Rom, BusInterconnect
│   │   ├── Cpu/                        # IIsaCore, CpuState
│   │   ├── Cache/                      # ICache/DCache simulation
│   │   ├── Mmu/                        # Memory Management Unit
│   │   ├── System/                     # Esp32MemoryMap
│   │   └── Decode/                     # IInstructionDecoder
│   ├── Xtensa/                         # Xtensa LX6/LX7 implementation
│   │   ├── XtensaCore.cs
│   │   ├── XtensaRegisterFile.cs
│   │   ├── Decode/                     # LUT decoder + predecode cache
│   │   ├── Instructions/               # Instruction handlers
│   │   └── Exceptions/                 # Window exceptions, interrupts
│   ├── RiscV/                          # RISC-V implementation (planned)
│   └── Peripherals/                    # Shared peripherals
│       ├── Gpio/
│       ├── Uart/
│       ├── Spi/
│       ├── I2c/
│       ├── Dma/
│       ├── Ledc/
│       ├── Flash/
│       ├── Timer/
│       ├── InterruptController/
│       └── System/                     # DPORT, eFuse, RTC_CNTL, SHA
└── tests/ESP32Sharp.Tests/             # xUnit tests

Getting Started

Prerequisites

Build

dotnet build

Run Tests

dotnet test

Usage Example

using ESP32Sharp.Core.Memory;
using ESP32Sharp.Xtensa;

// Create memory bus and RAM
var ram = new Ram(512 * 1024);  // 512KB
var bus = new BusInterconnect();
bus.RegisterDevice(0x3FFE_0000, ram);  // ESP32 DRAM base

// Create Xtensa CPU core
var core = new XtensaCore(bus, ram, 0x3FFE_0000, 512 * 1024);

// Execute instructions
core.Step();  // Execute one instruction
core.Step();

Architecture Principles

  1. TRM-First: Every register address, bitfield, and hardware behavior is cited from the Espressif TRM or Xtensa ISA Reference
  2. AOT-Safe: No Reflection, dynamic, Emit, or runtime code generation
  3. Zero Hot-Path Allocations: Predecode cache, readonly struct, Span<T>, stackalloc
  4. Sealed Everywhere: All concrete classes are sealed for AOT devirtualization
  5. ISA Isolation: Xtensa code in ESP32Sharp.Xtensa.*, RISC-V in ESP32Sharp.RiscV.*, shared code in ESP32Sharp.Core.*

Development Roadmap

Phase 1: Foundations ✅

  • Project structure with NativeAOT configuration
  • Core memory interfaces (IMemoryBus, Ram, BusInterconnect)
  • Core CPU abstraction (IIsaCore)
  • Xtensa register file with window support
  • XtensaCore stub
  • Basic tests

Phase 2: Decode Pipeline ✅

  • DecodedInsn struct + cache
  • Primary opcode LUT (delegate* array)
  • Basic instructions: ADD, MOV, L32I, S32I, MOVI
  • Test: execute synthetic "hello world" firmware

Phase 3: Window Exceptions ✅

  • Full XtensaRegisterFile with backing store
  • WindowOverflow4/8/12 exception handlers
  • WindowUnderflow4/8/12 exception handlers
  • ENTRY, RETW, ROTW implementation
  • Test: recursive function with window rotation

Phase 4: Essential Peripherals ✅

  • GPIO (output for LED blink)
  • UART (tx/rx basic)
  • Timer (periodic interrupts)
  • Interrupt controller and CPU dispatch

Phase 5: Peripheral Expansion ✅

  • SPI peripheral (SPI0/1/2/3) with transaction support
  • I2C peripheral (I2C0/I2C1) with command queue
  • DMA controller (13 channels, interrupt support)
  • LEDC (LED PWM Controller, 16 channels, hardware fade)

Phase 6: Memory Management & Flash ✅

  • ROM implementation (ESP32 bootloader ROM at 0x40000000)
  • Flash controller (SPI flash interface, 4MB, sector erase)
  • Complete DRAM/IRAM memory map (DRAM0/1, IRAM0/1, 520KB total)
  • DPORT registers for system control
  • Memory aliasing support (DRAM1/IRAM1 shared)
  • RTC memory (FAST + SLOW)
  • Memory Management Unit (MMU) for flash mapping (64 entries, 64KB pages)
  • Cache simulation (ICache/DCache with hit tracking)
  • Test: Boot real ESP32 ROM bootloader (deferred)

Phase 7: Advanced Exception & Interrupt Handling ✅

  • NMI (Non-Maskable Interrupt) support — RaiseNmi/ClearNmi, bypasses INTENABLE, vectors to VECBASE+0x2C0
  • Debug exception handling — DEBUGCAUSE, EPC6/EPS6, debug vector at VECBASE+0x280
  • Watch exceptions (IBREAK, DBREAK) — IBREAKA0/1, DBREAKA0/1, DBREAKC0/1 with load/store tracking
  • BREAK/BREAK.N — fire debug exception with DEBUGCAUSE.BREAK/BREAKN bits
  • RFNMI — return from NMI, restores EPC7/EPS7
  • ICOUNT — decrement counter with configurable level, fires debug exception on wrap
  • Correct SR register dispatch: WINDOWBASE=72, PS=230, DEPC=192, EPS2-7=193-198, EXCCAUSE=232, EXCVADDR=238
  • CCOUNT (SR 234) + CCOMPARE0/1/2 (SR 240-242) cycle counter with timer interrupt firing
  • INTENABLE/INTERRUPT/INTSET/INTCLEAR registers complete (SR 228/226/227)
  • Cross-core interrupts (ESP32 dual-core) — deferred to Phase 14
  • Test: Multi-level interrupt scenarios (30 tests)

Phase 8: Coprocessor & FPU ✅

  • FP register file: FR0–FR15 (InlineArray), BR (16-bit boolean), FCR, FSR
  • FP load/store: LSI, SSI, LSIU, SSIU, LSX, SSX, LSXU, SSXU
  • FP arithmetic: ADD.S, SUB.S, MUL.S, MADD.S, MSUB.S
  • FP conversions: FLOAT.S, UFLOAT.S, TRUNC.S, ROUND.S, FLOOR.S, CEIL.S, UTRUNC.S
  • FP unary/move: ABS.S, NEG.S, MOVE.S, WFR, RFR
  • FP compare→boolean: OEQ.S, UEQ.S, OLE.S, ULE.S, OLT.S, ULT.S, UN.S
  • FP conditional moves: MOVEQZ.S, MOVNEZ.S, MOVLTZ.S, MOVGEZ.S, MOVF.S, MOVT.S
  • Boolean coprocessor: ANDB, ANDBC, ORB, ORBC, XORB, ALL4, ALL8, ANY4, ANY8
  • Boolean AR conditional moves: MOVF, MOVT
  • WUR/RUR for FCR (UR 232) and FSR (UR 233)
  • SR 4 (BR) read/write via RSR/WSR
  • 64 new unit tests covering all FPU and boolean instructions, including NaN edge cases

Phase 9: System Integration & Firmware Boot ✅

  • Complete DRAM/IRAM memory map (DRAM0/1, IRAM0/1, RTC FAST/SLOW)
  • eFuse controller (chip ID, MAC address, calibration) — Esp32Efuse.cs
  • RTC/Sleep controller (power-on reset, APB clock, WDT key) — Esp32RtcCntl.cs
  • MWDT (Main Watchdog Timer) in TIMG0/TIMG1 — write-protect key (0x50F361A3), feed register, stage-0 timeout interrupt (TRM §18.3.5)
  • RWDT (RTC Watchdog Timer) in RTC_CNTL — write-protect key (0x50D83AA1), feed register, stage-0 timeout interrupt, INT_ENA/INT_RAW/INT_CLR (TRM §29.3.8)
  • RTC time counter — 48-bit counter, TIME_UPDATE snapshot, TIME0/TIME1 registers (TRM §29.3.2)
  • TIMG0/TIMG1 registered in Esp32MemoryMap at 0x3FF5_F000/0x3FF6_0000
  • 27 new unit tests covering MWDT, RWDT, RTC time counter, bus integration
  • Test: Boot to ESP-IDF “Hello World” firmware (deferred — requires full IDF boot sequence)

Phase 10: MicroPython Support ✅

  • Load MicroPython/app binary from flash via partition table (Esp32PartitionTable + Esp32FirmwareLoader)
  • REPL over UART — UartReplHost captures TX and injects RX bytes for REPL-style test interaction
  • Heap allocation simulation — Esp32HeapTracker records alloc/free events, tracks peak usage and counts
  • SPIFFS filesystem — Esp32Spiffs page-scanner reads files from SPIFFS-formatted flash partitions
  • 50 new unit tests: partition table parsing, firmware segment loading, SPIFFS file I/O, REPL I/O, heap stats
  • Test: Execute real MicroPython firmware end-to-end (deferred — requires Phase 13 Flash MMU)

Phase 11: Performance & Optimization ✅

  • Cache-first instruction fetch — TryGet before FetchInstruction; bus traversal only on miss
  • Single ReadWord in FetchInstruction slow path — replaces 3 separate ReadByte calls (3 binary searches → 1)
  • _debugActive guard — all three debug check blocks (IBREAK, DBREAK, ICOUNT) are short-circuited to a single branch when no debug SRs are armed
  • RunFor(int n) batch execution — runs N steps in a tight [AggressiveOptimization] loop; avoids repeated managed↔native transitions
  • Esp32BenchmarkMeasureMips(), MeasureMipsWarmed(), BenchmarkResult with cold/warm MIPS reporting
  • UpdateDebugActive() called from WriteSR so the guard flag tracks hardware state exactly
  • 14 new performance regression and benchmark tests (521 total)

Phase 12: Instruction Completeness Audit ✅

  • Audit XtensaExecute opcode table against complete Xtensa LX6 ISA encoding tables
  • Implement missing arithmetic: QUOS, QUOU, REMS, REMU (signed/unsigned divide), MULSH, MULUH, MUL16S, MUL16U
  • Implement missing bit manipulation: EXTUI, SEXT, CLAMPS, NSA, NSAU
  • Implement missing load/store: L8UI, L16SI, L16UI, S8I, S16I
  • Implement MEMW, EXTW (memory barriers), ISYNC, RSYNC, ESYNC, DSYNC
  • Implement SYSCALL, SIMCALL (syscall interface used by QEMU and IDF)
  • Implement remaining narrow (16-bit) instructions: ADD.N, ADDI.N, BEQZ.N, BNEZ.N, ILL.N
  • NSA/NSAU decoder paths — op2=4, r=14/15 (LLVM XtensaInstrInfo.td authoritative encoding)
  • SEXT/CLAMPS decoder paths — op1=3, op2=2/3 (correct register field extraction)
  • SIMCALL distinguished from SYSCALL by s-field in decoder
  • 29 new tests: NSA/NSAU semantics (boundary cases), CLAMPS (byte/short saturation), SIMCALL (NOP), binary decode verification
  • Synthetic Hello World CPU test — hand-encoded Xtensa (L8UI/BEQ/S8I/ADDI/J) drives UART output end-to-end (551 total tests)

Phase 13: Flash MMU / IROM+DROM Mapping ✅

Goal: Map IROM (0x400D_0000) and DROM (0x3F40_0000) segments directly from flash pages. This is the single biggest blocker for booting real firmware — MicroPython's .text lives in IROM.

  • Esp32Mmu.MapIrom(loadVirtualAddr, flashDataOffset, dataLen) — maps 64KB flash pages into IROM virtual window (ESP32 TRM §3.4, §7.3.14)
  • Esp32Mmu.MapDrom(loadVirtualAddr, flashDataOffset, dataLen) — same for DROM virtual window
  • FlashMmuDevice : IMemoryMappedDevice — sealed wrapper that reconstructs virtual addresses and delegates reads through Esp32Mmu page-table translation; writes silently discarded (read-only window)
  • Esp32MemoryMap.CreateMemorySystem() registers FlashMmuDevice at IROM/DROM bus windows instead of raw flash; default identity map preserves existing behaviour; Esp32MemorySystem exposes Mmu property
  • Esp32FirmwareLoader maps IROM/DROM segments via mmu.MapIrom/MapDrom (optional Esp32Mmu? param, backward-compatible); new SegmentsMapped counter
  • 19 new tests: MapIrom/MapDrom page-table correctness, FlashMmuDevice translation, MemorySystem integration, FirmwareLoader end-to-end — 570 total tests

Phase 14: ROM System Call Stubs ✅

Goal: Stub the ESP32 ROM functions that early-boot code calls before the IDF heap is set up.

  • Stub dispatch table at ROM addresses (0x4000_xxxx) — return safe defaults
  • ets_printfPrintfOutput event; ets_delay_us → CCOUNT advance
  • SPIRead(src, dst, size)Esp32Flash; Cache_Read_Enable → no-op
  • heap_caps_malloc/freeEsp32HeapTracker (via RegisterStub)
  • XtensaCore.SetRomStubs hook — single null-check in hot path
  • NVS peripheral (Esp32Nvs): in-memory key-value store mirroring esp_err_t
  • 33 tests — 603 total

Phase 16: ADC, DAC & Sensor Peripherals ✅

Goal: Implement the hardware peripherals that firmware's machine module (MicroPython) and IDF drivers access via memory-mapped registers.

  • ADC (12-bit, 18 channels) — Esp32Adc: SENS register block 0x3FF48800; ADC1 (ch0–7) + ADC2 (ch0–9); channel injection API
  • DAC (8-bit, 2 channels) — Esp32Dac: GPIO25/26; SENS_SAR_DAC_CTRL2_REG
  • RMT (8 channels) — Esp32Rmt: 64-word RAM/ch at 0x3FF56800; TX-start event; INT management
  • Touch sensor (10 pads) — Esp32TouchPad: threshold + raw-count registers; IsTouched(pad) API
  • I2S (2 units) — Esp32I2s: 64-deep TX/RX FIFO; CLKM/SAMPLE_RATE/FIFO conf; TxSample event
  • PCNT (8 units) — Esp32Pcnt: 16-bit signed counters; H/L threshold events; InjectPulses API
  • Temperature sensor — Esp32TempSensor: SENS_SAR_TSENS_CTRL_REG; ReadCelsius()
  • 31 tests — 644 total

Phase 17: Firmware Boot Blockers

Goal: Resolve the 4 blockers preventing real IDF firmware from booting. No C# simulation — only real peripheral and instruction implementations.

  • B1 — Complete eFuse boot sequence: chip-ID/MAC read path from 0x6001A000 wired into XtensaCore.Reset() + early-boot flow
  • B2 — RTC_CNTL reset reason register initialised on reset so esp_reset_reason() returns a valid value
  • B3 — SHA accelerator: implement SHA-256 computation registers (not just the register skeleton)
  • B4 — Xtensa instruction audit: trace IDF Hello World startup, identify and implement any missing opcodes
  • Un-skip WokwiFirmwareTests.HelloWorld once all 4 blockers are resolved
  • Baseline: IDF Hello World prints to UART0 and halts cleanly

Phase 18: Real MicroPython Firmware Boot

Goal: Boot the real MicroPython .bin image through XtensaCore — no C# simulation. Real .text code in IROM executes instruction-by-instruction. The REPL runs inside the firmware.

  • Load Wokwi MicroPython UF2 into Esp32Flash via Uf2Loader
  • Parse partition table → locate app at 0x10000Esp32FirmwareLoader maps IROM/DROM
  • Boot through Esp32FirmwareHarness up to >>> prompt on UART0
  • Inject 1+1\r\n via UartReplHost — assert UART0 output contains 2
  • machine.Pin(2, OUT).on() via REPL → assert GPIO bit 2 set in Esp32Gpio.OutputState
  • machine.ADC(34).read() via REPL → returns integer from ADC1 channel 2
  • Un-skip WokwiFirmwareTests.MicroPython when passing
  • Any blocker (missing instruction, missing peripheral) → implement it, not a workaround

Phase 19: WiFi & Bluetooth MAC Emulation

Goal: Emulate the ESP32 WiFi and Bluetooth MAC layer so firmware using esp_wifi / esp_bt APIs can boot without crashing on missing peripherals.

  • WiFi MAC peripheral (Esp32WifiMac): register block at 0x3FF73000 (TRM §10); WIFI_MAC_CTRL_REG, TX/RX queue registers, interrupt delivery
  • WiFi PHY stub: packet injection API for test injection of 802.11 frames; PHY register block at 0x3FF70000
  • esp_wifi_init / esp_wifi_start ROM stub hooks → hand off to Esp32WifiMac
  • BT/BLE controller peripheral (Esp32BtController): register block at 0x3FF6E000; HCI command/event FIFO; ACL data path
  • esp_bt_controller_enable ROM stub hook → hand off to Esp32BtController
  • LWIP integration: TCP/IP packet loopback via packet injection API (no real networking — packets are injected/captured in tests)
  • Test: firmware calling esp_wifi_init does not crash; WiFi station connect sequence captured as packet trace
  • Test: BLE advertisement packet injected → firmware callback fires

Phase 20: RISC-V Support (ESP32-C3/C6/H2)

  • RiscVCore : IIsaCore (RV32IMC decoder + executor) in ESP32Sharp.RiscV.*
  • RISC-V interrupt controller (CLIC for ESP32-C3/C6)
  • Reuse ESP32Sharp.Peripherals.* (GPIO, UART, SPI, I2C, Timer)
  • Test: ESP32-C3 Hello World firmware boots to UART output

Phase 21: Dual-Core Emulation (PRO_CPU + APP_CPU)

  • Second XtensaCore instance (APP_CPU)
  • DPORT dual-core control registers (CPU stall, reset, launch)
  • Inter-processor communication (IPC) and dual-core interrupt routing
  • Cache coherency protocol simulation
  • Spinlock and atomic operations (S32C1I — SR 12 SCOMPARE1) ✅
  • Test: Producer-consumer across cores

Phase 22: Advanced Peripherals

  • TWAI (CAN bus 2.0, up to 1 Mbps)
  • MCPWM (Motor Control PWM, 6 outputs)
  • SDMMC/SDIO host controller
  • Ethernet MAC (RMII interface)

Phase 23: Security & Cryptography

  • Flash encryption (AES-256)
  • Secure boot verification chain
  • AES accelerator (128/192/256-bit)
  • RSA accelerator (up to 4096-bit)
  • TRNG (True Random Number Generator)
  • eFuse read/write protection

Phase 24: Power Management & Debugging

  • Deep/light sleep modes with wake sources
  • ULP coprocessor (FSM and RISC-V variants)
  • GDB stub (remote debugging protocol)
  • Instruction trace export

Phase 25: Production & Distribution

  • CI/CD pipeline (GitHub Actions)
  • Performance regression tests (Esp32Benchmark suite)
  • NuGet package publishing
  • Sample applications (LED blink, IDF Hello World, MicroPython REPL over UART)

References

License

MIT © Iván Montiel

Contributing

This project uses AI-assisted development with custom GitHub Copilot agents. See .github/copilot-instructions.md for agent routing guidelines.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on ESP32Sharp:

Package Downloads
ESP32Sharp.TestKit

FluentAssertions-based test kit for integration-testing real firmware (CircuitPython, MicroPython, ESP-IDF) on the ESP32Sharp emulator.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.0-beta.1 174 7/11/2026