-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathcrctool.py
executable file
·115 lines (90 loc) · 3.31 KB
/
crctool.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#!/usr/bin/env python3
import sys
import struct
import argparse
from zlib import crc32
from pathlib import Path
def bit_rev(val):
"Reverse bits of 32-bit words, stored as ints"
if isinstance(val, int):
return int('{:032b}'.format(val)[::-1], 2)
else:
output = []
for x in val:
output.append(int('{:032b}'.format(x)[::-1], 2))
return output
def calculate_stm32_crc(data):
"""Generate a CRC compatible with the STM32 hardware CRC
STM's CRC uses a normal CRC32 (MSB first) with a reset value of 0xFFFFFFFF
zlib uses a reversed CRC32 (LSB first) with a reset value of 0
To convert from zlib to STM formats we have to:
- bit reverse every 32-bit word of the input
- bit reverse the CRC generated by zlib.crc32
- bit invert the result as an unsigned number
from https://community.st.com/s/question/0D50X00009XkbXeSAJ/crc32-calculation-mismatch (post 3)
"""
# TODO test if data len is a multiple of 4
words = len(data) / 4
if not words.is_integer():
raise ValueError("Data size must be a multiple of 4 bytes")
# convert data into 32 bit words
data_words = struct.unpack(f'{int(words)}I', data)
# bit reverse words and convert back to bytes
data_rev = struct.pack(f'{int(words)}I', *bit_rev(data_words))
# compute an inital CRC using a reverse 32 bit polynomial
crc_raw = crc32(data_rev)
# invert the CRC
crc = bit_rev(crc_raw) ^ 0xffffffff
return crc
def insert_crc(data, crc, addr):
"""Insert CRC-32 at addr
This overwrites the 4 bytes at addr
"""
data_array = bytearray(data) # make the data mutable
# convert CRC to bytes, padded to 4 bytes
crc_bytes = crc.to_bytes(4, byteorder=sys.byteorder)
# insert CRC using slicing
data_array[addr:(addr + 4)] = crc_bytes
return bytes(data_array)
def ExistingFile(val):
""" To be used as an argparse argument type
Tests the file exists without opening it to avoid issues with files
not being closed: https://bugs.python.org/issue13824
"""
f = Path(val)
try:
if not f.is_file():
raise argparse.ArgumentTypeError(f'File not found {val}')
except PermissionError as e:
raise argparse.ArgumentTypeError(e)
return f
def main():
parser = argparse.ArgumentParser(
description="Compute STM32 compatible CRC32 values"
)
parser.add_argument(
'-w', '--write', action='store_true',
help="Write the CRC value to the third 32-bit word of the file"
)
parser.add_argument(
'-S', '--start', metavar='start_offset', type=int, default=0,
help="The address offset from the start of the file begin calculating the CRC from"
)
parser.add_argument('file', type=ExistingFile, help="The file to compute the CRC on")
args = parser.parse_args()
data = args.file.read_bytes()
try:
crc = calculate_stm32_crc(data[args.start:])
except ValueError as e:
print(e)
return
if args.write:
data = insert_crc(data, crc, args.start + 8)
try:
args.file.write_bytes(data)
except PermissionError():
print(f"Failed to write {args.file}, CRC is {crc:8X}")
else:
print(f"{crc:8X}")
if __name__ == "__main__":
main()