blob: 6d81084fcaac7feaeb772d10f8f8d2cb5955de11 [file] [log] [blame]
Tim Edwardsd7c3a522022-10-01 17:58:29 -04001#!/usr/bin/env python3
2#
3#
4import os
5import re
6import sys
7import subprocess
8
9#---------------------------------------------------------------------------
10# usage: run_all.py [-nosim] [-keep]
11#
12# Run ngspice simulations on all major transistor devices in the process
13# (excluding high-voltage > 3V devices) at all corners, and generate
14# IRSIM parameter files for each corner.
15#
16# The "-nosim" option assumes that simulation output files have been
17# saved, and will run the parser to generate the parameterf files from
18# the existing ngspice output files.
19#
20# The "-keep" option will retain the ngspice input and output files
21# after each parameter file has been generated. Otherwise, they will
22# be removed.
23#---------------------------------------------------------------------------
24
25#---------------------------------------------------------------------------
26# Devices must be paired P and N for each test. There are some redundant
27# devices below where one type exists that does not have a corresponding
28# device in the opposite type. In cases of redundancy, the first results
29# computed will be the ones used for that device. The third item in each
30# list is the voltage range to use for the device, and determines which
31# voltages are used for max/min/typ simulations.
32#---------------------------------------------------------------------------
33
34#---------------------------------------------------------------------------
35# NOTE: This method requires IRSIM 9.7.114, which supports multiple
36# transistor device parameters and multiple supply voltages.
37#---------------------------------------------------------------------------
38
39#---------------------------------------------------------------------------
40# To do: Speed this up by using multiprocessing
41#---------------------------------------------------------------------------
42
43# Parse options
44
45keep = False
46nosim = False
47
48options = []
49arguments = []
50for item in sys.argv[1:]:
51 if item.find('-', 0) == 0:
52 options.append(item)
53 else:
54 arguments.append(item)
55
56if len(arguments) > 0:
57 print("Usage: run_all.py [-nosim] [-keep]")
58 sys.exit(1)
59
60if '-keep' in options:
61 keep = True
62 print("Keep mode: Retaining all intermediate files.")
63
64if '-nosim' in options:
65 nosim = True
66 print("No-sim mode: Not running any simulations.")
67
68if '-help' in options:
69 print("Usage: run_all.py [-nosim] [-keep]")
70 sys.exit(0)
71
72#---------------------------------------------------------------------------
73# Each entry in "devices" list has 9 items:
74# [test-text, pFET-name, nFET-name, voltage-range,
75# p-length, p-width, n-length, n-width, load-cap]
76#---------------------------------------------------------------------------
77
78devices = [
79 [
80 "1.8V devices",
81 "sky130_fd_pr__pfet_01v8", "sky130_fd_pr__nfet_01v8",
82 "1v8", 0.15, 1.0, 0.15, 1.0, 250
83 ],
84 [
85 "1.8V LVT devices",
86 "sky130_fd_pr__pfet_01v8_lvt", "sky130_fd_pr__nfet_01v8_lvt",
87 "1v8", 0.35, 1.0, 0.15, 1.0, 250
88 ],
89 [
90 "1.8V HVT pFET",
91 "sky130_fd_pr__pfet_01v8_hvt", "sky130_fd_pr__nfet_01v8",
92 "1v8", 0.45, 1.0, 0.15, 1.0, 250
93 ],
94 [
95 "SRAM latching FETs",
96 "sky130_fd_pr__special_pfet_pass", "sky130_fd_pr__special_nfet_latch",
97 "1v8", 0.15, 0.14, 0.15, 0.21, 100
98 ],
99 [
100 "SRAM pass nFET",
101 "sky130_fd_pr__special_pfet_pass", "sky130_fd_pr__special_nfet_pass",
102 "1v8", 0.15, 0.14, 0.15, 0.14, 100
103 ],
104 [
105 "3.3V devices",
106 "sky130_fd_pr__pfet_g5v0d10v5", "sky130_fd_pr__nfet_g5v0d10v5",
107 "3v3", 0.50, 1.0, 0.50, 1.0, 250
108 ],
109 [
110 "5.0V native nFET",
111 "sky130_fd_pr__pfet_g5v0d10v5", "sky130_fd_pr__nfet_05v0_nvt",
112 "3v3", 0.50, 1.0, 0.90, 1.0, 250
113 ]
114]
115
116voltages1v8 = [ 1.62, 1.80, 1.98 ]
117
118voltages3v3 = [ 2.97, 3.30, 3.63 ]
119
120vnames = [ 'low', 'nom', 'high' ]
121
122temps = [ -40, 27, 125 ]
123
124corners = [ "ss", "tt", "ff" ]
125
126# Read the parameter file header and save the contents
127
128header = []
129with open('header.txt', 'r') as ifile:
130 hlines = ifile.read().splitlines()
131
132for corner in corners:
133 for temp in temps:
134 tname = str(temp).replace('-', 'n')
135 for vidx in range(0,3):
136 vname = vnames[vidx]
137
138 generated_files = []
139 ndevtypes = []
140 pdevtypes = []
141
142 ndynh = {}
143 ndynl = {}
144 pdynh = {}
145 pdynl = {}
146 nstat = {}
147 pstat = {}
148
149 for devidx in range(0, len(devices)):
150 devicepair = devices[devidx]
151 devset = devicepair[0]
152 if len(devicepair) != 9:
153 print('Error: Bad entry for device set ' + devset + '.\n')
154 continue
155 ptype = devicepair[1]
156 ntype = devicepair[2]
157 vtype = devicepair[3]
158 plength = devicepair[4]
159 pwidth = devicepair[5]
160 nlength = devicepair[6]
161 nwidth = devicepair[7]
162 loadcap = devicepair[8]
163
164 if ntype not in ndevtypes:
165 ndevtypes.append(ntype)
166 if ptype not in pdevtypes:
167 pdevtypes.append(ptype)
168
169 if vtype == '1v8':
170 volt = voltages1v8[vidx]
171 else:
172 volt = voltages3v3[vidx]
173
174 if not nosim:
175
176 # Read template and generate SPICE simulation netlist
177 newlines = []
178 with open('circuit_template.spi', 'r') as ifile:
179 template = ifile.read().splitlines()
180 for line in template:
181 outline = re.sub('CORNER', corner, line)
182 outline = re.sub('FULL_VOLTAGE', str(volt), outline)
183 outline = re.sub('HALF_VOLTAGE', str(volt / 2.0), outline)
184 outline = re.sub('TEMPERATURE', str(temp), outline)
185 outline = re.sub('DEVICENAME_N', ntype, outline)
186 outline = re.sub('DEVICENAME_P', ptype, outline)
187 outline = re.sub('WIDTH_N', str(nwidth), outline)
188 outline = re.sub('WIDTH_P', str(pwidth), outline)
189 outline = re.sub('LENGTH_N', str(nlength), outline)
190 outline = re.sub('LENGTH_P', str(plength), outline)
191 outline = re.sub('LOADCAP', str(loadcap), outline)
192 newlines.append(outline)
193
194 simname = 'sky130_' + corner + '_' + vname + '_' + tname + '_devpair' + str(devidx) + '.spice'
195
196 with open(simname, 'w') as ofile:
197 for line in newlines:
198 print(line, file=ofile)
199
200 generated_files.append(simname)
201
202 # Run ngspice simulation
203
204 print('** Running simulation on ' + devset + '(file ' + simname + ')')
205 print('** Conditions: temp=' + tname + ' corner=' + corner
206 + ' volt=' + vname)
207 p = subprocess.run(['ngspice', simname],
208 stdout = subprocess.PIPE,
209 universal_newlines = True)
210
211 if p.stdout:
212 parameters = p.stdout.splitlines()
213
214 for parameter in parameters:
215 valueline = parameter.split()
216 if len(valueline) < 3:
217 continue
218 if valueline[0] == 'ndynh':
219 try:
220 ndynh[ntype]
221 except:
222 ndynh[ntype] = valueline[2]
223 elif valueline[0] == 'pdynh':
224 try:
225 pdynh[ptype]
226 except:
227 pdynh[ptype] = valueline[2]
228 elif valueline[0] == 'ndynl':
229 try:
230 ndynl[ntype]
231 except:
232 ndynl[ntype] = valueline[2]
233 elif valueline[0] == 'pdynl':
234 try:
235 pdynl[ptype]
236 except:
237 pdynl[ptype] = valueline[2]
238 elif valueline[0] == 'nstat':
239 try:
240 nstat[ntype]
241 except:
242 nstat[ntype] = valueline[2]
243 elif valueline[0] == 'pstat':
244 try:
245 pstat[ptype]
246 except:
247 pstat[ptype] = valueline[2]
248 else:
249 print('** No file ' + outname + '; skipping.')
250
251 paramfile = 'sky130_' + corner + '_' + vname + '_' + tname + '.prm'
252
253 with open(paramfile, 'w') as ofile:
254 for line in hlines:
255 print(line, file=ofile)
256
257 # Now output information for every device
258 print('', file=ofile)
259
260 for device in ndevtypes:
261 devicepair = next(item for item in devices if item[2] == device)
262 if not devicepair:
263 print('Error: Bad entry for nFET device ' + device + '.\n')
264 continue
265 devset = devicepair[0]
266 if len(devicepair) != 9:
267 print('Error: Bad entry for device set ' + devset + '.\n')
268 continue
269 ntype = devicepair[2]
270 vtype = devicepair[3]
271 nlength = devicepair[6]
272 nwidth = devicepair[7]
273 loadcap = devicepair[8]
274
275 print('; C=' + str(loadcap) + ', N(w=' + str(nwidth) + ', l=' + str(nlength) + ')', file=ofile)
276 print('resistance ' + ntype + ' dynamic-high ' + str(nwidth) + ' ' + str(nlength) + ' ' + ndynh[ntype], file=ofile)
277 print('resistance ' + ntype + ' dynamic-low ' + str(nwidth) + ' ' + str(nlength) + ' ' + ndynl[ntype], file=ofile)
278 print('resistance ' + ntype + ' static ' + str(nwidth) + ' ' + str(nlength) + ' ' + nstat[ntype], file=ofile)
279 print('', file=ofile)
280
281 for device in pdevtypes:
282 devicepair = next(item for item in devices if item[1] == device)
283 if not devicepair:
284 print('Error: Bad entry for pFET device ' + device + '.\n')
285 continue
286 devset = devicepair[0]
287 if len(devicepair) != 9:
288 print('Error: Bad entry for device set ' + devset + '.\n')
289 continue
290 ptype = devicepair[1]
291 vtype = devicepair[3]
292 plength = devicepair[4]
293 pwidth = devicepair[5]
294 loadcap = devicepair[8]
295
296 print('; C=' + str(loadcap) + ', P(w=' + str(pwidth) + ', l=' + str(plength) + ')', file=ofile)
297 print('resistance ' + ptype + ' dynamic-high ' + str(pwidth) + ' ' + str(plength) + ' ' + pdynh[ptype], file=ofile)
298 print('resistance ' + ptype + ' dynamic-low ' + str(pwidth) + ' ' + str(plength) + ' ' + pdynl[ptype], file=ofile)
299 print('resistance ' + ptype + ' static ' + str(pwidth) + ' ' + str(plength) + ' ' + pstat[ptype], file=ofile)
300 print('', file=ofile)
301
302 if not keep:
303 print('**Removing generated intermediate files.')
304 for file in generated_files:
305 try:
306 os.remove(file)
307 except:
308 pass
309
310sys.exit(0)