blob: 90d4e65a3654a223385c12c17838bd96a307129c [file] [log] [blame]
Tim Edwards51f81422020-07-26 12:49:48 -04001#!/usr/bin/env python3
2#
3# create_spice_library.py
4#
5#----------------------------------------------------------------------------
6# Given a destination directory holding individual SPICE netlists of a number
7# of cells, create a single SPICE library file named <alllibname> and place
8# it in the same directory. This is done for the option "compile" if specified
9# for the "-spice" install.
10#----------------------------------------------------------------------------
11
12import sys
13import os
14import re
15import glob
16import fnmatch
17
18#----------------------------------------------------------------------------
19
20def usage():
21 print('')
22 print('Usage:')
23 print(' create_spice_library <destlibdir> <destlib> <spiext>')
24 print(' [-compile-only] [-stub] [-excludelist="file1,file2,..."]')
25 print('')
26 print('Create a single SPICE or CDL library from a set of individual files.')
27 print('')
28 print('where:')
29 print(' <destlibdir> is the directory containing the individual files')
30 print(' <destlib> is the root name of the library file')
31 print(' <spiext> is the extension used (with ".") by the SPICE or CDL files')
32 print(' -compile-only remove the indidual files if specified')
33 print(' -stub generate only .subckt ... .ends for each cell')
34 print(' -excludelist= is a comma-separated list of files to ignore')
35 print('')
36
37#----------------------------------------------------------------------------
38
Tim Edwards9be4ac22020-07-26 12:59:30 -040039def create_spice_library(destlibdir, destlib, spiext, do_compile_only=False, do_stub=False, excludelist=[]):
Tim Edwards51f81422020-07-26 12:49:48 -040040
Tim Edwards05e66eb2020-09-24 13:11:59 -040041 # destlib should not have a file extension
42 destlibroot = os.path.splitext(destlib)[0]
43
Tim Edwards51f81422020-07-26 12:49:48 -040044 fformat = 'CDL' if spiext == '.cdl' else 'SPICE'
45
46 allstubname = destlibdir + '/stub' + spiext
Tim Edwards05e66eb2020-09-24 13:11:59 -040047 alllibname = destlibdir + '/' + destlibroot + spiext
Tim Edwards51f81422020-07-26 12:49:48 -040048 if do_stub:
49 outputname = allstubname
50 else:
51 outputname = alllibname
52
53 print('Diagnostic: Creating consolidated ' + fformat + ' library ' + outputname)
54
55 if os.path.isfile(outputname):
56 os.remove(outputname)
57
Tim Edwards995c1332020-09-25 15:33:58 -040058 # If file "filelist.txt" exists in the directory, get the list of files from it
59 if os.path.exists(destlibdir + '/filelist.txt'):
60 with open(destlibdir + '/filelist.txt', 'r') as ifile:
61 rlist = ifile.read().splitlines()
62 slist = []
63 for rfile in rlist:
64 slist.append(destlibdir + '/' + rfile)
Tim Edwards51f81422020-07-26 12:49:48 -040065 else:
Tim Edwards995c1332020-09-25 15:33:58 -040066 if fformat == 'CDL':
67 slist = glob.glob(destlibdir + '/*.cdl')
68 else:
69 # Sadly, there is no consensus on what a SPICE file extension should be.
70 slist = glob.glob(destlibdir + '/*.spc')
71 slist.extend(glob.glob(destlibdir + '/*.spice'))
72 slist.extend(glob.glob(destlibdir + '/*.spi'))
73 slist.extend(glob.glob(destlibdir + '/*.ckt'))
74 slist.extend(glob.glob(destlibdir + '/*.cir'))
75 slist.extend(glob.glob(destlibdir + '/*' + spiext))
Tim Edwards51f81422020-07-26 12:49:48 -040076
77 if alllibname in slist:
78 slist.remove(alllibname)
79
80 if allstubname in slist:
81 slist.remove(allstubname)
82
83 # Create exclude list with glob-style matching using fnmatch
84 if len(slist) > 0:
85 slistnames = list(os.path.split(item)[1] for item in slist)
86 notslist = []
87 for exclude in excludelist:
88 notslist.extend(fnmatch.filter(slistnames, exclude))
89
90 # Apply exclude list
91 if len(notslist) > 0:
92 for file in slist[:]:
93 if os.path.split(file)[1] in notslist:
94 slist.remove(file)
95
96 if len(slist) > 1:
97 with open(outputname, 'w') as ofile:
98 allsubckts = []
99 for sfile in slist:
100 with open(sfile, 'r') as ifile:
101 # print('Adding ' + sfile + ' to library.')
102 stext = ifile.read()
103 subckts = re.findall(r'\.subckt[ \t]+([^ \t\n]+)', stext, flags=re.IGNORECASE)
104 sseen = list(item for item in subckts if item in allsubckts)
105 allsubckts.extend(list(item for item in subckts if item not in allsubckts))
106 sfilter = remove_redundant_subckts(stext, allsubckts, sseen)
107 print(sfilter, file=ofile)
108 print('\n******* EOF\n', file=ofile)
109
110 if do_compile_only == True:
111 print('Compile-only: Removing individual SPICE files')
112 for sfile in slist:
113 if os.path.isfile(sfile):
114 os.remove(sfile)
115 elif os.path.islink(sfile):
116 os.unlink(sfile)
117 else:
118 print('Only one file (' + str(slist) + '); ignoring "compile" option.')
119
120#----------------------------------------------------------------------------
121# Remove redundant subcircuit entries from a SPICE or CDL netlist file. "sseen"
122# is a list of subcircuit names gleaned from all previously read files using
123# re.findall(). "slist" is a list of subcircuits including those in "ntext".
124# If a subcircuit is defined outside of "ntext", then remove all occurrences in
125# "ntext". Otherwise, if a subcircuit is defined more than once in "ntext",
126# remove all but one copy. The reason for doing this is that some netlists will
127# include primitive device definitions used by all the standard cell subcircuits.
128#
129# It may be necessary to remove redundant .include statements and redundant .model
130# and/or .option statements as well.
131#----------------------------------------------------------------------------
132
133def remove_redundant_subckts(ntext, slist, sseen):
134 updated = ntext
135 for subckt in slist:
136 if subckt in sseen:
137 # Remove all occurrences of subckt
138 updated = re.sub(r'\n\.subckt[ \t]+' + subckt + '[ \t\n]+.*\n\.ends[ \t\n]+', '\n', updated, flags=re.IGNORECASE | re.DOTALL)
139
140 else:
141 # Determine the number of times the subcircuit appears in the text
142 n = len(re.findall(r'\n\.subckt[ \t]+' + subckt + '[ \t\n]+.*\n\.ends[ \t\n]+', updated, flags=re.IGNORECASE | re.DOTALL))
143 # Optimization: Just keep original text if n < 2
144 if n < 2:
145 continue
146
147 # Remove all but one
148 updated = re.sub(r'\n\.subckt[ \t]+' + subckt + '[ \t\n]+.*\n\.ends[ \t\n]+', '\n', n - 1, updated, flags=re.IGNORECASE | re.DOTALL)
149 return updated
150
151#----------------------------------------------------------------------------
152
153if __name__ == '__main__':
154
155 if len(sys.argv) == 1:
156 usage()
157 sys.exit(0)
158
159 argumentlist = []
160
161 # Defaults
162 do_compile_only = False
163 do_stub = False
164 excludelist = []
165
166 # Break arguments into groups where the first word begins with "-".
167 # All following words not beginning with "-" are appended to the
168 # same list (optionlist). Then each optionlist is processed.
169 # Note that the first entry in optionlist has the '-' removed.
170
171 for option in sys.argv[1:]:
172 if option.find('-', 0) == 0:
173 keyval = option[1:].split('=')
174 if keyval[0] == 'compile-only':
175 if len(keyval) > 0:
176 if keyval[1].tolower() == 'true' or keyval[1].tolower() == 'yes' or keyval[1] == '1':
177 do_compile_only = True
178 else:
179 do_compile_only = True
180 elif keyval[1] == 'exclude' or key == 'excludelist':
181 if len(keyval) > 0:
182 excludelist = keyval[1].trim('"').split(',')
183 else:
184 print("No items in exclude list (ignoring).")
185 elif keyval[1] == 'stub':
186 if len(keyval) > 0:
187 if keyval[1].tolower() == 'true' or keyval[1].tolower() == 'yes' or keyval[1] == '1':
188 do_stub = True
189 else:
190 do_stub = True
191 else:
192 print("Unknown option '" + keyval[0] + "' (ignoring).")
193 else:
194 argumentlist.append(option)
195
196 if len(argumentlist) < 3:
197 print("Not enough arguments given to create_spice_library.py.")
198 usage()
199 sys.exit(1)
200
201 destlibdir = argumentlist[0]
202 destlib = argumentlist[1]
203 startup_script = argumentlist[2]
204
205 print('')
206 if spiext == '.cdl':
207 print('Create CDL library from files:')
208 else:
209 print('Create SPICE library from files:')
210 print('')
211 print('Path to files: ' + destlibdir)
212 print('Name of compiled library: ' + destlib + spiext)
213 print('Remove individual files: ' + 'Yes' if do_compile_only else 'No')
214 if len(excludelist) > 0:
215 print('List of files to exclude: ')
216 for file in excludelist:
217 print(file)
218 print('')
219
220 create_spice_library(destlibdir, destlib, spiext, do_compile_only, do_stub, excludelist)
221 print('Done.')
222 sys.exit(0)
223
224#----------------------------------------------------------------------------