summaryrefslogblamecommitdiff
path: root/util/bitstream2mcs.py
blob: 571375fe3b73533f2eab8682e6e95e4a8f57aab6 (plain) (tree)

































































































































































                                                                                                                       
# ---------------------------------------------------------------------------------------------------------------------
# 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 <bitstream.bin> <bitstream.mcs>" % 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
# ---------------------------------------------------------------------------------------------------------------------