po4yka/rust-skills/rust-embedded-no-std

Claude Code and agent skill by po4yka/rust-skills

Trust tier 2
  • community
LLM review pending
Source check pending

Checking that the SKILL.md file is still in the repository.

Use when writing, porting, or debugging bare-metal Rust firmware or no_std libraries, such as embedded-hal drivers, Embassy or RTIC tasks, flashing and logging with probe-rs and defmt, memory.x and linker scripts, panic and allocator policy, interrupt-shared state, and atomics on cores without compare-and-swap. Triggers on embedded Rust, cortex-m-rt, thumbv6m, critical-section, portable-atomic, panic_handler, or HardFault.

Install

npx vskill@latest install po4yka/rust-skills/rust-embedded-no-std

SKILL.md

Rust Embedded and no_std

Keep the firmware bootable, bounded, observable, and testable on a host. Do not add an executor, allocator, or hardware abstraction until the product needs it. A synchronous static program is the smallest reliable firmware.

Use these skills, when they are installed, for adjacent work:

  • cargo-workflows for workspace features, profiles, and CI gates;
  • rust-native-linking when firmware links a C library or vendor SDK;
  • rust-unsafe for raw pointers, SAFETY comments, and soundness review;
  • memory-model for atomic orderings and general static mut rules;
  • rust-wasm for wasm32v1-none and other WebAssembly targets.

Failure triage

SymptomLikely causeCheck and fix
` custom targets are unstable and require -Zunstable-options , or .json target specs require -Zjson-target-spec to be added to the cargo invocation `JSON target spec on stable (Rust 1.95+)Use a built-in target, or pin nightly with -Z json-target-spec and -Z build-std=core
E0599 `no method named fetch_add found, or the same for compare_exchange or swap`Target has no compare-and-swapUse portable-atomic; the final binary selects its backend
E0432 ` unresolved import core::sync::atomic::AtomicU32 `Target has no atomic typesUse portable-atomic; the final binary selects its backend
`invalid definition of the runtime memset symbol used by the standard library`Hand-written memset, memcpy, memmove, memcmp, bcmp, or strlen with a wrong arity, a non-extern "C" ABI, or as a static (invalid_runtime_symbol_definitions, deny-by-default since 1.98)Match the C signature, or remove #[unsafe(no_mangle)] or export_name
warning `suspicious definition of the runtime memset symbol used by the standard library`The same symbol with the right arity and a wrong parameter type, such as c: u8 for the c_int of memset (suspicious_runtime_symbol_definitions, warn-by-default since 1.98). The build succeeds, and the caller and callee disagree on the ABITreat it as an error: match the C types (core::ffi::c_int, usize for size_t). Add #![deny(suspicious_runtime_symbol_definitions)] to the crate root
` -C soft-float: this option has been removed (use a corresponding *eabi target instead) , or target feature <name> must be enabled to ensure that the ABI of the current target can be implemented correctly`Float ABI changed by a flagSelect the eabi or eabihf target that matches the linked C objects; do not toggle ABI features
creating a shared reference to mutable static (or mutable reference)static mut under edition 2024Use an atomic, critical_section::Mutex, or framework resources
Duplicate panic implementationTwo panic provider cratesInspect cargo tree; select one in the binary
#[global_allocator] missingalloc is used with no allocatorRemove allocation or initialize one bounded heap
Link errors that name defmt symbols or sectionsNo -Tdefmt.x, or zero or two global loggersAdd the link argument; link exactly one logger in the binary
Linker reports region overflowCode, statics, heap, or stack exceed layoutInspect sections; reduce use or change only a verified memory budget
Firmware flashes but does not startWrong vector address, entry point, or reset setupCheck boot offset, vector contents, and runtime feature
Hard fault after enabling DMABuffer lifetime, alignment, cache, or region errorStop DMA; inspect ownership and chip memory rules
Interrupt fires oncePending flag is not cleared correctlyRead the peripheral sequence; clear only documented flags
Interrupt stormLevel source remains asserted or priority is wrongInspect source state and acknowledgement order
Async task stops progressingBlocking call, lost wake, or exhausted task slotInspect executor trace and all bounded slots
Logs missing below ERRORDEFMT_LOG not set for this buildSet DEFMT_LOG and rebuild; record it with the ELF
Logs corrupt or disappearWrong ELF, transport overflow, or reset during outputMatch build identity and reduce bounded log volume
Host tests try to run firmwareGlobal embedded target or runner leaked into testsPass the host target explicitly or scope target config
Debug build works, release failsTiming race, overflow, layout, or logging side effectTest the release ELF and inspect optimization-sensitive sharing

Do not weaken optimization, increase memory, or add delays as the final fix until inspection identifies the violated invariant.

Apply these rules to every change. Each one prevents a silent, unsound, or irreversible defect:

  • Access memory-mapped registers only through the PAC or HAL, or with

core::ptr::read_volatile and write_volatile. The compiler can elide or reorder a plain dereference.

  • Run no Rust code before RAM initialization; static access there is undefined

behavior. Write __pre_init in assembly (global_asm!), not #[pre_init].

  • Use critical-section-single-core (cortex-m) or unsafe-assume-single-core

(portable-atomic) only on a single-core chip in privileged mode.

  • Keep a DMA buffer alive, at a stable address, and untouched until the driver

confirms the stop. A timeout, select, or dropped future does not stop DMA.

  • Keep one memory-layout owner; two owners can silently select the wrong memory

map. Do not add a memory.x copy in build.rs when the runtime or HAL has one.

  • Do not treat a successful flash as proof that the image fits. The programmer

can erase adjacent data before it notices an invalid layout.

Verify with the check that proves the claim

Keep protocol parsing, state machines, unit conversion, filters, and retry decisions independent of registers and global peripherals. Test that library on the host. Replace a peripheral with a small trait fake only at the existing HAL boundary; do not emulate the whole chip.

bash
host_target="$(rustc -vV | sed -n 's/^host: //p')"
cargo test --locked -p <logic-crate> --lib --target "$host_target"
cargo check --locked --target <target> --no-default-features --features <chip-feature>
cargo build --locked --release --target <target> --bin <firmware>

Host tests prove logic only. They prove nothing about startup, registers, timing, or interrupts. A clean target build proves that the image links inside the declared regions; it does not prove stack or heap limits at run time. Do not put a global build.target in a reusable workspace without a host-test command like the one above: it can make cargo test try to execute firmware on the host.

Use --all-features only when the features are additive. When features select a chip or runtime, check each supported feature set explicitly, and reject invalid combinations with compile_error!.

When a library claims support for cores without compare-and-swap (CAS), add this check to its gate even if the product ships on a CAS-capable core. Run rustup target add thumbv6m-none-eabi first.

bash
cargo check --locked -p <lib> --target thumbv6m-none-eabi --features portable-atomic/critical-section

The backend feature belongs to this command only, never to the library's manifest. cargo check does not link, so it needs no critical-section implementation. Without the flag, a library that needs CAS fails this check as expected; do not fix that by adding a backend to its Cargo.toml.

Inspect the final ELF. These cargo-binutils wrappers need rustup component add llvm-tools. If the workspace does not install them, use the matching LLVM tools from the pinned toolchain; do not add tools only to make command spelling uniform.

bash
cargo size --release --target <target> --bin <firmware> -- -A
cargo objdump --release --target <target> --bin <firmware> -- -h
cargo nm --release --target <target> --bin <firmware> -- --size-sort

Install the probe tools with cargo install probe-rs-tools --locked. It provides probe-rs, cargo-flash, and cargo-embed. Give probe-rs an explicit chip. Do not rely on automatic chip detection in CI or production instructions.

bash
probe-rs run --chip <chip> target/<target>/release/<firmware>
probe-rs attach --chip <chip> target/<target>/release/<firmware>

probe-rs attach does not reset or flash, so it preserves a running fault.

Add hardware-in-the-loop checks only for behavior that a host cannot prove: startup, interrupt routing, clock accuracy, DMA, reset cause, watchdog action, sleep and wake, flash update, and real peripheral timing. Give each check a timeout and a recovery action so a failed board does not occupy CI forever.

Completion checklist

For every change:

  • Reusable logic compiles as no_std; host tests run on the host triple.
  • The final binary owns one panic policy, one allocator if needed, one

critical-section implementation, and one defmt logger if it logs.

  • Interrupt, DMA, and task ownership are explicit and bounded.

For a firmware release, or a change to boot, memory layout, interrupts, DMA, or panic policy, also complete the hardware items below. For a no_std library change, host tests and a target build are the evidence. When no board or probe is available, report each hardware item as not run.

  • The exact chip, target, runtime, boot offset, and memory regions are recorded.
  • Flash, RAM, stack, heap, queue, latency, and timing budgets have evidence.
  • The release ELF is inspected, flashed, and run without a debugger attached.

Its DEFMT_LOG value, if any, is recorded with it.

  • Panic, watchdog, reset, queue-full, and transport-failure paths are exercised.
  • Calibration values remain bounded knobs and have real-hardware measurements.

Keep concurrency bounded

Use interrupt handlers only to acknowledge hardware and move bounded state. Move parsing, formatting, allocation, and blocking I/O to task context.

For every interrupt-shared value, write down:

  1. Which contexts read and write it.
  2. Which operation makes access exclusive.
  3. The maximum time that interrupts stay masked.
  4. Which memory ordering or critical-section rule publishes the data.

Do not share state through static mut. Edition 2024 denies every reference to it (static_mut_refs). &raw mut and by-value access still compile, but they keep the data race with the interrupt handler. Use an atomic, a critical_section::Mutex<RefCell<T>>, or the framework's resource model.

Read atomic support from rustc --target <target> --print cfg | rg 'target_has_atomic='. target_has_atomic="32" means that 32-bit CAS exists. A target that prints no target_has_atomic="N" line has no CAS. target_has_atomic_primitive_alignment lines describe alignment, not atomic support. Stable --print cfg does not show load and store support. On thumbv6m-none-eabi and riscv32imc-unknown-none-elf, AtomicU32::load compiles and fetch_add fails with E0599. On armv4t-none-eabi, armv5te-none-eabi, and msp430-none-elf, the import of core::sync::atomic::AtomicU32 fails with E0432. Do not assume native atomic loads; compile a one-line probe on the target.

Use native atomics only when --print cfg lists the required width. On a target without CAS, use portable-atomic, which also supplies the missing atomic types. A library depends on it without a backend feature, and enables require-cas when it cannot work without CAS. The final binary selects the backend: critical-section, or unsafe-assume-single-core.

Let the final binary select exactly one critical-section implementation. On a multi-core chip such as the RP2040, use the HAL's implementation. Do not create a second interrupt-mask implementation in a driver.

Never hold a critical section across these operations:

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 100 of 100Pattern checks on the skill's files.
  • Model reviewNot runA model review is scheduled for the next rescan.

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
2
Repository forks
1
7-day trend score
5

Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/po4yka/rust-skills/rust-embedded-no-std/badge)](https://verified-skill.com/skills/po4yka/rust-skills/rust-embedded-no-std)

Something wrong with this skill or its page? Tell us.