/* -*- tab-width: 4 -*- * * Electric(tm) VLSI Design System * * File: ERCWellCheck.java * * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved. * * Electric(tm) is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * Electric(tm) is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Electric(tm); see the file COPYING. If not, write to * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, Mass 02111-1307, USA. */ package com.sun.electric.tool.erc; 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.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicInteger; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JTextField; import javax.swing.Timer; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import com.sun.electric.Main; 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.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.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 com.sun.electric.tool.util.concurrent.utils.ElapseTimer; import com.sun.electric.util.TextUtils; import com.sun.electric.util.math.DBMath; import com.sun.electric.util.math.GenMath.MutableBoolean; /** * This is the Electrical Rule Checker tool. * * @author Steve Rubin, Gilda Garreton */ @Deprecated public class ERCWellCheckOld { 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 Map<Integer, List<Transistor>> transistors; private Set<Integer> networkExportAvailable; private List<Transistor> alreadyHit; private Set<Integer> networkWithExportCache; // TODO [felix] remove this flag public static boolean useFelixCode = true; 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 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) { ERCWellCheckOld check = new ERCWellCheckOld(cell, null, newAlgorithm, wellPrefs); return check.runNow(); } private ERCWellCheckOld(Cell cell, WellCheckJob job, GeometryHandler.GHMode newAlgorithm, WellCheckPreferences wellPrefs) { this.job = job; this.mode = newAlgorithm; this.cell = cell; this.wellPrefs = wellPrefs; this.transistors = new HashMap<Integer, List<Transistor>>(); this.networkWithExportCache = new HashSet<Integer>(); } 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 { ERCWellCheckOld check = new ERCWellCheckOld(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() + "' ..."); ElapseTimer timer = ElapseTimer.createInstance().start(); 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 timer.end(); if (errorCount == 0) { System.out.println("No Well errors found (took " + timer + ")"); } else { System.out.println("FOUND " + errorCount + " WELL ERRORS (took " + timer + ")"); } return errorCount; } // XXX [felix] ugly style use concurrent data structure private WellCon getNextWellCon(int threadIndex) { synchronized (wellConLists[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 (wellConLists[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(); } } // FIXME remove this debug stuff Integer total; double ratio = 1; int cacheHits = 0; // XXX [felix] define each analyzing step in each own module and use a // strategy pattern // XXX [felix] do analyzing parallel (task parallel) - easy to parallize // after rework private int doNewWay() { pWellRoot = RTNode.makeTopLevel(); nWellRoot = RTNode.makeTopLevel(); // enumerate the hierarchy below here ElapseTimer timer = ElapseTimer.createInstance().start(); NewWellCheckVisitor wcVisitor = new NewWellCheckVisitor(); HierarchyEnumerator.enumerateCell(cell, VarContext.globalContext, wcVisitor); int numPRects = getTreeSize(pWellRoot); int numNRects = getTreeSize(nWellRoot); timer.end(); System.out.println(" Geometry collection found " + (numPRects + numNRects) + " well pieces, took " + timer); timer.start(); wcVisitor.clear(); wcVisitor = null; // determine the number of threads to use // XXX [felix] not here in the algorithm try to determine this in the // preferences 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 clustdered for each processor assignWellContacts(numberOfThreads); if (Job.getDebug()) { timer.end(); System.out.println(" Assign well contacts took: " + timer); timer.start(); } // analyze the contacts NetValues.reset(); if (numberOfThreads <= 1) spreadSeeds(0); else { // XXX [felix] do not use semaphores here, the Thread object // provides you with an join method. 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); } timer.end(); String msg = " Geometry analysis "; if (numberOfThreads > 1) msg += "used " + numberOfThreads + " threads and "; msg += "took "; System.out.println(msg + timer); timer.start(); // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx // 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); } } } } // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx // more analysis cacheHits = 0; // System.out.println(wellCons.size()); boolean hasPCon = false, hasNCon = false; for (WellCon wc : wellCons) { if (canBeSubstrateTap(wc.fun)) hasPCon = true; else hasNCon = true; if (!wc.onRail && useFelixCode) { if (networkExportAvailable.contains(wc.netNum)) wc.onRail = true; else if (networkWithExportCache.contains(wc.netNum)) { wc.onRail = true; cacheHits++; } else { Set<Integer> startPath = new HashSet<Integer>(); List<Transistor> startTrans = transistors.get(wc.netNum); if (startTrans != null) { if (createTransistorChain(startPath, startTrans.get(0), false)) { wc.onRail = true; networkWithExportCache.addAll(startPath); } } } } if (!(wc.onRail || 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); } } } } // System.out.println("Cache Hits: " + cacheHits); // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx // 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); } // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx timer.end(); System.out.println(" Additional analysis took " + timer); timer.start(); // make sure the wells are separated properly // Emany // times // THIS IS A DRC JOB .. not efficient if done here. // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 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)); timer.end(); System.out.println(" Design rule check took " + timer); timer.start(); } // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx // 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; } } } } timer.end(); System.out.println(" Worst-case distance analysis took " + timer); } // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx errorLogger.termLogging(true); int errorCount = errorLogger.getNumErrors(); return errorCount; } private boolean createTransistorChain(Set<Integer> path, Transistor node, boolean result) { alreadyHit = new LinkedList<Transistor>(); result |= createTransistorRec(path, node, result, node.drainNet.get()); if (!result) result |= createTransistorRec(path, node, result, node.sourceNet.get()); return result; } private boolean createTransistorRec(Set<Integer> path, Transistor node, boolean result, int num) { List<Transistor> transis = new LinkedList<Transistor>(); transis.add(node); Transistor transis_old = null; Transistor transis_parent = null; alreadyHit.add(node); while (transis.size() > 0) { Transistor transistor = transis.get(0); transis_old = transistor; transis.remove(transistor); Integer neighbor = null; if (transistor.drainNet.get() == num) { neighbor = transistor.sourceNet.get(); } else { neighbor = transistor.drainNet.get(); } path.add(neighbor); num = neighbor; result |= networkExportAvailable.contains(neighbor); if (!result) { result |= networkWithExportCache.contains(neighbor); if (result) cacheHits++; } // cut the line if (result) return result; for (Transistor trans : transistors.get(neighbor)) { if (!alreadyHit.contains(trans)) { transis.add(trans); alreadyHit.add(trans); } } } return result; } // XXX [felix] Use Concurrent Lists instead of ArrayList for task pools - // comment above: creates ugly style // XXX [felix] Use ArrayLists instead of arrays // XXX [felix] why creating the iterators here? 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) // XXX [felix] distance from the center of // the cell to the center of WellCon 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); } } } // provide this information in the rtree structure private int getTreeSize(RTNode rtree) { int total = 0; if (rtree.getFlag()) total += rtree.getTotal(); // FIXME [felix] ugly style of // assignment, why doesn't the RTree // provide this information 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); } } } // XXX Use enum instead of string for title 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); // XXX [felix] Use a atomic reference - jdk gives you exactly this what // you want 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; } // XXX [felix] whats the main difference between index and indexValues // XXX [felix] create a static factory method - you could use this method // atomically // XXX [felix] create such a way also for thread ids - could also be useful // on other places 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; boolean onRail; PrimitiveNode.Function fun; NodeInst ni; } private static class Transistor { AtomicInteger drainNet = new AtomicInteger(); AtomicInteger sourceNet = new AtomicInteger(); } /** * 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; boolean onExport; } 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; private Map<Integer, Transistor> neighborCache; public NewWellCheckVisitor() { essentialPWell = new HashMap<Cell, List<Rectangle2D>>(); essentialNWell = new HashMap<Cell, List<Rectangle2D>>(); networkCache = new HashMap<Network, NetRails>(); networkExportAvailable = new HashSet<Integer>(); neighborCache = new HashMap<Integer, Transistor>(); } 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)); } } private void addNetwork(Network net, AtomicInteger netNum, HierarchyEnumerator.CellInfo cinfo, Transistor trans) { if (net != null) { Integer num = cinfo.getNetID(net); netNum.set(num); if (!transistors.containsKey(num)) { List<Transistor> tmpList = new LinkedList<Transistor>(); transistors.put(num, tmpList); } transistors.get(num).add(trans); // Transistor otherTrans = neighborCache.get(num); // // if (otherTrans != null) { // trans.neighbors.add(otherTrans); // otherTrans.neighbors.add(trans); // } // // neighborCache.put(num, trans); } } 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 ((fun.isNTypeTransistor() || fun.isPTypeTransistor()) && useFelixCode) { Transistor trans = new Transistor(); addNetwork(netList.getNetwork(ni.getTransistorDrainPort()), trans.drainNet, info, trans); addNetwork(netList.getNetwork(ni.getTransistorSourcePort()), trans.sourceNet, info, trans); } else 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.onRail = 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); Iterator<Export> it = parentNet.getExports(); while (it.hasNext()) { Export exp = it.next(); networkExportAvailable.add(parentNet.getNetIndex()); if (exp.isGround()) nr.onGround = true; if (exp.isPower()) nr.onPower = true; nr.onExport = true; } } boolean searchWell = (canBeSubstrateTap(fun)); if ((searchWell && nr.onGround) || (!searchWell && nr.onPower)) wc.onProperRail = true; if (nr.onExport && useFelixCode) wc.onRail = 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; } } } } public void clear() { neighborCache.clear(); networkCache.clear(); neighborCache = null; networkCache = null; // System.out.println("visitor cleared ..."); } } // XXX [felix] use a enum 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((JFrame) Main.getCurrentJFrame()); 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; } }