![JAR search and dependency download from the Maven repository](/logo.png)
com.sun.electric.tool.drc.DRC Maven / Gradle / Ivy
/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: DRC.java
*
* Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
*
* Electric(tm) is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* Electric(tm) is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Electric(tm); see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, Mass 02111-1307, USA.
*/
package com.sun.electric.tool.drc;
import com.sun.electric.database.CellBackup;
import com.sun.electric.database.CellRevision;
import com.sun.electric.database.EditingPreferences;
import com.sun.electric.database.ImmutableArcInst;
import com.sun.electric.database.ImmutableNodeInst;
import com.sun.electric.database.Snapshot;
import com.sun.electric.database.network.Netlist;
import com.sun.electric.database.constraint.Layout;
import com.sun.electric.database.geometry.*;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.hierarchy.Library;
import com.sun.electric.database.hierarchy.HierarchyEnumerator;
import com.sun.electric.database.hierarchy.Nodable;
import com.sun.electric.database.id.CellId;
import com.sun.electric.database.prototype.NodeProto;
import com.sun.electric.database.text.PrefPackage;
import com.sun.electric.database.text.TextUtils;
import com.sun.electric.database.text.Version;
import com.sun.electric.database.topology.*;
import com.sun.electric.database.variable.Variable;
import com.sun.electric.technology.*;
import com.sun.electric.technology.technologies.Artwork;
import com.sun.electric.technology.technologies.Generic;
import com.sun.electric.technology.technologies.Schematics;
import com.sun.electric.tool.Job;
import com.sun.electric.tool.JobException;
import com.sun.electric.tool.Listener;
import com.sun.electric.tool.user.ErrorLogger;
import com.sun.electric.tool.user.User;
import com.sun.electric.tool.user.CircuitChangeJobs;
import com.sun.electric.util.ElapseTimer;
import com.sun.electric.util.math.DBMath;
import com.sun.electric.util.math.ECoord;
import com.sun.electric.util.math.FixpTransform;
import com.sun.electric.util.math.GenMath;
import com.sun.electric.util.math.MutableDouble;
import com.sun.electric.util.math.MutableLong;
import com.sun.electric.util.math.Orientation;
import java.awt.geom.Area;
import java.awt.geom.Rectangle2D;
import java.awt.geom.Point2D;
import java.util.*;
import java.io.Serializable;
import java.util.prefs.Preferences;
/**
* This is the Design Rule Checker tool.
*/
public class DRC extends Listener
{
/** the DRC tool. */ protected static DRC tool = new DRC();
private static final boolean THREAD_SAFE_DRC = true;
// Client static variables
/** for logging incremental errors */ private static ErrorLogger errorLoggerIncremental = ErrorLogger.newInstance("DRC (incremental)", true);
/** map of cells and their objects to DRC */ private static Map> cellsToCheck = new HashMap>();
/** flag to show that incrementatal DRC is running */ private static boolean incrementalRunning = false;
// Server static variables
/** to temporary store DRC dates for spacing checking */ private static Map storedSpacingDRCDate = new HashMap();
/** to temporary store DRC dates for area checking */ private static Map storedAreaDRCDate = new HashMap();
static final double TINYDELTA = DBMath.getEpsilon()*1.1;
/** key of Variable holding DRC Cell annotations. */ static final Variable.Key DRC_ANNOTATION_KEY = Variable.newKey("ATTR_DRC");
/*********************************** Annotations ***********************************/
public static void makeDRCAnnotation()
{
CircuitChangeJobs.MakeCellAnnotationJob.makeAnnotationMenuCommand(tool, DRC.DRC_ANNOTATION_KEY, "BLACK");
}
/*********************************** End of Annotations ***********************************/
/*********************************** Crop Functions ***********************************/
/**
* Method to see if polygons in "pList" (describing arc "ai") should be cropped against a
* connecting transistor. Crops the polygon if so.
*/
static void cropActiveArc(ArcInst ai, boolean ignoreCenterCuts, Poly [] pList)
{
// look for an active layer in this arc
int tot = pList.length;
int diffPoly = -1;
// note: arcs would never have tot=0 since they are only defined to wire elements with valid rules
assert(tot!=0);
for(int j=0; j it = cell.searchIterator(bounds); it.hasNext();)
{
Geometric g = it.next();
// You can't skip the same geometry otherwise layers in the same Geometric won't
// be tested.
// But it is necessary while testing flat geometries... in minWidthInternal
if (ignoreSameGeometry && (g == geo1 || g == geo2))
continue;
// I can't skip geometries to exclude from the search
if (g instanceof NodeInst)
{
NodeInst ni = (NodeInst) g;
if (NodeInst.isSpecialNode(ni))
continue; // Nov 16, no need for checking pins or other special nodes;
if (ni.isCellInstance())
{
// compute bounding area inside of sub-cell
FixpTransform rotI = ni.rotateIn();
FixpTransform transI = ni.translateIn();
rotI.preConcatenate(transI);
newBounds.setRect(bounds);
DBMath.transformRect(newBounds, rotI);
// compute new matrix for sub-cell examination
FixpTransform trans = ni.translateOut(ni.rotateOut());
trans.preConcatenate(moreTrans);
if (lookForLayerCoverage(geo1, poly1, geo2, poly2, (Cell) ni.getProto(), layer, trans, newBounds,
pt1, pt2, pt3, pointsFound, overlap, layerFunction, false, ignoreCenterCuts))
return true;
continue;
}
Technology tech = ni.getProto().getTechnology();
// I have to ask for electrical layers otherwise it will retrieve one polygon for polysilicon
// and poly.polySame(poly1) will never be true. CONTRADICTION!
Poly[] layerLookPolyList = tech.getShapeOfNode(ni, false, ignoreCenterCuts, layerFunction);
int tot = layerLookPolyList.length;
if (tot == 0)
continue;
FixpTransform bound = ni.rotateOut();
bound.preConcatenate(moreTrans);
for (int i = 0; i < tot; i++)
{
Poly poly = layerLookPolyList[i];
// sameLayer test required to check if Active layer is not identical to thick active layer
if (!tech.sameLayer(poly.getLayer(), layer))
{
// This happens when you have implant with VTH implant, Function.Set doesn't distinguish them
if (Job.getDebug())
System.out.println("DRC: Wrong Function.Set with " + layer.getName() + " and " + poly.getLayer().getName());
// assert(false);
continue;
}
// Should be the transform before?
poly.transform(bound);
if (poly1 != null && !overlap && poly.polySame(poly1))
continue;
if (poly2 != null && !overlap && poly.polySame(poly2))
continue;
if (!pointsFound[0] && poly.isInside(pt1))
pointsFound[0] = true; // @TODO Should still evaluate isInside if pointsFound[i] is already valid?
if (!pointsFound[1] && poly.isInside(pt2)) pointsFound[1] = true;
if (pt3 != null && !pointsFound[2] && poly.isInside(pt3)) pointsFound[2] = true;
for (j = 0; j < pointsFound.length && pointsFound[j]; j++) ;
if (j == pointsFound.length)
return true;
// No need of checking rest of the layers
break; // assuming only 1 polygon per layer (non-electrical)
}
} else
{
ArcInst ai = (ArcInst) g;
Technology tech = ai.getProto().getTechnology();
Poly[] layerLookPolyList = tech.getShapeOfArc(ai, layerFunction);
int tot = layerLookPolyList.length;
for (int i = 0; i < tot; i++)
{
Poly poly = layerLookPolyList[i];
// sameLayer test required to check if Active layer is not identical to thich actice layer
if (!tech.sameLayer(poly.getLayer(), layer))
{
// This happens when you have implant with VTH implant, Function.Set doesn't distinguish them
if (Job.getDebug())
System.out.println("DRC: Wrong Function.Set with " + layer.getName() + " and " + poly.getLayer().getName());
continue;
}
poly.transform(moreTrans); // @TODO Should still evaluate isInside if pointsFound[i] is already valid?
if (!pointsFound[0] && poly.isInside(pt1)) pointsFound[0] = true;
if (!pointsFound[1] && poly.isInside(pt2)) pointsFound[1] = true;
if (pt3 != null && !pointsFound[2] && poly.isInside(pt3)) pointsFound[2] = true;
for (j = 0; j < pointsFound.length && pointsFound[j]; j++) ;
if (j == pointsFound.length)
return true;
// No need of checking rest of the layers
break;
}
}
for (j = 0; j < pointsFound.length && pointsFound[j]; j++) ;
if (j == pointsFound.length)
{
assert (false); // test when otherwise the calculation is useless! System.out.println("When?");
return true;
}
}
return false;
}
/**
* Method to determine if neighbor would help to cover the minimum conditions
*
* @return true if error was found (not warning)
*/
static boolean checkExtensionWithNeighbors(Cell cell, Geometric geom, Poly poly, Layer layer, Rectangle2D bounds,
DRCTemplate minWidthRule, int dir, boolean onlyOne, boolean reportError,
Layer.Function.Set layerFunction, ReportInfo reportInfo)
{
double actual = 0;
Point2D left1, left2, left3, right1, right2, right3;
//if (bounds.getWidth() < minWidthRule.value)
String msg = "";
// potential problem along X
if (dir == 0)
{
actual = bounds.getWidth();
msg = "(X axis)";
double leftW = bounds.getMinX() - TINYDELTA;
left1 = new Point2D.Double(leftW, bounds.getMinY());
left2 = new Point2D.Double(leftW, bounds.getMaxY());
left3 = new Point2D.Double(leftW, bounds.getCenterY());
double rightW = bounds.getMaxX() + TINYDELTA;
right1 = new Point2D.Double(rightW, bounds.getMinY());
right2 = new Point2D.Double(rightW, bounds.getMaxY());
right3 = new Point2D.Double(rightW, bounds.getCenterY());
} else
{
actual = bounds.getHeight();
msg = "(Y axis)";
double leftH = bounds.getMinY() - TINYDELTA;
left1 = new Point2D.Double(bounds.getMinX(), leftH);
left2 = new Point2D.Double(bounds.getMaxX(), leftH);
left3 = new Point2D.Double(bounds.getCenterX(), leftH);
double rightH = bounds.getMaxY() + TINYDELTA;
right1 = new Point2D.Double(bounds.getMinX(), rightH);
right2 = new Point2D.Double(bounds.getMaxX(), rightH);
right3 = new Point2D.Double(bounds.getCenterX(), rightH);
}
// see if there is more of this layer adjoining on either side
boolean[] pointsFound = new boolean[3];
pointsFound[0] = pointsFound[1] = pointsFound[2] = false;
Rectangle2D newBounds = new Rectangle2D.Double(bounds.getMinX() - TINYDELTA, bounds.getMinY() - TINYDELTA,
bounds.getWidth() + TINYDELTA * 2, bounds.getHeight() + TINYDELTA * 2);
boolean zeroWide = (bounds.getWidth() == 0 || bounds.getHeight() == 0);
boolean overlapLayer = lookForLayer(poly, cell, layer, DBMath.MATID, newBounds,
left1, left2, left3, pointsFound, layerFunction, reportInfo.ignoreCenterCuts); //) return false;
// if (overlapLayer && !zeroWide) return false;
if (overlapLayer) return false;
// Try the other corner
pointsFound[0] = pointsFound[1] = pointsFound[2] = false;
overlapLayer = lookForLayer(poly, cell, layer, DBMath.MATID, newBounds,
right1, right2, right3, pointsFound, layerFunction, reportInfo.ignoreCenterCuts); //) return false;
// if (overlapLayer && !zeroWide) return false;
if (overlapLayer) return false;
DRCErrorType errorType = DRCErrorType.MINWIDTHERROR;
String extraMsg = msg;
String rule = minWidthRule.ruleName;
// Only when the flat element is fully covered send the warning
// otherwise it is considered an error.
if (zeroWide && overlapLayer)
{
extraMsg = " but covered by other layer";
errorType = DRCErrorType.ZEROLENGTHARCWARN;
rule = null;
}
if (reportError)
createDRCErrorLogger(reportInfo, errorType, extraMsg, cell, minWidthRule.getValue(0), actual, rule,
(onlyOne) ? null : poly, geom, layer, null, null, null);
return !overlapLayer;
}
/**
* Method to examine cell "cell" in the area (lx<=X<=hx, ly<=Y<=hy) for objects
* on layer "layer". Apply transformation "moreTrans" to the objects. If polygons are
* found at (xf1,yf1) or (xf2,yf2) or (xf3,yf3) then sets "p1found/p2found/p3found" to 1.
* If all locations are found, returns true.
*/
static boolean lookForLayer(Poly thisPoly, Cell cell, Layer layer, FixpTransform moreTrans,
Rectangle2D bounds, Point2D pt1, Point2D pt2, Point2D pt3, boolean[] pointsFound,
Layer.Function.Set layerFunction, boolean ignoreCenterCuts)
{
int j;
boolean skip = false;
Rectangle2D newBounds = new Rectangle2D.Double(); // sept 30
for (Iterator it = cell.searchIterator(bounds); it.hasNext();)
{
Geometric g = it.next();
if (g instanceof NodeInst)
{
NodeInst ni = (NodeInst) g;
if (NodeInst.isSpecialNode(ni))
continue; // Nov 16, no need for checking pins or other special nodes;
if (ni.isCellInstance())
{
// compute bounding area inside of sub-cell
FixpTransform rotI = ni.rotateIn();
FixpTransform transI = ni.translateIn();
rotI.preConcatenate(transI);
newBounds.setRect(bounds);
DBMath.transformRect(newBounds, rotI);
// compute new matrix for sub-cell examination
FixpTransform trans = ni.translateOut(ni.rotateOut());
trans.preConcatenate(moreTrans);
if (lookForLayer(thisPoly, (Cell) ni.getProto(), layer, trans, newBounds,
pt1, pt2, pt3, pointsFound, layerFunction, ignoreCenterCuts))
return true;
continue;
}
Technology tech = ni.getProto().getTechnology();
Poly[] layerLookPolyList = tech.getShapeOfNode(ni, false, ignoreCenterCuts, layerFunction);
int tot = layerLookPolyList.length;
if (tot == 0)
continue;
FixpTransform bound = ni.rotateOut();
bound.preConcatenate(moreTrans);
for (int i = 0; i < tot; i++)
{
Poly poly = layerLookPolyList[i];
// sameLayer test required to check if Active layer is not identical to thich actice layer
if (!tech.sameLayer(poly.getLayer(), layer))
{
// This happens when you have implant with VTH implant, Function.Set doesn't distinguish them
if (Job.getDebug())
System.out.println("DRC: Wrong Function.Set with " + layer.getName() + " and " + poly.getLayer().getName());
continue;
}
if (thisPoly != null && poly.polySame(thisPoly)) continue;
poly.transform(bound);
if (poly.isInside(pt1)) pointsFound[0] = true;
if (poly.isInside(pt2)) pointsFound[1] = true;
if (pt3 != null && poly.isInside(pt3)) pointsFound[2] = true;
for (j = 0; j < pointsFound.length && pointsFound[j]; j++) ;
boolean newR = (j == pointsFound.length);
if (newR)
{
return true;
}
// No need of checking rest of the layers?
//break;
}
} else
{
ArcInst ai = (ArcInst) g;
Technology tech = ai.getProto().getTechnology();
Poly[] layerLookPolyList = tech.getShapeOfArc(ai, layerFunction);
int tot = layerLookPolyList.length;
for (int i = 0; i < tot; i++)
{
Poly poly = layerLookPolyList[i];
// sameLayer test required to check if Active layer is not identical to thich actice layer
if (!tech.sameLayer(poly.getLayer(), layer))
{
// This happens when you have implant with VTH implant, Function.Set doesn't distinguish them
if (Job.getDebug())
System.out.println("DRC: Wrong Function.Set with " + layer.getName() + " and " + poly.getLayer().getName());
continue;
}
poly.transform(moreTrans);
if (poly.isInside(pt1)) pointsFound[0] = true;
if (poly.isInside(pt2)) pointsFound[1] = true;
if (pt3 != null && poly.isInside(pt3)) pointsFound[2] = true;
for (j = 0; j < pointsFound.length && pointsFound[j]; j++) ;
boolean newR = (j == pointsFound.length);
if (newR)
return true;
// No need of checking rest of the layers
//break;
}
}
for (j = 0; j < pointsFound.length && pointsFound[j]; j++) ;
if (j == pointsFound.length)
{
System.out.println("When?");
return true;
}
}
if (skip) System.out.println("This case in lookForLayerNew before");
return false;
}
static boolean checkMinWidthInternal(Geometric geom, Layer layer, Poly poly, boolean onlyOne,
DRCTemplate minWidthRule, boolean reportError,
Layer.Function.Set layerFunction, ReportInfo reportInfo)
{
Cell cell = geom.getParent();
if (minWidthRule == null) return false;
double minWidthValue = minWidthRule.getValue(0);
// simpler analysis if manhattan
Rectangle2D bounds = poly.getBox();
// only in case of flat elements represented by a line
// most likely an flat arc, vertical or horizontal.
// It doesn't consider arbitrary angled lines.
// If bounds is null, it might have area if it is non-manhattan
boolean flatPoly = ((bounds == null && GenMath.doublesEqual(poly.getArea(), 0)));
if (flatPoly)
{
Point2D [] points = poly.getPoints();
Point2D from = points[0];
Point2D to = points[1];
// Assuming it is a single segment the flat region
// looking for two distinct points
if (DBMath.areEquals(from, to))
{
boolean found = false;
for (int i = 2; i < points.length; i++)
{
if (!DBMath.areEquals(from, points[i]))
{
to = points[i];
found = true;
break;
}
}
if (!found) // single segment where to == from
{
return false; // skipping this case.
}
}
Point2D center = new Point2D.Double((from.getX() + to.getX()) / 2, (from.getY() + to.getY()) / 2);
// looking if points around the overlapping area are inside another region
// to avoid the error
boolean [] pointsFound = new boolean[3];
pointsFound[0] = pointsFound[1] = pointsFound[2] = false;
boolean found = lookForLayerCoverage(geom, poly, null, null, cell, layer, DBMath.MATID, poly.getBounds2D(),
from, to, center, pointsFound, true, layerFunction, true, reportInfo.ignoreCenterCuts);
if (found) return false; // no error, flat element covered by othe elements.
if (reportError)
createDRCErrorLogger(reportInfo, DRCErrorType.MINWIDTHERROR, null, cell, minWidthValue, 0, minWidthRule.ruleName,
(onlyOne) ? null : poly, geom, layer, null, null, null);
return true;
}
if (bounds != null)
{
boolean tooSmallWidth = DBMath.isGreaterThan(minWidthValue, bounds.getWidth());
boolean tooSmallHeight = DBMath.isGreaterThan(minWidthValue, bounds.getHeight());
if (!tooSmallWidth && !tooSmallHeight) return false;
boolean foundError = false;
if (tooSmallWidth && checkExtensionWithNeighbors(cell, geom, poly, layer, bounds, minWidthRule,
0, onlyOne, reportError, layerFunction, reportInfo))
foundError = true;
if (tooSmallHeight && checkExtensionWithNeighbors(cell, geom, poly, layer, bounds, minWidthRule,
1, onlyOne, reportError, layerFunction, reportInfo))
foundError = true;
return foundError;
}
// nonmanhattan polygon: stop now if it has no size
Poly.Type style = poly.getStyle();
if (style != Poly.Type.FILLED && style != Poly.Type.CLOSED && style != Poly.Type.CROSSED &&
style != Poly.Type.OPENED && style != Poly.Type.OPENEDT1 && style != Poly.Type.OPENEDT2 &&
style != Poly.Type.OPENEDT3 && style != Poly.Type.VECTORS) return false;
// simple check of nonmanhattan polygon for minimum width
bounds = poly.getBounds2D();
double actual = Math.min(bounds.getWidth(), bounds.getHeight());
if (actual < minWidthValue)
{
if (reportError)
createDRCErrorLogger(reportInfo, DRCErrorType.MINWIDTHERROR, null, cell, minWidthValue, actual, minWidthRule.ruleName,
(onlyOne) ? null : poly, geom, layer, null, null, null);
return true;
}
// check distance of each line's midpoint to perpendicular opposite point
Point2D[] points = poly.getPoints();
int count = points.length;
for (int i = 0; i < count; i++)
{
Point2D from;
if (i == 0) from = points[count - 1];
else
from = points[i - 1];
Point2D to = points[i];
if (from.equals(to)) continue;
double ang = DBMath.figureAngleRadians(from, to);
Point2D center = new Point2D.Double((from.getX() + to.getX()) / 2, (from.getY() + to.getY()) / 2);
double perpang = ang + Math.PI / 2;
for (int j = 0; j < count; j++)
{
if (j == i) continue;
Point2D oFrom;
if (j == 0) oFrom = points[count - 1];
else
oFrom = points[j - 1];
Point2D oTo = points[j];
if (oFrom.equals(oTo)) continue;
double oAng = DBMath.figureAngleRadians(oFrom, oTo);
double rAng = ang;
while (rAng > Math.PI) rAng -= Math.PI;
double rOAng = oAng;
while (rOAng > Math.PI) rOAng -= Math.PI;
if (DBMath.doublesEqual(rAng, rOAng))
{
// lines are parallel: see if they are colinear
if (DBMath.isOnLine(from, to, oFrom)) continue;
if (DBMath.isOnLine(from, to, oTo)) continue;
if (DBMath.isOnLine(oFrom, oTo, from)) continue;
if (DBMath.isOnLine(oFrom, oTo, to)) continue;
}
Point2D inter = DBMath.intersectRadians(center, perpang, oFrom, oAng);
if (inter == null) continue;
if (inter.getX() < Math.min(oFrom.getX(), oTo.getX()) || inter.getX() > Math.max(oFrom.getX(), oTo.getX()))
continue;
if (inter.getY() < Math.min(oFrom.getY(), oTo.getY()) || inter.getY() > Math.max(oFrom.getY(), oTo.getY()))
continue;
double fdx = center.getX() - inter.getX();
double fdy = center.getY() - inter.getY();
actual = DBMath.round(Math.sqrt(fdx * fdx + fdy * fdy));
if (actual < minWidthValue)
{
if (reportError)
{
// look between the points to see if it is minimum width or notch
if (poly.isInside(new Point2D.Double((center.getX() + inter.getX()) / 2, (center.getY() + inter.getY()) / 2)))
{
createDRCErrorLogger(reportInfo, DRCErrorType.MINWIDTHERROR, null, cell, minWidthValue,
actual, minWidthRule.ruleName, (onlyOne) ? null : poly, geom, layer, null, null, null);
} else
{
createDRCErrorLogger(reportInfo, DRCErrorType.NOTCHERROR, null, cell, minWidthValue,
actual, minWidthRule.ruleName, (onlyOne) ? null : poly, geom, layer, poly, geom, layer);
}
}
return true;
}
}
}
return false;
}
/**
* Method to determine if it is allowed to have both layers touching.
* special rule for allowing touching:
* the layers are the same and either:
* they connect and are *NOT* contact layers
* or:
* they don't connect and are implant layers (substrate/well)
* @param tech
* @param con
* @param layer1
* @param layer2
* @return true if the layer may touch
*/
static boolean mayTouch(Technology tech, boolean con, Layer layer1, Layer layer2)
{
boolean maytouch = false;
if (tech.sameLayer(layer1, layer2))
{
Layer.Function fun = layer1.getFunction();
if (con)
{
if (!fun.isContact()) maytouch = true;
} else
{
if (fun.isSubstrate()) maytouch = true;
// Special cases for thick actives
else
{
// Searching for THICK bit
int funExtras = layer1.getFunctionExtras();
if (fun.isDiff() && (funExtras&Layer.Function.THICK) != 0)
{
if (Job.LOCALDEBUGFLAG) System.out.println("Thick active found in Quick.checkDist");
maytouch = true;
}
}
}
}
return maytouch;
}
/**
* Method to check if a PrimitiveNode contains a layer that is forbidden in the current technology
* @param ni node to analyze
* @param reportInfo data for the report
* @return True if the node is forbidden
*/
static boolean checkNodeAgainstCombinationRules(NodeInst ni, ReportInfo reportInfo)
{
Cell cell = ni.getParent();
NodeProto np = ni.getProto();
Technology tech = np.getTechnology();
if (np instanceof PrimitiveNode)
{
DRCTemplate forbidRule =
DRC.isForbiddenNode(tech.getPrimNodeIndexInTech((PrimitiveNode)np), -1,
DRCTemplate.DRCRuleType.FORBIDDEN, tech);
if (forbidRule != null)
{
DRC.createDRCErrorLogger(reportInfo, DRC.DRCErrorType.FORBIDDEN, " is not allowed by selected foundry", cell,
-1, -1, forbidRule.ruleName, null, ni, null, null, null, null);
return true;
}
}
return false;
}
/**
* Method to check which combination of OD2 layers are allowed
* @param layer
* @return true if there is an invalid combination of OD2 layers
*/
static boolean checkOD2Combination(Technology tech, NodeInst ni, Layer layer, Map od2Layers,
ReportInfo reportInfo)
{
int funExtras = layer.getFunctionExtras();
boolean notOk = false;
if (layer.getFunction().isImplant() && (funExtras&Layer.Function.THICK) != 0)
{
// Only stores first node found
od2Layers.put(layer, ni);
// More than one type used.
if (od2Layers.size() != 1)
{
for (Map.Entry e : od2Layers.entrySet())
{
Layer lay1 = e.getKey();
if (lay1 == layer) continue;
DRCTemplate rule = isForbiddenNode(lay1.getIndex(), layer.getIndex(), DRCTemplate.DRCRuleType.FORBIDDEN, tech);
if (rule != null)
{
NodeInst node = e.getValue(); // od2Layers.get(lay1);
String message = "- combination of layers '" + layer.getName() + "' and '" + lay1.getName() + "' (in '" +
node.getParent().getName() + ":" + node.getName() +"') not allowed by selected foundry";
createDRCErrorLogger(reportInfo, DRCErrorType.FORBIDDEN, message, ni.getParent(),
-1, -1, rule.ruleName, null, ni, null, null, node, null);
return true;
}
}
}
}
return notOk;
}
/**
* Method to retrieve only Polys from a NodeInst associated with layers with rules.
* @param tech
* @param reportInfo
* @param ni
* @return
*/
static Poly [] getShapeOfNodeBasedOnRules(Technology tech, ReportInfo reportInfo, NodeInst ni)
{
// get layers from the NodeInst. Not a cell at this point.
Layer.Function.Set setFunction = getOnlyLayersWithRulesFromPrimitive(ni.getProto(), tech);
// get all of the polygons on this node
return tech.getShapeOfNode(ni, true, reportInfo.ignoreCenterCuts, setFunction);
}
/**
* Method to retrieve only Polyss from an ArcInst associated with layers with rules.
* @param tech
* @param ai
* @return
*/
static Poly[] getShapeOfArcBasedOnRules(Technology tech, ArcInst ai)
{
// get layers from the NodeInst. Not a cell at this point.
Layer.Function.Set setFunction = getOnlyLayersWithRulesFromArc(ai.getProto(), tech);
// get all of the polygons on this arc
Poly [] arcInstPolyList = tech.getShapeOfArc(ai, setFunction);
// Poly [] arcInstPolyListOLD = tech.getShapeOfArc(ai);
return arcInstPolyList;
}
/**
* Method to look for an interaction between instances "ni1" and "ni2". If it is found,
* return TRUE. If not found, add to the list and return FALSE.
*/
static boolean checkInteraction(Map> instanceInteractionMap,
DRCCheckMode errorTypeSearch,
NodeInst ni1, NodeInst n1Parent, boolean n1cellParameterized,
NodeInst ni2, NodeInst n2Parent, boolean n2cellParameterized,
NodeInst triggerNi, Rectangle2D searchBnd)
{
if (errorTypeSearch == DRCCheckMode.ERROR_CHECK_EXHAUSTIVE) return false;
// must recheck parameterized instances always
if (n1cellParameterized || n2cellParameterized)
return false;
// CheckProto cp = getCheckProto((Cell)ni1.getProto());
// if (cp.cellParameterized) return false;
// cp = getCheckProto((Cell)ni2.getProto());
// if (cp.cellParameterized) return false;
// keep the instances in proper numeric order
InstanceInter dii = new InstanceInter();
if (ni1.getNodeIndex() < ni2.getNodeIndex())
{
NodeInst swapni = ni1; ni1 = ni2; ni2 = swapni;
} else if (ni1 == ni2)
{
int node1Orientation = ni1.getAngle();
if (ni1.isMirroredAboutXAxis()) node1Orientation += 3600;
if (ni1.isMirroredAboutYAxis()) node1Orientation += 7200;
int node2Orientation = ni2.getAngle();
if (ni2.isMirroredAboutXAxis()) node2Orientation += 3600;
if (ni2.isMirroredAboutYAxis()) node2Orientation += 7200;
if (node1Orientation < node2Orientation)
{
NodeInst swapNI = ni1; ni1 = ni2; ni2 = swapNI;
System.out.println("Check this case in Quick.checkInteraction");
}
}
// get essential information about their interaction
dii.cell1 = (Cell)ni1.getProto();
dii.or1 = ni1.getOrient();
// dii.bnd = searchBnd;
dii.cell2 = (Cell)ni2.getProto();
dii.or2 = ni2.getOrient();
// This has to be calculated before the swap
dii.dx = ni2.getAnchorCenterX() - ni1.getAnchorCenterX();
dii.dy = ni2.getAnchorCenterY() - ni1.getAnchorCenterY();
dii.n1Parent = n1Parent;
dii.n2Parent = n2Parent;
dii.triggerNi = triggerNi;
// if found, stop now
// Find the interaction
if (findInteraction(instanceInteractionMap, dii))
return true;
// insert it now
// instanceInteractionList.add(dii);
List list = instanceInteractionMap.get(triggerNi);
assert (list != null); // if this is the first time looking, then list should be built in findInteraction
list.add(dii);
return false;
}
/**
* Method to look for the instance-interaction in "dii" in the global list of instances interactions
* that have already been checked. Returns the entry if it is found, NOINSTINTER if not.
*/
private static boolean findInteraction(Map> instanceInteractionMap, InstanceInter dii)
{
List theList = null;
// new code
theList = instanceInteractionMap.get(dii.triggerNi);
if (theList == null) // first time
{
theList = new ArrayList();
// ni1 as a key should have been added in findInteraction. Big assumption though
instanceInteractionMap.put(dii.triggerNi, theList);
}
//
// boolean newValue = false;
for (InstanceInter thisII : theList)
{
if (thisII.cell1 == dii.cell1 && thisII.cell2 == dii.cell2 &&
thisII.or1.equals(dii.or1) && thisII.or2.equals(dii.or2) &&
thisII.dx == dii.dx && thisII.dy == dii.dy &&
thisII.n1Parent == dii.n1Parent && thisII.n2Parent == dii.n2Parent)
// thisII.bnd == dii.bnd)
{
if (dii.triggerNi == thisII.triggerNi)
{
// newValue = true;
return true;
}
}
}
// for (InstanceInter thisII : instanceInteractionList)
// {
// if (thisII.cell1 == dii.cell1 && thisII.cell2 == dii.cell2 &&
// thisII.or1.equals(dii.or1) && thisII.or2.equals(dii.or2) &&
// thisII.dx == dii.dx && thisII.dy == dii.dy &&
// thisII.n1Parent == dii.n1Parent && thisII.n2Parent == dii.n2Parent)
//// thisII.bnd == dii.bnd)
// {
// if (dii.triggerNi == thisII.triggerNi)
// {
// assert(newValue);
// return true;
// }
// }
// }
// assert(!newValue);
return false;
}
/*********************************** QUICK DRC ERROR REPORTING ***********************************/
public static enum DRCErrorType
{
// the different types of errors
SPACINGERROR, MINWIDTHERROR, NOTCHERROR, MINSIZEERROR, BADLAYERERROR, LAYERSURROUNDERROR,
MINAREAERROR, ENCLOSEDAREAERROR, SURROUNDERROR, FORBIDDEN, RESOLUTION, CUTERROR, SLOTSIZEERROR,
CROOKEDERROR,
// Different types of warnings
ZEROLENGTHARCWARN, TECHMIXWARN
}
public static class ReportInfo
{
/** DRC preferences */ DRCPreferences dp;
/** error type search */ DRCCheckMode errorTypeSearch;
/** minimum output grid resolution */ ECoord minAllowedResolution;
/** true to ignore center cuts in large contacts. */ boolean ignoreCenterCuts;
/** maximum area to examine (the worst spacing rule). */ double worstInteractionDistance;
/** time stamp for numbering networks. */ int checkTimeStamp;
/** for numbering networks. */ int checkNetNumber;
/** total errors found in all threads. */ int totalSpacingMsgFound;
/** for logging errors */ ErrorLogger errorLogger;
/** for interactive error logging */ boolean interactiveLogger;
/** to cache current extra bits */ int activeSpacingBits = 0;
Map exclusionMap = new HashMap(); // The DRCExclusion object lists areas where Generic:DRC-Nodes exist to ignore errors.
boolean inMemory;
public ReportInfo(ErrorLogger eL, Technology tech, DRCPreferences dp, boolean specificGeoms)
{
errorLogger = eL;
this.dp = dp;
interactiveLogger = dp.interactiveLog;
activeSpacingBits = DRC.getActiveBits(tech, dp);
worstInteractionDistance = getWorstSpacingDistance(tech, -1);
// minimim resolution different from zero if flag is on otherwise stays at zero (default)
minAllowedResolution = dp.getResolution(tech);
ignoreCenterCuts = dp.ignoreCenterCuts;
inMemory = dp.storeDatesInMemory;
errorTypeSearch = dp.errorType;
if (specificGeoms)
{
errorTypeSearch = DRC.DRCCheckMode.ERROR_CHECK_CELL;
}
}
}
public static void createDRCErrorLogger(ReportInfo reportInfo,
DRCErrorType errorType, String msg,
Cell cell, double limit, double actual, String rule,
PolyBase poly1, Geometric geom1, Layer layer1,
PolyBase poly2, Geometric geom2, Layer layer2)
{
ErrorLogger errorLogger = reportInfo.errorLogger;
if (errorLogger == null) return;
// if this error is in an ignored area, don't record it
StringBuffer DRCexclusionMsg = new StringBuffer();
if (reportInfo.exclusionMap != null && reportInfo.exclusionMap.get(cell) != null)
{
// determine the bounding box of the error
List polyList = new ArrayList(2);
List geomList = new ArrayList(2);
polyList.add(poly1); geomList.add(geom1);
if (poly2 != null)
{
polyList.add(poly2);
geomList.add(geom2);
}
boolean found = checkExclusionMap(reportInfo.exclusionMap, cell, polyList, geomList, DRCexclusionMsg);
// At least one DRC exclusion that contains both
if (found) return;
}
// describe the error
Cell np1 = (geom1 != null) ? geom1.getParent() : null;
Cell np2 = (geom2 != null) ? geom2.getParent() : null;
// Message already logged
boolean onlyWarning = (errorType == DRCErrorType.ZEROLENGTHARCWARN || errorType == DRCErrorType.TECHMIXWARN);
// Until a decent algorithm is in place for detecting repeated errors, ERROR_CHECK_EXHAUSTIVE might report duplicate errros
if ( geom2 != null && reportInfo.errorTypeSearch != DRCCheckMode.ERROR_CHECK_EXHAUSTIVE &&
errorLogger.findMessage(cell, geom1, geom2.getParent(), geom2, !onlyWarning))
return;
StringBuffer errorMessage = new StringBuffer();
DRCCheckLogging loggingType = reportInfo.dp.errorLoggingType;
int sortKey = cell.hashCode(); // 0;
if (errorType == DRCErrorType.SPACINGERROR || errorType == DRCErrorType.NOTCHERROR || errorType == DRCErrorType.SURROUNDERROR)
{
// describe spacing width error
if (errorType == DRCErrorType.SPACINGERROR)
errorMessage.append("Spacing");
else if (errorType == DRCErrorType.SURROUNDERROR)
errorMessage.append("Surround");
else
errorMessage.append("Notch");
if (layer1 == layer2)
errorMessage.append(" (layer '" + layer1.getName() + "')");
errorMessage.append(": ");
if (np1 != np2)
{
errorMessage.append(np1 + ", ");
} else if (np1 != cell && np1 != null)
{
errorMessage.append("[in " + np1 + "] ");
}
if (geom1 != null)
errorMessage.append(geom1);
if (layer1 != layer2)
errorMessage.append(", layer '" + layer1.getName() + "'");
if (actual < 0) errorMessage.append(" OVERLAPS (BY " + TextUtils.formatDistance(limit-actual) + ") ");
else if (actual == 0) errorMessage.append(" TOUCHES ");
else errorMessage.append(" LESS (BY " + TextUtils.formatDistance(limit-actual) + ") THAN " + TextUtils.formatDistance(limit) +
((geom2!=null)?" TO ":""));
if (np1 != np2 && np2 != null)
errorMessage.append(np2 + ", ");
if (geom2 != null)
errorMessage.append(geom2);
if (layer1 != layer2)
errorMessage.append(", layer '" + layer2.getName() + "'");
if (msg != null)
errorMessage.append("; " + msg);
} else
{
// describe minimum width/size or layer error
StringBuffer errorMessagePart2 = null;
switch (errorType)
{
case CROOKEDERROR:
errorMessage.append("Crooked error:");
errorMessagePart2 = new StringBuffer(" is not horizontal nor vertical");
break;
case RESOLUTION:
errorMessage.append("Resolution error:");
errorMessagePart2 = new StringBuffer(msg);
break;
case FORBIDDEN:
errorMessage.append("Forbidden error:");
errorMessagePart2 = new StringBuffer(msg);
break;
case SLOTSIZEERROR:
errorMessage.append("Slot size error:");
errorMessagePart2 = new StringBuffer(", layer '" + layer1.getName() + "'");
errorMessagePart2.append(" BIGGER THAN " + TextUtils.formatDistance(limit) + " IN LENGTH (IS " + TextUtils.formatDistance(actual) + ")");
break;
case MINAREAERROR:
errorMessage.append("Minimum area error:");
errorMessagePart2 = new StringBuffer(", layer '" + layer1.getName() + "'");
errorMessagePart2.append(" LESS THAN " + TextUtils.formatDistance(limit) + " IN AREA (IS " + TextUtils.formatDistance(actual) + ")");
break;
case ENCLOSEDAREAERROR:
errorMessage.append("Enclosed area error:");
errorMessagePart2 = new StringBuffer(", layer '" + layer1.getName() + "'");
errorMessagePart2.append(" LESS THAN " + TextUtils.formatDistance(limit) + " IN AREA (IS " + TextUtils.formatDistance(actual) + ")");
break;
case TECHMIXWARN:
errorMessage.append("Technology mixture warning:");
errorMessagePart2 = new StringBuffer(msg);
break;
case ZEROLENGTHARCWARN:
errorMessage.append("Zero width warning:");
errorMessagePart2 = new StringBuffer(msg); break;
case CUTERROR:
errorMessage.append("Maximum cut error" + ((msg != null) ? ("(" + msg + "):") : ""));
errorMessagePart2 = new StringBuffer(", layer '" + layer1.getName() + "'");
errorMessagePart2.append(" BIGGER THAN " + TextUtils.formatDistance(limit) + " WIDE (IS " + TextUtils.formatDistance(actual) + ")");
break;
case MINWIDTHERROR:
errorMessage.append("Minimum width/height error" + ((msg != null) ? ("(" + msg + "):") : ""));
errorMessagePart2 = new StringBuffer(", layer '" + layer1.getName() + "'");
errorMessagePart2.append(" LESS THAN " + TextUtils.formatDistance(limit) + " WIDE (IS " + TextUtils.formatDistance(actual) + ")");
break;
case MINSIZEERROR:
errorMessage.append("Minimum size error on " + msg + ":");
errorMessagePart2 = new StringBuffer(" LESS THAN " + TextUtils.formatDistance(limit) + " IN SIZE (IS " + TextUtils.formatDistance(actual) + ")");
break;
case BADLAYERERROR:
errorMessage.append("Invalid layer ('" + layer1.getName() + "'):");
break;
case LAYERSURROUNDERROR:
errorMessage.append("Layer surround error: " + msg);
errorMessagePart2 = new StringBuffer(", layer '" + layer1.getName() + "'");
String layerName = (layer2 != null) ? layer2.getName() : "Select";
errorMessagePart2.append(" NEEDS SURROUND OF LAYER '" + layerName + "' BY " + limit);
break;
default:
assert(false); // it should not happen
}
errorMessage.append(" " + cell + " ");
if (geom1 != null)
{
errorMessage.append(geom1);
}
// only when is flat -> use layer index for sorting
if (layer1 != null && loggingType == DRCCheckLogging.DRC_LOG_FLAT) sortKey = layer1.getIndex();
// errorMessagePart2 is at least null for BADLAYERERROR
if (errorMessagePart2 != null) errorMessage.append(errorMessagePart2);
}
if (rule != null && rule.length() > 0) errorMessage.append(" [rule '" + rule + "']");
errorMessage.append(DRCexclusionMsg);
List geomList = new ArrayList();
List polyList = new ArrayList();
if (poly1 != null) polyList.add(poly1); //else
if (geom1 != null) geomList.add(geom1);
if (poly2 != null) polyList.add(poly2); //else
if (geom2 != null) geomList.add(geom2);
switch (loggingType)
{
case DRC_LOG_PER_CELL:
errorLogger.setGroupName(sortKey, cell.getName());
break;
case DRC_LOG_PER_RULE:
String theRuleName = rule;
if (theRuleName == null)
theRuleName = errorType.name();
sortKey = theRuleName.hashCode();
if (errorLogger.getGroupName(sortKey) == null) // only if nothing was found
errorLogger.setGroupName(sortKey, theRuleName);
break;
}
errorLogger.logMessage(errorMessage.toString(), geomList, polyList, cell, sortKey, !onlyWarning);
// Temporary display of errors.
if (reportInfo.interactiveLogger)
Job.getUserInterface().termLogging(errorLogger, false, false);
}
private static boolean checkExclusionMap(Map exclusionMap, Cell cell, List polyList,
List geomList, StringBuffer DRCexclusionMsg) {
Area area = exclusionMap.get(cell);
if (area == null) return false;
int count = 0, i = -1;
for (PolyBase thisPoly : polyList) {
i++;
if (thisPoly == null)
continue; // MinNode case
boolean found = area.contains(thisPoly.getBounds2D());
if (found) count++;
else {
Rectangle2D rect = (geomList.get(i) != null) ? geomList.get(i).getBounds() : thisPoly.getBounds2D();
DRCexclusionMsg.append("\n\t(DRC Exclusion in '" + cell.getName() + "' does not completely contain element (" +
rect.getMinX() + "," + rect.getMinY() + ") (" + rect.getMaxX() + "," + rect.getMaxY() + "))");
}
}
// At least one DRC exclusion that contains both
// if (count == polyList.size())
if (count >= 1) // at one element is inside the DRC exclusion
return true;
return false;
}
/*********************************** END DRC ERROR REPORTING ***********************************/
private static class StoreDRCInfo
{
long date;
int bits;
StoreDRCInfo(long d, int b)
{
date = d;
bits = b;
}
}
/** key of Variable for last valid DRC date on a Cell. Only area rules */
// private static final int DRC_BIT_AREA = 01; /* Min area condition */
private static final int DRC_BIT_EXTENSION = 02; /* Coverage DRC condition */
private static final int DRC_BIT_ST_FOUNDRY = 04; /* For ST foundry selection */
private static final int DRC_BIT_TSMC_FOUNDRY = 010; /* For TSMC foundry selection */
private static final int DRC_BIT_MOSIS_FOUNDRY = 020; /* For Mosis foundry selection */
private static final int DRC_BIT_NONE_FOUNDRY = 040; /* For NONE foundry selection */
public enum DRCCheckMinArea
{
AREA_BASIC("Simple") /*brute force algorithm*/, AREA_LOCAL("Local"), AREA_MANHATTAN("Manhattan");
private final String name;
DRCCheckMinArea(String s)
{
name = s;
}
public String toString() {return name;}
}
public enum DRCCheckLogging
{
DRC_LOG_FLAT("Flat")/*original strategy*/, DRC_LOG_PER_CELL("By Cell"), DRC_LOG_PER_RULE("By Rule");
private final String name;
DRCCheckLogging(String s)
{
name = s;
}
public String toString() {return name;}
}
/** Control different level of error checking */
public enum DRCCheckMode
{
ERROR_CHECK_DEFAULT (0), /** DRC stops after first error between 2 nodes is found (default) */
ERROR_CHECK_CELL (1), /** DRC stops after first error per cell is found */
ERROR_CHECK_EXHAUSTIVE (2); /** DRC checks all combinations */
private final int mode; // mode
DRCCheckMode(int m) {this.mode = m; assert m == ordinal(); }
public int mode() { return this.mode; }
public String toString() {return name();}
}
/****************************** TOOL CONTROL ******************************/
/**
* The constructor sets up the DRC tool.
*/
private DRC()
{
super("drc");
}
/**
* Method to initialize the DRC tool.
*/
public void init()
{
setOn();
}
/**
* Method to retrieve the singleton associated with the DRC tool.
* @return the DRC tool.
*/
public static DRC getDRCTool() { return tool; }
private static void includeGeometric(Geometric geom)
{
Cell cell = geom.getParent();
assert !THREAD_SAFE_DRC || Job.isClientThread();
synchronized (cellsToCheck)
{
Set cellSet = cellsToCheck.get(cell);
if (cellSet == null)
{
cellSet = new HashSet();
cellsToCheck.put(cell, cellSet);
}
cellSet.add(geom);
}
}
private static void doIncrementalDRCTask(DRCPreferences dp, Cell cellToCheck)
{
if (!dp.incrementalDRC) return;
assert !THREAD_SAFE_DRC || Job.isClientThread();
if (incrementalRunning) return;
Set cellSet = null;
// get a cell to check
synchronized (cellsToCheck)
{
if (cellToCheck != null)
cellSet = cellsToCheck.get(cellToCheck);
if (cellSet == null && cellsToCheck.size() > 0)
{
cellToCheck = cellsToCheck.keySet().iterator().next();
cellSet = cellsToCheck.get(cellToCheck);
}
if (cellSet != null)
cellsToCheck.remove(cellToCheck);
}
if (cellToCheck == null) return; // nothing to do
// don't check if cell not in database anymore
if (!cellToCheck.isLinked()) return;
// Handling clipboard case (one type of hidden libraries)
if (cellToCheck.getLibrary().isHidden()) return;
// if there is a cell to check, do it
if (cellSet != null)
{
Geometric [] objectsToCheck = new Geometric[cellSet.size()];
int i = 0;
for(Geometric geom : cellSet)
objectsToCheck[i++] = geom;
// cleaning previous errors on the cells to check now.
for (Geometric geo : objectsToCheck)
{
Cell c = geo.getParent();
List getAllLogs = errorLoggerIncremental.getAllLogs(c);
Job.updateIncrementalDRCErrors(c, null, getAllLogs);
}
new CheckDRCIncrementally(dp, cellToCheck, objectsToCheck, cellToCheck.getTechnology().isScaleRelevant());
}
}
/**
* Handles database changes of a Job.
* @param oldSnapshot database snapshot before Job.
* @param newSnapshot database snapshot after Job and constraint propagation.
* @param undoRedo true if Job was Undo/Redo job.
*/
public void endBatch(Snapshot oldSnapshot, Snapshot newSnapshot, boolean undoRedo)
{
DRCPreferences dp = new DRCPreferences(false);
if (dp.incrementalDRC) {
for (CellId cellId: newSnapshot.getChangedCells(oldSnapshot)) {
Cell cell = Cell.inCurrentThread(cellId);
if (cell == null) continue;
CellBackup oldBackup = oldSnapshot.getCell(cellId);
CellRevision oldRevision = oldBackup != null ? oldBackup.cellRevision : null;
for (Iterator it = cell.getNodes(); it.hasNext(); ) {
NodeInst ni = it.next();
ImmutableNodeInst d = ni.getD();
if (oldRevision == null || oldRevision.getNodeById(d.nodeId) != d)
includeGeometric(ni);
}
for (Iterator it = cell.getArcs(); it.hasNext(); ) {
ArcInst ai = it.next();
ImmutableArcInst d = ai.getD();
if (oldRevision == null || oldRevision.getArcById(d.arcId) != d)
includeGeometric(ai);
}
}
}
Library curLib = Library.getCurrent();
if (curLib == null) return;
Cell cellToCheck = curLib.getCurCell();
doIncrementalDRCTask(new DRC.DRCPreferences(false), cellToCheck);
}
/****************************** DRC INTERFACE ******************************/
public static ErrorLogger getDRCErrorLogger(boolean layout, String extraMsg)
{
String title = (layout) ? "Layout " : "Schematic ";
return ErrorLogger.newInstance(title + "DRC (full)" + ((extraMsg != null) ? extraMsg:""));
}
public static ErrorLogger getDRCIncrementalLogger() {
assert !THREAD_SAFE_DRC || Job.isClientThread();
return errorLoggerIncremental;
}
/**
* This method generates a DRC job from the GUI or for a bash script.
*/
public static void checkDRCHierarchically(DRCPreferences dp, Cell cell, List objs, Rectangle2D bounds,
GeometryHandler.GHMode mode, boolean onlyArea)
{
if (cell == null) return;
boolean isLayout = true; // hierarchical check of layout by default
if (cell.isSchematic() || cell.getTechnology() == Schematics.tech() ||
cell.isIcon() || cell.getTechnology() == Artwork.tech())
// hierarchical check of schematics
isLayout = false;
if (mode == null) mode = GeometryHandler.GHMode.ALGO_SWEEP;
new CheckDRCHierarchically(dp, cell, isLayout, objs, bounds, mode, onlyArea);
}
/**
* Base class for checking design rules.
*
*/
public static class CheckDRCJob extends Job
{
Cell cell;
DRCPreferences dp;
boolean isLayout; // to check layout
private static String getJobName(Cell cell) { return "Design-Rule Check " + cell; }
protected CheckDRCJob(Cell cell, Listener tool, Priority priority, DRCPreferences dp, boolean layout)
{
super(getJobName(cell), tool, THREAD_SAFE_DRC ? Job.Type.SERVER_EXAMINE : Job.Type.CLIENT_EXAMINE, null, null, priority);
this.cell = cell;
this.dp = dp;
this.isLayout = layout;
}
// never used
public boolean doIt() { return (false);}
}
/**
* Class for hierarchical DRC for layout and schematics
*/
private static class CheckDRCHierarchically extends CheckDRCJob
{
Rectangle2D bounds;
private GeometryHandler.GHMode mergeMode; // to select the merge algorithm
private boolean onlyArea;
private Geometric[] geoms;
/**
* Check bounds within cell. If bounds is null, check entire cell.
* @param cell
* @param layout
* @param bounds
*/
protected CheckDRCHierarchically(DRCPreferences dp, Cell cell, boolean layout, List objs,
Rectangle2D bounds, GeometryHandler.GHMode mode,
boolean onlyA)
{
super(cell, tool, Job.Priority.USER, dp, layout);
this.bounds = bounds;
this.mergeMode = mode;
this.onlyArea = onlyA;
if (objs != null)
{
this.geoms = new Geometric[objs.size()];
objs.toArray(this.geoms);
}
startJob();
}
public boolean doIt()
{
ElapseTimer timer = ElapseTimer.createInstance();
timer.start();
ErrorLogger errorLog = getDRCErrorLogger(isLayout, null);
checkNetworks(errorLog, cell, isLayout);
if (isLayout)
Quick.checkDesignRules(errorLog, cell, geoms, null, bounds, this, dp, mergeMode, onlyArea);
else
Schematic.doCheck(errorLog, cell, geoms, dp);
errorLog.termLogging(true);
timer.end();
int errorCount = errorLog.getNumErrors();
int warnCount = errorLog.getNumWarnings();
System.out.println(errorCount + " errors and " + warnCount + " warnings found (took " + timer.toString() + ")");
if (onlyArea)
Job.getUserInterface().termLogging(errorLog, false, false); // otherwise the errors don't appear
return true;
}
}
private static class CheckDRCIncrementally extends CheckDRCJob
{
Geometric [] objectsToCheck;
Cell cellToCheck;
ErrorLogger errorLog;
protected CheckDRCIncrementally(DRCPreferences dp, Cell cell, Geometric[] objectsToCheck, boolean layout)
{
super(cell, tool, Job.Priority.ANALYSIS, dp, layout);
this.objectsToCheck = objectsToCheck;
Library curLib = Library.getCurrent();
if (curLib == null) return;
cellToCheck = curLib.getCurCell();
if (THREAD_SAFE_DRC) {
assert Job.isClientThread();
incrementalRunning = true;
}
startJob();
}
public boolean doIt()
{
if (!THREAD_SAFE_DRC)
incrementalRunning = true;
errorLog = getDRCErrorLogger(isLayout, null);
if (isLayout)
Quick.checkDesignRules(errorLog, cell, objectsToCheck, null, null, null, dp,
GeometryHandler.GHMode.ALGO_SWEEP, false);
else
Schematic.doCheck(errorLog, cell, objectsToCheck, dp);
int errorsFound = errorLog.getNumErrors();
if (errorsFound > 0)
System.out.println("Incremental DRC found " + errorsFound + " errors/warnings in "+ cell);
if (THREAD_SAFE_DRC) {
fieldVariableChanged("errorLog");
} else {
errorLoggerIncremental.addMessages(errorLog);
errorLoggerIncremental.termLogging(true);
incrementalRunning = false;
doIncrementalDRCTask(dp, cellToCheck);
}
return true;
}
public void terminateOK() {
if (THREAD_SAFE_DRC) {
errorLoggerIncremental.addMessages(errorLog);
errorLoggerIncremental.termLogging(true);
incrementalRunning = false;
doIncrementalDRCTask(dp, cellToCheck);
}
}
}
/****************************** DESIGN RULE CONTROL ******************************/
/** The Technology whose rules are cached. */ private static Technology currentTechnology = null;
/**
* Method to build a Rules object that contains the current design rules for a Technology.
* The DRC dialogs use this to hold the values while editing them.
* It also provides a cache for the design rule checker.
* @param tech the Technology to examine.
* @return a new Rules object with the design rules for the given Technology.
*/
public static DRCRules getRules(Technology tech)
{
XMLRules currentRules = tech.getCachedRules();
if (currentRules != null && tech == currentTechnology) return currentRules;
// constructing design rules: start with factory rules
currentRules = tech.getFactoryDesignRules();
if (currentRules != null)
{
// add overrides
String override = ""; // TODO: propagate getDRCOverrides
// String override = dp.getDRCOverrides(tech);
currentRules.applyDRCOverrides(override, tech);
}
// remember technology whose rules are cached
currentTechnology = tech;
tech.setCachedRules(currentRules);
return currentRules;
}
/**
* Method to load a full set of design rules for a Technology.
* @param tech the Technology to load.
* @param newRules a complete design rules object.
*/
public static void setRules(DRC.DRCPreferences dp, Technology tech, DRCRules newRules)
{
// get factory design rules
DRCRules factoryRules = tech.getFactoryDesignRules();
// determine override differences from the factory rules
String changes = Technology.getRuleDifferences(factoryRules, newRules).toString();
if (Job.LOCALDEBUGFLAG)
System.out.println("This function needs attention");
// get current overrides of factory rules
String override = dp.getDRCOverrides(tech);
// if the differences are the same as before, stop
if (changes.equals(override)) return;
// update the preference for the rule overrides
dp.setDRCOverrides(tech, changes);
// update variables on the technology
tech.setRuleVariables(newRules);
// flush the cache of rules
if (currentTechnology == tech) currentTechnology = null;
}
/****************************** INDIVIDUAL DESIGN RULES ******************************/
/**
* Method to find the worst spacing distance in the design rules.
* Finds the largest spacing rule in the Technology.
* @param tech the Technology to examine.
* @param lastMetal
* @return the largest spacing distance in the Technology. Zero if nothing found
*/
public static double getWorstSpacingDistance(Technology tech, int lastMetal)
{
DRCRules rules = getRules(tech);
if (rules == null)
return 0;
MutableDouble mutableDist = new MutableDouble(0);
boolean found = rules.getWorstSpacingDistance(lastMetal, mutableDist);
if (!found && Job.getDebug())
System.out.println("Not found in DRC.getWorstSpacingDistance");
return mutableDist.doubleValue();
}
/**
* Method to find the worst spacing distance in the design rules for a given list of layers.
* Finds the largest spacing rule in the Technology for those layers.
* @param tech the Technology to examine.
* @param layers List of layers to check
* @param mutableDist the largest spacing distance in the Technology for the given layers. Zero if nothing found
* @return true if a valid value was found
*/
public static boolean getWorstSpacingDistance(Technology tech, Set layers, MutableDouble mutableDist)
{
mutableDist.setValue(0);
DRCRules rules = getRules(tech);
if (rules == null)
return false;
boolean found = rules.getWorstSpacingDistance(layers, mutableDist);
// false when cell has only pins for example
return found;
}
/**
* Method to find the maximum design-rule distance around a layer.
* @param layer the Layer to examine.
* @return the maximum design-rule distance around the layer. -1 if nothing found.
*/
public static boolean getMaxSurround(Layer layer, double maxSize, MutableDouble mutableDist)
{
Technology tech = layer.getTechnology();
mutableDist.setValue(-1);
if (tech == null) return false; // case when layer is a Graphics
DRCRules rules = getRules(tech);
if (rules == null) return false;
return (rules.getMaxSurround(layer, maxSize, mutableDist));
}
/**
* Method to retrieve only the layer functions with rules in a node.
* A Layer.Function.Set object is needed to pass it to tech.getShapeOfNode()
* @param np the Primitive node to obtain.
* @param tech the Technology that holds the Primitive node.
* @return a Layer.Function.Set describing the rules.
*/
public static Layer.Function.Set getOnlyLayersWithRulesFromPrimitive(NodeProto np, Technology tech)
{
// Cell case
if (!(np instanceof PrimitiveNode)) return null;
PrimitiveNode pn = (PrimitiveNode)np;
DRCRules rules = getRules(tech);
if (rules == null) return null;
// In this case, no need of checking the electrical layers
// since we want the valid layers from the DRC perspective, not the geometry yet
Layer.Function.Set functionSet = new Layer.Function.Set();
Technology.NodeLayer[] layers = pn.getNodeLayers();
if (pn.getElectricalLayers() != null)
layers = pn.getElectricalLayers();
for (Technology.NodeLayer layer : layers)
{
Layer l = layer.getLayer();
if (rules.hasLayerRules(l))
functionSet.add(l);
}
return functionSet;
}
public static Layer.Function.Set getOnlyLayersWithRulesFromArc(ArcProto ap, Technology tech)
{
DRCRules rules = getRules(tech);
if (rules == null) return null;
// in case the arc has valid electric layers
Layer.Function.Set functionSet = new Layer.Function.Set();
Technology.ArcLayer[] layers = ap.getArcLayers();
for (Technology.ArcLayer layer : layers)
{
Layer l = layer.getLayer();
if (rules.hasLayerRules(l))
functionSet.add(l);
}
return functionSet;
}
/**
* Method to find the edge spacing rule between two layer.
* @param layer1 the first layer.
* @param layer2 the second layer.
* @return the edge rule distance between the layers.
* Returns null if there is no edge spacing rule.
*/
public static DRCTemplate getEdgeRule(Layer layer1, Layer layer2)
{
Technology tech = layer1.getTechnology();
DRCRules rules = getRules(tech);
if (rules == null) return null;
return (rules.getEdgeRule(layer1, layer2));
}
/**
* Method to find the spacing rule between two layer.
* @param layer1 the first layer.
* @param geo1
* @param layer2 the second layer.
* @param geo2
* @param connected true to find the distance when the layers are connected.
* @param multiCut true to find the distance when this is part of a multicut contact.
* @param wideS widest polygon
* @param length length of the intersection
* @return the spacing rule between the layers.
* Returns null if there is no spacing rule.
*/
public static DRCTemplate getSpacingRule(Layer layer1, Geometric geo1, Layer layer2, Geometric geo2,
boolean connected, int multiCut, double wideS, double length)
{
Technology tech = layer1.getTechnology();
DRCRules rules = getRules(tech);
if (rules == null) return null;
return (rules.getSpacingRule(layer1, geo1, layer2, geo2, connected, multiCut, wideS, length));
}
/**
* Method to find all possible rules of DRCRuleType type associated a layer.
* @param layer1 the layer whose rules are desired.
* @return a list of DRCTemplate objects associated with the layer.
*/
public static List getRules(Layer layer1, DRCTemplate.DRCRuleType type)
{
Technology tech = layer1.getTechnology();
DRCRules rules = getRules(tech);
if (rules == null)
return null;
return (rules.getRules(layer1, type));
}
/**
* Method to find the extension rule between two layer.
* @param layer1 the first layer.
* @param layer2 the second layer.
* @param isGateExtension to decide between the rule EXTENSIONGATE or EXTENSION
* @return the extension rule between the layers.
* Returns null if there is no extension rule.
*/
public static DRCTemplate getExtensionRule(Layer layer1, Layer layer2, boolean isGateExtension)
{
Technology tech = layer1.getTechnology();
DRCRules rules = getRules(tech);
if (rules == null) return null;
return (rules.getExtensionRule(layer1, layer2, isGateExtension));
}
/**
* Method to tell whether there are any design rules between two layers.
* @param layer1 the first Layer to check.
* @param layer2 the second Layer to check.
* @return true if there are design rules between the layers.
*/
public static boolean isAnySpacingRule(Layer layer1, Layer layer2)
{
Technology tech = layer1.getTechnology();
DRCRules rules = getRules(tech);
if (rules == null) return false;
return (rules.isAnySpacingRule(layer1, layer2));
}
/**
* Method to get the minimum rule for a Layer
* where is the rule type. E.g. MinWidth or Area
* @param layer the Layer to examine.
* @param type rule type
* @return the minimum width rule for the layer.
* Returns null if there is no minimum width rule.
*/
public static DRCTemplate getMinValue(Layer layer, DRCTemplate.DRCRuleType type)
{
Technology tech = layer.getTechnology();
if (tech == null) return null;
DRCRules rules = getRules(tech);
if (rules == null) return null;
return (rules.getMinValue(layer, type));
}
/**
* Determine if node represented by index in DRC mapping table is forbidden under
* this foundry.
*/
public static DRCTemplate isForbiddenNode(int index1, int index2, DRCTemplate.DRCRuleType type, Technology tech)
{
DRCRules rules = getRules(tech);
if (rules == null) return null;
return isForbiddenNode(rules, index1, index2, type);
}
public static DRCTemplate isForbiddenNode(DRCRules rules, int index1, int index2, DRCTemplate.DRCRuleType type)
{
int index = index1; // In case of primitive nodes
if (index2 != -1 )
index = rules.getRuleIndex(index1, index2);
else
index += rules.getTechnology().getNumLayers(); // Node forbidden
return (rules.isForbiddenNode(index, type));
}
/**
* Method to get the minimum size rule for a NodeProto.
* @param np the NodeProto to examine.
* @return the minimum size rule for the NodeProto.
* Returns null if there is no minimum size rule.
*/
public static PrimitiveNode.NodeSizeRule getMinSize(NodeProto np)
{
if (np instanceof Cell) return null;
PrimitiveNode pnp = (PrimitiveNode)np;
return pnp.getMinSizeRule();
}
/****************************** SUPPORT FOR DESIGN RULES ******************************/
/**
* Method to clean those cells that were marked with a valid date due to
* changes in the DRC rules.
* @param f
*/
public static void cleanCellsDueToFoundryChanges(Technology tech, Foundry f, DRCPreferences dp)
{
// Need to clean cells using this foundry because the rules might have changed.
System.out.println("Cleaning good DRC dates in cells using '" + f.getType().getName() +
"' in '" + tech.getTechName() + "'");
Set cleanSpacingDRCDate = new HashSet();
Set cleanAreaDRCDate = new HashSet();
int bit = f.getType().getBit();
for (Iterator it = Library.getLibraries(); it.hasNext();)
{
Library lib = it.next();
for (Iterator itC = lib.getCells(); itC.hasNext();)
{
Cell cell = itC.next();
if (cell.getTechnology() != tech) continue;
StoreDRCInfo data = getCellGoodDRCDateAndBits(cell, true, !dp.storeDatesInMemory);
if (data != null) // there is data
{
// It was marked as valid with previous set of rules
if ((data.bits & bit) != 0)
cleanSpacingDRCDate.add(cell);
}
// Checking area bit
data = getCellGoodDRCDateAndBits(cell, false, !dp.storeDatesInMemory);
if (data != null) // there is data
{
// It was marked as valid with previous set of rules
if ((data.bits & bit) != 0)
cleanAreaDRCDate.add(cell);
}
}
}
addDRCUpdate(0, null, cleanSpacingDRCDate, null, cleanAreaDRCDate, null, dp);
}
/**
* Method to retrieve Date in miliseconds if a valid date for the given Key is found
* @param cell
* @param key
* @return true if a valid date is found
*/
private static boolean getDateStored(Cell cell, Variable.Key key, MutableLong date)
{
long lastDRCDateInMilliseconds;
// disk version
Long lastDRCDateAsLong = cell.getVarValue(key, Long.class); // new strategy
if (lastDRCDateAsLong != null) {
lastDRCDateInMilliseconds = lastDRCDateAsLong.longValue();
} else {
Integer[] lastDRCDateAsInts = cell.getVarValue(key, Integer[].class);
if (lastDRCDateAsInts == null) return false;
long lastDRCDateInSecondsHigh = lastDRCDateAsInts[0].intValue();
long lastDRCDateInSecondsLow = lastDRCDateAsInts[1].intValue();
lastDRCDateInMilliseconds = (lastDRCDateInSecondsHigh << 32) | (lastDRCDateInSecondsLow & 0xFFFFFFFFL);
}
date.setValue(lastDRCDateInMilliseconds);
return true;
}
/**
* Method to extract the corresponding DRC bits stored in disk(database) or
* in memory for a given cell for further analysis
* @param cell
* @param fromDisk
* @return temporary class containing date and bits if available. Null if nothing is found
*/
private static StoreDRCInfo getCellGoodDRCDateAndBits(Cell cell, boolean spacingCheck, boolean fromDisk)
{
Map storedDRCDate = storedSpacingDRCDate;
Variable.Key dateKey = Layout.DRC_LAST_GOOD_DATE_SPACING;
Variable.Key bitKey = Layout.DRC_LAST_GOOD_BIT_SPACING;
if (!spacingCheck)
{
storedDRCDate = storedAreaDRCDate;
dateKey = Layout.DRC_LAST_GOOD_DATE_AREA;
bitKey = null;
}
StoreDRCInfo data = storedDRCDate.get(cell);
boolean firstTime = false;
if (data == null)
{
boolean validVersion = true;
Version version = cell.getLibrary().getVersion();
if (version != null) validVersion = version.compareTo(Version.getVersion()) >=0;
data = new StoreDRCInfo(-1, Layout.DRC_LAST_GOOD_BIT_DEFAULT);
storedDRCDate.put(cell, data);
firstTime = true; // to load Variable date from disk in case of inMemory case.
if (!validVersion)
return null; // only the first the data is access the version is considered
}
if (fromDisk || (!fromDisk && firstTime))
{
MutableLong lastDRCDateInMilliseconds = new MutableLong(0);
// Nothing found
if (!getDateStored(cell, dateKey, lastDRCDateInMilliseconds))
return null;
int thisByte = Layout.DRC_LAST_GOOD_BIT_DEFAULT;
if (bitKey != null)
{
Integer varBitsAsInt = cell.getVarValue(bitKey, Integer.class);
if (varBitsAsInt != null) {
thisByte = varBitsAsInt.intValue();
} else {
Byte varBitsAsByte = cell.getVarValue(bitKey, Byte.class);
if (varBitsAsByte != null)
thisByte = varBitsAsByte.byteValue();
else
System.out.println("No valid bit associated to DRC data was found as cell variable");
}
}
data.bits = thisByte;
data.date = lastDRCDateInMilliseconds.longValue();
}
else
{
data = storedDRCDate.get(cell);
}
return data;
}
/**
* Method to check if current date is later than cell revision
* @param cell
* @param date
* @return true if DRC date in cell is valid
*/
public static boolean isCellDRCDateGood(Cell cell, Date date)
{
if (date != null)
{
Date lastChangeDate = cell.getRevisionDate();
if (date.after(lastChangeDate)) return true;
}
return false;
}
/**
* Method to tell the date of the last successful DRC of a given Cell.
* @param cell the cell to query.
* @param fromDisk
* @return the date of the last successful DRC of that Cell.
*/
public static Date getLastDRCDateBasedOnBits(Cell cell, boolean spacingCheck,
int activeBits, boolean fromDisk)
{
assert !THREAD_SAFE_DRC || Job.inServerThread();
StoreDRCInfo data = getCellGoodDRCDateAndBits(cell, spacingCheck, fromDisk);
// if data is null -> nothing found
if (data == null)
return null;
int thisByte = data.bits;
if (fromDisk && spacingCheck)
assert(thisByte!=0);
if (activeBits != Layout.DRC_LAST_GOOD_BIT_DEFAULT)
{
// boolean area = (thisByte & DRC_BIT_AREA) == (activeBits & DRC_BIT_AREA);
boolean extension = (thisByte & DRC_BIT_EXTENSION) == (activeBits & DRC_BIT_EXTENSION);
// DRC date is invalid if conditions were checked for another foundry
boolean sameManufacturer = (thisByte & DRC_BIT_TSMC_FOUNDRY) == (activeBits & DRC_BIT_TSMC_FOUNDRY) &&
(thisByte & DRC_BIT_ST_FOUNDRY) == (activeBits & DRC_BIT_ST_FOUNDRY) &&
(thisByte & DRC_BIT_MOSIS_FOUNDRY) == (activeBits & DRC_BIT_MOSIS_FOUNDRY);
assert(activeBits != 0);
if (activeBits != 0 && (/*!area* || */ !extension || !sameManufacturer))
return null;
}
// If in memory, date doesn't matter
Date revisionDate = cell.getRevisionDate();
Date lastDRCDate = new Date(data.date);
return (lastDRCDate.after(revisionDate)) ? lastDRCDate : null;
}
/**
* Method to clean any DRC date stored previously
* @param cell the cell to clean
*/
private static void cleanDRCDateAndBits(Cell cell, Variable.Key key)
{
if (key == Layout.DRC_LAST_GOOD_DATE_SPACING)
{
cell.delVar(Layout.DRC_LAST_GOOD_DATE_SPACING);
cell.delVar(Layout.DRC_LAST_GOOD_BIT_SPACING);
}
else
cell.delVar(Layout.DRC_LAST_GOOD_DATE_AREA);
}
public static String explainBits(int bits, DRCPreferences dp)
{
boolean on = !dp.ignoreAreaCheck; // (bits & DRC_BIT_AREA) != 0;
String msg = "area bit ";
msg += on ? "on" : "off";
on = (bits & DRC_BIT_EXTENSION) != 0;
msg += ", extension bit ";
msg += on ? "on" : "off";
if ((bits & DRC_BIT_TSMC_FOUNDRY) != 0)
msg += ", TSMC bit";
else if ((bits & DRC_BIT_ST_FOUNDRY) != 0)
msg += ", ST bit";
else if ((bits & DRC_BIT_MOSIS_FOUNDRY) != 0)
msg += ", Mosis bit";
return msg;
}
public static int getActiveBits(Technology tech, DRCPreferences dp)
{
int bits = 0;
// if (!isIgnoreAreaChecking()) bits |= DRC_BIT_AREA;
if (!dp.ignoreExtensionRuleChecking) bits |= DRC_BIT_EXTENSION;
// Adding foundry to bits set
Foundry foundry = tech.getSelectedFoundry();
if (foundry != null)
{
bits |= foundry.getType().getBit();
}
return bits;
}
/**
* Check networks rules of this Cell.
* @param errorLog error logger
* @param cell cell to check
*/
private static void checkNetworks(ErrorLogger errorLog, Cell cell, boolean isLayout) {
final int errorSortNetworks = 0;
final int errorSortNodes = 1;
Map> strangeNodes = null;
Map> unconnectedPins = null;
for (int i = 0, numNodes = cell.getNumNodes(); i < numNodes; i++) {
NodeInst ni = cell.getNode(i);
NodeProto np = ni.getProto();
if (!cell.isIcon()) {
if (ni.isIconOfParent() ||
np.getFunction() == PrimitiveNode.Function.ART && np != Generic.tech().simProbeNode ||
// np == Artwork.tech.pinNode ||
np == Generic.tech().invisiblePinNode) {
if (ni.hasConnections()) {
String msg = "Network: " + cell + " has connections on " + ni;
System.out.println(msg);
errorLog.logError(msg, ni, cell, null, errorSortNodes);
}
} else if (np.getFunction().isPin() &&
cell.getTechnology().isLayout() && !ni.hasConnections()) {
if (unconnectedPins == null)
unconnectedPins = new HashMap>();
ArrayList pinsOfType = unconnectedPins.get(np);
if (pinsOfType == null) {
pinsOfType = new ArrayList();
unconnectedPins.put(np, pinsOfType);
}
pinsOfType.add(ni);
}
}
if (isLayout) {
if (ni.getNameKey().isBus()) {
String msg = "Network: Layout " + cell + " has arrayed " + ni;
System.out.println(msg);
errorLog.logError(msg, ni, cell, null, errorSortNetworks);
}
boolean isSchematicNode;
if (ni.isCellInstance()) {
Cell subCell = (Cell)np;
isSchematicNode = subCell.isIcon() || subCell.isSchematic();
} else {
isSchematicNode = np == Generic.tech().universalPinNode ||np.getTechnology() == Schematics.tech();
}
if (isSchematicNode) {
if (strangeNodes == null)
strangeNodes = new HashMap>();
ArrayList nodesOfType = strangeNodes.get(np);
if (nodesOfType == null) {
nodesOfType = new ArrayList();
strangeNodes.put(np, nodesOfType);
}
nodesOfType.add(ni);
}
}
}
if (unconnectedPins != null) {
for (NodeProto np : unconnectedPins.keySet()) {
ArrayList pinsOfType = unconnectedPins.get(np);
String msg = "Network: " + cell + " has " + pinsOfType.size() + " unconnected pins " + np;
System.out.println(msg);
errorLog.logMessage(msg, Collections.unmodifiableList(pinsOfType), cell,
errorSortNodes, false);
}
}
if (strangeNodes != null) {
for (NodeProto np : strangeNodes.keySet()) {
ArrayList nodesOfType = strangeNodes.get(np);
String msg = "Network: Layout " + cell + " has " + nodesOfType.size() +
" " + np.describe(true) + " nodes";
System.out.println(msg);
boolean realError = np != Generic.tech().universalPinNode; // universal pins generate only warnings
errorLog.logMessage(msg, Collections.unmodifiableList(nodesOfType), cell, errorSortNetworks, realError);
}
}
}
static boolean checkNodeSize(NodeInst ni, Cell cell, ReportInfo reportInfo)
{
boolean errorsFound = false;
// check node for minimum size
NodeProto np = ni.getProto();
PrimitiveNode.NodeSizeRule sizeRule = getMinSize(np);
if (sizeRule != null)
{
PrimitiveNodeSize npSize = ni.getNodeInstSize(null);
List errorsList = sizeRule.checkSize(npSize);
// EPoint niSize = new EPoint(ni.getXSize(), ni.getYSize());
// EPoint niBase = new EPoint(ni.getLambdaBaseXSize(), ni.getLambdaBaseYSize());
// List errorsLis = sizeRule.checkSize(niSize, niBase);
if (errorsList != null)
{
for (PrimitiveNode.NodeSizeRule.NodeSizeRuleError e : errorsList)
{
createDRCErrorLogger(reportInfo, DRC.DRCErrorType.MINSIZEERROR, e.message, cell,
e.minSize, e.actual, sizeRule.getRuleName(), null, ni, null, null, null, null);
errorsFound = true;
}
}
}
return errorsFound;
}
/****************************** OPTIONS ******************************/
public static class DRCPreferences extends PrefPackage {
private static final String DRC_NODE = "tool/drc";
private static final String KEY_ERROR_LOGGING_TYPE = "ErrorLoggingType";
private static final String KEY_ERROR_CHECK_LEVEL = "ErrorCheckLevel";
private static final String KEY_MIN_AREA_ALGORITHM = "MinAreaAlgorithm";
private static final String KEY_RESOLUTION = "ResolutionValueFor";
private static final String KEY_OVERRIDES = "DRCOverridesFor";
/** Whether DRC should DRC should be done incrementally. The default is "false". */
@BooleanPref(node = DRC_NODE, key = "IncrementalDRCOn", factory = false)
public boolean incrementalDRC;
/** Whether DRC violations should be shown while nodes and arcs are dragged. The default is "true". */
@BooleanPref(node = DRC_NODE, key = "InteractiveDRCDrag", factory = true)
public boolean interactiveDRCDrag;
/** Logging type in DRC. The default is "DRC_LOG_PER_CELL". */
public DRCCheckLogging errorLoggingType;
private static final DRCCheckLogging DEF_ERROR_LOGGING_TYPE = DRCCheckLogging.DRC_LOG_PER_CELL;
/** Checking level in DRC. The default is "ERROR_CHECK_DEFAULT". */
public DRCCheckMode errorType;
private static final DRCCheckMode DEF_ERROR_CHECK_LEVEL = DRCCheckMode.ERROR_CHECK_DEFAULT;
/** Whether DRC should ignore center cuts in large contacts.
* Only the perimeter of cuts will be checked. The default is "false".
*/
@BooleanPref(node = DRC_NODE, key = "IgnoreCenterCuts", factory = false)
public boolean ignoreCenterCuts;
/** Whether DRC should ignore minimum/enclosed area checking. The default is "false". */
@BooleanPref(node = DRC_NODE, key = "IgnoreAreaCheck", factory = false)
public boolean ignoreAreaCheck;
/** Whether DRC should should check extension rules. The default is "false". */
@BooleanPref(node = DRC_NODE, key = "IgnoreExtensionRuleCheck", factory = false)
public boolean ignoreExtensionRuleChecking;
/** Whether DRC dates should be stored in memory or not. The default is "false". */
@BooleanPref(node = DRC_NODE, key = "StoreDatesInMemory", factory = false)
public boolean storeDatesInMemory;
/** Whether DRC loggers should be displayed in Explorer immediately. The default is "false". */
@BooleanPref(node = DRC_NODE, key = "InteractiveLog", factory = false)
public boolean interactiveLog;
/** Which min area algorithm to use. The default is AREA_LOCAL */
public DRCCheckMinArea minAreaAlgoOption;
private static final DRCCheckMinArea DEF_MIN_AREA_ALGORITHM = DRCCheckMinArea.AREA_LOCAL;
/** Whether DRC should run in a single thread or multi-threaded. The default is single-threaded. */
@BooleanPref(node=DRC_NODE, key = "MinMultiThread", factory = false)
public boolean isMultiThreaded;
public Map resolutions = new HashMap();
public Map overrides = new HashMap();
public DRCPreferences(boolean factory)
{
super(factory);
Preferences techPrefs = getPrefRoot().node(TECH_NODE);
Preferences drcPrefs = getPrefRoot().node(DRC_NODE);
errorLoggingType = DRCCheckLogging.valueOf(drcPrefs.get(KEY_ERROR_LOGGING_TYPE, DEF_ERROR_LOGGING_TYPE.name()));
errorType = DRCCheckMode.class.getEnumConstants()[drcPrefs.getInt(KEY_ERROR_CHECK_LEVEL, DEF_ERROR_CHECK_LEVEL.ordinal())];
minAreaAlgoOption = DRCCheckMinArea.valueOf(drcPrefs.get(KEY_MIN_AREA_ALGORITHM, DEF_MIN_AREA_ALGORITHM.name()));
for (Iterator it = Technology.getTechnologies(); it.hasNext(); ) {
Technology tech = it.next();
String keyResolution = getKey(KEY_RESOLUTION, tech.getId());
double lambdaResolution = techPrefs.getDouble(keyResolution, tech.getFactoryResolution().getLambda()); //tech.getFactoryScaledResolution());
ECoord resolution = ECoord.fromLambdaRoundSizeGrid(lambdaResolution);
resolutions.put(tech, resolution);
String keyOverrides = getKey(KEY_OVERRIDES, tech.getId());
String override = drcPrefs.get(keyOverrides, "");
overrides.put(tech, override);
}
}
/**
* Store annotated option fields of the subclass into the speciefied Preferences subtree.
* @param prefRoot the root of the Preferences subtree.
* @param removeDefaults remove from the Preferences subtree options which have factory default value.
*/
@Override
public void putPrefs(Preferences prefRoot, boolean removeDefaults) {
super.putPrefs(prefRoot, removeDefaults);
Preferences techPrefs = prefRoot.node(TECH_NODE);
Preferences drcPrefs = prefRoot.node(DRC_NODE);
if (removeDefaults && errorLoggingType == DEF_ERROR_LOGGING_TYPE)
drcPrefs.remove(KEY_ERROR_LOGGING_TYPE);
else
drcPrefs.put(KEY_ERROR_LOGGING_TYPE, errorLoggingType.name());
if (removeDefaults && errorType == DEF_ERROR_CHECK_LEVEL)
drcPrefs.remove(KEY_ERROR_CHECK_LEVEL);
else
drcPrefs.putInt(KEY_ERROR_CHECK_LEVEL, errorType.ordinal());
if (removeDefaults && minAreaAlgoOption == DEF_MIN_AREA_ALGORITHM)
drcPrefs.remove(KEY_MIN_AREA_ALGORITHM);
else
drcPrefs.put(KEY_MIN_AREA_ALGORITHM, minAreaAlgoOption.name());
for (Map.Entry e: resolutions.entrySet()) {
Technology tech = e.getKey();
String keyResolution = getKey(KEY_RESOLUTION, tech.getId());
ECoord factoryResolution = tech.getFactoryResolution();
// double factoryResolution = tech.getFactoryScaledResolution();
ECoord resolution = e.getValue();
if (removeDefaults && resolution.equals(factoryResolution))
techPrefs.remove(keyResolution);
else
techPrefs.putDouble(keyResolution, resolution.getLambda());
}
for (Map.Entry e: overrides.entrySet()) {
Technology tech = e.getKey();
String keyOverrides = getKey(KEY_OVERRIDES, tech.getId());
String override = e.getValue();
if (removeDefaults && override.length() == 0)
drcPrefs.remove(keyOverrides);
else
drcPrefs.put(keyOverrides, override);
}
}
/**
* Method to set the technology resolution.
* This is the minimum size unit that can be represented.
* @param tech Technology
* @param resolution new resolution value.
*/
public void setResolution(Technology tech, ECoord resolution)
{
if (!resolution.isExact(ECoord.SIZE_GRID)) {
throw new IllegalArgumentException();
}
resolutions.put(tech, resolution);
}
/**
* Method to retrieve the resolution associated to specified.
* This is the minimum size unit that can be represented.
* @param tech specified technolgy
* @return the technology's resolution value.
*/
public ECoord getResolution(Technology tech)
{
return resolutions.get(tech);
}
/**
* Method to get the DRC overrides from the preferences for this technology.
* @param tech specified technolgy
* @return a Pref describing DRC overrides for the Technology.
*/
public String getDRCOverrides(Technology tech) {
return overrides.get(tech);
}
/**
* Method to set the DRC overrides for a this technology.
* @param tech specified technolgy
* @param overrides the overrides.
*/
public void setDRCOverrides(Technology tech, String overrides) {
if (overrides.length() >= Preferences.MAX_VALUE_LENGTH) {
System.out.println("Warning: Design rule overrides are too complex to be saved (are " +
overrides.length() + " long which is more than the limit of " + Preferences.MAX_VALUE_LENGTH + ")");
}
this.overrides.put(tech, overrides);
}
}
/****************************** END OF OPTIONS ******************************/
/***********************************
* Update Functions
***********************************/
static void addDRCUpdate(int spacingBits,
Set goodSpacingDRCDate, Set cleanSpacingDRCDate,
Set goodAreaDRCDate, Set cleanAreaDRCDate,
Map> newVariables, DRCPreferences dp)
{
boolean goodSpace = (goodSpacingDRCDate != null && goodSpacingDRCDate.size() > 0);
boolean cleanSpace = (cleanSpacingDRCDate != null && cleanSpacingDRCDate.size() > 0);
boolean goodArea = (goodAreaDRCDate != null && goodAreaDRCDate.size() > 0);
boolean cleanArea = (cleanAreaDRCDate != null && cleanAreaDRCDate.size() > 0);
boolean vars = (newVariables != null && newVariables.size() > 0);
if (!goodSpace && !cleanSpace && !vars && !goodArea && !cleanArea) return; // nothing to do
new DRCUpdate(spacingBits, goodSpacingDRCDate, cleanSpacingDRCDate,
goodAreaDRCDate, cleanAreaDRCDate, newVariables, dp);
}
/**
* Method to delete all cached date information on all cells.
* @param startJob
*/
public static void resetDRCDates(boolean startJob)
{
new DRCReset(startJob);
}
/***********************************
* DRCReset class
***********************************/
private static class DRCReset extends Job
{
DRCReset(boolean startJob)
{
super("Resetting DRC Dates", User.getUserTool(), Job.Type.CHANGE, null, null, Priority.USER);
if (startJob)
startJob();
else
doIt();
}
public boolean doIt()
{
storedSpacingDRCDate.clear();
storedAreaDRCDate.clear();
// Always clean the dates as variables.
// if (!isDatesStoredInMemory())
{
for(Iterator it = Library.getLibraries(); it.hasNext(); )
{
Library lib = it.next();
for(Iterator cIt = lib.getCells(); cIt.hasNext(); )
{
Cell cell = cIt.next();
cleanDRCDateAndBits(cell, Layout.DRC_LAST_GOOD_DATE_SPACING);
cleanDRCDateAndBits(cell, Layout.DRC_LAST_GOOD_DATE_AREA);
}
}
}
return true;
}
}
/***********************************
* DRCUpdate class
***********************************/
/**
* Class to save good Layout DRC dates in a new thread or add new variables in Schematic DRC
*/
private static class DRCUpdate extends Job
{
Set goodSpacingDRCDate;
Set cleanSpacingDRCDate;
Set goodAreaDRCDate;
Set cleanAreaDRCDate;
Map> newVariables;
int activeBits = Layout.DRC_LAST_GOOD_BIT_DEFAULT;
DRCPreferences dp;
public DRCUpdate(int bits,
Set goodSpacingDRCD, Set cleanSpacingDRCD,
Set goodAreaDRCD, Set cleanAreaDRCD,
Map> newVars,
DRCPreferences dp)
{
super("Update DRC data", tool, Type.CHANGE, null, null, Priority.USER);
this.goodSpacingDRCDate = goodSpacingDRCD;
this.cleanSpacingDRCDate = cleanSpacingDRCD;
this.goodAreaDRCDate = goodAreaDRCD;
this.cleanAreaDRCDate = cleanAreaDRCD;
this.newVariables = newVars;
this.activeBits = bits;
this.dp = dp;
// Only works for layout with in memory dates -> no need of adding the job into the queue
if (dp.storeDatesInMemory && (newVars == null || newVars.isEmpty()))
{
try {doIt();} catch (Exception e) {e.printStackTrace();}
}
else // put it into the queue
startJobOnMyResult();
}
/**
* Template method to set DAte and bits information for a given map.
* @param inMemory
*/
private static void setInformation(Map storedDRCDate,
Set goodDRCDate, Set cleanDRCDate,
Variable.Key key, int bits, boolean inMemory, EditingPreferences ep)
{
Set goodDRCCells = new HashSet();
long time = System.currentTimeMillis();
if (goodDRCDate != null)
{
for (Cell cell : goodDRCDate)
{
if (!cell.isLinked())
new JobException("Cell '" + cell + "' is invalid to clean DRC date");
else
{
if (inMemory)
storedDRCDate.put(cell, new StoreDRCInfo(time, bits));
else
goodDRCCells.add(cell);
}
}
}
if (!goodDRCCells.isEmpty())
Layout.setGoodDRCCells(goodDRCCells, key, bits, inMemory, ep);
if (cleanDRCDate != null)
{
for (Cell cell : cleanDRCDate)
{
if (!cell.isLinked())
new JobException("Cell '" + cell + "' is invalid to clean DRC date");
else
{
StoreDRCInfo data = storedDRCDate.get(cell);
assert(data != null);
data.date = -1;
data.bits = Layout.DRC_LAST_GOOD_BIT_DEFAULT; // I can't put null because of the version
if (!inMemory)
cleanDRCDateAndBits(cell, key);
}
}
}
}
public boolean doIt() throws JobException
{
boolean inMemory = dp.storeDatesInMemory;
EditingPreferences ep = getEditingPreferences();
setInformation(storedSpacingDRCDate, goodSpacingDRCDate, cleanSpacingDRCDate,
Layout.DRC_LAST_GOOD_DATE_SPACING, activeBits, inMemory, ep);
setInformation(storedAreaDRCDate, goodAreaDRCDate, cleanAreaDRCDate,
Layout.DRC_LAST_GOOD_DATE_AREA, Layout.DRC_LAST_GOOD_BIT_DEFAULT, inMemory, ep);
// Update variables in Schematics DRC
if (newVariables != null)
{
assert(!inMemory);
for (Map.Entry> e : newVariables.entrySet())
{
Geometric ni = e.getKey();
for (Variable var : e.getValue()) {
if (ni.isParam(var.getKey()))
((NodeInst)ni).addParameter(var);
else
ni.addVar(var);
}
}
}
return true;
}
}
/***********************************
* JUnit interface
***********************************/
public static boolean testAll()
{
return true;
}
}
/**************************************************************************************************************
* CellLayersContainer class
**************************************************************************************************************/
class CellLayersContainer implements Serializable
{
private Map> cellLayersMap;
CellLayersContainer() {
cellLayersMap = new HashMap>();
}
Set getLayersSet(NodeProto cell) {
return cellLayersMap.get(cell);
}
boolean getWorstSpacingDistance(NodeProto cell, MutableDouble mutableDist)
{
Set layers = getLayersSet(cell);
Technology tech = cell.getTechnology();
return DRC.getWorstSpacingDistance(tech, layers, mutableDist);
}
void addCellLayers(Cell cell, Set set) {
cellLayersMap.put(cell, set);
}
boolean addCellLayers(Cell cell, Layer layer) {
Set set = cellLayersMap.get(cell);
// first time the cell is accessed
if (set == null) {
set = new HashSet(1);
cellLayersMap.put(cell, set);
}
return set.add(layer);
}
}
/**************************************************************************************************************
* CheckCellLayerEnumerator class
**************************************************************************************************************/
/**
* Class to collect which layers are available in the design
*/
class CheckCellLayerEnumerator extends HierarchyEnumerator.Visitor {
private Map cellsMap;
private CellLayersContainer cellLayersCon;
CheckCellLayerEnumerator(CellLayersContainer cellLayersC) {
cellsMap = new HashMap();
cellLayersCon = cellLayersC;
}
/**
* When the cell should be visited. Either it is the first time or the number of layers hasn't reached
* the maximum
*
* @param cell
* @return
*/
private boolean skipCell(Cell cell) {
return cellsMap.get(cell) != null;
}
public boolean enterCell(HierarchyEnumerator.CellInfo info) {
Cell cell = info.getCell();
if (skipCell(cell)) return false; // skip
cellsMap.put(cell, cell);
return true;
}
private Set getLayersInCell(Cell cell) {
Map tempNodeMap = new HashMap();
Map tempArcMap = new HashMap();
Set set = new HashSet();
Technology tech = cell.getTechnology();
// Nodes
for (Iterator it = cell.getNodes(); it.hasNext();) {
NodeInst ni = it.next();
NodeProto np = ni.getProto();
if (ni.isCellInstance()) {
Set s = cellLayersCon.getLayersSet(np);
set.addAll(s);
assert (s != null); // it must have layers? unless is empty
} else {
if (tempNodeMap.get(np) != null)
continue; // done with this PrimitiveNode
tempNodeMap.put(np, np);
if (NodeInst.isSpecialNode(ni)) // like pins
continue;
PrimitiveNode pNp = (PrimitiveNode) np;
for (Technology.NodeLayer nLayer : pNp.getNodeLayers()) {
Layer layer = nLayer.getLayer();
if (tech.findLayer(layer.getName()) == null) continue;
set.add(layer);
}
}
}
// Arcs
for (Iterator it = cell.getArcs(); it.hasNext();) {
ArcInst ai = it.next();
ArcProto ap = ai.getProto();
if (tempArcMap.get(ap) != null)
continue; // done with this arc primitive
tempArcMap.put(ap, ap);
for (int i = 0; i < ap.getNumArcLayers(); i++) {
Layer layer = ap.getLayer(i);
if (tech.findLayer(layer.getName()) == null) continue;
set.add(layer);
}
}
return set;
}
public void exitCell(HierarchyEnumerator.CellInfo info) {
Cell cell = info.getCell();
Set set = getLayersInCell(cell);
assert (cellLayersCon.getLayersSet(cell) == null);
cellLayersCon.addCellLayers(cell, set);
}
public boolean visitNodeInst(Nodable no, HierarchyEnumerator.CellInfo info) {
NodeInst ni = no.getNodeInst();
// true only for Cells
return ni.isCellInstance();
}
}
/***************** LAYER INTERACTIONS ******************/
/**************************************************************************************************************
* ValidationLayers class
**************************************************************************************************************/
class ValidationLayers
{
/* for figuring out which layers are valid for DRC */
private Technology layersValidTech = null;
private boolean [] layersValid;
/* for tracking which layers interact with which nodes */
private Technology layerInterTech = null;
private HashMap layersInterNodes = null;
private HashMap layersInterArcs = null;
private ErrorLogger errorLogger;
private Cell topCell;
private DRCRules currentRules;
/**
* Class to determine which layers in a Technology are valid.
*/
ValidationLayers(ErrorLogger logger, Cell cell, DRCRules rules)
{
topCell = cell;
errorLogger = logger;
currentRules = rules;
layersValidTech = rules.getTechnology();
// determine the layers that are being used
fillValidLayers();
cacheValidLayers(layersValidTech);
buildLayerInteractions(layersValidTech);
}
private void fillValidLayers()
{
// determine the layers that are being used
int numLayers = layersValidTech.getNumLayers();
layersValid = new boolean[numLayers];
for(int i=0; i < numLayers; i++)
layersValid[i] = false;
for(Iterator it = layersValidTech.getNodes(); it.hasNext(); )
{
PrimitiveNode np = it.next();
if (np.isNotUsed()) continue;
Technology.NodeLayer [] layers = np.getNodeLayers();
for (Technology.NodeLayer l : layers)
{
Layer layer = l.getLayer();
layersValid[layer.getIndex()] = true;
}
}
for(Iterator it = layersValidTech.getArcs(); it.hasNext(); )
{
ArcProto ap = it.next();
if (ap.isNotUsed()) continue;
for (Iterator lIt = ap.getLayerIterator(); lIt.hasNext(); )
{
Layer layer = lIt.next();
layersValid[layer.getIndex()] = true;
}
}
}
boolean isABadLayer(Technology tech, int layerNumber)
{
return (tech == layersValidTech && !layersValid[layerNumber]);
}
/**
* Method to determine which layers in a Technology are valid.
*/
void cacheValidLayers(Technology tech)
{
if (tech == null) return;
if (layersValidTech == tech) return;
layersValidTech = tech;
// determine the layers that are being used
fillValidLayers();
}
/**
* Method to build the internal data structures that tell which layers interact with
* which primitive nodes in technology "tech".
*/
void buildLayerInteractions(Technology tech)
{
Technology old = layerInterTech;
if (layerInterTech == tech) return;
layerInterTech = tech;
int numLayers = tech.getNumLayers();
// build the node table
if (layersInterNodes != null && old != null)
{
errorLogger.logWarning("Switching from '" + old.getTechName() +
"' to '" + tech.getTechName() + "' in DRC process. Check for non desired nodes in ",
topCell, -1);
}
layersInterNodes = new HashMap();
for (Iterator it = tech.getNodes(); it.hasNext();)
{
PrimitiveNode np = it.next();
if (np.isNotUsed()) continue;
boolean[] layersInNode = new boolean[numLayers];
Arrays.fill(layersInNode, false);
Technology.NodeLayer[] layers = np.getNodeLayers();
Technology.NodeLayer[] eLayers = np.getElectricalLayers();
if (eLayers != null) layers = eLayers;
for (Technology.NodeLayer l : layers)
{
Layer layer = l.getLayer();
if (layer.isNonElectrical())
continue; // such as pseudo
for (Iterator lIt = tech.getLayers(); lIt.hasNext();)
{
Layer oLayer = lIt.next();
if (oLayer.isNonElectrical())
continue; // such as pseudo
if (currentRules.isAnySpacingRule(layer, oLayer))
layersInNode[oLayer.getIndex()] = true;
}
}
layersInterNodes.put(np, layersInNode);
}
// build the arc table
layersInterArcs = new HashMap();
for (Iterator it = tech.getArcs(); it.hasNext();)
{
ArcProto ap = it.next();
boolean[] layersInArc = new boolean[numLayers];
Arrays.fill(layersInArc, false);
for (Iterator alIt = ap.getLayerIterator(); alIt.hasNext();)
{
Layer layer = alIt.next();
for (Iterator lIt = tech.getLayers(); lIt.hasNext();)
{
Layer oLayer = lIt.next();
if (currentRules.isAnySpacingRule(layer, oLayer))
layersInArc[oLayer.getIndex()] = true;
}
}
layersInterArcs.put(ap, layersInArc);
}
}
/**
* Method to determine whether layer "layer" interacts in any way with a node of type "np".
* If not, returns FALSE.
*/
boolean checkLayerWithNode(Layer layer, NodeProto np)
{
buildLayerInteractions(np.getTechnology());
// find this node in the table
boolean[] validLayers = layersInterNodes.get(np);
if (validLayers == null) return false;
return validLayers[layer.getIndex()];
}
/**
* Method to determine whether layer "layer" interacts in any way with an arc of type "ap".
* If not, returns FALSE.
*/
boolean checkLayerWithArc(Layer layer, ArcProto ap)
{
buildLayerInteractions(ap.getTechnology());
// find this node in the table
boolean[] validLayers = layersInterArcs.get(ap);
if (validLayers == null) return false;
return validLayers[layer.getIndex()];
}
}
/**
* The CheckInst object is associated with every cell instance in the library.
* It helps determine network information on a global scale.
* It takes a "global-index" parameter, inherited from above (intially zero).
* It then computes its own index number as follows:
* thisindex = global-index * multiplier + localIndex + offset
* This individual index is used to lookup an entry on each network in the cell
* (an array is stored on each network, giving its global net number).
*/
class CheckInst
{
int localIndex;
int multiplier;
int offset;
}
/**
* The CheckProto object is placed on every cell and is used only temporarily
* to number the instances.
*/
class CheckProto
{
/** time stamp for counting within a particular parent */ int timeStamp;
/** number of instances of this cell in a particular parent */ int instanceCount;
/** total number of instances of this cell, hierarchically */ int hierInstanceCount;
/** number of instances of this cell in a particular parent */ int totalPerCell;
/** true if this cell has been checked */ boolean cellChecked;
/** true if this cell has parameters */ boolean cellParameterized;
/** list of instances in a particular parent */ List nodesInCell;
/** netlist of this cell */ Netlist netlist;
}
/**
* The InstanceInter object records interactions between two cell instances and prevents checking
* them multiple times.
*/
class InstanceInter
{
/** the two cell instances being compared */ Cell cell1, cell2;
/** orientation of cell instance 1 */ Orientation or1;
/** orientation of cell instance 2 */ Orientation or2;
/** distance from instance 1 to instance 2 */ double dx, dy;
/** the two NodeInst parents */ NodeInst n1Parent, n2Parent, triggerNi;
// /** bounding used to select first element */ Rectangle2D bnd;
}
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
© 2015 - 2025 Weber Informatics LLC | Privacy Policy