Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 31 additions & 11 deletions ports/mimxrt/sdcard.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

#include "sdcard.h"
#include "ticks.h"
#include "fsl_cache.h"
#include "fsl_iomuxc.h"
#include "pin.h"

Expand Down Expand Up @@ -294,33 +295,52 @@ static void sdcard_error_recovery(USDHC_Type *base) {
static status_t sdcard_transfer_blocking(USDHC_Type *base, usdhc_handle_t *handle, usdhc_transfer_t *transfer, uint32_t timeout_ms) {
status_t status;

usdhc_adma_config_t dma_config;

(void)memset(&dma_config, 0, sizeof(usdhc_adma_config_t));
dma_config.dmaMode = kUSDHC_DmaModeAdma2;

#if !(defined(FSL_FEATURE_USDHC_HAS_NO_RW_BURST_LEN) && FSL_FEATURE_USDHC_HAS_NO_RW_BURST_LEN)
dma_config.burstLen = kUSDHC_EnBurstLenForINCR;
usdhc_adma_config_t dma_config = {
.dmaMode = kUSDHC_DmaModeAdma2,
#if !(defined(FSL_FEATURE_USDHC_HAS_NO_RW_BURST_LEN) && FSL_FEATURE_USDHC_HAS_NO_RW_BURST_LEN)
.burstLen = kUSDHC_EnBurstLenForINCR,
#endif
.admaTable = sdcard_adma_descriptor_table,
.admaTableWords = DMA_DESCRIPTOR_BUFFER_SIZE,
};
usdhc_adma_config_t *p_dma_config = &dma_config;

#if __DCACHE_PRESENT
size_t byte_len = transfer->data->blockCount * transfer->data->blockSize;
if ((uintptr_t)transfer->data->rxData % FSL_FEATURE_L1DCACHE_LINESIZE_BYTE != 0 ||
byte_len % FSL_FEATURE_L1DCACHE_LINESIZE_BYTE != 0) {
// A DMA RX transfer that isn't cache line aligned can be corrupted if the CPU writes the same cache line during
// the read, so make a polling transfer instead.
//
// (Note that the USDHC driver will internally disable DMA if the address isn't 4 byte aligned, so this check only
// changes behaviour for addresses which are word aligned but not cache line aligned!)
p_dma_config = NULL;
}
#endif

dma_config.admaTable = sdcard_adma_descriptor_table;
dma_config.admaTableWords = DMA_DESCRIPTOR_BUFFER_SIZE;

// Wait while the card is busy before a transfer
status = kStatus_Timeout;
for (int i = 0; i < timeout_ms * 100; i++) {
// Wait until Data0 is low any more. Low indicates "Busy".
if (((transfer->data->txData == NULL) && (transfer->data->rxData == NULL)) ||
(USDHC_GetPresentStatusFlags(base) & (uint32_t)kUSDHC_Data0LineLevelFlag) != 0) {
// Not busy anymore or no TX-Data
status = USDHC_TransferBlocking(base, &dma_config, transfer);
status = USDHC_TransferBlocking(base, p_dma_config, transfer);
if (status != kStatus_Success) {
sdcard_error_recovery(base);
}
break;
}
ticks_delay_us64(10);
}

#if __DCACHE_PRESENT
if (p_dma_config && transfer->data->rxData) {
// Invalidate any cache lines that were filled while the transfer was in progress
L1CACHE_InvalidateDCacheByRange((uintptr_t)transfer->data->rxData, byte_len);
}
#endif

return status;

}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,44 @@
# Test DMA read operations when the buffer alignment in RAM varies.
#
# Runs on both pyb.SDCard and machine.SDCard with default arguments,
# although some port-specific info is needed (see below)
#
# Test requirements:
# A mostly empty FAT formatted SDCard installed in SD socket
# A mostly empty FAT formatted SDCard installed in SD socket (a card with other
# files may be much slower to start the test.)
import errno
import os
import pyb
import machine
import micropython
import sys
import vfs
import unittest

from micropython import const

# Use pyb classes on stm32, machine class otherwise
try:
from pyb import SDCard, Timer
except ImportError:
try:
from machine import SDCard, Timer
except ImportError:
print("SKIP")
raise SystemExit

READBLOCKS_SUCCESS = (0, True) # some drivers return True for success, some return 0...

_BLOCK_SZ = const(512)
_OFFS_WIDTH = const(64) # Should be at least the cache line size plus the GC block size
_TEST_BUF_SZ = const(_BLOCK_SZ + _OFFS_WIDTH)

# More repeats = longer test run, more chance of triggering a cache coherence issue
_REPEATS = const(8)

MOUNT_POINT = "/sd"
FILE_PATH = "/sd/stm32_align.blk"

# Skip the whole test if there isn't a mountable SDCard
try:
sd = pyb.SDCard()
sd = SDCard()
fs = vfs.VfsFat(sd)
vfs.mount(sd, MOUNT_POINT)
vfs.umount(MOUNT_POINT)
del sd
Expand All @@ -33,6 +47,26 @@
raise SystemExit


# Set some port-specific parameters for test repeats & interrupt frequency
# (ports which can issue interrupts at a high frequency don't need to run as many
# repeats of the test in order to trigger a failure due to cache bugs.)
if "pyboard" in sys.platform:

def make_timer(timer_cb):
# Pyboard SF6 can do at least this frequency and still run the test,
# possibly as high as 40kHz depending on the callback details.
return Timer(1, freq=35_000, callback=timer_cb, hard=True)

_REPEATS = 8
elif "mimxrt" in sys.platform:

def make_timer(timer_cb):
return Timer(-1, period=1, callback=timer_cb, hard=True)

# virtual timer limited to 1kHz so run more iterations (slow test!)
_REPEATS = 64


def verify_contents(buf, silent=False):
# Verify that each byte in 'buf' has the value of its index in the buffer
bad_pos = []
Expand All @@ -57,7 +91,7 @@ def verify_contents(buf, silent=False):
class TestSDAlign(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.sd = pyb.SDCard()
cls.sd = SDCard()
vfs.mount(cls.sd, MOUNT_POINT)
buf = bytearray(_BLOCK_SZ)
try:
Expand All @@ -77,9 +111,11 @@ def setUpClass(cls):

# Now look for the sector which holds the temporary file
# (assume is in the first 20MB of the SD Card)
vfs.umount(MOUNT_POINT)
for b in range(1, 40960):
if not cls.sd.readblocks(b, buf):
raise RuntimeError("Failed to call readblocks on SDCard. block:", b)
res = cls.sd.readblocks(b, buf)
if res not in READBLOCKS_SUCCESS:
raise RuntimeError("Failed to call readblocks on SDCard:", res, "block:", b)
if verify_contents(buf, True):
print("Temporary file contents found in block {}".format(b))
cls.block = b
Expand All @@ -96,7 +132,10 @@ def tearDownClass(cls):
print("Deleted temp file")
except OSError:
pass
vfs.umount(MOUNT_POINT)
try:
vfs.umount(MOUNT_POINT)
except OSError:
pass
del cls.sd

def setUp(self):
Expand All @@ -110,8 +149,9 @@ def _test_reads_inner(self, buf, repeats=_REPEATS):
slice = memoryview(buf)[offs : offs + _BLOCK_SZ]
assert len(slice) == _BLOCK_SZ
for r in range(repeats):
self.assertTrue(
self.assertIn(
self.sd.readblocks(self.block, slice),
READBLOCKS_SUCCESS,
"Read failed for block {} offs {} repeat {}/{}".format(
self.block, offs, r, repeats
),
Expand Down Expand Up @@ -148,7 +188,7 @@ def timer_cb(_):
self.val = buf[self.scan % _TEST_BUF_SZ]
self.scan += 1

t = pyb.Timer(1, freq=30_000, callback=timer_cb, hard=True)
t = make_timer(timer_cb)
self._test_reads_inner(buf)
finally:
if t:
Expand Down Expand Up @@ -176,8 +216,7 @@ def timer_cb(t):
if offs < _TEST_BUF_SZ - 3:
buf[offs] = 0x56

# pybd SF2 at default CPU frequency can manage >30kHz <40kHz
t = pyb.Timer(1, freq=35_000, callback=timer_cb, hard=True)
t = make_timer(timer_cb)
self._test_reads_inner(buf)
finally:
if t:
Expand Down
Loading