[tools] Move functions to utilities, add universal CRC16 class
This commit is contained in:
@@ -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}")
|
||||
|
||||
@@ -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
133
tools/util/crc16.py
Normal 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
|
||||
@@ -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
53
tools/util/fileio.py
Normal 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)
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user