1 /* -*- tab-width: 4 -*-
2  *
3  * Electric(tm) VLSI Design System
4  *
5  * File: netflat.c
6  * Network tool: module for fully instantiating a hierarchical network
7  * Written by: Steven M. Rubin, Static Free Software
8  *
9  * Copyright (c) 2000 Static Free Software.
10  *
11  * Electric(tm) is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * Electric(tm) is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with Electric(tm); see the file COPYING.  If not, write to
23  * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
24  * Boston, Mass 02111-1307, USA.
25  *
26  * Static Free Software
27  * 4119 Alpine Road
28  * Portola Valley, California 94028
29  * info@staticfreesoft.com
30  */
31 
32 #include "global.h"
33 #include "network.h"
34 #include "efunction.h"
35 #include "tecschem.h"
36 
37 #define IGNOREFOURPORT 1				/* comment out to handle 4-port transistors properly */
38 
39 static INTBIG    net_pseudonode;		/* net number for pseudonetworks */
40 static BOOLEAN   net_mfactorwarned;		/* TRUE if a warning was issued about zero m-factors */
41 static NODEINST *net_toplevelinst;		/* actual top-instance associated with this node */
42 static PCOMP    *net_seriesend1, *net_seriesend2;	/* end component of series transistor chains */
43 static INTBIG    net_seriescon1,  net_seriescon2;	/* end index of series transistor chains */
44 
45 /* working memory for "net_comparewirelist()" */
46 static INTBIG     net_comparelistsize = 0;
47 static BOOLEAN   *net_comparelist;
48 
49 /* working memory for "net_mergeseries()" */
50 static PCOMP    **net_serieslist;
51 static INTBIG     net_serieslistcount;
52 static INTBIG     net_serieslisttotal = 0;
53 
54 #define STATIC
55 
56 /* prototypes for local routines */
57 STATIC BOOLEAN net_addtoserieslist(PCOMP *pc);
58 STATIC void    net_addtraversalpath(PNET *pn, NODEPROTO *cell);
59 STATIC PCOMP  *net_allocpcomp(void);
60 STATIC PNET   *net_allocpnet(void);
61 STATIC PCOMP  *net_buildpseudo(NODEPROTO*, PCOMP*, INTBIG*, PNET**, PNET**, BOOLEAN, BOOLEAN, float);
62 STATIC void    net_crawlforseries(PCOMP *pc);
63 STATIC INTBIG  net_findotherexporttopology(NODEPROTO *np, CHAR *exportname);
64 STATIC INTBIG  net_findotherexportcharacteristic(NODEPROTO *parent, INTBIG bits);
65 STATIC PCOMP **net_gatherseries(PCOMP *pc, INTBIG *seriescount);
66 STATIC INTBIG  net_getfunction(NODEINST*);
67 STATIC float   net_getpartvalue(NODEINST *ni);
68 STATIC float   net_findunits(NODEINST *ni, INTBIG units);
69 STATIC BOOLEAN net_getpnetandstate(NODEINST *ni, PORTPROTO *pp, NETWORK *forcenet,
70 				INTBIG index, PNET **netnumber, INTSML *state, PNET **pnetlist,
71 				PNET **globalpnetlist, INTBIG nodewidth, INTBIG nindex, INTBIG destsignals);
72 STATIC INTBIG  net_mergeseries(PCOMP **pcomp, PNET *pnet, INTBIG *components);
73 STATIC PNET   *net_newpnet(NETWORK *net, PNET **pnetlist, PNET **globalpnetlist);
74 STATIC void    net_setthisexporttopology(PORTPROTO *pp, INTBIG *index);
75 STATIC int     net_sortpcompbyhash(const void *e1, const void *e2);
76 STATIC void    net_addexporttopnet(PNET *pn, PORTPROTO *pp);
77 STATIC void    net_gatherglobals(NODEPROTO *cell, PNET **pnetlist, PNET **globalpnetlist);
78 STATIC int     net_cellnameascending(const void *e1, const void *e2);
79 STATIC INTBIG  net_getcellfunction(NODEPROTO *np);
80 
81 /*
82  * Routine to free all memory associated with this module.
83  */
net_freeflatmemory(void)84 void net_freeflatmemory(void)
85 {
86 	REGISTER PCOMP *p;
87 	REGISTER PNET *pn;
88 
89 	while (net_pcompfree != NOPCOMP)
90 	{
91 		p = net_pcompfree;
92 		net_pcompfree = net_pcompfree->nextpcomp;
93 		efree((CHAR *)p);
94 	}
95 	while (net_pnetfree != NOPNET)
96 	{
97 		pn = net_pnetfree;
98 		net_pnetfree = net_pnetfree->nextpnet;
99 		if (pn->nodetotal > 0)
100 		{
101 			efree((CHAR *)pn->nodelist);
102 			efree((CHAR *)pn->nodewire);
103 		}
104 		efree((CHAR *)pn);
105 	}
106 	if (net_comparelistsize > 0) efree((CHAR *)net_comparelist);
107 }
108 
109 /*********************** PSEUDO-NETWORK CONSTRUCTION ***********************/
110 
111 /*
112  * The usage of this module is:
113  *   #include "network.h"
114  *   PCOMP *pcomp;
115  *   PNET *pnet;
116  *   INTBIG components, nets, powernets, groundnets;
117  *
118  *   net_initnetflattening();
119  *   pcomp = net_makepseudo(cell, &components, &nets, &powernets, &groundnets,
120  *	    &pnet, hierarchical, mergeparallel, mergeseries, checkcelloverrides, figuresizes);
121  *   .....
122  *        do something with the network in "pcomp" and "pnet"
123  *   .....
124  *   net_freeallpnet(pnet);
125  *   net_freeallpcomp(pcomp);
126  *
127  * "net_initnetflattening" gathers network topology for primitives and cells.  It needs to be
128  * called only once for any set of libraries.
129  * "net_makepseudo" builds a pseudonetwork structure that represents the
130  * network in cell "cell".  If it returns NOPCOMP and sets "components" negative,
131  * there is an error (if it returns NOPCOMP with "components" zero, there just are not
132  * any components in the cell).
133  * A linked list of PCOMP objects is returned, one for every component
134  * in the pseudonetwork.  A linked list of PNET objects is also returned,
135  * one for every network in the pseudonetwork.  Finally, the number of
136  * components, networks, power networks, and ground networks is returned
137  * in the reference parameters "components", "nets", "powernets", and
138  * "groundnets".
139  *
140  * A number of switches controls the flattening:
141  * If "hierarchical"        is true, the network will be fully instantiated.
142  * If "mergeparallel"       is true, parallel components are merged into one.
143  * If "mergeseries"         is true, series transistors are merged into one.
144  * If "checkcelloverrides"  is true, individual cells may override "mergeparallel".
145  * If "figuresizes"         is true, extract size information (takes time if programmed).
146  */
net_makepseudo(NODEPROTO * cell,INTBIG * components,INTBIG * nets,INTBIG * powernets,INTBIG * groundnets,PNET ** pnetlist,BOOLEAN hierarchical,BOOLEAN mergeparallel,BOOLEAN mergeseries,BOOLEAN checkcelloverrides,BOOLEAN figuresizes)147 PCOMP *net_makepseudo(NODEPROTO *cell, INTBIG *components, INTBIG *nets, INTBIG *powernets,
148 	INTBIG *groundnets, PNET **pnetlist, BOOLEAN hierarchical, BOOLEAN mergeparallel,
149 	BOOLEAN mergeseries, BOOLEAN checkcelloverrides, BOOLEAN figuresizes)
150 {
151 	PCOMP *pcomplist;
152 	PNET *globalpnetlist;
153 	REGISTER BOOLEAN localmergeparallel, localmergeseries, killnet;
154 	REGISTER INTBIG i, mergecount;
155 	REGISTER NETWORK *net, *subnet;
156 	REGISTER PNET *pn, *lastpnet, *nextpnet;
157 	REGISTER VARIABLE *var;
158 	REGISTER LIBRARY *lib;
159 	REGISTER NODEPROTO *np;
160 	REGISTER PORTPROTO *pp, *opp;
161 
162 	/* cache NCC options */
163 	var = getvalkey((INTBIG)net_tool, VTOOL, VINTEGER, net_ncc_optionskey);
164 	if (var == NOVARIABLE) net_ncc_options = 0; else
165 		net_ncc_options = var->addr;
166 
167 	/* see if the current cell overrides the options */
168 	localmergeparallel = mergeparallel;
169 	localmergeseries = mergeseries;
170 	if (checkcelloverrides)
171 	{
172 		var = getvalkey((INTBIG)cell, VNODEPROTO, VINTEGER, net_ncc_optionskey);
173 		if (var != NOVARIABLE)
174 		{
175 			if ((var->addr&NCCNOMERGEPARALLELOVER) != 0)
176 			{
177 				if ((var->addr&NCCNOMERGEPARALLEL) == 0) localmergeparallel = TRUE; else
178 					localmergeparallel = FALSE;
179 			}
180 			if ((var->addr&NCCMERGESERIESOVER) != 0)
181 			{
182 				if ((var->addr&NCCMERGESERIES) != 0) localmergeseries = TRUE; else
183 					localmergeseries = FALSE;
184 			}
185 		}
186 	}
187 
188 	/* first create net numbers inside of this cell */
189 	net_pseudonode = 0;
190 	*pnetlist = NOPNET;
191 	globalpnetlist = NOPNET;
192 	for(net = cell->firstnetwork; net != NONETWORK; net = net->nextnetwork)
193 		net->temp1 = (INTBIG)NOPNET;
194 	for(pp = cell->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
195 	{
196 		net = pp->network;
197 		if (net->temp1 != (INTBIG)NOPNET) continue;
198 		pn = net_newpnet(net, pnetlist, &globalpnetlist);
199 		if (pn == NOPNET) { *components = -1;   return(NOPCOMP); }
200 		if ((pp->userbits&STATEBITS) == PWRPORT) pn->flags |= POWERNET; else
201 			if ((pp->userbits&STATEBITS) == GNDPORT) pn->flags |= GROUNDNET;
202 		pn->flags |= EXPORTEDNET;
203 		net->temp1 = (INTBIG)pn;
204 		net_addexporttopnet(pn, pp);
205 		for(opp = pp->nextportproto; opp != NOPORTPROTO; opp = opp->nextportproto)
206 		{
207 			if (net != opp->network) continue;
208 			net_addexporttopnet(pn, opp);
209 		}
210 		if (net->buswidth > 1)
211 		{
212 			for(i=0; i<net->buswidth; i++)
213 			{
214 				subnet = net->networklist[i];
215 				pn = (PNET *)subnet->temp1;
216 				if (pn != NOPNET)
217 				{
218 					net_addexporttopnet(pn, pp);
219 					continue;
220 				}
221 				pn = net_newpnet(subnet, pnetlist, &globalpnetlist);
222 				pn->flags |= EXPORTEDNET;
223 				subnet->temp1 = (INTBIG)pn;
224 				pn->realportcount = 1;
225 				pn->realportlist = pp;
226 			}
227 		}
228 	}
229 
230 	/* create a list of pseudocomponents in this cell */
231 	*components = 0;
232 	begintraversehierarchy();
233 	net_toplevelinst = NONODEINST;
234 	net_mfactorwarned = FALSE;
235 	pcomplist = net_buildpseudo(cell, NOPCOMP, components, pnetlist, &globalpnetlist,
236 		hierarchical, figuresizes, 1.0f);
237 	endtraversehierarchy();
238 	if (pcomplist == 0) { *components = -1;   return(NOPCOMP); }
239 	if (*components == 0)
240 		ttyputmsg(_("There are no components in cell %s"), describenodeproto(cell));
241 
242 	/* append all global networks to the main list */
243 	while (globalpnetlist != NOPNET)
244 	{
245 		pn = globalpnetlist;
246 		globalpnetlist = pn->nextpnet;
247 
248 		pn->nextpnet = *pnetlist;
249 		*pnetlist = pn;
250 	}
251 
252 	/* reduce network by merging parallel components */
253 	if (localmergeparallel)
254 	{
255 		ttyputmsg(_("--- Merging parallel components in cell %s..."),
256 			describenodeproto(cell));
257 		mergecount = net_mergeparallel(&pcomplist, *pnetlist, components);
258 		if (mergecount < 0) { *components = -1;   return(NOPCOMP); }
259 		if (mergecount != 0)
260 			ttyputmsg(_("--- Merged %ld parallel components"), mergecount);
261 	}
262 	if (localmergeseries)
263 	{
264 		mergecount = net_mergeseries(&pcomplist, *pnetlist, components);
265 		if (mergecount != 0)
266 			ttyputmsg(_("--- Merged %ld series transistors in cell %s"),
267 				mergecount, describenodeproto(cell));
268 	}
269 
270 	/* look to the bottom of the hierarchy and be sure all global signals are listed */
271 	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
272 		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
273 			np->temp1 &= ~NETGLOBALCHECKED;
274 	net_gatherglobals(cell, pnetlist, &globalpnetlist);
275 
276 	/* count the power and ground nets */
277 	net_fillinnetpointers(pcomplist, *pnetlist);
278 	*powernets = *groundnets = *nets = 0;
279 	lastpnet = NOPNET;
280 	for(pn = *pnetlist; pn != NOPNET; pn = nextpnet)
281 	{
282 		nextpnet = pn->nextpnet;
283 		net = pn->network;
284 		killnet = FALSE;
285 		if (pn->nodecount <= 0 && (pn->flags&(POWERNET|GROUNDNET)) == 0)
286 		{
287 			if (net == NONETWORK) killnet = TRUE;
288 			if ((net_ncc_options & NCCINCLUDENOCOMPNETS) == 0 && net->buswidth <= 1)
289 				killnet = TRUE;
290 		}
291 		if (killnet)
292 		{
293 			if (lastpnet == NOPNET) *pnetlist = nextpnet; else
294 				lastpnet->nextpnet = nextpnet;
295 			net_freepnet(pn);
296 		} else
297 		{
298 			(*nets)++;
299 			if ((pn->flags&POWERNET) != 0) (*powernets)++;
300 			if ((pn->flags&GROUNDNET) != 0) (*groundnets)++;
301 			if ((pn->flags&(POWERNET|GROUNDNET)) == (POWERNET|GROUNDNET))
302 			{
303 				if (pn->network == NONETWORK) np = NONODEPROTO; else
304 					np = pn->network->parent;
305 				ttyputmsg(_("WARNING: cell %s, network '%s' shorts power and ground"),
306 					describenodeproto(np), describenetwork(pn->network));
307 			}
308 			lastpnet = pn;
309 		}
310 	}
311 
312 	return(pcomplist);
313 }
314 
315 /*
316  * Routine to recursively examine the hierarchy and make sure that all globals are mentioned
317  * at the top level.
318  */
net_gatherglobals(NODEPROTO * cell,PNET ** pnetlist,PNET ** globalpnetlist)319 void net_gatherglobals(NODEPROTO *cell, PNET **pnetlist, PNET **globalpnetlist)
320 {
321 	REGISTER INTBIG i, index;
322 	REGISTER NETWORK *net;
323 	REGISTER NODEINST *ni;
324 	REGISTER NODEPROTO *cnp, *subnp, *pnparent;
325 	REGISTER PNET *pn;
326 
327 	if ((cell->temp1&NETGLOBALCHECKED) != 0) return;
328 	cell->temp1 |= NETGLOBALCHECKED;
329 
330 	/* look at all globals in this cell */
331 	for(i=0; i<cell->globalnetcount; i++)
332 	{
333 		net = cell->globalnetworks[i];
334 		if (net == NONETWORK) continue;
335 		for(pn = *pnetlist; pn != NOPNET; pn = pn->nextpnet)
336 		{
337 			if (pn->network == NONETWORK) continue;
338 			index = pn->network->globalnet;
339 			if (index < 0) continue;
340 			pnparent = pn->network->parent;
341 			if (namesame(pnparent->globalnetnames[index], cell->globalnetnames[i]) == 0) break;
342 		}
343 		if (pn != NOPNET) continue;
344 
345 		/* didn't find network "net" in the list */
346 		pn = net_newpnet(net, pnetlist, globalpnetlist);
347 		net->temp1 = (INTBIG)pn;
348 	}
349 
350 	/* now recurse */
351 	for(ni = cell->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
352 	{
353 		subnp = ni->proto;
354 		if (subnp->primindex != 0) continue;
355 
356 		/* don't recurse into a schematic's own icon */
357 		if (isiconof(subnp, cell)) continue;
358 
359 		/* switch from icon to schematic */
360 		cnp = contentsview(subnp);
361 		if (cnp != NONODEPROTO) subnp = cnp;
362 		net_gatherglobals(subnp, pnetlist, globalpnetlist);
363 	}
364 }
365 
366 /*
367  * routine to build a linked list of pseudocomponents in cell "cell".  The
368  * list is appended to "initiallist" and returned.  The values of "power" and
369  * "ground" are the PNETs of such components.  Routine increments the
370  * integer at "components" for every component created.  If
371  * "compare_hierarchically" is nonzero, net is flattened.  Returns zero on error.
372  */
net_buildpseudo(NODEPROTO * cell,PCOMP * initiallist,INTBIG * components,PNET ** pnetlist,PNET ** globalpnetlist,BOOLEAN compare_hierarchically,BOOLEAN figuresizes,float mfactor)373 PCOMP *net_buildpseudo(NODEPROTO *cell, PCOMP *initiallist, INTBIG *components,
374 	PNET **pnetlist, PNET **globalpnetlist, BOOLEAN compare_hierarchically,
375 	BOOLEAN figuresizes, float mfactor)
376 {
377 	REGISTER PCOMP *pcomp;
378 	REGISTER PORTPROTO *pp, *opp, *realpp, *temprealpp;
379 	REGISTER PORTARCINST *pi;
380 	REGISTER PORTEXPINST *pe;
381 	REGISTER NETWORK *net, *subnet, *foundnet;
382 	REGISTER NODEINST *ni;
383 	REGISTER ARCINST *ai;
384 	NODEINST **nilist;
385 	float submfactor, thismfactor;
386 	REGISTER INTBIG fun, i, j, k, l, toplevel, flattenit, lambda,
387 		nodewidth, nindex, sigcount, stopcheck;
388 	BOOLEAN localcompare_hierarchically;
389 	REGISTER CHAR *pt;
390 	INTBIG width, length, pathcount, *indexlist;
391 	REGISTER NODEPROTO *realnp, *anp, *cnp;
392 	REGISTER PNET *pn;
393 	REGISTER VARIABLE *var;
394 
395 	if (net_toplevelinst == NONODEINST) toplevel = 1; else
396 		toplevel = 0;
397 
398 	/* make simple checks that port characteristics match the name */
399 	for(pp = cell->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
400 	{
401 		/* check busses for consistent component characteristics */
402 		if (pp->network->buswidth > 1)
403 		{
404 			for(i=0; i<pp->network->buswidth; i++)
405 			{
406 				subnet = pp->network->networklist[i];
407 				for(opp = cell->firstportproto; opp != NOPORTPROTO; opp = opp->nextportproto)
408 				{
409 					if (opp->network != subnet) continue;
410 					if ((opp->userbits&STATEBITS) != (pp->userbits&STATEBITS))
411 					{
412 						ttyputerr(_("Warning: bus export %s is %s but export %s is %s"),
413 							pp->protoname, describeportbits(pp->userbits), opp->protoname,
414 								describeportbits(opp->userbits));
415 					}
416 					break;
417 				}
418 			}
419 		}
420 	}
421 
422 	/* spread power and ground information from appropriate nodes */
423 	for(ni = cell->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
424 	{
425 		/* see if power or ground comes from this node */
426 		for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
427 			if ((pe->exportproto->userbits&STATEBITS) == PWRPORT ||
428 				(pe->exportproto->userbits&STATEBITS) == GNDPORT) break;
429 		if (pe == NOPORTEXPINST) continue;
430 
431 		/* they do: get the network */
432 		pp = pe->proto;
433 		for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
434 			if (pi->proto == pp) break;
435 		if (pi == NOPORTARCINST) continue;
436 		net = pi->conarcinst->network;
437 
438 		/* propagate power and ground */
439 		if ((pe->exportproto->userbits&STATEBITS) == PWRPORT)
440 		{
441 			pn = (PNET *)net->temp1;
442 			if (pn == NOPNET)
443 				pn = net_newpnet(net, pnetlist, globalpnetlist);
444 			pn->flags |= POWERNET;
445 		}
446 		if ((pe->exportproto->userbits&STATEBITS) == GNDPORT)
447 		{
448 			pn = (PNET *)net->temp1;
449 			if (pn == NOPNET)
450 				pn = net_newpnet(net, pnetlist, globalpnetlist);
451 			pn->flags |= GROUNDNET;
452 		}
453 	}
454 
455 	/* generate new pseudo-netnumbers for networks not connected to ports */
456 	for(net = cell->firstnetwork; net != NONETWORK; net = net->nextnetwork)
457 	{
458 		if (net->temp1 != (INTBIG)NOPNET) continue;
459 		pn = net_newpnet(net, pnetlist, globalpnetlist);
460 		if (pn == NOPNET) return(0);
461 		net_addtraversalpath(pn, cell);
462 		net->temp1 = (INTBIG)pn;
463 		if (net->buswidth > 1)
464 		{
465 			for(i=0; i<net->buswidth; i++)
466 			{
467 				subnet = net->networklist[i];
468 				if (subnet->temp1 != (INTBIG)NOPNET) continue;
469 				pn = net_newpnet(subnet, pnetlist, globalpnetlist);
470 				net_addtraversalpath(pn, cell);
471 				subnet->temp1 = (INTBIG)pn;
472 			}
473 		}
474 	}
475 
476 	/* add in forced matches on arcs */
477 	for(ai = cell->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
478 	{
479 		var = getvalkey((INTBIG)ai, VARCINST, VSTRING, net_ncc_forcedassociationkey);
480 		if (var == NOVARIABLE) continue;
481 		pn = (PNET *)ai->network->temp1;
482 		for(pt = (CHAR *)var->addr; *pt != 0; pt++)
483 			pn->forcedassociation += (INTBIG)*pt;
484 	}
485 
486 	/* search every component in the cell */
487 	stopcheck = 0;
488 	for(ni = cell->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
489 	{
490 		if (((stopcheck++)%5) == 0)
491 		{
492 			if (stopping(STOPREASONNCC)) return(0);
493 		}
494 
495 		anp = ni->proto;
496 		if (toplevel != 0) net_toplevelinst = ni;
497 
498 		/* ignore recursive references (showing icon in contents) */
499 		if (isiconof(anp, cell)) continue;
500 
501 		/* if there is an alternate contents cell, use it */
502 		realnp = contentsview(anp);
503 		if (realnp == NONODEPROTO) realnp = anp;
504 
505 		/* if flattening the circuit, explore contents of cell instances */
506 		flattenit = 0;
507 		localcompare_hierarchically = compare_hierarchically;
508 		var = getvalkey((INTBIG)realnp, VNODEPROTO, VINTEGER, net_ncc_optionskey);
509 		if (var != NOVARIABLE)
510 		{
511 			if ((var->addr&NCCHIERARCHICALOVER) != 0)
512 			{
513 				if ((var->addr&NCCHIERARCHICAL) != 0) localcompare_hierarchically = TRUE; else
514 					localcompare_hierarchically = FALSE;
515 			}
516 		}
517 		if (anp->primindex == 0 && localcompare_hierarchically) flattenit = 1;
518 
519 		/* determine whether the node is arrayed */
520 		nodewidth = ni->arraysize;
521 		if (nodewidth < 1) nodewidth = 1;
522 
523 		/* accumulate m factors */
524 		submfactor = mfactor;
525 		var = getvalkey((INTBIG)ni, VNODEINST, -1, el_attrkey_M);
526 		if (var != NOVARIABLE)
527 		{
528 			CHAR *pt = describevariable(var, -1, -1);
529 			if (isanumber(pt))
530 			{
531 				thismfactor = (float)atof(pt);
532 				if (thismfactor == 0.0)
533 				{
534 					if (!net_mfactorwarned)
535 						ttyputmsg(_("WARNING: Cell %s, node %s has a zero M-factor"),
536 							describenodeproto(ni->parent), describenodeinst(ni));
537 					net_mfactorwarned = TRUE;
538 				}
539 				submfactor *= thismfactor;
540 			}
541 		}
542 
543 		/* run through each instantiation of the node */
544 		for(nindex = 0; nindex < nodewidth; nindex++)
545 		{
546 			if (flattenit != 0)
547 			{
548 				/* put pseudo-netnumbers on the networks of this cell */
549 				for(net = realnp->firstnetwork; net != NONETWORK; net = net->nextnetwork)
550 					net->temp1 = (INTBIG)NOPNET;
551 				for(realpp = realnp->firstportproto; realpp != NOPORTPROTO; realpp = realpp->nextportproto)
552 				{
553 					if (realpp->network->temp1 != (INTBIG)NOPNET) continue;
554 
555 					/* if there is an alternate contents cell, compute the port */
556 					if (realnp == anp) pp = realpp; else
557 					{
558 						pp = equivalentport(realnp, realpp, anp);
559 						if (pp == NOPORTPROTO) pp = anp->firstportproto;
560 					}
561 
562 					/* see if an arc connects to the port */
563 					foundnet = NONETWORK;
564 					for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
565 					{
566 						temprealpp = equivalentport(anp, pi->proto, realnp);
567 						if (temprealpp == NOPORTPROTO)
568 						{
569 							if (pi->proto->network == pp->network) break;
570 						} else
571 						{
572 							if (temprealpp->network == realpp->network) break;
573 						}
574 					}
575 					if (pi != NOPORTARCINST && pi->conarcinst->network != NONETWORK)
576 					{
577 						foundnet = pi->conarcinst->network;
578 					} else
579 					{
580 						/* see if the port is an export */
581 						for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
582 						{
583 							temprealpp = equivalentport(anp, pe->proto, realnp);
584 							if (temprealpp == NOPORTPROTO)
585 							{
586 								if (pe->proto->network == pp->network) break;
587 							} else
588 							{
589 								if (temprealpp->network == realpp->network) break;
590 							}
591 						}
592 						if (pe != NOPORTEXPINST) foundnet = pe->exportproto->network; else
593 						{
594 							pn = net_newpnet(realpp->network, pnetlist, globalpnetlist);
595 							if (pn == NOPNET) return(0);
596 							net_addtraversalpath(pn, cell);
597 							if ((realpp->userbits&STATEBITS) == PWRPORT)
598 							{
599 								pn->flags = POWERNET;
600 							} else if ((realpp->userbits&STATEBITS) == GNDPORT)
601 							{
602 								pn->flags = GROUNDNET;
603 							}
604 							realpp->network->temp1 = (INTBIG)pn;
605 						}
606 					}
607 					if (foundnet != NONETWORK)
608 					{
609 						/* propagate export networks to nets inside cell */
610 						if (foundnet->buswidth > 1)
611 						{
612 							sigcount = foundnet->buswidth;
613 							if (nodewidth > 1 && realpp->network->buswidth * nodewidth == foundnet->buswidth)
614 							{
615 								/* map wide bus to a particular instantiation of an arrayed node */
616 								if (realpp->network->buswidth == 1)
617 								{
618 									realpp->network->temp1 = foundnet->networklist[nindex]->temp1;
619 								} else
620 								{
621 									for(i=0; i<realpp->network->buswidth; i++)
622 										realpp->network->networklist[i]->temp1 =
623 											foundnet->networklist[i + nindex*realpp->network->buswidth]->temp1;
624 								}
625 							} else
626 							{
627 								if (realpp->network->buswidth != foundnet->buswidth)
628 								{
629 									ttyputerr(_("***ERROR: port %s on node %s in cell %s is %d wide, but is connected/exported with width %d"),
630 										realpp->protoname, describenodeinst(ni), describenodeproto(cell),
631 											realpp->network->buswidth, foundnet->buswidth);
632 									sigcount = mini(sigcount, realpp->network->buswidth);
633 									if (sigcount == 1) sigcount = 0;
634 								}
635 								realpp->network->temp1 = (INTBIG)foundnet->temp1;
636 								for(i=0; i<sigcount; i++)
637 									realpp->network->networklist[i]->temp1 = foundnet->networklist[i]->temp1;
638 							}
639 						} else
640 						{
641 							realpp->network->temp1 = (INTBIG)foundnet->temp1;
642 						}
643 					}
644 				}
645 
646 				/* recurse into the cell */
647 				downhierarchy(ni, realnp, nindex);
648 				initiallist = net_buildpseudo(realnp, initiallist, components, pnetlist,
649 					globalpnetlist, compare_hierarchically, figuresizes, submfactor);
650 				uphierarchy();
651 				if (initiallist == 0) return(0);
652 				continue;
653 			}
654 
655 			/* nonflattenable component: add it to the pseudocomponent list */
656 			if (anp->primindex == 0)
657 			{
658 				fun = net_getcellfunction(anp);
659 			} else
660 			{
661 				fun = net_getfunction(ni);
662 				if (fun == NPCONNECT || fun == NPART || fun == NPUNKNOWN ||
663 					fun == NPCONPOWER || fun == NPCONGROUND) continue;
664 
665 				/* special case: ignore resistors and capacitors if they are being ignored electrically */
666 				if (fun == NPRESIST || fun == NPCAPAC || fun == NPECAPAC)
667 				{
668 					if (asktech(sch_tech, x_("ignoring-resistor-topology")) != 0)
669 						continue;
670 				}
671 			}
672 
673 			/* create a pseudo-component */
674 			pcomp = net_allocpcomp();
675 			if (pcomp == NOPCOMP) return(0);
676 			pcomp->nextpcomp = initiallist;
677 			initiallist = pcomp;
678 			pcomp->function = (INTSML)fun;
679 			pcomp->hashreason = 0;
680 			pcomp->forcedassociation = 0;
681 			var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, net_ncc_forcedassociationkey);
682 			if (var != NOVARIABLE)
683 			{
684 				pt = (CHAR *)var->addr;
685 				for(pt = (CHAR *)var->addr; *pt != 0; pt++)
686 					pcomp->forcedassociation += (INTBIG)*pt;
687 			}
688 			gettraversalpath(ni->parent, NOWINDOWPART, &nilist, &indexlist, &pathcount, 0);
689 			pcomp->hierpathcount = pathcount;
690 			if (pcomp->hierpathcount > 0)
691 			{
692 				pcomp->hierpath = (NODEINST **)emalloc(pcomp->hierpathcount *
693 					(sizeof (NODEINST *)), net_tool->cluster);
694 				if (pcomp->hierpath == 0) return(0);
695 				pcomp->hierindex = (INTBIG *)emalloc(pcomp->hierpathcount *
696 					SIZEOFINTBIG, net_tool->cluster);
697 				if (pcomp->hierindex == 0) return(0);
698 				for(i=0; i<pcomp->hierpathcount; i++)
699 				{
700 					pcomp->hierpath[i] = nilist[i];
701 					pcomp->hierindex[i] = indexlist[i];
702 				}
703 			}
704 			pcomp->actuallist = (void *)ni;
705 			pcomp->numactual = 1;
706 			pcomp->topactual = net_toplevelinst;
707 			(*components)++;
708 
709 			/* count the number of electrically distinct nets on the component */
710 			pcomp->wirecount = 0;
711 			cnp = contentsview(anp);
712 			for(pp = anp->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
713 			{
714 				/* get real export on contents */
715 				if (cnp == NONODEPROTO) realpp = pp; else
716 				{
717 					realpp = equivalentport(anp, pp, cnp);
718 					if (realpp == NOPORTPROTO) continue;
719 				}
720 
721 				/* special case for isolated ports */
722 				if ((realpp->userbits&PORTISOLATED) != 0)
723 				{
724 					/* add one wire for each arc on the port */
725 					for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
726 						if (pi->proto == pp) pcomp->wirecount++;
727 					continue;
728 				}
729 
730 				/* new port, add in the number of signals */
731 				if (pp->network->buswidth <= 1) pcomp->wirecount++; else
732 					pcomp->wirecount += pp->network->buswidth;
733 			}
734 
735 			/* get parameters for the node */
736 			switch (fun)
737 			{
738 				case NPTRANMOS:  case NPTRA4NMOS:
739 				case NPTRADMOS:  case NPTRA4DMOS:
740 				case NPTRAPMOS:  case NPTRA4PMOS:
741 				case NPTRANJFET: case NPTRA4NJFET:
742 				case NPTRAPJFET: case NPTRA4PJFET:
743 				case NPTRADMES:  case NPTRA4DMES:
744 				case NPTRAEMES:  case NPTRA4EMES:
745 					/* transistors that have a length and a width */
746 					if (figuresizes)
747 					{
748 						lambda = lambdaofnode(ni);
749 						if (ni->proto->primindex == 0)
750 						{
751 							var = getvalkey((INTBIG)ni, VNODEINST, VINTEGER, el_attrkey_width);
752 							if (var != NOVARIABLE) width = var->addr * lambda; else
753 								width = 0;
754 							var = getvalkey((INTBIG)ni, VNODEINST, VINTEGER, el_attrkey_length);
755 							if (var != NOVARIABLE) length = var->addr * lambda; else
756 								length = 0;
757 						} else
758 						{
759 							transistorsize(ni, &length, &width);
760 						}
761 						pcomp->length = (float)muldiv(length, WHOLE, lambda);
762 						pcomp->width = (float)muldiv(width, WHOLE, lambda) * submfactor;
763 					} else
764 					{
765 						/* just set fake sizes */
766 						pcomp->length = pcomp->width = 2.0f * submfactor;
767 					}
768 					pcomp->flags |= COMPHASWIDLEN;
769 #ifdef IGNOREFOURPORT
770 					pcomp->wirecount = 3;
771 #endif
772 					break;
773 
774 				case NPTRANPN:    case NPTRA4NPN:
775 				case NPTRAPNP:    case NPTRA4PNP:
776 					/* transistors that have an area */
777 					if (figuresizes)
778 					{
779 						lambda = lambdaofnode(ni);
780 						if (ni->proto->primindex == 0)
781 						{
782 							var = getvalkey((INTBIG)ni, VNODEINST, VINTEGER, el_attrkey_area);
783 							if (var != NOVARIABLE) length = var->addr * lambda; else
784 								length = 0;
785 						} else
786 						{
787 							transistorsize(ni, &length, &width);
788 						}
789 						if (length < 0) pcomp->length = 0.0; else
790 							pcomp->length = (float)muldiv(length, WHOLE, lambda) * submfactor;
791 					} else pcomp->length = 4.0f * submfactor;
792 					pcomp->width = 0.0;
793 					pcomp->flags |= COMPHASAREA;
794 					break;
795 
796 				case NPRESIST:
797 				case NPCAPAC:   case NPECAPAC:
798 				case NPINDUCT:
799 					pcomp->length = net_getpartvalue(ni) * submfactor;
800 					pcomp->width = 0.0;
801 					pcomp->flags |= COMPHASAREA;
802 					break;
803 			}
804 
805 			/* no further information if there are no wires */
806 			if (pcomp->wirecount == 0) continue;
807 
808 			/* allocate the port and connection lists */
809 			pcomp->portlist = (PORTPROTO **)emalloc(((sizeof (PORTPROTO *)) * pcomp->wirecount),
810 				net_tool->cluster);
811 			if (pcomp->portlist == 0) return(0);
812 			pcomp->state = (INTSML *)emalloc((SIZEOFINTSML * pcomp->wirecount),
813 				net_tool->cluster);
814 			if (pcomp->state == 0) return(0);
815 			pcomp->portindices = (INTSML *)emalloc((SIZEOFINTSML * pcomp->wirecount),
816 				net_tool->cluster);
817 			if (pcomp->portindices == 0) return(0);
818 			pcomp->netnumbers = (PNET **)emalloc(((sizeof (PNET *)) * pcomp->wirecount),
819 				net_tool->cluster);
820 			if (pcomp->netnumbers == 0) return(0);
821 			for(i=0; i<pcomp->wirecount; i++)
822 				pcomp->state[i] = 0;
823 
824 			switch (pcomp->function)
825 			{
826 				case NPTRANMOS:
827 				case NPTRADMOS:
828 				case NPTRAPMOS:
829 				case NPTRADMES:
830 				case NPTRAEMES:
831 				case NPTRANPN:
832 				case NPTRAPNP:
833 				case NPTRANJFET:
834 				case NPTRAPJFET:
835 					/* transistors make the active ports equivalent */
836 					if (anp->primindex == 0)
837 					{
838 						pcomp->portlist[0] = getportproto(anp, "g");
839 						pcomp->portlist[1] = getportproto(anp, "s");
840 						pcomp->portlist[2] = getportproto(anp, "d");
841 						if (pcomp->portlist[0] == NOPORTPROTO || pcomp->portlist[0] == NOPORTPROTO ||
842 							pcomp->portlist[0] == NOPORTPROTO)
843 						{
844                             ttyputmsg("Transistor cell %s must have exports 'g', 's', and 'd'",
845 								describenodeproto(anp));
846 						}
847 					} else
848 					{
849 						pcomp->portlist[0] = anp->firstportproto;
850 						pcomp->portlist[1] = pcomp->portlist[0]->nextportproto;
851 						pcomp->portlist[2] = pcomp->portlist[1]->nextportproto;
852 						if (anp != sch_transistorprim && anp != sch_transistor4prim)
853 							pcomp->portlist[2] = pcomp->portlist[2]->nextportproto;
854 					}
855 					for(j=0; j<pcomp->wirecount; j++)
856 					{
857 						pp = pcomp->portlist[j];
858 						if (pp == NOPORTPROTO) continue;
859 						pcomp->portindices[j] = (INTSML)pp->network->temp2;
860 						if (net_getpnetandstate(ni, pp, NONETWORK, -1, &pcomp->netnumbers[j],
861 							&pcomp->state[j], pnetlist, globalpnetlist, nodewidth, nindex,
862 								pp->network->buswidth)) return(0);
863 					}
864 					break;
865 
866 				case NPTRA4NMOS:
867 				case NPTRA4DMOS:
868 				case NPTRA4PMOS:
869 				case NPTRA4DMES:
870 				case NPTRA4EMES:
871 				case NPTRA4NPN:
872 				case NPTRA4PNP:
873 				case NPTRA4NJFET:
874 				case NPTRA4PJFET:
875 					/* 4-port transistors make the active two equivalent */
876 					if (anp->primindex == 0)
877 					{
878 						ttyputmsg("Cannot handle transistor functions yet");
879 					} else
880 					{
881 						pcomp->portlist[0] = anp->firstportproto;
882 						pcomp->portlist[1] = pcomp->portlist[0]->nextportproto;
883 						pcomp->portlist[2] = pcomp->portlist[1]->nextportproto;
884 						pcomp->portlist[3] = pcomp->portlist[2]->nextportproto;
885 						for(j=0; j<pcomp->wirecount; j++)
886 						{
887 							pp = pcomp->portlist[j];
888 							pcomp->portindices[j] = (INTSML)pp->network->temp2;
889 							if (net_getpnetandstate(ni, pp, NONETWORK, -1, &pcomp->netnumbers[j],
890 								&pcomp->state[j], pnetlist, globalpnetlist, nodewidth, nindex,
891 									pp->network->buswidth)) return(0);
892 						}
893 					}
894 					break;
895 
896 				case NPRESIST:
897 				case NPCAPAC:   case NPECAPAC:
898 				case NPINDUCT:
899 					/* resistors, capacitors, and inductors have equivalent ports */
900 					pcomp->portlist[0] = anp->firstportproto;
901 					pcomp->portlist[1] = pcomp->portlist[0]->nextportproto;
902 					for(j=0; j<pcomp->wirecount; j++)
903 					{
904 						pp = pcomp->portlist[j];
905 						pcomp->portindices[j] = (INTSML)getprime(0);
906 						if (net_getpnetandstate(ni, pp, NONETWORK, -1, &pcomp->netnumbers[j],
907 							&pcomp->state[j], pnetlist, globalpnetlist, nodewidth, nindex,
908 								pp->network->buswidth)) return(0);
909 					}
910 					break;
911 
912 				default:
913 					j = 0;
914 					for(pp = anp->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
915 					{
916 						/* get real export on contents */
917 						if (cnp == NONODEPROTO) realpp = pp; else
918 						{
919 							realpp = equivalentport(anp, pp, cnp);
920 							if (realpp == NOPORTPROTO) continue;
921 						}
922 						sigcount = realpp->network->buswidth;
923 
924 						/* special case for isolated ports */
925 						if ((realpp->userbits&PORTISOLATED) != 0)
926 						{
927 							/* add one wire for each arc on the port */
928 							for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
929 							{
930 								if (pi->proto != pp) continue;
931 								pcomp->portlist[j] = pp;
932 								pcomp->portindices[j] = (INTSML)pp->network->temp2;
933 								if (net_getpnetandstate(ni, pp, pi->conarcinst->network, -1,
934 									&pcomp->netnumbers[j], &pcomp->state[j], pnetlist, globalpnetlist,
935 										nodewidth, nindex, sigcount)) return(0);
936 								j++;
937 							}
938 							continue;
939 						}
940 
941 						if (sigcount <= 1)
942 						{
943 							/* single-wire port */
944 							pcomp->portlist[j] = pp;
945 							pcomp->portindices[j] = (INTSML)realpp->network->temp2;
946 							for(l=0; l<j; l++) if (pcomp->portindices[l] == pcomp->portindices[j]) break;
947 							if (l >= j)
948 							{
949 								if (net_getpnetandstate(ni, pp, NONETWORK, -1, &pcomp->netnumbers[j],
950 									&pcomp->state[j], pnetlist, globalpnetlist, nodewidth, nindex,
951 										sigcount)) return(0);
952 								j++;
953 							}
954 						} else
955 						{
956 							/* bus port */
957 							for(k=0; k<sigcount; k++)
958 							{
959 								pcomp->portlist[j] = pp;
960 								pcomp->portindices[j] = (INTSML)realpp->network->networklist[k]->temp2;
961 								for(l=0; l<j; l++) if (pcomp->portindices[l] == pcomp->portindices[j]) break;
962 								if (l >= j)
963 								{
964 									if (net_getpnetandstate(ni, pp, NONETWORK, k,
965 										&pcomp->netnumbers[j], &pcomp->state[j], pnetlist, globalpnetlist,
966 											nodewidth, nindex, sigcount)) return(0);
967 									j++;
968 								}
969 							}
970 						}
971 					}
972 					pcomp->wirecount = (INTSML)j;
973 					break;
974 			}
975 		}
976 	}
977 	return(initiallist);
978 }
979 
980 /*
981  * Routine to add the traversal path to cell "cell" to the PNET "pn".
982  */
net_addtraversalpath(PNET * pn,NODEPROTO * cell)983 void net_addtraversalpath(PNET *pn, NODEPROTO *cell)
984 {
985 #ifdef PATHTOPNET
986 	NODEINST **nilist;
987 	INTBIG pathcount, *indexlist;
988 	REGISTER INTBIG i;
989 
990 	gettraversalpath(cell, NOWINDOWPART, &nilist, &indexlist, &pathcount, 0);
991 	pn->hierpathcount = pathcount;
992 	if (pn->hierpathcount > 0)
993 	{
994 		pn->hierpath = (NODEINST **)emalloc(pn->hierpathcount *
995 			(sizeof (NODEINST *)), net_tool->cluster);
996 		if (pn->hierpath == 0) return;
997 		pn->hierindex = (INTBIG *)emalloc(pn->hierpathcount *
998 			SIZEOFINTBIG, net_tool->cluster);
999 		if (pn->hierindex == 0) return;
1000 		for(i=0; i<pn->hierpathcount; i++)
1001 		{
1002 			pn->hierpath[i] = nilist[i];
1003 			pn->hierindex[i] = indexlist[i];
1004 		}
1005 	}
1006 #endif
1007 }
1008 
1009 /*
1010  * Routine to examine node "ni", port "pp", and find the PNET that is connected to it.
1011  * If "forcenet" is not NONETWORK, use it as the arc site.
1012  * If "index" is not negative, look for that entry in a bus.
1013  * The PNET and its state are stored in "netnumber" and "state".  If nothing is connected,
1014  * a new PNET is allocated and saved in the list "pnetlist".
1015  * Returns true on error.
1016  */
net_getpnetandstate(NODEINST * ni,PORTPROTO * pp,NETWORK * forcenet,INTBIG index,PNET ** netnumber,INTSML * state,PNET ** pnetlist,PNET ** globalpnetlist,INTBIG nodewidth,INTBIG nindex,INTBIG destsignals)1017 BOOLEAN net_getpnetandstate(NODEINST *ni, PORTPROTO *pp, NETWORK *forcenet,
1018 	INTBIG index, PNET **netnumber, INTSML *state, PNET **pnetlist, PNET **globalpnetlist,
1019 	INTBIG nodewidth, INTBIG nindex, INTBIG destsignals)
1020 {
1021 	REGISTER ARCINST *ai;
1022 	REGISTER INTBIG entry;
1023 	REGISTER PORTARCINST *pi;
1024 	REGISTER PORTEXPINST *pe;
1025 	REGISTER NETWORK *net;
1026 	REGISTER PNET *pn;
1027 
1028 	/* first look for an arc that connects */
1029 	for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
1030 	{
1031 		if (forcenet != NONETWORK)
1032 		{
1033 			if (forcenet != pi->conarcinst->network) continue;
1034 		} else
1035 		{
1036 			if (pi->proto->network != pp->network) continue;
1037 		}
1038 
1039 		/* pickup the network number of this connection */
1040 		ai = pi->conarcinst;
1041 		net = ai->network;
1042 		if (net == NONETWORK) continue;
1043 		if (index < 0)
1044 		{
1045 			if (nodewidth > 1 && net->buswidth == nodewidth)
1046 				*netnumber = (PNET *)net->networklist[nindex]->temp1; else
1047 					*netnumber = (PNET *)net->temp1;
1048 		} else
1049 		{
1050 			entry = index;
1051 			if (nodewidth > 1 && net->buswidth == nodewidth * destsignals)
1052 				entry = index + nindex*destsignals;
1053 			if (entry < net->buswidth)
1054 				*netnumber = (PNET *)net->networklist[entry]->temp1;
1055 		}
1056 		if ((ai->userbits&ISNEGATED) != 0)
1057 		{
1058 			if ((ai->end[0].portarcinst == pi && (ai->userbits&REVERSEEND) == 0) ||
1059 				(ai->end[1].portarcinst == pi && (ai->userbits&REVERSEEND) != 0))
1060 					*state = NEGATEDPORT;
1061 		}
1062 		if (*netnumber != NOPNET)
1063 		{
1064 			if (((*netnumber)->flags&EXPORTEDNET) != 0)
1065 				*state |= EXPORTEDPORT;
1066 			if (forcenet == NONETWORK)
1067 			{
1068 				if ((pp->userbits&STATEBITS) == GNDPORT) (*netnumber)->flags |= GROUNDNET; else
1069 					if ((pp->userbits&STATEBITS) == PWRPORT) (*netnumber)->flags |= POWERNET;
1070 			}
1071 			return(FALSE);
1072 		}
1073 	}
1074 
1075 	for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
1076 	{
1077 		if (forcenet != NONETWORK)
1078 		{
1079 			if (forcenet != pe->proto->network) continue;
1080 		} else
1081 		{
1082 			if (pe->proto->network != pp->network) continue;
1083 		}
1084 		net = pe->exportproto->network;
1085 		if (index < 0)
1086 		{
1087 			if (nodewidth > 1 && net->buswidth == nodewidth)
1088 				*netnumber = (PNET *)net->networklist[nindex]->temp1; else
1089 					*netnumber = (PNET *)net->temp1;
1090 		} else
1091 		{
1092 			entry = index;
1093 			if (nodewidth > 1 && net->buswidth == nodewidth * destsignals)
1094 				entry = index + nindex*destsignals;
1095 			if (entry < net->buswidth)
1096 				*netnumber = (PNET *)net->networklist[entry]->temp1;
1097 		}
1098 		*state |= EXPORTEDPORT;
1099 		if (*netnumber != NOPNET) return(FALSE);
1100 	}
1101 
1102 	/* not found on arc or export: create a new entry */
1103 	if (forcenet == NONETWORK)
1104 		ttyputmsg(_("Warning: cell %s: no connection to node %s, port %s"),
1105 			describenodeproto(ni->parent), describenodeinst(ni), pp->protoname);
1106 	pn = net_newpnet(forcenet, pnetlist, globalpnetlist);
1107 	if (pn == NOPNET) return(TRUE);
1108 	net_addtraversalpath(pn, ni->parent);
1109 	pn->network = forcenet;
1110 	*netnumber = pn;
1111 	return(FALSE);
1112 }
1113 
1114 /*
1115  * Routine to fill in the network pointers to components.
1116  */
net_fillinnetpointers(PCOMP * pcomplist,PNET * pnetlist)1117 void net_fillinnetpointers(PCOMP *pcomplist, PNET *pnetlist)
1118 {
1119 	REGISTER PCOMP *pc;
1120 	REGISTER PNET *pn;
1121 	REGISTER INTBIG i, index;
1122 
1123 	for(pn = pnetlist; pn != NOPNET; pn = pn->nextpnet)
1124 		pn->nodecount = 0;
1125 	for(pc = pcomplist; pc != NOPCOMP; pc = pc->nextpcomp)
1126 	{
1127 		for(i=0; i<pc->wirecount; i++)
1128 		{
1129 			pn = pc->netnumbers[i];
1130 			pn->nodecount++;
1131 		}
1132 	}
1133 
1134 	for(pn = pnetlist; pn != NOPNET; pn = pn->nextpnet)
1135 	{
1136 		if (pn->nodecount <= pn->nodetotal) continue;
1137 		if (pn->nodetotal > 0)
1138 		{
1139 			efree((CHAR *)pn->nodelist);
1140 			efree((CHAR *)pn->nodewire);
1141 			pn->nodetotal = 0;
1142 		}
1143 		pn->nodelist = (PCOMP **)emalloc(pn->nodecount * (sizeof (PCOMP *)), net_tool->cluster);
1144 		if (pn->nodelist == 0) return;
1145 		pn->nodewire = (INTBIG *)emalloc(pn->nodecount * SIZEOFINTBIG, net_tool->cluster);
1146 		if (pn->nodelist == 0) return;
1147 		pn->nodetotal = pn->nodecount;
1148 	}
1149 	for(pn = pnetlist; pn != NOPNET; pn = pn->nextpnet)
1150 		pn->nodecount = 0;
1151 	for(pc = pcomplist; pc != NOPCOMP; pc = pc->nextpcomp)
1152 	{
1153 		for(i=0; i<pc->wirecount; i++)
1154 		{
1155 			pn = pc->netnumbers[i];
1156 			if (pn->nodelist == 0)
1157 				continue;
1158 			index = pn->nodecount++;
1159 			if (index >= pn->nodetotal) continue;
1160 			pn->nodelist[index] = pc;
1161 			pn->nodewire[index] = i;
1162 		}
1163 	}
1164 }
1165 
1166 /*
1167  * Routine to reduce the network in "pcomp" to merge parallel components.
1168  */
net_mergeparallel(PCOMP ** pcomp,PNET * pnet,INTBIG * components)1169 INTBIG net_mergeparallel(PCOMP **pcomp, PNET *pnet, INTBIG *components)
1170 {
1171 	REGISTER PCOMP *pc, *opc, *nextpc, *lastpc, **complist;
1172 	REGISTER PNET *pn;
1173 	REGISTER INTBIG i, j, k, m, newnum, mergecount, compcount, counter;
1174 	REGISTER NODEINST *ni, **newlist, *newsingle;
1175 
1176 	net_fillinnetpointers(*pcomp, pnet);
1177 
1178 	/* assign values to each net */
1179 	counter = 0;
1180 	for(pn = pnet; pn != NOPNET; pn = pn->nextpnet)
1181 	{
1182 		counter += 6;
1183 		pn->timestamp = counter;
1184 	}
1185 
1186 	/* compute hash value for each component */
1187 	compcount = 0;
1188 	for(pc = *pcomp; pc != NOPCOMP; pc = pc->nextpcomp)
1189 	{
1190 		pc->flags &= ~COMPDELETED;
1191 		if (pc->function == NPUNKNOWN) continue;
1192 		counter = pc->function;
1193 		for(i=0; i<pc->wirecount; i++)
1194 		{
1195 			pn = pc->netnumbers[i];
1196 			counter += pn->timestamp * pc->portindices[i];
1197 		}
1198 
1199 		pc->timestamp = counter;
1200 		compcount++;
1201 	}
1202 
1203 	if (compcount == 0) return(0);
1204 	complist = (PCOMP **)emalloc(compcount * (sizeof (PCOMP *)), net_tool->cluster);
1205 	if (complist == 0) return(0);
1206 	compcount = 0;
1207 	for(pc = *pcomp; pc != NOPCOMP; pc = pc->nextpcomp)
1208 	{
1209 		if (pc->function == NPUNKNOWN) continue;
1210 		complist[compcount++] = pc;
1211 	}
1212 	esort(complist, compcount, sizeof (PCOMP *), net_sortpcompbyhash);
1213 
1214 	mergecount = 0;
1215 	for(i=0; i<compcount; i++)
1216 	{
1217 		if ((i%5) == 0)
1218 		{
1219 			if (stopping(STOPREASONNCC)) break;
1220 		}
1221 		opc = complist[i];
1222 		if ((opc->flags & COMPDELETED) != 0) continue;
1223 
1224 		for(m=i+1; m<compcount; m++)
1225 		{
1226 			pc = complist[m];
1227 			if (pc->timestamp != opc->timestamp) break;
1228 			if ((pc->flags & COMPDELETED) != 0) continue;
1229 
1230 			/* both components must have the same function */
1231 			if (pc->function != opc->function) continue;
1232 
1233 			/* compare the wire lists */
1234 			if (net_comparewirelist(pc, opc, FALSE)) continue;
1235 
1236 			/* components are equivalent: delete "pc" */
1237 			mergecount++;
1238 
1239 			/* add "pc"s node pointer to "opc" */
1240 			newnum = pc->numactual + opc->numactual;
1241 			newsingle = NONODEINST;
1242 			if (newnum > 1)
1243 			{
1244 				newlist = (NODEINST **)emalloc(newnum * (sizeof (NODEINST *)),
1245 					net_tool->cluster);
1246 				if (newlist == 0) return(0);
1247 			} else newlist = 0;
1248 			k = 0;
1249 			for(j=0; j<pc->numactual; j++)
1250 			{
1251 				if (pc->numactual == 1) ni = (NODEINST *)pc->actuallist; else
1252 					ni = ((NODEINST **)pc->actuallist)[j];
1253 				if (newnum == 1) newsingle = ni; else
1254 					newlist[k++] = ni;
1255 			}
1256 			for(j=0; j<opc->numactual; j++)
1257 			{
1258 				if (opc->numactual == 1) ni = (NODEINST *)opc->actuallist; else
1259 					ni = ((NODEINST **)opc->actuallist)[j];
1260 				if (newnum == 1) newsingle = ni; else
1261 					newlist[k++] = ni;
1262 			}
1263 			if (opc->numactual > 1) efree((CHAR *)opc->actuallist);
1264 			if (newnum == 1) opc->actuallist = (void *)newsingle; else
1265 				opc->actuallist = (void *)newlist;
1266 			opc->numactual = newnum;
1267 
1268 			/* combine sizes (as specified by Robert Bosnyak) */
1269 			if ((pc->flags&(COMPHASWIDLEN|COMPHASAREA)) != 0 &&
1270 				(opc->flags&(COMPHASWIDLEN|COMPHASAREA)) != 0)
1271 			{
1272 				switch (pc->function)
1273 				{
1274 					case NPTRANMOS:  case NPTRA4NMOS:
1275 					case NPTRADMOS:  case NPTRA4DMOS:
1276 					case NPTRAPMOS:  case NPTRA4PMOS:
1277 						/* FET transistors in parallel depend on whether the length is the same */
1278 						if (opc->length == pc->length)
1279 						{
1280 							/* same-length transistors: sum the width */
1281 							opc->width += pc->width;
1282 						} else
1283 						{
1284 							/* different-length transistors: more complex formula */
1285 							if (pc->width + opc->width != 0.0)
1286 							{
1287 								opc->length = (pc->width * pc->length + opc->width * opc->length) /
1288 									(pc->width + opc->width);
1289 							}
1290 							opc->width += pc->width;
1291 						}
1292 						break;
1293 					case NPTRANPN:   case NPTRA4NPN:
1294 					case NPTRAPNP:   case NPTRA4PNP:
1295 					case NPTRANJFET: case NPTRA4NJFET:
1296 					case NPTRAPJFET: case NPTRA4PJFET:
1297 					case NPTRADMES:  case NPTRA4DMES:
1298 					case NPTRAEMES:  case NPTRA4EMES:
1299 						/* nonFET transistors in parallel sum the area */
1300 						opc->length += pc->length;
1301 						break;
1302 					case NPRESIST:
1303 					case NPINDUCT:
1304 						/* resistance and capacitance in parallel take product over sum */
1305 						if (pc->length + opc->length != 0.0)
1306 							opc->length = (pc->length * opc->length) / (pc->length + opc->length);
1307 						break;
1308 					case NPCAPAC:  case NPECAPAC:
1309 					case NPDIODE:  case NPDIODEZ:
1310 						/* capacitance and diode in parallel sum the farads/area */
1311 						opc->length += pc->length;
1312 						break;
1313 				}
1314 			}
1315 
1316 			pc->flags |= COMPDELETED;
1317 		}
1318 	}
1319 	efree((CHAR *)complist);
1320 
1321 	/* remove deleted components */
1322 	lastpc = NOPCOMP;
1323 	for(pc = *pcomp; pc != NOPCOMP; pc = nextpc)
1324 	{
1325 		nextpc = pc->nextpcomp;
1326 		if ((pc->flags&COMPDELETED) != 0)
1327 		{
1328 			if (lastpc == NOPCOMP) *pcomp = pc->nextpcomp; else
1329 				lastpc->nextpcomp = pc->nextpcomp;
1330 			net_freepcomp(pc);
1331 			(*components)--;
1332 			continue;
1333 		}
1334 		lastpc = pc;
1335 	}
1336 	return(mergecount);
1337 }
1338 
net_sortpcompbyhash(const void * e1,const void * e2)1339 int net_sortpcompbyhash(const void *e1, const void *e2)
1340 {
1341 	REGISTER PCOMP *pc1, *pc2;
1342 
1343 	pc1 = *((PCOMP **)e1);
1344 	pc2 = *((PCOMP **)e2);
1345 	return(pc1->timestamp - pc2->timestamp);
1346 }
1347 
1348 /*
1349  * Routine to add PCOMP "pc" to the list of series transistors.
1350  */
net_addtoserieslist(PCOMP * pc)1351 BOOLEAN net_addtoserieslist(PCOMP *pc)
1352 {
1353 	REGISTER INTBIG newtotal, i;
1354 	REGISTER PCOMP **newlist;
1355 
1356 	if (net_serieslistcount >= net_serieslisttotal)
1357 	{
1358 		newtotal = net_serieslisttotal * 2;
1359 		if (newtotal <= net_serieslistcount)
1360 			newtotal = net_serieslistcount+1;
1361 		newlist = (PCOMP **)emalloc(newtotal * (sizeof (PCOMP *)), net_tool->cluster);
1362 		if (newlist == 0) return(TRUE);
1363 		for(i=0; i<net_serieslistcount; i++)
1364 			newlist[i] = net_serieslist[i];
1365 		if (net_serieslisttotal > 0) efree((CHAR *)net_serieslist);
1366 		net_serieslist = newlist;
1367 		net_serieslisttotal = newtotal;
1368 	}
1369 	net_serieslist[net_serieslistcount] = pc;
1370 	net_serieslistcount++;
1371 	return(FALSE);
1372 }
1373 
1374 /*
1375  * Routine to gather a list of series transistors that include "pc".  Returns
1376  * the list, and its length in "seriescount".  If "seriescount" is less than 2,
1377  * no chain of series transistors has been found.
1378  */
net_gatherseries(PCOMP * pc,INTBIG * seriescount)1379 PCOMP **net_gatherseries(PCOMP *pc, INTBIG *seriescount)
1380 {
1381 	net_serieslistcount = 0;
1382 	net_seriescon1 = net_seriescon2 = -1;
1383 	(void)net_addtoserieslist(pc);
1384 	net_crawlforseries(pc);
1385 	*seriescount = net_serieslistcount;
1386 	return(net_serieslist);
1387 }
1388 
1389 /*
1390  * Recursive helper routine for "net_gatherseries" to find transistors adjacent
1391  * to "pc" and add them to the series chain if they are series transistors.
1392  */
net_crawlforseries(PCOMP * pc)1393 void net_crawlforseries(PCOMP *pc)
1394 {
1395 	REGISTER INTBIG i, j, badend;
1396 	REGISTER PCOMP *opc;
1397 	REGISTER PNET *pn;
1398 
1399 	/* check source and drain connections */
1400 	for(i=1; i<3; i++)
1401 	{
1402 		badend = 0;
1403 		opc = NOPCOMP;
1404 
1405 		/* connection must be to exactly 1 other component */
1406 		pn = pc->netnumbers[i];
1407 		if (pn->nodecount != 2) badend = 1; else
1408 		{
1409 			opc = pn->nodelist[0];
1410 			if (opc == pc) opc = pn->nodelist[1];
1411 		}
1412 
1413 		/* other component must be the same as this */
1414 		if (badend == 0)
1415 		{
1416 			if (opc->function != pc->function) badend = 1;
1417 		}
1418 
1419 		/* both components must be in the same cell */
1420 		if (badend == 0)
1421 		{
1422 			if (pc->hierpathcount != opc->hierpathcount) continue;
1423 			for(j=0; j<pc->hierpathcount; j++)
1424 				if (pc->hierpath[j] != opc->hierpath[j] ||
1425 					pc->hierindex[j] != opc->hierindex[j]) break;
1426 			if (j < pc->hierpathcount) badend = 1;
1427 		}
1428 
1429 		/* other component must point just to this on its source or drain */
1430 		if (badend == 0)
1431 		{
1432 			pn = pc->netnumbers[i];
1433 			if ((pn->flags&(EXPORTEDNET|POWERNET|GROUNDNET)) != 0) badend = 1;
1434 		}
1435 
1436 		/* other component must not already be in the list */
1437 		if (badend == 0)
1438 		{
1439 			for(j=0; j<net_serieslistcount; j++)
1440 				if (net_serieslist[j] == opc) break;
1441 			if (j < net_serieslistcount) badend = 2;
1442 		}
1443 
1444 		switch (badend)
1445 		{
1446 			case 0:		/* good end */
1447 				/* another series transistor found */
1448 				(void)net_addtoserieslist(opc);
1449 
1450 				/* recursively search for others */
1451 				net_crawlforseries(opc);
1452 				break;
1453 			case 1:		/* bad end: end of chain */
1454 				/* add to the end list */
1455 				if (net_seriescon1 < 0)
1456 				{
1457 					net_seriesend1 = pc;
1458 					net_seriescon1 = i;
1459 				} else if (net_seriescon2 < 0)
1460 				{
1461 					net_seriesend2 = pc;
1462 					net_seriescon2 = i;
1463 				}
1464 				break;
1465 		}
1466 	}
1467 }
1468 
1469 /*
1470  * Routine to reduce the network in "pcomp" to merge series transistors into more complex
1471  * single components.
1472  */
net_mergeseries(PCOMP ** pcomp,PNET * pnet,INTBIG * components)1473 INTBIG net_mergeseries(PCOMP **pcomp, PNET *pnet, INTBIG *components)
1474 {
1475 	REGISTER PCOMP *pc, *opc, *nextpc, *lastpc, **serieslist, *newpc, *npc;
1476 	REGISTER INTBIG i, j, k, t, mergecount;
1477 	INTBIG seriescount;
1478 	REGISTER NODEINST *ni;
1479 
1480 	/* clear flags on every component */
1481 	for(pc = *pcomp; pc != NOPCOMP; pc = pc->nextpcomp) pc->timestamp = -1;
1482 	net_fillinnetpointers(*pcomp, pnet);
1483 
1484 	/* scan for series transistors */
1485 	mergecount = 0;
1486 	for(pc = *pcomp; pc != NOPCOMP; pc = nextpc)
1487 	{
1488 		nextpc = pc->nextpcomp;
1489 		if (pc->function != NPTRANMOS && pc->function != NPTRADMOS &&
1490 			pc->function != NPTRAPMOS) continue;
1491 
1492 		/* look for adjacent single transistor */
1493 		serieslist = net_gatherseries(pc, &seriescount);
1494 		if (seriescount <= 1) continue;
1495 
1496 		mergecount++;
1497 
1498 		/* mark transistors */
1499 		for(t=0; t<seriescount; t++) serieslist[t]->timestamp = t;
1500 
1501 		/* create a new component with all the features of the gates and ends */
1502 		newpc = net_allocpcomp();
1503 		if (newpc == NOPCOMP) return(0);
1504 		newpc->nextpcomp = *pcomp;
1505 		*pcomp = newpc;
1506 		newpc->topactual = pc->topactual;
1507 		newpc->hierpathcount = pc->hierpathcount;
1508 		if (newpc->hierpathcount > 0)
1509 		{
1510 			newpc->hierpath = (NODEINST **)emalloc(newpc->hierpathcount *
1511 				(sizeof (NODEINST *)), net_tool->cluster);
1512 			if (newpc->hierpath == 0) return(0);
1513 			newpc->hierindex = (INTBIG *)emalloc(newpc->hierpathcount *
1514 				SIZEOFINTBIG, net_tool->cluster);
1515 			if (newpc->hierindex == 0) return(0);
1516 			for(i=0; i<newpc->hierpathcount; i++)
1517 			{
1518 				newpc->hierpath[i] = pc->hierpath[i];
1519 				newpc->hierindex[i] = pc->hierindex[i];
1520 			}
1521 		}
1522 		newpc->flags = pc->flags;
1523 		newpc->function = (INTSML)(pc->function * 1000 + seriescount);
1524 		newpc->wirecount = (INTSML)(seriescount + 2);
1525 		newpc->timestamp = -1;
1526 		newpc->hashreason = 0;
1527 		newpc->forcedassociation = 0;
1528 		for(t=0; t<seriescount; t++) newpc->forcedassociation += serieslist[t]->forcedassociation;
1529 
1530 		/* length is the sum of all lengths, width is the average width */
1531 		newpc->length = 0.0;
1532 		newpc->width = 0.0;
1533 		for(t=0; t<seriescount; t++)
1534 		{
1535 			newpc->length += serieslist[t]->length;
1536 			newpc->width += serieslist[t]->width;
1537 		}
1538 		newpc->width /= seriescount;
1539 
1540 		/* build "actual" list from all of the individual transistors */
1541 		newpc->numactual = 0;
1542 		for(t=0; t<seriescount; t++) newpc->numactual += serieslist[t]->numactual;
1543 		newpc->actuallist = (NODEINST **)emalloc(newpc->numactual * (sizeof (NODEINST *)),
1544 			net_tool->cluster);
1545 		if (newpc->actuallist == 0) return(0);
1546 		j = 0;
1547 		for(t=0; t<seriescount; t++)
1548 		{
1549 			opc = serieslist[t];
1550 			for(k=0; k < opc->numactual; k++)
1551 			{
1552 				if (opc->numactual == 1) ni = (NODEINST *)opc->actuallist; else
1553 					ni = ((NODEINST **)opc->actuallist)[k];
1554 				((NODEINST **)newpc->actuallist)[j++] = ni;
1555 			}
1556 		}
1557 
1558 		/* allocate the pointer arrays */
1559 		newpc->portlist = (PORTPROTO **)emalloc(((sizeof (PORTPROTO *)) * newpc->wirecount),
1560 			net_tool->cluster);
1561 		if (newpc->portlist == 0) return(0);
1562 		newpc->state = (INTSML *)emalloc((SIZEOFINTSML * newpc->wirecount),
1563 			net_tool->cluster);
1564 		if (newpc->state == 0) return(0);
1565 		newpc->portindices = (INTSML *)emalloc((SIZEOFINTSML * newpc->wirecount),
1566 			net_tool->cluster);
1567 		if (newpc->portindices == 0) return(0);
1568 		newpc->netnumbers = (PNET **)emalloc(((sizeof (PNET *)) * newpc->wirecount),
1569 			net_tool->cluster);
1570 		if (newpc->netnumbers == 0) return(0);
1571 
1572 		/* pick up gates from all series transistors */
1573 		for(t=0; t<seriescount; t++)
1574 		{
1575 			newpc->portindices[t] = (INTSML)getprime(0);
1576 			newpc->portlist[t] = serieslist[t]->portlist[0];
1577 			newpc->netnumbers[t] = serieslist[t]->netnumbers[0];
1578 			newpc->state[t] = serieslist[t]->state[0];
1579 		}
1580 
1581 		/* add in new source and drain */
1582 		newpc->portindices[t] = (INTSML)getprime(1);
1583 		newpc->portlist[t] = net_seriesend1->portlist[net_seriescon1];
1584 		newpc->netnumbers[t] = net_seriesend1->netnumbers[net_seriescon1];
1585 		newpc->state[t] = net_seriesend1->state[net_seriescon1];
1586 		t++;
1587 		newpc->portindices[t] = (INTSML)getprime(1);
1588 		newpc->portlist[t] = net_seriesend2->portlist[net_seriescon2];
1589 		newpc->netnumbers[t] = net_seriesend2->netnumbers[net_seriescon2];
1590 		newpc->state[t] = net_seriesend2->state[net_seriescon2];
1591 		(*components)++;
1592 
1593 		/* fix pointer to the next transistor */
1594 		while (nextpc != NOPCOMP && nextpc->timestamp != -1)
1595 			nextpc = nextpc->nextpcomp;
1596 
1597 		/* remove the series transistors */
1598 		lastpc = NOPCOMP;
1599 		for(opc = *pcomp; opc != NOPCOMP; opc = npc)
1600 		{
1601 			npc = opc->nextpcomp;
1602 			if (opc->timestamp != -1)
1603 			{
1604 				/* remove this */
1605 				if (lastpc == NOPCOMP) *pcomp = npc; else
1606 					lastpc->nextpcomp = npc;
1607 				net_freepcomp(opc);
1608 				(*components)--;
1609 				continue;
1610 			}
1611 			lastpc = opc;
1612 		}
1613 	}
1614 	return(mergecount);
1615 }
1616 
1617 /*
1618  * routine to compare pseudocomponent "p1" and "p2", returning true if they
1619  * are different.
1620  */
net_comparewirelist(PCOMP * p1,PCOMP * p2,BOOLEAN useportnames)1621 BOOLEAN net_comparewirelist(PCOMP *p1, PCOMP *p2, BOOLEAN useportnames)
1622 {
1623 	REGISTER INTBIG i, j;
1624 	Q_UNUSED( useportnames );
1625 
1626 	/* simple test: number of wire lists must be equal */
1627 	if (p1->wirecount != p2->wirecount) return(TRUE);
1628 
1629 	/* if ports must match in sequence, check is simpler */
1630 	if (p1->function == NPTRANPN || p1->function == NPTRAPNP ||
1631 		p1->function == NPDIODE || p1->function == NPDIODEZ ||
1632 		p1->function == NPBUFFER || p1->function == NPFLIPFLOP ||
1633 		p1->function == NPUNKNOWN)
1634 	{
1635 		for(i=0; i<p1->wirecount; i++)
1636 		{
1637 			if (p1->netnumbers[i] != p2->netnumbers[i]) return(TRUE);
1638 		}
1639 		return(FALSE);
1640 	}
1641 
1642 	/* make sure there is memory for flags corresponding to component 2 */
1643 	if (p2->wirecount > net_comparelistsize)
1644 	{
1645 		if (net_comparelistsize != 0) efree((CHAR *)net_comparelist);
1646 		net_comparelist = (BOOLEAN *)emalloc(((sizeof (BOOLEAN)) * p2->wirecount),
1647 			net_tool->cluster);
1648 		net_comparelistsize = p2->wirecount;
1649 	}
1650 
1651 	/* reset flags in list for component 2 */
1652 	for(j=0; j<p2->wirecount; j++) net_comparelist[j] = FALSE;
1653 
1654 	for(i=0; i<p1->wirecount; i++)
1655 	{
1656 		if (p1->netnumbers[i]->nodecount == 0) return(TRUE);
1657 		for(j=0; j<p2->wirecount; j++)
1658 		{
1659 			if (net_comparelist[j]) continue;
1660 			if (p1->portindices[i] != p2->portindices[j]) continue;
1661 			if (p1->netnumbers[i] != p2->netnumbers[j]) continue;
1662 			net_comparelist[j] = TRUE;
1663 			break;
1664 		}
1665 		if (j >= p2->wirecount) return(TRUE);
1666 	}
1667 	return(FALSE);
1668 }
1669 
net_findunits(NODEINST * ni,INTBIG units)1670 float net_findunits(NODEINST *ni, INTBIG units)
1671 {
1672 	REGISTER INTBIG i;
1673 	REGISTER VARIABLE *var;
1674 
1675 	for(i=0; i<ni->numvar; i++)
1676 	{
1677 		var = &ni->firstvar[i];
1678 		if (TDGETUNITS(var->textdescript) == units)
1679 		{
1680 			return((float)eatof(describesimplevariable(var)));
1681 		}
1682 	}
1683 	return(0.0);
1684 }
1685 
net_getpartvalue(NODEINST * ni)1686 float net_getpartvalue(NODEINST *ni)
1687 {
1688 	REGISTER NODEPROTO *np;
1689 	REGISTER VARIABLE *var;
1690 	REGISTER CHAR *pt;
1691 	REGISTER INTBIG fun;
1692 
1693 	np = ni->proto;
1694 	if (np->primindex == 0)
1695 	{
1696 		fun = net_getcellfunction(np);
1697 		if (fun == NPUNKNOWN) return(0.0);
1698 
1699 		/* cell has function override */
1700 		if (fun == NPRESIST)
1701 			return(net_findunits(ni, VTUNITSRES));
1702 		if (fun == NPCAPAC || fun == NPECAPAC)
1703 			return(net_findunits(ni, VTUNITSCAP));
1704 		if (fun == NPINDUCT)
1705 			return(net_findunits(ni, VTUNITSIND));
1706 	}
1707 
1708 	/* diodes have area on them */
1709 	if (np == sch_diodeprim)
1710 	{
1711 		var = getvalkey((INTBIG)ni, VNODEINST, -1, sch_diodekey);
1712 		pt = describesimplevariable(var);
1713 		return((float)eatof(pt));
1714 	}
1715 
1716 	/* capacitors have Farads on them */
1717 	if (np == sch_capacitorprim)
1718 	{
1719 		var = getvalkey((INTBIG)ni, VNODEINST, -1, sch_capacitancekey);
1720 		if (var == NOVARIABLE) return(0.0);
1721 		return((float)eatof(describesimplevariable(var)));
1722 	}
1723 
1724 	/* resistors have Ohms on them */
1725 	if (np == sch_resistorprim)
1726 	{
1727 		var = getvalkey((INTBIG)ni, VNODEINST, -1, sch_resistancekey);
1728 		if (var == NOVARIABLE) return(0.0);
1729 		return((float)eatof(describesimplevariable(var)));
1730 	}
1731 
1732 	/* inductors have Henrys on them */
1733 	if (np == sch_inductorprim)
1734 	{
1735 		var = getvalkey((INTBIG)ni, VNODEINST, -1, sch_inductancekey);
1736 		if (var == NOVARIABLE) return(0.0);
1737 		return((float)eatof(describesimplevariable(var)));
1738 	}
1739 	return(0.0);
1740 }
1741 
net_getcellfunction(NODEPROTO * np)1742 INTBIG net_getcellfunction(NODEPROTO *np)
1743 {
1744 	REGISTER VARIABLE *var;
1745 	REGISTER INTBIG fun;
1746 
1747 	var = getvalkey((INTBIG)np, VNODEPROTO, VSTRING, net_ncc_function_key);
1748 	if (var != NOVARIABLE)
1749 	{
1750 		for(fun=0; fun<MAXNODEFUNCTION; fun++)
1751 			if (namesame((CHAR *)var->addr, nodefunctionname(fun, NONODEINST)) == 0)
1752 				return(fun);
1753 	}
1754 	return(NPUNKNOWN);
1755 }
1756 
1757 /*********************** ALLOCATION ***********************/
1758 
1759 /*
1760  * routine to create a new PNET module for network "net" (if this is a global net,
1761  * merge it with others of the same name).  Increases the net number count in the
1762  * global "net_pseudonode" and adds the module to the list in "pnetlist".
1763  * Return the module (NOPNET on error).
1764  */
net_newpnet(NETWORK * net,PNET ** localpnetlist,PNET ** globalpnetlist)1765 PNET *net_newpnet(NETWORK *net, PNET **localpnetlist, PNET **globalpnetlist)
1766 {
1767 	REGISTER PNET *pn, **pnetlist;
1768 	REGISTER NETWORK *onet;
1769 	REGISTER CHAR *globalname;
1770 	REGISTER UINTBIG characteristics;
1771 	REGISTER BOOLEAN isglobal;
1772 
1773 	/* see if this is a global network */
1774 	if (net != NONETWORK && net->globalnet >= 0 &&
1775 		net->globalnet < net->parent->globalnetcount) isglobal = TRUE; else
1776 			isglobal = FALSE;
1777 
1778 	/* if the net is global, look for an equivalent */
1779 	if (isglobal)
1780 	{
1781 		pnetlist = globalpnetlist;
1782 		if (net->globalnet <= 1)
1783 		{
1784 			/* power and ground: search by index */
1785 			for(pn = *pnetlist; pn != NOPNET; pn = pn->nextpnet)
1786 			{
1787 				onet = pn->network;
1788 				if (onet == NONETWORK) continue;
1789 				if (net->globalnet == onet->globalnet) return(pn);
1790 			}
1791 		} else
1792 		{
1793 			/* not power-and-ground: search by name */
1794 			globalname = net->parent->globalnetnames[net->globalnet];
1795 			for(pn = *pnetlist; pn != NOPNET; pn = pn->nextpnet)
1796 			{
1797 				if ((pn->flags&GLOBALNET) == 0) continue;
1798 				onet = pn->network;
1799 				if (onet == NONETWORK) continue;
1800 				if (onet->globalnet < 0) continue;
1801 				if (onet->globalnet >= onet->parent->globalnetcount) continue;
1802 				if (namesame(onet->parent->globalnetnames[onet->globalnet], globalname) == 0)
1803 					return(pn);
1804 			}
1805 		}
1806 	} else
1807 	{
1808 		pnetlist = localpnetlist;
1809 	}
1810 
1811 	/* global net not found */
1812 	pn = net_allocpnet();
1813 	if (pn == 0) return(NOPNET);
1814 	net_pseudonode++;
1815 	pn->nextpnet = *pnetlist;
1816 	*pnetlist = pn;
1817 	pn->network = net;
1818 	pn->forcedassociation = 0;
1819 	if (isglobal)
1820 	{
1821 		pn->flags |= GLOBALNET;
1822 		characteristics = net->parent->globalnetchar[net->globalnet];
1823 		if (characteristics == PWRPORT) pn->flags |= POWERNET; else
1824 			if (characteristics == GNDPORT) pn->flags |= GROUNDNET;
1825 	}
1826 	return(pn);
1827 }
1828 
1829 /*
1830  * Routine to add portproto "pp" to the list of ports on PNET "pn".
1831  */
net_addexporttopnet(PNET * pn,PORTPROTO * pp)1832 void net_addexporttopnet(PNET *pn, PORTPROTO *pp)
1833 {
1834 	REGISTER PORTPROTO **newportlist, *npp;
1835 	REGISTER INTBIG i;
1836 
1837 	if (pn->realportcount <= 0)
1838 	{
1839 		pn->realportcount = 1;
1840 		pn->realportlist = pp;
1841 		return;
1842 	}
1843 	newportlist = (PORTPROTO **)emalloc((pn->realportcount+1) *
1844 		(sizeof (PORTPROTO *)), net_tool->cluster);
1845 	if (newportlist == 0) return;
1846 	for(i=0; i<pn->realportcount; i++)
1847 	{
1848 		if (pn->realportcount == 1) npp = (PORTPROTO *)pn->realportlist; else
1849 			npp = ((PORTPROTO **)pn->realportlist)[i];
1850 		newportlist[i] = npp;
1851 	}
1852 	newportlist[pn->realportcount] = pp;
1853 	if (pn->realportcount > 1) efree((CHAR *)pn->realportlist);
1854 	pn->realportlist = newportlist;
1855 	pn->realportcount++;
1856 }
1857 
1858 /*
1859  * routine to allocate a new pcomp module from the pool (if any) or memory
1860  */
net_allocpcomp(void)1861 PCOMP *net_allocpcomp(void)
1862 {
1863 	REGISTER PCOMP *pc;
1864 
1865 	if (net_pcompfree == NOPCOMP)
1866 	{
1867 		pc = (PCOMP *)emalloc(sizeof (PCOMP), net_tool->cluster);
1868 		if (pc == 0) return(NOPCOMP);
1869 	} else
1870 	{
1871 		pc = net_pcompfree;
1872 		net_pcompfree = (PCOMP *)pc->nextpcomp;
1873 	}
1874 	pc->flags = 0;
1875 	return(pc);
1876 }
1877 
1878 /*
1879  * routine to return pcomp module "pc" to the pool of free modules
1880  */
net_freepcomp(PCOMP * pc)1881 void net_freepcomp(PCOMP *pc)
1882 {
1883 	if (pc->wirecount != 0)
1884 	{
1885 		efree((CHAR *)pc->portlist);
1886 		efree((CHAR *)pc->state);
1887 		efree((CHAR *)pc->netnumbers);
1888 		efree((CHAR *)pc->portindices);
1889 	}
1890 	if (pc->numactual > 1) efree((CHAR *)pc->actuallist);
1891 	if (pc->hashreason != 0) efree((CHAR *)pc->hashreason);
1892 	if (pc->hierpathcount != 0)
1893 	{
1894 		efree((CHAR *)pc->hierpath);
1895 		efree((CHAR *)pc->hierindex);
1896 	}
1897 
1898 	pc->nextpcomp = net_pcompfree;
1899 	net_pcompfree = pc;
1900 }
1901 
1902 
1903 /*
1904  * routine to allocate a new pnet module from the pool (if any) or memory
1905  */
net_allocpnet(void)1906 PNET *net_allocpnet(void)
1907 {
1908 	REGISTER PNET *pn;
1909 
1910 	if (net_pnetfree == NOPNET)
1911 	{
1912 		pn = (PNET *)emalloc(sizeof (PNET), net_tool->cluster);
1913 		if (pn == 0) return(NOPNET);
1914 		pn->nodetotal = 0;
1915 	} else
1916 	{
1917 		pn = net_pnetfree;
1918 		net_pnetfree = (PNET *)pn->nextpnet;
1919 	}
1920 
1921 	pn->flags = 0;
1922 	pn->nodecount = 0;
1923 	pn->realportcount = 0;
1924 #ifdef PATHTOPNET
1925 	pn->hierpathcount = 0;
1926 #endif
1927 	return(pn);
1928 }
1929 
1930 /*
1931  * routine to return pnet module "pn" to the pool of free modules
1932  */
net_freepnet(PNET * pn)1933 void net_freepnet(PNET *pn)
1934 {
1935 	if (pn->realportcount > 1)
1936 		efree((CHAR *)pn->realportlist);
1937 #ifdef PATHTOPNET
1938 	if (pn->hierpathcount > 0)
1939 	{
1940 		efree((CHAR *)pn->hierpath);
1941 		efree((CHAR *)pn->hierindex);
1942 	}
1943 #endif
1944 
1945 	pn->nextpnet = net_pnetfree;
1946 	net_pnetfree = pn;
1947 }
1948 
1949 /*
1950  * routine to free all allocated structures in the list of pseudonets
1951  * headed by "pnlist"
1952  */
net_freeallpnet(PNET * pnlist)1953 void net_freeallpnet(PNET *pnlist)
1954 {
1955 	REGISTER PNET *pn, *nextpn;
1956 
1957 	for(pn = pnlist; pn != NOPNET; pn = nextpn)
1958 	{
1959 		nextpn = pn->nextpnet;
1960 		net_freepnet(pn);
1961 	}
1962 }
1963 
1964 /*
1965  * routine to free all allocated structures in the list of pseudocomponents
1966  * headed by "pclist"
1967  */
net_freeallpcomp(PCOMP * pclist)1968 void net_freeallpcomp(PCOMP *pclist)
1969 {
1970 	REGISTER PCOMP *pc, *nextpc;
1971 
1972 	for(pc = pclist; pc != NOPCOMP; pc = nextpc)
1973 	{
1974 		nextpc = pc->nextpcomp;
1975 		net_freepcomp(pc);
1976 	}
1977 }
1978 
1979 /*********************** COMPONENT FUNCTION ***********************/
1980 
1981 #define NOTRANMODEL ((TRANMODEL *)-1)
1982 
1983 typedef struct Itranmodel
1984 {
1985 	CHAR *modelname;
1986 	INTBIG tmindex;
1987 	struct Itranmodel *nexttranmodel;
1988 } TRANMODEL;
1989 
1990 static TRANMODEL *net_firsttranmodel = NOTRANMODEL;
1991 
1992 /* must be larger than largest node function entry in "efunction.h" */
1993 static INTBIG net_tranmodelindex = 100;
1994 
1995 /*
1996  * routine to return the function of node "ni"
1997  */
net_getfunction(NODEINST * ni)1998 INTBIG net_getfunction(NODEINST *ni)
1999 {
2000 	REGISTER INTBIG fun;
2001 	REGISTER TRANMODEL *tm;
2002 
2003 	fun = nodefunction(ni);
2004 	switch (fun)
2005 	{
2006 		case NPTRANS:
2007 			fun = NPTRANMOS;
2008 			break;
2009 
2010 		case NPTRANS4:
2011 #ifdef IGNOREFOURPORT
2012 			fun = NPTRANMOS;
2013 #else
2014 			fun = NPTRA4NMOS;
2015 #endif
2016 			break;
2017 
2018 #ifdef IGNOREFOURPORT
2019 		case NPTRA4NMOS:  fun = NPTRANMOS;   break;
2020 		case NPTRA4DMOS:  fun = NPTRADMOS;   break;
2021 		case NPTRA4PMOS:  fun = NPTRAPMOS;   break;
2022 		case NPTRA4NJFET: fun = NPTRANJFET;  break;
2023 		case NPTRA4PJFET: fun = NPTRAPJFET;  break;
2024 		case NPTRA4DMES:  fun = NPTRADMES;   break;
2025 		case NPTRA4EMES:  fun = NPTRAEMES;   break;
2026 		case NPTRA4NPN:   fun = NPTRANPN;    break;
2027 		case NPTRA4PNP:   fun = NPTRAPNP;    break;
2028 #endif
2029 
2030 		case NPTRANSREF:
2031 			/* self-referential transistor: lookup the string in the table */
2032 			for(tm = net_firsttranmodel; tm != NOTRANMODEL; tm = tm->nexttranmodel)
2033 				if (namesame(tm->modelname, ni->proto->protoname) == 0) break;
2034 			if (tm == NOTRANMODEL)
2035 			{
2036 				/* new table entry */
2037 				tm = (TRANMODEL *)emalloc(sizeof (TRANMODEL), net_tool->cluster);
2038 				if (tm == 0) break;
2039 				(void)allocstring(&tm->modelname, ni->proto->protoname, net_tool->cluster);
2040 				tm->tmindex = net_tranmodelindex++;
2041 				tm->nexttranmodel = net_firsttranmodel;
2042 				net_firsttranmodel = tm;
2043 			}
2044 			fun = tm->tmindex;
2045 			break;
2046 
2047 		case NPPIN:
2048 		case NPNODE:
2049 		case NPCONTACT:
2050 		case NPWELL:
2051 		case NPSUBSTRATE:
2052 			fun = NPCONNECT;
2053 			break;
2054 	}
2055 	return(fun);
2056 }
2057 
2058 /*
2059  * Routine to initialize the database for network flattening and comparison.
2060  * Assigns unique function numbers to the "temp1&NETCELLCODE" field of each cell.
2061  */
net_initnetflattening(void)2062 void net_initnetflattening(void)
2063 {
2064 	REGISTER LIBRARY *lib;
2065 	REGISTER NODEPROTO *np, **celllist;
2066 	REGISTER INTBIG i, primeindex, cellcount, cellcode;
2067 	REGISTER PORTPROTO *pp;
2068 	REGISTER TECHNOLOGY *tech;
2069 	REGISTER NETWORK *net;
2070 
2071 	/* assign unique factors to the "temp1&NETCELLCODE" field of each cell, matching across libraries */
2072 	cellcount = 0;
2073 	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
2074 	{
2075 		if ((lib->userbits&HIDDENLIBRARY) != 0) continue;
2076 		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
2077 			cellcount++;
2078 	}
2079 	if (cellcount > 0)
2080 	{
2081 		celllist = (NODEPROTO **)emalloc(cellcount * (sizeof (NODEPROTO *)), net_tool->cluster);
2082 		if (celllist == 0) return;
2083 		cellcount = 0;
2084 		for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
2085 		{
2086 			if ((lib->userbits&HIDDENLIBRARY) != 0) continue;
2087 			for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
2088 				celllist[cellcount++] = np;
2089 		}
2090 		esort(celllist, cellcount, sizeof (NODEPROTO *), net_cellnameascending);
2091 		primeindex = 1;
2092 		for(i=0; i<cellcount; i++)
2093 		{
2094 			np = celllist[i];
2095 			cellcode = getprime(primeindex++);
2096 			np->temp1 = (np->temp1 & ~NETCELLCODE) | (cellcode << NETCELLCODESH);
2097 			while (i+1 < cellcount && namesame(np->protoname, celllist[i+1]->protoname) == 0)
2098 			{
2099 				i++;
2100 				np = celllist[i];
2101 				np->temp1 = (np->temp1 & ~NETCELLCODE) | (cellcode << NETCELLCODESH);
2102 			}
2103 		}
2104 		efree((CHAR *)celllist);
2105 	}
2106 
2107 	/* clear prime number indices in the cells */
2108 	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
2109 		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
2110 			np->temp2 = 0;
2111 
2112 	/* clear network topology information */
2113 	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
2114 	{
2115 		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
2116 		{
2117 			for(net = np->firstnetwork; net != NONETWORK; net = net->nextnetwork)
2118 				net->temp2 = 0;
2119 		}
2120 	}
2121 
2122 	/* assign network topology based on export names */
2123 	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
2124 	{
2125 		/* assign topology information uniformly in each cell */
2126 		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
2127 		{
2128 			/* first pass: ignore icon and skeleton views */
2129 			if (np->cellview == el_iconview || np->cellview == el_skeletonview) continue;
2130 			for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
2131 			{
2132 				if (pp->network->temp2 != 0) continue;
2133 				net_setthisexporttopology(pp, &np->temp2);
2134 			}
2135 			for(net = np->firstnetwork; net != NONETWORK; net = net->nextnetwork)
2136 			{
2137 				if (net->temp2 == 0) net->temp2 = getprime(np->temp2++);
2138 			}
2139 		}
2140 		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
2141 		{
2142 			/* second pass: do icon and skeleton views */
2143 			if (np->cellview != el_iconview && np->cellview != el_skeletonview) continue;
2144 			for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
2145 			{
2146 				if (pp->network->temp2 != 0) continue;
2147 				net_setthisexporttopology(pp, &np->temp2);
2148 			}
2149 			for(net = np->firstnetwork; net != NONETWORK; net = net->nextnetwork)
2150 			{
2151 				if (net->temp2 == 0) net->temp2 = getprime(np->temp2++);
2152 			}
2153 		}
2154 	}
2155 	for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
2156 	{
2157 		for(np = tech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
2158 		{
2159 			switch ((np->userbits&NFUNCTION) >> NFUNCTIONSH)
2160 			{
2161 				case NPTRANMOS:  case NPTRADMOS: case NPTRAPMOS:
2162 				case NPTRANJFET: case NPTRAPJFET:
2163 				case NPTRADMES:  case NPTRAEMES:
2164 					pp = np->firstportproto;   pp->network->temp2 = getprime(0);	/* poly */
2165 					pp = pp->nextportproto;    pp->network->temp2 = getprime(1);	/* active */
2166 					pp = pp->nextportproto;    pp->network->temp2 = getprime(0);	/* poly */
2167 					pp = pp->nextportproto;    pp->network->temp2 = getprime(1);	/* active */
2168 					break;
2169 				case NPTRANS:
2170 					pp = np->firstportproto;   pp->network->temp2 = getprime(0);	/* poly */
2171 					pp = pp->nextportproto;    pp->network->temp2 = getprime(1);	/* active */
2172 					pp = pp->nextportproto;    pp->network->temp2 = getprime(1);	/* active */
2173 					break;
2174 				case NPTRANS4:
2175 					pp = np->firstportproto;   pp->network->temp2 = getprime(0);	/* poly */
2176 					pp = pp->nextportproto;    pp->network->temp2 = getprime(1);	/* active */
2177 					pp = pp->nextportproto;    pp->network->temp2 = getprime(1);	/* active */
2178 					pp = pp->nextportproto;    pp->network->temp2 = getprime(2);	/* bias */
2179 					break;
2180 				case NPTRANPN:   case NPTRAPNP:
2181 					pp = np->firstportproto;   pp->network->temp2 = getprime(0);	/* collector */
2182 					pp = pp->nextportproto;    pp->network->temp2 = getprime(1);	/* emitter */
2183 					pp = pp->nextportproto;    pp->network->temp2 = getprime(2);	/* base */
2184 					break;
2185 				case NPRESIST:
2186 				case NPCAPAC:   case NPECAPAC:
2187 				case NPINDUCT:
2188 					pp = np->firstportproto;   pp->network->temp2 = getprime(0);	/* side 1 */
2189 					pp = pp->nextportproto;
2190 					if (pp != NOPORTPROTO)     pp->network->temp2 = getprime(0);	/* side 2 */
2191 					break;
2192 				default:
2193 					i = 0;
2194 					for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
2195 					{
2196 						pp->network->temp2 = getprime(i);
2197 						i++;
2198 					}
2199 					break;
2200 			}
2201 		}
2202 	}
2203 }
2204 
2205 /*
2206  * Helper routine for "esort" that makes cells go in ascending name order.
2207  */
net_cellnameascending(const void * e1,const void * e2)2208 int net_cellnameascending(const void *e1, const void *e2)
2209 {
2210 	REGISTER NODEPROTO *c1, *c2;
2211 
2212 	c1 = *((NODEPROTO **)e1);
2213 	c2 = *((NODEPROTO **)e2);
2214 	return(namesame(c1->protoname, c2->protoname));
2215 }
2216 
net_setthisexporttopology(PORTPROTO * pp,INTBIG * index)2217 void net_setthisexporttopology(PORTPROTO *pp, INTBIG *index)
2218 {
2219 	REGISTER NETWORK *net, *subnet;
2220 	REGISTER PORTPROTO *opp;
2221 	REGISTER NODEPROTO *np;
2222 	REGISTER INTBIG i;
2223 	REGISTER UINTBIG bits;
2224 	REGISTER CHAR *name;
2225 
2226 	/* assign the unique prime numbers to the export's network */
2227 	net = pp->network;
2228 	np = pp->parent;
2229 	if (net->temp2 == 0)
2230 	{
2231 		net->temp2 = net_findotherexporttopology(np, pp->protoname);
2232 		if (net->temp2 == 0)
2233 		{
2234 			/* this name not found: see if another name in this cell is connected */
2235 			for(opp = np->firstportproto; opp != NOPORTPROTO; opp = opp->nextportproto)
2236 			{
2237 				if (opp == pp) continue;
2238 				if (opp->network != pp->network) continue;
2239 				net->temp2 = net_findotherexporttopology(np, opp->protoname);
2240 				if (net->temp2 != 0) break;
2241 			}
2242 
2243 			/* if no name found, see if power/ground association can be made */
2244 			if (net->temp2 == 0)
2245 			{
2246 				bits = pp->userbits & STATEBITS;
2247 				if (bits == PWRPORT || bits == GNDPORT)
2248 					net->temp2 = net_findotherexportcharacteristic(np, bits);
2249 			}
2250 			if (net->temp2 == 0)
2251 				net->temp2 = getprime((*index)++);
2252 		}
2253 	}
2254 	if (net->buswidth > 1)
2255 	{
2256 		for(i=0; i<net->buswidth; i++)
2257 		{
2258 			subnet = net->networklist[i];
2259 			if (subnet->temp2 == 0)
2260 			{
2261 				if (subnet->namecount > 0) name = networkname(subnet, 0); else
2262 					name = pp->protoname;
2263 				subnet->temp2 = net_findotherexporttopology(np, name);
2264 				if (subnet->temp2 == 0) subnet->temp2 = getprime((*index)++);
2265 			}
2266 		}
2267 	}
2268 }
2269 
2270 /*
2271  * Helper routine to look at all cells associated with "parent" (i.e. in the same cell
2272  * but with a different view) and find an export with the name "exportname".  Returns
2273  * the "temp2" field (unique ID) of that network.  Returns zero if no association can be found.
2274  */
net_findotherexporttopology(NODEPROTO * parent,CHAR * exportname)2275 INTBIG net_findotherexporttopology(NODEPROTO *parent, CHAR *exportname)
2276 {
2277 	REGISTER NODEPROTO *np;
2278 	REGISTER PORTPROTO *opp;
2279 	REGISTER NETWORK *net;
2280 	REGISTER LIBRARY *lib;
2281 
2282 	/* first look for an equivalent export in another cell */
2283 	FOR_CELLGROUP(np, parent)
2284 	{
2285 		if (np == parent) continue;
2286 
2287 		/* cell from the same cell: look for an equivalent port */
2288 		opp = getportproto(np, exportname);
2289 		if (opp != NOPORTPROTO && opp->network->temp2 != 0) return(opp->network->temp2);
2290 	}
2291 
2292 	/* next look for an equivalent network name in another cell */
2293 	FOR_CELLGROUP(np, parent)
2294 	{
2295 		if (np == parent) continue;
2296 
2297 		/* cell from the same cell: look for an equivalent network name */
2298 		net = getnetwork(exportname, np);
2299 		if (net == NONETWORK) continue;
2300 		if (net->temp2 != 0) return(net->temp2);
2301 	}
2302 
2303 	/* now look for an equivalent in another library */
2304 	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
2305 	{
2306 		if (lib == parent->lib) continue;
2307 		if ((lib->userbits&HIDDENLIBRARY) != 0) continue;
2308 		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
2309 		{
2310 			if ((np->temp1&NETCELLCODE) != (parent->temp1&NETCELLCODE)) continue;
2311 
2312 			/* cell from the same cell in another library: look for an equivalent export or network name */
2313 			opp = getportproto(np, exportname);
2314 			if (opp != NOPORTPROTO && opp->network->temp2 != 0) return(opp->network->temp2);
2315 			net = getnetwork(exportname, np);
2316 			if (net == NONETWORK) continue;
2317 			if (net->temp2 != 0) return(net->temp2);
2318 		}
2319 	}
2320 	return(0);
2321 }
2322 
2323 /*
2324  * Helper routine to look at all cells associated with "parent" (i.e. in the same cell
2325  * but with a different view) and find a network with the characteristics "bits".  Returns
2326  * the "temp2" field (unique ID) of that network.  Returns zero if no association can be found.
2327  */
net_findotherexportcharacteristic(NODEPROTO * parent,INTBIG bits)2328 INTBIG net_findotherexportcharacteristic(NODEPROTO *parent, INTBIG bits)
2329 {
2330 	REGISTER NODEPROTO *np;
2331 	REGISTER PORTPROTO *opp;
2332 	REGISTER NETWORK *net;
2333 	REGISTER INTBIG obits;
2334 
2335 	/* first look for an equivalent export in another cell */
2336 	FOR_CELLGROUP(np, parent)
2337 	{
2338 		if (np == parent) continue;
2339 
2340 		/* cell from the same cell: look for an equivalent port */
2341 		for(opp = np->firstportproto; opp != NOPORTPROTO; opp = opp->nextportproto)
2342 		{
2343 			obits = opp->userbits & STATEBITS;
2344 			if (bits != obits) continue;
2345 			if (opp->network->temp2 != 0) return(opp->network->temp2);
2346 		}
2347 	}
2348 
2349 	/* next look for an equivalent network name in another cell */
2350 	FOR_CELLGROUP(np, parent)
2351 	{
2352 		if (np == parent) continue;
2353 
2354 		/* cell from the same cell: look for an equivalent network name */
2355 		for(net = np->firstnetwork; net != NONETWORK; net = net->nextnetwork)
2356 		{
2357 			if (net->globalnet < 0) continue;
2358 			if (net->globalnet >= np->globalnetcount) continue;
2359 			if (np->globalnetchar[net->globalnet] != bits) continue;
2360 			if (net->temp2 != 0) return(net->temp2);
2361 		}
2362 	}
2363 	return(0);
2364 }
2365