Corrected an error in the PCB netlisting which could cause nets to
[xcircuit.git] / netlist.c
blob108c25db22410ed703ab310b57e2bb1913d834b4
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 /*----------------------------------------------------------------------*/
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <ctype.h>
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
30 #ifdef HAVE_PYTHON
31 #include <Python.h>
32 #endif
34 #ifndef _MSC_VER
35 #include <X11/Intrinsic.h>
36 #include <X11/StringDefs.h>
37 #endif
39 #ifdef TCL_WRAPPER
40 #include <tk.h>
41 #endif
43 /*----------------------------------------------------------------------*/
44 /* Local includes */
45 /*----------------------------------------------------------------------*/
47 #include "xcircuit.h"
48 #include "colordefs.h"
50 /*----------------------------------------------------------------------*/
51 /* Function prototype declarations */
52 /*----------------------------------------------------------------------*/
53 #include "prototypes.h"
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
63 /*----------------------------------------------------------------------*/
64 /* Externally declared global variables */
65 /*----------------------------------------------------------------------*/
67 extern Display *dpy;
69 /*----------------------------------------------------------------------*/
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;
79 LabellistPtr global_labels;
81 typedef struct _flatindex *fidxptr;
83 typedef struct _flatindex {
84 char *devname;
85 u_int index;
86 fidxptr next;
87 } flatindex; /* count for labeling devices in a flattened file */
89 flatindex *flatrecord = NULL;
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 */
95 #define EndPoint(n) (((n == 1) ? 1 : (int)(n - 1)))
96 #define NextPoint(n) (((n == 1) ? 0 : 1))
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. */
101 #define MATCH_EXACT 0
102 #define MATCH_SUBNETS 1
103 #define MATCH_SIZE 2
104 #define MATCH_OVERLAP 3
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 /*----------------------------------------------------------------------*/
112 #define ONDIST 4 /* "slack" in algorithm for almost-touching lines */
113 #define onsegment(a, b, x) (finddist(a, b, x) <= ONDIST)
115 /*--------------------------------------------------------------*/
116 /* d36a: Base 36 to string conversion */
117 /*--------------------------------------------------------------*/
119 char *d36a(int number)
121 static char bconv[10];
122 int i, locn, rem;
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');
133 return &bconv[i + 1];
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 /*--------------------------------------------------------------*/
146 int NameToPinLocation(objinstptr thisinst, char *pinname, int *x_ret, int *y_ret)
148 objectptr thisobj = thisinst->thisobject;
149 genericptr *pgen;
150 labelptr plab;
152 if (thisobj->schemtype == SECONDARY)
153 thisobj = thisobj->symschem;
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;
167 return -1;
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 /*--------------------------------------------------------------*/
177 void ReferencePosition(objinstptr thisinst, XPoint *thispoint, XPoint *refpoint)
179 /* objectptr thisobj = thisinst->thisobject; (jdk) */
180 Matrix locctm;
182 /* Translate pin position back to originating object */
184 UResetCTM(&locctm);
185 UPreMultCTM(&locctm, thisinst->position, thisinst->scale,
186 thisinst->rotation);
187 UTransformbyCTM(&locctm, thispoint, refpoint, 1);
190 /*--------------------------------------------------------------*/
191 /* Variant of NameToPinLocation(): Given a port number, find */
192 /* the pin associated with that port. */
193 /*--------------------------------------------------------------*/
195 labelptr PortToLabel(objinstptr thisinst, int portno)
197 labelptr plab;
198 objectptr thisobj = thisinst->thisobject;
199 PortlistPtr ports;
201 if ((thisobj->schemtype == SYMBOL) && (thisobj->symschem != NULL))
202 ports = thisobj->symschem->ports;
203 else
204 ports = thisobj->ports;
206 for (; ports != NULL; ports = ports->next) {
207 if (ports->portid == portno) {
208 plab = NetToLabel(ports->netid, thisobj); /* in the symbol! */
209 return plab;
212 return NULL;
215 /*--------------------------------------------------------------*/
216 /* This function is the same as PortToLocalPosition() but */
217 /* returns the point in the coordinate system of the parent. */
218 /*--------------------------------------------------------------*/
220 Boolean PortToPosition(objinstptr thisinst, int portno, XPoint *refpoint)
222 labelptr plab = PortToLabel(thisinst, portno);
223 if (plab)
224 ReferencePosition(thisinst, &(plab->position), refpoint);
226 return (plab != NULL) ? TRUE : FALSE;
229 /*----------------------------------------------------------------------*/
230 /* Get the hierarchy of the page stack and return as a string */
231 /*----------------------------------------------------------------------*/
233 Boolean getnexthier(pushlistptr stack, char **hierstr, objinstptr callinst,
234 Boolean canonical)
236 objectptr topobj, thisobj;
237 CalllistPtr calls;
238 /* char numstr[12]; (jdk) */
239 int hierlen, devlen;
240 char *devstr;
242 if (!stack) return False;
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;
249 else {
250 topobj = stack->thisinst->thisobject;
252 if (topobj->schemtype != PRIMARY && topobj->symschem != NULL)
253 topobj = topobj->symschem;
255 if (topobj->calls == NULL) {
256 if (topobj->schemtype == FUNDAMENTAL) {
257 /* Top level is a fundamental symbol */
258 return True;
260 else if ((updatenets(stack->thisinst, FALSE) <= 0) || (topobj->calls == NULL)) {
261 Wprintf("Error in generating netlists!");
262 return False;
267 thisobj = stack->thisinst->thisobject;
269 /* When we have "traveled" through both a symbol and its */
270 /* associated schematic, only append the prefix for the */
271 /* first one encountered. */
274 if (thisobj->symschem != NULL && stack->next != NULL &&
275 thisobj->symschem == stack->next->thisinst->thisobject)
276 return True;
279 if ((thisobj->calls == NULL) && (thisobj->schemtype != PRIMARY)
280 && (thisobj->symschem != NULL))
281 thisobj = thisobj->symschem;
283 /* Check for resolved device indices and generate them if necessary */
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;
293 /* Add this level of the hierarchy to the prefix string */
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;
305 else {
306 hierlen = strlen(*hierstr) + 2;
307 *hierstr = realloc(*hierstr, hierlen + devlen);
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;
321 return True;
324 /*----------------------------------------------------------------------*/
326 char *GetHierarchy(pushlistptr *stackptr, Boolean canonical)
328 Boolean pushed_top = FALSE;
329 char *snew = NULL;
331 if ((*stackptr) && ((*stackptr)->thisinst != areawin->topinstance)) {
332 pushed_top = TRUE;
333 push_stack(stackptr, areawin->topinstance, NULL);
336 getnexthier(*stackptr, &snew, NULL, canonical);
338 if (pushed_top) pop_stack(stackptr);
340 return snew;
343 /*----------------------------------------------------------------------*/
344 /* Invalidate a netlist. The invalidation must be referred to the */
345 /* master schematic, if the object is a secondary schematic. */
346 /*----------------------------------------------------------------------*/
348 void invalidate_netlist(objectptr thisobject)
350 if (thisobject->schemtype != NONETWORK) {
351 if (thisobject->schemtype == SECONDARY)
352 thisobject->symschem->valid = False;
353 else
354 thisobject->valid = False;
358 /*--------------------------------------------------------------*/
359 /* Check if the selected items are relevant to the netlist. */
360 /* Only invalidate the current netlist if they are. */
361 /*--------------------------------------------------------------*/
363 void select_invalidate_netlist()
365 int i;
366 Boolean netcheck = FALSE;
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;
385 if (netcheck) invalidate_netlist(topobject);
388 /*------------------------------------------------------------------*/
389 /* Check proximity of two points (within roundoff tolerance ONDIST) */
390 /*------------------------------------------------------------------*/
392 Boolean proximity(XPoint *point1, XPoint *point2)
394 int dx, dy;
396 dx = point1->x - point2->x;
397 dy = point1->y - point2->y;
399 if ((abs(dx) < ONDIST) && (abs(dy) < ONDIST)) return True;
400 else return False;
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 /*----------------------------------------------------------------------*/
423 void createnets(objinstptr thisinst, Boolean quiet)
425 objectptr thisobject = thisinst->thisobject;
427 if (!setobjecttype(thisobject)) {
429 /* New in 3.3.32: Generating a netlist from a symbol is */
430 /* okay if the symbol has an associated netlist. */
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;
442 /* Wprintf("Generating netlists"); */ /* Diagnostic */
443 gennetlist(thisinst);
444 gencalls(thisobject);
445 cleartraversed(thisobject);
446 resolve_devnames(thisobject);
447 /* Wprintf("Finished netlists"); */
451 /*----------------------------------------------------------------------*/
452 /* Free all memory associated with the netlists. */
453 /*----------------------------------------------------------------------*/
455 void destroynets(objectptr thisobject)
457 objectptr pschem;
459 pschem = (thisobject->schemtype == SECONDARY) ? thisobject->symschem :
460 thisobject;
462 freetemplabels(pschem);
463 freenets(pschem);
464 freeglobals();
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 /*----------------------------------------------------------------------*/
476 Boolean nonnetwork(polyptr cpoly)
478 if (!(cpoly->style & UNCLOSED)) return True;
479 if (cpoly->style & (DASHED | DOTTED | FILLSOLID | BBOX))
480 return True;
481 return False;
484 /*----------------------------------------------------------------------*/
485 /* Return the largest (most negative) net number in the global netlist */
486 /*----------------------------------------------------------------------*/
488 int globalmax()
490 LabellistPtr gl;
491 int bidx, sbus;
492 buslist *lbus;
493 int smin = 0;
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;
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;
509 return smin;
512 /*----------------------------------------------------------------------*/
513 /* Return the largest net number in an object's netlist */
514 /*----------------------------------------------------------------------*/
516 int netmax(objectptr cschem)
518 PolylistPtr gp;
519 LabellistPtr gl;
520 int bidx, sbus;
521 buslist *lbus;
522 int smax = 0;
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;
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;
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;
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;
552 return smax;
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 /*----------------------------------------------------------------------*/
565 void gennetlist(objinstptr thisinst)
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;
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;
583 newlist.subnets = 0;
584 newlist.net.id = -1;
586 /* Determine the type of object being netlisted */
587 thisobject = thisinst->thisobject;
588 setobjecttype(thisobject);
590 if (thisobject->schemtype == NONETWORK) return;
592 /* Has this object been visited before? */
593 visited = ((thisobject->labels != NULL) || (thisobject->polygons != NULL)) ?
594 TRUE : FALSE;
596 if (!visited && (thisobject->schemtype == SYMBOL)
597 && (thisobject->symschem != NULL)) {
599 /* Make sure that schematics are netlisted before their symbols */
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;
608 else
609 gennetlist(xobjs.pagelist[n]->pageinst);
612 /* Sanity check on symbols with schematics: Are there any pins? */
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;
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);
628 /* If this is a secondary schematic, run on the primary (master) */
629 pschem = (thisobject->schemtype == SECONDARY) ? thisobject->symschem :
630 thisobject;
632 nextnet = netmax(pschem) + 1;
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. . . */
639 for (j = 0; j < xobjs.pages; j++) {
640 if (pschem->schemtype != PRIMARY) {
641 j = xobjs.pages;
642 cinst = thisinst;
643 cschem = thisobject;
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;
654 /* Determine the existing number of parts. We do not want to */
655 /* search over elements that we create in this routine. */
657 old_parts = cschem->parts;
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. */
663 if ((!visited) && (cschem->schemtype == PRIMARY ||
664 cschem->schemtype == SECONDARY ||
665 (cschem->schemtype == SYMBOL && cschem->symschem == NULL))) {
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);
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;
680 else
681 callinst = xobjs.pagelist[n]->pageinst;
683 else {
684 callobj = geninst->thisobject;
685 callinst = geninst;
688 /* object on its own schematic */
689 if (callobj == pschem) continue;
691 gennetlist(callinst);
693 /* Also generate netlist for pins in the corresponding symbol */
694 if (geninst->thisobject->symschem != NULL)
695 gennetlist(geninst);
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. */
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) {
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. */
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;
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. */
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;
734 else if (visited)
735 netlist = NULL;
736 else
737 /* Note any overlapping labels. */
738 netlist = pointtonet(cschem, cinst, &clab->position);
740 if (pschem->symschem != NULL && pschem->schemtype == SYMBOL) {
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. */
747 tmplist = pintonet(pschem->symschem, cinst, clab);
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. */
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;
768 else {
769 /* For schematics: Relate the pin to a network */
770 tmplist = pintonet(pschem, cinst, clab);
772 /* If only part of a bus has been identified, generate */
773 /* the net ID's of the parts that haven't been. */
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++;
784 if (clab->pin == LOCAL) {
785 if (tmplist != NULL) {
786 addpin(cschem, labinst, clab, tmplist);
787 if (netlist != NULL)
788 mergenets(pschem, netlist, tmplist);
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);
800 else {
801 tmplist = addpin(cschem, labinst, clab, netlist);
802 netlist = tmplist;
805 else {
806 tmplist = addpin(cschem, labinst, clab, netlist);
807 netlist = tmplist;
811 else if (clab->pin == GLOBAL) {
812 if (tmplist == NULL) {
813 tmplist = &newlist;
814 tmplist->net.id = globalmax() - 1;
816 addglobalpin(cschem, cinst, clab, tmplist);
817 addpin(cschem, labinst, clab, tmplist);
818 if (netlist != NULL)
819 mergenets(pschem, netlist, tmplist);
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;
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. */
837 if ((!visited) && (cschem->schemtype == PRIMARY ||
838 cschem->schemtype == SECONDARY ||
839 (cschem->schemtype == SYMBOL && cschem->symschem == NULL))) {
841 for (i = 0; i < old_parts; i++) {
842 cgen = cschem->plist + i;
843 if (IS_POLYGON(*cgen)) {
844 cpoly = TOPOLY(cgen);
846 /* Ignore non-network (closed, bbox, filled) polygons */
847 if (nonnetwork(cpoly)) continue;
849 resolved_net = (Genericlist *)NULL;
851 /* Check for attachment of each segment of this polygon */
852 /* to position of every recorded pin label. */
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)) {
865 if (resolved_net != NULL) {
866 if (mergenets(pschem, resolved_net, tmplist))
867 resolved_net = tmplist;
869 if (resolved_net == NULL) {
870 addpoly(cschem, cpoly, tmplist);
871 resolved_net = tmplist;
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;
882 /* Check for attachment of each segment of this polygon */
883 /* to endpoints of every recorded network polygon. */
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;
892 for (endpt = cpoly->points; endpt < cpoly->points
893 + EndPoint(cpoly->number); endpt++) {
894 endpt2 = endpt + NextPoint(cpoly->number);
896 if (onsegment(endpt, endpt2, tpt) ||
897 onsegment(endpt, endpt2, tpt2)) {
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;
905 if (resolved_net == NULL) {
906 addpoly(cschem, cpoly, tmplist);
907 resolved_net = tmplist;
912 /* Check for attachment of the endpoints of this polygon */
913 /* to each segment of every recorded network polygon. */
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);
921 if (onsegment(tpt, tpt2, endpt) ||
922 onsegment(tpt, tpt2, endpt2)) {
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;
930 if (resolved_net == 0) {
931 addpoly(cschem, cpoly, tmplist);
932 resolved_net = tmplist;
937 if (resolved_net == 0) {
939 /* This polygon belongs to an unvisited */
940 /* network. Give this polygon a new net */
941 /* number and add to the net list. */
943 newlist.net.id = nextnet++;
944 addpoly(cschem, cpoly, &newlist);
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 /*--------------------------------------------------------------*/
958 void search_on_siblings(objinstptr cinst, objinstptr isib, pushlistptr schemtop,
959 short llx, short lly, short urx, short ury)
961 XPoint *tmppts, sbbox[2];
962 int i;
963 labelptr olabel;
964 polyptr tpoly;
965 PolylistPtr pseek;
966 LabellistPtr lseek;
968 genericptr *iseek;
969 objinstptr subsibinst;
970 pushlistptr psearch, newlist;
971 objectptr sibling = isib->thisobject;
973 tmppts = (XPoint *)malloc(sizeof(XPoint));
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. */
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;
983 tmppts = (XPoint *)realloc(tmppts, sizeof(XPoint));
984 UTransformPoints(&(olabel->position), tmppts, 1,
985 isib->position, isib->scale, isib->rotation);
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);
993 searchconnect(tmppts, 1, cinst, lseek->subnets);
997 /* If the sibling is a schematic, we look at connections to pins and */
998 /* polygons, and recursively search subschematics of the sibling. */
1000 else {
1002 /* Look for pins connecting to pins in the object */
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);
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);
1016 searchconnect(tmppts, 1, cinst, lseek->subnets);
1019 /* Look for polygon ends connecting into the object */
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);
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);
1033 searchconnect(tmppts, tpoly->number, cinst, pseek->subnets);
1036 /* Recursively search all schematic children of the sibling */
1038 for (i = 0; i < sibling->parts; i++) {
1039 iseek = sibling->plist + i;
1040 if (IS_OBJINST(*iseek)) {
1042 /* objinstptr iinst = (objinstptr)iseek; (jdk) */
1044 /* Don't search this instance unless the bounding box */
1045 /* overlaps the bounding box of the calling object. */
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);
1053 if ((llx > sbbox[1].x) || (urx < sbbox[0].x) || (lly > sbbox[1].y)
1054 || (ury < sbbox[0].y))
1055 continue;
1057 subsibinst = TOOBJINST(iseek);
1059 /* push stack */
1060 newlist = (pushlistptr)malloc(sizeof(pushlist));
1061 newlist->thisinst = isib;
1062 newlist->next = schemtop;
1063 schemtop = newlist;
1065 search_on_siblings(cinst, subsibinst, schemtop, llx, lly, urx, ury);
1067 /* pop stack */
1069 newlist = schemtop;
1070 schemtop = schemtop->next;
1071 free(newlist);
1075 free(tmppts);
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 /*--------------------------------------------------------------*/
1091 void gencalls(objectptr thisobject)
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) */
1108 /* The netlist is always kept in the master schematic */
1109 pschem = (thisobject->schemtype == SECONDARY) ? thisobject->symschem :
1110 thisobject;
1112 pschem->traversed = True; /* This object has been dealt with */
1113 pschem->valid = True; /* This object has a valid netlist */
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) */
1119 for (j = 0; j < xobjs.pages; j++) {
1120 if (pschem->schemtype != PRIMARY) {
1121 j = xobjs.pages;
1122 cschem = thisobject;
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;
1133 for (i = 0; i < cschem->parts; i++) {
1134 cgen = cschem->plist + i;
1135 if (IS_OBJINST(*cgen)) {
1136 callinst = TOOBJINST(cgen);
1138 /* Determine where the hierarchy continues downward */
1140 if (callinst->thisobject->symschem != NULL)
1141 callobj = callinst->thisobject->symschem;
1142 else
1143 callobj = callinst->thisobject;
1145 /* Ignore any object on its own schematic */
1147 if (callobj == pschem) continue;
1149 callsymb = callinst->thisobject;
1151 /* Note: callobj is the next schematic in the hierarchy. */
1152 /* callsymb is the next visible object in the hierarchy, */
1153 /* which may be either a schematic or a symbol. */
1155 /*-------------------------------------------------------------*/
1156 /* For object instances which are their own schematics (i.e., */
1157 /* have netlist elements), don't rely on any pin list but make */
1158 /* a survey of how polygons connect into the object. */
1159 /*-------------------------------------------------------------*/
1161 if (callsymb->symschem == NULL
1162 && callobj->schemtype != FUNDAMENTAL
1163 && callobj->schemtype != TRIVIAL) {
1165 /* Fprintf(stdout, "*** Analyzing connections from %s"
1166 " to instance of %s\n", cschem->name,
1167 callinst->thisobject->name); */
1169 /* Look for pins connecting to pins in the object */
1171 for (lseek = pschem->labels; lseek != NULL; lseek = lseek->next) {
1172 if (lseek->cschem != cschem) continue;
1173 else if ((lseek->cinst != NULL) && (lseek->cinst != callinst))
1174 continue;
1175 olabel = lseek->label;
1176 searchconnect(&(olabel->position), 1, callinst, lseek->subnets);
1177 /* if we've encountered a unique instance, then con- */
1178 /* tinue past all other instances using this label. */
1179 if (lseek->cinst != NULL)
1180 while (lseek->next && (lseek->next->label == lseek->label))
1181 lseek = lseek->next;
1184 /* Look for polygon ends connecting into the object */
1186 for (pseek = pschem->polygons; pseek != NULL; pseek = pseek->next) {
1187 if (pseek->cschem != cschem) continue;
1188 tpoly = pseek->poly;
1189 searchconnect(tpoly->points, tpoly->number, callinst, pseek->subnets);
1192 /* For each call to a schematic or symbol which is NOT the */
1193 /* one under consideration, see if it touches or overlaps */
1194 /* the object under consideration. Search for connections */
1195 /* between the two objects. */
1197 calcinstbbox(cgen, &ibllx, &iblly, &iburx, &ibury);
1199 /* Only need to look forward from the current position. */
1200 for (k = i + 1; k < cschem->parts; k++) {
1202 iseek = cschem->plist + k;
1203 if (IS_OBJINST(*iseek)) {
1204 calcinstbbox(iseek, &sbllx, &sblly, &sburx, &sbury);
1206 /* Check intersection of the two object instances; */
1207 /* don't do a search if they are disjoint. */
1209 if ((ibllx <= sburx) && (iburx >= sbllx) &&
1210 (iblly <= sbury) && (ibury >= sblly)) {
1211 isib = TOOBJINST(iseek);
1212 search_on_siblings(callinst, isib, NULL,
1213 ibllx, iblly, iburx, ibury);
1219 /*----------------------------------------------------------*/
1220 /* Recursively call gencalls() on the schematic. */
1221 /*----------------------------------------------------------*/
1223 if (callobj->traversed == False)
1224 gencalls(callobj);
1226 /*----------------------------------------------------------*/
1227 /* Create a call to the object callsymb from object cschem */
1228 /*----------------------------------------------------------*/
1230 addcall(cschem, callobj, callinst);
1232 /*----------------------------------------------------------*/
1233 /* Search again on symbol pins to generate calls to ports. */
1234 /*----------------------------------------------------------*/
1236 UResetCTM(&locctm);
1237 UPreMultCTM(&locctm, callinst->position, callinst->scale,
1238 callinst->rotation);
1240 for (lseek = callsymb->labels; lseek != NULL; lseek = lseek->next) {
1241 /* LabellistPtr slab; (jdk) */
1242 /* labelptr slabel; (jdk) */
1244 if (lseek->cschem != callsymb) continue;
1245 else if ((lseek->cinst != NULL) && (lseek->cinst != callinst))
1246 continue;
1248 olabel = lseek->label;
1249 netto = (Genericlist *)lseek;
1251 /* Translate pin position back to object cschem */
1252 UTransformbyCTM(&locctm, &(olabel->position), &xpos, 1);
1254 /* What net in the calling object connects to this point? */
1255 netfrom = pointtonet(cschem, callinst, &xpos);
1257 /* If there's no net, we make one */
1258 if (netfrom == NULL)
1259 netfrom = make_tmp_pin(cschem, callinst, &xpos, netto);
1261 /* Generate a port call for a global signal if the */
1262 /* global label appears in the symbol (9/29/04). This */
1263 /* is a change from previous behavior, which was to not */
1264 /* make the call. */
1266 if ((netto->subnets == 0) && (netto->net.id < 0))
1267 mergenets(pschem, netfrom, netto);
1269 /* Attempt to generate a port in the object. */
1270 addport(callobj, netto);
1272 /* Generate the call to the port */
1273 if (addportcall(pschem, netfrom, netto) == FALSE) {
1275 // If object is "dot" then copy the bus from the
1276 // net to the dot. The dot takes on whatever
1277 // dimension the bus is.
1279 if (strstr(callobj->name, "::dot") != NULL) {
1280 copy_bus(netto, netfrom);
1282 else {
1283 Fprintf(stderr, "Error: attempt to connect bus size "
1284 "%d in %s to bus size %d in %s\n",
1285 netfrom->subnets, cschem->name,
1286 netto->subnets, callobj->name);
1290 /* if we've encountered a unique instance, then continue */
1291 /* past all other instances using this label. */
1292 if (lseek->cinst != NULL)
1293 while (lseek->next && (lseek->next->label == lseek->label))
1294 lseek = lseek->next;
1297 /*----------------------------------------------------------*/
1298 /* If after all that, no ports were called, then remove the */
1299 /* call to this object instance. However, we should check */
1300 /* for info labels, because the device may produce output */
1301 /* for the netlist even if it has no declared ports. This */
1302 /* is irrespective of the presence of pin labels---if the */
1303 /* symbol is "trivial", the netlist connections are */
1304 /* resolved in the level of the hierarchy above, and there */
1305 /* may be no ports, but the call list must retain the call */
1306 /* to ensure that the netlist output is generated. */
1307 /*----------------------------------------------------------*/
1309 if (pschem->calls->ports == NULL)
1310 if (pschem->infolabels == FALSE)
1311 removecall(pschem, pschem->calls); /* Remove the call */
1317 /*----------------------------------------------------------------------*/
1318 /* Translate a net number down in the calling hierarchy */
1319 /*----------------------------------------------------------------------*/
1321 int translatedown(int rnet, int portid, objectptr nextobj)
1323 PortlistPtr nport;
1324 int downnet = 0;
1326 for (nport = nextobj->ports; nport != NULL; nport = nport->next) {
1327 if (nport->portid == portid) {
1328 downnet = nport->netid;
1329 break;
1332 return downnet;
1335 /*----------------------------------------------------------------------*/
1336 /* Translate a netlist up in the calling hierarchy */
1337 /* */
1338 /* This routine creates a new netlist header which needs to be freed */
1339 /* by the calling routine. */
1340 /* */
1341 /* Note that if the entire netlist cannot be translated up, then the */
1342 /* routine returns NULL. This could be modified to return the part of */
1343 /* the network that can be translated up. */
1344 /*----------------------------------------------------------------------*/
1346 Genericlist *translateup(Genericlist *rlist, objectptr thisobj,
1347 objectptr nextobj, objinstptr nextinst)
1349 PortlistPtr nport;
1350 CalllistPtr ccall;
1351 int portid = 0;
1352 int upnet = 0;
1353 int rnet, lbus;
1354 buslist *sbus;
1355 Genericlist *tmplist;
1357 tmplist = (Genericlist *)malloc(sizeof(Genericlist));
1358 tmplist->subnets = 0;
1359 tmplist->net.id = 0;
1360 copy_bus(tmplist, rlist);
1362 for (lbus = 0;; ) {
1363 if (rlist->subnets == 0)
1364 rnet = rlist->net.id;
1365 else {
1366 sbus = rlist->net.list + lbus;
1367 rnet = sbus->netid;
1369 for (nport = nextobj->ports; nport != NULL; nport = nport->next) {
1370 if (nport->netid == rnet) {
1371 portid = nport->portid;
1372 break;
1376 upnet = 0;
1377 for (ccall = thisobj->calls; ccall != NULL; ccall = ccall->next) {
1378 if (ccall->callinst == nextinst) {
1379 for (nport = ccall->ports; nport != NULL; nport = nport->next) {
1380 if (nport->portid == portid) {
1381 upnet = nport->netid;
1382 break;
1385 if (nport != NULL) break;
1388 if (upnet == 0) {
1389 freegenlist(tmplist);
1390 return NULL;
1392 else {
1393 if (tmplist->subnets == 0) {
1394 tmplist->net.id = upnet;
1396 else {
1397 sbus = tmplist->net.list + lbus;
1398 sbus->netid = upnet;
1399 sbus->subnetid = getsubnet(upnet, thisobj);
1402 if (++lbus >= rlist->subnets) break;
1404 return tmplist;
1407 /*----------------------------------------------------------------------*/
1408 /* Check whether the indicated polygon is already resolved into the */
1409 /* netlist of the object hierarchy described by seltop. */
1410 /* Return the netlist if resolved, NULL otherwise. The netlist */
1411 /* returned is referred (translated) upward through the calling */
1412 /* hierarchy to the topmost object containing that net or those nets. */
1413 /* This topmost object is returned in parameter topobj. */
1414 /* Note that the netlist returned does not necessarily correspond to */
1415 /* any object in the top level. It is allocated and must be freed by */
1416 /* the calling routine. */
1417 /*----------------------------------------------------------------------*/
1419 Genericlist *is_resolved(genericptr *rgen, pushlistptr seltop, objectptr *topobj)
1421 objectptr thisobj = seltop->thisinst->thisobject;
1422 objectptr pschem;
1423 PolylistPtr pseek;
1424 LabellistPtr lseek;
1425 Genericlist *rlist = NULL, *newlist;
1427 pschem = (thisobj->schemtype == SECONDARY) ? thisobj->symschem : thisobj;
1429 /* Recursively call self, since we have to back out from the bottom of */
1430 /* the stack. */
1432 if (seltop->next != NULL) {
1433 rlist = is_resolved(rgen, seltop->next, topobj);
1435 /* Translate network ID up the hierarchy to the topmost object in which */
1436 /* the network exists. */
1438 if (rlist != NULL) {
1439 newlist = translateup(rlist, pschem, seltop->next->thisinst->thisobject,
1440 seltop->next->thisinst);
1441 if (newlist == NULL)
1442 /* Net does not exist upwards of this object. Pass net ID */
1443 /* upward with "topobj" unchanged. */
1444 return rlist;
1445 else {
1446 freegenlist(rlist);
1447 rlist = newlist;
1451 else {
1453 /* Find the net ID for the listed object, which should be in the object */
1454 /* on the bottom of the pushlist stack. */
1456 if (IS_POLYGON(*rgen)) {
1457 for (pseek = pschem->polygons; pseek != NULL; pseek = pseek->next) {
1458 if (pseek->poly == TOPOLY(rgen)) {
1459 rlist = (Genericlist *)pseek;
1460 break;
1464 else if (IS_LABEL(*rgen)) {
1465 for (lseek = pschem->labels; lseek != NULL; lseek = lseek->next) {
1466 if (lseek->label == TOLABEL(rgen)) {
1467 rlist = (Genericlist *)lseek;
1468 break;
1473 if (rlist != NULL) {
1474 /* Make a copy of the netlist header from this element */
1475 newlist = (Genericlist *)malloc(sizeof(Genericlist));
1476 newlist->subnets = 0;
1477 copy_bus(newlist, rlist);
1478 rlist = newlist;
1482 /* done: (jdk) */
1483 *topobj = (rlist == NULL) ? NULL : seltop->thisinst->thisobject;
1484 return rlist;
1487 /*--------------------------------------------------------------*/
1488 /* Highlight all the polygons and pin labels in a network */
1489 /* (recursively, downward). Pin labels are drawn only on the */
1490 /* topmost schematic object. */
1491 /* Returns true if some part of the hierarchy declared a net to */
1492 /* be highlighted. */
1493 /* mode = 1 highlight, mode = 0 erase */
1494 /*--------------------------------------------------------------*/
1496 Boolean highlightnet(objectptr cschem, objinstptr cinst, int netid, u_char mode)
1498 CalllistPtr calls;
1499 PortlistPtr ports;
1500 PolylistPtr plist;
1501 LabellistPtr llist;
1502 polyptr cpoly;
1503 labelptr clabel;
1504 objinstptr ccinst;
1505 int netto, locnetid, lbus;
1506 int curcolor = AUXCOLOR;
1507 Boolean rval = FALSE;
1508 objectptr pschem;
1510 SetForeground(dpy, areawin->gc, curcolor);
1512 pschem = (cschem->schemtype == SECONDARY) ? cschem->symschem : cschem;
1514 for (plist = pschem->polygons; plist != NULL; plist = plist->next) {
1515 if (plist->cschem != cschem) continue;
1516 cpoly = plist->poly;
1517 for (lbus = 0;;) {
1518 if (plist->subnets == 0)
1519 locnetid = plist->net.id;
1520 else
1521 locnetid = (plist->net.list + lbus)->netid;
1522 if (locnetid == netid) {
1523 /* Fprintf(stdout, " >> Found polygon belonging to net %d at (%d, %d)\n",
1524 locnetid, cpoly->points[0].x, cpoly->points[0].y); */
1525 if (mode == 0 && cpoly->color != curcolor) {
1526 curcolor = cpoly->color;
1527 XTopSetForeground(curcolor);
1529 UDrawPolygon(cpoly, xobjs.pagelist[areawin->page]->wirewidth);
1530 break;
1532 if (++lbus >= plist->subnets) break;
1536 /* Highlight labels if they belong to the top-level object */
1538 if (cschem == topobject) {
1539 for (llist = pschem->labels; llist != NULL; llist = llist->next) {
1540 if (llist->cschem != cschem) continue;
1541 else if ((llist->cinst != NULL) && (llist->cinst != cinst)) continue;
1542 clabel = llist->label;
1543 for (lbus = 0;;) {
1544 if (llist->subnets == 0)
1545 locnetid = llist->net.id;
1546 else
1547 locnetid = (llist->net.list + lbus)->netid;
1548 if (locnetid == netid) {
1549 if (clabel->string->type == FONT_NAME) { /* don't draw temp labels */
1550 if ((mode == 0) && (clabel->color != curcolor)) {
1551 curcolor = clabel->color;
1552 UDrawString(clabel, curcolor, cinst);
1554 else
1555 UDrawString(clabel, DOFORALL, cinst);
1556 /* Fprintf(stdout, " >> Found label belonging to net "
1557 "%d at (%d, %d)\n",
1558 locnetid, clabel->position.x,
1559 clabel->position.y); */
1562 break;
1564 if (++lbus >= llist->subnets) break;
1566 /* if we've encountered a unique instance, then continue */
1567 /* past all other instances using this label. */
1568 if (llist->cinst != NULL)
1569 while (llist->next && (llist->next->label == llist->label))
1570 llist = llist->next;
1573 /* Highlight all pins connecting this net to symbols */
1577 /* Connectivity recursion */
1579 for (calls = pschem->calls; calls != NULL; calls = calls->next) {
1580 if (calls->cschem != cschem) continue;
1581 for (ports = calls->ports; ports != NULL; ports = ports->next) {
1582 if (ports->netid == netid) {
1583 ccinst = calls->callinst;
1585 /* Recurse only on objects for which network polygons are visible */
1586 /* from the calling object: i.e., non-trivial, non-fundamental */
1587 /* objects acting as their own schematics. */
1589 UPushCTM();
1590 UPreMultCTM(DCTM, ccinst->position, ccinst->scale, ccinst->rotation);
1592 if (ccinst->thisobject->symschem == NULL &&
1593 ccinst->thisobject->schemtype != FUNDAMENTAL &&
1594 ccinst->thisobject->schemtype != TRIVIAL) {
1596 netto = translatedown(netid, ports->portid, calls->callobj);
1598 /* Fprintf(stdout, " > Calling object %s at (%d, %d)\n",
1599 calls->callobj->name, ccinst->position.x, ccinst->position.y); */
1600 /* Fprintf(stdout, " > Net translation from %d to %d (port %d)\n",
1601 netid, netto, ports->portid); */
1603 if (highlightnet(calls->callobj, calls->callinst, netto, mode))
1604 rval = TRUE;
1606 else {
1607 /* Otherwise (symbols, fundamental, trivial, etc., objects), we */
1608 /* highlight the pin position of the port. */
1609 if ((clabel = PortToLabel(ccinst, ports->portid)))
1610 UDrawXDown(clabel);
1612 UPopCTM();
1616 return rval;
1619 /*----------------------------------------------------------------------*/
1620 /* Highlight whatever nets are listed in the current object instance, */
1621 /* if any. */
1622 /*----------------------------------------------------------------------*/
1624 void highlightnetlist(objectptr nettop, objinstptr cinst, u_char mode)
1626 int lbus, netid;
1627 buslist *sbus;
1628 Genericlist *netlist = cinst->thisobject->highlight.netlist;
1629 objinstptr nextinst = cinst->thisobject->highlight.thisinst;
1631 if (netlist == NULL) return;
1633 for (lbus = 0;; ) {
1634 if (netlist->subnets == 0)
1635 netid = netlist->net.id;
1636 else {
1637 sbus = netlist->net.list + lbus;
1638 netid = sbus->netid;
1640 highlightnet(nettop, nextinst, netid, mode);
1641 if (++lbus >= netlist->subnets) break;
1644 /* If we are erasing, remove the netlist entry from the object */
1645 if (mode == (u_char)0) {
1646 freegenlist(netlist);
1647 cinst->thisobject->highlight.netlist = NULL;
1648 cinst->thisobject->highlight.thisinst = NULL;
1652 /*----------------------------------------------------------------------*/
1653 /* Push the matrix stack for each object (instance) until the indicated */
1654 /* object is reached. This works similarly to highlightnet() above, */
1655 /* but makes calls according to the hierarchy described by the */
1656 /* pushlistptr parameter. */
1657 /* Returns the number of stack objects to pop after we're done. */
1658 /*----------------------------------------------------------------------*/
1660 int pushnetwork(pushlistptr seltop, objectptr nettop)
1662 pushlistptr cursel = seltop;
1663 objinstptr sinst;
1664 int rno = 0;
1666 while ((cursel->thisinst->thisobject != nettop) && (cursel->next != NULL)) {
1667 cursel = cursel->next;
1668 sinst = cursel->thisinst;
1669 UPushCTM();
1670 UPreMultCTM(DCTM, sinst->position, sinst->scale, sinst->rotation);
1671 rno++;
1674 if (cursel->thisinst->thisobject != nettop) {
1675 Fprintf(stderr, "Error: object does not exist in calling stack!\n");
1676 rno = 0;
1679 return rno;
1682 /*----------------------------------------------------------------------*/
1683 /* Determine if two netlists match. If "mode" is MATCH_EXACT, the */
1684 /* net numbers and subnet numbers must all be the same. If */
1685 /* MATCH_SUBNETS, then the subnet numbers must be the same. If */
1686 /* MATCH_SIZE, then they need only have the same number of subnets. */
1687 /*----------------------------------------------------------------------*/
1689 Boolean match_buses(Genericlist *list1, Genericlist *list2, int mode)
1691 int i;
1692 buslist *bus1, *bus2;
1694 if (list1->subnets != list2->subnets) {
1695 // A wire (no subnets) matches a bus of 1 subnet. All others
1696 // are non-matching.
1698 if (list1->subnets != 0 && list2->subnets != 0)
1699 return FALSE;
1700 else if (list1->subnets != 1 && list2->subnets != 1)
1701 return FALSE;
1704 if (mode == MATCH_SIZE) return TRUE;
1706 if (list1->subnets == 0) {
1707 if (mode == MATCH_SUBNETS) return TRUE;
1708 if (list2->subnets != 0) {
1709 bus2 = list2->net.list + 0;
1710 if (list1->net.id != bus2->netid) return FALSE;
1712 else if (list1->net.id != list2->net.id) return FALSE;
1714 else if (list2->subnets == 0) {
1715 if (mode == MATCH_SUBNETS) return TRUE;
1716 bus1 = list1->net.list + 0;
1717 if (bus1->netid != list2->net.id) return FALSE;
1719 else {
1720 for (i = 0; i < list1->subnets; i++) {
1721 bus1 = list1->net.list + i;
1722 bus2 = list2->net.list + i;
1723 /* A subnetid of < 0 indicates an unassigned bus */
1724 if ((bus1->subnetid != -1) && (bus1->subnetid != bus2->subnetid))
1725 return FALSE;
1727 if (mode == MATCH_SUBNETS) return TRUE;
1729 for (i = 0; i < list1->subnets; i++) {
1730 bus1 = list1->net.list + i;
1731 bus2 = list2->net.list + i;
1732 if (bus1->netid != bus2->netid)
1733 return FALSE;
1736 return TRUE;
1739 /*----------------------------------------------------------------------*/
1740 /* Copy the netlist structure from one netlist element to another */
1741 /*----------------------------------------------------------------------*/
1743 void copy_bus(Genericlist *dest, Genericlist *source)
1745 buslist *sbus, *dbus;
1746 int i;
1748 if (dest->subnets > 0)
1749 free(dest->net.list);
1751 dest->subnets = source->subnets;
1752 if (source->subnets == 0)
1753 dest->net.id = source->net.id;
1754 else {
1755 dest->net.list = (buslist *)malloc(dest->subnets * sizeof(buslist));
1756 for (i = 0; i < dest->subnets; i++) {
1757 sbus = source->net.list + i;
1758 dbus = dest->net.list + i;
1759 dbus->netid = sbus->netid;
1760 dbus->subnetid = sbus->subnetid;
1765 /*------------------------------------------------------*/
1766 /* Create a new "temporary" label object for a pin */
1767 /* This type of label is never drawn, so it doesn't */
1768 /* need font info. It is identified as "temporary" by */
1769 /* this lack of a leading font record. */
1770 /*------------------------------------------------------*/
1772 Genericlist *new_tmp_pin(objectptr cschem, XPoint *pinpt, char *pinstring,
1773 char *prefix, Genericlist *netlist)
1775 labelptr *newlabel;
1776 stringpart *strptr;
1778 if (pinpt == NULL) {
1779 Fprintf(stderr, "NULL label location!\n");
1780 return NULL;
1783 NEW_LABEL(newlabel, cschem);
1784 labeldefaults(*newlabel, LOCAL, pinpt->x, pinpt->y);
1785 (*newlabel)->anchor = 0;
1786 (*newlabel)->color = DEFAULTCOLOR;
1787 strptr = (*newlabel)->string;
1788 strptr->type = TEXT_STRING;
1789 if (pinstring != NULL) {
1790 strptr->data.string = (char *)malloc(strlen(pinstring));
1791 strcpy(strptr->data.string, pinstring);
1793 else {
1794 strptr->data.string = textprintnet(prefix, NULL, netlist);
1797 /* Add label to object's netlist and return a pointer to the */
1798 /* netlist entry. */
1800 return (addpin(cschem, NULL, *newlabel, netlist));
1803 /*------------------------------------------------------*/
1804 /* Create a label for use in the list of global nets. */
1805 /* This label is like a temporary label (new_tmp_pin) */
1806 /* except that it is not represented in any object. */
1807 /* The string contains the verbatim contents of any */
1808 /* parameter substitutions in the original label. */
1809 /*------------------------------------------------------*/
1811 labelptr new_global_pin(labelptr clabel, objinstptr cinst)
1813 labelptr newlabel;
1815 newlabel = (labelptr) malloc(sizeof(label));
1816 newlabel->type = LABEL;
1817 labeldefaults(newlabel, GLOBAL, 0, 0);
1818 newlabel->anchor = 0;
1819 newlabel->color = DEFAULTCOLOR;
1820 free(newlabel->string);
1821 newlabel->string = stringcopyall(clabel->string, cinst);
1823 /* Add label to the global netlist and return a pointer to */
1824 /* the netlist entry. */
1826 return newlabel;
1829 /*----------------------------------------------------------------------*/
1830 /* Create a temporary I/O pin (becomes part of netlist and also part of */
1831 /* the object itself). */
1832 /*----------------------------------------------------------------------*/
1834 Genericlist *make_tmp_pin(objectptr cschem, objinstptr cinst, XPoint *pinpt,
1835 Genericlist *sublist)
1837 LabellistPtr lseek;
1838 objectptr pschem;
1839 char *pinstring = NULL;
1840 /* buslist *sbus; (jdk) */
1841 /* int lbus; (jdk) */
1842 Genericlist *netlist, *tmplist, newlist;
1844 newlist.subnets = 0;
1845 newlist.net.id = 0;
1847 /* Primary schematic (contains the netlist) */
1848 pschem = (cschem->schemtype == SECONDARY) ? cschem->symschem : cschem;
1850 /* Determine a netlist for this pin */
1852 netlist = pointtonet(cschem, cinst, pinpt);
1853 if (netlist == NULL) {
1854 newlist.net.id = netmax(pschem) + 1;
1855 netlist = &newlist;
1858 /* If there is any other pin at this location, don't make another */
1859 /* one. If there is already a temporary pin associated with the */
1860 /* net, use its name. */
1862 else {
1863 for (lseek = pschem->labels; lseek != NULL; lseek = lseek->next) {
1864 if (lseek->cschem != cschem) continue;
1865 else if ((lseek->cinst != NULL) && (lseek->cinst != cinst)) continue;
1866 tmplist = (Genericlist *)lseek;
1867 if (match_buses(netlist, tmplist, MATCH_EXACT)) {
1868 if (proximity(&(lseek->label->position), pinpt))
1869 return (Genericlist *)lseek;
1870 else if (lseek->label->string->type == TEXT_STRING)
1871 pinstring = lseek->label->string->data.string;
1873 /* if we've encountered a unique instance, then continue past */
1874 /* all other instances using this label. */
1875 if (lseek->cinst != NULL)
1876 while (lseek->next && (lseek->next->label == lseek->label))
1877 lseek = lseek->next;
1880 return (new_tmp_pin(cschem, pinpt, pinstring, "ext", netlist));
1883 /*--------------------------------------------------------------*/
1884 /* Search for connections into a non-symbol subcircuit, based */
1885 /* on various combinations of polygon and pin label overlaps. */
1886 /*--------------------------------------------------------------*/
1888 int searchconnect(XPoint *points, int number, objinstptr cinst, int subnets)
1890 XPoint *tmppts, *tpt, *tpt2, *endpt, *endpt2, *pinpt, opinpt;
1891 objinstptr tinst;
1892 genericptr *cgen;
1893 polyptr tpoly;
1894 labelptr tlab;
1895 objectptr tobj, cobj = cinst->thisobject;
1896 LabellistPtr tseek;
1897 PolylistPtr pseek;
1898 int i; /* , lbus, sub_bus; (jdk) */
1899 int found = 0;
1901 /* Generate temporary polygon in the coordinate system of */
1902 /* the object instance in question */
1904 tmppts = (XPoint *)malloc(number * sizeof(XPoint));
1905 InvTransformPoints(points, tmppts, number,
1906 cinst->position, cinst->scale, cinst->rotation);
1907 /* Fprintf(stdout, "Info: translated polygon w.r.t. object %s\n", */
1908 /* cinst->thisobject->name); */
1910 /* Recursion on all appropriate sub-schematics. */
1911 /* (Use parts list, not call list, as call list may not have created yet) */
1913 for (i = 0; i < cobj->parts; i++) {
1914 cgen = cobj->plist + i;
1915 if (IS_OBJINST(*cgen)) {
1916 tinst = TOOBJINST(cgen);
1917 if (tinst->thisobject->symschem == NULL) {
1918 tobj = tinst->thisobject;
1919 if (tobj->schemtype != FUNDAMENTAL && tobj->schemtype != TRIVIAL)
1920 found += searchconnect(tmppts, number, tinst, subnets);
1925 for (endpt = tmppts; endpt < tmppts + EndPoint(number); endpt++) {
1926 endpt2 = endpt + NextPoint(number);
1927 for (i = 0; i < cobj->parts; i++) {
1928 cgen = cobj->plist + i;
1929 if (!IS_OBJINST(*cgen)) continue;
1930 tinst = TOOBJINST(cgen);
1932 /* Look at the object only (symbol, or schematic if it has no symbol) */
1933 tobj = tinst->thisobject;
1935 /* Search for connections to pin labels */
1937 for (tseek = tobj->labels; tseek != NULL; tseek = tseek->next) {
1938 tlab = tseek->label;
1939 UTransformPoints(&(tlab->position), &opinpt, 1, tinst->position,
1940 tinst->scale, tinst->rotation);
1941 if (onsegment(endpt2, endpt, &opinpt)) {
1942 /* Fprintf(stdout, "%s connects to pin %s of %s in %s\n", */
1943 /* ((number > 1) ? "Polygon" : "Pin"), */
1944 /* tlab->string + 2, tinst->thisobject->name, */
1945 /* cinst->thisobject->name); */
1946 make_tmp_pin(cobj, cinst, &opinpt, (Genericlist *)tseek);
1947 found += (tseek->subnets == 0) ? 1 : tseek->subnets;
1948 break;
1953 for (pseek = cobj->polygons; pseek != NULL; pseek = pseek->next) {
1954 tpoly = pseek->poly;
1956 /* Search for connections from segments passed to this */
1957 /* function to endpoints of polygons in the netlist. */
1959 pinpt = NULL;
1960 tpt = tpoly->points;
1961 tpt2 = tpoly->points + tpoly->number - 1;
1962 if (onsegment(endpt2, endpt, tpt)) pinpt = tpt;
1963 if (onsegment(endpt2, endpt, tpt2)) pinpt = tpt2;
1965 /* Create new pinlabel (only if there is not one already there) */
1967 if (pinpt != NULL) {
1968 make_tmp_pin(cobj, cinst, pinpt, (Genericlist *)pseek);
1969 found += (pseek->subnets == 0) ? 1 : pseek->subnets;
1974 endpt = tmppts;
1975 endpt2 = tmppts + EndPoint(number) - 1;
1977 /* Search for connections from endpoints passed to this */
1978 /* function to segments of polygons in the netlist. */
1980 for (pseek = cobj->polygons; pseek != NULL; pseek = pseek->next) {
1982 tpoly = pseek->poly;
1983 for (tpt = tpoly->points; tpt < tpoly->points
1984 + EndPoint(tpoly->number); tpt++) {
1985 tpt2 = tpt + NextPoint(tpoly->number);
1987 pinpt = NULL;
1988 if (onsegment(tpt2, tpt, endpt)) pinpt = endpt;
1989 if (onsegment(tpt2, tpt, endpt2)) pinpt = endpt2;
1991 /* Create new pinlabel (only if there is not one already there) */
1993 if (pinpt != NULL) {
1994 make_tmp_pin(cobj, cinst, pinpt, (Genericlist *)pseek);
1995 found += (pseek->subnets == 0) ? 1 : pseek->subnets;
1999 free(tmppts);
2000 return(found);
2003 /*----------------------------------------------------------------------*/
2004 /* Associate polygon with given netlist and add to the object's list */
2005 /* of network polygons (i.e., wires). */
2006 /*----------------------------------------------------------------------*/
2008 Genericlist *addpoly(objectptr cschem, polyptr poly, Genericlist *netlist)
2010 PolylistPtr newpoly;
2011 objectptr pschem;
2012 /* buslist *sbus; (jdk) */
2013 /* int lbus, sub_bus; (jdk) */
2015 /* Netlist is in the master schematic */
2016 pschem = (cschem->schemtype == SECONDARY) ? cschem->symschem : cschem;
2018 /* If this polygon is already in the list, then add an extra subnet */
2019 /* if necessary. */
2021 for (newpoly = pschem->polygons; newpoly != NULL; newpoly = newpoly->next) {
2022 if (newpoly->poly == poly) {
2023 if (!match_buses((Genericlist *)newpoly, netlist, MATCH_EXACT)) {
2024 Fprintf(stderr, "addpoly: Error in bus assignment\n");
2025 return NULL;
2027 return (Genericlist *)newpoly;
2031 /* Create a new entry and link to polygon list of this object */
2033 newpoly = (PolylistPtr) malloc(sizeof(Polylist));
2034 newpoly->cschem = cschem;
2035 newpoly->poly = poly;
2036 newpoly->subnets = 0;
2037 copy_bus((Genericlist *)newpoly, netlist);
2038 newpoly->next = pschem->polygons;
2039 pschem->polygons = newpoly;
2041 return (Genericlist *)newpoly;
2044 /*-------------------------------------------------------------------------*/
2046 long zsign(long a, long b)
2048 if (a > b) return 1;
2049 else if (a < b) return -1;
2050 else return 0;
2053 /*----------------------------------------------------------------------*/
2054 /* Promote a single net to a bus. The bus size will be equal to the */
2055 /* value "subnets". */
2056 /*----------------------------------------------------------------------*/
2058 void promote_net(objectptr cschem, Genericlist *netfrom, int subnets)
2060 Genericlist *netref = NULL;
2061 CalllistPtr calls;
2062 PortlistPtr ports;
2063 PolylistPtr plist;
2064 LabellistPtr llist;
2065 int netid, firstid, lbus; /* curid, (jdk) */
2066 buslist *sbus;
2067 Boolean foundlabel;
2069 /* If no promotion is required, don't do anything */
2070 if (netfrom->subnets == subnets) return;
2072 /* It "netfrom" is already a bus, but of different size than */
2073 /* subnets, then it cannot be changed. */
2075 if (netfrom->subnets != 0) {
2076 Fprintf(stderr, "Attempt to change the size of a bus!\n");
2077 return;
2080 netid = netfrom->net.id;
2082 /* If "subnets" is 1, then "netfrom" can be promoted regardless. */
2083 /* Otherwise, if the "netfrom" net is used in any calls, then it */
2084 /* cannot be promoted. */
2086 if (subnets > 1) {
2087 for (calls = cschem->calls; calls != NULL; calls = calls->next)
2088 for (ports = calls->ports; ports != NULL; ports = ports->next)
2089 if (ports->netid == netid) {
2090 Fprintf(stderr, "Cannot promote net to bus: Net already connected"
2091 " to single-wire port\n");
2092 return;
2094 firstid = netmax(cschem) + 1;
2097 for (plist = cschem->polygons; plist != NULL; plist = plist->next)
2098 if ((plist->subnets == 0) && (plist->net.id == netid)) {
2099 plist->subnets = subnets;
2100 plist->net.list = (buslist *)malloc(subnets * sizeof(buslist));
2101 for (lbus = 0; lbus < subnets; lbus++) {
2102 sbus = plist->net.list + lbus;
2103 sbus->netid = (lbus == 0) ? netid : firstid + lbus;
2104 sbus->subnetid = lbus; /* By default, number from zero */
2106 netref = (Genericlist *)plist;
2109 /* It's possible for a label without bus notation to be attached */
2110 /* to this net. */
2112 foundlabel = FALSE;
2113 for (llist = cschem->labels; llist != NULL; llist = llist->next)
2114 if ((llist->subnets == 0) && (llist->net.id == netid)) {
2115 llist->subnets = subnets;
2116 llist->net.list = (buslist *)malloc(subnets * sizeof(buslist));
2117 for (lbus = 0; lbus < subnets; lbus++) {
2118 sbus = llist->net.list + lbus;
2119 sbus->netid = (lbus == 0) ? netid : firstid + lbus;
2120 sbus->subnetid = lbus; /* By default, number from zero */
2122 netref = (Genericlist *)llist;
2123 foundlabel = TRUE;
2126 /* We need to create a temp label associated with this net to */
2127 /* encompass the promoted bus size. If this bus is later attached */
2128 /* to a known bus, that bus name will be canonical. If no bus name */
2129 /* is ever assigned to the bus, this temp one will be used. */
2131 if (!foundlabel) {
2132 XPoint *pinpos;
2133 pinpos = NetToPosition(netid, cschem);
2134 new_tmp_pin(cschem, pinpos, NULL, "int", netref);
2138 /*----------------------------------------------------------------------*/
2139 /* Change any part of the netlist "testlist" that matches the net IDs */
2140 /* of "orignet" to the net IDs of "newnet". It is assumed that the */
2141 /* lengths and sub-bus numbers of "orignet" and "newnet" match. */
2142 /*----------------------------------------------------------------------*/
2144 Boolean mergenetlist(objectptr cschem, Genericlist *testlist,
2145 Genericlist *orignet, Genericlist *newnet)
2147 int obus, onetid, osub, nsub, nnetid, tbus;
2148 buslist *sbus;
2149 Boolean rval = FALSE;
2151 for (obus = 0;;) {
2152 if (orignet->subnets == 0) {
2153 onetid = orignet->net.id;
2154 osub = -1;
2156 else {
2157 sbus = orignet->net.list + obus;
2158 onetid = sbus->netid;
2159 osub = sbus->subnetid;
2162 if (newnet->subnets == 0) {
2163 nnetid = newnet->net.id;
2164 nsub = -1;
2166 else {
2167 sbus = newnet->net.list + obus;
2168 nnetid = sbus->netid;
2169 nsub = sbus->subnetid;
2172 if (testlist->subnets == 0) {
2173 if (testlist->net.id == onetid) {
2174 rval = TRUE;
2175 if (orignet->subnets == 0) {
2176 testlist->net.id = nnetid;
2177 return TRUE;
2179 else {
2180 /* Promote testlist to a bus subnet of size 1 */
2181 testlist->subnets = 1;
2182 testlist->net.list = (buslist *)malloc(sizeof(buslist));
2183 sbus = testlist->net.list;
2184 sbus->netid = nnetid;
2185 sbus->subnetid = nsub;
2186 return rval;
2191 for (tbus = 0; tbus < testlist->subnets; tbus++) {
2192 /* If the sub-bus numbers match, then change the net */
2193 /* ID to the new net number. If the sub-bus is */
2194 /* unnamed (has no associated bus-notation label), */
2195 /* then it can take on the net and subnet numbers. */
2197 sbus = testlist->net.list + tbus;
2199 if (sbus->netid == onetid) {
2200 if (sbus->subnetid == osub) {
2201 sbus->netid = nnetid;
2202 sbus->subnetid = nsub;
2203 rval = TRUE;
2205 else {
2206 labelptr blab = NetToLabel(nnetid, cschem);
2207 if (blab == NULL) {
2208 Fprintf(stderr, "Warning: isolated subnet?\n");
2209 sbus->netid = nnetid;
2210 /* Keep subnetid---but, does newnet need to be promoted? */
2211 return TRUE;
2213 else if (blab->string->type != FONT_NAME) {
2214 sbus->netid = nnetid;
2215 sbus->subnetid = nsub;
2216 rval = TRUE;
2217 Fprintf(stderr, "Warning: Unexpected subnet value in mergenetlist!\n");
2222 if (++obus >= orignet->subnets) break;
2224 return rval;
2227 /*----------------------------------------------------------------------*/
2228 /* Combine two networks in an object's linked-list Netlist */
2229 /* Parameters: cschem - pointer to object containing netlist */
2230 /* orignet - original netlist to be changed */
2231 /* newnet - new netlist to be changed to */
2232 /* */
2233 /* Attempts to merge different subnets in a bus are thwarted. */
2234 /*----------------------------------------------------------------------*/
2236 Boolean netmerge(objectptr cschem, Genericlist *orignet, Genericlist *newnet)
2238 PolylistPtr plist;
2239 LabellistPtr llist;
2240 CalllistPtr calls;
2241 PortlistPtr ports;
2242 Genericlist savenet;
2243 int i, net;
2244 buslist *obus, *nbus;
2245 Boolean rval;
2247 /* Trivial case; do nothing */
2248 if (match_buses(orignet, newnet, MATCH_EXACT)) return TRUE;
2250 /* Disallow an attempt to convert a global net to a local net: */
2251 /* The global net ID always dominates! */
2253 if ((orignet->subnets == 0) && (newnet->subnets == 0) &&
2254 (orignet->net.id < 0) && (newnet->net.id > 0)) {
2255 int globnet = orignet->net.id;
2256 orignet->net.id = newnet->net.id;
2257 newnet->net.id = globnet;
2260 /* Check that the lists of changes are compatible. It appears to be */
2261 /* okay to have non-matching buses (although they should not be */
2262 /* merged), as this is a valid style in which buses do not have taps */
2263 /* but the sub-buses are explicitly called out with labels. In such */
2264 /* case, the polygons of different sub-buses touch, and this routine */
2265 /* rejects them as being incompatible. */
2267 if (!match_buses(orignet, newnet, MATCH_SUBNETS)) {
2268 if (match_buses(orignet, newnet, MATCH_SIZE)) {
2269 labelptr nlab;
2270 /* If only the subnet numbers don't match up, check if */
2271 /* "orignet" has a temp label. */
2272 nbus = orignet->net.list;
2273 if ((nlab = NetToLabel(nbus->netid, cschem)) != NULL) {
2274 if (nlab->string->type != FONT_NAME)
2275 goto can_merge;
2278 else
2279 Fprintf(stderr, "netmerge warning: non-matching bus subnets touching.\n");
2280 return FALSE;
2283 can_merge:
2285 /* If orignet is a bus size 1 and newnet is a wire, then promote */
2286 /* newnet to a bus size 1. */
2288 if (orignet->subnets == 1 && newnet->subnets == 0) {
2289 net = newnet->net.id;
2290 newnet->subnets = 1;
2291 newnet->net.list = (buslist *)malloc(sizeof(buslist));
2292 obus = orignet->net.list;
2293 nbus = newnet->net.list;
2294 nbus->subnetid = obus->subnetid;
2295 nbus->netid = net;
2298 /* Make a copy of the net so we don't overwrite it */
2299 savenet.subnets = 0;
2300 copy_bus(&savenet, orignet);
2302 rval = FALSE;
2303 for (plist = cschem->polygons; plist != NULL; plist = plist->next)
2304 if (mergenetlist(cschem, (Genericlist *)plist, &savenet, newnet))
2305 rval = TRUE;
2307 for (llist = cschem->labels; llist != NULL; llist = llist->next)
2308 if (mergenetlist(cschem, (Genericlist *)llist, &savenet, newnet)) {
2309 int pinnet;
2310 char *newtext;
2311 rval = TRUE;
2313 /* Because nets that have been merged away may be re-used later, */
2314 /* change the name of any temporary labels to the new net number */
2316 if (llist->label->string->type != FONT_NAME) {
2317 newtext = llist->label->string->data.string;
2318 if (sscanf(newtext + 3, "%d", &pinnet) == 1) {
2319 if (pinnet == savenet.net.id) {
2320 *(newtext + 3) = '\0';
2321 llist->label->string->data.string = textprintnet(newtext,
2322 NULL, newnet);
2323 free(newtext);
2329 if (rval) {
2331 /* Reflect the net change in the object's call list, if it has one. */
2333 for (calls = cschem->calls; calls != NULL; calls = calls->next) {
2334 for (ports = calls->ports; ports != NULL; ports = ports->next) {
2335 if (newnet->subnets == 0) {
2336 if (ports->netid == savenet.net.id)
2337 ports->netid = newnet->net.id;
2339 else {
2340 for (i = 0; i < newnet->subnets; i++) {
2341 obus = savenet.net.list + i;
2342 nbus = newnet->net.list + i;
2343 if (ports->netid == obus->netid)
2344 ports->netid = nbus->netid;
2351 /* Free the copy of the bus that we made, if we made one */
2352 if (savenet.subnets > 0) free(savenet.net.list);
2354 return rval;
2357 /*----------------------------------------------------------------------*/
2358 /* Wrapper to netmerge() to make sure change is made both to the symbol */
2359 /* and schematic, if both exist. */
2360 /*----------------------------------------------------------------------*/
2362 Boolean mergenets(objectptr cschem, Genericlist *orignet, Genericlist *newnet)
2364 Boolean merged;
2366 if (cschem->symschem != NULL)
2367 merged = netmerge(cschem->symschem, orignet, newnet);
2368 if (netmerge(cschem, orignet, newnet))
2369 merged = TRUE;
2371 return merged;
2374 /*----------------------------------------------------------------------*/
2375 /* Remove a call to an object instance from the call list of cschem */
2376 /*----------------------------------------------------------------------*/
2378 void removecall(objectptr cschem, CalllistPtr dontcallme)
2380 CalllistPtr lastcall, seeklist;
2381 PortlistPtr ports, savelist;
2383 /* find the instance call before this one and link it to the one following */
2385 lastcall = NULL;
2386 for (seeklist = cschem->calls; seeklist != NULL; seeklist = seeklist->next) {
2387 if (seeklist == dontcallme)
2388 break;
2389 lastcall = seeklist;
2392 if (seeklist == NULL) {
2393 Fprintf(stderr, "Error in removecall(): Call does not exist!\n");
2394 return;
2397 if (lastcall == NULL)
2398 cschem->calls = dontcallme->next;
2399 else
2400 lastcall->next = dontcallme->next;
2402 ports = dontcallme->ports;
2403 while (ports != NULL) {
2404 savelist = ports;
2405 ports = ports->next;
2406 free (savelist);
2408 free(dontcallme);
2411 /*----------------------------------------------------------------------*/
2412 /* Add a pin label to the netlist */
2413 /* If cschem == NULL, add the pin to the list of global pins. */
2414 /* The new label entry in the netlist gets a copy of the netlist */
2415 /* "netlist". */
2416 /*----------------------------------------------------------------------*/
2418 Genericlist *addpin(objectptr cschem, objinstptr cinst, labelptr pin,
2419 Genericlist *netlist)
2421 LabellistPtr srchlab, newlabel, lastlabel = NULL;
2422 objectptr pschem;
2423 /* buslist *sbus; (jdk) */
2424 /* int lbus, sub_bus; (jdk) */
2426 pschem = (cschem->schemtype == SECONDARY) ? cschem->symschem : cschem;
2428 for (srchlab = pschem->labels; srchlab != NULL; srchlab = srchlab->next) {
2429 if (srchlab->label == pin) {
2430 if (!match_buses(netlist, (Genericlist *)srchlab, MATCH_EXACT)) {
2431 if (srchlab->cinst == cinst) {
2432 Fprintf(stderr, "addpin: Error in bus assignment\n");
2433 return NULL;
2436 else if (srchlab->cinst == NULL)
2437 return (Genericlist *)srchlab;
2438 break; /* Stop at the first record for this label */
2440 lastlabel = srchlab;
2443 /* Create a new entry and link to label list of the object */
2445 newlabel = (LabellistPtr) malloc(sizeof(Labellist));
2446 newlabel->cschem = cschem;
2447 newlabel->cinst = cinst;
2448 newlabel->label = pin;
2449 newlabel->subnets = 0;
2450 copy_bus((Genericlist *)newlabel, netlist);
2452 /* Always put the specific (instanced) cases in front of */
2453 /* generic cases for the same label. */
2455 /* Generic case---make it the last record for this label */
2456 if ((cinst == NULL) && (lastlabel != NULL)) {
2457 while ((srchlab != NULL) && (srchlab->label == pin)) {
2458 lastlabel = srchlab;
2459 srchlab = srchlab->next;
2462 if (lastlabel != NULL) {
2463 newlabel->next = srchlab;
2464 lastlabel->next = newlabel;
2466 else {
2467 newlabel->next = pschem->labels;
2468 pschem->labels = newlabel;
2470 return (Genericlist *)newlabel;
2473 /*----------------------------------------------------------------------*/
2474 /* Add a pin label to the list of global net names. */
2475 /* The new label entry in the netlist gets a copy of the netlist */
2476 /* "netlist" and a copy of label "pin" containing the *instance* value */
2477 /* of the text. */
2478 /*----------------------------------------------------------------------*/
2480 Genericlist *addglobalpin(objectptr cschem, objinstptr cinst, labelptr pin,
2481 Genericlist *netlist)
2483 LabellistPtr srchlab, newlabel, lastlabel = NULL;
2485 if (cinst == NULL) {
2486 Fprintf(stderr, "Error: Global pin does not have an associated instance!\n");
2487 return NULL;
2490 for (srchlab = global_labels; srchlab != NULL; srchlab = srchlab->next) {
2491 if (srchlab->label == pin) {
2492 if (!match_buses(netlist, (Genericlist *)srchlab, MATCH_EXACT)) {
2493 if (srchlab->cinst == cinst) {
2494 Fprintf(stderr, "addglobalpin: Error in bus assignment\n");
2495 return NULL;
2498 else if (srchlab->cinst == NULL)
2499 return (Genericlist *)srchlab;
2500 break; /* Stop at the first record for this label */
2502 lastlabel = srchlab;
2505 /* Create a new entry and link to label list of the object */
2507 newlabel = (LabellistPtr) malloc(sizeof(Labellist));
2508 newlabel->cschem = cschem;
2509 newlabel->cinst = cinst;
2510 newlabel->label = new_global_pin(pin, cinst);
2511 newlabel->subnets = 0;
2512 copy_bus((Genericlist *)newlabel, netlist);
2514 if (lastlabel != NULL) {
2515 newlabel->next = srchlab;
2516 lastlabel->next = newlabel;
2518 else {
2519 newlabel->next = global_labels;
2520 global_labels = newlabel;
2522 return (Genericlist *)newlabel;
2525 /*----------------------------------------------------------------------*/
2526 /* Allocate memory for the new call list element */
2527 /* Define the values for the new call list element */
2528 /* Insert new call into call list */
2529 /* The new call is at the beginning of the list. */
2530 /*----------------------------------------------------------------------*/
2532 void addcall(objectptr cschem, objectptr callobj, objinstptr callinst)
2534 CalllistPtr newcall;
2535 objectptr pschem;
2537 /* Netlist is on the master schematic */
2538 pschem = (cschem->schemtype == SECONDARY) ? cschem->symschem : cschem;
2540 newcall = (CalllistPtr) malloc(sizeof(Calllist));
2541 newcall->cschem = cschem;
2542 newcall->callobj = callobj;
2543 newcall->callinst = callinst;
2544 newcall->devindex = -1;
2545 newcall->devname = NULL;
2546 newcall->ports = NULL;
2547 newcall->next = pschem->calls;
2548 pschem->calls = newcall;
2551 /*----------------------------------------------------------------------*/
2552 /* Add a port to the object cschem which connects net "netto" to */
2553 /* the calling object. One port is created for each net ID in "netto" */
2554 /* (which may be a bus). The port contains the net ID in the called */
2555 /* object. The port may already exist, in which case this routine does */
2556 /* nothing. */
2557 /*----------------------------------------------------------------------*/
2559 void addport(objectptr cschem, Genericlist *netto)
2561 PortlistPtr newport, seekport;
2562 int portid = 0, netid, lbus;
2563 buslist *sbus;
2564 Boolean duplicate;
2566 for (lbus = 0;;) {
2568 if (netto->subnets == 0)
2569 netid = netto->net.id;
2570 else {
2571 sbus = netto->net.list + lbus;
2572 netid = sbus->netid;
2575 /* If a port already exists for this net, don't add another one! */
2577 duplicate = FALSE;
2578 for (seekport = cschem->ports; seekport != NULL; seekport = seekport->next) {
2579 if (seekport->netid != netid) {
2580 if (seekport->portid > portid)
2581 portid = seekport->portid;
2583 else
2584 duplicate = TRUE;
2587 if (!duplicate) {
2588 portid++;
2590 newport = (PortlistPtr)malloc(sizeof(Portlist));
2591 newport->netid = netid;
2592 newport->portid = portid;
2594 if (cschem->ports != NULL)
2595 newport->next = cschem->ports;
2596 else
2597 newport->next = NULL;
2599 cschem->ports = newport;
2601 if (++lbus >= netto->subnets) break;
2605 /*----------------------------------------------------------------------*/
2606 /* Add a specific port connection from object cschem into the instance */
2607 /* cinst. This equates a net number in the calling object cschem */
2608 /* (netfrom) to a net number in the object instance being called */
2609 /* (netto). */
2610 /* */
2611 /* If we attempt to connect a bus of one size to a port of a different */
2612 /* size, return FALSE. Otherwise, add the call and return TRUE. */
2613 /*----------------------------------------------------------------------*/
2615 Boolean addportcall(objectptr cschem, Genericlist *netfrom, Genericlist *netto)
2617 CalllistPtr ccall;
2618 PortlistPtr seekport, sp, newport;
2619 objectptr instobj;
2620 objinstptr cinst;
2621 int lbus, netid_from, netid_to;
2622 buslist *sbus, *tbus;
2623 Boolean duplicate;
2625 /* The call that we need to add a port to is the first on */
2626 /* the list for cschem, as created by addcall(). */
2627 ccall = cschem->calls;
2628 instobj = ccall->callobj;
2629 cinst = ccall->callinst;
2631 if (netfrom->subnets != netto->subnets) {
2632 if (netfrom->subnets == 0) {
2633 /* It is possible that "netfrom" is an unlabeled polygon that */
2634 /* is implicitly declared a bus by its connection to this */
2635 /* port. If so, we promote "netfrom" to a bus but set the */
2636 /* subnet entries to negative values, since we don't know what */
2637 /* they are yet. */
2638 promote_net(cschem, netfrom, netto->subnets);
2641 /* Only other allowable condition is a bus size 1 connecting into */
2642 /* a single-wire port. However, one should consider promotion of */
2643 /* the pin in the target object to a bus on a per-instance basis. */
2644 /* This has not yet been done. . . */
2646 else if ((netfrom->subnets != 1) || (netto->subnets != 0)) {
2647 /* Let the caller report error information, because it knows more */
2648 return FALSE;
2652 for (lbus = 0;;) {
2653 buslist bsingf, bsingo;
2654 Genericlist subnet_from, subnet_other;
2656 if (netfrom->subnets == 0) {
2657 netid_from = netfrom->net.id;
2659 subnet_from.subnets = 0;
2660 subnet_from.net.id = netid_from;
2662 subnet_other.subnets = 0;
2664 else {
2665 sbus = netfrom->net.list + lbus;
2666 netid_from = sbus->netid;
2668 subnet_from.subnets = 1;
2669 subnet_from.net.list = &bsingf;
2670 bsingf.netid = netid_from;
2671 bsingf.subnetid = sbus->subnetid;
2673 bsingo.subnetid = lbus;
2674 subnet_other.subnets = 1;
2675 subnet_other.net.list = &bsingo;
2677 if (netto->subnets == 0) {
2678 netid_to = netto->net.id;
2680 else {
2681 tbus = netto->net.list + lbus;
2682 netid_to = tbus->netid;
2685 /* Check the ports of the instance's object for the one matching */
2686 /* the "netto" net ID. */
2688 duplicate = FALSE;
2689 for (seekport = instobj->ports; seekport != NULL;
2690 seekport = seekport->next) {
2691 if (seekport->netid == netid_to) {
2693 /* If there is already an entry for this port, then */
2694 /* we may need to merge nets in cschem. */
2696 for (sp = ccall->ports; sp != NULL; sp = sp->next)
2697 if (sp->portid == seekport->portid) {
2698 if (sp->netid != netid_from) {
2699 if (netfrom->subnets == 0)
2700 subnet_other.net.id = sp->netid;
2701 else {
2702 bsingo.netid = sp->netid;
2703 bsingo.subnetid = getsubnet(bsingo.netid, cschem);
2705 if (!mergenets(cschem, &subnet_other, &subnet_from)) {
2706 /* Upon failure, see if we can merge the other */
2707 /* direction. */
2708 if (!mergenets(cschem, &subnet_from, &subnet_other))
2709 continue;
2710 else {
2711 if (subnet_from.subnets == 0)
2712 subnet_from.net.id = subnet_other.net.id;
2713 else {
2714 bsingf.netid = bsingo.netid;
2715 bsingf.subnetid = bsingo.subnetid;
2720 duplicate = TRUE;
2723 if (!duplicate) {
2724 newport = (PortlistPtr)malloc(sizeof(Portlist));
2725 newport->netid = netid_from;
2726 newport->portid = seekport->portid;
2727 newport->next = ccall->ports;
2728 ccall->ports = newport;
2730 break;
2733 if (++lbus >= netfrom->subnets) break;
2735 return TRUE;
2738 /*----------------------------------------------------------------------*/
2739 /* Find the net ID corresponding to the indicated port ID in the */
2740 /* indicated object. */
2741 /*----------------------------------------------------------------------*/
2743 int porttonet(objectptr cschem, int portno)
2745 PortlistPtr plist;
2747 for (plist = cschem->ports; plist != NULL; plist = plist->next) {
2748 if (plist->portid == portno)
2749 return plist->netid;
2751 return 0;
2754 /*----------------------------------------------------------------------*/
2755 /* Traverse netlist and return netid of polygon or pin on which the */
2756 /* indicated point is located. */
2757 /* If point is not on any polygon or does not match any pin position, */
2758 /* return NULL. */
2759 /* Labels which have a non-NULL "cinst" record are instance-dependent */
2760 /* and must match parameter "cinst". */
2761 /* */
2762 /* This routine checks to see if more than one net crosses the point */
2763 /* of interest, and merges the nets if found. */
2764 /* (the method has been removed and must be reinstated, presumably) */
2765 /*----------------------------------------------------------------------*/
2767 Genericlist *pointtonet(objectptr cschem, objinstptr cinst, XPoint *testpoint)
2769 XPoint *tpt, *tpt2;
2770 PolylistPtr ppoly;
2771 LabellistPtr plab;
2772 Genericlist *preturn;
2773 objectptr pschem; /* primary schematic */
2775 /* cschem is the object containing the point. However, if the object */
2776 /* is a secondary schematic, the netlist is located in the master. */
2778 pschem = (cschem->schemtype == SECONDARY) ? cschem->symschem : cschem;
2780 for (plab = pschem->labels; plab != NULL; plab = plab->next) {
2781 if (plab->cschem != cschem) continue;
2782 else if ((plab->cinst != NULL) && (plab->cinst != cinst)) continue;
2783 tpt = &(plab->label->position);
2784 if (proximity(tpt, testpoint))
2785 return (Genericlist *)plab;
2787 /* if we've encountered a unique instance, then continue past */
2788 /* all other instances using this label. */
2790 if (plab->cinst != NULL)
2791 while (plab->next && (plab->next->label == plab->label))
2792 plab = plab->next;
2795 /* Check against polygons. We use this part of the routine to see */
2796 /* if there are crossing wires on top of a port. If so, they are */
2797 /* merged together. */
2799 preturn = (Genericlist *)NULL;
2800 for (ppoly = pschem->polygons; ppoly != NULL; ppoly = ppoly->next) {
2801 if (ppoly->cschem != cschem) continue;
2802 for (tpt = ppoly->poly->points; tpt < ppoly->poly->points
2803 + EndPoint(ppoly->poly->number); tpt++) {
2804 tpt2 = tpt + NextPoint(ppoly->poly->number);
2806 if (onsegment(tpt, tpt2, testpoint)) {
2807 if (preturn == (Genericlist *)NULL)
2808 preturn = (Genericlist *)ppoly;
2809 else
2810 mergenets(pschem, (Genericlist *)ppoly, preturn);
2815 return preturn;
2818 /*----------------------------------------------------------------------*/
2819 /* localpin keeps track of temporary pin labels when flattening the */
2820 /* hierarchy without destroying the original pin names. */
2821 /*----------------------------------------------------------------------*/
2823 void makelocalpins(objectptr cschem, CalllistPtr clist, char *prefix)
2825 NetnamePtr netnames;
2826 PortlistPtr ports, plist;
2827 stringpart *locpin;
2828 int locnet, callnet;
2829 objectptr callobj = clist->callobj;
2831 /* Copy all net names which are passed from above through ports */
2833 for (ports = clist->ports; ports != NULL; ports = ports->next) {
2834 callnet = ports->netid;
2835 for (plist = callobj->ports; plist != NULL; plist = plist->next) {
2836 if (plist->portid == ports->portid) {
2837 locnet = plist->netid;
2838 locpin = nettopin(callnet, cschem, prefix);
2839 break;
2843 for (netnames = callobj->netnames; netnames != NULL; netnames = netnames->next)
2844 if (netnames->netid == locnet)
2845 break;
2847 if (netnames == NULL) {
2848 netnames = (NetnamePtr)malloc(sizeof(Netname));
2849 netnames->netid = locnet;
2850 netnames->localpin = stringcopy(locpin);
2851 netnames->next = callobj->netnames;
2852 callobj->netnames = netnames;
2857 /*----------------------------------------------------------------------*/
2858 /* Find the first point associated with the net "netid" in the object */
2859 /* cschem. */
2860 /*----------------------------------------------------------------------*/
2862 XPoint *NetToPosition(int netid, objectptr cschem)
2864 PolylistPtr plist;
2865 LabellistPtr llist;
2866 buslist *sbus;
2867 int lbus, locnetid; /* sub_bus, (jdk) */
2869 plist = cschem->polygons;
2870 for (; plist != NULL; plist = plist->next) {
2871 for (lbus = 0;;) {
2872 if (plist->subnets == 0) {
2873 locnetid = plist->net.id;
2875 else {
2876 sbus = plist->net.list + lbus;
2877 locnetid = sbus->netid;
2879 if (locnetid == netid) {
2880 return plist->poly->points;
2882 if (++lbus >= plist->subnets) break;
2886 llist = (netid < 0) ? global_labels : cschem->labels;
2887 for (; llist != NULL; llist = llist->next) {
2888 for (lbus = 0;;) {
2889 if (llist->subnets == 0) {
2890 locnetid = llist->net.id;
2892 else {
2893 sbus = llist->net.list + lbus;
2894 locnetid = sbus->netid;
2896 if (locnetid == netid) {
2897 return (&(llist->label->position));
2899 if (++lbus >= llist->subnets) break;
2902 return NULL;
2905 /*----------------------------------------------------------------------*/
2906 /* Find a label element for the given net number. In a symbol, this */
2907 /* will be the label representing the pin (or the first one found, if */
2908 /* the pin has multiple labels). If no label is found, return NULL. */
2909 /* Preferably choose a non-temporary label, if one exists */
2910 /*----------------------------------------------------------------------*/
2912 labelptr NetToLabel(int netid, objectptr cschem)
2914 LabellistPtr llist;
2915 labelptr standby = NULL;
2916 buslist *sbus;
2917 int lbus, locnetid;
2919 llist = (netid < 0) ? global_labels : cschem->labels;
2921 for (; llist != NULL; llist = llist->next) {
2922 for (lbus = 0;;) {
2923 if (llist->subnets == 0) {
2924 locnetid = llist->net.id;
2926 else {
2927 sbus = llist->net.list + lbus;
2928 locnetid = sbus->netid;
2930 if (locnetid == netid) {
2931 if (llist->label->string->type == FONT_NAME)
2932 return llist->label;
2933 else if (standby == NULL)
2934 standby = llist->label;
2936 if (++lbus >= llist->subnets) break;
2939 return standby;
2943 /*----------------------------------------------------------------------*/
2944 /* Find the subnet number of the given net. This routine should be */
2945 /* used only as a last resort in case the subnet number is not */
2946 /* available; it has to look through the polygon and label lists in */
2947 /* detail to find the specified net ID. */
2948 /*----------------------------------------------------------------------*/
2950 int getsubnet(int netid, objectptr cschem)
2952 PolylistPtr plist;
2953 LabellistPtr llist;
2954 buslist *sbus;
2955 int lbus, sub_bus, locnetid;
2957 plist = cschem->polygons;
2958 for (; plist != NULL; plist = plist->next) {
2959 for (lbus = 0;;) {
2960 if (plist->subnets == 0) {
2961 locnetid = plist->net.id;
2962 sub_bus = -1;
2964 else {
2965 sbus = plist->net.list + lbus;
2966 locnetid = sbus->netid;
2967 sub_bus = sbus->subnetid;
2969 if (locnetid == netid) {
2970 return sub_bus;
2972 if (++lbus >= plist->subnets) break;
2976 llist = (netid < 0) ? global_labels : cschem->labels;
2977 for (; llist != NULL; llist = llist->next) {
2978 for (lbus = 0;;) {
2979 if (llist->subnets == 0) {
2980 locnetid = llist->net.id;
2981 sub_bus = -1;
2983 else {
2984 sbus = llist->net.list + lbus;
2985 locnetid = sbus->netid;
2986 sub_bus = sbus->subnetid;
2988 if (locnetid == netid) {
2989 return sub_bus;
2991 if (++lbus >= llist->subnets) break;
2994 return -1;
2997 /*----------------------------------------------------------------------*/
2998 /* Find a pin name for the given net number */
2999 /* Either take the last pin name with the net, or generate a new pin, */
3000 /* assigning it a net number for a string. */
3001 /* prefix = NULL indicates spice netlist, but is also used for PCB-type */
3002 /* netlists because the calling hierarchy is generated elsewhere. */
3003 /*----------------------------------------------------------------------*/
3005 stringpart *nettopin(int netid, objectptr cschem, char *prefix)
3007 NetnamePtr netname;
3008 labelptr pinlab;
3009 LabellistPtr netlabel;
3010 char *newtext, *snew = NULL;
3011 XPoint *pinpos;
3012 int locnetid; /* lbus, (jdk) */
3013 /* buslist *sbus; (jdk) */
3014 Genericlist newnet;
3015 static stringpart *newstring = NULL;
3017 /* prefix is NULL for hierarchical (spice) netlists and for */
3018 /* internal netlist manipulation. */
3020 if (prefix == NULL) {
3021 pinlab = NetToLabel(netid, cschem);
3022 if (pinlab != NULL) {
3024 /* If this is a "temp label", regenerate the name according to */
3025 /* the actual net number, if it does not match. This prevents */
3026 /* unrelated temp labels from getting the same name. */
3028 if (pinlab->string->type != FONT_NAME) {
3029 if (sscanf(pinlab->string->data.string + 3, "%d", &locnetid) == 1) {
3030 if (locnetid != netid) {
3031 newtext = pinlab->string->data.string;
3032 newtext[3] = '\0';
3033 newnet.subnets = 0;
3034 newnet.net.id = netid;
3035 pinlab->string->data.string = textprintnet(newtext, NULL, &newnet);
3036 free(newtext);
3040 return pinlab->string;
3042 else {
3043 /* If there's no label associated with this network, make one */
3044 /* called "intn" where n is the net number (netid). This is a */
3045 /* temp label (denoted by lack of a font specifier). */
3047 newnet.subnets = 0;
3048 newnet.net.id = netid;
3049 pinpos = NetToPosition(netid, cschem);
3050 netlabel = (LabellistPtr)new_tmp_pin(cschem, pinpos, NULL, "int", &newnet);
3051 return (netlabel) ? netlabel->label->string : NULL;
3055 /* Flattened (e.g., sim) netlists */
3057 for (netname = cschem->netnames; netname != NULL; netname = netname->next) {
3058 if (netname->netid == netid) {
3059 if (netname->localpin != NULL)
3060 return netname->localpin;
3061 break;
3065 /* Generate the string for the local instantiation of this pin */
3066 /* If this node does not have a pin, create one using the current */
3067 /* hierarchy prefix as the string. */
3069 pinlab = NetToLabel(netid, cschem);
3070 if (pinlab != NULL) {
3071 stringpart *tmpstring = pinlab->string;
3072 snew = textprint(tmpstring, NULL);
3074 else {
3075 snew = (char *)malloc(12);
3076 sprintf(snew, "int%d", netid);
3079 if (netid < 0) {
3080 newtext = snew;
3082 else {
3083 newtext = (char *)malloc(1 + strlen(snew) + strlen(prefix));
3084 sprintf(newtext, "%s%s", prefix, snew);
3085 free(snew);
3088 /* "newstring" is allocated only once and should not be free'd */
3089 /* by the calling routine. */
3091 if (newstring == NULL) {
3092 newstring = (stringpart *)malloc(sizeof(stringpart));
3093 newstring->nextpart = NULL;
3094 newstring->type = TEXT_STRING;
3096 else {
3097 free(newstring->data.string);
3099 newstring->data.string = newtext;
3100 return newstring;
3103 /*----------------------------------------------------------------------*/
3104 /* Find the net which connects to the given pin label */
3105 /* Return NULL (no net) if no match was found. */
3106 /*----------------------------------------------------------------------*/
3108 Genericlist *pintonet(objectptr cschem, objinstptr cinst, labelptr testpin)
3110 LabellistPtr seeklabel;
3111 int lbus, matched;
3112 buslist *sbus, *tbus;
3113 Genericlist netlist, *tmplist;
3115 /* check against local pins, if this pin is declared local */
3117 seeklabel = (testpin->pin == GLOBAL) ? global_labels : cschem->labels;
3119 netlist.subnets = 0;
3120 for (; seeklabel != NULL; seeklabel = seeklabel->next)
3121 if (!stringcomprelaxed(seeklabel->label->string, testpin->string, cinst))
3123 /* Quick simple case: no bus */
3124 if (seeklabel->subnets == 0)
3125 return (Genericlist *)seeklabel;
3127 tmplist = break_up_bus(testpin, cinst, (Genericlist *)seeklabel);
3129 /* It is necessary to be able to put together a netlist from */
3130 /* partial sources none of which may be complete. */
3132 if (tmplist != NULL) {
3133 if (netlist.subnets == 0) copy_bus(&netlist, tmplist);
3134 matched = 0;
3135 for (lbus = 0; lbus < tmplist->subnets; lbus++) {
3136 sbus = netlist.net.list + lbus;
3137 tbus = tmplist->net.list + lbus;
3138 if (sbus->netid == 0)
3139 sbus->netid = tbus->netid; /* copy forward */
3140 else if (tbus->netid == 0)
3141 tbus->netid = sbus->netid; /* copy back */
3142 if (sbus->netid != 0)
3143 matched++;
3145 if (matched == netlist.subnets) {
3146 free(netlist.net.list);
3147 return tmplist;
3152 if (netlist.subnets != 0) {
3153 free(netlist.net.list);
3154 return tmplist; /* Returns list with partial entries */
3156 return NULL; /* No matches found */
3159 #ifdef TCL_WRAPPER
3161 /*----------------------------------------------------------------------*/
3162 /* Automatic Schematic Generation stuff--- */
3163 /* Blow away all polygons in the schematic and replace them with a */
3164 /* simple rat's-nest (point-to-point). */
3165 /* */
3166 /* This routine is more of a proof-of-concept than a useful routine, */
3167 /* intended to be called only from the Tcl console. It is intended to */
3168 /* reveal most of the issues related to automatic schematic generation, */
3169 /* in which the netlist, or a portion of it, can be redrawn as objects */
3170 /* are moved around to maintain the relationships present in the */
3171 /* calls structure. */
3172 /*----------------------------------------------------------------------*/
3174 void ratsnest(objinstptr thisinst)
3176 CalllistPtr calls;
3177 PortlistPtr ports;
3178 PolylistPtr plist;
3179 LabellistPtr llist;
3180 objectptr pschem, cschem;
3181 objinstptr cinst;
3182 polyptr ppoly, *newpoly;
3183 buslist *sbus;
3184 int i, netid, points, sub_bus, lbus;
3185 XPoint portpos;
3186 Boolean result;
3188 cschem = thisinst->thisobject;
3189 pschem = (cschem->schemtype == SECONDARY) ? cschem->symschem : cschem;
3191 /* Go through the netlist and remove all polygons. Remove */
3192 /* the polygons from the object as well as from the netlist. */
3194 for (plist = pschem->polygons; plist != NULL; plist = plist->next) {
3195 ppoly = plist->poly;
3196 ppoly->type += REMOVE_TAG; /* tag for removal */
3198 freepolylist(&pschem->polygons);
3200 /* for each schematic (PRIMARY or SECONDARY), remove the tagged */
3201 /* elements. */
3203 for (i = 0; i < xobjs.pages; i++) {
3204 cinst = xobjs.pagelist[i]->pageinst;
3205 if (cinst && (cinst->thisobject->schemtype == SECONDARY) &&
3206 (cinst->thisobject->symschem == pschem)) {
3207 delete_tagged(cinst);
3209 else if (cinst == thisinst)
3210 delete_tagged(cinst);
3213 /* Now, for each net ID in the label list, run through the calls and */
3214 /* find all points in the calls connecting to that net. Link */
3215 /* them with as many polygons as needed. These polygons are point- */
3216 /* to-point lines, turning the schematic representation into a */
3217 /* "rat's-nest". */
3219 for (llist = pschem->labels; llist != NULL; llist = llist->next) {
3220 for (lbus = 0;;) {
3221 if (llist->subnets == 0) {
3222 netid = llist->net.id;
3223 sub_bus = -1;
3225 else {
3226 sbus = llist->net.list + lbus;
3227 netid = sbus->netid;
3228 sub_bus = sbus->subnetid;
3231 points = 0;
3232 for (calls = pschem->calls; calls != NULL; calls = calls->next) {
3234 /* Determine schematic object from the object called. Start a */
3235 /* new polygon any time we switch schematic pages. */
3237 if (calls->cschem != cschem)
3238 points = 0;
3239 cschem = calls->cschem;
3241 for (ports = calls->ports; ports != NULL; ports = ports->next) {
3242 if (ports->netid == netid) {
3244 /* Find the location of this port in the coordinates of cschem */
3246 result = PortToPosition(calls->callinst, ports->portid, &portpos);
3247 if (result == True) {
3248 points++;
3249 if (points == 1) {
3250 NEW_POLY(newpoly, cschem);
3251 polydefaults(*newpoly, 1, portpos.x, portpos.y);
3252 (*newpoly)->style |= UNCLOSED;
3253 (*newpoly)->color = RATSNESTCOLOR;
3254 addpoly(cschem, *newpoly, (Genericlist *)llist);
3256 else
3257 poly_add_point(*newpoly, &portpos);
3259 else {
3260 Fprintf(stderr, "Error: Cannot find pin connection in symbol!\n");
3261 /* Deal with error condition? */
3266 if (++lbus >= llist->subnets) break;
3270 /* Now, move all the labels to reconnect to their networks */
3272 /* (to be done) */
3274 /* Refresh the screen */
3275 drawarea(NULL, NULL, NULL);
3278 #endif
3280 /*----------------------------------------------------------------------*/
3281 /* Find a net or netlist in object cschem with the indicated name. */
3282 /* */
3283 /* This is the same routine as above, but finds the net with the given */
3284 /* name, or all nets which are a subset of the given name, if the name */
3285 /* is the name of a bus. Return NULL if no match was found. */
3286 /*----------------------------------------------------------------------*/
3288 Genericlist *nametonet(objectptr cschem, objinstptr cinst, char *netname)
3290 LabellistPtr seeklabel;
3291 stringpart newstring;
3293 /* Build a simple xcircuit string from "netname" (nothing malloc'd) */
3294 newstring.type = TEXT_STRING;
3295 newstring.nextpart = NULL;
3296 newstring.data.string = netname;
3298 /* Check against local networks */
3300 for (seeklabel = cschem->labels; seeklabel != NULL; seeklabel = seeklabel->next)
3301 if (!stringcomprelaxed(seeklabel->label->string, &newstring, cinst))
3302 return (Genericlist *)seeklabel;
3304 /* Check against global networks */
3306 for (seeklabel = global_labels; seeklabel != NULL; seeklabel = seeklabel->next)
3307 if (!stringcomprelaxed(seeklabel->label->string, &newstring, cinst))
3308 return (Genericlist *)seeklabel;
3310 return NULL;
3313 /*----------------------------------------------------------------------*/
3314 /* Check if two parts may have the same component index. This is true */
3315 /* for parts with parameterized pins, such as those in the "quadparts" */
3316 /* library. Compare the names of the ports in each instance. If none */
3317 /* match, then these are unique subparts of a single component, and we */
3318 /* should return FALSE. Otherwise, return TRUE. */
3319 /*----------------------------------------------------------------------*/
3321 Boolean samepart(CalllistPtr clist1, CalllistPtr clist2)
3323 PortlistPtr port;
3324 labelptr plab;
3325 char *pinname1, *pinname2;
3326 Boolean rvalue;
3328 if (clist1->callobj != clist2->callobj) return FALSE;
3330 rvalue = FALSE;
3331 for (port = clist1->ports; port != NULL; port = port->next) {
3332 plab = PortToLabel(clist1->callinst, port->portid);
3333 pinname1 = textprint(plab->string, clist1->callinst);
3334 pinname2 = textprint(plab->string, clist2->callinst);
3335 if (!strcmp(pinname1, pinname2)) rvalue = TRUE;
3336 free(pinname1);
3337 free(pinname2);
3339 return rvalue;
3342 /*----------------------------------------------------------------------*/
3343 /* Generate an index number for a device in a flat netlist format. */
3344 /* We ignore any values given to "index" (this is a to-do item, since */
3345 /* many schematics are only a single level of hierarchy, so it makes */
3346 /* sense to keep designated indices when possible), but do generate */
3347 /* independent index numbering for different device classes. */
3348 /*----------------------------------------------------------------------*/
3350 u_int devflatindex(char *devname)
3352 flatindex *fp = flatrecord;
3353 while (fp != NULL) {
3354 if (!strcmp(devname, fp->devname)) {
3355 return ++fp->index;
3357 fp = fp->next;
3359 fp = (flatindex *)malloc(sizeof(flatindex));
3360 fp->next = flatrecord;
3361 flatrecord = fp;
3362 fp->index = 1;
3363 fp->devname = devname;
3364 return 1;
3367 /*----------------------------------------------------------------------*/
3368 /* Free the list of per-class flat device indices */
3369 /*----------------------------------------------------------------------*/
3371 void freeflatindex()
3373 flatindex *fpnext, *fp = flatrecord;
3374 while (fp != NULL) {
3375 fpnext = fp->next;
3376 free(fp);
3377 fp = fpnext;
3379 flatrecord = NULL;
3382 /*---------------------------------------------------------*/
3383 /* Convert a number (up to 99999) to its base-36 equivalent */
3384 /*---------------------------------------------------------*/
3386 int convert_to_b36(int number)
3388 int b36idx, tmpidx;
3390 tmpidx = number;
3391 b36idx = (tmpidx / 10000) * 1679616;
3392 tmpidx %= 10000;
3393 b36idx += (tmpidx / 1000) * 46656;
3394 tmpidx %= 1000;
3395 b36idx += (tmpidx / 100) * 1296;
3396 tmpidx %= 100;
3397 b36idx += ((tmpidx / 10) * 36) + (tmpidx % 10);
3399 return b36idx;
3402 /*----------------------------------------------------------------------*/
3403 /* Generate an index number for this device. Count all devices having */
3404 /* the same device name (as printed in the netlist) in the calls of */
3405 /* the calling object (cfrom) */
3406 /* */
3407 /* Update (9/29/04): Remove any leading whitespace from the device */
3408 /* name to prevent treating, for example, "J?" and " J?" as separate */
3409 /* device types. */
3410 /* */
3411 /* Update (7/28/05): To allow SPICE device indices to include all */
3412 /* alphanumerics, we parse in base-36, and count in the subset of */
3413 /* base-36 that can be interpreted as decimal (1-9, 36-45, etc.) */
3414 /*----------------------------------------------------------------------*/
3416 u_int devindex(objectptr cfrom, CalllistPtr clist)
3418 CalllistPtr cptr, listfrom = cfrom->calls;
3419 objectptr devobj = clist->callobj;
3420 u_int *occupied, total, objindex, i, b36idx;
3421 char *devname, *cname;
3422 /* oparamptr ops; (jdk) */
3424 if (listfrom == NULL) return (u_int)0;
3425 if (clist->devindex >= 0) return clist->devindex;
3427 devname = (clist->devname == NULL) ? devobj->name : clist->devname;
3428 while (isspace(*devname)) devname++;
3430 /* Count total number of devices */
3431 for (cptr = listfrom, total = 0; cptr != NULL; cptr = cptr->next, total++);
3432 occupied = (u_int *)malloc(total * sizeof(u_int));
3433 objindex = 1;
3435 for (cptr = listfrom, total = 0; cptr != NULL; cptr = cptr->next, total++) {
3436 occupied[total] = 0;
3437 if (cptr == clist) continue;
3438 cname = (cptr->devname == NULL) ? cptr->callobj->name : cptr->devname;
3439 while (isspace(*cname)) cname++;
3440 if (!strcmp(cname, devname)) {
3441 occupied[total] = cptr->devindex;
3442 if (cptr->devindex == objindex) objindex++;
3446 b36idx = convert_to_b36(objindex);
3447 for (; objindex <= total; objindex++) {
3448 b36idx = convert_to_b36(objindex);
3449 for (i = 0; i < total; i++)
3450 if (occupied[i] == b36idx)
3451 break;
3452 if (i == total)
3453 break;
3455 free(occupied);
3457 clist->devindex = b36idx;
3458 return objindex;
3461 /*----------------------------------------------------------------------*/
3462 /* Generate and return a list of all info label prefixes in a design */
3463 /* This does not depend on the netlist being generated (i.e., does not */
3464 /* use CalllistPtr) */
3465 /*----------------------------------------------------------------------*/
3467 void genprefixlist(objectptr cschem, slistptr *modelist)
3469 int locpos;
3470 genericptr *pgen;
3471 labelptr plabel;
3472 objinstptr cinst;
3473 stringpart *strptr;
3474 slistptr modeptr;
3476 for (pgen = cschem->plist; pgen < cschem->plist + cschem->parts; pgen++) {
3477 if (IS_LABEL(*pgen)) {
3478 plabel = TOLABEL(pgen);
3479 if (plabel->pin == INFO) {
3481 strptr = findtextinstring(":", &locpos, plabel->string, cinst);
3482 if (locpos <= 0 || strptr == NULL)
3483 continue; /* null after netlist type designator, don't count */
3485 for (modeptr = *modelist; modeptr; modeptr = modeptr->next)
3486 if (!strncmp(modeptr->alias, strptr->data.string, locpos))
3487 break;
3489 if (modeptr == NULL) { /* Mode has not been enumerated */
3490 modeptr = (slistptr)malloc(sizeof(stringlist));
3491 modeptr->alias = (char *)malloc(locpos + 1);
3492 strncpy(modeptr->alias, strptr->data.string, locpos);
3493 modeptr->next = *modelist;
3494 *modelist = modeptr;
3498 else if (IS_OBJINST(*pgen)) {
3499 cinst = TOOBJINST(pgen);
3500 genprefixlist(cinst->thisobject, modelist);
3501 if (cinst->thisobject->symschem != NULL)
3502 genprefixlist(cinst->thisobject->symschem, modelist);
3507 /*----------------------------------------------------------------------*/
3508 /* Create and return an ordered list of info labels for the schematic */
3509 /* page cschem. A linked label list is returned, combining info labels */
3510 /* from all schematic pages related to cschem (master and slave pages). */
3511 /* It is the responsibility of the calling routine to free the linked */
3512 /* list. */
3513 /*----------------------------------------------------------------------*/
3515 LabellistPtr geninfolist(objectptr cschem, objinstptr cinst, char *mode)
3517 int locpos, j;
3518 int vmax;
3519 genericptr *pgen;
3520 labelptr plabel;
3521 LabellistPtr newllist, listtop, srchlist;
3522 u_char *strt;
3523 stringpart *strptr;
3525 listtop = NULL;
3526 vmax = 0;
3528 for (pgen = cschem->plist; pgen < cschem->plist + cschem->parts; pgen++) {
3529 if (IS_LABEL(*pgen)) {
3530 plabel = TOLABEL(pgen);
3531 if ((plabel->pin == INFO) && !textncomp(plabel->string, mode, cinst)) {
3533 if (mode[0] == '\0') {
3534 strptr = findtextinstring(":", &locpos, plabel->string, cinst);
3535 locpos--;
3537 else
3538 strptr = findstringpart(strlen(mode), &locpos, plabel->string, cinst);
3540 if (locpos < 0)
3541 continue; /* null after netlist type designator */
3543 strt = strptr->data.string + locpos + 1;
3545 if (*strt != ':') {
3546 if (sscanf(strt, "%d", &j) != 1) continue;
3548 /* Consider only positive-valued numbers. Negative */
3549 /* indices are handled by "writenet" by merging the */
3550 /* minus character into the mode name. */
3552 if (j < 0) continue;
3553 if (j >= vmax) vmax = j + 1;
3555 else
3556 j = ++vmax;
3558 newllist = (LabellistPtr)malloc(sizeof(Labellist));
3559 newllist->label = plabel;
3560 newllist->cschem = cschem;
3561 newllist->cinst = cinst;
3562 newllist->net.id = j; /* use this to find the ordering */
3563 newllist->subnets = 0; /* so free() doesn't screw up */
3565 /* Order the linked list */
3567 if ((listtop == NULL) || (listtop->net.id >= j)) {
3568 newllist->next = listtop;
3569 listtop = newllist;
3571 else {
3572 for (srchlist = listtop; srchlist != NULL; srchlist = srchlist->next) {
3573 if ((srchlist->next != NULL) && (srchlist->next->net.id >= j)) {
3574 newllist->next = srchlist->next;
3575 srchlist->next = newllist;
3576 break;
3578 else if (srchlist->next == NULL) {
3579 srchlist->next = newllist;
3580 newllist->next = NULL;
3587 return listtop;
3590 /*----------------------------------------------------------------------*/
3591 /* Trivial default pin name lookup. Parse an object's pin labels for */
3592 /* pin names. Return the text of the nth pin name, as counted by the */
3593 /* position in the object's parts list. If there are fewer than (n+1) */
3594 /* pin labels in the object, return NULL. Otherwise, return the pin */
3595 /* name in a malloc'd character string which the caller must free. */
3596 /*----------------------------------------------------------------------*/
3598 char *defaultpininfo(objinstptr cinst, int pidx)
3600 genericptr *pgen;
3601 labelptr plabel;
3602 objectptr cschem = cinst->thisobject;
3603 int count = 0;
3604 char *stxt;
3606 for (pgen = cschem->plist; pgen < cschem->plist + cschem->parts; pgen++) {
3607 if (IS_LABEL(*pgen)) {
3608 plabel = TOLABEL(pgen);
3609 if (plabel->pin == LOCAL) {
3610 if (count == pidx) {
3611 stxt = textprint(plabel->string, cinst);
3612 return stxt;
3614 count++;
3618 return NULL;
3621 /*----------------------------------------------------------------------*/
3622 /* Parse an object's info labels for pin names. This is *not* a */
3623 /* netlist function. The nth pin name is returned, or NULL if not */
3624 /* found. Return value is a malloc'd string which the caller must */
3625 /* free. */
3626 /*----------------------------------------------------------------------*/
3628 char *parsepininfo(objinstptr cinst, char *mode, int pidx)
3630 genericptr *pgen;
3631 labelptr plabel;
3632 u_char *strt, *fnsh;
3633 int slen, i, locpos;
3634 objectptr cschem = cinst->thisobject;
3635 stringpart *strptr;
3636 char *sout;
3637 int count = 0;
3639 for (pgen = cschem->plist; pgen < cschem->plist + cschem->parts; pgen++) {
3640 if (IS_LABEL(*pgen)) {
3641 plabel = TOLABEL(pgen);
3642 if (plabel->pin == INFO) {
3643 slen = stringlength(plabel->string, True, cinst);
3644 for (i = 1; i < slen; i++) {
3645 strptr = findstringpart(i, &locpos, plabel->string, cinst);
3646 if (locpos >= 0 && *(strptr->data.string + locpos) == ':') break;
3648 /* Currently we are not checking against "mode". . .*/
3649 /* interpret all characters after the colon */
3651 for (++i; i < slen; i++) {
3652 strptr = findstringpart(i, &locpos, plabel->string, cinst);
3653 if (locpos >= 0) {
3655 /* Do for all text characters */
3656 strt = strptr->data.string + locpos;
3657 if (*strt == '%') {
3658 strt++;
3659 i++;
3660 if (*strt == 'p') { /* Pin found! */
3661 if (count == pidx) { /* Get pin text */
3662 strt++;
3663 fnsh = strt + 1;
3664 while (*fnsh != ' ' && *fnsh != '\0') fnsh++;
3665 sout = malloc(fnsh - strt + 1);
3666 strncpy(sout, strt, fnsh - strt);
3667 return sout;
3669 count++;
3677 return NULL;
3680 /*----------------------------------------------------------------------*/
3681 /* Look for information labels in the object parts list. Parse the */
3682 /* information labels and print results to specified output device. */
3683 /* (This is not very robust to errors---needs work!) */
3684 /* */
3685 /* If "autonumber" is true, then the parsed info label is used to */
3686 /* auto-number devices. */
3687 /*----------------------------------------------------------------------*/
3689 char *parseinfo(objectptr cfrom, objectptr cthis, CalllistPtr clist,
3690 char *prefix, char *mode, Boolean autonumber, Boolean no_output)
3692 /* genericptr *pgen; (jdk) */
3693 stringpart *strptr, *ppin, *optr;
3694 char *snew, *sout = NULL, *pstring;
3695 u_char *strt, *fnsh;
3696 PortlistPtr ports;
3697 oparamptr ops, instops;
3698 int portid, locpos, i, k, subnet, slen; /* j, (jdk) */
3699 char *key = NULL;
3700 u_int newindex;
3701 Boolean is_flat = False, is_symbol = False, is_iso = False, is_quoted;
3702 Boolean do_update = True, include_once = False;
3703 char *locmode, *b36str, *sptr;
3704 LabellistPtr infolist, infoptr;
3705 FILE *finclude;
3707 /* For flat netlists, prefix the mode with the word "flat". */
3708 /* As of 3/8/07, this includes the ".sim" format, which */
3709 /* should be called as mode "flatsim". */
3711 locmode = mode;
3712 if (locmode && (!strncmp(mode, "flat", 4) || !strncmp(mode, "pseu", 4))) {
3713 locmode += 4;
3714 is_flat = True;
3717 /* mode == "" is passed when running resolve_devindex() and indicates */
3718 /* that the routine should be used to assign devindex to calls whose */
3719 /* devices have been manually assigned numbers. The preferred method */
3720 /* is to assign these values through the "index" parameter. Use of */
3721 /* parseinfo() ensures that objects that have no "index" parameter */
3722 /* but have valid info-labels for SPICE or PCB output will be */
3723 /* assigned the correct device index. */
3724 /* Sept. 3, 2006---The use of the prepended "@" character on */
3725 /* parameter names means that I can replace the cryptic "idx" with */
3726 /* the more obvious "index", which previously conflicted with the */
3727 /* PostScript command of the same name. Parameters "index" and "idx" */
3728 /* are treated interchangeably by the netlister. */
3730 if (locmode[0] == '\0')
3731 do_update = False;
3733 /* 1st pass: look for valid labels; see if more than one applies. */
3734 /* If so, order them correctly. Labels may not be numbered in */
3735 /* sequence, and may begin with zero. We allow a lot of flexibility */
3736 /* but the general expectation is that sequences start at 0 or 1 and */
3737 /* increase by consecutive integers. */
3739 infolist = geninfolist(cthis, clist->callinst, locmode);
3741 /* Now parse each label in sequence and output the result to */
3742 /* the return string. */
3744 sout = (char *)malloc(1);
3745 *sout = '\0';
3747 for (infoptr = infolist; infoptr != NULL; infoptr = infoptr->next) {
3748 objectptr pschem, cschem = infoptr->cschem;
3749 labelptr plabel = infoptr->label;
3750 objinstptr cinst = infoptr->cinst;
3752 /* move to colon character */
3753 slen = stringlength(plabel->string, True, cinst);
3754 for (i = 1; i < slen; i++) {
3755 strptr = findstringpart(i, &locpos, plabel->string, cinst);
3756 if (locpos >= 0 && *(strptr->data.string + locpos) == ':') break;
3759 /* interpret all characters after the colon */
3760 for (++i; i < slen; i++) {
3761 strptr = findstringpart(i, &locpos, plabel->string, cinst);
3762 if (locpos >= 0) {
3764 /* Do for all text characters */
3765 strt = strptr->data.string + locpos;
3766 if (*strt == '%') {
3767 is_quoted = False;
3768 strt++;
3769 i++;
3770 switch(*strt) {
3771 case '%':
3772 sout = (char *)realloc(sout, strlen(sout) + 2);
3773 strcat(sout, "%");
3774 break;
3775 case 'r':
3776 sout = (char *)realloc(sout, strlen(sout) + 2);
3777 strcat(sout, "\n");
3778 break;
3779 case 't':
3780 sout = (char *)realloc(sout, strlen(sout) + 2);
3781 strcat(sout, "\t");
3782 break;
3783 case 'i':
3784 if (clist->devname == NULL)
3785 clist->devname = strdup(sout);
3786 if (do_update) {
3787 if (is_flat)
3788 newindex = devflatindex(clist->devname);
3789 else
3790 newindex = devindex(cfrom, clist);
3791 b36str = d36a(newindex);
3792 sout = (char *)realloc(sout, strlen(sout) + strlen(b36str) + 1);
3793 sprintf(sout + strlen(sout), "%s", b36str);
3795 break;
3796 case 'N':
3797 sout = (char *)realloc(sout, strlen(sout)
3798 + strlen(cschem->name) + 1);
3799 strcat(sout, cschem->name);
3800 break;
3801 case 'n':
3802 sptr = strstr(cschem->name, "::");
3803 if (sptr == NULL)
3804 sptr = cschem->name;
3805 else
3806 sptr += 2;
3807 sout = (char *)realloc(sout, strlen(sout)
3808 + strlen(sptr) + 1);
3809 strcat(sout, sptr);
3810 break;
3811 case 'x':
3812 sout = (char *)realloc(sout, strlen(sout) + 7);
3813 sprintf(sout + strlen(sout), "%d",
3814 cinst->position.x);
3815 break;
3816 case 'y':
3817 sout = (char *)realloc(sout, strlen(sout) + 7);
3818 sprintf(sout + strlen(sout), "%d",
3819 cinst->position.y);
3820 break;
3821 case 'F':
3822 if (no_output) break;
3823 include_once = True;
3824 /* drop through */
3825 case 'f':
3826 if (no_output) break;
3828 /* Followed by a filename to include verbatim into */
3829 /* the output. Filename either has no spaces or */
3830 /* is in quotes. */
3832 /* Use textprint to catch any embedded parameters */
3834 snew = textprint(strptr, cinst);
3835 strt = snew;
3836 while (*strt != '\0') {
3837 if (*strt == '%') {
3838 if (include_once && *(strt + 1) == 'F')
3839 break;
3840 else if (!include_once && *(strt + 1) == 'f')
3841 break;
3843 strt++;
3846 if (*strt == '\0') {
3847 /* No filename; print verbatim */
3848 free(snew);
3849 include_once = False;
3850 break;
3853 strt += 2;
3854 if (*strt == '"') strt++;
3855 if (*strt == '"' || *strt == '\0') break;
3856 fnsh = strt + 1;
3857 while (*fnsh != '\0' && !isspace(*fnsh) && *fnsh != '"')
3858 fnsh++;
3859 strncpy(_STR, strt, (int)(fnsh - strt));
3860 _STR[(int)(fnsh - strt)] = '\0';
3861 free(snew);
3862 i = slen;
3864 /* Do variable and tilde expansion on the filename */
3865 xc_tilde_expand(_STR, 149);
3866 xc_variable_expand(_STR, 149);
3868 /* Check if this file has been included already */
3869 if (include_once) {
3870 if (check_included(_STR)) {
3871 include_once = False;
3872 break;
3874 append_included(_STR);
3877 /* Open the indicated file and dump to the output */
3878 finclude = fopen(_STR, "r");
3879 if (finclude != NULL) {
3880 char *sptr = sout;
3881 int nlen;
3882 int stlen = strlen(sout);
3883 while (fgets(_STR, 149, finclude)) {
3884 nlen = strlen(_STR);
3885 sout = (char *)realloc(sout, stlen + nlen + 1);
3886 sptr = sout + stlen;
3887 strcpy(sptr, _STR);
3888 stlen += nlen;
3890 fclose(finclude);
3892 else {
3893 Wprintf("No file named %s found", _STR);
3895 include_once = False;
3896 break;
3898 case 'p':
3899 /* Pin name either has no spaces or is in quotes */
3900 strt++;
3901 if (*strt == '"') {
3902 is_quoted = True;
3903 strt++;
3904 i++;
3906 if (*strt == '"' || *strt == '\0') break;
3907 fnsh = strt + 1;
3908 while (*fnsh != '\0' && !isspace(*fnsh) && *fnsh != '"')
3909 fnsh++;
3910 strncpy(_STR2, strt, (int)(fnsh - strt));
3911 _STR2[(int)(fnsh - strt)] = '\0';
3912 i += (fnsh - strt);
3913 /* if (is_quoted || *fnsh == '\0') i++; */
3914 if (is_quoted) i++;
3916 /* Find the port which corresponds to this pin name */
3917 /* in the called object (cschem). If this is a */
3918 /* linked symbol/schematic, then the port list will */
3919 /* be in the schematic view, not in the symbol view */
3921 pschem = cschem;
3922 if (cschem->ports == NULL && cschem->symschem != NULL &&
3923 cschem->symschem->ports != NULL)
3924 pschem = cschem->symschem;
3926 for (ports = pschem->ports; ports != NULL;
3927 ports = ports->next) {
3929 subnet = getsubnet(ports->netid, pschem);
3930 ppin = nettopin(ports->netid, pschem, NULL);
3931 // NOTE: _STR is used inside textprintsubnet()
3932 pstring = textprintsubnet(ppin, NULL, subnet);
3933 if (!strcmp(pstring, _STR2)) {
3934 portid = ports->portid;
3935 free(pstring);
3936 break;
3938 else
3939 free(pstring);
3941 if (ports != NULL) {
3943 /* Find the matching port in the calling object instance */
3945 for (ports = clist->ports; ports != NULL;
3946 ports = ports->next)
3947 if (ports->portid == portid) break;
3949 if (ports != NULL) {
3951 ppin = nettopin(ports->netid, cfrom, prefix);
3952 subnet = getsubnet(ports->netid, cfrom);
3953 snew = textprintsubnet(ppin, cinst, subnet);
3954 sout = (char *)realloc(sout, strlen(sout) +
3955 strlen(snew) + 1);
3956 strcat(sout, snew);
3957 free(snew);
3958 break;
3960 else {
3961 Fprintf(stderr, "Error: called non-existant port\n");
3964 else {
3965 Wprintf("No pin named %s in device %s", _STR, cschem->name);
3967 break;
3968 case 'v':
3969 /* Parameter name either has no spaces or is in quotes */
3970 strt++;
3971 if (*strt == '"') {
3972 is_quoted = True;
3973 strt++;
3974 i++;
3976 if (*strt == '"' || *strt == '\0') break;
3977 fnsh = strt + 1;
3978 while (*fnsh != '\0' && !isspace(*fnsh) && *fnsh != '"')
3979 fnsh++;
3980 strncpy(_STR, strt, (int)(fnsh - strt));
3981 _STR[(int)(fnsh - strt)] = '\0';
3982 i += (fnsh - strt);
3983 /* if (is_quoted || *fnsh == '\0') i++; */
3984 if (is_quoted) i++;
3986 /* Compare this name against the parameter keys */
3987 ops = match_param(cschem, _STR);
3989 /* For backwards compatibility, also try matching against */
3990 /* the parameter default value (method used before */
3991 /* implementing parameters as key:value pairs). */
3993 if (ops == NULL) {
3994 for (ops = cschem->params; ops != NULL; ops = ops->next) {
3995 if (ops->type == XC_STRING) {
3996 if (!textcomp(ops->parameter.string, _STR, NULL)) {
3997 /* substitute the parameter or default */
3998 instops = find_param(cinst, ops->key);
3999 optr = instops->parameter.string;
4000 if (stringlength(optr, True, cinst) > 0) {
4001 snew = textprint(optr, cinst);
4002 sout = (char *)realloc(sout, strlen(sout)
4003 + strlen(snew) + 1);
4004 strcat(sout, snew);
4005 free(snew);
4007 break;
4013 if (ops == NULL) {
4014 Wprintf("No parameter named %s in device %s",
4015 _STR, cschem->name);
4017 break;
4019 default:
4020 /* Presence of ".ends" statement forces xcircuit */
4021 /* not to write ".end" at the end of the netlist. */
4023 if (*strt == '.')
4024 if (!strncmp(strt + 1, "ends", 4))
4025 spice_end = FALSE;
4027 sout = (char *)realloc(sout, strlen(sout) + 2);
4028 *(sout + strlen(sout) - 1) = *strt;
4029 *(sout + strlen(sout)) = '\0';
4032 else {
4033 if (key != NULL)
4034 if (clist->devname == NULL)
4035 clist->devname = strdup(sout);
4037 /* Parameters with unresolved question marks are treated */
4038 /* like a "%i" string. */
4040 if ((key != NULL) && !textncomp(strptr, "?", cinst)) {
4041 if (do_update || autonumber) {
4042 if (is_flat || (cfrom == NULL))
4043 newindex = devflatindex(clist->devname);
4044 else
4045 newindex = devindex(cfrom, clist);
4046 k = strlen(sout);
4047 b36str = d36a(newindex);
4048 sout = (char *)realloc(sout, strlen(sout) + strlen(b36str) + 1);
4049 sprintf(sout + k, "%s", b36str);
4052 /* When called with autonumber = TRUE, generate a parameter */
4053 /* instance, replacing the question mark with the new index */
4054 /* number. */
4056 if (autonumber) {
4057 copyparams(cinst, cinst);
4058 ops = match_instance_param(cinst, key);
4059 optr = ops->parameter.string;
4060 if (!textncomp(optr, "?", cinst)) {
4061 optr->data.string = (char *)realloc(optr->data.string,
4062 strlen(sout + k) + 1);
4063 strcpy(optr->data.string, sout + k);
4065 else Wprintf("Error while auto-numbering parameters");
4066 resolveparams(cinst);
4069 else {
4070 /* A "?" default parameter that has a (different) */
4071 /* instance value becomes the device number. */
4072 int stlen;
4073 if ((key != NULL) && (clist->devindex < 0)) {
4074 ops = match_param(cschem, key);
4075 if (ops->type == XC_STRING) {
4076 CalllistPtr plist;
4077 if (!textcomp(ops->parameter.string, "?", NULL)) {
4078 if (is_flat) {
4079 /* Ignore the value and generate one instead */
4080 newindex = devflatindex(clist->devname);
4081 b36str = d36a(newindex);
4082 k = strlen(sout);
4083 sout = (char *)realloc(sout, strlen(sout)
4084 + strlen(b36str) + 1);
4085 sprintf(sout + k, "%s", b36str);
4086 continue; /* go on to next stringpart */
4088 else if (cfrom != NULL) {
4090 /* Interpret parameter string as a component */
4091 /* number so we can check for duplicates. Assume */
4092 /* a base-36 number, which includes all alphabet */
4093 /* characters. This will disambiguate, e.g., "1" */
4094 /* and "1R", but flag case-sensitivity, e.g., */
4095 /* "1R" and "1r". Thanks to Carsten Thomas for */
4096 /* pointing out the problem with assuming */
4097 /* numerical component values. */
4099 char *endptr;
4100 newindex = (u_int) strtol(strt, &endptr, 36);
4101 if (*endptr != '\0') {
4102 Fprintf(stderr, "Warning: Use of non-alphanumeric"
4103 " characters in component number \"%s\"\n", strt);
4105 clist->devindex = newindex;
4106 for (plist = cfrom->calls; plist; plist = plist->next) {
4107 if ((plist == clist) ||
4108 (plist->callobj != clist->callobj)) continue;
4110 /* Two parts have been named the same. Flag */
4111 /* it, but don't try to do anything about it. */
4112 /* 11/22/06---additionally check part numbers, */
4113 /* in case the symbol is a component sub-part. */
4115 if (plist->devindex == newindex) {
4116 if (samepart(plist, clist)) {
4117 Fprintf(stderr, "Warning: duplicate part number"
4118 " %s%s and %s%s\n", sout, strt, sout, strt);
4119 break;
4127 stlen = strlen(sout);
4128 sout = (char *)realloc(sout, stlen + 2);
4130 /* By convention, greek "mu" becomes ASCII "u", NOT "m", */
4131 /* otherwise we get, e.g., millifarads instead of microfarads */
4133 if ((is_symbol && (*strt == 'm')) || (is_iso && (*strt == 0265)))
4134 *(sout + stlen) = 'u';
4135 else
4136 *(sout + stlen) = *strt;
4137 *(sout + stlen + 1) = '\0';
4142 /* Some label controls are interpreted. Most are ignored */
4143 else {
4144 switch(strptr->type) {
4145 case PARAM_START:
4146 key = strptr->data.string;
4147 break;
4148 case PARAM_END:
4149 key = NULL;
4150 break;
4151 case RETURN:
4152 sout = (char *)realloc(sout, strlen(sout) + 2);
4153 strcat(sout, "\n");
4154 break;
4155 case TABFORWARD:
4156 sout = (char *)realloc(sout, strlen(sout) + 2);
4157 strcat(sout, "\t");
4158 break;
4159 case FONT_NAME:
4160 is_symbol = issymbolfont(strptr->data.font);
4161 is_iso = isisolatin1(strptr->data.font);
4162 break;
4167 /* Insert a newline between multiple valid info labels */
4168 if (infoptr->next != NULL) {
4169 sout = (char *)realloc(sout, strlen(sout) + 2);
4170 strcat(sout, "\n");
4173 freelabellist(&infoptr);
4175 if (*sout == '\0') {
4176 free(sout);
4177 return NULL;
4179 return sout;
4182 /*----------------------------------------------------------------------*/
4183 /* Write a low-level device */
4184 /*----------------------------------------------------------------------*/
4186 int writedevice(FILE *fp, char *mode, objectptr cfrom, CalllistPtr clist,
4187 char *prefix)
4189 char *sout;
4190 objectptr cthis;
4192 if (clist == NULL) {
4193 if (fp != NULL) fprintf(fp, "error: null device\n");
4194 return -1;
4197 /* Devices are always symbols. If the call indicates a schematic */
4198 /* and a separate associated symbol, then we want the symbol. */
4199 /* If we're writing a flat netlist, then return -1 to indicate that */
4200 /* the symbol instance needs to be flattened. */
4202 cthis = clist->callobj;
4203 if (clist->callobj->schemtype == PRIMARY || clist->callobj->schemtype ==
4204 SECONDARY)
4205 if (clist->callobj->symschem != NULL) {
4206 if (!strncmp(mode, "flat", 4))
4207 return -1;
4208 else
4209 cthis = clist->callobj->symschem;
4212 /* Look for information labels in the object parts list. */
4214 if ((sout = parseinfo(cfrom, cthis, clist, prefix, mode, FALSE, FALSE)) != NULL) {
4215 if (fp != NULL) {
4216 fputs(sout, fp);
4217 fprintf(fp, "\n");
4219 free(sout);
4220 return 0;
4223 /* Information labels do not necessarily exist. */
4224 return -1;
4227 #ifdef TCL_WRAPPER
4229 /*----------------------------------------------------------------------*/
4230 /* Translate a hierarchical net name into an object and instance. */
4231 /* Return TRUE if found, FALSE if not. If found, object and instance */
4232 /* are returned in the argument pointers. */
4233 /*----------------------------------------------------------------------*/
4235 Boolean HierNameToObject(objinstptr cinst, char *hiername, pushlistptr *stack)
4237 CalllistPtr calls;
4238 char *nexttoken, *hptr, *pptr;
4239 objectptr cschem, curobj, newobj;
4240 objinstptr newinst;
4241 int devindex, devlen;
4242 /* pushlistptr stackentry; (jdk) */
4244 cschem = cinst->thisobject;
4245 curobj = (cschem->schemtype == SECONDARY) ? cschem->symschem : cschem;
4247 if (curobj->calls == NULL) {
4248 if ((updatenets(cinst, FALSE) <= 0) || (curobj->calls == NULL)) {
4249 Wprintf("Error in generating netlists!");
4250 return False;
4254 hptr = hiername;
4255 while (hptr != NULL) {
4256 nexttoken = strchr(hptr, '/');
4257 if (nexttoken != NULL) *nexttoken = '\0';
4258 pptr = strrchr(hptr, '(');
4259 if (pptr != NULL) {
4260 if (sscanf(pptr + 1, "%d", &devindex) == 0) {
4261 pptr = NULL;
4262 devindex = 0;
4264 else
4265 *pptr = '\0';
4267 else devindex = -1;
4269 /* check to make sure that the netlist and device indices have been generated */
4270 for (calls = curobj->calls; calls != NULL; calls = calls->next) {
4271 if (calls->devindex == -1) {
4272 cleartraversed(curobj);
4273 resolve_indices(curobj, FALSE);
4277 /* hptr is now the object name, and devindex is the instance's call index */
4278 /* find the object corresponding to the name. */
4280 newobj = NameToObject(hptr, &newinst, TRUE);
4282 if (newobj == NULL) {
4284 /* Try device names (for netlist output) instead of object names */
4286 for (calls = curobj->calls; calls != NULL; calls = calls->next) {
4287 if (calls->devname != NULL) {
4288 devlen = strlen(calls->devname);
4289 if (!strncmp(hptr, calls->devname, devlen)) {
4290 if (devindex == -1) {
4291 if (sscanf(hptr + devlen, "%d", &devindex) == 0)
4292 devindex = 0;
4294 if (calls->devindex == devindex) {
4295 newobj = calls->callinst->thisobject;
4296 break;
4302 else {
4303 for (calls = curobj->calls; calls != NULL; calls = calls->next)
4304 if ((calls->callobj == newobj) && (calls->devindex == devindex))
4305 break;
4307 if (newobj == NULL || calls == NULL) {
4308 Fprintf(stderr, "object %s in hierarchy not found in schematic.\n", hptr);
4309 free_stack(stack);
4310 return FALSE;
4313 /* Diagnostic information */
4314 /* fprintf(stderr, "object %s(%d) = %s\n", newobj->name, devindex, hptr); */
4316 curobj = calls->callobj;
4317 push_stack(stack, calls->callinst, NULL);
4319 if (pptr != NULL) *pptr = '(';
4320 if (nexttoken == NULL) break;
4321 *nexttoken = '/';
4322 hptr = nexttoken + 1;
4324 return TRUE;
4327 #endif
4329 /*----------------------------------------------------------------------*/
4330 /* Save netlist into a flattened sim or spice file */
4331 /*----------------------------------------------------------------------*/
4333 void writeflat(objectptr cschem, CalllistPtr cfrom, char *prefix, FILE *fp, char *mode)
4335 CalllistPtr calls = cschem->calls;
4336 char *newprefix = (char *)malloc(sizeof(char));
4338 /* reset device indexes */
4340 for (calls = cschem->calls; calls != NULL; calls = calls->next)
4341 calls->devindex = -1;
4343 /* The naming convention for nodes below the top level uses a */
4344 /* slash-separated list of hierarchical names. Thus the call to */
4345 /* the hierarchical resolve_indices(). Indices used by bottom-most */
4346 /* symbols (e.g., flattened spice) will be generated by a separate */
4347 /* routine devflatindex(), unrelated to what is generated here, */
4348 /* which only affects the hierarchical naming of nodes. */
4350 resolve_indices(cschem, FALSE);
4352 /* write all the subcircuits */
4354 for (calls = cschem->calls; calls != NULL; calls = calls->next) {
4356 makelocalpins(cschem, calls, prefix);
4357 if (writedevice(fp, mode, cschem, calls, prefix) < 0) {
4358 sprintf(_STR, "%s_%u", calls->callobj->name,
4359 devindex(cschem, calls));
4360 newprefix = (char *)realloc(newprefix, sizeof(char) * (strlen(prefix)
4361 + strlen(_STR) + 2));
4362 sprintf(newprefix, "%s%s/", prefix, _STR);
4363 /* Allow cross-referenced parameters between symbols and schematics */
4364 /* by substituting into a schematic from its corresponding symbol */
4365 opsubstitute(calls->callobj, calls->callinst);
4366 /* psubstitute(calls->callinst); */
4367 writeflat(calls->callobj, calls, newprefix, fp, mode);
4369 clearlocalpins(calls->callobj);
4371 free(newprefix);
4374 /*----------------------------------------------------------------------*/
4375 /* Topmost call to write a flattened netlist */
4376 /*----------------------------------------------------------------------*/
4378 void topflat(objectptr cschem, objinstptr thisinst, CalllistPtr cfrom,
4379 char *prefix, FILE *fp, char *mode)
4381 char *locmode, *stsave = NULL;
4382 int modlen;
4383 Calllist loccalls;
4385 /* Set up local call list for parsing info labels in the top-level schematic */
4386 loccalls.cschem = NULL;
4387 loccalls.callobj = cschem;
4388 loccalls.callinst = thisinst;
4389 loccalls.devindex = -1;
4390 loccalls.ports = NULL;
4391 loccalls.next = NULL;
4393 modlen = strlen(mode);
4394 locmode = malloc(2 + modlen);
4395 strcpy(locmode, mode);
4396 locmode[modlen + 1] = '\0';
4398 /* "<mode>@" lines go in front */
4400 locmode[modlen] = '@';
4401 if (fp != NULL) stsave = parseinfo(NULL, cschem, &loccalls, NULL, locmode, FALSE,
4402 FALSE);
4403 if (stsave != NULL) {
4404 fputs(stsave, fp);
4405 fprintf(fp, "\n");
4406 free(stsave);
4407 stsave = NULL;
4410 writeflat(cschem, cfrom, prefix, fp, mode);
4411 freeflatindex();
4413 /* Check for negative-numbered info labels, indicated output that */
4414 /* should be processed after everything else. */
4416 locmode[modlen] = '-';
4417 stsave = parseinfo(NULL, cschem, &loccalls, NULL, locmode, FALSE, FALSE);
4418 if (stsave != NULL) {
4419 fputs(stsave, fp);
4420 fprintf(fp, "\n");
4421 free(stsave);
4422 stsave = NULL;
4424 free(locmode);
4427 /*----------------------------------------------------------------------*/
4428 /* Write out the list of global nets and their pin names */
4429 /*----------------------------------------------------------------------*/
4431 void writeglobals(objectptr cschem, FILE *fp)
4433 LabellistPtr llist;
4434 labelptr gpin;
4435 char *snew;
4437 if (fp == NULL) return;
4439 for (llist = global_labels; llist != NULL; llist = llist->next) {
4440 gpin = llist->label;
4441 snew = textprint(gpin->string, NULL);
4442 fprintf(fp, ".GLOBAL %s\n", snew); /* hspice format */
4443 /* fprintf(fp, "%s = %d\n", snew, -gptr->netid); */ /* generic format */
4444 free(snew);
4446 fprintf(fp, "\n");
4449 /*----------------------------------------------------------------------*/
4450 /* Write a SPICE subcircuit entry. */
4451 /*----------------------------------------------------------------------*/
4453 void writesubcircuit(FILE *fp, objectptr cschem)
4455 PortlistPtr ports;
4456 char *pstring;
4457 stringpart *ppin;
4458 int netid, length, plen, subnet; /* portid, (jdk) */
4460 /* Objects which have no ports are not written */
4461 if ((cschem->ports != NULL) && (fp != NULL)) {
4463 fprintf(fp, ".subckt %s", cschem->name);
4464 length = 9 + strlen(cschem->name);
4466 /* List of parameters in subcircuit. */
4467 /* Each parameter connects to a net which may have multiple */
4468 /* names, so find the net associated with the parameter */
4469 /* and convert it back into a pin name in the usual manner */
4470 /* using nettopin(). */
4472 for (ports = cschem->ports; ports != NULL;
4473 ports = ports->next) {
4474 netid = ports->netid;
4475 subnet = getsubnet(netid, cschem);
4476 ppin = nettopin(netid, cschem, NULL);
4477 pstring = textprintsubnet(ppin, NULL, subnet);
4478 plen = strlen(pstring) + 1;
4479 if (length + plen > 78) {
4480 fprintf(fp, "\n+ "); /* SPICE line break & continuation */
4481 length = 0;
4483 else length += plen;
4484 fprintf(fp, " %s", pstring);
4485 free(pstring);
4487 fprintf(fp, "\n");
4491 /*----------------------------------------------------------------------*/
4492 /* Resolve device names (fill calllist structure member "devname") */
4493 /*----------------------------------------------------------------------*/
4495 void resolve_devnames(objectptr cschem)
4497 CalllistPtr calls;
4498 char *stmp;
4499 oparamptr ops;
4501 for (calls = cschem->calls; calls != NULL; calls = calls->next) {
4502 if (calls->callobj->traversed == False) {
4503 calls->callobj->traversed = True;
4504 resolve_devnames(calls->callobj);
4507 if (calls->devname == NULL) {
4509 /* Check for "class" parameter. Note that although this */
4510 /* parameter may take a different instance value, the */
4511 /* device class is determined by the default value. */
4513 ops = find_param(calls->callinst, "class");
4514 if (ops && (ops->type == XC_STRING))
4515 calls->devname = textprint(ops->parameter.string, NULL);
4516 else {
4517 /* The slow way---parse info labels for any device information */
4518 if ((stmp = parseinfo(cschem, calls->callinst->thisobject, calls,
4519 NULL, "", FALSE, TRUE)) != NULL)
4520 free(stmp);
4526 /*----------------------------------------------------------------------*/
4527 /* Resolve (hierarchical) component numbering. If "autonumber" is TRUE */
4528 /* then this routine does auto-numbering. Otherwise, it auto-generates */
4529 /* device indices internally (in the "calllist" structure) but does not */
4530 /* copy them into parameter space. */
4531 /* */
4532 /* Note: resolve_devindex() only operates on a single object. Use */
4533 /* resolve_indices() to operate on the entire hierarchy. */
4534 /*----------------------------------------------------------------------*/
4536 void resolve_devindex(objectptr cschem, Boolean autonumber)
4538 CalllistPtr calls;
4539 char *stmp, *endptr;
4540 char *idxtype[] = {"index", "idx", NULL};
4541 oparamptr ops, ips;
4542 objinstptr cinst;
4543 stringpart *optr;
4544 int newindex, j;
4546 for (calls = cschem->calls; calls != NULL; calls = calls->next) {
4548 /* Check if there is an "index" parameter set to "?" in */
4549 /* the object with no existing instance value. */
4551 for (j = 0; idxtype[j] != NULL; j++)
4552 if ((ops = match_param(calls->callinst->thisobject, idxtype[j])) != NULL)
4553 break;
4555 if (ops && (ops->type == XC_STRING)) {
4556 if (!textcomp(ops->parameter.string, "?", NULL)) {
4557 cinst = calls->callinst;
4558 ips = match_instance_param(cinst, idxtype[j]);
4559 if ((autonumber == TRUE) && (ips == NULL)) {
4560 copyparams(cinst, cinst);
4561 ops = match_instance_param(cinst, idxtype[j]);
4562 optr = ops->parameter.string;
4563 newindex = devindex(cschem, calls);
4564 stmp = d36a(newindex);
4565 optr->data.string = (char *)realloc(optr->data.string, strlen(stmp) + 1);
4566 sprintf(optr->data.string, "%s", stmp);
4568 else if (calls->devindex < 0) {
4569 if (ips != NULL) {
4570 optr = ips->parameter.string;
4571 if (optr->type != TEXT_STRING) {
4572 /* Not a simple string. Dump the text. */
4573 char *snew = NULL;
4574 snew = textprint(optr, NULL),
4575 newindex = (u_int) strtol(snew, &endptr, 36);
4576 free(snew);
4578 else {
4579 newindex = (u_int) strtol(optr->data.string, &endptr, 36);
4581 if (*endptr != '\0') {
4582 /* It is possible to get an instance value that is */
4583 /* the same as the default, although this normally */
4584 /* doesn't happen. If it does, quietly remove the */
4585 /* unneeded instance value. */
4587 if (!stringcomp(ops->parameter.string, ips->parameter.string))
4588 resolveparams(cinst);
4589 else
4590 Fprintf(stderr, "Warning: Use of non-alphanumeric"
4591 " characters in component \"%s%s\" (instance"
4592 " of %s)\n", (calls->devname) ? calls->devname
4593 : calls->callobj->name, optr->data.string,
4594 calls->callobj->name);
4596 else
4597 calls->devindex = newindex;
4599 else /* if (autonumber) */
4600 devindex(cschem, calls);
4604 else {
4605 /* The slow way---parse info labels for any index information */
4606 if ((stmp = parseinfo(cschem, calls->callinst->thisobject, calls,
4607 NULL, "", autonumber, TRUE)) != NULL)
4608 free(stmp);
4613 /*----------------------------------------------------------------------*/
4614 /* Recursive call to resolve_devindex() through the circuit hierarchy */
4615 /*----------------------------------------------------------------------*/
4617 void resolve_indices(objectptr cschem, Boolean autonumber) {
4618 CalllistPtr calls;
4620 for (calls = cschem->calls; calls != NULL; calls = calls->next) {
4621 if (calls->callobj->traversed == False) {
4622 calls->callobj->traversed = True;
4623 resolve_indices(calls->callobj, autonumber);
4626 resolve_devindex(cschem, autonumber);
4629 /*----------------------------------------------------------------------*/
4630 /* Recursive call to clear all generated device numbers. */
4631 /*----------------------------------------------------------------------*/
4633 void clear_indices(objectptr cschem) {
4634 CalllistPtr calls;
4636 for (calls = cschem->calls; calls != NULL; calls = calls->next) {
4637 if (calls->callobj->traversed == False) {
4638 calls->callobj->traversed = True;
4639 clear_indices(calls->callobj);
4641 calls->devindex = -1;
4645 /*----------------------------------------------------------------------*/
4646 /* Recursive call to clear all device indexes (parameter "index") */
4647 /*----------------------------------------------------------------------*/
4649 void unnumber(objectptr cschem) {
4650 CalllistPtr calls;
4651 oparamptr ops, ips;
4652 char *idxtype[] = {"index", "idx", NULL};
4653 int j;
4655 for (calls = cschem->calls; calls != NULL; calls = calls->next) {
4656 if (calls->callobj->traversed == False) {
4657 calls->callobj->traversed = True;
4658 unnumber(calls->callobj);
4661 for (j = 0; idxtype[j] != NULL; j++)
4662 if ((ops = match_param(calls->callobj, idxtype[j])) != NULL) break;
4664 if (ops && (ops->type == XC_STRING)) {
4665 if (!textcomp(ops->parameter.string, "?", NULL)) {
4666 ips = match_instance_param(calls->callinst, idxtype[j]);
4667 if (ips != NULL)
4668 free_instance_param(calls->callinst, ips);
4674 /*----------------------------------------------------------------------*/
4675 /* Save netlist into a hierarchical file */
4676 /*----------------------------------------------------------------------*/
4678 void writehierarchy(objectptr cschem, objinstptr thisinst, CalllistPtr cfrom,
4679 FILE *fp, char *mode)
4681 CalllistPtr calls = cschem->calls;
4682 PortlistPtr ports, plist;
4683 int pnet, portid, length, plen, subnet, modlen; /* netid, (jdk) */
4684 char *locmode = mode, *stsave = NULL, *pstring;
4685 stringpart *ppin;
4686 Calllist loccalls;
4688 if (cschem->traversed == True) return;
4690 /* Set up local call list for parsing info labels in this schematic */
4691 loccalls.cschem = NULL;
4692 loccalls.callobj = cschem;
4693 loccalls.callinst = thisinst;
4694 loccalls.devindex = -1;
4695 loccalls.ports = NULL;
4696 loccalls.next = NULL;
4698 modlen = strlen(mode);
4699 locmode = malloc(2 + modlen);
4700 strcpy(locmode, mode);
4701 locmode[modlen + 1] = '\0';
4703 /* "<mode>@" lines go before any subcircuit calls */
4705 locmode[modlen] = '@';
4706 if (fp != NULL) stsave = parseinfo(NULL, cschem, &loccalls, NULL, locmode, FALSE,
4707 FALSE);
4708 if (stsave != NULL) {
4709 fputs(stsave, fp);
4710 fprintf(fp, "\n");
4711 free(stsave);
4712 stsave = NULL;
4715 /* Subcircuits which make no calls or have no devices do not get written */
4717 if (calls != NULL) {
4719 /* Make sure that all the subcircuits have been written first */
4721 for (; calls != NULL; calls = calls->next) {
4722 if (calls->callobj->traversed == False) {
4723 psubstitute(calls->callinst);
4724 writehierarchy(calls->callobj, calls->callinst, calls, fp, mode);
4725 calls->callobj->traversed = True;
4728 if (cschem->schemtype == FUNDAMENTAL) {
4729 free(locmode);
4730 return;
4734 /* Info-labels on a schematic (if any) get printed out first */
4736 if ((fp != NULL) && (cschem->calls != NULL)) {
4737 stsave = parseinfo(NULL, cschem, &loccalls, NULL, mode, FALSE, FALSE);
4738 if (stsave != NULL) {
4740 /* Check stsave for embedded SPICE subcircuit syntax */
4742 if (!strcmp(mode, "spice"))
4743 if (strstr(stsave, ".subckt ") == NULL)
4744 writesubcircuit(fp, cschem);
4746 fputs(stsave, fp);
4747 fprintf(fp, "\n");
4748 free(stsave);
4749 stsave = NULL;
4751 else if (cschem->calls != NULL)
4752 /* top-level schematics will be ignored because they have no ports */
4753 writesubcircuit(fp, cschem);
4756 /* Resolve all fixed part assignments (devindex) */
4757 resolve_devindex(cschem, FALSE);
4759 /* If the output file is NULL, then we're done */
4761 if (fp == NULL) {
4762 free(locmode);
4763 return;
4766 for (calls = cschem->calls; calls != NULL; calls = calls->next) {
4768 /* writedevice() is just another call to parseinfo() */
4769 if (writedevice(fp, mode, cschem, calls, NULL) < 0) {
4771 /* If the device did not produce any output in writedevice(), but */
4772 /* does not make any calls itself, then it is implicitly a TRIVIAL */
4773 /* symbol. */
4775 if ((calls->callobj->schemtype == TRIVIAL) || (calls->callobj->calls == NULL))
4776 continue;
4778 /* No syntax indicating how to write this call. Use SPICE "X" */
4779 /* format and arrange the parameters according to the structure. */
4781 calls->devname = strdup(spice_devname);
4782 fprintf(fp, "X%s", d36a(devindex(cschem, calls)));
4783 stsave = calls->callobj->name;
4784 length = 6;
4786 /* The object's definition lists calls in the order of the object's */
4787 /* port list. Therefore, use this order to find the port list. */
4788 /* This will also deal with the fact that not every port has to be */
4789 /* called by the instance (ports may be left disconnected). */
4791 for (ports = calls->callobj->ports; ports != NULL;
4792 ports = ports->next) {
4793 portid = ports->portid;
4794 for (plist = calls->ports; plist != NULL; plist = plist->next)
4795 if (plist->portid == ports->portid)
4796 break;
4798 pnet = (plist == NULL) ? netmax(cschem) + 1 : plist->netid;
4799 subnet = getsubnet(pnet, cschem);
4800 ppin = nettopin(pnet, cschem, NULL);
4801 pstring = textprintsubnet(ppin, NULL, subnet);
4802 plen = strlen(pstring) + 1;
4803 if (length + plen > 78) {
4804 fprintf(fp, "\n+ "); /* SPICE line continuation */
4805 length = 0;
4807 else length += plen;
4808 fprintf(fp, " %s", pstring);
4809 free(pstring);
4811 plen = 1 + strlen(stsave);
4812 if (length + plen > 78) fprintf(fp, "\n+ "); /* SPICE line cont. */
4813 fprintf(fp, " %s\n", stsave);
4817 /* Check for negative-numbered info labels, indicated output that */
4818 /* should be processed after everything else. If we're processing */
4819 /* SPICE output but the schematic does not provide a ".ends" */
4820 /* statement, then add it to the end of the deck. */
4822 if (cschem->calls != NULL) {
4823 locmode[modlen] = '-';
4824 stsave = parseinfo(NULL, cschem, &loccalls, NULL, locmode, FALSE, FALSE);
4825 if (stsave != NULL) {
4826 fputs(stsave, fp);
4827 fprintf(fp, "\n");
4828 if (cfrom != NULL)
4829 if (!strcmp(mode, "spice"))
4830 if (strstr(stsave, ".ends") == NULL)
4831 fprintf(fp, ".ends\n");
4832 free(stsave);
4834 else if (cfrom != NULL)
4835 fprintf(fp, ".ends\n");
4837 fprintf(fp, "\n");
4840 free(locmode);
4843 /*----------------------------------------------------------------------*/
4844 /* Create generic netlist in the Tcl interpreter variable space */
4845 /*----------------------------------------------------------------------*/
4847 #ifdef TCL_WRAPPER
4849 static Tcl_Obj *tclparseinfo(objectptr cschem)
4851 genericptr *pgen;
4852 labelptr plabel;
4854 Tcl_Obj *rlist = Tcl_NewListObj(0, NULL);
4856 for (pgen = cschem->plist; pgen < cschem->plist + cschem->parts; pgen++) {
4857 if (IS_LABEL(*pgen)) {
4858 plabel = TOLABEL(pgen);
4859 if (plabel->pin == INFO) {
4860 Tcl_ListObjAppendElement(xcinterp, rlist,
4861 TclGetStringParts(plabel->string));
4865 return rlist;
4868 /*----------------------------------------------------------------------*/
4869 /* Write global variables to Tcl list (in key-value pairs which can be */
4870 /* turned into an array variable using the "array set" command) */
4871 /* */
4872 /* Note: argument "cinst" is unused, but it really should be. We */
4873 /* should not assume that different schematics have the same list of */
4874 /* globals! */
4875 /*----------------------------------------------------------------------*/
4877 Tcl_Obj *tclglobals(objinstptr cinst)
4879 LabellistPtr llist;
4880 labelptr gpin;
4881 Tcl_Obj *gdict; /*, *netnum; (jdk) */
4882 buslist *sbus;
4883 int netid, lbus;
4885 gdict = Tcl_NewListObj(0, NULL);
4886 for (llist = global_labels; llist != NULL; llist = llist->next) {
4887 gpin = llist->label;
4888 Tcl_ListObjAppendElement(xcinterp, gdict, TclGetStringParts(gpin->string));
4889 for (lbus = 0;;) {
4890 if (llist->subnets == 0) {
4891 netid = llist->net.id;
4893 else {
4894 sbus = llist->net.list + lbus;
4895 netid = sbus->netid;
4897 Tcl_ListObjAppendElement(xcinterp, gdict, Tcl_NewIntObj(netid));
4898 if (++lbus >= llist->subnets) break;
4901 return gdict;
4904 /*----------------------------------------------------------------------*/
4905 /* Write a generic hierarchical netlist into Tcl list "subcircuits" */
4906 /*----------------------------------------------------------------------*/
4908 void tclhierarchy(objectptr cschem, objinstptr cinst, CalllistPtr cfrom, Tcl_Obj *cktlist)
4910 CalllistPtr calls = cschem->calls;
4911 PortlistPtr ports, plist;
4912 int netid, pnet, portid; /* , length, plen, i; (jdk) */
4913 Tcl_Obj *tclports, *tclcalls, *tclnewcall, *tclnets, *netnum;
4914 Tcl_Obj *tcldevs, *tclparams, *subckt, *newdev, *tcllabel, *portnum;
4915 oparamptr paramlist; /* , locparam; (jdk) */
4916 char *netsdone;
4918 /* Trivial objects are by definition those that are supposed */
4919 /* to be resolved by the netlist generation prior to output */
4920 /* and ignored by the output generator. */
4922 if (cschem->schemtype == TRIVIAL) return;
4924 /* Make sure that all the subcircuits have been written first */
4926 for (; calls != NULL; calls = calls->next) {
4927 if (calls->callobj->traversed == False) {
4928 tclhierarchy(calls->callobj, calls->callinst, calls, cktlist);
4929 calls->callobj->traversed = True;
4933 /* Write own subcircuit netlist */
4934 subckt = Tcl_NewListObj(0, NULL);
4936 /* Prepare the list of network cross-references */
4937 tclnets = Tcl_NewListObj(0, NULL);
4939 /* Make list of the nets which have been written so we don't redundantly */
4940 /* list any entries (use of calloc() initializes all entries to FALSE). */
4941 /* (calloc() replaced by malloc() and bzero() because Tcl doesn't */
4942 /* have a calloc() function (2/6/04)) */
4944 netsdone = (char *)malloc(2 + netmax(cschem));
4945 #ifdef _MSC_VER
4946 memset((void *)netsdone, 0, 2 + netmax(cschem));
4947 #else
4948 bzero((void *)netsdone, 2 + netmax(cschem));
4949 #endif
4951 /* Write the name (key value pair) */
4952 Tcl_ListObjAppendElement(xcinterp, subckt, Tcl_NewStringObj("name", 4));
4953 Tcl_ListObjAppendElement(xcinterp, subckt, Tcl_NewStringObj(cschem->name,
4954 strlen(cschem->name)));
4956 /* Write the handle of the object instance (key value pair) */
4957 Tcl_ListObjAppendElement(xcinterp, subckt, Tcl_NewStringObj("handle", 6));
4958 Tcl_ListObjAppendElement(xcinterp, subckt, Tcl_NewHandleObj(cinst));
4960 /* Write the list of ports */
4961 if ((ports = cschem->ports) != NULL) {
4963 /* List of ports in subcircuit. */
4964 /* Each parameter connects to a net which may have multiple */
4965 /* names, so find the net associated with the parameter */
4966 /* and convert it back into a pin name in the usual manner */
4967 /* using nettopin(). */
4969 tclports = Tcl_NewListObj(0, NULL);
4970 for (; ports != NULL; ports = ports->next) {
4971 netid = ports->netid;
4972 portid = ports->portid;
4973 netnum = Tcl_NewIntObj(netid);
4974 portnum = Tcl_NewIntObj(portid);
4975 Tcl_ListObjAppendElement(xcinterp, tclports, portnum);
4976 Tcl_ListObjAppendElement(xcinterp, tclports, netnum);
4977 if ((netid >= 0) && (netsdone[netid] == (char)0))
4979 Tcl_ListObjAppendElement(xcinterp, tclnets, netnum);
4980 Tcl_ListObjAppendElement(xcinterp, tclnets,
4981 TclGetStringParts(nettopin(netid, cschem, NULL)));
4982 /* record this net as having been added to the list */
4983 netsdone[netid] = (char)1;
4986 Tcl_ListObjAppendElement(xcinterp, subckt, Tcl_NewStringObj("ports", 5));
4987 Tcl_ListObjAppendElement(xcinterp, subckt, tclports);
4990 /* Write the list of parameter defaults (key:value pairs) */
4992 if (cschem->params != NULL) {
4993 tclparams = Tcl_NewListObj(0, NULL);
4995 for (paramlist = cschem->params; paramlist != NULL; paramlist = paramlist->next) {
4996 Tcl_ListObjAppendElement(xcinterp, tclparams,
4997 Tcl_NewStringObj(paramlist->key, strlen(paramlist->key)));
4998 switch(paramlist->type) {
4999 case XC_INT:
5000 Tcl_ListObjAppendElement(xcinterp, tclparams,
5001 Tcl_NewIntObj((int)paramlist->parameter.ivalue));
5002 break;
5003 case XC_FLOAT:
5004 Tcl_ListObjAppendElement(xcinterp, tclparams,
5005 Tcl_NewDoubleObj((double)paramlist->parameter.fvalue));
5006 break;
5007 case XC_STRING:
5008 tcllabel = TclGetStringParts(paramlist->parameter.string);
5009 /* Should get rid of last entry, "End Parameter"; not needed */
5010 Tcl_ListObjAppendElement(xcinterp, tclparams, tcllabel);
5011 break;
5012 case XC_EXPR:
5013 Tcl_ListObjAppendElement(xcinterp, tclparams,
5014 evaluate_raw(cschem, paramlist, cinst, NULL));
5015 break;
5018 Tcl_ListObjAppendElement(xcinterp, subckt, Tcl_NewStringObj("parameters", 10));
5019 Tcl_ListObjAppendElement(xcinterp, subckt, tclparams);
5022 /* Write the list of calls to subcircuits */
5024 if ((calls = cschem->calls) != NULL) {
5025 tclcalls = Tcl_NewListObj(0, NULL);
5026 for (; calls != NULL; calls = calls->next) {
5028 /* Don't write calls to non-functional subcircuits. */
5029 if (calls->callobj->schemtype == TRIVIAL) continue;
5031 tclnewcall = Tcl_NewListObj(0, NULL);
5032 Tcl_ListObjAppendElement(xcinterp, tclnewcall, Tcl_NewStringObj("name", 4));
5033 Tcl_ListObjAppendElement(xcinterp, tclnewcall,
5034 Tcl_NewStringObj(calls->callobj->name,
5035 strlen(calls->callobj->name)));
5038 /* Log any local parameter instances */
5039 if (calls->callinst->params != NULL) {
5040 tclparams = Tcl_NewListObj(0, NULL);
5042 for (paramlist = calls->callinst->params; paramlist != NULL;
5043 paramlist = paramlist->next) {
5044 Tcl_ListObjAppendElement(xcinterp, tclparams,
5045 Tcl_NewStringObj(paramlist->key, strlen(paramlist->key)));
5046 switch(paramlist->type) {
5047 case XC_INT:
5048 Tcl_ListObjAppendElement(xcinterp, tclparams,
5049 Tcl_NewIntObj((int)paramlist->parameter.ivalue));
5050 break;
5051 case XC_FLOAT:
5052 Tcl_ListObjAppendElement(xcinterp, tclparams,
5053 Tcl_NewDoubleObj((double)paramlist->parameter.fvalue));
5054 break;
5055 case XC_STRING:
5056 Tcl_ListObjAppendElement(xcinterp, tclparams,
5057 TclGetStringParts(paramlist->parameter.string));
5058 break;
5059 case XC_EXPR:
5060 Tcl_ListObjAppendElement(xcinterp, tclparams,
5061 evaluate_raw(cschem, paramlist, cinst, NULL));
5062 break;
5065 Tcl_ListObjAppendElement(xcinterp, tclnewcall,
5066 Tcl_NewStringObj("parameters", 10));
5067 Tcl_ListObjAppendElement(xcinterp, tclnewcall, tclparams);
5070 /* Called ports are listed by key:value pair (port number: net id) */
5072 if (calls->callobj->ports != NULL) {
5073 tclports = Tcl_NewListObj(0, NULL);
5074 for (ports = calls->callobj->ports; ports != NULL;
5075 ports = ports->next) {
5076 portid = ports->portid;
5077 for (plist = calls->ports; plist != NULL; plist = plist->next)
5078 if (plist->portid == ports->portid)
5079 break;
5081 pnet = (plist == NULL) ? netmax(cschem) + 1 : plist->netid;
5083 netnum = Tcl_NewIntObj((int)pnet);
5084 portnum = Tcl_NewIntObj(portid);
5085 Tcl_ListObjAppendElement(xcinterp, tclports, portnum);
5086 Tcl_ListObjAppendElement(xcinterp, tclports, netnum);
5087 if ((pnet >= 0) && (netsdone[pnet] == (char)0))
5089 Tcl_ListObjAppendElement(xcinterp, tclnets, netnum);
5090 Tcl_ListObjAppendElement(xcinterp, tclnets,
5091 TclGetStringParts(nettopin(pnet, cschem, NULL)));
5092 /* record this net as having been added to the list */
5093 netsdone[pnet] = (char)1;
5096 Tcl_ListObjAppendElement(xcinterp, tclnewcall,
5097 Tcl_NewStringObj("ports", 5));
5098 Tcl_ListObjAppendElement(xcinterp, tclnewcall, tclports);
5100 Tcl_ListObjAppendElement(xcinterp, tclcalls, tclnewcall);
5102 Tcl_ListObjAppendElement(xcinterp, subckt, Tcl_NewStringObj("calls", 5));
5103 Tcl_ListObjAppendElement(xcinterp, subckt, tclcalls);
5105 free(netsdone);
5107 /* If the object has info labels, write a device list. */
5108 /* Check both the schematic and its symbol for info labels. */
5110 tcldevs = Tcl_NewListObj(0, NULL);
5111 if (cschem->symschem != NULL) {
5112 newdev = tclparseinfo(cschem->symschem);
5113 Tcl_ListObjAppendElement(xcinterp, tcldevs, newdev);
5115 newdev = tclparseinfo(cschem);
5116 Tcl_ListObjAppendElement(xcinterp, tcldevs, newdev);
5117 Tcl_ListObjAppendElement(xcinterp, subckt, Tcl_NewStringObj("devices", 7));
5118 Tcl_ListObjAppendElement(xcinterp, subckt, tcldevs);
5120 /* Write the network cross-reference dictionary */
5121 Tcl_ListObjAppendElement(xcinterp, subckt, Tcl_NewStringObj("nets", 4));
5122 Tcl_ListObjAppendElement(xcinterp, subckt, tclnets);
5124 Tcl_ListObjAppendElement(xcinterp, cktlist, subckt);
5127 /*----------------------------------------------------------------------*/
5129 Tcl_Obj *tcltoplevel(objinstptr cinst)
5131 Tcl_Obj *cktlist;
5132 objectptr cschem = cinst->thisobject;
5134 cktlist = Tcl_NewListObj(0, NULL);
5135 cleartraversed(cschem);
5136 tclhierarchy(cschem, cinst, NULL, cktlist);
5137 return cktlist;
5140 #endif /* TCL_WRAPPER */
5142 /*----------------------------------------------------------------------*/
5143 /* Create generic netlist in the Python interpreter variable space */
5144 /*----------------------------------------------------------------------*/
5146 #ifdef HAVE_PYTHON
5148 static PyObject *pyparseinfo(objectptr cschem)
5150 genericptr *pgen;
5151 labelptr plabel;
5153 PyObject *rlist = PyList_New(0);
5155 for (pgen = cschem->plist; pgen < cschem->plist + cschem->parts; pgen++) {
5156 if (IS_LABEL(*pgen)) {
5157 plabel = TOLABEL(pgen);
5158 if (plabel->pin == INFO)
5159 PyList_Append(rlist, PyGetStringParts(plabel->string));
5162 return rlist;
5165 /*----------------------------------------------------------------------*/
5166 /* Write global variables to Python dictionary */
5167 /*----------------------------------------------------------------------*/
5169 PyObject *pyglobals(objectptr cschem)
5171 LabellistPtr llist;
5172 labelptr gpin;
5173 PyObject *gdict, *netnum;
5174 int netid, lbus;
5175 buslist *sbus;
5177 gdict = PyDict_New();
5178 for (llist = global_labels; llist != NULL; llist = llist->next) {
5179 gpin = llist->label;
5180 for (lbus = 0;;) {
5181 if (llist->subnets == 0)
5182 netid = llist->net.id;
5183 else {
5184 sbus = llist->net.list + lbus;
5185 netid = sbus->netid;
5187 netnum = PyInt_FromLong((long)(netid));
5188 PyDict_SetItem(gdict, netnum, PyGetStringParts(gpin->string));
5189 if (++lbus >= llist->subnets) break;
5192 return gdict;
5195 /*----------------------------------------------------------------------*/
5196 /* Write a generic hierarchical netlist into Python list "subcircuits" */
5197 /*----------------------------------------------------------------------*/
5199 void pyhierarchy(objectptr cschem, CalllistPtr cfrom, PyObject *cktlist)
5201 CalllistPtr calls = cschem->calls;
5202 PortlistPtr ports, plist;
5203 int netid, pnet, portid, length, plen, i;
5204 PyObject *pyports, *pycalls, *pynewcall, *pynets, *netnum;
5205 PyObject *pydevs, *pyparams, *subckt, *newdev, *pylabel;
5206 oparamptr paramlist;
5208 /* Trivial objects are by definition those that are supposed */
5209 /* to be resolved by the netlist generation prior to output */
5210 /* and ignored by the output generator. */
5212 if (cschem->schemtype == TRIVIAL) return;
5214 /* Make sure that all the subcircuits have been written first */
5216 for (; calls != NULL; calls = calls->next) {
5217 if (calls->callobj->traversed == False) {
5218 calls->callobj->traversed = True;
5219 pyhierarchy(calls->callobj, calls, cktlist);
5223 /* Write own subcircuit netlist */
5224 subckt = PyDict_New();
5226 /* Prepare the dictionary of network cross-references */
5227 pynets = PyDict_New();
5229 /* Write the name */
5230 PyDict_SetItem(subckt, PyString_FromString("name"),
5231 PyString_FromString(cschem->name));
5233 /* Write the list of ports */
5234 if ((ports = cschem->ports) != NULL) {
5236 /* List of ports in subcircuit. */
5237 /* Each parameter connects to a net which may have multiple */
5238 /* names, so find the net associated with the parameter */
5239 /* and convert it back into a pin name in the usual manner */
5240 /* using nettopin(). */
5242 pyports = PyList_New(0);
5243 for (; ports != NULL; ports = ports->next) {
5244 netid = ports->netid;
5245 netnum = PyInt_FromLong((long)netid);
5246 PyList_Append(pyports, netnum);
5247 if (netid >= 0)
5248 PyDict_SetItem(pynets, netnum,
5249 PyGetStringParts(nettopin(netid, cschem, NULL)));
5251 PyDict_SetItem(subckt, PyString_FromString("ports"), pyports);
5254 /* Write the list of parameters */
5256 if (cschem->params != NULL) {
5257 pyparams = PyList_New(0);
5259 for (paramlist = cschem->params; paramlist != NULL; paramlist = paramlist->next) {
5260 if (paramlist->type == XC_INT)
5261 PyList_Append(pyparams,
5262 PyInt_FromLong((long)paramlist->parameter.ivalue));
5263 if (paramlist->type == XC_FLOAT)
5264 PyList_Append(pyparams,
5265 PyFloat_FromDouble((double)paramlist->parameter.fvalue));
5266 else if (paramlist->type == XC_STRING) {
5267 pylabel = PyGetStringParts(paramlist->parameter.string);
5268 /* Should get rid of last entry, "End Parameter"; not needed */
5269 PyList_Append(pyparams, pylabel);
5272 PyDict_SetItem(subckt, PyString_FromString("parameters"), pyparams);
5275 /* Write the list of calls to subcircuits */
5277 if ((calls = cschem->calls) != NULL) {
5278 pycalls = PyList_New(0);
5279 for (; calls != NULL; calls = calls->next) {
5281 /* Don't write calls to non-functional subcircuits. */
5282 if (calls->callobj->schemtype == TRIVIAL) continue;
5284 pynewcall = PyDict_New();
5285 PyDict_SetItem(pynewcall, PyString_FromString("name"),
5286 PyString_FromString(calls->callobj->name));
5288 /* Log any local parameter instances */
5289 if (calls->callinst->params != NULL) {
5290 pyparams = PyList_New(0);
5292 for (paramlist = cschem->params; paramlist != NULL;
5293 paramlist = paramlist->next) {
5294 if (paramlist->type == XC_INT)
5295 PyList_Append(pyparams,
5296 PyInt_FromLong((long)paramlist->parameter.ivalue));
5297 else if (paramlist->type == XC_FLOAT)
5298 PyList_Append(pyparams,
5299 PyFloat_FromDouble((double)paramlist->parameter.fvalue));
5300 else if (paramlist->type == XC_STRING)
5301 PyList_Append(pyparams,
5302 PyGetStringParts(paramlist->parameter.string));
5304 PyDict_SetItem(pynewcall, PyString_FromString("parameters"),
5305 pyparams);
5308 /* The object's definition lists calls in the order of the object's */
5309 /* port list. Therefore, use this order to find the port list. */
5310 /* Unconnected ports will be NULL entries in the list (?). */
5312 if (calls->callobj->ports != NULL) {
5313 pyports = PyList_New(0);
5314 for (ports = calls->callobj->ports; ports != NULL;
5315 ports = ports->next) {
5316 portid = ports->portid;
5317 for (plist = calls->ports; plist != NULL; plist = plist->next)
5318 if (plist->portid == ports->portid)
5319 break;
5321 pnet = (plist == NULL) ? netmax(cschem) + 1 : plist->netid;
5323 netnum = PyInt_FromLong((long)pnet);
5324 PyList_Append(pyports, netnum);
5325 if (pnet >= 0)
5326 PyDict_SetItem(pynets, netnum,
5327 PyGetStringParts(nettopin(pnet, cschem, NULL)));
5329 PyDict_SetItem(pynewcall, PyString_FromString("ports"), pyports);
5331 PyList_Append(pycalls, pynewcall);
5333 PyDict_SetItem(subckt, PyString_FromString("calls"), pycalls);
5336 /* If the object has info labels, write a device list. */
5337 /* Check both the schematic and its symbol for info labels. */
5339 pydevs = PyList_New(0);
5340 if (cschem->symschem != NULL) {
5341 newdev = pyparseinfo(cschem->symschem);
5342 PyList_Append(pydevs, newdev);
5344 newdev = pyparseinfo(cschem);
5345 PyList_Append(pydevs, newdev);
5346 PyDict_SetItem(subckt, PyString_FromString("devices"), pydevs);
5348 /* Write the network cross-reference dictionary */
5349 PyDict_SetItem(subckt, PyString_FromString("nets"), pynets);
5351 PyList_Append(cktlist, subckt);
5354 /*----------------------------------------------------------------------*/
5356 PyObject *pytoplevel(objectptr cschem)
5358 PyObject *cktlist;
5360 cktlist = PyList_New(0);
5361 cleartraversed(cschem);
5362 pyhierarchy(cschem, NULL, cktlist);
5363 return cktlist;
5366 #endif /* HAVE_PYTHON */
5368 /*----------------------------------------------------------------------*/
5369 /* Update networks in an object by checking if the network is valid, */
5370 /* and destroying and recreating the network if necessary. */
5371 /* */
5372 /* Run cleartraversed() on all types. The traversed flag allows a */
5373 /* check for infinite recursion when creating calls. */
5374 /* */
5375 /* Returns -1 on discovery of infinite recursion, 0 on failure to */
5376 /* generate a net, and 1 on success. */
5377 /*----------------------------------------------------------------------*/
5379 int updatenets(objinstptr uinst, Boolean quiet) {
5380 objectptr thisobject;
5381 objinstptr thisinst;
5382 int spage;
5384 /* Never try to generate a netlist while a file is being read, as */
5385 /* can happen if an expression parameter attempts to query a netlist */
5386 /* node. */
5388 if (load_in_progress) return 0;
5390 if (uinst->thisobject->symschem != NULL
5391 && uinst->thisobject->schemtype != PRIMARY) {
5392 thisobject = uinst->thisobject->symschem;
5393 if ((spage = is_page(thisobject)) >= 0)
5394 thisinst = xobjs.pagelist[spage]->pageinst;
5396 else {
5397 thisobject = uinst->thisobject;
5398 thisinst = uinst;
5401 if (checkvalid(thisobject) == -1) {
5402 uselection *ssave;
5404 if (cleartraversed(thisobject) == -1) {
5405 Wprintf("Netlist error: Check for recursion in circuit!");
5406 return -1;
5409 /* Note: destroy/create nets messes up the part list. Why?? */
5411 if (areawin->selects > 0)
5412 ssave = remember_selection(areawin->topinstance, areawin->selectlist,
5413 areawin->selects);
5414 destroynets(thisobject);
5415 createnets(thisinst, quiet);
5416 if (areawin->selects > 0) {
5417 areawin->selectlist = regen_selection(areawin->topinstance, ssave);
5418 free_selection(ssave);
5422 if (thisobject->labels == NULL && thisobject->polygons == NULL) {
5423 if (quiet == FALSE)
5424 Wprintf("Netlist error: No netlist elements in object %s",
5425 thisobject->name);
5426 return 0;
5428 return 1;
5431 /*----------------------------------------------------------------------*/
5432 /* Generate netlist, write information according to mode, and then */
5433 /* destroy the netlist (this will be replaced eventually with a dynamic */
5434 /* netlist model in which the netlist changes according to editing of */
5435 /* individual elements, not created and destroyed wholesale) */
5436 /*----------------------------------------------------------------------*/
5438 void writenet(objectptr thisobject, char *mode, char *suffix)
5440 objectptr cschem;
5441 objinstptr thisinst;
5442 char filename[100];
5443 char *prefix, *cpos, *locmode = mode, *stsave = NULL;
5444 FILE *fp;
5445 /* Calllist topcell; (jdk) */
5446 Boolean is_spice = FALSE, sp_end_save = spice_end;
5448 /* Always use the master schematic, if there is one. */
5450 if (thisobject->schemtype == SECONDARY)
5451 cschem = thisobject->symschem;
5452 else
5453 cschem = thisobject;
5455 /* Update the netlist if this has not been done */
5457 if (NameToPageObject(cschem->name, &thisinst, NULL) == NULL) {
5458 Wprintf("Not a schematic. . . cannot generate output!\n");
5459 return;
5461 if (updatenets(thisinst, FALSE) <= 0) {
5462 Wprintf("No file written!");
5463 return;
5466 prefix = (char *)malloc(sizeof(char));
5467 *prefix = '\0';
5469 if ((cpos = strchr(cschem->name, ':')) != NULL) *cpos = '\0';
5470 sprintf(filename, "%s.%s", cschem->name, suffix);
5471 if (cpos != NULL) *cpos = ':';
5473 if (!strncmp(mode, "index", 5)) { /* This mode generates no output */
5474 locmode += 5;
5475 fp = (FILE *)NULL;
5477 else if ((fp = fopen(filename, "w")) == NULL) {
5478 Wprintf("Could not open file %s for writing.", filename);
5479 free(prefix);
5480 return;
5483 /* Clear device indices from any previous netlist output */
5484 cleartraversed(cschem);
5485 clear_indices(cschem);
5487 /* Make sure list of include-once files is empty */
5489 free_included();
5491 /* Handle different netlist modes */
5493 if (!strcmp(mode, "spice")) {
5495 if (thisobject->schemtype == SYMBOL)
5496 cschem = thisobject->symschem;
5498 is_spice = TRUE;
5499 fprintf(fp, "*SPICE %scircuit <%s> from XCircuit v%s rev %s\n\n",
5500 (thisobject->schemtype == SYMBOL) ? "sub" : "",
5501 cschem->name, PROG_VERSION, PROG_REVISION);
5503 /* writeglobals(cschem, fp); */
5504 cleartraversed(cschem);
5505 writehierarchy(cschem, thisinst, NULL, fp, mode);
5507 else if (!strcmp(mode, "flatspice")) {
5508 is_spice = TRUE;
5509 fprintf(fp, "*SPICE (flattened) circuit \"%s\" from XCircuit v%s rev %s\n\n",
5510 cschem->name, PROG_VERSION, PROG_REVISION);
5511 if (stsave != NULL) {
5512 fputs(stsave, fp);
5513 fprintf(fp, "\n");
5515 topflat(cschem, thisinst, NULL, prefix, fp, mode);
5517 else if (!strcmp(mode, "pseuspice")) {
5518 is_spice = TRUE;
5519 fprintf(fp, "*SPICE subcircuit \"%s\" from XCircuit v%s rev %s\n\n",
5520 cschem->name, PROG_VERSION, PROG_REVISION);
5521 if (stsave != NULL) {
5522 fputs(stsave, fp);
5523 fprintf(fp, "\n");
5525 writeflat(cschem, NULL, prefix, fp, mode);
5526 freeflatindex();
5528 else if (!strcmp(mode, "flatsim") || !strcmp(mode, "pseusim")) {
5529 fprintf(fp, "| sim circuit \"%s\" from XCircuit v%s rev %s\n",
5530 cschem->name, PROG_VERSION, PROG_REVISION);
5531 if (stsave != NULL) {
5532 fputs(stsave, fp);
5533 fprintf(fp, "\n");
5535 topflat(cschem, thisinst, NULL, prefix, fp, mode);
5537 else if (!strcmp(locmode, "pcb")) {
5538 struct Ptab *ptable = (struct Ptab *)NULL;
5539 writepcb(&ptable, cschem, NULL, "", mode);
5540 if (stsave != NULL) {
5541 fputs(stsave, fp);
5542 fprintf(fp, "\n");
5544 outputpcb(ptable, fp);
5545 freepcb(ptable);
5547 else if (!strncmp(mode, "flat", 4)) {
5548 /* User-defined modes beginning with the word "flat": */
5549 /* Assume a flattened netlist, and assume nothing else. */
5551 if (thisobject->schemtype == SYMBOL)
5552 cschem = thisobject->symschem;
5553 cleartraversed(cschem);
5554 writeflat(cschem, NULL, prefix, fp, mode);
5555 freeflatindex();
5557 else if (!strncmp(mode, "pseu", 4)) {
5558 /* User-defined modes beginning with the word "pseu": */
5559 /* Assume a flattened netlist for everything under the top level. */
5561 if (thisobject->schemtype == SYMBOL)
5562 cschem = thisobject->symschem;
5563 cleartraversed(cschem);
5564 topflat(cschem, thisinst, NULL, prefix, fp, mode);
5566 else {
5567 /* All user-defined modes: Assume a hierarchical netlist, and */
5568 /* assume nothing else. */
5570 if (thisobject->schemtype == SYMBOL)
5571 cschem = thisobject->symschem;
5572 cleartraversed(cschem);
5573 writehierarchy(cschem, thisinst, NULL, fp, mode);
5576 /* Finish up SPICE files with a ".end" statement (if requested) */
5577 if (is_spice && (spice_end == True)) fprintf(fp, ".end\n");
5578 spice_end = sp_end_save;
5580 /* Finish up */
5582 if (fp != NULL) {
5583 fclose(fp);
5584 Wprintf("%s netlist saved as %s", mode, filename);
5586 if (stsave != NULL) free(stsave);
5587 free(prefix);
5590 /*----------------------------------------------------------------------*/
5591 /* Flatten netlist and save into a table of pcb-style nets */
5592 /* The routine is recursive. Each call returns TRUE if some nested */
5593 /* call generated output; this is a reasonable way to tell if we have */
5594 /* reached the bottom of the hierarchy. */
5595 /*----------------------------------------------------------------------*/
5597 Boolean writepcb(struct Ptab **ptableptr, objectptr cschem, CalllistPtr cfrom,
5598 char *prefix, char *mode)
5600 PolylistPtr plist;
5601 LabellistPtr llist;
5602 CalllistPtr calls;
5603 PortlistPtr ports;
5604 int i, testnet, tmplen, subnet;
5605 char *newprefix = (char *)malloc(sizeof(char));
5606 char *sout, *snew;
5607 struct Ptab *hidx, *htmp;
5608 struct Pstr *tmpstr;
5609 struct Pnet *tmpnet;
5610 objinstptr cinst;
5611 buslist *sbus;
5612 int locnet, lbus;
5613 Boolean outputdone = FALSE;
5614 Boolean outputcall;
5616 /* Step 1A: Go through the polygons of this object and add */
5617 /* any unvisited nets to the table. */
5619 for (plist = cschem->polygons; plist != NULL; plist = plist->next) {
5620 for (lbus = 0;;) {
5621 if (plist->subnets == 0)
5622 testnet = plist->net.id;
5623 else {
5624 sbus = plist->net.list + lbus;
5625 testnet = sbus->netid;
5628 hidx = *ptableptr;
5629 while (hidx != NULL) {
5630 if (hidx->nets != NULL) {
5631 for (i = 0; i < hidx->nets->numnets; i++)
5632 if (*(hidx->nets->netidx + i) == testnet) break;
5633 if (i < hidx->nets->numnets) break;
5635 hidx = hidx->next;
5637 if (hidx == NULL) { /* make new entry for net in table */
5638 htmp = (struct Ptab *)malloc(sizeof(struct Ptab));
5639 tmpnet = (struct Pnet *)malloc(sizeof(struct Pnet));
5640 tmpnet->numnets = 1;
5641 tmpnet->netidx = (int *)malloc(sizeof(int));
5642 *(tmpnet->netidx) = testnet;
5643 tmpnet->next = NULL;
5644 htmp->cschem = cschem;
5645 htmp->nets = tmpnet;
5646 htmp->pins = NULL;
5647 htmp->next = *ptableptr;
5648 *ptableptr = htmp;
5649 /* Fprintf(stdout, "Added new index entry for net %d\n", testnet); */
5651 if (++lbus >= plist->subnets) break;
5655 /* Step 1B: Do the same thing for labels. */
5657 for (llist = cschem->labels; llist != NULL; llist = llist->next) {
5658 for (lbus = 0;;) {
5659 if (llist->subnets == 0)
5660 testnet = llist->net.id;
5661 else {
5662 sbus = llist->net.list + lbus;
5663 testnet = sbus->netid;
5666 hidx = *ptableptr;
5667 while (hidx != NULL) {
5668 if (hidx->nets != NULL) {
5669 for (i = 0; i < hidx->nets->numnets; i++)
5670 if (*(hidx->nets->netidx + i) == testnet) break;
5671 if (i < hidx->nets->numnets) break;
5673 hidx = hidx->next;
5675 if (hidx == NULL) { /* make new entry for net in table */
5676 htmp = (struct Ptab *)malloc(sizeof(struct Ptab));
5677 tmpnet = (struct Pnet *)malloc(sizeof(struct Pnet));
5678 tmpnet->numnets = 1;
5679 tmpnet->netidx = (int *)malloc(sizeof(int));
5680 *(tmpnet->netidx) = testnet;
5681 tmpnet->next = NULL;
5682 htmp->cschem = cschem;
5683 htmp->nets = tmpnet;
5684 htmp->pins = NULL;
5685 htmp->next = *ptableptr;
5686 *ptableptr = htmp;
5687 /* Fprintf(stdout, "Added new index entry for net %d\n", testnet); */
5689 if (++lbus >= llist->subnets) break;
5693 /* Step 2: Resolve fixed device indices */
5694 resolve_devindex(cschem, FALSE);
5696 /* Step 3: Go through the list of calls to search for endpoints */
5698 for (calls = cschem->calls; calls != NULL; calls = calls->next) {
5699 objectptr cthis;
5701 cinst = calls->callinst;
5703 /* Trivial objects should have been dealt with already. */
5704 /* If we don't continue the loop here, you get netlist output for */
5705 /* objects like dots and circles. */
5706 if (calls->callobj->schemtype == TRIVIAL) continue;
5708 /* Step 4: If call is to a bottom-most schematic, output the device connections */
5709 /* Info-label can provide an alternate name or specify the instance number */
5711 cthis = calls->callobj;
5712 if (calls->callobj->schemtype == PRIMARY || calls->callobj->schemtype ==
5713 SECONDARY)
5714 if (calls->callobj->symschem != NULL)
5715 cthis = calls->callobj->symschem;
5717 if ((sout = parseinfo(cschem, cthis, calls, prefix, mode, FALSE, TRUE)) == NULL) {
5718 if (calls->devname == NULL)
5719 calls->devname = strdup(calls->callinst->thisobject->name);
5720 sprintf(_STR, "%s_%s", calls->devname, d36a(devindex(cschem, calls)));
5722 else {
5723 sscanf(sout, "%s", _STR); /* Copy the first word out of sout */
5725 newprefix = (char *)realloc(newprefix, sizeof(char) * (strlen(prefix)
5726 + strlen(_STR) + 2));
5727 sprintf(newprefix, "%s%s/", prefix, _STR);
5729 /*----------------------------------------------------------------*/
5730 /* Parsing of the "pcb:" info label--- */
5731 /* Find all <net>=<name> strings and create the appropriate nets */
5732 /*----------------------------------------------------------------*/
5734 if (sout) {
5735 char rsave, *lhs, *rhs, *rend, *sptr = sout, *tmppinname;
5737 while ((rhs = strchr(sptr, '=')) != NULL) {
5738 Genericlist *implicit, newlist;
5739 struct Pstr *psrch = NULL;
5741 lhs = rhs - 1;
5742 while ((lhs >= sptr) && isspace(*lhs)) lhs--;
5743 while ((lhs >= sptr) && !isspace(*lhs)) lhs--;
5744 *rhs = '\0';
5745 rhs++;
5746 if (isspace(*lhs)) lhs++;
5747 while ((*rhs) && isspace(*rhs)) rhs++;
5748 for (rend = rhs; (*rend) && (!isspace(*rend)); rend++);
5749 rsave = *rend;
5750 *rend = '\0';
5752 /* Get the net from the equation RHS. If none exists, assume */
5753 /* the name is a global net and add it to the list of globals. */
5755 implicit = nametonet(cschem, cinst, rhs);
5756 if (implicit == NULL) {
5757 stringpart *strptr;
5758 label templabel;
5760 labeldefaults(&templabel, GLOBAL, 0, 0);
5761 strptr = templabel.string;
5762 strptr->type = TEXT_STRING;
5763 strptr->data.string = strdup(rhs);
5764 newlist.subnets = 0;
5765 newlist.net.id = globalmax() - 1;
5766 addglobalpin(cschem, cinst, &templabel, &newlist);
5767 implicit = &newlist;
5768 free(strptr->data.string);
5771 /* Get the name of the pin */
5772 tmppinname = (char *)malloc(strlen(newprefix) + strlen(lhs) + 2);
5773 strcpy(tmppinname, newprefix);
5774 if ((tmplen = strlen(newprefix)) > 0) tmppinname[tmplen - 1] = '-';
5775 strcat(tmppinname, lhs);
5777 /* Find the net in the ptable, or create a new entry for it */
5779 hidx = *ptableptr;
5780 while (hidx != NULL) {
5781 if (hidx->nets != NULL) {
5782 for (i = 0; i < hidx->nets->numnets; i++)
5783 if (*(hidx->nets->netidx + i) == implicit->net.id)
5784 break;
5785 if (i < hidx->nets->numnets) break;
5787 hidx = hidx->next;
5789 if (hidx == NULL) {
5790 htmp = (struct Ptab *)malloc(sizeof(struct Ptab));
5791 tmpnet = (struct Pnet *)malloc(sizeof(struct Pnet));
5792 tmpnet->numnets = 1;
5793 tmpnet->netidx = (int *)malloc(sizeof(int));
5794 *(tmpnet->netidx) = implicit->net.id;
5795 tmpnet->next = NULL;
5796 htmp->cschem = cschem;
5797 htmp->nets = tmpnet;
5798 htmp->pins = NULL;
5799 htmp->next = *ptableptr;
5800 *ptableptr = htmp;
5801 hidx = htmp;
5803 else {
5804 /* Check if any entries are the same as tmppinname */
5805 for (psrch = hidx->pins; psrch != NULL; psrch = psrch->next) {
5806 if (!strcmp(psrch->string->data.string, tmppinname))
5807 break;
5811 /* Get the pin name from the equation LHS */
5812 if (psrch == NULL) {
5813 tmpstr = (struct Pstr *)malloc(sizeof(struct Pstr));
5814 tmpstr->string = (stringpart *)malloc(sizeof(stringpart));
5815 tmpstr->string->type = TEXT_STRING;
5816 tmpstr->string->nextpart = NULL;
5817 tmpstr->string->data.string = tmppinname;
5818 tmpstr->next = hidx->pins;
5819 hidx->pins = tmpstr;
5821 else {
5822 free(tmppinname);
5825 /* Proceed to the next LHS=RHS pair */
5826 *rend = rsave;
5827 sptr = rend;
5829 free(sout);
5832 outputcall = FALSE;
5833 if (calls->callobj->calls != NULL) {
5835 /* Step 4A: Push current net translations */
5836 /* (Don't push or pop global net numbers: no translation needed!) */
5838 hidx = *ptableptr;
5839 while (hidx != NULL) {
5840 if ((hidx->nets != NULL) && (((hidx->nets->numnets > 0) &&
5841 (*(hidx->nets->netidx) >= 0)) || (hidx->nets->numnets == 0))) {
5842 tmpnet = (struct Pnet *)malloc(sizeof(struct Pnet));
5843 tmpnet->numnets = 0;
5844 tmpnet->netidx = NULL;
5845 tmpnet->next = hidx->nets;
5846 hidx->nets = tmpnet;
5848 hidx = hidx->next;
5851 /* Step 4B: Generate net translation table for each subcircuit */
5853 for (ports = calls->ports; ports != NULL; ports = ports->next) {
5854 for (hidx = *ptableptr; hidx != NULL; hidx = hidx->next) {
5855 if (hidx->nets != NULL) {
5856 if (hidx->nets->next != NULL) {
5857 for (i = 0; i < hidx->nets->next->numnets; i++)
5858 if (*(hidx->nets->next->netidx + i) == ports->netid)
5859 break;
5860 if (i < hidx->nets->next->numnets) break;
5862 else if (ports->netid < 0) {
5863 if (hidx->nets->netidx != NULL)
5864 if (*(hidx->nets->netidx) == ports->netid)
5865 break;
5869 if (hidx != NULL) {
5870 hidx->nets->numnets++;
5871 if (hidx->nets->numnets == 1)
5872 hidx->nets->netidx = (int *)malloc(sizeof(int));
5873 else
5874 hidx->nets->netidx = (int *)realloc(hidx->nets->netidx,
5875 hidx->nets->numnets * sizeof(int));
5877 /* Translate net value */
5878 locnet = translatedown(ports->netid, ports->portid,
5879 calls->callobj);
5880 *(hidx->nets->netidx + hidx->nets->numnets - 1) = locnet;
5882 /* Fprintf(stdout, "Translation: net %d in object %s is net "
5883 "%d in object %s\n", ports->netid, cschem->name,
5884 locnet, calls->callobj->name); */
5888 /* Step 4C: Run routine recursively on the subcircuit */
5889 /* If it had a "pcb:" info label that was handled, then ignore */
5891 /* Fprintf(stdout, "Recursive call of writepcb() to %s\n",
5892 calls->callobj->name); */
5894 outputcall = writepcb(ptableptr, calls->callobj, calls, newprefix, mode);
5896 /* Step 4D: Pop the translation table */
5897 /* (Don't pop global nets (designated by negative net number)) */
5899 hidx = *ptableptr;
5900 while (hidx != NULL) {
5901 if ((hidx->nets != NULL) && (((hidx->nets->numnets > 0) &&
5902 (*(hidx->nets->netidx) >= 0)) || (hidx->nets->numnets == 0))) {
5903 tmpnet = hidx->nets->next;
5904 if (hidx->nets->numnets > 0) free(hidx->nets->netidx);
5905 free(hidx->nets);
5906 hidx->nets = tmpnet;
5908 hidx = hidx->next;
5912 if (!outputcall) {
5913 stringpart *ppin;
5915 /* Fprintf(stdout, "Reached lowest-level schematic: Finding connections\n"); */
5916 for (ports = calls->ports; ports != NULL; ports = ports->next) {
5917 locnet = translatedown(ports->netid, ports->portid, calls->callobj);
5918 /* Global pin names should probably be ignored, not just */
5919 /* when an object is declared to be "trivial". Not sure if */
5920 /* this breaks certain netlists. . . */
5921 if (locnet < 0) continue;
5923 /* Get the name of the pin in the called object with no prefix. */
5924 subnet = getsubnet(locnet, calls->callobj);
5925 ppin = nettopin(locnet, calls->callobj, NULL);
5926 hidx = *ptableptr;
5927 while (hidx != NULL) {
5928 if (hidx->nets != NULL) {
5929 /* Global nets were not translated, do not iterate through list */
5930 if (*(hidx->nets->netidx) >= 0) {
5931 for (i = 0; i < hidx->nets->numnets; i++)
5932 if (*(hidx->nets->netidx + i) == ports->netid)
5933 break;
5934 if (i < hidx->nets->numnets) break;
5935 } else {
5936 if (*(hidx->nets->netidx) == ports->netid) break;
5939 hidx = hidx->next;
5941 if (hidx == NULL) {
5942 snew = textprintsubnet(ppin, cinst, subnet);
5943 Fprintf(stdout, "Warning: Unconnected pin %s%s\n", newprefix, snew);
5944 free(snew);
5946 else {
5947 outputcall = TRUE;
5948 outputdone = TRUE;
5949 tmpstr = (struct Pstr *)malloc(sizeof(struct Pstr));
5950 tmpstr->string = (stringpart *)malloc(sizeof(stringpart));
5951 tmpstr->string->type = TEXT_STRING;
5952 tmpstr->string->nextpart = NULL;
5953 snew = textprintsubnet(ppin, cinst, subnet);
5954 tmpstr->string->data.string = (char *)malloc(strlen(newprefix)
5955 + strlen(snew) + 2);
5956 strcpy(tmpstr->string->data.string, newprefix);
5957 /* Replace slash '/' with dash '-' at bottommost level */
5958 if ((tmplen = strlen(newprefix)) > 0)
5959 tmpstr->string->data.string[tmplen - 1] = '-';
5960 strcat(tmpstr->string->data.string, snew);
5961 free(snew);
5962 tmpstr->next = hidx->pins;
5963 hidx->pins = tmpstr;
5965 /* diagnostic information */
5967 struct Pnet *locnet = hidx->nets;
5968 int ctr = 0;
5969 while (locnet->next != NULL) {
5970 locnet = locnet->next;
5971 ctr++;
5973 /* Fprintf(stdout, "Logged level-%d net %d (local net %d) pin %s\n",
5974 ctr, *(locnet->netidx), *(hidx->nets->netidx + i),
5975 tmpstr->string); */
5982 /* Step 5: Cleanup */
5984 free(newprefix);
5985 return outputdone;
5988 /*----------------------------------------------------------------------*/
5989 /* Save PCB table into pcb-style file */
5990 /*----------------------------------------------------------------------*/
5992 void outputpcb(struct Ptab *ptable, FILE *fp)
5994 int netidx = 1, ccol, subnet;
5995 struct Ptab *pseek;
5996 struct Pstr *sseek;
5997 char *snew;
5998 stringpart *ppin;
6000 if (fp == NULL) return;
6002 for (pseek = ptable; pseek != NULL; pseek = pseek->next) {
6003 if (pseek->pins != NULL) {
6004 if ((pseek->nets != NULL) && (pseek->nets->numnets > 0)) {
6005 subnet = getsubnet(*(pseek->nets->netidx), pseek->cschem);
6006 ppin = nettopin(*(pseek->nets->netidx), pseek->cschem, "");
6007 snew = textprintsubnet(ppin, NULL, subnet);
6008 strcpy(_STR, snew);
6009 free(snew);
6011 else
6012 sprintf(_STR, "NET%d ", netidx++);
6013 fprintf(fp, "%-11s ", _STR);
6014 ccol = 12;
6015 for (sseek = pseek->pins; sseek != NULL; sseek = sseek->next) {
6016 ccol += stringlength(sseek->string, False, NULL) + 3;
6017 if (ccol > 78) {
6018 fprintf(fp, "\\\n ");
6019 ccol = 18 + stringlength(sseek->string, False, NULL);
6021 snew = textprint(sseek->string, NULL);
6022 fprintf(fp, "%s ", snew);
6023 free(snew);
6025 fprintf(fp, "\n");
6027 /* else fprintf(fp, "NET%d *UNCONNECTED*\n", netidx++); */
6031 /*----------------------------------------------*/
6032 /* free memory allocated to PCB net tables */
6033 /*----------------------------------------------*/
6035 void freepcb(struct Ptab *ptable)
6037 struct Ptab *pseek, *pseek2;
6038 struct Pstr *sseek, *sseek2;
6039 struct Pnet *nseek, *nseek2;
6041 pseek = ptable;
6042 pseek2 = pseek;
6044 while (pseek2 != NULL) {
6045 pseek = pseek->next;
6047 sseek = pseek2->pins;
6048 sseek2 = sseek;
6049 while (sseek2 != NULL) {
6050 sseek = sseek->next;
6051 freelabel(sseek2->string);
6052 free(sseek2);
6053 sseek2 = sseek;
6056 nseek = pseek2->nets;
6057 nseek2 = nseek;
6058 while (nseek2 != NULL) {
6059 nseek = nseek->next;
6060 if (nseek2->numnets > 0) free(nseek2->netidx);
6061 free(nseek2);
6062 nseek2 = nseek;
6065 free(pseek2);
6066 pseek2 = pseek;
6070 /*----------------------------------------------------------------------*/
6071 /* Remove an element from the netlist. This is necessary when an */
6072 /* element is deleted from the object, so that the netlist does not */
6073 /* refer to a nonexistant element, or try to perform netlist function */
6074 /* on it. */
6075 /* */
6076 /* Return True or False depending on whether a pin was "orphaned" on */
6077 /* the corresponding schematic or symbol (if any), as this may cause */
6078 /* the class of the object (FUNDAMENTAL, TRIVIAL, etc.) to change. */
6079 /*----------------------------------------------------------------------*/
6081 Boolean RemoveFromNetlist(objectptr thisobject, genericptr thiselem)
6083 Boolean pinchanged = False;
6084 labelptr nlab;
6085 polyptr npoly;
6086 objectptr pschem;
6087 objinstptr ninst;
6089 PolylistPtr plist, plast;
6090 LabellistPtr llist, llast;
6091 CalllistPtr clist, clast;
6093 pschem = (thisobject->schemtype == SECONDARY) ? thisobject->symschem
6094 : thisobject;
6096 switch (thiselem->type) {
6097 case OBJINST:
6098 ninst = (objinstptr)thiselem;
6099 /* If this is an object instance, remove it from the calls */
6100 clast = NULL;
6101 for (clist = pschem->calls; clist != NULL; clist = clist->next) {
6102 if (clist->callinst == ninst) {
6103 if (clast == NULL)
6104 pschem->calls = clist->next;
6105 else
6106 clast->next = clist->next;
6107 freecalls(clist);
6108 clist = (clast) ? clast : pschem->calls;
6109 break;
6111 else
6112 clast = clist;
6114 break;
6116 case POLYGON:
6117 npoly = (polyptr)thiselem;
6118 if (nonnetwork(npoly)) break;
6120 /* If this is a polygon, remove it from the netlist */
6121 plast = NULL;
6122 for (plist = pschem->polygons; plist != NULL; plist = plist->next) {
6123 if (plist->poly == npoly) {
6124 if (plast == NULL)
6125 pschem->polygons = plist->next;
6126 else
6127 plast->next = plist->next;
6128 if (plist->subnets > 0)
6129 free(plist->net.list);
6130 break;
6132 else
6133 plast = plist;
6135 break;
6137 case LABEL:
6138 nlab = (labelptr)thiselem;
6139 if ((nlab->pin != LOCAL) && (nlab->pin != GLOBAL)) break;
6141 /* If this is a label, remove it from the netlist */
6142 llast = NULL;
6143 for (llist = pschem->labels; llist != NULL; llist = llist->next) {
6144 if (llist->label == nlab) {
6145 if (llast == NULL)
6146 pschem->labels = llist->next;
6147 else
6148 llast->next = llist->next;
6149 if (llist->subnets > 0)
6150 free(llist->net.list);
6151 break;
6153 else
6154 llast = llist;
6157 /* Mark pin label in corresponding schematic/symbol as "orphaned" */
6158 /* by changing designation from type "pin" to type "label". */
6160 if (findlabelcopy(nlab, nlab->string) == NULL) {
6161 changeotherpins(NULL, nlab->string);
6162 if (nlab->pin == INFO) pinchanged = True;
6165 return pinchanged;
6168 /*----------------------------------------------------------------------*/
6169 /* Remove one element from the netlist */
6170 /*----------------------------------------------------------------------*/
6172 void remove_netlist_element(objectptr cschem, genericptr genelem) {
6174 objectptr pschem;
6175 CalllistPtr clist, clast, cnext;
6176 LabellistPtr llist, llast, lnext;
6177 PolylistPtr plist, plast, pnext;
6178 Boolean found = FALSE;
6180 /* Always call on the primary schematic */
6181 pschem = (cschem->schemtype == SECONDARY) ? cschem->symschem : cschem;
6183 switch (ELEMENTTYPE(genelem)) {
6184 case POLYGON:
6185 /* remove polygon from polygon list */
6186 plast = NULL;
6187 for (plist = pschem->polygons; plist != NULL; ) {
6188 pnext = plist->next;
6189 if ((genericptr)plist->poly == genelem) {
6190 found = TRUE;
6191 if (plist->subnets > 0)
6192 free(plist->net.list);
6193 free(plist);
6194 if (plast != NULL)
6195 plast->next = pnext;
6196 else
6197 pschem->polygons = pnext;
6198 break;
6200 else
6201 plast = plist;
6202 plist = pnext;
6204 break;
6206 case LABEL:
6208 /* remove label from label list */
6209 llast = NULL;
6210 for (llist = pschem->labels; llist != NULL; ) {
6211 lnext = llist->next;
6212 if ((genericptr)llist->label == genelem) {
6213 found = TRUE;
6214 if (llist->subnets > 0)
6215 free(llist->net.list);
6216 free(llist);
6217 if (llast != NULL)
6218 llast->next = lnext;
6219 else
6220 pschem->labels = lnext;
6221 break;
6223 else
6224 llast = llist;
6225 llist = lnext;
6228 /* also check the globals */
6229 llast = NULL;
6230 for (llist = global_labels; llist != NULL; ) {
6231 lnext = llist->next;
6232 if ((genericptr)llist->label == genelem) {
6233 found = TRUE;
6234 if (llist->subnets > 0)
6235 free(llist->net.list);
6236 free(llist);
6237 if (llast != NULL)
6238 llast->next = lnext;
6239 else
6240 global_labels = lnext;
6241 break;
6243 else
6244 llast = llist;
6245 llist = lnext;
6247 break;
6249 case OBJINST:
6251 /* remove instance from call list */
6252 clast = NULL;
6253 for (clist = pschem->calls; clist != NULL; ) {
6254 cnext = clist->next;
6255 if ((genericptr)clist->callinst == genelem) {
6256 found = TRUE;
6257 freecalls(clist);
6258 if (clast != NULL)
6259 clast->next = cnext;
6260 else
6261 pschem->calls = cnext;
6263 else
6264 clast = clist;
6265 clist = cnext;
6267 break;
6269 if (found)
6270 pschem->valid = FALSE;
6273 /*----------------------------------------------------------------------*/
6274 /* Free memory allocated for the ports in a calls. */
6275 /*----------------------------------------------------------------------*/
6277 void freecalls(CalllistPtr calls)
6279 PortlistPtr ports, pptr;
6281 for (ports = calls->ports; ports != NULL;) {
6282 pptr = ports->next;
6283 free(ports);
6284 ports = pptr;
6286 if (calls->devname != NULL) free(calls->devname);
6287 free(calls);
6290 /*----------------------------------------------------------------------*/
6291 /* Free memory for a Genericlist structure (may also be a Labellist or */
6292 /* Polylist structure). */
6293 /*----------------------------------------------------------------------*/
6295 void freegenlist(Genericlist *nets)
6297 if (nets == NULL) return;
6298 if (nets->subnets > 0)
6299 free(nets->net.list);
6300 free(nets);
6303 /*----------------------------------------------------------------------*/
6304 /* Free memory allocated for the label list in a netlist. */
6305 /*----------------------------------------------------------------------*/
6307 void freelabellist(LabellistPtr *listtop)
6309 LabellistPtr labellist, llist;
6311 for (labellist = *listtop; labellist != NULL;) {
6312 llist = labellist->next;
6313 freegenlist((Genericlist *)labellist);
6314 labellist = llist;
6316 *listtop = NULL;
6319 /*----------------------------------------------------------------------*/
6320 /* Free memory allocated for the polygon list in a netlist. */
6321 /*----------------------------------------------------------------------*/
6323 void freepolylist(PolylistPtr *listtop)
6325 PolylistPtr polylist, plist;
6327 for (polylist = *listtop; polylist != NULL;) {
6328 plist = polylist->next;
6329 freegenlist((Genericlist *)polylist);
6330 polylist = plist;
6332 *listtop = NULL;
6335 /*----------------------------------------------------------------------*/
6336 /* Free memory allocated for netlist */
6337 /*----------------------------------------------------------------------*/
6339 void freenetlist(objectptr cschem)
6341 PolylistPtr *plist;
6342 LabellistPtr *llist;
6344 plist = &cschem->polygons;
6345 freepolylist(plist);
6346 llist = &cschem->labels;
6347 freelabellist(llist);
6350 /*----------------------------------------------------------------------*/
6351 /* Clear the "traversed" flag in all objects of the hierarchy. */
6352 /*----------------------------------------------------------------------*/
6354 int cleartraversed_level(objectptr cschem, int level)
6356 genericptr *cgen;
6357 objinstptr cinst;
6358 objectptr callobj, pschem;
6360 /* Always call on the primary schematic */
6361 pschem = (cschem->schemtype == SECONDARY) ? cschem->symschem : cschem;
6363 /* Recursively call cleartraversed() on all subobjects, a la gennetlist() */
6364 /* Use the parts list of the object, not the calls, because calls */
6365 /* may have been removed. */
6367 if (level == HIERARCHY_LIMIT) return -1;
6369 for (cgen = pschem->plist; cgen < pschem->plist + pschem->parts; cgen++) {
6370 if (IS_OBJINST(*cgen)) {
6371 cinst = TOOBJINST(cgen);
6373 if (cinst->thisobject->symschem != NULL)
6374 callobj = cinst->thisobject->symschem;
6375 else
6376 callobj = cinst->thisobject;
6378 /* Don't infinitely recurse if object is on its own schematic */
6379 /* However, we need to take a stab at more subtle recursion, too */
6381 if (callobj != pschem)
6382 if (cleartraversed_level(callobj, level + 1) == -1)
6383 return -1;
6386 pschem->traversed = False;
6388 return 0;
6391 /*----------------------------------------------------------------------*/
6392 /* This is the routine normally called, as it hides the "level" */
6393 /* argument tagging the level of recursion. */
6394 /*----------------------------------------------------------------------*/
6396 int cleartraversed(objectptr cschem) {
6397 return cleartraversed_level(cschem, 0);
6400 /*----------------------------------------------------------------------*/
6401 /* If any part of the netlist is invalid, destroy the entire netlist */
6402 /*----------------------------------------------------------------------*/
6404 int checkvalid(objectptr cschem)
6406 genericptr *cgen;
6407 objinstptr cinst;
6408 objectptr callobj, pschem;
6410 /* If the object has been declared a non-network object, ignore it */
6411 if (cschem->schemtype == NONETWORK) return 0;
6413 /* Always operate on the master schematic */
6414 pschem = (cschem->schemtype == SECONDARY) ? cschem->symschem : cschem;
6416 /* Stop immediately if the netlist is invalid */
6417 if (pschem->valid == False) return -1;
6419 /* Otherwise, recursively call checkvalid() on all subobjects. */
6420 /* Use the parts list of the object, not the calls, because calls */
6421 /* may have been removed. */
6423 for (cgen = pschem->plist; cgen < pschem->plist + pschem->parts; cgen++) {
6424 if (IS_OBJINST(*cgen)) {
6425 cinst = TOOBJINST(cgen);
6427 if (cinst->thisobject->symschem != NULL)
6428 callobj = cinst->thisobject->symschem;
6429 else
6430 callobj = cinst->thisobject;
6432 /* Don't infinitely recurse if object is on its own schematic */
6434 if (callobj == pschem) continue;
6436 /* If there is a symbol, don't check its parts, but check if */
6437 /* its netlist has been checkvalid. */
6439 if ((cinst->thisobject->symschem != NULL) &&
6440 (cinst->thisobject->labels == NULL) &&
6441 (cinst->thisobject->polygons == NULL) &&
6442 (cinst->thisobject->valid == False))
6443 return -1;
6445 /* Recursive call on subschematic */
6446 if (checkvalid(callobj) == -1)
6447 return -1;
6450 return 0; /* All subnetlists and own netlist are valid */
6453 /*----------------------------------------------------------------------*/
6454 /* Free memory allocated to temporary labels generated for the netlist */
6455 /*----------------------------------------------------------------------*/
6457 void freetemplabels(objectptr cschem)
6459 genericptr *cgen;
6460 objinstptr cinst;
6461 objectptr callobj;
6463 /* Recursively call freetemplabels() on all subobjects, a la gennetlist() */
6464 /* Use the parts list of the object, not the calls, because calls */
6465 /* may have been removed. */
6467 for (cgen = cschem->plist; cgen < cschem->plist + cschem->parts; cgen++) {
6468 if (IS_OBJINST(*cgen)) {
6470 cinst = TOOBJINST(cgen);
6471 if (cinst->thisobject->symschem != NULL)
6472 callobj = cinst->thisobject->symschem;
6473 else
6474 callobj = cinst->thisobject;
6476 /* Don't infinitely recurse if object is on its own schematic */
6477 if (callobj != cschem) freetemplabels(callobj);
6479 /* Also free the temp labels of any associated symbol */
6480 if (cinst->thisobject->symschem != NULL) freetemplabels(cinst->thisobject);
6483 /* Free any temporary labels which have been created */
6485 else if (IS_LABEL(*cgen)) {
6486 labelptr clab = TOLABEL(cgen);
6487 /* int tmpval = (int)(cgen - cschem->plist); (jdk) */
6488 if (clab->string->type != FONT_NAME) {
6489 genericptr *tgen;
6491 clab = TOLABEL(cgen);
6492 freelabel(clab->string);
6493 free(clab);
6494 for (tgen = cgen + 1; tgen < cschem->plist + cschem->parts; tgen++)
6495 *(tgen - 1) = *tgen;
6496 cschem->parts--;
6497 cgen--;
6503 /*----------------------------------------------------------------------*/
6504 /* Free memory allocated for netlists, ports, and calls */
6505 /*----------------------------------------------------------------------*/
6507 void freenets(objectptr cschem)
6509 CalllistPtr calls, cptr;
6510 PortlistPtr ports, pptr;
6511 genericptr *cgen;
6512 objinstptr cinst;
6513 objectptr callobj;
6515 /* Recursively call freenets() on all subobjects, a la gennetlist() */
6516 /* Use the parts list of the object, not the calls, because calls */
6517 /* may have been removed. */
6519 if (cschem->schemtype == PRIMARY || cschem->schemtype == SECONDARY ||
6520 (cschem->schemtype == SYMBOL && cschem->symschem == NULL)) {
6521 for (cgen = cschem->plist; cgen < cschem->plist + cschem->parts; cgen++) {
6522 if (IS_OBJINST(*cgen)) {
6524 cinst = TOOBJINST(cgen);
6525 if (cinst->thisobject->symschem != NULL)
6526 callobj = cinst->thisobject->symschem;
6527 else
6528 callobj = cinst->thisobject;
6530 /* Don't infinitely recurse if object is on its own schematic */
6531 if (callobj != cschem) freenets(callobj);
6533 /* Also free the netlist of any associated symbol */
6534 if (cinst->thisobject->symschem != NULL) freenets(cinst->thisobject);
6539 /* Free the allocated structures for this object */
6541 for (calls = cschem->calls; calls != NULL;) {
6542 cptr = calls->next;
6543 freecalls(calls);
6544 calls = cptr;
6546 cschem->calls = NULL;
6548 for (ports = cschem->ports; ports != NULL;) {
6549 pptr = ports->next;
6550 free(ports);
6551 ports = pptr;
6553 cschem->ports = NULL;
6555 freenetlist(cschem);
6557 cschem->traversed = False;
6558 cschem->valid = False;
6559 freegenlist(cschem->highlight.netlist);
6560 cschem->highlight.netlist = NULL;
6561 cschem->highlight.thisinst = NULL;
6564 /*----------------------------------------------------------------------*/
6565 /* Free the global pin list */
6566 /*----------------------------------------------------------------------*/
6568 void freeglobals()
6570 LabellistPtr labellist, llist;
6572 for (labellist = global_labels; labellist != NULL;) {
6573 llist = labellist->next;
6575 /* Labels in the global list are temporary and must be deallocated */
6576 freelabel(labellist->label->string);
6577 free(labellist->label);
6579 freegenlist((Genericlist *)labellist);
6580 labellist = llist;
6582 global_labels = NULL;
6585 /*----------------------------------------------------------------------*/
6586 /* Get rid of locally-defined pin names */
6587 /*----------------------------------------------------------------------*/
6589 void clearlocalpins(objectptr cschem)
6591 NetnamePtr netnames, nextname;
6593 for (netnames = cschem->netnames; netnames != NULL; ) {
6594 nextname = netnames->next;
6595 if (netnames->localpin != NULL) {
6596 freelabel(netnames->localpin);
6598 free(netnames);
6599 netnames = nextname;
6601 cschem->netnames = NULL;
6604 /*----------------------------------------------------------------------*/
6605 /* Handle lists of included files */
6606 /*----------------------------------------------------------------------*/
6608 /*----------------------------------------------------------------------*/
6609 /* check_included() --- check if a file has already been included. */
6610 /* Check by inode instead of filename so that we cannot trick the */
6611 /* program by giving, e.g., one relative path and one full path. */
6612 /*----------------------------------------------------------------------*/
6614 Boolean check_included(char *filename)
6616 struct stat filestatus;
6617 int numi;
6619 if (stat(filename, &filestatus) == 0) {
6620 if (included_files == NULL) return FALSE;
6621 for (numi = 0; *(included_files + numi) != (ino_t)NULL; numi++) {
6622 if (*(included_files + numi) == filestatus.st_ino) return TRUE;
6625 return FALSE;
6628 /*----------------------------------------------------------------------*/
6629 /* append_included() --- update the list of included files by adding */
6630 /* the inode of filename to the list. */
6631 /*----------------------------------------------------------------------*/
6633 void append_included(char *filename)
6635 struct stat filestatus;
6636 int numi;
6638 if (stat(filename, &filestatus) == 0) {
6640 if (included_files == NULL) {
6641 included_files = (ino_t *)malloc(2 * sizeof(ino_t));
6642 *included_files = filestatus.st_ino;
6643 *(included_files + 1) = (ino_t)NULL;
6645 else {
6646 for (numi = 0; *(included_files + numi) != (ino_t)NULL; numi++);
6648 included_files = (ino_t *)realloc(included_files,
6649 (++numi + 1) * sizeof(ino_t));
6651 *(included_files + numi - 1) = filestatus.st_ino;
6652 *(included_files + numi) = (ino_t)NULL;
6655 else {
6656 Wprintf("Error: Cannot stat include file \"%s\"\n", filename);
6660 /*----------------------------------------------------------------------*/
6662 void free_included()
6664 if (included_files != NULL) {
6665 free(included_files);
6666 included_files = NULL;
6670 /*-------------------------------------------------------------------------*/