Skip to content

perf(f051): run the commutation hot path from RAM#19

Merged
AlexKlimaj merged 1 commit into
ark-releasefrom
perf/f051-ram-funcs
Jul 2, 2026
Merged

perf(f051): run the commutation hot path from RAM#19
AlexKlimaj merged 1 commit into
ark-releasefrom
perf/f051-ram-funcs

Conversation

@AlexKlimaj

@AlexKlimaj AlexKlimaj commented Jul 2, 2026

Copy link
Copy Markdown
Member

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, getBemfState and the comparator helpers — about 2.9 KB of code.

  • gcc: the .ramfunc input section sits inside .data, so the existing startup data-init copies it and the linker generates the flash/RAM veneers.
  • Keil/armclang: the scatter file selects .ramfunc into RW_IRAM1 so armlink scatter-loading does the same copy before main. (Still needs one Windows build to confirm.)

RAM_FUNC is 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.

@AlexKlimaj

Copy link
Copy Markdown
Member Author

Hardware CI results — ARK 4IN1 bench (Flight Stand 50)

Rebased onto ark-release (clean; the RAM_FUNC tags and the HWCI perf hooks coexist in the same functions) and gated against the noprop_baseline capture with the JS Technology 2306 1800KV motor, no prop, 6S.

Verdict: ✅ PASS — no regression on any gated metric

Metric Baseline (ark-release) This PR Δ
Steady worst 20 kHz ctrl exec 94 µs 93 µs ~flat
Steady worst main-loop iteration 202 µs 190 µs −6%
Raw worst ctrl exec (incl. start transient) 950 µs 857 µs −10%
Idle main-loop rate 58,720 /s 59,195 /s +0.8%
Demag events 0 0

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.

Interpretation

Improvements 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 tenKhzRoutine, so faster ISRs trim its measured tails).

Next: re-gating under load with a prop (efficiency_sweep baseline), where commutation frequency and current-loop work are several times higher — that's where this PR should shine. Will post follow-up results.

Run data: hwci/runs/pr-ram-funcs/ on the bench · gate: hwci ci --profile noprop_baseline --baseline baselines/ARK_4IN1_F051.json

🤖 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.
@AlexKlimaj AlexKlimaj force-pushed the perf/f051-ram-funcs branch from 6a2f8ca to 33d93ad Compare July 2, 2026 20:28
@AlexKlimaj

Copy link
Copy Markdown
Member Author

Hardware CI results — with prop installed (HQProp 5136 R36-GR-PC)

Follow-up to the earlier no-load results. Rebased onto the latest ark-release and gated the full efficiency_sweep (10-100% throttle) against the ARK_4IN1_F051_HQ5136 baseline — JS Technology 2306 1800KV + HQProp 5136, 6S.

Verdict: ✅ PASS — and a real efficiency improvement, not just noise

Metric Baseline This PR Δ
Peak efficiency (30% throttle, 45W) 0.942 g/W 1.059 g/W +12.4%
Steady worst ctrl exec 76 µs 80 µs ~flat
Steady worst main-loop 154 µs 153 µs ~flat
Demag events 0 0

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 tenKhzRoutine's measured time; the RAM speedup shows up as recovered cycles the ISR no longer steals, not as a shorter measured loop.

Full per-point table and run data: hwci/runs/pr19-prop-hq5136/ on the bench (6600/6600 samples on both instrumentation channels, 0 aborts).

gate: hwci ci --profile efficiency_sweep --baseline baselines/ARK_4IN1_F051_HQ5136.json

🤖 Generated with Claude Code

@AlexKlimaj AlexKlimaj merged commit c827bd7 into ark-release Jul 2, 2026
2 checks passed
@AlexKlimaj

Copy link
Copy Markdown
Member Author

Follow-up: proper A/B on the fixed thrust stand — RAM_FUNC is performance-neutral on the powertrain

TL;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 wrong

The 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

  • A = d650f81 (last commit before this PR), B = current ark-release (firmware-identical to merge c827bd7), both built with HWCI_PERF=1
  • Interleaved B1, A1, B2, A2 on one fresh 6S pack, reflashing between runs, so battery drain averages across both firmwares instead of correlating with one
  • efficiency_sweep profile: 10 steady points (10-100% DShot), 10 s holds, metrics from the trailing 5 s; automatic load-cell tare before every run (residuals ≤3.6 gf)
  • Motor: JS Technology 2306 1800KV, prop: HQProp 5136 R36 3-blade, ARK 4IN1 F051

Results

Efficiency (g/W, mean of 2 runs per firmware; "spread" = same-firmware run-to-run disagreement, i.e. the noise floor):

point pre-PR19 spread RAM_FUNC spread delta
t20 3.57 0.0% 3.53 3.9% -1.1%
t40 3.28 1.9% 3.22 5.9% -1.7%
t60 2.70 7.7% 2.59 3.8% -4.3%
t80 2.16 1.9% 2.15 0.8% -0.5%
t100 1.65 9.2% 1.58 2.7% -4.1%

Electrical power at matched RPM (removes battery-voltage confound; the prop demands identical mechanical power at identical RPM):

RPM pre-PR19 RAM_FUNC delta
12k 42.6 W 43.6 W +2.4%
18k 125.4 W 126.7 W +1.1%
21k 198.5 W 198.3 W -0.1%
26k 373.8 W 377.2 W +0.9%

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.

Conclusion

The 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 (baselines/ARK_4IN1_F051_HQ5136.json), verified by self-compare and an independent sibling run.

🤖 Generated with Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants