Remove extra debug message
[geda-gaf.git] / gnetlist-legacy / scheme / gnet-spice-sdb.scm
blobf7f5d6aa2e48cb3bf206571c00463e902b51948f
1 ;;; gEDA - GPL Electronic Design Automation
2 ;;; gnetlist - gEDA Netlist
3 ;;; Copyright (C) 1998-2010 Ales Hvezda
4 ;;; Copyright (C) 1998-2019 gEDA Contributors (see ChangeLog for details)
5 ;;;
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.
10 ;;;
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.
15 ;;;
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
18 ;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 ;;; MA 02111-1301 USA.
21 ;; --------------------------------------------------------------------------
23 ;; SPICE netlist backend written by S. Gieltjes starts here
25 ;; further modified by W. Kazubski to use scaling parameters for devices
26 ;; other than MOSFETS
28 ;;----------------------------------------------------------------------
30 ;; Started with gnet-spice1.scm by W. Kazubski.  Radically
31 ;; hacked by SDB to support advanced spice netlist generation.
32 ;; Project started 3.5.2003 -- SDB.
34 ;; Details and documentation at http://www.brorson.com/gEDA/SPICE/
36 ;;  Change log:
37 ;;  3.5.2003 -- Started hacking.  SDB.
38 ;;  3.17.2003 -- 2nd version.  Hacked to allow for .SUBCKT files to model ics.
39 ;;               Changed write-ic.  Added get-file-type.  Added
40 ;;               write-subcircuit.  SDB.
41 ;;  3.31.2003 -- 3rd version.  Hacked to enable creating .SUBCKT schematics for
42 ;;               hierarchical circuit modeling.
43 ;;  8.29.2003 -- 4th version.  Include patches from Ken Healy to sort netlist,
44 ;;               code by SDB to use gnetlist command line args in Scheme fcns,
45 ;;               as well as from Theo Deckers to fix strange problem with '.SUBCKT
46 ;;               quoting.
47 ;;  9.9.2003  -- 5th version.  Rearranged code for more organization (I was beginning
48 ;;               to get lost. . . .).  Incorporated changes to handle external SPICE
49 ;;               files more intelligently.  Changed spew to be configurable by setting
50 ;;               -v from the command line.  Placed new fcn debug-spew into gnetlist.scm.
51 ;;               Added -I command line flag.
52 ;;  10.14.2003 -- Bugfixes: Added empty-string? and hacked get-file-type to handle
53 ;;                case where a model file has an empty line before .SUBCKT or .MODEL.
54 ;;                Also modified write-net-names-on-component to gracefully handle
55 ;;                case where not every pin has a pinseq attribute.  Now only outputs
56 ;;                pins with valid pinseq attribute.
57 ;;  12.25.2003 -- Bugfix:  Unswizzled emission of pins from user-defined .subckts.
58 ;;                (Now correctly uses pinseq to define emission order of pins.)  Also
59 ;;                added ability to emit attributes for semiconductors (e.g. area, off,
60 ;;                ic, etc.)  Added in response to user requests.
61 ;;  12.29.2003 -- Two small enhancements requested by Peter Kaiser.
62 ;;  12.29.2003.a -- Minor bugfix.
63 ;;  12.29.2003.b -- Second minor bugfix.
64 ;;  12.29.2003.c -- Change res & cap to incorporate modelname & "area=" attrib.
65 ;;  3.24.2004 -- Bugfixes made to JFET stuff during Feb.  Change released now.
66 ;;  8.22.2004 -- Added command line as first line of file.
67 ;;  8.29.2004 -- Changed sense source naming in controlled sources because the old convention
68 ;;               was confusing ngspice.
69 ;;  10.9.2004 -- Added patches for voltage controlled switches from Peter Kaiser.
70 ;;  3.16.2005 -- Fixed CCCS bug (typo in Vsense) noticed by David Logan
71 ;;  5.16.2005 -- Modified behavior of .INCLUDE directive.  Now by default it just
72 ;;               spits out the string instead of putting the contents of the file
73 ;;               into the SPICE netlist.  You can force insertion of the file using
74 ;;               the -e flag.
75 ;;  6.12.2005 -- Changed order of writing out netlist and .model/.subckt cards to
76 ;;               facilitate use of numparam with ngspice.  Change supplied by
77 ;;               Dominique Michel.
78 ;;  9.11.2005 -- Incorporated patch from Paul Bunyk to enable netlisting of
79 ;;               Josephson junctions and "K" mutual inductances.  Also enabled
80 ;;               netlisting of "COIL" devices as inductors.
81 ;;  12.27.2005 -- Fix bug discovered by John Doty: spice-IO pins with refdes greater
82 ;;                than P9 were sorted incorrectly (as strings).  Now they are sorted
83 ;;                as numbers.
84 ;;  3.10.2006 -- Added "m" attribute to PMOS and NMOS per request of Peter Kaiser.
85 ;;  4.11.2006 --  Changed the .END and .ENDS cards to lowercase.
86 ;;                This fixes bug 1442912. Carlos Nieves Onega.
87 ;;  2.10.2007 -- Various bugfixes.  Also incorporated slotted part
88 ;;               netlist patch from Jeff Mallatt.  SDB.
89 ;;  4.28.2007 -- Fixed slotted part stuff so that it uses pinseq to emit pins.  SDB
90 ;;  1.9.2008 -- Fix slotted part handling to work without a modified pinseq.  pcjc2
91 ;;  1.3.2011 -- Combine write-ic and write-subcircuit with a fix to the unbound
92 ;;              type variable.  Fully document a check for the special "?" value
93 ;;              explaining why it fails silently.  Clean up
94 ;;              write-net-names-on-component to make it a bit more flexible.
95 ;;              Combine write-probe-item and write-net-names-on-component.  Add
96 ;;              a range utility function.  CC
97 ;;  1.13.2011 -- Add four lines of code (and some comments) that allow formaitting strings
98 ;;               to be used for netlisting NGspice device models. CC
99 ;;  6.12.2011 -- Updated the Problematci name=? symbols to name=unknown and removed the
100 ;;               FIXME check for them. This should be a step closer to place holder consistancy. CC
101 ;;  10.11.2020 -- Added support for a spice-title device to prove a netlist
102 ;;                title. Epilitimus
104 ;;**********************************************************************************
106 ;;  Organization of gnet-spice-sdb.scm file:
107 ;;  --  Functions for program housekeeping, handling of calling flags, file manipulation.
108 ;;  --  Functions for handling nets & devices and creating SPICE cards.
109 ;;  --  High-level functions which control program flow.  Note that the program entry
110 ;;      point lives at the very bottom of this file.
112 ;;  Unfortunately, no organization is present beneath this top level. . . .
114 ;;**********************************************************************************
117 ;;**********************************************************************************
118 ;;************  Program housekeeping, handling calling flags, etc.  ****************
119 ;;**********************************************************************************
121 ;; The following is needed to make guile 1.8.x happy.
122 (use-modules (ice-9 rdelim) (srfi srfi-1))
124 ;; Common functions for the `spice' and `spice-sdb' backends
125 (load-from-path "spice-common.scm")
127 ;;--------------------------------------------------------------------------------
128 ;; spice-sdb:loop-through-files -- loops through the model-file list, and for each file
129 ;;  name discovered in the list, it processes the file by invoking handle-spice-file.
130 ;;--------------------------------------------------------------------------------
131 (define spice-sdb:loop-through-files
132   (lambda (file-info-list)
133     (if (not (null? file-info-list))
134         (let*  ((list-element (car file-info-list))
135                 (model-name (car list-element))
136                 (file-name (cadr list-element))
137                 (file-type (caddr list-element))
138                )
139           (spice-sdb:handle-spice-file file-name)
140           (spice-sdb:loop-through-files (cdr file-info-list))
141         )  ;; end of let*
145 ;;--------------------------------------------------------------------------------
146 ;; spice-sdb:get-file-info-list-item  -- loops through the model-file list looking
147 ;;  for triplet corresponding to model-name.  If found, it returns the corresponding
148 ;;  list.  If not found, returns #f
149 ;;--------------------------------------------------------------------------------
150 (define spice-sdb:get-file-info-list-item
151   (lambda (model-name file-info-list)
152     (if (null? file-info-list)
153         '()                                           ;; return #f upon empty list.
154                                                       ;; #f replaced with '() by peter
155         (let*  ((list-element (car file-info-list))   ;; else process list-item
156                 (list-elt-model-name (car list-element))
157                 (list-elt-file-name (cadr list-element))
158                 (list-elt-file-type (caddr list-element))
159                )
160           (if (string=? list-elt-model-name model-name)
161               list-element                                                        ;; found model-name.  Return list-element.
162               (spice-sdb:get-file-info-list-item model-name (cdr file-info-list)) ;; otherwise, recurse.
163           )
164         )  ;; end of let*
168 ;;--------------------------------------------------------------------------
169 ;; handle-spice-file:  This wraps insert-text-file.
170 ;; Calling form: (handle-spice-file file-name)
171 ;; It looks to see if the -I flag was set at the command line.  If so,
172 ;; it just writes a .INCLUDE card with the file name.  If not,  it calls
173 ;; insert-text-file to stick the file's contents into the SPICE netlist.
174 ;;--------------------------------------------------------------------------
175 (define spice-sdb:handle-spice-file
176   (lambda (file-name)
177     (debug-spew (string-append "Handling spice model file " file-name "\n"))
178     (if (calling-flag? "include_mode" (gnetlist:get-calling-flags))
179         (display (string-append ".INCLUDE " file-name "\n"))       ;; -I found: just print out .INCLUDE card
180         (spice-sdb:insert-text-file file-name)                     ;; -I not found: invoke insert-text-file
181     )  ;; end of if (calling-flag
185 ;;--------------------------------------------------------------------------
186 ;; Given a filename, open the file, get the contents, and dump them
187 ;; into the spice file.
188 ;; Calling form is "(insert-text-file input-file output-file)"
189 ;; The function opens input-file, but assumes that output-file is
190 ;; already open.
192 ;; This function is usually used to include spice models contained in
193 ;; files into the netlist.  Note that it doesn't
194 ;; check the correctness of the spice code in the file -- you're on your own!
195 ;;---------------------------------------------------------------------------
196 (define spice-sdb:insert-text-file
197   (lambda (model-filename)
198     (if (file-exists? model-filename)
199     (let ((model-file (open-input-file model-filename)) )
200       (display (string-append "*vvvvvvvv  Included SPICE model from " model-filename " vvvvvvvv\n"))
201       (let while ((model-line (read-line model-file)))
202           (if (not (eof-object? model-line))
203                    (begin
204                      (display (string-append model-line "\n"))
205                      (while (read-line model-file))
206                    )  ;; end of inner begin
207           ) ;; end of if
208         )  ;; end of inner let
209         (close-port model-file)
210         (display (string-append "*^^^^^^^^  End of included SPICE model from " model-filename " ^^^^^^^^\n*\n"))
211      ) ;; end of outer let
212     (begin
213       (message (string-append "ERROR: File '" model-filename "' not found.\n"))
214       (primitive-exit 1))
215     )
216   )
220 ;;----------------------------------------------------------
221 ;; Figure out if this schematic is a .SUBCKT lower level.
222 ;; This is determined if there is a spice-subcircuit-LL
223 ;; device instantiated somewhere on the schematic.
224 ;; If it is a .SUBCKT, return ".SUBCKT model-name"
225 ;;----------------------------------------------------------
226 (define spice-sdb:get-schematic-type
227   (lambda (ls)
228      (if (not (null? ls))
229       (let* ((package (car ls))             ;; assign package
230              (device (get-device package))  ;; assign device.
231             )                               ;; end of let* assignments
232         (if (string=? device "spice-subcircuit-LL")  ;; look for subcircuit label
233               (string-append ".SUBCKT " (gnetlist:get-package-attribute package "model-name"))
234               (spice-sdb:get-schematic-type (cdr ls))  ;; otherwise just iterate to next package.
235         )
236       )    ; end of let*
237       "normal schematic"   ; return "normal schematic" if no spice-subcircuit-LL is found
238     )    ; end of if
242 ;;----------------------------------------------------------
243 ;; Extract the modelname from the .SUBCKT modelname line.
244 ;; Just grab the chars from char 8 to the end of the string.
245 ;;---------------------------------------------------------
246 (define spice-sdb:get-subcircuit-modelname
247   (lambda (schematic-type)
248     (substring schematic-type 8 (string-length schematic-type))
249   )
253 ;;-----------------------------------------------------------
254 ;;  This iterates through the schematic and compiles a list of
255 ;;  all spice-IO pins found.  This is used when writing out
256 ;;  a .SUBCKT lower level netlist.
257 ;;-----------------------------------------------------------
258 (define spice-sdb:get-spice-IO-pins
259   (lambda (ls spice-io-package-list)
260     (if (null? ls)
262         spice-io-package-list        ;; end iteration & return list if ls is empty.
264         (let* ((package (car ls))    ;; otherwise process package. . .
265                (device (get-device package))
266               )
267            (if (string=? device "spice-IO")  ;; look for subcircuit label
269                ;; we have found a spice-IO pin.
270                (spice-sdb:get-spice-IO-pins (cdr ls) (cons package spice-io-package-list))
272                ;; no spice-IO pin found.  Iterate . . . .
273                (spice-sdb:get-spice-IO-pins (cdr ls) spice-io-package-list)
275            ) ;; end of if string=?
277         )  ;; end of let*
278    ) ;; end if null
283 ;;----------------------------------------------------------------
284 ;;  This takes the list of io-pin-packages and sorts it in order of
285 ;;  refdes.
286 ;;  Repaired on 12.27.2005 to correctly sort pin numbers > 9.
287 ;;----------------------------------------------------------------
288 (define spice-sdb:sort-spice-IO-pins
289   (lambda (package-list)
290     ;;  Yes, this isn't good Scheme form.  Tough!  Writing this out
291     ;;  in a functional programming form would be totally confusing!
292     ;;  Note that this fcn requires that
293     ;;  each spice-IO pin have the same, single character prefix (i.e. 'P')
294     (let* ((char-prefixes              (map car (map string->list package-list)))  ;; Pull off first char (prefix)
295            (prefixes                   (map string char-prefixes))                 ;; Make list of strings from prefixes
296            (split-numbers-list         (map cdr (map string->list package-list)))  ;; Pull off refdes numbers as list elements
297            (string-numbers-list        (map list->string split-numbers-list))      ;; Recombine split up (multidigit) number strings
298            (numbers-list               (map string->number string-numbers-list))   ;; Convert strings to numbers for sorting
299            (sorted-numbers-list        (sort numbers-list <))                      ;; Sort refdes numbers as numbers
300            (sorted-string-numbers-list (map number->string sorted-numbers-list)) ) ;; Create sorted list of refdes strings.
302       (map-in-order string-append  prefixes sorted-string-numbers-list)  ;; Laminate prefixes back onto refdes numbers & return.
304     )
305   )
309 ;;----------------------------------------------------------------
310 ;;  Given a list of spice-IO packages (refdeses), this function returns the list
311 ;;  of nets attached to the IOs.
312 ;;----------------------------------------------------------------
313 (define spice-sdb:get-IO-nets
314   (lambda (package-list net-list)
315     (if (null? package-list)
317         net-list        ;; end iteration & return net-list if ls is empty.
319         (let* ((package (car package-list))                  ;; otherwise process package. . .
320                (net (car (gnetlist:get-nets package "1")))   ;; get the net attached to pin 1
321               )
322          ;; now iterate
323           (spice-sdb:get-IO-nets (cdr package-list) (cons net net-list))
324         )
325     ) ;; end of if
326   )
330 ;;----------------------------------------------------------
331 ;;  This takes a list and turns it into a string.
332 ;;  The difference between this and list->string is that
333 ;;  this fun can handle lists made up of multi-char strings.
334 ;;----------------------------------------------------------
335 (define list-2-string
336   (lambda (ls)
337     (let while
338         ((st (string))
339          (local-list ls)
340         )
341       (if (null? local-list)
342           st                                                    ;; end iteration & return string if list is empty.
343           (begin                                                ;; otherwise turn next element of list into string. . .
344             (set! st (string-append (car local-list) " " st))   ;; stuff next element onto st
345             (while st (cdr local-list))                         ;; iterate with remainder of ls
346           )
347       ) ;; end of if
348     )
349   )
353 ;;----------------------------------------------------------
354 ;;  This returns #t if the string is composed only of
355 ;;  whitespace.  It works by turning the string into
356 ;;  a list, and then checking to see if it is the empty
357 ;;  list.  If so, it returns #t.
358 ;;----------------------------------------------------------
359 (define empty-string?
360   (lambda (string)
361     (null? (string->list string))
362   )
366 ;;----------------------------------------------------------
367 ;;  This returns a list of all the integers from start to
368 ;;  stop, with the optional step size.
369 ;;  It is similar to perl's range operator '..'
370 ;;----------------------------------------------------------
371 (define (range start stop . step)
372   (if (null? step)
373     (iota (+ (- stop start) 1) start)
374     (begin
375       (set! step (car step))
376       (iota (+ (ceiling (/ (- stop start) step)) 1) start step)
377     )
378   )
382 ;;----------------------------------------------------------
383 ;; Given a filename, open the file, get the first line,
384 ;; and see if it is a .MODEL or .SUBCKT file.
385 ;; Returns either ".MODEL" or ".SUBCKT" or "OTHER"
386 ;; Calling form is "(spice-sdb:get-file-type input-file)"
387 ;; The function opens input-file, and closes it when it is done.
388 ;;----------------------------------------------------------
389 (define spice-sdb:get-file-type
390   (lambda (model-filename)
392     (if (file-exists? model-filename)
393     (let ((model-file (open-input-file model-filename)) )
394       (let while ((file-line (read-line model-file)) )
396         (cond
397          ((eof-object? file-line)         ;;  Arrived at end of line without finding .MODEL or .SUBCKT.  Return "OTHER"
398             "OTHER")
400          ((empty-string? file-line)
401             (while (read-line model-file)) )        ;; Found empty line.  Iterate before doing anything else.
403          ((string=? (string (string-ref file-line 0)) "*")
404             (while (read-line model-file)) )                       ;; Found *comment.  Iterate.
406          ((string=? (string (string-ref file-line 0)) ".")
407           (begin
408             (debug-spew "In get-file-type, first-char = .\n")  ;; DEBUG stuff
409             (cond
411               ((string-ci=? (safe-string-head file-line 7) ".subckt")  ;; found .subckt as first line.
412                ".SUBCKT" )
414               ((string-ci=? (safe-string-head file-line 6) ".model")   ;; found .model as first line.
415                ".MODEL"  )
417               (else "OTHER")   ;; first . spice card is neither .model nor .subckt
419             ) ; inner cond
420           ) ; inner begin
421          )
423          (else
424             (while (read-line model-file))
425           )
427         ) ; outer cond
429        ) ;; end of inner lets
430       ) ;; end of outer let
431     (begin
432       (message (string-append "ERROR: File '" model-filename "' not found.\n"))
433       (primitive-exit 1))
434     )
435   )
436 ) ;; end define
439 ;;---------------------------------------------------------------
440 ;;  write prefix if first char of refdes is improper,
441 ;;  eg. if MOSFET is named T1 then becomes MT1 in SPICE
442 ;;---------------------------------------------------------------
443 (define spice-sdb:write-prefix
444     (lambda (package prefix)
445       (let ((different-prefix (not (string=? (substring package 0 1) prefix)) )
446             (nomunge (calling-flag? "nomunge_mode" (gnetlist:get-calling-flags)) )
447            )
448         (debug-spew (string-append "Checking prefix.  Package prefix =" (substring package 0 1) "\n"))
449         (debug-spew (string-append "                  correct prefix =" prefix "\n"))
450         (debug-spew "   nomunge mode = ")
451         (debug-spew nomunge)
452         (debug-spew (string-append "\n  different-prefix="))
453         (debug-spew different-prefix)
454         (debug-spew "\n")
455         (if (and different-prefix (not nomunge))
456             (display prefix) )
457       )
458     )
462 ;;---------------------------------------------------------------
463 ;; spice-sdb:packsort
464 ;;   Sort procedure to order refdes's alphabetically but
465 ;;   keep A? packages at the end of list so SPICE simulation
466 ;;   directives operate correctly.
467 ;;  This fcn written by Ken Healy to enable SPICE netlisting for
468 ;;  Gnucap, which wants A refdes cards (i.e. SPICE directives)
469 ;;  to appear last in the SPICE netlist.  Slightly modified
470 ;;  and incorporated into main spice-sdb release by SDB on 9.1.2003.
471 ;;  To output the netlist in sorted order, use the -s switch
472 ;;  when invoking gnetlist from the command line.  Example:
473 ;;  gnetlist -s -g spice-sdb -o output.spice Schematic.sch
474 ;;  The default behavior (i.e. if -s is not specified) is to do
475 ;;  no sorting.
476 ;;---------------------------------------------------------------
477 (define spice-sdb:packsort
478   (lambda (x y)
479     (let ((xdes (string-ref x 0))
480           (ydes (string-ref y 0))
481           (xnum (string-tail x 1))
482           (ynum (string-tail y 1))
483          )
485       (if (char-ci=? xdes ydes)
486           (if (string-ci<? xnum ynum) #t #f)
487           (if (char-ci=? xdes #\A) #f
488               (if (char-ci=? ydes #\A) #t
489                   (if (char-ci<? xdes ydes) #t #f)))))))
491 (define (string-tail string start)
492   (substring string start (string-length string)))
495 ;;---------------------------------------------------------------
496 ;; spice-sdb:sort_refdes?
497 ;;   Returns #t or #f depending upon if -s was discovered in
498 ;;   the calling flags given to gnetlist.   Used in conjunction with
499 ;;   spice-sdb:packsort.
500 ;;   Calling form: (spice-sdb:sort-refdes? (gnetlist:get-calling-flags))
501 ;;   9.1.2003 -- SDB.
502 ;;---------------------------------------------------------------
503 ;;  Note:  I should re-write this to use calling-flag? . . . .
504 (define spice-sdb:sort-refdes?
505   (lambda (calling-flag-list)
507     (if (null? calling-flag-list)
508           '#f                                             ;; return #f if null list -- sort_mode not found.
509           (let* ((calling-pair (car calling-flag-list))   ;; otherwise look for sort_mode in remainder of list.
510                  (calling-flag (car calling-pair))
511                  (flag-value (cadr calling-pair))  )
513             (if (string=? calling-flag "sort_mode")
514                 flag-value                                               ;; return flag-value if sort_mode found
515                 (spice-sdb:sort-refdes? (cdr calling-flag-list))    ;; otherwise recurse until sort_mode is found
516             )  ;; end if
517           )  ;; end of let*
518      )  ;; end of if
522 ;;**********************************************************************************
523 ;;***************  Dealing with nets, devices, & SPICE cards.    *******************
524 ;;**********************************************************************************
527 ;;----------------------------------------------------------------
529 ;; Write-transistor-diode: writes out component followed by
530 ;; model or model file associated
531 ;; with the component.
532 ;;  This function does the following:
533 ;;   1.  Writes out the correct refdes prefix (if specified and necessary).
534 ;;   2.  Writes out the refdes and nets
535 ;;   3.  Looks for "model-name" attribute. Writes it out if it exists.
536 ;;   4.  If there is no "model-name" attribute, it writes out the "value"
537 ;;       attribute.  If there is no "value" attribute, it writes out "unknown"
538 ;;       and returns, causing the spice simulator to puke when the netlist
539 ;;       is run.  This is important
540 ;;       'cause the spice simulator needs to have some indication of what
541 ;;       model to look for.
542 ;;   5.  Outputs optional attributes attached to device, if any.  Feature
543 ;;       added by SDB on 12.25.2003.
544 ;;   6.  Outputs a new line
545 ;;   7.  Looks for a the "model" attribute.  If it exists, it it writes out
546 ;;       a .MODEL line like this:  .MODEL model-name type (model)
548 ;;----------------------------------------------------------------
549 (define spice-sdb:write-transistor-diode
550   (lambda (package prefix type attrib-list)
552     ;; First do local assignments
553     (let ((model-name (gnetlist:get-package-attribute package "model-name"))
554           (model (gnetlist:get-package-attribute package "model"))
555           (value (gnetlist:get-package-attribute package "value"))
556           (area (gnetlist:get-package-attribute package "area"))
557           (off (gnetlist:get-package-attribute package "off"))
558           (model-file (gnetlist:get-package-attribute package "file"))
559          )   ;; end of local assignments
561    ;; Write out the refdes prefix, if specified and necessary.
562       (if prefix
563         (spice-sdb:write-prefix package prefix)
564       )
566    ;; Next we write out the refdes and nets.
567       (spice-sdb:write-component-no-value package)
569    ;; next look for "model-name" attribute.  Write it out if it exists.
570    ;; otherwise look for "value" attribute.
571       (if (not (string=? model-name "unknown"))
572           (display (string-append model-name " " ))  ;; display model-name if known
573           (display (string-append value " ")))       ;; otherwise display value
575   ;; Next write out attributes if they exist
576   ;; First attribute is area.  It is written as a simple string
577       (if (not (string=? area "unknown"))
578           (display (string-append area " ")))
580   ;; Next attribute is off.    It is written as a simple string
581       (if (not (string=? off "unknown"))
582           (display (string-append off " ")))
584   ;; Write out remaining attributes
585       (spice:write-list-of-attributes package attrib-list)
587   ;; Now write out newline in preparation for writing out model.
588       (newline)
590      ;; Now write out any model which is pointed to by the part.
591         (cond
593      ;; one line model and model name exist
594          ( (not (or (string=? model "unknown") (string=? model-name "unknown")))
595            (debug-spew (string-append "found model and model-name for " package "\n"))
596            (display (string-append ".MODEL " model-name " " type " (" model ")\n")) )
598      ;; one line model and component value exist
599          ( (not (or (string=? model "unknown") (string=? value "unknown")))
600            (debug-spew (string-append "found model and value for " package "\n"))
601            (display (string-append ".MODEL " model-name " " type " (" value ")\n")) )
603      ;; model file and model name exist
604          ( (not (or (string=? model-file "unknown") (string=? model-name "unknown")))
605            (debug-spew (string-append "found file and model-name for " package "\n"))
606            (debug-spew "I'll deal with the file later . . .\n")
607          )
609      ;; model file and component value exist
610          ( (not (or (string=? model-file "unknown") (string=? value "unknown")))
611            (debug-spew (string-append "found file and value for " package "\n"))
612            (debug-spew "I'll deal with the file later . . .\n")
613          )
615          )  ;; close of cond
616         )
617     )
621 ;;----------------------------------------------------------------
622 ;;  write diode
623 ;;  This writes out a valid diode refdes & then calls
624 ;;  the function which writes the rest of the line.
625 ;;----------------------------------------------------------------
626 (define spice-sdb:write-diode
627   (lambda (package)
628     (debug-spew (string-append "Found diode.  Refdes = " package "\n"))
629     (let ((attrib-list (list "ic" "temp") ))
630       (spice-sdb:write-transistor-diode package "D" "D" attrib-list))
631   )
635 ;;----------------------------------------------------------------
636 ;;  spice-sdb:write-ic
637 ;;  This writes out a valid ic or subcircuit line.
638 ;;  The algorithm is as follows:
639 ;;  1.  Figure out what type of model goes with this part from
640 ;;      file-info-list.  If it isn't listed, look for a MODEL attribute.
641 ;;      If MODEL attribute is attached, write out SPICE card, and then
642 ;;      write out .MODEL on next line.
643 ;;      If no MODEL attribute is attached, just write out what little
644 ;;      we know.  Then return
645 ;;  2.  If the model-name is in the file-info-list, get the associated
646 ;;      file-type.  Compare it against the component's refdes.  If model-type
647 ;;      is .MODEL or .SUBCKT and refdes doesn't begin with a U or X
648 ;;      respectively, prepend the correct prefix to the refdes.
649 ;; 3.   Print out the rest of the line.
651 ;;----------------------------------------------------------------
652 (define (spice-sdb:write-ic package file-info-list)
654     ;; First do local assignments
655     (let ((first-char (string (string-ref package 0)))  ;; extract first char of refdes
656           (model-name (gnetlist:get-package-attribute package "model-name"))
657           (model (gnetlist:get-package-attribute package "model"))
658           (value (gnetlist:get-package-attribute package "value"))
659           (type  (gnetlist:get-package-attribute package "type"))
660           (model-file (gnetlist:get-package-attribute package "file"))
661           (list-item (list))
662          )   ;; end of local assignments
664       (cond
665         ((string=? first-char "U") (debug-spew (string-append "Found ic.  Refdes = " package "\n")))
666         ((string=? first-char "X") (debug-spew (string-append "Found subcircuit.  Refdes = " package "\n")))
667       )
669     ;; First, if model-name is empty, we use value attribute instead.
670     ;; We do this by sticking the contents of "value" into "model-name".
671       (if (string=? model-name "unknown")
672           (set! model-name value))
674     ;; Now get item from file-info-list using model-name as key
675       (set! list-item (spice-sdb:get-file-info-list-item model-name file-info-list))
677     ;; check to see if list-item is null.
678       (if (null? list-item)
680     ;; list-item is null.  Evidently, we didn't discover any files holding this model.
681     ;; Instead we look for model attribute
682           (if (not (string=? model "unknown"))
683             (begin                                     ;; model attribute exists -- write out card and model.
684               (debug-spew "Model info not found in model file list, but model attribute exists.  Write out spice card and .model line..\n")
685               (spice-sdb:write-component-no-value package)
686               (display (string-append model-name "\n" ))
687               (display (string-append ".MODEL " model-name " "))
688               (if (not (string=? type "unknown")) (display (string-append type " ")))  ;; If no type then just skip it.
689               (display (string-append "(" model ")\n"))
690             )
691             (begin                                     ;; no model attribute either.  Just write out card.
692               (debug-spew "Model info not found in model file list.  No model attribute either.  Just write what we know.\n")
693               (spice-sdb:write-component-no-value package)
694               (display (string-append model-name "\n" ))
695             )
696           )   ;; end if (not (string=? . . . .
698     ;; list-item is not null.  Therefore we process line depending upon contents of list-item
699           (let ((file-type (caddr list-item)) )
700            (cond
701               ;; ---- file holds a model ----
702               ((string=? file-type ".MODEL")
703                (begin
704                 (debug-spew (string-append "Found .MODEL with model-file and model-name for " package "\n"))
705                  (spice-sdb:write-prefix package "U")  ;; this prepends an "U" to the refdes if needed, since we have a .model
706                  (spice-sdb:write-component-no-value package)
707                  (display (string-append model-name "\n" ))
708                 (debug-spew "We'll handle the file contents later . . .\n")
709                ))
711               ;; ---- file holds a subcircuit ----
712               ((string=? file-type ".SUBCKT")
713                (begin
714                  (debug-spew (string-append "Found .SUBCKT with model-file and model-name for " package "\n"))
715                  (spice-sdb:write-prefix package "X")  ;; this prepends an "X" to the refdes if needed, since we have a .subckt
716                  (spice-sdb:write-component-no-value package)
717                  (display (string-append model-name "\n" ))
718                  (debug-spew "We'll handle the file contents later . . .\n")
719                ))
720            )  ;; close of inner cond
721          )   ;; end of inner let
722        )  ;; end of if (null? list-item
724   ) ;; end of outer let
728 ;;-----------------------------------------------------------
729 ;;  write npn bipolar transistor
730 ;;  This writes out a valid transistor refdes & then calls
731 ;;  the function which writes the rest of the line.
732 ;;-----------------------------------------------------------
733 (define spice-sdb:write-npn-bipolar-transistor
734   (lambda (package)
735     (debug-spew (string-append "Found npn bipolar transistor.  Refdes = " package "\n"))
736     (let ((attrib-list (list "ic" "temp") ))
737       (spice-sdb:write-transistor-diode package "Q" "NPN" attrib-list))
738   )
742 ;;-----------------------------------------------------------
743 ;;  write pnp bipolar transistor
744 ;;-----------------------------------------------------------
745 (define spice-sdb:write-pnp-bipolar-transistor
746   (lambda (package)
747     (debug-spew (string-append "Found pnp bipolar transistor.  Refdes = " package "\n"))
748     (let ((attrib-list (list "ic" "temp") ))
749       (spice-sdb:write-transistor-diode package "Q" "PNP" attrib-list))
750   )
754 ;;-----------------------------------------------------------
755 ;;  write n-channel jfet transistor
756 ;;-----------------------------------------------------------
757 (define spice-sdb:write-nfet-transistor
758   (lambda (package)
759     (debug-spew (string-append "Found n-channel JFET.  Refdes = " package "\n"))
760     (let ((attrib-list (list "ic" "temp") ))
761       (spice-sdb:write-transistor-diode package "J" "NJF" attrib-list))
762   )
766 ;;-----------------------------------------------------------
767 ;;  write p-channel jfet transistor
768 ;;-----------------------------------------------------------
769 (define spice-sdb:write-pfet-transistor
770   (lambda (package)
771     (debug-spew (string-append "Found p-channel JFET.  Refdes = " package "\n"))
772     (let ((attrib-list (list "ic" "temp") ))
773       (spice-sdb:write-transistor-diode package "J" "PJF" attrib-list))
774   )
778 ;;------------------------------------------------------
779 ;;  write pmos transistor
780 ;;------------------------------------------------------
781 (define spice-sdb:write-pmos-transistor
782   (lambda (package)
783     (debug-spew (string-append "Found PMOS transistor.  Refdes = " package "\n"))
784     (let ((attrib-list (list "l" "w" "as" "ad" "pd" "ps" "nrd" "nrs" "temp" "ic" "m")))
785       (spice-sdb:write-transistor-diode package "M" "PMOS" attrib-list))
786   )
790 ;;------------------------------------------------------
791 ;;  write nmos transistor
792 ;;------------------------------------------------------
793 (define spice-sdb:write-nmos-transistor
794   (lambda (package)
795     (debug-spew (string-append "Found NMOS transistor.  Refdes = " package "\n"))
796     (let ((attrib-list (list "l" "w" "as" "ad" "pd" "ps" "nrd" "nrs" "temp" "ic" "m")))
797       (spice-sdb:write-transistor-diode package "M" "NMOS" attrib-list))
798   )
802 ;;------------------------------------------------------
803 ;;  write subckt pmos transistor
804 ;;------------------------------------------------------
805 (define spice-sdb:write-subckt-pmos-transistor
806   (lambda (package)
807     (debug-spew (string-append "Found PMOS subcircuit transistor.  Refdes = " package "\n"))
808     (let ((attrib-list (list "l" "w" "as" "ad" "pd" "ps" "nrd" "nrs" "temp" "ic" "m")))
809       (spice-sdb:write-transistor-diode package "X" "PMOS" attrib-list))
810   )
813 ;;------------------------------------------------------
814 ;;  write subckt nmos transistor
815 ;;------------------------------------------------------
816 (define spice-sdb:write-subckt-nmos-transistor
817   (lambda (package)
818     (debug-spew (string-append "Found NMOS subcircuit transistor.  Refdes = " package "\n"))
819     (let ((attrib-list (list "l" "w" "as" "ad" "pd" "ps" "nrd" "nrs" "temp" "ic" "m")))
820       (spice-sdb:write-transistor-diode package "X" "NMOS" attrib-list))
821   )
823 ;;------------------------------------------------------------
824 ;;  write mesfet transistor
825 ;;------------------------------------------------------------
826 ;; ************  Fix this!!!!!!!!!!  **************
827 (define spice-sdb:write-mesfet-transistor
828   (lambda (package)
829     (spice-sdb:write-transistor-diode package "Z" "MESFET" (list))))  ;; XXXXXX Fix this!!!
832 ;;-----------------------------------------------------------
833 ;;  write voltage controled switch
834 ;;-----------------------------------------------------------
835 (define spice-sdb:write-vc-switch
836   (lambda (package)
837     (debug-spew (string-append "Found voltage controlled switch.  Refdes = " package "\n"))
838     (let ((attrib-list (list " " ) ))
839       (spice-sdb:write-transistor-diode package "S" "SW" attrib-list))
840   )
844 ;;--------------------------------------------------------------------
845 ;;  write resistor
846 ;;--------------------------------------------------------------------
847 (define spice-sdb:write-resistor
848   (lambda (package)
850     (debug-spew (string-append "Found resistor.  Refdes = " package "\n"))
852     ;; first write out refdes and attached nets
853     (spice-sdb:write-component-no-value package)
855     ;; next write out mandatory resistor value if it exists.
856     (let ((value (gnetlist:get-package-attribute package "value")))
857         (if (not (string=? value "unknown"))
858                 (display (string-append value " " )))
859     )
861     ;; next write our model name if it exists
862     (let ((model-name (gnetlist:get-package-attribute package "model-name")))
863         (if (not (string=? model-name "unknown"))
864                 (display (string-append model-name " " )))
865     )
867     ;; next create list of attributes which can be attached to a resistor.
868     ;; I include non-standard "area" attrib here per popular demand.
869     (let ((attrib-list (list "area" "l" "w" "temp")))
870             ;; write the attributes (if any) separately
871       (spice:write-list-of-attributes package attrib-list)
872       (display " "))  ;; add additional space. . . .
874     ;; finally output a new line
875     (newline)
877   )
881 ;;----------------------------------------------------------------------------
882 ;;  write capacitor
883 ;;----------------------------------------------------------------------------
884 (define spice-sdb:write-capacitor
885   (lambda (package)
887     (debug-spew (string-append "Found capacitor.  Refdes = " package "\n"))
889     ;; first write out refdes and attached nets
890     (spice-sdb:write-component-no-value package)
892     ;; next write capacitor value, if any.  Note that if the
893     ;; component value is not assigned nothing will be written out.
894     (let ((value (gnetlist:get-package-attribute package "value")))
895         (if (not (string=? value "unknown"))
896                 (display (string-append value " " )))
897     )
899     ;; next write capacitor model name, if any.  This is applicable to
900     ;; semiconductor caps used in chip design.
901     (let ((model-name (gnetlist:get-package-attribute package "model-name")))
902         (if (not (string=? model-name "unknown"))
903                 (display (string-append model-name " " )))
904     )
906     ;; Next write out attributes if they exist.  Use
907     ;; a list of attributes which can be attached to a capacitor.
908     ;; I include non-standard "area" attrib here per request of Peter Kaiser.
909     (let ((attrib-list (list "area" "l" "w" "ic")))
910       (spice:write-list-of-attributes package attrib-list)
911             ;; write the off attribute separately
912                 (display " "))  ;; add additional space. . . .
914     (newline)
915   )
919 ;;----------------------------------------------------------------------------
920 ;;  write inductor
921 ;;----------------------------------------------------------------------------
922 (define spice-sdb:write-inductor
923   (lambda (package)
925     (debug-spew (string-append "Found inductor.  Refdes = " package "\n"))
927     ;; first write out refdes and attached nets
928     (spice-sdb:write-component-no-value package)
931     ;; next write inductor value, if any.  Note that if the
932     ;; component value is not assigned, then it will write "unknown"
933     (let ((value (gnetlist:get-package-attribute package "value")))
934                 (display value)
935     )
938     ;; create list of attributes which can be attached to a inductor
939     (let ((attrib-list (list "l" "w" "ic")))
940       (spice:write-list-of-attributes package attrib-list)
942       ;; write the off attribute separately
943       (display " "))  ;; add additional space. . . .
945     (newline)
946   )
950 ;;-------------------------------------------------------------------------
951 ;;  write independent voltage source
952 ;;  The behavior of the voltage source is held in the "value" attribute
953 ;;-------------------------------------------------------------------------
954 (define spice-sdb:write-independent-voltage-source
955   (lambda (package)
956     (debug-spew (string-append "Found independent voltage source.  Refdes = " package "\n"))
958             ;; first write out refdes and attached nets
959     (spice-sdb:write-component-no-value package)
961             ;; next write voltage value, if any.  Note that if the
962             ;; voltage value is not assigned, then it will write "unknown"
963     (let ((value (gnetlist:get-package-attribute package "value")))
964                 (display value)
965     )
967     (newline)
968   )
972 ;;-------------------------------------------------------------------------
973 ;;  write independent current source
974 ;;  The behavior of the current source is held in the "value" attribute
975 ;;-------------------------------------------------------------------------
976 (define spice-sdb:write-independent-current-source
977   (lambda (package)
979         (debug-spew (string-append "Found independent current source.  Refdes = " package "\n"))
981             ;; first write out refdes and attached nets
982     (spice-sdb:write-component-no-value package)
984             ;; next write current value, if any.  Note that if the
985             ;; current value is not assigned, then it will write "unknown"
986     (let ((value (gnetlist:get-package-attribute package "value")))
987                 (display value)
988     )
990     (newline)
991   )
995 ;;----------------------------------------------------------------------------
996 ;;  write Josephson junction in wrspice format. Paul Bunyk, Sep 2, 2005
997 ;;----------------------------------------------------------------------------
998 (define spice-sdb:write-josephson-junction
999   (lambda (package)
1001     (debug-spew (string-append "Found Josephson junction.  Refdes = " package "\n"))
1003     ;; first write out refdes and attached nets
1004     (spice-sdb:write-component-no-value package)
1006     ;; next, add a dummy node for JJ phase. Unlike in Xic netlister, give it
1007     ;; a reasonable name, not a number, e.g., refdes.
1008     (display (string-append package " "))
1010     ;; next write JJ model name, if any.
1011     (let ((model-name (gnetlist:get-package-attribute package "model-name")))
1012         (if (not (string=? model-name "unknown"))
1013                 (display (string-append model-name " " )))
1014     )
1016     ;; Next write out attributes if they exist.  Use
1017     ;; a list of attributes which can be attached to a junction.
1018     (spice:write-list-of-attributes package (list "area"))
1019     (newline)
1020   )
1024 ;;----------------------------------------------------------------------------
1025 ;;  write mutual inductance(actually K). Paul Bunyk, Sep 2, 2005
1026 ;;----------------------------------------------------------------------------
1027 (define spice-sdb:write-coupling-coefficient
1028   (lambda (package)
1030     (debug-spew (string-append "Found mutual inductance.  Refdes = " package "\n"))
1032     ;; first write out refdes and attached nets (none)
1033     (spice-sdb:write-component-no-value package)
1035     ;; next two inductor names and value
1036     (let ((inductors (gnetlist:get-package-attribute package "inductors"))
1037           (value (gnetlist:get-package-attribute package "value")) )
1038         (if (not (string=? inductors "unknown"))
1039                 (display (string-append inductors " " )))
1040         (if (not (string=? value "unknown"))
1041                 (display (string-append value " " )))
1043     )
1045     (newline)
1046   )
1050 ;;----------------------------------------------------------------------------
1051 ;; write a voltage probe
1052 ;;----------------------------------------------------------------------------
1053 (define (spice-sdb:write-probe package)
1054     ;; fetch only one attr we care about, so far
1055     (let ((value (gnetlist:get-package-attribute package "value"))
1056          ) ;; end of local assignments
1058     (debug-spew (string-append "Found Probe item, refdes = " package "\n"))
1060     (if (string=? value "unknown")
1061       (set! value "TRAN"))
1063     (display (string-append "* Probe device " package " on nets "))
1064     (spice-sdb:write-net-names-on-component package)
1065     (newline)
1066     (display (string-append ".print " value " +"))
1067     (spice-sdb:write-net-names-on-component package
1068       (string-join (map (lambda (x) "V(~a)") (gnetlist:get-pins package)) " " 'infix) ) ;; make format string
1069     (newline)
1070   ) ;; end of let
1071 ) ;; close of define
1074 ;;--------------------------------------------------------------------
1075 ;; Given a refdes, and optionally a format string, this writes
1076 ;; out the nets attached to the component's pins. If it's not called
1077 ;; with a format string it looks for one in the net-format attribute,
1078 ;; otherwise it writes out the pins unformatted. This is used to write
1079 ;; out non-slotted parts.
1080 ;;--------------------------------------------------------------------
1081 (define (spice-sdb:write-net-names-on-component refdes . format)
1083 ;; get-net-name -- helper function. Called with pinseq, returns net name,
1084 ;; unless net name is "ERROR_INVALID_PIN" then it returns false.
1085     (define (get-net-name pin)
1086         (set! pin (number->string pin))
1088 ;; -------  Super debug stuff  --------
1089           (if #f
1090             (begin
1091               (debug-spew "  In write-net-names-on-component. . . . \n")
1092               (debug-spew (string-append "     pin-name = " pin "\n"))
1093               (debug-spew (string-append "     pinnumber = " (gnetlist:get-attribute-by-pinseq refdes pin "pinnumber") "\n"))
1094               (debug-spew (string-append "     pinseq = " (gnetlist:get-attribute-by-pinseq refdes pin "pinseq")))
1095               (if (not (string=? pin (gnetlist:get-attribute-by-pinseq refdes pin "pinseq")))
1096                 (debug-spew " <== INCONSISTENT!\n")
1097                 (debug-spew "\n") )
1098               (debug-spew (string-append "     netname = " (car (spice:get-net refdes (gnetlist:get-attribute-by-pinseq refdes pin "pinnumber"))) "\n"))
1099           )) ;; if #T for super debugging
1100 ;; -------------------------------------
1102         (set! pin (car (spice:get-net refdes (gnetlist:get-attribute-by-pinseq refdes pin "pinnumber"))))
1103         (if (string=? pin "ERROR_INVALID_PIN")
1104           (begin
1105             (debug-spew (string-append "For " refdes ", found pin with no pinseq attribute.  Ignoring. . . .\n"))
1106             #f)  ;; begin
1107         pin)  ;; if
1108     )  ;; define get-net-name
1110     ;; First do local assignments
1111     (let ((netnames (filter-map get-net-name (range 1 (length (gnetlist:get-pins refdes)))))
1112          )  ;; let
1113       (if (null? format) ;; Format agument take priority, otherwise use attribute
1114         (set! format (gnetlist:get-package-attribute refdes "net-format"))
1115         (set! format (car format)) )
1116       (if (string=? format "unknown")
1117         (display (string-join netnames " " 'suffix))               ;; write out nets.
1118         (apply simple-format (cons #t (cons format netnames))) )   ;; write out nets with format string
1119     )  ;; let
1123 ;;-------------------------------------------------------------------
1124 ;; Write the refdes and the net names connected to pins on this component.
1125 ;; No return, and no component value is written, or extra attribs.
1126 ;; Those are handled later.
1127 ;;-------------------------------------------------------------------
1128 (define spice-sdb:write-component-no-value
1129   (lambda (package)
1130     (display (string-append package " "))  ;; write component refdes
1131     (spice-sdb:write-net-names-on-component package)
1132   )
1136 ;;------------------------------------------------------------
1137 ;; Given a refdes, returns the device attribute "value" as string
1138 ;; Used when "value" is an optional attribute.
1139 ;; Returns "unknown" if not available.
1140 ;;------------------------------------------------------------
1141 (define spice-sdb:component-optional-value
1142   (lambda (package)
1143     (let ((value (gnetlist:get-package-attribute package "value")))
1144       (if (not (string=? value "unknown"))
1145         (string-append value " ")
1146         ""))))
1149 ;;-----------------------------------------------------------
1150 ;; Given a refdes, returns the device attribute "model" as string
1151 ;;-----------------------------------------------------------
1152 (define spice-sdb:component-model
1153   (lambda (package)
1154     (let ((model (gnetlist:get-package-attribute package "model")))
1155       (if (not (string=? model "unknown"))
1156         model spice:component-value))))
1159 ;;----------------------------------------------------------
1160 ;; Include SPICE statements from a SPICE directive block.
1161 ;;----------------------------------------------------------
1162 (define spice-sdb:write-directive
1163   (lambda (package)
1164              ;; Collect variables used in creating spice code
1165         (let ((value (gnetlist:get-package-attribute package "value"))
1166               (file (gnetlist:get-package-attribute package "file"))
1167              )   ;; end of local assignments
1169           (debug-spew (string-append "Found SPICE directive box.  Refdes = " package "\n"))
1171           (cond
1173               ;; First look to see if there is a value.
1174            ((not (string=? value "unknown"))
1175             (begin
1176               (display (string-append value "\n"))
1177               (debug-spew (string-append "Appending value = \"" value "\" to output file.\n"))
1178             ))
1180               ;; since there is no value, look for file.
1181            ((not (string=? file "unknown"))
1182             (begin
1183               (spice-sdb:insert-text-file file)   ;; Note that we don't wait until the end here.  Is that OK?
1184               (debug-spew (string-append "Inserting contents of file = " file " into output file.\n"))
1185             ))
1187           ) ;; close of cond
1188         ) ;; close of let
1189     ) ;; close of lambda
1190 ) ;; close of define
1193 ;;----------------------------------------------------------
1194 ;; Include a file using an .INCLUDE directive
1195 ;; Changed on 6.12.2005: to embed the contents of the file,
1196 ;; you must call gnetlist with the -e flag set.
1197 ;;----------------------------------------------------------
1198 (define spice-sdb:write-include
1199   (lambda (package)
1200     (let ((file (gnetlist:get-package-attribute package "file")))
1202       (debug-spew (string-append "Found SPICE include box.  Refdes = " package "\n"))
1204       (if (not (string=? file "unknown"))
1205         (if  (calling-flag? "embedd_mode" (gnetlist:get-calling-flags))
1206               (begin
1207                 (spice-sdb:insert-text-file file)                 ;; -e found: invoke insert-text-file
1208                 (debug-spew (string-append "embedding contents of file " file " into netlist.\n")))
1209               (begin
1210                 (display (string-append ".INCLUDE " file "\n"))   ;; -e not found: just print out .INCLUDE card
1211                 (debug-spew "placing .include directive string into netlist.\n"))
1212           )
1213         (debug-spew "silently skip \"unknown\" file.\n")
1214        )
1218 ;;----------------------------------------------------------
1219 ;; Include an option using an .OPTIONS directive
1220 ;;----------------------------------------------------------
1221 (define spice-sdb:write-options
1222   (lambda (package)
1223     (debug-spew (string-append "Found .OPTIONS box.  Refdes = " package "\n"))
1224     (display (string-append ".OPTIONS " (spice:component-value package) "\n"))))
1227 ;;----------------------------------------------------------
1228 ;; Include a spice model (instantiated as a model box on the schematic)
1229 ;;  Two types of model can be included:
1230 ;;  1.  An embedded model, which is a one- or multi-line string held in the attribute "model".
1231 ;;      In this case, the following attributes are mandatory:
1232 ;;      --  model (i.e. list of parameter=value strings)
1233 ;;      --  model-name
1234 ;;      --  type
1235 ;;      In this case, the function creates and formats the correct spice model line(s).
1236 ;;  2.  A model held in a file whose name is held in the attribute "file"
1237 ;;      In this case, the following attribute are mandatory:
1238 ;;      --  file (i.e. list of parameter=value strings)
1239 ;;      In this case, the function just opens the file and dumps the contents
1240 ;;      into the netlist.
1241 ;;----------------------------------------------------------
1242 (define spice-sdb:write-model
1243   (lambda (package)
1244              ;; Collect variables used in creating spice code
1245         (let ((model-name (gnetlist:get-package-attribute package "model-name"))
1246               (model-file (gnetlist:get-package-attribute package "file"))
1247               (model (gnetlist:get-package-attribute package "model"))
1248               (type (gnetlist:get-package-attribute package "type"))
1249              )   ;; end of local assignments
1251           (debug-spew (string-append "Found .MODEL box.  Refdes = " package "\n"))
1253           ;; Now, depending upon what combination of model, model-file, and model-name
1254           ;; exist (as described above) write out lines into spice netlist.
1255           (cond
1256              ;; one model and model name exist
1257            ( (not (or (string=? model "unknown") (string=? model-name "unknown")))
1258              (debug-spew (string-append "found model and model-name for " package "\n"))
1259              (display (string-append ".MODEL " model-name " " type " (" model ")\n")) )
1261              ;; model file exists
1262            ( (not (or (string=? model-file "unknown") ))
1263              (debug-spew (string-append "found model-file for " package "\n"))
1264              ;; (spice-sdb:insert-text-file model-file)   ;; don't write it out -- it's handled after the second pass.
1265            )
1267           )  ;; close of cond
1268         ) ;; close of let
1269     ) ;; close of lambda
1270 ) ;; close of define
1273 ;;-------------------------------------------------------------------
1274 ;;  This writes out the default component (i.e. the "device" attribute
1275 ;;  was not recognized).  This function does the following:
1277 ;;  1.  Gets the refdes (package).
1278 ;;  2.  Checks the refdes against a short list of possible values.
1279 ;;      Depending upon the refdes, it does the following thing:
1280 ;;      A? -- Invokes write-ic. This provides the opportunity for a code model
1281 ;;            which may include a .model line.
1282 ;;      D? -- Invokes write-diode
1283 ;;      Q? -- Invokes write-transistor-diode. (The "type" attribute is <unknown>
1284 ;;            in this case so that the spice simulator will barf if the user
1285 ;;            has been careless.)
1286 ;;      M? -- Same as Q
1287 ;;      U? -- Invokes write-ic. This provides the opportunity for a component
1288 ;;            model to be instantiated.
1289 ;;      X? -- Invokes write-ic.  This provides the opportunity for a component
1290 ;;            subcircuit to be instantiated.
1291 ;;      V? -- Invokes write-independent-voltage-source
1292 ;;      I? -- Invokes write-independent-current-source
1293 ;;      Otherwise, it just outputs the refdes, the attached nets, and the
1294 ;;      value of the "value" attribute.
1296 ;;-------------------------------------------------------------------
1297 (define spice-sdb:write-default-component
1298   (lambda (package file-info-list)
1300     (let ((first-char (string (string-ref package 0)) ))  ;; extract first char of refdes.
1301       (cond
1302        ((string=? first-char "A") (spice-sdb:write-ic package file-info-list))
1303        ((string=? first-char "D") (spice-sdb:write-diode package))
1304        ((string=? first-char "Q") (spice-sdb:write-transistor-diode package #f "<unknown>" (list)))
1305        ((string=? first-char "M") (spice-sdb:write-transistor-diode package #f "<unknown>" (list)))
1306        ((string=? first-char "U") (spice-sdb:write-ic package file-info-list))
1307        ((string=? first-char "V") (spice-sdb:write-independent-voltage-source package))
1308        ((string=? first-char "I") (spice-sdb:write-independent-current-source package))
1309        ((string=? first-char "X") (spice-sdb:write-ic package file-info-list))
1310        (else
1311         (message (string-append "Found unknown component.  Refdes = " package "\n"))
1312         (spice-sdb:write-component-no-value package)
1313         ;; write component value, if components have a label "value=#"
1314         ;; what if a component has no value label, currently unknown is written
1315         (display (spice:component-value package))
1316         (newline)
1317        )
1318       ) ;; end cond
1319      )  ;; end let
1320   )
1324 ;;**********************************************************************************
1325 ;;***************  High-level functions for program control  ***********************
1326 ;;**********************************************************************************
1328 ;;----------------------------------------------------------------------
1329 ;; write-netlist is passed a list of refdesses (ls).  It uses
1330 ;; each refdes to get the corresponding
1331 ;; "device" attribute.  Depending upon the device, it then invokes one or another of the
1332 ;; spice line output fcns to output a line of the spice netlist.
1333 ;; I have enlarged the number of devices it recognizes -- SDB.
1334 ;; write the refdes, to the pin# connected net and component
1335 ;; value and optional extra attributes
1336 ;; check if the component is a special spice component.
1337 ;;----------------------------------------------------------------------
1338 (define spice-sdb:write-netlist
1339   (lambda (file-info-list ls)
1340      (if (not (null? ls))
1341       (let* ((package (car ls))             ;; assign package
1342              (device (get-device package))  ;; assign device.
1343             )                               ;; end of let* assignments
1345 ;; Super debug stuff -- outputs line describing device being processed.
1346         (debug-spew (string-append "--- checking package = " package "\n"))
1347         (debug-spew (string-append "    device = " device "\n"))
1348 ;; done with debug stuff
1350         (cond
1351           ( (string=? device "none"))                 ;; do nothing for graphical symbols.
1352           ( (string=? device "spice-subcircuit-LL"))  ;; do nothing for subcircuit declaration.
1353           ( (string=? device "spice-IO"))             ;; do nothing for SPICE IO pins.
1354           ( (string=? device "spice-title"))          ;; do nothing for spice title blocks
1355           ( (string=? device "SPICE-ccvs")
1356               (spice:write-ccvs package))
1357           ( (string=? device "SPICE-cccs")
1358               (spice:write-cccs package))
1359           ( (string=? device "SPICE-vcvs")
1360               (spice:write-vcvs package))
1361           ( (string=? device "SPICE-vccs")
1362               (spice:write-vccs package))
1363           ( (string=? device "SPICE-nullor")
1364               (spice:write-nullor package))
1365           ( (string=? device "DIODE")
1366               (spice-sdb:write-diode package))
1367           ( (string=? device "PMOS_TRANSISTOR")
1368               (spice-sdb:write-pmos-transistor package))
1369           ( (string=? device "NMOS_TRANSISTOR")
1370               (spice-sdb:write-nmos-transistor package))
1371           ( (string=? device "PNP_TRANSISTOR")
1372               (spice-sdb:write-pnp-bipolar-transistor package))
1373           ( (string=? device "SPICE-PNP")
1374               (spice-sdb:write-pnp-bipolar-transistor package))
1375           ( (string=? device "NPN_TRANSISTOR")
1376               (spice-sdb:write-npn-bipolar-transistor package))
1377           ( (string=? device "SPICE-NPN")
1378               (spice-sdb:write-npn-bipolar-transistor package))
1379           ( (string=? device "PFET_TRANSISTOR")
1380               (spice-sdb:write-pfet-transistor package))
1381           ( (string=? device "NFET_TRANSISTOR")
1382               (spice-sdb:write-nfet-transistor package))
1383           ( (string=? device "MESFET_TRANSISTOR")
1384               (spice-sdb:write-mesfet-transistor package))
1385           ( (string=? device "SPICE-VC-switch")
1386               (spice-sdb:write-vc-switch package))
1387           ( (string=? device "RESISTOR")
1388               (spice-sdb:write-resistor package))
1389           ( (string=? device "CAPACITOR")
1390               (spice-sdb:write-capacitor package))
1391           ( (string=? device "POLARIZED_CAPACITOR")
1392               (spice-sdb:write-capacitor package))                       ;; change someday
1393           ( (string=? device "INDUCTOR")
1394               (spice-sdb:write-inductor package))
1395           ( (string=? device "COIL")           ;; Added to enable netlisting of coil-*.sym
1396               (spice-sdb:write-inductor package))
1397           ( (string=? device "VOLTAGE_SOURCE")
1398               (spice-sdb:write-independent-voltage-source package)) ;; change someday
1399           ( (string=? device "CURRENT_SOURCE")
1400               (spice-sdb:write-independent-current-source package)) ;; change someday
1401           ( (string=? device "JOSEPHSON_JUNCTION")
1402               (spice-sdb:write-josephson-junction package))
1403           ( (string=? device "K")
1404               (spice-sdb:write-coupling-coefficient package))
1405           ( (string=? device "model")
1406               (spice-sdb:write-model package))
1407           ( (string=? device "options")
1408               (spice-sdb:write-options package))
1409           ( (string=? device "directive")
1410               (spice-sdb:write-directive package))
1411           ( (string=? device "include")
1412               (spice-sdb:write-include package))
1413           ( (string=? device "TESTPOINT")
1414               (spice-sdb:write-probe package))
1415           ( (string=? device "SUBCKT_PMOS")
1416               (spice-sdb:write-subckt-pmos-transistor package))
1417           ( (string=? device "SUBCKT_NMOS")
1418               (spice-sdb:write-subckt-nmos-transistor package))
1419           ( else
1420               (spice-sdb:write-default-component package file-info-list))
1421         ) ;; end of cond
1422         (spice-sdb:write-netlist file-info-list (cdr ls))
1423          ))))
1426 ;;----------------------------------------------------------------------
1427 ;; create-file-info-list: This takes as argument the list of packages (refdesses).
1428 ;;   It runs through the package list, and for each gets the attributes.  If there is a
1429 ;;   "FILE" attribute, it gets the file info & uses it to build the
1430 ;;   file-info-list.  When done, it returns the file-info-list.
1431 ;;----------------------------------------------------------------------
1432 (define spice-sdb:create-file-info-list
1433   (lambda (package-list file-info-list)
1434      (if (null? package-list)
1435         file-info-list                          ;; end of packages processed.  Return file-info-list
1436         (let* ((package (car package-list))     ;; otherwise get next package (i.e. refdes)
1437                (device (string))
1438                (model (string))
1439                (value (string))
1440                (model-file (string))
1441               )                                 ;; end of let* assignments
1443           (set! device (get-device package) )
1444           (set! model (gnetlist:get-package-attribute package "model-name") )
1445           (set! value (gnetlist:get-package-attribute package "value") )
1446           (set! model-file (gnetlist:get-package-attribute package "file") )
1448           ;; Now run a series of checks to see if we should stick this file into the file-info-list
1449           ;; Check to see if "file" attribute is non-empty
1450           (if (not (string-ci=? model-file "unknown"))
1451               (begin
1452                 (debug-spew
1453                    (string-append "found file attribute for " package ".  File name = " model-file "\n"))  ;;  ******* Debug stuff
1455               ;; Now check to see if file is in file-info-list
1456                 (if (not (spice-sdb:in-file-info-list? model-file file-info-list))
1458               ;; File is new.  Open file, find out what type it is, and push info into file-info-list
1459                     (let ((file-type (spice-sdb:get-file-type model-file))
1460                          )
1461                       (debug-spew (string-append "File is new.  New file type is " file-type " \n"))      ;; DEBUG
1463               ;; Check to see if file-type is known.
1464                       (if (not (string=? file-type "OTHER"))
1465               ;; file-type is OK.  Return file-info-list with new triplet attached.
1466                           (begin
1467                             (debug-spew (string-append "Inserting " model-file " into list of known model files.\n"))
1468                             (set! file-info-list (append (list (list model model-file file-type)) file-info-list) )
1469                           )
1471               ;;  Otherwise, file type is not a model type.  Don't stick it in list.  Print debug spew if desired.
1472                           (debug-spew "File type is OTHER, and therefore will not be entered in known model file list.\n")
1473                        )   ;; end if (not (string=?
1475                      )  ;; end let ((file-type . . .
1477               ;;  File is already in list.  Print debug spew if desired.
1478                     (debug-spew "File has already been seen and entered into known model file list.\n")
1479                 )  ;; end if (spice-sdb:in-file-info-list . . .
1481               )  ;; end begin . . .
1482           )  ;; end if (not( string-ci=? model-file
1484           ;; having done checking and processing of this package, iterate to the next one.
1485           (spice-sdb:create-file-info-list (cdr package-list) file-info-list)
1487       )  ;; end let*
1488    )  ;; end if (null? package-list . . .
1493 ;;  in-file-info-list? -- helper function.  Returns #t if file is already in file-info-list, otherwise #f
1494 ;;  assumes file-info-list of form: ((model1 file1 file-type1)  (model2 file2 file-type2) . . . .)
1495 (define spice-sdb:in-file-info-list?
1496   (lambda (model-file file-info-list)
1497     (if (null? file-info-list)
1498         (begin
1499           #f                                            ;; return #f if file-info-list itself is empty.
1500         )
1501         (let ((list-element (car file-info-list)) )   ;; otherwise process list-element
1502           (if (null? list-element)
1503               #f                                      ;;  item not found.  Return #f.  Note that we should never get here . . .
1504               (let ((list-file-name (cadr list-element)) )
1505                 (if (string=? list-file-name model-file)
1506                    #t                            ;; item found.  Return #t
1507                    (spice-sdb:in-file-info-list? model-file (cdr file-info-list))  ;; iterate . . .
1508                 )  ;; end if (string=?
1509               )  ;; end of let . . .
1510           )  ;; end if (null? list-element . . .
1511         )  ;; end let* ((list-element . . .
1512     )  ;; end if (null? file-info-list . .
1513 ))  ;; end define spice-sdb:in-file-info-list?
1516 ;;--------------------------------------------------------------
1517 ;; Write out spice netlist header
1518 ;;--------------------------------------------------------------
1519 (define (spice-sdb:write-top-header)
1520   (display "*********************************************************\n")
1521   (display "* Spice file generated by gnetlist                      *\n")
1522   (display "* spice-sdb version 4.28.2007 by SDB --                 *\n")
1523   (display "* provides advanced spice netlisting capability.        *\n")
1524   (display "* Documentation at http://www.brorson.com/gEDA/SPICE/   *\n")
1525   (display "*********************************************************\n")
1529 ;;--------------------------------------------------------------
1530 ;; Write out .SUBCKT netlist header
1531 ;;--------------------------------------------------------------
1532 (define (spice-sdb:write-subcircuit-header)
1533   (display "*******************************\n")
1534   (display "* Begin .SUBCKT model         *\n")
1535   (display "* spice-sdb ver 4.28.2007     *\n")
1536   (display "*******************************\n")
1540 ;;---------------------------------------------------------------
1541 ;; Write the .END line
1542 ;;---------------------------------------------------------------
1543 (define (spice-sdb:write-bottom-footer salutation)
1544   (display salutation)
1545   (newline))
1548 ;;---------------------------------------------------------------
1549 ;; Spice netlist generation
1550 ;;   This is the entry point.
1551 ;;   Hacked on 3.31.2003 to enable writing out .SUBCKT models -- SDB.
1552 ;;   Hacked again in Sept 2003 to enable more intelligent embedding of external
1553 ;;       SPICE files into netlist -- SDB.
1554 ;;   The algorithm is as follows:
1555 ;;   1.  Figure out if there is a .SUBCKT block on the schematic,
1556 ;;       or if it is just a normal schematic.
1557 ;;       If a .SUBCKT:
1558 ;;       -- Write out subcircuit header (a comment identifying the netlister).
1559 ;;       -- find all spice-IO pins.  Get a list of the packages.
1560 ;;       -- put them in order (ordered by package refdes)
1561 ;;       -- get the list of nets attached to the spice-IO pins.
1562 ;;       -- write out .SUBCKT line
1563 ;;       If a normal schematic:
1564 ;;       -- Write out top header (a comment identifying the netlister).
1565 ;;   2.  Loop through all components, looking for components with a "file"
1566 ;;       attribute.  Every time a "file" attribute is found do this:
1567 ;;       --  Open the file and find out what kind of file it is (.SUBCKT or .MODEL).
1568 ;;       --  Determine if the file has previously been processed.  If not: stick the
1569 ;;           following info into the file-info list: (model-name file-name file-type).
1570 ;;           Otherwise just continue.
1571 ;;   3.  Loop through all components again, and write out a SPICE card for each.
1572 ;;   4.  Afterwards, for each item in the file-info list, open the file, and
1573 ;        write its contents into the netlist.
1574 ;;   5.  If the schematic-type is .SUBCKT:  write out .ENDS,  Otherwise: write out .END
1575 ;;   6.  Close up the SPICE netlist file and return.
1576 ;;---------------------------------------------------------------
1577 (define spice-sdb
1578   (lambda (output-filename)
1579     ;; Redefine write-net-names-on-component
1580     (set! spice:write-net-names-on-component spice-sdb:write-net-names-on-component)
1583 ;; First find out if this is a .SUBCKT lower level,
1584 ;; or if it is a regular schematic.
1586     (set-current-output-port (gnetlist:output-port output-filename))
1587     (let* ((schematic-type (spice-sdb:get-schematic-type packages))
1588            (model-name (spice-sdb:get-subcircuit-modelname schematic-type))
1589            (file-info-list (list))
1590           )
1591       (message "Using SPICE backend by SDB -- Version of 4.28.2007\n")
1592       (message (string-append "schematic-type = " schematic-type "\n"))
1594       (if (not (string=? schematic-type "normal schematic"))
1595       ;; we have found a .SUBCKT type schematic.
1596           (let* ((io-pin-packages (spice-sdb:get-spice-IO-pins packages (list) ))
1597                  (io-pin-packages-ordered (spice-sdb:sort-spice-IO-pins io-pin-packages))
1598                  (io-nets-list (spice-sdb:get-IO-nets io-pin-packages-ordered (list) ))
1599                 )
1600             (debug-spew "found .SUBCKT type schematic")
1601       ;; now write out .SUBCKT header and .SUBCKT line
1602             (spice-sdb:write-subcircuit-header)
1603             (let ((io-nets-string (list-2-string io-nets-list)) )
1604               (display (string-append schematic-type " " (list-2-string io-nets-list) "\n"))
1605             )
1606           )
1608       ;; Otherwise it's a regular schematic.  Write out command line followed by comments in file header.
1609           (begin
1610             (debug-spew "found normal type schematic")
1611             (let ((title (find-device packages "spice-title"))) ;; If the schematic contains a spice-title device
1612               (when (string? title)
1613                  (set! title (get-value title)) ;; and the value attribute is a string
1614                  (when (string? title)
1615                   (display title) ;;use that as the title of the spice netlist
1616                   (newline)
1617                  )
1618               )
1619             )
1620             (display (string-append "* " (gnetlist:get-command-line) "\n"))
1621             (spice-sdb:write-top-header)
1622           )
1624       ) ;; end of if (not (string=? . . . .
1628 ;; Now loop through all devices and process all "FILE" attributes.  Create
1629 ;; file-info-list.
1630 ;; Thanks to Carlos Nieves Onega for his e-mail to
1631 ;; geda-dev which is the genesis of this section.
1633       (debug-spew "\nMake first pass through design and create list of all model files referenced.\n")
1634       (set! file-info-list (spice-sdb:create-file-info-list packages file-info-list))
1635       (debug-spew "Done creating file-info-list.\n\n")
1639 ;;  Moved this loop before the next one to get numparam to work with ngspice,
1640 ;;  because numparam will at the subckt definition come before the main netlist.
1641 ;;  Change suggested by Dominique Michel; implemented in code on 6.12.2005.
1643 ;;  Next loop through all items in file-info-list in the SPICE netlist.
1644 ;;  For each model-name, open up the corresponding file, and call handle-spice-file
1645 ;;  to stick the corresponding stuff into the output SPICE file.
1647       (debug-spew "Now process the items in model file list -- stick appropriate references to models in output SPICE file.\n")
1648       (spice-sdb:loop-through-files file-info-list)
1649       (debug-spew "Done processing items in model file list.\n")
1653 ;; Now write out netlist as before.  But don't write file contents out.
1654 ;; **** Modified by kh to sort list of packages so Spice directives, etc. (A?) are output last,
1655 ;; **** and in increasing order.
1657       (debug-spew "Make second pass through design and write out a SPICE card for each component found.\n")
1658       (display "*==============  Begin SPICE netlist of main design ============\n")
1659       (if (spice-sdb:sort-refdes? (gnetlist:get-calling-flags))
1660           (spice-sdb:write-netlist file-info-list (sort packages spice-sdb:packsort))  ;; sort on refdes
1661           (spice-sdb:write-netlist file-info-list packages)                            ;; don't sort.
1662       )
1663       (debug-spew "Done writing SPICE cards . . .\n\n")
1667 ;;  Now write out .END(S) of netlist, depending upon whether this schematic is a
1668 ;;  "normal schematic" or a .SUBCKT.
1670       (if (not (string=? schematic-type "normal schematic"))
1671           (begin
1672             (spice-sdb:write-bottom-footer (string-append ".ends " model-name))
1673             (display "*******************************\n")
1674           )
1675           (if (not (calling-flag? "no_end_card" (gnetlist:get-calling-flags)))
1676               (spice-sdb:write-bottom-footer ".end"))
1677       )
1680       (debug-spew "\nOutput file is written.  We are done.\n")
1681    )
1683 ;;  Finally, close up and go home.
1685     (close-output-port (current-output-port))
1690 ;; Custom get-uref function to append ".${SLOT}" where a component
1691 ;; has a "slot=${SLOT}" attribute attached.
1693 ;; NOTE: Original test for appending the ".<SLOT>" was this:
1694 ;;   (let ((numslots (gnetlist:get-package-attribute package "numslots"))
1695 ;;        (slot-count (length (gnetlist:get-unique-slots package)))
1696 ;;     (if (or (string=? numslots "unknown") (string=? numslots "0"))
1698 (define get-uref
1699   (lambda (object)
1700     (let ((real_uref (gnetlist:get-uref object)))
1701       (if (null? (get-attrib-value-by-attrib-name object "slot"))
1702         real_uref
1703         (string-append real_uref "."
1704           (car (get-attrib-value-by-attrib-name object "slot")))
1705       )
1706     )
1707   )