Skip to content

F7: enable the D-cache, DMA buffers in a non-cacheable SRAM2 region - #12119

Open
MrScothh wants to merge 3 commits into
iNavFlight:maintenance-10.xfrom
MrScothh:feature/f7-dcache
Open

MrScothh wants to merge 3 commits into
iNavFlight:maintenance-10.xfrom
MrScothh:feature/f7-dcache

Conversation

@MrScothh

@MrScothh MrScothh commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

The F7 D-cache has been off since 2017 (39997f0, "Disable F7 I/D-caches for now"). F7 runs its code from flash
over AXIM: instructions go through the I-cache, but every constant and table the code reads as data pays the flash
wait states again (7 at 216 MHz). H7 already runs with the D-cache on, because its DMA buffers sit in DMA_RAM, a
region the MPU makes shareable, which the Cortex-M7 does not cache. This PR does the same on F7.

  • DMA_RAM becomes a real section on F7, as on H7 and AT32: .DMA_RAM (NOLOAD) in stm32_flash_f7_split.ld,
    placed in SRAM2 (16 KB). F722 and F745 carve SRAM2 out of their RAM region; F765 already had it.
  • memprot_stm32f7xx.c: one MPU region over the whole SRAM2 bank, shareable, set up before the caches are enabled.
    It uses H7's memprot_hal.c, now built for F7 too.
  • The DShot burst buffer (USE_DSHOT_DMAR) and the ADC buffer move into DMA_RAM. They join the per-port DShot
    buffers, the keep-alive buffer and the LED strip buffer, which were already tagged. A NOLOAD section is not zeroed
    at startup, so the ADC buffer is cleared at init, and a burst buffer is cleared when its timer gets its first
    motor (a channel that drives no motor has to read zero).
  • SD over SDIO (MATEKF765, MATEKF765SE) gets what H7 already has:
    • the afatfs cache and the MSC bounce buffer are 32-byte aligned;
    • the alignment check is no longer skipped on F7, and a refused write no longer leaves the card reported busy;
    • the buffer is invalidated before a read as well as after it.
      The cache maintenance by address works on whole lines, so an unaligned sector would lose its last bytes on a
      write. On the current image the afatfs cache happened to be aligned; on the base it was not.
  • After a config write, the config area is invalidated in the D-cache, so the reload reads flash. The erase leaves
    the old lines in the cache. Cortex-M7 erratum 1259864 (ES0334 2.1.1, "data corruption when using data cache
    configured in write-through") can also return stale data after a sequence of write-through stores, and flash
    programming is exactly that: flash is the only write-through memory in the default map.

Measured

Three boots per image, tasks 40 s after boot, same config on both images.

MAMBAF722_2022B, off on FLYWOOF722PRO (V2 board), off on
PID task avg / max 104 / 144-146 us 83-84 / 117-121 us 116-117 / 158-162 us 95-96 / 130-144 us
total load, SERIAL excluded 35.3-35.6 % 30.8-31.2 % 37.6-38.4 % 33.3-34.0 %

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

target flash ITCM moved to SRAM2
MAMBAF722_2022B +320 B (98.55 %) same 5440 B
FLYWOOF722PRO +232 B same 2624 B
MATEKF722SE +516 B (98.60 %) -16 B 2624 B
KAKUTEF7 / KAKUTEF7HDV -8 / -56 B -24 B (98.93 %) 2432 B
MATEKF765SE +136 B -16 B 3360 B
FRSKYPILOT +16 B -16 B 2112 B

All 107 F7 targets build. Across them, the fullest SRAM2 is at 34 % and the fullest RAM at 52 %.

The cost is RAM: SRAM2 now holds only DMA buffers. The RAM left for everything else shrinks by 16 KB minus what
moved (MAMBAF722_2022B: 101 104 -> 90 120 B free).

Testing

On a MAMBAF722_2022B and a Flywoo F722 Pro V2 (FLYWOOF722PRO image):

  • both boot, and every sensor comes up as on the base image (ICM42605, SPL06 or DPS310, GPS);
  • reboot to DFU from the D-cache image, and the flash after it, works;
  • config save without a reboot: 20 rounds of MSP_EEPROM_WRITE on each board, which reloads the config from flash.
    Every value read back right, and the last one survived a reboot;
  • the unit tests pass;
  • blackbox to the SPI flash chip, read back over MSP and decoded (Mamba: 66 s of log);
  • DShot300 to the boards' own ESCs, props off, and battery voltage and current through the ADC. I ran motor-test
    steps while disarmed and read MSP_ANALOG on each step. Both boards read the same with and without this PR:
motors MAMBAF722_2022B (burst DMA), this PR / base FLYWOOF722PRO (DMA per channel), this PR / base
1080 0.30 / 0.30 A 0.81 / 0.81 A
1150 0.74 / 0.76 A 0.98 / 0.97 A
1250 1.36 / 1.37 A, 15.7 V both 1.27 / 1.26 A, 15.9 V both
1150 0.76 / 0.76 A 0.99 / 0.97 A
1080 0.30 / 0.30 A 0.82 / 0.80 A
  • On the Mamba I also read the MPU and cache registers from the running image:
    • region 0 covers 0x2003C000 + 16 KB, shareable, TEX 0, C 1, B 0, full access;
    • the D-cache and I-cache are on;
    • CACR.SIWT and FORCEWT are 0, so the shareable region is not cached.

Not tested yet, and testers are welcome:

  • LED strip;
  • SD card logging and USB mass storage on an F765 board (SDIO);
  • any F745 board.

Open PRs that touch the same thing

The D-cache has been off on F7 since 2017 ("Disable F7 I/D-caches for
now"), so every constant and table read from flash pays the AXIM wait
states. On a MAMBAF722 2022B the PID task drops from 104 to 84 us on
average with it on.

DMA_RAM becomes a real section on F7, as on H7 and AT32: SRAM2 (16 KB) is
carved out of RAM in each chip linker script and the MPU makes the whole
bank shareable, which the Cortex-M7 does not cache. The DShot burst buffer
and the ADC buffer join the buffers already tagged DMA_RAM; the ADC buffer
is cleared at init, since the section is not zeroed at startup.

SD over SDIO (MATEKF765, MATEKF765SE) gets what H7 already has: the afatfs
cache and the MSC bounce buffer 32-byte aligned, the alignment check no
longer skipped on F7, and the buffer invalidated before a read as well as
after it. The cache maintenance by address works on whole lines.

After a config write the config area is invalidated in the D-cache, so
the reload reads flash: the erase leaves the old lines cached, and
Cortex-M7 erratum 1259864 (ES0334 2.1.1) can return stale data after
write-through stores, which is what flash programming does.
@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

Enable F7 D-cache with DMA buffers in non-cacheable SRAM2

✨ Enhancement 🐞 Bug fix ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Enable the F7 D-cache while isolating DMA buffers in MPU-protected SRAM2 for safe transfers.
• Initialize relocated ADC and DShot buffers because the new DMA section is not zeroed at startup.
• Align SDIO buffers and maintain cache coherence for SD transfers and configuration reloads.
Diagram

graph TD
  DMA["DMA clients"] --> SRAM2["SRAM2 DMA RAM"] <-- MPU["MPU region"] <-- INIT(["F7 startup"]) --> CACHE["D-cache"] --> SDIO["SDIO buffers"]
  CONFIG["Flash config"] --> CACHE
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Keep DMA buffers in general RAM with per-transfer cache maintenance
  • ➕ Preserves SRAM2 for other allocations and avoids reserving its entire 16 KB bank.
  • ➖ Requires correct clean or invalidate operations at every DMA boundary.
  • ➖ Introduces alignment and shared-cache-line risks across existing DMA users.

Recommendation: The dedicated SRAM2 region is preferable for the existing DMA buffers: it centralizes coherency policy and avoids adding maintenance to every peripheral path. Reviewers should weigh that simplicity against the documented reduction in general-purpose RAM.

Files changed (21) +118 / -21

Enhancement (7) +78 / -12
build_config.hMap F7 DMA_RAM attributes to a linker section +3/-0

Map F7 DMA_RAM attributes to a linker section

• Makes F7 DMA_RAM declarations land in the new .DMA_RAM section.

src/main/build/build_config.h

adc_impl.hPlace F7 ADC values in aligned DMA_RAM +1/-1

Place F7 ADC values in aligned DMA_RAM

• Extends the H7 ADC DMA buffer placement and 32-byte alignment rule to F7.

src/main/drivers/adc_impl.h

memprot_stm32f7xx.cDefine an MPU region for F7 SRAM2 +50/-0

Define an MPU region for F7 SRAM2

• Adds a single shareable MPU region covering the 16 KB SRAM2 bank used by DMA_RAM.

src/main/drivers/memprot_stm32f7xx.c

pwm_output.cMove DShot burst DMA buffers into SRAM2 +5/-1

Move DShot burst DMA buffers into SRAM2

• Places burst buffers in DMA_RAM and clears each timer's buffer when first assigned a motor, preserving zeros for unused channels.

src/main/drivers/pwm_output.c

asyncfatfs.cAlign the F7 FAT filesystem sector cache +4/-3

Align the F7 FAT filesystem sector cache

• Uses a separate 32-byte-aligned sector cache on F7, as on H7, to keep SDIO cache maintenance within its buffer.

src/main/io/asyncfatfs/asyncfatfs.c

usbd_storage_sd_spi.cUse aligned MSC bounce buffers for F7 SDIO +6/-6

Use aligned MSC bounce buffers for F7 SDIO

• Extends the H7 aligned intermediate-buffer path to F7 SDIO mass-storage reads and writes.

src/main/msc/usbd_storage_sd_spi.c

system_stm32f7xx.cConfigure the MPU before enabling F7 D-cache +9/-1

Configure the MPU before enabling F7 D-cache

• Sets up memory protection before enabling the D-cache in firmware startup. Bootloader builds retain their previous cache behavior.

src/main/target/system_stm32f7xx.c

Bug fix (3) +8 / -4
config_streamer_stm32f7.cInvalidate cached configuration after flash writes +2/-0

Invalidate cached configuration after flash writes

• Invalidates the configuration flash range when locking flash so a subsequent reload cannot read stale cached data.

src/main/config/config_streamer_stm32f7.c

adc_stm32f7xx.cClear F7 ADC values during initialization +3/-0

Clear F7 ADC values during initialization

• Zeros each ADC buffer explicitly because the NOLOAD DMA_RAM section is not cleared at startup.

src/main/drivers/adc_stm32f7xx.c

sdmmc_sdio_hal.cEnforce SDIO DMA alignment and pre-read invalidation +3/-4

Enforce SDIO DMA alignment and pre-read invalidation

• Applies 32-byte buffer alignment checks on F7 and invalidates receive buffers before DMA reads, complementing completion-time invalidation.

src/main/drivers/sdcard/sdmmc_sdio_hal.c

Documentation (1) +1 / -1
AGENTS.mdDocument F7 DMA_RAM cache behavior +1/-1

Document F7 DMA_RAM cache behavior

• Clarifies that DMA_RAM is available on F7 and is not cached on F7 or H7.

AGENTS.md

Other (10) +31 / -4
stm32f7.cmakeBuild F7 memory-protection support +4/-0

Build F7 memory-protection support

• Adds the shared MPU implementation and F7 region definition to F7 builds, with an eight-region limit.

cmake/stm32f7.cmake

stm32_flash_f722xe.ldReserve F722 SRAM2 for DMA_RAM +3/-1

Reserve F722 SRAM2 for DMA_RAM

• Carves 16 KB from general RAM and aliases the resulting SRAM2 bank as DMARAM.

src/main/target/link/stm32_flash_f722xe.ld

stm32_flash_f722xe_for_bl.ldReserve F722 SRAM2 in the bootloader-layout image +3/-1

Reserve F722 SRAM2 in the bootloader-layout image

• Splits out the same 16 KB SRAM2 bank and adds its DMARAM alias for this linker variant.

src/main/target/link/stm32_flash_f722xe_for_bl.ld

stm32_flash_f745xg.ldReserve F745 SRAM2 for DMA_RAM +3/-1

Reserve F745 SRAM2 for DMA_RAM

• Reduces general RAM by 16 KB and defines a dedicated SRAM2 bank aliased as DMARAM.

src/main/target/link/stm32_flash_f745xg.ld

stm32_flash_f745xg_for_bl.ldReserve F745 SRAM2 in the bootloader-layout image +3/-1

Reserve F745 SRAM2 in the bootloader-layout image

• Splits out F745 SRAM2 and provides the DMARAM alias in this linker variant.

src/main/target/link/stm32_flash_f745xg_for_bl.ld

stm32_flash_f765xg.ldAlias existing F765xg SRAM2 for DMA_RAM +1/-0

Alias existing F765xg SRAM2 for DMA_RAM

• Maps DMARAM to the SRAM2 bank already defined in the linker script.

src/main/target/link/stm32_flash_f765xg.ld

stm32_flash_f765xg_for_bl.ldAlias F765xg SRAM2 in the bootloader-layout image +1/-0

Alias F765xg SRAM2 in the bootloader-layout image

• Adds the DMARAM alias to this existing F765xg memory layout.

src/main/target/link/stm32_flash_f765xg_for_bl.ld

stm32_flash_f765xi.ldAlias existing F765xi SRAM2 for DMA_RAM +1/-0

Alias existing F765xi SRAM2 for DMA_RAM

• Maps DMARAM to the SRAM2 bank already defined for F765xi.

src/main/target/link/stm32_flash_f765xi.ld

stm32_flash_f765xi_for_bl.ldAlias F765xi SRAM2 in the bootloader-layout image +1/-0

Alias F765xi SRAM2 in the bootloader-layout image

• Adds the DMARAM alias to this existing F765xi memory layout.

src/main/target/link/stm32_flash_f765xi_for_bl.ld

stm32_flash_f7_split.ldDefine the F7 DMA_RAM linker section +11/-0

Define the F7 DMA_RAM linker section

• Places a 32-byte-aligned NOLOAD .DMA_RAM section in DMARAM and exposes its boundaries to MPU setup.

src/main/target/link/stm32_flash_f7_split.ld

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

qodo-free-for-open-source-projects Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Optional SD writes can stall ✓ Resolved
Description
SD_WriteBlocks_DMA() now rejects a buffer that is not 32-byte aligned, but the optional
multi-block writeCache is aligned to only four bytes. When that cache is enabled and placed at an
unaligned address, its writes are rejected; the rejection also leaves TXCplt set, so write-status
checks continue to report busy.
Code

src/main/drivers/sdcard/sdmmc_sdio_hal.c[609]

-#ifndef STM32F7
Evidence
The optional cache declaration guarantees only four-byte alignment, and the SDIO writer substitutes
it for the caller's buffer. The newly applicable F7 check rejects addresses outside a 32-byte
boundary after setting the busy flag; SD_CheckWrite() reads that flag.

src/main/drivers/sdcard/sdcard_sdio.c[48-57]
src/main/drivers/sdcard/sdcard_sdio.c[439-461]
src/main/drivers/sdcard/sdmmc_sdio_hal.c[590-614]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new F7 SDIO alignment check can reject the optional multi-block write cache, which has only four-byte alignment and can leave writes reporting busy.
## Fix Focus Areas
- src/main/drivers/sdcard/sdcard_sdio.c[54-57]
- src/main/drivers/sdcard/sdmmc_sdio_hal.c[600-614]
## Recommended Fix
Align `writeCache` to 32 bytes and ensure a rejected write does not leave `TXCplt` set.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can keep summaries lean with Findings visible per group, which tucks the rest behind a View link

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread src/main/drivers/sdcard/sdmmc_sdio_hal.c
A write refused before the transfer (block size, or the alignment check
that F7 now runs too) left TXCplt set, so the card read as busy for good.
Set it only right before the DMA starts, and clear it if the start fails.
The optional multi-block write cache is now 32-byte aligned as well, so
it passes that check if someone enables it.
@github-actions

github-actions Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

RAM / Flash usage vs. base commit e87050f — commit 4d2c700

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 +92 B (+0.02%) +76 B (+0.07%)
region layout changed since baseline
MATEKF765 +224 B (+0.03%) +584 B (+0.38%)
region layout changed since baseline
MATEKH743 +80 B (+0.01%) 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 4d2c700

Download firmware for PR #12119

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.

MrScothh added a commit to MrScothh/inav that referenced this pull request Oct 8, 2026
iNavFlight#12119 turns the F7 D-cache on and makes DMA_RAM an uncached SRAM2
region. A ring left in cached RAM could then hand the CPU a line it
cached before the stream filled it. The rings go in DMA_RAM, as on H7;
on F7 that is plain RAM until iNavFlight#12119, so on its own this changes nothing.

Not seen to fail at 115200: FLYWOOF722PRO with iNavFlight#12119 and the GPS
sending on the interrupt had 0-1 errors per boot with the rings in
cached RAM and in DMA_RAM alike.
MrScothh added a commit to MrScothh/inav that referenced this pull request Oct 8, 2026
MSP DisplayPort and the serial gimbal swap their own TX buffer into the port after opening it. On
H7 that buffer is in AXI SRAM, which the CPU caches, so the stream sends what is in RAM rather than
what was just written. Kakute H7 Mini with the GPS port given such a buffer (a test patch): the
u-blox never got its configuration (3 timeouts, no navigation messages); with this, a navigation
message every 120 ms and 0 errors over 3 reboots. On F7 every ring is cached once iNavFlight#12119 turns its
D-cache on: FLYWOOF722PRO with iNavFlight#12119 had 11-14 GPS errors and no fix on every boot, and 0 with this.

The lines the transfer covers are written back first. Without a D-cache, or on the uncached D2
rings, that does nothing.
The F7 UART receive rings of iNavFlight#12033 are compiled only then; without the
D-cache the port's own ring does.
MrScothh added a commit to MrScothh/inav that referenced this pull request Oct 8, 2026
On F7 DMA_RAM is plain RAM until iNavFlight#12119 turns the D-cache on and makes
it an uncached SRAM2 section, so until then each ring only duplicated
the port's own, 256 B per UART with a receive stream. They are compiled
only where build_config.h says DMA_RAM is uncached (DMA_RAM_UNCACHED,
set together with the D-cache), so either PR can go in first.
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.

1 participant