Corrected a long-standing error in which ending text with a literal
[xcircuit.git] / netlist.c
blob3af9481d7296bf5e1dbb68c63f375c01e5af73ec
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 /* Ignore any instance that is specifically marked non-netlistable */
1139 if (callinst->style & INST_NONETLIST) continue;
1141 /* Determine where the hierarchy continues downward */
1143 if (callinst->thisobject->symschem != NULL)
1144 callobj = callinst->thisobject->symschem;
1145 else
1146 callobj = callinst->thisobject;
1148 /* Always ignore any object on its own schematic */
1150 if (callobj == pschem) continue;
1152 callsymb = callinst->thisobject;
1154 /* Note: callobj is the next schematic in the hierarchy. */
1155 /* callsymb is the next visible object in the hierarchy, */
1156 /* which may be either a schematic or a symbol. */
1158 /*--------------------------------------------------------------*/
1159 /* For object instances which are their own schematics (i.e., */
1160 /* have netlist elements), don't rely on any pin list but make */
1161 /* a survey of how polygons connect into the object. */
1162 /*--------------------------------------------------------------*/
1164 if (callsymb->symschem == NULL
1165 && callobj->schemtype != FUNDAMENTAL
1166 && callobj->schemtype != TRIVIAL) {
1168 /* Fprintf(stdout, "*** Analyzing connections from %s"
1169 " to instance of %s\n", cschem->name,
1170 callinst->thisobject->name); */
1172 /* Look for pins connecting to pins in the object */
1174 for (lseek = pschem->labels; lseek != NULL; lseek = lseek->next) {
1175 if (lseek->cschem != cschem) continue;
1176 else if ((lseek->cinst != NULL) && (lseek->cinst != callinst))
1177 continue;
1178 olabel = lseek->label;
1179 searchconnect(&(olabel->position), 1, callinst, lseek->subnets);
1180 /* if we've encountered a unique instance, then con- */
1181 /* tinue past all other instances using this label. */
1182 if (lseek->cinst != NULL)
1183 while (lseek->next && (lseek->next->label == lseek->label))
1184 lseek = lseek->next;
1187 /* Look for polygon ends connecting into the object */
1189 for (pseek = pschem->polygons; pseek != NULL; pseek = pseek->next) {
1190 if (pseek->cschem != cschem) continue;
1191 tpoly = pseek->poly;
1192 searchconnect(tpoly->points, tpoly->number, callinst, pseek->subnets);
1195 /* For each call to a schematic or symbol which is NOT the */
1196 /* one under consideration, see if it touches or overlaps */
1197 /* the object under consideration. Search for connections */
1198 /* between the two objects. */
1200 calcinstbbox(cgen, &ibllx, &iblly, &iburx, &ibury);
1202 /* Only need to look forward from the current position. */
1203 for (k = i + 1; k < cschem->parts; k++) {
1205 iseek = cschem->plist + k;
1206 if (IS_OBJINST(*iseek)) {
1207 calcinstbbox(iseek, &sbllx, &sblly, &sburx, &sbury);
1209 /* Check intersection of the two object instances; */
1210 /* don't do a search if they are disjoint. */
1212 if ((ibllx <= sburx) && (iburx >= sbllx) &&
1213 (iblly <= sbury) && (ibury >= sblly)) {
1214 isib = TOOBJINST(iseek);
1215 search_on_siblings(callinst, isib, NULL,
1216 ibllx, iblly, iburx, ibury);
1222 /*----------------------------------------------------------*/
1223 /* Recursively call gencalls() on the schematic. */
1224 /*----------------------------------------------------------*/
1226 if (callobj->traversed == False)
1227 gencalls(callobj);
1229 /*----------------------------------------------------------*/
1230 /* Create a call to the object callsymb from object cschem */
1231 /*----------------------------------------------------------*/
1233 addcall(cschem, callobj, callinst);
1235 /*----------------------------------------------------------*/
1236 /* Search again on symbol pins to generate calls to ports. */
1237 /*----------------------------------------------------------*/
1239 UResetCTM(&locctm);
1240 UPreMultCTM(&locctm, callinst->position, callinst->scale,
1241 callinst->rotation);
1243 for (lseek = callsymb->labels; lseek != NULL; lseek = lseek->next) {
1244 /* LabellistPtr slab; (jdk) */
1245 /* labelptr slabel; (jdk) */
1247 if (lseek->cschem != callsymb) continue;
1248 else if ((lseek->cinst != NULL) && (lseek->cinst != callinst))
1249 continue;
1251 olabel = lseek->label;
1252 netto = (Genericlist *)lseek;
1254 /* Translate pin position back to object cschem */
1255 UTransformbyCTM(&locctm, &(olabel->position), &xpos, 1);
1257 /* What net in the calling object connects to this point? */
1258 netfrom = pointtonet(cschem, callinst, &xpos);
1260 /* If there's no net, we make one */
1261 if (netfrom == NULL)
1262 netfrom = make_tmp_pin(cschem, callinst, &xpos, netto);
1264 /* Generate a port call for a global signal if the */
1265 /* global label appears in the symbol (9/29/04). This */
1266 /* is a change from previous behavior, which was to not */
1267 /* make the call. */
1269 if ((netto->subnets == 0) && (netto->net.id < 0))
1270 mergenets(pschem, netfrom, netto);
1272 /* Attempt to generate a port in the object. */
1273 addport(callobj, netto);
1275 /* Generate the call to the port */
1276 if (addportcall(pschem, netfrom, netto) == FALSE) {
1278 // If object is "dot" then copy the bus from the
1279 // net to the dot. The dot takes on whatever
1280 // dimension the bus is.
1282 if (strstr(callobj->name, "::dot") != NULL) {
1283 copy_bus(netto, netfrom);
1285 else {
1286 Fprintf(stderr, "Error: attempt to connect bus size "
1287 "%d in %s to bus size %d in %s\n",
1288 netfrom->subnets, cschem->name,
1289 netto->subnets, callobj->name);
1293 /* if we've encountered a unique instance, then continue */
1294 /* past all other instances using this label. */
1295 if (lseek->cinst != NULL)
1296 while (lseek->next && (lseek->next->label == lseek->label))
1297 lseek = lseek->next;
1300 /*----------------------------------------------------------*/
1301 /* If after all that, no ports were called, then remove the */
1302 /* call to this object instance. However, we should check */
1303 /* for info labels, because the device may produce output */
1304 /* for the netlist even if it has no declared ports. This */
1305 /* is irrespective of the presence of pin labels---if the */
1306 /* symbol is "trivial", the netlist connections are */
1307 /* resolved in the level of the hierarchy above, and there */
1308 /* may be no ports, but the call list must retain the call */
1309 /* to ensure that the netlist output is generated. */
1310 /*----------------------------------------------------------*/
1312 if (pschem->calls->ports == NULL)
1313 if (pschem->infolabels == FALSE)
1314 removecall(pschem, pschem->calls); /* Remove the call */
1320 /*----------------------------------------------------------------------*/
1321 /* Translate a net number down in the calling hierarchy */
1322 /*----------------------------------------------------------------------*/
1324 int translatedown(int rnet, int portid, objectptr nextobj)
1326 PortlistPtr nport;
1327 int downnet = 0;
1329 for (nport = nextobj->ports; nport != NULL; nport = nport->next) {
1330 if (nport->portid == portid) {
1331 downnet = nport->netid;
1332 break;
1335 return downnet;
1338 /*----------------------------------------------------------------------*/
1339 /* Translate a netlist up in the calling hierarchy */
1340 /* */
1341 /* This routine creates a new netlist header which needs to be freed */
1342 /* by the calling routine. */
1343 /* */
1344 /* Note that if the entire netlist cannot be translated up, then the */
1345 /* routine returns NULL. This could be modified to return the part of */
1346 /* the network that can be translated up. */
1347 /*----------------------------------------------------------------------*/
1349 Genericlist *translateup(Genericlist *rlist, objectptr thisobj,
1350 objectptr nextobj, objinstptr nextinst)
1352 PortlistPtr nport;
1353 CalllistPtr ccall;
1354 int portid = 0;
1355 int upnet = 0;
1356 int rnet, lbus;
1357 buslist *sbus;
1358 Genericlist *tmplist;
1360 tmplist = (Genericlist *)malloc(sizeof(Genericlist));
1361 tmplist->subnets = 0;
1362 tmplist->net.id = 0;
1363 copy_bus(tmplist, rlist);
1365 for (lbus = 0;; ) {
1366 if (rlist->subnets == 0)
1367 rnet = rlist->net.id;
1368 else {
1369 sbus = rlist->net.list + lbus;
1370 rnet = sbus->netid;
1372 for (nport = nextobj->ports; nport != NULL; nport = nport->next) {
1373 if (nport->netid == rnet) {
1374 portid = nport->portid;
1375 break;
1379 upnet = 0;
1380 for (ccall = thisobj->calls; ccall != NULL; ccall = ccall->next) {
1381 if (ccall->callinst == nextinst) {
1382 for (nport = ccall->ports; nport != NULL; nport = nport->next) {
1383 if (nport->portid == portid) {
1384 upnet = nport->netid;
1385 break;
1388 if (nport != NULL) break;
1391 if (upnet == 0) {
1392 freegenlist(tmplist);
1393 return NULL;
1395 else {
1396 if (tmplist->subnets == 0) {
1397 tmplist->net.id = upnet;
1399 else {
1400 sbus = tmplist->net.list + lbus;
1401 sbus->netid = upnet;
1402 sbus->subnetid = getsubnet(upnet, thisobj);
1405 if (++lbus >= rlist->subnets) break;
1407 return tmplist;
1410 /*----------------------------------------------------------------------*/
1411 /* Check whether the indicated polygon is already resolved into the */
1412 /* netlist of the object hierarchy described by seltop. */
1413 /* Return the netlist if resolved, NULL otherwise. The netlist */
1414 /* returned is referred (translated) upward through the calling */
1415 /* hierarchy to the topmost object containing that net or those nets. */
1416 /* This topmost object is returned in parameter topobj. */
1417 /* Note that the netlist returned does not necessarily correspond to */
1418 /* any object in the top level. It is allocated and must be freed by */
1419 /* the calling routine. */
1420 /*----------------------------------------------------------------------*/
1422 Genericlist *is_resolved(genericptr *rgen, pushlistptr seltop, objectptr *topobj)
1424 objectptr thisobj = seltop->thisinst->thisobject;
1425 objectptr pschem;
1426 PolylistPtr pseek;
1427 LabellistPtr lseek;
1428 Genericlist *rlist = NULL, *newlist;
1430 pschem = (thisobj->schemtype == SECONDARY) ? thisobj->symschem : thisobj;
1432 /* Recursively call self, since we have to back out from the bottom of */
1433 /* the stack. */
1435 if (seltop->next != NULL) {
1436 rlist = is_resolved(rgen, seltop->next, topobj);
1438 /* Translate network ID up the hierarchy to the topmost object in which */
1439 /* the network exists. */
1441 if (rlist != NULL) {
1442 newlist = translateup(rlist, pschem, seltop->next->thisinst->thisobject,
1443 seltop->next->thisinst);
1444 if (newlist == NULL)
1445 /* Net does not exist upwards of this object. Pass net ID */
1446 /* upward with "topobj" unchanged. */
1447 return rlist;
1448 else {
1449 freegenlist(rlist);
1450 rlist = newlist;
1454 else {
1456 /* Find the net ID for the listed object, which should be in the object */
1457 /* on the bottom of the pushlist stack. */
1459 if (IS_POLYGON(*rgen)) {
1460 for (pseek = pschem->polygons; pseek != NULL; pseek = pseek->next) {
1461 if (pseek->poly == TOPOLY(rgen)) {
1462 rlist = (Genericlist *)pseek;
1463 break;
1467 else if (IS_LABEL(*rgen)) {
1468 for (lseek = pschem->labels; lseek != NULL; lseek = lseek->next) {
1469 if (lseek->label == TOLABEL(rgen)) {
1470 rlist = (Genericlist *)lseek;
1471 break;
1476 if (rlist != NULL) {
1477 /* Make a copy of the netlist header from this element */
1478 newlist = (Genericlist *)malloc(sizeof(Genericlist));
1479 newlist->subnets = 0;
1480 copy_bus(newlist, rlist);
1481 rlist = newlist;
1485 /* done: (jdk) */
1486 *topobj = (rlist == NULL) ? NULL : seltop->thisinst->thisobject;
1487 return rlist;
1490 /*--------------------------------------------------------------*/
1491 /* Highlight all the polygons and pin labels in a network */
1492 /* (recursively, downward). Pin labels are drawn only on the */
1493 /* topmost schematic object. */
1494 /* Returns true if some part of the hierarchy declared a net to */
1495 /* be highlighted. */
1496 /* mode = 1 highlight, mode = 0 erase */
1497 /*--------------------------------------------------------------*/
1499 Boolean highlightnet(objectptr cschem, objinstptr cinst, int netid, u_char mode)
1501 CalllistPtr calls;
1502 PortlistPtr ports;
1503 PolylistPtr plist;
1504 LabellistPtr llist;
1505 polyptr cpoly;
1506 labelptr clabel;
1507 objinstptr ccinst;
1508 int netto, locnetid, lbus;
1509 int curcolor = AUXCOLOR;
1510 Boolean rval = FALSE;
1511 objectptr pschem;
1513 SetForeground(dpy, areawin->gc, curcolor);
1515 pschem = (cschem->schemtype == SECONDARY) ? cschem->symschem : cschem;
1517 for (plist = pschem->polygons; plist != NULL; plist = plist->next) {
1518 if (plist->cschem != cschem) continue;
1519 cpoly = plist->poly;
1520 for (lbus = 0;;) {
1521 if (plist->subnets == 0)
1522 locnetid = plist->net.id;
1523 else
1524 locnetid = (plist->net.list + lbus)->netid;
1525 if (locnetid == netid) {
1526 /* Fprintf(stdout, " >> Found polygon belonging to net %d at (%d, %d)\n",
1527 locnetid, cpoly->points[0].x, cpoly->points[0].y); */
1528 if (mode == 0 && cpoly->color != curcolor) {
1529 curcolor = cpoly->color;
1530 XTopSetForeground(curcolor);
1532 UDrawPolygon(cpoly, xobjs.pagelist[areawin->page]->wirewidth);
1533 break;
1535 if (++lbus >= plist->subnets) break;
1539 /* Highlight labels if they belong to the top-level object */
1541 if (cschem == topobject) {
1542 for (llist = pschem->labels; llist != NULL; llist = llist->next) {
1543 if (llist->cschem != cschem) continue;
1544 else if ((llist->cinst != NULL) && (llist->cinst != cinst)) continue;
1545 clabel = llist->label;
1546 for (lbus = 0;;) {
1547 if (llist->subnets == 0)
1548 locnetid = llist->net.id;
1549 else
1550 locnetid = (llist->net.list + lbus)->netid;
1551 if (locnetid == netid) {
1552 if (clabel->string->type == FONT_NAME) { /* don't draw temp labels */
1553 if ((mode == 0) && (clabel->color != curcolor)) {
1554 curcolor = clabel->color;
1555 UDrawString(clabel, curcolor, cinst);
1557 else
1558 UDrawString(clabel, DOFORALL, cinst);
1559 /* Fprintf(stdout, " >> Found label belonging to net "
1560 "%d at (%d, %d)\n",
1561 locnetid, clabel->position.x,
1562 clabel->position.y); */
1565 break;
1567 if (++lbus >= llist->subnets) break;
1569 /* if we've encountered a unique instance, then continue */
1570 /* past all other instances using this label. */
1571 if (llist->cinst != NULL)
1572 while (llist->next && (llist->next->label == llist->label))
1573 llist = llist->next;
1576 /* Highlight all pins connecting this net to symbols */
1580 /* Connectivity recursion */
1582 for (calls = pschem->calls; calls != NULL; calls = calls->next) {
1583 if (calls->cschem != cschem) continue;
1584 for (ports = calls->ports; ports != NULL; ports = ports->next) {
1585 if (ports->netid == netid) {
1586 ccinst = calls->callinst;
1588 /* Recurse only on objects for which network polygons are visible */
1589 /* from the calling object: i.e., non-trivial, non-fundamental */
1590 /* objects acting as their own schematics. */
1592 UPushCTM();
1593 UPreMultCTM(DCTM, ccinst->position, ccinst->scale, ccinst->rotation);
1595 if (ccinst->thisobject->symschem == NULL &&
1596 ccinst->thisobject->schemtype != FUNDAMENTAL &&
1597 ccinst->thisobject->schemtype != TRIVIAL) {
1599 netto = translatedown(netid, ports->portid, calls->callobj);
1601 /* Fprintf(stdout, " > Calling object %s at (%d, %d)\n",
1602 calls->callobj->name, ccinst->position.x, ccinst->position.y); */
1603 /* Fprintf(stdout, " > Net translation from %d to %d (port %d)\n",
1604 netid, netto, ports->portid); */
1606 if (highlightnet(calls->callobj, calls->callinst, netto, mode))
1607 rval = TRUE;
1609 else {
1610 /* Otherwise (symbols, fundamental, trivial, etc., objects), we */
1611 /* highlight the pin position of the port. */
1612 if ((clabel = PortToLabel(ccinst, ports->portid)))
1613 UDrawXDown(clabel);
1615 UPopCTM();
1619 return rval;
1622 /*----------------------------------------------------------------------*/
1623 /* Highlight whatever nets are listed in the current object instance, */
1624 /* if any. */
1625 /*----------------------------------------------------------------------*/
1627 void highlightnetlist(objectptr nettop, objinstptr cinst, u_char mode)
1629 int lbus, netid;
1630 buslist *sbus;
1631 Genericlist *netlist = cinst->thisobject->highlight.netlist;
1632 objinstptr nextinst = cinst->thisobject->highlight.thisinst;
1634 if (netlist == NULL) return;
1636 for (lbus = 0;; ) {
1637 if (netlist->subnets == 0)
1638 netid = netlist->net.id;
1639 else {
1640 sbus = netlist->net.list + lbus;
1641 netid = sbus->netid;
1643 highlightnet(nettop, nextinst, netid, mode);
1644 if (++lbus >= netlist->subnets) break;
1647 /* If we are erasing, remove the netlist entry from the object */
1648 if (mode == (u_char)0) {
1649 freegenlist(netlist);
1650 cinst->thisobject->highlight.netlist = NULL;
1651 cinst->thisobject->highlight.thisinst = NULL;
1655 /*----------------------------------------------------------------------*/
1656 /* Push the matrix stack for each object (instance) until the indicated */
1657 /* object is reached. This works similarly to highlightnet() above, */
1658 /* but makes calls according to the hierarchy described by the */
1659 /* pushlistptr parameter. */
1660 /* Returns the number of stack objects to pop after we're done. */
1661 /*----------------------------------------------------------------------*/
1663 int pushnetwork(pushlistptr seltop, objectptr nettop)
1665 pushlistptr cursel = seltop;
1666 objinstptr sinst;
1667 int rno = 0;
1669 while ((cursel->thisinst->thisobject != nettop) && (cursel->next != NULL)) {
1670 cursel = cursel->next;
1671 sinst = cursel->thisinst;
1672 UPushCTM();
1673 UPreMultCTM(DCTM, sinst->position, sinst->scale, sinst->rotation);
1674 rno++;
1677 if (cursel->thisinst->thisobject != nettop) {
1678 Fprintf(stderr, "Error: object does not exist in calling stack!\n");
1679 rno = 0;
1682 return rno;
1685 /*----------------------------------------------------------------------*/
1686 /* Determine if two netlists match. If "mode" is MATCH_EXACT, the */
1687 /* net numbers and subnet numbers must all be the same. If */
1688 /* MATCH_SUBNETS, then the subnet numbers must be the same. If */
1689 /* MATCH_SIZE, then they need only have the same number of subnets. */
1690 /*----------------------------------------------------------------------*/
1692 Boolean match_buses(Genericlist *list1, Genericlist *list2, int mode)
1694 int i;
1695 buslist *bus1, *bus2;
1697 if (list1->subnets != list2->subnets) {
1698 // A wire (no subnets) matches a bus of 1 subnet. All others
1699 // are non-matching.
1701 if (list1->subnets != 0 && list2->subnets != 0)
1702 return FALSE;
1703 else if (list1->subnets != 1 && list2->subnets != 1)
1704 return FALSE;
1707 if (mode == MATCH_SIZE) return TRUE;
1709 if (list1->subnets == 0) {
1710 if (mode == MATCH_SUBNETS) return TRUE;
1711 if (list2->subnets != 0) {
1712 bus2 = list2->net.list + 0;
1713 if (list1->net.id != bus2->netid) return FALSE;
1715 else if (list1->net.id != list2->net.id) return FALSE;
1717 else if (list2->subnets == 0) {
1718 if (mode == MATCH_SUBNETS) return TRUE;
1719 bus1 = list1->net.list + 0;
1720 if (bus1->netid != list2->net.id) return FALSE;
1722 else {
1723 for (i = 0; i < list1->subnets; i++) {
1724 bus1 = list1->net.list + i;
1725 bus2 = list2->net.list + i;
1726 /* A subnetid of < 0 indicates an unassigned bus */
1727 if ((bus1->subnetid != -1) && (bus1->subnetid != bus2->subnetid))
1728 return FALSE;
1730 if (mode == MATCH_SUBNETS) return TRUE;
1732 for (i = 0; i < list1->subnets; i++) {
1733 bus1 = list1->net.list + i;
1734 bus2 = list2->net.list + i;
1735 if (bus1->netid != bus2->netid)
1736 return FALSE;
1739 return TRUE;
1742 /*----------------------------------------------------------------------*/
1743 /* Copy the netlist structure from one netlist element to another */
1744 /*----------------------------------------------------------------------*/
1746 void copy_bus(Genericlist *dest, Genericlist *source)
1748 buslist *sbus, *dbus;
1749 int i;
1751 if (dest->subnets > 0)
1752 free(dest->net.list);
1754 dest->subnets = source->subnets;
1755 if (source->subnets == 0)
1756 dest->net.id = source->net.id;
1757 else {
1758 dest->net.list = (buslist *)malloc(dest->subnets * sizeof(buslist));
1759 for (i = 0; i < dest->subnets; i++) {
1760 sbus = source->net.list + i;
1761 dbus = dest->net.list + i;
1762 dbus->netid = sbus->netid;
1763 dbus->subnetid = sbus->subnetid;
1768 /*------------------------------------------------------*/
1769 /* Create a new "temporary" label object for a pin */
1770 /* This type of label is never drawn, so it doesn't */
1771 /* need font info. It is identified as "temporary" by */
1772 /* this lack of a leading font record. */
1773 /*------------------------------------------------------*/
1775 Genericlist *new_tmp_pin(objectptr cschem, XPoint *pinpt, char *pinstring,
1776 char *prefix, Genericlist *netlist)
1778 labelptr *newlabel;
1779 stringpart *strptr;
1781 if (pinpt == NULL) {
1782 Fprintf(stderr, "NULL label location!\n");
1783 return NULL;
1786 NEW_LABEL(newlabel, cschem);
1787 labeldefaults(*newlabel, LOCAL, pinpt->x, pinpt->y);
1788 (*newlabel)->anchor = 0;
1789 (*newlabel)->color = DEFAULTCOLOR;
1790 strptr = (*newlabel)->string;
1791 strptr->type = TEXT_STRING;
1792 if (pinstring != NULL) {
1793 strptr->data.string = (char *)malloc(strlen(pinstring));
1794 strcpy(strptr->data.string, pinstring);
1796 else {
1797 strptr->data.string = textprintnet(prefix, NULL, netlist);
1800 /* Add label to object's netlist and return a pointer to the */
1801 /* netlist entry. */
1803 return (addpin(cschem, NULL, *newlabel, netlist));
1806 /*------------------------------------------------------*/
1807 /* Create a label for use in the list of global nets. */
1808 /* This label is like a temporary label (new_tmp_pin) */
1809 /* except that it is not represented in any object. */
1810 /* The string contains the verbatim contents of any */
1811 /* parameter substitutions in the original label. */
1812 /*------------------------------------------------------*/
1814 labelptr new_global_pin(labelptr clabel, objinstptr cinst)
1816 labelptr newlabel;
1818 newlabel = (labelptr) malloc(sizeof(label));
1819 newlabel->type = LABEL;
1820 labeldefaults(newlabel, GLOBAL, 0, 0);
1821 newlabel->anchor = 0;
1822 newlabel->color = DEFAULTCOLOR;
1823 free(newlabel->string);
1824 newlabel->string = stringcopyall(clabel->string, cinst);
1826 /* Add label to the global netlist and return a pointer to */
1827 /* the netlist entry. */
1829 return newlabel;
1832 /*----------------------------------------------------------------------*/
1833 /* Create a temporary I/O pin (becomes part of netlist and also part of */
1834 /* the object itself). */
1835 /*----------------------------------------------------------------------*/
1837 Genericlist *make_tmp_pin(objectptr cschem, objinstptr cinst, XPoint *pinpt,
1838 Genericlist *sublist)
1840 LabellistPtr lseek;
1841 objectptr pschem;
1842 char *pinstring = NULL;
1843 /* buslist *sbus; (jdk) */
1844 /* int lbus; (jdk) */
1845 Genericlist *netlist, *tmplist, newlist;
1847 newlist.subnets = 0;
1848 newlist.net.id = 0;
1850 /* Primary schematic (contains the netlist) */
1851 pschem = (cschem->schemtype == SECONDARY) ? cschem->symschem : cschem;
1853 /* Determine a netlist for this pin */
1855 netlist = pointtonet(cschem, cinst, pinpt);
1856 if (netlist == NULL) {
1857 newlist.net.id = netmax(pschem) + 1;
1858 netlist = &newlist;
1861 /* If there is any other pin at this location, don't make another */
1862 /* one. If there is already a temporary pin associated with the */
1863 /* net, use its name. */
1865 else {
1866 for (lseek = pschem->labels; lseek != NULL; lseek = lseek->next) {
1867 if (lseek->cschem != cschem) continue;
1868 else if ((lseek->cinst != NULL) && (lseek->cinst != cinst)) continue;
1869 tmplist = (Genericlist *)lseek;
1870 if (match_buses(netlist, tmplist, MATCH_EXACT)) {
1871 if (proximity(&(lseek->label->position), pinpt))
1872 return (Genericlist *)lseek;
1873 else if (lseek->label->string->type == TEXT_STRING)
1874 pinstring = lseek->label->string->data.string;
1876 /* if we've encountered a unique instance, then continue past */
1877 /* all other instances using this label. */
1878 if (lseek->cinst != NULL)
1879 while (lseek->next && (lseek->next->label == lseek->label))
1880 lseek = lseek->next;
1883 return (new_tmp_pin(cschem, pinpt, pinstring, "ext", netlist));
1886 /*--------------------------------------------------------------*/
1887 /* Search for connections into a non-symbol subcircuit, based */
1888 /* on various combinations of polygon and pin label overlaps. */
1889 /*--------------------------------------------------------------*/
1891 int searchconnect(XPoint *points, int number, objinstptr cinst, int subnets)
1893 XPoint *tmppts, *tpt, *tpt2, *endpt, *endpt2, *pinpt, opinpt;
1894 objinstptr tinst;
1895 genericptr *cgen;
1896 polyptr tpoly;
1897 labelptr tlab;
1898 objectptr tobj, cobj = cinst->thisobject;
1899 LabellistPtr tseek;
1900 PolylistPtr pseek;
1901 int i; /* , lbus, sub_bus; (jdk) */
1902 int found = 0;
1904 /* Generate temporary polygon in the coordinate system of */
1905 /* the object instance in question */
1907 tmppts = (XPoint *)malloc(number * sizeof(XPoint));
1908 InvTransformPoints(points, tmppts, number,
1909 cinst->position, cinst->scale, cinst->rotation);
1910 /* Fprintf(stdout, "Info: translated polygon w.r.t. object %s\n", */
1911 /* cinst->thisobject->name); */
1913 /* Recursion on all appropriate sub-schematics. */
1914 /* (Use parts list, not call list, as call list may not have created yet) */
1916 for (i = 0; i < cobj->parts; i++) {
1917 cgen = cobj->plist + i;
1918 if (IS_OBJINST(*cgen)) {
1919 tinst = TOOBJINST(cgen);
1920 if (tinst->thisobject->symschem == NULL) {
1921 tobj = tinst->thisobject;
1922 if (tobj->schemtype != FUNDAMENTAL && tobj->schemtype != TRIVIAL)
1923 found += searchconnect(tmppts, number, tinst, subnets);
1928 for (endpt = tmppts; endpt < tmppts + EndPoint(number); endpt++) {
1929 endpt2 = endpt + NextPoint(number);
1930 for (i = 0; i < cobj->parts; i++) {
1931 cgen = cobj->plist + i;
1932 if (!IS_OBJINST(*cgen)) continue;
1933 tinst = TOOBJINST(cgen);
1935 /* Look at the object only (symbol, or schematic if it has no symbol) */
1936 tobj = tinst->thisobject;
1938 /* Search for connections to pin labels */
1940 for (tseek = tobj->labels; tseek != NULL; tseek = tseek->next) {
1941 tlab = tseek->label;
1942 UTransformPoints(&(tlab->position), &opinpt, 1, tinst->position,
1943 tinst->scale, tinst->rotation);
1944 if (onsegment(endpt2, endpt, &opinpt)) {
1945 /* Fprintf(stdout, "%s connects to pin %s of %s in %s\n", */
1946 /* ((number > 1) ? "Polygon" : "Pin"), */
1947 /* tlab->string + 2, tinst->thisobject->name, */
1948 /* cinst->thisobject->name); */
1949 make_tmp_pin(cobj, cinst, &opinpt, (Genericlist *)tseek);
1950 found += (tseek->subnets == 0) ? 1 : tseek->subnets;
1951 break;
1956 for (pseek = cobj->polygons; pseek != NULL; pseek = pseek->next) {
1957 tpoly = pseek->poly;
1959 /* Search for connections from segments passed to this */
1960 /* function to endpoints of polygons in the netlist. */
1962 pinpt = NULL;
1963 tpt = tpoly->points;
1964 tpt2 = tpoly->points + tpoly->number - 1;
1965 if (onsegment(endpt2, endpt, tpt)) pinpt = tpt;
1966 if (onsegment(endpt2, endpt, tpt2)) pinpt = tpt2;
1968 /* Create new pinlabel (only if there is not one already there) */
1970 if (pinpt != NULL) {
1971 make_tmp_pin(cobj, cinst, pinpt, (Genericlist *)pseek);
1972 found += (pseek->subnets == 0) ? 1 : pseek->subnets;
1977 endpt = tmppts;
1978 endpt2 = tmppts + EndPoint(number) - 1;
1980 /* Search for connections from endpoints passed to this */
1981 /* function to segments of polygons in the netlist. */
1983 for (pseek = cobj->polygons; pseek != NULL; pseek = pseek->next) {
1985 tpoly = pseek->poly;
1986 for (tpt = tpoly->points; tpt < tpoly->points
1987 + EndPoint(tpoly->number); tpt++) {
1988 tpt2 = tpt + NextPoint(tpoly->number);
1990 pinpt = NULL;
1991 if (onsegment(tpt2, tpt, endpt)) pinpt = endpt;
1992 if (onsegment(tpt2, tpt, endpt2)) pinpt = endpt2;
1994 /* Create new pinlabel (only if there is not one already there) */
1996 if (pinpt != NULL) {
1997 make_tmp_pin(cobj, cinst, pinpt, (Genericlist *)pseek);
1998 found += (pseek->subnets == 0) ? 1 : pseek->subnets;
2002 free(tmppts);
2003 return(found);
2006 /*----------------------------------------------------------------------*/
2007 /* Associate polygon with given netlist and add to the object's list */
2008 /* of network polygons (i.e., wires). */
2009 /*----------------------------------------------------------------------*/
2011 Genericlist *addpoly(objectptr cschem, polyptr poly, Genericlist *netlist)
2013 PolylistPtr newpoly;
2014 objectptr pschem;
2015 /* buslist *sbus; (jdk) */
2016 /* int lbus, sub_bus; (jdk) */
2018 /* Netlist is in the master schematic */
2019 pschem = (cschem->schemtype == SECONDARY) ? cschem->symschem : cschem;
2021 /* If this polygon is already in the list, then add an extra subnet */
2022 /* if necessary. */
2024 for (newpoly = pschem->polygons; newpoly != NULL; newpoly = newpoly->next) {
2025 if (newpoly->poly == poly) {
2026 if (!match_buses((Genericlist *)newpoly, netlist, MATCH_EXACT)) {
2027 Fprintf(stderr, "addpoly: Error in bus assignment\n");
2028 return NULL;
2030 return (Genericlist *)newpoly;
2034 /* Create a new entry and link to polygon list of this object */
2036 newpoly = (PolylistPtr) malloc(sizeof(Polylist));
2037 newpoly->cschem = cschem;
2038 newpoly->poly = poly;
2039 newpoly->subnets = 0;
2040 copy_bus((Genericlist *)newpoly, netlist);
2041 newpoly->next = pschem->polygons;
2042 pschem->polygons = newpoly;
2044 return (Genericlist *)newpoly;
2047 /*-------------------------------------------------------------------------*/
2049 long zsign(long a, long b)
2051 if (a > b) return 1;
2052 else if (a < b) return -1;
2053 else return 0;
2056 /*----------------------------------------------------------------------*/
2057 /* Promote a single net to a bus. The bus size will be equal to the */
2058 /* value "subnets". */
2059 /*----------------------------------------------------------------------*/
2061 void promote_net(objectptr cschem, Genericlist *netfrom, int subnets)
2063 Genericlist *netref = NULL;
2064 CalllistPtr calls;
2065 PortlistPtr ports;
2066 PolylistPtr plist;
2067 LabellistPtr llist;
2068 int netid, firstid, lbus; /* curid, (jdk) */
2069 buslist *sbus;
2070 Boolean foundlabel;
2072 /* If no promotion is required, don't do anything */
2073 if (netfrom->subnets == subnets) return;
2075 /* It "netfrom" is already a bus, but of different size than */
2076 /* subnets, then it cannot be changed. */
2078 if (netfrom->subnets != 0) {
2079 Fprintf(stderr, "Attempt to change the size of a bus!\n");
2080 return;
2083 netid = netfrom->net.id;
2085 /* If "subnets" is 1, then "netfrom" can be promoted regardless. */
2086 /* Otherwise, if the "netfrom" net is used in any calls, then it */
2087 /* cannot be promoted. */
2089 if (subnets > 1) {
2090 for (calls = cschem->calls; calls != NULL; calls = calls->next)
2091 for (ports = calls->ports; ports != NULL; ports = ports->next)
2092 if (ports->netid == netid) {
2093 Fprintf(stderr, "Cannot promote net to bus: Net already connected"
2094 " to single-wire port\n");
2095 return;
2097 firstid = netmax(cschem) + 1;
2100 for (plist = cschem->polygons; plist != NULL; plist = plist->next)
2101 if ((plist->subnets == 0) && (plist->net.id == netid)) {
2102 plist->subnets = subnets;
2103 plist->net.list = (buslist *)malloc(subnets * sizeof(buslist));
2104 for (lbus = 0; lbus < subnets; lbus++) {
2105 sbus = plist->net.list + lbus;
2106 sbus->netid = (lbus == 0) ? netid : firstid + lbus;
2107 sbus->subnetid = lbus; /* By default, number from zero */
2109 netref = (Genericlist *)plist;
2112 /* It's possible for a label without bus notation to be attached */
2113 /* to this net. */
2115 foundlabel = FALSE;
2116 for (llist = cschem->labels; llist != NULL; llist = llist->next)
2117 if ((llist->subnets == 0) && (llist->net.id == netid)) {
2118 llist->subnets = subnets;
2119 llist->net.list = (buslist *)malloc(subnets * sizeof(buslist));
2120 for (lbus = 0; lbus < subnets; lbus++) {
2121 sbus = llist->net.list + lbus;
2122 sbus->netid = (lbus == 0) ? netid : firstid + lbus;
2123 sbus->subnetid = lbus; /* By default, number from zero */
2125 netref = (Genericlist *)llist;
2126 foundlabel = TRUE;
2129 /* We need to create a temp label associated with this net to */
2130 /* encompass the promoted bus size. If this bus is later attached */
2131 /* to a known bus, that bus name will be canonical. If no bus name */
2132 /* is ever assigned to the bus, this temp one will be used. */
2134 if (!foundlabel) {
2135 XPoint *pinpos;
2136 pinpos = NetToPosition(netid, cschem);
2137 new_tmp_pin(cschem, pinpos, NULL, "int", netref);
2141 /*----------------------------------------------------------------------*/
2142 /* Change any part of the netlist "testlist" that matches the net IDs */
2143 /* of "orignet" to the net IDs of "newnet". It is assumed that the */
2144 /* lengths and sub-bus numbers of "orignet" and "newnet" match. */
2145 /*----------------------------------------------------------------------*/
2147 Boolean mergenetlist(objectptr cschem, Genericlist *testlist,
2148 Genericlist *orignet, Genericlist *newnet)
2150 int obus, onetid, osub, nsub, nnetid, tbus;
2151 buslist *sbus;
2152 Boolean rval = FALSE;
2154 for (obus = 0;;) {
2155 if (orignet->subnets == 0) {
2156 onetid = orignet->net.id;
2157 osub = -1;
2159 else {
2160 sbus = orignet->net.list + obus;
2161 onetid = sbus->netid;
2162 osub = sbus->subnetid;
2165 if (newnet->subnets == 0) {
2166 nnetid = newnet->net.id;
2167 nsub = -1;
2169 else {
2170 sbus = newnet->net.list + obus;
2171 nnetid = sbus->netid;
2172 nsub = sbus->subnetid;
2175 if (testlist->subnets == 0) {
2176 if (testlist->net.id == onetid) {
2177 rval = TRUE;
2178 if (orignet->subnets == 0) {
2179 testlist->net.id = nnetid;
2180 return TRUE;
2182 else {
2183 /* Promote testlist to a bus subnet of size 1 */
2184 testlist->subnets = 1;
2185 testlist->net.list = (buslist *)malloc(sizeof(buslist));
2186 sbus = testlist->net.list;
2187 sbus->netid = nnetid;
2188 sbus->subnetid = nsub;
2189 return rval;
2194 for (tbus = 0; tbus < testlist->subnets; tbus++) {
2195 /* If the sub-bus numbers match, then change the net */
2196 /* ID to the new net number. If the sub-bus is */
2197 /* unnamed (has no associated bus-notation label), */
2198 /* then it can take on the net and subnet numbers. */
2200 sbus = testlist->net.list + tbus;
2202 if (sbus->netid == onetid) {
2203 if (sbus->subnetid == osub) {
2204 sbus->netid = nnetid;
2205 sbus->subnetid = nsub;
2206 rval = TRUE;
2208 else {
2209 labelptr blab = NetToLabel(nnetid, cschem);
2210 if (blab == NULL) {
2211 Fprintf(stderr, "Warning: isolated subnet?\n");
2212 sbus->netid = nnetid;
2213 /* Keep subnetid---but, does newnet need to be promoted? */
2214 return TRUE;
2216 else if (blab->string->type != FONT_NAME) {
2217 sbus->netid = nnetid;
2218 sbus->subnetid = nsub;
2219 rval = TRUE;
2220 Fprintf(stderr, "Warning: Unexpected subnet value in mergenetlist!\n");
2225 if (++obus >= orignet->subnets) break;
2227 return rval;
2230 /*----------------------------------------------------------------------*/
2231 /* Combine two networks in an object's linked-list Netlist */
2232 /* Parameters: cschem - pointer to object containing netlist */
2233 /* orignet - original netlist to be changed */
2234 /* newnet - new netlist to be changed to */
2235 /* */
2236 /* Attempts to merge different subnets in a bus are thwarted. */
2237 /*----------------------------------------------------------------------*/
2239 Boolean netmerge(objectptr cschem, Genericlist *orignet, Genericlist *newnet)
2241 PolylistPtr plist;
2242 LabellistPtr llist;
2243 CalllistPtr calls;
2244 PortlistPtr ports;
2245 Genericlist savenet;
2246 int i, net;
2247 buslist *obus, *nbus;
2248 Boolean rval;
2250 /* Trivial case; do nothing */
2251 if (match_buses(orignet, newnet, MATCH_EXACT)) return TRUE;
2253 /* Disallow an attempt to convert a global net to a local net: */
2254 /* The global net ID always dominates! */
2256 if ((orignet->subnets == 0) && (newnet->subnets == 0) &&
2257 (orignet->net.id < 0) && (newnet->net.id > 0)) {
2258 int globnet = orignet->net.id;
2259 orignet->net.id = newnet->net.id;
2260 newnet->net.id = globnet;
2263 /* Check that the lists of changes are compatible. It appears to be */
2264 /* okay to have non-matching buses (although they should not be */
2265 /* merged), as this is a valid style in which buses do not have taps */
2266 /* but the sub-buses are explicitly called out with labels. In such */
2267 /* case, the polygons of different sub-buses touch, and this routine */
2268 /* rejects them as being incompatible. */
2270 if (!match_buses(orignet, newnet, MATCH_SUBNETS)) {
2271 if (match_buses(orignet, newnet, MATCH_SIZE)) {
2272 labelptr nlab;
2273 /* If only the subnet numbers don't match up, check if */
2274 /* "orignet" has a temp label. */
2275 nbus = orignet->net.list;
2276 if ((nlab = NetToLabel(nbus->netid, cschem)) != NULL) {
2277 if (nlab->string->type != FONT_NAME)
2278 goto can_merge;
2281 else
2282 Fprintf(stderr, "netmerge warning: non-matching bus subnets touching.\n");
2283 return FALSE;
2286 can_merge:
2288 /* If orignet is a bus size 1 and newnet is a wire, then promote */
2289 /* newnet to a bus size 1. */
2291 if (orignet->subnets == 1 && newnet->subnets == 0) {
2292 net = newnet->net.id;
2293 newnet->subnets = 1;
2294 newnet->net.list = (buslist *)malloc(sizeof(buslist));
2295 obus = orignet->net.list;
2296 nbus = newnet->net.list;
2297 nbus->subnetid = obus->subnetid;
2298 nbus->netid = net;
2301 /* Make a copy of the net so we don't overwrite it */
2302 savenet.subnets = 0;
2303 copy_bus(&savenet, orignet);
2305 rval = FALSE;
2306 for (plist = cschem->polygons; plist != NULL; plist = plist->next)
2307 if (mergenetlist(cschem, (Genericlist *)plist, &savenet, newnet))
2308 rval = TRUE;
2310 for (llist = cschem->labels; llist != NULL; llist = llist->next)
2311 if (mergenetlist(cschem, (Genericlist *)llist, &savenet, newnet)) {
2312 int pinnet;
2313 char *newtext;
2314 rval = TRUE;
2316 /* Because nets that have been merged away may be re-used later, */
2317 /* change the name of any temporary labels to the new net number */
2319 if (llist->label->string->type != FONT_NAME) {
2320 newtext = llist->label->string->data.string;
2321 if (sscanf(newtext + 3, "%d", &pinnet) == 1) {
2322 if (pinnet == savenet.net.id) {
2323 *(newtext + 3) = '\0';
2324 llist->label->string->data.string = textprintnet(newtext,
2325 NULL, newnet);
2326 free(newtext);
2332 if (rval) {
2334 /* Reflect the net change in the object's call list, if it has one. */
2336 for (calls = cschem->calls; calls != NULL; calls = calls->next) {
2337 for (ports = calls->ports; ports != NULL; ports = ports->next) {
2338 if (newnet->subnets == 0) {
2339 if (ports->netid == savenet.net.id)
2340 ports->netid = newnet->net.id;
2342 else {
2343 for (i = 0; i < newnet->subnets; i++) {
2344 obus = savenet.net.list + i;
2345 nbus = newnet->net.list + i;
2346 if (ports->netid == obus->netid)
2347 ports->netid = nbus->netid;
2354 /* Free the copy of the bus that we made, if we made one */
2355 if (savenet.subnets > 0) free(savenet.net.list);
2357 return rval;
2360 /*----------------------------------------------------------------------*/
2361 /* Wrapper to netmerge() to make sure change is made both to the symbol */
2362 /* and schematic, if both exist. */
2363 /*----------------------------------------------------------------------*/
2365 Boolean mergenets(objectptr cschem, Genericlist *orignet, Genericlist *newnet)
2367 Boolean merged;
2369 if (cschem->symschem != NULL)
2370 merged = netmerge(cschem->symschem, orignet, newnet);
2371 if (netmerge(cschem, orignet, newnet))
2372 merged = TRUE;
2374 return merged;
2377 /*----------------------------------------------------------------------*/
2378 /* Remove a call to an object instance from the call list of cschem */
2379 /*----------------------------------------------------------------------*/
2381 void removecall(objectptr cschem, CalllistPtr dontcallme)
2383 CalllistPtr lastcall, seeklist;
2384 PortlistPtr ports, savelist;
2386 /* find the instance call before this one and link it to the one following */
2388 lastcall = NULL;
2389 for (seeklist = cschem->calls; seeklist != NULL; seeklist = seeklist->next) {
2390 if (seeklist == dontcallme)
2391 break;
2392 lastcall = seeklist;
2395 if (seeklist == NULL) {
2396 Fprintf(stderr, "Error in removecall(): Call does not exist!\n");
2397 return;
2400 if (lastcall == NULL)
2401 cschem->calls = dontcallme->next;
2402 else
2403 lastcall->next = dontcallme->next;
2405 ports = dontcallme->ports;
2406 while (ports != NULL) {
2407 savelist = ports;
2408 ports = ports->next;
2409 free (savelist);
2411 free(dontcallme);
2414 /*----------------------------------------------------------------------*/
2415 /* Add a pin label to the netlist */
2416 /* If cschem == NULL, add the pin to the list of global pins. */
2417 /* The new label entry in the netlist gets a copy of the netlist */
2418 /* "netlist". */
2419 /*----------------------------------------------------------------------*/
2421 Genericlist *addpin(objectptr cschem, objinstptr cinst, labelptr pin,
2422 Genericlist *netlist)
2424 LabellistPtr srchlab, newlabel, lastlabel = NULL;
2425 objectptr pschem;
2426 /* buslist *sbus; (jdk) */
2427 /* int lbus, sub_bus; (jdk) */
2429 pschem = (cschem->schemtype == SECONDARY) ? cschem->symschem : cschem;
2431 for (srchlab = pschem->labels; srchlab != NULL; srchlab = srchlab->next) {
2432 if (srchlab->label == pin) {
2433 if (!match_buses(netlist, (Genericlist *)srchlab, MATCH_EXACT)) {
2434 if (srchlab->cinst == cinst) {
2435 Fprintf(stderr, "addpin: Error in bus assignment\n");
2436 return NULL;
2439 else if (srchlab->cinst == NULL)
2440 return (Genericlist *)srchlab;
2441 break; /* Stop at the first record for this label */
2443 lastlabel = srchlab;
2446 /* Create a new entry and link to label list of the object */
2448 newlabel = (LabellistPtr) malloc(sizeof(Labellist));
2449 newlabel->cschem = cschem;
2450 newlabel->cinst = cinst;
2451 newlabel->label = pin;
2452 newlabel->subnets = 0;
2453 copy_bus((Genericlist *)newlabel, netlist);
2455 /* Always put the specific (instanced) cases in front of */
2456 /* generic cases for the same label. */
2458 /* Generic case---make it the last record for this label */
2459 if ((cinst == NULL) && (lastlabel != NULL)) {
2460 while ((srchlab != NULL) && (srchlab->label == pin)) {
2461 lastlabel = srchlab;
2462 srchlab = srchlab->next;
2465 if (lastlabel != NULL) {
2466 newlabel->next = srchlab;
2467 lastlabel->next = newlabel;
2469 else {
2470 newlabel->next = pschem->labels;
2471 pschem->labels = newlabel;
2473 return (Genericlist *)newlabel;
2476 /*----------------------------------------------------------------------*/
2477 /* Add a pin label to the list of global net names. */
2478 /* The new label entry in the netlist gets a copy of the netlist */
2479 /* "netlist" and a copy of label "pin" containing the *instance* value */
2480 /* of the text. */
2481 /*----------------------------------------------------------------------*/
2483 Genericlist *addglobalpin(objectptr cschem, objinstptr cinst, labelptr pin,
2484 Genericlist *netlist)
2486 LabellistPtr srchlab, newlabel, lastlabel = NULL;
2488 if (cinst == NULL) {
2489 Fprintf(stderr, "Error: Global pin does not have an associated instance!\n");
2490 return NULL;
2493 for (srchlab = global_labels; srchlab != NULL; srchlab = srchlab->next) {
2494 if (srchlab->label == pin) {
2495 if (!match_buses(netlist, (Genericlist *)srchlab, MATCH_EXACT)) {
2496 if (srchlab->cinst == cinst) {
2497 Fprintf(stderr, "addglobalpin: Error in bus assignment\n");
2498 return NULL;
2501 else if (srchlab->cinst == NULL)
2502 return (Genericlist *)srchlab;
2503 break; /* Stop at the first record for this label */
2505 lastlabel = srchlab;
2508 /* Create a new entry and link to label list of the object */
2510 newlabel = (LabellistPtr) malloc(sizeof(Labellist));
2511 newlabel->cschem = cschem;
2512 newlabel->cinst = cinst;
2513 newlabel->label = new_global_pin(pin, cinst);
2514 newlabel->subnets = 0;
2515 copy_bus((Genericlist *)newlabel, netlist);
2517 if (lastlabel != NULL) {
2518 newlabel->next = srchlab;
2519 lastlabel->next = newlabel;
2521 else {
2522 newlabel->next = global_labels;
2523 global_labels = newlabel;
2525 return (Genericlist *)newlabel;
2528 /*----------------------------------------------------------------------*/
2529 /* Allocate memory for the new call list element */
2530 /* Define the values for the new call list element */
2531 /* Insert new call into call list */
2532 /* The new call is at the beginning of the list. */
2533 /*----------------------------------------------------------------------*/
2535 void addcall(objectptr cschem, objectptr callobj, objinstptr callinst)
2537 CalllistPtr newcall;
2538 objectptr pschem;
2540 /* Netlist is on the master schematic */
2541 pschem = (cschem->schemtype == SECONDARY) ? cschem->symschem : cschem;
2543 newcall = (CalllistPtr) malloc(sizeof(Calllist));
2544 newcall->cschem = cschem;
2545 newcall->callobj = callobj;
2546 newcall->callinst = callinst;
2547 newcall->devindex = -1;
2548 newcall->devname = NULL;
2549 newcall->ports = NULL;
2550 newcall->next = pschem->calls;
2551 pschem->calls = newcall;
2554 /*----------------------------------------------------------------------*/
2555 /* Add a port to the object cschem which connects net "netto" to */
2556 /* the calling object. One port is created for each net ID in "netto" */
2557 /* (which may be a bus). The port contains the net ID in the called */
2558 /* object. The port may already exist, in which case this routine does */
2559 /* nothing. */
2560 /*----------------------------------------------------------------------*/
2562 void addport(objectptr cschem, Genericlist *netto)
2564 PortlistPtr newport, seekport;
2565 int portid = 0, netid, lbus;
2566 buslist *sbus;
2567 Boolean duplicate;
2569 for (lbus = 0;;) {
2571 if (netto->subnets == 0)
2572 netid = netto->net.id;
2573 else {
2574 sbus = netto->net.list + lbus;
2575 netid = sbus->netid;
2578 /* If a port already exists for this net, don't add another one! */
2580 duplicate = FALSE;
2581 for (seekport = cschem->ports; seekport != NULL; seekport = seekport->next) {
2582 if (seekport->netid != netid) {
2583 if (seekport->portid > portid)
2584 portid = seekport->portid;
2586 else
2587 duplicate = TRUE;
2590 if (!duplicate) {
2591 portid++;
2593 newport = (PortlistPtr)malloc(sizeof(Portlist));
2594 newport->netid = netid;
2595 newport->portid = portid;
2597 if (cschem->ports != NULL)
2598 newport->next = cschem->ports;
2599 else
2600 newport->next = NULL;
2602 cschem->ports = newport;
2604 if (++lbus >= netto->subnets) break;
2608 /*----------------------------------------------------------------------*/
2609 /* Add a specific port connection from object cschem into the instance */
2610 /* cinst. This equates a net number in the calling object cschem */
2611 /* (netfrom) to a net number in the object instance being called */
2612 /* (netto). */
2613 /* */
2614 /* If we attempt to connect a bus of one size to a port of a different */
2615 /* size, return FALSE. Otherwise, add the call and return TRUE. */
2616 /*----------------------------------------------------------------------*/
2618 Boolean addportcall(objectptr cschem, Genericlist *netfrom, Genericlist *netto)
2620 CalllistPtr ccall;
2621 PortlistPtr seekport, sp, newport;
2622 objectptr instobj;
2623 objinstptr cinst;
2624 int lbus, netid_from, netid_to;
2625 buslist *sbus, *tbus;
2626 Boolean duplicate;
2628 /* The call that we need to add a port to is the first on */
2629 /* the list for cschem, as created by addcall(). */
2630 ccall = cschem->calls;
2631 instobj = ccall->callobj;
2632 cinst = ccall->callinst;
2634 if (netfrom->subnets != netto->subnets) {
2635 if (netfrom->subnets == 0) {
2636 /* It is possible that "netfrom" is an unlabeled polygon that */
2637 /* is implicitly declared a bus by its connection to this */
2638 /* port. If so, we promote "netfrom" to a bus but set the */
2639 /* subnet entries to negative values, since we don't know what */
2640 /* they are yet. */
2641 promote_net(cschem, netfrom, netto->subnets);
2644 /* Only other allowable condition is a bus size 1 connecting into */
2645 /* a single-wire port. However, one should consider promotion of */
2646 /* the pin in the target object to a bus on a per-instance basis. */
2647 /* This has not yet been done. . . */
2649 else if ((netfrom->subnets != 1) || (netto->subnets != 0)) {
2650 /* Let the caller report error information, because it knows more */
2651 return FALSE;
2655 for (lbus = 0;;) {
2656 buslist bsingf, bsingo;
2657 Genericlist subnet_from, subnet_other;
2659 if (netfrom->subnets == 0) {
2660 netid_from = netfrom->net.id;
2662 subnet_from.subnets = 0;
2663 subnet_from.net.id = netid_from;
2665 subnet_other.subnets = 0;
2667 else {
2668 sbus = netfrom->net.list + lbus;
2669 netid_from = sbus->netid;
2671 subnet_from.subnets = 1;
2672 subnet_from.net.list = &bsingf;
2673 bsingf.netid = netid_from;
2674 bsingf.subnetid = sbus->subnetid;
2676 bsingo.subnetid = lbus;
2677 subnet_other.subnets = 1;
2678 subnet_other.net.list = &bsingo;
2680 if (netto->subnets == 0) {
2681 netid_to = netto->net.id;
2683 else {
2684 tbus = netto->net.list + lbus;
2685 netid_to = tbus->netid;
2688 /* Check the ports of the instance's object for the one matching */
2689 /* the "netto" net ID. */
2691 duplicate = FALSE;
2692 for (seekport = instobj->ports; seekport != NULL;
2693 seekport = seekport->next) {
2694 if (seekport->netid == netid_to) {
2696 /* If there is already an entry for this port, then */
2697 /* we may need to merge nets in cschem. */
2699 for (sp = ccall->ports; sp != NULL; sp = sp->next)
2700 if (sp->portid == seekport->portid) {
2701 if (sp->netid != netid_from) {
2702 if (netfrom->subnets == 0)
2703 subnet_other.net.id = sp->netid;
2704 else {
2705 bsingo.netid = sp->netid;
2706 bsingo.subnetid = getsubnet(bsingo.netid, cschem);
2708 if (!mergenets(cschem, &subnet_other, &subnet_from)) {
2709 /* Upon failure, see if we can merge the other */
2710 /* direction. */
2711 if (!mergenets(cschem, &subnet_from, &subnet_other))
2712 continue;
2713 else {
2714 if (subnet_from.subnets == 0)
2715 subnet_from.net.id = subnet_other.net.id;
2716 else {
2717 bsingf.netid = bsingo.netid;
2718 bsingf.subnetid = bsingo.subnetid;
2723 duplicate = TRUE;
2726 if (!duplicate) {
2727 newport = (PortlistPtr)malloc(sizeof(Portlist));
2728 newport->netid = netid_from;
2729 newport->portid = seekport->portid;
2730 newport->next = ccall->ports;
2731 ccall->ports = newport;
2733 break;
2736 if (++lbus >= netfrom->subnets) break;
2738 return TRUE;
2741 /*----------------------------------------------------------------------*/
2742 /* Find the net ID corresponding to the indicated port ID in the */
2743 /* indicated object. */
2744 /*----------------------------------------------------------------------*/
2746 int porttonet(objectptr cschem, int portno)
2748 PortlistPtr plist;
2750 for (plist = cschem->ports; plist != NULL; plist = plist->next) {
2751 if (plist->portid == portno)
2752 return plist->netid;
2754 return 0;
2757 /*----------------------------------------------------------------------*/
2758 /* Traverse netlist and return netid of polygon or pin on which the */
2759 /* indicated point is located. */
2760 /* If point is not on any polygon or does not match any pin position, */
2761 /* return NULL. */
2762 /* Labels which have a non-NULL "cinst" record are instance-dependent */
2763 /* and must match parameter "cinst". */
2764 /* */
2765 /* This routine checks to see if more than one net crosses the point */
2766 /* of interest, and merges the nets if found. */
2767 /* (the method has been removed and must be reinstated, presumably) */
2768 /*----------------------------------------------------------------------*/
2770 Genericlist *pointtonet(objectptr cschem, objinstptr cinst, XPoint *testpoint)
2772 XPoint *tpt, *tpt2;
2773 PolylistPtr ppoly;
2774 LabellistPtr plab;
2775 Genericlist *preturn;
2776 objectptr pschem; /* primary schematic */
2778 /* cschem is the object containing the point. However, if the object */
2779 /* is a secondary schematic, the netlist is located in the master. */
2781 pschem = (cschem->schemtype == SECONDARY) ? cschem->symschem : cschem;
2783 for (plab = pschem->labels; plab != NULL; plab = plab->next) {
2784 if (plab->cschem != cschem) continue;
2785 else if ((plab->cinst != NULL) && (plab->cinst != cinst)) continue;
2786 tpt = &(plab->label->position);
2787 if (proximity(tpt, testpoint))
2788 return (Genericlist *)plab;
2790 /* if we've encountered a unique instance, then continue past */
2791 /* all other instances using this label. */
2793 if (plab->cinst != NULL)
2794 while (plab->next && (plab->next->label == plab->label))
2795 plab = plab->next;
2798 /* Check against polygons. We use this part of the routine to see */
2799 /* if there are crossing wires on top of a port. If so, they are */
2800 /* merged together. */
2802 preturn = (Genericlist *)NULL;
2803 for (ppoly = pschem->polygons; ppoly != NULL; ppoly = ppoly->next) {
2804 if (ppoly->cschem != cschem) continue;
2805 for (tpt = ppoly->poly->points; tpt < ppoly->poly->points
2806 + EndPoint(ppoly->poly->number); tpt++) {
2807 tpt2 = tpt + NextPoint(ppoly->poly->number);
2809 if (onsegment(tpt, tpt2, testpoint)) {
2810 if (preturn == (Genericlist *)NULL)
2811 preturn = (Genericlist *)ppoly;
2812 else
2813 mergenets(pschem, (Genericlist *)ppoly, preturn);
2818 return preturn;
2821 /*----------------------------------------------------------------------*/
2822 /* localpin keeps track of temporary pin labels when flattening the */
2823 /* hierarchy without destroying the original pin names. */
2824 /*----------------------------------------------------------------------*/
2826 void makelocalpins(objectptr cschem, CalllistPtr clist, char *prefix)
2828 NetnamePtr netnames;
2829 PortlistPtr ports, plist;
2830 stringpart *locpin;
2831 int locnet, callnet;
2832 objectptr callobj = clist->callobj;
2834 /* Copy all net names which are passed from above through ports */
2836 for (ports = clist->ports; ports != NULL; ports = ports->next) {
2837 callnet = ports->netid;
2838 for (plist = callobj->ports; plist != NULL; plist = plist->next) {
2839 if (plist->portid == ports->portid) {
2840 locnet = plist->netid;
2841 locpin = nettopin(callnet, cschem, prefix);
2842 break;
2846 for (netnames = callobj->netnames; netnames != NULL; netnames = netnames->next)
2847 if (netnames->netid == locnet)
2848 break;
2850 if (netnames == NULL) {
2851 netnames = (NetnamePtr)malloc(sizeof(Netname));
2852 netnames->netid = locnet;
2853 netnames->localpin = stringcopy(locpin);
2854 netnames->next = callobj->netnames;
2855 callobj->netnames = netnames;
2860 /*----------------------------------------------------------------------*/
2861 /* Find the first point associated with the net "netid" in the object */
2862 /* cschem. */
2863 /*----------------------------------------------------------------------*/
2865 XPoint *NetToPosition(int netid, objectptr cschem)
2867 PolylistPtr plist;
2868 LabellistPtr llist;
2869 buslist *sbus;
2870 int lbus, locnetid; /* sub_bus, (jdk) */
2872 plist = cschem->polygons;
2873 for (; plist != NULL; plist = plist->next) {
2874 for (lbus = 0;;) {
2875 if (plist->subnets == 0) {
2876 locnetid = plist->net.id;
2878 else {
2879 sbus = plist->net.list + lbus;
2880 locnetid = sbus->netid;
2882 if (locnetid == netid) {
2883 return plist->poly->points;
2885 if (++lbus >= plist->subnets) break;
2889 llist = (netid < 0) ? global_labels : cschem->labels;
2890 for (; llist != NULL; llist = llist->next) {
2891 for (lbus = 0;;) {
2892 if (llist->subnets == 0) {
2893 locnetid = llist->net.id;
2895 else {
2896 sbus = llist->net.list + lbus;
2897 locnetid = sbus->netid;
2899 if (locnetid == netid) {
2900 return (&(llist->label->position));
2902 if (++lbus >= llist->subnets) break;
2905 return NULL;
2908 /*----------------------------------------------------------------------*/
2909 /* Find a label element for the given net number. In a symbol, this */
2910 /* will be the label representing the pin (or the first one found, if */
2911 /* the pin has multiple labels). If no label is found, return NULL. */
2912 /* Preferably choose a non-temporary label, if one exists */
2913 /*----------------------------------------------------------------------*/
2915 labelptr NetToLabel(int netid, objectptr cschem)
2917 LabellistPtr llist;
2918 labelptr standby = NULL;
2919 buslist *sbus;
2920 int lbus, locnetid;
2922 llist = (netid < 0) ? global_labels : cschem->labels;
2924 for (; llist != NULL; llist = llist->next) {
2925 for (lbus = 0;;) {
2926 if (llist->subnets == 0) {
2927 locnetid = llist->net.id;
2929 else {
2930 sbus = llist->net.list + lbus;
2931 locnetid = sbus->netid;
2933 if (locnetid == netid) {
2934 if (llist->label->string->type == FONT_NAME)
2935 return llist->label;
2936 else if (standby == NULL)
2937 standby = llist->label;
2939 if (++lbus >= llist->subnets) break;
2942 return standby;
2946 /*----------------------------------------------------------------------*/
2947 /* Find the subnet number of the given net. This routine should be */
2948 /* used only as a last resort in case the subnet number is not */
2949 /* available; it has to look through the polygon and label lists in */
2950 /* detail to find the specified net ID. */
2951 /*----------------------------------------------------------------------*/
2953 int getsubnet(int netid, objectptr cschem)
2955 PolylistPtr plist;
2956 LabellistPtr llist;
2957 buslist *sbus;
2958 int lbus, sub_bus, locnetid;
2960 plist = cschem->polygons;
2961 for (; plist != NULL; plist = plist->next) {
2962 for (lbus = 0;;) {
2963 if (plist->subnets == 0) {
2964 locnetid = plist->net.id;
2965 sub_bus = -1;
2967 else {
2968 sbus = plist->net.list + lbus;
2969 locnetid = sbus->netid;
2970 sub_bus = sbus->subnetid;
2972 if (locnetid == netid) {
2973 return sub_bus;
2975 if (++lbus >= plist->subnets) break;
2979 llist = (netid < 0) ? global_labels : cschem->labels;
2980 for (; llist != NULL; llist = llist->next) {
2981 for (lbus = 0;;) {
2982 if (llist->subnets == 0) {
2983 locnetid = llist->net.id;
2984 sub_bus = -1;
2986 else {
2987 sbus = llist->net.list + lbus;
2988 locnetid = sbus->netid;
2989 sub_bus = sbus->subnetid;
2991 if (locnetid == netid) {
2992 return sub_bus;
2994 if (++lbus >= llist->subnets) break;
2997 return -1;
3000 /*----------------------------------------------------------------------*/
3001 /* Find a pin name for the given net number */
3002 /* Either take the last pin name with the net, or generate a new pin, */
3003 /* assigning it a net number for a string. */
3004 /* prefix = NULL indicates spice netlist, but is also used for PCB-type */
3005 /* netlists because the calling hierarchy is generated elsewhere. */
3006 /*----------------------------------------------------------------------*/
3008 stringpart *nettopin(int netid, objectptr cschem, char *prefix)
3010 NetnamePtr netname;
3011 labelptr pinlab;
3012 LabellistPtr netlabel;
3013 char *newtext, *snew = NULL;
3014 XPoint *pinpos;
3015 int locnetid; /* lbus, (jdk) */
3016 /* buslist *sbus; (jdk) */
3017 Genericlist newnet;
3018 static stringpart *newstring = NULL;
3020 /* prefix is NULL for hierarchical (spice) netlists and for */
3021 /* internal netlist manipulation. */
3023 if (prefix == NULL) {
3024 pinlab = NetToLabel(netid, cschem);
3025 if (pinlab != NULL) {
3027 /* If this is a "temp label", regenerate the name according to */
3028 /* the actual net number, if it does not match. This prevents */
3029 /* unrelated temp labels from getting the same name. */
3031 if (pinlab->string->type != FONT_NAME) {
3032 if (sscanf(pinlab->string->data.string + 3, "%d", &locnetid) == 1) {
3033 if (locnetid != netid) {
3034 newtext = pinlab->string->data.string;
3035 newtext[3] = '\0';
3036 newnet.subnets = 0;
3037 newnet.net.id = netid;
3038 pinlab->string->data.string = textprintnet(newtext, NULL, &newnet);
3039 free(newtext);
3043 return pinlab->string;
3045 else {
3046 /* If there's no label associated with this network, make one */
3047 /* called "intn" where n is the net number (netid). This is a */
3048 /* temp label (denoted by lack of a font specifier). */
3050 newnet.subnets = 0;
3051 newnet.net.id = netid;
3052 pinpos = NetToPosition(netid, cschem);
3053 netlabel = (LabellistPtr)new_tmp_pin(cschem, pinpos, NULL, "int", &newnet);
3054 return (netlabel) ? netlabel->label->string : NULL;
3058 /* Flattened (e.g., sim) netlists */
3060 for (netname = cschem->netnames; netname != NULL; netname = netname->next) {
3061 if (netname->netid == netid) {
3062 if (netname->localpin != NULL)
3063 return netname->localpin;
3064 break;
3068 /* Generate the string for the local instantiation of this pin */
3069 /* If this node does not have a pin, create one using the current */
3070 /* hierarchy prefix as the string. */
3072 pinlab = NetToLabel(netid, cschem);
3073 if (pinlab != NULL) {
3074 stringpart *tmpstring = pinlab->string;
3075 snew = textprint(tmpstring, NULL);
3077 else {
3078 snew = (char *)malloc(12);
3079 sprintf(snew, "int%d", netid);
3082 if (netid < 0) {
3083 newtext = snew;
3085 else {
3086 newtext = (char *)malloc(1 + strlen(snew) + strlen(prefix));
3087 sprintf(newtext, "%s%s", prefix, snew);
3088 free(snew);
3091 /* "newstring" is allocated only once and should not be free'd */
3092 /* by the calling routine. */
3094 if (newstring == NULL) {
3095 newstring = (stringpart *)malloc(sizeof(stringpart));
3096 newstring->nextpart = NULL;
3097 newstring->type = TEXT_STRING;
3099 else {
3100 free(newstring->data.string);
3102 newstring->data.string = newtext;
3103 return newstring;
3106 /*----------------------------------------------------------------------*/
3107 /* Find the net which connects to the given pin label */
3108 /* Return NULL (no net) if no match was found. */
3109 /*----------------------------------------------------------------------*/
3111 Genericlist *pintonet(objectptr cschem, objinstptr cinst, labelptr testpin)
3113 LabellistPtr seeklabel;
3114 int lbus, matched;
3115 buslist *sbus, *tbus;
3116 Genericlist netlist, *tmplist;
3118 /* check against local pins, if this pin is declared local */
3120 seeklabel = (testpin->pin == GLOBAL) ? global_labels : cschem->labels;
3122 netlist.subnets = 0;
3123 for (; seeklabel != NULL; seeklabel = seeklabel->next)
3124 if (!stringcomprelaxed(seeklabel->label->string, testpin->string, cinst))
3126 /* Quick simple case: no bus */
3127 if (seeklabel->subnets == 0)
3128 return (Genericlist *)seeklabel;
3130 tmplist = break_up_bus(testpin, cinst, (Genericlist *)seeklabel);
3132 /* It is necessary to be able to put together a netlist from */
3133 /* partial sources none of which may be complete. */
3135 if (tmplist != NULL) {
3136 if (netlist.subnets == 0) copy_bus(&netlist, tmplist);
3137 matched = 0;
3138 for (lbus = 0; lbus < tmplist->subnets; lbus++) {
3139 sbus = netlist.net.list + lbus;
3140 tbus = tmplist->net.list + lbus;
3141 if (sbus->netid == 0)
3142 sbus->netid = tbus->netid; /* copy forward */
3143 else if (tbus->netid == 0)
3144 tbus->netid = sbus->netid; /* copy back */
3145 if (sbus->netid != 0)
3146 matched++;
3148 if (matched == netlist.subnets) {
3149 free(netlist.net.list);
3150 return tmplist;
3155 if (netlist.subnets != 0) {
3156 free(netlist.net.list);
3157 return tmplist; /* Returns list with partial entries */
3159 return NULL; /* No matches found */
3162 #ifdef TCL_WRAPPER
3164 /*----------------------------------------------------------------------*/
3165 /* Automatic Schematic Generation stuff--- */
3166 /* Blow away all polygons in the schematic and replace them with a */
3167 /* simple rat's-nest (point-to-point). */
3168 /* */
3169 /* This routine is more of a proof-of-concept than a useful routine, */
3170 /* intended to be called only from the Tcl console. It is intended to */
3171 /* reveal most of the issues related to automatic schematic generation, */
3172 /* in which the netlist, or a portion of it, can be redrawn as objects */
3173 /* are moved around to maintain the relationships present in the */
3174 /* calls structure. */
3175 /*----------------------------------------------------------------------*/
3177 void ratsnest(objinstptr thisinst)
3179 CalllistPtr calls;
3180 PortlistPtr ports;
3181 PolylistPtr plist;
3182 LabellistPtr llist;
3183 objectptr pschem, cschem;
3184 objinstptr cinst;
3185 polyptr ppoly, *newpoly;
3186 buslist *sbus;
3187 int i, netid, points, sub_bus, lbus;
3188 XPoint portpos;
3189 Boolean result;
3191 cschem = thisinst->thisobject;
3192 pschem = (cschem->schemtype == SECONDARY) ? cschem->symschem : cschem;
3194 /* Go through the netlist and remove all polygons. Remove */
3195 /* the polygons from the object as well as from the netlist. */
3197 for (plist = pschem->polygons; plist != NULL; plist = plist->next) {
3198 ppoly = plist->poly;
3199 ppoly->type += REMOVE_TAG; /* tag for removal */
3201 freepolylist(&pschem->polygons);
3203 /* for each schematic (PRIMARY or SECONDARY), remove the tagged */
3204 /* elements. */
3206 for (i = 0; i < xobjs.pages; i++) {
3207 cinst = xobjs.pagelist[i]->pageinst;
3208 if (cinst && (cinst->thisobject->schemtype == SECONDARY) &&
3209 (cinst->thisobject->symschem == pschem)) {
3210 delete_tagged(cinst);
3212 else if (cinst == thisinst)
3213 delete_tagged(cinst);
3216 /* Now, for each net ID in the label list, run through the calls and */
3217 /* find all points in the calls connecting to that net. Link */
3218 /* them with as many polygons as needed. These polygons are point- */
3219 /* to-point lines, turning the schematic representation into a */
3220 /* "rat's-nest". */
3222 for (llist = pschem->labels; llist != NULL; llist = llist->next) {
3223 for (lbus = 0;;) {
3224 if (llist->subnets == 0) {
3225 netid = llist->net.id;
3226 sub_bus = -1;
3228 else {
3229 sbus = llist->net.list + lbus;
3230 netid = sbus->netid;
3231 sub_bus = sbus->subnetid;
3234 points = 0;
3235 for (calls = pschem->calls; calls != NULL; calls = calls->next) {
3237 /* Determine schematic object from the object called. Start a */
3238 /* new polygon any time we switch schematic pages. */
3240 if (calls->cschem != cschem)
3241 points = 0;
3242 cschem = calls->cschem;
3244 for (ports = calls->ports; ports != NULL; ports = ports->next) {
3245 if (ports->netid == netid) {
3247 /* Find the location of this port in the coordinates of cschem */
3249 result = PortToPosition(calls->callinst, ports->portid, &portpos);
3250 if (result == True) {
3251 points++;
3252 if (points == 1) {
3253 NEW_POLY(newpoly, cschem);
3254 polydefaults(*newpoly, 1, portpos.x, portpos.y);
3255 (*newpoly)->style |= UNCLOSED;
3256 (*newpoly)->color = RATSNESTCOLOR;
3257 addpoly(cschem, *newpoly, (Genericlist *)llist);
3259 else
3260 poly_add_point(*newpoly, &portpos);
3262 else {
3263 Fprintf(stderr, "Error: Cannot find pin connection in symbol!\n");
3264 /* Deal with error condition? */
3269 if (++lbus >= llist->subnets) break;
3273 /* Now, move all the labels to reconnect to their networks */
3275 /* (to be done) */
3277 /* Refresh the screen */
3278 drawarea(NULL, NULL, NULL);
3281 #endif
3283 /*----------------------------------------------------------------------*/
3284 /* Find a net or netlist in object cschem with the indicated name. */
3285 /* */
3286 /* This is the same routine as above, but finds the net with the given */
3287 /* name, or all nets which are a subset of the given name, if the name */
3288 /* is the name of a bus. Return NULL if no match was found. */
3289 /*----------------------------------------------------------------------*/
3291 Genericlist *nametonet(objectptr cschem, objinstptr cinst, char *netname)
3293 LabellistPtr seeklabel;
3294 stringpart newstring;
3296 /* Build a simple xcircuit string from "netname" (nothing malloc'd) */
3297 newstring.type = TEXT_STRING;
3298 newstring.nextpart = NULL;
3299 newstring.data.string = netname;
3301 /* Check against local networks */
3303 for (seeklabel = cschem->labels; seeklabel != NULL; seeklabel = seeklabel->next)
3304 if (!stringcomprelaxed(seeklabel->label->string, &newstring, cinst))
3305 return (Genericlist *)seeklabel;
3307 /* Check against global networks */
3309 for (seeklabel = global_labels; seeklabel != NULL; seeklabel = seeklabel->next)
3310 if (!stringcomprelaxed(seeklabel->label->string, &newstring, cinst))
3311 return (Genericlist *)seeklabel;
3313 return NULL;
3316 /*----------------------------------------------------------------------*/
3317 /* Check if two parts may have the same component index. This is true */
3318 /* for parts with parameterized pins, such as those in the "quadparts" */
3319 /* library. Compare the names of the ports in each instance. If none */
3320 /* match, then these are unique subparts of a single component, and we */
3321 /* should return FALSE. Otherwise, return TRUE. */
3322 /*----------------------------------------------------------------------*/
3324 Boolean samepart(CalllistPtr clist1, CalllistPtr clist2)
3326 PortlistPtr port;
3327 labelptr plab;
3328 char *pinname1, *pinname2;
3329 Boolean rvalue;
3331 if (clist1->callobj != clist2->callobj) return FALSE;
3333 rvalue = FALSE;
3334 for (port = clist1->ports; port != NULL; port = port->next) {
3335 plab = PortToLabel(clist1->callinst, port->portid);
3336 pinname1 = textprint(plab->string, clist1->callinst);
3337 pinname2 = textprint(plab->string, clist2->callinst);
3338 if (!strcmp(pinname1, pinname2)) rvalue = TRUE;
3339 free(pinname1);
3340 free(pinname2);
3342 return rvalue;
3345 /*----------------------------------------------------------------------*/
3346 /* Generate an index number for a device in a flat netlist format. */
3347 /* We ignore any values given to "index" (this is a to-do item, since */
3348 /* many schematics are only a single level of hierarchy, so it makes */
3349 /* sense to keep designated indices when possible), but do generate */
3350 /* independent index numbering for different device classes. */
3351 /*----------------------------------------------------------------------*/
3353 u_int devflatindex(char *devname)
3355 flatindex *fp = flatrecord;
3356 while (fp != NULL) {
3357 if (!strcmp(devname, fp->devname)) {
3358 return ++fp->index;
3360 fp = fp->next;
3362 fp = (flatindex *)malloc(sizeof(flatindex));
3363 fp->next = flatrecord;
3364 flatrecord = fp;
3365 fp->index = 1;
3366 fp->devname = devname;
3367 return 1;
3370 /*----------------------------------------------------------------------*/
3371 /* Free the list of per-class flat device indices */
3372 /*----------------------------------------------------------------------*/
3374 void freeflatindex()
3376 flatindex *fpnext, *fp = flatrecord;
3377 while (fp != NULL) {
3378 fpnext = fp->next;
3379 free(fp);
3380 fp = fpnext;
3382 flatrecord = NULL;
3385 /*---------------------------------------------------------*/
3386 /* Convert a number (up to 99999) to its base-36 equivalent */
3387 /*---------------------------------------------------------*/
3389 int convert_to_b36(int number)
3391 int b36idx, tmpidx;
3393 tmpidx = number;
3394 b36idx = (tmpidx / 10000) * 1679616;
3395 tmpidx %= 10000;
3396 b36idx += (tmpidx / 1000) * 46656;
3397 tmpidx %= 1000;
3398 b36idx += (tmpidx / 100) * 1296;
3399 tmpidx %= 100;
3400 b36idx += ((tmpidx / 10) * 36) + (tmpidx % 10);
3402 return b36idx;
3405 /*----------------------------------------------------------------------*/
3406 /* Generate an index number for this device. Count all devices having */
3407 /* the same device name (as printed in the netlist) in the calls of */
3408 /* the calling object (cfrom) */
3409 /* */
3410 /* Update (9/29/04): Remove any leading whitespace from the device */
3411 /* name to prevent treating, for example, "J?" and " J?" as separate */
3412 /* device types. */
3413 /* */
3414 /* Update (7/28/05): To allow SPICE device indices to include all */
3415 /* alphanumerics, we parse in base-36, and count in the subset of */
3416 /* base-36 that can be interpreted as decimal (1-9, 36-45, etc.) */
3417 /*----------------------------------------------------------------------*/
3419 u_int devindex(objectptr cfrom, CalllistPtr clist)
3421 CalllistPtr cptr, listfrom = cfrom->calls;
3422 objectptr devobj = clist->callobj;
3423 u_int *occupied, total, objindex, i, b36idx;
3424 char *devname, *cname;
3425 /* oparamptr ops; (jdk) */
3427 if (listfrom == NULL) return (u_int)0;
3428 if (clist->devindex >= 0) return clist->devindex;
3430 devname = (clist->devname == NULL) ? devobj->name : clist->devname;
3431 while (isspace(*devname)) devname++;
3433 /* Count total number of devices */
3434 for (cptr = listfrom, total = 0; cptr != NULL; cptr = cptr->next, total++);
3435 occupied = (u_int *)malloc(total * sizeof(u_int));
3436 objindex = 1;
3438 for (cptr = listfrom, total = 0; cptr != NULL; cptr = cptr->next, total++) {
3439 occupied[total] = 0;
3440 if (cptr == clist) continue;
3441 cname = (cptr->devname == NULL) ? cptr->callobj->name : cptr->devname;
3442 while (isspace(*cname)) cname++;
3443 if (!strcmp(cname, devname)) {
3444 occupied[total] = cptr->devindex;
3445 if (cptr->devindex == objindex) objindex++;
3449 b36idx = convert_to_b36(objindex);
3450 for (; objindex <= total; objindex++) {
3451 b36idx = convert_to_b36(objindex);
3452 for (i = 0; i < total; i++)
3453 if (occupied[i] == b36idx)
3454 break;
3455 if (i == total)
3456 break;
3458 free(occupied);
3460 clist->devindex = b36idx;
3461 return objindex;
3464 /*----------------------------------------------------------------------*/
3465 /* Generate and return a list of all info label prefixes in a design */
3466 /* This does not depend on the netlist being generated (i.e., does not */
3467 /* use CalllistPtr) */
3468 /*----------------------------------------------------------------------*/
3470 void genprefixlist(objectptr cschem, slistptr *modelist)
3472 int locpos;
3473 genericptr *pgen;
3474 labelptr plabel;
3475 objinstptr cinst;
3476 stringpart *strptr;
3477 slistptr modeptr;
3479 for (pgen = cschem->plist; pgen < cschem->plist + cschem->parts; pgen++) {
3480 if (IS_LABEL(*pgen)) {
3481 plabel = TOLABEL(pgen);
3482 if (plabel->pin == INFO) {
3484 strptr = findtextinstring(":", &locpos, plabel->string, cinst);
3485 if (locpos <= 0 || strptr == NULL)
3486 continue; /* null after netlist type designator, don't count */
3488 for (modeptr = *modelist; modeptr; modeptr = modeptr->next)
3489 if (!strncmp(modeptr->alias, strptr->data.string, locpos))
3490 break;
3492 if (modeptr == NULL) { /* Mode has not been enumerated */
3493 modeptr = (slistptr)malloc(sizeof(stringlist));
3494 modeptr->alias = (char *)malloc(locpos + 1);
3495 strncpy(modeptr->alias, strptr->data.string, locpos);
3496 modeptr->next = *modelist;
3497 *modelist = modeptr;
3501 else if (IS_OBJINST(*pgen)) {
3502 cinst = TOOBJINST(pgen);
3503 genprefixlist(cinst->thisobject, modelist);
3504 if (cinst->thisobject->symschem != NULL)
3505 genprefixlist(cinst->thisobject->symschem, modelist);
3510 /*----------------------------------------------------------------------*/
3511 /* Create and return an ordered list of info labels for the schematic */
3512 /* page cschem. A linked label list is returned, combining info labels */
3513 /* from all schematic pages related to cschem (master and slave pages). */
3514 /* It is the responsibility of the calling routine to free the linked */
3515 /* list. */
3516 /*----------------------------------------------------------------------*/
3518 LabellistPtr geninfolist(objectptr cschem, objinstptr cinst, char *mode)
3520 int locpos, j;
3521 int vmax;
3522 genericptr *pgen;
3523 labelptr plabel;
3524 LabellistPtr newllist, listtop, srchlist;
3525 u_char *strt;
3526 stringpart *strptr;
3528 listtop = NULL;
3529 vmax = 0;
3531 for (pgen = cschem->plist; pgen < cschem->plist + cschem->parts; pgen++) {
3532 if (IS_LABEL(*pgen)) {
3533 plabel = TOLABEL(pgen);
3534 if ((plabel->pin == INFO) && !textncomp(plabel->string, mode, cinst)) {
3536 if (mode[0] == '\0') {
3537 strptr = findtextinstring(":", &locpos, plabel->string, cinst);
3538 locpos--;
3540 else
3541 strptr = findstringpart(strlen(mode), &locpos, plabel->string, cinst);
3543 if (locpos < 0)
3544 continue; /* null after netlist type designator */
3546 strt = strptr->data.string + locpos + 1;
3548 if (*strt != ':') {
3549 if (sscanf(strt, "%d", &j) != 1) continue;
3551 /* Consider only positive-valued numbers. Negative */
3552 /* indices are handled by "writenet" by merging the */
3553 /* minus character into the mode name. */
3555 if (j < 0) continue;
3556 if (j >= vmax) vmax = j + 1;
3558 else
3559 j = ++vmax;
3561 newllist = (LabellistPtr)malloc(sizeof(Labellist));
3562 newllist->label = plabel;
3563 newllist->cschem = cschem;
3564 newllist->cinst = cinst;
3565 newllist->net.id = j; /* use this to find the ordering */
3566 newllist->subnets = 0; /* so free() doesn't screw up */
3568 /* Order the linked list */
3570 if ((listtop == NULL) || (listtop->net.id >= j)) {
3571 newllist->next = listtop;
3572 listtop = newllist;
3574 else {
3575 for (srchlist = listtop; srchlist != NULL; srchlist = srchlist->next) {
3576 if ((srchlist->next != NULL) && (srchlist->next->net.id >= j)) {
3577 newllist->next = srchlist->next;
3578 srchlist->next = newllist;
3579 break;
3581 else if (srchlist->next == NULL) {
3582 srchlist->next = newllist;
3583 newllist->next = NULL;
3590 return listtop;
3593 /*----------------------------------------------------------------------*/
3594 /* Trivial default pin name lookup. Parse an object's pin labels for */
3595 /* pin names. Return the text of the nth pin name, as counted by the */
3596 /* position in the object's parts list. If there are fewer than (n+1) */
3597 /* pin labels in the object, return NULL. Otherwise, return the pin */
3598 /* name in a malloc'd character string which the caller must free. */
3599 /*----------------------------------------------------------------------*/
3601 char *defaultpininfo(objinstptr cinst, int pidx)
3603 genericptr *pgen;
3604 labelptr plabel;
3605 objectptr cschem = cinst->thisobject;
3606 int count = 0;
3607 char *stxt;
3609 for (pgen = cschem->plist; pgen < cschem->plist + cschem->parts; pgen++) {
3610 if (IS_LABEL(*pgen)) {
3611 plabel = TOLABEL(pgen);
3612 if (plabel->pin == LOCAL) {
3613 if (count == pidx) {
3614 stxt = textprint(plabel->string, cinst);
3615 return stxt;
3617 count++;
3621 return NULL;
3624 /*----------------------------------------------------------------------*/
3625 /* Parse an object's info labels for pin names. This is *not* a */
3626 /* netlist function. The nth pin name is returned, or NULL if not */
3627 /* found. Return value is a malloc'd string which the caller must */
3628 /* free. */
3629 /*----------------------------------------------------------------------*/
3631 char *parsepininfo(objinstptr cinst, char *mode, int pidx)
3633 genericptr *pgen;
3634 labelptr plabel;
3635 u_char *strt, *fnsh;
3636 int slen, i, locpos;
3637 objectptr cschem = cinst->thisobject;
3638 stringpart *strptr;
3639 char *sout;
3640 int count = 0;
3642 for (pgen = cschem->plist; pgen < cschem->plist + cschem->parts; pgen++) {
3643 if (IS_LABEL(*pgen)) {
3644 plabel = TOLABEL(pgen);
3645 if (plabel->pin == INFO) {
3646 slen = stringlength(plabel->string, True, cinst);
3647 for (i = 1; i < slen; i++) {
3648 strptr = findstringpart(i, &locpos, plabel->string, cinst);
3649 if (locpos >= 0 && *(strptr->data.string + locpos) == ':') break;
3651 /* Currently we are not checking against "mode". . .*/
3652 /* interpret all characters after the colon */
3654 for (++i; i < slen; i++) {
3655 strptr = findstringpart(i, &locpos, plabel->string, cinst);
3656 if (locpos >= 0) {
3658 /* Do for all text characters */
3659 strt = strptr->data.string + locpos;
3660 if (*strt == '%') {
3661 strt++;
3662 i++;
3663 if (*strt == 'p') { /* Pin found! */
3664 if (count == pidx) { /* Get pin text */
3665 strt++;
3666 fnsh = strt + 1;
3667 while (*fnsh != ' ' && *fnsh != '\0') fnsh++;
3668 sout = malloc(fnsh - strt + 1);
3669 strncpy(sout, strt, fnsh - strt);
3670 return sout;
3672 count++;
3680 return NULL;
3683 /*----------------------------------------------------------------------*/
3684 /* Look for information labels in the object parts list. Parse the */
3685 /* information labels and print results to specified output device. */
3686 /* (This is not very robust to errors---needs work!) */
3687 /* */
3688 /* If "autonumber" is true, then the parsed info label is used to */
3689 /* auto-number devices. */
3690 /*----------------------------------------------------------------------*/
3692 char *parseinfo(objectptr cfrom, objectptr cthis, CalllistPtr clist,
3693 char *prefix, char *mode, Boolean autonumber, Boolean no_output)
3695 /* genericptr *pgen; (jdk) */
3696 stringpart *strptr, *ppin, *optr;
3697 char *snew, *sout = NULL, *pstring;
3698 u_char *strt, *fnsh;
3699 PortlistPtr ports;
3700 oparamptr ops, instops;
3701 int portid, locpos, i, k, subnet, slen; /* j, (jdk) */
3702 char *key = NULL;
3703 u_int newindex;
3704 Boolean is_flat = False, is_symbol = False, is_iso = False, is_quoted;
3705 Boolean do_update = True, include_once = False;
3706 char *locmode, *b36str, *sptr;
3707 LabellistPtr infolist, infoptr;
3708 FILE *finclude;
3710 /* For flat netlists, prefix the mode with the word "flat". */
3711 /* As of 3/8/07, this includes the ".sim" format, which */
3712 /* should be called as mode "flatsim". */
3714 locmode = mode;
3715 if (locmode && (!strncmp(mode, "flat", 4) || !strncmp(mode, "pseu", 4))) {
3716 locmode += 4;
3717 is_flat = True;
3720 /* mode == "" is passed when running resolve_devindex() and indicates */
3721 /* that the routine should be used to assign devindex to calls whose */
3722 /* devices have been manually assigned numbers. The preferred method */
3723 /* is to assign these values through the "index" parameter. Use of */
3724 /* parseinfo() ensures that objects that have no "index" parameter */
3725 /* but have valid info-labels for SPICE or PCB output will be */
3726 /* assigned the correct device index. */
3727 /* Sept. 3, 2006---The use of the prepended "@" character on */
3728 /* parameter names means that I can replace the cryptic "idx" with */
3729 /* the more obvious "index", which previously conflicted with the */
3730 /* PostScript command of the same name. Parameters "index" and "idx" */
3731 /* are treated interchangeably by the netlister. */
3733 if (locmode[0] == '\0')
3734 do_update = False;
3736 /* 1st pass: look for valid labels; see if more than one applies. */
3737 /* If so, order them correctly. Labels may not be numbered in */
3738 /* sequence, and may begin with zero. We allow a lot of flexibility */
3739 /* but the general expectation is that sequences start at 0 or 1 and */
3740 /* increase by consecutive integers. */
3742 infolist = geninfolist(cthis, clist->callinst, locmode);
3744 /* Now parse each label in sequence and output the result to */
3745 /* the return string. */
3747 sout = (char *)malloc(1);
3748 *sout = '\0';
3750 for (infoptr = infolist; infoptr != NULL; infoptr = infoptr->next) {
3751 objectptr pschem, cschem = infoptr->cschem;
3752 labelptr plabel = infoptr->label;
3753 objinstptr cinst = infoptr->cinst;
3755 /* move to colon character */
3756 slen = stringlength(plabel->string, True, cinst);
3757 for (i = 1; i < slen; i++) {
3758 strptr = findstringpart(i, &locpos, plabel->string, cinst);
3759 if (locpos >= 0 && *(strptr->data.string + locpos) == ':') break;
3762 /* interpret all characters after the colon */
3763 for (++i; i < slen; i++) {
3764 strptr = findstringpart(i, &locpos, plabel->string, cinst);
3765 if (locpos >= 0) {
3767 /* Do for all text characters */
3768 strt = strptr->data.string + locpos;
3769 if (*strt == '%') {
3770 is_quoted = False;
3771 strt++;
3772 i++;
3773 switch(*strt) {
3774 case '%':
3775 sout = (char *)realloc(sout, strlen(sout) + 2);
3776 strcat(sout, "%");
3777 break;
3778 case 'r':
3779 sout = (char *)realloc(sout, strlen(sout) + 2);
3780 strcat(sout, "\n");
3781 break;
3782 case 't':
3783 sout = (char *)realloc(sout, strlen(sout) + 2);
3784 strcat(sout, "\t");
3785 break;
3786 case 'i':
3787 if (clist->devname == NULL)
3788 clist->devname = strdup(sout);
3789 if (do_update) {
3790 if (is_flat)
3791 newindex = devflatindex(clist->devname);
3792 else
3793 newindex = devindex(cfrom, clist);
3794 b36str = d36a(newindex);
3795 sout = (char *)realloc(sout, strlen(sout) + strlen(b36str) + 1);
3796 sprintf(sout + strlen(sout), "%s", b36str);
3798 break;
3799 case 'N':
3800 sout = (char *)realloc(sout, strlen(sout)
3801 + strlen(cschem->name) + 1);
3802 strcat(sout, cschem->name);
3803 break;
3804 case 'n':
3805 sptr = strstr(cschem->name, "::");
3806 if (sptr == NULL)
3807 sptr = cschem->name;
3808 else
3809 sptr += 2;
3810 sout = (char *)realloc(sout, strlen(sout)
3811 + strlen(sptr) + 1);
3812 strcat(sout, sptr);
3813 break;
3814 case 'x':
3815 sout = (char *)realloc(sout, strlen(sout) + 7);
3816 sprintf(sout + strlen(sout), "%d",
3817 cinst->position.x);
3818 break;
3819 case 'y':
3820 sout = (char *)realloc(sout, strlen(sout) + 7);
3821 sprintf(sout + strlen(sout), "%d",
3822 cinst->position.y);
3823 break;
3824 case 'F':
3825 if (no_output) break;
3826 include_once = True;
3827 /* drop through */
3828 case 'f':
3829 if (no_output) break;
3831 /* Followed by a filename to include verbatim into */
3832 /* the output. Filename either has no spaces or */
3833 /* is in quotes. */
3835 /* Use textprint to catch any embedded parameters */
3837 snew = textprint(strptr, cinst);
3838 strt = snew;
3839 while (*strt != '\0') {
3840 if (*strt == '%') {
3841 if (include_once && *(strt + 1) == 'F')
3842 break;
3843 else if (!include_once && *(strt + 1) == 'f')
3844 break;
3846 strt++;
3849 if (*strt == '\0') {
3850 /* No filename; print verbatim */
3851 free(snew);
3852 include_once = False;
3853 break;
3856 strt += 2;
3857 if (*strt == '"') strt++;
3858 if (*strt == '"' || *strt == '\0') break;
3859 fnsh = strt + 1;
3860 while (*fnsh != '\0' && !isspace(*fnsh) && *fnsh != '"')
3861 fnsh++;
3862 strncpy(_STR, strt, (int)(fnsh - strt));
3863 _STR[(int)(fnsh - strt)] = '\0';
3864 free(snew);
3865 i = slen;
3867 /* Do variable and tilde expansion on the filename */
3868 xc_tilde_expand(_STR, 149);
3869 xc_variable_expand(_STR, 149);
3871 /* Check if this file has been included already */
3872 if (include_once) {
3873 if (check_included(_STR)) {
3874 include_once = False;
3875 break;
3877 append_included(_STR);
3880 /* Open the indicated file and dump to the output */
3881 finclude = fopen(_STR, "r");
3882 if (finclude != NULL) {
3883 char *sptr = sout;
3884 int nlen;
3885 int stlen = strlen(sout);
3886 while (fgets(_STR, 149, finclude)) {
3887 nlen = strlen(_STR);
3888 sout = (char *)realloc(sout, stlen + nlen + 1);
3889 sptr = sout + stlen;
3890 strcpy(sptr, _STR);
3891 stlen += nlen;
3893 fclose(finclude);
3895 else {
3896 Wprintf("No file named %s found", _STR);
3898 include_once = False;
3899 break;
3901 case 'p':
3902 /* Pin name either has no spaces or is in quotes */
3903 strt++;
3904 if (*strt == '"') {
3905 is_quoted = True;
3906 strt++;
3907 i++;
3909 if (*strt == '"' || *strt == '\0') break;
3910 fnsh = strt + 1;
3911 while (*fnsh != '\0' && !isspace(*fnsh) && *fnsh != '"')
3912 fnsh++;
3913 strncpy(_STR2, strt, (int)(fnsh - strt));
3914 _STR2[(int)(fnsh - strt)] = '\0';
3915 i += (fnsh - strt);
3916 /* if (is_quoted || *fnsh == '\0') i++; */
3917 if (is_quoted) i++;
3919 /* Find the port which corresponds to this pin name */
3920 /* in the called object (cschem). If this is a */
3921 /* linked symbol/schematic, then the port list will */
3922 /* be in the schematic view, not in the symbol view */
3924 pschem = cschem;
3925 if (cschem->ports == NULL && cschem->symschem != NULL &&
3926 cschem->symschem->ports != NULL)
3927 pschem = cschem->symschem;
3929 for (ports = pschem->ports; ports != NULL;
3930 ports = ports->next) {
3932 subnet = getsubnet(ports->netid, pschem);
3933 ppin = nettopin(ports->netid, pschem, NULL);
3934 // NOTE: _STR is used inside textprintsubnet()
3935 pstring = textprintsubnet(ppin, NULL, subnet);
3936 if (!strcmp(pstring, _STR2)) {
3937 portid = ports->portid;
3938 free(pstring);
3939 break;
3941 else
3942 free(pstring);
3944 if (ports != NULL) {
3946 /* Find the matching port in the calling object instance */
3948 for (ports = clist->ports; ports != NULL;
3949 ports = ports->next)
3950 if (ports->portid == portid) break;
3952 if (ports != NULL) {
3954 ppin = nettopin(ports->netid, cfrom, prefix);
3955 subnet = getsubnet(ports->netid, cfrom);
3956 snew = textprintsubnet(ppin, cinst, subnet);
3957 sout = (char *)realloc(sout, strlen(sout) +
3958 strlen(snew) + 1);
3959 strcat(sout, snew);
3960 free(snew);
3961 break;
3963 else {
3964 Fprintf(stderr, "Error: called non-existant port\n");
3967 else {
3968 Wprintf("No pin named %s in device %s", _STR, cschem->name);
3970 break;
3971 case 'v':
3972 /* Parameter name either has no spaces or is in quotes */
3973 strt++;
3974 if (*strt == '"') {
3975 is_quoted = True;
3976 strt++;
3977 i++;
3979 if (*strt == '"' || *strt == '\0') break;
3980 fnsh = strt + 1;
3981 while (*fnsh != '\0' && !isspace(*fnsh) && *fnsh != '"')
3982 fnsh++;
3983 strncpy(_STR, strt, (int)(fnsh - strt));
3984 _STR[(int)(fnsh - strt)] = '\0';
3985 i += (fnsh - strt);
3986 /* if (is_quoted || *fnsh == '\0') i++; */
3987 if (is_quoted) i++;
3989 /* Compare this name against the parameter keys */
3990 ops = match_param(cschem, _STR);
3992 /* For backwards compatibility, also try matching against */
3993 /* the parameter default value (method used before */
3994 /* implementing parameters as key:value pairs). */
3996 if (ops == NULL) {
3997 for (ops = cschem->params; ops != NULL; ops = ops->next) {
3998 if (ops->type == XC_STRING) {
3999 if (!textcomp(ops->parameter.string, _STR, NULL)) {
4000 /* substitute the parameter or default */
4001 instops = find_param(cinst, ops->key);
4002 optr = instops->parameter.string;
4003 if (stringlength(optr, True, cinst) > 0) {
4004 snew = textprint(optr, cinst);
4005 sout = (char *)realloc(sout, strlen(sout)
4006 + strlen(snew) + 1);
4007 strcat(sout, snew);
4008 free(snew);
4010 break;
4016 if (ops == NULL) {
4017 Wprintf("No parameter named %s in device %s",
4018 _STR, cschem->name);
4020 break;
4022 default:
4023 /* Presence of ".ends" statement forces xcircuit */
4024 /* not to write ".end" at the end of the netlist. */
4026 if (*strt == '.')
4027 if (!strncmp(strt + 1, "ends", 4))
4028 spice_end = FALSE;
4030 sout = (char *)realloc(sout, strlen(sout) + 2);
4031 *(sout + strlen(sout) - 1) = *strt;
4032 *(sout + strlen(sout)) = '\0';
4035 else {
4036 if (key != NULL)
4037 if (clist->devname == NULL)
4038 clist->devname = strdup(sout);
4040 /* Parameters with unresolved question marks are treated */
4041 /* like a "%i" string. */
4043 if ((key != NULL) && !textncomp(strptr, "?", cinst)) {
4044 if (do_update || autonumber) {
4045 if (is_flat || (cfrom == NULL))
4046 newindex = devflatindex(clist->devname);
4047 else
4048 newindex = devindex(cfrom, clist);
4049 k = strlen(sout);
4050 b36str = d36a(newindex);
4051 sout = (char *)realloc(sout, strlen(sout) + strlen(b36str) + 1);
4052 sprintf(sout + k, "%s", b36str);
4055 /* When called with autonumber = TRUE, generate a parameter */
4056 /* instance, replacing the question mark with the new index */
4057 /* number. */
4059 if (autonumber) {
4060 copyparams(cinst, cinst);
4061 ops = match_instance_param(cinst, key);
4062 optr = ops->parameter.string;
4063 if (!textncomp(optr, "?", cinst)) {
4064 optr->data.string = (char *)realloc(optr->data.string,
4065 strlen(sout + k) + 1);
4066 strcpy(optr->data.string, sout + k);
4068 else Wprintf("Error while auto-numbering parameters");
4069 resolveparams(cinst);
4072 else {
4073 /* A "?" default parameter that has a (different) */
4074 /* instance value becomes the device number. */
4075 int stlen;
4076 if ((key != NULL) && (clist->devindex < 0)) {
4077 ops = match_param(cschem, key);
4078 if (ops->type == XC_STRING) {
4079 CalllistPtr plist;
4080 if (!textcomp(ops->parameter.string, "?", NULL)) {
4081 if (is_flat) {
4082 /* Ignore the value and generate one instead */
4083 newindex = devflatindex(clist->devname);
4084 b36str = d36a(newindex);
4085 k = strlen(sout);
4086 sout = (char *)realloc(sout, strlen(sout)
4087 + strlen(b36str) + 1);
4088 sprintf(sout + k, "%s", b36str);
4089 continue; /* go on to next stringpart */
4091 else if (cfrom != NULL) {
4093 /* Interpret parameter string as a component */
4094 /* number so we can check for duplicates. Assume */
4095 /* a base-36 number, which includes all alphabet */
4096 /* characters. This will disambiguate, e.g., "1" */
4097 /* and "1R", but flag case-sensitivity, e.g., */
4098 /* "1R" and "1r". Thanks to Carsten Thomas for */
4099 /* pointing out the problem with assuming */
4100 /* numerical component values. */
4102 char *endptr;
4103 newindex = (u_int) strtol(strt, &endptr, 36);
4104 if (*endptr != '\0') {
4105 Fprintf(stderr, "Warning: Use of non-alphanumeric"
4106 " characters in component number \"%s\"\n", strt);
4108 clist->devindex = newindex;
4109 for (plist = cfrom->calls; plist; plist = plist->next) {
4110 if ((plist == clist) ||
4111 (plist->callobj != clist->callobj)) continue;
4113 /* Two parts have been named the same. Flag */
4114 /* it, but don't try to do anything about it. */
4115 /* 11/22/06---additionally check part numbers, */
4116 /* in case the symbol is a component sub-part. */
4118 if (plist->devindex == newindex) {
4119 if (samepart(plist, clist)) {
4120 Fprintf(stderr, "Warning: duplicate part number"
4121 " %s%s and %s%s\n", sout, strt, sout, strt);
4122 break;
4130 stlen = strlen(sout);
4131 sout = (char *)realloc(sout, stlen + 2);
4133 /* By convention, greek "mu" becomes ASCII "u", NOT "m", */
4134 /* otherwise we get, e.g., millifarads instead of microfarads */
4136 if ((is_symbol && (*strt == 'm')) || (is_iso && (*strt == 0265)))
4137 *(sout + stlen) = 'u';
4138 else
4139 *(sout + stlen) = *strt;
4140 *(sout + stlen + 1) = '\0';
4145 /* Some label controls are interpreted. Most are ignored */
4146 else {
4147 switch(strptr->type) {
4148 case PARAM_START:
4149 key = strptr->data.string;
4150 break;
4151 case PARAM_END:
4152 key = NULL;
4153 break;
4154 case RETURN:
4155 sout = (char *)realloc(sout, strlen(sout) + 2);
4156 strcat(sout, "\n");
4157 break;
4158 case TABFORWARD:
4159 sout = (char *)realloc(sout, strlen(sout) + 2);
4160 strcat(sout, "\t");
4161 break;
4162 case FONT_NAME:
4163 is_symbol = issymbolfont(strptr->data.font);
4164 is_iso = isisolatin1(strptr->data.font);
4165 break;
4170 /* Insert a newline between multiple valid info labels */
4171 if (infoptr->next != NULL) {
4172 sout = (char *)realloc(sout, strlen(sout) + 2);
4173 strcat(sout, "\n");
4176 freelabellist(&infoptr);
4178 if (*sout == '\0') {
4179 free(sout);
4180 return NULL;
4182 return sout;
4185 /*----------------------------------------------------------------------*/
4186 /* Write a low-level device */
4187 /*----------------------------------------------------------------------*/
4189 int writedevice(FILE *fp, char *mode, objectptr cfrom, CalllistPtr clist,
4190 char *prefix)
4192 char *sout;
4193 objectptr cthis;
4195 if (clist == NULL) {
4196 if (fp != NULL) fprintf(fp, "error: null device\n");
4197 return -1;
4200 /* Devices are always symbols. If the call indicates a schematic */
4201 /* and a separate associated symbol, then we want the symbol. */
4202 /* If we're writing a flat netlist, then return -1 to indicate that */
4203 /* the symbol instance needs to be flattened. */
4205 cthis = clist->callobj;
4206 if (clist->callobj->schemtype == PRIMARY || clist->callobj->schemtype ==
4207 SECONDARY)
4208 if (clist->callobj->symschem != NULL) {
4209 if (!strncmp(mode, "flat", 4))
4210 return -1;
4211 else
4212 cthis = clist->callobj->symschem;
4215 /* Look for information labels in the object parts list. */
4217 if ((sout = parseinfo(cfrom, cthis, clist, prefix, mode, FALSE, FALSE)) != NULL) {
4218 if (fp != NULL) {
4219 fputs(sout, fp);
4220 fprintf(fp, "\n");
4222 free(sout);
4223 return 0;
4226 /* Information labels do not necessarily exist. */
4227 return -1;
4230 #ifdef TCL_WRAPPER
4232 /*----------------------------------------------------------------------*/
4233 /* Translate a hierarchical net name into an object and instance. */
4234 /* Return TRUE if found, FALSE if not. If found, object and instance */
4235 /* are returned in the argument pointers. */
4236 /*----------------------------------------------------------------------*/
4238 Boolean HierNameToObject(objinstptr cinst, char *hiername, pushlistptr *stack)
4240 CalllistPtr calls;
4241 char *nexttoken, *hptr, *pptr;
4242 objectptr cschem, curobj, newobj;
4243 objinstptr newinst;
4244 int devindex, devlen;
4245 /* pushlistptr stackentry; (jdk) */
4247 cschem = cinst->thisobject;
4248 curobj = (cschem->schemtype == SECONDARY) ? cschem->symschem : cschem;
4250 if (curobj->calls == NULL) {
4251 if ((updatenets(cinst, FALSE) <= 0) || (curobj->calls == NULL)) {
4252 Wprintf("Error in generating netlists!");
4253 return False;
4257 hptr = hiername;
4258 while (hptr != NULL) {
4259 nexttoken = strchr(hptr, '/');
4260 if (nexttoken != NULL) *nexttoken = '\0';
4261 pptr = strrchr(hptr, '(');
4262 if (pptr != NULL) {
4263 if (sscanf(pptr + 1, "%d", &devindex) == 0) {
4264 pptr = NULL;
4265 devindex = 0;
4267 else
4268 *pptr = '\0';
4270 else devindex = -1;
4272 /* check to make sure that the netlist and device indices have been generated */
4273 for (calls = curobj->calls; calls != NULL; calls = calls->next) {
4274 if (calls->devindex == -1) {
4275 cleartraversed(curobj);
4276 resolve_indices(curobj, FALSE);
4280 /* hptr is now the object name, and devindex is the instance's call index */
4281 /* find the object corresponding to the name. */
4283 newobj = NameToObject(hptr, &newinst, TRUE);
4285 if (newobj == NULL) {
4287 /* Try device names (for netlist output) instead of object names */
4289 for (calls = curobj->calls; calls != NULL; calls = calls->next) {
4290 if (calls->devname != NULL) {
4291 devlen = strlen(calls->devname);
4292 if (!strncmp(hptr, calls->devname, devlen)) {
4293 if (devindex == -1) {
4294 if (sscanf(hptr + devlen, "%d", &devindex) == 0)
4295 devindex = 0;
4297 if (calls->devindex == devindex) {
4298 newobj = calls->callinst->thisobject;
4299 break;
4305 else {
4306 for (calls = curobj->calls; calls != NULL; calls = calls->next)
4307 if ((calls->callobj == newobj) && (calls->devindex == devindex))
4308 break;
4310 if (newobj == NULL || calls == NULL) {
4311 Fprintf(stderr, "object %s in hierarchy not found in schematic.\n", hptr);
4312 free_stack(stack);
4313 return FALSE;
4316 /* Diagnostic information */
4317 /* fprintf(stderr, "object %s(%d) = %s\n", newobj->name, devindex, hptr); */
4319 curobj = calls->callobj;
4320 push_stack(stack, calls->callinst, NULL);
4322 if (pptr != NULL) *pptr = '(';
4323 if (nexttoken == NULL) break;
4324 *nexttoken = '/';
4325 hptr = nexttoken + 1;
4327 return TRUE;
4330 #endif
4332 /*----------------------------------------------------------------------*/
4333 /* Save netlist into a flattened sim or spice file */
4334 /*----------------------------------------------------------------------*/
4336 void writeflat(objectptr cschem, CalllistPtr cfrom, char *prefix, FILE *fp, char *mode)
4338 CalllistPtr calls = cschem->calls;
4339 char *newprefix = (char *)malloc(sizeof(char));
4341 /* reset device indexes */
4343 for (calls = cschem->calls; calls != NULL; calls = calls->next)
4344 calls->devindex = -1;
4346 /* The naming convention for nodes below the top level uses a */
4347 /* slash-separated list of hierarchical names. Thus the call to */
4348 /* the hierarchical resolve_indices(). Indices used by bottom-most */
4349 /* symbols (e.g., flattened spice) will be generated by a separate */
4350 /* routine devflatindex(), unrelated to what is generated here, */
4351 /* which only affects the hierarchical naming of nodes. */
4353 resolve_indices(cschem, FALSE);
4355 /* write all the subcircuits */
4357 for (calls = cschem->calls; calls != NULL; calls = calls->next) {
4359 makelocalpins(cschem, calls, prefix);
4360 if (writedevice(fp, mode, cschem, calls, prefix) < 0) {
4361 sprintf(_STR, "%s_%u", calls->callobj->name,
4362 devindex(cschem, calls));
4363 newprefix = (char *)realloc(newprefix, sizeof(char) * (strlen(prefix)
4364 + strlen(_STR) + 2));
4365 sprintf(newprefix, "%s%s/", prefix, _STR);
4366 /* Allow cross-referenced parameters between symbols and schematics */
4367 /* by substituting into a schematic from its corresponding symbol */
4368 opsubstitute(calls->callobj, calls->callinst);
4369 /* psubstitute(calls->callinst); */
4370 writeflat(calls->callobj, calls, newprefix, fp, mode);
4372 clearlocalpins(calls->callobj);
4374 free(newprefix);
4377 /*----------------------------------------------------------------------*/
4378 /* Topmost call to write a flattened netlist */
4379 /*----------------------------------------------------------------------*/
4381 void topflat(objectptr cschem, objinstptr thisinst, CalllistPtr cfrom,
4382 char *prefix, FILE *fp, char *mode)
4384 char *locmode, *stsave = NULL;
4385 int modlen;
4386 Calllist loccalls;
4388 /* Set up local call list for parsing info labels in the top-level schematic */
4389 loccalls.cschem = NULL;
4390 loccalls.callobj = cschem;
4391 loccalls.callinst = thisinst;
4392 loccalls.devindex = -1;
4393 loccalls.ports = NULL;
4394 loccalls.next = NULL;
4396 modlen = strlen(mode);
4397 locmode = malloc(2 + modlen);
4398 strcpy(locmode, mode);
4399 locmode[modlen + 1] = '\0';
4401 /* "<mode>@" lines go in front */
4403 locmode[modlen] = '@';
4404 if (fp != NULL) stsave = parseinfo(NULL, cschem, &loccalls, NULL, locmode, FALSE,
4405 FALSE);
4406 if (stsave != NULL) {
4407 fputs(stsave, fp);
4408 fprintf(fp, "\n");
4409 free(stsave);
4410 stsave = NULL;
4413 writeflat(cschem, cfrom, prefix, fp, mode);
4414 freeflatindex();
4416 /* Check for negative-numbered info labels, indicated output that */
4417 /* should be processed after everything else. */
4419 locmode[modlen] = '-';
4420 stsave = parseinfo(NULL, cschem, &loccalls, NULL, locmode, FALSE, FALSE);
4421 if (stsave != NULL) {
4422 fputs(stsave, fp);
4423 fprintf(fp, "\n");
4424 free(stsave);
4425 stsave = NULL;
4427 free(locmode);
4430 /*----------------------------------------------------------------------*/
4431 /* Write out the list of global nets and their pin names */
4432 /*----------------------------------------------------------------------*/
4434 void writeglobals(objectptr cschem, FILE *fp)
4436 LabellistPtr llist;
4437 labelptr gpin;
4438 char *snew;
4440 if (fp == NULL) return;
4442 for (llist = global_labels; llist != NULL; llist = llist->next) {
4443 gpin = llist->label;
4444 snew = textprint(gpin->string, NULL);
4445 fprintf(fp, ".GLOBAL %s\n", snew); /* hspice format */
4446 /* fprintf(fp, "%s = %d\n", snew, -gptr->netid); */ /* generic format */
4447 free(snew);
4449 fprintf(fp, "\n");
4452 /*----------------------------------------------------------------------*/
4453 /* Write a SPICE subcircuit entry. */
4454 /*----------------------------------------------------------------------*/
4456 void writesubcircuit(FILE *fp, objectptr cschem)
4458 PortlistPtr ports;
4459 char *pstring;
4460 stringpart *ppin;
4461 int netid, length, plen, subnet; /* portid, (jdk) */
4463 /* Objects which have no ports are not written */
4464 if ((cschem->ports != NULL) && (fp != NULL)) {
4466 fprintf(fp, ".subckt %s", cschem->name);
4467 length = 9 + strlen(cschem->name);
4469 /* List of parameters in subcircuit. */
4470 /* Each parameter connects to a net which may have multiple */
4471 /* names, so find the net associated with the parameter */
4472 /* and convert it back into a pin name in the usual manner */
4473 /* using nettopin(). */
4475 for (ports = cschem->ports; ports != NULL;
4476 ports = ports->next) {
4477 netid = ports->netid;
4478 subnet = getsubnet(netid, cschem);
4479 ppin = nettopin(netid, cschem, NULL);
4480 pstring = textprintsubnet(ppin, NULL, subnet);
4481 plen = strlen(pstring) + 1;
4482 if (length + plen > 78) {
4483 fprintf(fp, "\n+ "); /* SPICE line break & continuation */
4484 length = 0;
4486 else length += plen;
4487 fprintf(fp, " %s", pstring);
4488 free(pstring);
4490 fprintf(fp, "\n");
4494 /*----------------------------------------------------------------------*/
4495 /* Resolve device names (fill calllist structure member "devname") */
4496 /*----------------------------------------------------------------------*/
4498 void resolve_devnames(objectptr cschem)
4500 CalllistPtr calls;
4501 char *stmp;
4502 oparamptr ops;
4504 for (calls = cschem->calls; calls != NULL; calls = calls->next) {
4505 if (calls->callobj->traversed == False) {
4506 calls->callobj->traversed = True;
4507 resolve_devnames(calls->callobj);
4510 if (calls->devname == NULL) {
4512 /* Check for "class" parameter. Note that although this */
4513 /* parameter may take a different instance value, the */
4514 /* device class is determined by the default value. */
4516 ops = find_param(calls->callinst, "class");
4517 if (ops && (ops->type == XC_STRING))
4518 calls->devname = textprint(ops->parameter.string, NULL);
4519 else {
4520 /* The slow way---parse info labels for any device information */
4521 if ((stmp = parseinfo(cschem, calls->callinst->thisobject, calls,
4522 NULL, "", FALSE, TRUE)) != NULL)
4523 free(stmp);
4529 /*----------------------------------------------------------------------*/
4530 /* Resolve (hierarchical) component numbering. If "autonumber" is TRUE */
4531 /* then this routine does auto-numbering. Otherwise, it auto-generates */
4532 /* device indices internally (in the "calllist" structure) but does not */
4533 /* copy them into parameter space. */
4534 /* */
4535 /* Note: resolve_devindex() only operates on a single object. Use */
4536 /* resolve_indices() to operate on the entire hierarchy. */
4537 /*----------------------------------------------------------------------*/
4539 void resolve_devindex(objectptr cschem, Boolean autonumber)
4541 CalllistPtr calls;
4542 char *stmp, *endptr;
4543 char *idxtype[] = {"index", "idx", NULL};
4544 oparamptr ops, ips;
4545 objinstptr cinst;
4546 stringpart *optr;
4547 int newindex, j;
4549 for (calls = cschem->calls; calls != NULL; calls = calls->next) {
4551 /* Check if there is an "index" parameter set to "?" in */
4552 /* the object with no existing instance value. */
4554 for (j = 0; idxtype[j] != NULL; j++)
4555 if ((ops = match_param(calls->callinst->thisobject, idxtype[j])) != NULL)
4556 break;
4558 if (ops && (ops->type == XC_STRING)) {
4559 if (!textcomp(ops->parameter.string, "?", NULL)) {
4560 cinst = calls->callinst;
4561 ips = match_instance_param(cinst, idxtype[j]);
4562 if ((autonumber == TRUE) && (ips == NULL)) {
4563 copyparams(cinst, cinst);
4564 ops = match_instance_param(cinst, idxtype[j]);
4565 optr = ops->parameter.string;
4566 newindex = devindex(cschem, calls);
4567 stmp = d36a(newindex);
4568 optr->data.string = (char *)realloc(optr->data.string, strlen(stmp) + 1);
4569 sprintf(optr->data.string, "%s", stmp);
4571 else if (calls->devindex < 0) {
4572 if (ips != NULL) {
4573 optr = ips->parameter.string;
4574 if (optr->type != TEXT_STRING) {
4575 /* Not a simple string. Dump the text. */
4576 char *snew = NULL;
4577 snew = textprint(optr, NULL),
4578 newindex = (u_int) strtol(snew, &endptr, 36);
4579 free(snew);
4581 else {
4582 newindex = (u_int) strtol(optr->data.string, &endptr, 36);
4584 if (*endptr != '\0') {
4585 /* It is possible to get an instance value that is */
4586 /* the same as the default, although this normally */
4587 /* doesn't happen. If it does, quietly remove the */
4588 /* unneeded instance value. */
4590 if (!stringcomp(ops->parameter.string, ips->parameter.string))
4591 resolveparams(cinst);
4592 else
4593 Fprintf(stderr, "Warning: Use of non-alphanumeric"
4594 " characters in component \"%s%s\" (instance"
4595 " of %s)\n", (calls->devname) ? calls->devname
4596 : calls->callobj->name, optr->data.string,
4597 calls->callobj->name);
4599 else
4600 calls->devindex = newindex;
4602 else /* if (autonumber) */
4603 devindex(cschem, calls);
4607 else {
4608 /* The slow way---parse info labels for any index information */
4609 if ((stmp = parseinfo(cschem, calls->callinst->thisobject, calls,
4610 NULL, "", autonumber, TRUE)) != NULL)
4611 free(stmp);
4616 /*----------------------------------------------------------------------*/
4617 /* Recursive call to resolve_devindex() through the circuit hierarchy */
4618 /*----------------------------------------------------------------------*/
4620 void resolve_indices(objectptr cschem, Boolean autonumber) {
4621 CalllistPtr calls;
4623 for (calls = cschem->calls; calls != NULL; calls = calls->next) {
4624 if (calls->callobj->traversed == False) {
4625 calls->callobj->traversed = True;
4626 resolve_indices(calls->callobj, autonumber);
4629 resolve_devindex(cschem, autonumber);
4632 /*----------------------------------------------------------------------*/
4633 /* Recursive call to clear all generated device numbers. */
4634 /*----------------------------------------------------------------------*/
4636 void clear_indices(objectptr cschem) {
4637 CalllistPtr calls;
4639 for (calls = cschem->calls; calls != NULL; calls = calls->next) {
4640 if (calls->callobj->traversed == False) {
4641 calls->callobj->traversed = True;
4642 clear_indices(calls->callobj);
4644 calls->devindex = -1;
4648 /*----------------------------------------------------------------------*/
4649 /* Recursive call to clear all device indexes (parameter "index") */
4650 /*----------------------------------------------------------------------*/
4652 void unnumber(objectptr cschem) {
4653 CalllistPtr calls;
4654 oparamptr ops, ips;
4655 char *idxtype[] = {"index", "idx", NULL};
4656 int j;
4658 for (calls = cschem->calls; calls != NULL; calls = calls->next) {
4659 if (calls->callobj->traversed == False) {
4660 calls->callobj->traversed = True;
4661 unnumber(calls->callobj);
4664 for (j = 0; idxtype[j] != NULL; j++)
4665 if ((ops = match_param(calls->callobj, idxtype[j])) != NULL) break;
4667 if (ops && (ops->type == XC_STRING)) {
4668 if (!textcomp(ops->parameter.string, "?", NULL)) {
4669 ips = match_instance_param(calls->callinst, idxtype[j]);
4670 if (ips != NULL)
4671 free_instance_param(calls->callinst, ips);
4677 /*----------------------------------------------------------------------*/
4678 /* Save netlist into a hierarchical file */
4679 /*----------------------------------------------------------------------*/
4681 void writehierarchy(objectptr cschem, objinstptr thisinst, CalllistPtr cfrom,
4682 FILE *fp, char *mode)
4684 CalllistPtr calls = cschem->calls;
4685 PortlistPtr ports, plist;
4686 int pnet, portid, length, plen, subnet, modlen; /* netid, (jdk) */
4687 char *locmode = mode, *stsave = NULL, *pstring;
4688 stringpart *ppin;
4689 Calllist loccalls;
4691 if (cschem->traversed == True) return;
4693 /* Set up local call list for parsing info labels in this schematic */
4694 loccalls.cschem = NULL;
4695 loccalls.callobj = cschem;
4696 loccalls.callinst = thisinst;
4697 loccalls.devindex = -1;
4698 loccalls.ports = NULL;
4699 loccalls.next = NULL;
4701 modlen = strlen(mode);
4702 locmode = malloc(2 + modlen);
4703 strcpy(locmode, mode);
4704 locmode[modlen + 1] = '\0';
4706 /* "<mode>@" lines go before any subcircuit calls */
4708 locmode[modlen] = '@';
4709 if (fp != NULL) stsave = parseinfo(NULL, cschem, &loccalls, NULL, locmode, FALSE,
4710 FALSE);
4711 if (stsave != NULL) {
4712 fputs(stsave, fp);
4713 fprintf(fp, "\n");
4714 free(stsave);
4715 stsave = NULL;
4718 /* Subcircuits which make no calls or have no devices do not get written */
4720 if (calls != NULL) {
4722 /* Make sure that all the subcircuits have been written first */
4724 for (; calls != NULL; calls = calls->next) {
4725 if (calls->callobj->traversed == False) {
4726 psubstitute(calls->callinst);
4727 writehierarchy(calls->callobj, calls->callinst, calls, fp, mode);
4728 calls->callobj->traversed = True;
4731 if (cschem->schemtype == FUNDAMENTAL) {
4732 free(locmode);
4733 return;
4737 /* Info-labels on a schematic (if any) get printed out first */
4739 if ((fp != NULL) && (cschem->calls != NULL)) {
4740 stsave = parseinfo(NULL, cschem, &loccalls, NULL, mode, FALSE, FALSE);
4741 if (stsave != NULL) {
4743 /* Check stsave for embedded SPICE subcircuit syntax */
4745 if (!strcmp(mode, "spice"))
4746 if (strstr(stsave, ".subckt ") == NULL)
4747 writesubcircuit(fp, cschem);
4749 fputs(stsave, fp);
4750 fprintf(fp, "\n");
4751 free(stsave);
4752 stsave = NULL;
4754 else if (cschem->calls != NULL)
4755 /* top-level schematics will be ignored because they have no ports */
4756 writesubcircuit(fp, cschem);
4759 /* Resolve all fixed part assignments (devindex) */
4760 resolve_devindex(cschem, FALSE);
4762 /* If the output file is NULL, then we're done */
4764 if (fp == NULL) {
4765 free(locmode);
4766 return;
4769 for (calls = cschem->calls; calls != NULL; calls = calls->next) {
4771 /* writedevice() is just another call to parseinfo() */
4772 if (writedevice(fp, mode, cschem, calls, NULL) < 0) {
4774 /* If the device did not produce any output in writedevice(), but */
4775 /* does not make any calls itself, then it is implicitly a TRIVIAL */
4776 /* symbol. */
4778 if ((calls->callobj->schemtype == TRIVIAL) || (calls->callobj->calls == NULL))
4779 continue;
4781 /* No syntax indicating how to write this call. Use SPICE "X" */
4782 /* format and arrange the parameters according to the structure. */
4784 calls->devname = strdup(spice_devname);
4785 fprintf(fp, "X%s", d36a(devindex(cschem, calls)));
4786 stsave = calls->callobj->name;
4787 length = 6;
4789 /* The object's definition lists calls in the order of the object's */
4790 /* port list. Therefore, use this order to find the port list. */
4791 /* This will also deal with the fact that not every port has to be */
4792 /* called by the instance (ports may be left disconnected). */
4794 for (ports = calls->callobj->ports; ports != NULL;
4795 ports = ports->next) {
4796 portid = ports->portid;
4797 for (plist = calls->ports; plist != NULL; plist = plist->next)
4798 if (plist->portid == ports->portid)
4799 break;
4801 pnet = (plist == NULL) ? netmax(cschem) + 1 : plist->netid;
4802 subnet = getsubnet(pnet, cschem);
4803 ppin = nettopin(pnet, cschem, NULL);
4804 pstring = textprintsubnet(ppin, NULL, subnet);
4805 plen = strlen(pstring) + 1;
4806 if (length + plen > 78) {
4807 fprintf(fp, "\n+ "); /* SPICE line continuation */
4808 length = 0;
4810 else length += plen;
4811 fprintf(fp, " %s", pstring);
4812 free(pstring);
4814 plen = 1 + strlen(stsave);
4815 if (length + plen > 78) fprintf(fp, "\n+ "); /* SPICE line cont. */
4816 fprintf(fp, " %s\n", stsave);
4820 /* Check for negative-numbered info labels, indicated output that */
4821 /* should be processed after everything else. If we're processing */
4822 /* SPICE output but the schematic does not provide a ".ends" */
4823 /* statement, then add it to the end of the deck. */
4825 if (cschem->calls != NULL) {
4826 locmode[modlen] = '-';
4827 stsave = parseinfo(NULL, cschem, &loccalls, NULL, locmode, FALSE, FALSE);
4828 if (stsave != NULL) {
4829 fputs(stsave, fp);
4830 fprintf(fp, "\n");
4831 if (cfrom != NULL)
4832 if (!strcmp(mode, "spice"))
4833 if (strstr(stsave, ".ends") == NULL)
4834 fprintf(fp, ".ends\n");
4835 free(stsave);
4837 else if (cfrom != NULL)
4838 fprintf(fp, ".ends\n");
4840 fprintf(fp, "\n");
4843 free(locmode);
4846 /*----------------------------------------------------------------------*/
4847 /* Create generic netlist in the Tcl interpreter variable space */
4848 /*----------------------------------------------------------------------*/
4850 #ifdef TCL_WRAPPER
4852 static Tcl_Obj *tclparseinfo(objectptr cschem)
4854 genericptr *pgen;
4855 labelptr plabel;
4857 Tcl_Obj *rlist = Tcl_NewListObj(0, NULL);
4859 for (pgen = cschem->plist; pgen < cschem->plist + cschem->parts; pgen++) {
4860 if (IS_LABEL(*pgen)) {
4861 plabel = TOLABEL(pgen);
4862 if (plabel->pin == INFO) {
4863 Tcl_ListObjAppendElement(xcinterp, rlist,
4864 TclGetStringParts(plabel->string));
4868 return rlist;
4871 /*----------------------------------------------------------------------*/
4872 /* Write global variables to Tcl list (in key-value pairs which can be */
4873 /* turned into an array variable using the "array set" command) */
4874 /* */
4875 /* Note: argument "cinst" is unused, but it really should be. We */
4876 /* should not assume that different schematics have the same list of */
4877 /* globals! */
4878 /*----------------------------------------------------------------------*/
4880 Tcl_Obj *tclglobals(objinstptr cinst)
4882 LabellistPtr llist;
4883 labelptr gpin;
4884 Tcl_Obj *gdict; /*, *netnum; (jdk) */
4885 buslist *sbus;
4886 int netid, lbus;
4888 gdict = Tcl_NewListObj(0, NULL);
4889 for (llist = global_labels; llist != NULL; llist = llist->next) {
4890 gpin = llist->label;
4891 Tcl_ListObjAppendElement(xcinterp, gdict, TclGetStringParts(gpin->string));
4892 for (lbus = 0;;) {
4893 if (llist->subnets == 0) {
4894 netid = llist->net.id;
4896 else {
4897 sbus = llist->net.list + lbus;
4898 netid = sbus->netid;
4900 Tcl_ListObjAppendElement(xcinterp, gdict, Tcl_NewIntObj(netid));
4901 if (++lbus >= llist->subnets) break;
4904 return gdict;
4907 /*----------------------------------------------------------------------*/
4908 /* Write a generic hierarchical netlist into Tcl list "subcircuits" */
4909 /*----------------------------------------------------------------------*/
4911 void tclhierarchy(objectptr cschem, objinstptr cinst, CalllistPtr cfrom, Tcl_Obj *cktlist)
4913 CalllistPtr calls = cschem->calls;
4914 PortlistPtr ports, plist;
4915 int netid, pnet, portid; /* , length, plen, i; (jdk) */
4916 Tcl_Obj *tclports, *tclcalls, *tclnewcall, *tclnets, *netnum;
4917 Tcl_Obj *tcldevs, *tclparams, *subckt, *newdev, *tcllabel, *portnum;
4918 oparamptr paramlist; /* , locparam; (jdk) */
4919 char *netsdone;
4921 /* Trivial objects are by definition those that are supposed */
4922 /* to be resolved by the netlist generation prior to output */
4923 /* and ignored by the output generator. */
4925 if (cschem->schemtype == TRIVIAL) return;
4927 /* Make sure that all the subcircuits have been written first */
4929 for (; calls != NULL; calls = calls->next) {
4930 if (calls->callobj->traversed == False) {
4931 tclhierarchy(calls->callobj, calls->callinst, calls, cktlist);
4932 calls->callobj->traversed = True;
4936 /* Write own subcircuit netlist */
4937 subckt = Tcl_NewListObj(0, NULL);
4939 /* Prepare the list of network cross-references */
4940 tclnets = Tcl_NewListObj(0, NULL);
4942 /* Make list of the nets which have been written so we don't redundantly */
4943 /* list any entries (use of calloc() initializes all entries to FALSE). */
4944 /* (calloc() replaced by malloc() and bzero() because Tcl doesn't */
4945 /* have a calloc() function (2/6/04)) */
4947 netsdone = (char *)malloc(2 + netmax(cschem));
4948 #ifdef _MSC_VER
4949 memset((void *)netsdone, 0, 2 + netmax(cschem));
4950 #else
4951 bzero((void *)netsdone, 2 + netmax(cschem));
4952 #endif
4954 /* Write the name (key value pair) */
4955 Tcl_ListObjAppendElement(xcinterp, subckt, Tcl_NewStringObj("name", 4));
4956 Tcl_ListObjAppendElement(xcinterp, subckt, Tcl_NewStringObj(cschem->name,
4957 strlen(cschem->name)));
4959 /* Write the handle of the object instance (key value pair) */
4960 Tcl_ListObjAppendElement(xcinterp, subckt, Tcl_NewStringObj("handle", 6));
4961 Tcl_ListObjAppendElement(xcinterp, subckt, Tcl_NewHandleObj(cinst));
4963 /* Write the list of ports */
4964 if ((ports = cschem->ports) != NULL) {
4966 /* List of ports in subcircuit. */
4967 /* Each parameter connects to a net which may have multiple */
4968 /* names, so find the net associated with the parameter */
4969 /* and convert it back into a pin name in the usual manner */
4970 /* using nettopin(). */
4972 tclports = Tcl_NewListObj(0, NULL);
4973 for (; ports != NULL; ports = ports->next) {
4974 netid = ports->netid;
4975 portid = ports->portid;
4976 netnum = Tcl_NewIntObj(netid);
4977 portnum = Tcl_NewIntObj(portid);
4978 Tcl_ListObjAppendElement(xcinterp, tclports, portnum);
4979 Tcl_ListObjAppendElement(xcinterp, tclports, netnum);
4980 if ((netid >= 0) && (netsdone[netid] == (char)0))
4982 Tcl_ListObjAppendElement(xcinterp, tclnets, netnum);
4983 Tcl_ListObjAppendElement(xcinterp, tclnets,
4984 TclGetStringParts(nettopin(netid, cschem, NULL)));
4985 /* record this net as having been added to the list */
4986 netsdone[netid] = (char)1;
4989 Tcl_ListObjAppendElement(xcinterp, subckt, Tcl_NewStringObj("ports", 5));
4990 Tcl_ListObjAppendElement(xcinterp, subckt, tclports);
4993 /* Write the list of parameter defaults (key:value pairs) */
4995 if (cschem->params != NULL) {
4996 tclparams = Tcl_NewListObj(0, NULL);
4998 for (paramlist = cschem->params; paramlist != NULL; paramlist = paramlist->next) {
4999 Tcl_ListObjAppendElement(xcinterp, tclparams,
5000 Tcl_NewStringObj(paramlist->key, strlen(paramlist->key)));
5001 switch(paramlist->type) {
5002 case XC_INT:
5003 Tcl_ListObjAppendElement(xcinterp, tclparams,
5004 Tcl_NewIntObj((int)paramlist->parameter.ivalue));
5005 break;
5006 case XC_FLOAT:
5007 Tcl_ListObjAppendElement(xcinterp, tclparams,
5008 Tcl_NewDoubleObj((double)paramlist->parameter.fvalue));
5009 break;
5010 case XC_STRING:
5011 tcllabel = TclGetStringParts(paramlist->parameter.string);
5012 /* Should get rid of last entry, "End Parameter"; not needed */
5013 Tcl_ListObjAppendElement(xcinterp, tclparams, tcllabel);
5014 break;
5015 case XC_EXPR:
5016 Tcl_ListObjAppendElement(xcinterp, tclparams,
5017 evaluate_raw(cschem, paramlist, cinst, NULL));
5018 break;
5021 Tcl_ListObjAppendElement(xcinterp, subckt, Tcl_NewStringObj("parameters", 10));
5022 Tcl_ListObjAppendElement(xcinterp, subckt, tclparams);
5025 /* Write the list of calls to subcircuits */
5027 if ((calls = cschem->calls) != NULL) {
5028 tclcalls = Tcl_NewListObj(0, NULL);
5029 for (; calls != NULL; calls = calls->next) {
5031 /* Don't write calls to non-functional subcircuits. */
5032 if (calls->callobj->schemtype == TRIVIAL) continue;
5034 tclnewcall = Tcl_NewListObj(0, NULL);
5035 Tcl_ListObjAppendElement(xcinterp, tclnewcall, Tcl_NewStringObj("name", 4));
5036 Tcl_ListObjAppendElement(xcinterp, tclnewcall,
5037 Tcl_NewStringObj(calls->callobj->name,
5038 strlen(calls->callobj->name)));
5041 /* Log any local parameter instances */
5042 if (calls->callinst->params != NULL) {
5043 tclparams = Tcl_NewListObj(0, NULL);
5045 for (paramlist = calls->callinst->params; paramlist != NULL;
5046 paramlist = paramlist->next) {
5047 Tcl_ListObjAppendElement(xcinterp, tclparams,
5048 Tcl_NewStringObj(paramlist->key, strlen(paramlist->key)));
5049 switch(paramlist->type) {
5050 case XC_INT:
5051 Tcl_ListObjAppendElement(xcinterp, tclparams,
5052 Tcl_NewIntObj((int)paramlist->parameter.ivalue));
5053 break;
5054 case XC_FLOAT:
5055 Tcl_ListObjAppendElement(xcinterp, tclparams,
5056 Tcl_NewDoubleObj((double)paramlist->parameter.fvalue));
5057 break;
5058 case XC_STRING:
5059 Tcl_ListObjAppendElement(xcinterp, tclparams,
5060 TclGetStringParts(paramlist->parameter.string));
5061 break;
5062 case XC_EXPR:
5063 Tcl_ListObjAppendElement(xcinterp, tclparams,
5064 evaluate_raw(cschem, paramlist, cinst, NULL));
5065 break;
5068 Tcl_ListObjAppendElement(xcinterp, tclnewcall,
5069 Tcl_NewStringObj("parameters", 10));
5070 Tcl_ListObjAppendElement(xcinterp, tclnewcall, tclparams);
5073 /* Called ports are listed by key:value pair (port number: net id) */
5075 if (calls->callobj->ports != NULL) {
5076 tclports = Tcl_NewListObj(0, NULL);
5077 for (ports = calls->callobj->ports; ports != NULL;
5078 ports = ports->next) {
5079 portid = ports->portid;
5080 for (plist = calls->ports; plist != NULL; plist = plist->next)
5081 if (plist->portid == ports->portid)
5082 break;
5084 pnet = (plist == NULL) ? netmax(cschem) + 1 : plist->netid;
5086 netnum = Tcl_NewIntObj((int)pnet);
5087 portnum = Tcl_NewIntObj(portid);
5088 Tcl_ListObjAppendElement(xcinterp, tclports, portnum);
5089 Tcl_ListObjAppendElement(xcinterp, tclports, netnum);
5090 if ((pnet >= 0) && (netsdone[pnet] == (char)0))
5092 Tcl_ListObjAppendElement(xcinterp, tclnets, netnum);
5093 Tcl_ListObjAppendElement(xcinterp, tclnets,
5094 TclGetStringParts(nettopin(pnet, cschem, NULL)));
5095 /* record this net as having been added to the list */
5096 netsdone[pnet] = (char)1;
5099 Tcl_ListObjAppendElement(xcinterp, tclnewcall,
5100 Tcl_NewStringObj("ports", 5));
5101 Tcl_ListObjAppendElement(xcinterp, tclnewcall, tclports);
5103 Tcl_ListObjAppendElement(xcinterp, tclcalls, tclnewcall);
5105 Tcl_ListObjAppendElement(xcinterp, subckt, Tcl_NewStringObj("calls", 5));
5106 Tcl_ListObjAppendElement(xcinterp, subckt, tclcalls);
5108 free(netsdone);
5110 /* If the object has info labels, write a device list. */
5111 /* Check both the schematic and its symbol for info labels. */
5113 tcldevs = Tcl_NewListObj(0, NULL);
5114 if (cschem->symschem != NULL) {
5115 newdev = tclparseinfo(cschem->symschem);
5116 Tcl_ListObjAppendElement(xcinterp, tcldevs, newdev);
5118 newdev = tclparseinfo(cschem);
5119 Tcl_ListObjAppendElement(xcinterp, tcldevs, newdev);
5120 Tcl_ListObjAppendElement(xcinterp, subckt, Tcl_NewStringObj("devices", 7));
5121 Tcl_ListObjAppendElement(xcinterp, subckt, tcldevs);
5123 /* Write the network cross-reference dictionary */
5124 Tcl_ListObjAppendElement(xcinterp, subckt, Tcl_NewStringObj("nets", 4));
5125 Tcl_ListObjAppendElement(xcinterp, subckt, tclnets);
5127 Tcl_ListObjAppendElement(xcinterp, cktlist, subckt);
5130 /*----------------------------------------------------------------------*/
5132 Tcl_Obj *tcltoplevel(objinstptr cinst)
5134 Tcl_Obj *cktlist;
5135 objectptr cschem = cinst->thisobject;
5137 cktlist = Tcl_NewListObj(0, NULL);
5138 cleartraversed(cschem);
5139 tclhierarchy(cschem, cinst, NULL, cktlist);
5140 return cktlist;
5143 #endif /* TCL_WRAPPER */
5145 /*----------------------------------------------------------------------*/
5146 /* Create generic netlist in the Python interpreter variable space */
5147 /*----------------------------------------------------------------------*/
5149 #ifdef HAVE_PYTHON
5151 static PyObject *pyparseinfo(objectptr cschem)
5153 genericptr *pgen;
5154 labelptr plabel;
5156 PyObject *rlist = PyList_New(0);
5158 for (pgen = cschem->plist; pgen < cschem->plist + cschem->parts; pgen++) {
5159 if (IS_LABEL(*pgen)) {
5160 plabel = TOLABEL(pgen);
5161 if (plabel->pin == INFO)
5162 PyList_Append(rlist, PyGetStringParts(plabel->string));
5165 return rlist;
5168 /*----------------------------------------------------------------------*/
5169 /* Write global variables to Python dictionary */
5170 /*----------------------------------------------------------------------*/
5172 PyObject *pyglobals(objectptr cschem)
5174 LabellistPtr llist;
5175 labelptr gpin;
5176 PyObject *gdict, *netnum;
5177 int netid, lbus;
5178 buslist *sbus;
5180 gdict = PyDict_New();
5181 for (llist = global_labels; llist != NULL; llist = llist->next) {
5182 gpin = llist->label;
5183 for (lbus = 0;;) {
5184 if (llist->subnets == 0)
5185 netid = llist->net.id;
5186 else {
5187 sbus = llist->net.list + lbus;
5188 netid = sbus->netid;
5190 netnum = PyInt_FromLong((long)(netid));
5191 PyDict_SetItem(gdict, netnum, PyGetStringParts(gpin->string));
5192 if (++lbus >= llist->subnets) break;
5195 return gdict;
5198 /*----------------------------------------------------------------------*/
5199 /* Write a generic hierarchical netlist into Python list "subcircuits" */
5200 /*----------------------------------------------------------------------*/
5202 void pyhierarchy(objectptr cschem, CalllistPtr cfrom, PyObject *cktlist)
5204 CalllistPtr calls = cschem->calls;
5205 PortlistPtr ports, plist;
5206 int netid, pnet, portid, length, plen, i;
5207 PyObject *pyports, *pycalls, *pynewcall, *pynets, *netnum;
5208 PyObject *pydevs, *pyparams, *subckt, *newdev, *pylabel;
5209 oparamptr paramlist;
5211 /* Trivial objects are by definition those that are supposed */
5212 /* to be resolved by the netlist generation prior to output */
5213 /* and ignored by the output generator. */
5215 if (cschem->schemtype == TRIVIAL) return;
5217 /* Make sure that all the subcircuits have been written first */
5219 for (; calls != NULL; calls = calls->next) {
5220 if (calls->callobj->traversed == False) {
5221 calls->callobj->traversed = True;
5222 pyhierarchy(calls->callobj, calls, cktlist);
5226 /* Write own subcircuit netlist */
5227 subckt = PyDict_New();
5229 /* Prepare the dictionary of network cross-references */
5230 pynets = PyDict_New();
5232 /* Write the name */
5233 PyDict_SetItem(subckt, PyString_FromString("name"),
5234 PyString_FromString(cschem->name));
5236 /* Write the list of ports */
5237 if ((ports = cschem->ports) != NULL) {
5239 /* List of ports in subcircuit. */
5240 /* Each parameter connects to a net which may have multiple */
5241 /* names, so find the net associated with the parameter */
5242 /* and convert it back into a pin name in the usual manner */
5243 /* using nettopin(). */
5245 pyports = PyList_New(0);
5246 for (; ports != NULL; ports = ports->next) {
5247 netid = ports->netid;
5248 netnum = PyInt_FromLong((long)netid);
5249 PyList_Append(pyports, netnum);
5250 if (netid >= 0)
5251 PyDict_SetItem(pynets, netnum,
5252 PyGetStringParts(nettopin(netid, cschem, NULL)));
5254 PyDict_SetItem(subckt, PyString_FromString("ports"), pyports);
5257 /* Write the list of parameters */
5259 if (cschem->params != NULL) {
5260 pyparams = PyList_New(0);
5262 for (paramlist = cschem->params; paramlist != NULL; paramlist = paramlist->next) {
5263 if (paramlist->type == XC_INT)
5264 PyList_Append(pyparams,
5265 PyInt_FromLong((long)paramlist->parameter.ivalue));
5266 if (paramlist->type == XC_FLOAT)
5267 PyList_Append(pyparams,
5268 PyFloat_FromDouble((double)paramlist->parameter.fvalue));
5269 else if (paramlist->type == XC_STRING) {
5270 pylabel = PyGetStringParts(paramlist->parameter.string);
5271 /* Should get rid of last entry, "End Parameter"; not needed */
5272 PyList_Append(pyparams, pylabel);
5275 PyDict_SetItem(subckt, PyString_FromString("parameters"), pyparams);
5278 /* Write the list of calls to subcircuits */
5280 if ((calls = cschem->calls) != NULL) {
5281 pycalls = PyList_New(0);
5282 for (; calls != NULL; calls = calls->next) {
5284 /* Don't write calls to non-functional subcircuits. */
5285 if (calls->callobj->schemtype == TRIVIAL) continue;
5287 pynewcall = PyDict_New();
5288 PyDict_SetItem(pynewcall, PyString_FromString("name"),
5289 PyString_FromString(calls->callobj->name));
5291 /* Log any local parameter instances */
5292 if (calls->callinst->params != NULL) {
5293 pyparams = PyList_New(0);
5295 for (paramlist = cschem->params; paramlist != NULL;
5296 paramlist = paramlist->next) {
5297 if (paramlist->type == XC_INT)
5298 PyList_Append(pyparams,
5299 PyInt_FromLong((long)paramlist->parameter.ivalue));
5300 else if (paramlist->type == XC_FLOAT)
5301 PyList_Append(pyparams,
5302 PyFloat_FromDouble((double)paramlist->parameter.fvalue));
5303 else if (paramlist->type == XC_STRING)
5304 PyList_Append(pyparams,
5305 PyGetStringParts(paramlist->parameter.string));
5307 PyDict_SetItem(pynewcall, PyString_FromString("parameters"),
5308 pyparams);
5311 /* The object's definition lists calls in the order of the object's */
5312 /* port list. Therefore, use this order to find the port list. */
5313 /* Unconnected ports will be NULL entries in the list (?). */
5315 if (calls->callobj->ports != NULL) {
5316 pyports = PyList_New(0);
5317 for (ports = calls->callobj->ports; ports != NULL;
5318 ports = ports->next) {
5319 portid = ports->portid;
5320 for (plist = calls->ports; plist != NULL; plist = plist->next)
5321 if (plist->portid == ports->portid)
5322 break;
5324 pnet = (plist == NULL) ? netmax(cschem) + 1 : plist->netid;
5326 netnum = PyInt_FromLong((long)pnet);
5327 PyList_Append(pyports, netnum);
5328 if (pnet >= 0)
5329 PyDict_SetItem(pynets, netnum,
5330 PyGetStringParts(nettopin(pnet, cschem, NULL)));
5332 PyDict_SetItem(pynewcall, PyString_FromString("ports"), pyports);
5334 PyList_Append(pycalls, pynewcall);
5336 PyDict_SetItem(subckt, PyString_FromString("calls"), pycalls);
5339 /* If the object has info labels, write a device list. */
5340 /* Check both the schematic and its symbol for info labels. */
5342 pydevs = PyList_New(0);
5343 if (cschem->symschem != NULL) {
5344 newdev = pyparseinfo(cschem->symschem);
5345 PyList_Append(pydevs, newdev);
5347 newdev = pyparseinfo(cschem);
5348 PyList_Append(pydevs, newdev);
5349 PyDict_SetItem(subckt, PyString_FromString("devices"), pydevs);
5351 /* Write the network cross-reference dictionary */
5352 PyDict_SetItem(subckt, PyString_FromString("nets"), pynets);
5354 PyList_Append(cktlist, subckt);
5357 /*----------------------------------------------------------------------*/
5359 PyObject *pytoplevel(objectptr cschem)
5361 PyObject *cktlist;
5363 cktlist = PyList_New(0);
5364 cleartraversed(cschem);
5365 pyhierarchy(cschem, NULL, cktlist);
5366 return cktlist;
5369 #endif /* HAVE_PYTHON */
5371 /*----------------------------------------------------------------------*/
5372 /* Update networks in an object by checking if the network is valid, */
5373 /* and destroying and recreating the network if necessary. */
5374 /* */
5375 /* Run cleartraversed() on all types. The traversed flag allows a */
5376 /* check for infinite recursion when creating calls. */
5377 /* */
5378 /* Returns -1 on discovery of infinite recursion, 0 on failure to */
5379 /* generate a net, and 1 on success. */
5380 /*----------------------------------------------------------------------*/
5382 int updatenets(objinstptr uinst, Boolean quiet) {
5383 objectptr thisobject;
5384 objinstptr thisinst;
5385 int spage;
5387 /* Never try to generate a netlist while a file is being read, as */
5388 /* can happen if an expression parameter attempts to query a netlist */
5389 /* node. */
5391 if (load_in_progress) return 0;
5393 if (uinst->thisobject->symschem != NULL
5394 && uinst->thisobject->schemtype != PRIMARY) {
5395 thisobject = uinst->thisobject->symschem;
5396 if ((spage = is_page(thisobject)) >= 0)
5397 thisinst = xobjs.pagelist[spage]->pageinst;
5399 else {
5400 thisobject = uinst->thisobject;
5401 thisinst = uinst;
5404 if (checkvalid(thisobject) == -1) {
5405 uselection *ssave;
5407 if (cleartraversed(thisobject) == -1) {
5408 Wprintf("Netlist error: Check for recursion in circuit!");
5409 return -1;
5412 /* Note: destroy/create nets messes up the part list. Why?? */
5414 if (areawin->selects > 0)
5415 ssave = remember_selection(areawin->topinstance, areawin->selectlist,
5416 areawin->selects);
5417 destroynets(thisobject);
5418 createnets(thisinst, quiet);
5419 if (areawin->selects > 0) {
5420 areawin->selectlist = regen_selection(areawin->topinstance, ssave);
5421 free_selection(ssave);
5425 if (thisobject->labels == NULL && thisobject->polygons == NULL) {
5426 if (quiet == FALSE)
5427 Wprintf("Netlist error: No netlist elements in object %s",
5428 thisobject->name);
5429 return 0;
5431 return 1;
5434 /*----------------------------------------------------------------------*/
5435 /* Generate netlist, write information according to mode, and then */
5436 /* destroy the netlist (this will be replaced eventually with a dynamic */
5437 /* netlist model in which the netlist changes according to editing of */
5438 /* individual elements, not created and destroyed wholesale) */
5439 /*----------------------------------------------------------------------*/
5441 void writenet(objectptr thisobject, char *mode, char *suffix)
5443 objectptr cschem;
5444 objinstptr thisinst;
5445 char filename[100];
5446 char *prefix, *cpos, *locmode = mode, *stsave = NULL;
5447 FILE *fp;
5448 /* Calllist topcell; (jdk) */
5449 Boolean is_spice = FALSE, sp_end_save = spice_end;
5451 /* Always use the master schematic, if there is one. */
5453 if (thisobject->schemtype == SECONDARY)
5454 cschem = thisobject->symschem;
5455 else
5456 cschem = thisobject;
5458 /* Update the netlist if this has not been done */
5460 if (NameToPageObject(cschem->name, &thisinst, NULL) == NULL) {
5461 Wprintf("Not a schematic. . . cannot generate output!\n");
5462 return;
5464 if (updatenets(thisinst, FALSE) <= 0) {
5465 Wprintf("No file written!");
5466 return;
5469 prefix = (char *)malloc(sizeof(char));
5470 *prefix = '\0';
5472 if ((cpos = strchr(cschem->name, ':')) != NULL) *cpos = '\0';
5473 sprintf(filename, "%s.%s", cschem->name, suffix);
5474 if (cpos != NULL) *cpos = ':';
5476 if (!strncmp(mode, "index", 5)) { /* This mode generates no output */
5477 locmode += 5;
5478 fp = (FILE *)NULL;
5480 else if ((fp = fopen(filename, "w")) == NULL) {
5481 Wprintf("Could not open file %s for writing.", filename);
5482 free(prefix);
5483 return;
5486 /* Clear device indices from any previous netlist output */
5487 cleartraversed(cschem);
5488 clear_indices(cschem);
5490 /* Make sure list of include-once files is empty */
5492 free_included();
5494 /* Handle different netlist modes */
5496 if (!strcmp(mode, "spice")) {
5498 if (thisobject->schemtype == SYMBOL)
5499 cschem = thisobject->symschem;
5501 is_spice = TRUE;
5502 fprintf(fp, "*SPICE %scircuit <%s> from XCircuit v%s rev %s\n\n",
5503 (thisobject->schemtype == SYMBOL) ? "sub" : "",
5504 cschem->name, PROG_VERSION, PROG_REVISION);
5506 /* writeglobals(cschem, fp); */
5507 cleartraversed(cschem);
5508 writehierarchy(cschem, thisinst, NULL, fp, mode);
5510 else if (!strcmp(mode, "flatspice")) {
5511 is_spice = TRUE;
5512 fprintf(fp, "*SPICE (flattened) circuit \"%s\" from XCircuit v%s rev %s\n\n",
5513 cschem->name, PROG_VERSION, PROG_REVISION);
5514 if (stsave != NULL) {
5515 fputs(stsave, fp);
5516 fprintf(fp, "\n");
5518 topflat(cschem, thisinst, NULL, prefix, fp, mode);
5520 else if (!strcmp(mode, "pseuspice")) {
5521 is_spice = TRUE;
5522 fprintf(fp, "*SPICE subcircuit \"%s\" from XCircuit v%s rev %s\n\n",
5523 cschem->name, PROG_VERSION, PROG_REVISION);
5524 if (stsave != NULL) {
5525 fputs(stsave, fp);
5526 fprintf(fp, "\n");
5528 writeflat(cschem, NULL, prefix, fp, mode);
5529 freeflatindex();
5531 else if (!strcmp(mode, "flatsim") || !strcmp(mode, "pseusim")) {
5532 fprintf(fp, "| sim circuit \"%s\" from XCircuit v%s rev %s\n",
5533 cschem->name, PROG_VERSION, PROG_REVISION);
5534 if (stsave != NULL) {
5535 fputs(stsave, fp);
5536 fprintf(fp, "\n");
5538 topflat(cschem, thisinst, NULL, prefix, fp, mode);
5540 else if (!strcmp(locmode, "pcb")) {
5541 struct Ptab *ptable = (struct Ptab *)NULL;
5542 writepcb(&ptable, cschem, NULL, "", mode);
5543 if (stsave != NULL) {
5544 fputs(stsave, fp);
5545 fprintf(fp, "\n");
5547 outputpcb(ptable, fp);
5548 freepcb(ptable);
5550 else if (!strncmp(mode, "flat", 4)) {
5551 /* User-defined modes beginning with the word "flat": */
5552 /* Assume a flattened netlist, and assume nothing else. */
5554 if (thisobject->schemtype == SYMBOL)
5555 cschem = thisobject->symschem;
5556 cleartraversed(cschem);
5557 writeflat(cschem, NULL, prefix, fp, mode);
5558 freeflatindex();
5560 else if (!strncmp(mode, "pseu", 4)) {
5561 /* User-defined modes beginning with the word "pseu": */
5562 /* Assume a flattened netlist for everything under the top level. */
5564 if (thisobject->schemtype == SYMBOL)
5565 cschem = thisobject->symschem;
5566 cleartraversed(cschem);
5567 topflat(cschem, thisinst, NULL, prefix, fp, mode);
5569 else {
5570 /* All user-defined modes: Assume a hierarchical netlist, and */
5571 /* assume nothing else. */
5573 if (thisobject->schemtype == SYMBOL)
5574 cschem = thisobject->symschem;
5575 cleartraversed(cschem);
5576 writehierarchy(cschem, thisinst, NULL, fp, mode);
5579 /* Finish up SPICE files with a ".end" statement (if requested) */
5580 if (is_spice && (spice_end == True)) fprintf(fp, ".end\n");
5581 spice_end = sp_end_save;
5583 /* Finish up */
5585 if (fp != NULL) {
5586 fclose(fp);
5587 Wprintf("%s netlist saved as %s", mode, filename);
5589 if (stsave != NULL) free(stsave);
5590 free(prefix);
5593 /*----------------------------------------------------------------------*/
5594 /* Flatten netlist and save into a table of pcb-style nets */
5595 /* The routine is recursive. Each call returns TRUE if some nested */
5596 /* call generated output; this is a reasonable way to tell if we have */
5597 /* reached the bottom of the hierarchy. */
5598 /*----------------------------------------------------------------------*/
5600 Boolean writepcb(struct Ptab **ptableptr, objectptr cschem, CalllistPtr cfrom,
5601 char *prefix, char *mode)
5603 PolylistPtr plist;
5604 LabellistPtr llist;
5605 CalllistPtr calls;
5606 PortlistPtr ports;
5607 int i, testnet, tmplen, subnet;
5608 char *newprefix = (char *)malloc(sizeof(char));
5609 char *sout, *snew;
5610 struct Ptab *hidx, *htmp;
5611 struct Pstr *tmpstr;
5612 struct Pnet *tmpnet;
5613 objinstptr cinst;
5614 buslist *sbus;
5615 int locnet, lbus;
5616 Boolean outputdone = FALSE;
5617 Boolean outputcall;
5619 /* Step 1A: Go through the polygons of this object and add */
5620 /* any unvisited nets to the table. */
5622 for (plist = cschem->polygons; plist != NULL; plist = plist->next) {
5623 for (lbus = 0;;) {
5624 if (plist->subnets == 0)
5625 testnet = plist->net.id;
5626 else {
5627 sbus = plist->net.list + lbus;
5628 testnet = sbus->netid;
5631 hidx = *ptableptr;
5632 while (hidx != NULL) {
5633 if (hidx->nets != NULL) {
5634 for (i = 0; i < hidx->nets->numnets; i++)
5635 if (*(hidx->nets->netidx + i) == testnet) break;
5636 if (i < hidx->nets->numnets) break;
5638 hidx = hidx->next;
5640 if (hidx == NULL) { /* make new entry for net in table */
5641 htmp = (struct Ptab *)malloc(sizeof(struct Ptab));
5642 tmpnet = (struct Pnet *)malloc(sizeof(struct Pnet));
5643 tmpnet->numnets = 1;
5644 tmpnet->netidx = (int *)malloc(sizeof(int));
5645 *(tmpnet->netidx) = testnet;
5646 tmpnet->next = NULL;
5647 htmp->cschem = cschem;
5648 htmp->nets = tmpnet;
5649 htmp->pins = NULL;
5650 htmp->next = *ptableptr;
5651 *ptableptr = htmp;
5652 /* Fprintf(stdout, "Added new index entry for net %d\n", testnet); */
5654 if (++lbus >= plist->subnets) break;
5658 /* Step 1B: Do the same thing for labels. */
5660 for (llist = cschem->labels; llist != NULL; llist = llist->next) {
5661 for (lbus = 0;;) {
5662 if (llist->subnets == 0)
5663 testnet = llist->net.id;
5664 else {
5665 sbus = llist->net.list + lbus;
5666 testnet = sbus->netid;
5669 hidx = *ptableptr;
5670 while (hidx != NULL) {
5671 if (hidx->nets != NULL) {
5672 for (i = 0; i < hidx->nets->numnets; i++)
5673 if (*(hidx->nets->netidx + i) == testnet) break;
5674 if (i < hidx->nets->numnets) break;
5676 hidx = hidx->next;
5678 if (hidx == NULL) { /* make new entry for net in table */
5679 htmp = (struct Ptab *)malloc(sizeof(struct Ptab));
5680 tmpnet = (struct Pnet *)malloc(sizeof(struct Pnet));
5681 tmpnet->numnets = 1;
5682 tmpnet->netidx = (int *)malloc(sizeof(int));
5683 *(tmpnet->netidx) = testnet;
5684 tmpnet->next = NULL;
5685 htmp->cschem = cschem;
5686 htmp->nets = tmpnet;
5687 htmp->pins = NULL;
5688 htmp->next = *ptableptr;
5689 *ptableptr = htmp;
5690 /* Fprintf(stdout, "Added new index entry for net %d\n", testnet); */
5692 if (++lbus >= llist->subnets) break;
5696 /* Step 2: Resolve fixed device indices */
5697 resolve_devindex(cschem, FALSE);
5699 /* Step 3: Go through the list of calls to search for endpoints */
5701 for (calls = cschem->calls; calls != NULL; calls = calls->next) {
5702 objectptr cthis;
5704 cinst = calls->callinst;
5706 /* Trivial objects should have been dealt with already. */
5707 /* If we don't continue the loop here, you get netlist output for */
5708 /* objects like dots and circles. */
5709 if (calls->callobj->schemtype == TRIVIAL) continue;
5711 /* Step 4: If call is to a bottom-most schematic, output the device connections */
5712 /* Info-label can provide an alternate name or specify the instance number */
5714 cthis = calls->callobj;
5715 if (calls->callobj->schemtype == PRIMARY || calls->callobj->schemtype ==
5716 SECONDARY)
5717 if (calls->callobj->symschem != NULL)
5718 cthis = calls->callobj->symschem;
5720 if ((sout = parseinfo(cschem, cthis, calls, prefix, mode, FALSE, TRUE)) == NULL) {
5721 if (calls->devname == NULL)
5722 calls->devname = strdup(calls->callinst->thisobject->name);
5723 sprintf(_STR, "%s_%s", calls->devname, d36a(devindex(cschem, calls)));
5725 else {
5726 sscanf(sout, "%s", _STR); /* Copy the first word out of sout */
5728 newprefix = (char *)realloc(newprefix, sizeof(char) * (strlen(prefix)
5729 + strlen(_STR) + 2));
5730 sprintf(newprefix, "%s%s/", prefix, _STR);
5732 /*----------------------------------------------------------------*/
5733 /* Parsing of the "pcb:" info label--- */
5734 /* Find all <net>=<name> strings and create the appropriate nets */
5735 /*----------------------------------------------------------------*/
5737 if (sout) {
5738 char rsave, *lhs, *rhs, *rend, *sptr = sout, *tmppinname;
5740 while ((rhs = strchr(sptr, '=')) != NULL) {
5741 Genericlist *implicit, newlist;
5742 struct Pstr *psrch = NULL;
5744 lhs = rhs - 1;
5745 while ((lhs >= sptr) && isspace(*lhs)) lhs--;
5746 while ((lhs >= sptr) && !isspace(*lhs)) lhs--;
5747 *rhs = '\0';
5748 rhs++;
5749 if (isspace(*lhs)) lhs++;
5750 while ((*rhs) && isspace(*rhs)) rhs++;
5751 for (rend = rhs; (*rend) && (!isspace(*rend)); rend++);
5752 rsave = *rend;
5753 *rend = '\0';
5755 /* Get the net from the equation RHS. If none exists, assume */
5756 /* the name is a global net and add it to the list of globals. */
5758 implicit = nametonet(cschem, cinst, rhs);
5759 if (implicit == NULL) {
5760 stringpart *strptr;
5761 label templabel;
5763 labeldefaults(&templabel, GLOBAL, 0, 0);
5764 strptr = templabel.string;
5765 strptr->type = TEXT_STRING;
5766 strptr->data.string = strdup(rhs);
5767 newlist.subnets = 0;
5768 newlist.net.id = globalmax() - 1;
5769 addglobalpin(cschem, cinst, &templabel, &newlist);
5770 implicit = &newlist;
5771 free(strptr->data.string);
5774 /* Get the name of the pin */
5775 tmppinname = (char *)malloc(strlen(newprefix) + strlen(lhs) + 2);
5776 strcpy(tmppinname, newprefix);
5777 if ((tmplen = strlen(newprefix)) > 0) tmppinname[tmplen - 1] = '-';
5778 strcat(tmppinname, lhs);
5780 /* Find the net in the ptable, or create a new entry for it */
5782 hidx = *ptableptr;
5783 while (hidx != NULL) {
5784 if (hidx->nets != NULL) {
5785 for (i = 0; i < hidx->nets->numnets; i++)
5786 if (*(hidx->nets->netidx + i) == implicit->net.id)
5787 break;
5788 if (i < hidx->nets->numnets) break;
5790 hidx = hidx->next;
5792 if (hidx == NULL) {
5793 htmp = (struct Ptab *)malloc(sizeof(struct Ptab));
5794 tmpnet = (struct Pnet *)malloc(sizeof(struct Pnet));
5795 tmpnet->numnets = 1;
5796 tmpnet->netidx = (int *)malloc(sizeof(int));
5797 *(tmpnet->netidx) = implicit->net.id;
5798 tmpnet->next = NULL;
5799 htmp->cschem = cschem;
5800 htmp->nets = tmpnet;
5801 htmp->pins = NULL;
5802 htmp->next = *ptableptr;
5803 *ptableptr = htmp;
5804 hidx = htmp;
5806 else {
5807 /* Check if any entries are the same as tmppinname */
5808 for (psrch = hidx->pins; psrch != NULL; psrch = psrch->next) {
5809 if (!strcmp(psrch->string->data.string, tmppinname))
5810 break;
5814 /* Get the pin name from the equation LHS */
5815 if (psrch == NULL) {
5816 tmpstr = (struct Pstr *)malloc(sizeof(struct Pstr));
5817 tmpstr->string = (stringpart *)malloc(sizeof(stringpart));
5818 tmpstr->string->type = TEXT_STRING;
5819 tmpstr->string->nextpart = NULL;
5820 tmpstr->string->data.string = tmppinname;
5821 tmpstr->next = hidx->pins;
5822 hidx->pins = tmpstr;
5824 else {
5825 free(tmppinname);
5828 /* Proceed to the next LHS=RHS pair */
5829 *rend = rsave;
5830 sptr = rend;
5832 free(sout);
5835 outputcall = FALSE;
5836 if (calls->callobj->calls != NULL) {
5838 /* Step 4A: Push current net translations */
5839 /* (Don't push or pop global net numbers: no translation needed!) */
5841 hidx = *ptableptr;
5842 while (hidx != NULL) {
5843 if ((hidx->nets != NULL) && (((hidx->nets->numnets > 0) &&
5844 (*(hidx->nets->netidx) >= 0)) || (hidx->nets->numnets == 0))) {
5845 tmpnet = (struct Pnet *)malloc(sizeof(struct Pnet));
5846 tmpnet->numnets = 0;
5847 tmpnet->netidx = NULL;
5848 tmpnet->next = hidx->nets;
5849 hidx->nets = tmpnet;
5851 hidx = hidx->next;
5854 /* Step 4B: Generate net translation table for each subcircuit */
5856 for (ports = calls->ports; ports != NULL; ports = ports->next) {
5857 for (hidx = *ptableptr; hidx != NULL; hidx = hidx->next) {
5858 if (hidx->nets != NULL) {
5859 if (hidx->nets->next != NULL) {
5860 for (i = 0; i < hidx->nets->next->numnets; i++)
5861 if (*(hidx->nets->next->netidx + i) == ports->netid)
5862 break;
5863 if (i < hidx->nets->next->numnets) break;
5865 else if (ports->netid < 0) {
5866 if (hidx->nets->netidx != NULL)
5867 if (*(hidx->nets->netidx) == ports->netid)
5868 break;
5872 if (hidx != NULL) {
5873 hidx->nets->numnets++;
5874 if (hidx->nets->numnets == 1)
5875 hidx->nets->netidx = (int *)malloc(sizeof(int));
5876 else
5877 hidx->nets->netidx = (int *)realloc(hidx->nets->netidx,
5878 hidx->nets->numnets * sizeof(int));
5880 /* Translate net value */
5881 locnet = translatedown(ports->netid, ports->portid,
5882 calls->callobj);
5883 *(hidx->nets->netidx + hidx->nets->numnets - 1) = locnet;
5885 /* Fprintf(stdout, "Translation: net %d in object %s is net "
5886 "%d in object %s\n", ports->netid, cschem->name,
5887 locnet, calls->callobj->name); */
5891 /* Step 4C: Run routine recursively on the subcircuit */
5892 /* If it had a "pcb:" info label that was handled, then ignore */
5894 /* Fprintf(stdout, "Recursive call of writepcb() to %s\n",
5895 calls->callobj->name); */
5897 outputcall = writepcb(ptableptr, calls->callobj, calls, newprefix, mode);
5899 /* Step 4D: Pop the translation table */
5900 /* (Don't pop global nets (designated by negative net number)) */
5902 hidx = *ptableptr;
5903 while (hidx != NULL) {
5904 if ((hidx->nets != NULL) && (((hidx->nets->numnets > 0) &&
5905 (*(hidx->nets->netidx) >= 0)) || (hidx->nets->numnets == 0))) {
5906 tmpnet = hidx->nets->next;
5907 if (hidx->nets->numnets > 0) free(hidx->nets->netidx);
5908 free(hidx->nets);
5909 hidx->nets = tmpnet;
5911 hidx = hidx->next;
5915 if (!outputcall) {
5916 stringpart *ppin;
5918 /* Fprintf(stdout, "Reached lowest-level schematic: Finding connections\n"); */
5919 for (ports = calls->ports; ports != NULL; ports = ports->next) {
5920 locnet = translatedown(ports->netid, ports->portid, calls->callobj);
5921 /* Global pin names should probably be ignored, not just */
5922 /* when an object is declared to be "trivial". Not sure if */
5923 /* this breaks certain netlists. . . */
5924 if (locnet < 0) continue;
5926 /* Get the name of the pin in the called object with no prefix. */
5927 subnet = getsubnet(locnet, calls->callobj);
5928 ppin = nettopin(locnet, calls->callobj, NULL);
5929 hidx = *ptableptr;
5930 while (hidx != NULL) {
5931 if ((hidx->nets != NULL) && (hidx->nets->numnets > 0)) {
5932 /* Global nets were not translated, do not iterate through list */
5933 if (*(hidx->nets->netidx) >= 0) {
5934 for (i = 0; i < hidx->nets->numnets; i++)
5935 if (*(hidx->nets->netidx + i) == ports->netid)
5936 break;
5937 if (i < hidx->nets->numnets) break;
5938 } else {
5939 if (*(hidx->nets->netidx) == ports->netid) break;
5942 hidx = hidx->next;
5944 if (hidx == NULL) {
5945 snew = textprintsubnet(ppin, cinst, subnet);
5946 Fprintf(stdout, "Warning: Unconnected pin %s%s\n", newprefix, snew);
5947 free(snew);
5949 else {
5950 outputcall = TRUE;
5951 outputdone = TRUE;
5952 tmpstr = (struct Pstr *)malloc(sizeof(struct Pstr));
5953 tmpstr->string = (stringpart *)malloc(sizeof(stringpart));
5954 tmpstr->string->type = TEXT_STRING;
5955 tmpstr->string->nextpart = NULL;
5956 snew = textprintsubnet(ppin, cinst, subnet);
5957 tmpstr->string->data.string = (char *)malloc(strlen(newprefix)
5958 + strlen(snew) + 2);
5959 strcpy(tmpstr->string->data.string, newprefix);
5960 /* Replace slash '/' with dash '-' at bottommost level */
5961 if ((tmplen = strlen(newprefix)) > 0)
5962 tmpstr->string->data.string[tmplen - 1] = '-';
5963 strcat(tmpstr->string->data.string, snew);
5964 free(snew);
5965 tmpstr->next = hidx->pins;
5966 hidx->pins = tmpstr;
5968 /* diagnostic information */
5970 struct Pnet *locnet = hidx->nets;
5971 int ctr = 0;
5972 while (locnet->next != NULL) {
5973 locnet = locnet->next;
5974 ctr++;
5976 /* Fprintf(stdout, "Logged level-%d net %d (local net %d) pin %s\n",
5977 ctr, *(locnet->netidx), *(hidx->nets->netidx + i),
5978 tmpstr->string); */
5985 /* Step 5: Cleanup */
5987 free(newprefix);
5988 return outputdone;
5991 /*----------------------------------------------------------------------*/
5992 /* Save PCB table into pcb-style file */
5993 /*----------------------------------------------------------------------*/
5995 void outputpcb(struct Ptab *ptable, FILE *fp)
5997 int netidx = 1, ccol, subnet;
5998 struct Ptab *pseek;
5999 struct Pstr *sseek;
6000 char *snew;
6001 stringpart *ppin;
6003 if (fp == NULL) return;
6005 for (pseek = ptable; pseek != NULL; pseek = pseek->next) {
6006 if (pseek->pins != NULL) {
6007 if ((pseek->nets != NULL) && (pseek->nets->numnets > 0)) {
6008 subnet = getsubnet(*(pseek->nets->netidx), pseek->cschem);
6009 ppin = nettopin(*(pseek->nets->netidx), pseek->cschem, "");
6010 snew = textprintsubnet(ppin, NULL, subnet);
6011 strcpy(_STR, snew);
6012 free(snew);
6014 else
6015 sprintf(_STR, "NET%d ", netidx++);
6016 fprintf(fp, "%-11s ", _STR);
6017 ccol = 12;
6018 for (sseek = pseek->pins; sseek != NULL; sseek = sseek->next) {
6019 ccol += stringlength(sseek->string, False, NULL) + 3;
6020 if (ccol > 78) {
6021 fprintf(fp, "\\\n ");
6022 ccol = 18 + stringlength(sseek->string, False, NULL);
6024 snew = textprint(sseek->string, NULL);
6025 fprintf(fp, "%s ", snew);
6026 free(snew);
6028 fprintf(fp, "\n");
6030 /* else fprintf(fp, "NET%d *UNCONNECTED*\n", netidx++); */
6034 /*----------------------------------------------*/
6035 /* free memory allocated to PCB net tables */
6036 /*----------------------------------------------*/
6038 void freepcb(struct Ptab *ptable)
6040 struct Ptab *pseek, *pseek2;
6041 struct Pstr *sseek, *sseek2;
6042 struct Pnet *nseek, *nseek2;
6044 pseek = ptable;
6045 pseek2 = pseek;
6047 while (pseek2 != NULL) {
6048 pseek = pseek->next;
6050 sseek = pseek2->pins;
6051 sseek2 = sseek;
6052 while (sseek2 != NULL) {
6053 sseek = sseek->next;
6054 freelabel(sseek2->string);
6055 free(sseek2);
6056 sseek2 = sseek;
6059 nseek = pseek2->nets;
6060 nseek2 = nseek;
6061 while (nseek2 != NULL) {
6062 nseek = nseek->next;
6063 if (nseek2->numnets > 0) free(nseek2->netidx);
6064 free(nseek2);
6065 nseek2 = nseek;
6068 free(pseek2);
6069 pseek2 = pseek;
6073 /*----------------------------------------------------------------------*/
6074 /* Remove an element from the netlist. This is necessary when an */
6075 /* element is deleted from the object, so that the netlist does not */
6076 /* refer to a nonexistant element, or try to perform netlist function */
6077 /* on it. */
6078 /* */
6079 /* Return True or False depending on whether a pin was "orphaned" on */
6080 /* the corresponding schematic or symbol (if any), as this may cause */
6081 /* the class of the object (FUNDAMENTAL, TRIVIAL, etc.) to change. */
6082 /*----------------------------------------------------------------------*/
6084 Boolean RemoveFromNetlist(objectptr thisobject, genericptr thiselem)
6086 Boolean pinchanged = False;
6087 labelptr nlab;
6088 polyptr npoly;
6089 objectptr pschem;
6090 objinstptr ninst;
6092 PolylistPtr plist, plast;
6093 LabellistPtr llist, llast;
6094 CalllistPtr clist, clast;
6096 pschem = (thisobject->schemtype == SECONDARY) ? thisobject->symschem
6097 : thisobject;
6099 switch (thiselem->type) {
6100 case OBJINST:
6101 ninst = (objinstptr)thiselem;
6102 /* If this is an object instance, remove it from the calls */
6103 clast = NULL;
6104 for (clist = pschem->calls; clist != NULL; clist = clist->next) {
6105 if (clist->callinst == ninst) {
6106 if (clast == NULL)
6107 pschem->calls = clist->next;
6108 else
6109 clast->next = clist->next;
6110 freecalls(clist);
6111 clist = (clast) ? clast : pschem->calls;
6112 break;
6114 else
6115 clast = clist;
6117 break;
6119 case POLYGON:
6120 npoly = (polyptr)thiselem;
6121 if (nonnetwork(npoly)) break;
6123 /* If this is a polygon, remove it from the netlist */
6124 plast = NULL;
6125 for (plist = pschem->polygons; plist != NULL; plist = plist->next) {
6126 if (plist->poly == npoly) {
6127 if (plast == NULL)
6128 pschem->polygons = plist->next;
6129 else
6130 plast->next = plist->next;
6131 if (plist->subnets > 0)
6132 free(plist->net.list);
6133 break;
6135 else
6136 plast = plist;
6138 break;
6140 case LABEL:
6141 nlab = (labelptr)thiselem;
6142 if ((nlab->pin != LOCAL) && (nlab->pin != GLOBAL)) break;
6144 /* If this is a label, remove it from the netlist */
6145 llast = NULL;
6146 for (llist = pschem->labels; llist != NULL; llist = llist->next) {
6147 if (llist->label == nlab) {
6148 if (llast == NULL)
6149 pschem->labels = llist->next;
6150 else
6151 llast->next = llist->next;
6152 if (llist->subnets > 0)
6153 free(llist->net.list);
6154 break;
6156 else
6157 llast = llist;
6160 /* Mark pin label in corresponding schematic/symbol as "orphaned" */
6161 /* by changing designation from type "pin" to type "label". */
6163 if (findlabelcopy(nlab, nlab->string) == NULL) {
6164 changeotherpins(NULL, nlab->string);
6165 if (nlab->pin == INFO) pinchanged = True;
6168 return pinchanged;
6171 /*----------------------------------------------------------------------*/
6172 /* Remove one element from the netlist */
6173 /*----------------------------------------------------------------------*/
6175 void remove_netlist_element(objectptr cschem, genericptr genelem) {
6177 objectptr pschem;
6178 CalllistPtr clist, clast, cnext;
6179 LabellistPtr llist, llast, lnext;
6180 PolylistPtr plist, plast, pnext;
6181 Boolean found = FALSE;
6183 /* Always call on the primary schematic */
6184 pschem = (cschem->schemtype == SECONDARY) ? cschem->symschem : cschem;
6186 switch (ELEMENTTYPE(genelem)) {
6187 case POLYGON:
6188 /* remove polygon from polygon list */
6189 plast = NULL;
6190 for (plist = pschem->polygons; plist != NULL; ) {
6191 pnext = plist->next;
6192 if ((genericptr)plist->poly == genelem) {
6193 found = TRUE;
6194 if (plist->subnets > 0)
6195 free(plist->net.list);
6196 free(plist);
6197 if (plast != NULL)
6198 plast->next = pnext;
6199 else
6200 pschem->polygons = pnext;
6201 break;
6203 else
6204 plast = plist;
6205 plist = pnext;
6207 break;
6209 case LABEL:
6211 /* remove label from label list */
6212 llast = NULL;
6213 for (llist = pschem->labels; llist != NULL; ) {
6214 lnext = llist->next;
6215 if ((genericptr)llist->label == genelem) {
6216 found = TRUE;
6217 if (llist->subnets > 0)
6218 free(llist->net.list);
6219 free(llist);
6220 if (llast != NULL)
6221 llast->next = lnext;
6222 else
6223 pschem->labels = lnext;
6224 break;
6226 else
6227 llast = llist;
6228 llist = lnext;
6231 /* also check the globals */
6232 llast = NULL;
6233 for (llist = global_labels; llist != NULL; ) {
6234 lnext = llist->next;
6235 if ((genericptr)llist->label == genelem) {
6236 found = TRUE;
6237 if (llist->subnets > 0)
6238 free(llist->net.list);
6239 free(llist);
6240 if (llast != NULL)
6241 llast->next = lnext;
6242 else
6243 global_labels = lnext;
6244 break;
6246 else
6247 llast = llist;
6248 llist = lnext;
6250 break;
6252 case OBJINST:
6254 /* remove instance from call list */
6255 clast = NULL;
6256 for (clist = pschem->calls; clist != NULL; ) {
6257 cnext = clist->next;
6258 if ((genericptr)clist->callinst == genelem) {
6259 found = TRUE;
6260 freecalls(clist);
6261 if (clast != NULL)
6262 clast->next = cnext;
6263 else
6264 pschem->calls = cnext;
6266 else
6267 clast = clist;
6268 clist = cnext;
6270 break;
6272 if (found)
6273 pschem->valid = FALSE;
6276 /*----------------------------------------------------------------------*/
6277 /* Free memory allocated for the ports in a calls. */
6278 /*----------------------------------------------------------------------*/
6280 void freecalls(CalllistPtr calls)
6282 PortlistPtr ports, pptr;
6284 for (ports = calls->ports; ports != NULL;) {
6285 pptr = ports->next;
6286 free(ports);
6287 ports = pptr;
6289 if (calls->devname != NULL) free(calls->devname);
6290 free(calls);
6293 /*----------------------------------------------------------------------*/
6294 /* Free memory for a Genericlist structure (may also be a Labellist or */
6295 /* Polylist structure). */
6296 /*----------------------------------------------------------------------*/
6298 void freegenlist(Genericlist *nets)
6300 if (nets == NULL) return;
6301 if (nets->subnets > 0)
6302 free(nets->net.list);
6303 free(nets);
6306 /*----------------------------------------------------------------------*/
6307 /* Free memory allocated for the label list in a netlist. */
6308 /*----------------------------------------------------------------------*/
6310 void freelabellist(LabellistPtr *listtop)
6312 LabellistPtr labellist, llist;
6314 for (labellist = *listtop; labellist != NULL;) {
6315 llist = labellist->next;
6316 freegenlist((Genericlist *)labellist);
6317 labellist = llist;
6319 *listtop = NULL;
6322 /*----------------------------------------------------------------------*/
6323 /* Free memory allocated for the polygon list in a netlist. */
6324 /*----------------------------------------------------------------------*/
6326 void freepolylist(PolylistPtr *listtop)
6328 PolylistPtr polylist, plist;
6330 for (polylist = *listtop; polylist != NULL;) {
6331 plist = polylist->next;
6332 freegenlist((Genericlist *)polylist);
6333 polylist = plist;
6335 *listtop = NULL;
6338 /*----------------------------------------------------------------------*/
6339 /* Free memory allocated for netlist */
6340 /*----------------------------------------------------------------------*/
6342 void freenetlist(objectptr cschem)
6344 PolylistPtr *plist;
6345 LabellistPtr *llist;
6347 plist = &cschem->polygons;
6348 freepolylist(plist);
6349 llist = &cschem->labels;
6350 freelabellist(llist);
6353 /*----------------------------------------------------------------------*/
6354 /* Clear the "traversed" flag in all objects of the hierarchy. */
6355 /*----------------------------------------------------------------------*/
6357 int cleartraversed_level(objectptr cschem, int level)
6359 genericptr *cgen;
6360 objinstptr cinst;
6361 objectptr callobj, pschem;
6363 /* Always call on the primary schematic */
6364 pschem = (cschem->schemtype == SECONDARY) ? cschem->symschem : cschem;
6366 /* Recursively call cleartraversed() on all subobjects, a la gennetlist() */
6367 /* Use the parts list of the object, not the calls, because calls */
6368 /* may have been removed. */
6370 if (level == HIERARCHY_LIMIT) return -1;
6372 for (cgen = pschem->plist; cgen < pschem->plist + pschem->parts; cgen++) {
6373 if (IS_OBJINST(*cgen)) {
6374 cinst = TOOBJINST(cgen);
6376 if (cinst->thisobject->symschem != NULL)
6377 callobj = cinst->thisobject->symschem;
6378 else
6379 callobj = cinst->thisobject;
6381 /* Don't infinitely recurse if object is on its own schematic */
6382 /* However, we need to take a stab at more subtle recursion, too */
6384 if (callobj != pschem)
6385 if (cleartraversed_level(callobj, level + 1) == -1)
6386 return -1;
6389 pschem->traversed = False;
6391 return 0;
6394 /*----------------------------------------------------------------------*/
6395 /* This is the routine normally called, as it hides the "level" */
6396 /* argument tagging the level of recursion. */
6397 /*----------------------------------------------------------------------*/
6399 int cleartraversed(objectptr cschem) {
6400 return cleartraversed_level(cschem, 0);
6403 /*----------------------------------------------------------------------*/
6404 /* If any part of the netlist is invalid, destroy the entire netlist */
6405 /*----------------------------------------------------------------------*/
6407 int checkvalid(objectptr cschem)
6409 genericptr *cgen;
6410 objinstptr cinst;
6411 objectptr callobj, pschem;
6413 /* If the object has been declared a non-network object, ignore it */
6414 if (cschem->schemtype == NONETWORK) return 0;
6416 /* Always operate on the master schematic */
6417 pschem = (cschem->schemtype == SECONDARY) ? cschem->symschem : cschem;
6419 /* Stop immediately if the netlist is invalid */
6420 if (pschem->valid == False) return -1;
6422 /* Otherwise, recursively call checkvalid() on all subobjects. */
6423 /* Use the parts list of the object, not the calls, because calls */
6424 /* may have been removed. */
6426 for (cgen = pschem->plist; cgen < pschem->plist + pschem->parts; cgen++) {
6427 if (IS_OBJINST(*cgen)) {
6428 cinst = TOOBJINST(cgen);
6430 if (cinst->thisobject->symschem != NULL)
6431 callobj = cinst->thisobject->symschem;
6432 else
6433 callobj = cinst->thisobject;
6435 /* Don't infinitely recurse if object is on its own schematic */
6437 if (callobj == pschem) continue;
6439 /* If there is a symbol, don't check its parts, but check if */
6440 /* its netlist has been checkvalid. */
6442 if ((cinst->thisobject->symschem != NULL) &&
6443 (cinst->thisobject->labels == NULL) &&
6444 (cinst->thisobject->polygons == NULL) &&
6445 (cinst->thisobject->valid == False))
6446 return -1;
6448 /* Recursive call on subschematic */
6449 if (checkvalid(callobj) == -1)
6450 return -1;
6453 return 0; /* All subnetlists and own netlist are valid */
6456 /*----------------------------------------------------------------------*/
6457 /* Free memory allocated to temporary labels generated for the netlist */
6458 /*----------------------------------------------------------------------*/
6460 void freetemplabels(objectptr cschem)
6462 genericptr *cgen;
6463 objinstptr cinst;
6464 objectptr callobj;
6466 /* Recursively call freetemplabels() on all subobjects, a la gennetlist() */
6467 /* Use the parts list of the object, not the calls, because calls */
6468 /* may have been removed. */
6470 for (cgen = cschem->plist; cgen < cschem->plist + cschem->parts; cgen++) {
6471 if (IS_OBJINST(*cgen)) {
6473 cinst = TOOBJINST(cgen);
6474 if (cinst->thisobject->symschem != NULL)
6475 callobj = cinst->thisobject->symschem;
6476 else
6477 callobj = cinst->thisobject;
6479 /* Don't infinitely recurse if object is on its own schematic */
6480 if (callobj != cschem) freetemplabels(callobj);
6482 /* Also free the temp labels of any associated symbol */
6483 if (cinst->thisobject->symschem != NULL) freetemplabels(cinst->thisobject);
6486 /* Free any temporary labels which have been created */
6488 else if (IS_LABEL(*cgen)) {
6489 labelptr clab = TOLABEL(cgen);
6490 /* int tmpval = (int)(cgen - cschem->plist); (jdk) */
6491 if (clab->string->type != FONT_NAME) {
6492 genericptr *tgen;
6494 clab = TOLABEL(cgen);
6495 freelabel(clab->string);
6496 free(clab);
6497 for (tgen = cgen + 1; tgen < cschem->plist + cschem->parts; tgen++)
6498 *(tgen - 1) = *tgen;
6499 cschem->parts--;
6500 cgen--;
6506 /*----------------------------------------------------------------------*/
6507 /* Free memory allocated for netlists, ports, and calls */
6508 /*----------------------------------------------------------------------*/
6510 void freenets(objectptr cschem)
6512 CalllistPtr calls, cptr;
6513 PortlistPtr ports, pptr;
6514 genericptr *cgen;
6515 objinstptr cinst;
6516 objectptr callobj;
6518 /* Recursively call freenets() on all subobjects, a la gennetlist() */
6519 /* Use the parts list of the object, not the calls, because calls */
6520 /* may have been removed. */
6522 if (cschem->schemtype == PRIMARY || cschem->schemtype == SECONDARY ||
6523 (cschem->schemtype == SYMBOL && cschem->symschem == NULL)) {
6524 for (cgen = cschem->plist; cgen < cschem->plist + cschem->parts; cgen++) {
6525 if (IS_OBJINST(*cgen)) {
6527 cinst = TOOBJINST(cgen);
6528 if (cinst->thisobject->symschem != NULL)
6529 callobj = cinst->thisobject->symschem;
6530 else
6531 callobj = cinst->thisobject;
6533 /* Don't infinitely recurse if object is on its own schematic */
6534 if (callobj != cschem) freenets(callobj);
6536 /* Also free the netlist of any associated symbol */
6537 if (cinst->thisobject->symschem != NULL) freenets(cinst->thisobject);
6542 /* Free the allocated structures for this object */
6544 for (calls = cschem->calls; calls != NULL;) {
6545 cptr = calls->next;
6546 freecalls(calls);
6547 calls = cptr;
6549 cschem->calls = NULL;
6551 for (ports = cschem->ports; ports != NULL;) {
6552 pptr = ports->next;
6553 free(ports);
6554 ports = pptr;
6556 cschem->ports = NULL;
6558 freenetlist(cschem);
6560 cschem->traversed = False;
6561 cschem->valid = False;
6562 freegenlist(cschem->highlight.netlist);
6563 cschem->highlight.netlist = NULL;
6564 cschem->highlight.thisinst = NULL;
6567 /*----------------------------------------------------------------------*/
6568 /* Free the global pin list */
6569 /*----------------------------------------------------------------------*/
6571 void freeglobals()
6573 LabellistPtr labellist, llist;
6575 for (labellist = global_labels; labellist != NULL;) {
6576 llist = labellist->next;
6578 /* Labels in the global list are temporary and must be deallocated */
6579 freelabel(labellist->label->string);
6580 free(labellist->label);
6582 freegenlist((Genericlist *)labellist);
6583 labellist = llist;
6585 global_labels = NULL;
6588 /*----------------------------------------------------------------------*/
6589 /* Get rid of locally-defined pin names */
6590 /*----------------------------------------------------------------------*/
6592 void clearlocalpins(objectptr cschem)
6594 NetnamePtr netnames, nextname;
6596 for (netnames = cschem->netnames; netnames != NULL; ) {
6597 nextname = netnames->next;
6598 if (netnames->localpin != NULL) {
6599 freelabel(netnames->localpin);
6601 free(netnames);
6602 netnames = nextname;
6604 cschem->netnames = NULL;
6607 /*----------------------------------------------------------------------*/
6608 /* Handle lists of included files */
6609 /*----------------------------------------------------------------------*/
6611 /*----------------------------------------------------------------------*/
6612 /* check_included() --- check if a file has already been included. */
6613 /* Check by inode instead of filename so that we cannot trick the */
6614 /* program by giving, e.g., one relative path and one full path. */
6615 /*----------------------------------------------------------------------*/
6617 Boolean check_included(char *filename)
6619 struct stat filestatus;
6620 int numi;
6622 if (stat(filename, &filestatus) == 0) {
6623 if (included_files == NULL) return FALSE;
6624 for (numi = 0; *(included_files + numi) != (ino_t)NULL; numi++) {
6625 if (*(included_files + numi) == filestatus.st_ino) return TRUE;
6628 return FALSE;
6631 /*----------------------------------------------------------------------*/
6632 /* append_included() --- update the list of included files by adding */
6633 /* the inode of filename to the list. */
6634 /*----------------------------------------------------------------------*/
6636 void append_included(char *filename)
6638 struct stat filestatus;
6639 int numi;
6641 if (stat(filename, &filestatus) == 0) {
6643 if (included_files == NULL) {
6644 included_files = (ino_t *)malloc(2 * sizeof(ino_t));
6645 *included_files = filestatus.st_ino;
6646 *(included_files + 1) = (ino_t)NULL;
6648 else {
6649 for (numi = 0; *(included_files + numi) != (ino_t)NULL; numi++);
6651 included_files = (ino_t *)realloc(included_files,
6652 (++numi + 1) * sizeof(ino_t));
6654 *(included_files + numi - 1) = filestatus.st_ino;
6655 *(included_files + numi) = (ino_t)NULL;
6658 else {
6659 Wprintf("Error: Cannot stat include file \"%s\"\n", filename);
6663 /*----------------------------------------------------------------------*/
6665 void free_included()
6667 if (included_files != NULL) {
6668 free(included_files);
6669 included_files = NULL;
6673 /*-------------------------------------------------------------------------*/