/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: GDS.java
* Input/output tool: GDS input
* Original C code written by Glen M. Lawson, S-MOS Systems, Inc.
* Translated into Java by Steven M. Rubin, Sun Microsystems.
*
* Copyright (c) 2004, 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.input;
import com.sun.electric.database.ImmutableExport;
import com.sun.electric.database.ImmutableNodeInst;
import com.sun.electric.database.geometry.EPoint;
import com.sun.electric.database.geometry.ERectangle;
import com.sun.electric.database.geometry.GeometryHandler;
import com.sun.electric.database.geometry.Poly;
import com.sun.electric.database.geometry.PolyBase;
import com.sun.electric.database.geometry.PolyMerge;
import com.sun.electric.database.geometry.PolySweepMerge;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.hierarchy.EDatabase;
import com.sun.electric.database.hierarchy.Export;
import com.sun.electric.database.hierarchy.Library;
import com.sun.electric.database.id.CellId;
import com.sun.electric.database.id.ExportId;
import com.sun.electric.database.id.PortProtoId;
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.Name;
import com.sun.electric.database.topology.Geometric;
import com.sun.electric.database.topology.NodeInst;
import com.sun.electric.database.topology.RTBounds;
import com.sun.electric.database.variable.ElectricObject;
import com.sun.electric.database.variable.MutableTextDescriptor;
import com.sun.electric.database.variable.TextDescriptor;
import com.sun.electric.technology.ArcProto;
import com.sun.electric.technology.Layer;
import com.sun.electric.technology.PrimitiveNode;
import com.sun.electric.technology.SizeOffset;
import com.sun.electric.technology.Technology;
import com.sun.electric.technology.Technology.NodeLayer;
import com.sun.electric.technology.technologies.Generic;
import com.sun.electric.tool.Job;
import com.sun.electric.tool.io.GDSLayers;
import com.sun.electric.tool.io.IOTool;
import com.sun.electric.tool.ncc.basic.NccCellAnnotations;
import com.sun.electric.util.TextUtils;
import com.sun.electric.util.math.DBMath;
import com.sun.electric.util.math.GenMath;
import com.sun.electric.util.math.GenMath.MutableInteger;
import com.sun.electric.util.math.Orientation;
import java.awt.Point;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
/**
* This class reads files in GDS files.
* <BR>
* Notes:
* <UL>
* <LI>Case sensitive.</LI>
* <LI>NODEs, TEXTNODEs, BOXs - don't have an example.</LI>
* <LI>PATHTYPE 1 - rounded ends on paths, not supported.</LI>
* <LI>Path dogears - no cleanup yet, simpler to map paths into arcs.</LI>
* <LI>Absolute angle - ???</LI>
* <LI>SUBLAYERS or XXXTYPE fields are not supported.</LI>
* <LI>PROPERTIES are not supported.</LI>
* <LI>REFLIBS are not supported.</LI>
* <LI>PATH-ARC mapping - should be done, problem is that any layer can be a path, only connection layers in Electric are arcs.
* Someone could make a GDS mapping technology for this purpose, defaults could be taken from this technology.</LI>
* <LI>Miscellaneous fields mapped to variables - should be done.</LI>
* <LI>MAG - no scaling is possible, must create a separate object for each value, don't scale. (TEXT does scale.)</LI>
* </UL>
*/
public class GDS extends Input<Object>
{
private static final boolean DEBUGALL = false; /* true for debugging */
private static final boolean SHOWPROGRESS = false; /* true for debugging */
// private static final boolean IGNOREIMMENSECELLS = false; /* true for debugging */
private static final boolean TALLYCONTENTS = false; /* true for debugging */
// data declarations
private static final int MAXPOINTS = 4096;
private static final int MINFONTWIDTH = 130;
private static final int MINFONTHEIGHT = 190;
private static class ShapeType {}
private static final ShapeType SHAPEPOLY = new ShapeType();
private static final ShapeType SHAPERECTANGLE = new ShapeType();
private static final ShapeType SHAPEOBLIQUE = new ShapeType();
private static final ShapeType SHAPELINE = new ShapeType();
private static final ShapeType SHAPECLOSED = new ShapeType();
private static class DatatypeSymbol {}
private static final DatatypeSymbol TYPEERR = new DatatypeSymbol();
private static final DatatypeSymbol TYPENONE = new DatatypeSymbol();
private static final DatatypeSymbol TYPEFLAGS = new DatatypeSymbol();
private static final DatatypeSymbol TYPESHORT = new DatatypeSymbol();
private static final DatatypeSymbol TYPELONG = new DatatypeSymbol();
private static final DatatypeSymbol TYPEFLOAT = new DatatypeSymbol();
private static final DatatypeSymbol TYPEDOUBLE = new DatatypeSymbol();
private static final DatatypeSymbol TYPESTRING = new DatatypeSymbol();
private final static double twoTo32 = makePower(2, 32);
private final static double twoToNeg56 = 1.0 / makePower (2, 56);
private int countBox, countText, countNode, countPath, countShape, countSRef, countARef, countATotal;
private Library theLibrary;
private Map<Library,Cell> currentCells;
private CellBuilder theCell;
private NodeProto theNodeProto;
private PrimitiveNode layerNodeProto;
private UnknownLayerMessage currentUnknownLayerMessage;
private PrimitiveNode pinNodeProto;
private int randomLayerSelection;
private boolean layerIsPin;
private Technology curTech;
private int recordCount;
private int curLayerNum, curLayerType;
private GSymbol theToken;
private DatatypeSymbol valuetype;
private int tokenFlags;
private int tokenValue16;
private int tokenValue32;
private double tokenValueDouble;
private String tokenString;
private Point2D [] theVertices;
private int numVertices;
private double theScale;
private Map<Integer,Layer> layerNames;
private Map<Integer,UnknownLayerMessage> layerErrorMessages;
private static Map<UnknownLayerMessage,Set<Cell>> cellLayerErrors;
private Set<Integer> pinLayers;
private PolyMerge merge;
private static boolean arraySimplificationUseful;
private Set<Cell> missingCells;
private static class GSymbol
{
private int value;
private static List<GSymbol> symbols = new ArrayList<GSymbol>();
private GSymbol(int value)
{
this.value = value;
symbols.add(this);
}
private static GSymbol findSymbol(int value)
{
for(GSymbol gs : symbols)
{
if (gs.value == value) return gs;
}
return null;
}
}
private static final GSymbol GDS_HEADER = new GSymbol(0);
private static final GSymbol GDS_BGNLIB = new GSymbol(1);
private static final GSymbol GDS_LIBNAME = new GSymbol(2);
private static final GSymbol GDS_UNITS = new GSymbol(3);
private static final GSymbol GDS_ENDLIB = new GSymbol(4);
private static final GSymbol GDS_BGNSTR = new GSymbol(5);
private static final GSymbol GDS_STRNAME = new GSymbol(6);
private static final GSymbol GDS_ENDSTR = new GSymbol(7);
private static final GSymbol GDS_BOUNDARY = new GSymbol(8);
private static final GSymbol GDS_PATH = new GSymbol(9);
private static final GSymbol GDS_SREF = new GSymbol(10);
private static final GSymbol GDS_AREF = new GSymbol(11);
private static final GSymbol GDS_TEXTSYM = new GSymbol(12);
private static final GSymbol GDS_LAYER = new GSymbol(13);
private static final GSymbol GDS_DATATYPSYM = new GSymbol(14);
private static final GSymbol GDS_WIDTH = new GSymbol(15);
private static final GSymbol GDS_XY = new GSymbol(16);
private static final GSymbol GDS_ENDEL = new GSymbol(17);
private static final GSymbol GDS_SNAME = new GSymbol(18);
private static final GSymbol GDS_COLROW = new GSymbol(19);
private static final GSymbol GDS_TEXTNODE = new GSymbol(20);
private static final GSymbol GDS_NODE = new GSymbol(21);
private static final GSymbol GDS_TEXTTYPE = new GSymbol(22);
private static final GSymbol GDS_PRESENTATION = new GSymbol(23);
// private static final GSymbol GDS_SPACING = new GSymbol(24);
private static final GSymbol GDS_STRING = new GSymbol(25);
private static final GSymbol GDS_STRANS = new GSymbol(26);
private static final GSymbol GDS_MAG = new GSymbol(27);
private static final GSymbol GDS_ANGLE = new GSymbol(28);
// private static final GSymbol GDS_UINTEGER = new GSymbol(29);
// private static final GSymbol GDS_USTRING = new GSymbol(30);
private static final GSymbol GDS_REFLIBS = new GSymbol(31);
private static final GSymbol GDS_FONTS = new GSymbol(32);
private static final GSymbol GDS_PATHTYPE = new GSymbol(33);
private static final GSymbol GDS_GENERATIONS = new GSymbol(34);
private static final GSymbol GDS_ATTRTABLE = new GSymbol(35);
// private static final GSymbol GDS_STYPTABLE = new GSymbol(36);
// private static final GSymbol GDS_STRTYPE = new GSymbol(37);
private static final GSymbol GDS_ELFLAGS = new GSymbol(38);
// private static final GSymbol GDS_ELKEY = new GSymbol(39);
// private static final GSymbol GDS_LINKTYPE = new GSymbol(40);
// private static final GSymbol GDS_LINKKEYS = new GSymbol(41);
private static final GSymbol GDS_NODETYPE = new GSymbol(42);
private static final GSymbol GDS_PROPATTR = new GSymbol(43);
private static final GSymbol GDS_PROPVALUE = new GSymbol(44);
private static final GSymbol GDS_BOX = new GSymbol(45);
private static final GSymbol GDS_BOXTYPE = new GSymbol(46);
private static final GSymbol GDS_PLEX = new GSymbol(47);
private static final GSymbol GDS_BGNEXTN = new GSymbol(48);
private static final GSymbol GDS_ENDEXTN = new GSymbol(49);
// private static final GSymbol GDS_TAPENUM = new GSymbol(50);
// private static final GSymbol GDS_TAPECODE = new GSymbol(51);
// private static final GSymbol GDS_STRCLASS = new GSymbol(52);
// private static final GSymbol GDS_NUMTYPES = new GSymbol(53);
private static final GSymbol GDS_IDENT = new GSymbol(54);
private static final GSymbol GDS_REALNUM = new GSymbol(55);
private static final GSymbol GDS_SHORT_NUMBER = new GSymbol(56);
private static final GSymbol GDS_NUMBER = new GSymbol(57);
private static final GSymbol GDS_FLAGSYM = new GSymbol(58);
private static final GSymbol GDS_FORMAT = new GSymbol(59);
private static final GSymbol GDS_MASK = new GSymbol(60);
private static final GSymbol GDS_ENDMASKS = new GSymbol(61);
private static GSymbol [] optionSet = {GDS_ATTRTABLE, GDS_REFLIBS, GDS_FONTS, GDS_GENERATIONS};
private static GSymbol [] shapeSet = {GDS_AREF, GDS_SREF, GDS_BOUNDARY, GDS_PATH, GDS_NODE, GDS_TEXTSYM, GDS_BOX};
private static GSymbol [] goodOpSet = {GDS_HEADER, GDS_BGNLIB, GDS_LIBNAME, GDS_UNITS, GDS_ENDLIB, GDS_BGNSTR, GDS_STRNAME,
GDS_ENDSTR, GDS_BOUNDARY, GDS_PATH, GDS_SREF, GDS_AREF, GDS_TEXTSYM, GDS_LAYER, GDS_DATATYPSYM, GDS_WIDTH, GDS_XY, GDS_ENDEL,
GDS_SNAME, GDS_COLROW, GDS_TEXTNODE, GDS_NODE, GDS_TEXTTYPE, GDS_PRESENTATION, GDS_STRING, GDS_STRANS, GDS_MAG, GDS_ANGLE,
GDS_REFLIBS, GDS_FONTS, GDS_PATHTYPE, GDS_GENERATIONS, GDS_ATTRTABLE, GDS_NODETYPE, GDS_PROPATTR, GDS_PROPVALUE, GDS_BOX,
GDS_BOXTYPE, GDS_FORMAT, GDS_MASK, GDS_ENDMASKS};
private static GSymbol [] maskSet = {GDS_DATATYPSYM, GDS_TEXTTYPE, GDS_BOXTYPE, GDS_NODETYPE};
private static GSymbol [] unsupportedSet = {GDS_ELFLAGS, GDS_PLEX};
private GDSPreferences localPrefs;
public static class GDSPreferences extends InputPreferences
{
public double inputScale;
public boolean simplifyCells;
public int arraySimplification;
public boolean instantiateArrays;
public boolean expandCells;
public boolean mergeBoxes;
public boolean includeText;
public int unknownLayerHandling;
public boolean cadenceCompatibility;
public GDSPreferences(boolean factory)
{
super(factory);
if (factory)
{
inputScale = IOTool.getFactoryGDSInputScale();
simplifyCells = IOTool.isFactoryGDSInSimplifyCells();
arraySimplification = IOTool.getFactoryGDSArraySimplification();
instantiateArrays = IOTool.isFactoryGDSInInstantiatesArrays();
expandCells = IOTool.isFactoryGDSInExpandsCells();
mergeBoxes = IOTool.isFactoryGDSInMergesBoxes();
includeText = IOTool.isFactoryGDSInIncludesText();
unknownLayerHandling = IOTool.getFactoryGDSInUnknownLayerHandling();
cadenceCompatibility = IOTool.isFactoryGDSCadenceCompatibility();
} else
{
inputScale = IOTool.getGDSInputScale();
simplifyCells = IOTool.isGDSInSimplifyCells();
arraySimplification = IOTool.getGDSArraySimplification();
instantiateArrays = IOTool.isGDSInInstantiatesArrays();
expandCells = IOTool.isGDSInExpandsCells();
mergeBoxes = IOTool.isGDSInMergesBoxes();
includeText = IOTool.isGDSInIncludesText();
unknownLayerHandling = IOTool.getGDSInUnknownLayerHandling();
cadenceCompatibility = IOTool.isGDSCadenceCompatibility();
}
}
@Override
public Library doInput(URL fileURL, Library lib, Technology tech, Map<Library,Cell> currentCells, Map<CellId,BitSet> nodesToExpand, Job job)
{
GDS in = new GDS(this);
if (in.openBinaryInput(fileURL)) return null;
// Libraries before loading
Set<Library> oldLibs = new HashSet<Library>();
for (Iterator<Library> it = Library.getLibraries(); it.hasNext(); )
oldLibs.add(it.next());
oldLibs.remove(lib);
lib = in.importALibrary(lib, tech, currentCells);
in.closeInput();
if (expandCells) {
// Expand subCells
EDatabase database = EDatabase.currentDatabase();
for (Iterator<Library> it = Library.getLibraries(); it.hasNext(); ) {
Library l = it.next();
if (oldLibs.contains(l)) continue;
for (Iterator<Cell> cit = l.getCells(); cit.hasNext(); ) {
Cell cell =cit.next();
for (Iterator<NodeInst> nit = cell.getNodes(); nit.hasNext(); ) {
NodeInst ni = nit.next();
if (ni.isCellInstance())
database.addToNodes(nodesToExpand, ni);
}
}
}
}
return lib;
}
}
/**
* Creates a new instance of GDS.
*/
GDS(GDSPreferences ap) { localPrefs = ap; }
/**
* Method to import a library from disk.
* @param lib the library to fill
* @param currentCells this map will be filled with currentCells in Libraries found in library file
* @return the created library (null on error).
*/
@Override
protected Library importALibrary(Library lib, Technology tech, Map<Library,Cell> currentCells)
{
// initialize
this.currentCells = currentCells;
arraySimplificationUseful = false;
CellBuilder.init();
theLibrary = lib;
curTech = tech;
initialize();
try
{
loadFile();
} catch (IllegalArgumentException e)
{
System.out.println("ERROR reading GDS file: " + e.getMessage());
if (Job.getDebug())
e.printStackTrace();
return null;
}
catch (Exception e)
{
System.out.println("ERROR reading GDS file: check input file, " + e.getMessage());
if (Job.getDebug())
e.printStackTrace();
return null;
}
// fix references to unknown cells that may be in other libraries
Map<Cell,Cell> foundCellMap = CellBuilder.substituteExternalCells(missingCells, theLibrary);
if (foundCellMap.size() > 0)
{
System.out.println("Note: these cells from other libraries were referenced in the GDS:");
for(Cell mCell : foundCellMap.keySet())
{
Cell found = foundCellMap.get(mCell);
System.out.println(" " + found.libDescribe());
missingCells.remove(mCell);
}
}
if (missingCells.size() > 0)
{
System.out.println("Note: these cells are missing in the GDS and were created with no contents:");
for(Cell cell : missingCells)
System.out.println(" " + cell.noLibDescribe());
}
// now build all instances recursively
CellBuilder.buildInstances();
CellBuilder.term();
// show unknown error messages
for(UnknownLayerMessage message : cellLayerErrors.keySet())
{
Set<Cell> cellList = cellLayerErrors.get(message);
System.out.println(message.message + " in cells:");
String prev = " ";
int count = 0;
for(Cell cell : cellList)
{
System.out.print(prev + cell.describe(false));
prev = ", ";
// break into lines otherwise the message line is too long
if (count > 10)
{
count = 0;
System.out.print("\n\t");
}
count++;
}
System.out.println();
}
if (arraySimplificationUseful)
{
System.out.println("NOTE: Found array references that could be simplified to save space and time");
System.out.println(" To simplify arrays, set the 'Input array simplification' in GDS Preferences");
}
return lib;
}
private void initialize()
{
layerNodeProto = Generic.tech().drcNode;
missingCells = new HashSet<Cell>();
theVertices = new Point2D[MAXPOINTS];
for(int i=0; i<MAXPOINTS; i++) theVertices[i] = new Point2D.Double();
recordCount = 0;
// get the array of GDS names
layerNames = new HashMap<Integer,Layer>();
layerErrorMessages = new HashMap<Integer,UnknownLayerMessage>();
cellLayerErrors = new HashMap<UnknownLayerMessage,Set<Cell>>();
pinLayers = new HashSet<Integer>();
randomLayerSelection = 0;
boolean valid = false;
for(Map.Entry<Layer,String> e: curTech.getGDSLayers().entrySet())
{
Layer layer = e.getKey();
String gdsName = e.getValue();
GDSLayers gdsl = GDSLayers.parseLayerString(gdsName);
for(Iterator<Integer> lIt = gdsl.getLayers(); lIt.hasNext(); ) {
Integer lVal = lIt.next();
Integer lay = new Integer(lVal.intValue());
if (layerNames.get(lay) == null)
layerNames.put(lay, layer);
}
if (gdsl.getPinLayer() != -1) {
pinLayers.add(new Integer(gdsl.getPinLayer()));
layerNames.put(new Integer(gdsl.getPinLayer()), layer);
}
if (gdsl.getTextLayer() != -1)
layerNames.put(new Integer(gdsl.getTextLayer()), layer);
valid = true;
}
if (!valid)
{
System.out.println("There are no GDS layer names assigned in the " + curTech.getTechName() + " technology");
}
}
private static class CellBuilder
{
private static Map<CellId,CellBuilder> allBuilders;
// private static Set<CellId> cellsTooComplex;
private GDSPreferences localPrefs;
private Technology tech;
private Cell cell;
private List<MakeInstance> insts = new ArrayList<MakeInstance>();
private Map<UnknownLayerMessage,List<MakeInstance>> allErrorInsts = new LinkedHashMap<UnknownLayerMessage,List<MakeInstance>>();
private List<MakeInstanceArray> instArrays = new ArrayList<MakeInstanceArray>();
private final TextDescriptor traceDescriptor;
private int nodeId;
private List<ImmutableNodeInst> nodesToCreate = new ArrayList<ImmutableNodeInst>();
private Map<String,ImmutableExport> exportsByName = new HashMap<String,ImmutableExport>();
private Set<String> alreadyExports = exportsByName.keySet();
private Map<String,GenMath.MutableInteger> nextExportPlainIndex = new HashMap<String,GenMath.MutableInteger>();
private Map<String,GenMath.MutableInteger> maxSuffixes = new HashMap<String,GenMath.MutableInteger>();
private Set<String> userNames = new HashSet<String>();
private MutableInteger count = new MutableInteger(0);
private CellBuilder(Cell cell, Technology tech, GDSPreferences localPrefs) {
this.cell = cell;
this.tech = tech;
this.localPrefs = localPrefs;
traceDescriptor = cell.getEditingPreferences().getTextDescriptor(TextDescriptor.TextType.NODE, false);
allBuilders.put(cell.getId(), this);
// sanity
Set<Export> exportsToKill = new HashSet<Export>();
for (Iterator<Export> it = cell.getExports(); it.hasNext(); )
exportsToKill.add(it.next());
cell.killExports(exportsToKill);
}
private void makeInstance(NodeProto proto, Point2D loc, Orientation orient, double wid, double hei,
EPoint[] points, UnknownLayerMessage ulm)
{
MakeInstance mi = null;
if (proto != null)
{
mi = new MakeInstance(this, proto, loc, orient, wid, hei, points, null, null);
insts.add(mi);
if (ulm != null)
{
List<MakeInstance> errorList = allErrorInsts.get(ulm);
if (errorList == null) allErrorInsts.put(ulm, errorList = new ArrayList<MakeInstance>());
errorList.add(mi);
}
}
}
private void makeInstanceArray(NodeProto proto, int nCols, int nRows, Orientation orient,
Point2D startLoc, Point2D rowOffset, Point2D colOffset)
{
MakeInstanceArray mia = new MakeInstanceArray(proto, nCols, nRows, orient,
new Point2D.Double(startLoc.getX(), startLoc.getY()), rowOffset, colOffset, localPrefs);
instArrays.add(mia);
}
private void makeExport(NodeProto proto, Point2D loc, Orientation orient,
String exportName, UnknownLayerMessage ulm)
{
MakeInstance mi = null;
if (proto != null && proto.getNumPorts() > 0)
{
double wid = proto.getDefWidth();
double hei = proto.getDefHeight();
mi = new MakeInstance(this, proto, loc, orient, wid, hei, null, exportName, null);
insts.add(mi);
if (ulm != null)
{
List<MakeInstance> errorList = allErrorInsts.get(ulm);
if (errorList == null) allErrorInsts.put(ulm, errorList = new ArrayList<MakeInstance>());
errorList.add(mi);
}
}
}
private void makeText(NodeProto proto, Point2D loc, String text,
TextDescriptor textDescriptor, UnknownLayerMessage ulm)
{
MakeInstance mi = null;
if (proto != null)
{
mi = new MakeInstance(this, proto, loc, Orientation.IDENT, 0, 0, null, null, Name.findName(text));
insts.add(mi);
if (ulm != null)
{
List<MakeInstance> errorList = allErrorInsts.get(ulm);
if (errorList == null) allErrorInsts.put(ulm, errorList = new ArrayList<MakeInstance>());
errorList.add(mi);
}
}
}
private static void init()
{
allBuilders = new HashMap<CellId,CellBuilder>();
// cellsTooComplex = new HashSet<CellId>();
}
private static void term()
{
allBuilders = null;
// if (cellsTooComplex.size() > 0)
// {
// System.out.print("THESE CELLS WERE TOO COMPLEX AND NOT FULLY READ:");
// for(CellId cellId : cellsTooComplex) System.out.print(" " + cellId/*.describe(false)*/);
// System.out.println();
// }
}
private void makeInstances(Set<CellId> builtCells)
{
if (builtCells.contains(cell.getId())) return;
builtCells.add(cell.getId());
// Traverse all subcells
for(MakeInstance mi : insts)
{
if (mi.proto instanceof Cell) {
Cell subCell = (Cell)mi.proto;
CellBuilder cellBuilder = allBuilders.get(subCell.getId());
if (cellBuilder != null)
cellBuilder.makeInstances(builtCells);
}
}
// for(List<MakeInstance> errorList: allErrorInsts.values())
// {
// for(MakeInstance mi : errorList)
// {
// if (mi == null) continue;
// if (mi.proto instanceof Cell) {
// Cell subCell = (Cell)mi.proto;
// CellBuilder cellBuilder = allBuilders.get(subCell.getId());
// if (cellBuilder != null)
// cellBuilder.makeInstances(builtCells);
// }
// }
// }
for(MakeInstanceArray mia : instArrays)
{
if (mia.proto instanceof Cell) {
Cell subCell = (Cell)mia.proto;
CellBuilder cellBuilder = allBuilders.get(subCell.getId());
if (cellBuilder != null)
cellBuilder.makeInstances(builtCells);
}
}
makeInstances();
assert builtCells.contains(this.cell.getId());
}
private void makeInstances()
{
boolean countOff = false;
if (SHOWPROGRESS /*|| IGNOREIMMENSECELLS*/)
{
int size = insts.size();
int arraySize = instArrays.size();
System.out.println("Building cell " + this.cell.describe(false) +
" with " + size + " single instances and " + arraySize + " arrayed instances");
if (size+arraySize >= 100000)
{
countOff = true;
// ignore internal contents when cell is very large (for testing only)
// if (IGNOREIMMENSECELLS)
// {
// cellsTooComplex.add(cell.getId());
// MakeInstance ll = null, ul = null, lr = null, ur = null;
// for(MakeInstance mi : insts)
// {
// if (ll == null) ll = ul = lr = ur = mi;
// if (mi.loc.getX() <= ll.loc.getX() && mi.loc.getY() <= ll.loc.getY()) ll = mi;
// if (mi.loc.getX() <= ul.loc.getX() && mi.loc.getY() >= ul.loc.getY()) ul = mi;
// if (mi.loc.getX() >= lr.loc.getX() && mi.loc.getY() <= lr.loc.getY()) lr = mi;
// if (mi.loc.getX() >= ur.loc.getX() && mi.loc.getY() >= ur.loc.getY()) ur = mi;
// }
// insts.clear();
// instArrays.clear();
// insts.add(ll);
// if (!insts.contains(ul)) insts.add(ul);
// if (!insts.contains(lr)) insts.add(lr);
// if (!insts.contains(ur)) insts.add(ur);
// }
}
}
int count = 0;
Map<String,String> exportUnify = new HashMap<String,String>();
// first make the geometry and instances
for(MakeInstance mi : insts)
{
if (mi.exportName != null) continue;
if (countOff && ((++count % 1000) == 0))
System.out.println(" Made " + count + " instances");
// make the instance
mi.instantiate(this, exportUnify, null);
}
createNodes();
// next make the exports
for(MakeInstance mi : insts)
{
if (mi.exportName == null) continue;
if (countOff && ((++count % 1000) == 0))
System.out.println(" Made " + count + " instances");
if (localPrefs.cadenceCompatibility)
{
// center the export if possible
ArcProto theArc = mi.proto.getPort(0).getBasePort().getConnections()[0];
Layer theLayer = theArc.getLayer(0);
NodeProto theProto = theLayer.getPureLayerNode();
Rectangle2D search = new Rectangle2D.Double(mi.loc.getX(), mi.loc.getY(), 0, 0);
for(Iterator<RTBounds> it = this.cell.searchIterator(search); it.hasNext(); )
{
Geometric geom = (Geometric)it.next();
if (geom instanceof NodeInst)
{
NodeInst ni = (NodeInst)geom;
if (ni.getProto() != theProto) continue;
Rectangle2D pointBounds = ni.getBounds();
double cX = pointBounds.getCenterX();
double cY = pointBounds.getCenterY();
EPoint [] trace = ni.getTrace();
if (trace != null)
{
Point2D [] newPoints = new Point2D[trace.length];
for(int i=0; i<trace.length; i++)
{
if (trace[i] != null)
newPoints[i] = new Point2D.Double(trace[i].getX()+cX, trace[i].getY()+cY);
}
PolyBase poly = new PolyBase(newPoints);
poly.transform(ni.rotateOut());
if (poly.contains(mi.loc))
{
GeometryHandler thisMerge = GeometryHandler.createGeometryHandler(GeometryHandler.GHMode.ALGO_SWEEP, 1);
thisMerge.add(theLayer, poly);
thisMerge.postProcess(true);
Collection<PolyBase> set = ((PolySweepMerge)thisMerge).getPolyPartition(theLayer);
for(PolyBase simplePoly : set)
{
Rectangle2D polyBounds = simplePoly.getBounds2D();
if (polyBounds.contains(mi.loc))
{
mi.loc.setLocation(polyBounds.getCenterX(), polyBounds.getCenterY());
break;
}
}
break;
}
} else
{
if (pointBounds.contains(mi.loc))
{
mi.loc.setLocation(cX, cY);
break;
}
}
}
}
// grid-align the export location
double scaledResolution = tech.getFactoryScaledResolution();
if (scaledResolution > 0)
{
double x = Math.round(mi.loc.getX() / scaledResolution) * scaledResolution;
double y = Math.round(mi.loc.getY() / scaledResolution) * scaledResolution;
mi.loc.setLocation(x, y);
}
}
// make the instance
mi.instantiate(this, exportUnify, null);
}
for(UnknownLayerMessage ulm : allErrorInsts.keySet())
{
List<ImmutableNodeInst> instantiated = new ArrayList<ImmutableNodeInst>();
List<MakeInstance> errorList = allErrorInsts.get(ulm);
for(MakeInstance mi : errorList)
{
if (mi == null) continue;
if (countOff && ((++count % 1000) == 0))
System.out.println(" Made " + count + " instances");
// make the instance
instantiated.add(mi.n);
// mi.instantiate(this, exportUnify, instantiated);
}
String msg = "Cell " + this.cell.noLibDescribe() + ": " + ulm.message;
Set<Cell> cellsWithError = cellLayerErrors.get(ulm);
if (cellsWithError == null) cellLayerErrors.put(ulm, cellsWithError = new TreeSet<Cell>());
cellsWithError.add(cell);
errorLogger.logMessage(msg, instantiated, cell, -1, true);
}
createNodes();
Map<NodeProto,List<EPoint>> massiveMerge = new HashMap<NodeProto,List<EPoint>>();
for(MakeInstanceArray mia : instArrays)
{
if (countOff && ((++count % 1000) == 0))
System.out.println(" Made " + count + " instances");
// make the instance array
mia.instantiate(this, massiveMerge);
}
List<NodeProto> mergeNodeSet = new ArrayList<NodeProto>();
for(NodeProto np : massiveMerge.keySet()) mergeNodeSet.add(np);
for(NodeProto np : mergeNodeSet)
{
// place a pure-layer node that embodies all arrays for the whole cell
List<EPoint> points = massiveMerge.get(np);
buildComplexNode(points, np, this.cell);
}
cell.addExports(exportsByName.values());
if (!exportUnify.isEmpty())
{
System.out.println("Cell " + this.cell.describe(false) + ": Renamed and NCC-unified " + exportUnify.size() +
" exports with duplicate names");
Map<String,String> unifyStrings = new HashMap<String,String>();
Set<String> finalNames = exportUnify.keySet();
for(String finalName : finalNames)
{
String singleName = exportUnify.get(finalName);
String us = unifyStrings.get(singleName);
if (us == null) us = singleName;
us += " " + finalName;
unifyStrings.put(singleName, us);
}
List<String> annotations = new ArrayList<String>();
for(String us : unifyStrings.keySet())
annotations.add("exportsConnectedByParent " + unifyStrings.get(us));
if (annotations.size() > 0)
{
String [] anArr = new String[annotations.size()];
for(int i=0; i<annotations.size(); i++) anArr[i] = annotations.get(i);
TextDescriptor td = TextDescriptor.getCellTextDescriptor().withInterior(true).withDispPart(TextDescriptor.DispPos.NAMEVALUE);
this.cell.newVar(NccCellAnnotations.NCC_ANNOTATION_KEY, anArr, td);
}
}
if (localPrefs.simplifyCells)
simplifyNodes(this.cell, tech);
}
private void createNodes() {
cell.addNodes(nodesToCreate);
nodesToCreate.clear();
}
/** Method to see if existing primitive nodesToCreate could be merged and define more complex nodesToCreate
* such as contacts
*/
private void simplifyNodes(Cell cell, Technology tech)
{
Map<Layer, List<NodeInst>> map = new HashMap<Layer, List<NodeInst>>();
for (Iterator<NodeInst> itNi = cell.getNodes(); itNi.hasNext();)
{
NodeInst ni = itNi.next();
if (!(ni.getProto() instanceof PrimitiveNode)) continue; // not primitive
PrimitiveNode pn = (PrimitiveNode)ni.getProto();
if (pn.getFunction() != PrimitiveNode.Function.NODE) continue; // not pure layer node.
Layer layer = pn.getLayerIterator().next(); // they are supposed to have only 1
List<NodeInst> list = map.get(layer);
if (list == null) // first time
{
list = new ArrayList<NodeInst>();
map.put(layer, list);
}
list.add(ni);
}
Set<NodeInst> toDelete = new HashSet<NodeInst>();
Set<NodeInst> viaToDelete = new HashSet<NodeInst>();
List<Geometric> geomList = new ArrayList<Geometric>();
for (Iterator<PrimitiveNode> itPn = tech.getNodes(); itPn.hasNext();)
{
PrimitiveNode pn = itPn.next();
boolean allFound = true;
if (!pn.getFunction().isContact()) continue; // only dealing with metal contacts for now.
Layer m1Layer = null, m2Layer = null;
Layer viaLayer = null;
SizeOffset so = pn.getProtoSizeOffset();
for (Iterator<Layer> itLa = pn.getLayerIterator(); itLa.hasNext();)
{
Layer l = itLa.next();
if (map.get(l) == null)
{
allFound = false;
break;
}
if (l.getFunction().isMetal())
{
if (m1Layer == null)
m1Layer = l;
else
m2Layer = l;
}
else if (l.getFunction().isContact())
viaLayer = l;
}
if (!allFound) continue; // not all layers for this particular node found
if (viaLayer == null) continue; // not metal contact
assert(m1Layer != null);
List<NodeInst> list = map.get(m1Layer);
assert(list != null);
Layer.Function.Set thisLayer = new Layer.Function.Set(viaLayer.getFunction());
List<NodeInst> viasList = map.get(viaLayer);
for (NodeInst ni : list)
{
Poly[] polys = tech.getShapeOfNode(ni, true, false, null);
assert(polys.length == 1); // it must be only 1
Poly m1P = polys[0];
List<NodeInst> nList = map.get(m2Layer);
if (nList == null) continue; // nothing found in m2Layer
for (NodeInst n : nList)
{
Poly[] otherPolys = tech.getShapeOfNode(n, true, false, null);
assert(otherPolys.length == 1); // it must be only 1
Poly m2P = otherPolys[0];
if (!m2P.getBounds2D().equals(m1P.getBounds2D())) continue; // no match
ImmutableNodeInst d = ni.getD();
String name = ni.getName();
int atIndex = name.indexOf('@');
if (atIndex < 0) name += "tmp"; else
name = name.substring(0, atIndex) + "tmp" + name.substring(atIndex);
NodeInst newNi = NodeInst.makeInstance(pn, d.anchor,
m2P.getBounds2D().getWidth() + so.getLowXOffset() + so.getHighXOffset(),
m2P.getBounds2D().getHeight() + so.getLowYOffset() + so.getHighYOffset(),
ni.getParent(), ni.getOrient(), name);
if (newNi == null) continue;
// Searching for vias to delete
assert(viasList != null);
Poly[] viaPolys = tech.getShapeOfNode(newNi, true, false, thisLayer);
boolean found = false;
// Can be more than 1 due to MxN cuts
viaToDelete.clear();
for (int i = 0; i < viaPolys.length; i++)
{
Poly poly = viaPolys[i];
Rectangle2D bb = poly.getBounds2D();
bb.setRect(ERectangle.fromLambda(bb));
found = false;
for (NodeInst viaNi : viasList)
{
Poly[] thisViaList = tech.getShapeOfNode(viaNi, true, false, thisLayer);
assert(thisViaList.length == 1);
// hack to get rid of the resolution issue
Poly p = thisViaList[0];
Rectangle2D b = p.getBounds2D();
b.setRect(ERectangle.fromLambda(b));
if (thisViaList[0].polySame(poly))
{
viaToDelete.add(viaNi);
assert(!found);
found = true;
}
}
if (!found)
{
break; // fail to find all nodesToCreate
}
}
if (!found) // rolling back new node
{
newNi.kill();
}
else
{
if (SHOWPROGRESS)
System.out.println("Adding " + newNi.getName());
toDelete.clear();
geomList.clear();
toDelete.add(ni);
toDelete.add(n);
toDelete.addAll(viaToDelete);
String message = toDelete.size() + " nodes were replaced for more complex primitives in cell '" + cell.getName() + "'";
geomList.add(newNi);
errorLogger.logMessage(message, geomList, cell, -1, false);
// Deleting now replaced pure primitives
cell.killNodes(toDelete);
}
}
}
}
}
/**
* Method to find missing cells in the GDS and substitute references to cells in other libraries.
* @param missingCells
* @param theLibrary
* @return
*/
private static Map<Cell,Cell> substituteExternalCells(Set<Cell> missingCells, Library theLibrary)
{
Map<Cell,Cell> missingCellMap = new HashMap<Cell,Cell>();
// make a list of other libraries to scan
List<Library> otherLibraries = new ArrayList<Library>();
for(Library lib : Library.getVisibleLibraries())
{
if (lib != theLibrary) otherLibraries.add(lib);
}
if (otherLibraries.size() == 0) return missingCellMap;
// first map missing cells to found ones
for(Cell mCell : missingCells)
{
for(Library lib : otherLibraries)
{
Cell found = lib.findNodeProto(mCell.getName());
if (found != null)
{
missingCellMap.put(mCell, found);
mCell.kill();
break;
}
}
}
for(CellBuilder cellBuilder : allBuilders.values())
{
for(MakeInstance mi : cellBuilder.insts)
{
if (mi.proto instanceof Cell)
{
Cell found = missingCellMap.get(mi.proto);
if (found != null) mi.proto = found;
}
}
for(MakeInstanceArray mia : cellBuilder.instArrays)
{
if (mia.proto instanceof Cell)
{
Cell found = missingCellMap.get(mia.proto);
if (found != null) mia.proto = found;
}
}
}
return missingCellMap;
}
private static void buildInstances()
{
Set<CellId> builtCells = new HashSet<CellId>();
for(CellBuilder cellBuilder : allBuilders.values())
cellBuilder.makeInstances(builtCells);
}
}
/**
* Class to save instance array information.
*/
private static class MakeInstanceArray
{
private NodeProto proto;
private int nCols, nRows;
private Orientation orient;
private Point2D startLoc, rowOffset, colOffset;
private GDSPreferences localPrefs;
private MakeInstanceArray(NodeProto proto, int nCols, int nRows, Orientation orient, Point2D startLoc,
Point2D rowOffset, Point2D colOffset, GDSPreferences localPrefs)
{
this.proto = proto;
this.nCols = nCols;
this.nRows = nRows;
this.orient = orient;
this.startLoc = startLoc;
this.rowOffset = rowOffset;
this.colOffset = colOffset;
this.localPrefs = localPrefs;
}
/**
* Method to instantiate an array of cell instances.
* @param parent the Cell in which to create the geometry.
*/
private void instantiate(CellBuilder theCell, Map<NodeProto,List<EPoint>> massiveMerge)
{
Cell parent = theCell.cell;
int arraySimplification = localPrefs.arraySimplification;
NodeInst subNi = null;
Cell subCell = (Cell)proto;
int numArcs = subCell.getNumArcs();
int numNodes = subCell.getNumNodes();
int numExports = subCell.getNumPorts();
if (numArcs == 0 && numExports == 0 && numNodes == 1)
{
subNi = subCell.getNode(0);
if (subNi.getProto().getFunction() != PrimitiveNode.Function.NODE)
subNi = null;
}
if (subNi != null && subNi.getTrace() != null) subNi = null;
if (subNi != null)
{
if (arraySimplification > 0)
{
List<EPoint> points = buildArray();
if (arraySimplification == 2)
{
// add the array's geometry the layer's outline
List<EPoint> soFar = massiveMerge.get(subNi.getProto());
if (soFar == null)
{
soFar = new ArrayList<EPoint>();
massiveMerge.put(subNi.getProto(), soFar);
}
if (soFar.size() > 0) soFar.add(null);
for(EPoint ep : points) soFar.add(ep);
} else
{
// place a pure-layer node that embodies the array
buildComplexNode(points, subNi.getProto(), parent);
}
return;
} else
{
// remember that array simplification would have helped
arraySimplificationUseful = true;
}
}
// generate an array
double ptcX = startLoc.getX();
double ptcY = startLoc.getY();
for (int ic = 0; ic < nCols; ic++)
{
double ptX = ptcX;
double ptY = ptcY;
for (int ir = 0; ir < nRows; ir++)
{
// create the node
if (localPrefs.instantiateArrays ||
(ir == 0 && ic == 0) ||
(ir == (nRows-1) && ic == (nCols-1)))
{
Point2D loc = new Point2D.Double(ptX, ptY);
/* NodeInst ni = */ NodeInst.makeInstance(proto, loc, proto.getDefWidth(), proto.getDefHeight(), parent, orient, null);
// if (ni != null)
// {
// if (localPrefs.expandCells && ni.isCellInstance())
// ni.setExpanded(true);
// }
}
// add the row displacement
ptX += rowOffset.getX(); ptY += rowOffset.getY();
}
// add displacement
ptcX += colOffset.getX(); ptcY += colOffset.getY();
}
}
private List<EPoint> buildArray()
{
List<EPoint> points = new ArrayList<EPoint>();
Rectangle2D bounds = ((Cell)proto).getBounds();
Rectangle2D boundCopy = new Rectangle2D.Double(bounds.getMinX(), bounds.getMinY(), bounds.getWidth(), bounds.getHeight());
DBMath.transformRect(boundCopy, orient.pureRotate());
double ptcX = startLoc.getX();
double ptcY = startLoc.getY();
for (int ic = 0; ic < nCols; ic++)
{
double ptX = ptcX;
double ptY = ptcY;
for (int ir = 0; ir < nRows; ir++)
{
points.add(new EPoint(ptX+boundCopy.getMinX(), ptY+boundCopy.getMinY()));
points.add(new EPoint(ptX+boundCopy.getMaxX(), ptY+boundCopy.getMinY()));
points.add(new EPoint(ptX+boundCopy.getMaxX(), ptY+boundCopy.getMaxY()));
points.add(new EPoint(ptX+boundCopy.getMinX(), ptY+boundCopy.getMaxY()));
// insert a "break" marker to start a new polygon
if (ic < nCols-1 || ir < nRows-1) points.add(null);
// add the row displacement
ptX += rowOffset.getX(); ptY += rowOffset.getY();
}
// add displacement
ptcX += colOffset.getX(); ptcY += colOffset.getY();
}
return points;
}
}
private static class MakeInstance
{
private NodeProto proto;
private final Point2D loc;
private final Orientation orient;
private final double wid, hei;
private final EPoint[] points; // trace
private String exportName; // export
private final Name nodeName; // text
private String origNodeName; // original text with invalid name
private ImmutableNodeInst n;
private MakeInstance(CellBuilder cb, NodeProto proto, Point2D loc, Orientation orient, double wid, double hei, EPoint[] points,
String exportName, Name nodeName)
{
this.proto = proto;
this.loc = loc;
this.orient = orient;
this.wid = DBMath.round(wid);
this.hei = DBMath.round(hei);
this.points = points;
this.exportName = exportName;
if (nodeName != null && !nodeName.isValid())
{
origNodeName = nodeName.toString();
if (origNodeName.equals("[@instanceName]"))
{
origNodeName = null;
nodeName = null;
} else if (origNodeName.endsWith(":"))
{
nodeName = Name.findName(origNodeName.substring(0, origNodeName.length()-1));
if (nodeName.isValid()) origNodeName = null;
} else
{
nodeName = null;
}
}
cb.count.increment();
// if (countOff && ((count.intValue() % 2000) == 0))
// System.out.println(" Named " + count + " instances");
if (nodeName != null)
{
if (!validGdsNodeName(nodeName))
{
System.out.println(" Warning: Node name '" + nodeName + "' in cell " + cb.cell.describe(false) +
" is bad (" + Name.checkName(nodeName.toString()) + ")...ignoring the name");
} else if (!cb.userNames.contains(nodeName.toString()))
{
cb.userNames.add(nodeName.toString());
this.nodeName = nodeName;
return;
}
}
Name baseName;
if (proto instanceof Cell) baseName = ((Cell)proto).getBasename(); else
{
PrimitiveNode np = (PrimitiveNode)proto;
baseName = np.getFunction().getBasename();
}
String basenameString = baseName.toString();
GenMath.MutableInteger maxSuffix = cb.maxSuffixes.get(basenameString);
if (maxSuffix == null)
{
maxSuffix = new GenMath.MutableInteger(-1);
cb.maxSuffixes.put(basenameString, maxSuffix);
}
maxSuffix.increment();
this.nodeName = baseName.findSuffixed(maxSuffix.intValue());
}
private boolean validGdsNodeName(Name name)
{
return name.isValid() && !name.hasEmptySubnames() && !name.isBus() || !name.isTempname();
}
/**
* Method to instantiate a node/export in a Cell.
* @param parent the Cell in which to create the geometry.
* @param exportUnify a map that shows how renamed exports connect.
* @param saveHere a list of ImmutableNodeInst to save this instance in.
* @return true if the export had to be renamed.
*/
private void instantiate(CellBuilder cb, Map<String,String> exportUnify, List<ImmutableNodeInst> saveHere)
{
Cell parent = cb.cell;
assert parent.isLinked();
// search for spare nodeId
int nodeId = cb.nodeId++;
assert nodeName != null;
String name = nodeName.toString();
assert parent.findNode(name) == null;
assert !NodeInst.checkNameKey(nodeName, parent) && !nodeName.isBus();
TextDescriptor nameDescriptor = TextDescriptor.getNodeTextDescriptor();
EPoint anchor = EPoint.snap(loc);
EPoint size = EPoint.ORIGIN;
if (proto instanceof PrimitiveNode) {
ERectangle full = ((PrimitiveNode) proto).getFullRectangle();
long gridWidth = DBMath.lambdaToSizeGrid(wid - full.getLambdaWidth());
long gridHeight = DBMath.lambdaToSizeGrid(hei - full.getLambdaHeight());
size = EPoint.fromGrid(gridWidth, gridHeight);
} else {
assert ((Cell)proto).isLinked();
}
int flags = 0;
int techBits = 0;
TextDescriptor protoDescriptor = TextDescriptor.getInstanceTextDescriptor();
n = ImmutableNodeInst.newInstance(nodeId, proto.getId(), nodeName, nameDescriptor,
orient, anchor, size, flags, techBits, protoDescriptor);
if (points != null && GenMath.getAreaOfPoints(points) != wid*hei) {
n = n.withTrace(points, cb.traceDescriptor);
}
cb.nodesToCreate.add(n);
String errorMsg = null;
if (saveHere != null) saveHere.add(n);
if (origNodeName != null)
{
errorMsg = "Cell " + parent.describe(false) + ": Original GDS name of '" + name + "' was '" + origNodeName + "'";
}
if (errorMsg != null)
{
errorLogger.logMessage(errorMsg, Collections.singleton(n), parent, -1, false);
System.out.println(errorMsg);
}
if (exportName != null)
{
if (exportName.endsWith(":"))
exportName = exportName.substring(0, exportName.length()-1);
exportName = exportName.replace(':', '_');
if (cb.alreadyExports.contains(exportName))
{
String newName = ElectricObject.uniqueObjectName(exportName, parent, Export.class,
cb.alreadyExports, cb.nextExportPlainIndex, true, true);
// while (exportNames.contains(newName))
// {
// int lastUnder = newName.lastIndexOf('_');
// if (lastUnder > 0 && TextUtils.isANumber(newName.substring(lastUnder+1)))
// {
// newName = newName.substring(0, lastUnder+1) + (TextUtils.atoi(newName.substring(lastUnder+1))+1);
// } else
// {
// newName += "_1";
// }
// }
//System.out.println(" Warning: Multiple exports called '" + exportName + "' in cell " +
// parent.describe(false) + " (renamed to " + newName + ")");
exportUnify.put(newName, exportName);
exportName = newName;
}
// Create ImmutableExport
ExportId exportId = parent.getD().cellId.newPortId(exportName);
boolean busNamesAllowed = false;
Name exportNameKey = ImmutableExport.validExportName(exportName, busNamesAllowed);
if (exportNameKey == null)
{
String newName = Export.repairExportName(parent, exportName);
if (newName == null) newName = Export.repairExportName(parent, "X");
if (newName != null)
exportNameKey = ImmutableExport.validExportName(newName, busNamesAllowed);
if (exportNameKey == null)
{
System.out.print("Error: Pin '" + exportName + "' is invalid");
if (newName != null)
System.out.print(" and alternate name, '" + newName + "' cannot be used");
System.out.println(". Pin ignored.");
return;
}
System.out.println("Warning: Pin '" + exportName + "' is invalid and was renamed to '" + newName + "'");
exportName = newName;
}
TextDescriptor nameTextDescriptor = TextDescriptor.getExportTextDescriptor();
PortProtoId portProtoId = proto.getPort(0).getId();
boolean alwaysDrawn = false;
boolean bodyOnly = false;
assert parent.findExport(exportName) == null;
PortCharacteristic characteristic = PortCharacteristic.UNKNOWN;
ImmutableExport d = ImmutableExport.newInstance(exportId, exportNameKey, nameTextDescriptor,
nodeId, portProtoId, alwaysDrawn, bodyOnly, characteristic);
// Put ImmutableExport in the CellBuiler.
// This also modifies the cb.alreadyExports
cb.exportsByName.put(exportName, d);
}
}
}
private void loadFile()
throws IOException
{
getToken();
readHeader();
getToken();
readLibrary();
getToken();
while (isMember(theToken, optionSet))
{
if (theToken == GDS_REFLIBS) readRefLibs(); else
if (theToken == GDS_FONTS) readFonts(); else
if (theToken == GDS_ATTRTABLE) readAttrTable(); else
if (theToken == GDS_GENERATIONS) readGenerations();
}
while (theToken != GDS_UNITS)
getToken();
readUnits();
getToken();
while (theToken != GDS_ENDLIB)
{
readStructure();
getToken();
}
}
private void readHeader()
throws IOException
{
if (theToken != GDS_HEADER) handleError("GDS II header statement is missing");
getToken();
if (theToken != GDS_SHORT_NUMBER) handleError("GDS II version number is not decipherable");
// version "tokenValue16"
}
private void readLibrary()
throws IOException
{
if (theToken != GDS_BGNLIB) handleError("Begin library statement is missing");
getToken();
determineTime(); // creation time
determineTime(); // modification time
if (theToken == GDS_LIBNAME)
{
getToken();
if (theToken != GDS_IDENT) handleError("Library name is missing");
}
}
private void readRefLibs()
throws IOException
{
getToken();
getToken();
}
private void readFonts()
throws IOException
{
getToken();
getToken();
}
private void readAttrTable()
throws IOException
{
getToken();
if (theToken == GDS_IDENT)
{
getToken();
}
}
private void readUnits()
throws IOException
{
if (theToken != GDS_UNITS) handleError("Units statement is missing");
getToken();
if (theToken != GDS_REALNUM) handleError("Units statement has invalid number format");
// get the meter unit
getToken();
double meterUnit = tokenValueDouble;
// round the meter unit
double shift = 1;
double roundedScale = meterUnit;
while (roundedScale < 1)
{
roundedScale *= 10;
shift *= 10;
}
roundedScale = DBMath.round(roundedScale) / shift;
meterUnit = roundedScale;
// compute the scale
double microScale = TextUtils.convertFromDistance(1, curTech, TextUtils.UnitScale.MICRO);
theScale = meterUnit * 1000000.0 * microScale * localPrefs.inputScale;
// // round the scale
// double shift = 1;
// double roundedScale = theScale;
// while (roundedScale < 1)
// {
// roundedScale *= 10;
// shift *= 10;
// }
// roundedScale = DBMath.round(roundedScale) / shift;
// theScale = roundedScale;
}
private double scaleValue(double value)
{
double result = value * theScale;
return result;
}
private void showResultsOfCell()
{
System.out.print("**** Cell "+theCell.cell.describe(false)+" has");
if (countBox > 0) System.out.print(" "+countBox+" boxes");
if (countText > 0) System.out.print(" "+countText+" texts");
if (countNode > 0) System.out.print(" "+countNode+" nodes");
if (countPath > 0) System.out.print(" "+countPath+" paths");
if (countShape > 0) System.out.print(" "+countShape+" shapes");
if (countSRef > 0) System.out.print(" "+countSRef+" instances");
if (countARef > 0)
System.out.print(" "+countARef+" arrays with "+countATotal+" elements");
System.out.println();
}
private void readStructure()
throws IOException
{
beginStructure();
getToken();
if (localPrefs.mergeBoxes)
{
// initialize merge if merging this cell
merge = new PolyMerge();
}
// read the cell
countBox = countText = countNode = countPath = countShape = countSRef = countARef = countATotal = 0;
while (theToken != GDS_ENDSTR)
{
getElement();
getToken();
}
if (TALLYCONTENTS) showResultsOfCell();
if (localPrefs.mergeBoxes)
{
// extract merge information for this cell
for(Layer layer : merge.getKeySet())
{
Layer primLayer = layer;
PrimitiveNode pnp = primLayer.getPureLayerNode();
List<PolyBase> polys = merge.getMergedPoints(layer, false);
for(PolyBase poly : polys)
{
Rectangle2D box = poly.getBox();
if (box == null)
{
box = poly.getBounds2D();
Point2D ctr = new EPoint(box.getCenterX(), box.getCenterY());
// store the trace information
Point2D [] pPoints = poly.getPoints();
EPoint [] points = new EPoint[pPoints.length];
for(int i=0; i<pPoints.length; i++)
{
points[i] = new EPoint(pPoints[i].getX(), pPoints[i].getY());
}
// store the trace information
theCell.makeInstance(pnp, ctr, Orientation.IDENT, box.getWidth(), box.getHeight(), points, null);
} else
{
Point2D ctr = new EPoint(box.getCenterX(), box.getCenterY());
theCell.makeInstance(pnp, ctr, Orientation.IDENT, box.getWidth(), box.getHeight(), null, null);
}
}
}
}
}
private void beginStructure()
throws IOException
{
if (theToken != GDS_BGNSTR) handleError("Begin structure statement is missing");
getToken();
determineTime(); // creation time
determineTime(); // modification time
if (theToken != GDS_STRNAME) handleError("Strname statement is missing");
getToken();
if (theToken != GDS_IDENT) handleError("Structure name is missing");
// look for this nodeproto
String name = tokenString + "{lay}";
Cell cell = findCell(name);
if (cell == null)
{
// create the prototype
cell = Cell.newInstance(theLibrary, name);
if (cell == null) handleError("Failed to create structure");
System.out.println("Reading " + name);
if (!currentCells.containsKey(theLibrary))
currentCells.put(theLibrary, cell);
} else
{
missingCells.remove(cell);
}
theCell = new CellBuilder(cell, curTech, localPrefs);
}
private Cell findCell(String name)
{
return theLibrary.findNodeProto(name);
}
/**
* Method to create a pure-layer node with a complex outline.
* @param points the outline description.
* @param pureType the type of the pure-layer node.
* @param parent the Cell in which to create the node.
*/
private static void buildComplexNode(List<EPoint> points, NodeProto pureType, Cell parent)
{
EPoint [] pointArray = new EPoint[points.size()];
double lX=0, hX=0, lY=0, hY=0;
for(int i=0; i<points.size(); i++)
{
pointArray[i] = points.get(i);
if (pointArray[i] == null) continue;
if (i == 0)
{
lX = hX = pointArray[i].getX();
lY = hY = pointArray[i].getY();
} else
{
if (pointArray[i].getX() < lX) lX = pointArray[i].getX();
if (pointArray[i].getX() > hX) hX = pointArray[i].getX();
if (pointArray[i].getY() < lY) lY = pointArray[i].getY();
if (pointArray[i].getY() > hY) hY = pointArray[i].getY();
}
}
NodeInst ni = NodeInst.makeInstance(pureType, new Point2D.Double((lX+hX)/2, (lY+hY)/2), hX-lX, hY-lY,
parent, Orientation.IDENT, null);
if (ni != null && GenMath.getAreaOfPoints(pointArray) != (hX-lX)*(hY-lY))
ni.setTrace(pointArray);
}
private void getElement()
throws IOException
{
while (isMember(theToken, shapeSet))
{
if (theToken == GDS_AREF)
{
determineARef();
} else if (theToken == GDS_SREF)
{
determineSRef();
} else if (theToken == GDS_BOUNDARY)
{
determineShape();
} else if (theToken == GDS_PATH)
{
determinePath();
} else if (theToken == GDS_NODE)
{
determineNode();
} else if (theToken == GDS_TEXTSYM)
{
determineText();
} else if (theToken == GDS_BOX)
{
determineBox();
}
}
while (theToken == GDS_PROPATTR)
determineProperty();
if (theToken != GDS_ENDEL)
{
showResultsOfCell();
handleError("Element end statement is missing");
}
}
private void determineARef()
throws IOException
{
getToken();
readUnsupported(unsupportedSet);
if (theToken != GDS_SNAME) handleError("Array reference name is missing");
getToken();
// get this nodeproto
getPrototype(tokenString);
getToken();
int angle = 0;
boolean trans = false;
if (theToken == GDS_STRANS)
{
ReadOrientation ro = new ReadOrientation();
ro.doIt();
angle = ro.angle;
trans = ro.trans;
}
int nCols = 0, nRows = 0;
if (theToken == GDS_COLROW)
{
getToken();
nCols = tokenValue16;
getToken();
nRows = tokenValue16;
getToken();
}
if (theToken != GDS_XY) handleError("Array reference has no parameters");
getToken();
determinePoints(3, 3);
// see if the instance is a single object
if (TALLYCONTENTS)
{
countARef++;
countATotal += nCols*nRows;
return;
}
boolean mY = false;
boolean mX = false;
if (trans)
{
mY = true;
angle = (angle + 900) % 3600;
}
Point2D colInterval = new Point2D.Double(0, 0);
if (nCols != 1)
{
colInterval.setLocation((theVertices[1].getX() - theVertices[0].getX()) / nCols,
(theVertices[1].getY() - theVertices[0].getY()) / nCols);
}
Point2D rowInterval = new Point2D.Double(0, 0);
if (nRows != 1)
{
rowInterval.setLocation((theVertices[2].getX() - theVertices[0].getX()) / nRows,
(theVertices[2].getY() - theVertices[0].getY()) / nRows);
}
theCell.makeInstanceArray(theNodeProto, nCols, nRows, Orientation.fromJava(angle, mX, mY),
theVertices[0], rowInterval, colInterval);
if (DEBUGALL) System.out.println("---- Array Reference: " + nCols + "x" + nRows + " of " + theNodeProto.describe(false));
}
private class ReadOrientation
{
private int angle;
private boolean trans;
private double scale;
private void doIt()
throws IOException
{
double anglevalue = 0.0;
scale = 1.0;
boolean mirror_x = false;
getToken();
if (theToken != GDS_FLAGSYM) handleError("Structure reference is missing its flags field");
if ((tokenFlags&0100000) != 0) mirror_x = true;
getToken();
if (theToken == GDS_MAG)
{
getToken();
scale = tokenValueDouble;
getToken();
}
if (theToken == GDS_ANGLE)
{
getToken();
anglevalue = tokenValueDouble * 10;
getToken();
}
angle = ((int)anglevalue) % 3600;
trans = mirror_x;
if (trans)
angle = (2700 - angle) % 3600;
// should not happen...*/
if (angle < 0) angle = angle + 3600;
}
}
private void determineSRef()
throws IOException
{
getToken();
readUnsupported(unsupportedSet);
if (theToken != GDS_SNAME) handleError("Structure reference name is missing");
getToken();
getPrototype(tokenString);
getToken();
int angle = 0;
boolean trans = false;
if (theToken == GDS_STRANS)
{
ReadOrientation ro = new ReadOrientation();
ro.doIt();
angle = ro.angle;
trans = ro.trans;
}
if (theToken != GDS_XY) handleError("Structure reference has no translation value");
getToken();
determinePoints(1, 1);
if (TALLYCONTENTS)
{
countSRef++;
return;
}
Point2D loc = new Point2D.Double(theVertices[0].getX(), theVertices[0].getY());
boolean mY = false;
if (trans)
{
mY = true;
angle = (angle + 900) % 3600;
}
theCell.makeInstance(theNodeProto, loc, Orientation.fromJava(angle, false, mY), 0, 0, null, null);
if (DEBUGALL) System.out.println("---- Instance of " + theNodeProto.describe(false) +
" at (" + loc.getX() + "," + loc.getY() + ")");
}
private void determineShape()
throws IOException
{
getToken();
readUnsupported(unsupportedSet);
determineLayer();
getToken();
if (theToken != GDS_XY) handleError("Boundary has no points");
getToken();
determinePoints(3, MAXPOINTS);
if (TALLYCONTENTS)
{
countShape++;
return;
}
determineBoundary();
if (DEBUGALL) System.out.println("---- Shape on " + (layerIsPin ? "pin " : "") + "layer " + curLayerNum + "/" + curLayerType +
" (" + layerNodeProto + ") has " + numVertices + " points");
}
private void determineBoundary()
{
boolean is90 = true;
boolean is45 = true;
for (int i=0; i<numVertices-1 && i<MAXPOINTS-1; i++)
{
double dx = theVertices[i+1].getX() - theVertices[i].getX();
double dy = theVertices[i+1].getY() - theVertices[i].getY();
if (dx != 0 && dy != 0)
{
is90 = false;
if (Math.abs(dx) != Math.abs(dy)) is45 = false;
}
}
ShapeType perimeter = SHAPELINE;
if (theVertices[0].getX() == theVertices[numVertices-1].getX() &&
theVertices[0].getY() == theVertices[numVertices-1].getY())
perimeter = SHAPECLOSED;
ShapeType oclass = SHAPEOBLIQUE;
if (perimeter == SHAPECLOSED && (is90 || is45))
oclass = SHAPEPOLY;
if (numVertices == 5 && is90 && perimeter == SHAPECLOSED)
oclass = SHAPERECTANGLE;
if (oclass == SHAPERECTANGLE)
{
readBox();
// create the rectangle
Point2D ctr = new Point2D.Double((theVertices[0].getX()+theVertices[1].getX())/2,
(theVertices[0].getY()+theVertices[1].getY())/2);
double sX = Math.abs(theVertices[1].getX() - theVertices[0].getX());
double sY = Math.abs(theVertices[1].getY() - theVertices[0].getY());
if (localPrefs.mergeBoxes)
{
if (layerNodeProto != null)
{
PrimitiveNode plnp = layerNodeProto;
NodeLayer [] layers = plnp.getNodeLayers();
merge.addPolygon(layers[0].getLayer(), new Poly(ctr.getX(), ctr.getY(), sX, sY));
}
} else
{
theCell.makeInstance(layerNodeProto, ctr, Orientation.IDENT, sX, sY, null, currentUnknownLayerMessage);
}
return;
}
if (oclass == SHAPEOBLIQUE || oclass == SHAPEPOLY)
{
if (localPrefs.mergeBoxes)
{
NodeLayer [] layers = layerNodeProto.getNodeLayers();
if (layerNodeProto != null)
merge.addPolygon(layers[0].getLayer(), new Poly(theVertices));
} else
{
// determine the bounds of the polygon
double lx = theVertices[0].getX();
double hx = theVertices[0].getX();
double ly = theVertices[0].getY();
double hy = theVertices[0].getY();
for (int i=1; i<numVertices;i++)
{
if (lx > theVertices[i].getX()) lx = theVertices[i].getX();
if (hx < theVertices[i].getX()) hx = theVertices[i].getX();
if (ly > theVertices[i].getY()) ly = theVertices[i].getY();
if (hy < theVertices[i].getY()) hy = theVertices[i].getY();
}
// store the trace information
EPoint [] points = new EPoint[numVertices];
for(int i=0; i<numVertices; i++)
{
points[i] = new EPoint(theVertices[i].getX(), theVertices[i].getY());
}
// now create the node
theCell.makeInstance(layerNodeProto, new EPoint((lx+hx)/2, (ly+hy)/2),
Orientation.IDENT, hx-lx, hy-ly, points, currentUnknownLayerMessage);
}
return;
}
}
private void readBox()
{
double pxm = theVertices[4].getX();
double pxs = theVertices[4].getX();
double pym = theVertices[4].getY();
double pys = theVertices[4].getY();
for (int i = 0; i<4; i++)
{
if (theVertices[i].getX() > pxm) pxm = theVertices[i].getX();
if (theVertices[i].getX() < pxs) pxs = theVertices[i].getX();
if (theVertices[i].getY() > pym) pym = theVertices[i].getY();
if (theVertices[i].getY() < pys) pys = theVertices[i].getY();
}
theVertices[0].setLocation(pxs, pys);
theVertices[1].setLocation(pxm, pym);
}
private void determinePath()
throws IOException
{
int endcode = 0;
getToken();
readUnsupported(unsupportedSet);
determineLayer();
getToken();
if (theToken == GDS_PATHTYPE)
{
getToken();
endcode = tokenValue16;
getToken();
}
double width = 0;
if (theToken == GDS_WIDTH)
{
getToken();
width = scaleValue(tokenValue32);
getToken();
}
double bgnextend = (endcode == 0 || endcode == 4 ? 0 : width/2);
double endextend = bgnextend;
if (theToken == GDS_BGNEXTN)
{
getToken();
if (endcode == 4)
bgnextend = scaleValue(tokenValue32);
getToken();
}
if (theToken == GDS_ENDEXTN)
{
getToken();
if (endcode == 4)
endextend = scaleValue(tokenValue32);
getToken();
}
if (theToken == GDS_XY)
{
getToken();
determinePoints(2, MAXPOINTS);
if (TALLYCONTENTS)
{
countPath++;
return;
}
// construct the path
for (int i=0; i < numVertices-1; i++)
{
Point2D fromPt = theVertices[i];
Point2D toPt = theVertices[i+1];
// determine whether either end needs to be shrunk
double fextend = width / 2;
double textend = fextend;
int thisAngle = GenMath.figureAngle(fromPt, toPt);
if (i > 0)
{
Point2D prevPoint = theVertices[i-1];
int lastAngle = GenMath.figureAngle(prevPoint, fromPt);
if (Math.abs(thisAngle-lastAngle) % 900 != 0)
{
int ang = Math.abs(thisAngle-lastAngle) / 10;
if (ang > 180) ang = 360 - ang;
if (ang > 90) ang = 180 - ang;
fextend = Poly.getExtendFactor(width, ang);
}
} else
{
fextend = bgnextend;
}
if (i+1 < numVertices-1)
{
Point2D nextPoint = theVertices[i+2];
int nextAngle = GenMath.figureAngle(toPt, nextPoint);
if (Math.abs(thisAngle-nextAngle) % 900 != 0)
{
int ang = Math.abs(thisAngle-nextAngle) / 10;
if (ang > 180) ang = 360 - ang;
if (ang > 90) ang = 180 - ang;
textend = Poly.getExtendFactor(width, ang);
}
} else
{
textend = endextend;
}
// handle arbitrary angle path segment
double length = fromPt.distance(toPt);
Poly poly = Poly.makeEndPointPoly(length, width, GenMath.figureAngle(toPt, fromPt),
fromPt, fextend, toPt, textend, Poly.Type.FILLED);
if (localPrefs.mergeBoxes)
{
if (layerNodeProto != null)
{
NodeLayer [] layers = layerNodeProto.getNodeLayers();
merge.addPolygon(layers[0].getLayer(), poly);
}
} else
{
// make the node for this segment
Rectangle2D polyBox = poly.getBox();
if (polyBox != null)
{
theCell.makeInstance(layerNodeProto, new EPoint(polyBox.getCenterX(),
polyBox.getCenterY()), Orientation.IDENT, polyBox.getWidth(), polyBox.getHeight(), null, currentUnknownLayerMessage);
} else
{
polyBox = poly.getBounds2D();
double cx = polyBox.getCenterX();
double cy = polyBox.getCenterY();
// store the trace information
Point2D [] polyPoints = poly.getPoints();
EPoint [] points = new EPoint[polyPoints.length];
for(int j=0; j<polyPoints.length; j++)
{
points[j] = new EPoint(polyPoints[j].getX(), polyPoints[j].getY());
}
// store the trace information
theCell.makeInstance(layerNodeProto, new EPoint(cx, cy), Orientation.IDENT,
polyBox.getWidth(), polyBox.getHeight(), points, currentUnknownLayerMessage);
}
}
}
} else
{
handleError("Path element has no points");
}
if (DEBUGALL) System.out.println("---- Path on " + (layerIsPin ? "pin " : "") + "layer " + curLayerNum + "/" + curLayerType +
" (" + layerNodeProto + ") has " + numVertices + " points");
}
private void determineNode()
throws IOException
{
getToken();
readUnsupported(unsupportedSet);
if (theToken != GDS_LAYER) handleError("Boundary has no points");
getToken();
int layerNum = tokenValue16;
if (theToken == GDS_SHORT_NUMBER)
{
getToken();
}
// also get node type
int layerType = tokenValue16;
if (theToken == GDS_NODETYPE)
{
getToken();
getToken();
}
setLayer(layerNum, layerType);
// make a dot
if (theToken != GDS_XY) handleError("Boundary has no points");
getToken();
determinePoints(1, 1);
if (TALLYCONTENTS)
{
countNode++;
return;
}
// create the node
if (localPrefs.mergeBoxes)
{
} else
{
theCell.makeInstance(layerNodeProto, new Point2D.Double(theVertices[0].getX(), theVertices[0].getY()),
Orientation.IDENT, 0, 0, null, currentUnknownLayerMessage);
}
if (DEBUGALL) System.out.println("---- Node on " + (layerIsPin ? "pin " : "") + "layer " + curLayerNum + "/" + curLayerType +
" (" + layerNodeProto + ") at (" + theVertices[0].getX() + "," + theVertices[0].getY() + ")");
}
private void determineText()
throws IOException
{
getToken();
readUnsupported(unsupportedSet);
determineLayer();
getToken();
int vert_just = -1;
int horiz_just = -1;
if (theToken == GDS_PRESENTATION)
{
Point just = determineJustification();
vert_just = just.x;
horiz_just = just.y;
}
if (theToken == GDS_PATHTYPE)
{
getToken();
// code = tokenValue16;
getToken();
}
if (theToken == GDS_WIDTH)
{
getToken();
// width = tokenValue32 * theScale;
getToken();
}
int angle = 0;
boolean trans = false;
double scale = 1.0;
String textString = "";
for(;;)
{
if (theToken == GDS_STRANS)
{
ReadOrientation ro = new ReadOrientation();
ro.doIt();
angle = ro.angle;
trans = ro.trans;
scale = ro.scale;
continue;
}
if (theToken == GDS_XY)
{
getToken();
determinePoints(1, 1);
continue;
}
if (theToken == GDS_ANGLE)
{
getToken();
angle = (int)(tokenValueDouble * 10.0);
getToken();
continue;
}
if (theToken == GDS_STRING)
{
if (recordCount != 0)
{
getToken();
textString = tokenString;
}
getToken();
break;
}
if (theToken == GDS_MAG)
{
getToken();
getToken();
continue;
}
handleError("Text element has no reference point");
break;
}
if (TALLYCONTENTS)
{
countText++;
return;
}
readText(textString, vert_just, horiz_just, angle, trans, scale);
if (DEBUGALL) System.out.println("---- Text " + (layerIsPin ? "(pin) " : "") + "'" + textString.replace('\n', '/') +
"' at (" + theVertices[0].getX() + "," + theVertices[0].getY() + ")");
}
private void readText(String charstring, int vjust, int hjust, int angle, boolean trans, double scale)
{
// handle pins specially
if (layerIsPin)
{
theCell.makeExport(pinNodeProto, new Point2D.Double(theVertices[0].getX(), theVertices[0].getY()),
Orientation.IDENT, charstring, currentUnknownLayerMessage);
return;
}
// stop if not handling text in GDS
if (!localPrefs.includeText) return;
double x = theVertices[0].getX() + MINFONTWIDTH * charstring.length();
double y = theVertices[0].getY() + MINFONTHEIGHT;
theVertices[1].setLocation(x, y);
// set the text size and orientation
MutableTextDescriptor td = MutableTextDescriptor.getNodeTextDescriptor();
double size = scale;
if (size <= 0) size = 2;
if (size > TextDescriptor.Size.TXTMAXQGRID) size = TextDescriptor.Size.TXTMAXQGRID;
if (size < TextDescriptor.Size.TXTMINQGRID) size = TextDescriptor.Size.TXTMINQGRID;
td.setRelSize(size);
// determine the presentation
td.setPos(TextDescriptor.Position.CENT);
switch (vjust)
{
case 1: // top
switch (hjust)
{
case 1: td.setPos(TextDescriptor.Position.UPRIGHT); break;
case 2: td.setPos(TextDescriptor.Position.UPLEFT); break;
default: td.setPos(TextDescriptor.Position.UP); break;
}
break;
case 2: // bottom
switch (hjust)
{
case 1: td.setPos(TextDescriptor.Position.DOWNRIGHT); break;
case 2: td.setPos(TextDescriptor.Position.DOWNLEFT); break;
default: td.setPos(TextDescriptor.Position.DOWN); break;
}
break;
default: // centered
switch (hjust)
{
case 1: td.setPos(TextDescriptor.Position.RIGHT); break;
case 2: td.setPos(TextDescriptor.Position.LEFT); break;
default: td.setPos(TextDescriptor.Position.CENT); break;
}
}
theCell.makeText(layerNodeProto, new Point2D.Double(theVertices[0].getX(), theVertices[0].getY()),
charstring, TextDescriptor.newTextDescriptor(td), currentUnknownLayerMessage);
}
/**
* untested feature, I don't have a box type
*/
private void determineBox()
throws IOException
{
getToken();
readUnsupported(unsupportedSet);
determineLayer();
if (theToken != GDS_XY) handleError("Boundary has no points");
getToken();
determinePoints(2, MAXPOINTS);
if (TALLYCONTENTS)
{
countBox++;
return;
}
// create the box
if (localPrefs.mergeBoxes)
{
if (layerNodeProto != null)
{
}
} else
{
theCell.makeInstance(layerNodeProto, new Point2D.Double(theVertices[0].getX(), theVertices[0].getY()),
Orientation.IDENT, 0, 0, null, currentUnknownLayerMessage);
}
if (DEBUGALL) System.out.println("---- Box on " + (layerIsPin ? "pin " : "") + "layer " + curLayerNum + "/" + curLayerType +
" (" + layerNodeProto + ") has " + numVertices + " points");
}
private static class UnknownLayerMessage
{
String message;
UnknownLayerMessage(String message)
{
this.message = message;
}
}
private void setLayer(int layerNum, int layerType)
{
curLayerNum = layerNum;
curLayerType = layerType;
layerIsPin = false;
currentUnknownLayerMessage = null;
Integer layerInt = new Integer(layerNum + (layerType<<16));
Layer layer = layerNames.get(layerInt);
if (layer == null)
{
layer = Generic.tech().drcLay;
if (localPrefs.unknownLayerHandling == IOTool.GDSUNKNOWNLAYERUSERANDOM)
{
// assign an unused layer here
for(Iterator<Layer> it = curTech.getLayers(); it.hasNext(); )
{
Layer l = it.next();
if (layerNames.values().contains(l)) continue;
layer = l;
break;
}
if (layer == null)
{
// no unused layers: start picking at random
if (randomLayerSelection >= curTech.getNumLayers()) randomLayerSelection = 0;
layer = curTech.getLayer(randomLayerSelection);
randomLayerSelection++;
}
}
layerNames.put(layerInt, layer);
String message = "GDS layer " + layerNum + ", type " + layerType + " unknown, ";
switch (localPrefs.unknownLayerHandling)
{
case IOTool.GDSUNKNOWNLAYERIGNORE: message += "ignoring it"; break;
case IOTool.GDSUNKNOWNLAYERUSEDRC: message += "using Generic:DRC layer"; break;
case IOTool.GDSUNKNOWNLAYERUSERANDOM: message += "using layer " + layer.getName(); break;
}
currentUnknownLayerMessage = layerErrorMessages.get(layerInt);
if (currentUnknownLayerMessage == null)
{
currentUnknownLayerMessage = new UnknownLayerMessage(message);
layerErrorMessages.put(layerInt, currentUnknownLayerMessage);
}
}
if (layer != null)
{
currentUnknownLayerMessage = layerErrorMessages.get(layerInt);
if (layer == Generic.tech().drcLay && localPrefs.unknownLayerHandling == IOTool.GDSUNKNOWNLAYERIGNORE)
{
layerNodeProto = null;
pinNodeProto = null;
return;
}
layerNodeProto = layer.getNonPseudoLayer().getPureLayerNode();
pinNodeProto = Generic.tech().universalPinNode;
if (pinLayers.contains(layerInt))
{
layerIsPin = true;
if (layerNodeProto != null && layerNodeProto.getNumPorts() > 0)
{
PortProto pp = layerNodeProto.getPort(0);
for (Iterator<ArcProto> it = layer.getTechnology().getArcs(); it.hasNext(); )
{
ArcProto arc = it.next();
if (pp.connectsTo(arc))
{
pinNodeProto = arc.findOverridablePinProto(ep);
break;
}
}
}
}
if (layerNodeProto == null)
{
String message = "Error: no pure layer node for layer '" + layer.getName() + "', ignoring it";
layerNames.put(layerInt, Generic.tech().drcLay);
currentUnknownLayerMessage = new UnknownLayerMessage(message);
layerErrorMessages.put(layerInt, currentUnknownLayerMessage);
}
}
}
private void determineLayer()
throws IOException
{
if (theToken != GDS_LAYER) handleError("Layer statement is missing");
getToken();
if (theToken != GDS_SHORT_NUMBER) handleError("Invalid layer number");
int layerNum = tokenValue16;
getToken();
if (!isMember(theToken, maskSet)) handleError("No datatype field");
getToken();
setLayer(layerNum, tokenValue16);
}
/**
* Method to get the justification information into a Point.
* @return a point whose "x" is the vertical justification and whose
* "y" is the horizontal justification.
*/
private Point determineJustification()
throws IOException
{
Point just = new Point();
getToken();
if (theToken != GDS_FLAGSYM) handleError("Array reference has no parameters");
int font_libno = tokenFlags & 0x0030;
font_libno = font_libno >> 4;
just.x = tokenFlags & 0x000C;
just.x = just.x >> 2;
just.y = tokenFlags & 0x0003;
getToken();
return just;
}
private void determineProperty()
throws IOException
{
getToken();
getToken();
if (theToken != GDS_PROPVALUE) handleError("Property has no value");
getToken();
// add to the current structure as a variable?
getToken();
}
private void getPrototype(String name)
throws IOException
{
// scan for this prototype
name = name + "{lay}";
Cell np = findCell(name);
if (np == null)
{
// FILO order, create this nodeproto
np = Cell.newInstance(theLibrary, name);
if (np == null) handleError("Failed to create SREF proto");
setProgressValue(0);
setProgressNote("Reading " + name);
missingCells.add(np);
}
// set the reference node prototype
theNodeProto = np;
}
private void readGenerations()
throws IOException
{
getToken();
if (theToken != GDS_SHORT_NUMBER) handleError("Generations value is invalid");
getToken();
}
private boolean isMember(GSymbol tok, GSymbol [] set)
{
for(int i=0; i<set.length; i++)
if (set[i] == tok) return true;
return false;
}
private void getToken()
throws IOException
{
if (recordCount == 0)
{
valuetype = readRecord();
} else
{
if (valuetype == TYPEFLAGS)
{
tokenFlags = getWord();
theToken = GDS_FLAGSYM;
return;
}
if (valuetype == TYPESHORT)
{
tokenValue16 = getWord();
theToken = GDS_SHORT_NUMBER;
return;
}
if (valuetype == TYPELONG)
{
tokenValue32 = getInteger();
theToken = GDS_NUMBER;
return;
}
if (valuetype == TYPEFLOAT)
{
tokenValueDouble = getFloat();
theToken = GDS_REALNUM;
return;
}
if (valuetype == TYPEDOUBLE)
{
tokenValueDouble = getDouble();
theToken = GDS_REALNUM;
return;
}
if (valuetype == TYPESTRING)
{
tokenString = getString();
theToken = GDS_IDENT;
return;
}
if (valuetype == TYPEERR) handleError("Invalid GDS II datatype");
}
}
private DatatypeSymbol readRecord()
throws IOException
{
int dataword = getWord();
recordCount = dataword - 2;
int recordtype = getByte() & 0xFF;
theToken = GSymbol.findSymbol(recordtype);
DatatypeSymbol datatypeSymbol = TYPEERR;
switch (getByte())
{
case 0: datatypeSymbol = TYPENONE; break;
case 1: datatypeSymbol = TYPEFLAGS; break;
case 2: datatypeSymbol = TYPESHORT; break;
case 3: datatypeSymbol = TYPELONG; break;
case 4: datatypeSymbol = TYPEFLOAT; break;
case 5: datatypeSymbol = TYPEDOUBLE; break;
case 6: datatypeSymbol = TYPESTRING; break;
}
return datatypeSymbol;
}
private void handleError(String msg)
throws IOException
{
String message = "Error: " + msg + " at byte " + byteCount +
" in '" + filePath + "'";
Cell cell = theCell != null ? theCell.cell : null;
System.out.println(message);
errorLogger.logError(message, cell, 0);
throw new IOException();
}
private void readUnsupported(GSymbol bad_op_set[])
throws IOException
{
if (isMember(theToken, bad_op_set))
{
do
{
getToken();
} while (!isMember(theToken, goodOpSet));
}
}
private void determinePoints(int min_points, int max_points)
throws IOException
{
numVertices = 0;
while (theToken == GDS_NUMBER)
{
double x = scaleValue(tokenValue32);
getToken();
double y = scaleValue(tokenValue32);
theVertices[numVertices].setLocation(x, y);
numVertices++;
if (numVertices > max_points)
{
System.out.println("Found " + numVertices + " points (too many)");
handleError("Too many points in the shape");
}
getToken();
}
if (numVertices < min_points)
{
System.out.println("Found " + numVertices + " points (too few)");
handleError("Not enough points in the shape");
}
}
private String determineTime()
throws IOException
{
String [] time_array = new String[7];
for (int i = 0; i < 6; i++)
{
if (theToken != GDS_SHORT_NUMBER) handleError("Date value is not a valid number");
if (i == 0 && tokenValue16 < 1900)
{
// handle Y2K date issues
if (tokenValue16 > 60) tokenValue16 += 1900; else
tokenValue16 += 2000;
}
time_array[i] = Integer.toString(tokenValue16);
getToken();
}
return time_array[1] + "-" + time_array[2] + "-" + time_array[0] + " at " +
time_array[3] + ":" + time_array[4] + ":" + time_array[5];
}
private float getFloat()
throws IOException
{
int reg = getByte() & 0xFF;
int sign = 1;
if ((reg & 0x00000080) != 0) sign = -1;
reg = reg & 0x0000007F;
// generate the exponent, currently in Excess-64 representation
int binary_exponent = (reg - 64) << 2;
reg = (getByte() & 0xFF) << 16;
int dataword = getWord();
reg = reg + dataword;
int shift_count = 0;
// normalize the mantissa
while ((reg & 0x00800000) == 0)
{
reg = reg << 1;
shift_count++;
}
// this is the exponent + normalize shift - precision of mantissa
// binary_exponent = binary_exponent + shift_count - 24;
binary_exponent = binary_exponent - shift_count - 24;
if (binary_exponent > 0)
{
return (float)(sign * reg * makePower(2, binary_exponent));
}
if (binary_exponent < 0)
return (float)(sign * reg / makePower(2, -binary_exponent));
return sign * reg;
}
private static double makePower(int val, int power)
{
return Math.pow(val, power);
// double result = 1.0;
// for(int count=0; count<power; count++)
// result *= val;
// return result;
}
private double getDouble()
throws IOException
{
// first byte is the exponent field (hex)
int register1 = getByte() & 0xFF;
// plus sign bit
double realValue = 1;
if ((register1 & 0x00000080) != 0) realValue = -1.0;
// the hex exponent is in excess 64 format
register1 = register1 & 0x0000007F;
int exponent = register1 - 64;
// bytes 2-4 are the high ordered bits of the mantissa
register1 = (getByte() & 0xFF) << 16;
int dataword = getWord();
register1 = register1 + dataword;
// next word completes the mantissa (1/16 to 1)
int long_integer = getInteger();
int register2 = long_integer;
// now normalize the value
if (register1 != 0 || register2 != 0)
{
// while ((register1 & 0x00800000) == 0)
// check for 0 in the high-order nibble
while ((register1 & 0x00F00000) == 0)
{
// register1 = register1 << 1;
// shift the 2 registers by 4 bits
register1 = (register1 << 4) + (register2>>28);
register2 = register2 << 4;
exponent--;
}
} else
{
// true 0
return 0;
}
// now create the mantissa (fraction between 1/16 to 1)
realValue *= (register1 * twoTo32 + register2) * twoToNeg56;
if (exponent > 0)
{
double pow = makePower(16, exponent);
realValue *= pow;
} else
{
if (exponent < 0)
{
double pow = makePower(16, -exponent);
realValue /= pow;
}
}
return realValue;
}
private String getString()
throws IOException
{
StringBuffer sb = new StringBuffer();
while (recordCount != 0)
{
char letter = (char)getByte();
if (letter != 0) sb.append(letter);
}
return sb.toString();
}
private int getInteger()
throws IOException
{
int highWord = getWord();
int lowWord = getWord();
return (highWord << 16) | lowWord;
}
private int getWord()
throws IOException
{
int highByte = getByte() & 0xFF;
int lowByte = getByte() & 0xFF;
return (highByte << 8) | lowByte;
}
private byte getByte()
throws IOException
{
byte b = dataInputStream.readByte();
updateProgressDialog(1);
recordCount--;
return b;
}
}