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