/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: CIF.java
* Input/output tool: CIF output
* Written by Steven M. Rubin, Sun Microsystems.
*
* Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
*
* 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.Poly;
import com.sun.electric.database.geometry.PolyBase;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.hierarchy.HierarchyEnumerator;
import com.sun.electric.database.hierarchy.Nodable;
import com.sun.electric.database.text.Version;
import com.sun.electric.database.topology.Geometric;
import com.sun.electric.database.topology.NodeInst;
import com.sun.electric.database.variable.VarContext;
import com.sun.electric.technology.Layer;
import com.sun.electric.technology.Technology;
import com.sun.electric.tool.io.IOTool;
import com.sun.electric.util.TextUtils;
import com.sun.electric.util.math.DBMath;
import com.sun.electric.util.math.GenMath;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Class to write CIF output to disk.
*/
public class CIF extends Geometry
{
// preferences
// crc checksum stuff
/** checksum */ private int crcChecksum;
/** crc tab */ private int [] crcTab = new int[256];
/** keep track of chars */ private boolean crcPrevIsCharSep;
/** num chars written */ private int crcNumChars;
/** MOSIS initial key */ private static final int[] crcRow =
{0x04C11DB7, 0x09823B6E, 0x130476DC, 0x2608EDB8, 0x4C11DB70, 0x9823B6E0, 0x34867077, 0x690CE0EE};
// cif output data
/** cell number */ private int cellNumber = 100;
/** cell to cell number map */ private Map<Cell,Integer> cellNumbers;
/** scale factor from internal units. */ private double scaleFactor;
/** illegal characters in names (not really illegal but can cause problems) */
private static final String badNameChars = ":{}/\\";
private CIFPreferences localPrefs;
public static class CIFPreferences extends OutputPreferences
{
// CIF Settings
int cifScale = IOTool.getCIFOutScaleFactor();
boolean cifOutInstantiatesTopLevel = IOTool.isCIFOutInstantiatesTopLevel();
boolean cifOutMergesBoxes = IOTool.isCIFOutMergesBoxes();
boolean cifOutMimicsDisplay = IOTool.isCIFOutMimicsDisplay();
public CIFPreferences(boolean factory) { super(factory); }
@Override
public Output doOutput(Cell cell, VarContext context, String filePath)
{
CIF out = new CIF(this);
if (!out.openTextOutputStream(filePath)) // no error
{
CIFVisitor visitor = out.makeCIFVisitor(getMaxHierDepth(cell));
if (!out.writeCell(cell, context, visitor)) // no error
{
if (!out.closeTextOutputStream()) // no error
{
System.out.println(filePath + " written");
if (out.errorLogger.getNumErrors() != 0)
System.out.println(out.errorLogger.getNumErrors() + " CIF RESOLUTION ERRORS FOUND");
}
}
}
return out.finishWrite();
}
}
/**
* Creates a new instance of CIF
*/
CIF(CIFPreferences cp)
{
localPrefs = cp;
cellNumbers = new HashMap<Cell,Integer>();
// scale is in centimicrons, technology scale is in nanometers
scaleFactor = Technology.getCurrent().getScale() / 10 * localPrefs.cifScale;
}
protected void start()
{
if (localPrefs.includeDateAndVersionInOutput)
{
writeLine("( Electric VLSI Design System, version " + Version.getVersion() + " );");
Date now = new Date();
writeLine("( written on " + TextUtils.formatDate(now) + " );");
} else
{
writeLine("( Electric VLSI Design System );");
}
emitCopyright("( ", " );");
// initialize crc checksum info
for (int i=0; i<crcTab.length; i++)
{
crcTab[i] = 0;
for (int j=0; j<8; j++)
{
if (((1 << j) & i) != 0)
crcTab[i] = crcTab[i] ^ crcRow[j];
}
crcTab[i] &= 0xFFFFFFFF; // unneccessary in java, int is always 32 bits
}
crcNumChars = 1;
crcPrevIsCharSep = true;
crcChecksum = crcTab[' '];
}
protected void done()
{
if (localPrefs.cifOutInstantiatesTopLevel)
writeLine("C " + cellNumber + ";");
writeLine("E");
// finish up crc stuff
if (!crcPrevIsCharSep)
{
crcChecksum = (crcChecksum << 8) ^ crcTab[((crcChecksum >> 24) ^ ' ') & 0xFF];
crcNumChars++;
}
int bytesread = crcNumChars;
while (bytesread > 0)
{
crcChecksum = (crcChecksum << 8) ^ crcTab[((crcChecksum >> 24) ^ bytesread) & 0xFF];
bytesread >>= 8;
}
crcChecksum = ~crcChecksum & 0xFFFFFFFF;
System.out.println("MOSIS CRC: " + GenMath.unsignedIntValue(crcChecksum) + " " + crcNumChars);
}
/**
* Method to write cellGeom
*/
protected void writeCellGeom(CellGeom cellGeom)
{
cellNumber++;
writeLine("DS " + cellNumber + " 1 " + localPrefs.cifScale + ";");
String cellName = (cellGeom.nonUniqueName ? (cellGeom.cell.getLibrary().getName() + ":") : "") +
cellGeom.cell.getName() + ";";
// remove bad chars from cell name
StringBuffer sb = new StringBuffer();
for (int i=0; i<cellName.length(); i++)
{
char ch = cellName.charAt(i);
if (badNameChars.indexOf(ch) != -1) ch = '_';
sb.append(ch);
}
writeLine("9 " + sb.toString());
cellNumbers.put(cellGeom.cell, new Integer(cellNumber));
// write all polys by Layer
Set<Layer> layers = cellGeom.polyMap.keySet();
for (Layer layer : layers)
{
if (writeLayer(layer)) continue;
List<Object> polyList = cellGeom.polyMap.get(layer);
for (Object polyObj : polyList)
{
if (includeGeometric())
{
PolyWithGeom pg = (PolyWithGeom)polyObj;
Poly poly = pg.poly;
writePoly(poly, cellGeom.cell, pg.geom);
} else
{
PolyBase poly = (PolyBase)polyObj;
writePoly(poly, cellGeom.cell, null);
}
}
}
// write all instances
for (Iterator<NodeInst> noIt = cellGeom.cell.getNodes(); noIt.hasNext(); )
{
NodeInst ni = noIt.next();
if (ni.isCellInstance())
writeNodable(ni);
}
writeLine("DF;");
}
/**
* method to determine whether or not to merge geometry
*/
protected boolean mergeGeom(int hierLevelsFromBottom)
{
return localPrefs.cifOutMergesBoxes;
}
/**
* Method to determine whether or not to include the original Geometric with a Poly.
* Only includes Geometric information if NOT merging boxes, because if merging
* boxes, then the original Geometric information is lost.
*/
protected boolean includeGeometric()
{
return !localPrefs.cifOutMergesBoxes;
}
/**
* Method to emit the current layer number.
* @return true if the layer is invalid.
*/
private boolean writeLayer(Layer layer)
{
if (layer.isPseudoLayer()) return true;
String layName = layer.getCIFLayer();
if (layName == null || layName.equals("")) return true;
writeLine("L " + layName + ";");
return false;
}
private void writePoly(PolyBase poly, Cell cell, Geometric geom)
{
Point2D [] points = poly.getPoints();
if (poly.getStyle() == Poly.Type.DISC)
{
checkResolution(poly, cell, geom);
double r = points[0].distance(points[1]);
if (r <= 0) return; // ignore zero size geometry
int radius = scale(r);
int x = scale(points[0].getX());
int y = scale(points[0].getY());
String line = " R " + radius + " " + x + " " + y + ";";
writeLine(line);
} else
{
// ignore zero size geometry
Rectangle2D bounds = poly.getBounds2D();
if (bounds.getHeight() <= 0 || bounds.getWidth() <= 0) return;
// simple case if poly is a box
Rectangle2D box = poly.getBox();
if (box != null)
{
checkPointResolution(box.getWidth(), box.getHeight(), cell, geom, poly.getLayer(), poly);
checkPointResolution(box.getCenterX(), box.getCenterY(), cell, geom, poly.getLayer(), poly);
int width = scale(box.getWidth());
int height = scale(box.getHeight());
int x = scale(box.getCenterX());
int y = scale(box.getCenterY());
String line = " B " + width + " " + height + " " + x + " " + y + ";";
writeLine(line);
return;
}
// not a box
checkResolution(poly, cell, geom);
StringBuffer line = new StringBuffer(" P");
for (int i=0; i<points.length; i++)
{
int x = scale(points[i].getX());
int y = scale(points[i].getY());
line.append(" " + x + " " + y);
}
line.append(";");
writeLine(line.toString());
}
}
private void writeNodable(NodeInst ni)
{
Cell cell = (Cell)ni.getProto();
// if mimicing display and unexpanded, draw outline
if (!ni.isExpanded() && localPrefs.cifOutMimicsDisplay)
{
Rectangle2D bounds = ni.getBounds();
Poly poly = new Poly(bounds.getCenterX(), bounds.getCenterY(), ni.getXSize(), ni.getYSize());
AffineTransform localPureTrans = ni.rotateOutAboutTrueCenter();
poly.transform(localPureTrans);
Point2D [] points = poly.getPoints();
String line = "0V";
for(int i=0; i<4; i++)
line += " " + scale(points[i].getX()) + " " + scale(points[i].getY());
line += " " + scale(points[0].getX()) + " " + scale(points[0].getY());
writeLine(line + ";");
writeLine("2C \"" + cell.describe(false) + "\" T " + scale(bounds.getCenterX()) + " " +
scale(bounds.getCenterY()) + ";");
return;
}
// write a call to the cell
int cellNum = cellNumbers.get(cell).intValue();
int rotx = (int)(DBMath.cos(ni.getAngle()) * 100);
int roty = (int)(DBMath.sin(ni.getAngle()) * 100);
String line = "C " + cellNum + " R " + rotx + " " + roty;
if (ni.isMirroredAboutXAxis()) line += " M Y";
if (ni.isMirroredAboutYAxis()) line += " M X";
line += " T " + scale(ni.getAnchorCenterX()) + " " + scale(ni.getAnchorCenterY());
writeLine(line + ";");
}
/**
* Write a line to the CIF file, and accumulate
* checksum information.
*/
private void writeLine(String line)
{
line = line + '\n';
printWriter.print(line);
// crc checksum stuff
for (int i=0; i < line.length(); i++)
{
char c = line.charAt(i);
if (c > ' ')
{
crcChecksum = (crcChecksum << 8) ^ crcTab[((crcChecksum >> 24) ^ c) & 0xFF];
crcPrevIsCharSep = false;
} else if (!crcPrevIsCharSep)
{
crcChecksum = (crcChecksum << 8) ^ crcTab[((crcChecksum >> 24) ^ ' ') & 0xFF];
crcPrevIsCharSep = true;
}
crcNumChars++;
}
}
/**
* Method to scale Electric units to CIF units
*/
private int scale(double n)
{
return (int)(scaleFactor * n);
}
/**
* Check Poly for CIF Resolution Errors
*/
private void checkResolution(PolyBase poly, Cell cell, Geometric geom)
{
Point2D [] points = poly.getPoints();
for (int i=0; i<points.length; i++)
{
double x = points[i].getX();
double y = points[i].getY();
if (checkPointResolution(x, y, cell, geom, poly.getLayer(), poly)) return;
}
}
private boolean checkPointResolution(double x, double y, Cell cell, Geometric geom, Layer layer, PolyBase poly)
{
// first check for CIF resolution
boolean badPoints = false;
x *= scaleFactor;
y *= scaleFactor;
if (Math.round(x) != x || Math.round(y) != y)
{
badPoints = true;
}
boolean badResolution = false;
// if (minAllowedResolution != 0)
// {
//// if ((x % minAllowedResolution) != 0 || (y % minAllowedResolution) != 0)
// if (DBMath.hasRemainder(x, minAllowedResolution) || DBMath.hasRemainder(y, minAllowedResolution))
// {
// badResolution = true;
// }
// }
boolean error = badPoints || badResolution;
if (error)
{
// there was an error, for now print error
String layerName;
if (layer == null)
{
layerName = "**UNKNOWN**";
} else
{
layerName = layer.getName();
}
if (geom != null)
{
errorLogger.logError("Resolution less than CIF allows on layer " + layerName, geom, cell, null, layer.getIndex());
} else
{
errorLogger.logError("Resolution less than CIF allows on layer " + layerName, poly, cell, layer.getIndex());
}
}
return error;
}
/****************************** VISITOR SUBCLASS ******************************/
private CIFVisitor makeCIFVisitor(int maxDepth)
{
CIFVisitor visitor = new CIFVisitor(this, maxDepth);
return visitor;
}
/**
* Class to override the Geometry visitor and add bloating to all polygons.
* Currently, no bloating is being done.
*/
private class CIFVisitor extends Geometry.Visitor
{
CIFVisitor(Geometry outGeom, int maxHierDepth)
{
super(outGeom, maxHierDepth);
}
/**
* Overriding this class allows us to stop descension on unexpanded instances in "mimic" mode.
*/
public boolean visitNodeInst(Nodable no, HierarchyEnumerator.CellInfo info)
{
NodeInst ni = (NodeInst)no;
if (ni.isCellInstance())
{
if (!ni.isExpanded() && localPrefs.cifOutMimicsDisplay)
{
return false;
}
}
return super.visitNodeInst(no, info);
}
}
}