/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: Maze.java
* Routing tool: Maze routing
* Original C Code written by Glen M. Lawson
* Translated to Java by Steven M. Rubin, Sun Microsystems.
*
* Copyright (c) 2004, 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;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.sun.electric.database.geometry.Poly;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.hierarchy.HierarchyEnumerator;
import com.sun.electric.database.hierarchy.Nodable;
import com.sun.electric.database.hierarchy.View;
import com.sun.electric.database.network.Netlist;
import com.sun.electric.database.network.Network;
import com.sun.electric.database.prototype.NodeProto;
import com.sun.electric.database.prototype.PortProto;
import com.sun.electric.database.topology.ArcInst;
import com.sun.electric.database.topology.Connection;
import com.sun.electric.database.topology.NodeInst;
import com.sun.electric.database.topology.PortInst;
import com.sun.electric.database.topology.RTBounds;
import com.sun.electric.database.variable.EditWindow_;
import com.sun.electric.database.variable.UserInterface;
import com.sun.electric.database.variable.VarContext;
import com.sun.electric.technology.ArcProto;
import com.sun.electric.technology.DRCTemplate;
import com.sun.electric.technology.Layer;
import com.sun.electric.technology.PrimitiveNode;
import com.sun.electric.technology.PrimitivePort;
import com.sun.electric.technology.Technology;
import com.sun.electric.technology.technologies.Generic;
import com.sun.electric.tool.Job;
import com.sun.electric.tool.JobException;
import com.sun.electric.tool.drc.DRC;
import com.sun.electric.tool.user.User;
import com.sun.electric.util.math.DBMath;
import com.sun.electric.util.math.GenMath;
/**
* Class to do maze routing (single wire at a time).
*/
public class Maze
{
/** bit width of long word */ private static final int SRMAXLAYERS = 64;
/** maximum size of maze */ private static final int MAXGRIDSIZE = 1000;
/** max grid points to "excavate" for initial grid access from a port */ private static final int BLOCKAGELIMIT = 10;
/** draw only on vertical layer */ private static final int HORILAYER = 0;
/** draw all layers */ private static final int VERTLAYER = 1;
/** draw all layers */ private static final int ALLLAYERS = 2;
// common bit masks
/** grid set (permanent) */ private static final int SR_GSET = 0x80;
/** is a wavefront point */ private static final int SR_GWAVE = 0x20;
/** is a port */ private static final int SR_GPORT = 0x10;
// for maze cells
/** mask 4 bits */ private static final int SR_GMASK = 0x0F;
/** maximum mark value */ private static final int SR_GMAX = 15;
/** start value */ private static final int SR_GSTART = 1;
// miscellaneous defines, return codes from expandWavefront
private static final int SRSUCCESS = 0;
private static final int SRERROR = 1;
private static final int SRROUTED = 2;
private static final int SRBLOCKED = 3;
private static final int SRUNROUTED = 4;
/** the arc used for vertical wires */ private ArcProto mazeVertWire;
/** the arc used for horizontal wires */ private ArcProto mazeHorizWire;
/** the pin used to join arcs */ private NodeProto mazeSteinerNode;
/** the layer used for vertical wires */ private Layer mazeVertLayer;
/** the layer used for horizontal wires */ private Layer mazeHorizLayer;
/** half the width of wires (for DRC bloat) */ private double mazeBloat;
/** the routing region with all data */ private SRREGION theRegion = null;
/** The netlist for the cell being routed */ private Netlist netList;
/** Space around net to build search grid */ private int mazeBoundary = 20;
static class SRDIRECTION {}
SRDIRECTION SRVERTPREF = new SRDIRECTION();
SRDIRECTION SRHORIPREF = new SRDIRECTION();
SRDIRECTION SRALL = new SRDIRECTION();
static class SRPTYPE {}
SRPTYPE SRPFIXED = new SRPTYPE();
SRPTYPE SRPROUTED = new SRPTYPE();
/**
* Defines a routing region to the router
*/
static class SRREGION
{
/** lower bound of the region */ int lx, ly;
/** upper bound of the region */ int hx, hy;
/** the array of layers */ SRLAYER [] layers;
/** the list of nets */ SRNET nets;
SRREGION()
{
layers = new SRLAYER[SRMAXLAYERS];
}
};
static class SRLAYER
{
byte [] hused, vused;
/** the layer index (sort order) */ int index;
/** the layer mask */ long mask;
/** translation value for grid */ int transx, transy;
/** the width and height of the grid */ int wid, hei;
/** bounds of the current maze */ int lx, ly, hx, hy;
/** the two dimensional grid array */ byte [][] grids;
/** up/down pointer to next layer */ SRLAYER up, down;
/** allowed direction of routes */ SRDIRECTION dir;
};
/**
* Defines a net in a region. Note that no existing segments of a net is predefined.
* Only ports are allowed. Not all nets in region need to be defined.
*/
static class SRNET
{
/** route state flag */ boolean routed;
/** the Network object */ Network eNet;
/** the parent region */ SRREGION region;
/** the list of ports on the net */ SRPORT ports;
/** the list of paths */ SRPATH paths;
/** the last path in the list */ SRPATH lastpath;
/** next in the list of nets */ SRNET next;
};
/**
* Defines a port on a net. Bounds of the port are in grid units.
* An index of 0 means all layers, 1 = 1st layer, 2 = 2nd layer, 4 - 3rd layer, etc.
*/
static class SRPORT
{
/** the port index */ int index;
/** the layer mask */ long layers;
/** true center of the port */ double cX, cY;
/** bounds of the port */ int lx, ly, hx, hy;
/** where this port comes from */ PortInst pi;
/** the master (connected) port */ SRPORT master;
/** the list of connected paths */ SRPATH paths;
/** the last path in the list */ SRPATH lastpath;
/** the current wave front */ SRWAVEPT wavefront;
/** the parent net */ SRNET net;
/** next in the list of ports */ SRPORT next;
};
static class SRPATH
{
/** end points of the path */ int [] x, y;
/** end pt in world units */ double [] wx, wy;
/** the layer of the path */ SRLAYER layer;
/** end style of the path */ boolean [] end;
/** the type of path */ SRPTYPE type;
/** the port path is attached to */ SRPORT port;
/** next in the list of paths */ SRPATH next;
SRPATH()
{
x = new int[2];
y = new int[2];
wx = new double[2];
wy = new double[2];
end = new boolean[2];
}
};
/**
* routing data types
*/
static class SRWAVEPT
{
/** x y location of the point */ int x, y;
/** the layer for the point */ SRLAYER layer;
/** the port for this point */ SRPORT port;
/** next/prev in the list of points */ SRWAVEPT next, prev;
};
/************************************* TOP-LEVEL CONTROL CODE *************************************/
public static void mazeRoute()
{
UserInterface ui = Job.getUserInterface();
Cell cell = ui.needCurrentCell();
if (cell == null) return;
EditWindow_ wnd = ui.getCurrentEditWindow_();
if (wnd == null) return;
Netlist netList = cell.getNetlist();
if (netList == null)
{
System.out.println("Sorry, a deadlock aborted routing (network information unavailable). Please try again");
return;
}
Set<Network> nets = wnd.getHighlightedNetworks();
// turn the set of Nets back into a list of Arcs to route
List<ArcInst> arcsToRoute = new ArrayList<ArcInst>();
for(Iterator<ArcInst> it = cell.getArcs(); it.hasNext(); )
{
ArcInst ai = it.next();
if (ai.getProto() != Generic.tech().unrouted_arc) continue;
Network net = netList.getNetwork(ai, 0);
if (nets.contains(net))
{
arcsToRoute.add(ai);
nets.remove(net);
}
}
// turn off highlighting
wnd.clearHighlighting();
wnd.finishedHighlighting();
new MazeRouteJob(cell, arcsToRoute);
}
private static class MazeRouteJob extends Job
{
private Cell cell;
private List<ArcInst> arcs;
protected MazeRouteJob(Cell cell, List<ArcInst> arcs)
{
super("Maze Route", Routing.getRoutingTool(), Job.Type.CHANGE, null, null, Job.Priority.USER);
this.cell = cell;
this.arcs = arcs;
startJob();
}
public boolean doIt() throws JobException
{
Maze router = new Maze();
router.routeSelected(cell, arcs);
return true;
}
}
/**
* This is the public interface for Maze Routing when done in batch mode.
* It replaces the selected unrouted arcs with routed geometry
* @param cell the cell to be Maze-routed.
*/
public void routeSelected(Cell cell, List<ArcInst> arcsToRoute)
{
if (arcsToRoute.size() == 0)
{
netList = cell.getNetlist();
Set<Network> nets = new HashSet<Network>();
for (Iterator<ArcInst> it = cell.getArcs(); it.hasNext();)
{
ArcInst ai = it.next();
if (ai.getProto() != Generic.tech().unrouted_arc) continue;
Network net = netList.getNetwork(ai, 0);
if (nets.contains(net)) continue;
arcsToRoute.add(ai);
nets.add(net);
}
}
// now route each arc
for(ArcInst ai : arcsToRoute)
{
// reacquire the netlist for the current configuration
netList = cell.getNetlist();
// route the unrouted arc
Network net = netList.getNetwork(ai, 0);
Map<Network,ArcInst[]> arcMap = null;
if (cell.getView() != View.SCHEMATIC) arcMap = netList.getArcInstsByNetwork();
if (routeNet(net, arcMap)) continue;
}
}
/**
* Method to reroute a network.
* @param net the network to route.
* @return true on error.
*/
private boolean routeNet(Network net, Map<Network,ArcInst[]> arcMap)
{
// get extent of net and mark nodes and arcs on it
HashSet<ArcInst> arcsToDelete = new HashSet<ArcInst>();
HashSet<NodeInst> nodesToDelete = new HashSet<NodeInst>();
List<Connection> netEnds = Routing.findNetEnds(net, arcMap, arcsToDelete, nodesToDelete, netList, true);
int count = netEnds.size();
if (count == 0) return false;
if (count != 2)
{
System.out.println("Error: Network " + net.describe(false) + " has " + count +
" ends, but can only route nets with 2 ends");
return true;
}
// determine bounds of this networks
Cell cell = net.getParent();
Rectangle2D routingBounds = null;
for(Iterator<ArcInst> it = cell.getArcs(); it.hasNext(); )
{
ArcInst ai = it.next();
Network aNet = netList.getNetwork(ai, 0);
if (aNet != net) continue;
Rectangle2D arcBounds = ai.getBounds();
if (routingBounds == null) routingBounds = arcBounds; else
{
Rectangle2D.union(routingBounds, arcBounds, routingBounds);
}
}
if (routingBounds == null)
{
System.out.println("Internal error: no bounding area for routing");
return true;
}
// determine arc to route
ArcProto routingArc = User.getUserTool().getCurrentArcProto();
if (routingArc == Generic.tech().unrouted_arc) routingArc = null;
if (routingArc != null)
{
// see if the default arc can be used to route
for(int i=0; i<count; i++)
{
Connection con = netEnds.get(i);
boolean found = con.getPortInst().getPortProto().getBasePort().connectsTo(routingArc);
if (!found) { routingArc = null; break; }
}
}
// if current arc cannot run, look for any that can
if (routingArc == null)
{
// check out all arcs for use in this route
HashSet<ArcProto> arcsUsed = new HashSet<ArcProto>();
for(int i=0; i<count; i++)
{
Connection con = netEnds.get(i);
ArcProto [] connections = con.getPortInst().getPortProto().getBasePort().getConnections();
for(int j = 0; j < connections.length; j++)
{
ArcProto ap = connections[j];
if (ap.getTechnology() == Generic.tech()) continue;
arcsUsed.add(ap);
}
}
for(Iterator<Technology> it = Technology.getTechnologies(); it.hasNext(); )
{
Technology tech = it.next();
if (tech == Generic.tech()) continue;
for(Iterator<ArcProto> aIt = tech.getArcs(); aIt.hasNext(); )
{
ArcProto ap = aIt.next();
if (!arcsUsed.contains(ap)) continue;
boolean allFound = true;
for(int i=0; i<count; i++)
{
Connection con = netEnds.get(i);
boolean found = con.getPortInst().getPortProto().getBasePort().connectsTo(ap);
if (!found) { allFound = false; break; }
}
if (allFound)
{
routingArc = ap;
break;
}
}
if (routingArc != null) break;
}
}
if (routingArc == null)
{
System.out.println("Cannot find wire to route");
return true;
}
mazeVertWire = routingArc;
mazeHorizWire = routingArc;
mazeSteinerNode = routingArc.findPinProto();
mazeVertLayer = mazeVertWire.getLayer(0);
mazeHorizLayer = mazeHorizWire.getLayer(0);
// Iterator<ArcLayer> it = mazeVertWire.getArcLayers();
// mazeVertLayer = it.next().getLayer();
// it = mazeHorizWire.getArcLayers();
// mazeHorizLayer = it.next().getLayer();
mazeBloat = 0;
double wid = 10, len = 100;
DRCTemplate rule = DRC.getSpacingRule(mazeVertLayer, null, mazeVertLayer, null, false, 0, wid, len);
if (rule != null) mazeBloat = rule.getValue(0) + mazeVertWire.getDefaultLambdaBaseWidth()/2;
// now create the routing region
int lx = (int)routingBounds.getMinX();
int hx = (int)routingBounds.getMaxX();
int ly = (int)routingBounds.getMinY();
int hy = (int)routingBounds.getMaxY();
SRREGION region = defineRegion(cell, net, lx, ly, hx, hy, arcsToDelete, nodesToDelete);
if (region == null) return true;
// create the net in the region
SRNET srnet = addNet(region, net);
if (srnet == null)
{
System.out.println("Could not allocate internal net");
return true;
}
// add the ports to the net
for(int i=0; i<count; i++)
{
Connection con = netEnds.get(i);
PortInst pi = con.getPortInst();
double cXD = con.getLocation().getX();
double cYD = con.getLocation().getY();
SRPORT fsp = addPort(srnet, determineDir(pi.getNodeInst(), cXD, cYD), cXD, cYD, pi);
if (fsp == null)
{
System.out.println("Port could not be defined");
return true;
}
}
//dumpLayer("BEFORE ROUTING", region, 0xFF);
// do maze routing
if (routeANet(srnet))
{
System.out.println("Could not route net " + srnet.eNet.describe(false));
return true;
}
// extract paths to create arcs
if (extractPaths(cell, srnet))
{
System.out.println("Could not create paths");
return true;
}
// remove marked networks
for(ArcInst ai : arcsToDelete)
{
ai.kill();
}
cell.killNodes(nodesToDelete);
return false;
}
/************************************* CODE TO TRAVERSE THE MAZE BUFFER *************************************/
private boolean routeANet(SRNET net)
{
// initialize all layers and ports for this route
boolean ret = false;
for (int index = 0; index < SRMAXLAYERS; index++)
{
SRLAYER layer = net.region.layers[index];
if (layer != null)
{
// unused bounds
layer.lx = layer.wid; layer.hx = -1;
layer.ly = layer.hei; layer.hy = -1;
// now set the vertical and horizontal preference
int count = 0;
for (int x = 0; x < layer.wid; x++)
{
if ((layer.vused[x] & SR_GSET) != 0)
{
if (count != 0)
{
for (int i = 1, prio = count>>1; i <= count; i++, prio--)
layer.vused[x-i] = (byte)Math.abs(prio);
count = 0;
}
} else count++;
}
// get the remainder
if (count != 0)
{
for (int i = 1, prio = count>>1; i < count; i++)
{
layer.vused[layer.wid-i] = (byte)Math.abs(prio);
if (--prio == 0 && (count&1) == 0) prio = -1;
}
}
// now horizontal tracks
count = 0;
for (int y = 0; y < layer.hei; y++)
{
if ((layer.hused[y] & SR_GSET) != 0)
{
if (count != 0)
{
for (int i = 1, prio = count>>1; i <= count; i++, prio--)
layer.hused[y-i] = (byte)Math.abs(prio);
count = 0;
}
} else count++;
}
// and the remainder ...
if (count != 0)
{
for (int i = 1, prio = count>>1; i < count; i++)
{
layer.hused[layer.hei-i] = (byte)Math.abs(prio);
if (--prio == 0 && (count&1) == 0) prio = -1;
}
}
}
}
// prepare each port for routing
int pCount = 0;
for (SRPORT port = net.ports; port != null; port = port.next, pCount++)
{
createWavefront(port);
}
// now begin routing until all ports merged
int code = SR_GSTART;
do
{
if (++code > SR_GMAX) code = SR_GSTART;
int blocked = 0;
int status = 0;
SRPORT port = null;
for (port = net.ports; port != null; port = port.next)
{
// if part of other wavefront, get the next one
if (port.master != null) continue;
// expand the wavefront
status = expandWavefront(port, code);
if (status == SRERROR) return true;
if (status == SRROUTED) break;
if (status == SRBLOCKED) blocked++;
}
// check for successful routing
if (port != null && status == SRROUTED)
{
// now clear routing region and restart expansion
clearMaze(net);
if (--pCount > 1)
{
// prepare each port for routing
for (port = net.ports; port != null; port = port.next)
createWavefront(port);
}
code = SR_GSTART;
} else
{
// check for blocked net
if (blocked == pCount)
{
ret = true;
clearMaze(net);
break;
}
}
} while (pCount > 1);
// move all the port paths to the net
for (SRPORT port = net.ports; port != null; port = port.next)
{
if (net.lastpath == null)
{
net.paths = port.paths;
if (net.paths != null) net.lastpath = port.lastpath;
} else
{
net.lastpath.next = port.paths;
if (net.lastpath.next != null) net.lastpath = port.lastpath;
}
port.paths = null;
port.lastpath = null;
}
if (!ret) net.routed = true;
return ret;
}
private void createWavefront(SRPORT port)
{
SRPORT master = port.master;
if (master == null) master = port;
// first assign each layer of the port as wavefront points
for (int index = 0, mask = 1; index < SRMAXLAYERS; index++, mask = mask<<1)
{
if ((mask & port.layers) != 0)
{
SRLAYER layer = port.net.region.layers[index];
if (layer == null) continue;
// convert to grid points
int lx = getGridX(port.lx, layer); int ly = getGridY(port.ly, layer);
int hx = getGridX(port.hx, layer); int hy = getGridY(port.hy, layer);
if (lx >= layer.wid || hx < 0 || ly >= layer.hei || hy < 0) continue;
// clip to window
if (lx < 0) lx = 0;
if (hx >= layer.wid) hx = layer.wid - 1;
if (ly < 0) ly = 0;
if (hy >= layer.hei) hy = layer.hei - 1;
/*
* added detection of immediate blockage ... smr
*/
boolean onEdge = false;
for (int x = lx; x <= hx; x++)
{
for (int y = ly; y <= hy; y++)
{
addWavePoint(master, layer, x, y, SR_GSTART);
if (x < layer.wid-1 && layer.grids[x+1][y] == 0) onEdge = true;
if (x > 0 && layer.grids[x-1][y] == 0) onEdge = true;
if (y < layer.hei-1 && layer.grids[x][y+1] == 0) onEdge = true;
if (y > 0 && layer.grids[x][y-1] == 0) onEdge = true;
}
}
if (!onEdge)
{
// port is inside of blocked area: search for opening
int cx = (lx + hx) / 2;
int cy = (ly + hy) / 2;
PrimitivePort prP = port.pi.getPortProto().getBasePort();
int angRange = prP.getAngleRange();
int ang = prP.getAngle();
NodeInst ni = port.pi.getNodeInst();
ang += (ni.getAngle()+5) / 10;
if (ni.isMirroredAboutXAxis() != ni.isMirroredAboutYAxis()) { ang = 270 - ang; if (ang < 0) ang += 360; }
if (angleDiff(ang, 0) <= angRange)
{
// port faces right
for(int spread=1; spread<BLOCKAGELIMIT; spread++)
{
if (hx+spread >= layer.wid) break;
if (layer.grids[hx+spread][cy] == 0) { onEdge = true; break; }
layer.grids[hx+spread][cy] = 0;
}
}
if (angleDiff(ang, 90) <= angRange)
{
// port faces up
for(int spread=1; spread<BLOCKAGELIMIT; spread++)
{
if (hy+spread >= layer.hei) break;
if (layer.grids[cx][hy+spread] == 0) { onEdge = true; break; }
layer.grids[cx][hy+spread] = 0;
}
}
if (angleDiff(ang, 180) <= angRange)
{
// port faces left
for(int spread=1; spread<BLOCKAGELIMIT; spread++)
{
if (lx-spread < 0) break;
if (layer.grids[lx-spread][cy] == 0) { onEdge = true; break; }
layer.grids[lx-spread][cy] = 0;
}
}
if (angleDiff(ang, 270) <= angRange)
{
// port faces down
for(int spread=1; spread<BLOCKAGELIMIT; spread++)
{
if (ly-spread < 0) break;
if (layer.grids[cx][ly-spread] == 0) { onEdge = true; break; }
layer.grids[cx][ly-spread] = 0;
}
}
if (!onEdge)
{
System.out.println("Node " + ni.describe(false) + ", port " +
port.pi.getPortProto().getName() + " is blocked");
return;
}
}
}
}
// now assign the paths of the port
for (SRPATH path = port.paths; path != null; path = path.next)
{
// note paths are always in the working area
if (path.x[0] == path.x[1])
{
// vertical path
int dy = -1;
if (path.y[0] < path.y[1]) dy = 1;
for (int x = path.x[0], y = path.y[0];
(dy < 0) ? y >= path.y[1] : y <= path.y[1]; y += dy)
{
addWavePoint(master, path.layer, x, y, SR_GSTART);
}
} else if (path.y[0] == path.y[1])
{
// horizontal path
int dx = -1;
if (path.x[0] < path.x[1]) dx = 1;
for (int y = path.y[0], x = path.x[0];
(dx < 0) ? x >= path.x[1] : x <= path.x[1]; x += dx)
{
addWavePoint(master, path.layer, x, y, SR_GSTART);
}
} else
{
// a 45 degree path, note assume x,y difference is equal
int dx = -1;
if (path.x[0] < path.x[1]) dx = 1;
int dy = -1;
if (path.y[0] < path.y[1]) dy = 1;
for (int x = path.x[0], y = path.y[0];
(dx < 0) ? x >= path.x[1] : x <= path.x[1]; x += dx, y += dy)
{
addWavePoint(master, path.layer, x, y, SR_GSTART);
}
}
}
}
/**
* routing commands
*/
private void addWavePoint(SRPORT port, SRLAYER layer, int x, int y, int code)
{
SRWAVEPT wavePt = new SRWAVEPT();
wavePt.x = x;
wavePt.y = y;
// set the grid
layer.grids[x][y] = (byte)((layer.grids[x][y] & ~SR_GMASK) | code | SR_GWAVE);
wavePt.layer = layer;
// set maze bounds
if (layer.lx > x) layer.lx = x;
if (layer.hx < x) layer.hx = x;
if (layer.ly > y) layer.ly = y;
if (layer.hy < y) layer.hy = y;
wavePt.prev = null;
if (port.master != null)
{
wavePt.port = port.master;
wavePt.next = port.master.wavefront;
port.master.wavefront = wavePt;
} else
{
wavePt.port = port;
wavePt.next = port.wavefront;
port.wavefront = wavePt;
}
if (wavePt.next != null)
wavePt.next.prev = wavePt;
}
private int angleDiff(int ang1, int ang2)
{
int diff = Math.abs(ang1 - ang2);
if (diff > 180) diff = 360 - diff;
return diff;
}
private int expandWavefront(SRPORT port, int code)
{
// begin expansion of all wavepts
// disconnect wavepts from the port
SRWAVEPT wavePt = port.wavefront;
if (wavePt == null) return SRBLOCKED;
SRWAVEPT next = null;
int status = SRSUCCESS;
boolean found = false;
int bx = 0, by = 0;
SRLAYER bLayer = null;
SRWAVEPT bWavePt = new SRWAVEPT();
for (wavePt = port.wavefront; wavePt != null ; wavePt = next)
{
boolean connected = false;
SRLAYER layer = wavePt.layer;
if (layer.dir == SRALL || layer.dir == SRHORIPREF)
{
// try horizontal route
int x = wavePt.x + 1;
if (x != layer.wid)
{
status = examinePoint(port, layer, x, wavePt.y, code);
if (status == SRROUTED)
{
// "bWavePt" used in proper order
if (!found || (layer.hused[bWavePt.y] & SR_GSET) != 0 ||
((layer.hused[wavePt.y] & SR_GSET) == 0 &&
layer.hused[wavePt.y] < layer.hused[bWavePt.y]))
{
bWavePt.x = wavePt.x; bWavePt.y = wavePt.y;
bWavePt.layer = wavePt.layer; bWavePt.port = wavePt.port;
bx = x; by = wavePt.y;
bLayer = layer;
}
found = true;
connected = true;
}
}
if (!connected && (x = wavePt.x - 1) >= 0)
{
status = examinePoint(port, layer, x, wavePt.y, code);
if (status == SRROUTED)
{
if (!found ||
(layer.hused[bWavePt.y] & SR_GSET) != 0 ||
((layer.hused[wavePt.y] & SR_GSET) == 0 &&
layer.hused[wavePt.y] < layer.hused[bWavePt.y]))
{
bWavePt.x = wavePt.x; bWavePt.y = wavePt.y;
bWavePt.layer = wavePt.layer; bWavePt.port = wavePt.port;
bx = x; by = wavePt.y;
bLayer = layer;
}
found = true;
connected = true;
}
}
}
if (layer.dir == SRALL || layer.dir == SRVERTPREF)
{
// try vertical route
int y = wavePt.y + 1;
if (!connected && y != layer.hei)
{
status = examinePoint(port, layer, wavePt.x, y, code);
if (status == SRROUTED)
{
if (!found ||
(layer.vused[bWavePt.x] & SR_GSET) != 0 ||
((layer.vused[wavePt.x] & SR_GSET) == 0 &&
layer.vused[wavePt.x] < layer.vused[bWavePt.x]))
{
bWavePt.x = wavePt.x; bWavePt.y = wavePt.y;
bWavePt.layer = wavePt.layer; bWavePt.port = wavePt.port;
bx = wavePt.x; by = y;
bLayer = layer;
}
found = true;
connected = true;
}
}
if (!connected && (y = wavePt.y - 1) >= 0)
{
status = examinePoint(port, layer, wavePt.x, y, code);
if (status == SRROUTED)
{
if (!found ||
(layer.vused[bWavePt.x] & SR_GSET) != 0 ||
((layer.vused[wavePt.x] & SR_GSET) == 0 &&
layer.vused[wavePt.x] < layer.vused[bWavePt.x]))
{
bWavePt.x = wavePt.x; bWavePt.y = wavePt.y;
bWavePt.layer = wavePt.layer; bWavePt.port = wavePt.port;
bx = wavePt.x; by = y;
bLayer = layer;
}
found = true;
connected = true;
}
}
}
if (!connected && layer.up != null)
{
// try via up
status = examinePoint(port, layer.up, wavePt.x, wavePt.y, code);
if (status == SRROUTED)
{
if (!found)
{
bWavePt.x = wavePt.x; bWavePt.y = wavePt.y;
bWavePt.layer = wavePt.layer; bWavePt.port = wavePt.port;
bx = wavePt.x; by = wavePt.y;
bLayer = layer.up;
}
found = true;
connected = true;
}
}
if (!connected && layer.down != null)
{
// try via down
status = examinePoint(port, layer.down, wavePt.x, wavePt.y, code);
if (status == SRROUTED)
{
if (!found)
{
bWavePt.x = wavePt.x; bWavePt.y = wavePt.y;
bWavePt.layer = wavePt.layer; bWavePt.port = wavePt.port;
bx = wavePt.x; by = wavePt.y;
bLayer = layer.down;
}
found = true;
connected = true;
}
}
next = wavePt.next;
// now release this wavept
if (wavePt.prev == null)
{
port.wavefront = wavePt.next;
if (wavePt.next != null)
wavePt.next.prev = null;
} else
{
wavePt.prev.next = wavePt.next;
if (wavePt.next != null)
wavePt.next.prev = wavePt.prev;
}
// set the grid point to a core point
if (!connected) layer.grids[wavePt.x][wavePt.y] &= ~SR_GWAVE;
}
if (found)
return initPath(port, bLayer, bWavePt, bx, by);
if (port.wavefront == null) return SRBLOCKED;
return SRSUCCESS;
}
private int initPath(SRPORT port, SRLAYER layer, SRWAVEPT wavePt, int x, int y)
{
// search for others
SRPORT target = null;
SRWAVEPT tWavePt = null;
for (target = port.net.ports; target != null; target = target.next)
{
if (target == port || target.master != null) continue;
tWavePt = searchWavefront(target, layer, x, y);
if (tWavePt != null) break;
}
if (target == null) return SRERROR;
/* now move the target's paths to the master. This is done to retain the
* original path creation order (port out), and also insures the existance
* of the arc in t-junction connections
*/
if (port.lastpath != null)
{
if ((port.lastpath.next = target.paths) != null)
port.lastpath = target.lastpath;
} else
{
// this should never happen
port.paths = target.paths;
port.lastpath = target.lastpath;
}
target.paths = null;
target.lastpath = null;
// connect the port with target
SRPATH path = null;
if (wavePt.layer == tWavePt.layer)
{
path = getPath(port, layer, false, false, wavePt.x, wavePt.y, tWavePt.x, tWavePt.y);
}
// now create paths to each target point
if (findPaths(wavePt, path) != SRSUCCESS) return SRERROR;
if (findPaths(tWavePt, path) != SRSUCCESS) return SRERROR;
// now set the target master
target.master = port;
// now scan through all ports and change target as master to port
for (SRPORT sport = port.net.ports; sport != null; sport = sport.next)
{
if (sport.master == target) sport.master = port;
}
// now move the rest of the paths to the master port
if (port.lastpath != null)
{
if ((port.lastpath.next = target.paths) != null)
port.lastpath = target.lastpath;
} else
{
// this should never happen
port.paths = target.paths;
port.lastpath = target.lastpath;
}
target.paths = null;
target.lastpath = null;
return SRROUTED;
}
/**
* Method to find the path through the maze to the target point.
* Will merge the starting point path with the first internal path if possible.
*/
private int findPaths(SRWAVEPT wavePt, SRPATH path)
{
// Start scan from the first point
int sx = wavePt.x;
int sy = wavePt.y;
SRLAYER layer = wavePt.layer;
int code = layer.grids[sx][sy] & SR_GMASK;
if (code == SR_GSTART) code = SR_GMAX;
else code--;
int pStart = 0;
int ex = 0, ey = 0;
for(;;)
{
int dx = 0, dy = 0;
// scan around the point
for(;;)
{
if (pStart == 1)
{
// always try to jump layer after the first path
// now try jumping layers
if (layer.up != null)
{
ex = sx; ey = sy;
int status = testPoint(layer.up.grids[ex][ey], code);
if (status == SRROUTED) return SRSUCCESS;
if (status == SRSUCCESS)
{
layer = layer.up;
if (code == SR_GSTART) code = SR_GMAX;
else code--;
pStart = 2;
continue;
}
}
if (layer.down != null)
{
ex = sx; ey = sy;
int status = testPoint(layer.down.grids[ex][ey], code);
if (status == SRROUTED) return SRSUCCESS;
if (status == SRSUCCESS)
{
layer = layer.down;
if (code == SR_GSTART) code = SR_GMAX;
else code--;
pStart = 2;
continue;
}
}
}
if (layer.dir == SRALL || layer.dir == SRHORIPREF)
{
// try right first
if ((ex = sx + 1) != layer.wid)
{
ey = sy;
int status = testPoint(layer.grids[ex][ey], code);
if (status == SRROUTED)
{
// check for common original path
if (path != null && path.layer == layer &&
path.y[0] == path.y[1] && path.y[0] == ey &&
Math.max(path.x[0], path.x[1]) == sx)
{
if (path.x[0] == sx)
{
path.x[0] = ex;
path.wx[0] = getWorldX(ex, layer);
} else
{
path.x[1] = ex;
path.wx[1] = getWorldX(ex, layer);
}
setLine(path.layer, SR_GPORT | SR_GSET,
path.wx[0], path.wy[0], path.wx[1], path.wy[1], true);
return SRSUCCESS;
}
getPath(wavePt.port, layer, false, true, sx, sy, ex, ey);
return SRSUCCESS;
} else if (status == SRSUCCESS)
{
dx = 1;
break;
}
}
}
if (layer.dir == SRALL || layer.dir == SRVERTPREF)
{
// try down first
if ((ey = sy - 1) >= 0)
{
ex = sx;
int status = testPoint(layer.grids[ex][ey], code);
if (status == SRROUTED)
{
// check for common original path
if (path != null && path.layer == layer &&
path.x[0] == path.x[1] && path.x[0] == ex &&
Math.min(path.y[0], path.y[1]) == sy)
{
if (path.y[0] == sy)
{
path.y[0] = ey;
path.wy[0] = getWorldY(ey, layer);
} else
{
path.y[1] = ey;
path.wy[1] = getWorldY(ey, layer);
}
setLine(path.layer, SR_GPORT | SR_GSET,
path.wx[0], path.wy[0], path.wx[1], path.wy[1], true);
return SRSUCCESS;
}
getPath(wavePt.port, layer, false, true, sx, sy, ex, ey);
return SRSUCCESS;
} else if (status == SRSUCCESS)
{
dy = -1;
break;
}
}
}
if (layer.dir == SRALL || layer.dir == SRHORIPREF)
{
// try left
if ((ex = sx - 1) >= 0)
{
ey = sy;
int status = testPoint(layer.grids[ex][ey], code);
if (status == SRROUTED)
{
// check for common original path
if (path != null && path.layer == layer &&
path.y[0] == path.y[1] && path.y[0] == ey &&
Math.min(path.x[0], path.x[1]) == sx)
{
if (path.x[0] == sx)
{
path.x[0] = ex;
path.wx[0] = getWorldX(ex, layer);
} else
{
path.x[1] = ex;
path.wx[1] = getWorldX(ex, layer);
}
setLine(path.layer, SR_GPORT | SR_GSET,
path.wx[0], path.wy[0], path.wx[1], path.wy[1], true);
return SRSUCCESS;
}
getPath(wavePt.port, layer, false, true, sx, sy, ex, ey);
return SRSUCCESS;
} else if (status == SRSUCCESS)
{
dx = -1;
break;
}
}
}
if (layer.dir == SRALL || layer.dir == SRVERTPREF)
{
// try up
if ((ey = sy + 1) != layer.hei)
{
ex = sx;
int status = testPoint(layer.grids[ex][ey], code);
if (status == SRROUTED)
{
// check for common original path
if (path != null && path.layer == layer &&
path.x[0] == path.x[1] && path.x[0] == ex &&
Math.max(path.y[0], path.y[1]) == sy)
{
if (path.y[0] == sy)
{
path.y[0] = ey;
path.wy[0] = getWorldY(ey, layer);
} else
{
path.y[1] = ey;
path.wy[1] = getWorldY(ey, layer);
}
setLine(path.layer, SR_GPORT | SR_GSET,
path.wx[0], path.wy[0], path.wx[1], path.wy[1], true);
return SRSUCCESS;
}
getPath(wavePt.port, layer, false, true, sx, sy, ex, ey);
return SRSUCCESS;
} else if (status == SRSUCCESS)
{
dy = 1;
break;
}
}
}
// now try jumping layers
if (pStart == 0)
{
if (layer.up != null)
{
ex = sx; ey = sy;
int status = testPoint(layer.up.grids[ex][ey], code);
if (status == SRROUTED) return SRSUCCESS;
if (status == SRSUCCESS)
{
layer = layer.up;
if (code == SR_GSTART) code = SR_GMAX;
else code--;
continue;
}
}
if (layer.down != null)
{
ex = sx; ey = sy;
int status = testPoint(layer.down.grids[ex][ey], code);
if (status == SRROUTED) return SRSUCCESS;
if (status == SRSUCCESS)
{
layer = layer.down;
if (code == SR_GSTART) code = SR_GMAX;
else code--;
continue;
}
}
}
// could not start route, just return
return SRERROR;
}
// set path started
pStart = 1;
// now continue scan until the end of the path
for(;;)
{
if (code == SR_GSTART) code = SR_GMAX;
else code--;
if (dx != 0)
{
// horizontal scan
int nx = ex + dx;
int ny = ey;
int status = testPoint(layer.grids[nx][ny], code);
if (status == SRROUTED)
{
// check for common original path
if (path != null && path.layer == layer &&
path.y[0] == path.y[1] && path.y[0] == sy &&
((dx < 0 && Math.min(path.x[0], path.x[1]) == sx) ||
(dx > 0 && Math.max(path.x[0], path.x[1]) == sx)))
{
if (path.x[0] == sx)
{
path.x[0] = nx;
path.wx[0] = getWorldX(nx, layer);
} else
{
path.x[1] = nx;
path.wx[1] = getWorldX(nx, layer);
}
setLine(path.layer, SR_GPORT | SR_GSET,
path.wx[0], path.wy[0], path.wx[1], path.wy[1], true);
return SRSUCCESS;
}
getPath(wavePt.port, layer, false, true, sx, sy, nx, ny);
return SRSUCCESS;
} else if (status == SRSUCCESS)
{
ex = nx;
continue;
}
}
if (dy != 0)
{
// veritical scan
int nx = ex;
int ny = ey + dy;
int status = testPoint(layer.grids[nx][ny], code);
if (status == SRROUTED)
{
// check for common original path
if (path != null && path.layer == layer &&
path.x[0] == path.x[1] && path.x[0] == sx &&
((dy < 0 && Math.min(path.y[0], path.y[1]) == sy) ||
(dy > 0 && Math.max(path.y[0], path.y[1]) == sy)))
{
if (path.y[0] == sy)
{
path.y[0] = ny;
path.wy[0] = getWorldY(ny, layer);
} else
{
path.y[1] = ny;
path.wy[1] = getWorldY(ny, layer);
}
setLine(path.layer, SR_GPORT | SR_GSET,
path.wx[0], path.wy[0], path.wx[1], path.wy[1], true);
return SRSUCCESS;
}
getPath(wavePt.port, layer, false, true, sx, sy, nx, ny);
return SRSUCCESS;
} else if (status == SRSUCCESS)
{
ey = ny;
continue;
}
}
// end of the path, add and break loop
// check for common original path
if (path != null && path.layer == layer &&
// horizontal path check
((sy == ey && path.y[0] == path.y[1] && path.y[0] == ey &&
(path.x[0] == sx || path.x[1] == sx)) ||
// vertical path check
(sx == ex && path.x[0] == path.x[1] && path.x[0] == ex &&
(path.y[0] == sy || path.y[1] == sy))))
{
// vertical path ?
if (sx == ex)
{
if (sy == path.y[0])
{
path.y[0] = ey;
path.wy[0] = getWorldY(ey, layer);
} else
{
path.y[1] = ey;
path.wy[1] = getWorldY(ey, layer);
}
}
// horizontal path ?
else
{
if (sx == path.x[0])
{
path.x[0] = ex;
path.wx[0] = getWorldX(ex, layer);
} else
{
path.x[1] = ex;
path.wx[1] = getWorldX(ex, layer);
}
}
setLine(path.layer, SR_GPORT | SR_GSET,
path.wx[0], path.wy[0], path.wx[1], path.wy[1], true);
path = null;
} else
{
getPath(wavePt.port, layer, false, false, sx, sy, ex, ey);
}
sx = ex;
sy = ey;
break;
}
}
}
private int testPoint(int pt, int code)
{
// don't check other wavefront points
if ((pt & SR_GWAVE) == 0)
{
// check for permanent grid object (blockage or port)
if ((pt & SR_GSET) != 0)
{
// check for port
if ((pt & SR_GPORT) != 0 && (pt & SR_GMASK) == code) return SRROUTED;
} else
{
// not permanent, check for matching code
if ((pt & SR_GMASK) == code) return SRSUCCESS;
}
}
return SRUNROUTED;
}
private SRPATH getPath(SRPORT port, SRLAYER layer, boolean e1, boolean e2, int x1, int y1, int x2, int y2)
{
SRPATH path = new SRPATH();
path.x[0] = x1; path.y[0] = y1;
path.x[1] = x2; path.y[1] = y2;
path.end[0] = e1; path.end[1] = e2;
path.layer = layer;
path.port = port;
path.type = SRPROUTED;
path.wx[0] = getWorldX(x1, layer); path.wy[0] = getWorldY(y1, layer);
path.wx[1] = getWorldX(x2, layer); path.wy[1] = getWorldY(y2, layer);
// insert the path at the end of the list
path.next = null;
if (port.lastpath == null) port.paths = path; else
port.lastpath.next = path;
port.lastpath = path;
// now draw it
setLine(path.layer, SR_GPORT | SR_GSET,
path.wx[0], path.wy[0], path.wx[1], path.wy[1], true);
return path;
}
private int examinePoint(SRPORT port, SRLAYER layer, int x, int y, int code)
{
// point is set
if ((layer.grids[x][y] & SR_GWAVE) != 0)
{
// look for common point in this wavefront
if (searchWavefront(port, layer, x, y) == null)
{
return SRROUTED;
}
} else if (layer.grids[x][y] == 0)
{
// point is not set
addWavePoint(port, layer, x, y, code);
return SRSUCCESS;
}
return SRBLOCKED;
}
private SRWAVEPT searchWavefront(SRPORT port, SRLAYER layer, int x, int y)
{
// scans port's wavefront for common point
for (SRWAVEPT wavept = port.wavefront; wavept != null; wavept = wavept.next)
{
if (wavept.layer == layer && wavept.x == x && wavept.y == y) return wavept;
}
return null;
}
private void clearMaze(SRNET net)
{
// clear each region, and reset bounds
for (int index = 0; index < SRMAXLAYERS; index++)
{
SRLAYER layer = net.region.layers[index];
if (layer != null)
{
int mask = ~(SR_GMASK | SR_GWAVE);
for (int x = layer.lx; x <= layer.hx; x++)
{
for (int y = layer.ly; y <= layer.hy; y++)
{
layer.grids[x][y] = (byte)(layer.grids[x][y] & mask);
}
}
layer.lx = layer.wid; layer.ly = layer.hei;
layer.hx = -1; layer.hy = -1;
}
}
for (SRPORT port = net.ports; port != null; port = port.next)
{
port.wavefront = null;
}
return;
}
/************************************* CODE TO CREATE THE MAZE BUFFER *************************************/
private SRREGION defineRegion(Cell cell, Network net, int lX, int lY, int hX, int hY, Set arcsToDelete, Set nodesToDelete)
{
// determine routing region bounds
lX = lX - mazeBoundary;
hX = hX + mazeBoundary;
lY = lY - mazeBoundary;
hY = hY + mazeBoundary;
// allocate region and layers
SRREGION region = getRegion(lX, lY, hX, hY);
if (region == null)
{
System.out.println("Could not allocate routing region (" + lX + "<=X<=" + hX + " " + lY + "<=Y<=" + hY + ")");
return null;
}
Rectangle2D searchBounds = new Rectangle2D.Double(lX, lY, hX-lX, hY-lY);
Visitor wcVisitor = new Visitor(searchBounds, region, net);
HierarchyEnumerator.enumerateCell(cell, VarContext.globalContext, wcVisitor);
return region;
}
private class Visitor extends HierarchyEnumerator.Visitor
{
private Rectangle2D searchBounds;
private SRREGION region;
private int notThisNetID;
public Visitor(Rectangle2D searchBounds, SRREGION region, Network net)
{
this.searchBounds = searchBounds;
this.region = region;
notThisNetID = net.getNetIndex();
}
public boolean enterCell(HierarchyEnumerator.CellInfo info) { return true; }
public void exitCell(HierarchyEnumerator.CellInfo info)
{
Cell cell = info.getCell();
Netlist nl = info.getNetlist();
AffineTransform trans = info.getTransformToRoot();
for(Iterator<ArcInst> it = cell.getArcs(); it.hasNext(); )
{
ArcInst ai = it.next();
Network net = nl.getNetwork(ai, 0);
int netID = info.getNetID(net);
if (netID == notThisNetID) continue;
Rectangle2D arcBounds = ai.getBounds();
Rectangle2D bounds = new Rectangle2D.Double(arcBounds.getMinX(), arcBounds.getMinY(),
arcBounds.getWidth(), arcBounds.getHeight());
DBMath.transformRect(bounds, trans);
if (bounds.intersects(searchBounds))
drawArcInst(ai, trans, region);
}
}
public boolean visitNodeInst(Nodable no, HierarchyEnumerator.CellInfo info)
{
AffineTransform trans = info.getTransformToRoot();
Netlist nl = info.getNetlist();
NodeInst ni = no.getNodeInst();
Rectangle2D nodeBounds = ni.getBounds();
Rectangle2D bounds = new Rectangle2D.Double(nodeBounds.getMinX(), nodeBounds.getMinY(),
nodeBounds.getWidth(), nodeBounds.getHeight());
DBMath.transformRect(bounds, trans);
if (!bounds.intersects(searchBounds)) return false;
if (!ni.isCellInstance())
{
PrimitiveNode pNp = (PrimitiveNode)ni.getProto();
Technology tech = pNp.getTechnology();
Poly [] nodeInstPolyList = tech.getShapeOfNode(ni, true, true, null);
for(int i=0; i<nodeInstPolyList.length; i++)
{
Poly poly = nodeInstPolyList[i];
PortProto pp = poly.getPort();
Network net = nl.getNetwork(no, pp, 0);
if (net != null)
{
int netID = info.getNetID(net);
if (netID == notThisNetID) continue;
}
poly.transform(trans);
drawPoly(poly, region, ALLLAYERS);
}
}
return true;
}
}
/**
* Method to draw an arcinst. Returns indicator of what else needs to
* be drawn. Returns negative if display interrupted
*/
private void drawArcInst(ArcInst ai, AffineTransform trans, SRREGION region)
{
// get the polygons of the arcinst, force line for path generation?
if (ai.getProto() == Generic.tech().unrouted_arc) return;
Technology tech = ai.getProto().getTechnology();
Poly [] polys = tech.getShapeOfArc(ai);
int total = polys.length;
for (int j = 0; j < total; j++)
{
// generate a polygon
Poly poly = polys[j];
// transform the polygon
poly.transform(trans);
// draw the polygon
Point2D [] points = poly.getPoints();
if (points[0].getX() == points[1].getX())
{
drawPoly(poly, region, VERTLAYER);
} else
{
if (points[0].getY() == points[1].getY())
drawPoly(poly, region, HORILAYER); else
drawPoly(poly, region, ALLLAYERS);
}
}
}
private SRPORT addPort(SRNET net, int layers, double cX, double cY, PortInst pi)
{
SRPORT port = new SRPORT();
port.cX = cX;
port.cY = cY;
port.lx = (int)cX; port.hx = (int)cX;
port.ly = (int)cY; port.hy = (int)cY;
port.layers = layers;
port.wavefront = null;
for (int index = 0, mask = 1; index < SRMAXLAYERS; index++, mask = mask<<1)
{
if (layers != 0 & mask != 0 && net.region.layers[index] != null)
{
setBox(net.region.layers[index], SR_GPORT | SR_GSET,
port.lx, port.ly, port.hx, port.hy, true);
}
}
// link into net
port.next = null;
port.master = null;
port.paths = null;
port.lastpath = null;
port.net = net;
port.pi = pi;
SRPORT lPort = net.ports;
int index = 0;
if (lPort == null)
{
net.ports = port;
} else
{
index = 1;
while (lPort.next != null) { index++; lPort = lPort.next; }
lPort.next = port;
}
port.index = index;
return port;
}
private int determineDir(NodeInst ni, double cX, double cY)
{
if (ni == null) return 3;
// get the center of the NODEINST
double nCX = ni.getTrueCenterX();
double nCY = ni.getTrueCenterY();
// center, all edges
if (nCX == cX && nCY == cY) return 3; // all layers
double dX = ni.getBounds().getMaxX() - nCX;
double dY = ni.getBounds().getMaxY() - nCY;
double pDX = Math.abs(cX - nCX);
double pDY = Math.abs(cY - nCY);
/* consider a point on a triangle, if left/right the seq center, port,
* upper left/right edge will be counter-clockwise, if top/bottom the seq.
* will be clock-wise :
* x1 * y2 + x2 * y3 + x3 * y1 - y1 * x2 - y2 * x3 - y3 * x1 == abs(area)
* where area < 0 == clockwise, area > 0 == counter-clockwise
*/
double area = pDX * dY - pDY * dX;
if (area > 0.0) return 1; // horizontal
if (area < 0.0) return 2; // vertical
return 3; // corner, all layers
}
/**
* routing definition functions
*/
private SRNET addNet(SRREGION region, Network eNet)
{
SRNET srNet = new SRNET();
srNet.routed = false;
srNet.eNet = eNet;
srNet.ports = null;
srNet.paths = null;
srNet.lastpath = null;
srNet.region = region;
// link into region list
srNet.next = region.nets;
region.nets = srNet;
return srNet;
}
/**
* Method to write polys into the maze buffer.
*/
private void drawPoly(Poly obj, SRREGION region, int layer)
{
// now draw the polygon
if (obj.getLayer() != mazeVertLayer && obj.getLayer() != mazeHorizLayer) return;
Point2D [] points = obj.getPoints();
if (obj.getStyle() == Poly.Type.CIRCLE || obj.getStyle() == Poly.Type.THICKCIRCLE || obj.getStyle() == Poly.Type.DISC)
{
double radius = points[0].distance(points[1]);
Rectangle2D circleBounds = new Rectangle2D.Double(points[0].getX()-radius, points[0].getY()-radius, radius*2, radius*2);
drawBox(circleBounds, layer, region);
return;
}
if (obj.getStyle() == Poly.Type.CIRCLEARC || obj.getStyle() == Poly.Type.THICKCIRCLEARC)
{
// arcs at [i] points [1+i] [2+i] clockwise
if (points.length == 0) return;
if ((points.length % 3) != 0) return;
for (int i = 0; i < points.length; i += 3)
{
Point2D si = GenMath.computeArcCenter(points[i], points[i+1], points[i+2]);
drawLine(points[i+1], si, layer, region);
drawLine(si, points[i+2], layer, region);
}
return;
}
if (obj.getStyle() == Poly.Type.FILLED || obj.getStyle() == Poly.Type.CLOSED)
{
Rectangle2D objBounds = obj.getBox();
if (objBounds != null)
{
drawBox(objBounds, layer, region);
return;
}
for (int i = 1; i < points.length; i++)
drawLine(points[i-1], points[i], layer, region);
// close the region
if (points.length > 2)
drawLine(points[points.length-1], points[0], layer, region);
return;
}
if (obj.getStyle() == Poly.Type.OPENED || obj.getStyle() == Poly.Type.OPENEDT1 ||
obj.getStyle() == Poly.Type.OPENEDT1 || obj.getStyle() == Poly.Type.OPENEDT3)
{
Rectangle2D objBounds = obj.getBox();
if (objBounds != null)
{
drawBox(objBounds, layer, region);
return;
}
for (int i = 1; i < points.length; i++)
drawLine(points[i-1], points[i], layer, region);
return;
}
if (obj.getStyle() == Poly.Type.VECTORS)
{
for (int i = 1; i < points.length; i += 2)
drawLine(points[i-1], points[i], layer, region);
return;
}
}
private void drawBox(Rectangle2D box, int layer, SRREGION region)
{
int lX = (int)Math.floor(box.getMinX()-mazeBloat);
int hX = (int)Math.ceil(box.getMaxX()+mazeBloat);
int lY = (int)Math.floor(box.getMinY()-mazeBloat);
int hY = (int)Math.ceil(box.getMaxY()+mazeBloat);
if (layer == HORILAYER || layer == ALLLAYERS)
setBox(region.layers[HORILAYER], SR_GSET, lX, lY, hX, hY, false);
if (layer == VERTLAYER || layer == ALLLAYERS)
setBox(region.layers[VERTLAYER], SR_GSET, lX, lY, hX, hY, false);
}
private void drawLine(Point2D from, Point2D to, int layer, SRREGION region)
{
double wX1 = from.getX();
double wY1 = from.getY();
double wX2 = to.getX();
double wY2 = to.getY();
if (layer == HORILAYER || layer == ALLLAYERS)
setLine(region.layers[HORILAYER], SR_GSET, wX1, wY1, wX2, wY2, false);
if (layer == VERTLAYER || layer == ALLLAYERS)
setLine(region.layers[VERTLAYER], SR_GSET, wX1, wY1, wX2, wY2, false);
}
private void setLine(SRLAYER layer, int type, double wX1, double wY1, double wX2, double wY2, boolean orMode)
{
// convert to grid coordinates
int [] x = new int[2];
int [] y = new int[2];
x[0] = getGridX((int)wX1, layer); x[1] = getGridX((int)wX2, layer);
y[0] = getGridY((int)wY1, layer); y[1] = getGridY((int)wY2, layer);
int lx = 1, hx = 0;
if (wX1 < wX2) { lx = 0; hx = 1; }
int ly = 1, hy = 0;
if (wY1 < wY2) { ly = 0; hy = 1; }
// do obvious clip if completely outside of an edge
if (x[hx] < 0 || x[lx] >= layer.wid || y[hy] < 0 || y[ly] >= layer.hei) return;
// clip x
if (x[lx] < 0)
{
y[lx] -= (y[hx] - y[lx]) * x[lx] / (x[hx] - x[lx]);
x[lx] = 0;
}
if (x[hx] >= layer.wid)
{
y[hx] -= (y[hx] - y[lx]) * (x[hx] - (layer.wid-1)) / (x[hx] - x[lx]);
x[hx] = layer.wid - 1;
}
// now clip y
if (y[ly] < 0)
{
x[ly] -= (x[hy] - x[ly]) * y[ly] / (y[hy] - y[ly]);
y[ly] = 0;
}
if (y[hy] >= layer.hei)
{
x[hy] -= (x[hy] - x[ly]) * (y[hy] - (layer.hei - 1)) / (y[hy] - y[ly]);
y[hy] = layer.hei - 1;
}
// use Bresenham's algorithm to set intersecting grid points
int dX = x[hx] - x[lx];
int dY = y[hy] - y[ly];
if (dY < dX)
{
// for 0 <= dY <= dX
int e = (dY<<1) - dX;
int yi = y[lx];
int diff = 1;
if (y[hx] < y[lx]) diff = -1;
for (int xi = x[lx]; xi <= x[hx]; xi++)
{
setPoint(layer, type, xi, yi, orMode);
if (e > 0)
{
yi += diff;
e = e + (dY<<1) - (dX<<1);
} else e = e + (dY<<1);
}
} else
{
// for 0 <= dX < dY
int e = (dX<<1) - dY;
int xi = x[ly];
int diff = 1;
if (x[hy] < x[ly]) diff = -1;
for (int yi = y[ly]; yi <= y[hy]; yi++)
{
setPoint(layer, type, xi, yi, orMode);
if (e > 0)
{
xi += diff;
e = e + (dX<<1) - (dY<<1);
} else e = e + (dX<<1);
}
}
}
private void setBox(SRLAYER layer, int type, int wX1, int wY1, int wX2, int wY2, boolean orMode)
{
int lX = getGridX(wX1, layer); int lY = getGridY(wY1, layer);
int hX = getGridX(wX2, layer); int hY = getGridY(wY2, layer);
if (lX > hX) { int x = lX; lX = hX; hX = x; }
if (lY > hY) { int y = lY; lY = hY; hY = y; }
if (hX < 0 || lX >= layer.wid || hY < 0 || lY >= layer.hei) return;
// clip (simple orthogonal)
if (lX < 0) lX = 0;
if (hX >= layer.wid) hX = layer.wid - 1;
if (lY < 0) lY = 0;
if (hY >= layer.hei) hY = layer.hei - 1;
// now fill the box
for (int x = lX; x <= hX; x++)
for (int y = lY; y <= hY; y++)
setPoint(layer, type, x, y, orMode);
}
/**
* drawing function
*/
private void setPoint(SRLAYER layer, int type, int x, int y, boolean orMode)
{
if (orMode)
{
layer.grids[x][y] |= type;
layer.vused[x] |= type;
layer.hused[y] |= type;
} else
{
layer.grids[x][y] = (byte)type;
layer.vused[x] = (byte)type;
layer.hused[y] = (byte)type;
}
}
/** general control commands */
private SRREGION getRegion(int wLX, int wLY, int wHX, int wHY)
{
if (wLX > wHX || wLY > wHY) return null;
if (theRegion == null)
{
theRegion = new SRREGION();
for (int index = 0; index < SRMAXLAYERS; index++)
theRegion.layers[index] = null;
theRegion.nets = null;
} else
{
cleanoutRegion(theRegion);
}
// now set bounds
theRegion.lx = wLX;
theRegion.hx = wHX;
theRegion.ly = wLY;
theRegion.hy = wHY;
SRLAYER hlayer = addLayer(theRegion, HORILAYER, SRHORIPREF);
if (hlayer == null)
{
System.out.println("Could not allocate horizontal layer");
return null;
}
SRLAYER vlayer = addLayer(theRegion, VERTLAYER, SRVERTPREF);
if (vlayer == null)
{
System.out.println("Could not allocate vertical layer");
return null;
}
return theRegion;
}
private void cleanoutRegion(SRREGION region)
{
region.nets = null;
}
private SRLAYER addLayer(SRREGION region, int index, SRDIRECTION direction)
{
// check for common index
SRLAYER layer = region.layers[index];
if (layer == null)
{
// allocate and initialize the layer
layer = new SRLAYER();
region.layers[index] = layer;
layer.grids = null;
layer.vused = null;
layer.hused = null;
}
layer.index = index;
layer.mask = 1<<index;
layer.dir = direction;
// determine the actual bounds of the world
// round low bounds up, high bounds down
int lX = region.lx - 1;
int lY = region.ly - 1;
int hX = region.hx + 1;
int hY = region.hy + 1;
// translate the region lx, ly into grid units
layer.wid = (hX - lX) + 1;
layer.hei = (hY - lY) + 1;
layer.transx = lX;
layer.transy = lY;
// sensibility check
if (layer.wid > MAXGRIDSIZE || layer.hei > MAXGRIDSIZE)
{
System.out.println("This route is too large to solve (limit is " + MAXGRIDSIZE + "x" + MAXGRIDSIZE +
" grid, this is " + layer.wid + "x" + layer.hei + ")");
return null;
}
// check that the hx, hy of grid is in bounds of region
if (getWorldX(layer.wid - 1, layer) > region.hx) layer.wid--;
if (getWorldY(layer.hei - 1, layer) > region.hy) layer.hei--;
// now allocate a grid array
layer.grids = new byte[layer.wid][];
layer.vused = new byte[layer.wid];
for (int x = 0; x < layer.wid; x++)
{
// get address for a column of grid points
layer.grids[x] = new byte[layer.hei];
// clear all points
for (int y = 0; y < layer.hei; y++)
layer.grids[x][y] = 0;
// clear the V-used flags
layer.vused[x] = 0;
}
// clear the H-used flags
layer.hused = new byte[layer.hei];
for (int y = 0; y < layer.hei; y++) layer.hused[y] = 0;
// set up/down pointers
layer.up = layer.down = null;
if (index != 0)
{
SRLAYER alayer = region.layers[index-1];
if (alayer != null)
{
layer.down = alayer;
alayer.up = layer;
}
}
if (index < SRMAXLAYERS - 1)
{
SRLAYER alayer = region.layers[index+1];
if (alayer != null)
{
layer.up = alayer;
alayer.down = layer;
}
}
return layer;
}
private int getGridX(int Mv, SRLAYER Ml) { return Mv - Ml.transx; }
private int getGridY(int Mv, SRLAYER Ml) { return Mv - Ml.transy; }
private int getWorldX(int Mv, SRLAYER Ml) { return Mv + Ml.transx; }
private int getWorldY(int Mv, SRLAYER Ml) { return Mv + Ml.transy; }
/************************************* CODE TO CREATE RESULTS IN THE CIRCUIT *************************************/
/**
* routing grid database methods
*/
private boolean extractPaths(Cell parent, SRNET net)
{
// adjust paths to account for precise port location
double fX = 0, fY = 0;
for (SRPATH path = net.paths; path != null; path = path.next)
{
if (path.type == SRPFIXED) continue;
SRPORT port = path.port;
fX = path.wx[0]; fY = path.wy[0];
double oFX = fX, oFY = fY;
if (path.end[0] && port.pi != null)
{
// fX = port.cX; fY = port.cY;
// if (fX != oFX || fY != oFY)
// {
// adjustPath(net.paths, path, 0, fX-oFX, fY-oFY);
// }
Poly portPoly = port.pi.getPoly();
Point2D closest = portPoly.closestPoint(new Point2D.Double(fX, fY));
if (closest.getX() != oFX || closest.getY() != oFY)
adjustPath(net.paths, path, 0, closest.getX()-oFX, closest.getY()-oFY);
} else
{
ArcProto ap = path.layer.index != 0 ? mazeVertWire : mazeHorizWire;
List<PortInst> portInstList = findPort(parent, fX, fY, ap, net, true);
if (portInstList.size() > 0)
{
PortInst pi = portInstList.get(0);
Poly portPoly = pi.getPoly();
Point2D closest = portPoly.closestPoint(new Point2D.Double(fX, fY));
if (closest.getX() != oFX || closest.getY() != oFY)
adjustPath(net.paths, path, 0, closest.getX()-oFX, closest.getY()-oFY);
}
}
fX = oFX = path.wx[1]; fY = oFY = path.wy[1];
if (path.end[1] && port.pi != null)
{
// fX = port.cX; fY = port.cY;
// if (fX != oFX || fY != oFY)
// {
// adjustPath(net.paths, path, 1, fX-oFX, fY-oFY);
// }
Poly portPoly = port.pi.getPoly();
Point2D closest = portPoly.closestPoint(new Point2D.Double(fX, fY));
if (closest.getX() != oFX || closest.getY() != oFY)
adjustPath(net.paths, path, 1, closest.getX()-oFX, closest.getY()-oFY);
} else
{
ArcProto ap = path.layer.index != 0 ? mazeVertWire : mazeHorizWire;
List<PortInst> portInstList = findPort(parent, fX, fY, ap, net, true);
if (portInstList.size() > 0)
{
PortInst pi = portInstList.get(0);
Poly portPoly = pi.getPoly();
Point2D closest = portPoly.closestPoint(new Point2D.Double(fX, fY));
if (closest.getX() != oFX || closest.getY() != oFY)
adjustPath(net.paths, path, 1, closest.getX()-oFX, closest.getY()-oFY);
}
}
}
// now do the routing
for (SRPATH path = net.paths; path != null; path = path.next)
{
if (path.type == SRPFIXED) continue;
ArcProto ap = path.layer.index != 0 ? mazeVertWire : mazeHorizWire;
SRPORT port = path.port;
// create arc between the end points
List<PortInst> fromPortInstList = null;
fX = path.wx[0]; fY = path.wy[0];
if (path.end[0] && port.pi != null)
{
fromPortInstList = new ArrayList<PortInst>();
fromPortInstList.add(port.pi);
} else
{
fromPortInstList = findPort(parent, fX, fY, ap, net, false);
if (fromPortInstList.size() == 0)
{
// create the from pin
double xS = mazeSteinerNode.getDefWidth();
double yS = mazeSteinerNode.getDefHeight();
NodeInst ni = NodeInst.makeInstance(mazeSteinerNode, new Point2D.Double(fX, fY), xS, yS, parent);
if (ni == null)
{
System.out.println("Could not create pin");
return true;
}
fromPortInstList.add(ni.getPortInst(0));
}
}
List<PortInst> toPortInstList = null;
double tX = path.wx[1]; double tY = path.wy[1];
if (path.end[1] && port.pi != null)
{
toPortInstList = new ArrayList<PortInst>();
toPortInstList.add(port.pi);
} else
{
toPortInstList = findPort(parent, tX, tY, ap, net, false);
if (toPortInstList.size() == 0)
{
// create the from pin
double xS = mazeSteinerNode.getDefWidth();
double yS = mazeSteinerNode.getDefHeight();
NodeInst ni = NodeInst.makeInstance(mazeSteinerNode, new Point2D.Double(tX, tY), xS, yS, parent);
if (ni == null)
{
System.out.println("Could not create pin");
return true;
}
toPortInstList.add(ni.getPortInst(0));
}
}
// now connect (note only nodes for now, no bus like connections)
if (fromPortInstList.size() > 0 && toPortInstList.size() > 0)
{
// now make the connection (simple wire to wire for now)
PortInst fPi = fromPortInstList.get(0);
PortInst tPi = toPortInstList.get(0);
ArcInst ai = ArcInst.makeInstance(ap, fPi, tPi, new Point2D.Double(fX, fY), new Point2D.Double(tX, tY), null);
// ArcInst ai = ArcInst.makeInstanceFull(ap, ap.getDefaultLambdaFullWidth(), fPi, tPi, new Point2D.Double(fX, fY), new Point2D.Double(tX, tY), null);
if (ai == null)
{
System.out.println("Could not create path (arc)");
return true;
}
}
}
return false;
}
/**
* Method to recursively adjust paths to account for port positions that may not
* be on the grid.
*/
private void adjustPath(SRPATH paths, SRPATH thisPath, int end, double dX, double dY)
{
if (dX != 0)
{
double formerThis = thisPath.wx[end];
double formerOther = thisPath.wx[1-end];
thisPath.wx[end] += dX;
if (formerThis == formerOther)
{
double formerX = thisPath.wx[1-end];
double formerY = thisPath.wy[1-end];
thisPath.wx[1-end] += dX;
for (SRPATH opath = paths; opath != null; opath = opath.next)
{
if (opath.wx[0] == formerX && opath.wy[0] == formerY)
{
adjustPath(paths, opath, 0, dX, 0);
break;
}
if (opath.wx[1] == formerX && opath.wy[1] == formerY)
{
adjustPath(paths, opath, 1, dX, 0);
break;
}
}
}
}
if (dY != 0)
{
double formerThis = thisPath.wy[end];
double formerOther = thisPath.wy[1-end];
thisPath.wy[end] += dY;
if (formerThis == formerOther)
{
double formerX = thisPath.wx[1-end];
double formerY = thisPath.wy[1-end];
thisPath.wy[1-end] += dY;
for (SRPATH opath = paths; opath != null; opath = opath.next)
{
if (opath.wx[0] == formerX && opath.wy[0] == formerY)
{
adjustPath(paths, opath, 0, 0, dY);
break;
}
if (opath.wx[1] == formerX && opath.wy[1] == formerY)
{
adjustPath(paths, opath, 1, 0, dY);
break;
}
}
}
}
}
/**
* Method to locate the PortInsts corresponding to
* a direct intersection with the given point.
* @param cell the cell to search
* @param x X coordinate of the point to examine.
* @param y Y coordinate of the point to examine.
* @param ap the arc used to connect port (must match pp)
*/
private List<PortInst> findPort(Cell cell, double x, double y, ArcProto ap, SRNET srnet, boolean forceFind)
{
List<PortInst> portInstList = new ArrayList<PortInst>();
Point2D searchPoint = new Point2D.Double(x, y);
double bestDist = 0;
PortInst closestPi = null;
Rectangle2D searchBounds = new Rectangle2D.Double(x-0.5, y-0.5, 1, 1);
for(Iterator<RTBounds> sea = cell.searchIterator(searchBounds); sea.hasNext(); )
{
RTBounds geom = sea.next();
if (geom instanceof NodeInst)
{
// now locate a portproto
NodeInst ni = (NodeInst)geom;
for(Iterator<PortInst> it = ni.getPortInsts(); it.hasNext(); )
{
PortInst pi = it.next();
Poly portPoly = pi.getPoly();
if (portPoly.isInside(searchPoint))
{
// check if port connects to arc ...*/
if (pi.getPortProto().getBasePort().connectsTo(ap))
{
portInstList.add(pi);
}
} else
{
double dist = portPoly.polyDistance(new Rectangle2D.Double(x, y, 0, 0));
// LINTED "bestDist" used in proper order
if (closestPi == null || dist < bestDist)
{
bestDist = dist;
closestPi = pi;
}
}
}
}
}
if (portInstList.size() == 0 && forceFind && closestPi != null && bestDist < 1)
{
portInstList.add(closestPi);
}
return portInstList;
}
// /**
// * Debugging code to show the maze.
// */
// private void dumpLayer(String message, SRREGION region, int layers)
// {
// // scan for the first layer
// SRLAYER layer = null;
// for (int index = 0, mask = 1; index < SRMAXLAYERS; index++, mask = mask<<1)
// {
// if ((mask & layers) == 0) continue;
// layer = region.layers[index];
// if (layer != null) break;
// }
// int hei = layer.hei;
// int wid = layer.wid;
// System.out.println("====================== " + message + " ======================");
// if (wid >= 100)
// {
// System.out.print(" ");
// for (int x = 0; x < wid; x++) System.out.print((x / 100));
// System.out.println();
// }
// System.out.print(" ");
// for (int x = 0; x < wid; x++) System.out.print((x / 10 % 10));
// System.out.println();
// System.out.print(" ");
// for (int x = 0; x < wid; x++) System.out.print((x % 10));
// System.out.println();
// for (int y = hei-1; y >= 0; y--)
// {
// String row = "" + y;
// while (row.length() < 3) row = " " + row;
// System.out.print(row);
// for (int x = 0; x < wid; x++)
// {
// char gpt = ' ';
// for (int index = 0, mask = 1; index < SRMAXLAYERS; index++, mask = mask<<1)
// {
// if ((mask & layers) == 0) continue;
// layer = region.layers[index];
// if (layer == null) continue;
// if ((layer.grids[x][y] & SR_GSET) != 0)
// {
// if ((layer.grids[x][y] & SR_GPORT) != 0) gpt = 'P'; else
// gpt = '.';
// } else
// {
// if ((layer.grids[x][y] & SR_GWAVE) != 0) gpt = 'W'; else
// if (layer.grids[x][y] != 0)
// gpt = (char)('A' + (layer.grids[x][y] & SR_GMASK) - SR_GSTART);
// }
// }
// System.out.print(gpt);
// }
// System.out.println("");
// }
// System.out.println("");
// }
}