[tools] Move functions to utilities, add universal CRC16 class

This commit is contained in:
Kuba Szczodrzyński
2022-06-10 11:02:49 +02:00
parent bbf8e0b6b6
commit c3f2ce57f0
6 changed files with 303 additions and 43 deletions

View File

@@ -10,7 +10,7 @@ from binascii import crc32
from os import makedirs
from os.path import basename, dirname, join
from tools.util.crypto import crc16
from tools.util.crc16 import CRC16
from tools.util.platform import get_board_manifest
if __name__ == "__main__":
@@ -61,7 +61,7 @@ if __name__ == "__main__":
cs = crc32(part)
cs = cs.to_bytes(length=4, byteorder="big")
else:
cs = crc16(part)
cs = CRC16.ARC.calc(part)
cs = cs.to_bytes(length=2, byteorder="big")
filename = f"{offset}_{name}_{cs.hex().upper()}.bin"
print(f"Writing {filename}")

View File

@@ -1,20 +1,32 @@
# Copyright (c) Kuba Szczodrzyński 2022-05-31.
import sys
from os.path import dirname, join
sys.path.append(join(dirname(__file__), ".."))
from argparse import ArgumentParser
from enum import Enum
from os import stat, unlink
from os.path import basename, dirname, getmtime, isfile, join
from os.path import basename, dirname, isfile, join
from shutil import copyfile
from subprocess import PIPE, Popen
from typing import IO, Dict, List, Tuple
from tools.util.fileio import chext, chname, isnewer, readbin
from tools.util.intbin import inttole32
class SocType(Enum):
UNSET = ()
AMBZ = (1, "arm-none-eabi-", True)
def cmd(self, cmd: str) -> IO[bytes]:
process = Popen(self.value[1] + cmd, stdout=PIPE)
try:
process = Popen(self.value[1] + cmd, stdout=PIPE)
except FileNotFoundError:
print(f"Toolchain not found while running: '{self.value[1] + cmd}'")
exit(1)
return process.stdout
@property
@@ -30,28 +42,6 @@ soc: "SocType" = SocType.UNSET
# | | | | __| | | | __| |/ _ \/ __|
# | |__| | |_| | | | |_| | __/\__ \
# \____/ \__|_|_|_|\__|_|\___||___/
def chname(path: str, name: str) -> str:
return join(dirname(path), name)
def chext(path: str, ext: str) -> str:
return path.rpartition(".")[0] + "." + ext
def isnewer(what: str, than: str) -> bool:
if not isfile(than):
return True
if not isfile(what):
return False
return getmtime(what) > getmtime(than)
def readbin(file: str) -> bytes:
with open(file, "rb") as f:
data = f.read()
return data
def checkfile(path: str):
if not isfile(path) or stat(path).st_size == 0:
exit(1)
@@ -97,8 +87,8 @@ def objcopy(
def elf2bin_ambz(input: str, ota_idx: int = 1) -> Tuple[int, str]:
def write_header(f: IO[bytes], start: int, end: int):
f.write(b"81958711")
f.write((end - start).to_bytes(length=4, byteorder="little"))
f.write(start.to_bytes(length=4, byteorder="little"))
f.write(inttole32(end - start))
f.write(inttole32(start))
f.write(b"\xff" * 16)
sections_ram = [
@@ -156,6 +146,12 @@ def elf2bin(input: str, ota_idx: int = 1) -> Tuple[int, str]:
raise NotImplementedError(f"SoC ELF->BIN not implemented: {soc}")
# _ _ _
# | | (_) | |
# | | _ _ __ | | _____ _ __
# | | | | '_ \| |/ / _ \ '__|
# | |____| | | | | < __/ |
# |______|_|_| |_|_|\_\___|_|
def ldargs_parse(
args: List[str],
ld_ota1: str,
@@ -178,6 +174,9 @@ def ldargs_parse(
if arg.endswith(".ld") and ld_ota1:
# use OTA2 linker script
args2[i] = arg.replace(ld_ota1, ld_ota2)
if not elf1 or not elf2:
print("Linker output .elf not found in arguments")
return None
return [(elf1, args1), (elf2, args2)]
@@ -194,6 +193,9 @@ def link2bin(
# just get .elf output name for single-OTA chips
elfs = ldargs_parse(args, None, None)
if not elfs:
return None
ota_idx = 1
for elf, ldargs in elfs:
# print graph element
@@ -203,6 +205,7 @@ def link2bin(
ldargs = " ".join(ldargs)
soc.cmd(f"gcc {ldargs}").read()
checkfile(elf)
# generate a set of binaries for the SoC
elf2bin(elf, ota_idx)
ota_idx += 1
@@ -223,5 +226,12 @@ if __name__ == "__main__":
parser.add_argument("ota2", type=str, help=".LD file OTA2 pattern")
parser.add_argument("args", type=str, nargs="*", help="Linker arguments")
args = parser.parse_args()
soc = next(soc for soc in SocType if soc.name == args.soc)
try:
soc = next(soc for soc in SocType if soc.name == args.soc)
except StopIteration:
print(f"Not a valid SoC: {args.soc}")
exit(1)
if not args.args:
print(f"Linker arguments must not be empty")
exit(1)
link2bin(args.args, args.ota1, args.ota2)

133
tools/util/crc16.py Normal file
View File

@@ -0,0 +1,133 @@
# Copyright (c) Kuba Szczodrzyński 2022-06-02.
from enum import Enum
from typing import List
class CRC16(Enum):
# based on https://crccalc.com/ and https://reveng.sourceforge.io/crc-catalogue/16.htm
ANSI = dict(poly=0x8005, init=0x0000, ref=False, out=0x0000)
ARC = dict(poly=0x8005, init=0x0000, ref=True, out=0x0000)
AUG_CCITT = dict(poly=0x1021, init=0x1D0F, ref=False, out=0x0000)
AUTOSAR = dict(poly=0x1021, init=0xFFFF, ref=False, out=0x0000)
BUYPASS = dict(poly=0x8005, init=0x0000, ref=False, out=0x0000)
CCITT = dict(poly=0x1021, init=0x0000, ref=True, out=0x0000)
CCITT_FALSE = dict(poly=0x1021, init=0xFFFF, ref=False, out=0x0000)
CCITT_TRUE = dict(poly=0x1021, init=0x0000, ref=True, out=0x0000)
CDMA2000 = dict(poly=0xC867, init=0xFFFF, ref=False, out=0x0000)
CMS = dict(poly=0x8005, init=0xFFFF, ref=False, out=0x0000)
CRC_A = dict(poly=0x1021, init=0xC6C6, ref=True, out=0x0000)
CRC_B = dict(poly=0x1021, init=0xFFFF, ref=True, out=0xFFFF)
DARC = dict(poly=0x1021, init=0xFFFF, ref=False, out=0xFFFF)
DDS_110 = dict(poly=0x8005, init=0x800D, ref=False, out=0x0000)
DECT_R = dict(poly=0x0589, init=0x0000, ref=False, out=0x0001)
DECT_X = dict(poly=0x0589, init=0x0000, ref=False, out=0x0000)
DNP = dict(poly=0x3D65, init=0x0000, ref=True, out=0xFFFF)
EN_13757 = dict(poly=0x3D65, init=0x0000, ref=False, out=0xFFFF)
EPC = dict(poly=0x1021, init=0xFFFF, ref=False, out=0xFFFF)
EPC_C1G2 = dict(poly=0x1021, init=0xFFFF, ref=False, out=0xFFFF)
GENIBUS = dict(poly=0x1021, init=0xFFFF, ref=False, out=0xFFFF)
GSM = dict(poly=0x1021, init=0x0000, ref=False, out=0xFFFF)
I_CODE = dict(poly=0x1021, init=0xFFFF, ref=False, out=0xFFFF)
IBM = dict(poly=0x8005, init=0x0000, ref=False, out=0x0000)
IBM_3740 = dict(poly=0x1021, init=0xFFFF, ref=False, out=0x0000)
IBM_SDLC = dict(poly=0x1021, init=0xFFFF, ref=True, out=0xFFFF)
IEC_61158_2 = dict(poly=0x1DCF, init=0xFFFF, ref=False, out=0xFFFF)
ISO_14443_3_A = dict(poly=0x1021, init=0xC6C6, ref=True, out=0x0000)
ISO_14443_3_B = dict(poly=0x1021, init=0xFFFF, ref=True, out=0xFFFF)
ISO_HDLC = dict(poly=0x1021, init=0xFFFF, ref=True, out=0xFFFF)
KERMIT = dict(poly=0x1021, init=0x0000, ref=True, out=0x0000)
LHA = dict(poly=0x8005, init=0x0000, ref=True, out=0x0000)
LJ1200 = dict(poly=0x6F63, init=0x0000, ref=False, out=0x0000)
M17 = dict(poly=0x5935, init=0xFFFF, ref=False, out=0x0000)
MAXIM = dict(poly=0x8005, init=0x0000, ref=True, out=0xFFFF)
MCRF4XX = dict(poly=0x1021, init=0xFFFF, ref=True, out=0x0000)
MODBUS = dict(poly=0x8005, init=0xFFFF, ref=True, out=0x0000)
NRSC_5 = dict(poly=0x080B, init=0xFFFF, ref=True, out=0x0000)
OPENSAFETY_A = dict(poly=0x5935, init=0x0000, ref=False, out=0x0000)
OPENSAFETY_B = dict(poly=0x755B, init=0x0000, ref=False, out=0x0000)
PROFIBUS = dict(poly=0x1DCF, init=0xFFFF, ref=False, out=0xFFFF)
RIELLO = dict(poly=0x1021, init=0xB2AA, ref=True, out=0x0000)
SPI_FUJITSU = dict(poly=0x1021, init=0x1D0F, ref=False, out=0x0000)
T10_DIF = dict(poly=0x8BB7, init=0x0000, ref=False, out=0x0000)
TELEDISK = dict(poly=0xA097, init=0x0000, ref=False, out=0x0000)
TMS37157 = dict(poly=0x1021, init=0x89EC, ref=True, out=0x0000)
UMTS = dict(poly=0x8005, init=0x0000, ref=False, out=0x0000)
USB = dict(poly=0x8005, init=0xFFFF, ref=True, out=0xFFFF)
V_41_LSB = dict(poly=0x1021, init=0x0000, ref=True, out=0x0000)
VERIFONE = dict(poly=0x8005, init=0x0000, ref=False, out=0x0000)
X_25 = dict(poly=0x1021, init=0xFFFF, ref=True, out=0xFFFF)
XMODEM = dict(poly=0x1021, init=0x0000, ref=False, out=0x0000)
poly: int
init: int
ref: bool
out: int
table: List[int]
def __init__(self, params: dict) -> None:
super().__init__()
self.poly = params["poly"]
self.init = params["init"]
self.ref = params["ref"]
self.out = params["out"]
self.table = None
if self.ref:
self.poly = self.reverse16(self.poly)
self.init = self.reverse16(self.init)
@staticmethod
def reverse16(num: int) -> int:
out = 0
for i in range(16):
out |= ((num & (1 << i)) >> i) << (15 - i)
return out
def calc(self, data: bytes) -> int:
if self.ref:
self._init_ref()
return self._calc_ref(data)
self._init_std()
return self._calc_std(data)
def _init_std(self):
if self.table:
return
self.table = []
for b in range(256):
crc = b << 8
for _ in range(8):
if crc & 0x8000:
crc <<= 1
crc ^= self.poly
else:
crc <<= 1
self.table.append(crc & 0xFFFF)
def _init_ref(self):
if self.table:
return
self.table = []
for b in range(256):
crc = b
for _ in range(8):
if crc & 0x0001:
crc >>= 1
crc ^= self.poly
else:
crc >>= 1
self.table.append(crc)
def _calc_std(self, data: bytes) -> int:
crc = self.init
for b in data:
b ^= crc // 256
crc = self.table[b] ^ (crc * 256 % 0x10000)
return crc ^ self.out
def _calc_ref(self, data: bytes) -> int:
crc = self.init
for b in data:
b ^= crc % 256
crc = self.table[b] ^ (crc // 256)
return crc ^ self.out

View File

@@ -1,14 +0,0 @@
# Copyright (c) Kuba Szczodrzyński 2022-06-02.
def crc16(data):
# https://gist.github.com/pintoXD/a90e398bba5a1b6c121de4e1265d9a29
crc = 0x0000
for b in data:
crc ^= b
for j in range(0, 8):
if (crc & 0x0001) > 0:
crc = (crc >> 1) ^ 0xA001
else:
crc = crc >> 1
return crc

53
tools/util/fileio.py Normal file
View File

@@ -0,0 +1,53 @@
# Copyright (c) Kuba Szczodrzyński 2022-06-10.
import json
from io import BytesIO
from os.path import dirname, getmtime, isfile, join
from typing import Union
def chname(path: str, name: str) -> str:
"""Change the basename of 'path' to 'name'."""
return join(dirname(path), name)
def chext(path: str, ext: str) -> str:
"""Change the file extension of 'path' to 'ext' (without the dot)."""
return path.rpartition(".")[0] + "." + ext
def isnewer(what: str, than: str) -> bool:
"""Check if 'what' is newer than 'than'.
Returns False if 'what' is not a file.
Returns True if 'than' is not a file.
"""
if not isfile(what):
return False
if not isfile(than):
return True
return getmtime(what) > getmtime(than)
def readbin(file: str) -> bytes:
"""Read a binary file into a bytes object."""
with open(file, "rb") as f:
data = f.read()
return data
def writebin(file: str, data: Union[bytes, BytesIO]):
"""Write data into a binary file."""
with open(file, "wb") as f:
if isinstance(data, BytesIO):
f.write(data.getvalue())
else:
f.write(data)
# same as load_json
def readjson(file: str) -> Union[dict, list]:
"""Read a JSON file into a dict or list."""
with open(file, "r", encoding="utf-8") as f:
return json.load(f)

View File

@@ -2,60 +2,138 @@
def bswap(data: bytes) -> bytes:
"""Reverse the byte array (big-endian <-> little-endian)."""
return bytes(reversed(data))
def betoint(data: bytes) -> int:
"""Convert bytes to big-endian unsigned integer."""
return int.from_bytes(data, byteorder="big")
def letoint(data: bytes) -> int:
"""Convert bytes to little-endian unsigned integer."""
return int.from_bytes(data, byteorder="little")
def betosint(data: bytes) -> int:
"""Convert bytes to big-endian signed integer."""
return int.from_bytes(data, byteorder="big", signed=True)
def letosint(data: bytes) -> int:
"""Convert bytes to little-endian signed integer."""
return int.from_bytes(data, byteorder="little", signed=True)
def inttobe32(data: int) -> bytes:
"""Convert unsigned integer to 32 bits, big-endian."""
return data.to_bytes(length=4, byteorder="big")
def inttole32(data: int) -> bytes:
"""Convert unsigned integer to 32 bits, little-endian."""
return data.to_bytes(length=4, byteorder="little")
def inttobe24(data: int) -> bytes:
"""Convert unsigned integer to 24 bits, big-endian."""
return data.to_bytes(length=3, byteorder="big")
def inttole24(data: int) -> bytes:
"""Convert unsigned integer to 24 bits, little-endian."""
return data.to_bytes(length=3, byteorder="little")
def inttobe16(data: int) -> bytes:
"""Convert unsigned integer to 16 bits, big-endian."""
return data.to_bytes(length=2, byteorder="big")
def inttole16(data: int) -> bytes:
"""Convert unsigned integer to 16 bits, little-endian."""
return data.to_bytes(length=2, byteorder="little")
def intto8(data: int) -> bytes:
"""Convert unsigned integer to 8 bits."""
return data.to_bytes(length=1, byteorder="big")
def sinttobe32(data: int) -> bytes:
"""Convert signed integer to 32 bits, big-endian."""
return data.to_bytes(length=4, byteorder="big", signed=True)
def sinttole32(data: int) -> bytes:
"""Convert signed integer to 32 bits, little-endian."""
return data.to_bytes(length=4, byteorder="little", signed=True)
def sinttobe24(data: int) -> bytes:
"""Convert signed integer to 24 bits, big-endian."""
return data.to_bytes(length=3, byteorder="big", signed=True)
def sinttole24(data: int) -> bytes:
"""Convert signed integer to 24 bits, little-endian."""
return data.to_bytes(length=3, byteorder="little", signed=True)
def sinttobe16(data: int) -> bytes:
"""Convert signed integer to 16 bits, big-endian."""
return data.to_bytes(length=2, byteorder="big", signed=True)
def sinttole16(data: int) -> bytes:
"""Convert signed integer to 16 bits, little-endian."""
return data.to_bytes(length=2, byteorder="little", signed=True)
def sintto8(data: int) -> bytes:
"""Convert signed integer to 8 bits."""
return data.to_bytes(length=1, byteorder="little", signed=True)
def align_up(x: int, n: int) -> int:
"""Return x aligned up to block size of n."""
return int((x - 1) // n + 1) * n
def align_down(x: int, n: int) -> int:
"""Return 'x' aligned down to block size of 'n'."""
return int(x // n) * n
def pad_up(x: int, n: int) -> int:
"""Return how many bytes of padding is needed to align 'x'
up to block size of 'n'."""
return n - (x % n)
def pad_data(data: bytes, n: int, char: int) -> bytes:
"""Add 'char'-filled padding to 'data' to align to a 'n'-sized block."""
if len(data) % n == 0:
return data
return data + (bytes([char]) * pad_up(len(data), n))
def uint8(val):
"""Get only the least-significant 8 bits of the value."""
return val & 0xFF
def uint16(val):
"""Get only the least-significant 16 bits of the value."""
return val & 0xFFFF
def uint32(val):
"""Get only the least-significant 32 bits of the value."""
return val & 0xFFFFFFFF
def uintmax(bits: int) -> int:
"""Get maximum integer size for given bit width."""
return (2**bits) - 1