Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Embedded & no_std

PulseMap runs on bare-metal microcontrollers — not just cargo check, but actually executed on emulated ARM hardware via QEMU.

Quick Start

# Most targets (Cortex-M3+, RISC-V with A extension, WASM)
pulse_map = { version = "0.6", default-features = false }

# Cortex-M0 / RP2040 / ESP32-C3 (no hardware atomic CAS)
pulse_map = { version = "0.6", default-features = false, features = ["critical-section"] }

What You Get in no_std

With default-features = false, PulseMap needs only alloc (a heap allocator). You get:

  • PulseMapRaw — insert / get / remove / peek / TTL
  • TypedPulseMap<K, V> — type-safe wrapper
  • MetaWord — 8-byte packed LFU+LRU metadata
  • Bucket — 64-byte cache-line-aligned storage

You do not get ConcurrentPulseMap or ShardedPulseMap (they need std for RwLock and threading).

Verified Targets

Every push runs cargo check in CI for all 8 targets. Two of them also execute on QEMU (marked with 🏃):

TargetChipsExtra FeatureTested
thumbv7m-none-eabiCortex-M3—🏃 QEMU
thumbv7em-none-eabihfCortex-M4F / M7F (STM32F4)—✅ build
thumbv8m.main-none-eabiCortex-M33 (nRF9160)—✅ build
thumbv6m-none-eabiCortex-M0 / M0+ (RP2040)critical-section🏃 QEMU
riscv32imac-unknown-none-elfRISC-V with A extension—✅ build
riscv32imc-unknown-none-elfESP32-C3critical-section✅ build
aarch64-unknown-none64-bit bare metal—✅ build
wasm32-unknown-unknownWebAssembly—✅ build

Why critical-section?

PulseMap’s MetaWord is an AtomicU64. On chips without hardware atomic CAS (Cortex-M0, RISC-V without the A extension), portable-atomic cannot build a 64-bit atomic at all. The critical-section feature solves this by masking interrupts around the update.

The critical-section implementation comes from your HAL, not from PulseMap:

#![allow(unused)]
fn main() {
// Cortex-M0 / RP2040 — add to your binary crate
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }

// ESP32-C3
esp-hal = { version = "...", features = ["critical-section"] }
}

Memory Footprint

On a Cortex-M with 16 KiB RAM, PulseMap fits comfortably:

ConfigBucketsSlotsHeap Used
16 buckets16642,048 bytes
4 buckets416512 bytes

Each bucket is exactly 128 bytes of heap (64B bucket + 64B slot TTL metadata). A 64-slot cache in 2 KiB of RAM is what makes PulseMap practical on parts where Moka and QuickCache cannot even compile.

Running the QEMU Tests Yourself

cd qemu-test

# Cortex-M3 (portable-atomic spinlock fallback)
cargo run --release --target thumbv7m-none-eabi

# Cortex-M0 (critical-section, interrupt masking)
cargo run --release --target thumbv6m-none-eabi --features m0

Requires qemu-system-arm installed (apt install qemu-system-arm).

The test inserts, gets, evicts, checks TTL, and removes — all on real ARM instructions, not a compile check. Output:

qemu-test: 16 buckets, 64 nominal slots, 3072 heap bytes used
qemu-test: all checks passed

The 3,072 covers all three maps the test creates — the 16-bucket main map (2,048 B) plus two 4-bucket maps for the TTL and remove checks (512 B each): 24 buckets × 128 B. A lone 16-bucket map is 2 KiB, matching the table above.

PulseMap vs LRU on Embedded

In no_std, the only other option is lru. Here’s why PulseMap wins:

PulseMaplru
Memory per entry34 B (at scale)68 B
Eviction policyLFU + LRU (smart)Pure LRU
Cache-line aligned✅ 64-byte buckets❌ pointer chasing
Hit rate (Zipfian)96.73%95.83%
Allocation patternUpfront, predictablePer-insert, fragmented

On memory-constrained MCUs, PulseMap stores 2x more entries in the same RAM, with a better eviction policy.