From c499f5f1e24b7637488f3d12eeace3ba7f106c0e Mon Sep 17 00:00:00 2001 From: Ray Morris Date: Tue, 23 Jun 2026 12:20:32 -0500 Subject: [PATCH 1/2] fix(sdcard): remove blocking delay from SDIO retry path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove blocking `delay(1)` calls from SDIO SD-card driver retry logic in both read and write paths. These delays stalled the flight-control loop on transient DMA/bus rejections, causing loop overruns and dropped gyro samples during active blackbox logging. The retries are naturally paced by the asyncfatfs/blackbox polling loop (one iteration per PID loop), which eliminates the need for an explicit delay. By returning SDCARD_OPERATION_BUSY / false immediately on rejection, the cooperative scheduler continues running other tasks instead of spinning the CPU for 1 ms per retry. Impact: - Eliminates up to ~3 ms of blocking CPU stall per transient rejection (3 retries × 1 ms delay at 2 kHz loop rate) - Retries still occur up to SDCARD_MAX_OPERATION_RETRIES (3) times before card reset, preserving the retry-before-reset intent - Retry spacing now depends on loop rate: - 1 kHz loop: 1 ms spacing (same as before) - 2-4 kHz loop: 250-500 µs spacing (tighter, safer for fast loops) Backend audit: sdcard_spi.c, sdmmc_sdio_f4xx.c, and sdmmc_sdio_hal.c all have no per-block hot-path blocking delays (only init/reset delays). --- src/main/drivers/sdcard/sdcard_sdio.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/drivers/sdcard/sdcard_sdio.c b/src/main/drivers/sdcard/sdcard_sdio.c index d7cd8ff3c54..bb8289f9b02 100644 --- a/src/main/drivers/sdcard/sdcard_sdio.c +++ b/src/main/drivers/sdcard/sdcard_sdio.c @@ -461,12 +461,12 @@ static sdcardOperationStatus_e sdcardSdio_writeBlock(uint32_t blockIndex, uint8_ if (SD_WriteBlocks_DMA(blockIndex, (uint32_t*) buffer, 512, block_count) != SD_OK) { /* Our write was rejected! Try a few times before giving up. - * This handles transient DMA/bus issues without full card reset. + * This handles transient DMA/bus issues without a full card reset. + * Returning busy without blocking: the blackbox/asyncfatfs flush re-issues + * this operation on the next PID loop, which paces the retries for us. */ if (sdcard.operationRetries < SDCARD_MAX_OPERATION_RETRIES) { sdcard.operationRetries++; - // Brief delay before retry - delay(1); return SDCARD_OPERATION_BUSY; } @@ -562,11 +562,11 @@ static bool sdcardSdio_readBlock(uint32_t blockIndex, uint8_t *buffer, sdcard_op } else { /* Read was rejected! Try a few times before giving up. * This handles transient DMA/bus issues without full card reset. + * Returning busy without blocking: the asyncfatfs read re-issues + * this operation on the next PID loop, which paces the retries for us. */ if (sdcard.operationRetries < SDCARD_MAX_OPERATION_RETRIES) { sdcard.operationRetries++; - // Brief delay before retry - delay(1); return false; } From 2fda3e4d66a1225321e225f164ac3f83ff4f238f Mon Sep 17 00:00:00 2001 From: Ray Morris Date: Tue, 23 Jun 2026 12:41:58 -0500 Subject: [PATCH 2/2] fix(sdcard): fix write-path state management in SDIO driver Move state assignment after confirming DMA operation starts successfully. Previously, sdcard.state was set to SDCARD_STATE_SENDING_WRITE before the SD_WriteBlocks_DMA() call. On DMA rejection and retry, the state remained SENDING_WRITE, causing the next poll cycle to call SD_CheckWrite() on a transfer that never actually started, leading to stale status misinterpretation. This fix mirrors the read-path behavior (sdcardSdio_readBlock) where state is only set after confirming the DMA operation succeeded. This ensures: - Retry mechanism works correctly (state stays READY for next attempt) - Poll cycle sees accurate state - No risk of invoking completion callbacks for failed operations This bug becomes critical after removing blocking delays from retry paths, as retries now happen immediately on the next loop iteration instead of after 1ms, exposing the state inconsistency more frequently. Related: commit 146ff5616c (introduced the retry logic with this bug) --- src/main/drivers/sdcard/sdcard_sdio.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/drivers/sdcard/sdcard_sdio.c b/src/main/drivers/sdcard/sdcard_sdio.c index bb8289f9b02..f76377550a7 100644 --- a/src/main/drivers/sdcard/sdcard_sdio.c +++ b/src/main/drivers/sdcard/sdcard_sdio.c @@ -457,7 +457,6 @@ static sdcardOperationStatus_e sdcardSdio_writeBlock(uint32_t blockIndex, uint8_ sdcard.pendingOperation.callback = callback; sdcard.pendingOperation.callbackData = callbackData; sdcard.pendingOperation.chunkIndex = 1; // (for non-DMA transfers) we've sent chunk #0 already - sdcard.state = SDCARD_STATE_SENDING_WRITE; if (SD_WriteBlocks_DMA(blockIndex, (uint32_t*) buffer, 512, block_count) != SD_OK) { /* Our write was rejected! Try a few times before giving up. @@ -481,6 +480,9 @@ static sdcardOperationStatus_e sdcardSdio_writeBlock(uint32_t blockIndex, uint8_ return SDCARD_OPERATION_FAILURE; } + // DMA started successfully - only set state after confirming operation will proceed + sdcard.state = SDCARD_STATE_SENDING_WRITE; + // Success - reset retry counter sdcard.operationRetries = 0; return SDCARD_OPERATION_IN_PROGRESS;