blob: 6d81084fcaac7feaeb772d10f8f8d2cb5955de11 [file] [log] [blame]
#!/usr/bin/env python3
#
#
import os
import re
import sys
import subprocess
#---------------------------------------------------------------------------
# usage: run_all.py [-nosim] [-keep]
#
# Run ngspice simulations on all major transistor devices in the process
# (excluding high-voltage > 3V devices) at all corners, and generate
# IRSIM parameter files for each corner.
#
# The "-nosim" option assumes that simulation output files have been
# saved, and will run the parser to generate the parameterf files from
# the existing ngspice output files.
#
# The "-keep" option will retain the ngspice input and output files
# after each parameter file has been generated. Otherwise, they will
# be removed.
#---------------------------------------------------------------------------
#---------------------------------------------------------------------------
# Devices must be paired P and N for each test. There are some redundant
# devices below where one type exists that does not have a corresponding
# device in the opposite type. In cases of redundancy, the first results
# computed will be the ones used for that device. The third item in each
# list is the voltage range to use for the device, and determines which
# voltages are used for max/min/typ simulations.
#---------------------------------------------------------------------------
#---------------------------------------------------------------------------
# NOTE: This method requires IRSIM 9.7.114, which supports multiple
# transistor device parameters and multiple supply voltages.
#---------------------------------------------------------------------------
#---------------------------------------------------------------------------
# To do: Speed this up by using multiprocessing
#---------------------------------------------------------------------------
# Parse options
keep = False
nosim = False
options = []
arguments = []
for item in sys.argv[1:]:
if item.find('-', 0) == 0:
options.append(item)
else:
arguments.append(item)
if len(arguments) > 0:
print("Usage: run_all.py [-nosim] [-keep]")
sys.exit(1)
if '-keep' in options:
keep = True
print("Keep mode: Retaining all intermediate files.")
if '-nosim' in options:
nosim = True
print("No-sim mode: Not running any simulations.")
if '-help' in options:
print("Usage: run_all.py [-nosim] [-keep]")
sys.exit(0)
#---------------------------------------------------------------------------
# Each entry in "devices" list has 9 items:
# [test-text, pFET-name, nFET-name, voltage-range,
# p-length, p-width, n-length, n-width, load-cap]
#---------------------------------------------------------------------------
devices = [
[
"1.8V devices",
"sky130_fd_pr__pfet_01v8", "sky130_fd_pr__nfet_01v8",
"1v8", 0.15, 1.0, 0.15, 1.0, 250
],
[
"1.8V LVT devices",
"sky130_fd_pr__pfet_01v8_lvt", "sky130_fd_pr__nfet_01v8_lvt",
"1v8", 0.35, 1.0, 0.15, 1.0, 250
],
[
"1.8V HVT pFET",
"sky130_fd_pr__pfet_01v8_hvt", "sky130_fd_pr__nfet_01v8",
"1v8", 0.45, 1.0, 0.15, 1.0, 250
],
[
"SRAM latching FETs",
"sky130_fd_pr__special_pfet_pass", "sky130_fd_pr__special_nfet_latch",
"1v8", 0.15, 0.14, 0.15, 0.21, 100
],
[
"SRAM pass nFET",
"sky130_fd_pr__special_pfet_pass", "sky130_fd_pr__special_nfet_pass",
"1v8", 0.15, 0.14, 0.15, 0.14, 100
],
[
"3.3V devices",
"sky130_fd_pr__pfet_g5v0d10v5", "sky130_fd_pr__nfet_g5v0d10v5",
"3v3", 0.50, 1.0, 0.50, 1.0, 250
],
[
"5.0V native nFET",
"sky130_fd_pr__pfet_g5v0d10v5", "sky130_fd_pr__nfet_05v0_nvt",
"3v3", 0.50, 1.0, 0.90, 1.0, 250
]
]
voltages1v8 = [ 1.62, 1.80, 1.98 ]
voltages3v3 = [ 2.97, 3.30, 3.63 ]
vnames = [ 'low', 'nom', 'high' ]
temps = [ -40, 27, 125 ]
corners = [ "ss", "tt", "ff" ]
# Read the parameter file header and save the contents
header = []
with open('header.txt', 'r') as ifile:
hlines = ifile.read().splitlines()
for corner in corners:
for temp in temps:
tname = str(temp).replace('-', 'n')
for vidx in range(0,3):
vname = vnames[vidx]
generated_files = []
ndevtypes = []
pdevtypes = []
ndynh = {}
ndynl = {}
pdynh = {}
pdynl = {}
nstat = {}
pstat = {}
for devidx in range(0, len(devices)):
devicepair = devices[devidx]
devset = devicepair[0]
if len(devicepair) != 9:
print('Error: Bad entry for device set ' + devset + '.\n')
continue
ptype = devicepair[1]
ntype = devicepair[2]
vtype = devicepair[3]
plength = devicepair[4]
pwidth = devicepair[5]
nlength = devicepair[6]
nwidth = devicepair[7]
loadcap = devicepair[8]
if ntype not in ndevtypes:
ndevtypes.append(ntype)
if ptype not in pdevtypes:
pdevtypes.append(ptype)
if vtype == '1v8':
volt = voltages1v8[vidx]
else:
volt = voltages3v3[vidx]
if not nosim:
# Read template and generate SPICE simulation netlist
newlines = []
with open('circuit_template.spi', 'r') as ifile:
template = ifile.read().splitlines()
for line in template:
outline = re.sub('CORNER', corner, line)
outline = re.sub('FULL_VOLTAGE', str(volt), outline)
outline = re.sub('HALF_VOLTAGE', str(volt / 2.0), outline)
outline = re.sub('TEMPERATURE', str(temp), outline)
outline = re.sub('DEVICENAME_N', ntype, outline)
outline = re.sub('DEVICENAME_P', ptype, outline)
outline = re.sub('WIDTH_N', str(nwidth), outline)
outline = re.sub('WIDTH_P', str(pwidth), outline)
outline = re.sub('LENGTH_N', str(nlength), outline)
outline = re.sub('LENGTH_P', str(plength), outline)
outline = re.sub('LOADCAP', str(loadcap), outline)
newlines.append(outline)
simname = 'sky130_' + corner + '_' + vname + '_' + tname + '_devpair' + str(devidx) + '.spice'
with open(simname, 'w') as ofile:
for line in newlines:
print(line, file=ofile)
generated_files.append(simname)
# Run ngspice simulation
print('** Running simulation on ' + devset + '(file ' + simname + ')')
print('** Conditions: temp=' + tname + ' corner=' + corner
+ ' volt=' + vname)
p = subprocess.run(['ngspice', simname],
stdout = subprocess.PIPE,
universal_newlines = True)
if p.stdout:
parameters = p.stdout.splitlines()
for parameter in parameters:
valueline = parameter.split()
if len(valueline) < 3:
continue
if valueline[0] == 'ndynh':
try:
ndynh[ntype]
except:
ndynh[ntype] = valueline[2]
elif valueline[0] == 'pdynh':
try:
pdynh[ptype]
except:
pdynh[ptype] = valueline[2]
elif valueline[0] == 'ndynl':
try:
ndynl[ntype]
except:
ndynl[ntype] = valueline[2]
elif valueline[0] == 'pdynl':
try:
pdynl[ptype]
except:
pdynl[ptype] = valueline[2]
elif valueline[0] == 'nstat':
try:
nstat[ntype]
except:
nstat[ntype] = valueline[2]
elif valueline[0] == 'pstat':
try:
pstat[ptype]
except:
pstat[ptype] = valueline[2]
else:
print('** No file ' + outname + '; skipping.')
paramfile = 'sky130_' + corner + '_' + vname + '_' + tname + '.prm'
with open(paramfile, 'w') as ofile:
for line in hlines:
print(line, file=ofile)
# Now output information for every device
print('', file=ofile)
for device in ndevtypes:
devicepair = next(item for item in devices if item[2] == device)
if not devicepair:
print('Error: Bad entry for nFET device ' + device + '.\n')
continue
devset = devicepair[0]
if len(devicepair) != 9:
print('Error: Bad entry for device set ' + devset + '.\n')
continue
ntype = devicepair[2]
vtype = devicepair[3]
nlength = devicepair[6]
nwidth = devicepair[7]
loadcap = devicepair[8]
print('; C=' + str(loadcap) + ', N(w=' + str(nwidth) + ', l=' + str(nlength) + ')', file=ofile)
print('resistance ' + ntype + ' dynamic-high ' + str(nwidth) + ' ' + str(nlength) + ' ' + ndynh[ntype], file=ofile)
print('resistance ' + ntype + ' dynamic-low ' + str(nwidth) + ' ' + str(nlength) + ' ' + ndynl[ntype], file=ofile)
print('resistance ' + ntype + ' static ' + str(nwidth) + ' ' + str(nlength) + ' ' + nstat[ntype], file=ofile)
print('', file=ofile)
for device in pdevtypes:
devicepair = next(item for item in devices if item[1] == device)
if not devicepair:
print('Error: Bad entry for pFET device ' + device + '.\n')
continue
devset = devicepair[0]
if len(devicepair) != 9:
print('Error: Bad entry for device set ' + devset + '.\n')
continue
ptype = devicepair[1]
vtype = devicepair[3]
plength = devicepair[4]
pwidth = devicepair[5]
loadcap = devicepair[8]
print('; C=' + str(loadcap) + ', P(w=' + str(pwidth) + ', l=' + str(plength) + ')', file=ofile)
print('resistance ' + ptype + ' dynamic-high ' + str(pwidth) + ' ' + str(plength) + ' ' + pdynh[ptype], file=ofile)
print('resistance ' + ptype + ' dynamic-low ' + str(pwidth) + ' ' + str(plength) + ' ' + pdynl[ptype], file=ofile)
print('resistance ' + ptype + ' static ' + str(pwidth) + ' ' + str(plength) + ' ' + pstat[ptype], file=ofile)
print('', file=ofile)
if not keep:
print('**Removing generated intermediate files.')
for file in generated_files:
try:
os.remove(file)
except:
pass
sys.exit(0)