Summary
Three named functions are the entire remaining distance between the PulseEngine toolchain and a complete falcon flight-cascade image for the Cortex-M7. Everything else in the chain now works: the components publish with all required markers, meld fuses and packs them, and the result compiles to a self-contained, executing M7 image. Only these three fail, all on GI-FPU-002.
Filing fresh because the prior anchors are closed (#369 → #782 → #869 → #881, all CLOSED) and GI-FPU-002 is currently tracked only inside the #242 epic, so there is no actionable item for it.
The three functions
pulseengine:falcon-cascade/attitude@0.7.0#tick
GI-FPU-002: VFP register file exhausted (S0..S15 all live) — f32 expression too deep (phase 1)
pulseengine:falcon-cascade/ekf@0.7.0#estimate
GI-FPU-002: VFP register file exhausted (S0..S15 all live) — f32 expression too deep (phase 1)
pulseengine:falcon-cascade/position@0.7.0#tick
GI-FPU-002 phase 2: caller-saved VFP D-register file exhausted (D0..D7 all live) — f64 expression too deep
Consequence, measured on the fused image:
| stage |
exported in the M7 image? |
| rate |
yes |
| mixer |
yes |
| attitude |
no |
| position |
no |
| iekf / ekf |
no |
2 of 5. The cascade cannot close a control loop on target without the other three.
Fully public repro — no jess-private inputs
# 1. pull the six published components (anonymous)
for c in iekf position attitude rate mixer; do
TOK=$(curl -s "https://ghcr.io/token?scope=repository:pulseengine/falcon/$c:pull&service=ghcr.io" | jq -r .token)
D=$(curl -s -H "Authorization: Bearer $TOK" -H "Accept: application/vnd.oci.image.manifest.v1+json" \
https://ghcr.io/v2/pulseengine/falcon/$c/manifests/1.134.1 | jq -r .layers[0].digest)
curl -sL -H "Authorization: Bearer $TOK" https://ghcr.io/v2/pulseengine/falcon/$c/blobs/$D -o $c.wasm
done
# 2. fuse + pack (meld 0.52.0) — accepts, 1 page / 64 KB
meld fuse --memory shared --pack-rebase iekf.wasm position.wasm attitude.wasm rate.wasm mixer.wasm -o cascade.wasm
loom optimize cascade.wasm -o cascade.loom.wasm
# 3. lower (synth 0.55.0)
synth compile cascade.loom.wasm --relocatable -t cortex-m7dp -o cascade.o # 3 skips, 18 functions
synth compile cascade.loom.wasm -t cortex-m7dp --cortex-m -o cascade.elf # EXEC, 65,452 B
The self-contained image is real and runs: I have it executing on an RT1176 Renode model — linear memory initialised byte-exact (verified at non-zero probes), 214,696 instructions retired, PC in the export region. So this is not a paper artifact; it is a working image that is simply missing three functions.
The two sub-problems
1. Phase-1 S-register exhaustion (attitude#tick, ekf#estimate) — deep f32 expression trees in the control math exceed the caller-saved S-register file. D8..D15 are callee-saved and currently untouched; using them is push/pop in prologue/epilogue and would roughly double the available file.
2. Phase-2 D-register exhaustion (position#tick) — this one is worth a second look, because position is f32-only code. The f64 pressure comes from synth's own inline-f64 lowering of the i64↔float conversions added in v0.52. The --builtins routing proposed in #869 (__aeabi_ul2f/l2f/ul2d/l2d, core-register based, zero VFP pressure) was described in that issue's acceptance comment but never implemented — the lowering went inline-f64 instead.
That choice has a second consequence worth stating: it pins any conversion-carrying function to cortex-m7dp. mixer (no conversion) lowers on m7dp, m7 and m4f; rate lowers only on m7dp. So the estimator cannot be built for the single-precision M4 core it is architecturally assigned to — not for lack of hardware capability, but from the lowering strategy for one conversion.
Definition of done
Those three symbols reaching nm → T in the fused image — i.e. 5 of 5 cascade stages exported, which is a complete falcon cascade for the M7.
Standing offer
Point me at any branch and I will run it against the real fused cascade and the per-stage components and report the delta by symbol name, including reading the generated code for soundness rather than only checking that symbols appeared. That loop has caught two "green in isolation, no movement in the real op-mix" cases already (#369→#782, #869→v0.52), so it is worth running before a release rather than after.
No urgency implied — rate and mixer lower today and there is on-target work in front of me regardless. This is a request to track, not to prioritise.
Summary
Three named functions are the entire remaining distance between the PulseEngine toolchain and a complete falcon flight-cascade image for the Cortex-M7. Everything else in the chain now works: the components publish with all required markers,
meldfuses and packs them, and the result compiles to a self-contained, executing M7 image. Only these three fail, all onGI-FPU-002.Filing fresh because the prior anchors are closed (#369 → #782 → #869 → #881, all CLOSED) and
GI-FPU-002is currently tracked only inside the #242 epic, so there is no actionable item for it.The three functions
Consequence, measured on the fused image:
2 of 5. The cascade cannot close a control loop on target without the other three.
Fully public repro — no jess-private inputs
The self-contained image is real and runs: I have it executing on an RT1176 Renode model — linear memory initialised byte-exact (verified at non-zero probes), 214,696 instructions retired, PC in the export region. So this is not a paper artifact; it is a working image that is simply missing three functions.
The two sub-problems
1. Phase-1 S-register exhaustion (
attitude#tick,ekf#estimate) — deep f32 expression trees in the control math exceed the caller-saved S-register file.D8..D15are callee-saved and currently untouched; using them is push/pop in prologue/epilogue and would roughly double the available file.2. Phase-2 D-register exhaustion (
position#tick) — this one is worth a second look, becausepositionis f32-only code. The f64 pressure comes from synth's own inline-f64 lowering of the i64↔float conversions added in v0.52. The--builtinsrouting proposed in #869 (__aeabi_ul2f/l2f/ul2d/l2d, core-register based, zero VFP pressure) was described in that issue's acceptance comment but never implemented — the lowering went inline-f64 instead.That choice has a second consequence worth stating: it pins any conversion-carrying function to
cortex-m7dp.mixer(no conversion) lowers onm7dp,m7andm4f;ratelowers only onm7dp. So the estimator cannot be built for the single-precision M4 core it is architecturally assigned to — not for lack of hardware capability, but from the lowering strategy for one conversion.Definition of done
Those three symbols reaching
nm → Tin the fused image — i.e. 5 of 5 cascade stages exported, which is a complete falcon cascade for the M7.Standing offer
Point me at any branch and I will run it against the real fused cascade and the per-stage components and report the delta by symbol name, including reading the generated code for soundness rather than only checking that symbols appeared. That loop has caught two "green in isolation, no movement in the real op-mix" cases already (#369→#782, #869→v0.52), so it is worth running before a release rather than after.
No urgency implied —
rateandmixerlower today and there is on-target work in front of me regardless. This is a request to track, not to prioritise.