# --------------------------------------------------------------------------------------------------------------------- # 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 # ---------------------------------------------------------------------------------------------------------------------