3.9 KiB
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,weights.rs}, include/ruv_temporal.h, CMakeLists.txt, .cargo/config.toml | Done. |
| 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. |
Done in main/temporal_task.c (no-op shim path verified by 8MB firmware build with feature off). |
| 7 — COM8 validation | Flash 8MB build with CONFIG_CSI_TEMPORAL_HEAD_ENABLED=y, soak ≥5 min, check no Tmr Svc / task_wdt overflow. |
Pending board reattach. |
Module map
| File | Purpose |
|---|---|
src/lib.rs |
C ABI: ruv_temporal_init / push / classify / destroy / kernel_self_test |
src/window.rs |
FrameRing rolling buffer used by ruv_temporal_push |
src/weights.rs |
Loader-side mirror of host wifi_densepose_temporal::weights. Parses the .rvne blob format (magic RVNE, version 1, FP32/FP16, CRC32-IEEE). Bit-exact with the host crate; a blob produced by the host's WeightBlob::serialize() parses here byte-for-byte. |
include/ruv_temporal.h |
Public C header consumed by main/temporal_task.c |
shim.c |
Empty C shim for idf_component_register |
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:
library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs—Copytrait andsize_ofnot in scope, ~16,000 errors.library/coreitself — "cannot resolve a prelude import", "attributes starting withrustcare 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.txt —
add_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.