1 # gaf.netlist - gEDA Netlist Extraction and Generation
2 # Copyright (C) 1998-2010 Ales Hvezda
3 # Copyright (C) 1998-2010 gEDA Contributors (see ChangeLog for details)
4 # Copyright (C) 2013-2020 Roland Lutz
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software Foundation,
18 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 # --------------------------------------------------------------------------
22 # SPICE netlist backend written by S. Gieltjes
24 # further modified by W. Kazubski to use scaling parameters for
25 # devices other than MOSFETS
27 # Started with gnet-spice1.scm by W. Kazubski. Radically
28 # hacked by SDB to support advanced spice netlist generation.
29 # Project started 2003-03-05 -- SDB.
31 # Details and documentation at http://www.brorson.com/gEDA/SPICE/
34 # 2003-03-05 -- Started hacking. SDB.
35 # 2003-03-17 -- 2nd version. Hacked to allow for .SUBCKT files to model
36 # ics. Changed write-ic. Added get-file-type. Added
37 # write-subcircuit. SDB.
38 # 2003-03-31 -- 3rd version. Hacked to enable creating .SUBCKT schematics
39 # for hierarchical circuit modeling.
40 # 2003-08-29 -- 4th version. Include patches from Ken Healy to sort
41 # netlist, code by SDB to use gnetlist command line args in
42 # Scheme fcns, as well as from Theo Deckers to fix strange
43 # problem with '.SUBCKT quoting.
44 # 2003-09-09 -- 5th version. Rearranged code for more organization (I was
45 # beginning to get lost. . . .). Incorporated changes to
46 # handle external SPICE files more intelligently. Changed
47 # spew to be configurable by setting -v from the command line.
48 # Placed new fcn debug-spew into gnetlist.scm.
49 # Added -I command line flag.
50 # 2003-10-14 -- Bugfixes: Added empty-string? and hacked get-file-type to
51 # handle case where a model file has an empty line before
53 # Also modified write-net-names-on-component to gracefully
54 # handle case where not every pin has a pinseq attribute.
55 # Now only outputs pins with valid pinseq attribute.
56 # 2003-12-25 -- Bugfix: Unswizzled emission of pins from user-defined
57 # .subckts. (Now correctly uses pinseq to define emission
58 # order of pins.) Also added ability to emit attributes for
59 # semiconductors (e.g. area, off, ic, etc.) Added in response
61 # 2003-12-29 -- * Two small enhancements requested by Peter Kaiser.
63 # * Second minor bugfix.
64 # * Change res & cap to incorporate modelname & "area=" attrib.
65 # 2004-03-24 -- Bugfixes made to JFET stuff during Feb. Change released now.
66 # 2004-08-22 -- Added command line as first line of file.
67 # 2004-08-29 -- Changed sense source naming in controlled sources because
68 # the old convention was confusing ngspice.
69 # 2004-10-09 -- Added patches for voltage controlled switches from
71 # 2005-03-16 -- Fixed CCCS bug (typo in Vsense) noticed by David Logan
72 # 2005-05-16 -- Modified behavior of .INCLUDE directive. Now by default it
73 # just spits out the string instead of putting the contents of
74 # the file into the SPICE netlist. You can force insertion of
75 # the file using the -e flag.
76 # 2005-06-12 -- Changed order of writing out netlist and .model/.subckt
77 # cards to facilitate use of numparam with ngspice. Change
78 # supplied by Dominique Michel.
79 # 2005-09-11 -- Incorporated patch from Paul Bunyk to enable netlisting of
80 # Josephson junctions and "K" mutual inductances. Also
81 # enabled netlisting of "COIL" devices as inductors.
82 # 2005-12-27 -- Fix bug discovered by John Doty: spice-IO pins with refdes
83 # greater than P9 were sorted incorrectly (as strings). Now
84 # they are sorted as numbers.
85 # 2006-03-10 -- Added "m" attribute to PMOS and NMOS per request of
87 # 2006-04-11 -- Changed the .END and .ENDS cards to lowercase.
88 # This fixes bug 1442912. Carlos Nieves Onega.
89 # 2007-02-10 -- Various bugfixes. Also incorporated slotted part netlist
90 # patch from Jeff Mallatt. SDB.
91 # 2007-04-28 -- Fixed slotted part stuff so that it uses pinseq to emit
93 # 2008-01-09 -- Fix slotted part handling to work without a modified pinseq.
95 # 2011-01-03 -- Combine write-ic and write-subcircuit with a fix to the
96 # unbound type variable. Fully document a check for the
97 # special "?" value explaining why it fails silently. Clean
98 # up write-net-names-on-component to make it a bit more
100 # Combine write-probe-item and write-net-names-on-component.
101 # Add a range utility function. CC
102 # 2011-01-13 -- Add four lines of code (and some comments) that allow
103 # formaitting strings to be used for netlisting NGspice device
105 # 2011-06-12 -- Updated the Problematci name=? symbols to name=unknown and
106 # removed the FIXME check for them. This should be a step
107 # closer to place holder consistancy. CC
108 # 2015-05-14 -- Translated to Python. Roland Lutz
110 #******************************************************************************
112 # Organization of gnet-spice-sdb.scm file:
113 # -- Functions for program housekeeping, handling of calling flags,
115 # -- Functions for handling nets & devices and creating SPICE cards.
116 # -- High-level functions which control program flow. Note that the
117 # program entry point lives at the very bottom of this file.
119 # Unfortunately, no organization is present beneath this top level. . . .
121 #******************************************************************************
124 from util_getopt
import *
125 from util_repackage
import repackage
127 # Common functions for the `spice' and `spice-sdb' backends
132 # Spew debug messages if enable_debug is set, otherwise do nothing.
134 def debug_spew(debug_string
):
136 sys
.stderr
.write(debug_string
)
138 # Custom get-uref function to append ".${SLOT}" where a component has
139 # a "slot=${SLOT}" attribute attached.
141 def get_refdes(component
):
143 slot
= component
.blueprint
.get_attribute(
144 'slot', search_inherited
= False)
146 return component
.blueprint
.refdes
148 return component
.blueprint
.refdes
+ '.' + slot
150 ############ Program housekeeping, handling calling flags, etc. ############
152 # Loops through the model-file list, and for each file name discovered
153 # in the list, it processes the file by invoking handle-spice-file.
155 def loop_through_files(f
, file_info_list
):
156 for list_element
in file_info_list
:
157 model_name
= list_element
[0]
158 file_name
= list_element
[1]
159 file_type
= list_element
[2]
160 handle_spice_file(f
, file_name
)
162 # Loops through the model-file list looking for triplet corresponding
163 # to model-name. If found, it returns the corresponding list. If not
164 # found, returns False.
166 def get_file_info_list_item(model_name
, file_info_list
):
167 for list_element
in file_info_list
:
168 list_elt_model_name
= list_element
[0]
169 list_elt_file_name
= list_element
[1]
170 list_elt_file_type
= list_element
[2]
172 if list_elt_model_name
== model_name
:
173 # found model_name. Return list_element.
178 # This wraps insert_text_file.
180 # It looks to see if the -I flag was set at the command line. If so,
181 # it just writes a .INCLUDE card with the file name. If not, it calls
182 # insert_text_file to stick the file's contents into the SPICE netlist.
184 def handle_spice_file(f
, file_name
):
185 debug_spew("Handling spice model file %s\n" % file_name
)
186 if 'include_mode' in calling_flags
:
187 # -I found: just print out .INCLUDE card
188 f
.write('.INCLUDE %s\n' % file_name
)
190 # -I not found: invoke insert_text_file
191 insert_text_file(f
, file_name
)
193 # Given a filename, open the file, get the contents, and dump them
194 # into the spice file.
196 # This function is usually used to include spice models contained in
197 # files into the netlist. Note that it doesn't check the correctness
198 # of the spice code in the file -- you're on your own!
200 def insert_text_file(f
, model_filename
):
201 if not os
.path
.isfile(model_filename
):
202 sys
.stderr
.write("ERROR: File '%s' not found.\n" % model_filename
)
205 model_file
= open(model_filename
)
207 f
.write('*vvvvvvvv Included SPICE model from %s vvvvvvvv\n'
210 for model_line
in model_file
:
215 f
.write('*^^^^^^^^ End of included SPICE model from %s ^^^^^^^^\n'
219 # Figure out if this schematic is a .SUBCKT lower level. This is
220 # determined if there is a spice-subcircuit-LL device instantiated
221 # somewhere on the schematic. If it is a .SUBCKT, return ".SUBCKT
224 def get_schematic_type(packages
):
225 for package
in reversed(packages
):
226 if package
.get_attribute('device', None) == 'spice-subcircuit-LL':
227 # look for subcircuit label
228 return '.SUBCKT ' + package
.get_attribute('model-name', 'unknown')
230 # no spice-subcircuit-LL found
231 return 'normal schematic'
233 # Extract the modelname from the .SUBCKT modelname line.
234 # Just grab the chars from char 8 to the end of the string.
236 def get_subcircuit_modelname(schematic_type
):
237 return schematic_type
[8:]
239 # This iterates through the schematic and compiles a list of all
240 # spice-IO pins found. This is used when writing out a .SUBCKT lower
243 def get_spice_IO_pins(packages
):
244 spice_io_package_list
= []
245 for package
in reversed(packages
):
246 # look for subcircuit label
247 if package
.get_attribute('device', None) == 'spice-IO':
248 # we have found a spice-IO pin.
249 spice_io_package_list
.append(package
)
251 # end iteration & return list
252 spice_io_package_list
.reverse()
253 return spice_io_package_list
255 # This takes the list of io-pin-packages and sorts it in order of
256 # refdes. Repaired on 2005-12-27 to correctly sort pin numbers > 9.
258 def sort_spice_IO_pins(package_list
):
259 return sort(package_list
, key
= lambda x
: int(x
.refdes
[1:]))
261 # Given a list of spice-IO packages (refdeses), this function returns
262 # the list of nets attached to the IOs.
264 def get_IO_nets(package_list
):
266 for package
in package_list
:
267 # get the net attached to pin 1
268 net_list
.append(package
.pins_by_number
['1'].net
.name
)
270 # end iteration & return net_list
274 # Given a filename, open the file, get the first line, and see if it
275 # is a .MODEL or .SUBCKT file. Returns either ".MODEL" or ".SUBCKT"
276 # or "OTHER". The function opens the model file, and closes it when
279 def get_file_type(model_filename
):
280 if not os
.path
.isfile(model_filename
):
281 sys
.stderr
.write("ERROR: File '%s' not found.\n" % model_filename
)
284 model_file
= open(model_filename
)
286 for file_line
in model_file
:
287 if file_line
.startswith('.'):
288 debug_spew("In get_file_type, first_char = .\n")
289 if file_line
[:7].lower() == '.subckt':
290 # found .subckt as first line.
292 if file_line
[:6].lower() == '.model':
293 # found .model as first line.
295 # first . spice card is neither .model nor .subckt
298 # Arrived at end of line without finding .MODEL or .SUBCKT.
303 # Write prefix if first char of refdes is improper, e.g. if MOSFET is
304 # named T1 then becomes MT1 in SPICE.
306 def write_prefix(f
, package
, prefix
):
307 different_prefix
= package
.refdes
[0] != prefix
308 nomunge
= 'nomunge_mode' in calling_flags
310 debug_spew("Checking prefix. Package prefix =%s\n" % package
.refdes
[0])
311 debug_spew(" correct prefix =%s\n" % prefix
)
312 debug_spew(" nomunge mode = %s\n different_prefix=%s\n"
313 % (nomunge
, different_prefix
))
314 if different_prefix
and not nomunge
:
317 # Sort procedure to order refdes's alphabetically but keep A? packages
318 # at the end of list so SPICE simulation directives operate correctly.
320 # This fcn written by Ken Healy to enable SPICE netlisting for Gnucap,
321 # which wants A refdes cards (i.e. SPICE directives) to appear last in
322 # the SPICE netlist. Slightly modified and incorporated into main
323 # spice-sdb release by SDB on 2003-09-01. To output the netlist in
324 # sorted order, use the -s switch when invoking gnetlist from the
327 # Example: gnetlist -s -g spice-sdb -o output.spice schematic.sch
329 # The default behavior (i.e. if -s is not specified) is to do no
333 xdes
= x
.refdes
[0].lower()
334 ydes
= y
.refdes
[0].lower()
335 xnum
= x
.refdes
[1:].lower()
336 ynum
= y
.refdes
[1:].lower()
344 if xdes
== 'a': # and ydes != 'a'
346 if ydes
== 'a': # and xdes != 'a'
355 ################ Dealing with nets, devices, & SPICE cards. ################
357 # Write-transistor-diode: writes out component followed by model or
358 # model file associated with the component.
359 # This function does the following:
360 # 1. Writes out the correct refdes prefix (if specified and necessary).
361 # 2. Writes out the refdes and nets
362 # 3. Looks for "model-name" attribute. Writes it out if it exists.
363 # 4. If there is no "model-name" attribute, it writes out the "value"
364 # attribute. If there is no "value" attribute, it writes out "unknown"
365 # and returns, causing the spice simulator to puke when the netlist
366 # is run. This is important
367 # 'cause the spice simulator needs to have some indication of what
369 # 5. Outputs optional attributes attached to device, if any. Feature
370 # added by SDB on 2003-12-25.
371 # 6. Outputs a new line
372 # 7. Looks for a the "model" attribute. If it exists, it it writes out
373 # a .MODEL line like this: .MODEL model-name type (model)
375 def write_transistor_diode(f
, package
, prefix
, type, attrib_list
):
376 # First do local assignments
377 model_name
= package
.get_attribute('model-name', None)
378 model
= package
.get_attribute('model', None)
379 value
= package
.get_attribute('value', None)
380 area
= package
.get_attribute('area', None)
381 off
= package
.get_attribute('off', None)
382 model_file
= package
.get_attribute('file', None)
384 # Write out the refdes prefix, if specified and necessary.
386 write_prefix(f
, package
, prefix
)
388 # Next we write out the refdes and nets.
389 write_component_no_value(f
, package
)
391 # next look for "model-name" attribute. Write it out if it exists.
392 # otherwise look for "value" attribute.
393 if model_name
is not None:
394 # display model-name if known
395 f
.write(model_name
+ ' ')
396 elif value
is not None:
397 # otherwise display value
402 # Next write out attributes if they exist
403 # First attribute is area. It is written as a simple string
407 # Next attribute is off. It is written as a simple string
411 # Write out remaining attributes
412 spice_common
.write_list_of_attributes(f
, package
, attrib_list
)
414 # Now write out newline in preparation for writing out model.
417 # Now write out any model which is pointed to by the part.
418 # one line model and model name exist
419 if model
is not None and model_name
is not None:
420 debug_spew("found model and model-name for %s\n" % package
.refdes
)
421 f
.write('.MODEL %s %s (%s)\n' % (model_name
, type, model
))
422 # one line model and component value exist
423 elif model_name
is not None and value
is not None:
424 debug_spew("found model and value for %s\n" % package
.refdes
)
425 f
.write('.MODEL %s %s (%s)\n' % (model_name
, type, value
))
426 # model file and model name exist
427 elif model_file
is not None and model_name
is not None:
428 debug_spew("found file and model-name for %s\n" % package
.refdes
)
429 debug_spew("I'll deal with the file later . . .\n")
430 # model file and component value exist
431 elif model_file
is not None and value
is not None:
432 debug_spew("found file and value for %s\n" % package
.refdes
)
433 debug_spew("I'll deal with the file later . . .\n")
435 # This writes out a valid diode refdes & then calls the function which
436 # writes the rest of the line.
438 def write_diode(f
, package
):
439 debug_spew("Found diode. Refdes = %s\n" % package
.refdes
)
440 write_transistor_diode(f
, package
, 'D', 'D', ['ic', 'temp'])
442 # This writes out a valid ic or subcircuit line.
443 # The algorithm is as follows:
444 # 1. Figure out what type of model goes with this part from
445 # file-info-list. If it isn't listed, look for a MODEL attribute.
446 # If MODEL attribute is attached, write out SPICE card, and then
447 # write out .MODEL on next line.
448 # If no MODEL attribute is attached, just write out what little
449 # we know. Then return
450 # 2. If the model-name is in the file-info-list, get the associated
451 # file-type. Compare it against the component's refdes. If model-type
452 # is .MODEL or .SUBCKT and refdes doesn't begin with a U or X
453 # respectively, prepend the correct prefix to the refdes.
454 # 3. Print out the rest of the line.
456 def write_ic(f
, package
, file_info_list
):
457 # First do local assignments
458 first_char
= package
.refdes
[0] # extract first char of refdes
459 value
= package
.get_attribute('value', 'unknown')
460 # If model-name is empty, we use value attribute instead.
461 model_name
= package
.get_attribute('model-name', value
)
462 model
= package
.get_attribute('model', None)
463 type = package
.get_attribute('type', None)
465 if first_char
== 'U':
466 debug_spew("Found ic. Refdes = %s\n" % package
.refdes
)
467 elif first_char
== 'X':
468 debug_spew("Found subcircuit. Refdes = %s\n" % package
.refdes
)
470 # Now get item from file_info_list using model_name as key
471 list_item
= get_file_info_list_item(model_name
, file_info_list
)
473 # check to see if list_item is null.
474 if list_item
is None:
475 # Evidently, we didn't discover any files holding this model.
476 # Instead we look for model attribute
477 if model
is not None:
478 # model attribute exists -- write out card and model.
479 debug_spew("Model info not found in model file list, "
480 "but model attribute exists. "
481 "Write out spice card and .model line..\n")
482 write_component_no_value(f
, package
)
483 f
.write(model_name
+ '\n')
484 f
.write('.MODEL %s ' % model_name
)
487 # If no type then just skip it.
488 f
.write('(%s)\n' % model
)
490 # no model attribute either. Just write out card.
491 debug_spew("Model info not found in model file list. No "
492 "model attribute either. Just write what we know.\n")
493 write_component_no_value(f
, package
)
494 f
.write(model_name
+ '\n')
497 # list_item is not null. Therefore we process line depending
498 # upon contents of list_item
499 file_type
= list_item
[2]
501 if file_type
== '.MODEL':
502 # ---- file holds a model ----
503 debug_spew("Found .MODEL with model-file and model-name for %s\n"
505 write_prefix(f
, package
, 'U')
506 # this prepends an "U" to the refdes if needed, since we
508 write_component_no_value(f
, package
)
509 f
.write(model_name
+ '\n')
510 debug_spew("We'll handle the file contents later . . .\n")
512 elif file_type
== '.SUBCKT':
513 # ---- file holds a subcircuit ----
514 debug_spew("Found .SUBCKT with model-file and model-name for %s\n"
516 write_prefix(f
, package
, 'X')
517 # this prepends an "X" to the refdes if needed, since we
519 write_component_no_value(f
, package
)
520 f
.write(model_name
+ '\n')
521 debug_spew("We'll handle the file contents later . . .\n")
523 # This writes out a valid transistor refdes & then calls the function
524 # which writes the rest of the line.
526 def write_npn_bipolar_transistor(f
, package
):
527 debug_spew("Found npn bipolar transistor. Refdes = %s\n" % package
.refdes
)
528 write_transistor_diode(f
, package
, 'Q', 'NPN', ['ic', 'temp'])
530 def write_pnp_bipolar_transistor(f
, package
):
531 debug_spew("Found pnp bipolar transistor. Refdes = %s\n" % package
.refdes
)
532 write_transistor_diode(f
, package
, 'Q', 'PNP', ['ic', 'temp'])
534 # Write n-channel JFET transistor.
536 def write_nfet_transistor(f
, package
):
537 debug_spew("Found n-channel JFET. Refdes = %s\n" % package
.refdes
)
538 write_transistor_diode(f
, package
, 'J', 'NJF', ['ic', 'temp'])
540 # Write p-channel JFET transistor.
542 def write_pfet_transistor(f
, package
):
543 debug_spew("Found p-channel JFET. Refdes = %s\n" % package
.refdes
)
544 write_transistor_diode(f
, package
, 'J', 'PJF', ['ic', 'temp'])
546 def write_pmos_transistor(f
, package
):
547 debug_spew("Found PMOS transistor. Refdes = %s\n" % package
.refdes
)
548 write_transistor_diode(f
, package
, 'M', 'PMOS', [
549 'l', 'w', 'as', 'ad', 'pd', 'ps', 'nrd', 'nrs', 'temp', 'ic', 'm'])
551 def write_nmos_transistor(f
, package
):
552 debug_spew("Found NMOS transistor. Refdes = %s\n" % package
.refdes
)
553 write_transistor_diode(f
, package
, 'M', 'NMOS', [
554 'l', 'w', 'as', 'ad', 'pd', 'ps', 'nrd', 'nrs', 'temp', 'ic', 'm'])
556 def write_subckt_pmos_transistor(f
, package
):
557 debug_spew("Found PMOS subcircuit transistor. "
558 "Refdes = %s\n" % package
.refdes
)
559 write_transistor_diode(f
, package
, 'X', 'PMOS', [
560 'l', 'w', 'as', 'ad', 'pd', 'ps', 'nrd', 'nrs', 'temp', 'ic', 'm'])
562 def write_subckt_nmos_transistor(f
, package
):
563 debug_spew("Found NMOS subcircuit transistor. "
564 "Refdes = %s\n" % package
.refdes
)
565 write_transistor_diode(f
, package
, 'X', 'NMOS', [
566 'l', 'w', 'as', 'ad', 'pd', 'ps', 'nrd', 'nrs', 'temp', 'ic', 'm'])
568 # ************ Fix this!!!!!!!!!! **************
569 def write_mesfet_transistor(f
, package
):
570 write_transistor_diode(f
, package
, 'Z', 'MESFET', [])
573 # Write voltage controlled switch.
575 def write_vc_switch(f
, package
):
576 debug_spew("Found voltage controlled switch. "
577 "Refdes = %s\n" % package
.refdes
)
578 write_transistor_diode(f
, package
, 'S', 'SW', [' '])
580 def write_resistor(f
, package
):
581 debug_spew("Found resistor. Refdes = %s\n" % package
.refdes
)
583 # first write out refdes and attached nets
584 write_component_no_value(f
, package
)
586 # next write out mandatory resistor value if it exists.
587 value
= package
.get_attribute('value', None)
588 if value
is not None:
591 # next write our model name if it exists
592 model_name
= package
.get_attribute('model-name', None)
593 if model_name
is not None:
594 f
.write(model_name
+ ' ')
596 # write the attributes (if any) separately
597 # I include non-standard "area" attrib here per popular demand
598 # in the list of attributes which can be attached to a resistor.
599 spice_common
.write_list_of_attributes(
600 f
, package
, ['area', 'l', 'w', 'temp'])
601 # add additional space. . . .
604 # finally output a new line
607 def write_capacitor(f
, package
):
608 debug_spew("Found capacitor. Refdes = %s\n" % package
.refdes
)
610 # first write out refdes and attached nets
611 write_component_no_value(f
, package
)
613 # next write capacitor value, if any. Note that if the
614 # component value is not assigned nothing will be written out.
615 value
= package
.get_attribute('value', None)
616 if value
is not None:
619 # next write capacitor model name, if any. This is applicable to
620 # semiconductor caps used in chip design.
621 model_name
= package
.get_attribute('model-name', None)
622 if model_name
is not None:
623 f
.write(model_name
+ ' ')
625 # Next write out attributes if they exist. Use
626 # a list of attributes which can be attached to a capacitor.
627 # I include non-standard "area" attrib here per request of Peter Kaiser.
628 spice_common
.write_list_of_attributes(
629 f
, package
, ['area', 'l', 'w', 'ic'])
630 # write the off attribute separately
631 # add additional space. . . .
636 def write_inductor(f
, package
):
637 debug_spew("Found inductor. Refdes = %s\n" % package
.refdes
)
639 # first write out refdes and attached nets
640 write_component_no_value(f
, package
)
642 # next write inductor value, if any. Note that if the
643 # component value is not assigned, then it will write "unknown"
644 f
.write(package
.get_attribute('value', 'unknown'))
646 spice_common
.write_list_of_attributes(
647 f
, package
, ['l', 'w', 'ic'])
648 # write the off attribute separately
649 # add additional space. . . .
654 # The behavior of the voltage source is held in the "value" attribute.
656 def write_independent_voltage_source(f
, package
):
657 debug_spew("Found independent voltage source. "
658 "Refdes = %s\n" % package
.refdes
)
660 # first write out refdes and attached nets
661 write_component_no_value(f
, package
)
663 # next write voltage value, if any. Note that if the
664 # voltage value is not assigned, then it will write "unknown"
665 f
.write(package
.get_attribute('value', 'unknown'))
669 # The behavior of the current source is held in the "value" attribute.
671 def write_independent_current_source(f
, package
):
672 debug_spew("Found independent current source. "
673 "Refdes = %s\n" % package
.refdes
)
675 # first write out refdes and attached nets
676 write_component_no_value(f
, package
)
678 # next write current value, if any. Note that if the
679 # current value is not assigned, then it will write "unknown"
680 f
.write(package
.get_attribute('value', 'unknown'))
684 # Write Josephson junction in wrspice format. Paul Bunyk, Sep 2, 2005
686 def write_josephson_junction(f
, package
):
687 debug_spew("Found Josephson junction. Refdes = %s\n" % package
.refdes
)
689 # first write out refdes and attached nets
690 write_component_no_value(f
, package
)
692 # next, add a dummy node for JJ phase. Unlike in Xic netlister, give it
693 # a reasonable name, not a number, e.g., refdes.
694 f
.write(package
.refdes
+ ' ')
696 # next write JJ model name, if any.
697 model_name
= package
.get_attribute('model-name', None)
698 if model_name
is not None:
699 f
.write(model_name
+ ' ')
701 # Next write out attribtes if they exist. Use
702 # a list of attributes which can be attached to a junction.
703 spice_common
.write_list_of_attributes(f
, package
, ['area'])
704 # write the off attribute separately
705 # add additional space. . . .
710 # Write mutual inductance (actually K). Paul Bunyk, Sep 2, 2005
712 def write_coupling_coefficient(f
, package
):
713 debug_spew("Found mutual inductance. Refdes = %s\n" % package
.refdes
)
715 # first write out refdes and attached nets (none)
716 write_component_no_value(f
, package
)
718 # next two inductor names and value
719 inductors
= package
.get_attribute('inductors', None)
720 value
= package
.get_attribute('value', None)
721 if inductors
is not None:
722 f
.write(inductors
+ ' ')
723 if value
is not None:
728 # Write a voltage probe.
730 def write_probe(f
, package
):
731 # fetch only one attr we care about, so far
732 value
= package
.get_attribute('value', 'TRAN')
734 debug_spew("Found Probe item, refdes = %s\n" % package
.refdes
)
736 f
.write('* Probe device %s on nets ' % packager
.refdes
)
737 write_net_names_on_component(f
, package
)
739 f
.write('.print %s +' % value
)
741 fmt
= ' '.join(['V(%s)'] * len(package
.pins
))
742 write_net_names_on_component(f
, package
, fmt
)
745 # Given a refdes, and optionally a format string, this writes out the
746 # nets attached to the component's pins. If it's not called with a
747 # format string it looks for one in the net-format attribute,
748 # otherwise it writes out the pins unformatted. This is used to write
749 # out non-slotted parts.
751 def write_net_names_on_component(f
, package
, format
= None):
753 for i
in range(1, len(package
.pins
) + 1):
754 # Get net name for pinseq i and add it to netnames,
755 # unless it is "ERROR_INVALID_PIN".
757 # ------- Super debug stuff --------
759 debug_spew(" In write_net_names_on_component. . . . \n")
760 debug_spew(" pin-name = %s\n" % str(i
))
761 debug_spew(" pinnumber = %s\n" %
762 package
.get_pin_by_pinseq(i
).get_attribute('pinnumber', 'unknown'))
763 debug_spew(" pinseq = %s" %
764 package
.get_pin_by_pinseq(i
).get_attribute('pinseq', 'unknown'))
765 if str(i
) != package
.get_pin_by_pinseq(i
).get_attribute('pinseq', 'unknown'):
766 debug_spew(" <== INCONSISTENT!\n")
769 debug_spew(" netname = %s\n",
770 spice_common
.get_net_name(package
,
771 package
.get_pin_by_pinseq(i
).get_attribute('pinnumber', 'unknown')))
772 # -------------------------------------
775 pin
= package
.get_pin_by_pinseq(i
)
777 debug_spew("For %s, found pin with no pinseq attribute. "
778 "Ignoring. . . .\n" % package
.refdes
)
780 netnames
.append(spice_common
.get_net_name(package
, pin
.number
))
782 # Format agument take priority, otherwise use attribute
784 format
= package
.get_attribute('net-format', None)
788 f
.write(''.join(netname
+ ' ' for netname
in netnames
))
790 # write out nets with format string
791 f
.write(format
% netnames
)
793 # Write the refdes and the net names connected to pins on this
794 # component. No return, and no component value is written, or extra
795 # attribs. Those are handled later.
797 def write_component_no_value(f
, package
):
798 f
.write(package
.refdes
+ ' ')
799 # write component refdes
800 write_net_names_on_component(f
, package
)
802 # Given a refdes, returns the device attribute "value" as string.
803 # Used when "value" is an optional attribute. Returns "unknown" if
806 def component_optional_value(package
):
807 value
= package
.get_attribute('value', None)
812 # Given a refdes, returns the device attribute "model" as string.
814 def component_model(package
):
815 # This returns either a string or a function!?
816 model
= package
.get_attribute('model', None)
818 return spice_common
.component_value
# TODO: looks wrong
821 # Include SPICE statements from a SPICE directive block.
823 def write_directive(f
, package
):
824 # Collect variables used in creating spice code
825 value
= package
.get_attribute('value', None)
826 file = package
.get_attribute('file', None)
828 debug_spew("Found SPICE directive box. Refdes = %s\n" % package
.refdes
)
830 # First look to see if there is a value.
831 if value
is not None:
832 f
.write(value
+ '\n')
833 debug_spew("Appending value = \"%s\" to output file.\n" % value
)
834 # since there is no value, look for file.
835 elif file is not None:
836 insert_text_file(f
, file)
837 # Note that we don't wait until the end here. Is that OK?
838 debug_spew("Inserting contents of file = %s into output file.\n" % file)
840 # Include a file using an .INCLUDE directive
841 # Changed on 2005-06-12: to embed the contents of the file,
842 # you must call gnetlist with the -e flag set.
844 def write_include(f
, package
):
845 file = package
.get_attribute('file', None)
846 debug_spew("Found SPICE include box. Refdes = %s\n" % package
.refdes
)
849 debug_spew("silently skip \"unknown\" file.\n")
852 if 'embedd_mode' in calling_flags
:
853 # -e found: invoke insert_text_file
854 insert_text_file(f
, file)
855 debug_spew("embedding contents of file %s into netlist.\n" % file)
857 # -e not found: just print out .INCLUDE card
858 f
.write('.INCLUDE %s\n' % file)
859 debug_spew("placing .include directive string into netlist.\n")
861 # Include an option using an .OPTIONS directive.
863 def write_options(f
, package
):
864 debug_spew("Found .OPTIONS box. Refdes = %s\n" % package
.refdes
)
865 f
.write('.OPTIONS %s\n' % spice_common
.component_value(package
))
867 # Include a spice model (instantiated as a model box on the schematic).
868 # Two types of model can be included:
869 # 1. An embedded model, which is a one- or multi-line string held in
870 # the attribute "model".
871 # In this case, the following attributes are mandatory:
872 # -- model (i.e. list of parameter=value strings)
875 # In this case, the function creates and formats the correct
876 # spice model line(s).
877 # 2. A model held in a file whose name is held in the attribute "file"
878 # In this case, the following attribute are mandatory:
879 # -- file (i.e. list of parameter=value strings)
880 # In this case, the function just opens the file and dumps the
881 # contents into the netlist.
883 def write_model(f
, package
):
884 # Collect variables used in creating spice code
885 model_name
= package
.get_attribute('model-name', None)
886 model_file
= package
.get_attribute('file', None)
887 model
= package
.get_attribute('model', None)
889 debug_spew("Found .MODEL box. Refdes = %s\n" % package
.refdes
)
891 # Now, depending upon what combination of model, model_file, and
892 # model_name exist (as described above) write out lines into spice
894 if model
is not None and model_name
is not None:
895 # one model and model name exist
896 debug_spew("found model and model_name for %s\n" % package
.refdes
)
897 f
.write('.MODEL %s %s (%s)\n' % (
898 model_name
, package
.get_attribute('type', 'unknown'), model
))
899 elif model_file
is not None:
901 debug_spew("found model_file for %s\n" % package
.refdes
)
902 # insert_text_file(f, model_file)
903 # don't write it out -- it's handled after the second pass.
905 # This writes out the default component (i.e. the "device" attribute
906 # was not recognized). This function does the following:
908 # 1. Gets the refdes (package).
909 # 2. Checks the refdes against a short list of possible values.
910 # Depending upon the refdes, it does the following thing:
911 # A? -- Invokes write-ic. This provides the opportunity for a
912 # code model which may include a .model line.
913 # D? -- Invokes write-diode
914 # Q? -- Invokes write-transistor-diode. (The "type" attribute is
915 # <unknown> in this case so that the spice simulator will
916 # barf if the user has been careless.)
918 # U? -- Invokes write-ic. This provides the opportunity for a
919 # component model to be instantiated.
920 # X? -- Invokes write-ic. This provides the opportunity for a
921 # component subcircuit to be instantiated.
922 # V? -- Invokes write-independent-voltage-source
923 # I? -- Invokes write-independent-current-source
924 # Otherwise, it just outputs the refdes, the attached nets, and
925 # the value of the "value" attribute.
927 def write_default_component(f
, package
, file_info_list
):
928 # extract first char of refdes.
929 first_char
= package
.refdes
[0]
931 if first_char
== 'A':
932 write_ic(f
, package
, file_info_list
)
933 elif first_char
== 'D':
934 write_diode(f
, package
)
935 elif first_char
== 'Q' or first_char
== 'M':
936 write_transistor_diode(f
, package
, False, '<unknown>', [])
937 elif first_char
== 'U':
938 write_ic(f
, package
, file_info_list
)
939 elif first_char
== 'V':
940 write_independent_voltage_source(f
, package
)
941 elif first_char
== 'I':
942 write_independent_current_source(f
, package
)
943 elif first_char
== 'X':
944 write_ic(f
, package
, file_info_list
)
946 package
.warn("unknown component")
947 write_component_no_value(f
, package
)
948 # write component value, if components have a label "value=#"
949 # what if a component has no value label, currently unknown is written
950 f
.write(spice_common
.component_value(package
))
954 ################# High-level functions for program control #################
956 # This function is passed a list of refdesses (ls). It uses each
957 # refdes to get the corresponding "device" attribute. Depending upon
958 # the device, it then invokes one or another of the spice line output
959 # fcns to output a line of the spice netlist. I have enlarged the
960 # number of devices it recognizes -- SDB.
961 # Write the refdes, to the pin# connected net and component value and
962 # optional extra attributes. Check if the component is a special
965 def write_netlist(f
, file_info_list
, ls
):
967 device
= package
.get_attribute('device', 'unknown')
969 # Super debug stuff -- outputs line describing device being processed.
970 debug_spew("--- checking package = %s\n" % package
.refdes
)
971 debug_spew(" device = %s\n" % device
)
974 pass # do nothing for graphical symbols.
975 elif device
== 'spice-subcircuit-LL':
976 pass # do nothing for subcircuit declaration.
977 elif device
== 'spice-IO':
978 pass # do nothing for SPICE IO pins.
979 elif device
== 'spice-title':
980 pass # do nothing for spice title blocks
981 elif device
== 'SPICE-ccvs':
982 spice_common
.write_ccvs(f
, package
)
983 elif device
== 'SPICE-cccs':
984 spice_common
.write_cccs(f
, package
)
985 elif device
== 'SPICE-vcvs':
986 spice_common
.write_vcvs(f
, package
)
987 elif device
== 'SPICE-vccs':
988 spice_common
.write_vccs(f
, package
)
989 elif device
== 'SPICE-nullor':
990 spice_common
.write_nullor(f
, package
)
991 elif device
== 'DIODE':
992 write_diode(f
, package
)
993 elif device
== 'PMOS_TRANSISTOR':
994 write_pmos_transistor(f
, package
)
995 elif device
== 'NMOS_TRANSISTOR':
996 write_nmos_transistor(f
, package
)
997 elif device
== 'PNP_TRANSISTOR':
998 write_pnp_bipolar_transistor(f
, package
)
999 elif device
== 'SPICE-PNP':
1000 write_pnp_bipolar_transistor(f
, package
)
1001 elif device
== 'NPN_TRANSISTOR':
1002 write_npn_bipolar_transistor(f
, package
)
1003 elif device
== 'SPICE-NPN':
1004 write_npn_bipolar_transistor(f
, package
)
1005 elif device
== 'PFET_TRANSISTOR':
1006 write_pfet_transistor(f
, package
)
1007 elif device
== 'NFET_TRANSISTOR':
1008 write_nfet_transistor(f
, package
)
1009 elif device
== 'MESFET_TRANSISTOR':
1010 write_mesfet_transistor(f
, package
)
1011 elif device
== 'SPICE-VC-switch':
1012 write_vc_switch(f
, package
)
1013 elif device
== 'RESISTOR':
1014 write_resistor(f
, package
)
1015 elif device
== 'CAPACITOR':
1016 write_capacitor(f
, package
)
1017 elif device
== 'POLARIZED_CAPACITOR':
1018 write_capacitor(f
, package
) # change someday
1019 elif device
== 'INDUCTOR':
1020 write_inductor(f
, package
)
1021 elif device
== 'COIL':
1022 # Added to enable netlisting of coil-*.sym
1023 write_inductor(f
, package
)
1024 elif device
== 'VOLTAGE_SOURCE':
1025 write_independent_voltage_source(f
, package
)
1027 elif device
== 'CURRENT_SOURCE':
1028 write_independent_current_source(f
, package
)
1030 elif device
== 'JOSEPHSON_JUNCTION':
1031 write_josephson_junction(f
, package
)
1033 write_coupling_coefficient(f
, package
)
1034 elif device
== 'model':
1035 write_model(f
, package
)
1036 elif device
== 'options':
1037 write_options(f
, package
)
1038 elif device
== 'directive':
1039 write_directive(f
, package
)
1040 elif device
== 'include':
1041 write_include(f
, package
)
1042 elif device
== 'TESTPOINT':
1043 write_probe(f
, package
)
1044 elif device
== 'SUBCKT_PMOS':
1045 write_subckt_pmos_transistor(f
, package
)
1046 elif device
== 'SUBCKT_NMOS':
1047 write_subckt_nmos_transistor(f
, package
)
1049 write_default_component(f
, package
, file_info_list
)
1051 # This runs through the list of packages (refdesses), and for each
1052 # gets the attributes. If there is a "FILE" attribute, it gets the
1053 # file info & uses it to build the file-info-list. When done, it
1054 # returns the file_info_list.
1056 def create_file_info_list(packages
):
1058 for package
in reversed(packages
):
1059 model
= package
.get_attribute('model-name', 'unknown')
1060 model_file
= package
.get_attribute('file', None)
1062 # Now run a series of checks to see if we should stick this
1063 # file into the file_info_list
1065 # Check to see if "file" attribute is non-empty
1066 if model_file
is None:
1069 debug_spew("found file attribute for %s. File name = %s\n"
1070 % (package
.refdes
, model_file
))
1072 # Now check to see if file is in file_info_list
1073 if is_in_file_info_list(model_file
, file_info_list
):
1074 # File is already in list. Print debug spew if desired.
1075 debug_spew("File has already been seen and entered into "
1076 "known model file list.\n")
1079 # File is new. Open file, find out what type it is, and push
1080 # info into file_info_list
1081 file_type
= get_file_type(model_file
)
1082 debug_spew("File is new. New file type is %s \n" % file_type
)
1084 # Check to see if file_type is known.
1085 if file_type
== 'OTHER':
1086 # File type is not a model type. Don't stick it in list.
1087 # Print debug spew if desired.
1088 debug_spew("File type is OTHER, and therefore will not be "
1089 "entered in known model file list.\n")
1092 # file_type is OK. Return file_info_list with new triplet attached.
1093 debug_spew("Inserting %s into list of known model files.\n"
1095 file_info_list
.append((model
, model_file
, file_type
))
1097 # end of packages processed. Return file_info_list
1098 file_info_list
.reverse()
1099 return file_info_list
1101 # Helper function. Returns True if file is already in file_info_list,
1102 # otherwise False. Assumes file_info_list of form:
1103 # [(model1, file1, file_type1), (model2, file2, file_type2), ...]
1105 def is_in_file_info_list(model_file
, file_info_list
):
1106 for list_element
in file_info_list
:
1107 list_file_name
= list_element
[1]
1108 if list_file_name
== model_file
:
1109 return True # item found
1114 # Write out spice netlist header
1116 def write_top_header(f
):
1117 f
.write('*********************************************************\n')
1118 f
.write('* Spice file generated by gnetlist *\n')
1119 f
.write('* spice-sdb version 4.28.2007 by SDB -- *\n')
1120 f
.write('* provides advanced spice netlisting capability. *\n')
1121 f
.write('* Documentation at http://www.brorson.com/gEDA/SPICE/ *\n')
1122 f
.write('*********************************************************\n')
1124 # Write out .SUBCKT netlist header
1126 def write_subcircuit_header(f
):
1127 f
.write('*******************************\n')
1128 f
.write('* Begin .SUBCKT model *\n')
1129 f
.write('* spice-sdb ver 4.28.2007 *\n')
1130 f
.write('*******************************\n')
1132 # Spice netlist generation.
1133 # This is the entry point.
1135 # Hacked on 2003-03-31 to enable writing out .SUBCKT models -- SDB.
1136 # Hacked again in Sept 2003 to enable more intelligent embedding of
1137 # external SPICE files into netlist -- SDB.
1138 # The algorithm is as follows:
1139 # 1. Figure out if there is a .SUBCKT block on the schematic,
1140 # or if it is just a normal schematic.
1142 # -- Write out subcircuit header (a comment identifying the netlister).
1143 # -- find all spice-IO pins. Get a list of the packages.
1144 # -- put them in order (ordered by package refdes)
1145 # -- get the list of nets attached to the spice-IO pins.
1146 # -- write out .SUBCKT line
1147 # If a normal schematic:
1148 # -- Write out top header (a comment identifying the netlister).
1149 # 2. Loop through all components, looking for components with a "file"
1150 # attribute. Every time a "file" attribute is found do this:
1151 # -- Open the file and find out what kind of file it is (.SUBCKT
1153 # -- Determine if the file has previously been processed. If
1154 # not: stick the following info into the file-info list:
1155 # (model_name, file_name, file_type). Otherwise just continue.
1156 # 3. Loop through all components again, and write out a SPICE card for each.
1157 # 4. Afterwards, for each item in the file-info list, open the file,
1158 # and write its contents into the netlist.
1159 # 5. If the schematic-type is .SUBCKT: write out .ENDS, otherwise
1161 # 6. Close up the SPICE netlist file and return.
1163 def run(f
, netlist
, args
):
1164 # Parse backend options passed via the `-O' command-line option.
1165 global calling_flags
1166 calling_flags
= backend_getopt(args
, {
1167 'include_mode': Option(False, NO_ARGUMENT
, None),
1168 'nomunge_mode': Option(False, NO_ARGUMENT
, None),
1169 'sort_mode': Option(False, NO_ARGUMENT
, None),
1170 'embedd_mode': Option(False, NO_ARGUMENT
, None),
1171 'no_end_card': Option(False, NO_ARGUMENT
, None)
1174 # Redefine write_net_names_on_component
1175 spice_common
.write_net_names_on_component
= write_net_names_on_component
1177 # Re-group components into packages using a custom refdes function
1178 # in order to treat slots as individual packages
1179 packages
= repackage(netlist
, get_refdes
)
1181 # First find out if this is a .SUBCKT lower level,
1182 # or if it is a regular schematic.
1184 schematic_type
= get_schematic_type(packages
)
1185 model_name
= get_subcircuit_modelname(schematic_type
)
1187 sys
.stderr
.write("Using SPICE backend by SDB -- Version of 2007-04-28\n")
1188 sys
.stderr
.write("schematic_type = %s\n" % schematic_type
)
1190 if schematic_type
!= 'normal schematic':
1191 # we have found a .SUBCKT type schematic.
1192 io_pin_packages
= get_spice_IO_pins(packages
)
1193 io_pin_packages_ordered
= sort_spice_IO_pins(io_pin_packages
)
1194 io_nets_list
= get_IO_nets(io_pin_packages_ordered
)
1195 debug_spew("found .SUBCKT type schematic\n")
1197 # now write out .SUBCKT header and .SUBCKT line
1198 write_subcircuit_header(f
)
1199 f
.write('%s %s \n' % (schematic_type
,
1200 ' '.join(reversed(io_nets_list
))))
1202 # Otherwise it's a regular schematic. Write out command line
1203 # followed by comments in file header.
1204 debug_spew("found normal type schematic\n")
1206 # If the schematic contains a spice-title device and the value
1207 # attribute is a string, use that as the title of the spice netlist
1208 for package
in reversed(packages
):
1209 if package
.get_attribute('device', None) == 'spice-title':
1210 title
= package
.get_attribute('value', None)
1213 f
.write(title
+ '\n')
1216 f
.write('* %s\n' % ' '.join(sys
.argv
))
1219 # Now loop through all devices and process all "FILE" attributes. Create
1221 # Thanks to Carlos Nieves Onega for his e-mail to
1222 # geda-dev which is the genesis of this section.
1224 debug_spew("Make first pass through design and "
1225 "create list of all model files referenced.\n")
1226 file_info_list
= create_file_info_list(packages
)
1227 debug_spew("Done creating file_info_list.\n\n")
1229 # Moved this loop before the next one to get numparam to work with
1230 # ngspice, because numparam will at the subckt definition come
1231 # before the main netlist. Change suggested by Dominique Michel;
1232 # implemented in code on 2005-06-12.
1234 # Next loop through all items in file_info_list in the SPICE
1235 # netlist. For each model_name, open up the corresponding file,
1236 # and call handle_spice_file to stick the corresponding stuff into
1237 # the output SPICE file.
1239 debug_spew("Now process the items in model file list -- "
1240 "stick appropriate references to models in output SPICE file.\n")
1241 loop_through_files(f
, file_info_list
)
1242 debug_spew("Done processing items in model file list.\n")
1244 # Now write out netlist as before. But don't write file contents out.
1245 # **** Modified by kh to sort list of packages so Spice
1246 # **** directives, etc. (A?) are output last, and in increasing
1249 debug_spew("Make second pass through design and "
1250 "write out a SPICE card for each component found.\n")
1252 '*============== Begin SPICE netlist of main design ============\n')
1253 if 'sort_mode' in calling_flags
:
1255 write_netlist(f
, file_info_list
, sorted(packages
, cmp = packsort
))
1258 write_netlist(f
, file_info_list
, reversed(packages
))
1259 debug_spew("Done writing SPICE cards . . .\n\n")
1261 # Now write out .END(S) of netlist, depending upon whether this
1262 # schematic is a "normal schematic" or a .SUBCKT.
1264 if schematic_type
!= 'normal schematic':
1265 f
.write('.ends %s\n' % model_name
)
1266 f
.write('*******************************\n')
1267 elif 'no_end_card' not in calling_flags
:
1270 debug_spew("\nOutput file is written. We are done.\n")