po4yka/rust-skills/rust-embedded-no-std
Claude Code and agent skill by po4yka/rust-skills
- community
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-stdSKILL.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-workflowsfor workspace features, profiles, and CI gates;rust-native-linkingwhen firmware links a C library or vendor SDK;rust-unsafefor raw pointers, SAFETY comments, and soundness review;memory-modelfor atomic orderings and generalstatic mutrules;rust-wasmforwasm32v1-noneand other WebAssembly targets.
Failure triage
| Symptom | Likely cause | Check 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-swap | Use portable-atomic; the final binary selects its backend | |
E0432 ` unresolved import core::sync::atomic::AtomicU32 ` | Target has no atomic types | Use 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 ABI | Treat 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 flag | Select 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 2024 | Use an atomic, critical_section::Mutex, or framework resources | |
| Duplicate panic implementation | Two panic provider crates | Inspect cargo tree; select one in the binary | |
#[global_allocator] missing | alloc is used with no allocator | Remove allocation or initialize one bounded heap | |
| Link errors that name defmt symbols or sections | No -Tdefmt.x, or zero or two global loggers | Add the link argument; link exactly one logger in the binary | |
| Linker reports region overflow | Code, statics, heap, or stack exceed layout | Inspect sections; reduce use or change only a verified memory budget | |
| Firmware flashes but does not start | Wrong vector address, entry point, or reset setup | Check boot offset, vector contents, and runtime feature | |
| Hard fault after enabling DMA | Buffer lifetime, alignment, cache, or region error | Stop DMA; inspect ownership and chip memory rules | |
| Interrupt fires once | Pending flag is not cleared correctly | Read the peripheral sequence; clear only documented flags | |
| Interrupt storm | Level source remains asserted or priority is wrong | Inspect source state and acknowledgement order | |
| Async task stops progressing | Blocking call, lost wake, or exhausted task slot | Inspect executor trace and all bounded slots | |
Logs missing below ERROR | DEFMT_LOG not set for this build | Set DEFMT_LOG and rebuild; record it with the ELF | |
| Logs corrupt or disappear | Wrong ELF, transport overflow, or reset during output | Match build identity and reduce bounded log volume | |
| Host tests try to run firmware | Global embedded target or runner leaked into tests | Pass the host target explicitly or scope target config | |
| Debug build works, release fails | Timing race, overflow, layout, or logging side effect | Test 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) orunsafe-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.
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.
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.
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.
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:
- Which contexts read and write it.
- Which operation makes access exclusive.
- The maximum time that interrupts stay masked.
- 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
- SKILL.md in the repositoryCheckingskills/rust-embedded-no-std/SKILL.md
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
[](https://verified-skill.com/skills/po4yka/rust-skills/rust-embedded-no-std)