1 
2 #ifndef lint
3 static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/resis/ResPrint.c,v 1.1.1.1 2008/02/03 20:43:50 tim Exp $";
4 #endif  /* not lint */
5 
6 #include <stdio.h>
7 #include <string.h>
8 #include <ctype.h>
9 #include <math.h>
10 
11 #include "tcltk/tclmagic.h"
12 #include "utils/magic.h"
13 #include "utils/geometry.h"
14 #include "utils/geofast.h"
15 #include "tiles/tile.h"
16 #include "utils/hash.h"
17 #include "database/database.h"
18 #include "utils/malloc.h"
19 #include "textio/textio.h"
20 #include "extract/extract.h"
21 #include "extract/extractInt.h"
22 #include "windows/windows.h"
23 #include "dbwind/dbwind.h"
24 #include "utils/utils.h"
25 #include "cif/cif.h"
26 #include "utils/tech.h"
27 #include "textio/txcommands.h"
28 #include "utils/stack.h"
29 #include "utils/styles.h"
30 #include "resis/resis.h"
31 
32 #define MAXNAME			1000
33 #define KV_TO_mV		1000000
34 
35 extern ResSimNode *ResInitializeNode();
36 
37 
38 /*
39  *-------------------------------------------------------------------------
40  *
41  * ResPrintExtRes-- Print resistor network to output file.
42  *
43  * Results:none
44  *
45  * Side Effects:prints network.
46  *
47  *-------------------------------------------------------------------------
48  */
49 
50 void
ResPrintExtRes(outextfile,resistors,nodename)51 ResPrintExtRes(outextfile, resistors, nodename)
52     FILE	*outextfile;
53     resResistor *resistors;
54     char	*nodename;
55 
56 {
57     int	        nodenum=0;
58     char	newname[MAXNAME];
59     HashEntry  *entry;
60     ResSimNode *node, *ResInitializeNode();
61 
62     for (; resistors != NULL; resistors = resistors->rr_nextResistor)
63     {
64 	/*
65 	 * These names shouldn't be null; they should either be set by
66 	 * the device name or by the node printing routine.  This
67 	 * code is included in case the resistor network is printed
68 	 * before the nodes.
69 	 */
70 
71 	if (resistors->rr_connection1->rn_name == NULL)
72 	{
73      	    (void)sprintf(newname, "%s%s%d", nodename, ".r", nodenum++);
74      	    entry = HashFind(&ResNodeTable, newname);
75 	    node = ResInitializeNode(entry);
76 	    resistors->rr_connection1->rn_name = node->name;
77 	    node->oldname = nodename;
78 	}
79 	if (resistors->rr_connection2->rn_name == NULL)
80 	{
81      	    (void)sprintf(newname, "%s%s%d", nodename, ".r", nodenum++);
82      	    entry = HashFind(&ResNodeTable, newname);
83 	    node = ResInitializeNode(entry);
84 	    resistors->rr_connection2->rn_name = node->name;
85 	    node->oldname = nodename;
86 	}
87 	if (ResOptionsFlags & ResOpt_DoExtFile)
88 	{
89      	    fprintf(outextfile, "resist \"%s\" \"%s\" %g\n",
90 		    resistors->rr_connection1->rn_name,
91 		    resistors->rr_connection2->rn_name,
92 		    resistors->rr_value / (float)ExtCurStyle->exts_resistScale);
93 	}
94     }
95 }
96 
97 /*
98  *-------------------------------------------------------------------------
99  *
100  * ResPrintExtDev-- Print out all devices that have had at least
101  *	one terminal changed.
102  *
103  * Results:none
104  *
105  * Side Effects:prints device lines to output file
106  *
107  *-------------------------------------------------------------------------
108  */
109 
110 void
ResPrintExtDev(outextfile,devices)111 ResPrintExtDev(outextfile, devices)
112     FILE	*outextfile;
113     RDev	*devices;
114 {
115     TileType t;
116     char *subsName;
117     ExtDevice *devptr;
118 
119     for (; devices != NULL; devices = devices->nextDev)
120     {
121 	if (devices->status & TRUE)
122 	{
123 	    if (ResOptionsFlags & ResOpt_DoExtFile)
124 	    {
125 		t = devices->layout->rd_devtype;
126 		devptr = ExtCurStyle->exts_device[t];
127 		subsName = devptr->exts_deviceSubstrateName;
128 
129 #ifdef MAGIC_WRAPPER
130 		/* Substrate variable name substitution */
131 		if (subsName && subsName[0] == '$' && subsName[1] != '$')
132 		{
133 		    char *varsub = (char *)Tcl_GetVar(magicinterp, &subsName[1],
134 				TCL_GLOBAL_ONLY);
135 		    if (varsub != NULL) subsName = varsub;
136 		}
137 #endif
138 		/* Output according to device type and class. */
139 		/* Code largely matches what's in ExtBasic.c extOutputDevices() */
140 
141 		if (devptr->exts_deviceClass != DEV_FET)
142 		    fprintf(outextfile,"device ");
143 
144 		fprintf(outextfile,"%s %s %d %d %d %d ",
145 			extDevTable[devptr->exts_deviceClass],
146 			devptr->exts_deviceName,
147 			devices->layout->rd_inside.r_ll.p_x,
148 			devices->layout->rd_inside.r_ll.p_y,
149 			devices->layout->rd_inside.r_ll.p_x + 1,
150 			devices->layout->rd_inside.r_ll.p_y + 1);
151 
152 		switch (devptr->exts_deviceClass)
153 		{
154 		    case DEV_FET:
155 			fprintf(outextfile," %d %d",
156 				devices->layout->rd_area,
157 				devices->layout->rd_perim);
158 			break;
159 
160 		    case DEV_MOSFET:
161 		    case DEV_ASYMMETRIC:
162 		    case DEV_BJT:
163 			fprintf(outextfile," %d %d",
164 				devices->layout->rd_length,
165 				devices->layout->rd_width);
166 			break;
167 		}
168 
169 		if (devices->subs != NULL)
170 		    fprintf(outextfile, " \"%s\"", devices->subs->name);
171 		else if (subsName != NULL)
172 		    fprintf(outextfile, " \"%s\"", subsName);
173 		else
174 		    fprintf(outextfile, " \"None\"");
175 
176 		if (devices->gate != NULL)
177 		    fprintf(outextfile, " \"%s\" %d %s",
178 			    devices->gate->name,
179 			    devices->layout->rd_length * 2,
180 			    devices->rs_gattr);
181 
182 		if (devices->source != NULL)
183 		    fprintf(outextfile, " \"%s\" %d %s",
184 			    devices->source->name,
185 			    devices->layout->rd_width,
186 			    devices->rs_sattr);
187 
188 		if (devices->drain != NULL)
189 		    fprintf(outextfile, " \"%s\" %d %s",
190 			    devices->drain->name,
191 			    devices->layout->rd_width,
192 			    devices->rs_dattr);
193 
194 		fprintf(outextfile, "\n");
195 	    }
196 	}
197     }
198 }
199 
200 
201 /*
202  *-------------------------------------------------------------------------
203  *
204  * ResPrintExtNode-- Prints out all the nodes in the extracted net.
205  *
206  * Results:none
207  *
208  * Side Effects: Prints out extracted net. It may add new nodes to the
209  *	node hash table.
210  *
211  *-------------------------------------------------------------------------
212  */
213 
214 void
ResPrintExtNode(outextfile,nodelist,nodename)215 ResPrintExtNode(outextfile, nodelist, nodename)
216 	FILE	*outextfile;
217 	resNode	*nodelist;
218 	char	*nodename;
219 {
220     int		nodenum = 0;
221     char	newname[MAXNAME], tmpname[MAXNAME], *cp;
222     HashEntry  *entry;
223     ResSimNode *node, *ResInitializeNode();
224     bool	DoKillNode = TRUE;
225     resNode	*snode = nodelist;
226 
227     /* If any of the subnode names match the original node name, then	*/
228     /* we don't want to rip out that node with a "killnode" statement.	*/
229 
230     for (; nodelist != NULL; nodelist = nodelist->rn_more)
231     {
232 	if (nodelist->rn_name != NULL)
233 	    if (!strcmp(nodelist->rn_name, nodename))
234 	    {
235 		DoKillNode = FALSE;
236 		break;
237 	    }
238     }
239 
240     if ((ResOptionsFlags & ResOpt_DoExtFile) && DoKillNode)
241     {
242           fprintf(outextfile, "killnode \"%s\"\n", nodename);
243     }
244 
245     /* Create "rnode" entries for each subnode */
246 
247     for (; snode != NULL; snode = snode->rn_more)
248     {
249 	if (snode->rn_name == NULL)
250 	{
251 	    (void)sprintf(tmpname,"%s",nodename);
252 
253 	    cp = tmpname + strlen(tmpname) - 1;
254             if (*cp == '!' || *cp == '#') *cp = '\0';
255 
256      	    (void)sprintf(newname, "%s%s%d", tmpname, ".n", nodenum++);
257      	    entry = HashFind(&ResNodeTable, newname);
258 	    node = ResInitializeNode(entry);
259 	    snode->rn_name = node->name;
260 	    node->oldname = nodename;
261 	}
262 
263 	if (ResOptionsFlags & ResOpt_DoExtFile)
264 	{
265 	    /* rnode name R C x y  type (R is always 0) */
266      	    fprintf(outextfile, "rnode \"%s\" 0 %g %d %d %d\n",
267 		    snode->rn_name,
268 		    (snode->rn_float.rn_area / ExtCurStyle->exts_capScale),
269 		    snode->rn_loc.p_x,
270 		    snode->rn_loc.p_y,
271 		    /* the following is TEMPORARILY set to 0 */
272 		    0);
273 	}
274     }
275 }
276 
277 /*
278  *-------------------------------------------------------------------------
279  *
280  * ResPrintStats -- Prints out the node name, the number of devices,
281  *	and the number of nodes for each net added.  Also keeps a running
282  *	track of the totals.
283  *
284  * Results:
285  *
286  * Side Effects:
287  *
288  *-------------------------------------------------------------------------
289  */
290 
291 void
ResPrintStats(goodies,name)292 ResPrintStats(goodies, name)
293     ResGlobalParams	*goodies;
294     char		*name;
295 {
296     static int	totalnets = 0, totalnodes = 0, totalresistors = 0;
297     int nodes, resistors;
298     resNode	*node;
299     resResistor *res;
300 
301     if (goodies == NULL)
302     {
303      	  TxError("nets:%d nodes:%d resistors:%d\n",
304 	  	  totalnets, totalnodes, totalresistors);
305 	totalnets = 0;
306 	totalnodes = 0;
307 	totalresistors = 0;
308 	return;
309     }
310     nodes = 0;
311     resistors = 0;
312     totalnets++;
313     for (node = ResNodeList; node != NULL; node=node->rn_more)
314 
315     {
316      	nodes++;
317 	totalnodes++;
318     }
319     for (res = ResResList; res != NULL; res=res->rr_nextResistor)
320     {
321      	resistors++;
322 	totalresistors++;
323     }
324     TxError("%s %d %d\n", name, nodes, resistors);
325 }
326 
327 /*
328  *-------------------------------------------------------------------------
329  *
330  * Write the nodename to the output.  If the name does not exist, the node
331  * ID number is used as the name.  Assumes that node has either a valid
332  * name or valid ID record.
333  *
334  *-------------------------------------------------------------------------
335  */
336 
337 void
resWriteNodeName(fp,nodeptr)338 resWriteNodeName(fp, nodeptr)
339    FILE		*fp;
340    resNode	*nodeptr;
341 {
342     if (nodeptr->rn_name == NULL)
343 	fprintf(fp, "N%d", nodeptr->rn_id);
344     else
345 	fprintf(fp, "N%s", nodeptr->rn_name);
346 }
347 
348 /*
349  *-------------------------------------------------------------------------
350  *
351  * Write a description of the resistor network geometry, compatible
352  * with FastHenry (mainly for doing inductance extraction)
353  *
354  *-------------------------------------------------------------------------
355  */
356 
357 void
ResPrintFHNodes(fp,nodelist,nodename,nidx,celldef)358 ResPrintFHNodes(fp, nodelist, nodename, nidx, celldef)
359     FILE	*fp;
360     resNode	*nodelist;
361     char	*nodename;
362     int		*nidx;
363     CellDef	*celldef;
364 {
365     char 	newname[16];
366     resNode	*nodeptr;
367     resResistor	*resptr, *contact_res;
368     resElement	*elemptr;
369     float	oscale, height;
370     int		np;
371 
372     if (fp == NULL) return;
373 
374     oscale = CIFGetOutputScale(1000);   /* 1000 for conversion to um */
375 
376     fprintf(fp, "\n* List of nodes in network\n");
377     for (nodeptr = nodelist; nodeptr; nodeptr = nodeptr->rn_more)
378     {
379 	if (nodeptr->rn_name == NULL)
380 	{
381 	    nodeptr->rn_id = (*nidx);
382 	    (*nidx)++;
383 	}
384 	else
385 	{
386 	    HashEntry  *entry;
387 	    ResSimNode *simnode;
388 
389 	    /* If we process another sim file node while doing this	*/
390 	    /* one, mark it as status "REDUNDANT" so we don't duplicate	*/
391 	    /* the entry.						*/
392 
393      	    entry = HashFind(&ResNodeTable, nodeptr->rn_name);
394 	    simnode = (ResSimNode *)HashGetValue(entry);
395 	    if (simnode != NULL)
396 		simnode->status |= REDUNDANT;
397 	}
398 	resWriteNodeName(fp, nodeptr);
399 
400 	/* Height of the layer is the height of the first non-contact   */
401 	/* layer type connected to any resistor connected to this node.	*/
402 
403 	contact_res = (resResistor *)NULL;
404 	for (elemptr = nodeptr->rn_re; elemptr; elemptr = elemptr->re_nextEl)
405 	{
406 	    resptr = elemptr->re_thisEl;
407 	    if (!DBIsContact(resptr->rr_tt))
408 	    {
409 		height = ExtCurStyle->exts_height[resptr->rr_tt];
410 		if (height == 0)
411 		{
412 		    int pnum = DBPlane(resptr->rr_tt);
413 		    int hnum = ExtCurStyle->exts_planeOrder[pnum];
414 		    height = 0.1 * hnum;
415 		}
416 	    }
417 	    else
418 		contact_res = resptr;
419 	}
420 	height *= oscale;
421 
422 	fprintf(fp, " x=%1.2f y=%1.2f z=%1.2f\n",
423 		(float)nodeptr->rn_loc.p_x * oscale,
424 		(float)nodeptr->rn_loc.p_y * oscale,
425 		height);
426 
427 	/* If it's a contact region and has more than one contact, add	*/
428 	/* contact points as individual nodes and connect to the main 	*/
429 	/* node with an "equiv" record.					*/
430 
431 	if (contact_res != (resResistor *)NULL &&
432 		(contact_res->rr_cl > 1 ||
433 		contact_res->rr_width > 1))
434 	{
435 	    int i, j, edge, spacing;
436 	    float del, cx, cy, cxb, cyb;
437 
438 	    CIFGetContactSize(contact_res->rr_tt, &edge, &spacing, NULL);
439 
440 	    del = (float)(spacing + edge) / (oscale * 100);
441 
442 	    cxb = (float)(contact_res->rr_cl - 1) / 2;
443 	    for (i = 0; i < contact_res->rr_cl; i++)
444 	    {
445 		cx = del * ((float)i - cxb);
446 	        cyb = (float)(contact_res->rr_width - 1) / 2;
447 		for (j = 0; j < contact_res->rr_width; j++)
448 		{
449 		    cy = del * ((float)j - cyb);
450 		    resWriteNodeName(fp, nodeptr);
451 		    fprintf(fp, "_%d_%d ", i, j);
452 		    fprintf(fp, "x=%1.2f y=%1.2f z=%1.2f\n",
453 			((float)nodeptr->rn_loc.p_x + cx) * oscale,
454 			((float)nodeptr->rn_loc.p_y + cy) * oscale,
455 			height);
456 		}
457 	    }
458 
459 	    /* Short all the contact nodes together with .equiv records */
460 
461 	    fprintf(fp, ".equiv ");
462 	    resWriteNodeName(fp, nodeptr);
463 	    for (i = 0; i < contact_res->rr_cl; i++)
464 	    {
465 		for (j = 0; j < contact_res->rr_width; j++)
466 		{
467 		    fprintf(fp, " ");
468 		    resWriteNodeName(fp, nodeptr);
469 		    fprintf(fp, "_%d_%d", i, j);
470 		}
471 	    }
472 	    fprintf(fp, "\n");
473 	}
474     }
475 
476     fprintf(fp, "\n* List of externally-connected ports\n.external");
477     np = 0;
478     for (nodeptr = nodelist; nodeptr; nodeptr = nodeptr->rn_more)
479     {
480 	if (nodeptr->rn_name != NULL)
481 	{
482 	    if (np < 2)
483 	    {
484 		Label *lab;
485 
486 		fprintf(fp, " N%s", nodeptr->rn_name);
487 
488 		/* This part is sort of a hack---need a better hook to	*/
489 		/* the original label this external port connects to,	*/
490 		/* rather than search for it every time we write an	*/
491 		/* external connection.					*/
492 
493 		for (lab = celldef->cd_labels; lab != NULL; lab = lab->lab_next)
494 		    if (lab->lab_flags & PORT_DIR_MASK)
495 			if (!strcmp(lab->lab_text, nodeptr->rn_name))
496 			{
497 			    if (lab->lab_port != ResPortIndex)
498 			    {
499 				lab->lab_port = ResPortIndex;
500 				TxPrintf("Port %s reassigned index %d\n",
501 					lab->lab_text, lab->lab_port);
502 				celldef->cd_flags |= (CDMODIFIED | CDGETNEWSTAMP);
503 			    }
504 			    ResPortIndex++;
505 			}
506 	    }
507 	    else
508 	    {
509 		if (np == 2)
510 		    fprintf(fp, "\n* Warning! external nodes not recorded:");
511 		fprintf(fp, " N%s", nodeptr->rn_name);
512 	    }
513 	    np++;
514 	}
515     }
516     fprintf(fp, "\n\n");
517 
518     /* Shouldn't this work? */
519 
520 /*
521     fprintf(fp, "\n* List of externally-connected ports\n");
522     for (nodeptr = nodelist; nodeptr; nodeptr = nodeptr->rn_more)
523 	if (nodeptr->rn_name != NULL)
524 	    fprintf(fp, ".external N%s Nsub\n", nodeptr->rn_name);
525 
526     fprintf(fp, "\n");
527 */
528 }
529 
530 /*
531  *-------------------------------------------------------------------------
532  * ResPrintFHRects --
533  *	Generate FastHenry segment output to the FastHenry data file
534  *	"fp".
535  *
536  * Results:
537  *	None.
538  *
539  * Side effects:
540  *	Stuff written to the stream file "fp".
541  *
542  *-------------------------------------------------------------------------
543  */
544 
545 void
ResPrintFHRects(fp,reslist,nodename,eidx)546 ResPrintFHRects(fp, reslist, nodename, eidx)
547     FILE	*fp;
548     resResistor	*reslist;
549     char	*nodename;
550     int		*eidx;		/* element (segment) index */
551 {
552     resResistor	*resistors;
553     float	oscale, thick, cwidth;
554     int		edge;
555 
556     if (fp == NULL) return;
557 
558     oscale = CIFGetOutputScale(1000);   /* 1000 for conversion to um */
559 
560     fprintf(fp, "* Segments connecting nodes in network\n");
561     for (resistors = reslist; resistors; resistors = resistors->rr_nextResistor)
562     {
563 	if (DBIsContact(resistors->rr_tt) &&
564 		(resistors->rr_cl > 1 || resistors->rr_width > 1))
565 	{
566 	    int i, j;
567 
568 	    CIFGetContactSize(resistors->rr_tt, &edge, NULL, NULL);
569 
570 	    /* 100 is for centimicrons to microns conversion */
571 	    cwidth = (float)edge / 100;
572 
573 	    /* for contacts, rr_cl = squares in x, rr_width = squares in y */
574 
575 	    for (i = 0; i < resistors->rr_cl; i++)
576 	    {
577 		for (j = 0; j < resistors->rr_width; j++)
578 		{
579 		    fprintf(fp, "E%d ", *eidx);
580 		    resWriteNodeName(fp, resistors->rr_connection1);
581 		    fprintf(fp, "_%d_%d ", i, j);
582 		    resWriteNodeName(fp, resistors->rr_connection2);
583 		    fprintf(fp, "_%d_%d ", i, j);
584 
585 		    /* Vias are vertical and so w and h are the dimensions of	*/
586 		    /* the via hole.  For other layers, h is layer thickness.	*/
587 
588 		    fprintf(fp, "w=%1.2f h=%1.2f\n", cwidth, cwidth);
589 
590 		    (*eidx)++;
591 		}
592 	    }
593 	}
594 	else
595 	{
596 	    fprintf(fp, "E%d ", *eidx);
597 	    resWriteNodeName(fp, resistors->rr_connection1);
598 	    fprintf(fp, " ");
599 	    resWriteNodeName(fp, resistors->rr_connection2);
600 
601 	    if (DBIsContact(resistors->rr_tt))
602 	    {
603 		CIFGetContactSize(resistors->rr_tt, &edge, NULL, NULL);
604 		/* 100 for centimicrons to microns conversion */
605 		cwidth = (float)edge / 100;
606 		fprintf(fp, " w=%1.2f h=%1.2f\n", cwidth, cwidth);
607 	    }
608 	    else
609 	    {
610 		/* For non-via layers, h is layer thickness. */
611 
612 		thick = ExtCurStyle->exts_thick[resistors->rr_tt];
613 		if (thick == 0) thick = 0.05;
614 		fprintf(fp, " w=%1.2f h=%1.2f\n",
615 			(float)resistors->rr_width * oscale,
616 			thick * oscale);
617 
618 	    }
619 	    (*eidx)++;
620 	}
621     }
622 }
623 
624 /*
625  *-------------------------------------------------------------------------
626  *
627  * ResPrintReference --
628  *
629  *	Write the reference plane (substrate) definition to the geometry
630  *	(FastHenry) file output.
631  *
632  *	NOTE:  For now, I am assuming that substrate = ground (GND).
633  *	However, a device list is passed, and it should be parsed
634  *	for substrate devices, allowing the creation of VDD and GND
635  *	reference planes for both substrate and wells.
636  *
637  *	Another note:  For now, I am assuming a uniform reference
638  *	plane of the size of the cell bounding box.  It may be
639  *	preferable to search tiles and generate multiple, connected
640  *	reference planes.  Or it may be desirable to have an effectively
641  *	infinite reference plane by extending it far out from the
642  *	subcircuit bounding box.
643  *
644  *-------------------------------------------------------------------------
645  */
646 
647 void
ResPrintReference(fp,devices,cellDef)648 ResPrintReference(fp, devices, cellDef)
649     FILE	*fp;
650     RDev	*devices;
651     CellDef	*cellDef;
652 {
653     char 	*outfile = cellDef->cd_name;
654     Rect	*bbox = &(cellDef->cd_bbox);
655     int		numsegsx, numsegsy;
656     float	oscale, llx, lly, urx, ury;
657 
658     oscale = CIFGetOutputScale(1000);   /* 1000 for conversion to um */
659     llx = (float)bbox->r_xbot * oscale;
660     lly = (float)bbox->r_ybot * oscale;
661     urx = (float)bbox->r_xtop * oscale;
662     ury = (float)bbox->r_ytop * oscale;
663 
664     fprintf(fp, "* FastHenry output for magic cell %s\n\n", outfile);
665     fprintf(fp, ".Units um\n");
666     fprintf(fp, ".Default rho=0.02 nhinc=3 nwinc=3 rh=2 rw=2\n\n");
667     fprintf(fp, "* Reference plane (substrate, ground)\n");
668 
669     fprintf(fp, "Gsub x1=%1.2f y1=%1.2f z1=0 x2=%1.2f y2=%1.2f z2=0\n",
670 		llx, lly, urx, lly);
671     fprintf(fp, "+ x3=%1.2f y3=%1.2f z3=0\n", urx, ury);
672 
673     /* Grid the reference plane at 20 lambda intervals.  This	*/
674     /* may warrant a more rigorous treatment.  20 is arbitrary.	*/
675     /* Minimum number of segments is 4 (also arbitrary).	*/
676 
677     numsegsx = (bbox->r_xtop - bbox->r_xbot) / 20;
678     numsegsy = (bbox->r_ytop - bbox->r_ybot) / 20;
679     if (numsegsx < 4) numsegsx = 4;
680     if (numsegsy < 4) numsegsy = 4;
681 
682     fprintf(fp, "+ thick=0.1 seg1=%d seg2=%d\n", numsegsx, numsegsy);
683 
684     fprintf(fp, "+ Ngp (%1.2f,%1.2f,0)\n", llx, lly);
685 
686     fprintf(fp, "\nNsub x=%1.2f y=%1.2f z=0\n", llx, lly);
687     fprintf(fp, ".Equiv Nsub Ngp\n");
688 }
689 
690 /*
691  *-------------------------------------------------------------------------
692  * ResCreateCenterlines --
693  *	Generate centerline markers on the layout that correspond to
694  * 	network routes.  Use the "DBWelement" mechanism.
695  *
696  * Results:
697  *	0 on success, -1 if a window cannot be found.
698  *
699  * Side effects:
700  *	Database "line" elements are generated in the layout.
701  *
702  *-------------------------------------------------------------------------
703  */
704 
705 int
ResCreateCenterlines(reslist,nidx,def)706 ResCreateCenterlines(reslist, nidx, def)
707     resResistor	*reslist;
708     int		*nidx;
709     CellDef *def;
710 {
711     resResistor	*resistors;
712     resNode *nodeptr;
713     Rect r, rcanon;
714     MagWindow *w;	/* should be passed from up in CmdExtResis. . . */
715     char name[128];
716 
717     w = ToolGetBoxWindow (&r,  (int *)NULL);
718     if (w == (MagWindow *)NULL)
719 	windCheckOnlyWindow(&w, DBWclientID);
720     if ((w == (MagWindow *) NULL) || (w->w_client != DBWclientID)) {
721         TxError("Put the cursor in a layout window.\n");
722         return -1;
723     }
724 
725     for (resistors = reslist; resistors; resistors = resistors->rr_nextResistor)
726     {
727 	/* Ignore vias */
728 
729 	if (!DBIsContact(resistors->rr_tt))
730 	{
731 	    nodeptr = resistors->rr_connection1;
732 	    r.r_xbot = nodeptr->rn_loc.p_x;
733 	    r.r_ybot = nodeptr->rn_loc.p_y;
734 	    if (nodeptr->rn_name == NULL)
735 	    {
736 		nodeptr->rn_id = (*nidx);
737 		(*nidx)++;
738 		sprintf(name, "N%d_", nodeptr->rn_id);
739 	    }
740 	    else
741 		sprintf(name, "N%s_", nodeptr->rn_name);
742 
743 	    nodeptr = resistors->rr_connection2;
744 	    r.r_xtop = nodeptr->rn_loc.p_x;
745 	    r.r_ytop = nodeptr->rn_loc.p_y;
746 	    GeoCanonicalRect(&r, &rcanon);
747 	    if (nodeptr->rn_name == NULL)
748 	    {
749 		nodeptr->rn_id = (*nidx);
750 		(*nidx)++;
751 		sprintf(name + strlen(name), "%d", nodeptr->rn_id);
752 	    }
753 	    else
754 		strcat(name, nodeptr->rn_name);
755 
756 	    /* Note that if any element exists with name "name"	*/
757 	    /* it will be deleted (overwritten).		*/
758 	    DBWElementAddLine(w, name, &rcanon, def, STYLE_YELLOW1);
759 	}
760     }
761     return 0;
762 }
763 
764