Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
_build/
.venv/
2 changes: 2 additions & 0 deletions docs/_static/css/custom.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/* RP2040Sharp docs — custom tweaks on top of the PyData theme.
Kept minimal; matches the shared PyMCU docs styling. */
27 changes: 27 additions & 0 deletions docs/compat/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Compatibility

RP2040Sharp runs real, unmodified firmware images.

## MicroPython

Stock MicroPython UF2 images for the Raspberry Pi Pico boot to an interactive REPL over
emulated USB-CDC. The integration suite runs against **v1.19.1**, **v1.20.0**, and
**v1.21.0**. Flash program/erase is implemented via native C# hooks, so MicroPython's
LittleFS filesystem works correctly.

```csharp
using var pico = new PicoSimulation();
pico.LoadFlash(RP2040Machine.Uf2ToFlash(microPythonUf2)!);
pico.RunUntilOutput(pico.UsbCdc, ">>> ", timeoutMs: 60_000);
```

## CircuitPython

CircuitPython boots to its REPL as well. USB host support is currently **CDC-only**
(sufficient for the REPL); MSC/HID host drivers are not included.

## Bare-metal (pico-sdk, PyMCU)

Any firmware built with the pico-sdk toolchain runs directly. This is the primary use
case for [PyMCU](https://docs.pymcu.org), which compiles Python to bare-metal ARM and uses
RP2040Sharp as its RP2040 integration testkit.
92 changes: 92 additions & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
project = "RP2040Sharp"
copyright = "2026, Iván Montiel Cardona"
author = "Iván Montiel Cardona"
release = "1.0.0"
version = "1.0"

extensions = [
"myst_parser",
"sphinx_copybutton",
"sphinx_design",
"sphinx.ext.intersphinx",
]

source_suffix = {
".rst": "restructuredtext",
".md": "markdown",
}

templates_path = ["_templates"]
exclude_patterns = ["_build", ".venv", "Thumbs.db", ".DS_Store"]

# Served as a sub-path of the shared PyMCU docs site.
html_baseurl = "https://docs.pymcu.org/rp2040sharp/"

# ---------------------------------------------------------------------------
# MyST extensions
# ---------------------------------------------------------------------------
myst_enable_extensions = [
"colon_fence",
"deflist",
"tasklist",
"attrs_inline",
]
myst_heading_anchors = 4

# ---------------------------------------------------------------------------
# HTML / PyData theme (matches the PyMCU docs site)
# ---------------------------------------------------------------------------
html_theme = "pydata_sphinx_theme"
html_static_path = ["_static"]
html_css_files = ["css/custom.css"]
html_title = "RP2040Sharp"

html_theme_options = {
"navbar_align": "left",
"navbar_end": ["navbar-icon-links", "theme-switcher"],
"secondary_sidebar_items": ["page-toc", "edit-this-page"],
"show_prev_next": True,
"navigation_with_keys": True,
"footer_start": ["copyright"],
"footer_end": ["sphinx-version"],
"pygments_light_style": "friendly",
"pygments_dark_style": "monokai",
"header_links_before_dropdown": 6,
"navigation_depth": 3,
"show_nav_level": 1,
"icon_links": [
{
"name": "PyMCU docs",
"url": "https://docs.pymcu.org",
"icon": "fa-solid fa-house",
},
{
"name": "GitHub",
"url": "https://github.com/PyMCU/RP2040Sharp",
"icon": "fa-brands fa-github",
},
{
"name": "NuGet",
"url": "https://www.nuget.org/packages/RP2040Sharp",
"icon": "fa-solid fa-cube",
},
],
}

html_sidebars = {
"**": ["sidebar-nav-bs"],
}

# "Edit this page" links point at the repo.
html_context = {
"github_user": "PyMCU",
"github_repo": "RP2040Sharp",
"github_version": "master",
"doc_path": "docs",
}

# ---------------------------------------------------------------------------
# copybutton: strip prompt characters
# ---------------------------------------------------------------------------
copybutton_prompt_text = r">>> |\.\.\. |\$ "
copybutton_prompt_is_regexp = True
10 changes: 10 additions & 0 deletions docs/getting-started/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Getting Started

Install the packages, run your first firmware, and learn the basic API.

```{toctree}
:maxdepth: 1

installation
quickstart
```
29 changes: 29 additions & 0 deletions docs/getting-started/installation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Installation

RP2040Sharp targets **.NET 10**. You need the .NET 10 SDK installed.

## From NuGet

```bash
dotnet add package RP2040Sharp # the emulator core
dotnet add package RP2040Sharp.TestKit # fluent harness for firmware tests (optional)
```

| Package | Purpose |
|---|---|
| [`RP2040Sharp`](https://www.nuget.org/packages/RP2040Sharp) | CPU, bus, peripherals, `RP2040Machine`. AOT-compatible, trimmable. |
| [`RP2040Sharp.TestKit`](https://www.nuget.org/packages/RP2040Sharp.TestKit) | `RP2040TestSimulation`, probes, and FluentAssertions-based health checks. |

## From source

```bash
git clone https://github.com/PyMCU/RP2040Sharp.git
cd RP2040Sharp
dotnet build
dotnet test
```

## Headless runner CLI

The repository also ships a headless runner (`src/RP2040Sharp.Runner`) for CI pipelines —
see [Validating firmware in CI](../guides/ci-validation.md).
52 changes: 52 additions & 0 deletions docs/getting-started/quickstart.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Quickstart

## Run a firmware image and capture its serial output

```csharp
using RP2040.Peripherals;

var machine = new RP2040Machine();

// Load a raw flash image or a UF2 (use Uf2ToFlash for UF2 files).
machine.LoadFlash(File.ReadAllBytes("firmware.bin"));

// Forward UART0 TX to the console.
machine.Uart0.OnByteTransmit += b => Console.Write((char)b);

// Run 1 ms of simulated time (125 000 cycles at 125 MHz).
machine.Run(125_000);
```

Loading a UF2 image:

```csharp
var flash = RP2040Machine.Uf2ToFlash(File.ReadAllBytes("firmware.uf2"));
machine.LoadFlash(flash!);
```

## Drive a test with the TestKit

The TestKit wraps `RP2040Machine` in a fluent harness with probes and assertions:

```csharp
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);
```

For bounded, never-hanging runs and CPU-health checks, see
[Firmware testing with the TestKit](../guides/testkit.md).

## Inject and read GPIO

```csharp
machine.Sio.SetGpioExternalIn(5, high: true); // drive GP5 from outside
bool isHigh = machine.Sio.GetGpioOut(3); // read firmware's GP3 output
bool isOutput = machine.Sio.GetGpioOutputEnable(3);
```
57 changes: 57 additions & 0 deletions docs/guides/ci-validation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Validating firmware in CI

RP2040Sharp is designed to validate compiler/firmware output (for example, the
[PyMCU](https://docs.pymcu.org) compiler) in CI **without flaky or hanging builds**:

- Runs are **bounded** — a wedged program fails with a reason instead of stalling the job.
- The clock is driven by executed cycles, so results are **deterministic** and reproducible.

There are two ways to use it in a pipeline.

## In a .NET test project (recommended)

Use the TestKit directly from xUnit/NUnit. This is what PyMCU's integration suite does:

```csharp
[Fact]
public void Blink_firmware_reports_pass()
{
using var pico = new PicoSimulation();
pico.LoadFlash(RP2040Machine.Uf2ToFlash(File.ReadAllBytes("blink.uf2"))!);

var result = pico.RunUntilHalt(pico.Uart0, "PASS");

result.Succeeded.Should().BeTrue($"firmware halted with {result.Outcome}");
pico.Cpu.Should().NotHaveFaulted();
}
```

## Headless runner CLI

For pipelines that just need an exit code (no C# harness), use the `rp2040sharp` runner:

```bash
dotnet run --project src/RP2040Sharp.Runner -c Release -- \
firmware.uf2 --expect-text "PASS" --channel uart --max-instructions 5000000
```

Exit codes:

| Code | Meaning |
|---|---|
| `0` | expected text found |
| `1` | text not found within the instruction budget |
| `2` | the firmware crashed (CPU lockup) |
| `64` | usage error |
| `66` | image file not found |

Options:

| Option | Default | Description |
|---|---|---|
| `--expect-text <text>` | — | Pass only if `<text>` appears in serial output |
| `--channel uart\|usb` | `uart` | Serial channel to watch |
| `--max-instructions <n>` | `500000000` | Hard execution budget |
| `--quiet` | off | Do not echo serial output to stdout |

Serial output goes to **stdout**; the run summary goes to **stderr**.
43 changes: 43 additions & 0 deletions docs/guides/gdb-debugging.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Debugging with GDB

RP2040Sharp ships a GDB Remote Serial Protocol server, so you can debug Core 0 with
`arm-none-eabi-gdb` exactly as you would on real hardware.

## From the demo

```bash
dotnet run --project src/RP2040Sharp.Demo -c Release -- --gdb
```

Then, in another terminal:

```bash
arm-none-eabi-gdb -ex "target remote :3333"
```

```
(gdb) info registers
(gdb) x/4xw 0x20000000
(gdb) stepi
(gdb) break *0x10000100
(gdb) continue
```

## Embedding the server in your own host

```csharp
using RP2040.Gdb;

var server = new GdbTcpServer(myGdbTarget, port: 3333); // myGdbTarget : IGdbTarget
server.OnLog = Console.Error.WriteLine;
server.Start();
```

`IGdbTarget` exposes the machine and an `Execute()`/`Stop()` pair so your host controls
when the CPU is free-running versus halted.

## Supported commands

Registers (`g`, `p`/`P`, including xPSR and MSP/PSP/PRIMASK/CONTROL), memory (`m`/`M`),
`c` (continue), `s`/`vCont;s` (step), `D` (detach), and BKPT-driven stop replies with PC
rewind. `BASEPRI`/`FAULTMASK` read as 0 — the Cortex-M0+ does not implement them.
11 changes: 11 additions & 0 deletions docs/guides/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Guides

Task-oriented guides for the main use cases.

```{toctree}
:maxdepth: 1

testkit
ci-validation
gdb-debugging
```
Loading
Loading