Merge branch 'master' into xcircuit-3.10
[xcircuit.git] / lib / python / spice.py
blob91716804daf5f5505aaad8380cc972666b7a5071
1 # spice.py
2 #-----------------------------------------------------------------------
3 # Python script which writes a SPICE-format netlist.
4 # Replaces the code formerly in "netlist.c" (deprecated).
5 # Python scripting is now the preferred method for handling
6 # netlist output formats.
7 #-----------------------------------------------------------------------
9 import string
11 #-----------------------------------------------------------------------
12 # Parse the output string, substituting for index, ports, parameters,
13 # and automatic numbering using the '?' parameter.
14 #-----------------------------------------------------------------------
16 def parsedev(lstr, portdict, devdict, classidx, paramdict):
17 rstr = lstr
18 ltext = ''
19 classname = rstr[0] # single-character SPICE element type
20 try:
21 index = classidx[classname]
22 except KeyError:
23 index = 1
24 classidx[classname] = 1
26 if string.find(rstr, '%i'):
27 rstr = string.replace(rstr, '%i', str(index))
28 classidx[classname] = classidx[classname] + 1
30 for pt in portdict.keys():
31 ptstr = '%p' + pt
32 rstr = string.replace(rstr, ptstr, portdict[pt])
33 ptstr = '%p' + '"' + pt + '"'
34 rstr = string.replace(rstr, ptstr, portdict[pt])
36 for vt in paramdict.keys():
37 vtvalue = paramdict[vt]
38 if vtvalue == '?':
39 vtvalue = str(index);
40 classidx[classname] = classidx[classname] + 1
42 vtstr = '%v' + vt
43 rstr = string.replace(rstr, vtstr, vtvalue)
44 vtstr = '%v' + '"' + vt + '"'
45 rstr = string.replace(rstr, vtstr, vtvalue)
47 # Simple replacements (%%, %r, %t) (all occurrences)
48 #...................................................
49 if string.find(rstr, '%%'):
50 rstr = string.replace(rstr, '%%', '%')
51 if string.find(rstr, '%n'):
52 rstr = string.replace(rstr, '%n', devdict['name'])
53 if string.find(rstr, '%r'):
54 rstr = string.replace(rstr, '%r', '\n')
55 if string.find(rstr, '%t'):
56 rstr = string.replace(rstr, '%t', '\t')
58 return rstr
60 #-----------------------------------------------------------------------
61 # Generate a dictionary of port substitutions and create the
62 # device string for output
63 #-----------------------------------------------------------------------
65 def devwrite(calldict, devdict, classidx):
66 ltext = ''
68 # Resolve network connections to ports and store as a dictionary
69 #...............................................................
70 portdict = {}
71 ports_from = calldict['ports']
72 ports_to = devdict['ports']
73 for i in range(0,len(ports_from)):
74 portdict[textprint(ports_to[i], params)] = textprint(ports_from[i], [])
76 # Resolve instanced vs. default parameters and store as a dictionary
77 # (only for use with the "%v" statement in info labels)
78 #...............................................................
79 paramdict = {}
80 try:
81 def_params = devdict['parameters']
82 except KeyError:
83 def_params = []
85 try:
86 params = calldict['parameters']
87 except KeyError:
88 params = []
90 params += def_params[len(params):]
92 for i in range(0,len(params)):
93 paramdict[textprint(def_params[i], [])] = textprint(params[i], [])
95 # For each "spice:" info label, parse the info label
96 #...............................................................
97 w = devdict['devices']
98 for y in w:
99 for u in y:
100 lstr = select(textprint(u, params), 'spice')
101 if lstr <> '':
102 ltext += parsedev(lstr, portdict, devdict, classidx, paramdict)
103 ltext += '\n'
104 return ltext
106 #-----------------------------------------------------------------------
107 # Check if a device is "fundamental"; that is, has a "devices" dictionary
108 # with at least one entry with "spice:" for SPICE output.
109 #-----------------------------------------------------------------------
111 def isfundamental(devdict):
112 try:
113 w = devdict['devices']
114 except KeyError:
115 return 0
116 else:
117 for y in w:
118 for u in y:
119 lstr = select(textprint(u, []), 'spice')
120 if lstr <> '':
121 return 1
122 return 0
124 #-----------------------------------------------------------------------
125 # Select the device string corresponding to the given prefix.
126 # Return the body of the string, or an empty string if the prefix
127 # doesn't match.
128 #-----------------------------------------------------------------------
130 def select(sstr, prefix):
131 ltext = ''
132 if sstr.startswith(prefix):
133 ltext += sstr[len(prefix) + 1:]
134 return ltext
136 #-----------------------------------------------------------------------
137 # Generate an ASCII string from an xcircuit string (list). Translate
138 # overlines to "!" and Greek "mu" (either from Symbol font or from the
139 # ISO-Latin1 encoding of any standard font) to "u".
140 # String parameter substitution is performed if "params" is a non-empty
141 # list and the dictionary key "Parameter" is encountered in the string
142 # list "slist".
143 #-----------------------------------------------------------------------
145 def textprint(slist, params):
146 ltext = ''
147 is_symbol = 0
148 is_iso = 0
149 for x in slist:
150 try:
151 f = x.keys()[0]
152 except AttributeError: # must be a string
153 if x == 'Return':
154 ltext += '\n'
155 elif x == 'Underline':
156 ltext += '_'
157 elif x == 'Overline':
158 ltext += '!'
159 else: # is a dictionary; will have only one key
160 if f == 'Font':
161 lfont = x[x.keys()[0]]
162 if lfont.startswith('Symbol'):
163 is_symbol = 1
164 else:
165 is_symbol = 0
166 if lfont.endswith('ISO'):
167 is_iso = 1
168 else:
169 is_iso = 0
170 elif f == 'Parameter':
171 try:
172 plist = params[x[x.keys()[0]]]
173 except IndexError:
174 ltext += ' --unknown parameter %d-- ' % x[x.keys()[0]]
175 else:
176 ltext += textprint(plist, [])
177 continue;
178 else: # text: SPICE translates "mu" to "u"
179 for y in x[x.keys()[0]]:
180 if is_symbol:
181 if y == 'f':
182 ltext += 'phi'
183 elif y == 'm':
184 ltext += 'u'
185 else:
186 ltext += y
187 else:
188 if ord(y) == 181:
189 ltext += 'u'
190 elif ord(y) > 127:
191 ltext += '/' + str(ord(y))
192 else:
193 ltext += y
194 return ltext
196 #-----------------------------------------------------------------------
197 # Write netlist to the output
198 #-----------------------------------------------------------------------
200 def writespice():
201 subidx=1 # subcircuit numbering
202 devdict={} # dictionary of low-level devices
203 classidx = {} # indices of SPICE devices (other than subcircuits)
204 p=netlist() # Generate the netlist
205 g=p['globals'] # Get the list of global networks
206 c=p['circuit'] # Get the list of (sub)circuits
207 l=len(c)
208 top=c[l-1] # Top-level circuit
209 topname=top['name'] # Name of the top-level circuit
210 topname += '.spc' # Filename to write to
211 try:
212 outfile=open(topname, 'w')
213 except IOError:
214 return
216 # Print header line
217 #...............................................................
219 outfile.write('*SPICE circuit "' + topname + '"')
220 outfile.write(' from XCircuit v' + str(xc_version))
221 outfile.write(' (Python script "spice.py")\n')
223 # Print global variables. This is for hspice and should be
224 # commented out for spice3.
225 #...............................................................
227 for x in g: # 'globals' is a list of strings
228 outfile.write('.GLOBAL ' + textprint(x, []) + '\n')
229 outfile.write('\n')
231 # Print the (sub)circuits. Top level circuit does not get a "subckt"
232 # entry.
233 #...............................................................
235 for x in c: # 'circuits' is a list of dictionaries
236 is_device = 0
237 try:
238 w = x['ports'] # write circuit name and ports, if any
239 except KeyError:
240 is_top = 1
241 else:
242 is_top = 0
243 if isfundamental(x) == 0:
244 outfile.write('.subckt ' + x['name'])
245 for y in w:
246 outfile.write(' ' + textprint(y, []))
247 outfile.write('\n')
248 else:
249 is_device = 1
250 devdict[x['name']] = x
252 try:
253 v = x['calls'] # calls to subcircuits in xcircuit
254 except KeyError:
255 pass
256 else:
257 for y in v:
258 try:
259 d = devdict[y['name']]
260 except KeyError: # A SPICE subcircuit instance
261 outfile.write('X' + str(subidx))
262 subidx+=1
263 for z in y['ports']:
264 outfile.write(' ' + textprint(z, []))
265 outfile.write(' ' + y['name'] + '\n')
266 else: # A SPICE circuit element
267 nstr = devwrite(y, d, classidx);
268 outfile.write(nstr)
270 if is_top:
271 outfile.write('.end\n')
272 elif is_device == 0:
273 outfile.write('.ends\n\n')
275 #-----------------------------------------------------------------------
276 # Key binding and menu button for the spice netlist output
277 # bind('Alt_S', 'writespice')
278 #-----------------------------------------------------------------------
280 newbutton('Netlist', 'Write Spice', 'writespice')
282 #-----------------------------------------------------------------------