moving scripts into separate repo

This commit is contained in:
Edward Arthur Bingham 2023-06-07 12:00:52 -04:00
commit 0f50dcaab0
2 changed files with 220 additions and 0 deletions

179
hspice2xyce Executable file
View File

@ -0,0 +1,179 @@
#!/usr/bin/python3
import sys
import re
class StackElem:
def __init__(self, parent):
self.parent = parent
self.methods = set()
def emitOperation(number, command, operation, stack):
if command in [".prot", ".protect", ".unprot", ".unprotect"]:
return # Xyce does not support these commands
elif command == ".option":
operation = fixOption(number, operation, stack)
else:
operation = fixParams(number, operation, stack)
if len(operation) > 0:
print("\n".join(operation))
def emitFunction(name, args, definition):
print(".func " + name + args + " {" + definition + "}")
# s/^include/* include/g
def fixParams(number, operation, stack):
result = []
index = []
for i, line in enumerate(operation):
if len(line) == 0 or line[0] == "*":
result.append(line)
continue
result.append("")
line = line.replace("$", ";")
for param in re.finditer(r'([^ \t=+\(\)]+|\'[^\']*\'|[\(\)])(?:[ \t]*(\([^\)]*\))?[ \t]*=[ \t]*(\'[^\']*\'|[^ \t]+))?', line):
name = param.group(1)
args = param.group(2)
definition = param.group(3)
# dev/gauss is an unsupported parameter in xyce
if name == "dev/gauss":
continue
elif name in ["vt", "Vt", "vT", "VT"]:
name = "local_" + name
if definition:
if ":" in definition:
definition = definition.replace(":", " :")
if definition[0] != "'":
definition = "'" + definition + "'"
# Convert .param functions to .func commands
if args and len(args) > 0:
found = False
for elem in stack:
if name in elem.methods:
found = True
break
if not found:
emitFunction(name, args, definition[1:-1] if definition[0] == "'" else definition)
stack[-1].methods.add(name)
# TODO(edward.bingham) other parameter fixes
elif definition and len(definition) > 0:
result[-1] += " " + name + "=" + definition
elif name and len(name) > 0:
if len(result[-1]) > 0:
result[-1] += " "
result[-1] += name
if len(result[-1]) == 0:
result.pop()
else:
index.append(len(result)-1)
if len(index) == 1 and result[index[0]] == ".param":
del result[index[0]]
for i in index[1:]:
result[i] = "+ " + result[i]
return result
def fixOption(number, operation, stack):
result = []
for i, line in enumerate(operation):
if len(line) == 0 or line[0] == "*":
result.append(line)
continue
for param in re.finditer(r'([a-zA-Z_][a-zA-Z0-9_]*)[ \t]*=[ \t]*(\'[^\']*\'|[^ \t]*)', line):
name = param.group(1)
definition = param.group(2)
if name in ["tmiflag", "modmonte", "tmipath", "etmiUsrInput"]:
# these options load the TSMC Model Interface (TMI) which isn't supported in Xyce
# TMI is generally used for modelling of silicon, transistor, and wire aging
continue
# TODO(edward.bingham) scale and geoshrink ultimately need to be
# combined, which means keeping track of the values of each in a given
# scope and generating options that combine those values as needed
if name == "scale":
result.append(".options parser " + name + "=" + definition)
elif name == "geoshrink":
result.append(".options parser scale=" + definition)
else:
# options need to be actively handled individually, all options between hspice and xyce are different
print("line " + str(number) + ": unhandled option '" + line + "'", file=sys.stderr)
continue
return result
def hspice2Xyce(path):
stack = [StackElem("")]
operation = []
command = ""
devices = "bcdefghijklmopqrstuvwxyz"
commands = "."
continuations = "+"
with open(path, 'r') as fptr:
for number, line in enumerate(fptr):
line = line.strip()
# extraneous include statements
if line.lower().startswith("include"):
line = "* " + line
if len(line) == 0 or line[0] == '*' or line[0] == '#':
pass
else:
if line[0].lower() in continuations:
pass
elif line[0].lower() in commands or line[0].lower() in devices:
# print and clear stored multiline command
emitOperation(number, command, operation, stack)
operation = []
command = re.split("[ \t]+", line)[0].lower().strip()
if line[0].lower() in commands:
if command in [".data", ".subckt", ".if", ".control"]:
stack.append(StackElem(command))
elif command == ".lib":
args = re.split("[ \t]+", line)
if len(args) <= 2:
stack.append(StackElem(command))
elif command.startswith(".end"):
if command == ".endl" and stack[-1].parent == ".lib":
stack.pop()
elif command == ".ends" and stack[-1].parent == ".subckt":
stack.pop()
elif command == ".enddata" and stack[-1].parent == ".data":
stack.pop()
elif command == ".endif" and stack[-1].parent == ".if":
stack.pop()
elif command == ".endc" and stack[-1].parent == ".control":
stack.pop()
elif command == ".end" and stack[-1].parent == "":
stack.pop()
else:
print("line " + str(number) + ": stack parse error '" + command + "' disagrees with '" + stack[-1].parent + "'", file=sys.stderr)
return
else:
print("line " + str(number) + ": unrecognized command '" + line + "'", file=sys.stderr)
return
operation.append(line)
emitOperation(-1, command, operation, stack)
operation = []
if __name__ == "__main__":
if len(sys.argv) > 1:
hspice2Xyce(sys.argv[1])
else:
print("$ hspice2xyce path")
print("Converts an HSPICE netlist to a Xyce netlist and emits result to stdout.")

41
shortenspice.py Executable file
View File

@ -0,0 +1,41 @@
#!/usr/bin/python3
import sys
import re
import datetime
import os
ckts = dict()
ids = dict()
if len(sys.argv) <= 1:
print("shortenspice.py <file>")
print("Prints the spice file, removing the template parameters from each subcircuit in favor of a unique id. This is useful for shortening subcircuit names for HSIM.")
else:
with open(sys.argv[1], 'r') as fptr:
for line in fptr:
line = line.split()
if line and line[0] == '.subckt':
ms = re.finditer(r'[a-zA-Z]', line[1])
if ms:
m = None
for m in ms:
pass
idx = m.span()[-1]
if idx < len(line[1]):
name = line[1][0:idx]
if name in ids:
ids[name] += 1
else:
ids[name] = 0
name += '_' + str(ids[name])
ckts[line[1]] = name
line[1] = name
elif line and line[0].startswith("x"):
i = 0
while i < len(line) and '=' in line[-1-i]:
i += 1
if i < len(line):
if line[-1-i] in ckts:
line[-1-i] = ckts[line[-1-i]]
print(' '.join(line))