1 /* -*- tab-width: 4 -*-
2  *
3  * Electric(tm) VLSI Design System
4  *
5  * File: usrctech.c
6  * User interface tool: technology translation module
7  * Written by: Steven M. Rubin, Static Free Software
8  * Schematic conversion written by: Nora Ryan, Schlumberger Palo Alto Research
9  *
10  * Copyright (c) 2000 Static Free Software.
11  *
12  * Electric(tm) is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * Electric(tm) is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with Electric(tm); see the file COPYING.  If not, write to
24  * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
25  * Boston, Mass 02111-1307, USA.
26  *
27  * Static Free Software
28  * 4119 Alpine Road
29  * Portola Valley, California 94028
30  * info@staticfreesoft.com
31  */
32 
33 #include "global.h"
34 #include "efunction.h"
35 #include "egraphics.h"
36 #include "tech.h"
37 #include "tecgen.h"
38 #include "tecschem.h"
39 #include "usr.h"
40 #include "drc.h"
41 #include "usredtec.h"		/* for technology documentation */
42 #include <math.h>
43 
44 #define TRAN_PIN   -1
45 #define TRAN_CELL -2
46 
47 /* prototypes for local routines */
48 static PORTPROTO *us_convport(NODEINST*, NODEINST*, PORTPROTO*);
49 static void       us_dumpfields(CHAR***, INTBIG, INTBIG, FILE*, CHAR*);
50 static ARCPROTO  *us_figurenewaproto(ARCPROTO*, TECHNOLOGY*);
51 static NODEPROTO *us_figurenewnproto(NODEINST*, TECHNOLOGY*);
52 static PORTPROTO *us_tranconvpp(NODEINST*, PORTPROTO*);
53 static PORTPROTO *us_trangetproto(NODEINST*, INTBIG);
54 static INTBIG     us_tranismos(NODEINST*);
55 static void       us_tranplacenode(NODEINST*, NODEPROTO*, NODEPROTO*, TECHNOLOGY*, TECHNOLOGY*);
56 static NODEPROTO *us_tran_linkage(CHAR*, VIEW*, NODEPROTO*);
57 static void       us_tran_logmakearcs(NODEPROTO*, NODEPROTO*);
58 static NODEINST  *us_tran_logmakenode(NODEPROTO*, NODEINST*, INTSML, INTSML, NODEPROTO*, TECHNOLOGY*);
59 static void       us_tran_logmakenodes(NODEPROTO*, NODEPROTO*, TECHNOLOGY*);
60 static NODEPROTO *us_tran_makelayoutcells(NODEPROTO*, CHAR*, TECHNOLOGY*, TECHNOLOGY*, VIEW*);
61 static BOOLEAN    us_tran_makelayoutparts(NODEPROTO*, NODEPROTO*, TECHNOLOGY*, TECHNOLOGY*, VIEW*);
62 static void       us_tran_makemanhattan(NODEPROTO*);
63 
64 /*
65  * this routine converts cell "oldcell" to one of technology "newtech".
66  * Returns the address of the new cell (NONODEPROTO on error).
67  */
us_convertcell(NODEPROTO * oldcell,TECHNOLOGY * newtech)68 NODEPROTO *us_convertcell(NODEPROTO *oldcell, TECHNOLOGY *newtech)
69 {
70 	NODEPROTO *newcell, *np;
71 	NODEPROTO *(*localconversion)(NODEPROTO*, TECHNOLOGY*);
72 	REGISTER TECHNOLOGY *oldtech, *tech;
73 	REGISTER ARCINST *ai;
74 	VARIABLE *var;
75 
76 	/* cannot convert text-only views */
77 	if ((oldcell->cellview->viewstate&TEXTVIEW) != 0)
78 	{
79 		ttyputerr(_("Cannot convert textual views: only layout and schematic"));
80 		return(NONODEPROTO);
81 	}
82 
83 	/* separate code if converting to the schematic technology */
84 	if (newtech == sch_tech)
85 	{
86 		/*
87 		 * Look to see if the technology has its own routine.
88 		 * Note that it may call some of the functions in this file!
89 		 */
90 		var = getval((INTBIG)el_curtech, VTECHNOLOGY, VADDRESS, x_("TECH_schematic_conversion"));
91 		if (var != NOVARIABLE)
92 		{
93 			localconversion = (NODEPROTO *(*)(NODEPROTO*, TECHNOLOGY*))var->addr;
94 			newcell = (*(localconversion))(oldcell, newtech);
95 			if (newcell == NONODEPROTO) return(NONODEPROTO);
96 		} else    /* Convert to schematic here */
97 		{
98 			/* create cell in new technology */
99 			newcell = us_tran_linkage(oldcell->protoname, el_schematicview, oldcell);
100 			if (newcell == NONODEPROTO) return(NONODEPROTO);
101 
102 			/* create the parts in this cell */
103 			us_tran_logmakenodes(oldcell, newcell, newtech);
104 			us_tran_logmakearcs(oldcell, newcell);
105 
106 			/* now make adjustments for manhattan-ness */
107 			us_tran_makemanhattan(newcell);
108 
109 			/* set "FIXANG" if reasonable */
110 			for(ai = newcell->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
111 			{
112 				if (ai->end[0].xpos == ai->end[1].xpos &&
113 					ai->end[0].ypos == ai->end[1].ypos) continue;
114 				if ((figureangle(ai->end[0].xpos, ai->end[0].ypos, ai->end[1].xpos,
115 					ai->end[1].ypos)%450) == 0) ai->userbits |= FIXANG;
116 			}
117 		}
118 
119 		/* after adjusting contents, must re-solve to get proper cell size */
120 		(*el_curconstraint->solve)(newcell);
121 	} else
122 	{
123 		/* do general conversion between technologies */
124 		oldtech = oldcell->tech;
125 
126 		/* reset flag that primitive cannot be converted */
127 		for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
128 			for(np = tech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
129 				np->temp1 = 0;
130 		newcell = us_tran_makelayoutcells(oldcell, oldcell->protoname,
131 			oldtech, newtech, el_layoutview);
132 	}
133 	return(newcell);
134 }
135 
136 /*
137  * routine to create a new cell called "newcellname" that is to be the
138  * equivalent to an old cell in "cell".  The view type of the new cell is
139  * in "newcellview" and the view type of the old cell is in "cellview"
140  */
us_tran_linkage(CHAR * newcellname,VIEW * newcellview,NODEPROTO * cell)141 NODEPROTO *us_tran_linkage(CHAR *newcellname, VIEW *newcellview, NODEPROTO *cell)
142 {
143 	NODEPROTO *newcell;
144 	REGISTER CHAR *cellname;
145 	REGISTER void *infstr;
146 
147 	/* create the new cell */
148 	if (newcellview->sviewname[0] == 0) cellname = newcellname; else
149 	{
150 		infstr = initinfstr();
151 		addstringtoinfstr(infstr, newcellname);
152 		addtoinfstr(infstr, '{');
153 		addstringtoinfstr(infstr, newcellview->sviewname);
154 		addtoinfstr(infstr, '}');
155 		cellname = returninfstr(infstr);
156 	}
157 	newcell = us_newnodeproto(cellname, cell->lib);
158 	if (newcell == NONODEPROTO)
159 		ttyputmsg(_("Could not create cell: %s"), cellname); else
160 			ttyputmsg(_("Creating new cell: %s"), cellname);
161 	return(newcell);
162 }
163 
164 /********************** CODE FOR CONVERSION TO SCHEMATIC **********************/
165 
us_tran_logmakenodes(NODEPROTO * cell,NODEPROTO * newcell,TECHNOLOGY * newtech)166 void us_tran_logmakenodes(NODEPROTO *cell, NODEPROTO *newcell, TECHNOLOGY *newtech)
167 {
168 	NODEINST *ni, *schemni;
169 	NODEPROTO *onp;
170 	PORTEXPINST *pexp;
171 	REGISTER PORTPROTO *pp, *pp2;
172 	REGISTER PORTARCINST *pi;
173 	REGISTER VARIABLE *var;
174 	INTSML rotate, trans;
175 	INTBIG type, len, wid, lambda, xoff, yoff, i, size;
176 	UINTBIG descript[TEXTDESCRIPTSIZE];
177 
178 	/*
179 	 * for each node, create a new node in the newcell, of the correct
180 	 * logical type.  Also, store a pointer to the new node in the old
181 	 * node's temp1.  This is used in the arc translation part of the
182 	 * program to find the new ends of each arc.
183 	 */
184 	for(ni = cell->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
185 	{
186 		type = us_tranismos(ni);
187 		switch (type)
188 		{
189 			case TRAN_PIN:
190 				/* compute new x, y coordinates */
191 				schemni = us_tran_logmakenode(sch_wirepinprim, ni, 0, 0, newcell, newtech);
192 				break;
193 			case TRAN_CELL:
194 				FOR_CELLGROUP(onp, ni->proto)
195 					if (onp->cellview == el_schematicview) break;
196 				if (onp == NONODEPROTO)
197 				{
198 					onp = us_convertcell(ni->proto, newtech);
199 					if (onp == NONODEPROTO) break;
200 				}
201 				schemni = us_tran_logmakenode(onp, ni, ni->transpose, ni->rotation,
202 					newcell, newtech);
203 				break;
204 			case NPUNKNOWN:	/* could not match it */
205 				 schemni = NONODEINST;
206 				 break;
207 			default:		/* always a transistor */
208 				rotate = ni->rotation;
209 				trans = ni->transpose;
210 				for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
211 					if (pi->proto == ni->proto->firstportproto) break;
212 				if (pi != NOPORTARCINST)
213 				{
214 					if (trans == 0) rotate += 900; else rotate += 2700;
215 					trans = 1 - trans;
216 				}
217 				rotate += 2700;
218 				while (rotate >= 3600) rotate -= 3600;
219 				schemni = us_tran_logmakenode(sch_transistorprim, ni, trans, rotate,
220 					newcell, newtech);
221 
222 				/* set the type of the transistor */
223 				switch (type)
224 				{
225 					case NPTRAPMOS:  schemni->userbits |= TRANPMOS;    break;
226 					case NPTRANMOS:  schemni->userbits |= TRANNMOS;    break;
227 					case NPTRADMOS:  schemni->userbits |= TRANDMOS;    break;
228 					case NPTRAPNP:   schemni->userbits |= TRANPNP;     break;
229 					case NPTRANPN:   schemni->userbits |= TRANNPN;     break;
230 					case NPTRANJFET: schemni->userbits |= TRANNJFET;   break;
231 					case NPTRAPJFET: schemni->userbits |= TRANPJFET;   break;
232 					case NPTRADMES:  schemni->userbits |= TRANDMES;    break;
233 					case NPTRAEMES:  schemni->userbits |= TRANEMES;    break;
234 				}
235 
236 				/* add in the size */
237 				transistorsize(ni, &len, &wid);
238 				if (len >= 0 && wid >= 0)
239 				{
240 					TDCLEAR(descript);
241 					defaulttextsize(3, descript);
242 					lambda = lambdaofnode(ni);
243 					if (type == NPTRAPMOS || type == NPTRANMOS || type == NPTRADMOS ||
244 						type == NPTRANJFET || type == NPTRAPJFET ||
245 						type == NPTRADMES || type == NPTRAEMES)
246 					{
247 						/* set length/width */
248 						us_getlenwidoffset(schemni, descript, &xoff, &yoff);
249 						var = setvalkey((INTBIG)schemni, VNODEINST, el_attrkey_length,
250 							len*WHOLE/lambda, VFRACT|VDISPLAY);
251 						if (var != NOVARIABLE)
252 						{
253 							TDCOPY(var->textdescript, descript);
254 							size = TDGETSIZE(var->textdescript);
255 							i = TXTGETPOINTS(size);
256 							if (i > 3) size = TXTSETPOINTS(i-2); else
257 							{
258 								i = TXTGETQLAMBDA(size);
259 								if (i > 3) size = TXTSETQLAMBDA(i-2);
260 							}
261 							TDSETSIZE(var->textdescript, size);
262 							TDSETOFF(var->textdescript, TDGETXOFF(descript)-xoff,
263 								TDGETYOFF(descript)-yoff);
264 						}
265 						var = setvalkey((INTBIG)schemni, VNODEINST, el_attrkey_width,
266 							wid*WHOLE/lambda, VFRACT|VDISPLAY);
267 						if (var != NOVARIABLE)
268 						{
269 							TDCOPY(var->textdescript, descript);
270 							TDSETOFF(var->textdescript, TDGETXOFF(descript)+xoff,
271 								TDGETYOFF(descript)+yoff);
272 						}
273 					} else
274 					{
275 						/* set area */
276 						var = setvalkey((INTBIG)schemni, VNODEINST, el_attrkey_area,
277 							len*WHOLE/lambda, VFRACT|VDISPLAY);
278 						if (var != NOVARIABLE)
279 							TDCOPY(var->textdescript, descript);
280 					}
281 				}
282 		}
283 
284 		/* store the new node in the old node */
285 		ni->temp1 = (INTBIG)schemni;
286 
287 		/* reexport ports */
288 		if (schemni != NONODEINST)
289 		{
290 			for(pexp = ni->firstportexpinst; pexp != NOPORTEXPINST; pexp = pexp->nextportexpinst)
291 			{
292 				pp = us_tranconvpp(ni, pexp->proto);
293 				if (pp == NOPORTPROTO) continue;
294 				pp2 = newportproto(newcell, schemni, pp, pexp->exportproto->protoname);
295 				if (pp2 == NOPORTPROTO) return;
296 				pp2->userbits = (pp2->userbits & ~STATEBITS) |
297 					(pexp->exportproto->userbits & STATEBITS);
298 				TDCOPY(pp2->textdescript, pexp->exportproto->textdescript);
299 				if (copyvars((INTBIG)pexp->exportproto, VPORTPROTO, (INTBIG)pp2, VPORTPROTO, FALSE))
300 					return;
301 			}
302 			endobjectchange((INTBIG)schemni, VNODEINST);
303 		}
304 	}
305 }
306 
us_tran_logmakenode(NODEPROTO * prim,NODEINST * orig,INTSML trn,INTSML rot,NODEPROTO * newcell,TECHNOLOGY * newtech)307 NODEINST *us_tran_logmakenode(NODEPROTO *prim, NODEINST *orig, INTSML trn, INTSML rot,
308 	NODEPROTO *newcell, TECHNOLOGY *newtech)
309 {
310 	REGISTER INTBIG cx, cy, scaleu, scaled;
311 	INTBIG sx, sy;
312 	REGISTER NODEINST *newni;
313 	REGISTER TECHNOLOGY *oldtech;
314 
315 	scaleu = el_curlib->lambda[newtech->techindex];
316 	oldtech = orig->proto->tech;
317 	scaled = el_curlib->lambda[oldtech->techindex];
318 	cx = muldiv((orig->lowx+orig->highx)/2, scaleu, scaled) - (prim->highx-prim->lowx)/2;
319 	cy = muldiv((orig->lowy+orig->highy)/2, scaleu, scaled) - (prim->highy-prim->lowy)/2;
320 	defaultnodesize(prim, &sx, &sy);
321 	newni = newnodeinst(prim, cx, cx+sx, cy, cy+sy, trn, rot, newcell);
322 	return(newni);
323 }
324 
us_tran_logmakearcs(NODEPROTO * cell,NODEPROTO * newcell)325 void us_tran_logmakearcs(NODEPROTO *cell, NODEPROTO *newcell)
326 {
327 	PORTPROTO *oldpp1, *oldpp2, *newpp1, *newpp2;
328 	ARCINST *oldai, *newai;
329 	NODEINST *newni1, *newni2;
330 	INTBIG x1, x2, y1, y2, bits;
331 
332 	/*
333 	 * for each arc in cell, find the ends in the new technology, and
334 	 * make a new arc to connect them in the new cell.
335 	 */
336 	for(oldai = cell->firstarcinst; oldai != NOARCINST; oldai = oldai->nextarcinst)
337 	{
338 		newni1 = (NODEINST *)oldai->end[0].nodeinst->temp1;
339 		newni2 = (NODEINST *)oldai->end[1].nodeinst->temp1;
340 		if (newni1 == NONODEINST || newni2 == NONODEINST) continue;
341 		oldpp1 = oldai->end[0].portarcinst->proto;
342 		oldpp2 = oldai->end[1].portarcinst->proto;
343 
344 		/* find the logical portproto for the first end node */
345 		newpp1 = us_tranconvpp(oldai->end[0].nodeinst, oldpp1);
346 		if (newpp1 == NOPORTPROTO) continue;
347 
348 		/* find the logical portproto for the second end node */
349 		newpp2 = us_tranconvpp(oldai->end[1].nodeinst, oldpp2);
350 		if (newpp2 == NOPORTPROTO) continue;
351 
352 		/* find the endpoints of the arc */
353 		portposition(newni1, newpp1, &x1, &y1);
354 		portposition(newni2, newpp2, &x2, &y2);
355 
356 		/* create the new arc */
357 		bits = us_makearcuserbits(sch_wirearc) & ~(FIXANG|FIXED);
358 		newai = newarcinst(sch_wirearc, defaultarcwidth(sch_wirearc), bits,
359 			newni1, newpp1, x1, y1, newni2, newpp2, x2, y2, newcell);
360 		if (newai == NOARCINST) break;
361 		endobjectchange((INTBIG)newai, VARCINST);
362 	}
363 }
364 
365 #define MAXADJUST 5
366 
us_tran_makemanhattan(NODEPROTO * newcell)367 void us_tran_makemanhattan(NODEPROTO *newcell)
368 {
369 	REGISTER NODEINST *ni;
370 	REGISTER PORTARCINST *pi;
371 	REGISTER ARCINST *ai;
372 	REGISTER INTBIG e, count, i, j;
373 	REGISTER INTBIG dist, bestdist, bestx, besty, xp, yp;
374 	INTBIG x[MAXADJUST], y[MAXADJUST];
375 
376 	/* adjust this cell */
377 	for(ni = newcell->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
378 	{
379 		if (ni->proto->primindex == 0) continue;
380 		if (((ni->proto->userbits&NFUNCTION) >> NFUNCTIONSH) != NPPIN) continue;
381 
382 		/* see if this pin can be adjusted so that all wires are manhattan */
383 		count = 0;
384 		for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
385 		{
386 			ai = pi->conarcinst;
387 			if (ai->end[0].nodeinst == ni)
388 			{
389 				if (ai->end[1].nodeinst == ni) continue;
390 				e = 1;
391 			} else e = 0;
392 			x[count] = ai->end[e].xpos;   y[count] = ai->end[e].ypos;
393 			count++;
394 			if (count >= MAXADJUST) break;
395 		}
396 		if (count == 0) continue;
397 
398 		/* now adjust for all these points */
399 		xp = (ni->lowx + ni->highx) / 2;   yp = (ni->lowy + ni->highy) / 2;
400 		bestdist = MAXINTBIG;
401 		for(i=0; i<count; i++) for(j=0; j<count; j++)
402 		{
403 			dist = abs(xp - x[i]) + abs(yp - y[j]);
404 			if (dist > bestdist) continue;
405 			bestdist = dist;
406 			bestx = x[i];   besty = y[j];
407 		}
408 
409 		/* if there was a better place, move the node */
410 		if (bestdist != MAXINTBIG)
411 			modifynodeinst(ni, bestx-xp, besty-yp, bestx-xp, besty-yp, 0, 0);
412 	}
413 }
414 
415 /* find the logical portproto corresponding to the mos portproto of ni */
us_tranconvpp(NODEINST * ni,PORTPROTO * mospp)416 PORTPROTO *us_tranconvpp(NODEINST *ni, PORTPROTO *mospp)
417 {
418 	PORTPROTO *schempp, *pp;
419 	NODEINST *schemni;
420 	INTBIG port;
421 
422 	schemni = (NODEINST *)ni->temp1;
423 
424 	switch (us_tranismos(schemni))
425 	{
426 		case TRAN_PIN:
427 			schempp = schemni->proto->firstportproto;
428 			break;
429 		case TRAN_CELL:
430 			schempp = getportproto(schemni->proto, mospp->protoname);
431 			break;
432 		default: /* transistor */
433 			for(port = 1, pp = ni->proto->firstportproto; pp != NOPORTPROTO;
434 				pp = pp->nextportproto, port++)
435 					if (pp == mospp) break;	 /* partic. port in MOS */
436 			schempp = us_trangetproto(schemni, port);
437 			break;
438 	}
439 	return(schempp);
440 }
441 
442 /*
443  * this routine figures out if the current nodeinst is a MOS component
444  * (a wire or transistor).  If it's a transistor, return corresponding
445  * define from efunction.h; if it's a passive connector, return TRAN_PIN;
446  * if it's a cell, return TRAN_CELL; else return NPUNKNOWN.
447  */
us_tranismos(NODEINST * ni)448 INTBIG us_tranismos(NODEINST *ni)
449 {
450 	INTBIG fun;
451 
452 	if (ni->proto->primindex == 0) return(TRAN_CELL);
453 	fun = (ni->proto->userbits & NFUNCTION) >> NFUNCTIONSH;
454 	switch(fun)
455 	{
456 		case NPTRANMOS:   case NPTRA4NMOS:
457 		case NPTRADMOS:   case NPTRA4DMOS:
458 		case NPTRAPMOS:   case NPTRA4PMOS:
459 		case NPTRANPN:    case NPTRA4NPN:
460 		case NPTRAPNP:    case NPTRA4PNP:
461 		case NPTRANJFET:  case NPTRA4NJFET:
462 		case NPTRAPJFET:  case NPTRA4PJFET:
463 		case NPTRADMES:   case NPTRA4DMES:
464 		case NPTRAEMES:   case NPTRA4EMES:
465 		case NPTRANSREF:
466 			return(fun);
467 		case NPPIN:
468 		case NPCONTACT:
469 		case NPNODE:
470 		case NPCONNECT:
471 		case NPSUBSTRATE:
472 		case NPWELL:
473 			return(TRAN_PIN);
474 	}
475 	return(NPUNKNOWN);
476 }
477 
us_trangetproto(NODEINST * ni,INTBIG port)478 PORTPROTO *us_trangetproto(NODEINST *ni, INTBIG port)
479 {
480 	PORTPROTO *pp;
481 	INTBIG count = port, i;
482 
483 	if (count == 4) count = 3; else
484 		if (count == 3) count = 1;
485 
486 	for(i = 1, pp = ni->proto->firstportproto; pp != NOPORTPROTO && i < count;
487 		pp = pp->nextportproto, i++) /* get portproto for schematic */
488 				;
489 	return(pp);
490 }
491 
492 /************************* CODE FOR CONVERSION TO LAYOUT *************************/
493 
494 /*
495  * routine to recursively descend from cell "oldcell" and find subcells that
496  * have to be converted.  When all subcells have been converted, convert this
497  * one into a new one called "newcellname".  The technology for the old cell
498  * is "oldtech" and the technology to use for the new cell is "newtech".  The
499  * old view type is "oldview" and the new view type is "nview".
500  */
us_tran_makelayoutcells(NODEPROTO * oldcell,CHAR * newcellname,TECHNOLOGY * oldtech,TECHNOLOGY * newtech,VIEW * nview)501 NODEPROTO *us_tran_makelayoutcells(NODEPROTO *oldcell, CHAR *newcellname,
502 	TECHNOLOGY *oldtech, TECHNOLOGY *newtech, VIEW *nview)
503 {
504 	REGISTER NODEPROTO *newcell, *rnp;
505 	REGISTER INTBIG bits;
506 	REGISTER NODEINST *ni;
507 	REGISTER ARCINST *ai;
508 	CHAR *str;
509 
510 	/* first convert the sub-cells */
511 	for(ni = oldcell->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
512 	{
513 		/* ignore primitives */
514 		if (ni->proto->primindex != 0) continue;
515 
516 		/* ignore recursive references (showing icon in contents) */
517 		if (isiconof(ni->proto, oldcell)) continue;
518 
519 		/* ignore cells with associations */
520 		FOR_CELLGROUP(rnp, ni->proto)
521 			if (rnp->cellview == nview) break;
522 		if (rnp != NONODEPROTO) continue;
523 
524 		/* make up a name for this cell */
525 		(void)allocstring(&str, ni->proto->protoname, el_tempcluster);
526 
527 		(void)us_tran_makelayoutcells(ni->proto, str, oldtech, newtech, nview);
528 		efree(str);
529 	}
530 
531 	/* create the cell and fill it with parts */
532 	newcell = us_tran_linkage(newcellname, nview, oldcell);
533 	if (newcell == NONODEPROTO) return(NONODEPROTO);
534 	if (us_tran_makelayoutparts(oldcell, newcell, oldtech, newtech, nview))
535 	{
536 		/* adjust for maximum Manhattan-ness */
537 		us_tran_makemanhattan(newcell);
538 
539 		/* reset shrinkage values and constraints to defaults (is this needed? !!!) */
540 		for(ai = newcell->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
541 		{
542 			bits = us_makearcuserbits(ai->proto);
543 			if ((bits&FIXED) != 0) ai->userbits |= FIXED;
544 			if ((bits&FIXANG) != 0) ai->userbits |= FIXANG;
545 			(void)setshrinkvalue(ai, FALSE);
546 		}
547 	}
548 
549 	return(newcell);
550 }
551 
552 /*
553  * routine to create a new cell in "newcell" from the contents of an old cell
554  * in "oldcell".  The technology for the old cell is "oldtech" and the
555  * technology to use for the new cell is "newtech".
556  */
us_tran_makelayoutparts(NODEPROTO * oldcell,NODEPROTO * newcell,TECHNOLOGY * oldtech,TECHNOLOGY * newtech,VIEW * nview)557 BOOLEAN us_tran_makelayoutparts(NODEPROTO *oldcell, NODEPROTO *newcell,
558 	TECHNOLOGY *oldtech, TECHNOLOGY *newtech, VIEW *nview)
559 {
560 	REGISTER NODEPROTO *newnp;
561 	REGISTER NODEINST *ni, *end1, *end2;
562 	ARCPROTO *ap, *newap;
563 	ARCINST *ai;
564 	REGISTER PORTPROTO *mospp1, *mospp2, *schempp1, *schempp2;
565 	INTBIG x1, y1, x2, y2, lx1, hx1, ly1, hy1, lx2, hx2, ly2, hy2, tx1, ty1, tx2, ty2;
566 	REGISTER INTBIG newwid, newbits, oldlambda, newlambda, defwid, curwid;
567 	REGISTER INTBIG badarcs, i, j;
568 	REGISTER BOOLEAN univarcs;
569 	static POLYGON *poly1 = NOPOLYGON, *poly2 = NOPOLYGON;
570 
571 	/* get a polygon */
572 	(void)needstaticpolygon(&poly1, 4, us_tool->cluster);
573 	(void)needstaticpolygon(&poly2, 4, us_tool->cluster);
574 
575 	/* get lambda values */
576 	oldlambda = el_curlib->lambda[oldtech->techindex];
577 	newlambda = el_curlib->lambda[newtech->techindex];
578 
579 	/* first convert the nodes */
580 	for(ni = oldcell->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
581 		ni->temp1 = 0;
582 	for(ni = oldcell->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
583 	{
584 		/* handle sub-cells */
585 		if (ni->proto->primindex == 0)
586 		{
587 			FOR_CELLGROUP(newnp, ni->proto)
588 				if (newnp->cellview == nview) break;
589 			if (newnp == NONODEPROTO)
590 			{
591 				ttyputerr(_("No equivalent cell for %s"), describenodeproto(ni->proto));
592 				continue;
593 			}
594 			us_tranplacenode(ni, newnp, newcell, oldtech, newtech);
595 			continue;
596 		}
597 
598 		/* handle primitives */
599 		if (ni->proto == gen_cellcenterprim) continue;
600 		newnp = us_figurenewnproto(ni, newtech);
601 		us_tranplacenode(ni, newnp, newcell, oldtech, newtech);
602 	}
603 
604 	/*
605 	 * for each arc in cell, find the ends in the new technology, and
606 	 * make a new arc to connect them in the new cell
607 	 */
608 	badarcs = 0;
609 	univarcs = FALSE;
610 	for(ai = oldcell->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
611 	{
612 		/* get the nodes and ports on the two ends of the arc */
613 		end1 = (NODEINST *)ai->end[0].nodeinst->temp1;
614 		end2 = (NODEINST *)ai->end[1].nodeinst->temp1;
615 		if (end1 == 0 || end2 == 0) continue;
616 		mospp1 = ai->end[0].portarcinst->proto;
617 		mospp2 = ai->end[1].portarcinst->proto;
618 		schempp1 = us_convport(ai->end[0].nodeinst, end1, mospp1);
619 		schempp2 = us_convport(ai->end[1].nodeinst, end2, mospp2);
620 
621 		/* set bits in arc prototypes that can make the connection */
622 		for(ap = newtech->firstarcproto; ap != NOARCPROTO; ap = ap->nextarcproto)
623 			ap->userbits &= ~CANCONNECT;
624 		for(i=0; schempp1->connects[i] != NOARCPROTO; i++)
625 		{
626 			for(j=0; schempp2->connects[j] != NOARCPROTO; j++)
627 			{
628 				if (schempp1->connects[i] != schempp2->connects[j]) continue;
629 				schempp1->connects[i]->userbits |= CANCONNECT;
630 				break;
631 			}
632 		}
633 
634 		/* compute arc type and see if it is acceptable */
635 		newap = us_figurenewaproto(ai->proto, newtech);
636 		if (newap->tech == newtech && (newap->userbits&CANCONNECT) == 0)
637 		{
638 			/* not acceptable: see if there are any valid ones */
639 			for(newap = newtech->firstarcproto; newap != NOARCPROTO; newap = newap->nextarcproto)
640 				if ((newap->userbits&CANCONNECT) != 0) break;
641 
642 			/* none are valid: use universal */
643 			if (newap == NOARCPROTO) newap = gen_universalarc;
644 		}
645 
646 		/* determine new arc width */
647 		newbits = ai->userbits;
648 		if (newap == gen_universalarc)
649 		{
650 			newwid = 0;
651 			univarcs = TRUE;
652 			newbits &= ~(FIXED | FIXANG);
653 		} else
654 		{
655 			defwid = ai->proto->nominalwidth - arcprotowidthoffset(ai->proto);
656 			curwid = ai->width - arcwidthoffset(ai);
657 			newwid = muldiv(newap->nominalwidth - arcprotowidthoffset(newap), curwid, defwid) +
658 				arcprotowidthoffset(newap);
659 			if (newwid <= 0) newwid = defaultarcwidth(newap);
660 		}
661 
662 		/* find the endpoints of the arc */
663 		x1 = muldiv(ai->end[0].xpos, newlambda, oldlambda);
664 		y1 = muldiv(ai->end[0].ypos, newlambda, oldlambda);
665 		shapeportpoly(end1, schempp1, poly1, FALSE);
666 		x2 = muldiv(ai->end[1].xpos, newlambda, oldlambda);
667 		y2 = muldiv(ai->end[1].ypos, newlambda, oldlambda);
668 		shapeportpoly(end2, schempp2, poly2, FALSE);
669 
670 		/* see if the new arc can connect without end adjustment */
671 		if (!isinside(x1, y1, poly1) || !isinside(x2, y2, poly2))
672 		{
673 			/* arc cannot be run exactly ... presume port centers */
674 			portposition(end1, schempp1, &x1, &y1);
675 			portposition(end2, schempp2, &x2, &y2);
676 			if ((newbits & FIXANG) != 0)
677 			{
678 				/* old arc was fixed-angle so look for a similar-angle path */
679 				reduceportpoly(poly1, end1, schempp1, newwid-arcprotowidthoffset(newap), -1);
680 				getbbox(poly1, &lx1, &hx1, &ly1, &hy1);
681 				reduceportpoly(poly2, end2, schempp2, newwid-arcprotowidthoffset(newap), -1);
682 				getbbox(poly2, &lx2, &hx2, &ly2, &hy2);
683 				if (!arcconnects(((ai->userbits&AANGLE) >> AANGLESH) * 10, lx1, hx1, ly1, hy1,
684 					lx2, hx2, ly2, hy2, &tx1, &ty1, &tx2, &ty2)) badarcs++; else
685 				{
686 					x1 = tx1;   y1 = ty1;
687 					x2 = tx2;   y2 = ty2;
688 				}
689 			}
690 		}
691 		/* create the new arc */
692 		if (newarcinst(newap, newwid, newbits, end1, schempp1, x1, y1,
693 			end2, schempp2, x2, y2, newcell) == NOARCINST)
694 		{
695 			ttyputmsg(_("Cell %s: can't run arc from node %s port %s at (%s,%s)"),
696 				describenodeproto(newcell), describenodeinst(end1),
697 					schempp1->protoname, latoa(x1, 0), latoa(y1, 0));
698 			ttyputmsg(_("   to node %s port %s at (%s,%s)"), describenodeinst(end2),
699 				schempp2->protoname, latoa(x2, 0), latoa(y2, 0));
700 		}
701 	}
702 
703 	/* print warning if arcs were made nonmanhattan */
704 	if (badarcs != 0)
705 		ttyputmsg(_("WARNING: %ld %s made not-fixed-angle in cell %s"), badarcs,
706 			makeplural(_("arc"), badarcs), describenodeproto(newcell));
707 	return(univarcs);
708 }
709 
us_tranplacenode(NODEINST * ni,NODEPROTO * newnp,NODEPROTO * newcell,TECHNOLOGY * oldtech,TECHNOLOGY * newtech)710 void us_tranplacenode(NODEINST *ni, NODEPROTO *newnp, NODEPROTO *newcell,
711 	TECHNOLOGY *oldtech, TECHNOLOGY *newtech)
712 {
713 	INTBIG lx, ly, hx, hy, nlx, nly, nhx, nhy, bx, by, length, width;
714 	REGISTER INTBIG i, len, newsx, newsy, x1, y1, newlx, newhx, newly, newhy, *newtrace,
715 		oldlambda, newlambda, thissizex, thissizey, defsizex, defsizey;
716 	XARRAY trans;
717 	REGISTER INTSML trn;
718 	REGISTER PORTEXPINST *pexp;
719 	REGISTER NODEINST *newni;
720 	REGISTER PORTPROTO *pp, *pp2;
721 	REGISTER VARIABLE *var;
722 
723 	oldlambda = el_curlib->lambda[oldtech->techindex];
724 	newlambda = el_curlib->lambda[newtech->techindex];
725 
726 	/* scale edge offsets if this is a primitive */
727 	trn = ni->transpose;
728 	if (ni->proto->primindex != 0)
729 	{
730 		/* get offsets for new node type */
731 		nodeprotosizeoffset(newnp, &nlx, &nly, &nhx, &nhy, NONODEPROTO);
732 
733 		/* special case for schematic transistors: get size from description */
734 		if (((ni->proto->userbits&NFUNCTION) >> NFUNCTIONSH) == NPTRANS)
735 		{
736 			transistorsize(ni, &length, &width);
737 			if (length < 0) length = newnp->highy - newnp->lowy - nly - nhy;
738 			if (width < 0) width = newnp->highx - newnp->lowx - nlx - nhx;
739 			lx = (ni->lowx + ni->highx - width) / 2;
740 			hx = (ni->lowx + ni->highx + width) / 2;
741 			ly = (ni->lowy + ni->highy - length) / 2;
742 			hy = (ni->lowy + ni->highy + length) / 2;
743 			trn = 1 - trn;
744 
745 			/* compute scaled size for new node */
746 			newsx = muldiv(hx - lx, newlambda, oldlambda);
747 			newsy = muldiv(hy - ly, newlambda, oldlambda);
748 		} else
749 		{
750 			/* determine this node's percentage of the default node's size */
751 			nodeprotosizeoffset(ni->proto, &lx, &ly, &hx, &hy, NONODEPROTO);
752 			defsizex = (ni->proto->highx - hx) - (ni->proto->lowx + lx);
753 			defsizey = (ni->proto->highy - hy) - (ni->proto->lowy + ly);
754 
755 			nodesizeoffset(ni, &lx, &ly, &hx, &hy);
756 			thissizex = (ni->highx - hx) - (ni->lowx + lx);
757 			thissizey = (ni->highy - hy) - (ni->lowy + ly);
758 
759 			/* compute size of new node that is the same percentage of its default */
760 			newsx = muldiv((newnp->highx - nhx) - (newnp->lowx + nlx), thissizex, defsizex);
761 			newsy = muldiv((newnp->highy - nhy) - (newnp->lowy + nly), thissizey, defsizey);
762 
763 			/* determine location of new node */
764 			lx = ni->lowx + lx;   hx = ni->highx - hx;
765 			ly = ni->lowy + ly;   hy = ni->highy - hy;
766 		}
767 
768 		/* compute center of old node */
769 		x1 = muldiv((hx + lx) / 2, newlambda, oldlambda);
770 		y1 = muldiv((hy + ly) / 2, newlambda, oldlambda);
771 
772 		/* compute bounds of the new node */
773 		newlx = x1 - newsx/2 - nlx;   newhx = newlx + newsx + nlx + nhx;
774 		newly = y1 - newsy/2 - nly;   newhy = newly + newsy + nly + nhy;
775 	} else
776 	{
777 		x1 = (newnp->highx+newnp->lowx)/2 - (ni->proto->highx+ni->proto->lowx)/2;
778 		y1 = (newnp->highy+newnp->lowy)/2 - (ni->proto->highy+ni->proto->lowy)/2;
779 		makeangle(ni->rotation, ni->transpose, trans);
780 		xform(x1, y1, &bx, &by, trans);
781 		newlx = ni->lowx + bx;   newhx = ni->highx + bx;
782 		newly = ni->lowy + by;   newhy = ni->highy + by;
783 		newlx += ((newhx-newlx) - (newnp->highx-newnp->lowx)) / 2;
784 		newhx = newlx + newnp->highx - newnp->lowx;
785 		newly += ((newhy-newly) - (newnp->highy-newnp->lowy)) / 2;
786 		newhy = newly + newnp->highy - newnp->lowy;
787 	}
788 
789 	/* create the node */
790 	newni = newnodeinst(newnp, newlx, newhx, newly, newhy, trn, ni->rotation, newcell);
791 	if (newni == NONODEINST) return;
792 	newni->userbits |= (ni->userbits & (NEXPAND | WIPED | NSHORT));
793 	ni->temp1 = (INTBIG)newni;
794 	(void)copyvars((INTBIG)ni, VNODEINST, (INTBIG)newni, VNODEINST, FALSE);
795 
796 	/* copy "trace" information if there is any */
797 	var = gettrace(ni);
798 	if (var != NOVARIABLE)
799 	{
800 		len = getlength(var);
801 		newtrace = emalloc((len * SIZEOFINTBIG), el_tempcluster);
802 		if (newtrace == 0) return;
803 		for(i=0; i<len; i++)
804 			newtrace[i] = muldiv(((INTBIG *)var->addr)[i], newlambda, oldlambda);
805 		(void)setvalkey((INTBIG)newni, VNODEINST, el_trace_key, (INTBIG)newtrace,
806 			VINTEGER|VISARRAY|(len<<VLENGTHSH));
807 		efree((CHAR *)newtrace);
808 	}
809 	endobjectchange((INTBIG)newni, VNODEINST);
810 
811 	/* re-export any ports on the node */
812 	for(pexp = ni->firstportexpinst; pexp != NOPORTEXPINST; pexp = pexp->nextportexpinst)
813 	{
814 		pp = us_convport(ni, newni, pexp->proto);
815 		pp2 = newportproto(newcell, newni, pp, pexp->exportproto->protoname);
816 		if (pp2 == NOPORTPROTO) return;
817 		pp2->userbits = (pp2->userbits & ~STATEBITS) | (pexp->exportproto->userbits & STATEBITS);
818 		TDCOPY(pp2->textdescript, pexp->exportproto->textdescript);
819 		if (copyvars((INTBIG)pexp->exportproto, VPORTPROTO, (INTBIG)pp2, VPORTPROTO, FALSE))
820 			return;
821 	}
822 }
823 
824 /*
825  * routine to determine the port to use on node "newni" assuming that it should
826  * be the same as port "oldpp" on equivalent node "ni"
827  */
us_convport(NODEINST * ni,NODEINST * newni,PORTPROTO * oldpp)828 PORTPROTO *us_convport(NODEINST *ni, NODEINST *newni, PORTPROTO *oldpp)
829 {
830 	REGISTER PORTPROTO *pp, *npp;
831 	REGISTER INTBIG oldfun, newfun;
832 
833 	if (newni->proto->primindex == 0)
834 	{
835 		/* cells can associate by comparing names */
836 		pp = getportproto(newni->proto, oldpp->protoname);
837 		if (pp != NOPORTPROTO) return(pp);
838 	}
839 
840 	/* if functions are different, handle some special cases */
841 	oldfun = (ni->proto->userbits&NFUNCTION) >> NFUNCTIONSH;
842 	newfun = (newni->proto->userbits&NFUNCTION) >> NFUNCTIONSH;
843 	if (oldfun != newfun)
844 	{
845 		if (oldfun == NPTRANS && isfet(newni->geom))
846 		{
847 			/* converting from stick-figure to layout */
848 			pp = ni->proto->firstportproto;   npp = newni->proto->firstportproto;
849 			if (pp == oldpp) return(npp);
850 			pp = pp->nextportproto;           npp = npp->nextportproto;
851 			if (pp == oldpp) return(npp);
852 			pp = pp->nextportproto;           npp = npp->nextportproto->nextportproto;
853 			if (pp == oldpp) return(npp);
854 		}
855 	}
856 
857 	/* associate by position in port list */
858 	for(pp = ni->proto->firstportproto, npp = newni->proto->firstportproto;
859 		pp != NOPORTPROTO && npp != NOPORTPROTO;
860 			pp = pp->nextportproto, npp = npp->nextportproto)
861 				if (pp == oldpp) return(npp);
862 
863 	/* special case again: one-port capacitors are OK */
864 	if (oldfun == NPCAPAC && newfun == NPCAPAC) return(newni->proto->firstportproto);
865 
866 	/* association has failed: assume the first port */
867 	ttyputmsg(_("No port association between %s, port %s and %s"),
868 		describenodeproto(ni->proto), oldpp->protoname,
869 			describenodeproto(newni->proto));
870 	return(newni->proto->firstportproto);
871 }
872 
873 /*
874  * routine to determine the equivalent prototype in technology "newtech" for
875  * node prototype "oldnp".
876  */
us_figurenewaproto(ARCPROTO * oldap,TECHNOLOGY * newtech)877 ARCPROTO *us_figurenewaproto(ARCPROTO *oldap, TECHNOLOGY *newtech)
878 {
879 	REGISTER INTBIG type;
880 	REGISTER ARCPROTO *ap;
881 
882 	/* schematic wires become universal arcs */
883 	if (oldap == sch_wirearc) return(gen_universalarc);
884 
885 	/* determine the proper association of this node */
886 	type = (oldap->userbits & AFUNCTION) >> AFUNCTIONSH;
887 	for(ap = newtech->firstarcproto; ap != NOARCPROTO; ap = ap->nextarcproto)
888 		if ((INTBIG)((ap->userbits&AFUNCTION) >> AFUNCTIONSH) == type) break;
889 	if (ap == NOARCPROTO)
890 	{
891 		ttyputmsg(_("No equivalent arc for %s"), describearcproto(oldap));
892 		return(oldap);
893 	}
894 	return(ap);
895 }
896 
897 /*
898  * routine to determine the equivalent prototype in technology "newtech" for
899  * node prototype "oldnp".
900  */
us_figurenewnproto(NODEINST * oldni,TECHNOLOGY * newtech)901 NODEPROTO *us_figurenewnproto(NODEINST *oldni, TECHNOLOGY *newtech)
902 {
903 	REGISTER INTBIG type, i, j, k;
904 	REGISTER ARCPROTO *ap, *oap;
905 	REGISTER INTBIG important, funct;
906 	REGISTER NODEPROTO *np, *rnp, *oldnp;
907 	REGISTER NODEINST *ni;
908 	NODEINST node;
909 	static POLYGON *poly = NOPOLYGON;
910 
911 	/* get a polygon */
912 	(void)needstaticpolygon(&poly, 4, us_tool->cluster);
913 
914 	/* easy translation if complex or already in the proper technology */
915 	oldnp = oldni->proto;
916 	if (oldnp->primindex == 0 || oldnp->tech == newtech) return(oldnp);
917 
918 	/* if this is a layer node, check the layer functions */
919 	type = nodefunction(oldni);
920 	if (type == NPNODE)
921 	{
922 		/* get the polygon describing the first box of the old node */
923 		(void)nodepolys(oldni, 0, NOWINDOWPART);
924 		shapenodepoly(oldni, 0, poly);
925 		important = LFTYPE | LFPSEUDO | LFNONELEC;
926 		funct = layerfunction(oldnp->tech, poly->layer) & important;
927 
928 		/* now search for that function in the other technology */
929 		for(np = newtech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
930 		{
931 			if (((np->userbits&NFUNCTION) >> NFUNCTIONSH) != NPNODE) continue;
932 			ni = &node;   initdummynode(ni);
933 			ni->proto = np;
934 			(void)nodepolys(ni, 0, NOWINDOWPART);
935 			shapenodepoly(ni, 0, poly);
936 			if ((layerfunction(newtech, poly->layer)&important) == funct)
937 				return(np);
938 		}
939 	}
940 
941 	/* see if one node in the new technology has the same function */
942 	for(i = 0, np = newtech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
943 		if ((INTBIG)((np->userbits&NFUNCTION) >> NFUNCTIONSH) == type)
944 	{
945 		rnp = np;   i++;
946 	}
947 	if (i == 1) return(rnp);
948 
949 	/* if there are too many matches, determine which is proper from arcs */
950 	if (i > 1)
951 	{
952 		for(np = newtech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
953 		{
954 			if ((INTBIG)((np->userbits&NFUNCTION) >> NFUNCTIONSH) != type) continue;
955 
956 			/* see if this node has equivalent arcs */
957 			for(j=0; oldnp->firstportproto->connects[j] != NOARCPROTO; j++)
958 			{
959 				oap = oldnp->firstportproto->connects[j];
960 				if (oap->tech == gen_tech) continue;
961 
962 				for(k=0; np->firstportproto->connects[k] != NOARCPROTO; k++)
963 				{
964 					ap = np->firstportproto->connects[k];
965 					if (ap->tech == gen_tech) continue;
966 					if ((ap->userbits&AFUNCTION) == (oap->userbits&AFUNCTION)) break;
967 				}
968 				if (np->firstportproto->connects[k] == NOARCPROTO) break;
969 			}
970 			if (oldnp->firstportproto->connects[j] == NOARCPROTO) break;
971 		}
972 		if (np != NONODEPROTO)
973 		{
974 			rnp = np;
975 			i = 1;
976 		}
977 	}
978 
979 	/* give up if it still cannot be determined */
980 	if (i != 1)
981 	{
982 		if (oldnp->temp1 == 0)
983 			ttyputmsg(_("Node %s (function %s) has no equivalent in the %s technology"),
984 				describenodeproto(oldnp), nodefunctionname(type, oldni),
985 					newtech->techname);
986 		oldnp->temp1 = 1;
987 		return(oldnp);
988 	}
989 	return(rnp);
990 }
991 
992 /************************* CODE FOR PRINTING TECHNOLOGIES *************************/
993 
994 extern LIST us_teclayer_functions[];
995 extern LIST us_tecarc_functions[];
996 
997 #define MAXCOLS 10
998 
us_printtechnology(TECHNOLOGY * tech)999 void us_printtechnology(TECHNOLOGY *tech)
1000 {
1001 	FILE *f;
1002 	CHAR *name, *fieldname, *colorsymbol, thefield[50], *truename, **fields[MAXCOLS];
1003 	REGISTER CHAR **names, **colors, **styles, **cifs, **gdss,
1004 		**funcs, **layers, **layersizes, **extensions, **angles, **wipes, **ports,
1005 		**portsizes, **portangles, **connections;
1006 	REGISTER INTBIG i, j, k, l, m, tot, base;
1007 	REGISTER INTBIG saveunit;
1008 	REGISTER INTBIG func, area, bits, lambda;
1009 	INTBIG lx, hx, ly, hy;
1010 	REGISTER ARCINST *ai;
1011 	REGISTER ARCPROTO *ap;
1012 	REGISTER NODEINST *ni;
1013 	REGISTER NODEPROTO *np;
1014 	REGISTER PORTPROTO *pp;
1015 	REGISTER VARIABLE *cifvar, *gdsvar, *funcvar, *var;
1016 	GRAPHICS *gra;
1017 	REGISTER TECH_ARCLAY *arclay;
1018 	REGISTER TECH_NODES *nodestr;
1019 	TECH_POLYGON *lay;
1020 	NODEINST node;
1021 	ARCINST arc;
1022 	static POLYGON *poly = NOPOLYGON;
1023 	REGISTER void *infstr;
1024 
1025 	/* get polygon */
1026 	(void)needstaticpolygon(&poly, 4, us_tool->cluster);
1027 
1028 	infstr = initinfstr();
1029 	addstringtoinfstr(infstr, tech->techname);
1030 	addstringtoinfstr(infstr, x_(".doc"));
1031 	name = returninfstr(infstr);
1032 	f = xcreate(name, el_filetypetext, _("Technology Documentation File"), &truename);
1033 	if (f == NULL)
1034 	{
1035 		if (truename != 0) us_abortcommand(_("Cannot write %s"), truename);
1036 		return;
1037 	}
1038 	ttyputverbose(M_("Writing: %s"), name);
1039 
1040 	/****************************** dump layers ******************************/
1041 
1042 	/* get layer variables */
1043 	cifvar = getval((INTBIG)tech, VTECHNOLOGY, VSTRING|VISARRAY, x_("IO_cif_layer_names"));
1044 	gdsvar = getval((INTBIG)tech, VTECHNOLOGY, VSTRING|VISARRAY, x_("IO_gds_layer_numbers"));
1045 	funcvar = getval((INTBIG)tech, VTECHNOLOGY, VINTEGER|VISARRAY, x_("TECH_layer_function"));
1046 
1047 	/* allocate space for all layer fields */
1048 	names = (CHAR **)emalloc((tech->layercount+1) * (sizeof (CHAR *)), el_tempcluster);
1049 	colors = (CHAR **)emalloc((tech->layercount+1) * (sizeof (CHAR *)), el_tempcluster);
1050 	styles = (CHAR **)emalloc((tech->layercount+1) * (sizeof (CHAR *)), el_tempcluster);
1051 	cifs = (CHAR **)emalloc((tech->layercount+1) * (sizeof (CHAR *)), el_tempcluster);
1052 	gdss = (CHAR **)emalloc((tech->layercount+1) * (sizeof (CHAR *)), el_tempcluster);
1053 	funcs = (CHAR **)emalloc((tech->layercount+1) * (sizeof (CHAR *)), el_tempcluster);
1054 	if (names == 0 || colors == 0 || styles == 0 || cifs == 0 || gdss == 0 || funcs == 0)
1055 		return;
1056 
1057 	/* load the header */
1058 	(void)allocstring(&names[0], x_("Layer"), el_tempcluster);
1059 	(void)allocstring(&colors[0], x_("Color"), el_tempcluster);
1060 	(void)allocstring(&styles[0], x_("Style"), el_tempcluster);
1061 	(void)allocstring(&cifs[0], x_("CIF"), el_tempcluster);
1062 	(void)allocstring(&gdss[0], x_("GDS"), el_tempcluster);
1063 	(void)allocstring(&funcs[0], x_("Function"), el_tempcluster);
1064 
1065 	/* compute each layer */
1066 	for(i=0; i<tech->layercount; i++)
1067 	{
1068 		(void)allocstring(&names[i+1], layername(tech, i), el_tempcluster);
1069 
1070 		gra = tech->layers[i];
1071 		if (ecolorname(gra->col, &fieldname, &colorsymbol)) fieldname = x_("?");
1072 		(void)allocstring(&colors[i+1], fieldname, el_tempcluster);
1073 
1074 		if (el_curwindowpart == NOWINDOWPART) fieldname = x_("unkwn."); else
1075 		{
1076 			switch (gra->colstyle&(NATURE|INVISIBLE))
1077 			{
1078 				case SOLIDC:              fieldname = x_("solid");    break;
1079 				case PATTERNED:           fieldname = x_("pat.");     break;
1080 				case INVISIBLE|SOLIDC:    fieldname = x_("INVsol");   break;
1081 				case INVISIBLE|PATTERNED: fieldname = x_("INVpat");   break;
1082 			}
1083 		}
1084 		(void)allocstring(&styles[i+1], fieldname, el_tempcluster);
1085 
1086 		if (cifvar == NOVARIABLE) fieldname = x_("---"); else
1087 			fieldname = ((CHAR **)cifvar->addr)[i];
1088 		(void)allocstring(&cifs[i+1], fieldname, el_tempcluster);
1089 
1090 		if (gdsvar == NOVARIABLE) fieldname = x_("---"); else
1091 			fieldname = ((CHAR **)gdsvar->addr)[i];
1092 		(void)allocstring(&gdss[i+1], fieldname, el_tempcluster);
1093 
1094 		if (funcvar == NOVARIABLE) fieldname = x_("---"); else
1095 		{
1096 			func = ((INTBIG *)funcvar->addr)[i];
1097 			infstr = initinfstr();
1098 			us_tecedaddfunstring(infstr, func);
1099 			fieldname = returninfstr(infstr);
1100 		}
1101 		(void)allocstring(&funcs[i+1], fieldname, el_tempcluster);
1102 	}
1103 
1104 	/* write the layer information */
1105 	fields[0] = names;   fields[1] = colors;   fields[2] = styles;
1106 	fields[3] = cifs;    fields[4] = gdss;     fields[5] = funcs;
1107 	us_dumpfields(fields, 6, tech->layercount+1, f, x_("LAYERS"));
1108 	for(i=0; i<=tech->layercount; i++)
1109 	{
1110 		efree(names[i]);
1111 		efree(colors[i]);
1112 		efree(styles[i]);
1113 		efree(cifs[i]);
1114 		efree(gdss[i]);
1115 		efree(funcs[i]);
1116 	}
1117 	efree((CHAR *)names);
1118 	efree((CHAR *)colors);
1119 	efree((CHAR *)styles);
1120 	efree((CHAR *)cifs);
1121 	efree((CHAR *)gdss);
1122 	efree((CHAR *)funcs);
1123 
1124 	/****************************** dump arcs ******************************/
1125 
1126 	/* allocate space for all arc fields */
1127 	ai = &arc;   initdummyarc(ai);
1128 	ai->end[0].xpos = -2000;   ai->end[0].ypos = 0;
1129 	ai->end[1].xpos = 2000;    ai->end[1].ypos = 0;
1130 	ai->length = 4000;
1131 	ai->userbits |= NOEXTEND;
1132 	tot = 1;
1133 	for(ap = tech->firstarcproto; ap != NOARCPROTO; ap = ap->nextarcproto)
1134 	{
1135 		ai->proto = ap;
1136 		if (tech->arcpolys != 0) j = (*(tech->arcpolys))(ai, NOWINDOWPART); else
1137 			j = tech->arcprotos[ap->arcindex]->laycount;
1138 		tot += j;
1139 	}
1140 	names = (CHAR **)emalloc(tot * (sizeof (CHAR *)), el_tempcluster);
1141 	layers = (CHAR **)emalloc(tot * (sizeof (CHAR *)), el_tempcluster);
1142 	layersizes = (CHAR **)emalloc(tot * (sizeof (CHAR *)), el_tempcluster);
1143 	extensions = (CHAR **)emalloc(tot * (sizeof (CHAR *)), el_tempcluster);
1144 	angles = (CHAR **)emalloc(tot * (sizeof (CHAR *)), el_tempcluster);
1145 	wipes = (CHAR **)emalloc(tot * (sizeof (CHAR *)), el_tempcluster);
1146 	funcs = (CHAR **)emalloc(tot * (sizeof (CHAR *)), el_tempcluster);
1147 	if (names == 0 || layers == 0 || layersizes == 0 || extensions == 0 || angles == 0 ||
1148 		wipes == 0 || funcs == 0) return;
1149 
1150 	/* load the header */
1151 	(void)allocstring(&names[0], x_("Arc"), el_tempcluster);
1152 	(void)allocstring(&layers[0], x_("Layer"), el_tempcluster);
1153 	(void)allocstring(&layersizes[0], x_("Size"), el_tempcluster);
1154 	(void)allocstring(&extensions[0], x_("Extend"), el_tempcluster);
1155 	(void)allocstring(&angles[0], x_("Angle"), el_tempcluster);
1156 	(void)allocstring(&wipes[0], x_("Wipes"), el_tempcluster);
1157 	(void)allocstring(&funcs[0], x_("Function"), el_tempcluster);
1158 
1159 	tot = 1;
1160 	for(ap = tech->firstarcproto; ap != NOARCPROTO; ap = ap->nextarcproto)
1161 	{
1162 		(void)allocstring(&names[tot], ap->protoname, el_tempcluster);
1163 
1164 		var = getvalkey((INTBIG)ap, VARCPROTO, VINTEGER, us_arcstylekey);
1165 		if (var != NOVARIABLE) bits = var->addr; else
1166 			bits = ap->userbits;
1167 		if ((bits&WANTNOEXTEND) == 0) fieldname = x_("yes"); else fieldname = x_("no");
1168 		(void)allocstring(&extensions[tot], fieldname, el_tempcluster);
1169 		(void)esnprintf(thefield, 50, x_("%ld"), (ap->userbits&AANGLEINC) >> AANGLEINCSH);
1170 		(void)allocstring(&angles[tot], thefield, el_tempcluster);
1171 		if ((ap->userbits&CANWIPE) == 0) fieldname = x_("no"); else fieldname = x_("yes");
1172 		(void)allocstring(&wipes[tot], fieldname, el_tempcluster);
1173 		func = (ap->userbits&AFUNCTION) >> AFUNCTIONSH;
1174 		(void)allocstring(&funcs[tot], us_tecarc_functions[func].name, el_tempcluster);
1175 
1176 		ai->proto = ap;
1177 		if (tech->arcpolys != 0) j = (*(tech->arcpolys))(ai, NOWINDOWPART); else
1178 			tech_oneprocpolyloop.realpolys = j = tech->arcprotos[ap->arcindex]->laycount;
1179 		for(k=0; k<j; k++)
1180 		{
1181 			ai->width = defaultarcwidth(ap);
1182 			if (tech->shapearcpoly != 0) (*(tech->shapearcpoly))(ai, k, poly); else
1183 			{
1184 				arclay = &tech->arcprotos[ap->arcindex]->list[k];
1185 				makearcpoly(ai->length, defaultarcwidth(ap)-arclay->off*lambdaofarc(ai)/WHOLE,
1186 					ai, poly, arclay->style);
1187 				poly->layer = arclay->lay;
1188 			}
1189 			(void)allocstring(&layers[tot], layername(tech, poly->layer), el_tempcluster);
1190 			area = (INTBIG)(fabs(areapoly(poly)) / 4000.0);
1191 			saveunit = el_units & DISPLAYUNITS;
1192 			el_units = (el_units & ~DISPLAYUNITS) | DISPUNITMIC;
1193 			(void)allocstring(&layersizes[tot], latoa(area, 0), el_tempcluster);
1194 			el_units = (el_units & ~DISPLAYUNITS) | saveunit;
1195 			if (k > 0)
1196 			{
1197 				(void)allocstring(&names[tot], x_(""), el_tempcluster);
1198 				(void)allocstring(&extensions[tot], x_(""), el_tempcluster);
1199 				(void)allocstring(&angles[tot], x_(""), el_tempcluster);
1200 				(void)allocstring(&wipes[tot], x_(""), el_tempcluster);
1201 				(void)allocstring(&funcs[tot], x_(""), el_tempcluster);
1202 			}
1203 			tot++;
1204 		}
1205 	}
1206 
1207 	/* write the arc information */
1208 	fields[0] = names;        fields[1] = layers;   fields[2] = layersizes;
1209 	fields[3] = extensions;   fields[4] = angles;   fields[5] = wipes;
1210 	fields[6] = funcs;
1211 	us_dumpfields(fields, 7, tot, f, x_("ARCS"));
1212 	for(i=0; i<tot; i++)
1213 	{
1214 		efree(names[i]);
1215 		efree(layers[i]);
1216 		efree(layersizes[i]);
1217 		efree(extensions[i]);
1218 		efree(angles[i]);
1219 		efree(wipes[i]);
1220 		efree(funcs[i]);
1221 	}
1222 	efree((CHAR *)names);
1223 	efree((CHAR *)layers);
1224 	efree((CHAR *)layersizes);
1225 	efree((CHAR *)extensions);
1226 	efree((CHAR *)angles);
1227 	efree((CHAR *)wipes);
1228 	efree((CHAR *)funcs);
1229 
1230 	/****************************** dump nodes ******************************/
1231 
1232 	/* allocate space for all node fields */
1233 	ni = &node;   initdummynode(ni);
1234 	tot = 1;
1235 	for(np = tech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
1236 	{
1237 		ni->proto = np;
1238 		if (tech->nodepolys != 0) j = (*(tech->nodepolys))(ni, 0, NOWINDOWPART); else
1239 			j = tech->nodeprotos[np->primindex-1]->layercount;
1240 		l = 0;
1241 		for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
1242 		{
1243 			m = 0;
1244 			for(k=0; pp->connects[k] != NOARCPROTO; k++)
1245 				if (pp->connects[k]->tech == tech) m++;
1246 			if (m == 0) m = 1;
1247 			l += m;
1248 		}
1249 		tot += maxi(j, l);
1250 	}
1251 	names = (CHAR **)emalloc(tot * (sizeof (CHAR *)), el_tempcluster);
1252 	funcs = (CHAR **)emalloc(tot * (sizeof (CHAR *)), el_tempcluster);
1253 	layers = (CHAR **)emalloc(tot * (sizeof (CHAR *)), el_tempcluster);
1254 	layersizes = (CHAR **)emalloc(tot * (sizeof (CHAR *)), el_tempcluster);
1255 	ports = (CHAR **)emalloc(tot * (sizeof (CHAR *)), el_tempcluster);
1256 	portsizes = (CHAR **)emalloc(tot * (sizeof (CHAR *)), el_tempcluster);
1257 	portangles = (CHAR **)emalloc(tot * (sizeof (CHAR *)), el_tempcluster);
1258 	connections = (CHAR **)emalloc(tot * (sizeof (CHAR *)), el_tempcluster);
1259 	if (names == 0 || funcs == 0 || layers == 0 || layersizes == 0 || ports == 0 ||
1260 		portsizes == 0 || portangles == 0 || connections == 0) return;
1261 
1262 	/* load the header */
1263 	(void)allocstring(&names[0], x_("Node"), el_tempcluster);
1264 	(void)allocstring(&funcs[0], x_("Function"), el_tempcluster);
1265 	(void)allocstring(&layers[0], x_("Layers"), el_tempcluster);
1266 	(void)allocstring(&layersizes[0], x_("Size"), el_tempcluster);
1267 	(void)allocstring(&ports[0], x_("Ports"), el_tempcluster);
1268 	(void)allocstring(&portsizes[0], x_("Size"), el_tempcluster);
1269 	(void)allocstring(&portangles[0], x_("Angle"), el_tempcluster);
1270 	(void)allocstring(&connections[0], x_("Connections"), el_tempcluster);
1271 
1272 	tot = 1;
1273 	for(np = tech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
1274 	{
1275 		base = tot;
1276 		(void)allocstring(&names[tot], np->protoname, el_tempcluster);
1277 
1278 		func = (np->userbits&NFUNCTION) >> NFUNCTIONSH;
1279 		(void)allocstring(&funcs[tot], nodefunctionname(func, NONODEINST), el_tempcluster);
1280 
1281 		ni->proto = np;
1282 		ni->lowx = np->lowx;   ni->highx = np->highx;
1283 		ni->lowy = np->lowy;   ni->highy = np->highy;
1284 		if (tech->nodepolys != 0) j = (*(tech->nodepolys))(ni, 0, NOWINDOWPART); else
1285 			tech_oneprocpolyloop.realpolys = j = tech->nodeprotos[np->primindex-1]->layercount;
1286 		for(k=0; k<j; k++)
1287 		{
1288 			if (tech->shapenodepoly != 0) (*(tech->shapenodepoly))(ni, k, poly); else
1289 			{
1290 				nodestr = tech->nodeprotos[np->primindex-1];
1291 				lambda = el_curlib->lambda[tech->techindex];
1292 				if (nodestr->special == SERPTRANS)
1293 				{
1294 					tech_filltrans(poly, &lay, nodestr->gra, ni,
1295 						lambda, k, (TECH_PORTS *)0, &tech_oneprocpolyloop);
1296 				} else
1297 				{
1298 					lay = &nodestr->layerlist[k];
1299 					tech_fillpoly(poly, lay, ni, lambda, FILLED);
1300 				}
1301 			}
1302 			(void)allocstring(&layers[tot], layername(tech, poly->layer), el_tempcluster);
1303 			getbbox(poly, &lx, &hx, &ly, &hy);
1304 			saveunit = el_units & DISPLAYUNITS;
1305 			el_units = (el_units & ~DISPLAYUNITS) | DISPUNITMIC;
1306 			(void)esnprintf(thefield, 50, x_("%s x %s"), latoa(hx-lx, 0), latoa(hy-ly, 0));
1307 			(void)allocstring(&layersizes[tot], thefield, el_tempcluster);
1308 			el_units = (el_units & ~DISPLAYUNITS) | saveunit;
1309 			if (k > 0)
1310 			{
1311 				(void)allocstring(&names[tot], x_(""), el_tempcluster);
1312 				(void)allocstring(&funcs[tot], x_(""), el_tempcluster);
1313 			}
1314 			tot++;
1315 		}
1316 		for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
1317 		{
1318 			(void)allocstring(&ports[base], pp->protoname, el_tempcluster);
1319 			shapeportpoly(ni, pp, poly, FALSE);
1320 			getbbox(poly, &lx, &hx, &ly, &hy);
1321 			saveunit = el_curtech->userbits & DISPLAYUNITS;
1322 			el_units = (el_units & ~DISPLAYUNITS) | DISPUNITMIC;
1323 			(void)esnprintf(thefield, 50, x_("%s x %s"), latoa(hx-lx, 0), latoa(hy-ly, 0));
1324 			(void)allocstring(&portsizes[base], thefield, el_tempcluster);
1325 			el_units = (el_units & ~DISPLAYUNITS) | saveunit;
1326 			if (((pp->userbits&PORTARANGE) >> PORTARANGESH) == 180) (void)estrcpy(thefield, x_("")); else
1327 				(void)esnprintf(thefield, 50, x_("%ld"), (pp->userbits&PORTANGLE) >> PORTANGLESH);
1328 			(void)allocstring(&portangles[base], thefield, el_tempcluster);
1329 			m = 0;
1330 			for(k=0; pp->connects[k] != NOARCPROTO; k++)
1331 			{
1332 				if (pp->connects[k]->tech != tech) continue;
1333 				(void)allocstring(&connections[base], pp->connects[k]->protoname, el_tempcluster);
1334 				if (m != 0)
1335 				{
1336 					(void)allocstring(&ports[base], x_(""), el_tempcluster);
1337 					(void)allocstring(&portsizes[base], x_(""), el_tempcluster);
1338 					(void)allocstring(&portangles[base], x_(""), el_tempcluster);
1339 				}
1340 				m++;
1341 				base++;
1342 			}
1343 			if (m == 0) (void)allocstring(&connections[base++], x_("<NONE>"), el_tempcluster);
1344 		}
1345 		for( ; base < tot; base++)
1346 		{
1347 			(void)allocstring(&ports[base], x_(""), el_tempcluster);
1348 			(void)allocstring(&portsizes[base], x_(""), el_tempcluster);
1349 			(void)allocstring(&portangles[base], x_(""), el_tempcluster);
1350 			(void)allocstring(&connections[base], x_(""), el_tempcluster);
1351 		}
1352 		for( ; tot < base; tot++)
1353 		{
1354 			(void)allocstring(&names[tot], x_(""), el_tempcluster);
1355 			(void)allocstring(&funcs[tot], x_(""), el_tempcluster);
1356 			(void)allocstring(&layers[tot], x_(""), el_tempcluster);
1357 			(void)allocstring(&layersizes[tot], x_(""), el_tempcluster);
1358 		}
1359 	}
1360 
1361 	/* write the node information */
1362 	fields[0] = names;        fields[1] = funcs;    fields[2] = layers;
1363 	fields[3] = layersizes;   fields[4] = ports;    fields[5] = portsizes;
1364 	fields[6] = portangles;   fields[7] = connections;
1365 	us_dumpfields(fields, 8, tot, f, x_("NODES"));
1366 	for(i=0; i<tot; i++)
1367 	{
1368 		efree(names[i]);
1369 		efree(funcs[i]);
1370 		efree(layers[i]);
1371 		efree(layersizes[i]);
1372 		efree(ports[i]);
1373 		efree(portsizes[i]);
1374 		efree(portangles[i]);
1375 		efree(connections[i]);
1376 	}
1377 	efree((CHAR *)names);
1378 	efree((CHAR *)funcs);
1379 	efree((CHAR *)layers);
1380 	efree((CHAR *)layersizes);
1381 	efree((CHAR *)ports);
1382 	efree((CHAR *)portsizes);
1383 	efree((CHAR *)portangles);
1384 	efree((CHAR *)connections);
1385 
1386 	xclose(f);
1387 }
1388 
us_dumpfields(CHAR *** fields,INTBIG count,INTBIG length,FILE * f,CHAR * title)1389 void us_dumpfields(CHAR ***fields, INTBIG count, INTBIG length, FILE *f, CHAR *title)
1390 {
1391 	INTBIG widths[MAXCOLS];
1392 	REGISTER INTBIG len, i, j, k, totwid, stars;
1393 
1394 	totwid = 0;
1395 	for(i=0; i<count; i++)
1396 	{
1397 		widths[i] = 8;
1398 		for(j=0; j<length; j++)
1399 		{
1400 			len = estrlen(fields[i][j]);
1401 			if (len > widths[i]) widths[i] = len;
1402 		}
1403 		widths[i]++;
1404 		totwid += widths[i];
1405 	}
1406 
1407 	stars = (totwid - estrlen(title) - 2) / 2;
1408 	for(i=0; i<stars; i++) xprintf(f, x_("*"));
1409 	xprintf(f, x_(" %s "), title);
1410 	for(i=0; i<stars; i++) xprintf(f, x_("*"));
1411 	xprintf(f, x_("\n"));
1412 
1413 	for(j=0; j<length; j++)
1414 	{
1415 		for(i=0; i<count; i++)
1416 		{
1417 			xprintf(f, x_("%s"), fields[i][j]);
1418 			if (i == count-1) continue;
1419 			for(k=estrlen(fields[i][j]); k<widths[i]; k++) xprintf(f, x_(" "));
1420 		}
1421 		xprintf(f, x_("\n"));
1422 	}
1423 	xprintf(f, x_("\n"));
1424 }
1425