/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: ERCWellCheck.java
*
* Copyright (c) 2004 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.erc;
import com.sun.electric.Main;
import com.sun.electric.database.geometry.DBMath;
import com.sun.electric.database.geometry.EPoint;
import com.sun.electric.database.geometry.GeometryHandler;
import com.sun.electric.database.geometry.Poly;
import com.sun.electric.database.geometry.PolyBase;
import com.sun.electric.database.geometry.GenMath.MutableBoolean;
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.hierarchy.View;
import com.sun.electric.database.network.Netlist;
import com.sun.electric.database.network.Network;
import com.sun.electric.database.text.PrefPackage;
import com.sun.electric.database.text.TextUtils;
import com.sun.electric.database.topology.ArcInst;
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.UserInterface;
import com.sun.electric.database.variable.VarContext;
import com.sun.electric.technology.ArcProto;
import com.sun.electric.technology.DRCTemplate;
import com.sun.electric.technology.Layer;
import com.sun.electric.technology.PrimitiveNode;
import com.sun.electric.technology.Technology;
import com.sun.electric.technology.Technology.NodeLayer;
import com.sun.electric.tool.Job;
import com.sun.electric.tool.JobException;
import com.sun.electric.tool.drc.DRC;
import com.sun.electric.tool.user.ErrorLogger;
import com.sun.electric.tool.user.Highlighter;
import com.sun.electric.tool.user.dialogs.EModelessDialog;
import com.sun.electric.tool.user.ui.EditWindow;
import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Collection;
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.concurrent.Semaphore;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.Timer;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
/**
* This is the Electrical Rule Checker tool.
* @author Steve Rubin, Gilda Garreton
*/
public class ERCWellCheck
{
private Cell cell;
private Set<Object> possiblePrimitives;
private List<WellCon> wellCons = new ArrayList<WellCon>();
private Iterator<WellCon> [] wellConIterator;
private List<WellCon> [] wellConLists;
private RTNode pWellRoot, nWellRoot;
private Layer pWellLayer, nWellLayer;
private ErrorLogger errorLogger;
private WellCheckJob job;
private double worstPWellDist;
private Point2D worstPWellCon;
private Point2D worstPWellEdge;
private double worstNWellDist;
private Point2D worstNWellCon;
private Point2D worstNWellEdge;
private WellCheckPreferences wellPrefs;
private static final boolean GATHERSTATISTICS = false;
private static final boolean DISTANTSEEDS = true;
private static final boolean INCREMENTALGROWTH = false;
public static class WellCheckPreferences extends PrefPackage
{
private static final long serialVersionUID = 1L;
private static final String PREF_NODE = "tool/erc";
/**
* Whether ERC should do well analysis using multiple processors.
* The default is "true".
*/
@BooleanPref(node=PREF_NODE, key="ParallelWellAnalysis", factory=true)
public boolean parallelWellAnalysis;
/**
* The number of processors to use in ERC well analysis.
* The default is "0" (as many as there are).
*/
@IntegerPref(node=PREF_NODE, key="WellAnalysisNumProc", factory=0)
public int maxProc;
/**
* Whether ERC should check that all P-Well contacts connect to ground.
* The default is "true".
*/
@BooleanPref(node=PREF_NODE, key="MustConnectPWellToGround", factory=true)
public boolean mustConnectPWellToGround;
/**
* Whether ERC should check that all N-Well contacts connect to power.
* The default is "true".
*/
@BooleanPref(node=PREF_NODE, key="MustConnectNWellToPower", factory=true)
public boolean mustConnectNWellToPower;
/**
* How much P-Well contact checking the ERC should do.
* The values are:
* <UL>
* <LI>0: must have a contact in every well area.</LI>
* <LI>1: must have at least one contact.</LI>
* <LI>2: do not check for contact presence.</LI>
* </UL>
* The default is "0".
*/
@IntegerPref(node=PREF_NODE, key="PWellCheck", factory=0)
public int pWellCheck;
/**
* How much N-Well contact checking the ERC should do.
* The values are:
* <UL>
* <LI>0: must have a contact in every well area.</LI>
* <LI>1: must have at least one contact.</LI>
* <LI>2: do not check for contact presence.</LI>
* </UL>
* The default is "0".
*/
@IntegerPref(node=PREF_NODE, key="NWellCheck", factory=0)
public int nWellCheck;
/**
* Whether ERC should check DRC Spacing condition.
* The default is "false".
*/
@BooleanPref(node=PREF_NODE, key="DRCCheckInERC", factory=false)
public boolean drcCheck;
/**
* Whether ERC should find the contact that is farthest from the well edge.
* The default is "false".
*/
@BooleanPref(node=PREF_NODE, key="FindWorstCaseWell", factory=false)
public boolean findWorstCaseWell;
public WellCheckPreferences(boolean factory)
{
super(factory);
}
}
/**
* Method to analyze the current Cell for well errors.
*/
public static void analyzeCurCell(GeometryHandler.GHMode newAlgorithm)
{
UserInterface ui = Job.getUserInterface();
Cell curCell = ui.needCurrentCell();
if (curCell == null) return;
View view = curCell.getView();
if (view.isTextView() || view == View.SCHEMATIC || view == View.ICON)
{
System.out.println("Sorry, Well checking runs only on layout cells");
return;
}
new WellCheckJob(curCell, newAlgorithm, new WellCheckPreferences(false));
}
/**
* Method used by the regressions.
* @param cell the Cell to well-check.
* @param newAlgorithm the geometry algorithm to use.
* @return the success of running well-check.
*/
public static int checkERCWell(Cell cell, GeometryHandler.GHMode newAlgorithm, WellCheckPreferences wellPrefs)
{
ERCWellCheck check = new ERCWellCheck(cell, null, newAlgorithm, wellPrefs);
return check.runNow();
}
private ERCWellCheck(Cell cell, WellCheckJob job, GeometryHandler.GHMode newAlgorithm, WellCheckPreferences wellPrefs)
{
this.job = job;
this.mode = newAlgorithm;
this.cell = cell;
this.wellPrefs = wellPrefs;
}
private static class WellCheckJob extends Job
{
private static final long serialVersionUID = 1L;
private Cell cell;
private GeometryHandler.GHMode newAlgorithm;
private double worstPWellDist, worstNWellDist;
private EPoint worstPWellCon, worstPWellEdge;
private EPoint worstNWellCon, worstNWellEdge;
private WellCheckPreferences wellPrefs;
private WellCheckJob(Cell cell, GeometryHandler.GHMode newAlgorithm, WellCheckPreferences wellPrefs)
{
super("ERC Well Check on " + cell, ERC.tool, Job.Type.SERVER_EXAMINE, null, null, Job.Priority.USER);
this.cell = cell;
this.newAlgorithm = newAlgorithm;
this.wellPrefs = wellPrefs;
startJob();
}
public boolean doIt() throws JobException
{
ERCWellCheck check = new ERCWellCheck(cell, this, newAlgorithm, wellPrefs);
check.runNow();
worstPWellDist = check.worstPWellDist;
fieldVariableChanged("worstPWellDist");
worstNWellDist = check.worstNWellDist;
fieldVariableChanged("worstNWellDist");
if (check.worstPWellCon != null)
{
worstPWellCon = new EPoint(check.worstPWellCon.getX(), check.worstPWellCon.getY());
fieldVariableChanged("worstPWellCon");
}
if (check.worstPWellEdge != null)
{
worstPWellEdge = new EPoint(check.worstPWellEdge.getX(), check.worstPWellEdge.getY());
fieldVariableChanged("worstPWellEdge");
}
if (check.worstNWellCon != null)
{
worstNWellCon = new EPoint(check.worstNWellCon.getX(), check.worstNWellCon.getY());
fieldVariableChanged("worstNWellCon");
}
if (check.worstNWellEdge != null)
{
worstNWellEdge = new EPoint(check.worstNWellEdge.getX(), check.worstNWellEdge.getY());
fieldVariableChanged("worstNWellEdge");
}
return true;
}
public void terminateOK()
{
// show the farthest distance from a well contact
UserInterface ui = Job.getUserInterface();
EditWindow_ wnd = ui.getCurrentEditWindow_();
if (wnd != null && (worstPWellDist > 0 || worstNWellDist > 0))
{
wnd.clearHighlighting();
if (worstPWellDist > 0)
{
wnd.addHighlightLine(worstPWellCon, worstPWellEdge, cell, false, false);
System.out.println("Farthest distance from a P-Well contact is " + worstPWellDist);
}
if (worstNWellDist > 0)
{
wnd.addHighlightLine(worstNWellCon, worstNWellEdge, cell, false, false);
System.out.println("Farthest distance from an N-Well contact is " + worstNWellDist);
}
wnd.finishedHighlighting();
}
}
}
private int runNow()
{
System.out.println("Checking Wells and Substrates in '" + cell.libDescribe() + "' ...");
long startTime = System.currentTimeMillis();
errorLogger = ErrorLogger.newInstance("ERC Well Check ");
initStatistics();
// make a list of primtivies that need to be examined
possiblePrimitives = new HashSet<Object>();
for(Iterator<Technology> it = Technology.getTechnologies(); it.hasNext(); )
{
Technology tech = it.next();
for(Iterator<PrimitiveNode> pIt = tech.getNodes(); pIt.hasNext(); )
{
PrimitiveNode pn = pIt.next();
NodeLayer [] nl = pn.getNodeLayers();
for(int i=0; i<nl.length; i++)
{
Layer lay = nl[i].getLayer();
if (lay.getFunction().isSubstrate())
{
possiblePrimitives.add(pn);
break;
}
}
}
for(Iterator<ArcProto> pIt = tech.getArcs(); pIt.hasNext(); )
{
ArcProto ap = pIt.next();
for(int i=0; i<ap.getNumArcLayers(); i++)
{
Layer lay = ap.getLayer(i);
if (lay.getFunction().isSubstrate())
{
if (lay.getFunction().isWell())
pWellLayer = lay; else
nWellLayer = lay;
possiblePrimitives.add(ap);
break;
}
}
}
}
// int errorCount = doOldWay();
int errorCount = doNewWay();
showStatistics();
// report the number of errors found
long endTime = System.currentTimeMillis();
if (errorCount == 0)
{
System.out.println("No Well errors found (took " + TextUtils.getElapsedTime(endTime - startTime) + ")");
} else
{
System.out.println("FOUND " + errorCount + " WELL ERRORS (took " + TextUtils.getElapsedTime(endTime - startTime) + ")");
}
return errorCount;
}
private WellCon getNextWellCon(int threadIndex)
{
synchronized(wellConIterator[threadIndex])
{
while (wellConIterator[threadIndex].hasNext())
{
WellCon wc = wellConIterator[threadIndex].next();
if (wc.wellNum == null) return wc;
}
}
// not found in this list: try the others
int numLists = wellConIterator.length;
for(int i=1; i<numLists; i++)
{
int otherList = (threadIndex + i) % numLists;
synchronized(wellConIterator[otherList])
{
while (wellConIterator[otherList].hasNext())
{
WellCon wc = wellConIterator[otherList].next();
if (wc.wellNum == null) return wc;
}
}
}
return null;
}
private void spreadSeeds(int threadIndex)
{
for(;;)
{
WellCon wc = getNextWellCon(threadIndex);
if (wc == null) break;
// see if this contact is in a marked well
Rectangle2D searchArea = new Rectangle2D.Double(wc.ctr.getX(), wc.ctr.getY(), 0, 0);
RTNode topSearch = nWellRoot;
if (canBeSubstrateTap(wc.fun)) topSearch = pWellRoot;
boolean geomFound = false;
for(RTNode.Search sea = new RTNode.Search(searchArea, topSearch, true); sea.hasNext(); )
{
WellBound wb = (WellBound)sea.next();
geomFound = true;
wc.wellNum = wb.netID;
if (wc.wellNum != null) break;
}
if (wc.wellNum != null) continue;
// mark it and spread the value
wc.wellNum = new NetValues();
// if nothing to spread, give an error
if (!geomFound)
{
String errorMsg = "N-Well contact is floating";
if (canBeSubstrateTap(wc.fun)) errorMsg = "P-Well contact is floating";
errorLogger.logError(errorMsg, new EPoint(wc.ctr.getX(), wc.ctr.getY()), cell, 0);
continue;
}
// spread out through the well area
spreadWellSeed(wc.ctr.getX(), wc.ctr.getY(), wc.wellNum, topSearch, threadIndex);
}
}
private class SpreadInThread extends Thread
{
private Semaphore whenDone;
private int threadIndex;
public SpreadInThread(String name, Semaphore whenDone, int index)
{
super(name);
this.whenDone = whenDone;
threadIndex = index;
start();
}
public void run()
{
spreadSeeds(threadIndex);
whenDone.release();
}
}
private int doNewWay()
{
pWellRoot = RTNode.makeTopLevel();
nWellRoot = RTNode.makeTopLevel();
// enumerate the hierarchy below here
long startTime = System.currentTimeMillis();
NewWellCheckVisitor wcVisitor = new NewWellCheckVisitor();
HierarchyEnumerator.enumerateCell(cell, VarContext.globalContext, wcVisitor);
int numPRects = getTreeSize(pWellRoot);
int numNRects = getTreeSize(nWellRoot);
long endTime = System.currentTimeMillis();
System.out.println(" Geometry collection found " + (numPRects + numNRects) + " well pieces, took " +
TextUtils.getElapsedTime(endTime - startTime));
startTime = endTime;
// determine the number of threads to use
int numberOfThreads = 1;
if (wellPrefs.parallelWellAnalysis) numberOfThreads = Runtime.getRuntime().availableProcessors();
if (numberOfThreads > 1)
{
int maxProc = wellPrefs.maxProc;
if (maxProc > 0) numberOfThreads = maxProc;
}
// make arrays of well contacts clustered for each processor
assignWellContacts(numberOfThreads);
// analyze the contacts
NetValues.reset();
if (numberOfThreads <= 1) spreadSeeds(0); else
{
Semaphore outSem = new Semaphore(0);
for(int i=0; i<numberOfThreads; i++)
new SpreadInThread("WellCheck propagate #" + (i+1), outSem, i);
// now wait for spread threads to finish
outSem.acquireUninterruptibly(numberOfThreads);
}
endTime = System.currentTimeMillis();
String msg = " Geometry analysis ";
if (numberOfThreads > 1) msg += "used " + numberOfThreads + " threads and ";
msg += "took ";
System.out.println(msg + TextUtils.getElapsedTime(endTime - startTime));
startTime = endTime;
// look for short-circuits
Map<Integer,WellCon> wellContacts = new HashMap<Integer,WellCon>();
Map<Integer,Set<Integer>> wellShorts = new HashMap<Integer,Set<Integer>>();
for(WellCon wc : wellCons)
{
Integer wellIndex = new Integer(wc.wellNum.getIndex());
WellCon other = wellContacts.get(wellIndex);
if (other == null) wellContacts.put(wellIndex, wc); else
{
if (wc.netNum != other.netNum && wc.ni != other.ni)
{
Integer wcNetNum = new Integer(wc.netNum);
Set<Integer> shortsInWC = wellShorts.get(wcNetNum);
if (shortsInWC == null)
{
shortsInWC = new HashSet<Integer>();
wellShorts.put(wcNetNum, shortsInWC);
}
Integer otherNetNum = new Integer(other.netNum);
Set<Integer> shortsInOther = wellShorts.get(otherNetNum);
if (shortsInOther == null)
{
shortsInOther = new HashSet<Integer>();
wellShorts.put(otherNetNum, shortsInOther);
}
// give error if not seen before
if (!shortsInWC.contains(otherNetNum))
{
List<EPoint> pointList = new ArrayList<EPoint>();
pointList.add(new EPoint(wc.ctr.getX(), wc.ctr.getY()));
pointList.add(new EPoint(other.ctr.getX(), other.ctr.getY()));
errorLogger.logMessage("Short circuit between well contacts", pointList, cell, 0, true);
shortsInWC.add(otherNetNum);
shortsInOther.add(wcNetNum);
}
}
}
}
// more analysis
boolean hasPCon = false, hasNCon = false;
for(WellCon wc : wellCons)
{
if (canBeSubstrateTap(wc.fun))
hasPCon = true;
else
hasNCon = true;
if (!wc.onProperRail)
{
if (canBeSubstrateTap(wc.fun))
{
if (wellPrefs.mustConnectPWellToGround)
{
errorLogger.logError("P-Well contact '" + wc.ni.getName() + "' not connected to ground", new EPoint(wc.ctr.getX(), wc.ctr.getY()), cell, 0);
}
} else
{
if (wellPrefs.mustConnectNWellToPower)
{
errorLogger.logError("N-Well contact '" + wc.ni.getName() + "' not connected to power", new EPoint(wc.ctr.getX(), wc.ctr.getY()), cell, 0);
}
}
}
}
// look for unconnected well areas
if (wellPrefs.pWellCheck != 2) findUnconnected(pWellRoot, pWellRoot, "P");
if (wellPrefs.nWellCheck != 2) findUnconnected(nWellRoot, nWellRoot, "N");
if (wellPrefs.pWellCheck == 1 && !hasPCon)
{
errorLogger.logError("No P-Well contact found in this cell", cell, 0);
}
if (wellPrefs.nWellCheck == 1 && !hasNCon)
{
errorLogger.logError("No N-Well contact found in this cell", cell, 0);
}
endTime = System.currentTimeMillis();
System.out.println(" Additional analysis took " + TextUtils.getElapsedTime(endTime - startTime));
startTime = endTime;
// make sure the wells are separated properly
// Local storage of rules.. otherwise getSpacingRule is called too many times
// THIS IS A DRC JOB .. not efficient if done here.
if (wellPrefs.drcCheck)
{
DRCTemplate pRule = DRC.getSpacingRule(pWellLayer, null, pWellLayer, null, false, -1, 0, 0);
DRCTemplate nRule = DRC.getSpacingRule(nWellLayer, null, nWellLayer, null, false, -1, 0, 0);
if (pRule != null) findDRCViolations(pWellRoot, pRule.getValue(0));
if (nRule != null) findDRCViolations(nWellRoot, nRule.getValue(0));
endTime = System.currentTimeMillis();
System.out.println(" Design rule check took " + TextUtils.getElapsedTime(endTime - startTime));
startTime = endTime;
}
// compute edge distance if requested
if (wellPrefs.findWorstCaseWell)
{
worstPWellDist = 0;
worstPWellCon = null;
worstPWellEdge = null;
worstNWellDist = 0;
worstNWellCon = null;
worstNWellEdge = null;
Map<Integer,WellNet> wellNets = new HashMap<Integer,WellNet>();
for(WellCon wc : wellCons)
{
if (wc.wellNum == null) continue;
Integer netNUM = new Integer(wc.wellNum.getIndex());
WellNet wn = wellNets.get(netNUM);
if (wn == null)
{
wn = new WellNet();
wn.pointsOnNet = new ArrayList<Point2D>();
wn.contactsOnNet = new ArrayList<WellCon>();
wn.fun = wc.fun;
wellNets.put(netNUM, wn);
}
wn.contactsOnNet.add(wc);
}
findWellNetPoints(pWellRoot, wellNets);
findWellNetPoints(nWellRoot, wellNets);
for(Integer netNUM : wellNets.keySet())
{
WellNet wn = wellNets.get(netNUM);
for(Point2D pt : wn.pointsOnNet)
{
// find contact closest to this point
double closestDist = Double.MAX_VALUE;
Point2D closestCon = null;
for(WellCon wc : wn.contactsOnNet)
{
double dist = wc.ctr.distance(pt);
if (dist < closestDist)
{
closestDist = dist;
closestCon = wc.ctr;
}
}
// see if this distance is worst for the well type
if (canBeSubstrateTap(wn.fun))
{
// pWell
if (closestDist > worstPWellDist)
{
worstPWellDist = closestDist;
worstPWellCon = closestCon;
worstPWellEdge = pt;
}
} else
{
// nWell
if (closestDist > worstNWellDist)
{
worstNWellDist = closestDist;
worstNWellCon = closestCon;
worstNWellEdge = pt;
}
}
}
}
endTime = System.currentTimeMillis();
System.out.println(" Worst-case distance analysis took " + TextUtils.getElapsedTime(endTime - startTime));
startTime = endTime;
}
errorLogger.termLogging(true);
int errorCount = errorLogger.getNumErrors();
return errorCount;
}
private void assignWellContacts(int numberOfThreads)
{
wellConIterator = new Iterator[numberOfThreads];
wellConLists = new List[numberOfThreads];
// load the lists
if (numberOfThreads == 1)
{
wellConLists[0] = wellCons;
} else
{
for(int i=0; i<numberOfThreads; i++)
wellConLists[i] = new ArrayList<WellCon>();
if (DISTANTSEEDS)
{
// the new way: cluster the well contacts together
Rectangle2D cellBounds = cell.getBounds();
Point2D ctr = new Point2D.Double(cellBounds.getCenterX(), cellBounds.getCenterY());
Point2D [] farPoints = new Point2D[numberOfThreads];
for(int i=0; i<numberOfThreads; i++)
{
double farthest = 0;
farPoints[i] = new Point2D.Double(0, 0);
for(WellCon wc : wellCons)
{
double dist = 0;
if (i == 0) dist += wc.ctr.distance(ctr); else
{
for(int j=0; j<i; j++)
dist += wc.ctr.distance(farPoints[j]);
}
if (dist > farthest)
{
farthest = dist;
farPoints[i].setLocation(wc.ctr);
}
}
}
// find the center of the cell
for(WellCon wc : wellCons)
{
double minDist = Double.MAX_VALUE;
int threadNum = 0;
for(int j=0; j<numberOfThreads; j++)
{
double dist = wc.ctr.distance(farPoints[j]);
if (dist < minDist)
{
minDist = dist;
threadNum = j;
}
}
wellConLists[threadNum].add(wc);
}
// for(int i=0; i<numberOfThreads; i++)
// Collections.sort(wellConLists[i], new SortWellCons(farPoints[i]));
} else
{
// old way where well contacts are analyzed in random order
for(int i=0; i<wellCons.size(); i++)
wellConLists[i%numberOfThreads].add(wellCons.get(i));
}
}
// create iterators over the lists
for(int i=0; i<numberOfThreads; i++)
wellConIterator[i] = wellConLists[i].iterator();
}
// /**
// * Comparator class for sorting Rectangle2D objects by their size.
// */
// private static class SortWellCons implements Comparator<WellCon>
// {
// private Point2D clusterPt;
//
// public SortWellCons(Point2D clusterPt)
// {
// super();
// this.clusterPt = clusterPt;
// }
//
// /**
// * Method to sort Rectangle2D objects by their size.
// */
// public int compare(WellCon wc1, WellCon wc2)
// {
// double dist1 = wc1.ctr.distance(clusterPt);
// double dist2 = wc2.ctr.distance(clusterPt);
// if (dist1 > dist2) return 1;
// if (dist1 < dist2) return -1;
// return 0;
// }
// }
private void findDRCViolations(RTNode rtree, double minDist)
{
for(int j=0; j<rtree.getTotal(); j++)
{
if (rtree.getFlag())
{
WellBound child = (WellBound)rtree.getChild(j);
if (child.netID == null) continue;
// look all around this geometry for others in the well area
Rectangle2D searchArea = new Rectangle2D.Double(child.bound.getMinX()-minDist, child.bound.getMinY()-minDist,
child.bound.getWidth()+minDist*2, child.bound.getHeight()+minDist*2);
for(RTNode.Search sea = new RTNode.Search(searchArea, rtree, true); sea.hasNext(); )
{
WellBound other = (WellBound)sea.next();
if (other.netID.getIndex() <= child.netID.getIndex()) continue;
if (child.bound.getMinX() > other.bound.getMaxX()+minDist ||
other.bound.getMinX() > child.bound.getMaxX()+minDist ||
child.bound.getMinY() > other.bound.getMaxY()+minDist ||
other.bound.getMinY() > child.bound.getMaxY()+minDist) continue;
PolyBase pb = new PolyBase(child.bound);
double trueDist = pb.polyDistance(other.bound);
if (trueDist < minDist)
{
List<PolyBase> polyList = new ArrayList<PolyBase>();
polyList.add(new PolyBase(child.bound));
polyList.add(new PolyBase(other.bound));
errorLogger.logMessage("Well areas too close (are " + TextUtils.formatDistance(trueDist) +
" but should be " + TextUtils.formatDistance(minDist) + " apart)", polyList, cell, 0, true);
}
}
} else
{
RTNode child = (RTNode)rtree.getChild(j);
findDRCViolations(child, minDist);
}
}
}
private int getTreeSize(RTNode rtree)
{
int total = 0;
if (rtree.getFlag()) total += rtree.getTotal(); else
{
for(int j=0; j<rtree.getTotal(); j++)
{
RTNode child = (RTNode)rtree.getChild(j);
total += getTreeSize(child);
}
}
return total;
}
private void findWellNetPoints(RTNode rtree, Map<Integer,WellNet> wellNets)
{
for(int j=0; j<rtree.getTotal(); j++)
{
if (rtree.getFlag())
{
WellBound child = (WellBound)rtree.getChild(j);
if (child.netID == null) continue;
Integer netNUM = new Integer(child.netID.getIndex());
WellNet wn = wellNets.get(netNUM);
if (wn == null) continue;
wn.pointsOnNet.add(new Point2D.Double(child.bound.getMinX(), child.bound.getMinY()));
wn.pointsOnNet.add(new Point2D.Double(child.bound.getMaxX(), child.bound.getMinY()));
wn.pointsOnNet.add(new Point2D.Double(child.bound.getMaxX(), child.bound.getMaxY()));
wn.pointsOnNet.add(new Point2D.Double(child.bound.getMinX(), child.bound.getMaxY()));
} else
{
RTNode child = (RTNode)rtree.getChild(j);
findWellNetPoints(child, wellNets);
}
}
}
private void findUnconnected(RTNode rtree, RTNode current, String title)
{
for(int j=0; j<current.getTotal(); j++)
{
if (current.getFlag())
{
WellBound child = (WellBound)current.getChild(j);
if (child.netID == null)
{
spreadWellSeed(child.bound.getCenterX(), child.bound.getCenterY(), new NetValues(), rtree, 0);
errorLogger.logError("No " + title + "-Well contact in this area",
new EPoint(child.bound.getCenterX(), child.bound.getCenterY()), cell, 0);
}
} else
{
RTNode child = (RTNode)current.getChild(j);
findUnconnected(rtree, child, title);
}
}
}
private void spreadWellSeed(double cX, double cY, NetValues wellNum, RTNode rtree, int threadIndex)
{
RTNode allFound = null;
Point2D ctr = new Point2D.Double(cX, cY);
Rectangle2D searchArea = new Rectangle2D.Double(cX, cY, 0, 0);
MutableBoolean keepSearching = new MutableBoolean(true);
Rectangle2D[] sides = new Rectangle2D[4];
for(int i=0; i<4; i++) sides[i] = new Rectangle2D.Double(0, 0, 0, 0);
int numSides = 1;
sides[0].setRect(searchArea);
while (keepSearching.booleanValue())
{
if (INCREMENTALGROWTH)
{
// grow the search area incrementally
double lX = sides[0].getMinX(), hX = sides[0].getMaxX();
double lY = sides[0].getMinY(), hY = sides[0].getMaxY();
for(int i=1; i<numSides; i++)
{
lX = Math.min(lX, sides[i].getMinX());
hX = Math.max(hX, sides[i].getMinX());
lY = Math.min(lY, sides[i].getMinY());
hY = Math.max(hY, sides[i].getMinY());
}
double newLX = lX, newHX = hX;
double newLY = lY, newHY = hY;
boolean anySearchesGood = false;
for(int i=0; i<numSides; i++)
{
allFound = searchInArea(sides[i], wellNum, rtree, allFound, ctr, keepSearching, threadIndex);
if (keepSearching.booleanValue()) anySearchesGood = true;
newLX = Math.min(newLX, sides[i].getMinX());
newHX = Math.max(newHX, sides[i].getMinX());
newLY = Math.min(newLY, sides[i].getMinY());
newHY = Math.max(newHY, sides[i].getMinY());
}
keepSearching.setValue(anySearchesGood);
// compute new bounds
numSides = 0;
if (newLX < lX) sides[numSides++].setRect(newLX, newLY, lX-newLX, newHY-newLY);
if (newHX > hX) sides[numSides++].setRect(hX, newLY, newHX-hX, newHY-newLY);
if (newLY < lY) sides[numSides++].setRect(newLX, newLY, newHX-newLX, lY-newLY);
if (newHY > hY) sides[numSides++].setRect(newLX, hY, newHX-newLX, newHY-hY);
} else
{
// just keep growing the search area
allFound = searchInArea(searchArea, wellNum, rtree, allFound, ctr, keepSearching, threadIndex);
}
}
}
private RTNode searchInArea(Rectangle2D searchArea, NetValues wellNum, RTNode rtree, RTNode allFound, Point2D ctr,
MutableBoolean keepSearching, int threadIndex)
{
keepSearching.setValue(false);
for(RTNode.Search sea = new RTNode.Search(searchArea, rtree, true); sea.hasNext(); )
{
WellBound wb = (WellBound)sea.next();
if (GATHERSTATISTICS) numObjSearches++;
// ignore if this object is already properly connected
if (wb.netID != null && wb.netID.getIndex() == wellNum.getIndex()) continue;
// see if this object actually touches something in the set
if (allFound == null)
{
// start from center of contact
if (!wb.bound.contains(ctr)) continue;
} else
{
boolean touches = false;
for(RTNode.Search subSea = new RTNode.Search(wb.bound, allFound, true); subSea.hasNext(); )
{
WellBound subWB = (WellBound)subSea.next();
if (DBMath.rectsIntersect(subWB.bound, wb.bound)) { touches = true; break; }
}
if (!touches) continue;
}
// this touches what is gathered so far: add to it
synchronized(wb)
{
if (wb.netID != null) wellNum.merge(wb.netID); else
wb.netID = wellNum;
}
if (GATHERSTATISTICS) wellBoundSearchOrder.add(new WellBoundRecord(wb, threadIndex));
// expand the next search area by this added bound
Rectangle2D.union(searchArea, wb.bound, searchArea);
if (allFound == null) allFound = RTNode.makeTopLevel();
allFound = RTNode.linkGeom(null, allFound, wb);
keepSearching.setValue(true);
}
return allFound;
}
private static class NetValues
{
private int index;
private static int indexValues;
static void reset()
{
indexValues = 0;
}
static synchronized int getFreeIndex()
{
return indexValues++;
}
public int getIndex() { return index; }
public NetValues()
{
index = getFreeIndex();
}
public synchronized void merge(NetValues other)
{
if (this.index == other.index) return;
other.index = this.index;
}
}
// well contacts
private static class WellCon
{
Point2D ctr;
int netNum;
NetValues wellNum;
boolean onProperRail;
PrimitiveNode.Function fun;
NodeInst ni;
}
/**
* Class to define an R-Tree leaf node for geometry in the blockage data structure.
*/
private static class WellBound implements RTBounds
{
private Rectangle2D bound;
private NetValues netID;
WellBound(Rectangle2D bound)
{
this.bound = bound;
this.netID = null;
}
public Rectangle2D getBounds() { return bound; }
public NetValues getNetID() { return netID; }
public String toString() { return "Well Bound on net " + netID.getIndex(); }
}
private static class NetRails
{
boolean onGround;
boolean onPower;
}
private static class WellNet
{
List<Point2D> pointsOnNet;
List<WellCon> contactsOnNet;
PrimitiveNode.Function fun;
}
/**
* Comparator class for sorting Rectangle2D objects by their size.
*/
private static class RectangleBySize implements Comparator<Rectangle2D>
{
/**
* Method to sort Rectangle2D objects by their size.
*/
public int compare(Rectangle2D b1, Rectangle2D b2)
{
double s1 = b1.getWidth() * b1.getHeight();
double s2 = b2.getWidth() * b2.getHeight();
if (s1 > s2) return -1;
if (s1 < s2) return 1;
return 0;
}
}
private class NewWellCheckVisitor extends HierarchyEnumerator.Visitor
{
private Map<Cell,List<Rectangle2D>> essentialPWell;
private Map<Cell,List<Rectangle2D>> essentialNWell;
private Map<Network,NetRails> networkCache;
public NewWellCheckVisitor()
{
essentialPWell = new HashMap<Cell,List<Rectangle2D>>();
essentialNWell = new HashMap<Cell,List<Rectangle2D>>();
networkCache = new HashMap<Network,NetRails>();
}
public boolean enterCell(HierarchyEnumerator.CellInfo info)
{
// Checking if job is scheduled for abort or already aborted
if (job != null && job.checkAbort()) return false;
// make sure essential well areas are gathered
Cell cell = info.getCell();
ensureCellCached(cell);
return true;
}
public void exitCell(HierarchyEnumerator.CellInfo info)
{
// Checking if job is scheduled for abort or already aborted
if (job != null && job.checkAbort()) return;
// get the object for merging all of the wells in this cell
Cell cell = info.getCell();
List<Rectangle2D> pWellsInCell = essentialPWell.get(cell);
List<Rectangle2D> nWellsInCell = essentialNWell.get(cell);
for(Rectangle2D b : pWellsInCell)
{
Rectangle2D bounds = new Rectangle2D.Double(b.getMinX(), b.getMinY(), b.getWidth(), b.getHeight());
DBMath.transformRect(bounds, info.getTransformToRoot());
pWellRoot = RTNode.linkGeom(null, pWellRoot, new WellBound(bounds));
}
for(Rectangle2D b : nWellsInCell)
{
Rectangle2D bounds = new Rectangle2D.Double(b.getMinX(), b.getMinY(), b.getWidth(), b.getHeight());
DBMath.transformRect(bounds, info.getTransformToRoot());
nWellRoot = RTNode.linkGeom(null, nWellRoot, new WellBound(bounds));
}
}
public boolean visitNodeInst(Nodable no, HierarchyEnumerator.CellInfo info)
{
NodeInst ni = no.getNodeInst();
// look for well and substrate contacts
PrimitiveNode.Function fun = ni.getFunction();
Netlist netList = info.getNetlist();
if (canBeSubstrateTap(fun) || canBeWellTap(fun))
{
// Allowing more than one port for well resistors.
for (Iterator<PortInst> pIt = ni.getPortInsts(); pIt.hasNext();)
{
// PortInst pi = ni.getOnlyPortInst();
PortInst pi = pIt.next();
WellCon wc = new WellCon();
wc.ctr = ni.getTrueCenter();
AffineTransform trans = ni.rotateOut();
trans.transform(wc.ctr, wc.ctr);
info.getTransformToRoot().transform(wc.ctr, wc.ctr);
wc.fun = fun;
Network net = netList.getNetwork(pi);
wc.onProperRail = false;
wc.wellNum = null;
wc.ni = ni;
if (net == null)
wc.netNum = -1;
else
{
wc.netNum = info.getNetID(net);
// PWell: must be on ground or NWell: must be on power
Network parentNet = net;
HierarchyEnumerator.CellInfo cinfo = info;
while (cinfo.getParentInst() != null) {
parentNet = cinfo.getNetworkInParent(parentNet);
cinfo = cinfo.getParentInfo();
}
if (parentNet != null)
{
NetRails nr = networkCache.get(parentNet);
if (nr == null)
{
nr = new NetRails();
networkCache.put(parentNet, nr);
for (Iterator<Export> it = parentNet.getExports(); it.hasNext(); )
{
Export exp = it.next();
if (exp.isGround()) nr.onGround = true;
if (exp.isPower()) nr.onPower = true;
}
}
boolean searchWell = (canBeSubstrateTap(fun));
if ((searchWell && nr.onGround) || (!searchWell && nr.onPower))
wc.onProperRail = true;
}
}
wellCons.add(wc);
}
}
return true;
}
private void ensureCellCached(Cell cell)
{
List<Rectangle2D> pWellsInCell = essentialPWell.get(cell);
List<Rectangle2D> nWellsInCell = essentialNWell.get(cell);
if (pWellsInCell == null)
{
// gather all wells in the cell
pWellsInCell = new ArrayList<Rectangle2D>();
nWellsInCell = new ArrayList<Rectangle2D>();
for(Iterator<NodeInst> it = cell.getNodes(); it.hasNext(); )
{
NodeInst ni = it.next();
if (ni.isCellInstance()) continue;
PrimitiveNode pn = (PrimitiveNode)ni.getProto();
if (!possiblePrimitives.contains(pn)) continue;
// Getting only ercLayers
Poly [] nodeInstPolyList = pn.getTechnology().getShapeOfNode(ni, true, true, ercLayers);
int tot = nodeInstPolyList.length;
for(int i=0; i<tot; i++)
{
Poly poly = nodeInstPolyList[i];
Layer layer = poly.getLayer();
AffineTransform trans = ni.rotateOut();
poly.transform(trans);
Rectangle2D bound = poly.getBounds2D();
int wellType = getWellLayerType(layer);
if (wellType == ERCPWell) pWellsInCell.add(bound);
else if (wellType == ERCNWell) nWellsInCell.add(bound);
}
}
for(Iterator<ArcInst> it = cell.getArcs(); it.hasNext(); )
{
ArcInst ai = it.next();
ArcProto ap = ai.getProto();
if (!possiblePrimitives.contains(ap)) continue;
// Getting only ercLayers
Poly [] arcInstPolyList = ap.getTechnology().getShapeOfArc(ai, ercLayers);
int tot = arcInstPolyList.length;
for(int i=0; i<tot; i++)
{
Poly poly = arcInstPolyList[i];
Layer layer = poly.getLayer();
Rectangle2D bound = poly.getBounds2D();
int wellType = getWellLayerType(layer);
if (wellType == ERCPWell) pWellsInCell.add(bound);
else if (wellType == ERCNWell) nWellsInCell.add(bound);
}
}
// eliminate duplicates
eliminateDuplicates(pWellsInCell);
eliminateDuplicates(nWellsInCell);
// save results
essentialPWell.put(cell, pWellsInCell);
essentialNWell.put(cell, nWellsInCell);
}
}
private void eliminateDuplicates(List<Rectangle2D> wbList)
{
// first sort by size
Collections.sort(wbList, new RectangleBySize());
for(int i=0; i<wbList.size(); i++)
{
Rectangle2D b = wbList.get(i);
for(int j=0; j<i; j++)
{
Rectangle2D prevB = wbList.get(j);
if (prevB.contains(b))
{
wbList.remove(i);
i--;
break;
}
}
}
}
}
private static final int ERCPWell = 1;
private static final int ERCNWell = 2;
private static final int ERCPSEUDO = 0;
private static final Layer.Function[] ercLayersArray = {
Layer.Function.WELLP, Layer.Function.WELL, Layer.Function.WELLN, Layer.Function.SUBSTRATE,
Layer.Function.IMPLANTP, Layer.Function.IMPLANT, Layer.Function.IMPLANTN};
private static final Layer.Function.Set ercLayers = new Layer.Function.Set(ercLayersArray);
/**
* Method to return nonzero if layer "layer" is a well/select layer.
* @return 1: P-Well, 2: N-Well
*/
private static int getWellLayerType(Layer layer)
{
Layer.Function fun = layer.getFunction();
if (layer.isPseudoLayer()) return ERCPSEUDO;
if (fun == Layer.Function.WELLP) return ERCPWell;
if (fun == Layer.Function.WELL || fun == Layer.Function.WELLN) return ERCNWell;
return ERCPSEUDO;
}
// variables for the "old way"
private GeometryHandler.GHMode mode;
private Set<Cell> doneCells = new HashSet<Cell>(); // Mark if cells are done already.
private Map<Cell,GeometryHandler> cellMerges = new HashMap<Cell,GeometryHandler>(); // make a map of merge information in each cell
// well areas
private static class WellArea
{
PolyBase poly;
int netNum;
int index;
}
private int doOldWay()
{
// enumerate the hierarchy below here
WellCheckVisitor wcVisitor = new WellCheckVisitor();
HierarchyEnumerator.enumerateCell(cell, VarContext.globalContext, wcVisitor);
// Checking if job is scheduled for abort or already aborted
if (job != null && job.checkAbort()) return 0;
// make a list of well and substrate areas
List<WellArea> wellAreas = new ArrayList<WellArea>();
int wellIndex = 0;
GeometryHandler topMerge = cellMerges.get(cell);
for (Layer layer : topMerge.getKeySet())
{
// Not sure if null goes here
Collection<PolyBase> set = topMerge.getObjects(layer, false, true);
for(PolyBase poly : set)
{
WellArea wa = new WellArea();
wa.poly = poly;
wa.poly.setLayer(layer);
wa.poly.setStyle(Poly.Type.FILLED);
wa.index = wellIndex++;
wellAreas.add(wa);
}
}
boolean foundPWell = false;
boolean foundNWell = false;
for(WellArea wa : wellAreas)
{
int wellType = getWellLayerType(wa.poly.getLayer());
if (wellType != ERCPWell && wellType != ERCNWell) continue;
// presume N-well
PrimitiveNode.Function desiredContact = PrimitiveNode.Function.SUBSTRATE;
String noContactError = "No N-Well contact found in this area";
int contactAction = wellPrefs.nWellCheck;
if (wellType == ERCPWell)
{
// P-well
desiredContact = PrimitiveNode.Function.WELL;
contactAction = wellPrefs.pWellCheck;
noContactError = "No P-Well contact found in this area";
foundPWell = true;
}
else
foundNWell = true;
// find a contact in the area
boolean found = false;
for(WellCon wc : wellCons)
{
if (wc.fun != desiredContact) continue;
if (!wa.poly.getBounds2D().contains(wc.ctr)) continue;
if (!wa.poly.contains(wc.ctr)) continue;
wa.netNum = wc.netNum;
found = true;
break;
}
// if no contact, issue appropriate errors
if (!found)
{
if (contactAction == 0)
{
errorLogger.logError(noContactError, wa.poly, cell, 0);
}
}
}
// make sure all of the contacts are on the same net
for(WellCon wc : wellCons)
{
// -1 means not connected in hierarchyEnumerator::numberNets()
if (wc.netNum == -1)
{
String errorMsg = "N-Well contact is floating";
if (canBeSubstrateTap(wc.fun)) errorMsg = "P-Well contact is floating";
errorLogger.logError(errorMsg, new EPoint(wc.ctr.getX(), wc.ctr.getY()), cell, 0);
continue;
}
if (!wc.onProperRail)
{
if (canBeSubstrateTap(wc.fun))
{
if (wellPrefs.mustConnectPWellToGround)
{
errorLogger.logError("P-Well contact not connected to ground", new EPoint(wc.ctr.getX(), wc.ctr.getY()), cell, 0);
}
} else
{
if (wellPrefs.mustConnectNWellToPower)
{
errorLogger.logError("N-Well contact not connected to power", new EPoint(wc.ctr.getX(), wc.ctr.getY()), cell, 0);
}
}
}
}
// if just 1 N-Well contact is needed, see if it is there
if (wellPrefs.nWellCheck == 1 && foundNWell)
{
boolean found = false;
for(WellCon wc : wellCons)
{
if (canBeWellTap(wc.fun)) { found = true; break; }
}
if (!found)
{
errorLogger.logError("No N-Well contact found in this cell", cell, 0);
}
}
// if just 1 P-Well contact is needed, see if it is there
if (wellPrefs.pWellCheck == 1 && foundPWell)
{
boolean found = false;
for(WellCon wc : wellCons)
{
if (canBeSubstrateTap(wc.fun)) { found = true; break; }
}
if (!found)
{
errorLogger.logError("No P-Well contact found in this cell", cell, 0);
}
}
// make sure the wells are separated properly
// Local storage of rules.. otherwise getSpacingRule is called too many times
// THIS IS A DRC JOB .. not efficient if done here.
if (wellPrefs.drcCheck)
{
Map<Layer,DRCTemplate> rulesCon = new HashMap<Layer,DRCTemplate>();
Map<Layer,DRCTemplate> rulesNonCon = new HashMap<Layer,DRCTemplate>();
for(WellArea wa : wellAreas)
{
for(WellArea oWa : wellAreas)
{
// Checking if job is scheduled for abort or already aborted
if (job != null && job.checkAbort()) return (0);
if (wa.index <= oWa.index) continue;
Layer waLayer = wa.poly.getLayer();
if (waLayer != oWa.poly.getLayer()) continue;
boolean con = false;
if (wa.netNum == oWa.netNum && wa.netNum >= 0) con = true;
DRCTemplate rule = (con)?
rulesCon.get(waLayer):
rulesNonCon.get(waLayer);
// @TODO Might still return NULL the first time!!. Need another array or aux class?
if (rule == null)
{
rule = DRC.getSpacingRule(waLayer, null, waLayer, null, con, -1, 0, 0);
if (rule != null)
{
if (con)
rulesCon.put(waLayer, rule);
else
rulesNonCon.put(waLayer, rule);
}
}
double ruleValue = -1;
if (rule != null) ruleValue = rule.getValue(0);
if (ruleValue < 0) continue;
if (wa.poly.getBounds2D().getMinX() > oWa.poly.getBounds2D().getMaxX()+ruleValue ||
oWa.poly.getBounds2D().getMinX() > wa.poly.getBounds2D().getMaxX()+ruleValue ||
wa.poly.getBounds2D().getMinY() > oWa.poly.getBounds2D().getMaxY()+ruleValue ||
oWa.poly.getBounds2D().getMinY() > wa.poly.getBounds2D().getMaxY()+ruleValue) continue;
double dist = wa.poly.separation(oWa.poly); // dist == 0 -> intersect or inner loops
if (dist > 0 && dist < ruleValue)
{
int layertype = getWellLayerType(waLayer);
if (layertype == ERCPSEUDO) continue;
List<PolyBase> polyList = new ArrayList<PolyBase>();
polyList.add(wa.poly);
polyList.add(oWa.poly);
errorLogger.logMessage(waLayer.getName() + " areas too close (are "
+ TextUtils.formatDistance(dist) + ", should be "
+ TextUtils.formatDistance(ruleValue) + ")", polyList, cell, 0, true);
}
}
}
}
// compute edge distance if requested
if (wellPrefs.findWorstCaseWell)
{
worstPWellDist = 0;
worstPWellCon = null;
worstPWellEdge = null;
worstNWellDist = 0;
worstNWellCon = null;
worstNWellEdge = null;
for(WellArea wa : wellAreas)
{
int wellType = getWellLayerType(wa.poly.getLayer());
if (wellType == ERCPSEUDO) continue;
PrimitiveNode.Function desiredContact = PrimitiveNode.Function.SUBSTRATE;
if (wellType == ERCPWell) desiredContact = PrimitiveNode.Function.WELL;
// find the worst distance to the edge of the area
Point2D [] points = wa.poly.getPoints();
int count = points.length;
for(int i=0; i<count*2; i++)
{
// figure out which point is being analyzed
Point2D testPoint = null;
if (i < count)
{
int prev = i-1;
if (i == 0) prev = count-1;
testPoint = new Point2D.Double((points[prev].getX() + points[i].getX()) / 2, (points[prev].getY() + points[i].getY()) / 2);
} else
{
testPoint = points[i-count];
}
// find the closest contact to this point
boolean first = true;
double bestDist = 0;
WellCon bestWc = null;
for(WellCon wc : wellCons)
{
if (wc.fun != desiredContact) continue;
if (!wa.poly.getBounds2D().contains(wc.ctr)) continue;
if (!wa.poly.contains(wc.ctr)) continue;
double dist = testPoint.distance(wc.ctr);
if (first || dist < bestDist)
{
bestDist = dist;
bestWc = wc;
}
first = false;
}
if (first) continue;
// accumulate worst distances to edges
if (wellType == 1)
{
if (bestDist > worstPWellDist)
{
worstPWellDist = bestDist;
worstPWellCon = bestWc.ctr;
worstPWellEdge = testPoint;
}
} else
{
if (bestDist > worstNWellDist)
{
worstNWellDist = bestDist;
worstNWellCon = bestWc.ctr;
worstNWellEdge = testPoint;
}
}
}
}
}
// clean up
wellCons.clear();
doneCells.clear();
cellMerges.clear();
errorLogger.termLogging(true);
int errorCount = errorLogger.getNumErrors();
return errorCount;
}
private class WellCheckVisitor extends HierarchyEnumerator.Visitor
{
public WellCheckVisitor() {}
public boolean enterCell(HierarchyEnumerator.CellInfo info)
{
// Checking if job is scheduled for abort or already aborted
if (job != null && job.checkAbort()) return false;
// make an object for merging all of the wells in this cell
Cell cell = info.getCell();
if (cellMerges.get(cell) == null)
{
GeometryHandler thisMerge = GeometryHandler.createGeometryHandler(mode,
ercLayersArray.length);
cellMerges.put(cell, thisMerge);
}
return true;
}
public void exitCell(HierarchyEnumerator.CellInfo info)
{
// Checking if job is scheduled for abort or already aborted
if (job != null && job.checkAbort()) return;
// get the object for merging all of the wells in this cell
Cell cell = info.getCell();
GeometryHandler thisMerge = cellMerges.get(cell);
if (thisMerge == null) throw new Error("wrong condition in ERCWellCheck.enterCell()");
if (!doneCells.contains(cell))
{
for(Iterator<ArcInst> it = cell.getArcs(); it.hasNext(); )
{
ArcInst ai = it.next();
Technology tech = ai.getProto().getTechnology();
// Getting only ercLayers
Poly [] arcInstPolyList = tech.getShapeOfArc(ai, ercLayers);
int tot = arcInstPolyList.length;
for(int i=0; i<tot; i++)
{
Poly poly = arcInstPolyList[i];
Layer layer = poly.getLayer();
// Only interested in well/select
Shape newElem = poly;
thisMerge.add(layer, newElem);
}
}
thisMerge.postProcess(true);
// merge everything sub trees
for(Iterator<NodeInst> it = cell.getNodes(); it.hasNext(); )
{
NodeInst ni = it.next();
if (!ni.isCellInstance()) continue;
// get sub-merge information for the cell instance
GeometryHandler subMerge = cellMerges.get(ni.getProto());
if (subMerge != null)
{
AffineTransform tTrans = ni.translateOut(ni.rotateOut());
thisMerge.addAll(subMerge, tTrans);
}
}
// To mark if cell is already done
doneCells.add(cell);
}
}
public boolean visitNodeInst(Nodable no, HierarchyEnumerator.CellInfo info)
{
NodeInst ni = no.getNodeInst();
if (NodeInst.isSpecialNode(ni))
return false; // Nothing to do, Dec 9;
// merge everything
AffineTransform trans = null;
// Not done yet
Cell cell = info.getCell();
if (!doneCells.contains(cell))
{
if (!ni.isCellInstance())
{
GeometryHandler thisMerge = cellMerges.get(cell);
PrimitiveNode pNp = (PrimitiveNode)ni.getProto();
Technology tech = pNp.getTechnology();
// Getting only ercLayers
Poly [] nodeInstPolyList = tech.getShapeOfNode(ni, true, true, ercLayers);
int tot = nodeInstPolyList.length;
for(int i=0; i<tot; i++)
{
Poly poly = nodeInstPolyList[i];
Layer layer = poly.getLayer();
// Only interested in well/select regions
if (trans == null) trans = ni.rotateOut(); // transformation only calculated when required.
poly.transform(trans);
Shape newElem = poly;
thisMerge.add(layer, newElem);
}
}
}
// look for well and substrate contacts
PrimitiveNode.Function fun = ni.getFunction();
if (canBeSubstrateTap(fun) || canBeWellTap(fun))
{
WellCon wc = new WellCon();
wc.ctr = ni.getTrueCenter();
if (trans == null) trans = ni.rotateOut(); // transformation only calculated when required.
trans.transform(wc.ctr, wc.ctr);
info.getTransformToRoot().transform(wc.ctr, wc.ctr);
wc.fun = fun;
PortInst pi = ni.getOnlyPortInst();
Netlist netList = info.getNetlist();
Network net = netList.getNetwork(pi);
wc.netNum = info.getNetID(net);
wc.onProperRail = false;
if (net != null)
{
boolean searchWell = canBeSubstrateTap(fun);
// PWell: must be on ground or NWell: must be on power
Network parentNet = net;
HierarchyEnumerator.CellInfo cinfo = info;
while (cinfo.getParentInst() != null) {
parentNet = cinfo.getNetworkInParent(parentNet);
cinfo = cinfo.getParentInfo();
}
if (parentNet != null)
{
for (Iterator<Export> it = parentNet.getExports(); !wc.onProperRail && it.hasNext();)
{
Export exp = it.next();
if ((searchWell && exp.isGround()) || (!searchWell && exp.isPower()))
wc.onProperRail = true;
}
}
}
wellCons.add(wc);
}
return true;
}
}
// **************************************** STATISTICS ****************************************
private List<WellBoundRecord> wellBoundSearchOrder;
private int numObjSearches;
private void initStatistics()
{
if (GATHERSTATISTICS)
{
numObjSearches = 0;
wellBoundSearchOrder = Collections.synchronizedList(new ArrayList<WellBoundRecord>());
}
}
private void showStatistics()
{
if (GATHERSTATISTICS)
{
System.out.println("SEARCHED "+numObjSearches+" OBJECTS");
new ShowWellBoundOrder();
}
}
private static class WellBoundRecord
{
WellBound wb;
int processor;
private WellBoundRecord(WellBound w, int p) { wb = w; processor = p; }
}
private class ShowWellBoundOrder extends EModelessDialog
{
private static final long serialVersionUID = 1L;
private Timer vcrTimer;
private long vcrLastAdvance;
private int wbIndex;
private int speed;
private JTextField tf;
private Highlighter h;
private Color[] hColors = new Color[] {Color.WHITE, Color.RED, Color.GREEN, Color.BLUE};
public ShowWellBoundOrder()
{
super(Main.getCurrentJFrame(), false);
initComponents();
finishInitialization();
setVisible(true);
wbIndex = 0;
EditWindow wnd = EditWindow.getCurrent();
h = wnd.getHighlighter();
h.clear();
}
private void initComponents()
{
getContentPane().setLayout(new GridBagLayout());
setTitle("Show ERC Progress");
setName("");
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent evt) { closeDialog(); }
});
GridBagConstraints gridBagConstraints;
JButton go = new JButton("Go");
go.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) { goNow(); }
});
gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.insets = new Insets(4, 4, 4, 4);
getContentPane().add(go, gridBagConstraints);
JButton stop = new JButton("Stop");
stop.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) { stopNow(); }
});
gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 0;
gridBagConstraints.insets = new Insets(4, 4, 4, 4);
getContentPane().add(stop, gridBagConstraints);
JLabel lab = new JLabel("Speed:");
gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 1;
gridBagConstraints.insets = new Insets(4, 4, 4, 4);
getContentPane().add(lab, gridBagConstraints);
speed = 1;
tf = new JTextField(Integer.toString(speed));
tf.getDocument().addDocumentListener(new BoundsPlayerDocumentListener());
gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 1;
gridBagConstraints.insets = new Insets(4, 4, 4, 4);
getContentPane().add(tf, gridBagConstraints);
pack();
}
private void updateSpeed()
{
speed = TextUtils.atoi(tf.getText());
if (vcrTimer != null) vcrTimer.setDelay(speed);
}
private void goNow()
{
if (vcrTimer == null)
{
ActionListener taskPerformer = new ActionListener()
{
public void actionPerformed(ActionEvent evt) { tick(); }
};
vcrTimer = new Timer(speed, taskPerformer);
vcrLastAdvance = System.currentTimeMillis();
vcrTimer.start();
}
}
private void stopNow()
{
if (vcrTimer == null) return;
vcrTimer.stop();
vcrTimer = null;
}
private void tick()
{
// see if it is time to advance the VCR
long curtime = System.currentTimeMillis();
if (curtime - vcrLastAdvance < speed) return;
vcrLastAdvance = curtime;
if (wbIndex >= wellBoundSearchOrder.size()) stopNow(); else
{
WellBoundRecord wbr = wellBoundSearchOrder.get(wbIndex++);
h.addPoly(new Poly(wbr.wb.bound), cell, hColors[wbr.processor]);
h.finished();
}
}
private class BoundsPlayerDocumentListener implements DocumentListener
{
BoundsPlayerDocumentListener() {}
public void changedUpdate(DocumentEvent e) { updateSpeed(); }
public void insertUpdate(DocumentEvent e) { updateSpeed(); }
public void removeUpdate(DocumentEvent e) { updateSpeed(); }
}
}
/**
* Method to tell whether this function describes an element which can be used as substrate tap (ERC)
* @return
*/
private static boolean canBeSubstrateTap(PrimitiveNode.Function fun)
{
return fun == PrimitiveNode.Function.SUBSTRATE || fun == PrimitiveNode.Function.RESPWELL;
}
/**
* Method to tell whether this function describes an element which can be used as well tap (ERC)
* @return
*/
private static boolean canBeWellTap(PrimitiveNode.Function fun)
{
return fun == PrimitiveNode.Function.WELL || fun == PrimitiveNode.Function.RESNWELL;
}
}