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