Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/* -*- 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, Oracle and/or its affiliates. All rights reserved.
*
* Electric(tm) is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* Electric(tm) is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Electric(tm); see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, Mass 02111-1307, USA.
*/
package com.sun.electric.tool.routing;
import java.awt.geom.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;
import com.sun.electric.database.EditingPreferences;
import com.sun.electric.database.geometry.EPoint;
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.Name;
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 com.sun.electric.util.TextUtils;
import com.sun.electric.util.math.DBMath;
import com.sun.electric.util.math.ECoord;
import com.sun.electric.util.math.EDimension;
import com.sun.electric.util.math.FixpRectangle;
import com.sun.electric.util.math.FixpTransform;
import com.sun.electric.util.math.GenMath;
/**
* 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;
/** default sizes and text descriptors */ private final EditingPreferences ep;
/** list of all routes to be created at end of analysis */ private List allRoutes;
/** list of pins that may be inline pins due to created arcs */ private List possibleInlinePins;
/** cache of true pin sizes */ private Map truePinSize;
/** set of nodes to check (prevents duplicate checks) */ private Set nodeMark;
/** edge alignment for arcs */ private EDimension 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 nodesToStitch = null;
List arcsToStitch = null;
Rectangle2D limitBound = null;
if (highlighted)
{
nodesToStitch = new ArrayList();
arcsToStitch = new ArrayList();
EditWindow_ wnd = ui.getCurrentEditWindow_();
if (wnd == null) return;
List 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 preferred 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 nodesToStitch;
private List arcsToStitch;
private double lX, hX, lY, hY;
private boolean forced;
private AutoOptions prefs;
private EDimension alignment;
private AutoStitchJob(Cell cell, List nodesToStitch, List 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(false);
ECoord resolution = cell.getTechnology().getFactoryResolution();
alignment = new EDimension(resolution, resolution);
startJob();
}
@Override
public boolean doIt() throws JobException
{
Rectangle2D limitBound = null;
if (lX != hX && lY != hY)
limitBound = new Rectangle2D.Double(lX, lY, hX-lX, hY-lY);
EditingPreferences ep = getEditingPreferences();
runAutoStitch(cell, nodesToStitch, arcsToStitch, this, null, limitBound, forced, false, ep, prefs, false, alignment);
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 ignored).
* @param ep EditingPreferences
* @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 nodesToStitch, List arcsToStitch, Job job,
PolyMerge stayInside, Rectangle2D limitBound, boolean forced, boolean includePureLayerNodes,
EditingPreferences ep, AutoOptions prefs, boolean showProgress, EDimension 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(ep);
as.alignment = alignment;
as.includePureLayerNodes = includePureLayerNodes;
as.runNow(cell, nodesToStitch, arcsToStitch, job, stayInside, limitBound, forced, prefs, showProgress);
}
private AutoStitch(EditingPreferences ep)
{
this.ep = ep;
possibleInlinePins = new ArrayList();
truePinSize = new HashMap();
router = new SimpleWirer(ep);
}
private List getArcsToStitch(Cell cell, List arcsToStitch)
{
List newArcsToStitch = new ArrayList();
if (arcsToStitch == null)
{
for(Iterator it = cell.getArcs(); it.hasNext(); )
newArcsToStitch.add(it.next());
} else
{
for(ArcInst ai : arcsToStitch)
if (ai.isLinked()) newArcsToStitch.add(ai);
}
return newArcsToStitch;
}
private List getNodesToStitch(Cell cell, List nodesToStitch)
{
List newNodesToStitch = new ArrayList();
if (nodesToStitch == null)
{
// no data from highlighter
for(Iterator 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 origNodesToStitch, List 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 nodesToStitch = getNodesToStitch(cell, origNodesToStitch);
List arcsToStitch = getArcsToStitch(cell, origArcsToStitch);
// next mark nodes to be checked
nodeMark = new HashSet();
for(NodeInst ni : nodesToStitch)
nodeMark.add(ni);
Map arcsCreatedMap = new HashMap();
Map nodesCreatedMap = new HashMap();
allRoutes = new ArrayList();
// 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> overlapMap = new HashMap>();
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 polyConns = overlapMap.get(netID);
makeExport(polyConns);
}
// now run these arcs and reinitialize the list
makeConnections(showProgress, arcsCreatedMap, nodesCreatedMap, stayInside);
allRoutes = new ArrayList();
if (showProgress) Job.getUserInterface().setProgressNote("Initializing routing");
// reinitialize list of objects to examine
nodesToStitch = getNodesToStitch(cell, origNodesToStitch);
arcsToStitch = getArcsToStitch(cell, origArcsToStitch);
}
// next precompute bounds on all nodes in cell
Map nodePortBounds = new HashMap();
for(Iterator nIt = cell.getNodes(); nIt.hasNext(); )
{
NodeInst ni = nIt.next();
Rectangle2D niBounds = ni.getBounds();
ObjectQTree oqt = new ObjectQTree(niBounds);
for(Iterator it = ni.getPortInsts(); it.hasNext(); )
{
PortInst pi = it.next();
PortProto pp = pi.getPortProto();
PortOriginal fp = new PortOriginal(ni, pp);
FixpTransform 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: Cell " + cell.describe(false) + ", node " + ni.describe(false) +
", port " + pp.getName() + " could not be added to quad-tree (bounds are " +
bounds.getMinX() + "<=X<=" + bounds.getMaxX() + " and " +
bounds.getMinY() + "<=Y<=" + bounds.getMaxY() + ")");
}
nodePortBounds.put(ni, oqt);
}
// finally, initialize the information about which layer is smallest on each arc
Map arcLayers = new HashMap();
// 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, ep);
if (failure)
{
System.out.println("AUTO STITCHER FAILED TO MAKE DAISY-CHAIN ARC");
}
}
// reset for the rest of the analysis
allRoutes = new ArrayList();
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 pinsToPassThrough = new ArrayList();
for (NodeInst ni : possibleInlinePins)
{
if (ni.isInlinePin())
{
CircuitChangeJobs.Reconnect re = CircuitChangeJobs.Reconnect.erasePassThru(ni, false, true, ep);
if (re != null)
{
pinsToPassThrough.add(re);
}
}
}
if (pinsToPassThrough.size() > 0)
{
CircuitChangeJobs.CleanupChanges ccJob = new CircuitChangeJobs.CleanupChanges(cell, true, Collections.emptySet(),
pinsToPassThrough, new HashMap(), new ArrayList(), new HashSet(), 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 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 arcsCreatedMap,
Map 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 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, ep);
}
}
/****************************************** 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
{
@Override
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 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 daisyPoints = new ArrayList();
Rectangle2D searchBounds = ai.getBounds();
for(Iterator it = cell.searchIterator(searchBounds); it.hasNext(); )
{
Geometric geom = 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(), ep);
RouteElementPort tailRE = RouteElementPort.existingPortInst(ai.getTailPortInst(), ai.getTailLocation(), ep);
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, ep);
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 ******************************************/
//private void scanRTree(RTNode rTop)
//{
// for(int i=0; i nodePortBounds, Map 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 geomsInArea = new ArrayList();
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);
//if (DEBUGFORDIMA)
//{
// if (ni != null && ni.getName().equals("plnode@29"))
// {
// System.out.println("==== R-TREE TEST ====");
// for(Iterator it = cell.getNodes(); it.hasNext(); )
// {
// NodeInst n = it.next();
// if (n.getName().equals("plnode@29"))
// {
// Rectangle2D r = n.getBounds();
// System.out.println(" CELL HAS NODE "+n.describe(false)+" AT "+r.getMinX()+"<=X<="+
// r.getMaxX()+" AND "+r.getMinY()+"<=Y<="+r.getMaxY());
// }
// }
// RTNode rTop = cell.getTopology().getRTree();
// scanRTree(rTop);
// }
//}
for(Iterator it = cell.searchIterator(searchBounds); it.hasNext(); )
{
Geometric oGeom = 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 nodePortBounds,
Map 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
FixpTransform trans = ni.rotateOut();
NodeInst rNi = ni;
PortProto rPp = pp;
while (rNi.isCellInstance())
{
FixpTransform 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 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 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 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 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= 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;
FixpTransform 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= 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 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 true if connections were made.
*/
private boolean testPoly(NodeInst ni, PortProto pp, ArcProto ap, Poly poly, NodeInst oNi, StitchingTopology top,
Map nodePortBounds,
Map 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);
Name portNK = pp.getNameKey();
boolean connectionsMade = false;
// keep track of which networks we've already tied together
Set netsConnectedTo = new TreeSet();
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();
// port must be able to connect to the arc
if (!mPp.getBasePort().connectsTo(ap)) continue;
Network oNet = null;
Name oPortNK = mPp.getNameKey();
if (portNK.isBus() && oPortNK.isBus())
{
// both ports are busses: do not stitch if their sizes differ
//System.out.println("ROUTING BUS ARC "+ap.describe());
if (portNK.busWidth() != oPortNK.busWidth()) continue;
ap = Schematics.tech().bus_arc;
} else
{
// do not stitch where there is already an electrical connection
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 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
FixpTransform trans = oNi.rotateOut();
NodeInst rNi = oNi;
PortProto rPp = mPp;
while (rNi.isCellInstance())
{
FixpTransform 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)) 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);
if (comparePoly(oNi, mPp, oPoly, oNet, ni, pp, poly, net, ap, stayInside, top, limitBound))
connectionsMade = true;
} else
{
// a geometric primitive: look for ports on layers that touch
Netlist subNetlist = rNi.getParent().getNetlist();
for(int j=0; j 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 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 connectionsMade;
}
/**
* 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 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 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");
// remember the current ArcProto and force the desired one
ArcProto oldAP = User.getUserTool().getCurrentArcProto();
User.getUserTool().setCurrentArcProtoTemporarily(ap);
// make the connection
boolean didConnect = connectObjects(pi, net, opi, oNet, ni.getParent(), new Point2D.Double(x,y), stayInside, top);
// restore the current ArcProto
User.getUserTool().setCurrentArcProtoTemporarily(oldAP);
return didConnect;
}
/**
* 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, ep, 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> 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 geomsInArea = new ArrayList();
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 it = cell.searchIterator(searchBounds); it.hasNext(); )
{
Geometric oGeom = 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 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> 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 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 overlappingEdges = new ArrayList();
List 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 instance 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 sub-polygon is exported all the way to the top level of the var context.
* @param sp the sub-polygon
* @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 instance
* @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; }
@Override
public boolean enterCell(HierarchyEnumerator.CellInfo info) { return true; }
@Override
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 it = info.getCell().getArcs(); it.hasNext(); )
{
ArcInst ai = it.next();
ArcProto ap = ai.getProto();
FixpTransform arcTrans = info.getTransformToRoot();
Technology tech = ap.getTechnology();
Poly [] arcInstPolyList = tech.getShapeOfArc(ai);
for(int i=0; i= DBMath.getEpsilon()) continue;
int netIDglobal = gatherNetworks.getGlobalNetworkID(info.getContext(), ai.getHeadPortInst());
SubPolygon sp = new SubPolygon(poly, info.getContext(), netIDglobal, ai);
bestSubPolygon = sp;
}
}
} else
{
// look at all nodes and see if they intersect the arc
for(Iterator it = info.getCell().getNodes(); it.hasNext(); )
{
NodeInst ni = it.next();
if (ni.isCellInstance()) continue;
PrimitiveNode pNp = (PrimitiveNode)ni.getProto();
FixpTransform nodeTrans = ni.rotateOut(info.getTransformToRoot());
Technology tech = pNp.getTechnology();
Poly [] nodeInstPolyList = tech.getShapeOfNode(ni, true, true, null);
for(int i=0; i= DBMath.getEpsilon()) continue;
SubPolygon sp = new SubPolygon(poly, info.getContext(), netIDglobal, ni);
if (bestSubPolygon != null)
{
if (!ni.hasExports()) continue;
}
bestSubPolygon = sp;
}
}
}
}
@Override
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());
FixpTransform 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 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 location 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 noit = where.getPathIterator(); noit.hasNext(); )
{
Nodable no = noit.next();
if (!(no instanceof NodeInst)) break;
NodeInst niHier = (NodeInst)no;
FixpTransform 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 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, ep, center, pin.getDefWidth(ep), pin.getDefHeight(ep), cell);
Route route = router.planRoute(cell, pinNi.getOnlyPortInst(), pi, center, null, ep, false, false, null, null);
if (!Router.createRouteNoJob(route, cell, new HashMap(), new HashMap(), ep)) {
pi = pinNi.getOnlyPortInst();
} else {
if (pinNi != null) pinNi.kill(); // delete if route failed
}
}
}
}
}
if (!existingExportFound)
exportThis = Export.newInstance(cell, pi, exportName, ep);
}
ni = where.getNodable().getNodeInst();
where = where.pop();
FixpTransform 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 noit = sp.context.getPathIterator(); noit.hasNext(); )
{
NodeInst niHier = noit.next().getNodeInst();
FixpTransform 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 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;
}
}
}
// 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, ep, topLoc, pNp.getDefWidth(ep), pNp.getDefHeight(ep), cell);
// insert the pin into the arc
ArcInst newAi1 = ArcInst.makeInstanceBase(ap, ep, width, lowAI.getHeadPortInst(), ni.getOnlyPortInst(), lowAI.getHeadLocation(), topLoc, null);
ArcInst newAi2 = ArcInst.makeInstanceBase(ap, ep, 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, ep);
newAi1.copyTextDescriptorFrom(lowAI, ArcInst.ARC_NAME);
} else
{
newAi2.setName(arcName, ep);
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 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;
SubPolygon(PolyBase poly, VarContext context, int netID, Geometric theObj)
{
this.poly = poly;
this.context = context;
this.netID = netID;
this.theObj = theObj;
}
@Override
public FixpRectangle 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> 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);
FixpTransform trans = ni1.rotateOut();
for(int i=0; i polysFound = cpv.getFoundConnections();
// add to map of things that overlap should be connected
for (PolyConnection p : polysFound) {
registerPoly(overlapMap, p);
}
}
/**
* HierarchyEnumerator subclass to check all geometry in a cell against polygons that may intersect.
*/
private class CheckPolygonVisitor extends HierarchyEnumerator.Visitor
{
private RTNode rtree;
private Rectangle2D intersectArea;
private NodeInst cellOfInterest;
private List 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();
}
public List getFoundConnections() { return connectionsFound; }
@Override
public boolean enterCell(HierarchyEnumerator.CellInfo info) { return true; }
@Override
public void exitCell(HierarchyEnumerator.CellInfo info)
{
FixpTransform toTop = info.getTransformToRoot();
// check all nodes against the list
for(Iterator it = info.getCell().getNodes(); it.hasNext(); )
{
NodeInst ni = it.next();
if (ni.isCellInstance()) continue;
FixpTransform nodeTrans = ni.rotateOut(toTop);
Technology tech = ni.getProto().getTechnology();
Poly[] polys = tech.getShapeOfNode(ni, true, true, null);
for(int i=0; i sea = new RTNode.Search(poly.getBounds2D(), rtree, true); sea.hasNext(); )
{
SubPolygon sp = 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);
addConnection(sp, sp2);
}
}
}
// check all arcs against the list
for(Iterator 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 sea = new RTNode.Search(poly.getBounds2D(), rtree, true); sea.hasNext(); )
{
SubPolygon sp = 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);
addConnection(sp, sp2);
}
}
}
}
@Override
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());
FixpTransform 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; }
@Override
public boolean enterCell(HierarchyEnumerator.CellInfo info) { return true; }
@Override
public void exitCell(HierarchyEnumerator.CellInfo info)
{
FixpTransform toTop = info.getTransformToRoot();
// add all nodes to the list
for(Iterator it = info.getCell().getNodes(); it.hasNext(); )
{
NodeInst ni = it.next();
if (ni.isCellInstance()) continue;
FixpTransform nodeTrans = ni.rotateOut(toTop);
Technology tech = ni.getProto().getTechnology();
Poly[] polys = tech.getShapeOfNode(ni, true, true, null);
for(int i=0; i 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> 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 polys = overlapMap.get(key);
if (polys == null) {
polys = new ArrayList();
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> networkToNetID = new HashMap>();
@Override
public boolean enterCell(HierarchyEnumerator.CellInfo info) {
return true;
}
@Override
public void exitCell(HierarchyEnumerator.CellInfo info) {
Cell cell = info.getCell();
VarContext context = info.getContext();
String key = context.getInstPath(".");
Map netIdMap = networkToNetID.get(key);
if (netIdMap == null) {
netIdMap = new HashMap();
networkToNetID.put(key, netIdMap);
}
Netlist netlist = info.getNetlist();
for (Iterator it = cell.getNodes(); it.hasNext(); ) {
NodeInst ni = it.next();
for (Iterator 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));
}
}
}
@Override
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 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 auto-router 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 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());
FixpTransform 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 arcLayers)
{
// quit if the value has already been computed
if (arcLayers.get(ap) != null) return;
// find the smallest layer
Layer smallestLayer = ap.getLayer(0);
ECoord smallestExtend = ap.getLayerExtend(0);
for (int arcLayer = 1; arcLayer < ap.getNumArcLayers(); arcLayer++) {
ECoord extend = ap.getLayerExtend(arcLayer);
if (extend.compareTo(smallestExtend) < 0) {
smallestLayer = ap.getLayer(arcLayer);
smallestExtend = extend;
}
}
arcLayers.put(ap, smallestLayer);
}
/**
* Class to sort Routes.
*/
private static class CompRoutes implements Comparator
{
@Override
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 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();
}
/**
* 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 AutoOptions(boolean factory)
{
if (factory)
{
createExports = false;
preferredArc = Technology.getCurrent().getArcs().next();
} else
{
createExports = Routing.isAutoStitchCreateExports();
preferredArc = Routing.getPreferredRoutingArcProto();
}
}
}
}