https://github.com/harshverma27/nucleus/raw/main/media/nucleus-brag.mp4
A CLI-first STM32 developer platform: declarative hardware configuration, a constraint solver that actually understands silicon, dual-backend hardware-in-the-loop testing, and real-time trace debugging.
Not an IDE replacement. A developer platform.
Nucleus replaces STM32CubeIDE's two real lock-ins:
- Graphical pin/peripheral configuration that produces opaque, un-diffable XML.
- Integrated debug/trace/test tooling with no open-source, CI-friendly equivalent.
Everything lives in one version-controlled file, stm32.toml, validated and
driven entirely from the CLI β usable from any editor, gatable in CI, with VS
Code as an optional thin client on top.
π Read the docs Β· Watch the demo video
- What Nucleus Is
- Quick Start
- Features
- Architecture
- The
stm32.tomlFormat - CLI Reference
- Workspace Layout
- Tech Stack
- Architectural Rules
- Contributing
- License
Three products inside one ecosystem, all owned by the Rust CLI:
| Product | What it does |
|---|---|
| Hardware-aware build system | stm32.toml β validated HAL init code β CMake project |
| Hardware-in-the-loop test runner | declarative + scripted tests, run on a QEMU twin and/or real silicon, with simβsilicon divergence detection |
| Embedded observability stack | ITM packet decoder β WebSocket β real-time React dashboard, plus a test-history view |
Two MCUs are supported end-to-end: STM32F446RE and STM32F411RE (NUCLEO-F446RE / NUCLEO-F411RE).
# Install
cargo install nucleus-cli
# β¦or from source: cargo install --git https://github.com/harshverma27/nucleus nucleus-cli --locked
# Scaffold a project
nucleus init --board NUCLEO-F446RE
cd my-project
# Validate, build, flash
nucleus check
nucleus build
nucleus flashFull walkthrough: Installation Β· Quickstart: Blink an LED Β· CLI Usage.
Nucleus's feature set splits into four pillars β the same structure the docs site uses.
nucleus check parses stm32.toml and validates it against a real silicon
model, deterministically generated from ST's own pack data:
| Check | What it catches |
|---|---|
| Pinout & peripheral verification | pin collisions, AF mismatches, invalid/missing pins, disabled clock domains, peripherals unavailable on the selected family |
| Clock-tree solver | PLL divider/VCO legality, prescaler legality, and silicon frequency limits (APB1 β€ 45 MHz, APB2 β€ 90 MHz, SYSCLK/AHB β€ 180 MHz on F446) |
| DMA arbitration | two peripherals silently fighting for the same DMA1/DMA2 stream |
| IRQ / NVIC verifier | unhandled NVIC vectors, shared EXTI line collisions, DMA-vs-IRQ priority inversion |
| Constraint auto-router | nucleus route β declare a peripheral instance with no pins assigned, get a complete, valid, deterministic pinout back |
nucleus check exits non-zero on any conflict β the same exit code CI gates
on. The CMSIS pack XML never has DMA/clock/IRQ data, so those models are
hand-maintained and cross-checked against ST's reference manuals (RM0390 for
F446RE, RM0383 for F411RE).
nucleus test runs [[test]] blocks against two backends behind one
trait β QEMU (always available, no hardware needed) and real silicon over
SWD/SWO:
- Dual-backend HIL substrate β the
Backendtrait both implementations share, and the empirically-confirmed QEMU fidelity gap (no GPIO model, full USART2 model) every test author should know about. - Declarative tests β one-line assertions (
"pin PA5 toggles at 1Hz Β±5%","USART2 echoes \"ping\" within 10ms","trace event \"boot\" within 500ms") with no host code. - Scripted tests β a real on-device test-agent + host SDK, for assertions too stateful for one line.
A backend with no available tool (no QEMU binary, no probe attached) is reported as skipped, never as a failure β a hosted CI runner with no board stays green for the legs it can't exercise.
- Trace dashboard & ITM decoding β a zero-panic, fuzz-tested ARM CoreSight ITM/SWO decoder, streamed over WebSocket to a React/Canvas dashboard (log lines + live variable charts), embeddable in VS Code or a standalone browser.
- Test history & CI β every
nucleus testrun appends totests/test_history.json;nucleus history/nucleus showread it back, the dashboard renders it as a bar chart, and a reusable GitHub composite action posts a per-backend PR summary.
Lockstep co-execution β
nucleus lockstep runs the same firmware on both backends at once,
collects an ITM-checkpointed observation trace from each, and reports the
first point where simulator and silicon disagree. This is the system's
answer to "passing on QEMU only proves your logic is correct in an idealized
model" β divergence at a checkpoint is the signal that a timing assumption,
register quirk, or simulator fidelity gap is hiding a real bug.
nucleus CLI (Rust β owns all logic)
β
βββ constraint solver parse stm32.toml β verify pin/clock/DMA/IRQ β emit C
β βββ LSP server diagnostics + hover over the language server protocol
β
βββ HIL test runner Backend trait β QEMU + hardware backends β declarative/scripted tests
β βββ lockstep run both backends on one firmware, diff their observation traces
β
βββ ITM trace daemon read SWO from OpenOCD β decode CoreSight packets β WebSocket
β
βββ build orchestrator invoke CMake + arm-none-eabi-gcc + st-flash
VS Code Extension (TypeScript β thin UI layer only)
βββ LSP client talks to nucleus lsp, shows squiggles in editor
βββ Webview panel embeds the React trace + history dashboard
The CLI owns everything. The extension is a display layer. This means CI works without VS Code installed, a future JetBrains/Neovim client needs only a new thin client (not a rewrite), and the hard logic lives in one place, tested independently.
[device]
family = "STM32F446RE"
board = "NUCLEO-F446RE"
clock_hz = 180_000_000
[clocks]
source = "pll"
pll_source = "hse"
pll_m = 8
pll_n = 360
pll_p = 2
apb1_prescaler = 2
apb2_prescaler = 1
[peripherals.usart2]
tx = "PA2"
rx = "PA3"
baud = 115200
dma = true # opt in to DMA arbitration
irq = true # opt in to NVIC-vector verification
[peripherals.spi1]
mosi = "PA7"
miso = "PA6"
sck = "PA5"
nss = "PA4"
[peripherals.tim2] # no pins assigned β `nucleus route` fills them in
channel1 = ""
[[exti]]
pin = "PA0"
[trace]
enabled = true
swo_freq = 2_000_000
[[trace.variables]]
name = "temperature"
port = 1
type = "f32"
[[test]]
name = "uart_echo"
assertion = "USART2 echoes \"ping\" within 10ms"
backend = "both"Fully version-controllable, diffable, and reviewable in PRs β "PA5 moved
from SPI1 to SPI2" is a one-line git diff.
| Command | What it does |
|---|---|
nucleus init |
Scaffold a new project (stm32.toml, CMakeLists.txt, src/main.c, CI workflow). --board NUCLEO-F446RE (default) or NUCLEO-F411RE. |
nucleus check |
Validate stm32.toml against the full constraint database; print conflicts; exit non-zero on any. |
nucleus route |
Auto-assign pins for peripherals declared without them; write a fully-specified config. |
nucleus build |
Run CMake + arm-none-eabi-gcc; emit firmware.elf/.bin. |
nucleus flash |
Program the board via st-flash/OpenOCD. |
nucleus test |
Run [[test]] assertions on the QEMU twin and/or real hardware. --backend qemu|hardware, --test <name>. |
nucleus lockstep |
Run both backends on the same firmware; report the first simβsilicon divergence. --explain (reserved for v3). |
nucleus history |
List recorded test runs with pass/fail counts. --graph for the dashboard JSON, --last N to limit. |
nucleus show [run] |
Every assertion result for one run (defaults to the latest). |
nucleus trace |
Start the ITM daemon + dashboard. |
nucleus lsp |
Start the language server (spawned by the VS Code extension, not run by hand). |
Full flag reference: CLI Usage.
nucleus/
βββ crates/
β βββ nucleus-cli/ # binary β command dispatch
β βββ nucleus-compiler/ # TOML parser, constraint solver, codegen, router, assertions
β βββ nucleus-db/ # STM32 constraint database: pin/AF (pack-generated) + clock/DMA/IRQ models (hand-maintained)
β βββ nucleus-lsp/ # tower-lsp server wrapping nucleus-compiler
β βββ nucleus-itm/ # zero-dependency, never-panic ITM/CoreSight decoder
β βββ nucleus-trace/ # OpenOCD telnet + WebSocket server
β βββ nucleus-hil/ # Backend trait, QEMU + hardware backends, lockstep comparator
β βββ nucleus-history/ # tests/test_history.json read/write
β βββ nucleus-test-sdk/ # device test-agent host SDK (scripted tests)
βββ extension/ # VS Code client (TypeScript) + React dashboard
βββ tests/fixtures/ # sample stm32.toml files for integration tests
βββ .github/actions/nucleus/ # reusable composite CI action
βββ docs/ # mdBook documentation source
| Layer | Technology | Why |
|---|---|---|
| CLI + compiler + LSP + ITM + HIL | Rust | memory safety, speed, one cargo workspace |
| Constraint database source | STM32 CMSIS Packs (pin/AF, pack-generated) + hand-maintained clock/DMA/IRQ models (RM-cited) | pin/AF has an official machine-readable source; clock/DMA/IRQ don't, so they're hand-verified |
| HIL simulator backend | QEMU (qemu-system-arm -M netduinoplus2) |
always available in CI, no hardware needed |
| HIL hardware backend | OpenOCD + SWD/SWO | works with the on-board ST-Link, no extra probe |
| Language server protocol | tower-lsp |
async LSP server framework |
| ITM packet decoding | hand-rolled Rust (ARM CoreSight spec) | no existing crate covers this correctly |
| WebSocket server | tokio-tungstenite |
async, integrates with the tokio runtime |
| VS Code extension host | TypeScript | required by the VS Code extension API |
| Dashboard | React + Canvas API | Canvas for high-frequency live chart rendering |
| Build system | CMake + arm-none-eabi-gcc | standard embedded toolchain |
| CI | GitHub Actions | reusable composite action, per-backend PR summary |
- The extension contains zero business logic. Any constraint checking, decoding, or validation belongs in the Rust CLI.
- Codegen does not reimplement the HAL. Generated
nucleus_init.ccalls only stockHAL_*_Initfunctions with resolved parameters β ST HAL updates don't break Nucleus. - Two target MCUs: F446RE + F411RE. Both supported end-to-end. No new families without a deliberate decision to expand.
- Local tool only. No cloud registry, no upload features.
- Lockstep has no explanation engine, by design. It reports where two backends diverge, not why β root-cause inference is deferred past v2.
Contributions are welcome β see CONTRIBUTING.md for the
build/test workflow and the architectural rules above. Run make check (the
exact CI gate) before pushing. Changes ship with tests.
Dual-licensed under either of
- Apache License, Version 2.0 (
LICENSE-APACHE) - MIT license (
LICENSE-MIT)
at your option. Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you shall be dual-licensed as above, without any additional terms or conditions.
Target hardware: NUCLEO-F446RE + NUCLEO-F411RE.
