wifi-densepose/firmware/esp32-csi-node/components/ruv_temporal
ruv 22d47a71e3 feat(firmware): scaffold ruv_temporal ESP-IDF Rust component (ADR-095 Phase 4, #513)
Phase 4 of the #513 roadmap: ESP-IDF component skeleton at
`firmware/esp32-csi-node/components/ruv_temporal/`. Source is complete
and self-consistent; cross-compile to xtensa-esp32s3-none-elf is
blocked by a known-broken esp-rs nightly snapshot (details in the
component README).

What's in the scaffold:

- `Cargo.toml` — staticlib, no_std + alloc, deps on the path-vendored
  `ruvllm_sparse_attention` (matching ADR-096's host-side dep) and
  `esp-alloc`/`critical-section` for the no_std allocator and lock
  primitives.
- `src/lib.rs` — public C ABI (init / push / classify / destroy /
  self_test) with `#[no_mangle]` exports, a `[#used]` keepalive table
  to defeat aggressive linker stripping, esp-alloc as the global
  allocator (heap region added at runtime by the firmware), and a
  loop-on-panic handler (Phase 5 will route through esp_system_abort).
- `src/window.rs` — `FrameRing`, the rolling-window buffer that
  `ruv_temporal_push` writes to. Chronological iteration via
  `iter_chronological()` so the kernel sees oldest-first.
- `include/ruv_temporal.h` — the public C header consumed by
  edge_processing.c. Threading contract documented inline (single
  dedicated FreeRTOS task, no internal locks).
- `CMakeLists.txt` — runs `cargo +esp build` as an ESP-IDF
  pre-component-register step, then registers the static library
  through `idf_component_register` + `target_link_libraries(...
  INTERFACE ...)`. `shim.c` exists only because
  `idf_component_register` requires SRCS.
- `.cargo/config.toml` + `rust-toolchain.toml` — pin the build to
  `xtensa-esp32s3-none-elf` and the `esp` toolchain channel so
  `cargo build` without flags Just Works once the toolchain is
  unblocked.
- `README.md` — Phase status table, Phase 5 toolchain blocker
  explanation, and the espup install fix.

ABI calls into edge_processing.c (Phase 6) and COM8 validation
(Phase 7) follow once the cross-compile is unblocked.

Closes nothing yet; advances #513.

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-08 09:44:01 -04:00
..
.cargo feat(firmware): scaffold ruv_temporal ESP-IDF Rust component (ADR-095 Phase 4, #513) 2026-05-08 09:44:01 -04:00
include feat(firmware): scaffold ruv_temporal ESP-IDF Rust component (ADR-095 Phase 4, #513) 2026-05-08 09:44:01 -04:00
src feat(firmware): scaffold ruv_temporal ESP-IDF Rust component (ADR-095 Phase 4, #513) 2026-05-08 09:44:01 -04:00
CMakeLists.txt feat(firmware): scaffold ruv_temporal ESP-IDF Rust component (ADR-095 Phase 4, #513) 2026-05-08 09:44:01 -04:00
Cargo.lock feat(firmware): scaffold ruv_temporal ESP-IDF Rust component (ADR-095 Phase 4, #513) 2026-05-08 09:44:01 -04:00
Cargo.toml feat(firmware): scaffold ruv_temporal ESP-IDF Rust component (ADR-095 Phase 4, #513) 2026-05-08 09:44:01 -04:00
README.md feat(firmware): scaffold ruv_temporal ESP-IDF Rust component (ADR-095 Phase 4, #513) 2026-05-08 09:44:01 -04:00
rust-toolchain.toml feat(firmware): scaffold ruv_temporal ESP-IDF Rust component (ADR-095 Phase 4, #513) 2026-05-08 09:44:01 -04:00
shim.c feat(firmware): scaffold ruv_temporal ESP-IDF Rust component (ADR-095 Phase 4, #513) 2026-05-08 09:44:01 -04:00

README.md

ruv_temporal — ESP32-S3 on-device temporal head

ESP-IDF component implementing ADR-095 (#513). The Rust staticlib at src/lib.rs wraps ruvllm_sparse_attention (vendored at vendor/ruvector/crates/ruvllm_sparse_attention) and exposes a narrow C ABI declared in include/ruv_temporal.h.

Status

Phase Scope State
4 — Scaffold Cargo.toml, src/{lib.rs,window.rs}, include/ruv_temporal.h, CMakeLists.txt, .cargo/config.toml Done. Source compiles host-side syntax check; not yet cross-compiled to xtensa.
5 — Cross-compile cargo +esp build --release --target xtensa-esp32s3-none-elf produces libruv_temporal.a. Blocked — see below.
6 — Wire from edge_processing.c FreeRTOS task on Core 1, queue from adaptive_controller fast loop, push() in fast tick, classify() at 1 Hz, emit 0xC5110007 packet. Not started.
7 — COM8 validation Flash 8MB build with CONFIG_CSI_TEMPORAL_HEAD_ENABLED=y, soak ≥5 min, check no Tmr Svc / task_wdt overflow. Not started.

Phase 5 blocker — esp toolchain rust-src bug

The system esp toolchain at C:\Users\ruv\.rustup\toolchains\esp has no precompiled core for xtensa-esp32s3-none-elf. It requires -Z build-std=core,alloc, but the bundled rust-src snapshot (esp channel, nightly 2025-09-16) hits two known bugs when build-std compiles core:

  1. library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rsCopy trait and size_of not in scope, ~16,000 errors.
  2. library/core itself — "cannot resolve a prelude import", "attributes starting with rustc are reserved", concat! macro not found.

These are upstream Rust nightly snapshot regressions, not anything this component is doing wrong. The fix is to refresh the esp toolchain to a newer nightly:

C:/Users/ruv/.cargo/bin/espup.exe install
# (re-source export-esp.ps1 / export-esp.sh after install)

espup install pulls the latest pinned esp Rust + LLVM. It is a ~1.5 GB download and ~5-10 min install. That step lands in the next loop iteration of #513 implementation work.

Build (once Phase 5 unblocks)

From this directory:

cargo +esp build --release --target xtensa-esp32s3-none-elf

Output: target/xtensa-esp32s3-none-elf/release/libruv_temporal.a.

ESP-IDF's idf.py build will pick this up via CMakeLists.txtadd_custom_command runs the cargo build before idf_component_register consumes the static library.

C ABI summary

esp_err_t ruv_temporal_init(const uint8_t *weights, size_t wlen,
                            uint32_t input_dim, uint32_t window_len,
                            uint32_t n_classes,
                            ruv_temporal_ctx_t **out_ctx);
esp_err_t ruv_temporal_push(ruv_temporal_ctx_t *ctx, const float *frame);
esp_err_t ruv_temporal_classify(ruv_temporal_ctx_t *ctx,
                                float *logits, uint32_t n_classes);
void      ruv_temporal_destroy(ruv_temporal_ctx_t *ctx);
esp_err_t ruv_temporal_kernel_self_test(void);

Threading: caller is responsible. Per ADR-095 §3.3, the firmware will spawn a single dedicated FreeRTOS task that owns the context and serialises all calls — push() and classify() are not internally synchronised.