From 9ecc9e83e8d182b7dbc3e21f3ba7870747c81e92 Mon Sep 17 00:00:00 2001 From: "Pavel V. Shatov (Meister)" Date: Mon, 20 Dec 2021 12:50:54 +0300 Subject: Script to combine main FPGA bitstream with the MKM bitmap so that the combined binary blob can be flashed using the upload script. --- util/.gitignore | 1 + util/link_bitstreams.py | 205 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 206 insertions(+) create mode 100644 util/link_bitstreams.py diff --git a/util/.gitignore b/util/.gitignore index 03fbf09..f7d1bfe 100644 --- a/util/.gitignore +++ b/util/.gitignore @@ -1,5 +1,6 @@ .idea/ venv/ +*.bit *.bin *.mcs diff --git a/util/link_bitstreams.py b/util/link_bitstreams.py new file mode 100644 index 0000000..7e1439a --- /dev/null +++ b/util/link_bitstreams.py @@ -0,0 +1,205 @@ +# --------------------------------------------------------------------------------------------------------------------- +# link_bitstreams.py +# --------------------------------------------------------------------------------------------------------------------- +# +# Glue iCE40 bitstream to the main FPGA bitstream. The MKM bitstream is aligned on a sector boundary. +# +# --------------------------------------------------------------------------------------------------------------------- +# +# Copyright 2021 The Commons Conservancy Cryptech Project +# +# SPDX-License-Identifier: BSD-3-Clause +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# * Neither the name of the copyright holder nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# --------------------------------------------------------------------------------------------------------------------- + + +# --------------------------------------------------------------------------------------------------------------------- +# Imports +# --------------------------------------------------------------------------------------------------------------------- +import os +import sys +import math + + +# --------------------------------------------------------------------------------------------------------------------- +# More Imports +# --------------------------------------------------------------------------------------------------------------------- +from enum import Enum +from typing import Optional + + +# --------------------------------------------------------------------------------------------------------------------- +# Markers +# --------------------------------------------------------------------------------------------------------------------- +MARKER_XILINX = b"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" \ + b"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" \ + b"\x00\x00\x00\xBB\x11\x22\x00\x44\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" \ + b"\xAA\x99\x55\x66" + +MARKER_LATTICE = b"\x7E\xAA\x99\x7E" + + +# --------------------------------------------------------------------------------------------------------------------- +class FPGA(Enum): + Artix7 = 'artix7' + iCE40 = 'ice40' +# --------------------------------------------------------------------------------------------------------------------- + + +# --------------------------------------------------------------------------------------------------------------------- +# Settings +# --------------------------------------------------------------------------------------------------------------------- +PROM_PAGE_BYTES = 256 +PROM_SECTOR_BYTES = 256 * PROM_PAGE_BYTES + +MARKER_DICT = {FPGA.Artix7: MARKER_XILINX, + FPGA.iCE40: MARKER_LATTICE} + + +# --------------------------------------------------------------------------------------------------------------------- +def _load_bitstream(filename: str, fpga: FPGA) -> Optional[bytes]: + + # try to load input bitstream + try: + with open(filename, 'rb') as f: + bitstream = f.read() + + except FileNotFoundError: + print("File '%s' not found." % filename) + return + + finally: + len_bitstream_orig = len(bitstream) + print("File '%s' loaded, %d bytes." % (filename, len_bitstream_orig)) + + # check magic marker and strip leading bytes + bitstream = _check_marker(bitstream, fpga) + + # done + return bitstream +# --------------------------------------------------------------------------------------------------------------------- + + +# --------------------------------------------------------------------------------------------------------------------- +def _check_marker(bitstream: bytes, fpga: FPGA) -> Optional[bytes]: + + len_bitstream_orig = len(bitstream) + marker = MARKER_DICT[fpga] + + # check, that marker is present + if marker not in bitstream: + print(" Magic marker for device '%s' not found, did you specify the right filename?" % fpga.value) + return + + # try to strip leading redundant bytes + while not bitstream.startswith(marker): + bitstream = bitstream[1:] + + # print how many bytes we stripped + num_stripped_bytes = len_bitstream_orig - len(bitstream) + print(" Stripped %d bytes before the magic marker." % num_stripped_bytes) + + # done + return bitstream +# --------------------------------------------------------------------------------------------------------------------- + + +# --------------------------------------------------------------------------------------------------------------------- +def _save_bit(bitstream: bytes, filename: str) -> int: + + # write memory to file + with open(filename, 'wb') as f: + f.write(bitstream) + + print("Output bitstream written.") + + # done + return 0 +# --------------------------------------------------------------------------------------------------------------------- + + +# --------------------------------------------------------------------------------------------------------------------- +def _link_bitstreams(filename_artix7: str, filename_ice40: str, filename_output: str) -> int: + + # load bitstream + bitstream_artix7 = _load_bitstream(filename_artix7, FPGA.Artix7) + bitstream_ice40 = _load_bitstream(filename_ice40, FPGA.iCE40) + if bitstream_artix7 is None or bitstream_ice40 is None: + return 1 + + # grow main bitstream to sector boundary + num_sectors_artix7 = math.ceil(len(bitstream_artix7) / PROM_SECTOR_BYTES) + num_padding_bytes = num_sectors_artix7 * PROM_SECTOR_BYTES - len(bitstream_artix7) + print("Added %d byte(s) after the main bitstream to align the MKM bitmap on sector boundary." % num_padding_bytes) + padding_bytes = bytes(0xFF for _ in range(num_padding_bytes)) + bitstream_artix7_padded = bitstream_artix7 + padding_bytes + + # glue two bitstreams + bitstream_output = bitstream_artix7_padded + bitstream_ice40 + + # fill bitstream with 0xFF's up to page boundary + num_padding_bytes = PROM_PAGE_BYTES - (len(bitstream_output) % 256) + print("Added %d byte(s) after the mkm bitmap to align output on page boundary." % num_padding_bytes) + padding_bytes = bytes(0xFF for _ in range(num_padding_bytes)) + bitstream_output += padding_bytes + + # save bit + return _save_bit(bitstream_output, filename_output) +# --------------------------------------------------------------------------------------------------------------------- + + +# --------------------------------------------------------------------------------------------------------------------- +def print_usage() -> int: + print("USAGE: %s " % + os.path.basename(sys.argv[0])) + return 2 +# --------------------------------------------------------------------------------------------------------------------- + + +# --------------------------------------------------------------------------------------------------------------------- +def main() -> int: + + # check, that exactly three arguments were supplied + if len(sys.argv) != 4: + return print_usage() + + # now run the script + return _link_bitstreams(*sys.argv[1:4]) +# --------------------------------------------------------------------------------------------------------------------- + + +# --------------------------------------------------------------------------------------------------------------------- +if __name__ == '__main__': + sys.exit(main()) +# --------------------------------------------------------------------------------------------------------------------- + + +# --------------------------------------------------------------------------------------------------------------------- +# End-of-File +# --------------------------------------------------------------------------------------------------------------------- -- cgit v1.2.3