/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: AStarRoutingFrame.java
* Written by: Christian Julg, Jonas Thedering (Team 1)
*
* 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.experimentalAStar1;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.sun.electric.database.hierarchy.Cell;
/**
* Integrates the routing algorithm into the Electric framework
*
* @author Christian Jülg
* @author Jonas Thedering
*/
public class AStarRoutingFrame extends BenchmarkRouter {
private boolean DEBUG = false;
private boolean PERFORMANCE = true;
private boolean outputEnabled = false;
// cleanup time between worker interruption and return from runRouting, in ms
private static final long CLEANUP_TIME = 250;
private static AStarRoutingFrame instance;
// will be set in runRouting() according to Electric Preferences,
// or config file if available
private int threadCount = 1;
private int timeout = Integer.MAX_VALUE;
private ExecutorService service;
private Map map;
private List<ObjectPool<Node>> nodePools;
private List<ObjectPool<Storage>> storagePools;
private RoutingLayer[] metalLayers;
private RoutingContact[] metalPins;
//count and numMax could be different, if metalNumbers are not contiguous 1,2,...,n
private int metalLayerCount;
// use nummax instead count to be safe
private int metalLayerNumMax;
private double scalingFactor;
public AStarRoutingFrame(){
super();
instance = this;
}
public String getAlgorithmName() {
return "A* - 1";
}
/**
* Method to do Routing (overridden by actual Routing algorithms).
*
* @param segmentsToRoute
* a list of all routes that need to be made.
* @param allLayers
* a list of all layers that can be used in routing.
* @param allNodes
* a list of all nodes involved in the routing.
* @param allContacts
* a list of all contacts that can be used in routing.
*/
protected void runRouting(Cell cell, List<RoutingSegment> segmentsToRoute, List<RoutingLayer> allLayers,
List<RoutingContact> allContacts, List<RoutingGeometry> blockages) {
if (segmentsToRoute.isEmpty()){
return;
}
long startTime = 0;
if (PERFORMANCE) {
startTime = System.currentTimeMillis();
}
this.threadCount = numThreads.getIntValue();
this.timeout = maxRuntime.getIntValue();
this.outputEnabled = enableOutput.getBooleanValue();
PERFORMANCE &= this.isOutputEnabled();
DEBUG &= this.isOutputEnabled();
long shutdownTime = System.currentTimeMillis() + (timeout * 1000) - CLEANUP_TIME;
service = Executors.newFixedThreadPool(threadCount);
// 1 nodepool per thread
nodePools = new ArrayList<ObjectPool<Node>>(threadCount); // fixed size
storagePools = new ArrayList<ObjectPool<Storage>>(threadCount); // fixed size
for (int i = 0; i < threadCount; i++) {
nodePools.add(new ObjectPool<Node>(Node.class));
storagePools.add(new ObjectPool<Storage>(Storage.class));
}
if (DEBUG) {
CellPrinter.printContacts(allContacts);
CellPrinter.printLayers(allLayers);
CellPrinter.printChipStatistics(cell, segmentsToRoute, allLayers, allContacts, blockages);
}
processLayers(allLayers);
processSpacing(allContacts);
map = findBoundingBox(blockages, cell, segmentsToRoute);
// do this in worker threads
addBlockagesToMap(blockages);
AStarMaster master = new AStarMaster(service, map, nodePools, storagePools, metalLayers, metalPins, threadCount, shutdownTime);
master.runRouting(cell, segmentsToRoute, allLayers, allContacts, blockages);
if (PERFORMANCE) {
long totalTime = System.currentTimeMillis() - startTime;
System.out.println("AStarRouting Settings: threadCount:" + threadCount + ", timeout:" + timeout);
System.out.printf("AStarRouting total time: %d ms\n", totalTime);
}
//cleanup
map = null;
nodePools = null;
storagePools = null;
metalLayers = null;
metalPins = null;
}
/** Calculates the grid granularity based on minimum wire widths and spacing rules */
private void processSpacing(List<RoutingContact> allContacts) {
scalingFactor = 0d;
// check minWidths
for (RoutingLayer rl : metalLayers) {
double layerMin = rl.getMinWidth();
scalingFactor = Math.max(scalingFactor, layerMin);
if (DEBUG) {
double h = rl.getPin().getDefHeight();
double w = rl.getPin().getDefWidth();
double viaSpacing = rl.getPin().getViaSpacing();
System.out.printf("Layer %d: minWireWidth is %f, defHeight=%f, defWidth=%f, viaSpacing=%f\n", rl.getMetalNumber(), rl.getMinWidth(), h, w, viaSpacing);
}
}
// Use double the minimum width to ensure the needed spacing between wires
scalingFactor *= 2;
//check viaSpacings
for (RoutingContact rc : allContacts){
double contactSpacing = rc.getViaSpacing();
scalingFactor = Math.max(scalingFactor, contactSpacing);
if (DEBUG) {
double h = rc.getDefHeight();
double w = rc.getDefWidth();
double viaSpacing = rc.getViaSpacing();
System.out.printf("Contact %d-%d: defHeight=%f, defWidth=%f, viaSpacing=%f\n", rc.getFirstLayer().getMetalNumber(), rc.getSecondLayer().getMetalNumber(), h, w, viaSpacing);
for (RoutingGeometry geo : rc.getGeometry()){
//TODO: what does this geometry mean? occurs at every via!
h = geo.getBounds().getHeight();
w = geo.getBounds().getWidth();
double minX = geo.getBounds().getMinX();
double maxX = geo.getBounds().getMaxX();
double minY = geo.getBounds().getMinY();
double maxY = geo.getBounds().getMaxY();
int id = geo.getNetID();
System.out.printf("Contact %d-%d has RoutingGeometry on Layer %d, netID %d: defHeight=%f, defWidth=%f, minX=%f, maxX=%f, minY=%f, maxY=%f\n", rc.getFirstLayer().getMetalNumber(), rc.getSecondLayer().getMetalNumber(), geo.getLayer().getMetalNumber(), id, h, w, minX, maxX, minY, maxY);
}
}
}
//scaling factor should never be below 1, would be too memory expensive
scalingFactor = Math.max(scalingFactor, 1d);
}
/** Collects the layers and pins needed to create RoutePoints/RouteWires */
private void processLayers(List<RoutingLayer> allLayers) {
metalLayerCount = 0;
metalLayerNumMax = 0;
int invalidLayerMin = Integer.MAX_VALUE;
boolean invalidLayerFound = false;
for (int i = 0; i < allLayers.size(); i++) {
RoutingLayer rl = allLayers.get(i);
if (rl.isMetal()) {
if (rl.getMinWidth() < 0.001d) {
// this layer is not valid!
invalidLayerFound = true;
invalidLayerMin = Math.min(invalidLayerMin, rl.getMetalNumber());
continue;
}
if (invalidLayerFound && rl.getMetalNumber() > invalidLayerMin) {
// assumption: when there are layers "beyond" invalid layers, they are also invalid
continue;
}
metalLayerCount++;
metalLayerNumMax = Math.max(metalLayerNumMax, rl.getMetalNumber());
}
}
if (DEBUG)
System.out.println("metalLayers: " + metalLayerCount + ", maxMetalLayer: " + metalLayerNumMax + ", invalidLayerMin: "+invalidLayerMin);
metalLayers = new RoutingLayer[metalLayerNumMax];
metalPins = new RoutingContact[metalLayerNumMax];
for (int i = 0; i < allLayers.size(); i++) {
RoutingLayer rl = allLayers.get(i);
if (rl.isMetal()) {
int num = rl.getMetalNumber();
if (invalidLayerFound && num >= invalidLayerMin) {
//invalid Layer
continue;
}
metalLayers[num-1] = rl;
metalPins[num-1] = rl.getPin();
}
}
}
/**
* finds the bounding box and returns a Map with the parameters found
*
* @param blockages
* @param cell
* @param segmentsToRoute
* @return
*/
private Map findBoundingBox(List<RoutingGeometry> blockages, Cell cell, List<RoutingSegment> segmentsToRoute) {
int minX = Integer.MAX_VALUE, minY = Integer.MAX_VALUE;
int maxX = Integer.MIN_VALUE, maxY = Integer.MIN_VALUE;
// temp, will be rounded downwards
double dMinX = minX, dMinY = minY;
// temp, will be rounded upwards
double dMaxX = maxX, dMaxY = maxY;
Rectangle2D rec;
// find min/max x/y of blockages
for (RoutingGeometry geo : blockages) {
rec = geo.getBounds();
dMinX = Math.min(rec.getMinX(), dMinX);
dMinY = Math.min(rec.getMinX(), dMinY);
dMaxX = Math.max(rec.getMaxX(), dMaxX);
dMaxY = Math.max(rec.getMaxY(), dMaxY);
}
// find min/max x/y of cell elements
// necessary to avoid arrayindexoutofboundsexceptions on repeated
// routing calls on the same Cell
rec = cell.getBounds();
dMinX = Math.min(rec.getMinX(), dMinX);
dMinY = Math.min(rec.getMinY(), dMinY);
dMaxX = Math.max(rec.getMaxX(), dMaxX);
dMaxY = Math.max(rec.getMaxY(), dMaxY);
// find min/max x/y of segments start/finish
Point2D endPoint;
for (RoutingSegment rs : segmentsToRoute) {
endPoint = rs.getStartEnd().getLocation();
dMinX = Math.min(endPoint.getX(), dMinX);
dMinY = Math.min(endPoint.getY(), dMinY);
dMaxX = Math.max(endPoint.getX(), dMaxX);
dMaxY = Math.max(endPoint.getY(), dMaxY);
endPoint = rs.getFinishEnd().getLocation();
dMinX = Math.min(endPoint.getX(), dMinX);
dMinY = Math.min(endPoint.getY(), dMinY);
dMaxX = Math.max(endPoint.getX(), dMaxX);
dMaxY = Math.max(endPoint.getY(), dMaxY);
}
// Round to minWidth so that the grid in Electric matches ours
minX = (int) (Math.floor(dMinX / scalingFactor) * scalingFactor);
minY = (int) (Math.floor(dMinY / scalingFactor) * scalingFactor);
maxX = (int) (Math.ceil(dMaxX / scalingFactor) * scalingFactor);
maxY = (int) (Math.ceil(dMaxY / scalingFactor) * scalingFactor);
int dispX, dispY;
int width, height;
// this margin allows bypassing blockages on the bounding box of the Cell
// could be calculated depending on the number of segments to route instead of fixed
int numOfSafetyPoints = 5;
int padding = (int) (numOfSafetyPoints*scalingFactor);
// +1 to account for field 0, times two because we add on both sides
int margin = 2 * padding + 1;
width = maxX - minX + margin;
height = maxY - minY + margin;
dispX = -minX + padding;
dispY = -minY + padding;
if (DEBUG) {
System.out.printf("minx: %d, miny: %d, maxx: %d, maxy: %d\n", minX, minY, maxX, maxY);
System.out.printf("dispx: %d, dispy: %d, width: %d, height: %d\n", dispX, dispY, width, height);
}
return new Map(scalingFactor, width, height, metalLayerNumMax, dispX, dispY);
}
public static AStarRoutingFrame getInstance(){
return instance;
}
public boolean isOutputEnabled(){
return outputEnabled;
}
/**
* add blockages to Map
*
* NOTE: usually there are no blockages
*
* @param blockages
*/
private void addBlockagesToMap(List<RoutingGeometry> blockages) {
CountDownLatch latch = new CountDownLatch(blockages.size());
for (RoutingGeometry block : blockages) {
// each blockage is on exactly one layer
AStarBlockageWorker worker = new AStarBlockageWorker(map, block, latch);
service.execute(worker);
}
try {
latch.await();
} catch (InterruptedException e) {
System.out.println("addBlockages(): Interrupted: " + e);
}
}
}