RP2040Sharp is a high-performance emulator for the Raspberry Pi RP2040 microcontroller, written entirely in modern C# (.NET 10). It runs real RP2040 firmware — including MicroPython — without modification.
This project is a port and re-imagination of the excellent rp2040js project by Uri Shaked. The goal is to bring embedded emulation to the .NET ecosystem with a strong focus on speed and type safety, leveraging the latest runtime features.
Measured on Apple Silicon (macOS, .NET 10, Release build):
| Workload | Throughput |
|---|---|
| Tight arithmetic loop (Flash, steady-state) | ~460 MIPS |
| MicroPython boot | ~250 MIPS |
| MicroPython REPL execution | ~250 MIPS |
The emulator boots MicroPython v1.21.0 and reaches the interactive REPL in approximately 3–4 seconds of simulated time (wall time varies by host). On iOS/MAUI (Mono AOT, no JIT), throughput is lower but the proportional optimizations still apply.
- ARM Cortex-M0+ full instruction set (Thumb-1), including exceptions and NVIC
- Real RP2040 BootROM (B1) — loaded as an embedded resource;
rom_table_lookup,memcpy44,memset4and bit-manipulation helpers run natively - Flash erase/program via C# native hooks — MicroPython's LittleFS filesystem works correctly
- MicroPython boots to interactive REPL over emulated USB-CDC
- Dual-core: Core 1 launches via the SIO FIFO multicore handshake (RP2040 §2.8.3); both cores advance in lock-step
- GDB stub: debug Core 0 with
arm-none-eabi-gdbovertarget remote :3333(registers, memory, stepi, breakpoints) - Peripherals: GPIO, SIO, UART0/1, SPI0/1, I2C0/1 (master + slave simulation), ADC, PWM, PIO0/1, DMA, Timer, Watchdog, RTC, USB (CDC-ACM host for the MicroPython REPL), Clocks, PSM, Resets, and more
- Per-pin GPIO API (
SetGpioExternalIn,GetGpioOutputEnable,GetGpioOut) for embedding in circuit simulators - TestKit fluent API for writing firmware integration tests
git clone https://github.com/PyMCU/RP2040Sharp.git
cd RP2040Sharp
dotnet restore
dotnet buildRun the demo (downloads MicroPython, boots it, executes REPL snippets, reports MIPS):
dotnet run --project src/RP2040Sharp.Demo -c ReleaseRun the tests:
dotnet testusing RP2040.Peripherals;
var machine = new RP2040Machine();
machine.LoadFlash(File.ReadAllBytes("firmware.bin"));
// Capture UART output
machine.Uart0.OnByteTransmit += b => Console.Write((char)b);
// Run 125 000 cycles (1 ms at 125 MHz)
machine.Run(125_000);using RP2040.TestKit;
var sim = RP2040TestSimulation.Create()
.WithBinary(File.ReadAllBytes("firmware.bin"))
.AddUart(0, out var uart);
sim.RunMilliseconds(100);
Assert.Contains("Hello", uart.Text);Built for using the emulator as a compiler/firmware testkit (e.g. for PyMCU) without flaky or hanging builds. A run is always bounded — wedged firmware fails the test with a reason instead of stalling the job — and the instruction count is deterministic and reproducible across machines.
var sim = RP2040TestSimulation.Create()
.WithBinary(File.ReadAllBytes("firmware.bin"))
.AddUart(0, out var uart);
// Never hangs: returns PredicateMet / LockedUp / BudgetReached.
var result = sim.RunUntilHalt(() => uart.Text.Contains("PASS"), maxInstructions: 5_000_000);
result.Succeeded.Should().BeTrue();
sim.Cpu.Should().NotHaveFaulted();
sim.Cpu.Should().HaveExecutedAtMost(2_000_000); // compiler-size regression guardOr headless from a pipeline, with the rp2040sharp runner CLI (exit 0 found · 1 not
found · 2 crashed):
dotnet run --project src/RP2040Sharp.Runner -c Release -- \
firmware.uf2 --expect-text "PASS" --channel uart// Inject an external signal on GP5
machine.Sio.SetGpioExternalIn(5, high: true);
// Read firmware output state
bool isHigh = machine.Sio.GetGpioOut(3);
bool isOutput = machine.Sio.GetGpioOutputEnable(3);Run the demo with --gdb to expose Core 0 over the GDB Remote Serial Protocol:
dotnet run --project src/RP2040Sharp.Demo -c Release -- --gdb
# in another terminal:
arm-none-eabi-gdb -ex "target remote :3333"Or embed the server in your own host:
using RP2040.Gdb;
var server = new GdbTcpServer(myGdbTarget, port: 3333); // myGdbTarget : IGdbTarget
server.Start();| Project | Description |
|---|---|
src/RP2040Sharp |
Core library — CPU, bus, peripherals, machine |
src/RP2040.TestKit |
Fluent test harness for firmware integration tests |
src/RP2040Sharp.Runner |
Headless rp2040sharp CLI: run firmware, --expect-text, CI exit codes |
src/RP2040Sharp.Demo |
Demo: boots MicroPython and drives the REPL |
- Instruction decoder: 65 536-entry flat table of
delegate*function pointers — O(1) dispatch with no branch on opcode - Bus reads: explicit SRAM → Flash → BootROM fast paths with direct pointer arithmetic; no table indirection
- Native hook guard: registered hooks are bounded by
_nativeHookMax; Flash-region instructions skip the dictionary lookup entirely via a single uint comparison - Fetch cache: region and base pointer cached in
Run()locals; region changes (rare) flush the cache
- Full Thumb-1 instruction set
- Exceptions, NVIC, SysTick, PendSV
- Native hooks (BootROM ROM API, flash erase/program)
- WFI / WFE sleep with correct peripheral wakeup
- Dual-core (Core 1 launch, SIO FIFO)
- GDB stub for step-debugging firmware
- GPIO, SIO (spinlocks, interpolator)
- UART0 / UART1
- SPI0 / SPI1
- I2C0 / I2C1 (master + slave-mode simulation)
- ADC
- PWM (all 8 slices)
- PIO0 / PIO1 (state machines, GPIO integration)
- DMA (all channels, DREQ sources)
- USB (CDC-ACM host driver for the MicroPython REPL)
- Timer / Alarms, Watchdog, RTC
- Clocks, Resets
- [~] XOSC, ROSC, PLL, PSM, VREG — register stubs (report stable/locked; no frequency model)
- Flash programming via SSI (XIP hardware path)
- UF2 parser in demo
- Real RP2040 B1 BootROM (embedded resource)
- MicroPython v1.21.0 boots to REPL
- Per-pin GPIO API for circuit simulator embedding
- iCircuit element (
RP2040Elm) — in design - NativeAOT targets (Windows, Linux, macOS, iOS)
- WebAssembly (WASM) target
- Fork the repository.
- Create a feature branch (
git checkout -b feature/my-feature). - Ensure all tests pass (
dotnet test). - Commit following Conventional Commits.
- Open a Pull Request against
master.
MIT License — see LICENSE.
Based on the original work from rp2040js © 2021 Uri Shaked.
C# Port © 2026 Iván Montiel Cardona.