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