blob: dedad2467fa334af04f8fc7fbdfd22ef0ec84540 [file] [log] [blame]
Tim Edwards9d3debb2020-10-20 20:52:18 -04001#!/usr/bin/env python3
Tim Edwards7f052a62020-07-28 11:07:26 -04002#
3# split_spice.py --
4#
5# Script that reads the SPICE output from the convert_spectre.py script,
6# which typically has parsed through files containing inline subcircuits
7# and recast them as normal SPICE .subckt ... .ends entries, and pulled
8# any model blocks inside the inline subckt out. This script removes
9# each .subckt ... .ends block from every file and moves it to its own
10# file named <subckt_name>.spice.
11#
12# The arguments are <path_to_input> and <path_to_output>. If there is
13# only one argument, or if <path_to_input> is equal to <path_to_output>,
14# then the new .spice files are added to the directory and the model
15# files are modified in place. Otherwise, all modified files are placed
16# in <path_to_output>.
17
18import os
19import sys
20import re
21import glob
22
23def usage():
24 print('split_spice.py <path_to_input> <path_to_output>')
25
26def convert_file(in_file, out_path, out_file):
27
28 # Regexp patterns
29 paramrex = re.compile('\.param[ \t]+(.*)')
30 subrex = re.compile('\.subckt[ \t]+([^ \t]+)[ \t]+([^ \t]*)')
31 modelrex = re.compile('\.model[ \t]+([^ \t]+)[ \t]+([^ \t]+)[ \t]+(.*)')
32 endsubrex = re.compile('\.ends[ \t]+(.+)')
33 increx = re.compile('\.include[ \t]+')
34
35 with open(in_file, 'r') as ifile:
36 inplines = ifile.read().splitlines()
37
38 insubckt = False
39 inparam = False
40 inmodel = False
41 inpinlist = False
42 spicelines = []
43 subcktlines = []
44 savematch = None
45 subname = ''
46 modname = ''
47 modtype = ''
48
49 for line in inplines:
50
51 # Item 1. Handle comment lines
52 if line.startswith('*'):
53 if subcktlines != []:
54 subcktlines.append(line.strip())
55 else:
56 spicelines.append(line.strip())
57 continue
58
59 # Item 2. Flag continuation lines
60 if line.startswith('+'):
61 contline = True
62 else:
63 contline = False
64 if inparam:
Tim Edwardsa4219782020-08-09 21:13:58 -040065 inparam = False
Tim Edwards7f052a62020-07-28 11:07:26 -040066 if inpinlist:
Tim Edwardsa4219782020-08-09 21:13:58 -040067 inpinlist = False
Tim Edwards7f052a62020-07-28 11:07:26 -040068
Tim Edwards4eb5c022020-07-28 11:46:53 -040069 # Item 3. Handle blank lines like comment lines
70 if line.strip() == '':
Tim Edwards4eb5c022020-07-28 11:46:53 -040071 if subcktlines != []:
72 subcktlines.append(line)
73 else:
74 spicelines.append(line)
75 continue
76
77 # Item 4. Handle continuation lines
Tim Edwards7f052a62020-07-28 11:07:26 -040078 if contline:
79 if inparam:
80 # Continue handling parameters
81 if subcktlines != []:
82 subcktlines.append(line)
83 else:
84 spicelines.append(line)
85 continue
86
Tim Edwards4eb5c022020-07-28 11:46:53 -040087 # Item 5. Regexp matching
Tim Edwards7f052a62020-07-28 11:07:26 -040088
89 # If inside a subcircuit, remove "parameters". If outside,
90 # change it to ".param"
91 pmatch = paramrex.match(line)
92 if pmatch:
93 inparam = True
94 if insubckt:
95 subcktlines.append(line)
96 else:
97 spicelines.append(line)
98 continue
Tim Edwardsa4219782020-08-09 21:13:58 -040099
Tim Edwards7f052a62020-07-28 11:07:26 -0400100 # model
101 mmatch = modelrex.match(line)
102 if mmatch:
103 modellines = []
104 modname = mmatch.group(1)
105 modtype = mmatch.group(2)
106
107 spicelines.append(line)
108 inmodel = 2
109 continue
110
111 if not insubckt:
112 # Things to parse if not in a subcircuit
113
114 imatch = subrex.match(line)
115 if imatch:
116 insubckt = True
117 subname = imatch.group(1)
118 devrex = re.compile(subname + '[ \t]*([^ \t]+)[ \t]*([^ \t]+)[ \t]*(.*)', re.IGNORECASE)
119 inpinlist = True
120 subcktlines.append(line)
121 continue
122
123 else:
124 # Things to parse when inside of a ".subckt" block
125
126 if inpinlist:
127 # Watch for pin list continuation line.
128 subcktlines.append(line)
129 continue
130
131 else:
132 ematch = endsubrex.match(line)
133 if ematch:
134 if ematch.group(1) != subname:
135 print('Error: "ends" name does not match "subckt" name!')
136 print('"ends" name = ' + ematch.group(1))
137 print('"subckt" name = ' + subname)
138
139 subcktlines.append(line)
140
141 # Dump the contents of subcktlines into a file
142 subckt_file = subname + '.spice'
143 with open(out_path + '/' + subckt_file, 'w') as ofile:
144 print('* Subcircuit definition of cell ' + subname, file=ofile)
145 for line in subcktlines:
146 print(line, file=ofile)
147 subcktlines = []
148
Tim Edwards44918582020-08-09 16:17:55 -0400149 # Add an include statement to this file in the source
150 spicelines.append('.include ' + subckt_file)
151
Tim Edwards7f052a62020-07-28 11:07:26 -0400152 insubckt = False
153 inmodel = False
154 subname = ''
155 continue
156
157 # Copy line as-is
158 if insubckt:
159 subcktlines.append(line)
160 else:
161 spicelines.append(line)
162
163 # Output the result to out_file.
164 with open(out_path + '/' + out_file, 'w') as ofile:
165 for line in spicelines:
166 print(line, file=ofile)
167
168if __name__ == '__main__':
169 debug = False
170
171 if len(sys.argv) == 1:
172 print("No options given to split_spice.py.")
173 usage()
174 sys.exit(0)
175
176 optionlist = []
177 arguments = []
178
179 for option in sys.argv[1:]:
180 if option.find('-', 0) == 0:
181 optionlist.append(option)
182 else:
183 arguments.append(option)
184
185 if len(arguments) != 2:
186 print("Wrong number of arguments given to split_spice.py.")
187 usage()
188 sys.exit(0)
189
190 if '-debug' in optionlist:
191 debug = True
192
193 inpath = arguments[0]
194 outpath = arguments[1]
195 do_one_file = False
196
197 if not os.path.exists(inpath):
198 print('No such source directory ' + inpath)
199 sys.exit(1)
200
201 if os.path.isfile(inpath):
202 do_one_file = True
203
204 if do_one_file:
205 if os.path.exists(outpath):
206 print('Error: File ' + outpath + ' exists.')
207 sys.exit(1)
208 convert_file(inpath, outpath)
209
210 else:
211 if not os.path.exists(outpath):
212 os.makedirs(outpath)
213
214 infilelist = glob.glob(inpath + '/*')
215
216 for filename in infilelist:
217 fileext = os.path.splitext(filename)[1]
218
219 # Ignore verilog or verilog-A files that might be in a model directory
220 if fileext == '.v' or fileext == '.va':
221 continue
222
223 froot = os.path.split(filename)[1]
224 convert_file(filename, outpath, froot)
225
226 print('Done.')
227 exit(0)