1 /*
2 * ext2spice.c --
3 *
4 * Program to flatten hierarchical .ext files and produce
5 * a .spice file, suitable for use as input to simulators
6 * such as spice and hspice.
7 *
8 * Flattens the tree rooted at file.ext, reading in additional .ext
9 * files as specified by "use" lines in file.ext. The output is left
10 * in file.spice, unless '-o esSpiceFile' is specified, in which case the
11 * output is left in 'esSpiceFile'.
12 *
13 */
14
15 #ifndef lint
16 static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/ext2spice/ext2spice.c,v 1.8 2010/08/25 17:33:56 tim Exp $";
17 #endif /* not lint */
18
19 #include <stdio.h>
20 #include <stdlib.h> /* for atof() */
21 #include <string.h>
22 #include <ctype.h>
23
24 #include "tcltk/tclmagic.h"
25 #include "utils/magic.h"
26 #include "utils/malloc.h"
27 #include "utils/geometry.h"
28 #include "utils/hash.h"
29 #include "utils/dqueue.h"
30 #include "utils/utils.h"
31 #include "tiles/tile.h"
32 #ifdef MAGIC_WRAPPER
33 #include "database/database.h"
34 #include "windows/windows.h"
35 #include "textio/textio.h"
36 #include "dbwind/dbwind.h" /* for DBWclientID */
37 #include "commands/commands.h" /* for module auto-load */
38 #include "textio/txcommands.h"
39 #endif
40 #include "extflat/extflat.h"
41 #include "extflat/EFint.h"
42 #include "extract/extract.h" /* for extDevTable */
43 #include "utils/runstats.h"
44
45 #include "ext2spice/ext2spice.h"
46
47 /* Options specific to ext2spice */
48 bool esDoExtResis = FALSE;
49 bool esDoPorts = TRUE;
50 bool esDoHierarchy = FALSE;
51 bool esDoBlackBox = FALSE;
52 bool esDoRenumber = FALSE;
53 bool esDoResistorTee = FALSE;
54 int esDoSubckt = AUTO;
55 bool esDevNodesOnly = FALSE;
56 bool esMergeNames = TRUE;
57 bool esNoAttrs = FALSE;
58 bool esHierAP = FALSE;
59 char spcesDefaultOut[FNSIZE];
60 int esCapAccuracy = 2;
61 char esSpiceCapFormat[FNSIZE];
62 char *spcesOutName = spcesDefaultOut;
63 FILE *esSpiceF = NULL;
64 float esScale = -1.0 ; /* negative if hspice the EFScale/100 otherwise */
65
66 unsigned short esFormat = SPICE3 ;
67
68 int esCapNum, esDevNum, esResNum, esDiodeNum, esVoltNum;
69 int esNodeNum; /* just in case we're extracting spice2 */
70 int esSbckNum; /* used in hspice node name shortening */
71 int esNoModelType; /* index for device type "None" (model-less device) */
72
73 /*
74 * The following hash table and associated functions are used only if
75 * the format is hspice, to keep the translation between the hierarchical
76 * prefix of a node and the x num that we use to output valid hspice
77 * which also are meaningful.
78 */
79 HashTable subcktNameTable ; /* the hash table itself */
80 DQueue subcktNameQueue ; /* q used to print it sorted at the end */
81
82 fetInfoList esFetInfo[TT_MAXTYPES];
83
84 /* Record for keeping a list of global names */
85
86 typedef struct GLL *globalListPtr;
87
88 typedef struct GLL {
89 globalListPtr gll_next;
90 char *gll_name;
91 } globalList;
92
93 TileTypeBitMask initMask; /* Used for device types, not tile types */
94
95 bool esMergeDevsA = FALSE; /* aggressive merging of devs L1=L2 merge them */
96 bool esMergeDevsC = FALSE; /* conservative merging of devs L1=L2 and W1=W2 */
97 /* used with the hspice multiplier */
98 bool esDistrJunct = FALSE;
99
100 /*
101 *---------------------------------------------------------
102 * Variables & macros used for merging parallel devs
103 * The merging of devs is based on the fact that spcdevVisit
104 * visits the devs in the same order all the time so the
105 * value of esFMult[i] keeps the multiplier for the ith dev
106 *---------------------------------------------------------
107 */
108
109 float *esFMult = NULL; /* the array itself */
110 int esFMIndex = 0; /* current index to it */
111 int esFMSize = FMULT_SIZE ; /* its current size (growable) */
112 int esSpiceDevsMerged;
113
114 devMerge *devMergeList = NULL ;
115
116 /*
117 * ----------------------------------------------------------------------------
118 *
119 * esFreeNodeClient ---
120 *
121 * Free the string spiceNodeName associated with the nodeClient
122 * record that ext2spice allocates per each node structure.
123 *
124 * Returns:
125 * 0 always.
126 *
127 * Side effects:
128 * Frees an allocated string.
129 *
130 * ----------------------------------------------------------------------------
131 */
132
133 int
esFreeNodeClient(client)134 esFreeNodeClient(client)
135 nodeClient *client;
136 {
137 if (client != (ClientData)NULL)
138 if (client->spiceNodeName != NULL)
139 freeMagic((char *)client->spiceNodeName);
140
141 return 0;
142 }
143
144 /*
145 * ----------------------------------------------------------------------------
146 *
147 * Apply modifications to a global node name and output to file "outf"
148 *
149 * Results:
150 * None.
151 *
152 * Side effects:
153 * Output to file
154 * ----------------------------------------------------------------------------
155 */
156
157 void
esFormatSubs(outf,suf)158 esFormatSubs(outf, suf)
159 FILE *outf;
160 char *suf;
161 {
162 char *specchar;
163 int l;
164
165 if (outf)
166 {
167 l = strlen(suf) - 1;
168 if ((EFOutputFlags & EF_TRIMGLOB ) && suf[l] == '!' ||
169 (EFOutputFlags & EF_TRIMLOCAL) && suf[l] == '#')
170 suf[l] = '\0' ;
171 if (EFOutputFlags & EF_CONVERTCOMMA)
172 while ((specchar = strchr(suf, ',')) != NULL)
173 *specchar = '|';
174 if (EFOutputFlags & EF_CONVERTBRACKETS)
175 {
176 while ((specchar = strchr(suf, '[')) != NULL)
177 *specchar = '_';
178 while ((specchar = strchr(suf, ']')) != NULL)
179 *specchar = '_';
180 }
181 if (EFOutputFlags & EF_CONVERTEQUAL)
182 while ((specchar = strchr(suf, '=')) != NULL)
183 *specchar = ':';
184 fprintf(outf, "%s", suf);
185 }
186 }
187
188 #ifdef MAGIC_WRAPPER
189
190 #ifdef EXT2SPICE_AUTO
191 /*
192 * ----------------------------------------------------------------------------
193 *
194 * Tcl package initialization function
195 *
196 * ----------------------------------------------------------------------------
197 */
198
199 int
Exttospice_Init(interp)200 Exttospice_Init(interp)
201 Tcl_Interp *interp;
202 {
203 /* Sanity checks! */
204 if (interp == NULL) return TCL_ERROR;
205 if (Tcl_PkgRequire(interp, "Tclmagic", MAGIC_VERSION, 0) == NULL)
206 return TCL_ERROR;
207 if (Tcl_InitStubs(interp, "8.5", 0) == NULL) return TCL_ERROR;
208
209 TxPrintf("Auto-loading EXTTOSPICE module\n");
210 TxFlushOut();
211
212 /* Replace the auto-load function with the one defined in */
213 /* this package in the command functions list. */
214
215 if (WindReplaceCommand(DBWclientID, "exttospice", CmdExtToSpice) < 0)
216 return TCL_ERROR;
217
218 /* Add "ext2spice" as an alias for "exttospice" */
219 if (WindReplaceCommand(DBWclientID, "ext2spice", CmdExtToSpice) < 0)
220 return TCL_ERROR;
221
222 Tcl_PkgProvide(interp, "Exttospice", MAGIC_VERSION);
223 return TCL_OK;
224 }
225 #endif /* EXT2SPICE_AUTO */
226
227 /*
228 * ----------------------------------------------------------------------------
229 *
230 * Main callback for command "magic::exttospice"
231 *
232 * ----------------------------------------------------------------------------
233 */
234
235 #define EXTTOSPC_RUN 0
236 #define EXTTOSPC_DEFAULT 1
237 #define EXTTOSPC_FORMAT 2
238 #define EXTTOSPC_RTHRESH 3
239 #define EXTTOSPC_CTHRESH 4
240 #define EXTTOSPC_MERGE 5
241 #define EXTTOSPC_EXTRESIST 6
242 #define EXTTOSPC_RESISTORTEE 7
243 #define EXTTOSPC_SCALE 8
244 #define EXTTOSPC_SHORT 9
245 #define EXTTOSPC_SUBCIRCUITS 10
246 #define EXTTOSPC_HIERARCHY 11
247 #define EXTTOSPC_BLACKBOX 12
248 #define EXTTOSPC_RENUMBER 13
249 #define EXTTOSPC_MERGENAMES 14
250 #define EXTTOSPC_LVS 15
251 #define EXTTOSPC_HELP 16
252
253 void
CmdExtToSpice(w,cmd)254 CmdExtToSpice(w, cmd)
255 MagWindow *w;
256 TxCommand *cmd;
257 {
258 int i,flatFlags;
259 char *inName;
260 FILE *f;
261
262 int value;
263 int option = EXTTOSPC_RUN;
264 int argc = cmd->tx_argc;
265 char **argv = cmd->tx_argv;
266 char **msg;
267 char *resstr = NULL;
268 char *substr = NULL;
269 bool err_result, locDoSubckt;
270
271 short s_rclass, d_rclass, sub_rclass;
272 char *devname;
273 char *subname;
274 TileType devtype;
275 int idx, idx2;
276 globalList *glist = NULL;
277
278 static EFCapValue LocCapThreshold = 2;
279 static int LocResistThreshold = INFINITE_THRESHOLD;
280
281 static char *spiceFormats[] = {
282 "SPICE2", "SPICE3", "HSPICE", "NGSPICE", NULL
283 };
284
285 static char *cmdExtToSpcOption[] = {
286 "[run] [options] run exttospice on current cell\n"
287 " use \"run -help\" to get standard options",
288 "default reset to default values",
289 "format [<type>] set output format",
290 "rthresh [<value>] set resistance threshold value",
291 "cthresh [<value>] set capacitance threshold value",
292 "merge [<type>] merge parallel transistors",
293 "extresist [on|off] incorporate information from extresist",
294 "resistor tee [on|off] model resistor capacitance as a T-network",
295 "scale [on|off] use .option card for scaling",
296 "short [voltage|resistor|none]\n"
297 " set method for handling shorted ports",
298 "subcircuits [top|descend] [on|off|auto]\n"
299 " standard cells become subcircuit calls",
300 "hierarchy [on|off] output hierarchical spice for LVS",
301 "blackbox [on|off] output abstract views as black-box entries",
302 "renumber [on|off] on = number instances X1, X2, etc.\n"
303 " off = keep instance ID names",
304 "global [on|off] on = merge unconnected global nets by name",
305 "lvs apply typical default settings for LVS",
306 "help print help information",
307 NULL
308 };
309
310 static char *cmdMergeTypes[] = {
311 "none don't merge parallel devices",
312 "conservative merge devices with same L, W",
313 "aggressive merge devices with same L",
314 NULL
315 };
316
317 static char *cmdShortTypes[] = {
318 "none merge shorted ports",
319 "resistor separate shorted ports with 0 ohm resistor",
320 "voltage separate shorted ports with 0 volt source",
321 NULL
322 };
323 static char *cmdExtToSpcFormat[] = {
324 "spice2",
325 "spice3",
326 "hspice",
327 "ngspice",
328 NULL
329 };
330
331 static char *yesno[] = {
332 "yes",
333 "true",
334 "on",
335 "no",
336 "false",
337 "off",
338 NULL
339 };
340
341 static char *subcktopts[] = {
342 "yes",
343 "true",
344 "on",
345 "no",
346 "false",
347 "off",
348 "automatic",
349 "top",
350 "descend",
351 NULL
352 };
353
354 static char *shorttypes[] = {
355 "none",
356 "resistor",
357 "voltage",
358 NULL
359 };
360
361 typedef enum {
362 IDX_YES, IDX_TRUE, IDX_ON, IDX_NO, IDX_FALSE, IDX_OFF,
363 IDX_AUTO, IDX_TOP, IDX_DESCEND
364 } yesnoType;
365
366 esNoModelType = -1;
367
368 if (cmd->tx_argc > 1)
369 {
370 option = Lookup(cmd->tx_argv[1], cmdExtToSpcOption);
371 if (option < 0) option = EXTTOSPC_RUN;
372 else argv++;
373 }
374
375 switch (option)
376 {
377 case EXTTOSPC_EXTRESIST:
378 if (cmd->tx_argc == 2)
379 {
380 Tcl_SetResult(magicinterp, (esDoExtResis) ? "on" : "off", NULL);
381 return;
382 }
383 else if (cmd->tx_argc != 3)
384 goto usage;
385 idx = Lookup(cmd->tx_argv[2], yesno);
386 if (idx < 0) goto usage;
387 else if (idx < 3) esDoExtResis = TRUE;
388 else esDoExtResis = FALSE;
389 break;
390
391 case EXTTOSPC_RESISTORTEE:
392 if (cmd->tx_argc == 3)
393 {
394 Tcl_SetResult(magicinterp, (esDoResistorTee) ? "on" : "off", NULL);
395 return;
396 }
397 else if (cmd->tx_argc != 4)
398 goto usage;
399 idx = Lookup(cmd->tx_argv[3], yesno);
400 if (idx < 0) goto usage;
401 else if (idx < 3) esDoResistorTee = TRUE;
402 else esDoResistorTee = FALSE;
403 break;
404
405 case EXTTOSPC_SCALE:
406 if (cmd->tx_argc == 2)
407 {
408 Tcl_SetResult(magicinterp, (esScale < 0) ? "on" : "off", NULL);
409 return;
410 }
411 else if (cmd->tx_argc != 3)
412 goto usage;
413 idx = Lookup(cmd->tx_argv[2], yesno);
414 if (idx < 0) goto usage;
415 else if (idx < 3) esScale = -1.0;
416 else esScale = 0.0;
417 break;
418
419 case EXTTOSPC_HIERARCHY:
420 if (cmd->tx_argc == 2)
421 {
422 Tcl_SetResult(magicinterp, (esDoHierarchy) ? "on" : "off", NULL);
423 return;
424 }
425 idx = Lookup(cmd->tx_argv[2], yesno);
426 if (idx < 0) goto usage;
427 else if (idx < 3) /* yes */
428 esDoHierarchy = TRUE;
429 else /* no */
430 esDoHierarchy = FALSE;
431 break;
432
433 case EXTTOSPC_BLACKBOX:
434 if (cmd->tx_argc == 2)
435 {
436 Tcl_SetResult(magicinterp, (esDoBlackBox) ? "on" : "off", NULL);
437 return;
438 }
439 idx = Lookup(cmd->tx_argv[2], yesno);
440 if (idx < 0) goto usage;
441 else if (idx < 3) /* yes */
442 esDoBlackBox = TRUE;
443 else /* no */
444 esDoBlackBox = FALSE;
445 break;
446
447 case EXTTOSPC_RENUMBER:
448 if (cmd->tx_argc == 2)
449 {
450 Tcl_SetResult(magicinterp, (esDoRenumber) ? "on" : "off", NULL);
451 return;
452 }
453 idx = Lookup(cmd->tx_argv[2], yesno);
454 if (idx < 0) goto usage;
455 else if (idx < 3) /* yes */
456 esDoRenumber = TRUE;
457 else /* no */
458 esDoRenumber = FALSE;
459 break;
460
461 case EXTTOSPC_MERGENAMES:
462 if (cmd->tx_argc == 2)
463 {
464 Tcl_SetResult(magicinterp, (esMergeNames) ? "on" : "off", NULL);
465 return;
466 }
467 idx = Lookup(cmd->tx_argv[2], yesno);
468 if (idx < 0) goto usage;
469 else if (idx < 3) /* yes */
470 esMergeNames = TRUE;
471 else /* no */
472 esMergeNames = FALSE;
473 break;
474
475 case EXTTOSPC_SHORT:
476 if (cmd->tx_argc == 2)
477 {
478 if ((EFOutputFlags & EF_SHORT_MASK) == EF_SHORT_NONE)
479 Tcl_SetResult(magicinterp, "none", NULL);
480 else if ((EFOutputFlags & EF_SHORT_MASK) == EF_SHORT_R)
481 Tcl_SetResult(magicinterp, "resistor", NULL);
482 else if ((EFOutputFlags & EF_SHORT_MASK) == EF_SHORT_V)
483 Tcl_SetResult(magicinterp, "voltage source", NULL);
484 return;
485 }
486 idx = Lookup(cmd->tx_argv[2], cmdShortTypes);
487 if (idx < 0) goto usage;
488 else switch (idx)
489 {
490 case 0:
491 EFOutputFlags &= ~EF_SHORT_MASK;
492 EFOutputFlags |= EF_SHORT_NONE;
493 break;
494 case 1:
495 EFOutputFlags &= ~EF_SHORT_MASK;
496 EFOutputFlags |= EF_SHORT_R;
497 break;
498 case 2:
499 EFOutputFlags &= ~EF_SHORT_MASK;
500 EFOutputFlags |= EF_SHORT_V;
501 break;
502 }
503 break;
504
505 case EXTTOSPC_LVS:
506 /* Apply default command settings for LVS */
507 /* hierarchy = on */
508 /* format = ngspice */
509 /* cthresh = infinite */
510 /* rthresh = infinite */
511 /* renumber = off */
512 /* scale = off */
513 /* blackbox = on */
514 /* global = off */
515 /* subcircuit top = auto */
516
517 esDoHierarchy = TRUE;
518 esFormat = NGSPICE;
519 LocCapThreshold = (EFCapValue)INFINITE_THRESHOLD_F;
520 LocResistThreshold = INFINITE_THRESHOLD;
521 esDoRenumber = FALSE;
522 esScale = 0.0;
523 esDoBlackBox = TRUE;
524 esMergeNames = FALSE;
525 esDoSubckt = 2;
526 break;
527
528 case EXTTOSPC_SUBCIRCUITS:
529 if (cmd->tx_argc == 2)
530 {
531 Tcl_SetResult(magicinterp, (esDoPorts) ? "on" : "off", NULL);
532 return;
533 }
534 idx = Lookup(cmd->tx_argv[2], subcktopts);
535 switch (idx) {
536 case IDX_YES: case IDX_TRUE: case IDX_ON:
537 esDoPorts = TRUE;
538 return;
539 break;
540 case IDX_NO: case IDX_FALSE: case IDX_OFF:
541 esDoPorts = FALSE;
542 return;
543 break;
544 case IDX_DESCEND:
545 if (cmd->tx_argc == 3)
546 {
547 Tcl_SetResult(magicinterp, (esDoPorts) ? "on" : "off", NULL);
548 return;
549 }
550 break;
551 case IDX_TOP:
552 if (cmd->tx_argc == 3)
553 {
554 Tcl_SetResult(magicinterp,
555 (esDoSubckt == 2) ? "auto" :
556 (esDoSubckt == 1) ? "on" : "off", NULL);
557 return;
558 }
559 break;
560 default:
561 goto usage;
562 break;
563 }
564
565 if (cmd->tx_argc != 4) goto usage;
566 idx2 = Lookup(cmd->tx_argv[3], subcktopts);
567 switch (idx2) {
568 case IDX_YES: case IDX_TRUE: case IDX_ON:
569 if (idx == IDX_DESCEND)
570 esDoPorts = TRUE;
571 else
572 esDoSubckt = TRUE;
573 break;
574 case IDX_NO: case IDX_FALSE: case IDX_OFF:
575 if (idx == IDX_DESCEND)
576 esDoPorts = FALSE;
577 else
578 esDoSubckt = FALSE;
579 break;
580 case IDX_AUTO:
581 esDoSubckt = AUTO;
582 break;
583 default:
584 goto usage;
585 }
586 break;
587
588 case EXTTOSPC_FORMAT:
589 if (cmd->tx_argc == 2)
590 {
591 Tcl_SetResult(magicinterp, cmdExtToSpcFormat[esFormat], NULL);
592 return;
593 }
594 else if (cmd->tx_argc < 3) goto usage;
595 idx = Lookup(cmd->tx_argv[2], cmdExtToSpcFormat);
596 if (idx < 0)
597 {
598 Tcl_SetResult(magicinterp, "Bad format type. Formats are:"
599 "spice2, spice3, hspice, and ngspice.", NULL);
600 return;
601 }
602 else
603 {
604 esFormat = idx;
605 /* By default, use .option to declare scale in HSPICE mode */
606 if (esFormat == HSPICE) esScale = -1.0;
607 }
608 break;
609
610 case EXTTOSPC_CTHRESH:
611 if (cmd->tx_argc == 2)
612 {
613 if (!IS_FINITE_F(LocCapThreshold))
614 Tcl_SetResult(magicinterp, "infinite", NULL);
615 else
616 Tcl_SetObjResult(magicinterp,
617 Tcl_NewDoubleObj((double)LocCapThreshold));
618 return;
619 }
620 else if (cmd->tx_argc < 3) goto usage;
621
622 /* Note that strtod() (called by StrIsNumeric()) accepts */
623 /* "infinite" as a valid numerical value; however, the */
624 /* conversion to C type INF is *not* INFINITE_THRESHOLD, so */
625 /* we need to check this case first. . . */
626
627 if (!strncmp(cmd->tx_argv[2], "inf", 3))
628 LocCapThreshold = (EFCapValue)INFINITE_THRESHOLD_F;
629 else if (StrIsNumeric(cmd->tx_argv[2]))
630 LocCapThreshold = atoCap(cmd->tx_argv[2]);
631 else
632 TxError("exttospice: numeric value or \"infinite\" expected.\n");
633 break;
634
635 case EXTTOSPC_RTHRESH:
636 if (cmd->tx_argc == 2)
637 {
638 if (LocResistThreshold == INFINITE_THRESHOLD)
639 Tcl_SetResult(magicinterp, "infinite", NULL);
640 else
641 Tcl_SetObjResult(magicinterp,
642 Tcl_NewIntObj(LocResistThreshold));
643 return;
644 }
645 else if (cmd->tx_argc < 3) goto usage;
646 if (StrIsInt(cmd->tx_argv[2]))
647 LocResistThreshold = atoi(cmd->tx_argv[2]);
648 else if (!strncmp(cmd->tx_argv[2], "inf", 3))
649 LocResistThreshold = INFINITE_THRESHOLD;
650 else
651 TxError("exttospice: integer value or \"infinite\" expected.\n");
652 break;
653
654 case EXTTOSPC_MERGE:
655 if (cmd->tx_argc == 2)
656 {
657 if (esMergeDevsA)
658 Tcl_SetResult(magicinterp, "aggressive", NULL);
659 else if (esMergeDevsC)
660 Tcl_SetResult(magicinterp, "conservative", NULL);
661 else
662 Tcl_SetResult(magicinterp, "none", NULL);
663 return;
664 }
665 else if (cmd->tx_argc < 3) goto usage;
666 value = Lookup(cmd->tx_argv[2], cmdMergeTypes);
667 if (value < 0)
668 {
669 TxError("Merge types are:\n");
670 for (msg = &(cmdMergeTypes[0]); *msg != NULL; msg++)
671 TxPrintf(" %s\n", *msg);
672 }
673 else switch (value) {
674 case 0:
675 esMergeDevsA = FALSE;
676 esMergeDevsC = FALSE;
677 break;
678 case 1:
679 esMergeDevsA = FALSE;
680 esMergeDevsC = TRUE;
681 break;
682 case 2:
683 esMergeDevsA = TRUE;
684 esMergeDevsC = FALSE;
685 break;
686 }
687 break;
688
689 case EXTTOSPC_DEFAULT:
690 LocCapThreshold = 2;
691 LocResistThreshold = INFINITE_THRESHOLD;
692 /* Clear EFOutputFlags but preserve "short" behavior */
693 EFOutputFlags &= ~EF_TRIM_MASK;
694 EFOutputFlags |= EF_CONVERTCOMMA | EF_CONVERTEQUAL;
695 EFScale = 0.0;
696 if (EFArgTech)
697 {
698 freeMagic(EFArgTech);
699 EFArgTech = NULL;
700 }
701 if (EFSearchPath)
702 {
703 freeMagic(EFSearchPath);
704 EFSearchPath = NULL;
705 }
706 break;
707
708 case EXTTOSPC_RUN:
709 goto runexttospice;
710 break;
711
712 case EXTTOSPC_HELP:
713 usage:
714 for (msg = &(cmdExtToSpcOption[0]); *msg != NULL; msg++)
715 {
716 TxPrintf(" %s\n", *msg);
717 }
718 break;
719 }
720 return;
721
722 runexttospice:
723
724 /* Reset the device indices */
725 esCapNum = 0;
726 esVoltNum = 0;
727 esDevNum = 1000;
728 esResNum = 0;
729 esDiodeNum = 0;
730 esSbckNum = 0;
731 esNodeNum = 10; /* just in case we're extracting spice2 */
732 esFMIndex = 0;
733 esSpiceDevsMerged = 0;
734 esDevNodesOnly = FALSE; /* so using -F doesn't become permanent */
735
736 EFInit();
737
738 EFResistThreshold = LocResistThreshold;
739 EFCapThreshold = LocCapThreshold;
740
741 /* Process command line arguments */
742
743 inName = EFArgs(argc, argv, &err_result, spcmainArgs, (ClientData) NULL);
744 if (err_result == TRUE)
745 {
746 EFDone();
747 return;
748 }
749
750 if (inName == NULL)
751 {
752 /* Assume that we want to do exttospice on the currently loaded cell */
753
754 if (w == (MagWindow *) NULL)
755 {
756 windCheckOnlyWindow(&w, DBWclientID);
757 }
758
759 if (w == (MagWindow *) NULL)
760 {
761 TxError("Point to a window or specify a cell name.\n");
762 return;
763 }
764 if ((inName = ((CellUse *) w->w_surfaceID)->cu_def->cd_name) == NULL)
765 {
766 TxError("No cell present\n");
767 return;
768 }
769 }
770
771 /*
772 * Initializations specific to this program.
773 * Make output name inName.spice if they weren't
774 * explicitly specified
775 */
776
777 if (spcesOutName == spcesDefaultOut)
778 sprintf(spcesDefaultOut, "%s.spice", inName);
779
780 /* Read the hierarchical description of the input circuit */
781 if (EFReadFile(inName, TRUE, esDoExtResis, FALSE) == FALSE)
782 {
783 EFDone();
784 return;
785 }
786
787 /* If the .ext file was read without error, then open the output file */
788
789 if ((esSpiceF = fopen(spcesOutName, "w")) == NULL)
790 {
791 char *tclres = Tcl_Alloc(128);
792 sprintf(tclres, "exttospice: Unable to open file %s for writing\n",
793 spcesOutName);
794 Tcl_SetResult(magicinterp, tclres, TCL_DYNAMIC);
795 EFDone();
796 return;
797 }
798
799 if (EFStyle == NULL)
800 {
801 TxError("Warning: Current extraction style does not match .ext file!\n");
802 TxError("Area/Perimeter values and parasitic values will be zero.\n");
803 }
804
805 /* create default devinfo entries (MOSIS) which can be overridden by
806 the command line arguments */
807
808 for ( i = 0 ; i < TT_MAXTYPES ; i++ ) {
809 esFetInfo[i].resClassSource = NO_RESCLASS;
810 esFetInfo[i].resClassDrain = NO_RESCLASS;
811 esFetInfo[i].resClassSub = NO_RESCLASS;
812 esFetInfo[i].defSubs = NULL;
813 }
814
815 /* Get esFetInfo information from the current extraction style */
816 /* (this works only for the Tcl version with the embedded exttospice */
817 /* command) */
818
819 idx = 0;
820 while (ExtGetDevInfo(idx++, &devname, &devtype, &s_rclass, &d_rclass,
821 &sub_rclass, &subname))
822 {
823 if (idx == TT_MAXTYPES)
824 {
825 TxError("Error: Ran out of space for device types!\n");
826 break;
827 }
828 i = efBuildAddStr(EFDevTypes, &EFDevNumTypes, TT_MAXTYPES, devname);
829 if (!strcmp(devname, "None"))
830 esNoModelType = i;
831 if (EFStyle != NULL)
832 {
833 esFetInfo[i].resClassSource = s_rclass;
834 esFetInfo[i].resClassDrain = d_rclass;
835 esFetInfo[i].resClassSub = sub_rclass;
836 esFetInfo[i].defSubs = subname;
837 }
838
839 if (EFCompat == TRUE)
840 {
841 /* Tcl variable substitution for substrate node names */
842 if (subname && (subname[0] == '$'))
843 {
844 resstr = (char *)Tcl_GetVar(magicinterp, &subname[1],
845 TCL_GLOBAL_ONLY);
846 if (resstr != NULL) esFetInfo[i].defSubs = resstr;
847 }
848 }
849
850 if (esDoHierarchy && (subname != NULL))
851 {
852 globalList *glptr;
853 char *locsubname, *bangptr;
854 bool isgood = TRUE;
855
856 locsubname = StrDup(NULL, subname);
857
858 bangptr = locsubname + strlen(locsubname) - 1;
859 if (*bangptr == '!') *bangptr = '\0';
860
861 // Ad-hoc check: Global names with "Error", "err", etc.
862 // should be rejected from the list. Also node name
863 // "None" is a common entry indicating that extracting
864 // an implicit substrate is disallowed.
865
866 if (!strncmp(locsubname, "err", 3)) isgood = FALSE;
867 else if (strstr(locsubname, "error") != NULL) isgood = FALSE;
868 else if (strstr(locsubname, "Error") != NULL) isgood = FALSE;
869 else if (strstr(locsubname, "ERROR") != NULL) isgood = FALSE;
870 else if (!strcasecmp(locsubname, "None")) isgood = FALSE;
871
872 for (glptr = glist; glptr; glptr = glptr->gll_next)
873 if (!strcmp(glptr->gll_name, locsubname))
874 break;
875
876 if (isgood && (glptr == NULL))
877 {
878 glptr = (globalList *)mallocMagic(sizeof(globalList));
879 glptr->gll_name = locsubname;
880 glptr->gll_next = glist;
881 glist = glptr;
882 }
883 else
884 freeMagic(locsubname);
885 }
886 }
887
888 if (EFCompat == TRUE)
889 {
890 /* Keep a pointer to the "GND" variable, if it exists. */
891
892 resstr = (char *)Tcl_GetVar(magicinterp, "GND", TCL_GLOBAL_ONLY);
893 if (resstr == NULL) resstr = "GND"; /* default value */
894 }
895
896 /* Write the output file */
897
898 fprintf(esSpiceF, "* %s file created from %s.ext - technology: %s\n\n",
899 spiceFormats[esFormat], inName, EFTech);
900 if (esScale < 0)
901 fprintf(esSpiceF, ".option scale=%gu\n\n", EFScale / 100.0);
902 else
903 esScale = EFScale / 100.0;
904
905 /* Set output format flags */
906
907 flatFlags = EF_FLATNODES;
908 if (esMergeNames == FALSE) flatFlags |= EF_NONAMEMERGE;
909
910 // This forces options TRIMGLOB and CONVERTEQUAL, not sure that's such a
911 // good idea. . .
912 EFOutputFlags |= EF_TRIMGLOB | EF_CONVERTEQUAL | EF_CONVERTCOMMA;
913 if (IS_FINITE_F(EFCapThreshold)) flatFlags |= EF_FLATCAPS;
914 if (esFormat == HSPICE)
915 EFOutputFlags |= EF_TRIMLOCAL;
916
917 /* Write globals under a ".global" card */
918
919 if (esDoHierarchy && (glist != NULL))
920 {
921 fprintf(esSpiceF, ".global ");
922 while (glist != NULL)
923 {
924 if (EFCompat == TRUE)
925 {
926 /* Handle global names that are TCL variables */
927 if (glist->gll_name[0] == '$')
928 {
929 resstr = (char *)Tcl_GetVar(magicinterp,
930 &(glist->gll_name[1]), TCL_GLOBAL_ONLY);
931 if (resstr != NULL)
932 esFormatSubs(esSpiceF, resstr);
933 else
934 esFormatSubs(esSpiceF, glist->gll_name);
935 }
936 else
937 esFormatSubs(esSpiceF, glist->gll_name);
938 }
939 else
940 esFormatSubs(esSpiceF, glist->gll_name);
941
942 fprintf(esSpiceF, " ");
943 freeMagic(glist->gll_name);
944 freeMagic(glist);
945 glist = glist->gll_next;
946 }
947 fprintf(esSpiceF, "\n\n");
948 }
949
950 /* Convert the hierarchical description to a flat one */
951
952 if (esFormat == HSPICE) {
953 HashInit(&subcktNameTable, 32, HT_STRINGKEYS);
954 #ifndef UNSORTED_SUBCKT
955 DQInit(&subcktNameQueue, 64);
956 #endif
957 }
958 locDoSubckt = FALSE;
959 if (esDoHierarchy)
960 {
961 ESGenerateHierarchy(inName, flatFlags);
962 }
963 else
964 {
965 EFFlatBuild(inName, flatFlags);
966
967 /* Determine if this is a subcircuit */
968 if (esDoSubckt == AUTO) {
969 if (efFlatRootDef->def_flags & DEF_SUBCIRCUIT)
970 locDoSubckt = TRUE;
971 }
972 if ((esDoSubckt == TRUE) || (locDoSubckt == TRUE))
973 topVisit(efFlatRootDef, FALSE);
974
975 /* When generating subcircuits, remove the subcircuit */
976 /* flag from the top level cell. Other than being */
977 /* used to generate the subcircuit wrapper, it should */
978 /* not prevent descending into its own hierarchy. */
979
980 efFlatRootDef->def_flags &= ~(DEF_SUBCIRCUIT);
981
982 /* If we don't want to write subcircuit calls, remove */
983 /* the subcircuit flag from all cells at this time. */
984
985 if (!esDoPorts)
986 EFVisitSubcircuits(subcktUndef, (ClientData) NULL);
987
988 TTMaskZero(&initMask);
989 if (!esDistrJunct)
990 TTMaskCom(&initMask);
991
992 if (esMergeDevsA || esMergeDevsC)
993 {
994 devMerge *p;
995
996 EFVisitDevs(devMergeVisit, (ClientData) NULL);
997 TxPrintf("Devs merged: %d\n", esSpiceDevsMerged);
998 esFMIndex = 0;
999 for (p = devMergeList; p != NULL; p = p->next)
1000 freeMagic(p);
1001 devMergeList = NULL;
1002 }
1003 else if (esDistrJunct)
1004 EFVisitDevs(devDistJunctVisit, (ClientData) NULL);
1005 EFVisitDevs(spcdevVisit, (ClientData) NULL);
1006 TTMaskZero(&initMask);
1007 if (flatFlags & EF_FLATCAPS)
1008 {
1009 (void) sprintf( esSpiceCapFormat, "C%%d %%s %%s %%.%dlffF\n",
1010 esCapAccuracy);
1011 EFVisitCaps(spccapVisit, (ClientData) NULL);
1012 }
1013 EFVisitResists(spcresistVisit, (ClientData) NULL);
1014 EFVisitSubcircuits(subcktVisit, (ClientData) NULL);
1015
1016 /* Visit nodes to find the substrate node */
1017 EFVisitNodes(spcsubVisit, (ClientData)&substr);
1018 if (substr == NULL)
1019 substr = StrDup((char **)NULL, "0");
1020
1021 (void) sprintf( esSpiceCapFormat, "C%%d %%s %s %%.%dlffF%%s",
1022 substr, esCapAccuracy);
1023 EFVisitNodes(spcnodeVisit, (ClientData) NULL);
1024
1025 if (EFCompat == FALSE) freeMagic(substr);
1026
1027 if ((esDoSubckt == TRUE) || (locDoSubckt == TRUE))
1028 fprintf(esSpiceF, ".ends\n");
1029
1030 if (esFormat == HSPICE)
1031 printSubcktDict();
1032
1033 EFFlatDone(esFreeNodeClient);
1034 }
1035 EFDone(esFreeNodeClient);
1036 if (esFormat == HSPICE) {
1037 HashKill(&subcktNameTable);
1038 #ifndef UNSORTED_SUBCKT
1039 DQFree(&subcktNameQueue);
1040 #endif
1041 }
1042
1043 if (esSpiceF) fclose(esSpiceF);
1044
1045 TxPrintf("exttospice finished.\n");
1046 return;
1047 }
1048
1049 #else /* MAGIC_WRAPPER */
1050
1051 /*
1052 * ----------------------------------------------------------------------------
1053 *
1054 * main --
1055 *
1056 * Top level of ext2spice.
1057 *
1058 * ----------------------------------------------------------------------------
1059 */
1060
1061 int
main(argc,argv)1062 main(argc, argv)
1063 int argc;
1064 char *argv[];
1065 {
1066
1067 int i,flatFlags;
1068 char *inName;
1069 FILE *f;
1070 bool locDoSubckt;
1071
1072 esSpiceDevsMerged = 0;
1073
1074 static char *spiceFormats[] = {
1075 "SPICE2", "SPICE3", "HSPICE", "NGSPICE", NULL
1076 };
1077
1078 EFInit();
1079 EFResistThreshold = INFINITE_THRESHOLD ;
1080 /* create default devinfo entries (MOSIS) which can be overriden by
1081 the command line arguments */
1082 for ( i = 0 ; i < TT_MAXTYPES ; i++ ) {
1083 esFetInfo[i].resClassSource = NO_RESCLASS;
1084 esFetInfo[i].resClassDrain = NO_RESCLASS;
1085 esFetInfo[i].resClassSub = NO_RESCLASS;
1086 esFetInfo[i].defSubs = NULL;
1087 }
1088 i = efBuildAddStr(EFDevTypes, &EFDevNumTypes, TT_MAXTYPES, "ndev");
1089 esFetInfo[i].resClassSource = esFetInfo[i].resClassDrain = 0 ;
1090 esFetInfo[i].resClassSub = NO_RESCLASS ;
1091 esFetInfo[i].defSubs = "Gnd!";
1092 i = efBuildAddStr(EFDevTypes, &EFDevNumTypes, TT_MAXTYPES, "pdev");
1093 esFetInfo[i].resClassSource = esFetInfo[i].resClassDrain = 1 ;
1094 esFetInfo[i].resClassSub = 8 ;
1095 esFetInfo[i].defSubs = "Vdd!";
1096 i = efBuildAddStr(EFDevTypes, &EFDevNumTypes, TT_MAXTYPES, "nmos");
1097 esFetInfo[i].resClassSource = esFetInfo[i].resClassDrain = 0 ;
1098 esFetInfo[i].resClassSub = NO_RESCLASS ;
1099 esFetInfo[i].defSubs = "Gnd!";
1100 i = efBuildAddStr(EFDevTypes, &EFDevNumTypes, TT_MAXTYPES, "pmos");
1101 esFetInfo[i].resClassSource = esFetInfo[i].resClassDrain = 1 ;
1102 esFetInfo[i].resClassSub = 8 ;
1103 esFetInfo[i].defSubs = "Vdd!";
1104 /* Process command line arguments */
1105
1106 inName = EFArgs(argc, argv, NULL, spcmainArgs, (ClientData) NULL);
1107 if (inName == NULL)
1108 exit (1);
1109
1110 /*
1111 * Initializations specific to this program.
1112 * Make output name inName.spice if they weren't
1113 * explicitly specified
1114 */
1115
1116 if (spcesOutName == spcesDefaultOut)
1117 sprintf(spcesDefaultOut, "%s.spice", inName);
1118
1119 if ((esSpiceF = fopen(spcesOutName, "w")) == NULL)
1120 {
1121 perror(spcesOutName);
1122 exit (1);
1123 }
1124
1125 /* Read the hierarchical description of the input circuit */
1126 if (EFReadFile(inName, TRUE, esDoExtResis, FALSE) == FALSE)
1127 {
1128 exit (1);
1129 }
1130
1131 fprintf(esSpiceF, "* %s file created from %s.ext - technology: %s\n\n",
1132 spiceFormats[esFormat], inName, EFTech);
1133 if (esScale < 0)
1134 fprintf(esSpiceF,".option scale=%gu\n\n", EFScale / 100.0);
1135 else
1136 esScale = EFScale / 100.0;
1137
1138 /* Convert the hierarchical description to a flat one */
1139 flatFlags = EF_FLATNODES;
1140 EFOutputFlags |= EF_TRIMGLOB ;
1141 if (IS_FINITE_F(EFCapThreshold)) flatFlags |= EF_FLATCAPS;
1142 if (esFormat == HSPICE) {
1143 EFOutputFlags |= EF_TRIMLOCAL ;
1144 HashInit(&subcktNameTable, 32, HT_STRINGKEYS);
1145 #ifndef UNSORTED_SUBCKT
1146 DQInit(&subcktNameQueue, 64);
1147 #endif
1148 }
1149 EFFlatBuild(inName, flatFlags);
1150
1151 /* Determine if this is a subcircuit */
1152 locDoSubckt = FALSE;
1153 if (esDoSubckt == AUTO) {
1154 if (efFlatRootDef->def_flags & DEF_SUBCIRCUIT)
1155 locDoSubckt = TRUE;
1156 }
1157 if ((esDoSubckt == TRUE) || (locDoSubckt == TRUE))
1158 topVisit(efFlatRootDef, FALSE);
1159
1160 /* If we don't want to write subcircuit calls, remove the */
1161 /* subcircuit flag from all cells at this time. */
1162
1163 if (!esDoPorts)
1164 EFVisitSubcircuits(subcktUndef, (ClientData) NULL);
1165
1166 TTMaskZero(&initMask);
1167 if (!esDistrJunct)
1168 TTMaskCom(&initMask);
1169
1170 if ( esMergeDevsA || esMergeDevsC ) {
1171 EFVisitDevs(devMergeVisit, (ClientData) NULL);
1172 TxPrintf("Devs merged: %d\n", esSpiceDevsMerged);
1173 esFMIndex = 0 ;
1174 {
1175 devMerge *p;
1176
1177 for ( p = devMergeList ; p != NULL ; p=p->next ) freeMagic(p);
1178 }
1179 } else if ( esDistrJunct )
1180 EFVisitDevs(devDistJunctVisit, (ClientData) NULL);
1181 EFVisitDevs(spcdevVisit, (ClientData) NULL);
1182 TTMaskZero(&initMask);
1183 if (flatFlags & EF_FLATCAPS) {
1184 (void) sprintf( esSpiceCapFormat, "C%%d %%s %%s %%.%dlffF\n",esCapAccuracy);
1185 EFVisitCaps(spccapVisit, (ClientData) NULL);
1186 }
1187 EFVisitResists(spcresistVisit, (ClientData) NULL);
1188 EFVisitSubcircuits(subcktVisit, (ClientData) NULL);
1189 (void) sprintf( esSpiceCapFormat, "C%%d %%s GND %%.%dlffF%%s", esCapAccuracy);
1190 EFVisitNodes(spcnodeVisit, (ClientData) NULL);
1191
1192 if ((esDoSubckt == TRUE) || (locDoSubckt == TRUE))
1193 fprintf(esSpiceF, ".ends\n");
1194
1195 if (esFormat == HSPICE)
1196 printSubcktDict();
1197
1198 EFFlatDone(esFreeNodeClient);
1199 EFDone(esFreeNodeClient);
1200 if (esFormat == HSPICE) {
1201 HashKill(&subcktNameTable);
1202 #ifndef UNSORTED_SUBCKT
1203 DQFree(&subcktNameQueue);
1204 #endif
1205 }
1206
1207 if (esSpiceF) fclose(esSpiceF);
1208
1209 TxPrintf("Memory used: %s\n", RunStats(RS_MEM, NULL, NULL));
1210 exit (0);
1211 }
1212
1213 #endif /* MAGIC_WRAPPER */
1214
1215
1216 /*
1217 * ----------------------------------------------------------------------------
1218 *
1219 * spcmainArgs --
1220 *
1221 * Process those arguments that are specific to ext2spice.
1222 * Assumes that *pargv[0][0] is '-', indicating a flag
1223 * argument.
1224 *
1225 * Results:
1226 * None.
1227 *
1228 * Side effects:
1229 * After processing an argument, updates *pargc and *pargv
1230 * to point to after the argument.
1231 *
1232 * May initialize various global variables based on the
1233 * arguments given to us.
1234 *
1235 * Exits in the event of an improper argument.
1236 *
1237 * ----------------------------------------------------------------------------
1238 */
1239
1240 int
spcmainArgs(pargc,pargv)1241 spcmainArgs(pargc, pargv)
1242 int *pargc;
1243 char ***pargv;
1244 {
1245 char **argv = *pargv, *cp;
1246 int argc = *pargc;
1247
1248 char usage_text[] = "Usage: ext2spice "
1249 "[-B] [-o spicefile] [-M|-m] [-y cap_digits] "
1250 "[-J flat|hier]\n"
1251 "[-f spice2|spice3|hspice|ngspice] [-M] [-m] "
1252 #ifdef MAGIC_WRAPPER
1253 "[file]\n";
1254 #else
1255 "[-j device:sdRclass[/subRclass]/defaultSubstrate]\n"
1256 "file\n\n or else see options to extcheck(1)\n";
1257 #endif
1258
1259 switch (argv[0][1])
1260 {
1261 case 'd':
1262 esDistrJunct = TRUE;
1263 break;
1264 case 'M':
1265 esMergeDevsA = TRUE;
1266 break;
1267 case 'm':
1268 esMergeDevsC = TRUE;
1269 break;
1270 case 'B':
1271 esNoAttrs = TRUE;
1272 break;
1273 case 'F':
1274 esDevNodesOnly = TRUE;
1275 break;
1276 case 'o':
1277 if ((spcesOutName = ArgStr(&argc, &argv, "filename")) == NULL)
1278 goto usage;
1279 break;
1280 case 'f': {
1281 char *ftmp ;
1282
1283 if ((ftmp = ArgStr(&argc, &argv, "format")) == NULL)
1284 goto usage;
1285 if (strcasecmp(ftmp, "SPICE2") == 0)
1286 esFormat = SPICE2;
1287 else if (strcasecmp(ftmp, "SPICE3") == 0)
1288 esFormat = SPICE3;
1289 else if (strcasecmp(ftmp, "HSPICE") == 0)
1290 {
1291 esFormat = HSPICE;
1292 esScale = -1.0;
1293 }
1294 else if (strcasecmp(ftmp, "NGSPICE") == 0)
1295 esFormat = NGSPICE;
1296 else goto usage;
1297 break;
1298 }
1299 case 'J':
1300 {
1301 char *ftmp ;
1302
1303 if ((ftmp = ArgStr(&argc, &argv, "hierAP_SD")) == NULL)
1304 goto usage;
1305 if ( strcasecmp(ftmp, "HIER") == 0 )
1306 esHierAP = TRUE ;
1307 else if ( strcasecmp(ftmp, "FLAT") == 0 )
1308 esHierAP = FALSE ;
1309 else goto usage;
1310
1311 break;
1312 }
1313 case 'y': {
1314 char *t;
1315
1316 if (( t = ArgStr(&argc, &argv, "cap-accuracy") ) == NULL)
1317 goto usage;
1318 esCapAccuracy = atoi(t);
1319 break;
1320 }
1321 #ifndef MAGIC_WRAPPER
1322 case 'j':
1323 {
1324 char *rp, subsNode[80] ;
1325 int ndx, rClass, rClassSub = -1;
1326
1327 if ((cp = ArgStr(&argc,&argv,"resistance class")) == NULL)
1328 goto usage;
1329 if ( (rp = (char *) strchr(cp,':')) == NULL )
1330 goto usage;
1331 else *rp++ = '\0';
1332 if ( sscanf(rp, "%d/%d/%s", &rClass, &rClassSub, subsNode) != 3 ) {
1333 rClassSub = -1 ;
1334 if ( sscanf(rp, "%d/%s", &rClass, subsNode) != 2 ) goto usage;
1335 }
1336 ndx = efBuildAddStr(EFDevTypes, &EFDevNumTypes, TT_MAXTYPES, cp);
1337 esFetInfo[ndx].resClassSource = esFetInfo[ndx].resClassDrain = rClass;
1338 esFetInfo[ndx].resClassSub = rClassSub;
1339 esFetInfo[ndx].defSubs = (char *)mallocMagic((unsigned)(strlen(subsNode)+1));
1340 strcpy(esFetInfo[ndx].defSubs,subsNode);
1341 TxError("info: dev %s(%d) srcRclass=%d drnRclass=%d subRclass=%d dSub=%s\n",
1342 cp, ndx, esFetInfo[ndx].resClassSource, esFetInfo[ndx].resClassDrain,
1343 esFetInfo[ndx].resClassSub, esFetInfo[ndx].defSubs);
1344 break;
1345 }
1346 #endif /* MAGIC_WRAPPER */
1347 case 'h': /* -h or -help, as suggested by "ext2spice help" */
1348 TxPrintf(usage_text);
1349 break;
1350 default:
1351 TxError("Unrecognized flag: %s\n", argv[0]);
1352 goto usage;
1353 }
1354
1355 *pargv = argv;
1356 *pargc = argc;
1357 return 0;
1358
1359 usage:
1360 TxError(usage_text);
1361
1362 #ifdef MAGIC_WRAPPER
1363 return 1;
1364 #else
1365 exit (1);
1366 #endif
1367 }
1368
1369 /*
1370 * ----------------------------------------------------------------------------
1371 *
1372 * SpiceGetNode --
1373 *
1374 * function to find a node given its hierarchical prefix and suffix
1375 *
1376 * Results:
1377 * a pointer to the node struct or NULL
1378 *
1379 * ----------------------------------------------------------------------------
1380 */
1381 EFNode *
SpiceGetNode(prefix,suffix)1382 SpiceGetNode(prefix, suffix)
1383 HierName *prefix;
1384 HierName *suffix;
1385 {
1386 HashEntry *he;
1387 EFNodeName *nn;
1388
1389 he = EFHNConcatLook(prefix, suffix, "output");
1390 if (he == NULL) return NULL;
1391 nn = (EFNodeName *) HashGetValue(he);
1392 if (nn == NULL) return NULL;
1393 return(nn->efnn_node);
1394 }
1395
1396 /*
1397 * ----------------------------------------------------------------------------
1398 *
1399 * extHierSDAttr --
1400 * Check if the attribute of the argument dev_terminal or the global
1401 * settings are such that we want a hierarchical extraction of its S/D
1402 *
1403 *
1404 * Results:
1405 * TRUE or FALSE
1406 *
1407 * Side effects:
1408 * None.
1409 *
1410 * ----------------------------------------------------------------------------
1411 */
1412
extHierSDAttr(term)1413 bool extHierSDAttr(term)
1414 DevTerm *term;
1415 {
1416 bool r = esHierAP;
1417
1418 if (term->dterm_attrs)
1419 {
1420 if (Match(ATTR_HIERAP, term->dterm_attrs) != FALSE)
1421 r = TRUE;
1422 else if (Match(ATTR_FLATAP, term->dterm_attrs) != FALSE)
1423 r = FALSE;
1424 }
1425 return r;
1426 }
1427
1428 /*
1429 * ----------------------------------------------------------------------------
1430 *
1431 * subcktVisit --
1432 *
1433 * Procedure to output a subcircuit definition to the .spice file.
1434 * Called by EFVisitSubcircuits().
1435 *
1436 * Results:
1437 * Returns 0 always.
1438 *
1439 * Side effects:
1440 * Writes to the file esSpiceF.
1441 *
1442 * Format of a .spice subcircuit call:
1443 *
1444 * X%d node1 node2 ... noden name
1445 *
1446 * where
1447 * node1 node2 ... noden are the nodes connecting to the ports of
1448 * the subcircuit. "name" is the name of the subcircuit. It is
1449 * assumed that the definition of "name" (.defs name ... .ends)
1450 * exists elsewhere and will be appended to the SPICE deck prior
1451 * to simulation (as is also assumed for device models).
1452 *
1453 * ----------------------------------------------------------------------------
1454 */
1455 int
subcktVisit(use,hierName,is_top)1456 subcktVisit(use, hierName, is_top)
1457 Use *use;
1458 HierName *hierName;
1459 bool is_top; /* TRUE if this is the top-level cell */
1460 {
1461 EFNode *snode;
1462 Def *def = use->use_def;
1463 EFNodeName *sname, *nodeName;
1464 HashSearch hs;
1465 HashEntry *he;
1466 int portorder, portmax, portidx, imp_max, tchars;
1467 char stmp[MAX_STR_SIZE];
1468 char *instname, *subcktname;
1469 DevParam *plist, *pptr;
1470 EFNodeName **nodeList;
1471
1472 if (is_top == TRUE) return 0; /* Ignore the top-level cell */
1473
1474 /* Retain instance name unless esDoRenumber is set, or format is Spice2 */
1475 if (use->use_id == NULL || esDoRenumber == TRUE || esFormat == SPICE2)
1476 {
1477 /* NOTE: This really needs to update subcktNameTable so that */
1478 /* it tracks between instance names and node names, when using */
1479 /* HSPICE format + esDoRenumber. */
1480
1481 fprintf(esSpiceF, "X%d", esSbckNum++);
1482 tchars = 5;
1483 }
1484 else
1485 {
1486 int savflags = EFOutputFlags;
1487 EFOutputFlags &= ~EF_TRIM_MASK;
1488 EFOutputFlags |= EF_CONVERTCOMMA; // Only substitute commas on subcircuit names
1489
1490 /* Use full hierarchical decomposition for name */
1491 /* (not just use->use_id. hierName already has use->use_id at end) */
1492 EFHNSprintf(stmp, hierName);
1493 fprintf(esSpiceF, "X%s", stmp);
1494 EFOutputFlags = savflags;
1495 tchars = 1 + strlen(stmp);
1496 }
1497
1498 /* This is not a DEV, but "spcdevOutNode" is a general-purpose routine that */
1499 /* turns a local name in the use's def to a hierarchical name in the */
1500 /* calling def. */
1501
1502 /* Note that the ports of the subcircuit will not necessarily be */
1503 /* ALL the entries in the hash table, so we have to check. */
1504
1505 portmax = EFGetPortMax(def, &imp_max);
1506
1507 if (portmax < 0)
1508 {
1509 /* No port order declared; print them as we see them. */
1510 /* This shouldn't happen for proper .ext files written */
1511 /* by the magic extractor, since explicit port order is */
1512 /* generated during topVisit(). */
1513
1514 HashStartSearch(&hs);
1515 while (he = HashNext(&def->def_nodes, &hs))
1516 {
1517 sname = (EFNodeName *) HashGetValue(he);
1518 if (sname == NULL) continue;
1519 snode = sname->efnn_node;
1520
1521 if (snode && (snode->efnode_flags & EF_PORT))
1522 for (nodeName = sname; nodeName != NULL; nodeName = nodeName->efnn_next)
1523 if (nodeName->efnn_port >= 0)
1524 {
1525 portmax++;
1526 if (tchars > 80)
1527 {
1528 fprintf(esSpiceF, "\n+");
1529 tchars = 1;
1530 }
1531 tchars += spcdevOutNode(hierName, nodeName->efnn_hier,
1532 "subcircuit", esSpiceF);
1533 }
1534 }
1535
1536 /* Look for all implicit substrate connections that are */
1537 /* declared as local node names, and put them last. */
1538
1539 HashStartSearch(&hs);
1540 while (he = HashNext(&def->def_nodes, &hs))
1541 {
1542 sname = (EFNodeName *) HashGetValue(he);
1543 if (sname == NULL) continue;
1544 snode = sname->efnn_node;
1545
1546 if (snode && (snode->efnode_flags & EF_SUBS_PORT))
1547 {
1548 nodeName = snode->efnode_name;
1549 if (nodeName->efnn_port < 0)
1550 nodeName->efnn_port = ++portmax;
1551
1552 /* This is not a hierarchical name or node! */
1553 EFHNSprintf(stmp, nodeName->efnn_hier);
1554 if (tchars > 80)
1555 {
1556 fprintf(esSpiceF, "\n+");
1557 tchars = 1;
1558 }
1559 fprintf(esSpiceF, " %s", stmp);
1560 tchars += (1 + strlen(stmp));
1561 }
1562 }
1563 }
1564 else
1565 {
1566 /* Port numbers need not start at zero or be contiguous. */
1567 /* They will be printed in numerical order. */
1568
1569 nodeList = (EFNodeName **)mallocMagic((portmax + 1) * sizeof(EFNodeName *));
1570 for (portidx = 0; portidx <= portmax; portidx++)
1571 nodeList[portidx] = (EFNodeName *)NULL;
1572
1573 HashStartSearch(&hs);
1574 while (he = HashNext(&def->def_nodes, &hs))
1575 {
1576 sname = (EFNodeName *) HashGetValue(he);
1577 if (sname == NULL) continue;
1578 snode = sname->efnn_node;
1579
1580 if ((snode == NULL) || !(snode->efnode_flags & EF_PORT)) continue;
1581
1582 for (nodeName = sname; nodeName != NULL; nodeName = nodeName->efnn_next)
1583 {
1584 EFNodeName *nn;
1585 HashEntry *he;
1586 char *pname;
1587
1588 portidx = nodeName->efnn_port;
1589 if (portidx < 0) continue;
1590 if (nodeList[portidx] == NULL)
1591 {
1592 nodeList[portidx] = nodeName;
1593 }
1594 else if (EFHNBest(nodeName->efnn_hier, nodeList[portidx]->efnn_hier))
1595 {
1596 nodeList[portidx] = nodeName;
1597 }
1598 }
1599 }
1600
1601 for (portidx = 0; portidx <= portmax; portidx++)
1602 {
1603 nodeName = nodeList[portidx];
1604
1605 if (nodeName != NULL)
1606 {
1607 if (tchars > 80)
1608 {
1609 fprintf(esSpiceF, "\n+");
1610 tchars = 1;
1611 }
1612 tchars += spcdevOutNode(hierName, nodeName->efnn_hier,
1613 "subcircuit", esSpiceF);
1614 }
1615 else
1616 {
1617 // As port indexes do not have to be contiguous, this does not
1618 // necessarily indicate an error condition. No need to report?
1619 // TxError("No port connection on port %d; need to resolve.\n", portidx);
1620 }
1621 }
1622 freeMagic(nodeList);
1623
1624 /* Look for all implicit substrate connections that are */
1625 /* declared as local node names, and put them last. */
1626
1627 portorder = portmax + 1;
1628 while (portorder <= imp_max)
1629 {
1630 HashStartSearch(&hs);
1631 while (he = HashNext(&def->def_nodes, &hs))
1632 {
1633 sname = (EFNodeName *) HashGetValue(he);
1634 if (sname == NULL) continue;
1635 snode = sname->efnn_node;
1636
1637 if ((snode == NULL) || !(snode->efnode_flags & EF_SUBS_PORT)) continue;
1638 nodeName = snode->efnode_name;
1639 if (nodeName->efnn_port == portorder)
1640 {
1641 /* This is not a hierarchical name or node! */
1642 EFHNSprintf(stmp, nodeName->efnn_hier);
1643 if (tchars > 80)
1644 {
1645 fprintf(esSpiceF, "\n+");
1646 tchars = 1;
1647 }
1648 fprintf(esSpiceF, " %s", stmp);
1649 tchars += (1 + strlen(stmp));
1650 }
1651 }
1652 portorder++;
1653 }
1654 }
1655
1656 /* SPICE subcircuit names must begin with A-Z. */
1657 subcktname = def->def_name;
1658 if (!isalpha(*subcktname))
1659 {
1660 subcktname = mallocMagic(2 + strlen(def->def_name));
1661 sprintf(subcktname, "x%s", def->def_name);
1662 freeMagic(def->def_name);
1663 def->def_name = subcktname;
1664 }
1665
1666 if (tchars > 80) fprintf(esSpiceF, "\n+");
1667 fprintf(esSpiceF, " %s", subcktname); /* subcircuit model name */
1668
1669 // Check for a "device parameter" defined with the name of the cell.
1670 // This contains a list of parameter strings to be passed to the
1671 // cell instance.
1672
1673 instname = mallocMagic(2 + strlen(def->def_name));
1674 sprintf(instname, ":%s", def->def_name);
1675 plist = efGetDeviceParams(instname);
1676 for (pptr = plist; pptr; pptr = pptr->parm_next)
1677 {
1678 if (tchars > 80)
1679 {
1680 fprintf(esSpiceF, "\n+");
1681 tchars = 1;
1682 }
1683 fprintf(esSpiceF, " %s", pptr->parm_name);
1684 tchars += (1 + strlen(pptr->parm_name));
1685 }
1686 freeMagic(instname);
1687 fprintf(esSpiceF, "\n");
1688 return 0;
1689 }
1690
1691 /*
1692 * ----------------------------------------------------------------------------
1693 *
1694 * subcktUndef --
1695 *
1696 * Procedure to remove the DEF_SUBCIRCUIT flag from all subcells.
1697 * Called by EFVisitSubcircuits().
1698 *
1699 * Results:
1700 * Returns 0 always.
1701 *
1702 * Side effects:
1703 * Undefines the DEF_SUBCIRCUIT flag in each encountered Def.
1704 *
1705 * ----------------------------------------------------------------------------
1706 */
1707 int
subcktUndef(use,hierName,is_top)1708 subcktUndef(use, hierName, is_top)
1709 Use *use;
1710 HierName *hierName;
1711 bool is_top; /* TRUE if this is the top-level cell */
1712 {
1713 Def *def = use->use_def;
1714
1715 def->def_flags &= ~(DEF_SUBCIRCUIT);
1716 return 0;
1717 }
1718
1719 /* Define a linked node name list */
1720
1721 typedef struct _lnn {
1722 EFNodeName *lnn_nodeName;
1723 struct _lnn *lnn_next;
1724 } linkedNodeName;
1725
1726 /*
1727 * ----------------------------------------------------------------------------
1728 *
1729 * topVisit --
1730 *
1731 * Procedure to output a subcircuit definition to the .spice file.
1732 *
1733 * Results:
1734 * None.
1735 *
1736 * Side effects:
1737 * Writes to the file esSpiceF.
1738 *
1739 * Format of a .spice subcircuit definition:
1740 *
1741 * .subckt name node1 node2 ... noden
1742 *
1743 * where
1744 * node1 node2 ... noden are the nodes connecting to the ports of
1745 * the subcircuit. "name" is the name of the cell def. If "doStub"
1746 * is TRUE, then the subcircuit is a stub (empty declaration) for a
1747 * subcircuit, and implicit substrate connections should not be
1748 * output.
1749 *
1750 * NOTE: The cookie-cutter method for extraction can result in multiple
1751 * connections to the same port if the net spans multiple extraction regions.
1752 * Because of this, it is necessary to make sure that the same port name is
1753 * not output twice.
1754 *
1755 * ----------------------------------------------------------------------------
1756 */
1757
1758 void
topVisit(def,doStub)1759 topVisit(def, doStub)
1760 Def *def;
1761 bool doStub;
1762 {
1763 EFNode *snode, *basenode;
1764 EFNodeName *sname, *nodeName;
1765 HashSearch hs;
1766 HashEntry *he, *hep;
1767 HashTable portNameTable;
1768 int portorder, portmax, tchars;
1769 bool explicit;
1770 DevParam *plist, *pptr;
1771 char *instname;
1772 char *subcktname;
1773 char *pname;
1774 char **sorted_ports;
1775 linkedNodeName *lnn = NULL;
1776
1777 HashInit(&portNameTable, 32, HT_STRINGKEYS);
1778
1779 /* SPICE subcircuit names must begin with A-Z. This will also be */
1780 /* enforced when writing X subcircuit calls. */
1781 subcktname = def->def_name;
1782 if (!isalpha(*subcktname))
1783 {
1784 subcktname = mallocMagic(2 + strlen(def->def_name));
1785 sprintf(subcktname, "x%s", def->def_name);
1786 freeMagic(def->def_name);
1787 def->def_name = subcktname;
1788 }
1789
1790 fprintf(esSpiceF, ".subckt %s", subcktname);
1791 tchars = 8 + strlen(subcktname);
1792
1793 /* Note that the ports of the subcircuit will not necessarily be */
1794 /* ALL the entries in the hash table, so we have to check. */
1795
1796 HashStartSearch(&hs);
1797 portmax = -1;
1798
1799 while (he = HashNext(&def->def_nodes, &hs))
1800 {
1801 sname = (EFNodeName *) HashGetValue(he);
1802 if (sname == NULL) continue;
1803 snode = sname->efnn_node;
1804 if ((!snode) || (!(snode->efnode_flags & EF_PORT))) continue;
1805 explicit = FALSE;
1806 for (nodeName = sname; nodeName != NULL; nodeName = nodeName->efnn_next)
1807 {
1808 portorder = nodeName->efnn_port;
1809 if (portorder > portmax) portmax = portorder;
1810 if (portorder != -1) explicit = TRUE;
1811 }
1812 if (explicit == FALSE)
1813 {
1814 /* Tag this an an implicit port (port without an assigned index) */
1815 linkedNodeName *newlnn =
1816 (linkedNodeName *)mallocMagic(sizeof(linkedNodeName));
1817 newlnn->lnn_next = lnn;
1818 newlnn->lnn_nodeName = sname;
1819 lnn = newlnn;
1820 }
1821 }
1822
1823 /* Make all port numbers explicit (unless this is a black-box */
1824 /* circuit "ext2spice blackbox on" is in effect). */
1825
1826 while (lnn != NULL)
1827 {
1828 sname = lnn->lnn_nodeName;
1829 if (esDoBlackBox == FALSE || !(def->def_flags & DEF_ABSTRACT))
1830 sname->efnn_port = ++portmax;
1831 freeMagic(lnn);
1832 lnn = lnn->lnn_next;
1833 }
1834
1835 /* Port numbers need not start at zero or be contiguous. They will be */
1836 /* printed in numerical order. This is done by allocating space for */
1837 /* the output first and generating text into the allocated array */
1838 /* indexed by port, to avoid multiple scans through the hash table. */
1839
1840 sorted_ports = (char **)mallocMagic((portmax + 1) * sizeof(char *));
1841 for (portorder = 0; portorder <= portmax; portorder++) sorted_ports[portorder] = NULL;
1842
1843 HashStartSearch(&hs);
1844 while (he = HashNext(&def->def_nodes, &hs))
1845 {
1846 char stmp[MAX_STR_SIZE];
1847 int portidx;
1848 EFNodeName *unnumbered;
1849
1850 sname = (EFNodeName *) HashGetValue(he);
1851 if (sname == NULL) continue; /* Should not happen */
1852 snode = sname->efnn_node;
1853
1854 if ((!snode) || (!(snode->efnode_flags & EF_PORT))) continue;
1855
1856 for (nodeName = sname; nodeName != NULL; nodeName = nodeName->efnn_next)
1857 {
1858 portidx = nodeName->efnn_port;
1859 if (portidx < 0) continue;
1860
1861 /* If view is abstract, rely on the given port name, not
1862 * the node. Otherwise, artifacts of the abstract view
1863 * may cause nodes to be merged and the names lost.
1864 */
1865
1866 if (def->def_flags & DEF_ABSTRACT)
1867 {
1868 EFHNSprintf(stmp, nodeName->efnn_hier);
1869 pname = stmp;
1870 }
1871 else
1872 pname = nodeSpiceName(snode->efnode_name->efnn_hier, NULL);
1873
1874 hep = HashLookOnly(&portNameTable, pname);
1875 if (hep == (HashEntry *)NULL)
1876 {
1877 hep = HashFind(&portNameTable, pname);
1878 HashSetValue(hep, (ClientData)(pointertype)nodeName->efnn_port);
1879 if (sorted_ports[portidx] == NULL)
1880 sorted_ports[portidx] = StrDup((char **)NULL, pname);
1881 }
1882 else
1883 {
1884 // Node that was unassigned has been found to be
1885 // a repeat (see NOTE at top), so make sure its
1886 // port number is set correctly.
1887
1888 nodeName->efnn_port = (int)(pointertype)HashGetValue(hep);
1889 }
1890 }
1891 }
1892 HashKill(&portNameTable);
1893
1894 /* Output all ports, in order */
1895
1896 for (portorder = 0; portorder <= portmax; portorder++)
1897 {
1898 if (sorted_ports[portorder] != NULL)
1899 {
1900 if (tchars > 80)
1901 {
1902 /* Line continuation */
1903 fprintf(esSpiceF, "\n+");
1904 tchars = 1;
1905 }
1906 fprintf(esSpiceF, " %s", sorted_ports[portorder]);
1907 tchars += strlen(sorted_ports[portorder]) + 1;
1908
1909 freeMagic(sorted_ports[portorder]);
1910 }
1911 }
1912 freeMagic(sorted_ports);
1913
1914 /* Add all implicitly-defined local substrate node names */
1915
1916 if (!doStub)
1917 {
1918 HashStartSearch(&hs);
1919 while (he = HashNext(&def->def_nodes, &hs))
1920 {
1921 sname = (EFNodeName *) HashGetValue(he);
1922 if (sname == NULL) continue;
1923 snode = sname->efnn_node;
1924
1925 if (snode && (snode->efnode_flags & EF_SUBS_PORT) &&
1926 !(snode->efnode_flags & EF_PORT))
1927 {
1928 if (snode->efnode_name->efnn_port < 0)
1929 {
1930 char stmp[MAX_STR_SIZE];
1931
1932 if (tchars > 80)
1933 {
1934 /* Line continuation */
1935 fprintf(esSpiceF, "\n+");
1936 tchars = 1;
1937 }
1938 /* This is not a hierarchical name or node! */
1939 EFHNSprintf(stmp, snode->efnode_name->efnn_hier);
1940 fprintf(esSpiceF, " %s", stmp);
1941 snode->efnode_name->efnn_port = portorder++;
1942 tchars += strlen(stmp) + 1;
1943 }
1944 }
1945 }
1946 }
1947
1948 // Add any parameters defined by "property parameter" in the cell
1949
1950 instname = mallocMagic(2 + strlen(def->def_name));
1951 sprintf(instname, ":%s", def->def_name);
1952 plist = efGetDeviceParams(instname);
1953 for (pptr = plist; pptr; pptr = pptr->parm_next)
1954 {
1955 if (tchars > 80)
1956 {
1957 /* Line continuation */
1958 fprintf(esSpiceF, "\n+");
1959 tchars = 1;
1960 }
1961 pname = pptr->parm_name;
1962 fprintf(esSpiceF, " %s", pname);
1963 tchars += strlen(pname) + 1;
1964 }
1965 freeMagic(instname);
1966
1967 fprintf(esSpiceF, "\n");
1968 }
1969
1970 /*
1971 * ----------------------------------------------------------------------------
1972 *
1973 * spcWriteParams ---
1974 *
1975 * Write parameters to a device line in SPICE output. This is normally
1976 * restricted to subcircuit devices but may include other devices to
1977 * accomodate various extensions to the basic SPICE format.
1978 *
1979 * ----------------------------------------------------------------------------
1980 */
1981
1982 void
spcWriteParams(dev,hierName,scale,l,w,sdM)1983 spcWriteParams(dev, hierName, scale, l, w, sdM)
1984 Dev *dev; /* Dev being output */
1985 HierName *hierName; /* Hierarchical path down to this dev */
1986 float scale; /* Scale transform for output */
1987 int l; /* Device length, in internal units */
1988 int w; /* Device width, in internal units */
1989 float sdM; /* Device multiplier */
1990 {
1991 bool hierD;
1992 DevParam *plist;
1993 int parmval;
1994 EFNode *dnode, *subnodeFlat = NULL;
1995
1996 bool extHierSDAttr();
1997
1998 plist = efGetDeviceParams(EFDevTypes[dev->dev_type]);
1999 while (plist != NULL)
2000 {
2001 switch (plist->parm_type[0])
2002 {
2003 case 'a':
2004 // Check for area of terminal node vs. device area
2005 if (plist->parm_type[1] == '\0' || plist->parm_type[1] == '0')
2006 {
2007 fprintf(esSpiceF, " %s=", plist->parm_name);
2008 parmval = dev->dev_area;
2009 if (esScale < 0)
2010 fprintf(esSpiceF, "%g", parmval * scale * scale);
2011 else if (plist->parm_scale != 1.0)
2012 fprintf(esSpiceF, "%g", parmval * scale * scale
2013 * esScale * esScale * plist->parm_scale
2014 * 1E-12);
2015 else
2016 fprintf(esSpiceF, "%gp", parmval * scale * scale
2017 * esScale * esScale);
2018 }
2019 else
2020 {
2021 int pn, resclass;
2022
2023 pn = plist->parm_type[1] - '0';
2024 if (pn >= dev->dev_nterm) pn = dev->dev_nterm - 1;
2025
2026 hierD = extHierSDAttr(&dev->dev_terms[pn]);
2027
2028 resclass = (pn > 1) ? esFetInfo[dev->dev_type].resClassDrain :
2029 esFetInfo[dev->dev_type].resClassSource;
2030
2031 // For parameter a<n> followed by parameter p<n>,
2032 // process both at the same time.
2033
2034 if (plist->parm_next && plist->parm_next->parm_type[0]
2035 == 'p' && plist->parm_next->parm_type[1]
2036 == plist->parm_type[1])
2037 {
2038 if (hierD)
2039 spcnAPHier(&dev->dev_terms[pn], hierName,
2040 resclass, scale, plist->parm_type,
2041 plist->parm_next->parm_type,
2042 sdM, esSpiceF);
2043 else
2044 {
2045 dnode = SpiceGetNode(hierName,
2046 dev->dev_terms[pn].dterm_node->efnode_name->efnn_hier);
2047 spcnAP(dnode, resclass, scale, plist->parm_name,
2048 plist->parm_next->parm_name,
2049 sdM, esSpiceF, w);
2050 }
2051 plist = plist->parm_next;
2052 }
2053 else
2054 {
2055 if (hierD)
2056 spcnAPHier(&dev->dev_terms[pn], hierName,
2057 resclass, scale, plist->parm_type, NULL,
2058 sdM, esSpiceF);
2059 else
2060 {
2061 dnode = SpiceGetNode(hierName,
2062 dev->dev_terms[pn].dterm_node->efnode_name->efnn_hier);
2063 spcnAP(dnode, resclass, scale, plist->parm_name, NULL,
2064 sdM, esSpiceF, w);
2065 }
2066 }
2067 }
2068
2069 break;
2070 case 'p':
2071 // Check for area of terminal node vs. device area
2072 if (plist->parm_type[1] == '\0' || plist->parm_type[1] == '0')
2073 {
2074 fprintf(esSpiceF, " %s=", plist->parm_name);
2075 parmval = dev->dev_perim;
2076 if (esScale < 0)
2077 fprintf(esSpiceF, "%g", parmval * scale);
2078 else if (plist->parm_scale != 1.0)
2079 fprintf(esSpiceF, "%g", parmval * scale
2080 * esScale * plist->parm_scale * 1E-6);
2081 else
2082 fprintf(esSpiceF, "%gu", parmval * scale * esScale);
2083 }
2084 else
2085 {
2086 int pn, resclass;
2087
2088 pn = plist->parm_type[1] - '0';
2089 if (pn >= dev->dev_nterm) pn = dev->dev_nterm - 1;
2090
2091 resclass = (pn > 1) ? esFetInfo[dev->dev_type].resClassDrain :
2092 esFetInfo[dev->dev_type].resClassSource;
2093
2094 hierD = extHierSDAttr(&dev->dev_terms[pn]);
2095
2096 // For parameter p<n> followed by parameter a<n>,
2097 // process both at the same time.
2098
2099 if (plist->parm_next && plist->parm_next->parm_type[0]
2100 == 'a' && plist->parm_next->parm_type[1]
2101 == plist->parm_type[1])
2102 {
2103 if (hierD)
2104 spcnAPHier(&dev->dev_terms[pn], hierName,
2105 resclass, scale, plist->parm_next->parm_type,
2106 plist->parm_type, sdM, esSpiceF);
2107 else
2108 {
2109 dnode = SpiceGetNode(hierName,
2110 dev->dev_terms[pn].dterm_node->efnode_name->efnn_hier);
2111 spcnAP(dnode, resclass, scale, plist->parm_next->parm_name,
2112 plist->parm_name, sdM, esSpiceF, w);
2113 }
2114 plist = plist->parm_next;
2115 }
2116 else
2117 {
2118 if (hierD)
2119 spcnAPHier(&dev->dev_terms[pn], hierName,
2120 resclass, scale, NULL, plist->parm_type,
2121 sdM, esSpiceF);
2122 else
2123 {
2124 dnode = SpiceGetNode(hierName,
2125 dev->dev_terms[pn].dterm_node->efnode_name->efnn_hier);
2126 spcnAP(dnode, resclass, scale, NULL, plist->parm_name,
2127 sdM, esSpiceF, w);
2128 }
2129 }
2130 }
2131 break;
2132
2133 case 'l':
2134 fprintf(esSpiceF, " %s=", plist->parm_name);
2135 if (esScale < 0)
2136 fprintf(esSpiceF, "%g", l * scale);
2137 else if (plist->parm_scale != 1.0)
2138 fprintf(esSpiceF, "%g", l * scale * esScale
2139 * plist->parm_scale * 1E-6);
2140 else
2141 fprintf(esSpiceF, "%gu", l * scale * esScale);
2142 break;
2143 case 'w':
2144 fprintf(esSpiceF, " %s=", plist->parm_name);
2145 if (esScale < 0)
2146 fprintf(esSpiceF, "%g", w * scale);
2147 else if (plist->parm_scale != 1.0)
2148 fprintf(esSpiceF, "%g", w * scale * esScale
2149 * plist->parm_scale * 1E-6);
2150 else
2151 fprintf(esSpiceF, "%gu", w * scale * esScale);
2152 break;
2153 case 's':
2154 fprintf(esSpiceF, " %s=", plist->parm_name);
2155 subnodeFlat = spcdevSubstrate(hierName,
2156 dev->dev_subsnode->efnode_name->efnn_hier,
2157 dev->dev_type, esSpiceF);
2158 break;
2159 case 'x':
2160 fprintf(esSpiceF, " %s=", plist->parm_name);
2161 if (esScale < 0)
2162 fprintf(esSpiceF, "%g", dev->dev_rect.r_xbot * scale);
2163 else if (plist->parm_scale != 1.0)
2164 fprintf(esSpiceF, "%g", dev->dev_rect.r_xbot * scale
2165 * esScale * plist->parm_scale * 1E-6);
2166 else
2167 fprintf(esSpiceF, "%gu", dev->dev_rect.r_xbot * scale
2168 * esScale);
2169 break;
2170 case 'y':
2171 fprintf(esSpiceF, " %s=", plist->parm_name);
2172 if (esScale < 0)
2173 fprintf(esSpiceF, "%g", dev->dev_rect.r_ybot * scale);
2174 else if (plist->parm_scale != 1.0)
2175 fprintf(esSpiceF, "%g", dev->dev_rect.r_ybot * scale
2176 * esScale * plist->parm_scale * 1E-6);
2177 else
2178 fprintf(esSpiceF, "%gu", dev->dev_rect.r_ybot * scale
2179 * esScale);
2180 break;
2181 case 'r':
2182 fprintf(esSpiceF, " %s=", plist->parm_name);
2183 fprintf(esSpiceF, "%f", (double)(dev->dev_res));
2184 break;
2185 case 'c':
2186 fprintf(esSpiceF, " %s=", plist->parm_name);
2187 fprintf(esSpiceF, "%ff", (double)(dev->dev_cap));
2188 break;
2189 }
2190 plist = plist->parm_next;
2191 }
2192
2193 /* Add parameters that are to be copied verbatim */
2194 for (plist = dev->dev_params; plist; plist = plist->parm_next)
2195 fprintf(esSpiceF, " %s", plist->parm_name);
2196 }
2197
2198 /*
2199 * ----------------------------------------------------------------------------
2200 *
2201 * esOutputResistor ---
2202 *
2203 * Routine used by spcdevVisit to print a resistor device. This
2204 * is broken out into a separate routine so that each resistor
2205 * device may be represented (if the option is selected) by a
2206 * "tee" network of two resistors on either side of the central
2207 * node, which then has a capacitance to ground.
2208 *
2209 * Results:
2210 * None.
2211 *
2212 * Side effects:
2213 * Output to the SPICE deck.
2214 *
2215 * ----------------------------------------------------------------------------
2216 */
2217
2218 void
esOutputResistor(dev,hierName,scale,term1,term2,has_model,l,w,dscale)2219 esOutputResistor(dev, hierName, scale, term1, term2, has_model, l, w, dscale)
2220 Dev *dev; /* Dev being output */
2221 HierName *hierName; /* Hierarchical path down to this dev */
2222 float scale; /* Scale transform for output */
2223 DevTerm *term1, *term2; /* Terminals of the device */
2224 bool has_model; /* Is this a modeled resistor? */
2225 int l, w; /* Device length and width */
2226 int dscale; /* Device scaling (for split resistors) */
2227 {
2228 float sdM ;
2229 char name[12], devchar;
2230
2231 /* Resistor is "Rnnn term1 term2 value" */
2232 /* extraction sets two terminals, which are assigned */
2233 /* term1=gate term2=source by the above code. */
2234 /* extracted units are Ohms; output is in Ohms */
2235
2236 spcdevOutNode(hierName, term1->dterm_node->efnode_name->efnn_hier,
2237 name, esSpiceF);
2238 spcdevOutNode(hierName, term2->dterm_node->efnode_name->efnn_hier,
2239 name, esSpiceF);
2240
2241 sdM = getCurDevMult();
2242
2243 /* SPICE has two resistor types. If the "name" (EFDevTypes) is */
2244 /* "None", the simple resistor type is used, and a value given. */
2245 /* If not, the "semiconductor resistor" is used, and L and W */
2246 /* and the device name are output. */
2247
2248 if (!has_model)
2249 {
2250 fprintf(esSpiceF, " %f", ((double)(dev->dev_res)
2251 / (double)(dscale)) / (double)sdM);
2252 spcWriteParams(dev, hierName, scale, l, w, sdM);
2253 }
2254 else
2255 {
2256 fprintf(esSpiceF, " %s", EFDevTypes[dev->dev_type]);
2257
2258 if (esScale < 0)
2259 fprintf(esSpiceF, " w=%g l=%g", w * scale, (l * scale) / dscale);
2260 else
2261 fprintf(esSpiceF, " w=%gu l=%gu",
2262 w * scale * esScale,
2263 ((l * scale * esScale) / dscale));
2264
2265 spcWriteParams(dev, hierName, scale, l, w, sdM);
2266 if (sdM != 1.0)
2267 fprintf(esSpiceF, " M=%g", sdM);
2268 }
2269 }
2270
2271 /* Report if device at index n has been deleted due to merging */
2272
2273 bool
devIsKilled(n)2274 devIsKilled(n)
2275 int n;
2276 {
2277 return (esFMult[(n)] <= (float)0.0) ? TRUE : FALSE;
2278 }
2279
2280 /* Add a dev's multiplier to the table and grow it if necessary */
2281
2282 void
addDevMult(f)2283 addDevMult(f)
2284 float f;
2285 {
2286 int i;
2287 float *op;
2288
2289 if (esFMult == NULL) {
2290 esFMult = (float *) mallocMagic((unsigned) (esFMSize*sizeof(float)));
2291 }
2292 else if (esFMIndex >= esFMSize)
2293 {
2294 op = esFMult;
2295 esFMSize *= 2;
2296 esFMult = (float *)mallocMagic((unsigned)(esFMSize * sizeof(float)));
2297 for (i = 0; i < esFMSize / 2; i++) esFMult[i] = op[i];
2298 if (op) freeMagic(op);
2299 }
2300 esFMult[esFMIndex++] = f;
2301 }
2302
2303 /* Set the multiplier value f of device at index i */
2304
2305 void
setDevMult(i,f)2306 setDevMult(i, f)
2307 int i;
2308 float f;
2309 {
2310 esFMult[i] = f;
2311 }
2312
2313 /* Get the multiplier value of the device at the current index esFMIndex */
2314
2315 float
getCurDevMult()2316 getCurDevMult()
2317 {
2318 return (esFMult && (esFMIndex > 0)) ? esFMult[esFMIndex-1] : (float)1.0;
2319 }
2320
2321
2322 /*
2323 *-----------------------------------------------------------------------------
2324 * swapDrainSource
2325 *
2326 * Swap drain and source ordering and the related stuff
2327 * including the drain/source area parameters
2328 *
2329 * This is typically called if any terminal is marked with attribute "D" or "S"
2330 * (label "D$" or "S$" at poly-diffusion interface),
2331 * then swap order of source and drain compared to the default ordering.
2332 *
2333 * Note:
2334 * Before calling this function, ensure that dev->dev_nterm >= 3
2335 *
2336 * Results:
2337 * None
2338 *
2339 * Side effects:
2340 * Soure (dev->dev_terms[1]) and drain (dev->dev_terms[2]) terminals
2341 * are swapped.
2342 *
2343 *-----------------------------------------------------------------------------
2344 */
2345
2346 void
swapDrainSource(dev)2347 swapDrainSource(dev)
2348 Dev *dev;
2349 {
2350 DevTerm tmpTerm;
2351
2352 /* swap original terminals */
2353 memcpy(&tmpTerm, &(dev->dev_terms[1]), sizeof(DevTerm));
2354 memcpy(&(dev->dev_terms[1]), &(dev->dev_terms[2]), sizeof(DevTerm));
2355 memcpy(&(dev->dev_terms[2]), &tmpTerm, sizeof(DevTerm));
2356 }
2357
2358
2359 /*
2360 * ----------------------------------------------------------------------------
2361 *
2362 * spcdevVisit --
2363 *
2364 * Procedure to output a single dev to the .spice file.
2365 * Called by EFVisitDevs().
2366 *
2367 * Results:
2368 * Returns 0 always.
2369 *
2370 * Side effects:
2371 * Writes to the file esSpiceF.
2372 *
2373 * Format of a .spice dev line:
2374 *
2375 * M%d drain gate source substrate type w=w l=l * x y
2376 * + ad= pd= as= ps= * asub= psub=
2377 * **devattr g= s= d=
2378 *
2379 * where
2380 * type is a name identifying this type of transistor
2381 * other types of transistors are extracted with
2382 * an M card but it should be easy to turn them to whatever
2383 * you want.
2384 * gate, source, and drain are the nodes to which these three
2385 * terminals connect
2386 * l, w are the length and width of the channel
2387 * x, y are the x, y coordinates of a point within the channel.
2388 * g=, s=, d= are the (optional) attributes; if present, each
2389 * is followed by a comma-separated list of attributes.
2390 *
2391 * ----------------------------------------------------------------------------
2392 */
2393
2394 int
spcdevVisit(dev,hc,scale,trans)2395 spcdevVisit(dev, hc, scale, trans)
2396 Dev *dev; /* Dev being output */
2397 HierContext *hc; /* Hierarchical context down to this dev */
2398 float scale; /* Scale transform for output */
2399 Transform *trans; /* (unused) */
2400 {
2401 DevParam *plist, *pptr;
2402 DevTerm *gate, *source, *drain;
2403 EFNode *subnode, *snode, *dnode, *subnodeFlat = NULL;
2404 int l, w, i, parmval;
2405 bool subAP= FALSE, hierS, hierD, extHierSDAttr();
2406 float sdM;
2407 char name[12], devchar;
2408 bool has_model = TRUE;
2409 HierName *hierName = hc->hc_hierName;
2410
2411 sprintf(name, "output");
2412
2413 /* If no terminals, can't do much of anything */
2414 if (dev->dev_nterm < 1 )
2415 return 0;
2416
2417 if ( (esMergeDevsA || esMergeDevsC) && devIsKilled(esFMIndex++) )
2418 return 0;
2419
2420 /* Get L and W of device */
2421 EFGetLengthAndWidth(dev, &l, &w);
2422
2423 /* If only two terminals, connect the source to the drain */
2424 gate = &dev->dev_terms[0];
2425 if (dev->dev_nterm >= 2)
2426 source = drain = &dev->dev_terms[1];
2427
2428 if (dev->dev_nterm >= 3)
2429 {
2430 drain = &dev->dev_terms[2];
2431
2432 /* If any terminal is marked with attribute "D" or "S" */
2433 /* (label "D$" or "S$" at poly-diffusion interface), */
2434 /* then force order of source and drain accordingly. */
2435
2436 if ((dev->dev_terms[1].dterm_attrs &&
2437 !strcmp(dev->dev_terms[1].dterm_attrs, "D")) ||
2438 (dev->dev_terms[2].dterm_attrs &&
2439 !strcmp(dev->dev_terms[2].dterm_attrs, "S")))
2440 {
2441 swapDrainSource(dev);
2442 }
2443 }
2444 subnode = dev->dev_subsnode;
2445
2446 /* Check for minimum number of terminals. */
2447
2448 switch(dev->dev_class)
2449 {
2450 case DEV_SUBCKT:
2451 case DEV_RSUBCKT:
2452 case DEV_CSUBCKT:
2453 case DEV_MSUBCKT:
2454 break;
2455 case DEV_DIODE:
2456 case DEV_PDIODE:
2457 case DEV_NDIODE:
2458 if ((dev->dev_nterm < 2) && (subnode == NULL))
2459 {
2460 TxError("Diode has only one terminal\n");
2461 return 0;
2462 }
2463 break;
2464 default:
2465 if (dev->dev_nterm < 2)
2466 {
2467 TxError("Device other than subcircuit has only "
2468 "one terminal\n");
2469 return 0;
2470 }
2471 break;
2472 }
2473
2474 /* Original hack for BiCMOS, Tim 10/4/97, is deprecated. */
2475 /* Use of "device bjt" preferred to "fet" with model="npn". */
2476
2477 if (!strcmp(EFDevTypes[dev->dev_type], "npn")) dev->dev_class = DEV_BJT;
2478
2479 /* For resistor and capacitor classes, set a boolean to */
2480 /* denote whether the device has a model or not, so we */
2481 /* don't have to keep doing a string compare on EFDevTypes. */
2482
2483 switch(dev->dev_class)
2484 {
2485 case DEV_RES:
2486 case DEV_CAP:
2487 case DEV_CAPREV:
2488 if (dev->dev_nterm < 1)
2489 return 0;
2490 if ((dev->dev_type == esNoModelType) ||
2491 !strcmp(EFDevTypes[dev->dev_type], "None"))
2492 has_model = FALSE;
2493 break;
2494 }
2495
2496 /* Flag shorted devices---this should probably be an option */
2497 switch(dev->dev_class)
2498 {
2499 case DEV_MOSFET:
2500 case DEV_ASYMMETRIC:
2501 case DEV_FET:
2502 if (source == drain)
2503 {
2504 if (esFormat == NGSPICE) fprintf(esSpiceF, "$ ");
2505 fprintf(esSpiceF, "** SOURCE/DRAIN TIED\n");
2506 }
2507 break;
2508
2509 default:
2510 if (gate == source)
2511 {
2512 if (esFormat == NGSPICE) fprintf(esSpiceF, "$ ");
2513 fprintf(esSpiceF, "** SHORTED DEVICE\n");
2514 }
2515 break;
2516 }
2517
2518 /* Generate SPICE device name */
2519 switch(dev->dev_class)
2520 {
2521 case DEV_MOSFET:
2522 case DEV_ASYMMETRIC:
2523 case DEV_FET:
2524 devchar = 'M';
2525 break;
2526 case DEV_BJT:
2527 devchar = 'Q';
2528 break;
2529 case DEV_DIODE:
2530 case DEV_NDIODE:
2531 case DEV_PDIODE:
2532 devchar = 'D';
2533 break;
2534 case DEV_RES:
2535 devchar = 'R';
2536 break;
2537 case DEV_VOLT:
2538 devchar = 'V';
2539 break;
2540 case DEV_CAP:
2541 case DEV_CAPREV:
2542 devchar = 'C';
2543 break;
2544 case DEV_SUBCKT:
2545 case DEV_RSUBCKT:
2546 case DEV_CSUBCKT:
2547 case DEV_MSUBCKT:
2548 devchar = 'X';
2549 break;
2550 }
2551 fprintf(esSpiceF, "%c", devchar);
2552
2553 /* Device index is taken from gate attributes if attached; */
2554 /* otherwise, the device is numbered in sequence. */
2555
2556 if (gate->dterm_attrs)
2557 {
2558 /* Output the name found in the gate attributes */
2559 /* prefixed by the hierarchical name. */
2560 fprintf(esSpiceF, "%s%s", EFHNToStr(hierName), gate->dterm_attrs);
2561 }
2562 else
2563 {
2564 switch (dev->dev_class)
2565 {
2566 case DEV_RES:
2567 fprintf(esSpiceF, "%d", esResNum++);
2568 /* For resistor tee networks, use, e.g., */
2569 /* "R1A" and "R1B", for clarity */
2570 if (esDoResistorTee) fprintf(esSpiceF, "A");
2571 break;
2572 case DEV_DIODE:
2573 case DEV_NDIODE:
2574 case DEV_PDIODE:
2575 fprintf(esSpiceF, "%d", esDiodeNum++);
2576 break;
2577 case DEV_CAP:
2578 case DEV_CAPREV:
2579 fprintf(esSpiceF, "%d", esCapNum++);
2580 break;
2581 case DEV_VOLT:
2582 fprintf(esSpiceF, "%d", esVoltNum++);
2583 break;
2584 case DEV_SUBCKT:
2585 case DEV_RSUBCKT:
2586 case DEV_CSUBCKT:
2587 case DEV_MSUBCKT:
2588 fprintf(esSpiceF, "%d", esSbckNum++);
2589 break;
2590 default:
2591 fprintf(esSpiceF, "%d", esDevNum++);
2592 break;
2593 }
2594 }
2595 /* Order and number of nodes in the output depends on the device class */
2596
2597 switch (dev->dev_class)
2598 {
2599 case DEV_BJT:
2600
2601 /* BJT is "Qnnn collector emitter base model" */
2602 /* extraction sets collector=subnode, emitter=gate, base=drain */
2603
2604 sprintf(name, "fet");
2605 spcdevOutNode(hierName, subnode->efnode_name->efnn_hier, name, esSpiceF);
2606 spcdevOutNode(hierName, gate->dterm_node->efnode_name->efnn_hier,
2607 name, esSpiceF);
2608
2609 /* fix mixed up drain/source for bjts hace 2/2/99 */
2610 if (gate->dterm_node->efnode_name->efnn_hier ==
2611 source->dterm_node->efnode_name->efnn_hier)
2612 spcdevOutNode(hierName, drain->dterm_node->efnode_name->efnn_hier,
2613 name, esSpiceF);
2614 else
2615 spcdevOutNode(hierName, source->dterm_node->efnode_name->efnn_hier,
2616 name, esSpiceF);
2617
2618 fprintf(esSpiceF, " %s", EFDevTypes[dev->dev_type]);
2619 sdM = getCurDevMult();
2620 spcWriteParams(dev, hierName, scale, l, w, sdM);
2621 break;
2622
2623 case DEV_MSUBCKT:
2624
2625 /* MOS-like subcircuit is "Xnnn drain gate [source [sub]]" */
2626 /* to more conveniently handle cases where MOS devices are */
2627 /* modeled by subcircuits with the same pin ordering. */
2628
2629 spcdevOutNode(hierName, drain->dterm_node->efnode_name->efnn_hier,
2630 name, esSpiceF);
2631
2632 /* Drop through to below (no break statement) */
2633
2634 case DEV_SUBCKT:
2635 case DEV_CSUBCKT:
2636
2637 /* Subcircuit is "Xnnn gate [source [drain [sub]]]" */
2638 /* Subcircuit .subckt record must be ordered to match! */
2639
2640 spcdevOutNode(hierName, gate->dterm_node->efnode_name->efnn_hier,
2641 name, esSpiceF);
2642
2643 /* Drop through to below (no break statement) */
2644
2645 case DEV_RSUBCKT:
2646 /* RC-like subcircuits are exactly like other subcircuits */
2647 /* except that the "gate" node is treated as an identifier */
2648 /* only and is not output. */
2649
2650 if (dev->dev_class != DEV_MSUBCKT)
2651 {
2652 if (dev->dev_nterm > 1)
2653 spcdevOutNode(hierName, source->dterm_node->efnode_name->efnn_hier,
2654 name, esSpiceF);
2655 if (dev->dev_nterm > 2)
2656 spcdevOutNode(hierName, drain->dterm_node->efnode_name->efnn_hier,
2657 name, esSpiceF);
2658 }
2659 else /* class DEV_MSUBCKT */
2660 {
2661 if (dev->dev_nterm > 2)
2662 spcdevOutNode(hierName, source->dterm_node->efnode_name->efnn_hier,
2663 name, esSpiceF);
2664 }
2665
2666 /* The following only applies to DEV_SUBCKT*, which may define as */
2667 /* many terminal types as it wants. */
2668
2669 for (i = 4; i < dev->dev_nterm; i++)
2670 {
2671 drain = &dev->dev_terms[i - 1];
2672 spcdevOutNode(hierName, drain->dterm_node->efnode_name->efnn_hier,
2673 name, esSpiceF);
2674 }
2675
2676 /* Get the device parameters now, and check if the substrate is */
2677 /* passed as a parameter rather than as a node. */
2678
2679 plist = efGetDeviceParams(EFDevTypes[dev->dev_type]);
2680 for (pptr = plist; pptr != NULL; pptr = pptr->parm_next)
2681 if (pptr->parm_type[0] == 's')
2682 break;
2683
2684 if ((pptr == NULL) && subnode)
2685 {
2686 fprintf(esSpiceF, " ");
2687 subnodeFlat = spcdevSubstrate(hierName,
2688 subnode->efnode_name->efnn_hier,
2689 dev->dev_type, esSpiceF);
2690 }
2691 fprintf(esSpiceF, " %s", EFDevTypes[dev->dev_type]);
2692
2693 /* Write all requested parameters to the subcircuit call. */
2694
2695 sdM = getCurDevMult();
2696 spcWriteParams(dev, hierName, scale, l, w, sdM);
2697 if (sdM != 1.0)
2698 fprintf(esSpiceF, " M=%g", sdM);
2699 break;
2700
2701 case DEV_RES:
2702 if (esDoResistorTee)
2703 {
2704 /* There are three ways of handling capacitance */
2705 /* on resistor networks. One is to ignore it */
2706 /* (the default; generates "floating" nodes in */
2707 /* the SPICE output) which is okay for LVS. */
2708 /* Another way is the Pi network, in which the */
2709 /* capacitance is split evenly between the */
2710 /* terminals. Again, the resistor node is left */
2711 /* floating. The third is the Tee network, in */
2712 /* which the resistance is split in two parts, */
2713 /* connecting to a capacitor to ground in the */
2714 /* middle. This is the best solution but plays */
2715 /* havoc with LVS. So, the choice is a command */
2716 /* line option. */
2717
2718 esOutputResistor(dev, hierName, scale, gate, source, has_model,
2719 l, w, 2);
2720 fprintf(esSpiceF, "\n%c", devchar);
2721 if (gate->dterm_attrs)
2722 fprintf(esSpiceF, "%s%sB", EFHNToStr(hierName), gate->dterm_attrs);
2723 else
2724 fprintf(esSpiceF, "%dB", esResNum - 1);
2725 esOutputResistor(dev, hierName, scale, gate, drain, has_model,
2726 l, w, 2);
2727 }
2728 else
2729 {
2730 esOutputResistor(dev, hierName, scale, source, drain, has_model,
2731 l, w, 1);
2732 }
2733 break;
2734
2735 case DEV_VOLT:
2736 /* The voltage source is "Vnnn term1 term2 0.0" and is used
2737 * only to separate shorted port names.
2738 */
2739 if (dev->dev_nterm > 1)
2740 spcdevOutNode(hierName, source->dterm_node->efnode_name->efnn_hier,
2741 name, esSpiceF);
2742 if (dev->dev_nterm > 2)
2743 spcdevOutNode(hierName, drain->dterm_node->efnode_name->efnn_hier,
2744 name, esSpiceF);
2745 fprintf(esSpiceF, " 0.0");
2746 break;
2747
2748 case DEV_DIODE:
2749 case DEV_PDIODE:
2750
2751 /* Diode is "Dnnn top bottom model" */
2752
2753 spcdevOutNode(hierName, gate->dterm_node->efnode_name->efnn_hier,
2754 name, esSpiceF);
2755 if (dev->dev_nterm > 1)
2756 spcdevOutNode(hierName, source->dterm_node->efnode_name->efnn_hier,
2757 name, esSpiceF);
2758 else if (subnode)
2759 spcdevOutNode(hierName, subnode->efnode_name->efnn_hier,
2760 name, esSpiceF);
2761
2762 fprintf(esSpiceF, " %s", EFDevTypes[dev->dev_type]);
2763 sdM = getCurDevMult();
2764 spcWriteParams(dev, hierName, scale, l, w, sdM);
2765 break;
2766
2767 case DEV_NDIODE:
2768
2769 /* Diode is "Dnnn bottom top model" */
2770
2771 if (dev->dev_nterm > 1)
2772 spcdevOutNode(hierName, source->dterm_node->efnode_name->efnn_hier,
2773 name, esSpiceF);
2774 else if (subnode)
2775 spcdevOutNode(hierName, subnode->efnode_name->efnn_hier,
2776 name, esSpiceF);
2777 spcdevOutNode(hierName, gate->dterm_node->efnode_name->efnn_hier,
2778 name, esSpiceF);
2779
2780 fprintf(esSpiceF, " %s", EFDevTypes[dev->dev_type]);
2781 sdM = getCurDevMult();
2782 spcWriteParams(dev, hierName, scale, l, w, sdM);
2783 break;
2784
2785 case DEV_CAP:
2786
2787 /* Capacitor is "Cnnn top bottom value" */
2788 /* extraction sets top=gate bottom=source */
2789 /* extracted units are fF; output is in fF */
2790
2791 spcdevOutNode(hierName, gate->dterm_node->efnode_name->efnn_hier,
2792 name, esSpiceF);
2793 spcdevOutNode(hierName, source->dterm_node->efnode_name->efnn_hier,
2794 name, esSpiceF);
2795
2796 sdM = getCurDevMult();
2797
2798 /* SPICE has two capacitor types. If the "name" (EFDevTypes) is */
2799 /* "None", the simple capacitor type is used, and a value given. */
2800 /* If not, the "semiconductor capacitor" is used, and L and W */
2801 /* and the device name are output. */
2802
2803 if (!has_model)
2804 {
2805 fprintf(esSpiceF, " %ffF", (double)sdM *
2806 (double)(dev->dev_cap));
2807 spcWriteParams(dev, hierName, scale, l, w, sdM);
2808 }
2809 else
2810 {
2811 fprintf(esSpiceF, " %s", EFDevTypes[dev->dev_type]);
2812
2813 if (esScale < 0)
2814 fprintf(esSpiceF, " w=%g l=%g", w*scale, l*scale);
2815 else
2816 fprintf(esSpiceF, " w=%gu l=%gu",
2817 w * scale * esScale,
2818 l * scale * esScale);
2819
2820 spcWriteParams(dev, hierName, scale, l, w, sdM);
2821 if (sdM != 1.0)
2822 fprintf(esSpiceF, " M=%g", sdM);
2823 }
2824 break;
2825
2826 case DEV_CAPREV:
2827
2828 /* Capacitor is "Cnnn bottom top value" */
2829 /* extraction sets top=source bottom=gate */
2830 /* extracted units are fF; output is in fF */
2831
2832 spcdevOutNode(hierName, source->dterm_node->efnode_name->efnn_hier,
2833 name, esSpiceF);
2834 spcdevOutNode(hierName, gate->dterm_node->efnode_name->efnn_hier,
2835 name, esSpiceF);
2836
2837 sdM = getCurDevMult();
2838
2839 /* SPICE has two capacitor types. If the "name" (EFDevTypes) is */
2840 /* "None", the simple capacitor type is used, and a value given. */
2841 /* If not, the "semiconductor capacitor" is used, and L and W */
2842 /* and the device name are output. */
2843
2844 if (!has_model)
2845 {
2846 fprintf(esSpiceF, " %ffF", (double)sdM *
2847 (double)(dev->dev_cap));
2848 spcWriteParams(dev, hierName, scale, l, w, sdM);
2849 }
2850 else
2851 {
2852 fprintf(esSpiceF, " %s", EFDevTypes[dev->dev_type]);
2853
2854 if (esScale < 0)
2855 fprintf(esSpiceF, " w=%g l=%g", w*scale, l*scale);
2856 else
2857 fprintf(esSpiceF, " w=%gu l=%gu",
2858 w * scale * esScale,
2859 l * scale * esScale);
2860
2861 spcWriteParams(dev, hierName, scale, l, w, sdM);
2862 if (sdM != 1.0)
2863 fprintf(esSpiceF, " M=%g", sdM);
2864 }
2865 break;
2866
2867 case DEV_FET:
2868 case DEV_MOSFET:
2869 case DEV_ASYMMETRIC:
2870
2871 /* MOSFET is "Mnnn drain gate source [L=x W=x [attributes]]" */
2872
2873 spcdevOutNode(hierName, drain->dterm_node->efnode_name->efnn_hier,
2874 name, esSpiceF);
2875 spcdevOutNode(hierName, gate->dterm_node->efnode_name->efnn_hier,
2876 name, esSpiceF);
2877 spcdevOutNode(hierName, source->dterm_node->efnode_name->efnn_hier,
2878 name, esSpiceF);
2879 if (subnode)
2880 {
2881 fprintf(esSpiceF, " ");
2882 subnodeFlat = spcdevSubstrate(hierName,
2883 subnode->efnode_name->efnn_hier,
2884 dev->dev_type, esSpiceF);
2885 }
2886 fprintf(esSpiceF, " %s", EFDevTypes[dev->dev_type]);
2887
2888 /*
2889 * Scale L and W appropriately by the same amount as distance
2890 * values in the transform. The transform will have a scale
2891 * different from 1 only in the case when the scale factors of
2892 * some of the .ext files differed, making it necessary to scale
2893 * all dimensions explicitly instead of having a single scale
2894 * factor at the beginning of the .spice file.
2895 */
2896
2897 sdM = getCurDevMult();
2898
2899 if (esScale < 0)
2900 fprintf(esSpiceF, " w=%g l=%g", w*scale, l*scale);
2901 else
2902 fprintf(esSpiceF, " w=%gu l=%gu",
2903 w * scale * esScale,
2904 l * scale * esScale);
2905
2906 spcWriteParams(dev, hierName, scale, l, w, sdM);
2907 if (sdM != 1.0)
2908 fprintf(esSpiceF, " M=%g", sdM);
2909
2910 /*
2911 * Check controlling attributes and output area and perimeter.
2912 */
2913 hierS = extHierSDAttr(source);
2914 hierD = extHierSDAttr(drain);
2915 if ( gate->dterm_attrs )
2916 subAP = Match(ATTR_SUBSAP, gate->dterm_attrs ) ;
2917
2918 fprintf(esSpiceF, "\n+ ");
2919 if (hierD)
2920 spcnAPHier(drain, hierName, esFetInfo[dev->dev_type].resClassDrain,
2921 scale, "ad", "pd", sdM, esSpiceF);
2922 else
2923 {
2924 dnode = SpiceGetNode(hierName, drain->dterm_node->efnode_name->efnn_hier);
2925 spcnAP(dnode, esFetInfo[dev->dev_type].resClassDrain, scale,
2926 "ad", "pd", sdM, esSpiceF, w);
2927 }
2928 if (hierS)
2929 spcnAPHier(source, hierName, esFetInfo[dev->dev_type].resClassSource,
2930 scale, "as", "ps", sdM, esSpiceF);
2931 else {
2932 snode= SpiceGetNode(hierName, source->dterm_node->efnode_name->efnn_hier);
2933 spcnAP(snode, esFetInfo[dev->dev_type].resClassSource, scale,
2934 "as", "ps", sdM, esSpiceF, w);
2935 }
2936 if (subAP)
2937 {
2938 fprintf(esSpiceF, " * ");
2939 if (esFetInfo[dev->dev_type].resClassSub < 0)
2940 {
2941 TxError("error: subap for devtype %d unspecified\n",
2942 dev->dev_type);
2943 fprintf(esSpiceF, "asub=0 psub=0");
2944 }
2945 else if (subnodeFlat)
2946 spcnAP(subnodeFlat, esFetInfo[dev->dev_type].resClassSub, scale,
2947 "asub", "psub", sdM, esSpiceF, -1);
2948 else
2949 fprintf(esSpiceF, "asub=0 psub=0");
2950 }
2951
2952 /* Now output attributes, if present */
2953 if (!esNoAttrs)
2954 {
2955 if (gate->dterm_attrs || source->dterm_attrs || drain->dterm_attrs)
2956 fprintf(esSpiceF,"\n**devattr");
2957 if (gate->dterm_attrs)
2958 fprintf(esSpiceF, " g=%s", gate->dterm_attrs);
2959 if (source->dterm_attrs)
2960 fprintf(esSpiceF, " s=%s", source->dterm_attrs);
2961 if (drain->dterm_attrs)
2962 fprintf(esSpiceF, " d=%s", drain->dterm_attrs);
2963 }
2964 break;
2965 }
2966 fprintf(esSpiceF, "\n");
2967
2968 return 0;
2969 }
2970
2971 /*
2972 * ----------------------------------------------------------------------------
2973 *
2974 * spcdevSubstrate -
2975 *
2976 * Output the node name of the substrate of a dev. If the suffix is the
2977 * same as the default dont go looking for it just output the default
2978 * (trimmed appropriately). Otherwise look it up ..
2979 *
2980 * Results:
2981 * NULL if not found or the default substrate or the node pointer
2982 * otherwise (might be reused to output area and perimeter of
2983 * the substrate).
2984 *
2985 * Side effects:
2986 * Might allocate the nodeClient for the node through nodeSpiceName.
2987 *
2988 * ----------------------------------------------------------------------------
2989 */
spcdevSubstrate(prefix,suffix,type,outf)2990 EFNode *spcdevSubstrate( prefix, suffix, type, outf)
2991 HierName *prefix, *suffix;
2992 int type;
2993 FILE *outf;
2994 {
2995 HashEntry *he;
2996 EFNodeName *nn;
2997 char *suf;
2998
2999 suf = EFHNToStr(suffix);
3000 if (esFetInfo[type].defSubs && strcasecmp(suf,esFetInfo[type].defSubs) == 0) {
3001 esFormatSubs(outf, suf);
3002 return NULL;
3003 }
3004 else {
3005 he = EFHNConcatLook(prefix, suffix, "substrate");
3006 if (he == NULL)
3007 {
3008 if (outf)
3009 fprintf(outf, "errGnd!");
3010 return NULL;
3011 }
3012 /* Canonical name */
3013 nn = (EFNodeName *) HashGetValue(he);
3014 if (outf)
3015 fprintf(outf, "%s", nodeSpiceName(nn->efnn_node->efnode_name->efnn_hier,
3016 NULL));
3017
3018 /* Create node client if it doesn't exist */
3019 if ((nodeClient *)nn->efnn_node->efnode_client == (ClientData)NULL)
3020 initNodeClientHier(nn->efnn_node);
3021
3022 /* Mark node as visited (set bit one higher than number of resist classes) */
3023 if (esDistrJunct)
3024 update_w(efNumResistClasses, 1, nn->efnn_node);
3025 else
3026 markVisited((nodeClientHier *)nn->efnn_node->efnode_client,
3027 efNumResistClasses);
3028 return nn->efnn_node;
3029 }
3030 }
3031
3032
3033 /*
3034 * ----------------------------------------------------------------------------
3035 *
3036 * spcnAP, spcnAPHier --
3037 *
3038 * Output the area perimeter of the node with type type if it has not
3039 * been visited.
3040 * The spcnAPHier version outputs the area and perimeter only within the
3041 * local subcell with hierarchical name hierName.
3042 *
3043 * Return:
3044 * 0 on success, 1 on error
3045 *
3046 * Side effects:
3047 * Set the visited flags so that the node A/P will not be output multiple
3048 * times
3049 *
3050 * ----------------------------------------------------------------------------
3051 */
spcnAP(node,resClass,scale,asterm,psterm,m,outf,w)3052 int spcnAP(node, resClass, scale, asterm, psterm, m, outf, w)
3053 EFNode *node;
3054 int resClass;
3055 float scale, m;
3056 char *asterm, *psterm;
3057 FILE *outf;
3058 int w;
3059 {
3060 char afmt[15], pfmt[15];
3061 float dsc;
3062
3063 if ((node == NULL) || (node->efnode_client == (ClientData)NULL))
3064 {
3065 TxError("spcnAP: major internal inconsistency\n");
3066 return 1;
3067 }
3068
3069 if (esScale < 0)
3070 {
3071 if (asterm) sprintf(afmt, " %s=%%g", asterm);
3072 if (psterm) sprintf(pfmt, " %s=%%g", psterm);
3073 }
3074 else
3075 {
3076 if (asterm) sprintf(afmt, " %s=%%gp", asterm);
3077 if (psterm) sprintf(pfmt, " %s=%%gu", psterm);
3078 }
3079
3080 if (!esDistrJunct || w == -1) goto oldFmt;
3081
3082 if (((nodeClient*)node->efnode_client)->m_w.widths != NULL)
3083 dsc = w / ((nodeClient*)node->efnode_client)->m_w.widths[resClass];
3084 else
3085 {
3086 TxError("Device missing records for source/drain area/perim.\n");
3087 dsc = w;
3088 }
3089
3090 if (esScale < 0)
3091 {
3092 if (asterm)
3093 fprintf(outf, afmt,
3094 node->efnode_pa[resClass].pa_area * scale * scale * dsc);
3095 if (psterm)
3096 fprintf(outf, pfmt,
3097 node->efnode_pa[resClass].pa_perim * scale * dsc);
3098 }
3099 else
3100 {
3101 if (asterm)
3102 fprintf(outf, afmt,
3103 ((float)node->efnode_pa[resClass].pa_area * scale * scale)
3104 * esScale * esScale * dsc);
3105 if (psterm)
3106 fprintf(outf, pfmt,
3107 ((float)node->efnode_pa[resClass].pa_perim * scale)
3108 * esScale * dsc);
3109 }
3110
3111 return 0;
3112
3113 oldFmt:
3114 if (resClass == NO_RESCLASS ||
3115 beenVisited((nodeClient *)node->efnode_client, resClass))
3116 scale = 0;
3117 else
3118 markVisited((nodeClient *)node->efnode_client, resClass);
3119
3120 if (esScale < 0)
3121 {
3122 if (asterm)
3123 fprintf(outf, afmt,
3124 node->efnode_pa[resClass].pa_area * scale * scale / m);
3125 if (psterm)
3126 fprintf(outf, pfmt,
3127 node->efnode_pa[resClass].pa_perim * scale / m);
3128 }
3129 else
3130 {
3131 if (asterm)
3132 fprintf(outf, afmt,
3133 ((float)node->efnode_pa[resClass].pa_area * scale * scale)
3134 * esScale * esScale);
3135 if (psterm)
3136 fprintf(outf, pfmt,
3137 ((float)node->efnode_pa[resClass].pa_perim * scale)
3138 * esScale);
3139 }
3140 return 0;
3141 }
3142
spcnAPHier(dterm,hierName,resClass,scale,asterm,psterm,m,outf)3143 int spcnAPHier(dterm, hierName, resClass, scale, asterm, psterm, m, outf)
3144 DevTerm *dterm;
3145 HierName *hierName;
3146 int resClass;
3147 float scale, m;
3148 char *asterm, *psterm;
3149 FILE *outf;
3150 {
3151 EFNode *node = dterm->dterm_node;
3152 nodeClientHier *nc;
3153 char afmt[15], pfmt[15];
3154
3155 if (esScale < 0)
3156 {
3157 sprintf(afmt," %s=%%g", asterm);
3158 sprintf(pfmt," %s=%%g", psterm);
3159 }
3160 else
3161 {
3162 sprintf(afmt," %s=%%gp", asterm);
3163 sprintf(pfmt," %s=%%gu", psterm);
3164 }
3165 if (node->efnode_client == (ClientData) NULL)
3166 initNodeClientHier(node);
3167
3168 nc = (nodeClientHier *)node->efnode_client;
3169 if (nc->lastPrefix != hierName)
3170 {
3171 clearVisited(nc);
3172 nc->lastPrefix = hierName;
3173 }
3174 if (resClass == NO_RESCLASS ||
3175 beenVisited((nodeClientHier *)node->efnode_client, resClass) )
3176 scale = 0.0;
3177 else
3178 markVisited((nodeClientHier *)node->efnode_client, resClass);
3179
3180 if (esScale < 0)
3181 {
3182 fprintf(outf, afmt,
3183 node->efnode_pa[resClass].pa_area * scale * scale / m);
3184 fprintf(outf, pfmt,
3185 node->efnode_pa[resClass].pa_perim * scale / m);
3186 }
3187 else
3188 {
3189 fprintf(outf, afmt,
3190 ((float)node->efnode_pa[resClass].pa_area * scale)
3191 * esScale * esScale);
3192 fprintf(outf, pfmt,
3193 ((float)node->efnode_pa[resClass].pa_perim * scale)
3194 * esScale);
3195 }
3196 return 0;
3197 }
3198
3199 /*
3200 * ----------------------------------------------------------------------------
3201 *
3202 * spcdevOutNode --
3203 *
3204 * Output the name of the node whose hierarchical prefix down to this
3205 * point is 'prefix' and whose name from the end of suffix down to the
3206 * leaves is 'suffix', just as in the arguments to EFHNConcat().
3207 *
3208 *
3209 * Results:
3210 * Return number of characters printed on success, 0 on error.
3211 *
3212 * Side effects:
3213 * Writes to the file 'outf'.
3214 * Sets the efnode_client field as described above.
3215 *
3216 * ----------------------------------------------------------------------------
3217 */
3218
3219 int
spcdevOutNode(prefix,suffix,name,outf)3220 spcdevOutNode(prefix, suffix, name, outf)
3221 HierName *prefix;
3222 HierName *suffix;
3223 char *name;
3224 FILE *outf;
3225 {
3226 HashEntry *he;
3227 EFNodeName *nn;
3228 char *nname;
3229
3230 he = EFHNConcatLook(prefix, suffix, name);
3231 if (he == NULL)
3232 {
3233 fprintf(outf, " errGnd!");
3234 return 0;
3235 }
3236 nn = (EFNodeName *) HashGetValue(he);
3237 nname = nodeSpiceName(nn->efnn_node->efnode_name->efnn_hier, NULL);
3238 fprintf(outf, " %s", nname);
3239
3240 /* Mark node as visited (set bit one higher than number of resist classes) */
3241 if (esDistrJunct)
3242 update_w(efNumResistClasses, 1, nn->efnn_node);
3243 else
3244 markVisited((nodeClientHier *)nn->efnn_node->efnode_client,
3245 efNumResistClasses);
3246
3247 return (1 + strlen(nname));
3248 }
3249
3250
3251 /*
3252 * ----------------------------------------------------------------------------
3253 *
3254 * spccapVisit --
3255 *
3256 * Procedure to output a single capacitor to the .spice file.
3257 * Called by EFVisitCaps().
3258 *
3259 * Results:
3260 * Returns 0 always.
3261 *
3262 * Side effects:
3263 * Writes to the file esSpiceF. Increments esCapNum.
3264 *
3265 * Format of a .spice cap line:
3266 *
3267 * C%d node1 node2 cap
3268 *
3269 * where
3270 * node1, node2 are the terminals of the capacitor
3271 * cap is the capacitance in femtofarads (NOT attofarads).
3272 *
3273 * ----------------------------------------------------------------------------
3274 */
3275
3276 int
spccapVisit(hierName1,hierName2,cap)3277 spccapVisit(hierName1, hierName2, cap)
3278 HierName *hierName1;
3279 HierName *hierName2;
3280 double cap;
3281 {
3282 cap = cap / 1000;
3283 if (cap <= EFCapThreshold)
3284 return 0;
3285
3286 fprintf(esSpiceF, esSpiceCapFormat ,esCapNum++,nodeSpiceName(hierName1, NULL),
3287 nodeSpiceName(hierName2, NULL), cap);
3288 return 0;
3289 }
3290
3291 /*
3292 * ----------------------------------------------------------------------------
3293 *
3294 * spcresistVisit --
3295 *
3296 * Procedure to output a single resistor to the .spice file.
3297 * Called by EFVisitResists().
3298 *
3299 * Results:
3300 * Returns 0 always.
3301 *
3302 * Side effects:
3303 * Writes to the file esSpiceF. Increments esResNum.
3304 *
3305 * Format of a .spice resistor line:
3306 *
3307 * R%d node1 node2 res
3308 *
3309 * where
3310 * node1, node2 are the terminals of the resistor
3311 * res is the resistance in ohms (NOT milliohms)
3312 *
3313 *
3314 * ----------------------------------------------------------------------------
3315 */
3316 int
spcresistVisit(hierName1,hierName2,res)3317 spcresistVisit(hierName1, hierName2, res)
3318 HierName *hierName1;
3319 HierName *hierName2;
3320 float res;
3321 {
3322 fprintf(esSpiceF, "R%d %s %s %g\n", esResNum++, nodeSpiceName(hierName1, NULL),
3323 nodeSpiceName(hierName2, NULL), res / 1000.);
3324
3325 return 0;
3326 }
3327
3328
3329 /*
3330 * ----------------------------------------------------------------------------
3331 *
3332 * spcsubVisit --
3333 *
3334 * Routine to find the node that connects to substrate. Copy the
3335 * string name of this node into "resstr" to be returned to the
3336 * caller.
3337 *
3338 * Results:
3339 * Return 1 if the substrate node has been found, to stop the search.
3340 * Otherwise return 0 to keep the search going.
3341 *
3342 * ----------------------------------------------------------------------------
3343 */
3344
3345 int
spcsubVisit(node,res,cap,resstr)3346 spcsubVisit(node, res, cap, resstr)
3347 EFNode *node;
3348 int res; // Unused
3349 double cap; // Unused
3350 char **resstr;
3351 {
3352 HierName *hierName;
3353 char *nsn;
3354
3355 if (node->efnode_flags & EF_SUBS_NODE)
3356 {
3357 hierName = (HierName *) node->efnode_name->efnn_hier;
3358 nsn = nodeSpiceName(hierName, NULL);
3359 *resstr = StrDup((char **)NULL, nsn);
3360 return 1;
3361 }
3362 return 0;
3363 }
3364
3365 /*
3366 * ----------------------------------------------------------------------------
3367 *
3368 * spcnodeVisit --
3369 *
3370 * Procedure to output a single node to the .spice file along with its
3371 * attributes and its dictionary (if present). Called by EFVisitNodes().
3372 *
3373 * Results:
3374 * Returns 0 always.
3375 *
3376 * Side effects:
3377 * Writes to the files esSpiceF
3378 *
3379 * ----------------------------------------------------------------------------
3380 */
3381
3382 int
spcnodeVisit(node,res,cap)3383 spcnodeVisit(node, res, cap)
3384 EFNode *node;
3385 int res;
3386 double cap;
3387 {
3388 EFNodeName *nn;
3389 HierName *hierName;
3390 bool isConnected = FALSE;
3391 char *fmt, *nsn;
3392 EFAttr *ap;
3393
3394 if (node->efnode_client)
3395 {
3396 isConnected = (esDistrJunct) ?
3397 (((nodeClient *)node->efnode_client)->m_w.widths != NULL) :
3398 (!TTMaskHasType(&((nodeClient *)node->efnode_client)->m_w.visitMask,
3399 efNumResistClasses));
3400 }
3401 if (!isConnected && esDevNodesOnly)
3402 return 0;
3403
3404 /* Don't mark known ports as "FLOATING" nodes */
3405 if (!isConnected && node->efnode_flags & EF_PORT) isConnected = TRUE;
3406
3407 hierName = (HierName *) node->efnode_name->efnn_hier;
3408 nsn = nodeSpiceName(hierName, NULL);
3409
3410 if (esFormat == SPICE2 || esFormat == HSPICE && strncmp(nsn, "z@", 2)==0 ) {
3411 static char ntmp[MAX_STR_SIZE];
3412
3413 EFHNSprintf(ntmp, hierName);
3414 if (esFormat == NGSPICE) fprintf(esSpiceF, "$ ");
3415 fprintf(esSpiceF, "** %s == %s\n", ntmp, nsn);
3416 }
3417 cap = cap / 1000;
3418 if (cap > EFCapThreshold)
3419 {
3420 fprintf(esSpiceF, esSpiceCapFormat, esCapNum++, nsn, cap,
3421 (isConnected) ? "\n" :
3422 (esFormat == NGSPICE) ? " $ **FLOATING\n" :
3423 " **FLOATING\n");
3424 }
3425 if (node->efnode_attrs && !esNoAttrs)
3426 {
3427 if (esFormat == NGSPICE) fprintf(esSpiceF, " $ ");
3428 fprintf(esSpiceF, "**nodeattr %s :",nsn );
3429 for (fmt = " %s", ap = node->efnode_attrs; ap; ap = ap->efa_next)
3430 {
3431 fprintf(esSpiceF, fmt, ap->efa_text);
3432 fmt = ",%s";
3433 }
3434 putc('\n', esSpiceF);
3435 }
3436
3437 return 0;
3438 }
3439
3440 /* a debugging procedure */
3441 int
nodeVisitDebug(node,res,cap)3442 nodeVisitDebug(node, res, cap)
3443 EFNode *node;
3444 int res;
3445 double cap;
3446 {
3447 EFNodeName *nn;
3448 HierName *hierName;
3449 char *fmt, *nsn;
3450 EFAttr *ap;
3451
3452 hierName = (HierName *) node->efnode_name->efnn_hier;
3453 nsn = nodeSpiceName(hierName, NULL);
3454 TxError("** %s (%x)\n", nsn, node);
3455
3456 printf("\t client.name=%s, client.m_w=%p\n",
3457 ((nodeClient *)node->efnode_client)->spiceNodeName,
3458 ((nodeClient *)node->efnode_client)->m_w.widths);
3459 return 0;
3460 }
3461
3462 /*
3463 * ----------------------------------------------------------------------------
3464 *
3465 * nodeSpiceName --
3466 * Find the real spice name for the node with hierarchical name hname.
3467 * SPICE2 ==> numeric
3468 * SPICE3 ==> full magic path
3469 * HSPICE ==> less than 15 characters long
3470 *
3471 * Results:
3472 * Returns the spice node name.
3473 *
3474 * Side effects:
3475 * Allocates nodeClients for the node.
3476 * Returns the node in the "rnode" pointer, if non-NULL.
3477 *
3478 * ----------------------------------------------------------------------------
3479 */
3480 static char esTempName[MAX_STR_SIZE];
3481
nodeSpiceName(hname,rnode)3482 char *nodeSpiceName(hname, rnode)
3483 HierName *hname;
3484 EFNode **rnode;
3485 {
3486 EFNodeName *nn;
3487 HashEntry *he;
3488 EFNode *node;
3489
3490 if (rnode) *rnode = (EFNode *)NULL;
3491 he = EFHNLook(hname, (char *) NULL, "nodeName");
3492 if ( he == NULL )
3493 return "errGnd!";
3494 nn = (EFNodeName *) HashGetValue(he);
3495 node = nn->efnn_node;
3496 if (rnode) *rnode = node;
3497
3498 if ( (nodeClient *) (node->efnode_client) == NULL ) {
3499 initNodeClient(node);
3500 goto makeName;
3501 } else if ( ((nodeClient *) (node->efnode_client))->spiceNodeName == NULL)
3502 goto makeName;
3503 else goto retName;
3504
3505
3506 makeName:
3507 if ( esFormat == SPICE2 )
3508 sprintf(esTempName, "%d", esNodeNum++);
3509 else {
3510 EFHNSprintf(esTempName, node->efnode_name->efnn_hier);
3511 if ( esFormat == HSPICE ) /* more processing */
3512 nodeHspiceName(esTempName);
3513 }
3514 ((nodeClient *) (node->efnode_client))->spiceNodeName =
3515 StrDup(NULL, esTempName);
3516
3517 retName:
3518 return ((nodeClient *) (node->efnode_client))->spiceNodeName;
3519 }
3520
3521 /*
3522 * ----------------------------------------------------------------------------
3523 *
3524 * EFHNSprintf --
3525 *
3526 * Create a hierarchical node name.
3527 * The flags in EFOutputFlags control whether global (!) or local (#)
3528 * suffixes are to be trimmed. Also substitutes \. with \@ if the
3529 * format is hspice.
3530 *
3531 * Results:
3532 * None.
3533 *
3534 * Side effects:
3535 * Changes the area pointed to by str
3536 *
3537 * ----------------------------------------------------------------------------
3538 */
3539
3540 int
EFHNSprintf(str,hierName)3541 EFHNSprintf(str, hierName)
3542 char *str;
3543 HierName *hierName;
3544 {
3545 bool trimGlob, trimLocal, convertComma, convertEqual, convertBrackets;
3546 char *s, *cp, c;
3547 char *efHNSprintfPrefix(HierName *, char *);
3548
3549 s = str;
3550 if (hierName->hn_parent) str = efHNSprintfPrefix(hierName->hn_parent, str);
3551 if (EFOutputFlags & EF_TRIM_MASK)
3552 {
3553 cp = hierName->hn_name;
3554 trimGlob = (EFOutputFlags & EF_TRIMGLOB);
3555 trimLocal = (EFOutputFlags & EF_TRIMLOCAL);
3556 convertComma = (EFOutputFlags & EF_CONVERTCOMMA);
3557 convertEqual = (EFOutputFlags & EF_CONVERTEQUAL);
3558 convertBrackets = (EFOutputFlags & EF_CONVERTBRACKETS);
3559 while (c = *cp++)
3560 {
3561 switch (c)
3562 {
3563 case '!': if (!trimGlob) *str++ = c; break;
3564 case '.': *str++ = (esFormat == HSPICE)?'@':'.'; break;
3565 case '=': if (convertEqual) *str++ = ':'; break;
3566 case ',': if (convertComma) *str++ = '|'; break;
3567 case '[': *str++ = (convertBrackets) ? '_' : '['; break;
3568 case ']': *str++ = (convertBrackets) ? '_' : ']'; break;
3569 case '#': if (trimLocal) break; // else fall through
3570 default: *str++ = c; break;
3571 }
3572 }
3573 *str++ = '\0';
3574 }
3575 else strcpy(str, hierName->hn_name);
3576 return 0;
3577 }
3578
efHNSprintfPrefix(hierName,str)3579 char *efHNSprintfPrefix(hierName, str)
3580 HierName *hierName;
3581 char *str;
3582 {
3583 char *cp, c;
3584 bool convertEqual = (EFOutputFlags & EF_CONVERTEQUAL) ? TRUE : FALSE;
3585 bool convertComma = (EFOutputFlags & EF_CONVERTCOMMA) ? TRUE : FALSE;
3586 bool convertBrackets = (EFOutputFlags & EF_CONVERTBRACKETS) ? TRUE : FALSE;
3587
3588 if (hierName->hn_parent)
3589 str = efHNSprintfPrefix(hierName->hn_parent, str);
3590
3591 cp = hierName->hn_name;
3592 while (1) {
3593 if (convertEqual && (*cp == '='))
3594 *str = ':';
3595 else if (convertBrackets && ((*cp == '[') || (*cp == ']')))
3596 *str = '_';
3597 else if (*cp == ',')
3598 {
3599 if (convertComma) *str = '|';
3600 else str--;
3601 }
3602 else
3603 *str = *cp;
3604 if (!(*str)) break;
3605 str++;
3606 cp++;
3607 }
3608 *str = '/';
3609 return ++str;
3610 }
3611
3612 /*
3613 * ----------------------------------------------------------------------------
3614 *
3615 * nodeHspiceName --
3616 *
3617 * Convert the hierarchical node name used in Berkeley spice
3618 * to a name understood by hspice and hopefully by the user.
3619 *
3620 * Results:
3621 * A somewhat meaningful node name
3622 *
3623 * Side effects:
3624 * Mucks with the hash table above.
3625 *
3626 * ----------------------------------------------------------------------------
3627 */
3628
nodeHspiceName(s)3629 int nodeHspiceName(s)
3630 char *s;
3631 {
3632 char *p, *sf;
3633 int l, snum = -1;
3634 HashEntry *he;
3635 static char map[MAX_STR_SIZE];
3636
3637 /*
3638 * find the suffix
3639 */
3640 l = strlen(s);
3641 for (p = s + l; (p > s) && *p != '/'; p--);
3642 if (p == s)
3643 {
3644 strcpy(map, s);
3645 goto topLevel;
3646 }
3647
3648 /*
3649 * break it into prefix '/0' suffix
3650 */
3651 if (*p == '/')
3652 *p = 0;
3653 sf = p + 1;
3654
3655 /*
3656 * look up prefix in the hash table and create it if doesn't exist
3657 */
3658 if ((he = HashLookOnly(&subcktNameTable, s)) == NULL)
3659 {
3660 snum = esSbckNum++;
3661 he = HashFind(&subcktNameTable, s);
3662 HashSetValue(he, (ClientData)(pointertype) snum);
3663 #ifndef UNSORTED_SUBCKT
3664 DQPushRear(&subcktNameQueue, he);
3665 #endif
3666 }
3667 else
3668 snum = (spointertype) HashGetValue(he);
3669 sprintf(map, "x%d/%s", snum, sf);
3670
3671 topLevel:
3672 strcpy(s, map);
3673 if (strlen(s) > 15)
3674 {
3675 /* still hspice does not get it */
3676 sprintf(s, "z@%d", esNodeNum++);
3677 if (strlen(s) > 15)
3678 {
3679 /* screw it: hspice will not work */
3680 TxError("Error: too many nodes in this circuit to be "
3681 "output as names\n");
3682 TxError(" use spice2 format or call and complain "
3683 "to Meta software about their stupid parser\n");
3684 #ifdef MAGIC_WRAPPER
3685 return TCL_ERROR;
3686 #else
3687 exit(1);
3688 #endif
3689 }
3690 }
3691 return 0;
3692 }
3693
3694 /*
3695 * ----------------------------------------------------------------------------
3696 *
3697 * printSubcktDict --
3698 *
3699 * Print out the hspice subcircuit dictionary. Ideally this should go to a
3700 * pa0 file but uncfortunately hspice crashes if the node names contain
3701 * dots so we just append it at the end of the spice file
3702 *
3703 * Results:
3704 * None.
3705 *
3706 * Side effects:
3707 * Writes to the output file
3708 *
3709 * ----------------------------------------------------------------------------
3710 */
3711
printSubcktDict()3712 int printSubcktDict()
3713 {
3714 HashSearch hs;
3715 HashEntry *he;
3716
3717 fprintf(esSpiceF,"\n** hspice subcircuit dictionary\n");
3718
3719 #ifndef UNSORTED_SUBCKT
3720 while ((he = (HashEntry *)DQPopFront(&subcktNameQueue)) != NULL)
3721 #else
3722 HashStartSearch(&hs);
3723 while ((he = HashNext(&subcktNameTable, &hs)) != NULL)
3724 #endif
3725 fprintf(esSpiceF,"* x%"DLONG_PREFIX"d\t%s\n", (dlong) HashGetValue(he), he->h_key.h_name);
3726 return 0;
3727 }
3728
3729 /*
3730 * ----------------------------------------------------------------------------
3731 *
3732 * mkDevMerge --
3733 * Create a new devMerge structure.
3734 *
3735 * Results:
3736 * Obvious
3737 *
3738 * Side effects:
3739 * Allocates memory and sets the fields of the structure.
3740 *
3741 * ----------------------------------------------------------------------------
3742 */
3743
mkDevMerge(l,w,g,s,d,b,hn,dev)3744 devMerge *mkDevMerge(l, w, g, s, d, b, hn, dev)
3745 float l, w;
3746 EFNode *g, *s, *d, *b;
3747 HierName *hn;
3748 Dev *dev;
3749 {
3750 devMerge *fp;
3751
3752 fp = (devMerge *) mallocMagic((unsigned) (sizeof(devMerge)));
3753 fp->l = l; fp->w = w;
3754 fp->g = g; fp->s = s;
3755 fp->d = d; fp->b = b;
3756 fp->dev = dev;
3757 fp->esFMIndex = esFMIndex;
3758 fp->hierName = hn;
3759 fp->next = NULL;
3760 addDevMult(1.0);
3761
3762 return fp;
3763 }
3764
3765 /*
3766 * ----------------------------------------------------------------------------
3767 *
3768 * parallelDevs --
3769 *
3770 * Determine if two devs are in parallel
3771 *
3772 * Results:
3773 * NOT_PARALLEL if not in parallel
3774 * PARALLEL if parallel and an exact match
3775 * ANTIPARALLEL if parallel but reversed source<->drain nodes
3776 *
3777 * Side effects:
3778 * None.
3779 *
3780 * ----------------------------------------------------------------------------
3781 */
3782
3783 int
parallelDevs(f1,f2)3784 parallelDevs(f1, f2)
3785 devMerge *f1, *f2;
3786 {
3787 /* If the devices are not in the same class, then */
3788 /* they cannot be parallel. */
3789
3790 if (f1->dev->dev_class != f2->dev->dev_class)
3791 return NOT_PARALLEL;
3792
3793 /* Can't merge devices with different models */
3794 if (f1->dev->dev_type != f2->dev->dev_type)
3795 return NOT_PARALLEL;
3796
3797 /* Class-dependent action */
3798 switch(f1->dev->dev_class)
3799 {
3800 case DEV_MSUBCKT:
3801 case DEV_MOSFET:
3802 case DEV_FET:
3803
3804 if (f1->b != f2->b) return NOT_PARALLEL;
3805 if ((f1->g == f2->g) && (f1->l == f2->l)
3806 && (esMergeDevsA || (f1->w == f2->w)))
3807 {
3808 if ((f1->d == f2->d) && (f1->s == f2->s))
3809 return PARALLEL;
3810 else if ((f1->s == f2->d) && (f1->d == f2->s))
3811 return ANTIPARALLEL;
3812 }
3813 break;
3814
3815 case DEV_ASYMMETRIC:
3816
3817 if (f1->b != f2->b) return NOT_PARALLEL;
3818 if ((f1->g == f2->g) && (f1->d == f2->d) && (f1->s == f2->s)
3819 && (f1->l == f2->l) && (esMergeDevsA || (f1->w == f2->w)))
3820 {
3821 return PARALLEL;
3822 }
3823 break;
3824
3825 /* Capacitors match if top ("gate") and bottom ("source") are */
3826 /* the same. Do not attempt to swap top and bottom, as we do */
3827 /* not know when it is safe to do so. */
3828
3829 case DEV_CAP:
3830 case DEV_CAPREV:
3831 if ((f1->g != f2->g) || (f1->s != f2->s))
3832 return NOT_PARALLEL;
3833
3834 else if (f1->dev->dev_type == esNoModelType)
3835 {
3836 /* Unmodeled capacitor */
3837 if (esMergeDevsA || (f1->dev->dev_cap == f2->dev->dev_cap))
3838 return PARALLEL;
3839 }
3840 else if (esMergeDevsA || ((f1->l == f2->l) && (f1->w == f2->w)))
3841 return PARALLEL;
3842 break;
3843
3844 /* We can't merge resistors because we accumulate capacitance */
3845 /* on the central ("gate") node. Merging the devices would */
3846 /* cause nodes to disappear. */
3847
3848 case DEV_RES:
3849 break;
3850
3851 /* For the remaining devices, it might be possible to merge */
3852 /* if we know that the device model level accepts length and */
3853 /* width parameters. However, at this time, no such */
3854 /* information is passed to the SPICE deck, so we don't try to */
3855 /* merge these devices. */
3856
3857 case DEV_BJT:
3858 case DEV_DIODE:
3859 case DEV_NDIODE:
3860 case DEV_PDIODE:
3861 break;
3862
3863 /* There is no way to merge subcircuit devices */
3864
3865 case DEV_SUBCKT:
3866 case DEV_RSUBCKT:
3867 case DEV_CSUBCKT:
3868 break;
3869
3870 case DEV_VOLT:
3871 break;
3872 }
3873 return NOT_PARALLEL;
3874 }
3875
3876 /*
3877 * ----------------------------------------------------------------------------
3878 *
3879 * mergeAttr --
3880 *
3881 * merge two attribute strings
3882 *
3883 * Results:
3884 * The merged strings
3885 *
3886 * Side effects:
3887 * Might allocate and free memory.
3888 *
3889 * ----------------------------------------------------------------------------
3890 */
3891
3892 void
mergeAttr(a1,a2)3893 mergeAttr(a1, a2)
3894 char **a1, **a2;
3895 {
3896 if (*a1 == NULL)
3897 *a1 = *a2;
3898 else
3899 {
3900 char *t;
3901 int l1 = strlen(*a1);
3902 int l2 = strlen(*a2);
3903 t = (char *) mallocMagic((unsigned int)((l1 + l2) + 1));
3904 t = (char *) strcat(*a1, *a2);
3905 freeMagic(*a1);
3906 *a1 = t;
3907 }
3908 }
3909
3910 /*
3911 * ----------------------------------------------------------------------------
3912 *
3913 * devMergeVisit --
3914 * Visits each dev throu EFVisitDevs and finds if it is in parallel with
3915 * any previously visited dev.
3916 *
3917 * Results:
3918 * 0 always to keep the caller going.
3919 *
3920 * Side effects:
3921 * Numerous.
3922 *
3923 * ----------------------------------------------------------------------------
3924 */
3925
3926 int
devMergeVisit(dev,hc,scale,trans)3927 devMergeVisit(dev, hc, scale, trans)
3928 Dev *dev; /* Dev to examine */
3929 HierContext *hc; /* Hierarchical context down to this dev */
3930 float scale; /* Scale transform */
3931 Transform *trans; /* (unused) */
3932 {
3933 DevTerm *gate, *source, *drain;
3934 Dev *cf;
3935 DevTerm *cg, *cs, *cd;
3936 EFNode *subnode, *snode, *dnode, *gnode;
3937 int pmode, l, w;
3938 bool hS, hD, chS, chD;
3939 devMerge *fp, *cfp;
3940 float m;
3941 HierName *hierName = hc->hc_hierName;
3942
3943 if (esDistrJunct)
3944 devDistJunctVisit(dev, hc, scale, trans);
3945
3946 if (dev->dev_nterm < 2)
3947 {
3948 TxError("outPremature\n");
3949 return 0;
3950 }
3951
3952 gate = &dev->dev_terms[0];
3953 if (dev->dev_nterm >= 2)
3954 source = drain = &dev->dev_terms[1];
3955 if (dev->dev_nterm >= 3)
3956 drain = &dev->dev_terms[2];
3957
3958 gnode = SpiceGetNode(hierName, gate->dterm_node->efnode_name->efnn_hier);
3959 if (dev->dev_nterm >= 2)
3960 {
3961 snode = SpiceGetNode(hierName, source->dterm_node->efnode_name->efnn_hier);
3962 dnode = SpiceGetNode(hierName, drain->dterm_node->efnode_name->efnn_hier);
3963 }
3964 if (dev->dev_subsnode)
3965 subnode = spcdevSubstrate(hierName,
3966 dev->dev_subsnode->efnode_name->efnn_hier,
3967 dev->dev_type, NULL);
3968 else
3969 subnode = NULL;
3970
3971 /* Get length and width of the device */
3972 EFGetLengthAndWidth(dev, &l, &w);
3973
3974 fp = mkDevMerge((float)((float)l * scale), (float)((float)w * scale),
3975 gnode, snode, dnode, subnode, hierName, dev);
3976 hS = extHierSDAttr(source);
3977 hD = extHierSDAttr(drain);
3978
3979 /*
3980 * run the list of devs. compare the current one with
3981 * each one in the list. if they fullfill the matching requirements
3982 * merge them only if:
3983 * 1) they have both apf S, D attributes
3984 * or
3985 * 2) one of them has aph S, D attributes and they have the same
3986 * hierarchical prefix
3987 * If one of them has apf and the other aph print a warning.
3988 */
3989
3990 for (cfp = devMergeList; cfp != NULL; cfp = cfp->next)
3991 {
3992 if ((pmode = parallelDevs(fp, cfp)) != NOT_PARALLEL)
3993 {
3994 cf = cfp->dev;
3995 cg = &cfp->dev->dev_terms[0];
3996 cs = cd = &cfp->dev->dev_terms[1];
3997 if (cfp->dev->dev_nterm >= 3)
3998 {
3999 if (pmode == PARALLEL)
4000 cd = &cfp->dev->dev_terms[2];
4001 else if (pmode == ANTIPARALLEL)
4002 cs = &cfp->dev->dev_terms[2];
4003 }
4004
4005 chS = extHierSDAttr(cs); chD = extHierSDAttr(cd);
4006 if (!(chS || chD || hS || hD)) /* all flat S, D */
4007 goto mergeThem;
4008 if (cfp->hierName != hierName &&
4009 ((chS && !hS) || (chD && !hD) ||
4010 (!chS && hS) || (!chD && hD)))
4011 {
4012
4013 efHNSprintfPrefix((cfp->hierName)?cfp->hierName:hierName,
4014 esTempName);
4015 TxError("Warning: conflicting SD attributes of parallel"
4016 " devs in cell: %s\n", esTempName);
4017 break;
4018 }
4019 else if (cfp->hierName == hierName)
4020 {
4021 if (hS && !chS)
4022 {
4023 mergeAttr(&cs->dterm_attrs, &source->dterm_attrs);
4024 }
4025 if (hD && !chD)
4026 {
4027 mergeAttr(&cd->dterm_attrs, &drain->dterm_attrs);
4028 }
4029 }
4030 else /* cfp->hierName != hierName */
4031 break;
4032 mergeThem:
4033 switch(dev->dev_class)
4034 {
4035 case DEV_MSUBCKT:
4036 case DEV_MOSFET:
4037 case DEV_ASYMMETRIC:
4038 case DEV_FET:
4039 m = esFMult[cfp->esFMIndex] + (fp->w / cfp->w);
4040 break;
4041 case DEV_RSUBCKT:
4042 case DEV_RES:
4043 if (fp->dev->dev_type == esNoModelType)
4044 m = esFMult[cfp->esFMIndex] + (fp->dev->dev_res
4045 / cfp->dev->dev_res);
4046 else
4047 m = esFMult[cfp->esFMIndex] + (fp->l / cfp->l);
4048 break;
4049 case DEV_CSUBCKT:
4050 case DEV_CAP:
4051 case DEV_CAPREV:
4052 if (fp->dev->dev_type == esNoModelType)
4053 m = esFMult[cfp->esFMIndex] + (fp->dev->dev_cap
4054 / cfp->dev->dev_cap);
4055 else
4056 m = esFMult[cfp->esFMIndex] +
4057 ((fp->l * fp->w) / (cfp->l * cfp->w));
4058 break;
4059 }
4060 setDevMult(fp->esFMIndex, DEV_KILLED);
4061 setDevMult(cfp->esFMIndex, m);
4062 esSpiceDevsMerged++;
4063 /* Need to do attribute stuff here */
4064 freeMagic(fp);
4065 return 0;
4066 }
4067 }
4068
4069 /* No parallel devs to it yet */
4070 fp->next = devMergeList;
4071 devMergeList = fp;
4072 return 0;
4073 }
4074
4075 /*
4076 * ----------------------------------------------------------------------------
4077 *
4078 * update_w --
4079 * Updates the width client of node n with the current dev width
4080 *
4081 * Results:
4082 * N/A
4083 *
4084 * Side effects:
4085 * might allocate node client and widths
4086 *
4087 * ----------------------------------------------------------------------------
4088 */
4089
4090 int
update_w(resClass,w,n)4091 update_w(resClass, w, n)
4092 short resClass;
4093 int w;
4094 EFNode *n;
4095 {
4096 nodeClient *nc;
4097 int i;
4098
4099 if (n->efnode_client == (ClientData)NULL)
4100 initNodeClient(n);
4101 nc = (nodeClient *) n->efnode_client;
4102 if (nc->m_w.widths == NULL)
4103 {
4104 (nc->m_w.widths) = (float *)mallocMagic((unsigned)sizeof(float)
4105 * (efNumResistClasses + 1));
4106 for (i = 0; i <= efNumResistClasses; i++)
4107 nc->m_w.widths[i] = 0.0;
4108 }
4109 nc->m_w.widths[resClass] += (float)w;
4110 return 0;
4111 }
4112
4113 /*
4114 * ----------------------------------------------------------------------------
4115 *
4116 * devDistJunctVisit --
4117 * Called for every dev and updates the nodeclients of its terminals
4118 *
4119 * Results:
4120 * 0 to keep the calling procedure going
4121 *
4122 * Side effects:
4123 * calls update_w which might allocate stuff
4124 *
4125 * ----------------------------------------------------------------------------
4126 */
4127
4128 int
devDistJunctVisit(dev,hc,scale,trans)4129 devDistJunctVisit(dev, hc, scale, trans)
4130 Dev *dev; /* Dev to examine */
4131 HierContext *hc; /* Hierarchical path down to this dev */
4132 float scale; /* Scale transform */
4133 Transform *trans; /* (unused) */
4134 {
4135 EFNode *n;
4136 int i;
4137 int l, w;
4138 HierName *hierName = hc->hc_hierName;
4139
4140 if (dev->dev_nterm < 2)
4141 {
4142 TxError("outPremature\n");
4143 return 0;
4144 }
4145
4146 EFGetLengthAndWidth(dev, &l, &w);
4147 w = (int)((float)w * scale);
4148
4149 for (i = 1; i<dev->dev_nterm; i++)
4150 {
4151 n = SpiceGetNode(hierName,
4152 dev->dev_terms[i].dterm_node->efnode_name->efnn_hier);
4153 if (i == 1)
4154 update_w(esFetInfo[dev->dev_type].resClassSource, w, n);
4155 else
4156 update_w(esFetInfo[dev->dev_type].resClassDrain, w, n);
4157 }
4158 return 0;
4159 }
4160
4161