1 /*
2  * Electric(tm) VLSI Design System
3  *
4  * File: logeffort.cpp
5  * Logical effort timing and sizing tool
6  * Written by: Steven M. Rubin
7  *
8  * Copyright (c) 2000 Static Free Software.
9  *
10  * Electric(tm) is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * Electric(tm) is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Electric(tm); see the file COPYING.  If not, write to
22  * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
23  * Boston, Mass 02111-1307, USA.
24  *
25  * Static Free Software
26  * 4119 Alpine Road
27  * Portola Valley, California 94028
28  * info@staticfreesoft.com
29  *
30  * This module is inspired by the book "Logical Effort"
31  * by Ivan Sutherland, Bob Sproull, and David Harris
32  * Morgan Kaufmann, San Francisco, 1999.
33  */
34 
35 #include "config.h"
36 #if LOGEFFTOOL
37 
38 #include "global.h"
39 #include "efunction.h"
40 #include "egraphics.h"
41 #include "edialogs.h"
42 #include "logeffort.h"
43 #include "network.h"
44 #include "usr.h"
45 #include <math.h>
46 
47 /* the LOGICAL EFFORT tool table */
48 static KEYWORD leopt[] =
49 {
50 	{x_("analyze-path"),       0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
51 	{x_("analyze-cell"),       0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
52 	{x_("analyze-network"),    0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
53 	{x_("show-loads"),         0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
54 	{x_("estimate-delay"),     0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
55 	{x_("set-options"),        0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
56 	{x_("set-capacitance"),    0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
57 	{x_("set-node-effort"),    0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
58 	TERMKEY
59 };
60 extern "C" {
61 COMCOMP le_tablep = {leopt, NOTOPLIST, NONEXTLIST, NOPARAMS,
62 	0, x_(" \t"), M_("Logical effort action"), M_("show defaults")};
63 }
64 
65 #define MAXITERATIONS  10
66 #define MAXSTAGEEFFORT  3.0
67 
68 /* the meaning of LENODE->type */
69 #define LEUNKNOWN    0
70 #define LETERMINAL   1
71 #define LEINVERTER   2
72 #define LENAND       3
73 #define LENOR        4
74 #define LEXOR        5
75 #define LEXNOR       6
76 #define LENMOS       7
77 #define LEPMOS       8
78 #define LEPOWER      9
79 #define LEGROUND    10
80 
81 #define NOLENODE ((LENODE *)-1)
82 
83 typedef struct Ilenode
84 {
85 	NODEINST        *ni;				/* node in database */
86 	PORTARCINST     *piin, *piout;		/* input and output ports on that NODEINST */
87 	double           cin, cout;			/* input and output capacitances */
88 	double           logeffort;			/* logical effort of the node (g) */
89 	double           parasitics;		/* parasitics on the node (p) */
90 	double           branching;			/* branching effort of the node (b) */
91 	INTBIG           type;				/* node type (see above) */
92 	INTBIG           inputs;			/* number of inputs */
93 	struct Ilenode  *nextlenode;		/* next in linked list */
94 	struct Ilenode  *prevlenode;		/* previous in linked list */
95 
96 	struct Ilenode **inputnodes;		/* array of input nodes */
97 	PORTARCINST    **inputports;		/* array of input ports on nodes */
98 	struct Ilenode **outputnodes;		/* array of output nodes */
99 	PORTARCINST    **outputports;		/* array of output ports on nodes */
100 	INTBIG           numinputnodes;		/* size of array of input nodes */
101 	INTBIG           numoutputnodes;	/* size of array of output nodes */
102 } LENODE;
103 
104        TOOL     *le_tool;					/* this tool */
105 static INTBIG    le_attrcapacitance_key;	/* variable key for "ATTR_Capacitance" */
106 static INTBIG    le_nodeeffort_key;			/* variable key for "LE_node_effort" */
107 static INTBIG    le_fanout_key;				/* variable key for "LE_fanout" */
108 static INTBIG    le_state_key;				/* variable key for "LE_state" */
109        INTBIG    le_wire_ratio_key;			/* variable key for "LE_wire_ratio" */
110 static INTBIG    le_maximumstageeffort_key;	/* variable key for "LE_maximum_stage_effort" */
111 static LENODE   *le_lastlenode;				/* for propagating capacitance values */
112 static double    le_lastcapacitance;		/* for propagating capacitance values */
113 static LENODE   *le_firstlenode;			/* first in list of LE nodes in the path */
114 static LENODE   *le_lenodefree;				/* list of free LE nodes */
115 
116 /* prototypes for local routines */
117 static void      le_addlinkage(LENODE *out, PORTARCINST *outpi, LENODE *in, PORTARCINST *inpi);
118 static LENODE   *le_alloclenode(void);
119 static void      le_analyzecell(void);
120 static void      le_analyzenetwork(void);
121 static void      le_analyzepath(void);
122 static CHAR     *le_describenode(LENODE *le);
123 static void      le_estimatedelay(NODEPROTO *cell);
124 static void      le_figurebranching(void);
125 static void      le_freealllenodes(void);
126 static void      le_freelenode(LENODE *le);
127 static void      le_gathercell(INTBIG show);
128 static void      le_gatherpath(INTBIG show);
129 static double    le_getcapacitance(NETWORK *net);
130 static INTBIG    le_getgatetype(NODEINST *ni, INTBIG *inputs);
131 static double    le_getlogeffort(LENODE *le);
132 static double    le_getparasitics(LENODE *le);
133 static CHAR     *le_nextarcs(void);
134 static NODEINST *le_propagate(NODEPROTO *cell, INTBIG edge);
135 static void      le_propagatebranch(PORTARCINST *tpi, LENODE *final, double capacitance,
136 					double *onpath, double *offpath);
137 static void      le_setarccapacitance(ARCINST *ai, double c);
138 static void      le_setlogicaleffort(void);
139 static void      le_setoptions(void);
140 static void      le_showloads(void);
141 static int       le_sortbyproductsize(const void *e1, const void *e2);
142 static BOOLEAN   le_topofarcs(CHAR **c);
143 static void      le_unwind(NODEINST *start, NODEINST *prev);
144 
145 /************************ CONTROL ***********************/
146 
147 /*
148  * tool initialization
149  */
le_init(INTBIG * argc,CHAR1 * argv[],TOOL * thistool)150 void le_init(INTBIG *argc, CHAR1 *argv[], TOOL *thistool)
151 {
152 	/* ignore pass 3 initialization */
153 	if (thistool == 0) return;
154 
155 	/* ignore pass 2 initialization */
156 	if (thistool == NOTOOL)
157 	{
158 		le_attrcapacitance_key = makekey(x_("ATTR_Capacitance"));
159 		le_nodeeffort_key = makekey(x_("LE_node_effort"));
160 		le_fanout_key = makekey(x_("LE_fanout"));
161 		le_state_key = makekey(x_("LE_state"));
162 		le_wire_ratio_key = makekey(x_("LE_wire_ratio"));
163 		le_maximumstageeffort_key = makekey(x_("LE_maximum_stage_effort"));
164 		le_firstlenode = NOLENODE;
165 		le_lenodefree = NOLENODE;
166 		nextchangequiet();
167 		setvalkey((INTBIG)le_tool, VTOOL, le_state_key, DEFAULTSTATE, VINTEGER|VDONTSAVE);
168 		return;
169 	}
170 
171 	/* copy tool pointer during pass 1 */
172 	le_tool = thistool;
173 }
174 
le_done(void)175 void le_done(void)
176 {
177 #ifdef DEBUGMEMORY
178 	LENODE *le;
179 
180 	le_freealllenodes();
181 
182 	while (le_lenodefree != NOLENODE)
183 	{
184 		le = le_lenodefree;
185 		le_lenodefree = le_lenodefree->nextlenode;
186 		efree((CHAR *)le);
187 	}
188 #endif
189 }
190 
191 /*
192  * Handle commands to the tool from the user.
193  */
le_set(INTBIG count,CHAR * par[])194 void le_set(INTBIG count, CHAR *par[])
195 {
196 	REGISTER INTBIG l;
197 	REGISTER VARIABLE *var;
198 	REGISTER NODEPROTO *np;
199 	REGISTER INTBIG state;
200 	REGISTER CHAR *pp;
201 
202 	if (count == 0)
203 	{
204 		ttyputusage(x_("telltool logeffort (analyze-path | analyze-cell | analyze-network | show-loads | set-options | set-capacitance | set-node-effort)"));
205 		return;
206 	}
207 
208 	l = estrlen(pp = par[0]);
209 	if (namesamen(pp, x_("analyze-cell"), l) == 0 && l >= 9)
210 	{
211 		var = getvalkey((INTBIG)le_tool, VTOOL, VINTEGER, le_state_key);
212 		if (var == NOVARIABLE) state = DEFAULTSTATE; else
213 			state = var->addr;
214 		le_gathercell(state & HIGHLIGHTCOMPONENTS);
215 		if (le_firstlenode == NOLENODE) return;
216 		le_analyzecell();
217 		return;
218 	}
219 	if (namesamen(pp, x_("analyze-path"), l) == 0 && l >= 9)
220 	{
221 		var = getvalkey((INTBIG)le_tool, VTOOL, VINTEGER, le_state_key);
222 		if (var == NOVARIABLE) state = DEFAULTSTATE; else
223 			state = var->addr;
224 		le_gatherpath(state & HIGHLIGHTCOMPONENTS);
225 		if (le_firstlenode == NOLENODE) return;
226 		le_analyzepath();
227 		return;
228 	}
229 	if (namesamen(pp, x_("estimate-delay"), l) == 0)
230 	{
231 		/* analyze cell */
232 		np = getcurcell();
233 		if (np == NONODEPROTO) return;
234 		le_estimatedelay(np);
235 		return;
236 	}
237 	if (namesamen(pp, x_("show-loads"), l) == 0 && l >= 2)
238 	{
239 		le_showloads();
240 		return;
241 	}
242 	if (namesamen(pp, x_("analyze-network"), l) == 0 && l >= 2)
243 	{
244 		le_analyzenetwork();
245 		return;
246 	}
247 	if (namesamen(pp, x_("set-options"), l) == 0 && l >= 5)
248 	{
249 		le_setoptions();
250 		return;
251 	}
252 	if (namesamen(pp, x_("set-node-effort"), l) == 0 && l >= 5)
253 	{
254 		le_setlogicaleffort();
255 		return;
256 	}
257 
258 	ttyputbadusage(x_("telltool logeffort"));
259 }
260 
261 /* Logical Effort Options dialog */
262 static DIALOGITEM le_leoptionsdialogitems[] =
263 {
264  /*  1 */ {0, {196,204,220,268}, BUTTON, N_("OK")},
265  /*  2 */ {0, {132,204,156,268}, BUTTON, N_("Cancel")},
266  /*  3 */ {0, {8,8,24,160}, MESSAGE, N_("Maximum Stage Gain")},
267  /*  4 */ {0, {8,168,24,220}, EDITTEXT, x_("")},
268  /*  5 */ {0, {36,8,52,244}, CHECK, N_("Display intermediate capacitances")},
269  /*  6 */ {0, {64,8,80,192}, CHECK, N_("Highlight components")},
270  /*  7 */ {0, {112,8,220,180}, SCROLL, x_("")},
271  /*  8 */ {0, {224,8,240,92}, MESSAGE, N_("Wire ratio:")},
272  /*  9 */ {0, {224,96,240,180}, EDITTEXT, x_("")},
273  /* 10 */ {0, {92,8,108,180}, MESSAGE, N_("Wire ratio for each layer:")}
274 };
275 static DIALOG le_leoptionsdialog = {{75,75,324,352}, N_("Logical Effort Options"), 0, 10, le_leoptionsdialogitems, 0, 0};
276 
277 /* special items for the "Logical Effort Options" dialog: */
278 #define DLEO_MAXGAIN    4		/* Maximum Stage Gain (edit text) */
279 #define DLEO_INTERCAP   5		/* Show intermediate capacitances (check) */
280 #define DLEO_HIGHCOMP   6		/* Highlight components (check) */
281 #define DLEO_ARCLIST    7		/* List of arcs (scroll) */
282 #define DLEO_WIRERATIO  9		/* Arc wire ratio (edit text) */
283 
284 static ARCPROTO *le_posarcs;
le_topofarcs(CHAR ** c)285 BOOLEAN le_topofarcs(CHAR **c)
286 {
287 	le_posarcs = el_curtech->firstarcproto;
288 	return(TRUE);
289 }
290 
le_nextarcs(void)291 CHAR *le_nextarcs(void)
292 {
293 	REGISTER ARCPROTO *ap;
294 	REGISTER void *infstr;
295 
296 	ap = le_posarcs;
297 	if (ap != NOARCPROTO)
298 	{
299 		le_posarcs = ap->nextarcproto;
300 		infstr = initinfstr();
301 		formatinfstr(infstr, x_("%s (%ld)"), describearcproto(ap), ap->temp1);
302 		return(returninfstr(infstr));
303 	}
304 	return(0);
305 }
306 
307 /*
308  * Routine to interactively set the logical effort options.
309  */
le_setoptions(void)310 void le_setoptions(void)
311 {
312 	INTBIG itemHit;
313 	INTBIG state, newstate, i, lineno;
314 	float maxstageeffort, newmaxstageeffort;
315 	REGISTER VARIABLE *var;
316 	REGISTER ARCPROTO *ap;
317 	CHAR line[200], *pt;
318 	REGISTER void *infstr, *dia;
319 
320 	dia = DiaInitDialog(&le_leoptionsdialog);
321 	if (dia == 0) return;
322 	var = getvalkey((INTBIG)le_tool, VTOOL, VINTEGER, le_state_key);
323 	if (var == NOVARIABLE) state = DEFAULTSTATE; else
324 		state = var->addr;
325 	if ((state&DISPLAYCAPACITANCE) != 0) DiaSetControl(dia, DLEO_INTERCAP, 1);
326 	if ((state&HIGHLIGHTCOMPONENTS) != 0) DiaSetControl(dia, DLEO_HIGHCOMP, 1);
327 	var = getvalkey((INTBIG)le_tool, VTOOL, VFLOAT, le_maximumstageeffort_key);
328 	if (var == NOVARIABLE) maxstageeffort = MAXSTAGEEFFORT; else
329 		maxstageeffort = castfloat(var->addr);
330 
331 	/* handle wire ratios */
332 	for(ap = el_curtech->firstarcproto; ap != NOARCPROTO; ap = ap->nextarcproto)
333 	{
334 		var = getvalkey((INTBIG)ap, VARCPROTO, VINTEGER, le_wire_ratio_key);
335 		if (var != NOVARIABLE) ap->temp1 = var->addr; else
336 			ap->temp1 = DEFWIRERATIO;
337 	}
338 	DiaInitTextDialog(dia, DLEO_ARCLIST, le_topofarcs, le_nextarcs,
339 		DiaNullDlogDone, 0, SCSELMOUSE|SCREPORT);
340 
341 	esnprintf(line, 200, x_("%g"), maxstageeffort);
342 	DiaSetText(dia, -DLEO_MAXGAIN, line);
343 	for(;;)
344 	{
345 		itemHit = DiaNextHit(dia);
346 		if (itemHit == OK || itemHit == CANCEL) break;
347 		if (itemHit == DLEO_INTERCAP || itemHit == DLEO_HIGHCOMP)
348 		{
349 			DiaSetControl(dia, itemHit, 1 - DiaGetControl(dia, itemHit));
350 			continue;
351 		}
352 		if (itemHit == DLEO_ARCLIST)
353 		{
354 			lineno = DiaGetCurLine(dia, DLEO_ARCLIST);
355 			if (lineno < 0) continue;
356 			estrcpy(line, DiaGetScrollLine(dia, DLEO_ARCLIST, lineno));
357 			for(pt = line; *pt != 0; pt++) if (*pt == ' ') break;
358 			*pt = 0;
359 			ap = getarcproto(line);
360 			if (ap == NOARCPROTO) continue;
361 			esnprintf(line, 200, x_("%ld"), ap->temp1);
362 			DiaSetText(dia, DLEO_WIRERATIO, line);
363 			continue;
364 		}
365 		if (itemHit == DLEO_WIRERATIO)
366 		{
367 			lineno = DiaGetCurLine(dia, DLEO_ARCLIST);
368 			if (lineno < 0) continue;
369 			estrcpy(line, DiaGetScrollLine(dia, DLEO_ARCLIST, lineno));
370 			for(pt = line; *pt != 0; pt++) if (*pt == ' ') break;
371 			*pt = 0;
372 			ap = getarcproto(line);
373 			if (ap == NOARCPROTO) continue;
374 			i = eatoi(DiaGetText(dia, DLEO_WIRERATIO));
375 			if (i == ap->temp1) continue;
376 			ap->temp1 = i;
377 			infstr = initinfstr();
378 			formatinfstr(infstr, x_("%s (%ld)"), describearcproto(ap), ap->temp1);
379 			DiaSetScrollLine(dia, DLEO_ARCLIST, lineno, returninfstr(infstr));
380 			continue;
381 		}
382 	}
383 	if (itemHit == OK)
384 	{
385 		newstate = 0;
386 		if (DiaGetControl(dia, DLEO_INTERCAP) != 0) newstate |= DISPLAYCAPACITANCE;
387 		if (DiaGetControl(dia, DLEO_HIGHCOMP) != 0) newstate |= HIGHLIGHTCOMPONENTS;
388 		if (newstate != state)
389 			(void)setvalkey((INTBIG)le_tool, VTOOL, le_state_key, newstate, VINTEGER);
390 		newmaxstageeffort = (float)eatof(DiaGetText(dia, DLEO_MAXGAIN));
391 		if (newmaxstageeffort != maxstageeffort)
392 			(void)setvalkey((INTBIG)le_tool, VTOOL, le_maximumstageeffort_key,
393 				castint(newmaxstageeffort), VFLOAT);
394 		for(ap = el_curtech->firstarcproto; ap != NOARCPROTO; ap = ap->nextarcproto)
395 		{
396 			var = getvalkey((INTBIG)ap, VARCPROTO, VINTEGER, le_wire_ratio_key);
397 			if (var != NOVARIABLE) i = var->addr; else
398 				i = DEFWIRERATIO;
399 			if (i == ap->temp1) continue;
400 			if (ap->temp1 == DEFWIRERATIO)
401 			{
402 				delvalkey((INTBIG)ap, VARCPROTO, le_wire_ratio_key);
403 			} else
404 			{
405 				setvalkey((INTBIG)ap, VARCPROTO, le_wire_ratio_key, ap->temp1, VINTEGER);
406 			}
407 		}
408 	}
409 	DiaDoneDialog(dia);
410 }
411 
412 /* Logical Effort effort dialog */
413 static DIALOGITEM le_logeffortdialogitems[] =
414 {
415  /*  1 */ {0, {40,128,64,192}, BUTTON, N_("OK")},
416  /*  2 */ {0, {40,12,64,76}, BUTTON, N_("Cancel")},
417  /*  3 */ {0, {8,8,24,116}, MESSAGE, N_("Logical Effort:")},
418  /*  4 */ {0, {8,128,24,192}, EDITTEXT, x_("")}
419 };
420 static DIALOG le_logeffortdialog = {{75,75,149,281}, N_("Logical Effort"), 0, 4, le_logeffortdialogitems, 0, 0};
421 
422 /* special items for the "Logical Effort effort" dialog: */
423 #define DLEE_EFFVALUE    4		/* Effort (edit text) */
424 
425 /*
426  * Routine to interactively set the logical effort value on the selected node.
427  */
le_setlogicaleffort(void)428 void le_setlogicaleffort(void)
429 {
430 	INTBIG itemHit;
431 	INTBIG inputs;
432 	REGISTER NODEINST *ni;
433 	REGISTER INTBIG type;
434 	double e;
435 	CHAR line[50], *pt;
436 	LENODE statle;
437 	REGISTER void *dia;
438 
439 	ni = (NODEINST *)asktool(us_tool, x_("get-node"));
440 	if (ni == NONODEINST) return;
441 	type = le_getgatetype(ni, &inputs);
442 	if (type == LEUNKNOWN) return;
443 	statle.type = type;
444 	statle.ni = ni;
445 	statle.inputs = inputs;
446 	e = le_getlogeffort(&statle);
447 	dia = DiaInitDialog(&le_logeffortdialog);
448 	if (dia == 0) return;
449 
450 	esnprintf(line, 50, x_("%g"), e);
451 	DiaSetText(dia, -DLEE_EFFVALUE, line);
452 	for(;;)
453 	{
454 		itemHit = DiaNextHit(dia);
455 		if (itemHit == OK || itemHit == CANCEL) break;
456 	}
457 	if (itemHit == OK)
458 	{
459 		pt = DiaGetText(dia, DLEE_EFFVALUE);
460 		startobjectchange((INTBIG)ni, VNODEINST);
461 		esnprintf(line, 50, x_("g=%s"), pt);
462 		setvalkey((INTBIG)ni, VNODEINST, le_nodeeffort_key, (INTBIG)line, VSTRING|VDISPLAY);
463 		endobjectchange((INTBIG)ni, VNODEINST);
464 	}
465 	DiaDoneDialog(dia);
466 }
467 
468 /*
469  * Routine to set the capacitance on arc "ai" to "c".  This is displayed on the arc.
470  */
le_setarccapacitance(ARCINST * ai,double c)471 void le_setarccapacitance(ARCINST *ai, double c)
472 {
473 	REGISTER VARIABLE *var;
474 
475 	startobjectchange((INTBIG)ai, VARCINST);
476 	var = setvalkey((INTBIG)ai, VARCINST, le_attrcapacitance_key, castint((float)c),
477 		VFLOAT|VDISPLAY);
478 	if (var != NOVARIABLE)
479 	{
480 		TDSETDISPPART(var->textdescript, VTDISPLAYNAMEVALUE);
481 		TDSETUNITS(var->textdescript, VTUNITSCAP);
482 		TDSETSIZE(var->textdescript, TXTSETQLAMBDA(3));
483 		if (ai->end[0].ypos == ai->end[1].ypos)
484 		{
485 			/* horizontal arc: push text to top */
486 			TDSETPOS(var->textdescript, VTPOSUP);
487 		}
488 		if (ai->end[0].xpos == ai->end[1].xpos)
489 		{
490 			/* vertical arc: push text to right */
491 			TDSETPOS(var->textdescript, VTPOSRIGHT);
492 		}
493 	}
494 	endobjectchange((INTBIG)ai, VARCINST);
495 }
496 
497 /******************** ANALYSIS ********************/
498 
499 /*
500  * Routine to analyze a path in "le_firstlenode".
501  */
le_analyzepath(void)502 void le_analyzepath(void)
503 {
504 	LENODE *le, *pathend;
505 	REGISTER VARIABLE *var;
506 	REGISTER INTBIG displaycapacitance, state;
507 	CHAR line[50];
508 	double g, G, B, h, H, F, P, N, fhat, Dhat, Cin, Cout, CinI, CoutI;
509 
510 	/* find the end of the path */
511 	for(le = le_firstlenode; le != NOLENODE; le = le->nextlenode) pathend = le;
512 
513 	/* determine the number of stages of logic in the path */
514 	N = 0.0;
515 	for(le = le_firstlenode; le != NOLENODE; le = le->nextlenode) N++;
516 	if (N == 0.0) return;
517 
518 	/* compute the path logical effort by multiplying the individual ones */
519 	G = 1.0;
520 	for(le = le_firstlenode; le != NOLENODE; le = le->nextlenode)
521 	{
522 		g = le->logeffort;
523 		if (g == 0.0) return;
524 		G = G * g;
525 	}
526 
527 	/* compute the branching effort along the path */
528 	B = 1.0;
529 	for(le = le_firstlenode; le != NOLENODE; le = le->nextlenode)
530 		B = B * le->branching;
531 
532 	/* compute the electrical effort along the path */
533 	Cin = le_firstlenode->cin;
534 	Cout = pathend->cout;
535 	ttyputmsg(_("Capacitance starts at %g, ends at %g"), Cin, Cout);
536 	if (Cin == 0.0) return;
537 	H = Cout / Cin;
538 
539 	/* compute the overall path effort */
540 	F = G * B * H;
541 
542 	/* determine the total parasitic effect */
543 	P = 0.0;
544 	for(le = le_firstlenode; le != NOLENODE; le = le->nextlenode)
545 		P = P + le->parasitics;
546 
547 	/* compute the stage effort */
548 	fhat = pow(F, 1/N);
549 	ttyputmsg(_("Optimum stage effort is %g"), fhat);
550 	if (fhat == 0.0) return;
551 
552 	/* compute the minimum path delay */
553 	Dhat = N * fhat + P;
554 	ttyputmsg(_("Minimum path delay is %g"), Dhat);
555 
556 	/* determine whether or not to set capacitance values */
557 	var = getvalkey((INTBIG)le_tool, VTOOL, VINTEGER, le_state_key);
558 	if (var == NOVARIABLE) state = DEFAULTSTATE; else
559 		state = var->addr;
560 	displaycapacitance = state & DISPLAYCAPACITANCE;
561 
562 	/* work backwards through the path, computing electrical effort of each gate */
563 	CoutI = Cout;
564 	for(le = pathend; le != NOLENODE; le = le->prevlenode)
565 	{
566 		/* determine input capacitance to this node */
567 		CinI = le->logeffort * CoutI / fhat;
568 		if (le->prevlenode != NOLENODE && displaycapacitance != 0)
569 			le_setarccapacitance(le->piin->conarcinst, CinI);
570 		if (CinI == 0.0) break;
571 
572 		/* set fanout (h) on this node */
573 		h = CoutI / CinI;
574 		esnprintf(line, 50, x_("h=%g"), h);
575 		startobjectchange((INTBIG)le->ni, VNODEINST);
576 		setvalkey((INTBIG)le->ni, VNODEINST, le_fanout_key, (INTBIG)line, VSTRING|VDISPLAY);
577 		endobjectchange((INTBIG)le->ni, VNODEINST);
578 
579 		/* shift input capacitance to the output of the previous node */
580 		CoutI = CinI;
581 	}
582 }
583 
584 /******************** DELAY ESTIMATION ********************/
585 
586 #define NONETDELAY ((NETDELAY *)-1)
587 
588 typedef struct Inetdelay
589 {
590 	float             numerator;
591 	float             pdenominator;
592 	float             ndenominator;
593 	NETWORK          *net;
594 	struct Inetdelay *nextnetdelay;
595 } NETDELAY;
596 
597 /*
598  * Routine to analyze cell "cell" and build a list of ERC errors.
599  */
le_estimatedelay(NODEPROTO * cell)600 void le_estimatedelay(NODEPROTO *cell)
601 {
602 	REGISTER NETWORK *net;
603 	TRANSISTORINFO *p_gate, *n_gate, *p_active, *n_active;
604 	REGISTER AREAPERIM *ap, *aplist, *nextap;
605 	REGISTER INTBIG fun;
606 	REGISTER NETDELAY *nd, *firstnd;
607 	float numerator, coefficient, flambda;
608 
609 	firstnd = NONETDELAY;
610 	flambda = (float)lambdaofcell(cell);
611 	for(net = cell->firstnetwork; net != NONETWORK; net = net->nextnetwork)
612 	{
613 		aplist = net_gathergeometry(net, &p_gate, &n_gate, &p_active, &n_active, TRUE);
614 
615 		/* numerator has the area of all transistors connected by their gate */
616 		numerator = (float)(p_gate->area + n_gate->area);
617 ttyputmsg(x_("Network %s has numerator:"), describenetwork(net));
618 ttyputmsg(x_("      N Gates = %g, P Gates = %g"), n_gate->area/flambda/flambda, p_gate->area/flambda/flambda);
619 
620 		/* numerator also sums area of each layer */
621 		for(ap = aplist; ap != NOAREAPERIM; ap = nextap)
622 		{
623 			nextap = ap->nextareaperim;
624 			fun = layerfunction(ap->tech, ap->layer);
625 			if ((fun&LFTYPE) != LFDIFF && !layerismetal(fun) && !layerispoly(fun)) continue;
626 			coefficient = 1.0f;
627 			if (layerismetal(fun)) coefficient = 0.1f;
628 			if (layerispoly(fun)) coefficient = 0.1f;
629 ttyputmsg(x_("      Layer %s has %g x %g = %g"), layername(ap->tech, ap->layer),
630 	ap->area/flambda/flambda, coefficient, ap->area*coefficient/flambda/flambda);
631 			numerator += (float)ap->area * coefficient;
632 			efree((CHAR *)ap);
633 		}
634 
635 		/* save the results */
636 		nd = (NETDELAY *)emalloc(sizeof (NETDELAY), le_tool->cluster);
637 		if (nd == 0) break;
638 		nd->pdenominator = (float)p_active->width / flambda;
639 		nd->ndenominator = (float)n_active->width / flambda;
640 		nd->numerator = numerator / flambda / flambda;
641 if (n_active->width == 0) ttyputmsg(x_("   N denominator undefined")); else
642   ttyputmsg(x_("   N denominator = %g, ratio = %g"), nd->ndenominator, nd->numerator/nd->ndenominator);
643 if (p_active->width == 0) ttyputmsg(x_("   P denominator undefined")); else
644   ttyputmsg(x_("   P denominator = %g, ratio = %g"), nd->pdenominator, nd->numerator/nd->pdenominator);
645 if (n_active->width+p_active->width == 0) ttyputmsg(x_("   Denominator undefined")); else
646   ttyputmsg(x_("   Denominator = %g, ratio = %g"), nd->ndenominator+nd->pdenominator,
647 	nd->numerator/(nd->ndenominator+nd->pdenominator));
648 		nd->net = net;
649 		nd->nextnetdelay = firstnd;
650 		firstnd = nd;
651 	}
652 
653 	/* now sort by delay size */
654 
655 #if 0	/* code to show the results */
656 	for(nd = firstnd; nd != NONETDELAY; nd = nd->nextnetdelay)
657 	{
658 		REGISTER void *infstr;
659 
660 		ttyputmsg(M_("Network %s:"), describenetwork(nd->net));
661 		infstr = initinfstr();
662 		formatinfstr(infstr, M_("Network %s has N delay "), describenetwork(nd->net));
663 		if (nd->ndenominator == 0.0)
664 		{
665 			ttyputmsg(M_("   N delay UNDEFINED"));
666 		} else
667 		{
668 			ttyputmsg(M_("   N delay is %g/%g = %g"), nd->numerator, nd->ndenominator,
669 				nd->numerator / nd->ndenominator);
670 		}
671 		if (nd->pdenominator == 0.0)
672 		{
673 			ttyputmsg(M_("   P delay UNDEFINED"));
674 		} else
675 		{
676 			ttyputmsg(M_("   P delay is %g/%g = %g"), nd->numerator, nd->pdenominator,
677 				nd->numerator / nd->pdenominator);
678 		}
679 	}
680 #endif
681 }
682 
683 /******************** LOAD CALCULATION ********************/
684 
le_showloads(void)685 void le_showloads(void)
686 {
687 	REGISTER NODEPROTO *np;
688 	REGISTER NETWORK *net, **netlist, **selnets;
689 	REGISTER ARCPROTO *ap;
690 	REGISTER VARIABLE *var;
691 	REGISTER INTBIG lambda, gwidth, gtotal, atotal, wirelen, fun, load, wireratio, thisload, thiswl,
692 		total, i;
693 	float areatotal;
694 	REGISTER CHAR *lname;
695 	REGISTER AREAPERIM *arpe, *firstarpe, *nextarpe;
696 	TRANSISTORINFO *p_gate, *n_gate, *p_active, *n_active;
697 	REGISTER void *infstr;
698 
699 	/* get the current cell */
700 	np = getcurcell();
701 	if (np == NONODEPROTO)
702 	{
703 		ttyputerr(_("No current cell"));
704 		return;
705 	}
706 	lambda = lambdaofcell(np);
707 	selnets = net_gethighlightednets(FALSE);
708 
709 	/* gather product information for all nets in the cell */
710 	for(net = np->firstnetwork; net != NONETWORK; net = net->nextnetwork)
711 	{
712 		net->temp2 = 0;
713 
714 		/* if some nets were selected, ignore all others */
715 		if (selnets[0] != NONETWORK)
716 		{
717 			for(i=0; selnets[i] != NONETWORK; i++)
718 				if (selnets[i] == net) break;
719 			if (selnets[i] == NONETWORK) continue;
720 		}
721 
722 		/* gather geometry on this network */
723 		firstarpe = net_gathergeometry(net, &p_gate, &n_gate, &p_active, &n_active, TRUE);
724 
725 		/* see if there are gates on the network */
726 		gtotal = net_transistor_p_gate.count + net_transistor_n_gate.count;
727 		if (gtotal > 0)
728 		{
729 			/* sum the metal half-perimeters on the network */
730 			wirelen = 0;
731 			for(arpe = firstarpe; arpe != NOAREAPERIM; arpe = arpe->nextareaperim)
732 			{
733 				fun = layerfunction(arpe->tech, arpe->layer);
734 				if (!layerismetal(fun)) continue;
735 				wirelen += arpe->perimeter / 2;
736 			}
737 			gwidth = net_transistor_p_gate.width + net_transistor_n_gate.width;
738 			net->temp2 = muldiv(wirelen, gwidth, lambda);
739 		}
740 
741 		/* free the area/perimeter information */
742 		for(arpe = firstarpe; arpe != NOAREAPERIM; arpe = nextarpe)
743 		{
744 			nextarpe = arpe->nextareaperim;
745 			efree((CHAR *)arpe);
746 		}
747 	}
748 
749 	/* now sort the networks by this product number in "temp2" */
750 	total = 0;
751 	for(net = np->firstnetwork; net != NONETWORK; net = net->nextnetwork)
752 		if (net->temp2 != 0) total++;
753 	if (total == 0)
754 	{
755 		ttyputmsg(_("There are no networks with load information"));
756 		return;
757 	}
758 	netlist = (NETWORK **)emalloc(total * (sizeof (NETWORK *)), le_tool->cluster);
759 	if (netlist == 0) return;
760 	total = 0;
761 	for(net = np->firstnetwork; net != NONETWORK; net = net->nextnetwork)
762 		if (net->temp2 != 0) netlist[total++] = net;
763 
764 	/* sort by product number */
765 	esort(netlist, total, sizeof (NETWORK *), le_sortbyproductsize);
766 
767 	/* now report the results */
768 	for(i=0; i<total; i++)
769 	{
770 		/* gather geometry on this network */
771 		net = netlist[i];
772 		ttyputmsg(_("For network %s:"), describenetwork(net));
773 		firstarpe = net_gathergeometry(net, &p_gate, &n_gate, &p_active, &n_active, TRUE);
774 
775 		/* determine the wire length */
776 		load = wirelen = 0;
777 		areatotal = 0.0;
778 		for(arpe = firstarpe; arpe != NOAREAPERIM; arpe = arpe->nextareaperim)
779 		{
780 			lname = layername(arpe->tech, arpe->layer);
781 			fun = layerfunction(arpe->tech, arpe->layer);
782 			if (!layerismetal(fun)) continue;
783 			ap = getarconlayer(arpe->layer, arpe->tech);
784 			wireratio = DEFWIRERATIO;
785 			if (ap != NOARCPROTO)
786 			{
787 				var = getvalkey((INTBIG)ap, VARCPROTO, VINTEGER, le_wire_ratio_key);
788 				if (var != NOVARIABLE) wireratio = var->addr;
789 			}
790 			lname = layername(arpe->tech, arpe->layer);
791 			thiswl = arpe->perimeter / 2;
792 			thisload = thiswl / wireratio;
793 			ttyputmsg(x_("  Layer %s wire-length (%s) / wire ratio (%ld) = load (%s)"),
794 				lname, latoa(thiswl, 0), wireratio, latoa(thisload, 0));
795 			load += thisload;
796 			wirelen += thiswl;
797 			areatotal += arpe->area;
798 		}
799 
800 		gwidth = net_transistor_p_gate.width + net_transistor_n_gate.width;
801 		ttyputmsg(_("  Total wire-length (%s) x gate-widths (%s) = product (%s); average wire-width = %g"),
802 			latoa(wirelen, 0), latoa(gwidth, 0), latoa(muldiv(wirelen, gwidth, lambda), 0),
803 				areatotal / (float)wirelen / (float)lambda);
804 
805 		atotal = net_transistor_p_active.count + net_transistor_n_active.count;
806 		if (atotal > 0)
807 		{
808 			infstr = initinfstr();
809 			formatinfstr(infstr, _("  Load = %s"), latoa(load, 0));
810 			if (net_transistor_p_active.width != 0)
811 				formatinfstr(infstr, x_("; load / P-active-width (%s) = %g"), latoa(net_transistor_p_active.width, 0),
812 					(float)load / (float)net_transistor_p_active.width);
813 			if (net_transistor_n_active.width != 0)
814 				formatinfstr(infstr, x_("; load / N-active-width (%s) = %g"), latoa(net_transistor_n_active.width, 0),
815 					(float)load / (float)net_transistor_n_active.width);
816 			ttyputmsg(x_("%s"), returninfstr(infstr));
817 		}
818 
819 		for(arpe = firstarpe; arpe != NOAREAPERIM; arpe = nextarpe)
820 		{
821 			nextarpe = arpe->nextareaperim;
822 			efree((CHAR *)arpe);
823 		}
824 	}
825 	efree((CHAR *)netlist);
826 }
827 
828 /*
829  * Helper routine for to sort NETWORK objects by product in "temp2"
830  */
le_sortbyproductsize(const void * e1,const void * e2)831 int le_sortbyproductsize(const void *e1, const void *e2)
832 {
833 	REGISTER NETWORK *net1, *net2;
834 
835 	net1 = *((NETWORK **)e1);
836 	net2 = *((NETWORK **)e2);
837 	return(net1->temp2 - net2->temp2);
838 }
839 
le_analyzenetwork(void)840 void le_analyzenetwork(void)
841 {
842 	REGISTER NETWORK **netlist, *net;
843 	REGISTER NODEPROTO *np;
844 	REGISTER ARCPROTO *ap;
845 	REGISTER INTBIG i, j, k, widest, len, lambda, total, gtotal, atotal,
846 		fun, metpolhalfperim, load, wireratio, additionalload;
847 	REGISTER CHAR *lname, *pad;
848 	REGISTER VARIABLE *var;
849 	REGISTER AREAPERIM *arpe, *firstarpe, **arpelist;
850 	TRANSISTORINFO *p_gate, *n_gate, *p_active, *n_active;
851 	float ratio;
852 	REGISTER void *infstr;
853 
854 	netlist = net_gethighlightednets(0);
855 	if (netlist[0] == NONETWORK) return;
856 	for(k=0; netlist[k] != NONETWORK; k++)
857 	{
858 		net = netlist[k];
859 
860 		/* gather geometry on this network */
861 		np = net->parent;
862 		firstarpe = net_gathergeometry(net, &p_gate, &n_gate, &p_active, &n_active, TRUE);
863 
864 		/* copy the linked list to an array for sorting */
865 		total = 0;
866 		for(arpe = firstarpe; arpe != NOAREAPERIM; arpe = arpe->nextareaperim)
867 			if (arpe->layer >= 0) total++;
868 		if (total == 0)
869 		{
870 			ttyputmsg(_("No geometry on network '%s' in cell %s"), describenetwork(net),
871 				describenodeproto(np));
872 			continue;
873 		}
874 		arpelist = (AREAPERIM **)emalloc(total * (sizeof (AREAPERIM *)), net_tool->cluster);
875 		if (arpelist == 0) return;
876 		i = 0;
877 		for(arpe = firstarpe; arpe != NOAREAPERIM; arpe = arpe->nextareaperim)
878 			if (arpe->layer >= 0) arpelist[i++] = arpe;
879 
880 		/* sort the layers */
881 		esort(arpelist, total, sizeof (AREAPERIM *), net_areaperimdepthascending);
882 
883 		ttyputmsg(_("For network '%s' in cell %s:"), describenetwork(net),
884 			describenodeproto(np));
885 		lambda = lambdaofcell(np);
886 		widest = 0;
887 		for(i=0; i<total; i++)
888 		{
889 			arpe = arpelist[i];
890 			lname = layername(arpe->tech, arpe->layer);
891 			len = estrlen(lname);
892 			if (len > widest) widest = len;
893 		}
894 		metpolhalfperim = 0;
895 		for(i=0; i<total; i++)
896 		{
897 			arpe = arpelist[i];
898 			lname = layername(arpe->tech, arpe->layer);
899 			infstr = initinfstr();
900 			for(j=estrlen(lname); j<widest; j++) addtoinfstr(infstr, ' ');
901 			pad = returninfstr(infstr);
902 			infstr = initinfstr();
903 			formatinfstr(infstr, _("Layer %s:%s area=%7g  half-perimeter=%s"), lname, pad,
904 				arpe->area/(float)lambda/(float)lambda, latoa(arpe->perimeter/2, 0));
905 			fun = layerfunction(arpe->tech, arpe->layer);
906 			if (layerispoly(fun) != 0 || layerismetal(fun) != 0)
907 			{
908 				ap = getarconlayer(arpe->layer, arpe->tech);
909 				wireratio = DEFWIRERATIO;
910 				if (ap != NOARCPROTO)
911 				{
912 					var = getvalkey((INTBIG)ap, VARCPROTO, VINTEGER, le_wire_ratio_key);
913 					if (var != NOVARIABLE) wireratio = var->addr;
914 				}
915 				additionalload = arpe->perimeter / 2 / wireratio;
916 				metpolhalfperim += additionalload;
917 				formatinfstr(infstr, _(" / wire-ratio (%ld) = %s"), wireratio,
918 					latoa(additionalload, 0));
919 			}
920 			if (arpe->perimeter != 0)
921 			{
922 				ratio = (arpe->area / (float)lambda) / (float)(arpe->perimeter/2);
923 				formatinfstr(infstr, _("; area/half-perimeter = %g"), ratio);
924 			}
925 			ttyputmsg(x_("%s"), returninfstr(infstr));
926 			efree((CHAR *)arpe);
927 		}
928 		efree((CHAR *)arpelist);
929 		gtotal = net_transistor_p_gate.count + net_transistor_n_gate.count;
930 		if (gtotal > 0)
931 		{
932 			infstr = initinfstr();
933 			formatinfstr(infstr, _("Connects to the gate of %ld %s (total width %s, average length %s)"),
934 				gtotal, makeplural(_("transistor"), gtotal),
935 					latoa(net_transistor_p_gate.width+net_transistor_n_gate.width, 0),
936 						latoa((net_transistor_p_gate.length+net_transistor_n_gate.length)/gtotal, 0));
937 			ttyputmsg(x_("%s"), returninfstr(infstr));
938 		}
939 		atotal = net_transistor_p_active.count + net_transistor_n_active.count;
940 		if (atotal > 0)
941 		{
942 			infstr = initinfstr();
943 			formatinfstr(infstr, _("Connects to the active of %ld %s (total width %s, average length %s)"),
944 				atotal, makeplural(_("transistor"), atotal),
945 					latoa(net_transistor_p_active.width+net_transistor_n_active.width, 0),
946 						latoa((net_transistor_p_active.length+net_transistor_n_active.length)/atotal, 0));
947 			ttyputmsg(x_("%s"), returninfstr(infstr));
948 		}
949 		if (metpolhalfperim > 0 && gtotal > 0 && atotal > 0)
950 		{
951 			ttyputmsg(x_("---------- Load Calculations:"));
952 			ttyputmsg(x_("Sum of Metal and Poly half-perimeters / wire-ratio = %s"), latoa(metpolhalfperim, 0));
953 			load = metpolhalfperim + net_transistor_p_gate.width+net_transistor_n_gate.width;
954 			ttyputmsg(x_("  Sum + gate-width (%s) = %s (Load)"),
955 				latoa(net_transistor_p_gate.width+net_transistor_n_gate.width, 0), latoa(load, 0));
956 			if (net_transistor_p_active.width != 0)
957 				ttyputmsg(x_("  Load / P-active-width (%s) = %g"), latoa(net_transistor_p_active.width, 0),
958 					(float)load / (float)net_transistor_p_active.width);
959 			if (net_transistor_n_active.width != 0)
960 				ttyputmsg(x_("  Load / N-active-width (%s) = %g"), latoa(net_transistor_n_active.width, 0),
961 					(float)load / (float)net_transistor_n_active.width);
962 		}
963 	}
964 }
965 
966 /******************** CELL EXTRACTION ********************/
967 
968 /*
969  * Routine to gather all relevant nodes in the current cell and build the structure
970  * headed by "le_firstlenode".
971  */
le_gathercell(INTBIG show)972 void le_gathercell(INTBIG show)
973 {
974 	REGISTER INTBIG inport, oinport, which;
975 	REGISTER BOOLEAN first;
976 	REGISTER INTBIG arrowsize, x, y, i, type;
977 	INTBIG inputs;
978 	REGISTER NODEINST *ni;
979 	REGISTER ARCINST *ai, *oai;
980 	REGISTER NODEPROTO *np;
981 	REGISTER NETWORK *net;
982 	REGISTER VARIABLE *var;
983 	REGISTER PORTARCINST *pi, *opi;
984 	LENODE *le, *ole;
985 	double cap;
986 	REGISTER void *infstr;
987 
988 	/* make sure there is a current cell */
989 	le_freealllenodes();
990 	np = getcurcell();
991 	if (np == NONODEPROTO) return;
992 
993 	/* reset to find power and ground nets */
994 	for(net = np->firstnetwork; net != NONETWORK; net = net->nextnetwork)
995 		net->temp1 = 0;
996 
997 	/* gather all relevant nodes in the cell */
998 	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
999 	{
1000 		type = le_getgatetype(ni, &inputs);
1001 		switch (type)
1002 		{
1003 			case LEUNKNOWN:
1004 			case LETERMINAL:
1005 				break;
1006 			case LEPOWER:
1007 			case LEGROUND:
1008 				for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
1009 					if (pi->conarcinst->network != NONETWORK)
1010 						pi->conarcinst->network->temp1 = 1;
1011 				break;
1012 			default:
1013 				le = le_alloclenode();
1014 				if (le == NOLENODE) break;
1015 				le->ni = ni;
1016 				le->piin = le->piout = NOPORTARCINST;
1017 				le->cin = 0.0;
1018 				le->cout = 0.0;
1019 				le->type = type;
1020 				le->inputs = inputs;
1021 				le->logeffort = le_getlogeffort(le);
1022 				le->parasitics = le_getparasitics(le);
1023 				le->numinputnodes = 0;
1024 				le->numoutputnodes = 0;
1025 				le->nextlenode = le_firstlenode;
1026 				le_firstlenode = le;
1027 				break;
1028 		}
1029 	}
1030 
1031 	/* add input capacitances to the LENODEs */
1032 	for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
1033 		ai->temp1 = 0;
1034 	for(le = le_firstlenode; le != NOLENODE; le = le->nextlenode)
1035 	{
1036 		for(pi = le->ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
1037 		{
1038 			ai = pi->conarcinst;
1039 			if (ai->network == NONETWORK) continue;
1040 			if ((pi->proto->userbits & STATEBITS) != INPORT) continue;
1041 
1042 			/* see if there is a capacitance specification on the input to this node */
1043 			cap = le_getcapacitance(ai->network);
1044 			if (cap == 0.0) continue;
1045 			le->cin = cap;
1046 			ai->temp1 = 1;
1047 		}
1048 	}
1049 
1050 	/* create additional LENODEs for those nodes with input capacitances */
1051 	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1052 	{
1053 		/* see if there is capacitance on this node */
1054 		var = getvalkey((INTBIG)ni, VNODEINST, -1, le_attrcapacitance_key);
1055 		if (var == NOVARIABLE) continue;
1056 
1057 		cap = eatof(describesimplevariable(var));
1058 		le = le_alloclenode();
1059 		if (le == NOLENODE) continue;
1060 		le->ni = ni;
1061 		le->piin = le->piout = NOPORTARCINST;
1062 		le->cin = cap;
1063 		le->cout = 0.0;
1064 		le->type = LETERMINAL;
1065 		le->logeffort = 1.0;
1066 		le->parasitics = 0.0;
1067 		le->numinputnodes = 0;
1068 		le->numoutputnodes = 0;
1069 		le->nextlenode = le_firstlenode;
1070 		le_firstlenode = le;
1071 	}
1072 
1073 	/* add linkage between the nodes */
1074 	for(le = le_firstlenode; le != NOLENODE; le = le->nextlenode)
1075 	{
1076 		for(pi = le->ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
1077 		{
1078 			ai = pi->conarcinst;
1079 			if (ai->network == NONETWORK) continue;
1080 			if (ai->network->temp1 != 0) continue;
1081 			switch (pi->proto->userbits & STATEBITS)
1082 			{
1083 				case INPORT:    inport =  1;   break;
1084 				case BIDIRPORT:
1085 				case OUTPORT:   inport =  0;   break;
1086 				default:        inport = -1;   break;
1087 			}
1088 			if (le->type == LETERMINAL) inport = 1;
1089 			if (inport < 0) continue;
1090 
1091 			/* find other LENODE that connects to this */
1092 			for(ole = le->nextlenode; ole != NOLENODE; ole = ole->nextlenode)
1093 			{
1094 				for(opi = ole->ni->firstportarcinst; opi != NOPORTARCINST; opi = opi->nextportarcinst)
1095 				{
1096 					oai = opi->conarcinst;
1097 					if (oai->network != ai->network) continue;
1098 					if (oai->network->temp1 != 0) continue;
1099 
1100 					/* LENODEs connect, check directionality of ports */
1101 					switch (opi->proto->userbits & STATEBITS)
1102 					{
1103 						case INPORT:    oinport =  1;   break;
1104 						case BIDIRPORT:
1105 						case OUTPORT:   oinport =  0;   break;
1106 						default:        oinport = -1;   break;
1107 					}
1108 					if (ole->type == LETERMINAL) oinport = 1;
1109 					if (oinport < 0) continue;
1110 
1111 					/* figure out how to connect them */
1112 					if (inport != 0)
1113 					{
1114 						/* configure input on node "le" */
1115 						if (oinport != 0) continue;
1116 						le_addlinkage(ole, opi, le, pi);
1117 					} else
1118 					{
1119 						/* configure output on node "le" */
1120 						if (oinport == 0)
1121 						{
1122 							ttyputerr(_("Node %s and %s drive the same network"),
1123 								le_describenode(le), le_describenode(ole));
1124 							continue;
1125 						}
1126 						le_addlinkage(le, pi, ole, opi);
1127 					}
1128 				}
1129 			}
1130 		}
1131 	}
1132 
1133 	/* stop now if display is not requested */
1134 	if (show == 0) return;
1135 
1136 	/* display the nodes */
1137 	infstr = initinfstr();
1138 	first = FALSE;
1139 	for(le = le_firstlenode; le != NOLENODE; le = le->nextlenode)
1140 	{
1141 		if (first) addtoinfstr(infstr, '\n');
1142 		first = TRUE;
1143 		formatinfstr(infstr, x_("CELL=%s FROM=0%lo;-1;0"),
1144 			describenodeproto(np), (INTBIG)le->ni->geom);
1145 	}
1146 	(void)asktool(us_tool, x_("show-multiple"), (INTBIG)returninfstr(infstr));
1147 
1148 	/* display the connection sites */
1149 	arrowsize = lambdaofcell(np) * 2;
1150 	for(le = le_firstlenode; le != NOLENODE; le = le->nextlenode)
1151 	{
1152 		if (le->piin != NOPORTARCINST)
1153 		{
1154 			ai = le->piin->conarcinst;
1155 			if (ai->end[0].portarcinst == le->piin) which = 0; else which = 1;
1156 			x = ai->end[which].xpos;
1157 			y = ai->end[which].ypos;
1158 			(void)asktool(us_tool, x_("show-line"), x, y, x-arrowsize, y-arrowsize, ai->parent);
1159 			(void)asktool(us_tool, x_("show-line"), x, y, x-arrowsize/2, y-arrowsize/5, ai->parent);
1160 			(void)asktool(us_tool, x_("show-line"), x, y, x-arrowsize/5, y-arrowsize/2, ai->parent);
1161 		}
1162 		for(i=0; i<le->numoutputnodes; i++)
1163 		{
1164 			ai = le->outputports[i]->conarcinst;
1165 			if (ai->end[0].portarcinst == le->outputports[i]) which = 0; else which = 1;
1166 			x = ai->end[which].xpos+arrowsize;
1167 			y = ai->end[which].ypos+arrowsize;
1168 			(void)asktool(us_tool, x_("show-line"), x, y, x-arrowsize, y-arrowsize, ai->parent);
1169 			(void)asktool(us_tool, x_("show-line"), x, y, x-arrowsize/2, y-arrowsize/5, ai->parent);
1170 			(void)asktool(us_tool, x_("show-line"), x, y, x-arrowsize/5, y-arrowsize/2, ai->parent);
1171 		}
1172 	}
1173 }
1174 
1175 /*
1176  * Routine to add a connection that runs out of LENODE "out", port "outpi" into
1177  * LENODE "in", port "inpi".
1178  */
le_addlinkage(LENODE * out,PORTARCINST * outpi,LENODE * in,PORTARCINST * inpi)1179 void le_addlinkage(LENODE *out, PORTARCINST *outpi, LENODE *in, PORTARCINST *inpi)
1180 {
1181 	LENODE **newinputnodelist, **newoutputnodelist;
1182 	PORTARCINST **newinputportlist, **newoutputportlist;
1183 	REGISTER INTBIG i;
1184 
1185 	/* add input node to list of outputs */
1186 	newoutputnodelist = (LENODE **)emalloc((out->numoutputnodes+1) * (sizeof (LENODE *)), le_tool->cluster);
1187 	newoutputportlist = (PORTARCINST **)emalloc((out->numoutputnodes+1) * (sizeof (PORTARCINST *)), le_tool->cluster);
1188 	for(i=0; i<out->numoutputnodes; i++)
1189 	{
1190 		newoutputnodelist[i] = out->outputnodes[i];
1191 		newoutputportlist[i] = out->outputports[i];
1192 	}
1193 	newoutputnodelist[out->numoutputnodes] = in;
1194 	newoutputportlist[out->numoutputnodes] = outpi;
1195 	if (out->numoutputnodes > 0)
1196 	{
1197 		efree((CHAR *)out->outputnodes);
1198 		efree((CHAR *)out->outputports);
1199 	}
1200 	out->outputnodes = newoutputnodelist;
1201 	out->outputports = newoutputportlist;
1202 	out->numoutputnodes++;
1203 
1204 	/* add output node to list of inputs */
1205 	newinputnodelist = (LENODE **)emalloc((in->numinputnodes+1) * (sizeof (LENODE *)), le_tool->cluster);
1206 	newinputportlist = (PORTARCINST **)emalloc((in->numinputnodes+1) * (sizeof (PORTARCINST *)), le_tool->cluster);
1207 	for(i=0; i<in->numinputnodes; i++)
1208 	{
1209 		newinputnodelist[i] = in->inputnodes[i];
1210 		newinputportlist[i] = in->inputports[i];
1211 	}
1212 	newinputnodelist[in->numinputnodes] = out;
1213 	newinputportlist[in->numinputnodes] = inpi;
1214 	if (in->numinputnodes > 0)
1215 	{
1216 		efree((CHAR *)in->inputnodes);
1217 		efree((CHAR *)in->inputports);
1218 	}
1219 	in->inputnodes = newinputnodelist;
1220 	in->inputports = newinputportlist;
1221 	in->numinputnodes++;
1222 }
1223 
le_analyzecell(void)1224 void le_analyzecell(void)
1225 {
1226 	REGISTER LENODE *le;
1227 	REGISTER PORTARCINST *pi;
1228 	REGISTER INTBIG iteration, changeneeded, changemade, i, displaycapacitance, state;
1229 	REGISTER VARIABLE *var;
1230 	CHAR line[50];
1231 	double h, needed, maxstageeffort;
1232 
1233 	/* determine maximum stage effort */
1234 	var = getvalkey((INTBIG)le_tool, VTOOL, VFLOAT, le_maximumstageeffort_key);
1235 	if (var == NOVARIABLE) maxstageeffort = MAXSTAGEEFFORT; else
1236 		maxstageeffort = (double)castfloat(var->addr);
1237 	ttyputmsg(_("Maximum stage effort is %g"), maxstageeffort);
1238 
1239 	/* now iterate */
1240 	for(iteration=0; iteration<MAXITERATIONS; iteration++)
1241 	{
1242 		changeneeded = changemade = 0;
1243 		for(le = le_firstlenode; le != NOLENODE; le = le->nextlenode)
1244 		{
1245 			/* propagate this node */
1246 			if (le->numoutputnodes == 0) continue;
1247 
1248 			/* sum up all of the capacitances that this node generates */
1249 			le->cout = 0.0;
1250 			for(i=0; i<le->numoutputnodes; i++)
1251 			{
1252 				le->cout += le->outputnodes[i]->cin;
1253 			}
1254 
1255 			/* see if this node can generate that much capacitance */
1256 			needed = le->cout / maxstageeffort * le->logeffort;
1257 			if (le->cin < needed)
1258 			{
1259 				changeneeded = 1;
1260 				if (le->cin != needed) changemade = 1;
1261 				le->cin = needed;
1262 			}
1263 		}
1264 		if (changemade == 0) break;
1265 	}
1266 	if (changemade != 0)
1267 	{
1268 		ttyputerr(_("WARNING: After %d iterations, analysis is still not stable"), MAXITERATIONS);
1269 	} else if (changeneeded != 0)
1270 	{
1271 		ttyputerr(_("WARNING: Unable to find solution with maximum stage effort of %g"), maxstageeffort);
1272 	}
1273 
1274 	/* determine whether or not to set capacitance values */
1275 	var = getvalkey((INTBIG)le_tool, VTOOL, VINTEGER, le_state_key);
1276 	if (var == NOVARIABLE) state = DEFAULTSTATE; else
1277 		state = var->addr;
1278 	displaycapacitance = state & DISPLAYCAPACITANCE;
1279 
1280 	for(le = le_firstlenode; le != NOLENODE; le = le->nextlenode)
1281 	{
1282 		if (le->numoutputnodes == 0) continue;
1283 		if (displaycapacitance != 0)
1284 		{
1285 			for(pi = le->ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
1286 			{
1287 				if ((pi->proto->userbits & STATEBITS) != INPORT) continue;
1288 				le_setarccapacitance(pi->conarcinst, le->cin);
1289 			}
1290 		}
1291 		h = le->cout / le->cin;
1292 		esnprintf(line, 50, x_("h=%g"), h);
1293 		startobjectchange((INTBIG)le->ni, VNODEINST);
1294 		setvalkey((INTBIG)le->ni, VNODEINST, le_fanout_key, (INTBIG)line, VSTRING|VDISPLAY);
1295 		endobjectchange((INTBIG)le->ni, VNODEINST);
1296 	}
1297 }
1298 
1299 /******************** PATH EXTRACTION ********************/
1300 
1301 /*
1302  * Routine to gather a path, given that two nodes at the ends of the path
1303  * are highlighted.  Fills the list of LENODEs pointed to by "le_firstlenode".
1304  */
le_gatherpath(INTBIG show)1305 void le_gatherpath(INTBIG show)
1306 {
1307 	REGISTER BOOLEAN first;
1308 	REGISTER INTBIG edge, x, y, arrowsize, agree, disagree, which;
1309 	REGISTER NODEINST *ret, *ni;
1310 	REGISTER ARCINST *ai;
1311 	REGISTER NODEPROTO *np;
1312 	REGISTER PORTARCINST *pi;
1313 	double c, cstart, cend;
1314 	GEOM **list;
1315 	LENODE *le, *newle, *nextle, *lastle;
1316 	REGISTER void *infstr;
1317 
1318 	/* make sure there is a current cell */
1319 	le_freealllenodes();
1320 	np = getcurcell();
1321 	if (np == NONODEPROTO) return;
1322 
1323 	/* get all selected objects (must be 2 nodes) */
1324 	list = (GEOM **)asktool(us_tool, x_("get-all-objects"));
1325 	if (list[0] == NOGEOM || list[1] == NOGEOM || list[2] != NOGEOM)
1326 	{
1327 		ttyputerr(_("Select two objects at the ends of a path"));
1328 		return;
1329 	}
1330 
1331 	/* find a path from one node to the other (wavefront propagation) */
1332 	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1333 		ni->temp1 = ni->temp2 = 0;
1334 	for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
1335 		ai->temp1 = ai->temp2 = 0;
1336 	if (list[0]->entryisnode)
1337 	{
1338 		ni = list[0]->entryaddr.ni;
1339 		ni->temp1 = 1;
1340 		cstart = 0.0;
1341 	} else
1342 	{
1343 		ai = list[0]->entryaddr.ai;
1344 		ai->temp2 = 1;
1345 		ai->end[0].nodeinst->temp1 = 1;
1346 		ai->end[1].nodeinst->temp1 = 1;
1347 		cstart = le_getcapacitance(ai->network);
1348 	}
1349 	if (list[1]->entryisnode)
1350 	{
1351 		ni = list[1]->entryaddr.ni;
1352 		ni->temp1 = -1;
1353 		cend = 0.0;
1354 	} else
1355 	{
1356 		ai = list[1]->entryaddr.ai;
1357 		ai->temp2 = 1;
1358 		ai->end[0].nodeinst->temp1 = -1;
1359 		ai->end[1].nodeinst->temp1 = -1;
1360 		cend = le_getcapacitance(ai->network);
1361 	}
1362 	for(edge = 1; ; edge++)
1363 	{
1364 		ret = le_propagate(np, edge);
1365 		if (ret != 0) break;
1366 	}
1367 	if (ret == NONODEINST)
1368 	{
1369 		ttyputerr(_("No path exists between these nodes"));
1370 		return;
1371 	}
1372 
1373 	/* unwind the path and create a chain of LENODEs */
1374 	le_lastlenode = NOLENODE;
1375 	le_lastcapacitance = 1.0;
1376 	le_unwind(ret, NONODEINST);
1377 	if (le_firstlenode != NOLENODE)
1378 	{
1379 		if (cstart != 0.0) le_firstlenode->cin = cstart;
1380 		if (cend != 0.0)
1381 		{
1382 			for(le = le_firstlenode; le != NOLENODE; le = le->nextlenode) lastle = le;
1383 			lastle->cout = cend;
1384 		}
1385 	}
1386 
1387 	/* reverse the path if the ports indicate it */
1388 	agree = disagree = 0;
1389 	for(le = le_firstlenode; le != NOLENODE; le = le->nextlenode)
1390 	{
1391 		if (le->piin != NOPORTARCINST)
1392 		{
1393 			switch (le->piin->proto->userbits & STATEBITS)
1394 			{
1395 				case INPORT:  agree++;      break;
1396 				case OUTPORT: disagree++;   break;
1397 			}
1398 		}
1399 		if (le->piout != NOPORTARCINST)
1400 		{
1401 			switch (le->piout->proto->userbits & STATEBITS)
1402 			{
1403 				case INPORT:  disagree++;   break;
1404 				case OUTPORT: agree++;      break;
1405 			}
1406 		}
1407 	}
1408 	if (agree != 0 && disagree != 0)
1409 		ttyputmsg(_("Directionality of path is conflicting"));
1410 	if (agree == 0 && disagree == 0)
1411 		ttyputmsg(_("Directionality of path is unknown"));
1412 	if (disagree != 0)
1413 	{
1414 		/* reverse the path */
1415 		newle = NOLENODE;
1416 		for(le = le_firstlenode; le != NOLENODE; le = nextle)
1417 		{
1418 			nextle = le->nextlenode;
1419 			le->nextlenode = newle;
1420 			newle = le;
1421 			pi = le->piin;   le->piin = le->piout;   le->piout = pi;
1422 			c = le->cin;     le->cin = le->cout;     le->cout = c;
1423 		}
1424 		le_firstlenode = newle;
1425 	}
1426 
1427 	/* add back-pointers to path */
1428 	lastle = NOLENODE;
1429 	for(le = le_firstlenode; le != NOLENODE; le = le->nextlenode)
1430 	{
1431 		le->prevlenode = lastle;
1432 		lastle = le;
1433 	}
1434 
1435 	/* determine branching */
1436 	le_figurebranching();
1437 
1438 	/* stop now if display is not requested */
1439 	if (show == 0) return;
1440 
1441 	/* describe the nodes in the path */
1442 	for(le = le_firstlenode; le != NOLENODE; le = le->nextlenode)
1443 	{
1444 		ttyputmsg(_("%s: LogEffort=%g Parasitics=%g Cin=%g Cout=%g Branching=%g"),
1445 			le_describenode(le), le->logeffort, le->parasitics, le->cin, le->cout,
1446 				le->branching);
1447 	}
1448 
1449 	/* display the path between the nodes */
1450 	(void)asktool(us_tool, x_("clear"));
1451 	infstr = initinfstr();
1452 	first = FALSE;
1453 	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1454 	{
1455 		if (ni->temp2 == 0) continue;
1456 		if (first) addtoinfstr(infstr, '\n');
1457 		first = TRUE;
1458 		formatinfstr(infstr, x_("CELL=%s FROM=0%lo;-1;0"),
1459 			describenodeproto(np), (INTBIG)ni->geom);
1460 	}
1461 	for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
1462 	{
1463 		if (ai->temp2 == 0) continue;
1464 		addtoinfstr(infstr, '\n');
1465 		formatinfstr(infstr, x_("CELL=%s FROM=0%lo;-1;0"),
1466 			describenodeproto(np), (INTBIG)ai->geom);
1467 	}
1468 	(void)asktool(us_tool, x_("show-multiple"), (INTBIG)returninfstr(infstr));
1469 
1470 	arrowsize = lambdaofcell(np) * 2;
1471 	for(le = le_firstlenode; le != NOLENODE; le = le->nextlenode)
1472 	{
1473 		if (le->piin != NOPORTARCINST)
1474 		{
1475 			ai = le->piin->conarcinst;
1476 			if (ai->end[0].portarcinst == le->piin) which = 0; else which = 1;
1477 			x = ai->end[which].xpos;
1478 			y = ai->end[which].ypos;
1479 			(void)asktool(us_tool, x_("show-line"), x, y, x-arrowsize, y-arrowsize, ai->parent);
1480 			(void)asktool(us_tool, x_("show-line"), x, y, x-arrowsize/2, y-arrowsize/5, ai->parent);
1481 			(void)asktool(us_tool, x_("show-line"), x, y, x-arrowsize/5, y-arrowsize/2, ai->parent);
1482 		}
1483 		if (le->piout != NOPORTARCINST)
1484 		{
1485 			ai = le->piout->conarcinst;
1486 			if (ai->end[0].portarcinst == le->piout) which = 0; else which = 1;
1487 			x = ai->end[which].xpos+arrowsize;
1488 			y = ai->end[which].ypos+arrowsize;
1489 			(void)asktool(us_tool, x_("show-line"), x, y, x-arrowsize, y-arrowsize, ai->parent);
1490 			(void)asktool(us_tool, x_("show-line"), x, y, x-arrowsize/2, y-arrowsize/5, ai->parent);
1491 			(void)asktool(us_tool, x_("show-line"), x, y, x-arrowsize/5, y-arrowsize/2, ai->parent);
1492 		}
1493 	}
1494 }
1495 
1496 /*
1497  * helper function for "le_gatherpath" which propagates a wave of connectivity
1498  * through the circuit to find a path between the end nodes.
1499  * Returns NONODEINST if the propagation has failed to advance.
1500  * Returns zero if the propagation must run more.
1501  * Returns a NODEINST if it has found that node at then end of the path
1502  */
le_propagate(NODEPROTO * cell,INTBIG edge)1503 NODEINST *le_propagate(NODEPROTO *cell, INTBIG edge)
1504 {
1505 	REGISTER NODEINST *ni, *oni;
1506 	REGISTER PORTARCINST *pi;
1507 	REGISTER ARCINST *ai;
1508 	REGISTER INTBIG otherend, moved;
1509 
1510 	moved = 0;
1511 	for(ni = cell->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1512 	{
1513 		if (ni->temp1 != edge) continue;
1514 
1515 		/* propagate from here */
1516 		for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
1517 		{
1518 			ai = pi->conarcinst;
1519 			if (ai->end[0].portarcinst == pi) otherend = 1; else otherend = 0;
1520 			oni = ai->end[otherend].nodeinst;
1521 			if (oni->temp1 == -1)
1522 			{
1523 				ai->temp1 = edge;
1524 				oni->temp1 = edge+1;
1525 				return(oni);
1526 			}
1527 			if (oni->temp1 == 0)
1528 			{
1529 				ai->temp1 = edge;
1530 				oni->temp1 = edge+1;
1531 				moved++;
1532 			}
1533 		}
1534 	}
1535 	if (moved == 0) return(NONODEINST);
1536 	return(0);
1537 }
1538 
1539 /*
1540  * helper function for "le_gatherpath" which retraces the wave information
1541  * to construct the minimum path between two nodes.
1542  */
le_unwind(NODEINST * start,NODEINST * prev)1543 void le_unwind(NODEINST *start, NODEINST *prev)
1544 {
1545 	REGISTER NODEINST *oni;
1546 	REGISTER PORTARCINST *pi;
1547 	REGISTER ARCINST *ai;
1548 	REGISTER INTBIG otherend;
1549 	double capacitance;
1550 	REGISTER INTBIG func;
1551 	LENODE *le;
1552 
1553 	/* add this node to the chain, if it can be analyzed */
1554 	le = NOLENODE;
1555 	func = nodefunction(start);
1556 	switch (func)
1557 	{
1558 		case NPPIN:
1559 		case NPCONTACT:
1560 		case NPNODE:
1561 		case NPCONNECT:
1562 		case NPCONPOWER:
1563 		case NPCONGROUND:
1564 			break;
1565 		case NPBUFFER:
1566 		case NPGATEAND:
1567 		case NPGATEOR:
1568 		case NPGATEXOR:
1569 			le = le_alloclenode();
1570 			if (le == NOLENODE) break;
1571 			le->ni = start;
1572 			le->piin = le->piout = NOPORTARCINST;
1573 			le->cin = 0.0;
1574 			le->cout = le_lastcapacitance;
1575 			le->type = le_getgatetype(start, &le->inputs);
1576 			le->logeffort = le_getlogeffort(le);
1577 			le->parasitics = le_getparasitics(le);
1578 			le->numinputnodes = 0;
1579 			le->numoutputnodes = 0;
1580 			le_lastcapacitance = 1.0;
1581 			le->nextlenode = le_firstlenode;
1582 			le_firstlenode = le;
1583 			le_lastlenode = le;
1584 			break;
1585 		default:
1586 			ttyputmsg(_("Sorry, cannot analyze %s nodes"), nodefunctionname(func, start));
1587 			break;
1588 	}
1589 	if (le != NOLENODE)
1590 	{
1591 		for(pi = start->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
1592 		{
1593 			ai = pi->conarcinst;
1594 			if (ai->temp1 == 0) continue;
1595 			if (ai->end[0].portarcinst == pi) otherend = 1; else otherend = 0;
1596 			oni = ai->end[otherend].nodeinst;
1597 			if (oni == prev) le->piout = pi;
1598 			if (ai->temp1 == start->temp1 - 1 && oni->temp1 == start->temp1 - 1)
1599 				le->piin = pi;
1600 		}
1601 	}
1602 
1603 	/* mark and trace the path */
1604 	start->temp2 = 1;
1605 	if (start->temp1 == 1) return;
1606 	for(pi = start->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
1607 	{
1608 		ai = pi->conarcinst;
1609 		if (ai->temp1 == 0) continue;
1610 		if (ai->end[0].portarcinst == pi) otherend = 1; else otherend = 0;
1611 		oni = ai->end[otherend].nodeinst;
1612 		if (ai->temp1 == start->temp1 - 1 && oni->temp1 == start->temp1 - 1)
1613 		{
1614 			ai->temp2 = 1;
1615 			capacitance = le_getcapacitance(ai->network);
1616 			if (capacitance != 0.0)
1617 			{
1618 				if (le_lastlenode != NOLENODE) le_lastlenode->cin = capacitance;
1619 				le_lastcapacitance = capacitance;
1620 			}
1621 			le_unwind(oni, start);
1622 			break;
1623 		}
1624 	}
1625 }
1626 
1627 /*
1628  * Routine to figure out the branching for the chain of LENODEs pointed to by
1629  * "le_firstlenode".  Loads the "branching" field of each LENODE.
1630  */
le_figurebranching(void)1631 void le_figurebranching(void)
1632 {
1633 	REGISTER LENODE *le, *nextle;
1634 	REGISTER ARCINST *ai;
1635 	REGISTER PORTARCINST *pi;
1636 	double onpath, offpath;
1637 
1638 	for(le = le_firstlenode; le != NOLENODE; le = le->nextlenode)
1639 	{
1640 		nextle = le->nextlenode;
1641 		le->branching = 1.0;
1642 		if (nextle == NOLENODE) continue;
1643 		if (le->piout == NOPORTARCINST) continue;
1644 
1645 		/* reset indication of arcs that have been examined */
1646 		for(ai = le->ni->parent->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
1647 			ai->temp1 = 0;
1648 
1649 		/* find useful and stray capacitance in network coming from this LE node */
1650 		onpath = 0.0;
1651 		offpath = 0.0;
1652 		for(pi = le->ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
1653 		{
1654 			if (pi->proto != le->piout->proto) continue;
1655 			le_propagatebranch(pi, nextle, 1.0, &onpath, &offpath);
1656 		}
1657 
1658 		/* determine branching factor */
1659 		if (onpath == 0.0) le->branching = 1.0; else
1660 			le->branching = (onpath + offpath) / onpath;
1661 	}
1662 }
1663 
1664 /*
1665  * Helper routine for "le_figurebranching" to propagate through the circuit, starting at
1666  * port "tpi" and gather capacitances into either "onpath" (if the path ends at the
1667  * desired "final" LENODE) or "offpath".  "capacitance" is the capacitance value seen
1668  * so far while following the path.
1669  */
le_propagatebranch(PORTARCINST * tpi,LENODE * final,double capacitance,double * onpath,double * offpath)1670 void le_propagatebranch(PORTARCINST *tpi, LENODE *final, double capacitance,
1671 	double *onpath, double *offpath)
1672 {
1673 	REGISTER ARCINST *ai;
1674 	REGISTER PORTARCINST *pi;
1675 	REGISTER INTBIG otherend;
1676 	REGISTER INTBIG func;
1677 	double cap;
1678 	REGISTER NODEINST *oni;
1679 
1680 	/* see if this arc has been examined */
1681 	ai = tpi->conarcinst;
1682 	if (ai->temp1 != 0) return;
1683 	ai->temp1 = 1;
1684 
1685 	/* gather any capacitance specification on this arc */
1686 	cap = le_getcapacitance(ai->network);
1687 	if (cap != 0.0) capacitance = cap;
1688 
1689 	/* see if the other end of the arc is a terminal point */
1690 	if (ai->end[0].portarcinst == tpi) otherend = 1; else otherend = 0;
1691 	oni = ai->end[otherend].nodeinst;
1692 	func = nodefunction(oni);
1693 	if (func != NPPIN && func != NPCONTACT && func != NPNODE && func != NPCONNECT)
1694 	{
1695 		/* end of this chain: add capacitance into one of the path accumulators */
1696 		if (oni == final->ni) *onpath += capacitance; else
1697 			*offpath += capacitance;
1698 		return;
1699 	}
1700 
1701 	/* propagate further */
1702 	for(pi = oni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
1703 	{
1704 		le_propagatebranch(pi, final, capacitance, onpath, offpath);
1705 	}
1706 }
1707 
1708 /******************** SUPPORT ********************/
1709 
1710 /*
1711  * Routine to determine the type of object at "ni", returning the type and the number
1712  * of inputs.
1713  */
le_getgatetype(NODEINST * ni,INTBIG * inputs)1714 INTBIG le_getgatetype(NODEINST *ni, INTBIG *inputs)
1715 {
1716 	REGISTER INTBIG inputnegates, outputnegates, thisend, isinput, gatetype, func;
1717 	CHAR *inputport, *outputport1, *outputport2;
1718 	REGISTER ARCINST *ai;
1719 	REGISTER PORTARCINST *pi;
1720 	REGISTER VARIABLE *var;
1721 
1722 	/* determine the number of inputs to the gate */
1723 	*inputs = 0;
1724 	inputnegates = outputnegates = 0;
1725 	func = nodefunction(ni);
1726 	if (func == NPUNKNOWN)
1727 	{
1728 		/* ignore if it is a capacitance element */
1729 		var = getvalkey((INTBIG)ni, VNODEINST, -1, le_attrcapacitance_key);
1730 		if (var == NOVARIABLE)
1731 			ttyputmsg(_("Sorry, Logical Effort cannot handle cells"));
1732 		return(NPUNKNOWN);
1733 	}
1734 
1735 	inputport = x_("a");   outputport1 = outputport2 = x_("y");
1736 	if (func == NPTRANMOS || func == NPTRAPMOS)
1737 	{
1738 		inputport = x_("g");   outputport1 = x_("s");   outputport2 = x_("d");
1739 	}
1740 	for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
1741 	{
1742 		isinput = 0;
1743 		if (estrcmp(pi->proto->protoname, inputport) == 0)
1744 		{
1745 			isinput = 1;
1746 			(*inputs)++;
1747 		}
1748 		if (estrcmp(pi->proto->protoname, inputport) == 0 ||
1749 			estrcmp(pi->proto->protoname, outputport1) == 0 ||
1750 			estrcmp(pi->proto->protoname, outputport2) == 0)
1751 		{
1752 			if ((pi->conarcinst->userbits&ISNEGATED) != 0)
1753 			{
1754 				ai = pi->conarcinst;
1755 				if (ai->end[0].portarcinst == pi) thisend = 0; else thisend = 1;
1756 				if ((thisend == 0 && (ai->userbits&REVERSEEND) == 0) ||
1757 					(thisend == 1 && (ai->userbits&REVERSEEND) != 0))
1758 				{
1759 					if (isinput != 0) inputnegates++; else outputnegates++;
1760 				}
1761 			}
1762 		}
1763 	}
1764 
1765 	/* determine the type of the gate */
1766 	gatetype = LEUNKNOWN;
1767 	switch (func)
1768 	{
1769 		case NPBUFFER:
1770 			if (inputnegates+outputnegates == 0)
1771 			{
1772 				ttyputmsg(_("Cannot handle BUFFER gates, only INVERTER"));
1773 			} else if (inputnegates+outputnegates != 1)
1774 			{
1775 				ttyputmsg(_("Cannot handle INVERTER gates that invert more than once"));
1776 			} else gatetype = LEINVERTER;
1777 			break;
1778 
1779 		case NPGATEAND:
1780 			if (inputnegates != 0)
1781 			{
1782 				ttyputmsg(_("Cannot handle AND gates with inverted inputs"));
1783 			} else if (outputnegates == 0)
1784 			{
1785 				ttyputmsg(_("Cannot handle AND gates, only NAND"));
1786 			} else gatetype = LENAND;
1787 			break;
1788 
1789 		case NPGATEOR:
1790 			if (inputnegates != 0)
1791 			{
1792 				ttyputmsg(_("Cannot handle OR gates with inverted inputs"));
1793 			} else if (outputnegates == 0)
1794 			{
1795 				ttyputmsg(_("Cannot handle OR gates, only NOR"));
1796 			} else gatetype = LENOR;
1797 			break;
1798 
1799 		case NPGATEXOR:
1800 			if (inputnegates != 0)
1801 			{
1802 				ttyputmsg(_("Cannot handle XOR gates with inverted inputs"));
1803 			} else
1804 			{
1805 				if (outputnegates != 0) gatetype = LEXNOR; else
1806 					gatetype = LEXOR;
1807 			}
1808 			break;
1809 
1810 		case NPTRANMOS:
1811 			if (inputnegates != 0 || outputnegates != 0)
1812 			{
1813 				ttyputmsg(_("Cannot negate inputs to transistors"));
1814 			} else gatetype = LENMOS;
1815 			break;
1816 
1817 		case NPTRAPMOS:
1818 			if (inputnegates != 0 || outputnegates != 0)
1819 			{
1820 				ttyputmsg(_("Cannot negate inputs to transistors"));
1821 			} else gatetype = LEPMOS;
1822 			break;
1823 
1824 		case NPCONPOWER:
1825 			if (inputnegates != 0 || outputnegates != 0)
1826 			{
1827 				ttyputmsg(_("Cannot negate inputs to power"));
1828 			} else gatetype = LEPOWER;
1829 			break;
1830 
1831 		case NPCONGROUND:
1832 			if (inputnegates != 0 || outputnegates != 0)
1833 			{
1834 				ttyputmsg(_("Cannot negate inputs to ground"));
1835 			} else gatetype = LEGROUND;
1836 			break;
1837 
1838 		case NPPIN:
1839 		case NPCONTACT:
1840 		case NPNODE:
1841 		case NPCONNECT:
1842 			break;
1843 
1844 		default:
1845 			ttyputmsg(_("Sorry, Logical Effort cannot handle %s nodes"), nodefunctionname(func, ni));
1846 			break;
1847 	}
1848 
1849 	return(gatetype);
1850 }
1851 
1852 /*
1853  * Routine to return a string describing LENODE "le".
1854  */
le_describenode(LENODE * le)1855 CHAR *le_describenode(LENODE *le)
1856 {
1857 	REGISTER VARIABLE *var;
1858 	REGISTER void *infstr;
1859 
1860 	infstr = initinfstr();
1861 	switch (le->type)
1862 	{
1863 		case LEINVERTER:
1864 			addstringtoinfstr(infstr, _("inverter"));
1865 			break;
1866 		case LENAND:
1867 			addstringtoinfstr(infstr, _("nand"));
1868 			break;
1869 		case LENOR:
1870 			addstringtoinfstr(infstr, _("nor"));
1871 			break;
1872 		case LEXOR:
1873 			addstringtoinfstr(infstr, _("xor"));
1874 			break;
1875 		case LEXNOR:
1876 			addstringtoinfstr(infstr, _("xnor"));
1877 			break;
1878 		case LENMOS:
1879 			addstringtoinfstr(infstr, _("nMOS"));
1880 			break;
1881 		case LEPMOS:
1882 			addstringtoinfstr(infstr, _("pMOS"));
1883 			break;
1884 		default:
1885 			addstringtoinfstr(infstr, describenodeproto(le->ni->proto));
1886 			break;
1887 	}
1888 	var = getvalkey((INTBIG)le->ni, VNODEINST, VSTRING, el_node_name_key);
1889 	if (var != NOVARIABLE)
1890 	{
1891 		addstringtoinfstr(infstr, x_("["));
1892 		addstringtoinfstr(infstr, (CHAR *)var->addr);
1893 		addstringtoinfstr(infstr, x_("]"));
1894 	}
1895 	return(returninfstr(infstr));
1896 }
1897 
1898 /*
1899  * Routine to determine the logical effort of the gate in "le".  Uses the table
1900  * from page 7 of Sutherland's book.
1901  */
le_getlogeffort(LENODE * le)1902 double le_getlogeffort(LENODE *le)
1903 {
1904 	REGISTER VARIABLE *var;
1905 	REGISTER CHAR *pt;
1906 
1907 	/* see if there is an override on the node */
1908 	var = getvalkey((INTBIG)le->ni, VNODEINST, VSTRING, le_nodeeffort_key);
1909 	if (var != NOVARIABLE)
1910 	{
1911 		pt = (CHAR *)var->addr;
1912 		while (*pt == ' ' || *pt == '\t') pt++;
1913 		if (*pt == 'g' || *pt == 'G')
1914 		{
1915 			pt++;
1916 			while (*pt == ' ' || *pt == '\t') pt++;
1917 		}
1918 		if (*pt == '=')
1919 		{
1920 			pt++;
1921 			while (*pt == ' ' || *pt == '\t') pt++;
1922 		}
1923 		return(eatof(pt));
1924 	}
1925 
1926 	/* calculate the logical effort */
1927 	switch (le->type)
1928 	{
1929 		case LEINVERTER: return(1.0);
1930 		case LENAND:     return((le->inputs+2.0) / 3.0);
1931 		case LENOR:      return((2.0*le->inputs+1.0) / 3.0);
1932 		case LEXOR:
1933 		case LEXNOR:
1934 			switch (le->inputs)
1935 			{
1936 				case 2: return(4.0);
1937 				case 3: return(12.0);
1938 				case 4: return(32.0);
1939 			}
1940 			ttyputmsg(_("Cannot handle %ld-input XOR gates"), le->inputs);
1941 			return(0.0);
1942 		case LENMOS:     return(1.0/3.0);
1943 		case LEPMOS:     return(2.0/3.0);
1944 	}
1945 	ttyputmsg(_("Sorry, node %s has unknown type"), describenodeinst(le->ni));
1946 	return(0.0);
1947 }
1948 
1949 /*
1950  * Routine to determine the parasitics of the gate in "le".  Uses the table
1951  * from page 10 of Sutherland's book.
1952  */
le_getparasitics(LENODE * le)1953 double le_getparasitics(LENODE *le)
1954 {
1955 	switch (le->type)
1956 	{
1957 		case LEINVERTER: return(1.0);
1958 		case LENAND:     return((double)le->inputs);
1959 		case LENOR:      return((double)le->inputs);
1960 		case LEXOR:      return(4.0);
1961 		case LEXNOR:     return(4.0);
1962 		case LENMOS:     return(1.0);		/* probably not right!!! */
1963 		case LEPMOS:     return(1.0);		/* probably not right!!! */
1964 	}
1965 	ttyputmsg(_("Sorry, node %s has unknown type"), describenodeinst(le->ni));
1966 	return(0.0);
1967 }
1968 
1969 /*
1970  * Routine to extract the capacitance value that is stored on network "net".
1971  * Returns zero if no value is stored there.
1972  */
le_getcapacitance(NETWORK * net)1973 double le_getcapacitance(NETWORK *net)
1974 {
1975 	REGISTER VARIABLE *var;
1976 	REGISTER NODEPROTO *np;
1977 	REGISTER NODEINST *ni;
1978 	REGISTER PORTARCINST *pi;
1979 
1980 	np = net->parent;
1981 	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1982 	{
1983 		/* see if the node is connected to this arc */
1984 		for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
1985 			if (pi->conarcinst->network == net) break;
1986 		if (pi == NOPORTARCINST) continue;
1987 
1988 		/* see if there is capacitance on this node */
1989 		var = getvalkey((INTBIG)ni, VNODEINST, -1, le_attrcapacitance_key);
1990 		if (var != NOVARIABLE)
1991 		{
1992 			return(eatof(describesimplevariable(var)));
1993 		}
1994 	}
1995 	return(0.0);
1996 }
1997 
1998 /*
1999  * Routine to free the global array of LE nodes (headed by "le_firstlenode").
2000  */
le_freealllenodes(void)2001 void le_freealllenodes(void)
2002 {
2003 	LENODE *nextle, *le;
2004 
2005 	for(le = le_firstlenode; le != NOLENODE; le = nextle)
2006 	{
2007 		nextle = le->nextlenode;
2008 		le_freelenode(le);
2009 	}
2010 	le_firstlenode = NOLENODE;
2011 }
2012 
2013 /*
2014  * Routine to allocate a new LENODE (either from the pool of unused ones
2015  * or from memory).
2016  */
le_alloclenode(void)2017 LENODE *le_alloclenode(void)
2018 {
2019 	LENODE *le;
2020 
2021 	if (le_lenodefree != NOLENODE)
2022 	{
2023 		le = le_lenodefree;
2024 		le_lenodefree = le->nextlenode;
2025 	} else
2026 	{
2027 		le = (LENODE *)emalloc(sizeof (LENODE), le_tool->cluster);
2028 		if (le == 0) return(NOLENODE);
2029 	}
2030 	return(le);
2031 }
2032 
2033 /*
2034  * Routine to return LENODE "le" to the pool of unused ones.
2035  */
le_freelenode(LENODE * le)2036 void le_freelenode(LENODE *le)
2037 {
2038 	if (le->numinputnodes > 0)
2039 	{
2040 		efree((CHAR *)le->inputnodes);
2041 		efree((CHAR *)le->inputports);
2042 	}
2043 	if (le->numoutputnodes > 0)
2044 	{
2045 		efree((CHAR *)le->outputnodes);
2046 		efree((CHAR *)le->outputports);
2047 	}
2048 	le->nextlenode = le_lenodefree;
2049 	le_lenodefree = le;
2050 }
2051 
2052 #endif  /* LOGEFFTOOL - at top */
2053