| #!/usr/bin/env python3 |
| # |
| # create_verilog_library.py |
| # |
| #---------------------------------------------------------------------------- |
| # Given a destination directory holding individual verilog files of a number |
| # of modules, create a single verilog library file named <alllibname> and place |
| # it in the same directory. This is done for the option "compile" if specified |
| # for the "-verilog" install. |
| #---------------------------------------------------------------------------- |
| |
| import sys |
| import os |
| import re |
| import glob |
| import fnmatch |
| |
| #---------------------------------------------------------------------------- |
| |
| def usage(): |
| print('') |
| print('Usage:') |
| print(' create_verilog_library <destlibdir> <destlib> [-compile-only]') |
| print(' [-stub] [-excludelist="file1,file2,..."]') |
| print('') |
| print('Create a single verilog library from a set of individual verilog files.') |
| print('') |
| print('where:') |
| print(' <destlibdir> is the directory containing the individual files') |
| print(' <destlib> is the root name of the library file') |
| print(' -compile-only remove the indidual files if specified') |
| print(' -stub generate only the module headers for each cell') |
| print(' -excludelist= is a comma-separated list of files to ignore') |
| print('') |
| |
| #---------------------------------------------------------------------------- |
| |
| def create_verilog_library(destlibdir, destlib, do_compile_only=False, do_stub=False, excludelist=[]): |
| |
| alllibname = destlibdir + '/' + destlib + '.v' |
| if os.path.isfile(alllibname): |
| os.remove(alllibname) |
| |
| print('Diagnostic: Creating consolidated verilog library ' + destlib + '.v') |
| vlist = glob.glob(destlibdir + '/*.v') |
| if alllibname in vlist: |
| vlist.remove(alllibname) |
| |
| # Create exclude list with glob-style matching using fnmatch |
| if len(vlist) > 0: |
| vlistnames = list(os.path.split(item)[1] for item in vlist) |
| notvlist = [] |
| for exclude in excludelist: |
| notvlist.extend(fnmatch.filter(vlistnames, exclude)) |
| |
| # Apply exclude list |
| if len(notvlist) > 0: |
| for file in vlist[:]: |
| if os.path.split(file)[1] in notvlist: |
| vlist.remove(file) |
| |
| if len(vlist) > 1: |
| print('New file is: ' + alllibname) |
| with open(alllibname, 'w') as ofile: |
| allmodules = [] |
| for vfile in vlist: |
| with open(vfile, 'r') as ifile: |
| # print('Adding ' + vfile + ' to library.') |
| vtext = ifile.read() |
| modules = re.findall(r'[ \t\n]module[ \t]+([^ \t\n\(]+)', vtext) |
| mseen = list(item for item in modules if item in allmodules) |
| allmodules.extend(list(item for item in modules if item not in allmodules)) |
| vfilter = remove_redundant_modules(vtext, allmodules, mseen) |
| # NOTE: The following workaround resolves an issue with iverilog, |
| # which does not properly parse specify timing paths that are not in |
| # parentheses. Easy to work around |
| vlines = re.sub(r'\)[ \t]*=[ \t]*([01]:[01]:[01])[ \t]*;', r') = ( \1 ) ;', vfilter) |
| print(vlines, file=ofile) |
| print('\n//--------EOF---------\n', file=ofile) |
| |
| if do_compile_only == True: |
| print('Compile-only: Removing individual verilog files') |
| for vfile in vlist: |
| if os.path.isfile(vfile): |
| os.remove(vfile) |
| elif os.path.islink(vfile): |
| os.unlink(vfile) |
| else: |
| print('Only one file (' + str(vlist) + '); ignoring "compile" option.') |
| |
| #---------------------------------------------------------------------------- |
| # Remove redundant module entries from a verilog file. "m2list" is a list of |
| # module names gleaned from all previously read files using re.findall(). |
| # "mlist" is a list of all module names including those in "ntext". |
| # The reason for doing this is that some verilog files may includes modules used |
| # by all the files, and if included more than once, then iverilog complains. |
| #---------------------------------------------------------------------------- |
| |
| def remove_redundant_modules(ntext, mlist, m2list): |
| updated = ntext |
| for module in mlist: |
| # Determine the number of times the module appears in the text |
| if module in m2list: |
| # This module seen before outside of ntext, so remove all occurrances in ntext |
| new = re.sub(r'[ \t\n]+module[ \t]+' + module + '[ \t\n\(]+.*[ \t\n]endmodule', '\n', updated, flags=re.DOTALL) |
| updated = new |
| |
| else: |
| n = len(re.findall(r'[ \t\n]module[ \t]+' + module + '[ \t\n\(]+.*[ \t\n]endmodule', updated, flags=re.DOTALL)) |
| # This module defined more than once inside ntext, so remove all but one |
| # Optimization: Just keep original text if n < 2 |
| if n < 2: |
| continue |
| |
| # Remove all but one |
| updated = re.sub(r'[ \t\n]+module[ \t]+' + module + '[ \t\n]+.*[ \t\n]endmodule', '\n', n - 1, updated, flags=re.IGNORECASE | re.DOTALL) |
| return updated |
| |
| #---------------------------------------------------------------------------- |
| |
| if __name__ == '__main__': |
| |
| if len(sys.argv) == 1: |
| usage() |
| sys.exit(0) |
| |
| argumentlist = [] |
| |
| # Defaults |
| do_compile_only = False |
| do_stub = False |
| excludelist = [] |
| |
| # Break arguments into groups where the first word begins with "-". |
| # All following words not beginning with "-" are appended to the |
| # same list (optionlist). Then each optionlist is processed. |
| # Note that the first entry in optionlist has the '-' removed. |
| |
| for option in sys.argv[1:]: |
| if option.find('-', 0) == 0: |
| keyval = option[1:].split('=') |
| if keyval[0] == 'compile-only': |
| if len(keyval) > 0: |
| if keyval[1].tolower() == 'true' or keyval[1].tolower() == 'yes' or keyval[1] == '1': |
| do_compile_only = True |
| else: |
| do_compile_only = True |
| elif keyval[1] == 'exclude' or key == 'excludelist': |
| if len(keyval) > 0: |
| excludelist = keyval[1].trim('"').split(',') |
| else: |
| print("No items in exclude list (ignoring).") |
| elif keyval[0] == 'stub': |
| if len(keyval) > 0: |
| if keyval[1].tolower() == 'true' or keyval[1].tolower() == 'yes' or keyval[1] == '1': |
| do_stub = True |
| else: |
| do_stub = True |
| else: |
| print("Unknown option '" + keyval[0] + "' (ignoring).") |
| else: |
| argumentlist.append(option) |
| |
| if len(argumentlist) < 3: |
| print("Not enough arguments given to create_verilog_library.py.") |
| usage() |
| sys.exit(1) |
| |
| destlibdir = argumentlist[0] |
| destlib = argumentlist[1] |
| startup_script = argumentlist[2] |
| |
| print('') |
| print('Create verilog library from files:') |
| print('') |
| print('Path to files: ' + destlibdir) |
| print('Name of compiled library: ' + destlib + '.v') |
| print('Path to magic startup script: ' + startup_script) |
| print('Remove individual files: ' + 'Yes' if do_compile_only else 'No') |
| if len(excludelist) > 0: |
| print('List of files to exclude: ') |
| for file in excludelist: |
| print(file) |
| print('') |
| |
| create_verilog_library(destlibdir, destlib, startup_script, do_compile_only, do_stub, excludelist) |
| print('Done.') |
| sys.exit(0) |
| |
| #---------------------------------------------------------------------------- |