1 /* "NETGEN", a netlist-specification tool for VLSI
2    Copyright (C) 1989, 1990   Massimo A. Sivilotti
3    Author's address: mass@csvax.cs.caltech.edu;
4                      Caltech 256-80, Pasadena CA 91125.
5 
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation (any version).
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; see the file copying.  If not, write to
17 the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
18 
19 /* netgen.c  -- most of the netlist manipulation routines and
20                 embedded-language specification routines.
21 */
22 
23 #include "config.h"
24 
25 #include <stdio.h>
26 #include <stdlib.h>	/* for strtof() */
27 #include <stdarg.h>
28 #include <ctype.h>	/* toupper() */
29 #ifdef IBMPC
30 #include <alloc.h>
31 #endif
32 
33 #ifdef TCL_NETGEN
34 #include <tcl.h>
35 #endif
36 
37 #include "netgen.h"
38 #include "hash.h"
39 #include "objlist.h"
40 #include "netfile.h"
41 #include "print.h"
42 #include "netcmp.h"
43 
44 int Debug = 0;
45 int VerboseOutput = 1;  /* by default, we get verbose output */
46 int IgnoreRC = 0;
47 
48 int NextNode;
49 
50 int Composition = NONE;
51 int QuickSearch = 0;
52 int GlobalParallelNone = FALSE;
53 int GlobalParallelOpen = TRUE;
54 
55 int AddToExistingDefinition = 0;  /* default: overwrite cell when reopened */
56 
57 extern int errno;	/* Defined in stdlib.h */
58 
59 #define MAX_STATIC_STRINGS 5
60 static char staticstrings[MAX_STATIC_STRINGS][200];
61 static int laststring;
62 
63 extern struct hashdict spiceparams;	/* From spice.c */
64 
Str(char * format,...)65 char *Str(char *format, ...)
66 {
67   va_list ap;
68 
69   laststring++;
70   laststring = laststring % MAX_STATIC_STRINGS;
71 
72   va_start(ap, format);
73   vsprintf(staticstrings[laststring], format, ap);
74   va_end(ap);
75   return(staticstrings[laststring]);
76 }
77 
78 /*--------------------------------------------------------------*/
79 /* Push a token on to the expression stack			*/
80 /*--------------------------------------------------------------*/
81 
PushTok(int toktype,void * tval,struct tokstack ** top)82 void PushTok(int toktype, void *tval, struct tokstack **top)
83 {
84     struct tokstack *newstack;
85     double dval;
86     char *string;
87 
88     newstack = (struct tokstack *)CALLOC(1, sizeof(struct tokstack));
89     newstack->toktype = toktype;
90 
91     switch (toktype) {
92 	case TOK_DOUBLE:
93 	    newstack->data.dvalue = *((double *)tval);
94 	    break;
95 	case TOK_STRING:
96 	    newstack->data.string = strsave((char *)tval);
97 	    break;
98 	case TOK_SGL_QUOTE:
99 	case TOK_DBL_QUOTE:
100 	case TOK_FUNC_OPEN:
101 	case TOK_FUNC_CLOSE:
102 	case TOK_GROUP_OPEN:
103 	case TOK_GROUP_CLOSE:
104 	case TOK_FUNC_IF:
105 	case TOK_FUNC_THEN:
106 	case TOK_FUNC_ELSE:
107 	    newstack->data.dvalue = 0.0;
108 	    break;
109 	default:
110 	    newstack->data.string = NULL;
111 	    break;
112     }
113     newstack->last = NULL;
114     newstack->next = *top;
115     if (*top != NULL)
116 	(*top)->last = newstack;
117     *top = newstack;
118 }
119 
120 /*--------------------------------------------------------------*/
121 /* Pop a token off of the expression stack, freeing the memory	*/
122 /* associated with it.						*/
123 /*--------------------------------------------------------------*/
124 
PopTok(struct tokstack ** top)125 void PopTok(struct tokstack **top)
126 {
127     struct tokstack *stackptr;
128 
129     stackptr = *top;
130     if (!stackptr) return;
131     *top = stackptr->next;
132     (*top)->last = NULL;
133 
134     /* Free the memory allocated to the popped entry */
135     if (stackptr->toktype == TOK_STRING)
136 	FREE(stackptr->data.string);
137     FREE(stackptr);
138 }
139 
140 /*--------------------------------------------------------------*/
141 /* Make a copy of an expression 				*/
142 /*--------------------------------------------------------------*/
143 
CopyTokStack(struct tokstack * stack)144 struct tokstack *CopyTokStack(struct tokstack *stack)
145 {
146     struct tokstack *stackptr, *newstack, *newptr;
147 
148     newptr = NULL;
149     if (stack == NULL) return NULL;	/* Shouldn't happen. . . */
150     for (stackptr = stack; stackptr->next; stackptr = stackptr->next);
151     for (; stackptr; stackptr = stackptr->last) {
152 	newstack = (struct tokstack *)CALLOC(1, sizeof(struct tokstack));
153 	newstack->last = NULL;
154 	newstack->toktype = stackptr->toktype;
155 	switch (stackptr->toktype) {
156 	    case TOK_STRING:
157 		newstack->data.string = strsave(stackptr->data.string);
158 		break;
159 	    default:
160 		newstack->data.dvalue = stackptr->data.dvalue;
161 		break;
162 	}
163 	newstack->next = newptr;
164 	if (newptr) newptr->last = newstack;
165 	newptr = newstack;
166     }
167     return newptr;
168 }
169 
170 /*--------------------------------------------------------------*/
171 /* Get a value from a token by converting a string, potentially	*/
172 /* using unit suffixes, into a double floating-point value.	*/
173 /* Return the value in "dval", and return a result of 1 if the	*/
174 /* value was successfully converted, 0 if there was no value to	*/
175 /* convert (empty string), and -1 if unable to convert the	*/
176 /* string to a value (e.g., unknown parameter name).		*/
177 /*								*/
178 /* Inheritance is taken from "parprops" (instanced property	*/
179 /* values) if available, and from parent->propdict if not, or	*/
180 /* if the property is not instanced.				*/
181 /*--------------------------------------------------------------*/
182 
TokGetValue(char * estr,struct nlist * parent,struct objlist * parprops,int glob,double * dval)183 int TokGetValue(char *estr, struct nlist *parent, struct objlist *parprops,
184 		int glob, double *dval)
185 {
186     struct property *kl = NULL;
187     struct valuelist *kv;
188     int i, result;
189 
190     if (*estr == '\0') return 0;
191 
192     /* Grab the last numerical value */
193 
194     if (StringIsValue(estr)) {
195 	result = ConvertStringToFloat(estr, dval);
196 	if (result == 1) return 1;
197     }
198 
199     /* No numerical value found.  Try substituting parameters */
200 
201     if (glob == TRUE) {
202 	/* Check global parameters */
203 	kl = (struct property *)HashLookup(estr, &spiceparams);
204 	if (kl != NULL) {
205 	    result = ConvertStringToFloat(kl->pdefault.string, dval);
206 	    return ((result == 0) ? -1 : 1);
207 	}
208     }
209 
210     /* Check local instanced parameters */
211     result = 0;
212     if ((parprops != NULL) && (parprops->type == PROPERTY)) {
213 	for (i = 0; ; i++) {
214 	    kv = &(parprops->instance.props[i]);
215 	    if (kv->type == PROP_ENDLIST) break;
216 	    else if ((*matchfunc)(estr, kv->key)) {
217 		switch (kv->type) {
218 		    case PROP_STRING:
219 			result = ConvertStringToFloat(kv->value.string, dval);
220 			break;
221 		    case PROP_DOUBLE:
222 		    case PROP_VALUE:
223 			*dval = kv->value.dval;
224 			result = 1;
225 			break;
226 		    case PROP_INTEGER:
227 			*dval = (double)kv->value.ival;
228 			result = 1;
229 			break;
230 		}
231 		break;
232 	    }
233 	}
234     }
235 
236     /* Check local parent parameters */
237     if (result == 0) {
238 	kl = (struct property *)HashLookup(estr, &(parent->propdict));
239 	if (kl != NULL) {
240 	    switch(kl->type) {
241 		case PROP_STRING:
242 		    if (kl->pdefault.string != NULL)
243 			result = ConvertStringToFloat(kl->pdefault.string, dval);
244 		    break;
245 		case PROP_DOUBLE:
246 		case PROP_VALUE:
247 		    *dval = kl->pdefault.dval;
248 		    result = 1;
249 		    break;
250 		case PROP_INTEGER:
251 		    *dval = (double)kl->pdefault.ival;
252 		    result = 1;
253 		    break;
254 	    }
255 	}
256     }
257     return ((result == 0) ? -1 : 1);
258 }
259 
260 /*--------------------------------------------------------------*/
261 /* Work through the property list of an instance, looking for	*/
262 /* properties that are marked as expressions.  For each 	*/
263 /* expression, parse and attempt to reduce to a simpler		*/
264 /* expression, preferably a single value.  "glob" is TRUE when	*/
265 /* reading in a netlist, and substitutions should be made from	*/
266 /* the global parameter list.  "glob" is FALSE when elaborating	*/
267 /* the netlist, and substitutions should be made from the	*/
268 /* property list of the parent.	 If an expression resolves to a	*/
269 /* single value, then replace the property type.		*/
270 /*--------------------------------------------------------------*/
271 
ReduceOneExpression(struct valuelist * kv,struct objlist * parprops,struct nlist * parent,int glob)272 int ReduceOneExpression(struct valuelist *kv, struct objlist *parprops,
273         struct nlist *parent, int glob) {
274 
275     struct tokstack *expstack, *stackptr, *lptr, *nptr;
276     struct property *kl = NULL;
277     char *estr, *tstr, *sstr;
278     int toktype, functype, result, modified, numlast, savetok;
279     double dval;
280 
281     if (kv->type == PROP_EXPRESSION)
282 	expstack = kv->value.stack;
283 
284     else if (kv->type == PROP_STRING) {
285 
286 	/* Compile the string into an expression stack */
287 	expstack = NULL;
288 	estr = kv->value.string;
289 	tstr = estr;
290 
291 	numlast = 0;
292 	while (*tstr != '\0') {
293 	    switch(*tstr) {
294 
295 		case '+':
296 		    if (numlast == 0) {
297 			/* This is part of a number */
298 			dval = strtod(estr, &sstr);
299 			if (sstr > estr && sstr > tstr) {
300 		            tstr = sstr - 1;
301 		            numlast = 1;
302 			}
303 			break;
304 		    }
305 		    /* Not a number, so must be arithmetic */
306 		    *tstr = '\0';
307 		    result = TokGetValue(estr, parent, parprops, glob, &dval);
308 		    if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack);
309 		    else if (result == -1) PushTok(TOK_STRING, estr, &expstack);
310 		    PushTok(TOK_PLUS, NULL, &expstack);
311 		    estr = tstr + 1;
312 		    numlast = 0;
313 		    break;
314 
315 		case '-':
316 		    if (numlast == 0) {
317 			/* This is part of a number */
318 			dval = strtod(estr, &sstr);
319 			if (sstr > estr && sstr > tstr) {
320 			    tstr = sstr - 1;
321 			    numlast = 1;
322 			}
323 			break;
324 		    }
325 		    /* Not a number, so must be arithmetic */
326 		    *tstr = '\0';
327 		    result = TokGetValue(estr, parent, parprops, glob, &dval);
328 		    if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack);
329 		    else if (result == -1) PushTok(TOK_STRING, estr, &expstack);
330 		    PushTok(TOK_MINUS, NULL, &expstack);
331 		    estr = tstr + 1;
332 		    numlast = 0;
333 		    break;
334 
335 		case '1': case '2': case '3': case '4': case '5':
336 		case '6': case '7': case '8': case '9': case '0':
337 		    /* Numerical value.  Use strtod() to capture */
338 		    if (numlast == 1) break;
339 		    dval = strtod(estr, &sstr);
340 		    if (sstr > estr && sstr > tstr) {
341 			tstr = sstr - 1;
342 			numlast = 1;
343 		    }
344 		    break;
345 
346 		case '/':
347 		    *tstr = '\0';
348 		    result = TokGetValue(estr, parent, parprops, glob, &dval);
349 		    if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack);
350 		    else if (result == -1) PushTok(TOK_STRING, estr, &expstack);
351 		    PushTok(TOK_DIVIDE, NULL, &expstack);
352 		    estr = tstr + 1;
353 		    numlast = 0;
354 		    break;
355 
356 		case '*':
357 		    *tstr = '\0';
358 		    result = TokGetValue(estr, parent, parprops, glob, &dval);
359 		    if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack);
360 		    else if (result == -1) PushTok(TOK_STRING, estr, &expstack);
361 		    PushTok(TOK_MULTIPLY, NULL, &expstack);
362 		    estr = tstr + 1;
363 		    numlast = 0;
364 		    break;
365 
366 		case '(':
367 		    *tstr = '\0';
368 
369 		    /* Check for predefined function keywords */
370 
371 		    if (!strcmp(estr, "IF")) {
372 			PushTok(TOK_FUNC_IF, NULL, &expstack);
373 		    }
374 		    else {
375 		        /* Treat as a parenthetical grouping */
376 
377 		        result = TokGetValue(estr, parent, parprops,
378 				glob, &dval);
379 		        if (result == 1)
380 			    PushTok(TOK_DOUBLE, &dval, &expstack);
381 		        else if (result == -1)
382 			    PushTok(TOK_STRING, estr, &expstack);
383 		        PushTok(TOK_FUNC_OPEN, NULL, &expstack);
384 		    }
385 		    estr = tstr + 1;
386 		    numlast = 0;
387 		    break;
388 
389 		case ')':
390 		    *tstr = '\0';
391 
392 		    if (expstack == NULL) break;
393 		    savetok = expstack->toktype;
394 
395 		    result = TokGetValue(estr, parent, parprops, glob, &dval);
396 		    if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack);
397 		    else if (result == -1) PushTok(TOK_STRING, estr, &expstack);
398 
399 		    switch (savetok) {
400 			case TOK_FUNC_THEN:
401 			    PushTok(TOK_FUNC_ELSE, NULL, &expstack);
402 			    break;
403 			default:
404 			    PushTok(TOK_FUNC_CLOSE, NULL, &expstack);
405 			    break;
406 		    }
407 		    numlast = 1;
408 		    estr = tstr + 1;
409 		    break;
410 
411 		case '\'':
412 		    *tstr = '\0';
413 		    result = TokGetValue(estr, parent, parprops, glob, &dval);
414 		    if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack);
415 		    else if (result == -1) PushTok(TOK_STRING, estr, &expstack);
416 		    PushTok(TOK_SGL_QUOTE, NULL, &expstack);
417 		    estr = tstr + 1;
418 		    numlast = 0;
419 		    break;
420 
421 		case '"':
422 		    *tstr = '\0';
423 		    result = TokGetValue(estr, parent, parprops, glob, &dval);
424 		    if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack);
425 		    else if (result == -1) PushTok(TOK_STRING, estr, &expstack);
426 		    PushTok(TOK_DBL_QUOTE, NULL, &expstack);
427 		    estr = tstr + 1;
428 		    numlast = 0;
429 		    break;
430 
431 		case '{':
432 		    *tstr = '\0';
433 		    result = TokGetValue(estr, parent, parprops, glob, &dval);
434 		    if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack);
435 		    else if (result == -1) PushTok(TOK_STRING, estr, &expstack);
436 		    PushTok(TOK_GROUP_OPEN, NULL, &expstack);
437 		    estr = tstr + 1;
438 		    numlast = 0;
439 		    break;
440 
441 		case '}':
442 		    *tstr = '\0';
443 		    result = TokGetValue(estr, parent, parprops, glob, &dval);
444 		    if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack);
445 		    PushTok(TOK_GROUP_CLOSE, NULL, &expstack);
446 		    estr = tstr + 1;
447 		    numlast = 1;
448 		    break;
449 
450 		case '!':
451 		    if (*(tstr + 1) == '=') {
452 			*tstr = '\0';
453 			result = TokGetValue(estr, parent, parprops,
454 				glob, &dval);
455 			if (result == 1)
456 			    PushTok(TOK_DOUBLE, &dval, &expstack);
457 			else if (result == -1)
458 			    PushTok(TOK_STRING, estr, &expstack);
459 			PushTok(TOK_NE, NULL, &expstack);
460 		    }
461 		    numlast = 0;
462 		    break;
463 
464 		case '=':
465 		    if (*(tstr + 1) == '=') {
466  			*tstr = '\0';
467  			result = TokGetValue(estr, parent, parprops,
468 					glob, &dval);
469  			if (result == 1)
470 			    PushTok(TOK_DOUBLE, &dval, &expstack);
471  			else if (result == -1)
472 			    PushTok(TOK_STRING, estr, &expstack);
473 			PushTok(TOK_EQ, NULL, &expstack);
474 			numlast = 0;
475 		    }
476 		    break;
477 
478 		case '>':
479 		    *tstr = '\0';
480 		    result = TokGetValue(estr, parent, parprops, glob, &dval);
481 		    if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack);
482 		    else if (result == -1) PushTok(TOK_STRING, estr, &expstack);
483 
484 		    if (*(tstr + 1) == '=') {
485 			PushTok(TOK_GE, NULL, &expstack);
486 			tstr++;
487 		    }
488 		    else
489 			PushTok(TOK_GT, NULL, &expstack);
490 		    estr = tstr + 1;
491 		    numlast = 0;
492 		    break;
493 
494 		case '<':
495 		    *tstr = '\0';
496 		    result = TokGetValue(estr, parent, parprops, glob, &dval);
497 		    if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack);
498 		    else if (result == -1) PushTok(TOK_STRING, estr, &expstack);
499 
500 		    if (*(tstr + 1) == '=') {
501 			PushTok(TOK_LE, NULL, &expstack);
502 			tstr++;
503 		    }
504 		    else
505 			PushTok(TOK_LT, NULL, &expstack);
506 		    estr = tstr + 1;
507 		    numlast = 0;
508 		    break;
509 
510 		case ',':
511 		    *tstr = '\0';
512 		    result = TokGetValue(estr, parent, parprops, glob, &dval);
513 		    if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack);
514 		    else if (result == -1) PushTok(TOK_STRING, estr, &expstack);
515 		    if (expstack == NULL) break;
516 		    lptr = expstack;
517 		    while (lptr->next) {
518 			lptr = lptr->next;
519 			if (lptr->toktype == TOK_FUNC_THEN) {
520 			    PushTok(TOK_FUNC_ELSE, NULL, &expstack);
521 			    break;
522 			}
523 			else if (lptr->toktype == TOK_FUNC_IF) {
524 			    PushTok(TOK_FUNC_THEN, NULL, &expstack);
525 			    break;
526 			}
527 		    }
528 		    estr = tstr + 1;
529 		    numlast = 0;
530 		    break;
531 
532 		default:
533 		    break;
534 	    }
535 	    tstr++;
536 	}
537 	result = TokGetValue(estr, parent, parprops, glob, &dval);
538 	if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack);
539 	else if (result == -1) PushTok(TOK_STRING, estr, &expstack);
540 
541 	FREE(kv->value.string);
542 	kv->value.stack = expstack;
543 	kv->type = PROP_EXPRESSION;
544     }
545 
546     /* Find the beginning of the expression, which is the bottom of
547      * the stack.
548      */
549 
550     for (stackptr = kv->value.stack; stackptr != NULL &&
551 			stackptr->next != NULL; stackptr = stackptr->next);
552 
553     /* For each pass, start at the bottom and work forward. */
554     expstack = stackptr;
555 
556     modified = 1;
557     while (modified) {
558 	double dval1, dval2;
559 
560 	modified = 0;
561 
562 	// Reduce conditionals
563 
564 	for (stackptr = expstack; stackptr != NULL; stackptr = stackptr->last) {
565 	    switch (stackptr->toktype) {
566 		case TOK_LE:
567 		case TOK_LT:
568 		case TOK_GE:
569 		case TOK_GT:
570 		case TOK_EQ:
571 		case TOK_NE:
572 		    lptr = stackptr->last;
573 		    nptr = stackptr->next;
574 		    if (lptr && nptr && (lptr->toktype == TOK_DOUBLE) &&
575 					(nptr->toktype == TOK_DOUBLE)) {
576 
577 			switch (stackptr->toktype) {
578 			    case TOK_LE:
579 				if (nptr->data.dvalue <= lptr->data.dvalue) {
580 				    stackptr->data.dvalue = 1.0;
581 				}
582 				else {
583 				    stackptr->data.dvalue = 0.0;
584 				}
585 				break;
586 			    case TOK_LT:
587 				if (nptr->data.dvalue < lptr->data.dvalue) {
588 				    stackptr->data.dvalue = 1.0;
589 				}
590 				else {
591 				    stackptr->data.dvalue = 0.0;
592 				}
593 				break;
594 			    case TOK_GE:
595 				if (nptr->data.dvalue >= lptr->data.dvalue) {
596 				    stackptr->data.dvalue = 1.0;
597 				}
598 				else {
599 				    stackptr->data.dvalue = 0.0;
600 				}
601 				break;
602 			    case TOK_GT:
603 				if (nptr->data.dvalue > lptr->data.dvalue) {
604 				    stackptr->data.dvalue = 1.0;
605 				}
606 				else {
607 				    stackptr->data.dvalue = 0.0;
608 				}
609 				break;
610 			    case TOK_EQ:
611 				if (nptr->data.dvalue == lptr->data.dvalue) {
612 				    stackptr->data.dvalue = 1.0;
613 				}
614 				else {
615 				    stackptr->data.dvalue = 0.0;
616 				}
617 				break;
618 			    case TOK_NE:
619 				if (nptr->data.dvalue != lptr->data.dvalue) {
620 				    stackptr->data.dvalue = 1.0;
621 				}
622 				else {
623 				    stackptr->data.dvalue = 0.0;
624 				}
625 				break;
626 			}
627 			modified = 1;
628 			stackptr->toktype = TOK_DOUBLE;
629 			stackptr->last = lptr->last;
630 			if (lptr->last) lptr->last->next = stackptr;
631 			else kv->value.stack = stackptr;
632 			stackptr->next = nptr->next;
633 			if (nptr->next) nptr->next->last = stackptr;
634 			if (expstack == nptr) expstack = stackptr;
635 
636 			FREE(nptr);
637 			FREE(lptr);
638 		    }
639 	    }
640 	}
641 
642 	// Reduce IF(a,b,c)
643 
644 	for (stackptr = expstack; stackptr != NULL; stackptr = stackptr->last) {
645 	    struct tokstack *ifptr, *thenptr;
646 	    if (stackptr->toktype == TOK_FUNC_IF) {
647 		ifptr = stackptr->last;
648 		if (ifptr->toktype == TOK_DOUBLE) {
649 		    stackptr->toktype = TOK_FUNC_OPEN;
650 		    if (ifptr->data.dvalue == 0.0) {
651 			/* Keep ELSE value, remove IF and THEN */
652 			for (thenptr = ifptr; thenptr->toktype !=
653 					TOK_FUNC_ELSE; ) {
654 			    lptr = thenptr->last;
655 			    nptr = thenptr->next;
656 			    lptr->next = nptr;
657 			    nptr->last = lptr;
658 			    if (thenptr->toktype == TOK_STRING)
659 				FREE(thenptr->data.string);
660 			    FREE(thenptr);
661 			    thenptr = lptr;
662 			}
663 			/* Free the TOK_FUNC_ELSE record */
664 			lptr = thenptr->last;
665 			nptr = thenptr->next;
666 			lptr->next = nptr;
667 			nptr->last = lptr;
668 			FREE(thenptr);
669 			modified = 1;
670 		    }
671 		    else {
672 			/* Keep THEN value, remove IF and ELSE */
673 			/* Free the conditional result value record */
674 			lptr = ifptr->last;
675 			nptr = ifptr->next;
676 			lptr->next = nptr;
677 			nptr->last = lptr;
678 			FREE(ifptr);
679 			thenptr = nptr;
680 
681 			/* Free the TOK_FUNC_THEN record */
682 			lptr = thenptr->last;
683 			nptr = thenptr->next;
684 			lptr->next = nptr;
685 			nptr->last = lptr;
686 			FREE(thenptr);
687 
688 			/* Free to end of IF block */
689 			for (thenptr = nptr->last; thenptr->toktype !=
690 					TOK_FUNC_CLOSE; ) {
691 			    lptr = thenptr->last;
692 			    nptr = thenptr->next;
693 			    lptr->next = nptr;
694 			    nptr->last = lptr;
695 			    if (thenptr->toktype == TOK_STRING)
696 				FREE(thenptr->data.string);
697 			    FREE(thenptr);
698 			    thenptr = lptr;
699 			}
700 			modified = 1;
701 		    }
702 		}
703 	    }
704 	}
705 
706 	// Reduce (value) * (value) and (value) / (value)
707 
708 	for (stackptr = expstack; stackptr != NULL; stackptr = stackptr->last) {
709 	    switch (stackptr->toktype) {
710 		case TOK_MULTIPLY:
711 		case TOK_DIVIDE:
712 		    lptr = stackptr->last;
713 		    nptr = stackptr->next;
714 		    if (lptr && nptr && (lptr->toktype == TOK_DOUBLE) &&
715 					(nptr->toktype == TOK_DOUBLE)) {
716 
717 			if (stackptr->toktype == TOK_MULTIPLY)
718 			    stackptr->data.dvalue = nptr->data.dvalue *
719 					lptr->data.dvalue;
720 			else
721 			    stackptr->data.dvalue = nptr->data.dvalue /
722 					lptr->data.dvalue;
723 
724 			modified = 1;
725 			stackptr->toktype = TOK_DOUBLE;
726 			stackptr->last = lptr->last;
727 			if (lptr->last) lptr->last->next = stackptr;
728 			else kv->value.stack = stackptr;
729 			stackptr->next = nptr->next;
730 			if (nptr->next) nptr->next->last = stackptr;
731 			if (expstack == nptr) expstack = stackptr;
732 
733 			FREE(nptr);
734 			FREE(lptr);
735 		    }
736 	    }
737 	}
738 
739 	// Reduce (value) + (value) and (value) - (value)
740 
741 	for (stackptr = expstack; stackptr != NULL; stackptr = stackptr->last) {
742 	    switch (stackptr->toktype) {
743 		case TOK_PLUS:
744 		case TOK_MINUS:
745 		    lptr = stackptr->last;
746 		    nptr = stackptr->next;
747 		    if (lptr && nptr && (lptr->toktype == TOK_DOUBLE) &&
748 					(nptr->toktype == TOK_DOUBLE)) {
749 
750 			if (stackptr->toktype == TOK_PLUS)
751 			    stackptr->data.dvalue = nptr->data.dvalue +
752 					lptr->data.dvalue;
753 			else
754 			    stackptr->data.dvalue = nptr->data.dvalue -
755 					lptr->data.dvalue;
756 
757 			modified = 1;
758 			stackptr->toktype = TOK_DOUBLE;
759 			stackptr->last = lptr->last;
760 			if (lptr->last) lptr->last->next = stackptr;
761 			else kv->value.stack = stackptr;
762 			stackptr->next = nptr->next;
763 			if (nptr->next) nptr->next->last = stackptr;
764 			if (expstack == nptr) expstack = stackptr;
765 
766 			FREE(nptr);
767 			FREE(lptr);
768 		    }
769 	    }
770 	}
771 
772 	// Reduce {value}, (value), and 'value'
773 
774 	for (stackptr = expstack; stackptr != NULL; stackptr = stackptr->last) {
775 	    switch (stackptr->toktype) {
776 		case TOK_DOUBLE:
777 		    lptr = stackptr->last;
778 		    nptr = stackptr->next;
779 		    if (lptr && nptr &&
780 				(((nptr->toktype == TOK_FUNC_OPEN) &&
781 					(lptr->toktype == TOK_FUNC_CLOSE)) ||
782 				((nptr->toktype == TOK_GROUP_OPEN) &&
783 					(lptr->toktype == TOK_GROUP_CLOSE)) ||
784 				((nptr->toktype == TOK_DBL_QUOTE) &&
785 					(lptr->toktype == TOK_DBL_QUOTE)) ||
786 				((nptr->toktype == TOK_SGL_QUOTE) &&
787 					(lptr->toktype == TOK_SGL_QUOTE)))) {
788 
789 			modified = 1;
790 			stackptr->last = lptr->last;
791 			if (lptr->last) lptr->last->next = stackptr;
792 			else kv->value.stack = stackptr;
793 			stackptr->next = nptr->next;
794 			if (nptr->next) nptr->next->last = stackptr;
795 			if (expstack == nptr) expstack = stackptr;
796 
797 			FREE(nptr);
798 			FREE(lptr);
799 		    }
800 		    break;
801 	    }
802 	}
803 
804 	// Replace value if string can be substituted with a number
805 
806 	for (stackptr = expstack; stackptr != NULL; stackptr = stackptr->last) {
807 	    switch (stackptr->toktype) {
808 		case TOK_STRING:
809 		    result = TokGetValue(stackptr->data.string, parent,
810 				parprops, glob, &dval);
811 		    if (result == 1) {
812 			stackptr->toktype = TOK_DOUBLE;
813 			FREE(stackptr->data.string);
814 			stackptr->data.dvalue = dval;
815 			modified = 1;
816 		    }
817 		    break;
818 	    }
819 	}
820     }
821 
822     // Replace the expression with the reduced expression or
823     // value.
824 
825     expstack = kv->value.stack;	    // Now pointing at the end
826 
827     if (expstack && expstack->next == NULL) {
828 	if (expstack->toktype == TOK_DOUBLE) {
829 	    kv->type = PROP_DOUBLE;
830 	    kv->value.dval = expstack->data.dvalue;
831 	}
832 	else if (expstack->toktype == TOK_STRING) {
833 	    kv->type = PROP_STRING;
834 	    kv->value.string = strsave(expstack->data.string);
835 	}
836     }
837     else {
838 	// Still an expression;  do nothing
839     }
840 
841     // Free up the stack if it's not being used
842 
843     if (kv->type != PROP_EXPRESSION)
844     {
845 	while (expstack != NULL) {
846 	    nptr = expstack->next;
847 	    if (expstack->toktype == TOK_STRING)
848 		FREE(expstack->data.string);
849 	    FREE(expstack);
850 	    expstack = nptr;
851 	}
852     }
853 
854     return 0;
855 }
856 
857 /*----------------------------------------------------------------------*/
858 /* Wrapper around the above routine, to reduce all expressions in a	*/
859 /* property list.							*/
860 /*----------------------------------------------------------------------*/
861 
ReduceExpressions(struct objlist * instprop,struct objlist * parprops,struct nlist * parent,int glob)862 int ReduceExpressions(struct objlist *instprop, struct objlist *parprops,
863         struct nlist *parent, int glob) {
864 
865     struct valuelist *kv;
866     int i;
867 
868     if (instprop == NULL) return 0;	// Nothing to do
869     if (instprop->type != PROPERTY) return -1;	// Shouldn't happen
870 
871     for (i = 0;; i++) {
872 	kv = &(instprop->instance.props[i]);
873 	if (kv->type == PROP_ENDLIST) break;
874 	switch (kv->type)
875 	{
876 	    case PROP_INTEGER:
877 	    case PROP_DOUBLE:
878 	    case PROP_VALUE:
879 		continue;
880 
881 	    default:
882 		ReduceOneExpression(kv, parprops, parent, glob);
883 		break;
884 	}
885     }
886     return 0;
887 }
888 
889 /*----------------------------------------------------------------------*/
890 /* Set/clear the flag bit COMB_NO_PARALLEL on all cells.  Note that the	*/
891 /* function is called with value for enabling combine parallel, so	*/
892 /* value TRUE means clear bit, value FALSE means set bit.		*/
893 /*----------------------------------------------------------------------*/
894 
SetParallelCombineFlag(struct hashlist * p,void * clientdata)895 struct nlist *SetParallelCombineFlag(struct hashlist *p, void *clientdata)
896 {
897   struct nlist *ptr;
898   int *value = (int *)clientdata;
899 
900   ptr = (struct nlist *)(p->ptr);
901   if (*value == TRUE)
902      ptr->flags &= (~COMB_NO_PARALLEL);
903   else
904      ptr->flags |= COMB_NO_PARALLEL;
905 
906   return NULL;	/* NULL keeps search alive */
907 }
908 
SetParallelCombine(int value)909 void SetParallelCombine(int value)
910 {
911   ClearDumpedList();
912   RecurseCellHashTable2(SetParallelCombineFlag, (void *)(&value));
913 }
914 
915 /*----------------------------------------------------------------------*/
916 /* Same as above, for series (here for symmetry, although "property	*/
917 /* series all" would be an odd command to issue, and "property series	*/
918 /* none" is the default, so not needed).				*/
919 /*----------------------------------------------------------------------*/
920 
SetSeriesCombineFlag(struct hashlist * p,void * clientdata)921 struct nlist *SetSeriesCombineFlag(struct hashlist *p, void *clientdata)
922 {
923   struct nlist *ptr;
924   int *value = (int *)clientdata;
925 
926   ptr = (struct nlist *)(p->ptr);
927   if (*value == TRUE)
928      ptr->flags &= (~COMB_SERIES);
929   else
930      ptr->flags |= COMB_SERIES;
931 
932   return NULL;	/* NULL keeps search alive */
933 }
934 
SetSeriesCombine(int value)935 void SetSeriesCombine(int value)
936 {
937   ClearDumpedList();
938   RecurseCellHashTable2(SetSeriesCombineFlag, (void *)(&value));
939 }
940 
941 /*----------------------------------------------------------------------*/
942 /* Delete a property from the master cell record.			*/
943 /*----------------------------------------------------------------------*/
944 
945 int
PropertyDelete(char * name,int fnum,char * key)946 PropertyDelete(char *name, int fnum, char *key)
947 {
948     struct property *kl = NULL;
949     struct nlist *tc;
950     int result;
951 
952     if ((fnum == -1) && (Circuit1 != NULL) && (Circuit2 != NULL)) {
953 	result = PropertyDelete(name, Circuit1->file, key);
954 	result = PropertyDelete(name, Circuit2->file, key);
955 	return result;
956     }
957 
958     tc = LookupCellFile(name, fnum);
959     if (tc == NULL) {
960 	Printf("No device %s found for PropertyDelete()\n", name);
961 	return -1;
962     }
963     else if (key == NULL) {
964 
965 	/* key == NULL means delete all properties. */
966 
967 	RecurseHashTable(&(tc->propdict), freeprop);
968 	HashKill(&(tc->propdict));
969 	InitializeHashTable(&(tc->propdict), OBJHASHSIZE);
970     }
971     else {
972 	kl = (struct property *)HashLookup(key, &(tc->propdict));
973 	if (kl != NULL) {
974 	    if (kl->type == PROP_STRING || kl->type == PROP_EXPRESSION)
975 		FREE(kl->pdefault.string);
976 	    FREE(kl->key);
977 	    HashDelete(key, &(tc->propdict));
978 	}
979 	else {
980 	    Printf("No property %s found for device %s\n", key, name);
981 	    return -1;
982 	}
983     }
984     return 0;
985 }
986 
987 /*----------------------------------------------------------------------*/
988 /* Set the tolerance of a property in the master cell record.		*/
989 /*----------------------------------------------------------------------*/
990 
991 int
PropertyTolerance(char * name,int fnum,char * key,int ival,double dval)992 PropertyTolerance(char *name, int fnum, char *key, int ival, double dval)
993 {
994     struct property *kl = NULL;
995     struct nlist *tc;
996     int result;
997 
998     if ((fnum == -1) && (Circuit1 != NULL) && (Circuit2 != NULL)) {
999 	result = PropertyTolerance(name, Circuit1->file, key, ival, dval);
1000 	result = PropertyTolerance(name, Circuit2->file, key, ival, dval);
1001 	return result;
1002     }
1003 
1004     tc = LookupCellFile(name, fnum);
1005     if (tc == NULL) {
1006 	Printf("No device %s found for PropertyTolerance()\n", name);
1007 	return -1;
1008     }
1009 
1010     kl = (struct property *)HashLookup(key, &(tc->propdict));
1011     if (kl == NULL) {
1012 	Printf("No property %s found for device %s\n", key, name);
1013 	return -1;
1014     }
1015     else {
1016         switch (kl->type) {
1017 	    case PROP_DOUBLE:
1018 	    case PROP_VALUE:
1019 	    case PROP_STRING:
1020 		kl->slop.dval = dval;
1021 		break;
1022 	    case PROP_INTEGER:
1023 	    case PROP_EXPRESSION:
1024 		kl->slop.ival = ival;
1025 		break;
1026 	}
1027     }
1028     return 0;
1029 }
1030 
1031 /*----------------------------------------------------------------------*/
1032 /* Set the merge type of a property in the master cell record.		*/
1033 /*----------------------------------------------------------------------*/
1034 
1035 int
PropertyMerge(char * name,int fnum,char * key,int merge_type,int merge_mask)1036 PropertyMerge(char *name, int fnum, char *key, int merge_type, int merge_mask)
1037 {
1038     struct property *kl = NULL;
1039     struct nlist *tc;
1040     int result;
1041 
1042     if ((fnum == -1) && (Circuit1 != NULL) && (Circuit2 != NULL)) {
1043 	result = PropertyMerge(name, Circuit1->file, key, merge_type, merge_mask);
1044 	result = PropertyMerge(name, Circuit2->file, key, merge_type, merge_mask);
1045 	return result;
1046     }
1047 
1048     tc = LookupCellFile(name, fnum);
1049     if (tc == NULL) {
1050 	Printf("No device %s found for PropertyTolerance()\n", name);
1051 	return -1;
1052     }
1053 
1054     kl = (struct property *)HashLookup(key, &(tc->propdict));
1055     if (kl == NULL) {
1056 	Printf("No property %s found for device %s\n", key, name);
1057 	return -1;
1058     }
1059     else {
1060 	kl->merge &= ~merge_mask;
1061 	kl->merge |= merge_type;
1062     }
1063     return 0;
1064 }
1065 
1066 /*----------------------------------------------------------------------*/
1067 /* Add a new value property to the indicated cell			*/
1068 /* Value properties are used for resistors and capacitors in SPICE	*/
1069 /* netlists where the value is not syntactically like other properties.	*/
1070 /* For the purpose of netgen, it is treated like a PROP_DOUBLE except	*/
1071 /* when reading and writing netlist files.				*/
1072 /*----------------------------------------------------------------------*/
1073 
PropertyValue(char * name,int fnum,char * key,double slop,double pdefault)1074 struct property *PropertyValue(char *name, int fnum, char *key,
1075 		double slop, double pdefault)
1076 {
1077    struct property *kl = NULL;
1078    struct nlist *tc;
1079 
1080    if ((fnum == -1) && (Circuit1 != NULL) && (Circuit2 != NULL)) {
1081       PropertyValue(name, Circuit1->file, key, slop, pdefault);
1082       PropertyValue(name, Circuit2->file, key, slop, pdefault);
1083       return NULL;
1084    }
1085 
1086    tc = LookupCellFile(name, fnum);
1087    if (tc == NULL)
1088       Printf("No device %s found for PropertyValue()\n", name);
1089    else if ((kl = (struct property *)HashLookup(key, &(tc->propdict))) != NULL) {
1090       Printf("Device %s already has property named \"%s\"\n", name, key);
1091    }
1092    else {
1093       kl = NewProperty();
1094       kl->key = strsave(key);
1095       kl->idx = 0;
1096       kl->merge = MERGE_NONE;
1097       kl->type = PROP_VALUE;
1098       kl->slop.dval = slop;
1099       kl->pdefault.dval = pdefault;
1100       HashPtrInstall(kl->key, kl, &(tc->propdict));
1101    }
1102    return kl;
1103 }
1104 
1105 /*----------------------------------------------------------------------*/
1106 /* Add a new double-valued property key to the current cell		*/
1107 /*----------------------------------------------------------------------*/
1108 
PropertyDouble(char * name,int fnum,char * key,double slop,double pdefault)1109 struct property *PropertyDouble(char *name, int fnum, char *key,
1110 			double slop, double pdefault)
1111 {
1112    struct property *kl = NULL;
1113    struct nlist *tc;
1114 
1115    if ((fnum == -1) && (Circuit1 != NULL) && (Circuit2 != NULL)) {
1116       PropertyDouble(name, Circuit1->file, key, slop, pdefault);
1117       PropertyDouble(name, Circuit2->file, key, slop, pdefault);
1118       return NULL;
1119    }
1120 
1121    tc = LookupCellFile(name, fnum);
1122    if (tc == NULL)
1123       Printf("No device %s found for PropertyDouble()\n", name);
1124    else if ((kl = (struct property *)HashLookup(key, &(tc->propdict))) != NULL) {
1125       Printf("Device %s already has property named \"%s\"\n", name, key);
1126    }
1127    else {
1128       kl = NewProperty();
1129       kl->key = strsave(key);
1130       kl->idx = 0;
1131       kl->merge = MERGE_NONE;
1132       kl->type = PROP_DOUBLE;
1133       kl->slop.dval = slop;
1134       kl->pdefault.dval = pdefault;
1135       HashPtrInstall(kl->key, kl, &(tc->propdict));
1136    }
1137    return kl;
1138 }
1139 
1140 /*----------------------------------------------------------------------*/
1141 
PropertyInteger(char * name,int fnum,char * key,int slop,int pdefault)1142 struct property *PropertyInteger(char *name, int fnum, char *key,
1143 			int slop, int pdefault)
1144 {
1145    struct property *kl = NULL;
1146    struct nlist *tc;
1147 
1148    if ((fnum == -1) && (Circuit1 != NULL) && (Circuit2 != NULL)) {
1149       PropertyInteger(name, Circuit1->file, key, slop, pdefault);
1150       PropertyInteger(name, Circuit2->file, key, slop, pdefault);
1151       return NULL;
1152    }
1153 
1154    tc = LookupCellFile(name, fnum);
1155    if (tc == NULL)
1156       Printf("No device %s found for PropertyInteger()\n", name);
1157    else if ((kl = (struct property *)HashLookup(key, &(tc->propdict))) != NULL) {
1158       Printf("Device %s already has property named \"%s\"\n", name, key);
1159    }
1160    else {
1161       kl = NewProperty();
1162       kl->key = strsave(key);
1163       kl->idx = 0;
1164       kl->merge = MERGE_NONE;
1165       kl->type = PROP_INTEGER;
1166       kl->slop.ival = slop;
1167       kl->pdefault.ival = pdefault;
1168       HashPtrInstall(kl->key, kl, &(tc->propdict));
1169    }
1170    return kl;
1171 }
1172 
1173 /*----------------------------------------------------------------------*/
1174 
PropertyString(char * name,int fnum,char * key,double dval,char * pdefault)1175 struct property *PropertyString(char *name, int fnum, char *key, double dval,
1176 		char *pdefault)
1177 {
1178    struct property *kl = NULL;
1179    struct nlist *tc;
1180 
1181    if ((fnum == -1) && (Circuit1 != NULL) && (Circuit2 != NULL)) {
1182       PropertyString(name, Circuit1->file, key, dval, pdefault);
1183       PropertyString(name, Circuit2->file, key, dval, pdefault);
1184       return NULL;
1185    }
1186 
1187    tc = LookupCellFile(name, fnum);
1188    if (tc == NULL)
1189       Printf("No device %s found for PropertyString()\n", name);
1190    else if ((kl = (struct property *)HashLookup(key, &(tc->propdict))) != NULL) {
1191       Printf("Device %s already has property named \"%s\"\n", name, key);
1192    }
1193    else {
1194       kl = NewProperty();
1195       kl->key = strsave(key);
1196       kl->idx = 0;
1197       kl->merge = MERGE_NONE;
1198       kl->type = PROP_STRING;
1199       kl->slop.dval = dval;
1200       if (pdefault != NULL)
1201          kl->pdefault.string = strsave(pdefault);
1202       else
1203          kl->pdefault.string = NULL;
1204       HashPtrInstall(kl->key, kl, &(tc->propdict));
1205    }
1206    return kl;
1207 }
1208 
1209 /*----------------------------------------------------------------------*/
1210 /* Declare the element class of the current cell			*/
1211 /*----------------------------------------------------------------------*/
1212 
SetClass(unsigned char class)1213 void SetClass(unsigned char class)
1214 {
1215 	if (CurrentCell == NULL)
1216 	  Printf("No current cell for SetClass()\n");
1217 	else
1218 	  CurrentCell->class = class;
1219 }
1220 
1221 /*----------------------------------------------------------------------*/
1222 
ReopenCellDef(char * name,int fnum)1223 void ReopenCellDef(char *name, int fnum)
1224 {
1225   struct objlist *ob;
1226 
1227   if (Debug) Printf("Reopening cell definition: %s\n",name);
1228   GarbageCollect();
1229   if ((CurrentCell = LookupCellFile(name, fnum)) == NULL) {
1230     Printf("Undefined cell: %s\n", name);
1231     return;
1232   }
1233   /* cell exists, so append to the end of it */
1234   NextNode = 1;
1235   CurrentTail = CurrentCell->cell;
1236   for (ob = CurrentTail; ob != NULL; ob = ob->next) {
1237     CurrentTail = ob;
1238     if (ob->node >= NextNode) NextNode = ob->node + 1;
1239   }
1240 }
1241 
1242 /*----------------------------------------------------------------------*/
1243 
CellDef(char * name,int fnum)1244 void CellDef(char *name, int fnum)
1245 {
1246     struct nlist *np;
1247 
1248     if (Debug) Printf("Defining cell: %s\n",name);
1249     GarbageCollect();
1250     if ((CurrentCell = LookupCellFile(name, fnum)) != NULL) {
1251 	if (AddToExistingDefinition) {
1252 	    ReopenCellDef(name, fnum);
1253 	    return ;
1254 	}
1255 	else {
1256 	    Printf("Cell: %s exists already, and will be overwritten.\n", name);
1257 	    CellDelete(name, fnum);
1258 	}
1259     }
1260     /* install a new cell in lookup table (hashed) */
1261     np = InstallInCellHashTable(name, fnum);
1262     CurrentCell = LookupCellFile(name, fnum);
1263     CurrentCell->class = CLASS_SUBCKT;	/* default */
1264     CurrentCell->flags = (GlobalParallelNone) ? COMB_NO_PARALLEL : 0;
1265 
1266     LastPlaced = NULL;
1267     CurrentTail = NULL;
1268     FreeNodeNames(CurrentCell);
1269     NextNode = 1;
1270 
1271     // Mark cell as case insensitive if case insensitivity is in effect
1272     if (matchfunc == matchnocase) CurrentCell->flags |= CELL_NOCASE;
1273 }
1274 
1275 /*----------------------------------------------------------------------*/
1276 /* Return 0 if class 'name' is not being ignored (per the 'ignore'	*/
1277 /* command);  1 if it is ignored, and 2 if shorted instances should be	*/
1278 /* ignored.								*/
1279 /*----------------------------------------------------------------------*/
1280 
IsIgnored(char * name,int file)1281 int IsIgnored(char *name, int file)
1282 {
1283     struct IgnoreList *ilist;
1284     char *nptr = name;
1285 
1286     for (ilist = ClassIgnore; ilist; ilist = ilist->next)
1287     {
1288 	if ((file == -1) || (ilist->file == -1) || (file == ilist->file))
1289 	    if ((*matchfunc)(ilist->class, nptr))
1290 		return ilist->type;	/* IGNORE_CLASS or IGNORE_SHORTED */
1291     }
1292    return 0;
1293 }
1294 
1295 /*----------------------------------------------------------------------*/
1296 
Port(char * name)1297 void Port(char *name)
1298 {
1299 	struct objlist *tp;
1300 
1301 	if (Debug) Printf("   Defining port: %s\n",name);
1302 	if ((tp = GetObject()) == NULL) {
1303 	  perror("Failed GetObject in Port");
1304 	  return;
1305 	}
1306 	tp->type = PORT;  /* port type */
1307 	if (name == NULL) {
1308 	   // Name becomes "no pins" and shows up in the pin matching
1309 	   // output for both devices.
1310 	   tp->name = strsave("(no pins)");
1311 	   tp->model.port = PROXY;
1312 	}
1313 	else {
1314 	   tp->name = strsave(name);
1315 	   tp->model.port = PORT;
1316 	}
1317 	tp->instance.name = NULL;
1318 	tp->node = -1;  /* null node */
1319 	tp->next = NULL;
1320 	AddToCurrentCell (tp);
1321 }
1322 
1323 /*----------------------------------------------------------------------*/
1324 
CountPorts(char * name,int fnum)1325 int CountPorts(char *name, int fnum)
1326 {
1327     struct nlist *tc;
1328     struct objlist *ob;
1329     int ports = 0;
1330 
1331     tc = LookupCellFile(name, fnum);
1332     if (tc != NULL) {
1333 	for (ob = tc->cell; ob; ob = ob->next) {
1334 	    if (ob->type != PORT) break;
1335 	    ports++;
1336 	}
1337     }
1338     return ports;
1339 }
1340 
1341 /*----------------------------------------------------------------------*/
1342 
Node(char * name)1343 void Node(char *name)
1344 {
1345 	struct objlist *tp;
1346 
1347 	if (Debug) Printf("   Defining internal node: %s\n",name);
1348 	if ((tp = GetObject()) == NULL) {
1349 	  perror("Failed GetObject in Node");
1350 	  return;
1351 	}
1352 	tp->name = strsave(name);
1353 	tp->type = NODE;  /* internal node type */
1354 	tp->model.class = NULL;
1355 	tp->instance.name = NULL;
1356 	tp->flags = 0;
1357 	tp->node = -1;  /* null node */
1358 	tp->next = NULL;
1359 	AddToCurrentCell (tp);
1360 }
1361 
1362 /*----------------------------------------------------------------------*/
1363 
Global(char * name)1364 void Global(char *name)
1365 {
1366     struct objlist *tp;
1367 
1368     // Check if "name" is already in the current cell as a global node
1369     // or a port.  If it is, then we're done.  Otherwise, add "name" as
1370     // a new global in CurrentCell.
1371 
1372     for (tp = CurrentCell->cell; tp; tp = tp->next)
1373 	if (tp->type == GLOBAL || tp->type == UNIQUEGLOBAL || tp->type == PORT)
1374 	    if ((*matchfunc)(tp->name, name))
1375 		return;
1376 
1377     if (Debug) Printf("   Defining global node: %s\n",name);
1378     if ((tp = GetObject()) == NULL) {
1379 	perror("Failed GetObject in Global");
1380 	return;
1381     }
1382     tp->name = strsave(name);
1383     tp->type = GLOBAL;		/* internal node type */
1384     tp->model.class = NULL;
1385     tp->instance.name = NULL;
1386     tp->node = -1;		/* null node */
1387     tp->next = NULL;
1388     AddToCurrentCell (tp);
1389 }
1390 
1391 /*----------------------------------------------------------------------*/
1392 
UniqueGlobal(char * name)1393 void UniqueGlobal(char *name)
1394 {
1395 	struct objlist *tp;
1396 
1397 	if (Debug) Printf("   Defining unique global node: %s\n",name);
1398 	if ((tp = GetObject()) == NULL) {
1399 	  perror("Failed GetObject in UniqueGlobal");
1400 	  return;
1401 	}
1402 
1403 	tp->name = strsave(name);
1404 	tp->type = UNIQUEGLOBAL;  /* internal node type */
1405 	tp->model.class = NULL;
1406 	tp->instance.name = NULL;
1407 	tp->node = -1;  /* null node */
1408 	tp->next = NULL;
1409 	AddToCurrentCell (tp);
1410 }
1411 
1412 /*----------------------------------------------------------------------*/
1413 
Instance(char * model,char * instancename)1414 void Instance(char *model, char *instancename)
1415 {
1416   struct objlist *tp, *tp2;
1417   struct nlist *instanced_cell;
1418   int portnum;
1419   char tmpname[512], tmpname2[512];
1420   int firstobj, fnum;
1421 
1422   if (Debug) Printf("   Instance: %s of class: %s\n",
1423 		    instancename, model);
1424   if (CurrentCell == NULL) {
1425     Printf("No current cell for Instance(%s,%s)\n", model,instancename);
1426     return;
1427   }
1428   fnum = CurrentCell->file;
1429   instanced_cell = LookupCellFile(model, fnum);
1430   if (instanced_cell == NULL) {
1431     Printf("Attempt to instance undefined model '%s'\n", model);
1432     return;
1433   }
1434   /* class exists */
1435   instanced_cell->number++;		/* one more allocated */
1436   portnum = 1;
1437   firstobj = 1;
1438   for (tp2 = instanced_cell->cell; tp2 != NULL; tp2 = tp2->next)
1439     if (IsPort(tp2)) {
1440       /* it is a port */
1441       tp = GetObject();
1442       if (tp == NULL) {
1443 	perror("Failed GetObject in Instance()");
1444 	return;
1445       }
1446       strcpy(tmpname,instancename);
1447       strcat(tmpname,SEPARATOR);
1448       strcat(tmpname,tp2->name);
1449       tp->name = strsave(tmpname);
1450       tp->model.class = strsave(model);
1451       tp->instance.name = strsave(instancename);
1452       tp->type = portnum++;	/* instance type */
1453       tp->node = -1;		/* null node */
1454       tp->next = NULL;
1455       AddToCurrentCell (tp);
1456       if (firstobj) {
1457 	AddInstanceToCurrentCell(tp);
1458 	firstobj = 0;
1459       }
1460     }
1461   /* now run through list of new objects, processing global ports */
1462   for (tp2 = instanced_cell->cell; tp2 != NULL; tp2 = tp2->next) {
1463     /* check to see if it is a global port */
1464     if (tp2->type == GLOBAL) {
1465       if (Debug) Printf("   processing global port: %s\n",
1466 			tp2->name);
1467       strcpy(tmpname,instancename);
1468       strcat(tmpname,SEPARATOR);
1469       strcat(tmpname,tp2->name);
1470       /* see if element already exists */
1471       if (LookupObject(tp2->name,CurrentCell) != NULL)
1472 	join(tp2->name, tmpname);
1473       else {
1474 	/* define global node if not already there */
1475 	Global(tp2->name);
1476 	join(tp2->name, tmpname);
1477       }
1478     }
1479     else if (tp2->type == UNIQUEGLOBAL) {
1480       if (Debug) Printf("   processing unique global port: %s\n",
1481 			tp2->name);
1482       strcpy(tmpname,CurrentCell->name);
1483       strcat(tmpname,INSTANCE_DELIMITER);
1484       strcat(tmpname,instancename);
1485       strcat(tmpname,SEPARATOR);
1486       strcat(tmpname,tp2->name);
1487       /* make this element UniqueGlobal */
1488       UniqueGlobal(tmpname);
1489       strcpy(tmpname2,instancename);
1490       strcat(tmpname2,SEPARATOR);
1491       strcat(tmpname2,tp2->name);
1492       Connect(tmpname,tmpname2);
1493     }
1494   }
1495   /* now run through list of new objects, checking for shorted ports */
1496   for (tp2 = instanced_cell->cell; tp2 != NULL; tp2 = tp2->next) {
1497     /* check to see if it is a unique port */
1498     /* remember to NOT consider unconnected ports (node = -1)
1499        as being shorted out */
1500 
1501     if (IsPort(tp2)) {
1502       struct objlist *ob;
1503 
1504       ob = LookupObject(tp2->name, instanced_cell);
1505       if (ob->node != -1 && !(*matchfunc)(tp2->name,
1506 			NodeAlias(instanced_cell, ob))) {
1507 	if (Debug) Printf("shorted ports found on Instance\n");
1508 	strcpy(tmpname,instancename);
1509 	strcat(tmpname,SEPARATOR);
1510 	strcat(tmpname,tp2->name);
1511 	strcpy(tmpname2,instancename);
1512 	strcat(tmpname2,SEPARATOR);
1513 	strcat(tmpname2, NodeAlias(instanced_cell, ob));
1514 	join(tmpname,tmpname2);
1515       }
1516     }
1517   }
1518 }
1519 
1520 /*----------------------------------------------------------------------*/
1521 
Next(char * name)1522 char *Next(char *name)
1523 {
1524     int filenum = CurrentCell->file;
1525 
1526 	/* generate a unique instance name with 'name') */
1527 	char buffer[1024];
1528 	int n;
1529 
1530 	n = 0;
1531 	if (QuickSearch) {
1532 	  struct nlist *tp;
1533 	  tp = LookupCellFile(name, filenum);
1534 	  if (tp != NULL)
1535 	    n = tp->number; /* was +1, but would miss #2 */
1536 	}
1537 	do {
1538 	  n++;
1539 	  sprintf(buffer, "%s%d", name, n);
1540 	} while (LookupInstance(buffer,CurrentCell) != NULL);
1541 	return (strsave(buffer));
1542 }
1543 
1544 /*
1545  *---------------------------------------------------------------------
1546  * This procedure provides a versatile interface to Instance/Connect.
1547  * Cell() accepts a variable length list of arguments, in either of
1548  * two forms:  (i) named arguments -- take the form "port=something"
1549  * (ii) unnamed arguments, which are bound to ports in the order they
1550  * appear. Arguments are read until all cell ports have been connected,
1551  * or until a NULL is encountered.
1552  *
1553  * Returns the name of the instance, which remains valid at least
1554  * until the next call to Cell().
1555  *---------------------------------------------------------------------
1556  */
1557 
Cell(char * inststr,char * model,...)1558 char *Cell(char *inststr, char *model, ...)
1559 {
1560   va_list ap;
1561   char *nodelist;
1562   char tmpname[512];
1563   struct nlist *instanced_cell;
1564   struct objlist *head, *tp, *tp2;
1565   struct objlist *namedporthead, *namedportp, *namedlisthead, *namedlistp;
1566   int portnum, portlist, done;
1567   char namedport[512]; /* tmp buffers */
1568   int filenum, itype, samenode;
1569 
1570   static char *instancename = NULL;
1571   char *instnameptr;
1572 
1573   if (CurrentCell == NULL) {
1574      Printf("No current cell defined for call to Cell().\n");
1575      return NULL;
1576   }
1577   else
1578      filenum = CurrentCell->file;
1579 
1580   if (Debug) Printf("   calling cell: %s\n",model);
1581   if ((itype = IsIgnored(model, filenum)) == IGNORE_CLASS) {
1582     Printf("Class '%s' instanced in input but is being ignored.\n", model);
1583     return NULL;
1584   }
1585   instanced_cell = LookupCellFile(model, filenum);
1586   if (instanced_cell == NULL) {
1587     Printf("Attempt to instance undefined class '%s'\n", model);
1588     return NULL;
1589   }
1590   /* class exists */
1591   tp2 = instanced_cell->cell;
1592   portnum = 0;
1593   while (tp2 != NULL) {
1594     if (IsPort(tp2)) portnum++;
1595     tp2 = tp2->next;
1596   }
1597 
1598   /* Automatically ignore any cell that has no ports (e.g., logo art) */
1599   if (portnum == 0) {
1600     Printf("Class '%s' has no pins and so will be ignored.\n", model);
1601     return NULL;
1602   }
1603 
1604   /* now generate lists of nodes using variable length parameter list */
1605   va_start(ap, model);
1606   head = NULL;
1607   namedporthead = namedlisthead = NULL;
1608   done = 0;
1609   portlist = 0;
1610   while (!done && portlist < portnum) {
1611     struct objlist *tmp;
1612     char *equals;
1613 
1614     nodelist = va_arg(ap, char *);
1615     if (nodelist == NULL) break; /* out of while loop */
1616 
1617     if (strchr(nodelist,'=') != NULL) {
1618       /* we have a named element */
1619       struct objlist *tmpport, *tmpname;
1620       struct nlist *oldCurCell;
1621       int ports;
1622 
1623       strcpy(namedport, nodelist);
1624       equals = strchr(namedport, '=');
1625       *equals = '\0';
1626       equals++;  /* point to first char of node */
1627 
1628       /* need to get list out of cell: 'model' */
1629       oldCurCell = CurrentCell;
1630       CurrentCell = instanced_cell;
1631       tmpport = List(namedport);
1632       CurrentCell = oldCurCell;
1633       tmpname = List(equals);
1634 
1635       if ((ports = ListLen(tmpport)) != ListLen(tmpname)) {
1636 	Printf("List %s has %d elements, list %s has %d\n",
1637 	       namedport, ListLen(tmpport), equals, ListLen(tmpname));
1638 	done = 1;
1639       }
1640       else if (tmpport == NULL) {
1641 	Printf("List %s has no elements\n", namedport);
1642 	done = 1;
1643       }
1644       else if (tmpname == NULL) {
1645 	Printf("List %s has no elements\n", equals);
1646 	done = 1;
1647       }
1648       else {
1649 	portlist += ports;
1650 	namedporthead = ListCat(namedporthead, tmpport);
1651 	namedlisthead = ListCat(namedlisthead, tmpname);
1652       }
1653     }
1654     else {
1655       /* unnamed element, so add it to the list */
1656       tmp = List(nodelist);
1657       if (tmp == NULL) {
1658 	Printf("No such pin '%s' in Cell(%s); Current cell = %s\n",
1659              nodelist, model, CurrentCell->name);
1660 	done = 1;
1661       }
1662       else {
1663 	portlist += ListLen(tmp);
1664 	head = ListCat(head, tmp);
1665       }
1666     }
1667   }
1668   va_end(ap);
1669 
1670   /* Check for shorted pins */
1671   if ((itype == IGNORE_SHORTED) && (head != NULL)) {
1672      unsigned char shorted = (unsigned char)1;
1673      for (tp = head->next; tp; tp = tp->next) {
1674         if (strcasecmp(head->name, tp->name))
1675            shorted = (unsigned char)0;
1676            break;
1677      }
1678      if (shorted == (unsigned char)1) {
1679         Printf("Instance of '%s' is shorted, ignoring.\n", model);
1680         FreeObject(head);
1681         return NULL;
1682      }
1683   }
1684 
1685   if (inststr == NULL) {
1686     if (instancename != NULL)
1687        FreeString(instancename);
1688     QuickSearch = 1;
1689     instancename = Next(model);
1690     QuickSearch = 0;
1691     instnameptr = instancename;
1692   }
1693   else
1694      instnameptr = inststr;
1695 
1696   Instance(model, instnameptr);
1697   tp = head;
1698   for (tp2 = instanced_cell->cell; tp2 != NULL; tp2 = tp2->next) {
1699     if (IsPort(tp2)) {
1700       strcpy(tmpname, instnameptr);
1701       strcat(tmpname, SEPARATOR);
1702       strcat(tmpname, tp2->name);
1703       namedlistp = namedlisthead;
1704       namedportp = namedporthead;
1705       while (namedportp != NULL) {
1706 	if ((*matchfunc)(namedportp->name, tp2->name)) {
1707 	  join(namedlistp->name, tmpname);
1708 	  break; /* out of while loop */
1709 	}
1710 	namedlistp = namedlistp->next;
1711 	namedportp = namedportp->next;
1712       }
1713       if (namedportp == NULL) {
1714 	/* port was NOT a named port, so connect to unnamed list */
1715 	if (tp == NULL) {
1716 	  Printf( "Not enough ports in Cell().\n");
1717 	  break; /* out of for loop */
1718 	}
1719 	else {
1720 	  join(tp->name, tmpname);
1721 	  tp = tp->next;
1722 	}
1723       }
1724     }
1725   }
1726   return instnameptr;
1727 }
1728 
1729 /*----------------------------------------------------------------------*/
1730 /* These default classes correspond to .sim file format types and other	*/
1731 /* basic classes, and may be used by any netlist-reading routine to	*/
1732 /* define basic types.	The classes are only defined when called (in	*/
1733 /* contrast to netgen v. 1.3 and earlier, where they were pre-defined)	*/
1734 /*----------------------------------------------------------------------*/
1735 
P(char * fname,char * inststr,char * gate,char * drain,char * source)1736 char *P(char *fname, char *inststr, char *gate, char *drain, char *source)
1737 {
1738     int fnum = CurrentCell->file;
1739 
1740     if (LookupCellFile("p", fnum) == NULL) {
1741        CellDef("p", fnum);
1742        Port("drain");
1743        Port("gate");
1744        Port("source");
1745        PropertyDouble("p", fnum, "length", 0.01, 0.0);
1746        PropertyDouble("p", fnum, "width", 0.01, 0.0);
1747        SetClass(CLASS_PMOS);
1748        EndCell();
1749        if (fname) ReopenCellDef(fname, fnum);  /* Reopen */
1750     }
1751     return Cell(inststr, "p", drain, gate, source);
1752 }
1753 
1754 /*----------------------------------------------------------------------*/
1755 
P4(char * fname,char * inststr,char * drain,char * gate,char * source,char * bulk)1756 char *P4(char *fname, char *inststr, char *drain, char *gate, char *source, char *bulk)
1757 {
1758     int fnum = CurrentCell->file;
1759 
1760     if (LookupCellFile("p4", fnum) == NULL) {
1761        CellDef("p4", fnum);
1762        Port("drain");
1763        Port("gate");
1764        Port("source");
1765        Port("well");
1766        PropertyDouble("p4", fnum, "length", 0.01, 0.0);
1767        PropertyDouble("p4", fnum, "width", 0.01, 0.0);
1768        SetClass(CLASS_PMOS4);
1769        EndCell();
1770        if (fname) ReopenCellDef(fname, fnum);  /* Reopen */
1771     }
1772     return Cell(inststr, "p4", drain, gate, source, bulk);
1773 }
1774 
1775 /*----------------------------------------------------------------------*/
1776 
N(char * fname,char * inststr,char * gate,char * drain,char * source)1777 char *N(char *fname, char *inststr, char *gate, char *drain, char *source)
1778 {
1779     int fnum = CurrentCell->file;
1780 
1781     if (LookupCellFile("n", fnum) == NULL) {
1782        CellDef("n", fnum);
1783        Port("drain");
1784        Port("gate");
1785        Port("source");
1786        PropertyDouble("n", fnum, "length", 0.01, 0.0);
1787        PropertyDouble("n", fnum, "width", 0.01, 0.0);
1788        SetClass(CLASS_NMOS);
1789        EndCell();
1790        if (fname) ReopenCellDef(fname, fnum);  /* Reopen */
1791     }
1792     return Cell(inststr, "n", drain, gate, source);
1793 }
1794 
1795 /*----------------------------------------------------------------------*/
1796 
N4(char * fname,char * inststr,char * drain,char * gate,char * source,char * bulk)1797 char *N4(char *fname, char *inststr, char *drain, char *gate, char *source, char *bulk)
1798 {
1799     int fnum = CurrentCell->file;
1800 
1801     if (LookupCellFile("n4", fnum) == NULL) {
1802        CellDef("n4", fnum);
1803        Port("drain");
1804        Port("gate");
1805        Port("source");
1806        Port("bulk");
1807        PropertyDouble("n4", fnum, "length", 0.01, 0.0);
1808        PropertyDouble("n4", fnum, "width", 0.01, 0.0);
1809        SetClass(CLASS_NMOS4);
1810        EndCell();
1811        if (fname) ReopenCellDef(fname, fnum);  /* Reopen */
1812     }
1813     return Cell(inststr, "n4", drain, gate, source, bulk);
1814 }
1815 
1816 /*----------------------------------------------------------------------*/
1817 
E(char * fname,char * inststr,char * top,char * bottom_a,char * bottom_b)1818 char *E(char *fname, char *inststr, char *top, char *bottom_a, char *bottom_b)
1819 {
1820     int fnum = CurrentCell->file;
1821 
1822     if (LookupCellFile("e", fnum) == NULL) {
1823        CellDef("e", fnum);
1824        Port("top");
1825        Port("bottom_a");
1826        Port("bottom_b");
1827        PropertyDouble("e", fnum, "length", 0.01, 0.0);
1828        PropertyDouble("e", fnum, "width", 0.01, 0.0);
1829        SetClass(CLASS_ECAP);
1830        EndCell();
1831        if (fname) ReopenCellDef(fname, fnum);  /* Reopen */
1832     }
1833     return Cell(inststr, "e", top, bottom_a, bottom_b);
1834 }
1835 
1836 /*----------------------------------------------------------------------*/
1837 
B(char * fname,char * inststr,char * collector,char * base,char * emitter)1838 char *B(char *fname, char *inststr, char *collector, char *base, char *emitter)
1839 {
1840     int fnum = CurrentCell->file;
1841 
1842     if (LookupCellFile("b", fnum) == NULL) {
1843        CellDef("b", fnum);
1844        Port("collector");
1845        Port("base");
1846        Port("emitter");
1847        SetClass(CLASS_NPN);
1848        EndCell();
1849        if (fname) ReopenCellDef(fname, fnum);  /* Reopen */
1850     }
1851     return Cell(inststr, "b", collector, base, emitter);
1852 }
1853 
1854 /*----------------------------------------------------------------------*/
1855 
Res(char * fname,char * inststr,char * end_a,char * end_b)1856 char *Res(char *fname, char *inststr, char *end_a, char *end_b)
1857 {
1858     int fnum = CurrentCell->file;
1859 
1860     if (LookupCellFile("r", fnum) == NULL) {
1861        CellDef("r", fnum);
1862        Port("end_a");
1863        Port("end_b");
1864        PropertyDouble("r", fnum, "value", 0.01, 0.0);
1865        SetClass(CLASS_RES);
1866        EndCell();
1867        if (fname) ReopenCellDef(fname, fnum);  /* Reopen */
1868     }
1869     return Cell(inststr, "r", end_a, end_b);
1870 }
1871 
1872 /*----------------------------------------------------------------------*/
1873 
Res3(char * fname,char * inststr,char * rdummy,char * end_a,char * end_b)1874 char *Res3(char *fname, char *inststr, char *rdummy, char *end_a, char *end_b)
1875 {
1876     int fnum = CurrentCell->file;
1877 
1878     if (LookupCellFile("r3", fnum) == NULL) {
1879        CellDef("r3", fnum);
1880        Port("dummy");
1881        Port("end_a");
1882        Port("end_b");
1883        PropertyDouble("r3", fnum, "value", 0.01, 0.0);
1884        SetClass(CLASS_RES3);
1885        EndCell();
1886        if (fname) ReopenCellDef(fname, fnum);  /* Reopen */
1887     }
1888     return Cell(inststr, "r3", rdummy, end_a, end_b);
1889 }
1890 
1891 /*----------------------------------------------------------------------*/
1892 
XLine(char * fname,char * inststr,char * node1,char * node2,char * node3,char * node4)1893 char *XLine(char *fname, char *inststr, char *node1, char *node2,
1894 		char *node3, char *node4)
1895 {
1896     int fnum = CurrentCell->file;
1897 
1898     if (LookupCellFile("t", fnum) == NULL) {
1899        CellDef("t", fnum);
1900        Port("node1");
1901        Port("node2");
1902        Port("node3");
1903        Port("node4");
1904        SetClass(CLASS_XLINE);
1905        EndCell();
1906        if (fname) ReopenCellDef(fname, fnum);  /* Reopen */
1907     }
1908     return Cell(inststr, "t", node1, node2, node3, node4);
1909 }
1910 
1911 /*----------------------------------------------------------------------*/
1912 
Cap(char * fname,char * inststr,char * top,char * bottom)1913 char *Cap(char *fname, char *inststr, char *top, char *bottom)
1914 {
1915     int fnum = CurrentCell->file;
1916 
1917     if (LookupCellFile("c", fnum) == NULL) {
1918        CellDef("c", fnum);
1919        Port("top");
1920        Port("bottom");
1921        PropertyDouble("c", fnum, "value", 0.01, 0.0);
1922        SetClass(CLASS_CAP);
1923        EndCell();
1924        if (fname) ReopenCellDef(fname, fnum);  /* Reopen */
1925     }
1926     return Cell(inststr, "c", top, bottom);
1927 }
1928 
1929 /*----------------------------------------------------------------------*/
1930 
Cap3(char * fname,char * inststr,char * top,char * bottom,char * cdummy)1931 char *Cap3(char *fname, char *inststr, char *top, char *bottom, char *cdummy)
1932 {
1933     int fnum = CurrentCell->file;
1934 
1935     if (LookupCellFile("c3", fnum) == NULL) {
1936        CellDef("c3", fnum);
1937        Port("top");
1938        Port("bottom");
1939        Port("dummy");
1940        PropertyDouble("c3", fnum, "value", 0.01, 0.0);
1941        SetClass(CLASS_CAP3);
1942        EndCell();
1943        if (fname) ReopenCellDef(fname, fnum);  /* Reopen */
1944     }
1945     return Cell(inststr, "c3", top, bottom, cdummy);
1946 }
1947 
1948 /*----------------------------------------------------------------------*/
1949 
Inductor(char * fname,char * inststr,char * end_a,char * end_b)1950 char *Inductor(char *fname, char *inststr, char *end_a, char *end_b)
1951 {
1952     int fnum = CurrentCell->file;
1953 
1954     if (LookupCellFile("l", fnum) == NULL) {
1955        CellDef("l", fnum);
1956        Port("end_a");
1957        Port("end_b");
1958        PropertyDouble("l", fnum, "value", 0.01, 0.0);
1959        SetClass(CLASS_INDUCTOR);
1960        EndCell();
1961        if (fname) ReopenCellDef(fname, fnum);  /* Reopen */
1962     }
1963     return Cell(inststr, "l", end_a, end_b);
1964 }
1965 
1966 /*----------------------------------------------------------------------*/
1967 /* Determine if two property keys are matching strings			*/
1968 /* Return 1 on match, 0 on failure to match				*/
1969 /*----------------------------------------------------------------------*/
1970 
PropertyKeyMatch(char * key1,char * key2)1971 int PropertyKeyMatch(char *key1, char *key2)
1972 {
1973    /* For now, an unsophisticated direct string match */
1974 
1975    if (!strcasecmp(key1, key2)) return 1;
1976    return 0;
1977 }
1978 
1979 /*----------------------------------------------------------------------*/
1980 /* Determine if two property values are matching.			*/
1981 /* Return 1 on match, 0 on failure to match				*/
1982 /*----------------------------------------------------------------------*/
1983 
PropertyValueMatch(char * value1,char * value2)1984 int PropertyValueMatch(char *value1, char *value2)
1985 {
1986    /* For now, an unsophisticated direct string match */
1987 
1988    if (!strcasecmp(value1, value2)) return 1;
1989    return 0;
1990 }
1991 
1992 /*----------------------------------------------------------------------*/
1993 /* Add a key:value property pair to the list of property pairs		*/
1994 /*----------------------------------------------------------------------*/
1995 
AddProperty(struct keyvalue ** topptr,char * key,char * value)1996 void AddProperty(struct keyvalue **topptr, char *key, char *value)
1997 {
1998     struct keyvalue *kv;
1999 
2000     if (Debug) Printf("   Defining key:value property pair: %s:%s\n", key, value);
2001     if ((kv = NewKeyValue()) == NULL) {
2002 	perror("Failed NewKeyValue in Property");
2003 	return;
2004     }
2005     kv->key = strsave(key);
2006     kv->value = strsave(value);
2007     kv->next = *topptr;
2008     *topptr = kv;
2009 }
2010 
2011 /*----------------------------------------------------------------------*/
2012 /*----------------------------------------------------------------------*/
2013 
AddScaledProperty(struct keyvalue ** topptr,char * key,char * value,double scale)2014 void AddScaledProperty(struct keyvalue **topptr, char *key, char *value, double scale)
2015 {
2016     struct keyvalue *kv;
2017 
2018     if (Debug) Printf("   Defining key:value property pair: %s:%s\n", key, value);
2019     if ((kv = NewKeyValue()) == NULL) {
2020 	perror("Failed NewKeyValue in Property");
2021 	return;
2022     }
2023     kv->key = strsave(key);
2024     kv->value = strsave(value);
2025     kv->next = *topptr;
2026     *topptr = kv;
2027 }
2028 
2029 /*----------------------------------------------------------------------*/
2030 /* Free up a property list 						*/
2031 /*----------------------------------------------------------------------*/
2032 
DeleteProperties(struct keyvalue ** topptr)2033 void DeleteProperties(struct keyvalue **topptr)
2034 {
2035     struct keyvalue *kv, *nextkv;
2036 
2037     kv = *topptr;
2038     while (kv != NULL)
2039     {
2040 	nextkv = kv->next;
2041 	FreeString(kv->key);
2042 	FreeString(kv->value);
2043 	FREE(kv);
2044 	kv = nextkv;
2045     }
2046     *topptr = NULL;
2047 }
2048 
2049 /*----------------------------------------------------------------------*/
2050 /* LinkProperties() ---							*/
2051 /*									*/
2052 /* Add a list of properties to the current cell	(instance).		*/
2053 /* This just keeps a record of key:value pairs;  it does not attempt	*/
2054 /* to relate them to the cell that is being instanced.  Because this	*/
2055 /* is used to link the same properties multiple times for parallel	*/
2056 /* devices, copy the list (a refcount would work better. . .)		*/
2057 /*----------------------------------------------------------------------*/
2058 
LinkProperties(char * model,struct keyvalue * topptr)2059 struct objlist *LinkProperties(char *model, struct keyvalue *topptr)
2060 {
2061     int filenum = -1;
2062     struct nlist *cell;
2063     struct objlist *tp;
2064     struct keyvalue *kv;
2065     struct valuelist *newkv;
2066     int entries;
2067 
2068     if (topptr == NULL) return NULL;
2069 
2070     if (CurrentCell == NULL) {
2071 	Printf("LinkProperties() called with no current cell.\n");
2072 	return NULL;
2073     }
2074     else
2075 	filenum = CurrentCell->file;
2076 
2077     if (IsIgnored(model, filenum) == IGNORE_CLASS) {
2078 	Printf("Class '%s' instanced in input but is being ignored.\n", model);
2079 	return NULL;
2080     }
2081     cell = LookupCellFile(model, filenum);
2082 
2083     tp = GetObject();
2084     tp->type = PROPERTY;
2085     tp->name = strsave("properties");
2086     tp->node = -2;		/* Don't report as disconnected node */
2087     tp->next = NULL;
2088     tp->model.class = strsave(model);
2089 
2090     /* Save a copy of the key:value pairs in tp->instance.props */
2091 
2092     for (entries = 0, kv = topptr; kv != NULL; kv = kv->next, entries++);
2093     tp->instance.props = NewPropValue(entries + 1);
2094 
2095     for (entries = 0, kv = topptr; kv != NULL; kv = kv->next, entries++)
2096     {
2097 	newkv = &(tp->instance.props[entries]);
2098 	newkv->key = strsave(kv->key);
2099 	/* No promotion to types other than string at this point */
2100 	newkv->type = PROP_STRING;
2101 	newkv->value.string = strsave(kv->value);
2102 
2103 	if (cell != NULL) {
2104 	    struct property *kl = NULL;
2105 
2106 	    /* If there is a matching cell, make sure that the property */
2107 	    /* key exists.  If not, create it and flag a warning.	*/
2108 
2109 	    kl = (struct property *)HashLookup(newkv->key, &(cell->propdict));
2110 	    if (kl == NULL) {
2111 		/* Ideally, for devices, one should check against all	*/
2112 		/* known standard properties.  That's a pain, so	*/
2113 		/* instead just assume that the property is correct.	*/
2114 
2115 		if (cell->class == CLASS_SUBCKT)
2116 		    Fprintf(stderr, "Warning:  Property %s passed to cell %s which "
2117 				"does not define a default.\n",
2118 				newkv->key, cell->name);
2119 		kl = NewProperty();
2120 		kl->key = strsave(newkv->key);
2121 		kl->idx = 0;
2122 		kl->merge = MERGE_NONE;
2123 		kl->type = PROP_STRING;
2124 		kl->slop.dval = 0.0;
2125 		kl->pdefault.string = NULL;
2126 		HashPtrInstall(kl->key, kl, &(cell->propdict));
2127 	    }
2128 	}
2129     }
2130 
2131     /* Final entry marks the end of the list */
2132     newkv = &(tp->instance.props[entries]);
2133     newkv->key = NULL;
2134     newkv->type = PROP_ENDLIST;
2135     newkv->value.ival = 0;
2136 
2137     AddToCurrentCellNoHash(tp);
2138     return tp;
2139 }
2140 
2141 /*----------------------------------------------------------------------*/
2142 /* SetPropertyDefault() ---						*/
2143 /*									*/
2144 /* If a cell does not set property defaults, but an instance of that	*/
2145 /* cell passes a property to it, then there will be a default value	*/
2146 /* with a string type and a NULL value.  Set the default to be equal	*/
2147 /* to the instance type and value.					*/
2148 /*----------------------------------------------------------------------*/
2149 
SetPropertyDefault(struct property * prop,struct valuelist * vl)2150 int SetPropertyDefault(struct property *prop, struct valuelist *vl)
2151 {
2152     if (prop == NULL || vl == NULL) return -1;
2153     if (prop->type != PROP_STRING || prop->pdefault.string != NULL) return 1;
2154 
2155     prop->type = vl->type;
2156 
2157     switch (vl->type) {
2158 	case PROP_STRING:
2159 	    prop->pdefault.string = strsave(vl->value.string);
2160 	    break;
2161 	case PROP_INTEGER:
2162 	    prop->pdefault.ival = vl->value.ival;
2163 	    break;
2164 	case PROP_DOUBLE:
2165 	case PROP_VALUE:
2166 	    prop->pdefault.dval = vl->value.dval;
2167 	    break;
2168 	case PROP_EXPRESSION:
2169 	    prop->pdefault.stack = CopyTokStack(vl->value.stack);
2170 	    break;
2171     }
2172     return 1;
2173 }
2174 
2175 /*----------------------------------------------------------------------*/
2176 /* PromoteProperty() ---						*/
2177 /*									*/
2178 /* Instances have all properties recorded as strings.  If the cell	*/
2179 /* record has an integer or double type, then attempt to convert the	*/
2180 /* instance record into that type.					*/
2181 /*									*/
2182 /* Do not attempt to promote properties that cannot be promoted		*/
2183 /* without altering the content.  Fractional values cannot be converted	*/
2184 /* to integers, and strings that are not numbers must be left as	*/
2185 /* strings. Return 1 if the property was promoted successfully, and 0	*/
2186 /* if no promotion was possible, and -1 if passed a bad argument.	*/
2187 /*									*/
2188 /* NOTE: Arguments ob and tc are only used in the case of evaluating	*/
2189 /* an expression, used to generate a derived property.			*/
2190 /*----------------------------------------------------------------------*/
2191 
PromoteProperty(struct property * prop,struct valuelist * vl,struct objlist * ob,struct nlist * tc)2192 int PromoteProperty(struct property *prop, struct valuelist *vl,
2193 	    struct objlist *ob, struct nlist *tc)
2194 {
2195     char tstr[256];
2196     int ival, result;
2197     double dval;
2198 
2199     if (prop == NULL || vl == NULL) return -1;
2200     if (prop->type == vl->type) return 1;	/* Nothing to do */
2201     result = 0;
2202     if (prop->type == PROP_EXPRESSION) {
2203 	ReduceOneExpression(vl, ob, tc, FALSE);
2204     }
2205     switch (prop->type) {
2206 	case PROP_STRING:
2207 	    switch (vl->type) {
2208 		case PROP_INTEGER:
2209 		    vl->type = PROP_STRING;
2210 		    sprintf(tstr, "%d", vl->value.ival);
2211 		    vl->value.string = strsave(tstr);
2212 		    result = 1;
2213 		    break;
2214 		case PROP_DOUBLE:
2215 		case PROP_VALUE:
2216 		    vl->type = PROP_STRING;
2217 		    sprintf(tstr, "%g", vl->value.dval);
2218 		    vl->value.string = strsave(tstr);
2219 		    result = 1;
2220 		    break;
2221 	    }
2222 	    break;
2223 	case PROP_INTEGER:
2224 	    switch (vl->type) {
2225 		case PROP_STRING:
2226 		    if (StringIsValue(vl->value.string)) {
2227 			result = ConvertStringToFloat(vl->value.string, &dval);
2228 			if (result != 0) {
2229 			    if ((double)((int)dval) == dval) {
2230 				vl->type = PROP_INTEGER;
2231 				FREE(vl->value.string);
2232 				vl->value.ival = (int)dval;
2233 				result = 1;
2234 			    }
2235 			}
2236 		    }
2237 		    break;
2238 		case PROP_DOUBLE:
2239 		case PROP_VALUE:
2240 		    vl->type = PROP_INTEGER;
2241 		    dval = vl->value.dval;
2242 		    if ((double)((int)dval) == dval) {
2243 			vl->value.ival = (int)dval;
2244 			result = 1;
2245 		    }
2246 		    break;
2247 	    }
2248 	    break;
2249 	case PROP_DOUBLE:
2250 	case PROP_VALUE:
2251 	    switch (vl->type) {
2252 		case PROP_STRING:
2253 		    if (StringIsValue(vl->value.string)) {
2254 			result = ConvertStringToFloat(vl->value.string, &dval);
2255 			if (result != 0) {
2256 			   vl->type = PROP_DOUBLE;
2257 			   FREE(vl->value.string);
2258 			   vl->value.dval = dval;
2259 			   result = 1;
2260 			}
2261 		    }
2262 		    break;
2263 		case PROP_INTEGER:
2264 		    vl->type = PROP_DOUBLE;
2265 		    vl->value.dval = (double)vl->value.ival;
2266 		    result = 1;
2267 		    break;
2268 	    }
2269 	    break;
2270     }
2271     return result;
2272 }
2273 
2274 /*----------------------------------------------------------------------*/
2275 /* Structure used by resolveprops()					*/
2276 /*----------------------------------------------------------------------*/
2277 
2278 typedef struct _propdata {
2279     struct nlist *cell;
2280     int entries;
2281 } PropData;
2282 
2283 /*----------------------------------------------------------------------*/
2284 /* resolveprops() ---							*/
2285 /*									*/
2286 /* Match instance property lists to the cell it's an instance of	*/
2287 /*----------------------------------------------------------------------*/
2288 
resolveprops(struct hashlist * p,void * clientdata)2289 struct nlist *resolveprops(struct hashlist *p, void *clientdata)
2290 {
2291    struct nlist *ptr, *pmod;
2292    struct objlist *ob;
2293    struct valuelist *vl, vtemp, *vlnew;
2294    struct property *prop;
2295    struct nlist *tc;
2296    int entries, i, j;
2297    PropData *pdp = (PropData *)clientdata;
2298 
2299    tc = pdp->cell;
2300    entries = pdp->entries;
2301 
2302    ptr = (struct nlist *)(p->ptr);
2303    if (ptr->file != tc->file) return NULL;
2304 
2305    for (ob = ptr->cell; ob; ob = ob->next) {
2306       if (ob->type == PROPERTY) {
2307 	 if ((*matchfunc)(ob->model.class, tc->name)) {
2308 	    /* Check length of the record, resize if necessary */
2309 	    for (i = 0;; i++) {
2310 	       vl = &(ob->instance.props[i]);
2311 	       if (vl->type == PROP_ENDLIST) break;
2312 	    }
2313 	    if (i > entries) {
2314 	       Printf("Warning: Instance defines more properties than cell.\n");
2315 	       Printf("This shouldn't happen.\n");
2316 	    }
2317 
2318 	    /* Create new structure in the correct order, and replace	*/
2319 	    /* the old property structure with it.			*/
2320 
2321 	    vlnew = NewPropValue(entries + 1);
2322 	    for (i = 0;; i++) {
2323 	       vl = &(ob->instance.props[i]);
2324 	       if (vl->type == PROP_ENDLIST) break;
2325 	       prop = (struct property *)HashLookup(vl->key, &(tc->propdict));
2326 
2327 	       /* Warning:  prop should never be null, but condition	*/
2328 	       /* should be handled.					*/
2329 
2330 	       if (prop != NULL) {
2331 		  j = prop->idx;
2332 		  vlnew[j].key = vl->key;
2333 		  vlnew[j].type = vl->type;
2334 		  vlnew[j].value = vl->value;
2335 	       }
2336 	    }
2337 	    vlnew[entries].key = NULL;
2338 	    vlnew[entries].type = PROP_ENDLIST;
2339 	    vlnew[entries].value.ival = 0;
2340 	    FREE(ob->instance.props);
2341 	    ob->instance.props = vlnew;
2342 	 }
2343       }
2344    }
2345    return ptr;
2346 }
2347 
2348 /*----------------------------------------------------------------------*/
2349 /* ResolveProperties() ---						*/
2350 /*									*/
2351 /* This routine does the greatest part of the work for the property-	*/
2352 /* handling mechanism.  It determines which properties are common to	*/
2353 /* two models, and arranges the lists of properties in both models	*/
2354 /* such that the lists are in the same order.  Properties that exist in	*/
2355 /* one cell but not the other are floated to the end of the list.  All	*/
2356 /* instances of both models are checked and their property lists also	*/
2357 /* arranged in order.  							*/
2358 /*----------------------------------------------------------------------*/
2359 
ResolveProperties(char * name1,int file1,char * name2,int file2)2360 void ResolveProperties(char *name1, int file1, char *name2, int file2)
2361 {
2362     PropData pdp;
2363     struct property *kl1, *kl2;
2364     struct nlist *tp1, *tp2;
2365     int i;
2366 
2367     struct valuelist *kv, *newkv;
2368     struct valuelist *vl, *lastvl;
2369     int isnum, filenum;
2370 
2371     if ((tp1 = LookupCellFile(name1, file1)) == NULL) return;
2372     if ((tp2 = LookupCellFile(name2, file2)) == NULL) return;
2373 
2374     /* Find all properties defined in the cell tp1, and index them in	*/
2375     /* numerical order.  For each property, find the equivalent		*/
2376     /* property in the cell to be matched, and give them both the same	*/
2377     /* index.  If the property does not exist in the second cell, then	*/
2378     /* create it.							*/
2379 
2380     kl1 = (struct property *)HashFirst(&(tp1->propdict));
2381     /* If indexes are not zero, then properties have already been matched. */
2382     if (kl1 == NULL) return;	/* Cell has no properties */
2383     if (kl1->idx != 0) return;
2384     i = 1;
2385 
2386     while (kl1 != NULL) {
2387 	kl1->idx = i;
2388 
2389 	kl2 = (struct property *)HashLookup(kl1->key, &(tp2->propdict));
2390 	if (kl2 == NULL) {
2391 	    /* No such property in tp2 */
2392 	    switch (kl1->type) {
2393 		case PROP_STRING:
2394 		    kl2 = PropertyString(tp2->name, tp2->file, kl1->key,
2395 				kl1->slop.dval, kl1->pdefault.string);
2396 		    break;
2397 		case PROP_INTEGER:
2398 		    kl2 = PropertyInteger(tp2->name, tp2->file, kl1->key,
2399 				kl1->slop.ival, kl1->pdefault.ival);
2400 		    break;
2401 		case PROP_DOUBLE:
2402 		    kl2 = PropertyDouble(tp2->name, tp2->file, kl1->key,
2403 				kl1->slop.dval, kl1->pdefault.dval);
2404 		    break;
2405 		case PROP_VALUE:
2406 		    kl2 = PropertyValue(tp2->name, tp2->file, kl1->key,
2407 				kl1->slop.dval, kl1->pdefault.dval);
2408 		    break;
2409 	    }
2410 	}
2411 	if (kl2 != NULL) kl2->idx = i;
2412 	kl1 = (struct property *)HashNext(&(tp1->propdict));
2413 	i++;
2414     }
2415 
2416     /* Now check tp2 for properties not in tp1 */
2417 
2418     kl2 = (struct property *)HashFirst(&(tp2->propdict));
2419 
2420     while (kl2 != NULL) {
2421 	kl1 = (struct property *)HashLookup(kl2->key, &(tp1->propdict));
2422 	if (kl1 == NULL) {
2423 	    /* No such property in tp1 */
2424 	    switch (kl2->type) {
2425 		case PROP_STRING:
2426 		    kl1 = PropertyString(tp1->name, tp1->file, kl2->key,
2427 				kl2->slop.dval, kl2->pdefault.string);
2428 		    break;
2429 		case PROP_INTEGER:
2430 		    kl1 = PropertyInteger(tp1->name, tp1->file, kl2->key,
2431 				kl2->slop.ival, kl2->pdefault.ival);
2432 		    break;
2433 		case PROP_DOUBLE:
2434 		    kl1 = PropertyDouble(tp1->name, tp1->file, kl2->key,
2435 				kl2->slop.dval, kl2->pdefault.dval);
2436 		    break;
2437 		case PROP_VALUE:
2438 		    kl1 = PropertyValue(tp1->name, tp1->file, kl2->key,
2439 				kl2->slop.dval, kl2->pdefault.dval);
2440 		    break;
2441 	    }
2442 	}
2443 	if (kl1 != NULL) kl1->idx = i;
2444 	kl2 = (struct property *)HashNext(&(tp1->propdict));
2445 	i++;
2446     }
2447 
2448     /* Now that the properties of the two cells are ordered, find all	*/
2449     /* instances of both cells, and order their properties to match.	*/
2450 
2451     pdp.cell = tp1;
2452     pdp.entries = i;
2453     RecurseCellHashTable2(resolveprops, (void *)(&pdp));
2454     pdp.cell = tp2;
2455     RecurseCellHashTable2(resolveprops, (void *)(&pdp));
2456 }
2457 
2458 /*--------------------------------------------------------------*/
2459 /* Copy properties from one object to another (used when	*/
2460 /* flattening cells).						*/
2461 /*--------------------------------------------------------------*/
2462 
CopyProperties(struct objlist * obj_to,struct objlist * obj_from)2463 void CopyProperties(struct objlist *obj_to, struct objlist *obj_from)
2464 {
2465    int i;
2466    struct valuelist *kv, *kvcopy, *kvcur;
2467 
2468    if (obj_from->instance.props != NULL) {
2469       for (i = 0;; i++) {
2470          kv = &(obj_from->instance.props[i]);
2471          if (kv->type == PROP_ENDLIST)
2472 	    break;
2473       }
2474       kvcopy = NewPropValue(i + 1);
2475 
2476       for (i = 0;; i++) {
2477 	 kv = &(obj_from->instance.props[i]);
2478 	 kvcur = &(kvcopy[i]);
2479          kvcur->type = kv->type;
2480 	 if (kv->type == PROP_ENDLIST) break;
2481          kvcur->key = strsave(kv->key);
2482          switch (kvcur->type) {
2483 	    case PROP_STRING:
2484       	        kvcur->value.string = strsave(kv->value.string);
2485 	        break;
2486 	    case PROP_INTEGER:
2487       	        kvcur->value.ival = kv->value.ival;
2488 	        break;
2489 	    case PROP_DOUBLE:
2490 	    case PROP_VALUE:
2491       	        kvcur->value.dval = kv->value.dval;
2492 	        break;
2493 	    case PROP_EXPRESSION:
2494       	        kvcur->value.stack = CopyTokStack(kv->value.stack);
2495 	        break;
2496          }
2497       }
2498       kvcur->key = NULL;
2499       kvcur->value.ival = 0;
2500 
2501       obj_to->instance.props = kvcopy;
2502       if (obj_from->model.class)
2503          obj_to->model.class = strsave(obj_from->model.class);
2504    }
2505 }
2506 
2507 /*--------------------------------------------------------------*/
2508 /* Convert a string to an integer.				*/
2509 /* At the moment, we do nothing with error conditions.		*/
2510 /*--------------------------------------------------------------*/
2511 
ConvertStringToInteger(char * string,int * ival)2512 int ConvertStringToInteger(char *string, int *ival)
2513 {
2514    long lval;
2515    char *eptr = NULL;
2516 
2517    lval = strtol(string, &eptr, 10);
2518    if (eptr > string) {
2519       *ival = (int)lval;
2520       return 1;
2521    }
2522    else return 0;		/* No conversion */
2523 }
2524 
2525 /*--------------------------------------------------------------*/
2526 /* Check if a string is a valid number (with optional metric	*/
2527 /* unit suffix).						*/
2528 /* Returns 1 if the string is a proper value, 0 if not.		*/
2529 /*--------------------------------------------------------------*/
2530 
StringIsValue(char * string)2531 int StringIsValue(char *string)
2532 {
2533    double fval;
2534    char *eptr = NULL;
2535 
2536    fval = strtod(string, &eptr);
2537    if (eptr > string)
2538    {
2539       while (isspace(*eptr)) eptr++;
2540       switch (tolower(*eptr)) {
2541 	 case 'g':	/* giga */
2542 	 case 'k':	/* kilo */
2543 	 case 'c':	/* centi */
2544 	 case 'm':	/* milli */
2545 	 case 'u':	/* micro */
2546 	 case 'n':	/* nano */
2547 	 case 'p':	/* pico */
2548 	 case 'f':	/* femto */
2549 	 case 'a':	/* atto */
2550 	 case '\0':	/* no units */
2551 	    return 1;
2552       }
2553    }
2554    return 0;
2555 }
2556 
2557 /*--------------------------------------------------------------*/
2558 /* Convert a string with possible metric notation into a float.	*/
2559 /* This follows SPICE notation with case-insensitive prefixes,	*/
2560 /* using "meg" to distinguish 1x10^6 from "m" 1x10^-3		*/
2561 /*								*/
2562 /* Put the result in "dval".  Return 1 if successful, 0 if	*/
2563 /* unsuccessful.						*/
2564 /*--------------------------------------------------------------*/
2565 
ConvertStringToFloat(char * string,double * dval)2566 int ConvertStringToFloat(char *string, double *dval)
2567 {
2568    long double fval;
2569    char *eptr = NULL;
2570 
2571    fval = strtold(string, &eptr);
2572    if (eptr > string)
2573    {
2574       while (isspace(*eptr)) eptr++;
2575       switch (tolower(*eptr)) {
2576 	 case 'g':	/* giga */
2577 	    fval *= 1.0e9L;
2578 	    eptr++;
2579 	    break;
2580 	 case 'k':	/* kilo */
2581 	    fval *= 1.0e3L;
2582 	    eptr++;
2583 	    break;
2584 	 case 'c':	/* centi */
2585 	    fval *= 1.0e-2L;
2586 	    eptr++;
2587 	    break;
2588 	 case 'm':	/* milli */
2589 	    if (tolower(*(eptr + 1)) == 'e' &&
2590 			tolower(*(eptr + 2)) == 'g') {
2591 	       fval *= 1.0e6L;
2592 	       eptr += 2;
2593 	    }
2594 	    else
2595 	       fval *= 1.0e-3L;
2596 	    eptr++;
2597 	    break;
2598 	 case 'u':	/* micro */
2599 	    fval *= 1.0e-6L;
2600 	    eptr++;
2601 	    break;
2602 	 case 'n':	/* nano */
2603 	    fval *= 1.0e-9L;
2604 	    eptr++;
2605 	    break;
2606 	 case 'p':	/* pico */
2607 	    fval *= 1.0e-12L;
2608 	    eptr++;
2609 	    break;
2610 	 case 'f':	/* femto */
2611 	    fval *= 1.0e-15L;
2612 	    eptr++;
2613 	    break;
2614 	 case 'a':	/* atto */
2615 	    fval *= 1.0e-18L;
2616 	    eptr++;
2617 	    break;
2618 	 default:
2619 	    break;	/* No units, no adjustment */
2620       }
2621       if (*eptr != '\0') {
2622 	 switch (tolower(*eptr)) {
2623 	     case 'f':	/* Farads */
2624 		if (!strncasecmp(eptr, "farad", 5)) {
2625 		    eptr += 5;
2626 		    if (tolower(*eptr) == 's') eptr++;
2627 		}
2628 		else eptr++;
2629 		if (*eptr != '\0') return 0; 	/* Unknown units */
2630 		break;
2631 	     case 'm':	/* Meters */
2632 		if (!strncasecmp(eptr, "meter", 5)) {
2633 		    eptr += 5;
2634 		    if (tolower(*eptr) == 's') eptr++;
2635 		}
2636 		else eptr++;
2637 		if (*eptr != '\0') return 0; 	/* Unknown units */
2638 		break;
2639 	     case 'h':	/* Henrys */
2640 		if (!strncasecmp(eptr, "henr", 4)) {
2641 		    eptr += 4;
2642 		    if (tolower(*eptr) == 'y') {
2643 			eptr++;
2644 		        if (*eptr == 's') eptr++;
2645 		    }
2646 		    else if (!strncasecmp(eptr, "ies", 3))
2647 			eptr += 3;
2648 		}
2649 		else eptr++;
2650 		if (*eptr != '\0') return 0; 	/* Unknown units */
2651 		break;
2652 	     case 's':	/* Seconds */
2653 		if (!strncasecmp(eptr, "second", 6)) {
2654 		    eptr += 6;
2655 		    if (tolower(*eptr) == 's') eptr++;
2656 		}
2657 		else eptr++;
2658 		if (*eptr != '\0') return 0; 	/* Unknown units */
2659 		break;
2660 	     case 'o':	/* Ohms */
2661 		if (!strncasecmp(eptr, "ohm", 3)) {
2662 		    eptr += 3;
2663 		    if (tolower(*eptr) == 's') eptr++;
2664 		}
2665 		else eptr++;
2666 		if (*eptr != '\0') return 0; 	/* Unknown units */
2667 		break;
2668 	     case 'v':	/* Volts */
2669 		if (!strncasecmp(eptr, "volt", 4)) {
2670 		    eptr += 4;
2671 		    if (tolower(*eptr) == 's') eptr++;
2672 		}
2673 		else eptr++;
2674 		if (*eptr != '\0') return 0; 	/* Unknown units */
2675 		break;
2676 	     case 'a':	/* Amps */
2677 		if (!strncasecmp(eptr, "amp", 3)) {
2678 		    eptr += 3;
2679 		    if (tolower(*eptr) == 's') eptr++;
2680 		}
2681 		else eptr++;
2682 		if (*eptr != '\0') return 0; 	/* Unknown units */
2683 		break;
2684 	     default:
2685 		return 0;	/* Unknown units;  no conversion */
2686 	 }
2687       }
2688    }
2689    else if (eptr == string) return 0;	/* No conversion */
2690    *dval = (double)fval;
2691    return 1;
2692 }
2693 
2694 /*--------------------------------------------------------------*/
2695 /* Convert a string into a double, scale it, and pass it back	*/
2696 /* as another string value.					*/
2697 /*--------------------------------------------------------------*/
2698 
ScaleStringFloatValue(char * vstr,double scale)2699 char *ScaleStringFloatValue(char *vstr, double scale)
2700 {
2701    static char newstr[32];
2702    double fval, afval;
2703    int result;
2704 
2705    result = ConvertStringToFloat(vstr, &fval);
2706    if (result == 1) {
2707       fval *= scale;
2708 
2709       snprintf(newstr, 31, "%g", fval);
2710       return newstr;
2711    }
2712    else
2713       return vstr;
2714 }
2715 
2716 /*----------------------------------------------------------------------*/
2717 /* Workhorse subroutine for the Connect() function			*/
2718 /*----------------------------------------------------------------------*/
2719 
join(char * node1,char * node2)2720 void join(char *node1, char *node2)
2721 {
2722 	struct objlist *tp1, *tp2, *tp3;
2723 	int nodenum, oldnode;
2724 
2725 	if (CurrentCell == NULL) {
2726 		Printf( "No current cell for join(%s,%s)\n",
2727 			node1,node2);
2728 		return;
2729 	}
2730 	tp1 = LookupObject(node1, CurrentCell);
2731 	if (tp1 == NULL) {
2732 		Printf("No node '%s' found in current cell '%s'\n",
2733 			node1, CurrentCell->name);
2734 		return;
2735 	}
2736 	tp2 = LookupObject(node2, CurrentCell);
2737 	if (tp2 == NULL) {
2738 		Printf("No node '%s' found in current cell '%s'\n",
2739 			node2, CurrentCell->name);
2740 		return;
2741 	}
2742 	if (Debug) Printf("         joining: %s == %s (",
2743 		           tp1->name,tp2->name);
2744 
2745 	/* see if either node has an assigned node number */
2746 	if ((tp1->node == -1) && (tp2->node == -1)) {
2747 		tp1->node = NextNode;
2748 		tp2->node = NextNode++;
2749 		if (Debug) Printf("New ");
2750 	}
2751 	else if (tp1->node == -1) tp1->node = tp2->node;
2752 	else if (tp2->node == -1) tp2->node = tp1->node;
2753 	else {
2754 		if (tp1->node < tp2->node) {
2755 			nodenum = tp1->node;
2756 			oldnode = tp2->node;
2757 		} else {
2758 			nodenum = tp2->node;
2759 			oldnode = tp1->node;
2760 		}
2761 		/* now search through entire list, updating nodes as needed */
2762 		for (tp3 = CurrentCell->cell; tp3 != NULL; tp3 = tp3->next)
2763 			if (tp3->node == oldnode)  tp3->node = nodenum;
2764 	}
2765 	if (Debug) Printf("Node = %d)\n",tp1->node);
2766 }
2767 
2768 /*----------------------------------------------------------------------*/
2769 /*----------------------------------------------------------------------*/
2770 
Connect(char * tplt1,char * tplt2)2771 void Connect(char *tplt1, char *tplt2)
2772 {
2773 	struct objlist *list1, *list2;
2774 	int n1, n2;  /* lengths of two lists */
2775 
2776 	if (Debug) Printf("      Connect(%s,%s)\n",tplt1,tplt2);
2777 	if (CurrentCell == NULL) {
2778 		Printf( "No current cell for Connect(%s,%s)\n",
2779 			tplt1,tplt2);
2780 		return;
2781 	}
2782 	list1 = List(tplt1);
2783 	n1 = ListLen(list1);
2784 	list2 = List(tplt2);
2785 	n2 = ListLen(list2);
2786 	if (n1==n2) {
2787 	  while (list1 != NULL) {
2788 	    join(list1->name,list2->name);
2789 	    list1 = list1->next;
2790 	    list2 = list2->next;
2791 	  }
2792 	}
2793 	else if (n1==1 && n2>0) {
2794 		while (list2 != NULL) {
2795 			join(list1->name,list2->name);
2796 			list2 = list2->next;
2797 		}
2798 	}
2799 	else if (n2==1 && n1>0) {
2800 		while (list1 != NULL) {
2801 			join(list1->name,list2->name);
2802 			list1 = list1->next;
2803 		}
2804 	}
2805 	else Printf("Unequal element lists: '%s' has %d, '%s' has %d.\n",
2806 		    tplt1,n1,tplt2,n2);
2807 }
2808 
2809 /*----------------------------------------------------------------------*/
2810 /*----------------------------------------------------------------------*/
2811 
PortList(char * prefix,char * list_template)2812 void PortList(char *prefix, char *list_template)
2813 {
2814   struct objlist *list;
2815   char buffer[1024];
2816   int buflen;
2817   int i;
2818 
2819   for (list = List(list_template); list != NULL; list = list->next) {
2820     strcpy(buffer,prefix);
2821     strcat(buffer,list->name);
2822     buflen = strlen(buffer);
2823     for (i=0; i < buflen; i++)
2824       if (buffer[i] == SEPARATOR[0]) buffer[i] = PORT_DELIMITER[0];
2825     Port(buffer);
2826     join(buffer,list->name);
2827   }
2828 }
2829 
2830 /*----------------------------------------------------------------------*/
2831 /*----------------------------------------------------------------------*/
2832 
Place(char * name)2833 void Place(char *name)
2834 {
2835   char *freename;
2836   char buffer1[1024], buffer2[1024];
2837   char prefix[20];
2838 
2839   QuickSearch = (LastPlaced != NULL);
2840   freename = Next(name);
2841   Instance(name,freename);
2842   if (Composition == HORIZONTAL) {
2843     sprintf(buffer2,"%s%s%s%s%s", freename, SEPARATOR, "W", PORT_DELIMITER, "*");
2844     if (LastPlaced != NULL) {
2845       sprintf(buffer1,"%s%s%s%s%s",
2846 	      LastPlaced->instance.name, SEPARATOR, "E", PORT_DELIMITER, "*");
2847       Connect (buffer1,buffer2);
2848     }
2849     else {  /* promote left-hand ports */
2850       sprintf(prefix,"%s%s","W", PORT_DELIMITER);
2851       PortList(prefix,buffer2);
2852     }
2853     buffer2[strlen(buffer2)-3] = 'N';
2854     sprintf(prefix,"%s%s", "N", PORT_DELIMITER);
2855     PortList(prefix,buffer2);
2856     buffer2[strlen(buffer2)-3] = 'S';
2857     sprintf(prefix,"%s%s", "S", PORT_DELIMITER);
2858     PortList(prefix,buffer2);
2859   }
2860   else if (Composition == VERTICAL) {
2861     sprintf(buffer2,"%s%s%s%s%s",
2862 	    freename, SEPARATOR, "S", PORT_DELIMITER, "*");
2863     if (LastPlaced != NULL) {
2864       sprintf(buffer1,"%s%s%s%s%s",
2865 	      LastPlaced->instance.name, SEPARATOR, "N", PORT_DELIMITER, "*");
2866       Connect (buffer1,buffer2);
2867     }
2868     else { /* promote bottom ports */
2869       sprintf(prefix,"%s%s","S", PORT_DELIMITER);
2870       PortList(prefix,buffer2);
2871     }
2872     buffer2[strlen(buffer2)-3] = 'E';
2873     sprintf(prefix,"%s%s", "E", PORT_DELIMITER);
2874     PortList(prefix,buffer2);
2875     buffer2[strlen(buffer2)-3] = 'W';
2876     sprintf(prefix,"%s%s", "W", PORT_DELIMITER);
2877     PortList(prefix,buffer2);
2878   }
2879   LastPlaced = LookupInstance(freename,CurrentCell);
2880   QuickSearch = 0;
2881   FreeString(freename);
2882 }
2883 
2884 /*----------------------------------------------------------------------*/
2885 /*----------------------------------------------------------------------*/
2886 
Array(char * Cell,int num)2887 void Array(char *Cell, int num)
2888 {
2889         int i;
2890 
2891 	for (i=0; i<num; i++) {
2892 	  if (Debug) Printf(".");
2893 	  Place(Cell);
2894 	}
2895 }
2896 
2897 
2898 /* if TRUE, always connect all nodes upon EndCell() */
2899 int NoDisconnectedNodes = 0;
2900 
2901 /*----------------------------------------------------------------------*/
2902 /* Within the definition of 'model', traverse the object list		*/
2903 /* and connect all the otherwise disconnected nodes (i.e., those	*/
2904 /* with node==-1) to unique node numbers 				*/
2905 /*----------------------------------------------------------------------*/
2906 
ConnectAllNodes(char * model,int file)2907 void ConnectAllNodes(char *model, int file)
2908 {
2909   int nodenum;
2910   struct nlist *tp;
2911   struct objlist *ob;
2912 
2913   if ((tp = LookupCellFile(model, file)) == NULL) {
2914     Printf("Cell: %s does not exist.\n", model);
2915     return;
2916   }
2917 
2918   nodenum = 0;
2919   for (ob = tp->cell; ob != NULL; ob = ob->next)
2920     if (ob->node >= nodenum) nodenum = ob->node + 1;
2921 
2922   for (ob = tp->cell; ob != NULL; ob = ob->next)
2923     if (ob->node == -1) ob->node = nodenum++;
2924 }
2925 
2926 /*----------------------------------------------------------------------*/
2927 /* Series and Parallel combination:					*/
2928 /* All devices of the same type that exist in series and parallel	*/
2929 /* combinations will be treated as a single device in a network.	*/
2930 /* Series connections are only allowed for resistors and inductors.	*/
2931 /* Any device may be connected in parallel.  For combinations of series	*/
2932 /* and parallel, as in a resistor network, there is a set of rules:	*/
2933 /*									*/
2934 /* Running parallel and series checks:					*/
2935 /* 1. Run parallel once.  If a repeat run and no devices are merged,	*/
2936 /*    then go to 4.							*/
2937 /* 2. Run series until no devices are merged.				*/
2938 /* 3. If series ran more than once, then go to 1.			*/
2939 /* 4. End merge								*/
2940 /*									*/
2941 /* Each merge procedure, when it finds two devices that can be merged,	*/
2942 /* removes the second device from the netlist and adds its properties	*/
2943 /* to the first device.  If a series merge, then the nodes are adjusted	*/
2944 /* appropriately.  Where A is the property list of the first device and	*/
2945 /* B is the property list of the second device, the first and last	*/
2946 /* properties of A and the first property of B may require a marker to	*/
2947 /* indicate the topology of the network, as follows (in order):		*/
2948 /*									*/
2949 /* For a parallel merge:						*/
2950 /*    1) If A has series components then tag first property of A with	*/
2951 /*	 "open" and tag first property of B with "close".		*/
2952 /*    2) If B has series components then tag first property of B with	*/
2953 /*	 "open". 							*/
2954 /*									*/
2955 /* For a series merge:							*/
2956 /*    1) If A has unbalanced "opens", then add "close" to first		*/
2957 /*	 property of B to balance the "opens".				*/
2958 /*    2) Always tag B with "series".					*/
2959 /*									*/
2960 /* Tags are indicated by a property named "_tag" which has a string	*/
2961 /* value of ordered characters, "+" for series, "(" for open, and ")"	*/
2962 /* for close.  A device with only one property record has no "_tag"	*/
2963 /* record.  A device which is in parallel with the device(s) in front	*/
2964 /* of it is implicitly parallel by not having an "+" tag, and may not	*/
2965 /* have a tag at all.							*/
2966 /*									*/
2967 /* The property check routine is responsible for comparing device	*/
2968 /* series/parallel networks against each other.  Otherwise, each	*/
2969 /* series/parallel network is considered topologically as a single	*/
2970 /* device, and any differences in the series/parallel networks between	*/
2971 /* two circuits being matched will be treated as a property error.	*/
2972 /*----------------------------------------------------------------------*/
2973 
2974 /* add_prop_tag --- add the tag character tagc to the property list of	*/
2975 /* obr.  obr points to the first property record.			*/
2976 
add_prop_tag(struct objlist * obr,char tagc)2977 int add_prop_tag(struct objlist *obr, char tagc)
2978 {
2979    struct objlist *nob;
2980    int i, k, l;
2981    struct valuelist *kv, *kv2;
2982    int hastag;
2983    char *tmpstr;
2984 
2985    hastag = FALSE;
2986    for (nob = obr; nob; nob = nob->next) {
2987       if (nob->type != PROPERTY) break;
2988       for (i = 0; ; i++) {
2989          kv = &(nob->instance.props[i]);
2990 	 if (kv->type == PROP_ENDLIST) break;
2991          if (kv->type == PROP_STRING) {
2992 	    if (!strcmp(kv->key, "_tag")) {
2993 	       hastag = TRUE;
2994 	       break;
2995 	    }
2996 	 }
2997       }
2998    }
2999    if (hastag) {
3000       if (nob == obr) {
3001          // If _tag was first in the list, then just prepend tagc to the tag value
3002 	 tmpstr = kv->value.string;
3003          kv->value.string = (char *)MALLOC(strlen(tmpstr) + 2);
3004 	 sprintf(kv->value.string, "%c%s", tagc, tmpstr);
3005 	 FREE(tmpstr);
3006       }
3007       else {
3008          // Add a _tag key to the first property list and set value to tagc
3009 
3010 	 kv = &(obr->instance.props[i]);
3011 	 k = 0;
3012 	 for (k = 0; ; k++) {
3013 	    kv = &(obr->instance.props[k]);
3014 	    if (kv->type == PROP_ENDLIST)
3015 	       break;
3016          }
3017 	 kv2 = (struct valuelist *)MALLOC((k + 2) * sizeof(struct valuelist));
3018 	 kv2->key = strsave("_tag");
3019 	 kv2->type = PROP_STRING;
3020 	 /* Value is set to tagc */
3021 	 kv2->value.string = (char *)MALLOC(2);
3022 	 sprintf(kv2->value.string, "%c", tagc);
3023 	 for (l = 0; l <= k; l++)
3024 	    kv2[l + 1] = obr->instance.props[l];
3025 	 FREE(obr->instance.props);
3026 	 obr->instance.props = kv2;
3027       }
3028    }
3029    return hastag;
3030 }
3031 
3032 /* add_balancing_close --- find the number of unbalanced 'open'		*/
3033 /* records in ob1's property list, and prepend the correct number of	*/
3034 /* 'C' closures to the property list of ob2.				*/
3035 
add_balancing_close(struct objlist * ob1,struct objlist * ob2)3036 void add_balancing_close(struct objlist *ob1, struct objlist *ob2)
3037 {
3038    struct objlist *nob;
3039    int i, k, l;
3040    struct valuelist *kv, *kv2;
3041    int opentags;
3042    char *tmpstr, *tag;
3043 
3044    /* Find the first property record in ob1. */
3045    for (nob = ob1->next; nob && nob->type != FIRSTPIN; nob = nob->next)
3046       if (nob->type == PROPERTY)
3047 	 break;
3048    if (nob->type != PROPERTY) return;	// shouldn't happen
3049 
3050    opentags = 0;
3051    for (; nob->next && nob->next->type == PROPERTY; nob = nob->next) {
3052       for (i = 0; ; i++) {
3053          kv = &(nob->instance.props[i]);
3054 	 if (kv->type == PROP_ENDLIST) break;
3055          if (kv->type == PROP_STRING) {
3056 	    if (!strcmp(kv->key, "_tag")) {
3057 	       for (tag = kv->value.string; *tag != '\0'; tag++) {
3058 		  if (*tag == '(') opentags++;
3059 		  else if (*tag == ')') opentags--;
3060 	       }
3061                break;
3062 	    }
3063 	 }
3064       }
3065    }
3066    if (opentags == 0) return;
3067 
3068    /* Find the first property record in ob2. */
3069    for (nob = ob2->next; nob && nob->type != FIRSTPIN; nob = nob->next)
3070       if (nob->type == PROPERTY)
3071 	 break;
3072    if (nob->type != PROPERTY) return;	// shouldn't happen
3073 
3074    // This is slow but it's the easiest way to do it
3075    while (opentags-- > 0) add_prop_tag(nob, ')');
3076 }
3077 
3078 /* Remove unneeded group tags, when device merging has removed all but	*/
3079 /* one, or all but parallel devices inside a group.   For simplicity,	*/
3080 /* handle only one group at a time.  Return 1 if a group was removed	*/
3081 /* and 0 if not.  The caller should run repeatedly until the routine	*/
3082 /* returns 0.								*/
3083 
remove_group_tags(struct objlist * ob)3084 int remove_group_tags(struct objlist *ob)
3085 {
3086    struct objlist *nob, *sob;
3087    int i, si, stags;
3088    struct valuelist *kv;
3089    char *tag;
3090 
3091    /* Find the first property record in ob. */
3092    for (nob = ob->next; nob && nob->type != FIRSTPIN; nob = nob->next)
3093       if (nob->type == PROPERTY)
3094 	 break;
3095    if (nob->type != PROPERTY) return 0;	// shouldn't happen
3096 
3097    for (sob = NULL; nob && nob->type == PROPERTY; nob = nob->next) {
3098       for (i = 0; ; i++) {
3099          kv = &(nob->instance.props[i]);
3100 	 if (kv->type == PROP_ENDLIST) break;
3101          if (kv->type == PROP_STRING) {
3102 	    if (!strcmp(kv->key, "_tag")) {
3103 	       for (tag = kv->value.string; *tag != '\0'; tag++) {
3104 		  if (*tag == '(') {
3105 		     sob = nob;		/* Save position of open group */
3106 		     si = i;		/* Save index of open group */
3107 		     stags = 0;		/* Check for series tags */
3108 		  }
3109 		  else if (*tag == '+')
3110 		     stags++;
3111 		  else if (*tag == ')') {
3112 		     if (stags == 0) {
3113 			/* Remove close tag */
3114 			for (++i; ; i++) {
3115 			   nob->instance.props[i - 1] = nob->instance.props[i];
3116 			   if (nob->instance.props[i].type == PROP_ENDLIST) break;
3117 			}
3118 			/* Remove open tag */
3119 			for (i = si + 1; ; i++) {
3120 			   sob->instance.props[i - 1] = sob->instance.props[i];
3121 			   if (sob->instance.props[i].type == PROP_ENDLIST) break;
3122 			}
3123 			return 1;
3124 		     }
3125 		     sob = NULL;
3126 		     stags = 0;
3127 		  }
3128 	       }
3129 	    }
3130 	 }
3131       }
3132    }
3133    if (sob != NULL) {
3134       /* Implicit close tag at end */
3135       if (stags == 0) {
3136 	 /* Remove open tag */
3137 	 for (i = si + 1; ; i++) {
3138 	    sob->instance.props[i - 1] = sob->instance.props[i];
3139 	    if (sob->instance.props[i].type == PROP_ENDLIST) break;
3140 	 }
3141 	 return 1;
3142       }
3143    }
3144    return 0;
3145 }
3146 
3147 /*----------------------------------------------------------------------*/
3148 /* Find all devices that are of the same class and check for parallel	*/
3149 /* combinations, and combine them where found, adjusting property "M"	*/
3150 /* as needed.								*/
3151 /*									*/
3152 /* Procedure:  Hash each cell by the model name and a space-separated	*/
3153 /* list of node numbers connected to each pin, and the value of the 	*/
3154 /* property that affects number of devices (if any).  The hash stores	*/
3155 /* the instance record and property record (if any) of the first cell.	*/
3156 /* If there is a hash match, then the cell instance gets deleted and	*/
3157 /* the property M of the instance pointed to in the hash gets		*/
3158 /* incremented.	 If it has no property M, one is created and the value	*/
3159 /* set to 2.								*/
3160 /*									*/
3161 /* If the device has permutable pins, then duplicate hashes are made	*/
3162 /* for each permutation.						*/
3163 /*									*/
3164 /* If the device has isolated (unconnected) pins, then treat them as	*/
3165 /* all belonging to the same net for the purpose of parallel merging.	*/
3166 /*									*/
3167 /* Return the number of devices merged.					*/
3168 /*----------------------------------------------------------------------*/
3169 
CombineParallel(char * model,int file)3170 int CombineParallel(char *model, int file)
3171 {
3172    struct nlist *tp, *tsub;
3173    struct objlist *ob, *ob2, *nextob;
3174    struct objlist *sob, *lob, *nob, *pob, *obr;
3175    struct objlist *propfirst, *proplast, *spropfirst, *sproplast;
3176    struct hashdict devdict;
3177    struct Permutation *perm;
3178    size_t pcnt;
3179    int i, dcnt = 0, hastag;
3180    char *pstr, *p2str, *pptr;
3181    int *nodecount;
3182    struct valuelist *kv;
3183 
3184    if ((tp = LookupCellFile(model, file)) == NULL) {
3185       Printf("Cell: %s does not exist.\n", model);
3186       return -1;
3187    }
3188 
3189    InitializeHashTable(&devdict, OBJHASHSIZE);
3190 
3191    /* Make one pass to count the number of times each node number is	*/
3192    /* used.  This list indicates which pins are no-connects, so they	*/
3193    /* can be treated as equivalent for the purpose of parallelization.	*/
3194 
3195    nodecount = (int *)CALLOC((tp->nodename_cache_maxnodenum + 1), sizeof(int));
3196 
3197    /* Do not combine open connections on top level cells.  The situation */
3198    /* where it is meant to optimize run time, namely large digital	 */
3199    /* standard cell layouts, will generally have ports and not be run on */
3200    /* the top level cell, while a small analog circuit might.  A	 */
3201    /* combination of running on the top level and a difference between	 */
3202    /* individual devices in one netlist vs. fingered devices in the	 */
3203    /* can cause parallelizing devices with similar "no-connect" pins to	 */
3204    /* produce incorrect results.					 */
3205 
3206    if (GlobalParallelOpen && !(tp->flags & CELL_TOP)) {
3207       for (ob = tp->cell; ob; ob = ob->next) {
3208          if (ob->node >= 0)
3209             if (ob->type != NODE)
3210 	       nodecount[ob->node]++;
3211       }
3212    }
3213 
3214    lob = NULL;
3215    for (ob = tp->cell; ob; ) {
3216       if (ob->type == FIRSTPIN) {
3217 
3218          /* Watch for devices prohibited from parallel combination.	*/
3219 	 /* All devices allow parallel combination by default.		*/
3220 
3221 	 tsub = LookupCellFile(ob->model.class, file);
3222          if ((tsub != NULL) && (tsub->flags & COMB_NO_PARALLEL)) {
3223             lob = ob;
3224 	    ob = ob->next;
3225 	    continue;
3226          }
3227 
3228 	 /* ------------------------------------*/
3229 	 /* Generate hash key from pins 	*/
3230 	 /* Handle pin permuations		*/
3231 	 /* ------------------------------------*/
3232 
3233 	 if ((tsub != NULL) && (tsub->permutes != NULL))
3234 	    perm = tsub->permutes;
3235 	 else
3236 	    perm = NULL;	/* Device has no pin permutations */
3237 
3238 	 pcnt = strlen(ob->model.class) + 2;
3239 	 pptr = (char *)pcnt;
3240 
3241 	 propfirst = proplast = NULL;
3242 	 for (ob2 = ob; ob2 && (ob2->type > FIRSTPIN || ob2 == ob); ob2 = ob2->next) {
3243 	    pob = ob2;
3244 	    pcnt += 10;
3245 	 }
3246 	 if (ob2 && (ob2->type == PROPERTY)) propfirst = ob2;
3247 
3248 	 /* Find last record in device and first record in next object */
3249 	 while (ob2 && ob2->type == PROPERTY) {
3250 	    proplast = ob2;
3251 	    ob2 = ob2->next;
3252 	 }
3253 	 nextob = ob2;
3254 
3255 	 pstr = (char *)MALLOC(pcnt);
3256 	 sprintf(pstr, "%s", ob->model.class);
3257 	 pptr += pstr - (char *)2;
3258 
3259 	 for (ob2 = ob; ob2 && (ob2->type > FIRSTPIN || ob2 == ob); ob2 = ob2->next) {
3260 	    if ((ob2->node >= 0) && (nodecount[ob2->node] == 1))
3261 	    {
3262 	       nob = (tp->nodename_cache)[ob2->node];
3263 	       nob->flags = NO_CONNECT;
3264 	       strcat(pptr, "_nc");
3265 	    }
3266 	    else
3267 	       sprintf(pptr, "_%d", ob2->node);
3268 	    pptr += strlen(pptr);
3269 	 }
3270 
3271 	 /* Now check the hash table for any similar instance */
3272 	 sob = (struct objlist *)HashLookup(pstr, &devdict);
3273 	 if (sob == NULL) {
3274 	    /* Generate hash entry */
3275 	    HashPtrInstall(pstr, ob, &devdict);
3276 
3277 	    /* If pins are permutable, generate alternative hash entries */
3278 
3279 	    if (perm != NULL) {
3280 		char *pname;
3281 		struct objlist *pob1, *pob2;
3282 
3283 		/* NOTE:  This is only set up for a single permutation	*/
3284 		/* per cell and needs to be expanded to the general	*/
3285 		/* case, which requires one nested loop per permute	*/
3286 		/* pair							*/
3287 
3288 		p2str = (char *)MALLOC(pcnt);
3289 		sprintf(p2str, "%s", ob->model.class);
3290 		pptr = p2str + strlen(ob->model.class);
3291 
3292 		for (ob2 = ob; ob2 && (ob2->type > FIRSTPIN || ob2 == ob);
3293 				ob2 = ob2->next) {
3294 		    pname = ob2->name + strlen(ob2->instance.name) + 1;
3295 		    if ((*matchfunc)(perm->pin1, pname))
3296 			pob1 = ob2;
3297 		    else if ((*matchfunc)(perm->pin2, pname))
3298 			pob2 = ob2;
3299 		}
3300 		for (ob2 = ob; ob2 && (ob2->type > FIRSTPIN || ob2 == ob);
3301 				ob2 = ob2->next) {
3302 		    if (ob2 == pob1)
3303 		    {
3304 	     		if ((pob2->node >= 0) && (nodecount[pob2->node] == 1))
3305 	       		   strcat(pptr, "_nc");
3306 			else
3307 		           sprintf(pptr, "_%d", pob2->node);
3308 		    }
3309 		    else if (ob2 == pob2)
3310 		    {
3311 	     		if ((pob1->node >= 0) && (nodecount[pob1->node] == 1))
3312 	       		   strcat(pptr, "_nc");
3313 			else
3314 		           sprintf(pptr, "_%d", pob1->node);
3315 		    }
3316 		    else
3317 		    {
3318 	     		if ((ob2->node >= 0) && (nodecount[ob2->node] == 1))
3319 	       		   strcat(pptr, "_nc");
3320 			else
3321 		           sprintf(pptr, "_%d", ob2->node);
3322 		    }
3323 		    pptr += strlen(pptr);
3324 		}
3325 		HashPtrInstall(p2str, ob, &devdict);
3326 		FREE((char *)p2str);
3327 	    }
3328 
3329 	    /* Move last object marker to end of sob record */
3330 	    if (proplast != NULL)
3331 	       lob = proplast;
3332 	    else
3333 	       lob = pob;
3334 	 }
3335 	 else {
3336 	    /* Find parallel device "ob" and append properties of	*/
3337 	    /* "sob" to it.  If "ob" does not have properties, then	*/
3338 	    /* create a property record and set property "M" to 1.	*/
3339 
3340 	    /* Find last non-property record of sob ( = pob) */
3341 	    /* Find first property record of sob ( = spropfirst) */
3342 	    /* Find last property record of sob ( = sproplast)	*/
3343 
3344 	    spropfirst = sproplast = NULL;
3345 	    for (ob2 = sob; ob2->type > FIRSTPIN || ob2 == sob; ob2 = ob2->next)
3346 	       pob = ob2;
3347 	    if (ob2->type == PROPERTY) spropfirst = ob2;
3348 	    for (; ob2->type == PROPERTY; ob2 = ob2->next)
3349 	       sproplast = ob2;
3350 
3351             if (spropfirst == NULL) {
3352 	       /* Create new property instance record if one doesn't exist */
3353 	       nob = GetObject();
3354 	       nob->type = PROPERTY;
3355 	       nob->name = strsave("properties");
3356 	       nob->node = -2;	/* Don't report as disconnected node */
3357 	       nob->model.class = strsave(sob->model.class);
3358 	       nob->instance.props = NewPropValue(2);
3359 
3360 	       /* Create property record for property "M" and set to 1 */
3361 	       kv = &(nob->instance.props[0]);
3362 	       kv->key = strsave("M");
3363 	       kv->type = PROP_INTEGER;
3364 	       kv->value.ival = 1;
3365 
3366 	       /* End of property list */
3367 	       kv = &(nob->instance.props[1]);
3368 	       kv->key = NULL;
3369 	       kv->type = PROP_ENDLIST;
3370 	       kv->value.ival = 0;
3371 
3372 	       nob->next = pob->next;
3373 	       pob->next = nob;
3374 
3375 	       if (lob == pob) lob = nob;
3376 	       spropfirst = sproplast = nob;
3377 	    }
3378 	    if (propfirst == NULL) {
3379 	       /* Create new property instance record if one doesn't exist */
3380 	       nob = GetObject();
3381 	       nob->type = PROPERTY;
3382 	       nob->name = strsave("properties");
3383 	       nob->node = -2;	/* Don't report as disconnected node */
3384 	       nob->model.class = strsave(ob->model.class);
3385 	       nob->instance.props = NewPropValue(2);
3386 
3387 	       /* Create property record for property "M" and set to 1 */
3388 	       kv = &(nob->instance.props[0]);
3389 	       kv->key = strsave("M");
3390 	       kv->type = PROP_INTEGER;
3391 	       kv->value.ival = 1;
3392 
3393 	       /* End of property list */
3394 	       kv = &(nob->instance.props[1]);
3395 	       kv->key = NULL;
3396 	       kv->type = PROP_ENDLIST;
3397 	       kv->value.ival = 0;
3398 
3399 	       /* Append to sob's property list */
3400 	       nob->next = sproplast->next;
3401 	       sproplast->next = nob;
3402 	       if (lob == sproplast) lob = nob;
3403 	    }
3404 
3405 	    if (propfirst != NULL) {
3406 
3407 	       // Series/Parallel logic:
3408 	       // If propfirst has _tag in properties,
3409 	       // then add an "open" tag at propfirst
3410                add_prop_tag(propfirst, '(');
3411 
3412 	       // if spropfirst has _tag in properties then add an "open" tag
3413 	       // to spropfirst and a "close" tag to propfirst
3414 	       if (add_prop_tag(spropfirst, '(')) add_prop_tag(propfirst, ')');
3415 
3416 	       /* Append ob's property list to sob */
3417 	       proplast->next = sproplast->next;
3418 	       sproplast->next = propfirst;
3419 	       if (lob == sproplast) lob = proplast;
3420 	    }
3421 
3422             /* Link up around object to be removed */
3423 	    lob->next = nextob;
3424 
3425 	    /* Remove the object */
3426 	    for (obr = ob; obr != propfirst && obr != nextob; ) {
3427 	       nob = obr->next;
3428 	       FreeObjectAndHash(obr, tp);
3429 	       obr = nob;
3430 	    }
3431 	    dcnt++;
3432 
3433 	 }
3434 	 FREE((char *)pstr);
3435       }
3436       else {
3437          lob = ob;
3438 	 nextob = ob->next;
3439       }
3440       ob = nextob;
3441    }
3442    HashKill(&devdict);
3443    if (dcnt > 0) {
3444       Fprintf(stdout, "Class %s(%d):  Merged %d parallel devices.\n", model, file, dcnt);
3445    }
3446    FREE(nodecount);
3447    return dcnt;
3448 }
3449 
3450 /*----------------------------------------------------------------------*/
3451 /* For the purposes of series connection checking, find if all pins	*/
3452 /* of two instances after the first two pins are connected to the name	*/
3453 /* nodes.  This depends on the definition of a series device as having	*/
3454 /* two ports, but any additional ports (such as a substrate connection)	*/
3455 /* must be the same for all devices in series.				*/
3456 /*----------------------------------------------------------------------*/
3457 
check_pin_nodes(struct objlist * ob1,struct objlist * ob2)3458 int check_pin_nodes(struct objlist *ob1, struct objlist *ob2)
3459 {
3460    struct objlist *nob, *pob;
3461 
3462    /* A dummy device may have both terminals connected to the same	*/
3463    /* point, triggering a false check for a series device.		*/
3464    if (ob1 == ob2) return FALSE;
3465 
3466    for (nob = ob1->next; nob && nob->type != FIRSTPIN; nob = nob->next)
3467       if (nob->type == 3) break;
3468 
3469    for (pob = ob2->next; pob && pob->type != FIRSTPIN; pob = pob->next)
3470       if (pob->type == 3) break;
3471 
3472    while (nob && pob && nob->type > FIRSTPIN && pob->type > FIRSTPIN) {
3473       if (nob->node != pob->node)
3474           return FALSE;
3475       nob = nob->next;
3476       pob = pob->next;
3477    }
3478 
3479    if (nob && pob && (nob->type > FIRSTPIN || pob->type > FIRSTPIN))
3480       return FALSE;
3481 
3482    return TRUE;
3483 }
3484 
3485 /*----------------------------------------------------------------------*/
3486 /* Find all nodes that are connected to exactly two devices of the same	*/
3487 /* class.  Where found, if the device is allowed to combine serially	*/
3488 /* (check properties), then remove the node and merge the devices into	*/
3489 /* one. 								*/
3490 /*									*/
3491 /* This routine depends on CombineParallel() being run first so that no	*/
3492 /* parallel devices are reported as series.				*/
3493 /*									*/
3494 /* Return the number of devices merged.					*/
3495 /*----------------------------------------------------------------------*/
3496 
CombineSeries(char * model,int file)3497 int CombineSeries(char *model, int file)
3498 {
3499    struct nlist *tp, *tp2;
3500    struct objlist ***instlist;
3501    struct objlist *ob, *ob2, *obs, *obp, *obn;
3502    int i, j, scnt = 0;
3503    struct valuelist *kv;
3504 
3505    if ((tp = LookupCellFile(model, file)) == NULL) {
3506       Printf("Cell: %s does not exist.\n", model);
3507       return -1;
3508    }
3509    /* Diagnostic */
3510    /* Printf("CombineSeries start model = %s file = %d\n", model, file); */
3511 
3512    instlist = (struct objlist ***)CALLOC((tp->nodename_cache_maxnodenum + 1),
3513 		sizeof(struct objlist **));
3514 
3515    for (ob = tp->cell; ob; ob = ob->next) {
3516       if ((ob->type >= FIRSTPIN) && (ob->node >= 0)) {
3517           if (ob->type == FIRSTPIN)
3518 	     obp = ob;	// Save pointer to first pin of device
3519 
3520           if (instlist[ob->node] == NULL) {
3521              /* Node has not been seen before, so add it to list */
3522 	     instlist[ob->node] = (struct objlist **)CALLOC(2,
3523 			sizeof(struct objlist *));
3524 
3525 	     /* Device must be marked as able to be combined in	series.	*/
3526 	     /* Note that devices with more than two pins are expected	*/
3527 	     /* to series connect along the first two pins, and the 	*/
3528 	     /* remaining pins must all connect to the same nodes.  By	*/
3529 	     /* default, CLASS_RES, CLASS_RES3, and CLASS_INDUCTOR are	*/
3530 	     /* all allowed to combine in series.  All other devices	*/
3531 	     /* must have series combination explicitly enabled.	*/
3532 	     /* NOTE:  Arbitrarily, the first two pins of a device are	*/
3533 	     /* assumed to be the ones that make series connections.	*/
3534 	     /* Additional pins, if any, do not.			*/
3535 
3536              tp2 = LookupCellFile(ob->model.class, file);
3537              if ((tp2->flags & COMB_SERIES) && (ob->type <= 2)) {
3538 	         instlist[ob->node][0] = obp;
3539 
3540 		 /* Node may not be a port of the subcircuit */
3541 		 for (obn = tp->cell; obn && obn->type == PORT; obn = obn->next) {
3542 		     if (obn->node == ob->node) {
3543 			/* invalidate node */
3544 			instlist[ob->node][0] = NULL;
3545 			break;
3546 		     }
3547 		 }
3548 	     }
3549 	     else
3550 	         /* invalidate node */
3551 	         instlist[ob->node][0] = NULL;
3552           }
3553           else if (instlist[ob->node][0] == NULL) {
3554              /* Node is not valid for series connection */
3555           }
3556           else if (instlist[ob->node][1] == NULL) {
3557              /* Check if first instance is the same type */
3558              if ((*matchfunc)(instlist[ob->node][0]->model.class, ob->model.class)) {
3559                  if (check_pin_nodes(instlist[ob->node][0], obp))
3560 	             instlist[ob->node][1] = obp;
3561 		 else
3562 		    /* invalidate node */
3563 		    instlist[ob->node][0] = NULL;
3564 	     }
3565 	     else
3566 		/* invalidate node */
3567 		instlist[ob->node][0] = NULL;
3568           }
3569 	  else {
3570 	     /* More than two devices connect here, so invalidate */
3571 	     instlist[ob->node][0] = NULL;
3572 	  }
3573       }
3574    }
3575    for (i = 0; i <= tp->nodename_cache_maxnodenum; i++) {
3576       if (instlist[i] != NULL) {
3577          if ((instlist[i][0] != NULL) && (instlist[i][1] != NULL)) {
3578 	    int k, l;
3579 	    struct valuelist *kv2;
3580 
3581             /* Diagnostic */
3582 	    /* Fprintf(stdout, "Found series instances %s and %s\n",
3583 			instlist[i][0]->instance.name,
3584 			instlist[i][1]->instance.name); */
3585             scnt++;
3586 
3587 	    /* To maintain knowledge of the topology, 2nd device gets	*/
3588 	    /* a parameter '_tag', string value set to "+".		*/
3589 
3590             for (obn = instlist[i][1]; obn->next &&
3591 			obn->next->type > FIRSTPIN; obn = obn->next);
3592 	    obp = obn->next;
3593 
3594 	    if (obp == NULL || obp->type != PROPERTY) {
3595 	       struct objlist *nob;
3596 	       /* No property record, so insert one */
3597 	       nob = GetObject();
3598 	       nob->type = PROPERTY;
3599 	       nob->name = strsave("properties");
3600 	       nob->node = -2;	/* Don't report as disconnected node */
3601 	       nob->model.class = (obp->model.class == NULL) ? NULL :
3602 				strsave(obp->model.class);
3603 	       nob->instance.props = NewPropValue(2);
3604 
3605 	       /* Create property record for property "_tag" */
3606 	       kv = &(nob->instance.props[0]);
3607 	       kv->key = strsave("_tag");
3608 	       kv->type = PROP_STRING;
3609 	       /* Value is set to "+" */
3610 	       kv->value.string = (char *)MALLOC(2);
3611 	       sprintf(kv->value.string, "+");
3612 
3613 	       /* End of property list */
3614 	       kv = &(nob->instance.props[1]);
3615 	       kv->key = NULL;
3616 	       kv->type = PROP_ENDLIST;
3617 	       kv->value.ival = 0;
3618 
3619 	       nob->next = obp;
3620 	       obn->next = nob;
3621 	    }
3622 	    else if (obp->type == PROPERTY) {
3623 	       /* Add to properties */
3624 	       k = 0;
3625 	       for (k = 0; ; k++) {
3626 		  kv = &(obp->instance.props[k]);
3627 		  if (kv->type == PROP_ENDLIST) {
3628 		     kv2 = (struct valuelist *)MALLOC((k + 2) *
3629 				sizeof(struct valuelist));
3630 		     kv2->key = strsave("_tag");
3631 		     kv2->type = PROP_STRING;
3632 		     /* Value is set to "+" */
3633 		     kv2->value.string = (char *)MALLOC(2);
3634 		     sprintf(kv2->value.string, "+");
3635 		     for (l = 0; l <= k; l++)
3636 		        kv2[l + 1] = obp->instance.props[l];
3637 		     FREE(obp->instance.props);
3638 		     obp->instance.props = kv2;
3639 		     break;
3640 		  }
3641 		  else if (!strcmp(kv->key, "_tag")) {
3642 		     int l = strlen(kv->value.string);
3643 		     char *newstr = (char *)MALLOC(l + 2);
3644 		     sprintf(newstr, "S%s", kv->value.string);
3645 		     FREE(kv->value.string);
3646 		     kv->value.string = newstr;
3647 		     break;
3648 		  }
3649 	       }
3650 	    }
3651 
3652             /* Combine these two instances and remove node i */
3653             for (ob2 = instlist[i][0]; ob2; ob2 = ob2->next) {
3654 	       if (ob2->node == i)
3655 		  break;
3656 	    }
3657             for (obs = instlist[i][1]; obs; obs = obs->next) {
3658 	       if (obs->node != i) {
3659 		  ob2->node = obs->node;
3660 		  break;
3661 	       }
3662 	    }
3663 
3664 	    /* Excise the 2nd instance.  instlist[i][1] remains as the	*/
3665 	    /* only pointer to it.					*/
3666             for (obp = instlist[i][0]; obp->next && (obp->next->type > FIRSTPIN ||
3667 			obp->next->type == PROPERTY); obp = obp->next);
3668             for (ob2 = obp; ob2 && ob2->next != instlist[i][1]; ob2 = ob2->next);
3669 
3670 	    /* Device may have been moved by the above code.  If so, look for	*/
3671 	    /* it from the beginning of the list. */
3672 	    if ((ob2 == NULL) && (tp->cell == instlist[i][1]))
3673 	    {
3674 	       ob2 = tp->cell;
3675 	       for (obs = ob2; obs->next && (obs->next->type > FIRSTPIN
3676 			|| obs->next->type == PROPERTY); obs = obs->next);
3677 	       tp->cell = obs->next;
3678 	    }
3679 	    else
3680 	    {
3681 	       if (ob2 == NULL)
3682                   for (ob2 = tp->cell; ob2->next != instlist[i][1]; ob2 = ob2->next);
3683 	       for (obs = ob2->next; obs->next && (obs->next->type > FIRSTPIN
3684 			|| obs->next->type == PROPERTY); obs = obs->next);
3685 	       ob2->next = obs->next;
3686 	    }
3687 	    if (obs->next) obs->next = NULL;	// Terminate 2nd instance record
3688 
3689 	    /* If 1st device has unbalanced 'open' records, then add 'close'	*/
3690 	    /* records to the 2nd device to balance.				*/
3691             add_balancing_close(instlist[i][0], instlist[i][1]);
3692 
3693 	    /* Move property record(s) of the 2nd device to the first */
3694 	    for (obs = instlist[i][1]; obs && obs->type != PROPERTY; obs = obs->next);
3695 	    while (obs && (obs->type == PROPERTY)) {
3696 	       obn = obs->next;
3697 	       obs->next = obp->next;
3698 	       obp->next = obs;
3699 	       obs = obn;
3700 	    }
3701 
3702 	    /* If 2nd device appears anywhere else in the series device	*/
3703 	    /* list, replace it with the 1st device.			*/
3704 	    for (j = i + 1; j <= tp->nodename_cache_maxnodenum; j++) {
3705                if (instlist[j] == NULL) continue;
3706 
3707 	       if (instlist[j][0] == instlist[i][1])
3708 		  instlist[j][0] = instlist[i][0];
3709 	       if (instlist[j][1] == instlist[i][1])
3710 		  instlist[j][1] = instlist[i][0];
3711 
3712                /* If instlist[j]'s two entries point to the same device */
3713 	       /* then invalidate it.					*/
3714 	       if (instlist[j][0] == instlist[j][1]) {
3715 		  FREE(instlist[j]);
3716 		  instlist[j] = NULL;
3717 	       }
3718 	    }
3719 
3720 	    /* Free 2nd device's object */
3721 	    for (obs = instlist[i][1]; obs && obs->type != PROPERTY; ) {
3722                obn = obs->next;
3723 	       FreeObjectAndHash(obs, tp);
3724 	       obs = obn;
3725 	    }
3726 
3727 	    /* Free node i and remove from object hash */
3728 	    for (obp = tp->cell; obp->next; obp = obp->next) {
3729 	       if ((obp->next->type == NODE) && (obp->next->node == i)) {
3730 	          obn = obp->next;
3731 		  obp->next = obp->next->next;
3732 		  FreeObjectAndHash(obn, tp);
3733 	          break;
3734 	       }
3735 	    }
3736          }
3737          FREE(instlist[i]);
3738       }
3739    }
3740    FREE(instlist);
3741    if (scnt > 0) {
3742       Fprintf(stdout, "Class %s(%d):  Merged %d series devices.\n", model, file, scnt);
3743    }
3744    return scnt;
3745 }
3746 
3747 /*----------------------------------------------------------------------*/
3748 /*----------------------------------------------------------------------*/
3749 
EndCell(void)3750 void EndCell(void)
3751 {
3752   char buffer1[1024];
3753   char prefix[10];
3754 
3755   if (CurrentCell == NULL) return;
3756 
3757   if (Composition == HORIZONTAL) {
3758     if (LastPlaced != NULL) {
3759       sprintf(buffer1,"%s%s%s%s%s",
3760 	      LastPlaced->instance.name, SEPARATOR, "E", PORT_DELIMITER, "*");
3761       sprintf(prefix,"%s%s", "E", PORT_DELIMITER);
3762       PortList(prefix,buffer1);
3763     }
3764   }
3765   else if (Composition == VERTICAL) { /* vcomposing */
3766     if (LastPlaced != NULL) {
3767       sprintf(buffer1,"%s%s%s%s%s",
3768 	      LastPlaced->instance.name, SEPARATOR, "N", PORT_DELIMITER, "*");
3769       sprintf(prefix,"%s%s", "N", PORT_DELIMITER);
3770       PortList(prefix,buffer1);
3771     }
3772   }
3773   LastPlaced = NULL;
3774   CacheNodeNames(CurrentCell);
3775   if (NoDisconnectedNodes)  ConnectAllNodes(CurrentCell->name, CurrentCell->file);
3776   CurrentCell = NULL;
3777   CurrentTail = NULL;
3778 }
3779