Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.sun.electric.tool.extract.LayerCoverageTool Maven / Gradle / Ivy
/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: LayerCoverageTool.java
*
* Copyright (c) 2005, 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.extract;
import com.sun.electric.database.EditingPreferences;
import com.sun.electric.database.geometry.EPoint;
import com.sun.electric.database.geometry.GeometryHandler;
import com.sun.electric.database.geometry.Poly;
import com.sun.electric.database.geometry.PolyBase;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.hierarchy.HierarchyEnumerator;
import com.sun.electric.database.hierarchy.Nodable;
import com.sun.electric.database.id.LayerId;
import com.sun.electric.database.network.Netlist;
import com.sun.electric.database.network.Network;
import com.sun.electric.database.prototype.PortProto;
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.variable.EditWindow_;
import com.sun.electric.database.variable.VarContext;
import com.sun.electric.technology.ArcProto;
import com.sun.electric.technology.DRCTemplate;
import com.sun.electric.technology.Layer;
import com.sun.electric.technology.PrimitiveNode;
import com.sun.electric.technology.TechPool;
import com.sun.electric.technology.Technology;
import com.sun.electric.technology.TransistorSize;
import com.sun.electric.tool.Job;
import com.sun.electric.tool.JobException;
import com.sun.electric.tool.Tool;
import com.sun.electric.tool.drc.DRC;
import com.sun.electric.tool.routing.SeaOfGates;
import com.sun.electric.tool.user.ErrorLogger;
import com.sun.electric.tool.user.User;
import com.sun.electric.util.TextUtils;
import com.sun.electric.util.math.DBMath;
import com.sun.electric.util.math.FixpTransform;
import java.awt.Shape;
import java.awt.geom.Area;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.prefs.Preferences;
/**
* Class to describe coverage percentage for a layer.
*/
public class LayerCoverageTool extends Tool
{
/** the LayerCoverageTool tool. */ protected static LayerCoverageTool tool = new LayerCoverageTool();
/**
* The constructor sets up the DRC tool.
*/
private LayerCoverageTool()
{
super("coverage");
}
/**
* Method to retrieve the singleton associated with the LayerCoverageTool tool.
* @return the LayerCoverageTool tool.
*/
public static LayerCoverageTool getLayerCoverageTool() { return tool; }
/****************************** OPTIONS ******************************/
public static class LayerCoveragePreferences extends PrefPackage {
public static final double DEFAULT_AREA_COVERAGE = 10; // 10%
// Default value is in um to be technology independent
private static final double defaultSize = 50000;
private static final String EXTRACT_NODE = "tool/extract";
private static final String KEY_COVERAGE = "AreaCoverageJobOf";
private final transient TechPool techPool;
public Map areaCoverage = new HashMap();
/** User preference for deltaX. The default is 50 mm. */
@DoublePref(node = EXTRACT_NODE, key = "DeltaX", factory = defaultSize)
public double deltaXInMicrons;
/** User preference for deltaY. The default is 50 mm. */
@DoublePref(node = EXTRACT_NODE, key = "DeltaY", factory = defaultSize)
public double deltaYInMicrons;
/** User preference for width of the bounding box. The default is 50 mm. */
@DoublePref(node = EXTRACT_NODE, key = "Width", factory = defaultSize)
public double widthInMicrons;
/** User preference for height of the bounding box. The default is 50 mm. */
@DoublePref(node = EXTRACT_NODE, key = "Height", factory = defaultSize)
public double heightInMicrons;
/** Preference for SeaOfGate if used */
public SeaOfGates.SeaOfGatesOptions seaIfGatesPrefs;
public LayerCoveragePreferences(boolean factory) {
this(factory, TechPool.getThreadTechPool());
seaIfGatesPrefs = new SeaOfGates.SeaOfGatesOptions();
if (!factory) seaIfGatesPrefs.getOptionsFromPreferences();
}
private LayerCoveragePreferences(boolean factory, TechPool techPool)
{
super(factory);
this.techPool = techPool;
if (factory) return;
Preferences techPrefs = getPrefRoot().node(TECH_NODE);
for (Technology tech: techPool.values()) {
for (Iterator it = tech.getLayers(); it.hasNext(); ) {
Layer layer = it.next();
LayerId layerId = layer.getId();
double factoryValue = DEFAULT_AREA_COVERAGE;
double value = techPrefs.getDouble(getKey(KEY_COVERAGE, layerId), factoryValue);
if (value == factoryValue) continue;
areaCoverage.put(layerId, Double.valueOf(value));
}
}
}
/**
* 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);
for (Technology tech: techPool.values()) {
for (Iterator it = tech.getLayers(); it.hasNext(); ) {
Layer layer = it.next();
LayerId layerId = layer.getId();
String key = getKey(KEY_COVERAGE, layerId);
double factoryValue = DEFAULT_AREA_COVERAGE;
Double valueObj = areaCoverage.get(layerId);
double value = valueObj != null ? valueObj.doubleValue() : factoryValue;
if (removeDefaults && value == factoryValue)
techPrefs.remove(key);
else
techPrefs.putDouble(key, value);
}
}
}
/**
* Method to return the minimum area coverage that the layer must reach in the technology.
* @return the minimum area coverage (in percentage).
*/
public double getAreaCoverage(Layer layer) {
Double valueObj = areaCoverage.get(layer.getId());
return valueObj != null ? valueObj.doubleValue() : DEFAULT_AREA_COVERAGE;
}
/**
* Methot to set minimum area coverage that the layer must reach in the technology.
* @param layer Layer
* @param area the minimum area coverage (in percentage).
*/
public void setAreaCoverageInfo(Layer layer, double area) {
if (area == DEFAULT_AREA_COVERAGE)
areaCoverage.remove(layer.getId());
else
areaCoverage.put(layer.getId(), Double.valueOf(area));
}
public void reset() {
areaCoverage.clear();
deltaXInMicrons = deltaYInMicrons = widthInMicrons = heightInMicrons = defaultSize;
}
}
/**
* Method to handle the "List Layer Coverage", "Coverage Implant Generator", polygons merge
* except "List Geometry on Network" commands.
*/
public static List layerCoverageCommand(LCMode func, GeometryHandler.GHMode mode, Cell curCell, boolean startJob, LayerCoveragePreferences lcp)
{
// Must be change job for merge and implant;
Job.Type jobType = (func == LayerCoverageTool.LCMode.MERGE || func == LayerCoverageTool.LCMode.IMPLANT) ?
Job.Type.CHANGE : Job.Type.SERVER_EXAMINE;
LayerCoverageJob job = new LayerCoverageJob(curCell, jobType, func, mode, null, null, lcp);
if (startJob)
job.startJob();
else
{
try
{
job.doIt();
}
catch (Exception e) {}
return job.nodesAdded;
}
return null;
}
/**
* Method to kick area coverage per layer in a cell. It has to be public due to regressions.
* @param cell
* @param mode
* @param startJob to determine if job has to run in a separate thread
* @return true if job runs without errors. Only valid if startJob is false (regression purpose)
*/
public static Map layerCoverageCommand(Cell cell, GeometryHandler.GHMode mode, boolean startJob, LayerCoveragePreferences lcp)
{
if (cell == null) return null;
double techScale = cell.getTechnology().getScale();
double width = lcp.widthInMicrons/techScale;
double height = lcp.heightInMicrons/techScale;
double deltaX = lcp.deltaXInMicrons/techScale;
double deltaY = lcp.deltaYInMicrons/techScale;
// Reset values to cell bounding box if area is bigger than the actual cell
Rectangle2D bbox = cell.getBounds();
if (width > bbox.getWidth()) width = bbox.getWidth();
if (height > bbox.getHeight()) height = bbox.getHeight();
Map map = null;
AreaCoverageJob job = new AreaCoverageJob(cell, mode, width, height, deltaX, deltaY, lcp);
// No regression
if (startJob)
job.startJob();
else
{
try
{
job.doIt();
map = job.getDataInfo();
} catch (JobException e)
{
}
}
return (map);
}
/**
* Method to extract bounding box for a particular Network/Layer
* @param exportCell
* @return Rectangle2D containing the bounding box for a particular Network/Layer
*/
public static Rectangle2D getGeometryOnNetwork(Cell exportCell, PortInst pi, Layer layer, LayerCoverageTool.LayerCoveragePreferences lcp)
{
Netlist netlist = exportCell.getNetlist();
Network net = netlist.getNetwork(pi);
Set nets = new HashSet();
nets.add(net);
GeometryOnNetwork geoms = new GeometryOnNetwork(exportCell, nets, 1.0, false, layer, lcp);
// This assumes that pi.getBounds() alywas gives you a degenerated rectangle (zero area) so
// only a point should be searched.
Rectangle2D bnd = pi.getBounds();
LayerCoverageJob job = new LayerCoverageJob(exportCell, Job.Type.SERVER_EXAMINE, LCMode.NETWORK,
GeometryHandler.GHMode.ALGO_SWEEP, geoms,
new Point2D.Double(bnd.getX(), bnd.getY()), lcp);
// Must run it now
try
{
job.doIt(); // Former listGeometryOnNetworksNoJob
} catch (JobException e)
{
e.printStackTrace();
}
List list = job.nodesAdded;
// Don't know what to do if there is more than one
Rectangle2D rect = null;
if (list.size() != 1)
System.out.println("Problem here");
// assert(list.size() == 1);
else
{
PolyBase poly = (PolyBase)list.toArray()[0];
rect = poly.getBounds2D();
}
return rect;
}
/**
* Method to calculate area, half-perimeter and ratio of each layer by merging geometries
* @param cell cell to analyze
* @param nets networks to analyze
* @param startJob if job has to run on thread
* @param mode geometric algorithm to use: GeometryHandler.ALGO_QTREE, GeometryHandler.SWEEP or GeometryHandler.ALGO_MERGE
* @param lcp LayerCoveragePreferences
*/
public static GeometryOnNetwork listGeometryOnNetworks(Cell cell, Set nets, boolean startJob,
GeometryHandler.GHMode mode, LayerCoveragePreferences lcp)
{
if (cell == null || nets == null || nets.isEmpty()) return null;
double lambda = 1; // lambdaofcell(np);
// startJob is identical to printable
GeometryOnNetwork geoms = new GeometryOnNetwork(cell, nets, lambda, startJob, null, lcp);
Job job = new LayerCoverageJob(cell, Job.Type.SERVER_EXAMINE, LCMode.NETWORK, mode, geoms, null, lcp);
if (startJob)
job.startJob();
else
{
try
{
job.doIt(); // Former listGeometryOnNetworksNoJob
} catch (JobException e)
{
}
}
return geoms;
}
/************************************************************************
* LayerCoverageData Class
************************************************************************/
private static class LayerCoverageData
{
private EditingPreferences ep;
private Cell curCell;
private Job parentJob; // to stop if parent job is killed
private LCMode function;
private GeometryHandler.GHMode mode;
private GeometryOnNetwork geoms; // Valid only for network job
private Rectangle2D bBox; // To crop geometry under analysis by given bounding box
private List nodesToExamine = new ArrayList(); // for implant
private Point2D overlapPoint; // to detect if obtained geometry is connected to original request,
// Network in fill generator
LayerCoverageData(Job parentJob, Cell cell, LCMode func, GeometryHandler.GHMode mode,
GeometryOnNetwork geoms, Rectangle2D bBox, Point2D overlapPoint, EditingPreferences ep, LayerCoverageTool.LayerCoveragePreferences lcp)
{
this.ep = ep;
this.parentJob = parentJob;
this.curCell = cell;
this.mode = mode;
this.function = func;
this.geoms = geoms; // Valid only for network
this.bBox = bBox;
this.overlapPoint = overlapPoint;
if (func == LCMode.AREA && this.geoms == null)
this.geoms = new GeometryOnNetwork(curCell, null, 1, true, null, lcp);
}
List getNodesToHighlight() { return nodesToExamine; }
boolean doIt()
{
GeometryHandler tree = GeometryHandler.createGeometryHandler(mode, curCell.getTechnology().getNumLayers());
Map> originalPolygons = new HashMap>(); // Storing initial nodes
Set nodesToDelete = new HashSet(); // should only be used by IMPLANT
Set netSet = null;
Layer onlyThisLayer = null;
Netlist.ShortResistors shortResistors = Netlist.ShortResistors.NO;
if (geoms != null)
{
netSet = geoms.nets;
if (geoms.nets != null && !geoms.nets.isEmpty()) {
Iterator nIt = geoms.nets.iterator();
shortResistors = nIt.next().getNetlist().getShortResistors();
while (nIt.hasNext()) {
Netlist.ShortResistors sh = nIt.next().getNetlist().getShortResistors();
if (sh != shortResistors)
throw new IllegalArgumentException("shortResistors");
}
}
onlyThisLayer = geoms.onlyThisLayer;
}
// enumerate the hierarchy below here
LayerVisitor visitor = new LayerVisitor(parentJob, tree, nodesToDelete, function,
originalPolygons, netSet, bBox, onlyThisLayer, geoms);
HierarchyEnumerator.enumerateCell(curCell, VarContext.globalContext, visitor, shortResistors);
tree.postProcess(true);
switch (function)
{
case MERGE:
case IMPLANT:
{
// With polygons collected, new geometries are calculated
boolean noNewNodes = true;
boolean isMerge = (function == LCMode.MERGE);
Rectangle2D rect;
Point2D [] points;
// Need to detect if geometry was really modified
for (Layer layer : tree.getKeySet())
{
Collection set = tree.getObjects(layer, !isMerge, true);
Object[] polyArray = null;
if (function == LCMode.IMPLANT)
{
Set polySet = originalPolygons.get(layer);
polyArray = polySet.toArray();
}
List newImplants = new ArrayList();
// Ready to create new implants.
for (PolyBase polyB : set)
{
points = polyB.getPoints();
rect = polyB.getBounds2D();
if (isMerge)
{
// ignore if one of the original elements (implant merge only)
if (polyArray != null)
{
boolean foundOrigPoly = false;
for (Object poly : polyArray)
{
foundOrigPoly = polyB.polySame((PolyBase)poly);
if (foundOrigPoly) break;
}
if (foundOrigPoly) continue;
}
// Adding the new implant. New implant not assigned to any local variable
Point2D center = new Point2D.Double(rect.getCenterX(), rect.getCenterY());
PrimitiveNode priNode = layer.getPureLayerNode();
NodeInst node = NodeInst.makeInstance(priNode, ep, center, rect.getWidth(), rect.getHeight(), curCell);
nodesToExamine.add(node);
EPoint [] ePoints = new EPoint[points.length];
for(int j=0; j oR.getMaxX() ||
oR.getMinX()-dist > r.getMaxX() ||
r.getMinY()-dist > oR.getMaxY() ||
oR.getMinY()-dist > r.getMaxY()) continue;
// they are too close: merge them
double lx = Math.min(r.getMinX(), oR.getMinX());
double hx = Math.max(r.getMaxX(), oR.getMaxX());
double ly = Math.min(r.getMinY(), oR.getMinY());
double hy = Math.max(r.getMaxY(), oR.getMaxY());
r.setRect(lx, ly, hx-lx, hy-ly);
newImplants.remove(j);
merged = true;
break;
}
if (merged) i--;
}
}
PrimitiveNode priNode = layer.getPureLayerNode();
for(Rectangle2D r : newImplants)
{
// ignore if one of the original elements
if (polyArray != null)
{
boolean covered = false;
for (Object poly : polyArray)
{
PolyBase pb = (PolyBase)poly;
Rectangle2D pbRect = pb.getBox();
if (pbRect == null) continue;
covered = pbRect.contains(r);
if (covered) break;
}
if (covered) continue;
}
Point2D center = new Point2D.Double(r.getCenterX(), r.getCenterY());
NodeInst node = NodeInst.makeInstance(priNode, ep, center, r.getWidth(), r.getHeight(), curCell);
nodesToExamine.add(node);
node.setHardSelect();
}
}
}
curCell.killNodes(nodesToDelete);
if (noNewNodes)
System.out.println("No new areas added");
}
break;
case AREA:
case NETWORK:
{
double lambdaSqr = 1; // lambdaofcell(np);
Rectangle2D bbox = curCell.getBounds();
double totalArea = (bbox.getHeight()*bbox.getWidth())/lambdaSqr;
// Traversing tree with merged geometry and sorting layers per name first
List list = new ArrayList(tree.getKeySet());
Collections.sort(list, Layer.layerSortByName);
for (Layer layer : list)
{
Collection set = tree.getObjects(layer, false, true);
if (geoms != null && geoms.onlyThisLayer != null)
{
if (layer != geoms.onlyThisLayer) continue; // ignore this layer
// Add all elements
if (overlapPoint == null)
nodesToExamine.addAll(set);
else
{
// they must be connected
for (PolyBase p : set)
{
if (p.contains(overlapPoint))
nodesToExamine.add(p);
}
}
}
double layerArea = 0;
double perimeter = 0;
// Get all objects and sum the area
for (PolyBase poly : set)
{
layerArea += poly.getArea();
perimeter += poly.getPerimeter();
}
layerArea /= lambdaSqr;
perimeter /= 2;
if (geoms != null)
geoms.addLayer(layer, layerArea, perimeter);
else
System.out.println("Layer " + layer.getName() + " covers " + TextUtils.formatDouble(layerArea)
+ " square lambda (" + TextUtils.formatDouble((layerArea/totalArea)*100) + "%)");
}
geoms.setTotalArea(totalArea);
if (geoms != null)
geoms.print();
else
System.out.println("Cell is " + TextUtils.formatDouble(totalArea) + " square lambda");
}
break;
default:
System.out.println("Error in LayerCoverage: function not implemented");
}
return true;
}
}
/************************************************************************
* LayerCoverageJob Class
************************************************************************/
private static class LayerCoverageJob extends Job
{
private Cell cell;
private LCMode func;
private GeometryHandler.GHMode mode;
private GeometryOnNetwork geoms;
private List nodesAdded;
private Point2D overlapPoint; // to get to crop the search if a given bbox is not null
private LayerCoverageTool.LayerCoveragePreferences lcp;
public LayerCoverageJob(Cell cell, Job.Type jobType, LCMode func, GeometryHandler.GHMode mode,
GeometryOnNetwork geoms, Point2D overlapPoint, LayerCoverageTool.LayerCoveragePreferences lcp)
{
super("Layer Coverage on " + cell, User.getUserTool(), jobType, null, null, Priority.USER);
this.cell = cell;
this.func = func;
this.mode = mode;
this.geoms = geoms;
this.overlapPoint = overlapPoint;
this.lcp = lcp;
setReportExecutionFlag(true);
}
@Override
public boolean doIt() throws JobException
{
EditingPreferences ep = getEditingPreferences();
LayerCoverageData data = new LayerCoverageData(this, cell, func, mode, geoms, null, overlapPoint, ep, lcp);
boolean done = data.doIt();
if (func == LCMode.IMPLANT || (func == LCMode.NETWORK && geoms != null))
{
nodesAdded = new ArrayList();
nodesAdded.addAll(data.getNodesToHighlight());
if (func == LCMode.IMPLANT)
fieldVariableChanged("nodesAdded");
}
return done;
}
@Override
public void terminateOK()
{
// For implant highlight the new nodes
if (func == LCMode.IMPLANT)
{
EditWindow_ wnd = Job.getUserInterface().getCurrentEditWindow_();
if (wnd == null) return; // no GUI present
if (nodesAdded == null) return; // nothing to show
for (Object node : nodesAdded)
{
wnd.addElectricObject((NodeInst)node, cell);
}
}
}
}
/************************************************************************
* AreaCoverageJob Class
************************************************************************/
private static class AreaCoverageJob extends Job
{
private Cell curCell;
private double deltaX, deltaY;
private double width, height;
private GeometryHandler.GHMode mode;
private Map internalMap;
private LayerCoverageTool.LayerCoveragePreferences lcp;
public AreaCoverageJob(Cell cell, GeometryHandler.GHMode mode,
double width, double height, double deltaX, double deltaY, LayerCoveragePreferences lcp)
{
super("Layer Coverage", User.getUserTool(), Type.SERVER_EXAMINE, null, null, Priority.USER);
this.curCell = cell;
this.mode = mode;
this.width = width;
this.height = height;
this.deltaX = deltaX;
this.deltaY = deltaY;
this.lcp = lcp;
setReportExecutionFlag(true); // Want to report statistics
}
@Override
public boolean doIt() throws JobException
{
EditingPreferences ep = getEditingPreferences();
ErrorLogger errorLogger = ErrorLogger.newInstance("Area Coverage");
Rectangle2D bBoxOrig = curCell.getBounds();
double maxY = bBoxOrig.getMaxY();
double maxX = bBoxOrig.getMaxX();
// if negative or zero values -> only once
if (deltaX <= 0) deltaX = bBoxOrig.getWidth();
if (deltaY <= 0) deltaY = bBoxOrig.getHeight();
if (width <= 0) width = bBoxOrig.getWidth();
if (height <= 0) height = bBoxOrig.getHeight();
internalMap = new HashMap();
// fieldVariableChanged("internalMap");
for (double posY = bBoxOrig.getMinY(); posY < maxY; posY += deltaY)
{
for (double posX = bBoxOrig.getMinX(); posX < maxX; posX += deltaX)
{
Rectangle2D box = new Rectangle2D.Double(posX, posY, width, height);
GeometryOnNetwork geoms = new GeometryOnNetwork(curCell, null, 1, true, null, lcp);
System.out.println("Calculating Coverage on cell '" + curCell.getName() + "' for area (" +
DBMath.round(posX) + "," + DBMath.round(posY) + ") (" +
DBMath.round(box.getMaxX()) + "," + DBMath.round(box.getMaxY()) + ")");
LayerCoverageData data = new LayerCoverageData(this, curCell, LCMode.AREA, mode, geoms, box, null, ep, lcp);
if (!data.doIt()) // aborted by user
{
return false; // didn't finish
}
geoms.analyzeCoverage(box, errorLogger);
for (int i = 0; i < geoms.layers.size(); i++)
{
Layer layer = geoms.layers.get(i);
Double area = geoms.areas.get(i);
Double oldV = internalMap.get(layer);
double newV = area.doubleValue();
if (oldV != null)
newV += oldV.doubleValue();
internalMap.put(layer, new Double(newV));
}
}
}
errorLogger.termLogging(true);
return true;
}
public Map getDataInfo() { return internalMap; }
}
public enum LCMode // LC = LayerCoverageTool mode
{
AREA, // function Layer Coverage
MERGE, // Generic merge polygons function
IMPLANT, // Coverage implants
NETWORK // List Geometry on Network function
}
/************************************************************************
* LayerVisitor Class
************************************************************************/
public static class LayerVisitor extends HierarchyEnumerator.Visitor
{
private Job parentJob;
private GeometryHandler tree;
private Set deleteList; // Only used for coverage Implants. New coverage implants are pure primitive nodes
private final LCMode function;
private Map> originalPolygons;
private Set netSet; // For network type, rest is null
private Rectangle2D origBBox;
private Area origBBoxArea; // Area is always in coordinates of top cell
private Layer onlyThisLayer;
private GeometryOnNetwork geoms;
public LayerVisitor(Job job, GeometryHandler t, Set delList, LCMode func,
Map> original, Set netSet, Rectangle2D bBox, Layer onlyThisLayer, GeometryOnNetwork geoms)
{
this.parentJob = job;
this.tree = t;
this.deleteList = delList;
this.function = func;
this.originalPolygons = original;
this.netSet = netSet;
this.origBBox = bBox;
origBBoxArea = (bBox != null) ? new Area(origBBox) : null;
this.onlyThisLayer = onlyThisLayer;
this.geoms = geoms;
}
/**
* Determines if function of given layer is applicable for the corresponding operation
*/
private boolean isValidFunction(Layer layer, LCMode function)
{
if (onlyThisLayer != null && layer != onlyThisLayer)
return false;
Layer.Function func = layer.getFunction();
switch (function)
{
case MERGE:
case NETWORK:
return (true);
case IMPLANT:
return (func.isSubstrate());
case AREA:
return (func.isPoly() || func.isMetal());
default:
return (false);
}
}
/**
* In case of non null bounding box, it will undo the
* transformation
* @param info
*/
public void exitCell(HierarchyEnumerator.CellInfo info)
{
}
private boolean doesIntersectBoundingBox(Rectangle2D rect, HierarchyEnumerator.CellInfo info)
{
// Default case when no bounding box is used to crop the geometry
if (origBBox == null) return true;
// only because I need to transform the points.
PolyBase polyRect = new PolyBase(rect);
// To avoid transformation while traversing the hierarchy
polyRect.transform(info.getTransformToRoot());
rect = polyRect.getBounds2D();
return rect.intersects(origBBox);
}
private boolean isJobAborted()
{
return (parentJob != null && parentJob.checkAbort());
}
public boolean enterCell(HierarchyEnumerator.CellInfo info)
{
// Checking if job is scheduled for abort or already aborted
if (isJobAborted())
return (false);
Cell curCell = info.getCell();
Netlist netlist = info.getNetlist();
// Nothing to visit CAREFUL WITH TRANSFORMATION IN SUBCELL!!
if (!doesIntersectBoundingBox(curCell.getBounds(), info))
return false;
// Checking if any network is found
boolean found = (netSet == null);
for (Iterator it = netlist.getNetworks(); !found && it.hasNext(); )
{
// In case there are many networks
if (isJobAborted())
return (false);
Network parentNet = it.next();
HierarchyEnumerator.CellInfo cinfo = info;
boolean netFound = false;
while ((netFound = netSet.contains(parentNet)) == false && cinfo.getParentInst() != null) {
parentNet = cinfo.getNetworkInParent(parentNet);
cinfo = cinfo.getParentInfo();
}
found = netFound;
}
if (!found) return (false);
// Traversing arcs
for (Iterator it = curCell.getArcs(); it.hasNext(); )
{
ArcInst arc = it.next();
int width = netlist.getBusWidth(arc);
found = (netSet == null);
for (int i=0; !found && i polySet = originalPolygons.get(layer);
if (polySet == null)
{
polySet = new HashSet();
originalPolygons.put(layer, polySet);
}
//map.put(pnode, pnode.clone());
polySet.add(poly);
}
/**
*
* @param no
* @param info
* @return true if node was visited
*/
public boolean visitNodeInst(Nodable no, HierarchyEnumerator.CellInfo info)
{
if (isJobAborted())
return false;
//if (checkAbort()) return false;
NodeInst node = no.getNodeInst();
// Its like pins, facet-center
if (NodeInst.isSpecialNode(node)) return (false);
boolean inside = doesIntersectBoundingBox(node.getBounds(), info);
// Its a cell
if (node.isCellInstance()) return (inside);
// Geometry outside contour
if (!inside) return false;
// boolean found = (netSet == null);
// for(Iterator pIt = node.getPortInsts(); !found && pIt.hasNext(); )
// {
// PortInst pi = pIt.next();
// PortProto subPP = pi.getPortProto();
// Network parentNet = info.getNetlist().getNetwork(node, subPP, 0);
// HierarchyEnumerator.CellInfo cinfo = info;
// boolean netFound = false;
// while ((netFound = netSet.contains(parentNet)) == false && cinfo.getParentInst() != null) {
// parentNet = cinfo.getNetworkInParent(parentNet);
// cinfo = cinfo.getParentInfo();
// }
// found = netFound;
// }
// if (!found) return (false); // skipping this node
// Coverage implants are pure primitive nodes and they are ignored.
if (node.isPrimtiveSubstrateNode()) //node.getFunction() == PrimitiveNode.Function.NODE)
{
if (info.isRootCell())
deleteList.add(node);
return (false);
}
Technology tech = node.getProto().getTechnology();
Poly[] polyList = tech.getShapeOfNode(node, true, false, null);
FixpTransform transform = node.rotateOut();
boolean includedNode = false;
for (int i = 0; i < polyList.length; i++)
{
Poly poly = polyList[i];
if (netSet != null)
{
PortProto subPP = poly.getPort();
Network parentNet = info.getNetlist().getNetwork(node, subPP, 0);
HierarchyEnumerator.CellInfo cinfo = info;
boolean netFound = false;
while ((netFound = netSet.contains(parentNet)) == false && cinfo.getParentInst() != null) {
parentNet = cinfo.getNetworkInParent(parentNet);
cinfo = cinfo.getParentInfo();
}
if (!netFound) continue; // skipping this polygon
}
includedNode = true;
Layer layer = poly.getLayer();
// Only checking poly or metal for AREA case
boolean value = isValidFunction(layer, function);
if (!value) continue;
if (poly.getPoints().length < 3)
{
// When is this happening?
continue;
}
poly.transform(transform);
// Not sure if I need this for general merge polygons function
poly.transform(info.getTransformToRoot());
// If points are not rounded, in IMPLANT map.containsValue() might not work
poly.roundPoints();
storeOriginalPolygons(layer, poly);
Shape pnode = cropGeometry(poly, origBBoxArea);
// empty intersection
if (pnode == null)
continue;
tree.add(layer, pnode);
}
// add transistor gate/active if any part of the node matched a network
if (includedNode && geoms != null)
{
PrimitiveNode.Function fun = node.getFunction();
if (fun.isTransistor())
{
if (fun.isNTypeTransistor())
{
TransistorSize ts = node.getTransistorSize(info.getContext());
geoms.n_active.area += ts.getDoubleArea();
geoms.n_active.width += ts.getDoubleWidth();
geoms.n_gate.area += ts.getDoubleArea();
geoms.n_gate.width += ts.getDoubleWidth();
} else if (fun.isPTypeTransistor())
{
TransistorSize ts = node.getTransistorSize(info.getContext());
geoms.p_active.area += ts.getDoubleArea();
geoms.p_active.width += ts.getDoubleWidth();
geoms.p_gate.area += ts.getDoubleArea();
geoms.p_gate.width += ts.getDoubleWidth();
}
}
}
return (true);
}
/**
* Method to crop original polygon by given bounding box. If they
* don't intersect, returns original shape
* @param origGeom polygon to crop
* @param bBoxArea area that defines bounding box
* @return cropped shape
*/
private static Shape cropGeometry(Shape origGeom, Area bBoxArea)
{
Shape pnode = origGeom;
// exclude area outside bounding box
if (bBoxArea != null)
{
Area tmpA = new Area(pnode);
tmpA.intersect(bBoxArea);
// Empty intersection
if (tmpA.isEmpty()) return null;
pnode = tmpA;
}
return pnode;
}
}
public static class TransistorInfo implements Serializable
{
/** sum of area of transistors */ public double area;
/** sum of width of transistors */ public double width;
};
/**
* Class to represent all geometry on a network during layer coverage analysis.
*/
public static class GeometryOnNetwork implements Serializable {
public final Cell cell;
protected Set nets;
private double lambda;
private boolean printable;
private Layer onlyThisLayer;
// these lists tie together a layer, its area, and its half-perimeter
private ArrayList layers;
private ArrayList areas;
private ArrayList halfPerimeters;
private double totalWire;
private double totalArea;
private LayerCoveragePreferences lcp;
// these are the area and transistor widths for gate and active in N and P
TransistorInfo p_gate, n_gate, p_active, n_active;
public GeometryOnNetwork(Cell cell, Set nets, double lambda, boolean printable,
Layer onlyThisLayer, LayerCoveragePreferences lcp)
{
this.lcp = lcp;
this.cell = cell;
this.nets = nets;
this.lambda = lambda;
this.onlyThisLayer = onlyThisLayer;
layers = new ArrayList();
areas = new ArrayList();
halfPerimeters = new ArrayList();
this.printable = printable;
totalWire = 0;
totalArea = 0;
p_gate = new TransistorInfo();
n_gate = new TransistorInfo();
p_active = new TransistorInfo();
n_active = new TransistorInfo();
}
public double getTotalWireLength() { return totalWire; }
protected void setTotalArea(double area) {totalArea = area; }
public TransistorInfo getPGate() { return p_gate; }
public TransistorInfo getNGate() { return n_gate; }
public TransistorInfo getPActive() { return p_active; }
public TransistorInfo getNActive() { return n_active; }
public List getLayers() { return layers; }
public List getAreas() { return areas; }
public List getHalfPerimeters() { return halfPerimeters; }
private void addLayer(Layer layer, double area, double halfperimeter)
{
assert(layer != null);
if (onlyThisLayer != null && layer != onlyThisLayer) return; // skip this one
layers.add(layer);
areas.add(new Double(area));
halfPerimeters.add(new Double(halfperimeter));
Layer.Function func = layer.getFunction();
// accumulate total wire length on all metal/poly layers
if (func.isPoly() && !func.isGatePoly() || func.isMetal()) {
totalWire += halfperimeter;
}
}
/**
* Method to analyze amount of area covered by layer and if meets the minimum
* specified
* @param bbox
* @param errorLogger
* @return true if no errors are found
*/
public boolean analyzeCoverage(Rectangle2D bbox, ErrorLogger errorLogger)
{
totalArea = (bbox.getHeight()*bbox.getWidth())/(lambda*lambda);
boolean foundError = false;
for (int i = 0; i < layers.size(); i++)
{
Layer layer = layers.get(i);
Double area = areas.get(i);
double percentage = area.doubleValue()/totalArea * 100;
double minV = lcp.getAreaCoverage(layer);
if (percentage < minV)
{
String msg = "Error area coverage " + layer.getName() + " min value = " + minV + " actual value = " + percentage;
PolyBase poly = new PolyBase(bbox);
errorLogger.logError(msg, poly, cell, layer.getIndex());
foundError = true;
}
}
return foundError;
}
public void print() {
// Doesn't print information
if (!printable) return;
// nets is null for mode=AREA
if (nets != null)
{
for(Network net : nets)
{
System.out.println("For " + net + " in " + cell + ":");
}
}
for (int i=0; i 0)
System.out.println("Total wire length = " + TextUtils.formatDouble(totalWire/lambda));
if (totalArea > 0)
System.out.println("Total cell area = " + TextUtils.formatDouble(totalArea));
}
}
/***********************************
* JUnit interface
***********************************/
public static boolean testAll()
{
//return (LayerCoverageToolTest.basicAreaCoverageTest("area.log"));
return true;
}
}