Skip to content

asyncfatfs: keep afatfs_poll out of the ITCM scheduler - #12118

Closed
MrScothh wants to merge 2 commits into
iNavFlight:maintenance-10.xfrom
MrScothh:opt/afatfs-poll-out-of-itcm
Closed

MrScothh wants to merge 2 commits into
iNavFlight:maintenance-10.xfrom
MrScothh:opt/afatfs-poll-out-of-itcm

Conversation

@MrScothh

@MrScothh MrScothh commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

#12120 and #12121 (throttle boost, thrust linearization) and #12132 (CRSF and SBUS receivers on DMA) build on this one, for the ITCM it frees on KAKUTEF7 and KAKUTEF7HDV. It can go in on its own.

taskRunRealtimeCallbacks() calls afatfs_poll() from scheduler(), which is FAST_CODE, and LTO inlines the
whole poll into it. On targets with an SD card that puts 800 to 980 bytes of the file system's state machine in ITCM,
depending on the target. KAKUTEF7 and KAKUTEF7HDV are at 99 % ITCM on maintenance-10.x, so they stop linking as
soon as anything else on the fast path grows a little. It already happens:

On F7, whose ITCM is 16 KB, NOINLINE on afatfs_poll() keeps it in flash with the rest of afatfs; H7 has the
room and keeps it in ITCM. The poll only starts the next SD operation
when one is pending, at most once per scheduler loop, and SD writes are bound by the card, not by where this code
runs. The DShot completion called from the same place stays inlined in the scheduler, in ITCM (checked in the map).

Size, maintenance-10.x 3f9fde4 -> this branch

target ITCM flash
KAKUTEF7 16 232 -> 15 288 B (99.07 -> 93.31 %) -68 B
KAKUTEF7HDV 16 232 -> 15 416 B (99.07 -> 94.09 %) -768 B
FRSKYPILOT 15 552 -> 14 576 B -64 B
MATEKF765SE 16 160 -> 15 192 B -56 B
MATEKF722SE 12 360 -> 11 432 B +156 B (98.52 %)
MATEKH743 unchanged (H7 keeps the poll in ITCM) unchanged
MATEKF405SE, KAKUTEF7MINI, MAMBAF722_2022B (no SD) unchanged unchanged

Testing

On F7 this change only moves code: afatfs_poll() and its callers are the same, and the unit tests pass. On H7,
F4 and AT32 the builds are identical to maintenance-10.x. None of my F7 boards has an SD card, so a blackbox log
written to SD and decoded from an F7 board would be welcome.

taskRunRealtimeCallbacks() calls afatfs_poll() from scheduler(), which is
FAST_CODE, and LTO inlines the file system's state machine into it. On
targets with an SD card that puts 800 to 980 bytes of afatfs code in ITCM,
depending on the target, where KAKUTEF7 and KAKUTEF7HDV are at 99%. The
poll does a little work at most once per scheduler loop, so it runs from
flash like the rest of afatfs.
@qodo-code-review

Copy link
Copy Markdown
Contributor

ⓘ Qodo reviews are paused because the subscription is no longer active. Ask your workspace admin to reactivate the subscription to resume reviews. Manage billing

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Keep async FAT polling out of the ITCM scheduler

🐞 Bug fix 🕐 Less than 10 minutes

Grey Divider

AI Description

• Prevent LTO from inlining async FAT polling into the ITCM scheduler, restoring space on SD-card
 targets.
• Keep polling behavior unchanged; reported builds regain ITCM headroom and unit tests pass.
Diagram

graph TD
    A["Scheduler ITCM"] --> B["Realtime callbacks"] --> C["FAT poll flash"] --> D["SD card poll"] --> E["FAT operations"]
Loading
High-Level Assessment

Keep NOINLINE on afatfs_poll: it targets the unwanted filesystem inlining without moving the other realtime callbacks out of the fast path. Making the entire callback out of line would be broader than necessary.

Files changed (1) +2 / -1

Bug fix (1) +2 / -1
asyncfatfs.cPrevent async FAT polling from being inlined into ITCM +2/-1

Prevent async FAT polling from being inlined into ITCM

• Marks afatfs_poll NOINLINE and documents why its scheduler caller must not pull the filesystem state machine into ITCM. The poll body and its callers remain unchanged.

src/main/io/asyncfatfs/asyncfatfs.c

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider

Great, no issues found!

Qodo reviewed your code and found no material issues that require review

Grey Divider

Tip of the day
💡 Did you know, you can route each severity your way: inline, summary, both, or drop

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

@github-actions

github-actions Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

RAM / Flash usage vs. base commit e87050f — commit c8ecb50

Using the nearest available size baseline — the PR's exact base commit has no stored baseline yet.

Target Flash Δ RAM Δ
MATEKF405 -32 B (-0.00%) CCM: ±0 B (±0.00%)
RAM: +8 B (+0.01%)
MATEKF722 -252 B (-0.05%) ITCM_RAM: -928 B (-7.40%)
RAM: -8 B (-0.01%)
TCM: +4 B (+0.02%)
MATEKF765 +32 B (+0.00%) DTCM_RAM: ±0 B (±0.00%)
SRAM1: ±0 B (±0.00%)
MATEKH743 +32 B (+0.00%) D2_RAM: ±0 B (±0.00%)
DTCM_RAM: ±0 B (±0.00%)
ITCM_RAM: ±0 B (±0.00%)
RAM: ±0 B (±0.00%)

See RAM/flash optimization guide for techniques to reduce usage.

@github-actions

github-actions Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Test firmware build ready — commit c8ecb50

Download firmware for PR #12118

251 targets built. Find your board's .hex file by name on that page (e.g. MATEKF405SE.hex). Files are individually downloadable — no GitHub login required.

Development build for testing only. Use Full Chip Erase when flashing.

@error414

error414 commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

I rememeber that make afatfs_poll inline was first proposal from claude when I wanted to reduce size of code in ITCM RAM for F7 CPU.

The question is if whole taskRunRealtimeCallbacks should not be NOINLINE and pasted in ordinary RAM. Scheculer was moved to ITCM by commit 7faeb47 (Second iteration of makinf F7 fast)

@MrScothh

MrScothh commented Oct 7, 2026

Copy link
Copy Markdown
Contributor Author

@error414 I measured both on a Flywoo F722 (DSHOT300, 2 kHz PID loop, tasks read three times on each image) and the ITCM on the KAKUTEF7 targets:

this PR (afatfs_poll out) whole taskRunRealtimeCallbacks out
PID task, average 121 / 121 / 120 us 124 / 123 / 124 us
SYSTEM task (the callbacks), average 3 us 4 us
system load 12 % 13 %
ITCM used on KAKUTEF7HDV 15 416 B 13 832 B

Taking the whole function out frees another 1.5 KB, but the PID task gets about 3 us slower in every reading. pwmCompleteMotorUpdate() is only called from the callbacks, so this comes from how LTO lays out the rest once the function leaves ITCM; I have not tracked down exactly what moved. This PR frees the ~800 B #12063 needs without touching the motor path, so I'd keep it as is and move the rest only when ITCM runs out again. If you'd rather have the larger margin now, it's a one-line change on top.

@error414

error414 commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

ook, and do wee need to move afatfs_poll for H743 which has much bigger ITCM memory 16kB vs 64kB? on H743 could be activated terrain feature which reads from SD card and can be affected

The H7's ITCM is 64 KB and about a quarter used, so the poll can stay
inlined in the scheduler there, as on maintenance-10.x. F7 builds are
identical to the previous commit; H7, F4 and AT32 builds are identical
to maintenance-10.x.
@MrScothh

MrScothh commented Oct 8, 2026

Copy link
Copy Markdown
Contributor Author

@error414 Good point: the H743 doesn't need it. Its ITCM is about a quarter full (TBS_LUCID_H7_WING 16 KB of 64 KB), so I've limited the change to F7 in c8ecb50: F7 builds are identical to before, and H7, F4 and AT32 builds are identical to maintenance-10.x, so on H7 afatfs_poll stays in ITCM as it was.

Before changing it I checked whether it mattered, on an H743 (TBS Lucid H7 Wing) with an SD card and terrain on, with a real tile file (N46E011), a HITL flight from Trento to the top of the Bondone and the blackbox logging at 2 kHz to the same card:

afatfs_poll in ITCM in flash
terrain: blocks requested / granted / valid 40 / 40 / 13 40 / 40 / 13
terrain height at the summit 2079 m 2079 m
SYSTEM task (where afatfs_poll runs), max / avg 26 / 1 us 28 / 1 us
TERRAIN_IO task, max / avg 145 / 1 us 145 / 1 us
PID task, max / avg 169 / 58 us 166 / 58 us
blackbox log on the same card 13.1 MB, read back whole 13.0 MB, read back whole

No difference either way, but there's no reason to move it where the room isn't needed.

@error414

error414 commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

I have same experience, I tested "NOINLINE" on matekh743 and I didn't see any issue. Unfortunately I don't have F7 with SD card slot.

@MrScothh

MrScothh commented Oct 8, 2026

Copy link
Copy Markdown
Contributor Author

@error414 Thanks for trying it on the MATEKH743. I haven't got an F7 with an SD slot on the bench either; only F7 targets with SD change here (the others build the same as before), so a log from one of those is still the missing piece.

@MrScothh MrScothh mentioned this pull request Oct 9, 2026
5 tasks done
@MrScothh MrScothh closed this Oct 19, 2026
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