| #!/usr/bin/env python3 |
| # |
| # sp_to_spice --- |
| # |
| # This script runs as a filter to foundry_install.sh and converts file |
| # names ending with ".sp" to ".spice". If the file has multiple extensions |
| # then all are stripped before adding ".spice". The script is specifically |
| # intended to run on the SRAM macro library ".lvs.sp" files, which include |
| # two parasitic devices that make them compatible with the extracted layout, |
| # but have a few syntactical incompatibilities that can be corrected by |
| # filtering the file during the copy. |
| # |
| # This script is a filter to be run by setting the name of this script as |
| # the value to "filter=" for the model install in the sky130 Makefile. |
| |
| import os |
| import re |
| import sys |
| |
| def filter(inname): |
| |
| filepath = os.path.split(inname)[0] |
| filename = os.path.split(inname)[1] |
| |
| filebits = filename.split('.') |
| newname = filebits[0] + '.spice' |
| outname = os.path.join(filepath, newname) |
| if not os.path.isfile(inname): |
| print('No such file ' + inname) |
| return 1 |
| |
| print('Converting file ' + filename + ' to ' + newname + ' by reversing buses and scaling l and w.') |
| # Read input |
| try: |
| with open(inname, 'r') as inFile: |
| stext = inFile.read() |
| slines = stext.splitlines() |
| except: |
| print('sp_to_spice.py: failed to open ' + inname + ' for reading.', file=sys.stderr) |
| return 1 |
| |
| fixedlines = [] |
| modified = False |
| base_rex = re.compile(r'([^\[]*).*') # regular expression for matching base signal |
| |
| for line in slines: |
| |
| # remove 'u' suffix from 'l' and 'w' parameters |
| newline = re.sub('([ \t][lL]=[0-9\.]*)u', r'\1', line) |
| newline = re.sub('([ \t][wW]=[0-9\.]*)u', r'\1', newline) |
| |
| # reverse bus indices - NOTE: Only works if all ports are on the subckt line |
| if newline.startswith(".subckt ") or newline.startswith(".SUBCKT "): |
| tokens = newline.split() |
| if tokens[1] == filebits[0]: # top subckt |
| bus_start = 2 |
| last_base = base_rex.match(tokens[2]).group(1) |
| i = 3 |
| while i < len(tokens): |
| base_match = base_rex.match(tokens[i]) # always matches one base net |
| if last_base != base_match.group(1): |
| tokens[bus_start:i] = tokens[i-1:bus_start-1:-1] # reverse the bus indices |
| bus_start = i |
| last_base = base_match.group(1) |
| i += 1 |
| tokens[bus_start:i] = tokens[i-1:bus_start-1:-1] |
| newline = " ".join(tokens) |
| |
| if line != newline: |
| modified = True |
| |
| fixedlines.append(newline) |
| |
| # Write output |
| if outname == None: |
| for i in fixedlines: |
| print(i) |
| else: |
| # If the output is a symbolic link but no modifications have been made, |
| # then leave it alone. If it was modified, then remove the symbolic |
| # link before writing. |
| if os.path.islink(outname): |
| if not modified: |
| return 0 |
| else: |
| os.unlink(outname) |
| try: |
| with open(outname, 'w') as outFile: |
| for i in fixedlines: |
| print(i, file=outFile) |
| except: |
| print('sp_to_spice.py: failed to open ' + outname + ' for writing.', file=sys.stderr) |
| return 1 |
| |
| # If the name was changed, remove the original file. |
| if filename != newname: |
| os.remove(inname) |
| |
| if __name__ == '__main__': |
| |
| # This script expects to get one argument, which is the input file. |
| # The script renames the file. |
| |
| 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: |
| infilename = arguments[0] |
| else: |
| sys.exit(1) |
| |
| result = filter(infilename) |
| sys.exit(result) |