All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.electric.tool.erc.ERCWellCheck Maven / Gradle / Ivy

/* -*- 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 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.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.Layer;
import com.sun.electric.technology.PrimitiveNode;
import com.sun.electric.technology.PrimitiveNode.Function;
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.erc.wellcheck.ConnectionCheck;
import com.sun.electric.tool.erc.wellcheck.DRCCheck;
import com.sun.electric.tool.erc.wellcheck.DistanceCheck;
import com.sun.electric.tool.erc.wellcheck.NetValues;
import com.sun.electric.tool.erc.wellcheck.OnRailCheck;
import com.sun.electric.tool.erc.wellcheck.ShortCircuitCheck;
import com.sun.electric.tool.erc.wellcheck.Utils;
import com.sun.electric.tool.erc.wellcheck.Utils.WorkDistributionStrategy;
import com.sun.electric.tool.erc.wellcheck.WellCheckAnalysisStrategy;
import com.sun.electric.tool.erc.wellcheck.WellCon;
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.user.ui.TopLevel;
import com.sun.electric.tool.util.concurrent.Parallel;
import com.sun.electric.tool.util.concurrent.patterns.PForTask;
import com.sun.electric.tool.util.concurrent.patterns.PJob;
import com.sun.electric.tool.util.concurrent.patterns.PTask;
import com.sun.electric.tool.util.concurrent.runtime.taskParallel.IThreadPool;
import com.sun.electric.tool.util.concurrent.utils.BlockedRange1D;
import com.sun.electric.util.CollectionFactory;
import com.sun.electric.util.ElapseTimer;
import com.sun.electric.util.TextUtils;
import com.sun.electric.util.config.Configuration;
import com.sun.electric.util.math.DBMath;
import com.sun.electric.util.math.FixpRectangle;
import com.sun.electric.util.math.FixpTransform;

import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

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.
 */
public class ERCWellCheck {
    private Cell cell;
    private Set possiblePrimitives;
    private List wellCons = new ArrayList();
    private Iterator[] wellConIterator;
    private List[] 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> transistors;
    private Set networkExportAvailable;
    private boolean hasPCon;
    private boolean hasNCon;
    private IThreadPool threadPool;

    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:
         * 
    *
  • 0: must have a contact in every well area.
  • *
  • 1: must have at least one contact.
  • *
  • 2: do not check for contact presence.
  • *
* 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: *
    *
  • 0: must have a contact in every well area.
  • *
  • 1: must have at least one contact.
  • *
  • 2: do not check for contact presence.
  • *
* 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); } } public enum WellType { none("none"), nwell("N"), pwell("P"); private String name; private WellType(String name) { this.name = name; } /* * (non-Javadoc) * * @see java.lang.Enum#toString() */ @Override public String toString() { return this.name; } } // /** // * Method to analyze the current Cell for well errors. // */ // @Deprecated // 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)); // } public static void analyzeCurCell() { 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, 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. // */ // @Deprecated // public static int checkERCWell(Cell cell, GeometryHandler.GHMode newAlgorithm, WellCheckPreferences wellPrefs) { // ERCWellCheck check = new ERCWellCheck(cell, null, newAlgorithm, wellPrefs); // return check.runNow(); // } public static int checkERCWell(Cell cell, WellCheckPreferences wellPrefs) { ERCWellCheck check = new ERCWellCheck(cell, null, wellPrefs); return check.runNow(); } // @Deprecated // private ERCWellCheck(Cell cell, WellCheckJob job, GeometryHandler.GHMode newAlgorithm, // WellCheckPreferences wellPrefs) { // this.job = job; // this.cell = cell; // this.wellPrefs = wellPrefs; // this.transistors = new HashMap>(); // } private ERCWellCheck(Cell cell, WellCheckJob job, WellCheckPreferences wellPrefs) { this.job = job; this.cell = cell; this.wellPrefs = wellPrefs; this.transistors = new HashMap>(); } private static class WellCheckJob extends Job { private Cell cell; private double worstPWellDist, worstNWellDist; private EPoint worstPWellCon, worstPWellEdge; private EPoint worstNWellCon, worstNWellEdge; private WellCheckPreferences wellPrefs; // @Deprecated // 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.wellPrefs = wellPrefs; // startJob(); // } private WellCheckJob(Cell cell, WellCheckPreferences wellPrefs) { super("ERC Well Check on " + cell, ERC.tool, Job.Type.SERVER_EXAMINE, null, null, Job.Priority.USER); this.cell = cell; this.wellPrefs = wellPrefs; startJob(); } public boolean doIt() throws JobException { ERCWellCheck check = new ERCWellCheck(cell, this, 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 primitives that need to be examined possiblePrimitives = new HashSet(); for (Iterator it = Technology.getTechnologies(); it.hasNext();) { Technology tech = it.next(); for (Iterator 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 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; } private WellCon getNextWellCon(int threadIndex) { synchronized (wellConLists[threadIndex]) { while (wellConIterator[threadIndex].hasNext()) { WellCon wc = wellConIterator[threadIndex].next(); if (wc.getWellNum() == 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.getWellNum() == null) return wc; } } } return null; } private void spreadSeeds(int threadIndex) { for (;;) { WellCon wc = getNextWellCon(threadIndex); if (wc == null) break; // see if this contact is a marked well Rectangle2D searchArea = new Rectangle2D.Double(wc.getCtr().getX(), wc.getCtr().getY(), 0, 0); RTNode topSearch = nWellRoot; if (Utils.canBeSubstrateTap(wc.getFun())) topSearch = pWellRoot; boolean geomFound = false; for (Iterator sea = new RTNode.Search(searchArea, topSearch, true); sea.hasNext();) { WellBound wb = sea.next(); geomFound = true; wc.setWellNum(wb.netID); if (wc.getWellNum() != null) break; } if (wc.getWellNum() != null) continue; // mark it and spread the value wc.setWellNum(new NetValues()); // if nothing to spread, give an error if (!geomFound) { String errorMsg = "N-Well contact is floating"; if (Utils.canBeSubstrateTap(wc.getFun())) errorMsg = "P-Well contact is floating"; errorLogger.logError(errorMsg, new EPoint(wc.getCtr().getX(), wc.getCtr().getY()), cell, 0); continue; } // spread out through the well area Utils.spreadWellSeed(wc.getCtr().getX(), wc.getCtr().getY(), wc.getWellNum(), topSearch, threadIndex); } } public class SpreadInThread extends PTask { private int threadIndex; public SpreadInThread(PJob job, int index) { super(job); threadIndex = index; } public void execute() { spreadSeeds(threadIndex); } } private int doNewWay() { int numberOfThreads = 1; if (wellPrefs.parallelWellAnalysis) numberOfThreads = Runtime.getRuntime().availableProcessors(); if (numberOfThreads > 1) { int maxProc = wellPrefs.maxProc; if (maxProc > 0) numberOfThreads = maxProc; } hasNCon = false; hasPCon = false; NetValues.numberOfMerges = 0; pWellRoot = RTNode.makeTopLevel(); nWellRoot = RTNode.makeTopLevel(); // enumerate the hierarchy below here ElapseTimer timer = ElapseTimer.createInstance().start(); WellCheckVisitor wcVisitor = new WellCheckVisitor(); 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); wcVisitor.clear(); wcVisitor = null; if(numberOfThreads == 0) numberOfThreads = Runtime.getRuntime().availableProcessors(); IThreadPool.NUM_THREADS = Integer.valueOf(numberOfThreads); threadPool = Configuration.lookup(IThreadPool.class); timer.start(); try { // make arrays of well contacts clustered 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 { PJob spreadJob = new PJob(threadPool); for (int i = 0; i < numberOfThreads; i++) spreadJob.add(new SpreadInThread(spreadJob, i)); spreadJob.execute(); } timer.end(); String msg = " Geometry analysis "; if (numberOfThreads > 1) msg += "used " + numberOfThreads + " threads and "; msg += "took "; System.out.println(msg + timer); try { threadPool.shutdown(); threadPool.join(); } catch (InterruptedException e) { e.printStackTrace(); } timer.start(); if (Job.getDebug()) { System.out.println(" Amount of merges: " + NetValues.numberOfMerges); } } catch (Throwable t) { t.printStackTrace(); } StrategyParameter parameter = new StrategyParameter(wellCons, wellPrefs, cell, errorLogger); // create analysis steps List analysisParts = CollectionFactory.createArrayList(); analysisParts.add(new ShortCircuitCheck(parameter)); analysisParts.add(new OnRailCheck(parameter, networkExportAvailable, transistors)); analysisParts.add(new ConnectionCheck(parameter, hasPCon, hasNCon, pWellRoot, nWellRoot)); analysisParts.add(new DRCCheck(parameter, pWellLayer, nWellLayer, pWellRoot, nWellRoot)); analysisParts.add(new DistanceCheck(parameter, worstPWellDist, worstPWellCon, worstPWellEdge, worstNWellDist, worstNWellCon, worstNWellEdge, pWellRoot, nWellRoot)); // execute analysis steps for (WellCheckAnalysisStrategy strategy : analysisParts) { strategy.execute(); } timer.end(); System.out.println(" Additional analysis took " + timer); errorLogger.termLogging(true); int errorCount = errorLogger.getNumErrors(); return errorCount; } public static class StrategyParameter { private final List wellCons; private final WellCheckPreferences wellPrefs; private final Cell cell; private final ErrorLogger errorLogger; /** * @param wellCons * @param wellPrefs * @param cell */ public StrategyParameter(List wellCons, WellCheckPreferences wellPrefs, Cell cell, ErrorLogger errorLogger) { super(); this.wellCons = wellCons; this.wellPrefs = wellPrefs; this.cell = cell; this.errorLogger = errorLogger; } public List getWellCons() { return wellCons; } public WellCheckPreferences getWellPrefs() { return wellPrefs; } public Cell getCell() { return cell; } // public ErrorLogger getErrorLogger() { // return errorLogger; // } // Apply translation to place the bounding box of the well contact // in the correct location on the top cell. private Rectangle2D placedWellCon(WellCon wc) { Rectangle2D orig = wc.getNi().getBounds(); return new Rectangle2D.Double(wc.getCtr().getX() - orig.getWidth() / 2, wc.getCtr().getY() - orig.getHeight() / 2, orig.getWidth(), orig.getHeight()); } public void logError(String message) { errorLogger.logError(message, cell, 0); } public void logError(String message, Object... wblist) { List list = new ArrayList(); for (Object w : wblist) { if (w instanceof WellBound) { list.add(((WellBound) w).getBounds()); } else if (w instanceof WellCon) { // calculate the correct location of WellCon bound with respect to // the top cell list.add(placedWellCon(((WellCon) w))); } } errorLogger.logMessage(message, list, cell, 0, true); } } @SuppressWarnings("unchecked") 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(); } if (Utils.WORKDISTRIBUTION == WorkDistributionStrategy.cluster) { // 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.getCtr().distance(ctr); else { for (int j = 0; j < i; j++) dist += wc.getCtr().distance(farPoints[j]); } if (dist > farthest) { farthest = dist; farPoints[i].setLocation(wc.getCtr()); } } } // 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.getCtr().distance(farPoints[j]); if (dist < minDist) { minDist = dist; threadNum = j; } } wellConLists[threadNum].add(wc); } } else if (Utils.WORKDISTRIBUTION == WorkDistributionStrategy.random) { for (int i = 0; i < wellCons.size(); i++) wellConLists[i % numberOfThreads].add(wellCons.get(i)); } else if (Utils.WORKDISTRIBUTION == WorkDistributionStrategy.bucket) { GridDim dim = calculateGridDim(numberOfThreads); double sizeCellX = cell.getDefWidth() / dim.xDim; double sizeCellY = cell.getDefHeight() / dim.yDim; int stepWidth = (wellCons.size() < numberOfThreads) ? wellCons.size() : wellCons.size() / numberOfThreads; Parallel.For(new BlockedRange1D(0, wellCons.size(), stepWidth), new WorkDistributionTask(sizeCellX, sizeCellY, dim), threadPool); } } // create iterators over the lists for (int i = 0; i < numberOfThreads; i++) wellConIterator[i] = wellConLists[i].iterator(); } public class WorkDistributionTask extends PForTask { private double sizeX; private double sizeY; private GridDim dim; public WorkDistributionTask(double sizeX, double sizeY, GridDim dim) { this.sizeX = sizeX; this.sizeY = sizeY; this.dim = dim; } /* * (non-Javadoc) * * @see * com.sun.electric.tool.util.concurrent.patterns.PForJob.PForTask#execute * (com.sun.electric.tool.util.concurrent.patterns.PForJob.BlockedRange) */ @Override public void execute() { for (int i = range.start(); i < range.end(); i++) { WellCon con = wellCons.get(i); GridDim tmpDim = calculateBucket(con, sizeX, sizeY); int threadId = tmpDim.xDim + tmpDim.yDim * dim.xDim; CollectionFactory.threadSafeListAdd(con, wellConLists[threadId]); } } } private GridDim calculateBucket(WellCon con, double sizeX, double sizeY) { GridDim result = new GridDim(); result.xDim = (int) ((con.getCtr().getX() - cell.getBounds().getMinX()) / sizeX); result.yDim = (int) ((con.getCtr().getY() - cell.getBounds().getMinY()) / sizeY); return result; } private GridDim calculateGridDim(int numberOfThreads) { GridDim result = new GridDim(); for (int i = (int) Math.sqrt(numberOfThreads); i >= 1; i--) { if ((i * (numberOfThreads / i)) == numberOfThreads) { result.xDim = i; result.yDim = numberOfThreads / i; return result; } } return null; } private static class GridDim { private int xDim; private int yDim; } // provide this information in the R-Tree structure 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 = rtree.getChildTree(j); total += getTreeSize(child); } } return total; } public static class Transistor { private AtomicInteger drainNet = new AtomicInteger(); private AtomicInteger sourceNet = new AtomicInteger(); public AtomicInteger getDrainNet() { return drainNet; } public void setDrainNet(AtomicInteger drainNet) { this.drainNet = drainNet; } public AtomicInteger getSourceNet() { return sourceNet; } public void setSourceNet(AtomicInteger sourceNet) { this.sourceNet = sourceNet; } } /** * Class to define an R-Tree leaf node for geometry in the blockage data * structure. */ public static class WellBound implements RTBounds { private final FixpRectangle bound; private NetValues netID; WellBound(FixpRectangle bound) { this.bound = bound; this.netID = null; } @Override public FixpRectangle getBounds() { return bound; } public NetValues getNetID() { return netID; } public void setNetID(NetValues netNum) { this.netID = netNum; } @Override public String toString() { return "Well Bound on net " + netID.getIndex(); } } private static class NetRails { boolean onGround; boolean onPower; boolean onExport; } public static class WellNet { private List pointsOnNet; private List contactsOnNet; private PrimitiveNode.Function fun; /** * @param pointsOnNet * @param contactsOnNet * @param fun */ public WellNet(List pointsOnNet, List contactsOnNet, Function fun) { super(); this.pointsOnNet = pointsOnNet; this.contactsOnNet = contactsOnNet; this.fun = fun; } public List getPointsOnNet() { return pointsOnNet; } public void setPointsOnNet(List pointsOnNet) { this.pointsOnNet = pointsOnNet; } public List getContactsOnNet() { return contactsOnNet; } public void setContactsOnNet(List contactsOnNet) { this.contactsOnNet = contactsOnNet; } public PrimitiveNode.Function getFun() { return fun; } public void setFun(PrimitiveNode.Function fun) { this.fun = fun; } } /** * Comparator class for sorting Rectangle2D objects by their size. */ private static class RectangleBySize implements Comparator { /** * 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 WellCheckVisitor extends HierarchyEnumerator.Visitor { private Map> essentialPWell; private Map> essentialNWell; private Map networkCache; private Map neighborCache; public WellCheckVisitor() { essentialPWell = new HashMap>(); essentialNWell = new HashMap>(); networkCache = new HashMap(); networkExportAvailable = new HashSet(); neighborCache = new HashMap(); } 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; } @Override 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 pWellsInCell = essentialPWell.get(cell); List nWellsInCell = essentialNWell.get(cell); for (Rectangle2D b : pWellsInCell) { FixpRectangle bounds = FixpRectangle.from(b); DBMath.transformRect(bounds, info.getTransformToRoot()); pWellRoot = RTNode.linkGeom(null, pWellRoot, new WellBound(bounds)); } for (Rectangle2D b : nWellsInCell) { FixpRectangle bounds = FixpRectangle.from(b); 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) { int num = cinfo.getNetID(net); Integer iNum = Integer.valueOf(num); netNum.set(num); if (!transistors.containsKey(iNum)) { List tmpList = new LinkedList(); transistors.put(iNum, tmpList); } transistors.get(iNum).add(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())) { Transistor trans = new Transistor(); addNetwork(netList.getNetwork(ni.getTransistorDrainPort()), trans.drainNet, info, trans); addNetwork(netList.getNetwork(ni.getTransistorSourcePort()), trans.sourceNet, info, trans); } else if (Utils.canBeSubstrateTap(fun) || Utils.canBeWellTap(fun)) { // Allowing more than one port for well resistors. for (Iterator pIt = ni.getPortInsts(); pIt.hasNext();) { // PortInst pi = ni.getOnlyPortInst(); PortInst pi = pIt.next(); Network net = netList.getNetwork(pi); int tmpNetNum = 0; if (net == null) tmpNetNum = -1; else tmpNetNum = info.getNetID(net); WellCon wc = new WellCon(ni.getTrueCenter(), tmpNetNum, null, false, false, fun, ni); FixpTransform trans = ni.rotateOut(); trans.transform(wc.getCtr(), wc.getCtr()); info.getTransformToRoot().transform(wc.getCtr(), wc.getCtr()); if (net != null) { // 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 it = parentNet.getExports(); while (it.hasNext()) { Export exp = it.next(); networkExportAvailable.add(new Integer(parentNet.getNetIndex())); if (exp.isGround()) nr.onGround = true; if (exp.isPower()) nr.onPower = true; nr.onExport = true; } } boolean searchWell = (Utils.canBeSubstrateTap(fun)); if (Utils.canBeSubstrateTap(wc.getFun())) hasPCon = true; else hasNCon = true; if (searchWell) hasPCon = true; else hasNCon = true; if ((searchWell && nr.onGround) || (!searchWell && nr.onPower)) wc.setOnProperRail(true); if (nr.onExport) wc.setOnRail(true); } } wellCons.add(wc); } } return true; } private void ensureCellCached(Cell cell) { List pWellsInCell = essentialPWell.get(cell); List nWellsInCell = essentialNWell.get(cell); if (pWellsInCell == null) { // gather all wells in the cell pWellsInCell = new ArrayList(); nWellsInCell = new ArrayList(); for (Iterator 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(); FixpTransform trans = ni.rotateOut(); poly.transform(trans); Rectangle2D bound = poly.getBounds2D(); WellType wellType = getWellLayerType(layer); if (wellType == WellType.pwell) pWellsInCell.add(bound); else if (wellType == WellType.nwell) nWellsInCell.add(bound); } } for (Iterator 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(); WellType wellType = getWellLayerType(layer); if (wellType == WellType.pwell) pWellsInCell.add(bound); else if (wellType == WellType.nwell) nWellsInCell.add(bound); } } // eliminate duplicates eliminateDuplicates(pWellsInCell); eliminateDuplicates(nWellsInCell); // save results essentialPWell.put(cell, pWellsInCell); essentialNWell.put(cell, nWellsInCell); } } private void eliminateDuplicates(List 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; } } 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 WellType getWellLayerType(Layer layer) { Layer.Function fun = layer.getFunction(); if (layer.isPseudoLayer()) return WellType.none; if (fun == Layer.Function.WELLP) return WellType.pwell; if (fun == Layer.Function.WELL || fun == Layer.Function.WELLN) return WellType.nwell; return WellType.none; } // **************************************** STATISTICS **************************************** private void initStatistics() { if (Utils.GATHERSTATISTICS) { Utils.numObjSearches = 0; Utils.wellBoundSearchOrder = Collections.synchronizedList(new ArrayList()); } } private void showStatistics() { if (Utils.GATHERSTATISTICS) { System.out.println("SEARCHED " + Utils.numObjSearches + " OBJECTS"); new ShowWellBoundOrder(); } } public static class WellBoundRecord { private WellBound wb; private int processor; public WellBoundRecord(WellBound w, int p) { wb = w; processor = p; } public WellBound getWb() { return wb; } public void setWb(WellBound wb) { this.wb = wb; } public int getProcessor() { return processor; } public void setProcessor(int processor) { this.processor = processor; } } private class ShowWellBoundOrder extends EModelessDialog { 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(TopLevel.isMDIMode() ? TopLevel.getCurrentJFrame() : null); 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 >= Utils.wellBoundSearchOrder.size()) stopNow(); else { WellBoundRecord wbr = Utils.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(); } } } }