From 55853527a8f6740f8c088884be819e0dca978513 Mon Sep 17 00:00:00 2001
From: MrScothh <167884257+MrScothh@users.noreply.github.com>
Date: Wed, 7 Oct 2026 01:39:33 +0200
Subject: [PATCH 1/3] F7: enable the D-cache, DMA buffers in a non-cacheable
SRAM2 region
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.
---
AGENTS.md | 2 +-
cmake/stm32f7.cmake | 4 ++
src/main/build/build_config.h | 3 ++
src/main/config/config_streamer_stm32f7.c | 2 +
src/main/drivers/adc_impl.h | 2 +-
src/main/drivers/adc_stm32f7xx.c | 3 ++
src/main/drivers/memprot_stm32f7xx.c | 50 +++++++++++++++++++
src/main/drivers/pwm_output.c | 6 ++-
src/main/drivers/sdcard/sdmmc_sdio_hal.c | 7 ++-
src/main/io/asyncfatfs/asyncfatfs.c | 7 +--
src/main/msc/usbd_storage_sd_spi.c | 12 ++---
src/main/target/link/stm32_flash_f722xe.ld | 4 +-
.../target/link/stm32_flash_f722xe_for_bl.ld | 4 +-
src/main/target/link/stm32_flash_f745xg.ld | 4 +-
.../target/link/stm32_flash_f745xg_for_bl.ld | 4 +-
src/main/target/link/stm32_flash_f765xg.ld | 1 +
.../target/link/stm32_flash_f765xg_for_bl.ld | 1 +
src/main/target/link/stm32_flash_f765xi.ld | 1 +
.../target/link/stm32_flash_f765xi_for_bl.ld | 1 +
src/main/target/link/stm32_flash_f7_split.ld | 11 ++++
src/main/target/system_stm32f7xx.c | 10 +++-
21 files changed, 118 insertions(+), 21 deletions(-)
create mode 100644 src/main/drivers/memprot_stm32f7xx.c
diff --git a/AGENTS.md b/AGENTS.md
index b2c077191d5..ab9ab5fd6cb 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -86,7 +86,7 @@ INAV uses special memory attributes for performance-critical code on resource-co
```c
FASTRAM // Fast RAM section (aligned)
EXTENDED_FASTRAM // Extended fast RAM (STM32F4/F7 only)
-DMA_RAM // DMA-accessible RAM (STM32H7, AT32F43x)
+DMA_RAM // DMA-accessible RAM, not cached on F7/H7 (STM32F7, STM32H7, AT32F43x)
SLOW_RAM // Slower external RAM - deprecated, o not use
STATIC_FASTRAM // Static variable in fast RAM
STATIC_FASTRAM_UNIT_TESTED // Static fast RAM variable visible in unit tests
diff --git a/cmake/stm32f7.cmake b/cmake/stm32f7.cmake
index d8f52de333f..a0da095fc51 100644
--- a/cmake/stm32f7.cmake
+++ b/cmake/stm32f7.cmake
@@ -75,6 +75,9 @@ main_sources(STM32F7_SRC
drivers/timer_impl_hal.c
drivers/timer_stm32f7xx.c
drivers/system_stm32f7xx.c
+ drivers/memprot.h
+ drivers/memprot_hal.c
+ drivers/memprot_stm32f7xx.c
drivers/serial_uart_stm32f7xx.c
drivers/serial_uart_hal.c
drivers/sdcard/sdmmc_sdio_hal.c
@@ -91,6 +94,7 @@ set(STM32F7_DEFINITIONS
${CORTEX_M7_DEFINITIONS}
USE_HAL_DRIVER
USE_FULL_LL_DRIVER
+ MAX_MPU_REGIONS=8
)
function(target_stm32f7xx)
diff --git a/src/main/build/build_config.h b/src/main/build/build_config.h
index 75bb46668d7..2e505450bac 100644
--- a/src/main/build/build_config.h
+++ b/src/main/build/build_config.h
@@ -57,6 +57,9 @@
#elif defined (AT32F43x)
#define DMA_RAM __attribute__ ((section(".DMA_RAM")))
#define SLOW_RAM __attribute__ ((section(".SLOW_RAM")))
+#elif defined (STM32F7)
+#define DMA_RAM __attribute__ ((section(".DMA_RAM")))
+#define SLOW_RAM
#else
#define DMA_RAM
#define SLOW_RAM
diff --git a/src/main/config/config_streamer_stm32f7.c b/src/main/config/config_streamer_stm32f7.c
index 61b148fce57..867e86972a0 100644
--- a/src/main/config/config_streamer_stm32f7.c
+++ b/src/main/config/config_streamer_stm32f7.c
@@ -106,6 +106,8 @@ void config_streamer_impl_unlock(void)
void config_streamer_impl_lock(void)
{
HAL_FLASH_Lock();
+ // The D-cache keeps lines from before the erase, and erratum 1259864 can serve stale write-through data
+ SCB_InvalidateDCache_by_Addr((uint32_t *)&__config_start, &__config_end - &__config_start);
}
int config_streamer_impl_write_word(config_streamer_t *c, config_streamer_buffer_align_type_t *buffer)
diff --git a/src/main/drivers/adc_impl.h b/src/main/drivers/adc_impl.h
index 32f4414e83f..146d65e404c 100644
--- a/src/main/drivers/adc_impl.h
+++ b/src/main/drivers/adc_impl.h
@@ -28,7 +28,7 @@
#define ADC_TAG_MAP_COUNT 10
#endif
-#if defined(STM32H7)
+#if defined(STM32H7) || defined(STM32F7)
#define ADC_VALUES_ALIGNMENT(def) DMA_RAM def __attribute__ ((aligned (32)))
#else
#define ADC_VALUES_ALIGNMENT(def) def
diff --git a/src/main/drivers/adc_stm32f7xx.c b/src/main/drivers/adc_stm32f7xx.c
index 4b5aae88384..a18dd6f4289 100644
--- a/src/main/drivers/adc_stm32f7xx.c
+++ b/src/main/drivers/adc_stm32f7xx.c
@@ -83,6 +83,9 @@ static void adcInstanceInit(ADCDevice adcDevice)
{
adcDevice_t * adc = &adcHardware[adcDevice];
+ // DMA_RAM is not zeroed at startup: read 0, not leftovers, if the DMA never starts
+ memset((void *)adcValues[adcDevice], 0, sizeof(adcValues[adcDevice]));
+
RCC_ClockCmd(adc->rccDMA, ENABLE);
RCC_ClockCmd(adc->rccADC, ENABLE);
diff --git a/src/main/drivers/memprot_stm32f7xx.c b/src/main/drivers/memprot_stm32f7xx.c
new file mode 100644
index 00000000000..5dfb4eec0ca
--- /dev/null
+++ b/src/main/drivers/memprot_stm32f7xx.c
@@ -0,0 +1,50 @@
+/*
+ * This file is part of Cleanflight and Betaflight.
+ *
+ * Cleanflight and Betaflight are free software. You can redistribute
+ * this software and/or modify this software under the terms of the
+ * GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * Cleanflight and Betaflight are distributed in the hope that they
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software.
+ *
+ * If not, see .
+ */
+
+#include "platform.h"
+
+#include "common/utils.h"
+
+#include "memprot.h"
+
+#ifndef BOOTLOADER
+
+// Defined in linker script
+extern uint8_t dmaram_start;
+
+mpuRegion_t mpuRegions[] = {
+ {
+ // The whole 16 KB SRAM2 bank, where DMA_RAM is the only section: shareable, which the Cortex-M7 does not cache
+ .start = (uint32_t)&dmaram_start,
+ .end = 0,
+ .size = MPU_REGION_SIZE_16KB,
+ .perm = MPU_REGION_FULL_ACCESS,
+ .exec = MPU_INSTRUCTION_ACCESS_ENABLE,
+ .shareable = MPU_ACCESS_SHAREABLE,
+ .cacheable = MPU_ACCESS_CACHEABLE,
+ .bufferable = MPU_ACCESS_NOT_BUFFERABLE,
+ },
+};
+
+unsigned mpuRegionCount = ARRAYLEN(mpuRegions);
+
+STATIC_ASSERT(ARRAYLEN(mpuRegions) <= MAX_MPU_REGIONS, MPU_region_count_exceeds_limit);
+
+#endif
diff --git a/src/main/drivers/pwm_output.c b/src/main/drivers/pwm_output.c
index 20aed6c7264..17136b26a36 100644
--- a/src/main/drivers/pwm_output.c
+++ b/src/main/drivers/pwm_output.c
@@ -91,7 +91,7 @@
typedef void (*pwmWriteFuncPtr)(uint8_t index, uint16_t value); // function pointer used to write motors
#ifdef USE_DSHOT_DMAR
- timerDMASafeType_t dmaBurstBuffer[MAX_DMA_TIMERS][DSHOT_DMA_BUFFER_SIZE * 4];
+static DMA_RAM timerDMASafeType_t dmaBurstBuffer[MAX_DMA_TIMERS][DSHOT_DMA_BUFFER_SIZE * 4];
#endif
#ifdef USE_DSHOT
@@ -437,6 +437,10 @@ static pwmOutputPort_t * motorConfigDshot(const timerHardware_t * timerHardware,
return NULL;
}
+ // DMA_RAM is NOLOAD: a channel of this timer that drives no motor must read zero, not leftovers
+ if (!burstDmaTimers[burstDmaTimerIndex].dmaBurstBuffer) {
+ ZERO_FARRAY(dmaBurstBuffer[burstDmaTimerIndex]);
+ }
port->dmaBurstBuffer = &dmaBurstBuffer[burstDmaTimerIndex][0];
burstDmaTimer_t *burstDmaTimer = &burstDmaTimers[burstDmaTimerIndex];
burstDmaTimer->dmaBurstBuffer = port->dmaBurstBuffer;
diff --git a/src/main/drivers/sdcard/sdmmc_sdio_hal.c b/src/main/drivers/sdcard/sdmmc_sdio_hal.c
index 23b9c34678c..7b4d232555e 100644
--- a/src/main/drivers/sdcard/sdmmc_sdio_hal.c
+++ b/src/main/drivers/sdcard/sdmmc_sdio_hal.c
@@ -606,11 +606,9 @@ SD_Error_t SD_WriteBlocks_DMA(uint64_t WriteAddress, uint32_t *buffer, uint32_t
return SD_ERROR; // unsupported.
}
-#ifndef STM32F7
if ((uint32_t)buffer & 0x1f) {
return SD_ADDR_MISALIGNED;
}
-#endif
// Ensure the data is flushed to main memory
SCB_CleanDCache_by_Addr(buffer, NumberOfBlocks * BlockSize);
@@ -646,11 +644,9 @@ SD_Error_t SD_ReadBlocks_DMA(uint64_t ReadAddress, uint32_t *buffer, uint32_t Bl
return SD_ERROR; // unsupported.
}
-#ifndef STM32F7
if ((uint32_t)buffer & 0x1f) {
return SD_ADDR_MISALIGNED;
}
-#endif
SD_Handle.RXCplt = 1;
@@ -658,6 +654,9 @@ SD_Error_t SD_ReadBlocks_DMA(uint64_t ReadAddress, uint32_t *buffer, uint32_t Bl
sdReadParameters.BlockSize = BlockSize;
sdReadParameters.NumberOfBlocks = NumberOfBlocks;
+ // A dirty line evicted during the transfer would overwrite what the DMA wrote
+ SCB_InvalidateDCache_by_Addr(buffer, NumberOfBlocks * BlockSize);
+
#ifdef STM32F7
if (sd_dma.Init.Direction != DMA_PERIPH_TO_MEMORY) {
sd_dma.Init.Direction = DMA_PERIPH_TO_MEMORY;
diff --git a/src/main/io/asyncfatfs/asyncfatfs.c b/src/main/io/asyncfatfs/asyncfatfs.c
index cfa7da71ae8..57ec1cccad7 100644
--- a/src/main/io/asyncfatfs/asyncfatfs.c
+++ b/src/main/io/asyncfatfs/asyncfatfs.c
@@ -437,7 +437,7 @@ typedef struct afatfs_t {
} initState;
#endif
-#ifdef STM32H7
+#if defined(STM32H7) || defined(STM32F7)
uint8_t *cache;
#else
uint8_t cache[AFATFS_SECTOR_SIZE * AFATFS_NUM_CACHE_SECTORS];
@@ -487,7 +487,8 @@ typedef struct afatfs_t {
uint32_t rootDirectorySectors; // Zero on FAT32, for FAT16 the number of sectors that the root directory occupies
} afatfs_t;
-#ifdef STM32H7
+#if defined(STM32H7) || defined(STM32F7)
+// SD DMA with the D-cache on: cache maintenance works on whole 32-byte lines
static uint8_t afatfs_cache[AFATFS_SECTOR_SIZE * AFATFS_NUM_CACHE_SECTORS] __attribute__((aligned(32)));
#endif
@@ -3675,7 +3676,7 @@ bool afatfs_isCurrentDirRoot(void)
void afatfs_init(void)
{
-#ifdef STM32H7
+#if defined(STM32H7) || defined(STM32F7)
afatfs.cache = afatfs_cache;
#endif
afatfs.filesystemState = AFATFS_FILESYSTEM_STATE_INITIALIZATION;
diff --git a/src/main/msc/usbd_storage_sd_spi.c b/src/main/msc/usbd_storage_sd_spi.c
index 26a38e00b6a..d6c50caf19f 100644
--- a/src/main/msc/usbd_storage_sd_spi.c
+++ b/src/main/msc/usbd_storage_sd_spi.c
@@ -58,9 +58,9 @@
#define STORAGE_BLK_NBR 0x10000
#define STORAGE_BLK_SIZ 0x200
-// H7 SDIO DMA requires 32-byte aligned buffers
+// SDIO DMA with the D-cache on (F7, H7) requires 32-byte aligned buffers
// USB MSC bot_data buffer is only 16-byte aligned, so we need an intermediate buffer
-#if defined(STM32H7) && defined(USE_SDCARD_SDIO)
+#if (defined(STM32H7) || defined(STM32F7)) && defined(USE_SDCARD_SDIO)
__attribute__((aligned(32))) static uint8_t alignedBuffer[512];
#endif
@@ -233,8 +233,8 @@ static int8_t STORAGE_Read (uint8_t lun,
LED1_ON;
for (int i = 0; i < blk_len; i++) {
uint32_t timeout;
-#if defined(STM32H7) && defined(USE_SDCARD_SDIO)
- // H7 SDIO DMA requires 32-byte aligned buffers
+#if (defined(STM32H7) || defined(STM32F7)) && defined(USE_SDCARD_SDIO)
+ // SDIO DMA requires 32-byte aligned buffers
// USB MSC buffer may not be aligned, so use intermediate buffer
timeout = 0;
while (sdcard_readBlock(blk_addr + i, alignedBuffer, NULL, 0) == 0) {
@@ -293,8 +293,8 @@ static int8_t STORAGE_Write (uint8_t lun,
LED1_ON;
for (int i = 0; i < blk_len; i++) {
uint32_t timeout;
-#if defined(STM32H7) && defined(USE_SDCARD_SDIO)
- // H7 SDIO DMA requires 32-byte aligned buffers
+#if (defined(STM32H7) || defined(STM32F7)) && defined(USE_SDCARD_SDIO)
+ // SDIO DMA requires 32-byte aligned buffers
// USB MSC buffer may not be aligned, so use intermediate buffer
memcpy(alignedBuffer, buf + (i * 512), 512);
timeout = 0;
diff --git a/src/main/target/link/stm32_flash_f722xe.ld b/src/main/target/link/stm32_flash_f722xe.ld
index 3a9094ed81f..739b24056a5 100644
--- a/src/main/target/link/stm32_flash_f722xe.ld
+++ b/src/main/target/link/stm32_flash_f722xe.ld
@@ -38,11 +38,13 @@ MEMORY
FLASH1 (rx) : ORIGIN = 0x08008000, LENGTH = 480K
TCM (rwx) : ORIGIN = 0x20000000, LENGTH = 64K
- RAM (rwx) : ORIGIN = 0x20010000, LENGTH = 192K
+ RAM (rwx) : ORIGIN = 0x20010000, LENGTH = 176K
+ SRAM2 (rwx) : ORIGIN = 0x2003C000, LENGTH = 16K
MEMORY_B1 (rx) : ORIGIN = 0x60000000, LENGTH = 0K
}
REGION_ALIAS("STACKRAM", TCM)
REGION_ALIAS("FASTRAM", TCM)
+REGION_ALIAS("DMARAM", SRAM2)
INCLUDE "stm32_flash_f7_split.ld"
diff --git a/src/main/target/link/stm32_flash_f722xe_for_bl.ld b/src/main/target/link/stm32_flash_f722xe_for_bl.ld
index 83c9a43fc5e..581c5d34015 100644
--- a/src/main/target/link/stm32_flash_f722xe_for_bl.ld
+++ b/src/main/target/link/stm32_flash_f722xe_for_bl.ld
@@ -38,12 +38,14 @@ MEMORY
FLASH1 (rx) : ORIGIN = 0x08010000, LENGTH = 448K
TCM (rwx) : ORIGIN = 0x20000000, LENGTH = 64K
- RAM (rwx) : ORIGIN = 0x20010000, LENGTH = 192K
+ RAM (rwx) : ORIGIN = 0x20010000, LENGTH = 176K
+ SRAM2 (rwx) : ORIGIN = 0x2003C000, LENGTH = 16K
MEMORY_B1 (rx) : ORIGIN = 0x60000000, LENGTH = 0K
}
REGION_ALIAS("STACKRAM", TCM)
REGION_ALIAS("FASTRAM", TCM)
+REGION_ALIAS("DMARAM", SRAM2)
__firmware_start = ORIGIN(FLASH);
diff --git a/src/main/target/link/stm32_flash_f745xg.ld b/src/main/target/link/stm32_flash_f745xg.ld
index ddb73a4dc50..54981a7ae1e 100644
--- a/src/main/target/link/stm32_flash_f745xg.ld
+++ b/src/main/target/link/stm32_flash_f745xg.ld
@@ -38,11 +38,13 @@ MEMORY
FLASH1 (rx) : ORIGIN = 0x08010000, LENGTH = 960K
TCM (rwx) : ORIGIN = 0x20000000, LENGTH = 64K
- RAM (rwx) : ORIGIN = 0x20010000, LENGTH = 256K
+ RAM (rwx) : ORIGIN = 0x20010000, LENGTH = 240K
+ SRAM2 (rwx) : ORIGIN = 0x2004C000, LENGTH = 16K
MEMORY_B1 (rx) : ORIGIN = 0x60000000, LENGTH = 0K
}
/* note CCM could be used for stack */
REGION_ALIAS("STACKRAM", TCM)
REGION_ALIAS("FASTRAM", TCM)
+REGION_ALIAS("DMARAM", SRAM2)
INCLUDE "stm32_flash_f7_split.ld"
diff --git a/src/main/target/link/stm32_flash_f745xg_for_bl.ld b/src/main/target/link/stm32_flash_f745xg_for_bl.ld
index c7667d1dc26..54a5bddcb7d 100644
--- a/src/main/target/link/stm32_flash_f745xg_for_bl.ld
+++ b/src/main/target/link/stm32_flash_f745xg_for_bl.ld
@@ -38,12 +38,14 @@ MEMORY
FLASH1 (rx) : ORIGIN = 0x08018000, LENGTH = 928K
TCM (rwx) : ORIGIN = 0x20000000, LENGTH = 64K
- RAM (rwx) : ORIGIN = 0x20010000, LENGTH = 256K
+ RAM (rwx) : ORIGIN = 0x20010000, LENGTH = 240K
+ SRAM2 (rwx) : ORIGIN = 0x2004C000, LENGTH = 16K
MEMORY_B1 (rx) : ORIGIN = 0x60000000, LENGTH = 0K
}
/* note CCM could be used for stack */
REGION_ALIAS("STACKRAM", TCM)
REGION_ALIAS("FASTRAM", TCM)
+REGION_ALIAS("DMARAM", SRAM2)
__firmware_start = ORIGIN(FLASH);
diff --git a/src/main/target/link/stm32_flash_f765xg.ld b/src/main/target/link/stm32_flash_f765xg.ld
index ea548ed4ff3..2dc4e906677 100644
--- a/src/main/target/link/stm32_flash_f765xg.ld
+++ b/src/main/target/link/stm32_flash_f765xg.ld
@@ -48,5 +48,6 @@ MEMORY
REGION_ALIAS("STACKRAM", DTCM_RAM)
REGION_ALIAS("FASTRAM", DTCM_RAM)
REGION_ALIAS("RAM", SRAM1)
+REGION_ALIAS("DMARAM", SRAM2)
INCLUDE "stm32_flash_f7_split.ld"
diff --git a/src/main/target/link/stm32_flash_f765xg_for_bl.ld b/src/main/target/link/stm32_flash_f765xg_for_bl.ld
index b50247b015d..9405858f04d 100644
--- a/src/main/target/link/stm32_flash_f765xg_for_bl.ld
+++ b/src/main/target/link/stm32_flash_f765xg_for_bl.ld
@@ -48,6 +48,7 @@ MEMORY
REGION_ALIAS("STACKRAM", DTCM_RAM)
REGION_ALIAS("FASTRAM", DTCM_RAM)
REGION_ALIAS("RAM", SRAM1)
+REGION_ALIAS("DMARAM", SRAM2)
__firmware_start = ORIGIN(FLASH);
diff --git a/src/main/target/link/stm32_flash_f765xi.ld b/src/main/target/link/stm32_flash_f765xi.ld
index 130169290ea..c1165c25dbc 100644
--- a/src/main/target/link/stm32_flash_f765xi.ld
+++ b/src/main/target/link/stm32_flash_f765xi.ld
@@ -51,5 +51,6 @@ REGION_ALIAS("FLASH1", AXIM_FLASH1)
REGION_ALIAS("STACKRAM", DTCM_RAM)
REGION_ALIAS("FASTRAM", DTCM_RAM)
REGION_ALIAS("RAM", SRAM1)
+REGION_ALIAS("DMARAM", SRAM2)
INCLUDE "stm32_flash_f7_split.ld"
diff --git a/src/main/target/link/stm32_flash_f765xi_for_bl.ld b/src/main/target/link/stm32_flash_f765xi_for_bl.ld
index 14f2a4bedff..eb9c3a820cd 100644
--- a/src/main/target/link/stm32_flash_f765xi_for_bl.ld
+++ b/src/main/target/link/stm32_flash_f765xi_for_bl.ld
@@ -51,6 +51,7 @@ REGION_ALIAS("FLASH1", AXIM_FLASH1)
REGION_ALIAS("STACKRAM", DTCM_RAM)
REGION_ALIAS("FASTRAM", DTCM_RAM)
REGION_ALIAS("RAM", SRAM1)
+REGION_ALIAS("DMARAM", SRAM2)
__firmware_start = ORIGIN(AXIM_FLASH);
diff --git a/src/main/target/link/stm32_flash_f7_split.ld b/src/main/target/link/stm32_flash_f7_split.ld
index 4738eb7af5e..bbd5cf5f99c 100644
--- a/src/main/target/link/stm32_flash_f7_split.ld
+++ b/src/main/target/link/stm32_flash_f7_split.ld
@@ -140,6 +140,17 @@ SECTIONS
__bss_end__ = _ebss;
} >RAM
+ /* DMA buffers, in a region the MPU keeps out of the D-cache (memprot_stm32f7xx.c) */
+ .DMA_RAM (NOLOAD) :
+ {
+ . = ALIGN(32);
+ PROVIDE(dmaram_start = .);
+ *(.DMA_RAM)
+ *(.DMA_RAM*)
+ . = ALIGN(32);
+ PROVIDE(dmaram_end = .);
+ } >DMARAM
+
.fastram_bss (NOLOAD) :
{
__fastram_bss_start__ = .;
diff --git a/src/main/target/system_stm32f7xx.c b/src/main/target/system_stm32f7xx.c
index ea974199681..c1de7010047 100644
--- a/src/main/target/system_stm32f7xx.c
+++ b/src/main/target/system_stm32f7xx.c
@@ -66,6 +66,7 @@
#include
#include "stm32f7xx.h"
#include "drivers/system.h"
+#include "drivers/memprot.h"
#if !defined (HSE_VALUE)
#define HSE_VALUE ((uint32_t)8000000) /*!< Default value of the External oscillator in Hz */
@@ -296,11 +297,18 @@ void SystemInit(void)
SCB->VTOR = (uint32_t) &isr_vector_table_base;
#endif
+#ifndef BOOTLOADER
+ // DMA buffers sit in a region the MPU keeps out of the D-cache
+ memProtConfigure(mpuRegions, mpuRegionCount);
+#endif
+
/* Enable I-Cache */
SCB_EnableICache();
+#ifndef BOOTLOADER
/* Enable D-Cache */
- //SCB_EnableDCache();
+ SCB_EnableDCache();
+#endif
/* Configure the system clock to 216 MHz */
SystemClock_Config();
From 3cba9b295a3295e6b915446fada38460f6220910 Mon Sep 17 00:00:00 2001
From: MrScothh <167884257+MrScothh@users.noreply.github.com>
Date: Wed, 7 Oct 2026 12:35:48 +0200
Subject: [PATCH 2/3] sdio: report a write busy only once its transfer starts
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.
---
src/main/drivers/sdcard/sdcard_sdio.c | 2 +-
src/main/drivers/sdcard/sdmmc_sdio_hal.c | 4 +++-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/main/drivers/sdcard/sdcard_sdio.c b/src/main/drivers/sdcard/sdcard_sdio.c
index f76377550a7..78838c976cc 100644
--- a/src/main/drivers/sdcard/sdcard_sdio.c
+++ b/src/main/drivers/sdcard/sdcard_sdio.c
@@ -54,7 +54,7 @@
#if defined(USE_SDCARD_SDIO_CACHE)
// Use this to speed up writing to SDCARD... asyncfatfs has limited support for multiblock write
#define FATFS_BLOCK_CACHE_SIZE 16
-uint8_t writeCache[512 * FATFS_BLOCK_CACHE_SIZE] __attribute__ ((aligned (4)));
+uint8_t writeCache[512 * FATFS_BLOCK_CACHE_SIZE] __attribute__ ((aligned (32))); // SDIO DMA with the D-cache
uint32_t cacheCount = 0;
void cache_write(uint8_t *buffer)
diff --git a/src/main/drivers/sdcard/sdmmc_sdio_hal.c b/src/main/drivers/sdcard/sdmmc_sdio_hal.c
index 7b4d232555e..f9ec8c88bee 100644
--- a/src/main/drivers/sdcard/sdmmc_sdio_hal.c
+++ b/src/main/drivers/sdcard/sdmmc_sdio_hal.c
@@ -600,7 +600,6 @@ SD_Error_t SD_CheckRead(void) {
SD_Error_t SD_WriteBlocks_DMA(uint64_t WriteAddress, uint32_t *buffer, uint32_t BlockSize, uint32_t NumberOfBlocks)
{
SD_Error_t ErrorState = SD_OK;
- SD_Handle.TXCplt = 1;
if (BlockSize != 512) {
return SD_ERROR; // unsupported.
@@ -621,7 +620,10 @@ SD_Error_t SD_WriteBlocks_DMA(uint64_t WriteAddress, uint32_t *buffer, uint32_t
}
}
#endif
+ // Busy only once a transfer really starts: a rejected write must not leave the card reported busy
+ SD_Handle.TXCplt = 1;
if (HAL_SD_WriteBlocks_DMA(&hsd, (uint8_t *)buffer, WriteAddress, NumberOfBlocks) != HAL_OK) {
+ SD_Handle.TXCplt = 0;
return SD_ERROR;
}
From 4d2c700d9089805422c91d46dc9adf9008ba1380 Mon Sep 17 00:00:00 2001
From: MrScothh <167884257+MrScothh@users.noreply.github.com>
Date: Thu, 8 Oct 2026 18:34:49 +0200
Subject: [PATCH 3/3] build: say where DMA_RAM is uncached on F7
The F7 UART receive rings of #12033 are compiled only then; without the
D-cache the port's own ring does.
---
src/main/build/build_config.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/main/build/build_config.h b/src/main/build/build_config.h
index 2e505450bac..959267fc2d2 100644
--- a/src/main/build/build_config.h
+++ b/src/main/build/build_config.h
@@ -59,6 +59,7 @@
#define SLOW_RAM __attribute__ ((section(".SLOW_RAM")))
#elif defined (STM32F7)
#define DMA_RAM __attribute__ ((section(".DMA_RAM")))
+#define DMA_RAM_UNCACHED // the UART receive rings move here only then
#define SLOW_RAM
#else
#define DMA_RAM