|
| 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") |
0 commit comments