perf(f051): run the commutation hot path from RAM#19
Conversation
Hardware CI results — ARK 4IN1 bench (Flight Stand 50)Rebased onto Verdict: ✅ PASS — no regression on any gated metric
Per steady point, ctrl exec improved at 4 of 5 throttle points (best: 40% throttle, 79 → 70 µs). Coverage was complete: 3700/3700 samples on both the SWD perf channel and the stand channel. InterpretationImprovements are consistently in the right direction but mostly within the bench's ±6% run-to-run noise band at no-load — expected, since this change targets the commutation ISR path and no-load commutation work is light. The clearest signal is the −10% on the transient-heavy raw worst case (commutation ISRs preempt Next: re-gating under load with a prop ( Run data: 🤖 Generated with Claude Code |
The F051 executes flash at one wait state at 48MHz and SRAM at zero. The prefetch buffer hides the wait state on straight line code but every taken branch and interrupt entry pays it, and the commutation interrupts are exactly that kind of branchy code. Tag the hot path RAM_FUNC: the ADC1_COMP/TIM6_DAC/TIM14 handlers, interruptRoutine, PeriodElapsedCallback, commutate, comStep, tenKhzRoutine, getBemfState and the comparator helpers, about 2.9KB of code. For gcc the .ramfunc input section sits inside .data so the existing startup data init copies it, and the linker generates the flash/RAM veneers. For Keil/armclang the scatter file selects .ramfunc into RW_IRAM1 so armlink scatter loading does the same copy before main; toolchains must not differ in hot path timing. The Keil path still needs one build on Windows to confirm. RAM_FUNC is empty on every other MCU. Worst case F051 target keeps 2.1KB of RAM headroom above the 1.5KB heap/stack reserve.
6a2f8ca to
33d93ad
Compare
Hardware CI results — with prop installed (HQProp 5136 R36-GR-PC)Follow-up to the earlier no-load results. Rebased onto the latest Verdict: ✅ PASS — and a real efficiency improvement, not just noise
Current draw is lower at nearly every throttle point for the same commanded throttle while thrust holds or improves (e.g. 60%: 8.74A→8.19A at 140→132 gf; 70%: 11.89A→11.29A). That's a genuine efficiency gain, consistent with the theory: faster commutation ISRs waste less time on interrupt overhead as commutation frequency rises with load — which no-load testing couldn't exercise. Loop timing is essentially unchanged, which also makes sense: the commutation ISRs were never the bottleneck stretching Full per-point table and run data: gate: 🤖 Generated with Claude Code |
Follow-up: proper A/B on the fixed thrust stand — RAM_FUNC is performance-neutral on the powertrainTL;DR: re-tested this PR against pre-merge firmware with a corrected bench and an interleaved A/B protocol. The ~12% efficiency improvement reported earlier does not reproduce and is retracted — it was a thrust-measurement artifact, not a firmware effect. RAM_FUNC shows no measurable powertrain change (all deltas within the bench's now-quantified ±4% noise floor) and no regressions. Regression gates: PASS on both test runs. Why the earlier numbers were wrongThe 5" prop disc sat entirely inside the Flight Stand 50's mounting-plate footprint with the wake blowing into the plate. Wake impingement on the metrology side cancelled ~75% of true thrust (we measured 0.5 g/W where a 5" prop should read 2-4 g/W) and produced 55-66% sample-to-sample thrust noise. After flipping the prop and motor direction so the wake exits into free air: thrust coefficient tripled to physically-correct values, thrust CV dropped to 5.5-8.8%, and full-throttle efficiency reads a textbook 1.6-1.8 g/W at 27k RPM / 940 gf. Method
ResultsEfficiency (g/W, mean of 2 runs per firmware; "spread" = same-firmware run-to-run disagreement, i.e. the noise floor):
Electrical power at matched RPM (removes battery-voltage confound; the prop demands identical mechanical power at identical RPM):
Every delta is within the same-firmware spread; the signs are mixed. There is no efficiency effect, in either direction, resolvable by this bench. Firmware timing (steady-window, SWD perf struct): idle loop rate +1.2% on RAM_FUNC (61.1k → 61.9k iters/s, consistent with 0-wait-state execution); worst steady ctrl-loop exec 77-80 µs (A) vs 83-86 µs (B) — a few µs apart, far under gate thresholds; zero demag/desync events in all four runs. ConclusionThe merge stands on its original merits — moving the commutation hot path to RAM for deterministic 0-wait-state execution — but no powertrain efficiency claim should be attached to it. New propped baseline for gating future PRs captured from the RAM_FUNC runs ( 🤖 Generated with Claude Code |
The F051 executes flash at one wait state at 48 MHz and SRAM at zero. The prefetch buffer hides the wait state on straight-line code, but every taken branch and interrupt entry pays it — and the commutation interrupts are exactly that kind of branchy code.
This tags the hot path
RAM_FUNC: the ADC1_COMP/TIM6_DAC/TIM14 handlers,interruptRoutine,PeriodElapsedCallback,commutate,comStep,tenKhzRoutine,getBemfStateand the comparator helpers — about 2.9 KB of code..ramfuncinput section sits inside.data, so the existing startup data-init copies it and the linker generates the flash/RAM veneers..ramfuncintoRW_IRAM1so armlink scatter-loading does the same copy beforemain. (Still needs one Windows build to confirm.)RAM_FUNCis empty on every other MCU. Worst-case F051 target keeps 2.1 KB of RAM headroom above the 1.5 KB heap/stack reserve.Rebased onto
ark-release(coexists cleanly with the HWCI perf instrumentation). Hardware-CI gate results from the thrust-stand bench are posted below; propped load testing to follow.The mechanism is timing precision in the commutation interrupt, not raw speed.
AM32's sensorless commutation (getBemfState, commutate, PeriodElapsedCallback) detects the back-EMF zero-crossing and fires the next commutation step from an ISR. On the F051, flash executes at 1 wait state; every taken branch or interrupt-vector fetch pays that stall while the prefetch buffer refills. RAM has 0 wait states, so the same branchy ISR code runs faster and more deterministically — not just on average, but with less jitter from one commutation event to the next.
That timing precision matters directly for efficiency: if the commutation instant lags the true BEMF zero-crossing by even a couple of microseconds, the drive current ends up slightly out of phase with the back-EMF. That misalignment shows up as extra I²R copper loss and less torque per amp — the motor draws more current to produce the same thrust. Tighten the commutation timing, and the ESC extracts more torque per amp: same thrust, less current, higher g/W. This also explains the pattern we saw — the effect should scale with electrical frequency (commutation rate), and it did show up more consistently at mid-to-high throttle than at 10-20%, where the electrical period is long enough that a few microseconds of jitter barely matters.
One honest caveat: this is a single baseline-vs-PR comparison, and our own repeatability check earlier found up to ~9% run-to-run swing in efficiency at high power from bench noise alone (battery SOC drift, thermal effects) — so part of the observed +12% could still be within that noise band. What makes me reasonably confident it's real rather than noise is that 8 of 10 throttle points moved the same direction; pure noise wouldn't consistently favor one side. If you want to firm that up, a repeat capture of the PR branch itself (not just the baseline) would show whether the improvement holds under a second draw of the same noise.