70 lines
2.2 KiB
Python
70 lines
2.2 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
"""Turn a Quartus .ttf bitstream into the app image embedded in a MKR Vidor 4000 sketch.
|
||
|
|
|
||
|
|
The Vidor bootloader expects the bitstream bytes bit-reversed relative to Quartus' .ttf
|
||
|
|
output, written as a C initializer list (bitstream.h), plus a signature block (signature.h)
|
||
|
|
describing where the image goes in the FPGA's flash. The app image slot starts at 512 KB.
|
||
|
|
"""
|
||
|
|
|
||
|
|
import argparse
|
||
|
|
import hashlib
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
APP_OFFSET_KB = 512
|
||
|
|
IMAGE_TYPE_APP = 1
|
||
|
|
|
||
|
|
|
||
|
|
def reverse_bits(byte: int) -> int:
|
||
|
|
return int(f"{byte:08b}"[::-1], 2)
|
||
|
|
|
||
|
|
|
||
|
|
def read_ttf(path: Path) -> bytes:
|
||
|
|
values = (field.strip() for field in path.read_text().split(","))
|
||
|
|
return bytes(reverse_bits(int(v)) for v in values if v)
|
||
|
|
|
||
|
|
|
||
|
|
def format_initializer(data: bytes) -> str:
|
||
|
|
lines = []
|
||
|
|
for start in range(0, len(data), 16):
|
||
|
|
chunk = data[start:start + 16]
|
||
|
|
if len(chunk) == 16:
|
||
|
|
lines.append(", ".join(f"{b:3d}" for b in chunk) + ",")
|
||
|
|
else:
|
||
|
|
lines.append("".join(f"{b:3d}, " for b in chunk))
|
||
|
|
return "\n".join(lines) + "\n"
|
||
|
|
|
||
|
|
|
||
|
|
def hex_bytes(data: bytes) -> str:
|
||
|
|
return ", ".join(f"0x{b:02x}" for b in data) + ","
|
||
|
|
|
||
|
|
|
||
|
|
def format_signature(image_text: str, length: int) -> str:
|
||
|
|
digest = hashlib.sha256(image_text.encode()).digest()
|
||
|
|
return "\n".join([
|
||
|
|
"NO_BOOTLOADER,",
|
||
|
|
hex_bytes((APP_OFFSET_KB * 1024).to_bytes(4, "little")),
|
||
|
|
hex_bytes(length.to_bytes(4, "little")),
|
||
|
|
hex_bytes(digest),
|
||
|
|
hex_bytes(IMAGE_TYPE_APP.to_bytes(4, "little")),
|
||
|
|
"0x00, 0x00, 0x00, 0x00, // force flag",
|
||
|
|
"NO_USER_DATA,",
|
||
|
|
]) + "\n"
|
||
|
|
|
||
|
|
|
||
|
|
def main() -> None:
|
||
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
||
|
|
parser.add_argument("ttf", type=Path, help="Quartus .ttf bitstream")
|
||
|
|
parser.add_argument("out_dir", type=Path, help="directory for bitstream.h and signature.h")
|
||
|
|
args = parser.parse_args()
|
||
|
|
|
||
|
|
data = read_ttf(args.ttf)
|
||
|
|
image_text = format_initializer(data)
|
||
|
|
args.out_dir.mkdir(parents=True, exist_ok=True)
|
||
|
|
(args.out_dir / "bitstream.h").write_text(image_text)
|
||
|
|
(args.out_dir / "signature.h").write_text(format_signature(image_text, len(data)))
|
||
|
|
print(f"{len(data)} bytes -> {args.out_dir}/bitstream.h, signature.h")
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
main()
|