Corrected the handling of parameters inside and outside a subcircuit
call.
diff --git a/common/spectre_to_spice.py b/common/spectre_to_spice.py
index ccaea96..c522367 100755
--- a/common/spectre_to_spice.py
+++ b/common/spectre_to_spice.py
@@ -27,25 +27,27 @@
# If inside a subcircuit, remove the keyword "parameters". If outside,
# change it to ".param"
-def parse_param_line(line, inparam, insub):
+def parse_param_line(line, inparam, insub, iscall, ispassed):
# Regexp patterns
parm1rex = re.compile('[ \t]*parameters[ \t]*(.*)')
parm2rex = re.compile('[ \t]*params:[ \t]*(.*)')
- parm3rex = re.compile('\+[ \t]*(.*)')
+ parm3rex = re.compile('[ \t]*\+[ \t]*(.*)')
parm4rex = re.compile('[ \t]*([^= \t]+)[ \t]*=[ \t]*([^ \t]+)[ \t]*(.*)')
parm5rex = re.compile('[ \t]*([^= \t]+)[ \t]*(.*)')
+ rtok = re.compile('([^ \t\n]+)[ \t]*(.*)')
fmtline = []
- iscdl = False
- if inparam:
+ if iscall:
+ rest = line
+ elif inparam:
pmatch = parm3rex.match(line)
if pmatch:
fmtline.append('+')
rest = pmatch.group(1)
else:
- return ''
+ return '', ispassed
else:
pmatch = parm1rex.match(line)
if pmatch:
@@ -60,62 +62,96 @@
if insub:
fmtline.append('+')
else:
- return ''
+ return '', ispassed
rest = pmatch.group(1)
- iscdl = True
else:
- return ''
+ return '', ispassed
while rest != '':
pmatch = parm4rex.match(rest)
if pmatch:
+ if ispassed:
+ # End of passed parameters. Break line and generate ".param"
+ ispassed = False
+ fmtline.append('\n.param ')
+
fmtline.append(pmatch.group(1))
fmtline.append('=')
value = pmatch.group(2)
rest = pmatch.group(3)
+ # 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)
+
+ needmore = False
+ while rest != '':
+ rmatch = rtok.match(rest)
+ if rmatch:
+ expch = rmatch.group(1)[0]
+ if (expch.isalpha() or expch == '$') and not needmore:
+ break
+ else:
+ needmore = False
+ value += rmatch.group(1)
+ rest = rmatch.group(2)
+ expch = rmatch.group(1).strip()
+ if expch in '+-*/(){}^~!':
+ needmore = True
+ else:
+ break
+
if is_number(value):
fmtline.append(value)
else:
fmtline.append('{' + value + '}')
- # These parameter sub-expressions are related to
- # monte carlo simulation and are incompatible with
- # ngspice. So put them in an in-line comment
+ # These parameter sub-expressions are related to monte carlo
+ # simulation and are incompatible with ngspice. So put them
+ # in an in-line comment. Avoid double-commenting things that
+ # were already in-line comments.
if rest != '':
nmatch = parm4rex.match(rest)
if not nmatch:
- fmtline.append(' $ ' + rest.replace(' ', '').replace('\t', ''))
+ if rest.lstrip().startswith('$ '):
+ fmtline.append(rest)
+ elif rest.strip() != '':
+ fmtline.append(' $ ' + rest.replace(' ', '').replace('\t', ''))
rest = ''
- elif iscdl:
+ else:
# Match to a CDL subckt parameter that does not have an '=' and so
- # assumes that the parameter is always passed. That is not legal SPICE,
- # so supply a default value of 1.
+ # assumes that the parameter is always passed, and therefore must
+ # be part of the .subckt line. A parameter without a value is not
+ # legal SPICE, so supply a default value of 1.
pmatch = parm5rex.match(rest)
if pmatch:
fmtline.append(pmatch.group(1) + '=1')
+ ispassed = True
rest = pmatch.group(2)
- else:
- break
+ else:
+ break
- return ' '.join(fmtline)
+ return ' '.join(fmtline), ispassed
def convert_file(in_file, out_file):
# Regexp patterns
statrex = re.compile('[ \t]*statistics[ \t]*\{(.*)')
simrex = re.compile('[ \t]*simulator[ \t]+([^= \t]+)[ \t]*=[ \t]*(.+)')
- insubrex = re.compile('[ \t]*inline[ \t]+subckt[ \t]+([^ \t]+)[ \t]*\(([^)]*)')
- cdlsubrex = re.compile('\.subckt[ \t]+([^ \t]+)[ \t]*\(([^)]*)')
+ insubrex = re.compile('[ \t]*inline[ \t]+subckt[ \t]+([^ \t\(]+)[ \t]*\(([^)]*)')
+ cdlsubrex = re.compile('\.?subckt[ \t]+([^ \t\(]+)[ \t]*\(([^)]*)')
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]*(.*)')
shincrex = re.compile('\.inc[ \t]+')
stdsubrex = re.compile('\.subckt[ \t]+([^ \t]+)[ \t]+([^ \t]*)')
stdmodelrex = re.compile('\.model[ \t]+([^ \t]+)[ \t]+([^ \t]+)[ \t]+(.*)')
stdendsubrex = re.compile('\.ends[ \t]+(.+)')
+ stdendonlysubrex = re.compile('\.ends[ \t]*')
# Devices (resistor, capacitor, subcircuit as resistor or capacitor)
caprex = re.compile('c([^ \t]+)[ \t]*\(([^)]*)\)[ \t]*capacitor[ \t]*(.*)', re.IGNORECASE)
@@ -123,13 +159,18 @@
cdlrex = re.compile('[ \t]*([crdlmqx])([^ \t]+)[ \t]*\(([^)]*)\)[ \t]*([^ \t]+)[ \t]*(.*)', re.IGNORECASE)
with open(in_file, 'r') as ifile:
- speclines = ifile.read().splitlines()
+ try:
+ speclines = ifile.read().splitlines()
+ except:
+ print('Failure to read ' + in_file + '; not an ASCII file?')
+ return
insub = False
inparam = False
inmodel = False
inpinlist = False
isspectre = False
+ ispassed = False
spicelines = []
calllines = []
modellines = []
@@ -141,7 +182,7 @@
for line in speclines:
- # Item 1. C++-style // comments get replae with * comment character
+ # Item 1a. C++-style // comments get replaced with * comment character
if line.strip().startswith('//'):
# Replace the leading "//" with SPICE-style comment "*".
if modellines != []:
@@ -152,6 +193,12 @@
spicelines.append(line.strip().replace('//', '*', 1))
continue
+ # Item 1b. In-line C++-style // comments get replaced with $ comment character
+ elif ' //' in line:
+ line = line.replace(' //', ' $ ', 1)
+ elif '\t//' in line:
+ line = line.replace('\t//', '\t$ ', 1)
+
# Item 2. Handle SPICE-style comment lines
if line.strip().startswith('*'):
if modellines != []:
@@ -167,10 +214,11 @@
contline = True
else:
contline = False
- if inparam:
- inparam = False
- if inpinlist:
- inpinlist = False
+ if line.strip() != '':
+ if inparam:
+ inparam = False
+ if inpinlist:
+ inpinlist = False
# Item 3. Handle blank lines like comment lines
if line.strip() == '':
@@ -201,7 +249,7 @@
if contline:
if inparam:
# Continue handling parameters
- fmtline = parse_param_line(line, inparam, insub)
+ fmtline, ispassed = parse_param_line(line, inparam, insub, False, ispassed)
if fmtline != '':
if modellines != []:
modellines.append(fmtline)
@@ -225,7 +273,7 @@
# If inside a subcircuit, remove "parameters". If outside,
# change it to ".param"
- fmtline = parse_param_line(line, inparam, insub)
+ fmtline, ispassed = parse_param_line(line, inparam, insub, False, ispassed)
if fmtline != '':
inparam = True
spicelines.append(fmtline)
@@ -240,10 +288,15 @@
continue
# model---not sure if it is always inside an inline subcircuit
+ iscdl = False
if isspectre:
mmatch = modelrex.match(line)
+ if not mmatch:
+ mmatch = cdlmodelrex.match(line)
+ iscdl = True
else:
mmatch = stdmodelrex.match(line)
+
if mmatch:
modname = mmatch.group(1)
modtype = mmatch.group(2)
@@ -253,20 +306,25 @@
inmodel = 1
# Continue to "if inmodel == 1" block below
else:
- if not isspectre:
- modellines.append(line)
+ fmtline, ispassed = parse_param_line(mmatch.group(3), True, False, True, ispassed)
+ modellines.append('.model ' + mmatch.group(1) + ' ' + mmatch.group(2) + ' ' + fmtline)
+ if fmtline != '':
+ inparam = True
+
inmodel = 2
continue
if not insub:
# Things to parse if not in a subcircuit
+ imatch = insubrex.match(line) if isspectre else None
- if isspectre:
- imatch = insubrex.match(line)
- else:
- # Check for CDL format .subckt lines first
+ if not imatch:
+ # Check for spectre format subckt or CDL format .subckt lines
imatch = cdlsubrex.match(line)
- if not imatch:
+
+ if not imatch:
+ if not isspectre:
+ # Check for standard SPICE format .subckt lines
imatch = stdsubrex.match(line)
if imatch:
@@ -278,6 +336,7 @@
inmodel = False
insub = True
+ ispassed = True
subname = imatch.group(1)
if isspectre:
devrex = re.compile(subname + '[ \t]*\(([^)]*)\)[ \t]*([^ \t]+)[ \t]*(.*)', re.IGNORECASE)
@@ -307,13 +366,23 @@
else:
if isspectre:
ematch = endsubrex.match(line)
+ if not ematch:
+ ematch = endonlysubrex.match(line)
else:
ematch = stdendsubrex.match(line)
+ if not ematch:
+ ematch = stdendonlysubrex.match(line)
+
if ematch:
- if ematch.group(1) != subname:
- print('Error: "ends" name does not match "subckt" name!')
- print('"ends" name = ' + ematch.group(1))
- print('"subckt" name = ' + subname)
+ try:
+ endname = ematch.group(1).strip()
+ except:
+ pass
+ else:
+ if endname != subname and endname != '':
+ print('Error: "ends" name does not match "subckt" name!')
+ print('"ends" name = ' + ematch.group(1).strip())
+ print('"subckt" name = ' + subname)
if len(calllines) > 0:
line = calllines[0]
if modtype.startswith('bsim'):
@@ -340,14 +409,15 @@
spicelines.append(line)
calllines = []
- spicelines.append('.ends ' + subname)
-
- # Now add the .model after the subcircuit definition
+ # Now add any in-circuit models
spicelines.append('')
for line in modellines:
spicelines.append(line)
modellines = []
+ # Complete the subcircuit definition
+ spicelines.append('.ends ' + subname)
+
insub = False
inmodel = False
subname = ''
@@ -362,7 +432,7 @@
# Check for devices R and C.
dmatch = caprex.match(line)
if dmatch:
- fmtline = parse_param_line(dmatch.group(3), True, insub)
+ fmtline, ispassed = parse_param_line(dmatch.group(3), True, insub, True, ispassed)
if fmtline != '':
inparam = True
spicelines.append('c' + dmatch.group(1) + ' ' + dmatch.group(2) + ' ' + fmtline)
@@ -373,7 +443,7 @@
dmatch = resrex.match(line)
if dmatch:
- fmtline = parse_param_line(dmatch.group(3), True, insub)
+ fmtline, ispassed = parse_param_line(dmatch.group(3), True, insub, True, ispassed)
if fmtline != '':
inparam = True
spicelines.append('r' + dmatch.group(1) + ' ' + dmatch.group(2) + ' ' + fmtline)
@@ -384,6 +454,7 @@
cmatch = cdlrex.match(line)
if cmatch:
+ ispassed = False
devtype = cmatch.group(1)
devmodel = cmatch.group(4)
@@ -401,7 +472,7 @@
# model is a resistor and not a subcircuit.
devtype = 'r'
- fmtline = parse_param_line(cmatch.group(5), True, insub)
+ fmtline, ispassed = parse_param_line(cmatch.group(5), True, insub, True, ispassed)
if fmtline != '':
inparam = True
spicelines.append(devtype + cmatch.group(2) + ' ' + cmatch.group(3) + ' ' + devmodel + ' ' + fmtline)
@@ -414,7 +485,7 @@
dmatch = devrex.match(line)
if dmatch:
- fmtline = parse_param_line(dmatch.group(3), True, insub)
+ fmtline, ispassed = parse_param_line(dmatch.group(3), True, insub, True, ispassed)
if fmtline != '':
inparam = True
calllines.append(subname + ' ' + dmatch.group(1) + ' ' + dmatch.group(2) + ' ' + fmtline)
@@ -447,7 +518,7 @@
continue
else:
- fmtline = parse_param_line(line, True, True)
+ fmtline, ispassed = parse_param_line(line, True, True, False, ispassed)
if fmtline != '':
modellines.append(fmtline)
continue