Removed the "minenclosedarea" patch for the HD and HDLL libraries and
moved the fix to the "fix_techlefA|B.py" scripts. This avoids the use
of patch files and avoids having to re-do the patch files to deal with
the change in the technology LEF files to cover process corners. Also:
made some enhancements to the spectre_to_spice.py script stemming from
conversion of the SkyWater continuous models.
diff --git a/VERSION b/VERSION
index 2bae0e1..ca94b4b 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.0.297
+1.0.298
diff --git a/common/spectre_to_spice.py b/common/spectre_to_spice.py
index 4243a1a..38cd2ec 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):
+def parse_param_line(line, inparam, insub, iscall, ispassed, linenum):
# Regexp patterns
parm1rex = re.compile('[ \t]*parameters[ \t]*(.*)')
@@ -69,16 +69,15 @@
return '', ispassed
while rest != '':
- if iscall:
- # It is hard to believe that this is legal even in spectre.
- # Parameter expression given with no braces or quotes around
- # the expression. Fix the expression by removing the spaces
- # around '*'.
- rest = re.sub('[ \t]*\*[ \t]*', '*', rest)
+ # It is hard to believe that this is legal even in spectre.
+ # Parameter expression given with no braces or quotes around
+ # the expression. Fix the expression by removing the spaces
+ # around '*'.
+ rest = re.sub('[ \t]*([\*\+\-])[ \t]*', '\g<1>', rest)
pmatch = parm4rex.match(rest)
if pmatch:
- if ispassed:
+ if ispassed and not iscall:
# End of passed parameters. Break line and generate ".param"
ispassed = False
fmtline.append('\n.param ')
@@ -97,14 +96,14 @@
# Watch for spaces in expressions (have they no rules??!)
# as indicated by something after a space not being an
- # alphabetical character (parameter name) or '$' (comment)
+ # alphabetical character (parameter name) or ';' (comment)
needmore = False
while rest != '':
rmatch = rtok.match(rest)
if rmatch:
expch = rmatch.group(1)[0]
- if expch == '$':
+ if expch == ';' or expch == '$':
break
elif expch.isalpha() and not needmore:
break
@@ -141,10 +140,10 @@
if rest != '':
nmatch = parm4rex.match(rest)
if not nmatch:
- if rest.lstrip().startswith('$ '):
+ if rest.lstrip().startswith('; '):
fmtline.append(rest)
elif rest.strip() != '':
- fmtline.append(' $ ' + rest.replace(' ', '').replace('\t', ''))
+ fmtline.append(' ; ' + rest.replace(' ', '').replace('\t', ''))
rest = ''
else:
# Match to a CDL subckt parameter that does not have an '=' and so
@@ -211,6 +210,40 @@
break
return paramnames
+# Dump any saved model lines to a list. Check for "type =" syntax in a
+# binned model
+
+def addmodel(modellines, linenum):
+ typerex = re.compile('([ \t]*type[ \t]*=[ \t]*)([^ \t]+)[ \t]*', re.IGNORECASE)
+
+ # 'ZYXW' was left as a placeholder for the device type
+ if len(modellines) > 0:
+ typename = None
+ for j in range(len(modellines) - 1, -1, -1):
+ line = modellines[j]
+ pmatch = typerex.search(line)
+ if pmatch:
+ typename = pmatch.group(2).strip('{}').strip()
+ newline = typerex.sub(' ', line)
+ modellines[j] = newline
+ line = newline
+
+ if 'ZYXW' in line:
+ if typename:
+ if typename == 'n':
+ newtype = 'nmos'
+ elif typename == 'p':
+ newtype = 'pmos'
+ else:
+ newtype = typename
+ modellines[j] = re.sub('ZYXW', newtype, line)
+ typename = None
+ else:
+ print('Error: No type name for model at line ' + str(linenum))
+ modellines[j] = re.sub('ZYXW', 'unknown', line)
+
+ return modellines
+
# Run the spectre-to-ngspice conversion
def convert_file(in_file, out_file):
@@ -223,13 +256,13 @@
endsubrex = re.compile('[ \t]*ends[ \t]+(.+)')
endonlysubrex = re.compile('[ \t]*ends[ \t]*')
modelrex = re.compile('[ \t]*model[ \t]+([^ \t]+)[ \t]+([^ \t]+)[ \t]+\{(.*)')
- cdlmodelrex = re.compile('[ \t]*model[ \t]+([^ \t]+)[ \t]+([^ \t]+)[ \t]+(.*)')
- binrex = re.compile('[ \t]*([0-9]+):[ \t]+type[ \t]*=[ \t]*(.*)')
+ cdlmodelrex = re.compile('[ \t]*model[ \t]+([^ \t]+)[ \t]+([^ \t]+)[ \t]*(.*)')
+ binrex = re.compile('[ \t]*([0-9]+):[ \t]*(.*)')
shincrex = re.compile('\.inc[ \t]+')
isexprrex = re.compile('[^0-9a-zA-Z_]')
paramrex = re.compile('\.param[ \t]+(.*)')
- stdsubrex = re.compile('\.subckt[ \t]+([^ \t]+)[ \t]+(.*)')
+ stdsubrex = re.compile('\.?subckt[ \t]+([^ \t]+)[ \t]+(.*)')
stdmodelrex = re.compile('\.model[ \t]+([^ \t]+)[ \t]+([^ \t]+)[ \t]*(.*)')
stdendsubrex = re.compile('\.ends[ \t]+(.+)')
stdendonlysubrex = re.compile('\.ends[ \t]*')
@@ -242,7 +275,6 @@
stddev2rex = re.compile('[ \t]*([cr])([^ \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:
try:
speclines = ifile.read().splitlines()
@@ -266,8 +298,12 @@
subnames = []
modname = ''
modtype = ''
+ othermodnames = []
+ othermodtypes = []
+ linenum = 0
for line in speclines:
+ linenum = linenum + 1
# Item 1a. C++-style // comments get replaced with * comment character
if line.strip().startswith('//'):
@@ -280,13 +316,13 @@
spicelines.append(line.strip().replace('//', '*', 1))
continue
- # Item 1b. In-line C++-style // comments get replaced with $ comment character
+ # Item 1b. In-line C++-style // comments get replaced with ; comment character
elif ' //' in line:
- line = line.replace(' //', ' $ ', 1)
+ line = line.replace(' //', ' ; ', 1)
elif '//' in line:
- line = line.replace('//', ' $ ', 1)
+ line = line.replace('//', ' ; ', 1)
elif '\t//' in line:
- line = line.replace('\t//', '\t$ ', 1)
+ line = line.replace('\t//', '\t; ', 1)
# Item 2. Handle SPICE-style comment lines
if line.strip().startswith('*'):
@@ -338,7 +374,7 @@
if contline:
if inparam:
# Continue handling parameters
- fmtline, ispassed = parse_param_line(line, inparam, insub, False, ispassed)
+ fmtline, ispassed = parse_param_line(line, inparam, insub, False, ispassed, linenum)
if fmtline != '':
if modellines != []:
modellines.append(fmtline)
@@ -362,7 +398,7 @@
# If inside a subcircuit, remove "parameters". If outside,
# change it to ".param"
- fmtline, ispassed = parse_param_line(line, inparam, insub, False, ispassed)
+ fmtline, ispassed = parse_param_line(line, inparam, insub, False, ispassed, linenum)
if fmtline != '':
inparam = True
spicelines.append(fmtline)
@@ -388,6 +424,9 @@
mmatch = stdmodelrex.match(line)
if mmatch:
+ if modname:
+ othermodnames.append(modname)
+ othermodtypes.append(modtype)
modname = mmatch.group(1)
modtype = mmatch.group(2)
@@ -396,9 +435,13 @@
inmodel = 1
# Continue to "if inmodel == 1" block below
else:
- fmtline, ispassed = parse_param_line(mmatch.group(3), True, False, True, ispassed)
+ fmtline, ispassed = parse_param_line(mmatch.group(3), True, False, True, ispassed, linenum)
if isspectre and (modtype == 'resistor' or modtype == 'r2'):
modtype = 'r'
+ if isspectre and (modtype == 'bsim4'):
+ # "bsim4"---cast to special string so that it can be later
+ # converted to "nmos" or "pmos" based on the "type" parameter
+ modtype = 'ZYXW'
modellines.append('.model ' + modname + ' ' + modtype + ' ' + fmtline)
if fmtline != '':
inparam = True
@@ -415,10 +458,9 @@
imatch = cdlsubrex.match(line)
if not imatch:
- if not isspectre:
- # Check for standard SPICE format .subckt lines
- imatch = stdsubrex.match(line)
-
+ # Check for standard SPICE format .subckt lines
+ # or CDL format subckt line without parentheses
+ imatch = stdsubrex.match(line)
if imatch:
# If a model block is pending, then dump it
if modellines != []:
@@ -432,16 +474,15 @@
subname = imatch.group(1)
subnames.append(subname)
if isspectre:
- devrex = re.compile(subname + '[ \t]*\(([^)]*)\)[ \t]*([^ \t]+)[ \t]*(.*)', re.IGNORECASE)
+ devrex = re.compile('[ \t]*' + subname + '[ \t]*\(([^)]*)\)[ \t]*([^ \t]+)[ \t]*(.*)', re.IGNORECASE)
else:
- devrex = re.compile(subname + '[ \t]*([^ \t]+)[ \t]*([^ \t]+)[ \t]*(.*)', re.IGNORECASE)
+ devrex = re.compile('[ \t]*' + subname + '[ \t]*([^ \t]+)[ \t]*([^ \t]+)[ \t]*(.*)', re.IGNORECASE)
# If there is no close-parenthesis then we should expect it on
# a continuation line
inpinlist = True if ')' not in line else False
# Remove parentheses groups from subcircuit arguments
spicelines.append('.subckt ' + ' ' + subname + ' ' + imatch.group(2))
continue
-
else:
# Things to parse when inside of an "inline subckt" block
@@ -476,29 +517,58 @@
print('Error: "ends" name does not match "subckt" name!')
print('"ends" name = ' + endname)
print('"subckt" name = ' + subname)
- if len(calllines) > 0:
- line = calllines[0]
- if modtype.startswith('bsim'):
- line = 'M' + line
- elif modtype.startswith('nmos'):
- line = 'M' + line
- elif modtype.startswith('pmos'):
- line = 'M' + line
- elif modtype.startswith('res'):
- line = 'R' + line
- elif modtype.startswith('cap'):
- line = 'C' + line
- elif modtype.startswith('pnp'):
- line = 'Q' + line
- elif modtype.startswith('npn'):
- line = 'Q' + line
- elif modtype.startswith('d'):
- line = 'D' + line
- spicelines.append(line)
+ if modname:
+ othermodnames.append(modname)
+ othermodtypes.append(modtype)
- for line in calllines[1:]:
- spicelines.append(line)
- calllines = []
+ for line in calllines:
+ modified = False
+ for j in range(len(othermodnames)):
+ if othermodnames[j] in line:
+ modtype = othermodtypes[j]
+ if modtype.startswith('bsim'):
+ line = 'M' + line
+ modified = True
+ elif modtype.startswith('nmos'):
+ line = 'M' + line
+ modified = True
+ elif modtype.startswith('pmos'):
+ line = 'M' + line
+ modified = True
+ elif modtype.startswith('res'):
+ line = 'R' + line
+ modified = True
+ elif modtype.startswith('cap'):
+ line = 'C' + line
+ modified = True
+ elif modtype.startswith('pnp'):
+ line = 'Q' + line
+ modified = True
+ elif modtype.startswith('npn'):
+ line = 'Q' + line
+ modified = True
+ elif modtype.startswith('d'):
+ line = 'D' + line
+ modified = True
+ elif modtype.startswith('ZYXW'):
+ line = 'M' + line
+ modified = True
+ else:
+ print('Error: No prefix at line ' + str(linenum))
+ print(' Model type is ' + modtype)
+ if modified:
+ break
+ if not modified:
+ # Also check for "standard" models, e.g. "resistor"
+ pnames = line.split()
+ if 'resistor' in pnames[1:]:
+ pnames.remove('resistor')
+ line = 'R' + ' '.join(pnames)
+ elif 'capacitor' in pnames[1:]:
+ pnames.remove('capacitor')
+ line = 'C' + ' '.join(pnames)
+ spicelines.append(line)
+ calllines = []
# Last check: Do any model types confict with the way they
# are called within the subcircuit? Spectre makes it very
@@ -529,16 +599,18 @@
# Now add any in-circuit models
spicelines.append('')
- for line in modellines:
- spicelines.append(line)
+ spicelines.extend(addmodel(modellines, linenum))
modellines = []
# Complete the subcircuit definition
spicelines.append('.ends ' + subname)
-
insub = False
inmodel = False
subname = ''
+ modname = ''
+ modtype = ''
+ othermodnames = []
+ othermodtypes = []
paramnames = []
continue
@@ -551,7 +623,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)
+ fmtline, ispassed = parse_param_line(dmatch.group(3), True, insub, True, ispassed, linenum)
if fmtline != '':
inparam = True
spicelines.append('c' + dmatch.group(1) + ' ' + dmatch.group(2) + ' ' + fmtline)
@@ -562,7 +634,7 @@
dmatch = resrex.match(line)
if dmatch:
- fmtline, ispassed = parse_param_line(dmatch.group(3), True, insub, True, ispassed)
+ fmtline, ispassed = parse_param_line(dmatch.group(3), True, insub, True, ispassed, linenum)
if fmtline != '':
inparam = True
spicelines.append('r' + dmatch.group(1) + ' ' + dmatch.group(2) + ' ' + fmtline)
@@ -598,7 +670,7 @@
# it must be enclosed in single quotes. Otherwise, if the named
# device model is a subcircuit, then the devtype must be "x".
- elif devtype.lower() == 'c' or devtype.lower() == 'r':
+ elif devtype.lower() == 'c' or devtype.lower() == 'r' or devtype.lower() == 'd':
if devmodel in subnames:
devtype = 'x' + devtype
else:
@@ -622,7 +694,7 @@
if devmodel.strip("'") == devmodel:
devmodel = "'" + devmodel + "'"
- fmtline, ispassed = parse_param_line(cmatch.group(5), True, insub, True, ispassed)
+ fmtline, ispassed = parse_param_line(cmatch.group(5), True, insub, True, ispassed, linenum)
if fmtline != '':
inparam = True
spicelines.append(devtype + cmatch.group(2) + ' ' + cmatch.group(3) + ' ' + devmodel + ' ' + fmtline)
@@ -640,12 +712,13 @@
dmatch = devrex.match(line)
if dmatch:
- fmtline, ispassed = parse_param_line(dmatch.group(3), True, insub, True, ispassed)
+ fmtline, ispassed = parse_param_line(dmatch.group(3), True, insub, True, ispassed, linenum)
if fmtline != '':
inparam = True
calllines.append(subname + ' ' + dmatch.group(1) + ' ' + dmatch.group(2) + ' ' + fmtline)
continue
else:
+ inparam = True
calllines.append(subname + ' ' + dmatch.group(1) + ' ' + dmatch.group(2) + ' ' + dmatch.group(3))
continue
@@ -659,39 +732,65 @@
if bmatch:
bin = bmatch.group(1)
- type = bmatch.group(2)
-
- if type == 'n':
- convtype = 'nmos'
- elif type == 'p':
- convtype = 'pmos'
- else:
- convtype = type
+ rest = bmatch.group(2)
# If there is a binned model then it replaces any original
# model line that was saved.
if modellines[-1].startswith('.model'):
modellines = modellines[0:-1]
modellines.append('')
- modellines.append('.model ' + modname + '.' + bin + ' ' + convtype)
- continue
+ modellines.append('.model ' + modname + '.' + bin + ' ZYXW')
- else:
- fmtline, ispassed = parse_param_line(line, True, True, False, ispassed)
- if fmtline != '':
- modellines.append(fmtline)
+ if rest != '':
+ # Somehow model numbers are allowed to break syntax and
+ # not start a new line with a continuation character.
+ if rest[0] == '+':
+ line = rest
+ else:
+ line = '+' + rest
+ else:
continue
+ fmtline, ispassed = parse_param_line(line, True, True, False, ispassed, linenum)
+ if fmtline != '':
+ modellines.append(fmtline)
+ continue
+
# Copy line as-is
spicelines.append(line)
# If any model lines remain at end, append them before output
if modellines != []:
- for line in modellines:
- spicelines.append(line)
+ spicelines.extend(addmodel(modellines, linenum))
modellines = []
inmodel = False
+ # Catching the spectre use of "m" is difficult, so do a 2nd 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]*')
+ needmult = []
+ for j in range(len(spicelines)):
+ line = spicelines[j]
+ smatch = subrex.match(line)
+ if smatch:
+ cursub = smatch.group(1)
+ smatch = sqrtrex.match(line)
+ if smatch:
+ spicelines[j] = sqsubrex.sub('\g<1>mult)', line)
+ needmult.append(cursub)
+
+ # Now add "mult=1" parameter to any subcircuit in the "needmult" list
+ for j in range(len(spicelines)):
+ line = spicelines[j]
+ smatch = subrex.match(line)
+ if smatch:
+ spicelines[j] = line + ' mult=1'
+
# Output the result to out_file.
with open(out_file, 'w') as ofile:
for line in spicelines:
diff --git a/sky130/Makefile.in b/sky130/Makefile.in
index 6715ffa..07ca4c4 100644
--- a/sky130/Makefile.in
+++ b/sky130/Makefile.in
@@ -1275,9 +1275,6 @@
# the libraries
${RM} ${STAGING_PATH}/${SKY130$*}/libs.ref/${HD_VERILOG}/*.*.v
# Apply extra PDK patches until they get fixed properly in the source
- ${PATCH} -p1 -f -d ${STAGING_PATH}/${SKY130$*}/libs.ref/${HD_TECHLEF} \
- < custom/patches/hd_minenclosed.squeaky.patch \
- 2>&1 | tee -a ${SKY130$*}_make.log || true
${PATCH} -p1 -f -d ${STAGING_PATH}/${SKY130$*}/libs.ref/${HD_VERILOG} \
< custom/patches/hd_wire_syntax.patch \
2>&1 | tee -a ${SKY130$*}_make.log || true
@@ -1315,10 +1312,6 @@
# Remove the base verilog files which have already been included into
# the libraries
${RM} ${STAGING_PATH}/${SKY130$*}/libs.ref/${HDLL_VERILOG}/*.*.v
- # Apply extra PDK patches until they get fixed properly in the source
- ${PATCH} -p1 -f -d ${STAGING_PATH}/${SKY130$*}/libs.ref/${HDLL_TECHLEF} \
- < custom/patches/hdll_minenclosed.squeaky.patch \
- 2>&1 | tee -a ${SKY130$*}_make.log || true
digital-hvl-%:
# Install custom additions to standard cell libraries
diff --git a/sky130/custom/patches/hd_minenclosed.squeaky.patch b/sky130/custom/patches/hd_minenclosed.squeaky.patch
deleted file mode 100644
index 64376e7..0000000
--- a/sky130/custom/patches/hd_minenclosed.squeaky.patch
+++ /dev/null
@@ -1,20 +0,0 @@
-diff --git a/sky130_fd_sc_hd.tlef b/sky130_fd_sc_hd.tlef
-index 1c913ba..815b750 100644
---- a/sky130_fd_sc_hd.tlef
-+++ b/sky130_fd_sc_hd.tlef
-@@ -107,6 +107,7 @@ LAYER met1
- WIDTH 3 0.28 ;
- AREA 0.083 ; # Met1 6
- THICKNESS 0.35 ;
-+ MINENCLOSEDAREA 0.14 ;
-
- ANTENNAMODEL OXIDE1 ;
- ANTENNADIFFSIDEAREARATIO PWL ( ( 0 400 ) ( 0.0125 400 ) ( 0.0225 2609 ) ( 22.5 11600 ) ) ;
-@@ -149,6 +150,7 @@ LAYER met2
- WIDTH 3 0.28 ;
- AREA 0.0676 ; # Met2 6
- THICKNESS 0.35 ;
-+ MINENCLOSEDAREA 0.14 ;
-
- EDGECAPACITANCE 37.759E-6 ;
- CAPACITANCE CPERSQDIST 16.9423E-6 ;
diff --git a/sky130/custom/patches/hdll_minenclosed.squeaky.patch b/sky130/custom/patches/hdll_minenclosed.squeaky.patch
deleted file mode 100644
index 5d8dba5..0000000
--- a/sky130/custom/patches/hdll_minenclosed.squeaky.patch
+++ /dev/null
@@ -1,21 +0,0 @@
-diff --git a/sky130_fd_sc_hdll.tlef b/sky130_fd_sc_hdll.tlef
-index 1c913ba..815b750 100644
---- a/sky130_fd_sc_hdll.tlef
-+++ b/sky130_fd_sc_hdll.tlef
-@@ -107,6 +107,7 @@ LAYER met1
- WIDTH 3 0.28 ;
- AREA 0.083 ; # Met1 6
- THICKNESS 0.35 ;
-+ MINENCLOSEDAREA 0.14 ;
-
- ANTENNAMODEL OXIDE1 ;
- ANTENNADIFFSIDEAREARATIO PWL ( ( 0 400 ) ( 0.0125 400 ) ( 0.0225 2609 ) ( 22.5 11600 ) ) ;
-@@ -149,6 +150,7 @@ LAYER met2
- WIDTH 3 0.28 ;
- AREA 0.0676 ; # Met2 6
- THICKNESS 0.35 ;
-+ MINENCLOSEDAREA 0.14 ;
-
- EDGECAPACITANCE 37.759E-6 ;
- CAPACITANCE CPERSQDIST 16.9423E-6 ;
-
diff --git a/sky130/custom/scripts/fix_techlefA.py b/sky130/custom/scripts/fix_techlefA.py
index fa992d9..6c61f54 100755
--- a/sky130/custom/scripts/fix_techlefA.py
+++ b/sky130/custom/scripts/fix_techlefA.py
@@ -45,9 +45,19 @@
resrex = re.compile('[ \t]*ENCLOSURE ABOVE')
layerrex = re.compile('[ \t]*LAYER ([^ \t\n]+)')
resrex2 = re.compile('[ \t]*RESISTANCE RPERSQ 12.2 ;')
+ thickrex = re.compile('[ \t]*THICKNESS')
+ emptyrex = re.compile('^[ \t]*$')
curlayer = None
+ thickness_seen = False
for line in llines:
+ if thickness_seen:
+ thickness_seen = False
+ if curlayer and (curlayer == 'met1' or curlayer == 'met2'):
+ ematch = emptyrex.match(line)
+ if ematch:
+ fixedlines.append(' MINENCLOSEDAREA 0.14 ;')
+
rmatch = resrex2.match(line)
if rmatch:
fixedlines.append(' RESISTANCE RPERSQ 12.8 ;')
@@ -72,6 +82,10 @@
if lmatch:
curlayer = lmatch.group(1)
+ tmatch = thickrex.match(line)
+ if tmatch:
+ thickness_seen = True
+
# Write output
if outname == None:
for i in fixedlines:
diff --git a/sky130/custom/scripts/fix_techlefB.py b/sky130/custom/scripts/fix_techlefB.py
index 731851d..f34b51b 100755
--- a/sky130/custom/scripts/fix_techlefB.py
+++ b/sky130/custom/scripts/fix_techlefB.py
@@ -62,9 +62,18 @@
resrex = re.compile('[ \t]*ENCLOSURE ABOVE')
layerrex = re.compile('[ \t]*LAYER ([^ \t\n]+)')
resrex2 = re.compile('[ \t]*RESISTANCE RPERSQ 12.2 ;')
+ thickrex = re.compile('[ \t]*THICKNESS')
+ emptyrex = re.compile('^[ \t]*$')
curlayer = None
+ thickness_seen = False
for line in llines:
+ if thickness_seen:
+ thickness_seen = False
+ if curlayer and (curlayer == 'met1' or curlayer == 'met2'):
+ ematch = emptyrex.match(line)
+ if ematch:
+ fixedlines.append(' MINENCLOSEDAREA 0.14 ;')
# Check for the MANUFACTURINGGRID statement in the file, and
# add the USEMINSPACING statement after it.
diff --git a/sky130/custom/scripts/mismatch_params.py b/sky130/custom/scripts/mismatch_params.py
index 0053542..c9388cc 100755
--- a/sky130/custom/scripts/mismatch_params.py
+++ b/sky130/custom/scripts/mismatch_params.py
@@ -36,7 +36,7 @@
mismatch_params = []
-mmrex = re.compile('^\*[ \t]*mismatch[ \t]+\{')
+mmrex = re.compile('^\*[ \t]*mismatch[ \t]*\{')
endrex = re.compile('^\*[ \t]*\}')
filelist = []
@@ -72,10 +72,32 @@
tokens = line.split()
if 'vary' in tokens:
if ('dist=gauss' in tokens) or ('gauss' in tokens):
+ gtype = 'A'
mismatch_param = tokens[2]
- std_dev = float(tokens[-1].split('=')[-1])
- replacement = '{}*AGAUSS(0,{!s},1)'.format(mm_switch_param, std_dev)
+ for token in tokens[3:]:
+ gparam = token.split('=')
+ if len(gparam) == 2:
+ if gparam[0] == 'std':
+ std_dev = float(gparam[1])
+ elif gparam[0] == 'percent' and gparam[1] == 'yes':
+ gtype = ''
+
+ if gtype == '':
+ # Convert percentage to a fraction
+ std_dev = std_dev / 100
+ repltext = '{}*' + gtype + 'GAUSS(0,{!s},1)'
+ replacement = repltext.format(mm_switch_param, std_dev)
mismatch_params.append((mismatch_param, replacement))
+ elif ('dist=lnorm' in tokens) or ('lnorm' in tokens):
+ mismatch_param = tokens[2]
+ for token in tokens[3:]:
+ gparam = token.split('=')
+ if len(gparam) == 2:
+ if gparam[0] == 'std':
+ std_dev = float(gparam[1])
+ replacement = '{}*EXP(AGAUSS(0,{!s},1))'.format(mm_switch_param, std_dev)
+ mismatch_params.append((mismatch_param, replacement))
+
infile.close()
diff --git a/sky130/custom/scripts/pdk_update.sh b/sky130/custom/scripts/pdk_update.sh
index 47910b2..c6bb970 100755
--- a/sky130/custom/scripts/pdk_update.sh
+++ b/sky130/custom/scripts/pdk_update.sh
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/bash
#
# pdk_update.sh --
#
@@ -8,8 +8,8 @@
# Usage: pdk_update.sh <directory>
#
-if [ ! test -d $1 ] ; then
- echo "Project does not exist in $pdir ; Cannot update."
+if [ ! -d $1 ] ; then
+ echo "Project does not exist in $1 ; Cannot update."
exit 0
fi
diff --git a/sky130/custom/scripts/process_params.py b/sky130/custom/scripts/process_params.py
index c27840a..9cfc122 100755
--- a/sky130/custom/scripts/process_params.py
+++ b/sky130/custom/scripts/process_params.py
@@ -37,7 +37,7 @@
process_params = []
parmrex = re.compile('^\.param[ \t]+')
-prrex = re.compile('^\*[ \t]*process[ \t]+\{')
+prrex = re.compile('^\*[ \t]*process[ \t]*\{')
endrex = re.compile('^\*[ \t]*\}')
filelist = []
@@ -73,10 +73,31 @@
tokens = line.split()
if 'vary' in tokens:
if ('dist=gauss' in tokens) or ('gauss' in tokens):
+ gtype = 'A'
process_param = tokens[2]
- std_dev = float(tokens[-1].split('=')[-1])
- replacement = ' + {}*AGAUSS(0,{!s},1)'.format(pr_switch_param, std_dev)
+ for token in tokens[3:]:
+ gparam = token.split('=')
+ if len(gparam) == 2:
+ if gparam[0] == 'std':
+ std_dev = float(gparam[1])
+ elif gparam[0] == 'percent' and gparam[1] == 'yes':
+ gtype = ''
+ if gtype == '':
+ # Convert percentage to a fraction
+ std_dev = std_dev / 100
+ repltext = ' + {}*' + gtype + 'GAUSS(0,{!s},1)'
+ replacement = repltext.format(pr_switch_param, std_dev)
process_params.append((process_param, replacement))
+ elif ('dist=lnorm' in tokens) or ('lnorm' in tokens):
+ process_param = tokens[2]
+ for token in tokens[3:]:
+ gparam = token.split('=')
+ if len(gparam) == 2:
+ if gparam[0] == 'std':
+ std_dev = float(gparam[1])
+ replacement = ' + {}*EXP(AGAUSS(0,{!s},1))'.format(pr_switch_param, std_dev)
+ process_params.append((process_param, replacement))
+
infile.close()
diff --git a/sky130/qflow/sky130.sh b/sky130/qflow/sky130.sh
index 31a1fc9..effc569 100644
--- a/sky130/qflow/sky130.sh
+++ b/sky130/qflow/sky130.sh
@@ -32,9 +32,9 @@
#ifdef METAL5
#ifdef EF_FORMAT
-set techleffile=STAGING_PATH/TECHNAME/libs.ref/techLEF/LIBRARY/LIBRARY.tlef
+set techleffile=STAGING_PATH/TECHNAME/libs.ref/techLEF/LIBRARY/LIBRARY__nom.tlef
#else (!EF_FORMAT)
-set techleffile=STAGING_PATH/TECHNAME/libs.ref/LIBRARY/techlef/LIBRARY.tlef
+set techleffile=STAGING_PATH/TECHNAME/libs.ref/LIBRARY/techlef/LIBRARY__nom.tlef
#endif (!EF_FORMAT)
#else
# NOTE: There is no technology LEF file for the 3-metal stack!