Additional changes to the spectre_to_spice.py script in support of
onboarding the continuous SPICE models. Also: Added "-j" option
to "make timing" for the PDK repository download and update, so
that "make timing", which takes a long time, can run a lot faster.
diff --git a/Makefile.in b/Makefile.in
index 65efa56..9adbc77 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -117,7 +117,7 @@
#---------------------------------------------------
tech-%: %
- (cd $* && ${MAKE} all)
+ (cd $* && ${MAKE} -j$(nproc) all)
#---------------------------------------------------
diff --git a/VERSION b/VERSION
index ca94b4b..ea13f22 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.0.298
+1.0.299
diff --git a/common/spectre_to_spice.py b/common/spectre_to_spice.py
index c95f7ce..52698e0 100755
--- a/common/spectre_to_spice.py
+++ b/common/spectre_to_spice.py
@@ -27,7 +27,7 @@
# If inside a subcircuit, remove the keyword "parameters". If outside,
# change it to ".param"
-def parse_param_line(line, inparam, insub, iscall, ispassed, linenum):
+def parse_param_line(line, inparam, insub, iscall, ispassed, inmod, linenum):
# Regexp patterns
parm1rex = re.compile('[ \t]*parameters[ \t]*(.*)')
@@ -77,7 +77,7 @@
pmatch = parm4rex.match(rest)
if pmatch:
- if ispassed and not iscall:
+ if ispassed and not iscall and not inmod:
# End of passed parameters. Break line and generate ".param"
ispassed = False
fmtline.append('\n.param ')
@@ -153,7 +153,7 @@
pmatch = parm5rex.match(rest)
if pmatch:
- # NOTE: Something that is not a parameters name should be
+ # NOTE: Something that is not a parameter name should be
# extended from the previous line. Note that this parsing
# is not rigorous and is possible to break. . .
if any((c in '+-*/(){}^~!') for c in pmatch.group(1).strip()):
@@ -271,8 +271,8 @@
caprex = re.compile('c([^ \t]+)[ \t]*\(([^)]*)\)[ \t]*capacitor[ \t]*(.*)', re.IGNORECASE)
resrex = re.compile('r([^ \t]+)[ \t]*\(([^)]*)\)[ \t]*resistor[ \t]*(.*)', re.IGNORECASE)
cdlrex = re.compile('[ \t]*([npcrdlmqx])([^ \t]+)[ \t]*\(([^)]*)\)[ \t]*([^ \t]+)[ \t]*(.*)', re.IGNORECASE)
- stddevrex = re.compile('[ \t]*([cr])([^ \t]+)[ \t]+([^ \t]+[ \t]+[^ \t]+)[ \t]+([^ \t]+)[ \t]*(.*)', re.IGNORECASE)
- stddev2rex = re.compile('[ \t]*([cr])([^ \t]+)[ \t]+([^ \t]+[ \t]+[^ \t]+)[ \t]+([^ \t\'{]+[\'{][^\'}]+[\'}])[ \t]*(.*)', re.IGNORECASE)
+ stddevrex = re.compile('[ \t]*([crd])([^ \t]+)[ \t]+([^ \t]+[ \t]+[^ \t]+)[ \t]+([^ \t]+)[ \t]*(.*)', re.IGNORECASE)
+ stddev2rex = re.compile('[ \t]*([crd])([^ \t]+)[ \t]+([^ \t]+[ \t]+[^ \t]+)[ \t]+([^ \t\'{]+[\'{][^\'}]+[\'}])[ \t]*(.*)', re.IGNORECASE)
stddev3rex = re.compile('[ \t]*([npcrdlmqx])([^ \t]+)[ \t]+(.*)', re.IGNORECASE)
with open(in_file, 'r') as ifile:
@@ -282,6 +282,26 @@
print('Failure to read ' + in_file + '; not an ASCII file?')
return
+ #---------------------------------------------------------------
+ # Do one pass through the file and pick up all subcircuit names,
+ # because any subcircuit call could be a forward reference.
+ #---------------------------------------------------------------
+
+ allsubrex = re.compile('.*subckt[ \t]+([^ \t\(]+)')
+ subnames = []
+ for line in speclines:
+ testline = line.strip()
+ if testline.startswith('//') or testline.startswith('*'):
+ continue
+ smatch = allsubrex.match(testline)
+ if smatch:
+ subnames.append(smatch.group(1))
+
+ #---------------------------------------------------------------
+ # Now do the main pass (this is probably a bad idea and it would
+ # have been more efficient to do this in multiple passes).
+ #---------------------------------------------------------------
+
insub = False
inparam = False
inmodel = False
@@ -295,7 +315,6 @@
savematch = None
blockskip = 0
subname = ''
- subnames = []
modname = ''
modtype = ''
othermodnames = []
@@ -338,8 +357,8 @@
if line.strip().startswith('+'):
contline = True
else:
- contline = False
if line.strip() != '':
+ contline = False
if inparam:
inparam = False
if inpinlist:
@@ -374,7 +393,7 @@
if contline:
if inparam:
# Continue handling parameters
- fmtline, ispassed = parse_param_line(line, inparam, insub, False, ispassed, linenum)
+ fmtline, ispassed = parse_param_line(line, inparam, insub, False, ispassed, inmodel, linenum)
if fmtline != '':
if modellines != []:
modellines.append(fmtline)
@@ -398,7 +417,7 @@
# If inside a subcircuit, remove "parameters". If outside,
# change it to ".param"
- fmtline, ispassed = parse_param_line(line, inparam, insub, False, ispassed, linenum)
+ fmtline, ispassed = parse_param_line(line, inparam, insub, False, ispassed, inmodel, linenum)
if fmtline != '':
inparam = True
spicelines.append(fmtline)
@@ -435,7 +454,7 @@
inmodel = 1
# Continue to "if inmodel == 1" block below
else:
- fmtline, ispassed = parse_param_line(mmatch.group(3), True, False, True, ispassed, linenum)
+ fmtline, ispassed = parse_param_line(mmatch.group(3), True, False, True, ispassed, inmodel, linenum)
if isspectre and (modtype == 'resistor' or modtype == 'r2'):
modtype = 'r'
if isspectre and (modtype == 'diode'):
@@ -445,9 +464,7 @@
# converted to "nmos" or "pmos" based on the "type" parameter
modtype = 'ZYXW'
modellines.append('.model ' + modname + ' ' + modtype + ' ' + fmtline)
- if fmtline != '':
- inparam = True
-
+ inparam = True
inmodel = 2
continue
@@ -474,7 +491,6 @@
insub = True
ispassed = True
subname = imatch.group(1)
- subnames.append(subname)
if isspectre:
devrex = re.compile('[ \t]*' + subname + '[ \t]*\(([^)]*)\)[ \t]*([^ \t]+)[ \t]*(.*)', re.IGNORECASE)
else:
@@ -569,6 +585,10 @@
elif 'capacitor' in pnames[1:]:
pnames.remove('capacitor')
line = 'C' + ' '.join(pnames)
+ elif len(line) > 1 and line[0].lower() != 'x' and line[0] != '*' and line[0] != '+':
+ # NOTE: Assumes that anything else being called
+ # is a known subcircuit (probably better to check)
+ line = 'X' + ' '.join(pnames)
spicelines.append(line)
calllines = []
@@ -625,7 +645,7 @@
# Check for devices R and C.
dmatch = caprex.match(line)
if dmatch:
- fmtline, ispassed = parse_param_line(dmatch.group(3), True, insub, True, ispassed, linenum)
+ fmtline, ispassed = parse_param_line(dmatch.group(3), True, insub, True, ispassed, inmodel, linenum)
if fmtline != '':
inparam = True
spicelines.append('c' + dmatch.group(1) + ' ' + dmatch.group(2) + ' ' + fmtline)
@@ -636,7 +656,7 @@
dmatch = resrex.match(line)
if dmatch:
- fmtline, ispassed = parse_param_line(dmatch.group(3), True, insub, True, ispassed, linenum)
+ fmtline, ispassed = parse_param_line(dmatch.group(3), True, insub, True, ispassed, inmodel, linenum)
if fmtline != '':
inparam = True
spicelines.append('r' + dmatch.group(1) + ' ' + dmatch.group(2) + ' ' + fmtline)
@@ -662,6 +682,10 @@
elif devmodel == 'resistor':
devtype = 'r'
devmodel = ''
+ # The "device model" might be a known (inline) subcircuit in
+ # which case this is really a subcircuit.
+ elif devmodel in subnames:
+ devtype = 'x' + devtype
elif devtype.lower() == 'n' or devtype.lower() == 'p':
# May be specific to SkyWater models, or is it a spectreism?
# NOTE: There is a check, below, to revisit this assignment
@@ -696,7 +720,7 @@
if devmodel.strip("'") == devmodel:
devmodel = "'" + devmodel + "'"
- fmtline, ispassed = parse_param_line(cmatch.group(5), True, insub, True, ispassed, linenum)
+ fmtline, ispassed = parse_param_line(cmatch.group(5), True, insub, True, ispassed, inmodel, linenum)
if fmtline != '':
inparam = True
spicelines.append(devtype + cmatch.group(2) + ' ' + cmatch.group(3) + ' ' + devmodel + ' ' + fmtline)
@@ -714,7 +738,7 @@
dmatch = devrex.match(line)
if dmatch:
- fmtline, ispassed = parse_param_line(dmatch.group(3), True, insub, True, ispassed, linenum)
+ fmtline, ispassed = parse_param_line(dmatch.group(3), True, insub, True, ispassed, inmodel, linenum)
if fmtline != '':
inparam = True
calllines.append(subname + ' ' + dmatch.group(1) + ' ' + dmatch.group(2) + ' ' + fmtline)
@@ -753,7 +777,7 @@
else:
continue
- fmtline, ispassed = parse_param_line(line, True, True, False, ispassed, linenum)
+ fmtline, ispassed = parse_param_line(line, True, True, False, ispassed, inmodel, linenum)
if fmtline != '':
modellines.append(fmtline)
continue
@@ -777,14 +801,14 @@
if len(line) > 1 and line[0].lower() == 'd':
spicelines[j] = perimrex.sub('pj\g<1>', line)
- # Catching the spectre use of "m" is difficult, so do a 2nd pass
+ # Catching the spectre use of "m" is difficult, so do another pass
# on "spicelines" to catch which subcircuits use "*m" expressions,
# then for those subcircuits, add "mult=1" to the subcircuit
# parameters. (NOTE: Need a more generic regular expression here)
sqrtrex = re.compile('.*(sqrt\([ \t]*[^ \t]+[ \t]*\*[ \t]*)m[ \t]*\)', re.IGNORECASE)
sqsubrex = re.compile('(sqrt\([ \t]*[^ \t]+[ \t]*\*[ \t]*)m[ \t]*\)', re.IGNORECASE)
- subrex = re.compile('\.subckt[ \t]+([^ \t\(]+)[ \t]*')
+ subrex = re.compile('\.subckt[ \t]+([^ \t\(]+)[ \t]*', re.IGNORECASE)
needmult = []
for j in range(len(spicelines)):
line = spicelines[j]
@@ -803,11 +827,22 @@
if smatch:
spicelines[j] = line + ' mult=1'
+ # Check for resistor models using "tc1r" and "tc2r"
+ rmodrex = re.compile('.*[ \t]+tc[12]r[ \t]+=', re.IGNORECASE)
+ for j in range(len(spicelines)):
+ line = spicelines[j]
+ rmatch = rmodrex.match(line)
+ if rmatch:
+ spicelines[j] = re.sub('([ \t]+tc[12])r([ \t])', '\g<1>\g<2>', line)
+
# Output the result to out_file.
with open(out_file, 'w') as ofile:
for line in spicelines:
print(line, file=ofile)
+ # Debug info
+ # print('Defined subcircuits: ' + ' '.join(subnames))
+
if __name__ == '__main__':
debug = False
diff --git a/sky130/custom/scripts/pdk_download.sh b/sky130/custom/scripts/pdk_download.sh
index 70f9ae0..a8e8804 100755
--- a/sky130/custom/scripts/pdk_download.sh
+++ b/sky130/custom/scripts/pdk_download.sh
@@ -29,4 +29,4 @@
# Generate liberty files
echo "Building liberty timing files"
-make timing
+make -j$(nproc) timing
diff --git a/sky130/custom/scripts/pdk_update.sh b/sky130/custom/scripts/pdk_update.sh
index c6bb970..dd8b66b 100755
--- a/sky130/custom/scripts/pdk_update.sh
+++ b/sky130/custom/scripts/pdk_update.sh
@@ -28,4 +28,4 @@
# Regenerate liberty files
echo "Regenerating liberty timing files"
-make timing
+make -j$(nproc) timing