Added additional option "sort" to supply a sorting script to specify the order of files when compiled into a library; this allows the Makefile to enforce natural sort order and/or put dependent entries at the top. Also added a custom script for sky130 to handle the assortment of "include" statements in the standard cell verilog before generating the library files.
diff --git a/README b/README index 5d63be4..fce389d 100644 --- a/README +++ b/README
@@ -382,7 +382,7 @@ [option_arguments] may be one of the following: - up <number> + up=<number> Any tool option can use this argument to indicate that the source hierarchy should be copied entirely, starting from <number> levels above the files indicated by <path>. @@ -394,7 +394,7 @@ would install all .lib files directly into libs.ref/<libname>/liberty/*.lib while - -liberty x/y/z/PVT_*/*.lib up 1 + -liberty x/y/z/PVT_*/*.lib up=1 would install all .lib files into libs.ref/liberty/<libname>/PVT_*/*.lib. @@ -425,13 +425,13 @@ them in a separate root directory libs.priv where they can be given additional read/write restrictions. - rename <file_name> + rename=<file_name> Rename the file being copied to the name of the argument. This can be used to copy one file into multiple destination libraries and give each copy a name related to the destination library. - filter <script_file_path> + filter=<script_file_path> Process all files through the script <script_file_path>, which is given as a relative path to the directory containing the Makefile. The filter script traditionally @@ -442,6 +442,13 @@ in the common/ directory. See common/fixspice.py for an example. + sort=<script_file_path> + Generate a list of all files to combine in a library, + called "filelist.txt", then call the script. The + script should read the "filelist.txt" file and sort + the files according to the order in which they should + appear in the library. + noclobber Mainly diagnostic. When specified, any temporary files used during installation will be retained instead of
diff --git a/VERSION b/VERSION index 7cb055c..4ad595c 100644 --- a/VERSION +++ b/VERSION
@@ -1 +1 @@ -1.0.41 +1.0.42
diff --git a/common/create_gds_library.py b/common/create_gds_library.py index 34424fb..b02535f 100755 --- a/common/create_gds_library.py +++ b/common/create_gds_library.py
@@ -45,9 +45,18 @@ 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 file "filelist.txt" exists in the directory, get the list of files from it + if os.path.exists(destlibdir + '/filelist.txt'): + with open(destlibdir + '/filelist.txt', 'r') as ifile: + rlist = ifile.read().splitlines() + glist = [] + for rfile in rlist: + glist.append(destlibdir + '/' + rfile) + else: + glist = glob.glob(destlibdir + '/*.gds') + glist.extend(glob.glob(destlibdir + '/*.gdsii')) + glist.extend(glob.glob(destlibdir + '/*.gds2')) + if alllibname in glist: glist.remove(alllibname)
diff --git a/common/create_lef_library.py b/common/create_lef_library.py index 8a922c8..4a46e5f 100755 --- a/common/create_lef_library.py +++ b/common/create_lef_library.py
@@ -43,7 +43,17 @@ os.remove(alllibname) print('Diagnostic: Creating consolidated LEF library ' + destlibroot + '.lef') - llist = glob.glob(destlibdir + '/*.lef') + + # If file "filelist.txt" exists in the directory, get the list of files from it + if os.path.exists(destlibdir + '/filelist.txt'): + with open(destlibdir + '/filelist.txt', 'r') as ifile: + rlist = ifile.read().splitlines() + llist = [] + for rfile in rlist: + llist.append(destlibdir + '/' + rfile) + else: + llist = glob.glob(destlibdir + '/*.lef') + if alllibname in llist: llist.remove(alllibname)
diff --git a/common/create_lib_library.py b/common/create_lib_library.py index bfa773d..1f058c7 100755 --- a/common/create_lib_library.py +++ b/common/create_lib_library.py
@@ -51,6 +51,16 @@ print('Diagnostic: Creating consolidated liberty library ' + destlibroot + '.lib') + # If file "filelist.txt" exists in the directory, get the list of files from it + if os.path.exists(destlibdir + '/filelist.txt'): + with open(destlibdir + '/filelist.txt', 'r') as ifile: + rlist = ifile.read().splitlines() + llist = [] + for rfile in rlist: + llist.append(destlibdir + '/' + rfile) + else: + llist = glob.glob(destlibdir + '/*.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)
diff --git a/common/create_spice_library.py b/common/create_spice_library.py index ca95fa6..90d4e65 100755 --- a/common/create_spice_library.py +++ b/common/create_spice_library.py
@@ -55,16 +55,24 @@ if os.path.isfile(outputname): os.remove(outputname) - if fformat == 'CDL': - slist = glob.glob(destlibdir + '/*.cdl') + # If file "filelist.txt" exists in the directory, get the list of files from it + if os.path.exists(destlibdir + '/filelist.txt'): + with open(destlibdir + '/filelist.txt', 'r') as ifile: + rlist = ifile.read().splitlines() + slist = [] + for rfile in rlist: + slist.append(destlibdir + '/' + rfile) 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 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)
diff --git a/common/create_verilog_library.py b/common/create_verilog_library.py index 32ce2c5..55e1c76 100755 --- a/common/create_verilog_library.py +++ b/common/create_verilog_library.py
@@ -45,7 +45,18 @@ os.remove(alllibname) print('Diagnostic: Creating consolidated verilog library ' + destlibroot + '.v') - vlist = glob.glob(destlibdir + '/*.v') + + # If file "filelist.txt" exists in the directory, get the list of files from it + if os.path.exists(destlibdir + '/filelist.txt'): + print('Diagnostic: Reading sorted verilog file list.') + with open(destlibdir + '/filelist.txt', 'r') as ifile: + rlist = ifile.read().splitlines() + vlist = [] + for rfile in rlist: + vlist.append(destlibdir + '/' + rfile) + else: + vlist = glob.glob(destlibdir + '/*.v') + if alllibname in vlist: vlist.remove(alllibname)
diff --git a/common/foundry_install.py b/common/foundry_install.py index 7028956..3fa7f1c 100755 --- a/common/foundry_install.py +++ b/common/foundry_install.py
@@ -79,6 +79,10 @@ # when a foundry library has inconveniently split # an IP library (LEF, CDL, verilog, etc.) into # individual files. +# compile-only: Like "compile" except that the individual +# files are removed after the library file has been +# created. +# # stub : Remove contents of subcircuits from CDL or SPICE # netlist files. # @@ -98,6 +102,20 @@ # "compile" or "compile-only", this refers to the # name of the target compiled file. # +# filter: Followed by "=" and the name of a script. +# Each file is passed through the filter script +# before writing into the staging area. +# +# sort: Optionally followed by "=" and the name of a script. +# The list of files to process (after applying items +# from "exclude") will be written to a file +# "filelist.txt", which will be used by the +# library compile routines, if present. If a script +# name is specified, then the sort script will rewrite +# the file with the order in which entries should +# appear in the compiled library. Only useful when +# used with "compile" or "compile-only". +# # noconvert : Install only; do not attempt to convert to other # formats (applies only to GDS, CDL, and LEF). # @@ -669,16 +687,11 @@ # the source should be copied (or linked) from <number> levels up # in the hierarchy (see below). - if 'up' in option: - uparg = option.index('up') - try: - hier_up = int(option[uparg + 1]) - except: - print("Non-numeric option to 'up': " + option[uparg + 1]) - print("Ignoring 'up' option.") - hier_up = 0 - else: - hier_up = 0 + hier_up = 0 + for item in option: + if item.split('=')[0] == 'up': + hier_up = int(item.split('=')[1]) + break filter_scripts = [] for item in option: @@ -713,6 +726,17 @@ else: print('Renaming file to: ' + newname) + # Option 'sort' may have an argument. . . + try: + sortscript = list(item.split('=')[1] for item in option if item.startswith('sort'))[0] + except IndexError: + sortscript = None + else: + print('Sorting files with script ' + sortscript) + # . . . or not. + if 'sort' in option: + sortscript = True + # 'anno' may be specified for LEF, in which case the LEF is used only # to annotate GDS and is not itself installed; this allows LEF to # be generated from Magic and avoids quirky use of obstruction layers. @@ -791,6 +815,7 @@ print(' ' + item) print('(' + str(len(liblist)) + ' files total)') + destfilelist = [] for libname in liblist: # Note that there may be a hierarchy to the files in option[1], # say for liberty timing files under different conditions, so @@ -877,6 +902,19 @@ # Apply filter script to all files in the target directory tfilter(targname, filter_script) + destfilelist.append(os.path.split(targname)[1]) + + if sortscript: + print('Diagnostic: Sorting files to compile.') + with open(destlibdir + '/filelist.txt', 'w') as ofile: + for destfile in destfilelist: + print(destfile, file=ofile) + if os.path.isfile(sortscript): + print('Diagnostic: Sorting files with ' + sortscript) + subprocess.run([sortscript, destlibdir], + stdout = subprocess.DEVNULL, + stderr = subprocess.DEVNULL) + if do_compile == True or do_compile_only == True: # NOTE: The purpose of "rename" is to put a destlib-named # library elsewhere so that it can be merged with another @@ -946,6 +984,11 @@ targrename = destlibdir + destpath + '/' + newname if os.path.isfile(origname): os.rename(origname, targrename) + + # If "filelist.txt" was created, remove it + if sortscript: + if os.path.isfile(destlibdir + '/filelist.txt'): + os.remove(destlibdir + '/filelist.txt') # Find any libraries/options marked as "privileged" (or "private") and # move the files from libs.tech or libs.ref to libs.priv, leaving a
diff --git a/sky130/Makefile.in b/sky130/Makefile.in index 2cfa21e..88ad096 100644 --- a/sky130/Makefile.in +++ b/sky130/Makefile.in
@@ -353,7 +353,8 @@ vendor-a: # Install device subcircuits from vendor files ${STAGE} -source ${SKYWATER_PATH} -target ${STAGING_PATH}/${SKY130A} \ - -ngspice sky130_fd_pr/latest/models/* filter=custom/scripts/rename_models.py \ + -ngspice sky130_fd_pr/latest/models/* \ + filter=custom/scripts/rename_models.py \ |& tee -a ${SKY130A}_install.log # Install base device library from vendor files ${STAGE} -source ${SKYWATER_PATH} -target ${STAGING_PATH}/${SKY130A} \ @@ -380,13 +381,22 @@ ${STAGE} -source ${SKYWATER_PATH} -target ${STAGING_PATH}/${SKY130A} \ -techlef %l/latest/tech/*.tlef \ -spice %l/latest/cells/*/*.spice compile-only \ + sort=custom/scripts/sort_pdkfiles.py \ -cdl %l/latest/cells/*/*.cdl ignore=topography compile-only \ + sort=custom/scripts/sort_pdkfiles.py \ -lef %l/latest/cells/*/*.magic.lef compile-only \ + sort=custom/scripts/sort_pdkfiles.py \ -doc %l/latest/cells/*/*.pdf \ -lib %l/latest/timing/*.lib \ -gds %l/latest/cells/*/*.gds compile-only \ - -verilog %l/latest/models/*/*.v exclude *.*.v compile-only rename test \ - -verilog %l/latest/cells/*/*.v exclude *.*.v,test,test.v compile-only \ + sort=custom/scripts/sort_pdkfiles.py \ + -verilog %l/latest/models/*/*.v exclude=*.*.v compile-only \ + rename=primitives filter=custom/scripts/inc_verilog.py \ + sort=custom/scripts/sort_pdkfiles.py \ + -verilog %l/latest/cells/*/*.*.v \ + -verilog %l/latest/cells/*/*.v exclude=*.*.v,primitives.v \ + compile-only filter=custom/scripts/inc_verilog.py \ + sort=custom/scripts/sort_pdkfiles.py \ -library digital sky130_fd_sc_hd \ -library digital sky130_fd_sc_hdll \ -library digital sky130_fd_sc_hvl \ @@ -394,6 +404,15 @@ -library digital sky130_fd_sc_ls \ -library digital sky130_fd_sc_ms \ -library digital sky130_fd_sc_lp |& tee -a ${SKY130A}_install.log + # Remove the base verilog files which have already been included into + # the libraries + ${RM} ${STAGING_PATH}/${SKY130A}/libs.ref/sky130_fd_sc_hd/verilog/*.*.v + ${RM} ${STAGING_PATH}/${SKY130A}/libs.ref/sky130_fd_sc_hdll/verilog/*.*.v + ${RM} ${STAGING_PATH}/${SKY130A}/libs.ref/sky130_fd_sc_hvl/verilog/*.*.v + ${RM} ${STAGING_PATH}/${SKY130A}/libs.ref/sky130_fd_sc_hs/verilog/*.*.v + ${RM} ${STAGING_PATH}/${SKY130A}/libs.ref/sky130_fd_sc_ms/verilog/*.*.v + ${RM} ${STAGING_PATH}/${SKY130A}/libs.ref/sky130_fd_sc_ls/verilog/*.*.v + ${RM} ${STAGING_PATH}/${SKY130A}/libs.ref/sky130_fd_sc_lp/verilog/*.*.v # Install OSU digital standard cells. # ${STAGE} -source ${OSU_PATH} -target ${STAGING_PATH}/${SKY130A} \ # -techlef char/techfiles/scs8.lef rename sky130_osu_sc.tlef \
diff --git a/sky130/custom/scripts/inc_verilog.py b/sky130/custom/scripts/inc_verilog.py index d34129d..2bcd0a3 100755 --- a/sky130/custom/scripts/inc_verilog.py +++ b/sky130/custom/scripts/inc_verilog.py
@@ -47,11 +47,23 @@ # NOTE: These files are assumed not to need in-line # includes, but includes of primitives need to be ignored. + # Quick hack: Remove this when the filenames are corrected + if not os.path.exists(inpath + '/' + incfilename): + print('Detected incorrect filename') + print(' Old filename was: ' + incfilename) + dlist = incfilename.split('.') + ilist = dlist[0:-3] + ilist.append(dlist[-2]) + ilist.append(dlist[-3]) + ilist.append(dlist[-1]) + print(' New filename is: ' + incfilename) + incfilename = '.'.join(ilist) + with open(inpath + '/' + incfilename, 'r') as incfile: v2text = incfile.read() v2lines = v2text.splitlines() for line2 in v2lines: - i2match = imatch.match(line2) + i2match = increx.match(line2) if not i2match: fixedlines.append(line2) else:
diff --git a/sky130/custom/scripts/sort_pdkfiles.py b/sky130/custom/scripts/sort_pdkfiles.py new file mode 100755 index 0000000..ac0a107 --- /dev/null +++ b/sky130/custom/scripts/sort_pdkfiles.py
@@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +# +# sort_pdkfiles.py +# +# Read "filelist.txt" which is a list of all files to be compiled. +# Do a natural sort, which puts the "base" files (those without a +# drive strength) in front of "top level" files (those with a drive +# strength), and orders the drive strengths numerically. +# +# Note that foundry_install.py executes this script using 'subprocess' +# in the directory where "filelist.txt" and the files are located. No +# path components are in the file list. + +import re +import os +import sys + +# Natural sort thanks to Mark Byers in StackOverflow +def natural_sort(l): + convert = lambda text: int(text) if text.isdigit() else text.lower() + alphanum_key= lambda key: [ convert(c) for c in re.split('([0-9]+)', key) ] + return sorted(l, key = alphanum_key) + +def pdk_sort(destdir): + if not os.path.isfile(destdir + '/filelist.txt'): + print('No file "filelist.txt" in ' + destdir + '. . . Nothing to sort.') + sys.exit() + + with open(destdir + '/filelist.txt', 'r') as ifile: + vlist = ifile.read().splitlines() + + vlist = natural_sort(vlist) + + with open(destdir + '/filelist.txt', 'w') as ofile: + for vfile in vlist: + print(vfile, file=ofile) + +if __name__ == '__main__': + + # This script expects to get one argument, which is a path name + + options = [] + arguments = [] + for item in sys.argv[1:]: + if item.find('-', 0) == 0: + options.append(item[1:]) + else: + arguments.append(item) + + if len(arguments) > 0: + destdir = arguments[0] + + pdk_sort(destdir) + sys.exit(0) +