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 #-----------------------------------------------------------------------
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
):
19 classname
= rstr
[0] # single-character SPICE element type
21 index
= classidx
[classname
]
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():
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
]
40 classidx
[classname
] = classidx
[classname
] + 1
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')
60 #-----------------------------------------------------------------------
61 # Generate a dictionary of port substitutions and create the
62 # device string for output
63 #-----------------------------------------------------------------------
65 def devwrite(calldict
, devdict
, classidx
):
68 # Resolve network connections to ports and store as a dictionary
69 #...............................................................
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 #...............................................................
81 def_params
= devdict
['parameters']
86 params
= calldict
['parameters']
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']
100 lstr
= select(textprint(u
, params
), 'spice')
102 ltext
+= parsedev(lstr
, portdict
, devdict
, classidx
, paramdict
)
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
):
113 w
= devdict
['devices']
119 lstr
= select(textprint(u
, []), 'spice')
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
128 #-----------------------------------------------------------------------
130 def select(sstr
, prefix
):
132 if sstr
.startswith(prefix
):
133 ltext
+= sstr
[len(prefix
) + 1:]
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
143 #-----------------------------------------------------------------------
145 def textprint(slist
, params
):
152 except AttributeError: # must be a string
155 elif x
== 'Underline':
157 elif x
== 'Overline':
159 else: # is a dictionary; will have only one key
161 lfont
= x
[x
.keys()[0]]
162 if lfont
.startswith('Symbol'):
166 if lfont
.endswith('ISO'):
170 elif f
== 'Parameter':
172 plist
= params
[x
[x
.keys()[0]]]
174 ltext
+= ' --unknown parameter %d-- ' % x
[x
.keys()[0]]
176 ltext
+= textprint(plist
, [])
178 else: # text: SPICE translates "mu" to "u"
179 for y
in x
[x
.keys()[0]]:
191 ltext
+= '/' + str(ord(y
))
196 #-----------------------------------------------------------------------
197 # Write netlist to the output
198 #-----------------------------------------------------------------------
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
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
212 outfile
=open(topname
, 'w')
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')
231 # Print the (sub)circuits. Top level circuit does not get a "subckt"
233 #...............................................................
235 for x
in c
: # 'circuits' is a list of dictionaries
238 w
= x
['ports'] # write circuit name and ports, if any
243 if isfundamental(x
) == 0:
244 outfile
.write('.subckt ' + x
['name'])
246 outfile
.write(' ' + textprint(y
, []))
250 devdict
[x
['name']] = x
253 v
= x
['calls'] # calls to subcircuits in xcircuit
259 d
= devdict
[y
['name']]
260 except KeyError: # A SPICE subcircuit instance
261 outfile
.write('X' + str(subidx
))
264 outfile
.write(' ' + textprint(z
, []))
265 outfile
.write(' ' + y
['name'] + '\n')
266 else: # A SPICE circuit element
267 nstr
= devwrite(y
, d
, classidx
);
271 outfile
.write('.end\n')
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 #-----------------------------------------------------------------------