Skip to content

stm32h7/sdmmc: Add configurable IDMA bounce buffering. - #20381

Open
msli-dev wants to merge 2 commits into
apache:masterfrom
msli-dev:stm32h7-sdmmc-idma-bounce
Open

msli-dev wants to merge 2 commits into
apache:masterfrom
msli-dev:stm32h7-sdmmc-idma-bounce

Conversation

@msli-dev

@msli-dev msli-dev commented Sep 28, 2026 •

Copy link
Copy Markdown

Summary

STM32H743 SDMMC1 cannot directly DMA to client buffers in D2/D3 SRAM or DTCM. Add optional, per-instance aligned IDMA bounce buffers so these buffers, and unaligned buffers, can use multi-block transfers. Writes are copied before IDMA; successful bounced reads are copied in the waiting thread rather than copying the full request in interrupt context.

Add an optional sdio_dev_s::maxrequest() callback, returning bytes (zero/unset means no additional host limit). MMC/SD combines the host capacity with CONFIG_MMCSD_MULTIBLOCK_LIMIT and splits normal block reads/writes before DMA setup. With eight 512-byte bounce blocks, a 32 KiB request becomes eight 4 KiB host requests.

The series is organized into two commits:

  1. 44f3bccddf — common SDIO/MMCSD changes: optional host request limit, block splitting, and generic multi-block error cleanup. All changes to drivers/mmcsd/mmcsd_sdio.c are in this first commit, together with include/nuttx/sdio.h and drivers/mmcsd/Kconfig.
  2. 294c46fc37 — STM32H7 driver and Kconfig changes: per-instance IDMA bounce buffers, cache handling, runtime checks and transfer cleanup. This commit depends on the common interface introduced by the first commit.

The upstream ioctl formatting cleanup is retained, so there is no separate formatting-only commit. The rebase also preserves upstream checks of non-DMA RECVSETUP/SENDSETUP return values.

IDMAEN is writable only with inactive DPSM. If abort finds an active data state machine, the driver resets that host peripheral and restores its bus configuration before returning buffer ownership. Card-side recovery still requires STOP_TRANSMISSION or reinitialization. See RM0433, SDMMC_IDMACTRLR. The branch is rebased onto apache/nuttx master at 8df6e2a3f45bf4a6cef791fb3cf9fb36e34cdb30.

Hardware reference: ST AN5200, section 1.1 and Table 2.

Impact

  • New opt-in CONFIG_STM32_SDMMC_IDMA_BOUNCE_BUFFER, with CONFIG_STM32_SDMMC_IDMA_BOUNCE_BLOCKS=8 by default when enabled. Each enabled SDMMC instance has a separate buffer.
  • Default buffer storage is 4 KiB per instance, replacing the existing 512-byte internal buffer (3584 additional buffer bytes, plus structure/alignment overhead). The linker must place it in IDMA-accessible RAM; initialization checks its address and alignment when bounce support is enabled.
  • A positive RAM allow-list tightens IDMA preflight even with bounce support disabled. External-memory addresses outside that list can be rejected; compatibility with affected boards needs review.
  • The host request cap also splits directly accessible, aligned buffers. Large direct-I/O performance therefore needs comparison on hardware.
  • Other hosts with a zero-initialized/unset callback retain their existing request limits. The optional callback is appended to the public SDIO structure, preserving the order of existing fields for positional initializers. Drivers must be rebuilt, and dynamically allocated/out-of-tree instances must initialize the callback to NULL if unsupported.
  • Automatic splitting applies to the normal MMC/SD block read/write entry points. Oversized direct DMA setup requests and raw multi-block commands are rejected with -E2BIG; commands are not implicitly split. A host cap smaller than one sector is rejected before normal block I/O, including single-block-only configurations.
  • Kconfig and interface comments explain the new configuration and capability. No Markdown documents are included in this PR.

Testing

Validation after the review update

I rebuilt the first (common-only) commit with stm32f746-ws:nsh, then rebuilt the complete series with weact-stm32h743:sdcard and the 4 KiB bounce buffer both enabled and disabled. All three builds linked successfully with no compiler warnings using GNU Arm Embedded GCC 10.2.1 and nuttx-apps 00b6e59123363d7fc92d0a08cb43f449d438e4bc. Full-file tools/checkpatch.sh and git diff --check also pass. These are build checks of the rebased series; the hardware results below remain from the previously tested PX4 port.

Build host: Linux x86_64, WSL2 kernel 6.6.114.1-microsoft-standard-WSL2.
Compiler: GNU Arm Embedded Toolchain 10.2.1 20201103 (10-2020-q4-major).
Apps revision: apache/nuttx-apps b66303e26aa537dd74d6abaeeeded81c151a7e35.

Built in a separate worktree. Full builds used make olddefconfig and make -j8.

Target/configuration Result
WeAct H743 SDMMC1, 4 KiB bounce, D-cache write-through Pass
Same, D-cache write-back Pass
Same, D-cache disabled Pass
H743, bounce disabled Pass
H743, IDMA disabled Pass; existing warning about large non-DMA RX overrun risk
H743, MMCSD_MULTIBLOCK_LIMIT=1 Pass
H743, MMCSD_MULTIBLOCK_LIMIT=4 Pass
H743, MMCSD_MULTIBLOCK_LIMIT=16, Debug assertions Pass
stm32f746-ws:nsh, SDMMC1 DMA, no maxrequest callback Pass

Except for the intentional pre-existing no-IDMA warning, these builds produced no warnings. H743 bounce-enabled builds place g_sdmmc1_idmabuffer at 0x24002c20, with size 0x1000, in AXI SRAM. Bounce-disabled builds retain a 0x200 buffer. F7 build linked nuttx and generated nuttx.bin.

Local host-side tests compile the actual function bodies extracted from the edited sources with mocked registers, semaphores, watchdogs and lower-level block transfers, using GCC and UndefinedBehaviorSanitizer:

  • Driver tests pass under write-back, write-through and no-cache compilation: complete address ranges, invalid lengths/overflow, direct/bounce selection, TX/RX copies, guard bytes, DATAEND plus CRC/timeout/overrun/IDMA error, cancellation/reuse, active-DPSM abort, zero-timeout, watchdog-start failure and write-busy timeout.
  • Regression test verifies bounced DATAEND does not invalidate the client buffer; direct RX still invalidates its destination. The register model ignores IDMAEN writes while DPSM is active, exercising the reset fallback before wakeup.
  • MMC/SD read/write tests pass for configuration limits 0/1/4/16, host limits 0/1/511/512/4096/4608/8192 bytes, and requests from 0 through 64 sectors. They check sector/address continuity, NULL callback, intermediate failure propagation and lock release.
  • Read-command tests pass for setup/CMD23/CMD18 failure cleanup, CMD12 after failed reads, stop-command error propagation and oversized raw multi-block rejection.
  • Repository initializer inspection found 35 designated host initializers in 24 source files. The new optional member is zero-initialized there; the F7 build checks a host without a callback.
Driver model: writeback PASS; writethrough PASS; no-cache PASS
MMC/SD splitting: limits 0, 1, 4, 16 PASS
Command failure cleanup: PASS
Full-file tools/checkpatch.sh: All checks pass
git diff --check: pass

Hardware results: PX4 community flight controller

Hardware test update: I tested this change on a flight controller supported by the PX4 community, using an STM32H743 with the SD card connected to SDMMC1.

I ran the following tests using the PX4 port of this PR on the flight controller, with firmware based on PX4 ea495a418e and its NuttX 030417d09b (the console reports NuttX 12.12.0), with this PR's changes through 51557742ec adapted to the older STM32H7_* configuration names. This is hardware evidence from that port, rather than a standalone upstream NuttX firmware run.

My build enables IDMA, a 4096-byte bounce buffer (8 × 512), MMCSD_MULTIBLOCK_LIMIT=0, and D-cache write-back. Its linked bounce buffer is at 0x2400eea0 in AXI SRAM, size 0x1000.

Command Average write, KB/s Average read + verify, KB/s Observed result
sd_bench -v -b 4096 -r 3 -d 3000 955.18 1997.56 2154 application blocks read and verified
sd_bench -U -v -b 4096 -r 3 -d 3000 1173.91 1998.34 2647 application blocks read and verified
sd_bench -U -v -b 8192 -r 3 -d 3000 1259.43 1997.04 1421 application blocks read and verified

For the unaligned 4 KiB test, the printed write/read pointers are 0x300170a1 and 0x300180a9: both are byte-unaligned addresses in D2 SRAM1. Thus the completed file-I/O test exercises D2 caller buffers and reports successful readback verification. The ordinary 4 KiB run does not print an address, so it is not being labeled a verified direct-AXI comparison.

Before these changes, I observed approximately 300 KB/s. My current write averages are approximately 3.2–4.2 times that baseline. I do not have the original command, exact configuration or raw baseline log available, so I am treating this as an indicative comparison rather than a controlled before/after benchmark. I am not drawing a performance conclusion from the unaligned run being faster than the ordinary run.

All three of my runs completed readback verification without reported verification errors. The 8 KiB test also used D2 write/read pointers (0x300170a1 / 0x300190a9) and completed successfully with the configured 4 KiB host cap. File-system I/O does not by itself prove the exact host-level split pattern. Neighboring-buffer guards, D3/DTCM coverage and timeout/cancellation/card-removal recovery remain outstanding. I am submitting this PR for review.

Full console logs: hardware test update.

Review notes

  • Extend the completed D2 unaligned file-I/O verification with explicit direct-AXI comparison, D3/DTCM coverage, neighboring-memory guards and host-level request-size tracing.

  • Validate the active-transfer reset/abort timing, card reinitialization if necessary, and next-request recovery after timeout, cancellation and card removal. Model tests cannot establish those hardware properties.

  • Measure direct versus bounced multi-block performance and the cost of the global request cap.

  • This build matrix does not establish support for every H7 family or board, or on-board SDMMC2 behavior.

  • The commits include Signed-off-by and retain the Assisted-by attribution.

  • Software cleanup and request-boundary fixes implemented and locally tested.

  • PR is ready for review.

  • H743/SDMMC1 PX4-port file-I/O verification completed for 4 KiB, unaligned 4 KiB and unaligned 8 KiB buffers.

  • Remaining hardware recovery, guard-buffer and coverage validation complete.

  • PR is ready to merge.

@github-actions github-actions Bot added Arch: arm Issues related to ARM (32-bit) architecture Area: Drivers Drivers issues Size: L The size of the change in this PR is large labels Sep 28, 2026
@github-actions

github-actions Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

MemBrowse Memory Report

No memory changes detected for:

@github-actions github-actions Bot added Size: XL The size of the change in this PR is very large. Consider breaking down the PR into smaller pieces. and removed Size: L The size of the change in this PR is large labels Sep 28, 2026
@msli-dev

msli-dev commented Sep 30, 2026 •

Copy link
Copy Markdown
Author

Hardware test update: I tested this change on a flight controller supported by the PX4 community, using an STM32H743 with the SD card connected to SDMMC1.

I ran the following tests using the PX4 port of this PR on the flight controller, with firmware based on PX4 ea495a418e and its NuttX 030417d09b (the console reports NuttX 12.12.0), with this PR's changes through 51557742ec adapted to the older STM32H7_* configuration names. This is hardware evidence from that port, rather than a standalone upstream NuttX firmware run.

My build enables IDMA, a 4096-byte bounce buffer (8 × 512), MMCSD_MULTIBLOCK_LIMIT=0, and D-cache write-back. Its linked bounce buffer is at 0x2400eea0 in AXI SRAM, size 0x1000.

Command Average write, KB/s Average read + verify, KB/s Observed result
sd_bench -v -b 4096 -r 3 -d 3000 955.18 1997.56 2154 application blocks read and verified
sd_bench -U -v -b 4096 -r 3 -d 3000 1173.91 1998.34 2647 application blocks read and verified
sd_bench -U -v -b 8192 -r 3 -d 3000 1259.43 1997.04 1421 application blocks read and verified

For the unaligned 4 KiB test, the printed write/read pointers are 0x300170a1 and 0x300180a9: both are byte-unaligned addresses in D2 SRAM1. Thus the completed file-I/O test exercises D2 caller buffers and reports successful readback verification. The ordinary 4 KiB run does not print an address, so it is not being labeled a verified direct-AXI comparison.

Before these changes, I observed approximately 300 KB/s. My current write averages are approximately 3.2–4.2 times that baseline. I do not have the original command, exact configuration or raw baseline log available, so I am treating this as an indicative comparison rather than a controlled before/after benchmark. I am not drawing a performance conclusion from the unaligned run being faster than the ordinary run.

All three of my runs completed readback verification without reported verification errors. The 8 KiB test also used D2 write/read pointers (0x300170a1 / 0x300190a9) and completed successfully with the configured 4 KiB host cap. File-system I/O does not by itself prove the exact host-level split pattern. Neighboring-buffer guards, D3/DTCM coverage and timeout/cancellation/card-removal recovery remain outstanding. I am keeping this PR as Draft while I address those points and complete the required sign-off.

My test console output
nsh> sd_bench -v -b 4096 -r 3 -d 3000
NuttShell (NSH) NuttX-12.12.0
INFO  [sd_bench] Using block size = 4096 bytes, sync=0
INFO  [sd_bench] Testing Sequential Write Speed...
INFO  [sd_bench]   Run  0:   979.31 KB/s, max write time: 50 ms (=  80.00 KB/s), fsync: 5 ms
INFO  [sd_bench]   Run  1:   912.35 KB/s, max write time: 48 ms (=  83.33 KB/s), fsync: 5 ms
INFO  [sd_bench]   Run  2:   973.90 KB/s, max write time: 39 ms (= 102.56 KB/s), fsync: 5 ms
INFO  [sd_bench]   Avg   :   955.18 KB/s
INFO  [sd_bench]   Overall max write time: 50 ms
INFO  [sd_bench] Testing Sequential Read Speed of 2154 blocks
INFO  [sd_bench]   Run  0:  1997.19 KB/s, max read/verify time: 2 ms (=2000.00 KB/s)
INFO  [sd_bench]   Run  1:  1998.41 KB/s, max read/verify time: 2 ms (=2000.00 KB/s)
INFO  [sd_bench]   Avg   :  1997.56 KB/s 2154 blocks read and verified

nsh> sd_bench -U -v -b 4096 -r 3 -d 3000
Block ptr 0x300170a1
INFO  [sd_bench] Using block size = 4096 bytes, sync=0
INFO  [sd_bench] Testing Sequential Write Speed...
INFO  [sd_bench]   Run  0:  1017.74 KB/s, max write time: 48 ms (=  83.33 KB/s), fsync: 5 ms
INFO  [sd_bench]   Run  1:  1214.40 KB/s, max write time: 20 ms (= 200.00 KB/s), fsync: 5 ms
INFO  [sd_bench]   Run  2:  1289.64 KB/s, max write time: 24 ms (= 166.67 KB/s), fsync: 5 ms
INFO  [sd_bench]   Avg   :  1173.91 KB/s
INFO  [sd_bench]   Overall max write time: 48 ms
INFO  [sd_bench] Testing Sequential Read Speed of 2647 blocks
Read Block ptr 0x300180a9
INFO  [sd_bench]   Run  0:  1997.90 KB/s, max read/verify time: 2 ms (=2000.00 KB/s)
INFO  [sd_bench]   Run  1:  1998.92 KB/s, max read/verify time: 2 ms (=2000.00 KB/s)
INFO  [sd_bench]   Avg   :  1998.34 KB/s 2647 blocks read and verified

nsh> sd_bench -U -v -b 8192 -r 3 -d 3000
Block ptr 0x300170a1
INFO  [sd_bench] Using block size = 8192 bytes, sync=0
INFO  [sd_bench] Testing Sequential Write Speed...
INFO  [sd_bench]   Run  0:  1262.24 KB/s, max write time: 23 ms (= 347.83 KB/s), fsync: 5 ms
INFO  [sd_bench]   Run  1:  1223.94 KB/s, max write time: 32 ms (= 250.00 KB/s), fsync: 5 ms
INFO  [sd_bench]   Run  2:  1292.08 KB/s, max write time: 27 ms (= 296.30 KB/s), fsync: 5 ms
INFO  [sd_bench]   Avg   :  1259.43 KB/s
INFO  [sd_bench]   Overall max write time: 32 ms
INFO  [sd_bench] Testing Sequential Read Speed of 1421 blocks
Read Block ptr 0x300190a9
INFO  [sd_bench]   Run  0:  1998.02 KB/s, max read/verify time: 4 ms (=2000.00 KB/s)
INFO  [sd_bench]   Run  1:  1995.94 KB/s, max read/verify time: 4 ms (=2000.00 KB/s)
INFO  [sd_bench]   Avg   :  1997.04 KB/s 1421 blocks read and verified

Whitespace normalized. I included the remaining 8 KiB readback output from my follow-up test log.

@msli-dev
msli-dev force-pushed the stm32h7-sdmmc-idma-bounce branch from 5155774 to 9388370 Compare September 30, 2026 10:49
@msli-dev msli-dev changed the title [WIP] stm32h7/sdmmc: Add configurable IDMA bounce buffering. stm32h7/sdmmc: Add configurable IDMA bounce buffering. Oct 2, 2026
@msli-dev
msli-dev marked this pull request as ready for review October 2, 2026 14:14
xiaoxiang781216
xiaoxiang781216 previously approved these changes Oct 2, 2026
Comment thread drivers/mmcsd/mmcsd_sdio.c Outdated
@xiaoxiang781216

Copy link
Copy Markdown
Contributor

@msli-dev please fix the conflict

Add an optional maxrequest callback at the end of sdio_dev_s. A zero
or unset callback adds no host-specific limit; nonzero values are byte
limits that apply to all request buffers.

Combine the host limit with MMCSD_MULTIBLOCK_LIMIT when splitting
block reads and writes. Reject a host limit smaller than one block and
oversized raw multi-block commands before starting the transfer.

Cancel receive setup after a failed CMD23, attempt CMD12 after failed
open-ended multi-block reads, and propagate stop-command failures.
Keep these generic MMC/SD changes separate from the STM32H7 driver.

Assisted-by: Codex:GPT-6
Signed-off-by: msli-dev <747640013@qq.com>
Use per-instance aligned buffers for memory that SDMMC IDMA cannot
access directly or that does not meet cache alignment requirements.
Copy writes before IDMA and copy successful bounced reads in the
waiting thread. Report the configured capacity through maxrequest.

Keep a positive IDMA RAM allow-list and runtime setup validation.
Invalidate only the actual DMA destination, prioritize errors over
DATAEND, and stop data access before releasing buffer ownership.
Reset an active data path on abort while restoring host bus settings;
clear cancellation state and handle immediate/watchdog-start errors.

This commit contains the STM32H7 driver and its Kconfig changes only.
The preceding commit supplies the common SDIO/MMCSD functionality.

Assisted-by: Codex:GPT-6
Signed-off-by: msli-dev <747640013@qq.com>
@msli-dev
msli-dev force-pushed the stm32h7-sdmmc-idma-bounce branch from 9388370 to 294c46f Compare October 2, 2026 23:04
@github-actions github-actions Bot added Size: L The size of the change in this PR is large and removed Size: XL The size of the change in this PR is very large. Consider breaking down the PR into smaller pieces. labels Oct 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Arch: arm Issues related to ARM (32-bit) architecture Area: Drivers Drivers issues Size: L The size of the change in this PR is large

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants