/* -*- 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.experimentalSeaOfGates;
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.PolyBase;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.topology.RTBounds;
import com.sun.electric.database.topology.RTNode;
import com.sun.electric.tool.Job;
import com.sun.electric.tool.routing.RoutingFrame;
import com.sun.electric.tool.user.ErrorLogger;
import com.sun.electric.util.TextUtils;
import com.sun.electric.util.math.DBMath;
import com.sun.electric.util.math.GenMath;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.Semaphore;
/**
* Class to do sea-of-gates routing inside of a routing frame.
* 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
* > 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
* > 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
*/
public class RoutingFrameSeaOfGates extends RoutingFrame
{
/** True to display each step in the search. */ private static final boolean DEBUGSTEPS = false;
/** True to debug "infinite" loops. */ private static final boolean DEBUGLOOPS = false;
/** 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 making a turn. */ private static final int COSTTURNING = 1;
/** Cost of having coordinates that are off-grid. */ private static final int COSTOFFGRID = 15;
private SearchVertex svAborted = new SearchVertex(0, 0, 0, 0, null, 0, null);
private SearchVertex svExhausted = new SearchVertex(0, 0, 0, 0, null, 0, null);
private SearchVertex svLimited = new SearchVertex(0, 0, 0, 0, null, 0, null);
/** Cell in which routing occurs. */ private Cell cell;
/** Cell size. */ private Rectangle2D cellBounds;
/** R-Trees for metal blockage in the cell. */ private Map<RoutingLayer,RTNode> metalTrees;
/** R-Trees for via blockage in the cell. */ private Map<RoutingLayer,RTNode> viaTrees;
/** number of metal layers in the technology. */ private int numMetalLayers;
/** metal layers in the technology. */ private RoutingLayer [] metalLayers;
/** vias to use to go up from each metal layer. */ private MetalVias [] metalVias;
/** true to run to/from and from/to routing in parallel */ private boolean parallelDij;
/** for logging errors */ private ErrorLogger errorLogger;
public RoutingParameter maxArcWidth = new RoutingParameter("maxarcwidth", "Maximum arc width:", 10);
public RoutingParameter complexityLimit = new RoutingParameter("complexity", "Complexity limit:", 200000);
public RoutingParameter useParallelFromToRoutes = new RoutingParameter("parallelFromToRoutes", "Use two processors per route", false);
public RoutingParameter useParallelRoutes = new RoutingParameter("parallelRoutes", "Do multiple routes in parallel", false);
/**
* Method to return the name of this routing algorithm.
* @return the name of this routing algorithm.
*/
public String getAlgorithmName() { return "Sea-of-Gates in Framework"; }
/**
* Method to do Sea of Gates Routing.
*/
protected void runRouting(Cell cell, List<RoutingSegment> segmentsToRoute, List<RoutingLayer> allLayers,
List<RoutingContact> allContacts, List<RoutingGeometry> blockages)
{
this.cell = cell;
routeIt(Job.getRunningJob(), segmentsToRoute, allLayers, allContacts, blockages);
}
/**
* Class to hold a "batch" of routes, all on the same network.
*/
private static class RouteBatches
{
int segsInBatch;
}
private class Wavefront
{
/** The route that this is part of. */ private NeededRoute nr;
/** Wavefront name (for debugging). */ private String name;
/** List of active search vertices while running wavefront. */ private TreeSet<SearchVertex> active;
/** Resulting list of vertices found for this wavefront. */ private List<SearchVertex> vertices;
/** Set true to abort this wavefront's search. */ private boolean abort;
/** The starting and ending ports of the wavefront. */ private RoutingEnd from, to;
/** The starting X/Y coordinates of the wavefront. */ private double fromX, fromY;
/** The starting metal layer of the wavefront. */ private int fromZ;
/** The ending X/Y coordinates of the wavefront. */ private double toX, toY;
/** The ending metal layer of the wavefront. */ private int toZ;
/** debugging state */ private final boolean debug;
/** Gridless search vertices found while propagating wavefront. */ private Map<Double,Set<Double>> [] searchVertexPlanesDBL;
/** minimum spacing between this metal and itself. */ private Map<Double,Map<Double,Double>>[] layerSurround;
Wavefront(NeededRoute nr, RoutingEnd from, double fromX, double fromY, int fromZ,
RoutingEnd 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;
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)
{
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;
}
/**
* 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)
{
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));
}
/**
* 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 = metalLayers[layer].getMinWidth();
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
RoutingLayer lay = metalLayers[layer];
double v = lay.getMinSpacing(lay);
value = new Double(v);
widMap.put(len, value);
}
return value.doubleValue();
}
}
/**
* Class to hold a route that must be run.
*/
private class NeededRoute
{
String routeName;
int netID;
double minWidth;
int batchNumber;
int routeInBatch;
Rectangle2D routeBounds;
double minimumSearchBoundX;
double maximumSearchBoundX;
double minimumSearchBoundY;
double maximumSearchBoundY;
Rectangle2D jumpBound;
Wavefront dir1, dir2;
Wavefront winningWF;
RoutingSegment rs;
NeededRoute(String routeName, double fromX, double fromY, int fromZ, double toX, double toY, int toZ,
int netID, double minWidth, int batchNumber, int routeInBatch, RoutingSegment rs)
{
this.routeName = routeName;
this.netID = netID;
this.minWidth = minWidth;
this.batchNumber = batchNumber;
this.routeInBatch = routeInBatch;
this.rs = rs;
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, rs.getStartEnd(), fromX, fromY, fromZ, rs.getFinishEnd(), toX, toY, toZ, "a->b", DEBUGSTEPS);
dir2 = new Wavefront(this, rs.getFinishEnd(), toX, toY, toZ, rs.getStartEnd(), fromX, fromY, fromZ, "b->a", false);
}
public void cleanSearchMemory()
{
dir1.searchVertexPlanesDBL = null;
dir1.active = null;
if (dir1.vertices != null)
{
for(SearchVertex sv : dir1.vertices)
sv.clearCuts();
}
dir2.searchVertexPlanesDBL = null;
dir2.active = null;
if (dir2.vertices != null)
{
for(SearchVertex sv : dir2.vertices)
sv.clearCuts();
}
}
}
/************************************** ROUTING **************************************/
/**
* This is the public interface for Sea-of-Gates Routing when done in batch mode.
* @param cell the cell to be Sea-of-Gates-routed.
* @param arcsToRoute a List of ArcInsts on networks to be routed.
*/
public void routeIt(Job job, List<RoutingSegment> segmentsToRoute, List<RoutingLayer> allLayers,
List<RoutingContact> allContacts, List<RoutingGeometry> blockages)
{
// initialize information about the technology
if (initializeDesignRules(allLayers, allContacts)) return;
// user-interface initialization
Job.getUserInterface().startProgressDialog("Routing " + segmentsToRoute.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<RoutingLayer, RTNode>();
viaTrees = new HashMap<RoutingLayer, RTNode>();
// add all blockage geometry to the list
for(RoutingGeometry rg : blockages)
{
RoutingLayer layer = rg.getLayer();
Rectangle2D bounds = rg.getBounds();
if (layer.isMetal())
{
addRectangle(bounds, layer, rg.getNetID());
} else
{
addVia(new Point2D.Double(bounds.getCenterX(), bounds.getCenterY()), layer, rg.getNetID());
}
}
addBlockagesAtPorts(segmentsToRoute);
// make a list of all routes that are needed
List<NeededRoute> allRoutes = new ArrayList<NeededRoute>();
int numBatches = segmentsToRoute.size();
RouteBatches [] routeBatches = new RouteBatches[numBatches];
for(int b=0; b<numBatches; b++)
{
// get list of PortInsts that comprise this net
RoutingSegment rs = segmentsToRoute.get(b);
routeBatches[b] = new RouteBatches();
routeBatches[b].segsInBatch = 0;
// determine the minimum width of arcs on this net
double minWidth = Math.max(rs.getWidestArcAtStart(), rs.getWidestArcAtStart());
// find a path between the ends of the network
int batchNumber = 1;
// get information about one end of the path
List<RoutingLayer> fromArcs = rs.getStartLayers();
RoutingLayer fromArc = fromArcs.get(0);
// get information about the other end of the path
List<RoutingLayer> toArcs = rs.getFinishLayers();
RoutingLayer toArc = toArcs.get(0);
// determine the coordinates of the route
Point2D fromLoc = rs.getStartEnd().getLocation();
Point2D toLoc = rs.getFinishEnd().getLocation();
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.getMetalNumber()-1;
int toZ = toArc.getMetalNumber()-1;
// see if access is blocked
double metalSpacing = Math.max(metalLayers[fromZ].getMinWidth(), minWidth) / 2;
// determine "from" surround
RoutingLayer lay = metalLayers[fromZ];
double surround = lay.getMinSpacing(lay);
SOGBound block = getMetalBlockage(rs.getNetID(), fromZ, metalSpacing, metalSpacing, surround, fromX, fromY);
if (block != null)
{
// see if gridding caused the blockage
fromX = fromLoc.getX();
fromY = fromLoc.getY();
block = getMetalBlockage(rs.getNetID(), fromZ, metalSpacing, metalSpacing, surround, fromX, fromY);
if (block != null)
{
String errorMsg = "Cannot Route to port " + rs.getStartEnd().describe() + " 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(metalLayers[toZ].getMinWidth(), minWidth) / 2;
// determine "to" surround
lay = metalLayers[toZ];
surround = lay.getMinSpacing(lay);
block = getMetalBlockage(rs.getNetID(), toZ, metalSpacing, metalSpacing, surround, toX, toY);
if (block != null)
{
// see if gridding caused the blockage
toX = toLoc.getX();
toY = toLoc.getY();
block = getMetalBlockage(rs.getNetID(), toZ, metalSpacing, metalSpacing, surround, toX, toY);
if (block != null)
{
String errorMsg = "Cannot route to port " + rs.getFinishEnd().describe() + " 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(rs.getNetName(), fromX, fromY, fromZ, toX, toY, toZ,
rs.getNetID(), minWidth, b, batchNumber++, rs);
routeBatches[b].segsInBatch++;
allRoutes.add(nr);
}
// now do the actual routing
boolean parallel = useParallelRoutes.getBooleanValue();
parallelDij = useParallelFromToRoutes.getBooleanValue();
int numberOfProcessors = Runtime.getRuntime().availableProcessors();
if (numberOfProcessors <= 1) parallelDij = false;
int numberOfThreads = numberOfProcessors;
if (parallelDij) numberOfThreads /= 2;
if (!parallel) numberOfThreads = 1;
if (numberOfThreads == 1) parallel = false;
// show what is being done
System.out.println("Sea-of-gates router finding " + allRoutes.size() + " paths on " + numBatches + " networks");
if (parallel || parallelDij)
{
String message = "NOTE: System has " + numberOfProcessors + " processors so";
if (parallel) message += " routing " + numberOfThreads + " paths in parallel";
if (parallelDij)
{
if (parallel) message += " and";
message += " routing both directions of each path in parallel";
}
System.out.println(message);
}
Environment env = Environment.getThreadEnvironment();
EditingPreferences ep = cell.getEditingPreferences();
if (numberOfThreads > 1)
{
doRoutingParallel(numberOfThreads, allRoutes, routeBatches, env, ep);
} else
{
doRouting(allRoutes, routeBatches, job, env, ep);
}
// clean up at end
Job.getUserInterface().stopProgressDialog();
errorLogger.termLogging(true);
}
/**
* Method to do the routing in a single thread.
* @param allRoutes the routes that need to be done.
* @param routeBatches the routing batches (by network)
* @param job the job that invoked this routing.
*/
private void doRouting(List<NeededRoute> allRoutes, RouteBatches [] routeBatches, Job job, Environment env, EditingPreferences ep)
{
int totalRoutes = allRoutes.size();
for(int r=0; r<totalRoutes; r++)
{
if (job != null && job.checkAbort())
{
System.out.println("Sea-of-gates routing aborted");
break;
}
// get information on the segment to be routed
NeededRoute nr = allRoutes.get(r);
Job.getUserInterface().setProgressValue(r*100/totalRoutes);
String routeName = nr.routeName;
if (routeBatches[nr.batchNumber].segsInBatch > 1)
routeName += " (" + nr.routeInBatch + " of " + routeBatches[nr.batchNumber].segsInBatch + ")";
Job.getUserInterface().setProgressNote("Network " + routeName);
System.out.println("Routing network " + routeName + "...");
// route the segment
findPath(nr, env, ep);
// if the routing was good, place the results
if (nr.winningWF != null && nr.winningWF.vertices != null)
createRoute(nr);
}
}
private void doRoutingParallel(int numberOfThreads, List<NeededRoute> allRoutes, RouteBatches [] routeBatches, Environment env, EditingPreferences ep)
{
// create threads and other threading data structures
RouteInThread[] threads = new RouteInThread[numberOfThreads];
for(int i=0; i<numberOfThreads; i++) threads[i] = new RouteInThread("Route #" + (i+1), env, ep);
NeededRoute [] routesToDo = new NeededRoute[numberOfThreads];
int [] routeIndices = new int[numberOfThreads];
Semaphore outSem = new Semaphore(0);
// create list of routes and blocked areas
List<NeededRoute> myList = new ArrayList<NeededRoute>();
for(NeededRoute nr : allRoutes) myList.add(nr);
List<Rectangle2D> blocked = new ArrayList<Rectangle2D>();
// now run the threads
int totalRoutes = allRoutes.size();
int routesDone = 0;
while (myList.size() > 0)
{
int threadAssign = 0;
blocked.clear();
for(int i=0; i<myList.size(); i++)
{
NeededRoute nr = myList.get(i);
boolean isBlocked = false;
for(Rectangle2D block : blocked)
{
if (block.intersects(nr.routeBounds)) { isBlocked = true; break; }
}
if (isBlocked) continue;
// this route can be done: start it
blocked.add(nr.routeBounds);
routesToDo[threadAssign] = nr;
routeIndices[threadAssign] = i;
threads[threadAssign].startRoute(nr, outSem);
threadAssign++;
if (threadAssign >= numberOfThreads) break;
}
String routes = "";
for(int i=0; i<threadAssign; i++)
{
String routeName = routesToDo[i].routeName;
if (routeBatches[routesToDo[i].batchNumber].segsInBatch > 1)
routeName += "(" + routesToDo[i].routeInBatch + "/" + routeBatches[routesToDo[i].batchNumber].segsInBatch + ")";
if (routes.length() > 0) routes += ", ";
routes += routeName;
}
System.out.println("Parallel routing " + routes + "...");
Job.getUserInterface().setProgressNote(routes);
// now wait for routing threads to finish
outSem.acquireUninterruptibly(threadAssign);
// all done, now handle the results
for(int i=0; i<threadAssign; i++)
{
if (routesToDo[i].winningWF != null && routesToDo[i].winningWF.vertices != null)
createRoute(routesToDo[i]);
}
for(int i=threadAssign-1; i>=0; i--)
myList.remove(routeIndices[i]);
routesDone += threadAssign;
Job.getUserInterface().setProgressValue(routesDone*100/totalRoutes);
}
// terminate the threads
for(int i=0; i<numberOfThreads; i++) threads[i].startRoute(null, null);
}
private class RouteInThread extends Thread
{
private Semaphore inSem = new Semaphore(0);
private NeededRoute nr;
private Semaphore whenDone;
private Environment env;
private EditingPreferences ep;
public RouteInThread(String name, Environment env, EditingPreferences ep)
{
super(name);
this.env = env;
this.ep = ep;
start();
}
public void startRoute(NeededRoute nr, Semaphore whenDone)
{
this.nr = nr;
this.whenDone = whenDone;
inSem.release();
}
public void run()
{
Environment.setThreadEnvironment(env);
EditingPreferences.setThreadEditingPreferences(ep);
for (;;)
{
inSem.acquireUninterruptibly();
if (nr == null) return;
findPath(nr, env, ep);
whenDone.release();
}
}
}
/**
* Method to initialize technology information, including design rules.
* @return true on error.
*/
private boolean initializeDesignRules(List<RoutingLayer> allLayers, List<RoutingContact> allContacts)
{
// find the metal layers, arcs, and contacts
numMetalLayers = 0;
for(RoutingLayer rl : allLayers) if (rl.isMetal()) numMetalLayers++;
metalLayers = new RoutingLayer[numMetalLayers];
int metNo = 0;
for(RoutingLayer rl : allLayers)
{
if (rl.isMetal()) metalLayers[metNo++] = rl;
}
metalVias = new MetalVias[numMetalLayers-1];
for(int i=0; i<numMetalLayers-1; i++) metalVias[i] = new MetalVias();
for(RoutingContact rc : allContacts)
{
for(int i=0; i<numMetalLayers-1; i++)
{
if ((rc.getFirstLayer() == metalLayers[i] && rc.getSecondLayer() == metalLayers[i+1]) ||
(rc.getSecondLayer() == metalLayers[i] && rc.getFirstLayer() == metalLayers[i+1]))
{
metalVias[i].addVia(rc, 0);
// see if the node is asymmetric and should exist in rotated states
boolean square = true, offCenter = false;
List<RoutingGeometry> geoms = rc.getGeometry();
for(RoutingGeometry rg : geoms)
{
RoutingLayer conLayer = rg.getLayer();
if (conLayer.isMetal())
{
Rectangle2D conRect = rg.getBounds();
if (conRect.getWidth() != conRect.getHeight()) square = false;
if (conRect.getCenterX() != 0 || conRect.getCenterY() != 0) offCenter = true;
}
}
if (offCenter)
{
// off center: test in all 4 rotations
metalVias[i].addVia(rc, 90);
metalVias[i].addVia(rc, 180);
metalVias[i].addVia(rc, 270);
} else if (!square)
{
// centered but not square: test in 90-degree rotation
metalVias[i].addVia(rc, 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 (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;
}
}
}
return false;
}
/**
* Method to add extra blockage information that corresponds to ends of unrouted arcs.
* @param segmentsToRoute the list of segments to route.
* @param tech the technology to use.
*/
private void addBlockagesAtPorts(List<RoutingSegment> segmentsToRoute)
{
for(RoutingSegment rs : segmentsToRoute)
{
// determine the minimum width of arcs on this net
double minWidth = Math.min(rs.getWidestArcAtStart(), rs.getWidestArcAtFinish());
addBlockage(rs.getStartEnd().getLocation(), rs.getStartLayers(), minWidth, rs.getNetID());
addBlockage(rs.getFinishEnd().getLocation(), rs.getFinishLayers(), minWidth, rs.getNetID());
}
}
private void addBlockage(Point2D loc, List<RoutingLayer> layers, double minWidth, int netID)
{
int lowMetal = -1, highMetal = -1;
for(RoutingLayer rl : layers)
{
int level = rl.getMetalNumber();
if (lowMetal < 0) lowMetal = highMetal = level; else
{
lowMetal = Math.min(lowMetal, level);
highMetal = Math.max(highMetal, level);
}
}
if (lowMetal < 0) return;
// 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);
for(RoutingGeometry rg : mv.via.getGeometry())
{
Rectangle2D centeredBounds = rg.getBounds();
Rectangle2D bounds = new Rectangle2D.Double(centeredBounds.getMinX() + loc.getX(),
centeredBounds.getMinY() + loc.getY(), centeredBounds.getWidth(), centeredBounds.getHeight());
addRectangle(bounds, rg.getLayer(), netID);
}
}
}
/**
* Method to create the geometry for a route.
* Places nodes and arcs to make the route, and also updates the R-Tree data structure.
* @param nr the route information.
*/
private void createRoute(NeededRoute nr)
{
Wavefront wf = nr.winningWF;
Point2D pt = wf.to.getLocation();
RoutingContact startContact = RoutingContact.STARTPOINT;
RoutingContact finishContact = RoutingContact.FINISHPOINT;
if (wf == nr.dir2)
{
startContact = RoutingContact.FINISHPOINT;
finishContact = RoutingContact.STARTPOINT;
}
int endIndex = wf.vertices.size();
RoutePoint lastRP = new RoutePoint(finishContact, pt, 0);
if (!DBMath.doublesClose(pt.getX(), wf.toX) || !DBMath.doublesClose(pt.getY(), wf.toY))
{
// end of route is off-grid: adjust it
if (endIndex >= 2)
{
SearchVertex v1 = wf.vertices.get(0);
SearchVertex v2 = wf.vertices.get(1);
RoutingLayer type = metalLayers[wf.toZ];
double width = Math.max(type.getMinWidth(), nr.minWidth);
RoutingContact pin = type.getPin();
if (v1.getX() == v2.getX())
{
// first line is vertical: run a horizontal bit
RoutePoint rp = makeNodeInst(pin, new Point2D.Double(v1.getX(), pt.getY()), 0, nr);
makeArcInst(type, width, rp, lastRP, nr);
lastRP = rp;
} else if (v1.getY() == v2.getY())
{
// first line is horizontal: run a vertical bit
RoutePoint rp = makeNodeInst(pin, new Point2D.Double(pt.getX(), v1.getY()), 0, nr);
makeArcInst(type, width, rp, lastRP, nr);
lastRP = rp;
}
}
}
for(int i=0; i<endIndex; i++)
{
SearchVertex sv = wf.vertices.get(i);
boolean madeContacts = false;
while (i < endIndex-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);
RoutePoint rp = makeNodeInst(mv.via, new Point2D.Double(sv.getX(), sv.getY()), mv.orientation, nr);
RoutingLayer type = metalLayers[sv.getZ()];
double width = Math.max(type.getMinWidth(), nr.minWidth);
makeArcInst(type, width, lastRP, rp, nr);
madeContacts = true;
sv = svNext;
i++;
lastRP = rp;
}
if (madeContacts && i != endIndex-1) continue;
RoutingContact np = metalLayers[sv.getZ()].getPin();
RoutePoint rp = null;
if (i == endIndex-1)
{
Point2D fPoint = wf.from.getLocation();
rp = new RoutePoint(startContact, fPoint, 0);
if (!DBMath.doublesClose(fPoint.getX(), sv.getX()) || !DBMath.doublesClose(fPoint.getY(), sv.getY()))
{
// end of route is off-grid: adjust it
if (endIndex >= 2)
{
SearchVertex v1 = wf.vertices.get(wf.vertices.size()-2);
SearchVertex v2 = wf.vertices.get(wf.vertices.size()-1);
RoutingLayer type = metalLayers[wf.fromZ];
double width = Math.max(type.getMinWidth(), nr.minWidth);
if (v1.getX() == v2.getX())
{
// last line is vertical: run a horizontal bit
RoutingContact pNp = metalLayers[wf.fromZ].getPin();
RoutePoint intRP = makeNodeInst(pNp, new Point2D.Double(v1.getX(), fPoint.getY()), 0, nr);
makeArcInst(type, width, lastRP, intRP, nr);
lastRP = intRP;
} else if (v1.getY() == v2.getY())
{
// last line is horizontal: run a vertical bit
RoutingContact pNp = metalLayers[wf.fromZ].getPin();
RoutePoint intRP = makeNodeInst(pNp, new Point2D.Double(fPoint.getX(), v1.getY()), 0, nr);
makeArcInst(type, width, lastRP, intRP, nr);
lastRP = intRP;
}
}
}
} else
{
rp = makeNodeInst(np, new Point2D.Double(sv.getX(), sv.getY()), 0, nr);
}
if (lastRP != null)
{
RoutingLayer type = metalLayers[sv.getZ()];
double width = Math.max(type.getMinWidth(), nr.minWidth);
makeArcInst(type, width, lastRP, rp, nr);
}
lastRP = rp;
}
}
/**
* 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.
*/
private RoutePoint makeNodeInst(RoutingContact np, Point2D loc, int angle, NeededRoute nr)
{
RoutePoint rp = new RoutePoint(np, loc, angle);
nr.rs.addWireEnd(rp);
List<RoutingGeometry> geoms = np.getGeometry();
if (geoms != null)
{
for(RoutingGeometry rg : geoms)
{
addLayer(rg.getLayer(), rg.getBounds(), GenMath.MATID, nr.netID, false);
}
}
return rp;
}
/**
* 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 RouteWire makeArcInst(RoutingLayer type, double wid, RoutePoint from, RoutePoint to, NeededRoute nr)
{
RouteWire rw = new RouteWire(type, from, to, wid);
nr.rs.addWire(rw);
//System.out.println("PLAN ARC ON LAYER "+type.getName()+" FROM "+from.getContact().getName()+
// " AT ("+from.getLocation().getX()+","+from.getLocation().getY()+") TO "+to.getContact().getName()+
// " AT ("+to.getLocation().getX()+","+to.getLocation().getY()+")");
Rectangle2D box = makeArcBox(from.getLocation(), to.getLocation(), wid);
if (box != null)
addLayer(type, box, GenMath.MATID, nr.netID, false);
return rw;
}
public Rectangle2D makeArcBox(Point2D from, Point2D to, double width)
{
double w2 = width / 2;
int angle = DBMath.figureAngle(from, to);
switch (angle)
{
case 0:
case 1800:
double y = to.getY();
double x1 = to.getX();
double x2 = from.getX();
return new Rectangle2D.Double(Math.min(x1, x2)-w2, y - w2, Math.abs(x1-x2)+width, width);
case 900:
case 2700:
double x = to.getX();
double y1 = to.getY();
double y2 = from.getY();
return new Rectangle2D.Double(x-w2, Math.min(y1, y2)-w2, width, Math.abs(y1-y2)+width);
}
return null;
}
/**
* Method to sum up the distance that a route takes.
* @param vertices the list of SearchVertices in the route.
* @return the length of the route.
*/
private double getVertexLength(List<SearchVertex> vertices)
{
if (vertices == null) return Double.MAX_VALUE;
if (vertices.size() == 0) return Double.MAX_VALUE;
double sum = 0;
SearchVertex last = null;
for(SearchVertex sv : vertices)
{
if (last != null)
sum += Math.abs(sv.getX() - last.getX()) +
Math.abs(sv.getY() - last.getY()) +
Math.abs(sv.getZ() - last.getZ())*10;
last = sv;
}
return sum;
}
/**
* Method to find a path between two ports.
* @param nr the NeededRoute object with all necessary information.
* If successful, the NeededRoute's "vertices" field is filled with the route data.
*/
private void findPath(NeededRoute nr, Environment env, EditingPreferences ep)
{
// special case when route is null length
Wavefront d1 = nr.dir1;
if (DBMath.areEquals(d1.toX, d1.fromX) && DBMath.areEquals(d1.toY, d1.fromY) && d1.toZ == d1.fromZ)
{
nr.winningWF = d1;
nr.winningWF.vertices = new ArrayList<SearchVertex>();
SearchVertex sv = new SearchVertex(d1.toX, d1.toY, d1.toZ, 0, null, 0, nr.winningWF);
nr.winningWF.vertices.add(sv);
nr.cleanSearchMemory();
return;
}
if (parallelDij)
{
// create threads and start them running
Semaphore outSem = new Semaphore(0);
new DijkstraInThread("Route a->b", nr.dir1, nr.dir2, outSem, env, ep);
new DijkstraInThread("Route b->a", nr.dir2, nr.dir1, outSem, env, ep);
// wait for threads to complete and get results
outSem.acquireUninterruptibly(2);
} else
{
// run both wavefronts in parallel (interleaving steps)
doTwoWayDijkstra(nr);
}
// analyze the winning wavefront
Wavefront wf = nr.winningWF;
double verLength = Double.MAX_VALUE;
if (wf != null) verLength = getVertexLength(wf.vertices);
if (verLength == Double.MAX_VALUE)
{
// failed to route
String errorMsg;
if (wf == null) wf = nr.dir1;
if (wf.vertices == null)
{
errorMsg = "Search too complex (exceeds complexity limit of " +
complexityLimit.getIntValue() + " steps)";
} else
{
errorMsg = "Failed to route from port " + wf.from.describe() + " to port " + wf.to.describe();
}
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);
}
nr.cleanSearchMemory();
}
private void doTwoWayDijkstra(NeededRoute nr)
{
SearchVertex result = null;
int numSearchVertices = 0;
while (result == null)
{
// stop if the search is too complex
numSearchVertices++;
if (numSearchVertices > complexityLimit.getIntValue()) 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;
}
List<SearchVertex> realVertices = getOptimizedList(result);
nr.winningWF.vertices = realVertices;
}
private class DijkstraInThread extends Thread
{
private Wavefront wf;
private Wavefront otherWf;
private Semaphore whenDone;
private Environment env;
private EditingPreferences ep;
public DijkstraInThread(String name, Wavefront wf, Wavefront otherWf, Semaphore whenDone, Environment env, EditingPreferences ep)
{
super(name);
this.wf = wf;
this.otherWf = otherWf;
this.whenDone = whenDone;
this.env = env;
this.ep = ep;
start();
}
public void run()
{
Environment.setThreadEnvironment(env);
EditingPreferences.setThreadEditingPreferences(ep);
SearchVertex result = null;
int numSearchVertices = 0;
while (result == null)
{
// stop if the search is too complex
numSearchVertices++;
if (numSearchVertices > complexityLimit.getIntValue())
{
result = svLimited;
} else
{
if (wf.abort) result = svAborted; else
{
result = advanceWavefront(wf);
}
}
}
if (result != svAborted && result != svExhausted && result != svLimited)
{
if (DEBUGLOOPS)
System.out.println(" Wavefront " + wf.name + " first completion");
wf.vertices = getOptimizedList(result);
wf.nr.winningWF = wf;
otherWf.abort = true;
} else
{
if (DEBUGLOOPS)
{
String status = "completed";
if (result == svAborted) status = "aborted"; else
if (result == svExhausted) status = "exhausted"; else
if (result == svLimited) status = "limited";
System.out.println(" Wavefront " + wf.name + " " + status);
}
}
whenDone.release();
}
}
private SearchVertex advanceWavefront(Wavefront wf)
{
// get the lowest cost point
if (wf.active.size() == 0) return svExhausted;
SearchVertex svCurrent = wf.active.first();
wf.active.remove(svCurrent);
double curX = svCurrent.getX();
double curY = svCurrent.getY();
int curZ = svCurrent.getZ();
if (wf.debug) System.out.print("AT ("+TextUtils.formatDouble(curX)+","+TextUtils.formatDouble(curY)+",M"
+(curZ+1)+")C="+svCurrent.cost+" WENT");
// look at all directions from this point
for(int i=0; i<6; i++)
{
// compute a neighboring point
double dx = 0, dy = 0;
int dz = 0;
switch (i)
{
case 0:
dx = -GRAINSIZE;
double intermediate = upToGrainAlways(curX+dx);
if (intermediate != curX+dx) dx = intermediate - curX;
break;
case 1:
dx = GRAINSIZE;
intermediate = downToGrainAlways(curX+dx);
if (intermediate != curX+dx) dx = intermediate - curX;
break;
case 2:
dy = -GRAINSIZE;
intermediate = upToGrainAlways(curY+dy);
if (intermediate != curY+dy) dy = intermediate - curY;
break;
case 3:
dy = GRAINSIZE;
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; }
// 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(metalLayers[nZ].getMinWidth(), 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;
}
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 = metalLayers[nZ].getMaxSurround();
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);
RoutingContact np = mv.via;
List<RoutingGeometry> conGeoms = mv.via.getGeometry();
AffineTransform trans = null;
if (mv.orientation != 0) trans = makePureTransform(mv.orientation);
// count the number of cuts and make an array for the data
int cutCount = 0;
for(RoutingGeometry rg : conGeoms)
if (!rg.getLayer().isMetal()) cutCount++;
Point2D [] curCuts = new Point2D[cutCount];
cutCount = 0;
boolean failed = false;
for(RoutingGeometry rg : conGeoms)
{
Rectangle2D conRect = rg.getBounds();
if (trans != null)
{
conRect = (Rectangle2D)conRect.clone();
DBMath.transformRect(conRect, trans);
}
RoutingLayer conLayer = rg.getLayer();
if (conLayer.isMetal())
{
int metalNo = conLayer.getMetalNumber() - 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
{
// make sure vias don't get too close
double conCX = conRect.getCenterX();
double conCY = conRect.getCenterY();
double surround = np.getViaSpacing();
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 (downToGrainAlways(nX) != nX && nX != wf.toX) svNext.cost += COSTOFFGRID;
if (downToGrainAlways(nY) != nY && nY != wf.toY) svNext.cost += COSTOFFGRID;
// add this vertex into the data structures
wf.setVertex(nX, nY, nZ);
wf.active.add(svNext);
if (wf.debug)
System.out.print("("+TextUtils.formatDouble(svNext.getX())+","+TextUtils.formatDouble(svNext.getY())+
",M"+(svNext.getZ()+1)+")C="+svNext.cost);
// add intermediate steps along the way if it was a jump
if (dz == 0)
{
if (Math.abs(dx) > 1)
{
double inc = dx < 0 ? 1 : -1;
double lessDX = dx + inc;
int cost = svNext.cost;
int countDown = 0;
for(;;)
{
double nowX = curX+lessDX;
cost++;
if (!wf.getVertex(nowX, nY, nZ))
{
SearchVertex svIntermediate = new SearchVertex(nowX, nY, nZ, whichContact, cuts, curZ, wf);
svIntermediate.last = svCurrent;
svIntermediate.cost = cost;
wf.setVertex(nowX, nY, nZ);
wf.active.add(svIntermediate);
}
lessDX += inc;
if (inc < 0)
{
if (lessDX < 1) break;
} else
{
if (lessDX > -1) break;
}
if (countDown++ >= 10) { countDown = 0; inc += inc; }
}
}
if (Math.abs(dy) > 1)
{
double inc = dy < 0 ? 1 : -1;
double lessDY = dy + inc;
int cost = svNext.cost;
int countDown = 0;
for(;;)
{
double nowY = curY+lessDY;
cost++;
if (!wf.getVertex(nX, nowY, nZ))
{
SearchVertex svIntermediate = new SearchVertex(nX, nowY, nZ, whichContact, cuts, curZ, wf);
svIntermediate.last = svCurrent;
svIntermediate.cost = cost;
wf.setVertex(nX, nowY, nZ);
wf.active.add(svIntermediate);
}
lessDY += inc;
if (inc < 0)
{
if (lessDY < 1) break;
} else
{
if (lessDY > -1) break;
}
if (countDown++ >= 10) { countDown = 0; inc += inc; }
}
}
}
}
if (wf.debug) System.out.println();
return null;
}
/**
* Method to convert a linked list of SearchVertex objects to an optimized path.
* @param initialThread the initial SearchVertex in the linked list.
* @return a List of SearchVertex objects optimized to consolidate runs in the X
* or Y axes.
*/
private List<SearchVertex> getOptimizedList(SearchVertex initialThread)
{
List<SearchVertex> realVertices = new ArrayList<SearchVertex>();
SearchVertex thread = initialThread;
if (thread != null)
{
SearchVertex lastVertex = thread;
realVertices.add(lastVertex);
thread = thread.last;
while (thread != null)
{
if (lastVertex.getZ() != thread.getZ())
{
realVertices.add(thread);
lastVertex = thread;
thread = thread.last;
} else
{
// gather a run of vertices on this layer
double dx = thread.getX() - lastVertex.getX();
double dy = thread.getY() - lastVertex.getY();
lastVertex = thread;
thread = thread.last;
while (thread != null)
{
if (lastVertex.getZ() != thread.getZ()) break;
if ((thread.getX() - lastVertex.getX() != 0 && dx == 0) ||
(thread.getY() - lastVertex.getY() != 0 && dy == 0)) break;
lastVertex = thread;
thread = thread.last;
}
realVertices.add(lastVertex);
}
}
}
return realVertices;
}
private double getJumpSize(double curX, double curY, int curZ, double dx, double dy, Wavefront wf)
{
Rectangle2D jumpBound = wf.nr.jumpBound;
double width = Math.max(metalLayers[curZ].getMinWidth(), 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) 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) 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) 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) 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)
{
return 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)
{
return 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; }
}
/**
* 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
RoutingLayer 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 = metalLayers[metNo].getMaxSurround();
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);
RoutingContact np = mv.via;
AffineTransform trans = null;
if (mv.orientation != 0) trans = makePureTransform(mv.orientation);
List<RoutingGeometry> conPolys = np.getGeometry();
for(RoutingGeometry rg : conPolys)
{
if (rg.getLayer() != layer) continue;
Rectangle2D bound = rg.getBounds();
if (trans != null)
{
bound = (Rectangle2D)bound.clone();
DBMath.transformRect(bound, trans);
}
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
RoutingLayer type = metalLayers[metNo];
double width = Math.max(type.getMinWidth(), minWidth);
Point2D head = new Point2D.Double(sv.getX(), sv.getY());
Point2D tail = new Point2D.Double(lastSv.getX(), lastSv.getY());
Rectangle2D bound = makeArcBox(head, tail, width);
if (bound != null)
{
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;
}
// 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);
RoutingContact np = mv.via;
AffineTransform trans = null;
if (mv.orientation != 0) trans = makePureTransform(mv.orientation);
List<RoutingGeometry> conPolys = np.getGeometry();
for(RoutingGeometry rg : conPolys)
{
if (rg.getLayer() != layer) continue;
Rectangle2D bound = rg.getBounds();
if (trans != null)
{
bound = (Rectangle2D)bound.clone();
DBMath.transformRect(bound, trans);
}
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
RoutingLayer type = metalLayers[metNo];
double width = Math.max(type.getMinWidth(), minWidth);
Point2D head = new Point2D.Double(sv.getX(), sv.getY());
Point2D tail = new Point2D.Double(lastSv.getX(), lastSv.getY());
Rectangle2D bound = makeArcBox(head, tail, width);
if (bound != null)
{
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;
}
private AffineTransform makePureTransform(int angle)
{
int sect = angle / 45;
int ang = angle % 45;
if (sect % 2 != 0) ang = 45 - ang;
double cos0, sin0;
if (ang == 0) {
cos0 = 1;
sin0 = 0;
} else if (ang == 45) {
cos0 = sin0 = StrictMath.sqrt(0.5);
} else {
double alpha = ang * Math.PI / 180.0;
cos0 = StrictMath.cos(alpha);
sin0 = StrictMath.sin(alpha);
}
double cos = 0, sin = 0;
switch (sect) {
case 0: cos = cos0; sin = sin0; break;
case 1: cos = sin0; sin = cos0; break;
case 2: cos = -sin0; sin = cos0; break;
case 3: cos = -cos0; sin = sin0; break;
case 4: cos = -cos0; sin = -sin0; break;
case 5: cos = -sin0; sin = -cos0; break;
case 6: cos = sin0; sin = -cos0; break;
case 7: cos = cos0; sin = -sin0; break;
}
double[] matrix = new double[4];
matrix[0] = cos;
matrix[1] = sin;
matrix[2] = sin;
matrix[3] = cos;
return new AffineTransform(matrix);
}
/**
* 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
RoutingLayer 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;
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, RoutingLayer 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;
}
/**
* 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(RoutingLayer layer, Rectangle2D box, AffineTransform trans, int netID, boolean canPlacePseudo)
{
if (layer.isMetal())
{
addRectangle(box, layer, netID);
} else
{
addVia(new Point2D.Double(box.getCenterX(), box.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, RoutingLayer 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(bounds, netID));
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(Point2D loc, RoutingLayer 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
{
RoutingContact via;
int orientation;
MetalVia(RoutingContact 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(RoutingContact 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)
{
RoutingContact pn1 = mv1.via;
RoutingContact pn2 = mv2.via;
double sz1 = pn1.getDefWidth() * pn1.getDefHeight();
double sz2 = pn2.getDefWidth() * pn2.getDefHeight();
if (sz1 < sz2) return -1;
if (sz1 > sz2) return 1;
return 0;
}
}
/************************************** DIJKSTRA PATH SEARCHING **************************************/
/**
* Class to define a vertex in the Dijkstra search.
*/
private class SearchVertex implements Comparable<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 sv)
{
int diff = cost - sv.cost;
if (diff != 0) return diff;
if (w != null)
{
double thisDist = Math.abs(xv-w.toX) + Math.abs(yv-w.toY) + Math.abs(zv-w.toZ);
double otherDist = Math.abs(sv.xv-w.toX) + Math.abs(sv.yv-w.toY) + Math.abs(sv.zv-w.toZ);
if (thisDist < otherDist) return -1;
if (thisDist > otherDist) return 1;
}
return 0;
}
}
// private void showSearchVertices(Map<Integer,Set<Integer>> [] planes, boolean horiz)
// {
// EditWindow_ wnd = Job.getUserInterface().getCurrentEditWindow_();
// for(int i=0; i<numMetalLayers; i++)
// {
// double offset = i;
// offset -= (numMetalLayers-2) / 2.0;
// offset /= numMetalLayers+2;
// Map<Integer,Set<Integer>> plane = planes[i];
// if (plane == null) continue;
// for(Integer y : plane.keySet())
// {
// double yv = y.doubleValue();
// Set<Integer> row = plane.get(y);
// for(Iterator<Integer> it = row.iterator(); it.hasNext(); )
// {
// Integer x = it.next();
// double xv = x.doubleValue();
// Point2D pt1, pt2;
// if (horiz)
// {
// pt1 = new Point2D.Double(xv-0.5, yv+offset);
// pt2 = new Point2D.Double(xv+0.5, yv+offset);
// } else
// {
// pt1 = new Point2D.Double(xv+offset, yv-0.5);
// pt2 = new Point2D.Double(xv+offset, yv+0.5);
// }
// wnd.addHighlightLine(pt1, pt2, cell, false, false);
// }
// }
// }
// }
}