/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: L.java
* Input/output tool: L Netlist output
* Written by Steven M. Rubin, Sun Microsystems.
*
* Copyright (c) 2004 Sun Microsystems and Static Free Software
*
* Electric(tm) is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* Electric(tm) is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Electric(tm); see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, Mass 02111-1307, USA.
*/
package com.sun.electric.tool.io.output;
import com.sun.electric.database.geometry.Orientation;
import com.sun.electric.database.geometry.Poly;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.hierarchy.Export;
import com.sun.electric.database.hierarchy.View;
import com.sun.electric.database.prototype.NodeProto;
import com.sun.electric.database.prototype.PortCharacteristic;
import com.sun.electric.database.prototype.PortProto;
import com.sun.electric.database.text.TextUtils;
import com.sun.electric.database.topology.ArcInst;
import com.sun.electric.database.topology.Connection;
import com.sun.electric.database.topology.NodeInst;
import com.sun.electric.database.topology.PortInst;
import com.sun.electric.database.variable.VarContext;
import com.sun.electric.technology.ArcProto;
import com.sun.electric.technology.PrimitiveNode;
import com.sun.electric.technology.PrimitivePort;
import com.sun.electric.technology.TransistorSize;
import java.awt.geom.Rectangle2D;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/**
* This is the netlister for L.
*/
public class L extends Output
{
// node types returned by "getNodeType"
private static final int TRUEPIN = 1;
private static final int TRANSISTOR = 2;
private static final int INSTANCE = 3;
private static final int OTHERNODE = 4;
private Set<Cell> cellsSeen;
/** the results of calling "transistorPorts". */ private PortInst gateLeft, gateRight, activeTop, activeBottom;
// private LPreferences localPrefs;
public static class LPreferences extends OutputPreferences
{
public LPreferences(boolean factory) { super(factory); }
public Output doOutput(Cell cell, VarContext context, String filePath)
{
L out = new L(this);
if (out.openTextOutputStream(filePath)) return out.finishWrite();
out.writeLCells(cell);
if (out.closeTextOutputStream()) return out.finishWrite();
System.out.println(filePath + " written");
return out.finishWrite();
}
}
/**
* Creates a new instance of the L netlister.
*/
L(LPreferences lp) { /* localPrefs = lp; */ }
/**
* Method to write all cells below a given Cell.
* @param cell the top Cell of the hierarchy to write.
*/
private void writeLCells(Cell cell)
{
printWriter.println("L:: TECH ANY");
cellsSeen = new HashSet<Cell>();
writeLCell(cell);
}
/**
* Method to write "L" for a cell.
* @param cell the Cell to write.
*/
private void writeLCell(Cell cell)
{
// if there are any sub-cells that have not been written, write them
for(Iterator<NodeInst> it = cell.getNodes(); it.hasNext(); )
{
NodeInst ni = it.next();
if (!ni.isCellInstance()) continue;
if (ni.isIconOfParent()) continue;
Cell np = (Cell)ni.getProto();
// convert body cells to contents cells
Cell oNp = np.contentsView();
if (oNp != null) np = oNp;
// don't recurse if this cell has already been written
if (cellsSeen.contains(np)) continue;
// recurse to the bottom
writeLCell(np);
}
cellsSeen.add(cell);
// write the cell header
printWriter.println("");
if (cell.getView() == View.LAYOUT) printWriter.print("LAYOUT ");
if (cell.isSchematic()) printWriter.print("SCHEMATIC ");
if (cell.isIcon()) printWriter.print("ICON ");
if (cell.getView() == View.LAYOUTSKEL) printWriter.print("BBOX ");
printWriter.println("CELL " + getLegalName(cell.getName()) + " ( )\n{");
// write the bounding box
Rectangle2D bounds = cell.getBounds();
printWriter.println("#bbox: ll= (" + TextUtils.formatDouble(bounds.getMinX()) + "," +
TextUtils.formatDouble(bounds.getMinY()) + ") ur= (" +
TextUtils.formatDouble(bounds.getMaxX()) + "," +
TextUtils.formatDouble(bounds.getMaxY()) + ")");
// write the ports
for(Iterator<PortProto> it = cell.getPorts(); it.hasNext(); )
{
Export e = (Export)it.next();
Poly poly = e.getPoly();
double xPos = poly.getCenterX();
double yPos = poly.getCenterY();
String type = "";
if (e.getCharacteristic() == PortCharacteristic.GND) type = "GND"; else
if (e.getCharacteristic() == PortCharacteristic.PWR) type = "VDD"; else
if (e.getCharacteristic() == PortCharacteristic.IN) type = "IN"; else
if (e.getCharacteristic() == PortCharacteristic.OUT) type = "OUT"; else
if (e.getCharacteristic() == PortCharacteristic.BIDIR) type = "INOUT";
ArcProto ap = e.getBasePort().getConnection();
String lay = getArcFunctionName(ap, ap.getName());
printWriter.println("\t" + type + " " + lay + " " + getLegalName(e.getName()) +
" (" + TextUtils.formatDouble(xPos) + "," + TextUtils.formatDouble(yPos) + ") ;");
}
printWriter.println("");
// write the components
for(Iterator<NodeInst> it = cell.getNodes(); it.hasNext(); )
{
NodeInst ni = it.next();
if (getNodeType(ni) == TRUEPIN) continue;
NodeProto np = ni.getProto();
PrimitiveNode.Function fun = ni.getFunction();
// determine type of component
String type = np.getName();
if (ni.isCellInstance())
{
// ignore recursive references (showing icon in contents)
if (ni.isIconOfParent()) continue;
// convert body cells to contents cells
Cell oNp = ((Cell)np).contentsView();
if (oNp != null) np = oNp;
type = np.getName();
printWriter.print("\tINST " + type + " " + getLegalName(ni.getName()));
} else
{
PrimitiveNode npPrim = (PrimitiveNode)np;
if (fun.isPin())
{
// if pin is an export, don't write separate node statement
if (ni.hasExports()) continue;
PrimitivePort primPp = npPrim.getPort(0);
ArcProto ap = primPp.getConnection();
type = "NODE " + getArcFunctionName(ap, "???");
}
// special type names for well/substrate contacts
if (fun == PrimitiveNode.Function.WELL) type = "MNSUB";
if (fun == PrimitiveNode.Function.SUBSTRATE) type = "MPSUB";
// special type names for contacts
if (fun.isContact() || fun == PrimitiveNode.Function.CONNECT)
{
boolean conMetal1 = false, conMetal2 = false, conPActive = false, conNActive = false, conPoly = false;
for(int j=0; j<npPrim.getNumPorts(); j++)
{
PrimitivePort primPp = npPrim.getPort(j);
ArcProto [] arcs = primPp.getConnections();
for(int k=0; k<arcs.length; k++)
{
ArcProto ap = arcs[k];
ArcProto.Function aFun = ap.getFunction();
if (aFun == ArcProto.Function.METAL1) conMetal1 = true;
if (aFun == ArcProto.Function.METAL2) conMetal2 = true;
if (aFun == ArcProto.Function.POLY1) conPoly = true;
if (aFun == ArcProto.Function.DIFFP) conPActive = true;
if (aFun == ArcProto.Function.DIFFN) conNActive = true;
}
}
if (conMetal1)
{
if (conMetal2) type = "M1M2"; else
if (conPoly) type = "MPOLY"; else
if (conPActive) type = "MPDIFF"; else
if (conNActive) type = "MNDIFF";
}
}
// special type names for transistors
if (fun.isNTypeTransistor()) type = "TN"; else
if (fun.isPTypeTransistor()) type = "TP"; else
if (fun == PrimitiveNode.Function.TRADMOS ||
fun == PrimitiveNode.Function.TRAPMOSD) type = "TD";
// write the type and name
printWriter.print("\t" + type + " " + getLegalName(ni.getName()));
}
// write rotation
Orientation or = ni.getOrient();
int oldRotation = or.getCAngle();
int oldTranspose = or.isCTranspose() ? 1 : 0;
if (oldRotation != 0 || oldTranspose != 0)
{
if (oldTranspose != 0)
{
printWriter.print(" RX");
oldRotation = (oldRotation+2700) % 3600;
}
printWriter.print(" R" + TextUtils.formatDouble(oldRotation/10));
}
// write size if nonstandard
if (ni.getXSize() != np.getDefWidth() || ni.getYSize() != np.getDefHeight())
{
double wid = ni.getLambdaBaseXSize();
double len = ni.getLambdaBaseYSize();
if (fun.isFET())
{
TransistorSize ts = ni.getTransistorSize(null);
len = ts.getDoubleLength();
wid = ts.getDoubleWidth();
}
printWriter.print(" W=" + TextUtils.formatDouble(wid) + " L=" + TextUtils.formatDouble(len));
}
// write location
if (ni.isCellInstance())
{
Rectangle2D cellBounds = ((Cell)ni.getProto()).getBounds();
printWriter.println(" AT (" + TextUtils.formatDouble(ni.getTrueCenterX() - cellBounds.getCenterX()) + "," +
TextUtils.formatDouble(ni.getTrueCenterY() - cellBounds.getCenterY()) + ") ;");
} else
{
printWriter.println(" AT (" + TextUtils.formatDouble(ni.getTrueCenterX()) + "," +
TextUtils.formatDouble(ni.getTrueCenterY()) + ") ;");
}
}
printWriter.println("");
// write all arcs connected to nodes
Set<ArcInst> arcsSeen = new HashSet<ArcInst>();
for(Iterator<NodeInst> it = cell.getNodes(); it.hasNext(); )
{
NodeInst ni = it.next();
int nature = getNodeType(ni);
if (nature == TRUEPIN) continue;
for(Iterator<Connection> cIt = ni.getConnections(); cIt.hasNext(); )
{
Connection con = cIt.next();
ArcInst ai = con.getArc();
if (arcsSeen.contains(ai)) continue;
printWriter.print("\tWIRE");
String alt = getArcFunctionName(ai.getProto(), null);
if (alt != null) printWriter.print(" " + alt);
// write the wire width if nonstandard
if (ai.getLambdaBaseWidth() != ai.getProto().getDefaultLambdaBaseWidth())
printWriter.print(" W=" + TextUtils.formatDouble(ai.getLambdaBaseWidth()));
// write the starting node name (use port name if pin is an export)
if (ni.hasExports() && ni.getFunction().isPin())
{
Export e = ni.getExports().next();
printWriter.print(" " + getLegalName(e.getName()));
} else
{
printWriter.print(" "+ getLegalName(ni.getName()));
}
// qualify node name with port name if a transistor or instance
PortInst pi = con.getPortInst();
if (nature == TRANSISTOR)
{
transistorPorts(ni);
if (pi == gateLeft) printWriter.print(".gl");
if (pi == activeTop) printWriter.print(".d");
if (pi == gateRight) printWriter.print(".gr");
if (pi == activeBottom) printWriter.print(".s");
} else if (nature == INSTANCE)
printWriter.print("." + getLegalName(pi.getPortProto().getName()));
// prepare to run along the wire to a terminating node
int thatEnd = 1 - con.getEndIndex();
String lastDir = "";
double segDist = -1;
int segCount = 0;
int eNature = 0;
NodeInst oNi = null;
for(;;)
{
// get information about this segment (arc "ai")
arcsSeen.add(ai);
int thisEnd = 1 - thatEnd;
String dir = lastDir;
if (ai.getLocation(thatEnd).getX() == ai.getLocation(thisEnd).getX())
{
if (ai.getLocation(thatEnd).getY() > ai.getLocation(thisEnd).getY()) dir = "UP"; else
if (ai.getLocation(thatEnd).getY() < ai.getLocation(thisEnd).getY()) dir = "DOWN";
} else if (ai.getLocation(thatEnd).getY() == ai.getLocation(thisEnd).getY())
{
if (ai.getLocation(thatEnd).getX() > ai.getLocation(thisEnd).getX()) dir = "RIGHT"; else
if (ai.getLocation(thatEnd).getX() < ai.getLocation(thisEnd).getX()) dir = "LEFT";
}
// if segment is different from last, write out last one
if (!dir.equals(lastDir) && lastDir.length() > 0)
{
printWriter.print(" " + lastDir);
if (segDist >= 0) printWriter.print("=" + TextUtils.formatDouble(segDist));
segDist = -1;
segCount++;
}
// remember this segment's direction and length
lastDir = dir;
oNi = ai.getPortInst(thatEnd).getNodeInst();
eNature = getNodeType(oNi);
if ((nature != TRANSISTOR || segCount > 0) && eNature != TRANSISTOR)
{
if (segDist < 0) segDist = 0;
segDist += ai.getLambdaLength();
}
// if other node not a pin, stop now
if (eNature != TRUEPIN) break;
// end the loop if more than 1 wire out of next node "oNi"
int tot = 0;
int ot = 0;
ArcInst oAi = null;
for(Iterator<Connection> oCIt = oNi.getConnections(); oCIt.hasNext(); )
{
Connection oCon = oCIt.next();
if (arcsSeen.contains(oCon.getArc())) continue;
oAi = oCon.getArc();
tot++;
ot = 1 - oCon.getEndIndex();
}
if (tot != 1) break;
ai = oAi;
thatEnd = ot;
}
if (lastDir.length() > 0)
{
printWriter.print(" " + lastDir);
if (segDist >= 0) printWriter.print("=" + TextUtils.formatDouble(segDist));
} else printWriter.print(" TO");
// write the terminating node name (use port name if pin is an export)
if (oNi.hasExports() && oNi.getFunction().isPin())
{
Export e = oNi.getExports().next();
printWriter.print(" " + getLegalName(e.getName()));
} else
{
printWriter.print(" " + getLegalName(oNi.getName()));
}
// qualify node name with port name if a transistor or an instance
PortInst oPi = ai.getPortInst(thatEnd);
if (eNature == TRANSISTOR)
{
transistorPorts(oNi);
if (oPi == gateLeft) printWriter.print(".gl");
if (oPi == activeTop) printWriter.print(".d");
if (oPi == gateRight) printWriter.print(".gr");
if (oPi == activeBottom) printWriter.print(".s");
} else if (eNature == INSTANCE)
printWriter.print("." + getLegalName(oPi.getPortProto().getName()));
printWriter.println(" ;");
}
}
// write any unmentioned wires (shouldn't be any)
for(Iterator<ArcInst> it = cell.getArcs(); it.hasNext(); )
{
ArcInst ai = it.next();
if (arcsSeen.contains(ai)) continue;
printWriter.println("# WIRE " + ai.describe(true) + " not described!!");
}
printWriter.println("}");
}
/**
* Method to return the ports of a transistor in field variables "gateLeft", "gateRight",
* "activeTop", and "activeBottom". If "gateRight" is null, there is only
* one gate port.
* @param ni the NodeInst to analyze.
*/
private void transistorPorts(NodeInst ni)
{
PrimitiveNode.Function fun = ni.getFunction();
gateLeft = activeTop = gateRight = activeBottom = null;
if (ni.getNumPortInsts() < 3) return;
gateLeft = ni.getPortInst(0);
activeTop = ni.getPortInst(1);
gateRight = ni.getPortInst(2);
if (ni.getNumPortInsts() == 3 || fun == PrimitiveNode.Function.TRANPN ||
fun == PrimitiveNode.Function.TRAPNP || fun == PrimitiveNode.Function.TRA4NMOS || fun == PrimitiveNode.Function.TRA4DMOS ||
fun == PrimitiveNode.Function.TRA4PMOS || fun == PrimitiveNode.Function.TRA4NPN || fun == PrimitiveNode.Function.TRA4PNP ||
fun == PrimitiveNode.Function.TRA4NJFET || fun == PrimitiveNode.Function.TRA4PJFET ||
fun == PrimitiveNode.Function.TRA4DMES || fun == PrimitiveNode.Function.TRA4EMES)
{
activeBottom = gateRight;
gateRight = null;
} else
{
activeBottom = ni.getPortInst(3);
}
}
/**
* Method to convert a name to a legal L name.
* @param name the name to convert.
* @return the legal L name to use.
*/
private String getLegalName(String name)
{
// check for reserved names
if (name.equals("VDD")) return "VDDXXX";
if (name.equals("GND")) return "GNDXXX";
// check for special characters
boolean badChars = false;
for(int i=0; i<name.length(); i++)
if (!TextUtils.isLetterOrDigit(name.charAt(i))) badChars = true;
if (!badChars) return name;
// name has special characters: remove them
StringBuffer sb = new StringBuffer();
for(int i=0; i<name.length(); i++)
if (TextUtils.isLetterOrDigit(name.charAt(i))) sb.append(name.charAt(i));
return sb.toString();
}
/**
* Method to determine the type of a NodeInst.
* @param ni the NodeInst to analyze.
* @return
* TRUEPIN if a true pin (exactly two connections)<BR>
* TRANSISTOR if a transistor<BR>
* INSTANCE if a cell instance<BR>
* OTHERNODE otherwise.
*/
private int getNodeType(NodeInst ni)
{
if (ni.isCellInstance()) return INSTANCE;
PrimitiveNode.Function fun = ni.getFunction();
if (fun.isFET()) return TRANSISTOR;
if (!fun.isPin()) return OTHERNODE;
if (ni.getNumConnections() != 2) return OTHERNODE;
return TRUEPIN;
}
/**
* Method to return the name of an arc prototype's function.
* @param ap the ArcProto to analyze.
* @param def the default name to return if nothing can be determined.
* @return the name of the ArcProto's function.
*/
private String getArcFunctionName(ArcProto ap, String def)
{
ArcProto.Function fun = ap.getFunction();
if (fun.isMetal())
{
return "MET" + fun.getLevel();
}
if (fun.isPoly()) return "POLY";
if (fun == ArcProto.Function.DIFFP) return "PDIFF";
if (fun == ArcProto.Function.DIFFN) return "NDIFF";
if (fun == ArcProto.Function.DIFFS) return "NWELL";
if (fun == ArcProto.Function.DIFFW) return "PWELL";
return def;
}
}