1 /* -*- tab-width: 4 -*-
2  *
3  * Electric(tm) VLSI Design System
4  *
5  * File: iolout.c
6  * Input/output tool: L output
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 "config.h"
33 #include "global.h"
34 #include "eio.h"
35 #include "efunction.h"
36 
37 /* node types returned by "io_l_nodetype" */
38 #define TRUEPIN    1
39 #define TRANSISTOR 2
40 #define INSTANCE   3
41 #define OTHERNODE  4
42 
43 /* table that associates arc functionality with port types */
44 static struct
45 {
46 	INTBIG arcfunct;
47 	CHAR *portname;
48 } io_lports[] =
49 {
50 	{APMETAL1, x_("MET1")},
51 	{APMETAL2, x_("MET2")},
52 	{APPOLY1,  x_("POLY")},
53 	{APDIFFP,  x_("PDIFF")},
54 	{APDIFFN,  x_("NDIFF")},
55 	{APDIFFS,  x_("NWELL")},
56 	{APDIFFW,  x_("PWELL")},
57 	{0, NULL}  /* 0 */
58 };
59 
60 /* table that associates arc functionality with contact types */
61 static struct
62 {
63 	INTBIG arcfunct[3];
64 	CHAR *contactname;
65 } io_lcontacts[] =
66 {
67 	{{APMETAL1, APDIFFP,  0}, x_("MPDIFF")},
68 	{{APMETAL1, APDIFFN,  0}, x_("MNDIFF")},
69 	{{APMETAL1, APPOLY1,  0}, x_("MPOLY")},
70 	{{APMETAL1, APMETAL2, 0}, x_("M1M2")},
71 	{{0,0,0}, NULL}  /* 0 */
72 };
73 
74 /* prototypes for local routines */
75 static INTBIG io_l_writecell(NODEPROTO*);
76 static CHAR *io_l_name(NODEINST*);
77 static CHAR *io_l_fixname(CHAR*);
78 static INTBIG io_l_nodetype(NODEINST*);
79 
io_writellibrary(LIBRARY * lib)80 BOOLEAN io_writellibrary(LIBRARY *lib)
81 {
82 	CHAR file[100], *truename;
83 	REGISTER CHAR *name;
84 	REGISTER NODEPROTO *np;
85 	REGISTER LIBRARY *olib;
86 
87 	/* create the proper disk file for the L */
88 	if (lib->curnodeproto == NONODEPROTO)
89 	{
90 		ttyputerr(_("Must be editing a cell to generate L output"));
91 		return(TRUE);
92 	}
93 	(void)estrcpy(file, lib->curnodeproto->protoname);
94 	(void)estrcat(file, x_(".L"));
95 	name = truepath(file);
96 	io_fileout = xcreate(name, io_filetypel, _("L File"), &truename);
97 	if (io_fileout == NULL)
98 	{
99 		if (truename != 0) ttyputerr(_("Cannot write %s"), truename);
100 		return(TRUE);
101 	}
102 
103 	xprintf(io_fileout, x_("L:: TECH ANY\n"));
104 
105 	/* write the L */
106 	for(olib = el_curlib; olib != NOLIBRARY; olib = olib->nextlibrary)
107 		for(np = olib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
108 			np->temp1 = 0;
109 	if (io_l_writecell(lib->curnodeproto) != 0)
110 		ttyputmsg(_("Back-annotation information has been added (library must be saved)"));
111 
112 	/* clean up */
113 	xclose(io_fileout);
114 
115 	/* tell the user that the file is written */
116 	ttyputmsg(_("%s written"), truename);
117 
118 	return(FALSE);
119 }
120 
121 /*
122  * Routine to write "L" for cell "np".  Returns nonzero if back-annotation was added.
123  */
io_l_writecell(NODEPROTO * np)124 INTBIG io_l_writecell(NODEPROTO *np)
125 {
126 	REGISTER NODEINST *subno, *oni;
127 	REGISTER ARCINST *subar, *oar;
128 	REGISTER NODEPROTO *subnt, *onp;
129 	REGISTER PORTPROTO *pp;
130 	PORTPROTO *gateleft, *gateright, *activetop, *activebottom;
131 	REGISTER PORTARCINST *pi, *opi;
132 	REGISTER INTBIG i, j, k, l, r, thisend, thatend, tot, nature, enature, ot, segcount, backannotate;
133 	REGISTER INTBIG segdist;
134 	REGISTER CHAR *type, *lay, *dir, *lastdir;
135 	CHAR line[50];
136 	INTBIG xpos, ypos, len, wid, lx, hx, ly, hy;
137 	REGISTER ARCPROTO **aplist;
138 
139 	/* if there are any sub-cells that have not been written, write them */
140 	backannotate = 0;
141 	for(subno = np->firstnodeinst; subno != NONODEINST; subno = subno->nextnodeinst)
142 	{
143 		subnt = subno->proto;
144 		if (subnt->primindex != 0) continue;
145 
146 		/* ignore recursive references (showing icon in contents) */
147 		if (isiconof(subnt, np)) continue;
148 
149 		/* convert body cells to contents cells */
150 		onp = contentsview(subnt);
151 		if (onp != NONODEPROTO) subnt = onp;
152 
153 		/* don't recurse if this cell has already been written */
154 		if (subnt->temp1 != 0) continue;
155 
156 		/* recurse to the bottom */
157 		if (io_l_writecell(subnt) != 0) backannotate = 1;
158 	}
159 	np->temp1 = 1;
160 
161 	/* make sure that all nodes have names on them */
162 	if (asktool(net_tool, x_("name-nodes"), (INTBIG)np) != 0) backannotate++;
163 
164 	/* write the cell header */
165 	xprintf(io_fileout, x_("\n"));
166 	if (np->cellview == el_layoutview) xprintf(io_fileout, x_("LAYOUT "));
167 	if (np->cellview == el_schematicview) xprintf(io_fileout, x_("SCHEMATIC "));
168 	if (np->cellview == el_iconview) xprintf(io_fileout, x_("ICON "));
169 	if (np->cellview == el_skeletonview) xprintf(io_fileout, x_("BBOX "));
170 	xprintf(io_fileout, x_("CELL %s ( )\n{\n"), io_l_fixname(np->protoname));
171 
172 	/* write the bounding box */
173 	xprintf(io_fileout, x_("#bbox: ll= (%s,%s) ur= (%s,%s)\n"),
174 		latoa(np->lowx, 0), latoa(np->lowy, 0), latoa(np->highx, 0), latoa(np->highy, 0));
175 
176 	/* write the ports */
177 	for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
178 	{
179 		portposition(pp->subnodeinst, pp->subportproto, &xpos, &ypos);
180 		switch (pp->userbits&STATEBITS)
181 		{
182 			case GNDPORT:   type = x_("GND");    break;
183 			case PWRPORT:   type = x_("VDD");    break;
184 			case INPORT:    type = x_("IN");     break;
185 			case OUTPORT:   type = x_("OUT");    break;
186 			case BIDIRPORT: type = x_("INOUT");  break;
187 			default:        type = x_("");       break;
188 		}
189 		lay = pp->connects[0]->protoname;
190 		for(i=0; io_lports[i].arcfunct != 0; i++)
191 			if ((INTBIG)(((pp->connects[0]->userbits&AFUNCTION)>>AFUNCTIONSH)) == io_lports[i].arcfunct)
192 		{
193 			lay = io_lports[i].portname;   break;
194 		}
195 		xprintf(io_fileout, x_("\t%s %s %s (%s,%s) ;\n"), type, lay,
196 			io_l_fixname(pp->protoname), latoa(xpos, 0), latoa(ypos, 0));
197 	}
198 	xprintf(io_fileout, x_("\n"));
199 
200 	/* number all components in the cell */
201 	i = 1;
202 	for(subno = np->firstnodeinst; subno != NONODEINST; subno = subno->nextnodeinst)
203 		subno->temp1 = i++;
204 
205 	/* write the components */
206 	for(subno = np->firstnodeinst; subno != NONODEINST; subno = subno->nextnodeinst)
207 	{
208 		if (io_l_nodetype(subno) == TRUEPIN) continue;
209 		subnt = subno->proto;
210 
211 		/* determine type of component */
212 		type = subnt->protoname;
213 		if (subnt->primindex == 0)
214 		{
215 			/* ignore recursive references (showing icon in contents) */
216 			if (isiconof(subnt, np)) continue;
217 
218 			/* convert body cells to contents cells */
219 			onp = contentsview(subnt);
220 			if (onp != NONODEPROTO) subnt = onp;
221 			type = subnt->protoname;
222 			xprintf(io_fileout, x_("\tINST %s %s"), type, io_l_name(subno));
223 		} else
224 		{
225 			i = nodefunction(subno);
226 			if (i == NPPIN)
227 			{
228 				/* if pin is an export, don't write separate node statement */
229 				if (subno->firstportexpinst != NOPORTEXPINST) continue;
230 				for(j=0; io_lports[j].arcfunct != 0; j++)
231 					if ((INTBIG)(((subno->proto->firstportproto->connects[0]->userbits&
232 						AFUNCTION)>>AFUNCTIONSH)) == io_lports[j].arcfunct) break;
233 				if (io_lports[j].arcfunct != 0) type = io_lports[j].portname; else
234 					type = x_("???");
235 				(void)esnprintf(line, 50, x_("NODE %s"), type);
236 				type = line;
237 			}
238 
239 			/* special type names for well/substrate contacts */
240 			if (i == NPWELL) type = x_("MNSUB");
241 			if (i == NPSUBSTRATE) type = x_("MPSUB");
242 
243 			/* special type names for contacts */
244 			if (i == NPCONTACT || i == NPCONNECT)
245 			{
246 				aplist = subnt->firstportproto->connects;
247 				for(k=0; io_lcontacts[k].arcfunct[0] != 0; k++)
248 				{
249 					for(j=0; j<3; j++)
250 					{
251 						if (io_lcontacts[k].arcfunct[j] == 0) continue;
252 						for(l=0; aplist[l] != NOARCPROTO; l++)
253 							if ((INTBIG)(((aplist[l]->userbits&AFUNCTION) >> AFUNCTIONSH)) ==
254 								io_lcontacts[k].arcfunct[j]) break;
255 						if (aplist[l] == NOARCPROTO) break;
256 					}
257 					if (j < 3) continue;
258 					type = io_lcontacts[k].contactname;
259 					break;
260 				}
261 			}
262 
263 			/* special type names for transistors */
264 			if (i == NPTRANMOS) type = x_("TN");
265 			if (i == NPTRADMOS) type = x_("TD");
266 			if (i == NPTRAPMOS) type = x_("TP");
267 
268 			/* write the type and name */
269 			xprintf(io_fileout, x_("\t%s %s"), type, io_l_name(subno));
270 		}
271 
272 		/* write rotation */
273 		if (subno->rotation != 0 || subno->transpose != 0)
274 		{
275 			r = subno->rotation;
276 			if (subno->transpose != 0)
277 			{
278 				xprintf(io_fileout, x_(" RX"));
279 				r = (r+2700) % 3600;
280 			}
281 			xprintf(io_fileout, x_(" R%s"), frtoa(r*WHOLE/10));
282 		}
283 
284 		/* write size if nonstandard */
285 		if (subno->highx-subno->lowx != subnt->highx-subnt->lowx ||
286 			subno->highy-subno->lowy != subnt->highy-subnt->lowy)
287 		{
288 			if (i == NPTRANMOS || i == NPTRADMOS || i == NPTRAPMOS)
289 				transistorsize(subno, &len, &wid); else
290 			{
291 				nodesizeoffset(subno, &lx, &ly, &hx, &hy);
292 				len = subno->highy-hy - (subno->lowy+ly);
293 				wid = subno->highx-hx - (subno->lowx+lx);
294 			}
295 			xprintf(io_fileout, x_(" W=%s L=%s"), latoa(wid, 0), latoa(len, 0));
296 		}
297 
298 		/* write location */
299 		if (subnt->primindex != 0)
300 		{
301 			xprintf(io_fileout, x_(" AT (%s,%s) ;\n"),
302 				latoa((subno->lowx+subno->highx)/2, 0), latoa((subno->lowy+subno->highy)/2, 0));
303 		} else
304 		{
305 			xprintf(io_fileout, x_(" AT (%s,%s) ;\n"),
306 				latoa((subno->lowx+subno->highx-subnt->lowx-subnt->highx)/2, 0),
307 					latoa((subno->lowy+subno->highy-subnt->lowy-subnt->highy)/2, 0));
308 		}
309 	}
310 	xprintf(io_fileout, x_("\n"));
311 
312 	/* write all arcs connected to nodes */
313 	for(subar = np->firstarcinst; subar != NOARCINST; subar = subar->nextarcinst)
314 		subar->temp1 = 0;
315 	for(subno = np->firstnodeinst; subno != NONODEINST; subno = subno->nextnodeinst)
316 	{
317 		nature = io_l_nodetype(subno);
318 		if (nature == TRUEPIN) continue;
319 		for(pi = subno->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
320 		{
321 			subar = pi->conarcinst;
322 			if (subar->temp1 != 0) continue;
323 			xprintf(io_fileout, x_("\tWIRE"));
324 			for(i=0; io_lports[i].arcfunct != 0; i++)
325 				if ((INTBIG)(((subar->proto->userbits&AFUNCTION)>>AFUNCTIONSH)) == io_lports[i].arcfunct)
326 			{
327 				xprintf(io_fileout, x_(" %s"), io_lports[i].portname);
328 				break;
329 			}
330 
331 			/* write the wire width if nonstandard */
332 			if (subar->width != defaultarcwidth(subar->proto))
333 				xprintf(io_fileout, x_(" W=%s"), latoa(subar->width-arcwidthoffset(subar), 0));
334 
335 			/* write the starting node name (use port name if pin is an export) */
336 			if (subno->firstportexpinst != NOPORTEXPINST && nodefunction(subno) == NPPIN)
337 				xprintf(io_fileout, x_(" %s"),
338 						io_l_fixname(subno->firstportexpinst->exportproto->protoname)); else
339 							xprintf(io_fileout, x_(" %s"), io_l_name(subno));
340 
341 			/* qualify node name with port name if a transistor or instance */
342 			if (nature == TRANSISTOR)
343 			{
344 				transistorports(subno, &gateleft, &gateright, &activetop, &activebottom);
345 				if (pi->proto == gateleft) xprintf(io_fileout, x_(".gl"));
346 				if (pi->proto == activetop) xprintf(io_fileout, x_(".d"));
347 				if (pi->proto == gateright) xprintf(io_fileout, x_(".gr"));
348 				if (pi->proto == activebottom) xprintf(io_fileout, x_(".s"));
349 			} else if (nature == INSTANCE)
350 				xprintf(io_fileout, x_(".%s"), io_l_fixname(pi->proto->protoname));
351 
352 			/* prepare to run along the wire to a terminating node */
353 			if (subar->end[0].portarcinst == pi) thatend = 1; else thatend = 0;
354 			lastdir = x_("");
355 			segdist = -1;
356 			segcount = 0;
357 			for(;;)
358 			{
359 				/* get information about this segment (arc "subar") */
360 				subar->temp1 = 1;
361 				thisend = 1 - thatend;
362 				dir = lastdir;
363 				if (subar->end[thatend].xpos == subar->end[thisend].xpos)
364 				{
365 					if (subar->end[thatend].ypos > subar->end[thisend].ypos) dir = x_("UP"); else
366 						if (subar->end[thatend].ypos < subar->end[thisend].ypos) dir = x_("DOWN");
367 				} else if (subar->end[thatend].ypos == subar->end[thisend].ypos)
368 				{
369 					if (subar->end[thatend].xpos > subar->end[thisend].xpos) dir = x_("RIGHT"); else
370 						if (subar->end[thatend].xpos < subar->end[thisend].xpos) dir = x_("LEFT");
371 				}
372 
373 				/* if segment is different from last, write out last one */
374 				if (estrcmp(dir, lastdir) != 0 && *lastdir != 0)
375 				{
376 					xprintf(io_fileout, x_(" %s"), lastdir);
377 					if (segdist >= 0) xprintf(io_fileout, x_("=%s"), latoa(segdist, 0));
378 					segdist = -1;
379 					segcount++;
380 				}
381 
382 				/* remember this segment's direction and length */
383 				lastdir = dir;
384 				oni = subar->end[thatend].nodeinst;
385 				enature = io_l_nodetype(oni);
386 				if ((nature != TRANSISTOR || segcount > 0) && enature != TRANSISTOR)
387 				{
388 					if (segdist < 0) segdist = 0;
389 					segdist += subar->length;
390 				}
391 
392 				/* if other node not a pin, stop now */
393 				if (enature != TRUEPIN) break;
394 
395 				/* end the loop if more than 1 wire out of next node "oni" */
396 				tot = 0;
397 				for(opi = oni->firstportarcinst; opi != NOPORTARCINST; opi = opi->nextportarcinst)
398 				{
399 					if (opi->conarcinst->temp1 != 0) continue;
400 					tot++;
401 					oar = opi->conarcinst;
402 					if (oar->end[0].portarcinst == opi) ot = 1; else ot = 0;
403 				}
404 				if (tot != 1) break;
405 				subar = oar;
406 				thatend = ot;
407 			}
408 			if (*lastdir != 0)
409 			{
410 				xprintf(io_fileout, x_(" %s"), lastdir);
411 				if (segdist >= 0) xprintf(io_fileout, x_("=%s"), latoa(segdist, 0));
412 			} else xprintf(io_fileout, x_(" TO"));
413 
414 			/* write the terminating node name (use port name if pin is an export) */
415 			if (oni->firstportexpinst != NOPORTEXPINST && nodefunction(oni) == NPPIN)
416 				xprintf(io_fileout, x_(" %s"),
417 					io_l_fixname(oni->firstportexpinst->exportproto->protoname)); else
418 						xprintf(io_fileout, x_(" %s"), io_l_name(oni));
419 
420 			/* qualify node name with port name if a transistor or an instance */
421 			opi = subar->end[thatend].portarcinst;
422 			if (enature == TRANSISTOR)
423 			{
424 				transistorports(oni, &gateleft, &gateright, &activetop, &activebottom);
425 				if (opi->proto == gateleft) xprintf(io_fileout, x_(".gl"));
426 				if (opi->proto == activetop) xprintf(io_fileout, x_(".d"));
427 				if (opi->proto == gateright) xprintf(io_fileout, x_(".gr"));
428 				if (opi->proto == activebottom) xprintf(io_fileout, x_(".s"));
429 			} else if (enature == INSTANCE)
430 				xprintf(io_fileout, x_(".%s"), io_l_fixname(opi->proto->protoname));
431 			xprintf(io_fileout, x_(" ;\n"));
432 		}
433 	}
434 
435 	/* write any unmentioned wires (shouldn't be any) */
436 	for(subar = np->firstarcinst; subar != NOARCINST; subar = subar->nextarcinst)
437 	{
438 		if (subar->temp1 != 0) continue;
439 		xprintf(io_fileout, x_("# WIRE %s not described!!\n"), describearcinst(subar));
440 	}
441 	xprintf(io_fileout, x_("}\n"));
442 	return(backannotate);
443 }
444 
io_l_name(NODEINST * ni)445 CHAR *io_l_name(NODEINST *ni)
446 {
447 	REGISTER VARIABLE *var;
448 	static CHAR line[30];
449 
450 	var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, el_node_name_key);
451 	if (var != NOVARIABLE) return(io_l_fixname((CHAR *)var->addr));
452 	ttyputerr(_("L generation warning: no name on node %s"), describenodeinst(ni));
453 	(void)esnprintf(line, 30, x_("c%ld"), ni->temp1);
454 	return(line);
455 }
456 
io_l_fixname(CHAR * name)457 CHAR *io_l_fixname(CHAR *name)
458 {
459 	REGISTER CHAR *pt;
460 	REGISTER INTBIG i;
461 	static CHAR *badname[] = {x_("VDD"), x_("GND"), 0};
462 	REGISTER void *infstr;
463 
464 	/* check for reserved names */
465 	for(i=0; badname[i] != 0; i++)
466 		if (estrcmp(name, badname[i]) == 0)
467 	{
468 		/* this is a reserved name: change it */
469 		infstr = initinfstr();
470 		addstringtoinfstr(infstr, name);
471 		addstringtoinfstr(infstr, x_("XXX"));
472 		return(returninfstr(infstr));
473 	}
474 
475 	/* check for special characters */
476 	for(pt = name; *pt != 0; pt++) if (isalnum(*pt) == 0) break;
477 	if (*pt != 0)
478 	{
479 		/* name has special characters: remove them */
480 		infstr = initinfstr();
481 		for(pt = name; *pt != 0; pt++)
482 			if (isalnum(*pt) != 0) addtoinfstr(infstr, *pt);
483 		return(returninfstr(infstr));
484 	}
485 
486 	/* name is fine: use it as is */
487 	return(name);
488 }
489 
490 /*
491  * Routine to determine the type of node "ni".  Returns:
492  *    TRUEPIN    if a true pin (exactly two connections)
493  *    TRANSISTOR if a transistor
494  *    INSTANCE   if a cell instance
495  *    OTHERNODE  otherwise.
496  */
io_l_nodetype(NODEINST * ni)497 INTBIG io_l_nodetype(NODEINST *ni)
498 {
499 	REGISTER INTBIG i;
500 	REGISTER PORTARCINST *pi;
501 
502 	if (ni->proto->primindex == 0) return(INSTANCE);
503 	i = nodefunction(ni);
504 	if (i == NPTRANMOS || i == NPTRADMOS || i == NPTRAPMOS) return(TRANSISTOR);
505 	if (i != NPPIN) return(OTHERNODE);
506 	i = 0;
507 	for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
508 		i++;
509 	if (i != 2) return(OTHERNODE);
510 	return(TRUEPIN);
511 }
512