From 69eda72fa8db3e332de6a66a92f994dfed99de94 Mon Sep 17 00:00:00 2001 From: "Pavel V. Shatov (Meister)" Date: Mon, 13 Sep 2021 11:22:37 +0300 Subject: Utility script to convert iCE40 bitstream into .mcs format suitable for programming with Xilinx iMPACT tool. --- util/.gitignore | 5 ++ util/bitstream2mcs.py | 162 ++++++++++++++++++++++++++++++++++++++++++++++++++ util/requirements.txt | 1 + 3 files changed, 168 insertions(+) create mode 100644 util/.gitignore create mode 100644 util/bitstream2mcs.py create mode 100644 util/requirements.txt diff --git a/util/.gitignore b/util/.gitignore new file mode 100644 index 0000000..03fbf09 --- /dev/null +++ b/util/.gitignore @@ -0,0 +1,5 @@ +.idea/ +venv/ + +*.bin +*.mcs diff --git a/util/bitstream2mcs.py b/util/bitstream2mcs.py new file mode 100644 index 0000000..571375f --- /dev/null +++ b/util/bitstream2mcs.py @@ -0,0 +1,162 @@ +# --------------------------------------------------------------------------------------------------------------------- +# bitstream2mcs.py +# --------------------------------------------------------------------------------------------------------------------- +# +# Convert iCE40 bitstream into .mcs format suitable for programming using Xilinx iMPACT tool. Xilinx .mcs is in fact +# just plain Intel Hex. Since the beginning of our configuration memory is occupied by the main FPGA bitstream, we +# have to move iCE40 bitstream to the end of the configuration memory space. +# +# --------------------------------------------------------------------------------------------------------------------- +# +# 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 typing import Union +from intelhex import IntelHex + + +# --------------------------------------------------------------------------------------------------------------------- +# Settings +# --------------------------------------------------------------------------------------------------------------------- +LATTICE_MAGIC_MARKER = b'\x7E\xAA\x99\x7E' + +PROM_SECTOR_SIZE = 256 * 256 +PROM_NUM_SECTORS = 256 + + +# --------------------------------------------------------------------------------------------------------------------- +def _load_bitstream(filename: str) -> Union[bytes, None]: + + # 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)) + + # try to strip header + while not bitstream.startswith(LATTICE_MAGIC_MARKER): + bitstream = bitstream[1:] + if not bitstream: + print("Magic marker not found??") + return + + # 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_mcs(offset: int, bitstream: bytes, filename: str) -> int: + + # create Intel Hex class + ih = IntelHex() + + # initialize memory from bitstream + ih.frombytes(bitstream, offset=offset) + + # write memory to file + with open(filename, 'w') as f: + ih.write_hex_file(f) + + # done + return 0 +# --------------------------------------------------------------------------------------------------------------------- + + +# --------------------------------------------------------------------------------------------------------------------- +def _bitstream2mcs(filename_in: str, filename_out: str) -> int: + + # load bitstream + bitstream = _load_bitstream(filename_in) + if bitstream is None: + return 1 + + # determine number of sectors needed + num_sectors = math.ceil(len(bitstream) / PROM_SECTOR_SIZE) + print("Number of PROM sectors needed: %d" % num_sectors) + prom_offset = (PROM_NUM_SECTORS - num_sectors) * PROM_SECTOR_SIZE + print("Bitstream offset: 0x%x" % prom_offset) + + # save mcs + return _save_mcs(prom_offset, bitstream, filename_out) +# --------------------------------------------------------------------------------------------------------------------- + + +# --------------------------------------------------------------------------------------------------------------------- +def print_usage() -> int: + print("USAGE: %s " % os.path.basename(sys.argv[0])) + return 2 +# --------------------------------------------------------------------------------------------------------------------- + + +# --------------------------------------------------------------------------------------------------------------------- +def main() -> int: + + # check, that exactly two arguments were supplied + if len(sys.argv) != 3: + return print_usage() + + # now run the script + return _bitstream2mcs(sys.argv[1], sys.argv[2]) +# --------------------------------------------------------------------------------------------------------------------- + + +# --------------------------------------------------------------------------------------------------------------------- +if __name__ == '__main__': + sys.exit(main()) +# --------------------------------------------------------------------------------------------------------------------- + + +# --------------------------------------------------------------------------------------------------------------------- +# End-of-File +# --------------------------------------------------------------------------------------------------------------------- diff --git a/util/requirements.txt b/util/requirements.txt new file mode 100644 index 0000000..e4a435a --- /dev/null +++ b/util/requirements.txt @@ -0,0 +1 @@ +intelhex==2.3.0 -- cgit v1.2.3