/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: AutoStitch.java
* Routing tool: Auto-Stitcher (places wires where geometry touches).
* Written by Steven M. Rubin, Sun Microsystems.
*
* Copyright (c) 2003 Sun Microsystems and Static Free Software
*
* 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 com.sun.electric.database.EditingPreferences;
import com.sun.electric.database.geometry.DBMath;
import com.sun.electric.database.geometry.Dimension2D;
import com.sun.electric.database.geometry.EPoint;
import com.sun.electric.database.geometry.GenMath;
import com.sun.electric.database.geometry.ObjectQTree;
import com.sun.electric.database.geometry.Poly;
import com.sun.electric.database.geometry.PolyBase;
import com.sun.electric.database.geometry.PolyMerge;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.hierarchy.Export;
import com.sun.electric.database.hierarchy.HierarchyEnumerator;
import com.sun.electric.database.hierarchy.Nodable;
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.PortOriginal;
import com.sun.electric.database.prototype.PortProto;
import com.sun.electric.database.text.TextUtils;
import com.sun.electric.database.topology.ArcInst;
import com.sun.electric.database.topology.Connection;
import com.sun.electric.database.topology.Geometric;
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.topology.RTNode;
import com.sun.electric.database.variable.EditWindow_;
import com.sun.electric.database.variable.ElectricObject;
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.Layer;
import com.sun.electric.technology.PrimitiveNode;
import com.sun.electric.technology.Technology;
import com.sun.electric.technology.technologies.Generic;
import com.sun.electric.technology.technologies.Schematics;
import com.sun.electric.tool.Job;
import com.sun.electric.tool.JobException;
import com.sun.electric.tool.user.CircuitChangeJobs;
import com.sun.electric.tool.user.User;
import java.awt.geom.AffineTransform;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
/**
* Class which implements the Auto Stitching tool.
*/
public class AutoStitch
{
/** true to ignore true pin size */ private static final boolean ZEROSIZEPINS = true;
/** router used to wire */ private InteractiveRouter router;
/** list of all routes to be created at end of analysis */ private List<Route> allRoutes;
/** list of pins that may be inline pins due to created arcs */ private List<NodeInst> possibleInlinePins;
/** cache of true pin sizes */ private Map<PortInst,Double> truePinSize;
/** set of nodes to check (prevents duplicate checks) */ private Set<NodeInst> nodeMark;
/** edge alignment for arcs */ private Dimension2D alignment;
/** true to stitch pure-layer nodes */ private boolean includePureLayerNodes;
/****************************************** CONTROL ******************************************/
/**
* Method to do auto-stitching.
* @param highlighted true to stitch only the highlighted objects.
* False to stitch the entire current cell.
* @param forced true if the stitching was explicitly requested (and so results should be printed).
*/
public static void autoStitch(boolean highlighted, boolean forced)
{
UserInterface ui = Job.getUserInterface();
Cell cell = ui.needCurrentCell();
if (cell == null) return;
List<NodeInst> nodesToStitch = null;
List<ArcInst> arcsToStitch = null;
Rectangle2D limitBound = null;
if (highlighted)
{
nodesToStitch = new ArrayList<NodeInst>();
arcsToStitch = new ArrayList<ArcInst>();
EditWindow_ wnd = ui.getCurrentEditWindow_();
if (wnd == null) return;
List<Geometric> highs = wnd.getHighlightedEObjs(true, true);
limitBound = wnd.getHighlightedArea();
for(Geometric geom : highs)
{
ElectricObject eObj = geom;
if (eObj instanceof PortInst) eObj = ((PortInst)eObj).getNodeInst();
if (eObj instanceof NodeInst)
{
NodeInst ni = (NodeInst)eObj;
if (!ni.isCellInstance())
{
PrimitiveNode pnp = (PrimitiveNode)ni.getProto();
if (pnp.getTechnology() == Generic.tech()) continue;
if (pnp.getFunction() == PrimitiveNode.Function.NODE) continue;
}
nodesToStitch.add((NodeInst)eObj);
} else if (eObj instanceof ArcInst)
{
arcsToStitch.add((ArcInst)eObj);
}
}
if (nodesToStitch.size() == 0 && arcsToStitch.size() == 0)
{
if (forced) System.out.println("Nothing selected to auto-route");
return;
}
}
double lX = 0, hX = 0, lY = 0, hY = 0;
if (limitBound != null)
{
lX = limitBound.getMinX();
hX = limitBound.getMaxX();
lY = limitBound.getMinY();
hY = limitBound.getMaxY();
}
// find out the prefered routing arc
new AutoStitchJob(cell, nodesToStitch, arcsToStitch, lX, hX, lY, hY, forced);
}
/**
* Class to do auto-stitching in a new thread.
*/
private static class AutoStitchJob extends Job
{
private Cell cell;
private List<NodeInst> nodesToStitch;
private List<ArcInst> arcsToStitch;
private double lX, hX, lY, hY;
private boolean forced;
private AutoOptions prefs;
private AutoStitchJob(Cell cell, List<NodeInst> nodesToStitch, List<ArcInst> arcsToStitch,
double lX, double hX, double lY, double hY, boolean forced)
{
super("Auto-Stitch", Routing.getRoutingTool(), Job.Type.CHANGE, null, null, Job.Priority.USER);
this.cell = cell;
this.nodesToStitch = nodesToStitch;
this.arcsToStitch = arcsToStitch;
this.lX = lX;
this.hX = hX;
this.lY = lY;
this.hY = hY;
this.forced = forced;
setReportExecutionFlag(true);
prefs = new AutoOptions();
prefs.initFromUserDefaults();
startJob();
}
public boolean doIt() throws JobException
{
Rectangle2D limitBound = null;
if (lX != hX && lY != hY)
limitBound = new Rectangle2D.Double(lX, lY, hX-lX, hY-lY);
runAutoStitch(cell, nodesToStitch, arcsToStitch, this, null, limitBound, forced, false, prefs, false, null);
return true;
}
}
/**
* This is the public interface for Auto-stitching when done in batch mode.
* @param cell the cell in which to stitch.
* @param nodesToStitch a list of NodeInsts to stitch (null to use all in the cell).
* @param arcsToStitch a list of ArcInsts to stitch (null to use all in the cell).
* @param job the Job running this, for aborting.
* @param stayInside is the area in which to route (null to route arbitrarily).
* @param limitBound if not null, only consider connections that occur in this area.
* @param forced true if the stitching was explicitly requested (and so results should be printed).
* @param includePureLayerNodes true to route pure-layer nodes (normally ignorned).
* @param prefs routing preferences.
* @param showProgress true to show progress.
* @param alignment grid alignment for edges of arcs (null if none).
*/
public static void runAutoStitch(Cell cell, List<NodeInst> nodesToStitch, List<ArcInst> arcsToStitch, Job job,
PolyMerge stayInside, Rectangle2D limitBound, boolean forced, boolean includePureLayerNodes,
AutoOptions prefs, boolean showProgress, Dimension2D alignment)
{
// initialization
if (cell.isAllLocked())
{
System.out.println("WARNING: Cell " + cell.describe(false) + " is locked: no changes can be made");
return;
}
AutoStitch as = new AutoStitch(prefs.fatWires);
as.alignment = alignment;
as.includePureLayerNodes = includePureLayerNodes;
as.runNow(cell, nodesToStitch, arcsToStitch, job, stayInside, limitBound, forced, prefs, showProgress);
}
private AutoStitch(boolean fatWires)
{
possibleInlinePins = new ArrayList<NodeInst>();
truePinSize = new HashMap<PortInst,Double>();
router = new SimpleWirer(fatWires);
}
private List<ArcInst> getArcsToStitch(Cell cell, List<ArcInst> arcsToStitch)
{
List<ArcInst> newArcsToStitch = new ArrayList<ArcInst>();
if (arcsToStitch == null)
{
for(Iterator<ArcInst> it = cell.getArcs(); it.hasNext(); )
newArcsToStitch.add(it.next());
} else
{
for(ArcInst ai : arcsToStitch)
if (ai.isLinked()) newArcsToStitch.add(ai);
}
return newArcsToStitch;
}
private List<NodeInst> getNodesToStitch(Cell cell, List<NodeInst> nodesToStitch)
{
List<NodeInst> newNodesToStitch = new ArrayList<NodeInst>();
if (nodesToStitch == null)
{
// no data from highlighter
for(Iterator<NodeInst> it = cell.getNodes(); it.hasNext(); )
{
NodeInst ni = it.next();
if (ni.isIconOfParent()) continue;
if (!ni.isCellInstance())
{
PrimitiveNode pnp = (PrimitiveNode)ni.getProto();
if (pnp.getTechnology() == Generic.tech()) continue;
if (!includePureLayerNodes && pnp.getFunction() == PrimitiveNode.Function.NODE) continue;
}
newNodesToStitch.add(ni);
}
} else
{
for(NodeInst ni : nodesToStitch)
if (ni.isLinked()) newNodesToStitch.add(ni);
}
return newNodesToStitch;
}
/**
* Method to run auto-stitching.
* @param cell the cell in which to stitch.
* @param nodesToStitch a list of NodeInsts to stitch (null to use all in the cell).
* @param arcsToStitch a list of ArcInsts to stitch (null to use all in the cell).
* @param job the Job running this, for aborting.
* @param stayInside is the area in which to route (null to route arbitrarily).
* @param limitBound if not null, only consider connections that occur in this area.
* @param forced true if the stitching was explicitly requested (and so results should be printed).
* @param prefs routing preferences.
* @param showProgress true to show progress.
*/
private void runNow(Cell cell, List<NodeInst> origNodesToStitch, List<ArcInst> origArcsToStitch, Job job,
PolyMerge stayInside, Rectangle2D limitBound, boolean forced, AutoOptions prefs, boolean showProgress)
{
if (showProgress) Job.getUserInterface().setProgressNote("Initializing routing");
ArcProto preferredArc = prefs.preferredArc;
// gather objects to stitch
List<NodeInst> nodesToStitch = getNodesToStitch(cell, origNodesToStitch);
List<ArcInst> arcsToStitch = getArcsToStitch(cell, origArcsToStitch);
// next mark nodes to be checked
nodeMark = new HashSet<NodeInst>();
for(NodeInst ni : nodesToStitch)
nodeMark.add(ni);
Map<ArcProto,Integer> arcsCreatedMap = new HashMap<ArcProto,Integer>();
Map<NodeProto,Integer> nodesCreatedMap = new HashMap<NodeProto,Integer>();
allRoutes = new ArrayList<Route>();
// compute the number of tasks to perform and start progress bar
int totalToStitch = nodesToStitch.size() + arcsToStitch.size();
if (prefs.createExports) totalToStitch *= 2;
totalToStitch += arcsToStitch.size();
int soFar = 0;
if (job != null && job.checkAbort()) return;
// if creating exports, make first pass in which exports must be created
if (prefs.createExports)
{
if (showProgress) Job.getUserInterface().setProgressNote("Routing " + totalToStitch + " objects with export creation...");
// make global network map
GatherNetworksVisitor gatherNetworks = new GatherNetworksVisitor();
HierarchyEnumerator.enumerateCell(cell, VarContext.globalContext, gatherNetworks);
Map<Long,List<PolyConnection>> overlapMap = new HashMap<Long,List<PolyConnection>>();
for(NodeInst ni : nodesToStitch)
{
soFar++;
if (showProgress && (soFar%100) == 0)
{
if (job != null && job.checkAbort()) return;
Job.getUserInterface().setProgressValue(soFar * 100 / totalToStitch);
}
checkExportCreationStitching(ni, overlapMap, gatherNetworks);
}
// now run through the arcinsts to be checked for export-creation stitching
for(ArcInst ai : arcsToStitch)
{
soFar++;
if (showProgress && (soFar%100) == 0)
{
if (job != null && job.checkAbort()) return;
Job.getUserInterface().setProgressValue(soFar * 100 / totalToStitch);
}
// only interested in arcs that are wider than their nodes (and have geometry that sticks out)
if (!arcTooWide(ai)) continue;
if (!ai.isLinked()) continue;
checkExportCreationStitching(ai, overlapMap, gatherNetworks);
}
if (showProgress) Job.getUserInterface().setProgressNote("Gathering " + totalToStitch + " objects for export creation...");
// check for existing exports, or make export if needed
for (Long netID : overlapMap.keySet())
{
List<PolyConnection> polyConns = overlapMap.get(netID);
makeExport(polyConns);
}
// now run these arcs and reinitialize the list
makeConnections(showProgress, arcsCreatedMap, nodesCreatedMap, stayInside);
allRoutes = new ArrayList<Route>();
if (showProgress) Job.getUserInterface().setProgressNote("Initializing routing");
// reinitialize list of objects to examine
nodesToStitch = getNodesToStitch(cell, origNodesToStitch);
arcsToStitch = getArcsToStitch(cell, origArcsToStitch);
}
// next pre-compute bounds on all nodes in cell
Map<NodeInst, ObjectQTree> nodePortBounds = new HashMap<NodeInst, ObjectQTree>();
for(Iterator<NodeInst> nIt = cell.getNodes(); nIt.hasNext(); )
{
NodeInst ni = nIt.next();
Rectangle2D niBounds = ni.getBounds();
ObjectQTree oqt = new ObjectQTree(niBounds);
for(Iterator<PortInst> it = ni.getPortInsts(); it.hasNext(); )
{
PortInst pi = it.next();
PortProto pp = pi.getPortProto();
PortOriginal fp = new PortOriginal(ni, pp);
AffineTransform trans = fp.getTransformToTop();
NodeInst rNi = fp.getBottomNodeInst();
double xSize = rNi.getXSize(), ySize = rNi.getYSize();
if (ZEROSIZEPINS && rNi.getFunction() == PrimitiveNode.Function.PIN)
{
double pinSize = getPortSize(pi);
xSize = ySize = pinSize;
}
Rectangle2D bounds = new Rectangle2D.Double(rNi.getAnchorCenterX() - xSize/2,
rNi.getAnchorCenterY() - ySize/2, xSize, ySize);
DBMath.transformRect(bounds, trans);
if (!oqt.add(pi, bounds))
System.out.println("ERROR: Failed to construct quad-tree with port " + pp.getName() + " of node " + ni.describe(false));
}
nodePortBounds.put(ni, oqt);
}
// finally, initialize the information about which layer is smallest on each arc
Map<ArcProto,Layer> arcLayers = new HashMap<ArcProto,Layer>();
// get the topology object for knowing what is connected
StitchingTopology top = new StitchingTopology(cell);
if (showProgress) Job.getUserInterface().setProgressNote("Routing " + totalToStitch + " objects...");
// first check for arcs that daisy-chain many nodes
for(ArcInst ai : arcsToStitch)
{
soFar++;
if (showProgress && (soFar%100) == 0)
{
if (job != null && job.checkAbort()) return;
Job.getUserInterface().setProgressValue(soFar * 100 / totalToStitch);
}
checkDaisyChain(ai, nodePortBounds, stayInside, top);
}
if (allRoutes.size() > 0)
{
// found daisy-chain elements: do them now
System.out.println("Auto-routing detected " + allRoutes.size() + " daisy-chained arcs");
for(Route route : allRoutes)
{
boolean failure = Router.createRouteNoJob(route, cell, arcsCreatedMap, nodesCreatedMap);
if (failure)
{
System.out.println("AUTO STITCHER FAILED TO MAKE DAISY-CHAIN ARC");
}
}
// reset for the rest of the analysis
allRoutes = new ArrayList<Route>();
top = new StitchingTopology(cell);
// reinitialize list of objects to examine
nodesToStitch = getNodesToStitch(cell, origNodesToStitch);
arcsToStitch = getArcsToStitch(cell, origArcsToStitch);
}
// now run through the nodeinsts to be checked for stitching
for(NodeInst ni : nodesToStitch)
{
soFar++;
if (showProgress && (soFar%100) == 0)
{
if (job != null && job.checkAbort()) return;
Job.getUserInterface().setProgressValue(soFar * 100 / totalToStitch);
}
checkStitching(ni, nodePortBounds, arcLayers, stayInside, top, limitBound, preferredArc);
}
// now run through the arcinsts to be checked for stitching
for(ArcInst ai : arcsToStitch)
{
soFar++;
if (showProgress && (soFar%100) == 0)
{
if (job != null && job.checkAbort()) return;
Job.getUserInterface().setProgressValue(soFar * 100 / totalToStitch);
}
if (!ai.isLinked()) continue;
// only interested in arcs that are wider than their nodes (and have geometry that sticks out)
if (!arcTooWide(ai)) continue;
checkStitching(ai, nodePortBounds, arcLayers, stayInside, top, limitBound, preferredArc);
}
// create the routes
makeConnections(showProgress, arcsCreatedMap, nodesCreatedMap, stayInside);
// report results
boolean beep = User.isPlayClickSoundsWhenCreatingArcs();
if (forced) Router.reportRoutingResults("AUTO ROUTING", arcsCreatedMap, nodesCreatedMap, beep);
// check for any inline pins due to created wires
if (showProgress)
{
if (job != null && job.checkAbort()) return;
Job.getUserInterface().setProgressValue(0);
Job.getUserInterface().setProgressNote("Cleaning up pins...");
}
List<CircuitChangeJobs.Reconnect> pinsToPassThrough = new ArrayList<CircuitChangeJobs.Reconnect>();
for (NodeInst ni : possibleInlinePins)
{
if (ni.isInlinePin())
{
CircuitChangeJobs.Reconnect re = CircuitChangeJobs.Reconnect.erasePassThru(ni, false, true);
if (re != null)
{
pinsToPassThrough.add(re);
}
}
}
if (pinsToPassThrough.size() > 0)
{
CircuitChangeJobs.CleanupChanges ccJob = new CircuitChangeJobs.CleanupChanges(cell, true, Collections.<NodeInst>emptySet(),
pinsToPassThrough, new HashMap<NodeInst,EPoint>(), new ArrayList<NodeInst>(), new HashSet<ArcInst>(), 0, 0, 0);
try
{
ccJob.doIt();
} catch (JobException e)
{
}
}
}
private double getPortSize(PortInst pi)
{
Double size = truePinSize.get(pi);
if (size != null) return size.doubleValue();
double widestArc = 0;
PortInst bottomPort = pi;
NodeInst bottomNi;
for(;;)
{
bottomNi = bottomPort.getNodeInst();
PortProto bottomPp = bottomPort.getPortProto();
// analyze all arcs connected to bottomPort
for(Iterator<Connection> it = bottomPort.getConnections(); it.hasNext(); )
{
Connection con = it.next();
ArcInst ai = con.getArc();
int end = con.getEndIndex();
if (!ai.isExtended(end)) continue;
widestArc = Math.max(ai.getLambdaBaseWidth(), widestArc);
}
// if at the bottom, stop
if (!bottomNi.isCellInstance()) break;
bottomPort = ((Export)bottomPp).getOriginalPort();
}
if (widestArc > bottomNi.getXSize() && widestArc > bottomNi.getYSize())
widestArc = Math.max(bottomNi.getXSize(), bottomNi.getYSize());
truePinSize.put(pi, new Double(widestArc));
return widestArc;
}
private void makeConnections(boolean showProgress, Map<ArcProto,Integer> arcsCreatedMap,
Map<NodeProto,Integer> nodesCreatedMap, PolyMerge stayInside)
{
// create the routes
int totalToStitch = allRoutes.size();
int soFar = 0;
if (showProgress)
{
Job.getUserInterface().setProgressValue(0);
Job.getUserInterface().setProgressNote("Creating " + totalToStitch + " wires...");
}
Collections.sort(allRoutes, new CompRoutes());
for (Route route : allRoutes)
{
soFar++;
if (showProgress && (soFar%100) == 0)
Job.getUserInterface().setProgressValue(soFar * 100 / totalToStitch);
RouteElement re = route.get(0);
Cell c = re.getCell();
// see if the route is unnecessary because of existing connections
RouteElementPort start = route.getStart();
RouteElementPort end = route.getEnd();
PortInst startPi = start.getPortInst();
PortInst endPi = end.getPortInst();
if (startPi != null && endPi != null)
{
boolean already = false;
for(Iterator<Connection> cIt = startPi.getConnections(); cIt.hasNext(); )
{
Connection con = cIt.next();
ArcInst existingAI = con.getArc();
if (existingAI.getHead() == con)
{
if (existingAI.getTail().getPortInst() == endPi) { already = true; break; }
} else
{
if (existingAI.getHead().getPortInst() == endPi) { already = true; break; }
}
}
if (already) continue;
}
// // if forced to fit in area, don't auto-rotate arcs
// if (stayInside != null)
// {
// for (RouteElement e : route)
// {
// if (e.getAction() == RouteElement.RouteElementAction.newArc)
// {
// RouteElementArc rea = (RouteElementArc)e;
// rea.setArcAngle(0);
// }
// }
// }
Router.createRouteNoJob(route, c, arcsCreatedMap, nodesCreatedMap);
}
}
/****************************************** ARCS THAT DAISY-CHAIN ******************************************/
private static class DaisyChainPoint
{
PortInst pi;
EPoint location;
DaisyChainPoint(PortInst p, Point2D loc)
{
pi = p;
location = new EPoint(loc.getX(), loc.getY());
}
}
/**
* Class to sort DaisyChainPoints.
*/
private static class SortDaisyPoints implements Comparator<DaisyChainPoint>
{
public int compare(DaisyChainPoint dcp1, DaisyChainPoint dcp2)
{
if (dcp1.location.getX() < dcp2.location.getX()) return 1;
if (dcp1.location.getX() > dcp2.location.getX()) return -1;
if (dcp1.location.getY() < dcp2.location.getY()) return 1;
if (dcp1.location.getY() > dcp2.location.getY()) return -1;
return 0;
}
}
/**
* Method to see if an ArcInst daisy-chains over multiple ports.
* @param ai the ArcInst in question.
* @param nodePortBounds quad-tree bounds information for all nodes in the Cell.
* @param stayInside is the area in which to route (null to route arbitrarily).
* @param top network information for the Cell with these objects.
*/
private void checkDaisyChain(ArcInst ai, Map<NodeInst, ObjectQTree> nodePortBounds, PolyMerge stayInside, StitchingTopology top)
{
// do not daisy-chain busses
if (ai.getProto() == Schematics.tech().bus_arc) return;
// make a list of PortInsts that are on the centerline of this arc
Cell cell = ai.getParent();
Network arcNet = top.getArcNetwork(ai);
Point2D e1 = ai.getHeadLocation();
Point2D e2 = ai.getTailLocation();
List<DaisyChainPoint> daisyPoints = new ArrayList<DaisyChainPoint>();
Rectangle2D searchBounds = ai.getBounds();
for(Iterator<RTBounds> it = cell.searchIterator(searchBounds); it.hasNext(); )
{
Geometric geom = (Geometric)it.next();
if (geom instanceof NodeInst)
{
NodeInst ni = (NodeInst)geom;
// find ports on this arc
ObjectQTree oqt = nodePortBounds.get(ni);
Set set = oqt.find(searchBounds);
if (set != null)
{
for (Object obj : set)
{
PortInst pi = (PortInst)obj;
if (!pi.getPortProto().getBasePort().connectsTo(ai.getProto())) continue;
PolyBase portPoly = pi.getPoly();
Point2D closest = GenMath.closestPointToSegment(e1, e2, portPoly.getCenter());
// if this port can connect, save it
if (DBMath.pointInRect(closest, portPoly.getBounds2D()))
{
// ignore if they are already connected
Network portNet = top.getPortNetwork(pi);
if (portNet == arcNet) continue;
daisyPoints.add(new DaisyChainPoint(pi, closest));
}
}
}
}
}
// now see if there are multiple intermediate daisy-chain points
if (daisyPoints.size() <= 1) return;
Collections.sort(daisyPoints, new SortDaisyPoints());
Route route = new Route();
String name = ai.getName();
//System.out.println("===DELETING DAISY CHAIN ARC FROM ("+ai.getHeadLocation().getX()+","+ai.getHeadLocation().getY()+") TO ("+
//ai.getTailLocation().getX()+","+ai.getTailLocation().getY()+") IN CELL "+ai.getParent().describe(false));
// route.add(RouteElementArc.deleteArc(ai));
name=null;
RouteElementPort headRE = RouteElementPort.existingPortInst(ai.getHeadPortInst(), ai.getHeadLocation());
RouteElementPort tailRE = RouteElementPort.existingPortInst(ai.getTailPortInst(), ai.getTailLocation());
DaisyChainPoint firstDCP = daisyPoints.get(0);
DaisyChainPoint lastDCP = daisyPoints.get(daisyPoints.size()-1);
double distOK = firstDCP.location.distance(ai.getHeadLocation()) +
lastDCP.location.distance(ai.getTailLocation());
double distSwap = firstDCP.location.distance(ai.getTailLocation()) +
lastDCP.location.distance(ai.getHeadLocation());
if (distOK > distSwap)
{
RouteElementPort swap = headRE; headRE = tailRE; tailRE = swap;
}
// if (headRE.getNodeInst().getNumConnections() == 1 && headRE.getLocation().equals(firstDCP.location))
// {
// route.add(RouteElementPort.deleteNode(headRE.getNodeInst()));
// headRE = null;
// }
// if (tailRE.getNodeInst().getNumConnections() == 1 && tailRE.getLocation().equals(lastDCP.location))
// {
// route.add(RouteElementPort.deleteNode(tailRE.getNodeInst()));
// tailRE = null;
// }
for(DaisyChainPoint dcp : daisyPoints)
{
RouteElementPort dcpRE = RouteElementPort.existingPortInst(dcp.pi, dcp.location);
if (headRE != null && headRE.getPortInst() != tailRE.getPortInst())
{
RouteElementArc re = RouteElementArc.newArc(cell, ai.getProto(), ai.getLambdaBaseWidth(), headRE, dcpRE,
headRE.getConnectingSite().getCenter(), dcpRE.getConnectingSite().getCenter(), name,
ai.getTextDescriptor(ArcInst.ARC_NAME), ai, ai.isHeadExtended(), ai.isTailExtended(), stayInside);
route.add(re);
}
headRE = dcpRE;
name = null;
}
if (tailRE != null)
{
if (headRE.getPortInst() != tailRE.getPortInst())
{
RouteElementArc re = RouteElementArc.newArc(cell, ai.getProto(), ai.getLambdaBaseWidth(), headRE, tailRE,
headRE.getConnectingSite().getCenter(), tailRE.getConnectingSite().getCenter(), name,
ai.getTextDescriptor(ArcInst.ARC_NAME), ai, ai.isHeadExtended(), ai.isTailExtended(), stayInside);
route.add(re);
}
}
allRoutes.add(route);
}
/****************************************** NORMAL STITCHING ******************************************/
/**
* Method to check an object for possible stitching to neighboring objects.
* @param geom the object to check for stitching.
* @param nodePortBounds quad-tree bounds information for all nodes in the Cell.
* @param arcLayers a map from ArcProtos to Layers.
* @param stayInside is the area in which to route (null to route arbitrarily).
* @param top network information for the Cell with these objects.
* @param limitBound if not null, only consider connections that occur in this area.
* @param preferredArc preferred ArcProto to use.
*/
private void checkStitching(Geometric geom, Map<NodeInst, ObjectQTree> nodePortBounds, Map<ArcProto,Layer> arcLayers,
PolyMerge stayInside, StitchingTopology top, Rectangle2D limitBound, ArcProto preferredArc)
{
Cell cell = geom.getParent();
NodeInst ni = null;
if (geom instanceof NodeInst) ni = (NodeInst)geom;
// make a list of other geometrics that touch or overlap this one (copy it because the main list will change)
List<Geometric> geomsInArea = new ArrayList<Geometric>();
Rectangle2D geomBounds = geom.getBounds();
double epsilon = DBMath.getEpsilon();
Rectangle2D searchBounds = new Rectangle2D.Double(geomBounds.getMinX()-epsilon, geomBounds.getMinY()-epsilon,
geomBounds.getWidth()+epsilon*2, geomBounds.getHeight()+epsilon*2);
for(Iterator<RTBounds> it = cell.searchIterator(searchBounds); it.hasNext(); )
{
Geometric oGeom = (Geometric)it.next();
if (oGeom != geom) geomsInArea.add(oGeom);
}
for(Geometric oGeom : geomsInArea)
{
// find another node in this area
if (oGeom instanceof ArcInst)
{
// other geometric is an ArcInst
ArcInst oAi = (ArcInst)oGeom;
if (ni == null)
{
// only interested in arcs that are wider than their nodes (and have geometry that sticks out)
if (!arcTooWide(oAi)) continue;
// compare arc "geom" against arc "oAi"
compareTwoArcs((ArcInst)geom, oAi, stayInside, top);
continue;
}
// compare node "ni" against arc "oAi"
if (ni.isCellInstance())
{
compareNodeInstWithArc(ni, oAi, stayInside, top, nodePortBounds);
} else
{
compareNodePrimWithArc(ni, oAi, stayInside, top);
}
} else
{
// other geometric a NodeInst
NodeInst oNi = (NodeInst)oGeom;
if (!oNi.isCellInstance())
{
PrimitiveNode pnp = (PrimitiveNode)oNi.getProto();
if (pnp.getTechnology() == Generic.tech()) continue;
if (!includePureLayerNodes && pnp.getFunction() == PrimitiveNode.Function.NODE) continue;
}
if (ni == null)
{
// compare arc "geom" against node "oNi"
if (oNi.isCellInstance())
{
compareNodeInstWithArc(oNi, (ArcInst)geom, stayInside, top, nodePortBounds);
} else
{
compareNodePrimWithArc(oNi, (ArcInst)geom, stayInside, top);
}
continue;
}
// compare node "ni" against node "oNi"
compareTwoNodes(ni, oNi, nodePortBounds, arcLayers, stayInside, top, limitBound, preferredArc);
}
}
}
/**
* Method to compare two nodes and see if they should be connected.
* @param ni the first NodeInst to compare.
* @param oNi the second NodeInst to compare.
* @param nodePortBounds quad-tree bounds information for all nodes in the Cell.
* @param arcLayers a map from ArcProtos to Layers.
* @param stayInside is the area in which to route (null to route arbitrarily).
* @param top network information for the Cell with these nodes.
* @param limitBound if not null, only consider connections that occur in this area.
* @param preferredArc preferred ArcProto to use.
*/
private void compareTwoNodes(NodeInst ni, NodeInst oNi, Map<NodeInst,ObjectQTree> nodePortBounds,
Map<ArcProto,Layer> arcLayers, PolyMerge stayInside,
StitchingTopology top, Rectangle2D limitBound, ArcProto preferredArc)
{
// if both nodes are being checked, examine them only once
if (nodeMark.contains(oNi) && oNi.getNodeIndex() <= ni.getNodeIndex()) return;
// now look at every layer in this node
Rectangle2D oBounds = oNi.getBounds();
if (ni.isCellInstance())
{
// complex node instance: look at all ports near this bound
ObjectQTree oqt = nodePortBounds.get(ni);
Rectangle2D biggerBounds = new Rectangle2D.Double(oBounds.getMinX()-1, oBounds.getMinY()-1, oBounds.getWidth()+2, oBounds.getHeight()+2);
Set set = oqt.find(biggerBounds);
if (set != null)
{
for (Object obj : set)
{
PortInst pi = (PortInst)obj;
PortProto pp = pi.getPortProto();
// find the primitive node at the bottom of this port
AffineTransform trans = ni.rotateOut();
NodeInst rNi = ni;
PortProto rPp = pp;
while (rNi.isCellInstance())
{
AffineTransform temp = rNi.translateOut();
temp.preConcatenate(trans);
Export e = (Export)rPp;
rNi = e.getOriginalPort().getNodeInst();
rPp = e.getOriginalPort().getPortProto();
trans = rNi.rotateOut();
trans.preConcatenate(temp);
}
// determine the smallest layer for all possible arcs
ArcProto [] connections = pp.getBasePort().getConnections();
for(int i=0; i<connections.length; i++)
{
findSmallestLayer(connections[i], arcLayers);
}
// look at all polygons on this nodeinst
boolean usePortPoly = false;
Poly [] nodePolys = shapeOfNode(rNi);
int tot = nodePolys.length;
if (tot == 0 || rNi.getProto() == Generic.tech().simProbeNode)
{
usePortPoly = true;
tot = 1;
}
Netlist subNetlist = rNi.getParent().getNetlist();
for(int j=0; j<tot; j++)
{
Layer layer = null;
Poly poly = null;
if (usePortPoly)
{
poly = ni.getShapeOfPort(pp);
layer = poly.getLayer();
} else
{
poly = nodePolys[j];
// only want electrically connected polygons
if (poly.getPort() == null) continue;
// only want polygons on correct part of this nodeinst
if (!subNetlist.portsConnected(rNi, rPp, poly.getPort())) continue;
// transformed polygon
poly.transform(trans);
// if the polygon layer is pseudo, substitute real layer
layer = poly.getLayer();
if (layer != null) layer = layer.getNonPseudoLayer();
}
// see which arc can make the connection
boolean connected = false;
for(int pass=0; pass<2; pass++)
{
for(int i=0; i<connections.length; i++)
{
ArcProto ap = connections[i];
if (pass == 0)
{
if (ap != preferredArc) continue;
} else
{
if (ap == preferredArc) continue;
// arc must be in the same technology
if (ap.getTechnology() != rNi.getProto().getTechnology()) continue;
}
// this polygon must be the smallest arc layer
if (!usePortPoly)
{
Layer oLayer = arcLayers.get(ap);
if (!layer.getTechnology().sameLayer(oLayer, layer)) continue;
}
// pass it on to the next test
connected = testPoly(ni, pp, ap, poly, oNi, top, nodePortBounds, arcLayers, stayInside, limitBound);
if (connected) break;
}
if (connected) break;
}
if (connected) break;
}
}
}
} else
{
// primitive node: check its layers
AffineTransform trans = ni.rotateOut();
// save information about the other node
double oX = oNi.getAnchorCenterX();
double oY = oNi.getAnchorCenterY();
// look at all polygons on this nodeinst
boolean usePortPoly = false;
Poly [] polys = shapeOfNode(ni);
int tot = polys.length;
if (tot == 0 || ni.getProto() == Generic.tech().simProbeNode)
{
usePortPoly = true;
tot = 1;
}
for(int j=0; j<tot; j++)
{
PortProto rPp = null;
Poly polyPtr = null;
if (usePortPoly)
{
// search all ports for the closest
PortProto bestPp = null;
double bestDist = 0;
for(Iterator<PortProto> pIt = ni.getProto().getPorts(); pIt.hasNext(); )
{
PortProto tPp = pIt.next();
// compute best distance to the other node
Poly portPoly = ni.getShapeOfPort(tPp);
double x = portPoly.getCenterX();
double y = portPoly.getCenterY();
double dist = Math.abs(x-oX) + Math.abs(y-oY);
if (bestPp == null)
{
bestDist = dist;
bestPp = tPp;
}
if (dist > bestDist) continue;
bestPp = tPp; bestDist = dist;
}
if (bestPp == null) continue;
rPp = bestPp;
polyPtr = ni.getShapeOfPort(rPp);
} else
{
polyPtr = polys[j];
// only want electrically connected polygons
if (polyPtr.getPort() == null) continue;
// search all ports for the closest connected to this layer
PortProto bestPp = null;
double bestDist = 0;
for(Iterator<PortProto> pIt = ni.getProto().getPorts(); pIt.hasNext(); )
{
PortProto tPp = pIt.next();
if (!top.portsConnected(ni, tPp, polyPtr.getPort())) continue;
// compute best distance to the other node
Poly portPoly = ni.getShapeOfPort(tPp);
double x = portPoly.getCenterX();
double y = portPoly.getCenterY();
double dist = Math.abs(x-oX) + Math.abs(y-oY);
if (bestPp == null) bestDist = dist;
if (dist > bestDist) continue;
bestPp = tPp; bestDist = dist;
}
if (bestPp == null) continue;
rPp = bestPp;
// transformed the polygon
polyPtr.transform(trans);
}
// if the polygon layer is pseudo, substitute real layer
Layer layer = polyPtr.getLayer();
if (layer != null) layer = layer.getNonPseudoLayer();
// stop now if already an arc on this port to other node
boolean found = false;
for(Iterator<Connection> cIt = ni.getConnections(); cIt.hasNext(); )
{
Connection con = cIt.next();
PortInst pi = con.getPortInst();
if (!top.portsConnected(ni, rPp, pi.getPortProto())) continue;
if (con.getArc().getHeadPortInst().getNodeInst() == oNi ||
con.getArc().getTailPortInst().getNodeInst() == oNi) { found = true; break; }
}
if (found) continue;
// see if an arc is possible
boolean connected = false;
ArcProto [] connections = rPp.getBasePort().getConnections();
for(int pass=0; pass<2; pass++)
{
for(int i=0; i<connections.length; i++)
{
ArcProto ap = connections[i];
if (pass == 0)
{
if (ap != preferredArc) continue;
} else
{
if (ap == preferredArc) continue;
}
// arc must be in the same technology
if (ap.getTechnology() != ni.getProto().getTechnology()) break;
// this polygon must be the smallest arc layer
findSmallestLayer(ap, arcLayers);
if (!usePortPoly)
{
Layer oLayer = arcLayers.get(ap);
if (!ap.getTechnology().sameLayer(oLayer, layer)) continue;
}
// pass it on to the next test
connected = testPoly(ni, rPp, ap, polyPtr, oNi, top, nodePortBounds, arcLayers, stayInside, limitBound);
if (connected) break;
}
if (connected) break;
}
if (connected) break;
}
}
}
/**
* Method to compare two arcs and see if they should be connected.
* @param ai1 the first ArcInst to compare.
* @param ai2 the second ArcInst to compare.
* @param stayInside is the area in which to route (null to route arbitrarily).
* @param top the Netlist information for the Cell with the arcs.
*/
private void compareTwoArcs(ArcInst ai1, ArcInst ai2, PolyMerge stayInside, StitchingTopology top)
{
// if connected, stop now
if (ai1.getProto() != ai2.getProto()) return;
Network net1 = top.getArcNetwork(ai1);
Network net2 = top.getArcNetwork(ai2);
if (net1 == net2) return;
// look at all polygons on the first arcinst
Poly [] polys1 = ai1.getProto().getTechnology().getShapeOfArc(ai1);
int tot1 = polys1.length;
Poly [] polys2 = ai2.getProto().getTechnology().getShapeOfArc(ai2);
int tot2 = polys2.length;
for(int i1=0; i1<tot1; i1++)
{
Poly poly1 = polys1[i1];
Layer layer1 = poly1.getLayer();
Layer.Function fun = layer1.getFunction();
if (!fun.isMetal() && !fun.isDiff() && !fun.isPoly()) continue;
Rectangle2D bounds1 = poly1.getBounds2D();
// compare them against all of the polygons in the second arcinst
for(int i2=0; i2<tot2; i2++)
{
Poly poly2 = polys2[i2];
if (layer1 != poly2.getLayer()) continue;
// two polygons on the same layer...are they even near each other?
Rectangle2D bounds2 = poly2.getBounds2D();
if (!bounds1.intersects(bounds2)) continue;
// do precise test for touching
// connect their closest ends
Rectangle2D intersection = new Rectangle2D.Double();
Rectangle2D.intersect(bounds1, bounds2, intersection);
double x = intersection.getCenterX();
double y = intersection.getCenterY();
// run the wire
connectObjects(ai1, net1, ai2, net2, ai1.getParent(), new Point2D.Double(x,y), stayInside, top);
return;
}
}
}
/**
* Method to compare a node instance and an arc to see if they touch and should be connected.
* @param ni the NodeInst to compare.
* @param ai the ArcInst to compare.
* @param stayInside is the area in which to route (null to route arbitrarily).
* @param top the Netlist information for the Cell with the node and arc.
* @param nodePortBounds quad-tree bounds information for all nodes in the Cell.
*/
private void compareNodeInstWithArc(NodeInst ni, ArcInst ai, PolyMerge stayInside, StitchingTopology top,
Map<NodeInst,ObjectQTree> nodePortBounds)
{
Network arcNet = top.getArcNetwork(ai);
// find all ports on the instance that are near the arc
ObjectQTree oqt = nodePortBounds.get(ni);
Rectangle2D aBounds = ai.getBounds();
Rectangle2D biggerBounds = new Rectangle2D.Double(aBounds.getMinX()-1, aBounds.getMinY()-1, aBounds.getWidth()+2, aBounds.getHeight()+2);
Set set = oqt.find(biggerBounds);
if (set == null || set.size() == 0) return;
// look at all polygons on the arcinst
Poly [] arcPolys = ai.getProto().getTechnology().getShapeOfArc(ai);
int aTot = arcPolys.length;
for(int i=0; i<aTot; i++)
{
Poly arcPoly = arcPolys[i];
Layer arcLayer = arcPoly.getLayer();
Layer.Function arcLayerFun = arcLayer.getFunction();
if (!arcLayerFun.isMetal() && !arcLayerFun.isDiff() && !arcLayerFun.isPoly()) continue;
// find ports near the arc
for (Object obj : set)
{
PortInst pi = (PortInst)obj;
// ignore if already connected
Network portNet = top.getPortNetwork(pi);
if (portNet == arcNet) continue;
// find the primitive node at the bottom of the port
AffineTransform trans = ni.rotateOut();
NodeInst rNi = ni;
PortProto rPp = pi.getPortProto();
while (rNi.isCellInstance())
{
AffineTransform temp = rNi.translateOut();
temp.preConcatenate(trans);
Export e = (Export)rPp;
rNi = e.getOriginalPort().getNodeInst();
rPp = e.getOriginalPort().getPortProto();
trans = rNi.rotateOut();
trans.preConcatenate(temp);
}
// see if anything on the base node touches the arc
Poly [] polys = shapeOfNode(rNi);
int tot = polys.length;
for(int j=0; j<tot; j++)
{
Poly baseNodePoly = polys[j];
Layer nodeLayer = baseNodePoly.getLayer();
if (nodeLayer == null) continue;
nodeLayer = nodeLayer.getNonPseudoLayer();
if (nodeLayer.getFunction() != arcLayerFun) continue;
baseNodePoly.transform(trans);
double polyDist = arcPoly.separation(baseNodePoly);
if (polyDist >= DBMath.getEpsilon()) continue;
// arc touches the port: connect them
Poly portPoly = pi.getPoly();
double portCX = portPoly.getCenterX();
double portCY = portPoly.getCenterY();
Rectangle2D arcBounds = arcPoly.getBounds2D();
double aCX = arcBounds.getCenterX();
double aCY = arcBounds.getCenterY();
Point2D bend1 = new Point2D.Double(portCX, aCY);
Point2D bend2 = new Point2D.Double(aCX, portCY);
if (stayInside != null)
{
if (!stayInside.contains(arcLayer, bend1)) bend1 = bend2;
} else
{
if (!arcPoly.contains(bend1)) bend1 = bend2;
}
connectObjects(ai, arcNet, pi, portNet, ai.getParent(), bend1, stayInside, top);
return;
}
}
}
}
/**
* Method to compare a primitive node and an arc to see if they touch and should be connected.
* @param ni the NodeInst to compare.
* @param ai the ArcInst to compare.
* @param stayInside is the area in which to route (null to route arbitrarily).
* @param top the Netlist information for the Cell with the node and arc.
*/
private void compareNodePrimWithArc(NodeInst ni, ArcInst ai, PolyMerge stayInside, StitchingTopology top)
{
Network arcNet = top.getArcNetwork(ai);
// gather information about the node
Poly [] nodePolys = shapeOfNode(ni);
int nTot = nodePolys.length;
AffineTransform trans = ni.rotateOut();
// look at all polygons on the arcinst
Poly [] arcPolys = ai.getProto().getTechnology().getShapeOfArc(ai);
int aTot = arcPolys.length;
for(int i=0; i<aTot; i++)
{
Poly arcPoly = arcPolys[i];
Layer arcLayer = arcPoly.getLayer();
Layer.Function arcLayerFun = arcLayer.getFunction();
if (!arcLayerFun.isMetal() && !arcLayerFun.isDiff() && !arcLayerFun.isPoly()) continue;
Rectangle2D arcBounds = arcPoly.getBounds2D();
double aCX = arcBounds.getCenterX();
double aCY = arcBounds.getCenterY();
// compare them against all of the polygons in the node
for(int j=0; j<nTot; j++)
{
Poly nodePoly = nodePolys[j];
nodePoly.transform(trans);
// they must be on the same layer and touch
Layer nodeLayer = nodePoly.getLayer();
if (nodeLayer != null) nodeLayer = nodeLayer.getNonPseudoLayer();
if (nodeLayer.getFunction() != arcLayerFun) continue;
double polyDist = arcPoly.separation(nodePoly);
if (polyDist >= DBMath.getEpsilon()) continue;
// only want electrically connected polygons
if (nodePoly.getPort() == null) continue;
// search all ports for the closest connected to this layer
PortProto bestPp = null;
double bestDist = 0;
for(Iterator<PortProto> pIt = ni.getProto().getPorts(); pIt.hasNext(); )
{
PortProto tPp = pIt.next();
if (!top.portsConnected(ni, tPp, nodePoly.getPort())) continue;
// compute best distance to the other node
Poly portPoly = ni.getShapeOfPort(tPp);
double portCX = portPoly.getCenterX();
double portCY = portPoly.getCenterY();
double dist = Math.abs(portCX-aCX) + Math.abs(portCY-aCY);
if (bestPp == null) bestDist = dist;
if (dist > bestDist) continue;
bestPp = tPp; bestDist = dist;
}
if (bestPp == null) continue;
// run the wire
PortInst pi = ni.findPortInstFromProto(bestPp);
Poly portPoly = ni.getShapeOfPort(bestPp);
double portCX = portPoly.getCenterX();
double portCY = portPoly.getCenterY();
Network nodeNet = top.getPortNetwork(pi);
if (arcNet == nodeNet) continue;
if (alignment != null)
{
if (alignment.getWidth() > 0)
{
portCX = Math.round(portCX / alignment.getWidth()) * alignment.getWidth();
aCX = Math.round(aCX / alignment.getWidth()) * alignment.getWidth();
}
if (alignment.getHeight() > 0)
{
portCY = Math.round(portCY / alignment.getHeight()) * alignment.getHeight();
aCY = Math.round(aCY / alignment.getHeight()) * alignment.getHeight();
}
}
Point2D bend1 = new Point2D.Double(portCX, aCY);
Point2D bend2 = new Point2D.Double(aCX, portCY);
if (stayInside != null)
{
if (!stayInside.contains(arcLayer, bend1)) bend1 = bend2;
} else
{
if (!arcPoly.contains(bend1)) bend1 = bend2;
}
connectObjects(ai, arcNet, pi, nodeNet, ai.getParent(), bend1, stayInside, top);
return;
}
}
}
/**
* Method to connect two nodes if they touch.
* @param ni the first node to test.
* @param pp the port on the first node to test.
* @param ap the arcproto to use when connecting the nodes.
* @param poly the polygon on the first node to test.
* @param oNi the second node to test.
* @param top network information for the cell with the nodes.
* @param nodePortBounds quad-tree bounds information for all nodes in the Cell.
* @param arcLayers a map from ArcProtos to Layers.
* @param stayInside is the area in which to route (null to route arbitrarily).
* @param limitBound if not null, only consider connections that occur in this area.
* @return the number of connections made (0 if none).
*/
private boolean testPoly(NodeInst ni, PortProto pp, ArcProto ap, Poly poly, NodeInst oNi, StitchingTopology top,
Map<NodeInst, ObjectQTree> nodePortBounds,
Map<ArcProto,Layer> arcLayers, PolyMerge stayInside, Rectangle2D limitBound)
{
//System.out.println("FOR LAYER "+ap.getName()+" CONSIDER PORT "+pp.getName()+" OF NODE "+ni.describe(false));
// get network associated with the node/port
PortInst pi = ni.findPortInstFromProto(pp);
// keep track of which networks we've already tied together
Set<Network> netsConnectedTo = new TreeSet<Network>();
Network net = top.getNodeNetwork(ni, pp);
if (net == null) return false;
netsConnectedTo.add(net);
// now look at every layer in this node
if (oNi.isCellInstance())
{
// complex cell: look at all exports
Rectangle2D bounds = poly.getBounds2D();
// find ports near this bound
ObjectQTree oqt = nodePortBounds.get(oNi);
Rectangle2D biggerBounds = new Rectangle2D.Double(bounds.getMinX()-1, bounds.getMinY()-1, bounds.getWidth()+2, bounds.getHeight()+2);
Set set = oqt.find(biggerBounds);
if (set != null)
{
for (Object obj : set)
{
PortInst oPi = (PortInst)obj;
PortProto mPp = oPi.getPortProto();
// // keep track of which networks we've already tied together
// Cell subcell = (Cell)oNi.getProto();
// Netlist netlist = subcell.getNetlist();
// if (mPp instanceof Export) {
// Export mPpe = (Export)mPp;
// Network netm = netlist.getNetwork(mPpe, 0);
// assert netm != null;
// if (netsConnectedTo.contains(netm)) continue;
// netsConnectedTo.add(netm);
// }
// port must be able to connect to the arc
if (!mPp.getBasePort().connectsTo(ap)) continue;
// do not stitch where there is already an electrical connection
Network oNet = top.getPortNetwork(oNi.findPortInstFromProto(mPp));
if (net != null && oNet == net) continue;
// do not stitch if there is already an arc connecting these two ports
boolean ignore = false;
for (Iterator<Connection> piit = oPi.getConnections(); piit.hasNext(); )
{
Connection conn = piit.next();
ArcInst ai = conn.getArc();
if (ai.getHeadPortInst() == pi) ignore = true;
if (ai.getTailPortInst() == pi) ignore = true;
}
if (ignore) continue;
// find the primitive node at the bottom of this port
AffineTransform trans = oNi.rotateOut();
NodeInst rNi = oNi;
PortProto rPp = mPp;
while (rNi.isCellInstance())
{
AffineTransform temp = rNi.translateOut();
temp.preConcatenate(trans);
Export e = (Export)rPp;
rNi = e.getOriginalPort().getNodeInst();
rPp = e.getOriginalPort().getPortProto();
trans = rNi.rotateOut();
trans.preConcatenate(temp);
}
// keep track of which sub-networks we've already considered
Cell subcell = (Cell)oNi.getProto();
Netlist netlist = subcell.getNetlist();
if (mPp instanceof Export) {
Export mPpe = (Export)mPp;
Network netm = netlist.getNetwork(mPpe, 0);
assert netm != null;
if (netsConnectedTo.contains(netm))
{
//System.out.println(" ---PORT "+mPp.getName()+" OF NODE "+oPi.getNodeInst().describe(false)+" FAILS NETLIST");
continue;
}
netsConnectedTo.add(netm);
}
// see how much geometry is on this node
Poly [] polys = shapeOfNode(rNi);
int tot = polys.length;
if (tot == 0)
{
// not a geometric primitive: look for ports that touch
Poly oPoly = oNi.getShapeOfPort(mPp);
comparePoly(oNi, mPp, oPoly, oNet, ni, pp, poly, net, ap, stayInside, top, limitBound);
} else
{
// a geometric primitive: look for ports on layers that touch
Netlist subNetlist = rNi.getParent().getNetlist();
for(int j=0; j<tot; j++)
{
Poly oPoly = polys[j];
// only want electrically connected polygons
if (oPoly.getPort() == null) continue;
// only want polygons connected to correct part of nodeinst
if (!subNetlist.portsConnected(rNi, rPp, oPoly.getPort())) continue;
// if the polygon layer is pseudo, substitute real layer
if (ni.getProto() != Generic.tech().simProbeNode)
{
Layer oLayer = oPoly.getLayer();
if (oLayer != null) oLayer = oLayer.getNonPseudoLayer();
Layer apLayer = arcLayers.get(ap);
if (!oLayer.getTechnology().sameLayer(oLayer, apLayer)) continue;
}
// transform the polygon and pass it on to the next test
oPoly.transform(trans);
if (comparePoly(oNi, mPp, oPoly, oNet, ni, pp, poly, net, ap, stayInside, top, limitBound))
break;
}
}
}
}
} else
{
// primitive node: check its layers
AffineTransform trans = oNi.rotateOut();
// determine target point
double ox = poly.getCenterX();
double oy = poly.getCenterY();
// look at all polygons on nodeinst oNi
Poly [] polys = shapeOfNode(oNi);
int tot = polys.length;
if (tot == 0)
{
// not a geometric primitive: look for ports that touch
PortProto bestPp = null;
double bestDist = 0;
for(Iterator<PortProto> pIt = oNi.getProto().getPorts(); pIt.hasNext(); )
{
PortProto rPp = pIt.next();
// compute best distance to the other node
Poly portPoly = oNi.getShapeOfPort(rPp);
double dist = Math.abs(portPoly.getCenterX()-ox) + Math.abs(portPoly.getCenterY()-oy);
if (bestPp == null)
{
bestDist = dist;
bestPp = rPp;
}
if (dist > bestDist) continue;
bestPp = rPp; bestDist = dist;
}
if (bestPp != null)
{
PortProto rPp = bestPp;
Network oNet = top.getPortNetwork(oNi.findPortInstFromProto(bestPp));
if (net == null || oNet != net)
{
// port must be able to connect to the arc
if (rPp.getBasePort().connectsTo(ap))
{
// transformed the polygon and pass it on to the next test
Poly oPoly = oNi.getShapeOfPort(rPp);
if (comparePoly(oNi, rPp, oPoly, oNet, ni, pp, poly, net, ap, stayInside, top, limitBound))
return true;
}
}
}
} else
{
// a geometric primitive: look for ports on layers that touch
for(int j=0; j<tot; j++)
{
Poly oPoly = polys[j];
// only want electrically connected polygons
if (oPoly.getPort() == null) continue;
// if the polygon layer is pseudo, substitute real layer
Layer oLayer = oPoly.getLayer();
if (oLayer != null) oLayer = oLayer.getNonPseudoLayer();
// this must be the smallest layer on the arc
Layer apLayer = arcLayers.get(ap);
if (!apLayer.getTechnology().sameLayer(apLayer, oLayer)) continue;
// do not stitch where there is already an electrical connection
PortInst oPi = oNi.findPortInstFromProto(oPoly.getPort());
Network oNet = top.getPortNetwork(oPi);
if (net != null && oNet == net) continue;
// search all ports for the closest connected to this layer
PortProto bestPp = null;
double bestDist = 0;
for(Iterator<PortProto> pIt = oNi.getProto().getPorts(); pIt.hasNext(); )
{
PortProto rPp = pIt.next();
if (!top.portsConnected(oNi, rPp, oPoly.getPort())) continue;
// compute best distance to the other node
Poly portPoly = oNi.getShapeOfPort(rPp);
double dist = Math.abs(ox-portPoly.getCenterX()) + Math.abs(oy-portPoly.getCenterY());
if (bestPp == null) bestDist = dist;
if (dist > bestDist) continue;
bestPp = rPp; bestDist = dist;
}
if (bestPp == null) continue;
PortProto rPp = bestPp;
// port must be able to connect to the arc
if (!rPp.getBasePort().connectsTo(ap)) continue;
// transformed the polygon and pass it on to the next test
oPoly.transform(trans);
if (comparePoly(oNi, rPp, oPoly, oNet, ni, pp, poly, net, ap, stayInside, top, limitBound))
return true;
}
}
}
return false;
}
/**
* Method to compare two polygons. If these polygons touch
* or overlap then the two nodes should be connected.
* @param oNi the NodeInst responsible for the first polygon.
* @param opp the PortProto responsible for the first polygon.
* @param oPoly the first polygon.
* @param oNet the Network responsible for the first polygon.
* @param ni the NodeInst responsible for the second polygon.
* @param pp the PortProto responsible for the second polygon.
* @param poly the second polygon.
* @param net the Network responsible for the second polygon.
* @param ap the type of arc to use when stitching the nodes.
* @param stayInside is the area in which to route (null to route arbitrarily).
* @param top the netlist for the Cell with the polygons.
* @param limitBound if not null, only consider connections that occur in this area.
* @return true if the connection is made.
*/
private boolean comparePoly(NodeInst oNi, PortProto opp, Poly oPoly, Network oNet,
NodeInst ni, PortProto pp, Poly poly, Network net,
ArcProto ap, PolyMerge stayInside, StitchingTopology top, Rectangle2D limitBound)
{
double sep = poly.separation(oPoly);
//System.out.println(" DISTANCE BETWEEN PORT "+pp.getName()+" OF NODE "+ni.describe(false)+" AND PORT "+opp.getName()+" OF NODE "+oNi.describe(false)+" IS "+sep);
// find the bounding boxes of the polygons
if (sep > DBMath.getEpsilon()) return false;
// be sure the closest ports are being used
Poly portPoly = ni.getShapeOfPort(pp);
Point2D portCenter = new Point2D.Double(portPoly.getCenterX(), portPoly.getCenterY());
Poly oPortPoly = oNi.getShapeOfPort(opp);
Point2D oPortCenter = new Point2D.Double(oPortPoly.getCenterX(), oPortPoly.getCenterY());
if (stayInside == null)
{
if (ni.isCellInstance() || oNi.isCellInstance())
{
Rectangle2D polyBounds = portPoly.getBounds2D();
Rectangle2D oPolyBounds = oPortPoly.getBounds2D();
// quit now if bounding boxes don't intersect
if ((polyBounds.getMinX() > oPolyBounds.getMaxX() || oPolyBounds.getMinX() > polyBounds.getMaxX()) &&
(polyBounds.getMinY() > oPolyBounds.getMaxY() || oPolyBounds.getMinY() > polyBounds.getMaxY())) return false;
}
}
double dist = portCenter.distance(oPortCenter);
for(Iterator<PortProto> it = oNi.getProto().getPorts(); it.hasNext(); )
{
PortProto tPp = it.next();
if (tPp == opp) continue;
if (!top.portsConnected(oNi, tPp, opp)) continue;
if (!tPp.getBasePort().connectsTo(ap)) continue;
portPoly = oNi.getShapeOfPort(tPp);
Point2D tPortCenter = new Point2D.Double(portPoly.getCenterX(), portPoly.getCenterY());
double tDist = portCenter.distance(tPortCenter);
if (tDist >= dist) continue;
dist = tDist;
opp = tPp;
oPortCenter.setLocation(tPortCenter);
}
for(Iterator<PortProto> it = ni.getProto().getPorts(); it.hasNext(); )
{
PortProto tPp = it.next();
if (tPp == pp) continue;
if (!top.portsConnected(ni, tPp, pp)) continue;
if (!tPp.getBasePort().connectsTo(ap)) continue;
portPoly = ni.getShapeOfPort(tPp);
Point2D tPortCenter = new Point2D.Double(portPoly.getCenterX(), portPoly.getCenterY());
double tDist = oPortCenter.distance(tPortCenter);
if (tDist >= dist) continue;
dist = tDist;
pp = tPp;
portCenter.setLocation(tPortCenter);
}
// reject connection if it is out of the limit bounds
if (limitBound != null)
{
if (!DBMath.pointInRect(portCenter, limitBound) && !DBMath.pointInRect(oPortCenter, limitBound))
return false;
}
// find some dummy position to help run the arc
double x = (oPortCenter.getX() + portCenter.getX()) / 2;
double y = (oPortCenter.getY() + portCenter.getY()) / 2;
if (alignment != null)
{
if (alignment.getWidth() > 0)
x = Math.round(x / alignment.getWidth()) * alignment.getWidth();
if (alignment.getHeight() > 0)
y = Math.round(y / alignment.getHeight()) * alignment.getHeight();
}
// run the wire
PortInst pi = ni.findPortInstFromProto(pp);
PortInst opi = oNi.findPortInstFromProto(opp);
//System.out.println(" *** MAKING THE CONNECTION");
return connectObjects(pi, net, opi, oNet, ni.getParent(), new Point2D.Double(x,y), stayInside, top);
}
/**
* Method to connect two objects if they touch.
* @param eobj1 the first object (either an ArcInst or a PortInst).
* @param net1 the network on which the first object resides.
* @param eobj2 the second object (either an ArcInst or a PortInst).
* @param net2 the network on which the second object resides.
* @param cell the Cell in which these objects reside.
* @param ctr bend point suggestion when making "L" connection.
* @param stayInside is the area in which to route (null to route arbitrarily).
* @param top the topology of the cell.
* @return true if a connection is made.
*/
private boolean connectObjects(ElectricObject eobj1, Network net1, ElectricObject eobj2, Network net2,
Cell cell, Point2D ctr, PolyMerge stayInside, StitchingTopology top)
{
// run the wire
NodeInst ni1 = null;
if (eobj1 instanceof NodeInst) ni1 = (NodeInst)eobj1; else
if (eobj1 instanceof PortInst) ni1 = ((PortInst)eobj1).getNodeInst();
NodeInst ni2 = null;
if (eobj2 instanceof NodeInst) ni2 = (NodeInst)eobj2; else
if (eobj2 instanceof PortInst) ni2 = ((PortInst)eobj2).getNodeInst();
Rectangle2D nullRect = null;
Route route = router.planRoute(cell, eobj1, eobj2, ctr, stayInside, true, true, nullRect, alignment);
if (route.size() == 0) return false;
allRoutes.add(route);
top.connect(net1, net2);
// if either ni or oNi is a pin primitive, see if it is a candidate for clean-up
if (ni1 != null)
{
if (ni1.getFunction().isPin() &&
!ni1.hasExports() && !ni1.hasConnections())
{
if (!possibleInlinePins.contains(ni1))
possibleInlinePins.add(ni1);
}
}
if (ni2 != null)
{
if (ni2.getFunction().isPin() &&
!ni2.hasExports() && !ni2.hasConnections())
{
if (!possibleInlinePins.contains(ni2))
possibleInlinePins.add(ni2);
}
}
return true;
}
/****************************************** EXPORT-CREATION STITCHING ******************************************/
/**
* Method to check an object for possible stitching to neighboring objects with export creation.
* Actual stitching is not done, but necessary exports are created.
* @param geom the object to check for stitching.
*/
private void checkExportCreationStitching(Geometric geom, Map<Long,List<PolyConnection>> overlapMap, GatherNetworksVisitor gatherNetworks)
{
Cell cell = geom.getParent();
NodeInst ni = null;
if (geom instanceof NodeInst) ni = (NodeInst)geom;
// make a list of other geometrics that touch or overlap this one (copy it because the main list will change)
List<Geometric> geomsInArea = new ArrayList<Geometric>();
Rectangle2D geomBounds = geom.getBounds();
double epsilon = DBMath.getEpsilon();
Rectangle2D searchBounds = new Rectangle2D.Double(geomBounds.getMinX()-epsilon, geomBounds.getMinY()-epsilon,
geomBounds.getWidth()+epsilon*2, geomBounds.getHeight()+epsilon*2);
for(Iterator<RTBounds> it = cell.searchIterator(searchBounds); it.hasNext(); )
{
Geometric oGeom = (Geometric)it.next();
if (oGeom != geom) geomsInArea.add(oGeom);
}
for(Geometric oGeom : geomsInArea)
{
// find another node in this area
if (oGeom instanceof ArcInst)
{
// other geometric is an ArcInst
ArcInst oAi = (ArcInst)oGeom;
if (ni == null) continue;
// compare node "ni" against arc "oAi"
if (ni.isCellInstance())
compareNodeInstWithArcMakeExport(ni, oAi, overlapMap, gatherNetworks);
} else
{
// other geometric a NodeInst
NodeInst oNi = (NodeInst)oGeom;
if (!oNi.isCellInstance())
{
PrimitiveNode pnp = (PrimitiveNode)oNi.getProto();
if (pnp.getTechnology() == Generic.tech()) continue;
if (!includePureLayerNodes && pnp.getFunction() == PrimitiveNode.Function.NODE) continue;
if (includePureLayerNodes && pnp.getFunction() == PrimitiveNode.Function.NODE) {
// check that pure layer node has routable layer (this filters dummy layers, etc)
boolean hasValidLayer = false;
for (Iterator<Layer> layIt = pnp.getLayerIterator(); layIt.hasNext(); ) {
Layer l = layIt.next();
if (l.getFunction().isMetal() || l.getFunction().isDiff() || l.getFunction().isPoly()) {
hasValidLayer = true; break;
}
}
if (!hasValidLayer) continue;
} else {
continue;
}
}
if (ni == null)
{
// compare arc "geom" against node "oNi"
if (oNi.isCellInstance())
compareNodeInstWithArcMakeExport(oNi, (ArcInst)geom, overlapMap, gatherNetworks);
continue;
}
// compare node "ni" against node "oNi"
if (ni.isCellInstance())
{
compareTwoNodesMakeExport(ni, oNi, overlapMap, gatherNetworks);
}
}
}
}
/**
* Method to compare a node instance and an arc to see if they touch and should be connected and an export created.
* @param ni the NodeInst to compare.
* @param ai the ArcInst to compare.
*/
private void compareNodeInstWithArcMakeExport(NodeInst ni, ArcInst ai, Map<Long,List<PolyConnection>> overlapMap, GatherNetworksVisitor gatherNetworks)
{
// get the polygon and layer that needs to connect
Poly arcPoly = null;
Poly [] arcPolys = ai.getProto().getTechnology().getShapeOfArc(ai);
int aTot = arcPolys.length;
for(int i=0; i<aTot; i++)
{
arcPoly = arcPolys[i];
Layer arcLayer = arcPoly.getLayer();
Layer.Function arcLayerFun = arcLayer.getFunction();
if (arcLayerFun.isMetal() || arcLayerFun.isDiff() || arcLayerFun.isPoly()) break;
arcPoly = null;
}
if (arcPoly == null) return;
// this is the arc ai at the top level
int netID = gatherNetworks.getGlobalNetworkID(VarContext.globalContext, ai.getHeadPortInst());
SubPolygon sp2 = new SubPolygon(arcPoly, VarContext.globalContext, netID, ai, null);
// look for geometry inside the cell that touches the arc, and make an export so it can connect
ArcTouchVisitor atv = new ArcTouchVisitor(ai, arcPoly, ni, false, gatherNetworks);
HierarchyEnumerator.enumerateCell(ni.getParent(), VarContext.globalContext, atv);
SubPolygon sp = atv.getExportDrillLocation();
if (sp != null)
{
registerPoly(overlapMap, new PolyConnection(sp, sp2));
//makeExportDrill((NodeInst)sp.theObj, sp.poly.getPort(), sp.context, null, null);
return;
}
// try arcs
atv.setDoArcs(true);
HierarchyEnumerator.enumerateCell(ni.getParent(), VarContext.globalContext, atv);
sp = atv.getExportDrillLocation();
if (sp != null)
{
registerPoly(overlapMap, new PolyConnection(sp, sp2));
// // get arc transformed to top-level
// ArcInst breakArc = (ArcInst)sp.theObj;
// if (!breakArc.isLinked()) return;
// Point2D head = breakArc.getHeadLocation();
// head = new Point2D.Double(head.getX(), head.getY());
// Point2D tail = breakArc.getTailLocation();
// tail = new Point2D.Double(tail.getX(), tail.getY());
// sp.xfToTop.transform(head, head);
// sp.xfToTop.transform(tail, tail);
// int angle = DBMath.figureAngle(head, tail);
//
// // find where it intersects the top-level arc
// Point2D breakPt = null;
// if (angle%1800 == ai.getAngle()%1800)
// {
// if (DBMath.distToLine(head, tail, ai.getHeadLocation()) <
// DBMath.distToLine(head, tail, ai.getTailLocation()))
// {
// breakPt = DBMath.intersect(head, angle, ai.getHeadLocation(), (ai.getAngle()+900)%3600);
// } else
// {
// breakPt = DBMath.intersect(head, angle, ai.getTailLocation(), (ai.getAngle()+900)%3600);
// }
// } else
// {
// breakPt = DBMath.intersect(head, angle, ai.getHeadLocation(), ai.getAngle());
// }
// if (breakPt == null) return;
//
// // transform the intersection point back down into low-level
// try
// {
// sp.xfToTop.inverseTransform(breakPt, breakPt);
// } catch (NoninvertibleTransformException e) { return; }
//
// // break the arc at that point
// PrimitiveNode pinType = breakArc.getProto().findPinProto();
// NodeInst pin = NodeInst.newInstance(pinType, breakPt, pinType.getDefaultLambdaBaseWidth(ep),
// pinType.getDefaultLambdaBaseHeight(ep), breakArc.getParent());
// if (pin == null) return;
//
// PortInst pi = pin.getOnlyPortInst();
// PortInst headPort = breakArc.getHeadPortInst();
// PortInst tailPort = breakArc.getTailPortInst();
// Point2D headPt = breakArc.getHeadLocation();
// Point2D tailPt = breakArc.getTailLocation();
// double width = breakArc.getLambdaBaseWidth();
// String arcName = breakArc.getName();
//
// // create the new arcs
// ArcInst newAi1 = ArcInst.makeInstanceBase(breakArc.getProto(), width, headPort, pi, headPt, breakPt, null);
// ArcInst newAi2 = ArcInst.makeInstanceBase(breakArc.getProto(), width, pi, tailPort, breakPt, tailPt, null);
// if (newAi1 == null || newAi2 == null) return;
// newAi1.setHeadNegated(breakArc.isHeadNegated());
// newAi1.setHeadExtended(breakArc.isHeadExtended());
// newAi1.setHeadArrowed(breakArc.isHeadArrowed());
// newAi2.setTailNegated(breakArc.isTailNegated());
// newAi2.setTailExtended(breakArc.isTailExtended());
// newAi2.setTailArrowed(breakArc.isTailArrowed());
// breakArc.kill();
// if (arcName != null)
// {
// if (headPt.distance(breakPt) > tailPt.distance(breakPt))
// {
// newAi1.setName(arcName);
// newAi1.copyTextDescriptorFrom(breakArc, ArcInst.ARC_NAME);
// } else
// {
// newAi2.setName(arcName);
// newAi2.copyTextDescriptorFrom(breakArc, ArcInst.ARC_NAME);
// }
// }
//
// // now drill the break pin to the top
// makeExportDrill(pin, pi.getPortProto(), sp.context, null, null);
}
}
/**
* Make an export if necessary to connect the two networks.
* Each pair of subpolys in the list represents the same two networks,
* so being able to connect one pair means the others will also be connected.
* @param polys A list of poly connections (pairs of subpolygons)
*/
private void makeExport(List<PolyConnection> polys)
{
if (polys.size() == 0) return;
// check for existing export
Point2D sp1AtTop = null, sp2AtTop = null;
for (PolyConnection p : polys)
{
sp1AtTop = isExportedToTop(p.sp1);
sp2AtTop = isExportedToTop(p.sp2);
if (sp1AtTop != null && sp2AtTop != null) return; // both exported to top
}
if (sp1AtTop != null) sp1AtTop = new Point2D.Double(sp1AtTop.getX(), sp1AtTop.getY()); //convert Epoint to Point2D
if (sp2AtTop != null) sp2AtTop = new Point2D.Double(sp2AtTop.getX(), sp2AtTop.getY()); //convert Epoint to Point2D
// none can connect at top level, export up first pair
PolyConnection p = polys.get(0);
List<Line2D> overlappingEdges = new ArrayList<Line2D>();
List<PolyBase> intersectionList = p.sp1.poly.getIntersection(p.sp2.poly, overlappingEdges);
PolyBase preferredExportArea = null;
if (intersectionList != null && intersectionList.size() > 0) {
preferredExportArea = intersectionList.get(0);
} else if (overlappingEdges.size() > 0) {
preferredExportArea = new PolyBase(overlappingEdges.get(0).getBounds());
}
// figure out which arc to use to connect the two - needed to decide how to create exports
ArcProto ap = null;
if (p.sp1.theObj instanceof ArcInst) {
ap = ((ArcInst)p.sp1.theObj).getProto();
}
if (p.sp2.theObj instanceof ArcInst) {
ap = ((ArcInst)p.sp2.theObj).getProto();
}
if (ap == null) {
ap = Router.getArcToUse(p.sp1.poly.getPort(), p.sp2.poly.getPort());
}
// make nodeinst exports first to get locations for arc exports
if (sp1AtTop == null && (p.sp1.theObj instanceof NodeInst)) {
sp1AtTop = makeExportDrill((NodeInst)p.sp1.theObj, p.sp1.poly.getPort(), p.sp1.context, preferredExportArea, ap);
}
if (sp2AtTop == null && (p.sp2.theObj instanceof NodeInst)) {
sp2AtTop = makeExportDrill((NodeInst)p.sp2.theObj, p.sp2.poly.getPort(), p.sp2.context, preferredExportArea, ap);
}
// make arc inst connections
if (sp1AtTop == null && (p.sp1.theObj instanceof ArcInst) && sp2AtTop != null) {
makeExportDrillOnArc(sp2AtTop, p.sp1, preferredExportArea);
//System.out.println("Making export on arc for netID "+p.sp1.netID);
}
if (sp2AtTop == null && (p.sp2.theObj instanceof ArcInst) && sp1AtTop != null) {
makeExportDrillOnArc(sp1AtTop, p.sp2, preferredExportArea);
//System.out.println("Making export on arc for netID "+p.sp2.netID);
}
}
/**
* See if the subpolygon is exported all the way to the top level of the var context.
* @param sp the subpolygon
* @return true if exported to the top level, false otherwise
*/
private Point2D isExportedToTop(SubPolygon sp)
{
Geometric geom = sp.theObj;
if (geom instanceof NodeInst)
{
NodeInst ni = (NodeInst)geom;
PortInst pi = ni.findPortInstFromProto(sp.poly.getPort());
return isExportedToTop(pi, sp.context);
}
if (geom instanceof ArcInst)
{
ArcInst ai = (ArcInst)geom;
PortInst pi1 = ai.getHead().getPortInst();
PortInst pi2 = ai.getTail().getPortInst();
Point2D point1 = isExportedToTop(pi1, sp.context);
if (point1 != null) return point1;
return isExportedToTop(pi2, sp.context);
}
return null;
}
/**
* See if the portinst is exported all the way to the top level of the var context.
* @param pi the port inst
* @param context context
* @return true if exported to the top level, false otherwise
*/
private Point2D isExportedToTop(PortInst pi, VarContext context)
{
while (context != VarContext.globalContext)
{
if (pi.getExports().hasNext())
{
Export e = pi.getExports().next();
Nodable no = context.getNodable();
if (no instanceof NodeInst)
{
NodeInst ni = (NodeInst)no;
pi = ni.findPortInstFromProto(e);
context = context.pop();
} else
{
return null;
}
} else
{
return null;
}
}
return pi.getCenter();
}
/**
* HierarchyEnumerator subclass to find cell geometry that touches an arc at the upper level.
*/
private class ArcTouchVisitor extends HierarchyEnumerator.Visitor
{
private ArcInst arcOfInterest;
private Poly arcPoly;
private int arcNetID;
private NodeInst cellOfInterest;
private boolean doArcs;
private Rectangle2D arcBounds;
private SubPolygon bestSubPolygon;
private GatherNetworksVisitor gatherNetworks;
public ArcTouchVisitor(ArcInst arcOfInterest, Poly arcPoly, NodeInst cellOfInterest, boolean doArcs, GatherNetworksVisitor gatherNetworks)
{
this.arcOfInterest = arcOfInterest;
this.arcPoly = arcPoly;
this.cellOfInterest = cellOfInterest;
this.doArcs = doArcs;
arcNetID = -1;
arcBounds = arcPoly.getBounds2D();
bestSubPolygon = null;
this.gatherNetworks = gatherNetworks;
}
public SubPolygon getExportDrillLocation() { return bestSubPolygon; }
public void setDoArcs(boolean doArcs) { this.doArcs = doArcs; }
public boolean enterCell(HierarchyEnumerator.CellInfo info) { return true; }
public void exitCell(HierarchyEnumerator.CellInfo info)
{
if (info.isRootCell()) return;
Netlist nl = info.getNetlist();
if (doArcs)
{
// look at all arcs and see if they intersect the arc
for(Iterator<ArcInst> it = info.getCell().getArcs(); it.hasNext(); )
{
ArcInst ai = it.next();
ArcProto ap = ai.getProto();
AffineTransform arcTrans = info.getTransformToRoot();
Technology tech = ap.getTechnology();
Poly [] arcInstPolyList = tech.getShapeOfArc(ai);
for(int i=0; i<arcInstPolyList.length; i++)
{
PolyBase poly = arcInstPolyList[i];
if (poly.getLayer() != arcPoly.getLayer()) continue;
int netID = -1;
Network net = nl.getNetwork(ai, 0);
if (net != null) netID = info.getNetID(net);
if (netID == arcNetID) continue;
poly.transform(arcTrans);
double dist = poly.separation(arcPoly);
if (dist >= DBMath.getEpsilon()) continue;
int netIDglobal = gatherNetworks.getGlobalNetworkID(info.getContext(), ai.getHeadPortInst());
SubPolygon sp = new SubPolygon(poly, info.getContext(), netIDglobal, ai, arcTrans);
bestSubPolygon = sp;
}
}
} else
{
// look at all nodes and see if they intersect the arc
for(Iterator<NodeInst> it = info.getCell().getNodes(); it.hasNext(); )
{
NodeInst ni = it.next();
if (ni.isCellInstance()) continue;
PrimitiveNode pNp = (PrimitiveNode)ni.getProto();
AffineTransform nodeTrans = ni.rotateOut(info.getTransformToRoot());
Technology tech = pNp.getTechnology();
Poly [] nodeInstPolyList = tech.getShapeOfNode(ni, true, true, null);
for(int i=0; i<nodeInstPolyList.length; i++)
{
PolyBase poly = nodeInstPolyList[i];
if (poly.getLayer() != arcPoly.getLayer()) continue;
int netID = -1, netIDglobal = -1;
if (poly.getPort() != null)
{
netIDglobal = gatherNetworks.getGlobalNetworkID(info.getContext(), ni.findPortInstFromProto(poly.getPort()));
Network net = nl.getNetwork(ni, poly.getPort(), 0);
if (net != null) netID = info.getNetID(net);
}
if (netID == arcNetID) continue;
poly.transform(nodeTrans);
double dist = poly.separation(arcPoly);
if (dist >= DBMath.getEpsilon()) continue;
SubPolygon sp = new SubPolygon(poly, info.getContext(), netIDglobal, ni, null);
if (bestSubPolygon != null)
{
if (!ni.hasExports()) continue;
}
bestSubPolygon = sp;
}
}
}
}
public boolean visitNodeInst(Nodable no, HierarchyEnumerator.CellInfo info)
{
NodeInst ni = no.getNodeInst();
if (info.isRootCell())
{
// ignore any subcells that aren't the one being examined
if (ni != cellOfInterest) return false;
// cache the network ID of the arc
if (arcNetID < 0)
{
Netlist nl = info.getNetlist();
Network net = nl.getNetwork(arcOfInterest, 0);
if (net != null) arcNetID = info.getNetID(net);
}
return true;
}
// only examine subcells if they intersect the arc
if (!ni.isCellInstance()) return false;
Rectangle2D b = ni.getBounds();
Rectangle2D bounds = new Rectangle2D.Double(b.getMinX(), b.getMinY(), b.getWidth(), b.getHeight());
AffineTransform trans = info.getTransformToRoot();
DBMath.transformRect(bounds, trans);
if (DBMath.rectsIntersect(bounds, arcBounds)) return true;
return false;
}
}
/**
* Method to create a stack of exports to reach a NodeInst down the hierarchy.
* @param ni the NodeInst at the bottom of the hierarchy being exported.
* @param exportThis the port on the NodeInst being exported.
* @param where the hierarchical stack that defines the path to the top.
* @return the coordinate at the top-level where the drill happened.
*/
private Point2D makeExportDrill(NodeInst ni, PortProto exportThis, VarContext where, PolyBase preferredExportArea, ArcProto ap)
{
Point2D topCoord = new Point2D.Double(0, 0);
while (where != VarContext.globalContext)
{
PortInst pi = ni.findPortInstFromProto(exportThis);
if (pi == null) break;
Iterator<Export> eIt = pi.getExports();
boolean existingExportFound = false;
if (eIt.hasNext())
{
exportThis = eIt.next();
} else
{
Rectangle2D bounds = ni.getBounds();
Cell cell = ni.getParent();
String exportName = getExportNameInCell(cell, pi);
// special case for primitive node (bottom most port) - export a pin at a different loc to make it
// easier to connect to at the top level
if (!ni.isCellInstance() && preferredExportArea != null && ap != null) // primitive node
{
PrimitiveNode pn = (PrimitiveNode)ni.getProto();
// unfortunately preferredExportArea is relative to the top level, but we need it relative to the current level
for (Iterator<Nodable> noit = where.getPathIterator(); noit.hasNext(); )
{
Nodable no = noit.next();
if (!(no instanceof NodeInst)) break;
NodeInst niHier = (NodeInst)no;
AffineTransform trans = niHier.transformIn();
preferredExportArea.transform(trans);
}
// check if there is already an export on this network in the preferred area,
// and that it can connect to pi
Netlist netlist = cell.getNetlist();
Network net = netlist.getNetwork(pi);
for (Iterator<Export> eit = cell.getExports(); eit.hasNext(); )
{
Export ex = eit.next();
PortInst expi = ex.getOriginalPort();
if (preferredExportArea.contains(expi.getCenter()))
{
if (net != netlist.getNetwork(expi)) continue;
if (ex.connectsTo(ap)) {
existingExportFound = true;
exportThis = ex;
break;
}
}
}
if (!existingExportFound)
{
Point2D center = preferredExportArea.getCenter();
if (alignment != null) {
double x = center.getX();
double y = center.getY();
if (alignment.getWidth() > 0) x = Math.round(x/alignment.getWidth()) * alignment.getWidth();
if (alignment.getHeight() > 0) y = Math.round(y/alignment.getHeight()) * alignment.getHeight();
center = new Point2D.Double(x, y);
}
// if the new export center is still within bounds
if (DBMath.pointInRect(center, bounds))
{
// make the new export if it is a primitive node (avoid huge export area), or if the export would
// not be within the preferred area
if (!preferredExportArea.contains(pi.getCenter()) || pn.getFunction() == PrimitiveNode.Function.NODE)
{
// make export on pin and wire to pi, rather than exporting pi
PrimitiveNode pin = ap.findPinProto();
NodeInst pinNi = NodeInst.newInstance(pin, center, pin.getDefWidth(), pin.getDefHeight(), cell);
Route route = router.planRoute(cell, pinNi.getOnlyPortInst(), pi, center, null, false, false, null, null);
if (!Router.createRouteNoJob(route, cell, new HashMap<ArcProto,Integer>(), new HashMap<NodeProto,Integer>())) {
pi = pinNi.getOnlyPortInst();
} else {
if (pinNi != null) pinNi.kill(); // delete if route failed
}
}
}
}
}
if (!existingExportFound)
exportThis = Export.newInstance(cell, pi, exportName);
}
ni = where.getNodable().getNodeInst();
where = where.pop();
AffineTransform trans = ni.transformOut();
trans.transform(pi.getPoly().getCenter(), topCoord);
}
return topCoord;
}
/**
* Method to create a stack of exports to reach an ArcInst down the hierarchy.
* @param topLoc the coordinate at the top level where the ArcInst should be broken.
* @param sp the context to the ArcInst.
*/
private void makeExportDrillOnArc(Point2D topLoc, SubPolygon sp, PolyBase preferredExportArea)
{
// save information about the arc
ArcInst lowAI = (ArcInst)sp.theObj;
if (!lowAI.isLinked()) return;
String arcName = lowAI.getName();
if (lowAI.getNameKey().isTempname()) arcName = null;
int angle = lowAI.getAngle();
ArcProto ap = lowAI.getProto();
double width = lowAI.getLambdaBaseWidth();
Cell cell = lowAI.getParent();
for (Iterator<Nodable> noit = sp.context.getPathIterator(); noit.hasNext(); )
{
NodeInst niHier = noit.next().getNodeInst();
AffineTransform trans = niHier.transformIn();
trans.transform(topLoc, topLoc);
if (preferredExportArea != null)
preferredExportArea.transform(trans);
}
// check if there is already an export on this network in the preferred area,
// and that it can connect to ai
Netlist netlist = cell.getNetlist();
Network net = netlist.getNetwork(lowAI, 0);
for (Iterator<Export> eit = cell.getExports(); eit.hasNext(); )
{
Export ex = eit.next();
PortInst expi = ex.getOriginalPort();
if (preferredExportArea.contains(expi.getCenter()))
{
if (net != netlist.getNetwork(expi)) continue;
if (ex.connectsTo(ap)) {
// re-export up the hierarchy
makeExportDrill(ex.getOriginalPort().getNodeInst(), ex, sp.context.pop(), preferredExportArea, ap);
return;
}
}
}
// // see if there is already a pin/export here
// NodeInst ni = null;
// Rectangle2D searchBound = new Rectangle2D.Double(topLoc.getX(), topLoc.getY(), 0, 0);
// for(Iterator<RTBounds> it = cell.searchIterator(searchBound); it.hasNext(); )
// {
// Geometric geom = (Geometric)it.next();
// if (geom instanceof ArcInst) continue;
// NodeInst foundNI = (NodeInst)geom;
// if (foundNI.getAnchorCenterX() != topLoc.getX() || foundNI.getAnchorCenterY() != topLoc.getY()) continue;
// if (foundNI.getFunction() != PrimitiveNode.Function.PIN) continue;
// ni = foundNI;
// break;
// }
// make a pin at the desired location on the arc
NodeInst ni = null;
// make sure the location is on the arc
if (GenMath.distToLine(lowAI.getHeadLocation(), lowAI.getTailLocation(), topLoc) > 0)
return;
PrimitiveNode pNp = lowAI.getProto().findPinProto();
ni = NodeInst.makeInstance(pNp, topLoc, pNp.getDefWidth(), pNp.getDefHeight(), cell);
// insert the pin into the arc
ArcInst newAi1 = ArcInst.makeInstanceBase(ap, width, lowAI.getHeadPortInst(), ni.getOnlyPortInst(), lowAI.getHeadLocation(), topLoc, null);
ArcInst newAi2 = ArcInst.makeInstanceBase(ap, width, ni.getOnlyPortInst(), lowAI.getTailPortInst(), topLoc, lowAI.getTailLocation(), null);
newAi1.setHeadNegated(lowAI.isHeadNegated());
newAi1.setHeadExtended(lowAI.isHeadExtended());
newAi1.setHeadArrowed(lowAI.isHeadArrowed());
newAi2.setTailNegated(lowAI.isTailNegated());
newAi2.setTailExtended(lowAI.isTailExtended());
newAi2.setTailArrowed(lowAI.isTailArrowed());
lowAI.kill();
if (arcName != null)
{
if (lowAI.getHeadLocation().distance(topLoc) > lowAI.getTailLocation().distance(topLoc))
{
newAi1.setName(arcName);
newAi1.copyTextDescriptorFrom(lowAI, ArcInst.ARC_NAME);
} else
{
newAi2.setName(arcName);
newAi2.copyTextDescriptorFrom(lowAI, ArcInst.ARC_NAME);
}
}
newAi1.setAngle(angle);
newAi2.setAngle(angle);
makeExportDrill(ni, ni.getOnlyPortInst().getPortProto(), sp.context, preferredExportArea, ap);
}
private String getExportNameInCell(Cell cell, PortInst pi)
{
Netlist nl = cell.getNetlist();
Network net = nl.getNetwork(pi);
String exportName = null;
for(Iterator<String> nIt = net.getExportedNames(); nIt.hasNext(); )
{
String eName = nIt.next();
if (eName.startsWith("E") && eName.length() > 1 && TextUtils.isDigit(eName.charAt(1)))
continue;
if (exportName == null || exportName.length() < eName.length()) exportName = eName;
}
if (exportName == null) exportName = "E1";
exportName = ElectricObject.uniqueObjectName(exportName, cell, PortProto.class, false, true);
return exportName;
}
/**
* Class to define a polygon that is down in the hierarchy and has a context.
*/
private static class SubPolygon implements RTBounds
{
PolyBase poly;
int netID;
VarContext context;
Geometric theObj;
AffineTransform xfToTop;
SubPolygon(PolyBase poly, VarContext context, int netID, Geometric theObj, AffineTransform xfToTop)
{
this.poly = poly;
this.context = context;
this.netID = netID;
this.theObj = theObj;
this.xfToTop = xfToTop;
}
public Rectangle2D getBounds() { return poly.getBounds2D(); }
}
/**
* Class to define two polygons that will connect down in the hierarchy.
*/
private static class PolyConnection
{
SubPolygon sp1, sp2;
double distance;
PolyConnection(SubPolygon sp1, SubPolygon sp2)
{
this.sp1 = sp1;
this.sp2 = sp2;
distance = sp1.poly.getCenter().distance(sp2.poly.getCenter());
}
}
/**
* Method to compare two node instances to see if anything inside of them needs to connect.
* May have to create exports to make the connection.
* @param ni1 the first cell instance being checked.
* @param ni2 the second cell instance being checked.
*/
private void compareTwoNodesMakeExport(NodeInst ni1, NodeInst ni2, Map<Long,List<PolyConnection>> overlapMap, GatherNetworksVisitor gatherNetworks)
{
// force the second to be a cell instance
if (!ni2.isCellInstance())
{
NodeInst swap = ni1; ni1 = ni2; ni2 = swap;
}
if (!ni2.isCellInstance()) return;
// ignore stuff from different technologies
if (ni1.getProto().getTechnology() != ni2.getProto().getTechnology()) return;
// first find the area of intersection between the two nodes
Rectangle2D bound1 = ni1.getBounds();
Rectangle2D bound2 = ni2.getBounds();
bound1 = new Rectangle2D.Double(bound1.getMinX()-DBMath.getEpsilon(), bound1.getMinY()-DBMath.getEpsilon(),
bound1.getWidth()+DBMath.getEpsilon()*2, bound1.getHeight()+DBMath.getEpsilon()*2);
bound2 = new Rectangle2D.Double(bound2.getMinX()-DBMath.getEpsilon(), bound2.getMinY()-DBMath.getEpsilon(),
bound2.getWidth()+DBMath.getEpsilon()*2, bound2.getHeight()+DBMath.getEpsilon()*2);
if (!DBMath.rectsIntersect(bound1, bound2)) return;
Rectangle2D intersectArea = bound1.createIntersection(bound2);
// now find all polygons in Node 1 that are in the intersection area
RTNode rtree = null;
if (ni1.isCellInstance())
{
GatherPolygonVisitor gpv = new GatherPolygonVisitor(intersectArea, ni1, gatherNetworks);
HierarchyEnumerator.enumerateCell(ni1.getParent(), VarContext.globalContext, gpv);
rtree = gpv.getRTree();
} else
{
rtree = RTNode.makeTopLevel();
Technology tech = ni1.getProto().getTechnology();
Poly[] polys = tech.getShapeOfNode(ni1, true, true, null);
AffineTransform trans = ni1.rotateOut();
for(int i=0; i<polys.length; i++)
{
Poly poly = polys[i];
poly.transform(trans);
int netID = -1;
if (poly.getPort() != null) {
netID = gatherNetworks.getGlobalNetworkID(VarContext.globalContext, ni1.findPortInstFromProto(poly.getPort()));
}
if (!DBMath.rectsIntersect(poly.getBounds2D(), intersectArea)) continue;
rtree = RTNode.linkGeom(null, rtree, new SubPolygon(poly, VarContext.globalContext, netID, ni1, null));
}
}
// now take the list into the second node and look for connections
CheckPolygonVisitor cpv = new CheckPolygonVisitor(rtree, intersectArea, ni2, gatherNetworks);
HierarchyEnumerator.enumerateCell(ni2.getParent(), VarContext.globalContext, cpv);
List<PolyConnection> polysFound = cpv.getFoundConnections();
// add to map of things that overlap should be connected
for (PolyConnection p : polysFound) {
registerPoly(overlapMap, p);
}
// // show what is matched
// for(PolyConnection p : polysFound)
// {
// if (p.sp1.theObj instanceof ArcInst && p.sp2.theObj instanceof NodeInst)
// {
// if (p.sp1.context != VarContext.globalContext)
// {
// Point2D topLoc = makeExportDrill((NodeInst)p.sp2.theObj, p.sp2.poly.getPort(), p.sp2.context);
// makeExportDrillOnArc(topLoc, p.sp1);
// }
// continue;
// }
// if (p.sp1.theObj instanceof NodeInst && p.sp2.theObj instanceof ArcInst)
// {
// if (p.sp2.context != VarContext.globalContext)
// {
// Point2D topLoc = makeExportDrill((NodeInst)p.sp1.theObj, p.sp1.poly.getPort(), p.sp1.context);
// makeExportDrillOnArc(topLoc, p.sp2);
// }
// continue;
// }
// if (p.sp1.theObj instanceof NodeInst && p.sp2.theObj instanceof NodeInst)
// {
// makeExportDrill((NodeInst)p.sp1.theObj, p.sp1.poly.getPort(), p.sp1.context);
// makeExportDrill((NodeInst)p.sp2.theObj, p.sp2.poly.getPort(), p.sp2.context);
// }
// }
}
/**
* HierarchyEnumerator subclass to check all geometry in a cell aganst polygons that may intersect.
*/
private class CheckPolygonVisitor extends HierarchyEnumerator.Visitor
{
private RTNode rtree;
private Rectangle2D intersectArea;
private NodeInst cellOfInterest;
private List<PolyConnection> connectionsFound;
private GatherNetworksVisitor gatherNetworks;
public CheckPolygonVisitor(RTNode rtree, Rectangle2D intersectArea, NodeInst cellOfInterest, GatherNetworksVisitor gatherNetworks)
{
this.rtree = rtree;
this.intersectArea = intersectArea;
this.cellOfInterest = cellOfInterest;
this.gatherNetworks = gatherNetworks;
connectionsFound = new ArrayList<PolyConnection>();
}
public List<PolyConnection> getFoundConnections() { return connectionsFound; }
public boolean enterCell(HierarchyEnumerator.CellInfo info) { return true; }
public void exitCell(HierarchyEnumerator.CellInfo info)
{
AffineTransform toTop = info.getTransformToRoot();
// check all nodes against the list
for(Iterator<NodeInst> it = info.getCell().getNodes(); it.hasNext(); )
{
NodeInst ni = it.next();
if (ni.isCellInstance()) continue;
AffineTransform nodeTrans = ni.rotateOut(toTop);
Technology tech = ni.getProto().getTechnology();
Poly[] polys = tech.getShapeOfNode(ni, true, true, null);
for(int i=0; i<polys.length; i++)
{
Poly poly = polys[i];
if (poly.getPort() == null) continue;
poly.transform(nodeTrans);
if (!DBMath.rectsIntersect(poly.getBounds2D(), intersectArea)) continue;
for(RTNode.Search sea = new RTNode.Search(poly.getBounds2D(), rtree, true); sea.hasNext(); )
{
SubPolygon sp = (SubPolygon)sea.next();
if (sp.poly.getLayer() != poly.getLayer()) continue;
if (sp.poly.separation(poly) >= DBMath.getEpsilon()) continue;
int netID = gatherNetworks.getGlobalNetworkID(info.getContext(), ni.findPortInstFromProto(poly.getPort()));
SubPolygon sp2 = new SubPolygon(poly, info.getContext(), netID, ni, null);
addConnection(sp, sp2);
}
}
}
// check all arcs against the list
for(Iterator<ArcInst> it = info.getCell().getArcs(); it.hasNext(); )
{
ArcInst ai = it.next();
Technology tech = ai.getProto().getTechnology();
Poly [] arcPolyList = tech.getShapeOfArc(ai);
for(int i=0; i<arcPolyList.length; i++)
{
PolyBase poly = arcPolyList[i];
poly.transform(toTop);
if (!DBMath.rectsIntersect(poly.getBounds2D(), intersectArea)) continue;
for(RTNode.Search sea = new RTNode.Search(poly.getBounds2D(), rtree, true); sea.hasNext(); )
{
SubPolygon sp = (SubPolygon)sea.next();
if (sp.poly.getLayer() != poly.getLayer()) continue;
if (sp.poly.separation(poly) > 0) continue;
int netID = gatherNetworks.getGlobalNetworkID(info.getContext(), ai.getHeadPortInst());
SubPolygon sp2 = new SubPolygon(poly, info.getContext(), netID, ai, null);
addConnection(sp, sp2);
}
}
}
}
public boolean visitNodeInst(Nodable no, HierarchyEnumerator.CellInfo info)
{
// only examine subcells if they intersect the area of interest
NodeInst ni = no.getNodeInst();
if (info.isRootCell())
{
// ignore any subcells that aren't the one being examined
if (ni != cellOfInterest) return false;
}
if (!ni.isCellInstance()) return false;
Rectangle2D b = ni.getBounds();
Rectangle2D bounds = new Rectangle2D.Double(b.getMinX(), b.getMinY(), b.getWidth(), b.getHeight());
AffineTransform trans = info.getTransformToRoot();
DBMath.transformRect(bounds, trans);
if (DBMath.rectsIntersect(bounds, intersectArea)) return true;
return false;
}
private void addConnection(SubPolygon sp1, SubPolygon sp2)
{
double distance = sp1.poly.getCenter().distance(sp2.poly.getCenter());
boolean found = false;
for(PolyConnection pc : connectionsFound)
{
if (pc.sp1.netID == sp1.netID && pc.sp2.netID == sp2.netID)
{
// found this connection: see if it is closer
boolean replace = distance < pc.distance;
int oldNodeCount = (pc.sp1.theObj instanceof NodeInst?1:0) + (pc.sp2.theObj instanceof NodeInst?1:0);
int newNodeCount = (sp1.theObj instanceof NodeInst?1:0) + (sp2.theObj instanceof NodeInst?1:0);
if (newNodeCount > oldNodeCount) replace = true; else
if (newNodeCount < oldNodeCount) replace = false;
if (replace)
{
pc.sp1 = sp1;
pc.sp2 = sp2;
pc.distance = distance;
}
found = true;
break;
}
}
if (!found)
{
connectionsFound.add(new PolyConnection(sp1, sp2));
}
}
}
/**
* HierarchyEnumerator subclass to find all geometry in a cell that touches a desired area at the upper level.
*/
private class GatherPolygonVisitor extends HierarchyEnumerator.Visitor
{
private RTNode rtree;
private Rectangle2D intersectArea;
private NodeInst cellOfInterest;
private GatherNetworksVisitor gatherNetworks;
public GatherPolygonVisitor(Rectangle2D intersectArea, NodeInst cellOfInterest, GatherNetworksVisitor gatherNetworks)
{
rtree = RTNode.makeTopLevel();
this.intersectArea = intersectArea;
this.cellOfInterest = cellOfInterest;
this.gatherNetworks = gatherNetworks;
}
public RTNode getRTree() { return rtree; }
public boolean enterCell(HierarchyEnumerator.CellInfo info) { return true; }
public void exitCell(HierarchyEnumerator.CellInfo info)
{
AffineTransform toTop = info.getTransformToRoot();
// add all nodes to the list
for(Iterator<NodeInst> it = info.getCell().getNodes(); it.hasNext(); )
{
NodeInst ni = it.next();
if (ni.isCellInstance()) continue;
AffineTransform nodeTrans = ni.rotateOut(toTop);
Technology tech = ni.getProto().getTechnology();
Poly[] polys = tech.getShapeOfNode(ni, true, true, null);
for(int i=0; i<polys.length; i++)
{
Poly poly = polys[i];
Layer.Function nodeLayerFun = poly.getLayer().getFunction();
if (!nodeLayerFun.isMetal() && !nodeLayerFun.isDiff() && !nodeLayerFun.isPoly()) continue;
if (poly.getPort() == null) continue;
poly.transform(nodeTrans);
if (!DBMath.rectsIntersect(poly.getBounds2D(), intersectArea)) continue;
int netID = gatherNetworks.getGlobalNetworkID(info.getContext(), ni.findPortInstFromProto(poly.getPort()));
rtree = RTNode.linkGeom(null, rtree, new SubPolygon(poly, info.getContext(), netID, ni, null));
}
}
// add all arcs to the list
for(Iterator<ArcInst> it = info.getCell().getArcs(); it.hasNext(); )
{
ArcInst ai = it.next();
Technology tech = ai.getProto().getTechnology();
Poly [] arcPolyList = tech.getShapeOfArc(ai);
for(int i=0; i<arcPolyList.length; i++)
{
PolyBase poly = arcPolyList[i];
Layer.Function arcLayerFun = poly.getLayer().getFunction();
if (!arcLayerFun.isMetal() && !arcLayerFun.isDiff() && !arcLayerFun.isPoly()) continue;
poly.transform(toTop);
if (!DBMath.rectsIntersect(poly.getBounds2D(), intersectArea)) continue;
int netID = gatherNetworks.getGlobalNetworkID(info.getContext(), ai.getHeadPortInst());
rtree = RTNode.linkGeom(null, rtree, new SubPolygon(poly, info.getContext(), netID, ai, null));
}
}
}
public boolean visitNodeInst(Nodable no, HierarchyEnumerator.CellInfo info)
{
// only examine subcells if they intersect the area of interest
NodeInst ni = no.getNodeInst();
if (info.isRootCell())
{
// ignore any subcells that aren't the one being examined
if (ni != cellOfInterest) return false;
}
if (!ni.isCellInstance()) return false;
Rectangle2D b = ni.getBounds();
Rectangle2D bounds = new Rectangle2D.Double(b.getMinX(), b.getMinY(), b.getWidth(), b.getHeight());
AffineTransform trans = info.getTransformToRoot();
DBMath.transformRect(bounds, trans);
if (DBMath.rectsIntersect(bounds, intersectArea)) return true;
return false;
}
}
private void registerPoly(Map<Long,List<PolyConnection>> overlapMap, PolyConnection p)
{
// make sure lower netID is in sp1
if (p.sp1.netID > p.sp2.netID) {
SubPolygon spTemp = p.sp1;
p.sp1 = p.sp2;
p.sp2 = spTemp;
}
long netID1 = p.sp1.netID;
long netID2 = p.sp2.netID;
if (netID1 == netID2) return; // already connected
if (netID1 < 0 || netID2 < 0) {
System.out.println("Ignoring poly "+p.sp1.theObj.describe(false)+", netID is "+netID1);
System.out.println("Ignoring poly "+p.sp2.theObj.describe(false)+", netID is "+netID2);
return;
}
// get unique key
Long key = new Long((netID1 << Integer.SIZE) | (netID2));
List<PolyConnection> polys = overlapMap.get(key);
if (polys == null) {
polys = new ArrayList<PolyConnection>();
overlapMap.put(key,polys);
}
polys.add(p);
}
/**
* HierarchyEnumerator subclass to gather a global consistent network map
*/
private class GatherNetworksVisitor extends HierarchyEnumerator.Visitor
{
// String key is varContext.getInstPath(".")
private Map<String,Map<PortInst,Integer>> networkToNetID = new HashMap<String,Map<PortInst,Integer>>();
public boolean enterCell(HierarchyEnumerator.CellInfo info) {
return true;
}
public void exitCell(HierarchyEnumerator.CellInfo info) {
Cell cell = info.getCell();
VarContext context = info.getContext();
String key = context.getInstPath(".");
Map<PortInst,Integer> netIdMap = networkToNetID.get(key);
if (netIdMap == null) {
netIdMap = new HashMap<PortInst,Integer>();
networkToNetID.put(key, netIdMap);
}
Netlist netlist = info.getNetlist();
for (Iterator<NodeInst> it = cell.getNodes(); it.hasNext(); ) {
NodeInst ni = it.next();
for (Iterator<PortInst> pit = ni.getPortInsts(); pit.hasNext(); ) {
PortInst pi = pit.next();
Network net = netlist.getNetwork(pi);
if (net == null) continue;
int netID = info.getNetID(net);
netIdMap.put(pi, new Integer(netID));
}
}
}
public boolean visitNodeInst(Nodable ni, HierarchyEnumerator.CellInfo info) {
return true;
}
private int getGlobalNetworkID(VarContext context, PortInst pi) {
if (pi == null) return -1;
if (context == null) return -1;
String key = context.getInstPath(".");
Map<PortInst,Integer> netIdMap = networkToNetID.get(key);
if (netIdMap == null) return -1;
if (!netIdMap.containsKey(pi)) return -1;
return netIdMap.get(pi).intValue();
}
}
/****************************************** SUPPORT ******************************************/
/**
* Method to determine if an arc is too wide for its ends.
* Arcs that are wider than their nodes stick out from those nodes,
* and their geometry must be considered, even though the nodes have been checked.
* @param ai the ArcInst to check.
* @return true if the arc is wider than its end nodes
*/
private boolean arcTooWide(ArcInst ai)
{
boolean headTooWide = true;
NodeInst hNi = ai.getHeadPortInst().getNodeInst();
if (hNi.isCellInstance()) headTooWide = false; else
if (ai.getLambdaBaseWidth() <= hNi.getLambdaBaseXSize() &&
ai.getLambdaBaseWidth() <= hNi.getLambdaBaseYSize()) headTooWide = false;
boolean tailTooWide = true;
NodeInst tNi = ai.getTailPortInst().getNodeInst();
if (tNi.isCellInstance()) tailTooWide = false; else
if (ai.getLambdaBaseWidth() <= tNi.getLambdaBaseXSize() &&
ai.getLambdaBaseWidth() <= tNi.getLambdaBaseYSize()) tailTooWide = false;
return headTooWide || tailTooWide;
}
/**
* Method to get the shape of a node as a list of Polys.
* The autorouter uses this instead of Technology.getShapeOfNode()
* because this gets electrical layers and makes invisible pins be visible
* if they have coverage from connecting arcs.
* @param ni the node to inspect. It must be primitive.
* @return an array of Poly objects that describe the node.
*/
private Poly [] shapeOfNode(NodeInst ni)
{
// compute the list of polygons
Technology tech = ni.getProto().getTechnology();
if (tech.isSchematics()) return new Poly[0];
Poly [] nodePolys = tech.getShapeOfNode(ni, true, true, null);
if (nodePolys.length == 0) return nodePolys;
// if this is a pin, check the arcs that cover it
if (ni.getFunction().isPin())
{
// pins must be covered by an arc that is extended and has enough width to cover the pin
boolean gotOne = false;
Rectangle2D coverage = null;
for(Iterator<Connection> it = ni.getConnections(); it.hasNext(); )
{
Connection con = it.next();
ArcInst ai = con.getArc();
if (ai.getLambdaBaseWidth() >= ni.getLambdaBaseXSize() &&
ai.getLambdaBaseWidth() >= ni.getLambdaBaseYSize() && ai.isHeadExtended() && ai.isTailExtended())
{
gotOne = true;
break;
}
// figure out how much of the pin is covered by the arc
Poly [] arcPolys = ai.getProto().getTechnology().getShapeOfArc(ai);
if (arcPolys.length == 0) continue;
Poly arcPoly = arcPolys[0];
Rectangle2D arcBounds = arcPoly.getBounds2D();
Rectangle2D arcBoundsLimited = new Rectangle2D.Double();
Rectangle2D.intersect(nodePolys[0].getBounds2D(), arcBounds, arcBoundsLimited);
if (coverage == null)
{
coverage = arcBoundsLimited;
} else
{
// not known, intersection is a bit restrictive...
Rectangle2D.union(coverage, arcBoundsLimited, coverage);
}
}
if (!gotOne) // && !ni.hasExports())
{
if (coverage == null) return new Poly[0];
Poly newPoly = new Poly(coverage);
newPoly.setStyle(nodePolys[0].getStyle());
newPoly.setLayer(nodePolys[0].getLayerOrPseudoLayer());
newPoly.setPort(nodePolys[0].getPort());
AffineTransform trans = ni.rotateIn();
newPoly.transform(trans);
nodePolys[0] = newPoly;
}
}
return nodePolys;
}
/**
* Method to find and cache the smallest layer on an ArcProto.
* @param ap the ArcProto being examined.
* @param arcLayers a map from ArcProtos to their smallest Layers.
*/
private void findSmallestLayer(ArcProto ap, Map<ArcProto,Layer> arcLayers)
{
// quit if the value has already been computed
if (arcLayers.get(ap) != null) return;
// find the smallest layer
Layer smallestLayer = ap.getLayer(0);
int smallestGridExtend = ap.getLayerGridExtend(0);
for (int arcLayer = 1; arcLayer < ap.getNumArcLayers(); arcLayer++) {
int gridExtend = ap.getLayerGridExtend(arcLayer);
if (gridExtend < smallestGridExtend) {
smallestLayer = ap.getLayer(arcLayer);
smallestGridExtend = gridExtend;
}
}
arcLayers.put(ap, smallestLayer);
}
/**
* Class to sort Routes.
*/
private static class CompRoutes implements Comparator<Route>
{
public int compare(Route r1, Route r2)
{
// separate nodes from arcs
RouteElementPort r1s = r1.getStart();
RouteElementPort r1e = r1.getEnd();
RouteElementPort r2s = r2.getStart();
RouteElementPort r2e = r2.getEnd();
boolean r1ToArc = r1s.getPortInst() == null || r1e.getPortInst() == null;
boolean r2ToArc = r2s.getPortInst() == null || r2e.getPortInst() == null;
if (r1ToArc && !r2ToArc) return 1;
if (!r1ToArc && r2ToArc) return -1;
if (r1ToArc && r2ToArc)
{
ArcProto ap1 = null, ap2 = null;
if (r1s.getNewArcs().hasNext()) ap1 = ((RouteElementArc)(r1s.getNewArcs().next())).getArcProto();
if (r1e.getNewArcs().hasNext()) ap1 = ((RouteElementArc)(r1e.getNewArcs().next())).getArcProto();
if (r2s.getNewArcs().hasNext()) ap2 = ((RouteElementArc)(r2s.getNewArcs().next())).getArcProto();
if (r2e.getNewArcs().hasNext()) ap2 = ((RouteElementArc)(r2e.getNewArcs().next())).getArcProto();
if (ap1 == null || ap2 == null) return 0;
return ap1.compareTo(ap2);
}
// get the first route in proper order
NodeInst n1s = r1s.getPortInst().getNodeInst();
NodeInst n1e = r1e.getPortInst().getNodeInst();
if (n1s.compareTo(n1e) < 0)
{
NodeInst s = n1s; n1s = n1e; n1e = s;
RouteElementPort se = r1s; r1s = r1e; r1e = se;
}
// get the second route in proper order
NodeInst n2s = r2s.getPortInst().getNodeInst();
NodeInst n2e = r2e.getPortInst().getNodeInst();
if (n2s.compareTo(n2e) < 0)
{
NodeInst s = n2s; n2s = n2e; n2e = s;
RouteElementPort se = r2s; r2s = r2e; r2e = se;
}
// sort by the starting and ending nodes
int res = n1s.compareTo(n2s);
if (res != 0) return res;
res = n1e.compareTo(n2e);
if (res != 0) return res;
// sort by the starting and ending port names
res = r1s.getPortInst().getPortProto().getName().compareTo(r2s.getPortInst().getPortProto().getName());
if (res != 0) return res;
res = r1e.getPortInst().getPortProto().getName().compareTo(r2e.getPortInst().getPortProto().getName());
if (res != 0) return res;
return 0;
}
}
/**
* Class to handle complex topology in the cell.
* Accounts for existing as well as planned connections.
*/
private static class StitchingTopology
{
private Netlist netlist;
private Map<Network,Network> connected;
StitchingTopology(Cell cell)
{
netlist = cell.getNetlist();
if (netlist == null)
{
System.out.println("Auto-router cannot get netlist information for cell " + cell.describe(false));
}
connected = new HashMap<Network,Network>();
}
/**
* Method to return the Network associated with a given node/port combination.
* @param ni the NodeInst in question.
* @param pp the PortProto on the NodeInst in question.
* @return the Network associated with that node/port.
*/
Network getNodeNetwork(NodeInst ni, PortProto pp)
{
Network net = netlist.getNetwork(ni, pp, 0);
return getRealNet(net);
}
/**
* Method to return the Network associated with a given PortInst.
* @param pi the PortInst in question.
* @return the Network associated with that PortInst.
*/
Network getPortNetwork(PortInst pi)
{
Network net = netlist.getNetwork(pi);
return getRealNet(net);
}
/**
* Method to return the Network associated with a given ArcInst.
* @param ai the ArcInst in question.
* @return the Network associated with that ArcInst.
*/
Network getArcNetwork(ArcInst ai)
{
Network net = netlist.getNetwork(ai, 0);
return getRealNet(net);
}
/**
* Method to tell whether two ports on a node are connected.
* @param ni the NodeInst in question.
* @param pp1 the first PortProto on that NodeInst.
* @param pp2 the first PortProto on that NodeInst.
* @return true if the ports are connected.
*/
boolean portsConnected(NodeInst ni, PortProto pp1, PortProto pp2)
{
return netlist.portsConnected(ni, pp1, pp2);
}
/**
* Method to convert a Network to the actual one, once intended connections are made.
* @param net the original Network.
* @return the actual Network, for comparison purposes.
*/
private Network getRealNet(Network net)
{
for(;;)
{
Network nextNet = connected.get(net);
if (nextNet == null) return net;
net = nextNet;
}
}
/**
* Method to plan for the connection of two Networks.
* @param net1 the first Network that will be connected.
* @param net2 the second Network that will be connected.
*/
void connect(Network net1, Network net2)
{
Network conNet1 = connected.get(net1);
Network conNet2 = connected.get(net2);
// if both nets are unknown, link one to the other
if (conNet1 == null && conNet2 == null)
{
connected.put(net1, net2);
return;
}
// if one net is unknown, link it to the known network
if (conNet1 == null)
{
connected.put(net1, conNet2);
return;
}
if (conNet2 == null)
{
connected.put(net2, conNet1);
return;
}
// if both nets are known, link them
connected.put(net2, conNet1);
}
}
/**
* Class to package Preferences for the server.
*/
public static class AutoOptions implements Serializable
{
public boolean createExports;
public ArcProto preferredArc;
public boolean fatWires = true;
public AutoOptions()
{
createExports = false;
preferredArc = Technology.getCurrent().getArcs().next();
}
public void initFromUserDefaults()
{
createExports = Routing.isAutoStitchCreateExports();
preferredArc = Routing.getPreferredRoutingArcProto();
fatWires = EditingPreferences.getThreadEditingPreferences().fatWires;
}
}
}