/* -*- tab-width: 4 -*- * * Electric(tm) VLSI Design System * * File: DEF.java * Input/output tool: DEF (Design Exchange Format) reader * 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.input; 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.Library; import com.sun.electric.database.hierarchy.View; import com.sun.electric.database.id.CellId; 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.CellName; import com.sun.electric.database.text.TextUtils; import com.sun.electric.database.topology.ArcInst; import com.sun.electric.database.topology.NodeInst; import com.sun.electric.database.topology.PortInst; import com.sun.electric.database.variable.Variable; import com.sun.electric.technology.ArcProto; import com.sun.electric.technology.PrimitiveNode; import com.sun.electric.technology.SizeOffset; import com.sun.electric.technology.Technology; import com.sun.electric.technology.technologies.Generic; import com.sun.electric.tool.Job; import com.sun.electric.tool.user.IconParameters; import com.sun.electric.tool.io.IOTool; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.io.IOException; import java.net.URL; import java.util.ArrayList; import java.util.BitSet; import java.util.Enumeration; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * This class reads files in DEF files. * <BR> * Note that this reader was built by examining DEF files and reverse-engineering them. * It does not claim to be compliant with the DEF specification, but it also does not * claim to define a new specification. It is merely incomplete. * * R. Reese (RBR) - modified Spring 2007 to be able to import a DEF file to a currently * opened View. The intended use is for the Views to either be layout or schematic. * If the view is layout, then all geometry is input and unrouted net connections * are used to maintain connectivity between logical nets and physical geometries. * At some point in the future, these unrouted nets need to be cleaned up, but for * now, the use of unrouted nets allows the layout to pass DRC and to be simulated. * Can also import to a schematic view - this creates a hodgepodge of icons in the * schematic view but net connections are correct so NCC can be used to check * layout vs schematic. This is useful in a hierarchical design where part of the * design is imported DEF (say, a standard cell layout), and the rest of the design * is manual layout. Having a schematic view for the imported DEF allows NCC to * complain less when checking the design. */ public class DEF extends LEFDEF { private double scaleUnits; private Technology curTech; private ViaDef firstViaDef; private Hashtable<String,PortInst> specialNetsHT = null; private Hashtable<String,PortInst> normalNetsHT = null; private Hashtable<Double,List<NodeInst>> PortHT = null; private boolean schImport = false; private Pattern pat_starleftbracket = Pattern.compile(".*\\\\"+ "\\["); private Pattern pat_leftbracket = Pattern.compile("\\\\"+ "\\["); private Pattern pat_starrightbracket = Pattern.compile(".*\\\\"+ "\\]"); private Pattern pat_rightbracket = Pattern.compile("\\\\"+ "\\]"); private DEFPreferences localPrefs; public static class DEFPreferences extends InputPreferences { public boolean logicalPlacement; public boolean physicalPlacement; public IconParameters iconParameters = IconParameters.makeInstance(false); public DEFPreferences(boolean factory) { super(factory); } public void initFromUserDefaults() { logicalPlacement = IOTool.isDEFLogicalPlacement(); physicalPlacement = IOTool.isDEFPhysicalPlacement(); iconParameters.initFromUserDefaults(); } @Override public Library doInput(URL fileURL, Library lib, Technology tech, Map<Library,Cell> currentCells, Map<CellId,BitSet> nodesToExpand, Job job) { DEF in = new DEF(this); if (in.openTextInput(fileURL)) return null; lib = in.importALibrary(lib, tech, currentCells); in.closeInput(); return lib; } } /** * Creates a new instance of DEF. */ DEF(DEFPreferences 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) { initKeywordParsing(); scaleUnits = 1000; firstViaDef = null; curTech = tech; // read the file try { if (readFile(lib, currentCells)) return null; // error during reading } catch (IOException e) { System.out.println("ERROR reading DEF libraries"); } return lib; } private boolean ignoreToSemicolon(String command) throws IOException { // ignore up to the next semicolon for(;;) { String key = mustGetKeyword(command); if (key == null) return true; if (key.equals(";")) break; } return false; } private String mustGetKeyword(String where) throws IOException { String key = getAKeyword(); if (key == null) reportError("EOF parsing " + where); return key; } private double convertDEFString(String key) { double v = TextUtils.atof(key) / scaleUnits; return TextUtils.convertFromDistance(v, curTech, TextUtils.UnitScale.MICRO); } private void reportError(String command) { System.out.println("File " + filePath + ", line " + lineReader.getLineNumber() + ": " + command); } /** * Method to read the DEF file. * @return true on error. */ private boolean readFile(Library lib, Map<Library,Cell> currentCells) throws IOException { Cell cell = null; for(;;) { // get the next keyword String key = getAKeyword(); if (key == null) break; if (key.equalsIgnoreCase("VERSION") || key.equalsIgnoreCase("NAMESCASESENSITIVE") || key.equalsIgnoreCase("DIVIDERCHAR") || key.equalsIgnoreCase("BUSBITCHARS") || key.equalsIgnoreCase("DIEAREA") || key.equalsIgnoreCase("ROW") || key.equalsIgnoreCase("TRACKS") || key.equalsIgnoreCase("GCELLGRID") || key.equalsIgnoreCase("HISTORY") || key.equalsIgnoreCase("TECHNOLOGY")) { if (ignoreToSemicolon(key)) return true; continue; } if (key.equalsIgnoreCase("DEFAULTCAP") || key.equalsIgnoreCase("REGIONS")) { if (ignoreBlock(key)) return true; continue; } if (key.equalsIgnoreCase("DESIGN")) { String cellName = mustGetKeyword("DESIGN"); if (cellName == null) return true; /* RBR - first, see if Cell name is equal to current cells * it exists then read into cell */ cell = currentCells.get(lib); if (Input.isNewLibraryCreated()== false) { // reading into current cell, current library if (cell == null) { reportError("A cell must be currently opened for this operation, aborting."); return true; } if (!cell.getCellName().getName().equals(cellName)) { reportError("Cell name in DEF file '" + cellName + "' does not equal current cell name '" + cell.getCellName().getName() + "', aborting."); return true; } View cellView = cell.getCellName().getView(); if (cellView.getAbbreviation().equals("sch")) { schImport = true; // special flag when importing into schematic view } } else if (cell == null || !cell.getCellName().getName().equals(cellName)) { // does not equal current cell, so lets cell = Cell.makeInstance(lib, cellName); } if (cell == null) { reportError("Cannot create cell '" + cellName + "'"); return true; } if (ignoreToSemicolon("DESIGN")) return true; continue; } if (key.equalsIgnoreCase("UNITS")) { if (readUnits()) return true; continue; } if (key.equalsIgnoreCase("PROPERTYDEFINITIONS")) { if (readPropertyDefinitions()) return true; continue; } if (key.equalsIgnoreCase("VIAS")) { if (readVias(cell)) return true; continue; } if (key.equalsIgnoreCase("COMPONENTS")) { if (readComponents(cell)) return true; continue; } if (key.equalsIgnoreCase("PINS")) { if (readPins(cell)) return true; continue; } if (key.equalsIgnoreCase("SPECIALNETS")) { if (readNets(cell, true)) return true; continue; } if (key.equalsIgnoreCase("NETS")) { if (readNets(cell, false)) return true; continue; } if (key.equalsIgnoreCase("END")) { key = getAKeyword(); break; } } return false; } private boolean ignoreBlock(String command) throws IOException { for(;;) { // get the next keyword String key = mustGetKeyword(command); if (key == null) return true; if (key.equalsIgnoreCase("END")) { getAKeyword(); break; } } return false; } private Point2D readCoordinate() throws IOException { // get "(" String key = mustGetKeyword("coordinate"); if (key == null) return null; if (!key.equals("(")) { reportError("Expected '(' in coordinate"); return null; } // get X key = mustGetKeyword("coordinate"); if (key == null) return null; double x = convertDEFString(key); // get Y key = mustGetKeyword("coordinate"); if (key == null) return null; double y = convertDEFString(key); // get ")" key = mustGetKeyword("coordinate"); if (key == null) return null; if (!key.equals(")")) { reportError("Expected ')' in coordinate"); return null; } return new Point2D.Double(x, y); } /** * Find nodeProto with same view as the parent cell */ private Cell getNodeProto(String name, Library curlib, Cell parent) { // first see if this cell is in the current library CellName cn; if (schImport) { cn = CellName.newName(name,View.ICON,0); } else { cn = CellName.newName(name,parent.getView(),0); } Cell cell = curlib.findNodeProto(cn.toString()); if (cell != null) return cell; // now look in other libraries for(Iterator<Library> it = Library.getLibraries(); it.hasNext(); ) { Library lib = it.next(); if (lib.isHidden()) continue; if (lib == curlib) continue; cell = lib.findNodeProto(name); if (cell != null) { // must copy the cell // Cell newCell = copyrecursively(cell, cell->protoname, curlib, cell->cellview, // FALSE, FALSE, "", FALSE, FALSE, TRUE, new HashSet()); // return newCell; return cell; } } return null; } private Cell getNodeProto(String name, Library curlib) { // first see if this cell is in the current library Cell cell = curlib.findNodeProto(name); if (cell != null) return cell; // now look in other libraries for(Iterator<Library> it = Library.getLibraries(); it.hasNext(); ) { Library lib = it.next(); if (lib.isHidden()) continue; if (lib == curlib) continue; cell = lib.findNodeProto(name); if (cell != null) { // must copy the cell // Cell newCell = copyrecursively(cell, cell->protoname, curlib, cell->cellview, // FALSE, FALSE, "", FALSE, FALSE, TRUE, new HashSet()); // return newCell; return cell; } } return null; } //RBR - temp method until I figure out //why in Java 6.0 my use of GetOrientation //generates a compile error private Orientation FetchOrientation() throws IOException { String key = mustGetKeyword("orientation"); if (key == null) return null; int angle; boolean transpose = false; if (key.equalsIgnoreCase("N")) { angle = 0; } else if (key.equalsIgnoreCase("S")) { angle = 1800; } else if (key.equalsIgnoreCase("E")) { angle = 2700; } else if (key.equalsIgnoreCase("W")) { angle = 900; } else if (key.equalsIgnoreCase("FN")) { angle = 900; transpose = true; } else if (key.equalsIgnoreCase("FS")) { angle = 2700; transpose = true; } else if (key.equalsIgnoreCase("FE")) { angle = 1800; transpose = true; } else if (key.equalsIgnoreCase("FW")) { angle = 0; transpose = true; } else { reportError("Unknown orientation (" + key + ")"); return null; } return (Orientation.fromC(angle, transpose)); } private class GetOrientation { private Orientation orient; private GetOrientation() throws IOException { String key = mustGetKeyword("orientation"); if (key == null) return; int angle; boolean transpose = false; if (key.equalsIgnoreCase("N")) { angle = 0; } else if (key.equalsIgnoreCase("S")) { angle = 1800; } else if (key.equalsIgnoreCase("E")) { angle = 2700; } else if (key.equalsIgnoreCase("W")) { angle = 900; } else if (key.equalsIgnoreCase("FN")) { angle = 900; transpose = true; } else if (key.equalsIgnoreCase("FS")) { angle = 2700; transpose = true; } else if (key.equalsIgnoreCase("FE")) { angle = 1800; transpose = true; } else if (key.equalsIgnoreCase("FW")) { angle = 0; transpose = true; } else { reportError("Unknown orientation (" + key + ")"); return; } orient = Orientation.fromC(angle, transpose); } } /** * Method to look for a connection to arcs of type "ap" in cell "cell" * at (x, y). The connection can not be on "not" (if it is not null). * If found, return the PortInst. * * This function became too slow as the number of nets in cell increased. * Replaced by function below. RBR Mar 2007 */ // private PortInst findConnection(double x, double y, ArcProto ap, Cell cell, NodeInst noti) // { // Rectangle2D bound = new Rectangle2D.Double(x, y, 0, 0); // Point2D pt = new Point2D.Double(x, y); // for(Iterator<RTBounds> sea = cell.searchIterator(bound); sea.hasNext(); ) // { // RTBounds geom = sea.next(); // if (!(geom instanceof NodeInst)) continue; // NodeInst ni = (NodeInst)geom; // if (ni == noti) continue; // for(Iterator<PortInst> it = ni.getPortInsts(); it.hasNext(); ) // { // PortInst pi = (PortInst)it.next(); // if (!pi.getPortProto().connectsTo(ap)) continue; // Poly poly = pi.getPoly(); // if (poly.isInside(pt)) return pi; // } // } // return null; // } private PortInst findConnection(double x, double y, ArcProto ap, Cell cell, NodeInst noti) { Double key = new Double(x+y); if (PortHT.containsKey(key)) { List<NodeInst> pl = PortHT.get(key); Point2D pt = new Point2D.Double(x, y); for (int i=0; i < pl.size(); i++) { NodeInst ni = pl.get(i); if (ni == noti) continue; for(Iterator<PortInst> it = ni.getPortInsts(); it.hasNext(); ) { PortInst pi = it.next(); if (!pi.getPortProto().connectsTo(ap)) continue; Poly poly = pi.getPoly(); if (poly.isInside(pt)) return pi; } } } return null; } /** * Method to look for a connection to arcs of type "ap" in cell "cell" * at (x, y). If nothing is found, create a pin. In any case, return * the PortInst. Returns null on error. */ private PortInst getPin(double x, double y, ArcProto ap, Cell cell) { // if there is an existing connection, return it PortInst pi = findConnection(x, y, ap, cell, null); if (pi != null) return pi; // nothing found at this location: create a pin NodeProto pin = ap.findPinProto(); double sX = pin.getDefWidth(); double sY = pin.getDefHeight(); NodeInst ni = NodeInst.makeInstance(pin, new Point2D.Double(x, y), sX, sY, cell); if (ni == null) { reportError("Unable to create net pin"); return null; } List<NodeInst> pl; Double key = new Double(x+y); if (PortHT.containsKey(key)) { pl = PortHT.get(key); } else { pl = new ArrayList<NodeInst>(); PortHT.put(key, pl); } pl.add(ni); return ni.getOnlyPortInst(); } /*************** PINS ***************/ private boolean readPins(Cell cell) throws IOException { if (ignoreToSemicolon("PINS")) return true; for(;;) { // get the next keyword String key = mustGetKeyword("PINs"); if (key == null) return true; if (key.equals("-")) { if (readPin(cell)) return true; continue; } if (key.equalsIgnoreCase("END")) { key = getAKeyword(); break; } // ignore the keyword if (ignoreToSemicolon(key)) return true; } return false; } private String translateDefName (String name) { Matcher m_starleftbracket = pat_starleftbracket.matcher(name); Matcher m_starrightbracket = pat_starrightbracket.matcher(name); if ( m_starleftbracket.matches() || m_starrightbracket.matches()) { String tmpa, tmpb; Matcher m_leftbracket = pat_leftbracket.matcher(name); tmpa = m_leftbracket.replaceAll("["); Matcher m_rightbracket = pat_rightbracket.matcher(tmpa); tmpb = m_rightbracket.replaceAll("]"); return(tmpb); } return name; } private boolean readPin(Cell cell) throws IOException { // get the pin name String key = mustGetKeyword("PIN"); if (key == null) return true; String pinName = translateDefName(key); PortCharacteristic portCharacteristic = null; NodeProto np = null; Point2D ll = null, ur = null, xy = null; boolean haveCoord = false; GetOrientation orient = null; for(;;) { // get the next keyword key = mustGetKeyword("PIN"); if (key == null) return true; if (key.equals("+")) { key = mustGetKeyword("PIN"); if (key == null) return true; if (key.equalsIgnoreCase("NET")) { key = mustGetKeyword("net name"); if (key == null) return true; continue; } if (key.equalsIgnoreCase("DIRECTION")) { key = mustGetKeyword("DIRECTION"); if (key == null) return true; if (key.equalsIgnoreCase("INPUT")) portCharacteristic = PortCharacteristic.IN; else if (key.equalsIgnoreCase("OUTPUT")) portCharacteristic = PortCharacteristic.OUT; else if (key.equalsIgnoreCase("INOUT")) portCharacteristic = PortCharacteristic.BIDIR; else if (key.equalsIgnoreCase("FEEDTHRU")) portCharacteristic = PortCharacteristic.BIDIR; else { reportError("Unknown direction (" + key + ")"); return true; } continue; } if (key.equalsIgnoreCase("USE")) { key = mustGetKeyword("USE"); if (key == null) return true; if (key.equalsIgnoreCase("SIGNAL")) ; else if (key.equalsIgnoreCase("POWER")) portCharacteristic = PortCharacteristic.PWR; else if (key.equalsIgnoreCase("GROUND")) portCharacteristic = PortCharacteristic.GND; else if (key.equalsIgnoreCase("CLOCK")) portCharacteristic = PortCharacteristic.CLK; else if (key.equalsIgnoreCase("TIEOFF")) ; else if (key.equalsIgnoreCase("ANALOG")) ; else { reportError("Unknown usage (" + key + ")"); return true; } continue; } if (key.equalsIgnoreCase("LAYER")) { key = mustGetKeyword("LAYER"); if (key == null) return true; if (!schImport) { GetLayerInformation li = getLayerInformation(key); if (li.pin == null) { reportError("Unknown layer (" + key + ")"); return true; } np = li.pin; } ll = readCoordinate(); if (ll == null) return true; ur = readCoordinate(); if (ur == null) return true; continue; } if (key.equalsIgnoreCase("PLACED") || key.equalsIgnoreCase("FIXED")) { // get pin location and orientation xy = readCoordinate(); if (xy == null) return true; orient = new GetOrientation(); haveCoord = true; continue; } continue; } if (key.equals(";")) break; } if (schImport) { ArcProto apTry = null; for(Iterator<ArcProto> it = curTech.getArcs(); it.hasNext(); ) { apTry = it.next(); if (apTry.getName().equals("wire")) break; } if (apTry == null) { reportError("Unable to resolve pin component"); return true; } for(Iterator<PrimitiveNode> it = curTech.getNodes(); it.hasNext(); ) { PrimitiveNode loc_np = it.next(); // must have just one port if (loc_np.getNumPorts() != 1) continue; // port must connect to both arcs PortProto pp = loc_np.getPort(0); if (pp.connectsTo(apTry)) { np = loc_np; break; } } } // all factors read, now place the pin if (np != null && haveCoord) { // determine the pin size AffineTransform trans = orient.orient.pureRotate(); trans.transform(ll, ll); trans.transform(ur, ur); double sX = Math.abs(ll.getX() - ur.getX()); double sY = Math.abs(ll.getY() - ur.getY()); double cX = (ll.getX() + ur.getX()) / 2 + xy.getX(); double cY = (ll.getY() + ur.getY()) / 2 + xy.getY(); // make the pin NodeInst ni = NodeInst.makeInstance(np, new Point2D.Double(cX, cY), sX, sY, cell); if (ni == null) { reportError("Unable to create pin"); return true; } PortInst pi = ni.findPortInstFromProto(np.getPort(0)); Export e = Export.newInstance(cell, pi, pinName, portCharacteristic, localPrefs.iconParameters); if (e == null) { reportError("Unable to create pin name"); return true; } } return false; } /*************** COMPONENTS ***************/ private boolean readComponents(Cell cell) throws IOException { if (ignoreToSemicolon("COMPONENTS")) return true; for(;;) { // get the next keyword String key = mustGetKeyword("COMPONENTs"); if (key == null) return true; if (key.equals("-")) { if (readComponent(cell)) return true; continue; } if (key.equalsIgnoreCase("END")) { key = getAKeyword(); break; } // ignore the keyword if (ignoreToSemicolon(key)) return true; } return false; } /** * cell is the parent cell */ private boolean readComponent(Cell cell) throws IOException { // get the component name and model name String key = mustGetKeyword("COMPONENT"); if (key == null) return true; String compName = key; key = mustGetKeyword("COMPONENT"); if (key == null) return true; String modelName = key; // find the named cell Cell np; if (cell.getView() != null) { np = getNodeProto(modelName, cell.getLibrary(), cell); } else { /* cell does not have a view yet, have no idea * what view we need, so just get the first one */ np = getNodeProto(modelName, cell.getLibrary()); } if (np == null) { reportError("Unknown cell (" + modelName + ")"); return true; } for(;;) { // get the next keyword key = mustGetKeyword("COMPONENT"); if (key == null) return true; if (key.equals("+")) { key = mustGetKeyword("COMPONENT"); if (key == null) return true; if (key.equalsIgnoreCase("PLACED") || key.equalsIgnoreCase("FIXED")) { // handle placement Point2D pt = readCoordinate(); if (pt == null) return true; double nx = pt.getX(); double ny = pt.getY(); Orientation or = FetchOrientation(); // place the node double sX = np.getDefWidth(); double sY = np.getDefHeight(); Variable prX = np.getVar(prXkey); double width=0; if (prX != null) { String tmps = prX.getPureValue(0); width = TextUtils.atof(tmps); } else { width = sX; //no PR boundary, use cell boundary } Variable prY = np.getVar(prYkey); double height=0; if (prY != null) { String tmps = prY.getPureValue(0); height = TextUtils.atof(tmps); } else { height = sY; //no PR boundary, use cell boundary } // DEF orientations require translations from Java orientations if (or.equals(Orientation.YRR)) { // FN DEF orientation nx = nx + width; } if (or.equals(Orientation.Y)) { // FS DEF orientation ny = ny + height; } if (or.equals(Orientation.RR)) { // S DEF orientation ny = ny + height; nx = nx + width; } if (or.equals(Orientation.RRR)) { // E DEF orientation ny = ny + width; } if (or.equals(Orientation.R)) { // W DEF orientation nx = nx + height; } if (or.equals(Orientation.YRRR)) { // FE DEF orientation } if (or.equals(Orientation.YR)) { // FW DEF orientation nx = nx + height; ny = ny + width; } Point2D npt = new Point2D.Double(nx,ny); NodeInst ni = NodeInst.makeInstance(np, npt, sX, sY, cell, or, compName); if (ni == null) { reportError("Unable to create node"); return true; } continue; } continue; } if (key.equals(";")) break; } return false; } /*************** NETS ***************/ private boolean readNets(Cell cell, boolean special) throws IOException { if (special) specialNetsHT = new Hashtable<String,PortInst>(); else normalNetsHT = new Hashtable<String,PortInst>(); PortHT = new Hashtable<Double,List<NodeInst>>(); for(;;) { // get the next keyword String key = mustGetKeyword("NETs"); if (key == null) return true; if (key.equals("-")) { if (readNet(cell, special)) return true; continue; } if (key.equalsIgnoreCase("END")) { key = getAKeyword(); break; } // ignore the keyword if (ignoreToSemicolon(key)) return true; } connectSpecialNormalNets(); return false; } private PortInst connectGlobal(Cell cell, String portName) { PortInst pi = null; PortInst lastPi = null; NodeInst ni = null; PortProto pp = null; for(Iterator<NodeInst> it = cell.getNodes(); it.hasNext(); ) { ni = it.next(); pp = ni.getProto().findPortProto(portName); if (pp == null) continue; pi = ni.findPortInstFromProto(pp); if (lastPi != null && localPrefs.logicalPlacement) { //do connection ArcInst ai = ArcInst.makeInstance(Generic.tech().unrouted_arc, pi, lastPi); if (ai == null) { reportError("Could not create unrouted arc"); return null; } } lastPi = pi; } return lastPi; } /** * Look for special nets that need to be merged with normal nets * Synopsys Astro router places patches of metal in special nets * to cover normal nets as a method of filling notches */ private void connectSpecialNormalNets() { if (specialNetsHT == null) return; if (normalNetsHT == null) return; if (!localPrefs.logicalPlacement) return; for (Enumeration enSpec = specialNetsHT.keys(); enSpec.hasMoreElements();) { String netName = (String)enSpec.nextElement(); PortInst specPi = specialNetsHT.get(netName); PortInst normalPi = null; if (normalNetsHT.containsKey(netName)) { normalPi = normalNetsHT.get(netName); if (normalPi != null) { // create a logical net between these two points ArcInst ai = ArcInst.makeInstance(Generic.tech().unrouted_arc, specPi, normalPi); if (ai == null) { reportError("Could not create unrouted arc"); return; } } } } } private ViaDef checkForVia(String key) { ViaDef vd = null; for(vd = firstViaDef; vd != null; vd = vd.nextViaDef) if (key.equalsIgnoreCase(vd.viaName)) break; if (vd == null) { // see if the via name is from the LEF file for(vd = firstViaDefFromLEF; vd != null; vd = vd.nextViaDef) if (key.equalsIgnoreCase(vd.viaName)) break; } return vd; } private boolean readNet(Cell cell, boolean special) throws IOException { if (!PLACEDEFNETS) { ignoreToSemicolon("NET"); return false; } if (schImport && special) { // when doing schematic import, ignore special nets ignoreToSemicolon("NET"); return false; } // get the net name String key = mustGetKeyword("NET"); if (key == null) return true; String netName = translateDefName(key); // save this so net can be placed in hashtable // get the next keyword key = mustGetKeyword("NET"); if (key == null) return true; // scan the "net" statement boolean adjustPinLocPi = false; boolean adjustPinLocLastPi = false; boolean wantPinPairs = true; boolean connectAllComponents = false; String wildcardPort = null; double lastX = 0, lastY = 0; double curX = 0, curY = 0; double specialWidth = 0; boolean pathStart = true; PortInst lastLogPi = null; PortInst lastPi = null; GetLayerInformation li = null; boolean foundCoord = false; boolean stackedViaFlag = false; for(;;) { // examine the next keyword if (key.equals(";")) { if (lastPi != null) { // remember at least one physical port instance for this net! if (special) specialNetsHT.put(netName,lastPi); else normalNetsHT.put(netName,lastPi); } if (lastLogPi != null && lastPi != null && localPrefs.logicalPlacement) { // connect logical network and physical network so that DRC passes ArcInst ai = ArcInst.makeInstance(Generic.tech().unrouted_arc, lastPi, lastLogPi); if (ai == null) { reportError("Could not create unrouted arc"); return true; } } break; } if (key.equals("+")) { wantPinPairs = false; if (schImport) { // ignore the remainder ignoreToSemicolon("NET"); break; } key = mustGetKeyword("NET"); if (key == null) return true; if (key.equalsIgnoreCase("USE")) { // ignore "USE" keyword key = mustGetKeyword("NET"); if (key == null) return true; } else if (key.equalsIgnoreCase("ROUTED")) { // handle "ROUTED" keyword key = mustGetKeyword("NET"); if (key == null) return true; li = getLayerInformation(key); if (li.pin == null) { reportError("Unknown layer (" + key + ")"); return true; } pathStart = true; if (special) { // specialnets have width here key = mustGetKeyword("NET"); if (key == null) return true; specialWidth = convertDEFString(key); } } else if (key.equalsIgnoreCase("FIXED")) { // handle "FIXED" keyword key = mustGetKeyword("NET"); if (key == null) return true; li = getLayerInformation(key); if (li.pin == null) { reportError("Unknown layer (" + key + ")"); return true; } pathStart = true; } else if (key.equalsIgnoreCase("SHAPE")) { // handle "SHAPE" keyword key = mustGetKeyword("NET"); if (key == null) return true; } else if (key.equalsIgnoreCase("SOURCE")) { // handle "SOURCE" keyword key = mustGetKeyword("NET"); if (key == null) return true; } else if (key.equalsIgnoreCase("ORIGINAL")) { // handle "ORIGINAL" keyword key = mustGetKeyword("NET"); if (key == null) return true; } else { reportError("Cannot handle '" + key + "' nets"); return true; } // get next keyword key = mustGetKeyword("NET"); if (key == null) return true; continue; } // if still parsing initial pin pairs, do so if (wantPinPairs) { // it must be the "(" of a pin pair if (!key.equals("(")) { reportError("Expected '(' of pin pair"); return true; } // get the pin names key = mustGetKeyword("NET"); if (key == null) return true; PortInst pi = null; if (key.equalsIgnoreCase("PIN")) { // find the export key = mustGetKeyword("NET"); if (key == null) return true; key = translateDefName(key); Export pp = (Export)cell.findPortProto(key); if (pp == null) { reportError("Warning: unknown pin '" + key + "'"); if (ignoreToSemicolon("NETS")) return true; return false; } pi = pp.getOriginalPort(); } else { NodeInst found = null; if (key.equals("*")) connectAllComponents = true; else connectAllComponents = false; for(Iterator<NodeInst> it = cell.getNodes(); it.hasNext(); ) { NodeInst ni = it.next(); if (connectAllComponents || ni.getName().equalsIgnoreCase(key)) { found = ni; break; } } if (found == null) { reportError("Unknown component '" + key + "'"); return true; } // get the port name key = mustGetKeyword("NET"); if (key == null) return true; PortProto pp = found.getProto().findPortProto(key); if (pp == null) { reportError("Unknown port '" + key + "' on component " + found); return true; } if (connectAllComponents) wildcardPort = key; pi = found.findPortInstFromProto(pp); } // get the close parentheses key = mustGetKeyword("NET"); if (key == null) return true; if (!key.equals(")")) { reportError("Expected ')' of pin pair"); return true; } if (localPrefs.logicalPlacement) { if (connectAllComponents) { // must connect all components in netlist pi = connectGlobal(cell, wildcardPort); if (pi == null) return true; } else { if (lastLogPi != null) { ArcInst ai = ArcInst.makeInstance(Generic.tech().unrouted_arc, pi, lastLogPi); if (ai == null) { reportError("Could not create unrouted arc"); return true; } } } } lastLogPi = pi; // get the next keyword and continue parsing key = mustGetKeyword("NET"); if (key == null) return true; continue; } // handle "new" start of coordinate trace if (key.equalsIgnoreCase("NEW")) { // Connect last created segment to logical network if (lastLogPi != null && lastPi != null && localPrefs.logicalPlacement) { // connect logical network and physical network so that DRC passes ArcInst ai = ArcInst.makeInstance(Generic.tech().unrouted_arc, lastPi, lastLogPi); if (ai == null) { reportError("Could not create unrouted arc"); return true; } } key = mustGetKeyword("NET"); if (key == null) return true; li = getLayerInformation(key); if (li.pin == null) { reportError("Unknown layer (" + key + ")"); return true; } pathStart = true; key = mustGetKeyword("NET"); if (key == null) return true; if (special) { // specialnets have width here specialWidth = convertDEFString(key); // get the next keyword key = mustGetKeyword("NET"); if (key == null) return true; } continue; } if (!stackedViaFlag) foundCoord = false; if (key.equals("(")) { // get the X coordinate foundCoord = true; key = mustGetKeyword("NET"); if (key == null) return true; if (key.equals("*")) curX = lastX; else { curX = convertDEFString(key); } // get the Y coordinate key = mustGetKeyword("NET"); if (key == null) return true; if (key.equals("*")) curY = lastY; else { curY = convertDEFString(key); } // get the close parentheses key = mustGetKeyword("NET"); if (key == null) return true; if (!key.equals(")")) { reportError("Expected ')' of coordinate pair"); return true; } } /* * if stackedViaFlag is set, then we have already fetched * this Via key word, so don't fetch next keyword */ if (!stackedViaFlag) { // get the next keyword key = mustGetKeyword("NET"); if (key == null) return true; } // see if it is a via name ViaDef vd = checkForVia(key); // stop now if not placing physical nets if (!localPrefs.physicalPlacement || schImport) { // ignore the next keyword if a via name is coming if (vd != null) { key = mustGetKeyword("NET"); if (key == null) return true; } continue; } // if a via is mentioned next, use it PortInst pi = null; boolean placedVia = false; if (vd != null) { // place the via at this location double sX = vd.sX; double sY = vd.sY; if (vd.via == null) { reportError("Cannot to create via"); return true; } // see if there is a connection point here when starting a path if (pathStart) { lastPi = findConnection(curX, curY, li.arc, cell, null); } // create the via SizeOffset so = vd.via.getProtoSizeOffset(); sX += so.getLowXOffset() + so.getHighXOffset(); sY += so.getLowYOffset() + so.getHighYOffset(); NodeInst ni = NodeInst.makeInstance(vd.via, new Point2D.Double(curX, curY), sX, sY, cell); if (ni == null) { reportError("Unable to create via layer"); return true; } pi = ni.getOnlyPortInst(); // if the path starts with a via, wire it if (pathStart && lastPi != null && foundCoord) { double width = li.arc.getDefaultLambdaBaseWidth(); if (special) width = specialWidth; else { // get the width from the LEF file Double wid = widthsFromLEF.get(li.arc); if (wid != null) width = wid.doubleValue(); } ArcInst ai = ArcInst.makeInstanceBase(li.arc, width, lastPi, pi); if (ai == null) { reportError("Unable to create net starting point"); return true; } } // remember that a via was placed placedVia = true; // get the next keyword key = mustGetKeyword("NET"); if (key == null) return true; // check if next key is yet another via ViaDef vdStack = checkForVia(key) ; if (vdStack == null) stackedViaFlag = false; else stackedViaFlag = true; } else { // no via mentioned: just make a pin // this pin center will have to be adjusted if special! RBR pi = getPin(curX, curY, li.arc, cell); if (pi == null) return true; adjustPinLocPi = true; } if (!foundCoord) continue; // run the wire if (!pathStart) { // make sure that this arc can connect to the current pin if (!pi.getPortProto().connectsTo(li.arc)) { NodeProto np = li.arc.findPinProto(); double sX = np.getDefWidth(); double sY = np.getDefHeight(); NodeInst ni = NodeInst.makeInstance(np, new Point2D.Double(curX, curY), sX, sY, cell); if (ni == null) { reportError("Unable to create net pin"); return true; } pi = ni.getOnlyPortInst(); } // run the wire double width = li.arc.getDefaultLambdaBaseWidth(); if (special) width = specialWidth; else { // get the width from the LEF file Double wid = widthsFromLEF.get(li.arc); if (wid != null) width = wid.doubleValue(); } if (adjustPinLocLastPi && special) { // starting pin; have to adjust the last pin location double dX = 0; double dY = 0; if (curX != lastX) { //horizontal route dX = width/2; // default, adjust left if (curX < lastX) { dX = -dX; // route runs right to left, adjust right } } if (curY != lastY) { // vertical route dY = width/2; // default, adjust up if (curY < lastY) { dY = -dY; // route runs top to bottom, adjust down } } lastPi.getNodeInst().move(dX,dY); adjustPinLocLastPi = false; } /* note that this adjust is opposite of previous since * this pin is on the end of the wire instead of the beginning */ if (adjustPinLocPi && special) { // ending pin; have to adjust the last pin location double dX = 0; double dY = 0; if (curX != lastX) { // horizontal route dX = -width/2; // default, adjust right if (curX < lastX) { dX = -dX; // route runs right to left, adjust left } } if (curY != lastY) { // vertical route dY = -width/2; // default, adjust down if (curY < lastY) { dY = -dY; //route runs top to bottom, adjust up } } pi.getNodeInst().move(dX,dY); adjustPinLocPi = false; } ArcInst ai = ArcInst.makeInstanceBase(li.arc, width, lastPi, pi); if (ai == null) { reportError("Unable to create net path"); return true; } } lastX = curX; lastY = curY; pathStart = false; lastPi = pi; adjustPinLocLastPi=adjustPinLocPi; adjustPinLocPi = false; // switch layers to the other one supported by the via ArcProto liArc = li.arc; if (placedVia) { if (liArc == vd.lay1) { liArc = vd.lay2; } else if (liArc == vd.lay2) { liArc = vd.lay1; } li.pin = liArc.findPinProto(); } // if the path ends here, connect it if (key.equalsIgnoreCase("NEW") || key.equals(";")) { // see if there is a connection point here when starting a path PortInst nextPi = findConnection(curX, curY, liArc, cell, pi.getNodeInst()); // if the path starts with a via, wire it if (nextPi != null) { double width = liArc.getDefaultLambdaBaseWidth(); if (special) width = specialWidth; else { // get the width from the LEF file Double wid = widthsFromLEF.get(liArc); if (wid != null) width = wid.doubleValue(); } ArcInst ai = ArcInst.makeInstanceBase(liArc, width, pi, nextPi); if (ai == null) { reportError("Unable to create net ending point"); return true; } } } } return false; } /*************** VIAS ***************/ private boolean readVias(Cell cell) throws IOException { if (ignoreToSemicolon("VIAS")) return true; for(;;) { // get the next keyword String key = mustGetKeyword("VIAs"); if (key == null) return true; if (key.equals("-")) { if (readVia()) return true; continue; } if (key.equalsIgnoreCase("END")) { key = getAKeyword(); break; } // ignore the keyword if (ignoreToSemicolon(key)) return true; } return false; } private boolean readVia() throws IOException { if (schImport) { ignoreToSemicolon("VIA"); return false; } // get the via name String key = mustGetKeyword("VIA"); if (key == null) return true; // create a new via definition ViaDef vd = new ViaDef(); vd.viaName = key; vd.sX = vd.sY = 0; vd.via = null; vd.lay1 = vd.lay2 = null; vd.nextViaDef = firstViaDef; firstViaDef = vd; for(;;) { // get the next keyword key = mustGetKeyword("VIA"); if (key == null) return true; if (key.equals("+")) { key = mustGetKeyword("VIA"); if (key == null) return true; if (key.equalsIgnoreCase("RECT")) { // handle definition of a via rectangle key = mustGetKeyword("VIA"); if (key == null) return true; GetLayerInformation li = getLayerInformation(key); if (li.pure == null) { reportError("Layer " + key + " not in current technology"); } if (key.startsWith("VIA")) { if (li.pin == null) li.pin = Generic.tech().universalPinNode; vd.via = li.pin; } if (key.startsWith("METAL")) { if (li.arc == null) li.arc = Generic.tech().universal_arc; if (vd.lay1 == null) vd.lay1 = li.arc; else vd.lay2 = li.arc; } Point2D ll = readCoordinate(); if (ll == null) return true; Point2D ur = readCoordinate(); if (ur == null) return true; // accumulate largest contact size if (ur.getX()-ll.getX() > vd.sX) vd.sX = ur.getX() - ll.getX(); if (ur.getY()-ll.getY() > vd.sY) vd.sY = ur.getY() - ll.getY(); continue; } continue; } if (key.equals(";")) break; } if (vd.via != null) { if (vd.sX == 0) vd.sX = vd.via.getDefWidth(); if (vd.sY == 0) vd.sY = vd.via.getDefHeight(); } return false; } /*************** PROPERTY DEFINITIONS ***************/ private boolean readPropertyDefinitions() throws IOException { for(;;) { // get the next keyword String key = mustGetKeyword("PROPERTYDEFINITION"); if (key == null) return true; if (key.equalsIgnoreCase("END")) { key = getAKeyword(); break; } // ignore the keyword if (ignoreToSemicolon(key)) return true; } return false; } /*************** UNITS ***************/ private boolean readUnits() throws IOException { // get the "DISTANCE" keyword String key = mustGetKeyword("UNITS"); if (key == null) return true; if (!key.equalsIgnoreCase("DISTANCE")) { reportError("Expected 'DISTANCE' after 'UNITS'"); return true; } // get the "MICRONS" keyword key = mustGetKeyword("UNITS"); if (key == null) return true; if (!key.equalsIgnoreCase("MICRONS")) { reportError("Expected 'MICRONS' after 'UNITS'"); return true; } // get the amount key = mustGetKeyword("UNITS"); if (key == null) return true; scaleUnits = TextUtils.atof(key) * OVERALLSCALE; // ignore the keyword if (ignoreToSemicolon("UNITS")) return true; return false; } }