stm32/sdcard: Add support for UHS-I SD cards. - #19623
Open
kwagyeman wants to merge 2 commits into
Open
Conversation
Add support for running an SD card in UHS-I mode (1.8V signalling, up to SDR104) on boards whose hardware can switch the SD bus I/O rail between 3.3V and 1.8V. A board enables this by defining: - MICROPY_HW_SDCARD_VSELECT_PIN: output pin selecting the SD bus I/O rail voltage, low for 3.3V and high for 1.8V. - MICROPY_HW_SDCARD_RESET_PIN: active-low output pin controlling the SD card supply. It is sampled at startup to detect whether the hardware supports UHS-I (it reads high if so), allowing one firmware to support both UHS-I capable and older hardware revisions. - MICROPY_HW_SDCARD_UHS_SWITCH_PATTERN/MICROPY_HW_SDCARD_UHS_CLK_DIV: optional speed mode (default SDR50) and final clock divider. - MICROPY_HW_SDCARD_VDDIO: on STM32N6, which VDDIO power domain the SD bus pins are in (2-5), defaulting to the domain of the default pins of the SDMMC in use (4 for SDMMC1, 5 for SDMMC2). Card identification and the CMD11 voltage switch are handled by the HAL (USE_SD_TRANSCEIVER); HAL_SD_DriveTransceiver_1_8V_Callback drives the voltage select pin and, on STM32N6, also switches the configured VDDIO domain's pad voltage range in the PWR peripheral. The pad range is read back after the switch: on the N6 the VDDIOxVRSEL write is silently ignored unless the matching HSLV_VDDIOx OTP fuse is set, in which case the pads stay in 3.3V mode and fail at UHS-I rates even though the (slower) initialisation succeeds, so an unfused device deliberately fails the attempt and falls back. A card that does not support 1.8V signalling continues at 3.3V and is switched to high speed mode via CMD6, and any UHS-I failure falls back to the previous plain 3.3V behaviour after power cycling the card via the reset pin. The CMD6 switch itself is implemented here with the command-transfer (CMDTRANS) mechanism instead of HAL_SD_ConfigSpeedBusOperation: the HAL arms the data path via DCTRL.DTEN without linking it to the command, and additionally, after the 1.8V switch the SDMMC data path status flags read stale until the FIFO is accessed, so the HAL's flag-driven receive loop deadlocks (against its 0xFFFFFFFF ms software timeout). The switch status is instead drained from the FIFO after the transfer time has elapsed, and the status flags are only consulted afterwards, once they have refreshed. Boards without the new pins compile to byte-identical binaries (verified for OPENMV2/OPENMV3/OPENMV4/OPENMV4P), and SD operation was regression tested on STM32F427, STM32F765 and STM32H743 hardware. Tested on an OpenMV N6 (STM32N657) against the same board, card and 16MB file workload on unpatched firmware: file reads go from 21.8MB/s to 70.8MB/s and file writes from 19.7MB/s to 56.1MB/s (raw block reads from 22.4MB/s to 83.3MB/s) at SDR104/200MHz, with SDR50/100MHz reading 43.5MB/s, data verified against 3.3V-mode reads, and stable across repeated power cycle/init loops; the fuse-less fallback path was verified on a second N6 with the OTP fuse unprogrammed. Signed-off-by: Kwabena W. Agyeman <kwagyeman@live.com>
Enable UHS-I on the OpenMV N6: SD_RESET (PC7) is sampled at startup to detect UHS-I capable hardware, and SD_VSELECT (PG6) switches the SD bus rail (the VDDIO4 domain) to 1.8V. The board runs SDR104 at 200MHz (the SDMMC1 kernel clock is HCLK), raising raw SD reads from 22.4MB/s to 83.3MB/s. The HSLV_VDDIO4 OTP fuse (RM0486 Table 18, OTP word 124 bit 14) is programmed alongside the existing VDDIO2/3 fuses: without it the VDDIO4VRSEL 1.8V pad range selection is ignored by the hardware, which was verified on an unfused board (the SD driver detects this and falls back to 3.3V operation). VDDIO4 still boots in the 3.3V range; the SD driver switches it at runtime only after the card acknowledges the 1.8V voltage switch. Signed-off-by: Kwabena W. Agyeman <kwagyeman@live.com>
Contributor
Author
|
@dpgeorge - This script can be used to test the performance on a board. You need a microSD card with UHS-I support (pretty much any SDXC card). # SD card benchmark: 16MB file write/read + raw block reads.
# Run on any board with pyb.SDCard (results above: OPENMV_N6).
import os, time, vfs, pyb
sd = pyb.SDCard()
sd.power(1)
print("card:", sd.info())
fs = vfs.VfsFat(sd)
vfs.mount(fs, "/sd")
CHUNK = 512 * 1024
N = 32 # 16MB total
buf = bytearray(CHUNK)
for i in range(CHUNK):
buf[i] = i & 0xFF
# File write.
t = time.ticks_ms()
with open("/sd/sdbench.bin", "wb") as f:
for _ in range(N):
f.write(buf)
dt = time.ticks_diff(time.ticks_ms(), t)
print("file write: %.1f MB/s" % (N * CHUNK / 1024 / 1024 * 1000 / dt))
# File read.
t = time.ticks_ms()
with open("/sd/sdbench.bin", "rb") as f:
while f.readinto(buf):
pass
dt = time.ticks_diff(time.ticks_ms(), t)
print("file read: %.1f MB/s" % (N * CHUNK / 1024 / 1024 * 1000 / dt))
os.remove("/sd/sdbench.bin")
vfs.umount("/sd")
# Raw block reads (8MB from the start of the card).
t = time.ticks_ms()
for i in range(16):
sd.readblocks(i * 1024, buf)
dt = time.ticks_diff(time.ticks_ms(), t)
print("raw read: %.1f MB/s" % (16 * CHUNK / 1024 / 1024 * 1000 / dt)) |
|
Code size report: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This adds UHS-I support (1.8V signalling, SDR50/SDR104) to the stm32 port's SD card driver, for boards whose hardware can switch the SD bus I/O rail between 3.3V and 1.8V, and enables it on the OPENMV_N6 board at SDR104/200MHz.
On the OpenMV N6, same board/card/workload (16MB file, 512KB chunks), unpatched vs patched firmware:
SDR50 at 100MHz (43.5 MB/s raw reads) is available as a conservative alternative via one board macro.
A board opts in by defining
MICROPY_HW_SDCARD_VSELECT_PIN(drives the SD rail voltage select) andMICROPY_HW_SDCARD_RESET_PIN(active-low SD card power). The reset pin doubles as a hardware-revision detect: it is sampled at startup and reads high only on UHS-I capable hardware, so one firmware supports old and new board revisions. The voltage switch itself (ACMD41 S18R / CMD11) is the HAL'sUSE_SD_TRANSCEIVERflow; the transceiver callback drives the select pin and, on STM32N6, moves the configured VDDIO domain (MICROPY_HW_SDCARD_VDDIO, defaulting from the SDMMC instance) to the 1.8V pad range. Cards that don't support 1.8V get a proper CMD6 high-speed switch at 3.3V; any UHS failure power-cycles the card via the reset pin and falls back to the existing 3.3V behaviour.Two STM32N6 hardware findings shaped the implementation:
The SDMMC data-path status flags read stale after the 1.8V switch until the FIFO is accessed. Command-path flags are fine and IDMA/IRQ transfers are unaffected, but any CPU-polled receive loop that gates FIFO reads on RXFIFOHF/DATAEND deadlocks — which is exactly what the HAL's own
SD_UltraHighSpeed()/SD_SwitchSpeed()do (against a 0xFFFFFFFF ms software timeout), soHAL_SD_ConfigSpeedBusOperation()hangs forever on this part. The driver therefore performs the CMD6 switch itself using the CMDTRANS mechanism, drains the 64-byte status from the FIFO after the transfer time has elapsed, and only then consults the (by then refreshed) status flags. This was proven at register level: the FIFO contained the complete, correct switch status while STA/DCOUNT still claimed no data had arrived.VDDIOxVRSELwrites are silently ignored unless the matchingHSLV_VDDIOxOTP fuse is programmed. Without the fuse the pads stay in 3.3V mode: the (slow) initialisation still passes, then every transfer at UHS rates fails. Verified by A/B on two boards (fused vs unfused). The driver reads the range back after switching and treats a non-stick as a failed attempt, so unfused boards cleanly fall back to 3.3V — and once a bootloader update programs the fuse, the same firmware starts running SDR104 automatically. The OPENMV_N6 board commit programs the fuse (RM0486 Table 18, OTP word 124 bit 14) alongside the existing VDDIO2/3 fuses.Testing
USE_SD_TRANSCEIVER=0default path on N6).ClockPowerSaveenabled and the exact current init sequence; UHS-I init disables clock power saving (as the ST BSPs do) because the voltage-switch and CMD6 sequences stall with it enabled.Trade-offs and Alternatives
HAL_SD_ConfigSpeedBusOperation()instead of a driver-side CMD6: attempted first, but it hard-hangs on the N6 (finding 1 above), so the CMD6 transaction is implemented in sdcard.c with the same command-transfer mechanism regular block reads use.