1 /* -*- tab-width: 4 -*-
2  *
3  * Electric(tm) VLSI Design System
4  *
5  * File: dblibrary.c
6  * Database library and lambda control module
7  * Written by: Steven M. Rubin, Static Free Software
8  *
9  * Copyright (c) 2000 Static Free Software.
10  *
11  * Electric(tm) is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * Electric(tm) is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with Electric(tm); see the file COPYING.  If not, write to
23  * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
24  * Boston, Mass 02111-1307, USA.
25  *
26  * Static Free Software
27  * 4119 Alpine Road
28  * Portola Valley, California 94028
29  * info@staticfreesoft.com
30  */
31 
32 #include "global.h"
33 #include "database.h"
34 #include "network.h"
35 
36 /* prototypes for local routines */
37 static void   db_scalecell(NODEPROTO*, INTBIG, TECHNOLOGY**, INTBIG*);
38 static void   db_validatearcinst(ARCINST*);
39 static void   db_setarcinst(ARCINST*, INTBIG, INTBIG, INTBIG, INTBIG);
40 static INTBIG db_scaleunits(INTBIG *value, INTBIG num, INTBIG den);
41 static void   db_scalearcinst(ARCINST *ai, INTBIG num, INTBIG denom);
42 static void   db_scalenodeinst(NODEINST *ni, INTBIG num, INTBIG denom);
43 
44 /****************************** LIBRARIES ******************************/
45 
46 /*
47  * routine to allocate a library, places it in its own cluster, and return its
48  * address.  The routine returns NOLIBRARY if allocation fails.
49  */
alloclibrary(void)50 LIBRARY *alloclibrary(void)
51 {
52 	REGISTER LIBRARY *lib;
53 	REGISTER TECHNOLOGY *tech;
54 	REGISTER CLUSTER *cluster;
55 
56 	cluster = alloccluster(x_(""));
57 	if (cluster == NOCLUSTER) return((LIBRARY *)db_error(DBNOMEM|DBALLOCLIBRARY));
58 	lib = (LIBRARY *)emalloc((sizeof (LIBRARY)), cluster);
59 	if (lib == 0) return((LIBRARY *)db_error(DBNOMEM|DBALLOCLIBRARY));
60 	lib->cluster = cluster;
61 
62 	/* allocate space for lambda array */
63 	lib->lambda = emalloc(((el_maxtech+1) * SIZEOFINTBIG), cluster);
64 	if (lib->lambda == 0) return((LIBRARY *)db_error(DBNOMEM|DBALLOCLIBRARY));
65 	lib->lambda[el_maxtech] = -1;
66 
67 	lib->userbits = lib->temp1 = lib->temp2 = 0;
68 	for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
69 	{
70 		if (el_curlib == NOLIBRARY)
71 		{
72 			lib->lambda[tech->techindex] = tech->deflambda;
73 		} else
74 		{
75 			lib->lambda[tech->techindex] = el_curlib->lambda[tech->techindex];
76 		}
77 	}
78 	lib->numvar = 0;
79 	lib->libname = NOSTRING;
80 	lib->libfile = NOSTRING;
81 	lib->firstvar = NOVARIABLE;
82 	lib->firstnodeproto = NONODEPROTO;
83 	lib->tailnodeproto = NONODEPROTO;
84 	lib->curnodeproto = NONODEPROTO;
85 	lib->nextlibrary = NOLIBRARY;
86 	lib->numnodeprotos = 0;
87 	lib->nodeprotohashtablesize = 0;
88 	lib->freenetwork = NONETWORK;
89 	return(lib);
90 }
91 
92 /*
93  * routine to return library "lib" to the pool of free libraries
94  */
freelibrary(LIBRARY * lib)95 void freelibrary(LIBRARY *lib)
96 {
97 	REGISTER CLUSTER *clus;
98 	REGISTER NETWORK *net, *nextnet;
99 
100 	if (lib == NOLIBRARY) return;
101 	if (lib->numvar != 0) db_freevars(&lib->firstvar, &lib->numvar);
102 	efree((CHAR *)lib->lambda);
103 	if (lib->nodeprotohashtablesize > 0)
104 	{
105 		efree((CHAR *)lib->nodeprotohashtable);
106 		efree((CHAR *)lib->nodeprotoviewhashtable);
107 	}
108 	for(net = lib->freenetwork; net != NONETWORK; net = nextnet)
109 	{
110 		nextnet = net->nextnetwork;
111 		if (net->arctotal != 0) efree((CHAR *)net->arcaddr);
112 		efree((CHAR *)net);
113 	}
114 	clus = lib->cluster;
115 	efree((CHAR *)lib);
116 	freecluster(clus);
117 }
118 
119 /*
120  * routine to create a new library and return its address.  The library name
121  * is "name" and its disk file is "file".  If there is any error, NOLIBRARY
122  * is returned.
123  */
newlibrary(CHAR * name,CHAR * file)124 LIBRARY *newlibrary(CHAR *name, CHAR *file)
125 {
126 	REGISTER LIBRARY *lib;
127 	REGISTER CHAR *ch, nc;
128 	REGISTER BOOLEAN renamed;
129 
130 	/* error checks */
131 	renamed = FALSE;
132 	for(ch = name; *ch != 0; ch++)
133 	{
134 		nc = *ch;
135 		if (nc == ' ') nc = '-';
136 		if (nc == '\t' || nc == ':') nc = '-';
137 		if (nc != *ch) renamed = TRUE;
138 		*ch = nc;
139 	}
140 	if (renamed) ttyputerr(_("Warning: library renamed to '%s'"), name);
141 	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
142 		if (namesame(name, lib->libname) == 0)
143 			return((LIBRARY *)db_error(DBBADLIB|DBNEWLIBRARY));
144 
145 	/* create the library */
146 	lib = alloclibrary();
147 	if (lib == NOLIBRARY) return((LIBRARY *)db_error(DBNOMEM|DBNEWLIBRARY));
148 	(void)estrcpy(lib->cluster->clustername, x_("lib:"));
149 	(void)estrncpy(&lib->cluster->clustername[4], name, 25);
150 
151 	/* set library name and file */
152 	if (allocstring(&lib->libname, name, lib->cluster)) return(NOLIBRARY);
153 	if (allocstring(&lib->libfile, file, lib->cluster)) return(NOLIBRARY);
154 
155 	/* set units */
156 	lib->userbits |= (((el_units & INTERNALUNITS) >> INTERNALUNITSSH) << LIBUNITSSH);
157 
158 	/* link in the new library after the current library */
159 	if (el_curlib == NOLIBRARY)
160 	{
161 		/* this is the first library: make it the current one */
162 		lib->nextlibrary = NOLIBRARY;
163 		el_curlib = lib;
164 	} else
165 	{
166 		/* add this library to the list headed by "el_curlib" */
167 		lib->nextlibrary = el_curlib->nextlibrary;
168 		el_curlib->nextlibrary = lib;
169 	}
170 
171 	/* tell constraint system about new library */
172 	(*el_curconstraint->newlib)(lib);
173 
174 	/* mark a change to the database */
175 	db_changetimestamp++;
176 
177 	/* report library address */
178 	return(lib);
179 }
180 
181 /*
182  * routine to set the current library (represented by the global "el_curlib")
183  * to "lib"
184  */
selectlibrary(LIBRARY * lib,BOOLEAN changelambda)185 void selectlibrary(LIBRARY *lib, BOOLEAN changelambda)
186 {
187 	REGISTER LIBRARY *l, *lastlib;
188 	REGISTER TECHNOLOGY *tech;
189 
190 	/* quit if already done */
191 	if (lib == NOLIBRARY) return;
192 	if (el_curlib == lib) return;
193 
194 	/* unlink library from its current position */
195 	lastlib = NOLIBRARY;
196 	for(l = el_curlib; l != NOLIBRARY; l = l->nextlibrary)
197 	{
198 		if (l == lib) break;
199 		lastlib = l;
200 	}
201 	if (lastlib != NOLIBRARY) lastlib->nextlibrary = lib->nextlibrary;
202 
203 	/* link in at the head of the list */
204 	lib->nextlibrary = el_curlib;
205 	el_curlib = lib;
206 
207 	if (changelambda)
208 	{
209 		for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
210 			changetechnologylambda(tech, el_curlib->lambda[tech->techindex]);
211 	}
212 
213 	/* mark a change to the database */
214 	db_changetimestamp++;
215 }
216 
killlibrary(LIBRARY * lib)217 void killlibrary(LIBRARY *lib)
218 {
219 	REGISTER LIBRARY *l, *lastlib;
220 
221 	if (lib == el_curlib)
222 	{
223 		(void)db_error(DBBADLIB|DBKILLLIBRARY);
224 		return;
225 	}
226 
227 	/* tell constraint system about killed library */
228 	(*el_curconstraint->killlib)(lib);
229 
230 	/* unlink current library */
231 	lastlib = NOLIBRARY;
232 	for(l = el_curlib; l != NOLIBRARY; l = l->nextlibrary)
233 	{
234 		if (l == lib) break;
235 		lastlib = l;
236 	}
237 	if (lastlib != NOLIBRARY) lastlib->nextlibrary = lib->nextlibrary;
238 
239 	/* kill the requested library */
240 	eraselibrary(lib);
241 	efree(lib->libfile);
242 	efree(lib->libname);
243 	freelibrary(lib);
244 }
245 
246 /*
247  * routine to erase the contents of a library of cells.
248  * The index of the library is in "libindex".
249  */
eraselibrary(LIBRARY * lib)250 void eraselibrary(LIBRARY *lib)
251 {
252 	REGISTER NODEPROTO *np, *lnp;
253 	REGISTER PORTPROTO *pp, *lpt;
254 	REGISTER NODEINST *ni, *nni;
255 	REGISTER ARCINST *ai, *nai;
256 	REGISTER PORTARCINST *pi, *lpo;
257 	REGISTER PORTEXPINST *pe, *lpe;
258 	REGISTER NETWORK *net, *nnet;
259 	REGISTER INTBIG i;
260 
261 	/* see if this library exists */
262 	if (lib == NOLIBRARY) return;
263 
264 	/* flush all batched changes */
265 	noundoallowed();
266 
267 	for(i=0; i<el_maxtools; i++)
268 		if ((el_tools[i].toolstate & TOOLON) != 0 && el_tools[i].eraselibrary != 0)
269 			(*el_tools[i].eraselibrary)(lib);
270 
271 	/* erase the nodes, ports, arcs, and geometry modules in each nodeproto */
272 	for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
273 	{
274 		/* erase all arcs and nodes in this cell */
275 		for(ni = np->firstnodeinst; ni != NONODEINST; ni = nni)
276 		{
277 			nni = ni->nextnodeinst;
278 
279 			/* remove nodeinst link */
280 			if (ni->nextinst != NONODEINST) ni->nextinst->previnst = ni->previnst;
281 			if (ni->previnst != NONODEINST) ni->previnst->nextinst = ni->nextinst; else
282 				ni->proto->firstinst = ni->nextinst;
283 
284 			/* erase the portarcs on this nodeinst */
285 			for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = lpo)
286 			{
287 				lpo = pi->nextportarcinst;
288 				freeportarcinst(pi);
289 			}
290 
291 			/* erase the portexps on this nodeinst */
292 			for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = lpe)
293 			{
294 				lpe = pe->nextportexpinst;
295 				freeportexpinst(pe);
296 			}
297 
298 			/* erase the nodeinst */
299 			freegeom(ni->geom);
300 			freenodeinst(ni);
301 		}
302 		for(ai = np->firstarcinst; ai != NOARCINST; ai = nai)
303 		{
304 			nai = ai->nextarcinst;
305 			freegeom(ai->geom);
306 			freearcinst(ai);
307 		}
308 		for(net = np->firstnetwork; net != NONETWORK; net = nnet)
309 		{
310 			nnet = net->nextnetwork;
311 			net_freenetwork(net, np);
312 		}
313 		db_freertree(np->rtree);
314 	}
315 
316 	/* now erase the portprotos and nodeprotos */
317 	for(np = lib->firstnodeproto; np != NONODEPROTO; np = lnp)
318 	{
319 		lnp = np->nextnodeproto;
320 
321 		/* free the portproto entries */
322 		for(pp = np->firstportproto; pp != NOPORTPROTO; pp = lpt)
323 		{
324 			lpt = pp->nextportproto;
325 			efree(pp->protoname);
326 			freeportproto(pp);
327 		}
328 
329 		/* free the nodeinst proto */
330 		freenodeproto(np);
331 	}
332 	lib->firstnodeproto = NONODEPROTO;
333 	lib->tailnodeproto = NONODEPROTO;
334 	lib->curnodeproto = NONODEPROTO;
335 
336 	/* now erase the library information */
337 	if (lib->numvar != 0) db_freevars(&lib->firstvar, &lib->numvar);
338 
339 	/* mark a change to the database */
340 	db_changetimestamp++;
341 }
342 
343 /*
344  * routine to determine the appropriate library associated with the
345  * variable whose address is "addr" and type is "type".
346  */
whichlibrary(INTBIG addr,INTBIG type)347 LIBRARY *whichlibrary(INTBIG addr, INTBIG type)
348 {
349 	REGISTER GEOM *geom;
350 	REGISTER NODEPROTO *np;
351 
352 	switch (type&VTYPE)
353 	{
354 		case VNODEINST: return(((NODEINST *)addr)->parent->lib);
355 		case VNODEPROTO:
356 			np = (NODEPROTO *)addr;
357 			if (np->primindex == 0) return(np->lib);
358 			return(NOLIBRARY);
359 		case VPORTARCINST:
360 			return(((PORTARCINST *)addr)->conarcinst->parent->lib);
361 		case VPORTEXPINST:
362 			return(((PORTEXPINST *)addr)->exportproto->parent->lib);
363 		case VPORTPROTO:
364 			np = ((PORTPROTO *)addr)->parent;
365 			if (np->primindex == 0) return(np->lib);
366 			return(NOLIBRARY);
367 		case VARCINST: return(((ARCINST *)addr)->parent->lib);
368 		case VGEOM: geom = (GEOM *)addr;
369 			if (geom->entryisnode)
370 				return(geom->entryaddr.ni->parent->lib);
371 			return(geom->entryaddr.ai->parent->lib);
372 		case VLIBRARY: return((LIBRARY *)addr);
373 		case VNETWORK: return(((NETWORK *)addr)->parent->lib);
374 	}
375 	return(NOLIBRARY);
376 }
377 
378 /****************************** LAMBDA ******************************/
379 
380 /*
381  * routine to change value of lambda in "count" technologies.  The
382  * technologies are in "techarray" and the new lambda values for them
383  * are in "newlam".
384  * If "how" is 0, only change the technology (no libraries)
385  * If "how" is 1, change technology and library "whichlib"
386  * If "how" is 2, change technology and all libraries, specifically "whichlib"
387  */
changelambda(INTBIG count,TECHNOLOGY ** techarray,INTBIG * newlam,LIBRARY * whichlib,INTBIG how)388 void changelambda(INTBIG count, TECHNOLOGY **techarray, INTBIG *newlam,
389 	LIBRARY *whichlib, INTBIG how)
390 {
391 	REGISTER INTBIG *oldlam, i;
392 	REGISTER NODEPROTO *np;
393 	REGISTER LIBRARY *lib;
394 	REGISTER TECHNOLOGY *tech;
395 
396 	if (how == 0)
397 	{
398 		/* special case when only changing technology values */
399 		for(i=0; i<count; i++)
400 			changetechnologylambda(techarray[i], newlam[i]);
401 		return;
402 	}
403 
404 	/* make the technology agree with the current library */
405 	for(i=0; i<count; i++)
406 		changetechnologylambda(techarray[i], newlam[i]);
407 
408 	/* update lambda values in the libraries */
409 	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
410 	{
411 		lib->temp2 = 0;
412 		if (how == 1 && whichlib != lib) continue;
413 
414 		/* announce start of changes to this library */
415 		startobjectchange((INTBIG)lib, VLIBRARY);
416 
417 		/* preserve lambda for this library */
418 		oldlam = (INTBIG *)emalloc(count * SIZEOFINTBIG, el_tempcluster);
419 		if (oldlam == 0) return;
420 		for(i=0; i<count; i++)
421 		{
422 			tech = techarray[i];
423 			oldlam[i] = lib->lambda[tech->techindex];
424 			lib->lambda[tech->techindex] = newlam[i];
425 		}
426 
427 		lib->temp2 = (INTBIG)oldlam;
428 
429 	}
430 
431 	/* mark all cells in the library for scaling */
432 	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
433 		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
434 			np->temp1 = 0;
435 
436 	/* scale all the cells */
437 	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
438 	{
439 		if (how == 1 && whichlib != lib) continue;
440 		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
441 			db_scalecell(np, count, techarray, newlam);
442 		if (lib->firstnodeproto != NONODEPROTO)
443 			lib->userbits |= LIBCHANGEDMAJOR;
444 	}
445 
446 	/* free old lambda values */
447 	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
448 	{
449 		if (how == 1 && whichlib != lib) continue;
450 		efree((CHAR *)lib->temp2);
451 
452 		/* announce end of changes to this library */
453 		endobjectchange((INTBIG)lib, VLIBRARY);
454 	}
455 
456 }
457 
458 /*
459  * routine to recursively scale the contents of cell "np" by "newlam/oldlam"
460  */
db_scalecell(NODEPROTO * np,INTBIG count,TECHNOLOGY ** techarray,INTBIG * newlamarray)461 void db_scalecell(NODEPROTO *np, INTBIG count, TECHNOLOGY **techarray, INTBIG *newlamarray)
462 {
463 	REGISTER NODEINST *ni;
464 	REGISTER ARCINST *ai;
465 	REGISTER VARIABLE *var;
466 	REGISTER INTBIG i, *oldlamarray, *position, oldlam, newlam;
467 
468 	/* quit if the cell is already done */
469 	if (np->temp1 != 0) return;
470 
471 	/* first look for sub-cells that are not yet scaled */
472 	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
473 		if (ni->proto->primindex == 0)
474 			db_scalecell(ni->proto, count, techarray, newlamarray);
475 
476 	/* mark this cell "scaled" */
477 	np->temp1++;
478 
479 	/* see if this cell gets scaled */
480 	for(i=0; i<count; i++)
481 		if (np->tech == techarray[i]) break;
482 	if (i < count)
483 	{
484 		/* get the old lambda value */
485 		oldlamarray = (INTBIG *)np->lib->temp2;
486 		if (oldlamarray == 0) return;
487 		oldlam = oldlamarray[i];
488 		newlam = newlamarray[i];
489 
490 		/* scale nodes and arcs in the cell */
491 		for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
492 			db_scalenodeinst(ni, newlam, oldlam);
493 		for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
494 			db_scalearcinst(ai, newlam, oldlam);
495 
496 		/* scale the cell */
497 		db_boundcell(np, &np->lowx, &np->highx, &np->lowy, &np->highy);
498 
499 		/* scale variables such as cell-center and characteristic spacing */
500 		var = getvalkey((INTBIG)np, VNODEPROTO, VINTEGER|VISARRAY, el_prototype_center_key);
501 		if (var != NOVARIABLE)
502 		{
503 			position = (INTBIG *)var->addr;
504 			position[0] = muldiv(position[0], newlam, oldlam);
505 			position[1] = muldiv(position[1], newlam, oldlam);
506 		}
507 		var = getval((INTBIG)np, VNODEPROTO, VINTEGER|VISARRAY, x_("FACET_characteristic_spacing"));
508 		if (var != NOVARIABLE)
509 		{
510 			position = (INTBIG *)var->addr;
511 			position[0] = muldiv(position[0], newlam, oldlam);
512 			position[1] = muldiv(position[1], newlam, oldlam);
513 		}
514 	}
515 }
516 
db_scalenodeinst(NODEINST * ni,INTBIG newlam,INTBIG oldlam)517 void db_scalenodeinst(NODEINST *ni, INTBIG newlam, INTBIG oldlam)
518 {
519 	REGISTER VARIABLE *var;
520 	REGISTER INTBIG sizex, sizey, dx, dy, len, i, newlx, newhx, newly, newhy;
521 
522 	newlx = muldiv(ni->lowx,  newlam, oldlam);
523 	newhx = muldiv(ni->highx, newlam, oldlam);
524 	newly = muldiv(ni->lowy,  newlam, oldlam);
525 	newhy = muldiv(ni->highy, newlam, oldlam);
526 
527 	if (ni->proto->primindex == 0)
528 	{
529 		sizex = ni->proto->highx - ni->proto->lowx;
530 		sizey = ni->proto->highy - ni->proto->lowy;
531 		dx = ni->highx - ni->lowx - sizex;
532 		dy = ni->highy - ni->lowy - sizey;
533 		ni->lowx = newlx;   ni->highx = newhx;
534 		ni->lowy = newly;   ni->highy = newhy;
535 		if (dx != 0 || dy != 0)
536 		{
537 			dx = ni->highx - ni->lowx - sizex;
538 			dy = ni->highy - ni->lowy - sizey;
539 			if (dx != 0 || dy != 0)
540 			{
541 				ttyputmsg(_("Cell %s, node %s size and position adjusted"),
542 					describenodeproto(ni->parent), describenodeinst(ni));
543 				ni->lowx += dx/2;   ni->highx = ni->lowx + sizex;
544 				ni->lowy += dy/2;   ni->highy = ni->lowy + sizey;
545 			}
546 		}
547 	} else
548 	{
549 		/* look for trace data on primitives */
550 		ni->lowx = newlx;   ni->highx = newhx;
551 		ni->lowy = newly;   ni->highy = newhy;
552 		var = gettrace(ni);
553 		if (var != NOVARIABLE)
554 		{
555 			len = getlength(var);
556 			for(i=0; i<len; i++)
557 				((INTBIG *)var->addr)[i] = muldiv(((INTBIG *)var->addr)[i], newlam, oldlam);
558 		}
559 	}
560 	updategeom(ni->geom, ni->parent);
561 }
562 
db_scalearcinst(ARCINST * ai,INTBIG newlam,INTBIG oldlam)563 void db_scalearcinst(ARCINST *ai, INTBIG newlam, INTBIG oldlam)
564 {
565 	REGISTER VARIABLE *var;
566 
567 	ai->width = muldiv(ai->width, newlam, oldlam);
568 	ai->end[0].xpos = muldiv(ai->end[0].xpos, newlam, oldlam);
569 	ai->end[0].ypos = muldiv(ai->end[0].ypos, newlam, oldlam);
570 	ai->end[1].xpos = muldiv(ai->end[1].xpos, newlam, oldlam);
571 	ai->end[1].ypos = muldiv(ai->end[1].ypos, newlam, oldlam);
572 	ai->length = computedistance(ai->end[0].xpos, ai->end[0].ypos,
573 		ai->end[1].xpos, ai->end[1].ypos);
574 	db_validatearcinst(ai);
575 	updategeom(ai->geom, ai->parent);
576 
577 	/* look for curvature data on primitives */
578 	var = getvalkey((INTBIG)ai, VARCINST, VINTEGER, el_arc_radius_key);
579 	if (var != NOVARIABLE) var->addr = muldiv(var->addr, newlam, oldlam);
580 }
581 
582 /*
583  * routine to validate arcinst "ai" after being scaled.
584  */
db_validatearcinst(ARCINST * ai)585 void db_validatearcinst(ARCINST *ai)
586 {
587 	REGISTER BOOLEAN inside0, inside1;
588 	INTBIG lx0, lx1, hx0, hx1, ly0, ly1, hy0, hy1, fx, fy, tx, ty;
589 	REGISTER POLYGON *poly0, *poly1;
590 
591 	/* make sure there is a polygon */
592 	poly0 = allocpolygon(4, db_cluster);
593 	poly1 = allocpolygon(4, db_cluster);
594 
595 	/* if nothing is outside port, quit */
596 	fx = ai->end[0].xpos;
597 	fy = ai->end[0].ypos;
598 	tx = ai->end[1].xpos;
599 	ty = ai->end[1].ypos;
600 	shapeportpoly(ai->end[0].nodeinst, ai->end[0].portarcinst->proto, poly0, FALSE);
601 	inside0 = isinside(fx, fy, poly0);
602 	shapeportpoly(ai->end[1].nodeinst, ai->end[1].portarcinst->proto, poly1, FALSE);
603 	inside1 = isinside(tx, ty, poly1);
604 	if (!inside0 || !inside1)
605 	{
606 		/* if arcinst is not fixed-angle, run it directly to the port centers */
607 		if ((ai->userbits&FIXANG) == 0)
608 		{
609 			if (!inside0) closestpoint(poly0, &fx, &fy);
610 			if (!inside1) closestpoint(poly1, &tx, &ty);
611 			db_setarcinst(ai, fx, fy, tx, ty);
612 		} else
613 		{
614 			/* get bounding boxes of polygons */
615 			getbbox(poly0, &lx0, &hx0, &ly0, &hy0);
616 			getbbox(poly1, &lx1, &hx1, &ly1, &hy1);
617 
618 			/* if fixed-angle path runs between the ports, adjust the arcinst */
619 			if (arcconnects(((ai->userbits&AANGLE) >> AANGLESH) * 10, lx0,hx0, ly0,hy0, lx1,hx1,
620 				ly1,hy1, &fx,&fy, &tx,&ty))
621 			{
622 				closestpoint(poly0, &fx, &fy);
623 				closestpoint(poly1, &tx, &ty);
624 				db_setarcinst(ai, fx, fy, tx, ty);
625 			} else
626 			{
627 				/* give up and remove the constraint */
628 				fx = ai->end[0].xpos;
629 				fy = ai->end[0].ypos;
630 				tx = ai->end[1].xpos;
631 				ty = ai->end[1].ypos;
632 				if (!inside0) closestpoint(poly0, &fx, &fy);
633 				if (!inside1) closestpoint(poly1, &tx, &ty);
634 				ai->userbits &= ~FIXANG;
635 				db_setarcinst(ai, fx, fy, tx, ty);
636 				ttyputmsg(_("Cell %s, arc %s no longer fixed-angle"),
637 					describenodeproto(ai->parent), describearcinst(ai));
638 			}
639 		}
640 	}
641 	freepolygon(poly0);
642 	freepolygon(poly1);
643 }
644 
db_setarcinst(ARCINST * ai,INTBIG fx,INTBIG fy,INTBIG tx,INTBIG ty)645 void db_setarcinst(ARCINST *ai, INTBIG fx, INTBIG fy, INTBIG tx, INTBIG ty)
646 {
647 	/* check for null arcinst motion */
648 	if (fx == ai->end[0].xpos && fy == ai->end[0].ypos &&
649 		tx == ai->end[1].xpos && ty == ai->end[1].ypos) return;
650 
651 	ai->end[0].xpos = fx;   ai->end[0].ypos = fy;
652 	ai->end[1].xpos = tx;   ai->end[1].ypos = ty;
653 	ai->length = computedistance(fx,fy, tx,ty);
654 	determineangle(ai);
655 	updategeom(ai->geom, ai->parent);
656 }
657 
658 /*
659  * Routine to change the value of lambda in technology "tech" to
660  * "newlambda".
661  */
changetechnologylambda(TECHNOLOGY * tech,INTBIG newlambda)662 void changetechnologylambda(TECHNOLOGY *tech, INTBIG newlambda)
663 {
664 	REGISTER NODEPROTO *np;
665 	REGISTER ARCPROTO *ap;
666 	REGISTER INTBIG oldlambda;
667 
668 	oldlambda = tech->deflambda;
669 	if (newlambda <= 0) newlambda = 1;
670 	if (newlambda == oldlambda) return;
671 
672 	/* change the default width of the primitive arc prototypes */
673 	for(ap = tech->firstarcproto; ap != NOARCPROTO; ap = ap->nextarcproto)
674 	{
675 		ap->nominalwidth = muldiv(ap->nominalwidth, newlambda, oldlambda);
676 	}
677 
678 	/* now change the default size of the primitive node prototypes */
679 	for(np = tech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
680 	{
681 		np->lowx  = muldiv(np->lowx,  newlambda, oldlambda);
682 		np->highx = muldiv(np->highx, newlambda, oldlambda);
683 		np->lowy  = muldiv(np->lowy,  newlambda, oldlambda);
684 		np->highy = muldiv(np->highy, newlambda, oldlambda);
685 	}
686 
687 	/* finally, reset the default lambda value */
688 	tech->deflambda = newlambda;
689 
690 	/* mark a change to the database */
691 	db_changetimestamp++;
692 }
693 
694 /*
695  * Routine to change the internal units in library "whichlib" from "oldunits"
696  * to "newunits".  If "whichlib" is NOLIBRARY, change the entire database (all
697  * libraries and technologies).
698  */
changeinternalunits(LIBRARY * whichlib,INTBIG oldunits,INTBIG newunits)699 void changeinternalunits(LIBRARY *whichlib, INTBIG oldunits, INTBIG newunits)
700 {
701 	REGISTER INTBIG hardscaleerror, softscaleerror, len;
702 	INTBIG num, den;
703 	REGISTER INTBIG i;
704 	REGISTER CHAR *errortype, *errorlocation;
705 	REGISTER TECHNOLOGY *tech;
706 	REGISTER LIBRARY *lib;
707 	REGISTER NODEPROTO *np;
708 	REGISTER ARCPROTO *ap;
709 	REGISTER NODEINST *ni;
710 	REGISTER ARCINST *ai;
711 	REGISTER VARIABLE *var;
712 	REGISTER WINDOWPART *w;
713 
714 	hardscaleerror = softscaleerror = 0;
715 	db_getinternalunitscale(&num, &den, oldunits, newunits);
716 	if (whichlib == NOLIBRARY)
717 	{
718 		/* global change: set units */
719 		if ((oldunits&INTERNALUNITS) == newunits) return;
720 		el_units = (el_units & ~INTERNALUNITS) | newunits;
721 
722 		/* scale all technologies */
723 		for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
724 		{
725 			/* scale arc width */
726 			for(ap = tech->firstarcproto; ap != NOARCPROTO; ap = ap->nextarcproto)
727 			{
728 				hardscaleerror += db_scaleunits(&ap->nominalwidth, num, den);
729 			}
730 
731 			/* scale node sizes */
732 			for(np = tech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
733 			{
734 				hardscaleerror += db_scaleunits(&np->lowx,  num, den);
735 				hardscaleerror += db_scaleunits(&np->highx, num, den);
736 				hardscaleerror += db_scaleunits(&np->lowy,  num, den);
737 				hardscaleerror += db_scaleunits(&np->highy, num, den);
738 			}
739 
740 			/* finally, scale lambda */
741 			hardscaleerror += db_scaleunits(&tech->deflambda, num, den);
742 			if (tech->deflambda <= 0) tech->deflambda = 1;
743 		}
744 
745 		/* scale lambda in libraries */
746 		for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
747 			for(i=0; i<el_maxtech; i++)
748 				hardscaleerror += db_scaleunits(&lib->lambda[i], num, den);
749 
750 		/* scale all display windows */
751 		for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
752 		{
753 			if (w->curnodeproto == NONODEPROTO) continue;
754 			softscaleerror += db_scaleunits(&w->screenlx, num, den);
755 			softscaleerror += db_scaleunits(&w->screenly, num, den);
756 			softscaleerror += db_scaleunits(&w->screenhx, num, den);
757 			softscaleerror += db_scaleunits(&w->screenhy, num, den);
758 		}
759 	}
760 
761 	/* scale all appropriate libraries */
762 	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
763 	{
764 		/* only scale requested library if request was made */
765 		if (whichlib != NOLIBRARY && whichlib != lib) continue;
766 
767 		/* scale each cell */
768 		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
769 		{
770 			/* scale nodes */
771 			for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
772 			{
773 				hardscaleerror += db_scaleunits(&ni->lowx,  num, den);
774 				hardscaleerror += db_scaleunits(&ni->highx, num, den);
775 				hardscaleerror += db_scaleunits(&ni->lowy,  num, den);
776 				hardscaleerror += db_scaleunits(&ni->highy, num, den);
777 				if (ni->proto->primindex != 0)
778 				{
779 					/* look for trace data on primitives */
780 					var = gettrace(ni);
781 					if (var != NOVARIABLE)
782 					{
783 						len = getlength(var);
784 						for(i=0; i<len; i++)
785 							hardscaleerror += db_scaleunits(&((INTBIG *)var->addr)[i], num, den);
786 					}
787 				}
788 				updategeom(ni->geom, np);
789 			}
790 
791 			/* scale arcs */
792 			for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
793 			{
794 				hardscaleerror += db_scaleunits(&ai->width, num, den);
795 				hardscaleerror += db_scaleunits(&ai->end[0].xpos, num, den);
796 				hardscaleerror += db_scaleunits(&ai->end[0].ypos, num, den);
797 				hardscaleerror += db_scaleunits(&ai->end[1].xpos, num, den);
798 				hardscaleerror += db_scaleunits(&ai->end[1].ypos, num, den);
799 				ai->length = computedistance(ai->end[0].xpos, ai->end[0].ypos,
800 					ai->end[1].xpos, ai->end[1].ypos);
801 				updategeom(ai->geom, np);
802 
803 				/* look for curvature data */
804 				var = getvalkey((INTBIG)ai, VARCINST, VINTEGER, el_arc_radius_key);
805 				if (var != NOVARIABLE) hardscaleerror += db_scaleunits(&var->addr, num, den);
806 			}
807 
808 			/* scale cell size */
809 			db_boundcell(np, &np->lowx, &np->highx, &np->lowy, &np->highy);
810 		}
811 		lib->userbits = (lib->userbits & ~LIBUNITS) |
812 			(((newunits & INTERNALUNITS) >> INTERNALUNITSSH) << LIBUNITSSH);
813 		if (lib->firstnodeproto != NONODEPROTO) lib->userbits |= LIBCHANGEDMAJOR;
814 	}
815 
816 	/* report any failures */
817 	if (hardscaleerror != 0 || softscaleerror != 0)
818 	{
819 		if (den > num) errortype = _("roundoff"); else
820 			errortype = _("overflow");
821 		if (hardscaleerror == 0) errorlocation = _("display"); else
822 		{
823 			if (whichlib == NOLIBRARY) errorlocation = _("database"); else
824 				errorlocation = _("library");
825 		}
826 		ttyputerr(_("Change caused %s errors in %s"), errortype, errorlocation);
827 		if (hardscaleerror != 0)
828 		{
829 			if (whichlib == NOLIBRARY)
830 			{
831 				ttyputmsg(_("Recommend check of database for validity and keep old library files"));
832 				for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
833 					lib->userbits &= ~READFROMDISK;
834 			} else
835 			{
836 				ttyputmsg(_("Recommend check of database for validity and keep old library file"));
837 				whichlib->userbits &= ~READFROMDISK;
838 			}
839 		} else
840 		{
841 			ttyputmsg(_("Recommend redisplay"));
842 		}
843 	}
844 
845 	/* mark a change to the database */
846 	db_changetimestamp++;
847 }
848 
849 /*
850  * Routine to scale "value" by "num"/"den".  Returns zero if scale worked,
851  * nonzero if overflow/underflow occurred.
852  */
db_scaleunits(INTBIG * value,INTBIG num,INTBIG den)853 INTBIG db_scaleunits(INTBIG *value, INTBIG num, INTBIG den)
854 {
855 	REGISTER INTBIG orig, scaled, reconstructed;
856 
857 	orig = *value;
858 	scaled = muldiv(orig, num, den);
859 	*value = scaled;
860 	reconstructed = muldiv(scaled, den, num);
861 	if (orig == reconstructed) return(0);
862 	return(1);
863 }
864