/* -*- tab-width: 4 -*- * * Electric(tm) VLSI Design System * * File: SeaOfGatesEngine.java * Routing tool: Sea of Gates routing * Written by: Steven M. Rubin * * Copyright (c) 2007 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.routing; import com.sun.electric.database.EditingPreferences; import com.sun.electric.database.Environment; import com.sun.electric.database.geometry.DBMath; import com.sun.electric.database.geometry.EPoint; import com.sun.electric.database.geometry.ERectangle; import com.sun.electric.database.geometry.GenMath; import com.sun.electric.database.geometry.Orientation; import com.sun.electric.database.geometry.Poly; import com.sun.electric.database.geometry.PolyBase; import com.sun.electric.database.hierarchy.Cell; import com.sun.electric.database.hierarchy.Export; import com.sun.electric.database.hierarchy.HierarchyEnumerator; import com.sun.electric.database.hierarchy.Nodable; import com.sun.electric.database.hierarchy.View; import com.sun.electric.database.network.Netlist; import com.sun.electric.database.network.Network; import com.sun.electric.database.prototype.NodeProto; import com.sun.electric.database.text.TextUtils; import com.sun.electric.database.topology.ArcInst; import com.sun.electric.database.topology.Connection; import com.sun.electric.database.topology.NodeInst; import com.sun.electric.database.topology.PortInst; import com.sun.electric.database.topology.RTBounds; import com.sun.electric.database.topology.RTNode; import com.sun.electric.database.variable.EditWindow_; import com.sun.electric.database.variable.VarContext; import com.sun.electric.technology.ArcProto; import com.sun.electric.technology.DRCTemplate; 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.technologies.Generic; import com.sun.electric.tool.Job; import com.sun.electric.tool.drc.DRC; import com.sun.electric.tool.user.ErrorLogger; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; import java.util.concurrent.Semaphore; /** * Class to do sea-of-gates routing. * This router replaces unrouted arcs with real geometry. It has these features: * > The router only works in layout, and only routes metal wires. * > The router uses vias to move up and down the metal layers. * > Understands multiple vias and multiple via orientations. * > The router is not tracked: it runs on the Electric grid * > Favors wires on full-grid units * > Tries to cover multiple grid units in a single jump * > Routes power and ground first, then goes by length (shortest nets first) * > Prefers to run odd metal layers on horizontal, even layers on vertical * > Routes in both directions (from A to B and from B to A) and chooses the one that completes first * > Serial method alternates advancing each wavefront, stopping when one completes * > Parallel option runs both wavefronts at once and aborts the slower one * > Users can request that some layers not be used, can request that some layers be favored * > Routes are made as wide as the widest arc already connected to any point * > User preference can limit width * > Cost penalty also includes space left in the track on either side of a segment * * Things to do: * At the end of routing, try again with those that failed * Detect "river routes" and route specially * Ability to route to any previous part of route when daisy-chaining? * Lower cost if running over existing layout (on the same net) * Ability to connect to anything on the destination net * Rip-up * Global routing(?) * Sweeping cost parameters (with parallel processors) to find best, dynamically * Forcing each processor to use a different grid track * Characterizing routing task (which parallelism to use) */ public class SeaOfGatesEngine { /** True to display each step in the search. */ private static final boolean DEBUGSTEPS = false; /** True to display the first routing failure. */ private static final boolean DEBUGFAILURE = false; /** True to debug "infinite" loops. */ private static final boolean DEBUGLOOPS = false; /** true to use full, gridless routing */ private static final boolean FULLGRAIN = true; /** Percent of min cell size that route must stay inside. */ private static final double PERCENTLIMIT = 7; /** Number of steps per unit when searching. */ private static final double GRANULARITY = 1; /** Size of steps when searching. */ private static final double GRAINSIZE = (1/GRANULARITY); /** Cost of routing in wrong direction (alternating horizontal/vertical) */ private static final int COSTALTERNATINGMETAL = 100; /** Cost of changing layers. */ private static final int COSTLAYERCHANGE = 8; /** Cost of routing away from the target. */ private static final int COSTWRONGDIRECTION = 15; /** Cost of running on non-favored layer. */ private static final int COSTUNFAVORED = 10; /** Cost of making a turn. */ private static final int COSTTURNING = 1; /** Cost of having coordinates that are off-grid. */ private static final int COSTOFFGRID = 15; private SearchVertex svAborted = new SearchVertex(0, 0, 0, 0, null, 0, null); private SearchVertex svExhausted = new SearchVertex(0, 0, 0, 0, null, 0, null); private SearchVertex svLimited = new SearchVertex(0, 0, 0, 0, null, 0, null); /** Cell in which routing occurs. */ private Cell cell; /** Cell size. */ private Rectangle2D cellBounds; /** Technology to use for routing. */ private Technology tech; /** R-Trees for metal blockage in the cell. */ private Map<Layer,RTNode> metalTrees; /** R-Trees for via blockage in the cell. */ private Map<Layer,RTNode> viaTrees; /** Maps Arcs to network IDs in the R-Tree. */ private Map<ArcInst,Integer> netIDs; /** number of metal layers in the technology. */ private int numMetalLayers; /** metal layers in the technology. */ private Layer [] metalLayers; /** via layers in the technology. */ private Layer [] viaLayers; /** arcs to use for each metal layer. */ private ArcProto [] metalArcs; /** favoritism for each metal layer. */ private boolean [] favorArcs; /** avoidance for each metal layer. */ private boolean [] preventArcs; /** vias to use to go up from each metal layer. */ private MetalVias [] metalVias; /** worst spacing rule for a given metal layer. */ private double [] worstMetalSurround; /** minimum spacing between the centers of two vias. */ private double [] viaSurround; /** the total length of wires routed */ private double totalWireLength; /** true if this is the first failure of a route (for debugging) */ private boolean firstFailure; /** true to run to/from and from/to routing in parallel */ private boolean parallelDij; /** for logging errors */ private ErrorLogger errorLogger; /** * Class to hold a "batch" of routes, all on the same network. */ private static class RouteBatches { Set<ArcInst> unroutedArcs; Set<NodeInst> unroutedNodes; List<PortInst> orderedPorts; int segsInBatch; int numRouted, numUnrouted; } private class Wavefront { /** The route that this is part of. */ private NeededRoute nr; /** Wavefront name (for debugging). */ private String name; /** List of active search vertices while running wavefront. */ private TreeSet<SearchVertex> active; /** Resulting list of vertices found for this wavefront. */ private List<SearchVertex> vertices; /** Set true to abort this wavefront's search. */ private boolean abort; /** The starting and ending ports of the wavefront. */ private PortInst from, to; /** The starting X/Y coordinates of the wavefront. */ private double fromX, fromY; /** The starting metal layer of the wavefront. */ private int fromZ; /** The ending X/Y coordinates of the wavefront. */ private double toX, toY; /** The ending metal layer of the wavefront. */ private int toZ; /** debugging state */ private final boolean debug; /** Search vertices found while propagating the wavefront. */ private Map<Integer,Set<Integer>> [] searchVertexPlanes; /** Gridless search vertices found while propagating wavefront. */ private Map<Double,Set<Double>> [] searchVertexPlanesDBL; /** minimum spacing between this metal and itself. */ private Map<Double,Map<Double,Double>>[] layerSurround; Wavefront(NeededRoute nr, PortInst from, double fromX, double fromY, int fromZ, PortInst to, double toX, double toY, int toZ, String name, boolean debug) { this.nr = nr; this.from = from; this.fromX = fromX; this.fromY = fromY; this.fromZ = fromZ; this.to = to; this.toX = toX; this.toY = toY; this.toZ = toZ; this.name = name; this.debug = debug; active = new TreeSet<SearchVertex>(); vertices = null; abort = false; searchVertexPlanes = new Map[numMetalLayers]; searchVertexPlanesDBL = new Map[numMetalLayers]; layerSurround = new Map[numMetalLayers]; for(int i=0; i<numMetalLayers; i++) layerSurround[i] = new HashMap<Double,Map<Double,Double>>(); if (debug) System.out.println("----------- SEARCHING FROM ("+TextUtils.formatDouble(fromX)+","+ TextUtils.formatDouble(fromY)+",M"+(fromZ+1)+") TO ("+TextUtils.formatDouble(toX)+","+ TextUtils.formatDouble(toY)+",M"+(toZ+1)+") -----------"); SearchVertex svStart = new SearchVertex(fromX, fromY, fromZ, 0, null, 0, this); svStart.cost = 0; setVertex(fromX, fromY, fromZ); active.add(svStart); } /** * Method to get the SearchVertex at a given coordinate. * @param x the X coordinate desired. * @param y the Y coordinate desired. * @param z the Z coordinate (metal layer) desired. * @return the SearchVertex at that point (null if none). */ public boolean getVertex(double x, double y, int z) { if (FULLGRAIN) { Map<Double,Set<Double>> plane = searchVertexPlanesDBL[z]; if (plane == null) return false; Set<Double> row = plane.get(new Double(y)); if (row == null) return false; boolean found = row.contains(new Double(x)); return found; } Map<Integer,Set<Integer>> plane = searchVertexPlanes[z]; if (plane == null) return false; Set<Integer> row = plane.get(new Integer((int)(y*GRANULARITY))); if (row == null) return false; boolean found = row.contains(new Integer((int)(x*GRANULARITY))); return found; } /** * Method to mark a given coordinate. * @param x the X coordinate desired. * @param y the Y coordinate desired. * @param z the Z coordinate (metal layer) desired. */ public void setVertex(double x, double y, int z) { if (FULLGRAIN) { Map<Double,Set<Double>> plane = searchVertexPlanesDBL[z]; if (plane == null) { plane = new HashMap<Double,Set<Double>>(); searchVertexPlanesDBL[z] = plane; } Double iY = new Double(y); Set<Double> row = plane.get(iY); if (row == null) { row = new HashSet<Double>(); plane.put(iY, row); } row.add(new Double(x)); return; } Map<Integer,Set<Integer>> plane = searchVertexPlanes[z]; if (plane == null) { plane = new HashMap<Integer,Set<Integer>>(); searchVertexPlanes[z] = plane; } Integer iY = new Integer((int)(y*GRANULARITY)); Set<Integer> row = plane.get(iY); if (row == null) { row = new HashSet<Integer>(); plane.put(iY, row); } row.add(new Integer((int)(x*GRANULARITY))); } /** * Method to determine the design rule spacing between two pieces of a given layer. * @param layer the layer index. * @param width the width of one of the pieces (-1 to use default). * @param length the length of one of the pieces (-1 to use default). * @return the design rule spacing (0 if none). */ public double getSpacingRule(int layer, double width, double length) { // use default width if none specified if (width < 0) width = metalArcs[layer].getDefaultLambdaBaseWidth(); if (length < 0) length = 50; // convert these to the next largest integers Double wid = new Double(upToGrain(width)); Double len = new Double(upToGrain(length)); // see if the rule is cached6 Map<Double,Double> widMap = layerSurround[layer].get(wid); if (widMap == null) { widMap = new HashMap<Double,Double>(); layerSurround[layer].put(wid, widMap); } Double value = widMap.get(len); if (value == null) { // rule not cached: compute it Layer lay = metalLayers[layer]; DRCTemplate rule = DRC.getSpacingRule(lay, null, lay, null, false, -1, width, length); double v = 0; if (rule != null) v = rule.getValue(0); value = new Double(v); widMap.put(len, value); } return value.doubleValue(); } } /** * Class to hold a route that must be run. */ private class NeededRoute { String routeName; int netID; double minWidth; int batchNumber; int routeInBatch; Rectangle2D routeBounds; double minimumSearchBoundX; double maximumSearchBoundX; double minimumSearchBoundY; double maximumSearchBoundY; Rectangle2D jumpBound; Wavefront dir1, dir2; Wavefront winningWF; SeaOfGates.SeaOfGatesOptions prefs; NeededRoute(String routeName, PortInst from, double fromX, double fromY, int fromZ, PortInst to, double toX, double toY, int toZ, int netID, double minWidth, int batchNumber, int routeInBatch, SeaOfGates.SeaOfGatesOptions prefs) { this.routeName = routeName; this.netID = netID; this.minWidth = minWidth; this.batchNumber = batchNumber; this.routeInBatch = routeInBatch; this.prefs = prefs; cellBounds = cell.getBounds(); minimumSearchBoundX = downToGrain(cellBounds.getMinX()); maximumSearchBoundX = upToGrain(cellBounds.getMaxX()); minimumSearchBoundY = downToGrain(cellBounds.getMinY()); maximumSearchBoundY = upToGrain(cellBounds.getMaxY()); if (PERCENTLIMIT > 0) { double maxStrayFromRouteBounds = Math.min(cellBounds.getWidth(), cellBounds.getHeight()) * PERCENTLIMIT / 100; routeBounds = new Rectangle2D.Double( Math.min(fromX, toX)-maxStrayFromRouteBounds, Math.min(fromY, toY)-maxStrayFromRouteBounds, Math.abs(fromX-toX)+maxStrayFromRouteBounds*2, Math.abs(fromY-toY)+maxStrayFromRouteBounds*2); minimumSearchBoundX = routeBounds.getMinX(); maximumSearchBoundX = routeBounds.getMaxX(); minimumSearchBoundY = routeBounds.getMinY(); maximumSearchBoundY = routeBounds.getMaxY(); } jumpBound = new Rectangle2D.Double(Math.min(fromX, toX), Math.min(fromY, toY), Math.abs(fromX-toX), Math.abs(fromY-toY)); // make two wavefronts going in both directions dir1 = new Wavefront(this, from, fromX, fromY, fromZ, to, toX, toY, toZ, "a->b", DEBUGSTEPS); dir2 = new Wavefront(this, to, toX, toY, toZ, from, fromX, fromY, fromZ, "b->a", false); } public void cleanSearchMemory() { dir1.searchVertexPlanes = null; dir1.searchVertexPlanesDBL = null; dir1.active = null; if (dir1.vertices != null) { for(SearchVertex sv : dir1.vertices) sv.clearCuts(); } dir2.searchVertexPlanes = null; dir2.searchVertexPlanesDBL = null; dir2.active = null; if (dir2.vertices != null) { for(SearchVertex sv : dir2.vertices) sv.clearCuts(); } } } /************************************** ROUTING **************************************/ /** * This is the public interface for Sea-of-Gates Routing when done in batch mode. * @param cell the cell to be Sea-of-Gates-routed. * @param arcsToRoute a List of ArcInsts on networks to be routed. */ public void routeIt(Job job, Cell cell, List<ArcInst> arcsToRoute, SeaOfGates.SeaOfGatesOptions prefs) { // initialize information about the technology if (initializeDesignRules(cell, prefs)) return; // user-interface initialization long startTime = System.currentTimeMillis(); Job.getUserInterface().startProgressDialog("Routing " + arcsToRoute.size() + " nets", null); Job.getUserInterface().setProgressNote("Building blockage information..."); // create an error logger errorLogger = ErrorLogger.newInstance("Routing (Sea of gates)"); // get all blockage information into R-Trees metalTrees = new HashMap<Layer, RTNode>(); viaTrees = new HashMap<Layer, RTNode>(); netIDs = new HashMap<ArcInst,Integer>(); BlockageVisitor visitor = new BlockageVisitor(arcsToRoute); HierarchyEnumerator.enumerateCell(cell, VarContext.globalContext, visitor); addBlockagesAtPorts(arcsToRoute, prefs); //for(Iterator<Layer> it = metalTrees.keySet().iterator(); it.hasNext(); ) //{ // Layer layer = it.next(); // RTNode root = metalTrees.get(layer); // System.out.println("RTree for "+layer.getName()+":"); // root.printRTree(2); //} // make a list of all routes that are needed List<NeededRoute> allRoutes = new ArrayList<NeededRoute>(); int numBatches = arcsToRoute.size(); RouteBatches [] routeBatches = new RouteBatches[numBatches]; for(int b=0; b<numBatches; b++) { // get list of PortInsts that comprise this net ArcInst ai = arcsToRoute.get(b); Netlist netList = cell.getNetlist(); Network net = netList.getNetwork(ai, 0); if (net == null) { System.out.println("Arc " + ai.describe(false) + " has no network!"); continue; } Set<ArcInst> arcsToDelete = new HashSet<ArcInst>(); Set<NodeInst> nodesToDelete = new HashSet<NodeInst>(); Map<Network,ArcInst[]> arcMap = null; if (cell.getView() != View.SCHEMATIC) arcMap = netList.getArcInstsByNetwork(); List<Connection> netEnds = Routing.findNetEnds(net, arcMap, arcsToDelete, nodesToDelete, netList, true); List<PortInst> orderedPorts = makeOrderedPorts(net, netEnds); if (orderedPorts == null) { System.out.println("No valid connection points found on the network."); continue; } routeBatches[b] = new RouteBatches(); routeBatches[b].unroutedArcs = arcsToDelete; routeBatches[b].unroutedNodes = nodesToDelete; routeBatches[b].orderedPorts = orderedPorts; routeBatches[b].segsInBatch = 0; // determine the minimum width of arcs on this net double minWidth = getMinWidth(orderedPorts, prefs); int netID = -1; Integer netIDI = netIDs.get(ai); if (netIDI != null) netID = netIDI.intValue() - 1; // find a path between the ends of the network int batchNumber = 1; for(int i=0; i<orderedPorts.size()-1; i++) { PortInst fromPi = orderedPorts.get(i); PortInst toPi = orderedPorts.get(i+1); if (inValidPort(fromPi) || inValidPort(toPi)) continue; // get information about one end of the path ArcProto fromArc = null; ArcProto[] fromArcs = fromPi.getPortProto().getBasePort().getConnections(); for(int j=0; j<fromArcs.length; j++) if (fromArcs[j].getFunction().isMetal()) { fromArc = fromArcs[j]; break; } if (fromArc == null) { String errorMsg = "Cannot connect port " + fromPi.getPortProto().getName() + " of node " + fromPi.getNodeInst().describe(false) + " because it has no metal connection"; System.out.println("ERROR: " + errorMsg); List<PolyBase> polyList = new ArrayList<PolyBase>(); polyList.add(fromPi.getPoly()); errorLogger.logMessage(errorMsg, polyList, cell, 0, true); continue; } // get information about the other end of the path ArcProto toArc = null; ArcProto[] toArcs = toPi.getPortProto().getBasePort().getConnections(); for(int j=0; j<toArcs.length; j++) if (toArcs[j].getFunction().isMetal()) { toArc = toArcs[j]; break; } if (toArc == null) { String errorMsg = "Cannot connect port " + toPi.getPortProto().getName() + " of node " + toPi.getNodeInst().describe(false) + " because it has no metal connection"; System.out.println("ERROR: " + errorMsg); List<PolyBase> polyList = new ArrayList<PolyBase>(); polyList.add(toPi.getPoly()); errorLogger.logMessage(errorMsg, polyList, cell, 0, true); continue; } if (fromArc.getTechnology() != tech || toArc.getTechnology() != tech) { String errorMsg = "Route from port " + fromPi.getPortProto().getName() + " of node " + fromPi.getNodeInst().describe(false) + " on arc " + fromArc.describe() + " cannot connect to port " + toPi.getPortProto().getName() + " of node " + toPi.getNodeInst().describe(false) + " on arc " + toArc.describe() + " because they have different technologies"; System.out.println("ERROR: " + errorMsg); List<PolyBase> polyList = new ArrayList<PolyBase>(); PolyBase fromPoly = fromPi.getPoly(); PolyBase toPoly = toPi.getPoly(); polyList.add(fromPoly); polyList.add(toPoly); List<EPoint> lineList = new ArrayList<EPoint>(); lineList.add(new EPoint(toPoly.getCenterX(), toPoly.getCenterY())); lineList.add(new EPoint(fromPoly.getCenterX(), fromPoly.getCenterY())); errorLogger.logMessageWithLines(errorMsg, polyList, lineList, cell, 0, true); continue; } // determine the coordinates of the route EPoint fromLoc = fromPi.getPoly().getCenter(); EPoint toLoc = toPi.getPoly().getCenter(); double fromX = fromLoc.getX(), fromY = fromLoc.getY(); double toX = toLoc.getX(), toY = toLoc.getY(); if (toLoc.getX() < fromLoc.getX()) { toX = upToGrain(toLoc.getX()); fromX = downToGrain(fromLoc.getX()); } else if (toLoc.getX() > fromLoc.getX()) { toX = downToGrain(toLoc.getX()); fromX = upToGrain(fromLoc.getX()); } else { toX = fromX = upToGrain(fromLoc.getX()); } if (toLoc.getY() < fromLoc.getY()) { toY = upToGrain(toLoc.getY()); fromY = downToGrain(fromLoc.getY()); } else if (toLoc.getY() > fromLoc.getY()) { toY = downToGrain(toLoc.getY()); fromY = upToGrain(fromLoc.getY()); } else { toY = fromY = upToGrain(fromLoc.getY()); } int fromZ = fromArc.getFunction().getLevel()-1; int toZ = toArc.getFunction().getLevel()-1; // see if access is blocked double metalSpacing = Math.max(metalArcs[fromZ].getDefaultLambdaBaseWidth(), minWidth) / 2; // determine "from" surround Layer lay = metalLayers[fromZ]; DRCTemplate rule = DRC.getSpacingRule(lay, null, lay, null, false, -1, metalArcs[fromZ].getDefaultLambdaBaseWidth(), -1); double surround = 0; if (rule != null) surround = rule.getValue(0); SOGBound block = getMetalBlockage(netID, fromZ, metalSpacing, metalSpacing, surround, fromX, fromY); if (block != null) { // see if gridding caused the blockage fromX = fromLoc.getX(); fromY = fromLoc.getY(); block = getMetalBlockage(netID, fromZ, metalSpacing, metalSpacing, surround, fromX, fromY); if (block != null) { String errorMsg = "Cannot Route to port " + fromPi.getPortProto().getName() + " of node " + fromPi.getNodeInst().describe(false) + " at (" + TextUtils.formatDistance(fromX) + "," + TextUtils.formatDistance(fromY) + ") because it is blocked on layer " + metalLayers[fromZ].getName() + " [needs " + TextUtils.formatDistance(metalSpacing+surround) + " all around, blockage is " + TextUtils.formatDistance(block.bound.getMinX()) + "<=X<=" + TextUtils.formatDistance(block.bound.getMaxX()) + " and " + TextUtils.formatDistance(block.bound.getMinY()) + "<=Y<=" + TextUtils.formatDistance(block.bound.getMaxY()) + "]"; System.out.println(errorMsg); List<PolyBase> polyList = new ArrayList<PolyBase>(); polyList.add(new PolyBase(fromX, fromY, (metalSpacing+surround)*2, (metalSpacing+surround)*2)); polyList.add(new PolyBase(block.bound)); List<EPoint> lineList = new ArrayList<EPoint>(); lineList.add(new EPoint(block.bound.getMinX(), block.bound.getMinY())); lineList.add(new EPoint(block.bound.getMaxX(), block.bound.getMaxY())); lineList.add(new EPoint(block.bound.getMinX(), block.bound.getMaxY())); lineList.add(new EPoint(block.bound.getMaxX(), block.bound.getMinY())); errorLogger.logMessageWithLines(errorMsg, polyList, lineList, cell, 0, true); continue; } } metalSpacing = Math.max(metalArcs[toZ].getDefaultLambdaBaseWidth(), minWidth) / 2; // determine "to" surround lay = metalLayers[toZ]; rule = DRC.getSpacingRule(lay, null, lay, null, false, -1, metalArcs[toZ].getDefaultLambdaBaseWidth(), -1); surround = 0; if (rule != null) surround = rule.getValue(0); block = getMetalBlockage(netID, toZ, metalSpacing, metalSpacing, surround, toX, toY); if (block != null) { // see if gridding caused the blockage toX = toLoc.getX(); toY = toLoc.getY(); block = getMetalBlockage(netID, toZ, metalSpacing, metalSpacing, surround, toX, toY); if (block != null) { String errorMsg = "Cannot route to port " + toPi.getPortProto().getName() + " of node " + toPi.getNodeInst().describe(false) + " at (" + TextUtils.formatDistance(toX) + "," + TextUtils.formatDistance(toY) + ") because it is blocked on layer " + metalLayers[toZ].getName() + " [needs " + TextUtils.formatDistance(metalSpacing+surround) + " all around, blockage is " + TextUtils.formatDistance(block.bound.getMinX()) + "<=X<=" + TextUtils.formatDistance(block.bound.getMaxX()) + " and " + TextUtils.formatDistance(block.bound.getMinY()) + "<=Y<=" + TextUtils.formatDistance(block.bound.getMaxY()) + "]"; System.out.println("ERROR: " + errorMsg); List<PolyBase> polyList = new ArrayList<PolyBase>(); polyList.add(new PolyBase(toX, toY, (metalSpacing+surround)*2, (metalSpacing+surround)*2)); polyList.add(new PolyBase(block.bound)); List<EPoint> lineList = new ArrayList<EPoint>(); lineList.add(new EPoint(block.bound.getMinX(), block.bound.getMinY())); lineList.add(new EPoint(block.bound.getMaxX(), block.bound.getMaxY())); lineList.add(new EPoint(block.bound.getMinX(), block.bound.getMaxY())); lineList.add(new EPoint(block.bound.getMaxX(), block.bound.getMinY())); errorLogger.logMessageWithLines(errorMsg, polyList, lineList, cell, 0, true); continue; } } NeededRoute nr = new NeededRoute(net.getName(), fromPi, fromX, fromY, fromZ, toPi, toX, toY, toZ, netID, minWidth, b, batchNumber++, prefs); routeBatches[b].segsInBatch++; allRoutes.add(nr); } } // now do the actual routing boolean parallel = prefs.useParallelRoutes; parallelDij = prefs.useParallelFromToRoutes; firstFailure = true; totalWireLength = 0; int numberOfProcessors = Runtime.getRuntime().availableProcessors(); if (numberOfProcessors <= 1) parallelDij = false; int numberOfThreads = numberOfProcessors; if (parallelDij) numberOfThreads /= 2; if (!parallel) numberOfThreads = 1; if (numberOfThreads == 1) parallel = false; // show what is being done System.out.println("Sea-of-gates router finding " + allRoutes.size() + " paths on " + numBatches + " networks"); if (parallel || parallelDij) { String message = "NOTE: System has " + numberOfProcessors + " processors so"; if (parallel) message += " routing " + numberOfThreads + " paths in parallel"; if (parallelDij) { if (parallel) message += " and"; message += " routing both directions of each path in parallel"; } System.out.println(message); } Environment env = Environment.getThreadEnvironment(); EditingPreferences ep = cell.getEditingPreferences(); if (numberOfThreads > 1) { doRoutingParallel(numberOfThreads, allRoutes, routeBatches, env, ep); } else { doRouting(allRoutes, routeBatches, job, env, ep); } // finally analyze the results and remove unrouted arcs int numRoutedSegments = 0; int totalRoutes = allRoutes.size(); for(int a=0; a<totalRoutes; a++) { NeededRoute nr = allRoutes.get(a); if (nr.winningWF != null && nr.winningWF.vertices != null) { routeBatches[nr.batchNumber].numRouted++; numRoutedSegments++; } else routeBatches[nr.batchNumber].numUnrouted++; } int numFailedRoutes = 0; for(int b=0; b<numBatches; b++) { if (routeBatches[b] == null) continue; if (routeBatches[b].numUnrouted == 0) { // routed: remove the unrouted arcs for(ArcInst aiKill : routeBatches[b].unroutedArcs) if (aiKill.isLinked()) aiKill.kill(); cell.killNodes(routeBatches[b].unroutedNodes); } else { numFailedRoutes++; // remove arcs that are routed List<PortInst> orderedPorts = routeBatches[b].orderedPorts; for(ArcInst aiKill : routeBatches[b].unroutedArcs) { int headPort = -1, tailPort = -1; for(int i=0; i<orderedPorts.size(); i++) { PortInst pi = orderedPorts.get(i); if (aiKill.getHeadPortInst() == pi) headPort = i; else if (aiKill.getTailPortInst() == pi) tailPort = i; } if (headPort >= 0 && tailPort >= 0) { boolean allRouted = true; if (headPort > tailPort) { int swap = headPort; headPort = tailPort; tailPort = swap; } for(NeededRoute nr : allRoutes) { if (nr.batchNumber != b) continue; if (nr.routeInBatch-1 < headPort || nr.routeInBatch-1 >= tailPort) continue; if (nr.winningWF == null || nr.winningWF.vertices == null) allRouted = false; } if (allRouted && aiKill.isLinked()) aiKill.kill(); } } } } // clean up at end errorLogger.termLogging(true); long stopTime = System.currentTimeMillis(); Job.getUserInterface().stopProgressDialog(); System.out.println("Routed " + numRoutedSegments + " out of " + totalRoutes + " segments; total length of routed wires is " + TextUtils.formatDistance(totalWireLength) + "; took " + TextUtils.getElapsedTime(stopTime-startTime)); if (numFailedRoutes > 0) System.out.println("NOTE: " + numFailedRoutes + " nets were not routed"); } /** * Method to do the routing in a single thread. * @param allRoutes the routes that need to be done. * @param routeBatches the routing batches (by network) * @param job the job that invoked this routing. */ private void doRouting(List<NeededRoute> allRoutes, RouteBatches [] routeBatches, Job job, Environment env, EditingPreferences ep) { int totalRoutes = allRoutes.size(); for(int r=0; r<totalRoutes; r++) { if (job != null && job.checkAbort()) { System.out.println("Sea-of-gates routing aborted"); break; } // get information on the segment to be routed NeededRoute nr = allRoutes.get(r); Job.getUserInterface().setProgressValue(r*100/totalRoutes); String routeName = nr.routeName; if (routeBatches[nr.batchNumber].segsInBatch > 1) routeName += " (" + nr.routeInBatch + " of " + routeBatches[nr.batchNumber].segsInBatch + ")"; Job.getUserInterface().setProgressNote("Network " + routeName); System.out.println("Routing network " + routeName + "..."); // route the segment findPath(nr, env, ep); // if the routing was good, place the results if (nr.winningWF != null && nr.winningWF.vertices != null) createRoute(nr); } } private void doRoutingParallel(int numberOfThreads, List<NeededRoute> allRoutes, RouteBatches [] routeBatches, Environment env, EditingPreferences ep) { // create threads and other threading data structures RouteInThread[] threads = new RouteInThread[numberOfThreads]; for(int i=0; i<numberOfThreads; i++) threads[i] = new RouteInThread("Route #" + (i+1), env, ep); NeededRoute [] routesToDo = new NeededRoute[numberOfThreads]; int [] routeIndices = new int[numberOfThreads]; Semaphore outSem = new Semaphore(0); // create list of routes and blocked areas List<NeededRoute> myList = new ArrayList<NeededRoute>(); for(NeededRoute nr : allRoutes) myList.add(nr); List<Rectangle2D> blocked = new ArrayList<Rectangle2D>(); // now run the threads int totalRoutes = allRoutes.size(); int routesDone = 0; while (myList.size() > 0) { int threadAssign = 0; blocked.clear(); for(int i=0; i<myList.size(); i++) { NeededRoute nr = myList.get(i); boolean isBlocked = false; for(Rectangle2D block : blocked) { if (block.intersects(nr.routeBounds)) { isBlocked = true; break; } } if (isBlocked) continue; // this route can be done: start it blocked.add(nr.routeBounds); routesToDo[threadAssign] = nr; routeIndices[threadAssign] = i; threads[threadAssign].startRoute(nr, outSem); threadAssign++; if (threadAssign >= numberOfThreads) break; } String routes = ""; for(int i=0; i<threadAssign; i++) { String routeName = routesToDo[i].routeName; if (routeBatches[routesToDo[i].batchNumber].segsInBatch > 1) routeName += "(" + routesToDo[i].routeInBatch + "/" + routeBatches[routesToDo[i].batchNumber].segsInBatch + ")"; if (routes.length() > 0) routes += ", "; routes += routeName; } System.out.println("Parallel routing " + routes + "..."); Job.getUserInterface().setProgressNote(routes); // now wait for routing threads to finish outSem.acquireUninterruptibly(threadAssign); // all done, now handle the results for(int i=0; i<threadAssign; i++) { if (routesToDo[i].winningWF != null && routesToDo[i].winningWF.vertices != null) createRoute(routesToDo[i]); } for(int i=threadAssign-1; i>=0; i--) myList.remove(routeIndices[i]); routesDone += threadAssign; Job.getUserInterface().setProgressValue(routesDone*100/totalRoutes); } // terminate the threads for(int i=0; i<numberOfThreads; i++) threads[i].startRoute(null, null); } private class RouteInThread extends Thread { private Semaphore inSem = new Semaphore(0); private NeededRoute nr; private Semaphore whenDone; private Environment env; private EditingPreferences ep; public RouteInThread(String name, Environment env, EditingPreferences ep) { super(name); this.env = env; this.ep = ep; start(); } public void startRoute(NeededRoute nr, Semaphore whenDone) { this.nr = nr; this.whenDone = whenDone; inSem.release(); } public void run() { Environment.setThreadEnvironment(env); EditingPreferences.setThreadEditingPreferences(ep); for (;;) { inSem.acquireUninterruptibly(); if (nr == null) return; findPath(nr, env, ep); whenDone.release(); } } } /** * Method to initialize technology information, including design rules. * @return true on error. */ private boolean initializeDesignRules(Cell c, SeaOfGates.SeaOfGatesOptions prefs) { // find the metal layers, arcs, and contacts cell = c; tech = cell.getTechnology(); numMetalLayers = tech.getNumMetals(); metalLayers = new Layer[numMetalLayers]; metalArcs = new ArcProto[numMetalLayers]; favorArcs = new boolean[numMetalLayers]; preventArcs = new boolean[numMetalLayers]; viaLayers = new Layer[numMetalLayers-1]; metalVias = new MetalVias[numMetalLayers-1]; for(int i=0; i<numMetalLayers-1; i++) metalVias[i] = new MetalVias(); for(Iterator<Layer> it = tech.getLayers(); it.hasNext(); ) { Layer lay = it.next(); if (!lay.getFunction().isMetal()) continue; if (lay.isPseudoLayer()) continue; int layerIndex = lay.getFunction().getLevel()-1; if (layerIndex < numMetalLayers) metalLayers[layerIndex] = lay; } boolean hasFavorites = false; for(Iterator<ArcProto> it = tech.getArcs(); it.hasNext(); ) { ArcProto ap = it.next(); for(int i=0; i<numMetalLayers; i++) { if (ap.getLayer(0) == metalLayers[i]) { metalArcs[i] = ap; favorArcs[i] = prefs.isPrevented(ap); if (favorArcs[i]) hasFavorites = true; preventArcs[i] = prefs.isPrevented(ap); break; } } } if (!hasFavorites) for(int i=0; i<numMetalLayers; i++) favorArcs[i] = true; for(Iterator<PrimitiveNode> it = tech.getNodes(); it.hasNext(); ) { PrimitiveNode np = it.next(); if (np.isNotUsed()) continue; if (!np.getFunction().isContact()) continue; ArcProto [] conns = np.getPort(0).getConnections(); for(int i=0; i<numMetalLayers-1; i++) { if ((conns[0] == metalArcs[i] && conns[1] == metalArcs[i+1]) || (conns[1] == metalArcs[i] && conns[0] == metalArcs[i+1])) { metalVias[i].addVia(np, 0); // see if the node is asymmetric and should exist in rotated states boolean square = true, offCenter = false; NodeInst dummyNi = NodeInst.makeDummyInstance(np); Poly [] conPolys = tech.getShapeOfNode(dummyNi); for(int p=0; p<conPolys.length; p++) { Poly conPoly = conPolys[p]; Layer conLayer = conPoly.getLayer(); Layer.Function lFun = conLayer.getFunction(); if (lFun.isMetal()) { Rectangle2D conRect = conPoly.getBounds2D(); if (conRect.getWidth() != conRect.getHeight()) square = false; if (conRect.getCenterX() != 0 || conRect.getCenterY() != 0) offCenter = true; } else if (lFun.isContact()) { viaLayers[i] = conLayer; } } if (offCenter) { // off center: test in all 4 rotations metalVias[i].addVia(np, 90); metalVias[i].addVia(np, 180); metalVias[i].addVia(np, 270); } else if (!square) { // centered but not square: test in 90-degree rotation metalVias[i].addVia(np, 90); } break; } } } for(int i=0; i<numMetalLayers; i++) { if (metalLayers[i] == null) { System.out.println("ERROR: Cannot find layer for Metal " + (i+1)); return true; } if (metalArcs[i] == null) { System.out.println("ERROR: Cannot find arc for Metal " + (i+1)); return true; } if (i < numMetalLayers-1) { if (metalVias[i].getVias().size() == 0) { System.out.println("ERROR: Cannot find contact node between Metal " + (i+1) + " and Metal " + (i+2)); return true; } if (viaLayers[i] == null) { System.out.println("ERROR: Cannot find contact layer between Metal " + (i+1) + " and Metal " + (i+2)); return true; } } } // compute design rule spacings worstMetalSurround = new double[numMetalLayers]; for(int i=0; i<numMetalLayers; i++) worstMetalSurround[i] = DRC.getMaxSurround(metalLayers[i], Double.MAX_VALUE); viaSurround = new double[numMetalLayers-1]; for(int i=0; i<numMetalLayers-1; i++) { Layer lay = viaLayers[i]; double spacing = 2; double arcWidth = metalArcs[i].getDefaultLambdaBaseWidth(); DRCTemplate ruleSpacing = DRC.getSpacingRule(lay, null, lay, null, false, -1, arcWidth, 50); if (ruleSpacing != null) spacing = ruleSpacing.getValue(0); // determine cut size double width = 0; DRCTemplate ruleWidth = DRC.getMinValue(lay, DRCTemplate.DRCRuleType.NODSIZ); if (ruleWidth != null) width = ruleWidth.getValue(0); // extend to the size of the largest cut List<MetalVia> nps = metalVias[i].getVias(); for(MetalVia mv : nps) { NodeInst dummyNi = NodeInst.makeDummyInstance(mv.via); Poly [] conPolys = tech.getShapeOfNode(dummyNi); for(int p=0; p<conPolys.length; p++) { Poly conPoly = conPolys[p]; if (conPoly.getLayer().getFunction().isContact()) { Rectangle2D bounds = conPoly.getBounds2D(); width = Math.max(width, bounds.getWidth()); width = Math.max(width, bounds.getHeight()); } } } viaSurround[i] = spacing + width; } return false; } private double getMinWidth(List<PortInst> orderedPorts, SeaOfGates.SeaOfGatesOptions prefs) { double minWidth = 0; for(PortInst pi : orderedPorts) { double widestAtPort = getWidestMetalArcOnPort(pi); if (widestAtPort > minWidth) minWidth = widestAtPort; } if (minWidth > prefs.maxArcWidth) minWidth = prefs.maxArcWidth; return minWidth; } /** * Get the widest metal arc already connected to a given PortInst. * Looks recursively down the hierarchy. * @param pi the PortInst to connect. * @return the widest metal arc connect to that port (zero if none) */ private double getWidestMetalArcOnPort(PortInst pi) { // first check the top level double width = 0; for (Iterator<Connection> it = pi.getConnections(); it.hasNext(); ) { Connection c = it.next(); ArcInst ai = c.getArc(); if (!ai.getProto().getFunction().isMetal()) continue; double newWidth = ai.getLambdaBaseWidth(); if (newWidth > width) width = newWidth; } // now recurse down the hierarchy NodeInst ni = pi.getNodeInst(); if (ni.isCellInstance()) { Export export = (Export)pi.getPortProto(); PortInst exportedInst = export.getOriginalPort(); double width2 = getWidestMetalArcOnPort(exportedInst); if (width2 > width) width = width2; } return width; } private boolean inValidPort(PortInst pi) { ArcProto [] conns = pi.getPortProto().getBasePort().getConnections(); boolean valid = false; for(int j=0; j<conns.length; j++) { ArcProto ap = conns[j]; if (ap.getTechnology() != tech) continue; if (!ap.getFunction().isMetal()) continue; if (preventArcs[conns[j].getFunction().getLevel()-1]) continue; valid = true; break; } if (!valid) { System.out.println("Cannot connect to port " + pi.getPortProto().getName() + " on node " + pi.getNodeInst().describe(false) + " because all connecting layers have been prevented by Routing Preferences"); return true; } return false; } /** * Method to add extra blockage information that corresponds to ends of unrouted arcs. * @param arcsToRoute the list of arcs to route. * @param tech the technology to use. */ private void addBlockagesAtPorts(List<ArcInst> arcsToRoute, SeaOfGates.SeaOfGatesOptions prefs) { Netlist netList = cell.getNetlist(); Map<Network,ArcInst[]> arcMap = null; if (cell.getView() != View.SCHEMATIC) arcMap = netList.getArcInstsByNetwork(); for(ArcInst ai : arcsToRoute) { int netID = -1; Integer netIDI = netIDs.get(ai); if (netIDI != null) { netID = -(netIDI.intValue()-1); if (netID > 0) System.out.println("INTERNAL ERROR! net="+netID+" but should be negative"); } Network net = netList.getNetwork(ai, 0); HashSet<ArcInst> arcsToDelete = new HashSet<ArcInst>(); HashSet<NodeInst> nodesToDelete = new HashSet<NodeInst>(); List<Connection> netEnds = Routing.findNetEnds(net, arcMap, arcsToDelete, nodesToDelete, netList, true); List<PortInst> orderedPorts = makeOrderedPorts(net, netEnds); if (orderedPorts == null) continue; // determine the minimum width of arcs on this net double minWidth = getMinWidth(orderedPorts, prefs); for(PortInst pi : orderedPorts) { PolyBase poly = pi.getPoly(); Rectangle2D polyBounds = poly.getBounds2D(); ArcProto[] poss = pi.getPortProto().getBasePort().getConnections(); int lowMetal = -1, highMetal = -1; for(int i=0; i<poss.length; i++) { if (poss[i].getTechnology() != tech) continue; if (!poss[i].getFunction().isMetal()) continue; int level = poss[i].getFunction().getLevel(); if (lowMetal < 0) lowMetal = highMetal = level; else { lowMetal = Math.min(lowMetal, level); highMetal = Math.max(highMetal, level); } } if (lowMetal < 0) continue; // reserve space on layers above and below for(int via = lowMetal-2; via < highMetal; via++) { if (via < 0 || via >= numMetalLayers-1) continue; MetalVia mv = metalVias[via].getVias().get(0); PrimitiveNode np = mv.via; SizeOffset so = np.getProtoSizeOffset(); double xOffset = so.getLowXOffset() + so.getHighXOffset(); double yOffset = so.getLowYOffset() + so.getHighYOffset(); double wid = Math.max(np.getDefWidth()-xOffset, minWidth) + xOffset; double hei = Math.max(np.getDefHeight()-yOffset, minWidth) + yOffset; NodeInst dummy = NodeInst.makeDummyInstance(np, EPoint.ORIGIN, wid, hei, Orientation.IDENT); PolyBase [] polys = tech.getShapeOfNode(dummy); for(int i=0; i<polys.length; i++) { PolyBase viaPoly = polys[i]; Layer layer = viaPoly.getLayer(); if (!layer.getFunction().isMetal()) continue; Rectangle2D viaBounds = viaPoly.getBounds2D(); Rectangle2D bounds = new Rectangle2D.Double(viaBounds.getMinX() + polyBounds.getCenterX(), viaBounds.getMinY() + polyBounds.getCenterY(), viaBounds.getWidth(), viaBounds.getHeight()); addRectangle(bounds, layer, netID); } } } } } /** * Method to order a set of connections for optimal routing. * @param net the Network being ordered. * @param netEnds a list of Connections that must be routed. * @return a list of PortInsts to connect which are ordered such that they * form the proper sequence of routes to make. Returns null on error. */ private List<PortInst> makeOrderedPorts(Network net, List<Connection> netEnds) { List<PortInst> portEndList = new ArrayList<PortInst>(); for(int i=0; i<netEnds.size(); i++) { PortInst pi = netEnds.get(i).getPortInst(); if (!pi.getNodeInst().isCellInstance() && ((PrimitiveNode)pi.getNodeInst().getProto()).getTechnology() == Generic.tech()) continue; if (portEndList.contains(pi)) continue; portEndList.add(pi); } int count = portEndList.size(); if (count == 0) return null; if (count == 1) { System.out.println("Error: Network " + net.describe(false) + " has only one end"); return null; } PortInst [] portEnds = new PortInst[count]; int k=0; for(PortInst pi : portEndList) portEnds[k++] = pi; // find the closest two points int closest1 = 0, closest2 = 0; double closestDist = Double.MAX_VALUE; for(int i=0; i<count; i++) { PolyBase poly1 = portEnds[i].getPoly(); for(int j=i+1; j<count; j++) { PolyBase poly2 = portEnds[j].getPoly(); double dist = poly1.getCenter().distance(poly2.getCenter()); if (dist < closestDist) { closestDist = dist; closest1 = i; closest2 = j; } } } List<PortInst> orderedPorts = new ArrayList<PortInst>(); orderedPorts.add(portEnds[closest1]); orderedPorts.add(portEnds[closest2]); portEnds[closest1] = null; portEnds[closest2] = null; for(;;) { // find closest port to ends of current string boolean foundsome = false; double closestDist1 = Double.MAX_VALUE, closestDist2 = Double.MAX_VALUE; for(int i=0; i<count; i++) { if (portEnds[i] == null) continue; PolyBase poly = portEnds[i].getPoly(); PolyBase poly1 = orderedPorts.get(0).getPoly(); double dist1 = poly.getCenter().distance(poly1.getCenter()); if (dist1 < closestDist1) { closestDist1 = dist1; closest1 = i; foundsome = true; } PolyBase poly2 = orderedPorts.get(orderedPorts.size()-1).getPoly(); double dist2 = poly.getCenter().distance(poly2.getCenter()); if (dist2 < closestDist2) { closestDist2 = dist2; closest2 = i; foundsome = true; } } if (!foundsome) break; if (closestDist1 < closestDist2) { orderedPorts.add(0, portEnds[closest1]); portEnds[closest1] = null; } else { orderedPorts.add(portEnds[closest2]); portEnds[closest2] = null; } } return orderedPorts; } /** * Method to create the geometry for a route. * Places nodes and arcs to make the route, and also updates the R-Tree data structure. * @param nr the route information. */ private void createRoute(NeededRoute nr) { Wavefront wf = nr.winningWF; PortInst lastPort = wf.to; PolyBase toPoly = wf.to.getPoly(); if (toPoly.getCenterX() != wf.toX || toPoly.getCenterY() != wf.toY) { // end of route is off-grid: adjust it if (wf.vertices.size() >= 2) { SearchVertex v1 = wf.vertices.get(0); SearchVertex v2 = wf.vertices.get(1); ArcProto type = metalArcs[wf.toZ]; double width = Math.max(type.getDefaultLambdaBaseWidth(), nr.minWidth); PrimitiveNode np = metalArcs[wf.toZ].findPinProto(); if (v1.getX() == v2.getX()) { // first line is vertical: run a horizontal bit NodeInst ni = makeNodeInst(np, new EPoint(v1.getX(), toPoly.getCenterY()), np.getDefWidth(), np.getDefHeight(), Orientation.IDENT, cell, nr.netID); makeArcInst(type, width, ni.getOnlyPortInst(), wf.to, nr.netID); lastPort = ni.getOnlyPortInst(); } else if (v1.getY() == v2.getY()) { // first line is horizontal: run a vertical bit NodeInst ni = makeNodeInst(np, new EPoint(toPoly.getCenterX(), v1.getY()), np.getDefWidth(), np.getDefHeight(), Orientation.IDENT, cell, nr.netID); makeArcInst(type, width, ni.getOnlyPortInst(), wf.to, nr.netID); lastPort = ni.getOnlyPortInst(); } } } for(int i=0; i<wf.vertices.size(); i++) { SearchVertex sv = wf.vertices.get(i); boolean madeContacts = false; while (i < wf.vertices.size()-1) { SearchVertex svNext = wf.vertices.get(i+1); if (sv.getX() != svNext.getX() || sv.getY() != svNext.getY() || sv.getZ() == svNext.getZ()) break; List<MetalVia> nps = metalVias[Math.min(sv.getZ(), svNext.getZ())].getVias(); int whichContact = sv.getContactNo(); MetalVia mv = nps.get(whichContact); PrimitiveNode np = mv.via; Orientation orient = Orientation.fromJava(mv.orientation*10, false, false); SizeOffset so = np.getProtoSizeOffset(); double xOffset = so.getLowXOffset() + so.getHighXOffset(); double yOffset = so.getLowYOffset() + so.getHighYOffset(); double wid = Math.max(np.getDefWidth()-xOffset, nr.minWidth) + xOffset; double hei = Math.max(np.getDefHeight()-yOffset, nr.minWidth) + yOffset; NodeInst ni = makeNodeInst(np, new EPoint(sv.getX(), sv.getY()), wid, hei, orient, cell, nr.netID); ArcProto type = metalArcs[sv.getZ()]; double width = Math.max(type.getDefaultLambdaBaseWidth(), nr.minWidth); makeArcInst(type, width, lastPort, ni.getOnlyPortInst(), nr.netID); lastPort = ni.getOnlyPortInst(); madeContacts = true; sv = svNext; i++; } if (madeContacts && i != wf.vertices.size()-1) continue; PrimitiveNode np = metalArcs[sv.getZ()].findPinProto(); PortInst pi = null; if (i == wf.vertices.size()-1) { pi = wf.from; PolyBase fromPoly = wf.from.getPoly(); if (fromPoly.getCenterX() != sv.getX() || fromPoly.getCenterY() != sv.getY()) { // end of route is off-grid: adjust it if (wf.vertices.size() >= 2) { SearchVertex v1 = wf.vertices.get(wf.vertices.size()-2); SearchVertex v2 = wf.vertices.get(wf.vertices.size()-1); ArcProto type = metalArcs[wf.fromZ]; double width = Math.max(type.getDefaultLambdaBaseWidth(), nr.minWidth); if (v1.getX() == v2.getX()) { // last line is vertical: run a horizontal bit PrimitiveNode pNp = metalArcs[wf.fromZ].findPinProto(); NodeInst ni = makeNodeInst(pNp, new EPoint(v1.getX(), fromPoly.getCenterY()), np.getDefWidth(), np.getDefHeight(), Orientation.IDENT, cell, nr.netID); makeArcInst(type, width, ni.getOnlyPortInst(), wf.from, nr.netID); pi = ni.getOnlyPortInst(); } else if (v1.getY() == v2.getY()) { // last line is horizontal: run a vertical bit PrimitiveNode pNp = metalArcs[wf.fromZ].findPinProto(); NodeInst ni = makeNodeInst(pNp, new EPoint(fromPoly.getCenterX(), v1.getY()), np.getDefWidth(), np.getDefHeight(), Orientation.IDENT, cell, nr.netID); makeArcInst(type, width, ni.getOnlyPortInst(), wf.from, nr.netID); pi = ni.getOnlyPortInst(); } } } } else { NodeInst ni = makeNodeInst(np, new EPoint(sv.getX(), sv.getY()), np.getDefWidth(), np.getDefHeight(), Orientation.IDENT, cell, nr.netID); pi = ni.getOnlyPortInst(); } if (lastPort != null) { ArcProto type = metalArcs[sv.getZ()]; double width = Math.max(type.getDefaultLambdaBaseWidth(), nr.minWidth); makeArcInst(type, width, lastPort, pi, nr.netID); } lastPort = pi; } // now see how far out of the bounding rectangle the route went Rectangle2D routeBounds = new Rectangle2D.Double(Math.min(wf.fromX,wf.toX), Math.min(wf.fromY,wf.toY), Math.abs(wf.fromX-wf.toX), Math.abs(wf.fromY-wf.toY)); double lowX = routeBounds.getMinX(), highX = routeBounds.getMaxX(); double lowY = routeBounds.getMinY(), highY = routeBounds.getMaxY(); for(int i=0; i<wf.vertices.size(); i++) { SearchVertex sv = wf.vertices.get(i); if (i == 0) { lowX = highX = sv.getX(); lowY = highY = sv.getY(); } else { if (sv.getX() < lowX) lowX = sv.getX(); if (sv.getX() > highX) highX = sv.getX(); if (sv.getY() < lowY) lowY = sv.getY(); if (sv.getY() > highY) highY = sv.getY(); } } } /** * Method to sum up the distance that a route takes. * @param vertices the list of SearchVertices in the route. * @return the length of the route. */ private double getVertexLength(List<SearchVertex> vertices) { if (vertices == null) return Double.MAX_VALUE; if (vertices.size() == 0) return Double.MAX_VALUE; double sum = 0; SearchVertex last = null; for(SearchVertex sv : vertices) { if (last != null) sum += Math.abs(sv.getX() - last.getX()) + Math.abs(sv.getY() - last.getY()) + Math.abs(sv.getZ() - last.getZ())*10; last = sv; } return sum; } /** * Method to create a NodeInst and update the R-Trees. * @param np the prototype of the new NodeInst. * @param loc the location of the new NodeInst. * @param wid the width of the new NodeInst. * @param hei the height of the new NodeInst. * @param orient the orientation of the new NodeInst. * @param cell the Cell in which to place the new NodeInst. * @param netID the network ID of geometry in this NodeInst. * @return the NodeInst that was created (null on error). */ private NodeInst makeNodeInst(NodeProto np, EPoint loc, double wid, double hei, Orientation orient, Cell cell, int netID) { NodeInst ni = NodeInst.makeInstance(np, loc, wid, hei, cell, orient, null); if (ni != null) { AffineTransform trans = ni.rotateOut(); Poly [] nodeInstPolyList = tech.getShapeOfNode(ni, true, false, null); for(int i=0; i<nodeInstPolyList.length; i++) { PolyBase poly = nodeInstPolyList[i]; if (poly.getPort() == null) continue; poly.transform(trans); addLayer(poly, GenMath.MATID, netID, false); } } return ni; } /** * Method to create an ArcInst and update the R-Trees. * @param type the prototype of the new ArcInst. * @param wid the width of the new ArcInst. * @param from the head PortInst of the new ArcInst. * @param to the tail PortInst of the new ArcInst. * @param netID the network ID of geometry in this ArcInst. * @return the ArcInst that was created (null on error). */ private ArcInst makeArcInst(ArcProto type, double wid, PortInst from, PortInst to, int netID) { ArcInst ai = ArcInst.makeInstanceBase(type, wid, from, to); if (ai != null) { PolyBase [] polys = tech.getShapeOfArc(ai); for(int i=0; i<polys.length; i++) addLayer(polys[i], GenMath.MATID, netID, false); // accumulate the total length of wires placed PolyBase fromPoly = from.getPoly(); PolyBase toPoly = to.getPoly(); double length = fromPoly.getCenter().distance(toPoly.getCenter()); totalWireLength += length; } return ai; } /** * Method to find a path between two ports. * @param nr the NeededRoute object with all necessary information. * If successful, the NeededRoute's "vertices" field is filled with the route data. */ private void findPath(NeededRoute nr, Environment env, EditingPreferences ep) { // special case when route is null length Wavefront d1 = nr.dir1; if (DBMath.areEquals(d1.toX, d1.fromX) && DBMath.areEquals(d1.toY, d1.fromY) && d1.toZ == d1.fromZ) { nr.winningWF = d1; nr.winningWF.vertices = new ArrayList<SearchVertex>(); SearchVertex sv = new SearchVertex(d1.toX, d1.toY, d1.toZ, 0, null, 0, nr.winningWF); nr.winningWF.vertices.add(sv); nr.cleanSearchMemory(); return; } if (parallelDij) { // create threads and start them running Semaphore outSem = new Semaphore(0); new DijkstraInThread("Route a->b", nr.dir1, nr.dir2, outSem, env, ep); new DijkstraInThread("Route b->a", nr.dir2, nr.dir1, outSem, env, ep); // wait for threads to complete and get results outSem.acquireUninterruptibly(2); } else { // run both wavefronts in parallel (interleaving steps) doTwoWayDijkstra(nr); } // analyze the winning wavefront Wavefront wf = nr.winningWF; double verLength = Double.MAX_VALUE; if (wf != null) verLength = getVertexLength(wf.vertices); if (verLength == Double.MAX_VALUE) { // failed to route String errorMsg; if (wf == null) wf = nr.dir1; if (wf.vertices == null) { errorMsg = "Search too complex (exceeds complexity limit of " + nr.prefs.complexityLimit + " steps)"; } else { errorMsg = "Failed to route from port " + wf.from.getPortProto().getName() + " of node " + wf.from.getNodeInst().describe(false) + " to port " + wf.to.getPortProto().getName() + " of node " + wf.to.getNodeInst().describe(false); } System.out.println("ERROR: " + errorMsg); List<EPoint> lineList = new ArrayList<EPoint>(); lineList.add(new EPoint(wf.toX, wf.toY)); lineList.add(new EPoint(wf.fromX, wf.fromY)); errorLogger.logMessageWithLines(errorMsg, null, lineList, cell, 0, true); if (DEBUGFAILURE && firstFailure) { firstFailure = false; EditWindow_ wnd = Job.getUserInterface().getCurrentEditWindow_(); wnd.clearHighlighting(); showSearchVertices(nr.dir1.searchVertexPlanes, false); wnd.finishedHighlighting(); } } nr.cleanSearchMemory(); } private void doTwoWayDijkstra(NeededRoute nr) { SearchVertex result = null; int numSearchVertices = 0; while (result == null) { // stop if the search is too complex numSearchVertices++; if (numSearchVertices > nr.prefs.complexityLimit) return; SearchVertex resultA = advanceWavefront(nr.dir1); SearchVertex resultB = advanceWavefront(nr.dir2); if (resultA != null || resultB != null) { result = resultA; nr.winningWF = nr.dir1; if (result == null || result == svExhausted) { result = resultB; nr.winningWF = nr.dir2; } } } if (result == svAborted || result == svExhausted) { nr.winningWF = null; return; } // dumpPlane(0); // dumpPlane(1); // dumpPlane(2); List<SearchVertex> realVertices = getOptimizedList(result); nr.winningWF.vertices = realVertices; } private class DijkstraInThread extends Thread { private Wavefront wf; private Wavefront otherWf; private Semaphore whenDone; private Environment env; private EditingPreferences ep; public DijkstraInThread(String name, Wavefront wf, Wavefront otherWf, Semaphore whenDone, Environment env, EditingPreferences ep) { super(name); this.wf = wf; this.otherWf = otherWf; this.whenDone = whenDone; this.env = env; this.ep = ep; start(); } public void run() { Environment.setThreadEnvironment(env); EditingPreferences.setThreadEditingPreferences(ep); SearchVertex result = null; int numSearchVertices = 0; while (result == null) { // stop if the search is too complex numSearchVertices++; if (numSearchVertices > wf.nr.prefs.complexityLimit) { result = svLimited; } else { if (wf.abort) result = svAborted; else { result = advanceWavefront(wf); } } } if (result != svAborted && result != svExhausted && result != svLimited) { if (DEBUGLOOPS) System.out.println(" Wavefront " + wf.name + " first completion"); wf.vertices = getOptimizedList(result); wf.nr.winningWF = wf; otherWf.abort = true; } else { if (DEBUGLOOPS) { String status = "completed"; if (result == svAborted) status = "aborted"; else if (result == svExhausted) status = "exhausted"; else if (result == svLimited) status = "limited"; System.out.println(" Wavefront " + wf.name + " " + status); } } whenDone.release(); } } private SearchVertex advanceWavefront(Wavefront wf) { // get the lowest cost point if (wf.active.size() == 0) return svExhausted; SearchVertex svCurrent = wf.active.first(); wf.active.remove(svCurrent); double curX = svCurrent.getX(); double curY = svCurrent.getY(); int curZ = svCurrent.getZ(); if (wf.debug) System.out.print("AT ("+TextUtils.formatDouble(curX)+","+TextUtils.formatDouble(curY)+",M" +(curZ+1)+")C="+svCurrent.cost+" WENT"); // look at all directions from this point for(int i=0; i<6; i++) { // compute a neighboring point double dx = 0, dy = 0; int dz = 0; switch (i) { case 0: dx = -GRAINSIZE; if (FULLGRAIN) { double intermediate = upToGrainAlways(curX+dx); if (intermediate != curX+dx) dx = intermediate - curX; } break; case 1: dx = GRAINSIZE; if (FULLGRAIN) { double intermediate = downToGrainAlways(curX+dx); if (intermediate != curX+dx) dx = intermediate - curX; } break; case 2: dy = -GRAINSIZE; if (FULLGRAIN) { double intermediate = upToGrainAlways(curY+dy); if (intermediate != curY+dy) dy = intermediate - curY; } break; case 3: dy = GRAINSIZE; if (FULLGRAIN) { double intermediate = downToGrainAlways(curY+dy); if (intermediate != curY+dy) dy = intermediate - curY; } break; case 4: dz = -1; break; case 5: dz = 1; break; } // extend the distance if heading toward the goal boolean stuck = false; if (dz == 0) { boolean goFarther = false; if (dx != 0) { if ((wf.toX-curX) * dx > 0) goFarther = true; } else { if ((wf.toY-curY) * dy > 0) goFarther = true; } if (goFarther) { double jumpSize = getJumpSize(curX, curY, curZ, dx, dy, wf); if (dx > 0) { if (jumpSize <= 0) stuck = true; dx = jumpSize; } if (dx < 0) { if (jumpSize >= 0) stuck = true; dx = jumpSize; } if (dy > 0) { if (jumpSize <= 0) stuck = true; dy = jumpSize; } if (dy < 0) { if (jumpSize >= 0) stuck = true; dy = jumpSize; } } } double nX = curX + dx; double nY = curY + dy; int nZ = curZ + dz; if (wf.debug) { switch (i) { case 0: System.out.print(" X-"+TextUtils.formatDouble(Math.abs(dx))); break; case 1: System.out.print(" X+"+TextUtils.formatDouble(dx)); break; case 2: System.out.print(" Y-"+TextUtils.formatDouble(Math.abs(dy))); break; case 3: System.out.print(" Y+"+TextUtils.formatDouble(dy)); break; case 4: System.out.print(" -Z"); break; case 5: System.out.print(" +Z"); break; } } if (stuck) { if (wf.debug) System.out.print(":CannotMove"); continue; } if (nX < wf.nr.minimumSearchBoundX) { nX = wf.nr.minimumSearchBoundX; dx = nX - curX; if (dx == 0) { if (wf.debug) System.out.print(":OutOfBounds"); continue; } } if (nX > wf.nr.maximumSearchBoundX) { nX = wf.nr.maximumSearchBoundX; dx = nX - curX; if (dx == 0) { if (wf.debug) System.out.print(":OutOfBounds"); continue; } } if (nY < wf.nr.minimumSearchBoundY) { nY = wf.nr.minimumSearchBoundY; dy = nY - curY; if (dy == 0) { if (wf.debug) System.out.print(":OutOfBounds"); continue; } } if (nY > wf.nr.maximumSearchBoundY) { nY = wf.nr.maximumSearchBoundY; dy = nY - curY; if (dy == 0) { if (wf.debug) System.out.print(":OutOfBounds"); continue; } } if (nZ < 0 || nZ >= numMetalLayers) { if (wf.debug) System.out.print(":OutOfBounds"); continue; } if (preventArcs[nZ]) { if (wf.debug) System.out.print(":IllegalArc"); continue; } // see if the adjacent point has already been visited if (wf.getVertex(nX, nY, nZ)) { if (wf.debug) System.out.print(":AlreadyVisited"); continue; } // see if the space is available int whichContact = 0; Point2D [] cuts = null; if (dz == 0) { // running on one layer: check surround double width = Math.max(metalArcs[nZ].getDefaultLambdaBaseWidth(), wf.nr.minWidth); double metalSpacing = width / 2; boolean allClear = false; for(;;) { SearchVertex prevPath = svCurrent; double checkX = (curX+nX)/2, checkY = (curY+nY)/2; double halfWid = metalSpacing + Math.abs(dx)/2; double halfHei = metalSpacing + Math.abs(dy)/2; while (prevPath != null && prevPath.last != null) { if (prevPath.zv != nZ || prevPath.last.zv != nZ) break; if (prevPath.xv == prevPath.last.xv && dx == 0) { checkY = (prevPath.last.yv + nY) / 2; halfHei = metalSpacing + Math.abs(prevPath.last.yv - nY)/2; prevPath = prevPath.last; } else if (prevPath.yv == prevPath.last.yv && dy == 0) { checkX = (prevPath.last.xv + nX) / 2; halfWid = metalSpacing + Math.abs(prevPath.last.xv - nX)/2; prevPath = prevPath.last; } else break; } SOGBound sb = getMetalBlockageAndNotch(wf, nZ, halfWid, halfHei, checkX, checkY, prevPath); if (sb == null) { allClear = true; break; } // see if it can be backed out slightly if (i == 0) { // moved left too far...try a bit to the right double newNX = downToGrainAlways(nX + GRAINSIZE); if (newNX >= curX) break; dx = newNX - curX; } else if (i == 1) { // moved right too far...try a bit to the left double newNX = upToGrainAlways(nX - GRAINSIZE); if (newNX <= curX) break; dx = newNX - curX; } else if (i == 2) { // moved down too far...try a bit up double newNY = downToGrainAlways(nY + GRAINSIZE); if (newNY >= curY) break; double newDY = newNY - curY; dy = newDY; } else if (i == 3) { // moved up too far...try a bit down double newNY = upToGrainAlways(nY - GRAINSIZE); if (newNY <= curY) break; dy = newNY - curY; } // if (!getVertex(nX, nY, nZ)) setVertex(nX, nY, nZ); nX = curX + dx; nY = curY + dy; } if (!allClear) { if (wf.debug) { double checkX = (curX+nX)/2, checkY = (curY+nY)/2; double halfWid = metalSpacing + Math.abs(dx)/2; double halfHei = metalSpacing + Math.abs(dy)/2; double surround = worstMetalSurround[nZ]; SOGBound sb = getMetalBlockage(wf.nr.netID, nZ, halfWid, halfHei, surround, checkX, checkY); if (sb != null) System.out.print(":Blocked"); else System.out.print(":BlockedNotch"); } continue; } } else { int lowMetal = Math.min(curZ, nZ); int highMetal = Math.max(curZ, nZ); List<MetalVia> nps = metalVias[lowMetal].getVias(); whichContact = -1; for(int contactNo = 0; contactNo < nps.size(); contactNo++) { MetalVia mv = nps.get(contactNo); PrimitiveNode np = mv.via; Orientation orient = Orientation.fromJava(mv.orientation*10, false, false); SizeOffset so = np.getProtoSizeOffset(); double conWid = Math.max(np.getDefWidth()-so.getLowXOffset()-so.getHighXOffset(), wf.nr.minWidth)+ so.getLowXOffset()+so.getHighXOffset(); double conHei = Math.max(np.getDefHeight()-so.getLowYOffset()-so.getHighYOffset(), wf.nr.minWidth)+ so.getLowYOffset()+so.getHighYOffset(); NodeInst dummyNi = NodeInst.makeDummyInstance(np, new EPoint(nX, nY), conWid, conHei, orient); Poly [] conPolys = tech.getShapeOfNode(dummyNi); AffineTransform trans = null; if (orient != Orientation.IDENT) trans = dummyNi.rotateOut(); // count the number of cuts and make an array for the data int cutCount = 0; for(int p=0; p<conPolys.length; p++) if (conPolys[p].getLayer().getFunction().isContact()) cutCount++; Point2D [] curCuts = new Point2D[cutCount]; cutCount = 0; boolean failed = false; for(int p=0; p<conPolys.length; p++) { Poly conPoly = conPolys[p]; if (trans != null) conPoly.transform(trans); Layer conLayer = conPoly.getLayer(); Layer.Function lFun = conLayer.getFunction(); if (lFun.isMetal()) { Rectangle2D conRect = conPoly.getBounds2D(); int metalNo = lFun.getLevel() - 1; double halfWid = conRect.getWidth()/2; double halfHei = conRect.getHeight()/2; if (getMetalBlockageAndNotch(wf, metalNo, halfWid, halfHei, conRect.getCenterX(), conRect.getCenterY(), svCurrent) != null) { failed = true; break; } } else if (lFun.isContact()) { // make sure vias don't get too close Rectangle2D conRect = conPoly.getBounds2D(); double conCX = conRect.getCenterX(); double conCY = conRect.getCenterY(); double surround = viaSurround[lowMetal]; if (getViaBlockage(wf.nr.netID, conLayer, surround, surround, conCX, conCY) != null) { failed = true; break; } curCuts[cutCount++] = new Point2D.Double(conCX, conCY); // look at all previous cuts in this path for(SearchVertex sv = svCurrent; sv != null; sv = sv.last) { SearchVertex lastSv = sv.last; if (lastSv == null) break; if (Math.min(sv.getZ(), lastSv.getZ()) == lowMetal && Math.max(sv.getZ(), lastSv.getZ()) == highMetal) { // make sure the cut isn't too close Point2D [] svCuts; if (sv.getCutLayer() == lowMetal) svCuts = sv.getCuts(); else svCuts = lastSv.getCuts(); if (svCuts != null) { for(Point2D cutPt : svCuts) { if (Math.abs(cutPt.getX() - conCX) >= surround || Math.abs(cutPt.getY() - conCY) >= surround) continue; failed = true; break; } } if (failed) break; } } if (failed) break; } } if (failed) continue; whichContact = contactNo; cuts = curCuts; break; } if (whichContact < 0) { if (wf.debug) System.out.print(":Blocked"); continue; } } // we have a candidate next-point SearchVertex svNext = new SearchVertex(nX, nY, nZ, whichContact, cuts, Math.min(curZ, nZ), wf); svNext.last = svCurrent; // stop if we found the destination boolean foundDest = DBMath.areEquals(nX, wf.toX) && DBMath.areEquals(nY, wf.toY); if (foundDest && nZ == wf.toZ) { if (wf.debug) System.out.print(":FoundDestination"); return svNext; } // compute the cost svNext.cost = svCurrent.cost; if (dx != 0) { if (wf.toX == curX) svNext.cost += COSTWRONGDIRECTION/2; else if ((wf.toX-curX) * dx < 0) svNext.cost += COSTWRONGDIRECTION; if ((nZ%2) == 0) { int c = COSTALTERNATINGMETAL * (int)Math.abs(dx) / (int)cellBounds.getWidth(); svNext.cost += c; } } if (dy != 0) { if (wf.toY == curY) svNext.cost += COSTWRONGDIRECTION/2; else if ((wf.toY-curY) * dy < 0) svNext.cost += COSTWRONGDIRECTION; if ((nZ%2) != 0) { int c = COSTALTERNATINGMETAL * (int)Math.abs(dy) / (int)cellBounds.getHeight(); svNext.cost += c; } } if (dz != 0) { if (wf.toZ == curZ) svNext.cost += COSTLAYERCHANGE; else if ((wf.toZ-curZ) * dz < 0) svNext.cost += COSTLAYERCHANGE * COSTWRONGDIRECTION; } else { // not changing layers: compute penalty for unused tracks on either side of run double jumpSize1 = Math.abs(getJumpSize(nX, nY, nZ, dx, dy, wf)); double jumpSize2 = Math.abs(getJumpSize(curX, curY, curZ, -dx, -dy, wf)); if (jumpSize1 > GRAINSIZE && jumpSize2 > GRAINSIZE) { svNext.cost += (jumpSize1 * jumpSize2) / 10; } // not changing layers: penalize if turning in X or Y if (svCurrent.last != null) { boolean xTurn = svCurrent.getX() != svCurrent.last.getX(); boolean yTurn = svCurrent.getY() != svCurrent.last.getY(); if (xTurn != (dx != 0) || yTurn != (dy != 0)) svNext.cost += COSTTURNING; } } if (!favorArcs[nZ]) svNext.cost += (COSTLAYERCHANGE*COSTUNFAVORED)*Math.abs(dz) + COSTUNFAVORED*Math.abs(dx + dy); if (downToGrainAlways(nX) != nX && nX != wf.toX) svNext.cost += COSTOFFGRID; if (downToGrainAlways(nY) != nY && nY != wf.toY) svNext.cost += COSTOFFGRID; // add this vertex into the data structures wf.setVertex(nX, nY, nZ); wf.active.add(svNext); if (wf.debug) System.out.print("("+TextUtils.formatDouble(svNext.getX())+","+TextUtils.formatDouble(svNext.getY())+ ",M"+(svNext.getZ()+1)+")C="+svNext.cost); // add intermediate steps along the way if it was a jump if (dz == 0) { if (Math.abs(dx) > 1) { double inc = dx < 0 ? 1 : -1; double lessDX = dx + inc; int cost = svNext.cost; int countDown = 0; for(;;) { double nowX = curX+lessDX; cost++; if (!wf.getVertex(nowX, nY, nZ)) { SearchVertex svIntermediate = new SearchVertex(nowX, nY, nZ, whichContact, cuts, curZ, wf); svIntermediate.last = svCurrent; svIntermediate.cost = cost; wf.setVertex(nowX, nY, nZ); wf.active.add(svIntermediate); } lessDX += inc; if (inc < 0) { if (lessDX < 1) break; } else { if (lessDX > -1) break; } if (countDown++ >= 10) { countDown = 0; inc += inc; } } } if (Math.abs(dy) > 1) { double inc = dy < 0 ? 1 : -1; double lessDY = dy + inc; int cost = svNext.cost; int countDown = 0; for(;;) { double nowY = curY+lessDY; cost++; if (!wf.getVertex(nX, nowY, nZ)) { SearchVertex svIntermediate = new SearchVertex(nX, nowY, nZ, whichContact, cuts, curZ, wf); svIntermediate.last = svCurrent; svIntermediate.cost = cost; wf.setVertex(nX, nowY, nZ); wf.active.add(svIntermediate); } lessDY += inc; if (inc < 0) { if (lessDY < 1) break; } else { if (lessDY > -1) break; } if (countDown++ >= 10) { countDown = 0; inc += inc; } } } } } if (wf.debug) System.out.println(); return null; } /** * Method to convert a linked list of SearchVertex objects to an optimized path. * @param initialThread the initial SearchVertex in the linked list. * @return a List of SearchVertex objects optimized to consolidate runs in the X * or Y axes. */ private List<SearchVertex> getOptimizedList(SearchVertex initialThread) { List<SearchVertex> realVertices = new ArrayList<SearchVertex>(); SearchVertex thread = initialThread; if (thread != null) { SearchVertex lastVertex = thread; realVertices.add(lastVertex); thread = thread.last; while (thread != null) { if (lastVertex.getZ() != thread.getZ()) { realVertices.add(thread); lastVertex = thread; thread = thread.last; } else { // gather a run of vertices on this layer double dx = thread.getX() - lastVertex.getX(); double dy = thread.getY() - lastVertex.getY(); lastVertex = thread; thread = thread.last; while (thread != null) { if (lastVertex.getZ() != thread.getZ()) break; if ((thread.getX() - lastVertex.getX() != 0 && dx == 0) || (thread.getY() - lastVertex.getY() != 0 && dy == 0)) break; lastVertex = thread; thread = thread.last; } realVertices.add(lastVertex); } } } return realVertices; } private double getJumpSize(double curX, double curY, int curZ, double dx, double dy, Wavefront wf) { Rectangle2D jumpBound = wf.nr.jumpBound; double width = Math.max(metalArcs[curZ].getDefaultLambdaBaseWidth(), wf.nr.minWidth); double metalToMetal = wf.getSpacingRule(curZ, width, -1); double metalSpacing = width / 2 + metalToMetal; double lX = curX - metalSpacing, hX = curX + metalSpacing; double lY = curY - metalSpacing, hY = curY + metalSpacing; if (dx > 0) hX = jumpBound.getMaxX()+metalSpacing; else if (dx < 0) lX = jumpBound.getMinX()-metalSpacing; else if (dy > 0) hY = jumpBound.getMaxY()+metalSpacing; else if (dy < 0) lY = jumpBound.getMinY()-metalSpacing; RTNode rtree = metalTrees.get(metalLayers[curZ]); if (rtree != null) { // see if there is anything in that area Rectangle2D searchArea = new Rectangle2D.Double(lX, lY, hX-lX, hY-lY); for(RTNode.Search sea = new RTNode.Search(searchArea, rtree, true); sea.hasNext(); ) { SOGBound sBound = (SOGBound)sea.next(); if (Math.abs(sBound.getNetID()) == wf.nr.netID) continue; Rectangle2D bound = sBound.getBounds(); if (bound.getMinX() >= hX || bound.getMaxX() <= lX || bound.getMinY() >= hY || bound.getMaxY() <= lY) continue; if (dx > 0 && bound.getMinX() < hX) hX = bound.getMinX(); if (dx < 0 && bound.getMaxX() > lX) lX = bound.getMaxX(); if (dy > 0 && bound.getMinY() < hY) hY = bound.getMinY(); if (dy < 0 && bound.getMaxY() > lY) lY = bound.getMaxY(); } } if (dx > 0) { dx = downToGrain(hX-metalSpacing)-curX; if (curX+dx > wf.toX && curY == wf.toY && curZ == wf.toZ) dx = wf.toX-curX; if (curX+dx != wf.toX && FULLGRAIN) dx = downToGrainAlways(hX-metalSpacing)-curX; return dx; } if (dx < 0) { dx = upToGrain(lX+metalSpacing)-curX; if (curX+dx < wf.toX && curY == wf.toY && curZ == wf.toZ) dx = wf.toX-curX; if (curX+dx != wf.toX && FULLGRAIN) dx = upToGrainAlways(lX+metalSpacing)-curX; return dx; } if (dy > 0) { dy = downToGrain(hY-metalSpacing)-curY; if (curX == wf.toX && curY+dy > wf.toY && curZ == wf.toZ) dy = wf.toY-curY; if (curY+dy != wf.toY && FULLGRAIN) dy = downToGrainAlways(hY-metalSpacing)-curY; return dy; } if (dy < 0) { dy = upToGrain(lY+metalSpacing)-curY; if (curX == wf.toX && curY+dy < wf.toY && curZ == wf.toZ) dy = wf.toY-curY; if (curY+dy != wf.toY && FULLGRAIN) dy = upToGrainAlways(lY+metalSpacing)-curY; return dy; } return 0; } /** * Method to round a value up to the nearest routing grain size. * @param v the value to round up. * @return the granularized value. */ private double upToGrain(double v) { if (FULLGRAIN) return v; return upToGrainAlways(v); } /** * Method to round a value up to the nearest routing grain size. * @param v the value to round up. * @return the granularized value. */ private double upToGrainAlways(double v) { return Math.ceil(v * GRANULARITY) * GRAINSIZE; } /** * Method to round a value down to the nearest routing grain size. * @param v the value to round down. * @return the granularized value. */ private double downToGrain(double v) { if (FULLGRAIN) return v; return downToGrainAlways(v); } /** * Method to round a value down to the nearest routing grain size. * @param v the value to round down. * @return the granularized value. */ private double downToGrainAlways(double v) { return Math.floor(v * GRANULARITY) * GRAINSIZE; } /************************************** BLOCKAGE DATA STRUCTURE **************************************/ /** * Class to define an R-Tree leaf node for geometry in the blockage data structure. */ private static class SOGBound implements RTBounds { private Rectangle2D bound; private int netID; SOGBound(Rectangle2D bound, int netID) { this.bound = bound; this.netID = netID; } public Rectangle2D getBounds() { return bound; } public int getNetID() { return netID; } public String toString() { return "SOGBound on net " + netID; } } private static class SOGPoly extends SOGBound { private PolyBase poly; SOGPoly(Rectangle2D bound, int netID, PolyBase poly) { super(bound, netID); this.poly = poly; } public PolyBase getPoly() { return poly; } } /** * Class to define an R-Tree leaf node for vias in the blockage data structure. */ private static class SOGVia implements RTBounds { private Point2D loc; private int netID; SOGVia(Point2D loc, int netID) { this.loc = loc; this.netID = netID; } public Rectangle2D getBounds() { return new Rectangle2D.Double(loc.getX(), loc.getY(), 0, 0); } public int getNetID() { return netID; } public String toString() { return "SOGVia on net " + netID; } } /** * Method to see if a proposed piece of metal has DRC errors. * @param wf the Wavefront being processed. * @param metNo the level of the metal. * @param halfWidth half of the width of the metal. * @param halfHeight half of the height of the metal. * @param x the X coordinate at the center of the metal. * @param y the Y coordinate at the center of the metal. * @param svCurrent the list of SearchVertex's for finding notch errors in the current path. * @return a blocking SOGBound object that is in the area. * Returns null if the area is clear. */ private SOGBound getMetalBlockageAndNotch(Wavefront wf, int metNo, double halfWidth, double halfHeight, double x, double y, SearchVertex svCurrent) { // get the R-Tree data for the metal layer Layer layer = metalLayers[metNo]; RTNode rtree = metalTrees.get(layer); if (rtree == null) return null; // determine the size and width/length of this piece of metal int netID = wf.nr.netID; double minWidth = wf.nr.minWidth; double metLX = x - halfWidth, metHX = x + halfWidth; double metLY = y - halfHeight, metHY = y + halfHeight; Rectangle2D metBound = new Rectangle2D.Double(metLX, metLY, metHX-metLX, metHY-metLY); double metWid = Math.min(halfWidth, halfHeight) * 2; double metLen = Math.max(halfWidth, halfHeight) * 2; // determine the area to search about the metal double surround = worstMetalSurround[metNo]; double lX = metLX - surround, hX = metHX + surround; double lY = metLY - surround, hY = metHY + surround; Rectangle2D searchArea = new Rectangle2D.Double(lX, lY, hX-lX, hY-lY); // prepare for notch detection List<Rectangle2D> recsOnPath; // make a list of rectangles on the path recsOnPath = new ArrayList<Rectangle2D>(); if (svCurrent != null) { List<SearchVertex> svList = getOptimizedList(svCurrent); for(int ind=1; ind<svList.size(); ind++) { SearchVertex sv = svList.get(ind); SearchVertex lastSv = svList.get(ind-1); if (sv.getZ() != metNo && lastSv.getZ() != metNo) continue; if (sv.getZ() != lastSv.getZ()) { // changed layers: compute via rectangles List<MetalVia> nps = metalVias[Math.min(sv.getZ(), lastSv.getZ())].getVias(); int whichContact = lastSv.getContactNo(); MetalVia mv = nps.get(whichContact); PrimitiveNode np = mv.via; Orientation orient = Orientation.fromJava(mv.orientation*10, false, false); SizeOffset so = np.getProtoSizeOffset(); double xOffset = so.getLowXOffset() + so.getHighXOffset(); double yOffset = so.getLowYOffset() + so.getHighYOffset(); double wid = Math.max(np.getDefWidth()-xOffset, minWidth) + xOffset; double hei = Math.max(np.getDefHeight()-yOffset, minWidth) + yOffset; NodeInst ni = NodeInst.makeDummyInstance(np, new EPoint(sv.getX(), sv.getY()), wid, hei, orient); AffineTransform trans = null; if (orient != Orientation.IDENT) trans = ni.rotateOut(); Poly [] polys = np.getTechnology().getShapeOfNode(ni); for(int i=0; i<polys.length; i++) { Poly poly = polys[i]; if (poly.getLayer() != layer) continue; if (trans != null) poly.transform(trans); Rectangle2D bound = poly.getBounds2D(); if (bound.getMaxX() <= lX || bound.getMinX() >= hX || bound.getMaxY() <= lY || bound.getMinY() >= hY) continue; recsOnPath.add(bound); } continue; } // stayed on one layer: compute arc rectangle ArcProto type = metalArcs[metNo]; double width = Math.max(type.getDefaultLambdaBaseWidth(), minWidth); Point2D head = new Point2D.Double(sv.getX(), sv.getY()); Point2D tail = new Point2D.Double(lastSv.getX(), lastSv.getY()); int ang = 0; if (head.getX() != tail.getX() || head.getY() != tail.getY()) ang = GenMath.figureAngle(tail, head); Poly poly = Poly.makeEndPointPoly(head.distance(tail), width, ang, head, width/2, tail, width/2, Poly.Type.FILLED); Rectangle2D bound = poly.getBounds2D(); if (bound.getMaxX() <= lX || bound.getMinX() >= hX || bound.getMaxY() <= lY || bound.getMinY() >= hY) continue; recsOnPath.add(bound); } } for(RTNode.Search sea = new RTNode.Search(searchArea, rtree, true); sea.hasNext(); ) { SOGBound sBound = (SOGBound)sea.next(); Rectangle2D bound = sBound.getBounds(); // eliminate if out of worst surround if (bound.getMaxX() <= lX || bound.getMinX() >= hX || bound.getMaxY() <= lY || bound.getMinY() >= hY) continue; // see if it is within design-rule distance double drWid = Math.max(Math.min(bound.getWidth(), bound.getHeight()), metWid); double drLen = Math.max(Math.max(bound.getWidth(), bound.getHeight()), metLen); double spacing = wf.getSpacingRule(metNo, drWid, drLen); double lXAllow = metLX - spacing, hXAllow = metHX + spacing; double lYAllow = metLY - spacing, hYAllow = metHY + spacing; if (DBMath.isLessThanOrEqualTo(bound.getMaxX(), lXAllow) || DBMath.isGreaterThanOrEqualTo(bound.getMinX(), hXAllow) || DBMath.isLessThanOrEqualTo(bound.getMaxY(), lYAllow) || DBMath.isGreaterThanOrEqualTo(bound.getMinY(), hYAllow)) continue; // too close for DRC: allow if on the same net if (Math.abs(sBound.getNetID()) == netID) { // on same net: make sure there is no notch error if (sBound.getNetID() >= 0) { if (foundANotch(rtree, metBound, sBound.bound, netID, recsOnPath, spacing)) return sBound; } continue; } // if this is a polygon, do closer examination if (sBound instanceof SOGPoly) { PolyBase poly = ((SOGPoly)sBound).getPoly(); Rectangle2D drcArea = new Rectangle2D.Double(lXAllow, lYAllow, hXAllow-lXAllow, hYAllow-lYAllow); if (!poly.contains(drcArea)) continue; } // DRC error found: return the offending geometry return sBound; } // consider notch errors in the existing path if (svCurrent != null) { double spacing = wf.getSpacingRule(metNo, metWid, metLen); List<SearchVertex> svList = getOptimizedList(svCurrent); for(int ind=1; ind<svList.size(); ind++) { SearchVertex sv = svList.get(ind); SearchVertex lastSv = svList.get(ind-1); if (sv.getZ() != metNo && lastSv.getZ() != metNo) continue; if (sv.getZ() != lastSv.getZ()) { // changed layers: analyze the contact for notches List<MetalVia> nps = metalVias[Math.min(sv.getZ(), lastSv.getZ())].getVias(); int whichContact = lastSv.getContactNo(); MetalVia mv = nps.get(whichContact); PrimitiveNode np = mv.via; Orientation orient = Orientation.fromJava(mv.orientation*10, false, false); SizeOffset so = np.getProtoSizeOffset(); double xOffset = so.getLowXOffset() + so.getHighXOffset(); double yOffset = so.getLowYOffset() + so.getHighYOffset(); double wid = Math.max(np.getDefWidth()-xOffset, minWidth) + xOffset; double hei = Math.max(np.getDefHeight()-yOffset, minWidth) + yOffset; NodeInst ni = NodeInst.makeDummyInstance(np, new EPoint(sv.getX(), sv.getY()), wid, hei, orient); AffineTransform trans = null; if (orient != Orientation.IDENT) trans = ni.rotateOut(); Poly [] polys = np.getTechnology().getShapeOfNode(ni); for(int i=0; i<polys.length; i++) { Poly poly = polys[i]; if (poly.getLayer() != layer) continue; if (trans != null) poly.transform(trans); Rectangle2D bound = poly.getBounds2D(); if (bound.getMaxX() <= lX || bound.getMinX() >= hX || bound.getMaxY() <= lY || bound.getMinY() >= hY) continue; SOGBound sBound = new SOGBound(bound, netID); if (foundANotch(rtree, metBound, bound, netID, recsOnPath, spacing)) return sBound; } continue; } // stayed on one layer: analyze the arc for notches ArcProto type = metalArcs[metNo]; double width = Math.max(type.getDefaultLambdaBaseWidth(), minWidth); Point2D head = new Point2D.Double(sv.getX(), sv.getY()); Point2D tail = new Point2D.Double(lastSv.getX(), lastSv.getY()); int ang = 0; if (head.getX() != tail.getX() || head.getY() != tail.getY()) ang = GenMath.figureAngle(tail, head); Poly poly = Poly.makeEndPointPoly(head.distance(tail), width, ang, head, width/2, tail, width/2, Poly.Type.FILLED); Rectangle2D bound = poly.getBounds2D(); if (bound.getMaxX() <= lX || bound.getMinX() >= hX || bound.getMaxY() <= lY || bound.getMinY() >= hY) continue; SOGBound sBound = new SOGBound(bound, netID); if (foundANotch(rtree, metBound, bound, netID, recsOnPath, spacing)) return sBound; } } return null; } /** * Method to tell whether there is a notch between two pieces of metal. * @param rtree the R-Tree with the metal information. * @param metBound one piece of metal. * @param bound another piece of metal. * @return true if there is a notch error between the pieces of metal. */ private boolean foundANotch(RTNode rtree, Rectangle2D metBound, Rectangle2D bound, int netID, List<Rectangle2D> recsOnPath, double dist) { // see if they overlap in X or Y boolean hOverlap = metBound.getMinX() <= bound.getMaxX() && metBound.getMaxX() >= bound.getMinX(); boolean vOverlap = metBound.getMinY() <= bound.getMaxY() && metBound.getMaxY() >= bound.getMinY(); // if they overlap in both, they touch and it is not a notch if (hOverlap && vOverlap) return false; // if they overlap horizontally then they line-up vertically if (hOverlap) { double ptY; if (metBound.getCenterY() > bound.getCenterY()) { if (metBound.getMinY() - bound.getMaxY() > dist) return false; ptY = (metBound.getMinY() + bound.getMaxY()) / 2; } else { if (bound.getMinY() - metBound.getMaxY() > dist) return false; ptY = (metBound.getMaxY() + bound.getMinY()) / 2; } double pt1X = Math.max(metBound.getMinX(), bound.getMinX()); double pt2X = Math.min(metBound.getMaxX(), bound.getMaxX()); double pt3X = (pt1X + pt2X) / 2; if (!pointInRTree(rtree, pt1X, ptY, netID, recsOnPath)) return true; if (!pointInRTree(rtree, pt2X, ptY, netID, recsOnPath)) return true; if (!pointInRTree(rtree, pt3X, ptY, netID, recsOnPath)) return true; return false; } // if they overlap vertically then they line-up horizontally if (vOverlap) { double ptX; if (metBound.getCenterX() > bound.getCenterX()) { if (metBound.getMinX() - bound.getMaxX() > dist) return false; ptX = (metBound.getMinX() + bound.getMaxX()) / 2; } else { if (bound.getMinX() - metBound.getMaxX() > dist) return false; ptX = (metBound.getMaxX() + bound.getMinX()) / 2; } double pt1Y = Math.max(metBound.getMinY(), bound.getMinY()); double pt2Y = Math.min(metBound.getMaxY(), bound.getMaxY()); double pt3Y = (pt1Y + pt2Y) / 2; if (!pointInRTree(rtree, ptX, pt1Y, netID, recsOnPath)) return true; if (!pointInRTree(rtree, ptX, pt2Y, netID, recsOnPath)) return true; if (!pointInRTree(rtree, ptX, pt3Y, netID, recsOnPath)) return true; return false; } // they are diagonal, ensure that one of the "L"s is filled if (metBound.getMinX() > bound.getMaxX() && metBound.getMinY() > bound.getMaxY()) { // metal to upper-right of test area double pt1X = metBound.getMinX(); double pt1Y = bound.getMaxY(); double pt2X = bound.getMaxX(); double pt2Y = metBound.getMinY(); if (Math.sqrt((pt1X-pt2X)*(pt1X-pt2X) + (pt1Y-pt2Y)*(pt1Y-pt2Y)) > dist) return false; if (pointInRTree(rtree, pt1X, pt1Y, netID, recsOnPath)) return false; if (pointInRTree(rtree, pt2X, pt2Y, netID, recsOnPath)) return false; return true; } if (metBound.getMaxX() < bound.getMinX() && metBound.getMinY() > bound.getMaxY()) { // metal to upper-left of test area double pt1X = metBound.getMaxX(); double pt1Y = bound.getMaxY(); double pt2X = bound.getMinX(); double pt2Y = metBound.getMinY(); if (Math.sqrt((pt1X-pt2X)*(pt1X-pt2X) + (pt1Y-pt2Y)*(pt1Y-pt2Y)) > dist) return false; if (pointInRTree(rtree, pt1X, pt1Y, netID, recsOnPath)) return false; if (pointInRTree(rtree, pt2X, pt2Y, netID, recsOnPath)) return false; return true; } if (metBound.getMaxX() < bound.getMinX() && metBound.getMaxY() < bound.getMinY()) { // metal to lower-left of test area double pt1X = metBound.getMaxX(); double pt1Y = bound.getMinY(); double pt2X = bound.getMinX(); double pt2Y = metBound.getMaxY(); if (Math.sqrt((pt1X-pt2X)*(pt1X-pt2X) + (pt1Y-pt2Y)*(pt1Y-pt2Y)) > dist) return false; if (pointInRTree(rtree, pt1X, pt1Y, netID, recsOnPath)) return false; if (pointInRTree(rtree, pt2X, pt2Y, netID, recsOnPath)) return false; return true; } if (metBound.getMinX() > bound.getMaxX() && metBound.getMaxY() < bound.getMinY()) { // metal to lower-right of test area double pt1X = metBound.getMinX(); double pt1Y = bound.getMinY(); double pt2X = bound.getMaxX(); double pt2Y = metBound.getMaxY(); if (Math.sqrt((pt1X-pt2X)*(pt1X-pt2X) + (pt1Y-pt2Y)*(pt1Y-pt2Y)) > dist) return false; if (pointInRTree(rtree, pt1X, pt1Y, netID, recsOnPath)) return false; if (pointInRTree(rtree, pt2X, pt2Y, netID, recsOnPath)) return false; return true; } return false; } private boolean pointInRTree(RTNode rtree, double x, double y, int netID, List<Rectangle2D> recsOnPath) { Rectangle2D searchArea = new Rectangle2D.Double(x, y, 0, 0); for(RTNode.Search sea = new RTNode.Search(searchArea, rtree, true); sea.hasNext(); ) { SOGBound sBound = (SOGBound)sea.next(); if (sBound.netID != netID) continue; if (sBound.bound.getMinX() > x || sBound.bound.getMaxX() < x || sBound.bound.getMinY() > y || sBound.bound.getMaxY() < y) continue; return true; } // now see if it is on the path for(Rectangle2D bound : recsOnPath) { if (bound.getMinX() > x || bound.getMaxX() < x || bound.getMinY() > y || bound.getMaxY() < y) continue; return true; } return false; } /** * Method to see if a proposed piece of metal has DRC errors (ignoring notches). * @param netID the network ID of the desired metal (blockages on this netID are ignored). * @param metNo the level of the metal. * @param halfWidth half of the width of the metal. * @param halfHeight half of the height of the metal. * @param surround is the maximum possible DRC surround around the metal. * @param x the X coordinate at the center of the metal. * @param y the Y coordinate at the center of the metal. * @return a blocking SOGBound object that is in the area. * Returns null if the area is clear. */ private SOGBound getMetalBlockage(int netID, int metNo, double halfWidth, double halfHeight, double surround, double x, double y) { // get the R-Tree data for the metal layer Layer layer = metalLayers[metNo]; RTNode rtree = metalTrees.get(layer); if (rtree == null) return null; // compute the area to search double lX = x - halfWidth - surround, hX = x + halfWidth + surround; double lY = y - halfHeight - surround, hY = y + halfHeight + surround; Rectangle2D searchArea = new Rectangle2D.Double(lX, lY, hX-lX, hY-lY); // see if there is anything in that area for(RTNode.Search sea = new RTNode.Search(searchArea, rtree, true); sea.hasNext(); ) { SOGBound sBound = (SOGBound)sea.next(); Rectangle2D bound = sBound.getBounds(); if (DBMath.isLessThanOrEqualTo(bound.getMaxX(), lX) || DBMath.isGreaterThanOrEqualTo(bound.getMinX(), hX) || DBMath.isLessThanOrEqualTo(bound.getMaxY(), lY) || DBMath.isGreaterThanOrEqualTo(bound.getMinY(), hY)) continue; // ignore if on the same net if (Math.abs(sBound.getNetID()) == netID) continue; // if this is a polygon, do closer examination if (sBound instanceof SOGPoly) { PolyBase poly = ((SOGPoly)sBound).getPoly(); if (!poly.contains(searchArea)) continue; } return sBound; } return null; } /** * Method to find a via blockage in the R-Tree. * @param netID the network ID of the desired space (vias at this point and on this netID are ignored). * @param layer the via layer being examined. * @param halfWidth half of the width of the area to examine. * @param halfHeight half of the height of the area to examine. * @param x the X coordinate at the center of the area to examine. * @param y the Y coordinate at the center of the area to examine. * @return a blocking SOGVia object that is in the area. * Returns null if the area is clear. */ private SOGVia getViaBlockage(int netID, Layer layer, double halfWidth, double halfHeight, double x, double y) { RTNode rtree = viaTrees.get(layer); if (rtree == null) return null; // see if there is anything in that area Rectangle2D searchArea = new Rectangle2D.Double(x-halfWidth, y-halfHeight, halfWidth*2, halfHeight*2); for(RTNode.Search sea = new RTNode.Search(searchArea, rtree, true); sea.hasNext(); ) { SOGVia sLoc = (SOGVia)sea.next(); if (sLoc.getNetID() == netID) { if (sLoc.loc.getX() == x && sLoc.loc.getY() == y) continue; } return sLoc; } return null; } /** * HierarchyEnumerator subclass to examine a cell for a given layer and fill an R-Tree. */ private class BlockageVisitor extends HierarchyEnumerator.Visitor { private List<ArcInst> arcsToRoute; private boolean didTopLevel; public BlockageVisitor(List<ArcInst> arcsToRoute) { this.arcsToRoute = arcsToRoute; didTopLevel = false; } public boolean enterCell(HierarchyEnumerator.CellInfo info) { return true; } public void exitCell(HierarchyEnumerator.CellInfo info) { Cell cell = info.getCell(); Netlist nl = info.getNetlist(); AffineTransform trans = info.getTransformToRoot(); for(Iterator<ArcInst> it = cell.getArcs(); it.hasNext(); ) { ArcInst ai = it.next(); int netID = -1; Network net = nl.getNetwork(ai, 0); if (net != null) netID = info.getNetID(net); Technology tech = ai.getProto().getTechnology(); PolyBase [] polys = tech.getShapeOfArc(ai); for(int i=0; i<polys.length; i++) addLayer(polys[i], trans, netID, false); } } public boolean visitNodeInst(Nodable no, HierarchyEnumerator.CellInfo info) { if (info.isRootCell() && !didTopLevel) { didTopLevel = true; if (arcsToRoute != null) { Netlist nl = info.getNetlist(); for(ArcInst ai : arcsToRoute) { Network net = nl.getNetwork(ai, 0); int netID = info.getNetID(net); netIDs.put(ai, new Integer(netID+1)); } } } NodeInst ni = no.getNodeInst(); if (!ni.isCellInstance()) { Netlist nl = info.getNetlist(); AffineTransform trans = info.getTransformToRoot(); AffineTransform nodeTrans = ni.rotateOut(trans); PrimitiveNode pNp = (PrimitiveNode)ni.getProto(); Technology tech = pNp.getTechnology(); Poly [] nodeInstPolyList = tech.getShapeOfNode(ni, true, false, null); boolean canPlacePseudo = info.isRootCell(); if (!ni.hasExports()) canPlacePseudo = false; for(int i=0; i<nodeInstPolyList.length; i++) { PolyBase poly = nodeInstPolyList[i]; int netID = -1; if (poly.getPort() != null) { Network net = nl.getNetwork(no, poly.getPort(), 0); if (net != null) netID = info.getNetID(net); } addLayer(poly, nodeTrans, netID, canPlacePseudo); } } return true; } } /** * Method to add geometry to the R-Tree. * @param poly the polygon to add (only rectangles are added, so the bounds is used). * @param trans a transformation matrix to apply to the polygon. * @param netID the global network ID of the geometry. * @param canPlacePseudo true if pseudo-layers should be considered (converted to nonpseudo and stored). * False to ignore pseudo-layers. */ private void addLayer(PolyBase poly, AffineTransform trans, int netID, boolean canPlacePseudo) { if (!canPlacePseudo && poly.isPseudoLayer()) return; Layer layer = poly.getLayer(); if (canPlacePseudo) layer = layer.getNonPseudoLayer(); else { if (layer.isPseudoLayer()) return; } Layer.Function fun = layer.getFunction(); if (fun.isMetal()) { poly.transform(trans); Rectangle2D bounds = poly.getBox(); if (bounds == null) { addPolygon(poly, layer, netID); } else { addRectangle(bounds, layer, netID); } } else if (fun.isContact()) { Rectangle2D bounds = poly.getBounds2D(); DBMath.transformRect(bounds, trans); addVia(new EPoint(bounds.getCenterX(), bounds.getCenterY()), layer, netID); } } /** * Method to add a rectangle to the metal R-Tree. * @param bounds the rectangle to add. * @param layer the metal layer on which to add the rectangle. * @param netID the global network ID of the geometry. */ private void addRectangle(Rectangle2D bounds, Layer layer, int netID) { RTNode root = metalTrees.get(layer); if (root == null) { root = RTNode.makeTopLevel(); metalTrees.put(layer, root); } RTNode newRoot = RTNode.linkGeom(null, root, new SOGBound(ERectangle.fromLambda(bounds), netID)); if (newRoot != root) metalTrees.put(layer, newRoot); } /** * Method to add a polygon to the metal R-Tree. * @param poly the polygon to add. * @param layer the metal layer on which to add the rectangle. * @param netID the global network ID of the geometry. */ private void addPolygon(PolyBase poly, Layer layer, int netID) { RTNode root = metalTrees.get(layer); if (root == null) { root = RTNode.makeTopLevel(); metalTrees.put(layer, root); } Rectangle2D bounds = poly.getBounds2D(); RTNode newRoot = RTNode.linkGeom(null, root, new SOGPoly(ERectangle.fromLambda(bounds), netID, poly)); if (newRoot != root) metalTrees.put(layer, newRoot); } /** * Method to add a point to the via R-Tree. * @param loc the point to add. * @param layer the via layer on which to add the point. * @param netID the global network ID of the geometry. */ private void addVia(EPoint loc, Layer layer, int netID) { RTNode root = viaTrees.get(layer); if (root == null) { root = RTNode.makeTopLevel(); viaTrees.put(layer, root); } RTNode newRoot = RTNode.linkGeom(null, root, new SOGVia(loc, netID)); if (newRoot != root) viaTrees.put(layer, newRoot); } /** * Class to define a list of possible nodes that can connect two layers. * This includes orientation */ private static class MetalVia { PrimitiveNode via; int orientation; MetalVia(PrimitiveNode v, int o) { via = v; orientation = o; } } /** * Class to define a list of possible nodes that can connect two layers. * This includes orientation */ private static class MetalVias { List<MetalVia> vias = new ArrayList<MetalVia>(); void addVia(PrimitiveNode pn, int o) { vias.add(new MetalVia(pn, o)); Collections.sort(vias, new PrimsBySize()); } List<MetalVia> getVias() { return vias; } } /** * Comparator class for sorting primitives by their size. */ private static class PrimsBySize implements Comparator<MetalVia> { /** * Method to sort primitives by their size. */ public int compare(MetalVia mv1, MetalVia mv2) { PrimitiveNode pn1 = mv1.via; PrimitiveNode pn2 = mv2.via; double sz1 = pn1.getDefWidth() * pn1.getDefHeight(); double sz2 = pn2.getDefWidth() * pn2.getDefHeight(); if (sz1 < sz2) return -1; if (sz1 > sz2) return 1; return 0; } } /************************************** DIJKSTRA PATH SEARCHING **************************************/ /** * Class to define a vertex in the Dijkstra search. */ private class SearchVertex implements Comparable { /** the coordinate of the search vertex. */ private double xv, yv; /** the layer of the search vertex. */ private int zv; /** the cost of search to this vertex. */ private int cost; /** the layer of cuts in "cuts". */ private int cutLayer; /** the cuts in the contact. */ private Point2D [] cuts; /** the previous vertex in the search. */ private SearchVertex last; /** the routing state. */ private Wavefront w; /** * Method to create a new SearchVertex. * @param x the X coordinate of the SearchVertex. * @param y the Y coordinate of the SearchVertex. * @param z the Z coordinate (metal layer) of the SearchVertex. * @param whichContact the contact number to use if switching layers. * @param cuts an array of cuts in this contact (if switching layers). * @param cl the layer of the cut (if switching layers). * @param nr the NeededRoute that this SearchVertex is part of. */ SearchVertex(double x, double y, int z, int whichContact, Point2D [] cuts, int cl, Wavefront w) { xv = x; yv = y; zv = (z<<8) + (whichContact & 0xFF); this.cuts = cuts; cutLayer = cl; this.w = w; } double getX() { return xv; } double getY() { return yv; } int getZ() { return zv >> 8; } int getContactNo() { return zv & 0xFF; } Point2D [] getCuts() { return cuts; } void clearCuts() { cuts = null; } int getCutLayer() { return cutLayer; } /** * Method to sort SearchVertex objects by their cost. */ public int compareTo(Object svo) { SearchVertex sv = (SearchVertex)svo; int diff = cost - sv.cost; if (diff != 0) return diff; if (w != null) { double thisDist = Math.abs(xv-w.toX) + Math.abs(yv-w.toY) + Math.abs(zv-w.toZ); double otherDist = Math.abs(sv.xv-w.toX) + Math.abs(sv.yv-w.toY) + Math.abs(sv.zv-w.toZ); if (thisDist < otherDist) return -1; if (thisDist > otherDist) return 1; } return 0; } } // /** // * Debugging method to dump an R-Tree plane. // * @param z the metal layer to dump. // */ // private void dumpPlane(int z) // { // System.out.println("************************* METAL " + (z+1) + " *************************"); // Map<Integer,Map<Integer,SearchVertex>> plane = searchVertexPlanes[z]; // if (plane == null) return; // List<Integer> yValues = new ArrayList<Integer>(); // for(Iterator<Integer> it = plane.keySet().iterator(); it.hasNext(); ) // yValues.add(it.next()); // Collections.sort(yValues); // int lowY = yValues.get(0).intValue(); // int highY = yValues.get(yValues.size()-1).intValue(); // int lowX = 1, highX = 0; // for(Integer y : yValues) // { // Map<Integer,SearchVertex> row = plane.get(y); // for(Iterator<Integer> it = row.keySet().iterator(); it.hasNext(); ) // { // Integer x = it.next(); // int xv = x.intValue(); // if (lowX > highX) lowX = highX = xv; else // { // if (xv < lowX) lowX = xv; // if (xv > highX) highX = xv; // } // } // } // String lowXStr = " " + lowX; // String highXStr = " " + highX; // int numXDigits = Math.max(lowXStr.length(), highXStr.length()); // String lowYStr = " " + lowY; // String highYStr = " " + highY; // int numYDigits = Math.max(lowYStr.length(), highYStr.length()); // // String space = " "; // while (space.length() < numYDigits+3) space += " "; // System.out.print(space); // for(int x=lowX; x<=highX; x++) // { // String xCoord = " " + x; // while (xCoord.length() < numXDigits) xCoord = " " + xCoord; // System.out.print(xCoord); // } // System.out.println(); // // for(int y=highY; y>=lowY; y--) // { // String yCoord = " " + y; // while (yCoord.length() < numYDigits) yCoord = " " + yCoord; // System.out.print("Row"+yCoord); // Map<Integer,SearchVertex> row = plane.get(y); // if (row != null) // { // for(int x=lowX; x<=highX; x++) // { // SearchVertex sv = row.get(x); // String xCoord; // if (sv == null) xCoord = " X"; else // xCoord = " " + sv.cost; // while (xCoord.length() < numXDigits) xCoord = " " + xCoord; // System.out.print(xCoord); // } // } // System.out.println(); // } // } private void showSearchVertices(Map<Integer,Set<Integer>> [] planes, boolean horiz) { EditWindow_ wnd = Job.getUserInterface().getCurrentEditWindow_(); for(int i=0; i<numMetalLayers; i++) { double offset = i; offset -= (numMetalLayers-2) / 2.0; offset /= numMetalLayers+2; Map<Integer,Set<Integer>> plane = planes[i]; if (plane == null) continue; for(Integer y : plane.keySet()) { double yv = y.doubleValue(); Set<Integer> row = plane.get(y); for(Iterator<Integer> it = row.iterator(); it.hasNext(); ) { Integer x = it.next(); double xv = x.doubleValue(); Point2D pt1, pt2; if (horiz) { pt1 = new Point2D.Double(xv-0.5, yv+offset); pt2 = new Point2D.Double(xv+0.5, yv+offset); } else { pt1 = new Point2D.Double(xv+offset, yv-0.5); pt2 = new Point2D.Double(xv+offset, yv+0.5); } wnd.addHighlightLine(pt1, pt2, cell, false, false); } } } } }