/* -*- 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, Oracle and/or its affiliates. All rights reserved.
*
* Electric(tm) is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* Electric(tm) is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Electric(tm); see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, Mass 02111-1307, USA.
*/
package com.sun.electric.tool.routing.seaOfGates;
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;
import com.sun.electric.database.EditingPreferences;
import com.sun.electric.database.Environment;
import com.sun.electric.database.geometry.EPoint;
import com.sun.electric.database.geometry.ERectangle;
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.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.routing.Routing;
import com.sun.electric.tool.routing.SeaOfGates;
import com.sun.electric.tool.user.ErrorLogger;
import com.sun.electric.tool.util.concurrent.runtime.taskParallel.ThreadPool.PoolWorkerStrategyFactory;
import com.sun.electric.tool.util.concurrent.utils.ElapseTimer;
import com.sun.electric.util.TextUtils;
import com.sun.electric.util.concurrent.ElectricWorkerStrategy;
import com.sun.electric.util.math.DBMath;
import com.sun.electric.util.math.GenMath;
import com.sun.electric.util.math.Orientation;
/**
* 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
* j * 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 abstract class SeaOfGatesEngine {
/** True to display each step in the search. */
private static final boolean DEBUGSTEPS = false;
/** True to display the first routing failure. */
protected static final boolean DEBUGFAILURE = false;
/** True to debug "infinite" loops. */
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;
protected SearchVertex svAborted = new SearchVertex(0, 0, 0, 0, null, 0, null);
protected SearchVertex svExhausted = new SearchVertex(0, 0, 0, 0, null, 0, null);
protected SearchVertex svLimited = new SearchVertex(0, 0, 0, 0, null, 0, null);
/** Cell in which routing occurs. */
protected 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 static 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 */
protected double totalWireLength;
/** true if this is the first failure of a route (for debugging) */
protected static volatile boolean firstFailure;
/** true to run to/from and from/to routing in parallel */
protected boolean parallelDij;
/** for logging errors */
protected ErrorLogger errorLogger;
/**
* Class to hold a "batch" of routes, all on the same network.
*/
protected static class RouteBatches {
Set<ArcInst> unroutedArcs;
Set<NodeInst> unroutedNodes;
List<PortInst> orderedPorts;
int segsInBatch;
int numRouted, numUnrouted;
}
protected class Wavefront {
/** The route that this is part of. */
NeededRoute nr;
/** Wavefront name (for debugging). */
String name;
/** List of active search vertices while running wavefront. */
private TreeSet<SearchVertex> active;
/** Resulting list of vertices found for this wavefront. */
List<SearchVertex> vertices;
/** Set true to abort this wavefront's search. */
boolean abort;
/** The starting and ending ports of the wavefront. */
PortInst from;
PortInst to;
/** The starting X/Y coordinates of the wavefront. */
double fromX;
double fromY;
/** The starting metal layer of the wavefront. */
int fromZ;
/** The ending X/Y coordinates of the wavefront. */
double toX;
double toY;
/** The ending metal layer of the wavefront. */
int toZ;
/** debugging state */
private final boolean debug;
/** Search vertices found while propagating the wavefront. */
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;
@SuppressWarnings("unchecked")
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.
*/
protected 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;
protected 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 int routeIt(Job job, Cell cell, List<ArcInst> arcsToRoute, SeaOfGates.SeaOfGatesOptions prefs) {
// initialize information about the technology
if (initializeDesignRules(cell, prefs))
return 0;
EditingPreferences ep = cell.getEditingPreferences();
// user-interface initialization
ElapseTimer timer = ElapseTimer.createInstance().start();
ElapseTimer debugTimer = ElapseTimer.createInstance().start();
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];
this.makeListOfRoutes(numBatches, routeBatches, allRoutes, arcsToRoute, prefs, ep);
debugTimer.end();
System.out.println("### debug time (initialize): " + debugTimer);
// 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();
if (prefs.useSpecificNumberOfThreads) {
numberOfThreads = prefs.numberOfThreads;
}
startRouting(numberOfThreads, allRoutes, routeBatches, env, ep, job);
// 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);
timer.end();
Job.getUserInterface().stopProgressDialog();
System.out.println("Routed " + numRoutedSegments + " out of " + totalRoutes
+ " segments; total length of routed wires is " + TextUtils.formatDistance(totalWireLength)
+ "; took " + timer);
if (numFailedRoutes > 0)
System.out.println("NOTE: " + numFailedRoutes + " nets were not routed");
return numRoutedSegments;
}
/**
* 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.
*/
protected 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);
}
}
protected abstract void doRoutingParallel(int numberOfThreads, List<NeededRoute> allRoutes,
RouteBatches[] routeBatches, Environment env, EditingPreferences ep);
protected void startRouting(int numberOfThreads, List<NeededRoute> allRoutes,
RouteBatches[] routeBatches, Environment env, EditingPreferences ep, Job job) {
ElectricWorkerStrategy electricWorker = new ElectricWorkerStrategy(null);
electricWorker.setUi(Job.getUserInterface());
PoolWorkerStrategyFactory.userDefinedStrategy = electricWorker;
if (numberOfThreads >= 1) {
doRoutingParallel(numberOfThreads, allRoutes, routeBatches, env, ep);
} else {
doRouting(allRoutes, routeBatches, job, env, ep);
}
}
/**
* 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];
GenMath.MutableDouble mutableDist = new GenMath.MutableDouble(0);
for (int i = 0; i < numMetalLayers; i++)
{
if (DRC.getMaxSurround(metalLayers[i], Double.MAX_VALUE, mutableDist)) // only when found
worstMetalSurround[i] = mutableDist.doubleValue();
}
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;
}
protected void makeListOfRoutes(int numBatches, RouteBatches[] routeBatches, List<NeededRoute> allRoutes,
List<ArcInst> arcsToRoute, SeaOfGates.SeaOfGatesOptions prefs, EditingPreferences ep) {
this.doMakeListOfRoutes(0, numBatches, routeBatches, allRoutes, arcsToRoute, prefs);
}
protected void doMakeListOfRoutes(int start, int end, RouteBatches[] routeBatches,
List<NeededRoute> allRoutes, List<ArcInst> arcsToRoute, SeaOfGates.SeaOfGatesOptions prefs) {
for (int b = start; b < end; 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);
}
}
}
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.
*/
protected 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.
*/
protected static 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.
*/
protected 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 = SeaOfGatesEngine.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, cell);
wnd.finishedHighlighting();
}
}
nr.cleanSearchMemory();
}
protected 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;
}
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();
}
}
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.
*/
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.
*/
protected class SearchVertex implements Comparable<SearchVertex> {
/** 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(SearchVertex svo) {
int diff = cost - svo.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(svo.xv - w.toX) + Math.abs(svo.yv - w.toY)
+ Math.abs(svo.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();
// }
// }
protected static void showSearchVertices(Map<Integer, Set<Integer>>[] planes, boolean horiz, Cell cell) {
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);
}
}
}
}
}