/* -*- tab-width: 4 -*- * * Electric(tm) VLSI Design System * * File: DetailedRouterWorker.java * Written by: Alexander Herzog, Martin Fietz (Team 4) * * 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.experimentalLeeMoore2; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.TreeMap; import com.sun.electric.tool.routing.RoutingFrame.RoutingLayer; import com.sun.electric.tool.routing.experimentalLeeMoore2.DetailedRouter.DetailedRoutingSolution; import com.sun.electric.tool.routing.experimentalLeeMoore2.GlobalRouterV3.RegionToRoute; import com.sun.electric.tool.routing.experimentalLeeMoore2.RoutingFrameLeeMoore.Coordinate; import com.sun.electric.tool.routing.experimentalLeeMoore2.RoutingFrameLeeMoore.ManhattenAlignment; public final class DetailedRouterWorker implements Runnable { /** field */ private MazeField mf; private final double offsetX; private final double offsetY; private final int offsetZ; private final int lengthX; private final int lengthY; private final int lengthZ; private static boolean enableOutput = false; final static int HORIZONTAL = 1; final static int VERTICAL = 0; private RegionToRoute region; private final DetailedRoutingSolution allSolutions; private final DetailedRoutingSolution curSolutions; private final double tileSize; private double wireSpacing; private boolean isDone; private static ManhattenAlignment aStart; private static ManhattenAlignment aFinish; public DetailedRouterWorker(RegionToRoute region, RoutingLayer[] metalLayers, double tileSize) { this.tileSize = tileSize; wireSpacing = Double.MIN_VALUE; for (RoutingLayer lay : metalLayers) { if (null == lay) { continue; } wireSpacing = Math.max(lay.getMinSpacing(lay), wireSpacing); } offsetX = region.bounds.getMinX(); offsetY = region.bounds.getMinY(); offsetZ = 1; lengthX = (int) Math.round(region.bounds.getWidth() / tileSize) + 1; lengthY = (int) Math.round(region.bounds.getHeight() / tileSize) + 1; lengthZ = metalLayers.length - 1; allSolutions = new DetailedRoutingSolution(); curSolutions = new DetailedRoutingSolution(); } public void setRegion(RegionToRoute region) { this.region = region; } public void run() { isDone = false; curSolutions.clear(); List<SegPart> unrouted = region.segments_to_route; if (unrouted.size() > 0) { mf = new MazeField(lengthX, lengthY, lengthZ); mf.resetAll(); for (List<Coordinate> solution : allSolutions.values()) { //mf.addBlockage(toMazePoint(solution)); mf.addBlockage(getPointsToBlock(toMazePoint(solution))); } // reserve start- and end-point tiles for (SegPart segs : unrouted) { MazePoint start = toMazePoint(segs.segment_part.get(0)); MazePoint finish = toMazePoint(segs.segment_part.get(1)); if (mf.canBeReserved(start) && mf.canBeReserved(finish)) { mf.reserve(start); mf.reserve(finish); } } for (SegPart sp : unrouted) { mf.reset(); MazePoint start = toMazePoint(sp.segment_part.get(0)); aStart = sp.segment_part.get(0).alignment; MazePoint finish = toMazePoint(sp.segment_part.get(1)); aFinish = sp.segment_part.get(1).alignment; if (mf.getValue(start) == MazeField.POINT_RESERVED && mf.getValue(finish) == MazeField.POINT_RESERVED && mf.setStart(start) && mf.setFinish(finish)) { if (mf.wavefront()) { debug("1"); List<MazePoint> ws = mf.backtracking(); //mf.addBlockage(ws); mf.addBlockage(getPointsToBlock(ws)); curSolutions.put(sp, toCoordinate((ws))); } else { debug("0"); } } else { debug("?"); } } } mf = null; // let the garbage collector do its magic isDone = true; } private List<MazePoint> getPointsToBlock( List<MazePoint> lmp ) { List<MazePoint> result = new ArrayList<MazePoint>(); for( MazePoint mp : lmp ) { result.add(mp); } if( lmp.size() > 0 && mf.contains(lmp.get(0)) ) { result.add(lmp.get(0)); } if( lmp.size() > 0 && mf.contains(lmp.get(lmp.size() - 1)) ) { result.add(lmp.get(lmp.size() - 1)); } return result; } private static void debug(String s) { if (enableOutput) { System.out.print(s); } } public void enableOutput() { enableOutput = true; } public DetailedRoutingSolution getSolution() { return this.curSolutions; } public void removeSolutions(List<Integer> ids) { for (Iterator<Map.Entry<SegPart, List<Coordinate>>> it = curSolutions .entrySet().iterator(); it.hasNext();) { Map.Entry<SegPart, List<Coordinate>> s = it.next(); if (ids.contains(s.getKey().id)) { it.remove(); } else { allSolutions.put(s.getKey(), s.getValue()); } } } public boolean isDone() { return isDone; } private List<MazePoint> toMazePoint(final List<Coordinate> lc) { List<MazePoint> lm = new ArrayList<MazePoint>(lc.size()); for (Coordinate c : lc) lm.add(toMazePoint(c)); return lm; } private MazePoint toMazePoint(Coordinate c) { int x = (int) Math.floor((c.x - offsetX + 0.5 * tileSize) / tileSize); int y = (int) Math.floor((c.y - offsetY + 0.5 * tileSize) / tileSize); int z = c.layer - offsetZ; return new MazePoint(x, y, z); } private List<Coordinate> toCoordinate(final List<MazePoint> lm) { List<Coordinate> result = new ArrayList<Coordinate>(lm.size()); for (MazePoint mp : lm) { double x = mp.x * tileSize + offsetX; double y = mp.y * tileSize + offsetY; int z = mp.z + offsetZ; result.add(new Coordinate(x, y, z)); } return result; } public final static class MazePoint { public final int x, y, z; public MazePoint(int i, int j, int z) { this.x = i; this.y = j; this.z = z; } } public final static class MazeField { /** POINT VALUES */ private final static int POINT_UNDEFINED = 0; private final static int POINT_START = -1; private final static int POINT_FINISH = -2; private final static int POINT_BLOCK = -4; private final static int POINT_BORDER = -8; private final static int POINT_RESERVED = -16; private final int[][][] field; private MazePoint start; private MazePoint finish; public MazeField(int sizeX, int sizeY, int sizeZ) { field = new int[sizeX][sizeY][sizeZ]; } public MazeField reset() { for (int x = 0; x < field.length; x++) { for (int y = 0; y < field[x].length; y++) { for (int z = 0; z < field[x][y].length; z++) { if (getValue(x, y, z) != POINT_BLOCK && getValue(x, y, z) != POINT_BORDER && getValue(x, y, z) != POINT_RESERVED) { setValue(x, y, z, POINT_UNDEFINED); } } } } if (start != null && getValue(start) != POINT_BLOCK) { setValue(start, POINT_BORDER); } if (finish != null && getValue(finish) != POINT_BLOCK) { setValue(finish, POINT_BORDER); } return this; } public MazeField resetAll() { for (int x = 0; x < field.length; x++) { for (int y = 0; y < field[x].length; y++) { for (int z = 0; z < field[x][y].length; z++) { if (x == 0 || x == field.length - 1 || y == 0 || y == field[x].length - 1) { setValue(x, y, z, POINT_BORDER); } else { setValue(x, y, z, POINT_UNDEFINED); } } } } return this; } public boolean setStart(MazePoint mp) { if (contains(mp) && (getValue(mp) == POINT_BORDER || getValue(mp) == POINT_UNDEFINED || getValue(mp) == POINT_RESERVED)) { start = mp; setValue(mp, POINT_START); return true; } else { return false; } } public boolean reserve(MazePoint mp) { if (contains(mp) && (getValue(mp) == POINT_BORDER || getValue(mp) == POINT_UNDEFINED)) { setValue(mp, POINT_RESERVED ); return true; } else { return false; } } public boolean canBeReserved(MazePoint mp) { if (contains(mp)) { return (getValue(mp) == POINT_BORDER || getValue(mp) == POINT_UNDEFINED); } else { return false; } } public boolean setFinish(MazePoint mp) { if (contains(mp) && (getValue(mp) == POINT_BORDER || getValue(mp) == POINT_UNDEFINED || getValue(mp) == POINT_RESERVED)) { finish = mp; setValue(mp, POINT_FINISH); return true; } else { return false; } } public void addBlockage(List<MazePoint> blockList) { for (MazePoint block : blockList) addBlockage(block); } public void addBlockage(MazePoint mp) { addBlockage(mp.x, mp.y, mp.z); } public void addBlockage(int x, int y, int z) { setValue(x, y, z, POINT_BLOCK); } public void setValue(MazePoint mp, int i) { setValue(mp.x, mp.y, mp.z, i); } public void setValue(int x, int y, int z, int value) { if (contains(x, y, z)) { field[x][y][z] = value; } } public int getValue(final MazePoint mp) { return getValue(mp.x, mp.y, mp.z); } public int getDistance(MazePoint m, MazePoint n) { return Math.abs(m.x - n.x) + Math.abs(m.y - n.y) + Math.abs(m.z - n.z); } public int getValue(int x, int y, int z) { if (contains(x, y, z)) { return field[x][y][z]; } else { return POINT_BLOCK; } } public List<MazePoint> getNeighbours(MazePoint mp) { return getNeighbours(mp.x, mp.y, mp.z); } public List<MazePoint> getNeighbours(int x, int y, int z) { ArrayList<MazePoint> neighbours = new ArrayList<MazePoint>(); /*int[] dx, dy; if ((z + 1) % 2 == 0) // even layer { dx = new int[] { 0, 0, 0, 0 }; dy = new int[] { -1, 1, 0, 0 }; } else // odd layer { dx = new int[] { -1, 1, 0, 0 }; dy = new int[] { 0, 0, 0, 0 }; } int[] dz = { 0, 0, -1, 1 }; */ int dx[] = { -1, 1, 0, 0, 0, 0 }; int dy[] = { 0, 0, -1, 1, 0, 0 }; int dz[] = { 0, 0, 0, 0, -1, 1 }; for (int i = 0; i < dx.length; i++) { MazePoint n = new MazePoint(x + dx[i], y + dy[i], z + dz[i]); if (contains(n)) { neighbours.add(n); } } return neighbours; } private boolean contains(MazePoint mp) { return contains(mp.x, mp.y, mp.z); } private boolean contains(int x, int y, int z) { if (0 <= x && x < field.length && 0 <= y && y < field[x].length && 0 <= z && z < field[x][y].length) return true; else return false; } public boolean wavefront() { /** add first element to queue */ MazeQueue queue = new MazeQueue(); queue.add(getDistance(start, finish), start); /** * go on while there are elements in queue or return when finish * reached */ while (queue.size() > 0) { /** get next element to work with */ MazePoint curP = queue.removeNext(); for (MazePoint nextP : getNeighbours(curP)) { int nextValue = getValue(curP) + 1 /*+ Math.abs(nextP.x - field.length / 2) + Math.abs(nextP.y - field[0].length / 2)*/; if (getValue(curP) == POINT_START) { nextValue = 1; } switch (getValue(nextP)) { case POINT_START: case POINT_BLOCK: case POINT_BORDER: case POINT_RESERVED: break; case POINT_FINISH: return true; case POINT_UNDEFINED: setValue(nextP, nextValue); queue.add(getDistance(nextP, finish), nextP); break; default: // we have already visited this point if (nextValue < getValue(nextP)) { setValue(nextP, nextValue); queue.add(getDistance(nextP, finish), nextP); } break; } } } // debugPrintField(); // wavefront could not reach any finish point return false; } /** second part of algorithm */ public List<MazePoint> backtracking() { List<MazePoint> route = new ArrayList<MazePoint>(); /** add finish to route */ route.add(new MazePoint(finish.x, finish.y, finish.z)); /** while start not reached */ MazePoint curPoint = finish; int curValue = Integer.MAX_VALUE; while (curValue != POINT_START) { MazePoint nextPoint = curPoint; List<MazePoint> neighbours = getNeighbours(nextPoint); setValue( curPoint, POINT_BLOCK ); for (MazePoint neighbour : neighbours) { int value = getValue(neighbour); switch (value) { case POINT_UNDEFINED: case POINT_BLOCK: case POINT_BORDER: case POINT_FINISH: case POINT_RESERVED: continue; default: if (value < curValue) { nextPoint = neighbour; } break; } } if (nextPoint == curPoint) { debug("oops, this should never ever happen!\n"); } else { curPoint = nextPoint; curValue = getValue(nextPoint); route.add(nextPoint); } } Collections.reverse(route); return route; } /** for debugging */ public void debugPrintField() { debug("\nstart: "); printMazePoint(start); if (aStart == ManhattenAlignment.ma_horizontal) debug(" h"); if (aStart == ManhattenAlignment.ma_vertical) debug(" v"); if (aStart == ManhattenAlignment.ma_undefined) debug(" u"); debug("\n"); debug("finish: "); printMazePoint(finish); if (aFinish == ManhattenAlignment.ma_horizontal) debug(" h"); if (aFinish == ManhattenAlignment.ma_vertical) debug(" v"); if (aFinish == ManhattenAlignment.ma_undefined) debug(" u"); debug("\n"); for (int z = 0; z < field[0][0].length; z++) { debug("z: " + z + " " + ((z + 1) % 2 == 0 ? "vertical" : "horizontal") + "\n"); for (int y = 0; y < field[0].length; y++) { for (int x = 0; x < field.length; x++) { switch (getValue(x, y, z)) { case POINT_START: debug("=S="); break; case POINT_FINISH: debug("=F="); break; case POINT_BORDER: debug(" B "); break; case POINT_BLOCK: debug(" L "); break; case POINT_RESERVED: debug(" R "); break; default: int value = getValue(x, y, z); debug((value < 10 ? " " : "") + value + " "); } } debug("\n"); } } } public static void debugPrintMazePointList(List<MazePoint> points) { for (MazePoint mp : points) printMazePoint(mp); debug(".\n"); } public static void printMazePoint(MazePoint mp) { debug("(" + mp.x + "," + mp.y + "," + mp.z + ") "); } private static void debug(String s) { if (enableOutput) { System.out.print(s); } } final static class MazeQueue extends TreeMap<Integer, List<MazePoint>> { private static final long serialVersionUID = -9114852150292907187L; public void add(int distance, MazePoint mp) { if (!this.containsKey(distance)) { this.put(distance, new ArrayList<MazePoint>()); } this.get(distance).add(mp); } public MazePoint removeNext() { while (this.get(this.firstKey()).size() == 0) { this.remove(this.firstKey()); } List<MazePoint> next = this.get(this.firstKey()); MazePoint result = next.remove(0); if (next.size() == 0) { this.remove(this.firstKey()); } return result; } } } }