1 /* -*- tab-width: 4 -*-
2  *
3  * Electric(tm) VLSI Design System
4  *
5  * File: netdiff.cpp
6  * Network tool: module for network comparison
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  * This module is inspired by the work of Carl Ebeling:
32  *   Ebeling, Carl, "GeminiII: A Second Generation Layout Validation Program",
33  *   Proceedings of ICCAD 1988, p322-325.
34  */
35 
36 #include "global.h"
37 #include "network.h"
38 #include "efunction.h"
39 #include "egraphics.h"
40 #include "edialogs.h"
41 #include "tecschem.h"
42 #include "usr.h"
43 #include <math.h>
44 
45 #define NEWNCC	1		/* comment out to remove last-minute changes */
46 #define STATIC
47 
48 #define MAXITERATIONS 10
49 #define SYMGROUPCOMP   0
50 #define SYMGROUPNET    1
51 
52 /* the meaning of errors returned by "net_analyzesymmetrygroups()" */
53 #define SIZEERRORS       1
54 #define EXPORTERRORS     2
55 #define STRUCTUREERRORS  4
56 
57 static GRAPHICS net_cleardesc = {LAYERH, ALLOFF, SOLIDC, SOLIDC,
58 	{0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,
59 	0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF}, NOVARIABLE, 0};
60 static GRAPHICS net_msgdesc = {LAYERH, HIGHLIT, SOLIDC, SOLIDC,
61 	{0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,
62 	0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF}, NOVARIABLE, 0};
63 
64 #define NOSYMGROUP ((SYMGROUP *)-1)
65 
66 /* the meaning of "SYMGROUP->groupflags" */
67 #define GROUPACTIVENOW		1					/* set if this group is active now */
68 #define GROUPFRESH	        2					/* set if this group is "fresh" (just split) */
69 #define GROUPPROMISING		4					/* set if this group is "promising" (refed from split group) */
70 
71 typedef struct Isymgroup
72 {
73 	HASHTYPE          hashvalue;			/* hash value of this symmetry group */
74 	INTBIG            grouptype;			/* SYMGROUPCOMP (components) or SYMGROUPNET (nets) */
75 	INTBIG            groupflags;			/* state of this group */
76 	INTBIG            groupindex;			/* ordinal value of group */
77 	INTBIG            checksum;				/* additional value for group to ensure hash codes don't clash */
78 	INTBIG            cellcount[2];			/* number of objects from cells */
79 	INTBIG            celltotal[2];			/* size of object list from cells */
80 	void            **celllist[2];			/* list of objects from cells */
81 	struct Isymgroup *nextsymgroup;			/* next in list */
82 	struct Isymgroup *nexterrsymgroup;		/* next in list of errors */
83 } SYMGROUP;
84 
85 static SYMGROUP    *net_firstsymgroup = NOSYMGROUP;
86 static SYMGROUP    *net_firstmatchedsymgroup = NOSYMGROUP;
87 static SYMGROUP    *net_symgroupfree = NOSYMGROUP;
88 
89 static SYMGROUP   **net_symgrouphashcomp;			/* hash table for components */
90 static SYMGROUP   **net_symgrouphashnet;			/* hash table for nets */
91 static INTBIG      *net_symgrouphashckcomp;			/* hash table checksums for components */
92 static INTBIG      *net_symgrouphashcknet;			/* hash table checksums for nets */
93 static INTBIG       net_symgrouphashcompsize = 0;	/* size of component hash table */
94 static INTBIG       net_symgrouphashnetsize = 0;	/* size of net hash table */
95 static INTBIG       net_symgrouplisttotal = 0;
96 static INTBIG       net_symgroupnumber;
97 static SYMGROUP   **net_symgrouplist;
98 static INTBIG       net_nodeCountMultiplier = 0;
99 static INTBIG       net_portFactorMultiplier;
100 static INTBIG       net_portNetFactorMultiplier;
101 static INTBIG       net_portHashFactorMultiplier;
102 static INTBIG       net_functionMultiplier;
103 static PNET        *net_nodelist1 = NOPNET, *net_nodelist2 = NOPNET;
104 static INTBIG       net_ncc_tolerance;				/* component value tolerance (%) */
105 static INTBIG       net_ncc_tolerance_amt;			/* component value tolerance (amt) */
106 static HASHTYPE     net_uniquehashvalue;
107 static BOOLEAN      net_nethashclashtold;
108 static BOOLEAN      net_comphashclashtold;
109 
110 static INTBIG       net_debuggeminipasscount;		/* number of passes through gemini renumbering */
111 static INTBIG       net_debuggeminiexpandglobal;	/* number of times entire set of groups considered */
112 static INTBIG       net_debuggeminiexpandglobalworked;	/* number of times entire group considereation worked */
113 static INTBIG       net_debuggeminigroupsrenumbered;/* number of symmetry groups renumbered */
114 static INTBIG       net_debuggeminigroupssplit;		/* number of symmetry groups split */
115 static INTBIG       net_debuggeminitotalgroups;		/* total number of groups examined */
116 static INTBIG       net_debuggeminitotalgroupsact;	/* number of active groups examined */
117 
118 /* structures for name matching */
119 typedef struct
120 {
121 	CHAR     *name;
122 	INTBIG    number;
123 	NODEINST *original;
124 } NAMEMATCH;
125 
126 static NAMEMATCH   *net_namematch[2];
127 static INTBIG       net_namematchtotal[2] = {0, 0};
128 static INTBIG      *net_compmatch0list;
129 static INTBIG      *net_compmatch1list;
130 static INTBIG       net_compmatch0total = 0;
131 static INTBIG       net_compmatch1total = 0;
132 
133 static INTBIG       net_foundsymgrouptotal = 0;
134 static INTBIG       net_foundsymgroupcount;
135 static SYMGROUP   **net_foundsymgroups;
136 
137 /* structures for size matching */
138 typedef struct
139 {
140 	float length, width;
141 } NODESIZE;
142 
143 static INTBIG    net_sizearraytotal[2] = {0, 0};
144 static NODESIZE *net_sizearray[2];
145 
146 /* used by "netanalyze.c" */
147 #if defined(__cplusplus) && !defined(ALLCPLUSPLUS)
148 extern "C" {
149 #endif
150        PCOMP       *net_pcomp1 = NOPCOMP, *net_pcomp2 = NOPCOMP;
151        INTBIG       net_timestamp;
152        NODEPROTO   *net_cell[2];
153        INTBIG       net_ncc_options;				/* options to use in NCC */
154 extern GRAPHICS     us_hbox;
155 #if defined(__cplusplus) && !defined(ALLCPLUSPLUS)
156 }
157 #endif
158 
159 /* prototypes for local routines */
160 STATIC void       net_addcomptoerror(void *err, PCOMP *pc);
161 STATIC void       net_addnettoerror(void *err, PNET *pn);
162 STATIC void       net_addsymgrouptoerror(void *err, SYMGROUP *sg);
163 STATIC void       net_addtonamematch(NAMEMATCH **match, INTBIG *total, INTBIG *count, CHAR *name,
164 					INTBIG number, NODEINST *orig);
165 STATIC BOOLEAN    net_addtosymgroup(SYMGROUP *sg, INTBIG cellno, void *obj);
166 STATIC INTBIG     net_analyzesymmetrygroups(BOOLEAN reporterrors, BOOLEAN checksize,
167 					BOOLEAN checkexportnames, BOOLEAN ignorepwrgnd, INTBIG *errorcount);
168 STATIC INTBIG     net_assignnewgrouphashvalues(SYMGROUP *sg, INTBIG verbose);
169 STATIC INTBIG     net_assignnewhashvalues(INTBIG grouptype);
170 STATIC CHAR      *net_describesizefactor(float sizew, float sizel);
171 STATIC INTBIG     net_dogemini(PCOMP *pcomp1, PNET *nodelist1, PCOMP *pcomp2, PNET *nodelist2,
172 					BOOLEAN checksize, BOOLEAN checkexportnames, BOOLEAN ignorepwrgnd);
173 STATIC INTBIG     net_findamatch(INTBIG verbose, BOOLEAN ignorepwrgnd);
174 STATIC BOOLEAN    net_findcommonsizefactor(SYMGROUP *sg, float *sizew, float *sizel);
175 STATIC BOOLEAN    net_findcomponentnamematch(SYMGROUP *sg, BOOLEAN usenccmatches,
176 					INTBIG verbose, BOOLEAN ignorepwrgnd, INTBIG total, INTBIG unmatchednets, INTBIG unmatchedcomps);
177 STATIC BOOLEAN    net_findexportnamematch(SYMGROUP *sg, INTBIG verbose, BOOLEAN ignorepwrgnd,
178 					INTBIG total, INTBIG unmatchednets, INTBIG unmatchedcomps);
179 STATIC SYMGROUP **net_findgeomsymmetrygroup(GEOM *obj);
180 STATIC SYMGROUP **net_findnetsymmetrygroup(NETWORK *net);
181 STATIC BOOLEAN    net_findnetworknamematch(SYMGROUP *sg, BOOLEAN usenccmatches,
182 					INTBIG verbose, BOOLEAN ignorepwrgnd, INTBIG total, INTBIG unmatchednets, INTBIG unmatchedcomps);
183 STATIC BOOLEAN    net_findpowerandgroundmatch(SYMGROUP *sg, INTBIG verbose, INTBIG total, INTBIG unmatchednets, INTBIG unmatchedcomps);
184 STATIC SYMGROUP  *net_findsymmetrygroup(INTBIG grouptype, HASHTYPE hashvalue, INTBIG checksum);
185 STATIC void       net_forceamatch(SYMGROUP *sg, INTBIG c1, INTBIG *i1, INTBIG c2, INTBIG *i2,
186 					float sizew, float sizel, INTBIG verbose, BOOLEAN ignorepwrgnd);
187 STATIC void       net_freesymgroup(SYMGROUP *sg);
188 STATIC void       net_initializeverbose(PCOMP *pcomplist, PNET *pnetlist);
189 STATIC BOOLEAN    net_insertinhashtable(SYMGROUP *sg);
190 STATIC BOOLEAN    net_isspice(PCOMP *pc);
191 STATIC INTBIG     net_ncconelevel(NODEPROTO *cell1, NODEPROTO *cell2, BOOLEAN preanalyze,
192 					BOOLEAN interactive);
193 STATIC SYMGROUP  *net_newsymgroup(INTBIG type, HASHTYPE hashvalue, INTBIG checksum);
194 STATIC void       net_preserveresults(NODEPROTO *np1, NODEPROTO *np2);
195 STATIC UINTBIG    net_recursiverevisiondate(NODEPROTO *cell);
196 STATIC void       net_rebuildhashtable(void);
197 STATIC void       net_redeemzerogroups(SYMGROUP *sgnewc, SYMGROUP *sgnewn, INTBIG verbose, BOOLEAN ignorepwrgnd);
198 STATIC void       net_removefromsymgroup(SYMGROUP *sg, INTBIG f, INTBIG index);
199 STATIC INTBIG     net_reporterror(SYMGROUP *sg, CHAR *errmsg, BOOLEAN ignorepwrgnd);
200 STATIC void       net_reportsizeerror(PCOMP *pc1, CHAR *size1, PCOMP *pc2, CHAR *size2, INTBIG pctdiff, SYMGROUP *sg);
201 STATIC BOOLEAN    net_sameexportnames(PNET *pn1, PNET *pn2);
202 STATIC void       net_showpreanalysis(NODEPROTO *cell1, PCOMP *pcomp1, PNET *nodelist1,
203 					NODEPROTO *cell2, PCOMP *pcomp2, PNET *nodelist2, BOOLEAN ignorepwrgnd);
204 STATIC void       net_showsymmetrygroups(INTBIG verbose, INTBIG type);
205 STATIC HASHTYPE   net_uniquesymmetrygrouphash(INTBIG grouptype);
206 STATIC void       net_unmatchedstatus(INTBIG *unmatchednets, INTBIG *unmatchedcomps, INTBIG *symgroupcount);
207 STATIC void       net_showmatchedgroup(SYMGROUP *sg);
208 STATIC void       net_addtofoundsymgroups(SYMGROUP *sg);
209 STATIC int        net_sortbycelltype(const void *n1, const void *n2);
210 STATIC int        net_sortbypnet(const void *n1, const void *n2);
211 STATIC int        net_sortexportcodes(const void *e1, const void *e2);
212 STATIC void       net_checkcomponenttypes(void *errorsa, BOOLEAN ignorepwrgnd, PCOMP *pcomp1, PCOMP *pcomp2,
213 					PNET *pnetlist1, PNET *pnetlist2, NODEPROTO *cell1, NODEPROTO *cell2);
214 STATIC NETWORK  **net_makeexternalnetlist(NODEINST *ni, INTBIG *size, BOOLEAN ignorepwrgnd);
215 STATIC void       net_randomizehashcodes(INTBIG verbose);
216 STATIC void       net_randomizesymgroup(SYMGROUP *sg, INTBIG verbose, INTBIG factor);
217 STATIC void       net_cleanupsymmetrygroups(void);
218 STATIC void       net_foundmismatch(NODEPROTO *cell, NETWORK *net, NODEPROTO *cellwithout, PCOMP **celllist,
219 					INTBIG start, INTBIG end, PNET *pnetlist, BOOLEAN ignorepwrgnd, void *errorsa);
220 #if defined(__cplusplus) && !defined(ALLCPLUSPLUS)
221 extern "C"
222 {
223 #endif
224 	STATIC int    net_sortnamematches(const void *e1, const void *e2);
225 	STATIC int    net_sortpcomp(const void *e1, const void *e2);
226 	STATIC int    net_sortpnet(const void *e1, const void *e2);
227 	STATIC int    net_sortsizearray(const void *e1, const void *e2);
228 	STATIC int    net_sortsymgroups(const void *e1, const void *e2);
229 #if defined(__cplusplus) && !defined(ALLCPLUSPLUS)
230 }
231 #endif
232 
233 /*
234  * Routine to free all memory associated with this module.
235  */
net_freediffmemory(void)236 void net_freediffmemory(void)
237 {
238 	REGISTER SYMGROUP *sg;
239 	REGISTER INTBIG f;
240 
241 	net_removeassociations();
242 	while (net_firstsymgroup != NOSYMGROUP)
243 	{
244 		sg = net_firstsymgroup;
245 		net_firstsymgroup = sg->nextsymgroup;
246 		net_freesymgroup(sg);
247 	}
248 	while (net_firstmatchedsymgroup != NOSYMGROUP)
249 	{
250 		sg = net_firstmatchedsymgroup;
251 		net_firstmatchedsymgroup = sg->nextsymgroup;
252 		net_freesymgroup(sg);
253 	}
254 	if (net_symgrouphashcompsize != 0)
255 	{
256 		efree((CHAR *)net_symgrouphashcomp);
257 		efree((CHAR *)net_symgrouphashckcomp);
258 	}
259 	if (net_symgrouphashnetsize != 0)
260 	{
261 		efree((CHAR *)net_symgrouphashnet);
262 		efree((CHAR *)net_symgrouphashcknet);
263 	}
264 	while (net_symgroupfree != NOSYMGROUP)
265 	{
266 		sg = net_symgroupfree;
267 		net_symgroupfree = sg->nextsymgroup;
268 		for(f=0; f<2; f++)
269 			if (sg->celltotal[f] > 0) efree((CHAR *)sg->celllist[f]);
270 		efree((CHAR *)sg);
271 	}
272 	if (net_symgrouplisttotal > 0) efree((CHAR *)net_symgrouplist);
273 	if (net_foundsymgrouptotal > 0) efree((CHAR *)net_foundsymgroups);
274 
275 	for(f=0; f<2; f++)
276 	{
277 		if (net_namematchtotal[f] > 0) efree((CHAR *)net_namematch[f]);
278 		if (net_sizearraytotal[f] > 0) efree((CHAR *)net_sizearray[f]);
279 	}
280 	if (net_compmatch0total > 0) efree((CHAR *)net_compmatch0list);
281 	if (net_compmatch1total > 0) efree((CHAR *)net_compmatch1list);
282 #ifdef FORCESUNTOOLS
283 	net_freeexpdiffmemory();
284 #endif
285 }
286 
net_removeassociations(void)287 void net_removeassociations(void)
288 {
289 	if (net_pcomp1 != NOPCOMP)
290 	{
291 		net_freeallpcomp(net_pcomp1);
292 		net_pcomp1 = NOPCOMP;
293 	}
294 	if (net_pcomp2 != NOPCOMP)
295 	{
296 		net_freeallpcomp(net_pcomp2);
297 		net_pcomp2 = NOPCOMP;
298 	}
299 	if (net_nodelist1 != NOPNET)
300 	{
301 		net_freeallpnet(net_nodelist1);
302 		net_nodelist1 = NOPNET;
303 	}
304 	if (net_nodelist2 != NOPNET)
305 	{
306 		net_freeallpnet(net_nodelist2);
307 		net_nodelist2 = NOPNET;
308 	}
309 }
310 
311 /******************************** EQUATING COMPARED OBJECTS ********************************/
312 
313 /*
314  * routine to identify the equivalent object associated with the currently
315  * highlighted one (comparison must have been done).  If "noise" is true,
316  * report errors.  Returns false if an equate was shown.
317  */
net_equate(BOOLEAN noise)318 BOOLEAN net_equate(BOOLEAN noise)
319 {
320 	REGISTER NODEPROTO *np;
321 	REGISTER PORTPROTO *pp;
322 	REGISTER ARCINST *ai;
323 	REGISTER NODEINST *ni;
324 	REGISTER NETWORK *net, *anet;
325 	REGISTER PCOMP *pc;
326 	REGISTER PNET *pn;
327 	REGISTER GEOM *obj;
328 	REGISTER SYMGROUP *sg, **sglist;
329 	REGISTER INTBIG i, j, f, k, fun;
330 	REGISTER BOOLEAN first;
331 	REGISTER void *infstr;
332 
333 	/* make sure an association has been done */
334 #ifdef NEWNCC
335 	if (net_pcomp1 == NOPCOMP && net_pcomp2 == NOPCOMP && net_nodelist1 == NOPNET && net_nodelist2 == NOPNET)
336 #else
337 	if (net_pcomp1 == NOPCOMP || net_pcomp2 == NOPCOMP)
338 #endif
339 	{
340 		if (noise) ttyputerr(_("First associate with '-telltool network compare'"));
341 		return(TRUE);
342 	}
343 
344 	/* get the highlighted object */
345 	obj = (GEOM *)asktool(us_tool, x_("get-object"));
346 	if (obj == NOGEOM)
347 	{
348 		if (noise) ttyputerr(_("Must select something to be equated"));
349 		return(TRUE);
350 	}
351 
352 	/* make sure this object is in one of the associated cells */
353 	np = geomparent(obj);
354 	if (np != net_cell[0] && np != net_cell[1])
355 	{
356 		if (!isachildof(np, net_cell[0]) && !isachildof(np, net_cell[1]))
357 		{
358 			if (noise)
359 				ttyputerr(_("This object is not in one of the two associated cells"));
360 			return(TRUE);
361 		}
362 	}
363 
364 	/* highlight the associated object */
365 	sglist = net_findgeomsymmetrygroup(obj);
366 	if (sglist[0] == NOSYMGROUP)
367 	{
368 #ifdef FORCESUNTOOLS
369 		return(net_equateexp(noise));
370 #endif
371 		ttyputmsg(_("This object is not associated with anything else"));
372 		return(TRUE);
373 	}
374 	if (sglist[1] == NOSYMGROUP && sglist[0]->hashvalue == 0)
375 	{
376 		ttyputmsg(_("This object was not matched successfully"));
377 		return(TRUE);
378 	}
379 
380 	(void)asktool(us_tool, x_("clear"));
381 	infstr = initinfstr();
382 	first = FALSE;
383 	for(k=0; sglist[k] != NOSYMGROUP; k++)
384 	{
385 		sg = sglist[k];
386 		switch (sg->grouptype)
387 		{
388 			case SYMGROUPCOMP:
389 				for(f=0; f<2; f++)
390 				{
391 					for(i=0; i<sg->cellcount[f]; i++)
392 					{
393 						pc = (PCOMP *)sg->celllist[f][i];
394 						for(j=0; j<pc->numactual; j++)
395 						{
396 							if (pc->numactual == 1) ni = (NODEINST *)pc->actuallist; else
397 								ni = ((NODEINST **)pc->actuallist)[j];
398 							if (first) addtoinfstr(infstr, '\n');
399 							first = TRUE;
400 							formatinfstr(infstr, x_("CELL=%s FROM=0%lo;-1;0"),
401 								describenodeproto(geomparent(ni->geom)), (INTBIG)ni->geom);
402 						}
403 					}
404 				}
405 				break;
406 			case SYMGROUPNET:
407 				for(f=0; f<2; f++)
408 				{
409 					for(i=0; i<sg->cellcount[f]; i++)
410 					{
411 						pn = (PNET *)sg->celllist[f][i];
412 						net = pn->network;
413 						if (net == NONETWORK) continue;
414 						np = net->parent;
415 						for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
416 						{
417 							anet = ai->network;
418 							if (ai->proto == sch_busarc)
419 							{
420 								if (anet->buswidth > 1)
421 								{
422 #ifdef NEWNCC
423 									for(j=0; j<anet->buswidth; j++)
424 										if (anet->networklist[j] == net) break;
425 									if (j >= anet->buswidth) continue;
426 #else
427 									for(i=0; i<anet->buswidth; i++)
428 										if (anet->networklist[i] == net) break;
429 									if (i >= anet->buswidth) continue;
430 #endif
431 								} else
432 								{
433 									if (anet != net) continue;
434 								}
435 							} else
436 							{
437 								if (anet != net) continue;
438 							}
439 							if (first) addtoinfstr(infstr, '\n');
440 							first = TRUE;
441 							formatinfstr(infstr, x_("CELL=%s FROM=0%lo;-1;0"),
442 								describenodeproto(np), (INTBIG)ai->geom);
443 						}
444 						for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
445 						{
446 							if (ni->proto->primindex == 0) continue;
447 							fun = nodefunction(ni);
448 							if (fun != NPPIN && fun != NPCONTACT && fun != NPNODE && fun != NPCONNECT)
449 								continue;
450 							if (ni->firstportarcinst == NOPORTARCINST) continue;
451 							if (ni->firstportarcinst->conarcinst->network != net) continue;
452 							if (first) addtoinfstr(infstr, '\n');
453 							first = TRUE;
454 							formatinfstr(infstr, x_("CELL=%s FROM=0%lo;-1;0"),
455 								describenodeproto(np), (INTBIG)ni->geom);
456 						}
457 						for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
458 						{
459 							if (pp->network != net) continue;
460 							if (first) addtoinfstr(infstr, '\n');
461 							first = TRUE;
462 							formatinfstr(infstr, x_("CELL=%s TEXT=0%lo;0%lo;-"),
463 								describenodeproto(np), (INTBIG)pp->subnodeinst->geom, (INTBIG)pp);
464 						}
465 					}
466 				}
467 		}
468 	}
469 	(void)asktool(us_tool, x_("show-multiple"), (INTBIG)returninfstr(infstr));
470 	return(FALSE);
471 }
472 
473 /*
474  * Routine to return equivalent network(s) associated with passed net
475  * (comparison must have been done).  Returns 0 if no equivalent net
476  * could be found.  Any equivalent networks found are put in **equiv, with array
477  * size *numequiv, and returns 1.  Returns -1 if no NCC data.
478  */
net_getequivalentnet(NETWORK * net,NETWORK *** equiv,INTBIG * numequiv,BOOLEAN allowchild)479 INTBIG net_getequivalentnet(NETWORK *net, NETWORK ***equiv, INTBIG *numequiv, BOOLEAN allowchild)
480 {
481 	REGISTER SYMGROUP *sg, **sglist;
482 	REGISTER PNET *pn;
483 	REGISTER NETWORK **newequiv;
484 	REGISTER INTBIG f, i, k;
485 	REGISTER BOOLEAN done;
486 
487 	if (net == NONETWORK) return(0);
488 
489 	/* make sure an association has been done */
490 #ifdef NEWNCC
491 	if (net_pcomp1 == NOPCOMP && net_pcomp2 == NOPCOMP && net_nodelist1 == NOPNET && net_nodelist2 == NOPNET)
492 		return(-1);
493 #else
494 	if (net_pcomp1 == NOPCOMP || net_pcomp2 == NOPCOMP) return(-1);
495 #endif
496 
497 	/* make sure ni is in one of the associated cells */
498 	if (net->parent != net_cell[0] && net->parent != net_cell[1])
499 	{
500 		if (!allowchild) return(-1);
501 		if (!isachildof(net->parent, net_cell[0]) && !isachildof(net->parent, net_cell[1]))
502 			return(-1);
503 	}
504 
505 	sglist = net_findnetsymmetrygroup(net);
506 	sg = sglist[0];
507 	if (sg == NOSYMGROUP) return(0);
508 	if (sglist[1] != NOSYMGROUP) return(0);
509 	if (sg->hashvalue == 0) return(0);
510 	if (sg->grouptype != SYMGROUPNET) return(0);
511 
512 	/* find group for net */
513 	done = FALSE;
514 	for (f=0; f<2; f++)
515 	{
516 		for (i=0; i<sg->cellcount[f]; i++)
517 		{
518 			pn = (PNET *)sg->celllist[f][i];
519 			if( net == pn->network) done = TRUE;
520 			if(done) break;
521 		}
522 		if(done) break;
523 	}
524 	if (f==2) return(0);
525 	if (f==1) f = 0; else f = 1;
526 
527 	/* build equivalent nodeinst list */
528 	*numequiv = 0;
529 	for (i=0; i<sg->cellcount[f]; i++)
530 	{
531 		pn = (PNET *)sg->celllist[f][i];
532 		newequiv = (NETWORK **)emalloc((*numequiv + 1)*(sizeof(NETWORK *)), el_tempcluster);
533 		for (k=0; k<(*numequiv); k++)
534 			newequiv[k] = (*equiv)[k];
535 		newequiv[*numequiv] = pn->network;
536 		if( *numequiv > 0) efree((CHAR *)(*equiv));
537 		*equiv = newequiv;
538 		(*numequiv)++;
539 	}
540 	return(TRUE);
541 }
542 
543 /*
544  * Routine to find the symmetry groups associated with object "obj".
545  * Returns an array of symmetry groups.  The first element is NOSYMGROUP
546  * if there are no associated objects.
547  */
net_findgeomsymmetrygroup(GEOM * obj)548 SYMGROUP **net_findgeomsymmetrygroup(GEOM *obj)
549 {
550 	REGISTER SYMGROUP *sg;
551 	REGISTER INTBIG f, i, j, fun;
552 	REGISTER PCOMP *pc;
553 	REGISTER NODEINST *ni, *wantni;
554 	REGISTER ARCINST *ai;
555 
556 	if (obj->entryisnode)
557 	{
558 		/* look for a node */
559 		wantni = obj->entryaddr.ni;
560 		for(sg = net_firstsymgroup; sg != NOSYMGROUP; sg = sg->nextsymgroup)
561 		{
562 			if (sg->grouptype != SYMGROUPCOMP) continue;
563 			for(f=0; f<2; f++)
564 			{
565 				for(i=0; i<sg->cellcount[f]; i++)
566 				{
567 					pc = (PCOMP *)sg->celllist[f][i];
568 					for(j=0; j<pc->numactual; j++)
569 					{
570 						if (pc->numactual == 1) ni = (NODEINST *)pc->actuallist; else
571 							ni = ((NODEINST **)pc->actuallist)[j];
572 						if (ni == wantni)
573 						{
574 							net_foundsymgroupcount = 0;
575 							net_addtofoundsymgroups(sg);
576 							net_addtofoundsymgroups(NOSYMGROUP);
577 							return(net_foundsymgroups);
578 						}
579 					}
580 				}
581 			}
582 		}
583 
584 		/* node not found, try network coming out of it */
585 		fun = nodefunction(wantni);
586 		if (fun == NPPIN || fun == NPCONTACT || fun == NPCONNECT)
587 		{
588 			if (wantni->firstportarcinst != NOPORTARCINST)
589 				obj = wantni->firstportarcinst->conarcinst->geom;
590 		}
591 		if (obj->entryisnode)
592 		{
593 			/* return a null list */
594 			net_foundsymgroupcount = 0;
595 			net_addtofoundsymgroups(NOSYMGROUP);
596 			return(net_foundsymgroups);
597 		}
598 	}
599 
600 	/* look for an arc */
601 	ai = obj->entryaddr.ai;
602 	return(net_findnetsymmetrygroup(ai->network));
603 }
604 
605 /*
606  * Routine to find the symmetry groups associated with network "net".
607  * Returns an array of symmetry groups.  The first element is NOSYMGROUP
608  * if there are no associated objects.
609  */
net_findnetsymmetrygroup(NETWORK * net)610 SYMGROUP **net_findnetsymmetrygroup(NETWORK *net)
611 {
612 	REGISTER SYMGROUP *sg;
613 	REGISTER INTBIG f, i, k;
614 	REGISTER PNET *pn;
615 	REGISTER NETWORK *subnet;
616 
617 	net_foundsymgroupcount = 0;
618 	for(sg = net_firstsymgroup; sg != NOSYMGROUP; sg = sg->nextsymgroup)
619 	{
620 		if (sg->grouptype != SYMGROUPNET) continue;
621 		for(f=0; f<2; f++)
622 		{
623 			for(i=0; i<sg->cellcount[f]; i++)
624 			{
625 				pn = (PNET *)sg->celllist[f][i];
626 				if (pn->network == net)
627 				{
628 					net_addtofoundsymgroups(sg);
629 					net_addtofoundsymgroups(NOSYMGROUP);
630 					return(net_foundsymgroups);
631 				}
632 			}
633 		}
634 	}
635 
636 	/* if this is a bus, load up all signals on it */
637 	if (net->buswidth > 1)
638 	{
639 		for(k=0; k<net->buswidth; k++)
640 		{
641 			subnet = net->networklist[k];
642 			for(sg = net_firstsymgroup; sg != NOSYMGROUP; sg = sg->nextsymgroup)
643 			{
644 				if (sg->grouptype != SYMGROUPNET) continue;
645 				for(f=0; f<2; f++)
646 				{
647 					for(i=0; i<sg->cellcount[f]; i++)
648 					{
649 						pn = (PNET *)sg->celllist[f][i];
650 						if (pn->network == subnet)
651 						{
652 							net_addtofoundsymgroups(sg);
653 							break;
654 						}
655 					}
656 					if (i < sg->cellcount[f]) break;
657 				}
658 				if (f < 2) break;
659 			}
660 		}
661 	}
662 	net_addtofoundsymgroups(NOSYMGROUP);
663 	return(net_foundsymgroups);
664 }
665 
666 /*
667  * Routine to add symmetry group "sg" to the global array "net_foundsymgroups".
668  */
net_addtofoundsymgroups(SYMGROUP * sg)669 void net_addtofoundsymgroups(SYMGROUP *sg)
670 {
671 	REGISTER INTBIG newtotal, i;
672 	REGISTER SYMGROUP **newlist;
673 
674 	if (net_foundsymgroupcount >= net_foundsymgrouptotal)
675 	{
676 		newtotal = net_foundsymgrouptotal * 2;
677 		if (net_foundsymgroupcount >= newtotal) newtotal = net_foundsymgroupcount + 5;
678 		newlist = (SYMGROUP **)emalloc(newtotal * (sizeof (SYMGROUP *)), net_tool->cluster);
679 		if (newlist == 0) return;
680 		for(i=0; i<net_foundsymgroupcount; i++)
681 			newlist[i] = net_foundsymgroups[i];
682 		if (net_foundsymgrouptotal > 0)
683 			efree((CHAR *)net_foundsymgroups);
684 		net_foundsymgroups = newlist;
685 		net_foundsymgrouptotal = newtotal;
686 	}
687 	net_foundsymgroups[net_foundsymgroupcount++] = sg;
688 }
689 
690 /******************************** COMPARISON ********************************/
691 
692 /*
693  * routine to compare the two networks in "cell1" and "cell2" (if they are NONODEPROTO,
694  * use the two cells on the screen).  If "preanalyze" is
695  * true, only do preanalysis and display results.
696  * Returns FALSE if the cells match.
697  */
net_compare(BOOLEAN preanalyze,BOOLEAN interactive,NODEPROTO * cell1,NODEPROTO * cell2)698 BOOLEAN net_compare(BOOLEAN preanalyze, BOOLEAN interactive, NODEPROTO *cell1, NODEPROTO *cell2)
699 {
700 	REGISTER INTBIG ret, resignore;
701 	REGISTER VARIABLE *var;
702 	REGISTER NODEPROTO *np;
703 	REGISTER LIBRARY *lib;
704 	REGISTER BOOLEAN backannotate;
705 	CHAR *respar[2];
706 
707 	backannotate = FALSE;
708 
709 	/* make sure network tool is on */
710 	if ((net_tool->toolstate&TOOLON) == 0)
711 	{
712 		ttyputerr(_("Network tool must be running...turning it on for you"));
713 		toolturnon(net_tool);
714 		return(TRUE);
715 	}
716 
717 	if (cell1 == NONODEPROTO || cell2 == NONODEPROTO)
718 	{
719 		if (net_getcells(&cell1, &cell2))
720 		{
721 			ttyputerr(_("Must have two windows with two different cells"));
722 			return(TRUE);
723 		}
724 	}
725 
726 	/* if the top cells are already checked, stop now */
727 	if (net_nccalreadydone(cell1, cell2))
728 	{
729 		ttyputmsg(_("Cells are already checked"));
730 		return(FALSE);
731 	}
732 
733 	/* get options to use during comparison */
734 	var = getvalkey((INTBIG)net_tool, VTOOL, VINTEGER, net_ncc_optionskey);
735 	if (var == NOVARIABLE) net_ncc_options = 0; else
736 		net_ncc_options = var->addr;
737 
738 	/* see if there are any resistors in this circuit */
739 	if ((net_ncc_options&NCCRESISTINCLUSION) != NCCRESISTLEAVE)
740 	{
741 		resignore = asktech(sch_tech, x_("ignoring-resistor-topology"));
742 		if (resignore != 0 && (net_ncc_options&NCCRESISTINCLUSION) == NCCRESISTINCLUDE)
743 		{
744 			/* must redo network topology to include resistors */
745 			respar[0] = x_("resistors");
746 			respar[1] = x_("include");
747 			(void)telltool(net_tool, 2, respar);
748 		} else if (resignore == 0 && (net_ncc_options&NCCRESISTINCLUSION) == NCCRESISTEXCLUDE)
749 		{
750 			/* must redo network topology to exclude resistors */
751 			respar[0] = x_("resistors");
752 			respar[1] = x_("ignore");
753 			(void)telltool(net_tool, 2, respar);
754 		}
755 	}
756 
757 	starttimer();
758 	if (preanalyze) ttyputmsg(_("Analyzing..."));
759 
760 	/* reset the random number generator so that the results are repeatable */
761 	srand(1);
762 	net_nethashclashtold = FALSE;
763 	net_comphashclashtold = FALSE;
764 
765 	var = getvalkey((INTBIG)net_tool, VTOOL, -1, net_ncc_comptolerancekey);
766 	if (var == NOVARIABLE) net_ncc_tolerance = 0; else
767 	{
768 		if ((var->type&VTYPE) == VINTEGER) net_ncc_tolerance = var->addr * WHOLE; else
769 			if ((var->type&VTYPE) == VFRACT) net_ncc_tolerance = var->addr;
770 	}
771 	var = getvalkey((INTBIG)net_tool, VTOOL, VINTEGER, net_ncc_comptoleranceamtkey);
772 	if (var == NOVARIABLE) net_ncc_tolerance_amt = 0; else net_ncc_tolerance_amt = var->addr;
773 
774 	/* mark all cells as not-checked and name all nets */
775 	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
776 	{
777 		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
778 		{
779 			np->temp1 = (np->temp1 & ~NETNCCCHECKSTATE) | NETNCCNOTCHECKED;
780 			if (asktool(net_tool, x_("name-nets"), (INTBIG)np) != 0) backannotate = TRUE;
781 		}
782 	}
783 
784 	ret = net_ncconelevel(cell1, cell2, preanalyze, interactive);
785 
786 	if (backannotate)
787 		ttyputmsg(_("Back-annotation information has been added (library must be saved)"));
788 
789 	return(ret!=0 ? TRUE : FALSE);
790 }
791 
792 /* NCC warning */
793 static DIALOGITEM net_nccwarndialogitems[] =
794 {
795  /*  1 */ {0, {276,340,300,480}, BUTTON, N_("Show Preanalysis")},
796  /*  2 */ {0, {244,340,268,480}, BUTTON, N_("Stop Now")},
797  /*  3 */ {0, {308,340,332,480}, BUTTON, N_("Do Full NCC")},
798  /*  4 */ {0, {8,8,24,512}, MESSAGE, x_("")},
799  /*  5 */ {0, {28,8,236,512}, SCROLL, x_("")},
800  /*  6 */ {0, {248,56,264,332}, MESSAGE, N_("You may stop the NCC now:")},
801  /*  7 */ {0, {280,56,296,332}, MESSAGE, N_("You may request additional detail:")},
802  /*  8 */ {0, {312,56,328,332}, MESSAGE, N_("You may continue with NCC:")}
803 };
804 static DIALOG net_nccwarndialog = {{75,75,416,597}, N_("NCC Differences Have Been Found"), 0, 8, net_nccwarndialogitems, 0, 0};
805 
806 /* special items for the "NCC warning" dialog: */
807 #define DNCW_DOPREANALYSIS       1		/* Do preanalysis (button) */
808 #define DNCW_STOPNOW             2		/* Stop now (button) */
809 #define DNCW_DONCC               3		/* Do NCC (button) */
810 #define DNCW_TITLE               4		/* Title line (stat text) */
811 #define DNCW_DIFFLIST            5		/* List of differences (scroll) */
812 
813 /*
814  * Routine to compare "cell1" and "cell2".
815  * If "preanalyze" is TRUE, only do preanalysis.
816  * If "interactive" is TRUE, do interactive comparison.
817  * Returns 0 if they compare, 1 if they do not compare, -1 on error.
818  */
net_ncconelevel(NODEPROTO * cell1,NODEPROTO * cell2,BOOLEAN preanalyze,BOOLEAN interactive)819 INTBIG net_ncconelevel(NODEPROTO *cell1, NODEPROTO *cell2, BOOLEAN preanalyze, BOOLEAN interactive)
820 {
821 	INTBIG comp1, comp2, power1, power2, ground1, ground2, netcount1, netcount2,
822 		unmatchednets, unmatchedcomps, prevunmatchednets, prevunmatchedcomps, errors,
823 		symgroupcount, net1remove, net2remove, errorcount;
824 	REGISTER INTBIG i, f, ocomp1, ocomp2, verbose, buscount1, buscount2, ret, itemHit;
825 	BOOLEAN hierarchical, ignorepwrgnd, mergeparallel, mergeseries, recurse,
826 		checkexportnames, checksize, localmergeseries1, localmergeseries2,
827 		localmergeparallel1, localmergeparallel2, subcellsbad,
828 		localhierarchical1, localhierarchical2, figuresizes;
829 	float elapsed;
830 	REGISTER CHAR *errortype, **errorstrings;
831 	REGISTER WINDOWPART *w, *savecurw;
832 	REGISTER NODEINST *ni;
833 	REGISTER NODEPROTO *subnp1, *subnp2;
834 	REGISTER NETWORK *net;
835 	REGISTER VARIABLE *var;
836 	REGISTER PCOMP *pc, *opc;
837 	REGISTER PNET *pn;
838 	REGISTER SYMGROUP *sg;
839 	REGISTER void *infstr, *dia, *errorsa;
840 
841 	/* make sure prime multipliers are computed */
842 	net_initdiff();
843 
844 	/* stop if already checked */
845 	if ((cell1->temp1&NETNCCCHECKSTATE) == NETNCCCHECKEDGOOD &&
846 		(cell2->temp1&NETNCCCHECKSTATE) == NETNCCCHECKEDGOOD) return(0);
847 	if ((cell1->temp1&NETNCCCHECKSTATE) != NETNCCNOTCHECKED ||
848 		(cell2->temp1&NETNCCCHECKSTATE) != NETNCCNOTCHECKED) return(1);
849 	if (net_nccalreadydone(cell1, cell2))
850 	{
851 		cell1->temp1 = (cell1->temp1 & ~NETNCCCHECKSTATE) | NETNCCCHECKEDGOOD;
852 		cell2->temp1 = (cell2->temp1 & ~NETNCCCHECKSTATE) | NETNCCCHECKEDGOOD;
853 		return(0);
854 	}
855 
856 	verbose = net_ncc_options & (NCCVERBOSETEXT | NCCVERBOSEGRAPHICS);
857 	if ((net_ncc_options&NCCRECURSE) != 0) recurse = TRUE; else
858 		recurse = FALSE;
859 	if ((net_ncc_options&NCCHIERARCHICAL) != 0) hierarchical = TRUE; else
860 		hierarchical = FALSE;
861 	if ((net_ncc_options&NCCIGNOREPWRGND) != 0) ignorepwrgnd = TRUE; else
862 		ignorepwrgnd = FALSE;
863 	if ((net_ncc_options&NCCNOMERGEPARALLEL) == 0) mergeparallel = TRUE; else
864 		mergeparallel = FALSE;
865 	if ((net_ncc_options&NCCMERGESERIES) != 0) mergeseries = TRUE; else
866 		mergeseries = FALSE;
867 	if ((net_ncc_options&NCCCHECKEXPORTNAMES) != 0) checkexportnames = TRUE; else
868 		checkexportnames = FALSE;
869 	if ((net_ncc_options&NCCCHECKSIZE) != 0) checksize = TRUE; else
870 		checksize = FALSE;
871 	figuresizes = TRUE;
872 	if (preanalyze) figuresizes = FALSE;
873 	if (!checksize) figuresizes = FALSE;
874 
875 	/* check for cell overrides */
876 	localhierarchical1 = localhierarchical2 = hierarchical;
877 	localmergeparallel1 = localmergeparallel2 = mergeparallel;
878 	localmergeseries1 = localmergeseries2 = mergeseries;
879 	var = getvalkey((INTBIG)cell1, VNODEPROTO, VINTEGER, net_ncc_optionskey);
880 	if (var != NOVARIABLE)
881 	{
882 		if ((var->addr&NCCHIERARCHICALOVER) != 0)
883 		{
884 			if ((var->addr&NCCHIERARCHICAL) != 0) localhierarchical1 = TRUE; else
885 				localhierarchical1 = FALSE;
886 		}
887 		if ((var->addr&NCCNOMERGEPARALLELOVER) != 0)
888 		{
889 			if ((var->addr&NCCNOMERGEPARALLEL) == 0) localmergeparallel1 = TRUE; else
890 				localmergeparallel1 = FALSE;
891 		}
892 		if ((var->addr&NCCMERGESERIESOVER) != 0)
893 		{
894 			if ((var->addr&NCCMERGESERIES) != 0) localmergeseries1 = TRUE; else
895 				localmergeseries1 = FALSE;
896 		}
897 	}
898 	var = getvalkey((INTBIG)cell2, VNODEPROTO, VINTEGER, net_ncc_optionskey);
899 	if (var != NOVARIABLE)
900 	{
901 		if ((var->addr&NCCHIERARCHICALOVER) != 0)
902 		{
903 			if ((var->addr&NCCHIERARCHICAL) != 0) localhierarchical2 = TRUE; else
904 				localhierarchical2 = FALSE;
905 		}
906 		if ((var->addr&NCCNOMERGEPARALLELOVER) != 0)
907 		{
908 			if ((var->addr&NCCNOMERGEPARALLEL) == 0) localmergeparallel2 = TRUE; else
909 				localmergeparallel2 = FALSE;
910 		}
911 		if ((var->addr&NCCMERGESERIESOVER) != 0)
912 		{
913 			if ((var->addr&NCCMERGESERIES) != 0) localmergeseries2 = TRUE; else
914 				localmergeseries2 = FALSE;
915 		}
916 	}
917 	if (localhierarchical1 || localhierarchical2) hierarchical = TRUE; else
918 		hierarchical = FALSE;
919 	if (localmergeparallel1 || localmergeparallel2) mergeparallel = TRUE; else
920 		mergeparallel = FALSE;
921 	if (localmergeseries1 || localmergeseries2) mergeseries = TRUE; else
922 		mergeseries = FALSE;
923 	if (hierarchical) recurse = FALSE;
924 
925 	/* if recursing, look at subcells first */
926 	subcellsbad = FALSE;
927 	if (recurse)
928 	{
929 		for(ni = cell1->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
930 		{
931 			subnp1 = ni->proto;
932 			if (subnp1->primindex != 0) continue;
933 
934 			/* ignore recursive references (showing icon in contents) */
935 			if (isiconof(subnp1, cell1)) continue;
936 			if (subnp1->cellview == el_iconview)
937 			{
938 				subnp1 = anyview(subnp1, cell1->cellview);
939 				if (subnp1 == NONODEPROTO) continue;
940 			}
941 
942 			/* find equivalent to this in the other view */
943 			subnp2 = anyview(subnp1, cell2->cellview);
944 			if (subnp2 == NONODEPROTO)
945 			{
946 				ttyputerr(_("Cannot find %s view of cell %s"), cell2->cellview->viewname,
947 					describenodeproto(subnp1));
948 				continue;
949 			}
950 			ret = net_ncconelevel(subnp1, subnp2, preanalyze, interactive);
951 			if (ret < 0)
952 			{
953 				cell1->temp1 = (cell1->temp1 & ~NETNCCCHECKSTATE) | NETNCCCHECKEDBAD;
954 				cell2->temp1 = (cell2->temp1 & ~NETNCCCHECKSTATE) | NETNCCCHECKEDBAD;
955 				return(ret);
956 			}
957 			if (ret > 0) subcellsbad = TRUE;
958 		}
959 		for(ni = cell2->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
960 		{
961 			subnp2 = ni->proto;
962 			if (subnp2->primindex != 0) continue;
963 
964 			/* ignore recursive references (showing icon in contents) */
965 			if (isiconof(subnp2, cell2)) continue;
966 			if (subnp2->cellview == el_iconview)
967 			{
968 				subnp2 = anyview(subnp2, cell2->cellview);
969 				if (subnp2 == NONODEPROTO) continue;
970 			}
971 
972 			/* find equivalent to this in the other view */
973 			subnp1 = anyview(subnp2, cell1->cellview);
974 			if (subnp1 == NONODEPROTO)
975 			{
976 				ttyputerr(_("Cannot find %s view of cell %s"), cell1->cellview->viewname,
977 					describenodeproto(subnp2));
978 				continue;
979 			}
980 			ret = net_ncconelevel(subnp1, subnp2, preanalyze, interactive);
981 			if (ret < 0)
982 			{
983 				cell1->temp1 = (cell1->temp1 & ~NETNCCCHECKSTATE) | NETNCCCHECKEDBAD;
984 				cell2->temp1 = (cell2->temp1 & ~NETNCCCHECKSTATE) | NETNCCCHECKEDBAD;
985 				return(ret);
986 			}
987 			if (ret > 0) subcellsbad = TRUE;
988 		}
989 	}
990 
991 	/* free any previous data structures */
992 	net_removeassociations();
993 
994 	/* announce what is happening */
995 	ttyputmsg(_("Comparing cell %s with cell %s"),
996 		describenodeproto(cell1), describenodeproto(cell2));
997 
998 	infstr = initinfstr();
999 	if ((net_ncc_options&NCCHIERARCHICAL) != 0) addstringtoinfstr(infstr, _("Flattening hierarchy")); else
1000 	{
1001 		if (recurse) addstringtoinfstr(infstr, _("Checking cells recursively")); else
1002 			addstringtoinfstr(infstr, _("Checking this cell only"));
1003 	}
1004 	if (ignorepwrgnd) addstringtoinfstr(infstr, _("; Ignoring Power and Ground nets")); else
1005 		addstringtoinfstr(infstr, _("; Considering Power and Ground nets"));
1006 	ttyputmsg(x_("- %s"), returninfstr(infstr));
1007 
1008 	infstr = initinfstr();
1009 	if (!mergeparallel) addstringtoinfstr(infstr, _("Parallel components not merged")); else
1010 		 addstringtoinfstr(infstr, _("Parallel components merged"));
1011 	if (!mergeseries) addstringtoinfstr(infstr, _("; Series transistors not merged")); else
1012 		 addstringtoinfstr(infstr, _("; Series transistors merged"));
1013 	ttyputmsg(x_("- %s"), returninfstr(infstr));
1014 
1015 	if (checkexportnames && checksize)
1016 	{
1017 		ttyputmsg(_("- Checking export names and component sizes"));
1018 	} else if (!checkexportnames && !checksize)
1019 	{
1020 		ttyputmsg(_("- Ignoring export names and component sizes"));
1021 	} else
1022 	{
1023 		if (checkexportnames)
1024 			ttyputmsg(_("- Checking export names; Ignoring component sizes")); else
1025 				ttyputmsg(_("- Ignoring export names; Checking component sizes"));
1026 	}
1027 	net_listnccoverrides(FALSE);
1028 
1029 	/* precompute network topology */
1030 	ttyputmsg(_("Preparing circuit for extraction..."));
1031 	net_initnetflattening();
1032 	ttyputmsg(_("--- Done preparing (%s so far)"), explainduration(endtimer()));
1033 
1034 	/* build network of pseudocomponents */
1035 	ttyputmsg(_("Extracting networks from %s..."), describenodeproto(cell1));
1036 	savecurw = el_curwindowpart;
1037 	for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
1038 		if (w->curnodeproto == cell1) break;
1039 	if (w != NOWINDOWPART) el_curwindowpart = w;
1040 	net_pcomp1 = net_makepseudo(cell1, &comp1, &netcount1, &power1, &ground1,
1041 		&net_nodelist1, hierarchical, mergeparallel, mergeseries, TRUE, figuresizes);
1042 	el_curwindowpart = savecurw;
1043 	ttyputmsg(_("--- Done extracting %s (%s so far)"), describenodeproto(cell1),
1044 		explainduration(endtimer()));
1045 	if (net_pcomp1 == NOPCOMP && comp1 < 0)
1046 	{
1047 		cell1->temp1 = (cell1->temp1 & ~NETNCCCHECKSTATE) | NETNCCCHECKEDBAD;
1048 		cell2->temp1 = (cell2->temp1 & ~NETNCCCHECKSTATE) | NETNCCCHECKEDBAD;
1049 		return(-1);
1050 	}
1051 	ttyputmsg(_("Extracting networks from %s..."), describenodeproto(cell2));
1052 	for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
1053 		if (w->curnodeproto == cell2) break;
1054 	if (w != NOWINDOWPART) el_curwindowpart = w;
1055 	net_pcomp2 = net_makepseudo(cell2, &comp2, &netcount2, &power2, &ground2,
1056 		&net_nodelist2, hierarchical, mergeparallel, mergeseries, TRUE, figuresizes);
1057 	el_curwindowpart = savecurw;
1058 	ttyputmsg(_("--- Done extracting %s (%s so far)"), describenodeproto(cell2),
1059 		explainduration(endtimer()));
1060 	if (net_pcomp2 == NOPCOMP && comp2 < 0)
1061 	{
1062 		cell1->temp1 = (cell1->temp1 & ~NETNCCCHECKSTATE) | NETNCCCHECKEDBAD;
1063 		cell2->temp1 = (cell2->temp1 & ~NETNCCCHECKSTATE) | NETNCCCHECKEDBAD;
1064 		return(-1);
1065 	}
1066 	net_cell[0] = cell1;   net_cell[1] = cell2;
1067 	net1remove = net2remove = 0;
1068 	if (ignorepwrgnd)
1069 	{
1070 		net1remove = power1 + ground1;
1071 		net2remove = power2 + ground2;
1072 	}
1073 
1074 	/* separate nets into plain and busses */
1075 	buscount1 = 0;
1076 	for(pn = net_nodelist1; pn != NOPNET; pn = pn->nextpnet)
1077 	{
1078 		net = pn->network;
1079 		if (net == NONETWORK) continue;
1080 		if (net->buswidth > 1) buscount1++;
1081 	}
1082 	netcount1 -= buscount1;
1083 	buscount2 = 0;
1084 	for(pn = net_nodelist2; pn != NOPNET; pn = pn->nextpnet)
1085 	{
1086 		net = pn->network;
1087 		if (net == NONETWORK) continue;
1088 		if (net->buswidth > 1) buscount2++;
1089 	}
1090 	netcount2 -= buscount2;
1091 
1092 	/* remove extraneous information */
1093 	net_removeextraneous(&net_pcomp1, &net_nodelist1, &comp1);
1094 	net_removeextraneous(&net_pcomp2, &net_nodelist2, &comp2);
1095 
1096 	if (!mergeparallel && comp1 != comp2)
1097 	{
1098 		/* see if merging parallel components makes them match */
1099 		ocomp1 = comp1;   ocomp2 = comp2;
1100 		if (comp1 < comp2)
1101 		{
1102 			/* try merging parallel components in cell 2 */
1103 			ttyputmsg(_("--- Cell %s has %ld components and cell %s has %ld: merging parallel components in cell %s..."),
1104 				describenodeproto(cell1), comp1, describenodeproto(cell2), comp2,
1105 					describenodeproto(cell2));
1106 			(void)net_mergeparallel(&net_pcomp2, net_nodelist2, &comp2);
1107 			if (comp1 > comp2)
1108 			{
1109 				ttyputmsg(_("--- Merging parallel components in cell %s..."),
1110 					describenodeproto(cell1));
1111 				(void)net_mergeparallel(&net_pcomp1, net_nodelist1, &comp1);
1112 			}
1113 		} else
1114 		{
1115 			/* try merging parallel components in cell 1 */
1116 			ttyputmsg(_("--- Cell %s has %ld components and cell %s has %ld: merging parallel components in cell %s..."),
1117 				describenodeproto(cell1), comp1, describenodeproto(cell2), comp2,
1118 					describenodeproto(cell1));
1119 			(void)net_mergeparallel(&net_pcomp1, net_nodelist1, &comp1);
1120 			if (comp2 > comp1)
1121 			{
1122 				ttyputmsg(_("--- Merging parallel components in cell %s..."),
1123 					describenodeproto(cell2));
1124 				(void)net_mergeparallel(&net_pcomp2, net_nodelist2, &comp2);
1125 			}
1126 		}
1127 	}
1128 
1129 	/* make sure network pointers are correct */
1130 	net_fillinnetpointers(net_pcomp1, net_nodelist1);
1131 	net_fillinnetpointers(net_pcomp2, net_nodelist2);
1132 
1133 	/* announce results of extraction */
1134 	errorsa = newstringarray(net_tool->cluster);
1135 	if (comp1 == comp2)
1136 		ttyputmsg(_("Both cells have %ld components"), comp1); else
1137 	{
1138 		infstr = initinfstr();
1139 		formatinfstr(infstr, _("Cell %s has %ld components but cell %s has %ld"),
1140 			describenodeproto(cell1), comp1, describenodeproto(cell2), comp2);
1141 		addtostringarray(errorsa, returninfstr(infstr));
1142 	}
1143 	if (netcount1-net1remove == netcount2-net2remove)
1144 		ttyputmsg(_("Both cells have %ld nets"), netcount1-net1remove); else
1145 	{
1146 		infstr = initinfstr();
1147 		formatinfstr(infstr, _("Cell %s has %ld nets but cell %s has %ld"),
1148 			describenodeproto(cell1), netcount1-net1remove, describenodeproto(cell2), netcount2-net2remove);
1149 		addtostringarray(errorsa, returninfstr(infstr));
1150 	}
1151 	if (buscount1 == buscount2)
1152 	{
1153 		if (buscount1 != 0)
1154 			ttyputmsg(_("Both cells have %ld busses"), netcount1-net1remove);
1155 	} else
1156 	{
1157 		ttyputmsg(_("Note: cell %s has %ld busses but cell %s has %ld"),
1158 			describenodeproto(cell1), buscount1, describenodeproto(cell2), buscount2);
1159 	}
1160 	if (!ignorepwrgnd)
1161 	{
1162 		if (power1 != power2)
1163 		{
1164 			infstr = initinfstr();
1165 			formatinfstr(infstr, _("Cell %s has %ld power nets but cell %s has %ld"),
1166 				describenodeproto(cell1), power1, describenodeproto(cell2), power2);
1167 			addtostringarray(errorsa, returninfstr(infstr));
1168 			infstr = initinfstr();
1169 			formatinfstr(infstr, _("  Number of components on power nets in cell %s:"),
1170 				describenodeproto(cell1));
1171 			for(pn = net_nodelist1; pn != NOPNET; pn = pn->nextpnet)
1172 				if ((pn->flags&POWERNET) != 0)
1173 					formatinfstr(infstr, x_(" %ld"), pn->nodecount);
1174 			addtostringarray(errorsa, returninfstr(infstr));
1175 			infstr = initinfstr();
1176 			formatinfstr(infstr, _("  Number of components on power nets in cell %s:"),
1177 				describenodeproto(cell2));
1178 			for(pn = net_nodelist2; pn != NOPNET; pn = pn->nextpnet)
1179 				if ((pn->flags&POWERNET) != 0)
1180 					formatinfstr(infstr, x_(" %ld"), pn->nodecount);
1181 			addtostringarray(errorsa, returninfstr(infstr));
1182 		}
1183 		if (ground1 != ground2)
1184 		{
1185 			infstr = initinfstr();
1186 			formatinfstr(infstr, _("Cell %s has %ld ground nets but cell %s has %ld"),
1187 				describenodeproto(cell1), ground1, describenodeproto(cell2), ground2);
1188 			addtostringarray(errorsa, returninfstr(infstr));
1189 			infstr = initinfstr();
1190 			formatinfstr(infstr, _("  Number of components on ground nets in cell %s:"),
1191 				describenodeproto(cell1));
1192 			for(pn = net_nodelist1; pn != NOPNET; pn = pn->nextpnet)
1193 				if ((pn->flags&GROUNDNET) != 0)
1194 					formatinfstr(infstr, x_(" %ld"), pn->nodecount);
1195 			addtostringarray(errorsa, returninfstr(infstr));
1196 			infstr = initinfstr();
1197 			formatinfstr(infstr, _("  Number of components on ground nets in cell %s:"),
1198 				describenodeproto(cell2));
1199 			for(pn = net_nodelist2; pn != NOPNET; pn = pn->nextpnet)
1200 				if ((pn->flags&GROUNDNET) != 0)
1201 					formatinfstr(infstr, x_(" %ld"), pn->nodecount);
1202 			addtostringarray(errorsa, returninfstr(infstr));
1203 		}
1204 	}
1205 
1206 	/* if there are no problems found, look deeper */
1207 	(void)getstringarray(errorsa, &errorcount);
1208 	if (errorcount == 0)
1209 	{
1210 		/* check to be sure the component types match */
1211 		net_checkcomponenttypes(errorsa, ignorepwrgnd, net_pcomp1, net_pcomp2,
1212 			net_nodelist1, net_nodelist2, cell1, cell2);
1213 	}
1214 
1215 	/* check for duplicate names */
1216 #if 0		/* why does this crash? */
1217 	net_checkforduplicatenames(net_nodelist1);
1218 	net_checkforduplicatenames(net_nodelist2);
1219 #endif
1220 
1221 	/* if there are possible problems, report them now */
1222 	errorstrings = getstringarray(errorsa, &errorcount);
1223 	for(i=0; i<errorcount; i++)
1224 		ttyputmsg(_("Note: %s"), errorstrings[i]);
1225 	if (!preanalyze && errorcount > 0 && interactive)
1226 	{
1227 		dia = DiaInitDialog(&net_nccwarndialog);
1228 		if (dia == 0) return(-1);
1229 		DiaInitTextDialog(dia, DNCW_DIFFLIST, DiaNullDlogList, DiaNullDlogItem,
1230 			DiaNullDlogDone, -1, SCHORIZBAR);
1231 		infstr = initinfstr();
1232 		formatinfstr(infstr, _("Differences between cells %s and %s:"),
1233 			describenodeproto(cell1), describenodeproto(cell2));
1234 		DiaSetText(dia, DNCW_TITLE, returninfstr(infstr));
1235 		for(i=0; i<errorcount; i++)
1236 			DiaStuffLine(dia, DNCW_DIFFLIST, errorstrings[i]);
1237 		DiaSelectLine(dia, DNCW_DIFFLIST, -1);
1238 
1239 		for(;;)
1240 		{
1241 			itemHit = DiaNextHit(dia);
1242 			if (itemHit == DNCW_DOPREANALYSIS || itemHit == DNCW_STOPNOW ||
1243 				itemHit == DNCW_DONCC) break;
1244 		}
1245 		DiaDoneDialog(dia);
1246 		if (itemHit == DNCW_STOPNOW)
1247 		{
1248 			killstringarray(errorsa);
1249 			return(-1);
1250 		}
1251 		if (itemHit == DNCW_DOPREANALYSIS) preanalyze = TRUE;
1252 	}
1253 	killstringarray(errorsa);
1254 
1255 	/* build list of PNODEs and wires on each net */
1256 	if (preanalyze) verbose = 0;
1257 	net_timestamp = 0;
1258 	if (verbose != 0)
1259 	{
1260 		net_initializeverbose(net_pcomp1, net_nodelist1);
1261 		net_initializeverbose(net_pcomp2, net_nodelist2);
1262 	}
1263 
1264 	if (preanalyze)
1265 	{
1266 		/* dump the networks and stop */
1267 		net_showpreanalysis(cell1, net_pcomp1, net_nodelist1,
1268 			cell2, net_pcomp2, net_nodelist2, ignorepwrgnd);
1269 		cell1->temp1 = (cell1->temp1 & ~NETNCCCHECKSTATE) | NETNCCCHECKEDGOOD;
1270 		cell2->temp1 = (cell2->temp1 & ~NETNCCCHECKSTATE) | NETNCCCHECKEDGOOD;
1271 		return(0);
1272 	}
1273 
1274 	/* try to find network symmetry with existing switches */
1275 	net_debuggeminipasscount = 0;
1276 	net_debuggeminiexpandglobal = net_debuggeminiexpandglobalworked = 0;
1277 	net_debuggeminigroupsrenumbered = net_debuggeminigroupssplit = 0;
1278 	net_debuggeminitotalgroups = net_debuggeminitotalgroupsact = 0;
1279 	ret = net_dogemini(net_pcomp1, net_nodelist1, net_pcomp2, net_nodelist2,
1280 		checksize, checkexportnames, ignorepwrgnd);
1281 	if (ret < 0) return(-1);
1282 
1283 	/* if match failed, see if unmerged parallel components are ambiguous */
1284 	if (ret != 0 && !mergeparallel)
1285 	{
1286 		for(sg = net_firstsymgroup; sg != NOSYMGROUP; sg = sg->nextsymgroup)
1287 		{
1288 			if (sg->grouptype != SYMGROUPCOMP) continue;
1289 			if (sg->cellcount[0] <= 1 || sg->cellcount[1] <= 1) continue;
1290 			for(f=0; f<2; f++)
1291 			{
1292 				for(i=1; i<sg->cellcount[f]; i++)
1293 				{
1294 					opc = (PCOMP *)sg->celllist[f][i-1];
1295 					pc = (PCOMP *)sg->celllist[f][i];
1296 					if (net_comparewirelist(pc, opc, FALSE)) continue;
1297 					mergeparallel = TRUE;
1298 					break;
1299 				}
1300 				if (mergeparallel) break;
1301 			}
1302 			if (mergeparallel) break;
1303 		}
1304 		if (mergeparallel)
1305 		{
1306 			/* might work if parallel components are merged */
1307 			ttyputmsg(_("--- No match: trying again with parallel components merged"));
1308 			net_unmatchedstatus(&prevunmatchednets, &prevunmatchedcomps, &symgroupcount);
1309 			(void)net_mergeparallel(&net_pcomp1, net_nodelist1, &comp1);
1310 			(void)net_mergeparallel(&net_pcomp2, net_nodelist2, &comp2);
1311 			net_debuggeminipasscount = 0;
1312 			net_debuggeminiexpandglobal = net_debuggeminiexpandglobalworked = 0;
1313 			net_debuggeminigroupsrenumbered = net_debuggeminigroupssplit = 0;
1314 			net_debuggeminitotalgroups = net_debuggeminitotalgroupsact = 0;
1315 			ret = net_dogemini(net_pcomp1, net_nodelist1, net_pcomp2, net_nodelist2,
1316 				checksize, checkexportnames, ignorepwrgnd);
1317 			if (ret < 0) return(-1);
1318 			if (ret != 0)
1319 			{
1320 				net_unmatchedstatus(&unmatchednets, &unmatchedcomps, &symgroupcount);
1321 				if (unmatchednets + unmatchedcomps < prevunmatchednets + prevunmatchedcomps)
1322 				{
1323 					/* this improved things but didn't solve them, use it */
1324 					ttyputmsg(_("------ Merge of parallel components improved match"));
1325 				} else if (unmatchednets + unmatchedcomps == prevunmatchednets + prevunmatchedcomps)
1326 					ttyputmsg(_("------ Merge of parallel components make no change")); else
1327 						ttyputmsg(_("------ Merge of parallel components make things worse"));
1328 			}
1329 		}
1330 	}
1331 
1332 	/* free reason information */
1333 	if (verbose != 0)
1334 	{
1335 		for(pn = net_nodelist1; pn != NOPNET; pn = pn->nextpnet)
1336 		{
1337 			efree((CHAR *)pn->hashreason);
1338 			pn->hashreason = 0;
1339 		}
1340 		for(pn = net_nodelist2; pn != NOPNET; pn = pn->nextpnet)
1341 		{
1342 			efree((CHAR *)pn->hashreason);
1343 			pn->hashreason = 0;
1344 		}
1345 		for(pc = net_pcomp1; pc != NOPCOMP; pc = pc->nextpcomp)
1346 		{
1347 			efree((CHAR *)pc->hashreason);
1348 			pc->hashreason = 0;
1349 		}
1350 		for(pc = net_pcomp2; pc != NOPCOMP; pc = pc->nextpcomp)
1351 		{
1352 			efree((CHAR *)pc->hashreason);
1353 			pc->hashreason = 0;
1354 		}
1355 	}
1356 	if ((net_ncc_options&NCCENASTATISTICS) != 0)
1357 	{
1358 		ttyputmsg(x_("--- Made %ld iterations of Gemini"), net_debuggeminipasscount);
1359 		if (net_debuggeminiexpandglobal != 0)
1360 		{
1361 			ttyputmsg(x_("---    Of those %ld iterations considered all groups"), net_debuggeminiexpandglobal);
1362 			ttyputmsg(x_("---    And %ld of the all-group examinations found new splits"), net_debuggeminiexpandglobalworked);
1363 		}
1364 		ttyputmsg(x_("--- Renumbered %ld symmetry groups"), net_debuggeminigroupsrenumbered);
1365 		if (net_debuggeminigroupssplit != net_debuggeminigroupsrenumbered)
1366 			ttyputmsg(x_("---    Of these, %ld split"), net_debuggeminigroupssplit);
1367 		if (net_debuggeminitotalgroups != net_debuggeminitotalgroupsact)
1368 		{
1369 			if (net_debuggeminitotalgroups == 0) net_debuggeminitotalgroups= 1;
1370 			ttyputmsg(x_("---    On average, %ld%% of groups are active"),
1371 				net_debuggeminitotalgroupsact*100/net_debuggeminitotalgroups);
1372 		}
1373 	}
1374 
1375 	/* see if errors were found */
1376 	initerrorlogging(_("NCC"));
1377 
1378 #ifdef FORCESUNTOOLS
1379 	if ((net_ncc_options&NCCEXPERIMENTAL) != 0)
1380 	{
1381 		errors = net_expanalyzesymmetrygroups(TRUE, checksize, checkexportnames, ignorepwrgnd, &errorcount);
1382 	} else
1383 #endif
1384 	errors = net_analyzesymmetrygroups(TRUE, checksize, checkexportnames, ignorepwrgnd, &errorcount);
1385 
1386 	/* write summary of NCC */
1387 	elapsed = endtimer();
1388 	if (elapsed > 60.0 && (us_useroptions&BEEPAFTERLONGJOB) != 0)
1389 		ttybeep(SOUNDBEEP, TRUE);
1390 	if (errors != 0)
1391 	{
1392 		switch (errors)
1393 		{
1394 			case SIZEERRORS:
1395 				errortype = N_("Size");                          break;
1396 			case EXPORTERRORS:
1397 				errortype = N_("Export");                        break;
1398 			case STRUCTUREERRORS:
1399 				errortype = N_("Structural");                    break;
1400 			case SIZEERRORS|EXPORTERRORS:
1401 				errortype = N_("Size and Export");               break;
1402 			case SIZEERRORS|STRUCTUREERRORS:
1403 				errortype = N_("Size and Structural");           break;
1404 			case EXPORTERRORS|STRUCTUREERRORS:
1405 				errortype = N_("Export and Structural");         break;
1406 			case SIZEERRORS|EXPORTERRORS|STRUCTUREERRORS:
1407 				errortype = N_("Size, Export and Structural");   break;
1408 			default:
1409 				errortype = 0;
1410 		}
1411 		ttyputmsg(_("******* Found %ld %s differences! (%s)"), errorcount,
1412 			errortype, explainduration(elapsed));
1413 		ret = 1;
1414 	} else
1415 	{
1416 		ttyputmsg(_("Cells %s and %s are equivalent (%s)"), describenodeproto(net_cell[0]),
1417 			describenodeproto(net_cell[1]), explainduration(elapsed));
1418 		if (subcellsbad)
1419 		{
1420 			ttyputmsg(_("******* But some subcells are not equivalent"));
1421 		} else
1422 		{
1423 #ifndef NEWNCC
1424 			net_preserveresults(cell1, cell2);
1425 			net_preserveresults(cell2, cell1);
1426 #endif
1427 		}
1428 		ret = subcellsbad ? 1 : 0;
1429 	}
1430 	termerrorlogging(TRUE);
1431 #ifdef NEWNCC
1432 	net_preserveresults(cell1, cell2);
1433 	net_preserveresults(cell2, cell1);
1434 #endif
1435 	if (ret == 0)
1436 	{
1437 		cell1->temp1 = (cell1->temp1 & ~NETNCCCHECKSTATE) | NETNCCCHECKEDGOOD;
1438 		cell2->temp1 = (cell2->temp1 & ~NETNCCCHECKSTATE) | NETNCCCHECKEDGOOD;
1439 	} else
1440 	{
1441 		cell1->temp1 = (cell1->temp1 & ~NETNCCCHECKSTATE) | NETNCCCHECKEDBAD;
1442 		cell2->temp1 = (cell2->temp1 & ~NETNCCCHECKSTATE) | NETNCCCHECKEDBAD;
1443 	}
1444 	return(ret);
1445 }
1446 
1447 /*
1448  * Routine to run the Gemini algorithm to match components/nets "pcomp1/pnet1" with
1449  * components/nets "pcomp2/pnet2".  Use "checksize" to check sizes,
1450  * "checkexportnames" to check port names, and "ignorepwrgnd" to ignore power and ground.
1451  * The value of "mergeparallel" indicates whether parallel components are merged.
1452  * The routine returns:
1453  *   -1  Hard error (memory allocation, etc.)
1454  *    0  Networks match
1455  *    1  No match, but association quiesced
1456  *    2  No match, and association did not quiesce
1457  */
net_dogemini(PCOMP * pcomp1,PNET * pnet1,PCOMP * pcomp2,PNET * pnet2,BOOLEAN checksize,BOOLEAN checkexportnames,BOOLEAN ignorepwrgnd)1458 INTBIG net_dogemini(PCOMP *pcomp1, PNET *pnet1, PCOMP *pcomp2, PNET *pnet2,
1459 	BOOLEAN checksize, BOOLEAN checkexportnames, BOOLEAN ignorepwrgnd)
1460 {
1461 	REGISTER SYMGROUP *sgc, *sgn, *sgnz, *sg, *lastsg;
1462 	REGISTER PCOMP *pc;
1463 	REGISTER PNET *pn;
1464 	REGISTER INTBIG i, changesc, changesn, verbose, redeemcount, unmatched,
1465 		prevunmatched, prevsymgroupcount, splittype, assigncompfirst, renumber,
1466 		changes, reassignnow, pass;
1467 	INTBIG unmatchednets, unmatchedcomps, symgroupcount, errors, errorcount;
1468 	CHAR prompt[100];
1469 	static BOOLEAN toldtohitreturn = FALSE;
1470 
1471 #ifdef FORCESUNTOOLS
1472 	if ((net_ncc_options&NCCEXPERIMENTAL) != 0)
1473 	{
1474 		return(net_doexpgemini(pcomp1, pnet1, pcomp2, pnet2,
1475 			checksize, checkexportnames, ignorepwrgnd));
1476 	}
1477 #endif
1478 
1479 	verbose = net_ncc_options & (NCCVERBOSETEXT | NCCVERBOSEGRAPHICS);
1480 
1481 	/* clear old symmetry group list */
1482 	while (net_firstsymgroup != NOSYMGROUP)
1483 	{
1484 		sg = net_firstsymgroup;
1485 		net_firstsymgroup = sg->nextsymgroup;
1486 		net_freesymgroup(sg);
1487 	}
1488 	while (net_firstmatchedsymgroup != NOSYMGROUP)
1489 	{
1490 		sg = net_firstmatchedsymgroup;
1491 		net_firstmatchedsymgroup = sg->nextsymgroup;
1492 		net_freesymgroup(sg);
1493 	}
1494 	if (net_symgrouphashcompsize != 0)
1495 	{
1496 		efree((CHAR *)net_symgrouphashcomp);
1497 		efree((CHAR *)net_symgrouphashckcomp);
1498 	}
1499 	if (net_symgrouphashnetsize != 0)
1500 	{
1501 		efree((CHAR *)net_symgrouphashnet);
1502 		efree((CHAR *)net_symgrouphashcknet);
1503 	}
1504 	net_uniquehashvalue = -1;
1505 	net_symgroupnumber = 1;
1506 
1507 	/* determine size of hash tables */
1508 	net_symgrouphashnetsize = net_symgrouphashcompsize = 0;
1509 	for(pc = pcomp1; pc != NOPCOMP; pc = pc->nextpcomp) net_symgrouphashcompsize++;
1510 	for(pc = pcomp2; pc != NOPCOMP; pc = pc->nextpcomp) net_symgrouphashcompsize++;
1511 	if (net_symgrouphashcompsize <= 0) net_symgrouphashcompsize = 2;
1512 	for(pn = pnet1; pn != NOPNET; pn = pn->nextpnet) net_symgrouphashnetsize++;
1513 	for(pn = pnet2; pn != NOPNET; pn = pn->nextpnet) net_symgrouphashnetsize++;
1514 	if (net_symgrouphashnetsize <= 0) net_symgrouphashnetsize = 2;
1515 	net_symgrouphashcompsize = pickprime(net_symgrouphashcompsize * 2);
1516 	net_symgrouphashnetsize = pickprime(net_symgrouphashnetsize * 2);
1517 	net_symgrouphashcomp = (SYMGROUP **)emalloc(net_symgrouphashcompsize * (sizeof (SYMGROUP *)),
1518 		net_tool->cluster);
1519 	if (net_symgrouphashcomp == 0) return(-1);
1520 	net_symgrouphashckcomp = (INTBIG *)emalloc(net_symgrouphashcompsize * SIZEOFINTBIG,
1521 		net_tool->cluster);
1522 	if (net_symgrouphashckcomp == 0) return(-1);
1523 	net_symgrouphashnet = (SYMGROUP **)emalloc(net_symgrouphashnetsize * (sizeof (SYMGROUP *)),
1524 		net_tool->cluster);
1525 	if (net_symgrouphashnet == 0) return(-1);
1526 	net_symgrouphashcknet = (INTBIG *)emalloc(net_symgrouphashnetsize * SIZEOFINTBIG,
1527 		net_tool->cluster);
1528 	if (net_symgrouphashcknet == 0) return(-1);
1529 	for(i=0; i<net_symgrouphashcompsize; i++) net_symgrouphashcomp[i] = NOSYMGROUP;
1530 	for(i=0; i<net_symgrouphashnetsize; i++) net_symgrouphashnet[i] = NOSYMGROUP;
1531 
1532 	/* reset hash explanations */
1533 	if (verbose != 0)
1534 	{
1535 		for(pc = pcomp1; pc != NOPCOMP; pc = pc->nextpcomp)
1536 			(void)reallocstring(&pc->hashreason, x_("initial"), net_tool->cluster);
1537 		for(pc = pcomp2; pc != NOPCOMP; pc = pc->nextpcomp)
1538 			(void)reallocstring(&pc->hashreason, x_("initial"), net_tool->cluster);
1539 		for(pn = pnet1; pn != NOPNET; pn = pn->nextpnet)
1540 			(void)reallocstring(&pn->hashreason, x_("initial"), net_tool->cluster);
1541 		for(pn = pnet2; pn != NOPNET; pn = pn->nextpnet)
1542 			(void)reallocstring(&pn->hashreason, x_("initial"), net_tool->cluster);
1543 	}
1544 
1545 	/* new time stamp for initial entry into symmetry groups */
1546 	net_timestamp++;
1547 
1548 	/* initially assign all components to the same symmetry group (ignore SPICE) */
1549 	sgc = net_newsymgroup(SYMGROUPCOMP, 1, 0);
1550 	if (sgc == NOSYMGROUP) return(-1);
1551 	for(pc = pcomp1; pc != NOPCOMP; pc = pc->nextpcomp)
1552 	{
1553 		pc->hashvalue = sgc->hashvalue;
1554 		if (net_addtosymgroup(sgc, 0, (void *)pc)) return(-1);
1555 		pc->symgroup = sgc;
1556 	}
1557 	for(pc = pcomp2; pc != NOPCOMP; pc = pc->nextpcomp)
1558 	{
1559 		pc->hashvalue = sgc->hashvalue;
1560 		if (net_addtosymgroup(sgc, 1, (void *)pc)) return(-1);
1561 		pc->symgroup = sgc;
1562 	}
1563 
1564 	/* initially assign all nets to the same symmetry group (with ignored pwr/gnd in zero group) */
1565 	sgn = net_newsymgroup(SYMGROUPNET, 1, 0);
1566 	if (sgn == NOSYMGROUP) return(-1);
1567 	sgnz = net_newsymgroup(SYMGROUPNET, 0, 0);
1568 	if (sgnz == NOSYMGROUP) return(-1);
1569 	for(pn = pnet1; pn != NOPNET; pn = pn->nextpnet)
1570 	{
1571 		if (ignorepwrgnd && (pn->flags&(POWERNET|GROUNDNET)) != 0)
1572 		{
1573 			pn->hashvalue = sgnz->hashvalue;
1574 			if (net_addtosymgroup(sgnz, 0, (void *)pn)) return(-1);
1575 			pn->symgroup = sgnz;
1576 		} else
1577 		{
1578 			pn->hashvalue = sgn->hashvalue;
1579 			if (net_addtosymgroup(sgn, 0, (void *)pn)) return(-1);
1580 			pn->symgroup = sgn;
1581 		}
1582 	}
1583 	for(pn = pnet2; pn != NOPNET; pn = pn->nextpnet)
1584 	{
1585 		if (ignorepwrgnd && (pn->flags&(POWERNET|GROUNDNET)) != 0)
1586 		{
1587 			pn->hashvalue = sgnz->hashvalue;
1588 			if (net_addtosymgroup(sgnz, 0, (void *)pn)) return(-1);
1589 			pn->symgroup = sgnz;
1590 		} else
1591 		{
1592 			pn->hashvalue = sgn->hashvalue;
1593 			if (net_addtosymgroup(sgn, 1, (void *)pn)) return(-1);
1594 			pn->symgroup = sgn;
1595 		}
1596 	}
1597 
1598 	/* determine the true wirecount (considering ignored power and ground) */
1599 	for(pc = pcomp1; pc != NOPCOMP; pc = pc->nextpcomp)
1600 	{
1601 		pc->truewirecount = pc->wirecount;
1602 		for(i=0; i<pc->wirecount; i++)
1603 			if (pc->netnumbers[i]->hashvalue == 0) pc->truewirecount--;
1604 	}
1605 	for(pc = pcomp2; pc != NOPCOMP; pc = pc->nextpcomp)
1606 	{
1607 		pc->truewirecount = pc->wirecount;
1608 		for(i=0; i<pc->wirecount; i++)
1609 			if (pc->netnumbers[i]->hashvalue == 0) pc->truewirecount--;
1610 	}
1611 
1612 	/* now iteratively refine the symmetry groups */
1613 	net_unmatchedstatus(&unmatchednets, &unmatchedcomps, &symgroupcount);
1614 	prevsymgroupcount = symgroupcount;
1615 	prevunmatched = unmatchednets + unmatchedcomps;
1616 	redeemcount = 1;
1617 	assigncompfirst = 1;
1618 	changesc = changesn = 0;
1619 	pass = 0;
1620 	for(i=0; i<MAXITERATIONS; i++)
1621 	{
1622 		if (stopping(STOPREASONNCC)) break;
1623 
1624 		/* new time stamp for entry into symmetry groups */
1625 		net_timestamp++;
1626 		changes = 0;
1627 		for(renumber=0; renumber<2; renumber++)
1628 		{
1629 			if ((renumber == 0 && assigncompfirst != 0) ||
1630 				(renumber == 1 && assigncompfirst == 0))
1631 			{
1632 				/* assign new hash values to components */
1633 				if ((verbose&NCCVERBOSETEXT) != 0)
1634 					ttyputmsg(x_("***Computing component codes from networks:"));
1635 				changes = changesc = net_assignnewhashvalues(reassignnow = SYMGROUPCOMP);
1636 				if (changesc < 0) break;
1637 			} else
1638 			{
1639 				/* assign new hash values to nets */
1640 				if ((verbose&NCCVERBOSETEXT) != 0)
1641 					ttyputmsg(x_("***Computing network codes from components:"));
1642 				changes = changesn = net_assignnewhashvalues(reassignnow = SYMGROUPNET);
1643 				if (changesn < 0) break;
1644 			}
1645 
1646 			/* show the state of the world if requested */
1647 			if (verbose != 0)
1648 			{
1649 				net_showsymmetrygroups(verbose, reassignnow);
1650 				esnprintf(prompt, 100, x_("%ld changed, %ld symmetry groups:"), changes, symgroupcount);
1651 				if (!toldtohitreturn)
1652 				{
1653 					estrcat(prompt, x_(" (hit return to continue) "));
1654 					toldtohitreturn = TRUE;
1655 				}
1656 				(void)asktool(us_tool, x_("flush-changes"));
1657 				if (*ttygetlinemessages(prompt) != 0) break;
1658 			}
1659 
1660 			/* pull out matched objects */
1661 			net_cleanupsymmetrygroups();
1662 
1663 			/* every 3rd time, randomize hash codes */
1664 			if (((pass++) % 3) == 0)
1665 				net_randomizehashcodes(verbose);
1666 		}
1667 		if (changes < 0) break;
1668 
1669 		/* if things are still improving, keep on */
1670 		net_unmatchedstatus(&unmatchednets, &unmatchedcomps, &symgroupcount);
1671 		unmatched = unmatchednets + unmatchedcomps;
1672 		if (unmatched == 0) break;
1673 		if (unmatched < prevunmatched)
1674 		{
1675 			prevunmatched = unmatched;
1676 			i--;
1677 			prevsymgroupcount = symgroupcount;
1678 			continue;
1679 		}
1680 
1681 		/* if nothing changed or about to stop the loop, look for ambiguity */
1682 		if (changesc + changesn == 0 || symgroupcount == prevsymgroupcount || i == MAXITERATIONS-1)
1683 		{
1684 			/* new time stamp for entry into symmetry groups */
1685 			net_timestamp++;
1686 
1687 			/* see if some incremental match can be applied */
1688 			splittype = net_findamatch(verbose, ignorepwrgnd);
1689 			if (splittype != 0)
1690 			{
1691 				if (splittype > 0)
1692 				{
1693 					/* if just split a component group, reassign networks first */
1694 					assigncompfirst = 0;
1695 				} else
1696 				{
1697 					/* if just split a network group, reassign components first */
1698 					assigncompfirst = 1;
1699 				}
1700 				i = 0;
1701 				prevsymgroupcount = symgroupcount;
1702 				continue;
1703 			}
1704 			break;
1705 		}
1706 		prevsymgroupcount = symgroupcount;
1707 	}
1708 
1709 	/* put matched symmetry groups back into the main list (place it at the end) */
1710 	lastsg = NOSYMGROUP;
1711 	for(sg = net_firstsymgroup; sg != NOSYMGROUP; sg = sg->nextsymgroup)
1712 		lastsg = sg;
1713 	if (lastsg == NOSYMGROUP) net_firstsymgroup = net_firstmatchedsymgroup; else
1714 		lastsg->nextsymgroup = net_firstmatchedsymgroup;
1715 	net_firstmatchedsymgroup = NOSYMGROUP;
1716 
1717 	/* see if errors were found */
1718 	errors = net_analyzesymmetrygroups(FALSE, checksize, checkexportnames, ignorepwrgnd, &errorcount);
1719 	if (errors == 0) return(0);
1720 	if (changesc + changesn != 0) return(2);
1721 	return(1);
1722 }
1723 
1724 /*
1725  * Routine to clean up the symmetry groups by deleting those with nothing in them
1726  * and pulling matched ones out of consideration.
1727  */
net_cleanupsymmetrygroups(void)1728 void net_cleanupsymmetrygroups(void)
1729 {
1730 	REGISTER SYMGROUP *sg, *lastsg, *nextsg;
1731 
1732 	lastsg = NOSYMGROUP;
1733 	for(sg = net_firstsymgroup; sg != NOSYMGROUP; sg = nextsg)
1734 	{
1735 		nextsg = sg->nextsymgroup;
1736 
1737 		/* delete empty groups */
1738 		if (sg->cellcount[0] == 0 && sg->cellcount[1] == 0)
1739 		{
1740 			if (lastsg == NOSYMGROUP) net_firstsymgroup = sg->nextsymgroup; else
1741 				lastsg->nextsymgroup = sg->nextsymgroup;
1742 			net_freesymgroup(sg);
1743 			continue;
1744 		}
1745 
1746 		/* pull matched groups into separate list */
1747 		if (sg->cellcount[0] == 1 && sg->cellcount[1] == 1)
1748 		{
1749 			if (lastsg == NOSYMGROUP) net_firstsymgroup = sg->nextsymgroup; else
1750 				lastsg->nextsymgroup = sg->nextsymgroup;
1751 			sg->nextsymgroup = net_firstmatchedsymgroup;
1752 			net_firstmatchedsymgroup = sg;
1753 			if ((net_ncc_options&NCCGRAPHICPROGRESS) != 0)
1754 				net_showmatchedgroup(sg);
1755 			continue;
1756 		}
1757 		lastsg = sg;
1758 	}
1759 }
1760 
1761 #define RANDOMIZEBYMULTIPLYING 1
1762 
net_randomizehashcodes(INTBIG verbose)1763 void net_randomizehashcodes(INTBIG verbose)
1764 {
1765 #ifdef RANDOMIZEBYMULTIPLYING
1766 	REGISTER SYMGROUP *sg;
1767 	REGISTER INTBIG index, factor;
1768 
1769 	if ((verbose&NCCVERBOSETEXT) != 0)
1770 		ttyputmsg(x_("***Renumbering hash codes:"));
1771 	index = 0;
1772 	for(sg = net_firstsymgroup; sg != NOSYMGROUP; sg = sg->nextsymgroup)
1773 	{
1774 		factor = getprime(index++);
1775 		net_randomizesymgroup(sg, verbose, factor);
1776 	}
1777 	for(sg = net_firstmatchedsymgroup; sg != NOSYMGROUP; sg = sg->nextsymgroup)
1778 	{
1779 		factor = getprime(index++);
1780 		net_randomizesymgroup(sg, verbose, factor);
1781 	}
1782 #else
1783 	REGISTER SYMGROUP *sg;
1784 
1785 	if ((verbose&NCCVERBOSETEXT) != 0)
1786 		ttyputmsg(x_("***Renumbering hash codes:"));
1787 	for(sg = net_firstsymgroup; sg != NOSYMGROUP; sg = sg->nextsymgroup)
1788 		net_randomizesymgroup(sg, verbose, 0);
1789 	for(sg = net_firstmatchedsymgroup; sg != NOSYMGROUP; sg = sg->nextsymgroup)
1790 		net_randomizesymgroup(sg, verbose, 0);
1791 #endif
1792 	net_rebuildhashtable();
1793 }
1794 
net_randomizesymgroup(SYMGROUP * sg,INTBIG verbose,INTBIG factor)1795 void net_randomizesymgroup(SYMGROUP *sg, INTBIG verbose, INTBIG factor)
1796 {
1797 	REGISTER INTBIG f, j;
1798 	REGISTER PCOMP *pc;
1799 	REGISTER PNET *pn;
1800 	REGISTER NODEINST *ni;
1801 	REGISTER NETWORK *net;
1802 
1803 	if (sg->hashvalue != 0)
1804 	{
1805 #ifdef RANDOMIZEBYMULTIPLYING
1806 		sg->hashvalue *= factor;
1807 #else
1808 		sg->hashvalue = (HASHTYPE)rand();
1809 		sg->hashvalue = (sg->hashvalue << 16) | (HASHTYPE)rand();
1810 		sg->hashvalue = (sg->hashvalue << 16) | (HASHTYPE)rand();
1811 		sg->hashvalue = (sg->hashvalue << 16) | (HASHTYPE)rand();
1812 #endif
1813 		if (sg->grouptype == SYMGROUPCOMP)
1814 		{
1815 			for(f=0; f<2; f++)
1816 			{
1817 				for(j=0; j<sg->cellcount[f]; j++)
1818 				{
1819 					pc = (PCOMP *)sg->celllist[f][j];
1820 					pc->hashvalue = sg->hashvalue;
1821 					if ((verbose&NCCVERBOSETEXT) != 0)
1822 					{
1823 						if (pc->numactual == 1) ni = (NODEINST *)pc->actuallist; else
1824 							ni = ((NODEINST **)pc->actuallist)[0];
1825 						if (sg->groupindex != 0)
1826 						{
1827 							ttyputmsg(x_(" COMPONENT %s: now %s (hash #%ld)"), describenodeinst(ni),
1828 								hugeinttoa(sg->hashvalue), sg->groupindex);
1829 						} else
1830 						{
1831 							ttyputmsg(x_(" COMPONENT %s: now %s (hash)"), describenodeinst(ni),
1832 								hugeinttoa(sg->hashvalue));
1833 						}
1834 					}
1835 				}
1836 			}
1837 		} else
1838 		{
1839 			for(f=0; f<2; f++)
1840 			{
1841 				for(j=0; j<sg->cellcount[f]; j++)
1842 				{
1843 					pn = (PNET *)sg->celllist[f][j];
1844 					pn->hashvalue = sg->hashvalue;
1845 					if ((verbose&NCCVERBOSETEXT) != 0)
1846 					{
1847 						net = pn->network;
1848 						if (net != NONETWORK)
1849 						{
1850 							if (sg->groupindex != 0)
1851 							{
1852 								ttyputmsg(x_(" NETWORK %s:%s: now %s (hash #%ld)"), describenodeproto(net->parent),
1853 									net_describepnet(pn), hugeinttoa(sg->hashvalue), sg->groupindex);
1854 							} else
1855 							{
1856 								ttyputmsg(x_(" NETWORK %s:%s: now %s (hash)"), describenodeproto(net->parent),
1857 									net_describepnet(pn), hugeinttoa(sg->hashvalue));
1858 							}
1859 						}
1860 					}
1861 				}
1862 			}
1863 		}
1864 	}
1865 }
1866 
1867 /*
1868  * Routine to assign new hash values to the components or nets (depending on the
1869  * value of "grouptype") in all symmetry groups.  Returns the number of changes
1870  * that were made (negative on error).
1871  */
net_assignnewhashvalues(INTBIG grouptype)1872 INTBIG net_assignnewhashvalues(INTBIG grouptype)
1873 {
1874 	REGISTER SYMGROUP *sg;
1875 	REGISTER INTBIG changes, verbose;
1876 	REGISTER INTBIG f, i, j, change, activemask;
1877 	REGISTER BOOLEAN matched, focusonactivegroups;
1878 	REGISTER SYMGROUP *lastsg, *nextsg;
1879 	REGISTER PCOMP *pc;
1880 	REGISTER PNET *pn;
1881 
1882 	/* pickup options */
1883 	net_debuggeminipasscount++;
1884 	verbose = net_ncc_options & (NCCVERBOSETEXT | NCCVERBOSEGRAPHICS);
1885 	activemask = 0;
1886 	if ((net_ncc_options & (NCCENAFOCSYMGRPFRE|NCCENAFOCSYMGRPPRO)) != 0)
1887 	{
1888 		if ((net_ncc_options&NCCENAFOCSYMGRPFRE) != 0) activemask |= GROUPFRESH;
1889 		if ((net_ncc_options&NCCENAFOCSYMGRPPRO) != 0) activemask |= GROUPPROMISING;
1890 		focusonactivegroups = TRUE;
1891 	} else focusonactivegroups = FALSE;
1892 
1893 	/* setup for spreading active groups */
1894 	for(sg = net_firstsymgroup; sg != NOSYMGROUP; sg = sg->nextsymgroup)
1895 		sg->groupflags &= ~activemask;
1896 
1897 	changes = 0;
1898 	for(sg = net_firstsymgroup; sg != NOSYMGROUP; sg = sg->nextsymgroup)
1899 	{
1900 		if (sg->hashvalue == 0) continue;
1901 		if (sg->grouptype != grouptype) continue;
1902 		net_debuggeminitotalgroups++;
1903 		if (focusonactivegroups)
1904 		{
1905 			if ((sg->groupflags&GROUPACTIVENOW) == 0) continue;
1906 		}
1907 		net_debuggeminitotalgroupsact++;
1908 		net_debuggeminigroupsrenumbered++;
1909 		change = net_assignnewgrouphashvalues(sg, verbose);
1910 		if (change != 0) net_debuggeminigroupssplit++;
1911 		changes += change;
1912 	}
1913 
1914 	if (changes == 0 && focusonactivegroups)
1915 	{
1916 		/* no changes found while examining the focus group: expand to the entire list */
1917 		net_debuggeminiexpandglobal++;
1918 		for(sg = net_firstsymgroup; sg != NOSYMGROUP; sg = sg->nextsymgroup)
1919 		{
1920 			if (sg->hashvalue == 0) continue;
1921 			if (sg->grouptype != grouptype) continue;
1922 			if ((sg->groupflags&GROUPACTIVENOW) != 0) continue;
1923 			net_debuggeminigroupsrenumbered++;
1924 			change = net_assignnewgrouphashvalues(sg, verbose);
1925 			if (change != 0) net_debuggeminigroupssplit++;
1926 			changes += change;
1927 		}
1928 		if (changes != 0) net_debuggeminiexpandglobalworked++;
1929 	}
1930 
1931 	/* propagate active groups */
1932 	for(sg = net_firstsymgroup; sg != NOSYMGROUP; sg = sg->nextsymgroup)
1933 	{
1934 		if (sg->grouptype != grouptype)
1935 		{
1936 			if ((sg->groupflags&activemask) != 0)
1937 				sg->groupflags |= GROUPACTIVENOW;
1938 		} else
1939 		{
1940 			if ((sg->groupflags&activemask) != 0)
1941 				sg->groupflags |= GROUPACTIVENOW; else
1942 					sg->groupflags &= ~GROUPACTIVENOW;
1943 		}
1944 	}
1945 
1946 	/* see if local processing after a match is requested */
1947 	if ((net_ncc_options&NCCDISLOCAFTERMATCH) != 0) return(changes);
1948 
1949 	/* now look for newly created matches and keep working from there */
1950 	for(;;)
1951 	{
1952 		/* clear all flags of locality */
1953 		for(sg = net_firstsymgroup; sg != NOSYMGROUP; sg = sg->nextsymgroup)
1954 		{
1955 			switch (sg->grouptype)
1956 			{
1957 				case SYMGROUPCOMP:
1958 					for(f=0; f<2; f++)
1959 					{
1960 						for(i=0; i<sg->cellcount[f]; i++)
1961 						{
1962 							pc = (PCOMP *)sg->celllist[f][i];
1963 							pc->flags &= ~COMPLOCALFLAG;
1964 						}
1965 					}
1966 					break;
1967 				case SYMGROUPNET:
1968 					for(f=0; f<2; f++)
1969 					{
1970 						for(i=0; i<sg->cellcount[f]; i++)
1971 						{
1972 							pn = (PNET *)sg->celllist[f][i];
1973 							pn->flags &= ~NETLOCALFLAG;
1974 						}
1975 					}
1976 					break;
1977 			}
1978 		}
1979 
1980 		/* mark local flags and remove match groups */
1981 		matched = FALSE;
1982 		lastsg = NOSYMGROUP;
1983 		for(sg = net_firstsymgroup; sg != NOSYMGROUP; sg = nextsg)
1984 		{
1985 			nextsg = sg->nextsymgroup;
1986 			if (sg->hashvalue != 0)
1987 			{
1988 				if (sg->cellcount[0] == 1 && sg->cellcount[1] == 1)
1989 				{
1990 					switch (sg->grouptype)
1991 					{
1992 						case SYMGROUPCOMP:
1993 							/* mark all related nets for local reevaluation */
1994 							for(f=0; f<2; f++)
1995 							{
1996 								pc = (PCOMP *)sg->celllist[f][0];
1997 								for(j=0; j<pc->wirecount; j++)
1998 								{
1999 									pn = pc->netnumbers[j];
2000 									pn->flags |= NETLOCALFLAG;
2001 								}
2002 							}
2003 							break;
2004 						case SYMGROUPNET:
2005 							/* mark all related components for local reevaluation */
2006 							for(f=0; f<2; f++)
2007 							{
2008 								pn = (PNET *)sg->celllist[f][0];
2009 								for(j=0; j<pn->nodecount; j++)
2010 								{
2011 									pc = pn->nodelist[j];
2012 									pc->flags |= COMPLOCALFLAG;
2013 								}
2014 							}
2015 							break;
2016 					}
2017 
2018 					/* pull the matched groups into separate list */
2019 					if (lastsg == NOSYMGROUP) net_firstsymgroup = sg->nextsymgroup; else
2020 						lastsg->nextsymgroup = sg->nextsymgroup;
2021 					sg->nextsymgroup = net_firstmatchedsymgroup;
2022 					net_firstmatchedsymgroup = sg;
2023 					matched = TRUE;
2024 					continue;
2025 				}
2026 			}
2027 			lastsg = sg;
2028 		}
2029 
2030 		/* if there are no new matches, stop now */
2031 		if (!matched) break;
2032 
2033 		/* search for groups that need to be reevaluated */
2034 		for(sg = net_firstsymgroup; sg != NOSYMGROUP; sg = sg->nextsymgroup)
2035 		{
2036 			if (sg->hashvalue == 0) continue;
2037 			switch (sg->grouptype)
2038 			{
2039 				case SYMGROUPCOMP:
2040 					/* see if this group is marked as local */
2041 					for(f=0; f<2; f++)
2042 					{
2043 						for(i=0; i<sg->cellcount[f]; i++)
2044 						{
2045 							pc = (PCOMP *)sg->celllist[f][i];
2046 							if ((pc->flags&COMPLOCALFLAG) != 0) break;
2047 						}
2048 						if (i < sg->cellcount[f]) break;
2049 					}
2050 					if (f >= 2) break;
2051 
2052 					/* reevaluate this group */
2053 					change = net_assignnewgrouphashvalues(sg, verbose);
2054 					changes += change;
2055 					break;
2056 				case SYMGROUPNET:
2057 					/* mark all related components for local reevaluation */
2058 					for(f=0; f<2; f++)
2059 					{
2060 						for(i=0; i<sg->cellcount[f]; i++)
2061 						{
2062 							pn = (PNET *)sg->celllist[f][i];
2063 							if ((pn->flags&NETLOCALFLAG) != 0) break;
2064 						}
2065 						if (i < sg->cellcount[f]) break;
2066 					}
2067 					if (f >= 2) break;
2068 
2069 					/* reevaluate this group */
2070 					change = net_assignnewgrouphashvalues(sg, verbose);
2071 					changes += change;
2072 					break;
2073 			}
2074 		}
2075 	}
2076 	return(changes);
2077 }
2078 
net_assignnewgrouphashvalues(SYMGROUP * sg,INTBIG verbose)2079 INTBIG net_assignnewgrouphashvalues(SYMGROUP *sg, INTBIG verbose)
2080 {
2081 	REGISTER INTBIG f, i, j;
2082 	REGISTER PCOMP *pc;
2083 	REGISTER PNET *pn;
2084 	REGISTER SYMGROUP *osg, *newgroup;
2085 	REGISTER BOOLEAN groupsplits;
2086 
2087 	switch (sg->grouptype)
2088 	{
2089 		case SYMGROUPCOMP:
2090 			for(f=0; f<2; f++)
2091 			{
2092 				for(i=0; i<sg->cellcount[f]; i++)
2093 				{
2094 					pc = (PCOMP *)sg->celllist[f][i];
2095 
2096 					/* if the group is properly matched, don't change its hash value */
2097 					if (sg->cellcount[0] == 1 && sg->cellcount[1] == 1)
2098 					{
2099 						if (verbose != 0)
2100 							(void)reallocstring(&pc->hashreason, x_("matched"), net_tool->cluster);
2101 						continue;
2102 					}
2103 
2104 					/* if the group is a singleton, set a zero hash value */
2105 					if (sg->cellcount[0] <= 0 || sg->cellcount[1] <= 0)
2106 					{
2107 						if (verbose != 0)
2108 							(void)reallocstring(&pc->hashreason, x_("unmatched"), net_tool->cluster);
2109 						pc->hashvalue = 0;
2110 					} else
2111 					{
2112 						/* compute a new hash value for the component */
2113 						pc->hashvalue = net_getcomphash(pc, verbose);
2114 					}
2115 				}
2116 			}
2117 			newgroup = NOSYMGROUP;
2118 			groupsplits = FALSE;
2119 			for(f=0; f<2; f++)
2120 			{
2121 				for(i=0; i<sg->cellcount[f]; i++)
2122 				{
2123 					pc = (PCOMP *)sg->celllist[f][i];
2124 					if (pc->hashvalue != sg->hashvalue)
2125 					{
2126 						/* reassign this component to a different symmetry group */
2127 						osg = net_findsymmetrygroup(SYMGROUPCOMP, pc->hashvalue, pc->truewirecount);
2128 						if (osg == NOSYMGROUP)
2129 						{
2130 							osg = net_newsymgroup(SYMGROUPCOMP, pc->hashvalue, pc->truewirecount);
2131 							if (osg == NOSYMGROUP) return(-1);
2132 						}
2133 						net_removefromsymgroup(sg, f, i);
2134 						i--;
2135 						if (net_addtosymgroup(osg, f, (void *)pc)) return(-1);
2136 						pc->symgroup = osg;
2137 						if (groupsplits) osg->groupflags |= GROUPFRESH; else
2138 						{
2139 							if (newgroup == NOSYMGROUP) newgroup = osg; else
2140 							{
2141 								if (newgroup != osg)
2142 								{
2143 									groupsplits = TRUE;
2144 									newgroup->groupflags |= GROUPFRESH;
2145 									osg->groupflags |= GROUPFRESH;
2146 								}
2147 							}
2148 						}
2149 					} else
2150 					{
2151 						if (groupsplits) sg->groupflags |= GROUPFRESH; else
2152 						{
2153 							if (newgroup == NOSYMGROUP) newgroup = sg; else
2154 							{
2155 								if (newgroup != sg)
2156 								{
2157 									groupsplits = TRUE;
2158 									newgroup->groupflags |= GROUPFRESH;
2159 									sg->groupflags |= GROUPFRESH;
2160 								}
2161 							}
2162 						}
2163 					}
2164 				}
2165 			}
2166 
2167 			/* propagate promising groups */
2168 			if (groupsplits && (net_ncc_options&NCCENAFOCSYMGRPPRO) != 0)
2169 			{
2170 				/* this group split into others: propagate this information for localism */
2171 				for(f=0; f<2; f++)
2172 				{
2173 					for(i=0; i<sg->cellcount[f]; i++)
2174 					{
2175 						pc = (PCOMP *)sg->celllist[f][i];
2176 						for(j=0; j<pc->wirecount; j++)
2177 						{
2178 							pn = pc->netnumbers[j];
2179 							((SYMGROUP *)pn->symgroup)->groupflags |= GROUPPROMISING;
2180 						}
2181 					}
2182 				}
2183 			}
2184 			break;
2185 
2186 		case SYMGROUPNET:
2187 			for(f=0; f<2; f++)
2188 			{
2189 				for(i=0; i<sg->cellcount[f]; i++)
2190 				{
2191 					pn = (PNET *)sg->celllist[f][i];
2192 
2193 					/* if the group is properly matched, don't change its hash value */
2194 					if (sg->cellcount[0] == 1 && sg->cellcount[1] == 1)
2195 					{
2196 						if (verbose != 0)
2197 							(void)reallocstring(&pn->hashreason, x_("matched"), net_tool->cluster);
2198 						continue;
2199 					}
2200 
2201 					/* if the group is a singleton, set a zero hash value */
2202 					if (sg->cellcount[0] <= 0 || sg->cellcount[1] <= 0)
2203 					{
2204 						pn->hashvalue = 0;
2205 						if (verbose != 0)
2206 							(void)reallocstring(&pn->hashreason, x_("unmatched"), net_tool->cluster);
2207 					} else
2208 					{
2209 						/* compute a new hash value for the net */
2210 						pn->hashvalue = net_getnethash(pn, verbose);
2211 					}
2212 				}
2213 			}
2214 			newgroup = NOSYMGROUP;
2215 			groupsplits = FALSE;
2216 			for(f=0; f<2; f++)
2217 			{
2218 				for(i=0; i<sg->cellcount[f]; i++)
2219 				{
2220 					pn = (PNET *)sg->celllist[f][i];
2221 					if (pn->hashvalue != sg->hashvalue)
2222 					{
2223 						/* reassign this component to a different symmetry group */
2224 						osg = net_findsymmetrygroup(SYMGROUPNET, pn->hashvalue, pn->nodecount);
2225 						if (osg == NOSYMGROUP)
2226 						{
2227 							osg = net_newsymgroup(SYMGROUPNET, pn->hashvalue, pn->nodecount);
2228 							if (osg == NOSYMGROUP) return(-1);
2229 						}
2230 						net_removefromsymgroup(sg, f, i);
2231 						i--;
2232 						if (net_addtosymgroup(osg, f, (void *)pn)) return(-1);
2233 						pn->symgroup = osg;
2234 						if (groupsplits) osg->groupflags |= GROUPFRESH; else
2235 						{
2236 							if (newgroup == NOSYMGROUP) newgroup = osg; else
2237 							{
2238 								if (newgroup != osg)
2239 								{
2240 									groupsplits = TRUE;
2241 									newgroup->groupflags |= GROUPFRESH;
2242 									osg->groupflags |= GROUPFRESH;
2243 								}
2244 							}
2245 						}
2246 					} else
2247 					{
2248 						if (groupsplits) sg->groupflags |= GROUPFRESH; else
2249 						{
2250 							if (newgroup == NOSYMGROUP) newgroup = sg; else
2251 							{
2252 								if (newgroup != sg)
2253 								{
2254 									groupsplits = TRUE;
2255 									newgroup->groupflags |= GROUPFRESH;
2256 									sg->groupflags |= GROUPFRESH;
2257 								}
2258 							}
2259 						}
2260 					}
2261 				}
2262 			}
2263 
2264 			if (groupsplits && (net_ncc_options&NCCENAFOCSYMGRPPRO) != 0)
2265 			{
2266 				/* this group split into others: propagate this information for localism */
2267 				for(f=0; f<2; f++)
2268 				{
2269 					for(i=0; i<sg->cellcount[f]; i++)
2270 					{
2271 						pn = (PNET *)sg->celllist[f][i];
2272 						for(j=0; j<pn->nodecount; j++)
2273 						{
2274 							pc = pn->nodelist[j];
2275 							((SYMGROUP *)pc->symgroup)->groupflags |= GROUPPROMISING;
2276 						}
2277 					}
2278 				}
2279 			}
2280 			break;
2281 	}
2282 	if (groupsplits) return(1);
2283 	return(0);
2284 }
2285 
2286 /*
2287  * Routine to fill out the "nodecount/nodelist/nodewire" fields of the PNET
2288  * list in "pnetlist", given that it points to "pcomplist".  Returns true on error.
2289  */
net_initializeverbose(PCOMP * pcomplist,PNET * pnetlist)2290 void net_initializeverbose(PCOMP *pcomplist, PNET *pnetlist)
2291 {
2292 	REGISTER PNET *pn;
2293 	REGISTER PCOMP *pc;
2294 
2295 	for(pn = pnetlist; pn != NOPNET; pn = pn->nextpnet)
2296 	{
2297 		(void)allocstring(&pn->hashreason, x_("initial"), net_tool->cluster);
2298 	}
2299 
2300 	for(pc = pcomplist; pc != NOPCOMP; pc = pc->nextpcomp)
2301 	{
2302 		(void)allocstring(&pc->hashreason, x_("initial"), net_tool->cluster);
2303 	}
2304 }
2305 
2306 /*
2307  * Routine to examine the unexpanded cell instances in lists "pcomp1" and "pcomp2"
2308  * and make sure there are matching numbers of each type.
2309  * Adds error messages to the string array "errorsa" if any problems are found.
2310  */
net_checkcomponenttypes(void * errorsa,BOOLEAN ignorepwrgnd,PCOMP * pcomp1,PCOMP * pcomp2,PNET * pnetlist1,PNET * pnetlist2,NODEPROTO * cell1,NODEPROTO * cell2)2311 void net_checkcomponenttypes(void *errorsa, BOOLEAN ignorepwrgnd, PCOMP *pcomp1, PCOMP *pcomp2,
2312 	PNET *pnetlist1, PNET *pnetlist2, NODEPROTO *cell1, NODEPROTO *cell2)
2313 {
2314 	REGISTER INTBIG cells1, cells2, cellnum, i, j, l, portcount1, portcount2, start;
2315 	REGISTER NODEPROTO *thecell;
2316 	REGISTER BOOLEAN found;
2317 	REGISTER PCOMP **celllist1, **celllist2, *pc1, *pc2;
2318 	REGISTER PNET *pn;
2319 	REGISTER NODEINST *ni1, *ni2;
2320 	REGISTER CHAR *pt;
2321 	REGISTER void *infstr;
2322 
2323 	/* first count the number of cell instances that are primitives in each cell */
2324 	cells1 = 0;
2325 	for(pc1 = pcomp1; pc1 != NOPCOMP; pc1 = pc1->nextpcomp)
2326 	{
2327 		if (pc1->numactual == 1) ni1 = (NODEINST *)pc1->actuallist; else
2328 			ni1 = ((NODEINST **)pc1->actuallist)[0];
2329 		if (ni1->proto->primindex == 0 && pc1->function == NPUNKNOWN) cells1++;
2330 	}
2331 	cells2 = 0;
2332 	for(pc2 = pcomp2; pc2 != NOPCOMP; pc2 = pc2->nextpcomp)
2333 	{
2334 		if (pc2->numactual == 1) ni2 = (NODEINST *)pc2->actuallist; else
2335 			ni2 = ((NODEINST **)pc2->actuallist)[0];
2336 		if (ni2->proto->primindex == 0 && pc2->function == NPUNKNOWN) cells2++;
2337 	}
2338 
2339 	/* if they are different, report that error */
2340 	if (cells1 != cells2)
2341 	{
2342 		infstr = initinfstr();
2343 		formatinfstr(infstr, _("Cell %s has %ld instances but cell %s has %ld instances"),
2344 			describenodeproto(cell1), cells1, describenodeproto(cell2), cells2);
2345 		addtostringarray(errorsa, returninfstr(infstr));
2346 		return;
2347 	}
2348 
2349 	/* if there are no cells on either side, stop now */
2350 	if (cells1 == 0) return;
2351 
2352 	/* make a list of each cell's "function" number */
2353 	celllist1 = (PCOMP **)emalloc(cells1 * (sizeof (PCOMP *)), net_tool->cluster);
2354 	if (celllist1 == 0) return;
2355 	celllist2 = (PCOMP **)emalloc(cells2 * (sizeof (PCOMP *)), net_tool->cluster);
2356 	if (celllist2 == 0) return;
2357 	cells1 = 0;
2358 	for(pc1 = pcomp1; pc1 != NOPCOMP; pc1 = pc1->nextpcomp)
2359 	{
2360 		if (pc1->numactual == 1) ni1 = (NODEINST *)pc1->actuallist; else
2361 			ni1 = ((NODEINST **)pc1->actuallist)[0];
2362 		if (ni1->proto->primindex != 0) continue;
2363 		celllist1[cells1++] = pc1;
2364 		portcount1 = 0;
2365 		for(l=0; l<pc1->wirecount; l++)
2366 		{
2367 			pn = pc1->netnumbers[l];
2368 			if (ignorepwrgnd && (pn->flags&(POWERNET|GROUNDNET)) != 0) continue;
2369 			portcount1++;
2370 		}
2371 		pc1->truewirecount = (INTSML)portcount1;
2372 	}
2373 	cells2 = 0;
2374 	for(pc2 = pcomp2; pc2 != NOPCOMP; pc2 = pc2->nextpcomp)
2375 	{
2376 		if (pc2->numactual == 1) ni2 = (NODEINST *)pc2->actuallist; else
2377 			ni2 = ((NODEINST **)pc2->actuallist)[0];
2378 		if (ni2->proto->primindex != 0) continue;
2379 		celllist2[cells2++] = pc2;
2380 		portcount2 = 0;
2381 		for(l=0; l<pc2->wirecount; l++)
2382 		{
2383 			pn = pc2->netnumbers[l];
2384 			if (ignorepwrgnd && (pn->flags&(POWERNET|GROUNDNET)) != 0) continue;
2385 			portcount2++;
2386 		}
2387 		pc2->truewirecount = (INTSML)portcount2;
2388 	}
2389 	esort(celllist1, cells1, sizeof (PCOMP *), net_sortbycelltype);
2390 	esort(celllist2, cells2, sizeof (PCOMP *), net_sortbycelltype);
2391 
2392 	/* see if the functions are the same */
2393 	for(i=0; i<cells1; i++)
2394 	{
2395 		pc1 = celllist1[i];
2396 		pc2 = celllist2[i];
2397 		if (pc1->numactual == 1) ni1 = (NODEINST *)pc1->actuallist; else
2398 			ni1 = ((NODEINST **)pc1->actuallist)[0];
2399 		if (pc2->numactual == 1) ni2 = (NODEINST *)pc2->actuallist; else
2400 			ni2 = ((NODEINST **)pc2->actuallist)[0];
2401 		if ((ni1->proto->temp1&NETCELLCODE) != (ni2->proto->temp1&NETCELLCODE)) break;
2402 	}
2403 	if (i >= cells1)
2404 	{
2405 		/* cell functions are the same: make sure the exports match */
2406 		for(i=0; i<cells1; i++)
2407 		{
2408 			start = i;
2409 			pc1 = celllist1[i];
2410 			if (pc1->numactual == 1) ni1 = (NODEINST *)pc1->actuallist; else
2411 				ni1 = ((NODEINST **)pc1->actuallist)[0];
2412 			thecell = ni1->proto;
2413 			cellnum = thecell->temp1 & NETCELLCODE;
2414 
2415 			/* determine the end of the block of cells with the same instance number */
2416 			while (i < cells1-1)
2417 			{
2418 				pc1 = celllist1[i+1];
2419 				if (pc1->numactual == 1) ni1 = (NODEINST *)pc1->actuallist; else
2420 					ni1 = ((NODEINST **)pc1->actuallist)[0];
2421 				if (cellnum != (ni1->proto->temp1 & NETCELLCODE)) break;
2422 				i++;
2423 			}
2424 
2425 			/* make sure the prototype exports match */
2426 			{
2427 				NETWORK **list1, **list2;
2428 				INTBIG size1, size2, pos1, pos2;
2429 
2430 				pc1 = celllist1[start];
2431 				if (pc1->numactual == 1) ni1 = (NODEINST *)pc1->actuallist; else
2432 					ni1 = ((NODEINST **)pc1->actuallist)[0];
2433 				pc2 = celllist2[start];
2434 				if (pc2->numactual == 1) ni2 = (NODEINST *)pc2->actuallist; else
2435 					ni2 = ((NODEINST **)pc2->actuallist)[0];
2436 				list1 = net_makeexternalnetlist(ni1, &size1, ignorepwrgnd);
2437 				list2 = net_makeexternalnetlist(ni2, &size2, ignorepwrgnd);
2438 				if (size1 > 0) esort(list1, size1, sizeof (NETWORK *), net_sortexportcodes);
2439 				if (size2 > 0) esort(list2, size2, sizeof (NETWORK *), net_sortexportcodes);
2440 				pos1 = pos2 = 0;
2441 				while (pos1 < size1 || pos2 < size2)
2442 				{
2443 					if (pos1 < size1 && pos2 < size2 && list1[pos1]->temp2 == list2[pos2]->temp2)
2444 					{
2445 						pos1++;   pos2++;
2446 						while (pos1 < size1 && list1[pos1]->temp2 == list1[pos1-1]->temp2)
2447 							pos1++;
2448 						while (pos2 < size2 && list2[pos2]->temp2 == list2[pos2-1]->temp2)
2449 							pos2++;
2450 						continue;
2451 					}
2452 					if (pos2 >= size2 ||
2453 						(pos1 < size1 && list1[pos1]->temp2 < list2[pos2]->temp2))
2454 					{
2455 						/* report or fix missing connection */
2456 						net_foundmismatch(list1[pos1]->parent, list1[pos1], ni2->proto, celllist2, start, i,
2457 							pnetlist2, ignorepwrgnd, errorsa);
2458 						pos1++;
2459 						while (pos1 < size1 && list1[pos1]->temp2 == list1[pos1-1]->temp2)
2460 							pos1++;
2461 					} else if (pos1 >= size1 ||
2462 						(pos2 < size2 && list2[pos2]->temp2 < list1[pos1]->temp2))
2463 					{
2464 						/* report or fix missing connection */
2465 						net_foundmismatch(list2[pos2]->parent, list2[pos2], ni1->proto, celllist1, start, i,
2466 							pnetlist1, ignorepwrgnd, errorsa);
2467 						pos2++;
2468 						while (pos2 < size2 && list2[pos2]->temp2 == list2[pos2-1]->temp2)
2469 							pos2++;
2470 					}
2471 				}
2472 				if (size1 > 0) efree((CHAR *)list1);
2473 				if (size2 > 0) efree((CHAR *)list2);
2474 			}
2475 
2476 			/* check all cells for export similarity */
2477 			for(j=start; j<=i; j++)
2478 			{
2479 				pc1 = celllist1[j];
2480 				if (pc1->numactual == 1) ni1 = (NODEINST *)pc1->actuallist; else
2481 					ni1 = ((NODEINST **)pc1->actuallist)[0];
2482 
2483 				pc2 = celllist2[j];
2484 				if (pc2->numactual == 1) ni2 = (NODEINST *)pc2->actuallist; else
2485 					ni2 = ((NODEINST **)pc2->actuallist)[0];
2486 				if (ni1->proto == ni2->proto) continue;
2487 
2488 				/* make sure the export count matches */
2489 				if (pc1->truewirecount != pc2->truewirecount)
2490 				{
2491 					infstr = initinfstr();
2492 					formatinfstr(infstr, _("Instance %s has %ld wires in cell %s but has %ld wires in cell %s"),
2493 						ni1->proto->protoname, pc1->truewirecount, describenodeproto(cell1),
2494 							pc2->truewirecount, describenodeproto(cell2));
2495 					addtostringarray(errorsa, returninfstr(infstr));
2496 				}
2497 			}
2498 		}
2499 	} else
2500 	{
2501 		/* cell functions are different: report the differences */
2502 		infstr = initinfstr();
2503 		formatinfstr(infstr, _("These instances exist only in cell %s:"),
2504 			describenodeproto(cell1));
2505 		found = FALSE;
2506 		for(i=0; i<cells1; i++)
2507 		{
2508 			/* get the index of the next cell instance in the list */
2509 			pc1 = celllist1[i];
2510 			if (pc1->numactual == 1) ni1 = (NODEINST *)pc1->actuallist; else
2511 				ni1 = ((NODEINST **)pc1->actuallist)[0];
2512 			thecell = ni1->proto;
2513 			cellnum = thecell->temp1 & NETCELLCODE;
2514 
2515 			/* advance to the end of the block of cells with the same instance number */
2516 			while (i < cells1-1)
2517 			{
2518 				pc1 = celllist1[i+1];
2519 				if (pc1->numactual == 1) ni1 = (NODEINST *)pc1->actuallist; else
2520 					ni1 = ((NODEINST **)pc1->actuallist)[0];
2521 				if (cellnum != (ni1->proto->temp1 & NETCELLCODE)) break;
2522 				i++;
2523 			}
2524 
2525 			/* make sure it exists in the other list */
2526 			for(j=0; j<cells2; j++)
2527 			{
2528 				pc2 = celllist2[j];
2529 				if (pc2->numactual == 1) ni2 = (NODEINST *)pc2->actuallist; else
2530 					ni2 = ((NODEINST **)pc2->actuallist)[0];
2531 				if (cellnum == (ni2->proto->temp1 & NETCELLCODE)) break;
2532 			}
2533 			if (j < cells2) continue;
2534 
2535 			formatinfstr(infstr, x_(" %s"), thecell->protoname);
2536 			found = TRUE;
2537 		}
2538 		pt = returninfstr(infstr);
2539 		if (found) addtostringarray(errorsa, pt);
2540 
2541 		infstr = initinfstr();
2542 		formatinfstr(infstr, _("These instances exist only in cell %s:"),
2543 			describenodeproto(cell2));
2544 		found = FALSE;
2545 		for(i=0; i<cells2; i++)
2546 		{
2547 			/* get the index of the next cell instance in the list */
2548 			pc2 = celllist2[i];
2549 			if (pc2->numactual == 1) ni2 = (NODEINST *)pc2->actuallist; else
2550 				ni2 = ((NODEINST **)pc2->actuallist)[0];
2551 			thecell = ni2->proto;
2552 			cellnum = thecell->temp1 & NETCELLCODE;
2553 
2554 			/* advance to the end of the block of cells with the same instance number */
2555 			while (i < cells2-1)
2556 			{
2557 				pc2 = celllist2[i+1];
2558 				if (pc2->numactual == 1) ni2 = (NODEINST *)pc2->actuallist; else
2559 					ni2 = ((NODEINST **)pc2->actuallist)[0];
2560 				if (cellnum != (ni2->proto->temp1 & NETCELLCODE)) break;
2561 				i++;
2562 			}
2563 
2564 			/* make sure it exists in the other list */
2565 			for(j=0; j<cells1; j++)
2566 			{
2567 				pc1 = celllist1[j];
2568 				if (pc1->numactual == 1) ni1 = (NODEINST *)pc1->actuallist; else
2569 					ni1 = ((NODEINST **)pc1->actuallist)[0];
2570 				if (cellnum == (ni1->proto->temp1 & NETCELLCODE)) break;
2571 			}
2572 			if (j < cells1) continue;
2573 
2574 			formatinfstr(infstr, x_(" %s"), thecell->protoname);
2575 			found = TRUE;
2576 		}
2577 		pt = returninfstr(infstr);
2578 		if (found) addtostringarray(errorsa, pt);
2579 	}
2580 	efree((CHAR *)celllist1);
2581 	efree((CHAR *)celllist2);
2582 }
2583 
2584 /*
2585  * Routine to handle a missing network on an unexpanded icon in NCC.
2586  * Cell "cell" has network "net", but "cellwithout" has no corresponding network.
2587  * The routine reports the problem in the string array "errorsa".
2588  *
2589  * A special case is handled.  If:
2590  *     "cellwithout" is an icon
2591  *     the network is of type power or ground
2592  *     there is a global network of that type in the flattened circuit
2593  * then:
2594  *     create a port on "cell" of that type
2595  *     connect it to that global net.
2596  *     To do this, modify the PCOMPs in entries "start" to "end" of "celllist".
2597  * If power and ground are being ignored, "ignorepwrgnd" is TRUE.
2598  * The list of PNETs in cell "cell" is in "pnetlist".
2599  */
net_foundmismatch(NODEPROTO * cell,NETWORK * net,NODEPROTO * cellwithout,PCOMP ** celllist,INTBIG start,INTBIG end,PNET * pnetlist,BOOLEAN ignorepwrgnd,void * errorsa)2600 void net_foundmismatch(NODEPROTO *cell, NETWORK *net, NODEPROTO *cellwithout, PCOMP **celllist,
2601 	INTBIG start, INTBIG end, PNET *pnetlist, BOOLEAN ignorepwrgnd, void *errorsa)
2602 {
2603 	REGISTER void *infstr;
2604 	REGISTER PNET *pn, **newnetnumbers;
2605 	REGISTER PCOMP *pc, **newnodelist;
2606 	REGISTER PORTPROTO **newportlist, *pp;
2607 	REGISTER INTBIG i, j, wantedflag, newnodecount, *newnodewire;
2608 	REGISTER INTSML *newportindices, *newstate, newwirecount;
2609 	REGISTER BOOLEAN fixed;
2610 
2611 	fixed = FALSE;
2612 
2613 	/* special case for icons with missing connections (and not ignoring power and ground) */
2614 	if (cellwithout->cellview == el_iconview && !ignorepwrgnd)
2615 	{
2616 		/* see if the missing connection is power or ground */
2617 		wantedflag = 0;
2618 		if (net->globalnet == GLOBALNETPOWER) wantedflag = POWERNET; else
2619 			if (net->globalnet == GLOBALNETGROUND) wantedflag = GROUNDNET; else
2620 		{
2621 			pc = celllist[start];
2622 			for(j=0; j<pc->wirecount; j++)
2623 			{
2624 				pn = pc->netnumbers[j];
2625 				if (pn->network == net) break;
2626 			}
2627 			if (j < pc->wirecount && (pn->flags&(POWERNET|GROUNDNET)) != 0)
2628 				wantedflag = pn->flags&(POWERNET|GROUNDNET);
2629 			if (wantedflag == 0 && net->portcount > 0)
2630 			{
2631 				for(pp = cell->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
2632 					if (pp->network == net) break;
2633 				if (pp != NOPORTPROTO)
2634 				{
2635 					if ((pp->userbits&STATEBITS) == PWRPORT) wantedflag = POWERNET; else
2636 						if ((pp->userbits&STATEBITS) == GNDPORT) wantedflag = GROUNDNET;
2637 				}
2638 			}
2639 		}
2640 		if (wantedflag != 0)
2641 		{
2642 			/* missing power or ground on an icon: see if such a signal is in the list */
2643 			for(pn = pnetlist; pn != NOPNET; pn = pn->nextpnet)
2644 				if ((pn->flags&(POWERNET|GROUNDNET)) == wantedflag) break;
2645 			if (pn != NOPNET)
2646 			{
2647 				/* signal found: add it to the components */
2648 				ttyputmsg(_("Note: added %s port to Cell %s"),
2649 					net_describepnet(pn), describenodeproto(cellwithout));
2650 				for(i=start; i<=end; i++)
2651 				{
2652 					/* add the PNET to the PCOMP */
2653 					pc = celllist[i];
2654 					newwirecount = pc->wirecount + 1;
2655 					newportindices = (INTSML *)emalloc(newwirecount * SIZEOFINTSML, net_tool->cluster);
2656 					newportlist = (PORTPROTO **)emalloc(newwirecount * sizeof (PORTPROTO *), net_tool->cluster);
2657 					newnetnumbers = (PNET **)emalloc(newwirecount * sizeof (PNET *), net_tool->cluster);
2658 					newstate = (INTSML *)emalloc(newwirecount * SIZEOFINTSML, net_tool->cluster);
2659 					if (newportindices == 0 || newportlist == 0 || newnetnumbers == 0 ||
2660 						newstate == 0) return;
2661 					for(j=0; j<pc->wirecount; j++)
2662 					{
2663 						newportindices[j] = pc->portindices[j];
2664 						newportlist[j] = pc->portlist[j];
2665 						newnetnumbers[j] = pc->netnumbers[j];
2666 						newstate[j] = pc->state[j];
2667 					}
2668 					newportindices[pc->wirecount] = (INTSML)net->temp2;
2669 					newportlist[pc->wirecount] = NOPORTPROTO;
2670 					newnetnumbers[pc->wirecount] = pn;
2671 					newstate[pc->wirecount] = 0;
2672 					if (pc->wirecount > 0)
2673 					{
2674 						efree((CHAR *)pc->portindices);
2675 						efree((CHAR *)pc->portlist);
2676 						efree((CHAR *)pc->netnumbers);
2677 						efree((CHAR *)pc->state);
2678 					}
2679 					pc->portindices = newportindices;
2680 					pc->portlist = newportlist;
2681 					pc->netnumbers = newnetnumbers;
2682 					pc->state = newstate;
2683 					pc->wirecount = newwirecount;
2684 					pc->truewirecount++;
2685 
2686 					/* add the PCOMP to the PNET */
2687 					newnodecount = pn->nodecount + 1;
2688 					newnodelist = (PCOMP **)emalloc(newnodecount * sizeof (PCOMP *), net_tool->cluster);
2689 					newnodewire = (INTBIG *)emalloc(newnodecount * SIZEOFINTBIG, net_tool->cluster);
2690 					if (newnodelist == 0 || newnodewire == 0) return;
2691 					for(j=0; j<pn->nodecount; j++)
2692 					{
2693 						newnodelist[j] = pn->nodelist[j];
2694 						newnodewire[j] = pn->nodewire[j];
2695 					}
2696 					newnodelist[pn->nodecount] = pc;
2697 					newnodewire[pn->nodecount] = pc->wirecount-1;
2698 					if (pn->nodecount > 0)
2699 					{
2700 						efree((CHAR *)pn->nodelist);
2701 						efree((CHAR *)pn->nodewire);
2702 					}
2703 					pn->nodelist = newnodelist;
2704 					pn->nodewire = newnodewire;
2705 					pn->nodecount = newnodecount;
2706 				}
2707 				fixed = TRUE;
2708 			}
2709 		}
2710 	}
2711 	if (!fixed)
2712 	{
2713 		/* can't fix it: report the error */
2714 		infstr = initinfstr();
2715 		formatinfstr(infstr, _("Cell %s has %s, which does not exist in cell %s"),
2716 			describenodeproto(cell), describenetwork(net), describenodeproto(cellwithout));
2717 		addtostringarray(errorsa, returninfstr(infstr));
2718 	}
2719 }
2720 
net_makeexternalnetlist(NODEINST * ni,INTBIG * size,BOOLEAN ignorepwrgnd)2721 NETWORK **net_makeexternalnetlist(NODEINST *ni, INTBIG *size, BOOLEAN ignorepwrgnd)
2722 {
2723 	REGISTER NODEPROTO *np;
2724 	REGISTER PORTPROTO *pp;
2725 	REGISTER NETWORK *net, *subnet, **list;
2726 	REGISTER INTBIG i, total;
2727 
2728 	np = contentsview(ni->proto);
2729 	if (np == NONODEPROTO) np = ni->proto;
2730 	total = 0;
2731 	for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
2732 	{
2733 		if (ignorepwrgnd)
2734 		{
2735 			if (portispower(pp) || portisground(pp)) continue;
2736 		}
2737 		net = pp->network;
2738 		if (net->buswidth > 1)
2739 		{
2740 			for(i=0; i<net->buswidth; i++) total++;
2741 		} else total++;
2742 	}
2743 	*size = total;
2744 	if (total > 0)
2745 	{
2746 		list = (NETWORK **)emalloc(total * (sizeof (NETWORK *)), el_tempcluster);
2747 		total = 0;
2748 		for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
2749 		{
2750 			if (ignorepwrgnd)
2751 			{
2752 				if (portispower(pp) || portisground(pp)) continue;
2753 			}
2754 			net = pp->network;
2755 			if (net->buswidth > 1)
2756 			{
2757 				for(i=0; i<net->buswidth; i++)
2758 				{
2759 					subnet = net->networklist[i];
2760 					list[total++] = subnet;
2761 				}
2762 			} else
2763 			{
2764 				list[total++] = net;
2765 			}
2766 		}
2767 	}
2768 	return(list);
2769 }
2770 
net_sortexportcodes(const void * e1,const void * e2)2771 int net_sortexportcodes(const void *e1, const void *e2)
2772 {
2773 	REGISTER NETWORK *pp1, *pp2;
2774 
2775 	pp1 = *((NETWORK **)e1);
2776 	pp2 = *((NETWORK **)e2);
2777 	return(pp1->temp2 - pp2->temp2);
2778 }
2779 
net_sortbycelltype(const void * n1,const void * n2)2780 int net_sortbycelltype(const void *n1, const void *n2)
2781 {
2782 	REGISTER PCOMP *pc1, *pc2;
2783 	REGISTER NODEINST *ni1, *ni2;
2784 	REGISTER INTBIG diff;
2785 
2786 	pc1 = *((PCOMP **)n1);
2787 	pc2 = *((PCOMP **)n2);
2788 	if (pc1->numactual == 1) ni1 = (NODEINST *)pc1->actuallist; else
2789 		ni1 = ((NODEINST **)pc1->actuallist)[0];
2790 	if (pc2->numactual == 1) ni2 = (NODEINST *)pc2->actuallist; else
2791 		ni2 = ((NODEINST **)pc2->actuallist)[0];
2792 	diff = (ni1->proto->temp1 & NETCELLCODE) - (ni2->proto->temp1 & NETCELLCODE);
2793 	if (diff != 0) return(diff);
2794 	return(pc1->truewirecount - pc2->truewirecount);
2795 }
2796 
net_sortbypnet(const void * n1,const void * n2)2797 int net_sortbypnet(const void *n1, const void *n2)
2798 {
2799 	REGISTER PNET *pn1, *pn2;
2800 
2801 	pn1 = *((PNET **)n1);
2802 	pn2 = *((PNET **)n2);
2803 	return(namesame(net_describepnet(pn1), net_describepnet(pn2)));
2804 }
2805 
2806 /*
2807  * Routine to remove extraneous information.  It removes busses from the
2808  * networks and it removes SPICE parts from the components.
2809  */
net_removeextraneous(PCOMP ** pcomplist,PNET ** pnetlist,INTBIG * comp)2810 void net_removeextraneous(PCOMP **pcomplist, PNET **pnetlist, INTBIG *comp)
2811 {
2812 	REGISTER PNET *pn, *lastpn, *nextpn;
2813 	REGISTER PCOMP *pc, *lastpc, *nextpc;
2814 	REGISTER NETWORK *net;
2815 
2816 	/* initialize all networks */
2817 	lastpn = NOPNET;
2818 	for(pn = *pnetlist; pn != NOPNET; pn = nextpn)
2819 	{
2820 		nextpn = pn->nextpnet;
2821 		net = pn->network;
2822 
2823 		/* remove networks that refer to busses (individual signals are compared) */
2824 		if (net != NONETWORK && net->buswidth > 1)
2825 		{
2826 			if (lastpn == NOPNET)
2827 			{
2828 				*pnetlist = pn->nextpnet;
2829 			} else
2830 			{
2831 				lastpn->nextpnet = pn->nextpnet;
2832 			}
2833 			net_freepnet(pn);
2834 			continue;
2835 		}
2836 		lastpn = pn;
2837 	}
2838 
2839 	lastpc = NOPCOMP;
2840 	for(pc = *pcomplist; pc != NOPCOMP; pc = nextpc)
2841 	{
2842 		nextpc = pc->nextpcomp;
2843 
2844 		/* remove components that relate to SPICE simulation */
2845 		if (net_isspice(pc))
2846 		{
2847 			if (lastpc == NOPCOMP)
2848 			{
2849 				*pcomplist = pc->nextpcomp;
2850 			} else
2851 			{
2852 				lastpc->nextpcomp = pc->nextpcomp;
2853 			}
2854 			net_freepcomp(pc);
2855 			(*comp)--;
2856 			continue;
2857 		}
2858 		lastpc = pc;
2859 	}
2860 }
2861 
2862 #if 0
2863 /*
2864  * Routine to check for duplicate names in the netlist, which may cause problems later.
2865  */
2866 void net_checkforduplicatenames(PNET *pnetlist)
2867 {
2868 	REGISTER PNET *pn, **pnlist, *lastpn;
2869 	REGISTER INTBIG total, i;
2870 	REGISTER NETWORK *net, *lastnet;
2871 
2872 	/* see how many PNETs there are */
2873 	total = 0;
2874 	for(pn = pnetlist; pn != NOPNET; pn = pn->nextpnet)
2875 	{
2876 		net = pn->network;
2877 		if (net == NONETWORK || net->namecount == 0) continue;
2878 		total++;
2879 	}
2880 	if (total == 0) return;
2881 
2882 	/* make a list of them */
2883 	pnlist = (PNET **)emalloc(total * (sizeof (PNET *)), net_tool->cluster);
2884 	if (pnlist == 0) return;
2885 	i = 0;
2886 	for(pn = pnetlist; pn != NOPNET; pn = pn->nextpnet)
2887 	{
2888 		net = pn->network;
2889 		if (net == NONETWORK || net->namecount == 0) continue;
2890 		pnlist[i++] = pn;
2891 	}
2892 
2893 	/* sort by name within parent */
2894 	esort(pnlist, total, sizeof (PNET *), net_sortpnetlist);
2895 
2896 	/* now look for duplicates */
2897 	for(i=1; i<total; i++)
2898 	{
2899 		lastpn = pnlist[i-1];
2900 		lastnet = lastpn->network;
2901 		pn = pnlist[i];
2902 		net = pn->network;
2903 		if (lastnet == net) continue;
2904 		if (net->parent != lastnet->parent) continue;
2905 		if (namesame(networkname(net, 0), networkname(lastnet, 0)) != 0) continue;
2906 		ttyputmsg(_("Warning: cell %s has multiple networks named %s"),
2907 			describenodeproto(net->parent), networkname(net, 0));
2908 	}
2909 	efree((CHAR *)pnlist);
2910 }
2911 
2912 int net_sortpnetlist(const void *n1, const void *n2)
2913 {
2914 	REGISTER PNET *pn1, *pn2;
2915 	REGISTER NETWORK *net1, *net2;
2916 
2917 	pn1 = *((PNET **)n1);
2918 	pn2 = *((PNET **)n2);
2919 	net1 = pn1->network;
2920 	net2 = pn2->network;
2921 	if (net1->parent != net2->parent) return((int)(net1->parent - net2->parent));
2922 	return(namesame(networkname(net1, 0), networkname(net2, 0)));
2923 }
2924 #endif
2925 
2926 /******************************** RESULTS ANALYSIS ********************************/
2927 
2928 #ifdef NEWNCC
2929 typedef struct
2930 {
2931 	PORTPROTO *pp;
2932 	SYMGROUP *sg;
2933 } EXPORTSYMGROUP;
2934 
2935 int net_sortexportsymgroup(const void *e1, const void *e2);
2936 
net_sortexportsymgroup(const void * e1,const void * e2)2937 int net_sortexportsymgroup(const void *e1, const void *e2)
2938 {
2939 	REGISTER EXPORTSYMGROUP *es1, *es2;
2940 
2941 	es1 = (EXPORTSYMGROUP *)e1;
2942 	es2 = (EXPORTSYMGROUP *)e2;
2943 	return(namesame(es1->pp->protoname, es2->pp->protoname));
2944 }
2945 
2946 #endif
2947 
2948 /*
2949  * Routine to look at the symmetry groups and return the number of hard errors and the number of
2950  * soft errors in "harderrors", and "softerrors".  If "reporterrors" is nonzero, these errors are
2951  * logged for perusal by the user.  If "checksize" is true, check sizes.  If "checkexportnames" is
2952  * true, check export names.  If "ignorepwrgnd" is true, ignore power and ground nets.  The total
2953  * number of errors is put in "errorcount".
2954  */
net_analyzesymmetrygroups(BOOLEAN reporterrors,BOOLEAN checksize,BOOLEAN checkexportnames,BOOLEAN ignorepwrgnd,INTBIG * errorcount)2955 INTBIG net_analyzesymmetrygroups(BOOLEAN reporterrors, BOOLEAN checksize, BOOLEAN checkexportnames,
2956 	BOOLEAN ignorepwrgnd, INTBIG *errorcount)
2957 {
2958 	REGISTER INTBIG f, i, j, errors, pctdiff1, pctdiff2, pctdiff, worstpctdiff, i1, i2;
2959 	float diff, largest;
2960 	BOOLEAN valid, exportcomparison;
2961 	REGISTER SYMGROUP *sg, *osg, *amblist, *unasslist;
2962 	REGISTER PCOMP *pc1, *pc2;
2963 	REGISTER void *err;
2964 	REGISTER CHAR *net1name, *pt1, *pt2;
2965 	CHAR size1[200], size2[200];
2966 	REGISTER PNET *pn, *pn1, *pn2, *opn;
2967 	PNET **pnlist[2];
2968 	REGISTER PORTPROTO *pp1, *pp2;
2969 	REGISTER NODEPROTO *par1, *par2;
2970 	REGISTER ARCINST *ai;
2971 	REGISTER NETWORK *net;
2972 	INTBIG pnlisttotal[2];
2973 	REGISTER void *infstr;
2974 
2975 	errors = 0;
2976 	*errorcount = 0;
2977 	worstpctdiff = 0;
2978 	amblist = unasslist = NOSYMGROUP;
2979 
2980 	/* now evaluate the differences */
2981 	for(sg = net_firstsymgroup; sg != NOSYMGROUP; sg = sg->nextsymgroup)
2982 	{
2983 		/* if there is nothing in the group, ignore it */
2984 		if (sg->cellcount[0] == 0 && sg->cellcount[1] == 0) continue;
2985 
2986 		/* if the group is a good match, make optional checks */
2987 		if (sg->cellcount[0] == 1 && sg->cellcount[1] == 1)
2988 		{
2989 			if (checksize && sg->grouptype == SYMGROUPCOMP)
2990 			{
2991 				/* see if sizes match */
2992 				pc1 = (PCOMP *)sg->celllist[0][0];
2993 				pc2 = (PCOMP *)sg->celllist[1][0];
2994 				if ((pc1->flags&COMPHASWIDLEN) != 0)
2995 				{
2996 					if (!net_componentequalvalue(pc1->width, pc2->width) ||
2997 						!net_componentequalvalue(pc1->length, pc2->length))
2998 					{
2999 						if (reporterrors)
3000 						{
3001 							diff = (float)fabs(pc1->width - pc2->width);
3002 							if (pc1->width > pc2->width) largest = pc1->width; else largest = pc2->width;
3003 							pctdiff1 = roundfloat(diff * 100.0f / largest);
3004 							diff = (float)fabs(pc1->length - pc2->length);
3005 							if (pc1->length > pc2->length) largest = pc1->length; else largest = pc2->length;
3006 							pctdiff2 = roundfloat(diff * 100.0f / largest);
3007 							pctdiff = maxi(pctdiff1, pctdiff2);
3008 							if (pctdiff > worstpctdiff) worstpctdiff = pctdiff;
3009 							esnprintf(size1, 200, x_("%s/%s"), frtoa(roundfloat(pc1->width)), frtoa(roundfloat(pc1->length)));
3010 							esnprintf(size2, 200, x_("%s/%s"), frtoa(roundfloat(pc2->width)), frtoa(roundfloat(pc2->length)));
3011 							net_reportsizeerror(pc1, size1, pc2, size2, pctdiff, sg);
3012 							(*errorcount)++;
3013 						}
3014 						errors |= SIZEERRORS;
3015 					}
3016 				} else if ((pc1->flags&COMPHASAREA) != 0)
3017 				{
3018 					if (!net_componentequalvalue(pc1->length, pc2->length))
3019 					{
3020 						if (reporterrors)
3021 						{
3022 							diff = (float)fabs(pc1->length - pc2->length);
3023 							if (pc1->length > pc2->length) largest = pc1->length; else largest = pc2->length;
3024 							pctdiff = roundfloat(diff * 100.0f / largest);
3025 							if (pctdiff > worstpctdiff) worstpctdiff = pctdiff;
3026 							if (pc1->function == NPRESIST)
3027 							{
3028 								/* not area: show the values as they are */
3029 								esnprintf(size1, 200, x_("%g"), pc1->length);
3030 								esnprintf(size2, 200, x_("%g"), pc2->length);
3031 								net_reportsizeerror(pc1, size1, pc2, size2, pctdiff, sg);
3032 							} else
3033 							{
3034 								/* area: make the values sensible */
3035 								net_reportsizeerror(pc1, frtoa(roundfloat(pc1->length)), pc2,
3036 									frtoa(roundfloat(pc2->length)), pctdiff, sg);
3037 							}
3038 							(*errorcount)++;
3039 						}
3040 						errors |= SIZEERRORS;
3041 					}
3042 				}
3043 			}
3044 			if (sg->grouptype == SYMGROUPNET)
3045 			{
3046 				/* see if names match */
3047 				pn1 = (PNET *)sg->celllist[0][0];
3048 				pn2 = (PNET *)sg->celllist[1][0];
3049 
3050 				/* ignore name match for power and ground nets */
3051 				if ((pn1->flags&(POWERNET|GROUNDNET)) != 0 || (pn2->flags&(POWERNET|GROUNDNET)) != 0)
3052 				{
3053 					if ((pn1->flags&(POWERNET|GROUNDNET)) != (pn2->flags&(POWERNET|GROUNDNET)))
3054 					{
3055 						if (reporterrors)
3056 						{
3057 							infstr = initinfstr();
3058 							formatinfstr(infstr, _("Network '%s' in cell %s is on different power/ground than network '%s' in cell %s"),
3059 								net_describepnet(pn1), describenodeproto(net_cell[0]),
3060 								net_describepnet(pn2), describenodeproto(net_cell[1]));
3061 							err = logerror(returninfstr(infstr), NONODEPROTO, 0);
3062 							net_addsymgrouptoerror(err, sg);
3063 							(*errorcount)++;
3064 						}
3065 						errors |= EXPORTERRORS;
3066 					}
3067 					continue;
3068 				}
3069 
3070 				if ((pn1->flags&EXPORTEDNET) != 0 &&
3071 					(pn1->realportcount > 0 || (pn1->network != NONETWORK && pn1->network->namecount > 0)))
3072 				{
3073 					if ((pn2->flags&EXPORTEDNET) == 0)
3074 					{
3075 						if (checkexportnames)
3076 						{
3077 							/* net in cell 1 is exported, but net in cell 2 isn't */
3078 							if (reporterrors)
3079 							{
3080 								infstr = initinfstr();
3081 								formatinfstr(infstr, _("Network in cell %s is '%s' but network in cell %s is not exported"),
3082 									describenodeproto(net_cell[0]), net_describepnet(pn1), describenodeproto(net_cell[1]));
3083 								err = logerror(returninfstr(infstr), NONODEPROTO, 0);
3084 								net_addsymgrouptoerror(err, sg);
3085 								(*errorcount)++;
3086 							}
3087 							errors |= EXPORTERRORS;
3088 						}
3089 					} else
3090 					{
3091 						/* both networks exported: check names */
3092 						if (checkexportnames)
3093 						{
3094 							exportcomparison = net_sameexportnames(pn1, pn2);
3095 							if (!exportcomparison)
3096 							{
3097 								if (reporterrors)
3098 								{
3099 									infstr = initinfstr();
3100 									addstringtoinfstr(infstr, net_describepnet(pn1));
3101 									net1name = returninfstr(infstr);
3102 									infstr = initinfstr();
3103 									par1 = pn1->network->parent;
3104 									par2 = pn2->network->parent;
3105 									if ((par1 == net_cell[0] && par2 == net_cell[1]) ||
3106 										(par1 == net_cell[1] && par2 == net_cell[0]) ||
3107 										insamecellgrp(par1, par2))
3108 									{
3109 										formatinfstr(infstr, _("Export names '%s:%s' and '%s:%s' do not match"),
3110 											describenodeproto(par1), net1name,
3111 												describenodeproto(par2), net_describepnet(pn2));
3112 									} else
3113 									{
3114 										formatinfstr(infstr, _("Export names '%s:%s' and '%s:%s' are not at the same level of hierarchy"),
3115 											describenodeproto(par1), net1name,
3116 												describenodeproto(par2), net_describepnet(pn2));
3117 									}
3118 									err = logerror(returninfstr(infstr), NONODEPROTO, 0);
3119 									net_addsymgrouptoerror(err, sg);
3120 									(*errorcount)++;
3121 								}
3122 								errors |= EXPORTERRORS;
3123 							}
3124 						}
3125 
3126 						/* check that the export characteristics match */
3127 						if (pn2->realportcount > 0)
3128 						{
3129 							pp1 = NOPORTPROTO;
3130 							for(i=0; i<pn1->realportcount; i++)
3131 							{
3132 								if (pn1->realportcount == 1) pp1 = (PORTPROTO *)pn1->realportlist; else
3133 									pp1 = ((PORTPROTO **)pn1->realportlist)[i];
3134 								for(j=0; j<pn2->realportcount; j++)
3135 								{
3136 									if (pn2->realportcount == 1) pp2 = (PORTPROTO *)pn2->realportlist; else
3137 										pp2 = ((PORTPROTO **)pn2->realportlist)[j];
3138 									if ((pp1->userbits&STATEBITS) == (pp2->userbits&STATEBITS)) break;
3139 								}
3140 								if (j < pn2->realportcount) break;
3141 							}
3142 							if (i >= pn1->realportcount)
3143 							{
3144 								if (reporterrors)
3145 								{
3146 									if (pn2->realportcount == 1) pp2 = (PORTPROTO *)pn2->realportlist; else
3147 										pp2 = ((PORTPROTO **)pn2->realportlist)[0];
3148 									infstr = initinfstr();
3149 									formatinfstr(infstr, _("Exports have different characteristics (%s:%s is %s and %s:%s is %s)"),
3150 										describenodeproto(net_cell[0]), pp1->protoname, describeportbits(pp1->userbits),
3151 										describenodeproto(net_cell[1]), pp2->protoname, describeportbits(pp2->userbits));
3152 									err = logerror(returninfstr(infstr), NONODEPROTO, 0);
3153 									net_addsymgrouptoerror(err, sg);
3154 									(*errorcount)++;
3155 								}
3156 								errors |= EXPORTERRORS;
3157 							}
3158 						}
3159 					}
3160 				} else
3161 				{
3162 					if ((pn2->flags&EXPORTEDNET) != 0 &&
3163 						(pn2->realportcount > 0 || (pn2->network != NONETWORK && pn2->network->namecount > 0)))
3164 					{
3165 						if (checkexportnames)
3166 						{
3167 							/* net in cell 2 is exported, but net in cell 1 isn't */
3168 							if (reporterrors)
3169 							{
3170 								infstr = initinfstr();
3171 								formatinfstr(infstr, _("Network in cell %s is '%s' but network in cell %s is not exported"),
3172 									describenodeproto(net_cell[1]), net_describepnet(pn2), describenodeproto(net_cell[0]));
3173 								err = logerror(returninfstr(infstr), NONODEPROTO, 0);
3174 								net_addsymgrouptoerror(err, sg);
3175 								(*errorcount)++;
3176 							}
3177 							errors |= EXPORTERRORS;
3178 						}
3179 					}
3180 				}
3181 			}
3182 			continue;
3183 		}
3184 
3185 		if (sg->cellcount[0] <= 0 || sg->cellcount[1] <= 0 || sg->hashvalue == 0)
3186 		{
3187 			if (sg->grouptype == SYMGROUPNET)
3188 			{
3189 				/* network group: ignore if no real associated networks */
3190 				valid = FALSE;
3191 				pn = NOPNET;
3192 				for(f=0; f<2; f++)
3193 				{
3194 					for(i=0; i<sg->cellcount[f]; i++)
3195 					{
3196 						pn = (PNET *)sg->celllist[f][i];
3197 						if (pn->network != NONETWORK) valid = TRUE;
3198 					}
3199 				}
3200 				if (!valid) continue;
3201 
3202 				/* network group: ignore if a bus */
3203 				for(f=0; f<2; f++)
3204 				{
3205 					for(i=0; i<sg->cellcount[f]; i++)
3206 					{
3207 						pn = (PNET *)sg->celllist[f][i];
3208 						net = pn->network;
3209 						if (net == NONETWORK) continue;
3210 						if (net->buswidth > 1) break;
3211 					}
3212 					if (i < sg->cellcount[f]) break;
3213 				}
3214 				if (f < 2) continue;
3215 
3216 				/* network group: ignore if all power and ground that is being ignored */
3217 				if (ignorepwrgnd)
3218 				{
3219 					valid = FALSE;
3220 					for(f=0; f<2; f++)
3221 					{
3222 						for(i=0; i<sg->cellcount[f]; i++)
3223 						{
3224 							pn = (PNET *)sg->celllist[f][i];
3225 							if ((pn->flags&(POWERNET|GROUNDNET)) == 0)
3226 								valid = TRUE;
3227 						}
3228 					}
3229 					if (!valid) continue;
3230 				}
3231 
3232 				/* network group: ignore if no components are on it (but warn) */
3233 				if (sg->cellcount[0] == 0 || sg->cellcount[1] == 0)
3234 				{
3235 					for(f=0; f<2; f++)
3236 					{
3237 						if (sg->cellcount[f] == 0) continue;
3238 						for(i=0; i<sg->cellcount[f]; i++)
3239 						{
3240 							pn = (PNET *)sg->celllist[f][i];
3241 							if (pn->nodecount != 0) break;
3242 						}
3243 						if (i < sg->cellcount[f]) continue;
3244 						if (reporterrors)
3245 						{
3246 							infstr = initinfstr();
3247 							formatinfstr(infstr, _("Network %s in cell %s is unused"),
3248 								net_describepnet(pn), describenodeproto(net_cell[f]));
3249 							err = logerror(returninfstr(infstr), NONODEPROTO, 0);
3250 							net_addsymgrouptoerror(err, sg);
3251 							(*errorcount)++;
3252 						}
3253 						errors |= EXPORTERRORS;
3254 						break;
3255 					}
3256 					if (f < 2) continue;
3257 				}
3258 			}
3259 
3260 			/* add to the list of unassociated groups */
3261 			sg->nexterrsymgroup = unasslist;
3262 			unasslist = sg;
3263 		} else
3264 		{
3265 			/* add to the list of ambiguous groups */
3266 			sg->nexterrsymgroup = amblist;
3267 			amblist = sg;
3268 		}
3269 	}
3270 
3271 #ifdef NEWNCC
3272 	/* make more thorough check if checking export names */
3273 	if (checkexportnames)
3274 	{
3275 		REGISTER INTBIG c1, c2, i1, i2, chdiff;
3276 		REGISTER PORTPROTO *pp;
3277 		EXPORTSYMGROUP *es1, *es2;
3278 
3279 		c1 = c2 = 0;
3280 		for(sg = net_firstsymgroup; sg != NOSYMGROUP; sg = sg->nextsymgroup)
3281 		{
3282 			/* only interested in matched network groups */
3283 			if (sg->grouptype != SYMGROUPNET) continue;
3284 			if (sg->cellcount[0] != 1 || sg->cellcount[1] != 1) continue;
3285 			pn1 = (PNET *)sg->celllist[0][0];
3286 			if ((pn1->flags&EXPORTEDNET) != 0)
3287 			{
3288 				for(i=0; i<pn1->realportcount; i++)
3289 				{
3290 					if (pn1->realportcount == 1) pp = (PORTPROTO *)pn1->realportlist; else
3291 						pp = ((PORTPROTO **)pn1->realportlist)[i];
3292 					if (pp->network->buswidth <= 1) c1++;
3293 				}
3294 			}
3295 			pn2 = (PNET *)sg->celllist[1][0];
3296 			if ((pn2->flags&EXPORTEDNET) != 0)
3297 			{
3298 				for(i=0; i<pn2->realportcount; i++)
3299 				{
3300 					if (pn2->realportcount == 1) pp = (PORTPROTO *)pn2->realportlist; else
3301 						pp = ((PORTPROTO **)pn2->realportlist)[i];
3302 					if (pp->network->buswidth <= 1) c2++;
3303 				}
3304 			}
3305 		}
3306 
3307 		if (c1 != 0 && c2 != 0)
3308 		{
3309 			/* build array of export names */
3310 			es1 = (EXPORTSYMGROUP *)emalloc(c1 * (sizeof (EXPORTSYMGROUP)), net_tool->cluster);
3311 			if (es1 == 0) return(0);
3312 			es2 = (EXPORTSYMGROUP *)emalloc(c2 * (sizeof (EXPORTSYMGROUP)), net_tool->cluster);
3313 			if (es1 == 0) return(0);
3314 
3315 			/* load the export lists */
3316 			c1 = c2 = 0;
3317 			for(sg = net_firstsymgroup; sg != NOSYMGROUP; sg = sg->nextsymgroup)
3318 			{
3319 				/* only interested in matched network groups */
3320 				if (sg->grouptype != SYMGROUPNET) continue;
3321 				if (sg->cellcount[0] != 1 || sg->cellcount[1] != 1) continue;
3322 				pn1 = (PNET *)sg->celllist[0][0];
3323 				if ((pn1->flags&EXPORTEDNET) != 0)
3324 				{
3325 					for(i=0; i<pn1->realportcount; i++)
3326 					{
3327 						if (pn1->realportcount == 1) pp = (PORTPROTO *)pn1->realportlist; else
3328 							pp = ((PORTPROTO **)pn1->realportlist)[i];
3329 						if (pp->network->buswidth > 1) continue;
3330 						es1[c1].pp = pp;
3331 						es1[c1].sg = sg;
3332 						c1++;
3333 					}
3334 				}
3335 				pn2 = (PNET *)sg->celllist[1][0];
3336 				if ((pn2->flags&EXPORTEDNET) != 0)
3337 				{
3338 					for(i=0; i<pn2->realportcount; i++)
3339 					{
3340 						if (pn2->realportcount == 1) pp = (PORTPROTO *)pn2->realportlist; else
3341 							pp = ((PORTPROTO **)pn2->realportlist)[i];
3342 						if (pp->network->buswidth > 1) continue;
3343 						es2[c2].pp = pp;
3344 						es2[c2].sg = sg;
3345 						c2++;
3346 					}
3347 				}
3348 			}
3349 
3350 			/* analyze */
3351 			esort(es1, c1, sizeof (EXPORTSYMGROUP), net_sortexportsymgroup);
3352 			esort(es2, c2, sizeof (EXPORTSYMGROUP), net_sortexportsymgroup);
3353 
3354 			i1 = i2 = 0;
3355 			for(;;)
3356 			{
3357 				if (i1 >= c1 || i2 >= c2) break;
3358 				chdiff = namesame(es1[i1].pp->protoname, es2[i2].pp->protoname);
3359 				if (chdiff == 0)
3360 				{
3361 					/* same name: make sure symgroups are the same */
3362 					if (es1[i1].sg != es2[i2].sg)
3363 					{
3364 						if (reporterrors)
3365 						{
3366 							infstr = initinfstr();
3367 							formatinfstr(infstr, _("Export %s not wired consistently"),
3368 								es1[i1].pp->protoname);
3369 							err = logerror(returninfstr(infstr), NONODEPROTO, 0);
3370 							addexporttoerror(err, es1[i1].pp, TRUE);
3371 							addexporttoerror(err, es2[i2].pp, TRUE);
3372 							(*errorcount)++;
3373 						}
3374 						errors |= EXPORTERRORS;
3375 					}
3376 					i1++;
3377 					i2++;
3378 					continue;
3379 				}
3380 				if (chdiff < 0) i1++; else
3381 					i2++;
3382 			}
3383 			efree((CHAR *)es1);
3384 			efree((CHAR *)es2);
3385 		}
3386 	}
3387 #endif
3388 
3389 	if (unasslist != NOSYMGROUP || amblist != NOSYMGROUP)
3390 	{
3391 		if (reporterrors)
3392 		{
3393 			if (unasslist != NOSYMGROUP)
3394 				*errorcount += net_reporterror(unasslist, _("Unassociated"), ignorepwrgnd);
3395 			if (amblist != NOSYMGROUP)
3396 				*errorcount += net_reporterror(amblist, _("Ambiguous"), ignorepwrgnd);
3397 		}
3398 		errors |= STRUCTUREERRORS;
3399 	}
3400 
3401 	/* if reporting errors, look for groups with missing parts */
3402 	if (reporterrors)
3403 	{
3404 		pnlisttotal[0] = pnlisttotal[1] = 0;
3405 		for(sg = net_firstsymgroup; sg != NOSYMGROUP; sg = sg->nextsymgroup)
3406 		{
3407 			if (sg->cellcount[0] <= 0 || sg->cellcount[1] <= 0) continue;
3408 			if (sg->grouptype != SYMGROUPNET) continue;
3409 			if (sg->cellcount[0] == 1 && sg->cellcount[1] == 1) continue;
3410 
3411 			/* first make a list of the networks in both cells */
3412 			for(f=0; f<2; f++)
3413 			{
3414 				if (sg->cellcount[f] > pnlisttotal[f])
3415 				{
3416 					if (pnlisttotal[f] > 0) efree((CHAR *)pnlist[f]);
3417 					pnlist[f] = (PNET **)emalloc(sg->cellcount[f] * (sizeof (PNET *)), el_tempcluster);
3418 					if (pnlist[f] == 0) return(errors);
3419 					pnlisttotal[f] = sg->cellcount[f];
3420 				}
3421 				for(i=0; i<sg->cellcount[f]; i++)
3422 					pnlist[f][i] = (PNET *)sg->celllist[f][i];
3423 			}
3424 
3425 			/* sort the names and look for matches */
3426 			esort(pnlist[0], sg->cellcount[0], sizeof (PNET *), net_sortbypnet);
3427 			esort(pnlist[1], sg->cellcount[1], sizeof (PNET *), net_sortbypnet);
3428 			i1 = i2 = 0;
3429 			for(;;)
3430 			{
3431 				if (i1 >= sg->cellcount[0] || i2 >= sg->cellcount[1]) break;
3432 				pn1 = pnlist[0][i1];
3433 				pn2 = pnlist[1][i2];
3434 				if (pn1 == NOPNET || pn2 == NOPNET) break;
3435 				pt1 = net_describepnet(pn1);
3436 				pt2 = net_describepnet(pn2);
3437 				i = namesame(pt1, pt2);
3438 				if (i < 0)
3439 				{
3440 					i1++;
3441 					continue;
3442 				}
3443 				if (i > 0)
3444 				{
3445 					i2++;
3446 					continue;
3447 				}
3448 				pnlist[0][i1] = NOPNET;
3449 				while (i1+1 < sg->cellcount[0])
3450 				{
3451 					pn = pnlist[0][i1+1];
3452 					if (pn == NOPNET) break;
3453 					if (namesame(net_describepnet(pn1), net_describepnet(pn)) != 0) break;
3454 					i1++;
3455 					pnlist[0][i1] = NOPNET;
3456 				}
3457 				pnlist[1][i2] = NOPNET;
3458 				while (i2+1 < sg->cellcount[1])
3459 				{
3460 					pn = pnlist[1][i2+1];
3461 					if (pn == NOPNET) break;
3462 					if (namesame(net_describepnet(pn2), net_describepnet(pn)) != 0) break;
3463 					i2++;
3464 					pnlist[1][i2] = NOPNET;
3465 				}
3466 			}
3467 
3468 			/* see if one entire side is eliminated */
3469 			for(f=0; f<2; f++)
3470 			{
3471 				for(i=0; i<sg->cellcount[f]; i++)
3472 					if (pnlist[f][i] != NOPNET) break;
3473 				if (i >= sg->cellcount[f]) break;
3474 			}
3475 			if (f < 2)
3476 			{
3477 				/* side "f" is eliminated: find name matches to those on side "1-f" */
3478 				for(j=0; j<sg->cellcount[1-f]; j++)
3479 				{
3480 					opn = (PNET *)sg->celllist[1-f][j];
3481 					if (opn == NOPNET) continue;
3482 					if (opn->network == NONETWORK) continue;
3483 					for(osg = net_firstsymgroup; osg != NOSYMGROUP; osg = osg->nextsymgroup)
3484 					{
3485 						if (osg->grouptype != SYMGROUPNET) continue;
3486 						if (osg == sg) continue;
3487 						for(i=0; i<osg->cellcount[f]; i++)
3488 						{
3489 							pn = (PNET *)osg->celllist[f][i];
3490 							if (opn->nodecount == pn->nodecount) continue;
3491 							if (pn->network == NONETWORK) continue;
3492 							pt1 = net_describepnet(opn);
3493 							pt2 = net_describepnet(pn);
3494 							if (namesame(pt1, pt2) == 0)
3495 							{
3496 								/* found it! */
3497 								infstr = initinfstr();
3498 								formatinfstr(infstr, _("Networks %s may be wired differently"),
3499 									net_describepnet(pn));
3500 								err = logerror(returninfstr(infstr), NONODEPROTO, 0);
3501 								for(ai = pn->network->parent->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
3502 									if (ai->network == pn->network)
3503 										addgeomtoerror(err, ai->geom, TRUE, 0, 0);
3504 								for(ai = opn->network->parent->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
3505 									if (ai->network == opn->network)
3506 										addgeomtoerror(err, ai->geom, TRUE, 0, 0);
3507 								(*errorcount)++;
3508 								break;
3509 							}
3510 						}
3511 						if (i < osg->cellcount[f]) break;
3512 					}
3513 				}
3514 			}
3515 		}
3516 		for(f=0; f<2; f++) if (pnlisttotal[f] > 0)
3517 			efree((CHAR *)pnlist[f]);
3518 	}
3519 	if (reporterrors && worstpctdiff != 0)
3520 		ttyputmsg(_("Worst size difference is %ld%%"), worstpctdiff);
3521 	return(errors);
3522 }
3523 
3524 /*
3525  * Routine to report a size error between component "pc1" with size "size1" and component "pc2" with
3526  * size "size2".  The error is "pctdiff" percent, and it comes from symmetry group "sg".
3527  */
net_reportsizeerror(PCOMP * pc1,CHAR * size1,PCOMP * pc2,CHAR * size2,INTBIG pctdiff,SYMGROUP * sg)3528 void net_reportsizeerror(PCOMP *pc1, CHAR *size1, PCOMP *pc2, CHAR *size2, INTBIG pctdiff, SYMGROUP *sg)
3529 {
3530 	REGISTER void *infstr, *err;
3531 
3532 	infstr = initinfstr();
3533 	formatinfstr(infstr, _("Node sizes differ by %ld%% ("), pctdiff);
3534 	if (pc1->numactual > 1)
3535 	{
3536 		formatinfstr(infstr, _("cell %s has %s from %ld transistors"), describenodeproto(net_cell[0]),
3537 			size1, pc1->numactual);
3538 	} else
3539 	{
3540 		formatinfstr(infstr, _("cell %s has %s"), describenodeproto(net_cell[0]), size1);
3541 	}
3542 	addstringtoinfstr(infstr, _(", but "));
3543 	if (pc2->numactual > 1)
3544 	{
3545 		formatinfstr(infstr, _("cell %s has %s from %ld transistors"), describenodeproto(net_cell[1]),
3546 			size2, pc2->numactual);
3547 	} else
3548 	{
3549 		formatinfstr(infstr, _("cell %s has %s"), describenodeproto(net_cell[1]), size2);
3550 	}
3551 	addstringtoinfstr(infstr, x_(")"));
3552 	err = logerror(returninfstr(infstr), NONODEPROTO, 0);
3553 	net_addsymgrouptoerror(err, sg);
3554 }
3555 
3556 /*
3557  * Routine to report the list of groups starting at "sg" as errors of type "errmsg".
3558  * Returns the number of errors reported.
3559  */
net_reporterror(SYMGROUP * sg,CHAR * errmsg,BOOLEAN ignorepwrgnd)3560 INTBIG net_reporterror(SYMGROUP *sg, CHAR *errmsg, BOOLEAN ignorepwrgnd)
3561 {
3562 	REGISTER INTBIG i, f, oi, of, errorsfound;
3563 	REGISTER PCOMP *pc, *opc;
3564 	REGISTER PNET *pn, *opn;
3565 	REGISTER void *infstr, *err;
3566 	REGISTER CHAR *segue;
3567 	CHAR errormessage[200];
3568 	REGISTER NODEPROTO *lastcell;
3569 	REGISTER NETWORK *net;
3570 
3571 	errorsfound = 0;
3572 	for( ; sg != NOSYMGROUP; sg = sg->nexterrsymgroup)
3573 	{
3574 		switch (sg->grouptype)
3575 		{
3576 			case SYMGROUPCOMP:
3577 				for(f=0; f<2; f++)
3578 				{
3579 					for(i=0; i<sg->cellcount[f]; i++)
3580 					{
3581 						pc = (PCOMP *)sg->celllist[f][i];
3582 						if (pc->timestamp <= 0) continue;
3583 						esnprintf(errormessage, 200, _("%s nodes"), errmsg);
3584 						err = logerror(errormessage, NONODEPROTO, 0);
3585 						net_addcomptoerror(err, pc);
3586 						for(of=0; of<2; of++)
3587 						{
3588 							for(oi=0; oi<sg->cellcount[of]; oi++)
3589 							{
3590 								opc = (PCOMP *)sg->celllist[of][oi];
3591 								if (opc == pc) continue;
3592 								if (opc->timestamp != pc->timestamp) continue;
3593 								net_addcomptoerror(err, opc);
3594 								opc->timestamp = -opc->timestamp;
3595 							}
3596 						}
3597 						pc->timestamp = -pc->timestamp;
3598 						errorsfound++;
3599 					}
3600 				}
3601 				for(f=0; f<2; f++)
3602 				{
3603 					for(i=0; i<sg->cellcount[f]; i++)
3604 					{
3605 						pc = (PCOMP *)sg->celllist[f][i];
3606 						pc->timestamp = -pc->timestamp;
3607 					}
3608 				}
3609 				break;
3610 			case SYMGROUPNET:
3611 				for(f=0; f<2; f++)
3612 				{
3613 					for(i=0; i<sg->cellcount[f]; i++)
3614 					{
3615 						pn = (PNET *)sg->celllist[f][i];
3616 						if (pn->timestamp <= 0) continue;
3617 						if (ignorepwrgnd && (pn->flags&(POWERNET|GROUNDNET)) != 0)
3618 							continue;
3619 
3620 						/* build the error message */
3621 						infstr = initinfstr();
3622 						formatinfstr(infstr, _("%s networks"), errmsg);
3623 						segue = x_(": ");
3624 						for(of=0; of<2; of++)
3625 						{
3626 							lastcell = NONODEPROTO;
3627 							for(oi=0; oi<sg->cellcount[of]; oi++)
3628 							{
3629 								opn = (PNET *)sg->celllist[of][oi];
3630 								if (opn->timestamp != pn->timestamp) continue;
3631 								if (ignorepwrgnd && (opn->flags&(POWERNET|GROUNDNET)) != 0)
3632 									continue;
3633 								net = opn->network;
3634 								if (net == NONETWORK) continue;
3635 								if (lastcell != net->parent)
3636 								{
3637 									addstringtoinfstr(infstr, segue);
3638 									segue = x_("; ");
3639 									formatinfstr(infstr, _("from cell %s:"),
3640 										describenodeproto(net->parent));
3641 								} else addtoinfstr(infstr, ',');
3642 								lastcell = net->parent;
3643 								formatinfstr(infstr, x_(" %s (%ld connections)"), describenetwork(net), opn->nodecount);
3644 							}
3645 						}
3646 
3647 						/* report the error */
3648 						err = logerror(returninfstr(infstr), NONODEPROTO, 0);
3649 						net_addnettoerror(err, pn);
3650 						for(of=0; of<2; of++)
3651 						{
3652 							for(oi=0; oi<sg->cellcount[of]; oi++)
3653 							{
3654 								opn = (PNET *)sg->celllist[of][oi];
3655 								if (opn == pn) continue;
3656 								if (opn->timestamp != pn->timestamp) continue;
3657 								if (ignorepwrgnd && (opn->flags&(POWERNET|GROUNDNET)) != 0)
3658 									continue;
3659 								net_addnettoerror(err, opn);
3660 								opn->timestamp = -opn->timestamp;
3661 							}
3662 						}
3663 						pn->timestamp = -pn->timestamp;
3664 						errorsfound++;
3665 					}
3666 				}
3667 				for(f=0; f<2; f++)
3668 				{
3669 					for(i=0; i<sg->cellcount[f]; i++)
3670 					{
3671 						pn = (PNET *)sg->celllist[f][i];
3672 						pn->timestamp = -pn->timestamp;
3673 					}
3674 				}
3675 				break;
3676 		}
3677 	}
3678 	return(errorsfound);
3679 }
3680 
3681 #define PRECISEBUSMATCH 1
3682 
3683 /*
3684  * Routine to return true if the exported ports on "pn1" and "pn2" match.
3685  */
net_sameexportnames(PNET * pn1,PNET * pn2)3686 BOOLEAN net_sameexportnames(PNET *pn1, PNET *pn2)
3687 {
3688 	REGISTER INTBIG i, j, c1, c2, nc1, nc2, c1ind, c1sig, c2ind, c2sig;
3689 	REGISTER CHAR *name1, *name2;
3690 	REGISTER PORTPROTO *pp1, *pp2;
3691 
3692 	/* determine the number of signal names on net 1 */
3693 	c1 = 0;
3694 	for(i=0; i<pn1->realportcount; i++)
3695 	{
3696 		if (pn1->realportcount == 1) pp1 = (PORTPROTO *)pn1->realportlist; else
3697 			pp1 = ((PORTPROTO **)pn1->realportlist)[i];
3698 		if (pp1->network->buswidth <= 1) c1++;
3699 #ifndef PRECISEBUSMATCH
3700 			else c1 += pp1->network->buswidth;
3701 #endif
3702 	}
3703 	if (pn1->network == NONETWORK) nc1 = 0; else
3704 		nc1 = pn1->network->namecount;
3705 
3706 	/* determine the number of signal names on net 2 */
3707 	c2 = 0;
3708 	for(i=0; i<pn2->realportcount; i++)
3709 	{
3710 		if (pn2->realportcount == 1) pp2 = (PORTPROTO *)pn2->realportlist; else
3711 			pp2 = ((PORTPROTO **)pn2->realportlist)[i];
3712 		if (pp2->network->buswidth <= 1) c2++;
3713 #ifndef PRECISEBUSMATCH
3714 			else c2 += pp2->network->buswidth;
3715 #endif
3716 	}
3717 	if (pn2->network == NONETWORK) nc2 = 0; else
3718 		nc2 = pn2->network->namecount;
3719 
3720 	c1ind = c1sig = 0;
3721 	for(i=0; i<nc1+c1; i++)
3722 	{
3723 		if (i < nc1)
3724 		{
3725 			name1 = networkname(pn1->network, i);
3726 		} else
3727 		{
3728 			if (pn1->realportcount == 1) pp1 = (PORTPROTO *)pn1->realportlist; else
3729 				pp1 = ((PORTPROTO **)pn1->realportlist)[c1ind];
3730 			if (pp1->network->buswidth <= 1)
3731 			{
3732 				name1 = pp1->protoname;
3733 				c1ind++;
3734 				c1sig = 0;
3735 #ifndef PRECISEBUSMATCH
3736 			} else
3737 			{
3738 				name1 = networkname(pp1->network->networklist[c1sig++], 0);
3739 				if (c1sig >= pp1->network->buswidth)
3740 				{
3741 					c1ind++;
3742 					c1sig = 0;
3743 				}
3744 #endif
3745 			}
3746 		}
3747 
3748 		c2ind = c2sig = 0;
3749 		for(j=0; j<nc2+c2; j++)
3750 		{
3751 			if (j < nc2)
3752 			{
3753 				name2 = networkname(pn2->network, j);
3754 			} else
3755 			{
3756 				if (pn2->realportcount == 1) pp2 = (PORTPROTO *)pn2->realportlist; else
3757 					pp2 = ((PORTPROTO **)pn2->realportlist)[c2ind];
3758 				if (pp2->network->buswidth <= 1)
3759 				{
3760 					name2 = pp2->protoname;
3761 					c2ind++;
3762 					c2sig = 0;
3763 #ifndef PRECISEBUSMATCH
3764 				} else
3765 				{
3766 					name2 = networkname(pp2->network->networklist[c2sig++], 0);
3767 					if (c2sig >= pp2->network->buswidth)
3768 					{
3769 						c2ind++;
3770 						c2sig = 0;
3771 					}
3772 #endif
3773 				}
3774 			}
3775 			if (namesame(name1, name2) == 0) return(TRUE);
3776 		}
3777 	}
3778 	return(FALSE);
3779 }
3780 
3781 /*
3782  * Routine to report the number of unmatched networks and components.
3783  */
net_unmatchedstatus(INTBIG * unmatchednets,INTBIG * unmatchedcomps,INTBIG * symgroupcount)3784 void net_unmatchedstatus(INTBIG *unmatchednets, INTBIG *unmatchedcomps, INTBIG *symgroupcount)
3785 {
3786 	REGISTER INTBIG f;
3787 	REGISTER SYMGROUP *sg;
3788 
3789 	*unmatchednets = *unmatchedcomps = *symgroupcount = 0;
3790 	for(sg = net_firstsymgroup; sg != NOSYMGROUP; sg = sg->nextsymgroup)
3791 	{
3792 		if (sg->cellcount[0] == 0 && sg->cellcount[1] == 0) continue;
3793 		(*symgroupcount)++;
3794 		if (sg->cellcount[0] == 1 && sg->cellcount[1] == 1) continue;
3795 		for(f=0; f<2; f++)
3796 		{
3797 			if (sg->grouptype == SYMGROUPCOMP)
3798 			{
3799 				*unmatchedcomps += sg->cellcount[f];
3800 			} else
3801 			{
3802 				*unmatchednets += sg->cellcount[f];
3803 			}
3804 		}
3805 	}
3806 	for(sg = net_firstmatchedsymgroup; sg != NOSYMGROUP; sg = sg->nextsymgroup)
3807 		(*symgroupcount)++;
3808 }
3809 
net_addcomptoerror(void * err,PCOMP * pc)3810 void net_addcomptoerror(void *err, PCOMP *pc)
3811 {
3812 	REGISTER NODEINST *ni;
3813 	REGISTER INTBIG i;
3814 
3815 	for(i=0; i<pc->numactual; i++)
3816 	{
3817 		if (pc->numactual == 1) ni = (NODEINST *)pc->actuallist; else
3818 			ni = ((NODEINST **)pc->actuallist)[i];
3819 		addgeomtoerror(err, ni->geom, TRUE, pc->hierpathcount, pc->hierpath);
3820 	}
3821 }
3822 
net_addnettoerror(void * err,PNET * pn)3823 void net_addnettoerror(void *err, PNET *pn)
3824 {
3825 	REGISTER ARCINST *ai;
3826 	REGISTER NETWORK *net, *anet;
3827 	REGISTER PORTPROTO *pp;
3828 	REGISTER NODEPROTO *np;
3829 	REGISTER BOOLEAN found;
3830 	REGISTER INTBIG i;
3831 
3832 	found = FALSE;
3833 	net = pn->network;
3834 	if (net == NONETWORK) return;
3835 	np = net->parent;
3836 	for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
3837 	{
3838 		anet = ai->network;
3839 		if (ai->proto == sch_busarc)
3840 		{
3841 			if (anet->buswidth > 1)
3842 			{
3843 				for(i=0; i<anet->buswidth; i++)
3844 					if (anet->networklist[i] == net) break;
3845 				if (i >= anet->buswidth) continue;
3846 			} else
3847 			{
3848 				if (anet != net) continue;
3849 			}
3850 		} else
3851 		{
3852 			if (anet != net) continue;
3853 		}
3854 		addgeomtoerror(err, ai->geom, TRUE, 0, 0);
3855 		found = TRUE;
3856 	}
3857 	for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
3858 	{
3859 		if (pp->network != net) continue;
3860 		addexporttoerror(err, pp, TRUE);
3861 		found = TRUE;
3862 	}
3863 	if (!found && net->namecount > 0)
3864 	{
3865 		if (np == net_cell[0]) np = net_cell[1]; else
3866 			np = net_cell[0];
3867 		net = getnetwork(networkname(net, 0), np);
3868 		if (net == NONETWORK) return;
3869 		for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
3870 		{
3871 			if (ai->network != net) continue;
3872 			addgeomtoerror(err, ai->geom, TRUE, 0, 0);
3873 		}
3874 		for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
3875 		{
3876 			if (pp->network != net) continue;
3877 			addexporttoerror(err, pp, TRUE);
3878 		}
3879 	}
3880 }
3881 
3882 /*
3883  * Routine to add all objects in symmetry group "sg" to the error report "err".
3884  */
net_addsymgrouptoerror(void * err,SYMGROUP * sg)3885 void net_addsymgrouptoerror(void *err, SYMGROUP *sg)
3886 {
3887 	REGISTER INTBIG i, f;
3888 	REGISTER PCOMP *pc;
3889 	REGISTER PNET *pn;
3890 
3891 	switch (sg->grouptype)
3892 	{
3893 		case SYMGROUPCOMP:
3894 			for(f=0; f<2; f++)
3895 			{
3896 				for(i=0; i<sg->cellcount[f]; i++)
3897 				{
3898 					pc = (PCOMP *)sg->celllist[f][i];
3899 					net_addcomptoerror(err, pc);
3900 				}
3901 			}
3902 			break;
3903 		case SYMGROUPNET:
3904 			for(f=0; f<2; f++)
3905 			{
3906 				for(i=0; i<sg->cellcount[f]; i++)
3907 				{
3908 					pn = (PNET *)sg->celllist[f][i];
3909 					net_addnettoerror(err, pn);
3910 				}
3911 			}
3912 			break;
3913 	}
3914 }
3915 
3916 /******************************** RESOLVING AMBIGUITY ********************************/
3917 
3918 /*
3919  * Routine to look for matches in ambiguous symmetry groups.
3920  * Returns 1 if a component group is split; -1 if a network group is split;
3921  * zero if no split was found.
3922  */
net_findamatch(INTBIG verbose,BOOLEAN ignorepwrgnd)3923 INTBIG net_findamatch(INTBIG verbose, BOOLEAN ignorepwrgnd)
3924 {
3925 	REGISTER SYMGROUP *sg, *osg;
3926 	REGISTER PNET *pn;
3927 	REGISTER PCOMP *pc;
3928 	NETWORK *nets[2];
3929 	REGISTER INTBIG i, f, u, any, total, newtype;
3930 	INTBIG is[2], unmatchednets, unmatchedcomps, symgroupcount;
3931 	CHAR uniquename[30];
3932 	float sizew, sizel;
3933 	REGISTER NODEINST *ni;
3934 	NODEPROTO *localcell[2];
3935 	REGISTER ARCINST *ai;
3936 	NODEINST *nis[2];
3937 	REGISTER VARIABLE *var, *var0, *var1;
3938 
3939 	/* determine the number of unmatched nets and components */
3940 	net_unmatchedstatus(&unmatchednets, &unmatchedcomps, &symgroupcount);
3941 
3942 	/* prepare a list of ambiguous symmetry groups */
3943 	total = 0;
3944 	for(sg = net_firstsymgroup; sg != NOSYMGROUP; sg = sg->nextsymgroup)
3945 	{
3946 		if (sg->hashvalue == 0) continue;
3947 #ifdef NEWNCC
3948 		if (sg->cellcount[0] < 2 && sg->cellcount[1] < 2) continue;
3949 #else
3950 		if (sg->cellcount[0] < 2 || sg->cellcount[1] < 2) continue;
3951 #endif
3952 		total++;
3953 	}
3954 	if (total > net_symgrouplisttotal)
3955 	{
3956 		if (net_symgrouplisttotal > 0) efree((CHAR *)net_symgrouplist);
3957 		net_symgrouplisttotal = 0;
3958 		net_symgrouplist = (SYMGROUP **)emalloc(total * (sizeof (SYMGROUP *)),
3959 			net_tool->cluster);
3960 		if (net_symgrouplist == 0) return(0);
3961 		net_symgrouplisttotal = total;
3962 	}
3963 	total = 0;
3964 	for(sg = net_firstsymgroup; sg != NOSYMGROUP; sg = sg->nextsymgroup)
3965 	{
3966 		if (sg->hashvalue == 0) continue;
3967 #ifdef NEWNCC
3968 		if (sg->cellcount[0] < 2 && sg->cellcount[1] < 2) continue;
3969 #else
3970 		if (sg->cellcount[0] < 2 || sg->cellcount[1] < 2) continue;
3971 #endif
3972 		net_symgrouplist[total++] = sg;
3973 	}
3974 
3975 	/* sort by size of ambiguity */
3976 	esort(net_symgrouplist, total, sizeof (SYMGROUP *), net_sortsymgroups);
3977 
3978 	/* now look through the groups, starting with the smallest */
3979 	for(i=0; i<total; i++)
3980 	{
3981 		sg = net_symgrouplist[i];
3982 
3983 		if (sg->grouptype == SYMGROUPNET)
3984 		{
3985 			/* look for export names that are the same */
3986 			if (net_findexportnamematch(sg, verbose, ignorepwrgnd,
3987 				symgroupcount, unmatchednets, unmatchedcomps)) return(-1);
3988 
3989 			/* look for network names that are the same */
3990 			if (net_findnetworknamematch(sg, FALSE, verbose, ignorepwrgnd,
3991 				symgroupcount, unmatchednets, unmatchedcomps)) return(-1);
3992 		} else
3993 		{
3994 			/* look for nodes that are uniquely the same size */
3995 			if (net_findcommonsizefactor(sg, &sizew, &sizel))
3996 			{
3997 				ttyputmsg(_("--- Forcing a match based on size %s nodes (%ld symmetry groups with %ld nets and %ld nodes unmatched)"),
3998 					net_describesizefactor(sizew, sizel), symgroupcount, unmatchednets, unmatchedcomps);
3999 				net_forceamatch(sg, 0, 0, 0, 0, sizew, sizel, verbose, ignorepwrgnd);
4000 				return(1);
4001 			}
4002 
4003 			/* look for node names that are the same */
4004 			if (net_findcomponentnamematch(sg, FALSE, verbose, ignorepwrgnd,
4005 				symgroupcount, unmatchednets, unmatchedcomps)) return(1);
4006 		}
4007 	}
4008 
4009 	/* now look for pseudo-matches with the "NCCMatch" tags */
4010 	for(i=0; i<total; i++)
4011 	{
4012 		sg = net_symgrouplist[i];
4013 
4014 		if (sg->grouptype == SYMGROUPNET)
4015 		{
4016 			/* look for network names that are the same */
4017 			if (net_findnetworknamematch(sg, TRUE, verbose, ignorepwrgnd,
4018 				symgroupcount, unmatchednets, unmatchedcomps)) return(-1);
4019 		} else
4020 		{
4021 			/* look for node names that are the same */
4022 			if (net_findcomponentnamematch(sg, TRUE, verbose, ignorepwrgnd,
4023 				symgroupcount, unmatchednets, unmatchedcomps)) return(1);
4024 		}
4025 	}
4026 
4027 	/* random match: look again through the groups, starting with the smallest */
4028 	for(i=0; i<total; i++)
4029 	{
4030 		sg = net_symgrouplist[i];
4031 		if (sg->cellcount[0] <= 0 || sg->cellcount[1] <= 0) continue;
4032 
4033 		if (sg->grouptype == SYMGROUPCOMP)
4034 		{
4035 			for(f=0; f<2; f++)
4036 			{
4037 				for(is[f]=0; is[f] < sg->cellcount[f]; is[f]++)
4038 				{
4039 					pc = (PCOMP *)sg->celllist[f][is[f]];
4040 					if (pc->numactual == 1) nis[f] = (NODEINST *)pc->actuallist; else
4041 						nis[f] = ((NODEINST **)pc->actuallist)[0];
4042 					var = getvalkey((INTBIG)nis[f], VNODEINST, VSTRING, el_node_name_key);
4043 					if (var == NOVARIABLE) break;
4044 					if ((var->type&VDISPLAY) == 0) break;
4045 				}
4046 				if (is[f] >= sg->cellcount[f])
4047 				{
4048 					is[f] = 0;
4049 					pc = (PCOMP *)sg->celllist[f][is[f]];
4050 					if (pc->numactual == 1) nis[f] = (NODEINST *)pc->actuallist; else
4051 						nis[f] = ((NODEINST **)pc->actuallist)[0];
4052 				}
4053 			}
4054 
4055 			/* copy "NCCMatch" information if possible */
4056 			localcell[0] = nis[0]->parent;
4057 			localcell[1] = nis[1]->parent;
4058 			var0 = getvalkey((INTBIG)nis[0], VNODEINST, VSTRING, net_ncc_matchkey);
4059 			var1 = getvalkey((INTBIG)nis[1], VNODEINST, VSTRING, net_ncc_matchkey);
4060 			if (var0 != NOVARIABLE && var1 != NOVARIABLE)
4061 			{
4062 				/* both have a name: warn if different */
4063 				if (namesame((CHAR *)var0->addr, (CHAR *)var1->addr) != 0)
4064 				{
4065 					ttyputmsg(x_("WARNING: want to match nodes %s:s and %s:%s but they are already tagged '%s' and '%s'"),
4066 						describenodeproto(localcell[0]), describenodeinst(nis[0]),
4067 						describenodeproto(localcell[1]), describenodeinst(nis[1]),
4068 						(CHAR *)var0->addr, (CHAR *)var1->addr);
4069 				}
4070 				var0 = var1 = NOVARIABLE;
4071 			}
4072 			if (var0 == NOVARIABLE && var1 != NOVARIABLE)
4073 			{
4074 				/* node in cell 0 has no name, see if it can take the name from cell 1 */
4075 				estrcpy(uniquename, (CHAR *)var1->addr);
4076 				for(ni = localcell[0]->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
4077 				{
4078 					var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, net_ncc_matchkey);
4079 					if (var == NOVARIABLE) continue;
4080 					if (namesame((CHAR *)var->addr, uniquename) == 0) break;
4081 				}
4082 				if (ni != NONODEINST) var1 = NOVARIABLE; else
4083 				{
4084 					/* copy from cell 1 to cell 0 */
4085 					startobjectchange((INTBIG)nis[0], VNODEINST);
4086 					newtype = VSTRING;
4087 					if ((net_ncc_options&NCCHIDEMATCHTAGS) == 0) newtype |= VDISPLAY;
4088 					var = setvalkey((INTBIG)nis[0], VNODEINST, net_ncc_matchkey,
4089 						(INTBIG)uniquename, newtype);
4090 					if (var != NOVARIABLE)
4091 						defaulttextsize(3, var->textdescript);
4092 					endobjectchange((INTBIG)nis[0], VNODEINST);
4093 				}
4094 			}
4095 			if (var0 != NOVARIABLE && var1 == NOVARIABLE)
4096 			{
4097 				/* node in cell 1 has no name, see if it can take the name from cell 0 */
4098 				estrcpy(uniquename, (CHAR *)var0->addr);
4099 				for(ni = localcell[1]->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
4100 				{
4101 					var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, net_ncc_matchkey);
4102 					if (var == NOVARIABLE) continue;
4103 					if (namesame((CHAR *)var->addr, uniquename) == 0) break;
4104 				}
4105 				if (ni != NONODEINST) var0 = NOVARIABLE; else
4106 				{
4107 					/* copy from cell 0 to cell 1 */
4108 					startobjectchange((INTBIG)nis[1], VNODEINST);
4109 					newtype = VSTRING;
4110 					if ((net_ncc_options&NCCHIDEMATCHTAGS) == 0) newtype |= VDISPLAY;
4111 					var = setvalkey((INTBIG)nis[1], VNODEINST, net_ncc_matchkey,
4112 						(INTBIG)uniquename, newtype);
4113 					if (var != NOVARIABLE)
4114 						defaulttextsize(3, var->textdescript);
4115 					endobjectchange((INTBIG)nis[1], VNODEINST);
4116 				}
4117 			}
4118 			if (var0 == NOVARIABLE && var1 == NOVARIABLE)
4119 			{
4120 				/* neither has a name: find a unique name and tag the selected nodes */
4121 				for(u=1; ; u++)
4122 				{
4123 					esnprintf(uniquename, 30, x_("NCCmatch%ld"), u);
4124 					for(f=0; f<2; f++)
4125 					{
4126 						for(ni = localcell[f]->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
4127 						{
4128 							var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, net_ncc_matchkey);
4129 							if (var == NOVARIABLE) continue;
4130 							if (namesame((CHAR *)var->addr, uniquename) == 0) break;
4131 						}
4132 						if (ni != NONODEINST) break;
4133 					}
4134 					if (f >= 2) break;
4135 				}
4136 				for(f=0; f<2; f++)
4137 				{
4138 					startobjectchange((INTBIG)nis[f], VNODEINST);
4139 					newtype = VSTRING;
4140 					if ((net_ncc_options&NCCHIDEMATCHTAGS) == 0) newtype |= VDISPLAY;
4141 					var = setvalkey((INTBIG)nis[f], VNODEINST, net_ncc_matchkey,
4142 						(INTBIG)uniquename, newtype);
4143 					if (var != NOVARIABLE)
4144 						defaulttextsize(3, var->textdescript);
4145 					endobjectchange((INTBIG)nis[f], VNODEINST);
4146 				}
4147 			}
4148 			if ((net_ncc_options&NCCSUPALLAMBREP) == 0)
4149 			{
4150 				ttyputmsg(_("--- Forcing a random match of nodes '%s:%s' and '%s:%s', called %s (%ld symmetry groups with %ld nets and %ld nodes unmatched)"),
4151 					describenodeproto(localcell[0]), describenodeinst(nis[0]),
4152 						describenodeproto(localcell[1]), describenodeinst(nis[1]),
4153 							uniquename, symgroupcount, unmatchednets, unmatchedcomps);
4154 			}
4155 			net_forceamatch(sg, 1, &is[0], 1, &is[1], 0.0, 0.0, verbose, ignorepwrgnd);
4156 			return(1);
4157 		} else
4158 		{
4159 			/* look for any ambiguous networks and randomly match them */
4160 			for(f=0; f<2; f++)
4161 			{
4162 				any = -1;
4163 				for(is[f]=0; is[f] < sg->cellcount[f]; is[f]++)
4164 				{
4165 					pn = (PNET *)sg->celllist[f][is[f]];
4166 					nets[f] = pn->network;
4167 					if (nets[f] == NONETWORK) continue;
4168 					any = is[f];
4169 					if (nets[f]->namecount == 0 ||
4170 						nets[f]->tempname != 0) break;
4171 				}
4172 				if (is[f] >= sg->cellcount[f])
4173 				{
4174 					if (any < 0) nets[f] = NONETWORK; else
4175 					{
4176 						is[f] = any;
4177 						pn = (PNET *)sg->celllist[f][any];
4178 						nets[f] = pn->network;
4179 					}
4180 				}
4181 			}
4182 			if (nets[0] != NONETWORK && nets[1] != NONETWORK)
4183 			{
4184 				/* find a unique name and tag the selected networks */
4185 				for(u=1; ; u++)
4186 				{
4187 					esnprintf(uniquename, 30, x_("NCCmatch%ld"), u);
4188 					for(f=0; f<2; f++)
4189 					{
4190 						for(ai = nets[f]->parent->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
4191 						{
4192 							var = getvalkey((INTBIG)ai, VARCINST, VSTRING, net_ncc_matchkey);
4193 							if (var == NOVARIABLE) continue;
4194 							if (namesame(uniquename, (CHAR *)var->addr) == 0) break;
4195 						}
4196 						if (ai != NOARCINST) break;
4197 					}
4198 					if (f >= 2) break;
4199 				}
4200 				for(f=0; f<2; f++)
4201 				{
4202 					for(ai = nets[f]->parent->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
4203 					{
4204 						if (ai->network != nets[f]) continue;
4205 						startobjectchange((INTBIG)ai, VARCINST);
4206 						newtype = VSTRING;
4207 						if ((net_ncc_options&NCCHIDEMATCHTAGS) == 0) newtype |= VDISPLAY;
4208 						var = setvalkey((INTBIG)ai, VARCINST, net_ncc_matchkey,
4209 							(INTBIG)uniquename, newtype);
4210 						if (var != NOVARIABLE)
4211 							defaulttextsize(4, var->textdescript);
4212 						endobjectchange((INTBIG)ai, VARCINST);
4213 
4214 						/* pickup new net number and remember it in the data structures */
4215 						for(osg = net_firstsymgroup; osg != NOSYMGROUP; osg = osg->nextsymgroup)
4216 						{
4217 							if (osg->grouptype == SYMGROUPCOMP) continue;
4218 							for(i=0; i<osg->cellcount[f]; i++)
4219 							{
4220 								pn = (PNET *)osg->celllist[f][i];
4221 								if (pn->network == nets[f])
4222 									pn->network = ai->network;
4223 							}
4224 						}
4225 						nets[f] = ai->network;
4226 						break;
4227 					}
4228 				}
4229 
4230 				if ((net_ncc_options&NCCSUPALLAMBREP) == 0)
4231 				{
4232 					ttyputmsg(_("--- Forcing a random match of networks '%s' in cells %s and %s (%ld symmetry groups with %ld nets and %ld nodes unmatched)"),
4233 						uniquename, describenodeproto(nets[0]->parent), describenodeproto(nets[1]->parent),
4234 							symgroupcount, unmatchednets, unmatchedcomps);
4235 				}
4236 				net_forceamatch(sg, 1, &is[0], 1, &is[1], 0.0, 0.0, verbose, ignorepwrgnd);
4237 				return(-1);
4238 			}
4239 		}
4240 	}
4241 
4242 	return(0);
4243 }
4244 
4245 /*
4246  * Routine to look through ambiguous symmetry group "sg" for export names that cause a match.
4247  * If one is found, it is reported and matched and the routine returns true.
4248  * Flags "verbose" and "ignorepwrgnd" apply to the match.  Tallys "total", "unmatchednets",
4249  * and "unmatchedcomps" are reported.
4250  */
net_findexportnamematch(SYMGROUP * sg,INTBIG verbose,BOOLEAN ignorepwrgnd,INTBIG total,INTBIG unmatchednets,INTBIG unmatchedcomps)4251 BOOLEAN net_findexportnamematch(SYMGROUP *sg, INTBIG verbose, BOOLEAN ignorepwrgnd,
4252 	INTBIG total, INTBIG unmatchednets, INTBIG unmatchedcomps)
4253 {
4254 	REGISTER INTBIG f, i0, i1, i, ip, i0base, i1base, comp;
4255 	INTBIG count[2];
4256 	REGISTER PNET *pn;
4257 	REGISTER PORTPROTO *pp;
4258 
4259 	/* build a list of all export names */
4260 	for(f=0; f<2; f++)
4261 	{
4262 		count[f] = 0;
4263 		for(i=0; i<sg->cellcount[f]; i++)
4264 		{
4265 			pn = (PNET *)sg->celllist[f][i];
4266 			for(ip=0; ip<pn->realportcount; ip++)
4267 			{
4268 				if (pn->realportcount == 1) pp = (PORTPROTO *)pn->realportlist; else
4269 					pp = ((PORTPROTO **)pn->realportlist)[ip];
4270 				net_addtonamematch(&net_namematch[f], &net_namematchtotal[f], &count[f],
4271 					pp->protoname, i, NONODEINST);
4272 			}
4273 		}
4274 		esort(net_namematch[f], count[f], sizeof(NAMEMATCH), net_sortnamematches);
4275 	}
4276 
4277 	/* now look for unique matches */
4278 	i0 = i1 = 0;
4279 	for(;;)
4280 	{
4281 		if (i0 >= count[0] || i1 >= count[1]) break;
4282 		comp = namesame(net_namematch[0][i0].name, net_namematch[1][i1].name);
4283 		i0base = i0;   i1base = i1;
4284 		while (i0+1 < count[0] &&
4285 			namesame(net_namematch[0][i0].name, net_namematch[0][i0+1].name) == 0)
4286 				i0++;
4287 		while (i1+1 < count[1] &&
4288 			namesame(net_namematch[1][i1].name, net_namematch[1][i1+1].name) == 0)
4289 				i1++;
4290 		if (comp == 0)
4291 		{
4292 			if (i0 == i0base && i1 == i1base)
4293 			{
4294 				/* found a unique match */
4295 				ttyputmsg(_("--- Forcing a match based on the export name '%s' (%ld symmetry groups with %ld nets and %ld nodes unmatched)"),
4296 					net_namematch[0][i0].name, total, unmatchednets, unmatchedcomps);
4297 				net_forceamatch(sg, 1, &net_namematch[0][i0].number, 1, &net_namematch[1][i1].number,
4298 					0.0, 0.0, verbose, ignorepwrgnd);
4299 				return(TRUE);
4300 			}
4301 			i0++;   i1++;
4302 		} else
4303 		{
4304 			if (comp < 0) i0++; else i1++;
4305 		}
4306 	}
4307 	return(FALSE);
4308 }
4309 
net_findpowerandgroundmatch(SYMGROUP * sg,INTBIG verbose,INTBIG total,INTBIG unmatchednets,INTBIG unmatchedcomps)4310 BOOLEAN net_findpowerandgroundmatch(SYMGROUP *sg, INTBIG verbose, INTBIG total, INTBIG unmatchednets, INTBIG unmatchedcomps)
4311 {
4312 	REGISTER INTBIG f, i, power[2], ground[2];
4313 	REGISTER PNET *pn;
4314 
4315 	power[0] = power[1] = ground[0] = ground[1] = -1;
4316 	for(f=0; f<2; f++)
4317 	{
4318 		for(i=0; i<sg->cellcount[f]; i++)
4319 		{
4320 			pn = (PNET *)sg->celllist[f][i];
4321 			if ((pn->flags&POWERNET) != 0)
4322 			{
4323 				if (power[f] == -1) power[f] = i; else power[f] = -2;
4324 			}
4325 			if ((pn->flags&GROUNDNET) != 0)
4326 			{
4327 				if (ground[f] == -1) ground[f] = i; else ground[f] = -2;
4328 			}
4329 		}
4330 	}
4331 
4332 	/* if there is exactly 1 power net in each side, disambiguate it */
4333 	if (power[0] >= 0 && power[1] >= 0)
4334 	{
4335 		/* found a unique match */
4336 		ttyputmsg(_("--- Forcing a match based on the power characteristic (%ld symmetry groups with %ld nets and %ld nodes unmatched)"),
4337 			total, unmatchednets, unmatchedcomps);
4338 		net_forceamatch(sg, 1, &power[0], 1, &power[1], 0.0, 0.0, verbose, FALSE);
4339 		return(TRUE);
4340 	}
4341 
4342 	/* if there is exactly 1 ground net in each side, disambiguate it */
4343 	if (ground[0] >= 0 && ground[1] >= 0)
4344 	{
4345 		/* found a unique match */
4346 		ttyputmsg(_("--- Forcing a match based on the ground characteristic (%ld symmetry groups with %ld nets and %ld nodes unmatched)"),
4347 			total, unmatchednets, unmatchedcomps);
4348 		net_forceamatch(sg, 1, &ground[0], 1, &ground[1], 0.0, 0.0, verbose, FALSE);
4349 		return(TRUE);
4350 	}
4351 	return(FALSE);
4352 }
4353 
4354 /*
4355  * Routine to look through ambiguous symmetry group "sg" for network names that cause a match.
4356  * If one is found, it is reported and matched and the routine returns true.
4357  * If "usenccmatches" is true, allow "NCCMatch" tags.
4358  * If "allowpseudonames" is zero and such names are found, the routine returns true.
4359  * Flags "verbose" and "ignorepwrgnd" apply to the match.  Tallys "total", "unmatchednets",
4360  * and "unmatchedcomps" are reported.
4361  */
net_findnetworknamematch(SYMGROUP * sg,BOOLEAN usenccmatches,INTBIG verbose,BOOLEAN ignorepwrgnd,INTBIG total,INTBIG unmatchednets,INTBIG unmatchedcomps)4362 BOOLEAN net_findnetworknamematch(SYMGROUP *sg, BOOLEAN usenccmatches, INTBIG verbose,
4363 	BOOLEAN ignorepwrgnd, INTBIG total, INTBIG unmatchednets, INTBIG unmatchedcomps)
4364 {
4365 	REGISTER INTBIG i0, i1, i, ip, f, comp, i0base, i1base;
4366 	INTBIG count[2];
4367 	REGISTER BOOLEAN foundexport;
4368 	REGISTER PNET *pn;
4369 	REGISTER VARIABLE *var;
4370 	REGISTER ARCINST *ai;
4371 	REGISTER NETWORK *net;
4372 	REGISTER CHAR *netname;
4373 
4374 	/* build a list of all network names */
4375 	foundexport = FALSE;
4376 	for(f=0; f<2; f++)
4377 	{
4378 		count[f] = 0;
4379 		for(i=0; i<sg->cellcount[f]; i++)
4380 		{
4381 			pn = (PNET *)sg->celllist[f][i];
4382 			net = pn->network;
4383 			if (net == NONETWORK) continue;
4384 			if ((pn->flags&EXPORTEDNET) != 0) foundexport = TRUE;
4385 			if (usenccmatches)
4386 			{
4387 				for(ai = net_cell[f]->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
4388 				{
4389 					if (ai->network != net) continue;
4390 					var = getvalkey((INTBIG)ai, VARCINST, VSTRING, net_ncc_matchkey);
4391 					if (var == NOVARIABLE) continue;
4392 					net_addtonamematch(&net_namematch[f], &net_namematchtotal[f], &count[f],
4393 						(CHAR *)var->addr, i, NONODEINST);
4394 				}
4395 			} else
4396 			{
4397 				if (net->namecount == 0 || net->tempname != 0) continue;
4398 				for(ip=0; ip<net->namecount; ip++)
4399 				{
4400 					netname = networkname(net, ip);
4401 					net_addtonamematch(&net_namematch[f], &net_namematchtotal[f], &count[f],
4402 						netname, i, NONODEINST);
4403 				}
4404 			}
4405 		}
4406 		esort(net_namematch[f], count[f], sizeof(NAMEMATCH), net_sortnamematches);
4407 	}
4408 
4409 	/* now look for unique matches */
4410 	i0 = i1 = 0;
4411 	for(;;)
4412 	{
4413 		if (i0 >= count[0] || i1 >= count[1]) break;
4414 		comp = namesame(net_namematch[0][i0].name, net_namematch[1][i1].name);
4415 		i0base = i0;   i1base = i1;
4416 		while (i0+1 < count[0] &&
4417 			namesame(net_namematch[0][i0].name, net_namematch[0][i0+1].name) == 0)
4418 				i0++;
4419 		while (i1+1 < count[1] &&
4420 			namesame(net_namematch[1][i1].name, net_namematch[1][i1+1].name) == 0)
4421 				i1++;
4422 		if (comp == 0)
4423 		{
4424 			if (i0 == i0base && i1 == i1base)
4425 			{
4426 				/* found a unique match */
4427 				if ((net_ncc_options&NCCSUPALLAMBREP) == 0 || foundexport)
4428 				{
4429 					ttyputmsg(_("--- Forcing a match based on the network name '%s' (%ld symmetry groups with %ld nets and %ld nodes unmatched)"),
4430 						net_namematch[0][i0].name, total, unmatchednets, unmatchedcomps);
4431 				}
4432 				net_forceamatch(sg, 1, &net_namematch[0][i0].number, 1, &net_namematch[1][i1].number,
4433 					0.0, 0.0, verbose, ignorepwrgnd);
4434 				return(TRUE);
4435 			}
4436 			i0++;   i1++;
4437 		} else
4438 		{
4439 			if (comp < 0) i0++; else i1++;
4440 		}
4441 	}
4442 
4443 	/* no unique name found */
4444 	return(FALSE);
4445 }
4446 
4447 /*
4448  * Routine to look through ambiguous symmetry group "sg" for component names that cause a match.
4449  * If one is found, it is reported and matched and the routine returns true.
4450  * If "usenccmatches" is true, use "NCCMatch" tags.
4451  * If "allowpseudonames" is zero and such names are found, the routine returns true.
4452  * Flags "verbose" and "ignorepwrgnd" apply to the match.  Tallys "total", "unmatchednets",
4453  * and "unmatchedcomps" are reported.
4454  */
net_findcomponentnamematch(SYMGROUP * sg,BOOLEAN usenccmatches,INTBIG verbose,BOOLEAN ignorepwrgnd,INTBIG total,INTBIG unmatchednets,INTBIG unmatchedcomps)4455 BOOLEAN net_findcomponentnamematch(SYMGROUP *sg, BOOLEAN usenccmatches,
4456 	INTBIG verbose, BOOLEAN ignorepwrgnd, INTBIG total, INTBIG unmatchednets, INTBIG unmatchedcomps)
4457 {
4458 	REGISTER INTBIG i0, i1, i0base, i1base, i, j, f, comp, i0ptr, i1ptr;
4459 	INTBIG count[2];
4460 	REGISTER PCOMP *pc;
4461 	REGISTER NODEINST *ni;
4462 	REGISTER VARIABLE *var;
4463 
4464 	/* build a list of all component names */
4465 	for(f=0; f<2; f++)
4466 	{
4467 		count[f] = 0;
4468 		for(i=0; i<sg->cellcount[f]; i++)
4469 		{
4470 			pc = (PCOMP *)sg->celllist[f][i];
4471 			if (pc->numactual != 1) continue;
4472 			ni = (NODEINST *)pc->actuallist;
4473 			if (usenccmatches)
4474 			{
4475 				var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, net_ncc_matchkey);
4476 				if (var == NOVARIABLE) continue;
4477 				net_addtonamematch(&net_namematch[f], &net_namematchtotal[f], &count[f],
4478 					(CHAR *)var->addr, i, ni);
4479 			} else
4480 			{
4481 				var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, el_node_name_key);
4482 				if (var != NOVARIABLE)
4483 				{
4484 					if ((var->type&VDISPLAY) != 0)
4485 					{
4486 						net_addtonamematch(&net_namematch[f], &net_namematchtotal[f], &count[f],
4487 							(CHAR *)var->addr, i, ni);
4488 					}
4489 				}
4490 			}
4491 		}
4492 		esort(net_namematch[f], count[f], sizeof(NAMEMATCH), net_sortnamematches);
4493 	}
4494 
4495 	/* now look for unique matches */
4496 	i0 = i1 = 0;
4497 	i0ptr = i1ptr = 0;
4498 	for(;;)
4499 	{
4500 		if (i0 >= count[0] || i1 >= count[1]) break;
4501 		comp = namesame(net_namematch[0][i0].name, net_namematch[1][i1].name);
4502 		if (comp == 0)
4503 		{
4504 			/* gather all with the same name */
4505 			i0base = i0;   i1base = i1;
4506 			while (i0+1 < count[0] &&
4507 				namesame(net_namematch[0][i0].name, net_namematch[0][i0+1].name) == 0)
4508 					i0++;
4509 			while (i1+1 < count[1] &&
4510 				namesame(net_namematch[1][i1].name, net_namematch[1][i1+1].name) == 0)
4511 					i1++;
4512 
4513 			/* make a list of entries from cell 0 */
4514 			j = i0 - i0base + 1;
4515 			if (j >= net_compmatch0total)
4516 			{
4517 				if (net_compmatch0total > 0) efree((CHAR *)net_compmatch0list);
4518 				net_compmatch0total = 0;
4519 				net_compmatch0list = (INTBIG *)emalloc(j * SIZEOFINTBIG, net_tool->cluster);
4520 				if (net_compmatch0list == 0) return(FALSE);
4521 				net_compmatch0total = j;
4522 			}
4523 			i0ptr = 0;
4524 			for(i=i0base; i<=i0; i++)
4525 				net_compmatch0list[i0ptr++] = net_namematch[0][i].number;
4526 			esort(net_compmatch0list, i0ptr, SIZEOFINTBIG, sort_intbigdescending);
4527 			j = 0; for(i=0; i<i0ptr; i++)
4528 			{
4529 				if (i == 0 || net_compmatch0list[i-1] != net_compmatch0list[i])
4530 					net_compmatch0list[j++] = net_compmatch0list[i];
4531 			}
4532 			i0ptr = j;
4533 
4534 			/* make a list of entries from cell 1 */
4535 			j = i1 - i1base + 1;
4536 			if (j >= net_compmatch1total)
4537 			{
4538 				if (net_compmatch1total > 0) efree((CHAR *)net_compmatch1list);
4539 				net_compmatch1total = 0;
4540 				net_compmatch1list = (INTBIG *)emalloc(j * SIZEOFINTBIG, net_tool->cluster);
4541 				if (net_compmatch1list == 0) return(FALSE);
4542 				net_compmatch1total = j;
4543 			}
4544 			i1ptr = 0;
4545 			for(i=i1base; i<=i1; i++)
4546 				net_compmatch1list[i1ptr++] = net_namematch[1][i].number;
4547 			esort(net_compmatch1list, i1ptr, SIZEOFINTBIG, sort_intbigdescending);
4548 			j = 0; for(i=0; i<i1ptr; i++)
4549 			{
4550 				if (i == 0 || net_compmatch1list[i-1] != net_compmatch1list[i])
4551 					net_compmatch1list[j++] = net_compmatch1list[i];
4552 			}
4553 			i1ptr = j;
4554 			if (i0ptr != 0 && i1ptr != 0 && i0ptr != sg->cellcount[0] && i1ptr != sg->cellcount[1])
4555 			{
4556 				/* found a unique match */
4557 				if ((net_ncc_options&NCCSUPALLAMBREP) == 0)
4558 				{
4559 					ttyputmsg(_("--- Forcing a match based on the nodes named '%s' in cells %s and %s (%ld symmetry groups with %ld nets and %ld nodes unmatched)"),
4560 						net_namematch[0][i0].name, describenodeproto(net_namematch[0][i0].original->parent),
4561 							describenodeproto(net_namematch[1][i1].original->parent), total,
4562 								unmatchednets, unmatchedcomps);
4563 				}
4564 				net_forceamatch(sg, i0ptr, net_compmatch0list, i1ptr, net_compmatch1list,
4565 					0.0, 0.0, verbose, ignorepwrgnd);
4566 				return(TRUE);
4567 			}
4568 			i0++;   i1++;
4569 		} else
4570 		{
4571 			if (comp < 0) i0++; else i1++;
4572 		}
4573 	}
4574 
4575 	/* no unique name found */
4576 	return(FALSE);
4577 }
4578 
4579 /*
4580  * Helper routine to build the list of names.
4581  */
net_addtonamematch(NAMEMATCH ** match,INTBIG * total,INTBIG * count,CHAR * name,INTBIG number,NODEINST * orig)4582 void net_addtonamematch(NAMEMATCH **match, INTBIG *total, INTBIG *count,
4583 	CHAR *name, INTBIG number, NODEINST *orig)
4584 {
4585 	REGISTER INTBIG i, newtotal;
4586 	REGISTER NAMEMATCH *newmatch;
4587 
4588 	if (*count >= *total)
4589 	{
4590 		newtotal = *total * 2;
4591 		if (newtotal <= *count) newtotal = *count + 25;
4592 		newmatch = (NAMEMATCH *)emalloc(newtotal * (sizeof (NAMEMATCH)), net_tool->cluster);
4593 		if (newmatch == 0) return;
4594 		for(i=0; i < *count; i++)
4595 		{
4596 			newmatch[i].name = (*match)[i].name;
4597 			newmatch[i].number = (*match)[i].number;
4598 			newmatch[i].original = (*match)[i].original;
4599 		}
4600 		if (*total > 0) efree((CHAR *)*match);
4601 		*match = newmatch;
4602 		*total = newtotal;
4603 	}
4604 	(*match)[*count].name = name;
4605 	(*match)[*count].number = number;
4606 	(*match)[*count].original = orig;
4607 	(*count)++;
4608 }
4609 
4610 /*
4611  * Helper routine to sort the list of names.
4612  */
net_sortnamematches(const void * e1,const void * e2)4613 int net_sortnamematches(const void *e1, const void *e2)
4614 {
4615 	REGISTER NAMEMATCH *nm1, *nm2;
4616 
4617 	nm1 = (NAMEMATCH *)e1;
4618 	nm2 = (NAMEMATCH *)e2;
4619 	return(namesame(nm1->name, nm2->name));
4620 }
4621 
net_sortsymgroups(const void * e1,const void * e2)4622 int net_sortsymgroups(const void *e1, const void *e2)
4623 {
4624 	REGISTER SYMGROUP *sg1, *sg2;
4625 	REGISTER INTBIG sg1size, sg2size;
4626 
4627 	sg1 = *((SYMGROUP **)e1);
4628 	sg2 = *((SYMGROUP **)e2);
4629 	sg1size = sg1->cellcount[0] + sg1->cellcount[1];
4630 	sg2size = sg2->cellcount[0] + sg2->cellcount[1];
4631 #ifdef NEWNCC
4632 	if (sg1->hashvalue == 0 || (sg1->cellcount[0] < 2 && sg1->cellcount[1] < 2)) sg1size = 0;
4633 	if (sg2->hashvalue == 0 || (sg2->cellcount[0] < 2 && sg2->cellcount[1] < 2)) sg2size = 0;
4634 #else
4635 	if (sg1->hashvalue == 0 || sg1->cellcount[0] < 2 || sg1->cellcount[1] < 2) sg1size = 0;
4636 	if (sg2->hashvalue == 0 || sg2->cellcount[0] < 2 || sg2->cellcount[1] < 2) sg2size = 0;
4637 #endif
4638 	return(sg1size - sg2size);
4639 }
4640 
net_sortpcomp(const void * e1,const void * e2)4641 int net_sortpcomp(const void *e1, const void *e2)
4642 {
4643 	REGISTER PCOMP *pc1, *pc2;
4644 	REGISTER CHAR *pt1, *pt2;
4645 
4646 	pc1 = *((PCOMP **)e1);
4647 	pc2 = *((PCOMP **)e2);
4648 	if (pc2->wirecount != pc1->wirecount)
4649 		return(pc2->wirecount - pc1->wirecount);
4650 	pt1 = pc1->hashreason;
4651 	pt2 = pc2->hashreason;
4652 	return(namesame(pt1, pt2));
4653 }
4654 
net_sortpnet(const void * e1,const void * e2)4655 int net_sortpnet(const void *e1, const void *e2)
4656 {
4657 	REGISTER PNET *pn1, *pn2;
4658 	REGISTER NETWORK *net1, *net2;
4659 	REGISTER NODEPROTO *cell1, *cell2;
4660 	REGISTER INTBIG un1, un2;
4661 
4662 	pn1 = *((PNET **)e1);
4663 	pn2 = *((PNET **)e2);
4664 	if (pn2->nodecount != pn1->nodecount)
4665 		return(pn2->nodecount - pn1->nodecount);
4666 	un1 = un2 = 0;
4667 	if ((pn1->flags&(POWERNET|GROUNDNET|EXPORTEDNET)) == 0 &&
4668 		(pn1->network == NONETWORK || pn1->network->namecount == 0)) un1 = 1;
4669 	if ((pn2->flags&(POWERNET|GROUNDNET|EXPORTEDNET)) == 0 &&
4670 		(pn2->network == NONETWORK || pn2->network->namecount == 0)) un2 = 1;
4671 	if (un1 == 0 && un2 == 0)
4672 	{
4673 		return(namesame(net_describepnet(pn1), net_describepnet(pn2)));
4674 	}
4675 	if (un1 != 0 && un2 != 0)
4676 	{
4677 		net1 = pn1->network;
4678 		net2 = pn2->network;
4679 		if (net1 != NONETWORK && net2 != NONETWORK)
4680 		{
4681 			cell1 = net1->parent;
4682 			cell2 = net2->parent;
4683 			return(namesame(cell1->protoname, cell2->protoname));
4684 		}
4685 		return(0);
4686 	}
4687 	return(un1 - un2);
4688 }
4689 
4690 /*
4691  * Routine to search symmetry group "sg" for a size factor that will distinguish part of
4692  * the group.  Returns true if a distinguishing size is found (and places it in "sizew" and
4693  * "sizel").
4694  */
net_findcommonsizefactor(SYMGROUP * sg,float * sizew,float * sizel)4695 BOOLEAN net_findcommonsizefactor(SYMGROUP *sg, float *sizew, float *sizel)
4696 {
4697 	REGISTER INTBIG i, j, f, newtotal, p0, p1, bestind0, bestind1;
4698 	INTBIG sizearraycount[2];
4699 	REGISTER PCOMP *pc;
4700 	REGISTER NODESIZE *newnodesizes, *ns0, *ns1;
4701 	REGISTER BOOLEAN firsttime;
4702 	float diff, bestdiff, wantlength, wantwidth;
4703 
4704 	for(f=0; f<2; f++)
4705 	{
4706 		sizearraycount[f] = 0;
4707 		for(i=0; i<sg->cellcount[f]; i++)
4708 		{
4709 			pc = (PCOMP *)sg->celllist[f][i];
4710 			if ((pc->flags&(COMPHASWIDLEN|COMPHASAREA)) == 0) continue;
4711 			if (sizearraycount[f] >= net_sizearraytotal[f])
4712 			{
4713 				newtotal = net_sizearraytotal[f] * 2;
4714 				if (sizearraycount[f] >= newtotal) newtotal = sizearraycount[f] + 20;
4715 				newnodesizes = (NODESIZE *)emalloc(newtotal * (sizeof (NODESIZE)), net_tool->cluster);
4716 				if (newnodesizes == 0) return(FALSE);
4717 				for(j=0; j<sizearraycount[f]; j++)
4718 					newnodesizes[j] = net_sizearray[f][j];
4719 				if (net_sizearraytotal[f] > 0) efree((CHAR *)net_sizearray[f]);
4720 				net_sizearray[f] = newnodesizes;
4721 				net_sizearraytotal[f] = newtotal;
4722 			}
4723 			j = sizearraycount[f]++;
4724 			if ((pc->flags&COMPHASWIDLEN) != 0)
4725 			{
4726 				net_sizearray[f][j].length = pc->length;
4727 				net_sizearray[f][j].width = pc->width;
4728 			} else
4729 			{
4730 				net_sizearray[f][j].length = pc->length;
4731 				net_sizearray[f][j].width = 0.0;
4732 			}
4733 		}
4734 		if (sizearraycount[f] > 0)
4735 			esort(net_sizearray[f], sizearraycount[f], sizeof (NODESIZE), net_sortsizearray);
4736 	}
4737 
4738 	/* now find the two values that are closest */
4739 	p0 = p1 = bestind0 = bestind1 = 0;
4740 	firsttime = TRUE;
4741 	for(;;)
4742 	{
4743 		if (p0 >= sizearraycount[0]) break;
4744 		if (p1 >= sizearraycount[1]) break;
4745 
4746 		ns0 = &net_sizearray[0][p0];
4747 		ns1 = &net_sizearray[1][p1];
4748 		diff = (float)(fabs(ns0->length-ns1->length) + fabs(ns0->width-ns1->width));
4749 		if (firsttime || diff < bestdiff)
4750 		{
4751 			bestdiff = diff;
4752 			bestind0 = p0;
4753 			bestind1 = p1;
4754 			firsttime = FALSE;
4755 		}
4756 		if (ns0->length + ns0->width < ns1->length + ns1->width) p0++; else
4757 			p1++;
4758 	}
4759 	if (firsttime) return(FALSE);
4760 
4761 	/* found the two closest values: see if they are indeed close */
4762 	ns0 = &net_sizearray[0][bestind0];
4763 	ns1 = &net_sizearray[1][bestind1];
4764 	if (!net_componentequalvalue(ns0->length, ns1->length) ||
4765 		!net_componentequalvalue(ns0->width, ns1->width)) return(FALSE);
4766 	wantlength = (ns0->length + ns1->length) / 2.0f;
4767 	wantwidth = (ns0->width + ns1->width) / 2.0f;
4768 
4769 	/* make sure these values distinguish */
4770 	ns0 = &net_sizearray[0][0];
4771 	ns1 = &net_sizearray[0][sizearraycount[0]-1];
4772 	if (net_componentequalvalue(ns0->length, wantlength) &&
4773 		net_componentequalvalue(ns1->length, wantlength) &&
4774 		net_componentequalvalue(ns0->width, wantwidth) &&
4775 		net_componentequalvalue(ns1->width, wantwidth)) return(FALSE);
4776 	ns0 = &net_sizearray[1][0];
4777 	ns1 = &net_sizearray[1][sizearraycount[1]-1];
4778 	if (net_componentequalvalue(ns0->length, wantlength) &&
4779 		net_componentequalvalue(ns1->length, wantlength) &&
4780 		net_componentequalvalue(ns0->width, wantwidth) &&
4781 		net_componentequalvalue(ns1->width, wantwidth)) return(FALSE);
4782 
4783 	/* return the size */
4784 	*sizew = wantwidth;
4785 	*sizel = wantlength;
4786 	return(TRUE);
4787 }
4788 
net_sortsizearray(const void * e1,const void * e2)4789 int net_sortsizearray(const void *e1, const void *e2)
4790 {
4791 	REGISTER NODESIZE *ns1, *ns2;
4792 	REGISTER float v1, v2;
4793 
4794 	ns1 = (NODESIZE *)e1;
4795 	ns2 = (NODESIZE *)e2;
4796 	v1 = ns1->length + ns1->width;
4797 	v2 = ns2->length + ns2->width;
4798 	if (floatsequal(v1, v2)) return(0);
4799 	if (v1 < v2) return(-1);
4800 	return(1);
4801 }
4802 
4803 /*
4804  * Routine to force a match between parts of symmetry group "sg".  If "sizefactorsplit" is
4805  * zero, then "c0" entries in "i0" in cell 0 and "c1" entries in "i1" in cell 1 are to be matched.
4806  * Otherwise, those components with size factor "sizew/sizel" are to be matched.
4807  */
net_forceamatch(SYMGROUP * sg,INTBIG c0,INTBIG * i0,INTBIG c1,INTBIG * i1,float sizew,float sizel,INTBIG verbose,BOOLEAN ignorepwrgnd)4808 void net_forceamatch(SYMGROUP *sg, INTBIG c0, INTBIG *i0, INTBIG c1, INTBIG *i1,
4809 	float sizew, float sizel, INTBIG verbose, BOOLEAN ignorepwrgnd)
4810 {
4811 	REGISTER SYMGROUP *sgnewc, *sgnewn;
4812 	REGISTER HASHTYPE hashvalue;
4813 	REGISTER BOOLEAN match;
4814 	REGISTER PNET *pn, *pn0, *pn1;
4815 	REGISTER PCOMP *pc, *pc0, *pc1;
4816 	REGISTER INTBIG i, f, ind;
4817 
4818 	if (sg->grouptype == SYMGROUPCOMP)
4819 	{
4820 		hashvalue = net_uniquesymmetrygrouphash(SYMGROUPCOMP);
4821 		sgnewc = net_newsymgroup(SYMGROUPCOMP, hashvalue, 0);
4822 
4823 		if (sizew == 0.0 && sizel == 0.0)
4824 		{
4825 			/* matching two like-named nodes */
4826 			for(i=0; i<c0; i++)
4827 			{
4828 				ind = i0[i];
4829 				pc0 = (PCOMP *)sg->celllist[0][ind];
4830 				net_removefromsymgroup(sg, 0, ind);
4831 				if (net_addtosymgroup(sgnewc, 0, (void *)pc0)) return;
4832 				pc0->symgroup = sgnewc;
4833 				pc0->hashvalue = hashvalue;
4834 				if (verbose != 0)
4835 					(void)reallocstring(&pc0->hashreason, x_("name matched"), net_tool->cluster);
4836 			}
4837 			for(i=0; i<c1; i++)
4838 			{
4839 				ind = i1[i];
4840 				pc1 = (PCOMP *)sg->celllist[1][ind];
4841 				net_removefromsymgroup(sg, 1, ind);
4842 				if (net_addtosymgroup(sgnewc, 1, (void *)pc1)) return;
4843 				pc1->symgroup = sgnewc;
4844 				pc1->hashvalue = hashvalue;
4845 				if (verbose != 0)
4846 					(void)reallocstring(&pc1->hashreason, x_("name matched"), net_tool->cluster);
4847 			}
4848 		} else
4849 		{
4850 			/* matching nodes with size "sizefactor" */
4851 			for(f=0; f<2; f++)
4852 			{
4853 				for(i=sg->cellcount[f]-1; i>=0; i--)
4854 				{
4855 					pc = (PCOMP *)sg->celllist[f][i];
4856 					match = FALSE;
4857 					if ((pc->flags&COMPHASWIDLEN) != 0)
4858 					{
4859 						if (net_componentequalvalue(sizew, pc->width) &&
4860 							net_componentequalvalue(sizel, pc->length)) match = TRUE;
4861 					} else
4862 					{
4863 						if (net_componentequalvalue(sizel, pc->length)) match = TRUE;
4864 					}
4865 					if (match)
4866 					{
4867 						net_removefromsymgroup(sg, f, i);
4868 						if (net_addtosymgroup(sgnewc, f, (void *)pc)) return;
4869 						pc->symgroup = sgnewc;
4870 						pc->hashvalue = hashvalue;
4871 						if (verbose != 0)
4872 							(void)reallocstring(&pc->hashreason, x_("size matched"), net_tool->cluster);
4873 					}
4874 				}
4875 			}
4876 		}
4877 
4878 		/* set the remaining components in this symmetry group to a nonzero hash */
4879 		hashvalue = net_uniquesymmetrygrouphash(SYMGROUPCOMP);
4880 		sgnewc = net_newsymgroup(SYMGROUPCOMP, hashvalue, 0);
4881 		for(f=0; f<2; f++)
4882 		{
4883 			for(i=sg->cellcount[f]-1; i>=0; i--)
4884 			{
4885 				pc = (PCOMP *)sg->celllist[f][i];
4886 				net_removefromsymgroup(sg, f, i);
4887 				if (net_addtosymgroup(sgnewc, f, (void *)pc)) return;
4888 				pc->symgroup = sgnewc;
4889 				pc->hashvalue = hashvalue;
4890 				if (verbose != 0)
4891 					(void)reallocstring(&pc->hashreason, x_("redeemed"), net_tool->cluster);
4892 			}
4893 		}
4894 		sgnewn = NOSYMGROUP;
4895 	} else
4896 	{
4897 		hashvalue = net_uniquesymmetrygrouphash(SYMGROUPNET);
4898 		sgnewn = net_newsymgroup(SYMGROUPNET, hashvalue, 0);
4899 
4900 		for(i=0; i<c0; i++)
4901 		{
4902 			ind = i0[i];
4903 			pn0 = (PNET *)sg->celllist[0][ind];
4904 			net_removefromsymgroup(sg, 0, ind);
4905 			if (net_addtosymgroup(sgnewn, 0, (void *)pn0)) return;
4906 			pn0->symgroup = sgnewn;
4907 			pn0->hashvalue = hashvalue;
4908 			if (verbose != 0)
4909 				(void)reallocstring(&pn0->hashreason, x_("export matched"), net_tool->cluster);
4910 		}
4911 		for(i=0; i<c1; i++)
4912 		{
4913 			ind = i1[i];
4914 			pn1 = (PNET *)sg->celllist[1][ind];
4915 			net_removefromsymgroup(sg, 1, ind);
4916 			if (net_addtosymgroup(sgnewn, 1, (void *)pn1)) return;
4917 			pn1->symgroup = sgnewn;
4918 			pn1->hashvalue = hashvalue;
4919 			if (verbose != 0)
4920 				(void)reallocstring(&pn1->hashreason, x_("export matched"), net_tool->cluster);
4921 		}
4922 
4923 		/* set the remaining nets in this symmetry group to a nonzero hash */
4924 		hashvalue = net_uniquesymmetrygrouphash(SYMGROUPNET);
4925 		sgnewn = net_newsymgroup(SYMGROUPNET, hashvalue, 0);
4926 		for(f=0; f<2; f++)
4927 		{
4928 			for(i=sg->cellcount[f]-1; i>=0; i--)
4929 			{
4930 				pn = (PNET *)sg->celllist[f][i];
4931 				net_removefromsymgroup(sg, f, i);
4932 				if (net_addtosymgroup(sgnewn, f, (void *)pn)) return;
4933 				pn->symgroup = sgnewn;
4934 				pn->hashvalue = hashvalue;
4935 				if (verbose != 0)
4936 					(void)reallocstring(&pn->hashreason, x_("redeemed"), net_tool->cluster);
4937 			}
4938 		}
4939 		sgnewc = NOSYMGROUP;
4940 	}
4941 
4942 	net_redeemzerogroups(sgnewc, sgnewn, verbose, ignorepwrgnd);
4943 }
4944 
net_redeemzerogroups(SYMGROUP * sgnewc,SYMGROUP * sgnewn,INTBIG verbose,BOOLEAN ignorepwrgnd)4945 void net_redeemzerogroups(SYMGROUP *sgnewc, SYMGROUP *sgnewn, INTBIG verbose, BOOLEAN ignorepwrgnd)
4946 {
4947 	REGISTER SYMGROUP *sg;
4948 	REGISTER HASHTYPE hashvalue;
4949 	REGISTER PNET *pn;
4950 	REGISTER PCOMP *pc;
4951 	REGISTER INTBIG i, f;
4952 
4953 	/* redeem all zero-symmetry groups */
4954 	sg = net_findsymmetrygroup(SYMGROUPNET, 0, 0);
4955 	if (sg != NOSYMGROUP)
4956 	{
4957 		if (sgnewn == NOSYMGROUP)
4958 		{
4959 			hashvalue = net_uniquesymmetrygrouphash(SYMGROUPNET);
4960 			sgnewn = net_newsymgroup(SYMGROUPNET, hashvalue, 0);
4961 		}
4962 		for(f=0; f<2; f++)
4963 		{
4964 			for(i=sg->cellcount[f]-1; i>=0; i--)
4965 			{
4966 				pn = (PNET *)sg->celllist[f][i];
4967 				if (ignorepwrgnd && (pn->flags&(POWERNET|GROUNDNET)) != 0) continue;
4968 				net_removefromsymgroup(sg, f, i);
4969 				if (net_addtosymgroup(sgnewn, f, (void *)pn)) return;
4970 				pn->symgroup = sgnewn;
4971 				pn->hashvalue = sgnewn->hashvalue;
4972 				if (verbose != 0)
4973 					(void)reallocstring(&pn->hashreason, x_("redeemed"), net_tool->cluster);
4974 			}
4975 		}
4976 	}
4977 
4978 	sg = net_findsymmetrygroup(SYMGROUPCOMP, 0, 0);
4979 	if (sg != NOSYMGROUP)
4980 	{
4981 		if (sgnewc == NOSYMGROUP)
4982 		{
4983 			hashvalue = net_uniquesymmetrygrouphash(SYMGROUPCOMP);
4984 			sgnewc = net_newsymgroup(SYMGROUPCOMP, hashvalue, 0);
4985 		}
4986 		for(f=0; f<2; f++)
4987 		{
4988 			for(i=sg->cellcount[f]-1; i>=0; i--)
4989 			{
4990 				pc = (PCOMP *)sg->celllist[f][i];
4991 				net_removefromsymgroup(sg, f, i);
4992 				if (net_addtosymgroup(sgnewc, f, (void *)pc)) return;
4993 				pc->symgroup = sgnewc;
4994 				pc->hashvalue = sgnewc->hashvalue;
4995 				if (verbose != 0)
4996 					(void)reallocstring(&pc->hashreason, x_("redeemed"), net_tool->cluster);
4997 			}
4998 		}
4999 	}
5000 }
5001 
5002 /*
5003  * Routine to return a string describing size factor "sizefactor".
5004  */
net_describesizefactor(float sizew,float sizel)5005 CHAR *net_describesizefactor(float sizew, float sizel)
5006 {
5007 	static CHAR sizedesc[80];
5008 
5009 	if (sizew == 0)
5010 	{
5011 		esnprintf(sizedesc, 80, x_("%g"), sizel/WHOLE);
5012 	} else
5013 	{
5014 		esnprintf(sizedesc, 80, x_("%gx%g"), sizew/WHOLE, sizel/WHOLE);
5015 	}
5016 	return(sizedesc);
5017 }
5018 
5019 /******************************** HASH CODE EVALUATION ********************************/
5020 
5021 /*
5022  * Routine to return a hash code for component "pc".
5023  */
net_getcomphash(PCOMP * pc,INTBIG verbose)5024 HASHTYPE net_getcomphash(PCOMP *pc, INTBIG verbose)
5025 {
5026 	REGISTER INTBIG function, i, portfactor;
5027 	REGISTER HASHTYPE hashvalue;
5028 	REGISTER NODEINST *ni;
5029 	REGISTER NETWORK *net;
5030 	REGISTER PNET *pn;
5031 	REGISTER PORTPROTO *pp;
5032 	REGISTER void *infstr = 0;
5033 
5034 	/* get the node's function */
5035 	if (pc->numactual == 1) ni = (NODEINST *)pc->actuallist; else
5036 		ni = ((NODEINST **)pc->actuallist)[0];
5037 	if (ni->proto->primindex == 0)
5038 	{
5039 		/* if the function is overridden for the cell, use it */
5040 		if (pc->function != NPUNKNOWN) function = pc->function; else
5041 		{
5042 			/* a cell instance: use the node's prototype address */
5043 			function = (INTBIG)(ni->proto->temp1 & NETCELLCODE) >> NETCELLCODESH;
5044 		}
5045 	} else
5046 	{
5047 		/* a primitive: use the node's function */
5048 		function = pc->function;
5049 	}
5050 
5051 	/* initialize the hash factor */
5052 	hashvalue = function * net_functionMultiplier + pc->forcedassociation;
5053 
5054 	if (verbose != 0)
5055 	{
5056 		/* compute new hash values and store an explanation */
5057 		infstr = initinfstr();
5058 		formatinfstr(infstr, x_("%ld(fun)"), function);
5059 		if (pc->forcedassociation != 0)
5060 			formatinfstr(infstr, x_("+%ld(force)"), pc->forcedassociation);
5061 
5062 		/* now add in all networks as a function of the port's network */
5063 		for(i=0; i<pc->wirecount; i++)
5064 		{
5065 			pp = pc->portlist[i];
5066 			pn = pc->netnumbers[i];
5067 			portfactor = pc->portindices[i];
5068 			hashvalue += (portfactor * net_portNetFactorMultiplier) *
5069 				(pn->hashvalue * net_portHashFactorMultiplier);
5070 			net = pn->network;
5071 			if (net == NONETWORK)
5072 			{
5073 				formatinfstr(infstr, x_(" + %ld[%s]"), portfactor, pp->protoname);
5074 			} else
5075 			{
5076 				formatinfstr(infstr, x_(" + %ld(%s)"), portfactor, describenetwork(net));
5077 			}
5078 			formatinfstr(infstr, x_("x%s(hash)"), hugeinttoa(pn->hashvalue));
5079 		}
5080 		(void)reallocstring(&pc->hashreason, returninfstr(infstr), net_tool->cluster);
5081 	} else
5082 	{
5083 		/* now add in all networks as a function of the port's network */
5084 		for(i=0; i<pc->wirecount; i++)
5085 		{
5086 			pn = pc->netnumbers[i];
5087 			portfactor = pc->portindices[i];
5088 			hashvalue += (portfactor * net_portNetFactorMultiplier) *
5089 				(pn->hashvalue * net_portHashFactorMultiplier);
5090 		}
5091 	}
5092 	return(hashvalue);
5093 }
5094 
5095 /*
5096  * Routine to return a hash code for network "pn".
5097  */
net_getnethash(PNET * pn,INTBIG verbose)5098 HASHTYPE net_getnethash(PNET *pn, INTBIG verbose)
5099 {
5100 	REGISTER INTBIG i, index, portfactor;
5101 	REGISTER BOOLEAN validcomponents;
5102 	REGISTER HASHTYPE hashvalue;
5103 	REGISTER PCOMP *pc;
5104 	REGISTER void *infstr=0;
5105 
5106 	/* initialize the hash factor */
5107 	hashvalue = 0;
5108 
5109 	/* start with the number of components on this net */
5110 	hashvalue += (pn->nodecount+1) * net_nodeCountMultiplier + pn->forcedassociation;
5111 
5112 	validcomponents = FALSE;
5113 	if (verbose != 0)
5114 	{
5115 		infstr = initinfstr();
5116 		formatinfstr(infstr, x_("%ld(cnt)"), pn->nodecount);
5117 		if (pn->forcedassociation != 0)
5118 			formatinfstr(infstr, x_("+%ld(force)"), pn->forcedassociation);
5119 
5120 		/* add in information for each component */
5121 		for(i=0; i<pn->nodecount; i++)
5122 		{
5123 			pc = pn->nodelist[i];
5124 			index = pn->nodewire[i];
5125 			portfactor = pc->portindices[index];
5126 			hashvalue += portfactor * net_portFactorMultiplier * pc->hashvalue;
5127 			if (pc->hashvalue != 0) validcomponents = TRUE;
5128 			formatinfstr(infstr, x_(" + %ld(port)x%s(hash)"), portfactor, hugeinttoa(pc->hashvalue));
5129 		}
5130 
5131 		/* if no components had valid hash values, make this net zero */
5132 		if (!validcomponents && pn->nodecount != 0) hashvalue = 0;
5133 		(void)reallocstring(&pn->hashreason, returninfstr(infstr), net_tool->cluster);
5134 	} else
5135 	{
5136 		for(i=0; i<pn->nodecount; i++)
5137 		{
5138 			pc = pn->nodelist[i];
5139 			index = pn->nodewire[i];
5140 			portfactor = pc->portindices[index];
5141 			hashvalue += portfactor * net_portFactorMultiplier * pc->hashvalue;
5142 			if (pc->hashvalue != 0) validcomponents = TRUE;
5143 		}
5144 
5145 		/* if no components had valid hash values, make this net zero */
5146 		if (!validcomponents && pn->nodecount != 0) hashvalue = 0;
5147 	}
5148 
5149 	return(hashvalue);
5150 }
5151 
5152 /******************************** DEBUGGING ********************************/
5153 
5154 /*
5155  * Routine to show the components in group "sg" because they have been matched.
5156  */
net_showmatchedgroup(SYMGROUP * sg)5157 void net_showmatchedgroup(SYMGROUP *sg)
5158 {
5159 	NODEPROTO *topcell;
5160 	NODEINST *ni;
5161 	XARRAY xf, xformtrans, temp;
5162 	INTBIG xp1, yp1, xp2, yp2, xp3, yp3, xp4, yp4, plx, phx, ply, phy;
5163 	REGISTER INTBIG f, j, lx, hx, ly, hy;
5164 	REGISTER PCOMP *pc;
5165 	REGISTER WINDOWPART *w;
5166 
5167 	us_hbox.col = HIGHLIT;
5168 
5169 	/* show the components in this group if requested */
5170 	if (sg->grouptype == SYMGROUPCOMP)
5171 	{
5172 		for(f=0; f<2; f++)
5173 		{
5174 			pc = (PCOMP *)sg->celllist[f][0];
5175 			transid(xf);
5176 			if (pc->numactual == 1) ni = (NODEINST *)pc->actuallist; else
5177 				ni = ((NODEINST **)pc->actuallist)[0];
5178 			topcell = ni->parent;
5179 			for(j=pc->hierpathcount-1; j>=0; j--)
5180 			{
5181 				ni = pc->hierpath[j];
5182 				if (ni->proto->cellview == el_iconview) break;
5183 				topcell = ni->parent;
5184 				maketrans(ni, xformtrans);
5185 				transmult(xformtrans, xf, temp);
5186 				makerot(ni, xformtrans);
5187 				transmult(xformtrans, temp, xf);
5188 			}
5189 			if (j >= 0) continue;
5190 			for(j=0; j<pc->numactual; j++)
5191 			{
5192 				if (pc->numactual == 1) ni = (NODEINST *)pc->actuallist; else
5193 					ni = ((NODEINST **)pc->actuallist)[j];
5194 				makerot(ni, xformtrans);
5195 				transmult(xformtrans, xf, temp);
5196 				nodesizeoffset(ni, &plx, &ply, &phx, &phy);
5197 				lx = ni->lowx+plx;   hx = ni->highx-phx;
5198 				ly = ni->lowy+ply;   hy = ni->highy-phy;
5199 				xform(lx, ly, &xp1, &yp1, temp);
5200 				xform(hx, hy, &xp2, &yp2, temp);
5201 				xform(lx, hy, &xp3, &yp3, temp);
5202 				xform(hx, ly, &xp4, &yp4, temp);
5203 				for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
5204 				{
5205 					if (w->curnodeproto != topcell) continue;
5206 					xp1 = applyxscale(w, xp1-w->screenlx) + w->uselx;
5207 					yp1 = applyyscale(w, yp1-w->screenly) + w->usely;
5208 					xp2 = applyxscale(w, xp2-w->screenlx) + w->uselx;
5209 					yp2 = applyyscale(w, yp2-w->screenly) + w->usely;
5210 					screendrawline(w, xp1, yp1, xp2, yp2, &us_hbox, 0);
5211 					xp3 = applyxscale(w, xp3-w->screenlx) + w->uselx;
5212 					yp3 = applyyscale(w, yp3-w->screenly) + w->usely;
5213 					xp4 = applyxscale(w, xp4-w->screenlx) + w->uselx;
5214 					yp4 = applyyscale(w, yp4-w->screenly) + w->usely;
5215 					screendrawline(w, xp3, yp3, xp4, yp4, &us_hbox, 0);
5216 					flushscreen();
5217 				}
5218 			}
5219 		}
5220 	}
5221 }
5222 
5223 /*
5224  * Debugging routine to show the hash codes on all symmetry groups.
5225  */
net_showsymmetrygroups(INTBIG verbose,INTBIG type)5226 void net_showsymmetrygroups(INTBIG verbose, INTBIG type)
5227 {
5228 	WINDOWPART *win[2];
5229 	REGISTER WINDOWPART *w;
5230 	REGISTER INTBIG i, f;
5231 	UINTBIG descript[TEXTDESCRIPTSIZE];
5232 	REGISTER SYMGROUP *sg;
5233 	REGISTER PNET *pn;
5234 	REGISTER PCOMP *pc;
5235 
5236 	if ((verbose&NCCVERBOSEGRAPHICS) != 0)
5237 	{
5238 		/* find the windows associated with the cells */
5239 		win[0] = win[1] = NOWINDOWPART;
5240 		for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
5241 		{
5242 			for(f=0; f<2; f++)
5243 				if (w->curnodeproto == net_cell[f]) win[f] = w;
5244 		}
5245 		if (win[0] == NOWINDOWPART || win[1] == NOWINDOWPART) return;
5246 
5247 		/* clear all highlighting */
5248 		(void)asktool(us_tool, x_("clear"));
5249 		for(f=0; f<2; f++)
5250 			screendrawbox(win[f], win[f]->uselx, win[f]->usehx, win[f]->usely, win[f]->usehy,
5251 				&net_cleardesc);
5252 
5253 		TDCLEAR(descript);
5254 		TDSETSIZE(descript, TXTSETPOINTS(16));
5255 		screensettextinfo(win[0], NOTECHNOLOGY, descript);
5256 		screensettextinfo(win[1], NOTECHNOLOGY, descript);
5257 	}
5258 	for(sg = net_firstsymgroup; sg != NOSYMGROUP; sg = sg->nextsymgroup)
5259 	{
5260 		if (sg->grouptype != type) continue;
5261 		switch (sg->grouptype)
5262 		{
5263 			case SYMGROUPCOMP:
5264 				for(f=0; f<2; f++)
5265 				{
5266 					for(i=0; i<sg->cellcount[f]; i++)
5267 					{
5268 						pc = (PCOMP *)sg->celllist[f][i];
5269 						net_showcomphash(win[f], pc, pc->hashvalue, sg->groupindex, verbose);
5270 					}
5271 				}
5272 				break;
5273 			case SYMGROUPNET:
5274 				for(f=0; f<2; f++)
5275 				{
5276 					for(i=0; i<sg->cellcount[f]; i++)
5277 					{
5278 						pn = (PNET *)sg->celllist[f][i];
5279 						net_shownethash(win[f], pn, pn->hashvalue, sg->groupindex, verbose);
5280 					}
5281 				}
5282 				break;
5283 		}
5284 	}
5285 	for(sg = net_firstmatchedsymgroup; sg != NOSYMGROUP; sg = sg->nextsymgroup)
5286 	{
5287 		if (sg->grouptype != type) continue;
5288 		switch (sg->grouptype)
5289 		{
5290 			case SYMGROUPCOMP:
5291 				for(f=0; f<2; f++)
5292 				{
5293 					for(i=0; i<sg->cellcount[f]; i++)
5294 					{
5295 						pc = (PCOMP *)sg->celllist[f][i];
5296 						net_showcomphash(win[f], pc, pc->hashvalue, sg->groupindex, verbose);
5297 					}
5298 				}
5299 				break;
5300 			case SYMGROUPNET:
5301 				for(f=0; f<2; f++)
5302 				{
5303 					for(i=0; i<sg->cellcount[f]; i++)
5304 					{
5305 						pn = (PNET *)sg->celllist[f][i];
5306 						net_shownethash(win[f], pn, pn->hashvalue, sg->groupindex, verbose);
5307 					}
5308 				}
5309 				break;
5310 		}
5311 	}
5312 }
5313 
net_shownethash(WINDOWPART * win,PNET * pn,HASHTYPE hashvalue,INTBIG hashindex,INTBIG verbose)5314 void net_shownethash(WINDOWPART *win, PNET *pn, HASHTYPE hashvalue, INTBIG hashindex, INTBIG verbose)
5315 {
5316 	REGISTER NETWORK *net;
5317 	CHAR msg[50];
5318 	REGISTER PORTPROTO *pp;
5319 	REGISTER ARCINST *ai;
5320 	INTBIG tsx, tsy, px, py;
5321 	REGISTER INTBIG j;
5322 	INTBIG xp, yp;
5323 
5324 	net = pn->network;
5325 	if (net == NONETWORK) return;
5326 	if ((verbose&NCCVERBOSEGRAPHICS) != 0)
5327 	{
5328 		if (hashindex != 0) esnprintf(msg, 50, x_("%ld"), hashindex); else
5329 			estrcpy(msg, hugeinttoa(hashvalue));
5330 		screengettextsize(win, msg, &tsx, &tsy);
5331 		for(j=0; j<net->arccount; j++)
5332 		{
5333 			if (net->arctotal == 0) ai = (ARCINST *)net->arcaddr; else
5334 				ai = ((ARCINST **)net->arcaddr)[j];
5335 			if (ai->parent == win->curnodeproto)
5336 			{
5337 				xp = (ai->end[0].xpos + ai->end[1].xpos) / 2;
5338 				yp = (ai->end[0].ypos + ai->end[1].ypos) / 2;
5339 				if ((win->state&INPLACEEDIT) != 0)
5340 					xform(xp, yp, &xp, &yp, win->outofcell);
5341 				xp = applyxscale(win, xp-win->screenlx) + win->uselx;
5342 				yp = applyyscale(win, yp-win->screenly) + win->usely;
5343 				px = xp - tsx/2;   py = yp - tsy/2;
5344 				if (px < win->uselx) px = win->uselx;
5345 				if (px+tsx > win->usehx) px = win->usehx - tsx;
5346 				screendrawtext(win, px, py, msg, &net_msgdesc);
5347 			}
5348 		}
5349 		if (net->portcount > 0)
5350 		{
5351 			for(pp = win->curnodeproto->firstportproto; pp != NOPORTPROTO;
5352 				pp = pp->nextportproto)
5353 			{
5354 				if (pp->network != net) continue;
5355 				portposition(pp->subnodeinst, pp->subportproto, &xp, &yp);
5356 				if ((win->state&INPLACEEDIT) != 0)
5357 					xform(xp, yp, &xp, &yp, win->outofcell);
5358 				xp = applyxscale(win, xp-win->screenlx) + win->uselx;
5359 				yp = applyyscale(win, yp-win->screenly) + win->usely;
5360 				px = xp - tsx/2;   py = yp - tsy/2;
5361 				if (px < win->uselx) px = win->uselx;
5362 				if (px+tsx > win->usehx) px = win->usehx - tsx;
5363 				screendrawtext(win, px, py, msg, &net_msgdesc);
5364 			}
5365 		}
5366 	}
5367 	if ((verbose&NCCVERBOSETEXT) != 0)
5368 	{
5369 		if (hashindex != 0)
5370 		{
5371 			ttyputmsg(x_(" NETWORK %s:%s: %s (hash #%ld) = %s"), describenodeproto(net->parent),
5372 				net_describepnet(pn), hugeinttoa(hashvalue), hashindex, pn->hashreason);
5373 		} else
5374 		{
5375 			ttyputmsg(x_(" NETWORK %s:%s: %s (hash) = %s"), describenodeproto(net->parent),
5376 				net_describepnet(pn), hugeinttoa(hashvalue), pn->hashreason);
5377 		}
5378 	}
5379 }
5380 
net_showcomphash(WINDOWPART * win,PCOMP * pc,HASHTYPE hashvalue,INTBIG hashindex,INTBIG verbose)5381 void net_showcomphash(WINDOWPART *win, PCOMP *pc, HASHTYPE hashvalue, INTBIG hashindex, INTBIG verbose)
5382 {
5383 	INTBIG xp, yp;
5384 	INTBIG tsx, tsy, px, py;
5385 	REGISTER NODEINST *ni;
5386 	CHAR msg[50];
5387 
5388 	if (pc->numactual == 1) ni = (NODEINST *)pc->actuallist; else
5389 		ni = ((NODEINST **)pc->actuallist)[0];
5390 	if ((verbose&NCCVERBOSEGRAPHICS) != 0)
5391 	{
5392 		if (ni->parent == win->curnodeproto)
5393 		{
5394 			xp = (ni->lowx + ni->highx) / 2;
5395 			yp = (ni->lowy + ni->highy) / 2;
5396 			if ((win->state&INPLACEEDIT) != 0)
5397 				xform(xp, yp, &xp, &yp, win->outofcell);
5398 			xp = applyxscale(win, xp-win->screenlx) + win->uselx;
5399 			yp = applyyscale(win, yp-win->screenly) + win->usely;
5400 			if (hashindex != 0) esnprintf(msg, 50, x_("%ld"), hashindex); else
5401 				estrcpy(msg, hugeinttoa(hashvalue));
5402 			screengettextsize(win, msg, &tsx, &tsy);
5403 			px = xp - tsx/2;   py = yp - tsy/2;
5404 			if (px < win->uselx) px = win->uselx;
5405 			if (px+tsx > win->usehx) px = win->usehx - tsx;
5406 			screendrawtext(win, px, py, msg, &net_msgdesc);
5407 		}
5408 	}
5409 	if ((verbose&NCCVERBOSETEXT) != 0)
5410 	{
5411 		if (hashindex != 0)
5412 		{
5413 			ttyputmsg(x_(" COMPONENT %s: %s (hash #%ld) = %s"), describenodeinst(ni),
5414 				hugeinttoa(hashvalue), hashindex, pc->hashreason);
5415 		} else
5416 		{
5417 			ttyputmsg(x_(" COMPONENT %s: %s (hash) = %s"), describenodeinst(ni),
5418 				hugeinttoa(hashvalue), pc->hashreason);
5419 		}
5420 	}
5421 }
5422 
net_dumpnetwork(PCOMP * pclist,PNET * pnlist)5423 void net_dumpnetwork(PCOMP *pclist, PNET *pnlist)
5424 {
5425 	REGISTER PCOMP *pc;
5426 	REGISTER PNET *pn;
5427 	REGISTER INTBIG i;
5428 	REGISTER NODEINST *ni;
5429 	CHAR nettype[50];
5430 	REGISTER void *infstr;
5431 
5432 	ttyputmsg(x_("Nodes:"));
5433 	for(pc = pclist; pc != NOPCOMP; pc = pc->nextpcomp)
5434 	{
5435 		if (pc->numactual == 1) ni = (NODEINST *)pc->actuallist; else
5436 			ni = ((NODEINST **)pc->actuallist)[0];
5437 		ttyputmsg(x_("  Node %s (fun=%d)"), describenodeinst(ni), pc->function);
5438 	}
5439 	ttyputmsg(x_("Nets:"));
5440 	for(pn = pnlist; pn != NOPNET; pn = pn->nextpnet)
5441 	{
5442 		infstr = initinfstr();
5443 		nettype[0] = 0;
5444 		if ((pn->flags&(POWERNET|GROUNDNET)) != 0)
5445 		{
5446 			if ((pn->flags&POWERNET) != 0) estrcat(nettype, x_("POWER ")); else
5447 				estrcat(nettype, x_("GROUND "));
5448 		}
5449 		formatinfstr(infstr, x_("  %sNet %s (%ld nodes):"), nettype, net_describepnet(pn), pn->nodecount);
5450 		for(i=0; i<pn->nodecount; i++)
5451 		{
5452 			pc = pn->nodelist[i];
5453 			if (pc->numactual == 1) ni = (NODEINST *)pc->actuallist; else
5454 				ni = ((NODEINST **)pc->actuallist)[0];
5455 			formatinfstr(infstr, x_(" %s"), describenodeinst(ni));
5456 		}
5457 		ttyputmsg(x_("%s"), returninfstr(infstr));
5458 	}
5459 }
5460 
5461 /* NCC preanalysis */
5462 static DIALOGITEM net_nccpredialogitems[] =
5463 {
5464  /*  1 */ {0, {8,315,24,615}, MESSAGE, N_("Cell2")},
5465  /*  2 */ {0, {28,8,384,308}, SCROLL, x_("")},
5466  /*  3 */ {0, {8,8,24,308}, MESSAGE, N_("Cell1")},
5467  /*  4 */ {0, {28,315,384,615}, SCROLL, x_("")},
5468  /*  5 */ {0, {392,244,408,392}, RADIOA, N_("Components")},
5469  /*  6 */ {0, {416,20,432,212}, AUTOCHECK, N_("Tie lists vertically")},
5470  /*  7 */ {0, {416,244,432,392}, RADIOA, N_("Networks")},
5471  /*  8 */ {0, {392,20,408,212}, AUTOCHECK, N_("Show only differences")},
5472  /*  9 */ {0, {400,524,424,604}, DEFBUTTON, N_("Close")},
5473  /* 10 */ {0, {400,420,424,500}, BUTTON, N_("Compare")}
5474 };
5475 static DIALOG net_nccpredialog = {{75,75,516,700}, N_("NCC Preanalysis Results"), 0, 10, net_nccpredialogitems, x_("nccpre"), 0};
5476 
5477 /* special items for the "nccpre" dialog: */
5478 #define DNCP_CELL2NAME      1		/* cell 2 name (message) */
5479 #define DNCP_CELL1LIST      2		/* cell 1 list (scroll) */
5480 #define DNCP_CELL1NAME      3		/* cell 1 name (message) */
5481 #define DNCP_CELL2LIST      4		/* cell 2 list (scroll) */
5482 #define DNCP_SHOWCOMPS      5		/* Show components (radioa) */
5483 #define DNCP_TIEVERTICALLY  6		/* Tie lists vertically (autocheck) */
5484 #define DNCP_SHOWNETS       7		/* Show networks (radioa) */
5485 #define DNCP_SHOWDIFFS      8		/* Show only differences (autocheck) */
5486 #define DNCP_CLOSE          9		/* Close (defbutton) */
5487 #define DNCP_COMPARE       10		/* Compare (button) */
5488 
5489 class EDiaNetShowPreanalysis : public EDialogModeless
5490 {
5491 public:
5492 	EDiaNetShowPreanalysis();
5493 	void reset();
5494 	void setCells(NODEPROTO *cell1, PCOMP *aPcomp1, PNET *aNodelist1,
5495 		NODEPROTO *cell2, PCOMP *aPcomp2, PNET *aNodelist2, BOOLEAN aIgnorepwrgnd);
5496 	static EDiaNetShowPreanalysis *dialog;
5497 private:
5498 	void itemHitAction(INTBIG itemHit);
5499 	void putNetIntoDialog();
5500 	void putCompIntoDialog();
5501 
5502 	BOOLEAN vSynch;
5503 	BOOLEAN showComps;
5504 	BOOLEAN showAll;
5505 	PCOMP *pcomp1, *pcomp2;
5506 	PNET *nodelist1, *nodelist2;
5507 	BOOLEAN ignorepwrgnd;
5508 };
5509 
5510 EDiaNetShowPreanalysis *EDiaNetShowPreanalysis::dialog = 0;
5511 
5512 /*
5513  * Routine to show preanalysis results for cells
5514  */
net_showpreanalysis(NODEPROTO * cell1,PCOMP * pcomp1,PNET * nodelist1,NODEPROTO * cell2,PCOMP * pcomp2,PNET * nodelist2,BOOLEAN ignorepwrgnd)5515 void net_showpreanalysis(NODEPROTO *cell1, PCOMP *pcomp1, PNET *nodelist1,
5516 	NODEPROTO *cell2, PCOMP *pcomp2, PNET *nodelist2, BOOLEAN ignorepwrgnd)
5517 {
5518 	if (EDiaNetShowPreanalysis::dialog == 0) EDiaNetShowPreanalysis::dialog = new EDiaNetShowPreanalysis();
5519 	if (EDiaNetShowPreanalysis::dialog == 0) return;
5520 	EDiaNetShowPreanalysis::dialog->setCells(cell1, pcomp1, nodelist1, cell2, pcomp2, nodelist2, ignorepwrgnd);
5521 }
5522 
EDiaNetShowPreanalysis()5523 EDiaNetShowPreanalysis::EDiaNetShowPreanalysis()
5524 	: EDialogModeless(&net_nccpredialog)
5525 {
5526 	reset();
5527 }
5528 
reset()5529 void EDiaNetShowPreanalysis::reset()
5530 {
5531 	vSynch = TRUE;
5532 	showComps = FALSE;
5533 	showAll = FALSE;
5534 	pcomp1 = pcomp2 = 0;
5535 	nodelist1 = nodelist2 = 0;
5536 	ignorepwrgnd = FALSE;
5537 }
5538 
setCells(NODEPROTO * cell1,PCOMP * aPcomp1,PNET * aNodelist1,NODEPROTO * cell2,PCOMP * aPcomp2,PNET * aNodelist2,BOOLEAN aIgnorepwrgnd)5539 void EDiaNetShowPreanalysis::setCells(NODEPROTO *cell1, PCOMP *aPcomp1, PNET *aNodelist1,
5540 	NODEPROTO *cell2, PCOMP *aPcomp2, PNET *aNodelist2, BOOLEAN aIgnorepwrgnd)
5541 {
5542 	REGISTER PCOMP *pc;
5543 
5544 	show();
5545 
5546 	initTextDialog(DNCP_CELL1LIST, DiaNullDlogList, DiaNullDlogItem,
5547 		DiaNullDlogDone, -1, SCSELMOUSE|SCREPORT|SCHORIZBAR|SCSMALLFONT);
5548 	initTextDialog(DNCP_CELL2LIST, DiaNullDlogList, DiaNullDlogItem,
5549 		DiaNullDlogDone, -1, SCSELMOUSE|SCREPORT|SCHORIZBAR|SCSMALLFONT);
5550 
5551 	/* make the horizontally-aligned lists scroll together */
5552 	if (vSynch)
5553 	{
5554 		synchVScrolls(DNCP_CELL1LIST, DNCP_CELL2LIST, 0);
5555 		setControl(DNCP_TIEVERTICALLY, 1);
5556 	}
5557 	if (showComps) setControl(DNCP_SHOWCOMPS, 1); else setControl(DNCP_SHOWNETS, 1);
5558 	setControl(DNCP_SHOWDIFFS, !showAll);
5559 
5560 	setText(DNCP_CELL1NAME, describenodeproto(cell1));
5561 	setText(DNCP_CELL2NAME, describenodeproto(cell2));
5562 	pcomp1 = aPcomp1;   nodelist1 = aNodelist1;
5563 	pcomp2 = aPcomp2;   nodelist2 = aNodelist2;
5564 	ignorepwrgnd = aIgnorepwrgnd;
5565 
5566 	/* precache the component names */
5567 	for(pc = pcomp1; pc != NOPCOMP; pc = pc->nextpcomp)
5568 		allocstring(&pc->hashreason, net_describepcomp(pc), net_tool->cluster);
5569 	for(pc = pcomp2; pc != NOPCOMP; pc = pc->nextpcomp)
5570 		allocstring(&pc->hashreason, net_describepcomp(pc), net_tool->cluster);
5571 
5572 	if (showComps) putCompIntoDialog(); else putNetIntoDialog();
5573 }
5574 
5575 typedef struct
5576 {
5577 	INTBIG     pcount;
5578 	PORTPROTO *pp;
5579 	NETWORK   *net;
5580 } COMPPORT;
5581 
itemHitAction(INTBIG itemHit)5582 void EDiaNetShowPreanalysis::itemHitAction(INTBIG itemHit)
5583 {
5584 	REGISTER INTBIG i, j, total1, total2, pgcount1, pgcount2, i1, i2, maxlen, diff;
5585 	REGISTER BOOLEAN first;
5586 	REGISTER PCOMP *pc, *pc1, *pc2;
5587 	REGISTER NETWORK *net;
5588 	REGISTER PNET *pn;
5589 	REGISTER void *infstr;
5590 	REGISTER CHAR **netnames1, **netnames2, *pt1, *pt2;
5591 	REGISTER NODEPROTO *np1, *np2, *np1orig, *np2orig;
5592 	REGISTER PORTPROTO *pp1, *pp2;
5593 	REGISTER NODEINST *ni, *ni1, *ni2;
5594 	REGISTER ARCINST *ai;
5595 	REGISTER COMPPORT *comportlist1, *comportlist2;
5596 
5597 	if (itemHit == DNCP_CLOSE)
5598 	{
5599 		for(pc = pcomp1; pc != NOPCOMP; pc = pc->nextpcomp)
5600 		{
5601 			efree((CHAR *)pc->hashreason);
5602 			pc->hashreason = 0;
5603 		}
5604 		for(pc = pcomp2; pc != NOPCOMP; pc = pc->nextpcomp)
5605 		{
5606 			efree((CHAR *)pc->hashreason);
5607 			pc->hashreason = 0;
5608 		}
5609 		hide();
5610 		return;
5611 	}
5612 	if (itemHit == DNCP_COMPARE)
5613 	{
5614 		if (showComps)
5615 		{
5616 			i = getCurLine(DNCP_CELL1LIST);
5617 			for(pc1 = pcomp1; pc1 != NOPCOMP; pc1 = pc1->nextpcomp)
5618 				if (pc1->timestamp == i) break;
5619 			i = getCurLine(DNCP_CELL2LIST);
5620 			for(pc2 = pcomp2; pc2 != NOPCOMP; pc2 = pc2->nextpcomp)
5621 				if (pc2->timestamp == i) break;
5622 			if (pc1 == NOPCOMP || pc2 == NOPCOMP)
5623 			{
5624 				ttyputmsg(_("First select a component from each list"));
5625 				return;
5626 			}
5627 
5628 			/* compare components "pc1" and "pc2" */
5629 			if (pc1->numactual == 1) ni1 = ((NODEINST *)pc1->actuallist); else
5630 				ni1 = ((NODEINST **)pc1->actuallist)[0];
5631 			np1orig = ni1->proto;
5632 			np1 = contentsview(np1orig);
5633 			if (np1 == NONODEPROTO) np1 = np1orig;
5634 			if (pc2->numactual == 1) ni2 = ((NODEINST *)pc2->actuallist); else
5635 				ni2 = ((NODEINST **)pc2->actuallist)[0];
5636 			np2orig = ni2->proto;
5637 			np2 = contentsview(np2orig);
5638 			if (np2 == NONODEPROTO) np2 = np2orig;
5639 			infstr = initinfstr();
5640 			formatinfstr(infstr, _("Differences between component %s"), describenodeproto(np1));
5641 			if (pc1->numactual > 1) formatinfstr(infstr, _(" (%ld merged nodes)"), pc1->numactual);
5642 			formatinfstr(infstr, _(" and component %s"), describenodeproto(np2));
5643 			if (pc2->numactual > 1) formatinfstr(infstr, _(" (%ld merged nodes)"), pc2->numactual);
5644 			ttyputmsg(x_("%s"), returninfstr(infstr));
5645 
5646 			/* gather information about component "pc1" */
5647 			total1 = pgcount1 = 0;
5648 			comportlist1 = 0;
5649 			for(pp1 = np1->firstportproto; pp1 != NOPORTPROTO; pp1 = pp1->nextportproto)
5650 			{
5651 				if (portispower(pp1) || portisground(pp1))
5652 				{
5653 					if (pp1->network->buswidth <= 1) pgcount1++; else
5654 						pgcount1 += pp1->network->buswidth;
5655 					if (ignorepwrgnd) continue;
5656 				}
5657 				if (pp1->network->buswidth > 1)
5658 					total1 += pp1->network->buswidth;
5659 				total1++;
5660 			}
5661 			if (total1 > 0)
5662 			{
5663 				comportlist1 = (COMPPORT *)emalloc(total1 * (sizeof (COMPPORT)), net_tool->cluster);
5664 				if (comportlist1 == 0) return;
5665 				total1 = 0;
5666 				for(pp1 = np1->firstportproto; pp1 != NOPORTPROTO; pp1 = pp1->nextportproto)
5667 				{
5668 					if (ignorepwrgnd && (portispower(pp1) || portisground(pp1))) continue;
5669 					comportlist1[total1].pp = equivalentport(np1, pp1, np1orig);
5670 					comportlist1[total1++].net = pp1->network;
5671 					if (pp1->network->buswidth > 1)
5672 					{
5673 						for(i=0; i<pp1->network->buswidth; i++)
5674 						{
5675 							comportlist1[total1].pp = NOPORTPROTO;
5676 							comportlist1[total1++].net = pp1->network->networklist[i];
5677 						}
5678 					}
5679 				}
5680 			}
5681 			for(i=0; i<total1; i++) comportlist1[i].pcount = 0;
5682 			for(i=0; i<pc1->wirecount; i++)
5683 			{
5684 				pp1 = pc1->portlist[i];
5685 				for(j=0; j<total1; j++)
5686 				{
5687 					if (comportlist1[j].pp == pp1) comportlist1[j].pcount++;
5688 				}
5689 			}
5690 
5691 			/* gather information about component "pc2" */
5692 			total2 = pgcount2 = 0;
5693 			comportlist2 = 0;
5694 			for(pp2 = np2->firstportproto; pp2 != NOPORTPROTO; pp2 = pp2->nextportproto)
5695 			{
5696 				if (portispower(pp2) || portisground(pp2))
5697 				{
5698 					if (pp2->network->buswidth <= 1) pgcount2++; else
5699 						pgcount2 += pp2->network->buswidth;
5700 					if (ignorepwrgnd) continue;
5701 				}
5702 				total2++;
5703 				if (pp2->network->buswidth > 1)
5704 					total2 += pp2->network->buswidth;
5705 			}
5706 			if (total2 > 0)
5707 			{
5708 				comportlist2 = (COMPPORT *)emalloc(total2 * (sizeof (COMPPORT)), net_tool->cluster);
5709 				if (comportlist2 == 0) return;
5710 				total2 = 0;
5711 				for(pp2 = np2->firstportproto; pp2 != NOPORTPROTO; pp2 = pp2->nextportproto)
5712 				{
5713 					if (ignorepwrgnd && (portispower(pp2) || portisground(pp2))) continue;
5714 					comportlist2[total2].pp = equivalentport(np2, pp2, np2orig);
5715 					comportlist2[total2++].net = pp2->network;
5716 					if (pp2->network->buswidth > 1)
5717 					{
5718 						for(i=0; i<pp2->network->buswidth; i++)
5719 						{
5720 							comportlist2[total2].pp = NOPORTPROTO;
5721 							comportlist2[total2++].net = pp2->network->networklist[i];
5722 						}
5723 					}
5724 				}
5725 			}
5726 			for(i=0; i<total2; i++) comportlist2[i].pcount = 0;
5727 			for(i=0; i<pc2->wirecount; i++)
5728 			{
5729 				pp2 = pc2->portlist[i];
5730 				for(j=0; j<total2; j++)
5731 				{
5732 					if (comportlist2[j].pp == pp2) comportlist2[j].pcount++;
5733 				}
5734 			}
5735 
5736 			/* analyze results */
5737 			if (pgcount1 != pgcount2)
5738 				ttyputmsg(_("  Cells %s has %ld power-and-ground exports but cell %s has %ld"),
5739 					describenodeproto(np1), pgcount1, describenodeproto(np2), pgcount2);
5740 
5741 			for(i=0; i<total1; i++)
5742 			{
5743 				if (comportlist1[i].net->buswidth > 1) continue;
5744 				for(j=0; j<total2; j++)
5745 					if (net_samenetworkname(comportlist1[i].net, comportlist2[j].net)) break;
5746 				if (j < total2) continue;
5747 				ttyputmsg(_("  Export %s exists only in %s"), describenetwork(comportlist1[i].net),
5748 					describenodeproto(np1));
5749 			}
5750 			for(i=0; i<total2; i++)
5751 			{
5752 				if (comportlist2[i].net->buswidth > 1) continue;
5753 				for(j=0; j<total1; j++)
5754 					if (net_samenetworkname(comportlist2[i].net, comportlist1[j].net)) break;
5755 				if (j < total1) continue;
5756 				ttyputmsg(_("  Export %s exists only in %s"), describenetwork(comportlist2[i].net),
5757 					describenodeproto(np2));
5758 			}
5759 			if (total1 > 0) efree((CHAR *)comportlist1);
5760 			if (total2 > 0) efree((CHAR *)comportlist2);
5761 			ttyputmsg(_("End of comparison"));
5762 
5763 
5764 			/* now do another analysis */
5765 			total1 = 0;
5766 			netnames1 = 0;
5767 			for(i=0; i<pc1->wirecount; i++)
5768 			{
5769 				pn = pc1->netnumbers[i];
5770 				if (ignorepwrgnd && (pn->flags&(POWERNET|GROUNDNET)) != 0) continue;
5771 				total1++;
5772 			}
5773 			if (total1 > 0)
5774 			{
5775 				netnames1 = (CHAR **)emalloc(total1 * (sizeof (CHAR *)), net_tool->cluster);
5776 				if (netnames1 == 0) return;
5777 				total1 = 0;
5778 				for(i=0; i<pc1->wirecount; i++)
5779 				{
5780 					pn = pc1->netnumbers[i];
5781 					if (ignorepwrgnd && (pn->flags&(POWERNET|GROUNDNET)) != 0) continue;
5782 					netnames1[total1++] = pc1->portlist[i]->protoname;
5783 				}
5784 			}
5785 			esort(netnames1, total1, sizeof (CHAR *), sort_stringascending);
5786 
5787 			total2 = 0;
5788 			netnames2 = 0;
5789 			for(i=0; i<pc2->wirecount; i++)
5790 			{
5791 				pn = pc2->netnumbers[i];
5792 				if (ignorepwrgnd && (pn->flags&(POWERNET|GROUNDNET)) != 0) continue;
5793 				total2++;
5794 			}
5795 			if (total2 > 0)
5796 			{
5797 				netnames2 = (CHAR **)emalloc(total2 * (sizeof (CHAR *)), net_tool->cluster);
5798 				if (netnames2 == 0) return;
5799 				total2 = 0;
5800 				for(i=0; i<pc2->wirecount; i++)
5801 				{
5802 					pn = pc2->netnumbers[i];
5803 					if (ignorepwrgnd && (pn->flags&(POWERNET|GROUNDNET)) != 0) continue;
5804 					netnames2[total2++] = pc2->portlist[i]->protoname;
5805 				}
5806 			}
5807 			esort(netnames2, total2, sizeof (CHAR *), sort_stringascending);
5808 
5809 			ttyputmsg(_("Comparison of wires on nodes %s and %s"), describenodeinst(ni1),
5810 				describenodeinst(ni2));
5811 			maxlen = estrlen(describenodeinst(ni1));
5812 			for(i=0; i<total1; i++)
5813 				maxlen = maxi(maxlen, estrlen(netnames1[i]));
5814 			maxlen += 5;
5815 			infstr = initinfstr();
5816 			addstringtoinfstr(infstr, describenodeinst(ni1));
5817 			for(i=estrlen(describenodeinst(ni1)); i<maxlen; i++) addtoinfstr(infstr, ' ');
5818 			addstringtoinfstr(infstr, describenodeinst(ni2));
5819 			ttyputmsg(x_("%s"), returninfstr(infstr));
5820 
5821 			i1 = i2 = 0;
5822 			for(;;)
5823 			{
5824 				if (i1 >= total1 && i2 >= total2) break;
5825 				if (i1 < total1) pt1 = netnames1[i1]; else
5826 					pt1 = x_("");
5827 				if (i2 < total2) pt2 = netnames2[i2]; else
5828 					pt2 = x_("");
5829 				infstr = initinfstr();
5830 				diff = namesame(pt1, pt2);
5831 				if (diff < 0 && i1 >= total1) diff = 1;
5832 				if (diff > 0 && i2 >= total2) diff = -1;
5833 				if (diff == 0)
5834 				{
5835 					addstringtoinfstr(infstr, pt1);
5836 					for(i=estrlen(pt1); i<maxlen; i++) addtoinfstr(infstr, ' ');
5837 					addstringtoinfstr(infstr, pt2);
5838 					if (i1 < total1) i1++;
5839 					if (i2 < total2) i2++;
5840 				} else if (diff < 0)
5841 				{
5842 					addstringtoinfstr(infstr, pt1);
5843 					i1++;
5844 				} else
5845 				{
5846 					for(i=0; i<maxlen; i++) addtoinfstr(infstr, ' ');
5847 					addstringtoinfstr(infstr, pt2);
5848 					i2++;
5849 				}
5850 				ttyputmsg(x_("%s"), returninfstr(infstr));
5851 			}
5852 			if (total1 > 0) efree((CHAR *)netnames1);
5853 			if (total2 > 0) efree((CHAR *)netnames2);
5854 		} else
5855 		{
5856 			ttyputmsg(_("Can only compare components"));
5857 		}
5858 		return;
5859 	}
5860 	if (itemHit == DNCP_TIEVERTICALLY)
5861 	{
5862 		vSynch = getControl(DNCP_TIEVERTICALLY) != 0;
5863 		if (vSynch) synchVScrolls(DNCP_CELL1LIST, DNCP_CELL2LIST, 0); else unSynchVScrolls();
5864 		return;
5865 	}
5866 	if (itemHit == DNCP_SHOWCOMPS || itemHit == DNCP_SHOWNETS || itemHit == DNCP_SHOWDIFFS)
5867 	{
5868 		showComps = getControl(DNCP_SHOWCOMPS) != 0;
5869 		showAll = !getControl(DNCP_SHOWDIFFS);
5870 		if (showComps) putCompIntoDialog(); else putNetIntoDialog();
5871 		return;
5872 	}
5873 	if (itemHit == DNCP_CELL1LIST || itemHit == DNCP_CELL2LIST)
5874 	{
5875 		(void)asktool(us_tool, x_("clear"));
5876 		infstr = initinfstr();
5877 		first = TRUE;
5878 		i = getCurLine(itemHit);
5879 		if (showComps)
5880 		{
5881 			for(pc = (itemHit == DNCP_CELL1LIST ? pcomp1 : pcomp2); pc != NOPCOMP; pc = pc->nextpcomp)
5882 			{
5883 				if (pc->timestamp != i) continue;
5884 				for(j=0; j<pc->numactual; j++)
5885 				{
5886 					if (pc->numactual == 1) ni = (NODEINST *)pc->actuallist; else
5887 						ni = ((NODEINST **)pc->actuallist)[j];
5888 					if (first) first = FALSE; else
5889 						addtoinfstr(infstr, '\n');
5890 					formatinfstr(infstr, x_("CELL=%s FROM=0%lo;-1;0;NOBBOX"),
5891 						describenodeproto(ni->parent), (INTBIG)ni->geom);
5892 				}
5893 			}
5894 		} else
5895 		{
5896 			for(pn = (itemHit == DNCP_CELL1LIST ? nodelist1 : nodelist2); pn != NOPNET; pn = pn->nextpnet)
5897 			{
5898 				if (pn->timestamp != i) continue;
5899 				net = pn->network;
5900 				if (net == NONETWORK) continue;
5901 				for(ai = net->parent->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
5902 				{
5903 					if (ai->network != net)
5904 					{
5905 						if (ai->network->buswidth <= 1) continue;
5906 						for(j=0; j<ai->network->buswidth; j++)
5907 							if (ai->network->networklist[j] == net) break;
5908 						if (j >= ai->network->buswidth) continue;
5909 					}
5910 					if (first) first = FALSE; else
5911 						addtoinfstr(infstr, '\n');
5912 					formatinfstr(infstr, x_("CELL=%s FROM=0%lo;-1;0;NOBBOX"),
5913 						describenodeproto(ai->parent), (INTBIG)ai->geom);
5914 				}
5915 			}
5916 		}
5917 		(void)asktool(us_tool, x_("show-multiple"), (INTBIG)returninfstr(infstr));
5918 		return;
5919 	}
5920 }
5921 
putNetIntoDialog()5922 void EDiaNetShowPreanalysis::putNetIntoDialog()
5923 {
5924 	REGISTER INTBIG pn1total, pn2total, ind1, ind2, i, maxwirecount,
5925 		reportedwid, curwid, line1, line2, *wirecountlist, numin1, numin2;
5926 	CHAR *pt, line[200];
5927 	REGISTER PNET **pn1list, **pn2list, *pn, *pn1, *pn2;
5928 	REGISTER NETWORK *net1, *net2;
5929 	REGISTER NODEPROTO *np1, *np2;
5930 	REGISTER void *infstr;
5931 
5932 	/* clear the scroll areas */
5933 	loadTextDialog(DNCP_CELL1LIST, DiaNullDlogList, DiaNullDlogItem, DiaNullDlogDone, -1);
5934 	loadTextDialog(DNCP_CELL2LIST, DiaNullDlogList, DiaNullDlogItem, DiaNullDlogDone, -1);
5935 
5936 	/* show the networks */
5937 	pn1total = 0;
5938 	pn1list = 0;
5939 	for(pn = nodelist1; pn != NOPNET; pn = pn->nextpnet)
5940 	{
5941 		pn->timestamp = -1;
5942 		pn1total++;
5943 	}
5944 	if (pn1total > 0)
5945 	{
5946 		pn1list = (PNET **)emalloc(pn1total * (sizeof (PNET *)), net_tool->cluster);
5947 		if (pn1list == 0) return;
5948 		pn1total = 0;
5949 		for(pn = nodelist1; pn != NOPNET; pn = pn->nextpnet)
5950 			pn1list[pn1total++] = pn;
5951 		esort(pn1list, pn1total, sizeof (PNET *), net_sortpnet);
5952 	}
5953 
5954 	/* count the number of networks in the second cell */
5955 	pn2total = 0;
5956 	pn2list = 0;
5957 	for(pn = nodelist2; pn != NOPNET; pn = pn->nextpnet)
5958 	{
5959 		pn->timestamp = -1;
5960 		pn2total++;
5961 	}
5962 	if (pn2total > 0)
5963 	{
5964 		pn2list = (PNET **)emalloc(pn2total * (sizeof (PNET *)), net_tool->cluster);
5965 		if (pn2list == 0) return;
5966 		pn2total = 0;
5967 		for(pn = nodelist2; pn != NOPNET; pn = pn->nextpnet)
5968 			pn2list[pn2total++] = pn;
5969 		esort(pn2list, pn2total, sizeof (PNET *), net_sortpnet);
5970 	}
5971 
5972 	/* if reducing to the obvious differences, remove all with a nodecount if numbers match */
5973 	if (!showAll)
5974 	{
5975 		maxwirecount = -1;
5976 		for(pn = nodelist1; pn != NOPNET; pn = pn->nextpnet)
5977 			if (pn->nodecount > maxwirecount) maxwirecount = pn->nodecount;
5978 		for(pn = nodelist2; pn != NOPNET; pn = pn->nextpnet)
5979 			if (pn->nodecount > maxwirecount) maxwirecount = pn->nodecount;
5980 		if (maxwirecount > 0)
5981 		{
5982 			wirecountlist = (INTBIG *)emalloc((maxwirecount+1) * SIZEOFINTBIG, el_tempcluster);
5983 			if (wirecountlist == 0) return;
5984 			for(i=0; i<=maxwirecount; i++) wirecountlist[i] = 0;
5985 			for(pn = nodelist1; pn != NOPNET; pn = pn->nextpnet)
5986 				wirecountlist[pn->nodecount] = 1;
5987 			for(pn = nodelist2; pn != NOPNET; pn = pn->nextpnet)
5988 				wirecountlist[pn->nodecount] = 1;
5989 			for(i=maxwirecount; i>=0; i--)
5990 			{
5991 				if (wirecountlist[i] == 0) continue;
5992 				for(ind1 = 0, pn = nodelist1; pn != NOPNET; pn = pn->nextpnet)
5993 					if (pn->nodecount == i) ind1++;
5994 				for(ind2 = 0, pn = nodelist2; pn != NOPNET; pn = pn->nextpnet)
5995 					if (pn->nodecount == i) ind2++;
5996 				if (ind1 != ind2) continue;
5997 				if (ind1 == 0) continue;
5998 				for(pn = nodelist1; pn != NOPNET; pn = pn->nextpnet)
5999 					if (pn->nodecount == i) pn->timestamp = 0;
6000 				for(pn = nodelist2; pn != NOPNET; pn = pn->nextpnet)
6001 					if (pn->nodecount == i) pn->timestamp = 0;
6002 			}
6003 			efree((CHAR *)wirecountlist);
6004 		}
6005 	}
6006 
6007 	ind1 = ind2 = 0;
6008 	line1 = line2 = 0;
6009 	reportedwid = -1;
6010 	for(;;)
6011 	{
6012 		if (ind1 >= pn1total) pn1 = NOPNET; else
6013 		{
6014 			pn1 = pn1list[ind1];
6015 			if (pn1->timestamp == 0)
6016 			{
6017 				ind1++;
6018 				pn1->timestamp = -1;
6019 				continue;
6020 			}
6021 			if (ignorepwrgnd && (pn1->flags&(POWERNET|GROUNDNET)) != 0)
6022 			{
6023 				ind1++;
6024 				pn1->timestamp = -1;
6025 				continue;
6026 			}
6027 		}
6028 		if (ind2 >= pn2total) pn2 = NOPNET; else
6029 		{
6030 			pn2 = pn2list[ind2];
6031 			if (pn2->timestamp == 0)
6032 			{
6033 				ind2++;
6034 				pn2->timestamp = -1;
6035 				continue;
6036 			}
6037 			if (ignorepwrgnd && (pn2->flags&(POWERNET|GROUNDNET)) != 0)
6038 			{
6039 				ind2++;
6040 				pn2->timestamp = -1;
6041 				continue;
6042 			}
6043 		}
6044 		if (pn1 == NOPNET && pn2 == NOPNET) break;
6045 		if (pn1 != NOPNET && pn2 != NOPNET)
6046 		{
6047 			if (pn1->nodecount < pn2->nodecount) pn1 = NOPNET; else
6048 				if (pn1->nodecount > pn2->nodecount) pn2 = NOPNET;
6049 		}
6050 		if (pn1 != NOPNET) curwid = pn1->nodecount; else curwid = pn2->nodecount;
6051 		if (curwid != reportedwid)
6052 		{
6053 			numin1 = 0;
6054 			if (pn1 != NOPNET)
6055 			{
6056 				for(i=ind1; i<pn1total; i++)
6057 				{
6058 					pn = pn1list[i];
6059 					if (pn->timestamp == 0) continue;
6060 					if (ignorepwrgnd && (pn->flags&(POWERNET|GROUNDNET)) != 0) continue;
6061 					if (pn->nodecount != pn1->nodecount) break;
6062 					numin1++;
6063 				}
6064 			}
6065 			numin2 = 0;
6066 			if (pn2 != NOPNET)
6067 			{
6068 				for(i=ind2; i<pn2total; i++)
6069 				{
6070 					pn = pn2list[i];
6071 					if (pn->timestamp == 0) continue;
6072 					if (ignorepwrgnd && (pn->flags&(POWERNET|GROUNDNET)) != 0) continue;
6073 					if (pn->nodecount != pn2->nodecount) break;
6074 					numin2++;
6075 				}
6076 			}
6077 			esnprintf(line, 200, _(":::::::: nets with %ld components (%ld) ::::::::"), curwid, numin1);
6078 			stuffLine(DNCP_CELL1LIST, line);
6079 			esnprintf(line, 200, _(":::::::: nets with %ld components (%ld) ::::::::"), curwid, numin2);
6080 			stuffLine(DNCP_CELL2LIST, line);
6081 			line1++;   line2++;
6082 			reportedwid = curwid;
6083 		}
6084 
6085 		if (pn1 == NOPNET) stuffLine(DNCP_CELL1LIST, x_("")); else
6086 		{
6087 			pn1->timestamp = line1;
6088 			if ((pn1->flags&(POWERNET|GROUNDNET|EXPORTEDNET)) == 0 &&
6089 				(pn1->network == NONETWORK || pn1->network->namecount == 0))
6090 			{
6091 				/* unnamed internal network: merge with others like it */
6092 				net1 = pn1->network;
6093 				if (net1 != NONETWORK) np1 = net1->parent; else
6094 					np1 = NONODEPROTO;
6095 				i = 0;
6096 				for(;;)
6097 				{
6098 					i++;
6099 					ind1++;
6100 					if (ind1 >= pn1total) break;
6101 					pn = pn1list[ind1];
6102 					if (pn->nodecount != pn1->nodecount) break;
6103 					if ((pn->flags&(POWERNET|GROUNDNET|EXPORTEDNET)) != 0 ||
6104 						(pn->network != NONETWORK && pn->network->namecount != 0)) break;
6105 					net2 = pn->network;
6106 					if (net2 != NONETWORK) np2 = net2->parent; else
6107 						np2 = NONODEPROTO;
6108 					if (np1 != np2) break;
6109 					pn->timestamp = line1;
6110 				}
6111 				if (i == 1) estrcpy(line, _("Unnamed net")); else
6112 					esnprintf(line, 200, _("Unnamed nets (%ld)"), i);
6113 				if (np1 != NONODEPROTO)
6114 				{
6115 					estrcat(line, _(" in cell "));
6116 					estrcat(line, describenodeproto(np1));
6117 				}
6118 				stuffLine(DNCP_CELL1LIST, line);
6119 			} else
6120 			{
6121 				(void)allocstring(&pt, net_describepnet(pn1), el_tempcluster);
6122 				infstr = initinfstr();
6123 				addstringtoinfstr(infstr, pt);
6124 				i = 0;
6125 				for(;;)
6126 				{
6127 					i++;
6128 					ind1++;
6129 					if (ind1 >= pn1total) break;
6130 					pn = pn1list[ind1];
6131 					if (pn->nodecount != pn1->nodecount) break;
6132 					if (namesame(pt, net_describepnet(pn)) != 0) break;
6133 					pn->timestamp = line1;
6134 				}
6135 				efree(pt);
6136 				if (i > 1)
6137 					formatinfstr(infstr, x_(" (%ld)"), i);
6138 				stuffLine(DNCP_CELL1LIST, returninfstr(infstr));
6139 			}
6140 		}
6141 		line1++;
6142 
6143 		if (pn2 == NOPNET) stuffLine(DNCP_CELL2LIST, x_("")); else
6144 		{
6145 			pn2->timestamp = line2;
6146 			if ((pn2->flags&(POWERNET|GROUNDNET|EXPORTEDNET)) == 0 &&
6147 				(pn2->network == NONETWORK || pn2->network->namecount == 0))
6148 			{
6149 				/* unnamed internal network: merge with others like it */
6150 				net2 = pn2->network;
6151 				if (net2 != NONETWORK) np2 = net2->parent; else
6152 					np2 = NONODEPROTO;
6153 				i = 0;
6154 				for(;;)
6155 				{
6156 					i++;
6157 					ind2++;
6158 					if (ind2 >= pn2total) break;
6159 					pn = pn2list[ind2];
6160 					if (pn->nodecount != pn2->nodecount) break;
6161 					if ((pn->flags&(POWERNET|GROUNDNET|EXPORTEDNET)) != 0 ||
6162 						(pn->network != NONETWORK && pn->network->namecount != 0)) break;
6163 					net1 = pn->network;
6164 					if (net1 != NONETWORK) np1 = net1->parent; else
6165 						np1 = NONODEPROTO;
6166 					if (np1 != np2) break;
6167 					pn->timestamp = line2;
6168 				}
6169 				if (i == 1) estrcpy(line, _("Unnamed net")); else
6170 					esnprintf(line, 200, _("Unnamed nets (%ld)"), i);
6171 				if (np2 != NONODEPROTO)
6172 				{
6173 					estrcat(line, _(" in cell "));
6174 					estrcat(line, describenodeproto(np2));
6175 				}
6176 				stuffLine(DNCP_CELL2LIST, line);
6177 			} else
6178 			{
6179 				(void)allocstring(&pt, net_describepnet(pn2), el_tempcluster);
6180 				infstr = initinfstr();
6181 				addstringtoinfstr(infstr, pt);
6182 				i = 0;
6183 				for(;;)
6184 				{
6185 					i++;
6186 					ind2++;
6187 					if (ind2 >= pn2total) break;
6188 					pn = pn2list[ind2];
6189 					if (pn->nodecount != pn2->nodecount) break;
6190 					if (namesame(pt, net_describepnet(pn)) != 0) break;
6191 					pn->timestamp = line2;
6192 				}
6193 				efree(pt);
6194 				if (i > 1)
6195 					formatinfstr(infstr, x_(" (%ld)"), i);
6196 				stuffLine(DNCP_CELL2LIST, returninfstr(infstr));
6197 			}
6198 		}
6199 		line2++;
6200 	}
6201 	if (pn1total > 0) efree((CHAR *)pn1list);
6202 	if (pn2total > 0) efree((CHAR *)pn2list);
6203 	selectLine(DNCP_CELL1LIST, 0);
6204 	selectLine(DNCP_CELL2LIST, 0);
6205 }
6206 
putCompIntoDialog()6207 void EDiaNetShowPreanalysis::putCompIntoDialog()
6208 {
6209 	REGISTER INTBIG ind1, ind2, i, maxwirecount, numin1, numin2,
6210 		reportedwid, curwid, pc1total, pc2total, w, line1, line2;
6211 	CHAR line[200];
6212 	REGISTER PNET *pn;
6213 	REGISTER PCOMP **pc1list, **pc2list, *pc, *pc1, *pc2;
6214 	REGISTER void *infstr;
6215 
6216 	/* clear the scroll areas */
6217 	loadTextDialog(DNCP_CELL1LIST, DiaNullDlogList, DiaNullDlogItem, DiaNullDlogDone, -1);
6218 	loadTextDialog(DNCP_CELL2LIST, DiaNullDlogList, DiaNullDlogItem, DiaNullDlogDone, -1);
6219 
6220 	/* count the number of components in the first cell */
6221 	pc1total = 0;
6222 	pc1list = 0;
6223 	for(pc = pcomp1; pc != NOPCOMP; pc = pc->nextpcomp)
6224 	{
6225 		pc1total++;
6226 		pc->timestamp = -1;
6227 
6228 		/* adjust the number of wires, removing ignored power and ground */
6229 		w = 0;
6230 		for(i=0; i<pc->wirecount; i++)
6231 		{
6232 			pn = pc->netnumbers[i];
6233 			if (ignorepwrgnd && (pn->flags&(POWERNET|GROUNDNET)) != 0) continue;
6234 			w++;
6235 		}
6236 		pc->hashvalue = pc->wirecount;
6237 		pc->wirecount = (INTSML)w;
6238 	}
6239 
6240 	/* make a sorted list of the components in the first cell */
6241 	if (pc1total > 0)
6242 	{
6243 		pc1list = (PCOMP **)emalloc(pc1total * (sizeof (PCOMP *)), net_tool->cluster);
6244 		if (pc1list == 0) return;
6245 		pc1total = 0;
6246 		for(pc = pcomp1; pc != NOPCOMP; pc = pc->nextpcomp)
6247 			pc1list[pc1total++] = pc;
6248 		esort(pc1list, pc1total, sizeof (PCOMP *), net_sortpcomp);
6249 	}
6250 
6251 	/* count the number of components in the second cell */
6252 	pc2total = 0;
6253 	pc2list = 0;
6254 	for(pc = pcomp2; pc != NOPCOMP; pc = pc->nextpcomp)
6255 	{
6256 		pc2total++;
6257 		pc->timestamp = -1;
6258 
6259 		/* adjust the number of wires, removing ignored power and ground */
6260 		w = 0;
6261 		for(i=0; i<pc->wirecount; i++)
6262 		{
6263 			pn = pc->netnumbers[i];
6264 			if (ignorepwrgnd && (pn->flags&(POWERNET|GROUNDNET)) != 0) continue;
6265 			w++;
6266 		}
6267 		pc->hashvalue = pc->wirecount;
6268 		pc->wirecount = (INTSML)w;
6269 	}
6270 
6271 	/* make a sorted list of the components in the second cell */
6272 	if (pc2total > 0)
6273 	{
6274 		pc2list = (PCOMP **)emalloc(pc2total * (sizeof (PCOMP *)), net_tool->cluster);
6275 		if (pc2list == 0) return;
6276 		pc2total = 0;
6277 		for(pc = pcomp2; pc != NOPCOMP; pc = pc->nextpcomp)
6278 			pc2list[pc2total++] = pc;
6279 		esort(pc2list, pc2total, sizeof (PCOMP *), net_sortpcomp);
6280 	}
6281 
6282 	/* if reducing to the obvious differences, remove all with a wirecount if numbers match */
6283 	if (!showAll)
6284 	{
6285 		maxwirecount = -1;
6286 		for(pc = pcomp1; pc != NOPCOMP; pc = pc->nextpcomp)
6287 			if (pc->wirecount > maxwirecount) maxwirecount = pc->wirecount;
6288 		for(pc = pcomp2; pc != NOPCOMP; pc = pc->nextpcomp)
6289 			if (pc->wirecount > maxwirecount) maxwirecount = pc->wirecount;
6290 		for(i=maxwirecount; i>=0; i--)
6291 		{
6292 			for(ind1 = 0, pc = pcomp1; pc != NOPCOMP; pc = pc->nextpcomp)
6293 				if (pc->wirecount == i) ind1++;
6294 			for(ind2 = 0, pc = pcomp2; pc != NOPCOMP; pc = pc->nextpcomp)
6295 				if (pc->wirecount == i) ind2++;
6296 			if (ind1 != ind2) continue;
6297 			if (ind1 == 0) continue;
6298 			for(pc = pcomp1; pc != NOPCOMP; pc = pc->nextpcomp)
6299 				if (pc->wirecount == i) pc->timestamp = 0;
6300 			for(pc = pcomp2; pc != NOPCOMP; pc = pc->nextpcomp)
6301 				if (pc->wirecount == i) pc->timestamp = 0;
6302 		}
6303 	}
6304 
6305 	ind1 = ind2 = 0;
6306 	line1 = line2 = 0;
6307 	reportedwid = -1;
6308 	for(;;)
6309 	{
6310 		if (ind1 >= pc1total) pc1 = NOPCOMP; else
6311 		{
6312 			pc1 = pc1list[ind1];
6313 			if (pc1->timestamp == 0)
6314 			{
6315 				ind1++;
6316 				pc1->timestamp = -1;
6317 				continue;
6318 			}
6319 		}
6320 		if (ind2 >= pc2total) pc2 = NOPCOMP; else
6321 		{
6322 			pc2 = pc2list[ind2];
6323 			if (pc2->timestamp == 0)
6324 			{
6325 				ind2++;
6326 				pc2->timestamp = -1;
6327 				continue;
6328 			}
6329 		}
6330 		if (pc1 == NOPCOMP && pc2 == NOPCOMP) break;
6331 		if (pc1 != NOPCOMP && pc2 != NOPCOMP)
6332 		{
6333 			if (pc1->wirecount < pc2->wirecount) pc1 = NOPCOMP; else
6334 				if (pc1->wirecount > pc2->wirecount) pc2 = NOPCOMP;
6335 		}
6336 		if (pc1 != NOPCOMP) curwid = pc1->wirecount; else curwid = pc2->wirecount;
6337 		if (curwid != reportedwid)
6338 		{
6339 			numin1 = 0;
6340 			if (pc1 != NOPCOMP)
6341 			{
6342 				for(i=ind1; i<pc1total; i++)
6343 				{
6344 					if (pc1list[i]->wirecount == pc1->wirecount) numin1++; else break;
6345 				}
6346 			}
6347 			numin2 = 0;
6348 			if (pc2 != NOPCOMP)
6349 			{
6350 				for(i=ind2; i<pc2total; i++)
6351 				{
6352 					if (pc2list[i]->wirecount == pc2->wirecount) numin2++; else break;
6353 				}
6354 			}
6355 			esnprintf(line, 200, _(":::::::: components with %ld nets (%ld) ::::::::"), curwid, numin1);
6356 			stuffLine(DNCP_CELL1LIST, line);
6357 			esnprintf(line, 200, _(":::::::: components with %ld nets (%ld) ::::::::"), curwid, numin2);
6358 			stuffLine(DNCP_CELL2LIST, line);
6359 			line1++;   line2++;
6360 			reportedwid = curwid;
6361 		}
6362 
6363 		if (pc1 == NOPCOMP) stuffLine(DNCP_CELL1LIST, x_("")); else
6364 		{
6365 			pc1->timestamp = line1;
6366 			i = 0;
6367 			for(;;)
6368 			{
6369 				i++;
6370 				ind1++;
6371 				if (ind1 >= pc1total) break;
6372 				if (pc1list[ind1]->timestamp == 0) break;
6373 				if (namesame(pc1->hashreason, pc1list[ind1]->hashreason) != 0)
6374 					break;
6375 				pc1list[ind1]->timestamp = line1;
6376 			}
6377 			infstr = initinfstr();
6378 			if (i > 1)
6379 				formatinfstr(infstr, x_("(%ld) "), i);
6380 			addstringtoinfstr(infstr, pc1->hashreason);
6381 			stuffLine(DNCP_CELL1LIST, returninfstr(infstr));
6382 		}
6383 		line1++;
6384 
6385 		if (pc2 == NOPCOMP) stuffLine(DNCP_CELL2LIST, x_("")); else
6386 		{
6387 			pc2->timestamp = line2;
6388 			i = 0;
6389 			for(;;)
6390 			{
6391 				i++;
6392 				ind2++;
6393 				if (ind2 >= pc2total) break;
6394 				if (pc2list[ind2]->timestamp == 0) break;
6395 				if (namesame(pc2->hashreason, pc2list[ind2]->hashreason) != 0)
6396 					break;
6397 				pc2list[ind2]->timestamp = line2;
6398 			}
6399 			infstr = initinfstr();
6400 			if (i > 1)
6401 				formatinfstr(infstr, x_("(%ld) "), i);
6402 			addstringtoinfstr(infstr, pc2->hashreason);
6403 			stuffLine(DNCP_CELL2LIST, returninfstr(infstr));
6404 		}
6405 		line2++;
6406 	}
6407 	selectLine(DNCP_CELL1LIST, 0);
6408 	selectLine(DNCP_CELL2LIST, 0);
6409 
6410 	/* restore true wire counts */
6411 	for(pc = pcomp1; pc != NOPCOMP; pc = pc->nextpcomp)
6412 		pc->wirecount = (INTSML)pc->hashvalue;
6413 	for(pc = pcomp2; pc != NOPCOMP; pc = pc->nextpcomp)
6414 		pc->wirecount = (INTSML)pc->hashvalue;
6415 	if (pc1total > 0) efree((CHAR *)pc1list);
6416 	if (pc2total > 0) efree((CHAR *)pc2list);
6417 }
6418 
6419 /******************************** SYMMETRY GROUP SUPPORT ********************************/
6420 
6421 /*
6422  * Routine to create a new symmetry group of type "grouptype" with hash value "hashvalue".
6423  * The group is linked into the global list of symmetry groups.
6424  * Returns NOSYMGROUP on error.
6425  */
net_newsymgroup(INTBIG grouptype,HASHTYPE hashvalue,INTBIG checksum)6426 SYMGROUP *net_newsymgroup(INTBIG grouptype, HASHTYPE hashvalue, INTBIG checksum)
6427 {
6428 	REGISTER SYMGROUP *sg;
6429 
6430 	if (net_symgroupfree == NOSYMGROUP)
6431 	{
6432 		sg = (SYMGROUP *)emalloc(sizeof (SYMGROUP), net_tool->cluster);
6433 		if (sg == 0) return(NOSYMGROUP);
6434 		sg->celltotal[0] = sg->celltotal[1] = 0;
6435 	} else
6436 	{
6437 		sg = net_symgroupfree;
6438 		net_symgroupfree = sg->nextsymgroup;
6439 	}
6440 	sg->hashvalue = hashvalue;
6441 	sg->grouptype = grouptype;
6442 	sg->groupflags = 0;
6443 	sg->groupindex = net_symgroupnumber++;
6444 	sg->checksum = checksum;
6445 	sg->cellcount[0] = sg->cellcount[1] = 0;
6446 	sg->nextsymgroup = net_firstsymgroup;
6447 	net_firstsymgroup = sg;
6448 
6449 	/* put it in the hash table */
6450 	if (net_insertinhashtable(sg))
6451 		net_rebuildhashtable();
6452 	return(sg);
6453 }
6454 
6455 /*
6456  * Routine to free symmetry group "sg" to the pool of unused ones.
6457  */
net_freesymgroup(SYMGROUP * sg)6458 void net_freesymgroup(SYMGROUP *sg)
6459 {
6460 	sg->nextsymgroup = net_symgroupfree;
6461 	net_symgroupfree = sg;
6462 }
6463 
6464 /*
6465  * Routine to find a unique hash number for a new symmetry group.
6466  */
net_uniquesymmetrygrouphash(INTBIG grouptype)6467 HASHTYPE net_uniquesymmetrygrouphash(INTBIG grouptype)
6468 {
6469 	REGISTER SYMGROUP *sg;
6470 
6471 	for( ; ; net_uniquehashvalue--)
6472 	{
6473 		sg = net_findsymmetrygroup(grouptype, net_uniquehashvalue, 0);
6474 		if (sg == NOSYMGROUP) break;
6475 	}
6476 	return(net_uniquehashvalue);
6477 }
6478 
6479 /*
6480  * Routine to find the symmetry group of type "grouptype" with hash value "hashvalue".
6481  * The "checksum" value is an additional piece of information about this hash value
6482  * to ensure that there are not clashes in the codes.  Returns NOSYMGROUP if none is found.
6483  */
net_findsymmetrygroup(INTBIG grouptype,HASHTYPE hashvalue,INTBIG checksum)6484 SYMGROUP *net_findsymmetrygroup(INTBIG grouptype, HASHTYPE hashvalue, INTBIG checksum)
6485 {
6486 	REGISTER SYMGROUP *sg;
6487 	REGISTER INTBIG i, hashindex;
6488 
6489 	if (grouptype == SYMGROUPNET)
6490 	{
6491 		hashindex = abs((INTBIG)(hashvalue % net_symgrouphashnetsize));
6492 		for(i=0; i<net_symgrouphashnetsize; i++)
6493 		{
6494 			sg = net_symgrouphashnet[hashindex];
6495 			if (sg == NOSYMGROUP) break;
6496 			if (sg->hashvalue == hashvalue)
6497 			{
6498 				if (sg->checksum != checksum && hashvalue != 0 && !net_nethashclashtold)
6499 				{
6500 					ttyputerr(_("-- POSSIBLE NETWORK HASH CLASH (%ld AND %ld)"),
6501 						sg->checksum, checksum);
6502 					net_nethashclashtold = TRUE;
6503 				}
6504 				return(sg);
6505 			}
6506 			hashindex++;
6507 			if (hashindex >= net_symgrouphashnetsize) hashindex = 0;
6508 		}
6509 	} else
6510 	{
6511 		hashindex = abs((INTBIG)(hashvalue % net_symgrouphashcompsize));
6512 		for(i=0; i<net_symgrouphashcompsize; i++)
6513 		{
6514 			sg = net_symgrouphashcomp[hashindex];
6515 			if (sg == NOSYMGROUP) break;
6516 			if (sg->hashvalue == hashvalue)
6517 			{
6518 				if (sg->checksum != checksum && hashvalue != 0 && !net_comphashclashtold)
6519 				{
6520 					ttyputerr(_("-- POSSIBLE COMPONENT HASH CLASH (%ld AND %ld)"),
6521 						sg->checksum, checksum);
6522 					net_comphashclashtold = TRUE;
6523 				}
6524 				return(sg);
6525 			}
6526 			hashindex++;
6527 			if (hashindex >= net_symgrouphashcompsize) hashindex = 0;
6528 		}
6529 	}
6530 	return(NOSYMGROUP);
6531 }
6532 
net_rebuildhashtable(void)6533 void net_rebuildhashtable(void)
6534 {
6535 	REGISTER INTBIG i;
6536 	REGISTER BOOLEAN problems;
6537 	REGISTER SYMGROUP *sg;
6538 
6539 	for(;;)
6540 	{
6541 		problems = FALSE;
6542 		for(i=0; i<net_symgrouphashcompsize; i++) net_symgrouphashcomp[i] = NOSYMGROUP;
6543 		for(i=0; i<net_symgrouphashnetsize; i++) net_symgrouphashnet[i] = NOSYMGROUP;
6544 		for(sg = net_firstsymgroup; sg != NOSYMGROUP; sg = sg->nextsymgroup)
6545 		{
6546 			problems = net_insertinhashtable(sg);
6547 			if (problems) break;
6548 		}
6549 		for(sg = net_firstmatchedsymgroup; sg != NOSYMGROUP; sg = sg->nextsymgroup)
6550 		{
6551 			problems = net_insertinhashtable(sg);
6552 			if (problems) break;
6553 		}
6554 		if (!problems) break;
6555 	}
6556 }
6557 
6558 /*
6559  * Routine to insert symmetry group "sg" into a hash table.  Returns true
6560  * if the hash table needed to be expanded (and is thus invalid now).
6561  */
net_insertinhashtable(SYMGROUP * sg)6562 BOOLEAN net_insertinhashtable(SYMGROUP *sg)
6563 {
6564 	REGISTER INTBIG i, hashindex, newsize, *newhashtableck;
6565 	REGISTER SYMGROUP **newhashtable;
6566 
6567 	if (sg->grouptype == SYMGROUPNET)
6568 	{
6569 		hashindex = abs((INTBIG)(sg->hashvalue % net_symgrouphashnetsize));
6570 		for(i=0; i<net_symgrouphashnetsize; i++)
6571 		{
6572 			if (net_symgrouphashnet[hashindex] == NOSYMGROUP)
6573 			{
6574 				net_symgrouphashnet[hashindex] = sg;
6575 				net_symgrouphashcknet[hashindex] = sg->checksum;
6576 				break;
6577 			}
6578 			hashindex++;
6579 			if (hashindex >= net_symgrouphashnetsize) hashindex = 0;
6580 		}
6581 		if (i >= net_symgrouphashnetsize)
6582 		{
6583 			newsize = pickprime(net_symgrouphashnetsize * 2);
6584 			newhashtable = (SYMGROUP **)emalloc(newsize * (sizeof (SYMGROUP *)),
6585 				net_tool->cluster);
6586 			if (newhashtable == 0) return(FALSE);
6587 			newhashtableck = (INTBIG *)emalloc(newsize * SIZEOFINTBIG,
6588 				net_tool->cluster);
6589 			if (newhashtableck == 0) return(FALSE);
6590 			efree((CHAR *)net_symgrouphashnet);
6591 			efree((CHAR *)net_symgrouphashcknet);
6592 			net_symgrouphashnet = newhashtable;
6593 			net_symgrouphashcknet = newhashtableck;
6594 			net_symgrouphashnetsize = newsize;
6595 			ttyputmsg(x_(" -- EXPANDING SIZE OF NETWORK HASH TABLE TO %ld ENTRIES"),
6596 				newsize);
6597 			return(TRUE);
6598 		}
6599 	} else
6600 	{
6601 		hashindex = abs((INTBIG)(sg->hashvalue % net_symgrouphashcompsize));
6602 		for(i=0; i<net_symgrouphashcompsize; i++)
6603 		{
6604 			if (net_symgrouphashcomp[hashindex] == NOSYMGROUP)
6605 			{
6606 				net_symgrouphashcomp[hashindex] = sg;
6607 				net_symgrouphashckcomp[hashindex] = sg->checksum;
6608 				break;
6609 			}
6610 			hashindex++;
6611 			if (hashindex >= net_symgrouphashcompsize) hashindex = 0;
6612 		}
6613 		if (i >= net_symgrouphashcompsize)
6614 		{
6615 			newsize = pickprime(net_symgrouphashcompsize * 2);
6616 			newhashtable = (SYMGROUP **)emalloc(newsize * (sizeof (SYMGROUP *)),
6617 				net_tool->cluster);
6618 			if (newhashtable == 0) return(FALSE);
6619 			newhashtableck = (INTBIG *)emalloc(newsize * SIZEOFINTBIG,
6620 				net_tool->cluster);
6621 			if (newhashtableck == 0) return(FALSE);
6622 			efree((CHAR *)net_symgrouphashcomp);
6623 			efree((CHAR *)net_symgrouphashckcomp);
6624 			net_symgrouphashcomp = newhashtable;
6625 			net_symgrouphashckcomp = newhashtableck;
6626 			net_symgrouphashcompsize = newsize;
6627 			ttyputmsg(x_(" -- EXPANDING SIZE OF COMPONENT HASH TABLE TO %ld ENTRIES"),
6628 				newsize);
6629 			return(TRUE);
6630 		}
6631 	}
6632 	return(FALSE);
6633 }
6634 
6635 /*
6636  * Routine to add object "obj" to cell "f" (0 or 1) of symmetry group "sg".
6637  * Returns true on error.
6638  */
net_addtosymgroup(SYMGROUP * sg,INTBIG f,void * obj)6639 BOOLEAN net_addtosymgroup(SYMGROUP *sg, INTBIG f, void *obj)
6640 {
6641 	INTBIG newtotal, i, count;
6642 	REGISTER PNET *pn;
6643 	REGISTER PCOMP *pc;
6644 	void **newlist;
6645 
6646 	count = sg->cellcount[f];
6647 	if (count >= sg->celltotal[f])
6648 	{
6649 		newtotal = count + 10;
6650 		newlist = (void **)emalloc(newtotal * (sizeof (void *)), net_tool->cluster);
6651 		if (newlist == 0) return(TRUE);
6652 		for(i=0; i < count; i++)
6653 			newlist[i] = sg->celllist[f][i];
6654 		if (sg->celltotal[f] > 0) efree((CHAR *)sg->celllist[f]);
6655 		sg->celllist[f] = newlist;
6656 		sg->celltotal[f] = newtotal;
6657 	}
6658 	sg->celllist[f][count] = obj;
6659 	sg->cellcount[f]++;
6660 	if (sg->grouptype == SYMGROUPNET)
6661 	{
6662 		pn = (PNET *)obj;
6663 		pn->timestamp = net_timestamp;
6664 	} else
6665 	{
6666 		pc = (PCOMP *)obj;
6667 		pc->timestamp = net_timestamp;
6668 	}
6669 	return(FALSE);
6670 }
6671 
6672 /*
6673  * Routine to remove entry "index" from cell "f" (0 or 1) of symmetry group "sg".
6674  */
net_removefromsymgroup(SYMGROUP * sg,INTBIG f,INTBIG index)6675 void net_removefromsymgroup(SYMGROUP *sg, INTBIG f, INTBIG index)
6676 {
6677 	REGISTER INTBIG count;
6678 
6679 	count = sg->cellcount[f];
6680 	sg->celllist[f][index] = sg->celllist[f][count-1];
6681 	sg->cellcount[f]--;
6682 }
6683 
6684 /*********************** NCC MATCH CACHING ***********************/
6685 
6686 /*
6687  * Routine to preserve NCC results on cell "np1" (after being compared with "np2")
6688  */
net_preserveresults(NODEPROTO * np1,NODEPROTO * np2)6689 void net_preserveresults(NODEPROTO *np1, NODEPROTO *np2)
6690 {
6691 	REGISTER time_t curtime;
6692 	UINTBIG matchdate;
6693 	NODEPROTO *othercell;
6694 	INTBIG count;
6695 	REGISTER INTBIG i, index, highestindex;
6696 	REGISTER VARIABLE *var;
6697 	REGISTER void *sa;
6698 	REGISTER CHAR *name1, *name2, *pt;
6699 	REGISTER PNET *pn1, *pn2;
6700 	REGISTER SYMGROUP *sg;
6701 	CHAR line[300], **stringarray;
6702 
6703 	sa = newstringarray(net_tool->cluster);
6704 	curtime = getcurrenttime();
6705 	esnprintf(line, 300, x_("TIME %lu"), curtime);
6706 	addtostringarray(sa, line);
6707 	esnprintf(line, 300, x_("MATCH %s:%s"), np2->lib->libname, nldescribenodeproto(np2));
6708 	addtostringarray(sa, line);
6709 	for(sg = net_firstsymgroup; sg != NOSYMGROUP; sg = sg->nextsymgroup)
6710 	{
6711 		if (sg->cellcount[0] == 1 && sg->cellcount[1] == 1 && sg->grouptype == SYMGROUPNET)
6712 		{
6713 			/* see if names match */
6714 			if (np1 == net_cell[0])
6715 			{
6716 				pn1 = (PNET *)sg->celllist[0][0];
6717 				pn2 = (PNET *)sg->celllist[1][0];
6718 			} else
6719 			{
6720 				pn1 = (PNET *)sg->celllist[1][0];
6721 				pn2 = (PNET *)sg->celllist[0][0];
6722 			}
6723 
6724 			if ((pn1->flags&EXPORTEDNET) == 0 || (pn2->flags&EXPORTEDNET) == 0) continue;
6725 			if (pn1->realportcount == 0 &&
6726 				(pn1->network == NONETWORK || pn1->network->namecount == 0)) continue;
6727 			if (pn2->realportcount == 0 &&
6728 				(pn2->network == NONETWORK || pn2->network->namecount == 0)) continue;
6729 
6730 			if (pn1->realportcount == 0)
6731 			{
6732 				name1 = networkname(pn1->network, 0);
6733 			} else if (pn1->realportcount == 1)
6734 			{
6735 				name1 = ((PORTPROTO *)pn1->realportlist)->protoname;
6736 			} else
6737 			{
6738 				name1 = (((PORTPROTO **)pn1->realportlist)[0])->protoname;
6739 			}
6740 			if (pn2->realportcount == 0)
6741 			{
6742 				name2 = networkname(pn2->network, 0);
6743 			} else if (pn2->realportcount == 1)
6744 			{
6745 				name2 = ((PORTPROTO *)pn2->realportlist)->protoname;
6746 			} else
6747 			{
6748 				name2 = (((PORTPROTO **)pn2->realportlist)[0])->protoname;
6749 			}
6750 			esnprintf(line, 300, x_("EXPORT %s:%s"), name1, name2);
6751 			addtostringarray(sa, line);
6752 		}
6753 	}
6754 	stringarray = getstringarray(sa, &count);
6755 
6756 	/* find a place to store this information */
6757 	highestindex = 0;
6758 	for(i=0; i<np1->numvar; i++)
6759 	{
6760 		var = &np1->firstvar[i];
6761 		pt = makename(var->key);
6762 		if (namesamen(pt, x_("NET_ncc_last_result"), 19) != 0) continue;
6763 		index = eatoi(&pt[19]);
6764 		if (index > highestindex) highestindex = index;
6765 
6766 		/* if this information is intended for the other cell, overwrite it */
6767 		net_parsenccresult(np1, var, &othercell, &matchdate);
6768 		if (othercell == np2)
6769 		{
6770 			highestindex = index-1;
6771 			break;
6772 		}
6773 	}
6774 	esnprintf(line, 300, x_("NET_ncc_last_result%ld"), highestindex+1);
6775 	(void)setval((INTBIG)np1, VNODEPROTO, line, (INTBIG)stringarray,
6776 		VSTRING|VISARRAY|(count<<VLENGTHSH));
6777 	killstringarray(sa);
6778 }
6779 
6780 /*
6781  * Routine to return true if the cells "cell1" and "cell2" are already NCC'd.
6782  */
net_nccalreadydone(NODEPROTO * cell1,NODEPROTO * cell2)6783 BOOLEAN net_nccalreadydone(NODEPROTO *cell1, NODEPROTO *cell2)
6784 {
6785 	REGISTER VARIABLE *var1, *var2;
6786 	UINTBIG cell1matchdate, cell2matchdate, cell1changedate, cell2changedate;
6787 
6788 	/* see if cell 1 has match information with cell 2 */
6789 	var1 = net_nccfindmatch(cell1, cell2, &cell1matchdate);
6790 	if (var1 == NOVARIABLE) return(FALSE);
6791 	cell1changedate = net_recursiverevisiondate(cell1);
6792 	if (cell1changedate > cell1matchdate) return(FALSE);
6793 
6794 	/* see if cell 2 has match information with cell 1 */
6795 	var2 = net_nccfindmatch(cell2, cell1, &cell2matchdate);
6796 	if (var2 == NOVARIABLE) return(FALSE);
6797 	cell2changedate = net_recursiverevisiondate(cell2);
6798 	if (cell2changedate > cell2matchdate) return(FALSE);
6799 
6800 	/* the cells are already checked */
6801 	return(TRUE);
6802 }
6803 
6804 /*
6805  * Routine to recursively obtain the most recent revision date for cell "cell"
6806  * or any of its subcells.
6807  */
net_recursiverevisiondate(NODEPROTO * cell)6808 UINTBIG net_recursiverevisiondate(NODEPROTO *cell)
6809 {
6810 	REGISTER UINTBIG latestrevision, instancerevision;
6811 	REGISTER NODEINST *ni;
6812 	REGISTER NODEPROTO *np, *cnp;
6813 
6814 	latestrevision = cell->revisiondate;
6815 	for(ni = cell->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
6816 	{
6817 		np = ni->proto;
6818 		if (np->primindex != 0) continue;
6819 		cnp = contentsview(np);
6820 		if (cnp == NONODEPROTO) cnp = np;
6821 		if (cnp == cell) continue;
6822 		instancerevision = net_recursiverevisiondate(cnp);
6823 		if (instancerevision > latestrevision)
6824 			latestrevision = instancerevision;
6825 	}
6826 	return(latestrevision);
6827 }
6828 
6829 /*
6830  * Routine to scan cell "np" looking for an NCC match to cell "onp".  If found,
6831  * the date of the match is returned in "matchdate" and the variable describing
6832  * the match information is returned.  Returns NOVARIABLE if not found.
6833  */
net_nccfindmatch(NODEPROTO * np,NODEPROTO * onp,UINTBIG * matchdate)6834 VARIABLE *net_nccfindmatch(NODEPROTO *np, NODEPROTO *onp, UINTBIG *matchdate)
6835 {
6836 	REGISTER INTBIG i;
6837 	NODEPROTO *othernp;
6838 	REGISTER VARIABLE *var;
6839 	REGISTER CHAR *pt;
6840 
6841 	/* find a place to store this information */
6842 	for(i=0; i<np->numvar; i++)
6843 	{
6844 		var = &np->firstvar[i];
6845 		pt = makename(var->key);
6846 		if (namesamen(pt, x_("NET_ncc_last_result"), 19) != 0) continue;
6847 		net_parsenccresult(np, var, &othernp, matchdate);
6848 		if (othernp == onp) return(var);
6849 	}
6850 	return(NOVARIABLE);
6851 }
6852 
6853 /*
6854  * Routine to return nonzero if cell "np" has NCC match information that is
6855  * still current with any other cell.
6856  */
net_ncchasmatch(NODEPROTO * np)6857 INTBIG net_ncchasmatch(NODEPROTO *np)
6858 {
6859 	REGISTER INTBIG i;
6860 	UINTBIG matchdate, revisiondate;
6861 	NODEPROTO *othernp;
6862 	REGISTER VARIABLE *var;
6863 	REGISTER CHAR *pt;
6864 
6865 	for(i=0; i<np->numvar; i++)
6866 	{
6867 		var = &np->firstvar[i];
6868 		pt = makename(var->key);
6869 		if (namesamen(pt, x_("NET_ncc_last_result"), 19) != 0) continue;
6870 		net_parsenccresult(np, var, &othernp, &matchdate);
6871 		revisiondate = net_recursiverevisiondate(np);
6872 		if (revisiondate <= matchdate) return(1);
6873 	}
6874 	return(0);
6875 }
6876 
6877 /*
6878  * Routine to remove all NCC match information on cell "np".
6879  */
net_nccremovematches(NODEPROTO * np)6880 void net_nccremovematches(NODEPROTO *np)
6881 {
6882 	REGISTER INTBIG i;
6883 	REGISTER VARIABLE *var;
6884 	REGISTER CHAR *pt;
6885 
6886 	/* find a place to store this information */
6887 	for(i=0; i<np->numvar; i++)
6888 	{
6889 		var = &np->firstvar[i];
6890 		pt = makename(var->key);
6891 		if (namesamen(pt, x_("NET_ncc_last_result"), 19) != 0) continue;
6892 		(void)delvalkey((INTBIG)np, VNODEPROTO, var->key);
6893 		i--;
6894 	}
6895 }
6896 
6897 /*
6898  * Routine to parse the NCC results on cell "np" and store the information in
6899  * "cellmatch" (the cell that was matched) and "celldate" (the time of the match)
6900  */
net_nccmatchinfo(NODEPROTO * np,NODEPROTO ** cellmatch,UINTBIG * celldate)6901 void net_nccmatchinfo(NODEPROTO *np, NODEPROTO **cellmatch, UINTBIG *celldate)
6902 {
6903 	REGISTER VARIABLE *var;
6904 	REGISTER INTBIG i;
6905 	REGISTER CHAR *pt;
6906 
6907 	*cellmatch = NONODEPROTO;
6908 	*celldate = 0;
6909 	for(i=0; i<np->numvar; i++)
6910 	{
6911 		var = &np->firstvar[i];
6912 		pt = makename(var->key);
6913 		if (namesamen(pt, x_("NET_ncc_last_result"), 19) != 0) continue;
6914 		net_parsenccresult(np, var, cellmatch, celldate);
6915 		break;
6916 	}
6917 }
6918 
6919 /*
6920  * Routine to parse the NCC results on cell "np" in variable "var" and store the
6921  * information in "cellmatch" (the cell that was matched) and "celldate" (the
6922  * time of the match).
6923  */
net_parsenccresult(NODEPROTO * np,VARIABLE * var,NODEPROTO ** cellmatch,UINTBIG * celldate)6924 void net_parsenccresult(NODEPROTO *np, VARIABLE *var, NODEPROTO **cellmatch,
6925 	UINTBIG *celldate)
6926 {
6927 	REGISTER INTBIG len, i;
6928 	REGISTER CHAR **strings, *pt;
6929 	Q_UNUSED( np );
6930 
6931 	*cellmatch = NONODEPROTO;
6932 	*celldate = 0;
6933 	if (var == NOVARIABLE) return;
6934 	len = getlength(var);
6935 	strings = (CHAR **)var->addr;
6936 	for(i=0; i<len; i++)
6937 	{
6938 		pt = strings[i];
6939 		if (namesamen(pt, x_("TIME "), 5) == 0)
6940 		{
6941 			*celldate = eatoi(&pt[5]);
6942 			continue;
6943 		}
6944 		if (namesamen(pt, x_("MATCH "), 6) == 0)
6945 		{
6946 			*cellmatch = getnodeproto(&pt[6]);
6947 			continue;
6948 		}
6949 	}
6950 }
6951 
6952 /*********************** HELPER ROUTINES ***********************/
6953 
net_describepcomp(PCOMP * pc)6954 CHAR *net_describepcomp(PCOMP *pc)
6955 {
6956 	REGISTER INTBIG i;
6957 	REGISTER NODEINST *ni;
6958 	REGISTER void *infstr;
6959 
6960 	infstr = initinfstr();
6961 	for(i=0; i<pc->numactual; i++)
6962 	{
6963 		if (pc->numactual == 1) ni = (NODEINST *)pc->actuallist; else
6964 			ni = ((NODEINST **)pc->actuallist)[i];
6965 		if (i != 0) addtoinfstr(infstr, '/');
6966 		addstringtoinfstr(infstr, ntdescribenodeinst(ni));
6967 	}
6968 	return(returninfstr(infstr));
6969 }
6970 
net_describepnet(PNET * pn)6971 CHAR *net_describepnet(PNET *pn)
6972 {
6973 	REGISTER PORTPROTO *pp;
6974 	REGISTER INTBIG i;
6975 	REGISTER NETWORK *net;
6976 	REGISTER void *infstr;
6977 
6978 	net = pn->network;
6979 	infstr = initinfstr();
6980 	if ((pn->flags&POWERNET) != 0) addstringtoinfstr(infstr, _("POWER "));
6981 	if ((pn->flags&GROUNDNET) != 0) addstringtoinfstr(infstr, _("GROUND "));
6982 	if ((pn->flags&EXPORTEDNET) == 0)
6983 	{
6984 		if (net == NONETWORK) addstringtoinfstr(infstr, _("INTERNAL")); else
6985 		{
6986 			if (net->globalnet >= 0 && net->globalnet < net->parent->globalnetcount)
6987 			{
6988 				addstringtoinfstr(infstr, describenetwork(net));
6989 			} else
6990 			{
6991 				formatinfstr(infstr, _("INTERNAL %s:%s"), describenodeproto(net->parent),
6992 					describenetwork(net));
6993 			}
6994 		}
6995 	} else
6996 	{
6997 #ifdef PATHTOPNET
6998 		if (pn->hierpathcount > 0)
6999 		{
7000 			for(i=0; i<pn->hierpathcount; i++)
7001 			{
7002 				addstringtoinfstr(infstr, describenodeinst(pn->hierpath[i]));
7003 				if (pn->hierindex[i] != 0) formatinfstr(infstr, "[%ld]", pn->hierindex[i]);
7004 				addstringtoinfstr(infstr, ".");
7005 			}
7006 		} else addstringtoinfstr(infstr, "TOP.");
7007 #endif
7008 		if (pn->realportcount == 1)
7009 		{
7010 			pp = (PORTPROTO *)pn->realportlist;
7011 			if (pp->network->buswidth > 1 && net != NONETWORK)
7012 				addstringtoinfstr(infstr, describenetwork(net)); else
7013 					addstringtoinfstr(infstr, pp->protoname);
7014 		} else if (pn->realportcount > 1)
7015 		{
7016 			for(i=0; i<pn->realportcount; i++)
7017 			{
7018 				pp = ((PORTPROTO **)pn->realportlist)[i];
7019 				if (i > 0) addtoinfstr(infstr, ',');
7020 				if (i == 0 && pp->network->buswidth > 1 && net != NONETWORK)
7021 					addstringtoinfstr(infstr, describenetwork(net)); else
7022 						addstringtoinfstr(infstr, pp->protoname);
7023 			}
7024 		} else
7025 		{
7026 			net = pn->network;
7027 			addstringtoinfstr(infstr, describenetwork(net));
7028 		}
7029 	}
7030 	return(returninfstr(infstr));
7031 }
7032 
7033 /*
7034  * Routine to see if the component values "v1" and "v2" are within the prescribed
7035  * tolerance (in "net_ncc_tolerance" and "net_ncc_tolerance_amt").  Returns true if so.
7036  */
net_componentequalvalue(float v1,float v2)7037 BOOLEAN net_componentequalvalue(float v1, float v2)
7038 {
7039 	float tolerance, largest, diff;
7040 
7041 	/* first see if it is within tolerance amount */
7042 	diff = (float)fabs(v1 - v2);
7043 	if (diff <= net_ncc_tolerance_amt) return(TRUE);
7044 
7045 	/* now see if it is within tolerance percentage */
7046 	if (v1 > v2) largest = v1; else largest = v2;
7047 	tolerance = largest * net_ncc_tolerance / (WHOLE * 100.0f);
7048 	if (diff <= tolerance) return(TRUE);
7049 
7050 	/* not within any tolerance */
7051 	return(FALSE);
7052 }
7053 
7054 /*
7055  * Routine to return true if component "pc" is a SPICE component.
7056  */
net_isspice(PCOMP * pc)7057 BOOLEAN net_isspice(PCOMP *pc)
7058 {
7059 	REGISTER NODEINST *ni;
7060 
7061 	switch (pc->function)
7062 	{
7063 		case NPMETER:
7064 		case NPSOURCE:
7065 			return(TRUE);
7066 	}
7067 	if (pc->numactual == 1)
7068 	{
7069 		ni = (NODEINST *)pc->actuallist;
7070 		if (ni->proto->primindex == 0 &&
7071 			namesamen(ni->proto->lib->libname, x_("spiceparts"), 10) == 0)
7072 				return(TRUE);
7073 	}
7074 	return(FALSE);
7075 }
7076 
net_initdiff(void)7077 void net_initdiff(void)
7078 {
7079 	REGISTER INTBIG i;
7080 
7081 	if (net_nodeCountMultiplier == 0)
7082 	{
7083 		i = 0;
7084 		net_nodeCountMultiplier = getprime(i++);
7085 		net_portFactorMultiplier = getprime(i++);
7086 		net_functionMultiplier = getprime(i++);
7087 		net_portNetFactorMultiplier = getprime(i++);
7088 		net_portHashFactorMultiplier = getprime(i++);
7089 	}
7090 }
7091