-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhspice2xyce
More file actions
executable file
·183 lines (155 loc) · 5.48 KB
/
hspice2xyce
File metadata and controls
executable file
·183 lines (155 loc) · 5.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
#!/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)
elif command in [".if", ".elif", ".elsif", ".else", ".endif"]:
pass
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
#elif name.lower().endswith("_fc"):
# continue
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.")