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