/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: RoutingFrameLeeMoore.java
* Written by: Dennis Appelt, Sven Janko (Team 2)
*
* Copyright (c) 2010, 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.experimentalLeeMoore3;
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.util.TextUtils;
import com.sun.electric.util.math.DBMath;
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.List;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* Class to do the Lee-Moore routing inside the RoutingFrame.
*/
public class RoutingFrameLeeMoore extends BenchmarkRouter {
private static final boolean DEBUG = false;
// route segments with the same net id in parallel
private static final boolean PARALLEL = true;
// this option is still very experimental an will not find correct routes
static final boolean GLOBALDETAILEDROUTING = false;
/** The cell in which all the routing takes place. */
private Cell cell;
private Rectangle2D cellBounds;
/** List of all metal layers on which the RoutingWires will be placed. */
private RoutingLayer[] metalLayers;
private int numMetalLayers;
private double globalGridGranularity;
// private double globalGridStepsize;
private double detailedGridGranularity;
private Wavefront dummyDetailedWavefront;
/** vias to use to go up from each metal layer. */
private MetalVias[] metalVias;
private Map<RoutingLayer, RTNode> metalBlockages;
private Map<RoutingLayer, RTNode> viaBlockages;
/** Rating function used for influencing the quality of the routing */
RatingFunction[] ratingFunctionsForDetailed = {
new DistanceRatingFunction(),
// new MinimalCrossingRatingFunction(),
// new ShiftInDirectionRatingFunction(),
new OutOfBoundsRatingFunction(),
new ForcedDirectionRatingFunction() };
RatingFunction[] ratingFunctionsForGlobal = {
new DistanceRatingFunction(),
// new MinimalCrossingRatingFunction(),
// new ShiftInDirectionRatingFunction(),
new OutOfBoundsRatingFunction(),
new ForcedDirectionRatingFunction() };
/** used for parallelisation **/
// private static Object lockObj = new Object();
private ThreadPoolExecutor workerThreadsExecutor;
private ArrayBlockingQueue<Runnable> inQueue;
private ArrayBlockingQueue<Wavefront> outQueue;
/**
* Method to return the name of this routing algorithm.
*
* @return the name of this routing algorithm.
*/
public String getAlgorithmName() {
return "Lee/Moore - 3";
}
/**
* Method to do LeeMoore routing.
*/
protected void runRouting(Cell cell, List<RoutingSegment> segmentsToRoute,
List<RoutingLayer> allLayers, List<RoutingContact> allContacts,
List<RoutingGeometry> blockages) {
// set default timeout
if (maxRuntime.getTempIntValue() <= 0) {
maxRuntime.setTempIntValue(800);
}
// stop the time so we can abort the routing if we exceed
// <code>this.secTimeout</code> seconds
long timeStart = System.currentTimeMillis();
// create Workerthreadpool
// Set max. number of threads according to the parameters
int maxPoolSize = numThreads.getTempIntValue();
maxPoolSize = maxPoolSize > 0 ? maxPoolSize : 4;
int poolSize = maxPoolSize > 1 ? maxPoolSize / 2 : 1;
long keepAliveTime = 10;
inQueue = new ArrayBlockingQueue<Runnable>(40);
outQueue = new ArrayBlockingQueue<Wavefront>(40);
workerThreadsExecutor = new ThreadPoolExecutor(poolSize, maxPoolSize,
keepAliveTime, TimeUnit.SECONDS, inQueue);
Job.getUserInterface().startProgressDialog(
"Collect startup information", null);
Job.getUserInterface().setProgressNote("Initialize design roules");
// initialize information about the technology
if (initializeDesignRules(allLayers, allContacts))
return;
Job.getUserInterface().setProgressNote("Set up data structures");
// Job.getUserInterface().setProgressValue(0);
this.cell = cell;
this.cellBounds = this.cell.getBounds();
double x = cellBounds.getMaxX() - cellBounds.getMinX();
double y = cellBounds.getMaxY() - cellBounds.getMinY();
globalGridGranularity = 1 / (((x < y) ? x : y) / 32);
// globalGridStepsize = 1 / globalGridGranularity;
detailedGridGranularity = globalGridGranularity * 32;
dummyDetailedWavefront = new Wavefront(segmentsToRoute.get(0),
detailedGridGranularity, cellBounds,
ratingFunctionsForDetailed, false);
this.metalBlockages = new HashMap<RoutingLayer, RTNode>();
this.viaBlockages = new HashMap<RoutingLayer, RTNode>();
for (RoutingGeometry rg : blockages) {
RoutingLayer l = rg.getLayer();
Rectangle2D rect = rg.getBounds();
if (l.isMetal())
addRectangle(rect, l, rg.getNetID());
else
addVia(
new Point2D.Double(rect.getCenterX(), rect.getCenterY()),
l, rg.getNetID());
}
addBlockagesAtPorts(segmentsToRoute);
Job.getUserInterface().stopProgressDialog();
Job.getUserInterface().startProgressDialog("Routing segments", null);
int numSegments = segmentsToRoute.size();
int prevNetId = (numSegments > 0) ? segmentsToRoute.get(0).getNetID()
: -1;
for (int i = 0; i < numSegments; i++) {
Job.getUserInterface().setProgressValue(
(int) (((double) i / (double) numSegments) * 100));
Job.getUserInterface().setProgressNote(
"Routing segment " + (i + 1) + " of " + numSegments + ".");
RoutingSegment currentSegment = segmentsToRoute.get(i);
if (PARALLEL) {
try {
// if the current segment is from a different net id as the
// last segment,
// finish all previous wavefronts and do the backtracking.
// Otherwise
// the blockage information wouldn't be consistent.
if (prevNetId != currentSegment.getNetID()) {
while (outQueue.size() != 0
|| workerThreadsExecutor.getQueue().size() != 0) {
dequeueWavefrontJob();
}
}
// the capacity of the job queue's is full, take some jobs
// from the out queue
if (workerThreadsExecutor.getQueue().size() > 39
|| outQueue.size() > 30) {
dequeueWavefrontJob();
}
if (GLOBALDETAILEDROUTING) {
Wavefront[] detailedWavefronts = doGlobalRouting(currentSegment);
for (Wavefront w : detailedWavefronts) {
enqueueWavefrontJob(w);
}
} else {
Wavefront w = new Wavefront(currentSegment, 0.2,
cellBounds, ratingFunctionsForDetailed, false);
enqueueWavefrontJob(w);
}
} catch (CouldNotCalculateVirtualTerminalException e2) {
// computation of detailed segments not possible, try to
// route
// the global segment
if (enableOutput.getTempBooleanValue())
System.out.print(i
+ ": (unable to calculate virtual Terminals) ");
Wavefront w = new Wavefront(currentSegment, 0.2,
cellBounds, ratingFunctionsForDetailed, false);
enqueueWavefrontJob(w);
} catch (RatingNotFoundException e) {
// caused likely because the global backtracking didn't find
// a rating
// for a already visited point
if (enableOutput.getTempBooleanValue())
System.out
.print(i
+ ": (global backtracking did not find rating) ");
Wavefront w = new Wavefront(currentSegment, 0.2,
cellBounds, ratingFunctionsForDetailed, false);
enqueueWavefrontJob(w);
}
prevNetId = currentSegment.getNetID();
} else {
// not parallel
if (enableOutput.getTempBooleanValue())
System.out.print(i + ": ");
Wavefront w = new Wavefront(currentSegment, 0.2, cellBounds,
ratingFunctionsForDetailed, DEBUG);
w.propagate();
if (w.foundRoute)
new Backtracking(w).routeIt();
}
if ((System.currentTimeMillis() - timeStart) / 1000 > maxRuntime
.getTempIntValue()) {
if (enableOutput.getTempBooleanValue())
System.out
.println("Routing aborted. Maximum execution time ("
+ maxRuntime.getTempIntValue()
+ " seconds) reached.");
break;
}
}
// wait until all running wavefront jobs are finished
while (outQueue.size() != 0
|| workerThreadsExecutor.getQueue().size() != 0) {
dequeueWavefrontJob();
}
Job.getUserInterface().stopProgressDialog();
}
private void enqueueWavefrontJob(Wavefront w) {
boolean submitToWorkpool = false;
do {
try {
workerThreadsExecutor.execute(new WavefrontExecutor(w));
submitToWorkpool = true;
// w.propagate();
}
// work pool for detailed routing is full
catch (RejectedExecutionException e) {
Wavefront doneWavefront;
try {
doneWavefront = outQueue.take();
if (doneWavefront.foundRoute)
new Backtracking(doneWavefront).routeIt();
} catch (InterruptedException e1) {
if (enableOutput.getTempBooleanValue())
e1.printStackTrace();
}
}
} while (!submitToWorkpool);
}
private void dequeueWavefrontJob() {
Wavefront w;
try {
w = outQueue.take();
if (w.foundRoute)
new Backtracking(w).routeIt();
} catch (InterruptedException e) {
if (enableOutput.getTempBooleanValue())
e.printStackTrace();
}
}
public Wavefront[] doGlobalRouting(RoutingSegment globalSegment)
throws CouldNotCalculateVirtualTerminalException,
RatingNotFoundException {
Wavefront globalWF = new Wavefront(globalSegment,
globalGridGranularity, cellBounds, ratingFunctionsForGlobal,
false, true);
globalWF.propagate();
ExperimentalGlobalBacktracking backtracking = new ExperimentalGlobalBacktracking(
globalWF);
List<ExperimentalGlobalBacktracking.Point> globalSections = backtracking
.routeIt();
ArrayList<Rectangle2D> detailedSegmentBounds = new ArrayList<Rectangle2D>();
for (ExperimentalGlobalBacktracking.Point p : globalSections)
detailedSegmentBounds.add(getBoundsForGridPoint(p, globalWF));
Wavefront[] detailedWavefronts = calcVirtualTerminals(
detailedSegmentBounds.toArray(new Rectangle2D[0]),
globalSegment, backtracking, globalWF);
return detailedWavefronts;
}
public Rectangle2D getBoundsForGridPoint(
ExperimentalGlobalBacktracking.Point p, Wavefront wf) {
double segmentSize = ((cellBounds.getMaxY() - cellBounds.getMinY()) / 32);
double diffToBorder = ((cellBounds.getMaxY() - cellBounds.getMinY()) / 32) / 2;
double min = 0.00000001;
double sizeOfRect = ((long) ((segmentSize - min) * 100000000)) / 100000000.0;
Rectangle2D rect = new Rectangle2D.Double(wf.toGrid(p.x) - diffToBorder
+ min, wf.toGrid(p.y) - diffToBorder + min, sizeOfRect,
sizeOfRect);
return rect;
}
public Rectangle2D getBoundsForGridPoint(Point2D p, Wavefront wf) {
double segmentSize = ((cellBounds.getMaxY() - cellBounds.getMinY()) / 32);
double diffToBorder = ((cellBounds.getMaxY() - cellBounds.getMinY()) / 32) / 2;
double min = 0.00000001;
double sizeOfRect = ((long) ((segmentSize - min) * 100000000)) / 100000000.0;
Rectangle2D rect = new Rectangle2D.Double(wf.toGrid(p.getX())
- diffToBorder + min, wf.toGrid(p.getY()) - diffToBorder + min,
sizeOfRect, sizeOfRect);
return rect;
}
public Wavefront[] calcVirtualTerminals(
Rectangle2D[] detailedSegmentBounds, RoutingSegment globalSegment,
ExperimentalGlobalBacktracking dummy, Wavefront dummyWF)
throws CouldNotCalculateVirtualTerminalException {
Wavefront[] detailedSegments = new Wavefront[detailedSegmentBounds.length];
Point2D startPoint = null, finishPoint = null, nextStartPoint = null;
startPoint = globalSegment.getFinishEnd().getLocation();
for (int i = 0; i + 1 < detailedSegmentBounds.length; i++) {
boolean blockage = false;
double rnd = Math.random();
for (int j = 0; j < 5; j++) {
double tmp = (rnd + (0.2 * j)) / 1.0;
// calculate the X- and Y-coordinate for the virtual terminals
Point2D[] points = calcPointsForVirtualTerminals(
detailedSegmentBounds[i], detailedSegmentBounds[i + 1],
tmp);
finishPoint = points[0];
nextStartPoint = points[1];
// check if the virtual terminal is located within a blockage
double minWidth = globalSegment.getWidestArcAtStart();
RoutingLayer currentLayer = globalSegment.getStartLayers().get(
0);
double surround = currentLayer.getMinSpacing(currentLayer);
double metalSpacing = Math.max(currentLayer.getMinWidth(),
minWidth) / 2;
blockage = false;
// if the calculated virtual terminals are within blockages,
// calculate new
// virtual terminals
for (Point2D p : points) {
LMBound block = getMetalBlockage(globalSegment.getNetID(),
currentLayer.getMetalNumber(), metalSpacing,
metalSpacing, surround, p.getX(), p.getY());
if (block != null) {
if (j == 4)
throw new CouldNotCalculateVirtualTerminalException();
blockage = true;
break;
}
}
// found a valid virtual terminals, stop the searching
if (!blockage)
break;
}
detailedSegments[i] = new Wavefront(globalSegment, startPoint,
finishPoint, globalSegment.getStartLayers().get(0),
globalSegment.getStartLayers().get(0),
detailedGridGranularity, detailedSegmentBounds[i],
ratingFunctionsForDetailed, false, false);
startPoint = nextStartPoint;
}
// add the wavefront which contains the finish end
finishPoint = globalSegment.getStartEnd().getLocation();
detailedSegments[detailedSegmentBounds.length - 1] = new Wavefront(
globalSegment, startPoint, finishPoint, globalSegment
.getStartLayers().get(0), globalSegment
.getFinishLayers().get(0), detailedGridGranularity,
detailedSegmentBounds[detailedSegmentBounds.length - 1],
ratingFunctionsForDetailed, false, false);
return detailedSegments;
}
private Point2D[] calcPointsForVirtualTerminals(
Rectangle2D firstSementBounds, Rectangle2D secondSementBounds,
double randomDouble) {
double crossingXforp1 = 0, crossingXforp2 = 0;
double crossingYforp1 = 0, crossingYforp2 = 0;
Point2D finishPoint = null, nextStartPoint = null;
if (firstSementBounds.getMinX() == secondSementBounds.getMinX()) {
boolean firstSmaller = firstSementBounds.getMaxY() < secondSementBounds
.getMaxY();
if (firstSmaller) {
crossingYforp1 = firstSementBounds.getMaxY();
crossingYforp2 = secondSementBounds.getMinY();
} else {
crossingYforp1 = firstSementBounds.getMinY();
crossingYforp2 = secondSementBounds.getMaxY();
}
// limit resolution of the random xCoord, otherwise there may be
// rounding errors if transforming the xCoord to a grid point
double xCoord = ((long) ((firstSementBounds.getMaxX() - firstSementBounds
.getMinX())
* randomDouble * 100000000)) / 100000000.0;
xCoord += firstSementBounds.getMinX();
xCoord = dummyDetailedWavefront.toGrid(xCoord);
crossingYforp1 = dummyDetailedWavefront.toGrid(crossingYforp1);
crossingYforp2 = dummyDetailedWavefront.toGrid(crossingYforp2);
finishPoint = new Point2D.Double(xCoord, crossingYforp1);
nextStartPoint = new Point2D.Double(xCoord, crossingYforp2);
} else {
boolean firstSmaller = firstSementBounds.getMaxX() < secondSementBounds
.getMaxX();
if (firstSmaller) {
crossingXforp1 = firstSementBounds.getMaxX();
crossingXforp2 = secondSementBounds.getMinX();
} else {
crossingXforp1 = firstSementBounds.getMinX();
crossingXforp2 = secondSementBounds.getMaxX();
}
// limit resolution of the random yCoord, otherwise there may be
// rounding errors if transforming the yCoord to a grid point
double yCoord = ((long) ((firstSementBounds.getMaxY() - firstSementBounds
.getMinY())
* randomDouble * 100000000)) / 100000000.0;
yCoord += firstSementBounds.getMinY();
yCoord = dummyDetailedWavefront.toGrid(yCoord);
crossingYforp1 = dummyDetailedWavefront.toGrid(crossingXforp1);
crossingYforp2 = dummyDetailedWavefront.toGrid(crossingXforp2);
finishPoint = new Point2D.Double(crossingXforp1, yCoord);
nextStartPoint = new Point2D.Double(crossingXforp2, yCoord);
}
return new Point2D[] { finishPoint, nextStartPoint };
}
/**
* Class to find a route on a segment.
*/
private class Wavefront {
private boolean debug;
RoutingSegment segment;
private RoutingLayer rlStart, rlFinish;
private double xStart, yStart;
/** These are the coords of the grid point next to the start point. */
double xGridStart, yGridStart;
int zStart;
double xFinish, yFinish;
/** These are the coords of the grid point next to the finish point. */
double xGridFinish, yGridFinish;
int zFinish;
private String netName;
private int netID;
RatingFunction[] ratingFunctions;
// variables related to the grid
private double granularity;
private double stepsize;
private Rectangle2D bounds;
boolean foundRoute;
boolean runInGlobalMode;
// the number of points the wavefront will visit (at most)
private int maxPointsLimit;
DataGrid[] grid;
/**
* The first point in this queue is the next that will be visited by the
* wavefront.<br>
* Sort criterion is the ranking of the grid point. The higher the
* ranking of a point the faster that point will be visited.
*/
PriorityQueue<Gridpoint> pointsToVisit;
Wavefront(RoutingSegment s, double granularity, Rectangle2D bounds,
RatingFunction[] ratingFunctions, boolean debug) {
// set start and finish layer
this.rlStart = s.getStartLayers().get(0);
this.rlFinish = s.getFinishLayers().get(0);
// set start and finish coordinates
this.xStart = s.getStartEnd().getLocation().getX();
this.yStart = s.getStartEnd().getLocation().getY();
this.xFinish = s.getFinishEnd().getLocation().getX();
this.yFinish = s.getFinishEnd().getLocation().getY();
// init everything else
init(s, granularity, bounds, ratingFunctions, debug, false);
// printInfo();
}
Wavefront(RoutingSegment s, Point2D startPoint, Point2D finishPoint,
RoutingLayer startLayer, RoutingLayer finishLayer,
double granularity, Rectangle2D bounds,
RatingFunction[] ratingFunctions, boolean debug,
boolean runInGlobalMode) {
// set start and finish layer
this.rlStart = startLayer;
this.rlFinish = finishLayer;
// set start and end point
this.xStart = startPoint.getX();
this.yStart = startPoint.getY();
this.xFinish = finishPoint.getX();
this.yFinish = finishPoint.getY();
// init everything else
init(s, granularity, bounds, ratingFunctions, debug,
runInGlobalMode);
// printInfo();
}
Wavefront(RoutingSegment s, double granularity, Rectangle2D bounds,
RatingFunction[] ratingFunctions, boolean debug,
boolean runInGlobalMode) {
// set start and finish layer
this.rlStart = s.getStartLayers().get(0);
this.rlFinish = s.getFinishLayers().get(0);
// set start and finish coordinates
this.xStart = s.getStartEnd().getLocation().getX();
this.yStart = s.getStartEnd().getLocation().getY();
this.xFinish = s.getFinishEnd().getLocation().getX();
this.yFinish = s.getFinishEnd().getLocation().getY();
// init everything else
init(s, granularity, bounds, ratingFunctions, debug,
runInGlobalMode);
// printInfo();
}
// helper method for constructor
private void init(RoutingSegment s, double granularity,
Rectangle2D bounds, RatingFunction[] ratingFunctions,
boolean debug, boolean runInGlobalMode) {
// set grid size
this.granularity = granularity;
stepsize = 1 / granularity;
this.bounds = bounds;
// default is detailed mode
this.runInGlobalMode = runInGlobalMode;
this.ratingFunctions = ratingFunctions;
this.segment = s;
this.debug = debug;
this.netName = this.segment.getNetName();
this.netID = this.segment.getNetID();
this.foundRoute = false;
// set grid start and finish coordinates
this.xGridStart = toGrid(this.xStart);
this.yGridStart = toGrid(this.yStart);
this.xGridFinish = toGrid(this.xFinish);
this.yGridFinish = toGrid(this.yFinish);
this.zStart = rlStart.getMetalNumber() - 1;
this.zFinish = rlFinish.getMetalNumber() - 1;
// if the Start-/End Points are virtual Terminals, transform the
// Start-/Endpoints to grid points
if (GLOBALDETAILEDROUTING) {
Point2D startPoint = segment.getStartEnd().getLocation();
if (!this.runInGlobalMode && xFinish != startPoint.getX()
&& yFinish != startPoint.getY()) {
xFinish = toGrid(xFinish);
yFinish = toGrid(yFinish);
}
Point2D finishPoint = segment.getFinishEnd().getLocation();
if (!this.runInGlobalMode && xStart != finishPoint.getX()
&& yStart != finishPoint.getY()) {
xStart = toGrid(xStart);
yStart = toGrid(yStart);
}
}
// generate a DataGrid for each metal layer
this.grid = new DataGrid[numMetalLayers];
for (int i = 0; i < numMetalLayers; i++) {
this.grid[i] = new DataGrid();
}
this.pointsToVisit = new PriorityQueue<Gridpoint>();
// add the start point to the list
this.pointsToVisit.add(new Gridpoint(this.xGridStart,
this.yGridStart, this.zStart, null));
this.grid[this.zStart].visit(this.xGridStart, this.yGridStart,
new Rating());
double tmp = DBMath.distBetweenPoints(this.segment.getStartEnd()
.getLocation(), this.segment.getFinishEnd().getLocation());
this.maxPointsLimit = (int) (tmp * this.granularity * 1800);
}
// helper method for constructor
private void printInfo() {
if (enableOutput.getTempBooleanValue()) {
System.out.println((runInGlobalMode ? "global" : "detailed")
+ " Wavefront \"" + this.netName + "\" started at ("
+ TextUtils.formatDouble(this.xStart) + ", "
+ TextUtils.formatDouble(this.yStart) + ", "
+ this.zStart + ") heading for ("
+ TextUtils.formatDouble(this.xFinish) + ", "
+ TextUtils.formatDouble(this.yFinish) + ", "
+ this.zFinish + ")" + " - NetID=" + this.netID + ", "
+ "Thread ID: " + Thread.currentThread().getId());
}
}
private void propagate() {
printInfo();
double x = this.xStart, y = this.yStart, nextX, nextY;
int z = this.zStart, nextZ;
RTNode blockages = metalBlockages.get(this.rlStart);
// check if the finish point can be reached
double minWidth = metalLayers[this.zFinish].getMinWidth();
double minSpacing = metalLayers[this.zFinish]
.getMinSpacing(metalLayers[this.zFinish]);
double halfWidth = minWidth / 2;
LMBound block = getMetalBlockage(netID, this.zFinish, halfWidth,
halfWidth, minSpacing, this.xGridFinish, this.yGridFinish);
if (block != null) {
if (enableOutput.getTempBooleanValue())
System.out
.println("There is a blockage at the finish point. Wavefront will not start.");
return;
}
Gridpoint curPoint = null;
// count the number of visited grid points
int counter = 0;
while ((curPoint = this.pointsToVisit.poll()) != null) {
counter++;
if (counter > this.maxPointsLimit) {
if (enableOutput.getTempBooleanValue())
System.out
.println("Wavefront did not find finish point (maximum number of visited points ("
+ this.maxPointsLimit + ") reached).");
return;
}
x = curPoint.getX();
y = curPoint.getY();
z = curPoint.getZ();
if (this.debug) {
this.segment.addWireEnd(new RoutePoint(metalLayers[z]
.getPin(), new Point2D.Double(x, y), 0));
}
// see if the wavefront reached the finish point
if (z == this.zFinish && x == this.xGridFinish
&& y == this.yGridFinish) {
this.foundRoute = true;
if (enableOutput.getTempBooleanValue() && this.debug) {
System.out.println("routing found");
System.out.println("Visited " + counter
+ " Grid Points.");
}
return;
}
// spread in all six directions
for (int i = 0; i < 6; i++) {
double dx = 0, dy = 0;
int dz = 0;
switch (i) {
// 0-3: the 4 directions on the same layer
case 0:
dy = stepsize;
break;
case 1:
dx = stepsize;
break;
case 2:
dy = -stepsize;
break;
case 3:
dx = -stepsize;
break;
// 4,5: change layer
case 4:
dz = -1;
break;
case 5:
dz = 1;
break;
}
nextX = toGrid(x + dx);
nextY = toGrid(y + dy);
nextZ = z + dz;
if (nextZ < 0 || nextZ >= numMetalLayers
|| this.grid[nextZ].isPointVisited(nextX, nextY))
continue; // continue with next direction
// if running in global Mode, ignore blockages
if (!runInGlobalMode) {
minWidth = metalLayers[nextZ].getMinWidth();
minSpacing = metalLayers[nextZ]
.getMinSpacing(metalLayers[nextZ]);
// add some extra space (this.stepsize / 2)
// to avoid spacing errors when we move wires
// at start and finish ends
// (see adjustment in backtracking)
halfWidth = (minWidth + this.stepsize) / 2;
block = getMetalBlockage(netID, nextZ, halfWidth,
halfWidth, minSpacing, nextX, nextY);
if (block != null) {
continue;
}
}
Gridpoint nextPoint = new Gridpoint(nextX, nextY, nextZ,
curPoint);
for (RatingFunction f : ratingFunctions) {
f.doRating(nextPoint, curPoint, this.xFinish,
this.yFinish, blockages, bounds);
}
nextPoint.getRating().calcRating();
this.pointsToVisit.add(nextPoint);
this.grid[nextZ].visit(nextX, nextY, nextPoint.getRating());
}
}
if (enableOutput.getTempBooleanValue())
System.out
.println("Wavefront did not find finish point (stuck).");
}
public double getGranularity() {
return granularity;
}
/**
* Method to round a value to the nearest point of our routing grid.
*
* @param d
* The value to round.
* @return The grid value.
*/
private double toGrid(double d) {
return Math.round(d * granularity) * stepsize;
}
} // end class Wavefront
/**
* Class to to the backtracking after the wavefront reached the finish
* point.
*/
private class Backtracking {
private Wavefront w;
private double granularity;
private double stepsize;
public Backtracking(Wavefront w) {
this.w = w;
granularity = w.getGranularity();
stepsize = 1 / granularity;
}
private class Point {
double x, y;
int zLast, z;
boolean isVia = false;
Point(double x, double y, int zLast, int z) {
this.x = x;
this.y = y;
this.zLast = zLast;
this.z = z;
this.isVia = true;
}
Point(double x, double y, int z) {
this.x = x;
this.y = y;
this.z = z;
}
}
public void routeIt() {
List<Point> points = new ArrayList<Point>();
double x = this.w.xGridFinish, y = this.w.yGridFinish;
double xLast = x, yLast = y;
double xNextToLast = xLast, yNextToLast = yLast;
int z = w.zFinish, zLast = w.zFinish;
// add finish route point
points.add(new Point(this.w.xFinish, this.w.yFinish, z));
int lastRating = 0;
Rating tmp = w.grid[z].getRating(x, y);
if (tmp != null)
lastRating = tmp.getRating();
else {
if (enableOutput.getTempBooleanValue())
System.out
.println("Backtracking did not find a route (Nullpointer Rating)!");
return;
}
Rating r = tmp;
// loop until start point is reached
while ((x != this.w.xGridStart) || (y != this.w.yGridStart)
|| z != w.zStart) {
// if there was a layer change
// or if there was a shift in direction
if (z != zLast) {
points.add(new Point(x, y, zLast, z));
} else if (x != xNextToLast && y != yNextToLast) {
points.add(new Point(xLast, yLast, z));
}
for (int i = 0; i < 6; i++) {
double dx = 0, dy = 0;
int dz = 0;
switch (i) {
// 0-3: the 4 directions on the same layer
case 0:
dy = stepsize;
break;
case 1:
dx = stepsize;
break;
case 2:
dy = -stepsize;
break;
case 3:
dx = -stepsize;
break;
// 4,5: change layer
case 4:
dz = -1;
break;
case 5:
dz = 1;
break;
}
// check bounds
if (z + dz < 0 || z + dz >= numMetalLayers)
continue;
r = this.w.grid[z + dz].getRating(toGrid(x + dx), toGrid(y
+ dy));
if (r == null || r.getRating() > lastRating) {
if (i == 5) {
if (enableOutput.getTempBooleanValue())
System.out
.println("Backtracking did not find a route!");
return;
} else
continue;
}
xNextToLast = xLast;
xLast = x;
yNextToLast = yLast;
yLast = y;
zLast = z;
lastRating = r.getRating();
x = toGrid(x + dx);
y = toGrid(y + dy);
z += dz;
break;
}
}
if (z != zLast) {
points.add(new Point(this.w.xStart, this.w.yStart, zLast, z));
} else {
points.add(new Point(this.w.xStart, this.w.yStart, z));
}
int size = points.size();
if (size == 2) {
Point p1 = points.get(0);
Point p2 = points.get(1);
if (p1.x != p2.x && p1.y != p2.y) {
points.add(1, new Point(p1.x, p2.y, p1.z));
}
} else if (size == 3) {
Point p = points.get(1);
if (Math.abs(p.x - this.w.xFinish) < Math.abs(p.y
- this.w.yFinish)) {
p.x = this.w.xFinish;
p.y = this.w.yStart;
} else {
p.x = this.w.xStart;
p.y = this.w.yFinish;
}
} else if (size == 4) {
Point p1 = points.get(1), p2 = points.get(2);
if (Math.abs(p1.x - this.w.xFinish) < Math.abs(p1.y
- this.w.yFinish)) {
p1.x = this.w.xFinish;
if (p2.x != p1.x && p2.y != p1.y) {
p2.x = p1.x;
p2.y = this.w.yStart;
} else {
p2.x = this.w.xStart;
}
} else {
p1.y = this.w.yFinish;
if (p2.x != p1.x && p2.y != p1.y) {
p2.y = p1.y;
p2.x = this.w.xStart;
} else {
p2.y = this.w.yStart;
}
}
} else if (size > 4) {
Point p1 = points.get(1), p2 = points.get(size - 2);
boolean adjustX = false, adjustY = false;
if (Math.abs(p1.x - this.w.xFinish) < Math.abs(p1.y
- this.w.yFinish)) {
p1.x = this.w.xFinish;
Point p = points.get(2);
if (p1.isVia || (p.x != p1.x && p.y != p1.y))
adjustX = true;
} else {
p1.y = this.w.yFinish;
Point p = points.get(2);
if (p1.isVia || (p.x != p1.x && p.y != p1.y))
adjustY = true;
}
if (adjustX || adjustY) {
x = p1.x;
y = p1.y;
int i = 2;
while (true) {
p1 = points.get(i);
if (p1.x == x || p1.y == y || i == size - 1)
break;
if (adjustX)
p1.x = this.w.xFinish;
else
p1.y = this.w.yFinish;
if (!p1.isVia)
break;
x = p1.x;
y = p1.y;
i++;
}
}
adjustX = adjustY = false;
if (Math.abs(p2.x - this.w.xStart) < Math.abs(p2.y
- this.w.yStart)) {
p2.x = this.w.xStart;
Point p = points.get(size - 3);
if (p2.isVia || (p.x != p2.x && p.y != p2.y))
adjustX = true;
} else {
p2.y = this.w.yStart;
Point p = points.get(size - 3);
if (p2.isVia || (p.x != p2.x && p.y != p2.y))
adjustY = true;
}
if (adjustX || adjustY) {
x = p2.x;
y = p2.y;
int i = size - 3;
while (true) {
p2 = points.get(i);
if (p2.x == x || p2.y == y || i == 0)
break;
if (adjustX)
p2.x = this.w.xStart;
else
p2.y = this.w.yStart;
if (!p2.isVia)
break;
x = p2.x;
y = p2.y;
i--;
}
}
}
RoutingContact pin;
RoutePoint rpCurrent = null, rpLast = null, rpFix = null;
RoutingLayer l = null;
int i = 0;
for (Point p : points) {
if (i == 0) {
if (GLOBALDETAILEDROUTING) {
Point2D startPoint = this.w.segment.getStartEnd()
.getLocation();
if (p.x == startPoint.getX()
&& p.y == startPoint.getY()) {
pin = RoutingContact.STARTPOINT;
l = metalLayers[p.z];
} else {
if (p.isVia) {
pin = metalVias[Math.min(p.z, p.zLast)]
.getVias().get(0).via;
l = metalLayers[p.zLast];
} else {
l = metalLayers[p.z];
pin = l.getPin();
}
}
} else {
pin = RoutingContact.FINISHPOINT;
l = metalLayers[p.z];
}
} else if (i == points.size() - 1) {
if (GLOBALDETAILEDROUTING) {
Point2D finishPoint = this.w.segment.getFinishEnd()
.getLocation();
if (p.x == finishPoint.getX()
&& p.y == finishPoint.getY()) {
pin = RoutingContact.FINISHPOINT;
l = metalLayers[p.z];
} else {
if (p.isVia) {
pin = metalVias[Math.min(p.z, p.zLast)]
.getVias().get(0).via;
l = metalLayers[p.zLast];
} else {
l = metalLayers[p.z];
pin = l.getPin();
}
}
} else {
pin = RoutingContact.STARTPOINT;
l = metalLayers[p.z];
}
} else {
if (p.isVia) {
pin = metalVias[Math.min(p.z, p.zLast)].getVias()
.get(0).via;
l = metalLayers[p.zLast];
} else {
l = metalLayers[p.z];
pin = l.getPin();
}
}
rpCurrent = new RoutePoint(pin, new Point2D.Double(p.x, p.y), 0);
if (p.isVia) {
this.addWireEndVia(rpCurrent);
} else {
this.addWireEnd(rpCurrent);
}
if (i == 0 && p.isVia) {
rpFix = new RoutePoint(metalVias[Math.min(p.z, p.zLast)]
.getVias().get(0).via,
new Point2D.Double(p.x, p.y), 0);
this.addWireEndVia(rpFix);
l = metalLayers[p.zLast];
this.addWire(l, l.getMinWidth(), rpCurrent, rpFix,
this.w.netID);
rpCurrent = rpFix;
} else if (i == points.size() - 1 && p.isVia) {
rpFix = new RoutePoint(metalVias[Math.min(p.z, p.zLast)]
.getVias().get(0).via,
new Point2D.Double(p.x, p.y), 0);
this.addWireEndVia(rpFix);
l = metalLayers[p.zLast];
this.addWire(l, l.getMinWidth(), rpLast, rpFix,
this.w.netID);
rpLast = rpFix;
l = metalLayers[p.z];
}
if (rpLast != null) {
this.addWire(l, l.getMinWidth(), rpLast, rpCurrent,
this.w.netID);
}
rpLast = rpCurrent;
i++;
}
}
/**
* Method to round a value to the nearest point of our routing grid.
*
* @param d
* The value to round.
* @return The corresponding grid value.
*/
private double toGrid(double d) {
return Math.round(d * granularity) * stepsize;
}
private void addWireEnd(RoutePoint p) {
this.w.segment.addWireEnd(p);
}
private void addWireEndVia(RoutePoint p) {
this.w.segment.addWireEnd(p);
RoutingContact rc = p.getContact();
Point2D loc = p.getLocation();
Rectangle2D rect = this
.makeArcBox(loc, loc, rc.getViaSpacing() * 4);
addRectangle(rect, rc.getFirstLayer(), -this.w.netID);
addRectangle(rect, rc.getSecondLayer(), -this.w.netID);
}
/**
* Method to add a wire to the segment and update the R-Trees
* (blockages).
*
* @param l
* The routing layer.
* @param width
* The width of the wire.
* @param from
* Start point of the wire.
* @param to
* End point of the wire.
* @param netID
* The network ID.
* @return The RouteWire that was added.
*/
private RouteWire addWire(RoutingLayer l, double width,
RoutePoint from, RoutePoint to, int netID) {
RouteWire rw = new RouteWire(l, from, to, width);
this.w.segment.addWire(rw);
Rectangle2D box = makeArcBox(from.getLocation(), to.getLocation(),
width);
if (box != null)
addRectangle(box, l, netID);
return rw;
}
private 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);
}
if (enableOutput.getTempBooleanValue())
System.out.println("ERROR: Angle not allowed!");
return null;
}
} // end class Backtracking
/**
* Class to to the backtracking after the wavefront reached the finish
* point.
*/
private class ExperimentalGlobalBacktracking {
private Wavefront w;
private double granularity;
private double stepsize;
public ExperimentalGlobalBacktracking(Wavefront w) {
this.w = w;
granularity = w.getGranularity();
stepsize = 1 / granularity;
}
protected class Point {
double x, y;
Point(double x, double y, int zLast, int z) {
this.x = x;
this.y = y;
}
Point(double x, double y, int z) {
this.x = x;
this.y = y;
}
}
public List<Point> routeIt() throws RatingNotFoundException {
List<Point> points = new ArrayList<Point>();
double x = this.w.xGridFinish, y = this.w.yGridFinish;
int z = w.zFinish, zLast = w.zFinish;
// add finish route point
// points.add(new Point(this.w.xFinish, this.w.yFinish, z));
int lastRating = 0;
Rating tmp = w.grid[z].getRating(x, y);
if (tmp != null)
lastRating = tmp.getRating();
else {
if (enableOutput.getTempBooleanValue())
System.out
.println("Backtracking did not find a route (Nullpointer Rating)!");
throw new RatingNotFoundException();
}
// loop until start point is reached
outer: while ((x != this.w.xGridStart) || (y != this.w.yGridStart)
|| z != w.zStart) {
// Save the visited point if there wasn't a layer change
// (layer changes doesnt matter in global routing)
// if (z == zLast && (x != xNextToLast || y != yNextToLast)) {
if (z == zLast) {
points.add(new Point(x, y, z));
}
for (int i = 0; i < 6; i++) {
double dx = 0, dy = 0;
int dz = 0;
switch (i) {
// 0-3: the 4 directions on the same layer
case 0:
dy = stepsize;
break;
case 1:
dx = stepsize;
break;
case 2:
dy = -stepsize;
break;
case 3:
dx = -stepsize;
break;
// 4,5: change layer
case 4:
dz = -1;
break;
case 5:
dz = 1;
break;
}
// check bounds
if (z + dz < 0 || z + dz >= numMetalLayers)
continue;
Rating r = this.w.grid[z + dz].getRating(toGrid(x + dx),
toGrid(y + dy));
if (r == null || r.getRating() >= lastRating) {
if (i == 5) {
if (enableOutput.getTempBooleanValue())
System.out
.println("Backtracking did not find a route!");
break outer;
} else
continue;
}
zLast = z;
lastRating = r.getRating();
x = toGrid(x + dx);
y = toGrid(y + dy);
z += dz;
break;
}
}
if (z != zLast) {
points.add(new Point(this.w.xStart, this.w.yStart, zLast, z));
} else {
points.add(new Point(this.w.xStart, this.w.yStart, z));
}
return points;
}
/**
* Method to round a value to the nearest point of our routing grid.
*
* @param d
* The value to round.
* @return The grid value.
*/
private double toGrid(double d) {
return Math.round(d * granularity) * stepsize;
}
} // end class Backtracking
private boolean initializeDesignRules(List<RoutingLayer> allLayers,
List<RoutingContact> allContacts) {
// find the metal layers, arcs, and contacts
this.numMetalLayers = 0;
for (RoutingLayer rl : allLayers)
if (rl.isMetal())
this.numMetalLayers++;
this.metalLayers = new RoutingLayer[this.numMetalLayers];
int metNo = 0;
for (RoutingLayer rl : allLayers) {
if (rl.isMetal())
this.metalLayers[metNo++] = rl;
}
this.metalVias = new MetalVias[this.numMetalLayers - 1];
for (int i = 0; i < this.numMetalLayers - 1; i++)
this.metalVias[i] = new MetalVias();
for (RoutingContact rc : allContacts) {
for (int i = 0; i < this.numMetalLayers - 1; i++) {
if ((rc.getFirstLayer() == this.metalLayers[i] && rc
.getSecondLayer() == this.metalLayers[i + 1])
|| (rc.getSecondLayer() == this.metalLayers[i] && rc
.getFirstLayer() == this.metalLayers[i + 1])) {
this.metalVias[i].addVia(rc);
break;
}
}
}
for (int i = 0; i < numMetalLayers; i++) {
if (metalLayers[i] == null) {
if (enableOutput.getTempBooleanValue())
System.out.println("ERROR: Cannot find layer for Metal "
+ (i + 1));
return true;
}
if (i < numMetalLayers - 1) {
if (metalVias[i].getVias().size() == 0) {
if (enableOutput.getTempBooleanValue())
System.out
.println("ERROR: Cannot find contact node between Metal "
+ (i + 1) + " and Metal " + (i + 2));
return true;
}
}
}
return false;
}
private class WavefrontExecutor implements Runnable {
private Wavefront w;
public WavefrontExecutor(Wavefront w) {
this.w = w;
}
public void run() {
w.propagate();
try {
outQueue.put(w);
} catch (InterruptedException e) {
if (enableOutput.getTempBooleanValue())
e.printStackTrace();
}
}
}
/**
* 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 LMBound 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 = metalBlockages.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);
try {
// see if there is anything in that area
for (RTNode.Search sea = new RTNode.Search(searchArea, rtree, true); sea
.hasNext();) {
LMBound sBound = (LMBound) sea.next();
// ignore if on the same net
if (sBound.getNetID() == netID)
continue;
return sBound;
}
} catch (NullPointerException e) {
return new LMBound(new Rectangle2D.Double(), -1);
}
return null;
}
/**
* 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) {
addBlockage(rs.getStartEnd().getLocation(), rs.getStartLayers(), rs
.getStartLayers().get(0).getMinWidth() + 2, rs.getNetID());
addBlockage(rs.getFinishEnd().getLocation(), rs.getFinishLayers(),
rs.getFinishLayers().get(0).getMinWidth() + 2, 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 bounds = new Rectangle2D.Double(loc.getX()
- minWidth, loc.getY() - minWidth, 2 * minWidth,
2 * minWidth);
addRectangle(bounds, rg.getLayer(), 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 = this.metalBlockages.get(layer);
if (root == null) {
root = RTNode.makeTopLevel();
this.metalBlockages.put(layer, root);
}
RTNode newRoot = RTNode
.linkGeom(null, root, new LMBound(bounds, netID));
if (newRoot != root)
this.metalBlockages.put(layer, newRoot);
}
/**
* Method to add a point to the via R-Tree.
*
* @param loc
* The point to add.
* @param layer
* The 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 = this.viaBlockages.get(layer);
if (root == null) {
root = RTNode.makeTopLevel();
this.viaBlockages.put(layer, root);
}
RTNode newRoot = RTNode.linkGeom(null, root, new LMVia(loc, netID));
if (newRoot != root)
this.viaBlockages.put(layer, newRoot);
}
/**
* Class to define a list of possible nodes that can connect two layers.
*/
private static class MetalVia {
RoutingContact via;
MetalVia(RoutingContact v) {
via = v;
}
}
/**
* 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) {
vias.add(new MetalVia(pn));
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;
}
}
/**
* Class to define an R-Tree leaf node for geometry in the blockage data
* structure.
*/
private static class LMBound implements RTBounds {
private Rectangle2D bound;
private int netID;
LMBound(Rectangle2D bound, int netID) {
this.bound = bound;
this.netID = netID;
}
public Rectangle2D getBounds() {
return bound;
}
public int getNetID() {
return netID;
}
// public String toString() { return "LMBound on net " + netID; }
}
/**
* Class to define an R-Tree leaf node for vias in the blockage data
* structure.
*/
private static class LMVia implements RTBounds {
private Point2D loc;
private int netID;
LMVia(Point2D loc, int netID) {
this.loc = loc;
this.netID = netID;
}
public Rectangle2D getBounds() {
return new Rectangle2D.Double(loc.getX(), loc.getY(), 0, 0);
}
public String toString() {
return "LMVia on net " + netID;
}
}
}