Skip to content

Commit aecced4

Browse files
committed
drivers/sdcard/sdcard.py: Fixes per PR discussion.
Merged crc7.py into sdcard.py, fixed many little things per suggestions. Removed hardwired crcs from cmd, always recompute. Signed-off-by: Marcus Mendenhall <mendenmh@gmail.com>
1 parent 7cdf708 commit aecced4

File tree

4 files changed

+421
-171
lines changed

4 files changed

+421
-171
lines changed
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import micropython
2+
from uctypes import addressof
3+
4+
# ruff: noqa: F821 - @asm_thumb and @viper decorator adds names to function scope
5+
6+
# https://electronics.stackexchange.com/questions/321304/how-to-use-the-data-crc-of-sd-cards-in-spi-mode
7+
# for sd bit mode
8+
import array
9+
10+
_sd_crc16_table = array.array("H", (0 for _ in range(256)))
11+
# /* Generate CRC16 table */
12+
# for (byt = 0U; byt < 256U; byt ++){
13+
# crc = byt << 8;
14+
# for (bit = 0U; bit < 8U; bit ++){
15+
# crc <<= 1;
16+
# if ((crc & 0x10000U) != 0U){ crc ^= 0x1021U; }
17+
# }
18+
# sd_crc16_table[byt] = (crc & 0xFFFFU);
19+
# }
20+
for byt in range(256):
21+
crc = byt << 8
22+
for bit in range(8):
23+
crc = crc << 1
24+
if (crc & 0x10000) != 0:
25+
crc ^= 0x1021
26+
_sd_crc16_table[byt] = crc & 0xFFFF
27+
28+
29+
# /* Running CRC16 calculation for a byte. */
30+
# static unsigned int sd_crc16_byte(unsigned int crcval, unsigned int byte)
31+
# {
32+
# return (sd_crc16_table[(byte ^ (crcval >> 8)) & 0xFFU] ^ (crcval << 8)) & 0xFFFFU;
33+
# }
34+
35+
36+
@micropython.viper
37+
def crc16_viper(crc: int, data) -> int:
38+
dp = ptr8(addressof(data))
39+
tp = ptr16(addressof(_sd_crc16_table))
40+
nn = int(len(data))
41+
idx = 0
42+
while idx < nn:
43+
crc = ((crc << 8) & 0xFFFF) ^ tp[((crc >> 8) ^ dp[idx]) & 0xFF]
44+
idx += 1
45+
return crc
46+
47+
48+
try: # if we have asm_thumb, this goes faster
49+
50+
@micropython.asm_thumb
51+
def _crc_loop_16(r0, r1, r2, r3) -> int:
52+
# r0 is data address
53+
# r1 is table address
54+
# r2 is CRC
55+
# r3 is count
56+
mov(r4, 0)
57+
mvn(r4, r4) # all ones now
58+
mov(r7, 16)
59+
lsr(r4, r7) # R4 = half-word of ones
60+
mov(r5, 0xFF) # used for byte masking
61+
label(loop)
62+
mov(r6, r2) # copy current CRC
63+
mov(r7, 8)
64+
lsr(r6, r7) # crc >> 8
65+
ldrb(r7, [r0, 0]) # fetch new byte dp[idx]
66+
add(r0, 1) # push to next byte address
67+
eor(r6, r7) # r6 = (crc>>8) ^ dp[idx]
68+
and_(r6, r5) # mask byte ( (crc>>8) ^ dp[idx]) & 0xff
69+
add(r6, r6, r6) # double for table offset
70+
add(r6, r6, r1) # table data address
71+
ldrh(r6, [r6, 0]) # fetch table syndrome
72+
mov(r7, 8)
73+
lsl(r2, r7) # CRC << 8
74+
and_(r2, r4) # (crc << 8) & 0xffff)
75+
eor(r2, r6) # new CRC
76+
sub(r3, 1)
77+
bne(loop)
78+
mov(r0, r2)
79+
80+
@micropython.viper
81+
def crc16(crc: int, data) -> int:
82+
return int(
83+
_crc_loop_16(
84+
int(addressof(data)),
85+
int(addressof(_sd_crc16_table)),
86+
crc,
87+
int(len(data)),
88+
)
89+
)
90+
91+
except:
92+
# wrapper to allow the pure-python implementation to be accessed by the right name if asm_thumb doesn't work
93+
@micropython.viper
94+
def crc16(crc: int, data) -> int:
95+
return int(crc16_viper(crc, data))
96+
97+
98+
# def test_speed():
99+
# data = b"\xaa"*1024
100+
# import time
101+
# crc = 0
102+
# start = time.ticks_us()
103+
# for i in range(1024):
104+
# crc = crc16(crc, data)
105+
# print("asm crc speed = ", f"{crc:08x}", 2**20 / (time.ticks_diff(time.ticks_us(), start) / 1e6), "bytes/s")
106+
#
107+
# crc = 0
108+
# start = time.ticks_us()
109+
# for i in range(1024):
110+
# crc = crc16_viper(crc, data)
111+
# print("py crc speed = ", f"{crc:08x}", 2**20 / (time.ticks_diff(time.ticks_us(), start) / 1e6), "bytes/s")
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
metadata(description="SDCard block device driver.", version="0.1.0")
1+
metadata(description="SDCard block device driver.", version="0.2.0")
22

33
module("sdcard.py", opt=3)
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# SD card setup
2+
3+
from machine import SPI, Pin, freq
4+
5+
freq(120_000_000)
6+
print("freq:", freq())
7+
8+
import os
9+
from sdcard import SDCard
10+
import time
11+
12+
# the bus spi1 on these pins on my test card
13+
14+
# I have cs on GP13 for this one
15+
spi = SPI(1, 24_000_000, sck=Pin(14), mosi=Pin(15), miso=Pin(12))
16+
spi.init() # Ensure right baudrate
17+
18+
from crc16 import crc16
19+
20+
sd = SDCard(spi=spi, cs=Pin(13, Pin.OUT), baudrate=24_000_000, crc16_function=None)
21+
vfs = os.VfsFat(sd)
22+
os.mount(vfs, "/fc")
23+
24+
25+
def sdtest():
26+
print("Filesystem check")
27+
print(os.listdir("/fc"))
28+
29+
line = "abcdefghijklmnopqrstuvwxyz\n"
30+
lines = line * 200 # 5400 chars
31+
short = "1234567890\n"
32+
33+
fn = "/fc/rats.txt"
34+
print()
35+
print("Multiple block read/write")
36+
loops = 1000
37+
t0 = time.ticks_ms()
38+
with open(fn, "w") as f:
39+
n = f.write(lines)
40+
print(n, "bytes written")
41+
n = f.write(short)
42+
print(n, "bytes written")
43+
for i in range(loops):
44+
n = f.write(lines)
45+
46+
nbytes = loops * len(lines) + len(lines) + len(short)
47+
rate = 1000 * nbytes / time.ticks_diff(time.ticks_ms(), t0)
48+
print(nbytes, "bytes written at ", rate / 1e6, "MB/s")
49+
50+
stat = os.stat(fn)
51+
filesize = stat[6]
52+
total = 0
53+
t0 = time.ticks_ms()
54+
55+
readbuf = bytearray(8192)
56+
import uctypes
57+
58+
with open(fn, "rb") as f:
59+
f.readinto(readbuf)
60+
big_readback = readbuf[: len(lines)] # check a big chunk of data
61+
62+
with open(fn, "rb") as f:
63+
while (count := f.readinto(readbuf)) != 0:
64+
total += count
65+
66+
rate = 1000 * total / time.ticks_diff(time.ticks_ms(), t0)
67+
68+
print("final file size", filesize, "expected", nbytes, "read", total, "rate=", rate / 1e6)
69+
70+
fn = "/fc/rats1.txt"
71+
print()
72+
print("Single block read/write")
73+
with open(fn, "w") as f:
74+
n = f.write(short) # one block
75+
print(n, "bytes written")
76+
77+
with open(fn, "r") as f:
78+
result2 = f.read()
79+
print(len(result2), "bytes read")
80+
81+
print()
82+
print("Verifying data read back")
83+
success = True
84+
if result2 == short:
85+
print("Small file Pass")
86+
else:
87+
print("Small file Fail")
88+
success = False
89+
if big_readback == lines:
90+
print("Big read Pass")
91+
else:
92+
print("Big readFail")
93+
success = False
94+
95+
print()
96+
print("Tests", "passed" if success else "failed")
97+
98+
99+
sdtest()
100+
101+
os.umount("/fc")

0 commit comments

Comments
 (0)