micropython-lib/micropython/drivers/storage/sdcard/crc16.py

112 wiersze
3.2 KiB
Python

import micropython
from uctypes import addressof
# ruff: noqa: F821 - @asm_thumb and @viper decorator adds names to function scope
# https://electronics.stackexchange.com/questions/321304/how-to-use-the-data-crc-of-sd-cards-in-spi-mode
# for sd bit mode
import array
_sd_crc16_table = array.array("H", (0 for _ in range(256)))
# /* Generate CRC16 table */
# for (byt = 0U; byt < 256U; byt ++){
# crc = byt << 8;
# for (bit = 0U; bit < 8U; bit ++){
# crc <<= 1;
# if ((crc & 0x10000U) != 0U){ crc ^= 0x1021U; }
# }
# sd_crc16_table[byt] = (crc & 0xFFFFU);
# }
for byt in range(256):
crc = byt << 8
for bit in range(8):
crc = crc << 1
if (crc & 0x10000) != 0:
crc ^= 0x1021
_sd_crc16_table[byt] = crc & 0xFFFF
# /* Running CRC16 calculation for a byte. */
# static unsigned int sd_crc16_byte(unsigned int crcval, unsigned int byte)
# {
# return (sd_crc16_table[(byte ^ (crcval >> 8)) & 0xFFU] ^ (crcval << 8)) & 0xFFFFU;
# }
@micropython.viper
def crc16_viper(crc: int, data) -> int:
dp = ptr8(addressof(data))
tp = ptr16(addressof(_sd_crc16_table))
nn = int(len(data))
idx = 0
while idx < nn:
crc = ((crc << 8) & 0xFFFF) ^ tp[((crc >> 8) ^ dp[idx]) & 0xFF]
idx += 1
return crc
try: # if we have asm_thumb, this goes faster
@micropython.asm_thumb
def _crc_loop_16(r0, r1, r2, r3) -> int:
# r0 is data address
# r1 is table address
# r2 is CRC
# r3 is count
mov(r4, 0)
mvn(r4, r4) # all ones now
mov(r7, 16)
lsr(r4, r7) # R4 = half-word of ones
mov(r5, 0xFF) # used for byte masking
label(loop)
mov(r6, r2) # copy current CRC
mov(r7, 8)
lsr(r6, r7) # crc >> 8
ldrb(r7, [r0, 0]) # fetch new byte dp[idx]
add(r0, 1) # push to next byte address
eor(r6, r7) # r6 = (crc>>8) ^ dp[idx]
and_(r6, r5) # mask byte ( (crc>>8) ^ dp[idx]) & 0xff
add(r6, r6, r6) # double for table offset
add(r6, r6, r1) # table data address
ldrh(r6, [r6, 0]) # fetch table syndrome
mov(r7, 8)
lsl(r2, r7) # CRC << 8
and_(r2, r4) # (crc << 8) & 0xffff)
eor(r2, r6) # new CRC
sub(r3, 1)
bne(loop)
mov(r0, r2)
@micropython.viper
def crc16(crc: int, data) -> int:
return int(
_crc_loop_16(
int(addressof(data)),
int(addressof(_sd_crc16_table)),
crc,
int(len(data)),
)
)
except:
# wrapper to allow the pure-python implementation to be accessed by the right name if asm_thumb doesn't work
@micropython.viper
def crc16(crc: int, data) -> int:
return int(crc16_viper(crc, data))
# def test_speed():
# data = b"\xaa"*1024
# import time
# crc = 0
# start = time.ticks_us()
# for i in range(1024):
# crc = crc16(crc, data)
# print("asm crc speed = ", f"{crc:08x}", 2**20 / (time.ticks_diff(time.ticks_us(), start) / 1e6), "bytes/s")
#
# crc = 0
# start = time.ticks_us()
# for i in range(1024):
# crc = crc16_viper(crc, data)
# print("py crc speed = ", f"{crc:08x}", 2**20 / (time.ticks_diff(time.ticks_us(), start) / 1e6), "bytes/s")