#!/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()