Merge branch 'master' of 192.168.0.7:/home/tim/gitsrc/open_pdks/
diff --git a/VERSION b/VERSION index 5b09c67..b668c3b 100644 --- a/VERSION +++ b/VERSION
@@ -1 +1 @@ -1.0.14 +1.0.16
diff --git a/common/create_gds_library.py b/common/create_gds_library.py new file mode 100755 index 0000000..23b7553 --- /dev/null +++ b/common/create_gds_library.py
@@ -0,0 +1,209 @@ +#!/usr/bin/env python3 +# +# create_gds_library.py +# +#---------------------------------------------------------------------------- +# Given a destination directory holding individual GDS files of a number +# of cells, create a single GDL library file named <alllibname> and place +# it in the same directory. This is done for the option "compile" if specified +# for the "-gds" install. +#---------------------------------------------------------------------------- + +import os +import sys +import glob +import fnmatch +import subprocess + +#---------------------------------------------------------------------------- + +def usage(): + print('') + print('Usage:') + print(' create_gds_library <destlibdir> <destlib> <startup_script> ') + print(' [-compile-only] [-excludelist="file1,file2,..."] [-keep]') + print('') + print('Create a single GDS library from a set of individual GDS files.') + print('') + print('where:') + print(' <destlibdir> is the directory containing the individual GDS files') + print(' <destlib> is the root name of the library file') + print(' <startup_script> is the full path to a magic startup script') + print(' -compile-only removes the indidual files if specified') + print(' -excludelist= is a comma-separated list of files to ignore') + print(' -keep keep the Tcl script used to generate the library') + print('') + +#---------------------------------------------------------------------------- + +def create_gds_library(destlibdir, destlib, startup_script, do_compile_only, excludelist, keep): + + alllibname = destlibdir + '/' + destlib + '.gds' + if os.path.isfile(alllibname): + os.remove(alllibname) + + glist = glob.glob(destlibdir + '/*.gds') + glist.extend(glob.glob(destlibdir + '/*.gdsii')) + glist.extend(glob.glob(destlibdir + '/*.gds2')) + if alllibname in glist: + glist.remove(alllibname) + + # Create exclude list with glob-style matching using fnmatch + if len(glist) > 0: + glistnames = list(os.path.split(item)[1] for item in glist) + notglist = [] + for exclude in excludelist: + notglist.extend(fnmatch.filter(glistnames, exclude)) + + # Apply exclude list + if len(notglist) > 0: + for file in glist[:]: + if os.path.split(file)[1] in notglist: + glist.remove(file) + + if len(glist) > 1: + print('New file is: ' + alllibname) + + if os.path.isfile(startup_script): + # If the symbolic link exists, remove it. + if os.path.isfile(destlibdir + '/.magicrc'): + os.remove(destlibdir + '/.magicrc') + os.symlink(startup_script, destlibdir + '/.magicrc') + + # A GDS library is binary and requires handling in Magic + print('Creating magic generation script to generate GDS library.') + with open(destlibdir + '/generate_magic.tcl', 'w') as ofile: + print('#!/usr/bin/env wish', file=ofile) + print('#--------------------------------------------', file=ofile) + print('# Script to generate .gds library from files ', file=ofile) + print('#--------------------------------------------', file=ofile) + print('drc off', file=ofile) + print('gds readonly true', file=ofile) + print('gds flatten true', file=ofile) + print('gds rescale false', file=ofile) + print('tech unlock *', file=ofile) + + for gdsfile in glist: + print('gds read ' + gdsfile, file=ofile) + + print('puts stdout "Creating cell ' + destlib + '"', file=ofile) + print('load ' + destlib, file=ofile) + print('puts stdout "Adding cells to library"', file=ofile) + print('box values 0 0 0 0', file=ofile) + for gdsfile in glist: + gdsroot = os.path.split(gdsfile)[1] + gdsname = os.path.splitext(gdsroot)[0] + print('getcell ' + gdsname, file=ofile) + # Could properly make space for the cell here. . . + print('box move e 200', file=ofile) + + print('puts stdout "Writing GDS library ' + destlib + '"', file=ofile) + print('gds library true', file=ofile) + print('gds write ' + destlib, file=ofile) + print('puts stdout "Done."', file=ofile) + print('quit -noprompt', file=ofile) + + # Run magic to read in the individual GDS files and + # write out the consolidated GDS library + + print('Running magic to create GDS library.') + sys.stdout.flush() + + mproc = subprocess.run(['magic', '-dnull', '-noconsole', + destlibdir + '/generate_magic.tcl'], + stdin = subprocess.DEVNULL, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, cwd = destlibdir, + universal_newlines = True) + + if mproc.stdout: + for line in mproc.stdout.splitlines(): + print(line) + if mproc.stderr: + print('Error message output from magic:') + for line in mproc.stderr.splitlines(): + print(line) + if mproc.returncode != 0: + print('ERROR: Magic exited with status ' + str(mproc.returncode)) + if do_compile_only == True: + print('Compile-only: Removing individual GDS files') + for gfile in glist: + if os.path.isfile(gfile): + os.remove(gfile) + if newname: + if os.path.isfile(newname): + os.remove(newname) + if not keep: + os.remove(destlibdir + '/generate_magic.tcl') + else: + print('Only one file (' + str(glist) + '); ignoring "compile" option.') + +#---------------------------------------------------------------------------- + +if __name__ == '__main__': + + if len(sys.argv) == 1: + usage() + sys.exit(0) + + argumentlist = [] + + # Defaults + do_compile_only = False + keep = 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] == '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[1] == 'keep': + keep = True + else: + print("Unknown option '" + keyval[0] + "' (ignoring).") + else: + argumentlist.append(option) + + if len(argumentlist) < 3: + print("Not enough arguments given to create_gds_library.py.") + usage() + sys.exit(1) + + destlibdir = argumentlist[0] + destlib = argumentlist[1] + startup_script = argumentlist[2] + + print('') + print('Create GDS library from files:') + print('') + print('Path to files: ' + destlibdir) + print('Name of compiled library: ' + destlib + '.gds') + 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('Keep generating script: ' + 'Yes' if keep else 'No') + print('') + + create_gds_library(destlibdir, destlib, startup_script, do_compile_only, excludelist, keep) + print('Done.') + sys.exit(0) + +#----------------------------------------------------------------------------
diff --git a/common/create_lef_library.py b/common/create_lef_library.py new file mode 100755 index 0000000..2b76f9a --- /dev/null +++ b/common/create_lef_library.py
@@ -0,0 +1,155 @@ +#!/usr/bin/env python3 +# +# create_lef_library.py +# +#---------------------------------------------------------------------------- +# Given a destination directory holding individual LEF files of a number +# of cells, create a single LEF library file named <alllibname> and place +# it in the same directory. This is done for the option "compile" if specified +# for the "-lef" install. +#---------------------------------------------------------------------------- + +import sys +import os +import glob +import fnmatch + +def usage(): + print('') + print('Usage:') + print(' create_lef_library <destlibdir> <destlib> [-compile-only]') + print(' [-excludelist="file1,file2,..."]') + print('') + print('Create a single LEF library from a set of individual LEF files.') + print('') + print('where:') + print(' <destlibdir> is the directory containing the individual LEF files') + print(' <destlib> is the root name of the library file') + print(' -compile-only remove the indidual files if specified') + print(' -excludelist= is a comma-separated list of files to ignore') + print('') + +def create_lef_library(destlibdir, destlib, do_compile_only, excludelist): + + alllibname = destlibdir + '/' + destlib + '.lef' + if os.path.isfile(alllibname): + os.remove(alllibname) + + print('Diagnostic: Creating consolidated LEF library ' + destlib + '.lef') + llist = glob.glob(destlibdir + '/*.lef') + if alllibname in llist: + llist.remove(alllibname) + + # Create exclude list with glob-style matching using fnmatch + if len(llist) > 0: + llistnames = list(os.path.split(item)[1] for item in llist) + notllist = [] + for exclude in excludelist: + notllist.extend(fnmatch.filter(llistnames, exclude)) + + # Apply exclude list + if len(notllist) > 0: + for file in llist[:]: + if os.path.split(file)[1] in notllist: + llist.remove(file) + + if len(llist) > 1: + print('New file is: ' + alllibname) + with open(alllibname, 'w') as ofile: + headerdone = False + for lfile in llist: + with open(lfile, 'r') as ifile: + # print('Adding ' + lfile + ' to library.') + ltext = ifile.read() + llines = ltext.splitlines() + headerseen = False + for lline in llines: + if headerdone: + if not headerseen: + if not lline.startswith('MACRO'): + continue + else: + headerseen = True + ltok = lline.split() + if ltok[0] == 'END' and ltok[1] == 'LIBRARY': + # Remove "END LIBRARY" line from individual files + pass + else: + print(lline, file=ofile) + headerdone = True + print('#--------EOF---------\n', file=ofile) + + if do_compile_only == True: + print('Compile-only: Removing individual LEF files') + for lfile in llist: + if os.path.isfile(lfile): + os.remove(lfile) + if newname: + if os.path.isfile(newname): + os.remove(newname) + else: + print('Only one file (' + str(llist) + '); ignoring "compile" option.') + +#---------------------------------------------------------------------------- + +if __name__ == '__main__': + + if len(sys.argv) == 1: + usage() + sys.exit(0) + + argumentlist = [] + + # Defaults + do_compile_only = 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] == '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).") + else: + print("Unknown option '" + keyval[0] + "' (ignoring).") + else: + argumentlist.append(option) + + if len(argumentlist) < 2: + print("Not enough arguments given to create_lef_library.py.") + usage() + sys.exit(1) + + destlibdir = argumentlist[0] + destlib = argumentlist[1] + + print('') + print('Create LEF library from files:') + print('') + print('Path to files: ' + destlibdir) + print('Name of compiled library: ' + destlib + '.lef') + 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_lef_library(destlibdir, destlib, do_compile_only, excludelist) + print('Done.') + sys.exit(0) + +#----------------------------------------------------------------------------
diff --git a/common/create_lib_library.py b/common/create_lib_library.py new file mode 100755 index 0000000..8f9a39f --- /dev/null +++ b/common/create_lib_library.py
@@ -0,0 +1,155 @@ +#!/usr/bin/env python3 +# +# create_lib_library.py +# +#---------------------------------------------------------------------------- +# Given a destination directory holding individual liberty files of a number +# of cells, create a single liberty library file named <alllibname> and place +# it in the same directory. This is done for the option "compile" if specified +# for the "-lib" install. +#---------------------------------------------------------------------------- + +import sys +import os +import glob +import fnmatch + +def usage(): + print('') + print('Usage:') + print(' create_lib_library <destlibdir> <destlib> [-compile-only] ') + print(' [-excludelist="file1,file2,..."]') + print('') + print('Create a single liberty library from a set of individual liberty files.') + print('') + print('where:') + print(' <destlibdir> is the directory containing the individual liberty files') + print(' <destlib> is the root name of the library file') + print(' -compile-only remove the indidual files if specified') + print(' -excludelist= is a comma-separated list of files to ignore') + print('') + +# Warning: This script is unfinished. Needs to parse the library header +# in each cell and generate a new library header combining the contents of +# all cell headers. Also: The library name in the header needs to be +# changed to the full library name. Also: There is no mechanism for +# collecting all files belonging to a single process corner/temperature/ +# voltage. + +def create_lib_library(destlibdir, destlib, do_compile_only, excludelist): + + alllibname = destlibdir + '/' + destlib + '.lib' + if os.path.isfile(alllibname): + os.remove(alllibname) + + print('Diagnostic: Creating consolidated liberty library ' + destlib + '.lib') + + # Create exclude list with glob-style matching using fnmatch + if len(llist) > 0: + llistnames = list(os.path.split(item)[1] for item in llist) + notllist = [] + for exclude in excludelist: + notllist.extend(fnmatch.filter(llistnames, exclude)) + + # Apply exclude list + if len(notllist) > 0: + for file in llist[:]: + if os.path.split(file)[1] in notllist: + llist.remove(file) + + if len(llist) > 1: + print('New file is: ' + alllibname) + with open(alllibname, 'w') as ofile: + headerdone = False + for lfile in llist: + with open(lfile, 'r') as ifile: + # print('Adding ' + lfile + ' to library.') + ltext = ifile.read() + llines = ltext.splitlines() + headerseen = False + for lline in llines: + if headerdone: + if not headerseen: + if not lline.split()[0] == 'cell': + continue + else: + headerseen = True + print(lline, file=ofile) + headerdone = True + print('/*--------EOF---------*/\n', file=ofile) + + if do_compile_only == True: + print('Compile-only: Removing individual LEF files') + for lfile in llist: + if os.path.isfile(lfile): + os.remove(lfile) + if newname: + if os.path.isfile(newname): + os.remove(newname) + else: + print('Only one file (' + str(llist) + '); ignoring "compile" option.') + +#---------------------------------------------------------------------------- + +if __name__ == '__main__': + + if len(sys.argv) == 1: + usage() + sys.exit(0) + + argumentlist = [] + + # Defaults + do_compile_only = 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] == '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).") + else: + print("Unknown option '" + keyval[0] + "' (ignoring).") + else: + argumentlist.append(option) + + if len(argumentlist) < 3: + print("Not enough arguments given to create_lib_library.py.") + usage() + sys.exit(1) + + destlibdir = argumentlist[0] + destlib = argumentlist[1] + startup_script = argumentlist[2] + + print('') + print('Create liberty library from files:') + print('') + print('Path to files: ' + destlibdir) + print('Name of compiled library: ' + destlib + '.lib') + 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_lib_library(destlibdir, destlib, do_compile_only, excludelist) + print('Done.') + sys.exit(0) + +#----------------------------------------------------------------------------
diff --git a/common/create_spice_library.py b/common/create_spice_library.py new file mode 100755 index 0000000..6b8aa83 --- /dev/null +++ b/common/create_spice_library.py
@@ -0,0 +1,213 @@ +#!/usr/bin/env python3 +# +# create_spice_library.py +# +#---------------------------------------------------------------------------- +# Given a destination directory holding individual SPICE netlists of a number +# of cells, create a single SPICE library file named <alllibname> and place +# it in the same directory. This is done for the option "compile" if specified +# for the "-spice" install. +#---------------------------------------------------------------------------- + +import sys +import os +import re +import glob +import fnmatch + +#---------------------------------------------------------------------------- + +def usage(): + print('') + print('Usage:') + print(' create_spice_library <destlibdir> <destlib> <spiext>') + print(' [-compile-only] [-stub] [-excludelist="file1,file2,..."]') + print('') + print('Create a single SPICE or CDL library from a set of individual files.') + print('') + print('where:') + print(' <destlibdir> is the directory containing the individual files') + print(' <destlib> is the root name of the library file') + print(' <spiext> is the extension used (with ".") by the SPICE or CDL files') + print(' -compile-only remove the indidual files if specified') + print(' -stub generate only .subckt ... .ends for each cell') + print(' -excludelist= is a comma-separated list of files to ignore') + print('') + +#---------------------------------------------------------------------------- + +def create_spice_library(destlibdir, destlib, spiext, do_compile_only, do_stub, excludelist): + + fformat = 'CDL' if spiext == '.cdl' else 'SPICE' + + allstubname = destlibdir + '/stub' + spiext + alllibname = destlibdir + '/' + destlib + spiext + if do_stub: + outputname = allstubname + else: + outputname = alllibname + + print('Diagnostic: Creating consolidated ' + fformat + ' library ' + outputname) + + if os.path.isfile(outputname): + os.remove(outputname) + + if fformat == 'CDL': + slist = glob.glob(destlibdir + '/*.cdl') + else: + # Sadly, there is no consensus on what a SPICE file extension should be. + slist = glob.glob(destlibdir + '/*.spc') + slist.extend(glob.glob(destlibdir + '/*.spice')) + slist.extend(glob.glob(destlibdir + '/*.spi')) + slist.extend(glob.glob(destlibdir + '/*.ckt')) + slist.extend(glob.glob(destlibdir + '/*.cir')) + slist.extend(glob.glob(destlibdir + '/*' + spiext)) + + if alllibname in slist: + slist.remove(alllibname) + + if allstubname in slist: + slist.remove(allstubname) + + # Create exclude list with glob-style matching using fnmatch + if len(slist) > 0: + slistnames = list(os.path.split(item)[1] for item in slist) + notslist = [] + for exclude in excludelist: + notslist.extend(fnmatch.filter(slistnames, exclude)) + + # Apply exclude list + if len(notslist) > 0: + for file in slist[:]: + if os.path.split(file)[1] in notslist: + slist.remove(file) + + if len(slist) > 1: + with open(outputname, 'w') as ofile: + allsubckts = [] + for sfile in slist: + with open(sfile, 'r') as ifile: + # print('Adding ' + sfile + ' to library.') + stext = ifile.read() + subckts = re.findall(r'\.subckt[ \t]+([^ \t\n]+)', stext, flags=re.IGNORECASE) + sseen = list(item for item in subckts if item in allsubckts) + allsubckts.extend(list(item for item in subckts if item not in allsubckts)) + sfilter = remove_redundant_subckts(stext, allsubckts, sseen) + print(sfilter, file=ofile) + print('\n******* EOF\n', file=ofile) + + if do_compile_only == True: + print('Compile-only: Removing individual SPICE files') + for sfile in slist: + if os.path.isfile(sfile): + os.remove(sfile) + elif os.path.islink(sfile): + os.unlink(sfile) + else: + print('Only one file (' + str(slist) + '); ignoring "compile" option.') + +#---------------------------------------------------------------------------- +# Remove redundant subcircuit entries from a SPICE or CDL netlist file. "sseen" +# is a list of subcircuit names gleaned from all previously read files using +# re.findall(). "slist" is a list of subcircuits including those in "ntext". +# If a subcircuit is defined outside of "ntext", then remove all occurrences in +# "ntext". Otherwise, if a subcircuit is defined more than once in "ntext", +# remove all but one copy. The reason for doing this is that some netlists will +# include primitive device definitions used by all the standard cell subcircuits. +# +# It may be necessary to remove redundant .include statements and redundant .model +# and/or .option statements as well. +#---------------------------------------------------------------------------- + +def remove_redundant_subckts(ntext, slist, sseen): + updated = ntext + for subckt in slist: + if subckt in sseen: + # Remove all occurrences of subckt + updated = re.sub(r'\n\.subckt[ \t]+' + subckt + '[ \t\n]+.*\n\.ends[ \t\n]+', '\n', updated, flags=re.IGNORECASE | re.DOTALL) + + else: + # Determine the number of times the subcircuit appears in the text + n = len(re.findall(r'\n\.subckt[ \t]+' + subckt + '[ \t\n]+.*\n\.ends[ \t\n]+', updated, flags=re.IGNORECASE | re.DOTALL)) + # Optimization: Just keep original text if n < 2 + if n < 2: + continue + + # Remove all but one + updated = re.sub(r'\n\.subckt[ \t]+' + subckt + '[ \t\n]+.*\n\.ends[ \t\n]+', '\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[1] == '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_spice_library.py.") + usage() + sys.exit(1) + + destlibdir = argumentlist[0] + destlib = argumentlist[1] + startup_script = argumentlist[2] + + print('') + if spiext == '.cdl': + print('Create CDL library from files:') + else: + print('Create SPICE library from files:') + print('') + print('Path to files: ' + destlibdir) + print('Name of compiled library: ' + destlib + spiext) + 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_spice_library(destlibdir, destlib, spiext, do_compile_only, do_stub, excludelist) + print('Done.') + sys.exit(0) + +#----------------------------------------------------------------------------
diff --git a/common/create_verilog_library.py b/common/create_verilog_library.py new file mode 100755 index 0000000..d8565f4 --- /dev/null +++ b/common/create_verilog_library.py
@@ -0,0 +1,190 @@ +#!/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, do_stub, 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) + +#----------------------------------------------------------------------------
diff --git a/common/foundry_install.py b/common/foundry_install.py index 149c21f..267342b 100755 --- a/common/foundry_install.py +++ b/common/foundry_install.py
@@ -116,6 +116,13 @@ import fnmatch import subprocess +# Import local routines +from create_gds_library import create_gds_library +from create_spice_library import create_spice_library +from create_lef_library import create_lef_library +from create_lib_library import create_lib_library +from create_verilog_library import create_verilog_library + def usage(): print("foundry_install.py [options...]") print(" -copy Copy files from source to target (default)") @@ -327,438 +334,6 @@ print(line) #---------------------------------------------------------------------------- -# 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. -#---------------------------------------------------------------------------- - -def create_verilog_library(destlibdir, destlib, do_compile_only, do_stub, 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 - -#---------------------------------------------------------------------------- -# Given a destination directory holding individual LEF files of a number -# of cells, create a single LEF library file named <alllibname> and place -# it in the same directory. This is done for the option "compile" if specified -# for the "-lef" install. -#---------------------------------------------------------------------------- - -def create_lef_library(destlibdir, destlib, do_compile_only, excludelist): - - alllibname = destlibdir + '/' + destlib + '.lef' - if os.path.isfile(alllibname): - os.remove(alllibname) - - print('Diagnostic: Creating consolidated LEF library ' + destlib + '.lef') - llist = glob.glob(destlibdir + '/*.lef') - if alllibname in llist: - llist.remove(alllibname) - - # Create exclude list with glob-style matching using fnmatch - if len(llist) > 0: - llistnames = list(os.path.split(item)[1] for item in llist) - notllist = [] - for exclude in excludelist: - notllist.extend(fnmatch.filter(llistnames, exclude)) - - # Apply exclude list - if len(notllist) > 0: - for file in llist[:]: - if os.path.split(file)[1] in notllist: - llist.remove(file) - - if len(llist) > 1: - print('New file is: ' + alllibname) - with open(alllibname, 'w') as ofile: - headerdone = False - for lfile in llist: - with open(lfile, 'r') as ifile: - # print('Adding ' + lfile + ' to library.') - ltext = ifile.read() - llines = ltext.splitlines() - headerseen = False - for lline in llines: - if headerdone: - if not headerseen: - if not lline.startswith('MACRO'): - continue - else: - headerseen = True - print(lline, file=ofile) - headerdone = True - print('#--------EOF---------\n', file=ofile) - - if do_compile_only == True: - print('Compile-only: Removing individual LEF files') - for lfile in llist: - if os.path.isfile(lfile): - os.remove(lfile) - if newname: - if os.path.isfile(newname): - os.remove(newname) - else: - print('Only one file (' + str(llist) + '); ignoring "compile" option.') - -#---------------------------------------------------------------------------- -# Given a destination directory holding individual liberty files of a number -# of cells, create a single liberty library file named <alllibname> and place -# it in the same directory. This is done for the option "compile" if specified -# for the "-lib" install. -#---------------------------------------------------------------------------- - -# Warning: This script is unfinished. Needs to parse the library header -# in each cell and generate a new library header combining the contents of -# all cell headers. Also: The library name in the header needs to be -# changed to the full library name. Also: There is no mechanism for -# collecting all files belonging to a single process corner/temperature/ -# voltage. - -def create_lib_library(destlibdir, destlib, do_compile_only, excludelist): - - alllibname = destlibdir + '/' + destlib + '.lib' - if os.path.isfile(alllibname): - os.remove(alllibname) - - print('Diagnostic: Creating consolidated liberty library ' + destlib + '.lib') - - # Create exclude list with glob-style matching using fnmatch - if len(llist) > 0: - llistnames = list(os.path.split(item)[1] for item in llist) - notllist = [] - for exclude in excludelist: - notllist.extend(fnmatch.filter(llistnames, exclude)) - - # Apply exclude list - if len(notllist) > 0: - for file in llist[:]: - if os.path.split(file)[1] in notllist: - llist.remove(file) - - if len(llist) > 1: - print('New file is: ' + alllibname) - with open(alllibname, 'w') as ofile: - headerdone = False - for lfile in llist: - with open(lfile, 'r') as ifile: - # print('Adding ' + lfile + ' to library.') - ltext = ifile.read() - llines = ltext.splitlines() - headerseen = False - for lline in llines: - if headerdone: - if not headerseen: - if not lline.split()[0] == 'cell': - continue - else: - headerseen = True - print(lline, file=ofile) - headerdone = True - print('/*--------EOF---------*/\n', file=ofile) - - if do_compile_only == True: - print('Compile-only: Removing individual LEF files') - for lfile in llist: - if os.path.isfile(lfile): - os.remove(lfile) - if newname: - if os.path.isfile(newname): - os.remove(newname) - else: - print('Only one file (' + str(llist) + '); ignoring "compile" option.') - -#---------------------------------------------------------------------------- -# Given a destination directory holding individual GDS files of a number -# of cells, create a single GDL library file named <alllibname> and place -# it in the same directory. This is done for the option "compile" if specified -# for the "-gds" install. -#---------------------------------------------------------------------------- - -def create_gds_library(destlibdir, destlib, startup_script, do_compile_only, excludelist): - - alllibname = destlibdir + '/' + destlib + '.gds' - if os.path.isfile(alllibname): - os.remove(alllibname) - - print('Diagnostic: Creating consolidated GDS library ' + destlib + '.gds') - glist = glob.glob(destlibdir + '/*.gds') - glist.extend(glob.glob(destlibdir + '/*.gdsii')) - glist.extend(glob.glob(destlibdir + '/*.gds2')) - if alllibname in glist: - glist.remove(alllibname) - - # Create exclude list with glob-style matching using fnmatch - if len(glist) > 0: - glistnames = list(os.path.split(item)[1] for item in glist) - notglist = [] - for exclude in excludelist: - notglist.extend(fnmatch.filter(glistnames, exclude)) - - # Apply exclude list - if len(notglist) > 0: - for file in glist[:]: - if os.path.split(file)[1] in notglist: - glist.remove(file) - - if len(glist) > 1: - print('New file is: ' + alllibname) - - if os.path.isfile(startup_script): - # If the symbolic link exists, remove it. - if os.path.isfile(destlibdir + '/.magicrc'): - os.remove(destlibdir + '/.magicrc') - os.symlink(startup_script, destlibdir + '/.magicrc') - - # A GDS library is binary and requires handling in Magic - print('Creating magic generation script to generate GDS library.') - with open(destlibdir + '/generate_magic.tcl', 'w') as ofile: - print('#!/usr/bin/env wish', file=ofile) - print('#--------------------------------------------', file=ofile) - print('# Script to generate .gds library from files ', file=ofile) - print('#--------------------------------------------', file=ofile) - print('drc off', file=ofile) - print('gds readonly true', file=ofile) - print('gds flatten true', file=ofile) - print('gds rescale false', file=ofile) - print('tech unlock *', file=ofile) - - for gdsfile in glist: - print('gds read ' + gdsfile, file=ofile) - - print('puts stdout "Creating cell ' + destlib + '"', file=ofile) - print('load ' + destlib, file=ofile) - print('puts stdout "Adding cells to library"', file=ofile) - print('box values 0 0 0 0', file=ofile) - for gdsfile in glist: - gdsroot = os.path.split(gdsfile)[1] - gdsname = os.path.splitext(gdsroot)[0] - print('getcell ' + gdsname, file=ofile) - # Could properly make space for the cell here. . . - print('box move e 200', file=ofile) - - print('puts stdout "Writing GDS library ' + destlib + '"', file=ofile) - print('gds library', file=ofile) - print('gds write ' + destlib, file=ofile) - print('puts stdout "Done."', file=ofile) - print('quit -noprompt', file=ofile) - - # Run magic to read in the individual GDS files and - # write out the consolidated GDS library - - print('Running magic to create GDS library.') - sys.stdout.flush() - - mproc = subprocess.run(['magic', '-dnull', '-noconsole', - destlibdir + '/generate_magic.tcl'], - stdin = subprocess.DEVNULL, - stdout = subprocess.PIPE, - stderr = subprocess.PIPE, cwd = destlibdir, - universal_newlines = True) - - if mproc.stdout: - for line in mproc.stdout.splitlines(): - print(line) - if mproc.stderr: - print('Error message output from magic:') - for line in mproc.stderr.splitlines(): - print(line) - if mproc.returncode != 0: - print('ERROR: Magic exited with status ' + str(mproc.returncode)) - if do_compile_only == True: - print('Compile-only: Removing individual GDS files') - for gfile in glist: - if os.path.isfile(gfile): - os.remove(gfile) - if newname: - if os.path.isfile(newname): - os.remove(newname) - else: - print('Only one file (' + str(glist) + '); ignoring "compile" option.') - -#---------------------------------------------------------------------------- -# Given a destination directory holding individual SPICE netlists of a number -# of cells, create a single SPICE library file named <alllibname> and place -# it in the same directory. This is done for the option "compile" if specified -# for the "-spice" install. -#---------------------------------------------------------------------------- - -def create_spice_library(destlibdir, destlib, spiext, do_compile_only, do_stub, excludelist): - - fformat = 'CDL' if spiext == '.cdl' else 'SPICE' - - allstubname = destlibdir + '/stub' + spiext - alllibname = destlibdir + '/' + destlib + spiext - if do_stub: - outputname = allstubname - else: - outputname = alllibname - - print('Diagnostic: Creating consolidated ' + fformat + ' library ' + outputname) - - if os.path.isfile(outputname): - os.remove(outputname) - - if fformat == 'CDL': - slist = glob.glob(destlibdir + '/*.cdl') - else: - # Sadly, there is no consensus on what a SPICE file extension should be. - slist = glob.glob(destlibdir + '/*.spc') - slist.extend(glob.glob(destlibdir + '/*.spice')) - slist.extend(glob.glob(destlibdir + '/*.spi')) - slist.extend(glob.glob(destlibdir + '/*.ckt')) - - if alllibname in slist: - slist.remove(alllibname) - - if allstubname in slist: - slist.remove(allstubname) - - # Create exclude list with glob-style matching using fnmatch - if len(slist) > 0: - slistnames = list(os.path.split(item)[1] for item in slist) - notslist = [] - for exclude in excludelist: - notslist.extend(fnmatch.filter(slistnames, exclude)) - - # Apply exclude list - if len(notslist) > 0: - for file in slist[:]: - if os.path.split(file)[1] in notslist: - slist.remove(file) - - if len(slist) > 1: - with open(outputname, 'w') as ofile: - allsubckts = [] - for sfile in slist: - with open(sfile, 'r') as ifile: - # print('Adding ' + sfile + ' to library.') - stext = ifile.read() - subckts = re.findall(r'\.subckt[ \t]+([^ \t\n]+)', stext, flags=re.IGNORECASE) - sseen = list(item for item in subckts if item in allsubckts) - allsubckts.extend(list(item for item in subckts if item not in allsubckts)) - sfilter = remove_redundant_subckts(stext, allsubckts, sseen) - print(sfilter, file=ofile) - print('\n******* EOF\n', file=ofile) - - if do_compile_only == True: - print('Compile-only: Removing individual SPICE files') - for sfile in slist: - if os.path.isfile(sfile): - os.remove(sfile) - elif os.path.islink(sfile): - os.unlink(sfile) - else: - print('Only one file (' + str(slist) + '); ignoring "compile" option.') - -#---------------------------------------------------------------------------- -# Remove redundant subcircuit entries from a SPICE or CDL netlist file. "sseen" -# is a list of subcircuit names gleaned from all previously read files using -# re.findall(). "slist" is a list of subcircuits including those in "ntext". -# If a subcircuit is defined outside of "ntext", then remove all occurrences in -# "ntext". Otherwise, if a subcircuit is defined more than once in "ntext", -# remove all but one copy. The reason for doing this is that some netlists will -# include primitive device definitions used by all the standard cell subcircuits. -# -# It may be necessary to remove redundant .include statements and redundant .model -# and/or .option statements as well. -#---------------------------------------------------------------------------- - -def remove_redundant_subckts(ntext, slist, sseen): - updated = ntext - for subckt in slist: - if subckt in sseen: - # Remove all occurrences of subckt - updated = re.sub(r'\n\.subckt[ \t]+' + subckt + '[ \t\n]+.*\n\.ends[ \t\n]+', '\n', updated, flags=re.IGNORECASE | re.DOTALL) - - else: - # Determine the number of times the subcircuit appears in the text - n = len(re.findall(r'\n\.subckt[ \t]+' + subckt + '[ \t\n]+.*\n\.ends[ \t\n]+', updated, flags=re.IGNORECASE | re.DOTALL)) - # Optimization: Just keep original text if n < 2 - if n < 2: - continue - - # Remove all but one - updated = re.sub(r'\n\.subckt[ \t]+' + subckt + '[ \t\n]+.*\n\.ends[ \t\n]+', '\n', n - 1, updated, flags=re.IGNORECASE | re.DOTALL) - return updated - -#---------------------------------------------------------------------------- # This is the main entry point for the foundry install script. #----------------------------------------------------------------------------
diff --git a/sky130/magic/sky130.tech b/sky130/magic/sky130.tech index 7f23296..d000ffd 100644 --- a/sky130/magic/sky130.tech +++ b/sky130/magic/sky130.tech
@@ -10,7 +10,7 @@ # Revisions: See below # # This file is an Open Source foundry process describing -# the SkyWater S8 hybrid 0.18um / 0.13um fabrication +# the SkyWater sky130 hybrid 0.18um / 0.13um fabrication # process. The file may be distributed under the terms # of the Apache 2.0 license agreement. #