1 /*----------------------------------------------------------------------*/
2 /* netlist.c --- xcircuit routines specific to schematic capture and */
3 /* netlist generation */
4 /*----------------------------------------------------------------------*/
5 /* Copyright (c) 2004 Tim Edwards, Johns Hopkins University, */
6 /* MultiGiG, Inc., and Open Circuit Design, Inc. */
7 /* Copyright (c) 2005 Tim Edwards, MultiGiG, Inc. */
8 /* */
9 /* Written April 1998 to January 2004 */
10 /* Original version for Pcb netlisting 3/20/98 by Chow Seong Hwai, */
11 /* Leeds University, U.K. */
12 /* */
13 /* Greatly modified 4/2/04 to handle bus notation; net identifier */
14 /* changed from a listing by net to two listings, by polygon and by */
15 /* label, with nets and subnets identifiers attached to each element, */
16 /* rather than vice versa. */
17 /*----------------------------------------------------------------------*/
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <ctype.h>
23
24 #include <sys/types.h> /* For preventing multiple file inclusions, use stat() */
25 #include <sys/stat.h>
26 #ifndef _MSC_VER
27 #include <unistd.h>
28 #endif
29
30 #ifdef HAVE_PYTHON
31 #include <Python.h>
32 #endif
33
34 #ifndef _MSC_VER
35 #include <X11/Intrinsic.h>
36 #include <X11/StringDefs.h>
37 #endif
38
39 #ifdef TCL_WRAPPER
40 #include <tk.h>
41 #endif
42
43 /*----------------------------------------------------------------------*/
44 /* Local includes */
45 /*----------------------------------------------------------------------*/
46
47 #include "xcircuit.h"
48 #include "colordefs.h"
49
50 /*----------------------------------------------------------------------*/
51 /* Function prototype declarations */
52 /*----------------------------------------------------------------------*/
53 #include "prototypes.h"
54
55 #ifdef HAVE_PYTHON
56 extern PyObject *PyGetStringParts(stringpart *);
57 #endif
58 #ifdef TCL_WRAPPER
59 extern Tcl_Interp *xcinterp;
60 extern Tcl_Obj *TclGetStringParts(stringpart *);
61 #endif
62
63 /*----------------------------------------------------------------------*/
64 /* Externally declared global variables */
65 /*----------------------------------------------------------------------*/
66
67 extern Display *dpy;
68
69 /*----------------------------------------------------------------------*/
70
71 extern char _STR[150];
72 extern char _STR2[250];
73 extern XCWindowData *areawin;
74 extern Globaldata xobjs;
75 extern Boolean load_in_progress;
76 extern int number_colors;
77 extern colorindex *colorlist;
78
79 LabellistPtr global_labels;
80
81 typedef struct _flatindex *fidxptr;
82
83 typedef struct _flatindex {
84 char *devname;
85 u_int index;
86 fidxptr next;
87 } flatindex; /* count for labeling devices in a flattened file */
88
89 flatindex *flatrecord = NULL;
90
91 static char *spice_devname = "X"; /* SPICE subcircuit device name */
92 Boolean spice_end = True; /* whether or not to write a .end statement */
93 ino_t *included_files = NULL; /* Files included with "%F" escape */
94
95 #define EndPoint(n) (((n == 1) ? 1 : (int)(n - 1)))
96 #define NextPoint(n) (((n == 1) ? 0 : 1))
97
98 /* For bus matching: whether to match by number of nets, by */
99 /* exact match of sub-bus numbers, or an exact match of net IDs. */
100
101 #define MATCH_EXACT 0
102 #define MATCH_SUBNETS 1
103 #define MATCH_SIZE 2
104 #define MATCH_OVERLAP 3
105
106 /*----------------------------------------------------------------------*/
107 /* Check whether two line segments attach to or cross each other */
108 /* Return 1 if attached, 0 if not (INLINE code) */
109 /* int onsegment(XPoint *a, XPoint *b, XPoint *x) {} */
110 /*----------------------------------------------------------------------*/
111
112 #define ONDIST 4 /* "slack" in algorithm for almost-touching lines */
113 #define onsegment(a, b, x) (finddist(a, b, x) <= ONDIST)
114
115 /*--------------------------------------------------------------*/
116 /* d36a: Base 36 to string conversion */
117 /*--------------------------------------------------------------*/
118
d36a(int number)119 char *d36a(int number)
120 {
121 static char bconv[10];
122 int i, locn, rem;
123
124
125 bconv[9] = '\0';
126 i = 8;
127 locn = number;
128 while ((locn > 0) && (i >= 0)) {
129 rem = locn % 36;
130 locn /= 36;
131 bconv[i--] = (rem < 10) ? (rem + '0') : (rem - 10 + 'A');
132 }
133 return &bconv[i + 1];
134 }
135
136 /*--------------------------------------------------------------*/
137 /* Translate a pin name to a position relative to an object's */
138 /* point of origin. This is used by the ASG module to find */
139 /* the placement of pins based on names from a netlist. */
140 /* */
141 /* returns 0 on success and fills x_ret and y_ret with the */
142 /* pin position coordinates. Returns -1 if the pin name did */
143 /* not match any label names in the object. */
144 /*--------------------------------------------------------------*/
145
NameToPinLocation(objinstptr thisinst,char * pinname,int * x_ret,int * y_ret)146 int NameToPinLocation(objinstptr thisinst, char *pinname, int *x_ret, int *y_ret)
147 {
148 objectptr thisobj = thisinst->thisobject;
149 genericptr *pgen;
150 labelptr plab;
151
152 if (thisobj->schemtype == SECONDARY)
153 thisobj = thisobj->symschem;
154
155 for (pgen = thisobj->plist; pgen < thisobj->plist + thisobj->parts; pgen++) {
156 if (IS_LABEL(*pgen)) {
157 plab = TOLABEL(pgen);
158 if (plab->pin != False && plab->pin != INFO) {
159 if (!textcomp(plab->string, pinname, thisinst)) {
160 *x_ret = (int)plab->position.x;
161 *y_ret = (int)plab->position.y;
162 return 0;
163 }
164 }
165 }
166 }
167 return -1;
168 }
169
170 /*--------------------------------------------------------------*/
171 /* Translate a point position back to the calling object's */
172 /* coordinate system. */
173 /* Original value is passed in "thispoint"; Translated value */
174 /* is put in "refpoint". */
175 /*--------------------------------------------------------------*/
176
ReferencePosition(objinstptr thisinst,XPoint * thispoint,XPoint * refpoint)177 void ReferencePosition(objinstptr thisinst, XPoint *thispoint, XPoint *refpoint)
178 {
179 /* objectptr thisobj = thisinst->thisobject; (jdk) */
180 Matrix locctm;
181
182 /* Translate pin position back to originating object */
183
184 UResetCTM(&locctm);
185 UPreMultCTM(&locctm, thisinst->position, thisinst->scale,
186 thisinst->rotation);
187 UTransformbyCTM(&locctm, thispoint, refpoint, 1);
188 }
189
190 /*--------------------------------------------------------------*/
191 /* Variant of NameToPinLocation(): Given a port number, find */
192 /* the pin associated with that port. */
193 /*--------------------------------------------------------------*/
194
PortToLabel(objinstptr thisinst,int portno)195 labelptr PortToLabel(objinstptr thisinst, int portno)
196 {
197 labelptr plab;
198 objectptr thisobj = thisinst->thisobject;
199 PortlistPtr ports;
200
201 if ((thisobj->schemtype == SYMBOL) && (thisobj->symschem != NULL))
202 ports = thisobj->symschem->ports;
203 else
204 ports = thisobj->ports;
205
206 for (; ports != NULL; ports = ports->next) {
207 if (ports->portid == portno) {
208 plab = NetToLabel(ports->netid, thisobj); /* in the symbol! */
209 return plab;
210 }
211 }
212 return NULL;
213 }
214
215 /*--------------------------------------------------------------*/
216 /* This function is the same as PortToLocalPosition() but */
217 /* returns the point in the coordinate system of the parent. */
218 /*--------------------------------------------------------------*/
219
PortToPosition(objinstptr thisinst,int portno,XPoint * refpoint)220 Boolean PortToPosition(objinstptr thisinst, int portno, XPoint *refpoint)
221 {
222 labelptr plab = PortToLabel(thisinst, portno);
223 if (plab)
224 ReferencePosition(thisinst, &(plab->position), refpoint);
225
226 return (plab != NULL) ? TRUE : FALSE;
227 }
228
229 /*----------------------------------------------------------------------*/
230 /* Get the hierarchy of the page stack and return as a string */
231 /*----------------------------------------------------------------------*/
232
getnexthier(pushlistptr stack,char ** hierstr,objinstptr callinst,Boolean canonical)233 Boolean getnexthier(pushlistptr stack, char **hierstr, objinstptr callinst,
234 Boolean canonical)
235 {
236 objectptr topobj, thisobj;
237 CalllistPtr calls;
238 /* char numstr[12]; (jdk) */
239 int hierlen, devlen;
240 char *devstr;
241
242 if (!stack) return False;
243
244 /* Recurse so we build up the prefix string from top down */
245 if (stack->next != NULL) {
246 if (getnexthier(stack->next, hierstr, stack->thisinst, canonical) == False)
247 return False;
248 }
249 else {
250 topobj = stack->thisinst->thisobject;
251
252 if (topobj->schemtype != PRIMARY && topobj->symschem != NULL)
253 topobj = topobj->symschem;
254
255 if (topobj->calls == NULL) {
256 if (topobj->schemtype == FUNDAMENTAL) {
257 /* Top level is a fundamental symbol */
258 return True;
259 }
260 else if ((updatenets(stack->thisinst, FALSE) <= 0) || (topobj->calls == NULL)) {
261 Wprintf("Error in generating netlists!");
262 return False;
263 }
264 }
265 }
266
267 thisobj = stack->thisinst->thisobject;
268
269 /* When we have "traveled" through both a symbol and its */
270 /* associated schematic, only append the prefix for the */
271 /* first one encountered. */
272
273 /*
274 if (thisobj->symschem != NULL && stack->next != NULL &&
275 thisobj->symschem == stack->next->thisinst->thisobject)
276 return True;
277 */
278
279 if ((thisobj->calls == NULL) && (thisobj->schemtype != PRIMARY)
280 && (thisobj->symschem != NULL))
281 thisobj = thisobj->symschem;
282
283 /* Check for resolved device indices and generate them if necessary */
284
285 for (calls = thisobj->calls; calls != NULL; calls = calls->next)
286 if (calls->callinst == callinst)
287 if (calls->devindex == -1) {
288 cleartraversed(thisobj);
289 resolve_indices(thisobj, FALSE);
290 break;
291 }
292
293 /* Add this level of the hierarchy to the prefix string */
294
295 for (calls = thisobj->calls; calls != NULL; calls = calls->next) {
296 if (calls->callinst == callinst) {
297 devlen = (canonical || calls->devname == NULL) ?
298 strlen(callinst->thisobject->name) : strlen(calls->devname);
299 devstr = d36a(calls->devindex);
300 devlen += strlen(devstr) + 1;
301 if (*hierstr == NULL) {
302 *hierstr = malloc(devlen);
303 hierlen = 0;
304 }
305 else {
306 hierlen = strlen(*hierstr) + 2;
307 *hierstr = realloc(*hierstr, hierlen + devlen);
308 }
309 if (canonical)
310 sprintf(*hierstr + hierlen, "%s%s(%s)",
311 ((hierlen > 0) ? "/" : ""),
312 callinst->thisobject->name, devstr);
313 else
314 sprintf(*hierstr + hierlen, "%s%s%s",
315 ((hierlen > 0) ? "/" : ""),
316 (calls->devname == NULL) ? callinst->thisobject->name
317 : calls->devname, devstr);
318 break;
319 }
320 }
321 return True;
322 }
323
324 /*----------------------------------------------------------------------*/
325
GetHierarchy(pushlistptr * stackptr,Boolean canonical)326 char *GetHierarchy(pushlistptr *stackptr, Boolean canonical)
327 {
328 Boolean pushed_top = FALSE;
329 char *snew = NULL;
330
331 if ((*stackptr) && ((*stackptr)->thisinst != areawin->topinstance)) {
332 pushed_top = TRUE;
333 push_stack(stackptr, areawin->topinstance, NULL);
334 }
335
336 getnexthier(*stackptr, &snew, NULL, canonical);
337
338 if (pushed_top) pop_stack(stackptr);
339
340 return snew;
341 }
342
343 /*----------------------------------------------------------------------*/
344 /* Invalidate a netlist. The invalidation must be referred to the */
345 /* master schematic, if the object is a secondary schematic. */
346 /*----------------------------------------------------------------------*/
347
invalidate_netlist(objectptr thisobject)348 void invalidate_netlist(objectptr thisobject)
349 {
350 if (thisobject->schemtype != NONETWORK) {
351 if (thisobject->schemtype == SECONDARY)
352 thisobject->symschem->valid = False;
353 else
354 thisobject->valid = False;
355 }
356 }
357
358 /*--------------------------------------------------------------*/
359 /* Check if the selected items are relevant to the netlist. */
360 /* Only invalidate the current netlist if they are. */
361 /*--------------------------------------------------------------*/
362
select_invalidate_netlist()363 void select_invalidate_netlist()
364 {
365 int i;
366 Boolean netcheck = FALSE;
367
368 for (i = 0; i < areawin->selects; i++) {
369 genericptr gptr = SELTOGENERIC(areawin->selectlist + i);
370 switch (gptr->type) {
371 case POLYGON:
372 if (!nonnetwork(TOPOLY(&gptr)))
373 netcheck = TRUE;
374 break;
375 case LABEL:
376 if ((TOLABEL(&gptr))->pin == LOCAL || (TOLABEL(&gptr))->pin == GLOBAL)
377 netcheck = TRUE;
378 break;
379 case OBJINST:
380 if ((TOOBJINST(&gptr))->thisobject->schemtype != NONETWORK)
381 netcheck = TRUE;
382 break;
383 }
384 }
385 if (netcheck) invalidate_netlist(topobject);
386 }
387
388 /*------------------------------------------------------------------*/
389 /* Check proximity of two points (within roundoff tolerance ONDIST) */
390 /*------------------------------------------------------------------*/
391
proximity(XPoint * point1,XPoint * point2)392 Boolean proximity(XPoint *point1, XPoint *point2)
393 {
394 int dx, dy;
395
396 dx = point1->x - point2->x;
397 dy = point1->y - point2->y;
398
399 if ((abs(dx) < ONDIST) && (abs(dy) < ONDIST)) return True;
400 else return False;
401 }
402
403 /*----------------------------------------------------------------------*/
404 /* createnets(): Generate netlist structures */
405 /* */
406 /* Result is the creation of three linked lists inside each object in */
407 /* the circuit hierarchy: */
408 /* */
409 /* 1) the netlist: assigns a number to each network of polygons and */
410 /* pin labels on the object. */
411 /* 2) the ports: a list of every network which is connected from */
412 /* objects above this one in the hierarchy. */
413 /* 3) the calls: a list of calls made from the object to all sub- */
414 /* circuits. Each calls indicates the object and/or */
415 /* instance being called, and a port list which must match */
416 /* the ports of the object being called (the port lists */
417 /* are not reflexive, as the caller does not have to call */
418 /* all of the instance's ports, but the instance must have */
419 /* a port defined for every connection being called). */
420 /* (see structure definitions in xcircuit.h). */
421 /*----------------------------------------------------------------------*/
422
createnets(objinstptr thisinst,Boolean quiet)423 void createnets(objinstptr thisinst, Boolean quiet)
424 {
425 objectptr thisobject = thisinst->thisobject;
426
427 if (!setobjecttype(thisobject)) {
428
429 /* New in 3.3.32: Generating a netlist from a symbol is */
430 /* okay if the symbol has an associated netlist. */
431
432 if (thisobject->schemtype == SYMBOL && thisobject->symschem != NULL)
433 thisobject = thisobject->symschem;
434 else {
435 if (!quiet)
436 Wprintf("Error: attempt to generate netlist for a symbol.");
437 return;
438 }
439 }
440
441
442 /* Wprintf("Generating netlists"); */ /* Diagnostic */
443 gennetlist(thisinst);
444 gencalls(thisobject);
445 cleartraversed(thisobject);
446 resolve_devnames(thisobject);
447 /* Wprintf("Finished netlists"); */
448
449 }
450
451 /*----------------------------------------------------------------------*/
452 /* Free all memory associated with the netlists. */
453 /*----------------------------------------------------------------------*/
454
destroynets(objectptr thisobject)455 void destroynets(objectptr thisobject)
456 {
457 objectptr pschem;
458
459 pschem = (thisobject->schemtype == SECONDARY) ? thisobject->symschem :
460 thisobject;
461
462 freetemplabels(pschem);
463 freenets(pschem);
464 freeglobals();
465 }
466
467 /*----------------------------------------------------------------------*/
468 /* Polygon types which are ignored when considering if a polygon */
469 /* belongs to a circuit network: */
470 /* Closed polygons (boxes) */
471 /* Bounding box polygons */
472 /* Filled polygons */
473 /* Dashed and dotted line-style polygons */
474 /*----------------------------------------------------------------------*/
475
nonnetwork(polyptr cpoly)476 Boolean nonnetwork(polyptr cpoly)
477 {
478 if (!(cpoly->style & UNCLOSED)) return True;
479 if (cpoly->style & (DASHED | DOTTED | FILLSOLID | BBOX))
480 return True;
481 return False;
482 }
483
484 /*----------------------------------------------------------------------*/
485 /* Return the largest (most negative) net number in the global netlist */
486 /*----------------------------------------------------------------------*/
487
globalmax()488 int globalmax()
489 {
490 LabellistPtr gl;
491 int bidx, sbus;
492 buslist *lbus;
493 int smin = 0;
494
495 for (gl = global_labels; gl != NULL; gl = gl->next) {
496 if (!(gl->subnets)) {
497 if (gl->net.id < smin)
498 smin = gl->net.id;
499 }
500 else {
501 for (bidx = 0; bidx < gl->subnets; bidx++) {
502 lbus = gl->net.list + bidx;
503 sbus = lbus->netid;
504 if (sbus < smin)
505 smin = sbus;
506 }
507 }
508 }
509 return smin;
510 }
511
512 /*----------------------------------------------------------------------*/
513 /* Return the largest net number in an object's netlist */
514 /*----------------------------------------------------------------------*/
515
netmax(objectptr cschem)516 int netmax(objectptr cschem)
517 {
518 PolylistPtr gp;
519 LabellistPtr gl;
520 int bidx, sbus;
521 buslist *lbus;
522 int smax = 0;
523
524 for (gp = cschem->polygons; gp != NULL; gp = gp->next) {
525 if (!(gp->subnets)) {
526 if (gp->net.id > smax)
527 smax = gp->net.id;
528 }
529 else {
530 for (bidx = 0; bidx < gp->subnets; bidx++) {
531 lbus = gp->net.list + bidx;
532 sbus = lbus->netid;
533 if (sbus > smax)
534 smax = sbus;
535 }
536 }
537 }
538 for (gl = cschem->labels; gl != NULL; gl = gl->next) {
539 if (!(gl->subnets)) {
540 if (gl->net.id > smax)
541 smax = gl->net.id;
542 }
543 else {
544 for (bidx = 0; bidx < gl->subnets; bidx++) {
545 lbus = gl->net.list + bidx;
546 sbus = lbus->netid;
547 if (sbus > smax)
548 smax = sbus;
549 }
550 }
551 }
552 return smax;
553 }
554
555 /*----------------------------------------------------------------------*/
556 /* Resolve nets and pins for the indicated object */
557 /* */
558 /* When encountering object instances, call gennetlist() on the object */
559 /* if it does not have a valid netlist, then recursively call */
560 /* gennetlist(). Ignore "info" labels, which are not part of the */
561 /* network. */
562 /* */
563 /*----------------------------------------------------------------------*/
564
gennetlist(objinstptr thisinst)565 void gennetlist(objinstptr thisinst)
566 {
567 genericptr *cgen;
568 labelptr olabel, clab;
569 polyptr cpoly, tpoly;
570 objectptr thisobject, callobj, cschem, pschem;
571 objinstptr cinst, labinst;
572 int old_parts; /* netid, tmpid, sub_bus, (jdk) */
573 stringpart *cstr;
574 Boolean visited;
575
576 XPoint *tpt, *tpt2, *endpt, *endpt2;
577 int i, j, n, nextnet, lbus;
578 buslist *sbus;
579 PolylistPtr plist;
580 LabellistPtr lseek;
581 Genericlist *netlist, *tmplist, *buspins, *resolved_net, newlist;
582
583 newlist.subnets = 0;
584 newlist.net.id = -1;
585
586 /* Determine the type of object being netlisted */
587 thisobject = thisinst->thisobject;
588 setobjecttype(thisobject);
589
590 if (thisobject->schemtype == NONETWORK) return;
591
592 /* Has this object been visited before? */
593 visited = ((thisobject->labels != NULL) || (thisobject->polygons != NULL)) ?
594 TRUE : FALSE;
595
596 if (!visited && (thisobject->schemtype == SYMBOL)
597 && (thisobject->symschem != NULL)) {
598
599 /* Make sure that schematics are netlisted before their symbols */
600
601 if ((thisobject->symschem->polygons == NULL) &&
602 (thisobject->symschem->labels == NULL)) {
603 n = is_page(thisobject->symschem);
604 if (n == -1) {
605 Fprintf(stderr, "Error: associated schematic is not a page!\n");
606 return;
607 }
608 else
609 gennetlist(xobjs.pagelist[n]->pageinst);
610 }
611
612 /* Sanity check on symbols with schematics: Are there any pins? */
613
614 for (i = 0; i < thisobject->parts; i++) {
615 cgen = thisobject->plist + i;
616 if (IS_LABEL(*cgen)) {
617 clab = TOLABEL(cgen);
618 if (clab->pin != False && clab->pin != INFO)
619 break;
620 }
621 }
622 if (i == thisobject->parts)
623 /* Don't warn if schematic has infolabels */
624 if (thisobject->symschem == NULL || thisobject->symschem->infolabels == 0)
625 Fprintf(stderr, "Warning: Symbol %s has no pins!\n", thisobject->name);
626 }
627
628 /* If this is a secondary schematic, run on the primary (master) */
629 pschem = (thisobject->schemtype == SECONDARY) ? thisobject->symschem :
630 thisobject;
631
632 nextnet = netmax(pschem) + 1;
633
634 /* We start the loop for schematics but will modify the loop */
635 /* variable to execute just once in the case of a symbol. */
636 /* It's just not worth the trouble to turn this into a */
637 /* separate subroutine. . . */
638
639 for (j = 0; j < xobjs.pages; j++) {
640 if (pschem->schemtype != PRIMARY) {
641 j = xobjs.pages;
642 cinst = thisinst;
643 cschem = thisobject;
644 }
645 else {
646 cinst = xobjs.pagelist[j]->pageinst;
647 if ((cinst == NULL) ||
648 ((cinst->thisobject != pschem) &&
649 ((cinst->thisobject->schemtype != SECONDARY) ||
650 (cinst->thisobject->symschem != pschem)))) continue;
651 cschem = cinst->thisobject;
652 }
653
654 /* Determine the existing number of parts. We do not want to */
655 /* search over elements that we create in this routine. */
656
657 old_parts = cschem->parts;
658
659 /* Part 1: Recursion */
660 /* Schematic pages and symbols acting as their own schematics are the */
661 /* two types on which we recurse to find all sub-schematics. */
662
663 if ((!visited) && (cschem->schemtype == PRIMARY ||
664 cschem->schemtype == SECONDARY ||
665 (cschem->schemtype == SYMBOL && cschem->symschem == NULL))) {
666
667 for (i = 0; i < old_parts; i++) {
668 cgen = cschem->plist + i;
669 if (IS_OBJINST(*cgen)) {
670 objinstptr geninst, callinst;
671 geninst = TOOBJINST(cgen);
672
673 if (geninst->thisobject->symschem != NULL) {
674 callobj = geninst->thisobject->symschem;
675 n = is_page(callobj);
676 if (n == -1) {
677 Fprintf(stderr, "Error: associated schematic is not a page!\n");
678 continue;
679 }
680 else
681 callinst = xobjs.pagelist[n]->pageinst;
682 }
683 else {
684 callobj = geninst->thisobject;
685 callinst = geninst;
686 }
687
688 /* object on its own schematic */
689 if (callobj == pschem) continue;
690
691 gennetlist(callinst);
692
693 /* Also generate netlist for pins in the corresponding symbol */
694 if (geninst->thisobject->symschem != NULL)
695 gennetlist(geninst);
696 }
697 }
698 }
699
700 /* Part 2: Match pin labels to nets, and add to list of globals */
701 /* if appropriate. We do all pins first, before ennumerating */
702 /* polygons, so that buses can be handled correctly, generating */
703 /* overlapping subnets. */
704
705 for (i = 0; i < old_parts; i++) {
706 cgen = cschem->plist + i;
707 if (IS_LABEL(*cgen)) {
708 clab = TOLABEL(cgen);
709 if (clab->pin != False && clab->pin != INFO) {
710
711 /* Check if the label has a non-default parameter. */
712 /* If so, we want to record the instance along */
713 /* the other netlist information about the pin. */
714
715 labinst = NULL;
716 for (cstr = clab->string; cstr != NULL; cstr = cstr->nextpart)
717 if (cstr->type == PARAM_START)
718 if (match_instance_param(cinst, cstr->data.string) != NULL) {
719 labinst = cinst;
720 break;
721 }
722
723 /* If we have netlisted this object before, and the */
724 /* label is already in the netlist, then ignore it. */
725 /* This only happens for labels without instanced */
726 /* parameter values. */
727
728 if (visited && (labinst == NULL)) {
729 for (lseek = pschem->labels; lseek != NULL; lseek = lseek->next)
730 if ((lseek->label == clab) && (lseek->cinst == NULL))
731 break;
732 if (lseek != NULL) continue;
733 }
734 else if (visited)
735 netlist = NULL;
736 else
737 /* Note any overlapping labels. */
738 netlist = pointtonet(cschem, cinst, &clab->position);
739
740 if (pschem->symschem != NULL && pschem->schemtype == SYMBOL) {
741
742 /* For symbols: Check that this pin has a */
743 /* corresponding pin in the schematic. The schematic */
744 /* is netlisted before the symbol, so the netlist */
745 /* should be there. */
746
747 tmplist = pintonet(pschem->symschem, cinst, clab);
748
749 /* There is always the possibility that the symbol */
750 /* pin does not correspond to anything in the */
751 /* schematic. If so, it gets its own unique net */
752 /* number. HOWEVER, this situation usually indicates */
753 /* an error in the schematic, so output a warning */
754 /* message. */
755
756 if (tmplist == NULL) {
757 char *snew = NULL;
758 snew = textprint(clab->string, NULL),
759 Fprintf(stderr, "Warning: Pin \"%s\" in symbol %s has no "
760 "connection in schematic %s\n",
761 snew, pschem->name,
762 pschem->symschem->name);
763 free(snew);
764 newlist.net.id = nextnet++;
765 tmplist = &newlist;
766 }
767 }
768 else {
769 /* For schematics: Relate the pin to a network */
770 tmplist = pintonet(pschem, cinst, clab);
771
772 /* If only part of a bus has been identified, generate */
773 /* the net ID's of the parts that haven't been. */
774
775 if ((tmplist != NULL) && (tmplist->subnets > 0)) {
776 for (lbus = 0; lbus < tmplist->subnets; lbus++) {
777 sbus = tmplist->net.list + lbus;
778 if (sbus->netid == 0)
779 sbus->netid = nextnet++;
780 }
781 }
782 }
783
784 if (clab->pin == LOCAL) {
785 if (tmplist != NULL) {
786 addpin(cschem, labinst, clab, tmplist);
787 if (netlist != NULL)
788 mergenets(pschem, netlist, tmplist);
789 }
790 else {
791 if (netlist == NULL) {
792 netlist = &newlist;
793 netlist->net.id = nextnet++;
794 buspins = break_up_bus(clab, cinst, netlist);
795 if (buspins != NULL) {
796 buslist *sbus = buspins->net.list + buspins->subnets - 1;
797 nextnet = sbus->netid + 1;
798 netlist = addpin(cschem, labinst, clab, buspins);
799 }
800 else {
801 tmplist = addpin(cschem, labinst, clab, netlist);
802 netlist = tmplist;
803 }
804 }
805 else {
806 tmplist = addpin(cschem, labinst, clab, netlist);
807 netlist = tmplist;
808 }
809 }
810 }
811 else if (clab->pin == GLOBAL) {
812 if (tmplist == NULL) {
813 tmplist = &newlist;
814 tmplist->net.id = globalmax() - 1;
815 }
816 addglobalpin(cschem, cinst, clab, tmplist);
817 addpin(cschem, labinst, clab, tmplist);
818 if (netlist != NULL)
819 mergenets(pschem, netlist, tmplist);
820 }
821 }
822 else if (clab->pin == INFO) {
823 /* Note the presence of info labels in the */
824 /* subcircuit, indicating that it needs to be */
825 /* called because it will generate output. */
826 cschem->infolabels = TRUE;
827 }
828 }
829 }
830
831 /* Part 3: Polygon enumeration */
832 /* Assign network numbers to all polygons in the object. */
833 /* (Ignore symbols, as we only consider pins on symbols) */
834 /* Where multiple pins occur at a point (bus notation), */
835 /* polygons will be duplicated in each network. */
836
837 if ((!visited) && (cschem->schemtype == PRIMARY ||
838 cschem->schemtype == SECONDARY ||
839 (cschem->schemtype == SYMBOL && cschem->symschem == NULL))) {
840
841 for (i = 0; i < old_parts; i++) {
842 cgen = cschem->plist + i;
843 if (IS_POLYGON(*cgen)) {
844 cpoly = TOPOLY(cgen);
845
846 /* Ignore non-network (closed, bbox, filled) polygons */
847 if (nonnetwork(cpoly)) continue;
848
849 resolved_net = (Genericlist *)NULL;
850
851 /* Check for attachment of each segment of this polygon */
852 /* to position of every recorded pin label. */
853
854 for (lseek = pschem->labels; lseek != NULL; lseek = lseek->next) {
855 if (lseek->cschem != cschem) continue;
856 else if ((lseek->cinst != NULL) && (lseek->cinst != cinst))
857 continue;
858 olabel = lseek->label;
859 tmplist = (Genericlist *)lseek;
860 for (endpt = cpoly->points; endpt < cpoly->points
861 + EndPoint(cpoly->number); endpt++) {
862 endpt2 = endpt + NextPoint(cpoly->number);
863 if (onsegment(endpt, endpt2, &olabel->position)) {
864
865 if (resolved_net != NULL) {
866 if (mergenets(pschem, resolved_net, tmplist))
867 resolved_net = tmplist;
868 }
869 if (resolved_net == NULL) {
870 addpoly(cschem, cpoly, tmplist);
871 resolved_net = tmplist;
872 }
873 }
874 }
875 /* if we've encountered a unique instance, then con- */
876 /* tinue past all other instances using this label. */
877 if (lseek->cinst != NULL)
878 while (lseek->next && (lseek->next->label == lseek->label))
879 lseek = lseek->next;
880 }
881
882 /* Check for attachment of each segment of this polygon */
883 /* to endpoints of every recorded network polygon. */
884
885 for (plist = pschem->polygons; plist != NULL; plist = plist->next) {
886 if (plist->cschem != cschem) continue;
887 else if ((tpoly = plist->poly) == cpoly) continue;
888 tpt = tpoly->points;
889 tpt2 = tpoly->points + tpoly->number - 1;
890 tmplist = (Genericlist *)plist;
891
892 for (endpt = cpoly->points; endpt < cpoly->points
893 + EndPoint(cpoly->number); endpt++) {
894 endpt2 = endpt + NextPoint(cpoly->number);
895
896 if (onsegment(endpt, endpt2, tpt) ||
897 onsegment(endpt, endpt2, tpt2)) {
898
899 /* Nets previously counted distinct have */
900 /* been connected together by this polygon. */
901 if (resolved_net != NULL) {
902 if (mergenets(pschem, resolved_net, tmplist))
903 resolved_net = tmplist;
904 }
905 if (resolved_net == NULL) {
906 addpoly(cschem, cpoly, tmplist);
907 resolved_net = tmplist;
908 }
909 }
910 }
911
912 /* Check for attachment of the endpoints of this polygon */
913 /* to each segment of every recorded network polygon. */
914
915 endpt = cpoly->points;
916 endpt2 = cpoly->points + cpoly->number - 1;
917 for (tpt = tpoly->points; tpt < tpoly->points
918 + EndPoint(tpoly->number); tpt++) {
919 tpt2 = tpt + NextPoint(tpoly->number);
920
921 if (onsegment(tpt, tpt2, endpt) ||
922 onsegment(tpt, tpt2, endpt2)) {
923
924 /* Nets previously counted distinct have */
925 /* been connected together by this polygon. */
926 if (resolved_net != 0) {
927 if (mergenets(pschem, resolved_net, tmplist))
928 resolved_net = tmplist;
929 }
930 if (resolved_net == 0) {
931 addpoly(cschem, cpoly, tmplist);
932 resolved_net = tmplist;
933 }
934 }
935 }
936 }
937 if (resolved_net == 0) {
938
939 /* This polygon belongs to an unvisited */
940 /* network. Give this polygon a new net */
941 /* number and add to the net list. */
942
943 newlist.net.id = nextnet++;
944 addpoly(cschem, cpoly, &newlist);
945 }
946 }
947 }
948 }
949 }
950 }
951
952 /*--------------------------------------------------------------*/
953 /* Search a sibling of object instance "cinst" for connections */
954 /* between the two. Recurse through the schematic hierarchy of */
955 /* the sibling object. */
956 /*--------------------------------------------------------------*/
957
search_on_siblings(objinstptr cinst,objinstptr isib,pushlistptr schemtop,short llx,short lly,short urx,short ury)958 void search_on_siblings(objinstptr cinst, objinstptr isib, pushlistptr schemtop,
959 short llx, short lly, short urx, short ury)
960 {
961 XPoint *tmppts, sbbox[2];
962 int i;
963 labelptr olabel;
964 polyptr tpoly;
965 PolylistPtr pseek;
966 LabellistPtr lseek;
967
968 genericptr *iseek;
969 objinstptr subsibinst;
970 pushlistptr psearch, newlist;
971 objectptr sibling = isib->thisobject;
972
973 tmppts = (XPoint *)malloc(sizeof(XPoint));
974
975 /* If the sibling is a symbol or fundamental or trivial object, then */
976 /* we just look at pin labels from the parts list and return. */
977
978 if (sibling->symschem != NULL || sibling->schemtype == FUNDAMENTAL
979 || sibling->schemtype == TRIVIAL) {
980 for (lseek = sibling->labels; lseek != NULL; lseek = lseek->next) {
981 olabel = lseek->label;
982
983 tmppts = (XPoint *)realloc(tmppts, sizeof(XPoint));
984 UTransformPoints(&(olabel->position), tmppts, 1,
985 isib->position, isib->scale, isib->rotation);
986
987 /* Transform all the way up to the level above cinst */
988 for (psearch = schemtop; psearch != NULL; psearch = psearch->next) {
989 subsibinst = psearch->thisinst;
990 UTransformPoints(tmppts, tmppts, 1, subsibinst->position,
991 subsibinst->scale, subsibinst->rotation);
992 }
993 searchconnect(tmppts, 1, cinst, lseek->subnets);
994 }
995 }
996
997 /* If the sibling is a schematic, we look at connections to pins and */
998 /* polygons, and recursively search subschematics of the sibling. */
999
1000 else {
1001
1002 /* Look for pins connecting to pins in the object */
1003
1004 for (lseek = sibling->labels; lseek != NULL; lseek = lseek->next) {
1005 olabel = lseek->label;
1006 tmppts = (XPoint *)realloc(tmppts, sizeof(XPoint));
1007 UTransformPoints(&(olabel->position), tmppts, 1,
1008 isib->position, isib->scale, isib->rotation);
1009
1010 /* Transform all the way up to the level above cinst */
1011 for (psearch = schemtop; psearch != NULL; psearch = psearch->next) {
1012 subsibinst = psearch->thisinst;
1013 UTransformPoints(tmppts, tmppts, 1, subsibinst->position,
1014 subsibinst->scale, subsibinst->rotation);
1015 }
1016 searchconnect(tmppts, 1, cinst, lseek->subnets);
1017 }
1018
1019 /* Look for polygon ends connecting into the object */
1020
1021 for (pseek = sibling->polygons; pseek != NULL; pseek = pseek->next) {
1022 tpoly = pseek->poly;
1023 tmppts = (XPoint *)realloc(tmppts, tpoly->number * sizeof(XPoint));
1024 UTransformPoints(tpoly->points, tmppts, tpoly->number,
1025 isib->position, isib->scale, isib->rotation);
1026
1027 /* Transform all the way up to the level above cinst */
1028 for (psearch = schemtop; psearch != NULL; psearch = psearch->next) {
1029 subsibinst = psearch->thisinst;
1030 UTransformPoints(tmppts, tmppts, tpoly->number, subsibinst->position,
1031 subsibinst->scale, subsibinst->rotation);
1032 }
1033 searchconnect(tmppts, tpoly->number, cinst, pseek->subnets);
1034 }
1035
1036 /* Recursively search all schematic children of the sibling */
1037
1038 for (i = 0; i < sibling->parts; i++) {
1039 iseek = sibling->plist + i;
1040 if (IS_OBJINST(*iseek)) {
1041
1042 /* objinstptr iinst = (objinstptr)iseek; (jdk) */
1043
1044 /* Don't search this instance unless the bounding box */
1045 /* overlaps the bounding box of the calling object. */
1046
1047 calcinstbbox(iseek, &sbbox[0].x, &sbbox[0].y, &sbbox[1].x, &sbbox[1].y);
1048 for (psearch = schemtop; psearch != NULL; psearch = psearch->next) {
1049 subsibinst = psearch->thisinst;
1050 UTransformPoints(sbbox, sbbox, 2, subsibinst->position,
1051 subsibinst->scale, subsibinst->rotation);
1052 }
1053 if ((llx > sbbox[1].x) || (urx < sbbox[0].x) || (lly > sbbox[1].y)
1054 || (ury < sbbox[0].y))
1055 continue;
1056
1057 subsibinst = TOOBJINST(iseek);
1058
1059 /* push stack */
1060 newlist = (pushlistptr)malloc(sizeof(pushlist));
1061 newlist->thisinst = isib;
1062 newlist->next = schemtop;
1063 schemtop = newlist;
1064
1065 search_on_siblings(cinst, subsibinst, schemtop, llx, lly, urx, ury);
1066
1067 /* pop stack */
1068
1069 newlist = schemtop;
1070 schemtop = schemtop->next;
1071 free(newlist);
1072 }
1073 }
1074 }
1075 free(tmppts);
1076 }
1077
1078 /*--------------------------------------------------------------*/
1079 /* Generate ports from pin labels for all objects: */
1080 /* For each pin-label in the subcircuit or symbol, generate */
1081 /* a port in the object or instance's object. */
1082 /* Translate the pin position to the level above and search */
1083 /* for & link in any connecting nets. */
1084 /* */
1085 /* Generate calls to object instances. */
1086 /* Pick up any other nets which might be formed by */
1087 /* juxtaposition of object instances on different levels of the */
1088 /* schematic hierarchy (e.g., pin-to-pin connections). */
1089 /*--------------------------------------------------------------*/
1090
gencalls(objectptr thisobject)1091 void gencalls(objectptr thisobject)
1092 {
1093 genericptr *cgen, *iseek;
1094 Matrix locctm;
1095 objinstptr cinst, isib, callinst;
1096 objectptr callobj, callsymb, cschem, pschem;
1097 XPoint xpos;
1098 short ibllx, iblly, iburx, ibury, sbllx, sblly, sburx, sbury;
1099 int i, j, k; /* , lbus; (jdk) */
1100 /* buslist *sbus; (jdk) */
1101 labelptr olabel;
1102 polyptr tpoly;
1103 PolylistPtr pseek;
1104 /* CalllistPtr cseek; (jdk) */
1105 LabellistPtr lseek;
1106 Genericlist *netfrom, *netto; /* , *othernet; (jdk) */
1107
1108 /* The netlist is always kept in the master schematic */
1109 pschem = (thisobject->schemtype == SECONDARY) ? thisobject->symschem :
1110 thisobject;
1111
1112 pschem->traversed = True; /* This object has been dealt with */
1113 pschem->valid = True; /* This object has a valid netlist */
1114
1115 /* We start the loop for schematics but will modify the loop */
1116 /* variable to execute just once in the case of a symbol. */
1117 /* (see gennetlist(), where the same thing is done) */
1118
1119 for (j = 0; j < xobjs.pages; j++) {
1120 if (pschem->schemtype != PRIMARY) {
1121 j = xobjs.pages;
1122 cschem = thisobject;
1123 }
1124 else {
1125 cinst = xobjs.pagelist[j]->pageinst;
1126 if ((cinst == NULL) ||
1127 ((cinst->thisobject != pschem) &&
1128 ((cinst->thisobject->schemtype != SECONDARY) ||
1129 (cinst->thisobject->symschem != pschem)))) continue;
1130 cschem = cinst->thisobject;
1131 }
1132
1133 for (i = 0; i < cschem->parts; i++) {
1134 cgen = cschem->plist + i;
1135 if (IS_OBJINST(*cgen)) {
1136 callinst = TOOBJINST(cgen);
1137
1138 /* Ignore any instance that is specifically marked non-netlistable */
1139 if (callinst->style & INST_NONETLIST) continue;
1140
1141 /* Determine where the hierarchy continues downward */
1142
1143 if (callinst->thisobject->symschem != NULL)
1144 callobj = callinst->thisobject->symschem;
1145 else
1146 callobj = callinst->thisobject;
1147
1148 /* Always ignore any object on its own schematic */
1149
1150 if (callobj == pschem) continue;
1151
1152 callsymb = callinst->thisobject;
1153
1154 /* Note: callobj is the next schematic in the hierarchy. */
1155 /* callsymb is the next visible object in the hierarchy, */
1156 /* which may be either a schematic or a symbol. */
1157
1158 /*--------------------------------------------------------------*/
1159 /* For object instances which are their own schematics (i.e., */
1160 /* have netlist elements), don't rely on any pin list but make */
1161 /* a survey of how polygons connect into the object. */
1162 /*--------------------------------------------------------------*/
1163
1164 if (callsymb->symschem == NULL
1165 && callobj->schemtype != FUNDAMENTAL
1166 && callobj->schemtype != TRIVIAL) {
1167
1168 /* Fprintf(stdout, "*** Analyzing connections from %s"
1169 " to instance of %s\n", cschem->name,
1170 callinst->thisobject->name); */
1171
1172 /* Look for pins connecting to pins in the object */
1173
1174 for (lseek = pschem->labels; lseek != NULL; lseek = lseek->next) {
1175 if (lseek->cschem != cschem) continue;
1176 else if ((lseek->cinst != NULL) && (lseek->cinst != callinst))
1177 continue;
1178 olabel = lseek->label;
1179 searchconnect(&(olabel->position), 1, callinst, lseek->subnets);
1180 /* if we've encountered a unique instance, then con- */
1181 /* tinue past all other instances using this label. */
1182 if (lseek->cinst != NULL)
1183 while (lseek->next && (lseek->next->label == lseek->label))
1184 lseek = lseek->next;
1185 }
1186
1187 /* Look for polygon ends connecting into the object */
1188
1189 for (pseek = pschem->polygons; pseek != NULL; pseek = pseek->next) {
1190 if (pseek->cschem != cschem) continue;
1191 tpoly = pseek->poly;
1192 searchconnect(tpoly->points, tpoly->number, callinst, pseek->subnets);
1193 }
1194
1195 /* For each call to a schematic or symbol which is NOT the */
1196 /* one under consideration, see if it touches or overlaps */
1197 /* the object under consideration. Search for connections */
1198 /* between the two objects. */
1199
1200 calcinstbbox(cgen, &ibllx, &iblly, &iburx, &ibury);
1201
1202 /* Only need to look forward from the current position. */
1203 for (k = i + 1; k < cschem->parts; k++) {
1204
1205 iseek = cschem->plist + k;
1206 if (IS_OBJINST(*iseek)) {
1207 calcinstbbox(iseek, &sbllx, &sblly, &sburx, &sbury);
1208
1209 /* Check intersection of the two object instances; */
1210 /* don't do a search if they are disjoint. */
1211
1212 if ((ibllx <= sburx) && (iburx >= sbllx) &&
1213 (iblly <= sbury) && (ibury >= sblly)) {
1214 isib = TOOBJINST(iseek);
1215 search_on_siblings(callinst, isib, NULL,
1216 ibllx, iblly, iburx, ibury);
1217 }
1218 }
1219 }
1220 }
1221
1222 /*----------------------------------------------------------*/
1223 /* Recursively call gencalls() on the schematic. */
1224 /*----------------------------------------------------------*/
1225
1226 if (callobj->traversed == False)
1227 gencalls(callobj);
1228
1229 /*----------------------------------------------------------*/
1230 /* Create a call to the object callsymb from object cschem */
1231 /*----------------------------------------------------------*/
1232
1233 addcall(cschem, callobj, callinst);
1234
1235 /*----------------------------------------------------------*/
1236 /* Search again on symbol pins to generate calls to ports. */
1237 /*----------------------------------------------------------*/
1238
1239 UResetCTM(&locctm);
1240 UPreMultCTM(&locctm, callinst->position, callinst->scale,
1241 callinst->rotation);
1242
1243 for (lseek = callsymb->labels; lseek != NULL; lseek = lseek->next) {
1244 /* LabellistPtr slab; (jdk) */
1245 /* labelptr slabel; (jdk) */
1246
1247 if (lseek->cschem != callsymb) continue;
1248 else if ((lseek->cinst != NULL) && (lseek->cinst != callinst))
1249 continue;
1250
1251 olabel = lseek->label;
1252 netto = (Genericlist *)lseek;
1253
1254 /* Translate pin position back to object cschem */
1255 UTransformbyCTM(&locctm, &(olabel->position), &xpos, 1);
1256
1257 /* What net in the calling object connects to this point? */
1258 netfrom = pointtonet(cschem, callinst, &xpos);
1259
1260 /* If there's no net, we make one */
1261 if (netfrom == NULL)
1262 netfrom = make_tmp_pin(cschem, callinst, &xpos, netto);
1263
1264 /* Generate a port call for a global signal if the */
1265 /* global label appears in the symbol (9/29/04). This */
1266 /* is a change from previous behavior, which was to not */
1267 /* make the call. */
1268
1269 if ((netto->subnets == 0) && (netto->net.id < 0))
1270 mergenets(pschem, netfrom, netto);
1271
1272 /* Attempt to generate a port in the object. */
1273 addport(callobj, netto);
1274
1275 /* Generate the call to the port */
1276 if (addportcall(pschem, netfrom, netto) == FALSE) {
1277
1278 // If object is "dot" then copy the bus from the
1279 // net to the dot. The dot takes on whatever
1280 // dimension the bus is.
1281
1282 if (strstr(callobj->name, "::dot") != NULL) {
1283 copy_bus(netto, netfrom);
1284 }
1285 else {
1286 Fprintf(stderr, "Error: attempt to connect bus size "
1287 "%d in %s to bus size %d in %s\n",
1288 netfrom->subnets, cschem->name,
1289 netto->subnets, callobj->name);
1290 }
1291 }
1292
1293 /* if we've encountered a unique instance, then continue */
1294 /* past all other instances using this label. */
1295 if (lseek->cinst != NULL)
1296 while (lseek->next && (lseek->next->label == lseek->label))
1297 lseek = lseek->next;
1298 }
1299
1300 /*----------------------------------------------------------*/
1301 /* If after all that, no ports were called, then remove the */
1302 /* call to this object instance. However, we should check */
1303 /* for info labels, because the device may produce output */
1304 /* for the netlist even if it has no declared ports. This */
1305 /* is irrespective of the presence of pin labels---if the */
1306 /* symbol is "trivial", the netlist connections are */
1307 /* resolved in the level of the hierarchy above, and there */
1308 /* may be no ports, but the call list must retain the call */
1309 /* to ensure that the netlist output is generated. */
1310 /*----------------------------------------------------------*/
1311
1312 if (pschem->calls->ports == NULL)
1313 if (pschem->infolabels == FALSE)
1314 removecall(pschem, pschem->calls); /* Remove the call */
1315 }
1316 }
1317 }
1318 }
1319
1320 /*----------------------------------------------------------------------*/
1321 /* Translate a net number down in the calling hierarchy */
1322 /*----------------------------------------------------------------------*/
1323
translatedown(int rnet,int portid,objectptr nextobj)1324 int translatedown(int rnet, int portid, objectptr nextobj)
1325 {
1326 PortlistPtr nport;
1327 int downnet = 0;
1328
1329 for (nport = nextobj->ports; nport != NULL; nport = nport->next) {
1330 if (nport->portid == portid) {
1331 downnet = nport->netid;
1332 break;
1333 }
1334 }
1335 return downnet;
1336 }
1337
1338 /*----------------------------------------------------------------------*/
1339 /* Translate a netlist up in the calling hierarchy */
1340 /* */
1341 /* This routine creates a new netlist header which needs to be freed */
1342 /* by the calling routine. */
1343 /* */
1344 /* Note that if the entire netlist cannot be translated up, then the */
1345 /* routine returns NULL. This could be modified to return the part of */
1346 /* the network that can be translated up. */
1347 /*----------------------------------------------------------------------*/
1348
translateup(Genericlist * rlist,objectptr thisobj,objectptr nextobj,objinstptr nextinst)1349 Genericlist *translateup(Genericlist *rlist, objectptr thisobj,
1350 objectptr nextobj, objinstptr nextinst)
1351 {
1352 PortlistPtr nport;
1353 CalllistPtr ccall;
1354 int portid = 0;
1355 int upnet = 0;
1356 int rnet, lbus;
1357 buslist *sbus;
1358 Genericlist *tmplist;
1359
1360 tmplist = (Genericlist *)malloc(sizeof(Genericlist));
1361 tmplist->subnets = 0;
1362 tmplist->net.id = 0;
1363 copy_bus(tmplist, rlist);
1364
1365 for (lbus = 0;; ) {
1366 if (rlist->subnets == 0)
1367 rnet = rlist->net.id;
1368 else {
1369 sbus = rlist->net.list + lbus;
1370 rnet = sbus->netid;
1371 }
1372 for (nport = nextobj->ports; nport != NULL; nport = nport->next) {
1373 if (nport->netid == rnet) {
1374 portid = nport->portid;
1375 break;
1376 }
1377 }
1378
1379 upnet = 0;
1380 for (ccall = thisobj->calls; ccall != NULL; ccall = ccall->next) {
1381 if (ccall->callinst == nextinst) {
1382 for (nport = ccall->ports; nport != NULL; nport = nport->next) {
1383 if (nport->portid == portid) {
1384 upnet = nport->netid;
1385 break;
1386 }
1387 }
1388 if (nport != NULL) break;
1389 }
1390 }
1391 if (upnet == 0) {
1392 freegenlist(tmplist);
1393 return NULL;
1394 }
1395 else {
1396 if (tmplist->subnets == 0) {
1397 tmplist->net.id = upnet;
1398 }
1399 else {
1400 sbus = tmplist->net.list + lbus;
1401 sbus->netid = upnet;
1402 sbus->subnetid = getsubnet(upnet, thisobj);
1403 }
1404 }
1405 if (++lbus >= rlist->subnets) break;
1406 }
1407 return tmplist;
1408 }
1409
1410 /*----------------------------------------------------------------------*/
1411 /* Check whether the indicated polygon is already resolved into the */
1412 /* netlist of the object hierarchy described by seltop. */
1413 /* Return the netlist if resolved, NULL otherwise. The netlist */
1414 /* returned is referred (translated) upward through the calling */
1415 /* hierarchy to the topmost object containing that net or those nets. */
1416 /* This topmost object is returned in parameter topobj. */
1417 /* Note that the netlist returned does not necessarily correspond to */
1418 /* any object in the top level. It is allocated and must be freed by */
1419 /* the calling routine. */
1420 /*----------------------------------------------------------------------*/
1421
is_resolved(genericptr * rgen,pushlistptr seltop,objectptr * topobj)1422 Genericlist *is_resolved(genericptr *rgen, pushlistptr seltop, objectptr *topobj)
1423 {
1424 objectptr thisobj = seltop->thisinst->thisobject;
1425 objectptr pschem;
1426 PolylistPtr pseek;
1427 LabellistPtr lseek;
1428 Genericlist *rlist = NULL, *newlist;
1429
1430 pschem = (thisobj->schemtype == SECONDARY) ? thisobj->symschem : thisobj;
1431
1432 /* Recursively call self, since we have to back out from the bottom of */
1433 /* the stack. */
1434
1435 if (seltop->next != NULL) {
1436 rlist = is_resolved(rgen, seltop->next, topobj);
1437
1438 /* Translate network ID up the hierarchy to the topmost object in which */
1439 /* the network exists. */
1440
1441 if (rlist != NULL) {
1442 newlist = translateup(rlist, pschem, seltop->next->thisinst->thisobject,
1443 seltop->next->thisinst);
1444 if (newlist == NULL)
1445 /* Net does not exist upwards of this object. Pass net ID */
1446 /* upward with "topobj" unchanged. */
1447 return rlist;
1448 else {
1449 freegenlist(rlist);
1450 rlist = newlist;
1451 }
1452 }
1453 }
1454 else {
1455
1456 /* Find the net ID for the listed object, which should be in the object */
1457 /* on the bottom of the pushlist stack. */
1458
1459 if (IS_POLYGON(*rgen)) {
1460 for (pseek = pschem->polygons; pseek != NULL; pseek = pseek->next) {
1461 if (pseek->poly == TOPOLY(rgen)) {
1462 rlist = (Genericlist *)pseek;
1463 break;
1464 }
1465 }
1466 }
1467 else if (IS_LABEL(*rgen)) {
1468 for (lseek = pschem->labels; lseek != NULL; lseek = lseek->next) {
1469 if (lseek->label == TOLABEL(rgen)) {
1470 rlist = (Genericlist *)lseek;
1471 break;
1472 }
1473 }
1474 }
1475
1476 if (rlist != NULL) {
1477 /* Make a copy of the netlist header from this element */
1478 newlist = (Genericlist *)malloc(sizeof(Genericlist));
1479 newlist->subnets = 0;
1480 copy_bus(newlist, rlist);
1481 rlist = newlist;
1482 }
1483 }
1484
1485 /* done: (jdk) */
1486 *topobj = (rlist == NULL) ? NULL : seltop->thisinst->thisobject;
1487 return rlist;
1488 }
1489
1490 /*--------------------------------------------------------------*/
1491 /* Highlight all the polygons and pin labels in a network */
1492 /* (recursively, downward). Pin labels are drawn only on the */
1493 /* topmost schematic object. */
1494 /* Returns true if some part of the hierarchy declared a net to */
1495 /* be highlighted. */
1496 /* mode = 1 highlight, mode = 0 erase */
1497 /*--------------------------------------------------------------*/
1498
highlightnet(objectptr cschem,objinstptr cinst,int netid,u_char mode)1499 Boolean highlightnet(objectptr cschem, objinstptr cinst, int netid, u_char mode)
1500 {
1501 CalllistPtr calls;
1502 PortlistPtr ports;
1503 PolylistPtr plist;
1504 LabellistPtr llist;
1505 polyptr cpoly;
1506 labelptr clabel;
1507 objinstptr ccinst;
1508 int netto, locnetid, lbus;
1509 int curcolor = AUXCOLOR;
1510 Boolean rval = FALSE;
1511 objectptr pschem;
1512
1513 SetForeground(dpy, areawin->gc, curcolor);
1514
1515 pschem = (cschem->schemtype == SECONDARY) ? cschem->symschem : cschem;
1516
1517 for (plist = pschem->polygons; plist != NULL; plist = plist->next) {
1518 if (plist->cschem != cschem) continue;
1519 cpoly = plist->poly;
1520 for (lbus = 0;;) {
1521 if (plist->subnets == 0)
1522 locnetid = plist->net.id;
1523 else
1524 locnetid = (plist->net.list + lbus)->netid;
1525 if (locnetid == netid) {
1526 /* Fprintf(stdout, " >> Found polygon belonging to net %d at (%d, %d)\n",
1527 locnetid, cpoly->points[0].x, cpoly->points[0].y); */
1528 if (mode == 0 && cpoly->color != curcolor) {
1529 curcolor = cpoly->color;
1530 XTopSetForeground(curcolor);
1531 }
1532 UDrawPolygon(cpoly, xobjs.pagelist[areawin->page]->wirewidth);
1533 break;
1534 }
1535 if (++lbus >= plist->subnets) break;
1536 }
1537 }
1538
1539 /* Highlight labels if they belong to the top-level object */
1540
1541 if (cschem == topobject) {
1542 for (llist = pschem->labels; llist != NULL; llist = llist->next) {
1543 if (llist->cschem != cschem) continue;
1544 else if ((llist->cinst != NULL) && (llist->cinst != cinst)) continue;
1545 clabel = llist->label;
1546 for (lbus = 0;;) {
1547 if (llist->subnets == 0)
1548 locnetid = llist->net.id;
1549 else
1550 locnetid = (llist->net.list + lbus)->netid;
1551 if (locnetid == netid) {
1552 if (clabel->string->type == FONT_NAME) { /* don't draw temp labels */
1553 if ((mode == 0) && (clabel->color != curcolor)) {
1554 curcolor = clabel->color;
1555 UDrawString(clabel, curcolor, cinst);
1556 }
1557 else
1558 UDrawString(clabel, DOFORALL, cinst);
1559 /* Fprintf(stdout, " >> Found label belonging to net "
1560 "%d at (%d, %d)\n",
1561 locnetid, clabel->position.x,
1562 clabel->position.y); */
1563
1564 }
1565 break;
1566 }
1567 if (++lbus >= llist->subnets) break;
1568 }
1569 /* if we've encountered a unique instance, then continue */
1570 /* past all other instances using this label. */
1571 if (llist->cinst != NULL)
1572 while (llist->next && (llist->next->label == llist->label))
1573 llist = llist->next;
1574 }
1575
1576 /* Highlight all pins connecting this net to symbols */
1577
1578 }
1579
1580 /* Connectivity recursion */
1581
1582 for (calls = pschem->calls; calls != NULL; calls = calls->next) {
1583 if (calls->cschem != cschem) continue;
1584 for (ports = calls->ports; ports != NULL; ports = ports->next) {
1585 if (ports->netid == netid) {
1586 ccinst = calls->callinst;
1587
1588 /* Recurse only on objects for which network polygons are visible */
1589 /* from the calling object: i.e., non-trivial, non-fundamental */
1590 /* objects acting as their own schematics. */
1591
1592 UPushCTM();
1593 UPreMultCTM(DCTM, ccinst->position, ccinst->scale, ccinst->rotation);
1594
1595 if (ccinst->thisobject->symschem == NULL &&
1596 ccinst->thisobject->schemtype != FUNDAMENTAL &&
1597 ccinst->thisobject->schemtype != TRIVIAL) {
1598
1599 netto = translatedown(netid, ports->portid, calls->callobj);
1600
1601 /* Fprintf(stdout, " > Calling object %s at (%d, %d)\n",
1602 calls->callobj->name, ccinst->position.x, ccinst->position.y); */
1603 /* Fprintf(stdout, " > Net translation from %d to %d (port %d)\n",
1604 netid, netto, ports->portid); */
1605
1606 if (highlightnet(calls->callobj, calls->callinst, netto, mode))
1607 rval = TRUE;
1608 }
1609 else {
1610 /* Otherwise (symbols, fundamental, trivial, etc., objects), we */
1611 /* highlight the pin position of the port. */
1612 if ((clabel = PortToLabel(ccinst, ports->portid)))
1613 UDrawXDown(clabel);
1614 }
1615 UPopCTM();
1616 }
1617 }
1618 }
1619 return rval;
1620 }
1621
1622 /*----------------------------------------------------------------------*/
1623 /* Highlight whatever nets are listed in the current object instance, */
1624 /* if any. */
1625 /*----------------------------------------------------------------------*/
1626
highlightnetlist(objectptr nettop,objinstptr cinst,u_char mode)1627 void highlightnetlist(objectptr nettop, objinstptr cinst, u_char mode)
1628 {
1629 int lbus, netid;
1630 buslist *sbus;
1631 Genericlist *netlist = cinst->thisobject->highlight.netlist;
1632 objinstptr nextinst = cinst->thisobject->highlight.thisinst;
1633
1634 if (netlist == NULL) return;
1635
1636 for (lbus = 0;; ) {
1637 if (netlist->subnets == 0)
1638 netid = netlist->net.id;
1639 else {
1640 sbus = netlist->net.list + lbus;
1641 netid = sbus->netid;
1642 }
1643 highlightnet(nettop, nextinst, netid, mode);
1644 if (++lbus >= netlist->subnets) break;
1645 }
1646
1647 /* If we are erasing, remove the netlist entry from the object */
1648 if (mode == (u_char)0) {
1649 freegenlist(netlist);
1650 cinst->thisobject->highlight.netlist = NULL;
1651 cinst->thisobject->highlight.thisinst = NULL;
1652 }
1653 }
1654
1655 /*----------------------------------------------------------------------*/
1656 /* Push the matrix stack for each object (instance) until the indicated */
1657 /* object is reached. This works similarly to highlightnet() above, */
1658 /* but makes calls according to the hierarchy described by the */
1659 /* pushlistptr parameter. */
1660 /* Returns the number of stack objects to pop after we're done. */
1661 /*----------------------------------------------------------------------*/
1662
pushnetwork(pushlistptr seltop,objectptr nettop)1663 int pushnetwork(pushlistptr seltop, objectptr nettop)
1664 {
1665 pushlistptr cursel = seltop;
1666 objinstptr sinst;
1667 int rno = 0;
1668
1669 while ((cursel->thisinst->thisobject != nettop) && (cursel->next != NULL)) {
1670 cursel = cursel->next;
1671 sinst = cursel->thisinst;
1672 UPushCTM();
1673 UPreMultCTM(DCTM, sinst->position, sinst->scale, sinst->rotation);
1674 rno++;
1675 }
1676
1677 if (cursel->thisinst->thisobject != nettop) {
1678 Fprintf(stderr, "Error: object does not exist in calling stack!\n");
1679 rno = 0;
1680 }
1681
1682 return rno;
1683 }
1684
1685 /*----------------------------------------------------------------------*/
1686 /* Determine if two netlists match. If "mode" is MATCH_EXACT, the */
1687 /* net numbers and subnet numbers must all be the same. If */
1688 /* MATCH_SUBNETS, then the subnet numbers must be the same. If */
1689 /* MATCH_SIZE, then they need only have the same number of subnets. */
1690 /*----------------------------------------------------------------------*/
1691
match_buses(Genericlist * list1,Genericlist * list2,int mode)1692 Boolean match_buses(Genericlist *list1, Genericlist *list2, int mode)
1693 {
1694 int i;
1695 buslist *bus1, *bus2;
1696
1697 if (list1->subnets != list2->subnets) {
1698 // A wire (no subnets) matches a bus of 1 subnet. All others
1699 // are non-matching.
1700
1701 if (list1->subnets != 0 && list2->subnets != 0)
1702 return FALSE;
1703 else if (list1->subnets != 1 && list2->subnets != 1)
1704 return FALSE;
1705 }
1706
1707 if (mode == MATCH_SIZE) return TRUE;
1708
1709 if (list1->subnets == 0) {
1710 if (mode == MATCH_SUBNETS) return TRUE;
1711 if (list2->subnets != 0) {
1712 bus2 = list2->net.list + 0;
1713 if (list1->net.id != bus2->netid) return FALSE;
1714 }
1715 else if (list1->net.id != list2->net.id) return FALSE;
1716 }
1717 else if (list2->subnets == 0) {
1718 if (mode == MATCH_SUBNETS) return TRUE;
1719 bus1 = list1->net.list + 0;
1720 if (bus1->netid != list2->net.id) return FALSE;
1721 }
1722 else {
1723 for (i = 0; i < list1->subnets; i++) {
1724 bus1 = list1->net.list + i;
1725 bus2 = list2->net.list + i;
1726 /* A subnetid of < 0 indicates an unassigned bus */
1727 if ((bus1->subnetid != -1) && (bus1->subnetid != bus2->subnetid))
1728 return FALSE;
1729 }
1730 if (mode == MATCH_SUBNETS) return TRUE;
1731
1732 for (i = 0; i < list1->subnets; i++) {
1733 bus1 = list1->net.list + i;
1734 bus2 = list2->net.list + i;
1735 if (bus1->netid != bus2->netid)
1736 return FALSE;
1737 }
1738 }
1739 return TRUE;
1740 }
1741
1742 /*----------------------------------------------------------------------*/
1743 /* Copy the netlist structure from one netlist element to another */
1744 /*----------------------------------------------------------------------*/
1745
copy_bus(Genericlist * dest,Genericlist * source)1746 void copy_bus(Genericlist *dest, Genericlist *source)
1747 {
1748 buslist *sbus, *dbus;
1749 int i;
1750
1751 if (dest->subnets > 0)
1752 free(dest->net.list);
1753
1754 dest->subnets = source->subnets;
1755 if (source->subnets == 0)
1756 dest->net.id = source->net.id;
1757 else {
1758 dest->net.list = (buslist *)malloc(dest->subnets * sizeof(buslist));
1759 for (i = 0; i < dest->subnets; i++) {
1760 sbus = source->net.list + i;
1761 dbus = dest->net.list + i;
1762 dbus->netid = sbus->netid;
1763 dbus->subnetid = sbus->subnetid;
1764 }
1765 }
1766 }
1767
1768 /*------------------------------------------------------*/
1769 /* Create a new "temporary" label object for a pin */
1770 /* This type of label is never drawn, so it doesn't */
1771 /* need font info. It is identified as "temporary" by */
1772 /* this lack of a leading font record. */
1773 /*------------------------------------------------------*/
1774
new_tmp_pin(objectptr cschem,XPoint * pinpt,char * pinstring,char * prefix,Genericlist * netlist)1775 Genericlist *new_tmp_pin(objectptr cschem, XPoint *pinpt, char *pinstring,
1776 char *prefix, Genericlist *netlist)
1777 {
1778 labelptr *newlabel;
1779 stringpart *strptr;
1780
1781 if (pinpt == NULL) {
1782 Fprintf(stderr, "NULL label location!\n");
1783 return NULL;
1784 }
1785
1786 NEW_LABEL(newlabel, cschem);
1787 labeldefaults(*newlabel, LOCAL, pinpt->x, pinpt->y);
1788 (*newlabel)->anchor = 0;
1789 (*newlabel)->color = DEFAULTCOLOR;
1790 strptr = (*newlabel)->string;
1791 strptr->type = TEXT_STRING;
1792 if (pinstring != NULL) {
1793 strptr->data.string = (char *)malloc(strlen(pinstring));
1794 strcpy(strptr->data.string, pinstring);
1795 }
1796 else {
1797 strptr->data.string = textprintnet(prefix, NULL, netlist);
1798 }
1799
1800 /* Add label to object's netlist and return a pointer to the */
1801 /* netlist entry. */
1802
1803 return (addpin(cschem, NULL, *newlabel, netlist));
1804 }
1805
1806 /*------------------------------------------------------*/
1807 /* Create a label for use in the list of global nets. */
1808 /* This label is like a temporary label (new_tmp_pin) */
1809 /* except that it is not represented in any object. */
1810 /* The string contains the verbatim contents of any */
1811 /* parameter substitutions in the original label. */
1812 /*------------------------------------------------------*/
1813
new_global_pin(labelptr clabel,objinstptr cinst)1814 labelptr new_global_pin(labelptr clabel, objinstptr cinst)
1815 {
1816 labelptr newlabel;
1817
1818 newlabel = (labelptr) malloc(sizeof(label));
1819 newlabel->type = LABEL;
1820 labeldefaults(newlabel, GLOBAL, 0, 0);
1821 newlabel->anchor = 0;
1822 newlabel->color = DEFAULTCOLOR;
1823 free(newlabel->string);
1824 newlabel->string = stringcopyall(clabel->string, cinst);
1825
1826 /* Add label to the global netlist and return a pointer to */
1827 /* the netlist entry. */
1828
1829 return newlabel;
1830 }
1831
1832 /*----------------------------------------------------------------------*/
1833 /* Create a temporary I/O pin (becomes part of netlist and also part of */
1834 /* the object itself). */
1835 /*----------------------------------------------------------------------*/
1836
make_tmp_pin(objectptr cschem,objinstptr cinst,XPoint * pinpt,Genericlist * sublist)1837 Genericlist *make_tmp_pin(objectptr cschem, objinstptr cinst, XPoint *pinpt,
1838 Genericlist *sublist)
1839 {
1840 LabellistPtr lseek;
1841 objectptr pschem;
1842 char *pinstring = NULL;
1843 /* buslist *sbus; (jdk) */
1844 /* int lbus; (jdk) */
1845 Genericlist *netlist, *tmplist, newlist;
1846
1847 newlist.subnets = 0;
1848 newlist.net.id = 0;
1849
1850 /* Primary schematic (contains the netlist) */
1851 pschem = (cschem->schemtype == SECONDARY) ? cschem->symschem : cschem;
1852
1853 /* Determine a netlist for this pin */
1854
1855 netlist = pointtonet(cschem, cinst, pinpt);
1856 if (netlist == NULL) {
1857 newlist.net.id = netmax(pschem) + 1;
1858 netlist = &newlist;
1859 }
1860
1861 /* If there is any other pin at this location, don't make another */
1862 /* one. If there is already a temporary pin associated with the */
1863 /* net, use its name. */
1864
1865 else {
1866 for (lseek = pschem->labels; lseek != NULL; lseek = lseek->next) {
1867 if (lseek->cschem != cschem) continue;
1868 else if ((lseek->cinst != NULL) && (lseek->cinst != cinst)) continue;
1869 tmplist = (Genericlist *)lseek;
1870 if (match_buses(netlist, tmplist, MATCH_EXACT)) {
1871 if (proximity(&(lseek->label->position), pinpt))
1872 return (Genericlist *)lseek;
1873 else if (lseek->label->string->type == TEXT_STRING)
1874 pinstring = lseek->label->string->data.string;
1875 }
1876 /* if we've encountered a unique instance, then continue past */
1877 /* all other instances using this label. */
1878 if (lseek->cinst != NULL)
1879 while (lseek->next && (lseek->next->label == lseek->label))
1880 lseek = lseek->next;
1881 }
1882 }
1883 return (new_tmp_pin(cschem, pinpt, pinstring, "ext", netlist));
1884 }
1885
1886 /*--------------------------------------------------------------*/
1887 /* Search for connections into a non-symbol subcircuit, based */
1888 /* on various combinations of polygon and pin label overlaps. */
1889 /*--------------------------------------------------------------*/
1890
searchconnect(XPoint * points,int number,objinstptr cinst,int subnets)1891 int searchconnect(XPoint *points, int number, objinstptr cinst, int subnets)
1892 {
1893 XPoint *tmppts, *tpt, *tpt2, *endpt, *endpt2, *pinpt, opinpt;
1894 objinstptr tinst;
1895 genericptr *cgen;
1896 polyptr tpoly;
1897 labelptr tlab;
1898 objectptr tobj, cobj = cinst->thisobject;
1899 LabellistPtr tseek;
1900 PolylistPtr pseek;
1901 int i; /* , lbus, sub_bus; (jdk) */
1902 int found = 0;
1903
1904 /* Generate temporary polygon in the coordinate system of */
1905 /* the object instance in question */
1906
1907 tmppts = (XPoint *)malloc(number * sizeof(XPoint));
1908 InvTransformPoints(points, tmppts, number,
1909 cinst->position, cinst->scale, cinst->rotation);
1910 /* Fprintf(stdout, "Info: translated polygon w.r.t. object %s\n", */
1911 /* cinst->thisobject->name); */
1912
1913 /* Recursion on all appropriate sub-schematics. */
1914 /* (Use parts list, not call list, as call list may not have created yet) */
1915
1916 for (i = 0; i < cobj->parts; i++) {
1917 cgen = cobj->plist + i;
1918 if (IS_OBJINST(*cgen)) {
1919 tinst = TOOBJINST(cgen);
1920 if (tinst->thisobject->symschem == NULL) {
1921 tobj = tinst->thisobject;
1922 if (tobj->schemtype != FUNDAMENTAL && tobj->schemtype != TRIVIAL)
1923 found += searchconnect(tmppts, number, tinst, subnets);
1924 }
1925 }
1926 }
1927
1928 for (endpt = tmppts; endpt < tmppts + EndPoint(number); endpt++) {
1929 endpt2 = endpt + NextPoint(number);
1930 for (i = 0; i < cobj->parts; i++) {
1931 cgen = cobj->plist + i;
1932 if (!IS_OBJINST(*cgen)) continue;
1933 tinst = TOOBJINST(cgen);
1934
1935 /* Look at the object only (symbol, or schematic if it has no symbol) */
1936 tobj = tinst->thisobject;
1937
1938 /* Search for connections to pin labels */
1939
1940 for (tseek = tobj->labels; tseek != NULL; tseek = tseek->next) {
1941 tlab = tseek->label;
1942 UTransformPoints(&(tlab->position), &opinpt, 1, tinst->position,
1943 tinst->scale, tinst->rotation);
1944 if (onsegment(endpt2, endpt, &opinpt)) {
1945 /* Fprintf(stdout, "%s connects to pin %s of %s in %s\n", */
1946 /* ((number > 1) ? "Polygon" : "Pin"), */
1947 /* tlab->string + 2, tinst->thisobject->name, */
1948 /* cinst->thisobject->name); */
1949 make_tmp_pin(cobj, cinst, &opinpt, (Genericlist *)tseek);
1950 found += (tseek->subnets == 0) ? 1 : tseek->subnets;
1951 break;
1952 }
1953 }
1954 }
1955
1956 for (pseek = cobj->polygons; pseek != NULL; pseek = pseek->next) {
1957 tpoly = pseek->poly;
1958
1959 /* Search for connections from segments passed to this */
1960 /* function to endpoints of polygons in the netlist. */
1961
1962 pinpt = NULL;
1963 tpt = tpoly->points;
1964 tpt2 = tpoly->points + tpoly->number - 1;
1965 if (onsegment(endpt2, endpt, tpt)) pinpt = tpt;
1966 if (onsegment(endpt2, endpt, tpt2)) pinpt = tpt2;
1967
1968 /* Create new pinlabel (only if there is not one already there) */
1969
1970 if (pinpt != NULL) {
1971 make_tmp_pin(cobj, cinst, pinpt, (Genericlist *)pseek);
1972 found += (pseek->subnets == 0) ? 1 : pseek->subnets;
1973 }
1974 }
1975 }
1976
1977 endpt = tmppts;
1978 endpt2 = tmppts + EndPoint(number) - 1;
1979
1980 /* Search for connections from endpoints passed to this */
1981 /* function to segments of polygons in the netlist. */
1982
1983 for (pseek = cobj->polygons; pseek != NULL; pseek = pseek->next) {
1984
1985 tpoly = pseek->poly;
1986 for (tpt = tpoly->points; tpt < tpoly->points
1987 + EndPoint(tpoly->number); tpt++) {
1988 tpt2 = tpt + NextPoint(tpoly->number);
1989
1990 pinpt = NULL;
1991 if (onsegment(tpt2, tpt, endpt)) pinpt = endpt;
1992 if (onsegment(tpt2, tpt, endpt2)) pinpt = endpt2;
1993
1994 /* Create new pinlabel (only if there is not one already there) */
1995
1996 if (pinpt != NULL) {
1997 make_tmp_pin(cobj, cinst, pinpt, (Genericlist *)pseek);
1998 found += (pseek->subnets == 0) ? 1 : pseek->subnets;
1999 }
2000 }
2001 }
2002 free(tmppts);
2003 return(found);
2004 }
2005
2006 /*----------------------------------------------------------------------*/
2007 /* Associate polygon with given netlist and add to the object's list */
2008 /* of network polygons (i.e., wires). */
2009 /*----------------------------------------------------------------------*/
2010
addpoly(objectptr cschem,polyptr poly,Genericlist * netlist)2011 Genericlist *addpoly(objectptr cschem, polyptr poly, Genericlist *netlist)
2012 {
2013 PolylistPtr newpoly;
2014 objectptr pschem;
2015 /* buslist *sbus; (jdk) */
2016 /* int lbus, sub_bus; (jdk) */
2017
2018 /* Netlist is in the master schematic */
2019 pschem = (cschem->schemtype == SECONDARY) ? cschem->symschem : cschem;
2020
2021 /* If this polygon is already in the list, then add an extra subnet */
2022 /* if necessary. */
2023
2024 for (newpoly = pschem->polygons; newpoly != NULL; newpoly = newpoly->next) {
2025 if (newpoly->poly == poly) {
2026 if (!match_buses((Genericlist *)newpoly, netlist, MATCH_EXACT)) {
2027 Fprintf(stderr, "addpoly: Error in bus assignment\n");
2028 return NULL;
2029 }
2030 return (Genericlist *)newpoly;
2031 }
2032 }
2033
2034 /* Create a new entry and link to polygon list of this object */
2035
2036 newpoly = (PolylistPtr) malloc(sizeof(Polylist));
2037 newpoly->cschem = cschem;
2038 newpoly->poly = poly;
2039 newpoly->subnets = 0;
2040 copy_bus((Genericlist *)newpoly, netlist);
2041 newpoly->next = pschem->polygons;
2042 pschem->polygons = newpoly;
2043
2044 return (Genericlist *)newpoly;
2045 }
2046
2047 /*-------------------------------------------------------------------------*/
2048
zsign(long a,long b)2049 long zsign(long a, long b)
2050 {
2051 if (a > b) return 1;
2052 else if (a < b) return -1;
2053 else return 0;
2054 }
2055
2056 /*----------------------------------------------------------------------*/
2057 /* Promote a single net to a bus. The bus size will be equal to the */
2058 /* value "subnets". */
2059 /*----------------------------------------------------------------------*/
2060
promote_net(objectptr cschem,Genericlist * netfrom,int subnets)2061 void promote_net(objectptr cschem, Genericlist *netfrom, int subnets)
2062 {
2063 Genericlist *netref = NULL;
2064 CalllistPtr calls;
2065 PortlistPtr ports;
2066 PolylistPtr plist;
2067 LabellistPtr llist;
2068 int netid, firstid, lbus; /* curid, (jdk) */
2069 buslist *sbus;
2070 Boolean foundlabel;
2071
2072 /* If no promotion is required, don't do anything */
2073 if (netfrom->subnets == subnets) return;
2074
2075 /* It "netfrom" is already a bus, but of different size than */
2076 /* subnets, then it cannot be changed. */
2077
2078 if (netfrom->subnets != 0) {
2079 Fprintf(stderr, "Attempt to change the size of a bus!\n");
2080 return;
2081 }
2082
2083 netid = netfrom->net.id;
2084
2085 /* If "subnets" is 1, then "netfrom" can be promoted regardless. */
2086 /* Otherwise, if the "netfrom" net is used in any calls, then it */
2087 /* cannot be promoted. */
2088
2089 if (subnets > 1) {
2090 for (calls = cschem->calls; calls != NULL; calls = calls->next)
2091 for (ports = calls->ports; ports != NULL; ports = ports->next)
2092 if (ports->netid == netid) {
2093 Fprintf(stderr, "Cannot promote net to bus: Net already connected"
2094 " to single-wire port\n");
2095 return;
2096 }
2097 firstid = netmax(cschem) + 1;
2098 }
2099
2100 for (plist = cschem->polygons; plist != NULL; plist = plist->next)
2101 if ((plist->subnets == 0) && (plist->net.id == netid)) {
2102 plist->subnets = subnets;
2103 plist->net.list = (buslist *)malloc(subnets * sizeof(buslist));
2104 for (lbus = 0; lbus < subnets; lbus++) {
2105 sbus = plist->net.list + lbus;
2106 sbus->netid = (lbus == 0) ? netid : firstid + lbus;
2107 sbus->subnetid = lbus; /* By default, number from zero */
2108 }
2109 netref = (Genericlist *)plist;
2110 }
2111
2112 /* It's possible for a label without bus notation to be attached */
2113 /* to this net. */
2114
2115 foundlabel = FALSE;
2116 for (llist = cschem->labels; llist != NULL; llist = llist->next)
2117 if ((llist->subnets == 0) && (llist->net.id == netid)) {
2118 llist->subnets = subnets;
2119 llist->net.list = (buslist *)malloc(subnets * sizeof(buslist));
2120 for (lbus = 0; lbus < subnets; lbus++) {
2121 sbus = llist->net.list + lbus;
2122 sbus->netid = (lbus == 0) ? netid : firstid + lbus;
2123 sbus->subnetid = lbus; /* By default, number from zero */
2124 }
2125 netref = (Genericlist *)llist;
2126 foundlabel = TRUE;
2127 }
2128
2129 /* We need to create a temp label associated with this net to */
2130 /* encompass the promoted bus size. If this bus is later attached */
2131 /* to a known bus, that bus name will be canonical. If no bus name */
2132 /* is ever assigned to the bus, this temp one will be used. */
2133
2134 if (!foundlabel) {
2135 XPoint *pinpos;
2136 pinpos = NetToPosition(netid, cschem);
2137 new_tmp_pin(cschem, pinpos, NULL, "int", netref);
2138 }
2139 }
2140
2141 /*----------------------------------------------------------------------*/
2142 /* Change any part of the netlist "testlist" that matches the net IDs */
2143 /* of "orignet" to the net IDs of "newnet". It is assumed that the */
2144 /* lengths and sub-bus numbers of "orignet" and "newnet" match. */
2145 /*----------------------------------------------------------------------*/
2146
mergenetlist(objectptr cschem,Genericlist * testlist,Genericlist * orignet,Genericlist * newnet)2147 Boolean mergenetlist(objectptr cschem, Genericlist *testlist,
2148 Genericlist *orignet, Genericlist *newnet)
2149 {
2150 int obus, onetid, osub, nsub, nnetid, tbus;
2151 buslist *sbus;
2152 Boolean rval = FALSE;
2153
2154 for (obus = 0;;) {
2155 if (orignet->subnets == 0) {
2156 onetid = orignet->net.id;
2157 osub = -1;
2158 }
2159 else {
2160 sbus = orignet->net.list + obus;
2161 onetid = sbus->netid;
2162 osub = sbus->subnetid;
2163 }
2164
2165 if (newnet->subnets == 0) {
2166 nnetid = newnet->net.id;
2167 nsub = -1;
2168 }
2169 else {
2170 sbus = newnet->net.list + obus;
2171 nnetid = sbus->netid;
2172 nsub = sbus->subnetid;
2173 }
2174
2175 if (testlist->subnets == 0) {
2176 if (testlist->net.id == onetid) {
2177 rval = TRUE;
2178 if (orignet->subnets == 0) {
2179 testlist->net.id = nnetid;
2180 return TRUE;
2181 }
2182 else {
2183 /* Promote testlist to a bus subnet of size 1 */
2184 testlist->subnets = 1;
2185 testlist->net.list = (buslist *)malloc(sizeof(buslist));
2186 sbus = testlist->net.list;
2187 sbus->netid = nnetid;
2188 sbus->subnetid = nsub;
2189 return rval;
2190 }
2191 }
2192 }
2193
2194 for (tbus = 0; tbus < testlist->subnets; tbus++) {
2195 /* If the sub-bus numbers match, then change the net */
2196 /* ID to the new net number. If the sub-bus is */
2197 /* unnamed (has no associated bus-notation label), */
2198 /* then it can take on the net and subnet numbers. */
2199
2200 sbus = testlist->net.list + tbus;
2201
2202 if (sbus->netid == onetid) {
2203 if (sbus->subnetid == osub) {
2204 sbus->netid = nnetid;
2205 sbus->subnetid = nsub;
2206 rval = TRUE;
2207 }
2208 else {
2209 labelptr blab = NetToLabel(nnetid, cschem);
2210 if (blab == NULL) {
2211 Fprintf(stderr, "Warning: isolated subnet?\n");
2212 sbus->netid = nnetid;
2213 /* Keep subnetid---but, does newnet need to be promoted? */
2214 return TRUE;
2215 }
2216 else if (blab->string->type != FONT_NAME) {
2217 sbus->netid = nnetid;
2218 sbus->subnetid = nsub;
2219 rval = TRUE;
2220 Fprintf(stderr, "Warning: Unexpected subnet value in mergenetlist!\n");
2221 }
2222 }
2223 }
2224 }
2225 if (++obus >= orignet->subnets) break;
2226 }
2227 return rval;
2228 }
2229
2230 /*----------------------------------------------------------------------*/
2231 /* Combine two networks in an object's linked-list Netlist */
2232 /* Parameters: cschem - pointer to object containing netlist */
2233 /* orignet - original netlist to be changed */
2234 /* newnet - new netlist to be changed to */
2235 /* */
2236 /* Attempts to merge different subnets in a bus are thwarted. */
2237 /*----------------------------------------------------------------------*/
2238
netmerge(objectptr cschem,Genericlist * orignet,Genericlist * newnet)2239 Boolean netmerge(objectptr cschem, Genericlist *orignet, Genericlist *newnet)
2240 {
2241 PolylistPtr plist;
2242 LabellistPtr llist;
2243 CalllistPtr calls;
2244 PortlistPtr ports;
2245 Genericlist savenet;
2246 int i, net;
2247 buslist *obus, *nbus;
2248 Boolean rval;
2249
2250 /* Trivial case; do nothing */
2251 if (match_buses(orignet, newnet, MATCH_EXACT)) return TRUE;
2252
2253 /* Disallow an attempt to convert a global net to a local net: */
2254 /* The global net ID always dominates! */
2255
2256 if ((orignet->subnets == 0) && (newnet->subnets == 0) &&
2257 (orignet->net.id < 0) && (newnet->net.id > 0)) {
2258 int globnet = orignet->net.id;
2259 orignet->net.id = newnet->net.id;
2260 newnet->net.id = globnet;
2261 }
2262
2263 /* Check that the lists of changes are compatible. It appears to be */
2264 /* okay to have non-matching buses (although they should not be */
2265 /* merged), as this is a valid style in which buses do not have taps */
2266 /* but the sub-buses are explicitly called out with labels. In such */
2267 /* case, the polygons of different sub-buses touch, and this routine */
2268 /* rejects them as being incompatible. */
2269
2270 if (!match_buses(orignet, newnet, MATCH_SUBNETS)) {
2271 if (match_buses(orignet, newnet, MATCH_SIZE)) {
2272 labelptr nlab;
2273 /* If only the subnet numbers don't match up, check if */
2274 /* "orignet" has a temp label. */
2275 nbus = orignet->net.list;
2276 if ((nlab = NetToLabel(nbus->netid, cschem)) != NULL) {
2277 if (nlab->string->type != FONT_NAME)
2278 goto can_merge;
2279 }
2280 }
2281 else
2282 Fprintf(stderr, "netmerge warning: non-matching bus subnets touching.\n");
2283 return FALSE;
2284 }
2285
2286 can_merge:
2287
2288 /* If orignet is a bus size 1 and newnet is a wire, then promote */
2289 /* newnet to a bus size 1. */
2290
2291 if (orignet->subnets == 1 && newnet->subnets == 0) {
2292 net = newnet->net.id;
2293 newnet->subnets = 1;
2294 newnet->net.list = (buslist *)malloc(sizeof(buslist));
2295 obus = orignet->net.list;
2296 nbus = newnet->net.list;
2297 nbus->subnetid = obus->subnetid;
2298 nbus->netid = net;
2299 }
2300
2301 /* Make a copy of the net so we don't overwrite it */
2302 savenet.subnets = 0;
2303 copy_bus(&savenet, orignet);
2304
2305 rval = FALSE;
2306 for (plist = cschem->polygons; plist != NULL; plist = plist->next)
2307 if (mergenetlist(cschem, (Genericlist *)plist, &savenet, newnet))
2308 rval = TRUE;
2309
2310 for (llist = cschem->labels; llist != NULL; llist = llist->next)
2311 if (mergenetlist(cschem, (Genericlist *)llist, &savenet, newnet)) {
2312 int pinnet;
2313 char *newtext;
2314 rval = TRUE;
2315
2316 /* Because nets that have been merged away may be re-used later, */
2317 /* change the name of any temporary labels to the new net number */
2318
2319 if (llist->label->string->type != FONT_NAME) {
2320 newtext = llist->label->string->data.string;
2321 if (sscanf(newtext + 3, "%d", &pinnet) == 1) {
2322 if (pinnet == savenet.net.id) {
2323 *(newtext + 3) = '\0';
2324 llist->label->string->data.string = textprintnet(newtext,
2325 NULL, newnet);
2326 free(newtext);
2327 }
2328 }
2329 }
2330 }
2331
2332 if (rval) {
2333
2334 /* Reflect the net change in the object's call list, if it has one. */
2335
2336 for (calls = cschem->calls; calls != NULL; calls = calls->next) {
2337 for (ports = calls->ports; ports != NULL; ports = ports->next) {
2338 if (newnet->subnets == 0) {
2339 if (ports->netid == savenet.net.id)
2340 ports->netid = newnet->net.id;
2341 }
2342 else {
2343 for (i = 0; i < newnet->subnets; i++) {
2344 obus = savenet.net.list + i;
2345 nbus = newnet->net.list + i;
2346 if (ports->netid == obus->netid)
2347 ports->netid = nbus->netid;
2348 }
2349 }
2350 }
2351 }
2352 }
2353
2354 /* Free the copy of the bus that we made, if we made one */
2355 if (savenet.subnets > 0) free(savenet.net.list);
2356
2357 return rval;
2358 }
2359
2360 /*----------------------------------------------------------------------*/
2361 /* Wrapper to netmerge() to make sure change is made both to the symbol */
2362 /* and schematic, if both exist. */
2363 /*----------------------------------------------------------------------*/
2364
mergenets(objectptr cschem,Genericlist * orignet,Genericlist * newnet)2365 Boolean mergenets(objectptr cschem, Genericlist *orignet, Genericlist *newnet)
2366 {
2367 Boolean merged;
2368
2369 if (cschem->symschem != NULL)
2370 merged = netmerge(cschem->symschem, orignet, newnet);
2371 if (netmerge(cschem, orignet, newnet))
2372 merged = TRUE;
2373
2374 return merged;
2375 }
2376
2377 /*----------------------------------------------------------------------*/
2378 /* Remove a call to an object instance from the call list of cschem */
2379 /*----------------------------------------------------------------------*/
2380
removecall(objectptr cschem,CalllistPtr dontcallme)2381 void removecall(objectptr cschem, CalllistPtr dontcallme)
2382 {
2383 CalllistPtr lastcall, seeklist;
2384 PortlistPtr ports, savelist;
2385
2386 /* find the instance call before this one and link it to the one following */
2387
2388 lastcall = NULL;
2389 for (seeklist = cschem->calls; seeklist != NULL; seeklist = seeklist->next) {
2390 if (seeklist == dontcallme)
2391 break;
2392 lastcall = seeklist;
2393 }
2394
2395 if (seeklist == NULL) {
2396 Fprintf(stderr, "Error in removecall(): Call does not exist!\n");
2397 return;
2398 }
2399
2400 if (lastcall == NULL)
2401 cschem->calls = dontcallme->next;
2402 else
2403 lastcall->next = dontcallme->next;
2404
2405 ports = dontcallme->ports;
2406 while (ports != NULL) {
2407 savelist = ports;
2408 ports = ports->next;
2409 free (savelist);
2410 }
2411 free(dontcallme);
2412 }
2413
2414 /*----------------------------------------------------------------------*/
2415 /* Add a pin label to the netlist */
2416 /* If cschem == NULL, add the pin to the list of global pins. */
2417 /* The new label entry in the netlist gets a copy of the netlist */
2418 /* "netlist". */
2419 /*----------------------------------------------------------------------*/
2420
addpin(objectptr cschem,objinstptr cinst,labelptr pin,Genericlist * netlist)2421 Genericlist *addpin(objectptr cschem, objinstptr cinst, labelptr pin,
2422 Genericlist *netlist)
2423 {
2424 LabellistPtr srchlab, newlabel, lastlabel = NULL;
2425 objectptr pschem;
2426 /* buslist *sbus; (jdk) */
2427 /* int lbus, sub_bus; (jdk) */
2428
2429 pschem = (cschem->schemtype == SECONDARY) ? cschem->symschem : cschem;
2430
2431 for (srchlab = pschem->labels; srchlab != NULL; srchlab = srchlab->next) {
2432 if (srchlab->label == pin) {
2433 if (!match_buses(netlist, (Genericlist *)srchlab, MATCH_EXACT)) {
2434 if (srchlab->cinst == cinst) {
2435 Fprintf(stderr, "addpin: Error in bus assignment\n");
2436 return NULL;
2437 }
2438 }
2439 else if (srchlab->cinst == NULL)
2440 return (Genericlist *)srchlab;
2441 break; /* Stop at the first record for this label */
2442 }
2443 lastlabel = srchlab;
2444 }
2445
2446 /* Create a new entry and link to label list of the object */
2447
2448 newlabel = (LabellistPtr) malloc(sizeof(Labellist));
2449 newlabel->cschem = cschem;
2450 newlabel->cinst = cinst;
2451 newlabel->label = pin;
2452 newlabel->subnets = 0;
2453 copy_bus((Genericlist *)newlabel, netlist);
2454
2455 /* Always put the specific (instanced) cases in front of */
2456 /* generic cases for the same label. */
2457
2458 /* Generic case---make it the last record for this label */
2459 if ((cinst == NULL) && (lastlabel != NULL)) {
2460 while ((srchlab != NULL) && (srchlab->label == pin)) {
2461 lastlabel = srchlab;
2462 srchlab = srchlab->next;
2463 }
2464 }
2465 if (lastlabel != NULL) {
2466 newlabel->next = srchlab;
2467 lastlabel->next = newlabel;
2468 }
2469 else {
2470 newlabel->next = pschem->labels;
2471 pschem->labels = newlabel;
2472 }
2473 return (Genericlist *)newlabel;
2474 }
2475
2476 /*----------------------------------------------------------------------*/
2477 /* Add a pin label to the list of global net names. */
2478 /* The new label entry in the netlist gets a copy of the netlist */
2479 /* "netlist" and a copy of label "pin" containing the *instance* value */
2480 /* of the text. */
2481 /*----------------------------------------------------------------------*/
2482
addglobalpin(objectptr cschem,objinstptr cinst,labelptr pin,Genericlist * netlist)2483 Genericlist *addglobalpin(objectptr cschem, objinstptr cinst, labelptr pin,
2484 Genericlist *netlist)
2485 {
2486 LabellistPtr srchlab, newlabel, lastlabel = NULL;
2487
2488 if (cinst == NULL) {
2489 Fprintf(stderr, "Error: Global pin does not have an associated instance!\n");
2490 return NULL;
2491 }
2492
2493 for (srchlab = global_labels; srchlab != NULL; srchlab = srchlab->next) {
2494 if (srchlab->label == pin) {
2495 if (!match_buses(netlist, (Genericlist *)srchlab, MATCH_EXACT)) {
2496 if (srchlab->cinst == cinst) {
2497 Fprintf(stderr, "addglobalpin: Error in bus assignment\n");
2498 return NULL;
2499 }
2500 }
2501 else if (srchlab->cinst == NULL)
2502 return (Genericlist *)srchlab;
2503 break; /* Stop at the first record for this label */
2504 }
2505 lastlabel = srchlab;
2506 }
2507
2508 /* Create a new entry and link to label list of the object */
2509
2510 newlabel = (LabellistPtr) malloc(sizeof(Labellist));
2511 newlabel->cschem = cschem;
2512 newlabel->cinst = cinst;
2513 newlabel->label = new_global_pin(pin, cinst);
2514 newlabel->subnets = 0;
2515 copy_bus((Genericlist *)newlabel, netlist);
2516
2517 if (lastlabel != NULL) {
2518 newlabel->next = srchlab;
2519 lastlabel->next = newlabel;
2520 }
2521 else {
2522 newlabel->next = global_labels;
2523 global_labels = newlabel;
2524 }
2525 return (Genericlist *)newlabel;
2526 }
2527
2528 /*----------------------------------------------------------------------*/
2529 /* Allocate memory for the new call list element */
2530 /* Define the values for the new call list element */
2531 /* Insert new call into call list */
2532 /* The new call is at the beginning of the list. */
2533 /*----------------------------------------------------------------------*/
2534
addcall(objectptr cschem,objectptr callobj,objinstptr callinst)2535 void addcall(objectptr cschem, objectptr callobj, objinstptr callinst)
2536 {
2537 CalllistPtr newcall;
2538 objectptr pschem;
2539
2540 /* Netlist is on the master schematic */
2541 pschem = (cschem->schemtype == SECONDARY) ? cschem->symschem : cschem;
2542
2543 newcall = (CalllistPtr) malloc(sizeof(Calllist));
2544 newcall->cschem = cschem;
2545 newcall->callobj = callobj;
2546 newcall->callinst = callinst;
2547 newcall->devindex = -1;
2548 newcall->devname = NULL;
2549 newcall->ports = NULL;
2550 newcall->next = pschem->calls;
2551 pschem->calls = newcall;
2552 }
2553
2554 /*----------------------------------------------------------------------*/
2555 /* Add a port to the object cschem which connects net "netto" to */
2556 /* the calling object. One port is created for each net ID in "netto" */
2557 /* (which may be a bus). The port contains the net ID in the called */
2558 /* object. The port may already exist, in which case this routine does */
2559 /* nothing. */
2560 /*----------------------------------------------------------------------*/
2561
addport(objectptr cschem,Genericlist * netto)2562 void addport(objectptr cschem, Genericlist *netto)
2563 {
2564 PortlistPtr newport, seekport;
2565 int portid = 0, netid, lbus;
2566 buslist *sbus;
2567 Boolean duplicate;
2568
2569 for (lbus = 0;;) {
2570
2571 if (netto->subnets == 0)
2572 netid = netto->net.id;
2573 else {
2574 sbus = netto->net.list + lbus;
2575 netid = sbus->netid;
2576 }
2577
2578 /* If a port already exists for this net, don't add another one! */
2579
2580 duplicate = FALSE;
2581 for (seekport = cschem->ports; seekport != NULL; seekport = seekport->next) {
2582 if (seekport->netid != netid) {
2583 if (seekport->portid > portid)
2584 portid = seekport->portid;
2585 }
2586 else
2587 duplicate = TRUE;
2588 }
2589
2590 if (!duplicate) {
2591 portid++;
2592
2593 newport = (PortlistPtr)malloc(sizeof(Portlist));
2594 newport->netid = netid;
2595 newport->portid = portid;
2596
2597 if (cschem->ports != NULL)
2598 newport->next = cschem->ports;
2599 else
2600 newport->next = NULL;
2601
2602 cschem->ports = newport;
2603 }
2604 if (++lbus >= netto->subnets) break;
2605 }
2606 }
2607
2608 /*----------------------------------------------------------------------*/
2609 /* Add a specific port connection from object cschem into the instance */
2610 /* cinst. This equates a net number in the calling object cschem */
2611 /* (netfrom) to a net number in the object instance being called */
2612 /* (netto). */
2613 /* */
2614 /* If we attempt to connect a bus of one size to a port of a different */
2615 /* size, return FALSE. Otherwise, add the call and return TRUE. */
2616 /*----------------------------------------------------------------------*/
2617
addportcall(objectptr cschem,Genericlist * netfrom,Genericlist * netto)2618 Boolean addportcall(objectptr cschem, Genericlist *netfrom, Genericlist *netto)
2619 {
2620 CalllistPtr ccall;
2621 PortlistPtr seekport, sp, newport;
2622 objectptr instobj;
2623 objinstptr cinst;
2624 int lbus, netid_from, netid_to;
2625 buslist *sbus, *tbus;
2626 Boolean duplicate;
2627
2628 /* The call that we need to add a port to is the first on */
2629 /* the list for cschem, as created by addcall(). */
2630 ccall = cschem->calls;
2631 instobj = ccall->callobj;
2632 cinst = ccall->callinst;
2633
2634 if (netfrom->subnets != netto->subnets) {
2635 if (netfrom->subnets == 0) {
2636 /* It is possible that "netfrom" is an unlabeled polygon that */
2637 /* is implicitly declared a bus by its connection to this */
2638 /* port. If so, we promote "netfrom" to a bus but set the */
2639 /* subnet entries to negative values, since we don't know what */
2640 /* they are yet. */
2641 promote_net(cschem, netfrom, netto->subnets);
2642 }
2643
2644 /* Only other allowable condition is a bus size 1 connecting into */
2645 /* a single-wire port. However, one should consider promotion of */
2646 /* the pin in the target object to a bus on a per-instance basis. */
2647 /* This has not yet been done. . . */
2648
2649 else if ((netfrom->subnets != 1) || (netto->subnets != 0)) {
2650 /* Let the caller report error information, because it knows more */
2651 return FALSE;
2652 }
2653 }
2654
2655 for (lbus = 0;;) {
2656 buslist bsingf, bsingo;
2657 Genericlist subnet_from, subnet_other;
2658
2659 if (netfrom->subnets == 0) {
2660 netid_from = netfrom->net.id;
2661
2662 subnet_from.subnets = 0;
2663 subnet_from.net.id = netid_from;
2664
2665 subnet_other.subnets = 0;
2666 }
2667 else {
2668 sbus = netfrom->net.list + lbus;
2669 netid_from = sbus->netid;
2670
2671 subnet_from.subnets = 1;
2672 subnet_from.net.list = &bsingf;
2673 bsingf.netid = netid_from;
2674 bsingf.subnetid = sbus->subnetid;
2675
2676 bsingo.subnetid = lbus;
2677 subnet_other.subnets = 1;
2678 subnet_other.net.list = &bsingo;
2679 }
2680 if (netto->subnets == 0) {
2681 netid_to = netto->net.id;
2682 }
2683 else {
2684 tbus = netto->net.list + lbus;
2685 netid_to = tbus->netid;
2686 }
2687
2688 /* Check the ports of the instance's object for the one matching */
2689 /* the "netto" net ID. */
2690
2691 duplicate = FALSE;
2692 for (seekport = instobj->ports; seekport != NULL;
2693 seekport = seekport->next) {
2694 if (seekport->netid == netid_to) {
2695
2696 /* If there is already an entry for this port, then */
2697 /* we may need to merge nets in cschem. */
2698
2699 for (sp = ccall->ports; sp != NULL; sp = sp->next)
2700 if (sp->portid == seekport->portid) {
2701 if (sp->netid != netid_from) {
2702 if (netfrom->subnets == 0)
2703 subnet_other.net.id = sp->netid;
2704 else {
2705 bsingo.netid = sp->netid;
2706 bsingo.subnetid = getsubnet(bsingo.netid, cschem);
2707 }
2708 if (!mergenets(cschem, &subnet_other, &subnet_from)) {
2709 /* Upon failure, see if we can merge the other */
2710 /* direction. */
2711 if (!mergenets(cschem, &subnet_from, &subnet_other))
2712 continue;
2713 else {
2714 if (subnet_from.subnets == 0)
2715 subnet_from.net.id = subnet_other.net.id;
2716 else {
2717 bsingf.netid = bsingo.netid;
2718 bsingf.subnetid = bsingo.subnetid;
2719 }
2720 }
2721 }
2722 }
2723 duplicate = TRUE;
2724 }
2725
2726 if (!duplicate) {
2727 newport = (PortlistPtr)malloc(sizeof(Portlist));
2728 newport->netid = netid_from;
2729 newport->portid = seekport->portid;
2730 newport->next = ccall->ports;
2731 ccall->ports = newport;
2732 }
2733 break;
2734 }
2735 }
2736 if (++lbus >= netfrom->subnets) break;
2737 }
2738 return TRUE;
2739 }
2740
2741 /*----------------------------------------------------------------------*/
2742 /* Find the net ID corresponding to the indicated port ID in the */
2743 /* indicated object. */
2744 /*----------------------------------------------------------------------*/
2745
porttonet(objectptr cschem,int portno)2746 int porttonet(objectptr cschem, int portno)
2747 {
2748 PortlistPtr plist;
2749
2750 for (plist = cschem->ports; plist != NULL; plist = plist->next) {
2751 if (plist->portid == portno)
2752 return plist->netid;
2753 }
2754 return 0;
2755 }
2756
2757 /*----------------------------------------------------------------------*/
2758 /* Traverse netlist and return netid of polygon or pin on which the */
2759 /* indicated point is located. */
2760 /* If point is not on any polygon or does not match any pin position, */
2761 /* return NULL. */
2762 /* Labels which have a non-NULL "cinst" record are instance-dependent */
2763 /* and must match parameter "cinst". */
2764 /* */
2765 /* This routine checks to see if more than one net crosses the point */
2766 /* of interest, and merges the nets if found. */
2767 /* (the method has been removed and must be reinstated, presumably) */
2768 /*----------------------------------------------------------------------*/
2769
pointtonet(objectptr cschem,objinstptr cinst,XPoint * testpoint)2770 Genericlist *pointtonet(objectptr cschem, objinstptr cinst, XPoint *testpoint)
2771 {
2772 XPoint *tpt, *tpt2;
2773 PolylistPtr ppoly;
2774 LabellistPtr plab;
2775 Genericlist *preturn;
2776 objectptr pschem; /* primary schematic */
2777
2778 /* cschem is the object containing the point. However, if the object */
2779 /* is a secondary schematic, the netlist is located in the master. */
2780
2781 pschem = (cschem->schemtype == SECONDARY) ? cschem->symschem : cschem;
2782
2783 for (plab = pschem->labels; plab != NULL; plab = plab->next) {
2784 if (plab->cschem != cschem) continue;
2785 else if ((plab->cinst != NULL) && (plab->cinst != cinst)) continue;
2786 tpt = &(plab->label->position);
2787 if (proximity(tpt, testpoint))
2788 return (Genericlist *)plab;
2789
2790 /* if we've encountered a unique instance, then continue past */
2791 /* all other instances using this label. */
2792
2793 if (plab->cinst != NULL)
2794 while (plab->next && (plab->next->label == plab->label))
2795 plab = plab->next;
2796 }
2797
2798 /* Check against polygons. We use this part of the routine to see */
2799 /* if there are crossing wires on top of a port. If so, they are */
2800 /* merged together. */
2801
2802 preturn = (Genericlist *)NULL;
2803 for (ppoly = pschem->polygons; ppoly != NULL; ppoly = ppoly->next) {
2804 if (ppoly->cschem != cschem) continue;
2805 for (tpt = ppoly->poly->points; tpt < ppoly->poly->points
2806 + EndPoint(ppoly->poly->number); tpt++) {
2807 tpt2 = tpt + NextPoint(ppoly->poly->number);
2808
2809 if (onsegment(tpt, tpt2, testpoint)) {
2810 if (preturn == (Genericlist *)NULL)
2811 preturn = (Genericlist *)ppoly;
2812 else
2813 mergenets(pschem, (Genericlist *)ppoly, preturn);
2814 }
2815 }
2816 }
2817
2818 return preturn;
2819 }
2820
2821 /*----------------------------------------------------------------------*/
2822 /* localpin keeps track of temporary pin labels when flattening the */
2823 /* hierarchy without destroying the original pin names. */
2824 /*----------------------------------------------------------------------*/
2825
makelocalpins(objectptr cschem,CalllistPtr clist,char * prefix)2826 void makelocalpins(objectptr cschem, CalllistPtr clist, char *prefix)
2827 {
2828 NetnamePtr netnames;
2829 PortlistPtr ports, plist;
2830 stringpart *locpin;
2831 int locnet, callnet;
2832 objectptr callobj = clist->callobj;
2833
2834 /* Copy all net names which are passed from above through ports */
2835
2836 for (ports = clist->ports; ports != NULL; ports = ports->next) {
2837 callnet = ports->netid;
2838 for (plist = callobj->ports; plist != NULL; plist = plist->next) {
2839 if (plist->portid == ports->portid) {
2840 locnet = plist->netid;
2841 locpin = nettopin(callnet, cschem, prefix);
2842 break;
2843 }
2844 }
2845
2846 for (netnames = callobj->netnames; netnames != NULL; netnames = netnames->next)
2847 if (netnames->netid == locnet)
2848 break;
2849
2850 if (netnames == NULL) {
2851 netnames = (NetnamePtr)malloc(sizeof(Netname));
2852 netnames->netid = locnet;
2853 netnames->localpin = stringcopy(locpin);
2854 netnames->next = callobj->netnames;
2855 callobj->netnames = netnames;
2856 }
2857 }
2858 }
2859
2860 /*----------------------------------------------------------------------*/
2861 /* Find the first point associated with the net "netid" in the object */
2862 /* cschem. */
2863 /*----------------------------------------------------------------------*/
2864
NetToPosition(int netid,objectptr cschem)2865 XPoint *NetToPosition(int netid, objectptr cschem)
2866 {
2867 PolylistPtr plist;
2868 LabellistPtr llist;
2869 buslist *sbus;
2870 int lbus, locnetid; /* sub_bus, (jdk) */
2871
2872 plist = cschem->polygons;
2873 for (; plist != NULL; plist = plist->next) {
2874 for (lbus = 0;;) {
2875 if (plist->subnets == 0) {
2876 locnetid = plist->net.id;
2877 }
2878 else {
2879 sbus = plist->net.list + lbus;
2880 locnetid = sbus->netid;
2881 }
2882 if (locnetid == netid) {
2883 return plist->poly->points;
2884 }
2885 if (++lbus >= plist->subnets) break;
2886 }
2887 }
2888
2889 llist = (netid < 0) ? global_labels : cschem->labels;
2890 for (; llist != NULL; llist = llist->next) {
2891 for (lbus = 0;;) {
2892 if (llist->subnets == 0) {
2893 locnetid = llist->net.id;
2894 }
2895 else {
2896 sbus = llist->net.list + lbus;
2897 locnetid = sbus->netid;
2898 }
2899 if (locnetid == netid) {
2900 return (&(llist->label->position));
2901 }
2902 if (++lbus >= llist->subnets) break;
2903 }
2904 }
2905 return NULL;
2906 }
2907
2908 /*----------------------------------------------------------------------*/
2909 /* Find a label element for the given net number. In a symbol, this */
2910 /* will be the label representing the pin (or the first one found, if */
2911 /* the pin has multiple labels). If no label is found, return NULL. */
2912 /* Preferably choose a non-temporary label, if one exists */
2913 /*----------------------------------------------------------------------*/
2914
NetToLabel(int netid,objectptr cschem)2915 labelptr NetToLabel(int netid, objectptr cschem)
2916 {
2917 LabellistPtr llist;
2918 labelptr standby = NULL;
2919 buslist *sbus;
2920 int lbus, locnetid;
2921
2922 llist = (netid < 0) ? global_labels : cschem->labels;
2923
2924 for (; llist != NULL; llist = llist->next) {
2925 for (lbus = 0;;) {
2926 if (llist->subnets == 0) {
2927 locnetid = llist->net.id;
2928 }
2929 else {
2930 sbus = llist->net.list + lbus;
2931 locnetid = sbus->netid;
2932 }
2933 if (locnetid == netid) {
2934 if (llist->label->string->type == FONT_NAME)
2935 return llist->label;
2936 else if (standby == NULL)
2937 standby = llist->label;
2938 }
2939 if (++lbus >= llist->subnets) break;
2940 }
2941 }
2942 return standby;
2943 }
2944
2945
2946 /*----------------------------------------------------------------------*/
2947 /* Find the subnet number of the given net. This routine should be */
2948 /* used only as a last resort in case the subnet number is not */
2949 /* available; it has to look through the polygon and label lists in */
2950 /* detail to find the specified net ID. */
2951 /*----------------------------------------------------------------------*/
2952
getsubnet(int netid,objectptr cschem)2953 int getsubnet(int netid, objectptr cschem)
2954 {
2955 PolylistPtr plist;
2956 LabellistPtr llist;
2957 buslist *sbus;
2958 int lbus, sub_bus, locnetid;
2959
2960 plist = cschem->polygons;
2961 for (; plist != NULL; plist = plist->next) {
2962 for (lbus = 0;;) {
2963 if (plist->subnets == 0) {
2964 locnetid = plist->net.id;
2965 sub_bus = -1;
2966 }
2967 else {
2968 sbus = plist->net.list + lbus;
2969 locnetid = sbus->netid;
2970 sub_bus = sbus->subnetid;
2971 }
2972 if (locnetid == netid) {
2973 return sub_bus;
2974 }
2975 if (++lbus >= plist->subnets) break;
2976 }
2977 }
2978
2979 llist = (netid < 0) ? global_labels : cschem->labels;
2980 for (; llist != NULL; llist = llist->next) {
2981 for (lbus = 0;;) {
2982 if (llist->subnets == 0) {
2983 locnetid = llist->net.id;
2984 sub_bus = -1;
2985 }
2986 else {
2987 sbus = llist->net.list + lbus;
2988 locnetid = sbus->netid;
2989 sub_bus = sbus->subnetid;
2990 }
2991 if (locnetid == netid) {
2992 return sub_bus;
2993 }
2994 if (++lbus >= llist->subnets) break;
2995 }
2996 }
2997 return -1;
2998 }
2999
3000 /*----------------------------------------------------------------------*/
3001 /* Find a pin name for the given net number */
3002 /* Either take the last pin name with the net, or generate a new pin, */
3003 /* assigning it a net number for a string. */
3004 /* prefix = NULL indicates spice netlist, but is also used for PCB-type */
3005 /* netlists because the calling hierarchy is generated elsewhere. */
3006 /*----------------------------------------------------------------------*/
3007
nettopin(int netid,objectptr cschem,char * prefix)3008 stringpart *nettopin(int netid, objectptr cschem, char *prefix)
3009 {
3010 NetnamePtr netname;
3011 labelptr pinlab;
3012 LabellistPtr netlabel;
3013 char *newtext, *snew = NULL;
3014 XPoint *pinpos;
3015 int locnetid; /* lbus, (jdk) */
3016 /* buslist *sbus; (jdk) */
3017 Genericlist newnet;
3018 static stringpart *newstring = NULL;
3019
3020 /* prefix is NULL for hierarchical (spice) netlists and for */
3021 /* internal netlist manipulation. */
3022
3023 if (prefix == NULL) {
3024 pinlab = NetToLabel(netid, cschem);
3025 if (pinlab != NULL) {
3026
3027 /* If this is a "temp label", regenerate the name according to */
3028 /* the actual net number, if it does not match. This prevents */
3029 /* unrelated temp labels from getting the same name. */
3030
3031 if (pinlab->string->type != FONT_NAME) {
3032 if (sscanf(pinlab->string->data.string + 3, "%d", &locnetid) == 1) {
3033 if (locnetid != netid) {
3034 newtext = pinlab->string->data.string;
3035 newtext[3] = '\0';
3036 newnet.subnets = 0;
3037 newnet.net.id = netid;
3038 pinlab->string->data.string = textprintnet(newtext, NULL, &newnet);
3039 free(newtext);
3040 }
3041 }
3042 }
3043 return pinlab->string;
3044 }
3045 else {
3046 /* If there's no label associated with this network, make one */
3047 /* called "intn" where n is the net number (netid). This is a */
3048 /* temp label (denoted by lack of a font specifier). */
3049
3050 newnet.subnets = 0;
3051 newnet.net.id = netid;
3052 pinpos = NetToPosition(netid, cschem);
3053 netlabel = (LabellistPtr)new_tmp_pin(cschem, pinpos, NULL, "int", &newnet);
3054 return (netlabel) ? netlabel->label->string : NULL;
3055 }
3056 }
3057
3058 /* Flattened (e.g., sim) netlists */
3059
3060 for (netname = cschem->netnames; netname != NULL; netname = netname->next) {
3061 if (netname->netid == netid) {
3062 if (netname->localpin != NULL)
3063 return netname->localpin;
3064 break;
3065 }
3066 }
3067
3068 /* Generate the string for the local instantiation of this pin */
3069 /* If this node does not have a pin, create one using the current */
3070 /* hierarchy prefix as the string. */
3071
3072 pinlab = NetToLabel(netid, cschem);
3073 if (pinlab != NULL) {
3074 stringpart *tmpstring = pinlab->string;
3075 snew = textprint(tmpstring, NULL);
3076 }
3077 else {
3078 snew = (char *)malloc(12);
3079 sprintf(snew, "int%d", netid);
3080 }
3081
3082 if (netid < 0) {
3083 newtext = snew;
3084 }
3085 else {
3086 newtext = (char *)malloc(1 + strlen(snew) + strlen(prefix));
3087 sprintf(newtext, "%s%s", prefix, snew);
3088 free(snew);
3089 }
3090
3091 /* "newstring" is allocated only once and should not be free'd */
3092 /* by the calling routine. */
3093
3094 if (newstring == NULL) {
3095 newstring = (stringpart *)malloc(sizeof(stringpart));
3096 newstring->nextpart = NULL;
3097 newstring->type = TEXT_STRING;
3098 }
3099 else {
3100 free(newstring->data.string);
3101 }
3102 newstring->data.string = newtext;
3103 return newstring;
3104 }
3105
3106 /*----------------------------------------------------------------------*/
3107 /* Find the net which connects to the given pin label */
3108 /* Return NULL (no net) if no match was found. */
3109 /*----------------------------------------------------------------------*/
3110
pintonet(objectptr cschem,objinstptr cinst,labelptr testpin)3111 Genericlist *pintonet(objectptr cschem, objinstptr cinst, labelptr testpin)
3112 {
3113 LabellistPtr seeklabel;
3114 int lbus, matched;
3115 buslist *sbus, *tbus;
3116 Genericlist netlist, *tmplist;
3117
3118 /* check against local pins, if this pin is declared local */
3119
3120 seeklabel = (testpin->pin == GLOBAL) ? global_labels : cschem->labels;
3121
3122 netlist.subnets = 0;
3123 for (; seeklabel != NULL; seeklabel = seeklabel->next)
3124 if (!stringcomprelaxed(seeklabel->label->string, testpin->string, cinst))
3125 {
3126 /* Quick simple case: no bus */
3127 if (seeklabel->subnets == 0)
3128 return (Genericlist *)seeklabel;
3129
3130 tmplist = break_up_bus(testpin, cinst, (Genericlist *)seeklabel);
3131
3132 /* It is necessary to be able to put together a netlist from */
3133 /* partial sources none of which may be complete. */
3134
3135 if (tmplist != NULL) {
3136 if (netlist.subnets == 0) copy_bus(&netlist, tmplist);
3137 matched = 0;
3138 for (lbus = 0; lbus < tmplist->subnets; lbus++) {
3139 sbus = netlist.net.list + lbus;
3140 tbus = tmplist->net.list + lbus;
3141 if (sbus->netid == 0)
3142 sbus->netid = tbus->netid; /* copy forward */
3143 else if (tbus->netid == 0)
3144 tbus->netid = sbus->netid; /* copy back */
3145 if (sbus->netid != 0)
3146 matched++;
3147 }
3148 if (matched == netlist.subnets) {
3149 free(netlist.net.list);
3150 return tmplist;
3151 }
3152 }
3153 }
3154
3155 if (netlist.subnets != 0) {
3156 free(netlist.net.list);
3157 return tmplist; /* Returns list with partial entries */
3158 }
3159 return NULL; /* No matches found */
3160 }
3161
3162 #ifdef TCL_WRAPPER
3163
3164 /*----------------------------------------------------------------------*/
3165 /* Automatic Schematic Generation stuff--- */
3166 /* Blow away all polygons in the schematic and replace them with a */
3167 /* simple rat's-nest (point-to-point). */
3168 /* */
3169 /* This routine is more of a proof-of-concept than a useful routine, */
3170 /* intended to be called only from the Tcl console. It is intended to */
3171 /* reveal most of the issues related to automatic schematic generation, */
3172 /* in which the netlist, or a portion of it, can be redrawn as objects */
3173 /* are moved around to maintain the relationships present in the */
3174 /* calls structure. */
3175 /*----------------------------------------------------------------------*/
3176
ratsnest(objinstptr thisinst)3177 void ratsnest(objinstptr thisinst)
3178 {
3179 CalllistPtr calls;
3180 PortlistPtr ports;
3181 PolylistPtr plist;
3182 LabellistPtr llist;
3183 objectptr pschem, cschem;
3184 objinstptr cinst;
3185 polyptr ppoly, *newpoly;
3186 buslist *sbus;
3187 int i, netid, points, sub_bus, lbus;
3188 XPoint portpos;
3189 Boolean result;
3190
3191 cschem = thisinst->thisobject;
3192 pschem = (cschem->schemtype == SECONDARY) ? cschem->symschem : cschem;
3193
3194 /* Go through the netlist and remove all polygons. Remove */
3195 /* the polygons from the object as well as from the netlist. */
3196
3197 for (plist = pschem->polygons; plist != NULL; plist = plist->next) {
3198 ppoly = plist->poly;
3199 ppoly->type += REMOVE_TAG; /* tag for removal */
3200 }
3201 freepolylist(&pschem->polygons);
3202
3203 /* for each schematic (PRIMARY or SECONDARY), remove the tagged */
3204 /* elements. */
3205
3206 for (i = 0; i < xobjs.pages; i++) {
3207 cinst = xobjs.pagelist[i]->pageinst;
3208 if (cinst && (cinst->thisobject->schemtype == SECONDARY) &&
3209 (cinst->thisobject->symschem == pschem)) {
3210 delete_tagged(cinst);
3211 }
3212 else if (cinst == thisinst)
3213 delete_tagged(cinst);
3214 }
3215
3216 /* Now, for each net ID in the label list, run through the calls and */
3217 /* find all points in the calls connecting to that net. Link */
3218 /* them with as many polygons as needed. These polygons are point- */
3219 /* to-point lines, turning the schematic representation into a */
3220 /* "rat's-nest". */
3221
3222 for (llist = pschem->labels; llist != NULL; llist = llist->next) {
3223 for (lbus = 0;;) {
3224 if (llist->subnets == 0) {
3225 netid = llist->net.id;
3226 sub_bus = -1;
3227 }
3228 else {
3229 sbus = llist->net.list + lbus;
3230 netid = sbus->netid;
3231 sub_bus = sbus->subnetid;
3232 }
3233
3234 points = 0;
3235 for (calls = pschem->calls; calls != NULL; calls = calls->next) {
3236
3237 /* Determine schematic object from the object called. Start a */
3238 /* new polygon any time we switch schematic pages. */
3239
3240 if (calls->cschem != cschem)
3241 points = 0;
3242 cschem = calls->cschem;
3243
3244 for (ports = calls->ports; ports != NULL; ports = ports->next) {
3245 if (ports->netid == netid) {
3246
3247 /* Find the location of this port in the coordinates of cschem */
3248
3249 result = PortToPosition(calls->callinst, ports->portid, &portpos);
3250 if (result == True) {
3251 points++;
3252 if (points == 1) {
3253 NEW_POLY(newpoly, cschem);
3254 polydefaults(*newpoly, 1, portpos.x, portpos.y);
3255 (*newpoly)->style |= UNCLOSED;
3256 (*newpoly)->color = RATSNESTCOLOR;
3257 addpoly(cschem, *newpoly, (Genericlist *)llist);
3258 }
3259 else
3260 poly_add_point(*newpoly, &portpos);
3261 }
3262 else {
3263 Fprintf(stderr, "Error: Cannot find pin connection in symbol!\n");
3264 /* Deal with error condition? */
3265 }
3266 }
3267 }
3268 }
3269 if (++lbus >= llist->subnets) break;
3270 }
3271 }
3272
3273 /* Now, move all the labels to reconnect to their networks */
3274
3275 /* (to be done) */
3276
3277 /* Refresh the screen */
3278 drawarea(NULL, NULL, NULL);
3279 }
3280
3281 #endif
3282
3283 /*----------------------------------------------------------------------*/
3284 /* Find a net or netlist in object cschem with the indicated name. */
3285 /* */
3286 /* This is the same routine as above, but finds the net with the given */
3287 /* name, or all nets which are a subset of the given name, if the name */
3288 /* is the name of a bus. Return NULL if no match was found. */
3289 /*----------------------------------------------------------------------*/
3290
nametonet(objectptr cschem,objinstptr cinst,char * netname)3291 Genericlist *nametonet(objectptr cschem, objinstptr cinst, char *netname)
3292 {
3293 LabellistPtr seeklabel;
3294 stringpart newstring;
3295
3296 /* Build a simple xcircuit string from "netname" (nothing malloc'd) */
3297 newstring.type = TEXT_STRING;
3298 newstring.nextpart = NULL;
3299 newstring.data.string = netname;
3300
3301 /* Check against local networks */
3302
3303 for (seeklabel = cschem->labels; seeklabel != NULL; seeklabel = seeklabel->next)
3304 if (!stringcomprelaxed(seeklabel->label->string, &newstring, cinst))
3305 return (Genericlist *)seeklabel;
3306
3307 /* Check against global networks */
3308
3309 for (seeklabel = global_labels; seeklabel != NULL; seeklabel = seeklabel->next)
3310 if (!stringcomprelaxed(seeklabel->label->string, &newstring, cinst))
3311 return (Genericlist *)seeklabel;
3312
3313 return NULL;
3314 }
3315
3316 /*----------------------------------------------------------------------*/
3317 /* Check if two parts may have the same component index. This is true */
3318 /* for parts with parameterized pins, such as those in the "quadparts" */
3319 /* library. Compare the names of the ports in each instance. If none */
3320 /* match, then these are unique subparts of a single component, and we */
3321 /* should return FALSE. Otherwise, return TRUE. */
3322 /*----------------------------------------------------------------------*/
3323
samepart(CalllistPtr clist1,CalllistPtr clist2)3324 Boolean samepart(CalllistPtr clist1, CalllistPtr clist2)
3325 {
3326 PortlistPtr port;
3327 labelptr plab;
3328 char *pinname1, *pinname2;
3329 Boolean rvalue;
3330
3331 if (clist1->callobj != clist2->callobj) return FALSE;
3332
3333 rvalue = FALSE;
3334 for (port = clist1->ports; port != NULL; port = port->next) {
3335 plab = PortToLabel(clist1->callinst, port->portid);
3336 pinname1 = textprint(plab->string, clist1->callinst);
3337 pinname2 = textprint(plab->string, clist2->callinst);
3338 if (!strcmp(pinname1, pinname2)) rvalue = TRUE;
3339 free(pinname1);
3340 free(pinname2);
3341 }
3342 return rvalue;
3343 }
3344
3345 /*----------------------------------------------------------------------*/
3346 /* Generate an index number for a device in a flat netlist format. */
3347 /* We ignore any values given to "index" (this is a to-do item, since */
3348 /* many schematics are only a single level of hierarchy, so it makes */
3349 /* sense to keep designated indices when possible), but do generate */
3350 /* independent index numbering for different device classes. */
3351 /*----------------------------------------------------------------------*/
3352
devflatindex(char * devname)3353 u_int devflatindex(char *devname)
3354 {
3355 flatindex *fp = flatrecord;
3356 while (fp != NULL) {
3357 if (!strcmp(devname, fp->devname)) {
3358 return ++fp->index;
3359 }
3360 fp = fp->next;
3361 }
3362 fp = (flatindex *)malloc(sizeof(flatindex));
3363 fp->next = flatrecord;
3364 flatrecord = fp;
3365 fp->index = 1;
3366 fp->devname = devname;
3367 return 1;
3368 }
3369
3370 /*----------------------------------------------------------------------*/
3371 /* Free the list of per-class flat device indices */
3372 /*----------------------------------------------------------------------*/
3373
freeflatindex()3374 void freeflatindex()
3375 {
3376 flatindex *fpnext, *fp = flatrecord;
3377 while (fp != NULL) {
3378 fpnext = fp->next;
3379 free(fp);
3380 fp = fpnext;
3381 }
3382 flatrecord = NULL;
3383 }
3384
3385 /*---------------------------------------------------------*/
3386 /* Convert a number (up to 99999) to its base-36 equivalent */
3387 /*---------------------------------------------------------*/
3388
convert_to_b36(int number)3389 int convert_to_b36(int number)
3390 {
3391 int b36idx, tmpidx;
3392
3393 tmpidx = number;
3394 b36idx = (tmpidx / 10000) * 1679616;
3395 tmpidx %= 10000;
3396 b36idx += (tmpidx / 1000) * 46656;
3397 tmpidx %= 1000;
3398 b36idx += (tmpidx / 100) * 1296;
3399 tmpidx %= 100;
3400 b36idx += ((tmpidx / 10) * 36) + (tmpidx % 10);
3401
3402 return b36idx;
3403 }
3404
3405 /*----------------------------------------------------------------------*/
3406 /* Generate an index number for this device. Count all devices having */
3407 /* the same device name (as printed in the netlist) in the calls of */
3408 /* the calling object (cfrom) */
3409 /* */
3410 /* Update (9/29/04): Remove any leading whitespace from the device */
3411 /* name to prevent treating, for example, "J?" and " J?" as separate */
3412 /* device types. */
3413 /* */
3414 /* Update (7/28/05): To allow SPICE device indices to include all */
3415 /* alphanumerics, we parse in base-36, and count in the subset of */
3416 /* base-36 that can be interpreted as decimal (1-9, 36-45, etc.) */
3417 /*----------------------------------------------------------------------*/
3418
devindex(objectptr cfrom,CalllistPtr clist)3419 u_int devindex(objectptr cfrom, CalllistPtr clist)
3420 {
3421 CalllistPtr cptr, listfrom = cfrom->calls;
3422 objectptr devobj = clist->callobj;
3423 u_int *occupied, total, objindex, i, b36idx;
3424 char *devname, *cname;
3425 /* oparamptr ops; (jdk) */
3426
3427 if (listfrom == NULL) return (u_int)0;
3428 if (clist->devindex >= 0) return clist->devindex;
3429
3430 devname = (clist->devname == NULL) ? devobj->name : clist->devname;
3431 while (isspace(*devname)) devname++;
3432
3433 /* Count total number of devices */
3434 for (cptr = listfrom, total = 0; cptr != NULL; cptr = cptr->next, total++);
3435 occupied = (u_int *)malloc(total * sizeof(u_int));
3436 objindex = 1;
3437
3438 for (cptr = listfrom, total = 0; cptr != NULL; cptr = cptr->next, total++) {
3439 occupied[total] = 0;
3440 if (cptr == clist) continue;
3441 cname = (cptr->devname == NULL) ? cptr->callobj->name : cptr->devname;
3442 while (isspace(*cname)) cname++;
3443 if (!strcmp(cname, devname)) {
3444 occupied[total] = cptr->devindex;
3445 if (cptr->devindex == objindex) objindex++;
3446 }
3447 }
3448
3449 b36idx = convert_to_b36(objindex);
3450 for (; objindex <= total; objindex++) {
3451 b36idx = convert_to_b36(objindex);
3452 for (i = 0; i < total; i++)
3453 if (occupied[i] == b36idx)
3454 break;
3455 if (i == total)
3456 break;
3457 }
3458 free(occupied);
3459
3460 clist->devindex = b36idx;
3461 return objindex;
3462 }
3463
3464 /*----------------------------------------------------------------------*/
3465 /* Generate and return a list of all info label prefixes in a design */
3466 /* This does not depend on the netlist being generated (i.e., does not */
3467 /* use CalllistPtr) */
3468 /*----------------------------------------------------------------------*/
3469
genprefixlist(objectptr cschem,slistptr * modelist)3470 void genprefixlist(objectptr cschem, slistptr *modelist)
3471 {
3472 int locpos;
3473 genericptr *pgen;
3474 labelptr plabel;
3475 objinstptr cinst;
3476 stringpart *strptr;
3477 slistptr modeptr;
3478
3479 for (pgen = cschem->plist; pgen < cschem->plist + cschem->parts; pgen++) {
3480 if (IS_LABEL(*pgen)) {
3481 plabel = TOLABEL(pgen);
3482 if (plabel->pin == INFO) {
3483
3484 strptr = findtextinstring(":", &locpos, plabel->string, cinst);
3485 if (locpos <= 0 || strptr == NULL)
3486 continue; /* null after netlist type designator, don't count */
3487
3488 for (modeptr = *modelist; modeptr; modeptr = modeptr->next)
3489 if (!strncmp(modeptr->alias, strptr->data.string, locpos))
3490 break;
3491
3492 if (modeptr == NULL) { /* Mode has not been enumerated */
3493 modeptr = (slistptr)malloc(sizeof(stringlist));
3494 modeptr->alias = (char *)malloc(locpos + 1);
3495 strncpy(modeptr->alias, strptr->data.string, locpos);
3496 modeptr->next = *modelist;
3497 *modelist = modeptr;
3498 }
3499 }
3500 }
3501 else if (IS_OBJINST(*pgen)) {
3502 cinst = TOOBJINST(pgen);
3503 genprefixlist(cinst->thisobject, modelist);
3504 if (cinst->thisobject->symschem != NULL)
3505 genprefixlist(cinst->thisobject->symschem, modelist);
3506 }
3507 }
3508 }
3509
3510 /*----------------------------------------------------------------------*/
3511 /* Create and return an ordered list of info labels for the schematic */
3512 /* page cschem. A linked label list is returned, combining info labels */
3513 /* from all schematic pages related to cschem (master and slave pages). */
3514 /* It is the responsibility of the calling routine to free the linked */
3515 /* list. */
3516 /*----------------------------------------------------------------------*/
3517
geninfolist(objectptr cschem,objinstptr cinst,char * mode)3518 LabellistPtr geninfolist(objectptr cschem, objinstptr cinst, char *mode)
3519 {
3520 int locpos, j;
3521 int vmax;
3522 genericptr *pgen;
3523 labelptr plabel;
3524 LabellistPtr newllist, listtop, srchlist;
3525 u_char *strt;
3526 stringpart *strptr;
3527
3528 listtop = NULL;
3529 vmax = 0;
3530
3531 for (pgen = cschem->plist; pgen < cschem->plist + cschem->parts; pgen++) {
3532 if (IS_LABEL(*pgen)) {
3533 plabel = TOLABEL(pgen);
3534 if ((plabel->pin == INFO) && !textncomp(plabel->string, mode, cinst)) {
3535
3536 if (mode[0] == '\0') {
3537 strptr = findtextinstring(":", &locpos, plabel->string, cinst);
3538 locpos--;
3539 }
3540 else
3541 strptr = findstringpart(strlen(mode), &locpos, plabel->string, cinst);
3542
3543 if (locpos < 0)
3544 continue; /* null after netlist type designator */
3545
3546 strt = strptr->data.string + locpos + 1;
3547
3548 if (*strt != ':') {
3549 if (sscanf(strt, "%d", &j) != 1) continue;
3550
3551 /* Consider only positive-valued numbers. Negative */
3552 /* indices are handled by "writenet" by merging the */
3553 /* minus character into the mode name. */
3554
3555 if (j < 0) continue;
3556 if (j >= vmax) vmax = j + 1;
3557 }
3558 else
3559 j = ++vmax;
3560
3561 newllist = (LabellistPtr)malloc(sizeof(Labellist));
3562 newllist->label = plabel;
3563 newllist->cschem = cschem;
3564 newllist->cinst = cinst;
3565 newllist->net.id = j; /* use this to find the ordering */
3566 newllist->subnets = 0; /* so free() doesn't screw up */
3567
3568 /* Order the linked list */
3569
3570 if ((listtop == NULL) || (listtop->net.id >= j)) {
3571 newllist->next = listtop;
3572 listtop = newllist;
3573 }
3574 else {
3575 for (srchlist = listtop; srchlist != NULL; srchlist = srchlist->next) {
3576 if ((srchlist->next != NULL) && (srchlist->next->net.id >= j)) {
3577 newllist->next = srchlist->next;
3578 srchlist->next = newllist;
3579 break;
3580 }
3581 else if (srchlist->next == NULL) {
3582 srchlist->next = newllist;
3583 newllist->next = NULL;
3584 }
3585 }
3586 }
3587 }
3588 }
3589 }
3590 return listtop;
3591 }
3592
3593 /*----------------------------------------------------------------------*/
3594 /* Trivial default pin name lookup. Parse an object's pin labels for */
3595 /* pin names. Return the text of the nth pin name, as counted by the */
3596 /* position in the object's parts list. If there are fewer than (n+1) */
3597 /* pin labels in the object, return NULL. Otherwise, return the pin */
3598 /* name in a malloc'd character string which the caller must free. */
3599 /*----------------------------------------------------------------------*/
3600
defaultpininfo(objinstptr cinst,int pidx)3601 char *defaultpininfo(objinstptr cinst, int pidx)
3602 {
3603 genericptr *pgen;
3604 labelptr plabel;
3605 objectptr cschem = cinst->thisobject;
3606 int count = 0;
3607 char *stxt;
3608
3609 for (pgen = cschem->plist; pgen < cschem->plist + cschem->parts; pgen++) {
3610 if (IS_LABEL(*pgen)) {
3611 plabel = TOLABEL(pgen);
3612 if (plabel->pin == LOCAL) {
3613 if (count == pidx) {
3614 stxt = textprint(plabel->string, cinst);
3615 return stxt;
3616 }
3617 count++;
3618 }
3619 }
3620 }
3621 return NULL;
3622 }
3623
3624 /*----------------------------------------------------------------------*/
3625 /* Parse an object's info labels for pin names. This is *not* a */
3626 /* netlist function. The nth pin name is returned, or NULL if not */
3627 /* found. Return value is a malloc'd string which the caller must */
3628 /* free. */
3629 /*----------------------------------------------------------------------*/
3630
parsepininfo(objinstptr cinst,char * mode,int pidx)3631 char *parsepininfo(objinstptr cinst, char *mode, int pidx)
3632 {
3633 genericptr *pgen;
3634 labelptr plabel;
3635 u_char *strt, *fnsh;
3636 int slen, i, locpos;
3637 objectptr cschem = cinst->thisobject;
3638 stringpart *strptr;
3639 char *sout;
3640 int count = 0;
3641
3642 for (pgen = cschem->plist; pgen < cschem->plist + cschem->parts; pgen++) {
3643 if (IS_LABEL(*pgen)) {
3644 plabel = TOLABEL(pgen);
3645 if (plabel->pin == INFO) {
3646 slen = stringlength(plabel->string, True, cinst);
3647 for (i = 1; i < slen; i++) {
3648 strptr = findstringpart(i, &locpos, plabel->string, cinst);
3649 if (locpos >= 0 && *(strptr->data.string + locpos) == ':') break;
3650 }
3651 /* Currently we are not checking against "mode". . .*/
3652 /* interpret all characters after the colon */
3653
3654 for (++i; i < slen; i++) {
3655 strptr = findstringpart(i, &locpos, plabel->string, cinst);
3656 if (locpos >= 0) {
3657
3658 /* Do for all text characters */
3659 strt = strptr->data.string + locpos;
3660 if (*strt == '%') {
3661 strt++;
3662 i++;
3663 if (*strt == 'p') { /* Pin found! */
3664 if (count == pidx) { /* Get pin text */
3665 strt++;
3666 fnsh = strt + 1;
3667 while (*fnsh != ' ' && *fnsh != '\0') fnsh++;
3668 sout = malloc(fnsh - strt + 1);
3669 strncpy(sout, strt, fnsh - strt);
3670 return sout;
3671 }
3672 count++;
3673 }
3674 }
3675 }
3676 }
3677 }
3678 }
3679 }
3680 return NULL;
3681 }
3682
3683 /*----------------------------------------------------------------------*/
3684 /* Look for information labels in the object parts list. Parse the */
3685 /* information labels and print results to specified output device. */
3686 /* (This is not very robust to errors---needs work!) */
3687 /* */
3688 /* If "autonumber" is true, then the parsed info label is used to */
3689 /* auto-number devices. */
3690 /*----------------------------------------------------------------------*/
3691
parseinfo(objectptr cfrom,objectptr cthis,CalllistPtr clist,char * prefix,char * mode,Boolean autonumber,Boolean no_output)3692 char *parseinfo(objectptr cfrom, objectptr cthis, CalllistPtr clist,
3693 char *prefix, char *mode, Boolean autonumber, Boolean no_output)
3694 {
3695 /* genericptr *pgen; (jdk) */
3696 stringpart *strptr, *ppin, *optr;
3697 char *snew, *sout = NULL, *pstring;
3698 u_char *strt, *fnsh;
3699 PortlistPtr ports;
3700 oparamptr ops, instops;
3701 int portid, locpos, i, k, subnet, slen; /* j, (jdk) */
3702 char *key = NULL;
3703 u_int newindex;
3704 Boolean is_flat = False, is_symbol = False, is_iso = False, is_quoted;
3705 Boolean do_update = True, include_once = False;
3706 char *locmode, *b36str, *sptr;
3707 LabellistPtr infolist, infoptr;
3708 FILE *finclude;
3709
3710 /* For flat netlists, prefix the mode with the word "flat". */
3711 /* As of 3/8/07, this includes the ".sim" format, which */
3712 /* should be called as mode "flatsim". */
3713
3714 locmode = mode;
3715 if (locmode && (!strncmp(mode, "flat", 4) || !strncmp(mode, "pseu", 4))) {
3716 locmode += 4;
3717 is_flat = True;
3718 }
3719
3720 /* mode == "" is passed when running resolve_devindex() and indicates */
3721 /* that the routine should be used to assign devindex to calls whose */
3722 /* devices have been manually assigned numbers. The preferred method */
3723 /* is to assign these values through the "index" parameter. Use of */
3724 /* parseinfo() ensures that objects that have no "index" parameter */
3725 /* but have valid info-labels for SPICE or PCB output will be */
3726 /* assigned the correct device index. */
3727 /* Sept. 3, 2006---The use of the prepended "@" character on */
3728 /* parameter names means that I can replace the cryptic "idx" with */
3729 /* the more obvious "index", which previously conflicted with the */
3730 /* PostScript command of the same name. Parameters "index" and "idx" */
3731 /* are treated interchangeably by the netlister. */
3732
3733 if (locmode[0] == '\0')
3734 do_update = False;
3735
3736 /* 1st pass: look for valid labels; see if more than one applies. */
3737 /* If so, order them correctly. Labels may not be numbered in */
3738 /* sequence, and may begin with zero. We allow a lot of flexibility */
3739 /* but the general expectation is that sequences start at 0 or 1 and */
3740 /* increase by consecutive integers. */
3741
3742 infolist = geninfolist(cthis, clist->callinst, locmode);
3743
3744 /* Now parse each label in sequence and output the result to */
3745 /* the return string. */
3746
3747 sout = (char *)malloc(1);
3748 *sout = '\0';
3749
3750 for (infoptr = infolist; infoptr != NULL; infoptr = infoptr->next) {
3751 objectptr pschem, cschem = infoptr->cschem;
3752 labelptr plabel = infoptr->label;
3753 objinstptr cinst = infoptr->cinst;
3754
3755 /* move to colon character */
3756 slen = stringlength(plabel->string, True, cinst);
3757 for (i = 1; i < slen; i++) {
3758 strptr = findstringpart(i, &locpos, plabel->string, cinst);
3759 if (locpos >= 0 && *(strptr->data.string + locpos) == ':') break;
3760 }
3761
3762 /* interpret all characters after the colon */
3763 for (++i; i < slen; i++) {
3764 strptr = findstringpart(i, &locpos, plabel->string, cinst);
3765 if (locpos >= 0) {
3766
3767 /* Do for all text characters */
3768 strt = strptr->data.string + locpos;
3769 if (*strt == '%') {
3770 is_quoted = False;
3771 strt++;
3772 i++;
3773 switch(*strt) {
3774 case '%':
3775 sout = (char *)realloc(sout, strlen(sout) + 2);
3776 strcat(sout, "%");
3777 break;
3778 case 'r':
3779 sout = (char *)realloc(sout, strlen(sout) + 2);
3780 strcat(sout, "\n");
3781 break;
3782 case 't':
3783 sout = (char *)realloc(sout, strlen(sout) + 2);
3784 strcat(sout, "\t");
3785 break;
3786 case 'i':
3787 if (clist->devname == NULL)
3788 clist->devname = strdup(sout);
3789 if (do_update) {
3790 if (is_flat)
3791 newindex = devflatindex(clist->devname);
3792 else
3793 newindex = devindex(cfrom, clist);
3794 b36str = d36a(newindex);
3795 sout = (char *)realloc(sout, strlen(sout) + strlen(b36str) + 1);
3796 sprintf(sout + strlen(sout), "%s", b36str);
3797 }
3798 break;
3799 case 'N':
3800 sout = (char *)realloc(sout, strlen(sout)
3801 + strlen(cschem->name) + 1);
3802 strcat(sout, cschem->name);
3803 break;
3804 case 'n':
3805 sptr = strstr(cschem->name, "::");
3806 if (sptr == NULL)
3807 sptr = cschem->name;
3808 else
3809 sptr += 2;
3810 sout = (char *)realloc(sout, strlen(sout)
3811 + strlen(sptr) + 1);
3812 strcat(sout, sptr);
3813 break;
3814 case 'x':
3815 sout = (char *)realloc(sout, strlen(sout) + 7);
3816 sprintf(sout + strlen(sout), "%d",
3817 cinst->position.x);
3818 break;
3819 case 'y':
3820 sout = (char *)realloc(sout, strlen(sout) + 7);
3821 sprintf(sout + strlen(sout), "%d",
3822 cinst->position.y);
3823 break;
3824 case 'F':
3825 if (no_output) break;
3826 include_once = True;
3827 /* drop through */
3828 case 'f':
3829 if (no_output) break;
3830
3831 /* Followed by a filename to include verbatim into */
3832 /* the output. Filename either has no spaces or */
3833 /* is in quotes. */
3834
3835 /* Use textprint to catch any embedded parameters */
3836
3837 snew = textprint(strptr, cinst);
3838 strt = snew;
3839 while (*strt != '\0') {
3840 if (*strt == '%') {
3841 if (include_once && *(strt + 1) == 'F')
3842 break;
3843 else if (!include_once && *(strt + 1) == 'f')
3844 break;
3845 }
3846 strt++;
3847 }
3848
3849 if (*strt == '\0') {
3850 /* No filename; print verbatim */
3851 free(snew);
3852 include_once = False;
3853 break;
3854 }
3855
3856 strt += 2;
3857 if (*strt == '"') strt++;
3858 if (*strt == '"' || *strt == '\0') break;
3859 fnsh = strt + 1;
3860 while (*fnsh != '\0' && !isspace(*fnsh) && *fnsh != '"')
3861 fnsh++;
3862 strncpy(_STR, strt, (int)(fnsh - strt));
3863 _STR[(int)(fnsh - strt)] = '\0';
3864 free(snew);
3865 i = slen;
3866
3867 /* Do variable and tilde expansion on the filename */
3868 xc_tilde_expand(_STR, 149);
3869 xc_variable_expand(_STR, 149);
3870
3871 /* Check if this file has been included already */
3872 if (include_once) {
3873 if (check_included(_STR)) {
3874 include_once = False;
3875 break;
3876 }
3877 append_included(_STR);
3878 }
3879
3880 /* Open the indicated file and dump to the output */
3881 finclude = fopen(_STR, "r");
3882 if (finclude != NULL) {
3883 char *sptr = sout;
3884 int nlen;
3885 int stlen = strlen(sout);
3886 while (fgets(_STR, 149, finclude)) {
3887 nlen = strlen(_STR);
3888 sout = (char *)realloc(sout, stlen + nlen + 1);
3889 sptr = sout + stlen;
3890 strcpy(sptr, _STR);
3891 stlen += nlen;
3892 }
3893 fclose(finclude);
3894 }
3895 else {
3896 Wprintf("No file named %s found", _STR);
3897 }
3898 include_once = False;
3899 break;
3900
3901 case 'p':
3902 /* Pin name either has no spaces or is in quotes */
3903 strt++;
3904 if (*strt == '"') {
3905 is_quoted = True;
3906 strt++;
3907 i++;
3908 }
3909 if (*strt == '"' || *strt == '\0') break;
3910 fnsh = strt + 1;
3911 while (*fnsh != '\0' && !isspace(*fnsh) && *fnsh != '"')
3912 fnsh++;
3913 strncpy(_STR2, strt, (int)(fnsh - strt));
3914 _STR2[(int)(fnsh - strt)] = '\0';
3915 i += (fnsh - strt);
3916 /* if (is_quoted || *fnsh == '\0') i++; */
3917 if (is_quoted) i++;
3918
3919 /* Find the port which corresponds to this pin name */
3920 /* in the called object (cschem). If this is a */
3921 /* linked symbol/schematic, then the port list will */
3922 /* be in the schematic view, not in the symbol view */
3923
3924 pschem = cschem;
3925 if (cschem->ports == NULL && cschem->symschem != NULL &&
3926 cschem->symschem->ports != NULL)
3927 pschem = cschem->symschem;
3928
3929 for (ports = pschem->ports; ports != NULL;
3930 ports = ports->next) {
3931
3932 subnet = getsubnet(ports->netid, pschem);
3933 ppin = nettopin(ports->netid, pschem, NULL);
3934 // NOTE: _STR is used inside textprintsubnet()
3935 pstring = textprintsubnet(ppin, NULL, subnet);
3936 if (!strcmp(pstring, _STR2)) {
3937 portid = ports->portid;
3938 free(pstring);
3939 break;
3940 }
3941 else
3942 free(pstring);
3943 }
3944 if (ports != NULL) {
3945
3946 /* Find the matching port in the calling object instance */
3947
3948 for (ports = clist->ports; ports != NULL;
3949 ports = ports->next)
3950 if (ports->portid == portid) break;
3951
3952 if (ports != NULL) {
3953
3954 ppin = nettopin(ports->netid, cfrom, prefix);
3955 subnet = getsubnet(ports->netid, cfrom);
3956 snew = textprintsubnet(ppin, cinst, subnet);
3957 sout = (char *)realloc(sout, strlen(sout) +
3958 strlen(snew) + 1);
3959 strcat(sout, snew);
3960 free(snew);
3961 break;
3962 }
3963 else {
3964 Fprintf(stderr, "Error: called non-existant port\n");
3965 }
3966 }
3967 else {
3968 Wprintf("No pin named %s in device %s", _STR, cschem->name);
3969 }
3970 break;
3971 case 'v':
3972 /* Parameter name either has no spaces or is in quotes */
3973 strt++;
3974 if (*strt == '"') {
3975 is_quoted = True;
3976 strt++;
3977 i++;
3978 }
3979 if (*strt == '"' || *strt == '\0') break;
3980 fnsh = strt + 1;
3981 while (*fnsh != '\0' && !isspace(*fnsh) && *fnsh != '"')
3982 fnsh++;
3983 strncpy(_STR, strt, (int)(fnsh - strt));
3984 _STR[(int)(fnsh - strt)] = '\0';
3985 i += (fnsh - strt);
3986 /* if (is_quoted || *fnsh == '\0') i++; */
3987 if (is_quoted) i++;
3988
3989 /* Compare this name against the parameter keys */
3990 ops = match_param(cschem, _STR);
3991
3992 /* For backwards compatibility, also try matching against */
3993 /* the parameter default value (method used before */
3994 /* implementing parameters as key:value pairs). */
3995
3996 if (ops == NULL) {
3997 for (ops = cschem->params; ops != NULL; ops = ops->next) {
3998 if (ops->type == XC_STRING) {
3999 if (!textcomp(ops->parameter.string, _STR, NULL)) {
4000 /* substitute the parameter or default */
4001 instops = find_param(cinst, ops->key);
4002 optr = instops->parameter.string;
4003 if (stringlength(optr, True, cinst) > 0) {
4004 snew = textprint(optr, cinst);
4005 sout = (char *)realloc(sout, strlen(sout)
4006 + strlen(snew) + 1);
4007 strcat(sout, snew);
4008 free(snew);
4009 }
4010 break;
4011 }
4012 }
4013 }
4014 }
4015
4016 if (ops == NULL) {
4017 Wprintf("No parameter named %s in device %s",
4018 _STR, cschem->name);
4019 }
4020 break;
4021
4022 default:
4023 /* Presence of ".ends" statement forces xcircuit */
4024 /* not to write ".end" at the end of the netlist. */
4025
4026 if (*strt == '.')
4027 if (!strncmp(strt + 1, "ends", 4))
4028 spice_end = FALSE;
4029
4030 sout = (char *)realloc(sout, strlen(sout) + 2);
4031 *(sout + strlen(sout) - 1) = *strt;
4032 *(sout + strlen(sout)) = '\0';
4033 }
4034 }
4035 else {
4036 if (key != NULL)
4037 if (clist->devname == NULL)
4038 clist->devname = strdup(sout);
4039
4040 /* Parameters with unresolved question marks are treated */
4041 /* like a "%i" string. */
4042
4043 if ((key != NULL) && !textncomp(strptr, "?", cinst)) {
4044 if (do_update || autonumber) {
4045 if (is_flat || (cfrom == NULL))
4046 newindex = devflatindex(clist->devname);
4047 else
4048 newindex = devindex(cfrom, clist);
4049 k = strlen(sout);
4050 b36str = d36a(newindex);
4051 sout = (char *)realloc(sout, strlen(sout) + strlen(b36str) + 1);
4052 sprintf(sout + k, "%s", b36str);
4053 }
4054
4055 /* When called with autonumber = TRUE, generate a parameter */
4056 /* instance, replacing the question mark with the new index */
4057 /* number. */
4058
4059 if (autonumber) {
4060 copyparams(cinst, cinst);
4061 ops = match_instance_param(cinst, key);
4062 optr = ops->parameter.string;
4063 if (!textncomp(optr, "?", cinst)) {
4064 optr->data.string = (char *)realloc(optr->data.string,
4065 strlen(sout + k) + 1);
4066 strcpy(optr->data.string, sout + k);
4067 }
4068 else Wprintf("Error while auto-numbering parameters");
4069 resolveparams(cinst);
4070 }
4071 }
4072 else {
4073 /* A "?" default parameter that has a (different) */
4074 /* instance value becomes the device number. */
4075 int stlen;
4076 if ((key != NULL) && (clist->devindex < 0)) {
4077 ops = match_param(cschem, key);
4078 if (ops->type == XC_STRING) {
4079 CalllistPtr plist;
4080 if (!textcomp(ops->parameter.string, "?", NULL)) {
4081 if (is_flat) {
4082 /* Ignore the value and generate one instead */
4083 newindex = devflatindex(clist->devname);
4084 b36str = d36a(newindex);
4085 k = strlen(sout);
4086 sout = (char *)realloc(sout, strlen(sout)
4087 + strlen(b36str) + 1);
4088 sprintf(sout + k, "%s", b36str);
4089 continue; /* go on to next stringpart */
4090 }
4091 else if (cfrom != NULL) {
4092
4093 /* Interpret parameter string as a component */
4094 /* number so we can check for duplicates. Assume */
4095 /* a base-36 number, which includes all alphabet */
4096 /* characters. This will disambiguate, e.g., "1" */
4097 /* and "1R", but flag case-sensitivity, e.g., */
4098 /* "1R" and "1r". Thanks to Carsten Thomas for */
4099 /* pointing out the problem with assuming */
4100 /* numerical component values. */
4101
4102 char *endptr;
4103 newindex = (u_int) strtol(strt, &endptr, 36);
4104 if (*endptr != '\0') {
4105 Fprintf(stderr, "Warning: Use of non-alphanumeric"
4106 " characters in component number \"%s\"\n", strt);
4107 }
4108 clist->devindex = newindex;
4109 for (plist = cfrom->calls; plist; plist = plist->next) {
4110 if ((plist == clist) ||
4111 (plist->callobj != clist->callobj)) continue;
4112
4113 /* Two parts have been named the same. Flag */
4114 /* it, but don't try to do anything about it. */
4115 /* 11/22/06---additionally check part numbers, */
4116 /* in case the symbol is a component sub-part. */
4117
4118 if (plist->devindex == newindex) {
4119 if (samepart(plist, clist)) {
4120 Fprintf(stderr, "Warning: duplicate part number"
4121 " %s%s and %s%s\n", sout, strt, sout, strt);
4122 break;
4123 }
4124 }
4125 }
4126 }
4127 }
4128 }
4129 }
4130 stlen = strlen(sout);
4131 sout = (char *)realloc(sout, stlen + 2);
4132
4133 /* By convention, greek "mu" becomes ASCII "u", NOT "m", */
4134 /* otherwise we get, e.g., millifarads instead of microfarads */
4135
4136 if ((is_symbol && (*strt == 'm')) || (is_iso && (*strt == 0265)))
4137 *(sout + stlen) = 'u';
4138 else
4139 *(sout + stlen) = *strt;
4140 *(sout + stlen + 1) = '\0';
4141 }
4142 }
4143 }
4144
4145 /* Some label controls are interpreted. Most are ignored */
4146 else {
4147 switch(strptr->type) {
4148 case PARAM_START:
4149 key = strptr->data.string;
4150 break;
4151 case PARAM_END:
4152 key = NULL;
4153 break;
4154 case RETURN:
4155 sout = (char *)realloc(sout, strlen(sout) + 2);
4156 strcat(sout, "\n");
4157 break;
4158 case TABFORWARD:
4159 sout = (char *)realloc(sout, strlen(sout) + 2);
4160 strcat(sout, "\t");
4161 break;
4162 case FONT_NAME:
4163 is_symbol = issymbolfont(strptr->data.font);
4164 is_iso = isisolatin1(strptr->data.font);
4165 break;
4166 }
4167 }
4168 }
4169
4170 /* Insert a newline between multiple valid info labels */
4171 if (infoptr->next != NULL) {
4172 sout = (char *)realloc(sout, strlen(sout) + 2);
4173 strcat(sout, "\n");
4174 }
4175 }
4176 freelabellist(&infoptr);
4177
4178 if (*sout == '\0') {
4179 free(sout);
4180 return NULL;
4181 }
4182 return sout;
4183 }
4184
4185 /*----------------------------------------------------------------------*/
4186 /* Write a low-level device */
4187 /*----------------------------------------------------------------------*/
4188
writedevice(FILE * fp,char * mode,objectptr cfrom,CalllistPtr clist,char * prefix)4189 int writedevice(FILE *fp, char *mode, objectptr cfrom, CalllistPtr clist,
4190 char *prefix)
4191 {
4192 char *sout;
4193 objectptr cthis;
4194
4195 if (clist == NULL) {
4196 if (fp != NULL) fprintf(fp, "error: null device\n");
4197 return -1;
4198 }
4199
4200 /* Devices are always symbols. If the call indicates a schematic */
4201 /* and a separate associated symbol, then we want the symbol. */
4202 /* If we're writing a flat netlist, then return -1 to indicate that */
4203 /* the symbol instance needs to be flattened. */
4204
4205 cthis = clist->callobj;
4206 if (clist->callobj->schemtype == PRIMARY || clist->callobj->schemtype ==
4207 SECONDARY)
4208 if (clist->callobj->symschem != NULL) {
4209 if (!strncmp(mode, "flat", 4))
4210 return -1;
4211 else
4212 cthis = clist->callobj->symschem;
4213 }
4214
4215 /* Look for information labels in the object parts list. */
4216
4217 if ((sout = parseinfo(cfrom, cthis, clist, prefix, mode, FALSE, FALSE)) != NULL) {
4218 if (fp != NULL) {
4219 fputs(sout, fp);
4220 fprintf(fp, "\n");
4221 }
4222 free(sout);
4223 return 0;
4224 }
4225
4226 /* Information labels do not necessarily exist. */
4227 return -1;
4228 }
4229
4230 #ifdef TCL_WRAPPER
4231
4232 /*----------------------------------------------------------------------*/
4233 /* Translate a hierarchical net name into an object and instance. */
4234 /* Return TRUE if found, FALSE if not. If found, object and instance */
4235 /* are returned in the argument pointers. */
4236 /*----------------------------------------------------------------------*/
4237
HierNameToObject(objinstptr cinst,char * hiername,pushlistptr * stack)4238 Boolean HierNameToObject(objinstptr cinst, char *hiername, pushlistptr *stack)
4239 {
4240 CalllistPtr calls;
4241 char *nexttoken, *hptr, *pptr;
4242 objectptr cschem, curobj, newobj;
4243 objinstptr newinst;
4244 int devindex, devlen;
4245 /* pushlistptr stackentry; (jdk) */
4246
4247 cschem = cinst->thisobject;
4248 curobj = (cschem->schemtype == SECONDARY) ? cschem->symschem : cschem;
4249
4250 if (curobj->calls == NULL) {
4251 if ((updatenets(cinst, FALSE) <= 0) || (curobj->calls == NULL)) {
4252 Wprintf("Error in generating netlists!");
4253 return False;
4254 }
4255 }
4256
4257 hptr = hiername;
4258 while (hptr != NULL) {
4259 nexttoken = strchr(hptr, '/');
4260 if (nexttoken != NULL) *nexttoken = '\0';
4261 pptr = strrchr(hptr, '(');
4262 if (pptr != NULL) {
4263 if (sscanf(pptr + 1, "%d", &devindex) == 0) {
4264 pptr = NULL;
4265 devindex = 0;
4266 }
4267 else
4268 *pptr = '\0';
4269 }
4270 else devindex = -1;
4271
4272 /* check to make sure that the netlist and device indices have been generated */
4273 for (calls = curobj->calls; calls != NULL; calls = calls->next) {
4274 if (calls->devindex == -1) {
4275 cleartraversed(curobj);
4276 resolve_indices(curobj, FALSE);
4277 }
4278 }
4279
4280 /* hptr is now the object name, and devindex is the instance's call index */
4281 /* find the object corresponding to the name. */
4282
4283 newobj = NameToObject(hptr, &newinst, TRUE);
4284
4285 if (newobj == NULL) {
4286
4287 /* Try device names (for netlist output) instead of object names */
4288
4289 for (calls = curobj->calls; calls != NULL; calls = calls->next) {
4290 if (calls->devname != NULL) {
4291 devlen = strlen(calls->devname);
4292 if (!strncmp(hptr, calls->devname, devlen)) {
4293 if (devindex == -1) {
4294 if (sscanf(hptr + devlen, "%d", &devindex) == 0)
4295 devindex = 0;
4296 }
4297 if (calls->devindex == devindex) {
4298 newobj = calls->callinst->thisobject;
4299 break;
4300 }
4301 }
4302 }
4303 }
4304 }
4305 else {
4306 for (calls = curobj->calls; calls != NULL; calls = calls->next)
4307 if ((calls->callobj == newobj) && (calls->devindex == devindex))
4308 break;
4309 }
4310 if (newobj == NULL || calls == NULL) {
4311 Fprintf(stderr, "object %s in hierarchy not found in schematic.\n", hptr);
4312 free_stack(stack);
4313 return FALSE;
4314 }
4315
4316 /* Diagnostic information */
4317 /* fprintf(stderr, "object %s(%d) = %s\n", newobj->name, devindex, hptr); */
4318
4319 curobj = calls->callobj;
4320 push_stack(stack, calls->callinst, NULL);
4321
4322 if (pptr != NULL) *pptr = '(';
4323 if (nexttoken == NULL) break;
4324 *nexttoken = '/';
4325 hptr = nexttoken + 1;
4326 }
4327 return TRUE;
4328 }
4329
4330 #endif
4331
4332 /*----------------------------------------------------------------------*/
4333 /* Save netlist into a flattened sim or spice file */
4334 /*----------------------------------------------------------------------*/
4335
writeflat(objectptr cschem,CalllistPtr cfrom,char * prefix,FILE * fp,char * mode)4336 void writeflat(objectptr cschem, CalllistPtr cfrom, char *prefix, FILE *fp, char *mode)
4337 {
4338 CalllistPtr calls = cschem->calls;
4339 char *newprefix = (char *)malloc(sizeof(char));
4340
4341 /* reset device indexes */
4342
4343 for (calls = cschem->calls; calls != NULL; calls = calls->next)
4344 calls->devindex = -1;
4345
4346 /* The naming convention for nodes below the top level uses a */
4347 /* slash-separated list of hierarchical names. Thus the call to */
4348 /* the hierarchical resolve_indices(). Indices used by bottom-most */
4349 /* symbols (e.g., flattened spice) will be generated by a separate */
4350 /* routine devflatindex(), unrelated to what is generated here, */
4351 /* which only affects the hierarchical naming of nodes. */
4352
4353 resolve_indices(cschem, FALSE);
4354
4355 /* write all the subcircuits */
4356
4357 for (calls = cschem->calls; calls != NULL; calls = calls->next) {
4358
4359 makelocalpins(cschem, calls, prefix);
4360 if (writedevice(fp, mode, cschem, calls, prefix) < 0) {
4361 sprintf(_STR, "%s_%u", calls->callobj->name,
4362 devindex(cschem, calls));
4363 newprefix = (char *)realloc(newprefix, sizeof(char) * (strlen(prefix)
4364 + strlen(_STR) + 2));
4365 sprintf(newprefix, "%s%s/", prefix, _STR);
4366 /* Allow cross-referenced parameters between symbols and schematics */
4367 /* by substituting into a schematic from its corresponding symbol */
4368 opsubstitute(calls->callobj, calls->callinst);
4369 /* psubstitute(calls->callinst); */
4370 writeflat(calls->callobj, calls, newprefix, fp, mode);
4371 }
4372 clearlocalpins(calls->callobj);
4373 }
4374 free(newprefix);
4375 }
4376
4377 /*----------------------------------------------------------------------*/
4378 /* Topmost call to write a flattened netlist */
4379 /*----------------------------------------------------------------------*/
4380
topflat(objectptr cschem,objinstptr thisinst,CalllistPtr cfrom,char * prefix,FILE * fp,char * mode)4381 void topflat(objectptr cschem, objinstptr thisinst, CalllistPtr cfrom,
4382 char *prefix, FILE *fp, char *mode)
4383 {
4384 char *locmode, *stsave = NULL;
4385 int modlen;
4386 Calllist loccalls;
4387
4388 /* Set up local call list for parsing info labels in the top-level schematic */
4389 loccalls.cschem = NULL;
4390 loccalls.callobj = cschem;
4391 loccalls.callinst = thisinst;
4392 loccalls.devindex = -1;
4393 loccalls.ports = NULL;
4394 loccalls.next = NULL;
4395
4396 modlen = strlen(mode);
4397 locmode = malloc(2 + modlen);
4398 strcpy(locmode, mode);
4399 locmode[modlen + 1] = '\0';
4400
4401 /* "<mode>@" lines go in front */
4402
4403 locmode[modlen] = '@';
4404 if (fp != NULL) stsave = parseinfo(NULL, cschem, &loccalls, NULL, locmode, FALSE,
4405 FALSE);
4406 if (stsave != NULL) {
4407 fputs(stsave, fp);
4408 fprintf(fp, "\n");
4409 free(stsave);
4410 stsave = NULL;
4411 }
4412
4413 writeflat(cschem, cfrom, prefix, fp, mode);
4414 freeflatindex();
4415
4416 /* Check for negative-numbered info labels, indicated output that */
4417 /* should be processed after everything else. */
4418
4419 locmode[modlen] = '-';
4420 stsave = parseinfo(NULL, cschem, &loccalls, NULL, locmode, FALSE, FALSE);
4421 if (stsave != NULL) {
4422 fputs(stsave, fp);
4423 fprintf(fp, "\n");
4424 free(stsave);
4425 stsave = NULL;
4426 }
4427 free(locmode);
4428 }
4429
4430 /*----------------------------------------------------------------------*/
4431 /* Write out the list of global nets and their pin names */
4432 /*----------------------------------------------------------------------*/
4433
writeglobals(objectptr cschem,FILE * fp)4434 void writeglobals(objectptr cschem, FILE *fp)
4435 {
4436 LabellistPtr llist;
4437 labelptr gpin;
4438 char *snew;
4439
4440 if (fp == NULL) return;
4441
4442 for (llist = global_labels; llist != NULL; llist = llist->next) {
4443 gpin = llist->label;
4444 snew = textprint(gpin->string, NULL);
4445 fprintf(fp, ".GLOBAL %s\n", snew); /* hspice format */
4446 /* fprintf(fp, "%s = %d\n", snew, -gptr->netid); */ /* generic format */
4447 free(snew);
4448 }
4449 fprintf(fp, "\n");
4450 }
4451
4452 /*----------------------------------------------------------------------*/
4453 /* Write a SPICE subcircuit entry. */
4454 /*----------------------------------------------------------------------*/
4455
writesubcircuit(FILE * fp,objectptr cschem)4456 void writesubcircuit(FILE *fp, objectptr cschem)
4457 {
4458 PortlistPtr ports;
4459 char *pstring;
4460 stringpart *ppin;
4461 int netid, length, plen, subnet; /* portid, (jdk) */
4462
4463 /* Objects which have no ports are not written */
4464 if ((cschem->ports != NULL) && (fp != NULL)) {
4465
4466 fprintf(fp, ".subckt %s", cschem->name);
4467 length = 9 + strlen(cschem->name);
4468
4469 /* List of parameters in subcircuit. */
4470 /* Each parameter connects to a net which may have multiple */
4471 /* names, so find the net associated with the parameter */
4472 /* and convert it back into a pin name in the usual manner */
4473 /* using nettopin(). */
4474
4475 for (ports = cschem->ports; ports != NULL;
4476 ports = ports->next) {
4477 netid = ports->netid;
4478 subnet = getsubnet(netid, cschem);
4479 ppin = nettopin(netid, cschem, NULL);
4480 pstring = textprintsubnet(ppin, NULL, subnet);
4481 plen = strlen(pstring) + 1;
4482 if (length + plen > 78) {
4483 fprintf(fp, "\n+ "); /* SPICE line break & continuation */
4484 length = 0;
4485 }
4486 else length += plen;
4487 fprintf(fp, " %s", pstring);
4488 free(pstring);
4489 }
4490 fprintf(fp, "\n");
4491 }
4492 }
4493
4494 /*----------------------------------------------------------------------*/
4495 /* Resolve device names (fill calllist structure member "devname") */
4496 /*----------------------------------------------------------------------*/
4497
resolve_devnames(objectptr cschem)4498 void resolve_devnames(objectptr cschem)
4499 {
4500 CalllistPtr calls;
4501 char *stmp;
4502 oparamptr ops;
4503
4504 for (calls = cschem->calls; calls != NULL; calls = calls->next) {
4505 if (calls->callobj->traversed == False) {
4506 calls->callobj->traversed = True;
4507 resolve_devnames(calls->callobj);
4508 }
4509
4510 if (calls->devname == NULL) {
4511
4512 /* Check for "class" parameter. Note that although this */
4513 /* parameter may take a different instance value, the */
4514 /* device class is determined by the default value. */
4515
4516 ops = find_param(calls->callinst, "class");
4517 if (ops && (ops->type == XC_STRING))
4518 calls->devname = textprint(ops->parameter.string, NULL);
4519 else {
4520 /* The slow way---parse info labels for any device information */
4521 if ((stmp = parseinfo(cschem, calls->callinst->thisobject, calls,
4522 NULL, "", FALSE, TRUE)) != NULL)
4523 free(stmp);
4524 }
4525 }
4526 }
4527 }
4528
4529 /*----------------------------------------------------------------------*/
4530 /* Resolve (hierarchical) component numbering. If "autonumber" is TRUE */
4531 /* then this routine does auto-numbering. Otherwise, it auto-generates */
4532 /* device indices internally (in the "calllist" structure) but does not */
4533 /* copy them into parameter space. */
4534 /* */
4535 /* Note: resolve_devindex() only operates on a single object. Use */
4536 /* resolve_indices() to operate on the entire hierarchy. */
4537 /*----------------------------------------------------------------------*/
4538
resolve_devindex(objectptr cschem,Boolean autonumber)4539 void resolve_devindex(objectptr cschem, Boolean autonumber)
4540 {
4541 CalllistPtr calls;
4542 char *stmp, *endptr;
4543 char *idxtype[] = {"index", "idx", NULL};
4544 oparamptr ops, ips;
4545 objinstptr cinst;
4546 stringpart *optr;
4547 int newindex, j;
4548
4549 for (calls = cschem->calls; calls != NULL; calls = calls->next) {
4550
4551 /* Check if there is an "index" parameter set to "?" in */
4552 /* the object with no existing instance value. */
4553
4554 for (j = 0; idxtype[j] != NULL; j++)
4555 if ((ops = match_param(calls->callinst->thisobject, idxtype[j])) != NULL)
4556 break;
4557
4558 if (ops && (ops->type == XC_STRING)) {
4559 if (!textcomp(ops->parameter.string, "?", NULL)) {
4560 cinst = calls->callinst;
4561 ips = match_instance_param(cinst, idxtype[j]);
4562 if ((autonumber == TRUE) && (ips == NULL)) {
4563 copyparams(cinst, cinst);
4564 ops = match_instance_param(cinst, idxtype[j]);
4565 optr = ops->parameter.string;
4566 newindex = devindex(cschem, calls);
4567 stmp = d36a(newindex);
4568 optr->data.string = (char *)realloc(optr->data.string, strlen(stmp) + 1);
4569 sprintf(optr->data.string, "%s", stmp);
4570 }
4571 else if (calls->devindex < 0) {
4572 if (ips != NULL) {
4573 optr = ips->parameter.string;
4574 if (optr->type != TEXT_STRING) {
4575 /* Not a simple string. Dump the text. */
4576 char *snew = NULL;
4577 snew = textprint(optr, NULL),
4578 newindex = (u_int) strtol(snew, &endptr, 36);
4579 free(snew);
4580 }
4581 else {
4582 newindex = (u_int) strtol(optr->data.string, &endptr, 36);
4583 }
4584 if (*endptr != '\0') {
4585 /* It is possible to get an instance value that is */
4586 /* the same as the default, although this normally */
4587 /* doesn't happen. If it does, quietly remove the */
4588 /* unneeded instance value. */
4589
4590 if (!stringcomp(ops->parameter.string, ips->parameter.string))
4591 resolveparams(cinst);
4592 else
4593 Fprintf(stderr, "Warning: Use of non-alphanumeric"
4594 " characters in component \"%s%s\" (instance"
4595 " of %s)\n", (calls->devname) ? calls->devname
4596 : calls->callobj->name, optr->data.string,
4597 calls->callobj->name);
4598 }
4599 else
4600 calls->devindex = newindex;
4601 }
4602 else /* if (autonumber) */
4603 devindex(cschem, calls);
4604 }
4605 }
4606 }
4607 else {
4608 /* The slow way---parse info labels for any index information */
4609 if ((stmp = parseinfo(cschem, calls->callinst->thisobject, calls,
4610 NULL, "", autonumber, TRUE)) != NULL)
4611 free(stmp);
4612 }
4613 }
4614 }
4615
4616 /*----------------------------------------------------------------------*/
4617 /* Recursive call to resolve_devindex() through the circuit hierarchy */
4618 /*----------------------------------------------------------------------*/
4619
resolve_indices(objectptr cschem,Boolean autonumber)4620 void resolve_indices(objectptr cschem, Boolean autonumber) {
4621 CalllistPtr calls;
4622
4623 for (calls = cschem->calls; calls != NULL; calls = calls->next) {
4624 if (calls->callobj->traversed == False) {
4625 calls->callobj->traversed = True;
4626 resolve_indices(calls->callobj, autonumber);
4627 }
4628 }
4629 resolve_devindex(cschem, autonumber);
4630 }
4631
4632 /*----------------------------------------------------------------------*/
4633 /* Recursive call to clear all generated device numbers. */
4634 /*----------------------------------------------------------------------*/
4635
clear_indices(objectptr cschem)4636 void clear_indices(objectptr cschem) {
4637 CalllistPtr calls;
4638
4639 for (calls = cschem->calls; calls != NULL; calls = calls->next) {
4640 if (calls->callobj->traversed == False) {
4641 calls->callobj->traversed = True;
4642 clear_indices(calls->callobj);
4643 }
4644 calls->devindex = -1;
4645 }
4646 }
4647
4648 /*----------------------------------------------------------------------*/
4649 /* Recursive call to clear all device indexes (parameter "index") */
4650 /*----------------------------------------------------------------------*/
4651
unnumber(objectptr cschem)4652 void unnumber(objectptr cschem) {
4653 CalllistPtr calls;
4654 oparamptr ops, ips;
4655 char *idxtype[] = {"index", "idx", NULL};
4656 int j;
4657
4658 for (calls = cschem->calls; calls != NULL; calls = calls->next) {
4659 if (calls->callobj->traversed == False) {
4660 calls->callobj->traversed = True;
4661 unnumber(calls->callobj);
4662 }
4663
4664 for (j = 0; idxtype[j] != NULL; j++)
4665 if ((ops = match_param(calls->callobj, idxtype[j])) != NULL) break;
4666
4667 if (ops && (ops->type == XC_STRING)) {
4668 if (!textcomp(ops->parameter.string, "?", NULL)) {
4669 ips = match_instance_param(calls->callinst, idxtype[j]);
4670 if (ips != NULL)
4671 free_instance_param(calls->callinst, ips);
4672 }
4673 }
4674 }
4675 }
4676
4677 /*----------------------------------------------------------------------*/
4678 /* Save netlist into a hierarchical file */
4679 /*----------------------------------------------------------------------*/
4680
writehierarchy(objectptr cschem,objinstptr thisinst,CalllistPtr cfrom,FILE * fp,char * mode)4681 void writehierarchy(objectptr cschem, objinstptr thisinst, CalllistPtr cfrom,
4682 FILE *fp, char *mode)
4683 {
4684 CalllistPtr calls = cschem->calls;
4685 PortlistPtr ports, plist;
4686 int pnet, portid, length, plen, subnet, modlen; /* netid, (jdk) */
4687 char *locmode = mode, *stsave = NULL, *pstring;
4688 stringpart *ppin;
4689 Calllist loccalls;
4690
4691 if (cschem->traversed == True) return;
4692
4693 /* Set up local call list for parsing info labels in this schematic */
4694 loccalls.cschem = NULL;
4695 loccalls.callobj = cschem;
4696 loccalls.callinst = thisinst;
4697 loccalls.devindex = -1;
4698 loccalls.ports = NULL;
4699 loccalls.next = NULL;
4700
4701 modlen = strlen(mode);
4702 locmode = malloc(2 + modlen);
4703 strcpy(locmode, mode);
4704 locmode[modlen + 1] = '\0';
4705
4706 /* "<mode>@" lines go before any subcircuit calls */
4707
4708 locmode[modlen] = '@';
4709 if (fp != NULL) stsave = parseinfo(NULL, cschem, &loccalls, NULL, locmode, FALSE,
4710 FALSE);
4711 if (stsave != NULL) {
4712 fputs(stsave, fp);
4713 fprintf(fp, "\n");
4714 free(stsave);
4715 stsave = NULL;
4716 }
4717
4718 /* Subcircuits which make no calls or have no devices do not get written */
4719
4720 if (calls != NULL) {
4721
4722 /* Make sure that all the subcircuits have been written first */
4723
4724 for (; calls != NULL; calls = calls->next) {
4725 if (calls->callobj->traversed == False) {
4726 psubstitute(calls->callinst);
4727 writehierarchy(calls->callobj, calls->callinst, calls, fp, mode);
4728 calls->callobj->traversed = True;
4729 }
4730 }
4731 if (cschem->schemtype == FUNDAMENTAL) {
4732 free(locmode);
4733 return;
4734 }
4735 }
4736
4737 /* Info-labels on a schematic (if any) get printed out first */
4738
4739 if ((fp != NULL) && (cschem->calls != NULL)) {
4740 stsave = parseinfo(NULL, cschem, &loccalls, NULL, mode, FALSE, FALSE);
4741 if (stsave != NULL) {
4742
4743 /* Check stsave for embedded SPICE subcircuit syntax */
4744
4745 if (!strcmp(mode, "spice"))
4746 if (strstr(stsave, ".subckt ") == NULL)
4747 writesubcircuit(fp, cschem);
4748
4749 fputs(stsave, fp);
4750 fprintf(fp, "\n");
4751 free(stsave);
4752 stsave = NULL;
4753 }
4754 else if (cschem->calls != NULL)
4755 /* top-level schematics will be ignored because they have no ports */
4756 writesubcircuit(fp, cschem);
4757 }
4758
4759 /* Resolve all fixed part assignments (devindex) */
4760 resolve_devindex(cschem, FALSE);
4761
4762 /* If the output file is NULL, then we're done */
4763
4764 if (fp == NULL) {
4765 free(locmode);
4766 return;
4767 }
4768
4769 for (calls = cschem->calls; calls != NULL; calls = calls->next) {
4770
4771 /* writedevice() is just another call to parseinfo() */
4772 if (writedevice(fp, mode, cschem, calls, NULL) < 0) {
4773
4774 /* If the device did not produce any output in writedevice(), but */
4775 /* does not make any calls itself, then it is implicitly a TRIVIAL */
4776 /* symbol. */
4777
4778 if ((calls->callobj->schemtype == TRIVIAL) || (calls->callobj->calls == NULL))
4779 continue;
4780
4781 /* No syntax indicating how to write this call. Use SPICE "X" */
4782 /* format and arrange the parameters according to the structure. */
4783
4784 calls->devname = strdup(spice_devname);
4785 fprintf(fp, "X%s", d36a(devindex(cschem, calls)));
4786 stsave = calls->callobj->name;
4787 length = 6;
4788
4789 /* The object's definition lists calls in the order of the object's */
4790 /* port list. Therefore, use this order to find the port list. */
4791 /* This will also deal with the fact that not every port has to be */
4792 /* called by the instance (ports may be left disconnected). */
4793
4794 for (ports = calls->callobj->ports; ports != NULL;
4795 ports = ports->next) {
4796 portid = ports->portid;
4797 for (plist = calls->ports; plist != NULL; plist = plist->next)
4798 if (plist->portid == ports->portid)
4799 break;
4800
4801 pnet = (plist == NULL) ? netmax(cschem) + 1 : plist->netid;
4802 subnet = getsubnet(pnet, cschem);
4803 ppin = nettopin(pnet, cschem, NULL);
4804 pstring = textprintsubnet(ppin, NULL, subnet);
4805 plen = strlen(pstring) + 1;
4806 if (length + plen > 78) {
4807 fprintf(fp, "\n+ "); /* SPICE line continuation */
4808 length = 0;
4809 }
4810 else length += plen;
4811 fprintf(fp, " %s", pstring);
4812 free(pstring);
4813 }
4814 plen = 1 + strlen(stsave);
4815 if (length + plen > 78) fprintf(fp, "\n+ "); /* SPICE line cont. */
4816 fprintf(fp, " %s\n", stsave);
4817 }
4818 }
4819
4820 /* Check for negative-numbered info labels, indicated output that */
4821 /* should be processed after everything else. If we're processing */
4822 /* SPICE output but the schematic does not provide a ".ends" */
4823 /* statement, then add it to the end of the deck. */
4824
4825 if (cschem->calls != NULL) {
4826 locmode[modlen] = '-';
4827 stsave = parseinfo(NULL, cschem, &loccalls, NULL, locmode, FALSE, FALSE);
4828 if (stsave != NULL) {
4829 fputs(stsave, fp);
4830 fprintf(fp, "\n");
4831 if (cfrom != NULL)
4832 if (!strcmp(mode, "spice"))
4833 if (strstr(stsave, ".ends") == NULL)
4834 fprintf(fp, ".ends\n");
4835 free(stsave);
4836 }
4837 else if (cfrom != NULL)
4838 fprintf(fp, ".ends\n");
4839
4840 fprintf(fp, "\n");
4841 }
4842
4843 free(locmode);
4844 }
4845
4846 /*----------------------------------------------------------------------*/
4847 /* Create generic netlist in the Tcl interpreter variable space */
4848 /*----------------------------------------------------------------------*/
4849
4850 #ifdef TCL_WRAPPER
4851
tclparseinfo(objectptr cschem)4852 static Tcl_Obj *tclparseinfo(objectptr cschem)
4853 {
4854 genericptr *pgen;
4855 labelptr plabel;
4856
4857 Tcl_Obj *rlist = Tcl_NewListObj(0, NULL);
4858
4859 for (pgen = cschem->plist; pgen < cschem->plist + cschem->parts; pgen++) {
4860 if (IS_LABEL(*pgen)) {
4861 plabel = TOLABEL(pgen);
4862 if (plabel->pin == INFO) {
4863 Tcl_ListObjAppendElement(xcinterp, rlist,
4864 TclGetStringParts(plabel->string));
4865 }
4866 }
4867 }
4868 return rlist;
4869 }
4870
4871 /*----------------------------------------------------------------------*/
4872 /* Write global variables to Tcl list (in key-value pairs which can be */
4873 /* turned into an array variable using the "array set" command) */
4874 /* */
4875 /* Note: argument "cinst" is unused, but it really should be. We */
4876 /* should not assume that different schematics have the same list of */
4877 /* globals! */
4878 /*----------------------------------------------------------------------*/
4879
tclglobals(objinstptr cinst)4880 Tcl_Obj *tclglobals(objinstptr cinst)
4881 {
4882 LabellistPtr llist;
4883 labelptr gpin;
4884 Tcl_Obj *gdict; /*, *netnum; (jdk) */
4885 buslist *sbus;
4886 int netid, lbus;
4887
4888 gdict = Tcl_NewListObj(0, NULL);
4889 for (llist = global_labels; llist != NULL; llist = llist->next) {
4890 gpin = llist->label;
4891 Tcl_ListObjAppendElement(xcinterp, gdict, TclGetStringParts(gpin->string));
4892 for (lbus = 0;;) {
4893 if (llist->subnets == 0) {
4894 netid = llist->net.id;
4895 }
4896 else {
4897 sbus = llist->net.list + lbus;
4898 netid = sbus->netid;
4899 }
4900 Tcl_ListObjAppendElement(xcinterp, gdict, Tcl_NewIntObj(netid));
4901 if (++lbus >= llist->subnets) break;
4902 }
4903 }
4904 return gdict;
4905 }
4906
4907 /*----------------------------------------------------------------------*/
4908 /* Write a generic hierarchical netlist into Tcl list "subcircuits" */
4909 /*----------------------------------------------------------------------*/
4910
tclhierarchy(objectptr cschem,objinstptr cinst,CalllistPtr cfrom,Tcl_Obj * cktlist)4911 void tclhierarchy(objectptr cschem, objinstptr cinst, CalllistPtr cfrom, Tcl_Obj *cktlist)
4912 {
4913 CalllistPtr calls = cschem->calls;
4914 PortlistPtr ports, plist;
4915 int netid, pnet, portid; /* , length, plen, i; (jdk) */
4916 Tcl_Obj *tclports, *tclcalls, *tclnewcall, *tclnets, *netnum;
4917 Tcl_Obj *tcldevs, *tclparams, *subckt, *newdev, *tcllabel, *portnum;
4918 oparamptr paramlist; /* , locparam; (jdk) */
4919 char *netsdone;
4920
4921 /* Trivial objects are by definition those that are supposed */
4922 /* to be resolved by the netlist generation prior to output */
4923 /* and ignored by the output generator. */
4924
4925 if (cschem->schemtype == TRIVIAL) return;
4926
4927 /* Make sure that all the subcircuits have been written first */
4928
4929 for (; calls != NULL; calls = calls->next) {
4930 if (calls->callobj->traversed == False) {
4931 tclhierarchy(calls->callobj, calls->callinst, calls, cktlist);
4932 calls->callobj->traversed = True;
4933 }
4934 }
4935
4936 /* Write own subcircuit netlist */
4937 subckt = Tcl_NewListObj(0, NULL);
4938
4939 /* Prepare the list of network cross-references */
4940 tclnets = Tcl_NewListObj(0, NULL);
4941
4942 /* Make list of the nets which have been written so we don't redundantly */
4943 /* list any entries (use of calloc() initializes all entries to FALSE). */
4944 /* (calloc() replaced by malloc() and bzero() because Tcl doesn't */
4945 /* have a calloc() function (2/6/04)) */
4946
4947 netsdone = (char *)malloc(2 + netmax(cschem));
4948 #ifdef _MSC_VER
4949 memset((void *)netsdone, 0, 2 + netmax(cschem));
4950 #else
4951 bzero((void *)netsdone, 2 + netmax(cschem));
4952 #endif
4953
4954 /* Write the name (key value pair) */
4955 Tcl_ListObjAppendElement(xcinterp, subckt, Tcl_NewStringObj("name", 4));
4956 Tcl_ListObjAppendElement(xcinterp, subckt, Tcl_NewStringObj(cschem->name,
4957 strlen(cschem->name)));
4958
4959 /* Write the handle of the object instance (key value pair) */
4960 Tcl_ListObjAppendElement(xcinterp, subckt, Tcl_NewStringObj("handle", 6));
4961 Tcl_ListObjAppendElement(xcinterp, subckt, Tcl_NewHandleObj(cinst));
4962
4963 /* Write the list of ports */
4964 if ((ports = cschem->ports) != NULL) {
4965
4966 /* List of ports in subcircuit. */
4967 /* Each parameter connects to a net which may have multiple */
4968 /* names, so find the net associated with the parameter */
4969 /* and convert it back into a pin name in the usual manner */
4970 /* using nettopin(). */
4971
4972 tclports = Tcl_NewListObj(0, NULL);
4973 for (; ports != NULL; ports = ports->next) {
4974 netid = ports->netid;
4975 portid = ports->portid;
4976 netnum = Tcl_NewIntObj(netid);
4977 portnum = Tcl_NewIntObj(portid);
4978 Tcl_ListObjAppendElement(xcinterp, tclports, portnum);
4979 Tcl_ListObjAppendElement(xcinterp, tclports, netnum);
4980 if ((netid >= 0) && (netsdone[netid] == (char)0))
4981 {
4982 Tcl_ListObjAppendElement(xcinterp, tclnets, netnum);
4983 Tcl_ListObjAppendElement(xcinterp, tclnets,
4984 TclGetStringParts(nettopin(netid, cschem, NULL)));
4985 /* record this net as having been added to the list */
4986 netsdone[netid] = (char)1;
4987 }
4988 }
4989 Tcl_ListObjAppendElement(xcinterp, subckt, Tcl_NewStringObj("ports", 5));
4990 Tcl_ListObjAppendElement(xcinterp, subckt, tclports);
4991 }
4992
4993 /* Write the list of parameter defaults (key:value pairs) */
4994
4995 if (cschem->params != NULL) {
4996 tclparams = Tcl_NewListObj(0, NULL);
4997
4998 for (paramlist = cschem->params; paramlist != NULL; paramlist = paramlist->next) {
4999 Tcl_ListObjAppendElement(xcinterp, tclparams,
5000 Tcl_NewStringObj(paramlist->key, strlen(paramlist->key)));
5001 switch(paramlist->type) {
5002 case XC_INT:
5003 Tcl_ListObjAppendElement(xcinterp, tclparams,
5004 Tcl_NewIntObj((int)paramlist->parameter.ivalue));
5005 break;
5006 case XC_FLOAT:
5007 Tcl_ListObjAppendElement(xcinterp, tclparams,
5008 Tcl_NewDoubleObj((double)paramlist->parameter.fvalue));
5009 break;
5010 case XC_STRING:
5011 tcllabel = TclGetStringParts(paramlist->parameter.string);
5012 /* Should get rid of last entry, "End Parameter"; not needed */
5013 Tcl_ListObjAppendElement(xcinterp, tclparams, tcllabel);
5014 break;
5015 case XC_EXPR:
5016 Tcl_ListObjAppendElement(xcinterp, tclparams,
5017 evaluate_raw(cschem, paramlist, cinst, NULL));
5018 break;
5019 }
5020 }
5021 Tcl_ListObjAppendElement(xcinterp, subckt, Tcl_NewStringObj("parameters", 10));
5022 Tcl_ListObjAppendElement(xcinterp, subckt, tclparams);
5023 }
5024
5025 /* Write the list of calls to subcircuits */
5026
5027 if ((calls = cschem->calls) != NULL) {
5028 tclcalls = Tcl_NewListObj(0, NULL);
5029 for (; calls != NULL; calls = calls->next) {
5030
5031 /* Don't write calls to non-functional subcircuits. */
5032 if (calls->callobj->schemtype == TRIVIAL) continue;
5033
5034 tclnewcall = Tcl_NewListObj(0, NULL);
5035 Tcl_ListObjAppendElement(xcinterp, tclnewcall, Tcl_NewStringObj("name", 4));
5036 Tcl_ListObjAppendElement(xcinterp, tclnewcall,
5037 Tcl_NewStringObj(calls->callobj->name,
5038 strlen(calls->callobj->name)));
5039
5040
5041 /* Log any local parameter instances */
5042 if (calls->callinst->params != NULL) {
5043 tclparams = Tcl_NewListObj(0, NULL);
5044
5045 for (paramlist = calls->callinst->params; paramlist != NULL;
5046 paramlist = paramlist->next) {
5047 Tcl_ListObjAppendElement(xcinterp, tclparams,
5048 Tcl_NewStringObj(paramlist->key, strlen(paramlist->key)));
5049 switch(paramlist->type) {
5050 case XC_INT:
5051 Tcl_ListObjAppendElement(xcinterp, tclparams,
5052 Tcl_NewIntObj((int)paramlist->parameter.ivalue));
5053 break;
5054 case XC_FLOAT:
5055 Tcl_ListObjAppendElement(xcinterp, tclparams,
5056 Tcl_NewDoubleObj((double)paramlist->parameter.fvalue));
5057 break;
5058 case XC_STRING:
5059 Tcl_ListObjAppendElement(xcinterp, tclparams,
5060 TclGetStringParts(paramlist->parameter.string));
5061 break;
5062 case XC_EXPR:
5063 Tcl_ListObjAppendElement(xcinterp, tclparams,
5064 evaluate_raw(cschem, paramlist, cinst, NULL));
5065 break;
5066 }
5067 }
5068 Tcl_ListObjAppendElement(xcinterp, tclnewcall,
5069 Tcl_NewStringObj("parameters", 10));
5070 Tcl_ListObjAppendElement(xcinterp, tclnewcall, tclparams);
5071 }
5072
5073 /* Called ports are listed by key:value pair (port number: net id) */
5074
5075 if (calls->callobj->ports != NULL) {
5076 tclports = Tcl_NewListObj(0, NULL);
5077 for (ports = calls->callobj->ports; ports != NULL;
5078 ports = ports->next) {
5079 portid = ports->portid;
5080 for (plist = calls->ports; plist != NULL; plist = plist->next)
5081 if (plist->portid == ports->portid)
5082 break;
5083
5084 pnet = (plist == NULL) ? netmax(cschem) + 1 : plist->netid;
5085
5086 netnum = Tcl_NewIntObj((int)pnet);
5087 portnum = Tcl_NewIntObj(portid);
5088 Tcl_ListObjAppendElement(xcinterp, tclports, portnum);
5089 Tcl_ListObjAppendElement(xcinterp, tclports, netnum);
5090 if ((pnet >= 0) && (netsdone[pnet] == (char)0))
5091 {
5092 Tcl_ListObjAppendElement(xcinterp, tclnets, netnum);
5093 Tcl_ListObjAppendElement(xcinterp, tclnets,
5094 TclGetStringParts(nettopin(pnet, cschem, NULL)));
5095 /* record this net as having been added to the list */
5096 netsdone[pnet] = (char)1;
5097 }
5098 }
5099 Tcl_ListObjAppendElement(xcinterp, tclnewcall,
5100 Tcl_NewStringObj("ports", 5));
5101 Tcl_ListObjAppendElement(xcinterp, tclnewcall, tclports);
5102 }
5103 Tcl_ListObjAppendElement(xcinterp, tclcalls, tclnewcall);
5104 }
5105 Tcl_ListObjAppendElement(xcinterp, subckt, Tcl_NewStringObj("calls", 5));
5106 Tcl_ListObjAppendElement(xcinterp, subckt, tclcalls);
5107 }
5108 free(netsdone);
5109
5110 /* If the object has info labels, write a device list. */
5111 /* Check both the schematic and its symbol for info labels. */
5112
5113 tcldevs = Tcl_NewListObj(0, NULL);
5114 if (cschem->symschem != NULL) {
5115 newdev = tclparseinfo(cschem->symschem);
5116 Tcl_ListObjAppendElement(xcinterp, tcldevs, newdev);
5117 }
5118 newdev = tclparseinfo(cschem);
5119 Tcl_ListObjAppendElement(xcinterp, tcldevs, newdev);
5120 Tcl_ListObjAppendElement(xcinterp, subckt, Tcl_NewStringObj("devices", 7));
5121 Tcl_ListObjAppendElement(xcinterp, subckt, tcldevs);
5122
5123 /* Write the network cross-reference dictionary */
5124 Tcl_ListObjAppendElement(xcinterp, subckt, Tcl_NewStringObj("nets", 4));
5125 Tcl_ListObjAppendElement(xcinterp, subckt, tclnets);
5126
5127 Tcl_ListObjAppendElement(xcinterp, cktlist, subckt);
5128 }
5129
5130 /*----------------------------------------------------------------------*/
5131
tcltoplevel(objinstptr cinst)5132 Tcl_Obj *tcltoplevel(objinstptr cinst)
5133 {
5134 Tcl_Obj *cktlist;
5135 objectptr cschem = cinst->thisobject;
5136
5137 cktlist = Tcl_NewListObj(0, NULL);
5138 cleartraversed(cschem);
5139 tclhierarchy(cschem, cinst, NULL, cktlist);
5140 return cktlist;
5141 }
5142
5143 #endif /* TCL_WRAPPER */
5144
5145 /*----------------------------------------------------------------------*/
5146 /* Create generic netlist in the Python interpreter variable space */
5147 /*----------------------------------------------------------------------*/
5148
5149 #ifdef HAVE_PYTHON
5150
pyparseinfo(objectptr cschem)5151 static PyObject *pyparseinfo(objectptr cschem)
5152 {
5153 genericptr *pgen;
5154 labelptr plabel;
5155
5156 PyObject *rlist = PyList_New(0);
5157
5158 for (pgen = cschem->plist; pgen < cschem->plist + cschem->parts; pgen++) {
5159 if (IS_LABEL(*pgen)) {
5160 plabel = TOLABEL(pgen);
5161 if (plabel->pin == INFO)
5162 PyList_Append(rlist, PyGetStringParts(plabel->string));
5163 }
5164 }
5165 return rlist;
5166 }
5167
5168 /*----------------------------------------------------------------------*/
5169 /* Write global variables to Python dictionary */
5170 /*----------------------------------------------------------------------*/
5171
pyglobals(objectptr cschem)5172 PyObject *pyglobals(objectptr cschem)
5173 {
5174 LabellistPtr llist;
5175 labelptr gpin;
5176 PyObject *gdict, *netnum;
5177 int netid, lbus;
5178 buslist *sbus;
5179
5180 gdict = PyDict_New();
5181 for (llist = global_labels; llist != NULL; llist = llist->next) {
5182 gpin = llist->label;
5183 for (lbus = 0;;) {
5184 if (llist->subnets == 0)
5185 netid = llist->net.id;
5186 else {
5187 sbus = llist->net.list + lbus;
5188 netid = sbus->netid;
5189 }
5190 netnum = PyInt_FromLong((long)(netid));
5191 PyDict_SetItem(gdict, netnum, PyGetStringParts(gpin->string));
5192 if (++lbus >= llist->subnets) break;
5193 }
5194 }
5195 return gdict;
5196 }
5197
5198 /*----------------------------------------------------------------------*/
5199 /* Write a generic hierarchical netlist into Python list "subcircuits" */
5200 /*----------------------------------------------------------------------*/
5201
pyhierarchy(objectptr cschem,CalllistPtr cfrom,PyObject * cktlist)5202 void pyhierarchy(objectptr cschem, CalllistPtr cfrom, PyObject *cktlist)
5203 {
5204 CalllistPtr calls = cschem->calls;
5205 PortlistPtr ports, plist;
5206 int netid, pnet, portid, length, plen, i;
5207 PyObject *pyports, *pycalls, *pynewcall, *pynets, *netnum;
5208 PyObject *pydevs, *pyparams, *subckt, *newdev, *pylabel;
5209 oparamptr paramlist;
5210
5211 /* Trivial objects are by definition those that are supposed */
5212 /* to be resolved by the netlist generation prior to output */
5213 /* and ignored by the output generator. */
5214
5215 if (cschem->schemtype == TRIVIAL) return;
5216
5217 /* Make sure that all the subcircuits have been written first */
5218
5219 for (; calls != NULL; calls = calls->next) {
5220 if (calls->callobj->traversed == False) {
5221 calls->callobj->traversed = True;
5222 pyhierarchy(calls->callobj, calls, cktlist);
5223 }
5224 }
5225
5226 /* Write own subcircuit netlist */
5227 subckt = PyDict_New();
5228
5229 /* Prepare the dictionary of network cross-references */
5230 pynets = PyDict_New();
5231
5232 /* Write the name */
5233 PyDict_SetItem(subckt, PyString_FromString("name"),
5234 PyString_FromString(cschem->name));
5235
5236 /* Write the list of ports */
5237 if ((ports = cschem->ports) != NULL) {
5238
5239 /* List of ports in subcircuit. */
5240 /* Each parameter connects to a net which may have multiple */
5241 /* names, so find the net associated with the parameter */
5242 /* and convert it back into a pin name in the usual manner */
5243 /* using nettopin(). */
5244
5245 pyports = PyList_New(0);
5246 for (; ports != NULL; ports = ports->next) {
5247 netid = ports->netid;
5248 netnum = PyInt_FromLong((long)netid);
5249 PyList_Append(pyports, netnum);
5250 if (netid >= 0)
5251 PyDict_SetItem(pynets, netnum,
5252 PyGetStringParts(nettopin(netid, cschem, NULL)));
5253 }
5254 PyDict_SetItem(subckt, PyString_FromString("ports"), pyports);
5255 }
5256
5257 /* Write the list of parameters */
5258
5259 if (cschem->params != NULL) {
5260 pyparams = PyList_New(0);
5261
5262 for (paramlist = cschem->params; paramlist != NULL; paramlist = paramlist->next) {
5263 if (paramlist->type == XC_INT)
5264 PyList_Append(pyparams,
5265 PyInt_FromLong((long)paramlist->parameter.ivalue));
5266 if (paramlist->type == XC_FLOAT)
5267 PyList_Append(pyparams,
5268 PyFloat_FromDouble((double)paramlist->parameter.fvalue));
5269 else if (paramlist->type == XC_STRING) {
5270 pylabel = PyGetStringParts(paramlist->parameter.string);
5271 /* Should get rid of last entry, "End Parameter"; not needed */
5272 PyList_Append(pyparams, pylabel);
5273 }
5274 }
5275 PyDict_SetItem(subckt, PyString_FromString("parameters"), pyparams);
5276 }
5277
5278 /* Write the list of calls to subcircuits */
5279
5280 if ((calls = cschem->calls) != NULL) {
5281 pycalls = PyList_New(0);
5282 for (; calls != NULL; calls = calls->next) {
5283
5284 /* Don't write calls to non-functional subcircuits. */
5285 if (calls->callobj->schemtype == TRIVIAL) continue;
5286
5287 pynewcall = PyDict_New();
5288 PyDict_SetItem(pynewcall, PyString_FromString("name"),
5289 PyString_FromString(calls->callobj->name));
5290
5291 /* Log any local parameter instances */
5292 if (calls->callinst->params != NULL) {
5293 pyparams = PyList_New(0);
5294
5295 for (paramlist = cschem->params; paramlist != NULL;
5296 paramlist = paramlist->next) {
5297 if (paramlist->type == XC_INT)
5298 PyList_Append(pyparams,
5299 PyInt_FromLong((long)paramlist->parameter.ivalue));
5300 else if (paramlist->type == XC_FLOAT)
5301 PyList_Append(pyparams,
5302 PyFloat_FromDouble((double)paramlist->parameter.fvalue));
5303 else if (paramlist->type == XC_STRING)
5304 PyList_Append(pyparams,
5305 PyGetStringParts(paramlist->parameter.string));
5306 }
5307 PyDict_SetItem(pynewcall, PyString_FromString("parameters"),
5308 pyparams);
5309 }
5310
5311 /* The object's definition lists calls in the order of the object's */
5312 /* port list. Therefore, use this order to find the port list. */
5313 /* Unconnected ports will be NULL entries in the list (?). */
5314
5315 if (calls->callobj->ports != NULL) {
5316 pyports = PyList_New(0);
5317 for (ports = calls->callobj->ports; ports != NULL;
5318 ports = ports->next) {
5319 portid = ports->portid;
5320 for (plist = calls->ports; plist != NULL; plist = plist->next)
5321 if (plist->portid == ports->portid)
5322 break;
5323
5324 pnet = (plist == NULL) ? netmax(cschem) + 1 : plist->netid;
5325
5326 netnum = PyInt_FromLong((long)pnet);
5327 PyList_Append(pyports, netnum);
5328 if (pnet >= 0)
5329 PyDict_SetItem(pynets, netnum,
5330 PyGetStringParts(nettopin(pnet, cschem, NULL)));
5331 }
5332 PyDict_SetItem(pynewcall, PyString_FromString("ports"), pyports);
5333 }
5334 PyList_Append(pycalls, pynewcall);
5335 }
5336 PyDict_SetItem(subckt, PyString_FromString("calls"), pycalls);
5337 }
5338
5339 /* If the object has info labels, write a device list. */
5340 /* Check both the schematic and its symbol for info labels. */
5341
5342 pydevs = PyList_New(0);
5343 if (cschem->symschem != NULL) {
5344 newdev = pyparseinfo(cschem->symschem);
5345 PyList_Append(pydevs, newdev);
5346 }
5347 newdev = pyparseinfo(cschem);
5348 PyList_Append(pydevs, newdev);
5349 PyDict_SetItem(subckt, PyString_FromString("devices"), pydevs);
5350
5351 /* Write the network cross-reference dictionary */
5352 PyDict_SetItem(subckt, PyString_FromString("nets"), pynets);
5353
5354 PyList_Append(cktlist, subckt);
5355 }
5356
5357 /*----------------------------------------------------------------------*/
5358
pytoplevel(objectptr cschem)5359 PyObject *pytoplevel(objectptr cschem)
5360 {
5361 PyObject *cktlist;
5362
5363 cktlist = PyList_New(0);
5364 cleartraversed(cschem);
5365 pyhierarchy(cschem, NULL, cktlist);
5366 return cktlist;
5367 }
5368
5369 #endif /* HAVE_PYTHON */
5370
5371 /*----------------------------------------------------------------------*/
5372 /* Update networks in an object by checking if the network is valid, */
5373 /* and destroying and recreating the network if necessary. */
5374 /* */
5375 /* Run cleartraversed() on all types. The traversed flag allows a */
5376 /* check for infinite recursion when creating calls. */
5377 /* */
5378 /* Returns -1 on discovery of infinite recursion, 0 on failure to */
5379 /* generate a net, and 1 on success. */
5380 /*----------------------------------------------------------------------*/
5381
updatenets(objinstptr uinst,Boolean quiet)5382 int updatenets(objinstptr uinst, Boolean quiet) {
5383 objectptr thisobject;
5384 objinstptr thisinst;
5385 int spage;
5386
5387 /* Never try to generate a netlist while a file is being read, as */
5388 /* can happen if an expression parameter attempts to query a netlist */
5389 /* node. */
5390
5391 if (load_in_progress) return 0;
5392
5393 if (uinst->thisobject->symschem != NULL
5394 && uinst->thisobject->schemtype != PRIMARY) {
5395 thisobject = uinst->thisobject->symschem;
5396 if ((spage = is_page(thisobject)) >= 0)
5397 thisinst = xobjs.pagelist[spage]->pageinst;
5398 }
5399 else {
5400 thisobject = uinst->thisobject;
5401 thisinst = uinst;
5402 }
5403
5404 if (checkvalid(thisobject) == -1) {
5405 uselection *ssave;
5406
5407 if (cleartraversed(thisobject) == -1) {
5408 Wprintf("Netlist error: Check for recursion in circuit!");
5409 return -1;
5410 }
5411
5412 /* Note: destroy/create nets messes up the part list. Why?? */
5413
5414 if (areawin->selects > 0)
5415 ssave = remember_selection(areawin->topinstance, areawin->selectlist,
5416 areawin->selects);
5417 destroynets(thisobject);
5418 createnets(thisinst, quiet);
5419 if (areawin->selects > 0) {
5420 areawin->selectlist = regen_selection(areawin->topinstance, ssave);
5421 free_selection(ssave);
5422 }
5423 }
5424
5425 if (thisobject->labels == NULL && thisobject->polygons == NULL) {
5426 if (quiet == FALSE)
5427 Wprintf("Netlist error: No netlist elements in object %s",
5428 thisobject->name);
5429 return 0;
5430 }
5431 return 1;
5432 }
5433
5434 /*----------------------------------------------------------------------*/
5435 /* Generate netlist, write information according to mode, and then */
5436 /* destroy the netlist (this will be replaced eventually with a dynamic */
5437 /* netlist model in which the netlist changes according to editing of */
5438 /* individual elements, not created and destroyed wholesale) */
5439 /*----------------------------------------------------------------------*/
5440
writenet(objectptr thisobject,char * mode,char * suffix)5441 void writenet(objectptr thisobject, char *mode, char *suffix)
5442 {
5443 objectptr cschem;
5444 objinstptr thisinst;
5445 char filename[100];
5446 char *prefix, *cpos, *locmode = mode, *stsave = NULL;
5447 FILE *fp;
5448 /* Calllist topcell; (jdk) */
5449 Boolean is_spice = FALSE, sp_end_save = spice_end;
5450
5451 /* Always use the master schematic, if there is one. */
5452
5453 if (thisobject->schemtype == SECONDARY)
5454 cschem = thisobject->symschem;
5455 else
5456 cschem = thisobject;
5457
5458 /* Update the netlist if this has not been done */
5459
5460 if (NameToPageObject(cschem->name, &thisinst, NULL) == NULL) {
5461 Wprintf("Not a schematic. . . cannot generate output!\n");
5462 return;
5463 }
5464 if (updatenets(thisinst, FALSE) <= 0) {
5465 Wprintf("No file written!");
5466 return;
5467 }
5468
5469 prefix = (char *)malloc(sizeof(char));
5470 *prefix = '\0';
5471
5472 if ((cpos = strchr(cschem->name, ':')) != NULL) *cpos = '\0';
5473 sprintf(filename, "%s.%s", cschem->name, suffix);
5474 if (cpos != NULL) *cpos = ':';
5475
5476 if (!strncmp(mode, "index", 5)) { /* This mode generates no output */
5477 locmode += 5;
5478 fp = (FILE *)NULL;
5479 }
5480 else if ((fp = fopen(filename, "w")) == NULL) {
5481 Wprintf("Could not open file %s for writing.", filename);
5482 free(prefix);
5483 return;
5484 }
5485
5486 /* Clear device indices from any previous netlist output */
5487 cleartraversed(cschem);
5488 clear_indices(cschem);
5489
5490 /* Make sure list of include-once files is empty */
5491
5492 free_included();
5493
5494 /* Handle different netlist modes */
5495
5496 if (!strcmp(mode, "spice")) {
5497
5498 if (thisobject->schemtype == SYMBOL)
5499 cschem = thisobject->symschem;
5500
5501 is_spice = TRUE;
5502 fprintf(fp, "*SPICE %scircuit <%s> from XCircuit v%s rev %s\n\n",
5503 (thisobject->schemtype == SYMBOL) ? "sub" : "",
5504 cschem->name, PROG_VERSION, PROG_REVISION);
5505
5506 /* writeglobals(cschem, fp); */
5507 cleartraversed(cschem);
5508 writehierarchy(cschem, thisinst, NULL, fp, mode);
5509 }
5510 else if (!strcmp(mode, "flatspice")) {
5511 is_spice = TRUE;
5512 fprintf(fp, "*SPICE (flattened) circuit \"%s\" from XCircuit v%s rev %s\n\n",
5513 cschem->name, PROG_VERSION, PROG_REVISION);
5514 if (stsave != NULL) {
5515 fputs(stsave, fp);
5516 fprintf(fp, "\n");
5517 }
5518 topflat(cschem, thisinst, NULL, prefix, fp, mode);
5519 }
5520 else if (!strcmp(mode, "pseuspice")) {
5521 is_spice = TRUE;
5522 fprintf(fp, "*SPICE subcircuit \"%s\" from XCircuit v%s rev %s\n\n",
5523 cschem->name, PROG_VERSION, PROG_REVISION);
5524 if (stsave != NULL) {
5525 fputs(stsave, fp);
5526 fprintf(fp, "\n");
5527 }
5528 writeflat(cschem, NULL, prefix, fp, mode);
5529 freeflatindex();
5530 }
5531 else if (!strcmp(mode, "flatsim") || !strcmp(mode, "pseusim")) {
5532 fprintf(fp, "| sim circuit \"%s\" from XCircuit v%s rev %s\n",
5533 cschem->name, PROG_VERSION, PROG_REVISION);
5534 if (stsave != NULL) {
5535 fputs(stsave, fp);
5536 fprintf(fp, "\n");
5537 }
5538 topflat(cschem, thisinst, NULL, prefix, fp, mode);
5539 }
5540 else if (!strcmp(locmode, "pcb")) {
5541 struct Ptab *ptable = (struct Ptab *)NULL;
5542 writepcb(&ptable, cschem, NULL, "", mode);
5543 if (stsave != NULL) {
5544 fputs(stsave, fp);
5545 fprintf(fp, "\n");
5546 }
5547 outputpcb(ptable, fp);
5548 freepcb(ptable);
5549 }
5550 else if (!strncmp(mode, "flat", 4)) {
5551 /* User-defined modes beginning with the word "flat": */
5552 /* Assume a flattened netlist, and assume nothing else. */
5553
5554 if (thisobject->schemtype == SYMBOL)
5555 cschem = thisobject->symschem;
5556 cleartraversed(cschem);
5557 writeflat(cschem, NULL, prefix, fp, mode);
5558 freeflatindex();
5559 }
5560 else if (!strncmp(mode, "pseu", 4)) {
5561 /* User-defined modes beginning with the word "pseu": */
5562 /* Assume a flattened netlist for everything under the top level. */
5563
5564 if (thisobject->schemtype == SYMBOL)
5565 cschem = thisobject->symschem;
5566 cleartraversed(cschem);
5567 topflat(cschem, thisinst, NULL, prefix, fp, mode);
5568 }
5569 else {
5570 /* All user-defined modes: Assume a hierarchical netlist, and */
5571 /* assume nothing else. */
5572
5573 if (thisobject->schemtype == SYMBOL)
5574 cschem = thisobject->symschem;
5575 cleartraversed(cschem);
5576 writehierarchy(cschem, thisinst, NULL, fp, mode);
5577 }
5578
5579 /* Finish up SPICE files with a ".end" statement (if requested) */
5580 if (is_spice && (spice_end == True)) fprintf(fp, ".end\n");
5581 spice_end = sp_end_save;
5582
5583 /* Finish up */
5584
5585 if (fp != NULL) {
5586 fclose(fp);
5587 Wprintf("%s netlist saved as %s", mode, filename);
5588 }
5589 if (stsave != NULL) free(stsave);
5590 free(prefix);
5591 }
5592
5593 /*----------------------------------------------------------------------*/
5594 /* Flatten netlist and save into a table of pcb-style nets */
5595 /* The routine is recursive. Each call returns TRUE if some nested */
5596 /* call generated output; this is a reasonable way to tell if we have */
5597 /* reached the bottom of the hierarchy. */
5598 /*----------------------------------------------------------------------*/
5599
writepcb(struct Ptab ** ptableptr,objectptr cschem,CalllistPtr cfrom,char * prefix,char * mode)5600 Boolean writepcb(struct Ptab **ptableptr, objectptr cschem, CalllistPtr cfrom,
5601 char *prefix, char *mode)
5602 {
5603 PolylistPtr plist;
5604 LabellistPtr llist;
5605 CalllistPtr calls;
5606 PortlistPtr ports;
5607 int i, testnet, tmplen, subnet;
5608 char *newprefix = (char *)malloc(sizeof(char));
5609 char *sout, *snew;
5610 struct Ptab *hidx, *htmp;
5611 struct Pstr *tmpstr;
5612 struct Pnet *tmpnet;
5613 objinstptr cinst;
5614 buslist *sbus;
5615 int locnet, lbus;
5616 Boolean outputdone = FALSE;
5617 Boolean outputcall;
5618
5619 /* Step 1A: Go through the polygons of this object and add */
5620 /* any unvisited nets to the table. */
5621
5622 for (plist = cschem->polygons; plist != NULL; plist = plist->next) {
5623 for (lbus = 0;;) {
5624 if (plist->subnets == 0)
5625 testnet = plist->net.id;
5626 else {
5627 sbus = plist->net.list + lbus;
5628 testnet = sbus->netid;
5629 }
5630
5631 hidx = *ptableptr;
5632 while (hidx != NULL) {
5633 if (hidx->nets != NULL) {
5634 for (i = 0; i < hidx->nets->numnets; i++)
5635 if (*(hidx->nets->netidx + i) == testnet) break;
5636 if (i < hidx->nets->numnets) break;
5637 }
5638 hidx = hidx->next;
5639 }
5640 if (hidx == NULL) { /* make new entry for net in table */
5641 htmp = (struct Ptab *)malloc(sizeof(struct Ptab));
5642 tmpnet = (struct Pnet *)malloc(sizeof(struct Pnet));
5643 tmpnet->numnets = 1;
5644 tmpnet->netidx = (int *)malloc(sizeof(int));
5645 *(tmpnet->netidx) = testnet;
5646 tmpnet->next = NULL;
5647 htmp->cschem = cschem;
5648 htmp->nets = tmpnet;
5649 htmp->pins = NULL;
5650 htmp->next = *ptableptr;
5651 *ptableptr = htmp;
5652 /* Fprintf(stdout, "Added new index entry for net %d\n", testnet); */
5653 }
5654 if (++lbus >= plist->subnets) break;
5655 }
5656 }
5657
5658 /* Step 1B: Do the same thing for labels. */
5659
5660 for (llist = cschem->labels; llist != NULL; llist = llist->next) {
5661 for (lbus = 0;;) {
5662 if (llist->subnets == 0)
5663 testnet = llist->net.id;
5664 else {
5665 sbus = llist->net.list + lbus;
5666 testnet = sbus->netid;
5667 }
5668
5669 hidx = *ptableptr;
5670 while (hidx != NULL) {
5671 if (hidx->nets != NULL) {
5672 for (i = 0; i < hidx->nets->numnets; i++)
5673 if (*(hidx->nets->netidx + i) == testnet) break;
5674 if (i < hidx->nets->numnets) break;
5675 }
5676 hidx = hidx->next;
5677 }
5678 if (hidx == NULL) { /* make new entry for net in table */
5679 htmp = (struct Ptab *)malloc(sizeof(struct Ptab));
5680 tmpnet = (struct Pnet *)malloc(sizeof(struct Pnet));
5681 tmpnet->numnets = 1;
5682 tmpnet->netidx = (int *)malloc(sizeof(int));
5683 *(tmpnet->netidx) = testnet;
5684 tmpnet->next = NULL;
5685 htmp->cschem = cschem;
5686 htmp->nets = tmpnet;
5687 htmp->pins = NULL;
5688 htmp->next = *ptableptr;
5689 *ptableptr = htmp;
5690 /* Fprintf(stdout, "Added new index entry for net %d\n", testnet); */
5691 }
5692 if (++lbus >= llist->subnets) break;
5693 }
5694 }
5695
5696 /* Step 2: Resolve fixed device indices */
5697 resolve_devindex(cschem, FALSE);
5698
5699 /* Step 3: Go through the list of calls to search for endpoints */
5700
5701 for (calls = cschem->calls; calls != NULL; calls = calls->next) {
5702 objectptr cthis;
5703
5704 cinst = calls->callinst;
5705
5706 /* Trivial objects should have been dealt with already. */
5707 /* If we don't continue the loop here, you get netlist output for */
5708 /* objects like dots and circles. */
5709 if (calls->callobj->schemtype == TRIVIAL) continue;
5710
5711 /* Step 4: If call is to a bottom-most schematic, output the device connections */
5712 /* Info-label can provide an alternate name or specify the instance number */
5713
5714 cthis = calls->callobj;
5715 if (calls->callobj->schemtype == PRIMARY || calls->callobj->schemtype ==
5716 SECONDARY)
5717 if (calls->callobj->symschem != NULL)
5718 cthis = calls->callobj->symschem;
5719
5720 if ((sout = parseinfo(cschem, cthis, calls, prefix, mode, FALSE, TRUE)) == NULL) {
5721 if (calls->devname == NULL)
5722 calls->devname = strdup(calls->callinst->thisobject->name);
5723 sprintf(_STR, "%s_%s", calls->devname, d36a(devindex(cschem, calls)));
5724 }
5725 else {
5726 sscanf(sout, "%s", _STR); /* Copy the first word out of sout */
5727 }
5728 newprefix = (char *)realloc(newprefix, sizeof(char) * (strlen(prefix)
5729 + strlen(_STR) + 2));
5730 sprintf(newprefix, "%s%s/", prefix, _STR);
5731
5732 /*----------------------------------------------------------------*/
5733 /* Parsing of the "pcb:" info label--- */
5734 /* Find all <net>=<name> strings and create the appropriate nets */
5735 /*----------------------------------------------------------------*/
5736
5737 if (sout) {
5738 char rsave, *lhs, *rhs, *rend, *sptr = sout, *tmppinname;
5739
5740 while ((rhs = strchr(sptr, '=')) != NULL) {
5741 Genericlist *implicit, newlist;
5742 struct Pstr *psrch = NULL;
5743
5744 lhs = rhs - 1;
5745 while ((lhs >= sptr) && isspace(*lhs)) lhs--;
5746 while ((lhs >= sptr) && !isspace(*lhs)) lhs--;
5747 *rhs = '\0';
5748 rhs++;
5749 if (isspace(*lhs)) lhs++;
5750 while ((*rhs) && isspace(*rhs)) rhs++;
5751 for (rend = rhs; (*rend) && (!isspace(*rend)); rend++);
5752 rsave = *rend;
5753 *rend = '\0';
5754
5755 /* Get the net from the equation RHS. If none exists, assume */
5756 /* the name is a global net and add it to the list of globals. */
5757
5758 implicit = nametonet(cschem, cinst, rhs);
5759 if (implicit == NULL) {
5760 stringpart *strptr;
5761 label templabel;
5762
5763 labeldefaults(&templabel, GLOBAL, 0, 0);
5764 strptr = templabel.string;
5765 strptr->type = TEXT_STRING;
5766 strptr->data.string = strdup(rhs);
5767 newlist.subnets = 0;
5768 newlist.net.id = globalmax() - 1;
5769 addglobalpin(cschem, cinst, &templabel, &newlist);
5770 implicit = &newlist;
5771 free(strptr->data.string);
5772 }
5773
5774 /* Get the name of the pin */
5775 tmppinname = (char *)malloc(strlen(newprefix) + strlen(lhs) + 2);
5776 strcpy(tmppinname, newprefix);
5777 if ((tmplen = strlen(newprefix)) > 0) tmppinname[tmplen - 1] = '-';
5778 strcat(tmppinname, lhs);
5779
5780 /* Find the net in the ptable, or create a new entry for it */
5781
5782 hidx = *ptableptr;
5783 while (hidx != NULL) {
5784 if (hidx->nets != NULL) {
5785 for (i = 0; i < hidx->nets->numnets; i++)
5786 if (*(hidx->nets->netidx + i) == implicit->net.id)
5787 break;
5788 if (i < hidx->nets->numnets) break;
5789 }
5790 hidx = hidx->next;
5791 }
5792 if (hidx == NULL) {
5793 htmp = (struct Ptab *)malloc(sizeof(struct Ptab));
5794 tmpnet = (struct Pnet *)malloc(sizeof(struct Pnet));
5795 tmpnet->numnets = 1;
5796 tmpnet->netidx = (int *)malloc(sizeof(int));
5797 *(tmpnet->netidx) = implicit->net.id;
5798 tmpnet->next = NULL;
5799 htmp->cschem = cschem;
5800 htmp->nets = tmpnet;
5801 htmp->pins = NULL;
5802 htmp->next = *ptableptr;
5803 *ptableptr = htmp;
5804 hidx = htmp;
5805 }
5806 else {
5807 /* Check if any entries are the same as tmppinname */
5808 for (psrch = hidx->pins; psrch != NULL; psrch = psrch->next) {
5809 if (!strcmp(psrch->string->data.string, tmppinname))
5810 break;
5811 }
5812 }
5813
5814 /* Get the pin name from the equation LHS */
5815 if (psrch == NULL) {
5816 tmpstr = (struct Pstr *)malloc(sizeof(struct Pstr));
5817 tmpstr->string = (stringpart *)malloc(sizeof(stringpart));
5818 tmpstr->string->type = TEXT_STRING;
5819 tmpstr->string->nextpart = NULL;
5820 tmpstr->string->data.string = tmppinname;
5821 tmpstr->next = hidx->pins;
5822 hidx->pins = tmpstr;
5823 }
5824 else {
5825 free(tmppinname);
5826 }
5827
5828 /* Proceed to the next LHS=RHS pair */
5829 *rend = rsave;
5830 sptr = rend;
5831 }
5832 free(sout);
5833 }
5834
5835 outputcall = FALSE;
5836 if (calls->callobj->calls != NULL) {
5837
5838 /* Step 4A: Push current net translations */
5839 /* (Don't push or pop global net numbers: no translation needed!) */
5840
5841 hidx = *ptableptr;
5842 while (hidx != NULL) {
5843 if ((hidx->nets != NULL) && (((hidx->nets->numnets > 0) &&
5844 (*(hidx->nets->netidx) >= 0)) || (hidx->nets->numnets == 0))) {
5845 tmpnet = (struct Pnet *)malloc(sizeof(struct Pnet));
5846 tmpnet->numnets = 0;
5847 tmpnet->netidx = NULL;
5848 tmpnet->next = hidx->nets;
5849 hidx->nets = tmpnet;
5850 }
5851 hidx = hidx->next;
5852 }
5853
5854 /* Step 4B: Generate net translation table for each subcircuit */
5855
5856 for (ports = calls->ports; ports != NULL; ports = ports->next) {
5857 for (hidx = *ptableptr; hidx != NULL; hidx = hidx->next) {
5858 if (hidx->nets != NULL) {
5859 if (hidx->nets->next != NULL) {
5860 for (i = 0; i < hidx->nets->next->numnets; i++)
5861 if (*(hidx->nets->next->netidx + i) == ports->netid)
5862 break;
5863 if (i < hidx->nets->next->numnets) break;
5864 }
5865 else if (ports->netid < 0) {
5866 if (hidx->nets->netidx != NULL)
5867 if (*(hidx->nets->netidx) == ports->netid)
5868 break;
5869 }
5870 }
5871 }
5872 if (hidx != NULL) {
5873 hidx->nets->numnets++;
5874 if (hidx->nets->numnets == 1)
5875 hidx->nets->netidx = (int *)malloc(sizeof(int));
5876 else
5877 hidx->nets->netidx = (int *)realloc(hidx->nets->netidx,
5878 hidx->nets->numnets * sizeof(int));
5879
5880 /* Translate net value */
5881 locnet = translatedown(ports->netid, ports->portid,
5882 calls->callobj);
5883 *(hidx->nets->netidx + hidx->nets->numnets - 1) = locnet;
5884
5885 /* Fprintf(stdout, "Translation: net %d in object %s is net "
5886 "%d in object %s\n", ports->netid, cschem->name,
5887 locnet, calls->callobj->name); */
5888 }
5889 }
5890
5891 /* Step 4C: Run routine recursively on the subcircuit */
5892 /* If it had a "pcb:" info label that was handled, then ignore */
5893
5894 /* Fprintf(stdout, "Recursive call of writepcb() to %s\n",
5895 calls->callobj->name); */
5896
5897 outputcall = writepcb(ptableptr, calls->callobj, calls, newprefix, mode);
5898
5899 /* Step 4D: Pop the translation table */
5900 /* (Don't pop global nets (designated by negative net number)) */
5901
5902 hidx = *ptableptr;
5903 while (hidx != NULL) {
5904 if ((hidx->nets != NULL) && (((hidx->nets->numnets > 0) &&
5905 (*(hidx->nets->netidx) >= 0)) || (hidx->nets->numnets == 0))) {
5906 tmpnet = hidx->nets->next;
5907 if (hidx->nets->numnets > 0) free(hidx->nets->netidx);
5908 free(hidx->nets);
5909 hidx->nets = tmpnet;
5910 }
5911 hidx = hidx->next;
5912 }
5913 }
5914
5915 if (!outputcall) {
5916 stringpart *ppin;
5917
5918 /* Fprintf(stdout, "Reached lowest-level schematic: Finding connections\n"); */
5919 for (ports = calls->ports; ports != NULL; ports = ports->next) {
5920 locnet = translatedown(ports->netid, ports->portid, calls->callobj);
5921 /* Global pin names should probably be ignored, not just */
5922 /* when an object is declared to be "trivial". Not sure if */
5923 /* this breaks certain netlists. . . */
5924 if (locnet < 0) continue;
5925
5926 /* Get the name of the pin in the called object with no prefix. */
5927 subnet = getsubnet(locnet, calls->callobj);
5928 ppin = nettopin(locnet, calls->callobj, NULL);
5929 hidx = *ptableptr;
5930 while (hidx != NULL) {
5931 if ((hidx->nets != NULL) && (hidx->nets->numnets > 0)) {
5932 /* Global nets were not translated, do not iterate through list */
5933 if (*(hidx->nets->netidx) >= 0) {
5934 for (i = 0; i < hidx->nets->numnets; i++)
5935 if (*(hidx->nets->netidx + i) == ports->netid)
5936 break;
5937 if (i < hidx->nets->numnets) break;
5938 } else {
5939 if (*(hidx->nets->netidx) == ports->netid) break;
5940 }
5941 }
5942 hidx = hidx->next;
5943 }
5944 if (hidx == NULL) {
5945 snew = textprintsubnet(ppin, cinst, subnet);
5946 Fprintf(stdout, "Warning: Unconnected pin %s%s\n", newprefix, snew);
5947 free(snew);
5948 }
5949 else {
5950 outputcall = TRUE;
5951 outputdone = TRUE;
5952 tmpstr = (struct Pstr *)malloc(sizeof(struct Pstr));
5953 tmpstr->string = (stringpart *)malloc(sizeof(stringpart));
5954 tmpstr->string->type = TEXT_STRING;
5955 tmpstr->string->nextpart = NULL;
5956 snew = textprintsubnet(ppin, cinst, subnet);
5957 tmpstr->string->data.string = (char *)malloc(strlen(newprefix)
5958 + strlen(snew) + 2);
5959 strcpy(tmpstr->string->data.string, newprefix);
5960 /* Replace slash '/' with dash '-' at bottommost level */
5961 if ((tmplen = strlen(newprefix)) > 0)
5962 tmpstr->string->data.string[tmplen - 1] = '-';
5963 strcat(tmpstr->string->data.string, snew);
5964 free(snew);
5965 tmpstr->next = hidx->pins;
5966 hidx->pins = tmpstr;
5967
5968 /* diagnostic information */
5969 {
5970 struct Pnet *locnet = hidx->nets;
5971 int ctr = 0;
5972 while (locnet->next != NULL) {
5973 locnet = locnet->next;
5974 ctr++;
5975 }
5976 /* Fprintf(stdout, "Logged level-%d net %d (local net %d) pin %s\n",
5977 ctr, *(locnet->netidx), *(hidx->nets->netidx + i),
5978 tmpstr->string); */
5979 }
5980 }
5981 }
5982 }
5983 }
5984
5985 /* Step 5: Cleanup */
5986
5987 free(newprefix);
5988 return outputdone;
5989 }
5990
5991 /*----------------------------------------------------------------------*/
5992 /* Save PCB table into pcb-style file */
5993 /*----------------------------------------------------------------------*/
5994
outputpcb(struct Ptab * ptable,FILE * fp)5995 void outputpcb(struct Ptab *ptable, FILE *fp)
5996 {
5997 int netidx = 1, ccol, subnet;
5998 struct Ptab *pseek;
5999 struct Pstr *sseek;
6000 char *snew;
6001 stringpart *ppin;
6002
6003 if (fp == NULL) return;
6004
6005 for (pseek = ptable; pseek != NULL; pseek = pseek->next) {
6006 if (pseek->pins != NULL) {
6007 if ((pseek->nets != NULL) && (pseek->nets->numnets > 0)) {
6008 subnet = getsubnet(*(pseek->nets->netidx), pseek->cschem);
6009 ppin = nettopin(*(pseek->nets->netidx), pseek->cschem, "");
6010 snew = textprintsubnet(ppin, NULL, subnet);
6011 strcpy(_STR, snew);
6012 free(snew);
6013 }
6014 else
6015 sprintf(_STR, "NET%d ", netidx++);
6016 fprintf(fp, "%-11s ", _STR);
6017 ccol = 12;
6018 for (sseek = pseek->pins; sseek != NULL; sseek = sseek->next) {
6019 ccol += stringlength(sseek->string, False, NULL) + 3;
6020 if (ccol > 78) {
6021 fprintf(fp, "\\\n ");
6022 ccol = 18 + stringlength(sseek->string, False, NULL);
6023 }
6024 snew = textprint(sseek->string, NULL);
6025 fprintf(fp, "%s ", snew);
6026 free(snew);
6027 }
6028 fprintf(fp, "\n");
6029 }
6030 /* else fprintf(fp, "NET%d *UNCONNECTED*\n", netidx++); */
6031 }
6032 }
6033
6034 /*----------------------------------------------*/
6035 /* free memory allocated to PCB net tables */
6036 /*----------------------------------------------*/
6037
freepcb(struct Ptab * ptable)6038 void freepcb(struct Ptab *ptable)
6039 {
6040 struct Ptab *pseek, *pseek2;
6041 struct Pstr *sseek, *sseek2;
6042 struct Pnet *nseek, *nseek2;
6043
6044 pseek = ptable;
6045 pseek2 = pseek;
6046
6047 while (pseek2 != NULL) {
6048 pseek = pseek->next;
6049
6050 sseek = pseek2->pins;
6051 sseek2 = sseek;
6052 while (sseek2 != NULL) {
6053 sseek = sseek->next;
6054 freelabel(sseek2->string);
6055 free(sseek2);
6056 sseek2 = sseek;
6057 }
6058
6059 nseek = pseek2->nets;
6060 nseek2 = nseek;
6061 while (nseek2 != NULL) {
6062 nseek = nseek->next;
6063 if (nseek2->numnets > 0) free(nseek2->netidx);
6064 free(nseek2);
6065 nseek2 = nseek;
6066 }
6067
6068 free(pseek2);
6069 pseek2 = pseek;
6070 }
6071 }
6072
6073 /*----------------------------------------------------------------------*/
6074 /* Remove an element from the netlist. This is necessary when an */
6075 /* element is deleted from the object, so that the netlist does not */
6076 /* refer to a nonexistant element, or try to perform netlist function */
6077 /* on it. */
6078 /* */
6079 /* Return True or False depending on whether a pin was "orphaned" on */
6080 /* the corresponding schematic or symbol (if any), as this may cause */
6081 /* the class of the object (FUNDAMENTAL, TRIVIAL, etc.) to change. */
6082 /*----------------------------------------------------------------------*/
6083
RemoveFromNetlist(objectptr thisobject,genericptr thiselem)6084 Boolean RemoveFromNetlist(objectptr thisobject, genericptr thiselem)
6085 {
6086 Boolean pinchanged = False;
6087 labelptr nlab;
6088 polyptr npoly;
6089 objectptr pschem;
6090 objinstptr ninst;
6091
6092 PolylistPtr plist, plast;
6093 LabellistPtr llist, llast;
6094 CalllistPtr clist, clast;
6095
6096 pschem = (thisobject->schemtype == SECONDARY) ? thisobject->symschem
6097 : thisobject;
6098
6099 switch (thiselem->type) {
6100 case OBJINST:
6101 ninst = (objinstptr)thiselem;
6102 /* If this is an object instance, remove it from the calls */
6103 clast = NULL;
6104 for (clist = pschem->calls; clist != NULL; clist = clist->next) {
6105 if (clist->callinst == ninst) {
6106 if (clast == NULL)
6107 pschem->calls = clist->next;
6108 else
6109 clast->next = clist->next;
6110 freecalls(clist);
6111 clist = (clast) ? clast : pschem->calls;
6112 break;
6113 }
6114 else
6115 clast = clist;
6116 }
6117 break;
6118
6119 case POLYGON:
6120 npoly = (polyptr)thiselem;
6121 if (nonnetwork(npoly)) break;
6122
6123 /* If this is a polygon, remove it from the netlist */
6124 plast = NULL;
6125 for (plist = pschem->polygons; plist != NULL; plist = plist->next) {
6126 if (plist->poly == npoly) {
6127 if (plast == NULL)
6128 pschem->polygons = plist->next;
6129 else
6130 plast->next = plist->next;
6131 if (plist->subnets > 0)
6132 free(plist->net.list);
6133 break;
6134 }
6135 else
6136 plast = plist;
6137 }
6138 break;
6139
6140 case LABEL:
6141 nlab = (labelptr)thiselem;
6142 if ((nlab->pin != LOCAL) && (nlab->pin != GLOBAL)) break;
6143
6144 /* If this is a label, remove it from the netlist */
6145 llast = NULL;
6146 for (llist = pschem->labels; llist != NULL; llist = llist->next) {
6147 if (llist->label == nlab) {
6148 if (llast == NULL)
6149 pschem->labels = llist->next;
6150 else
6151 llast->next = llist->next;
6152 if (llist->subnets > 0)
6153 free(llist->net.list);
6154 break;
6155 }
6156 else
6157 llast = llist;
6158 }
6159
6160 /* Mark pin label in corresponding schematic/symbol as "orphaned" */
6161 /* by changing designation from type "pin" to type "label". */
6162
6163 if (findlabelcopy(nlab, nlab->string) == NULL) {
6164 changeotherpins(NULL, nlab->string);
6165 if (nlab->pin == INFO) pinchanged = True;
6166 }
6167 }
6168 return pinchanged;
6169 }
6170
6171 /*----------------------------------------------------------------------*/
6172 /* Remove one element from the netlist */
6173 /*----------------------------------------------------------------------*/
6174
remove_netlist_element(objectptr cschem,genericptr genelem)6175 void remove_netlist_element(objectptr cschem, genericptr genelem) {
6176
6177 objectptr pschem;
6178 CalllistPtr clist, clast, cnext;
6179 LabellistPtr llist, llast, lnext;
6180 PolylistPtr plist, plast, pnext;
6181 Boolean found = FALSE;
6182
6183 /* Always call on the primary schematic */
6184 pschem = (cschem->schemtype == SECONDARY) ? cschem->symschem : cschem;
6185
6186 switch (ELEMENTTYPE(genelem)) {
6187 case POLYGON:
6188 /* remove polygon from polygon list */
6189 plast = NULL;
6190 for (plist = pschem->polygons; plist != NULL; ) {
6191 pnext = plist->next;
6192 if ((genericptr)plist->poly == genelem) {
6193 found = TRUE;
6194 if (plist->subnets > 0)
6195 free(plist->net.list);
6196 free(plist);
6197 if (plast != NULL)
6198 plast->next = pnext;
6199 else
6200 pschem->polygons = pnext;
6201 break;
6202 }
6203 else
6204 plast = plist;
6205 plist = pnext;
6206 }
6207 break;
6208
6209 case LABEL:
6210
6211 /* remove label from label list */
6212 llast = NULL;
6213 for (llist = pschem->labels; llist != NULL; ) {
6214 lnext = llist->next;
6215 if ((genericptr)llist->label == genelem) {
6216 found = TRUE;
6217 if (llist->subnets > 0)
6218 free(llist->net.list);
6219 free(llist);
6220 if (llast != NULL)
6221 llast->next = lnext;
6222 else
6223 pschem->labels = lnext;
6224 break;
6225 }
6226 else
6227 llast = llist;
6228 llist = lnext;
6229 }
6230
6231 /* also check the globals */
6232 llast = NULL;
6233 for (llist = global_labels; llist != NULL; ) {
6234 lnext = llist->next;
6235 if ((genericptr)llist->label == genelem) {
6236 found = TRUE;
6237 if (llist->subnets > 0)
6238 free(llist->net.list);
6239 free(llist);
6240 if (llast != NULL)
6241 llast->next = lnext;
6242 else
6243 global_labels = lnext;
6244 break;
6245 }
6246 else
6247 llast = llist;
6248 llist = lnext;
6249 }
6250 break;
6251
6252 case OBJINST:
6253
6254 /* remove instance from call list */
6255 clast = NULL;
6256 for (clist = pschem->calls; clist != NULL; ) {
6257 cnext = clist->next;
6258 if ((genericptr)clist->callinst == genelem) {
6259 found = TRUE;
6260 freecalls(clist);
6261 if (clast != NULL)
6262 clast->next = cnext;
6263 else
6264 pschem->calls = cnext;
6265 }
6266 else
6267 clast = clist;
6268 clist = cnext;
6269 }
6270 break;
6271 }
6272 if (found)
6273 pschem->valid = FALSE;
6274 }
6275
6276 /*----------------------------------------------------------------------*/
6277 /* Free memory allocated for the ports in a calls. */
6278 /*----------------------------------------------------------------------*/
6279
freecalls(CalllistPtr calls)6280 void freecalls(CalllistPtr calls)
6281 {
6282 PortlistPtr ports, pptr;
6283
6284 for (ports = calls->ports; ports != NULL;) {
6285 pptr = ports->next;
6286 free(ports);
6287 ports = pptr;
6288 }
6289 if (calls->devname != NULL) free(calls->devname);
6290 free(calls);
6291 }
6292
6293 /*----------------------------------------------------------------------*/
6294 /* Free memory for a Genericlist structure (may also be a Labellist or */
6295 /* Polylist structure). */
6296 /*----------------------------------------------------------------------*/
6297
freegenlist(Genericlist * nets)6298 void freegenlist(Genericlist *nets)
6299 {
6300 if (nets == NULL) return;
6301 if (nets->subnets > 0)
6302 free(nets->net.list);
6303 free(nets);
6304 }
6305
6306 /*----------------------------------------------------------------------*/
6307 /* Free memory allocated for the label list in a netlist. */
6308 /*----------------------------------------------------------------------*/
6309
freelabellist(LabellistPtr * listtop)6310 void freelabellist(LabellistPtr *listtop)
6311 {
6312 LabellistPtr labellist, llist;
6313
6314 for (labellist = *listtop; labellist != NULL;) {
6315 llist = labellist->next;
6316 freegenlist((Genericlist *)labellist);
6317 labellist = llist;
6318 }
6319 *listtop = NULL;
6320 }
6321
6322 /*----------------------------------------------------------------------*/
6323 /* Free memory allocated for the polygon list in a netlist. */
6324 /*----------------------------------------------------------------------*/
6325
freepolylist(PolylistPtr * listtop)6326 void freepolylist(PolylistPtr *listtop)
6327 {
6328 PolylistPtr polylist, plist;
6329
6330 for (polylist = *listtop; polylist != NULL;) {
6331 plist = polylist->next;
6332 freegenlist((Genericlist *)polylist);
6333 polylist = plist;
6334 }
6335 *listtop = NULL;
6336 }
6337
6338 /*----------------------------------------------------------------------*/
6339 /* Free memory allocated for netlist */
6340 /*----------------------------------------------------------------------*/
6341
freenetlist(objectptr cschem)6342 void freenetlist(objectptr cschem)
6343 {
6344 PolylistPtr *plist;
6345 LabellistPtr *llist;
6346
6347 plist = &cschem->polygons;
6348 freepolylist(plist);
6349 llist = &cschem->labels;
6350 freelabellist(llist);
6351 }
6352
6353 /*----------------------------------------------------------------------*/
6354 /* Clear the "traversed" flag in all objects of the hierarchy. */
6355 /*----------------------------------------------------------------------*/
6356
cleartraversed_level(objectptr cschem,int level)6357 int cleartraversed_level(objectptr cschem, int level)
6358 {
6359 genericptr *cgen;
6360 objinstptr cinst;
6361 objectptr callobj, pschem;
6362
6363 /* Always call on the primary schematic */
6364 pschem = (cschem->schemtype == SECONDARY) ? cschem->symschem : cschem;
6365
6366 /* Recursively call cleartraversed() on all subobjects, a la gennetlist() */
6367 /* Use the parts list of the object, not the calls, because calls */
6368 /* may have been removed. */
6369
6370 if (level == HIERARCHY_LIMIT) return -1;
6371
6372 for (cgen = pschem->plist; cgen < pschem->plist + pschem->parts; cgen++) {
6373 if (IS_OBJINST(*cgen)) {
6374 cinst = TOOBJINST(cgen);
6375
6376 if (cinst->thisobject->symschem != NULL)
6377 callobj = cinst->thisobject->symschem;
6378 else
6379 callobj = cinst->thisobject;
6380
6381 /* Don't infinitely recurse if object is on its own schematic */
6382 /* However, we need to take a stab at more subtle recursion, too */
6383
6384 if (callobj != pschem)
6385 if (cleartraversed_level(callobj, level + 1) == -1)
6386 return -1;
6387 }
6388 }
6389 pschem->traversed = False;
6390
6391 return 0;
6392 }
6393
6394 /*----------------------------------------------------------------------*/
6395 /* This is the routine normally called, as it hides the "level" */
6396 /* argument tagging the level of recursion. */
6397 /*----------------------------------------------------------------------*/
6398
cleartraversed(objectptr cschem)6399 int cleartraversed(objectptr cschem) {
6400 return cleartraversed_level(cschem, 0);
6401 }
6402
6403 /*----------------------------------------------------------------------*/
6404 /* If any part of the netlist is invalid, destroy the entire netlist */
6405 /*----------------------------------------------------------------------*/
6406
checkvalid(objectptr cschem)6407 int checkvalid(objectptr cschem)
6408 {
6409 genericptr *cgen;
6410 objinstptr cinst;
6411 objectptr callobj, pschem;
6412
6413 /* If the object has been declared a non-network object, ignore it */
6414 if (cschem->schemtype == NONETWORK) return 0;
6415
6416 /* Always operate on the master schematic */
6417 pschem = (cschem->schemtype == SECONDARY) ? cschem->symschem : cschem;
6418
6419 /* Stop immediately if the netlist is invalid */
6420 if (pschem->valid == False) return -1;
6421
6422 /* Otherwise, recursively call checkvalid() on all subobjects. */
6423 /* Use the parts list of the object, not the calls, because calls */
6424 /* may have been removed. */
6425
6426 for (cgen = pschem->plist; cgen < pschem->plist + pschem->parts; cgen++) {
6427 if (IS_OBJINST(*cgen)) {
6428 cinst = TOOBJINST(cgen);
6429
6430 if (cinst->thisobject->symschem != NULL)
6431 callobj = cinst->thisobject->symschem;
6432 else
6433 callobj = cinst->thisobject;
6434
6435 /* Don't infinitely recurse if object is on its own schematic */
6436
6437 if (callobj == pschem) continue;
6438
6439 /* If there is a symbol, don't check its parts, but check if */
6440 /* its netlist has been checkvalid. */
6441
6442 if ((cinst->thisobject->symschem != NULL) &&
6443 (cinst->thisobject->labels == NULL) &&
6444 (cinst->thisobject->polygons == NULL) &&
6445 (cinst->thisobject->valid == False))
6446 return -1;
6447
6448 /* Recursive call on subschematic */
6449 if (checkvalid(callobj) == -1)
6450 return -1;
6451 }
6452 }
6453 return 0; /* All subnetlists and own netlist are valid */
6454 }
6455
6456 /*----------------------------------------------------------------------*/
6457 /* Free memory allocated to temporary labels generated for the netlist */
6458 /*----------------------------------------------------------------------*/
6459
freetemplabels(objectptr cschem)6460 void freetemplabels(objectptr cschem)
6461 {
6462 genericptr *cgen;
6463 objinstptr cinst;
6464 objectptr callobj;
6465
6466 /* Recursively call freetemplabels() on all subobjects, a la gennetlist() */
6467 /* Use the parts list of the object, not the calls, because calls */
6468 /* may have been removed. */
6469
6470 for (cgen = cschem->plist; cgen < cschem->plist + cschem->parts; cgen++) {
6471 if (IS_OBJINST(*cgen)) {
6472
6473 cinst = TOOBJINST(cgen);
6474 if (cinst->thisobject->symschem != NULL)
6475 callobj = cinst->thisobject->symschem;
6476 else
6477 callobj = cinst->thisobject;
6478
6479 /* Don't infinitely recurse if object is on its own schematic */
6480 if (callobj != cschem) freetemplabels(callobj);
6481
6482 /* Also free the temp labels of any associated symbol */
6483 if (cinst->thisobject->symschem != NULL) freetemplabels(cinst->thisobject);
6484 }
6485
6486 /* Free any temporary labels which have been created */
6487
6488 else if (IS_LABEL(*cgen)) {
6489 labelptr clab = TOLABEL(cgen);
6490 /* int tmpval = (int)(cgen - cschem->plist); (jdk) */
6491 if (clab->string->type != FONT_NAME) {
6492 genericptr *tgen;
6493
6494 clab = TOLABEL(cgen);
6495 freelabel(clab->string);
6496 free(clab);
6497 for (tgen = cgen + 1; tgen < cschem->plist + cschem->parts; tgen++)
6498 *(tgen - 1) = *tgen;
6499 cschem->parts--;
6500 cgen--;
6501 }
6502 }
6503 }
6504 }
6505
6506 /*----------------------------------------------------------------------*/
6507 /* Free memory allocated for netlists, ports, and calls */
6508 /*----------------------------------------------------------------------*/
6509
freenets(objectptr cschem)6510 void freenets(objectptr cschem)
6511 {
6512 CalllistPtr calls, cptr;
6513 PortlistPtr ports, pptr;
6514 genericptr *cgen;
6515 objinstptr cinst;
6516 objectptr callobj;
6517
6518 /* Recursively call freenets() on all subobjects, a la gennetlist() */
6519 /* Use the parts list of the object, not the calls, because calls */
6520 /* may have been removed. */
6521
6522 if (cschem->schemtype == PRIMARY || cschem->schemtype == SECONDARY ||
6523 (cschem->schemtype == SYMBOL && cschem->symschem == NULL)) {
6524 for (cgen = cschem->plist; cgen < cschem->plist + cschem->parts; cgen++) {
6525 if (IS_OBJINST(*cgen)) {
6526
6527 cinst = TOOBJINST(cgen);
6528 if (cinst->thisobject->symschem != NULL)
6529 callobj = cinst->thisobject->symschem;
6530 else
6531 callobj = cinst->thisobject;
6532
6533 /* Don't infinitely recurse if object is on its own schematic */
6534 if (callobj != cschem) freenets(callobj);
6535
6536 /* Also free the netlist of any associated symbol */
6537 if (cinst->thisobject->symschem != NULL) freenets(cinst->thisobject);
6538 }
6539 }
6540 }
6541
6542 /* Free the allocated structures for this object */
6543
6544 for (calls = cschem->calls; calls != NULL;) {
6545 cptr = calls->next;
6546 freecalls(calls);
6547 calls = cptr;
6548 }
6549 cschem->calls = NULL;
6550
6551 for (ports = cschem->ports; ports != NULL;) {
6552 pptr = ports->next;
6553 free(ports);
6554 ports = pptr;
6555 }
6556 cschem->ports = NULL;
6557
6558 freenetlist(cschem);
6559
6560 cschem->traversed = False;
6561 cschem->valid = False;
6562 freegenlist(cschem->highlight.netlist);
6563 cschem->highlight.netlist = NULL;
6564 cschem->highlight.thisinst = NULL;
6565 }
6566
6567 /*----------------------------------------------------------------------*/
6568 /* Free the global pin list */
6569 /*----------------------------------------------------------------------*/
6570
freeglobals()6571 void freeglobals()
6572 {
6573 LabellistPtr labellist, llist;
6574
6575 for (labellist = global_labels; labellist != NULL;) {
6576 llist = labellist->next;
6577
6578 /* Labels in the global list are temporary and must be deallocated */
6579 freelabel(labellist->label->string);
6580 free(labellist->label);
6581
6582 freegenlist((Genericlist *)labellist);
6583 labellist = llist;
6584 }
6585 global_labels = NULL;
6586 }
6587
6588 /*----------------------------------------------------------------------*/
6589 /* Get rid of locally-defined pin names */
6590 /*----------------------------------------------------------------------*/
6591
clearlocalpins(objectptr cschem)6592 void clearlocalpins(objectptr cschem)
6593 {
6594 NetnamePtr netnames, nextname;
6595
6596 for (netnames = cschem->netnames; netnames != NULL; ) {
6597 nextname = netnames->next;
6598 if (netnames->localpin != NULL) {
6599 freelabel(netnames->localpin);
6600 }
6601 free(netnames);
6602 netnames = nextname;
6603 }
6604 cschem->netnames = NULL;
6605 }
6606
6607 /*----------------------------------------------------------------------*/
6608 /* Handle lists of included files */
6609 /*----------------------------------------------------------------------*/
6610
6611 /*----------------------------------------------------------------------*/
6612 /* check_included() --- check if a file has already been included. */
6613 /* Check by inode instead of filename so that we cannot trick the */
6614 /* program by giving, e.g., one relative path and one full path. */
6615 /*----------------------------------------------------------------------*/
6616
check_included(char * filename)6617 Boolean check_included(char *filename)
6618 {
6619 struct stat filestatus;
6620 int numi;
6621
6622 if (stat(filename, &filestatus) == 0) {
6623 if (included_files == NULL) return FALSE;
6624 for (numi = 0; *(included_files + numi) != (ino_t)NULL; numi++) {
6625 if (*(included_files + numi) == filestatus.st_ino) return TRUE;
6626 }
6627 }
6628 return FALSE;
6629 }
6630
6631 /*----------------------------------------------------------------------*/
6632 /* append_included() --- update the list of included files by adding */
6633 /* the inode of filename to the list. */
6634 /*----------------------------------------------------------------------*/
6635
append_included(char * filename)6636 void append_included(char *filename)
6637 {
6638 struct stat filestatus;
6639 int numi;
6640
6641 if (stat(filename, &filestatus) == 0) {
6642
6643 if (included_files == NULL) {
6644 included_files = (ino_t *)malloc(2 * sizeof(ino_t));
6645 *included_files = filestatus.st_ino;
6646 *(included_files + 1) = (ino_t)NULL;
6647 }
6648 else {
6649 for (numi = 0; *(included_files + numi) != (ino_t)NULL; numi++);
6650
6651 included_files = (ino_t *)realloc(included_files,
6652 (++numi + 1) * sizeof(ino_t));
6653
6654 *(included_files + numi - 1) = filestatus.st_ino;
6655 *(included_files + numi) = (ino_t)NULL;
6656 }
6657 }
6658 else {
6659 Wprintf("Error: Cannot stat include file \"%s\"\n", filename);
6660 }
6661 }
6662
6663 /*----------------------------------------------------------------------*/
6664
free_included()6665 void free_included()
6666 {
6667 if (included_files != NULL) {
6668 free(included_files);
6669 included_files = NULL;
6670 }
6671 }
6672
6673 /*-------------------------------------------------------------------------*/
6674