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. */
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. */
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 /*----------------------------------------------------------------------*/
24 #include <sys/types.h> /* For preventing multiple file inclusions, use stat() */
35 #include <X11/Intrinsic.h>
36 #include <X11/StringDefs.h>
43 /*----------------------------------------------------------------------*/
45 /*----------------------------------------------------------------------*/
48 #include "colordefs.h"
50 /*----------------------------------------------------------------------*/
51 /* Function prototype declarations */
52 /*----------------------------------------------------------------------*/
53 #include "prototypes.h"
56 extern PyObject
*PyGetStringParts(stringpart
*);
59 extern Tcl_Interp
*xcinterp
;
60 extern Tcl_Obj
*TclGetStringParts(stringpart
*);
63 /*----------------------------------------------------------------------*/
64 /* Externally declared global variables */
65 /*----------------------------------------------------------------------*/
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
{
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
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];
128 while ((locn
> 0) && (i
>= 0)) {
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. */
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
;
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
;
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) */
182 /* Translate pin position back to originating object */
185 UPreMultCTM(&locctm
, thisinst
->position
, thisinst
->scale
,
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
)
198 objectptr thisobj
= thisinst
->thisobject
;
201 if ((thisobj
->schemtype
== SYMBOL
) && (thisobj
->symschem
!= NULL
))
202 ports
= thisobj
->symschem
->ports
;
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! */
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
);
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
,
236 objectptr topobj
, thisobj
;
238 /* char numstr[12]; (jdk) */
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
)
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 */
260 else if ((updatenets(stack
->thisinst
, FALSE
) <= 0) || (topobj
->calls
== NULL
)) {
261 Wprintf("Error in generating netlists!");
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)
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
);
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
);
306 hierlen
= strlen(*hierstr
) + 2;
307 *hierstr
= realloc(*hierstr
, hierlen
+ devlen
);
310 sprintf(*hierstr
+ hierlen
, "%s%s(%s)",
311 ((hierlen
> 0) ? "/" : ""),
312 callinst
->thisobject
->name
, devstr
);
314 sprintf(*hierstr
+ hierlen
, "%s%s%s",
315 ((hierlen
> 0) ? "/" : ""),
316 (calls
->devname
== NULL
) ? callinst
->thisobject
->name
317 : calls
->devname
, devstr
);
324 /*----------------------------------------------------------------------*/
326 char *GetHierarchy(pushlistptr
*stackptr
, Boolean canonical
)
328 Boolean pushed_top
= FALSE
;
331 if ((*stackptr
) && ((*stackptr
)->thisinst
!= areawin
->topinstance
)) {
333 push_stack(stackptr
, areawin
->topinstance
, NULL
);
336 getnexthier(*stackptr
, &snew
, NULL
, canonical
);
338 if (pushed_top
) pop_stack(stackptr
);
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
;
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()
366 Boolean netcheck
= FALSE
;
368 for (i
= 0; i
< areawin
->selects
; i
++) {
369 genericptr gptr
= SELTOGENERIC(areawin
->selectlist
+ i
);
370 switch (gptr
->type
) {
372 if (!nonnetwork(TOPOLY(&gptr
)))
376 if ((TOLABEL(&gptr
))->pin
== LOCAL
|| (TOLABEL(&gptr
))->pin
== GLOBAL
)
380 if ((TOOBJINST(&gptr
))->thisobject
->schemtype
!= NONETWORK
)
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
)
396 dx
= point1
->x
- point2
->x
;
397 dy
= point1
->y
- point2
->y
;
399 if ((abs(dx
) < ONDIST
) && (abs(dy
) < ONDIST
)) return True
;
403 /*----------------------------------------------------------------------*/
404 /* createnets(): Generate netlist structures */
406 /* Result is the creation of three linked lists inside each object in */
407 /* the circuit hierarchy: */
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
;
436 Wprintf("Error: attempt to generate netlist for a symbol.");
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
)
459 pschem
= (thisobject
->schemtype
== SECONDARY
) ? thisobject
->symschem
:
462 freetemplabels(pschem
);
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
))
484 /*----------------------------------------------------------------------*/
485 /* Return the largest (most negative) net number in the global netlist */
486 /*----------------------------------------------------------------------*/
495 for (gl
= global_labels
; gl
!= NULL
; gl
= gl
->next
) {
496 if (!(gl
->subnets
)) {
497 if (gl
->net
.id
< smin
)
501 for (bidx
= 0; bidx
< gl
->subnets
; bidx
++) {
502 lbus
= gl
->net
.list
+ bidx
;
512 /*----------------------------------------------------------------------*/
513 /* Return the largest net number in an object's netlist */
514 /*----------------------------------------------------------------------*/
516 int netmax(objectptr cschem
)
524 for (gp
= cschem
->polygons
; gp
!= NULL
; gp
= gp
->next
) {
525 if (!(gp
->subnets
)) {
526 if (gp
->net
.id
> smax
)
530 for (bidx
= 0; bidx
< gp
->subnets
; bidx
++) {
531 lbus
= gp
->net
.list
+ bidx
;
538 for (gl
= cschem
->labels
; gl
!= NULL
; gl
= gl
->next
) {
539 if (!(gl
->subnets
)) {
540 if (gl
->net
.id
> smax
)
544 for (bidx
= 0; bidx
< gl
->subnets
; bidx
++) {
545 lbus
= gl
->net
.list
+ bidx
;
555 /*----------------------------------------------------------------------*/
556 /* Resolve nets and pins for the indicated object */
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 */
563 /*----------------------------------------------------------------------*/
565 void gennetlist(objinstptr thisinst
)
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) */
576 XPoint
*tpt
, *tpt2
, *endpt
, *endpt2
;
577 int i
, j
, n
, nextnet
, lbus
;
581 Genericlist
*netlist
, *tmplist
, *buspins
, *resolved_net
, newlist
;
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
)) ?
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
);
605 Fprintf(stderr
, "Error: associated schematic is not a page!\n");
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
)
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
:
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
) {
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
);
677 Fprintf(stderr
, "Error: associated schematic is not a page!\n");
681 callinst
= xobjs
.pagelist
[n
]->pageinst
;
684 callobj
= geninst
->thisobject
;
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
)
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. */
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
) {
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
))
732 if (lseek
!= NULL
) continue;
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 */
756 if (tmplist
== NULL
) {
758 snew
= textprint(clab
->string
, NULL
),
759 Fprintf(stderr
, "Warning: Pin \"%s\" in symbol %s has no "
760 "connection in schematic %s\n",
762 pschem
->symschem
->name
);
764 newlist
.net
.id
= nextnet
++;
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
);
788 mergenets(pschem
, netlist
, tmplist
);
791 if (netlist
== NULL
) {
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
);
801 tmplist
= addpin(cschem
, labinst
, clab
, netlist
);
806 tmplist
= addpin(cschem
, labinst
, clab
, netlist
);
811 else if (clab
->pin
== GLOBAL
) {
812 if (tmplist
== NULL
) {
814 tmplist
->net
.id
= globalmax() - 1;
816 addglobalpin(cschem
, cinst
, clab
, tmplist
);
817 addpin(cschem
, labinst
, clab
, tmplist
);
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
))
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
))
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;
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];
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. */
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
))
1057 subsibinst
= TOOBJINST(iseek
);
1060 newlist
= (pushlistptr
)malloc(sizeof(pushlist
));
1061 newlist
->thisinst
= isib
;
1062 newlist
->next
= schemtop
;
1065 search_on_siblings(cinst
, subsibinst
, schemtop
, llx
, lly
, urx
, ury
);
1070 schemtop
= schemtop
->next
;
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. */
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
;
1095 objinstptr cinst
, isib
, callinst
;
1096 objectptr callobj
, callsymb
, cschem
, pschem
;
1098 short ibllx
, iblly
, iburx
, ibury
, sbllx
, sblly
, sburx
, sbury
;
1099 int i
, j
, k
; /* , lbus; (jdk) */
1100 /* buslist *sbus; (jdk) */
1104 /* CalllistPtr cseek; (jdk) */
1106 Genericlist
*netfrom
, *netto
; /* , *othernet; (jdk) */
1108 /* The netlist is always kept in the master schematic */
1109 pschem
= (thisobject
->schemtype
== SECONDARY
) ? thisobject
->symschem
:
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
) {
1122 cschem
= thisobject
;
1125 cinst
= xobjs
.pagelist
[j
]->pageinst
;
1126 if ((cinst
== NULL
) ||
1127 ((cinst
->thisobject
!= pschem
) &&
1128 ((cinst
->thisobject
->schemtype
!= SECONDARY
) ||
1129 (cinst
->thisobject
->symschem
!= pschem
)))) continue;
1130 cschem
= cinst
->thisobject
;
1133 for (i
= 0; i
< cschem
->parts
; i
++) {
1134 cgen
= cschem
->plist
+ i
;
1135 if (IS_OBJINST(*cgen
)) {
1136 callinst
= TOOBJINST(cgen
);
1138 /* Determine where the hierarchy continues downward */
1140 if (callinst
->thisobject
->symschem
!= NULL
)
1141 callobj
= callinst
->thisobject
->symschem
;
1143 callobj
= callinst
->thisobject
;
1145 /* Ignore any object on its own schematic */
1147 if (callobj
== pschem
) continue;
1149 callsymb
= callinst
->thisobject
;
1151 /* Note: callobj is the next schematic in the hierarchy. */
1152 /* callsymb is the next visible object in the hierarchy, */
1153 /* which may be either a schematic or a symbol. */
1155 /*-------------------------------------------------------------*/
1156 /* For object instances which are their own schematics (i.e., */
1157 /* have netlist elements), don't rely on any pin list but make */
1158 /* a survey of how polygons connect into the object. */
1159 /*-------------------------------------------------------------*/
1161 if (callsymb
->symschem
== NULL
1162 && callobj
->schemtype
!= FUNDAMENTAL
1163 && callobj
->schemtype
!= TRIVIAL
) {
1165 /* Fprintf(stdout, "*** Analyzing connections from %s"
1166 " to instance of %s\n", cschem->name,
1167 callinst->thisobject->name); */
1169 /* Look for pins connecting to pins in the object */
1171 for (lseek
= pschem
->labels
; lseek
!= NULL
; lseek
= lseek
->next
) {
1172 if (lseek
->cschem
!= cschem
) continue;
1173 else if ((lseek
->cinst
!= NULL
) && (lseek
->cinst
!= callinst
))
1175 olabel
= lseek
->label
;
1176 searchconnect(&(olabel
->position
), 1, callinst
, lseek
->subnets
);
1177 /* if we've encountered a unique instance, then con- */
1178 /* tinue past all other instances using this label. */
1179 if (lseek
->cinst
!= NULL
)
1180 while (lseek
->next
&& (lseek
->next
->label
== lseek
->label
))
1181 lseek
= lseek
->next
;
1184 /* Look for polygon ends connecting into the object */
1186 for (pseek
= pschem
->polygons
; pseek
!= NULL
; pseek
= pseek
->next
) {
1187 if (pseek
->cschem
!= cschem
) continue;
1188 tpoly
= pseek
->poly
;
1189 searchconnect(tpoly
->points
, tpoly
->number
, callinst
, pseek
->subnets
);
1192 /* For each call to a schematic or symbol which is NOT the */
1193 /* one under consideration, see if it touches or overlaps */
1194 /* the object under consideration. Search for connections */
1195 /* between the two objects. */
1197 calcinstbbox(cgen
, &ibllx
, &iblly
, &iburx
, &ibury
);
1199 /* Only need to look forward from the current position. */
1200 for (k
= i
+ 1; k
< cschem
->parts
; k
++) {
1202 iseek
= cschem
->plist
+ k
;
1203 if (IS_OBJINST(*iseek
)) {
1204 calcinstbbox(iseek
, &sbllx
, &sblly
, &sburx
, &sbury
);
1206 /* Check intersection of the two object instances; */
1207 /* don't do a search if they are disjoint. */
1209 if ((ibllx
<= sburx
) && (iburx
>= sbllx
) &&
1210 (iblly
<= sbury
) && (ibury
>= sblly
)) {
1211 isib
= TOOBJINST(iseek
);
1212 search_on_siblings(callinst
, isib
, NULL
,
1213 ibllx
, iblly
, iburx
, ibury
);
1219 /*----------------------------------------------------------*/
1220 /* Recursively call gencalls() on the schematic. */
1221 /*----------------------------------------------------------*/
1223 if (callobj
->traversed
== False
)
1226 /*----------------------------------------------------------*/
1227 /* Create a call to the object callsymb from object cschem */
1228 /*----------------------------------------------------------*/
1230 addcall(cschem
, callobj
, callinst
);
1232 /*----------------------------------------------------------*/
1233 /* Search again on symbol pins to generate calls to ports. */
1234 /*----------------------------------------------------------*/
1237 UPreMultCTM(&locctm
, callinst
->position
, callinst
->scale
,
1238 callinst
->rotation
);
1240 for (lseek
= callsymb
->labels
; lseek
!= NULL
; lseek
= lseek
->next
) {
1241 /* LabellistPtr slab; (jdk) */
1242 /* labelptr slabel; (jdk) */
1244 if (lseek
->cschem
!= callsymb
) continue;
1245 else if ((lseek
->cinst
!= NULL
) && (lseek
->cinst
!= callinst
))
1248 olabel
= lseek
->label
;
1249 netto
= (Genericlist
*)lseek
;
1251 /* Translate pin position back to object cschem */
1252 UTransformbyCTM(&locctm
, &(olabel
->position
), &xpos
, 1);
1254 /* What net in the calling object connects to this point? */
1255 netfrom
= pointtonet(cschem
, callinst
, &xpos
);
1257 /* If there's no net, we make one */
1258 if (netfrom
== NULL
)
1259 netfrom
= make_tmp_pin(cschem
, callinst
, &xpos
, netto
);
1261 /* Generate a port call for a global signal if the */
1262 /* global label appears in the symbol (9/29/04). This */
1263 /* is a change from previous behavior, which was to not */
1264 /* make the call. */
1266 if ((netto
->subnets
== 0) && (netto
->net
.id
< 0))
1267 mergenets(pschem
, netfrom
, netto
);
1269 /* Attempt to generate a port in the object. */
1270 addport(callobj
, netto
);
1272 /* Generate the call to the port */
1273 if (addportcall(pschem
, netfrom
, netto
) == FALSE
) {
1275 // If object is "dot" then copy the bus from the
1276 // net to the dot. The dot takes on whatever
1277 // dimension the bus is.
1279 if (strstr(callobj
->name
, "::dot") != NULL
) {
1280 copy_bus(netto
, netfrom
);
1283 Fprintf(stderr
, "Error: attempt to connect bus size "
1284 "%d in %s to bus size %d in %s\n",
1285 netfrom
->subnets
, cschem
->name
,
1286 netto
->subnets
, callobj
->name
);
1290 /* if we've encountered a unique instance, then continue */
1291 /* past all other instances using this label. */
1292 if (lseek
->cinst
!= NULL
)
1293 while (lseek
->next
&& (lseek
->next
->label
== lseek
->label
))
1294 lseek
= lseek
->next
;
1297 /*----------------------------------------------------------*/
1298 /* If after all that, no ports were called, then remove the */
1299 /* call to this object instance. However, we should check */
1300 /* for info labels, because the device may produce output */
1301 /* for the netlist even if it has no declared ports. This */
1302 /* is irrespective of the presence of pin labels---if the */
1303 /* symbol is "trivial", the netlist connections are */
1304 /* resolved in the level of the hierarchy above, and there */
1305 /* may be no ports, but the call list must retain the call */
1306 /* to ensure that the netlist output is generated. */
1307 /*----------------------------------------------------------*/
1309 if (pschem
->calls
->ports
== NULL
)
1310 if (pschem
->infolabels
== FALSE
)
1311 removecall(pschem
, pschem
->calls
); /* Remove the call */
1317 /*----------------------------------------------------------------------*/
1318 /* Translate a net number down in the calling hierarchy */
1319 /*----------------------------------------------------------------------*/
1321 int translatedown(int rnet
, int portid
, objectptr nextobj
)
1326 for (nport
= nextobj
->ports
; nport
!= NULL
; nport
= nport
->next
) {
1327 if (nport
->portid
== portid
) {
1328 downnet
= nport
->netid
;
1335 /*----------------------------------------------------------------------*/
1336 /* Translate a netlist up in the calling hierarchy */
1338 /* This routine creates a new netlist header which needs to be freed */
1339 /* by the calling routine. */
1341 /* Note that if the entire netlist cannot be translated up, then the */
1342 /* routine returns NULL. This could be modified to return the part of */
1343 /* the network that can be translated up. */
1344 /*----------------------------------------------------------------------*/
1346 Genericlist
*translateup(Genericlist
*rlist
, objectptr thisobj
,
1347 objectptr nextobj
, objinstptr nextinst
)
1355 Genericlist
*tmplist
;
1357 tmplist
= (Genericlist
*)malloc(sizeof(Genericlist
));
1358 tmplist
->subnets
= 0;
1359 tmplist
->net
.id
= 0;
1360 copy_bus(tmplist
, rlist
);
1363 if (rlist
->subnets
== 0)
1364 rnet
= rlist
->net
.id
;
1366 sbus
= rlist
->net
.list
+ lbus
;
1369 for (nport
= nextobj
->ports
; nport
!= NULL
; nport
= nport
->next
) {
1370 if (nport
->netid
== rnet
) {
1371 portid
= nport
->portid
;
1377 for (ccall
= thisobj
->calls
; ccall
!= NULL
; ccall
= ccall
->next
) {
1378 if (ccall
->callinst
== nextinst
) {
1379 for (nport
= ccall
->ports
; nport
!= NULL
; nport
= nport
->next
) {
1380 if (nport
->portid
== portid
) {
1381 upnet
= nport
->netid
;
1385 if (nport
!= NULL
) break;
1389 freegenlist(tmplist
);
1393 if (tmplist
->subnets
== 0) {
1394 tmplist
->net
.id
= upnet
;
1397 sbus
= tmplist
->net
.list
+ lbus
;
1398 sbus
->netid
= upnet
;
1399 sbus
->subnetid
= getsubnet(upnet
, thisobj
);
1402 if (++lbus
>= rlist
->subnets
) break;
1407 /*----------------------------------------------------------------------*/
1408 /* Check whether the indicated polygon is already resolved into the */
1409 /* netlist of the object hierarchy described by seltop. */
1410 /* Return the netlist if resolved, NULL otherwise. The netlist */
1411 /* returned is referred (translated) upward through the calling */
1412 /* hierarchy to the topmost object containing that net or those nets. */
1413 /* This topmost object is returned in parameter topobj. */
1414 /* Note that the netlist returned does not necessarily correspond to */
1415 /* any object in the top level. It is allocated and must be freed by */
1416 /* the calling routine. */
1417 /*----------------------------------------------------------------------*/
1419 Genericlist
*is_resolved(genericptr
*rgen
, pushlistptr seltop
, objectptr
*topobj
)
1421 objectptr thisobj
= seltop
->thisinst
->thisobject
;
1425 Genericlist
*rlist
= NULL
, *newlist
;
1427 pschem
= (thisobj
->schemtype
== SECONDARY
) ? thisobj
->symschem
: thisobj
;
1429 /* Recursively call self, since we have to back out from the bottom of */
1432 if (seltop
->next
!= NULL
) {
1433 rlist
= is_resolved(rgen
, seltop
->next
, topobj
);
1435 /* Translate network ID up the hierarchy to the topmost object in which */
1436 /* the network exists. */
1438 if (rlist
!= NULL
) {
1439 newlist
= translateup(rlist
, pschem
, seltop
->next
->thisinst
->thisobject
,
1440 seltop
->next
->thisinst
);
1441 if (newlist
== NULL
)
1442 /* Net does not exist upwards of this object. Pass net ID */
1443 /* upward with "topobj" unchanged. */
1453 /* Find the net ID for the listed object, which should be in the object */
1454 /* on the bottom of the pushlist stack. */
1456 if (IS_POLYGON(*rgen
)) {
1457 for (pseek
= pschem
->polygons
; pseek
!= NULL
; pseek
= pseek
->next
) {
1458 if (pseek
->poly
== TOPOLY(rgen
)) {
1459 rlist
= (Genericlist
*)pseek
;
1464 else if (IS_LABEL(*rgen
)) {
1465 for (lseek
= pschem
->labels
; lseek
!= NULL
; lseek
= lseek
->next
) {
1466 if (lseek
->label
== TOLABEL(rgen
)) {
1467 rlist
= (Genericlist
*)lseek
;
1473 if (rlist
!= NULL
) {
1474 /* Make a copy of the netlist header from this element */
1475 newlist
= (Genericlist
*)malloc(sizeof(Genericlist
));
1476 newlist
->subnets
= 0;
1477 copy_bus(newlist
, rlist
);
1483 *topobj
= (rlist
== NULL
) ? NULL
: seltop
->thisinst
->thisobject
;
1487 /*--------------------------------------------------------------*/
1488 /* Highlight all the polygons and pin labels in a network */
1489 /* (recursively, downward). Pin labels are drawn only on the */
1490 /* topmost schematic object. */
1491 /* Returns true if some part of the hierarchy declared a net to */
1492 /* be highlighted. */
1493 /* mode = 1 highlight, mode = 0 erase */
1494 /*--------------------------------------------------------------*/
1496 Boolean
highlightnet(objectptr cschem
, objinstptr cinst
, int netid
, u_char mode
)
1505 int netto
, locnetid
, lbus
;
1506 int curcolor
= AUXCOLOR
;
1507 Boolean rval
= FALSE
;
1510 SetForeground(dpy
, areawin
->gc
, curcolor
);
1512 pschem
= (cschem
->schemtype
== SECONDARY
) ? cschem
->symschem
: cschem
;
1514 for (plist
= pschem
->polygons
; plist
!= NULL
; plist
= plist
->next
) {
1515 if (plist
->cschem
!= cschem
) continue;
1516 cpoly
= plist
->poly
;
1518 if (plist
->subnets
== 0)
1519 locnetid
= plist
->net
.id
;
1521 locnetid
= (plist
->net
.list
+ lbus
)->netid
;
1522 if (locnetid
== netid
) {
1523 /* Fprintf(stdout, " >> Found polygon belonging to net %d at (%d, %d)\n",
1524 locnetid, cpoly->points[0].x, cpoly->points[0].y); */
1525 if (mode
== 0 && cpoly
->color
!= curcolor
) {
1526 curcolor
= cpoly
->color
;
1527 XTopSetForeground(curcolor
);
1529 UDrawPolygon(cpoly
, xobjs
.pagelist
[areawin
->page
]->wirewidth
);
1532 if (++lbus
>= plist
->subnets
) break;
1536 /* Highlight labels if they belong to the top-level object */
1538 if (cschem
== topobject
) {
1539 for (llist
= pschem
->labels
; llist
!= NULL
; llist
= llist
->next
) {
1540 if (llist
->cschem
!= cschem
) continue;
1541 else if ((llist
->cinst
!= NULL
) && (llist
->cinst
!= cinst
)) continue;
1542 clabel
= llist
->label
;
1544 if (llist
->subnets
== 0)
1545 locnetid
= llist
->net
.id
;
1547 locnetid
= (llist
->net
.list
+ lbus
)->netid
;
1548 if (locnetid
== netid
) {
1549 if (clabel
->string
->type
== FONT_NAME
) { /* don't draw temp labels */
1550 if ((mode
== 0) && (clabel
->color
!= curcolor
)) {
1551 curcolor
= clabel
->color
;
1552 UDrawString(clabel
, curcolor
, cinst
);
1555 UDrawString(clabel
, DOFORALL
, cinst
);
1556 /* Fprintf(stdout, " >> Found label belonging to net "
1558 locnetid, clabel->position.x,
1559 clabel->position.y); */
1564 if (++lbus
>= llist
->subnets
) break;
1566 /* if we've encountered a unique instance, then continue */
1567 /* past all other instances using this label. */
1568 if (llist
->cinst
!= NULL
)
1569 while (llist
->next
&& (llist
->next
->label
== llist
->label
))
1570 llist
= llist
->next
;
1573 /* Highlight all pins connecting this net to symbols */
1577 /* Connectivity recursion */
1579 for (calls
= pschem
->calls
; calls
!= NULL
; calls
= calls
->next
) {
1580 if (calls
->cschem
!= cschem
) continue;
1581 for (ports
= calls
->ports
; ports
!= NULL
; ports
= ports
->next
) {
1582 if (ports
->netid
== netid
) {
1583 ccinst
= calls
->callinst
;
1585 /* Recurse only on objects for which network polygons are visible */
1586 /* from the calling object: i.e., non-trivial, non-fundamental */
1587 /* objects acting as their own schematics. */
1590 UPreMultCTM(DCTM
, ccinst
->position
, ccinst
->scale
, ccinst
->rotation
);
1592 if (ccinst
->thisobject
->symschem
== NULL
&&
1593 ccinst
->thisobject
->schemtype
!= FUNDAMENTAL
&&
1594 ccinst
->thisobject
->schemtype
!= TRIVIAL
) {
1596 netto
= translatedown(netid
, ports
->portid
, calls
->callobj
);
1598 /* Fprintf(stdout, " > Calling object %s at (%d, %d)\n",
1599 calls->callobj->name, ccinst->position.x, ccinst->position.y); */
1600 /* Fprintf(stdout, " > Net translation from %d to %d (port %d)\n",
1601 netid, netto, ports->portid); */
1603 if (highlightnet(calls
->callobj
, calls
->callinst
, netto
, mode
))
1607 /* Otherwise (symbols, fundamental, trivial, etc., objects), we */
1608 /* highlight the pin position of the port. */
1609 if ((clabel
= PortToLabel(ccinst
, ports
->portid
)))
1619 /*----------------------------------------------------------------------*/
1620 /* Highlight whatever nets are listed in the current object instance, */
1622 /*----------------------------------------------------------------------*/
1624 void highlightnetlist(objectptr nettop
, objinstptr cinst
, u_char mode
)
1628 Genericlist
*netlist
= cinst
->thisobject
->highlight
.netlist
;
1629 objinstptr nextinst
= cinst
->thisobject
->highlight
.thisinst
;
1631 if (netlist
== NULL
) return;
1634 if (netlist
->subnets
== 0)
1635 netid
= netlist
->net
.id
;
1637 sbus
= netlist
->net
.list
+ lbus
;
1638 netid
= sbus
->netid
;
1640 highlightnet(nettop
, nextinst
, netid
, mode
);
1641 if (++lbus
>= netlist
->subnets
) break;
1644 /* If we are erasing, remove the netlist entry from the object */
1645 if (mode
== (u_char
)0) {
1646 freegenlist(netlist
);
1647 cinst
->thisobject
->highlight
.netlist
= NULL
;
1648 cinst
->thisobject
->highlight
.thisinst
= NULL
;
1652 /*----------------------------------------------------------------------*/
1653 /* Push the matrix stack for each object (instance) until the indicated */
1654 /* object is reached. This works similarly to highlightnet() above, */
1655 /* but makes calls according to the hierarchy described by the */
1656 /* pushlistptr parameter. */
1657 /* Returns the number of stack objects to pop after we're done. */
1658 /*----------------------------------------------------------------------*/
1660 int pushnetwork(pushlistptr seltop
, objectptr nettop
)
1662 pushlistptr cursel
= seltop
;
1666 while ((cursel
->thisinst
->thisobject
!= nettop
) && (cursel
->next
!= NULL
)) {
1667 cursel
= cursel
->next
;
1668 sinst
= cursel
->thisinst
;
1670 UPreMultCTM(DCTM
, sinst
->position
, sinst
->scale
, sinst
->rotation
);
1674 if (cursel
->thisinst
->thisobject
!= nettop
) {
1675 Fprintf(stderr
, "Error: object does not exist in calling stack!\n");
1682 /*----------------------------------------------------------------------*/
1683 /* Determine if two netlists match. If "mode" is MATCH_EXACT, the */
1684 /* net numbers and subnet numbers must all be the same. If */
1685 /* MATCH_SUBNETS, then the subnet numbers must be the same. If */
1686 /* MATCH_SIZE, then they need only have the same number of subnets. */
1687 /*----------------------------------------------------------------------*/
1689 Boolean
match_buses(Genericlist
*list1
, Genericlist
*list2
, int mode
)
1692 buslist
*bus1
, *bus2
;
1694 if (list1
->subnets
!= list2
->subnets
) {
1695 // A wire (no subnets) matches a bus of 1 subnet. All others
1696 // are non-matching.
1698 if (list1
->subnets
!= 0 && list2
->subnets
!= 0)
1700 else if (list1
->subnets
!= 1 && list2
->subnets
!= 1)
1704 if (mode
== MATCH_SIZE
) return TRUE
;
1706 if (list1
->subnets
== 0) {
1707 if (mode
== MATCH_SUBNETS
) return TRUE
;
1708 if (list2
->subnets
!= 0) {
1709 bus2
= list2
->net
.list
+ 0;
1710 if (list1
->net
.id
!= bus2
->netid
) return FALSE
;
1712 else if (list1
->net
.id
!= list2
->net
.id
) return FALSE
;
1714 else if (list2
->subnets
== 0) {
1715 if (mode
== MATCH_SUBNETS
) return TRUE
;
1716 bus1
= list1
->net
.list
+ 0;
1717 if (bus1
->netid
!= list2
->net
.id
) return FALSE
;
1720 for (i
= 0; i
< list1
->subnets
; i
++) {
1721 bus1
= list1
->net
.list
+ i
;
1722 bus2
= list2
->net
.list
+ i
;
1723 /* A subnetid of < 0 indicates an unassigned bus */
1724 if ((bus1
->subnetid
!= -1) && (bus1
->subnetid
!= bus2
->subnetid
))
1727 if (mode
== MATCH_SUBNETS
) return TRUE
;
1729 for (i
= 0; i
< list1
->subnets
; i
++) {
1730 bus1
= list1
->net
.list
+ i
;
1731 bus2
= list2
->net
.list
+ i
;
1732 if (bus1
->netid
!= bus2
->netid
)
1739 /*----------------------------------------------------------------------*/
1740 /* Copy the netlist structure from one netlist element to another */
1741 /*----------------------------------------------------------------------*/
1743 void copy_bus(Genericlist
*dest
, Genericlist
*source
)
1745 buslist
*sbus
, *dbus
;
1748 if (dest
->subnets
> 0)
1749 free(dest
->net
.list
);
1751 dest
->subnets
= source
->subnets
;
1752 if (source
->subnets
== 0)
1753 dest
->net
.id
= source
->net
.id
;
1755 dest
->net
.list
= (buslist
*)malloc(dest
->subnets
* sizeof(buslist
));
1756 for (i
= 0; i
< dest
->subnets
; i
++) {
1757 sbus
= source
->net
.list
+ i
;
1758 dbus
= dest
->net
.list
+ i
;
1759 dbus
->netid
= sbus
->netid
;
1760 dbus
->subnetid
= sbus
->subnetid
;
1765 /*------------------------------------------------------*/
1766 /* Create a new "temporary" label object for a pin */
1767 /* This type of label is never drawn, so it doesn't */
1768 /* need font info. It is identified as "temporary" by */
1769 /* this lack of a leading font record. */
1770 /*------------------------------------------------------*/
1772 Genericlist
*new_tmp_pin(objectptr cschem
, XPoint
*pinpt
, char *pinstring
,
1773 char *prefix
, Genericlist
*netlist
)
1778 if (pinpt
== NULL
) {
1779 Fprintf(stderr
, "NULL label location!\n");
1783 NEW_LABEL(newlabel
, cschem
);
1784 labeldefaults(*newlabel
, LOCAL
, pinpt
->x
, pinpt
->y
);
1785 (*newlabel
)->anchor
= 0;
1786 (*newlabel
)->color
= DEFAULTCOLOR
;
1787 strptr
= (*newlabel
)->string
;
1788 strptr
->type
= TEXT_STRING
;
1789 if (pinstring
!= NULL
) {
1790 strptr
->data
.string
= (char *)malloc(strlen(pinstring
));
1791 strcpy(strptr
->data
.string
, pinstring
);
1794 strptr
->data
.string
= textprintnet(prefix
, NULL
, netlist
);
1797 /* Add label to object's netlist and return a pointer to the */
1798 /* netlist entry. */
1800 return (addpin(cschem
, NULL
, *newlabel
, netlist
));
1803 /*------------------------------------------------------*/
1804 /* Create a label for use in the list of global nets. */
1805 /* This label is like a temporary label (new_tmp_pin) */
1806 /* except that it is not represented in any object. */
1807 /* The string contains the verbatim contents of any */
1808 /* parameter substitutions in the original label. */
1809 /*------------------------------------------------------*/
1811 labelptr
new_global_pin(labelptr clabel
, objinstptr cinst
)
1815 newlabel
= (labelptr
) malloc(sizeof(label
));
1816 newlabel
->type
= LABEL
;
1817 labeldefaults(newlabel
, GLOBAL
, 0, 0);
1818 newlabel
->anchor
= 0;
1819 newlabel
->color
= DEFAULTCOLOR
;
1820 free(newlabel
->string
);
1821 newlabel
->string
= stringcopyall(clabel
->string
, cinst
);
1823 /* Add label to the global netlist and return a pointer to */
1824 /* the netlist entry. */
1829 /*----------------------------------------------------------------------*/
1830 /* Create a temporary I/O pin (becomes part of netlist and also part of */
1831 /* the object itself). */
1832 /*----------------------------------------------------------------------*/
1834 Genericlist
*make_tmp_pin(objectptr cschem
, objinstptr cinst
, XPoint
*pinpt
,
1835 Genericlist
*sublist
)
1839 char *pinstring
= NULL
;
1840 /* buslist *sbus; (jdk) */
1841 /* int lbus; (jdk) */
1842 Genericlist
*netlist
, *tmplist
, newlist
;
1844 newlist
.subnets
= 0;
1847 /* Primary schematic (contains the netlist) */
1848 pschem
= (cschem
->schemtype
== SECONDARY
) ? cschem
->symschem
: cschem
;
1850 /* Determine a netlist for this pin */
1852 netlist
= pointtonet(cschem
, cinst
, pinpt
);
1853 if (netlist
== NULL
) {
1854 newlist
.net
.id
= netmax(pschem
) + 1;
1858 /* If there is any other pin at this location, don't make another */
1859 /* one. If there is already a temporary pin associated with the */
1860 /* net, use its name. */
1863 for (lseek
= pschem
->labels
; lseek
!= NULL
; lseek
= lseek
->next
) {
1864 if (lseek
->cschem
!= cschem
) continue;
1865 else if ((lseek
->cinst
!= NULL
) && (lseek
->cinst
!= cinst
)) continue;
1866 tmplist
= (Genericlist
*)lseek
;
1867 if (match_buses(netlist
, tmplist
, MATCH_EXACT
)) {
1868 if (proximity(&(lseek
->label
->position
), pinpt
))
1869 return (Genericlist
*)lseek
;
1870 else if (lseek
->label
->string
->type
== TEXT_STRING
)
1871 pinstring
= lseek
->label
->string
->data
.string
;
1873 /* if we've encountered a unique instance, then continue past */
1874 /* all other instances using this label. */
1875 if (lseek
->cinst
!= NULL
)
1876 while (lseek
->next
&& (lseek
->next
->label
== lseek
->label
))
1877 lseek
= lseek
->next
;
1880 return (new_tmp_pin(cschem
, pinpt
, pinstring
, "ext", netlist
));
1883 /*--------------------------------------------------------------*/
1884 /* Search for connections into a non-symbol subcircuit, based */
1885 /* on various combinations of polygon and pin label overlaps. */
1886 /*--------------------------------------------------------------*/
1888 int searchconnect(XPoint
*points
, int number
, objinstptr cinst
, int subnets
)
1890 XPoint
*tmppts
, *tpt
, *tpt2
, *endpt
, *endpt2
, *pinpt
, opinpt
;
1895 objectptr tobj
, cobj
= cinst
->thisobject
;
1898 int i
; /* , lbus, sub_bus; (jdk) */
1901 /* Generate temporary polygon in the coordinate system of */
1902 /* the object instance in question */
1904 tmppts
= (XPoint
*)malloc(number
* sizeof(XPoint
));
1905 InvTransformPoints(points
, tmppts
, number
,
1906 cinst
->position
, cinst
->scale
, cinst
->rotation
);
1907 /* Fprintf(stdout, "Info: translated polygon w.r.t. object %s\n", */
1908 /* cinst->thisobject->name); */
1910 /* Recursion on all appropriate sub-schematics. */
1911 /* (Use parts list, not call list, as call list may not have created yet) */
1913 for (i
= 0; i
< cobj
->parts
; i
++) {
1914 cgen
= cobj
->plist
+ i
;
1915 if (IS_OBJINST(*cgen
)) {
1916 tinst
= TOOBJINST(cgen
);
1917 if (tinst
->thisobject
->symschem
== NULL
) {
1918 tobj
= tinst
->thisobject
;
1919 if (tobj
->schemtype
!= FUNDAMENTAL
&& tobj
->schemtype
!= TRIVIAL
)
1920 found
+= searchconnect(tmppts
, number
, tinst
, subnets
);
1925 for (endpt
= tmppts
; endpt
< tmppts
+ EndPoint(number
); endpt
++) {
1926 endpt2
= endpt
+ NextPoint(number
);
1927 for (i
= 0; i
< cobj
->parts
; i
++) {
1928 cgen
= cobj
->plist
+ i
;
1929 if (!IS_OBJINST(*cgen
)) continue;
1930 tinst
= TOOBJINST(cgen
);
1932 /* Look at the object only (symbol, or schematic if it has no symbol) */
1933 tobj
= tinst
->thisobject
;
1935 /* Search for connections to pin labels */
1937 for (tseek
= tobj
->labels
; tseek
!= NULL
; tseek
= tseek
->next
) {
1938 tlab
= tseek
->label
;
1939 UTransformPoints(&(tlab
->position
), &opinpt
, 1, tinst
->position
,
1940 tinst
->scale
, tinst
->rotation
);
1941 if (onsegment(endpt2
, endpt
, &opinpt
)) {
1942 /* Fprintf(stdout, "%s connects to pin %s of %s in %s\n", */
1943 /* ((number > 1) ? "Polygon" : "Pin"), */
1944 /* tlab->string + 2, tinst->thisobject->name, */
1945 /* cinst->thisobject->name); */
1946 make_tmp_pin(cobj
, cinst
, &opinpt
, (Genericlist
*)tseek
);
1947 found
+= (tseek
->subnets
== 0) ? 1 : tseek
->subnets
;
1953 for (pseek
= cobj
->polygons
; pseek
!= NULL
; pseek
= pseek
->next
) {
1954 tpoly
= pseek
->poly
;
1956 /* Search for connections from segments passed to this */
1957 /* function to endpoints of polygons in the netlist. */
1960 tpt
= tpoly
->points
;
1961 tpt2
= tpoly
->points
+ tpoly
->number
- 1;
1962 if (onsegment(endpt2
, endpt
, tpt
)) pinpt
= tpt
;
1963 if (onsegment(endpt2
, endpt
, tpt2
)) pinpt
= tpt2
;
1965 /* Create new pinlabel (only if there is not one already there) */
1967 if (pinpt
!= NULL
) {
1968 make_tmp_pin(cobj
, cinst
, pinpt
, (Genericlist
*)pseek
);
1969 found
+= (pseek
->subnets
== 0) ? 1 : pseek
->subnets
;
1975 endpt2
= tmppts
+ EndPoint(number
) - 1;
1977 /* Search for connections from endpoints passed to this */
1978 /* function to segments of polygons in the netlist. */
1980 for (pseek
= cobj
->polygons
; pseek
!= NULL
; pseek
= pseek
->next
) {
1982 tpoly
= pseek
->poly
;
1983 for (tpt
= tpoly
->points
; tpt
< tpoly
->points
1984 + EndPoint(tpoly
->number
); tpt
++) {
1985 tpt2
= tpt
+ NextPoint(tpoly
->number
);
1988 if (onsegment(tpt2
, tpt
, endpt
)) pinpt
= endpt
;
1989 if (onsegment(tpt2
, tpt
, endpt2
)) pinpt
= endpt2
;
1991 /* Create new pinlabel (only if there is not one already there) */
1993 if (pinpt
!= NULL
) {
1994 make_tmp_pin(cobj
, cinst
, pinpt
, (Genericlist
*)pseek
);
1995 found
+= (pseek
->subnets
== 0) ? 1 : pseek
->subnets
;
2003 /*----------------------------------------------------------------------*/
2004 /* Associate polygon with given netlist and add to the object's list */
2005 /* of network polygons (i.e., wires). */
2006 /*----------------------------------------------------------------------*/
2008 Genericlist
*addpoly(objectptr cschem
, polyptr poly
, Genericlist
*netlist
)
2010 PolylistPtr newpoly
;
2012 /* buslist *sbus; (jdk) */
2013 /* int lbus, sub_bus; (jdk) */
2015 /* Netlist is in the master schematic */
2016 pschem
= (cschem
->schemtype
== SECONDARY
) ? cschem
->symschem
: cschem
;
2018 /* If this polygon is already in the list, then add an extra subnet */
2021 for (newpoly
= pschem
->polygons
; newpoly
!= NULL
; newpoly
= newpoly
->next
) {
2022 if (newpoly
->poly
== poly
) {
2023 if (!match_buses((Genericlist
*)newpoly
, netlist
, MATCH_EXACT
)) {
2024 Fprintf(stderr
, "addpoly: Error in bus assignment\n");
2027 return (Genericlist
*)newpoly
;
2031 /* Create a new entry and link to polygon list of this object */
2033 newpoly
= (PolylistPtr
) malloc(sizeof(Polylist
));
2034 newpoly
->cschem
= cschem
;
2035 newpoly
->poly
= poly
;
2036 newpoly
->subnets
= 0;
2037 copy_bus((Genericlist
*)newpoly
, netlist
);
2038 newpoly
->next
= pschem
->polygons
;
2039 pschem
->polygons
= newpoly
;
2041 return (Genericlist
*)newpoly
;
2044 /*-------------------------------------------------------------------------*/
2046 long zsign(long a
, long b
)
2048 if (a
> b
) return 1;
2049 else if (a
< b
) return -1;
2053 /*----------------------------------------------------------------------*/
2054 /* Promote a single net to a bus. The bus size will be equal to the */
2055 /* value "subnets". */
2056 /*----------------------------------------------------------------------*/
2058 void promote_net(objectptr cschem
, Genericlist
*netfrom
, int subnets
)
2060 Genericlist
*netref
= NULL
;
2065 int netid
, firstid
, lbus
; /* curid, (jdk) */
2069 /* If no promotion is required, don't do anything */
2070 if (netfrom
->subnets
== subnets
) return;
2072 /* It "netfrom" is already a bus, but of different size than */
2073 /* subnets, then it cannot be changed. */
2075 if (netfrom
->subnets
!= 0) {
2076 Fprintf(stderr
, "Attempt to change the size of a bus!\n");
2080 netid
= netfrom
->net
.id
;
2082 /* If "subnets" is 1, then "netfrom" can be promoted regardless. */
2083 /* Otherwise, if the "netfrom" net is used in any calls, then it */
2084 /* cannot be promoted. */
2087 for (calls
= cschem
->calls
; calls
!= NULL
; calls
= calls
->next
)
2088 for (ports
= calls
->ports
; ports
!= NULL
; ports
= ports
->next
)
2089 if (ports
->netid
== netid
) {
2090 Fprintf(stderr
, "Cannot promote net to bus: Net already connected"
2091 " to single-wire port\n");
2094 firstid
= netmax(cschem
) + 1;
2097 for (plist
= cschem
->polygons
; plist
!= NULL
; plist
= plist
->next
)
2098 if ((plist
->subnets
== 0) && (plist
->net
.id
== netid
)) {
2099 plist
->subnets
= subnets
;
2100 plist
->net
.list
= (buslist
*)malloc(subnets
* sizeof(buslist
));
2101 for (lbus
= 0; lbus
< subnets
; lbus
++) {
2102 sbus
= plist
->net
.list
+ lbus
;
2103 sbus
->netid
= (lbus
== 0) ? netid
: firstid
+ lbus
;
2104 sbus
->subnetid
= lbus
; /* By default, number from zero */
2106 netref
= (Genericlist
*)plist
;
2109 /* It's possible for a label without bus notation to be attached */
2113 for (llist
= cschem
->labels
; llist
!= NULL
; llist
= llist
->next
)
2114 if ((llist
->subnets
== 0) && (llist
->net
.id
== netid
)) {
2115 llist
->subnets
= subnets
;
2116 llist
->net
.list
= (buslist
*)malloc(subnets
* sizeof(buslist
));
2117 for (lbus
= 0; lbus
< subnets
; lbus
++) {
2118 sbus
= llist
->net
.list
+ lbus
;
2119 sbus
->netid
= (lbus
== 0) ? netid
: firstid
+ lbus
;
2120 sbus
->subnetid
= lbus
; /* By default, number from zero */
2122 netref
= (Genericlist
*)llist
;
2126 /* We need to create a temp label associated with this net to */
2127 /* encompass the promoted bus size. If this bus is later attached */
2128 /* to a known bus, that bus name will be canonical. If no bus name */
2129 /* is ever assigned to the bus, this temp one will be used. */
2133 pinpos
= NetToPosition(netid
, cschem
);
2134 new_tmp_pin(cschem
, pinpos
, NULL
, "int", netref
);
2138 /*----------------------------------------------------------------------*/
2139 /* Change any part of the netlist "testlist" that matches the net IDs */
2140 /* of "orignet" to the net IDs of "newnet". It is assumed that the */
2141 /* lengths and sub-bus numbers of "orignet" and "newnet" match. */
2142 /*----------------------------------------------------------------------*/
2144 Boolean
mergenetlist(objectptr cschem
, Genericlist
*testlist
,
2145 Genericlist
*orignet
, Genericlist
*newnet
)
2147 int obus
, onetid
, osub
, nsub
, nnetid
, tbus
;
2149 Boolean rval
= FALSE
;
2152 if (orignet
->subnets
== 0) {
2153 onetid
= orignet
->net
.id
;
2157 sbus
= orignet
->net
.list
+ obus
;
2158 onetid
= sbus
->netid
;
2159 osub
= sbus
->subnetid
;
2162 if (newnet
->subnets
== 0) {
2163 nnetid
= newnet
->net
.id
;
2167 sbus
= newnet
->net
.list
+ obus
;
2168 nnetid
= sbus
->netid
;
2169 nsub
= sbus
->subnetid
;
2172 if (testlist
->subnets
== 0) {
2173 if (testlist
->net
.id
== onetid
) {
2175 if (orignet
->subnets
== 0) {
2176 testlist
->net
.id
= nnetid
;
2180 /* Promote testlist to a bus subnet of size 1 */
2181 testlist
->subnets
= 1;
2182 testlist
->net
.list
= (buslist
*)malloc(sizeof(buslist
));
2183 sbus
= testlist
->net
.list
;
2184 sbus
->netid
= nnetid
;
2185 sbus
->subnetid
= nsub
;
2191 for (tbus
= 0; tbus
< testlist
->subnets
; tbus
++) {
2192 /* If the sub-bus numbers match, then change the net */
2193 /* ID to the new net number. If the sub-bus is */
2194 /* unnamed (has no associated bus-notation label), */
2195 /* then it can take on the net and subnet numbers. */
2197 sbus
= testlist
->net
.list
+ tbus
;
2199 if (sbus
->netid
== onetid
) {
2200 if (sbus
->subnetid
== osub
) {
2201 sbus
->netid
= nnetid
;
2202 sbus
->subnetid
= nsub
;
2206 labelptr blab
= NetToLabel(nnetid
, cschem
);
2208 Fprintf(stderr
, "Warning: isolated subnet?\n");
2209 sbus
->netid
= nnetid
;
2210 /* Keep subnetid---but, does newnet need to be promoted? */
2213 else if (blab
->string
->type
!= FONT_NAME
) {
2214 sbus
->netid
= nnetid
;
2215 sbus
->subnetid
= nsub
;
2217 Fprintf(stderr
, "Warning: Unexpected subnet value in mergenetlist!\n");
2222 if (++obus
>= orignet
->subnets
) break;
2227 /*----------------------------------------------------------------------*/
2228 /* Combine two networks in an object's linked-list Netlist */
2229 /* Parameters: cschem - pointer to object containing netlist */
2230 /* orignet - original netlist to be changed */
2231 /* newnet - new netlist to be changed to */
2233 /* Attempts to merge different subnets in a bus are thwarted. */
2234 /*----------------------------------------------------------------------*/
2236 Boolean
netmerge(objectptr cschem
, Genericlist
*orignet
, Genericlist
*newnet
)
2242 Genericlist savenet
;
2244 buslist
*obus
, *nbus
;
2247 /* Trivial case; do nothing */
2248 if (match_buses(orignet
, newnet
, MATCH_EXACT
)) return TRUE
;
2250 /* Disallow an attempt to convert a global net to a local net: */
2251 /* The global net ID always dominates! */
2253 if ((orignet
->subnets
== 0) && (newnet
->subnets
== 0) &&
2254 (orignet
->net
.id
< 0) && (newnet
->net
.id
> 0)) {
2255 int globnet
= orignet
->net
.id
;
2256 orignet
->net
.id
= newnet
->net
.id
;
2257 newnet
->net
.id
= globnet
;
2260 /* Check that the lists of changes are compatible. It appears to be */
2261 /* okay to have non-matching buses (although they should not be */
2262 /* merged), as this is a valid style in which buses do not have taps */
2263 /* but the sub-buses are explicitly called out with labels. In such */
2264 /* case, the polygons of different sub-buses touch, and this routine */
2265 /* rejects them as being incompatible. */
2267 if (!match_buses(orignet
, newnet
, MATCH_SUBNETS
)) {
2268 if (match_buses(orignet
, newnet
, MATCH_SIZE
)) {
2270 /* If only the subnet numbers don't match up, check if */
2271 /* "orignet" has a temp label. */
2272 nbus
= orignet
->net
.list
;
2273 if ((nlab
= NetToLabel(nbus
->netid
, cschem
)) != NULL
) {
2274 if (nlab
->string
->type
!= FONT_NAME
)
2279 Fprintf(stderr
, "netmerge warning: non-matching bus subnets touching.\n");
2285 /* If orignet is a bus size 1 and newnet is a wire, then promote */
2286 /* newnet to a bus size 1. */
2288 if (orignet
->subnets
== 1 && newnet
->subnets
== 0) {
2289 net
= newnet
->net
.id
;
2290 newnet
->subnets
= 1;
2291 newnet
->net
.list
= (buslist
*)malloc(sizeof(buslist
));
2292 obus
= orignet
->net
.list
;
2293 nbus
= newnet
->net
.list
;
2294 nbus
->subnetid
= obus
->subnetid
;
2298 /* Make a copy of the net so we don't overwrite it */
2299 savenet
.subnets
= 0;
2300 copy_bus(&savenet
, orignet
);
2303 for (plist
= cschem
->polygons
; plist
!= NULL
; plist
= plist
->next
)
2304 if (mergenetlist(cschem
, (Genericlist
*)plist
, &savenet
, newnet
))
2307 for (llist
= cschem
->labels
; llist
!= NULL
; llist
= llist
->next
)
2308 if (mergenetlist(cschem
, (Genericlist
*)llist
, &savenet
, newnet
)) {
2313 /* Because nets that have been merged away may be re-used later, */
2314 /* change the name of any temporary labels to the new net number */
2316 if (llist
->label
->string
->type
!= FONT_NAME
) {
2317 newtext
= llist
->label
->string
->data
.string
;
2318 if (sscanf(newtext
+ 3, "%d", &pinnet
) == 1) {
2319 if (pinnet
== savenet
.net
.id
) {
2320 *(newtext
+ 3) = '\0';
2321 llist
->label
->string
->data
.string
= textprintnet(newtext
,
2331 /* Reflect the net change in the object's call list, if it has one. */
2333 for (calls
= cschem
->calls
; calls
!= NULL
; calls
= calls
->next
) {
2334 for (ports
= calls
->ports
; ports
!= NULL
; ports
= ports
->next
) {
2335 if (newnet
->subnets
== 0) {
2336 if (ports
->netid
== savenet
.net
.id
)
2337 ports
->netid
= newnet
->net
.id
;
2340 for (i
= 0; i
< newnet
->subnets
; i
++) {
2341 obus
= savenet
.net
.list
+ i
;
2342 nbus
= newnet
->net
.list
+ i
;
2343 if (ports
->netid
== obus
->netid
)
2344 ports
->netid
= nbus
->netid
;
2351 /* Free the copy of the bus that we made, if we made one */
2352 if (savenet
.subnets
> 0) free(savenet
.net
.list
);
2357 /*----------------------------------------------------------------------*/
2358 /* Wrapper to netmerge() to make sure change is made both to the symbol */
2359 /* and schematic, if both exist. */
2360 /*----------------------------------------------------------------------*/
2362 Boolean
mergenets(objectptr cschem
, Genericlist
*orignet
, Genericlist
*newnet
)
2366 if (cschem
->symschem
!= NULL
)
2367 merged
= netmerge(cschem
->symschem
, orignet
, newnet
);
2368 if (netmerge(cschem
, orignet
, newnet
))
2374 /*----------------------------------------------------------------------*/
2375 /* Remove a call to an object instance from the call list of cschem */
2376 /*----------------------------------------------------------------------*/
2378 void removecall(objectptr cschem
, CalllistPtr dontcallme
)
2380 CalllistPtr lastcall
, seeklist
;
2381 PortlistPtr ports
, savelist
;
2383 /* find the instance call before this one and link it to the one following */
2386 for (seeklist
= cschem
->calls
; seeklist
!= NULL
; seeklist
= seeklist
->next
) {
2387 if (seeklist
== dontcallme
)
2389 lastcall
= seeklist
;
2392 if (seeklist
== NULL
) {
2393 Fprintf(stderr
, "Error in removecall(): Call does not exist!\n");
2397 if (lastcall
== NULL
)
2398 cschem
->calls
= dontcallme
->next
;
2400 lastcall
->next
= dontcallme
->next
;
2402 ports
= dontcallme
->ports
;
2403 while (ports
!= NULL
) {
2405 ports
= ports
->next
;
2411 /*----------------------------------------------------------------------*/
2412 /* Add a pin label to the netlist */
2413 /* If cschem == NULL, add the pin to the list of global pins. */
2414 /* The new label entry in the netlist gets a copy of the netlist */
2416 /*----------------------------------------------------------------------*/
2418 Genericlist
*addpin(objectptr cschem
, objinstptr cinst
, labelptr pin
,
2419 Genericlist
*netlist
)
2421 LabellistPtr srchlab
, newlabel
, lastlabel
= NULL
;
2423 /* buslist *sbus; (jdk) */
2424 /* int lbus, sub_bus; (jdk) */
2426 pschem
= (cschem
->schemtype
== SECONDARY
) ? cschem
->symschem
: cschem
;
2428 for (srchlab
= pschem
->labels
; srchlab
!= NULL
; srchlab
= srchlab
->next
) {
2429 if (srchlab
->label
== pin
) {
2430 if (!match_buses(netlist
, (Genericlist
*)srchlab
, MATCH_EXACT
)) {
2431 if (srchlab
->cinst
== cinst
) {
2432 Fprintf(stderr
, "addpin: Error in bus assignment\n");
2436 else if (srchlab
->cinst
== NULL
)
2437 return (Genericlist
*)srchlab
;
2438 break; /* Stop at the first record for this label */
2440 lastlabel
= srchlab
;
2443 /* Create a new entry and link to label list of the object */
2445 newlabel
= (LabellistPtr
) malloc(sizeof(Labellist
));
2446 newlabel
->cschem
= cschem
;
2447 newlabel
->cinst
= cinst
;
2448 newlabel
->label
= pin
;
2449 newlabel
->subnets
= 0;
2450 copy_bus((Genericlist
*)newlabel
, netlist
);
2452 /* Always put the specific (instanced) cases in front of */
2453 /* generic cases for the same label. */
2455 /* Generic case---make it the last record for this label */
2456 if ((cinst
== NULL
) && (lastlabel
!= NULL
)) {
2457 while ((srchlab
!= NULL
) && (srchlab
->label
== pin
)) {
2458 lastlabel
= srchlab
;
2459 srchlab
= srchlab
->next
;
2462 if (lastlabel
!= NULL
) {
2463 newlabel
->next
= srchlab
;
2464 lastlabel
->next
= newlabel
;
2467 newlabel
->next
= pschem
->labels
;
2468 pschem
->labels
= newlabel
;
2470 return (Genericlist
*)newlabel
;
2473 /*----------------------------------------------------------------------*/
2474 /* Add a pin label to the list of global net names. */
2475 /* The new label entry in the netlist gets a copy of the netlist */
2476 /* "netlist" and a copy of label "pin" containing the *instance* value */
2478 /*----------------------------------------------------------------------*/
2480 Genericlist
*addglobalpin(objectptr cschem
, objinstptr cinst
, labelptr pin
,
2481 Genericlist
*netlist
)
2483 LabellistPtr srchlab
, newlabel
, lastlabel
= NULL
;
2485 if (cinst
== NULL
) {
2486 Fprintf(stderr
, "Error: Global pin does not have an associated instance!\n");
2490 for (srchlab
= global_labels
; srchlab
!= NULL
; srchlab
= srchlab
->next
) {
2491 if (srchlab
->label
== pin
) {
2492 if (!match_buses(netlist
, (Genericlist
*)srchlab
, MATCH_EXACT
)) {
2493 if (srchlab
->cinst
== cinst
) {
2494 Fprintf(stderr
, "addglobalpin: Error in bus assignment\n");
2498 else if (srchlab
->cinst
== NULL
)
2499 return (Genericlist
*)srchlab
;
2500 break; /* Stop at the first record for this label */
2502 lastlabel
= srchlab
;
2505 /* Create a new entry and link to label list of the object */
2507 newlabel
= (LabellistPtr
) malloc(sizeof(Labellist
));
2508 newlabel
->cschem
= cschem
;
2509 newlabel
->cinst
= cinst
;
2510 newlabel
->label
= new_global_pin(pin
, cinst
);
2511 newlabel
->subnets
= 0;
2512 copy_bus((Genericlist
*)newlabel
, netlist
);
2514 if (lastlabel
!= NULL
) {
2515 newlabel
->next
= srchlab
;
2516 lastlabel
->next
= newlabel
;
2519 newlabel
->next
= global_labels
;
2520 global_labels
= newlabel
;
2522 return (Genericlist
*)newlabel
;
2525 /*----------------------------------------------------------------------*/
2526 /* Allocate memory for the new call list element */
2527 /* Define the values for the new call list element */
2528 /* Insert new call into call list */
2529 /* The new call is at the beginning of the list. */
2530 /*----------------------------------------------------------------------*/
2532 void addcall(objectptr cschem
, objectptr callobj
, objinstptr callinst
)
2534 CalllistPtr newcall
;
2537 /* Netlist is on the master schematic */
2538 pschem
= (cschem
->schemtype
== SECONDARY
) ? cschem
->symschem
: cschem
;
2540 newcall
= (CalllistPtr
) malloc(sizeof(Calllist
));
2541 newcall
->cschem
= cschem
;
2542 newcall
->callobj
= callobj
;
2543 newcall
->callinst
= callinst
;
2544 newcall
->devindex
= -1;
2545 newcall
->devname
= NULL
;
2546 newcall
->ports
= NULL
;
2547 newcall
->next
= pschem
->calls
;
2548 pschem
->calls
= newcall
;
2551 /*----------------------------------------------------------------------*/
2552 /* Add a port to the object cschem which connects net "netto" to */
2553 /* the calling object. One port is created for each net ID in "netto" */
2554 /* (which may be a bus). The port contains the net ID in the called */
2555 /* object. The port may already exist, in which case this routine does */
2557 /*----------------------------------------------------------------------*/
2559 void addport(objectptr cschem
, Genericlist
*netto
)
2561 PortlistPtr newport
, seekport
;
2562 int portid
= 0, netid
, lbus
;
2568 if (netto
->subnets
== 0)
2569 netid
= netto
->net
.id
;
2571 sbus
= netto
->net
.list
+ lbus
;
2572 netid
= sbus
->netid
;
2575 /* If a port already exists for this net, don't add another one! */
2578 for (seekport
= cschem
->ports
; seekport
!= NULL
; seekport
= seekport
->next
) {
2579 if (seekport
->netid
!= netid
) {
2580 if (seekport
->portid
> portid
)
2581 portid
= seekport
->portid
;
2590 newport
= (PortlistPtr
)malloc(sizeof(Portlist
));
2591 newport
->netid
= netid
;
2592 newport
->portid
= portid
;
2594 if (cschem
->ports
!= NULL
)
2595 newport
->next
= cschem
->ports
;
2597 newport
->next
= NULL
;
2599 cschem
->ports
= newport
;
2601 if (++lbus
>= netto
->subnets
) break;
2605 /*----------------------------------------------------------------------*/
2606 /* Add a specific port connection from object cschem into the instance */
2607 /* cinst. This equates a net number in the calling object cschem */
2608 /* (netfrom) to a net number in the object instance being called */
2611 /* If we attempt to connect a bus of one size to a port of a different */
2612 /* size, return FALSE. Otherwise, add the call and return TRUE. */
2613 /*----------------------------------------------------------------------*/
2615 Boolean
addportcall(objectptr cschem
, Genericlist
*netfrom
, Genericlist
*netto
)
2618 PortlistPtr seekport
, sp
, newport
;
2621 int lbus
, netid_from
, netid_to
;
2622 buslist
*sbus
, *tbus
;
2625 /* The call that we need to add a port to is the first on */
2626 /* the list for cschem, as created by addcall(). */
2627 ccall
= cschem
->calls
;
2628 instobj
= ccall
->callobj
;
2629 cinst
= ccall
->callinst
;
2631 if (netfrom
->subnets
!= netto
->subnets
) {
2632 if (netfrom
->subnets
== 0) {
2633 /* It is possible that "netfrom" is an unlabeled polygon that */
2634 /* is implicitly declared a bus by its connection to this */
2635 /* port. If so, we promote "netfrom" to a bus but set the */
2636 /* subnet entries to negative values, since we don't know what */
2638 promote_net(cschem
, netfrom
, netto
->subnets
);
2641 /* Only other allowable condition is a bus size 1 connecting into */
2642 /* a single-wire port. However, one should consider promotion of */
2643 /* the pin in the target object to a bus on a per-instance basis. */
2644 /* This has not yet been done. . . */
2646 else if ((netfrom
->subnets
!= 1) || (netto
->subnets
!= 0)) {
2647 /* Let the caller report error information, because it knows more */
2653 buslist bsingf
, bsingo
;
2654 Genericlist subnet_from
, subnet_other
;
2656 if (netfrom
->subnets
== 0) {
2657 netid_from
= netfrom
->net
.id
;
2659 subnet_from
.subnets
= 0;
2660 subnet_from
.net
.id
= netid_from
;
2662 subnet_other
.subnets
= 0;
2665 sbus
= netfrom
->net
.list
+ lbus
;
2666 netid_from
= sbus
->netid
;
2668 subnet_from
.subnets
= 1;
2669 subnet_from
.net
.list
= &bsingf
;
2670 bsingf
.netid
= netid_from
;
2671 bsingf
.subnetid
= sbus
->subnetid
;
2673 bsingo
.subnetid
= lbus
;
2674 subnet_other
.subnets
= 1;
2675 subnet_other
.net
.list
= &bsingo
;
2677 if (netto
->subnets
== 0) {
2678 netid_to
= netto
->net
.id
;
2681 tbus
= netto
->net
.list
+ lbus
;
2682 netid_to
= tbus
->netid
;
2685 /* Check the ports of the instance's object for the one matching */
2686 /* the "netto" net ID. */
2689 for (seekport
= instobj
->ports
; seekport
!= NULL
;
2690 seekport
= seekport
->next
) {
2691 if (seekport
->netid
== netid_to
) {
2693 /* If there is already an entry for this port, then */
2694 /* we may need to merge nets in cschem. */
2696 for (sp
= ccall
->ports
; sp
!= NULL
; sp
= sp
->next
)
2697 if (sp
->portid
== seekport
->portid
) {
2698 if (sp
->netid
!= netid_from
) {
2699 if (netfrom
->subnets
== 0)
2700 subnet_other
.net
.id
= sp
->netid
;
2702 bsingo
.netid
= sp
->netid
;
2703 bsingo
.subnetid
= getsubnet(bsingo
.netid
, cschem
);
2705 if (!mergenets(cschem
, &subnet_other
, &subnet_from
)) {
2706 /* Upon failure, see if we can merge the other */
2708 if (!mergenets(cschem
, &subnet_from
, &subnet_other
))
2711 if (subnet_from
.subnets
== 0)
2712 subnet_from
.net
.id
= subnet_other
.net
.id
;
2714 bsingf
.netid
= bsingo
.netid
;
2715 bsingf
.subnetid
= bsingo
.subnetid
;
2724 newport
= (PortlistPtr
)malloc(sizeof(Portlist
));
2725 newport
->netid
= netid_from
;
2726 newport
->portid
= seekport
->portid
;
2727 newport
->next
= ccall
->ports
;
2728 ccall
->ports
= newport
;
2733 if (++lbus
>= netfrom
->subnets
) break;
2738 /*----------------------------------------------------------------------*/
2739 /* Find the net ID corresponding to the indicated port ID in the */
2740 /* indicated object. */
2741 /*----------------------------------------------------------------------*/
2743 int porttonet(objectptr cschem
, int portno
)
2747 for (plist
= cschem
->ports
; plist
!= NULL
; plist
= plist
->next
) {
2748 if (plist
->portid
== portno
)
2749 return plist
->netid
;
2754 /*----------------------------------------------------------------------*/
2755 /* Traverse netlist and return netid of polygon or pin on which the */
2756 /* indicated point is located. */
2757 /* If point is not on any polygon or does not match any pin position, */
2759 /* Labels which have a non-NULL "cinst" record are instance-dependent */
2760 /* and must match parameter "cinst". */
2762 /* This routine checks to see if more than one net crosses the point */
2763 /* of interest, and merges the nets if found. */
2764 /* (the method has been removed and must be reinstated, presumably) */
2765 /*----------------------------------------------------------------------*/
2767 Genericlist
*pointtonet(objectptr cschem
, objinstptr cinst
, XPoint
*testpoint
)
2772 Genericlist
*preturn
;
2773 objectptr pschem
; /* primary schematic */
2775 /* cschem is the object containing the point. However, if the object */
2776 /* is a secondary schematic, the netlist is located in the master. */
2778 pschem
= (cschem
->schemtype
== SECONDARY
) ? cschem
->symschem
: cschem
;
2780 for (plab
= pschem
->labels
; plab
!= NULL
; plab
= plab
->next
) {
2781 if (plab
->cschem
!= cschem
) continue;
2782 else if ((plab
->cinst
!= NULL
) && (plab
->cinst
!= cinst
)) continue;
2783 tpt
= &(plab
->label
->position
);
2784 if (proximity(tpt
, testpoint
))
2785 return (Genericlist
*)plab
;
2787 /* if we've encountered a unique instance, then continue past */
2788 /* all other instances using this label. */
2790 if (plab
->cinst
!= NULL
)
2791 while (plab
->next
&& (plab
->next
->label
== plab
->label
))
2795 /* Check against polygons. We use this part of the routine to see */
2796 /* if there are crossing wires on top of a port. If so, they are */
2797 /* merged together. */
2799 preturn
= (Genericlist
*)NULL
;
2800 for (ppoly
= pschem
->polygons
; ppoly
!= NULL
; ppoly
= ppoly
->next
) {
2801 if (ppoly
->cschem
!= cschem
) continue;
2802 for (tpt
= ppoly
->poly
->points
; tpt
< ppoly
->poly
->points
2803 + EndPoint(ppoly
->poly
->number
); tpt
++) {
2804 tpt2
= tpt
+ NextPoint(ppoly
->poly
->number
);
2806 if (onsegment(tpt
, tpt2
, testpoint
)) {
2807 if (preturn
== (Genericlist
*)NULL
)
2808 preturn
= (Genericlist
*)ppoly
;
2810 mergenets(pschem
, (Genericlist
*)ppoly
, preturn
);
2818 /*----------------------------------------------------------------------*/
2819 /* localpin keeps track of temporary pin labels when flattening the */
2820 /* hierarchy without destroying the original pin names. */
2821 /*----------------------------------------------------------------------*/
2823 void makelocalpins(objectptr cschem
, CalllistPtr clist
, char *prefix
)
2825 NetnamePtr netnames
;
2826 PortlistPtr ports
, plist
;
2828 int locnet
, callnet
;
2829 objectptr callobj
= clist
->callobj
;
2831 /* Copy all net names which are passed from above through ports */
2833 for (ports
= clist
->ports
; ports
!= NULL
; ports
= ports
->next
) {
2834 callnet
= ports
->netid
;
2835 for (plist
= callobj
->ports
; plist
!= NULL
; plist
= plist
->next
) {
2836 if (plist
->portid
== ports
->portid
) {
2837 locnet
= plist
->netid
;
2838 locpin
= nettopin(callnet
, cschem
, prefix
);
2843 for (netnames
= callobj
->netnames
; netnames
!= NULL
; netnames
= netnames
->next
)
2844 if (netnames
->netid
== locnet
)
2847 if (netnames
== NULL
) {
2848 netnames
= (NetnamePtr
)malloc(sizeof(Netname
));
2849 netnames
->netid
= locnet
;
2850 netnames
->localpin
= stringcopy(locpin
);
2851 netnames
->next
= callobj
->netnames
;
2852 callobj
->netnames
= netnames
;
2857 /*----------------------------------------------------------------------*/
2858 /* Find the first point associated with the net "netid" in the object */
2860 /*----------------------------------------------------------------------*/
2862 XPoint
*NetToPosition(int netid
, objectptr cschem
)
2867 int lbus
, locnetid
; /* sub_bus, (jdk) */
2869 plist
= cschem
->polygons
;
2870 for (; plist
!= NULL
; plist
= plist
->next
) {
2872 if (plist
->subnets
== 0) {
2873 locnetid
= plist
->net
.id
;
2876 sbus
= plist
->net
.list
+ lbus
;
2877 locnetid
= sbus
->netid
;
2879 if (locnetid
== netid
) {
2880 return plist
->poly
->points
;
2882 if (++lbus
>= plist
->subnets
) break;
2886 llist
= (netid
< 0) ? global_labels
: cschem
->labels
;
2887 for (; llist
!= NULL
; llist
= llist
->next
) {
2889 if (llist
->subnets
== 0) {
2890 locnetid
= llist
->net
.id
;
2893 sbus
= llist
->net
.list
+ lbus
;
2894 locnetid
= sbus
->netid
;
2896 if (locnetid
== netid
) {
2897 return (&(llist
->label
->position
));
2899 if (++lbus
>= llist
->subnets
) break;
2905 /*----------------------------------------------------------------------*/
2906 /* Find a label element for the given net number. In a symbol, this */
2907 /* will be the label representing the pin (or the first one found, if */
2908 /* the pin has multiple labels). If no label is found, return NULL. */
2909 /* Preferably choose a non-temporary label, if one exists */
2910 /*----------------------------------------------------------------------*/
2912 labelptr
NetToLabel(int netid
, objectptr cschem
)
2915 labelptr standby
= NULL
;
2919 llist
= (netid
< 0) ? global_labels
: cschem
->labels
;
2921 for (; llist
!= NULL
; llist
= llist
->next
) {
2923 if (llist
->subnets
== 0) {
2924 locnetid
= llist
->net
.id
;
2927 sbus
= llist
->net
.list
+ lbus
;
2928 locnetid
= sbus
->netid
;
2930 if (locnetid
== netid
) {
2931 if (llist
->label
->string
->type
== FONT_NAME
)
2932 return llist
->label
;
2933 else if (standby
== NULL
)
2934 standby
= llist
->label
;
2936 if (++lbus
>= llist
->subnets
) break;
2943 /*----------------------------------------------------------------------*/
2944 /* Find the subnet number of the given net. This routine should be */
2945 /* used only as a last resort in case the subnet number is not */
2946 /* available; it has to look through the polygon and label lists in */
2947 /* detail to find the specified net ID. */
2948 /*----------------------------------------------------------------------*/
2950 int getsubnet(int netid
, objectptr cschem
)
2955 int lbus
, sub_bus
, locnetid
;
2957 plist
= cschem
->polygons
;
2958 for (; plist
!= NULL
; plist
= plist
->next
) {
2960 if (plist
->subnets
== 0) {
2961 locnetid
= plist
->net
.id
;
2965 sbus
= plist
->net
.list
+ lbus
;
2966 locnetid
= sbus
->netid
;
2967 sub_bus
= sbus
->subnetid
;
2969 if (locnetid
== netid
) {
2972 if (++lbus
>= plist
->subnets
) break;
2976 llist
= (netid
< 0) ? global_labels
: cschem
->labels
;
2977 for (; llist
!= NULL
; llist
= llist
->next
) {
2979 if (llist
->subnets
== 0) {
2980 locnetid
= llist
->net
.id
;
2984 sbus
= llist
->net
.list
+ lbus
;
2985 locnetid
= sbus
->netid
;
2986 sub_bus
= sbus
->subnetid
;
2988 if (locnetid
== netid
) {
2991 if (++lbus
>= llist
->subnets
) break;
2997 /*----------------------------------------------------------------------*/
2998 /* Find a pin name for the given net number */
2999 /* Either take the last pin name with the net, or generate a new pin, */
3000 /* assigning it a net number for a string. */
3001 /* prefix = NULL indicates spice netlist, but is also used for PCB-type */
3002 /* netlists because the calling hierarchy is generated elsewhere. */
3003 /*----------------------------------------------------------------------*/
3005 stringpart
*nettopin(int netid
, objectptr cschem
, char *prefix
)
3009 LabellistPtr netlabel
;
3010 char *newtext
, *snew
= NULL
;
3012 int locnetid
; /* lbus, (jdk) */
3013 /* buslist *sbus; (jdk) */
3015 static stringpart
*newstring
= NULL
;
3017 /* prefix is NULL for hierarchical (spice) netlists and for */
3018 /* internal netlist manipulation. */
3020 if (prefix
== NULL
) {
3021 pinlab
= NetToLabel(netid
, cschem
);
3022 if (pinlab
!= NULL
) {
3024 /* If this is a "temp label", regenerate the name according to */
3025 /* the actual net number, if it does not match. This prevents */
3026 /* unrelated temp labels from getting the same name. */
3028 if (pinlab
->string
->type
!= FONT_NAME
) {
3029 if (sscanf(pinlab
->string
->data
.string
+ 3, "%d", &locnetid
) == 1) {
3030 if (locnetid
!= netid
) {
3031 newtext
= pinlab
->string
->data
.string
;
3034 newnet
.net
.id
= netid
;
3035 pinlab
->string
->data
.string
= textprintnet(newtext
, NULL
, &newnet
);
3040 return pinlab
->string
;
3043 /* If there's no label associated with this network, make one */
3044 /* called "intn" where n is the net number (netid). This is a */
3045 /* temp label (denoted by lack of a font specifier). */
3048 newnet
.net
.id
= netid
;
3049 pinpos
= NetToPosition(netid
, cschem
);
3050 netlabel
= (LabellistPtr
)new_tmp_pin(cschem
, pinpos
, NULL
, "int", &newnet
);
3051 return (netlabel
) ? netlabel
->label
->string
: NULL
;
3055 /* Flattened (e.g., sim) netlists */
3057 for (netname
= cschem
->netnames
; netname
!= NULL
; netname
= netname
->next
) {
3058 if (netname
->netid
== netid
) {
3059 if (netname
->localpin
!= NULL
)
3060 return netname
->localpin
;
3065 /* Generate the string for the local instantiation of this pin */
3066 /* If this node does not have a pin, create one using the current */
3067 /* hierarchy prefix as the string. */
3069 pinlab
= NetToLabel(netid
, cschem
);
3070 if (pinlab
!= NULL
) {
3071 stringpart
*tmpstring
= pinlab
->string
;
3072 snew
= textprint(tmpstring
, NULL
);
3075 snew
= (char *)malloc(12);
3076 sprintf(snew
, "int%d", netid
);
3083 newtext
= (char *)malloc(1 + strlen(snew
) + strlen(prefix
));
3084 sprintf(newtext
, "%s%s", prefix
, snew
);
3088 /* "newstring" is allocated only once and should not be free'd */
3089 /* by the calling routine. */
3091 if (newstring
== NULL
) {
3092 newstring
= (stringpart
*)malloc(sizeof(stringpart
));
3093 newstring
->nextpart
= NULL
;
3094 newstring
->type
= TEXT_STRING
;
3097 free(newstring
->data
.string
);
3099 newstring
->data
.string
= newtext
;
3103 /*----------------------------------------------------------------------*/
3104 /* Find the net which connects to the given pin label */
3105 /* Return NULL (no net) if no match was found. */
3106 /*----------------------------------------------------------------------*/
3108 Genericlist
*pintonet(objectptr cschem
, objinstptr cinst
, labelptr testpin
)
3110 LabellistPtr seeklabel
;
3112 buslist
*sbus
, *tbus
;
3113 Genericlist netlist
, *tmplist
;
3115 /* check against local pins, if this pin is declared local */
3117 seeklabel
= (testpin
->pin
== GLOBAL
) ? global_labels
: cschem
->labels
;
3119 netlist
.subnets
= 0;
3120 for (; seeklabel
!= NULL
; seeklabel
= seeklabel
->next
)
3121 if (!stringcomprelaxed(seeklabel
->label
->string
, testpin
->string
, cinst
))
3123 /* Quick simple case: no bus */
3124 if (seeklabel
->subnets
== 0)
3125 return (Genericlist
*)seeklabel
;
3127 tmplist
= break_up_bus(testpin
, cinst
, (Genericlist
*)seeklabel
);
3129 /* It is necessary to be able to put together a netlist from */
3130 /* partial sources none of which may be complete. */
3132 if (tmplist
!= NULL
) {
3133 if (netlist
.subnets
== 0) copy_bus(&netlist
, tmplist
);
3135 for (lbus
= 0; lbus
< tmplist
->subnets
; lbus
++) {
3136 sbus
= netlist
.net
.list
+ lbus
;
3137 tbus
= tmplist
->net
.list
+ lbus
;
3138 if (sbus
->netid
== 0)
3139 sbus
->netid
= tbus
->netid
; /* copy forward */
3140 else if (tbus
->netid
== 0)
3141 tbus
->netid
= sbus
->netid
; /* copy back */
3142 if (sbus
->netid
!= 0)
3145 if (matched
== netlist
.subnets
) {
3146 free(netlist
.net
.list
);
3152 if (netlist
.subnets
!= 0) {
3153 free(netlist
.net
.list
);
3154 return tmplist
; /* Returns list with partial entries */
3156 return NULL
; /* No matches found */
3161 /*----------------------------------------------------------------------*/
3162 /* Automatic Schematic Generation stuff--- */
3163 /* Blow away all polygons in the schematic and replace them with a */
3164 /* simple rat's-nest (point-to-point). */
3166 /* This routine is more of a proof-of-concept than a useful routine, */
3167 /* intended to be called only from the Tcl console. It is intended to */
3168 /* reveal most of the issues related to automatic schematic generation, */
3169 /* in which the netlist, or a portion of it, can be redrawn as objects */
3170 /* are moved around to maintain the relationships present in the */
3171 /* calls structure. */
3172 /*----------------------------------------------------------------------*/
3174 void ratsnest(objinstptr thisinst
)
3180 objectptr pschem
, cschem
;
3182 polyptr ppoly
, *newpoly
;
3184 int i
, netid
, points
, sub_bus
, lbus
;
3188 cschem
= thisinst
->thisobject
;
3189 pschem
= (cschem
->schemtype
== SECONDARY
) ? cschem
->symschem
: cschem
;
3191 /* Go through the netlist and remove all polygons. Remove */
3192 /* the polygons from the object as well as from the netlist. */
3194 for (plist
= pschem
->polygons
; plist
!= NULL
; plist
= plist
->next
) {
3195 ppoly
= plist
->poly
;
3196 ppoly
->type
+= REMOVE_TAG
; /* tag for removal */
3198 freepolylist(&pschem
->polygons
);
3200 /* for each schematic (PRIMARY or SECONDARY), remove the tagged */
3203 for (i
= 0; i
< xobjs
.pages
; i
++) {
3204 cinst
= xobjs
.pagelist
[i
]->pageinst
;
3205 if (cinst
&& (cinst
->thisobject
->schemtype
== SECONDARY
) &&
3206 (cinst
->thisobject
->symschem
== pschem
)) {
3207 delete_tagged(cinst
);
3209 else if (cinst
== thisinst
)
3210 delete_tagged(cinst
);
3213 /* Now, for each net ID in the label list, run through the calls and */
3214 /* find all points in the calls connecting to that net. Link */
3215 /* them with as many polygons as needed. These polygons are point- */
3216 /* to-point lines, turning the schematic representation into a */
3219 for (llist
= pschem
->labels
; llist
!= NULL
; llist
= llist
->next
) {
3221 if (llist
->subnets
== 0) {
3222 netid
= llist
->net
.id
;
3226 sbus
= llist
->net
.list
+ lbus
;
3227 netid
= sbus
->netid
;
3228 sub_bus
= sbus
->subnetid
;
3232 for (calls
= pschem
->calls
; calls
!= NULL
; calls
= calls
->next
) {
3234 /* Determine schematic object from the object called. Start a */
3235 /* new polygon any time we switch schematic pages. */
3237 if (calls
->cschem
!= cschem
)
3239 cschem
= calls
->cschem
;
3241 for (ports
= calls
->ports
; ports
!= NULL
; ports
= ports
->next
) {
3242 if (ports
->netid
== netid
) {
3244 /* Find the location of this port in the coordinates of cschem */
3246 result
= PortToPosition(calls
->callinst
, ports
->portid
, &portpos
);
3247 if (result
== True
) {
3250 NEW_POLY(newpoly
, cschem
);
3251 polydefaults(*newpoly
, 1, portpos
.x
, portpos
.y
);
3252 (*newpoly
)->style
|= UNCLOSED
;
3253 (*newpoly
)->color
= RATSNESTCOLOR
;
3254 addpoly(cschem
, *newpoly
, (Genericlist
*)llist
);
3257 poly_add_point(*newpoly
, &portpos
);
3260 Fprintf(stderr
, "Error: Cannot find pin connection in symbol!\n");
3261 /* Deal with error condition? */
3266 if (++lbus
>= llist
->subnets
) break;
3270 /* Now, move all the labels to reconnect to their networks */
3274 /* Refresh the screen */
3275 drawarea(NULL
, NULL
, NULL
);
3280 /*----------------------------------------------------------------------*/
3281 /* Find a net or netlist in object cschem with the indicated name. */
3283 /* This is the same routine as above, but finds the net with the given */
3284 /* name, or all nets which are a subset of the given name, if the name */
3285 /* is the name of a bus. Return NULL if no match was found. */
3286 /*----------------------------------------------------------------------*/
3288 Genericlist
*nametonet(objectptr cschem
, objinstptr cinst
, char *netname
)
3290 LabellistPtr seeklabel
;
3291 stringpart newstring
;
3293 /* Build a simple xcircuit string from "netname" (nothing malloc'd) */
3294 newstring
.type
= TEXT_STRING
;
3295 newstring
.nextpart
= NULL
;
3296 newstring
.data
.string
= netname
;
3298 /* Check against local networks */
3300 for (seeklabel
= cschem
->labels
; seeklabel
!= NULL
; seeklabel
= seeklabel
->next
)
3301 if (!stringcomprelaxed(seeklabel
->label
->string
, &newstring
, cinst
))
3302 return (Genericlist
*)seeklabel
;
3304 /* Check against global networks */
3306 for (seeklabel
= global_labels
; seeklabel
!= NULL
; seeklabel
= seeklabel
->next
)
3307 if (!stringcomprelaxed(seeklabel
->label
->string
, &newstring
, cinst
))
3308 return (Genericlist
*)seeklabel
;
3313 /*----------------------------------------------------------------------*/
3314 /* Check if two parts may have the same component index. This is true */
3315 /* for parts with parameterized pins, such as those in the "quadparts" */
3316 /* library. Compare the names of the ports in each instance. If none */
3317 /* match, then these are unique subparts of a single component, and we */
3318 /* should return FALSE. Otherwise, return TRUE. */
3319 /*----------------------------------------------------------------------*/
3321 Boolean
samepart(CalllistPtr clist1
, CalllistPtr clist2
)
3325 char *pinname1
, *pinname2
;
3328 if (clist1
->callobj
!= clist2
->callobj
) return FALSE
;
3331 for (port
= clist1
->ports
; port
!= NULL
; port
= port
->next
) {
3332 plab
= PortToLabel(clist1
->callinst
, port
->portid
);
3333 pinname1
= textprint(plab
->string
, clist1
->callinst
);
3334 pinname2
= textprint(plab
->string
, clist2
->callinst
);
3335 if (!strcmp(pinname1
, pinname2
)) rvalue
= TRUE
;
3342 /*----------------------------------------------------------------------*/
3343 /* Generate an index number for a device in a flat netlist format. */
3344 /* We ignore any values given to "index" (this is a to-do item, since */
3345 /* many schematics are only a single level of hierarchy, so it makes */
3346 /* sense to keep designated indices when possible), but do generate */
3347 /* independent index numbering for different device classes. */
3348 /*----------------------------------------------------------------------*/
3350 u_int
devflatindex(char *devname
)
3352 flatindex
*fp
= flatrecord
;
3353 while (fp
!= NULL
) {
3354 if (!strcmp(devname
, fp
->devname
)) {
3359 fp
= (flatindex
*)malloc(sizeof(flatindex
));
3360 fp
->next
= flatrecord
;
3363 fp
->devname
= devname
;
3367 /*----------------------------------------------------------------------*/
3368 /* Free the list of per-class flat device indices */
3369 /*----------------------------------------------------------------------*/
3371 void freeflatindex()
3373 flatindex
*fpnext
, *fp
= flatrecord
;
3374 while (fp
!= NULL
) {
3382 /*---------------------------------------------------------*/
3383 /* Convert a number (up to 99999) to its base-36 equivalent */
3384 /*---------------------------------------------------------*/
3386 int convert_to_b36(int number
)
3391 b36idx
= (tmpidx
/ 10000) * 1679616;
3393 b36idx
+= (tmpidx
/ 1000) * 46656;
3395 b36idx
+= (tmpidx
/ 100) * 1296;
3397 b36idx
+= ((tmpidx
/ 10) * 36) + (tmpidx
% 10);
3402 /*----------------------------------------------------------------------*/
3403 /* Generate an index number for this device. Count all devices having */
3404 /* the same device name (as printed in the netlist) in the calls of */
3405 /* the calling object (cfrom) */
3407 /* Update (9/29/04): Remove any leading whitespace from the device */
3408 /* name to prevent treating, for example, "J?" and " J?" as separate */
3411 /* Update (7/28/05): To allow SPICE device indices to include all */
3412 /* alphanumerics, we parse in base-36, and count in the subset of */
3413 /* base-36 that can be interpreted as decimal (1-9, 36-45, etc.) */
3414 /*----------------------------------------------------------------------*/
3416 u_int
devindex(objectptr cfrom
, CalllistPtr clist
)
3418 CalllistPtr cptr
, listfrom
= cfrom
->calls
;
3419 objectptr devobj
= clist
->callobj
;
3420 u_int
*occupied
, total
, objindex
, i
, b36idx
;
3421 char *devname
, *cname
;
3422 /* oparamptr ops; (jdk) */
3424 if (listfrom
== NULL
) return (u_int
)0;
3425 if (clist
->devindex
>= 0) return clist
->devindex
;
3427 devname
= (clist
->devname
== NULL
) ? devobj
->name
: clist
->devname
;
3428 while (isspace(*devname
)) devname
++;
3430 /* Count total number of devices */
3431 for (cptr
= listfrom
, total
= 0; cptr
!= NULL
; cptr
= cptr
->next
, total
++);
3432 occupied
= (u_int
*)malloc(total
* sizeof(u_int
));
3435 for (cptr
= listfrom
, total
= 0; cptr
!= NULL
; cptr
= cptr
->next
, total
++) {
3436 occupied
[total
] = 0;
3437 if (cptr
== clist
) continue;
3438 cname
= (cptr
->devname
== NULL
) ? cptr
->callobj
->name
: cptr
->devname
;
3439 while (isspace(*cname
)) cname
++;
3440 if (!strcmp(cname
, devname
)) {
3441 occupied
[total
] = cptr
->devindex
;
3442 if (cptr
->devindex
== objindex
) objindex
++;
3446 b36idx
= convert_to_b36(objindex
);
3447 for (; objindex
<= total
; objindex
++) {
3448 b36idx
= convert_to_b36(objindex
);
3449 for (i
= 0; i
< total
; i
++)
3450 if (occupied
[i
] == b36idx
)
3457 clist
->devindex
= b36idx
;
3461 /*----------------------------------------------------------------------*/
3462 /* Generate and return a list of all info label prefixes in a design */
3463 /* This does not depend on the netlist being generated (i.e., does not */
3464 /* use CalllistPtr) */
3465 /*----------------------------------------------------------------------*/
3467 void genprefixlist(objectptr cschem
, slistptr
*modelist
)
3476 for (pgen
= cschem
->plist
; pgen
< cschem
->plist
+ cschem
->parts
; pgen
++) {
3477 if (IS_LABEL(*pgen
)) {
3478 plabel
= TOLABEL(pgen
);
3479 if (plabel
->pin
== INFO
) {
3481 strptr
= findtextinstring(":", &locpos
, plabel
->string
, cinst
);
3482 if (locpos
<= 0 || strptr
== NULL
)
3483 continue; /* null after netlist type designator, don't count */
3485 for (modeptr
= *modelist
; modeptr
; modeptr
= modeptr
->next
)
3486 if (!strncmp(modeptr
->alias
, strptr
->data
.string
, locpos
))
3489 if (modeptr
== NULL
) { /* Mode has not been enumerated */
3490 modeptr
= (slistptr
)malloc(sizeof(stringlist
));
3491 modeptr
->alias
= (char *)malloc(locpos
+ 1);
3492 strncpy(modeptr
->alias
, strptr
->data
.string
, locpos
);
3493 modeptr
->next
= *modelist
;
3494 *modelist
= modeptr
;
3498 else if (IS_OBJINST(*pgen
)) {
3499 cinst
= TOOBJINST(pgen
);
3500 genprefixlist(cinst
->thisobject
, modelist
);
3501 if (cinst
->thisobject
->symschem
!= NULL
)
3502 genprefixlist(cinst
->thisobject
->symschem
, modelist
);
3507 /*----------------------------------------------------------------------*/
3508 /* Create and return an ordered list of info labels for the schematic */
3509 /* page cschem. A linked label list is returned, combining info labels */
3510 /* from all schematic pages related to cschem (master and slave pages). */
3511 /* It is the responsibility of the calling routine to free the linked */
3513 /*----------------------------------------------------------------------*/
3515 LabellistPtr
geninfolist(objectptr cschem
, objinstptr cinst
, char *mode
)
3521 LabellistPtr newllist
, listtop
, srchlist
;
3528 for (pgen
= cschem
->plist
; pgen
< cschem
->plist
+ cschem
->parts
; pgen
++) {
3529 if (IS_LABEL(*pgen
)) {
3530 plabel
= TOLABEL(pgen
);
3531 if ((plabel
->pin
== INFO
) && !textncomp(plabel
->string
, mode
, cinst
)) {
3533 if (mode
[0] == '\0') {
3534 strptr
= findtextinstring(":", &locpos
, plabel
->string
, cinst
);
3538 strptr
= findstringpart(strlen(mode
), &locpos
, plabel
->string
, cinst
);
3541 continue; /* null after netlist type designator */
3543 strt
= strptr
->data
.string
+ locpos
+ 1;
3546 if (sscanf(strt
, "%d", &j
) != 1) continue;
3548 /* Consider only positive-valued numbers. Negative */
3549 /* indices are handled by "writenet" by merging the */
3550 /* minus character into the mode name. */
3552 if (j
< 0) continue;
3553 if (j
>= vmax
) vmax
= j
+ 1;
3558 newllist
= (LabellistPtr
)malloc(sizeof(Labellist
));
3559 newllist
->label
= plabel
;
3560 newllist
->cschem
= cschem
;
3561 newllist
->cinst
= cinst
;
3562 newllist
->net
.id
= j
; /* use this to find the ordering */
3563 newllist
->subnets
= 0; /* so free() doesn't screw up */
3565 /* Order the linked list */
3567 if ((listtop
== NULL
) || (listtop
->net
.id
>= j
)) {
3568 newllist
->next
= listtop
;
3572 for (srchlist
= listtop
; srchlist
!= NULL
; srchlist
= srchlist
->next
) {
3573 if ((srchlist
->next
!= NULL
) && (srchlist
->next
->net
.id
>= j
)) {
3574 newllist
->next
= srchlist
->next
;
3575 srchlist
->next
= newllist
;
3578 else if (srchlist
->next
== NULL
) {
3579 srchlist
->next
= newllist
;
3580 newllist
->next
= NULL
;
3590 /*----------------------------------------------------------------------*/
3591 /* Trivial default pin name lookup. Parse an object's pin labels for */
3592 /* pin names. Return the text of the nth pin name, as counted by the */
3593 /* position in the object's parts list. If there are fewer than (n+1) */
3594 /* pin labels in the object, return NULL. Otherwise, return the pin */
3595 /* name in a malloc'd character string which the caller must free. */
3596 /*----------------------------------------------------------------------*/
3598 char *defaultpininfo(objinstptr cinst
, int pidx
)
3602 objectptr cschem
= cinst
->thisobject
;
3606 for (pgen
= cschem
->plist
; pgen
< cschem
->plist
+ cschem
->parts
; pgen
++) {
3607 if (IS_LABEL(*pgen
)) {
3608 plabel
= TOLABEL(pgen
);
3609 if (plabel
->pin
== LOCAL
) {
3610 if (count
== pidx
) {
3611 stxt
= textprint(plabel
->string
, cinst
);
3621 /*----------------------------------------------------------------------*/
3622 /* Parse an object's info labels for pin names. This is *not* a */
3623 /* netlist function. The nth pin name is returned, or NULL if not */
3624 /* found. Return value is a malloc'd string which the caller must */
3626 /*----------------------------------------------------------------------*/
3628 char *parsepininfo(objinstptr cinst
, char *mode
, int pidx
)
3632 u_char
*strt
, *fnsh
;
3633 int slen
, i
, locpos
;
3634 objectptr cschem
= cinst
->thisobject
;
3639 for (pgen
= cschem
->plist
; pgen
< cschem
->plist
+ cschem
->parts
; pgen
++) {
3640 if (IS_LABEL(*pgen
)) {
3641 plabel
= TOLABEL(pgen
);
3642 if (plabel
->pin
== INFO
) {
3643 slen
= stringlength(plabel
->string
, True
, cinst
);
3644 for (i
= 1; i
< slen
; i
++) {
3645 strptr
= findstringpart(i
, &locpos
, plabel
->string
, cinst
);
3646 if (locpos
>= 0 && *(strptr
->data
.string
+ locpos
) == ':') break;
3648 /* Currently we are not checking against "mode". . .*/
3649 /* interpret all characters after the colon */
3651 for (++i
; i
< slen
; i
++) {
3652 strptr
= findstringpart(i
, &locpos
, plabel
->string
, cinst
);
3655 /* Do for all text characters */
3656 strt
= strptr
->data
.string
+ locpos
;
3660 if (*strt
== 'p') { /* Pin found! */
3661 if (count
== pidx
) { /* Get pin text */
3664 while (*fnsh
!= ' ' && *fnsh
!= '\0') fnsh
++;
3665 sout
= malloc(fnsh
- strt
+ 1);
3666 strncpy(sout
, strt
, fnsh
- strt
);
3680 /*----------------------------------------------------------------------*/
3681 /* Look for information labels in the object parts list. Parse the */
3682 /* information labels and print results to specified output device. */
3683 /* (This is not very robust to errors---needs work!) */
3685 /* If "autonumber" is true, then the parsed info label is used to */
3686 /* auto-number devices. */
3687 /*----------------------------------------------------------------------*/
3689 char *parseinfo(objectptr cfrom
, objectptr cthis
, CalllistPtr clist
,
3690 char *prefix
, char *mode
, Boolean autonumber
, Boolean no_output
)
3692 /* genericptr *pgen; (jdk) */
3693 stringpart
*strptr
, *ppin
, *optr
;
3694 char *snew
, *sout
= NULL
, *pstring
;
3695 u_char
*strt
, *fnsh
;
3697 oparamptr ops
, instops
;
3698 int portid
, locpos
, i
, k
, subnet
, slen
; /* j, (jdk) */
3701 Boolean is_flat
= False
, is_symbol
= False
, is_iso
= False
, is_quoted
;
3702 Boolean do_update
= True
, include_once
= False
;
3703 char *locmode
, *b36str
, *sptr
;
3704 LabellistPtr infolist
, infoptr
;
3707 /* For flat netlists, prefix the mode with the word "flat". */
3708 /* As of 3/8/07, this includes the ".sim" format, which */
3709 /* should be called as mode "flatsim". */
3712 if (locmode
&& (!strncmp(mode
, "flat", 4) || !strncmp(mode
, "pseu", 4))) {
3717 /* mode == "" is passed when running resolve_devindex() and indicates */
3718 /* that the routine should be used to assign devindex to calls whose */
3719 /* devices have been manually assigned numbers. The preferred method */
3720 /* is to assign these values through the "index" parameter. Use of */
3721 /* parseinfo() ensures that objects that have no "index" parameter */
3722 /* but have valid info-labels for SPICE or PCB output will be */
3723 /* assigned the correct device index. */
3724 /* Sept. 3, 2006---The use of the prepended "@" character on */
3725 /* parameter names means that I can replace the cryptic "idx" with */
3726 /* the more obvious "index", which previously conflicted with the */
3727 /* PostScript command of the same name. Parameters "index" and "idx" */
3728 /* are treated interchangeably by the netlister. */
3730 if (locmode
[0] == '\0')
3733 /* 1st pass: look for valid labels; see if more than one applies. */
3734 /* If so, order them correctly. Labels may not be numbered in */
3735 /* sequence, and may begin with zero. We allow a lot of flexibility */
3736 /* but the general expectation is that sequences start at 0 or 1 and */
3737 /* increase by consecutive integers. */
3739 infolist
= geninfolist(cthis
, clist
->callinst
, locmode
);
3741 /* Now parse each label in sequence and output the result to */
3742 /* the return string. */
3744 sout
= (char *)malloc(1);
3747 for (infoptr
= infolist
; infoptr
!= NULL
; infoptr
= infoptr
->next
) {
3748 objectptr pschem
, cschem
= infoptr
->cschem
;
3749 labelptr plabel
= infoptr
->label
;
3750 objinstptr cinst
= infoptr
->cinst
;
3752 /* move to colon character */
3753 slen
= stringlength(plabel
->string
, True
, cinst
);
3754 for (i
= 1; i
< slen
; i
++) {
3755 strptr
= findstringpart(i
, &locpos
, plabel
->string
, cinst
);
3756 if (locpos
>= 0 && *(strptr
->data
.string
+ locpos
) == ':') break;
3759 /* interpret all characters after the colon */
3760 for (++i
; i
< slen
; i
++) {
3761 strptr
= findstringpart(i
, &locpos
, plabel
->string
, cinst
);
3764 /* Do for all text characters */
3765 strt
= strptr
->data
.string
+ locpos
;
3772 sout
= (char *)realloc(sout
, strlen(sout
) + 2);
3776 sout
= (char *)realloc(sout
, strlen(sout
) + 2);
3780 sout
= (char *)realloc(sout
, strlen(sout
) + 2);
3784 if (clist
->devname
== NULL
)
3785 clist
->devname
= strdup(sout
);
3788 newindex
= devflatindex(clist
->devname
);
3790 newindex
= devindex(cfrom
, clist
);
3791 b36str
= d36a(newindex
);
3792 sout
= (char *)realloc(sout
, strlen(sout
) + strlen(b36str
) + 1);
3793 sprintf(sout
+ strlen(sout
), "%s", b36str
);
3797 sout
= (char *)realloc(sout
, strlen(sout
)
3798 + strlen(cschem
->name
) + 1);
3799 strcat(sout
, cschem
->name
);
3802 sptr
= strstr(cschem
->name
, "::");
3804 sptr
= cschem
->name
;
3807 sout
= (char *)realloc(sout
, strlen(sout
)
3808 + strlen(sptr
) + 1);
3812 sout
= (char *)realloc(sout
, strlen(sout
) + 7);
3813 sprintf(sout
+ strlen(sout
), "%d",
3817 sout
= (char *)realloc(sout
, strlen(sout
) + 7);
3818 sprintf(sout
+ strlen(sout
), "%d",
3822 if (no_output
) break;
3823 include_once
= True
;
3826 if (no_output
) break;
3828 /* Followed by a filename to include verbatim into */
3829 /* the output. Filename either has no spaces or */
3832 /* Use textprint to catch any embedded parameters */
3834 snew
= textprint(strptr
, cinst
);
3836 while (*strt
!= '\0') {
3838 if (include_once
&& *(strt
+ 1) == 'F')
3840 else if (!include_once
&& *(strt
+ 1) == 'f')
3846 if (*strt
== '\0') {
3847 /* No filename; print verbatim */
3849 include_once
= False
;
3854 if (*strt
== '"') strt
++;
3855 if (*strt
== '"' || *strt
== '\0') break;
3857 while (*fnsh
!= '\0' && !isspace(*fnsh
) && *fnsh
!= '"')
3859 strncpy(_STR
, strt
, (int)(fnsh
- strt
));
3860 _STR
[(int)(fnsh
- strt
)] = '\0';
3864 /* Do variable and tilde expansion on the filename */
3865 xc_tilde_expand(_STR
, 149);
3866 xc_variable_expand(_STR
, 149);
3868 /* Check if this file has been included already */
3870 if (check_included(_STR
)) {
3871 include_once
= False
;
3874 append_included(_STR
);
3877 /* Open the indicated file and dump to the output */
3878 finclude
= fopen(_STR
, "r");
3879 if (finclude
!= NULL
) {
3882 int stlen
= strlen(sout
);
3883 while (fgets(_STR
, 149, finclude
)) {
3884 nlen
= strlen(_STR
);
3885 sout
= (char *)realloc(sout
, stlen
+ nlen
+ 1);
3886 sptr
= sout
+ stlen
;
3893 Wprintf("No file named %s found", _STR
);
3895 include_once
= False
;
3899 /* Pin name either has no spaces or is in quotes */
3906 if (*strt
== '"' || *strt
== '\0') break;
3908 while (*fnsh
!= '\0' && !isspace(*fnsh
) && *fnsh
!= '"')
3910 strncpy(_STR2
, strt
, (int)(fnsh
- strt
));
3911 _STR2
[(int)(fnsh
- strt
)] = '\0';
3913 /* if (is_quoted || *fnsh == '\0') i++; */
3916 /* Find the port which corresponds to this pin name */
3917 /* in the called object (cschem). If this is a */
3918 /* linked symbol/schematic, then the port list will */
3919 /* be in the schematic view, not in the symbol view */
3922 if (cschem
->ports
== NULL
&& cschem
->symschem
!= NULL
&&
3923 cschem
->symschem
->ports
!= NULL
)
3924 pschem
= cschem
->symschem
;
3926 for (ports
= pschem
->ports
; ports
!= NULL
;
3927 ports
= ports
->next
) {
3929 subnet
= getsubnet(ports
->netid
, pschem
);
3930 ppin
= nettopin(ports
->netid
, pschem
, NULL
);
3931 // NOTE: _STR is used inside textprintsubnet()
3932 pstring
= textprintsubnet(ppin
, NULL
, subnet
);
3933 if (!strcmp(pstring
, _STR2
)) {
3934 portid
= ports
->portid
;
3941 if (ports
!= NULL
) {
3943 /* Find the matching port in the calling object instance */
3945 for (ports
= clist
->ports
; ports
!= NULL
;
3946 ports
= ports
->next
)
3947 if (ports
->portid
== portid
) break;
3949 if (ports
!= NULL
) {
3951 ppin
= nettopin(ports
->netid
, cfrom
, prefix
);
3952 subnet
= getsubnet(ports
->netid
, cfrom
);
3953 snew
= textprintsubnet(ppin
, cinst
, subnet
);
3954 sout
= (char *)realloc(sout
, strlen(sout
) +
3961 Fprintf(stderr
, "Error: called non-existant port\n");
3965 Wprintf("No pin named %s in device %s", _STR
, cschem
->name
);
3969 /* Parameter name either has no spaces or is in quotes */
3976 if (*strt
== '"' || *strt
== '\0') break;
3978 while (*fnsh
!= '\0' && !isspace(*fnsh
) && *fnsh
!= '"')
3980 strncpy(_STR
, strt
, (int)(fnsh
- strt
));
3981 _STR
[(int)(fnsh
- strt
)] = '\0';
3983 /* if (is_quoted || *fnsh == '\0') i++; */
3986 /* Compare this name against the parameter keys */
3987 ops
= match_param(cschem
, _STR
);
3989 /* For backwards compatibility, also try matching against */
3990 /* the parameter default value (method used before */
3991 /* implementing parameters as key:value pairs). */
3994 for (ops
= cschem
->params
; ops
!= NULL
; ops
= ops
->next
) {
3995 if (ops
->type
== XC_STRING
) {
3996 if (!textcomp(ops
->parameter
.string
, _STR
, NULL
)) {
3997 /* substitute the parameter or default */
3998 instops
= find_param(cinst
, ops
->key
);
3999 optr
= instops
->parameter
.string
;
4000 if (stringlength(optr
, True
, cinst
) > 0) {
4001 snew
= textprint(optr
, cinst
);
4002 sout
= (char *)realloc(sout
, strlen(sout
)
4003 + strlen(snew
) + 1);
4014 Wprintf("No parameter named %s in device %s",
4015 _STR
, cschem
->name
);
4020 /* Presence of ".ends" statement forces xcircuit */
4021 /* not to write ".end" at the end of the netlist. */
4024 if (!strncmp(strt
+ 1, "ends", 4))
4027 sout
= (char *)realloc(sout
, strlen(sout
) + 2);
4028 *(sout
+ strlen(sout
) - 1) = *strt
;
4029 *(sout
+ strlen(sout
)) = '\0';
4034 if (clist
->devname
== NULL
)
4035 clist
->devname
= strdup(sout
);
4037 /* Parameters with unresolved question marks are treated */
4038 /* like a "%i" string. */
4040 if ((key
!= NULL
) && !textncomp(strptr
, "?", cinst
)) {
4041 if (do_update
|| autonumber
) {
4042 if (is_flat
|| (cfrom
== NULL
))
4043 newindex
= devflatindex(clist
->devname
);
4045 newindex
= devindex(cfrom
, clist
);
4047 b36str
= d36a(newindex
);
4048 sout
= (char *)realloc(sout
, strlen(sout
) + strlen(b36str
) + 1);
4049 sprintf(sout
+ k
, "%s", b36str
);
4052 /* When called with autonumber = TRUE, generate a parameter */
4053 /* instance, replacing the question mark with the new index */
4057 copyparams(cinst
, cinst
);
4058 ops
= match_instance_param(cinst
, key
);
4059 optr
= ops
->parameter
.string
;
4060 if (!textncomp(optr
, "?", cinst
)) {
4061 optr
->data
.string
= (char *)realloc(optr
->data
.string
,
4062 strlen(sout
+ k
) + 1);
4063 strcpy(optr
->data
.string
, sout
+ k
);
4065 else Wprintf("Error while auto-numbering parameters");
4066 resolveparams(cinst
);
4070 /* A "?" default parameter that has a (different) */
4071 /* instance value becomes the device number. */
4073 if ((key
!= NULL
) && (clist
->devindex
< 0)) {
4074 ops
= match_param(cschem
, key
);
4075 if (ops
->type
== XC_STRING
) {
4077 if (!textcomp(ops
->parameter
.string
, "?", NULL
)) {
4079 /* Ignore the value and generate one instead */
4080 newindex
= devflatindex(clist
->devname
);
4081 b36str
= d36a(newindex
);
4083 sout
= (char *)realloc(sout
, strlen(sout
)
4084 + strlen(b36str
) + 1);
4085 sprintf(sout
+ k
, "%s", b36str
);
4086 continue; /* go on to next stringpart */
4088 else if (cfrom
!= NULL
) {
4090 /* Interpret parameter string as a component */
4091 /* number so we can check for duplicates. Assume */
4092 /* a base-36 number, which includes all alphabet */
4093 /* characters. This will disambiguate, e.g., "1" */
4094 /* and "1R", but flag case-sensitivity, e.g., */
4095 /* "1R" and "1r". Thanks to Carsten Thomas for */
4096 /* pointing out the problem with assuming */
4097 /* numerical component values. */
4100 newindex
= (u_int
) strtol(strt
, &endptr
, 36);
4101 if (*endptr
!= '\0') {
4102 Fprintf(stderr
, "Warning: Use of non-alphanumeric"
4103 " characters in component number \"%s\"\n", strt
);
4105 clist
->devindex
= newindex
;
4106 for (plist
= cfrom
->calls
; plist
; plist
= plist
->next
) {
4107 if ((plist
== clist
) ||
4108 (plist
->callobj
!= clist
->callobj
)) continue;
4110 /* Two parts have been named the same. Flag */
4111 /* it, but don't try to do anything about it. */
4112 /* 11/22/06---additionally check part numbers, */
4113 /* in case the symbol is a component sub-part. */
4115 if (plist
->devindex
== newindex
) {
4116 if (samepart(plist
, clist
)) {
4117 Fprintf(stderr
, "Warning: duplicate part number"
4118 " %s%s and %s%s\n", sout
, strt
, sout
, strt
);
4127 stlen
= strlen(sout
);
4128 sout
= (char *)realloc(sout
, stlen
+ 2);
4130 /* By convention, greek "mu" becomes ASCII "u", NOT "m", */
4131 /* otherwise we get, e.g., millifarads instead of microfarads */
4133 if ((is_symbol
&& (*strt
== 'm')) || (is_iso
&& (*strt
== 0265)))
4134 *(sout
+ stlen
) = 'u';
4136 *(sout
+ stlen
) = *strt
;
4137 *(sout
+ stlen
+ 1) = '\0';
4142 /* Some label controls are interpreted. Most are ignored */
4144 switch(strptr
->type
) {
4146 key
= strptr
->data
.string
;
4152 sout
= (char *)realloc(sout
, strlen(sout
) + 2);
4156 sout
= (char *)realloc(sout
, strlen(sout
) + 2);
4160 is_symbol
= issymbolfont(strptr
->data
.font
);
4161 is_iso
= isisolatin1(strptr
->data
.font
);
4167 /* Insert a newline between multiple valid info labels */
4168 if (infoptr
->next
!= NULL
) {
4169 sout
= (char *)realloc(sout
, strlen(sout
) + 2);
4173 freelabellist(&infoptr
);
4175 if (*sout
== '\0') {
4182 /*----------------------------------------------------------------------*/
4183 /* Write a low-level device */
4184 /*----------------------------------------------------------------------*/
4186 int writedevice(FILE *fp
, char *mode
, objectptr cfrom
, CalllistPtr clist
,
4192 if (clist
== NULL
) {
4193 if (fp
!= NULL
) fprintf(fp
, "error: null device\n");
4197 /* Devices are always symbols. If the call indicates a schematic */
4198 /* and a separate associated symbol, then we want the symbol. */
4199 /* If we're writing a flat netlist, then return -1 to indicate that */
4200 /* the symbol instance needs to be flattened. */
4202 cthis
= clist
->callobj
;
4203 if (clist
->callobj
->schemtype
== PRIMARY
|| clist
->callobj
->schemtype
==
4205 if (clist
->callobj
->symschem
!= NULL
) {
4206 if (!strncmp(mode
, "flat", 4))
4209 cthis
= clist
->callobj
->symschem
;
4212 /* Look for information labels in the object parts list. */
4214 if ((sout
= parseinfo(cfrom
, cthis
, clist
, prefix
, mode
, FALSE
, FALSE
)) != NULL
) {
4223 /* Information labels do not necessarily exist. */
4229 /*----------------------------------------------------------------------*/
4230 /* Translate a hierarchical net name into an object and instance. */
4231 /* Return TRUE if found, FALSE if not. If found, object and instance */
4232 /* are returned in the argument pointers. */
4233 /*----------------------------------------------------------------------*/
4235 Boolean
HierNameToObject(objinstptr cinst
, char *hiername
, pushlistptr
*stack
)
4238 char *nexttoken
, *hptr
, *pptr
;
4239 objectptr cschem
, curobj
, newobj
;
4241 int devindex
, devlen
;
4242 /* pushlistptr stackentry; (jdk) */
4244 cschem
= cinst
->thisobject
;
4245 curobj
= (cschem
->schemtype
== SECONDARY
) ? cschem
->symschem
: cschem
;
4247 if (curobj
->calls
== NULL
) {
4248 if ((updatenets(cinst
, FALSE
) <= 0) || (curobj
->calls
== NULL
)) {
4249 Wprintf("Error in generating netlists!");
4255 while (hptr
!= NULL
) {
4256 nexttoken
= strchr(hptr
, '/');
4257 if (nexttoken
!= NULL
) *nexttoken
= '\0';
4258 pptr
= strrchr(hptr
, '(');
4260 if (sscanf(pptr
+ 1, "%d", &devindex
) == 0) {
4269 /* check to make sure that the netlist and device indices have been generated */
4270 for (calls
= curobj
->calls
; calls
!= NULL
; calls
= calls
->next
) {
4271 if (calls
->devindex
== -1) {
4272 cleartraversed(curobj
);
4273 resolve_indices(curobj
, FALSE
);
4277 /* hptr is now the object name, and devindex is the instance's call index */
4278 /* find the object corresponding to the name. */
4280 newobj
= NameToObject(hptr
, &newinst
, TRUE
);
4282 if (newobj
== NULL
) {
4284 /* Try device names (for netlist output) instead of object names */
4286 for (calls
= curobj
->calls
; calls
!= NULL
; calls
= calls
->next
) {
4287 if (calls
->devname
!= NULL
) {
4288 devlen
= strlen(calls
->devname
);
4289 if (!strncmp(hptr
, calls
->devname
, devlen
)) {
4290 if (devindex
== -1) {
4291 if (sscanf(hptr
+ devlen
, "%d", &devindex
) == 0)
4294 if (calls
->devindex
== devindex
) {
4295 newobj
= calls
->callinst
->thisobject
;
4303 for (calls
= curobj
->calls
; calls
!= NULL
; calls
= calls
->next
)
4304 if ((calls
->callobj
== newobj
) && (calls
->devindex
== devindex
))
4307 if (newobj
== NULL
|| calls
== NULL
) {
4308 Fprintf(stderr
, "object %s in hierarchy not found in schematic.\n", hptr
);
4313 /* Diagnostic information */
4314 /* fprintf(stderr, "object %s(%d) = %s\n", newobj->name, devindex, hptr); */
4316 curobj
= calls
->callobj
;
4317 push_stack(stack
, calls
->callinst
, NULL
);
4319 if (pptr
!= NULL
) *pptr
= '(';
4320 if (nexttoken
== NULL
) break;
4322 hptr
= nexttoken
+ 1;
4329 /*----------------------------------------------------------------------*/
4330 /* Save netlist into a flattened sim or spice file */
4331 /*----------------------------------------------------------------------*/
4333 void writeflat(objectptr cschem
, CalllistPtr cfrom
, char *prefix
, FILE *fp
, char *mode
)
4335 CalllistPtr calls
= cschem
->calls
;
4336 char *newprefix
= (char *)malloc(sizeof(char));
4338 /* reset device indexes */
4340 for (calls
= cschem
->calls
; calls
!= NULL
; calls
= calls
->next
)
4341 calls
->devindex
= -1;
4343 /* The naming convention for nodes below the top level uses a */
4344 /* slash-separated list of hierarchical names. Thus the call to */
4345 /* the hierarchical resolve_indices(). Indices used by bottom-most */
4346 /* symbols (e.g., flattened spice) will be generated by a separate */
4347 /* routine devflatindex(), unrelated to what is generated here, */
4348 /* which only affects the hierarchical naming of nodes. */
4350 resolve_indices(cschem
, FALSE
);
4352 /* write all the subcircuits */
4354 for (calls
= cschem
->calls
; calls
!= NULL
; calls
= calls
->next
) {
4356 makelocalpins(cschem
, calls
, prefix
);
4357 if (writedevice(fp
, mode
, cschem
, calls
, prefix
) < 0) {
4358 sprintf(_STR
, "%s_%u", calls
->callobj
->name
,
4359 devindex(cschem
, calls
));
4360 newprefix
= (char *)realloc(newprefix
, sizeof(char) * (strlen(prefix
)
4361 + strlen(_STR
) + 2));
4362 sprintf(newprefix
, "%s%s/", prefix
, _STR
);
4363 /* Allow cross-referenced parameters between symbols and schematics */
4364 /* by substituting into a schematic from its corresponding symbol */
4365 opsubstitute(calls
->callobj
, calls
->callinst
);
4366 /* psubstitute(calls->callinst); */
4367 writeflat(calls
->callobj
, calls
, newprefix
, fp
, mode
);
4369 clearlocalpins(calls
->callobj
);
4374 /*----------------------------------------------------------------------*/
4375 /* Topmost call to write a flattened netlist */
4376 /*----------------------------------------------------------------------*/
4378 void topflat(objectptr cschem
, objinstptr thisinst
, CalllistPtr cfrom
,
4379 char *prefix
, FILE *fp
, char *mode
)
4381 char *locmode
, *stsave
= NULL
;
4385 /* Set up local call list for parsing info labels in the top-level schematic */
4386 loccalls
.cschem
= NULL
;
4387 loccalls
.callobj
= cschem
;
4388 loccalls
.callinst
= thisinst
;
4389 loccalls
.devindex
= -1;
4390 loccalls
.ports
= NULL
;
4391 loccalls
.next
= NULL
;
4393 modlen
= strlen(mode
);
4394 locmode
= malloc(2 + modlen
);
4395 strcpy(locmode
, mode
);
4396 locmode
[modlen
+ 1] = '\0';
4398 /* "<mode>@" lines go in front */
4400 locmode
[modlen
] = '@';
4401 if (fp
!= NULL
) stsave
= parseinfo(NULL
, cschem
, &loccalls
, NULL
, locmode
, FALSE
,
4403 if (stsave
!= NULL
) {
4410 writeflat(cschem
, cfrom
, prefix
, fp
, mode
);
4413 /* Check for negative-numbered info labels, indicated output that */
4414 /* should be processed after everything else. */
4416 locmode
[modlen
] = '-';
4417 stsave
= parseinfo(NULL
, cschem
, &loccalls
, NULL
, locmode
, FALSE
, FALSE
);
4418 if (stsave
!= NULL
) {
4427 /*----------------------------------------------------------------------*/
4428 /* Write out the list of global nets and their pin names */
4429 /*----------------------------------------------------------------------*/
4431 void writeglobals(objectptr cschem
, FILE *fp
)
4437 if (fp
== NULL
) return;
4439 for (llist
= global_labels
; llist
!= NULL
; llist
= llist
->next
) {
4440 gpin
= llist
->label
;
4441 snew
= textprint(gpin
->string
, NULL
);
4442 fprintf(fp
, ".GLOBAL %s\n", snew
); /* hspice format */
4443 /* fprintf(fp, "%s = %d\n", snew, -gptr->netid); */ /* generic format */
4449 /*----------------------------------------------------------------------*/
4450 /* Write a SPICE subcircuit entry. */
4451 /*----------------------------------------------------------------------*/
4453 void writesubcircuit(FILE *fp
, objectptr cschem
)
4458 int netid
, length
, plen
, subnet
; /* portid, (jdk) */
4460 /* Objects which have no ports are not written */
4461 if ((cschem
->ports
!= NULL
) && (fp
!= NULL
)) {
4463 fprintf(fp
, ".subckt %s", cschem
->name
);
4464 length
= 9 + strlen(cschem
->name
);
4466 /* List of parameters in subcircuit. */
4467 /* Each parameter connects to a net which may have multiple */
4468 /* names, so find the net associated with the parameter */
4469 /* and convert it back into a pin name in the usual manner */
4470 /* using nettopin(). */
4472 for (ports
= cschem
->ports
; ports
!= NULL
;
4473 ports
= ports
->next
) {
4474 netid
= ports
->netid
;
4475 subnet
= getsubnet(netid
, cschem
);
4476 ppin
= nettopin(netid
, cschem
, NULL
);
4477 pstring
= textprintsubnet(ppin
, NULL
, subnet
);
4478 plen
= strlen(pstring
) + 1;
4479 if (length
+ plen
> 78) {
4480 fprintf(fp
, "\n+ "); /* SPICE line break & continuation */
4483 else length
+= plen
;
4484 fprintf(fp
, " %s", pstring
);
4491 /*----------------------------------------------------------------------*/
4492 /* Resolve device names (fill calllist structure member "devname") */
4493 /*----------------------------------------------------------------------*/
4495 void resolve_devnames(objectptr cschem
)
4501 for (calls
= cschem
->calls
; calls
!= NULL
; calls
= calls
->next
) {
4502 if (calls
->callobj
->traversed
== False
) {
4503 calls
->callobj
->traversed
= True
;
4504 resolve_devnames(calls
->callobj
);
4507 if (calls
->devname
== NULL
) {
4509 /* Check for "class" parameter. Note that although this */
4510 /* parameter may take a different instance value, the */
4511 /* device class is determined by the default value. */
4513 ops
= find_param(calls
->callinst
, "class");
4514 if (ops
&& (ops
->type
== XC_STRING
))
4515 calls
->devname
= textprint(ops
->parameter
.string
, NULL
);
4517 /* The slow way---parse info labels for any device information */
4518 if ((stmp
= parseinfo(cschem
, calls
->callinst
->thisobject
, calls
,
4519 NULL
, "", FALSE
, TRUE
)) != NULL
)
4526 /*----------------------------------------------------------------------*/
4527 /* Resolve (hierarchical) component numbering. If "autonumber" is TRUE */
4528 /* then this routine does auto-numbering. Otherwise, it auto-generates */
4529 /* device indices internally (in the "calllist" structure) but does not */
4530 /* copy them into parameter space. */
4532 /* Note: resolve_devindex() only operates on a single object. Use */
4533 /* resolve_indices() to operate on the entire hierarchy. */
4534 /*----------------------------------------------------------------------*/
4536 void resolve_devindex(objectptr cschem
, Boolean autonumber
)
4539 char *stmp
, *endptr
;
4540 char *idxtype
[] = {"index", "idx", NULL
};
4546 for (calls
= cschem
->calls
; calls
!= NULL
; calls
= calls
->next
) {
4548 /* Check if there is an "index" parameter set to "?" in */
4549 /* the object with no existing instance value. */
4551 for (j
= 0; idxtype
[j
] != NULL
; j
++)
4552 if ((ops
= match_param(calls
->callinst
->thisobject
, idxtype
[j
])) != NULL
)
4555 if (ops
&& (ops
->type
== XC_STRING
)) {
4556 if (!textcomp(ops
->parameter
.string
, "?", NULL
)) {
4557 cinst
= calls
->callinst
;
4558 ips
= match_instance_param(cinst
, idxtype
[j
]);
4559 if ((autonumber
== TRUE
) && (ips
== NULL
)) {
4560 copyparams(cinst
, cinst
);
4561 ops
= match_instance_param(cinst
, idxtype
[j
]);
4562 optr
= ops
->parameter
.string
;
4563 newindex
= devindex(cschem
, calls
);
4564 stmp
= d36a(newindex
);
4565 optr
->data
.string
= (char *)realloc(optr
->data
.string
, strlen(stmp
) + 1);
4566 sprintf(optr
->data
.string
, "%s", stmp
);
4568 else if (calls
->devindex
< 0) {
4570 optr
= ips
->parameter
.string
;
4571 if (optr
->type
!= TEXT_STRING
) {
4572 /* Not a simple string. Dump the text. */
4574 snew
= textprint(optr
, NULL
),
4575 newindex
= (u_int
) strtol(snew
, &endptr
, 36);
4579 newindex
= (u_int
) strtol(optr
->data
.string
, &endptr
, 36);
4581 if (*endptr
!= '\0') {
4582 /* It is possible to get an instance value that is */
4583 /* the same as the default, although this normally */
4584 /* doesn't happen. If it does, quietly remove the */
4585 /* unneeded instance value. */
4587 if (!stringcomp(ops
->parameter
.string
, ips
->parameter
.string
))
4588 resolveparams(cinst
);
4590 Fprintf(stderr
, "Warning: Use of non-alphanumeric"
4591 " characters in component \"%s%s\" (instance"
4592 " of %s)\n", (calls
->devname
) ? calls
->devname
4593 : calls
->callobj
->name
, optr
->data
.string
,
4594 calls
->callobj
->name
);
4597 calls
->devindex
= newindex
;
4599 else /* if (autonumber) */
4600 devindex(cschem
, calls
);
4605 /* The slow way---parse info labels for any index information */
4606 if ((stmp
= parseinfo(cschem
, calls
->callinst
->thisobject
, calls
,
4607 NULL
, "", autonumber
, TRUE
)) != NULL
)
4613 /*----------------------------------------------------------------------*/
4614 /* Recursive call to resolve_devindex() through the circuit hierarchy */
4615 /*----------------------------------------------------------------------*/
4617 void resolve_indices(objectptr cschem
, Boolean autonumber
) {
4620 for (calls
= cschem
->calls
; calls
!= NULL
; calls
= calls
->next
) {
4621 if (calls
->callobj
->traversed
== False
) {
4622 calls
->callobj
->traversed
= True
;
4623 resolve_indices(calls
->callobj
, autonumber
);
4626 resolve_devindex(cschem
, autonumber
);
4629 /*----------------------------------------------------------------------*/
4630 /* Recursive call to clear all generated device numbers. */
4631 /*----------------------------------------------------------------------*/
4633 void clear_indices(objectptr cschem
) {
4636 for (calls
= cschem
->calls
; calls
!= NULL
; calls
= calls
->next
) {
4637 if (calls
->callobj
->traversed
== False
) {
4638 calls
->callobj
->traversed
= True
;
4639 clear_indices(calls
->callobj
);
4641 calls
->devindex
= -1;
4645 /*----------------------------------------------------------------------*/
4646 /* Recursive call to clear all device indexes (parameter "index") */
4647 /*----------------------------------------------------------------------*/
4649 void unnumber(objectptr cschem
) {
4652 char *idxtype
[] = {"index", "idx", NULL
};
4655 for (calls
= cschem
->calls
; calls
!= NULL
; calls
= calls
->next
) {
4656 if (calls
->callobj
->traversed
== False
) {
4657 calls
->callobj
->traversed
= True
;
4658 unnumber(calls
->callobj
);
4661 for (j
= 0; idxtype
[j
] != NULL
; j
++)
4662 if ((ops
= match_param(calls
->callobj
, idxtype
[j
])) != NULL
) break;
4664 if (ops
&& (ops
->type
== XC_STRING
)) {
4665 if (!textcomp(ops
->parameter
.string
, "?", NULL
)) {
4666 ips
= match_instance_param(calls
->callinst
, idxtype
[j
]);
4668 free_instance_param(calls
->callinst
, ips
);
4674 /*----------------------------------------------------------------------*/
4675 /* Save netlist into a hierarchical file */
4676 /*----------------------------------------------------------------------*/
4678 void writehierarchy(objectptr cschem
, objinstptr thisinst
, CalllistPtr cfrom
,
4679 FILE *fp
, char *mode
)
4681 CalllistPtr calls
= cschem
->calls
;
4682 PortlistPtr ports
, plist
;
4683 int pnet
, portid
, length
, plen
, subnet
, modlen
; /* netid, (jdk) */
4684 char *locmode
= mode
, *stsave
= NULL
, *pstring
;
4688 if (cschem
->traversed
== True
) return;
4690 /* Set up local call list for parsing info labels in this schematic */
4691 loccalls
.cschem
= NULL
;
4692 loccalls
.callobj
= cschem
;
4693 loccalls
.callinst
= thisinst
;
4694 loccalls
.devindex
= -1;
4695 loccalls
.ports
= NULL
;
4696 loccalls
.next
= NULL
;
4698 modlen
= strlen(mode
);
4699 locmode
= malloc(2 + modlen
);
4700 strcpy(locmode
, mode
);
4701 locmode
[modlen
+ 1] = '\0';
4703 /* "<mode>@" lines go before any subcircuit calls */
4705 locmode
[modlen
] = '@';
4706 if (fp
!= NULL
) stsave
= parseinfo(NULL
, cschem
, &loccalls
, NULL
, locmode
, FALSE
,
4708 if (stsave
!= NULL
) {
4715 /* Subcircuits which make no calls or have no devices do not get written */
4717 if (calls
!= NULL
) {
4719 /* Make sure that all the subcircuits have been written first */
4721 for (; calls
!= NULL
; calls
= calls
->next
) {
4722 if (calls
->callobj
->traversed
== False
) {
4723 psubstitute(calls
->callinst
);
4724 writehierarchy(calls
->callobj
, calls
->callinst
, calls
, fp
, mode
);
4725 calls
->callobj
->traversed
= True
;
4728 if (cschem
->schemtype
== FUNDAMENTAL
) {
4734 /* Info-labels on a schematic (if any) get printed out first */
4736 if ((fp
!= NULL
) && (cschem
->calls
!= NULL
)) {
4737 stsave
= parseinfo(NULL
, cschem
, &loccalls
, NULL
, mode
, FALSE
, FALSE
);
4738 if (stsave
!= NULL
) {
4740 /* Check stsave for embedded SPICE subcircuit syntax */
4742 if (!strcmp(mode
, "spice"))
4743 if (strstr(stsave
, ".subckt ") == NULL
)
4744 writesubcircuit(fp
, cschem
);
4751 else if (cschem
->calls
!= NULL
)
4752 /* top-level schematics will be ignored because they have no ports */
4753 writesubcircuit(fp
, cschem
);
4756 /* Resolve all fixed part assignments (devindex) */
4757 resolve_devindex(cschem
, FALSE
);
4759 /* If the output file is NULL, then we're done */
4766 for (calls
= cschem
->calls
; calls
!= NULL
; calls
= calls
->next
) {
4768 /* writedevice() is just another call to parseinfo() */
4769 if (writedevice(fp
, mode
, cschem
, calls
, NULL
) < 0) {
4771 /* If the device did not produce any output in writedevice(), but */
4772 /* does not make any calls itself, then it is implicitly a TRIVIAL */
4775 if ((calls
->callobj
->schemtype
== TRIVIAL
) || (calls
->callobj
->calls
== NULL
))
4778 /* No syntax indicating how to write this call. Use SPICE "X" */
4779 /* format and arrange the parameters according to the structure. */
4781 calls
->devname
= strdup(spice_devname
);
4782 fprintf(fp
, "X%s", d36a(devindex(cschem
, calls
)));
4783 stsave
= calls
->callobj
->name
;
4786 /* The object's definition lists calls in the order of the object's */
4787 /* port list. Therefore, use this order to find the port list. */
4788 /* This will also deal with the fact that not every port has to be */
4789 /* called by the instance (ports may be left disconnected). */
4791 for (ports
= calls
->callobj
->ports
; ports
!= NULL
;
4792 ports
= ports
->next
) {
4793 portid
= ports
->portid
;
4794 for (plist
= calls
->ports
; plist
!= NULL
; plist
= plist
->next
)
4795 if (plist
->portid
== ports
->portid
)
4798 pnet
= (plist
== NULL
) ? netmax(cschem
) + 1 : plist
->netid
;
4799 subnet
= getsubnet(pnet
, cschem
);
4800 ppin
= nettopin(pnet
, cschem
, NULL
);
4801 pstring
= textprintsubnet(ppin
, NULL
, subnet
);
4802 plen
= strlen(pstring
) + 1;
4803 if (length
+ plen
> 78) {
4804 fprintf(fp
, "\n+ "); /* SPICE line continuation */
4807 else length
+= plen
;
4808 fprintf(fp
, " %s", pstring
);
4811 plen
= 1 + strlen(stsave
);
4812 if (length
+ plen
> 78) fprintf(fp
, "\n+ "); /* SPICE line cont. */
4813 fprintf(fp
, " %s\n", stsave
);
4817 /* Check for negative-numbered info labels, indicated output that */
4818 /* should be processed after everything else. If we're processing */
4819 /* SPICE output but the schematic does not provide a ".ends" */
4820 /* statement, then add it to the end of the deck. */
4822 if (cschem
->calls
!= NULL
) {
4823 locmode
[modlen
] = '-';
4824 stsave
= parseinfo(NULL
, cschem
, &loccalls
, NULL
, locmode
, FALSE
, FALSE
);
4825 if (stsave
!= NULL
) {
4829 if (!strcmp(mode
, "spice"))
4830 if (strstr(stsave
, ".ends") == NULL
)
4831 fprintf(fp
, ".ends\n");
4834 else if (cfrom
!= NULL
)
4835 fprintf(fp
, ".ends\n");
4843 /*----------------------------------------------------------------------*/
4844 /* Create generic netlist in the Tcl interpreter variable space */
4845 /*----------------------------------------------------------------------*/
4849 static Tcl_Obj
*tclparseinfo(objectptr cschem
)
4854 Tcl_Obj
*rlist
= Tcl_NewListObj(0, NULL
);
4856 for (pgen
= cschem
->plist
; pgen
< cschem
->plist
+ cschem
->parts
; pgen
++) {
4857 if (IS_LABEL(*pgen
)) {
4858 plabel
= TOLABEL(pgen
);
4859 if (plabel
->pin
== INFO
) {
4860 Tcl_ListObjAppendElement(xcinterp
, rlist
,
4861 TclGetStringParts(plabel
->string
));
4868 /*----------------------------------------------------------------------*/
4869 /* Write global variables to Tcl list (in key-value pairs which can be */
4870 /* turned into an array variable using the "array set" command) */
4872 /* Note: argument "cinst" is unused, but it really should be. We */
4873 /* should not assume that different schematics have the same list of */
4875 /*----------------------------------------------------------------------*/
4877 Tcl_Obj
*tclglobals(objinstptr cinst
)
4881 Tcl_Obj
*gdict
; /*, *netnum; (jdk) */
4885 gdict
= Tcl_NewListObj(0, NULL
);
4886 for (llist
= global_labels
; llist
!= NULL
; llist
= llist
->next
) {
4887 gpin
= llist
->label
;
4888 Tcl_ListObjAppendElement(xcinterp
, gdict
, TclGetStringParts(gpin
->string
));
4890 if (llist
->subnets
== 0) {
4891 netid
= llist
->net
.id
;
4894 sbus
= llist
->net
.list
+ lbus
;
4895 netid
= sbus
->netid
;
4897 Tcl_ListObjAppendElement(xcinterp
, gdict
, Tcl_NewIntObj(netid
));
4898 if (++lbus
>= llist
->subnets
) break;
4904 /*----------------------------------------------------------------------*/
4905 /* Write a generic hierarchical netlist into Tcl list "subcircuits" */
4906 /*----------------------------------------------------------------------*/
4908 void tclhierarchy(objectptr cschem
, objinstptr cinst
, CalllistPtr cfrom
, Tcl_Obj
*cktlist
)
4910 CalllistPtr calls
= cschem
->calls
;
4911 PortlistPtr ports
, plist
;
4912 int netid
, pnet
, portid
; /* , length, plen, i; (jdk) */
4913 Tcl_Obj
*tclports
, *tclcalls
, *tclnewcall
, *tclnets
, *netnum
;
4914 Tcl_Obj
*tcldevs
, *tclparams
, *subckt
, *newdev
, *tcllabel
, *portnum
;
4915 oparamptr paramlist
; /* , locparam; (jdk) */
4918 /* Trivial objects are by definition those that are supposed */
4919 /* to be resolved by the netlist generation prior to output */
4920 /* and ignored by the output generator. */
4922 if (cschem
->schemtype
== TRIVIAL
) return;
4924 /* Make sure that all the subcircuits have been written first */
4926 for (; calls
!= NULL
; calls
= calls
->next
) {
4927 if (calls
->callobj
->traversed
== False
) {
4928 tclhierarchy(calls
->callobj
, calls
->callinst
, calls
, cktlist
);
4929 calls
->callobj
->traversed
= True
;
4933 /* Write own subcircuit netlist */
4934 subckt
= Tcl_NewListObj(0, NULL
);
4936 /* Prepare the list of network cross-references */
4937 tclnets
= Tcl_NewListObj(0, NULL
);
4939 /* Make list of the nets which have been written so we don't redundantly */
4940 /* list any entries (use of calloc() initializes all entries to FALSE). */
4941 /* (calloc() replaced by malloc() and bzero() because Tcl doesn't */
4942 /* have a calloc() function (2/6/04)) */
4944 netsdone
= (char *)malloc(2 + netmax(cschem
));
4946 memset((void *)netsdone
, 0, 2 + netmax(cschem
));
4948 bzero((void *)netsdone
, 2 + netmax(cschem
));
4951 /* Write the name (key value pair) */
4952 Tcl_ListObjAppendElement(xcinterp
, subckt
, Tcl_NewStringObj("name", 4));
4953 Tcl_ListObjAppendElement(xcinterp
, subckt
, Tcl_NewStringObj(cschem
->name
,
4954 strlen(cschem
->name
)));
4956 /* Write the handle of the object instance (key value pair) */
4957 Tcl_ListObjAppendElement(xcinterp
, subckt
, Tcl_NewStringObj("handle", 6));
4958 Tcl_ListObjAppendElement(xcinterp
, subckt
, Tcl_NewHandleObj(cinst
));
4960 /* Write the list of ports */
4961 if ((ports
= cschem
->ports
) != NULL
) {
4963 /* List of ports in subcircuit. */
4964 /* Each parameter connects to a net which may have multiple */
4965 /* names, so find the net associated with the parameter */
4966 /* and convert it back into a pin name in the usual manner */
4967 /* using nettopin(). */
4969 tclports
= Tcl_NewListObj(0, NULL
);
4970 for (; ports
!= NULL
; ports
= ports
->next
) {
4971 netid
= ports
->netid
;
4972 portid
= ports
->portid
;
4973 netnum
= Tcl_NewIntObj(netid
);
4974 portnum
= Tcl_NewIntObj(portid
);
4975 Tcl_ListObjAppendElement(xcinterp
, tclports
, portnum
);
4976 Tcl_ListObjAppendElement(xcinterp
, tclports
, netnum
);
4977 if ((netid
>= 0) && (netsdone
[netid
] == (char)0))
4979 Tcl_ListObjAppendElement(xcinterp
, tclnets
, netnum
);
4980 Tcl_ListObjAppendElement(xcinterp
, tclnets
,
4981 TclGetStringParts(nettopin(netid
, cschem
, NULL
)));
4982 /* record this net as having been added to the list */
4983 netsdone
[netid
] = (char)1;
4986 Tcl_ListObjAppendElement(xcinterp
, subckt
, Tcl_NewStringObj("ports", 5));
4987 Tcl_ListObjAppendElement(xcinterp
, subckt
, tclports
);
4990 /* Write the list of parameter defaults (key:value pairs) */
4992 if (cschem
->params
!= NULL
) {
4993 tclparams
= Tcl_NewListObj(0, NULL
);
4995 for (paramlist
= cschem
->params
; paramlist
!= NULL
; paramlist
= paramlist
->next
) {
4996 Tcl_ListObjAppendElement(xcinterp
, tclparams
,
4997 Tcl_NewStringObj(paramlist
->key
, strlen(paramlist
->key
)));
4998 switch(paramlist
->type
) {
5000 Tcl_ListObjAppendElement(xcinterp
, tclparams
,
5001 Tcl_NewIntObj((int)paramlist
->parameter
.ivalue
));
5004 Tcl_ListObjAppendElement(xcinterp
, tclparams
,
5005 Tcl_NewDoubleObj((double)paramlist
->parameter
.fvalue
));
5008 tcllabel
= TclGetStringParts(paramlist
->parameter
.string
);
5009 /* Should get rid of last entry, "End Parameter"; not needed */
5010 Tcl_ListObjAppendElement(xcinterp
, tclparams
, tcllabel
);
5013 Tcl_ListObjAppendElement(xcinterp
, tclparams
,
5014 evaluate_raw(cschem
, paramlist
, cinst
, NULL
));
5018 Tcl_ListObjAppendElement(xcinterp
, subckt
, Tcl_NewStringObj("parameters", 10));
5019 Tcl_ListObjAppendElement(xcinterp
, subckt
, tclparams
);
5022 /* Write the list of calls to subcircuits */
5024 if ((calls
= cschem
->calls
) != NULL
) {
5025 tclcalls
= Tcl_NewListObj(0, NULL
);
5026 for (; calls
!= NULL
; calls
= calls
->next
) {
5028 /* Don't write calls to non-functional subcircuits. */
5029 if (calls
->callobj
->schemtype
== TRIVIAL
) continue;
5031 tclnewcall
= Tcl_NewListObj(0, NULL
);
5032 Tcl_ListObjAppendElement(xcinterp
, tclnewcall
, Tcl_NewStringObj("name", 4));
5033 Tcl_ListObjAppendElement(xcinterp
, tclnewcall
,
5034 Tcl_NewStringObj(calls
->callobj
->name
,
5035 strlen(calls
->callobj
->name
)));
5038 /* Log any local parameter instances */
5039 if (calls
->callinst
->params
!= NULL
) {
5040 tclparams
= Tcl_NewListObj(0, NULL
);
5042 for (paramlist
= calls
->callinst
->params
; paramlist
!= NULL
;
5043 paramlist
= paramlist
->next
) {
5044 Tcl_ListObjAppendElement(xcinterp
, tclparams
,
5045 Tcl_NewStringObj(paramlist
->key
, strlen(paramlist
->key
)));
5046 switch(paramlist
->type
) {
5048 Tcl_ListObjAppendElement(xcinterp
, tclparams
,
5049 Tcl_NewIntObj((int)paramlist
->parameter
.ivalue
));
5052 Tcl_ListObjAppendElement(xcinterp
, tclparams
,
5053 Tcl_NewDoubleObj((double)paramlist
->parameter
.fvalue
));
5056 Tcl_ListObjAppendElement(xcinterp
, tclparams
,
5057 TclGetStringParts(paramlist
->parameter
.string
));
5060 Tcl_ListObjAppendElement(xcinterp
, tclparams
,
5061 evaluate_raw(cschem
, paramlist
, cinst
, NULL
));
5065 Tcl_ListObjAppendElement(xcinterp
, tclnewcall
,
5066 Tcl_NewStringObj("parameters", 10));
5067 Tcl_ListObjAppendElement(xcinterp
, tclnewcall
, tclparams
);
5070 /* Called ports are listed by key:value pair (port number: net id) */
5072 if (calls
->callobj
->ports
!= NULL
) {
5073 tclports
= Tcl_NewListObj(0, NULL
);
5074 for (ports
= calls
->callobj
->ports
; ports
!= NULL
;
5075 ports
= ports
->next
) {
5076 portid
= ports
->portid
;
5077 for (plist
= calls
->ports
; plist
!= NULL
; plist
= plist
->next
)
5078 if (plist
->portid
== ports
->portid
)
5081 pnet
= (plist
== NULL
) ? netmax(cschem
) + 1 : plist
->netid
;
5083 netnum
= Tcl_NewIntObj((int)pnet
);
5084 portnum
= Tcl_NewIntObj(portid
);
5085 Tcl_ListObjAppendElement(xcinterp
, tclports
, portnum
);
5086 Tcl_ListObjAppendElement(xcinterp
, tclports
, netnum
);
5087 if ((pnet
>= 0) && (netsdone
[pnet
] == (char)0))
5089 Tcl_ListObjAppendElement(xcinterp
, tclnets
, netnum
);
5090 Tcl_ListObjAppendElement(xcinterp
, tclnets
,
5091 TclGetStringParts(nettopin(pnet
, cschem
, NULL
)));
5092 /* record this net as having been added to the list */
5093 netsdone
[pnet
] = (char)1;
5096 Tcl_ListObjAppendElement(xcinterp
, tclnewcall
,
5097 Tcl_NewStringObj("ports", 5));
5098 Tcl_ListObjAppendElement(xcinterp
, tclnewcall
, tclports
);
5100 Tcl_ListObjAppendElement(xcinterp
, tclcalls
, tclnewcall
);
5102 Tcl_ListObjAppendElement(xcinterp
, subckt
, Tcl_NewStringObj("calls", 5));
5103 Tcl_ListObjAppendElement(xcinterp
, subckt
, tclcalls
);
5107 /* If the object has info labels, write a device list. */
5108 /* Check both the schematic and its symbol for info labels. */
5110 tcldevs
= Tcl_NewListObj(0, NULL
);
5111 if (cschem
->symschem
!= NULL
) {
5112 newdev
= tclparseinfo(cschem
->symschem
);
5113 Tcl_ListObjAppendElement(xcinterp
, tcldevs
, newdev
);
5115 newdev
= tclparseinfo(cschem
);
5116 Tcl_ListObjAppendElement(xcinterp
, tcldevs
, newdev
);
5117 Tcl_ListObjAppendElement(xcinterp
, subckt
, Tcl_NewStringObj("devices", 7));
5118 Tcl_ListObjAppendElement(xcinterp
, subckt
, tcldevs
);
5120 /* Write the network cross-reference dictionary */
5121 Tcl_ListObjAppendElement(xcinterp
, subckt
, Tcl_NewStringObj("nets", 4));
5122 Tcl_ListObjAppendElement(xcinterp
, subckt
, tclnets
);
5124 Tcl_ListObjAppendElement(xcinterp
, cktlist
, subckt
);
5127 /*----------------------------------------------------------------------*/
5129 Tcl_Obj
*tcltoplevel(objinstptr cinst
)
5132 objectptr cschem
= cinst
->thisobject
;
5134 cktlist
= Tcl_NewListObj(0, NULL
);
5135 cleartraversed(cschem
);
5136 tclhierarchy(cschem
, cinst
, NULL
, cktlist
);
5140 #endif /* TCL_WRAPPER */
5142 /*----------------------------------------------------------------------*/
5143 /* Create generic netlist in the Python interpreter variable space */
5144 /*----------------------------------------------------------------------*/
5148 static PyObject
*pyparseinfo(objectptr cschem
)
5153 PyObject
*rlist
= PyList_New(0);
5155 for (pgen
= cschem
->plist
; pgen
< cschem
->plist
+ cschem
->parts
; pgen
++) {
5156 if (IS_LABEL(*pgen
)) {
5157 plabel
= TOLABEL(pgen
);
5158 if (plabel
->pin
== INFO
)
5159 PyList_Append(rlist
, PyGetStringParts(plabel
->string
));
5165 /*----------------------------------------------------------------------*/
5166 /* Write global variables to Python dictionary */
5167 /*----------------------------------------------------------------------*/
5169 PyObject
*pyglobals(objectptr cschem
)
5173 PyObject
*gdict
, *netnum
;
5177 gdict
= PyDict_New();
5178 for (llist
= global_labels
; llist
!= NULL
; llist
= llist
->next
) {
5179 gpin
= llist
->label
;
5181 if (llist
->subnets
== 0)
5182 netid
= llist
->net
.id
;
5184 sbus
= llist
->net
.list
+ lbus
;
5185 netid
= sbus
->netid
;
5187 netnum
= PyInt_FromLong((long)(netid
));
5188 PyDict_SetItem(gdict
, netnum
, PyGetStringParts(gpin
->string
));
5189 if (++lbus
>= llist
->subnets
) break;
5195 /*----------------------------------------------------------------------*/
5196 /* Write a generic hierarchical netlist into Python list "subcircuits" */
5197 /*----------------------------------------------------------------------*/
5199 void pyhierarchy(objectptr cschem
, CalllistPtr cfrom
, PyObject
*cktlist
)
5201 CalllistPtr calls
= cschem
->calls
;
5202 PortlistPtr ports
, plist
;
5203 int netid
, pnet
, portid
, length
, plen
, i
;
5204 PyObject
*pyports
, *pycalls
, *pynewcall
, *pynets
, *netnum
;
5205 PyObject
*pydevs
, *pyparams
, *subckt
, *newdev
, *pylabel
;
5206 oparamptr paramlist
;
5208 /* Trivial objects are by definition those that are supposed */
5209 /* to be resolved by the netlist generation prior to output */
5210 /* and ignored by the output generator. */
5212 if (cschem
->schemtype
== TRIVIAL
) return;
5214 /* Make sure that all the subcircuits have been written first */
5216 for (; calls
!= NULL
; calls
= calls
->next
) {
5217 if (calls
->callobj
->traversed
== False
) {
5218 calls
->callobj
->traversed
= True
;
5219 pyhierarchy(calls
->callobj
, calls
, cktlist
);
5223 /* Write own subcircuit netlist */
5224 subckt
= PyDict_New();
5226 /* Prepare the dictionary of network cross-references */
5227 pynets
= PyDict_New();
5229 /* Write the name */
5230 PyDict_SetItem(subckt
, PyString_FromString("name"),
5231 PyString_FromString(cschem
->name
));
5233 /* Write the list of ports */
5234 if ((ports
= cschem
->ports
) != NULL
) {
5236 /* List of ports in subcircuit. */
5237 /* Each parameter connects to a net which may have multiple */
5238 /* names, so find the net associated with the parameter */
5239 /* and convert it back into a pin name in the usual manner */
5240 /* using nettopin(). */
5242 pyports
= PyList_New(0);
5243 for (; ports
!= NULL
; ports
= ports
->next
) {
5244 netid
= ports
->netid
;
5245 netnum
= PyInt_FromLong((long)netid
);
5246 PyList_Append(pyports
, netnum
);
5248 PyDict_SetItem(pynets
, netnum
,
5249 PyGetStringParts(nettopin(netid
, cschem
, NULL
)));
5251 PyDict_SetItem(subckt
, PyString_FromString("ports"), pyports
);
5254 /* Write the list of parameters */
5256 if (cschem
->params
!= NULL
) {
5257 pyparams
= PyList_New(0);
5259 for (paramlist
= cschem
->params
; paramlist
!= NULL
; paramlist
= paramlist
->next
) {
5260 if (paramlist
->type
== XC_INT
)
5261 PyList_Append(pyparams
,
5262 PyInt_FromLong((long)paramlist
->parameter
.ivalue
));
5263 if (paramlist
->type
== XC_FLOAT
)
5264 PyList_Append(pyparams
,
5265 PyFloat_FromDouble((double)paramlist
->parameter
.fvalue
));
5266 else if (paramlist
->type
== XC_STRING
) {
5267 pylabel
= PyGetStringParts(paramlist
->parameter
.string
);
5268 /* Should get rid of last entry, "End Parameter"; not needed */
5269 PyList_Append(pyparams
, pylabel
);
5272 PyDict_SetItem(subckt
, PyString_FromString("parameters"), pyparams
);
5275 /* Write the list of calls to subcircuits */
5277 if ((calls
= cschem
->calls
) != NULL
) {
5278 pycalls
= PyList_New(0);
5279 for (; calls
!= NULL
; calls
= calls
->next
) {
5281 /* Don't write calls to non-functional subcircuits. */
5282 if (calls
->callobj
->schemtype
== TRIVIAL
) continue;
5284 pynewcall
= PyDict_New();
5285 PyDict_SetItem(pynewcall
, PyString_FromString("name"),
5286 PyString_FromString(calls
->callobj
->name
));
5288 /* Log any local parameter instances */
5289 if (calls
->callinst
->params
!= NULL
) {
5290 pyparams
= PyList_New(0);
5292 for (paramlist
= cschem
->params
; paramlist
!= NULL
;
5293 paramlist
= paramlist
->next
) {
5294 if (paramlist
->type
== XC_INT
)
5295 PyList_Append(pyparams
,
5296 PyInt_FromLong((long)paramlist
->parameter
.ivalue
));
5297 else if (paramlist
->type
== XC_FLOAT
)
5298 PyList_Append(pyparams
,
5299 PyFloat_FromDouble((double)paramlist
->parameter
.fvalue
));
5300 else if (paramlist
->type
== XC_STRING
)
5301 PyList_Append(pyparams
,
5302 PyGetStringParts(paramlist
->parameter
.string
));
5304 PyDict_SetItem(pynewcall
, PyString_FromString("parameters"),
5308 /* The object's definition lists calls in the order of the object's */
5309 /* port list. Therefore, use this order to find the port list. */
5310 /* Unconnected ports will be NULL entries in the list (?). */
5312 if (calls
->callobj
->ports
!= NULL
) {
5313 pyports
= PyList_New(0);
5314 for (ports
= calls
->callobj
->ports
; ports
!= NULL
;
5315 ports
= ports
->next
) {
5316 portid
= ports
->portid
;
5317 for (plist
= calls
->ports
; plist
!= NULL
; plist
= plist
->next
)
5318 if (plist
->portid
== ports
->portid
)
5321 pnet
= (plist
== NULL
) ? netmax(cschem
) + 1 : plist
->netid
;
5323 netnum
= PyInt_FromLong((long)pnet
);
5324 PyList_Append(pyports
, netnum
);
5326 PyDict_SetItem(pynets
, netnum
,
5327 PyGetStringParts(nettopin(pnet
, cschem
, NULL
)));
5329 PyDict_SetItem(pynewcall
, PyString_FromString("ports"), pyports
);
5331 PyList_Append(pycalls
, pynewcall
);
5333 PyDict_SetItem(subckt
, PyString_FromString("calls"), pycalls
);
5336 /* If the object has info labels, write a device list. */
5337 /* Check both the schematic and its symbol for info labels. */
5339 pydevs
= PyList_New(0);
5340 if (cschem
->symschem
!= NULL
) {
5341 newdev
= pyparseinfo(cschem
->symschem
);
5342 PyList_Append(pydevs
, newdev
);
5344 newdev
= pyparseinfo(cschem
);
5345 PyList_Append(pydevs
, newdev
);
5346 PyDict_SetItem(subckt
, PyString_FromString("devices"), pydevs
);
5348 /* Write the network cross-reference dictionary */
5349 PyDict_SetItem(subckt
, PyString_FromString("nets"), pynets
);
5351 PyList_Append(cktlist
, subckt
);
5354 /*----------------------------------------------------------------------*/
5356 PyObject
*pytoplevel(objectptr cschem
)
5360 cktlist
= PyList_New(0);
5361 cleartraversed(cschem
);
5362 pyhierarchy(cschem
, NULL
, cktlist
);
5366 #endif /* HAVE_PYTHON */
5368 /*----------------------------------------------------------------------*/
5369 /* Update networks in an object by checking if the network is valid, */
5370 /* and destroying and recreating the network if necessary. */
5372 /* Run cleartraversed() on all types. The traversed flag allows a */
5373 /* check for infinite recursion when creating calls. */
5375 /* Returns -1 on discovery of infinite recursion, 0 on failure to */
5376 /* generate a net, and 1 on success. */
5377 /*----------------------------------------------------------------------*/
5379 int updatenets(objinstptr uinst
, Boolean quiet
) {
5380 objectptr thisobject
;
5381 objinstptr thisinst
;
5384 /* Never try to generate a netlist while a file is being read, as */
5385 /* can happen if an expression parameter attempts to query a netlist */
5388 if (load_in_progress
) return 0;
5390 if (uinst
->thisobject
->symschem
!= NULL
5391 && uinst
->thisobject
->schemtype
!= PRIMARY
) {
5392 thisobject
= uinst
->thisobject
->symschem
;
5393 if ((spage
= is_page(thisobject
)) >= 0)
5394 thisinst
= xobjs
.pagelist
[spage
]->pageinst
;
5397 thisobject
= uinst
->thisobject
;
5401 if (checkvalid(thisobject
) == -1) {
5404 if (cleartraversed(thisobject
) == -1) {
5405 Wprintf("Netlist error: Check for recursion in circuit!");
5409 /* Note: destroy/create nets messes up the part list. Why?? */
5411 if (areawin
->selects
> 0)
5412 ssave
= remember_selection(areawin
->topinstance
, areawin
->selectlist
,
5414 destroynets(thisobject
);
5415 createnets(thisinst
, quiet
);
5416 if (areawin
->selects
> 0) {
5417 areawin
->selectlist
= regen_selection(areawin
->topinstance
, ssave
);
5418 free_selection(ssave
);
5422 if (thisobject
->labels
== NULL
&& thisobject
->polygons
== NULL
) {
5424 Wprintf("Netlist error: No netlist elements in object %s",
5431 /*----------------------------------------------------------------------*/
5432 /* Generate netlist, write information according to mode, and then */
5433 /* destroy the netlist (this will be replaced eventually with a dynamic */
5434 /* netlist model in which the netlist changes according to editing of */
5435 /* individual elements, not created and destroyed wholesale) */
5436 /*----------------------------------------------------------------------*/
5438 void writenet(objectptr thisobject
, char *mode
, char *suffix
)
5441 objinstptr thisinst
;
5443 char *prefix
, *cpos
, *locmode
= mode
, *stsave
= NULL
;
5445 /* Calllist topcell; (jdk) */
5446 Boolean is_spice
= FALSE
, sp_end_save
= spice_end
;
5448 /* Always use the master schematic, if there is one. */
5450 if (thisobject
->schemtype
== SECONDARY
)
5451 cschem
= thisobject
->symschem
;
5453 cschem
= thisobject
;
5455 /* Update the netlist if this has not been done */
5457 if (NameToPageObject(cschem
->name
, &thisinst
, NULL
) == NULL
) {
5458 Wprintf("Not a schematic. . . cannot generate output!\n");
5461 if (updatenets(thisinst
, FALSE
) <= 0) {
5462 Wprintf("No file written!");
5466 prefix
= (char *)malloc(sizeof(char));
5469 if ((cpos
= strchr(cschem
->name
, ':')) != NULL
) *cpos
= '\0';
5470 sprintf(filename
, "%s.%s", cschem
->name
, suffix
);
5471 if (cpos
!= NULL
) *cpos
= ':';
5473 if (!strncmp(mode
, "index", 5)) { /* This mode generates no output */
5477 else if ((fp
= fopen(filename
, "w")) == NULL
) {
5478 Wprintf("Could not open file %s for writing.", filename
);
5483 /* Clear device indices from any previous netlist output */
5484 cleartraversed(cschem
);
5485 clear_indices(cschem
);
5487 /* Make sure list of include-once files is empty */
5491 /* Handle different netlist modes */
5493 if (!strcmp(mode
, "spice")) {
5495 if (thisobject
->schemtype
== SYMBOL
)
5496 cschem
= thisobject
->symschem
;
5499 fprintf(fp
, "*SPICE %scircuit <%s> from XCircuit v%s rev %s\n\n",
5500 (thisobject
->schemtype
== SYMBOL
) ? "sub" : "",
5501 cschem
->name
, PROG_VERSION
, PROG_REVISION
);
5503 /* writeglobals(cschem, fp); */
5504 cleartraversed(cschem
);
5505 writehierarchy(cschem
, thisinst
, NULL
, fp
, mode
);
5507 else if (!strcmp(mode
, "flatspice")) {
5509 fprintf(fp
, "*SPICE (flattened) circuit \"%s\" from XCircuit v%s rev %s\n\n",
5510 cschem
->name
, PROG_VERSION
, PROG_REVISION
);
5511 if (stsave
!= NULL
) {
5515 topflat(cschem
, thisinst
, NULL
, prefix
, fp
, mode
);
5517 else if (!strcmp(mode
, "pseuspice")) {
5519 fprintf(fp
, "*SPICE subcircuit \"%s\" from XCircuit v%s rev %s\n\n",
5520 cschem
->name
, PROG_VERSION
, PROG_REVISION
);
5521 if (stsave
!= NULL
) {
5525 writeflat(cschem
, NULL
, prefix
, fp
, mode
);
5528 else if (!strcmp(mode
, "flatsim") || !strcmp(mode
, "pseusim")) {
5529 fprintf(fp
, "| sim circuit \"%s\" from XCircuit v%s rev %s\n",
5530 cschem
->name
, PROG_VERSION
, PROG_REVISION
);
5531 if (stsave
!= NULL
) {
5535 topflat(cschem
, thisinst
, NULL
, prefix
, fp
, mode
);
5537 else if (!strcmp(locmode
, "pcb")) {
5538 struct Ptab
*ptable
= (struct Ptab
*)NULL
;
5539 writepcb(&ptable
, cschem
, NULL
, "", mode
);
5540 if (stsave
!= NULL
) {
5544 outputpcb(ptable
, fp
);
5547 else if (!strncmp(mode
, "flat", 4)) {
5548 /* User-defined modes beginning with the word "flat": */
5549 /* Assume a flattened netlist, and assume nothing else. */
5551 if (thisobject
->schemtype
== SYMBOL
)
5552 cschem
= thisobject
->symschem
;
5553 cleartraversed(cschem
);
5554 writeflat(cschem
, NULL
, prefix
, fp
, mode
);
5557 else if (!strncmp(mode
, "pseu", 4)) {
5558 /* User-defined modes beginning with the word "pseu": */
5559 /* Assume a flattened netlist for everything under the top level. */
5561 if (thisobject
->schemtype
== SYMBOL
)
5562 cschem
= thisobject
->symschem
;
5563 cleartraversed(cschem
);
5564 topflat(cschem
, thisinst
, NULL
, prefix
, fp
, mode
);
5567 /* All user-defined modes: Assume a hierarchical netlist, and */
5568 /* assume nothing else. */
5570 if (thisobject
->schemtype
== SYMBOL
)
5571 cschem
= thisobject
->symschem
;
5572 cleartraversed(cschem
);
5573 writehierarchy(cschem
, thisinst
, NULL
, fp
, mode
);
5576 /* Finish up SPICE files with a ".end" statement (if requested) */
5577 if (is_spice
&& (spice_end
== True
)) fprintf(fp
, ".end\n");
5578 spice_end
= sp_end_save
;
5584 Wprintf("%s netlist saved as %s", mode
, filename
);
5586 if (stsave
!= NULL
) free(stsave
);
5590 /*----------------------------------------------------------------------*/
5591 /* Flatten netlist and save into a table of pcb-style nets */
5592 /* The routine is recursive. Each call returns TRUE if some nested */
5593 /* call generated output; this is a reasonable way to tell if we have */
5594 /* reached the bottom of the hierarchy. */
5595 /*----------------------------------------------------------------------*/
5597 Boolean
writepcb(struct Ptab
**ptableptr
, objectptr cschem
, CalllistPtr cfrom
,
5598 char *prefix
, char *mode
)
5604 int i
, testnet
, tmplen
, subnet
;
5605 char *newprefix
= (char *)malloc(sizeof(char));
5607 struct Ptab
*hidx
, *htmp
;
5608 struct Pstr
*tmpstr
;
5609 struct Pnet
*tmpnet
;
5613 Boolean outputdone
= FALSE
;
5616 /* Step 1A: Go through the polygons of this object and add */
5617 /* any unvisited nets to the table. */
5619 for (plist
= cschem
->polygons
; plist
!= NULL
; plist
= plist
->next
) {
5621 if (plist
->subnets
== 0)
5622 testnet
= plist
->net
.id
;
5624 sbus
= plist
->net
.list
+ lbus
;
5625 testnet
= sbus
->netid
;
5629 while (hidx
!= NULL
) {
5630 if (hidx
->nets
!= NULL
) {
5631 for (i
= 0; i
< hidx
->nets
->numnets
; i
++)
5632 if (*(hidx
->nets
->netidx
+ i
) == testnet
) break;
5633 if (i
< hidx
->nets
->numnets
) break;
5637 if (hidx
== NULL
) { /* make new entry for net in table */
5638 htmp
= (struct Ptab
*)malloc(sizeof(struct Ptab
));
5639 tmpnet
= (struct Pnet
*)malloc(sizeof(struct Pnet
));
5640 tmpnet
->numnets
= 1;
5641 tmpnet
->netidx
= (int *)malloc(sizeof(int));
5642 *(tmpnet
->netidx
) = testnet
;
5643 tmpnet
->next
= NULL
;
5644 htmp
->cschem
= cschem
;
5645 htmp
->nets
= tmpnet
;
5647 htmp
->next
= *ptableptr
;
5649 /* Fprintf(stdout, "Added new index entry for net %d\n", testnet); */
5651 if (++lbus
>= plist
->subnets
) break;
5655 /* Step 1B: Do the same thing for labels. */
5657 for (llist
= cschem
->labels
; llist
!= NULL
; llist
= llist
->next
) {
5659 if (llist
->subnets
== 0)
5660 testnet
= llist
->net
.id
;
5662 sbus
= llist
->net
.list
+ lbus
;
5663 testnet
= sbus
->netid
;
5667 while (hidx
!= NULL
) {
5668 if (hidx
->nets
!= NULL
) {
5669 for (i
= 0; i
< hidx
->nets
->numnets
; i
++)
5670 if (*(hidx
->nets
->netidx
+ i
) == testnet
) break;
5671 if (i
< hidx
->nets
->numnets
) break;
5675 if (hidx
== NULL
) { /* make new entry for net in table */
5676 htmp
= (struct Ptab
*)malloc(sizeof(struct Ptab
));
5677 tmpnet
= (struct Pnet
*)malloc(sizeof(struct Pnet
));
5678 tmpnet
->numnets
= 1;
5679 tmpnet
->netidx
= (int *)malloc(sizeof(int));
5680 *(tmpnet
->netidx
) = testnet
;
5681 tmpnet
->next
= NULL
;
5682 htmp
->cschem
= cschem
;
5683 htmp
->nets
= tmpnet
;
5685 htmp
->next
= *ptableptr
;
5687 /* Fprintf(stdout, "Added new index entry for net %d\n", testnet); */
5689 if (++lbus
>= llist
->subnets
) break;
5693 /* Step 2: Resolve fixed device indices */
5694 resolve_devindex(cschem
, FALSE
);
5696 /* Step 3: Go through the list of calls to search for endpoints */
5698 for (calls
= cschem
->calls
; calls
!= NULL
; calls
= calls
->next
) {
5701 cinst
= calls
->callinst
;
5703 /* Trivial objects should have been dealt with already. */
5704 /* If we don't continue the loop here, you get netlist output for */
5705 /* objects like dots and circles. */
5706 if (calls
->callobj
->schemtype
== TRIVIAL
) continue;
5708 /* Step 4: If call is to a bottom-most schematic, output the device connections */
5709 /* Info-label can provide an alternate name or specify the instance number */
5711 cthis
= calls
->callobj
;
5712 if (calls
->callobj
->schemtype
== PRIMARY
|| calls
->callobj
->schemtype
==
5714 if (calls
->callobj
->symschem
!= NULL
)
5715 cthis
= calls
->callobj
->symschem
;
5717 if ((sout
= parseinfo(cschem
, cthis
, calls
, prefix
, mode
, FALSE
, TRUE
)) == NULL
) {
5718 if (calls
->devname
== NULL
)
5719 calls
->devname
= strdup(calls
->callinst
->thisobject
->name
);
5720 sprintf(_STR
, "%s_%s", calls
->devname
, d36a(devindex(cschem
, calls
)));
5723 sscanf(sout
, "%s", _STR
); /* Copy the first word out of sout */
5725 newprefix
= (char *)realloc(newprefix
, sizeof(char) * (strlen(prefix
)
5726 + strlen(_STR
) + 2));
5727 sprintf(newprefix
, "%s%s/", prefix
, _STR
);
5729 /*----------------------------------------------------------------*/
5730 /* Parsing of the "pcb:" info label--- */
5731 /* Find all <net>=<name> strings and create the appropriate nets */
5732 /*----------------------------------------------------------------*/
5735 char rsave
, *lhs
, *rhs
, *rend
, *sptr
= sout
, *tmppinname
;
5737 while ((rhs
= strchr(sptr
, '=')) != NULL
) {
5738 Genericlist
*implicit
, newlist
;
5739 struct Pstr
*psrch
= NULL
;
5742 while ((lhs
>= sptr
) && isspace(*lhs
)) lhs
--;
5743 while ((lhs
>= sptr
) && !isspace(*lhs
)) lhs
--;
5746 if (isspace(*lhs
)) lhs
++;
5747 while ((*rhs
) && isspace(*rhs
)) rhs
++;
5748 for (rend
= rhs
; (*rend
) && (!isspace(*rend
)); rend
++);
5752 /* Get the net from the equation RHS. If none exists, assume */
5753 /* the name is a global net and add it to the list of globals. */
5755 implicit
= nametonet(cschem
, cinst
, rhs
);
5756 if (implicit
== NULL
) {
5760 labeldefaults(&templabel
, GLOBAL
, 0, 0);
5761 strptr
= templabel
.string
;
5762 strptr
->type
= TEXT_STRING
;
5763 strptr
->data
.string
= strdup(rhs
);
5764 newlist
.subnets
= 0;
5765 newlist
.net
.id
= globalmax() - 1;
5766 addglobalpin(cschem
, cinst
, &templabel
, &newlist
);
5767 implicit
= &newlist
;
5768 free(strptr
->data
.string
);
5771 /* Get the name of the pin */
5772 tmppinname
= (char *)malloc(strlen(newprefix
) + strlen(lhs
) + 2);
5773 strcpy(tmppinname
, newprefix
);
5774 if ((tmplen
= strlen(newprefix
)) > 0) tmppinname
[tmplen
- 1] = '-';
5775 strcat(tmppinname
, lhs
);
5777 /* Find the net in the ptable, or create a new entry for it */
5780 while (hidx
!= NULL
) {
5781 if (hidx
->nets
!= NULL
) {
5782 for (i
= 0; i
< hidx
->nets
->numnets
; i
++)
5783 if (*(hidx
->nets
->netidx
+ i
) == implicit
->net
.id
)
5785 if (i
< hidx
->nets
->numnets
) break;
5790 htmp
= (struct Ptab
*)malloc(sizeof(struct Ptab
));
5791 tmpnet
= (struct Pnet
*)malloc(sizeof(struct Pnet
));
5792 tmpnet
->numnets
= 1;
5793 tmpnet
->netidx
= (int *)malloc(sizeof(int));
5794 *(tmpnet
->netidx
) = implicit
->net
.id
;
5795 tmpnet
->next
= NULL
;
5796 htmp
->cschem
= cschem
;
5797 htmp
->nets
= tmpnet
;
5799 htmp
->next
= *ptableptr
;
5804 /* Check if any entries are the same as tmppinname */
5805 for (psrch
= hidx
->pins
; psrch
!= NULL
; psrch
= psrch
->next
) {
5806 if (!strcmp(psrch
->string
->data
.string
, tmppinname
))
5811 /* Get the pin name from the equation LHS */
5812 if (psrch
== NULL
) {
5813 tmpstr
= (struct Pstr
*)malloc(sizeof(struct Pstr
));
5814 tmpstr
->string
= (stringpart
*)malloc(sizeof(stringpart
));
5815 tmpstr
->string
->type
= TEXT_STRING
;
5816 tmpstr
->string
->nextpart
= NULL
;
5817 tmpstr
->string
->data
.string
= tmppinname
;
5818 tmpstr
->next
= hidx
->pins
;
5819 hidx
->pins
= tmpstr
;
5825 /* Proceed to the next LHS=RHS pair */
5833 if (calls
->callobj
->calls
!= NULL
) {
5835 /* Step 4A: Push current net translations */
5836 /* (Don't push or pop global net numbers: no translation needed!) */
5839 while (hidx
!= NULL
) {
5840 if ((hidx
->nets
!= NULL
) && (((hidx
->nets
->numnets
> 0) &&
5841 (*(hidx
->nets
->netidx
) >= 0)) || (hidx
->nets
->numnets
== 0))) {
5842 tmpnet
= (struct Pnet
*)malloc(sizeof(struct Pnet
));
5843 tmpnet
->numnets
= 0;
5844 tmpnet
->netidx
= NULL
;
5845 tmpnet
->next
= hidx
->nets
;
5846 hidx
->nets
= tmpnet
;
5851 /* Step 4B: Generate net translation table for each subcircuit */
5853 for (ports
= calls
->ports
; ports
!= NULL
; ports
= ports
->next
) {
5854 for (hidx
= *ptableptr
; hidx
!= NULL
; hidx
= hidx
->next
) {
5855 if (hidx
->nets
!= NULL
) {
5856 if (hidx
->nets
->next
!= NULL
) {
5857 for (i
= 0; i
< hidx
->nets
->next
->numnets
; i
++)
5858 if (*(hidx
->nets
->next
->netidx
+ i
) == ports
->netid
)
5860 if (i
< hidx
->nets
->next
->numnets
) break;
5862 else if (ports
->netid
< 0) {
5863 if (hidx
->nets
->netidx
!= NULL
)
5864 if (*(hidx
->nets
->netidx
) == ports
->netid
)
5870 hidx
->nets
->numnets
++;
5871 if (hidx
->nets
->numnets
== 1)
5872 hidx
->nets
->netidx
= (int *)malloc(sizeof(int));
5874 hidx
->nets
->netidx
= (int *)realloc(hidx
->nets
->netidx
,
5875 hidx
->nets
->numnets
* sizeof(int));
5877 /* Translate net value */
5878 locnet
= translatedown(ports
->netid
, ports
->portid
,
5880 *(hidx
->nets
->netidx
+ hidx
->nets
->numnets
- 1) = locnet
;
5882 /* Fprintf(stdout, "Translation: net %d in object %s is net "
5883 "%d in object %s\n", ports->netid, cschem->name,
5884 locnet, calls->callobj->name); */
5888 /* Step 4C: Run routine recursively on the subcircuit */
5889 /* If it had a "pcb:" info label that was handled, then ignore */
5891 /* Fprintf(stdout, "Recursive call of writepcb() to %s\n",
5892 calls->callobj->name); */
5894 outputcall
= writepcb(ptableptr
, calls
->callobj
, calls
, newprefix
, mode
);
5896 /* Step 4D: Pop the translation table */
5897 /* (Don't pop global nets (designated by negative net number)) */
5900 while (hidx
!= NULL
) {
5901 if ((hidx
->nets
!= NULL
) && (((hidx
->nets
->numnets
> 0) &&
5902 (*(hidx
->nets
->netidx
) >= 0)) || (hidx
->nets
->numnets
== 0))) {
5903 tmpnet
= hidx
->nets
->next
;
5904 if (hidx
->nets
->numnets
> 0) free(hidx
->nets
->netidx
);
5906 hidx
->nets
= tmpnet
;
5915 /* Fprintf(stdout, "Reached lowest-level schematic: Finding connections\n"); */
5916 for (ports
= calls
->ports
; ports
!= NULL
; ports
= ports
->next
) {
5917 locnet
= translatedown(ports
->netid
, ports
->portid
, calls
->callobj
);
5918 /* Global pin names should probably be ignored, not just */
5919 /* when an object is declared to be "trivial". Not sure if */
5920 /* this breaks certain netlists. . . */
5921 if (locnet
< 0) continue;
5923 /* Get the name of the pin in the called object with no prefix. */
5924 subnet
= getsubnet(locnet
, calls
->callobj
);
5925 ppin
= nettopin(locnet
, calls
->callobj
, NULL
);
5927 while (hidx
!= NULL
) {
5928 if (hidx
->nets
!= NULL
) {
5929 /* Global nets were not translated, do not iterate through list */
5930 if (*(hidx
->nets
->netidx
) >= 0) {
5931 for (i
= 0; i
< hidx
->nets
->numnets
; i
++)
5932 if (*(hidx
->nets
->netidx
+ i
) == ports
->netid
)
5934 if (i
< hidx
->nets
->numnets
) break;
5936 if (*(hidx
->nets
->netidx
) == ports
->netid
) break;
5942 snew
= textprintsubnet(ppin
, cinst
, subnet
);
5943 Fprintf(stdout
, "Warning: Unconnected pin %s%s\n", newprefix
, snew
);
5949 tmpstr
= (struct Pstr
*)malloc(sizeof(struct Pstr
));
5950 tmpstr
->string
= (stringpart
*)malloc(sizeof(stringpart
));
5951 tmpstr
->string
->type
= TEXT_STRING
;
5952 tmpstr
->string
->nextpart
= NULL
;
5953 snew
= textprintsubnet(ppin
, cinst
, subnet
);
5954 tmpstr
->string
->data
.string
= (char *)malloc(strlen(newprefix
)
5955 + strlen(snew
) + 2);
5956 strcpy(tmpstr
->string
->data
.string
, newprefix
);
5957 /* Replace slash '/' with dash '-' at bottommost level */
5958 if ((tmplen
= strlen(newprefix
)) > 0)
5959 tmpstr
->string
->data
.string
[tmplen
- 1] = '-';
5960 strcat(tmpstr
->string
->data
.string
, snew
);
5962 tmpstr
->next
= hidx
->pins
;
5963 hidx
->pins
= tmpstr
;
5965 /* diagnostic information */
5967 struct Pnet
*locnet
= hidx
->nets
;
5969 while (locnet
->next
!= NULL
) {
5970 locnet
= locnet
->next
;
5973 /* Fprintf(stdout, "Logged level-%d net %d (local net %d) pin %s\n",
5974 ctr, *(locnet->netidx), *(hidx->nets->netidx + i),
5982 /* Step 5: Cleanup */
5988 /*----------------------------------------------------------------------*/
5989 /* Save PCB table into pcb-style file */
5990 /*----------------------------------------------------------------------*/
5992 void outputpcb(struct Ptab
*ptable
, FILE *fp
)
5994 int netidx
= 1, ccol
, subnet
;
6000 if (fp
== NULL
) return;
6002 for (pseek
= ptable
; pseek
!= NULL
; pseek
= pseek
->next
) {
6003 if (pseek
->pins
!= NULL
) {
6004 if ((pseek
->nets
!= NULL
) && (pseek
->nets
->numnets
> 0)) {
6005 subnet
= getsubnet(*(pseek
->nets
->netidx
), pseek
->cschem
);
6006 ppin
= nettopin(*(pseek
->nets
->netidx
), pseek
->cschem
, "");
6007 snew
= textprintsubnet(ppin
, NULL
, subnet
);
6012 sprintf(_STR
, "NET%d ", netidx
++);
6013 fprintf(fp
, "%-11s ", _STR
);
6015 for (sseek
= pseek
->pins
; sseek
!= NULL
; sseek
= sseek
->next
) {
6016 ccol
+= stringlength(sseek
->string
, False
, NULL
) + 3;
6018 fprintf(fp
, "\\\n ");
6019 ccol
= 18 + stringlength(sseek
->string
, False
, NULL
);
6021 snew
= textprint(sseek
->string
, NULL
);
6022 fprintf(fp
, "%s ", snew
);
6027 /* else fprintf(fp, "NET%d *UNCONNECTED*\n", netidx++); */
6031 /*----------------------------------------------*/
6032 /* free memory allocated to PCB net tables */
6033 /*----------------------------------------------*/
6035 void freepcb(struct Ptab
*ptable
)
6037 struct Ptab
*pseek
, *pseek2
;
6038 struct Pstr
*sseek
, *sseek2
;
6039 struct Pnet
*nseek
, *nseek2
;
6044 while (pseek2
!= NULL
) {
6045 pseek
= pseek
->next
;
6047 sseek
= pseek2
->pins
;
6049 while (sseek2
!= NULL
) {
6050 sseek
= sseek
->next
;
6051 freelabel(sseek2
->string
);
6056 nseek
= pseek2
->nets
;
6058 while (nseek2
!= NULL
) {
6059 nseek
= nseek
->next
;
6060 if (nseek2
->numnets
> 0) free(nseek2
->netidx
);
6070 /*----------------------------------------------------------------------*/
6071 /* Remove an element from the netlist. This is necessary when an */
6072 /* element is deleted from the object, so that the netlist does not */
6073 /* refer to a nonexistant element, or try to perform netlist function */
6076 /* Return True or False depending on whether a pin was "orphaned" on */
6077 /* the corresponding schematic or symbol (if any), as this may cause */
6078 /* the class of the object (FUNDAMENTAL, TRIVIAL, etc.) to change. */
6079 /*----------------------------------------------------------------------*/
6081 Boolean
RemoveFromNetlist(objectptr thisobject
, genericptr thiselem
)
6083 Boolean pinchanged
= False
;
6089 PolylistPtr plist
, plast
;
6090 LabellistPtr llist
, llast
;
6091 CalllistPtr clist
, clast
;
6093 pschem
= (thisobject
->schemtype
== SECONDARY
) ? thisobject
->symschem
6096 switch (thiselem
->type
) {
6098 ninst
= (objinstptr
)thiselem
;
6099 /* If this is an object instance, remove it from the calls */
6101 for (clist
= pschem
->calls
; clist
!= NULL
; clist
= clist
->next
) {
6102 if (clist
->callinst
== ninst
) {
6104 pschem
->calls
= clist
->next
;
6106 clast
->next
= clist
->next
;
6108 clist
= (clast
) ? clast
: pschem
->calls
;
6117 npoly
= (polyptr
)thiselem
;
6118 if (nonnetwork(npoly
)) break;
6120 /* If this is a polygon, remove it from the netlist */
6122 for (plist
= pschem
->polygons
; plist
!= NULL
; plist
= plist
->next
) {
6123 if (plist
->poly
== npoly
) {
6125 pschem
->polygons
= plist
->next
;
6127 plast
->next
= plist
->next
;
6128 if (plist
->subnets
> 0)
6129 free(plist
->net
.list
);
6138 nlab
= (labelptr
)thiselem
;
6139 if ((nlab
->pin
!= LOCAL
) && (nlab
->pin
!= GLOBAL
)) break;
6141 /* If this is a label, remove it from the netlist */
6143 for (llist
= pschem
->labels
; llist
!= NULL
; llist
= llist
->next
) {
6144 if (llist
->label
== nlab
) {
6146 pschem
->labels
= llist
->next
;
6148 llast
->next
= llist
->next
;
6149 if (llist
->subnets
> 0)
6150 free(llist
->net
.list
);
6157 /* Mark pin label in corresponding schematic/symbol as "orphaned" */
6158 /* by changing designation from type "pin" to type "label". */
6160 if (findlabelcopy(nlab
, nlab
->string
) == NULL
) {
6161 changeotherpins(NULL
, nlab
->string
);
6162 if (nlab
->pin
== INFO
) pinchanged
= True
;
6168 /*----------------------------------------------------------------------*/
6169 /* Remove one element from the netlist */
6170 /*----------------------------------------------------------------------*/
6172 void remove_netlist_element(objectptr cschem
, genericptr genelem
) {
6175 CalllistPtr clist
, clast
, cnext
;
6176 LabellistPtr llist
, llast
, lnext
;
6177 PolylistPtr plist
, plast
, pnext
;
6178 Boolean found
= FALSE
;
6180 /* Always call on the primary schematic */
6181 pschem
= (cschem
->schemtype
== SECONDARY
) ? cschem
->symschem
: cschem
;
6183 switch (ELEMENTTYPE(genelem
)) {
6185 /* remove polygon from polygon list */
6187 for (plist
= pschem
->polygons
; plist
!= NULL
; ) {
6188 pnext
= plist
->next
;
6189 if ((genericptr
)plist
->poly
== genelem
) {
6191 if (plist
->subnets
> 0)
6192 free(plist
->net
.list
);
6195 plast
->next
= pnext
;
6197 pschem
->polygons
= pnext
;
6208 /* remove label from label list */
6210 for (llist
= pschem
->labels
; llist
!= NULL
; ) {
6211 lnext
= llist
->next
;
6212 if ((genericptr
)llist
->label
== genelem
) {
6214 if (llist
->subnets
> 0)
6215 free(llist
->net
.list
);
6218 llast
->next
= lnext
;
6220 pschem
->labels
= lnext
;
6228 /* also check the globals */
6230 for (llist
= global_labels
; llist
!= NULL
; ) {
6231 lnext
= llist
->next
;
6232 if ((genericptr
)llist
->label
== genelem
) {
6234 if (llist
->subnets
> 0)
6235 free(llist
->net
.list
);
6238 llast
->next
= lnext
;
6240 global_labels
= lnext
;
6251 /* remove instance from call list */
6253 for (clist
= pschem
->calls
; clist
!= NULL
; ) {
6254 cnext
= clist
->next
;
6255 if ((genericptr
)clist
->callinst
== genelem
) {
6259 clast
->next
= cnext
;
6261 pschem
->calls
= cnext
;
6270 pschem
->valid
= FALSE
;
6273 /*----------------------------------------------------------------------*/
6274 /* Free memory allocated for the ports in a calls. */
6275 /*----------------------------------------------------------------------*/
6277 void freecalls(CalllistPtr calls
)
6279 PortlistPtr ports
, pptr
;
6281 for (ports
= calls
->ports
; ports
!= NULL
;) {
6286 if (calls
->devname
!= NULL
) free(calls
->devname
);
6290 /*----------------------------------------------------------------------*/
6291 /* Free memory for a Genericlist structure (may also be a Labellist or */
6292 /* Polylist structure). */
6293 /*----------------------------------------------------------------------*/
6295 void freegenlist(Genericlist
*nets
)
6297 if (nets
== NULL
) return;
6298 if (nets
->subnets
> 0)
6299 free(nets
->net
.list
);
6303 /*----------------------------------------------------------------------*/
6304 /* Free memory allocated for the label list in a netlist. */
6305 /*----------------------------------------------------------------------*/
6307 void freelabellist(LabellistPtr
*listtop
)
6309 LabellistPtr labellist
, llist
;
6311 for (labellist
= *listtop
; labellist
!= NULL
;) {
6312 llist
= labellist
->next
;
6313 freegenlist((Genericlist
*)labellist
);
6319 /*----------------------------------------------------------------------*/
6320 /* Free memory allocated for the polygon list in a netlist. */
6321 /*----------------------------------------------------------------------*/
6323 void freepolylist(PolylistPtr
*listtop
)
6325 PolylistPtr polylist
, plist
;
6327 for (polylist
= *listtop
; polylist
!= NULL
;) {
6328 plist
= polylist
->next
;
6329 freegenlist((Genericlist
*)polylist
);
6335 /*----------------------------------------------------------------------*/
6336 /* Free memory allocated for netlist */
6337 /*----------------------------------------------------------------------*/
6339 void freenetlist(objectptr cschem
)
6342 LabellistPtr
*llist
;
6344 plist
= &cschem
->polygons
;
6345 freepolylist(plist
);
6346 llist
= &cschem
->labels
;
6347 freelabellist(llist
);
6350 /*----------------------------------------------------------------------*/
6351 /* Clear the "traversed" flag in all objects of the hierarchy. */
6352 /*----------------------------------------------------------------------*/
6354 int cleartraversed_level(objectptr cschem
, int level
)
6358 objectptr callobj
, pschem
;
6360 /* Always call on the primary schematic */
6361 pschem
= (cschem
->schemtype
== SECONDARY
) ? cschem
->symschem
: cschem
;
6363 /* Recursively call cleartraversed() on all subobjects, a la gennetlist() */
6364 /* Use the parts list of the object, not the calls, because calls */
6365 /* may have been removed. */
6367 if (level
== HIERARCHY_LIMIT
) return -1;
6369 for (cgen
= pschem
->plist
; cgen
< pschem
->plist
+ pschem
->parts
; cgen
++) {
6370 if (IS_OBJINST(*cgen
)) {
6371 cinst
= TOOBJINST(cgen
);
6373 if (cinst
->thisobject
->symschem
!= NULL
)
6374 callobj
= cinst
->thisobject
->symschem
;
6376 callobj
= cinst
->thisobject
;
6378 /* Don't infinitely recurse if object is on its own schematic */
6379 /* However, we need to take a stab at more subtle recursion, too */
6381 if (callobj
!= pschem
)
6382 if (cleartraversed_level(callobj
, level
+ 1) == -1)
6386 pschem
->traversed
= False
;
6391 /*----------------------------------------------------------------------*/
6392 /* This is the routine normally called, as it hides the "level" */
6393 /* argument tagging the level of recursion. */
6394 /*----------------------------------------------------------------------*/
6396 int cleartraversed(objectptr cschem
) {
6397 return cleartraversed_level(cschem
, 0);
6400 /*----------------------------------------------------------------------*/
6401 /* If any part of the netlist is invalid, destroy the entire netlist */
6402 /*----------------------------------------------------------------------*/
6404 int checkvalid(objectptr cschem
)
6408 objectptr callobj
, pschem
;
6410 /* If the object has been declared a non-network object, ignore it */
6411 if (cschem
->schemtype
== NONETWORK
) return 0;
6413 /* Always operate on the master schematic */
6414 pschem
= (cschem
->schemtype
== SECONDARY
) ? cschem
->symschem
: cschem
;
6416 /* Stop immediately if the netlist is invalid */
6417 if (pschem
->valid
== False
) return -1;
6419 /* Otherwise, recursively call checkvalid() on all subobjects. */
6420 /* Use the parts list of the object, not the calls, because calls */
6421 /* may have been removed. */
6423 for (cgen
= pschem
->plist
; cgen
< pschem
->plist
+ pschem
->parts
; cgen
++) {
6424 if (IS_OBJINST(*cgen
)) {
6425 cinst
= TOOBJINST(cgen
);
6427 if (cinst
->thisobject
->symschem
!= NULL
)
6428 callobj
= cinst
->thisobject
->symschem
;
6430 callobj
= cinst
->thisobject
;
6432 /* Don't infinitely recurse if object is on its own schematic */
6434 if (callobj
== pschem
) continue;
6436 /* If there is a symbol, don't check its parts, but check if */
6437 /* its netlist has been checkvalid. */
6439 if ((cinst
->thisobject
->symschem
!= NULL
) &&
6440 (cinst
->thisobject
->labels
== NULL
) &&
6441 (cinst
->thisobject
->polygons
== NULL
) &&
6442 (cinst
->thisobject
->valid
== False
))
6445 /* Recursive call on subschematic */
6446 if (checkvalid(callobj
) == -1)
6450 return 0; /* All subnetlists and own netlist are valid */
6453 /*----------------------------------------------------------------------*/
6454 /* Free memory allocated to temporary labels generated for the netlist */
6455 /*----------------------------------------------------------------------*/
6457 void freetemplabels(objectptr cschem
)
6463 /* Recursively call freetemplabels() on all subobjects, a la gennetlist() */
6464 /* Use the parts list of the object, not the calls, because calls */
6465 /* may have been removed. */
6467 for (cgen
= cschem
->plist
; cgen
< cschem
->plist
+ cschem
->parts
; cgen
++) {
6468 if (IS_OBJINST(*cgen
)) {
6470 cinst
= TOOBJINST(cgen
);
6471 if (cinst
->thisobject
->symschem
!= NULL
)
6472 callobj
= cinst
->thisobject
->symschem
;
6474 callobj
= cinst
->thisobject
;
6476 /* Don't infinitely recurse if object is on its own schematic */
6477 if (callobj
!= cschem
) freetemplabels(callobj
);
6479 /* Also free the temp labels of any associated symbol */
6480 if (cinst
->thisobject
->symschem
!= NULL
) freetemplabels(cinst
->thisobject
);
6483 /* Free any temporary labels which have been created */
6485 else if (IS_LABEL(*cgen
)) {
6486 labelptr clab
= TOLABEL(cgen
);
6487 /* int tmpval = (int)(cgen - cschem->plist); (jdk) */
6488 if (clab
->string
->type
!= FONT_NAME
) {
6491 clab
= TOLABEL(cgen
);
6492 freelabel(clab
->string
);
6494 for (tgen
= cgen
+ 1; tgen
< cschem
->plist
+ cschem
->parts
; tgen
++)
6495 *(tgen
- 1) = *tgen
;
6503 /*----------------------------------------------------------------------*/
6504 /* Free memory allocated for netlists, ports, and calls */
6505 /*----------------------------------------------------------------------*/
6507 void freenets(objectptr cschem
)
6509 CalllistPtr calls
, cptr
;
6510 PortlistPtr ports
, pptr
;
6515 /* Recursively call freenets() on all subobjects, a la gennetlist() */
6516 /* Use the parts list of the object, not the calls, because calls */
6517 /* may have been removed. */
6519 if (cschem
->schemtype
== PRIMARY
|| cschem
->schemtype
== SECONDARY
||
6520 (cschem
->schemtype
== SYMBOL
&& cschem
->symschem
== NULL
)) {
6521 for (cgen
= cschem
->plist
; cgen
< cschem
->plist
+ cschem
->parts
; cgen
++) {
6522 if (IS_OBJINST(*cgen
)) {
6524 cinst
= TOOBJINST(cgen
);
6525 if (cinst
->thisobject
->symschem
!= NULL
)
6526 callobj
= cinst
->thisobject
->symschem
;
6528 callobj
= cinst
->thisobject
;
6530 /* Don't infinitely recurse if object is on its own schematic */
6531 if (callobj
!= cschem
) freenets(callobj
);
6533 /* Also free the netlist of any associated symbol */
6534 if (cinst
->thisobject
->symschem
!= NULL
) freenets(cinst
->thisobject
);
6539 /* Free the allocated structures for this object */
6541 for (calls
= cschem
->calls
; calls
!= NULL
;) {
6546 cschem
->calls
= NULL
;
6548 for (ports
= cschem
->ports
; ports
!= NULL
;) {
6553 cschem
->ports
= NULL
;
6555 freenetlist(cschem
);
6557 cschem
->traversed
= False
;
6558 cschem
->valid
= False
;
6559 freegenlist(cschem
->highlight
.netlist
);
6560 cschem
->highlight
.netlist
= NULL
;
6561 cschem
->highlight
.thisinst
= NULL
;
6564 /*----------------------------------------------------------------------*/
6565 /* Free the global pin list */
6566 /*----------------------------------------------------------------------*/
6570 LabellistPtr labellist
, llist
;
6572 for (labellist
= global_labels
; labellist
!= NULL
;) {
6573 llist
= labellist
->next
;
6575 /* Labels in the global list are temporary and must be deallocated */
6576 freelabel(labellist
->label
->string
);
6577 free(labellist
->label
);
6579 freegenlist((Genericlist
*)labellist
);
6582 global_labels
= NULL
;
6585 /*----------------------------------------------------------------------*/
6586 /* Get rid of locally-defined pin names */
6587 /*----------------------------------------------------------------------*/
6589 void clearlocalpins(objectptr cschem
)
6591 NetnamePtr netnames
, nextname
;
6593 for (netnames
= cschem
->netnames
; netnames
!= NULL
; ) {
6594 nextname
= netnames
->next
;
6595 if (netnames
->localpin
!= NULL
) {
6596 freelabel(netnames
->localpin
);
6599 netnames
= nextname
;
6601 cschem
->netnames
= NULL
;
6604 /*----------------------------------------------------------------------*/
6605 /* Handle lists of included files */
6606 /*----------------------------------------------------------------------*/
6608 /*----------------------------------------------------------------------*/
6609 /* check_included() --- check if a file has already been included. */
6610 /* Check by inode instead of filename so that we cannot trick the */
6611 /* program by giving, e.g., one relative path and one full path. */
6612 /*----------------------------------------------------------------------*/
6614 Boolean
check_included(char *filename
)
6616 struct stat filestatus
;
6619 if (stat(filename
, &filestatus
) == 0) {
6620 if (included_files
== NULL
) return FALSE
;
6621 for (numi
= 0; *(included_files
+ numi
) != (ino_t
)NULL
; numi
++) {
6622 if (*(included_files
+ numi
) == filestatus
.st_ino
) return TRUE
;
6628 /*----------------------------------------------------------------------*/
6629 /* append_included() --- update the list of included files by adding */
6630 /* the inode of filename to the list. */
6631 /*----------------------------------------------------------------------*/
6633 void append_included(char *filename
)
6635 struct stat filestatus
;
6638 if (stat(filename
, &filestatus
) == 0) {
6640 if (included_files
== NULL
) {
6641 included_files
= (ino_t
*)malloc(2 * sizeof(ino_t
));
6642 *included_files
= filestatus
.st_ino
;
6643 *(included_files
+ 1) = (ino_t
)NULL
;
6646 for (numi
= 0; *(included_files
+ numi
) != (ino_t
)NULL
; numi
++);
6648 included_files
= (ino_t
*)realloc(included_files
,
6649 (++numi
+ 1) * sizeof(ino_t
));
6651 *(included_files
+ numi
- 1) = filestatus
.st_ino
;
6652 *(included_files
+ numi
) = (ino_t
)NULL
;
6656 Wprintf("Error: Cannot stat include file \"%s\"\n", filename
);
6660 /*----------------------------------------------------------------------*/
6662 void free_included()
6664 if (included_files
!= NULL
) {
6665 free(included_files
);
6666 included_files
= NULL
;
6670 /*-------------------------------------------------------------------------*/