diff --git a/tools/mem_init_gen.py b/tools/mem_init_gen.py new file mode 100644 index 0000000000000000000000000000000000000000..9fa2cf00d5695f5c5e47ce9f47cb96533f8bcc86 --- /dev/null +++ b/tools/mem_init_gen.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python +##------------------------------------------------------------------------------- +## CERN BE-CO-HT +## General Cores +## https://www.ohwr.org/projects/general-cores +##------------------------------------------------------------------------------- +## +## Python script to generate an init file for block rams. Input file should be +## raw binary, such as the one generated by GNU binutils "objcopy -O binary". +## +## Loosely based on the various init generation tools found in wrpc-sw. +## +## Usage: +## python mem_init_gen.py -of output_format input_file > output_file +## +## Supported output formats: +## +## bram: custom ascii-encoded binary, one 32-bit value per line, for Xilinx, +## to be used with the functions in the memory_loader_pkg +## +## mif : Altera Memory Initialization File +## +## vhd : constant VHDL array of type t_meminit_array, as defined in genram_pkg. +## +##------------------------------------------------------------------------------- +## Copyright CERN 2018 +##------------------------------------------------------------------------------- +## This Source Code Form is subject to the terms of the Mozilla Public License, +## version 2.0. If a copy of the MPL was not distributed with this file, You can +## obtain one at https://mozilla.org/MPL/2.0/. +##------------------------------------------------------------------------------- + +from __future__ import print_function + +import argparse +import binascii +import datetime + +today = datetime.date.today() + +parser = argparse.ArgumentParser ( + description='script to generate an init file for block rams' ) + +parser.add_argument ( 'in_file' ) +parser.add_argument ( '-i', '--invert', action='store_true', dest='invert', + help = 'Invert endianess of input file' ) +parser.add_argument ( '-of', '--oformat', choices = ['BRAM', 'MIF', 'VHD'], type = str.upper, + required = False, dest='oformat', default = 'BRAM', + help = 'output format (default is BRAM)' ) +parser.add_argument ( '-d', '--depth', type = int, default = 0, + required = False, dest='depth', + help = 'depth of memory in bytes (default is equal to the ' + 'size of the input file)' ) +parser.add_argument ( '-w', '--width', type = int, + required = False, dest='width', default = 4, + help = 'width of memory in bytes (default is 4)' ) +parser.add_argument ( '-p', '--pad', type = int, + required = False, dest='pad', default = 0, + help = 'byte padding value to use if size argument is larger ' + 'than input file size (default is 0, max is 255)' ) +parser.add_argument ( '-n', '--name', type = str.lower, + required = False, dest='name', default = 'mem_init', + help = 'name to use for this block (default is "mem_init")' ) + +args = parser.parse_args ( ) + +# check for proper padding value +if args.pad not in range ( 0, 256 ): + parser.error ( 'Padding value must be between 0 and 255' ) + +# read binary file +with open ( args.in_file, 'rb' ) as fin: + contents = fin.read ( ) + +# byte value to use for padding +pad_val = chr ( args.pad & 0xff ) + +# pad/trim if necessary to get to args.depth +if args.depth: + byte_count = args.depth * args.width + if byte_count > len ( contents ): + contents += pad_val * ( byte_count - len ( contents ) ) + else: + contents = contents[:byte_count] + +# pad if necessary to get to args.width boundary +contents += pad_val * ( len ( contents ) % args.width ) + +# convert to 2D list of WIDTH-sized ASCII-encoded (hex format) elements +# with optional endianess inversion +if args.invert == True and args.width > 1: + words = [ int ( binascii.hexlify ( contents[i:i+args.width][::-1] ), 16 ) + for i in range ( 0, len ( contents ), args.width ) ] +else: + words = [ int ( binascii.hexlify ( contents[i:i+args.width] ), 16 ) + for i in range ( 0, len ( contents ), args.width ) ] + +# BRAM output +if args.oformat == 'BRAM': + fwidth = args.width * 8 + for word in words: + print ( '{:0{fwidth}b}'.format ( word, fwidth = fwidth ) ) + +# MIF output +if args.oformat == 'MIF': + print ( 'DEPTH = {};'.format ( len ( words ) ) ) + print ( 'WIDTH = {};'.format ( args.width ) ) + print ( 'ADDRESS_RADIX = HEX;' ) + print ( 'DATA_RADIX = HEX;' ) + print ( 'CONTENT' ) + print ( 'BEGIN' ) + fwidth = args.width * 2 + for i, word in enumerate ( words ): + print ( '{:x} : {:0{fwidth}x};'.format ( i, word, fwidth = fwidth ) ) + print ( 'END;' ) + +# VHD output +if args.oformat == 'VHD': + print ( '-' * 80 ) + print ( '-- Memory initialization file for {}'.format ( args.in_file ) ) + print ( '--' ) + print ( '-- This file was automatically generated on {}'.format ( + today.strftime ( '%A, %B %d %Y') ) ) + print ( '-- by {} using the following arguments:'.format ( parser.prog ) ) + print ( '-- {}'.format ( vars(args) ) ) + print ( '--' ) + print ( '-- {} is part of OHWR general-cores:'.format ( parser.prog ) ) + print ( '-- https://www.ohwr.org/projects/general-cores/wiki' ) + print ( '-' * 80 ) + print ( ) + print ( 'library ieee;' ) + print ( 'use ieee.std_logic_1164.all;' ) + print ( 'use ieee.numeric_std.all;' ) + print ( ) + print ( 'library work;' ) + print ( 'use work.memory_loader_pkg.all;' ) + print ( ) + print ( 'package {}_pkg is'.format ( args.name ) ) + print ( ) + print ( ' constant {} : t_meminit_array('.format ( args.name ), + '{} downto 0, {} downto 0) := ('.format ( len ( words ) - 1, args.width * 8 - 1 ) ) + iwidth = len ( str ( len ( words ) ) ) + fwidth = args.width * 2 + numcol = 80 / (12 + iwidth + fwidth) + for i, word in enumerate ( words ): + print ( ' {:{iwidth}d} => x"{:0{fwidth}x}"'.format ( + i, word, iwidth = iwidth, fwidth = fwidth ), end = '' ) + if i == len ( words ) - 1: + print ( ');' ); + else: + print ( ',', end = '' ); + if i % numcol == numcol - 1: + print ( ) + print ( ) + print ( 'end package {}_pkg;'.format ( args.name ) )