1 
2 #ifndef lint
3 static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/resis/ResRex.c,v 1.3 2010/03/08 13:33:33 tim Exp $";
4 #endif  /* not lint */
5 
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <ctype.h>
10 #include <math.h>
11 #include <float.h>
12 
13 #include "tcltk/tclmagic.h"
14 #include "utils/magic.h"
15 #include "utils/geometry.h"
16 #include "utils/geofast.h"
17 #include "tiles/tile.h"
18 #include "utils/hash.h"
19 #include "utils/undo.h"
20 #include "utils/signals.h"
21 #include "database/database.h"
22 #include "utils/malloc.h"
23 #include "textio/textio.h"
24 #include "extract/extract.h"
25 #include "extract/extractInt.h"
26 #include "extflat/extflat.h"
27 #include "windows/windows.h"
28 #include "dbwind/dbwind.h"
29 #include "utils/utils.h"
30 #include "utils/tech.h"
31 #include "textio/txcommands.h"
32 #include	"resis/resis.h"
33 #ifdef LAPLACE
34 #include	"laplace.h"
35 #endif
36 
37 #define INITFLATSIZE		1024
38 #define MAXNAME			1000
39 
40 /* Time constants are produced by multiplying attofarads by milliohms,  */
41 /* giving zeptoseconds (yes, really.  Look it up).  This constant 	*/
42 /* converts zeptoseconds to picoseconds.				*/
43 
44 #define Z_TO_P		1e9
45 
46 /* Table of nodes to ignore (manually specified) */
47 
48 HashTable 	ResIgnoreTable;	    /* Hash table of nodes to ignore  */
49 
50 /* Table of nodes to include (manually specified) */
51 
52 HashTable 	ResIncludeTable;    /* Hash table of nodes to include  */
53 
54 /* Table of cells that have been processed */
55 
56 HashTable	ResProcessedTable;
57 
58 /* ResSimNode is a node read in from a sim file */
59 
60 HashTable 	ResNodeTable;   /* Hash table of sim file nodes   */
61 RDev		*ResRDevList;	/* Linked list of Sim devices	  */
62 ResGlobalParams	gparams;	/* Junk passed between 		  */
63 				/* ResCheckSimNodes and 	  */
64 				/* ResExtractNet.		  */
65 extern ResSimNode	*ResOriginalNodes;	/*Linked List of Nodes		  */
66 int		resNodeNum;
67 
68 #ifdef LAPLACE
69 int	ResOptionsFlags = ResOpt_Simplify | ResOpt_Tdi | ResOpt_DoExtFile
70 		| ResOpt_CacheLaplace;
71 #else
72 int	ResOptionsFlags = ResOpt_Simplify | ResOpt_Tdi | ResOpt_DoExtFile;
73 #endif
74 char	*ResCurrentNode;
75 
76 FILE	*ResExtFile;
77 FILE	*ResLumpFile;
78 FILE	*ResFHFile;
79 
80 int	ResPortIndex;	/* Port ordering to backannotate into magic */
81 
82 /* external declarations */
83 extern ResSimNode *ResInitializeNode();
84 extern CellUse	  *CmdGetSelectedCell();
85 
86 /* Structure stores information required to be sent to ExtResisForDef() */
87 typedef struct
88 {
89     float	tolerance;
90     float	tdiTolerance;
91     float	frequency;
92     CellDef	*mainDef;
93 } ResisData;
94 
95 /*
96  *-------------------------------------------------------------------------
97  *
98  *  ExtResisForDef --
99  *
100  *	Do resistance network extraction for the indicated CellDef.
101  *
102  *-------------------------------------------------------------------------
103  */
104 
105 void
ExtResisForDef(celldef,resisdata)106 ExtResisForDef(celldef, resisdata)
107     CellDef *celldef;
108     ResisData *resisdata;
109 {
110     RDev	*oldRDev;
111     HashSearch	hs;
112     HashEntry	*entry;
113     devPtr	*tptr, *oldtptr;
114     ResSimNode  *node;
115     int		result, idx;
116     char	*devname;
117 
118     ResRDevList = NULL;
119     ResOriginalNodes = NULL;
120 
121     /* Check if this cell has been processed */
122     if (HashLookOnly(&ResProcessedTable, celldef->cd_name)) return;
123     HashFind(&ResProcessedTable, celldef->cd_name);
124 
125     /* Get device information from the current extraction style */
126     idx = 0;
127     while (ExtGetDevInfo(idx++, &devname, NULL, NULL, NULL, NULL, NULL))
128     {
129         if (idx == TT_MAXTYPES)
130         {
131             TxError("Error:  Ran out of space for device types!\n");
132             break;
133         }
134         efBuildAddStr(EFDevTypes, &EFDevNumTypes, TT_MAXTYPES, devname);
135     }
136 
137     HashInit(&ResNodeTable, INITFLATSIZE, HT_STRINGKEYS);
138     /* read in .sim file */
139     result = (ResReadSim(celldef->cd_name,
140 	      	ResSimDevice, ResSimCapacitor, ResSimResistor,
141 		ResSimAttribute, ResSimMerge, ResSimSubckt) == 0);
142 
143     /* Clean up the EFDevTypes table */
144     for (idx = 0; idx < EFDevNumTypes; idx++) freeMagic(EFDevTypes[idx]);
145     EFDevNumTypes = 0;
146 
147     if (result)
148 	/* read in .nodes file   */
149 	result = (ResReadNode(celldef->cd_name) == 0);
150 
151     if (result)
152     {
153 	/* Check for subcircuit ports */
154 	if (ResOptionsFlags & ResOpt_Blackbox)
155 	    ResCheckBlackbox(celldef);
156 	else
157 	    ResCheckPorts(celldef);
158 
159 	/* Extract networks for nets that require it. */
160 	if (!(ResOptionsFlags & ResOpt_FastHenry) ||
161 			DBIsSubcircuit(celldef))
162 	    ResCheckSimNodes(celldef, resisdata);
163 
164 	if (ResOptionsFlags & ResOpt_Stat)
165 	    ResPrintStats((ResGlobalParams *)NULL, "");
166     }
167 
168     /* Clean up */
169 
170     HashStartSearch(&hs);
171     while((entry = HashNext(&ResNodeTable, &hs)) != NULL)
172     {
173 	node=(ResSimNode *) HashGetValue(entry);
174 	tptr = node->firstDev;
175 	if (node == NULL)
176 	{
177 	    TxError("Error:  NULL Hash entry!\n");
178 	    TxFlushErr();
179 	}
180 	while (tptr != NULL)
181 	{
182 	    oldtptr = tptr;
183 	    tptr = tptr->nextDev;
184 	    freeMagic((char *)oldtptr);
185 	}
186 	freeMagic((char *) node);
187     }
188     HashKill(&ResNodeTable);
189     while (ResRDevList != NULL)
190     {
191     	oldRDev = ResRDevList;
192 	ResRDevList = ResRDevList->nextDev;
193 	if (oldRDev->layout != NULL)
194 	{
195 	    freeMagic((char *)oldRDev->layout);
196 	    oldRDev->layout = NULL;
197 	}
198 	freeMagic((char *)oldRDev);
199     }
200 }
201 
202 
203 /*
204  *-------------------------------------------------------------------------
205  *
206  * CmdExtResis--  reads in sim file and layout, and produces patches to the
207  *	.ext files and .sim files that include resistors.
208  *
209  * Results:
210  *	None.
211  *
212  * Side Effects: Produces .res.sim file and .res.ext file for all nets that
213  *	require resistors.
214  *
215  *-------------------------------------------------------------------------
216  */
217 
218 void
CmdExtResis(win,cmd)219 CmdExtResis(win, cmd)
220 	MagWindow *win;
221 	TxCommand *cmd;
222 {
223     int		i, j, k, option, value, saveFlags;
224     static int	init = 1;
225     static float tolerance, tdiTolerance, fhFrequency;
226     CellDef	*mainDef;
227     CellUse	*selectedUse;
228     ResisData	resisdata;
229     char	*endptr;	/* for use with strtod() */
230 
231     extern int resSubcircuitFunc();	/* Forward declaration */
232 
233     static char *onOff[] =
234     {
235 	"off",
236 	"on",
237 	NULL
238     };
239 
240     static char *cmdExtresisCmd[] =
241     {
242 	"tolerance [value]    set ratio between resistor and device tol.",
243 	"all 		       extract all the nets",
244 	"simplify [on/off]    turn on/off simplification of resistor nets",
245 	"extout   [on/off]    turn on/off writing of .res.ext file",
246 	"lumped   [on/off]    turn on/off writing of updated lumped resistances",
247 	"silent   [on/off]    turn on/off printing of net statistics",
248 	"skip     mask        don't extract these types",
249 	"ignore	  names	      don't extract these nets",
250 	"include  names	      extract only these nets",
251 	"box      type        extract the signal under the box on layer type",
252 	"cell	   cellname    extract the network for the cell named cellname",
253 	"blackbox [on/off]    treat subcircuits with ports as black boxes",
254 	"fasthenry [freq]      extract subcircuit network geometry into .fh file",
255 	"geometry	      extract network centerline geometry (experimental)",
256 	"help                 print this message",
257 #ifdef LAPLACE
258 	"laplace  [on/off]    solve Laplace's equation using FEM",
259 #endif
260 	NULL
261     };
262 
263 typedef enum {
264 	RES_BAD=-2, RES_AMBIG, RES_TOL,
265 	RES_ALL, RES_SIMP, RES_EXTOUT, RES_LUMPED, RES_SILENT,
266 	RES_SKIP, RES_IGNORE, RES_INCLUDE, RES_BOX, RES_CELL,
267 	RES_BLACKBOX, RES_FASTHENRY, RES_GEOMETRY, RES_HELP,
268 #ifdef LAPLACE
269 	RES_LAPLACE,
270 #endif
271 	RES_RUN
272 } ResOptions;
273 
274     if (init)
275     {
276 	for (i = 0; i < NT; i++)
277 	{
278             TTMaskZero(&(ResCopyMask[i]));
279 	    TTMaskSetMask(&ResCopyMask[i], &DBConnectTbl[i]);
280      	}
281 	tolerance = 1;
282 	tdiTolerance = 1;
283 	fhFrequency = 10e6;	/* 10 MHz default */
284 	HashInit(&ResIgnoreTable, INITFLATSIZE, HT_STRINGKEYS);
285 	HashInit(&ResIncludeTable, INITFLATSIZE, HT_STRINGKEYS);
286 	init = 0;
287     }
288 
289     /* Initialize ResGlobalParams */
290     gparams.rg_ttype = TT_SPACE;
291     gparams.rg_Tdi = 0.0;
292     gparams.rg_nodecap = 0.0;
293     gparams.rg_maxres = 0.0;
294     gparams.rg_bigdevres = 0;
295     gparams.rg_tilecount = 0;
296     gparams.rg_status = 0;
297     gparams.rg_devloc = NULL;
298     gparams.rg_name = NULL;
299 
300     option = (cmd->tx_argc > 1) ? Lookup(cmd->tx_argv[1], cmdExtresisCmd)
301 		: RES_RUN;
302 
303     switch (option)
304     {
305 	case RES_SIMP:
306 	case RES_EXTOUT:
307 	case RES_LUMPED:
308 	case RES_SILENT:
309 	case RES_BLACKBOX:
310 	    if (cmd->tx_argc > 2)
311 	    {
312 		value = Lookup(cmd->tx_argv[2], onOff);
313 		if (value < 0)
314 		{
315 		    TxError("Value must be either \"on\" or \"off\".\n");
316 		    return;
317 		}
318 	    }
319 	    break;
320     }
321 
322     switch (option)
323     {
324 	 case RES_TOL:
325 	    ResOptionsFlags |=  ResOpt_ExplicitRtol;
326 	    if (cmd->tx_argc > 2)
327 	    {
328 		tolerance = MagAtof(cmd->tx_argv[2]);
329 		if (tolerance <= 0)
330 		{
331 		    TxError("Usage:  %s tolerance [value]\n", cmd->tx_argv[0]);
332 			return;
333 		}
334 		tdiTolerance = tolerance;
335 	    }
336 	    else
337 	    {
338 #ifdef MAGIC_WRAPPER
339 		Tcl_SetObjResult(magicinterp, Tcl_NewDoubleObj((double)tdiTolerance));
340 #else
341 		TxPrintf("Tolerance ratio is %g.\n", tdiTolerance);
342 #endif
343 	    }
344 	    return;
345 
346 	 case RES_ALL:
347 	    ResOptionsFlags |= ResOpt_ExtractAll;
348 	    break;
349 
350 	 case RES_GEOMETRY:
351 	    saveFlags = ResOptionsFlags;
352 	    ResOptionsFlags |= ResOpt_Geometry | ResOpt_ExtractAll;
353 	    ResOptionsFlags &= ~(ResOpt_DoExtFile | ResOpt_DoLumpFile
354 			| ResOpt_Simplify | ResOpt_Tdi);
355 	    break;
356 
357 	 case RES_FASTHENRY:
358 	    if (cmd->tx_argc == 3)
359 	    {
360 		  double tmpf = strtod(cmd->tx_argv[2], &endptr);
361 		  if (endptr == cmd->tx_argv[2])
362 		  {
363 		      TxError("Cannot parse frequency value.  Assuming default\n");
364 		      TxError("Frequency = %2.1f Hz\n", fhFrequency);
365 		  }
366 		  else
367 		      fhFrequency = (float)tmpf;
368 	    }
369 	    saveFlags = ResOptionsFlags;
370 	    ResOptionsFlags |= ResOpt_FastHenry | ResOpt_ExtractAll;
371 	    ResOptionsFlags &= ~(ResOpt_DoExtFile | ResOpt_DoLumpFile
372 			| ResOpt_Simplify | ResOpt_Tdi);
373 	    break;
374 
375 	case RES_BLACKBOX:
376 	    if (cmd->tx_argc == 2)
377 	    {
378 		value = (ResOptionsFlags & ResOpt_Blackbox) ?
379 			TRUE : FALSE;
380 		TxPrintf("%s\n", onOff[value]);
381 	    }
382 	    else
383 	    {
384 		value = Lookup(cmd->tx_argv[2], onOff);
385 
386 		if (value)
387 	      	   ResOptionsFlags |= ResOpt_Blackbox;
388 		else
389 	      	   ResOptionsFlags &= ~ResOpt_Blackbox;
390 	    }
391 	    return;
392 	case RES_SIMP:
393 	    if (cmd->tx_argc == 2)
394 	    {
395 		value = (ResOptionsFlags & (ResOpt_Simplify | ResOpt_Tdi)) ?
396 			TRUE : FALSE;
397 		TxPrintf("%s\n", onOff[value]);
398 	    }
399 	    else
400 	    {
401 		value = Lookup(cmd->tx_argv[2], onOff);
402 
403 		if (value)
404 	      	   ResOptionsFlags |= ResOpt_Simplify | ResOpt_Tdi;
405 		else
406 	      	   ResOptionsFlags &= ~(ResOpt_Simplify | ResOpt_Tdi);
407 	    }
408 	    return;
409 	case RES_EXTOUT:
410 	    if (cmd->tx_argc == 2)
411 	    {
412 		value = (ResOptionsFlags & ResOpt_DoExtFile) ?
413 			TRUE : FALSE;
414 		TxPrintf("%s\n", onOff[value]);
415 	    }
416 	    else
417 	    {
418 		value = Lookup(cmd->tx_argv[2], onOff);
419 		if (value)
420 	      	   ResOptionsFlags |= ResOpt_DoExtFile;
421 		else
422 	      	   ResOptionsFlags &= ~ResOpt_DoExtFile;
423 	    }
424 	    return;
425 	case RES_LUMPED:
426 	    if (cmd->tx_argc == 2)
427 	    {
428 		value = (ResOptionsFlags & ResOpt_DoLumpFile) ?
429 			TRUE : FALSE;
430 		TxPrintf("%s\n", onOff[value]);
431 	    }
432 	    else
433 	    {
434 		value = Lookup(cmd->tx_argv[2], onOff);
435 		if (value)
436 	      	   ResOptionsFlags |= ResOpt_DoLumpFile;
437 		else
438 	      	   ResOptionsFlags &= ~ResOpt_DoLumpFile;
439 	    }
440 	    return;
441 	case RES_SILENT:
442 	    if (cmd->tx_argc == 2)
443 	    {
444 		value = (ResOptionsFlags & ResOpt_RunSilent) ?
445 			TRUE : FALSE;
446 		TxPrintf("%s\n", onOff[value]);
447 	    }
448 	    else
449 	    {
450 		value = Lookup(cmd->tx_argv[2], onOff);
451 		if (value)
452 	      	   ResOptionsFlags |= ResOpt_RunSilent;
453 		else
454 	      	   ResOptionsFlags &= ~ResOpt_RunSilent;
455 	    }
456 	    return;
457 
458 	case RES_SKIP:
459 	    if (cmd->tx_argc > 2)
460 	    {
461 		j = DBTechNoisyNameType(cmd->tx_argv[2]);
462 		if (j >= 0)
463 		    for (k = TT_TECHDEPBASE; k < TT_MAXTYPES; k++)
464 			TTMaskClearType(&ResCopyMask[k], j);
465 		TTMaskZero(&(ResCopyMask[j]));
466 	    }
467 	    else
468 	    {
469     		for (i = 0; i != NT; i++)
470     		{
471 		    TTMaskZero(&(ResCopyMask[i]));
472 		    TTMaskSetMask(&ResCopyMask[i], &DBConnectTbl[i]);
473      		}
474 	    }
475 	    return;
476 
477 	case RES_IGNORE:
478 	    if (cmd->tx_argc > 2)
479 	    {
480 		if (!strcasecmp(cmd->tx_argv[2], "none"))
481 		{
482 		    /* Kill and reinitialize the table of ignored nets */
483 		    HashKill(&ResIgnoreTable);
484 		    HashInit(&ResIgnoreTable, INITFLATSIZE, HT_STRINGKEYS);
485 		}
486 		else
487 		    HashFind(&ResIgnoreTable, cmd->tx_argv[2]);
488 	    }
489 	    else
490 	    {
491 		HashSearch hs;
492 		HashEntry *entry;
493 
494 		/* List all net names that are being ignored */
495 		HashStartSearch(&hs);
496 		while((entry = HashNext(&ResIgnoreTable, &hs)) != NULL)
497 		    TxPrintf("%s ", (char *)entry->h_key.h_name);
498 		TxPrintf("\n");
499 	    }
500 	    return;
501 
502 	case RES_INCLUDE:
503 	    if (cmd->tx_argc > 2)
504 	    {
505 		if (!strcasecmp(cmd->tx_argv[2], "all"))
506 		{
507 		    /* Kill and reinitialize the table of ignored nets */
508 		    HashKill(&ResIncludeTable);
509 		    HashInit(&ResIncludeTable, INITFLATSIZE, HT_STRINGKEYS);
510 		}
511 		else
512 		    HashFind(&ResIncludeTable, cmd->tx_argv[2]);
513 	    }
514 	    else
515 	    {
516 		HashSearch hs;
517 		HashEntry *entry;
518 
519 		/* List all net names that are being included */
520 		HashStartSearch(&hs);
521 		while((entry = HashNext(&ResIncludeTable, &hs)) != NULL)
522 		    TxPrintf("%s ", (char *)entry->h_key.h_name);
523 		TxPrintf("\n");
524 	    }
525 	    return;
526 
527 	case RES_HELP:
528 	    for (i = 0; cmdExtresisCmd[i] != NULL; i++)
529 		TxPrintf("%s\n", cmdExtresisCmd[i]);
530 	    return;
531 
532 	case RES_BOX:
533 	    {
534 		TileType	tt;
535 		CellDef		*def;
536 		Rect		rect;
537 		int		oldoptions;
538 		ResSimNode	lnode;
539 
540 		if (ToolGetBoxWindow((Rect *) NULL, (int *) NULL) == NULL)
541 		{
542 		    TxError("Sorry, the box must appear in one of the windows.\n");
543 		    return;
544 		}
545 
546 	     	if (cmd->tx_argc != 3) return;
547 		tt = DBTechNoisyNameType(cmd->tx_argv[2]);
548 		if (tt <= 0 || ToolGetBox(&def, &rect)== FALSE) return;
549 		gparams.rg_devloc = &rect.r_ll;
550 		gparams.rg_ttype = tt;
551 		gparams.rg_status = DRIVEONLY;
552 		oldoptions = ResOptionsFlags;
553 		ResOptionsFlags = ResOpt_DoSubstrate | ResOpt_Signal | ResOpt_Box;
554 #ifdef LAPLACE
555 		ResOptionsFlags |= (oldoptions &
556 			    (ResOpt_CacheLaplace | ResOpt_DoLaplace));
557 		LaplaceMatchCount = 0;
558 		LaplaceMissCount = 0;
559 #endif
560 		lnode.location = rect.r_ll;
561 		lnode.type = tt;
562 		if (ResExtractNet(&lnode, &gparams, NULL) != 0) return;
563 		ResPrintResistorList(stdout, ResResList);
564 		ResPrintDeviceList(stdout, ResRDevList);
565 #ifdef LAPLACE
566 		if (ResOptionsFlags & ResOpt_DoLaplace)
567 		{
568 		    TxPrintf("Laplace   solved: %d matched %d\n",
569 				LaplaceMissCount, LaplaceMatchCount);
570 		}
571 #endif
572 
573 		ResOptionsFlags = oldoptions;
574 		return;
575 	    }
576 	case RES_CELL:
577 	    selectedUse = CmdGetSelectedCell((Transform *) NULL);
578 	    if (selectedUse == NULL)
579 	    {
580 		TxError("No cell selected\n");
581 		return;
582 	    }
583 	    mainDef = selectedUse->cu_def;
584 	    ResOptionsFlags &= ~ResOpt_ExtractAll;
585 	    break;
586 
587 	case RES_RUN:
588 	    ResOptionsFlags &= ~ResOpt_ExtractAll;
589 	    break;
590 #ifdef LAPLACE
591 	case RES_LAPLACE:
592 	    LaplaceParseString(cmd);
593 	    return;
594 #endif
595 	case RES_AMBIG:
596   	    TxPrintf("Ambiguous option: %s\n", cmd->tx_argv[1]);
597 	    TxFlushOut();
598 	    return;
599 	case RES_BAD:
600   	    TxPrintf("Unknown option: %s\n", cmd->tx_argv[1]);
601 	    TxFlushOut();
602 	    return;
603 	default:
604 	    return;
605     }
606 
607 #ifdef LAPLACE
608     LaplaceMatchCount = 0;
609     LaplaceMissCount = 0;
610 #endif
611     /* turn off undo stuff */
612     UndoDisable();
613 
614     if (!ToolGetBox(&mainDef,(Rect *) NULL))
615     {
616     	TxError("Couldn't find def corresponding to box\n");
617 	if ((option == RES_FASTHENRY) || (option == RES_GEOMETRY))
618 	    ResOptionsFlags = saveFlags;
619 	return;
620     }
621     ResOptionsFlags |= ResOpt_Signal;
622 #ifdef ARIEL
623     ResOptionsFlags &= ~ResOpt_Power;
624 #endif
625 
626     resisdata.tolerance = tolerance;
627     resisdata.tdiTolerance = tdiTolerance;
628     resisdata.frequency = fhFrequency;
629     resisdata.mainDef = mainDef;
630 
631     /* Do subcircuits (if any) first */
632     HashInit(&ResProcessedTable, INITFLATSIZE, HT_STRINGKEYS);
633     if (!(ResOptionsFlags & ResOpt_Blackbox))
634 	DBCellEnum(mainDef, resSubcircuitFunc, (ClientData) &resisdata);
635 
636     ExtResisForDef(mainDef, &resisdata);
637     HashKill(&ResProcessedTable);
638 
639     /* turn back on undo stuff */
640     UndoEnable();
641 #ifdef LAPLACE
642     if (ResOptionsFlags & ResOpt_DoLaplace)
643     {
644 	TxPrintf("Laplace solved: %d matched %d\n",
645 				LaplaceMissCount, LaplaceMatchCount);
646     }
647 #endif
648 
649     /* Revert to the original flags in the case of FastHenry or	*/
650     /* geometry centerline extraction.				*/
651 
652     if ((option == RES_FASTHENRY) || (option == RES_GEOMETRY))
653 	ResOptionsFlags = saveFlags;
654 
655     return;
656 }
657 
658 /*
659  *-------------------------------------------------------------------------
660  *
661  * resSubcircuitFunc --
662  *	For each encountered cell, call the resistance extractor,
663  *	then recursively call resSubcircuitFunc on all children
664  *	of the cell.
665  *
666  * Results:
667  *	Always return 0 to keep search alive.
668  *
669  * Side Effects:
670  *	Does resistance extraction for an entire cell.
671  *
672  *-------------------------------------------------------------------------
673  */
674 
675 int
resSubcircuitFunc(cellUse,rdata)676 resSubcircuitFunc(cellUse, rdata)
677     CellUse *cellUse;
678     ResisData *rdata;
679 {
680     CellDef *cellDef = cellUse->cu_def;
681 
682     if (DBIsSubcircuit(cellDef))
683     {
684 	ExtResisForDef(cellDef, rdata);
685 	DBCellEnum(cellDef, resSubcircuitFunc, (ClientData)rdata);
686     }
687     return 0;
688 }
689 
690 /*
691  *-------------------------------------------------------------------------
692  *
693  * Callback routine for ResCheckBlackBox.  For each label found in a
694  * subcell, transform the label position back to the top level and
695  * add to the list of nodes for extresis.
696  *
697  *-------------------------------------------------------------------------
698  */
699 
700 int
resPortFunc(scx,lab,tpath,result)701 resPortFunc(scx, lab, tpath, result)
702     SearchContext *scx;
703     Label *lab;
704     TerminalPath *tpath;
705     int *result;
706 {
707     Rect r;
708     int pclass, puse;
709     Point portloc;
710     HashEntry *entry;
711     ResSimNode *node;
712 
713     GeoTransRect(&scx->scx_trans, &lab->lab_rect, &r);
714 
715     // To be expanded.  Currently this handles digital signal inputs
716     // and outputs, for standard cells.
717 
718     if (lab->lab_flags & PORT_DIR_MASK) {
719 	pclass = lab->lab_flags & PORT_CLASS_MASK;
720 	puse = lab->lab_flags & PORT_USE_MASK;
721 
722 	// Ad hoc rule:  If port use is not declared, but port
723 	// direction is either INPUT or OUTPUT, then use SIGNAL is implied.
724 
725 	if ((puse == 0) && ((pclass == PORT_CLASS_INPUT)
726 		|| (pclass == PORT_CLASS_OUTPUT)))
727 	    puse = PORT_USE_SIGNAL;
728 
729 	if (puse == PORT_USE_SIGNAL || puse == PORT_USE_CLOCK) {
730 
731 	    if (lab->lab_flags & (PORT_DIR_NORTH | PORT_DIR_SOUTH))
732 		portloc.p_x = (r.r_xbot + r.r_xtop) >> 1;
733 	    else if (lab->lab_flags & (PORT_DIR_EAST | PORT_DIR_WEST))
734 		portloc.p_y = (r.r_ybot + r.r_ytop) >> 1;
735 
736 	    if (lab->lab_flags & PORT_DIR_NORTH)
737 		portloc.p_y = r.r_ytop;
738 	    if (lab->lab_flags & PORT_DIR_SOUTH)
739 		portloc.p_y = r.r_ybot;
740 	    if (lab->lab_flags & PORT_DIR_EAST)
741 		portloc.p_x = r.r_xtop;
742 	    if (lab->lab_flags & PORT_DIR_WEST)
743 		portloc.p_x = r.r_xbot;
744 
745 	    if ((pclass == PORT_CLASS_INPUT) || (pclass == PORT_CLASS_OUTPUT)) {
746 		int len;
747 		char *nodename;
748 
749 		// Port name is the instance name / pin name
750 		// To do:  Make use of tpath
751 		len = strlen(scx->scx_use->cu_id) + strlen(lab->lab_text) + 2;
752 		nodename = (char *) mallocMagic((unsigned) len);
753 		sprintf(nodename, "%s/%s", scx->scx_use->cu_id, lab->lab_text);
754 
755 		entry = HashFind(&ResNodeTable, nodename);
756 		node = ResInitializeNode(entry);
757 
758 		/* Digital outputs are drivers */
759 		if (pclass == PORT_CLASS_OUTPUT) node->status |= FORCE;
760 
761 		node->drivepoint = portloc;
762 		node->status |= DRIVELOC | PORTNODE;
763 		node->rs_bbox = r;
764 		node->location = portloc;
765 		node->rs_ttype = lab->lab_type;
766 		node->type = lab->lab_type;
767 
768 		*result = 0;
769 		freeMagic(nodename);
770 	    }
771 	}
772     }
773     return 0;	/* Keep the search going */
774 }
775 
776 /*
777  *-------------------------------------------------------------------------
778  *
779  * ResCheckBlackbox--
780  *
781  *	For standard cell parasitic extraction, search all children
782  *	of cellDef for ports, and add each port to the list of nodes
783  *	for extresist to process.  If the port use is "ground" or
784  *	"power", then don't process the node.  If the port class is
785  *	"output", then make this node a (forced) driver.
786  *
787  * Results: 0 if one or more nodes was created, 1 otherwise
788  *
789  * Side Effects: Adds driving nodes to the extresis network database.
790  *
791  *-------------------------------------------------------------------------
792  */
793 
794 int
ResCheckBlackbox(cellDef)795 ResCheckBlackbox(cellDef)
796     CellDef *cellDef;
797 {
798     int result = 1;
799     SearchContext scx;
800     CellUse dummy;
801 
802     dummy.cu_expandMask = 0;
803     dummy.cu_transform = GeoIdentityTransform;
804     dummy.cu_def = cellDef;
805     dummy.cu_id = NULL;
806 
807     scx.scx_area = cellDef->cd_bbox;
808     scx.scx_trans = GeoIdentityTransform;
809     scx.scx_use = (CellUse *)&dummy;
810 
811     /* Do a search on all children */
812 
813     DBTreeSrLabels(&scx, &DBAllButSpaceAndDRCBits, 0, NULL,
814 		TF_LABEL_ATTACH, resPortFunc, (ClientData)&result);
815 
816     return result;
817 }
818 
819 /*
820  *-------------------------------------------------------------------------
821  *
822  * ResCheckPorts--
823  *
824  *	Subcircuit boundaries mark an area which is to be checked
825  *	explicitly for geometry information.  Because there may be
826  *	no devices in the subcircuit cell, we must find the ports
827  *	into the subcircuit and declare them to be "driving" nodes so
828  *	the extresis algorithm will treat them as being part of valid
829  *	networks.
830  *
831  * Results: 0 if one or more nodes was created, 1 otherwise
832  *
833  * Side Effects: Adds driving nodes to the extresis network database.
834  *
835  *-------------------------------------------------------------------------
836  */
837 
838 int
ResCheckPorts(cellDef)839 ResCheckPorts(cellDef)
840     CellDef *cellDef;
841 {
842     Point portloc;
843     Label *lab;
844     HashEntry *entry;
845     ResSimNode *node;
846     int result = 1;
847 
848     for (lab = cellDef->cd_labels; lab; lab = lab->lab_next)
849     {
850 	if (lab->lab_flags & PORT_DIR_MASK)
851 	{
852 	    /* Get drivepoint from the port connection direction(s) */
853 	    /* NOTE:  This is not rigorous! */
854 
855 	    if (lab->lab_flags & (PORT_DIR_NORTH | PORT_DIR_SOUTH))
856 		portloc.p_x = (lab->lab_rect.r_xbot + lab->lab_rect.r_xtop) >> 1;
857 	    else if (lab->lab_flags & (PORT_DIR_EAST | PORT_DIR_WEST))
858 		portloc.p_y = (lab->lab_rect.r_ybot + lab->lab_rect.r_ytop) >> 1;
859 
860 	    if (lab->lab_flags & PORT_DIR_NORTH)
861 		portloc.p_y = lab->lab_rect.r_ytop;
862 	    if (lab->lab_flags & PORT_DIR_SOUTH)
863 		portloc.p_y = lab->lab_rect.r_ybot;
864 	    if (lab->lab_flags & PORT_DIR_EAST)
865 		portloc.p_x = lab->lab_rect.r_xtop;
866 	    if (lab->lab_flags & PORT_DIR_WEST)
867 		portloc.p_x = lab->lab_rect.r_xbot;
868 
869 	    entry = HashFind(&ResNodeTable, lab->lab_text);
870 	    result = 0;
871 	    if ((node = (ResSimNode *) HashGetValue(entry)) != NULL)
872 	    {
873 		TxPrintf("Port: name = %s exists, forcing drivepoint\n",
874 			lab->lab_text);
875 		TxPrintf("Location is (%d, %d); drivepoint (%d, %d)\n",
876 			node->location.p_x, node->location.p_y,
877 			portloc.p_x, portloc.p_y);
878 		TxFlush();
879 		node->drivepoint = portloc;
880 		node->status |= FORCE;
881 	    }
882 	    else
883 	    {
884 		/* This is a port, but it's merged with another node.	*/
885 		/* We have to make sure it's listed as a separate node	*/
886 		/* and a drivepoint.					*/
887 
888 		node = ResInitializeNode(entry);
889 		TxPrintf("Port: name = %s is new node 0x%x\n",
890 			lab->lab_text, node);
891 		TxPrintf("Location is (%d, %d); drivepoint (%d, %d)\n",
892 			portloc.p_x, portloc.p_y,
893 			portloc.p_x, portloc.p_y);
894 		node->location = portloc;
895 		node->drivepoint = node->location;
896 	    }
897 	    node->status |= DRIVELOC | PORTNODE;
898 	    node->rs_bbox = lab->lab_rect;
899 	    node->rs_ttype = lab->lab_type;
900 	    node->type = lab->lab_type;
901 	}
902     }
903     return result;
904 }
905 
906 /*
907  *-------------------------------------------------------------------------
908  *
909  * ResCheckSimNodes-- check to see if lumped resistance is greater than the
910  *		      device resistance; if it is, Extract the net
911  *		      resistance. If the maximum point to point resistance
912  *		      in the extracted net is still creater than the
913  *		      tolerance, then output the extracted net.
914  *
915  * Results: none
916  *
917  * Side Effects: Writes networks to .res.ext and .res.sim files.
918  *
919  *-------------------------------------------------------------------------
920  */
921 
922 void
ResCheckSimNodes(celldef,resisdata)923 ResCheckSimNodes(celldef, resisdata)
924     CellDef	*celldef;
925     ResisData	*resisdata;
926 {
927     ResSimNode	*node;
928     devPtr	*ptr;
929     float	ftolerance, rctolerance, minRes, cumRes;
930     int		failed1=0;
931     int 	failed3=0;
932     int		total =0;
933     char	*outfile = celldef->cd_name;
934     float	tol = resisdata->tolerance;
935     float	rctol = resisdata->tdiTolerance;
936     int		nidx = 1, eidx = 1;	/* node & segment counters for geom. */
937 
938     if (ResOptionsFlags & ResOpt_DoExtFile)
939     {
940 	ResExtFile = PaOpen(outfile,"w",".res.ext",".",(char *) NULL, (char **) NULL);
941     }
942     else
943     {
944 	ResExtFile = NULL;
945     }
946     if (ResOptionsFlags & ResOpt_DoLumpFile)
947     {
948         ResLumpFile = PaOpen(outfile, "w", ".res.lump", ".", (char *)NULL, (char **)NULL);
949     }
950     else
951     {
952      	ResLumpFile = NULL;
953     }
954     if (ResOptionsFlags & ResOpt_FastHenry)
955     {
956 	char *geofilename;
957         ResFHFile = PaOpen(outfile, "w", ".fh", ".", (char *)NULL, &geofilename);
958 	TxPrintf("Writing FastHenry-format geometry file \"%s\"\n", geofilename);
959 	ResPortIndex = 0;
960     }
961     else
962     {
963      	ResFHFile = NULL;
964     }
965 
966     if (ResExtFile == NULL && (ResOptionsFlags & ResOpt_DoExtFile)
967          || (ResOptionsFlags & ResOpt_DoLumpFile) && ResLumpFile == NULL
968          || (ResOptionsFlags & ResOpt_FastHenry) && ResFHFile == NULL)
969     {
970      	TxError("Couldn't open output file\n");
971 	return;
972     }
973 
974     /*
975      * Write a scale line at the top of the .res.ext file, as the
976      * scale may be different from the original .ext file.
977      */
978 
979     if (ResExtFile != NULL)
980     {
981 	fprintf(ResExtFile, "scale %d %d %g\n",
982                 ExtCurStyle->exts_resistScale,
983                 ExtCurStyle->exts_capScale,
984                 ExtCurStyle->exts_unitsPerLambda);
985     }
986 
987      /*
988       *	Write reference plane (substrate) definition and end statement
989       * to the FastHenry geometry file.
990       */
991     if (ResOptionsFlags & ResOpt_FastHenry)
992     {
993 	ResPrintReference(ResFHFile, ResRDevList, celldef);
994     }
995 
996     for (node = ResOriginalNodes; node != NULL; node=node->nextnode)
997     {
998 	HashEntry *he;
999 
1000 	if (SigInterruptPending) break;
1001 
1002 	/* Ignore or include specified nodes */
1003 
1004 	if (ResIncludeTable.ht_nEntries > 0)
1005 	{
1006 	    he = HashLookOnly(&ResIncludeTable, node->name);
1007 	    if (he == NULL) continue;
1008 	}
1009 	else
1010 	{
1011 	    he = HashLookOnly(&ResIgnoreTable, node->name);
1012 	    if (he != NULL) continue;
1013 	}
1014 
1015 	/* Has this node been merged away or is it marked as skipped? */
1016 	/* If so, skip it */
1017 	if ((node->status & (FORWARD | REDUNDANT)) ||
1018 		((node->status & SKIP) &&
1019 	  	(ResOptionsFlags & ResOpt_ExtractAll) == 0))
1020 	    continue;
1021 
1022 	ResCurrentNode = node->name;
1023 	total++;
1024 
1025      	ResSortByGate(&node->firstDev);
1026 
1027 	/* Find largest SD device connected to node. */
1028 
1029 	minRes = FLT_MAX;
1030 	gparams.rg_devloc = (Point *) NULL;
1031 	gparams.rg_status = FALSE;
1032 	gparams.rg_nodecap = node->capacitance;
1033 
1034 	/* The following is only used if there is a drivepoint */
1035 	/* to identify which tile the drivepoint is on.	       */
1036 
1037 	gparams.rg_ttype = node->rs_ttype;
1038 
1039 	for (ptr = node->firstDev; ptr != NULL; ptr = ptr->nextDev)
1040 	{
1041 	    RDev	*t1;
1042 	    RDev	*t2;
1043 
1044 	    if (ptr->terminal == GATE)
1045 	    {
1046 	       	break;
1047 	    }
1048 	    else
1049 	    {
1050 	       	/* Get cumulative resistance of all devices */
1051 		/* with same connections.		    */
1052 
1053 		cumRes = ptr->thisDev->resistance;
1054 	        t1 = ptr->thisDev;
1055 		for (; ptr->nextDev != NULL; ptr = ptr->nextDev)
1056 		{
1057 	            t1 = ptr->thisDev;
1058 		    t2 = ptr->nextDev->thisDev;
1059 		    if (t1->gate != t2->gate) break;
1060 		    if ((t1->source != t2->source ||
1061 			     t1->drain  != t2->drain) &&
1062 			    (t1->source != t2->drain ||
1063 			     t1->drain  != t2->source)) break;
1064 
1065 		    /* Do parallel combination  */
1066 		    if ((cumRes != 0.0) && (t2->resistance != 0.0))
1067 		    {
1068 			cumRes = (cumRes * t2->resistance) /
1069 			      	       (cumRes + t2->resistance);
1070 		    }
1071 		    else
1072 		    {
1073 			cumRes = 0;
1074 		    }
1075 		}
1076 		if (minRes > cumRes)
1077 		{
1078 		    minRes = cumRes;
1079 		    gparams.rg_devloc = &t1->location;
1080 		    gparams.rg_ttype = t1->rs_ttype;
1081 		}
1082 	    }
1083 	}
1084 
1085 	/* special handling for FORCE and DRIVELOC labels:  */
1086 	/* set minRes = node->minsizeres if it exists, 0 otherwise */
1087 
1088 	if (node->status & (FORCE|DRIVELOC))
1089 	{
1090 	    if (node->status & MINSIZE)
1091 	    {
1092 		minRes = node->minsizeres;
1093 	    }
1094 	    else
1095 	    {
1096 	      	minRes = 0;
1097 	    }
1098 	    if (node->status  & DRIVELOC)
1099 	    {
1100 	       	gparams.rg_devloc = &node->drivepoint;
1101 		gparams.rg_status |= DRIVEONLY;
1102 	    }
1103 	    if (node->status & PORTNODE)
1104 	    {
1105 		/* The node is a port, not a device, so make    */
1106 		/* sure rg_ttype is set accordingly.		*/
1107 		gparams.rg_ttype = node->rs_ttype;
1108 	    }
1109 	}
1110 	if ((gparams.rg_devloc == NULL) && (node->status & FORCE))
1111 	{
1112     	    TxError("Node %s has force label but no drive point or "
1113 			"driving device\n", node->name);
1114 	}
1115 	if ((minRes == FLT_MAX) || (gparams.rg_devloc == NULL))
1116 	{
1117 	    continue;
1118 	}
1119 	gparams.rg_bigdevres = (int)minRes * OHMSTOMILLIOHMS;
1120 	if ((rctol == 0.0) || (tol == 0.0))
1121 	{
1122 	    ftolerance = 0.0;
1123 	    rctolerance = 0.0;
1124 	}
1125 	else
1126 	{
1127 	    ftolerance =  minRes / tol;
1128 	    rctolerance = minRes / rctol;
1129 	}
1130 
1131 	/*
1132 	 *   Is the device resistance greater than the lumped node
1133 	 *   resistance? If so, extract net.
1134 	 */
1135 
1136 	if ((node->resistance > ftolerance) || (node->status & FORCE) ||
1137 		(ResOpt_ExtractAll & ResOptionsFlags))
1138 	{
1139 	    ResFixPoint	fp;
1140 
1141 	    failed1++;
1142 	    if (ResExtractNet(node, &gparams, outfile) != 0)
1143 	    {
1144 		/* On error, don't output this net, but keep going */
1145 	       	TxError("Error in extracting node %s\n", node->name);
1146 	    }
1147 	    else
1148 	    {
1149 		ResDoSimplify(ftolerance,rctol,&gparams);
1150 		if (ResOptionsFlags & ResOpt_DoLumpFile)
1151 		{
1152 		    ResWriteLumpFile(node);
1153 		}
1154 		if (gparams.rg_maxres >= ftolerance  ||
1155 		        gparams.rg_maxres >= rctolerance ||
1156 			(ResOptionsFlags & ResOpt_ExtractAll))
1157 		{
1158 		    resNodeNum = 0;
1159 		    failed3 += ResWriteExtFile(celldef, node, tol, rctol,
1160 				&nidx, &eidx);
1161 		}
1162 	    }
1163 #ifdef PARANOID
1164 	    ResSanityChecks(node->name, ResResList, ResNodeList, ResDevList);
1165 #endif
1166 	    ResCleanUpEverything();
1167 	}
1168     }
1169 
1170     /*
1171      * Print out all device which have had at least one terminal changed
1172      * by resistance extraction.
1173      */
1174 
1175     if (ResOptionsFlags & ResOpt_DoExtFile)
1176     {
1177 	ResPrintExtDev(ResExtFile, ResRDevList);
1178     }
1179 
1180     /*
1181      *	Write end statement to the FastHenry geometry file.
1182      * (Frequency range should be user-specified. . .)
1183      */
1184 
1185     if (ResOptionsFlags & ResOpt_FastHenry)
1186     {
1187 	Label *lab;
1188 
1189 	fprintf(ResFHFile, "\n.freq fmin=%2.1g fmax=%2.1g\n",
1190 			resisdata->frequency, resisdata->frequency);
1191 
1192 	/*----------------------------------------------------------------------*/
1193 	/* Write (in comment lines) the order in which arguments are written	*/
1194 	/* to a SPICE subcircuit call when Magic runs ext2spice (exttospice).	*/
1195 	/* At present, it is the responsibility of the program that generates	*/
1196 	/* SPICE from FastHenry output to use this information to appropriately	*/
1197 	/* order the arguments in the ".subckt" definition.			*/
1198 	/*----------------------------------------------------------------------*/
1199 
1200 	fprintf(ResFHFile, "\n* Order of arguments to SPICE subcircuit call:\n");
1201 	for (lab = celldef->cd_labels; lab != NULL; lab = lab->lab_next)
1202 	    if (lab->lab_flags & PORT_DIR_MASK)
1203 		fprintf(ResFHFile, "* %d %s\n", lab->lab_port, lab->lab_text);
1204 
1205 	fprintf(ResFHFile, "\n.end\n");
1206     }
1207 
1208     /* Output statistics about extraction */
1209 
1210     if (total)
1211     {
1212         TxPrintf("Total Nets: %d\nNets extracted: "
1213 		"%d (%f)\nNets output: %d (%f)\n", total, failed1,
1214 		(float)failed1 / (float)total, failed3,
1215 		(float)failed3 / (float)total);
1216     }
1217     else
1218     {
1219         TxPrintf("Total Nodes: %d\n",total);
1220     }
1221 
1222     /* close output files */
1223 
1224     if (ResExtFile != NULL)
1225      	(void) fclose(ResExtFile);
1226 
1227     if (ResLumpFile != NULL)
1228      	(void) fclose(ResLumpFile);
1229 
1230     if (ResFHFile != NULL)
1231 	(void) fclose(ResFHFile);
1232 }
1233 
1234 
1235 /*
1236  *-------------------------------------------------------------------------
1237  *
1238  * ResFixUpConnections-- Changes the connection to a terminal of the sim
1239  *	device.  The new name is formed by appending .t# to the old name.
1240  *	The new name is added to the hash table of node names.
1241  *
1242  * Results:none
1243  *
1244  * Side Effects: Allocates new ResSimNodes. Modifies the terminal connections
1245  *	of sim Devices.
1246  *
1247  *-------------------------------------------------------------------------
1248  */
1249 
1250 void
ResFixUpConnections(simDev,layoutDev,simNode,nodename)1251 ResFixUpConnections(simDev, layoutDev, simNode, nodename)
1252     RDev		*simDev;
1253     resDevice		*layoutDev;
1254     ResSimNode		*simNode;
1255     char		*nodename;
1256 
1257 {
1258     static char	newname[MAXNAME], oldnodename[MAXNAME];
1259     int		notdecremented;
1260     resNode	*gate, *source, *drain, *subs;
1261 
1262     /* If we aren't doing output (i.e. this is just a statistical run) */
1263     /* don't patch up networks.  This cuts down on memory use.		*/
1264 
1265     if ((ResOptionsFlags & (ResOpt_DoRsmFile | ResOpt_DoExtFile)) == 0)
1266 	return;
1267 
1268     if (simDev->layout == NULL)
1269     {
1270 	layoutDev->rd_status |= RES_DEV_SAVE;
1271 	simDev->layout = layoutDev;
1272     }
1273     simDev->status |= TRUE;
1274     if (strcmp(nodename, oldnodename) != 0)
1275     {
1276 	strcpy(oldnodename, nodename);
1277     }
1278     sprintf(newname, "%s%s%d", nodename, ".t", resNodeNum++);
1279     notdecremented = TRUE;
1280 
1281     if (simDev->gate == simNode)
1282     {
1283 	if ((gate = layoutDev->rd_fet_gate) != NULL)
1284 	{
1285 	    /* Cosmetic addition: If the layout device already has a      */
1286 	    /* name, the new one won't be used, so we decrement resNodeNum */
1287 	    if (gate->rn_name != NULL)
1288 	    {
1289 	       	resNodeNum--;
1290 		notdecremented = FALSE;
1291 	    }
1292 
1293 	    ResFixDevName(newname, GATE, simDev, gate);
1294 	    gate->rn_name = simDev->gate->name;
1295      	    sprintf(newname, "%s%s%d", nodename, ".t", resNodeNum++);
1296 	}
1297 	else
1298 	    TxError("Missing gate connection of device at (%d %d) on net %s\n",
1299 			layoutDev->rd_inside.r_xbot, layoutDev->rd_inside.r_ybot,
1300 			nodename);
1301     }
1302     if (simDev->subs == simNode)
1303     {
1304 	if ((subs = layoutDev->rd_fet_subs) != NULL)
1305 	{
1306 	    if (subs->rn_name != NULL && notdecremented)
1307 	    {
1308 	       	resNodeNum--;
1309 		notdecremented = FALSE;
1310 	    }
1311 	    ResFixDevName(newname, SUBS, simDev, subs);
1312 	    subs->rn_name = simDev->subs->name;
1313      	    sprintf(newname, "%s%s%d", nodename, ".t", resNodeNum++);
1314 	}
1315 	else
1316 	    TxError("Missing substrate connection of device at (%d %d) on net %s\n",
1317 			layoutDev->rd_inside.r_xbot, layoutDev->rd_inside.r_ybot,
1318 			nodename);
1319     }
1320     if (simDev->source == simNode)
1321     {
1322      	if (simDev->drain == simNode)
1323 	{
1324 	    if (((source = layoutDev->rd_fet_source) != NULL) &&
1325 	       	   ((drain = layoutDev->rd_fet_drain) != NULL))
1326 	    {
1327 	        if (source->rn_name != NULL && notdecremented)
1328 		{
1329 		    resNodeNum--;
1330 		    notdecremented = FALSE;
1331 		}
1332 	        ResFixDevName(newname, SOURCE, simDev, source);
1333 	        source->rn_name = simDev->source->name;
1334 		(void)sprintf(newname, "%s%s%d", nodename, ".t", resNodeNum++);
1335 	        if (drain->rn_name != NULL)  resNodeNum--;
1336 	        ResFixDevName(newname, DRAIN, simDev, drain);
1337 	        drain->rn_name = simDev->drain->name;
1338 	       	/* one to each */
1339 	    }
1340 	    else
1341 		TxError("Missing terminal connection of device at (%d %d) on net %s\n",
1342 			layoutDev->rd_inside.r_xbot, layoutDev->rd_inside.r_ybot,
1343 			nodename);
1344 	}
1345 	else
1346 	{
1347 	    if ((source = layoutDev->rd_fet_source) != NULL)
1348 	    {
1349 		if ((drain = layoutDev->rd_fet_drain) != NULL)
1350 		{
1351 		    if (source != drain)
1352 		    {
1353 			if (drain->rn_why & RES_NODE_ORIGIN)
1354 			{
1355 			   ResMergeNodes(drain, source, &ResNodeQueue,
1356 					&ResNodeList);
1357 		  	   ResDoneWithNode(drain);
1358 			   source = drain;
1359 			}
1360 			else
1361 			{
1362 			   ResMergeNodes(source, drain, &ResNodeQueue,
1363 					&ResNodeList);
1364 		  	   ResDoneWithNode(source);
1365 			   drain = source;
1366 			}
1367 		    }
1368 		    layoutDev->rd_fet_drain = (resNode *)NULL;
1369 	            if (source->rn_name != NULL)  resNodeNum--;
1370 	            ResFixDevName(newname, SOURCE, simDev, source);
1371 	            source->rn_name = simDev->source->name;
1372 		}
1373 		else
1374 		{
1375              	    if (source->rn_name != NULL && notdecremented)
1376 		    {
1377 			resNodeNum--;
1378 			notdecremented = FALSE;
1379 		    }
1380 	            ResFixDevName(newname, SOURCE, simDev, source);
1381 	            source->rn_name = simDev->source->name;
1382 		}
1383 
1384 	    }
1385 	    else
1386 	       	TxError("Missing terminal connection of device at (%d %d) on net %s\n",
1387 			layoutDev->rd_inside.r_xbot, layoutDev->rd_inside.r_ybot,
1388 			nodename);
1389 	}
1390     }
1391     else if (simDev->drain == simNode)
1392     {
1393 	if ((source = layoutDev->rd_fet_source) != NULL)
1394 	{
1395 	    if ((drain = layoutDev->rd_fet_drain) != NULL)
1396 	    {
1397 		if (drain != source)
1398 		{
1399 		    if (drain->rn_why & ORIGIN)
1400 		    {
1401 			 ResMergeNodes(drain, source, &ResNodeQueue,
1402 				&ResNodeList);
1403 		         ResDoneWithNode(drain);
1404 			 source = drain;
1405 		    }
1406   		    else
1407 		    {
1408  		         ResMergeNodes(source, drain, &ResNodeQueue,
1409 				&ResNodeList);
1410 		         ResDoneWithNode(source);
1411 			 drain = source;
1412 		    }
1413 		}
1414 		layoutDev->rd_fet_source = (resNode *) NULL;
1415              	if (drain->rn_name != NULL)
1416 		{
1417 		    resNodeNum--;
1418 		    notdecremented = FALSE;
1419 		}
1420 	        ResFixDevName(newname, DRAIN, simDev, drain);
1421 	        drain->rn_name = simDev->drain->name;
1422 	    }
1423 	    else
1424 	    {
1425 		if (source->rn_name != NULL  && notdecremented)
1426 		{
1427 		    resNodeNum--;
1428 		    notdecremented = FALSE;
1429 		}
1430 		ResFixDevName(newname,DRAIN,simDev,source);
1431 		source->rn_name = simDev->drain->name;
1432 	    }
1433 	}
1434 	else
1435 	    TxError("Missing terminal connection of device at (%d %d) on net %s\n",
1436 			layoutDev->rd_inside.r_xbot, layoutDev->rd_inside.r_ybot,
1437 			nodename);
1438     }
1439     else
1440 	resNodeNum--;
1441 }
1442 
1443 
1444 /*
1445  *-------------------------------------------------------------------------
1446  *
1447  *  ResFixDevName-- Moves device connection to new node.
1448  *
1449  * Results:
1450  *	None.
1451  *
1452  * Side Effects: May create a new node. Creates a new device pointer.
1453  *
1454  *-------------------------------------------------------------------------
1455  */
1456 
1457 void
ResFixDevName(line,type,device,layoutnode)1458 ResFixDevName(line, type, device, layoutnode)
1459     char 	line[];
1460     int		type;
1461     RDev	*device;
1462     resNode	*layoutnode;
1463 
1464 {
1465     HashEntry		*entry;
1466     ResSimNode		*node;
1467     devPtr		*tptr;
1468 
1469     if (layoutnode->rn_name != NULL)
1470     {
1471         entry = HashFind(&ResNodeTable, layoutnode->rn_name);
1472         node = ResInitializeNode(entry);
1473 
1474     }
1475     else
1476     {
1477         entry = HashFind(&ResNodeTable, line);
1478         node = ResInitializeNode(entry);
1479     }
1480     tptr = (devPtr *) mallocMagic((unsigned) (sizeof(devPtr)));
1481     tptr->thisDev = device;
1482     tptr->nextDev = node->firstDev;
1483     node->firstDev = tptr;
1484     tptr->terminal = type;
1485     switch(type)
1486     {
1487      	case GATE:
1488 	    node->oldname = device->gate->name;
1489 	    device->gate = node;
1490 	    break;
1491      	case SOURCE:
1492 	    node->oldname = device->source->name;
1493 	    device->source = node;
1494 	    break;
1495      	case DRAIN:
1496 	    node->oldname = device->drain->name;
1497 	    device->drain = node;
1498 	    break;
1499      	case SUBS:
1500 	    node->oldname = device->subs->name;
1501 	    device->subs = node;
1502 	    break;
1503 	default:
1504 	    TxError("Bad Terminal Specifier\n");
1505 	    break;
1506     }
1507 }
1508 
1509 /*
1510  *-------------------------------------------------------------------------
1511  *
1512  * devSortFunc ---
1513  *
1514  *	qsort() sorting function for gates.  See description in
1515  *	ResSortByGate() below.
1516  *
1517  * Returns:
1518  *	1 or -1 depending on comparison result.  The devices are sorted
1519  *	by gate first, then source or drain.
1520  *
1521  * Side effects:
1522  *	qsort() reorders the indexed list of which dev1 and dev2 are
1523  *	components.
1524  *
1525  *-------------------------------------------------------------------------
1526  */
1527 
1528 int
devSortFunc(rec1,rec2)1529 devSortFunc(rec1, rec2)
1530     devPtr **rec1, **rec2;
1531 {
1532     devPtr *dev1 = *rec1;
1533     devPtr *dev2 = *rec2;
1534     RDev *rd1 = dev1->thisDev;
1535     RDev *rd2 = dev2->thisDev;
1536 
1537     if (dev1->terminal == GATE)
1538 	return 1;
1539     else if (dev2->terminal == GATE)
1540 	return -1;
1541     else if (rd1->gate > rd2->gate)
1542     	return 1;
1543     else if (rd1->gate == rd2->gate)
1544     {
1545 	if ((dev1->terminal == SOURCE &&
1546 		dev2->terminal == SOURCE &&
1547 		rd1->drain > rd2->drain)    ||
1548 		(dev1->terminal == SOURCE &&
1549 		dev2->terminal == DRAIN &&
1550 		rd1->drain > rd2->source)    ||
1551 		(dev1->terminal == DRAIN &&
1552 		dev2->terminal == SOURCE &&
1553 		rd1->source > rd2->drain)    ||
1554 		(dev1->terminal == DRAIN &&
1555 		dev2->terminal == DRAIN &&
1556 		rd1->source >  rd2->source))
1557 	{
1558 	    return 1;
1559 	}
1560     }
1561     return -1;
1562 }
1563 
1564 /*
1565  *-------------------------------------------------------------------------
1566  *
1567  *  ResSortByGate -- sorts device pointers whose terminal field is either
1568  *	drain or source by gate node number, then by drain (source) number.
1569  *	This places devices with identical connections next to one
1570  *	another.
1571  *
1572  * Results: none
1573  *
1574  * Side Effects: modifies order of devices
1575  *
1576  *-------------------------------------------------------------------------
1577  */
1578 
1579 void
ResSortByGate(DevpointerList)1580 ResSortByGate(DevpointerList)
1581     devPtr	**DevpointerList;
1582 {
1583     devPtr	*working, **Devindexed;
1584     int		listlen, listidx;
1585 
1586     /* Linked lists are very slow to sort.  Create an indexed list  */
1587     /* and run qsort() to sort, then regenerate the links.	    */
1588 
1589     listlen = 0;
1590     for (working = *DevpointerList; working; working = working->nextDev) listlen++;
1591     if (listlen == 0) return;
1592 
1593     Devindexed = (devPtr **)mallocMagic(listlen * sizeof(devPtr *));
1594     listidx = 0;
1595     for (working = *DevpointerList; working; working = working->nextDev)
1596 	Devindexed[listidx++] = working;
1597 
1598     qsort(Devindexed, (size_t)listlen, (size_t)sizeof(devPtr *), devSortFunc);
1599 
1600     for (listidx = 0; listidx < listlen - 1; listidx++)
1601 	Devindexed[listidx]->nextDev = Devindexed[listidx + 1];
1602     Devindexed[listidx]->nextDev = NULL;
1603 
1604     *DevpointerList = Devindexed[0];
1605     freeMagic(Devindexed);
1606 }
1607 
1608 /*
1609  *-------------------------------------------------------------------------
1610  *
1611  * ResWriteLumpFile
1612  *
1613  * Results: none
1614  *
1615  * Side Effects:
1616  *
1617  *-------------------------------------------------------------------------
1618  */
1619 
1620 void
ResWriteLumpFile(node)1621 ResWriteLumpFile(node)
1622     ResSimNode	*node;
1623 {
1624     int	lumpedres;
1625 
1626     if (ResOptionsFlags & ResOpt_Tdi)
1627     {
1628 	if (gparams.rg_nodecap != 0)
1629 	{
1630 	    lumpedres = (int)((gparams.rg_Tdi / gparams.rg_nodecap
1631 			- (float)(gparams.rg_bigdevres)) / OHMSTOMILLIOHMS);
1632 	}
1633 	else
1634 	    lumpedres = 0;
1635     }
1636     else
1637     {
1638 	lumpedres = gparams.rg_maxres;
1639     }
1640     fprintf(ResLumpFile, "R %s %d\n", node->name, lumpedres);
1641 }
1642 
1643 
1644 /*
1645  *-------------------------------------------------------------------------
1646  *
1647  * ResAlignNodes --
1648  *	Attempt to put nodes onto a Manhattan grid.
1649  *	At the same time, assign height values to nodes and thickness
1650  *	values to resistors.
1651  *
1652  *-------------------------------------------------------------------------
1653  */
1654 
1655 void
ResAlignNodes(nodelist,reslist)1656 ResAlignNodes(nodelist, reslist)
1657     resNode	*nodelist;
1658     resResistor *reslist;
1659 {
1660     resResistor *resistor;
1661     resNode	*node1;
1662     short	i;
1663 
1664     for (resistor = reslist; resistor->rr_nextResistor != NULL;
1665 		resistor = resistor->rr_nextResistor)
1666     {
1667 	/* Don't try to align nodes which came from split */
1668 	/* tiles;  we assume that the geometry there is	  */
1669 	/* supposed to be non-Manhattan.		  */
1670 
1671 	if (resistor->rr_status & RES_DIAGONAL) continue;
1672 
1673 	for (i = 0; i < 2; i++)
1674 	{
1675 	    node1 = resistor->rr_node[i];
1676 	    if (resistor->rr_status & RES_EW)
1677 	    {
1678 		if (node1->rn_loc.p_y != resistor->rr_cl)
1679 		{
1680 		    if (node1->rn_status & RES_NODE_YADJ)
1681 			TxError("Warning: contention over node Y position\n");
1682 		    node1->rn_loc.p_y = resistor->rr_cl;
1683 		    node1->rn_status |= RES_NODE_YADJ;
1684 		}
1685 	    }
1686 	    else if (resistor->rr_status & RES_NS)
1687 	    {
1688 		if (node1->rn_loc.p_x != resistor->rr_cl)
1689 		{
1690 		    if (node1->rn_status & RES_NODE_XADJ)
1691 			TxError("Warning: contention over node X position\n");
1692 		    node1->rn_loc.p_x = resistor->rr_cl;
1693 		    node1->rn_status |= RES_NODE_XADJ;
1694 		}
1695 	    }
1696 	}
1697     }
1698 }
1699 
1700 /*
1701  *-------------------------------------------------------------------------
1702  *
1703  * ResWriteExtFile --
1704  *
1705  * Results:
1706  *	1 if output was generated
1707  *	0 if no output was generated
1708  *
1709  * Side Effects:
1710  *
1711  *-------------------------------------------------------------------------
1712  */
1713 
1714 int
ResWriteExtFile(celldef,node,tol,rctol,nidx,eidx)1715 ResWriteExtFile(celldef, node, tol, rctol, nidx, eidx)
1716     CellDef	*celldef;
1717     ResSimNode	*node;
1718     float	tol, rctol;
1719     int		*nidx, *eidx;
1720 {
1721     float	RCdev;
1722     char	*cp, newname[MAXNAME];
1723     devPtr	*ptr;
1724     resDevice	*layoutDev, *ResGetDevice();
1725 
1726     RCdev = gparams.rg_bigdevres * gparams.rg_nodecap;
1727 
1728     if (tol == 0.0 || (node->status & FORCE) ||
1729 		(ResOptionsFlags & ResOpt_ExtractAll) ||
1730 		(ResOptionsFlags & ResOpt_Simplify) == 0 ||
1731 		(rctol + 1) * RCdev < rctol * gparams.rg_Tdi)
1732     {
1733 	ASSERT(gparams.rg_Tdi != -1, "ResWriteExtFile");
1734 	(void)sprintf(newname,"%s", node->name);
1735         cp = newname + strlen(newname)-1;
1736         if (*cp == '!' || *cp == '#') *cp = '\0';
1737 	if ((rctol + 1) * RCdev < rctol * gparams.rg_Tdi ||
1738 	  			(ResOptionsFlags & ResOpt_Tdi) == 0)
1739 	{
1740 	    if ((ResOptionsFlags & (ResOpt_RunSilent | ResOpt_Tdi)) == ResOpt_Tdi)
1741 	    {
1742 		TxPrintf("Adding  %s; Tnew = %.2fns, Told = %.2fns\n",
1743 		     	    node->name, gparams.rg_Tdi / Z_TO_P, RCdev / Z_TO_P);
1744 	    }
1745         }
1746         for (ptr = node->firstDev; ptr != NULL; ptr=ptr->nextDev)
1747         {
1748 	    if (layoutDev = ResGetDevice(&ptr->thisDev->location, ptr->thisDev->rs_ttype))
1749 	    {
1750 		ResFixUpConnections(ptr->thisDev, layoutDev, node, newname);
1751 	    }
1752 	}
1753         if (ResOptionsFlags & ResOpt_DoExtFile)
1754         {
1755 	    ResPrintExtNode(ResExtFile, ResNodeList, node->name);
1756       	    ResPrintExtRes(ResExtFile, ResResList, newname);
1757         }
1758 	if (ResOptionsFlags & ResOpt_FastHenry)
1759 	{
1760 	    if (ResResList)
1761 		ResAlignNodes(ResNodeList, ResResList);
1762 	    ResPrintFHNodes(ResFHFile, ResNodeList, node->name, nidx, celldef);
1763 	    ResPrintFHRects(ResFHFile, ResResList, newname, eidx);
1764 	}
1765 	if (ResOptionsFlags & ResOpt_Geometry)
1766 	{
1767 	    if (ResResList)
1768 		ResAlignNodes(ResNodeList, ResResList);
1769 	    if (ResCreateCenterlines(ResResList, nidx, celldef) < 0)
1770 		return 0;
1771 	}
1772 	return 1;
1773     }
1774     else return 0;
1775 }
1776 
1777